Merge branch 'master' into dependency-injection

This commit is contained in:
Paul Hawke 2017-11-24 22:12:43 -06:00
commit 02b5b9b680
148 changed files with 1169 additions and 364 deletions

View file

@ -17,14 +17,17 @@ jdk:
android:
components:
- platform-tools
- tools
- build-tools-26.0.1
- platform-tools
- build-tools-26.0.2
- extra-google-m2repository
- extra-android-m2repository
- ${ANDROID_TARGET}
- android-25
- android-26
- sys-img-${ANDROID_ABI}-${ANDROID_TARGET}
licenses:
- 'android-sdk-license-.+'
before_script:
- echo no | android create avd --force -n test -t $ANDROID_TARGET --abi $ANDROID_ABI
@ -38,8 +41,10 @@ after_success:
- bash <(curl -s https://codecov.io/bash)
after_failure:
- echo '*** Connected Test Rsults ***'
- w3m -dump ${TRAVIS_BUILD_DIR}/app/build/reports/androidTests/connected/*Test.html
- echo '*** Debug Unit Test Results ***'
- w3m -dump ${TRAVIS_BUILD_DIR}/app/build/reports/tests/*/classes/*Test.html
- echo '*** Connected Test Results ***'
- w3m -dump ${TRAVIS_BUILD_DIR}/app/build/reports/androidTests/connected/flavors/*/*Test.html
before_cache:
- rm -f $HOME/.gradle/caches/modules-2/modules-2.lock

View file

@ -29,6 +29,7 @@ their contribution to the product.
* Jan Piotrowski
* Bruke Mekuria Mulugeta
* Paul Hawke
* Vishan Seru
3rd party open source libraries used:
* Butterknife

View file

@ -2,9 +2,7 @@
The Wikimedia Commons Android app allows users to upload pictures from their Android phone/tablet to Wikimedia Commons. Download the app [here][1], or view our [website][2].
Initially started by the Wikimedia Foundation, this app is now maintained by volunteers. Anyone is welcome to improve it, just choose among the [open issues][3] and send us a pull request :-)
We are currently applying for an [IEG renewal][10] to work on the app for the next 6 months. Feedback is very much welcomed.
Initially started by the Wikimedia Foundation, this app is now maintained by grantees and volunteers of the Wikimedia community. Anyone is welcome to improve it, just choose among the [open issues][3] and send us a pull request :-)
<a href="https://f-droid.org/repository/browse/?fdid=fr.free.nrw.commons" target="_blank">
<img src="https://f-droid.org/badge/get-it-on.png" alt="Get it on F-Droid" height="90"/></a>

View file

