diff --git a/CHANGELOG.md b/CHANGELOG.md
index 7c77174cf..1688b3b02 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,8 @@
# Wikimedia Commons for Android
+## v2.7.2
+- Modified subtext for "automatically get current location" setting to emphasize that it will reveal user's location
+
## v2.7.1
- Fixed UI and permission issues with Nearby
- Fixed issue with My Recent Uploads being empty
diff --git a/PULL_REQUEST_TEMPLATE.md b/PULL_REQUEST_TEMPLATE.md
index dffa9d28d..37e104d14 100644
--- a/PULL_REQUEST_TEMPLATE.md
+++ b/PULL_REQUEST_TEMPLATE.md
@@ -16,4 +16,4 @@ Tested on {API level & name of device/emulator}, with {build variant, e.g. ProdD
{Only for user interface changes, otherwise remove this section. See [how to take a screenshot](https://android.stackexchange.com/questions/1759/how-to-take-a-screenshot-with-an-android-device)}
-_Note: Please ensure that you have read CONTRIBUTING.md if this is your first pull request._
+_Note: Please ensure that you have read CONTRIBUTING.md if this is your first pull request._
\ No newline at end of file
diff --git a/app/build.gradle b/app/build.gradle
index 46da62b16..68a31f984 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -7,6 +7,8 @@ apply from: 'quality.gradle'
apply plugin: 'com.getkeepsafe.dexcount'
dependencies {
+ implementation 'com.squareup.picasso:picasso:2.71828'
+ implementation 'com.prof.rssparser:rssparser:1.1'
implementation 'com.github.nicolas-raoul:Quadtree:ac16ea8035bf07'
implementation 'fr.avianey.com.viewpagerindicator:library:2.4.1.1@aar'
implementation 'in.yuvi:http.fluent:1.3'
@@ -19,48 +21,37 @@ dependencies {
implementation 'com.jakewharton.timber:timber:4.5.1'
implementation 'info.debatty:java-string-similarity:0.24'
implementation 'com.borjabravo:readmoretextview:2.1.0'
- implementation 'com.android.support.constraint:constraint-layout:1.1.0'
- implementation ('com.mapbox.mapboxsdk:mapbox-android-sdk:5.4.1@aar'){
- transitive=true
+ implementation 'com.android.support.constraint:constraint-layout:1.0.2'
+ implementation('com.mapbox.mapboxsdk:mapbox-android-sdk:5.4.1@aar') {
+ transitive = true
}
-
- implementation "com.github.deano2390:MaterialShowcaseView:1.2.0"
-
+ implementation 'com.github.deano2390:MaterialShowcaseView:1.2.0'
implementation "com.android.support:support-v4:$SUPPORT_LIB_VERSION"
implementation "com.android.support:appcompat-v7:$SUPPORT_LIB_VERSION"
implementation "com.android.support:design:$SUPPORT_LIB_VERSION"
implementation "com.android.support:customtabs:$SUPPORT_LIB_VERSION"
-
implementation "com.android.support:cardview-v7:$SUPPORT_LIB_VERSION"
-
implementation "com.jakewharton:butterknife:$BUTTERKNIFE_VERSION"
kapt "com.jakewharton:butterknife-compiler:$BUTTERKNIFE_VERSION"
-
implementation 'com.squareup.okhttp3:okhttp:3.9.1'
implementation 'com.squareup.okio:okio:1.13.0'
-
implementation 'io.reactivex.rxjava2:rxandroid:2.0.1'
// Because RxAndroid releases are few and far between, it is recommended you also
// explicitly depend on RxJava's latest version for bug fixes and new features.
implementation 'com.android.support:multidex:1.0.3'
-
implementation 'io.reactivex.rxjava2:rxjava:2.1.2'
implementation 'com.jakewharton.rxbinding2:rxbinding:2.0.0'
implementation 'com.jakewharton.rxbinding2:rxbinding-support-v4:2.0.0'
implementation 'com.jakewharton.rxbinding2:rxbinding-appcompat-v7:2.0.0'
implementation 'com.jakewharton.rxbinding2:rxbinding-design:2.0.0'
-
implementation 'org.jsoup:jsoup:1.11.3'
-
implementation 'com.facebook.fresco:fresco:1.5.0'
implementation 'com.facebook.stetho:stetho:1.5.0'
-
implementation "com.google.dagger:dagger:$DAGGER_VERSION"
implementation "com.google.dagger:dagger-android-support:$DAGGER_VERSION"
kapt "com.google.dagger:dagger-android-processor:$DAGGER_VERSION"
kapt "com.google.dagger:dagger-compiler:$DAGGER_VERSION"
-
- testImplementation "org.robolectric:multidex:3.4.2"
+ testImplementation 'org.robolectric:multidex:3.4.2'
testImplementation "org.jetbrains.kotlin:kotlin-stdlib-jre7:$kotlin_version"
testImplementation "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version"
testImplementation 'junit:junit:4.12'
@@ -92,8 +83,8 @@ android {
defaultConfig {
applicationId 'fr.free.nrw.commons'
- versionCode 84
- versionName '2.7.1'
+ versionCode 85
+ versionName '2.7.2'
setProperty("archivesBaseName", "app-commons-v$versionName-" + getBranchName())
minSdkVersion project.minSdkVersion
@@ -134,7 +125,9 @@ android {
flavorDimensions 'tier'
productFlavors {
prod {
+ buildConfigField "String", "WIKIMEDIA_API_POTD", "\"https://commons.wikimedia.org/w/api.php?action=featuredfeed&feed=potd&feedformat=rss&language=en\""
buildConfigField "String", "WIKIMEDIA_API_HOST", "\"https://commons.wikimedia.org/w/api.php\""
+ buildConfigField "String", "WIKIDATA_API_HOST", "\"https://www.wikidata.org/w/api.php\""
buildConfigField "String", "WIKIMEDIA_FORGE_API_HOST", "\"https://tools.wmflabs.org/\""
buildConfigField "String", "IMAGE_URL_BASE", "\"https://upload.wikimedia.org/wikipedia/commons\""
buildConfigField "String", "HOME_URL", "\"https://commons.wikimedia.org/wiki/\""
@@ -150,7 +143,9 @@ android {
beta {
// What values do we need to hit the BETA versions of the site / api ?
+ buildConfigField "String", "WIKIMEDIA_API_POTD", "\"https://commons.wikimedia.org/w/api.php?action=featuredfeed&feed=potd&feedformat=rss&language=en\""
buildConfigField "String", "WIKIMEDIA_API_HOST", "\"https://commons.wikimedia.beta.wmflabs.org/w/api.php\""
+ buildConfigField "String", "WIKIDATA_API_HOST", "\"https://www.wikidata.org/w/api.php\""
buildConfigField "String", "WIKIMEDIA_FORGE_API_HOST", "\"https://tools.wmflabs.org/\""
buildConfigField "String", "IMAGE_URL_BASE", "\"https://upload.beta.wmflabs.org/wikipedia/commons\""
buildConfigField "String", "HOME_URL", "\"https://commons.wikimedia.beta.wmflabs.org/wiki/\""
diff --git a/app/libs/java-json.jar b/app/libs/java-json.jar
new file mode 100644
index 000000000..2f211e366
Binary files /dev/null and b/app/libs/java-json.jar differ
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 45a647227..38fc2e35a 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -27,10 +27,10 @@
android:theme="@style/LightAppTheme"
android:supportsRtl="true" >
+ android:theme="@android:style/Theme.Dialog"
+ android:launchMode="singleInstance"
+ android:excludeFromRecents="true"
+ android:finishOnTaskLaunch="true" />
@@ -165,6 +165,16 @@
android:label="@string/provider_categories"
android:syncable="false" />
+
+
+
+
+
+
+
+
diff --git a/app/src/main/java/fr/free/nrw/commons/AboutActivity.java b/app/src/main/java/fr/free/nrw/commons/AboutActivity.java
index 65cba7531..c8941dcd8 100644
--- a/app/src/main/java/fr/free/nrw/commons/AboutActivity.java
+++ b/app/src/main/java/fr/free/nrw/commons/AboutActivity.java
@@ -9,9 +9,6 @@ import android.os.Bundle;
import android.text.Html;
import android.text.SpannableString;
import android.text.style.UnderlineSpan;
-import android.util.Log;
-import android.support.customtabs.CustomTabsIntent;
-import android.support.v4.content.ContextCompat;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
@@ -20,7 +17,6 @@ import android.widget.ArrayAdapter;
import android.widget.LinearLayout;
import android.widget.Spinner;
import android.widget.TextView;
-import android.widget.Toast;
import butterknife.BindView;
import butterknife.ButterKnife;
@@ -28,8 +24,6 @@ import butterknife.OnClick;
import fr.free.nrw.commons.theme.NavigationBaseActivity;
import fr.free.nrw.commons.ui.widget.HtmlTextView;
-import static android.widget.Toast.LENGTH_SHORT;
-
/**
* Represents about screen of this app
*/
diff --git a/app/src/main/java/fr/free/nrw/commons/CommonsApplication.java b/app/src/main/java/fr/free/nrw/commons/CommonsApplication.java
index 57cb5fad1..61eecee00 100644
--- a/app/src/main/java/fr/free/nrw/commons/CommonsApplication.java
+++ b/app/src/main/java/fr/free/nrw/commons/CommonsApplication.java
@@ -1,6 +1,5 @@
package fr.free.nrw.commons;
-import android.app.Application;
import android.content.Context;
import android.content.SharedPreferences;
import android.database.sqlite.SQLiteDatabase;
@@ -27,7 +26,7 @@ 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.modifications.ModifierSequenceDao;
-import fr.free.nrw.commons.utils.FileUtils;
+import fr.free.nrw.commons.upload.FileUtils;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.schedulers.Schedulers;
import timber.log.Timber;
diff --git a/app/src/main/java/fr/free/nrw/commons/Utils.java b/app/src/main/java/fr/free/nrw/commons/Utils.java
index 91c23ce26..9f89586c0 100644
--- a/app/src/main/java/fr/free/nrw/commons/Utils.java
+++ b/app/src/main/java/fr/free/nrw/commons/Utils.java
@@ -178,6 +178,7 @@ public class Utils {
}
public static void handleWebUrl(Context context, Uri url) {
+ Timber.d("Launching web url %s", url.toString());
Intent browserIntent = new Intent(Intent.ACTION_VIEW, url);
if (browserIntent.resolveActivity(context.getPackageManager()) == null) {
Toast toast = Toast.makeText(context, context.getString(R.string.no_web_browser), LENGTH_SHORT);
diff --git a/app/src/main/java/fr/free/nrw/commons/auth/LoginActivity.java b/app/src/main/java/fr/free/nrw/commons/auth/LoginActivity.java
index 339af96d7..b6c0a2046 100644
--- a/app/src/main/java/fr/free/nrw/commons/auth/LoginActivity.java
+++ b/app/src/main/java/fr/free/nrw/commons/auth/LoginActivity.java
@@ -4,8 +4,8 @@ import android.accounts.Account;
import android.accounts.AccountAuthenticatorActivity;
import android.accounts.AccountAuthenticatorResponse;
import android.accounts.AccountManager;
-import android.app.Activity;
import android.app.ProgressDialog;
+import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.net.Uri;
@@ -23,7 +23,6 @@ import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
-import android.view.inputmethod.InputMethodManager;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
@@ -136,6 +135,11 @@ public class LoginActivity extends AccountAuthenticatorActivity {
}
}
+ public static void startYourself(Context context) {
+ Intent intent = new Intent(context, LoginActivity.class);
+ context.startActivity(intent);
+ }
+
private void forgotPassword() {
Utils.handleWebUrl(this, Uri.parse(BuildConfig.FORGOT_PASSWORD_URL));
}
diff --git a/app/src/main/java/fr/free/nrw/commons/auth/SessionManager.java b/app/src/main/java/fr/free/nrw/commons/auth/SessionManager.java
index ad9a0bda4..9ef6b7843 100644
--- a/app/src/main/java/fr/free/nrw/commons/auth/SessionManager.java
+++ b/app/src/main/java/fr/free/nrw/commons/auth/SessionManager.java
@@ -81,6 +81,12 @@ public class SessionManager {
return sharedPreferences.getBoolean("isUserLoggedIn", false);
}
+ public void forceLogin(Context context) {
+ if (context != null) {
+ LoginActivity.startYourself(context);
+ }
+ }
+
public Completable clearAllAccounts() {
AccountManager accountManager = AccountManager.get(context);
Account[] allAccounts = accountManager.getAccountsByType(ACCOUNT_TYPE);
diff --git a/app/src/main/java/fr/free/nrw/commons/category/CategoryImagesActivity.java b/app/src/main/java/fr/free/nrw/commons/category/CategoryImagesActivity.java
index 1f385b258..17601151c 100644
--- a/app/src/main/java/fr/free/nrw/commons/category/CategoryImagesActivity.java
+++ b/app/src/main/java/fr/free/nrw/commons/category/CategoryImagesActivity.java
@@ -14,7 +14,6 @@ import fr.free.nrw.commons.Media;
import fr.free.nrw.commons.R;
import fr.free.nrw.commons.auth.AuthenticatedActivity;
import fr.free.nrw.commons.media.MediaDetailPagerFragment;
-import timber.log.Timber;
/**
* This activity displays pictures of a particular category
diff --git a/app/src/main/java/fr/free/nrw/commons/category/CategoryImagesListFragment.java b/app/src/main/java/fr/free/nrw/commons/category/CategoryImagesListFragment.java
index 0c0b95bb2..a44e19a29 100644
--- a/app/src/main/java/fr/free/nrw/commons/category/CategoryImagesListFragment.java
+++ b/app/src/main/java/fr/free/nrw/commons/category/CategoryImagesListFragment.java
@@ -228,7 +228,7 @@ public class CategoryImagesListFragment extends DaggerFragment {
/**
* This method will be called on back pressed of CategoryImagesActivity.
* It initializes the grid view by setting adapter.
-\ */
+ */
@Override
public void onResume() {
gridView.setAdapter(gridAdapter);
diff --git a/app/src/main/java/fr/free/nrw/commons/contributions/Contribution.java b/app/src/main/java/fr/free/nrw/commons/contributions/Contribution.java
index 7861f96de..99009c029 100644
--- a/app/src/main/java/fr/free/nrw/commons/contributions/Contribution.java
+++ b/app/src/main/java/fr/free/nrw/commons/contributions/Contribution.java
@@ -45,6 +45,7 @@ public class Contribution extends Media {
private long transferred;
private String decimalCoords;
private boolean isMultiple;
+ private String wikiDataEntityId;
public Contribution(Uri contentUri, String filename, Uri localUri, String imageUrl, Date timestamp,
int state, long dataLength, Date dateUploaded, long transferred,
@@ -222,4 +223,17 @@ public class Contribution extends Media {
throw new RuntimeException("Unrecognized license value: " + license);
}
+
+ public String getWikiDataEntityId() {
+ return wikiDataEntityId;
+ }
+
+ /**
+ * When the corresponding wikidata entity is known as in case of nearby uploads, it can be set
+ * using the setter method
+ * @param wikiDataEntityId
+ */
+ public void setWikiDataEntityId(String wikiDataEntityId) {
+ this.wikiDataEntityId = wikiDataEntityId;
+ }
}
diff --git a/app/src/main/java/fr/free/nrw/commons/contributions/ContributionController.java b/app/src/main/java/fr/free/nrw/commons/contributions/ContributionController.java
index 37b3d5377..ed6001f94 100644
--- a/app/src/main/java/fr/free/nrw/commons/contributions/ContributionController.java
+++ b/app/src/main/java/fr/free/nrw/commons/contributions/ContributionController.java
@@ -90,7 +90,7 @@ public class ContributionController {
fragment.startActivityForResult(pickImageIntent, SELECT_FROM_GALLERY);
}
- public void handleImagePicked(int requestCode, Intent data, boolean isDirectUpload) {
+ public void handleImagePicked(int requestCode, Intent data, boolean isDirectUpload, String wikiDataEntityId) {
FragmentActivity activity = fragment.getActivity();
Timber.d("handleImagePicked() called with onActivityResult()");
Intent shareIntent = new Intent(activity, ShareActivity.class);
@@ -102,9 +102,6 @@ public class ContributionController {
shareIntent.setType(activity.getContentResolver().getType(imageData));
shareIntent.putExtra(EXTRA_STREAM, imageData);
shareIntent.putExtra(EXTRA_SOURCE, SOURCE_GALLERY);
- if (isDirectUpload) {
- shareIntent.putExtra("isDirectUpload", true);
- }
break;
case SELECT_FROM_CAMERA:
//FIXME: Find out appropriate mime type
@@ -113,9 +110,6 @@ public class ContributionController {
shareIntent.setType("image/jpeg");
shareIntent.putExtra(EXTRA_STREAM, lastGeneratedCaptureUri);
shareIntent.putExtra(EXTRA_SOURCE, SOURCE_CAMERA);
- if (isDirectUpload) {
- shareIntent.putExtra("isDirectUpload", true);
- }
break;
default:
@@ -123,6 +117,10 @@ public class ContributionController {
}
Timber.i("Image selected");
try {
+ shareIntent.putExtra("isDirectUpload", isDirectUpload);
+ if (wikiDataEntityId != null && !wikiDataEntityId.equals("")) {
+ shareIntent.putExtra("wikiDataEntityId", wikiDataEntityId);
+ }
activity.startActivity(shareIntent);
} catch (SecurityException e) {
Timber.e(e, "Security Exception");
diff --git a/app/src/main/java/fr/free/nrw/commons/contributions/ContributionDao.java b/app/src/main/java/fr/free/nrw/commons/contributions/ContributionDao.java
index 079cf6477..6d290b1a5 100644
--- a/app/src/main/java/fr/free/nrw/commons/contributions/ContributionDao.java
+++ b/app/src/main/java/fr/free/nrw/commons/contributions/ContributionDao.java
@@ -8,7 +8,6 @@ import android.net.Uri;
import android.os.RemoteException;
import android.support.annotation.Nullable;
import android.text.TextUtils;
-import android.util.Log;
import java.util.Date;
diff --git a/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsActivity.java b/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsActivity.java
index ad6cff606..e5f6f53bb 100644
--- a/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsActivity.java
+++ b/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsActivity.java
@@ -276,17 +276,25 @@ public class ContributionsActivity
.getUploadCount(sessionManager.getCurrentAccount().name)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
- .subscribe(
- uploadCount -> getSupportActionBar().setSubtitle(getResources()
- .getQuantityString(R.plurals.contributions_subtitle,
- uploadCount, uploadCount)),
+ .subscribe(this::displayUploadCount,
t -> Timber.e(t, "Fetching upload count failed")
));
}
- public void betaSetUploadCount(int betaUploadCount){
+ private void displayUploadCount(Integer uploadCount) {
+ if (isFinishing()
+ || getSupportActionBar() == null
+ || getResources() == null) {
+ return;
+ }
+
getSupportActionBar().setSubtitle(getResources()
- .getQuantityString(R.plurals.contributions_subtitle, betaUploadCount, betaUploadCount));
+ .getQuantityString(R.plurals.contributions_subtitle,
+ uploadCount, uploadCount));
+ }
+
+ public void betaSetUploadCount(int betaUploadCount) {
+ displayUploadCount(betaUploadCount);
}
diff --git a/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsListFragment.java b/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsListFragment.java
index ff400a8dd..0b600c5d0 100644
--- a/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsListFragment.java
+++ b/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsListFragment.java
@@ -117,7 +117,7 @@ public class ContributionsListFragment extends CommonsDaggerSupportFragment {
if (resultCode == RESULT_OK) {
Timber.d("OnActivityResult() parameters: Req code: %d Result code: %d Data: %s",
requestCode, resultCode, data);
- controller.handleImagePicked(requestCode, data, false);
+ controller.handleImagePicked(requestCode, data, false, null);
} else {
Timber.e("OnActivityResult() parameters: Req code: %d Result code: %d Data: %s",
requestCode, resultCode, data);
diff --git a/app/src/main/java/fr/free/nrw/commons/di/CommonsApplicationComponent.java b/app/src/main/java/fr/free/nrw/commons/di/CommonsApplicationComponent.java
index 5662bb885..43721a217 100644
--- a/app/src/main/java/fr/free/nrw/commons/di/CommonsApplicationComponent.java
+++ b/app/src/main/java/fr/free/nrw/commons/di/CommonsApplicationComponent.java
@@ -13,8 +13,10 @@ import fr.free.nrw.commons.contributions.ContributionsSyncAdapter;
import fr.free.nrw.commons.delete.DeleteTask;
import fr.free.nrw.commons.modifications.ModificationsSyncAdapter;
import fr.free.nrw.commons.nearby.PlaceRenderer;
+import fr.free.nrw.commons.upload.FileProcessor;
import fr.free.nrw.commons.settings.SettingsFragment;
+
@Singleton
@Component(modules = {
CommonsApplicationModule.class,
@@ -46,6 +48,8 @@ public interface CommonsApplicationComponent extends AndroidInjector provideLruCache() {
return new LruCache<>(1024);
}
+
+ @Provides
+ @Singleton
+ public WikidataEditListener provideWikidataEditListener() {
+ return new WikidataEditListenerImpl();
+ }
}
\ No newline at end of file
diff --git a/app/src/main/java/fr/free/nrw/commons/di/NetworkingModule.java b/app/src/main/java/fr/free/nrw/commons/di/NetworkingModule.java
index 8c0b52316..cd043e950 100644
--- a/app/src/main/java/fr/free/nrw/commons/di/NetworkingModule.java
+++ b/app/src/main/java/fr/free/nrw/commons/di/NetworkingModule.java
@@ -35,7 +35,7 @@ public class NetworkingModule {
@Named("default_preferences") SharedPreferences defaultPreferences,
@Named("category_prefs") SharedPreferences categoryPrefs,
Gson gson) {
- return new ApacheHttpClientMediaWikiApi(context, BuildConfig.WIKIMEDIA_API_HOST, defaultPreferences, categoryPrefs, gson);
+ return new ApacheHttpClientMediaWikiApi(context, BuildConfig.WIKIMEDIA_API_HOST, BuildConfig.WIKIDATA_API_HOST, defaultPreferences, categoryPrefs, gson);
}
@Provides
diff --git a/app/src/main/java/fr/free/nrw/commons/location/LocationServiceManager.java b/app/src/main/java/fr/free/nrw/commons/location/LocationServiceManager.java
index 49c422633..cd1082ba5 100644
--- a/app/src/main/java/fr/free/nrw/commons/location/LocationServiceManager.java
+++ b/app/src/main/java/fr/free/nrw/commons/location/LocationServiceManager.java
@@ -284,6 +284,7 @@ public class LocationServiceManager implements LocationListener {
LOCATION_SIGNIFICANTLY_CHANGED, //Went out of borders of nearby markers
LOCATION_SLIGHTLY_CHANGED, //User might be walking or driving
LOCATION_NOT_CHANGED,
- PERMISSION_JUST_GRANTED
+ PERMISSION_JUST_GRANTED,
+ MAP_UPDATED
}
}
diff --git a/app/src/main/java/fr/free/nrw/commons/media/MediaDetailFragment.java b/app/src/main/java/fr/free/nrw/commons/media/MediaDetailFragment.java
index fcfb1f4d9..d5b1cc0ce 100644
--- a/app/src/main/java/fr/free/nrw/commons/media/MediaDetailFragment.java
+++ b/app/src/main/java/fr/free/nrw/commons/media/MediaDetailFragment.java
@@ -23,6 +23,9 @@ import android.widget.ScrollView;
import android.widget.TextView;
import android.widget.Toast;
+import butterknife.BindView;
+import butterknife.ButterKnife;
+import butterknife.OnClick;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
@@ -32,9 +35,6 @@ import java.util.Locale;
import javax.inject.Inject;
import javax.inject.Provider;
-import butterknife.BindView;
-import butterknife.ButterKnife;
-import butterknife.OnClick;
import fr.free.nrw.commons.License;
import fr.free.nrw.commons.LicenseList;
import fr.free.nrw.commons.Media;
diff --git a/app/src/main/java/fr/free/nrw/commons/media/MediaDetailPagerFragment.java b/app/src/main/java/fr/free/nrw/commons/media/MediaDetailPagerFragment.java
index 2c58e8dbb..bd985ef20 100644
--- a/app/src/main/java/fr/free/nrw/commons/media/MediaDetailPagerFragment.java
+++ b/app/src/main/java/fr/free/nrw/commons/media/MediaDetailPagerFragment.java
@@ -2,11 +2,9 @@ package fr.free.nrw.commons.media;
import android.annotation.SuppressLint;
import android.app.DownloadManager;
-import android.app.WallpaperManager;
import android.content.Intent;
import android.content.SharedPreferences;
import android.database.DataSetObserver;
-import android.graphics.Bitmap;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
@@ -28,8 +26,6 @@ import android.view.View;
import android.view.ViewGroup;
import android.widget.Toast;
-import java.io.IOException;
-
import butterknife.BindView;
import butterknife.ButterKnife;
import javax.inject.Inject;
@@ -50,7 +46,6 @@ import static android.content.Context.DOWNLOAD_SERVICE;
import static android.content.Intent.ACTION_VIEW;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static android.widget.Toast.LENGTH_SHORT;
-import static com.mapbox.mapboxsdk.Mapbox.getApplicationContext;
public class MediaDetailPagerFragment extends CommonsDaggerSupportFragment implements ViewPager.OnPageChangeListener {
diff --git a/app/src/main/java/fr/free/nrw/commons/mwapi/ApacheHttpClientMediaWikiApi.java b/app/src/main/java/fr/free/nrw/commons/mwapi/ApacheHttpClientMediaWikiApi.java
index ab534b00f..a749ed59d 100644
--- a/app/src/main/java/fr/free/nrw/commons/mwapi/ApacheHttpClientMediaWikiApi.java
+++ b/app/src/main/java/fr/free/nrw/commons/mwapi/ApacheHttpClientMediaWikiApi.java
@@ -25,6 +25,8 @@ import org.apache.http.params.CoreProtocolPNames;
import org.apache.http.util.EntityUtils;
import org.mediawiki.api.ApiResult;
import org.mediawiki.api.MWApi;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import java.io.IOException;
@@ -63,6 +65,7 @@ public class ApacheHttpClientMediaWikiApi implements MediaWikiApi {
private static final String THUMB_SIZE = "640";
private AbstractHttpClient httpClient;
private MWApi api;
+ private MWApi wikidataApi;
private Context context;
private SharedPreferences defaultPreferences;
private SharedPreferences categoryPreferences;
@@ -70,6 +73,7 @@ public class ApacheHttpClientMediaWikiApi implements MediaWikiApi {
public ApacheHttpClientMediaWikiApi(Context context,
String apiURL,
+ String wikidatApiURL,
SharedPreferences defaultPreferences,
SharedPreferences categoryPreferences,
Gson gson) {
@@ -83,6 +87,7 @@ public class ApacheHttpClientMediaWikiApi implements MediaWikiApi {
params.setParameter(CoreProtocolPNames.USER_AGENT, getUserAgent());
httpClient = new DefaultHttpClient(cm, params);
api = new MWApi(apiURL, httpClient);
+ wikidataApi = new MWApi(wikidatApiURL, httpClient);
this.defaultPreferences = defaultPreferences;
this.categoryPreferences = categoryPreferences;
this.gson = gson;
@@ -207,6 +212,15 @@ public class ApacheHttpClientMediaWikiApi implements MediaWikiApi {
return api.getEditToken();
}
+ @Override
+ public String getCentralAuthToken() throws IOException {
+ String centralAuthToken = api.action("centralauthtoken")
+ .get()
+ .getString("/api/centralauthtoken/@centralauthtoken");
+ Timber.d("MediaWiki Central auth token is %s", centralAuthToken);
+ return centralAuthToken;
+ }
+
@Override
public boolean fileExistsWithName(String fileName) throws IOException {
return api.action("query")
@@ -352,6 +366,98 @@ public class ApacheHttpClientMediaWikiApi implements MediaWikiApi {
}).flatMapObservable(Observable::fromIterable);
}
+ /**
+ * Get the edit token for making wiki data edits
+ * https://www.mediawiki.org/wiki/API:Tokens
+ * @return
+ * @throws IOException
+ */
+ private String getWikidataEditToken() throws IOException {
+ return wikidataApi.getEditToken();
+ }
+
+ @Override
+ public String getWikidataCsrfToken() throws IOException {
+ String wikidataCsrfToken = wikidataApi.action("query")
+ .param("action", "query")
+ .param("centralauthtoken", getCentralAuthToken())
+ .param("meta", "tokens")
+ .post()
+ .getString("/api/query/tokens/@csrftoken");
+ Timber.d("Wikidata csrf token is %s", wikidataCsrfToken);
+ return wikidataCsrfToken;
+ }
+
+ /**
+ * Creates a new claim using the wikidata API
+ * https://www.mediawiki.org/wiki/Wikibase/API
+ * @param entityId the wikidata entity to be edited
+ * @param property the property to be edited, for eg P18 for images
+ * @param snaktype the type of value stored for that property
+ * @param value the actual value to be stored for the property, for eg filename in case of P18
+ * @return returns revisionId if the claim is successfully created else returns null
+ * @throws IOException
+ */
+ @Nullable
+ @Override
+ public String wikidatCreateClaim(String entityId, String property, String snaktype, String value) throws IOException {
+ Timber.d("Filename is %s", value);
+ ApiResult result = wikidataApi.action("wbcreateclaim")
+ .param("entity", entityId)
+ .param("centralauthtoken", getCentralAuthToken())
+ .param("token", getWikidataCsrfToken())
+ .param("snaktype", snaktype)
+ .param("property", property)
+ .param("value", value)
+ .post();
+
+ if (result == null || result.getNode("api") == null) {
+ return null;
+ }
+
+ Node node = result.getNode("api").getDocument();
+ Element element = (Element) node;
+
+ if (element != null && element.getAttribute("success").equals("1")) {
+ return result.getString("api/pageinfo/@lastrevid");
+ } else {
+ Timber.e(result.getString("api/error/@code") + " " + result.getString("api/error/@info"));
+ }
+ return null;
+ }
+
+ /**
+ * Adds the wikimedia-commons-app tag to the edits made on wikidata
+ * @param revisionId
+ * @return
+ * @throws IOException
+ */
+ @Nullable
+ @Override
+ public boolean addWikidataEditTag(String revisionId) throws IOException {
+ ApiResult result = wikidataApi.action("tag")
+ .param("revid", revisionId)
+ .param("centralauthtoken", getCentralAuthToken())
+ .param("token", getWikidataCsrfToken())
+ .param("add", "wikimedia-commons-app")
+ .param("reason", "Add tag for edits made using Android Commons app")
+ .post();
+
+ if (result == null || result.getNode("api") == null) {
+ return false;
+ }
+
+ Node node = result.getNode("api").getDocument();
+ Element element = (Element) node;
+
+ if (element != null && element.getAttribute("status").equals("success")) {
+ return true;
+ } else {
+ Timber.e(result.getString("api/error/@code") + " " + result.getString("api/error/@info"));
+ }
+ return false;
+ }
+
@Override
@NonNull
public Observable searchTitles(String title, int searchCatsLimit) {
@@ -481,6 +587,8 @@ public class ApacheHttpClientMediaWikiApi implements MediaWikiApi {
.param("format", "xml")
.param("gcmtype", "file")
.param("gcmtitle", categoryName)
+ .param("gcmsort", "timestamp")//property to sort by;timestamp
+ .param("gcmdir", "desc")//in which direction to sort;descending
.param("prop", "imageinfo")
.param("gcmlimit", "10")
.param("iiprop", "url|extmetadata");
@@ -587,6 +695,7 @@ public class ApacheHttpClientMediaWikiApi implements MediaWikiApi {
String resultStatus = result.getString("/api/upload/@result");
if (!resultStatus.equals("Success")) {
String errorCode = result.getString("/api/error/@code");
+ Timber.e(errorCode);
return new UploadResult(resultStatus, errorCode);
} else {
Date dateUploaded = parseMWDate(result.getString("/api/upload/imageinfo/@timestamp"));
diff --git a/app/src/main/java/fr/free/nrw/commons/mwapi/MediaWikiApi.java b/app/src/main/java/fr/free/nrw/commons/mwapi/MediaWikiApi.java
index 21f45e127..4ac185a4a 100644
--- a/app/src/main/java/fr/free/nrw/commons/mwapi/MediaWikiApi.java
+++ b/app/src/main/java/fr/free/nrw/commons/mwapi/MediaWikiApi.java
@@ -27,6 +27,10 @@ public interface MediaWikiApi {
String getEditToken() throws IOException;
+ String getWikidataCsrfToken() throws IOException;
+
+ String getCentralAuthToken() throws IOException;
+
boolean fileExistsWithName(String fileName) throws IOException;
boolean pageExists(String pageName) throws IOException;
@@ -49,6 +53,12 @@ public interface MediaWikiApi {
@Nullable
String appendEdit(String editToken, String processedPageContent, String filename, String summary) throws IOException;
+ @Nullable
+ String wikidatCreateClaim(String entityId, String property, String snaktype, String value) throws IOException;
+
+ @Nullable
+ boolean addWikidataEditTag(String revisionId) throws IOException;
+
@NonNull
MediaResult fetchMediaByFilename(String filename) throws IOException;
diff --git a/app/src/main/java/fr/free/nrw/commons/nearby/DirectUpload.java b/app/src/main/java/fr/free/nrw/commons/nearby/DirectUpload.java
index 7ab427b2d..9a12c6d39 100644
--- a/app/src/main/java/fr/free/nrw/commons/nearby/DirectUpload.java
+++ b/app/src/main/java/fr/free/nrw/commons/nearby/DirectUpload.java
@@ -1,6 +1,5 @@
package fr.free.nrw.commons.nearby;
-import android.content.SharedPreferences;
import android.os.Build;
import android.support.v4.app.Fragment;
import android.support.v4.content.ContextCompat;
diff --git a/app/src/main/java/fr/free/nrw/commons/nearby/NearbyActivity.java b/app/src/main/java/fr/free/nrw/commons/nearby/NearbyActivity.java
index 35e15b0d9..cb28df947 100644
--- a/app/src/main/java/fr/free/nrw/commons/nearby/NearbyActivity.java
+++ b/app/src/main/java/fr/free/nrw/commons/nearby/NearbyActivity.java
@@ -16,7 +16,6 @@ import android.support.design.widget.BottomSheetBehavior;
import android.support.v4.app.FragmentTransaction;
import android.support.v7.app.AlertDialog;
-import android.text.TextUtils;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
@@ -24,14 +23,9 @@ import android.view.View;
import android.widget.LinearLayout;
import android.widget.ProgressBar;
-import android.widget.Toast;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
-import io.reactivex.functions.Consumer;
-import java.io.IOException;
-import java.net.ConnectException;
-import java.net.UnknownHostException;
import java.util.List;
import javax.inject.Inject;
@@ -42,11 +36,13 @@ import butterknife.ButterKnife;
import fr.free.nrw.commons.R;
import fr.free.nrw.commons.location.LatLng;
import fr.free.nrw.commons.location.LocationServiceManager;
+import fr.free.nrw.commons.location.LocationServiceManager.LocationChangeType;
import fr.free.nrw.commons.location.LocationUpdateListener;
import fr.free.nrw.commons.theme.NavigationBaseActivity;
import fr.free.nrw.commons.utils.NetworkUtils;
import fr.free.nrw.commons.utils.UriSerializer;
import fr.free.nrw.commons.utils.ViewUtil;
+import fr.free.nrw.commons.wikidata.WikidataEditListener;
import io.reactivex.Observable;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.Disposable;
@@ -55,8 +51,12 @@ import timber.log.Timber;
import uk.co.deanwild.materialshowcaseview.IShowcaseListener;
import uk.co.deanwild.materialshowcaseview.MaterialShowcaseView;
+import static fr.free.nrw.commons.location.LocationServiceManager.LocationChangeType.*;
+import static fr.free.nrw.commons.location.LocationServiceManager.LocationChangeType.MAP_UPDATED;
-public class NearbyActivity extends NavigationBaseActivity implements LocationUpdateListener {
+
+public class NearbyActivity extends NavigationBaseActivity implements LocationUpdateListener,
+ WikidataEditListener.WikidataP18EditListener {
private static final int LOCATION_REQUEST = 1;
@@ -76,6 +76,8 @@ public class NearbyActivity extends NavigationBaseActivity implements LocationUp
LocationServiceManager locationManager;
@Inject
NearbyController nearbyController;
+ @Inject WikidataEditListener wikidataEditListener;
+
@Inject
@Named("application_preferences") SharedPreferences applicationPrefs;
private LatLng curLatLng;
@@ -110,6 +112,7 @@ public class NearbyActivity extends NavigationBaseActivity implements LocationUp
initBottomSheetBehaviour();
initDrawer();
+ wikidataEditListener.setAuthenticationStateListener(this);
}
private void resumeFragment() {
@@ -219,7 +222,7 @@ public class NearbyActivity extends NavigationBaseActivity implements LocationUp
//Still need to check if GPS is enabled
checkGps();
lastKnownLocation = locationManager.getLKL();
- refreshView(LocationServiceManager.LocationChangeType.PERMISSION_JUST_GRANTED);
+ refreshView(PERMISSION_JUST_GRANTED);
} else {
//If permission not granted, go to page that says Nearby Places cannot be displayed
hideProgressBar();
@@ -279,7 +282,7 @@ public class NearbyActivity extends NavigationBaseActivity implements LocationUp
private void checkLocationPermission() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
if (locationManager.isLocationPermissionGranted()) {
- refreshView(LocationServiceManager.LocationChangeType.LOCATION_SIGNIFICANTLY_CHANGED);
+ refreshView(LOCATION_SIGNIFICANTLY_CHANGED);
} else {
// Should we show an explanation?
if (locationManager.isPermissionExplanationRequired(this)) {
@@ -305,7 +308,7 @@ public class NearbyActivity extends NavigationBaseActivity implements LocationUp
}
}
} else {
- refreshView(LocationServiceManager.LocationChangeType.LOCATION_SIGNIFICANTLY_CHANGED);
+ refreshView(LOCATION_SIGNIFICANTLY_CHANGED);
}
}
@@ -314,7 +317,7 @@ public class NearbyActivity extends NavigationBaseActivity implements LocationUp
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == 1) {
Timber.d("User is back from Settings page");
- refreshView(LocationServiceManager.LocationChangeType.LOCATION_SIGNIFICANTLY_CHANGED);
+ refreshView(LOCATION_SIGNIFICANTLY_CHANGED);
}
}
@@ -373,8 +376,7 @@ public class NearbyActivity extends NavigationBaseActivity implements LocationUp
@Override
public void onReceive(Context context, Intent intent) {
if (NetworkUtils.isInternetConnectionEstablished(NearbyActivity.this)) {
- refreshView(LocationServiceManager
- .LocationChangeType.LOCATION_SIGNIFICANTLY_CHANGED);
+ refreshView(LOCATION_SIGNIFICANTLY_CHANGED);
} else {
ViewUtil.showLongToast(NearbyActivity.this, getString(R.string.no_internet));
}
@@ -390,7 +392,7 @@ public class NearbyActivity extends NavigationBaseActivity implements LocationUp
*
* @param locationChangeType defines if location shanged significantly or slightly
*/
- private void refreshView(LocationServiceManager.LocationChangeType locationChangeType) {
+ private void refreshView(LocationChangeType locationChangeType) {
if (lockNearbyView) {
return;
}
@@ -403,12 +405,13 @@ public class NearbyActivity extends NavigationBaseActivity implements LocationUp
registerLocationUpdates();
LatLng lastLocation = locationManager.getLastLocation();
- if (curLatLng != null && curLatLng.equals(lastLocation)) { //refresh view only if location has changed
+ if (curLatLng != null && curLatLng.equals(lastLocation)
+ && !locationChangeType.equals(MAP_UPDATED)) { //refresh view only if location has changed
return;
}
curLatLng = lastLocation;
- if (locationChangeType.equals(LocationServiceManager.LocationChangeType.PERMISSION_JUST_GRANTED)) {
+ if (locationChangeType.equals(PERMISSION_JUST_GRANTED)) {
curLatLng = lastKnownLocation;
}
@@ -417,8 +420,9 @@ public class NearbyActivity extends NavigationBaseActivity implements LocationUp
return;
}
- if (locationChangeType.equals(LocationServiceManager.LocationChangeType.LOCATION_SIGNIFICANTLY_CHANGED)
- || locationChangeType.equals(LocationServiceManager.LocationChangeType.PERMISSION_JUST_GRANTED)) {
+ if (locationChangeType.equals(LOCATION_SIGNIFICANTLY_CHANGED)
+ || locationChangeType.equals(PERMISSION_JUST_GRANTED)
+ || locationChangeType.equals(MAP_UPDATED)) {
progressBar.setVisibility(View.VISIBLE);
//TODO: This hack inserts curLatLng before populatePlaces is called (see #1440). Ideally a proper fix should be found
@@ -440,7 +444,7 @@ public class NearbyActivity extends NavigationBaseActivity implements LocationUp
progressBar.setVisibility(View.GONE);
});
} else if (locationChangeType
- .equals(LocationServiceManager.LocationChangeType.LOCATION_SLIGHTLY_CHANGED)) {
+ .equals(LOCATION_SLIGHTLY_CHANGED)) {
Gson gson = new GsonBuilder()
.registerTypeAdapter(Uri.class, new UriSerializer())
.create();
@@ -685,12 +689,12 @@ public class NearbyActivity extends NavigationBaseActivity implements LocationUp
@Override
public void onLocationChangedSignificantly(LatLng latLng) {
- refreshView(LocationServiceManager.LocationChangeType.LOCATION_SIGNIFICANTLY_CHANGED);
+ refreshView(LOCATION_SIGNIFICANTLY_CHANGED);
}
@Override
public void onLocationChangedSlightly(LatLng latLng) {
- refreshView(LocationServiceManager.LocationChangeType.LOCATION_SLIGHTLY_CHANGED);
+ refreshView(LOCATION_SLIGHTLY_CHANGED);
}
public void prepareViewsForSheetPosition(int bottomSheetState) {
@@ -700,4 +704,9 @@ public class NearbyActivity extends NavigationBaseActivity implements LocationUp
private void showErrorMessage(String message) {
ViewUtil.showLongToast(NearbyActivity.this, message);
}
+
+ @Override
+ public void onWikidataEditSuccessful() {
+ refreshView(MAP_UPDATED);
+ }
}
diff --git a/app/src/main/java/fr/free/nrw/commons/nearby/NearbyListFragment.java b/app/src/main/java/fr/free/nrw/commons/nearby/NearbyListFragment.java
index 1be2a8689..099792bc5 100644
--- a/app/src/main/java/fr/free/nrw/commons/nearby/NearbyListFragment.java
+++ b/app/src/main/java/fr/free/nrw/commons/nearby/NearbyListFragment.java
@@ -2,6 +2,7 @@ package fr.free.nrw.commons.nearby;
import android.content.Context;
import android.content.Intent;
+import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Bundle;
@@ -21,6 +22,9 @@ import java.lang.reflect.Type;
import java.util.Collections;
import java.util.List;
+import javax.inject.Inject;
+import javax.inject.Named;
+
import dagger.android.support.AndroidSupportInjection;
import dagger.android.support.DaggerFragment;
import fr.free.nrw.commons.R;
@@ -47,6 +51,11 @@ public class NearbyListFragment extends DaggerFragment {
private RecyclerView recyclerView;
private ContributionController controller;
+
+ @Inject
+ @Named("direct_nearby_upload_prefs")
+ SharedPreferences directPrefs;
+
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@@ -137,7 +146,7 @@ public class NearbyListFragment extends DaggerFragment {
if (resultCode == RESULT_OK) {
Timber.d("OnActivityResult() parameters: Req code: %d Result code: %d Data: %s",
requestCode, resultCode, data);
- controller.handleImagePicked(requestCode, data, true);
+ controller.handleImagePicked(requestCode, data, true, directPrefs.getString("WikiDataEntityId", null));
} else {
Timber.e("OnActivityResult() parameters: Req code: %d Result code: %d Data: %s",
requestCode, resultCode, data);
diff --git a/app/src/main/java/fr/free/nrw/commons/nearby/NearbyMapFragment.java b/app/src/main/java/fr/free/nrw/commons/nearby/NearbyMapFragment.java
index 69041d286..934d74353 100644
--- a/app/src/main/java/fr/free/nrw/commons/nearby/NearbyMapFragment.java
+++ b/app/src/main/java/fr/free/nrw/commons/nearby/NearbyMapFragment.java
@@ -731,6 +731,7 @@ public class NearbyMapFragment extends DaggerFragment {
editor.putString("Title", place.getName());
editor.putString("Desc", place.getLongDescription());
editor.putString("Category", place.getCategory());
+ editor.putString("WikiDataEntityId", place.getWikiDataEntityId());
editor.apply();
}
@@ -766,7 +767,7 @@ public class NearbyMapFragment extends DaggerFragment {
if (resultCode == RESULT_OK) {
Timber.d("OnActivityResult() parameters: Req code: %d Result code: %d Data: %s",
requestCode, resultCode, data);
- controller.handleImagePicked(requestCode, data, true);
+ controller.handleImagePicked(requestCode, data, true, directPrefs.getString("WikiDataEntityId", null));
} else {
Timber.e("OnActivityResult() parameters: Req code: %d Result code: %d Data: %s",
requestCode, resultCode, data);
diff --git a/app/src/main/java/fr/free/nrw/commons/nearby/NearbyPlaces.java b/app/src/main/java/fr/free/nrw/commons/nearby/NearbyPlaces.java
index d05d81251..c8d20f753 100644
--- a/app/src/main/java/fr/free/nrw/commons/nearby/NearbyPlaces.java
+++ b/app/src/main/java/fr/free/nrw/commons/nearby/NearbyPlaces.java
@@ -17,7 +17,7 @@ import java.util.regex.Pattern;
import fr.free.nrw.commons.Utils;
import fr.free.nrw.commons.location.LatLng;
-import fr.free.nrw.commons.utils.FileUtils;
+import fr.free.nrw.commons.upload.FileUtils;
import timber.log.Timber;
public class NearbyPlaces {
diff --git a/app/src/main/java/fr/free/nrw/commons/nearby/Place.java b/app/src/main/java/fr/free/nrw/commons/nearby/Place.java
index 9c5138245..93075e8fe 100644
--- a/app/src/main/java/fr/free/nrw/commons/nearby/Place.java
+++ b/app/src/main/java/fr/free/nrw/commons/nearby/Place.java
@@ -3,6 +3,7 @@ package fr.free.nrw.commons.nearby;
import android.graphics.Bitmap;
import android.net.Uri;
import android.support.annotation.DrawableRes;
+import android.support.annotation.Nullable;
import java.util.HashMap;
import java.util.Map;
@@ -50,6 +51,20 @@ public class Place {
this.distance = distance;
}
+ /**
+ * Extracts the entity id from the wikidata link
+ * @return returns the entity id if wikidata link exists
+ */
+ @Nullable
+ public String getWikiDataEntityId() {
+ if (!hasWikidataLink()) {
+ return null;
+ }
+
+ String wikiDataLink = siteLinks.getWikidataLink().toString();
+ return wikiDataLink.replace("http://www.wikidata.org/entity/", "");
+ }
+
public boolean hasWikipediaLink() {
return !(siteLinks == null || Uri.EMPTY.equals(siteLinks.getWikipediaLink()));
}
diff --git a/app/src/main/java/fr/free/nrw/commons/nearby/PlaceRenderer.java b/app/src/main/java/fr/free/nrw/commons/nearby/PlaceRenderer.java
index 9cbe28db4..0d33b4a5e 100644
--- a/app/src/main/java/fr/free/nrw/commons/nearby/PlaceRenderer.java
+++ b/app/src/main/java/fr/free/nrw/commons/nearby/PlaceRenderer.java
@@ -25,7 +25,6 @@ import javax.inject.Named;
import butterknife.BindView;
import butterknife.ButterKnife;
-import fr.free.nrw.commons.CommonsApplication;
import fr.free.nrw.commons.R;
import fr.free.nrw.commons.Utils;
import fr.free.nrw.commons.contributions.ContributionController;
diff --git a/app/src/main/java/fr/free/nrw/commons/notification/NotificationActivity.java b/app/src/main/java/fr/free/nrw/commons/notification/NotificationActivity.java
index dc52f198a..b366c944a 100644
--- a/app/src/main/java/fr/free/nrw/commons/notification/NotificationActivity.java
+++ b/app/src/main/java/fr/free/nrw/commons/notification/NotificationActivity.java
@@ -16,7 +16,6 @@ import android.widget.RelativeLayout;
import com.pedrogomez.renderers.RVRendererAdapter;
-import java.lang.ref.WeakReference;
import java.util.Collections;
import java.util.List;
@@ -26,6 +25,7 @@ import butterknife.BindView;
import butterknife.ButterKnife;
import fr.free.nrw.commons.R;
import fr.free.nrw.commons.Utils;
+import fr.free.nrw.commons.mwapi.MediaWikiApi;
import fr.free.nrw.commons.theme.NavigationBaseActivity;
import fr.free.nrw.commons.utils.NetworkUtils;
import fr.free.nrw.commons.utils.ViewUtil;
@@ -46,6 +46,8 @@ public class NotificationActivity extends NavigationBaseActivity {
@BindView(R.id.container) RelativeLayout relativeLayout;
@Inject NotificationController controller;
+ @Inject
+ MediaWikiApi mediaWikiApi;
private static final String TAG_NOTIFICATION_WORKER_FRAGMENT = "NotificationWorkerFragment";
private NotificationWorkerFragment mNotificationWorkerFragment;
@@ -81,7 +83,6 @@ public class NotificationActivity extends NavigationBaseActivity {
}
}
-
@SuppressLint("CheckResult")
private void addNotifications() {
Timber.d("Add notifications");
diff --git a/app/src/main/java/fr/free/nrw/commons/settings/SettingsFragment.java b/app/src/main/java/fr/free/nrw/commons/settings/SettingsFragment.java
index 6dd6056f7..d35170adf 100644
--- a/app/src/main/java/fr/free/nrw/commons/settings/SettingsFragment.java
+++ b/app/src/main/java/fr/free/nrw/commons/settings/SettingsFragment.java
@@ -3,13 +3,10 @@ package fr.free.nrw.commons.settings;
import android.Manifest;
import android.app.AlertDialog;
import android.content.ActivityNotFoundException;
-import android.content.ComponentName;
import android.content.Context;
-import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
@@ -24,8 +21,6 @@ import android.support.v4.content.FileProvider;
import android.widget.Toast;
import java.io.File;
-import java.util.ArrayList;
-import java.util.List;
import javax.inject.Inject;
import javax.inject.Named;
@@ -35,7 +30,7 @@ import fr.free.nrw.commons.CommonsApplication;
import fr.free.nrw.commons.R;
import fr.free.nrw.commons.Utils;
import fr.free.nrw.commons.di.ApplicationlessInjection;
-import fr.free.nrw.commons.utils.FileUtils;
+import fr.free.nrw.commons.upload.FileUtils;
public class SettingsFragment extends PreferenceFragment {
diff --git a/app/src/main/java/fr/free/nrw/commons/upload/DetectUnwantedPicturesAsync.java b/app/src/main/java/fr/free/nrw/commons/upload/DetectUnwantedPicturesAsync.java
index ab9fa5602..5a413e49a 100644
--- a/app/src/main/java/fr/free/nrw/commons/upload/DetectUnwantedPicturesAsync.java
+++ b/app/src/main/java/fr/free/nrw/commons/upload/DetectUnwantedPicturesAsync.java
@@ -1,10 +1,8 @@
package fr.free.nrw.commons.upload;
import android.app.Activity;
-import android.content.Context;
import android.content.Intent;
import android.graphics.BitmapRegionDecoder;
-import android.net.Uri;
import android.os.AsyncTask;
import android.support.v7.app.AlertDialog;
diff --git a/app/src/main/java/fr/free/nrw/commons/upload/FileProcessor.java b/app/src/main/java/fr/free/nrw/commons/upload/FileProcessor.java
new file mode 100644
index 000000000..2845b8d1f
--- /dev/null
+++ b/app/src/main/java/fr/free/nrw/commons/upload/FileProcessor.java
@@ -0,0 +1,263 @@
+package fr.free.nrw.commons.upload;
+
+import android.annotation.SuppressLint;
+import android.app.Activity;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.net.Uri;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.ParcelFileDescriptor;
+import android.support.annotation.Nullable;
+import android.support.v7.app.AppCompatActivity;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.lang.ref.WeakReference;
+import java.util.Date;
+import java.util.List;
+
+import javax.inject.Inject;
+import javax.inject.Named;
+
+import fr.free.nrw.commons.caching.CacheController;
+import fr.free.nrw.commons.di.ApplicationlessInjection;
+import fr.free.nrw.commons.mwapi.CategoryApi;
+import io.reactivex.schedulers.Schedulers;
+import timber.log.Timber;
+
+import static com.mapbox.mapboxsdk.Mapbox.getApplicationContext;
+
+/**
+ * Processing of the image file that is about to be uploaded via ShareActivity is done here
+ */
+public class FileProcessor implements SimilarImageDialogFragment.onResponse {
+
+ @Inject
+ CacheController cacheController;
+ @Inject
+ GpsCategoryModel gpsCategoryModel;
+ @Inject
+ CategoryApi apiCall;
+ @Inject
+ @Named("default_preferences")
+ SharedPreferences prefs;
+ private Uri mediaUri;
+ private ContentResolver contentResolver;
+ private GPSExtractor imageObj;
+ private Context context;
+ private String decimalCoords;
+ private boolean haveCheckedForOtherImages = false;
+ private String filePath;
+ private boolean useExtStorage;
+ private boolean cacheFound;
+ private GPSExtractor tempImageObj;
+
+ FileProcessor(Uri mediaUri, ContentResolver contentResolver, Context context) {
+ this.mediaUri = mediaUri;
+ this.contentResolver = contentResolver;
+ this.context = context;
+ ApplicationlessInjection.getInstance(context.getApplicationContext()).getCommonsApplicationComponent().inject(this);
+ useExtStorage = prefs.getBoolean("useExternalStorage", true);
+ }
+
+ /**
+ * Gets file path from media URI.
+ * In older devices getPath() may fail depending on the source URI, creating and using a copy of the file seems to work instead.
+ *
+ * @return file path of media
+ */
+ @Nullable
+ private String getPathOfMediaOrCopy() {
+ filePath = FileUtils.getPath(context, mediaUri);
+ Timber.d("Filepath: " + filePath);
+ if (filePath == null) {
+ String copyPath = null;
+ try {
+ ParcelFileDescriptor descriptor = contentResolver.openFileDescriptor(mediaUri, "r");
+ if (descriptor != null) {
+ if (useExtStorage) {
+ copyPath = FileUtils.createCopyPath(descriptor);
+ return copyPath;
+ }
+ copyPath = getApplicationContext().getCacheDir().getAbsolutePath() + "/" + new Date().getTime() + ".jpg";
+ FileUtils.copy(descriptor.getFileDescriptor(), copyPath);
+ Timber.d("Filepath (copied): %s", copyPath);
+ return copyPath;
+ }
+ } catch (IOException e) {
+ Timber.w(e, "Error in file " + copyPath);
+ return null;
+ }
+ }
+ return filePath;
+ }
+
+ /**
+ * Processes file coordinates, either from EXIF data or user location
+ *
+ * @param gpsEnabled if true use GPS
+ */
+ GPSExtractor processFileCoordinates(boolean gpsEnabled) {
+ Timber.d("Calling GPSExtractor");
+ try {
+ ParcelFileDescriptor descriptor = contentResolver.openFileDescriptor(mediaUri, "r");
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
+ if (descriptor != null) {
+ imageObj = new GPSExtractor(descriptor.getFileDescriptor(), context, prefs);
+ }
+ } else {
+ String filePath = getPathOfMediaOrCopy();
+ if (filePath != null) {
+ imageObj = new GPSExtractor(filePath, context, prefs);
+ }
+ }
+
+ decimalCoords = imageObj.getCoords(gpsEnabled);
+ if (decimalCoords == null || !imageObj.imageCoordsExists) {
+ //Find other photos taken around the same time which has gps coordinates
+ if (!haveCheckedForOtherImages)
+ findOtherImages(gpsEnabled);// Do not do repeat the process
+ } else {
+ useImageCoords();
+ }
+
+ } catch (FileNotFoundException e) {
+ Timber.w("File not found: " + mediaUri, e);
+ }
+ return imageObj;
+ }
+
+ String getDecimalCoords() {
+ return decimalCoords;
+ }
+
+ /**
+ * Find other images around the same location that were taken within the last 20 sec
+ *
+ * @param gpsEnabled True if GPS is enabled
+ */
+ private void findOtherImages(boolean gpsEnabled) {
+ Timber.d("filePath" + getPathOfMediaOrCopy());
+
+ long timeOfCreation = new File(filePath).lastModified();//Time when the original image was created
+ File folder = new File(filePath.substring(0, filePath.lastIndexOf('/')));
+ File[] files = folder.listFiles();
+ Timber.d("folderTime Number:" + files.length);
+
+
+ for (File file : files) {
+ if (file.lastModified() - timeOfCreation <= (120 * 1000) && file.lastModified() - timeOfCreation >= -(120 * 1000)) {
+ //Make sure the photos were taken within 20seconds
+ Timber.d("fild date:" + file.lastModified() + " time of creation" + timeOfCreation);
+ tempImageObj = null;//Temporary GPSExtractor to extract coords from these photos
+ ParcelFileDescriptor descriptor = null;
+ try {
+ descriptor = contentResolver.openFileDescriptor(Uri.parse(file.getAbsolutePath()), "r");
+ } catch (FileNotFoundException e) {
+ e.printStackTrace();
+ }
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
+ if (descriptor != null) {
+ tempImageObj = new GPSExtractor(descriptor.getFileDescriptor(), context, prefs);
+ }
+ } else {
+ if (filePath != null) {
+ tempImageObj = new GPSExtractor(file.getAbsolutePath(), context, prefs);
+ }
+ }
+
+ if (tempImageObj != null) {
+ Timber.d("not null fild EXIF" + tempImageObj.imageCoordsExists + " coords" + tempImageObj.getCoords(gpsEnabled));
+ if (tempImageObj.getCoords(gpsEnabled) != null && tempImageObj.imageCoordsExists) {
+ // Current image has gps coordinates and it's not current gps locaiton
+ Timber.d("This file has image coords:" + file.getAbsolutePath());
+ SimilarImageDialogFragment newFragment = new SimilarImageDialogFragment();
+ Bundle args = new Bundle();
+ args.putString("originalImagePath", filePath);
+ args.putString("possibleImagePath", file.getAbsolutePath());
+ newFragment.setArguments(args);
+ newFragment.show(((AppCompatActivity) context).getSupportFragmentManager(), "dialog");
+ break;
+ }
+ }
+ }
+ }
+ haveCheckedForOtherImages = true; //Finished checking for other images
+ }
+
+ /**
+ * Initiates retrieval of image coordinates or user coordinates, and caching of coordinates.
+ * Then initiates the calls to MediaWiki API through an instance of CategoryApi.
+ */
+ @SuppressLint("CheckResult")
+ public void useImageCoords() {
+ if (decimalCoords != null) {
+ Timber.d("Decimal coords of image: %s", decimalCoords);
+ Timber.d("is EXIF data present:" + imageObj.imageCoordsExists + " from findOther image");
+
+ // Only set cache for this point if image has coords
+ if (imageObj.imageCoordsExists) {
+ double decLongitude = imageObj.getDecLongitude();
+ double decLatitude = imageObj.getDecLatitude();
+ cacheController.setQtPoint(decLongitude, decLatitude);
+ }
+
+ List displayCatList = cacheController.findCategory();
+ boolean catListEmpty = displayCatList.isEmpty();
+
+
+ // If no categories found in cache, call MediaWiki API to match image coords with nearby Commons categories
+ if (catListEmpty) {
+ cacheFound = false;
+ apiCall.request(decimalCoords)
+ .subscribeOn(Schedulers.io())
+ .observeOn(Schedulers.io())
+ .subscribe(
+ gpsCategoryModel::setCategoryList,
+ throwable -> {
+ Timber.e(throwable);
+ gpsCategoryModel.clear();
+ }
+ );
+ Timber.d("displayCatList size 0, calling MWAPI %s", displayCatList);
+ } else {
+ cacheFound = true;
+ Timber.d("Cache found, setting categoryList in model to %s", displayCatList);
+ gpsCategoryModel.setCategoryList(displayCatList);
+ }
+ } else {
+ Timber.d("EXIF: no coords");
+ }
+ }
+
+ boolean isCacheFound() {
+ return cacheFound;
+ }
+
+ /**
+ * Calls the async task that detects if image is fuzzy, too dark, etc
+ */
+ void detectUnwantedPictures() {
+ String imageMediaFilePath = FileUtils.getPath(context, mediaUri);
+ DetectUnwantedPicturesAsync detectUnwantedPicturesAsync
+ = new DetectUnwantedPicturesAsync(new WeakReference((Activity) context), imageMediaFilePath);
+ detectUnwantedPicturesAsync.execute();
+ }
+
+ @Override
+ public void onPositiveResponse() {
+ imageObj = tempImageObj;
+ decimalCoords = imageObj.getCoords(false);// Not necessary to use gps as image already ha EXIF data
+ Timber.d("EXIF from tempImageObj");
+ useImageCoords();
+ }
+
+ @Override
+ public void onNegativeResponse() {
+ Timber.d("EXIF from imageObj");
+ useImageCoords();
+ }
+}
diff --git a/app/src/main/java/fr/free/nrw/commons/upload/FileUtils.java b/app/src/main/java/fr/free/nrw/commons/upload/FileUtils.java
index 612b86458..0cd45c189 100644
--- a/app/src/main/java/fr/free/nrw/commons/upload/FileUtils.java
+++ b/app/src/main/java/fr/free/nrw/commons/upload/FileUtils.java
@@ -15,18 +15,84 @@ import android.provider.MediaStore;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
+import java.io.BufferedReader;
import java.io.File;
import java.io.FileDescriptor;
import java.io.FileInputStream;
+import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStreamWriter;
+import java.math.BigInteger;
import java.nio.channels.FileChannel;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
import java.util.Date;
import timber.log.Timber;
public class FileUtils {
+ /**
+ * Get SHA1 of file from input stream
+ */
+ static String getSHA1(InputStream is) {
+
+ MessageDigest digest;
+ try {
+ digest = MessageDigest.getInstance("SHA1");
+ } catch (NoSuchAlgorithmException e) {
+ Timber.e(e, "Exception while getting Digest");
+ return "";
+ }
+
+ byte[] buffer = new byte[8192];
+ int read;
+ try {
+ while ((read = is.read(buffer)) > 0) {
+ digest.update(buffer, 0, read);
+ }
+ byte[] md5sum = digest.digest();
+ BigInteger bigInt = new BigInteger(1, md5sum);
+ String output = bigInt.toString(16);
+ // Fill to 40 chars
+ output = String.format("%40s", output).replace(' ', '0');
+ Timber.i("File SHA1: %s", output);
+
+ return output;
+ } catch (IOException e) {
+ Timber.e(e, "IO Exception");
+ return "";
+ } finally {
+ try {
+ is.close();
+ } catch (IOException e) {
+ Timber.e(e, "Exception on closing MD5 input stream");
+ }
+ }
+ }
+
+ /**
+ * In older devices getPath() may fail depending on the source URI. Creating and using a copy of the file seems to work instead.
+ * @return path of copy
+ */
+ @Nullable
+ static String createCopyPath(ParcelFileDescriptor descriptor) {
+ try {
+ String copyPath = Environment.getExternalStorageDirectory().toString() + "/CommonsApp/" + new Date().getTime() + ".jpg";
+ File newFile = new File(Environment.getExternalStorageDirectory().toString() + "/CommonsApp");
+ newFile.mkdir();
+ FileUtils.copy(descriptor.getFileDescriptor(), copyPath);
+ Timber.d("Filepath (copied): %s", copyPath);
+ return copyPath;
+ } catch (IOException e) {
+ Timber.e(e);
+ return null;
+ }
+ }
+
/**
* Get a file path from a Uri. This will get the the path for Storage Access
* Framework Documents, as well as the _data field for the MediaStore and
@@ -235,4 +301,80 @@ public class FileUtils {
copy(new FileInputStream(source), new FileOutputStream(destination));
}
+
+ /**
+ * Read and return the content of a resource file as string.
+ * @param fileName asset file's path (e.g. "/queries/nearby_query.rq")
+ * @return the content of the file
+ */
+ public static String readFromResource(String fileName) throws IOException {
+ StringBuilder buffer = new StringBuilder();
+ BufferedReader reader = null;
+ try {
+ InputStream inputStream = FileUtils.class.getResourceAsStream(fileName);
+ if (inputStream == null) {
+ throw new FileNotFoundException(fileName);
+ }
+ reader = new BufferedReader(new InputStreamReader(inputStream, "UTF-8"));
+ String line;
+ while ((line = reader.readLine()) != null) {
+ buffer.append(line).append("\n");
+ }
+ } finally {
+ if (reader != null) {
+ reader.close();
+ }
+ }
+ return buffer.toString();
+ }
+
+ /**
+ * Deletes files.
+ * @param file context
+ */
+ public static boolean deleteFile(File file) {
+ boolean deletedAll = true;
+ if (file != null) {
+ if (file.isDirectory()) {
+ String[] children = file.list();
+ for (String child : children) {
+ deletedAll = deleteFile(new File(file, child)) && deletedAll;
+ }
+ } else {
+ deletedAll = file.delete();
+ }
+ }
+
+ return deletedAll;
+ }
+
+ public static File createAndGetAppLogsFile(String logs) {
+ try {
+ File commonsAppDirectory = new File(Environment.getExternalStorageDirectory().toString() + "/CommonsApp");
+ if (!commonsAppDirectory.exists()) {
+ commonsAppDirectory.mkdir();
+ }
+
+ File logsFile = new File(commonsAppDirectory,"logs.txt");
+ if (logsFile.exists()) {
+ //old logs file is useless
+ logsFile.delete();
+ }
+
+ logsFile.createNewFile();
+
+ FileOutputStream outputStream = new FileOutputStream(logsFile);
+ OutputStreamWriter outputStreamWriter = new OutputStreamWriter(outputStream);
+ outputStreamWriter.append(logs);
+ outputStreamWriter.close();
+ outputStream.flush();
+ outputStream.close();
+
+ return logsFile;
+ } catch (IOException ioe) {
+ Timber.e(ioe);
+ return null;
+ }
+ }
+
}
\ No newline at end of file
diff --git a/app/src/main/java/fr/free/nrw/commons/upload/MultipleShareActivity.java b/app/src/main/java/fr/free/nrw/commons/upload/MultipleShareActivity.java
index 9c31e2b4d..58d6d61ca 100644
--- a/app/src/main/java/fr/free/nrw/commons/upload/MultipleShareActivity.java
+++ b/app/src/main/java/fr/free/nrw/commons/upload/MultipleShareActivity.java
@@ -47,6 +47,8 @@ import fr.free.nrw.commons.modifications.TemplateRemoveModifier;
import fr.free.nrw.commons.mwapi.MediaWikiApi;
import timber.log.Timber;
+//TODO: We should use this class to see how multiple uploads are handled, and then REMOVE it.
+
public class MultipleShareActivity extends AuthenticatedActivity
implements MediaDetailPagerFragment.MediaDetailProvider,
AdapterView.OnItemClickListener,
diff --git a/app/src/main/java/fr/free/nrw/commons/upload/MultipleUploadListFragment.java b/app/src/main/java/fr/free/nrw/commons/upload/MultipleUploadListFragment.java
index 089f1ac46..028456bb6 100644
--- a/app/src/main/java/fr/free/nrw/commons/upload/MultipleUploadListFragment.java
+++ b/app/src/main/java/fr/free/nrw/commons/upload/MultipleUploadListFragment.java
@@ -1,6 +1,5 @@
package fr.free.nrw.commons.upload;
-import android.app.Activity;
import android.content.Context;
import android.graphics.Point;
import android.net.Uri;
@@ -17,7 +16,6 @@ import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
-import android.view.inputmethod.InputMethodManager;
import android.widget.AdapterView;
import android.widget.BaseAdapter;
import android.widget.EditText;
diff --git a/app/src/main/java/fr/free/nrw/commons/upload/ShareActivity.java b/app/src/main/java/fr/free/nrw/commons/upload/ShareActivity.java
index 98d11c20e..5db101862 100644
--- a/app/src/main/java/fr/free/nrw/commons/upload/ShareActivity.java
+++ b/app/src/main/java/fr/free/nrw/commons/upload/ShareActivity.java
@@ -1,67 +1,53 @@
package fr.free.nrw.commons.upload;
import android.Manifest;
-import android.annotation.SuppressLint;
-import android.app.Activity;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
+import android.app.Activity;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.graphics.Bitmap;
-import android.graphics.BitmapRegionDecoder;
import android.graphics.Point;
import android.graphics.Rect;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
-import android.os.ParcelFileDescriptor;
-import android.provider.MediaStore;
import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
import android.support.annotation.RequiresApi;
import android.support.design.widget.FloatingActionButton;
import android.support.design.widget.Snackbar;
import android.support.graphics.drawable.VectorDrawableCompat;
import android.support.v4.app.ActivityCompat;
-import android.support.v4.app.FragmentManager;
import android.support.v4.content.ContextCompat;
-import android.support.v4.graphics.BitmapCompat;
-import android.util.Log;
+import android.view.KeyEvent;
import android.view.MenuItem;
import android.view.View;
import android.view.animation.DecelerateInterpolator;
import android.widget.FrameLayout;
-import android.widget.TextView;
import android.widget.Toast;
-import butterknife.BindView;
-import butterknife.OnClick;
import com.facebook.drawee.generic.GenericDraweeHierarchyBuilder;
import com.facebook.drawee.view.SimpleDraweeView;
import com.github.chrisbanes.photoview.PhotoView;
-
-import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.lang.ref.WeakReference;
-import java.math.BigInteger;
-import java.security.MessageDigest;
-import java.security.NoSuchAlgorithmException;
-import java.util.Date;
import java.util.List;
import javax.inject.Inject;
import javax.inject.Named;
+import butterknife.BindView;
import butterknife.ButterKnife;
+import butterknife.OnClick;
import fr.free.nrw.commons.R;
import fr.free.nrw.commons.auth.AuthenticatedActivity;
import fr.free.nrw.commons.auth.SessionManager;
@@ -75,14 +61,13 @@ import fr.free.nrw.commons.modifications.ModifierSequence;
import fr.free.nrw.commons.modifications.ModifierSequenceDao;
import fr.free.nrw.commons.modifications.TemplateRemoveModifier;
import fr.free.nrw.commons.mwapi.CategoryApi;
-
import fr.free.nrw.commons.mwapi.MediaWikiApi;
-import io.reactivex.schedulers.Schedulers;
import fr.free.nrw.commons.utils.ViewUtil;
import timber.log.Timber;
import static fr.free.nrw.commons.upload.ExistingFileAsync.Result.DUPLICATE_PROCEED;
import static fr.free.nrw.commons.upload.ExistingFileAsync.Result.NO_DUPLICATE;
+import static fr.free.nrw.commons.upload.FileUtils.getSHA1;
/**
* Activity for the title/desc screen after image is selected. Also starts processing image
@@ -91,30 +76,13 @@ import static fr.free.nrw.commons.upload.ExistingFileAsync.Result.NO_DUPLICATE;
public class ShareActivity
extends AuthenticatedActivity
implements SingleUploadFragment.OnUploadActionInitiated,
- OnCategoriesSaveHandler,SimilarImageDialogFragment.onResponse {
-
- @BindView(R.id.container)
- FrameLayout flContainer;
- @BindView(R.id.backgroundImage)
- SimpleDraweeView backgroundImageView;
- @BindView(R.id.media_map)
- FloatingActionButton mapsFragment; //Lets stick to camelCase
- @BindView(R.id.media_upload_zoom_in)
- FloatingActionButton zoomInButton;
- @BindView(R.id.media_upload_zoom_out)
- FloatingActionButton zoomOutButton;
- @BindView(R.id.main_fab)
- FloatingActionButton mainFab;
- @BindView(R.id.expanded_image)
- PhotoView expandedImageView;
-
-
- private static final int REQUEST_PERM_ON_CREATE_STORAGE = 1;
+ OnCategoriesSaveHandler {
private static final int REQUEST_PERM_ON_CREATE_LOCATION = 2;
- private static final int REQUEST_PERM_ON_CREATE_STORAGE_AND_LOCATION = 3;
private static final int REQUEST_PERM_ON_SUBMIT_STORAGE = 4;
- private CategorizationFragment categorizationFragment;
-
+ //Had to make them class variables, to extract out the click listeners, also I see no harm in this
+ final Rect startBounds = new Rect();
+ final Rect finalBounds = new Rect();
+ final Point globalOffset = new Point();
@Inject
MediaWikiApi mwApi;
@Inject
@@ -133,42 +101,48 @@ public class ShareActivity
@Inject
GpsCategoryModel gpsCategoryModel;
+ @BindView(R.id.container)
+ FrameLayout flContainer;
+ @BindView(R.id.backgroundImage)
+ SimpleDraweeView backgroundImageView;
+ @BindView(R.id.media_map)
+ FloatingActionButton mapButton;
+ @BindView(R.id.media_upload_zoom_in)
+ FloatingActionButton zoomInButton;
+ @BindView(R.id.media_upload_zoom_out)
+ FloatingActionButton zoomOutButton;
+ @BindView(R.id.main_fab)
+ FloatingActionButton mainFab;
+ @BindView(R.id.expanded_image)
+ PhotoView expandedImageView;
+
private String source;
private String mimeType;
-
+ private CategorizationFragment categorizationFragment;
private Uri mediaUri;
private Contribution contribution;
- private boolean cacheFound;
-
- private GPSExtractor imageObj;
- private GPSExtractor tempImageObj;
+ private GPSExtractor gpsObj;
private String decimalCoords;
-
+ private FileProcessor fileObj;
private boolean useNewPermissions = false;
private boolean storagePermitted = false;
private boolean locationPermitted = false;
-
private String title;
private String description;
+ private String wikiDataEntityId;
private Snackbar snackbar;
private boolean duplicateCheckPassed = false;
-
- private boolean haveCheckedForOtherImages = false;
private boolean isNearbyUpload = false;
-
private Animator CurrentAnimator;
private long ShortAnimationDuration;
private boolean isFABOpen = false;
-
- //Had to make them class variables, to extract out the click listeners, also I see no harm in this
- final Rect startBounds = new Rect();
- final Rect finalBounds = new Rect();
- final Point globalOffset = new Point();
private float startScaleFinal;
+ private boolean isZoom = false;
/**
* Called when user taps the submit button.
+ * Requests Storage permission, if needed.
*/
@Override
public void uploadActionInitiated(String title, String description) {
@@ -177,8 +151,6 @@ public class ShareActivity
this.description = description;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
- // Check for Storage permission that is required for upload.
- // Do not allow user to proceed without permission, otherwise will crash
if (needsToRequestStoragePermission()) {
requestPermissions(new String[]{Manifest.permission.READ_EXTERNAL_STORAGE},
REQUEST_PERM_ON_SUBMIT_STORAGE);
@@ -190,34 +162,44 @@ public class ShareActivity
}
}
+ /**
+ * Checks whether storage permissions need to be requested.
+ * Permissions are needed if the file is not owned by this application, (e.g. shared from the Gallery)
+ *
+ * @return true if file is not owned by this application and permission hasn't been granted beforehand
+ */
@RequiresApi(16)
private boolean needsToRequestStoragePermission() {
- // We need to ask storage permission when
- // the file is not owned by this application, (e.g. shared from the Gallery)
- // and permission is not obtained.
return !FileUtils.isSelfOwned(getApplicationContext(), mediaUri)
&& (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE)
!= PackageManager.PERMISSION_GRANTED);
}
+ /**
+ * Called after permission checks are done.
+ * Gets file metadata for category suggestions, displays toast, caches categories found, calls uploadController
+ */
private void uploadBegins() {
- getFileMetadata(locationPermitted);
+ fileObj.processFileCoordinates(locationPermitted);
Toast startingToast = Toast.makeText(this, R.string.uploading_started, Toast.LENGTH_LONG);
startingToast.show();
- if (!cacheFound) {
+ if (!fileObj.isCacheFound()) {
//Has to be called after apiCall.request()
cacheController.cacheCategory();
Timber.d("Cache the categories found");
}
- uploadController.startUpload(title, mediaUri, description, mimeType, source, decimalCoords, c -> {
+ uploadController.startUpload(title, mediaUri, description, mimeType, source, decimalCoords, wikiDataEntityId, c -> {
ShareActivity.this.contribution = c;
showPostUpload();
});
}
+ /**
+ * Starts CategorizationFragment after uploadBegins.
+ */
private void showPostUpload() {
if (categorizationFragment == null) {
categorizationFragment = new CategorizationFragment();
@@ -227,6 +209,11 @@ public class ShareActivity
.commit();
}
+ /**
+ * Send categories to modifications queue after they are selected
+ *
+ * @param categories categories selected
+ */
@Override
public void onCategoriesSave(List categories) {
if (categories.size() > 0) {
@@ -264,9 +251,6 @@ public class ShareActivity
finish();
}
- protected boolean isNearbyUpload() {
- return isNearbyUpload;
- }
@Override
public void onCreate(Bundle savedInstanceState) {
@@ -283,7 +267,54 @@ public class ShareActivity
R.drawable.ic_error_outline_black_24dp, getTheme()))
.build());
- //Receive intent from ContributionController.java when user selects picture to upload
+ receiveImageIntent();
+
+ if (savedInstanceState != null) {
+ contribution = savedInstanceState.getParcelable("contribution");
+ }
+
+ requestAuthToken();
+ Timber.d("Uri: %s", mediaUri.toString());
+ Timber.d("Ext storage dir: %s", Environment.getExternalStorageDirectory());
+
+ useNewPermissions = false;
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
+ useNewPermissions = true;
+ if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED) {
+ locationPermitted = true;
+ }
+ }
+
+ // Check location permissions if M or newer for category suggestions, request via snackbar if not present
+ if (!locationPermitted) {
+ requestPermissionUsingSnackBar(
+ getString(R.string.location_permission_rationale),
+ new String[]{Manifest.permission.ACCESS_FINE_LOCATION},
+ REQUEST_PERM_ON_CREATE_LOCATION);
+ }
+
+ SingleUploadFragment shareView = (SingleUploadFragment) getSupportFragmentManager().findFragmentByTag("shareView");
+ categorizationFragment = (CategorizationFragment) getSupportFragmentManager().findFragmentByTag("categorization");
+ if (shareView == null && categorizationFragment == null) {
+ shareView = new SingleUploadFragment();
+ getSupportFragmentManager()
+ .beginTransaction()
+ .add(R.id.single_upload_fragment_container, shareView, "shareView")
+ .commitAllowingStateLoss();
+ }
+ uploadController.prepareService();
+
+ ContentResolver contentResolver = this.getContentResolver();
+ fileObj = new FileProcessor(mediaUri, contentResolver, this);
+ checkIfFileExists();
+ gpsObj = fileObj.processFileCoordinates(locationPermitted);
+ decimalCoords = fileObj.getDecimalCoords();
+ }
+
+ /**
+ * Receive intent from ContributionController.java when user selects picture to upload
+ */
+ private void receiveImageIntent() {
Intent intent = getIntent();
if (Intent.ACTION_SEND.equals(intent.getAction())) {
@@ -303,231 +334,100 @@ public class ShareActivity
if (mediaUri != null) {
backgroundImageView.setImageURI(mediaUri);
}
- if (savedInstanceState != null) {
- contribution = savedInstanceState.getParcelable("contribution");
- }
-
- requestAuthToken();
-
- Timber.d("Uri: %s", mediaUri.toString());
- Timber.d("Ext storage dir: %s", Environment.getExternalStorageDirectory());
-
- useNewPermissions = false;
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
- useNewPermissions = true;
-
- if (!needsToRequestStoragePermission()) {
- storagePermitted = true;
- }
- if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED) {
- locationPermitted = true;
- }
- }
-
- // Check storage permissions if marshmallow or newer
- if (useNewPermissions && (!storagePermitted || !locationPermitted)) {
- if (!storagePermitted && !locationPermitted) {
- String permissionRationales =
- getResources().getString(R.string.read_storage_permission_rationale) + "\n"
- + getResources().getString(R.string.location_permission_rationale);
- snackbar = requestPermissionUsingSnackBar(
- permissionRationales,
- new String[]{
- Manifest.permission.READ_EXTERNAL_STORAGE,
- Manifest.permission.ACCESS_FINE_LOCATION},
- REQUEST_PERM_ON_CREATE_STORAGE_AND_LOCATION);
- View snackbarView = snackbar.getView();
- TextView textView = (TextView) snackbarView.findViewById(android.support.design.R.id.snackbar_text);
- textView.setMaxLines(3);
- } else if (!storagePermitted) {
- requestPermissionUsingSnackBar(
- getString(R.string.read_storage_permission_rationale),
- new String[]{Manifest.permission.READ_EXTERNAL_STORAGE},
- REQUEST_PERM_ON_CREATE_STORAGE);
- } else if (!locationPermitted) {
- requestPermissionUsingSnackBar(
- getString(R.string.location_permission_rationale),
- new String[]{Manifest.permission.ACCESS_FINE_LOCATION},
- REQUEST_PERM_ON_CREATE_LOCATION);
- }
- }
- performPreUploadProcessingOfFile();
-
-
- SingleUploadFragment shareView = (SingleUploadFragment) getSupportFragmentManager().findFragmentByTag("shareView");
- categorizationFragment = (CategorizationFragment) getSupportFragmentManager().findFragmentByTag("categorization");
- if (shareView == null && categorizationFragment == null) {
- shareView = new SingleUploadFragment();
- getSupportFragmentManager()
- .beginTransaction()
- .add(R.id.single_upload_fragment_container, shareView, "shareView")
- .commitAllowingStateLoss();
- }
- uploadController.prepareService();
- mapsFragment.setVisibility(View.VISIBLE);
- if( imageObj == null || imageObj.imageCoordsExists != true){
- mapsFragment.setVisibility(View.INVISIBLE);
- }
-
}
- /*
+ /**
* Function to display the zoom and map FAB
*/
- private void showFABMenu(){
- isFABOpen=true;
+ private void showFABMenu() {
+ isFABOpen = true;
- if( imageObj != null && imageObj.imageCoordsExists == true)
- mapsFragment.setVisibility(View.VISIBLE);
+ if (gpsObj != null && gpsObj.imageCoordsExists)
+ mapButton.setVisibility(View.VISIBLE);
zoomInButton.setVisibility(View.VISIBLE);
mainFab.animate().rotationBy(180);
- mapsFragment.animate().translationY(-getResources().getDimension(R.dimen.second_fab));
+ mapButton.animate().translationY(-getResources().getDimension(R.dimen.second_fab));
zoomInButton.animate().translationY(-getResources().getDimension(R.dimen.first_fab));
}
- /*
- * function to close the zoom and map FAB
+ /**
+ * Function to close the zoom and map FAB
*/
- private void closeFABMenu(){
- isFABOpen=false;
+ private void closeFABMenu() {
+ isFABOpen = false;
mainFab.animate().rotationBy(-180);
- mapsFragment.animate().translationY(0);
+ mapButton.animate().translationY(0);
zoomInButton.animate().translationY(0).setListener(new Animator.AnimatorListener() {
@Override
public void onAnimationStart(Animator animator) {
-
}
@Override
public void onAnimationEnd(Animator animator) {
- if(!isFABOpen){
- mapsFragment.setVisibility(View.GONE);
+ if (!isFABOpen) {
+ mapButton.setVisibility(View.GONE);
zoomInButton.setVisibility(View.GONE);
}
-
}
@Override
public void onAnimationCancel(Animator animator) {
-
}
@Override
public void onAnimationRepeat(Animator animator) {
-
}
});
}
+ /**
+ * Checks if upload was initiated via Nearby
+ *
+ * @return true if upload was initiated via Nearby
+ */
+ protected boolean isNearbyUpload() {
+ return isNearbyUpload;
+ }
+ /**
+ * Handles BOTH snackbar permission request (for location) and submit button permission request (for storage)
+ *
+ * @param requestCode type of request
+ * @param permissions permissions requested
+ * @param grantResults grant results
+ */
@Override
- public void onRequestPermissionsResult(int requestCode,
- @NonNull String[] permissions, @NonNull int[] grantResults) {
+ public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
switch (requestCode) {
- case REQUEST_PERM_ON_CREATE_STORAGE: {
- if (grantResults.length >= 1
- && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
- backgroundImageView.setImageURI(mediaUri);
- storagePermitted = true;
- performPreUploadProcessingOfFile();
- }
- return;
- }
case REQUEST_PERM_ON_CREATE_LOCATION: {
- if (grantResults.length >= 1
- && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
+ if (grantResults.length >= 1 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
locationPermitted = true;
- performPreUploadProcessingOfFile();
- }
- return;
- }
- case REQUEST_PERM_ON_CREATE_STORAGE_AND_LOCATION: {
- if (grantResults.length >= 2
- && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
- backgroundImageView.setImageURI(mediaUri);
- storagePermitted = true;
- performPreUploadProcessingOfFile();
- }
- if (grantResults.length >= 2
- && grantResults[1] == PackageManager.PERMISSION_GRANTED) {
- locationPermitted = true;
- performPreUploadProcessingOfFile();
+ checkIfFileExists();
}
return;
}
+
// Storage (from submit button) - this needs to be separate from (1) because only the
// submit button should bring user to next screen
case REQUEST_PERM_ON_SUBMIT_STORAGE: {
- if (grantResults.length >= 1
- && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
+ if (grantResults.length >= 1 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
//It is OK to call this at both (1) and (4) because if perm had been granted at
//snackbar, user should not be prompted at submit button
- performPreUploadProcessingOfFile();
+ checkIfFileExists();
//Uploading only begins if storage permission granted from arrow icon
uploadBegins();
snackbar.dismiss();
}
- return;
}
}
}
- private void performPreUploadProcessingOfFile() {
- if (!useNewPermissions || storagePermitted) {
- if (!duplicateCheckPassed) {
- //Test SHA1 of image to see if it matches SHA1 of a file on Commons
- try {
- InputStream inputStream = getContentResolver().openInputStream(mediaUri);
- Timber.d("Input stream created from %s", mediaUri.toString());
- String fileSHA1 = getSHA1(inputStream);
- Timber.d("File SHA1 is: %s", fileSHA1);
-
- ExistingFileAsync fileAsyncTask =
- new ExistingFileAsync(new WeakReference(this), fileSHA1, new WeakReference(this), result -> {
- Timber.d("%s duplicate check: %s", mediaUri.toString(), result);
- duplicateCheckPassed = (result == DUPLICATE_PROCEED
- || result == NO_DUPLICATE);
- /*
- TODO: 16/9/17 should we run DetectUnwantedPicturesAsync if DUPLICATE_PROCEED is returned? Since that means
- we are processing images that are already on server???...
- */
-
- if (duplicateCheckPassed) {
- //image can be uploaded, so now check if its a useless picture or not
- performUnwantedPictureDetectionProcess();
- }
-
- },mwApi);
- fileAsyncTask.execute();
- } catch (IOException e) {
- Timber.d(e, "IO Exception: ");
- }
- }
-
- getFileMetadata(locationPermitted);
- } else {
- Timber.w("not ready for preprocessing: useNewPermissions=%s storage=%s location=%s",
- useNewPermissions, storagePermitted, locationPermitted);
- }
- }
-
- private void performUnwantedPictureDetectionProcess() {
- String imageMediaFilePath = FileUtils.getPath(this,mediaUri);
- DetectUnwantedPicturesAsync detectUnwantedPicturesAsync
- = new DetectUnwantedPicturesAsync(new WeakReference(this)
- , imageMediaFilePath);
-
- detectUnwantedPicturesAsync.execute();
- }
-
- /*
- * to display permission snackbar in share activity
+ /**
+ * Displays Snackbar to ask for location permissions
*/
- private Snackbar requestPermissionUsingSnackBar(String rationale,
- final String[] perms,
- final int code) {
+ private Snackbar requestPermissionUsingSnackBar(String rationale, final String[] perms, final int code) {
Snackbar snackbar = Snackbar.make(findViewById(android.R.id.content), rationale,
Snackbar.LENGTH_INDEFINITE).setAction(R.string.ok,
view -> ActivityCompat.requestPermissions(ShareActivity.this, perms, code));
@@ -535,211 +435,44 @@ public class ShareActivity
return snackbar;
}
- @Nullable
- private String getPathOfMediaOrCopy() {
- String filePath = FileUtils.getPath(getApplicationContext(), mediaUri);
- Timber.d("Filepath: " + filePath);
- if (filePath == null) {
- // in older devices getPath() may fail depending on the source URI
- // creating and using a copy of the file seems to work instead.
- // TODO: there might be a more proper solution than this
- String copyPath = null;
- try {
- ParcelFileDescriptor descriptor
- = getContentResolver().openFileDescriptor(mediaUri, "r");
- if (descriptor != null) {
- boolean useExtStorage = prefs.getBoolean("useExternalStorage", true);
- if (useExtStorage) {
- copyPath = Environment.getExternalStorageDirectory().toString()
- + "/CommonsApp/" + new Date().getTime() + ".jpg";
- File newFile = new File(Environment.getExternalStorageDirectory().toString() + "/CommonsApp");
- newFile.mkdir();
- FileUtils.copy(
- descriptor.getFileDescriptor(),
- copyPath);
- Timber.d("Filepath (copied): %s", copyPath);
- return copyPath;
- }
- copyPath = getApplicationContext().getCacheDir().getAbsolutePath()
- + "/" + new Date().getTime() + ".jpg";
- FileUtils.copy(
- descriptor.getFileDescriptor(),
- copyPath);
- Timber.d("Filepath (copied): %s", copyPath);
- return copyPath;
- }
- } catch (IOException e) {
- Timber.w(e, "Error in file " + copyPath);
- return null;
- }
- }
- return filePath;
- }
-
/**
- * Gets coordinates for category suggestions, either from EXIF data or user location
- *
- * @param gpsEnabled if true use GPS
+ * Check if file user wants to upload already exists on Commons
*/
- private void getFileMetadata(boolean gpsEnabled) {
- Timber.d("Calling GPSExtractor");
- try {
- if (imageObj == null) {
- ParcelFileDescriptor descriptor
- = getContentResolver().openFileDescriptor(mediaUri, "r");
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
- if (descriptor != null) {
- imageObj = new GPSExtractor(descriptor.getFileDescriptor(), this, prefs);
- }
- } else {
- String filePath = getPathOfMediaOrCopy();
- if (filePath != null) {
- imageObj = new GPSExtractor(filePath, this, prefs);
- }
- }
- }
-
- if (imageObj != null) {
- // Gets image coords from exif data or user location
- decimalCoords = imageObj.getCoords(gpsEnabled);
- if(decimalCoords==null || !imageObj.imageCoordsExists){
-// Check if the location is from GPS or EXIF
-// Find other photos taken around the same time which has gps coordinates
- Timber.d("EXIF:false");
- Timber.d("EXIF call"+(imageObj==tempImageObj));
- if(!haveCheckedForOtherImages)
- findOtherImages(gpsEnabled);// Do not do repeat the process
- }
- else {
-// As the selected image has GPS data in EXIF go ahead with the same.
- useImageCoords();
- }
- }
- } catch (FileNotFoundException e) {
- Timber.w("File not found: " + mediaUri, e);
- }
- }
-
- private void findOtherImages(boolean gpsEnabled) {
- Timber.d("filePath"+getPathOfMediaOrCopy());
- String filePath = getPathOfMediaOrCopy();
- long timeOfCreation = new File(filePath).lastModified();//Time when the original image was created
- File folder = new File(filePath.substring(0,filePath.lastIndexOf('/')));
- File[] files = folder.listFiles();
- Timber.d("folderTime Number:"+files.length);
-
- for(File file : files){
- if(file.lastModified()-timeOfCreation<=(120*1000) && file.lastModified()-timeOfCreation>=-(120*1000)){
- //Make sure the photos were taken within 20seconds
- Timber.d("fild date:"+file.lastModified()+ " time of creation"+timeOfCreation);
- tempImageObj = null;//Temporary GPSExtractor to extract coords from these photos
- ParcelFileDescriptor descriptor
- = null;
+ private void checkIfFileExists() {
+ if (!useNewPermissions || storagePermitted) {
+ if (!duplicateCheckPassed) {
+ //Test SHA1 of image to see if it matches SHA1 of a file on Commons
try {
- descriptor = getContentResolver().openFileDescriptor(Uri.parse(file.getAbsolutePath()), "r");
- } catch (FileNotFoundException e) {
- e.printStackTrace();
- }
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
- if (descriptor != null) {
- tempImageObj = new GPSExtractor(descriptor.getFileDescriptor(),this, prefs);
- }
- } else {
- if (filePath != null) {
- tempImageObj = new GPSExtractor(file.getAbsolutePath(), this, prefs);
- }
- }
+ InputStream inputStream = getContentResolver().openInputStream(mediaUri);
+ String fileSHA1 = getSHA1(inputStream);
+ Timber.d("Input stream created from %s", mediaUri.toString());
+ Timber.d("File SHA1 is: %s", fileSHA1);
- if(tempImageObj!=null){
- Timber.d("not null fild EXIF"+tempImageObj.imageCoordsExists +" coords"+tempImageObj.getCoords(gpsEnabled));
- if(tempImageObj.getCoords(gpsEnabled)!=null && tempImageObj.imageCoordsExists){
-// Current image has gps coordinates and it's not current gps locaiton
- Timber.d("This fild has image coords:"+ file.getAbsolutePath());
-// Create a dialog fragment for the suggestion
- FragmentManager fragmentManager = getSupportFragmentManager();
- SimilarImageDialogFragment newFragment = new SimilarImageDialogFragment();
- Bundle args = new Bundle();
- args.putString("originalImagePath",filePath);
- args.putString("possibleImagePath",file.getAbsolutePath());
- newFragment.setArguments(args);
- newFragment.show(fragmentManager, "dialog");
- break;
- }
-
- }
-
- }
- }
- haveCheckedForOtherImages = true; //Finished checking for other images
- return;
- }
-
- //I might not be supposed to change it, but still, I saw it
- @Override
- public void onPositiveResponse() {
- imageObj = tempImageObj;
- decimalCoords = imageObj.getCoords(false);// Not necessary to use gps as image already ha EXIF data
- Timber.d("EXIF from tempImageObj");
- useImageCoords();
- }
-
- @Override
- public void onNegativeResponse() {
- Timber.d("EXIF from imageObj");
- useImageCoords();
-
- }
-
- /**
- * Initiates retrieval of image coordinates or user coordinates, and caching of coordinates.
- * Then initiates the calls to MediaWiki API through an instance of CategpryApi.
- */
- @SuppressLint("CheckResult")
- public void useImageCoords() {
- if (decimalCoords != null) {
- Timber.d("Decimal coords of image: %s", decimalCoords);
- Timber.d("is EXIF data present:"+imageObj.imageCoordsExists+" from findOther image:"+(imageObj==tempImageObj));
-
- // Only set cache for this point if image has coords
- if (imageObj.imageCoordsExists) {
- double decLongitude = imageObj.getDecLongitude();
- double decLatitude = imageObj.getDecLatitude();
- cacheController.setQtPoint(decLongitude, decLatitude);
- }
-
- List displayCatList = cacheController.findCategory();
- boolean catListEmpty = displayCatList.isEmpty();
-
- // If no categories found in cache, call MediaWiki API to match image coords with nearby Commons categories
- if (catListEmpty) {
- cacheFound = false;
- apiCall.request(decimalCoords)
- .subscribeOn(Schedulers.io())
- .observeOn(Schedulers.io())
- .subscribe(
- gpsCategoryModel::setCategoryList,
- throwable -> {
- Timber.e(throwable);
- gpsCategoryModel.clear();
+ ExistingFileAsync fileAsyncTask =
+ new ExistingFileAsync(new WeakReference(this), fileSHA1, new WeakReference(this), result -> {
+ Timber.d("%s duplicate check: %s", mediaUri.toString(), result);
+ duplicateCheckPassed = (result == DUPLICATE_PROCEED || result == NO_DUPLICATE);
+ if (duplicateCheckPassed) {
+ //image is not a duplicate, so now check if its a unwanted picture or not
+ fileObj.detectUnwantedPictures();
}
- );
- Timber.d("displayCatList size 0, calling MWAPI %s", displayCatList);
- } else {
- cacheFound = true;
- Timber.d("Cache found, setting categoryList in model to %s", displayCatList);
- gpsCategoryModel.setCategoryList(displayCatList);
+ }, mwApi);
+ fileAsyncTask.execute();
+ } catch (IOException e) {
+ Timber.e(e, "IO Exception: ");
+ }
}
- }else{
- Timber.d("EXIF: no coords");
+ } else {
+ Timber.w("not ready for preprocessing: useNewPermissions=%s storage=%s location=%s",
+ useNewPermissions, storagePermitted, locationPermitted);
}
-
}
@Override
public void onPause() {
super.onPause();
try {
- imageObj.unregisterLocationManager();
+ gpsObj.unregisterLocationManager();
Timber.d("Unregistered locationManager");
} catch (NullPointerException e) {
Timber.d("locationManager does not exist, not unregistered");
@@ -766,125 +499,32 @@ public class ShareActivity
return super.onOptionsItemSelected(item);
}
- /*
- * Get SHA1 of file from input stream
+ /**
+ * Allows zooming in to the image about to be uploaded. Called when zoom FAB is tapped
*/
- private String getSHA1(InputStream is) {
-
- MessageDigest digest;
- try {
- digest = MessageDigest.getInstance("SHA1");
- } catch (NoSuchAlgorithmException e) {
- Timber.e(e, "Exception while getting Digest");
- return "";
- }
-
- byte[] buffer = new byte[8192];
- int read;
- try {
- while ((read = is.read(buffer)) > 0) {
- digest.update(buffer, 0, read);
- }
- byte[] md5sum = digest.digest();
- BigInteger bigInt = new BigInteger(1, md5sum);
- String output = bigInt.toString(16);
- // Fill to 40 chars
- output = String.format("%40s", output).replace(' ', '0');
- Timber.i("File SHA1: %s", output);
-
- return output;
- } catch (IOException e) {
- Timber.e(e, "IO Exception");
- return "";
- } finally {
- try {
- is.close();
- } catch (IOException e) {
- Timber.e(e, "Exception on closing MD5 input stream");
- }
- }
- }
-
- /*
- * function to provide pinch zoom
- */
- private void zoomImageFromThumb(final View thumbView, Uri imageuri ) {
- // If there's an animation in progress, cancel it
- // immediately and proceed with this one.
+ private void zoomImageFromThumb(final View thumbView, Uri imageuri) {
+ // If there's an animation in progress, cancel it immediately and proceed with this one.
if (CurrentAnimator != null) {
CurrentAnimator.cancel();
}
+ isZoom = true;
ViewUtil.hideKeyboard(ShareActivity.this.findViewById(R.id.titleEdit | R.id.descEdit));
closeFABMenu();
mainFab.setVisibility(View.GONE);
+
InputStream input = null;
- Bitmap scaled = null;
try {
input = this.getContentResolver().openInputStream(imageuri);
} catch (FileNotFoundException e) {
e.printStackTrace();
}
- BitmapRegionDecoder decoder = null;
- try {
- decoder = BitmapRegionDecoder.newInstance(input, false);
- } catch (IOException e) {
- e.printStackTrace();
- }
- Bitmap bitmap = decoder.decodeRegion(new Rect(10, 10, 50, 50), null);
- try {
- //Compress the Image
- System.gc();
- Runtime rt = Runtime.getRuntime();
- long maxMemory = rt.freeMemory();
- bitmap = MediaStore.Images.Media.getBitmap(this.getContentResolver(), imageuri);
- int bitmapByteCount= BitmapCompat.getAllocationByteCount(bitmap);
- long height = bitmap.getHeight();
- long width = bitmap.getWidth();
- long calHeight = (long) ((height * maxMemory)/(bitmapByteCount * 1.1));
- long calWidth = (long) ((width * maxMemory)/(bitmapByteCount * 1.1));
- scaled = Bitmap.createScaledBitmap(bitmap,(int) Math.min(width,calWidth), (int) Math.min(height,calHeight), true);
- } catch (IOException e) {
- } catch (NullPointerException e){
- scaled = bitmap;
- }
+
+ Zoom zoomObj = new Zoom(thumbView, flContainer, this.getContentResolver());
+ Bitmap scaledImage = zoomObj.createScaledImage(input, imageuri);
+
// Load the high-resolution "zoomed-in" image.
- expandedImageView.setImageBitmap(scaled);
-
-
-
- // Calculate the starting and ending bounds for the zoomed-in image.
- // This step involves lots of math. Yay, math.
- // The start bounds are the global visible rectangle of the thumbnail,
- // and the final bounds are the global visible rectangle of the container
- // view. Also set the container view's offset as the origin for the
- // bounds, since that's the origin for the positioning animation
- // properties (X, Y).
- thumbView.getGlobalVisibleRect(startBounds);
- flContainer.getGlobalVisibleRect(finalBounds, globalOffset);
- startBounds.offset(-globalOffset.x, -globalOffset.y);
- finalBounds.offset(-globalOffset.x, -globalOffset.y);
-
- // Adjust the start bounds to be the same aspect ratio as the final
- // bounds using the "center crop" technique. This prevents undesirable
- // stretching during the animation. Also calculate the start scaling
- // factor (the end scaling factor is always 1.0).
- float startScale;
- if ((float) finalBounds.width() / finalBounds.height()
- > (float) startBounds.width() / startBounds.height()) {
- // Extend start bounds horizontally
- startScale = (float) startBounds.height() / finalBounds.height();
- float startWidth = startScale * finalBounds.width();
- float deltaWidth = (startWidth - startBounds.width()) / 2;
- startBounds.left -= deltaWidth;
- startBounds.right += deltaWidth;
- } else {
- // Extend start bounds vertically
- startScale = (float) startBounds.width() / finalBounds.width();
- float startHeight = startScale * finalBounds.height();
- float deltaHeight = (startHeight - startBounds.height()) / 2;
- startBounds.top -= deltaHeight;
- startBounds.bottom += deltaHeight;
- }
+ expandedImageView.setImageBitmap(scaledImage);
+ float startScale = zoomObj.adjustStartEndBounds(startBounds, finalBounds, globalOffset);
// Hide the thumbnail and show the zoomed-in view. When the animation
// begins, it will position the zoomed-in view in the place of the
@@ -903,15 +543,10 @@ public class ShareActivity
// Construct and run the parallel animation of the four translation and
// scale properties (X, Y, SCALE_X, and SCALE_Y).
AnimatorSet set = new AnimatorSet();
- set
- .play(ObjectAnimator.ofFloat(expandedImageView, View.X,
- startBounds.left, finalBounds.left))
- .with(ObjectAnimator.ofFloat(expandedImageView, View.Y,
- startBounds.top, finalBounds.top))
- .with(ObjectAnimator.ofFloat(expandedImageView, View.SCALE_X,
- startScale, 1f))
- .with(ObjectAnimator.ofFloat(expandedImageView,
- View.SCALE_Y, startScale, 1f));
+ set.play(ObjectAnimator.ofFloat(expandedImageView, View.X, startBounds.left, finalBounds.left))
+ .with(ObjectAnimator.ofFloat(expandedImageView, View.Y, startBounds.top, finalBounds.top))
+ .with(ObjectAnimator.ofFloat(expandedImageView, View.SCALE_X, startScale, 1f))
+ .with(ObjectAnimator.ofFloat(expandedImageView, View.SCALE_Y, startScale, 1f));
set.setDuration(ShortAnimationDuration);
set.setInterpolator(new DecelerateInterpolator());
set.addListener(new AnimatorListenerAdapter() {
@@ -932,11 +567,10 @@ public class ShareActivity
// to the original bounds and show the thumbnail instead of
// the expanded image.
startScaleFinal = startScale;
-
}
- /*
- * called when upper arrow floating button
+ /**
+ * Called when user taps the ^ FAB button, expands to show Zoom and Map
*/
@OnClick(R.id.main_fab)
public void onMainFabClicked() {
@@ -949,11 +583,10 @@ public class ShareActivity
@OnClick(R.id.media_upload_zoom_in)
public void onZoomInFabClicked() {
- //This try catch block was originally holding the entire click listener on the fab button, I did not wanted to risk exceptions
try {
zoomImageFromThumb(backgroundImageView, mediaUri);
} catch (Exception e) {
- Log.i("exception", e.toString());
+ Timber.e(e);
}
}
@@ -962,23 +595,18 @@ public class ShareActivity
if (CurrentAnimator != null) {
CurrentAnimator.cancel();
}
+ isZoom = false;
zoomOutButton.setVisibility(View.GONE);
mainFab.setVisibility(View.VISIBLE);
// Animate the four positioning/sizing properties in parallel,
// back to their original values.
AnimatorSet set = new AnimatorSet();
- set.play(ObjectAnimator
- .ofFloat(expandedImageView, View.X, startBounds.left))
- .with(ObjectAnimator
- .ofFloat(expandedImageView,
- View.Y, startBounds.top))
- .with(ObjectAnimator
- .ofFloat(expandedImageView,
- View.SCALE_X, startScaleFinal))
- .with(ObjectAnimator
- .ofFloat(expandedImageView,
- View.SCALE_Y, startScaleFinal));
+ set.play(ObjectAnimator.ofFloat(expandedImageView, View.X, startBounds.left))
+ .with(ObjectAnimator.ofFloat(expandedImageView, View.Y, startBounds.top))
+ .with(ObjectAnimator.ofFloat(expandedImageView, View.SCALE_X, startScaleFinal))
+ .with(ObjectAnimator.ofFloat(expandedImageView, View.SCALE_Y, startScaleFinal));
+
set.setDuration(ShortAnimationDuration);
set.setInterpolator(new DecelerateInterpolator());
set.addListener(new AnimatorListenerAdapter() {
@@ -1004,13 +632,24 @@ public class ShareActivity
@OnClick(R.id.media_map)
public void onFabShowMapsClicked() {
- if (imageObj != null && imageObj.imageCoordsExists == true) {
- Uri gmmIntentUri = Uri
- .parse("google.streetview:cbll=" + imageObj.getDecLatitude() + "," + imageObj
- .getDecLongitude());
+ if (gpsObj != null && gpsObj.imageCoordsExists) {
+ Uri gmmIntentUri = Uri.parse("google.streetview:cbll=" + gpsObj.getDecLatitude() + "," + gpsObj.getDecLongitude());
Intent mapIntent = new Intent(Intent.ACTION_VIEW, gmmIntentUri);
mapIntent.setPackage("com.google.android.apps.maps");
startActivity(mapIntent);
}
}
+
+ @Override
+ public boolean onKeyDown(int keyCode, KeyEvent event) {
+ switch (keyCode) {
+ case KeyEvent.KEYCODE_BACK:
+ if(isZoom) {
+ onZoomOutFabClicked();
+ return true;
+ }
+ }
+ return super.onKeyDown(keyCode,event);
+
+ }
}
diff --git a/app/src/main/java/fr/free/nrw/commons/upload/SingleUploadFragment.java b/app/src/main/java/fr/free/nrw/commons/upload/SingleUploadFragment.java
index a32fb7b42..a993d59da 100644
--- a/app/src/main/java/fr/free/nrw/commons/upload/SingleUploadFragment.java
+++ b/app/src/main/java/fr/free/nrw/commons/upload/SingleUploadFragment.java
@@ -74,13 +74,13 @@ public class SingleUploadFragment extends CommonsDaggerSupportFragment {
//What happens when the 'submit' icon is tapped
case R.id.menu_upload_single:
- if (titleEdit.getText().toString().isEmpty()) {
+ if (titleEdit.getText().toString().trim().isEmpty()) {
Toast.makeText(getContext(), R.string.add_title_toast, Toast.LENGTH_LONG).show();
return false;
}
- String title = titleEdit.getText().toString();
- String desc = descEdit.getText().toString();
+ String title = titleEdit.getText().toString().trim();
+ String desc = descEdit.getText().toString().trim();
//Save the title/desc in short-lived cache so next time this fragment is loaded, we can access these
prefs.edit()
diff --git a/app/src/main/java/fr/free/nrw/commons/upload/UploadController.java b/app/src/main/java/fr/free/nrw/commons/upload/UploadController.java
index 32554da0f..5fd6d909b 100644
--- a/app/src/main/java/fr/free/nrw/commons/upload/UploadController.java
+++ b/app/src/main/java/fr/free/nrw/commons/upload/UploadController.java
@@ -91,7 +91,7 @@ public class UploadController {
* @param decimalCoords the coordinates in decimal. (e.g. "37.51136|-77.602615")
* @param onComplete the progress tracker
*/
- public void startUpload(String title, Uri mediaUri, String description, String mimeType, String source, String decimalCoords, ContributionUploadProgress onComplete) {
+ public void startUpload(String title, Uri mediaUri, String description, String mimeType, String source, String decimalCoords, String wikiDataEntityId, ContributionUploadProgress onComplete) {
Contribution contribution;
//TODO: Modify this to include coords
@@ -101,6 +101,7 @@ public class UploadController {
contribution.setTag("mimeType", mimeType);
contribution.setSource(source);
+ contribution.setWikiDataEntityId(wikiDataEntityId);
//Calls the next overloaded method
startUpload(contribution, onComplete);
diff --git a/app/src/main/java/fr/free/nrw/commons/upload/UploadService.java b/app/src/main/java/fr/free/nrw/commons/upload/UploadService.java
index 94c005256..d5ab1d65a 100644
--- a/app/src/main/java/fr/free/nrw/commons/upload/UploadService.java
+++ b/app/src/main/java/fr/free/nrw/commons/upload/UploadService.java
@@ -7,7 +7,6 @@ import android.app.PendingIntent;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.Intent;
-import android.content.SharedPreferences;
import android.graphics.BitmapFactory;
import android.os.Bundle;
import android.support.v4.app.NotificationCompat;
@@ -23,7 +22,6 @@ import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.inject.Inject;
-import javax.inject.Named;
import fr.free.nrw.commons.HandlerService;
import fr.free.nrw.commons.R;
@@ -36,6 +34,7 @@ import fr.free.nrw.commons.contributions.ContributionsContentProvider;
import fr.free.nrw.commons.modifications.ModificationsContentProvider;
import fr.free.nrw.commons.mwapi.MediaWikiApi;
import fr.free.nrw.commons.mwapi.UploadResult;
+import fr.free.nrw.commons.wikidata.WikidataEditService;
import timber.log.Timber;
public class UploadService extends HandlerService {
@@ -49,8 +48,8 @@ public class UploadService extends HandlerService {
public static final String EXTRA_CAMPAIGN = EXTRA_PREFIX + ".campaign";
@Inject MediaWikiApi mwApi;
+ @Inject WikidataEditService wikidataEditService;
@Inject SessionManager sessionManager;
- @Inject @Named("default_preferences") SharedPreferences prefs;
@Inject ContributionDao contributionDao;
private NotificationManager notificationManager;
@@ -137,6 +136,7 @@ public class UploadService extends HandlerService {
@Override
public void queue(int what, Contribution contribution) {
+ Timber.d("Upload service queue has contribution with wiki data entity id as %s", contribution.getWikiDataEntityId());
switch (what) {
case ACTION_UPLOAD_FILE:
@@ -231,10 +231,10 @@ public class UploadService extends HandlerService {
Timber.d("Successfully revalidated token!");
} else {
Timber.d("Unable to revalidate :(");
- // TODO: Put up a new notification, ask them to re-login
stopForeground(true);
Toast failureToast = Toast.makeText(this, R.string.authentication_failed, Toast.LENGTH_LONG);
failureToast.show();
+ sessionManager.forceLogin(this);
return;
}
}
@@ -253,6 +253,7 @@ public class UploadService extends HandlerService {
if (!resultStatus.equals("Success")) {
showFailedNotification(contribution);
} else {
+ wikidataEditService.createClaimWithLogging(contribution.getWikiDataEntityId(), filename);
contribution.setFilename(uploadResult.getCanonicalFilename());
contribution.setImageUrl(uploadResult.getImageUrl());
contribution.setState(Contribution.STATE_COMPLETED);
diff --git a/app/src/main/java/fr/free/nrw/commons/upload/Zoom.java b/app/src/main/java/fr/free/nrw/commons/upload/Zoom.java
new file mode 100644
index 000000000..438c7f77b
--- /dev/null
+++ b/app/src/main/java/fr/free/nrw/commons/upload/Zoom.java
@@ -0,0 +1,115 @@
+package fr.free.nrw.commons.upload;
+
+import android.content.ContentResolver;
+import android.graphics.Bitmap;
+import android.graphics.BitmapRegionDecoder;
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.net.Uri;
+import android.provider.MediaStore;
+import android.support.v4.graphics.BitmapCompat;
+import android.view.View;
+import android.widget.FrameLayout;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+import timber.log.Timber;
+
+/**
+ * Contains utility methods for the Zoom function in ShareActivity.
+ */
+public class Zoom {
+
+ private View thumbView;
+ private ContentResolver contentResolver;
+ private FrameLayout flContainer;
+
+ Zoom(View thumbView, FrameLayout flContainer, ContentResolver contentResolver) {
+ this.thumbView = thumbView;
+ this.contentResolver = contentResolver;
+ this.flContainer = flContainer;
+ }
+
+ /**
+ * Create a scaled bitmap to display the zoomed-in image
+ * @param input the input stream corresponding to the uploaded image
+ * @param imageUri the uploaded image's URI
+ * @return a zoomable bitmap
+ */
+ Bitmap createScaledImage(InputStream input, Uri imageUri) {
+
+ Bitmap scaled = null;
+ BitmapRegionDecoder decoder = null;
+ Bitmap bitmap = null;
+
+ try {
+ decoder = BitmapRegionDecoder.newInstance(input, false);
+ bitmap = decoder.decodeRegion(new Rect(10, 10, 50, 50), null);
+ } catch (IOException e) {
+ Timber.e(e);
+ } catch (NullPointerException e) {
+ Timber.e(e);
+ }
+ try {
+ //Compress the Image
+ System.gc();
+ Runtime rt = Runtime.getRuntime();
+ long maxMemory = rt.freeMemory();
+ bitmap = MediaStore.Images.Media.getBitmap(contentResolver, imageUri);
+ int bitmapByteCount = BitmapCompat.getAllocationByteCount(bitmap);
+ long height = bitmap.getHeight();
+ long width = bitmap.getWidth();
+ long calHeight = (long) ((height * maxMemory) / (bitmapByteCount * 1.1));
+ long calWidth = (long) ((width * maxMemory) / (bitmapByteCount * 1.1));
+ scaled = Bitmap.createScaledBitmap(bitmap, (int) Math.min(width, calWidth), (int) Math.min(height, calHeight), true);
+ } catch (IOException e) {
+ Timber.e(e);
+ } catch (NullPointerException e) {
+ Timber.e(e);
+ scaled = bitmap;
+ }
+ return scaled;
+ }
+
+ /**
+ * Calculate the starting and ending bounds for the zoomed-in image.
+ * Also set the container view's offset as the origin for the
+ * bounds, since that's the origin for the positioning animation
+ * properties (X, Y).
+ * @param startBounds the global visible rectangle of the thumbnail
+ * @param finalBounds the global visible rectangle of the container view
+ * @param globalOffset the container view's offset
+ * @return scaled start bounds
+ */
+ float adjustStartEndBounds(Rect startBounds, Rect finalBounds, Point globalOffset) {
+
+ thumbView.getGlobalVisibleRect(startBounds);
+ flContainer.getGlobalVisibleRect(finalBounds, globalOffset);
+ startBounds.offset(-globalOffset.x, -globalOffset.y);
+ finalBounds.offset(-globalOffset.x, -globalOffset.y);
+
+ // Adjust the start bounds to be the same aspect ratio as the final
+ // bounds using the "center crop" technique. This prevents undesirable
+ // stretching during the animation. Also calculate the start scaling
+ // factor (the end scaling factor is always 1.0).
+ float startScale;
+ if ((float) finalBounds.width() / finalBounds.height()
+ > (float) startBounds.width() / startBounds.height()) {
+ // Extend start bounds horizontally
+ startScale = (float) startBounds.height() / finalBounds.height();
+ float startWidth = startScale * finalBounds.width();
+ float deltaWidth = (startWidth - startBounds.width()) / 2;
+ startBounds.left -= deltaWidth;
+ startBounds.right += deltaWidth;
+ } else {
+ // Extend start bounds vertically
+ startScale = (float) startBounds.width() / finalBounds.width();
+ float startHeight = startScale * finalBounds.height();
+ float deltaHeight = (startHeight - startBounds.height()) / 2;
+ startBounds.top -= deltaHeight;
+ startBounds.bottom += deltaHeight;
+ }
+ return startScale;
+ }
+}
diff --git a/app/src/main/java/fr/free/nrw/commons/utils/FileUtils.java b/app/src/main/java/fr/free/nrw/commons/utils/FileUtils.java
deleted file mode 100644
index d56a7b608..000000000
--- a/app/src/main/java/fr/free/nrw/commons/utils/FileUtils.java
+++ /dev/null
@@ -1,92 +0,0 @@
-package fr.free.nrw.commons.utils;
-
-import android.os.Environment;
-
-import java.io.BufferedReader;
-import java.io.File;
-import java.io.FileNotFoundException;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.io.OutputStreamWriter;
-
-import timber.log.Timber;
-
-public class FileUtils {
- /**
- * Read and return the content of a resource file as string.
- *
- * @param fileName asset file's path (e.g. "/queries/nearby_query.rq")
- * @return the content of the file
- */
- public static String readFromResource(String fileName) throws IOException {
- StringBuilder buffer = new StringBuilder();
- BufferedReader reader = null;
- try {
- InputStream inputStream = FileUtils.class.getResourceAsStream(fileName);
- if (inputStream == null) {
- throw new FileNotFoundException(fileName);
- }
- reader = new BufferedReader(new InputStreamReader(inputStream, "UTF-8"));
- String line;
- while ((line = reader.readLine()) != null) {
- buffer.append(line).append("\n");
- }
- } finally {
- if (reader != null) {
- reader.close();
- }
- }
- return buffer.toString();
- }
-
- /**
- * Deletes files.
- * @param file context
- */
- public static boolean deleteFile(File file) {
- boolean deletedAll = true;
- if (file != null) {
- if (file.isDirectory()) {
- String[] children = file.list();
- for (String child : children) {
- deletedAll = deleteFile(new File(file, child)) && deletedAll;
- }
- } else {
- deletedAll = file.delete();
- }
- }
-
- return deletedAll;
- }
-
- public static File createAndGetAppLogsFile(String logs) {
- try {
- File commonsAppDirectory = new File(Environment.getExternalStorageDirectory().toString() + "/CommonsApp");
- if (!commonsAppDirectory.exists()) {
- commonsAppDirectory.mkdir();
- }
-
- File logsFile = new File(commonsAppDirectory,"logs.txt");
- if (logsFile.exists()) {
- //old logs file is useless
- logsFile.delete();
- }
-
- logsFile.createNewFile();
-
- FileOutputStream outputStream = new FileOutputStream(logsFile);
- OutputStreamWriter outputStreamWriter = new OutputStreamWriter(outputStream);
- outputStreamWriter.append(logs);
- outputStreamWriter.close();
- outputStream.flush();
- outputStream.close();
-
- return logsFile;
- } catch (IOException ioe) {
- Timber.e(ioe);
- return null;
- }
- }
-}
diff --git a/app/src/main/java/fr/free/nrw/commons/utils/ImageUtils.java b/app/src/main/java/fr/free/nrw/commons/utils/ImageUtils.java
index a091d7758..79dad33e5 100644
--- a/app/src/main/java/fr/free/nrw/commons/utils/ImageUtils.java
+++ b/app/src/main/java/fr/free/nrw/commons/utils/ImageUtils.java
@@ -3,14 +3,11 @@ package fr.free.nrw.commons.utils;
import android.app.WallpaperManager;
import android.content.Context;
import android.graphics.Bitmap;
-import android.graphics.BitmapFactory;
import android.graphics.BitmapRegionDecoder;
import android.graphics.Color;
import android.graphics.Rect;
import android.net.Uri;
-import android.os.Build;
import android.support.annotation.Nullable;
-import android.support.annotation.RequiresApi;
import com.facebook.common.executors.CallerThreadExecutor;
import com.facebook.common.references.CloseableReference;
@@ -27,8 +24,6 @@ import java.io.IOException;
import fr.free.nrw.commons.R;
import timber.log.Timber;
-import static com.mapbox.mapboxsdk.Mapbox.getApplicationContext;
-
/**
* Created by bluesir9 on 3/10/17.
*/
diff --git a/app/src/main/java/fr/free/nrw/commons/widget/PicOfDayAppWidget.java b/app/src/main/java/fr/free/nrw/commons/widget/PicOfDayAppWidget.java
new file mode 100644
index 000000000..82bad3f09
--- /dev/null
+++ b/app/src/main/java/fr/free/nrw/commons/widget/PicOfDayAppWidget.java
@@ -0,0 +1,80 @@
+package fr.free.nrw.commons.widget;
+
+import android.appwidget.AppWidgetManager;
+import android.appwidget.AppWidgetProvider;
+import android.content.Context;
+import android.widget.RemoteViews;
+
+import com.prof.rssparser.Article;
+import com.prof.rssparser.Parser;
+import com.squareup.picasso.Picasso;
+
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+import org.jsoup.Jsoup;
+import org.jsoup.nodes.Document;
+import org.jsoup.nodes.Element;
+import org.jsoup.select.Elements;
+
+import java.util.ArrayList;
+
+import fr.free.nrw.commons.BuildConfig;
+import fr.free.nrw.commons.R;
+
+/**
+ * Implementation of App Widget functionality.
+ */
+public class PicOfDayAppWidget extends AppWidgetProvider {
+
+ static void updateAppWidget(Context context, AppWidgetManager appWidgetManager,
+ int appWidgetId) {
+
+ // Construct the RemoteViews object
+ RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.pic_of_day_app_widget);
+
+ String urlString = BuildConfig.WIKIMEDIA_API_POTD;
+ Parser parser = new Parser();
+ parser.execute(urlString);
+ parser.onFinish(new Parser.OnTaskCompleted() {
+ @Override
+ public void onTaskCompleted(ArrayList list) {
+ String desc = list.get(list.size() - 1).getDescription();
+ if (desc != null) {
+ Document document = Jsoup.parse(desc);
+ Elements elements = document.select("img");
+ String imageUrl = elements.get(0).attr("src");
+ if (imageUrl != null && imageUrl.length() > 0) {
+ Picasso.get().load(imageUrl).into(views, R.id.appwidget_image, new int[]{appWidgetId});
+ }
+ }
+
+ }
+
+ @Override
+ public void onError() {
+ }
+ });
+
+ // Instruct the widget manager to update the widget
+ appWidgetManager.updateAppWidget(appWidgetId, views);
+ }
+
+ @Override
+ public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
+ // There may be multiple widgets active, so update all of them
+ for (int appWidgetId : appWidgetIds) {
+ updateAppWidget(context, appWidgetManager, appWidgetId);
+ }
+ }
+
+ @Override
+ public void onEnabled(Context context) {
+ // Enter relevant functionality for when the first widget is created
+ }
+
+ @Override
+ public void onDisabled(Context context) {
+ // Enter relevant functionality for when the last widget is disabled
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/fr/free/nrw/commons/wikidata/WikidataEditListener.java b/app/src/main/java/fr/free/nrw/commons/wikidata/WikidataEditListener.java
new file mode 100644
index 000000000..30fb26ddc
--- /dev/null
+++ b/app/src/main/java/fr/free/nrw/commons/wikidata/WikidataEditListener.java
@@ -0,0 +1,16 @@
+package fr.free.nrw.commons.wikidata;
+
+public abstract class WikidataEditListener {
+
+ protected WikidataP18EditListener wikidataP18EditListener;
+
+ public abstract void onSuccessfulWikidataEdit();
+
+ public void setAuthenticationStateListener(WikidataP18EditListener wikidataP18EditListener) {
+ this.wikidataP18EditListener = wikidataP18EditListener;
+ }
+
+ public interface WikidataP18EditListener {
+ void onWikidataEditSuccessful();
+ }
+}
diff --git a/app/src/main/java/fr/free/nrw/commons/wikidata/WikidataEditListenerImpl.java b/app/src/main/java/fr/free/nrw/commons/wikidata/WikidataEditListenerImpl.java
new file mode 100644
index 000000000..407c24711
--- /dev/null
+++ b/app/src/main/java/fr/free/nrw/commons/wikidata/WikidataEditListenerImpl.java
@@ -0,0 +1,20 @@
+package fr.free.nrw.commons.wikidata;
+
+/**
+ * Listener for wikidata edits
+ */
+public class WikidataEditListenerImpl extends WikidataEditListener {
+
+ public WikidataEditListenerImpl() {
+ }
+
+ /**
+ * Fired when wikidata P18 edit is successful. If there's an active listener, then it is fired
+ */
+ @Override
+ public void onSuccessfulWikidataEdit() {
+ if (wikidataP18EditListener != null) {
+ wikidataP18EditListener.onWikidataEditSuccessful();
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/fr/free/nrw/commons/wikidata/WikidataEditService.java b/app/src/main/java/fr/free/nrw/commons/wikidata/WikidataEditService.java
new file mode 100644
index 000000000..8bff40b89
--- /dev/null
+++ b/app/src/main/java/fr/free/nrw/commons/wikidata/WikidataEditService.java
@@ -0,0 +1,134 @@
+package fr.free.nrw.commons.wikidata;
+
+import android.annotation.SuppressLint;
+import android.content.Context;
+import android.content.SharedPreferences;
+
+import java.util.Locale;
+
+import javax.inject.Inject;
+import javax.inject.Named;
+import javax.inject.Singleton;
+
+import fr.free.nrw.commons.R;
+import fr.free.nrw.commons.mwapi.MediaWikiApi;
+import fr.free.nrw.commons.utils.ViewUtil;
+import io.reactivex.Observable;
+import io.reactivex.android.schedulers.AndroidSchedulers;
+import io.reactivex.schedulers.Schedulers;
+import timber.log.Timber;
+
+/**
+ * This class is meant to handle the Wikidata edits made through the app
+ * It will talk with MediaWikiApi to make necessary API calls, log the edits and fire listeners
+ * on successful edits
+ */
+@Singleton
+public class WikidataEditService {
+
+ private final Context context;
+ private final MediaWikiApi mediaWikiApi;
+ private final WikidataEditListener wikidataEditListener;
+ private final SharedPreferences directPrefs;
+
+ @Inject
+ public WikidataEditService(Context context,
+ MediaWikiApi mediaWikiApi,
+ WikidataEditListener wikidataEditListener,
+ @Named("direct_nearby_upload_prefs") SharedPreferences directPrefs) {
+ this.context = context;
+ this.mediaWikiApi = mediaWikiApi;
+ this.wikidataEditListener = wikidataEditListener;
+ this.directPrefs = directPrefs;
+ }
+
+ /**
+ * Create a P18 claim and log the edit with custom tag
+ * @param wikidataEntityId
+ * @param fileName
+ */
+ public void createClaimWithLogging(String wikidataEntityId, String fileName) {
+ if(wikidataEntityId == null
+ || fileName == null) {
+ return;
+ }
+ editWikidataProperty(wikidataEntityId, fileName);
+ }
+
+ /**
+ * Edits the wikidata entity by adding the P18 property to it.
+ * Adding the P18 edit requires calling the wikidata API to create a claim against the entity
+ *
+ * @param wikidataEntityId
+ * @param fileName
+ */
+ @SuppressLint("CheckResult")
+ private void editWikidataProperty(String wikidataEntityId, String fileName) {
+ Timber.d("Upload successful with wiki data entity id as %s", wikidataEntityId);
+ Timber.d("Attempting to edit Wikidata property %s", wikidataEntityId);
+ Observable.fromCallable(() -> {
+ String propertyValue = getFileName(fileName);
+ return mediaWikiApi.wikidatCreateClaim(wikidataEntityId, "P18", "value", propertyValue);
+ })
+ .subscribeOn(Schedulers.io())
+ .observeOn(AndroidSchedulers.mainThread())
+ .subscribe(revisionId -> handleClaimResult(wikidataEntityId, revisionId), throwable -> {
+ Timber.e(throwable, "Error occurred while making claim");
+ ViewUtil.showLongToast(context, context.getString(R.string.wikidata_edit_failure));
+ });
+ }
+
+ private void handleClaimResult(String wikidataEntityId, String revisionId) {
+ if (revisionId != null) {
+ wikidataEditListener.onSuccessfulWikidataEdit();
+ showSuccessToast();
+ logEdit(revisionId);
+ } else {
+ Timber.d("Unable to make wiki data edit for entity %s", wikidataEntityId);
+ ViewUtil.showLongToast(context, context.getString(R.string.wikidata_edit_failure));
+ }
+ }
+
+ /**
+ * Log the Wikidata edit by adding Wikimedia Commons App tag to the edit
+ * @param revisionId
+ */
+ @SuppressLint("CheckResult")
+ private void logEdit(String revisionId) {
+ Observable.fromCallable(() -> mediaWikiApi.addWikidataEditTag(revisionId))
+ .subscribeOn(Schedulers.io())
+ .observeOn(AndroidSchedulers.mainThread())
+ .subscribe(result -> {
+ if (result) {
+ Timber.d("Wikidata edit was tagged successfully");
+ } else {
+ Timber.d("Wikidata edit couldn't be tagged");
+ }
+ }, throwable -> {
+ Timber.e(throwable, "Error occurred while adding tag to the edit");
+ });
+ }
+
+ /**
+ * Show a success toast when the edit is made successfully
+ */
+ private void showSuccessToast() {
+ String title = directPrefs.getString("Title", "");
+ String successStringTemplate = context.getString(R.string.successful_wikidata_edit);
+ String successMessage = String.format(Locale.getDefault(), successStringTemplate, title);
+ ViewUtil.showLongToast(context, successMessage);
+ }
+
+ /**
+ * Formats and returns the filename as accepted by the wiki base API
+ * https://www.mediawiki.org/wiki/Wikibase/API#wbcreateclaim
+ *
+ * @param fileName
+ * @return
+ */
+ private String getFileName(String fileName) {
+ fileName = String.format("\"%s\"", fileName.replace("File:", ""));
+ Timber.d("Wikidata property name is %s", fileName);
+ return fileName;
+ }
+}
diff --git a/app/src/main/res/layout/pic_of_day_app_widget.xml b/app/src/main/res/layout/pic_of_day_app_widget.xml
new file mode 100644
index 000000000..6b0559b37
--- /dev/null
+++ b/app/src/main/res/layout/pic_of_day_app_widget.xml
@@ -0,0 +1,25 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values-ab/strings.xml b/app/src/main/res/values-ab/strings.xml
index 76caf25de..2f91f2074 100644
--- a/app/src/main/res/values-ab/strings.xml
+++ b/app/src/main/res/values-ab/strings.xml
@@ -14,7 +14,7 @@
Аҭалара қәҿиарала имҩаҧысит!
Асистемахь аҭалараан агха!
Афаил ҧшаам. Даҽа фаилк шәахәаҧш.
- Аутентификациа агха!
+ Аутентификациа агха!
Аҭагалара иалагоуп!
%1$s иҭагалоуп!
Шәақәыӷәӷәа иҭагалоу афаил ахәаҧшраз
diff --git a/app/src/main/res/values-ar/strings.xml b/app/src/main/res/values-ar/strings.xml
index b8e98ac40..fb18f34f2 100644
--- a/app/src/main/res/values-ar/strings.xml
+++ b/app/src/main/res/values-ar/strings.xml
@@ -27,7 +27,7 @@
تم الدخول بشكل صحيح!
فشل تسجيل الدخول
الملف غير موجود. فضلا اختر ملفا آخر.
- فشل الاستيقان!
+ فشل الاستيقان!
بدأ الرفع!
رُفع %1$s!
انقر لعرض ملفك المرفوع
diff --git a/app/src/main/res/values-ast/strings.xml b/app/src/main/res/values-ast/strings.xml
index f7301c45d..b4012047f 100644
--- a/app/src/main/res/values-ast/strings.xml
+++ b/app/src/main/res/values-ast/strings.xml
@@ -21,7 +21,7 @@
¡Identificación correuta!
¡Falló l\'aniciu de sesión!
Nun s\'alcontró\'l ficheru. Tenta con otru.
- ¡Falló la identificación!
+ Falló la identificación, anicia sesión nuevamente
Principió la xuba
¡%1$s xubíu!
Toque pa ver la xuba
@@ -103,7 +103,7 @@
Llicencia predeterminada
Usar un títulu/descripción anterior
Llograr automáticamente l\'allugamientu actual
- Recuperar l\'allugamientu actual pa ufiertar suxerencies de categoríes si la imaxe nun tien etiquetes xeográfiques
+ Recupera la posición actual si la imaxe nun tien etiquetes xeográfiques, y marca la imaxe con ella. Atención: Esto revelará\'l to allugamientu actual.
Mou nocherniegu
Usar tema escuru
Reconocimientu-CompartirIgual 4.0
@@ -272,6 +272,10 @@
Compartir app
Nun s\'especificaron les coordenaes al escoyer la imaxe
Error al llograr los llugares cercanos.
+ Semeya del día
+ Semeya del día
+ Añadióse correutamente la imaxe a %1$s en Wikidata.
+ Nun pudo anovase la entidá de Wikidata correspondiente.
Definir fondu
Fondu definíu correutamente
diff --git a/app/src/main/res/values-b+sr+Latn/strings.xml b/app/src/main/res/values-b+sr+Latn/strings.xml
index 81b826d36..557be7714 100644
--- a/app/src/main/res/values-b+sr+Latn/strings.xml
+++ b/app/src/main/res/values-b+sr+Latn/strings.xml
@@ -16,7 +16,7 @@
Uspešno ste prijavljeni.
Prijavljivanje nije uspelo.
Datoteka nije pronađena. Pokušajte sa drugom datotekom.
- Provera identiteta nije uspela.
+ Provera identiteta nije uspela.
Otpremanje je započeto.
Datoteka „%1$s“ je otpremljena.
Tapnite da biste videli otpremanje
@@ -94,7 +94,7 @@
Licenca
Koristi prethodan naslov/opis
Automatski detektuj trenutnu lokaciju
- Primi trenutnu lokaciju da bi predložili kategoriju ako slika nije geografski označena
+ Primi trenutnu lokaciju da bi predložili kategoriju ako slika nije geografski označena
Noćni režim
Koristiti tamnu temu
Autorstvo-Deliti pod istim uslovima 4.0
diff --git a/app/src/main/res/values-ba/strings.xml b/app/src/main/res/values-ba/strings.xml
index f54a93831..f91a69803 100644
--- a/app/src/main/res/values-ba/strings.xml
+++ b/app/src/main/res/values-ba/strings.xml
@@ -23,7 +23,7 @@
Танышыу уңышлы үтте
Танылыу хатаһы
Файл табылманы. Башҡа файлды эҙлә.
- Кем икәнегеҙ танылманы!
+ Кем икәнегеҙ танылманы!
Тейәү башланды!
%1$s тейәлде!
Ошонда баҫып тейәлгән файлды ҡара
@@ -100,7 +100,7 @@
Нығытылған рөхсәтнамә
Алдағы атама/һәрәтләмәне ҡулланыу
Автомат рәүешендә сираттағы урынды алыу
- Әгәр рәсемдең геотегтары булмаһа, категориялар үҙенән-үҙе тәҡдим ителһен өсөн сираттағы урынды алырға
+ Әгәр рәсемдең геотегтары булмаһа, категориялар үҙенән-үҙе тәҡдим ителһен өсөн сираттағы урынды алырға
Төнгө режим
Ҡараңғы теманы ҡулланыу
Attribution-ShareAlike 4.0
diff --git a/app/src/main/res/values-bg/strings.xml b/app/src/main/res/values-bg/strings.xml
index 28b092add..d6a675bf2 100644
--- a/app/src/main/res/values-bg/strings.xml
+++ b/app/src/main/res/values-bg/strings.xml
@@ -18,7 +18,7 @@
Успешно влизане.
Неуспешно влизане!
Файлът не е намерен. Моля, опитайте с друг файл.
- Неуспешен опит за удостоверяване!
+ Неуспешен опит за удостоверяване!
Качването започна!
Файл %1$s е качен!
Докоснете, за да видите качения файл
diff --git a/app/src/main/res/values-bn/strings.xml b/app/src/main/res/values-bn/strings.xml
index 69bb5fc33..d9aa9e657 100644
--- a/app/src/main/res/values-bn/strings.xml
+++ b/app/src/main/res/values-bn/strings.xml
@@ -29,7 +29,7 @@
প্রবেশ সফল!
প্রবেশ ব্যর্থ :(
ফাইল পাওয়া যায়নি। আরেকটি ফাইল চেষ্টা করুন।
- প্রমাণীকরণ ব্যর্থ হয়েছে!
+ প্রমাণীকরণ ব্যর্থ হয়েছে, আবার প্রবেশ করুন
আপলোড আরম্ভ হয়েছে!
%1$s আপলোড হয়েছে!
আপনার আপলোড দেখতে টোকা দিন
@@ -110,7 +110,7 @@
পূর্বনির্ধারিত লাইসেন্স
পূর্ববর্তী শিরোনাম/বিবরণ ব্যবহার করুন
স্বয়ংক্রিয়ভাবে বর্তমান অবস্থান পান
- বিষয়শ্রেণীর পরামর্শ দিতে বর্তমান অবস্থান পান যদি ছবিতে ভূ-ট্যাগ না থেকে থাকে
+ বিষয়শ্রেণীর পরামর্শ দিতে বর্তমান অবস্থান পান যদি ছবিতে ভূ-ট্যাগ না থেকে থাকে
রাত্রি মোড
কালো থিম ব্যবহার করুন
অ্যাট্রিবিউশন-শেয়ারঅ্যালাইক ৪.০
diff --git a/app/src/main/res/values-br/strings.xml b/app/src/main/res/values-br/strings.xml
index 2a7cd2284..90ede0b76 100644
--- a/app/src/main/res/values-br/strings.xml
+++ b/app/src/main/res/values-br/strings.xml
@@ -3,6 +3,7 @@
* Dishual
* Fohanno
* Fulup
+* Gwendal
* Gwenn-Ael
* Y-M D
-->
@@ -24,7 +25,7 @@
Kevreet oc\'h !
Kudenn gevreañ !
N\'eo ket bet kavet ar restr. Klask gant unan all.
- Dilesadur c\'hwitet!
+ Dilesadur c\'hwitet!
Kroget da enporzhiañ!
%1$s bet enporzhiet !
Pouezit evit gwelet hoc\'h enporzhiadenn
@@ -102,7 +103,7 @@
Aotre-implijout dre ziouer
Ober gant an titl/deskrivadur kent
Tapout al lec\'hiadur red ent emgefre
- Kavout al lec\'hiadur red evit pourchas kinnigoù rummadoù ma n\'eo ket douarlec\'hiet ar skeudenn.
+ Kavout al lec\'hiadur red evit pourchas kinnigoù rummadoù ma n\'eo ket douarlec\'hiet ar skeudenn.
Mod noz
Ober gant an tem teñval
Deroadur-RannañHeñvel 4.0
@@ -217,4 +218,5 @@
N\'eo ket cheñchet al lec\'hiadur.
Kaout urzhioù
Lenn ar pennad
+ Skeudenn an deiz
diff --git a/app/src/main/res/values-bs/strings.xml b/app/src/main/res/values-bs/strings.xml
index e6667a083..1a8253fa3 100644
--- a/app/src/main/res/values-bs/strings.xml
+++ b/app/src/main/res/values-bs/strings.xml
@@ -16,7 +16,7 @@
Prijavljivanje uspješno!
Prijavljivanje nije uspjelo!
Datoteka nije pronađena. Pokušajte drugu.
- Provjera identiteta nije uspjela!
+ Provjera identiteta nije uspjela!
Postavljanje je započelo!
Datoteka %1$s je postavljena!
Dodirnite da biste vidjeli datoteku
@@ -93,7 +93,7 @@
Licenca
Koristi prethodni naziv/opis
Automatski dobavi trenutnu lokaciju
- Dobavi trenutnu lokaciju za davanje prijedloga o kategorijama ako nema geooznaku
+ Dobavi trenutnu lokaciju za davanje prijedloga o kategorijama ako nema geooznaku
Noćni režim
Koristi tamnu temu
Autorstvo-Dijeliti pod istim uslovima 4.0
diff --git a/app/src/main/res/values-ca/strings.xml b/app/src/main/res/values-ca/strings.xml
index aaf5ca737..98a120f00 100644
--- a/app/src/main/res/values-ca/strings.xml
+++ b/app/src/main/res/values-ca/strings.xml
@@ -23,7 +23,7 @@
S\'ha iniciat sessió correctament!
Error en iniciar la sessió!
No s\'ha trobat el fitxer. Proveu-ho amb un altre fitxer.
- L\'autenticació ha fallat!
+ L’autenticació ha fallat. Torneu a provar d’iniciar una sessió.
Ha començat la càrrega!
S’ha pujat %1$s.
Prem per veure la teva càrrega
diff --git a/app/src/main/res/values-cs/strings.xml b/app/src/main/res/values-cs/strings.xml
index eb87ab83e..192656956 100644
--- a/app/src/main/res/values-cs/strings.xml
+++ b/app/src/main/res/values-cs/strings.xml
@@ -33,7 +33,7 @@
Přihlášení uspělo!
Přihlášení se nezdařilo!
Soubor nebyl nalezen. Prosím, zkuste jiný soubor.
- Ověření se nezdařilo!
+ Ověření se nezdařilo, prosím přihlaste se znovu
Nahrávání začalo!
%1$s nahráno!
Klepnutím zobrazíte upload
@@ -113,7 +113,7 @@
Výchozí licence
Použít předchozí název a popis
Automaticky získat aktuální polohu
- Nabídnout kategorie na základě aktuální polohy (pokud není obrázek opatřen souřadnicemi)
+ Nabídnout kategorie na základě aktuální polohy (pokud není obrázek opatřen souřadnicemi)
Noční režim
Použít tmavý režim
Uveďte autora-Zachovejte licenci 4.0
diff --git a/app/src/main/res/values-csb/strings.xml b/app/src/main/res/values-csb/strings.xml
index f04df2055..3fab9c6fb 100644
--- a/app/src/main/res/values-csb/strings.xml
+++ b/app/src/main/res/values-csb/strings.xml
@@ -14,7 +14,7 @@
Ùdałi logòwanié!
Logòwanié nie darzëło sã!
Felënk lopka. Proszã spróbòwac znowa.
- Fela ùdowierzaniô!
+ Fela ùdowierzaniô!
Wladënk zrëszony!
%1$s wladowóné!
Tkni, abë òbôczëc ladowóny lopk
diff --git a/app/src/main/res/values-cy/strings.xml b/app/src/main/res/values-cy/strings.xml
index ca00d1c4d..134c2f116 100644
--- a/app/src/main/res/values-cy/strings.xml
+++ b/app/src/main/res/values-cy/strings.xml
@@ -5,18 +5,25 @@
* Robin Owain
-->
+ Ymddangosiad
+ Cyffredinol
+ Adborth
+ Lleoliad
Comin Wicimedia
+ •
Gosodiadau
Enw defnyddiwr
Cyfrinair
+ Mewngofnodwch i\'ch cyfri Comin Beta
Mewngofnodi
+ Anghofiwyd y Cyfrinair?
Cofrestru
Wrthi\'n mewngofnodi
Disgwyliwch…
Llwyddodd y mewngofnodi!
Methodd y mewngofnodi!
Ni chafwyd hyd i\'r ffeil. Ceisiwch un arall.
- Methodd y dilysu!
+ Methodd y dilysu! Mewngofnodwch eto.
Dechreuodd yr uwchlwytho!
Uwchlwythwyd %1$s!
Tapiwch i weld eich uwchlwythiad
@@ -41,8 +48,10 @@
Rhannu
Agor yn y Porwr
Teitl
+ Rhowch deitl i\'r ffeil
Disgrifiad
Yn methu mewngofnodi - methodd y rhwydwaith
+ Methwyd mewngofnodi - gwirwch eich enw defnyddiwr a\'ch cyfrinair
Cafwyd gormod o ymgeision aflwyddiannus. Oedwch ennyd cyn ceisio eto.
Ymddiheurwn. Mae\'r defnyddiwr hwn wedi ei flocio ar Gomin Wikimedia
Mae\'n rhaid i chi roi eich cod adnabod 2 ffactor.
@@ -54,6 +63,7 @@
Archwilio\'r categorïau
Cadw
Ailgyrchu
+ Rhestr
Ataliwyd GPS ar eich dyfais. Ydych chi am ei droi\'n weithredol?
Gweithredu\'r GPS
Heb uwchlwytho eto
@@ -75,11 +85,12 @@
Categorïau
Gosodiadau
Cofrestru
+ Delweddau nodwedd
Amdanom
Ap Cynnwys Agored a grewyd ac a gefnogir gan wirfoddolwyr cymuned Wicimedia yw ap Comin Wicimedia. Does a wnelo Sefydliad Wicimedia ddim byd ag e (ei greu, ei gynnal na\'i ddatblygu).
\n\nCrewch <a href=\"https://github.com/commons-app/apps-android-commons/issues\">ymholiad GitHub</a> os oes gennych fyg, broblem neu awgrym.
- <a href=\"https://github.com/commons-app/apps-android-commons/wiki/Privacy-policy\">Polisi Preifatrwydd</a>
- <a href=\"https://github.com/commons-app/apps-android-commons/blob/master/CREDITS\">Clod</a>
+ <u>Polisi preifatrwydd</u>
+ Clod a bri
Amdanom
Danfonwch Adborth (drwy Ebost)
Dim ebost client wedi\'i ganfod
@@ -91,10 +102,10 @@
Caiff y ddelwedd hon ei thrwyddedu yn ôl termau\'r drwydded %1$s.
Wrth gynnig y llun yma, rwy\'n datgan mai fy ngwaith i ydyw ac nad yw\'n cynnwys unrhyw beth dan hawlfrain, na hunlun, a\'i fod yn cadw at <a href=\"https://commons.wikimedia.org/wiki/Commons:Policies_and_guidelines\">Bolisiau Comin Wicimedia</a>.
Lawrlwytho
- Trwydded
+ Trwydded Ddiofyn (\'default\')
Defnydiwch y teitl/disgrifiad blaenorol
Defnyddiwch y lleoliad cyfredol
- Canfyddwch eich lleoliad, er mwyn i ni gynnig categori (os nad ydych wedi nodi\'r cyfesurynnau).
+ Adfer eich lleoliad presennol os nad yw\'r ddelwedd yn cynnwys cyfesurynnau. Bydd hyn yn datgelu eich lleoliad chi!
Modd fin nos
Defnyddiwch thema tywyll
Attribution-ShareAlike 4.0
@@ -120,9 +131,19 @@
Cyhelir llawer o luniau ar Gomin Wicimedia sy\'n cael eu defnyddio ar Wicipedia.
Mae eich lluniau\'n gymorth i addysgu pobl drwy\'r byd mawr crwn!
Uwchlwythwch lluniau a dynnoch eich hun:
- - Natur (blodau, anifeiliaid, mynyddoedd)\n- Pethau defnyddiol (beic, tren, gorsaf drenau)\n- Enwogion (beirdd, athletwyr, blogwyr)
+ Gwrthrychau byd natur (blodau, anifeiliaid, mynyddoedd)\n- Gwrthrychau defnyddiol (beics, trenau, gorsafoedd trenau)\n- Enwogion (beirdd, athletwyr, blogwyr)
+ Gwrthrychau byd natur (blodau, anifeiliaid, mynyddoedd)
+ Gwrthrychau defnyddiol (beics, trenau, gorsafoedd trenau)
+ Enwogion (beirdd, athletwyr, blogwyr)
Peidiwch ag uwchlwytho:
- hunanluniau ohonoch chi na\'ch ffrindiau\n- lluniau a gawsoch o\'r we\n- sgrinluniau o apiau masnachol
+ Hunanluniau neu luniau o\'ch ffrindiau
+ Lluniau a lawrlwythwyd o\'r we gennych
+ Sgrinluniau o aps
+ Enghraifft o uwchlwythiad:
+ Teitl:Tŷ Opera Sydney
+ Disgrifiad: Golygfa o Dŷ Opera Sydney o ochr arall y bae
+ Categoriau: Tŷ Opera Sydney o\'r gorllewin
Cyfranwch luniau. Cynorthwywch Wicipedia i roi bywyd yn yr erthyglau!
Mae\'r delweddau ar Wicipedia\'n dod o\nGomin Wikimedia.
Mae eich delweddau\'n cynorthwyo i addysgu pobl ledled y byd.
@@ -135,6 +156,32 @@
Dim disgrifiad
Trwydded anhysbys
Adnewyddu
+ Iawn
+ Lleoedd Cyfagos
+ Ni chafwyd hyd i leoedd cyfagos
+ Rhybudd
+ Ydw
+ Nac ydw
Teitl
+ teitl y cyfrwng
Disgrifiad
+ Awdur
+ Dyddiad yr uwchlwythiad
+ Trwydded
+ Cyfesurynnau
+ Dim
+ Eitem Wicidata
+ Erthygl Wicipedia
+ Mewngofnodwch i\'ch cyfri
+ Danfonwch y ffeil log
+ Gweld yn y porwr
+ Nid yw\'r lleoliad wedi newid.
+ Nid yw\'r lleoliad ar gael.
+ Parhau
+ Canslo
+ Ailgeisio
+ Gwnaed!
+ Llun y Dydd
+ Llun y Dydd
+ Mae %1$s o luniau wedi\'u hychwanegu ar Wicidata!
diff --git a/app/src/main/res/values-da/strings.xml b/app/src/main/res/values-da/strings.xml
index fe194ce94..4a724e640 100644
--- a/app/src/main/res/values-da/strings.xml
+++ b/app/src/main/res/values-da/strings.xml
@@ -26,7 +26,7 @@
Du er nu logget på!
Det mislykkedes at logge på!
Filen blev ikke fundet. Forsøg med en anden fil.
- Godkendelse mislykkedes!
+ Godkendelse mislykkedes!
Overførsel begyndt!
%1$s overført!
Tryk for at få vist din upload
@@ -105,7 +105,7 @@
Standardlicens
Brug forrige titel/beskrivelse
Hent automatisk nuværende placering
- Hent nuværende placering for at tilbyde kategoriforslag hvis billedet ikke er geografisk mærket
+ Hent nuværende placering for at tilbyde kategoriforslag hvis billedet ikke er geografisk mærket
Nat-tilstand
Brug mørkt tema
Attribution-ShareAlike 4.0
diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml
index f0eab6cb2..f6fddb820 100644
--- a/app/src/main/res/values-de/strings.xml
+++ b/app/src/main/res/values-de/strings.xml
@@ -25,7 +25,7 @@
Anmeldung erfolgreich!
Anmeldung fehlgeschlagen!
Datei nicht gefunden. Bitte versuche es mit einer anderen.
- Authentifizierung fehlgeschlagen!
+ Authentifizierung fehlgeschlagen. Bitte erneut anmelden.
Hochladen gestartet!
„%1$s“ hochgeladen!
Tippe, um deinen Upload anzusehen
@@ -107,7 +107,7 @@
Standardlizenz
Vorherige(n) Titel/Beschreibung verwenden
Aktuellen Standort automatisch abrufen
- Ruft den aktuellen Standort ab, um Kategorievorschläge anzubieten, falls das Bild keine Geotags hat.
+ Ruft den aktuellen Standort ab, falls das Bild nicht georeferenziert ist und markiert es. Warnung: Diese Aktion verrät deinen aktuellen Standort.
Nachtmodus
Dunkles Thema verwenden
Attribution-ShareAlike 4.0
@@ -278,6 +278,10 @@
App teilen
Während der Bildauswahl wurden keine Koordinaten angegeben
Fehler beim Abrufen der Orte in der Nähe.
+ Bild des Tages
+ Bild des Tages
+ Bild erfolgreich nach %1$s auf Wikidata hinzugefügt!
+ Fehler bei der Aktualisierung des dazugehörigen Wikidata-Objekts!
Hintergrundbild festlegen
Hintergrundbild erfolgreich festgelegt!
diff --git a/app/src/main/res/values-diq/strings.xml b/app/src/main/res/values-diq/strings.xml
index 79da245fe..a312fcc9d 100644
--- a/app/src/main/res/values-diq/strings.xml
+++ b/app/src/main/res/values-diq/strings.xml
@@ -25,7 +25,7 @@
Cıkewtış hewl bi.
Nidekeweya de
Dosya nêvineya. Dosyê da bine bıcerebnê.
- Tesdiq kerdış nebı
+ Tesdiq kerdış nebı
Barkerdış sertera!
%1$s bıbar!
Barkerdışê xo pıro bıde.
diff --git a/app/src/main/res/values-el/strings.xml b/app/src/main/res/values-el/strings.xml
index 876b76924..02ba64754 100644
--- a/app/src/main/res/values-el/strings.xml
+++ b/app/src/main/res/values-el/strings.xml
@@ -29,7 +29,7 @@
Επιτυχής σύνδεση!
Η είσοδος απέτυχε!
Το αρχείο δεν βρέθηκε. Παρακαλώ δοκιμάστε ένα άλλο αρχείο.
- Απέτυχε ο έλεγχος ταυτότητας!
+ Απέτυχε ο έλεγχος ταυτότητας, παρακαλώ συνδεθείτε ξανά
Η αποστολή ξεκίνησε!
%1$s επιφορτώθηκε!
Πατήστε για να προβάλλετε την αποστολή
@@ -111,7 +111,7 @@
Προεπιλεγμένη άδεια
Χρήση προηγούμενου τίτλου/περιγραφής
Αυτόματη ανάκτηση τρέχουσας θέσης
- Ανάκτηση τρέχουσας τοποθεσία για να σας προσφέρουμε προτάσεις κατηγοριών αν η εικόνα δεν είναι γεωσεσημασμένη.
+ Ανακτά την τρέχουσα τοποθεσία εάν η εικόνα δεν είναι γεωσεσημασμένη, και τις γεωσημάνσεις με αυτή. Προειδοποίηση: Αυτό θα αποκαλύψει την τρέχουσα τοποθεσία σας.
Νυχτερινή λειτουργία
Χρήση σκοτεινού θέματος
Attribution-ShareAlike 4.0
@@ -282,6 +282,10 @@
Κοινοποίηση εφαρμογής
Οι συντεταγμένες δεν ορίστηκαν κατά την διάρκεια της επιλογής εικόνας
Σφάλμα κατά την εύρεση κοντινών μερών.
+ Φωτογραφία της Ημέρας
+ Φωτογραφία της Ημέρας
+ Η εικόνα προστέθηκε επιτυχώς στο %1$s στο Wikidata!
+ Αποτυχία ενημέρωσης της αντιστοιχούσας οντότητας του Wikidata!
Ρύθμιση ταπετσαρίας
Η ταπετσαρία ρυθμίστηκε επιτυχώς!
diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml
index 1be0ebd1a..4c065f214 100644
--- a/app/src/main/res/values-es/strings.xml
+++ b/app/src/main/res/values-es/strings.xml
@@ -30,7 +30,7 @@
Acceso correcto.
Acceso fallido.
No se encontró el archivo. Prueba con otro.
- Falló la autenticación.
+ Falló la autenticación; prueba a acceder otra vez
Ha comenzado la carga.
Se ha cargado %1$s.
Pulsa para ver tu subida
@@ -112,7 +112,7 @@
Licencia predeterminada
Usar título/descripción anteriores
Obtener ubicación actual automáticamente
- Recuperar ubicación actual para ofrecer sugerencias de categorías si la imagen no tiene etiquetas geográficas
+ Recuperar ubicación actual para ofrecer sugerencias de categorías si la imagen no tiene etiquetas geográficas
Modo nocturno
Usar tema oscuro
Atribución-CompartirIgual 4.0
@@ -276,4 +276,6 @@
Compartir aplicación
No se especificaron las coordenadas al seleccionar la imagen
Error al recuperar los lugares cercanos.
+ Foto del día
+ Foto del día
diff --git a/app/src/main/res/values-eu/strings.xml b/app/src/main/res/values-eu/strings.xml
index 1f2ed1fca..78f6e5a20 100644
--- a/app/src/main/res/values-eu/strings.xml
+++ b/app/src/main/res/values-eu/strings.xml
@@ -25,7 +25,7 @@
Saio hasiera egina
Saio hasieran akatsa!
Fitxategia ez da aurkitu. Mesedez saiatu beste batekin.
- Autentifikazioan akatsa!
+ Autentifikazioan akatsa!
Igoera hasi da!
%1$s igotzen!
Ukitu igotakoa ikusteko
diff --git a/app/src/main/res/values-fa/strings.xml b/app/src/main/res/values-fa/strings.xml
index f546d2a2b..fe87d5736 100644
--- a/app/src/main/res/values-fa/strings.xml
+++ b/app/src/main/res/values-fa/strings.xml
@@ -28,7 +28,7 @@
ورود موفق!
ورود ناموفق!
پرونده یافت نشد لطفاً پرونده دیگری را امتحان کنید.
- تأیید اعتبار انجام نشد!
+ تأیید اعتبار انجام نشد!
بارگذاری آغاز شد!
%1$s بارگذاری شد!
برای دیدن بارگذاریتان بر روی صفحه انگشت بزنید
@@ -104,7 +104,7 @@
مجوز پیشفرض
از عنوان/توضیحات پیشین استفاده کنید
دریافت خودکار موقعیت کنونی
- درحال دریافت موقعیت برای پیشنهاد رده در صورتی که برچسب جغرافیایی وجود نداشته باشد.
+ درحال دریافت موقعیت برای پیشنهاد رده در صورتی که برچسب جغرافیایی وجود نداشته باشد.
حالت شبانه
استفاده از حالت تیره
CC Attribution-ShareAlike 4.0
diff --git a/app/src/main/res/values-fi/strings.xml b/app/src/main/res/values-fi/strings.xml
index b0ea9ce0f..7ecba53a5 100644
--- a/app/src/main/res/values-fi/strings.xml
+++ b/app/src/main/res/values-fi/strings.xml
@@ -30,7 +30,7 @@
Kirjautuminen onnistui!
Kirjautuminen epäonnistui!
Tiedostoa ei löytynyt. Yritä toista tiedostoa.
- Tunnistautuminen epäonnistui!
+ Tunnistautuminen epäonnistui!
Tallentaminen aloitettiin!
%1$s tallennettiin!
Napauta katsoaksesi tallennusta
@@ -110,7 +110,7 @@
Oletuslisenssi
Käytä edellistä otsikkoa/kuvausta
Hae tämänhetkinen sijainti automaattisesti
- Nouda nykyinen sijainti asettaaksesi käyttöön luokkaehdotuksia jos kuva ei ole paikkamerkitty
+ Nouda nykyinen sijainti asettaaksesi käyttöön luokkaehdotuksia jos kuva ei ole paikkamerkitty
Yötila
Käytä tummaa teemaa
Nimeä-JaaSamoin 4.0
diff --git a/app/src/main/res/values-fo/strings.xml b/app/src/main/res/values-fo/strings.xml
index 58e26797b..369c59d5d 100644
--- a/app/src/main/res/values-fo/strings.xml
+++ b/app/src/main/res/values-fo/strings.xml
@@ -12,7 +12,7 @@
Vinarliga bíða…
Innritan væleydnað!
Innritan miseydnaðist
- Góðkenning miseydnaðist!
+ Góðkenning miseydnaðist!
Upplóting er byrjað!
%1$s er lagt út!
Trýst fyri at síggja tað sum tú legði út
diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml
index e0826cc00..63eb7eefb 100644
--- a/app/src/main/res/values-fr/strings.xml
+++ b/app/src/main/res/values-fr/strings.xml
@@ -1,6 +1,7 @@
@@ -17,7 +18,7 @@
אריינלאגירט מיט הצלחה!
ארײַנלאגירן אדורכגעפאלן!
טעקע נישט געראפן. פרובירט אפשר אן אנדער טעקע.
- אויטענטיפֿיצירן דורכגעפֿאלן!
+ אויטענטיפֿיצירן דורכגעפֿאלן!
ארויפלאדן אנגעהויבן!
%1$s ארויפגעלאדן!!
דרוקט צו זען אײַער ארויפֿלאד
@@ -115,4 +116,7 @@
פֿידבעק
אַרויסלאָגירן
רעקאמנדירט
+ בילד פונעם טאָג
+ בילד פונעם טאָג
+ ס׳איז ניט געלונגען צו דערהיינטיקן דעם אַנטשפּרעכנדיקן בלאַט אין וויקידאַטן.
diff --git a/app/src/main/res/values-jv/strings.xml b/app/src/main/res/values-jv/strings.xml
index a4a85c80f..c02290b4c 100644
--- a/app/src/main/res/values-jv/strings.xml
+++ b/app/src/main/res/values-jv/strings.xml
@@ -14,7 +14,7 @@
Kasil mlebu log!
Wurung mlebu log!
Barkas ora katemu. Jajalana barkas liyané.
- Wurung otèntifikasi!
+ Wurung otèntifikasi!
Wiwit ngunggah!
%1$s kaunggah!
Dudul saperlu ndeleng unggahané panjenengan
diff --git a/app/src/main/res/values-ka/strings.xml b/app/src/main/res/values-ka/strings.xml
index 60320dd9e..31103028e 100644
--- a/app/src/main/res/values-ka/strings.xml
+++ b/app/src/main/res/values-ka/strings.xml
@@ -15,7 +15,7 @@
სისტემაში შესვლა წარმატებით განხორციელდა!
სისტემაში შესვლა ვერ განხორციელდა!
ფაილი არ მოიძებნა. გთხოვთ, სცადოთ სხვა ფაილი.
- ავთენტიფიკაცია ვერ მოხერხდა!
+ ავთენტიფიკაცია ვერ მოხერხდა!
ატვირთვა დაიწყო!
%1$ ატვირთულია!
დააჭირეთ თქვენი ატვირთვის სანახავად
diff --git a/app/src/main/res/values-kab/strings.xml b/app/src/main/res/values-kab/strings.xml
index c88a34978..f465ab913 100644
--- a/app/src/main/res/values-kab/strings.xml
+++ b/app/src/main/res/values-kab/strings.xml
@@ -15,7 +15,7 @@
Tuqqna tedda!
Tqqna ur teddi ara!
Ulac afaylu. Ɛreḍ wayeḍ ma ulac aɣilif.
- Asesteb yecceḍ!
+ Asesteb yecceḍ!
Asali yebda!
%1$s yuli!
Senned akken ad twaliḍ asali-ik
@@ -93,7 +93,7 @@
Turagt
Seqdec azwel neɣ aglam yezrin
Awi s wudem awurman adig amiran
- Awi adig amiran akken ad tsumreḍ taggayt ma yella tugna ur tettwacreḍ ara di tirakalt
+ Awi adig amiran akken ad tsumreḍ taggayt ma yella tugna ur tettwacreḍ ara di tirakalt
Askar n yiḍ
Seqdec asentel aberkan
Attribution-ShareAlike 4.0
diff --git a/app/src/main/res/values-km/strings.xml b/app/src/main/res/values-km/strings.xml
index ff4a6ce79..ba7a57655 100644
--- a/app/src/main/res/values-km/strings.xml
+++ b/app/src/main/res/values-km/strings.xml
@@ -4,56 +4,75 @@
* វ័ណថារិទ្ធ
-->
- Wikimedia Commons
- ការកំណត់នានា
- ឈ្មោះអ្នកប្រើប្រាស់
- លេខសម្ងាត់
- ឡុកអ៊ីន
- កំពុងឡុកអ៊ីន
- សូមរង់ចាំ…
- កត់ឈ្មោះចូលបានសំរេច
- កត់ឈ្មោះចូលបរាជ័យ
- Authentication បានបរាជ័យ!
- បានចាប់ផ្តើមការផ្ទុកឡើង!
- %1$s បានផ្ទូកឡើងហើយ!
- Tap ដើម្បីមើលការផ្ទុកឡើងរបស់អ្នក
- កំពុងចាប់ផ្តើម ផ្ទុកឡើង %1$s
- %1$s កំពុងផ្ទូកឡើង
+ ការរចនា
+ ទូទៅ
+ មតិយោបល់
+ ទីកន្លែង
+ វិគីមេឌារួម
+ ការកំណត់
+ អត្តនាម
+ ពាក្យសម្ងាត់
+ កត់ឈ្មោះចូលទៅក្នុងគណនីវិគីមេឌារួមបេតា
+ កត់ឈ្មោះចូល
+ ពេលភ្លេចពាក្យសម្ងាត់
+ ចុះឈ្មោះ
+ កំពុងកត់ឈ្មោះចូល
+ សូមរង់ចាំបន្តិច…
+ កត់ឈ្មោះចូលបានសម្រេច!
+ កត់ឈ្មោះចូលមិនបានសម្រេច!
+ រកមិនឃើញឯកសារទេ។ សូមសាកល្បងជាមួយឯកសារផ្សេងមួយទៀត។
+ ការបញ្ជាក់ទទួលស្គាល់មិនបានសម្រេច។ សូមកត់ឈ្មោះចូលម្ដងទៀត។
+ ការផ្ទុកឡើងបានចាប់ផ្តើមហើយ!
+ បានផ្ទុកឡើង %1$s ហើយ!
+ ចុចដើម្បីមើលអ្វីដែលអ្នកផ្ទុកឡើង
+ កំពុងចាប់ផ្តើមផ្ទុកឡើង %1$s
+ កំពុងផ្ទុកឡើង %1$s
កំពុងបញ្ចប់ការផ្ទុកឡើង %1$s
- ការផ្ទុកឡើង %1$s បានបរាជ័យ
- Tap ដើម្បីមើល
- ការផ្ទុកឡើងរបស់ខ្ញុំ
- បានដាក់ក្នុងជួររង់ចាំ
- បានបរាជ័យ
- %1$d%% រួចរាល់
+ ការផ្ទុកឡើង %1$s មិនបានសម្រេច
+ ចុចដើម្បីមើល
+ អ្វីដែលខ្ញុំផ្ទុកឡើងថ្មីៗ
+ ក្នុងជួររង់ចាំ
+ មិនបានសម្រេច
+ រួចរាល់បាន %1$d%%
កំពុងផ្ទុកឡើង
ពីវិចិត្រសាល
ថតរូប
- ការផ្ទុកឡើងរបស់ខ្ញុំ
- ចែករំលែក
- មើលក្នុង browser
+ ជិតខាង
+ អ្វីដែលខ្ញុំផ្ទុកឡើង
+ ចែកចាយ
+ មើលក្នុងឧបករណ៍រាយរក
ចំណងជើង
- បរិយាយ
- មិនអាចកត់ឈ្មោះចូល - បណ្តាញ network បរាជ័យ
- ការព្យាយាមមិនបានសម្រេចមានចំនួនច្រើនដងពេក។ សូមព្យាយាមម្តងទៀតនៅប៉ុន្មាននាទីក្រោយ។
- សូមអភ័យទោស អ្នកប្រើប្រាស់រូបនេះត្រូវបានហាមឃាត់នៅ Commons
- កត់ឈ្មោះចូលបរាជ័យ
+ សូមដាក់ចំណងជើងអោយឯកសារនេះ
+ ការពិពណ៌នា
+ មិនអាចកត់ឈ្មោះចូលបានទេ ព្រោះបណ្ដាញកំពុងមានបញ្ហា។
+ មិនអាចកត់ឈ្មោះចូលបានទេ។ សូមពិនិត្យអត្តនាមនិងពាក្យសម្ងាត់របស់អ្នកឡើងវិញ។
+ ការព្យាយាមមិនបានសម្រេចច្រើនដងពេក។ សូមព្យាយាមម្តងទៀតនៅប៉ុន្មាននាទីក្រោយ។
+ សូមអភ័យទោស អ្នកប្រើប្រាស់រូបនេះត្រូវបានហាមឃាត់នៅវិគីមេឌារួម
+ អ្នកចាំបាច់ត្រូវតែផ្ដល់លេខកូដសម្រាប់បញ្ជាក់ទទួលស្គាល់ពីរតង់។
+ កត់ឈ្មោះចូលមិនបានសម្រេច
ផ្ទុកឡើង
ដាក់ឈ្មោះឲ្យសំនុំនេះ
បម្រែបម្រួល
ផ្ទុកឡើង
ស្វែងរកចំណាត់ថ្នាក់ក្រុម
រក្សាទុក
+ ផ្ទុកឡើងវិញ
+ បញ្ជី
+ ឧបករណ៍របស់អ្នកកំពុងបិទមិនប្រើGPS។ តើអ្នកចង់បើកវាប្រើទេ?
+ បើកប្រើGPS
+ គ្មានអ្វីដែលបានផ្ទុកឡើងទេ
រកមិនឃើញចំណាត់ថ្នាក់ក្រុមដែលត្រូវនឹង %1$s ទេ
- បន្ថែមចំណាត់ថ្នាក់ក្រុមអោយរូបភាពរបស់អ្នកដើម្បីអោយងាយស្រួលស្វែងរក្នុង Wikimedia Commons។\nចាប់ផ្ដើមវាយបញ្ចូលឈ្មោះចំណាត់ថ្នាក់ក្រុម។\nចុចលើសារនេះ (ឬចុចប៊ូតុងត្រលប់ក្រោយ)ដើម្បីរំលងជំហ៊ាននេះ។
+ បន្ថែមចំណាត់ថ្នាក់ក្រុមអោយរូបភាពរបស់អ្នកដើម្បីអោយងាយស្រួលស្វែងរកក្នុងវិគីមេឌារួម។ ចាប់ផ្ដើមវាយបញ្ចូលឈ្មោះចំណាត់ថ្នាក់ក្រុម។
ចំណាត់ថ្នាក់ក្រុម
ការកំណត់
+ ចុះឈ្មោះ
+ រូបភាពឆ្នើម
អំពី
សូហ្វវែរប្រភពបើកទូលាយត្រូវបានចេញផ្សាយក្រោមអាជ្ញាបណ្ណ <a href=\"https://github.com/commons-app/apps-android-commons/blob/master/COPYING\">Apache License v2</a>
ប្រភពកូដមាននៅ <a href=\"https://github.com/commons-app/apps-android-commons\">GitHub</a>. Bugs មាននៅ <a href=\" https://github.com/commons-app/apps-android-commons/issues\">Github</a>.
- <a href=\"https://wikimediafoundation.org/wiki/Privacy_policy\">គោលការភាពជាឯកជន</a>
+ <a href=\"https://wikimediafoundation.org/wiki/Privacy_policy\">គោលការភាពឯកជន</a>
អំពី
- ផ្ញើមតិកែលម្អ (តាមអ៊ីមែល)
+ ផ្ញើមតិយោបល់ (តាមអ៊ីមែល)
ចំណាត់ថ្នាក់ក្រុមដែលត្រូវបានប្រើថ្មីៗ
កំពុងរង់ចាំ ធ្វើការ sync ជាលើកដំបូង…
អ្នកមិនទាន់បានផ្ទុករូបថតណាមួយឡើងនៅឡើយទេ។
@@ -90,4 +109,25 @@
គ្មានការពណ៌នា
Unknown license
ធ្វើឱ្យស្រស់
+ រូបភាពផ្ទៃខាងក្រោយ
+ គ្មានរូបភាព
+ ផ្ទុករូបភាពឡើង
+ ភ្នំហ្សាអូ
+ ស្ពានឥន្ទធនូ
+ បោះបង់
+ បើក
+ បិទ
+ ទំព័រដើម
+ ផ្ទុកឡើង
+ ជិតខាង
+ អំពី
+ ការកំណត់
+ មតិយោបល់
+ កត់ឈ្មោះចេញ
+ រៀនប្រើ
+ សារជូនដំណឹង
+ ឆ្នើម
+ គ្មានការពិពណ៌នា
+ វត្ថុក្នុងវិគីទិន្នន័យ
+ អត្ថបទវិគីភីឌា
diff --git a/app/src/main/res/values-ko-rKP/strings.xml b/app/src/main/res/values-ko-rKP/strings.xml
index ccbe86100..4a12d8efb 100644
--- a/app/src/main/res/values-ko-rKP/strings.xml
+++ b/app/src/main/res/values-ko-rKP/strings.xml
@@ -15,7 +15,7 @@
가입 성공!
가입 실패!
서류를 찾을수 없습니다. 다른 서류를 사용해주십시오.
- 인증 실패!
+ 인증 실패!
올리적재를 시작했습니다!
%1$s 서류를 올리적재하였습니다!
당신의 올리적재를 보려면 두드리세요
@@ -89,7 +89,7 @@
허가권
이전의 제목/설명을 사용하기
자동으로 현재 위치 얻기
- 화상에 지리정보꼬리표가 달려 있지 않다면, 현재 위치를 검색하여 분류를 제안해주십시오
+ 화상에 지리정보꼬리표가 달려 있지 않다면, 현재 위치를 검색하여 분류를 제안해주십시오
야간방식
어두운 주제 쓰기
저작자표시-동일조건변경허락 4.0
diff --git a/app/src/main/res/values-ko/strings.xml b/app/src/main/res/values-ko/strings.xml
index 4cd947699..05f001e24 100644
--- a/app/src/main/res/values-ko/strings.xml
+++ b/app/src/main/res/values-ko/strings.xml
@@ -1,6 +1,7 @@
Xuyabûn
@@ -79,4 +80,6 @@
Destûr bide
<u>Pirsên ku pir têne pirsîn</u>
Rênîşandanê derbas bike
+ Wêneya rojê
+ Wêneya rojê
diff --git a/app/src/main/res/values-ky/strings.xml b/app/src/main/res/values-ky/strings.xml
index 33e79bd3c..24e1fb1d7 100644
--- a/app/src/main/res/values-ky/strings.xml
+++ b/app/src/main/res/values-ky/strings.xml
@@ -13,7 +13,7 @@
Сураныч, күтө туруңуз…
Сиз ийгиликтүү кирдиңиз
Системага кирүүдө катачылык бар!
- Таану катачылыгы!
+ Таану катачылыгы!
Жүктөө башталды!
%1$s жүктөлүүдө
Жүктөлгөн файлды көрүү үчүн басыңыз
diff --git a/app/src/main/res/values-lb/strings.xml b/app/src/main/res/values-lb/strings.xml
index e39c06498..ffed21e4f 100644
--- a/app/src/main/res/values-lb/strings.xml
+++ b/app/src/main/res/values-lb/strings.xml
@@ -23,7 +23,7 @@
Umeldung huet geklappt!
D\'Aloggen huet net funktionéiert
Fichier net fonnt. probéiert w.e.g. en anere Fichier.
- Authentifizéierung huet net funktionéiert!
+ Authentifizéierung huet net funktionéiert, loggt Iech w.e.g. nach eng Kéier an.
D\'Eroplueden huet ugefaang!
%1$s eropgelueden!
Dréckt fir de Fichier ze gesinn deen Dir eropgelueden hutt
@@ -101,7 +101,7 @@
Standardlizenz
Viregen Titel/Beschreiwung benotzen
Automatesch déi aktuell Plaz kréien
- Aktuell Plaz ofruffe fir Propose fir Kategorien ze erméigleche wann d\'Bild keng Geotaggen huet
+ Aktuell Plaz ofruffe fir Propose fir Kategorien ze erméigleche wann d\'Bild keng Geotaggen huet
Nuetsmodus
Donkele Layout benotzen
Attribution-ShareAlike 4.0
@@ -242,6 +242,9 @@
Keng Biller fonnt!
Feeler beim Eropluede vu Biller.
Eropgeluede vum: %1$s
+ Bild vum Dag
+ Bild vum Dag
+ Déi entspriechend Wikidata-Entitéit konnt net aktualiséiert ginn!
Hannergrondbild festleeën
Hannergrondbild festgeluecht
diff --git a/app/src/main/res/values-li/strings.xml b/app/src/main/res/values-li/strings.xml
index f92167522..48733bd97 100644
--- a/app/src/main/res/values-li/strings.xml
+++ b/app/src/main/res/values-li/strings.xml
@@ -22,7 +22,7 @@
Aanmelje gelök!
Aanmelje mislök!
Bestandj neet gevónje. Perbeer \'n anger bestandj.
- Verificatie mislök!
+ Verificatie mislök!
Upload begós!
%1$s upgeloadj!
Wies aan veur dienen upload te betrachte
@@ -103,7 +103,7 @@
Standerdlicentie
Gebroek veurige naam/besjrieving
Haol autematis de hujige locatie op
- Haol de hujige locatie op veur categorieveurstèlle te make wen \'t bild gein geotags haet
+ Haol de hujige locatie op veur categorieveurstèlle te make wen \'t bild gein geotags haet
Nachmodus
Gebroeker duuster thema
Naamsvermeljing-GeliekDeile 4.0
diff --git a/app/src/main/res/values-lt/strings.xml b/app/src/main/res/values-lt/strings.xml
index 7fc63d8b9..08397cb0b 100644
--- a/app/src/main/res/values-lt/strings.xml
+++ b/app/src/main/res/values-lt/strings.xml
@@ -7,18 +7,25 @@
* Zygimantus
-->
+ Išvaizda
+ Bendra
+ Atsiliepimai
+ Vietovė
Vikiteka
+ •
Nustatymai
Naudotojo vardas
Slaptažodis
+ Prisijunkite prie savo Commons Beta paskyros
Prisijungti
+ Pamiršote Slaptažodį?
Užsiregistruoti
Jungiamasi
Prašome palaukti…
Sėkmingai prisijungėte!
Prisijungti nepavyko!
Failas nerastas. Prašome pabandyti kitą failą.
- Autentifikavimas nepavyko!
+ Autentifikavimas nepavyko, prašome prisijungti dar kartą
Įkėlimas prasidėjo!
%1$s įkelta!
Bakstelėkite norėdami peržiūrėti jūsų įkėlimą
@@ -43,10 +50,13 @@
Dalintis
Atidaryti naršyklėje
Pavadinimas
+ Prašome pateikti šiam failui pavadinimą
Aprašymas
Negalima prisijungti - tinklo klaida
+ Nepavyko prisijungti - prašome patikrinti savo naudotojo vardą ir slaptažodį
Per daug nesėkmingų bandymų. Pabandykite dar kartą po keleto minučių.
Atsiprašome, šis vartotojas buvo užblokuotas Commons
+ Turite pateikti savo dviejų žingsnių patvirtinimo kodą.
Prisijungti nepavyko
Įkelti
Pavadinkite šį rinkinį
@@ -55,6 +65,7 @@
Ieškoti kategorijas
Išsaugoti
Atnaujinti
+ Sąrašas
GPS išjungta jūsų įrenginyje. Ar norite įjungti?
Išjungti GPS
Nėra įkėlimų kol kas
@@ -76,8 +87,10 @@
Kategorijos
Nustatymai
Užsiregistruoti
+ Rinktiniai Paveikslėliai
Apie
- <a href=\"https://github.com/commons-app/apps-android-commons/wiki/Privacy-policy\">Privatumo politika</a>
+ <u>Privatumo politika</u>
+ <u>Kūrėjai</u>
Apie
Siųsti Atsiliepimą (El. paštu)
Nėra įdiegtos el. pašto tvarkyklės
@@ -88,10 +101,10 @@
Atšaukti
Šio paveikslėlio licencija bus %1$s
Parsisiųsti
- Licencija
+ Numatytoji Licencija
Naudoti ankstesnį pavadinimą/aprašymą
Automatiškai gauti dabartinę vietą
- Gauti dabartinę vietove, kad būtų pasiūlytos kategorijos, jei paveikslėlis neturi geografinės žymės
+ Gauti dabartinę vietove, kad būtų pasiūlytos kategorijos, jei paveikslėlis neturi geografinės žymės
Naktinis režimas
Naudoti tamsią temą
CC BY-SA 4.0
@@ -99,8 +112,15 @@
Vikimedija Commons talpina daugumą paveikslėlių, kurie yra naudojami Vikipedijoje.
Jūsų paveikslėliai padeda šviesti žmones visame pasaulyje!
Prašome kelti nuotraukos, kurios yra padarytos ar sukurtos tik jūsų:
- - Gamtos objektai (gėlės, gyvūnai, kalnai)\n- Naudingi objektai (dviračiai, traukinių stotys)\n- Įžymūs žmonės (merai, Olimpiniai atletai)
+ Gamtos objektai (gėlės, gyvūnai, kalnai)\n• Naudingi objektai (dviračiai, traukinių stotys)\n• Įžymūs žmonės (jūsų meras, Olimpiniai atletai, kurios sutikote)
+ Gamtos objektai (gėlės, gyvūnai, kalnai)
+ Naudingi objektai (dviračiai, traukinių stotys)
+ Įžymūs žmonės (jūsų meras, Olimpiniai atletai, kurios sutikote)
Prašome NEkelti:
+ - Asmenukės ar jūsų draugų nuotraukos\n- Nuotraukos, kurias atsisiuntėte iš interneto\n- Patentuotų programėlių nuotraukos
+ Asmenukės ar jūsų draugų nuotraukos
+ Nuotraukos, kurias atsisiuntėte iš interneto
+ Patentuotų programėlių nuotraukos
Pavyzdinis įkėlimas:
Įkelkite savo paveikslėlius. Padėkite Vikipedijos straipsniams būti spalvingesniems!
Paveikslėliai Vikipedijoje yra iš Vikimedija Commons.
diff --git a/app/src/main/res/values-lv/strings.xml b/app/src/main/res/values-lv/strings.xml
index b6c5103e2..5f1d28ce2 100644
--- a/app/src/main/res/values-lv/strings.xml
+++ b/app/src/main/res/values-lv/strings.xml
@@ -16,7 +16,7 @@
Lūdzu, uzgaidiet…
Ieiešana veiksmīga
Pieteikšanās neizdevās.
- Autentifikācija neizdevās!
+ Autentifikācija neizdevās!
Augšupielāde sākās!
%1$s augšupielādēti!
Uzsāk %1$s augšupielādi
diff --git a/app/src/main/res/values-mk/strings.xml b/app/src/main/res/values-mk/strings.xml
index 66a48e0a0..c00a6e97e 100644
--- a/app/src/main/res/values-mk/strings.xml
+++ b/app/src/main/res/values-mk/strings.xml
@@ -21,7 +21,7 @@
Најавата е успешна!
Најавата не успеа!
Не ја пронајдов податотеката. Пробајте со друга.
- Заверката не успеа!
+ Заверката не успеа. Најавете се повторно.
Подигањето започна
Податотеката „%1$s“ е подигната!
Допрете за да го погледате подигањето
@@ -103,7 +103,7 @@
Стандардна лиценца
Користи претходен наслов/опис
Автоматски давај тековна местоположба
- Добивање на тековната местоположба за да се дадат предлози за категории, доколку сликата нема геоознаки
+ Става геоознака од тековната местоположба во слика (ако ја нема). Предупредување: ова ви го разоткрива наоѓалиштето.
Ноќен режим
Користи темен изглед
Наведи извор-Сподели под исти услови 4.0
@@ -272,6 +272,10 @@
Сподели прилог
Не беа укажани координати при изборот на сликата
Грешка при добивањето на околните места.
+ Слика на денот
+ Слика на денот
+ Сликата е успешно додадена кон %1$s на Википодатоците!
+ Не успеав да ја изменам соодветната единица на Википодатоците!
Задај позадина
Позадината е успешно зададена!
diff --git a/app/src/main/res/values-ml/strings.xml b/app/src/main/res/values-ml/strings.xml
index c057fb068..f5ca39042 100644
--- a/app/src/main/res/values-ml/strings.xml
+++ b/app/src/main/res/values-ml/strings.xml
@@ -22,7 +22,7 @@
പ്രവേശനം വിജയകരം!
പ്രവേശനം പരാജയപ്പെട്ടു!
പ്രമാണം കണ്ടെത്താനായില്ല. ദയവായി മറ്റൊരു പ്രമാണം നോക്കുക.
- സാധുതാനിർണ്ണയം പരാജയപ്പെട്ടു!
+ സാധുതാനിർണ്ണയം പരാജയപ്പെട്ടു!
അപ്ലോഡ് തുടങ്ങി!
%1$s അപ്ലോഡ് ചെയ്തിരിക്കുന്നു!
താങ്കളുടെ അപ്ലോഡ് കാണാനായി ടാപ് ചെയ്യുക
diff --git a/app/src/main/res/values-mr/strings.xml b/app/src/main/res/values-mr/strings.xml
index e0a15ad36..b5edf2370 100644
--- a/app/src/main/res/values-mr/strings.xml
+++ b/app/src/main/res/values-mr/strings.xml
@@ -26,7 +26,7 @@
सनोंद प्रवेश यशस्वी!
सनोंद प्रवेश अयशस्वी!
संचिका सापडली नाही. कृपया दुसऱ्या संचिकेसाठी प्रयत्न करा.
- अधिप्रमाणन अयशस्वी!
+ अधिप्रमाणन अयशस्वी!
अपभारण सुरू झाले!
%1$s अपभारीत!
आपले अपभारण बघण्यास अलगद टपली मारा
@@ -105,7 +105,7 @@
डिफॉल्ट परवाना
मागील शीर्षक/वर्णन वापरा
आपोआप सध्याचे स्थान मिळवा
- जर छायाचित्राला जिओटॅग नसल्यास तुम्ही तुमच्या स्थानाची निश्चिती करा जेणे करुन संबंधीत वर्ग सुचवले जातील
+ जर छायाचित्राला जिओटॅग नसल्यास तुम्ही तुमच्या स्थानाची निश्चिती करा जेणे करुन संबंधीत वर्ग सुचवले जातील
रात्रीच्या वेळेच्या व्यवस्था
गडद त्वचा वापरा
Attribution-ShareAlike 3.0
diff --git a/app/src/main/res/values-ms/strings.xml b/app/src/main/res/values-ms/strings.xml
index ef244d049..e58c34daa 100644
--- a/app/src/main/res/values-ms/strings.xml
+++ b/app/src/main/res/values-ms/strings.xml
@@ -14,7 +14,7 @@
Sila tunggu…
Berjaya log masuk!
Gagal log masuk!
- Penentusahan gagal!
+ Penentusahan gagal!
Pemuatnaikan telah bermula!
%1$s telah dimuat naik!
Ketik untuk melihat muatan naik anda
diff --git a/app/src/main/res/values-nb/strings.xml b/app/src/main/res/values-nb/strings.xml
index d2f622fce..0275d1e3c 100644
--- a/app/src/main/res/values-nb/strings.xml
+++ b/app/src/main/res/values-nb/strings.xml
@@ -1,5 +1,6 @@
+ حليو
+ عام
+ پذيرائي
+ جڳهه
العام
+ •
ترتيبون
واپرائيندڙ-نانءُ
ڳجھولفظ
+ وڪي ڪامنز جي آزمائشي کاتي ۾ داخل ٿيو
داخل ٿيو
+ ڳجھو لفظ وساري ويٺا آهيو؟
کاتو کوليو
داخل ٿيندي
براءِ مھرباني انتظار ڪريو…
داخل ٿيڻ ڪامياب!
داخل ٿيڻ ناڪام!
فائيل نہ لڌو. براءِ مھرباني ٻيو ڪو فائيل آزمايو.
- اٿينٽيڪيشن ناڪام!
+ تصديق ناڪام! ٻيهر داخل ٿيو
چاڙھ شروع!
%1$s چڙھي چڪا!
پنھنجو چاڙھ ڏسڻ لاءِ ٺونگو ھڻو
@@ -40,10 +48,13 @@
ونڊيو
جھانگوءَ ۾ ڏسو
عنوان
+ هن فائيل لاءِ ڪا سُرخي ڏيو
تشريح
ناقابلِ داخل ٿيڻ - باھمڄار ناڪامي
+ داخل نه ٿيا آهيو - مهرباني ڪري ڳجهو لفظ ۽ کاتي جو نالو چيڪ ڪيو
ھيڪانديون ناڪام ڪوششون. براءِ مھرباني ڪجھ منٽن کانپوءِ ٻيھر ڪوشش ڪريو.
افسوس، ھي واپرائيندڙ العام تي بندشيل آھي
+ اوهان هر صورت ۾ ٻن عنصرن واري تصديق جو ڪوڊ ڏيو.
داخل ٿيڻ ناڪام
چاڙھيو
ھن سيٽ کي نالو ڏيو
@@ -52,14 +63,16 @@
زمرا ڳوليو
سانڍيو
تازو ڪيو
+ فهرست
+ اوهان جي ڊوائيس ۾ جي پي ايس بند آهي. اوهان کولڻ چاهيو ٿا؟
جي پي ايس چالو ڪيو (اين ايبل جي پي ايس)
اڃان تائين ڪو به ڄاڙهه (اَپلوڊ) نه ٿيو آهي
-
+
- \@string/contributions_subtitle_zero
- %1$d چاڙھ
- %1$d چاڙھَ
-
+
- چاڙھ %1$d شروع ڪندي
- چاڙھَ %d$1 شروع ڪندي
@@ -72,8 +85,9 @@
زمرا
ترتيبون
کاتو کوليو
+ چونڊ تصويرون
بابت
- <a href=\"https://github.com/commons-app/apps-android-commons/wiki/Privacy-policy\">ذاتيات پاليسي</a>
+ <u>ذاتيات پاليسي</u>
بابت
پذيرائي موڪليو (برقٽپال ذريعي)
ڪوبہ برقٽپال ڪلائينٽ تنصيبيل ناھي
@@ -84,10 +98,10 @@
رد
ھن عڪس کي %1$s جي تحت لائسنس ٿيندو
لاھيو
- لائسنس
+ رٿيل لائسنس
گذريل عنوان/تشريح استعمال ڪريو
خوبخود ھاڻوڪي مڪانيت وٺو
- ھاڻوڪي مڪانيت لھو زمرن جون تجويزون پيش ڪرڻ لاءِ جيڪڏھن عڪس تي جيوٽيگ ناھي لڳل
+ ھاڻوڪي مڪانيت ڏئي ٿو جيڪڏھن عڪس جيوٽيگ ناھي ٿيل، ۽ عڪس کي ان سان جيوٽيگ ڪري ٿو. چتاءُ: ھي توھان جي ھاڻوڪي مڪانيت ظاھر ڪندو.
رات جو ڏيک
گھرو نظارو استعمال ڪريو
انتساب-ھجھڙي ڀاڱيداري 4.0
@@ -113,11 +127,17 @@
وڪيپيڊيا تي استعمال ٿيندڙ گھڻن عڪسن جي وڪيميڊيا العام ميزباني ڪري ٿو.
توھان جا عڪس سڄي دنيا جي ماڻھن کي تعليم يافتا ڪرڻ ۾ مدد ڪن ٿا
براءِ مھرباني اھي تصويرون چاڙھيو مڪمل طور تي توھان پاران ڪڍيل يا تخليقيل آھن:
- u2022 قدرتي شيون (گل، جانور، جبل) \nu2022 استعمال جوڳيون شيون (سائيڪلون، ٽرين اسٽيشنون) \nu2022 مشھور شخصيتون (توھان جو ناظم، اولمپڪ رانديگر جنھن سان توھان مليئو)
+ قدرتي شيون (گل، جانور، جبل) \nاستعمال جوڳيون شيون (سائيڪلون، ريل اسٽيشنون) \nمشھور شخصيتون (توھان جو ناظم، اولمپڪ رانديگر جنھن سان توھان مليو)
+ قدرتي شيون (گل، جانور، جبل)
+ استعمال جوڳيون شيون (سائيڪلون، ريل اسٽيشنون)
+ مشھور شخصيتون (توھان جو ناظم، اولمپڪ رانديگر جنھن سان توھان مليو)
براءِ مھرباني نہ چاڙھيو:
u2022 سيلفيون يا پنھنجي دوستن جو تصويرون \nu2022 اھي تصويرون جيڪي توھان انٽرنيٽ تان ڊائونلوڊ ڪيون \nu2022 پروپرائيٽري ايپس جا اسڪرين شاٽ
+ سيلفي يا اوهان جي دوست جي تصوير
+ انٽرنيٽ تان کنيل تصويرون
مثال چاڙھ:
- عنوان: سڊني اوپيرا گھر \n- تشريح: سڊني اوپيرا گھر نھر جي پاسي کان ڏيک \n- زمرا: سڊني اوپيرا گھر، سڊني اوپيرا گھر اولھ کان، سڊني اوپيرا گھر ڏورانھان ڏيک
+ سُرخي: سڊني اوپيرا گھر
پنھنجي عڪسن جي ڀاڱيداري ڪريو. وڪيپيڊيا ڪي مضمونن ۾ زندگي آڻيو!
وڪيپيڊيا تي عڪس وڪيميڊيا العام تان اچن ٿا.
توھان جا عڪس سڄي دنيا ۾ ماڻھن کي تعليم يافتا ڪرڻ ۾ مدد ڪن ٿا.
@@ -180,9 +200,30 @@
پنھنجي کاتي ۾ داخل ٿيو
لاگ فائيل موڪليو
لاگ فائيل سرجڻھارن کي برقٽپال ذريعي موڪليو
+ تصوير مٽائڻ لاءِ نامزد ڪئي وئي آهي.
+ برائوزر ۾ ڏسو
مڪانيت تبديلي ناھي ٿي.
مڪانيت موجود ناھي.
ويجھين جڳھن جي فھرست ڏيکارڻ لاءِ اجازت گھربل آھي
ھدايتون وٺو
مضمون پڙھو
+ وڪيپيڊيا ڪامنز ۾ ڀليڪار، %1$s! اسان کي خوشي آهي ته اوهان هتي آهيو
+ %1$s اوهان جي بحث صفحي تي پيغام ڇڏيو آهي.
+ سنوارڻ لاءِ مهرباني
+ %1$s اوهان جو %2$s تي ذڪر ڪيو آهي.
+ طرف
+ وڪيپيڊيا
+ <u>پذيرائي ڏيو</u>
+ <u>عام سوال</u>
+ سبق کي ڇڏيو
+ انٽرنيٽ ناهي
+ انٽرنيٽ آهي
+ <u>ترجمو</u>
+ ٻوليون
+ رد
+ سمجھي ويس!
+ ڪوبہ عڪس نہ لڌو!
+ ايپ ونڊيو
+ اڄ جي تصوير
+ اڄ جي تصوير
diff --git a/app/src/main/res/values-si/strings.xml b/app/src/main/res/values-si/strings.xml
index cb553b304..6fb06002a 100644
--- a/app/src/main/res/values-si/strings.xml
+++ b/app/src/main/res/values-si/strings.xml
@@ -17,7 +17,7 @@
පිවිසුම සාර්ථකයි!
පිවිසීම අසාර්ථකයි!
ගොනුව හමු නොවිණි. තවත් ගොනුවක් උත්සාහ කරන්න.
- සහතික කිරීම අසාර්ථකයි
+ සහතික කිරීම අසාර්ථකයි
උඩුගතකිරීම ආරම්හවූවා!
%1$s උඩුගතකලා!
ඔබගේ උඩුගතකිරීම පෙන්වන්න තට්ටු කරන්න
diff --git a/app/src/main/res/values-sk/strings.xml b/app/src/main/res/values-sk/strings.xml
index 9ad6857ef..ae91a8d25 100644
--- a/app/src/main/res/values-sk/strings.xml
+++ b/app/src/main/res/values-sk/strings.xml
@@ -23,7 +23,7 @@
Prihlásenie úspešné
Prihlásenie zlyhalo!
Súbor nebol nájdený. Skúste, prosím, iný súbor.
- Overenie zlyhalo!
+ Overenie zlyhalo!
Nahrávanie začalo!
%1$s je nahraný!
Kliknutím zobrazíte váš upload
diff --git a/app/src/main/res/values-skr/strings.xml b/app/src/main/res/values-skr/strings.xml
index 73a5b7e51..7a4dffb41 100644
--- a/app/src/main/res/values-skr/strings.xml
+++ b/app/src/main/res/values-skr/strings.xml
@@ -20,7 +20,7 @@
لاگ ان کامیاب!
لاگ ان ناکام!
فائل کائنی لبھی،ٻئی فائل کیتے کوشش کرو۔
- تصدیق ناکام!
+ تصدیق ناکام!
اپ لوڈ شروع!
%1$s اپ لوڈ تھی ڳیا!
آپݨی اپلوڈ ݙیکھݨ کیتے ٹیپ کرو
diff --git a/app/src/main/res/values-sr/strings.xml b/app/src/main/res/values-sr/strings.xml
index 312cccb62..29a025bd8 100644
--- a/app/src/main/res/values-sr/strings.xml
+++ b/app/src/main/res/values-sr/strings.xml
@@ -29,7 +29,7 @@
Успешно сте пријављени.
Пријављивање није успело.
Датотека није пронађена. Покушајте са другом датотеком.
- Провера идентитета није успела.
+ Провера идентитета није успела.
Отпремање је започето.
Датотека „%1$s“ је отпремљена.
Додирните да бисте видели отпремање
@@ -57,6 +57,7 @@
Унесите наслов за ову датотеку
Опис
Неуспешно пријављивање – грешка на мрежи
+ Не могу да извршим пријаву — проверите своје корисничко име и лозинку
Превише неуспешних покушаја. Пробајте поново за неколико минута.
Нажалост, овај корисник је блокиран на Остави
Морате унети Ваш двофакторски код за аутентификацију.
@@ -68,6 +69,7 @@
Претражи категорије
Сачувај
Освежи
+ Списак
GPS је онемогућен на Вашем уређају. Желите ли га омогућити?
Омогући GPS
Још увек нема отпремања
@@ -89,6 +91,7 @@
Категорије
Подешавања
Отвори налог
+ Изабране слике
О апликацији
Софтвер отвореног кода доступан под лиценцом <a href=\"https://github.com/commons-app/apps-android-commons/blob/master/COPYING\">Apache вер. 2</a> Викимедијина Остава и њен лого су заштитни знаци Викимедијине Фондације и користе се са дозволом Викимедијине Фондацине. Ми не одобравамо или подржавмо Викимедијину Фондацију.\n\nАпликација за Викимедијину оставу је апликација отвореног кода која је направљена и која се одржава помоћу грантова и волонтера Викимедијине заједнице. Задужбина Викимедија није укључена у стварање, развој или одржавање апликације.
Направите нови <a href=\"https://github.com/commons-app/apps-android-commons/issues\">захтев на GitHub-у</a> да бисте пријавили грешке или дали предлоге.
@@ -108,7 +111,7 @@
Подразумевана лиценца
Користи претходан наслов/опис
Аутоматски детектуј тренутну локацију
- Прими тренутну локацију да би предложили категорију ако слика није географски означена
+ Прима тренутну локацију ако слика није геотагована и геотагује исту њоме. Упозорење: Овиме ће да се открије Ваша тренутна локација.
Ноћни режим
Користити тамну тему
Ауторство-Делити под истим условима 4.0
@@ -154,14 +157,15 @@
Избегавајте материјале које сте нашли на интернету, као и слике плаката, корица књига итд.
Јесте ли разумели?
Јесам!
+
Категорије
Учитавање…
Ништа није изабрано
Нема описа
Непозната лиценца
Освежи
- Потребна дозвола: читање спољашње меморије. \nАпликација не може да функционише без овога.
- Потребна дозвола: писање у спољашњој меморији. \nАпликација не може да функционише без овога.
+ Потребна дозвола: Читање спољашње меморије. Апликација не може да приступи Ваших галерији без овога.
+ Потребна дозвола: Писање спољашње меморије. Апликација не може да приступи Вашој камери без овога.
Необавезна дозвола: преузми тренутну локацију за предлоге категорија
У реду
Места у близини
@@ -174,6 +178,8 @@
Наслов медија
Опис
Опис датотеке иде овде. Може да буде поприлично дуг и приказиваће се у више редова. Надамо се да ће изгледати лепо.
+ Аутор
+ Корисничко име аутора изабране слике иде овде.
Датум отпремања
Лиценца
Координате
@@ -216,10 +222,12 @@
Одјави ме
Туторијал
Обавештења
+ Изабрана
Оближња места не могу да се приказују без дозволе за локацију
опис није пронађен
Страница датотеке на Остави
Ставка на Википодацима
+ Википедијски чланак
Грешка при кеширању слика
Јединствен описни наслов за датотеку, који ће бити име датотеке. Можете да користите обични језик са размацима. Не треба уносити екстензију датотеке
Молимо да опишете датотеку колико је то могуће: Где је направљена? Шта приказује? Шта је контекст? Опишите објекте и/или особе. Откријте информације које се не могу лако погодити, на пример доба дана ако је у питању пејзаж. Ако датотека приказује нешто необично, молимо да објасните шта је то чини необичном.
@@ -233,6 +241,10 @@
Пошаљи дневничку датотеку девелоперима преко имејла
Није пронађен веб-претраживач за отварање URL-а
Грешка! URL није пронађен
+ Номиновање за брисање
+ Ова слика је била номинована за брисање
+
+ Види у претраживачу
Локација није промењена.
Локација није доступна.
Потребна је дозвола за приказивање листе локација у близини
@@ -243,7 +255,35 @@
Хвала Вам за прављење измене
%1$s Вас је поменуо на страници %2$s.
Пребаци приказ
+ УПУТСТВА
+ ВИКИПОДАЦИ
+ ВИКИПЕДИЈА
+ ОСТАВА
<u>Оцените нас</u>
- Често постављана питања
+ <u>ЧПП</u>
Прескочи туторијал
+ Интернет недоступан
+ Интернет доступан
+ Грешка при фечовању нотификација
+ Нису пронађене нотификације
+ <u>Превођење</u>
+ Језици
+ Одаберите језик за који бисте желели да правите преводе
+ Настави
+ Откажи
+ Покушај поново
+ Разумем!
+ Ово су места у Вашој близини којима је потребна слика да илуструје њихове википедијске чланке
+ Клик на ово дугме даће Вам списак ових места
+ Можете да отпремите слику за било које место из своје галерије или камере
+ Нису пронађене слике!
+ Десила се грешка при учитавању слика.
+ Отпремио/ла: %1$s
+ Подели апликацију
+ Координате нису биле одређене током селекције слике
+ Грешка при фечовању оближњих места.
+ Слика успешно додата у %1$s на Википодацима!
+ Неуспешно ажурирање одговарајућег ентитета на Википодацима!
+ Постави позадину
+ Позадина успешно постављена!
diff --git a/app/src/main/res/values-su/strings.xml b/app/src/main/res/values-su/strings.xml
index d209cc1ad..7d61a97cb 100644
--- a/app/src/main/res/values-su/strings.xml
+++ b/app/src/main/res/values-su/strings.xml
@@ -16,7 +16,7 @@
Laksana login!
Gagal login!
Berkas teu kapanggih. Coba berkas séjén.
- Oténtikasi gagal!
+ Oténtikasi gagal!
Mitembeyan ngunjal!
%1$s diunjal!
Toél pikeun némpo unjalan anjeun
@@ -94,7 +94,7 @@
Lisénsi
Paké baé judul/pedaran saméméhna
Comot lokasi sacara otomatis
- Catet lokasi ayeuna pikeun nawarkeun usulan kategori lamun gambar tanpa géotag
+ Catet lokasi ayeuna pikeun nawarkeun usulan kategori lamun gambar tanpa géotag
Modus peuting
Gunakeun téma cakueum
Atribusi-BabagiSarupa 4.0
diff --git a/app/src/main/res/values-sv/strings.xml b/app/src/main/res/values-sv/strings.xml
index e57c98b5b..b582761bd 100644
--- a/app/src/main/res/values-sv/strings.xml
+++ b/app/src/main/res/values-sv/strings.xml
@@ -27,7 +27,7 @@
Inloggningen lyckades!
Det gick inte att logga in!
Filen hittades inte. Försök med en annan fil.
- Autentisering misslyckades!
+ Autentisering misslyckades, var god logga in igen!
Överföring påbörjad!
%1$s överförd!
Tryck för att visa din uppladdning
@@ -109,7 +109,7 @@
Standardlicens
Använd föregående titel/beskrivning
Hämta aktuell plats automatiskt
- Hämta aktuell plats för att erbjuda kategoriförslag om bilden inte är geotaggad
+ Hämtar aktuell plats om bilden inte är geotaggad och geotaggar bilden med den. Varning: Detta kommer att avslöja din nuvarande position.
Nattläge
Använd mörkt tema
Erkännande-DelaLika 4.0
@@ -280,6 +280,10 @@
Dela app
Koordinater specificerades inte vid bildvalet
Fel uppstod när platser i närheten hämtades.
+ Dagens bild
+ Dagens bild
+ Bilden lades till i %1$s på Wikidata!
+ Misslyckades att uppdatera motsvarande Wikidataentitet!
Ange som bakgrundsbild
Bakgrundsbilden ändrades!
diff --git a/app/src/main/res/values-tcy/strings.xml b/app/src/main/res/values-tcy/strings.xml
index c6469511e..3b540c75d 100644
--- a/app/src/main/res/values-tcy/strings.xml
+++ b/app/src/main/res/values-tcy/strings.xml
@@ -15,7 +15,7 @@
ಲಾಗಿನ್ ಅಂಡ್!
ಲಾಗಿನ್ ಅಯಿಜಾತ್ತ!
ಈ ಕಡತ ತಿಕ್ಕಿಜಿ. ದಯಮಲ್ತ್ ಕುಡೊಂಜಿ ಕಡತೊನು ಪ್ರಯತ್ನ ಮಲ್ಪುಲೆ.
- ದೃಢೀಕರಣ ಸರಿ ಆಯಿಜಿ!
+ ದೃಢೀಕರಣ ಸರಿ ಆಯಿಜಿ!
ದಿಂಜಪುನಾ ಸುರು ಅಂಡ್!
%1$s ಅಪ್ಲೋಡ್ ಆಂಡ್!
ಇರೆನ ಅಪ್ಲೋಡ್ ತೂಯೆರೆ ಒತ್ತುಲೆ
diff --git a/app/src/main/res/values-te/strings.xml b/app/src/main/res/values-te/strings.xml
index 59c919d35..50f8bd95f 100644
--- a/app/src/main/res/values-te/strings.xml
+++ b/app/src/main/res/values-te/strings.xml
@@ -15,7 +15,7 @@
వేచివుండండి…
లాగిన్ విజయవంతమైంది!
లాగిన్ విఫలమైంది!
- ఆథెంటికేషను విఫలమైంది!
+ ఆథెంటికేషను విఫలమైంది!
ఎక్కింపు మొదలైంది!
%1$s ను ఎక్కించాం!
మీ ఎక్కింపును చూసేందుకు నొక్కండి
diff --git a/app/src/main/res/values-th/strings.xml b/app/src/main/res/values-th/strings.xml
index 933f0c2d4..4b00eb215 100644
--- a/app/src/main/res/values-th/strings.xml
+++ b/app/src/main/res/values-th/strings.xml
@@ -23,7 +23,7 @@
การเข้าสู่ระบบสำเร็จแล้ว!
การเข้าสู่ระบบล้มเหลว!
ไม่พบไฟล์ กรุณาลองใช้ไฟล์อื่น
- การตรวจสอบความถูกต้องล้มเหลว!
+ การตรวจสอบความถูกต้องล้มเหลว!
เริ่มการอัปโหลดแล้ว!
อัปโหลด %1$s แล้ว!
แตะเพื่อดูการอัปโหลดของคุณ
@@ -96,7 +96,7 @@
สัญญาอนุญาตปริยาย
ใช้ชื่อเรื่อง/คำอธิบายก่อนหน้านี้
รับข้อมูลตำแหน่งที่ตั้งปัจจุบันโดยอัตโนมัติ
- ดึงข้อมูลตำแหน่งที่ตั้งปัจจุบันเพื่อรับข้อเสนอแนะเกี่ยวกับหมวดหมู่ถ้ารูปภาพไม่ได้ติดแท็กตำแหน่งที่ตั้งเอาไว้
+ ดึงข้อมูลตำแหน่งที่ตั้งปัจจุบันเพื่อรับข้อเสนอแนะเกี่ยวกับหมวดหมู่ถ้ารูปภาพไม่ได้ติดแท็กตำแหน่งที่ตั้งเอาไว้
โหมดกลางคืน
ใช้ธีมสีเข้ม
Attribution-ShareAlike 4.0
diff --git a/app/src/main/res/values-tr/strings.xml b/app/src/main/res/values-tr/strings.xml
index c81a11055..d04974f5a 100644
--- a/app/src/main/res/values-tr/strings.xml
+++ b/app/src/main/res/values-tr/strings.xml
@@ -6,6 +6,7 @@
* Incelemeelemani
* McAang
* Neslihan Turan
+* Rapsar
* Sayginer
* Trockya
* VikipediBilgini
@@ -29,7 +30,7 @@
Oturum açma başarılı!
Oturum açma başarısız oldu!
Dosya bulunamadı. Lütfen başka bir dosya deneyin.
- Kimlik doğrulama başarısız oldu!
+ Kimlik doğrulama başarısız oldu, lütfen tekrar giriş yapın
Yükleme başladı!
%1$s yüklendi!
Yüklemelerinizi görüntülemek için dokunun
@@ -111,7 +112,7 @@
Varsayılan lisans
Önceki başlığı/açıklamayı kullan
Otomatik olarak mevcut konumu al
- Resim koordinat olarak etiketlendirilmemişse kategori önerileri için mevcut konum bulun
+ Resim coğrafi etiketli değilse ve coğrafi etiketler resimle görüntüleniyorsa geçerli konumu alır. Uyarı: Mevcut konumunuzu gösterir.
Gece modu
Koyu temayı kullanın
Attribution-ShareAlike 4.0
@@ -241,7 +242,7 @@
Kayıt dosyasını, e-posta aracılığıyla geliştiricilere gönderin
URL\'yi açabilecek bir tarayıcı bulunamadı
Hata! URL bulunamadı
- Silinmesi için aday göster
+ Silinmeye aday göster
Bu görsel silinmesi için aday gösterildi.
Tarayıcıda görüntüle
Konum değiştirilmedi
@@ -281,6 +282,10 @@
Uygulamayı Paylaş
Koordinatlar görüntü seçimi sırasında belirlenmedi
Yakındaki yerler alınırken hata oluştu.
+ Günün Resmi
+ Günün Resmi
+ Resim, Vikiveri\'de %1$s içine başarıyla eklendi.
+ Karşılık gelen Vikiveri varlığı güncellenemedi!
Duvar kağıdı ayarla
Duvar kağıdı başarıyla ayarlandı!
diff --git a/app/src/main/res/values-ug/strings.xml b/app/src/main/res/values-ug/strings.xml
index 4c55cb548..40342d1d1 100644
--- a/app/src/main/res/values-ug/strings.xml
+++ b/app/src/main/res/values-ug/strings.xml
@@ -16,7 +16,7 @@
تىزىمغا كىرىش مۇۋەپپەقىيەتلىك!
تىزىمغا كىرەلمىدى!
ھۆججەت تېپىلمىدى . سىناپ بېقىڭ ، باشقا ھۆججەتلەر .
- سالاھىيەتنى ئىسپاتىنى تەكشۈرۈش مەغلۇپ بولدى !
+ سالاھىيەتنى ئىسپاتىنى تەكشۈرۈش مەغلۇپ بولدى !
يۈكلەش باشلاندى!
%1$s يۈكلەندى!
چېكىپ كۆرۈپ بېقىڭ ، سىزنىڭ يۇقىرىغا يوللاش
diff --git a/app/src/main/res/values-uk/strings.xml b/app/src/main/res/values-uk/strings.xml
index 4b0286605..8bace5fb9 100644
--- a/app/src/main/res/values-uk/strings.xml
+++ b/app/src/main/res/values-uk/strings.xml
@@ -28,7 +28,7 @@
Ви успішно увійшли!
Не вдалося увійти
Файл не знайдено. Будь ласка, спробуйте інший файл.
- Помилка автентифікації
+ Помилка автентифікації. Будь ласка, увійдіть у свій обліковий запис знову
Завантаження розпочато!
Завантажено %1$s!
Торкніться, щоб переглянути Ваше завантаження
@@ -118,7 +118,7 @@
Стандартна ліцензія
Використати попередню назву/опис
Автоматично отримати поточне розташування
- Отримати поточне розташування, щоб з\'явилися підказки категорій, якщо зображення не має геотегів
+ Якщо зображення не містить координат, то буде отримано і поставлено ваше поточне розташування. Будьте уважні, якщо ви не хочете розкривати ваше розташування.
Нічний режим
Використати темну тему
Attribution-ShareAlike 4.0
@@ -289,6 +289,10 @@
Поділитися програмою
Під час вибору зображення не були вказані координати
Помилка отримання місць поблизу.
+ Зображення дня
+ Зображення дня
+ Зображення успішно додано до сторінки %1$s у Вікіданих!
+ Не вдалось оновити відповідну сторінку Вікіданих!
Поставити шпалерами екрану
Шпалери екрану виставлено успішно!
diff --git a/app/src/main/res/values-ur/strings.xml b/app/src/main/res/values-ur/strings.xml
index 2620412d6..cc4246e94 100644
--- a/app/src/main/res/values-ur/strings.xml
+++ b/app/src/main/res/values-ur/strings.xml
@@ -18,7 +18,7 @@
لاگ ان کامیاب۔
داخل ہونے میں ناکامی ہوئی!
فائل نہیں ملی، براہ کرم دوسری فائل آزمائیں۔
- تصدیق ناکام!
+ تصدیق ناکام!
اپلوڈ شروع!
%1$s اپلوڈ شد!
اپنی اپلوڈ دیکھنے کے لیے ٹیپ کریں۔
@@ -97,7 +97,7 @@
اجازت نامہ
گزشتہ عنوان/وضاحت استعمال کریں
خودکارانہ طریقے سے حالیہ جگہ حاصل کریں
- اگر تصویر جغرافیائی نہیں ہے تو قسم کے تجاویز پیش کرنے کیلئے موجودہ مقام کو دوبارہ حاصل کریں
+ اگر تصویر جغرافیائی نہیں ہے تو قسم کے تجاویز پیش کرنے کیلئے موجودہ مقام کو دوبارہ حاصل کریں
نائٹ موڈ
کالا تھیم استعمال کریں
انتباہ-شراکت 4.0
diff --git a/app/src/main/res/values-v14/dimens.xml b/app/src/main/res/values-v14/dimens.xml
new file mode 100644
index 000000000..4db8c5906
--- /dev/null
+++ b/app/src/main/res/values-v14/dimens.xml
@@ -0,0 +1,10 @@
+
+
+
+
+ 0dp
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values-vi/strings.xml b/app/src/main/res/values-vi/strings.xml
index 6e32bfdde..a45a6d7c1 100644
--- a/app/src/main/res/values-vi/strings.xml
+++ b/app/src/main/res/values-vi/strings.xml
@@ -25,7 +25,7 @@
Đã đăng nhập thành công!
Đăng nhập thất bại!
Không tìm thấy tập tin. Xin vui lòng thử tập tin khác.
- Xác thực thất bại!
+ Xác thực thất bại!
Đã bắt đầu tải lên!
Đã tải lên %1$s!
Chạm để xem những tập tin tải lên của bạn
@@ -104,7 +104,7 @@
Giấy phép mặc định
Sử dụng tiêu đề/miêu tả trước
Lấy vị trí hiện tại tự động
- Định vị hiện tại để nhận gợi ý thể loại trong trường hợp hình ảnh chưa được gắn thẻ địa lý
+ Định vị hiện tại để nhận gợi ý thể loại trong trường hợp hình ảnh chưa được gắn thẻ địa lý
Chế độ ban đêm
Dùng chủ đề tối
Ghi công–Chia sẻ tương tự 4.0
diff --git a/app/src/main/res/values-xmf/strings.xml b/app/src/main/res/values-xmf/strings.xml
index d9c0b8f9d..3ab830fb9 100644
--- a/app/src/main/res/values-xmf/strings.xml
+++ b/app/src/main/res/values-xmf/strings.xml
@@ -14,7 +14,7 @@
სისტემაშა მიშულაქ წჷმოძინელო გეთუ!
სისტემაშა მიშულაქ ვემიხუჯინუ!
ფაილქ ვეგორუ. ქორთხინთ, ქოცადით შხვა ფაილი.
- აუთენტიფიკაციაქ ვემიხუჯინუ!
+ აუთენტიფიკაციაქ ვემიხუჯინუ!
ეხარგუაქ ქჷდიჭყუ!
%1$ ეხარგილი რე!
ქეგუწკანტეთ თქვანი ეხარგუაშ ოძირაფალო
diff --git a/app/src/main/res/values-zh-rTW/strings.xml b/app/src/main/res/values-zh-rTW/strings.xml
index bd1242e12..d2871c3b7 100644
--- a/app/src/main/res/values-zh-rTW/strings.xml
+++ b/app/src/main/res/values-zh-rTW/strings.xml
@@ -30,7 +30,7 @@
登入成功!
登入失敗!
找不到檔案。請試試看其它檔案。
- 未能核對身分!
+ 身份驗證失敗,請重新登入
開始上傳!
已上傳%1$s!
輕觸來檢視您上傳的項目
@@ -112,7 +112,7 @@
預設授權條款
使用先前標題、說明
自動獲取目前位置
- 若圖片未有地理標記,就以目前位置來作為分類建議。
+ 如果圖片沒有地理標記,就索取目前位置的地理資訊來標記在圖片上。注意:這會透露出您目前所在的位置。
夜間模式
使用暗黑佈景主題
姓名標示-相同方式分享4.0
@@ -156,8 +156,8 @@
維基百科的圖片,來自維基共享資源。
您的圖片可以幫助教育世界各地的人。
避免使用受版權保護的材料,例如從網際網路找來的圖片、海報、書籍封面等
- 明白了嗎?
- 是!
+ 以上您明白了嗎?
+ 是的!
此提示為空,可能無效。請見錯誤報告: https://github.com/commons-app/apps-android-commons/issues/1333 。
分類
載入中…
@@ -283,6 +283,10 @@
分享應用程式
當選擇圖片時未指定座標
索取附近地點時出錯。
+ 每日圖片
+ 每日圖片
+ 圖片已成功添加到維基數據上的 %1$s!
+ 更新所對應的維基數據項目失敗!
設定桌布
桌布設定成功!
diff --git a/app/src/main/res/values-zh/strings.xml b/app/src/main/res/values-zh/strings.xml
index 445e0f82d..090ddd1dd 100644
--- a/app/src/main/res/values-zh/strings.xml
+++ b/app/src/main/res/values-zh/strings.xml
@@ -1,6 +1,7 @@
外观
- 一般
+ 常规
反馈
位置
共享资源
@@ -27,7 +28,7 @@
登录成功!
登录失败!
找不到文件。请尝试其他文件。
- 身份验证失败!
+ 身份验证失败,请重新登录
上传开始!
%1$s已上传!
点击查看您的上传
@@ -109,7 +110,7 @@
默认许可协议
使用之前的标题/描述
自动获取当前位置
- 如果图片没有地理标记的话,就取得当前位置以提供分类建议
+ 如果图片没有地理标记,以及地理标签图片的话,就取得当前位置。警告:这将暴露您的当前位置。
夜间模式
使用黑暗主题
署名-相同方式共享4.0
@@ -278,6 +279,10 @@
分享应用
图片选择时,坐标并未指定
检索附近地点时出错。
+ 每日图片
+ 每日图片
+ 图片已成功添加到维基数据上的%1$s!
+ 更新对应维基数据实体失败!
设置墙纸
墙纸已成功设置!
diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml
index ef5000d60..1db51fd8b 100644
--- a/app/src/main/res/values/colors.xml
+++ b/app/src/main/res/values/colors.xml
@@ -52,4 +52,6 @@
#424242
#757575
+ #FFFFFF
+ #000000
diff --git a/app/src/main/res/values/dimens.xml b/app/src/main/res/values/dimens.xml
index 7eaf97880..1697853e8 100644
--- a/app/src/main/res/values/dimens.xml
+++ b/app/src/main/res/values/dimens.xml
@@ -25,4 +25,10 @@
14sp
15dp
25dp
+
+
+ 8dp
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index eb040f458..39ea1c416 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -18,7 +18,7 @@
Login success!
Login failed!
File not found. Please try another file.
- Authentication failed!
+ Authentication failed, please login again
Upload started!
%1$s uploaded!
Tap to view your upload
@@ -102,7 +102,7 @@
Default License
Use previous title/description
Automatically get current location
- Retrieve current location to offer category suggestions if image is not geotagged
+ Retrieves current location if image is not geotagged, and geotags image with it. Warning: This will reveal your current location.
Night mode
Use dark theme
Attribution-ShareAlike 4.0
@@ -241,49 +241,53 @@
See webpage for details
View in Browser
- Location has not changed.
- Location not available.
- Permission required to display a list of nearby places
- GET DIRECTIONS
- READ ARTICLE
+ Location has not changed.
+ Location not available.
+ Permission required to display a list of nearby places
+ GET DIRECTIONS
+ READ ARTICLE
- Welcome to Wikimedia Commons, %1$s! We\'re glad you\'re here.
- %1$s left a message on your talk page
- Thank you for making an edit
- %1$s mentioned you on %2$s.
- Toggle view
- DIRECTIONS
- WIKIDATA
- WIKIPEDIA
- COMMONS
- Rate us]]>
- FAQ]]>
- Skip Tutorial
- Internet unavailable
- Internet available
- Error fetching notifications
- No notifications found
- Translate]]>
- Languages
- Select the language that you would like to submit translations for
- Proceed
- Cancel
- Retry
+ Welcome to Wikimedia Commons, %1$s! We\'re glad you\'re here.
+ %1$s left a message on your talk page
+ Thank you for making an edit
+ %1$s mentioned you on %2$s.
+ Toggle view
+ DIRECTIONS
+ WIKIDATA
+ WIKIPEDIA
+ COMMONS
+ Rate us]]>
+ FAQ]]>
+ Skip Tutorial
+ Internet unavailable
+ Internet available
+ Error fetching notifications
+ No notifications found
+ Translate]]>
+ Languages
+ Select the language that you would like to submit translations for
+ Proceed
+ Cancel
+ Retry
- Got it!
- These are the places near you that need pictures to illustrate their Wikipedia articles
- Tapping this button brings up a list of these places
- You can upload a picture for any place from your gallery or camera
+ Got it!
+ These are the places near you that need pictures to illustrate their Wikipedia articles
+ Tapping this button brings up a list of these places
+ You can upload a picture for any place from your gallery or camera
- No images found!
- Error occurred while loading images.
- Uploaded by: %1$s
+ No images found!
+ Error occurred while loading images.
+ Uploaded by: %1$s
- Share App
- Coordinates were not specified during image selection
- Error fetching nearby places.
- You are blocked from editing Commons
+ You are blocked from editing Commons
+ Share App
+ Coordinates were not specified during image selection
+ Error fetching nearby places.
+ Pic of the Day
+ Pic of the Day
+ Image successfully added to %1$s on Wikidata!
+ Failed to update corresponding Wikidata entity!
Set wallpaper
Wallpaper set successfully!
diff --git a/app/src/main/res/xml/pic_of_day_app_widget_info.xml b/app/src/main/res/xml/pic_of_day_app_widget_info.xml
new file mode 100644
index 000000000..5b370e9a1
--- /dev/null
+++ b/app/src/main/res/xml/pic_of_day_app_widget_info.xml
@@ -0,0 +1,11 @@
+
+
\ No newline at end of file
diff --git a/app/src/main/res/xml/provider_paths.xml b/app/src/main/res/xml/provider_paths.xml
index 696723b68..c0669d474 100644
--- a/app/src/main/res/xml/provider_paths.xml
+++ b/app/src/main/res/xml/provider_paths.xml
@@ -1,5 +1,5 @@
-
+
\ No newline at end of file
diff --git a/app/src/test/kotlin/fr/free/nrw/commons/TestCommonsApplication.kt b/app/src/test/kotlin/fr/free/nrw/commons/TestCommonsApplication.kt
index 090cf39b5..84f6d5f10 100644
--- a/app/src/test/kotlin/fr/free/nrw/commons/TestCommonsApplication.kt
+++ b/app/src/test/kotlin/fr/free/nrw/commons/TestCommonsApplication.kt
@@ -4,6 +4,7 @@ import android.content.ContentProviderClient
import android.content.Context
import android.content.SharedPreferences
import android.support.v4.util.LruCache
+import com.google.gson.Gson
import com.nhaarman.mockito_kotlin.mock
import com.squareup.leakcanary.RefWatcher
import fr.free.nrw.commons.auth.AccountUtil
@@ -37,6 +38,7 @@ class MockCommonsApplicationModule(appContext: Context) : CommonsApplicationModu
val accountUtil: AccountUtil = mock()
val appSharedPreferences: SharedPreferences = mock()
val defaultSharedPreferences: SharedPreferences = mock()
+ val categorySharedPreferences: SharedPreferences = mock()
val otherSharedPreferences: SharedPreferences = mock()
val uploadController: UploadController = mock()
val mockSessionManager: SessionManager = mock()
@@ -44,6 +46,7 @@ class MockCommonsApplicationModule(appContext: Context) : CommonsApplicationModu
val mockDbOpenHelper: DBOpenHelper = mock()
val nearbyPlaces: NearbyPlaces = mock()
val lruCache: LruCache = mock()
+ val gson: Gson = Gson()
val categoryClient: ContentProviderClient = mock()
val contributionClient: ContentProviderClient = mock()
val modificationClient: ContentProviderClient = mock()
diff --git a/app/src/test/kotlin/fr/free/nrw/commons/mwapi/ApacheHttpClientMediaWikiApiTest.kt b/app/src/test/kotlin/fr/free/nrw/commons/mwapi/ApacheHttpClientMediaWikiApiTest.kt
index 2d624ced8..fd6e93fab 100644
--- a/app/src/test/kotlin/fr/free/nrw/commons/mwapi/ApacheHttpClientMediaWikiApiTest.kt
+++ b/app/src/test/kotlin/fr/free/nrw/commons/mwapi/ApacheHttpClientMediaWikiApiTest.kt
@@ -27,15 +27,17 @@ class ApacheHttpClientMediaWikiApiTest {
private lateinit var testObject: ApacheHttpClientMediaWikiApi
private lateinit var server: MockWebServer
+ private lateinit var wikidataServer: MockWebServer
private lateinit var sharedPreferences: SharedPreferences
private lateinit var categoryPreferences: SharedPreferences
@Before
fun setUp() {
server = MockWebServer()
+ wikidataServer = MockWebServer()
sharedPreferences = PreferenceManager.getDefaultSharedPreferences(RuntimeEnvironment.application)
categoryPreferences = PreferenceManager.getDefaultSharedPreferences(RuntimeEnvironment.application)
- testObject = ApacheHttpClientMediaWikiApi(RuntimeEnvironment.application, "http://" + server.hostName + ":" + server.port + "/", sharedPreferences, categoryPreferences, Gson())
+ testObject = ApacheHttpClientMediaWikiApi(RuntimeEnvironment.application, "http://" + server.hostName + ":" + server.port + "/", "http://" + wikidataServer.hostName + ":" + wikidataServer.port + "/", sharedPreferences, categoryPreferences, Gson())
testObject.setWikiMediaToolforgeUrl("http://" + server.hostName + ":" + server.port + "/")
}
diff --git a/script/style/checkstyle.xml b/script/style/checkstyle.xml
index cb0b13dca..216d1bce2 100644
--- a/script/style/checkstyle.xml
+++ b/script/style/checkstyle.xml
@@ -7,6 +7,8 @@
Modified from https://github.com/checkstyle/checkstyle/blob/master/src/main/resources/google_checks.xml.
Modifications are:
- doubled each value in Indentation
+ - exceptions for Butter Knife and Espresso
+ - larger (max)LineLength
Checkstyle configuration that checks the Google coding conventions from Google Java Style
that can be found at https://google.github.io/styleguide/javaguide.html.
@@ -44,7 +46,7 @@
-
+
@@ -56,7 +58,7 @@
-
+