@ -1,65 +1,75 @@
apply from: '../gitutils.gradle'
apply plugin: 'com.android.application'
apply plugin: 'me.tatarka.retrolambda'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-kapt'
apply plugin: 'jacoco-android'
apply from: 'quality.gradle'
apply plugin: 'com.getkeepsafe.dexcount'
dependencies {
compile 'com.github.nicolas-raoul:Quadtree:ac16ea8035bf07'
compile 'fr.avianey.com.viewpagerindicator:library:2.4.1.1@aar'
compile 'in.yuvi:http.fluent:1.3'
compile 'com.android.volley:volley:1.0.0'
compile 'ch.acra:acra:4.7.0'
compile 'org.mediawiki:api:1.3'
compile 'commons-codec:commons-codec:1.10'
compile 'com.github.pedrovgs:renderers:3.3.3'
compile 'com.google.code.gson:gson:2.8.1'
compile 'com.jakewharton.timber:timber:4.5.1'
compile 'info.debatty:java-string-similarity:0.24'
compile ('com.mapbox.mapboxsdk:mapbox-android-sdk:5.1.0@aar'){
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'
implementation 'com.android.volley:volley:1.0.0'
implementation 'ch.acra:acra:4.7.0'
implementation 'org.mediawiki:api:1.3'
implementation 'commons-codec:commons-codec:1.10'
implementation 'com.github.pedrovgs:renderers:3.3.3'
implementation 'com.google.code.gson:gson:2.8.1'
implementation 'com.jakewharton.timber:timber:4.5.1'
implementation 'info.debatty:java-string-similarity:0.24'
implementation ('com.mapbox.mapboxsdk:mapbox-android-sdk:5.1.0@aar'){
transitive=true
}
compile "com.android.support:support-v4:${project.supportLibVersion}"
compile "com.android.support:appcompat-v7:${project.supportLibVersion}"
compile "com.android.support:design:${project.supportLibVersion}"
compile "com.android.support:cardview-v7:${project.supportLibVersion}"
implementation "com.android.support:support-v4:${project.supportLibVersion}"
implementation "com.android.support:appcompat-v7:${project.supportLibVersion}"
implementation "com.android.support:design:${project.supportLibVersion}"
compile "com.jakewharton:butterknife:$BUTTERKNIFE_VERSION"
annotationProcessor "com.jakewharton:butterknife-compiler:$BUTTERKNIFE_VERSION"
implementation "com.android.support:cardview-v7:${project.supportLibVersion}"
compile 'com.squareup.okhttp3:okhttp:3.8.1'
compile 'com.squareup.okio:okio:1.13.0'
implementation "com.jakewharton:butterknife:$BUTTERKNIFE_VERSION"
kapt "com.jakewharton:butterknife-compiler:$BUTTERKNIFE_VERSION"
compile 'io.reactivex.rxjava2:rxandroid:2.0.1'
implementation 'com.squareup.okhttp3:okhttp:3.8.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.
compile 'io.reactivex.rxjava2:rxjava:2.1.2'
compile 'com.jakewharton.rxbinding2:rxbinding:2.0.0'
compile 'com.jakewharton.rxbinding2:rxbinding-support-v4:2.0.0'
compile 'com.jakewharton.rxbinding2:rxbinding-appcompat-v7:2.0.0'
compile 'com.jakewharton.rxbinding2:rxbinding-design:2.0.0'
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'
compile 'com.facebook.fresco:fresco:1.3.0'
compile 'com.facebook.stetho:stetho:1.5.0'
implementation 'com.facebook.fresco:fresco:1.3.0'
implementation 'com.facebook.stetho:stetho:1.5.0'
testCompile 'junit:junit:4.12'
testCompile 'org.robolectric:robolectric:3.4'
implementation "com.google.dagger:dagger:$DAGGER_VERSION"
implementation "com.google.dagger:dagger-android-support:$DAGGER_VERSION"
testCompile 'com.squareup.okhttp3:mockwebserver:3.8.1'
androidTestCompile 'com.squareup.okhttp3:mockwebserver:3.8.1'
androidTestCompile "com.android.support:support-annotations:${project.supportLibVersion}"
androidTestCompile ('com.android.support.test.espresso:espresso-core:3.0.1'){
exclude group: 'com.google.code.findbugs'
}
kapt "com.google.dagger:dagger-android-processor:$DAGGER_VERSION"
kapt "com.google.dagger:dagger-compiler:$DAGGER_VERSION"
debugCompile 'com.squareup.leakcanary:leakcanary-android:1.5.1'
releaseCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.5.1'
testCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.5.1'
testImplementation "org.jetbrains.kotlin:kotlin-stdlib-jre7:$kotlin_version"
androidTestImplementation "org.jetbrains.kotlin:kotlin-stdlib-jre7:$kotlin_version"
compile 'com.google.dagger:dagger:2.11'
compile 'com.google.dagger:dagger-android-support:2.11'
testImplementation 'junit:junit:4.12'
testImplementation 'org.robolectric:robolectric:3.4'
testImplementation 'com.squareup.okhttp3:mockwebserver:3.8.1'
androidTestImplementation 'com.squareup.okhttp3:mockwebserver:3.8.1'
androidTestImplementation "com.android.support:support-annotations:${project.supportLibVersion}"
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.1'
debugImplementation 'com.squareup.leakcanary:leakcanary-android:1.5.1'
releaseImplementation 'com.squareup.leakcanary:leakcanary-android-no-op:1.5.1'
testImplementation 'com.squareup.leakcanary:leakcanary-android-no-op:1.5.1'
implementation 'com.google.dagger:dagger:2.11'
implementation 'com.google.dagger:dagger-android-support:2.11'
annotationProcessor 'com.google.dagger:dagger-compiler:2.11'
annotationProcessor 'com.google.dagger:dagger-android-processor:2.11'
}
@ -74,12 +84,17 @@ android {
applicationId 'fr.free.nrw.commons'
versionCode 74
versionName '2.5.0'
setProperty("archivesBaseName", "app-commons-v$versionName-" + getBranchName())
minSdkVersion project.minSdkVersion
targetSdkVersion project.targetSdkVersion
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
vectorDrawables.useSupportLibrary = true
}
sourceSets {
test.java.srcDirs += 'src/test/kotlin'
}
buildTypes {
release {
minifyEnabled false // See https://stackoverflow.com/questions/40232404/google-play-apk-and-android-studio-apk-usb-debug-behaving-differently - proguard.cfg modification alone insufficient.
@ -87,9 +102,11 @@ android {
}
debug {
testCoverageEnabled true
versionNameSuffix "-debug-" + getBranchName() + "~" + getBuildVersion()
}
}
flavorDimensions 'tier'
productFlavors {
prod {
buildConfigField "String", "WIKIMEDIA_API_HOST", "\"https://commons.wikimedia.org/w/api.php\""
@ -101,6 +118,7 @@ android {
buildConfigField "String", "EVENTLOG_WIKI", "\"commonswiki\""
buildConfigField "String", "SIGNUP_LANDING_URL", "\"https://commons.m.wikimedia.org/w/index.php?title=Special:CreateAccount&returnto=Main+Page&returntoquery=welcome%3Dyes\""
buildConfigField "String", "SIGNUP_SUCCESS_REDIRECTION_URL", "\"https://commons.m.wikimedia.org/w/index.php?title=Main_Page&welcome=yes\""
dimension 'tier'
}
beta {
@ -114,6 +132,7 @@ android {
buildConfigField "String", "EVENTLOG_WIKI", "\"commonswiki\""
buildConfigField "String", "SIGNUP_LANDING_URL", "\"https://commons.m.wikimedia.beta.wmflabs.org/w/index.php?title=Special:CreateAccount&returnto=Main+Page&returntoquery=welcome%3Dyes\""
buildConfigField "String", "SIGNUP_SUCCESS_REDIRECTION_URL", "\"https://commons.m.wikimedia.beta.wmflabs.org/w/index.php?title=Main_Page&welcome=yes\""
dimension 'tier'
}
}
@ -132,4 +151,5 @@ android {
configurations.all {
resolutionStrategy.force 'com.android.support:support-annotations:25.2.0'
}
buildToolsVersion buildToolsVersion
}

View file

@ -14,6 +14,7 @@
<uses-permission android:name="android.permission.MANAGE_ACCOUNTS" />
<uses-permission android:name="android.permission.MANAGE_DOCUMENTS" />
<uses-permission android:name="com.google.android.apps.photos.permission.GOOGLE_PHOTOS" />
<uses-permission android:name="android.permission.READ_LOGS"/>
<application
android:name=".CommonsApplication"

View file

@ -1,7 +1,5 @@
package fr.free.nrw.commons;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.widget.TextView;
@ -27,9 +25,4 @@ public class AboutActivity extends NavigationBaseActivity {
versionText.setText(BuildConfig.VERSION_NAME);
initDrawer();
}
public static void startYourself(Context context) {
Intent settingsIntent = new Intent(context, AboutActivity.class);
context.startActivity(settingsIntent);
}
}

View file

@ -7,6 +7,7 @@ import android.database.sqlite.SQLiteDatabase;
import com.facebook.drawee.backends.pipeline.Fresco;
import com.facebook.stetho.Stetho;
import com.squareup.leakcanary.LeakCanary;
import com.squareup.leakcanary.RefWatcher;
import org.acra.ACRA;
import org.acra.ReportingInteractionMode;
@ -60,17 +61,15 @@ public class CommonsApplication extends DaggerApplication {
public static final String FEEDBACK_EMAIL_SUBJECT = "Commons Android App (%s) Feedback";
private CommonsApplicationComponent component;
private RefWatcher refWatcher;
@Override
public void onCreate() {
super.onCreate();
if (LeakCanary.isInAnalyzerProcess(this)) {
// This process is dedicated to LeakCanary for heap analysis.
// You should not init your app in this process.
if (setupLeakCanary() == RefWatcher.DISABLED) {
return;
}
LeakCanary.install(this);
Timber.plant(new Timber.DebugTree());
@ -86,6 +85,18 @@ public class CommonsApplication extends DaggerApplication {
Fresco.initialize(this);
}
protected RefWatcher setupLeakCanary() {
if (LeakCanary.isInAnalyzerProcess(this)) {
return RefWatcher.DISABLED;
}
return LeakCanary.install(this);
}
public static RefWatcher getRefWatcher(Context context) {
CommonsApplication application = (CommonsApplication) context.getApplicationContext();
return application.refWatcher;
}
@Override
protected AndroidInjector<? extends DaggerApplication> applicationInjector() {
return injector();

View file

@ -120,8 +120,9 @@ public class Media implements Parcelable {
return localUri;
}
@Nullable
public String getImageUrl() {
if (imageUrl == null) {
if (imageUrl == null && this.getFilename() != null) {
imageUrl = Utils.makeThumbBaseUrl(this.getFilename());
}
return imageUrl;

View file

@ -11,7 +11,6 @@ import org.xml.sax.SAXException;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.regex.Matcher;
@ -39,7 +38,6 @@ public class MediaDataExtractor {
private String filename;
private ArrayList<String> categories;
private Map<String, String> descriptions;
private Date date;
private String license;
private @Nullable LatLng coordinates;
private LicenseList licenseList;
@ -155,7 +153,7 @@ public class MediaDataExtractor {
}
private Node findTemplate(Element parentNode, String title_) throws IOException {
String title= new PageTitle(title_).getDisplayText();
String title = new PageTitle(title_).getDisplayText();
NodeList nodes = parentNode.getChildNodes();
for (int i = 0, length = nodes.getLength(); i < length; i++) {
Node node = nodes.item(i);
@ -181,7 +179,7 @@ public class MediaDataExtractor {
}
private static abstract class TemplateChildNodeComparator {
abstract public boolean match(Node node);
public abstract boolean match(Node node);
}
private Node findTemplateParameter(Node templateNode, String name) throws IOException {

View file

@ -42,7 +42,7 @@ public class MediaWikiImageView extends SimpleDraweeView {
if (currentThumbnailTask != null) {
currentThumbnailTask.cancel(true);
}
if(media == null) {
if (media == null) {
return;
}

View file

@ -2,18 +2,22 @@ package fr.free.nrw.commons;
import android.content.Context;
import android.preference.PreferenceManager;
import android.support.annotation.NonNull;
import org.apache.commons.codec.binary.Hex;
import org.apache.commons.codec.digest.DigestUtils;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.Locale;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import fr.free.nrw.commons.auth.LoginActivity;
import fr.free.nrw.commons.settings.Prefs;
import timber.log.Timber;
public class Utils {
@ -33,7 +37,7 @@ public class Utils {
}
}
public static String makeThumbBaseUrl(String filename) {
public static String makeThumbBaseUrl(@NonNull String filename) {
String name = new PageTitle(filename).getPrefixedText();
String sha = new String(Hex.encodeHex(DigestUtils.md5(name)));
return String.format("%s/%s/%s/%s", BuildConfig.IMAGE_URL_BASE, sha.substring(0, 1), sha.substring(0, 2), urlEncode(name));
@ -89,4 +93,37 @@ public class Utils {
public static boolean isDarkTheme(Context context) {
return PreferenceManager.getDefaultSharedPreferences(context).getBoolean("theme", false);
}
/**
* Will be used to fetch the logs generated by the app ever since the beginning of times....
* i.e. since the time the app started.
*
* @return String containing all the logs since the time the app started
*/
public static String getAppLogs() {
final String processId = Integer.toString(android.os.Process.myPid());
StringBuilder stringBuilder = new StringBuilder();
try {
String[] command = new String[] {"logcat","-d","-v","threadtime"};
Process process = Runtime.getRuntime().exec(command);
BufferedReader bufferedReader = new BufferedReader(
new InputStreamReader(process.getInputStream())
);
String line;
while ((line = bufferedReader.readLine()) != null) {
if (line.contains(processId)) {
stringBuilder.append(line);
}
}
} catch (IOException ioe) {
Timber.e("getAppLogs failed", ioe);
}
return stringBuilder.toString();
}
}

View file

@ -18,7 +18,7 @@ import static android.accounts.AccountManager.KEY_ACCOUNT_TYPE;
public class AccountUtil {
static final String ACCOUNT_TYPE = "fr.free.nrw.commons";
public static final String ACCOUNT_TYPE = "fr.free.nrw.commons";
private final Context context;
public AccountUtil(Context context) {

View file

@ -34,6 +34,7 @@ import fr.free.nrw.commons.Utils;
import fr.free.nrw.commons.WelcomeActivity;
import fr.free.nrw.commons.contributions.ContributionsActivity;
import fr.free.nrw.commons.mwapi.MediaWikiApi;
import fr.free.nrw.commons.theme.NavigationBaseActivity;
import timber.log.Timber;
import static android.view.KeyEvent.KEYCODE_ENTER;
@ -199,7 +200,7 @@ public class LoginActivity extends AccountAuthenticatorActivity {
}
public void startMainActivity() {
ContributionsActivity.startYourself(this);
NavigationBaseActivity.startActivityWithFlags(this, ContributionsActivity.class, Intent.FLAG_ACTIVITY_CLEAR_TOP);
finish();
}
@ -253,8 +254,8 @@ public class LoginActivity extends AccountAuthenticatorActivity {
@Override
public void afterTextChanged(Editable editable) {
boolean enabled = usernameEdit.getText().length() != 0 && passwordEdit.getText().length() != 0 &&
(BuildConfig.DEBUG || twoFactorEdit.getText().length() != 0 || twoFactorEdit.getVisibility() != View.VISIBLE);
boolean enabled = usernameEdit.getText().length() != 0 && passwordEdit.getText().length() != 0
&& (BuildConfig.DEBUG || twoFactorEdit.getText().length() != 0 || twoFactorEdit.getVisibility() != View.VISIBLE);
loginButton.setEnabled(enabled);
}
}

View file

@ -149,10 +149,13 @@ public class ContributionsActivity
if (savedInstanceState != null) {
mediaDetails = (MediaDetailPagerFragment)supportFragmentManager
.findFragmentById(R.id.contributionsFragmentContainer);
getSupportLoaderManager().initLoader(0, null, this);
}
requestAuthToken();
initDrawer();
setTitle(getString(R.string.title_activity_contributions));
setUploadCount();
}
@Override
@ -242,6 +245,8 @@ public class ContributionsActivity
@Override
public void onLoadFinished(Loader<Cursor> cursorLoader, Cursor cursor) {
contributionsList.changeProgressBarVisibility(false);
if (contributionsList.getAdapter() == null) {
contributionsList.setAdapter(new ContributionsListAdapter(getApplicationContext(),
cursor, 0));
@ -249,8 +254,6 @@ public class ContributionsActivity
((CursorAdapter) contributionsList.getAdapter()).swapCursor(cursor);
}
setUploadCount();
contributionsList.clearSyncMessage();
notifyAndMigrateDataSetObservers();
}
@ -281,18 +284,16 @@ public class ContributionsActivity
@SuppressWarnings("ConstantConditions")
private void setUploadCount() {
compositeDisposable.add(
mediaWikiApi
.getUploadCount(sessionManager.getCurrentAccount().name)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
uploadCount -> getSupportActionBar().setSubtitle(getResources()
.getQuantityString(R.plurals.contributions_subtitle,
uploadCount, uploadCount)),
t -> Timber.e(t, "Fetching upload count failed")
)
);
compositeDisposable.add(mediaWikiApi
.getUploadCount(sessionManager.getCurrentAccount().name)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
uploadCount -> getSupportActionBar().setSubtitle(getResources()
.getQuantityString(R.plurals.contributions_subtitle,
uploadCount, uploadCount)),
t -> Timber.e(t, "Fetching upload count failed")
));
}
@Override
@ -344,9 +345,4 @@ public class ContributionsActivity
public void refreshSource() {
getSupportLoaderManager().restartLoader(0, null, this);
}
public static void startYourself(Context context) {
context.startActivity(new Intent(context, ContributionsActivity.class));
}
}

View file

@ -34,7 +34,7 @@ class ContributionsListAdapter extends CursorAdapter {
views.seqNumView.setText(String.valueOf(cursor.getPosition() + 1));
views.seqNumView.setVisibility(View.VISIBLE);
switch(contribution.getState()) {
switch (contribution.getState()) {
case Contribution.STATE_COMPLETED:
views.stateView.setVisibility(View.GONE);
views.progressView.setVisibility(View.GONE);
@ -50,7 +50,7 @@ class ContributionsListAdapter extends CursorAdapter {
views.progressView.setVisibility(View.VISIBLE);
long total = contribution.getDataLength();
long transferred = contribution.getTransferred();
if(transferred == 0 || transferred >= total) {
if (transferred == 0 || transferred >= total) {
views.progressView.setIndeterminate(true);
} else {
views.progressView.setProgress((int)(((double)transferred / (double)total) * 100));

View file

@ -5,7 +5,6 @@ import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.os.Build;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.support.annotation.NonNull;
import android.support.v4.content.ContextCompat;
import android.support.v7.app.AlertDialog;
@ -18,6 +17,7 @@ import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.GridView;
import android.widget.ListAdapter;
import android.widget.ProgressBar;
import android.widget.TextView;
import javax.inject.Inject;
@ -42,10 +42,12 @@ public class ContributionsListFragment extends DaggerFragment {
GridView contributionsList;
@BindView(R.id.waitingMessage)
TextView waitingMessage;
@BindView(R.id.emptyMessage)
TextView emptyMessage;
@BindView(R.id.loadingContributionsProgressBar)
ProgressBar progressBar;
@Inject @Named("prefs") SharedPreferences prefs;
@Inject @Named("default_preferences") SharedPreferences defaultPrefs;
private ContributionController controller;
@Override
@ -69,6 +71,7 @@ public class ContributionsListFragment extends DaggerFragment {
waitingMessage.setVisibility(GONE);
}
changeProgressBarVisibility(true);
return v;
}
@ -80,6 +83,10 @@ public class ContributionsListFragment extends DaggerFragment {
this.contributionsList.setAdapter(adapter);
}
public void changeProgressBarVisibility(boolean isVisible) {
this.progressBar.setVisibility(isVisible ? View.VISIBLE : View.GONE);
}
@Override
public void onSaveInstanceState(Bundle outState) {
if (outState == null) {

View file

@ -57,6 +57,9 @@ public class ContributionsSyncAdapter extends AbstractThreadedSyncAdapter {
}
private boolean fileExists(ContentProviderClient client, String filename) {
if (filename == null) {
return false;
}
Cursor cursor = null;
try {
cursor = client.query(BASE_URI,

View file

@ -7,7 +7,7 @@ import android.database.sqlite.SQLiteOpenHelper;
import fr.free.nrw.commons.contributions.Contribution;
import fr.free.nrw.commons.modifications.ModifierSequence;
public class DBOpenHelper extends SQLiteOpenHelper{
public class DBOpenHelper extends SQLiteOpenHelper {
private static final String DATABASE_NAME = "commons.db";
private static final int DATABASE_VERSION = 6;

View file

@ -15,6 +15,7 @@ import fr.free.nrw.commons.auth.AccountUtil;
import fr.free.nrw.commons.auth.SessionManager;
import fr.free.nrw.commons.caching.CacheController;
import fr.free.nrw.commons.data.DBOpenHelper;
import fr.free.nrw.commons.location.LocationServiceManager;
import fr.free.nrw.commons.mwapi.ApacheHttpClientMediaWikiApi;
import fr.free.nrw.commons.mwapi.MediaWikiApi;
import fr.free.nrw.commons.nearby.NearbyPlaces;
@ -71,6 +72,12 @@ public class CommonsApplicationModule {
return new ApacheHttpClientMediaWikiApi(BuildConfig.WIKIMEDIA_API_HOST);
}
@Provides
@Singleton
public LocationServiceManager provideLocationServiceManager() {
return new LocationServiceManager(application);
}
@Provides
@Singleton
public CacheController provideCacheController() {

View file

@ -13,7 +13,7 @@ public class LatLng {
* @param longitude double value
*/
public LatLng(double latitude, double longitude, float accuracy) {
if(-180.0D <= longitude && longitude < 180.0D) {
if (-180.0D <= longitude && longitude < 180.0D) {
this.longitude = longitude;
} else {
this.longitude = ((longitude - 180.0D) % 360.0D + 360.0D) % 360.0D - 180.0D;
@ -33,9 +33,9 @@ public class LatLng {
}
public boolean equals(Object o) {
if(this == o) {
if (this == o) {
return true;
} else if(!(o instanceof LatLng)) {
} else if (!(o instanceof LatLng)) {
return false;
} else {
LatLng var2 = (LatLng)o;

View file

@ -7,22 +7,33 @@ import android.location.LocationListener;
import android.location.LocationManager;
import android.os.Bundle;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
import javax.inject.Inject;
import javax.inject.Singleton;
import timber.log.Timber;
public class LocationServiceManager implements LocationListener {
private String provider;
private LocationManager locationManager;
private LatLng latestLocation;
private LatLng lastLocation;
private Float latestLocationAccuracy;
private final List<LocationUpdateListener> locationListeners = new CopyOnWriteArrayList<>();
public LocationServiceManager(Context context) {
this.locationManager = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE);
provider = locationManager.getBestProvider(new Criteria(), true);
}
public LatLng getLatestLocation() {
return latestLocation;
public boolean isProviderEnabled() {
return locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER);
}
public LatLng getLastLocation() {
return lastLocation;
}
/**
@ -64,6 +75,16 @@ public class LocationServiceManager implements LocationListener {
}
}
public void addLocationListener(LocationUpdateListener listener) {
if (!locationListeners.contains(listener)) {
locationListeners.add(listener);
}
}
public void removeLocationListener(LocationUpdateListener listener) {
locationListeners.remove(listener);
}
@Override
public void onLocationChanged(Location location) {
double currentLatitude = location.getLatitude();
@ -71,8 +92,11 @@ public class LocationServiceManager implements LocationListener {
latestLocationAccuracy = location.getAccuracy();
Timber.d("Latitude: %f Longitude: %f Accuracy %f",
currentLatitude, currentLongitude, latestLocationAccuracy);
lastLocation = new LatLng(currentLatitude, currentLongitude, latestLocationAccuracy);
latestLocation = new LatLng(currentLatitude, currentLongitude, latestLocationAccuracy);
for (LocationUpdateListener listener : locationListeners) {
listener.onLocationChanged(lastLocation);
}
}
@Override

View file

@ -0,0 +1,5 @@
package fr.free.nrw.commons.location;
public interface LocationUpdateListener {
void onLocationChanged(LatLng latLng);
}

View file

@ -152,8 +152,14 @@ public class MediaDetailPagerFragment extends DaggerFragment implements ViewPage
private void downloadMedia(Media m) {
String imageUrl = m.getImageUrl(),
fileName = m.getFilename();
if (imageUrl == null || fileName == null) {
return;
}
// Strip 'File:' from beginning of filename, we really shouldn't store it
fileName = fileName.replaceFirst("^File:", "");
Uri imageUri = Uri.parse(imageUrl);
DownloadManager.Request req = new DownloadManager.Request(imageUri);

View file

@ -13,7 +13,7 @@ public class CategoryModifier extends PageModifier {
public CategoryModifier(String... categories) {
super(MODIFIER_NAME);
JSONArray categoriesArray = new JSONArray();
for(String category: categories) {
for (String category: categories) {
categoriesArray.put(category);
}
try {
@ -34,7 +34,7 @@ public class CategoryModifier extends PageModifier {
categories = params.optJSONArray(PARAM_CATEGORIES);
StringBuilder categoriesString = new StringBuilder();
for(int i=0; i < categories.length(); i++) {
for (int i = 0; i < categories.length(); i++) {
String category = categories.optString(i);
categoriesString.append("\n[[Category:").append(category).append("]]");
}

View file

@ -16,7 +16,7 @@ import dagger.android.AndroidInjection;
import fr.free.nrw.commons.data.DBOpenHelper;
import timber.log.Timber;
public class ModificationsContentProvider extends ContentProvider{
public class ModificationsContentProvider extends ContentProvider {
private static final int MODIFICATIONS = 1;
private static final int MODIFICATIONS_ID = 2;
@ -51,7 +51,7 @@ public class ModificationsContentProvider extends ContentProvider{
int uriType = uriMatcher.match(uri);
switch(uriType) {
switch (uriType) {
case MODIFICATIONS:
break;
default:
@ -112,7 +112,7 @@ public class ModificationsContentProvider extends ContentProvider{
sqlDB.beginTransaction();
switch (uriType) {
case MODIFICATIONS:
for(ContentValues value: values) {
for (ContentValues value: values) {
Timber.d("Inserting! %s", value);
sqlDB.insert(ModifierSequence.Table.TABLE_NAME, null, value);
}

View file

@ -27,7 +27,7 @@ public class ModifierSequence {
public ModifierSequence(Uri mediaUri, JSONObject data) {
this(mediaUri);
JSONArray modifiersJSON = data.optJSONArray("modifiers");
for (int i=0; i< modifiersJSON.length(); i++) {
for (int i = 0; i < modifiersJSON.length(); i++) {
modifiers.add(PageModifier.fromJSON(modifiersJSON.optJSONObject(i)));
}
}
@ -49,7 +49,7 @@ public class ModifierSequence {
public String getEditSummary() {
StringBuilder editSummary = new StringBuilder();
for(PageModifier modifier: modifiers) {
for (PageModifier modifier: modifiers) {
editSummary.append(modifier.getEditSumary()).append(" ");
}
editSummary.append("Via Commons Mobile App");
@ -93,12 +93,12 @@ public class ModifierSequence {
public void save() {
try {
if(contentUri == null) {
if (contentUri == null) {
contentUri = client.insert(ModificationsContentProvider.BASE_URI, this.toContentValues());
} else {
client.update(contentUri, toContentValues(), null, null);
}
} catch(RemoteException e) {
} catch (RemoteException e) {
throw new RuntimeException(e);
}
}

View file

@ -7,9 +7,9 @@ public abstract class PageModifier {
public static PageModifier fromJSON(JSONObject data) {
String name = data.optString("name");
if(name.equals(CategoryModifier.MODIFIER_NAME)) {
if (name.equals(CategoryModifier.MODIFIER_NAME)) {
return new CategoryModifier(data.optJSONObject("data"));
} else if(name.equals(TemplateRemoveModifier.MODIFIER_NAME)) {
} else if (name.equals(TemplateRemoveModifier.MODIFIER_NAME)) {
return new TemplateRemoveModifier(data.optJSONObject("data"));
}

View file

@ -41,18 +41,18 @@ public class TemplateRemoveModifier extends PageModifier {
Pattern templateStartPattern = Pattern.compile("\\{\\{" + templateNormalized, Pattern.CASE_INSENSITIVE);
Matcher matcher = templateStartPattern.matcher(pageContents);
while(matcher.find()) {
while (matcher.find()) {
int braceCount = 1;
int startIndex = matcher.start();
int curIndex = matcher.end();
Matcher openMatch = PATTERN_TEMPLATE_OPEN.matcher(pageContents);
Matcher closeMatch = PATTERN_TEMPLATE_CLOSE.matcher(pageContents);
while(curIndex < pageContents.length()) {
while (curIndex < pageContents.length()) {
boolean openFound = openMatch.find(curIndex);
boolean closeFound = closeMatch.find(curIndex);
if(openFound && (!closeFound || openMatch.start() < closeMatch.start())) {
if (openFound && (!closeFound || openMatch.start() < closeMatch.start())) {
braceCount++;
curIndex = openMatch.end();
} else if (closeFound) {
@ -71,8 +71,8 @@ public class TemplateRemoveModifier extends PageModifier {
}
// Strip trailing whitespace
while(curIndex < pageContents.length()) {
if(pageContents.charAt(curIndex) == ' ' || pageContents.charAt(curIndex) == '\n') {
while (curIndex < pageContents.length()) {
if (pageContents.charAt(curIndex) == ' ' || pageContents.charAt(curIndex) == '\n') {
curIndex++;
} else {
break;

View file

@ -388,10 +388,15 @@ public class ApacheHttpClientMediaWikiApi implements MediaWikiApi {
@Override
@NonNull
public UploadResult uploadFile(String filename, InputStream file, long dataLength, String pageContents, String editSummary, final ProgressListener progressListener) throws IOException {
public UploadResult uploadFile(String filename,
@NonNull InputStream file,
long dataLength,
String pageContents,
String editSummary,
final ProgressListener progressListener) throws IOException {
ApiResult result = api.upload(filename, file, dataLength, pageContents, editSummary, progressListener::onProgress);
Log.e("WTF", "Result: " +result.toString());
Log.e("WTF", "Result: " + result.toString());
String resultStatus = result.getString("/api/upload/@result");
if (!resultStatus.equals("Success")) {

View file

@ -5,9 +5,7 @@ import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.location.LocationManager;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Build;
import android.os.Bundle;
import android.preference.PreferenceManager;
@ -30,34 +28,42 @@ import com.google.gson.GsonBuilder;
import java.util.List;
import javax.inject.Inject;
import javax.inject.Named;
import butterknife.BindView;
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.LocationUpdateListener;
import fr.free.nrw.commons.theme.NavigationBaseActivity;
import fr.free.nrw.commons.utils.UriSerializer;
import fr.free.nrw.commons.utils.ViewUtil;
import io.reactivex.Observable;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.Disposable;
import io.reactivex.schedulers.Schedulers;
import timber.log.Timber;
public class NearbyActivity extends NavigationBaseActivity {
@BindView(R.id.progressBar)
ProgressBar progressBar;
@Inject NearbyPlaces nearbyPlaces;
@Inject @Named("default_preferences") SharedPreferences prefs;
public class NearbyActivity extends NavigationBaseActivity implements LocationUpdateListener {
private boolean isMapViewActive = false;
private static final int LOCATION_REQUEST = 1;
private static final String MAP_LAST_USED_PREFERENCE = "mapLastUsed";
private LocationServiceManager locationManager;
@BindView(R.id.progressBar)
ProgressBar progressBar;
@Inject
LocationServiceManager locationManager;
@Inject
NearbyController nearbyController;
private LatLng curLatLang;
private Bundle bundle;
private NearbyAsyncTask nearbyAsyncTask;
private SharedPreferences sharedPreferences;
private NearbyActivityMode viewMode;
private Disposable placesDisposable;
private boolean lockNearbyView; //Determines if the nearby places needs to be refreshed
@Override
protected void onCreate(Bundle savedInstanceState) {
@ -97,7 +103,8 @@ public class NearbyActivity extends NavigationBaseActivity {
// Handle item selection
switch (item.getItemId()) {
case R.id.action_refresh:
refreshView();
lockNearbyView = false;
refreshView(true);
return true;
case R.id.action_toggle_view:
viewMode = viewMode.toggle();
@ -109,19 +116,11 @@ public class NearbyActivity extends NavigationBaseActivity {
}
}
private void startLookingForNearby() {
locationManager = new LocationServiceManager(this);
locationManager.registerLocationManager();
curLatLang = locationManager.getLatestLocation();
nearbyAsyncTask = new NearbyAsyncTask(this, new NearbyController(nearbyPlaces, prefs));
nearbyAsyncTask.execute();
}
private void checkLocationPermission() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
if (ContextCompat.checkSelfPermission(this,
Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED) {
startLookingForNearby();
refreshView(false);
} else {
if (ContextCompat.checkSelfPermission(this,
Manifest.permission.ACCESS_FINE_LOCATION)
@ -162,7 +161,7 @@ public class NearbyActivity extends NavigationBaseActivity {
}
}
} else {
startLookingForNearby();
refreshView(false);
}
}
@ -171,16 +170,10 @@ public class NearbyActivity extends NavigationBaseActivity {
switch (requestCode) {
case LOCATION_REQUEST: {
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
startLookingForNearby();
refreshView(false);
} else {
//If permission not granted, go to page that says Nearby Places cannot be displayed
if (nearbyAsyncTask != null) {
nearbyAsyncTask.cancel(true);
}
if (progressBar != null) {
progressBar.setVisibility(View.GONE);
}
hideProgressBar();
showLocationPermissionDeniedErrorDialog();
}
}
@ -205,8 +198,7 @@ public class NearbyActivity extends NavigationBaseActivity {
}
private void checkGps() {
LocationManager manager = (LocationManager) getSystemService(LOCATION_SERVICE);
if (!manager.isProviderEnabled(LocationManager.GPS_PROVIDER)) {
if (!locationManager.isProviderEnabled()) {
Timber.d("GPS is not enabled");
new AlertDialog.Builder(this)
.setMessage(R.string.gps_disabled)
@ -231,104 +223,106 @@ public class NearbyActivity extends NavigationBaseActivity {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == 1) {
Timber.d("User is back from Settings page");
refreshView();
refreshView(false);
}
}
private void toggleView() {
if (nearbyAsyncTask != null) {
if (nearbyAsyncTask.getStatus() == AsyncTask.Status.FINISHED) {
if (viewMode.isMap()) {
setMapFragment();
} else {
setListFragment();
}
}
sharedPreferences.edit().putBoolean(MAP_LAST_USED_PREFERENCE, viewMode.isMap()).apply();
if (viewMode.isMap()) {
setMapFragment();
} else {
setListFragment();
}
sharedPreferences.edit().putBoolean(MAP_LAST_USED_PREFERENCE, viewMode.isMap()).apply();
}
@Override
protected void onStart() {
super.onStart();
locationManager.registerLocationManager();
locationManager.addLocationListener(this);
}
@Override
protected void onStop() {
super.onStop();
locationManager.removeLocationListener(this);
locationManager.unregisterLocationManager();
}
@Override
protected void onDestroy() {
super.onDestroy();
if (placesDisposable != null) {
placesDisposable.dispose();
}
}
@Override
protected void onResume() {
super.onResume();
lockNearbyView = false;
checkGps();
refreshView(false);
}
@Override
protected void onPause() {
super.onPause();
if (nearbyAsyncTask != null) {
nearbyAsyncTask.cancel(true);
private void refreshView(boolean isHardRefresh) {
if (lockNearbyView) {
return;
}
LatLng lastLocation = locationManager.getLastLocation();
if (curLatLang != null && curLatLang.equals(lastLocation)) { //refresh view only if location has changed
if (isHardRefresh) {
ViewUtil.showLongToast(this, R.string.nearby_location_has_not_changed);
}
return;
}
curLatLang = lastLocation;
if (curLatLang == null) {
Timber.d("Skipping update of nearby places as location is unavailable");
return;
}
progressBar.setVisibility(View.VISIBLE);
placesDisposable = Observable.fromCallable(() -> nearbyController
.loadAttractionsFromLocation(curLatLang, this))
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(this::populatePlaces);
}
private void refreshView() {
nearbyAsyncTask = new NearbyAsyncTask(this, new NearbyController(nearbyPlaces, prefs));
nearbyAsyncTask.execute();
private void populatePlaces(List<Place> placeList) {
Gson gson = new GsonBuilder()
.registerTypeAdapter(Uri.class, new UriSerializer())
.create();
String gsonPlaceList = gson.toJson(placeList);
String gsonCurLatLng = gson.toJson(curLatLang);
if (placeList.size() == 0) {
int duration = Toast.LENGTH_SHORT;
Toast toast = Toast.makeText(this, R.string.no_nearby, duration);
toast.show();
}
bundle.clear();
bundle.putString("PlaceList", gsonPlaceList);
bundle.putString("CurLatLng", gsonCurLatLng);
lockNearbyView = true;
// Begin the transaction
if (viewMode.isMap()) {
setMapFragment();
} else {
setListFragment();
}
hideProgressBar();
}
@Override
protected void onDestroy() {
super.onDestroy();
if (locationManager != null) {
locationManager.unregisterLocationManager();
}
}
private class NearbyAsyncTask extends AsyncTask<Void, Integer, List<Place>> {
private final Context mContext;
private final NearbyController nearbyController;
private NearbyAsyncTask(Context context, NearbyController nearbyController) {
this.mContext = context;
this.nearbyController = nearbyController;
}
@Override
protected void onProgressUpdate(Integer... values) {
super.onProgressUpdate(values);
}
@Override
protected List<Place> doInBackground(Void... params) {
return nearbyController.loadAttractionsFromLocation(curLatLang, NearbyActivity.this);
}
@Override
protected void onPostExecute(List<Place> placeList) {
super.onPostExecute(placeList);
if (isCancelled()) {
return;
}
Gson gson = new GsonBuilder()
.registerTypeAdapter(Uri.class, new UriSerializer())
.create();
String gsonPlaceList = gson.toJson(placeList);
String gsonCurLatLng = gson.toJson(curLatLang);
if (placeList.size() == 0) {
int duration = Toast.LENGTH_SHORT;
Toast toast = Toast.makeText(mContext, R.string.no_nearby, duration);
toast.show();
}
bundle.clear();
bundle.putString("PlaceList", gsonPlaceList);
bundle.putString("CurLatLng", gsonCurLatLng);
// Begin the transaction
if (viewMode.isMap()) {
setMapFragment();
} else {
setListFragment();
}
if (progressBar != null) {
progressBar.setVisibility(View.GONE);
}
private void hideProgressBar() {
if (progressBar != null) {
progressBar.setVisibility(View.GONE);
}
}
@ -354,8 +348,8 @@ public class NearbyActivity extends NavigationBaseActivity {
fragmentTransaction.commitAllowingStateLoss();
}
public static void startYourself(Context context) {
Intent settingsIntent = new Intent(context, NearbyActivity.class);
context.startActivity(settingsIntent);
@Override
public void onLocationChanged(LatLng latLng) {
refreshView(false);
}
}

View file

@ -14,6 +14,9 @@ import java.util.List;
import java.util.Locale;
import java.util.Map;
import javax.inject.Inject;
import javax.inject.Named;
import fr.free.nrw.commons.R;
import fr.free.nrw.commons.location.LatLng;
import fr.free.nrw.commons.utils.UiUtils;
@ -22,13 +25,14 @@ import timber.log.Timber;
import static fr.free.nrw.commons.utils.LengthUtils.computeDistanceBetween;
import static fr.free.nrw.commons.utils.LengthUtils.formatDistanceBetween;
public class NearbyController {
private static final int MAX_RESULTS = 1000;
private final NearbyPlaces nearbyPlaces;
private final SharedPreferences prefs;
public NearbyController(NearbyPlaces nearbyPlaces, SharedPreferences prefs) {
@Inject
public NearbyController(NearbyPlaces nearbyPlaces,
@Named("default_preferences") SharedPreferences prefs) {
this.nearbyPlaces = nearbyPlaces;
this.prefs = prefs;
}

View file

@ -46,7 +46,7 @@ public class NearbyPlaces {
try {
// increase the radius gradually to find a satisfactory number of nearby places
while (radius < MAX_RADIUS) {
while (radius <= MAX_RADIUS) {
places = getFromWikidataQuery(curLatLng, lang, radius);
Timber.d("%d results at radius: %f", places.size(), radius);
if (places.size() >= MIN_RESULTS) {
@ -62,6 +62,11 @@ public class NearbyPlaces {
Timber.d("back to initial radius: %f", radius);
radius = INITIAL_RADIUS;
}
// make sure we will be able to send at least one request next time
if (radius > MAX_RADIUS) {
radius = MAX_RADIUS;
}
return places;
}

View file

@ -1,7 +1,5 @@
package fr.free.nrw.commons.settings;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.support.v7.app.AppCompatDelegate;
@ -54,9 +52,4 @@ public class SettingsActivity extends NavigationBaseActivity {
return super.onOptionsItemSelected(item);
}
}
public static void startYourself(Context context) {
Intent settingsIntent = new Intent(context, SettingsActivity.class);
context.startActivity(settingsIntent);
}
}

View file

@ -1,23 +1,41 @@
package fr.free.nrw.commons.settings;
import android.Manifest;
import android.app.AlertDialog;
import android.content.ActivityNotFoundException;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.preference.CheckBoxPreference;
import android.preference.EditTextPreference;
import android.preference.ListPreference;
import android.preference.Preference;
import android.preference.PreferenceFragment;
import android.support.annotation.NonNull;
import android.support.v4.content.ContextCompat;
import android.support.v4.content.FileProvider;
import android.widget.Toast;
import java.io.File;
import javax.inject.Inject;
import javax.inject.Named;
import dagger.android.AndroidInjection;
import fr.free.nrw.commons.BuildConfig;
import fr.free.nrw.commons.CommonsApplication;
import fr.free.nrw.commons.R;
import fr.free.nrw.commons.Utils;
import fr.free.nrw.commons.utils.FileUtils;
public class SettingsFragment extends PreferenceFragment {
private static final int REQUEST_CODE_WRITE_EXTERNAL_STORAGE = 100;
@Inject @Named("default_preferences") SharedPreferences prefs;
@Override
@ -76,6 +94,63 @@ public class SettingsFragment extends PreferenceFragment {
return true;
});
Preference sendLogsPreference = findPreference("sendLogFile");
sendLogsPreference.setOnPreferenceClickListener(preference -> {
//first we need to check if we have the necessary permissions
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
if (ContextCompat.checkSelfPermission(
getActivity(),
Manifest.permission.WRITE_EXTERNAL_STORAGE)
==
PackageManager.PERMISSION_GRANTED) {
sendAppLogsViaEmail();
} else {
//first get the necessary permission
requestPermissions(new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},
REQUEST_CODE_WRITE_EXTERNAL_STORAGE);
}
} else {
sendAppLogsViaEmail();
}
return true;
});
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if (requestCode == REQUEST_CODE_WRITE_EXTERNAL_STORAGE) {
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
sendAppLogsViaEmail();
}
}
}
private void sendAppLogsViaEmail() {
String appLogs = Utils.getAppLogs();
File appLogsFile = FileUtils.createAndGetAppLogsFile(appLogs);
Context applicationContext = getActivity().getApplicationContext();
Uri appLogsFilePath = FileProvider.getUriForFile(
getActivity(),
applicationContext.getPackageName() + ".provider",
appLogsFile
);
Intent feedbackIntent = new Intent(Intent.ACTION_SEND);
feedbackIntent.setType("message/rfc822");
feedbackIntent.putExtra(Intent.EXTRA_EMAIL,
new String[]{CommonsApplication.FEEDBACK_EMAIL});
feedbackIntent.putExtra(Intent.EXTRA_SUBJECT,
String.format(CommonsApplication.FEEDBACK_EMAIL_SUBJECT,
BuildConfig.VERSION_NAME));
feedbackIntent.putExtra(Intent.EXTRA_STREAM,appLogsFilePath);
try {
startActivity(feedbackIntent);
} catch (ActivityNotFoundException e) {
Toast.makeText(getActivity(), R.string.no_email_client, Toast.LENGTH_SHORT).show();
}
}
}

View file

@ -13,7 +13,7 @@ public abstract class BaseActivity extends DaggerAppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
boolean currentThemeIsDark = PreferenceManager.getDefaultSharedPreferences(this).getBoolean("theme", false);
if (currentThemeIsDark) {
if (currentThemeIsDark){
currentTheme = true;
setTheme(R.style.DarkAppTheme);
} else {

View file

@ -1,6 +1,9 @@
package fr.free.nrw.commons.theme;
import android.accounts.Account;
import android.accounts.AccountManager;
import android.content.ActivityNotFoundException;
import android.content.Context;
import android.content.Intent;
import android.support.annotation.NonNull;
import android.support.design.widget.NavigationView;
@ -9,7 +12,9 @@ import android.support.v7.app.ActionBarDrawerToggle;
import android.support.v7.app.AlertDialog;
import android.support.v7.widget.Toolbar;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import android.widget.Toast;
import butterknife.BindView;
@ -18,6 +23,7 @@ import fr.free.nrw.commons.BuildConfig;
import fr.free.nrw.commons.CommonsApplication;
import fr.free.nrw.commons.R;
import fr.free.nrw.commons.WelcomeActivity;
import fr.free.nrw.commons.auth.AccountUtil;
import fr.free.nrw.commons.auth.LoginActivity;
import fr.free.nrw.commons.contributions.ContributionsActivity;
import fr.free.nrw.commons.nearby.NearbyActivity;
@ -47,6 +53,22 @@ public abstract class NavigationBaseActivity extends BaseActivity
toggle.setDrawerIndicatorEnabled(true);
toggle.syncState();
setDrawerPaneWidth();
setUserName();
}
/**
* Set the username in navigationHeader.
*/
private void setUserName() {
View navHeaderView = navigationView.getHeaderView(0);
TextView username = navHeaderView.findViewById(R.id.username);
AccountManager accountManager = AccountManager.get(this);
Account[] allAccounts = accountManager.getAccountsByType(AccountUtil.ACCOUNT_TYPE);
if (allAccounts.length != 0) {
username.setText(allAccounts[0].name);
}
}
public void initBackButton() {
@ -70,30 +92,25 @@ public abstract class NavigationBaseActivity extends BaseActivity
@Override
public boolean onNavigationItemSelected(@NonNull final MenuItem item) {
switch (item.getItemId()) {
final int itemId = item.getItemId();
switch (itemId) {
case R.id.action_home:
drawerLayout.closeDrawer(navigationView);
if (!(this instanceof ContributionsActivity)) {
ContributionsActivity.startYourself(this);
}
startActivityWithFlags(
this, ContributionsActivity.class, Intent.FLAG_ACTIVITY_CLEAR_TOP,
Intent.FLAG_ACTIVITY_SINGLE_TOP);
return true;
case R.id.action_nearby:
drawerLayout.closeDrawer(navigationView);
if (!(this instanceof NearbyActivity)) {
NearbyActivity.startYourself(this);
}
startActivityWithFlags(this, NearbyActivity.class, Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);
return true;
case R.id.action_about:
drawerLayout.closeDrawer(navigationView);
if (!(this instanceof AboutActivity)) {
AboutActivity.startYourself(this);
}
startActivityWithFlags(this, AboutActivity.class, Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);
return true;
case R.id.action_settings:
drawerLayout.closeDrawer(navigationView);
if (!(this instanceof SettingsActivity)) {
SettingsActivity.startYourself(this);
}
startActivityWithFlags(this, SettingsActivity.class, Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);
return true;
case R.id.action_introduction:
drawerLayout.closeDrawer(navigationView);
@ -127,6 +144,7 @@ public abstract class NavigationBaseActivity extends BaseActivity
.show();
return true;
default:
Timber.e("Unknown option [%s] selected from the navigation menu", itemId);
return false;
}
}
@ -143,4 +161,12 @@ public abstract class NavigationBaseActivity extends BaseActivity
finish();
}
}
public static <T> void startActivityWithFlags(Context context, Class<T> cls, int... flags) {
Intent intent = new Intent(context, cls);
for (int flag: flags) {
intent.addFlags(flag);
}
context.startActivity(intent);
}
}

View file

@ -123,8 +123,9 @@ public class FileUtils {
} catch (IllegalArgumentException e) {
Timber.d(e);
} finally {
if (cursor != null)
if (cursor != null) {
cursor.close();
}
}
return null;
}

View file

@ -224,7 +224,7 @@ public class GPSExtractor {
return decimalCoords;
}
private double convertToDegree(String stringDMS){
private double convertToDegree(String stringDMS) {
double result;
String[] DMS = stringDMS.split(",", 3);

View file

@ -69,7 +69,7 @@ public class MwVolleyApi {
* @param coords Coordinates to build query with
* @return URL for API query
*/
private String buildUrl (String coords){
private String buildUrl(String coords) {
Uri.Builder builder = Uri.parse(MWURL).buildUpon();

View file

@ -65,7 +65,7 @@ public class UploadController {
}
public void cleanup() {
if(isUploadServiceConnected) {
if (isUploadServiceConnected) {
context.unbindService(uploadServiceConnection);
}
}
@ -87,11 +87,11 @@ public class UploadController {
public void startUpload(final Contribution contribution, final ContributionUploadProgress onComplete) {
//Set creator, desc, and license
if(TextUtils.isEmpty(contribution.getCreator())) {
if (TextUtils.isEmpty(contribution.getCreator())) {
contribution.setCreator(sessionManager.getCurrentAccount().name);
}
if(contribution.getDescription() == null) {
if (contribution.getDescription() == null) {
contribution.setDescription("");
}
@ -109,11 +109,11 @@ public class UploadController {
long length;
ContentResolver contentResolver = context.getContentResolver();
try {
if(contribution.getDataLength() <= 0) {
if (contribution.getDataLength() <= 0) {
length = contentResolver
.openAssetFileDescriptor(contribution.getLocalUri(), "r")
.getLength();
if(length == -1) {
if (length == -1) {
// Let us find out the long way!
length = countBytes(contentResolver
.openInputStream(contribution.getLocalUri()));

View file

@ -185,7 +185,7 @@ public class UploadService extends HandlerService<Contribution> {
@SuppressLint("StringFormatInvalid")
private void uploadContribution(Contribution contribution) {
InputStream file = null;
InputStream file;
String notificationTag = contribution.getLocalUri().toString();
@ -196,6 +196,14 @@ public class UploadService extends HandlerService<Contribution> {
Timber.d("File not found");
Toast fileNotFound = Toast.makeText(this, R.string.upload_failed, Toast.LENGTH_LONG);
fileNotFound.show();
return;
}
//As the file is null there's no point in continuing the upload process
//mwapi.upload accepts a NonNull input stream
if(file == null) {
Timber.d("File not found");
return;
}
Timber.d("Before execution!");

View file

@ -15,6 +15,6 @@ public class ExecutorUtils {
}
};
public static Executor uiExecutor () { return uiExecutor;}
public static Executor uiExecutor() { return uiExecutor; }
}

View file

@ -1,11 +1,16 @@
package fr.free.nrw.commons.utils;
import android.os.Environment;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import fr.free.nrw.commons.CommonsApplication;
import timber.log.Timber;
public class FileUtils {
/**
@ -53,5 +58,32 @@ public class FileUtils {
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;
}
}
}

View file

@ -23,8 +23,8 @@ public class FragmentUtils {
.commitNow();
return true;
} catch (IllegalStateException e) {
Timber.e(e, "Could not add & commit fragment. " +
"Did you mean to call commitAllowingStateLoss?");
Timber.e(e, "Could not add & commit fragment. "
+ "Did you mean to call commitAllowingStateLoss?");
}
return false;
}

View file

@ -0,0 +1,17 @@
package fr.free.nrw.commons.utils;
import android.content.Context;
import android.support.annotation.StringRes;
import android.widget.Toast;
public class ViewUtil {
public static void showLongToast(final Context context, @StringRes final int stringResId) {
ExecutorUtils.uiExecutor().execute(new Runnable() {
@Override
public void run() {
Toast.makeText(context, context.getString(stringResId), Toast.LENGTH_LONG).show();
}
});
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 864 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 294 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 83 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

View file

@ -137,6 +137,7 @@
</android.support.design.widget.TextInputLayout>
<LinearLayout
android:id="@+id/buttonFrame"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/two_factor_container"
@ -170,6 +171,28 @@
</LinearLayout>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/buttonFrame"
android:layout_marginBottom="16dp"
android:layout_marginEnd="16dp"
android:layout_marginLeft="16dp"
android:layout_marginRight="16dp"
android:layout_marginStart="16dp">
<fr.free.nrw.commons.ui.widget.HtmlTextView
android:id="@+id/about_privacy_policy"
style="?android:textAppearanceSmall"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:layout_gravity="center_horizontal"
android:text="@string/about_privacy_policy"
android:layout_centerHorizontal="true"/>
</RelativeLayout>
</RelativeLayout>
</android.support.v7.widget.CardView>

View file

@ -137,6 +137,7 @@
</android.support.design.widget.TextInputLayout>
<LinearLayout
android:id="@+id/buttonFrame"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/two_factor_container"
@ -170,6 +171,28 @@
</LinearLayout>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/buttonFrame"
android:layout_marginBottom="16dp"
android:layout_marginEnd="16dp"
android:layout_marginLeft="16dp"
android:layout_marginRight="16dp"
android:layout_marginStart="16dp">
<fr.free.nrw.commons.ui.widget.HtmlTextView
android:id="@+id/about_privacy_policy"
style="?android:textAppearanceSmall"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:layout_gravity="center_horizontal"
android:text="@string/about_privacy_policy"
android:layout_centerHorizontal="true"/>
</RelativeLayout>
</RelativeLayout>
</android.support.v7.widget.CardView>

View file

@ -137,6 +137,7 @@
</android.support.design.widget.TextInputLayout>
<LinearLayout
android:id="@+id/buttonFrame"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/two_factor_container"
@ -170,6 +171,28 @@
</LinearLayout>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/buttonFrame"
android:layout_marginBottom="16dp"
android:layout_marginEnd="16dp"
android:layout_marginLeft="16dp"
android:layout_marginRight="16dp"
android:layout_marginStart="16dp">
<fr.free.nrw.commons.ui.widget.HtmlTextView
android:id="@+id/about_privacy_policy"
style="?android:textAppearanceSmall"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:layout_gravity="center_horizontal"
android:text="@string/about_privacy_policy"
android:layout_centerHorizontal="true"/>
</RelativeLayout>
</RelativeLayout>
</android.support.v7.widget.CardView>

View file

@ -1,8 +1,28 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_height="match_parent"
android:layout_width="match_parent"
android:background="@android:color/darker_gray">
<ImageView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/pictureOfTheDay"
android:layout_width="match_parent"
android:layout_height="172dp"
android:background="@android:color/darker_gray"
android:padding="16dp"
android:paddingLeft="16dp"
android:paddingTop="16dp"
android:paddingRight="16dp"
android:paddingBottom="5dp"
android:src="@drawable/commons_logo_large"/>
<TextView
android:id="@+id/username"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="TextView"
android:textColor="@color/item_white_background"
android:textSize="22sp"
android:layout_below="@+id/pictureOfTheDay"
android:layout_centerHorizontal="true"
android:paddingBottom="5dp"/>
</RelativeLayout>

View file

@ -1,5 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
@ -13,15 +14,14 @@
android:id="@+id/waitingMessage"
android:layout_gravity="center"
android:visibility="gone"
android:layout_centerHorizontal="true"
/>
<TextView
<ProgressBar
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/no_uploads_yet"
android:id="@+id/emptyMessage"
android:layout_gravity="center"
android:visibility="gone"
android:layout_centerInParent="true"
android:id="@+id/loadingContributionsProgressBar"
/>
<GridView
@ -36,4 +36,4 @@
android:fastScrollEnabled="true"
/>
</LinearLayout>
</RelativeLayout>

View file

@ -20,7 +20,10 @@
<string name="upload_progress_notification_title_finishing">اكتمال رفع %1$s</string>
<string name="upload_failed_notification_title">فشل رفع %1$s</string>
<string name="upload_failed_notification_subtitle">انقر لتشاهد</string>
<string name="uploads_pending_notification_indicator" fuzzy="true">متبقى %d</string>
<plurals name="uploads_pending_notification_indicator">
<item quantity="one">%d رفع ملف</item>
<item quantity="other">%d رفع ملفات</item>
</plurals>
<string name="title_activity_contributions">مرفوعاتي الأخيرة</string>
<string name="contribution_state_queued">في قائمة الانتظار</string>
<string name="contribution_state_failed">فشل</string>
@ -39,6 +42,7 @@
<string name="login_failed_password">لا يمكن تسجيل الدخول - فضلا تحقق من كلمة السر</string>
<string name="login_failed_throttled">الكثير من المحاولات غير الناجحة. الرجاء المحاولة مرة أخرى في بضع دقائق.</string>
<string name="login_failed_blocked">عذراً، لقد تم منع هذا المستخدم على كومنز</string>
<string name="login_failed_2fa_needed">يجب توفير رمز التحقق المزدوج.</string>
<string name="login_failed_generic">فشل تسجيل الدخول</string>
<string name="share_upload_button">ارفع</string>
<string name="multiple_share_base_title">اسم هذه المجموعة</string>
@ -47,6 +51,7 @@
<string name="categories_search_text_hint">تصنيفات البحث</string>
<string name="menu_save_categories">احفظ</string>
<string name="refresh_button">أنعش</string>
<string name="gps_disabled">عطل نظام الملاحة العالمي GPS بجهازك. أترغب في التنشيط؟</string>
<string name="enable_gps">تفعيل GPS</string>
<string name="contributions_subtitle_zero">لا مرفوعات بعد</string>
<string name="categories_not_found">لا توجد تصنيفات تطابق %1$s</string>
@ -61,6 +66,7 @@
<string name="about_credits" fuzzy="true">&lt;a href=\"https://github.com/commons-app/apps-android-commons/blob/master/CREDITS\"&gt;CREDITS&lt;/a&gt;</string>
<string name="title_activity_about">حول</string>
<string name="menu_feedback">إرسال ملاحظات (عبر البريد الإلكتروني)</string>
<string name="no_email_client">عميل البريد الإلكتروني غير مثبت</string>
<string name="provider_categories">التصانيف المستخدمة مؤخرا</string>
<string name="waiting_first_sync">ينتظر أول مزامنة…</string>
<string name="no_uploads_yet">لم ترفع بعد أية صور.</string>
@ -70,6 +76,10 @@
<string name="menu_download">نزّل</string>
<string name="preference_license">الرخصة</string>
<string name="preference_theme">الوضع الليلي</string>
<string name="preference_theme_summary">استخدم النمط الغامق</string>
<string name="tutorial_3_text">الرجاء عدم رفع</string>
<string name="tutorial_3_subtext">- صور ذاتية أو صور لأصدقاء\n- صور محملة من الانترنت\n- لقطات شاشة ذات ملكية خاصة</string>
<string name="tutorial_4_text">مثال رفع:</string>
<string name="welcome_wikipedia_text">ساهم بصورك. أنعش مقالات ويكيبيديا!</string>
<string name="welcome_wikipedia_subtext">تأتي صور ويكيبيديا من ويكيميديا كومنز.</string>
<string name="welcome_copyright_text">صورك تساعد الناس في شتى أنحاء العالم على التعلم.</string>
@ -84,16 +94,26 @@
<string name="menu_refresh">تحديث</string>
<string name="ok">موافق</string>
<string name="title_activity_nearby">الأماكن القريبة</string>
<string name="no_nearby">الأماكن القريبة غير متوفرة</string>
<string name="warning">تحذير</string>
<string name="yes">نعم</string>
<string name="no">لا</string>
<string name="media_detail_title">العنوان</string>
<string name="media_detail_media_title">عنوان الوسيط</string>
<string name="media_detail_description">الوصف</string>
<string name="media_detail_uploaded_date">تاريخ الرفع</string>
<string name="media_detail_license">الترخيص</string>
<string name="media_detail_coordinates">الإحداثيات</string>
<string name="media_detail_coordinates_empty">غير متوفر</string>
<string name="become_a_tester_title">انضم لمختبري اصدارات Beta (بيتا)</string>
<string name="use_wikidata">استخدم ويكي بيانات</string>
<string name="_2fa_code">رمز التحقق المزدوج 2FA</string>
<string name="number_of_uploads">حد الرفع الحالي</string>
<string name="maximum_limit">الحد الأقصى</string>
<string name="maximum_limit_alert">لا يمكن عرض أكثر من 500</string>
<string name="set_limit">تحديد حد الرفع الحالي</string>
<string name="login_failed_2fa_not_supported">التحقق المزدودج 2FA غير مدعوم حاليا</string>
<string name="logout_verification">أترغب فعلا في الخروج؟</string>
<string name="navigation_item_home">الرئيسية</string>
<string name="navigation_item_upload">رفع</string>
<string name="navigation_item_nearby">بالقرب من هنا</string>

View file

@ -73,9 +73,9 @@
<string name="title_activity_settings">Configuración</string>
<string name="title_activity_signup">Date d\'alta</string>
<string name="menu_about">Tocante a</string>
<string name="about_license" fuzzy="true">Software de códigu abiertu lliberáu baxo la &lt;a href=\"https://github.com/commons-app/apps-android-commons/blob/master/COPYING\"&gt;Llicencia Apache v2&lt;/a&gt;. %1$s ya\'l so logotipu son marques rexistraes de la Fundación Wikimedia y utilícense col so permisu. Nun tamos acreditaos pola Fundación Wikimedia nin tamos afiliaos con ella.</string>
<string name="about_license">La app de Wikimedia Commons ye software de códigu abiertu, creáu y calteníu por becaos y voluntarios de la comunidá de Wikimedia. La Fundación Wikimedia nun participa na creación, desendolcu nin caltenimientu de la app.</string>
<string name="about_improve">El &lt;a href=\"https://github.com/commons-app/apps-android-commons\"&gt;códigu fonte&lt;/a&gt; ya\'l &lt;a href=\"https://commons-app.github.io/\"&gt;sitiu web&lt;/a&gt; tán en GitHub. Crea una nueva &lt;a href=\"https://github.com/commons-app/apps-android-commons/issues\"&gt;incidencia en GitHub&lt;/a&gt; pa informar de problemes y suxerencies.</string>
<string name="about_privacy_policy" fuzzy="true">Wikimedia:Commons-android-strings-about privacy policy/ast</string>
<string name="about_privacy_policy">&lt;a href=\"https://github.com/commons-app/apps-android-commons/wiki/Privacy-policy\"&gt;Política d\'intimidá&lt;/a&gt;</string>
<string name="about_credits">&lt;a href=\"https://github.com/commons-app/apps-android-commons/blob/master/CREDITS\"&gt;Créditos&lt;/a&gt;</string>
<string name="title_activity_about">Tocante a</string>
<string name="menu_feedback">Unviar comentarios (per corréu)</string>
@ -147,7 +147,7 @@
<string name="media_detail_title">Títulu</string>
<string name="media_detail_media_title">Títulu del mediu</string>
<string name="media_detail_description">Descripción</string>
<string name="media_detail_description_explanation">Equí va la descripción del mediu. Posiblemente pué ser llargo enforma, y necesitará espardese per delles llinies. Sicasí, esperamos que se vea guapo.</string>
<string name="media_detail_description_explanation">Equí va la descripción del mediu. Esto pué ser llargo enforma, y necesitará espardese per delles llinies. Sicasí, esperamos que se vea bien.</string>
<string name="media_detail_uploaded_date">Data d\'unviu</string>
<string name="media_detail_license">Llicencia</string>
<string name="media_detail_coordinates">Coordenaes</string>
@ -198,4 +198,7 @@
<string name="give_permission">Dar permisu</string>
<string name="use_external_storage">Usar almacenamientu esternu</string>
<string name="use_external_storage_summary">Guardar nel preséu les imaxes tomaes cola cámara de la app</string>
<string name="send_log_file">Unviar ficheru de rexistru</string>
<string name="send_log_file_description">Unviar ficheru de rexistru a los desendolcadores per corréu electrónicu</string>
<string name="login_to_your_account">Anicia sesión na to cuenta</string>
</resources>

View file

@ -42,6 +42,7 @@
<string name="login_failed_password">প্রবেশ করা যাচ্ছে না - অনুগ্রহ করে আপনার পাসওয়ার্ড পরীক্ষা করুন</string>
<string name="login_failed_throttled">খুব বেশি অসফল প্রচেষ্টা। অনুগ্রহ করে কয়েক মিনিট পরে আবারও চেষ্টা করুন।</string>
<string name="login_failed_blocked">দুঃখিত, এই ব্যবহারকারীকে কমন্সে বাধা দেয়া হয়েছে</string>
<string name="login_failed_2fa_needed">অাপনাকে অবশ্যই অাপনার দু\'স্তরের সত্যায়নকরণ কোড দিতে হবে।</string>
<string name="login_failed_generic">প্রবেশ ব্যর্থ</string>
<string name="share_upload_button">আপলোড</string>
<string name="multiple_share_base_title">এই সেটের নাম</string>
@ -72,7 +73,7 @@
<string name="title_activity_settings">সেটিং</string>
<string name="title_activity_signup">নিবন্ধন করুন</string>
<string name="menu_about">পরিচিতি</string>
<string name="about_license" fuzzy="true">ওপেন সোর্স সফটওয়্যার &lt;a href=\"https://github.com/commons-app/apps-android-commons/blob/master/COPYING\"&gt;এ্যাপাচি লাইসেন্স v2&lt;/a&gt; অধীনে প্রকাশিত</string>
<string name="about_license">উইকিমিডিয়া কমন্স অ্যাপ হচ্ছে একটি উন্মুক্ত উৎস সম্বলিত অ্যাপ যা উইকিমিডিয়া সম্প্রদায়ের ব্যবহারকারী ও সেচ্ছাসেবকবৃন্দ কর্তৃক তৈরিকৃত এবং পরিচালিত। উইকিমিডিয়া ফাউন্ডেশন এই অ্যাপ তৈরি, উন্নয়ন বা রক্ষণাবেক্ষণে জড়িত নয়।</string>
<string name="about_improve">&lt;a href=\"https://github.com/commons-app/apps-android-commons\"&gt;উৎস&lt;/a&gt;&lt;a href=\"https://commons-app.github.io/\"&gt;ওয়েবসাইট&lt;/a&gt; GitHub এ&lt;/a&gt;। কোন সমস্যা ও পরামর্শের জন্য &lt;a href=\"https://github.com/commons-app/apps-android-commons/issues\"&gt;গিটহাব ইস্যু&lt;/a&gt; তৈরি করুন।</string>
<string name="about_privacy_policy">&lt;a href=\"https://github.com/commons-app/apps-android-commons/wiki/Privacy-policy\"&gt;গোপনীয়তার নীতি&lt;/a&gt;</string>
<string name="about_credits">&lt;a href=\"https://github.com/commons-app/apps-android-commons/blob/master/CREDITS\"&gt;কৃতিত্ব&lt;/a&gt;</string>
@ -134,6 +135,7 @@
<string name="detail_license_empty">অজানা লাইসেন্স</string>
<string name="menu_refresh">পুনঃসতেজ</string>
<string name="read_storage_permission_rationale">প্রয়োজনীয় অনুমতি: বহিঃস্ত সঞ্চয়স্থান পড়া। এটি ছাড়া অ্যাপ কাজ করবে না।</string>
<string name="write_storage_permission_rationale">অনুমতি প্রয়োজন: অালাদাভাবে সংযুক্ত স্টোরেজ লিখুন। এটি ছাড়া অ্যাপটি চলতে পারেনা।</string>
<string name="location_permission_rationale">ঐচ্ছিক অনুমতি: বিষয়শ্রেণী পরামর্শের জন্য বর্তমান অবস্থান নেয়</string>
<string name="ok">ঠিক আছে</string>
<string name="title_activity_nearby">কাছাকাছি স্থান</string>
@ -151,12 +153,15 @@
<string name="media_detail_coordinates">স্থানাঙ্কসমূহ</string>
<string name="media_detail_coordinates_empty">কিছু দেয়া হয়নি</string>
<string name="become_a_tester_title">বিটা টেস্টার হোন</string>
<string name="become_a_tester_description">গুগল প্লেতে আমাদের বেটা চ্যানেলে যুক্ত হন! এতে নতুন বৈশিষ্ট্যের পাশাপাশি পুরানো বাগগুলো\'র সংশোধিত রুপ সবার আগে ব্যবহারের সুযোগ পাবেন</string>
<string name="use_wikidata">উইকিউপাত্ত ব্যবহার করুন</string>
<string name="use_wikidata_summary">(সতর্কতা: এটি নিষ্ক্রিয় করা অধিক পরিমাণে মোবাইল ডেটা খরচ হওয়ার কারণ হতে পারে)</string>
<string name="_2fa_code">2FA কোড</string>
<string name="number_of_uploads">আমার সাম্প্রতিক আপলোড সীমা</string>
<string name="maximum_limit">সর্বোচ্চ সীমা</string>
<string name="maximum_limit_alert">৫০০টির বেশি প্রদর্শন করা সম্ভব নয়</string>
<string name="set_limit">সাম্প্রতিক আপলোড সীমা নির্ধারন করুন</string>
<string name="login_failed_2fa_not_supported">দুটি স্তরের প্রমাণীকরণ বর্তমানে সমর্থিত নয়।</string>
<string name="logout_verification">আপনি কি সত্যিই প্রস্থান করতে চান?</string>
<string name="commons_logo">কমন্সের লোগো</string>
<string name="background_image">পটভূমির চিত্র</string>
@ -187,7 +192,13 @@
<string name="no_description_found">কোন বিবরণ পাওয়া যায়নি</string>
<string name="nearby_info_menu_commons_article">কমন্সে ফাইলের পাতা</string>
<string name="nearby_info_menu_wikidata_article">উইকিউপাত্ত পদ</string>
<string name="error_while_cache">ছবি আনার সময় ত্রুটি</string>
<string name="title_info">ফাইলের একটি স্বতন্ত্র বর্ণনামূলক নাম যা ফাইলের নাম হিসাবে কাজ করবে। অাপনি সাধারণ ভাষা ব্যবহার করতে পারেন শূন্যস্থানসহ। ফাইলের এক্সটেনশন যুক্ত করবেন না।</string>
<string name="description_info">যতটা সম্ভব মিডিয়াটি বর্ণনা করুন: এটি কোথায় ধারণ করা হয়েছিল? এটি কি প্রদর্শন করে? এটির প্রসঙ্গ কি? ধারণকৃত বস্তু অথবা ব্যক্তির বর্ণনা করুন। সহজে অনুমান করা যায়না সেরকম তথ্য উদঘাটন করুন, উদাহরণস্বরূপ, যদি ল্যান্ডস্কেপ হয় তাহলে দিবসকালের সময় দিন।</string>
<string name="give_permission">অনুমতি দিন</string>
<string name="use_external_storage">বাহ্যিক সঞ্চয়স্থান ব্যবহার করুন</string>
<string name="use_external_storage_summary">অাপনার ডিভাইসের নিজস্ব ক্যামেরায় ধারণকৃত ছবি সংরক্ষণ করুন</string>
<string name="send_log_file">লগ ফাইল পাঠান</string>
<string name="send_log_file_description">ইমেইলের মাধ্যমে উন্নয়নকারীর কাছে লগ ফাইল পাঠান</string>
<string name="login_to_your_account">আপনার অ্যাকাউন্টে প্রবেশ করুন</string>
</resources>

View file

@ -20,11 +20,11 @@
<string name="upload_progress_notification_title_finishing">Acabant la càrrega al servidor de %1$s</string>
<string name="upload_failed_notification_title">Error al carregar %1$s</string>
<string name="upload_failed_notification_subtitle">Prem per veure-ho</string>
<plurals name="uploads_pending_notification_indicator" fuzzy="true">
<plurals name="uploads_pending_notification_indicator">
<item quantity="one">s\'està carregant 1 fitxer</item>
<item quantity="other">s\'estan carregant %d fitxers</item>
</plurals>
<string name="title_activity_contributions" fuzzy="true">Les meves càrregues</string>
<string name="title_activity_contributions">Les meves càrregues recents</string>
<string name="contribution_state_queued">En cua</string>
<string name="contribution_state_failed">Ha fallat</string>
<string name="contribution_state_in_progress">%1$d%% completat</string>
@ -42,6 +42,7 @@
<string name="login_failed_password">No sha pogut iniciar la sessió. Comproveu la vostra contrasenya</string>
<string name="login_failed_throttled">Massa intents erronis Proveu-ho de nou d\'aquí uns minuts.</string>
<string name="login_failed_blocked">Ho sentim, aquest usuari ha estat blocat a Commons</string>
<string name="login_failed_2fa_needed">Heu de proporcionar el vostre codi d\'autenticació de dos factors.</string>
<string name="login_failed_generic">Ha fallat l\'inici de sessió</string>
<string name="share_upload_button">Carrega</string>
<string name="multiple_share_base_title">Anomena aquest conjunt</string>
@ -50,17 +51,19 @@
<string name="categories_search_text_hint">Categories de cerca</string>
<string name="menu_save_categories">Desa</string>
<string name="refresh_button">Refresca</string>
<string name="gps_disabled">El vostre dispositiu no té el GPS habilitat. El voleu habilitar?</string>
<string name="enable_gps">Habilita el GPS</string>
<string name="contributions_subtitle_zero">Encara no hi ha cap càrrega</string>
<plurals name="contributions_subtitle" fuzzy="true">
<item quantity="zero">No hi ha cap càrrega encara</item>
<item quantity="one">1 càrrega</item>
<plurals name="contributions_subtitle">
<item quantity="zero">\@string/contributions_subtitle_zero</item>
<item quantity="one">%d càrrega</item>
<item quantity="other">%d càrregues</item>
</plurals>
<plurals name="starting_multiple_uploads" fuzzy="true">
<item quantity="one">S\'està iniciant 1 càrrega</item>
<plurals name="starting_multiple_uploads">
<item quantity="one">S\'està iniciant %d càrrega</item>
<item quantity="other">S\'estan iniciant %d càrregues</item>
</plurals>
<plurals name="multiple_uploads_title" fuzzy="true">
<plurals name="multiple_uploads_title">
<item quantity="one">1 càrrega</item>
<item quantity="other">%d càrregues</item>
</plurals>
@ -85,8 +88,10 @@
<string name="preference_license">Llicència</string>
<string name="preference_theme">Mode nocturn</string>
<string name="preference_theme_summary">Utilitza el tema fosc</string>
<string name="license_name_cc_by_sa" fuzzy="true">CC Reconeixement-CompartirIgual 3.0</string>
<string name="license_name_cc_by" fuzzy="true">CC Reconeixement 3.0</string>
<string name="license_name_cc_by_sa_four">Reconeixement-CompartirIgual 4.0</string>
<string name="license_name_cc_by_four">Reconeixement 4.0</string>
<string name="license_name_cc_by_sa">Reconeixement-CompartirIgual 3.0</string>
<string name="license_name_cc_by">Reconeixement 3.0</string>
<string name="license_name_cc0">CC0</string>
<string name="license_name_cc_by_sa_3_0">CC BY-SA 3.0</string>
<string name="license_name_cc_by_sa_3_0_at">CC BY-SA 3.0 (Àustria)</string>
@ -100,7 +105,10 @@
<string name="license_name_cc_by_sa_3_0_pl">CC BY-SA 3.0 (Polònia)</string>
<string name="license_name_cc_by_sa_3_0_ro">CC BY-SA 3.0 (Romania)</string>
<string name="license_name_cc_by_3_0">CC BY 3.0</string>
<string name="license_name_cc_by_sa_4_0">CC BY-SA 4.0</string>
<string name="license_name_cc_by_4_0">CC BY 4.0</string>
<string name="license_name_cc_zero">CC Zero</string>
<string name="tutorial_3_text">NO carregueu:</string>
<string name="tutorial_4_text">Exemple de càrrega:</string>
<string name="welcome_wikipedia_text">Doneu les vostres imatges. Ajudeu a donar vida als articles de la Viquipèdia!</string>
<string name="welcome_wikipedia_subtext">Les imatges de la Viquipèdia vénen de la Wikimedia Commons.</string>
@ -116,14 +124,20 @@
<string name="menu_refresh">Refresca</string>
<string name="ok">D\'acord</string>
<string name="title_activity_nearby">Llocs propers</string>
<string name="no_nearby">No s\'han trobat llocs propers</string>
<string name="warning">Avís</string>
<string name="file_exists">El fitxer ja existeix a Commons. Segur que voleu procedir?</string>
<string name="yes"></string>
<string name="no">No</string>
<string name="media_detail_title">Títol</string>
<string name="media_detail_media_title">Títol del fitxer multimèdia</string>
<string name="media_detail_description">Descripció</string>
<string name="media_detail_uploaded_date">Data de càrrega</string>
<string name="media_detail_license">Llicència</string>
<string name="media_detail_coordinates">Coordenades</string>
<string name="media_detail_coordinates_empty">No s\'ha proporcionat cap</string>
<string name="use_wikidata">Utilitza Wikidata</string>
<string name="_2fa_code">Codi 2FA</string>
<string name="maximum_limit">Límit màxim</string>
<string name="logout_verification">Realment voleu finalitzar la sessió?</string>
<string name="commons_logo">Logo de Commons</string>
@ -142,6 +156,6 @@
<string name="navigation_item_feedback">Comentaris</string>
<string name="navigation_item_logout">Finalitza la sessió</string>
<string name="no_description_found">no s\'ha trobat cap descripció</string>
<string name="nearby_info_menu_commons_article" fuzzy="true">Article al Commons</string>
<string name="nearby_info_menu_commons_article">Article del fitxer a Commons</string>
<string name="nearby_info_menu_wikidata_article">Element del Wikidata</string>
</resources>

View file

@ -198,5 +198,7 @@
<string name="give_permission">Giv tilladelse</string>
<string name="use_external_storage">Brug eksternt lager</string>
<string name="use_external_storage_summary">Gem billeder taget med din enheds program på kameraet</string>
<string name="send_log_file">Send logfil</string>
<string name="send_log_file_description">Send logfil til udviklerne via e-post</string>
<string name="login_to_your_account">Log ind på din konto</string>
</resources>

View file

@ -198,5 +198,7 @@
<string name="give_permission">Berechtigung geben</string>
<string name="use_external_storage">Externen Speicher verwenden</string>
<string name="use_external_storage_summary">Mit der In-App-Kamera aufgenommene Bilder auf deinem Gerät speichern</string>
<string name="send_log_file">Logdatei senden</string>
<string name="send_log_file_description">Logdatei an die Entwickler per E-Mail senden</string>
<string name="login_to_your_account">Bei deinem Benutzerkonto anmelden</string>
</resources>

View file

@ -20,11 +20,11 @@
<string name="upload_progress_notification_title_finishing">Αποπεράτωση επιφόρτωσης %1$s</string>
<string name="upload_failed_notification_title">Ανέβασμα του %1$s απέτυχε</string>
<string name="upload_failed_notification_subtitle">Πατήστε για να δείτε</string>
<plurals name="uploads_pending_notification_indicator" fuzzy="true">
<plurals name="uploads_pending_notification_indicator">
<item quantity="one">1 αρχείο επιφορτώνεται</item>
<item quantity="other">%d αρχεία επιφορτώνονται</item>
</plurals>
<string name="title_activity_contributions" fuzzy="true">Οι επιφορτώσεις μου</string>
<string name="title_activity_contributions">Οι Πρόσφατες Φορτώσεις Μου</string>
<string name="contribution_state_queued">Στην ουρά</string>
<string name="contribution_state_failed">Απέτυχε</string>
<string name="contribution_state_in_progress">%1$d%% ολοκληρώθηκε</string>
@ -42,6 +42,7 @@
<string name="login_failed_password">Δεν είναι δυνατή η σύνδεση - παρακαλούμε ελέγξτε τον κωδικό σας</string>
<string name="login_failed_throttled">Πάρα πολλές ανεπιτυχείς προσπάθειες. Παρακαλώ δοκιμάστε ξανά σε λίγα λεπτά.</string>
<string name="login_failed_blocked">Συγνώμη, αυτός ο χρήστης έχει αποκλειστεί από τα Commons</string>
<string name="login_failed_2fa_needed">Πρέπει να δώσετε τον κωδικό πιστοποίησης με δύο παράγοντες</string>
<string name="login_failed_generic">Η είσοδος απέτυχε</string>
<string name="share_upload_button">Ανέβασμα</string>
<string name="multiple_share_base_title">Ονομάστε το σύνολο</string>
@ -50,6 +51,9 @@
<string name="categories_search_text_hint">Αναζήτηση κατηγοριών</string>
<string name="menu_save_categories">Αποθήκευση</string>
<string name="refresh_button">Ανανέωση</string>
<string name="gps_disabled">Το GPS στην συσκευή είναι απενεργοποιημένο. Θέλετε να το ενεργοποιήσετε;</string>
<string name="enable_gps"> Ενεργοποιήσετε το GPS</string>
<string name="contributions_subtitle_zero">Δεν υπάρχουν ακόμα φορτωμένα αρχεία</string>
<plurals name="contributions_subtitle" fuzzy="true">
<item quantity="zero">Δεν υπάρχουν επιφορτώσεις ακόμη</item>
<item quantity="one">1 επιφόρτωση</item>
@ -69,9 +73,9 @@
<string name="title_activity_settings">Ρυθμίσεις</string>
<string name="title_activity_signup">Εγγραφή</string>
<string name="menu_about">Σχετικά</string>
<string name="about_license" fuzzy="true">Λογισμικό ανοικτού κώδικα και κυκλοφορεί υπό την &lt;a href=\"https://github.com/commons-app/apps-android-commons/blob/master/COPYING\"&gt;Άδεια Apache v2&lt;/a&gt;. Το Wikimedia Commons και το λογότυπο είναι εμπορικά σήματα του Ιδρύματος Wikimedia και χρησιμοποιούνται με άδεια από το Ίδρυμα Wikimedia. Δεν προτεινόμαστε ή συνδεόμαστε με το Ίδρυμα Wikimedia.</string>
<string name="about_license">Λογισμικό ανοικτού κωδικού που κυκλοφορεί υπό την &lt;a href=\"https://github.com/commons-app/apps-android-commons/blob/master/COPYING\"&gt;Άδεια Apache v2&lt;/a&gt;. Το Wikimedia Commons και το λογότυπο είναι εμπορικά σήματα του Ιδρύματος Wikimedia και χρησιμοποιούνται με άδεια από το Ίδρυμα Wikimedia. Δεν συμμετέχουμε στην δημιουργία, ανάπτυξη ή συντήρηση του Ιδρύματος Wikimedia.</string>
<string name="about_improve">&lt;a href=\"https://github.com/commons-app/apps-android-commons\"&gt;Πηγή&lt;/a&gt; και &lt;a href=\"https://commons-app.github.io/\"&gt;ιστοσελίδα&lt;/a&gt; στο GitHub. Δημιουργήστε ένα νέο &lt;a href=\"https://github.com/commons-app/apps-android-commons/issues\"&gt;GitHub θέμα&lt;/a&gt; για αναφορές σφαλμάτων και προτάσεις.</string>
<string name="about_privacy_policy" fuzzy="true">&lt;a href=\"https://wikimediafoundation.org/wiki/Privacy_policy\"&gt;Πολιτική προσωπικών δεδομένων&lt;/a&gt;</string>
<string name="about_privacy_policy">&lt;a href=\"https://wikimediafoundation.org/wiki/Privacy_policy\"&gt;Πολιτική Απορρήτου και προσωπικών δεδομένων&lt;/a&gt;</string>
<string name="about_credits" fuzzy="true">&lt;a href=\"https://github.com/commons-app/apps-android-commons/blob/master/CREDITS\"&gt;CREDITS&lt;/a&gt;</string>
<string name="title_activity_about">Σχετικά</string>
<string name="menu_feedback">Αποστολή σχολίων (μέσω Email)</string>
@ -82,6 +86,7 @@
<string name="menu_retry_upload">Ξαναπροσπαθήστε</string>
<string name="menu_cancel_upload">Ακύρωση</string>
<string name="share_license_summary">Αυτή η εικόνα θα έχει άδεια στα πλαίσια του %1$s</string>
<string name="media_upload_policy">Αποστέλλοντας αυτήν την εικόνα, δηλώνω πως αυτή η εργασία είναι δική μου, και δεν περιέχει υλικό άλλου συγγραφέα, και εκτός αυτού πρόσκειται στο , thathref=\"https://commons.wikimedia.org/wiki/Commons:Policies_and_guidelines\"&gt;Κοινές πολιτικές της Wikipedia&lt;/a&gt;.</string>
<string name="menu_download">Λήψη</string>
<string name="preference_license">Άδεια χρήσης</string>
<string name="use_previous">Χρήση προηγούμενου τίτλου/περιγραφής</string>
@ -124,15 +129,17 @@
<string name="welcome_final_text">Νομίζεις ότι έχεις;</string>
<string name="welcome_final_button_text">Ναι!</string>
<string name="detail_panel_cats_label">Κατηγορίες</string>
<string name="detail_panel_cats_loading" fuzzy="true">Φόρτωση…</string>
<string name="detail_panel_cats_loading">Φόρτωση…</string>
<string name="detail_panel_cats_none">Καμία επιλεγμένη</string>
<string name="detail_description_empty">Καμία περιγραφή</string>
<string name="detail_license_empty">Άγνωστη άδεια</string>
<string name="menu_refresh">Ανανέωση</string>
<string name="read_storage_permission_rationale">Απαιτούμενη άδεια: Ανάγνωση εξωτερικής αποθήκευσης. Η εφαρμογή δεν μπορεί να λειτουργήσει χωρίς αυτή.</string>
<string name="write_storage_permission_rationale">Απαιτούμενη άδεια: Με εξωτερική αποθήκευση.Το πρόγραμμα δεν μπορεί να λειτουργήσει με αυτήν.</string>
<string name="location_permission_rationale">Προαιρετική άδεια: Ανάκτηση τρέχουσας θέσης σας για προτάσεις κατηγοριών</string>
<string name="ok">Εντάξει</string>
<string name="title_activity_nearby">Κοντινοί Τόποι</string>
<string name="no_nearby">Δεν βρέθηκαν τόποι εδώ κοντά</string>
<string name="warning">Προειδοποίηση</string>
<string name="file_exists">Αυτό το αρχείο υπάρχει ήδη στα Commons. Είστε σίγουρος ότι θέλετε να συνεχίσετε;</string>
<string name="yes">Ναι</string>
@ -141,8 +148,55 @@
<string name="media_detail_media_title">Τίτλος πολυμέσου</string>
<string name="media_detail_description">Περιγραφή</string>
<string name="media_detail_description_explanation">Η περιγραφή του πολυμέσου μπαίνει εδώ. Αυτή μπορεί να είναι σχετικά μεγάλη, και θα χρειαστεί να αναδιπλωθεί σε πολλές γραμμές. Ελπίζουμε ωστόσο ότι θα φαίνεται όμορφα.</string>
<string name="media_detail_uploaded_date">Ημερομηνία φόρτωσης</string>
<string name="media_detail_license">Άδεια</string>
<string name="media_detail_coordinates">Συντεταγμένες</string>
<string name="media_detail_coordinates_empty">Δεν δόθηκε τίποτα</string>
<string name="become_a_tester_title">Γίνετε Δοκιμαστής Beta</string>
<string name="become_a_tester_description">Συμμετέχετε στο κανάλι beta μας στο Google Play και αποκτήστε πρώιμη πρόσβαση σε νέες λειτουργίες και διορθώσεις σφαλμάτων</string>
<string name="use_wikidata">χρήση wikidata</string>
<string name="use_wikidata_summary">(Προσοχή: η απενεργοποίηση αυτή μπορεί να προκαλέσει μεγάλη κατανάλωση κινητὠν δεδομένων)</string>
<string name="_2fa_code">Κωδικός 2FA</string>
<string name="number_of_uploads">To Όριο του Πρόσφατα Φορτωμένου Αρχείου μου</string>
<string name="maximum_limit">Μέγιστο Όριο</string>
<string name="maximum_limit_alert">Δεν μπορεί να προβάλλει περισσότερα από 500 αρχεία</string>
<string name="set_limit">Ορίσετε το Όριο της Πρόσφατης Επιφόρτωσης</string>
<string name="login_failed_2fa_not_supported">Πιστοποίηση με δύο παράγοντες δεν υποστηρίζεται προς το παρόν.</string>
<string name="logout_verification">Θέλετε πράγματι να αποσυνδεθείτε;</string>
<string name="commons_logo">Κοινό Λογότυπο</string>
<string name="background_image">Εικόνα Υποβάθρου</string>
<string name="mediaimage_failed">Η Εικόνα των Μέσων Απέτυχε (δεν μπορεί να φορτωθεί)</string>
<string name="no_image_found">Δεν Βρέθηκε καμία Εικόνα</string>
<string name="upload_image">Φορτώστε την Εικόνα</string>
<string name="welcome_image_mount_zao">Mount Zao</string>
<string name="welcome_image_llamas">Llamas</string>
<string name="welcome_image_rainbow_bridge">Γέφυρα Ουρανίου Τόξου</string>
<string name="welcome_image_tulip">Τουλίπα</string>
<string name="welcome_image_proprietary">Ιδιόκτητη Εικόνα</string>
<string name="welcome_image_welcome_wikipedia"> Καλωσόρισες Βικιπαίδεια</string>
<string name="welcome_image_welcome_copyright">Καλωσορίστε το Δικαίωμα Αντιγραφής</string>
<string name="welcome_image_sydney_opera_house">Κτίρια Όπερας Sidney</string>
<string name="cancel">Ακυρώστε</string>
<string name="navigation_drawer_open">Ανοίξετε</string>
<string name="navigation_drawer_close">Κλείσετε</string>
<string name="navigation_item_home">Αρχική Σελίδα</string>
<string name="navigation_item_upload">Φορτώστε</string>
<string name="navigation_item_nearby">Εδώ Κοντά</string>
<string name="navigation_item_about">Γύρω από</string>
<string name="navigation_item_settings">Ρυθμίσεις</string>
<string name="navigation_item_feedback">Σχόλια</string>
<string name="navigation_item_logout">Αποσύνδεση</string>
<string name="navigation_item_info">Σεμινάριο</string>
<string name="nearby_needs_permissions">Οι κοντινές τοποθεσίες δεν μπορούν να προβληθούν δίχως τις άδειες τοποθεσίας</string>
<string name="no_description_found">δεν βρέθηκε περιγραφή</string>
<string name="nearby_info_menu_commons_article">Σελίδα φακέλλου κοινής χρήσης</string>
<string name="nearby_info_menu_wikidata_article">Τεμάχιο Wikidata</string>
<string name="error_while_cache">Υπήρξε σφάλμα κατά την σκίαση εικόνων</string>
<string name="title_info">Ένας μοναδικός τίτλος περιγραφής του φακέλλου, που θα χρησιμεύσει ως όνομα φακέλλου. Μπορείτε να χρησιμοποιήσετε τις ήδη υπάρχουσες γλώσσες με διαστήματα. Μην συμπεριλάβετε την επέκταση φακέλλου</string>
<string name="description_info">\nΠαρακαλώ περιγράψετε τα μέσα το δυνατό περισσότερο : Πού οδηγήθηκε αυτό; Τι δείχνει; Ποιο είναι το περιεχόμενο του; Παρακαλώ περιγράψετε τα αντικείμενα ή τα πρόσωπα. Αποκαλύψετε πληροφορίες που δεν μπορούν εύκολο να μαντέψει κανείς, για παράδειγμα την ώρα εντός της ημέρας αν πρόκειται για τοπίο. Αν τα μέσα δείξουν κάτι ασύνηθες, παρακαλώ εξηγήστε τι το καθιστά μη συνηθισμένα.</string>
<string name="give_permission">Χορηγήστε άδεια</string>
<string name="use_external_storage">Χρησιμοποιήσετε την εξωτερική αποθήκευση</string>
<string name="use_external_storage_summary">Αποθηκεύσετε εικόνες που παίρνονται στην κάμερα εφαρμογής στην συσκευή σας</string>
<string name="send_log_file">Αποστείλατε τον φάκελλο σύνδεσης</string>
<string name="send_log_file_description">Στείλατε τον φάκελλο σύνδεσης στους δημιουργούς μέσω email</string>
</resources>

View file

@ -198,5 +198,7 @@
<string name="give_permission">Otorgar permiso</string>
<string name="use_external_storage">Utilizar almacenamiento externo</string>
<string name="use_external_storage_summary">Guardar en el dispositivo imágenes capturadas con la cámara de la aplicación</string>
<string name="send_log_file">Enviar archivo de registro</string>
<string name="send_log_file_description">Enviar archivo de registro a los desarrolladores por correo electrónico</string>
<string name="login_to_your_account">Accede a tu cuenta</string>
</resources>

View file

@ -198,5 +198,7 @@
<string name="give_permission">اجازه بده</string>
<string name="use_external_storage">استفاده از حافظهٔ خارجی</string>
<string name="use_external_storage_summary">ذخیرهٔ تصویرهای گرفته شده توسط دوربین درونکار اپلیکیشن بر روی دستگاه شما</string>
<string name="send_log_file">ارسال فایل سیاهه</string>
<string name="send_log_file_description">ارسال فایل سیاهه به‌وسیلهٔ ایمیل برای توسعه‌دهندگان</string>
<string name="login_to_your_account">ورود به حساب کاربریتان</string>
</resources>

View file

@ -73,7 +73,7 @@
<string name="title_activity_settings">Paramètres</string>
<string name="title_activity_signup">Sinscrire</string>
<string name="menu_about">À propos</string>
<string name="about_license">L\'application Wikimedia Commons est une application open-source crée et maintenue par les bénéficiaires et volontaires de la communauté Wikimedia. La fondation Wikimedia n\'est pas associée à la création, le développement ou la maintenance de l\'application.</string>
<string name="about_license">Lapplication Wikimedia Commons est une application ouverte créée et tenue à jour par les bénéficiaires et volontaires de la communauté Wikimedia. La fondation Wikimedia nest pas associée à la création, le développement ou lentretien de lapplication.</string>
<string name="about_improve">&lt;a href=\"https://github.com/commons-app/apps-android-commons\"&gt;Sources&lt;/a&gt; et &lt;a href=\"https://commons-app.github.io/\"&gt;site web&lt;/a&gt; sur GitHub. Créer un nouveau &lt;a href=\"https://github.com/commons-app/apps-android-commons/issues\"&gt;signalement GitHub&lt;/a&gt; pour signales des bogues ou des suggestions.</string>
<string name="about_privacy_policy">&lt;a href=\"https://github.com/commons-app/apps-android-commons/wiki/Privacy-policy\"&gt;Politique de confidentialité&lt;/a&gt;</string>
<string name="about_credits">&lt;a href=\"https://github.com/commons-app/apps-android-commons/blob/master/CREDITS\"&gt;Remerciements&lt;/a&gt;</string>
@ -198,5 +198,7 @@
<string name="give_permission">Accorder le droit</string>
<string name="use_external_storage">Utiliser le stockage externe</string>
<string name="use_external_storage_summary">Enregistrer les images prises avec lappareil photo de votre appareil</string>
<string name="send_log_file">Envoyer le journal</string>
<string name="send_log_file_description">Envoyer le journal aux développeurs par courriel</string>
<string name="login_to_your_account">Connectez-vous à votre compte</string>
</resources>

View file

@ -198,5 +198,7 @@
<string name="give_permission">Outorgar permiso</string>
<string name="use_external_storage">Usar o almacenamento externo</string>
<string name="use_external_storage_summary">Gardar as imaxes capturadas coa cámara do seu dispositivo</string>
<string name="send_log_file">Enviar ficheiro de rexistro</string>
<string name="send_log_file_description">Enviar ficheiro de rexistro ós desenvolvedores por correo electrónico</string>
<string name="login_to_your_account">Comezar sesión na súa conta</string>
</resources>

View file

@ -75,7 +75,7 @@
<string name="menu_about">Névjegy</string>
<string name="about_license">A Wikimedia Commons applikáció egy nyílt forráskódú szoftver, amit a Wikimedia-közösség önkéntesei készítettek és tartanak karban. A Wikimédia Alapítvány nem vesz részt az applikáció megalkotásában, fejlesztésében és üzemeltetésében.</string>
<string name="about_improve">&lt;a href=\"https://github.com/commons-app/apps-android-commons\"&gt;Forráskód&lt;/a&gt; és &lt;a href=\"https://commons-app.github.io/\"&gt;weboldal&lt;/a&gt; a GitHubon. Nyiss egy új &lt;a href=\"https://github.com/commons-app/apps-android-commons/issues\"&gt;GitHub-problémát&lt;/a&gt; hibabejelentéssel vagy fejlesztési javaslattal.</string>
<string name="about_privacy_policy" fuzzy="true">&lt;a href=\"https://wikimediafoundation.org/wiki/Adatvédelmi_irányelv\"&gt;Adatvédelmi irányelvek&lt;/a&gt;</string>
<string name="about_privacy_policy">&lt;a href=\"https://github.com/commons-app/apps-android-commons/wiki/Privacy-policy\"&gt;Adatvédelmi irányelvek&lt;/a&gt;</string>
<string name="about_credits">&lt;a href=\"https://github.com/commons-app/apps-android-commons/blob/master/CREDITS\"&gt;Köszönetnyilvánítás&lt;/a&gt;</string>
<string name="title_activity_about">Névjegy</string>
<string name="menu_feedback">Visszajelzés küldése (e-mailben)</string>
@ -190,6 +190,7 @@
<string name="no_description_found">nincs leírás</string>
<string name="nearby_info_menu_commons_article">Commons leírólap</string>
<string name="nearby_info_menu_wikidata_article">Wikidata-elem</string>
<string name="title_info">Egy egyedi, leíró cím a fájlnak, ami fájlnévként fog szolgálni. Egyszerű nyelvezetet használhatsz szóközökkel. Ne tedd bele a kiterjesztést.</string>
<string name="description_info">Kérlek a lehető legteljesebb módon írd le a fájlt: hol készült, mit ábrázol, mi a kontextus? Kérlek add meg az objektumokat vagy személyeket a képen, valamint a nehezen kitalálható információkat (például a kép készítésének dátumát, ha az egy tájkép). Amennyiben a média valami szokatlant ábrázol, kérlek fejtsd ki, hogy mi teszi szokatlanná.</string>
<string name="give_permission">Engedély adása</string>
<string name="use_external_storage">Külső tárhely használata</string>

View file

@ -72,9 +72,9 @@
<string name="title_activity_settings">הגדרות</string>
<string name="title_activity_signup">רישום</string>
<string name="menu_about">אודות</string>
<string name="about_license" fuzzy="true">תכנת קוד פתוח המתפרסמת לפי תנאי &lt;a href=\"https://github.com/commons-app/apps-android-commons/blob/master/COPYING\"&gt;Apache License v2&lt;/a&gt;. השם %1$s והסמל שמשויך אליו הם סימני מסחר של קרן ויקימדיה ומשמשים באישור קרן ויקימדיה. איננו נתמכים על־ידי קרן ויקימדיה או קשורים אליה בשותפות.</string>
<string name="about_license">יישום ויקישיתוף (Wikimedia Commons app) הוא יישום קוד פתוח שמפותח ומתוחזק על־ידי מקבלי מלגות ומתנדבים של קהילת ויקימדיה. קרן ויקימדיה אינה מעורבת ביצירה, פיתוח, או תחזוקה של היישום.</string>
<string name="about_improve">&lt;a href=\"https://github.com/commons-app/apps-android-commons\"&gt;קוד מקור&lt;/a&gt; ו&lt;a href=\"https://commons-app.github.io/\"&gt;אתר&lt;/a&gt; בגיטהאב. נא ליצור &lt;a href=\"https://github.com/commons-app/apps-android-commons/issues\"&gt;דיווח בגיטהאב&lt;/a&gt; בשביל באגים והצעות.</string>
<string name="about_privacy_policy" fuzzy="true"/>
<string name="about_privacy_policy">&lt;a href=\"https://github.com/commons-app/apps-android-commons/wiki/Privacy-policy\"&gt;מדיניות פרטיות&lt;/a&gt;</string>
<string name="about_credits">&lt;a href=\"https://github.com/commons-app/apps-android-commons/blob/master/CREDITS\"&gt;יוצרים&lt;/a&gt;</string>
<string name="title_activity_about">אודות</string>
<string name="menu_feedback">שליחת משוב (בדוא\"ל)</string>
@ -172,6 +172,7 @@
<string name="welcome_image_tulip">צבעוני</string>
<string name="welcome_image_no_selfies">בלי תמונות סלפי</string>
<string name="welcome_image_proprietary">תמונה קניינית</string>
<string name="welcome_image_welcome_wikipedia">ברוך בואך ויקיפדיה</string>
<string name="welcome_image_sydney_opera_house">בית האופרה של סידני</string>
<string name="cancel">ביטול</string>
<string name="navigation_drawer_open">פתיחה</string>
@ -191,6 +192,10 @@
<string name="error_while_cache">שגיאה במשירת תמונות במטמון</string>
<string name="title_info">כותרת מתארת ייחודית לקובץ, שתשמש שם קובץ. אפשר להשתמש בשפה פשוטה עם רווחים. אין לכלול סיומת קובץ</string>
<string name="description_info">נא לתאר את המדיה כמה שיותר: איפה היא נוצרה? מה היא מראה? מה ההקשר? נא לתאר את העצמים או את האנשים. נא לחשוף מידע שאי־אפשר לנחש בקלות, למשל, הזמן ביום אם זאת תמונת נוף. אם המדיה מציגה משהו בלתי־רגיל, נא להסביר מה מיוחד בה.</string>
<string name="give_permission">לתת הרשאה</string>
<string name="use_external_storage">להשתמש באחסון חיצוני</string>
<string name="use_external_storage_summary">שמירת תמונות שצולמו באמצעות מצלמה בתוך היישום במכשיר שלך</string>
<string name="send_log_file">שליחת קובץ יומן</string>
<string name="send_log_file_description">שליחת קובץ יומן למפתחים בדואר אלקטרוני</string>
<string name="login_to_your_account">כניסה לחשבון שלך</string>
</resources>

View file

@ -198,5 +198,7 @@
<string name="give_permission">Mudd tasiregt</string>
<string name="use_external_storage">Seqdec asekles azɣaray</string>
<string name="use_external_storage_summary">Sekles tiwlafin yettwaṭṭfen s tkamirat yellan deg ibenk</string>
<string name="send_log_file">Azen afaylu n uɣmis</string>
<string name="send_log_file_description">Azen afaylu n uɣmis i yinermisen s yimayl</string>
<string name="login_to_your_account">Qqen ar umiḍan-ik</string>
</resources>

View file

@ -72,7 +72,7 @@
<string name="title_activity_settings">설정</string>
<string name="title_activity_signup">가입하기</string>
<string name="menu_about">정보</string>
<string name="about_license" fuzzy="true">오픈 소스 소프트웨어는 &lt;a href=\"https://github.com/commons-app/apps-android-commons/blob/master/COPYING\"&gt;아파치 라이선스 v2&lt;/a&gt;에 따라 공개됩니다. %1$s 및 관련 로고는 위키미디어 재단의 상표이며 위키미디어 재단의 허가를 통해 사용될 수 있습니다. 저희는 위키미디어 재단에 의해 보증되거나 제휴되어 있지 않습니다.</string>
<string name="about_license">위키미디어 공용 앱은 오픈 소스 애플리케이션이며 위키미디어 공동체 내의 자원봉사자에 의해 유지됩니다. 위키미디어 재단은 애플리케이션의 생성, 개발, 유지보수에 관여하지 않습니다.</string>
<string name="about_improve">소스 코드는 &lt;a href=\"https://github.com/commons-app/apps-android-commons\"&gt;GitHub&lt;/a&gt;에 있으며, 웹사이트는 &lt;a href=\"https://commons-app.github.io/\"&gt;GitHub&lt;/a&gt;에 있습니다. 버그나 기타 제안은 &lt;a href=\" https://github.com/commons-app/apps-android-commons/issues\"&gt;GitHub&lt;/a&gt;에 보고해주세요.</string>
<string name="about_privacy_policy">&lt;a href=\"https://github.com/commons-app/apps-android-commons/wiki/Privacy-policy\"&gt;개인정보 정책&lt;/a&gt;</string>
<string name="about_credits">&lt;a href=\"https://github.com/commons-app/apps-android-commons/blob/master/CREDITS\"&gt;제작진&lt;/a&gt;</string>
@ -195,5 +195,7 @@
<string name="give_permission">권한 부여</string>
<string name="use_external_storage">외부 저장소 사용하기</string>
<string name="use_external_storage_summary">장치의 인앱 카메라로 찍은 사진 저장하기</string>
<string name="send_log_file">로그 파일 보내기</string>
<string name="send_log_file_description">이메일로 개발자에게 로그 파일 보내기</string>
<string name="login_to_your_account">자신의 계정으로 로그인</string>
</resources>

View file

@ -72,7 +72,7 @@
<string name="title_activity_settings">Astellungen</string>
<string name="title_activity_signup">Mellt Iech un</string>
<string name="menu_about">Iwwer</string>
<string name="about_license" fuzzy="true">\'Open-Source-Software\' verëffentlecht ënner der &lt;a href=\"https://github.com/commons-app/apps-android-commons/blob/master/COPYING\"&gt;Apache Lizenz v2&lt;/a&gt;. %1$s a säi Logo si Markenzeeche vun der Wikimedia Foundation a gi mat der Autorisatioun vun der Wikimedia Foundation benotzt. Mir sinn net confirméiert vun oder liéiert mat der Wikimedia Foundation.</string>
<string name="about_license">D\'App Wikimedia Commons ass eng \'Open-Source-App\' déi vu Fräiwëllege vun der Wikimedia Foundation entwéckelt gouf an och vun hinnen ënnerhal gëtt. D\'Wikimedia Foundation ass net an d\'Entwécklung oder den Ënnerhalt vun der App implizéiert.</string>
<string name="about_improve">&lt;a href=\"https://github.com/commons-app/apps-android-commons\"&gt;Quell&lt;/a&gt; an &lt;a href=\"https://commons-app.github.io/\"&gt;Internetsite&lt;/a&gt; vu GitHub.\nLeet w.e.g. &lt;a href=\"https://github.com/commons-app/apps-android-commons/issues\"&gt; e GitHub Problem&lt;/a&gt; fir Problemer ze mellen a Proposen ze maachen.</string>
<string name="about_privacy_policy">&lt;a href=\"https://github.com/commons-app/apps-android-commons/wiki/Privacy-policy\"&gt;Dateschutzerklärung&lt;/a&gt;</string>
<string name="about_credits">&lt;a href=\"https://github.com/commons-app/apps-android-commons/blob/master/CREDITS\"&gt;Merci&lt;/a&gt;</string>
@ -188,5 +188,7 @@
<string name="give_permission">Autorisatioun ginn</string>
<string name="use_external_storage">Externe Späicher benotzen</string>
<string name="use_external_storage_summary">Biller späicheren déi mat der in-app Kamera vun Ärem Apparat gemaach goufen</string>
<string name="send_log_file">Log-Fichier schécken</string>
<string name="send_log_file_description">Log-Fichier per E-Mail un d\'Entwéckler schécken</string>
<string name="login_to_your_account">An Äre Benotzerkont aloggen</string>
</resources>

View file

@ -42,8 +42,8 @@
<string name="title_activity_settings">Iestatījumi</string>
<string name="title_activity_signup">Reģistrēties</string>
<string name="menu_about">Par</string>
<string name="about_improve" fuzzy="true">Izejas kods pieejams &lt;a href=\"https://github.com/commons-app/apps-android-commons\"&gt;GitHub&lt;/a&gt;. Kļūdas ziņot &lt;a href=\" https://github.com/commons-app/apps-android-commons/issues\"&gt;Github&lt;/a&gt;.</string>
<string name="about_privacy_policy" fuzzy="true">&lt;a href=\"https://wikimediafoundation.org/wiki/Privacy_policy\"&gt;Privātuma politika&lt;/a&gt;</string>
<string name="about_improve">&lt;a href=\"https://github.com/commons-app/apps-android-commons\"&gt;Izejas kods&lt;/a&gt; un &lt;a href=\"https://commons-app.github.io/\"&gt;tīmekļa vietne&lt;/a&gt; GitHub. Izveido jaunu &lt;a href=\"https://github.com/commons-app/apps-android-commons/issues\"&gt;GitHub pieteikumu&lt;/a&gt; kļūdas ziņojumam vai ieteikumam.</string>
<string name="about_privacy_policy">&lt;a href=\"https://github.com/commons-app/apps-android-commons/wiki/Privacy-policy\"&gt;Privātuma politika&lt;/a&gt;</string>
<string name="title_activity_about">Par</string>
<string name="menu_feedback">Nosūtīt atsauksmes (pa e-pastu)</string>
<string name="provider_categories">Nesen lietotās kategorijas</string>

View file

@ -198,5 +198,7 @@
<string name="give_permission">Дај дозвола</string>
<string name="use_external_storage">Користи надворешен склад</string>
<string name="use_external_storage_summary">Зачувување на направените слики во прилогот со камерата на вашиот уред</string>
<string name="send_log_file">Испрати дневничка податотека</string>
<string name="send_log_file_description">Испрати дневничка податотека на разработувачите по е-пошта</string>
<string name="login_to_your_account">Најавете се со вашата сметка</string>
</resources>

View file

@ -198,5 +198,7 @@
<string name="give_permission">Gi tillatelse</string>
<string name="use_external_storage">Bruk ekstern lagring</string>
<string name="use_external_storage_summary">Lagre bilder som er tatt med kameraet i appen på enheten din</string>
<string name="send_log_file">Send loggfil</string>
<string name="send_log_file_description">Send loggfil til utviklerne via epost</string>
<string name="login_to_your_account">Logg inn med kontoen din</string>
</resources>

View file

@ -198,5 +198,7 @@
<string name="give_permission">Dé ël përmess</string>
<string name="use_external_storage">Dovré n\'anmagasinament estern</string>
<string name="use_external_storage_summary">Argistré le plance pijà con la màchina fòto ëd sò angign</string>
<string name="send_log_file">Mandé l\'archivi d\'argistr</string>
<string name="send_log_file_description">Mandé l\'archivi d\'argistr ai dësvlupator për pòsta eletrònica</string>
<string name="login_to_your_account">Ch\'as colega a sò cont</string>
</resources>

Some files were not shown because too many files have changed in this diff Show more