mirror of
https://github.com/commons-app/apps-android-commons.git
synced 2025-10-26 20:33:53 +01:00
Merge branch 'master' into master
This commit is contained in:
commit
05def522af
179 changed files with 3699 additions and 1650 deletions
|
|
@ -19,12 +19,13 @@ android:
|
|||
components:
|
||||
- tools
|
||||
- platform-tools
|
||||
- build-tools-26.0.2
|
||||
- build-tools-27.0.0
|
||||
- extra-google-m2repository
|
||||
- extra-android-m2repository
|
||||
- ${ANDROID_TARGET}
|
||||
- android-25
|
||||
- android-26
|
||||
- android-27
|
||||
- sys-img-${ANDROID_ABI}-${ANDROID_TARGET}
|
||||
licenses:
|
||||
- 'android-sdk-license-.+'
|
||||
|
|
|
|||
|
|
@ -1,5 +1,8 @@
|
|||
# Wikimedia Commons for Android
|
||||
|
||||
## v2.7.2
|
||||
- Modified subtext for "automatically get current location" setting to emphasize that it will reveal user's location
|
||||
|
||||
## v2.7.1
|
||||
- Fixed UI and permission issues with Nearby
|
||||
- Fixed issue with My Recent Uploads being empty
|
||||
|
|
|
|||
|
|
@ -1,3 +1,7 @@
|
|||
## Title (required)
|
||||
|
||||
Fixes #{GitHub issue number and title (Please do not forget adding title) }
|
||||
|
||||
## Description (required)
|
||||
|
||||
Fixes #{GitHub issue number and title}
|
||||
|
|
@ -12,4 +16,4 @@ Tested on {API level & name of device/emulator}, with {build variant, e.g. ProdD
|
|||
|
||||
{Only for user interface changes, otherwise remove this section. See [how to take a screenshot](https://android.stackexchange.com/questions/1759/how-to-take-a-screenshot-with-an-android-device)}
|
||||
|
||||
_Note: Please ensure that you have read CONTRIBUTING.md if this is your first pull request._
|
||||
_Note: Please ensure that you have read CONTRIBUTING.md if this is your first pull request._
|
||||
|
|
@ -7,11 +7,12 @@ apply from: 'quality.gradle'
|
|||
apply plugin: 'com.getkeepsafe.dexcount'
|
||||
|
||||
dependencies {
|
||||
implementation 'com.squareup.picasso:picasso:2.71828'
|
||||
implementation 'com.prof.rssparser:rssparser:1.1'
|
||||
implementation 'com.github.nicolas-raoul:Quadtree:ac16ea8035bf07'
|
||||
implementation 'fr.avianey.com.viewpagerindicator:library:2.4.1.1@aar'
|
||||
implementation 'in.yuvi:http.fluent:1.3'
|
||||
implementation 'com.github.chrisbanes:PhotoView:2.0.0'
|
||||
implementation 'com.android.volley:volley:1.0.0'
|
||||
implementation 'ch.acra:acra:4.9.2'
|
||||
implementation 'org.mediawiki:api:1.3'
|
||||
implementation 'commons-codec:commons-codec:1.10'
|
||||
|
|
@ -21,47 +22,36 @@ dependencies {
|
|||
implementation 'info.debatty:java-string-similarity:0.24'
|
||||
implementation 'com.borjabravo:readmoretextview:2.1.0'
|
||||
implementation 'com.android.support.constraint:constraint-layout:1.0.2'
|
||||
implementation ('com.mapbox.mapboxsdk:mapbox-android-sdk:5.4.1@aar'){
|
||||
transitive=true
|
||||
implementation('com.mapbox.mapboxsdk:mapbox-android-sdk:5.5.0@aar') {
|
||||
transitive = true
|
||||
}
|
||||
|
||||
implementation "com.github.deano2390:MaterialShowcaseView:1.2.0"
|
||||
|
||||
implementation 'com.github.deano2390:MaterialShowcaseView:1.2.0'
|
||||
implementation "com.android.support:support-v4:$SUPPORT_LIB_VERSION"
|
||||
implementation "com.android.support:appcompat-v7:$SUPPORT_LIB_VERSION"
|
||||
implementation "com.android.support:design:$SUPPORT_LIB_VERSION"
|
||||
implementation "com.android.support:customtabs:$SUPPORT_LIB_VERSION"
|
||||
|
||||
implementation "com.android.support:cardview-v7:$SUPPORT_LIB_VERSION"
|
||||
|
||||
implementation "com.jakewharton:butterknife:$BUTTERKNIFE_VERSION"
|
||||
kapt "com.jakewharton:butterknife-compiler:$BUTTERKNIFE_VERSION"
|
||||
|
||||
implementation 'com.squareup.okhttp3:okhttp:3.9.1'
|
||||
implementation 'com.squareup.okio:okio:1.13.0'
|
||||
|
||||
implementation 'io.reactivex.rxjava2:rxandroid:2.0.1'
|
||||
// Because RxAndroid releases are few and far between, it is recommended you also
|
||||
// explicitly depend on RxJava's latest version for bug fixes and new features.
|
||||
implementation 'com.android.support:multidex:1.0.3'
|
||||
|
||||
implementation 'io.reactivex.rxjava2:rxjava:2.1.2'
|
||||
implementation 'com.jakewharton.rxbinding2:rxbinding:2.0.0'
|
||||
implementation 'com.jakewharton.rxbinding2:rxbinding-support-v4:2.0.0'
|
||||
implementation 'com.jakewharton.rxbinding2:rxbinding-appcompat-v7:2.0.0'
|
||||
implementation 'com.jakewharton.rxbinding2:rxbinding-design:2.0.0'
|
||||
|
||||
implementation 'org.jsoup:jsoup:1.11.3'
|
||||
|
||||
implementation 'com.facebook.fresco:fresco:1.5.0'
|
||||
implementation 'com.facebook.stetho:stetho:1.5.0'
|
||||
|
||||
implementation "com.google.dagger:dagger:$DAGGER_VERSION"
|
||||
implementation "com.google.dagger:dagger-android-support:$DAGGER_VERSION"
|
||||
kapt "com.google.dagger:dagger-android-processor:$DAGGER_VERSION"
|
||||
kapt "com.google.dagger:dagger-compiler:$DAGGER_VERSION"
|
||||
|
||||
testImplementation "org.robolectric:multidex:3.4.2"
|
||||
testImplementation 'org.robolectric:multidex:3.4.2'
|
||||
testImplementation "org.jetbrains.kotlin:kotlin-stdlib-jre7:$kotlin_version"
|
||||
testImplementation "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version"
|
||||
testImplementation 'junit:junit:4.12'
|
||||
|
|
@ -69,10 +59,16 @@ dependencies {
|
|||
testImplementation 'com.nhaarman:mockito-kotlin:1.5.0'
|
||||
testImplementation 'com.squareup.okhttp3:mockwebserver:3.8.1'
|
||||
|
||||
implementation 'com.caverock:androidsvg:1.2.1'
|
||||
implementation 'com.github.bumptech.glide:glide:4.7.1'
|
||||
kapt 'com.github.bumptech.glide:compiler:4.7.1'
|
||||
|
||||
androidTestImplementation "org.jetbrains.kotlin:kotlin-stdlib-jre7:$kotlin_version"
|
||||
androidTestImplementation 'com.squareup.okhttp3:mockwebserver:3.8.1'
|
||||
androidTestImplementation "com.android.support:support-annotations:$SUPPORT_LIB_VERSION"
|
||||
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2-alpha1'
|
||||
androidTestImplementation 'com.android.support.test:rules:1.0.2'
|
||||
androidTestImplementation 'com.android.support.test:runner:1.0.2'
|
||||
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
|
||||
|
||||
debugImplementation "com.squareup.leakcanary:leakcanary-android:$LEAK_CANARY"
|
||||
releaseImplementation "com.squareup.leakcanary:leakcanary-android-no-op:$LEAK_CANARY"
|
||||
|
|
@ -87,8 +83,8 @@ android {
|
|||
|
||||
defaultConfig {
|
||||
applicationId 'fr.free.nrw.commons'
|
||||
versionCode 84
|
||||
versionName '2.7.1'
|
||||
versionCode 85
|
||||
versionName '2.7.2'
|
||||
setProperty("archivesBaseName", "app-commons-v$versionName-" + getBranchName())
|
||||
|
||||
minSdkVersion project.minSdkVersion
|
||||
|
|
@ -117,7 +113,7 @@ android {
|
|||
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.
|
||||
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt'
|
||||
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt', 'proguard-glide.txt'
|
||||
}
|
||||
debug {
|
||||
applicationIdSuffix ".debug"
|
||||
|
|
@ -129,7 +125,9 @@ android {
|
|||
flavorDimensions 'tier'
|
||||
productFlavors {
|
||||
prod {
|
||||
buildConfigField "String", "WIKIMEDIA_API_POTD", "\"https://commons.wikimedia.org/w/api.php?action=featuredfeed&feed=potd&feedformat=rss&language=en\""
|
||||
buildConfigField "String", "WIKIMEDIA_API_HOST", "\"https://commons.wikimedia.org/w/api.php\""
|
||||
buildConfigField "String", "WIKIDATA_API_HOST", "\"https://www.wikidata.org/w/api.php\""
|
||||
buildConfigField "String", "WIKIMEDIA_FORGE_API_HOST", "\"https://tools.wmflabs.org/\""
|
||||
buildConfigField "String", "IMAGE_URL_BASE", "\"https://upload.wikimedia.org/wikipedia/commons\""
|
||||
buildConfigField "String", "HOME_URL", "\"https://commons.wikimedia.org/wiki/\""
|
||||
|
|
@ -145,7 +143,9 @@ android {
|
|||
|
||||
beta {
|
||||
// What values do we need to hit the BETA versions of the site / api ?
|
||||
buildConfigField "String", "WIKIMEDIA_API_POTD", "\"https://commons.wikimedia.org/w/api.php?action=featuredfeed&feed=potd&feedformat=rss&language=en\""
|
||||
buildConfigField "String", "WIKIMEDIA_API_HOST", "\"https://commons.wikimedia.beta.wmflabs.org/w/api.php\""
|
||||
buildConfigField "String", "WIKIDATA_API_HOST", "\"https://www.wikidata.org/w/api.php\""
|
||||
buildConfigField "String", "WIKIMEDIA_FORGE_API_HOST", "\"https://tools.wmflabs.org/\""
|
||||
buildConfigField "String", "IMAGE_URL_BASE", "\"https://upload.beta.wmflabs.org/wikipedia/commons\""
|
||||
buildConfigField "String", "HOME_URL", "\"https://commons.wikimedia.beta.wmflabs.org/wiki/\""
|
||||
|
|
|
|||
BIN
app/libs/java-json.jar
Normal file
BIN
app/libs/java-json.jar
Normal file
Binary file not shown.
9
app/proguard-glide.txt
Normal file
9
app/proguard-glide.txt
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
-keep public class * implements com.bumptech.glide.module.GlideModule
|
||||
-keep public class * extends com.bumptech.glide.module.AppGlideModule
|
||||
-keep public enum com.bumptech.glide.load.ImageHeaderParser$** {
|
||||
**[] $VALUES;
|
||||
public *;
|
||||
}
|
||||
|
||||
# for DexGuard only
|
||||
-keepresourcexmlelements manifest/application/meta-data@value=GlideModule
|
||||
|
|
@ -1,5 +1,4 @@
|
|||
-dontobfuscate
|
||||
-keep class org.apache.http.** { *; }
|
||||
-dontwarn org.apache.http.**
|
||||
-keep class fr.free.nrw.commons.upload.MwVolleyApi$Page {*;}
|
||||
-keep class android.support.v7.widget.ShareActionProvider { *; }
|
||||
|
|
@ -18,7 +18,7 @@ task checkstyle(type: Checkstyle) {
|
|||
reports {
|
||||
html {
|
||||
enabled true
|
||||
destination "${project.buildDir}/reports/checkstyle/checkstyle.html"
|
||||
destination file("${project.buildDir}/reports/checkstyle/checkstyle.html")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -36,10 +36,10 @@ task pmd(type: Pmd) {
|
|||
xml.enabled = false
|
||||
html.enabled = true
|
||||
xml {
|
||||
destination "${project.buildDir}/reports/pmd/pmd.xml"
|
||||
destination file("${project.buildDir}/reports/pmd/pmd.xml")
|
||||
}
|
||||
html {
|
||||
destination "${project.buildDir}/reports/pmd/pmd.html"
|
||||
destination file("${project.buildDir}/reports/pmd/pmd.html")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@
|
|||
<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"/>
|
||||
<uses-permission android:name="android.permission.SET_WALLPAPER"/>
|
||||
|
||||
<!-- Needed only if your app targets Android 5.0 (API level 21) or higher. -->
|
||||
<uses-feature android:name="android.hardware.location.gps" />
|
||||
|
|
@ -26,10 +27,10 @@
|
|||
android:theme="@style/LightAppTheme"
|
||||
android:supportsRtl="true" >
|
||||
<activity android:name="org.acra.CrashReportDialog"
|
||||
android:theme="@android:style/Theme.Dialog"
|
||||
android:launchMode="singleInstance"
|
||||
android:excludeFromRecents="true"
|
||||
android:finishOnTaskLaunch="true" />
|
||||
android:theme="@android:style/Theme.Dialog"
|
||||
android:launchMode="singleInstance"
|
||||
android:excludeFromRecents="true"
|
||||
android:finishOnTaskLaunch="true" />
|
||||
|
||||
<activity android:name=".auth.LoginActivity">
|
||||
<intent-filter>
|
||||
|
|
@ -164,6 +165,16 @@
|
|||
android:label="@string/provider_categories"
|
||||
android:syncable="false" />
|
||||
|
||||
<receiver android:name=".widget.PicOfDayAppWidget">
|
||||
<intent-filter>
|
||||
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
|
||||
</intent-filter>
|
||||
|
||||
<meta-data
|
||||
android:name="android.appwidget.provider"
|
||||
android:resource="@xml/pic_of_day_app_widget_info" />
|
||||
</receiver>
|
||||
|
||||
</application>
|
||||
|
||||
</manifest>
|
||||
|
|
|
|||
|
|
@ -9,9 +9,6 @@ import android.os.Bundle;
|
|||
import android.text.Html;
|
||||
import android.text.SpannableString;
|
||||
import android.text.style.UnderlineSpan;
|
||||
import android.util.Log;
|
||||
import android.support.customtabs.CustomTabsIntent;
|
||||
import android.support.v4.content.ContextCompat;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuInflater;
|
||||
import android.view.MenuItem;
|
||||
|
|
@ -20,7 +17,6 @@ import android.widget.ArrayAdapter;
|
|||
import android.widget.LinearLayout;
|
||||
import android.widget.Spinner;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import butterknife.BindView;
|
||||
import butterknife.ButterKnife;
|
||||
|
|
@ -28,8 +24,6 @@ import butterknife.OnClick;
|
|||
import fr.free.nrw.commons.theme.NavigationBaseActivity;
|
||||
import fr.free.nrw.commons.ui.widget.HtmlTextView;
|
||||
|
||||
import static android.widget.Toast.LENGTH_SHORT;
|
||||
|
||||
/**
|
||||
* Represents about screen of this app
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
package fr.free.nrw.commons;
|
||||
|
||||
import android.app.Application;
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.database.sqlite.SQLiteDatabase;
|
||||
|
|
@ -27,7 +26,7 @@ import fr.free.nrw.commons.contributions.ContributionDao;
|
|||
import fr.free.nrw.commons.data.DBOpenHelper;
|
||||
import fr.free.nrw.commons.di.ApplicationlessInjection;
|
||||
import fr.free.nrw.commons.modifications.ModifierSequenceDao;
|
||||
import fr.free.nrw.commons.utils.FileUtils;
|
||||
import fr.free.nrw.commons.upload.FileUtils;
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers;
|
||||
import io.reactivex.schedulers.Schedulers;
|
||||
import timber.log.Timber;
|
||||
|
|
|
|||
|
|
@ -178,6 +178,7 @@ public class Utils {
|
|||
}
|
||||
|
||||
public static void handleWebUrl(Context context, Uri url) {
|
||||
Timber.d("Launching web url %s", url.toString());
|
||||
Intent browserIntent = new Intent(Intent.ACTION_VIEW, url);
|
||||
if (browserIntent.resolveActivity(context.getPackageManager()) == null) {
|
||||
Toast toast = Toast.makeText(context, context.getString(R.string.no_web_browser), LENGTH_SHORT);
|
||||
|
|
|
|||
|
|
@ -4,8 +4,8 @@ import android.accounts.Account;
|
|||
import android.accounts.AccountAuthenticatorActivity;
|
||||
import android.accounts.AccountAuthenticatorResponse;
|
||||
import android.accounts.AccountManager;
|
||||
import android.app.Activity;
|
||||
import android.app.ProgressDialog;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.net.Uri;
|
||||
|
|
@ -23,7 +23,6 @@ import android.view.MenuInflater;
|
|||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.inputmethod.InputMethodManager;
|
||||
import android.widget.Button;
|
||||
import android.widget.EditText;
|
||||
import android.widget.TextView;
|
||||
|
|
@ -136,6 +135,11 @@ public class LoginActivity extends AccountAuthenticatorActivity {
|
|||
}
|
||||
}
|
||||
|
||||
public static void startYourself(Context context) {
|
||||
Intent intent = new Intent(context, LoginActivity.class);
|
||||
context.startActivity(intent);
|
||||
}
|
||||
|
||||
private void forgotPassword() {
|
||||
Utils.handleWebUrl(this, Uri.parse(BuildConfig.FORGOT_PASSWORD_URL));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -81,6 +81,12 @@ public class SessionManager {
|
|||
return sharedPreferences.getBoolean("isUserLoggedIn", false);
|
||||
}
|
||||
|
||||
public void forceLogin(Context context) {
|
||||
if (context != null) {
|
||||
LoginActivity.startYourself(context);
|
||||
}
|
||||
}
|
||||
|
||||
public Completable clearAllAccounts() {
|
||||
AccountManager accountManager = AccountManager.get(context);
|
||||
Account[] allAccounts = accountManager.getAccountsByType(ACCOUNT_TYPE);
|
||||
|
|
|
|||
|
|
@ -7,18 +7,25 @@ import java.util.ArrayList;
|
|||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import fr.free.nrw.commons.upload.MwVolleyApi;
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
|
||||
import fr.free.nrw.commons.upload.GpsCategoryModel;
|
||||
import timber.log.Timber;
|
||||
|
||||
@Singleton
|
||||
public class CacheController {
|
||||
|
||||
private final GpsCategoryModel gpsCategoryModel;
|
||||
private final QuadTree<List<String>> quadTree;
|
||||
private double x, y;
|
||||
private QuadTree<List<String>> quadTree;
|
||||
private double xMinus, xPlus, yMinus, yPlus;
|
||||
|
||||
private static final int EARTH_RADIUS = 6378137;
|
||||
|
||||
public CacheController() {
|
||||
@Inject
|
||||
CacheController(GpsCategoryModel gpsCategoryModel) {
|
||||
this.gpsCategoryModel = gpsCategoryModel;
|
||||
quadTree = new QuadTree<>(-180, -90, +180, +90);
|
||||
}
|
||||
|
||||
|
|
@ -31,8 +38,8 @@ public class CacheController {
|
|||
|
||||
public void cacheCategory() {
|
||||
List<String> pointCatList = new ArrayList<>();
|
||||
if (MwVolleyApi.GpsCatExists.getGpsCatExists()) {
|
||||
pointCatList.addAll(MwVolleyApi.getGpsCat());
|
||||
if (gpsCategoryModel.getGpsCatExists()) {
|
||||
pointCatList.addAll(gpsCategoryModel.getCategoryList());
|
||||
Timber.d("Categories being cached: %s", pointCatList);
|
||||
} else {
|
||||
Timber.d("No categories found, so no categories cached");
|
||||
|
|
@ -65,7 +72,7 @@ public class CacheController {
|
|||
}
|
||||
|
||||
//Based on algorithm at http://gis.stackexchange.com/questions/2951/algorithm-for-offsetting-a-latitude-longitude-by-some-amount-of-meters
|
||||
public void convertCoordRange() {
|
||||
private void convertCoordRange() {
|
||||
//Position, decimal degrees
|
||||
double lat = y;
|
||||
double lon = x;
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@ import butterknife.ButterKnife;
|
|||
import fr.free.nrw.commons.R;
|
||||
import fr.free.nrw.commons.di.CommonsDaggerSupportFragment;
|
||||
import fr.free.nrw.commons.mwapi.MediaWikiApi;
|
||||
import fr.free.nrw.commons.upload.MwVolleyApi;
|
||||
import fr.free.nrw.commons.upload.GpsCategoryModel;
|
||||
import fr.free.nrw.commons.utils.StringSortingUtils;
|
||||
import fr.free.nrw.commons.utils.ViewUtil;
|
||||
import io.reactivex.Observable;
|
||||
|
|
@ -73,6 +73,7 @@ public class CategorizationFragment extends CommonsDaggerSupportFragment {
|
|||
@Inject @Named("prefs") SharedPreferences prefsPrefs;
|
||||
@Inject @Named("direct_nearby_upload_prefs") SharedPreferences directPrefs;
|
||||
@Inject CategoryDao categoryDao;
|
||||
@Inject GpsCategoryModel gpsCategoryModel;
|
||||
|
||||
private RVRendererAdapter<CategoryItem> categoriesAdapter;
|
||||
private OnCategoriesSaveHandler onCategoriesSaveHandler;
|
||||
|
|
@ -253,7 +254,6 @@ public class CategorizationFragment extends CommonsDaggerSupportFragment {
|
|||
}
|
||||
|
||||
private Observable<CategoryItem> defaultCategories() {
|
||||
|
||||
Observable<CategoryItem> directCat = directCategories();
|
||||
if (hasDirectCategories) {
|
||||
Timber.d("Image has direct Cat");
|
||||
|
|
@ -287,9 +287,7 @@ public class CategorizationFragment extends CommonsDaggerSupportFragment {
|
|||
}
|
||||
|
||||
private Observable<CategoryItem> gpsCategories() {
|
||||
return Observable.fromIterable(
|
||||
MwVolleyApi.GpsCatExists.getGpsCatExists()
|
||||
? MwVolleyApi.getGpsCat() : new ArrayList<>())
|
||||
return Observable.fromIterable(gpsCategoryModel.getCategoryList())
|
||||
.map(name -> new CategoryItem(name, false));
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -14,7 +14,6 @@ import fr.free.nrw.commons.Media;
|
|||
import fr.free.nrw.commons.R;
|
||||
import fr.free.nrw.commons.auth.AuthenticatedActivity;
|
||||
import fr.free.nrw.commons.media.MediaDetailPagerFragment;
|
||||
import timber.log.Timber;
|
||||
|
||||
/**
|
||||
* This activity displays pictures of a particular category
|
||||
|
|
|
|||
|
|
@ -224,4 +224,14 @@ public class CategoryImagesListFragment extends DaggerFragment {
|
|||
public ListAdapter getAdapter() {
|
||||
return gridView.getAdapter();
|
||||
}
|
||||
|
||||
/**
|
||||
* This method will be called on back pressed of CategoryImagesActivity.
|
||||
* It initializes the grid view by setting adapter.
|
||||
*/
|
||||
@Override
|
||||
public void onResume() {
|
||||
gridView.setAdapter(gridAdapter);
|
||||
super.onResume();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -45,6 +45,7 @@ public class Contribution extends Media {
|
|||
private long transferred;
|
||||
private String decimalCoords;
|
||||
private boolean isMultiple;
|
||||
private String wikiDataEntityId;
|
||||
|
||||
public Contribution(Uri contentUri, String filename, Uri localUri, String imageUrl, Date timestamp,
|
||||
int state, long dataLength, Date dateUploaded, long transferred,
|
||||
|
|
@ -222,4 +223,17 @@ public class Contribution extends Media {
|
|||
|
||||
throw new RuntimeException("Unrecognized license value: " + license);
|
||||
}
|
||||
|
||||
public String getWikiDataEntityId() {
|
||||
return wikiDataEntityId;
|
||||
}
|
||||
|
||||
/**
|
||||
* When the corresponding wikidata entity is known as in case of nearby uploads, it can be set
|
||||
* using the setter method
|
||||
* @param wikiDataEntityId
|
||||
*/
|
||||
public void setWikiDataEntityId(String wikiDataEntityId) {
|
||||
this.wikiDataEntityId = wikiDataEntityId;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -90,7 +90,7 @@ public class ContributionController {
|
|||
fragment.startActivityForResult(pickImageIntent, SELECT_FROM_GALLERY);
|
||||
}
|
||||
|
||||
public void handleImagePicked(int requestCode, Intent data, boolean isDirectUpload) {
|
||||
public void handleImagePicked(int requestCode, Intent data, boolean isDirectUpload, String wikiDataEntityId) {
|
||||
FragmentActivity activity = fragment.getActivity();
|
||||
Timber.d("handleImagePicked() called with onActivityResult()");
|
||||
Intent shareIntent = new Intent(activity, ShareActivity.class);
|
||||
|
|
@ -102,9 +102,6 @@ public class ContributionController {
|
|||
shareIntent.setType(activity.getContentResolver().getType(imageData));
|
||||
shareIntent.putExtra(EXTRA_STREAM, imageData);
|
||||
shareIntent.putExtra(EXTRA_SOURCE, SOURCE_GALLERY);
|
||||
if (isDirectUpload) {
|
||||
shareIntent.putExtra("isDirectUpload", true);
|
||||
}
|
||||
break;
|
||||
case SELECT_FROM_CAMERA:
|
||||
//FIXME: Find out appropriate mime type
|
||||
|
|
@ -113,9 +110,6 @@ public class ContributionController {
|
|||
shareIntent.setType("image/jpeg");
|
||||
shareIntent.putExtra(EXTRA_STREAM, lastGeneratedCaptureUri);
|
||||
shareIntent.putExtra(EXTRA_SOURCE, SOURCE_CAMERA);
|
||||
if (isDirectUpload) {
|
||||
shareIntent.putExtra("isDirectUpload", true);
|
||||
}
|
||||
|
||||
break;
|
||||
default:
|
||||
|
|
@ -123,6 +117,10 @@ public class ContributionController {
|
|||
}
|
||||
Timber.i("Image selected");
|
||||
try {
|
||||
shareIntent.putExtra("isDirectUpload", isDirectUpload);
|
||||
if (wikiDataEntityId != null && !wikiDataEntityId.equals("")) {
|
||||
shareIntent.putExtra("wikiDataEntityId", wikiDataEntityId);
|
||||
}
|
||||
activity.startActivity(shareIntent);
|
||||
} catch (SecurityException e) {
|
||||
Timber.e(e, "Security Exception");
|
||||
|
|
|
|||
|
|
@ -8,7 +8,6 @@ import android.net.Uri;
|
|||
import android.os.RemoteException;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
|
|
|
|||
|
|
@ -276,17 +276,25 @@ public class ContributionsActivity
|
|||
.getUploadCount(sessionManager.getCurrentAccount().name)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(
|
||||
uploadCount -> getSupportActionBar().setSubtitle(getResources()
|
||||
.getQuantityString(R.plurals.contributions_subtitle,
|
||||
uploadCount, uploadCount)),
|
||||
.subscribe(this::displayUploadCount,
|
||||
t -> Timber.e(t, "Fetching upload count failed")
|
||||
));
|
||||
}
|
||||
|
||||
public void betaSetUploadCount(int betaUploadCount){
|
||||
private void displayUploadCount(Integer uploadCount) {
|
||||
if (isFinishing()
|
||||
|| getSupportActionBar() == null
|
||||
|| getResources() == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
getSupportActionBar().setSubtitle(getResources()
|
||||
.getQuantityString(R.plurals.contributions_subtitle, betaUploadCount, betaUploadCount));
|
||||
.getQuantityString(R.plurals.contributions_subtitle,
|
||||
uploadCount, uploadCount));
|
||||
}
|
||||
|
||||
public void betaSetUploadCount(int betaUploadCount) {
|
||||
displayUploadCount(betaUploadCount);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -117,7 +117,7 @@ public class ContributionsListFragment extends CommonsDaggerSupportFragment {
|
|||
if (resultCode == RESULT_OK) {
|
||||
Timber.d("OnActivityResult() parameters: Req code: %d Result code: %d Data: %s",
|
||||
requestCode, resultCode, data);
|
||||
controller.handleImagePicked(requestCode, data, false);
|
||||
controller.handleImagePicked(requestCode, data, false, null);
|
||||
} else {
|
||||
Timber.e("OnActivityResult() parameters: Req code: %d Result code: %d Data: %s",
|
||||
requestCode, resultCode, data);
|
||||
|
|
|
|||
|
|
@ -9,17 +9,18 @@ import dagger.android.support.AndroidSupportInjectionModule;
|
|||
import fr.free.nrw.commons.CommonsApplication;
|
||||
import fr.free.nrw.commons.MediaWikiImageView;
|
||||
import fr.free.nrw.commons.auth.LoginActivity;
|
||||
import fr.free.nrw.commons.contributions.Contribution;
|
||||
import fr.free.nrw.commons.contributions.ContributionsActivity;
|
||||
import fr.free.nrw.commons.contributions.ContributionsSyncAdapter;
|
||||
import fr.free.nrw.commons.delete.DeleteTask;
|
||||
import fr.free.nrw.commons.modifications.ModificationsSyncAdapter;
|
||||
import fr.free.nrw.commons.settings.SettingsFragment;
|
||||
import fr.free.nrw.commons.nearby.PlaceRenderer;
|
||||
import fr.free.nrw.commons.upload.FileProcessor;
|
||||
import fr.free.nrw.commons.settings.SettingsFragment;
|
||||
|
||||
|
||||
@Singleton
|
||||
@Component(modules = {
|
||||
CommonsApplicationModule.class,
|
||||
NetworkingModule.class,
|
||||
AndroidInjectionModule.class,
|
||||
AndroidSupportInjectionModule.class,
|
||||
ActivityBuilderModule.class,
|
||||
|
|
@ -47,6 +48,8 @@ public interface CommonsApplicationComponent extends AndroidInjector<Application
|
|||
|
||||
void inject(PlaceRenderer placeRenderer);
|
||||
|
||||
void inject(FileProcessor fileProcessor);
|
||||
|
||||
@Component.Builder
|
||||
@SuppressWarnings({"WeakerAccess", "unused"})
|
||||
interface Builder {
|
||||
|
|
|
|||
|
|
@ -6,24 +6,21 @@ import android.content.SharedPreferences;
|
|||
import android.preference.PreferenceManager;
|
||||
import android.support.v4.util.LruCache;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
|
||||
import javax.inject.Named;
|
||||
import javax.inject.Singleton;
|
||||
|
||||
import dagger.Module;
|
||||
import dagger.Provides;
|
||||
import fr.free.nrw.commons.BuildConfig;
|
||||
import fr.free.nrw.commons.CommonsApplication;
|
||||
|
||||
import fr.free.nrw.commons.auth.AccountUtil;
|
||||
import fr.free.nrw.commons.auth.SessionManager;
|
||||
import fr.free.nrw.commons.caching.CacheController;
|
||||
import fr.free.nrw.commons.data.DBOpenHelper;
|
||||
import fr.free.nrw.commons.location.LocationServiceManager;
|
||||
import fr.free.nrw.commons.mwapi.ApacheHttpClientMediaWikiApi;
|
||||
import fr.free.nrw.commons.mwapi.MediaWikiApi;
|
||||
import fr.free.nrw.commons.nearby.NearbyPlaces;
|
||||
import fr.free.nrw.commons.upload.UploadController;
|
||||
import fr.free.nrw.commons.wikidata.WikidataEditListener;
|
||||
import fr.free.nrw.commons.wikidata.WikidataEditListenerImpl;
|
||||
|
||||
import static android.content.Context.MODE_PRIVATE;
|
||||
import static fr.free.nrw.commons.contributions.ContributionsContentProvider.CONTRIBUTION_AUTHORITY;
|
||||
|
|
@ -33,7 +30,6 @@ import static fr.free.nrw.commons.modifications.ModificationsContentProvider.MOD
|
|||
@SuppressWarnings({"WeakerAccess", "unused"})
|
||||
public class CommonsApplicationModule {
|
||||
public static final String CATEGORY_AUTHORITY = "fr.free.nrw.commons.categories.contentprovider";
|
||||
public static final long OK_HTTP_CACHE_SIZE = 10 * 1024 * 1024;
|
||||
|
||||
private Context applicationContext;
|
||||
|
||||
|
|
@ -117,37 +113,12 @@ public class CommonsApplicationModule {
|
|||
return new SessionManager(context, mediaWikiApi, sharedPreferences);
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
public MediaWikiApi provideMediaWikiApi(Context context,
|
||||
@Named("default_preferences") SharedPreferences defaultPreferences,
|
||||
@Named("category_prefs") SharedPreferences categoryPrefs,
|
||||
Gson gson) {
|
||||
return new ApacheHttpClientMediaWikiApi(context, BuildConfig.WIKIMEDIA_API_HOST, defaultPreferences, categoryPrefs, gson);
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
public LocationServiceManager provideLocationServiceManager(Context context) {
|
||||
return new LocationServiceManager(context);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gson objects are very heavy. The app should ideally be using just one instance of it instead of creating new instances everywhere.
|
||||
* @return returns a singleton Gson instance
|
||||
*/
|
||||
@Provides
|
||||
@Singleton
|
||||
public Gson provideGson() {
|
||||
return new Gson();
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
public CacheController provideCacheController() {
|
||||
return new CacheController();
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
public DBOpenHelper provideDBOpenHelper(Context context) {
|
||||
|
|
@ -165,4 +136,10 @@ public class CommonsApplicationModule {
|
|||
public LruCache<String, String> provideLruCache() {
|
||||
return new LruCache<>(1024);
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
public WikidataEditListener provideWikidataEditListener() {
|
||||
return new WikidataEditListenerImpl();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,59 @@
|
|||
package fr.free.nrw.commons.di;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.support.annotation.NonNull;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
|
||||
import javax.inject.Named;
|
||||
import javax.inject.Singleton;
|
||||
|
||||
import dagger.Module;
|
||||
import dagger.Provides;
|
||||
import fr.free.nrw.commons.BuildConfig;
|
||||
import fr.free.nrw.commons.mwapi.ApacheHttpClientMediaWikiApi;
|
||||
import fr.free.nrw.commons.mwapi.MediaWikiApi;
|
||||
import okhttp3.HttpUrl;
|
||||
import okhttp3.OkHttpClient;
|
||||
|
||||
@Module
|
||||
@SuppressWarnings({"WeakerAccess", "unused"})
|
||||
public class NetworkingModule {
|
||||
public static final long OK_HTTP_CACHE_SIZE = 10 * 1024 * 1024;
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
public OkHttpClient provideOkHttpClient() {
|
||||
return new OkHttpClient.Builder().build();
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
public MediaWikiApi provideMediaWikiApi(Context context,
|
||||
@Named("default_preferences") SharedPreferences defaultPreferences,
|
||||
@Named("category_prefs") SharedPreferences categoryPrefs,
|
||||
Gson gson) {
|
||||
return new ApacheHttpClientMediaWikiApi(context, BuildConfig.WIKIMEDIA_API_HOST, BuildConfig.WIKIDATA_API_HOST, defaultPreferences, categoryPrefs, gson);
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Named("commons_mediawiki_url")
|
||||
@NonNull
|
||||
@SuppressWarnings("ConstantConditions")
|
||||
public HttpUrl provideMwUrl() {
|
||||
return HttpUrl.parse(BuildConfig.COMMONS_URL);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gson objects are very heavy. The app should ideally be using just one instance of it instead of creating new instances everywhere.
|
||||
* @return returns a singleton Gson instance
|
||||
*/
|
||||
@Provides
|
||||
@Singleton
|
||||
public Gson provideGson() {
|
||||
return new GsonBuilder().create();
|
||||
}
|
||||
|
||||
}
|
||||
36
app/src/main/java/fr/free/nrw/commons/glide/SvgDecoder.java
Normal file
36
app/src/main/java/fr/free/nrw/commons/glide/SvgDecoder.java
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
package fr.free.nrw.commons.glide;
|
||||
|
||||
import android.support.annotation.NonNull;
|
||||
|
||||
import com.bumptech.glide.load.Options;
|
||||
import com.bumptech.glide.load.ResourceDecoder;
|
||||
import com.bumptech.glide.load.engine.Resource;
|
||||
import com.bumptech.glide.load.resource.SimpleResource;
|
||||
import com.caverock.androidsvg.SVG;
|
||||
import com.caverock.androidsvg.SVGParseException;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
/**
|
||||
* Decodes an SVG internal representation from an {@link InputStream}.
|
||||
*/
|
||||
public class SvgDecoder implements ResourceDecoder<InputStream, SVG> {
|
||||
|
||||
@Override
|
||||
public boolean handles(@NonNull InputStream source, @NonNull Options options) {
|
||||
// TODO: Can we tell?
|
||||
return true;
|
||||
}
|
||||
|
||||
public Resource<SVG> decode(@NonNull InputStream source, int width, int height,
|
||||
@NonNull Options options)
|
||||
throws IOException {
|
||||
try {
|
||||
SVG svg = SVG.getFromInputStream(source);
|
||||
return new SimpleResource<>(svg);
|
||||
} catch (SVGParseException ex) {
|
||||
throw new IOException("Cannot load SVG from stream", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
package fr.free.nrw.commons.glide;
|
||||
|
||||
import android.graphics.Picture;
|
||||
import android.graphics.drawable.PictureDrawable;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
|
||||
import com.bumptech.glide.load.Options;
|
||||
import com.bumptech.glide.load.engine.Resource;
|
||||
import com.bumptech.glide.load.resource.SimpleResource;
|
||||
import com.bumptech.glide.load.resource.transcode.ResourceTranscoder;
|
||||
import com.caverock.androidsvg.SVG;
|
||||
|
||||
/**
|
||||
* Convert the {@link SVG}'s internal representation to an Android-compatible one
|
||||
* ({@link Picture}).
|
||||
*/
|
||||
public class SvgDrawableTranscoder implements ResourceTranscoder<SVG, PictureDrawable> {
|
||||
@Nullable
|
||||
@Override
|
||||
public Resource<PictureDrawable> transcode(@NonNull Resource<SVG> toTranscode,
|
||||
@NonNull Options options) {
|
||||
SVG svg = toTranscode.get();
|
||||
Picture picture = svg.renderToPicture();
|
||||
PictureDrawable drawable = new PictureDrawable(picture);
|
||||
return new SimpleResource<>(drawable);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,51 @@
|
|||
package fr.free.nrw.commons.glide;
|
||||
|
||||
import android.graphics.drawable.PictureDrawable;
|
||||
import android.widget.ImageView;
|
||||
|
||||
import com.bumptech.glide.load.DataSource;
|
||||
import com.bumptech.glide.load.engine.GlideException;
|
||||
import com.bumptech.glide.request.RequestListener;
|
||||
import com.bumptech.glide.request.target.ImageViewTarget;
|
||||
import com.bumptech.glide.request.target.Target;
|
||||
|
||||
/**
|
||||
* Listener which updates the {@link ImageView} to be software rendered, because
|
||||
* {@link com.caverock.androidsvg.SVG SVG}/{@link android.graphics.Picture Picture} can't render on
|
||||
* a hardware backed {@link android.graphics.Canvas Canvas}.
|
||||
*/
|
||||
public class SvgSoftwareLayerSetter implements RequestListener<PictureDrawable> {
|
||||
|
||||
/**
|
||||
* Sets the layer type to none if the load fails
|
||||
* @param e
|
||||
* @param model
|
||||
* @param target
|
||||
* @param isFirstResource
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public boolean onLoadFailed(GlideException e, Object model, Target<PictureDrawable> target,
|
||||
boolean isFirstResource) {
|
||||
ImageView view = ((ImageViewTarget<?>) target).getView();
|
||||
view.setLayerType(ImageView.LAYER_TYPE_NONE, null);
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the layer type to software when the resource is ready
|
||||
* @param resource
|
||||
* @param model
|
||||
* @param target
|
||||
* @param dataSource
|
||||
* @param isFirstResource
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public boolean onResourceReady(PictureDrawable resource, Object model,
|
||||
Target<PictureDrawable> target, DataSource dataSource, boolean isFirstResource) {
|
||||
ImageView view = ((ImageViewTarget<?>) target).getView();
|
||||
view.setLayerType(ImageView.LAYER_TYPE_SOFTWARE, null);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
package fr.free.nrw.commons.location;
|
||||
|
||||
import android.Manifest;
|
||||
import android.annotation.SuppressLint;
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.content.pm.PackageManager;
|
||||
|
|
@ -10,9 +11,10 @@ import android.location.LocationManager;
|
|||
import android.os.Bundle;
|
||||
import android.support.v4.app.ActivityCompat;
|
||||
import android.support.v4.content.ContextCompat;
|
||||
import android.util.Log;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
|
||||
import timber.log.Timber;
|
||||
|
|
@ -29,6 +31,7 @@ public class LocationServiceManager implements LocationListener {
|
|||
private Location lastLocation;
|
||||
private final List<LocationUpdateListener> locationListeners = new CopyOnWriteArrayList<>();
|
||||
private boolean isLocationManagerRegistered = false;
|
||||
private Set<Activity> locationExplanationDisplayed = new HashSet<>();
|
||||
|
||||
/**
|
||||
* Constructs a new instance of LocationServiceManager.
|
||||
|
|
@ -51,7 +54,6 @@ public class LocationServiceManager implements LocationListener {
|
|||
|
||||
/**
|
||||
* Returns whether the location permission is granted.
|
||||
*
|
||||
* @return true if the location permission is granted
|
||||
*/
|
||||
public boolean isLocationPermissionGranted() {
|
||||
|
|
@ -73,10 +75,23 @@ public class LocationServiceManager implements LocationListener {
|
|||
LOCATION_REQUEST);
|
||||
}
|
||||
|
||||
/**
|
||||
* The permission explanation dialog box is now displayed just once for a particular activity. We are subscribing
|
||||
* to updates from multiple providers so its important to show the dialog just once. Otherwise it will be displayed
|
||||
* once for every provider, which in our case currently is 2.
|
||||
* @param activity
|
||||
* @return
|
||||
*/
|
||||
public boolean isPermissionExplanationRequired(Activity activity) {
|
||||
return !activity.isFinishing() &&
|
||||
ActivityCompat.shouldShowRequestPermissionRationale(activity,
|
||||
Manifest.permission.ACCESS_FINE_LOCATION);
|
||||
if (activity.isFinishing()) {
|
||||
return false;
|
||||
}
|
||||
boolean showRequestPermissionRationale = ActivityCompat.shouldShowRequestPermissionRationale(activity, Manifest.permission.ACCESS_FINE_LOCATION);
|
||||
if (showRequestPermissionRationale && !locationExplanationDisplayed.contains(activity)) {
|
||||
locationExplanationDisplayed.add(activity);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -84,8 +99,9 @@ public class LocationServiceManager implements LocationListener {
|
|||
* (e.g. when Location permission just granted)
|
||||
* @return last known LatLng
|
||||
*/
|
||||
@SuppressLint("MissingPermission")
|
||||
public LatLng getLKL() {
|
||||
if (ContextCompat.checkSelfPermission(context, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED) {
|
||||
if (isLocationPermissionGranted()) {
|
||||
Location lastKL = locationManager.getLastKnownLocation(LocationManager.GPS_PROVIDER);
|
||||
if (lastKL == null) {
|
||||
lastKL = locationManager.getLastKnownLocation(LocationManager.NETWORK_PROVIDER);
|
||||
|
|
@ -107,9 +123,10 @@ public class LocationServiceManager implements LocationListener {
|
|||
* Registers a LocationManager to listen for current location.
|
||||
*/
|
||||
public void registerLocationManager() {
|
||||
if (!isLocationManagerRegistered)
|
||||
if (!isLocationManagerRegistered) {
|
||||
isLocationManagerRegistered = requestLocationUpdatesFromProvider(LocationManager.NETWORK_PROVIDER)
|
||||
&& requestLocationUpdatesFromProvider(LocationManager.GPS_PROVIDER);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -142,7 +159,7 @@ public class LocationServiceManager implements LocationListener {
|
|||
* @return LOCATION_SIGNIFICANTLY_CHANGED if location changed significantly
|
||||
* LOCATION_SLIGHTLY_CHANGED if location changed slightly
|
||||
*/
|
||||
protected LocationChangeType isBetterLocation(Location location, Location currentBestLocation) {
|
||||
private LocationChangeType isBetterLocation(Location location, Location currentBestLocation) {
|
||||
|
||||
if (currentBestLocation == null) {
|
||||
// A new location is always better than no location
|
||||
|
|
@ -267,6 +284,7 @@ public class LocationServiceManager implements LocationListener {
|
|||
LOCATION_SIGNIFICANTLY_CHANGED, //Went out of borders of nearby markers
|
||||
LOCATION_SLIGHTLY_CHANGED, //User might be walking or driving
|
||||
LOCATION_NOT_CHANGED,
|
||||
PERMISSION_JUST_GRANTED
|
||||
PERMISSION_JUST_GRANTED,
|
||||
MAP_UPDATED
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -56,16 +56,16 @@ import static android.widget.Toast.LENGTH_SHORT;
|
|||
public class MediaDetailFragment extends CommonsDaggerSupportFragment {
|
||||
|
||||
private boolean editable;
|
||||
private boolean isFeaturedMedia;
|
||||
private boolean isCategoryImage;
|
||||
private MediaDetailPagerFragment.MediaDetailProvider detailProvider;
|
||||
private int index;
|
||||
|
||||
public static MediaDetailFragment forMedia(int index, boolean editable, boolean isFeaturedMedia) {
|
||||
public static MediaDetailFragment forMedia(int index, boolean editable, boolean isCategoryImage) {
|
||||
MediaDetailFragment mf = new MediaDetailFragment();
|
||||
|
||||
Bundle state = new Bundle();
|
||||
state.putBoolean("editable", editable);
|
||||
state.putBoolean("isFeaturedMedia", isFeaturedMedia);
|
||||
state.putBoolean("isCategoryImage", isCategoryImage);
|
||||
state.putInt("index", index);
|
||||
state.putInt("listIndex", 0);
|
||||
state.putInt("listTop", 0);
|
||||
|
|
@ -128,7 +128,7 @@ public class MediaDetailFragment extends CommonsDaggerSupportFragment {
|
|||
super.onSaveInstanceState(outState);
|
||||
outState.putInt("index", index);
|
||||
outState.putBoolean("editable", editable);
|
||||
outState.putBoolean("isFeaturedMedia", isFeaturedMedia);
|
||||
outState.putBoolean("isCategoryImage", isCategoryImage);
|
||||
|
||||
getScrollPosition();
|
||||
outState.putInt("listTop", initialListTop);
|
||||
|
|
@ -144,12 +144,12 @@ public class MediaDetailFragment extends CommonsDaggerSupportFragment {
|
|||
|
||||
if (savedInstanceState != null) {
|
||||
editable = savedInstanceState.getBoolean("editable");
|
||||
isFeaturedMedia = savedInstanceState.getBoolean("isFeaturedMedia");
|
||||
isCategoryImage = savedInstanceState.getBoolean("isCategoryImage");
|
||||
index = savedInstanceState.getInt("index");
|
||||
initialListTop = savedInstanceState.getInt("listTop");
|
||||
} else {
|
||||
editable = getArguments().getBoolean("editable");
|
||||
isFeaturedMedia = getArguments().getBoolean("isFeaturedMedia");
|
||||
isCategoryImage = getArguments().getBoolean("isCategoryImage");
|
||||
index = getArguments().getInt("index");
|
||||
initialListTop = 0;
|
||||
}
|
||||
|
|
@ -161,7 +161,7 @@ public class MediaDetailFragment extends CommonsDaggerSupportFragment {
|
|||
|
||||
ButterKnife.bind(this,view);
|
||||
|
||||
if (isFeaturedMedia){
|
||||
if (isCategoryImage){
|
||||
authorLayout.setVisibility(VISIBLE);
|
||||
} else {
|
||||
authorLayout.setVisibility(GONE);
|
||||
|
|
@ -328,7 +328,7 @@ public class MediaDetailFragment extends CommonsDaggerSupportFragment {
|
|||
if (!TextUtils.isEmpty(licenseLink(media))) {
|
||||
openWebBrowser(licenseLink(media));
|
||||
} else {
|
||||
if(isFeaturedMedia) {
|
||||
if(isCategoryImage) {
|
||||
Timber.d("Unable to fetch license URL for %s", media.getLicense());
|
||||
} else {
|
||||
Toast toast = Toast.makeText(getContext(), getString(R.string.null_url), Toast.LENGTH_SHORT);
|
||||
|
|
@ -503,8 +503,7 @@ public class MediaDetailFragment extends CommonsDaggerSupportFragment {
|
|||
if (media.getRequestedDeletion()){
|
||||
delete.setVisibility(GONE);
|
||||
nominatedForDeletion.setVisibility(VISIBLE);
|
||||
}
|
||||
else{
|
||||
} else if (!isCategoryImage) {
|
||||
delete.setVisibility(VISIBLE);
|
||||
nominatedForDeletion.setVisibility(GONE);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -38,6 +38,8 @@ import fr.free.nrw.commons.contributions.Contribution;
|
|||
import fr.free.nrw.commons.contributions.ContributionsActivity;
|
||||
import fr.free.nrw.commons.di.CommonsDaggerSupportFragment;
|
||||
import fr.free.nrw.commons.mwapi.MediaWikiApi;
|
||||
import fr.free.nrw.commons.utils.ImageUtils;
|
||||
import timber.log.Timber;
|
||||
|
||||
import static android.Manifest.permission.READ_EXTERNAL_STORAGE;
|
||||
import static android.content.Context.DOWNLOAD_SERVICE;
|
||||
|
|
@ -140,6 +142,10 @@ public class MediaDetailPagerFragment extends CommonsDaggerSupportFragment imple
|
|||
// Download
|
||||
downloadMedia(m);
|
||||
return true;
|
||||
case R.id.menu_set_as_wallpaper:
|
||||
// Set wallpaper
|
||||
setWallpaper(m);
|
||||
return true;
|
||||
case R.id.menu_retry_current_image:
|
||||
// Retry
|
||||
((ContributionsActivity) getActivity()).retryUpload(pager.getCurrentItem());
|
||||
|
|
@ -155,6 +161,19 @@ public class MediaDetailPagerFragment extends CommonsDaggerSupportFragment imple
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the media as the device's wallpaper if the imageUrl is not null
|
||||
* Fails silently if setting the wallpaper fails
|
||||
* @param media
|
||||
*/
|
||||
private void setWallpaper(Media media) {
|
||||
if(media.getImageUrl() == null || media.getImageUrl().isEmpty()) {
|
||||
Timber.d("Media URL not present");
|
||||
return;
|
||||
}
|
||||
ImageUtils.setWallpaperFromImageUrl(getActivity(), Uri.parse(media.getImageUrl()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Start the media file downloading to the local SD card/storage.
|
||||
* The file can then be opened in Gallery or other apps.
|
||||
|
|
|
|||
|
|
@ -25,6 +25,8 @@ import org.apache.http.params.CoreProtocolPNames;
|
|||
import org.apache.http.util.EntityUtils;
|
||||
import org.mediawiki.api.ApiResult;
|
||||
import org.mediawiki.api.MWApi;
|
||||
import org.w3c.dom.Element;
|
||||
import org.w3c.dom.Node;
|
||||
import org.w3c.dom.NodeList;
|
||||
|
||||
import java.io.IOException;
|
||||
|
|
@ -62,6 +64,7 @@ public class ApacheHttpClientMediaWikiApi implements MediaWikiApi {
|
|||
private static final String THUMB_SIZE = "640";
|
||||
private AbstractHttpClient httpClient;
|
||||
private MWApi api;
|
||||
private MWApi wikidataApi;
|
||||
private Context context;
|
||||
private SharedPreferences defaultPreferences;
|
||||
private SharedPreferences categoryPreferences;
|
||||
|
|
@ -69,6 +72,7 @@ public class ApacheHttpClientMediaWikiApi implements MediaWikiApi {
|
|||
|
||||
public ApacheHttpClientMediaWikiApi(Context context,
|
||||
String apiURL,
|
||||
String wikidatApiURL,
|
||||
SharedPreferences defaultPreferences,
|
||||
SharedPreferences categoryPreferences,
|
||||
Gson gson) {
|
||||
|
|
@ -82,6 +86,7 @@ public class ApacheHttpClientMediaWikiApi implements MediaWikiApi {
|
|||
params.setParameter(CoreProtocolPNames.USER_AGENT, getUserAgent());
|
||||
httpClient = new DefaultHttpClient(cm, params);
|
||||
api = new MWApi(apiURL, httpClient);
|
||||
wikidataApi = new MWApi(wikidatApiURL, httpClient);
|
||||
this.defaultPreferences = defaultPreferences;
|
||||
this.categoryPreferences = categoryPreferences;
|
||||
this.gson = gson;
|
||||
|
|
@ -206,6 +211,15 @@ public class ApacheHttpClientMediaWikiApi implements MediaWikiApi {
|
|||
return api.getEditToken();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCentralAuthToken() throws IOException {
|
||||
String centralAuthToken = api.action("centralauthtoken")
|
||||
.get()
|
||||
.getString("/api/centralauthtoken/@centralauthtoken");
|
||||
Timber.d("MediaWiki Central auth token is %s", centralAuthToken);
|
||||
return centralAuthToken;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean fileExistsWithName(String fileName) throws IOException {
|
||||
return api.action("query")
|
||||
|
|
@ -351,6 +365,98 @@ public class ApacheHttpClientMediaWikiApi implements MediaWikiApi {
|
|||
}).flatMapObservable(Observable::fromIterable);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the edit token for making wiki data edits
|
||||
* https://www.mediawiki.org/wiki/API:Tokens
|
||||
* @return
|
||||
* @throws IOException
|
||||
*/
|
||||
private String getWikidataEditToken() throws IOException {
|
||||
return wikidataApi.getEditToken();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getWikidataCsrfToken() throws IOException {
|
||||
String wikidataCsrfToken = wikidataApi.action("query")
|
||||
.param("action", "query")
|
||||
.param("centralauthtoken", getCentralAuthToken())
|
||||
.param("meta", "tokens")
|
||||
.post()
|
||||
.getString("/api/query/tokens/@csrftoken");
|
||||
Timber.d("Wikidata csrf token is %s", wikidataCsrfToken);
|
||||
return wikidataCsrfToken;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new claim using the wikidata API
|
||||
* https://www.mediawiki.org/wiki/Wikibase/API
|
||||
* @param entityId the wikidata entity to be edited
|
||||
* @param property the property to be edited, for eg P18 for images
|
||||
* @param snaktype the type of value stored for that property
|
||||
* @param value the actual value to be stored for the property, for eg filename in case of P18
|
||||
* @return returns revisionId if the claim is successfully created else returns null
|
||||
* @throws IOException
|
||||
*/
|
||||
@Nullable
|
||||
@Override
|
||||
public String wikidatCreateClaim(String entityId, String property, String snaktype, String value) throws IOException {
|
||||
Timber.d("Filename is %s", value);
|
||||
ApiResult result = wikidataApi.action("wbcreateclaim")
|
||||
.param("entity", entityId)
|
||||
.param("centralauthtoken", getCentralAuthToken())
|
||||
.param("token", getWikidataCsrfToken())
|
||||
.param("snaktype", snaktype)
|
||||
.param("property", property)
|
||||
.param("value", value)
|
||||
.post();
|
||||
|
||||
if (result == null || result.getNode("api") == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Node node = result.getNode("api").getDocument();
|
||||
Element element = (Element) node;
|
||||
|
||||
if (element != null && element.getAttribute("success").equals("1")) {
|
||||
return result.getString("api/pageinfo/@lastrevid");
|
||||
} else {
|
||||
Timber.e(result.getString("api/error/@code") + " " + result.getString("api/error/@info"));
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the wikimedia-commons-app tag to the edits made on wikidata
|
||||
* @param revisionId
|
||||
* @return
|
||||
* @throws IOException
|
||||
*/
|
||||
@Nullable
|
||||
@Override
|
||||
public boolean addWikidataEditTag(String revisionId) throws IOException {
|
||||
ApiResult result = wikidataApi.action("tag")
|
||||
.param("revid", revisionId)
|
||||
.param("centralauthtoken", getCentralAuthToken())
|
||||
.param("token", getWikidataCsrfToken())
|
||||
.param("add", "wikimedia-commons-app")
|
||||
.param("reason", "Add tag for edits made using Android Commons app")
|
||||
.post();
|
||||
|
||||
if (result == null || result.getNode("api") == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Node node = result.getNode("api").getDocument();
|
||||
Element element = (Element) node;
|
||||
|
||||
if (element != null && element.getAttribute("status").equals("success")) {
|
||||
return true;
|
||||
} else {
|
||||
Timber.e(result.getString("api/error/@code") + " " + result.getString("api/error/@info"));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
@NonNull
|
||||
public Observable<String> searchTitles(String title, int searchCatsLimit) {
|
||||
|
|
@ -444,8 +550,8 @@ public class ApacheHttpClientMediaWikiApi implements MediaWikiApi {
|
|||
.param("notprop", "list")
|
||||
.param("format", "xml")
|
||||
.param("meta", "notifications")
|
||||
// .param("meta", "notifications")
|
||||
.param("notformat", "model")
|
||||
.param("notwikis", "wikidatawiki|commonswiki|enwiki")
|
||||
.get()
|
||||
.getNode("/api/query/notifications/list");
|
||||
} catch (IOException e) {
|
||||
|
|
@ -480,6 +586,8 @@ public class ApacheHttpClientMediaWikiApi implements MediaWikiApi {
|
|||
.param("format", "xml")
|
||||
.param("gcmtype", "file")
|
||||
.param("gcmtitle", categoryName)
|
||||
.param("gcmsort", "timestamp")//property to sort by;timestamp
|
||||
.param("gcmdir", "desc")//in which direction to sort;descending
|
||||
.param("prop", "imageinfo")
|
||||
.param("gcmlimit", "10")
|
||||
.param("iiprop", "url|extmetadata");
|
||||
|
|
@ -586,6 +694,7 @@ public class ApacheHttpClientMediaWikiApi implements MediaWikiApi {
|
|||
String resultStatus = result.getString("/api/upload/@result");
|
||||
if (!resultStatus.equals("Success")) {
|
||||
String errorCode = result.getString("/api/error/@code");
|
||||
Timber.e(errorCode);
|
||||
return new UploadResult(resultStatus, errorCode);
|
||||
} else {
|
||||
Date dateUploaded = parseMWDate(result.getString("/api/upload/imageinfo/@timestamp"));
|
||||
|
|
|
|||
101
app/src/main/java/fr/free/nrw/commons/mwapi/CategoryApi.java
Normal file
101
app/src/main/java/fr/free/nrw/commons/mwapi/CategoryApi.java
Normal file
|
|
@ -0,0 +1,101 @@
|
|||
package fr.free.nrw.commons.mwapi;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Named;
|
||||
|
||||
import fr.free.nrw.commons.mwapi.model.ApiResponse;
|
||||
import fr.free.nrw.commons.mwapi.model.Page;
|
||||
import fr.free.nrw.commons.mwapi.model.PageCategory;
|
||||
import io.reactivex.Single;
|
||||
import okhttp3.HttpUrl;
|
||||
import okhttp3.OkHttpClient;
|
||||
import okhttp3.Request;
|
||||
import okhttp3.Response;
|
||||
import okhttp3.ResponseBody;
|
||||
import timber.log.Timber;
|
||||
|
||||
/**
|
||||
* Uses the OkHttp library to implement calls to the Commons MediaWiki API to match GPS coordinates
|
||||
* with nearby Commons categories. Parses the results using GSON to obtain a list of relevant
|
||||
* categories. Note: that caller is responsible for executing the request() method on a background
|
||||
* thread.
|
||||
*/
|
||||
public class CategoryApi {
|
||||
|
||||
private final OkHttpClient okHttpClient;
|
||||
private final HttpUrl mwUrl;
|
||||
private final Gson gson;
|
||||
|
||||
@Inject
|
||||
public CategoryApi(OkHttpClient okHttpClient, Gson gson,
|
||||
@Named("commons_mediawiki_url") HttpUrl mwUrl) {
|
||||
this.okHttpClient = okHttpClient;
|
||||
this.mwUrl = mwUrl;
|
||||
this.gson = gson;
|
||||
}
|
||||
|
||||
public Single<List<String>> request(String coords) {
|
||||
return Single.fromCallable(() -> {
|
||||
HttpUrl apiUrl = buildUrl(coords);
|
||||
Timber.d("URL: %s", apiUrl.toString());
|
||||
|
||||
Request request = new Request.Builder().get().url(apiUrl).build();
|
||||
Response response = okHttpClient.newCall(request).execute();
|
||||
ResponseBody body = response.body();
|
||||
if (body == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
ApiResponse apiResponse = gson.fromJson(body.charStream(), ApiResponse.class);
|
||||
Set<String> categories = new LinkedHashSet<>();
|
||||
if (apiResponse != null && apiResponse.hasPages()) {
|
||||
for (Page page : apiResponse.query.pages) {
|
||||
for (PageCategory category : page.getCategories()) {
|
||||
categories.add(category.withoutPrefix());
|
||||
}
|
||||
}
|
||||
}
|
||||
return new ArrayList<>(categories);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds URL with image coords for MediaWiki API calls
|
||||
* Example URL: https://commons.wikimedia.org/w/api.php?action=query&prop=categories|coordinates|pageprops&format=json&clshow=!hidden&coprop=type|name|dim|country|region|globe&codistancefrompoint=38.11386944444445|13.356263888888888&generator=geosearch&redirects=&ggscoord=38.11386944444445|1.356263888888888&ggsradius=100&ggslimit=10&ggsnamespace=6&ggsprop=type|name|dim|country|region|globe&ggsprimary=all&formatversion=2
|
||||
*
|
||||
* @param coords Coordinates to build query with
|
||||
* @return URL for API query
|
||||
*/
|
||||
private HttpUrl buildUrl(String coords) {
|
||||
return mwUrl.newBuilder()
|
||||
.addPathSegment("w")
|
||||
.addPathSegment("api.php")
|
||||
.addQueryParameter("action", "query")
|
||||
.addQueryParameter("prop", "categories|coordinates|pageprops")
|
||||
.addQueryParameter("format", "json")
|
||||
.addQueryParameter("clshow", "!hidden")
|
||||
.addQueryParameter("coprop", "type|name|dim|country|region|globe")
|
||||
.addQueryParameter("codistancefrompoint", coords)
|
||||
.addQueryParameter("generator", "geosearch")
|
||||
.addQueryParameter("ggscoord", coords)
|
||||
.addQueryParameter("ggsradius", "10000")
|
||||
.addQueryParameter("ggslimit", "10")
|
||||
.addQueryParameter("ggsnamespace", "6")
|
||||
.addQueryParameter("ggsprop", "type|name|dim|country|region|globe")
|
||||
.addQueryParameter("ggsprimary", "all")
|
||||
.addQueryParameter("formatversion", "2")
|
||||
.build();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
|
@ -27,6 +27,10 @@ public interface MediaWikiApi {
|
|||
|
||||
String getEditToken() throws IOException;
|
||||
|
||||
String getWikidataCsrfToken() throws IOException;
|
||||
|
||||
String getCentralAuthToken() throws IOException;
|
||||
|
||||
boolean fileExistsWithName(String fileName) throws IOException;
|
||||
|
||||
boolean pageExists(String pageName) throws IOException;
|
||||
|
|
@ -49,6 +53,12 @@ public interface MediaWikiApi {
|
|||
@Nullable
|
||||
String appendEdit(String editToken, String processedPageContent, String filename, String summary) throws IOException;
|
||||
|
||||
@Nullable
|
||||
String wikidatCreateClaim(String entityId, String property, String snaktype, String value) throws IOException;
|
||||
|
||||
@Nullable
|
||||
boolean addWikidataEditTag(String revisionId) throws IOException;
|
||||
|
||||
@NonNull
|
||||
MediaResult fetchMediaByFilename(String filename) throws IOException;
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,12 @@
|
|||
package fr.free.nrw.commons.mwapi.model;
|
||||
|
||||
public class ApiResponse {
|
||||
public Query query;
|
||||
|
||||
public ApiResponse() {
|
||||
}
|
||||
|
||||
public boolean hasPages() {
|
||||
return query != null && query.pages != null;
|
||||
}
|
||||
}
|
||||
17
app/src/main/java/fr/free/nrw/commons/mwapi/model/Page.java
Normal file
17
app/src/main/java/fr/free/nrw/commons/mwapi/model/Page.java
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
package fr.free.nrw.commons.mwapi.model;
|
||||
|
||||
import android.support.annotation.NonNull;
|
||||
|
||||
public class Page {
|
||||
public String title;
|
||||
public PageCategory[] categories;
|
||||
public PageCategory category;
|
||||
|
||||
public Page() {
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public PageCategory[] getCategories() {
|
||||
return categories != null ? categories : new PageCategory[0];
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
package fr.free.nrw.commons.mwapi.model;
|
||||
|
||||
public class PageCategory {
|
||||
public String title;
|
||||
|
||||
public PageCategory() {
|
||||
}
|
||||
|
||||
public String withoutPrefix() {
|
||||
return title != null ? title.replace("Category:", "") : "";
|
||||
}
|
||||
}
|
||||
10
app/src/main/java/fr/free/nrw/commons/mwapi/model/Query.java
Normal file
10
app/src/main/java/fr/free/nrw/commons/mwapi/model/Query.java
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
package fr.free.nrw.commons.mwapi.model;
|
||||
|
||||
public class Query {
|
||||
public Page[] pages;
|
||||
|
||||
public Query() {
|
||||
pages = new Page[0];
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,6 +1,5 @@
|
|||
package fr.free.nrw.commons.nearby;
|
||||
|
||||
import android.content.SharedPreferences;
|
||||
import android.os.Build;
|
||||
import android.support.v4.app.Fragment;
|
||||
import android.support.v4.content.ContextCompat;
|
||||
|
|
|
|||
|
|
@ -36,11 +36,13 @@ import butterknife.ButterKnife;
|
|||
import fr.free.nrw.commons.R;
|
||||
import fr.free.nrw.commons.location.LatLng;
|
||||
import fr.free.nrw.commons.location.LocationServiceManager;
|
||||
import fr.free.nrw.commons.location.LocationServiceManager.LocationChangeType;
|
||||
import fr.free.nrw.commons.location.LocationUpdateListener;
|
||||
import fr.free.nrw.commons.theme.NavigationBaseActivity;
|
||||
import fr.free.nrw.commons.utils.NetworkUtils;
|
||||
import fr.free.nrw.commons.utils.UriSerializer;
|
||||
import fr.free.nrw.commons.utils.ViewUtil;
|
||||
import fr.free.nrw.commons.wikidata.WikidataEditListener;
|
||||
import io.reactivex.Observable;
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers;
|
||||
import io.reactivex.disposables.Disposable;
|
||||
|
|
@ -49,8 +51,12 @@ import timber.log.Timber;
|
|||
import uk.co.deanwild.materialshowcaseview.IShowcaseListener;
|
||||
import uk.co.deanwild.materialshowcaseview.MaterialShowcaseView;
|
||||
|
||||
import static fr.free.nrw.commons.location.LocationServiceManager.LocationChangeType.*;
|
||||
import static fr.free.nrw.commons.location.LocationServiceManager.LocationChangeType.MAP_UPDATED;
|
||||
|
||||
public class NearbyActivity extends NavigationBaseActivity implements LocationUpdateListener {
|
||||
|
||||
public class NearbyActivity extends NavigationBaseActivity implements LocationUpdateListener,
|
||||
WikidataEditListener.WikidataP18EditListener {
|
||||
|
||||
private static final int LOCATION_REQUEST = 1;
|
||||
|
||||
|
|
@ -70,6 +76,8 @@ public class NearbyActivity extends NavigationBaseActivity implements LocationUp
|
|||
LocationServiceManager locationManager;
|
||||
@Inject
|
||||
NearbyController nearbyController;
|
||||
@Inject WikidataEditListener wikidataEditListener;
|
||||
|
||||
@Inject
|
||||
@Named("application_preferences") SharedPreferences applicationPrefs;
|
||||
private LatLng curLatLng;
|
||||
|
|
@ -104,6 +112,7 @@ public class NearbyActivity extends NavigationBaseActivity implements LocationUp
|
|||
|
||||
initBottomSheetBehaviour();
|
||||
initDrawer();
|
||||
wikidataEditListener.setAuthenticationStateListener(this);
|
||||
}
|
||||
|
||||
private void resumeFragment() {
|
||||
|
|
@ -213,7 +222,7 @@ public class NearbyActivity extends NavigationBaseActivity implements LocationUp
|
|||
//Still need to check if GPS is enabled
|
||||
checkGps();
|
||||
lastKnownLocation = locationManager.getLKL();
|
||||
refreshView(LocationServiceManager.LocationChangeType.PERMISSION_JUST_GRANTED);
|
||||
refreshView(PERMISSION_JUST_GRANTED);
|
||||
} else {
|
||||
//If permission not granted, go to page that says Nearby Places cannot be displayed
|
||||
hideProgressBar();
|
||||
|
|
@ -273,7 +282,7 @@ public class NearbyActivity extends NavigationBaseActivity implements LocationUp
|
|||
private void checkLocationPermission() {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
if (locationManager.isLocationPermissionGranted()) {
|
||||
refreshView(LocationServiceManager.LocationChangeType.LOCATION_SIGNIFICANTLY_CHANGED);
|
||||
refreshView(LOCATION_SIGNIFICANTLY_CHANGED);
|
||||
} else {
|
||||
// Should we show an explanation?
|
||||
if (locationManager.isPermissionExplanationRequired(this)) {
|
||||
|
|
@ -299,7 +308,7 @@ public class NearbyActivity extends NavigationBaseActivity implements LocationUp
|
|||
}
|
||||
}
|
||||
} else {
|
||||
refreshView(LocationServiceManager.LocationChangeType.LOCATION_SIGNIFICANTLY_CHANGED);
|
||||
refreshView(LOCATION_SIGNIFICANTLY_CHANGED);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -308,7 +317,7 @@ public class NearbyActivity extends NavigationBaseActivity implements LocationUp
|
|||
super.onActivityResult(requestCode, resultCode, data);
|
||||
if (requestCode == 1) {
|
||||
Timber.d("User is back from Settings page");
|
||||
refreshView(LocationServiceManager.LocationChangeType.LOCATION_SIGNIFICANTLY_CHANGED);
|
||||
refreshView(LOCATION_SIGNIFICANTLY_CHANGED);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -316,7 +325,7 @@ public class NearbyActivity extends NavigationBaseActivity implements LocationUp
|
|||
protected void onStart() {
|
||||
super.onStart();
|
||||
locationManager.addLocationListener(this);
|
||||
locationManager.registerLocationManager();
|
||||
registerLocationUpdates();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -367,8 +376,7 @@ public class NearbyActivity extends NavigationBaseActivity implements LocationUp
|
|||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
if (NetworkUtils.isInternetConnectionEstablished(NearbyActivity.this)) {
|
||||
refreshView(LocationServiceManager
|
||||
.LocationChangeType.LOCATION_SIGNIFICANTLY_CHANGED);
|
||||
refreshView(LOCATION_SIGNIFICANTLY_CHANGED);
|
||||
} else {
|
||||
ViewUtil.showLongToast(NearbyActivity.this, getString(R.string.no_internet));
|
||||
}
|
||||
|
|
@ -384,7 +392,7 @@ public class NearbyActivity extends NavigationBaseActivity implements LocationUp
|
|||
*
|
||||
* @param locationChangeType defines if location shanged significantly or slightly
|
||||
*/
|
||||
private void refreshView(LocationServiceManager.LocationChangeType locationChangeType) {
|
||||
private void refreshView(LocationChangeType locationChangeType) {
|
||||
if (lockNearbyView) {
|
||||
return;
|
||||
}
|
||||
|
|
@ -394,15 +402,16 @@ public class NearbyActivity extends NavigationBaseActivity implements LocationUp
|
|||
return;
|
||||
}
|
||||
|
||||
locationManager.registerLocationManager();
|
||||
registerLocationUpdates();
|
||||
LatLng lastLocation = locationManager.getLastLocation();
|
||||
|
||||
if (curLatLng != null && curLatLng.equals(lastLocation)) { //refresh view only if location has changed
|
||||
if (curLatLng != null && curLatLng.equals(lastLocation)
|
||||
&& !locationChangeType.equals(MAP_UPDATED)) { //refresh view only if location has changed
|
||||
return;
|
||||
}
|
||||
curLatLng = lastLocation;
|
||||
|
||||
if (locationChangeType.equals(LocationServiceManager.LocationChangeType.PERMISSION_JUST_GRANTED)) {
|
||||
if (locationChangeType.equals(PERMISSION_JUST_GRANTED)) {
|
||||
curLatLng = lastKnownLocation;
|
||||
}
|
||||
|
||||
|
|
@ -411,8 +420,9 @@ public class NearbyActivity extends NavigationBaseActivity implements LocationUp
|
|||
return;
|
||||
}
|
||||
|
||||
if (locationChangeType.equals(LocationServiceManager.LocationChangeType.LOCATION_SIGNIFICANTLY_CHANGED)
|
||||
|| locationChangeType.equals(LocationServiceManager.LocationChangeType.PERMISSION_JUST_GRANTED)) {
|
||||
if (locationChangeType.equals(LOCATION_SIGNIFICANTLY_CHANGED)
|
||||
|| locationChangeType.equals(PERMISSION_JUST_GRANTED)
|
||||
|| locationChangeType.equals(MAP_UPDATED)) {
|
||||
progressBar.setVisibility(View.VISIBLE);
|
||||
|
||||
//TODO: This hack inserts curLatLng before populatePlaces is called (see #1440). Ideally a proper fix should be found
|
||||
|
|
@ -427,8 +437,14 @@ public class NearbyActivity extends NavigationBaseActivity implements LocationUp
|
|||
.loadAttractionsFromLocation(curLatLng))
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(this::populatePlaces);
|
||||
} else if (locationChangeType.equals(LocationServiceManager.LocationChangeType.LOCATION_SLIGHTLY_CHANGED)) {
|
||||
.subscribe(this::populatePlaces,
|
||||
throwable -> {
|
||||
Timber.d(throwable);
|
||||
showErrorMessage(getString(R.string.error_fetching_nearby_places));
|
||||
progressBar.setVisibility(View.GONE);
|
||||
});
|
||||
} else if (locationChangeType
|
||||
.equals(LOCATION_SLIGHTLY_CHANGED)) {
|
||||
Gson gson = new GsonBuilder()
|
||||
.registerTypeAdapter(Uri.class, new UriSerializer())
|
||||
.create();
|
||||
|
|
@ -438,6 +454,39 @@ public class NearbyActivity extends NavigationBaseActivity implements LocationUp
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This method first checks if the location permissions has been granted and then register the location manager for updates.
|
||||
*/
|
||||
private void registerLocationUpdates() {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
if (locationManager.isLocationPermissionGranted()) {
|
||||
locationManager.registerLocationManager();
|
||||
} else {
|
||||
// Should we show an explanation?
|
||||
if (locationManager.isPermissionExplanationRequired(this)) {
|
||||
new AlertDialog.Builder(this)
|
||||
.setMessage(getString(R.string.location_permission_rationale_nearby))
|
||||
.setPositiveButton("OK", (dialog, which) -> {
|
||||
requestLocationPermissions();
|
||||
dialog.dismiss();
|
||||
})
|
||||
.setNegativeButton("Cancel", (dialog, id) -> {
|
||||
showLocationPermissionDeniedErrorDialog();
|
||||
dialog.cancel();
|
||||
})
|
||||
.create()
|
||||
.show();
|
||||
|
||||
} else {
|
||||
// No explanation needed, we can request the permission.
|
||||
requestLocationPermissions();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
locationManager.registerLocationManager();
|
||||
}
|
||||
}
|
||||
|
||||
private void populatePlaces(NearbyController.NearbyPlacesInfo nearbyPlacesInfo) {
|
||||
List<Place> placeList = nearbyPlacesInfo.placeList;
|
||||
LatLng[] boundaryCoordinates = nearbyPlacesInfo.boundaryCoordinates;
|
||||
|
|
@ -451,7 +500,7 @@ public class NearbyActivity extends NavigationBaseActivity implements LocationUp
|
|||
if (placeList.size() == 0) {
|
||||
ViewUtil.showSnackbar(findViewById(R.id.container), R.string.no_nearby);
|
||||
}
|
||||
|
||||
|
||||
bundle.putString("PlaceList", gsonPlaceList);
|
||||
//bundle.putString("CurLatLng", gsonCurLatLng);
|
||||
bundle.putString("BoundaryCoord", gsonBoundaryCoordinates);
|
||||
|
|
@ -518,7 +567,7 @@ public class NearbyActivity extends NavigationBaseActivity implements LocationUp
|
|||
locationManager.removeLocationListener(this);
|
||||
} else {
|
||||
lockNearbyView = false;
|
||||
locationManager.registerLocationManager();
|
||||
registerLocationUpdates();
|
||||
locationManager.addLocationListener(this);
|
||||
}
|
||||
}
|
||||
|
|
@ -580,7 +629,12 @@ public class NearbyActivity extends NavigationBaseActivity implements LocationUp
|
|||
.loadAttractionsFromLocation(curLatLng))
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(this::populatePlaces);
|
||||
.subscribe(this::populatePlaces,
|
||||
throwable -> {
|
||||
Timber.d(throwable);
|
||||
showErrorMessage(getString(R.string.error_fetching_nearby_places));
|
||||
progressBar.setVisibility(View.GONE);
|
||||
});
|
||||
nearbyMapFragment.setBundleForUpdtes(bundle);
|
||||
nearbyMapFragment.updateMapSignificantly();
|
||||
updateListFragment();
|
||||
|
|
@ -635,15 +689,24 @@ public class NearbyActivity extends NavigationBaseActivity implements LocationUp
|
|||
|
||||
@Override
|
||||
public void onLocationChangedSignificantly(LatLng latLng) {
|
||||
refreshView(LocationServiceManager.LocationChangeType.LOCATION_SIGNIFICANTLY_CHANGED);
|
||||
refreshView(LOCATION_SIGNIFICANTLY_CHANGED);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLocationChangedSlightly(LatLng latLng) {
|
||||
refreshView(LocationServiceManager.LocationChangeType.LOCATION_SLIGHTLY_CHANGED);
|
||||
refreshView(LOCATION_SLIGHTLY_CHANGED);
|
||||
}
|
||||
|
||||
public void prepareViewsForSheetPosition(int bottomSheetState) {
|
||||
// TODO
|
||||
}
|
||||
|
||||
private void showErrorMessage(String message) {
|
||||
ViewUtil.showLongToast(NearbyActivity.this, message);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onWikidataEditSuccessful() {
|
||||
refreshView(MAP_UPDATED);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import android.support.graphics.drawable.VectorDrawableCompat;
|
|||
|
||||
import com.mapbox.mapboxsdk.annotations.IconFactory;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
|
|
@ -44,7 +45,7 @@ public class NearbyController {
|
|||
* @return NearbyPlacesInfo a variable holds Place list without distance information
|
||||
* and boundary coordinates of current Place List
|
||||
*/
|
||||
public NearbyPlacesInfo loadAttractionsFromLocation(LatLng curLatLng) {
|
||||
public NearbyPlacesInfo loadAttractionsFromLocation(LatLng curLatLng) throws IOException {
|
||||
|
||||
Timber.d("Loading attractions near %s", curLatLng);
|
||||
NearbyPlacesInfo nearbyPlacesInfo = new NearbyPlacesInfo();
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ package fr.free.nrw.commons.nearby;
|
|||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
|
|
@ -21,6 +22,9 @@ import java.lang.reflect.Type;
|
|||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Named;
|
||||
|
||||
import dagger.android.support.AndroidSupportInjection;
|
||||
import dagger.android.support.DaggerFragment;
|
||||
import fr.free.nrw.commons.R;
|
||||
|
|
@ -47,6 +51,11 @@ public class NearbyListFragment extends DaggerFragment {
|
|||
private RecyclerView recyclerView;
|
||||
private ContributionController controller;
|
||||
|
||||
|
||||
@Inject
|
||||
@Named("direct_nearby_upload_prefs")
|
||||
SharedPreferences directPrefs;
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
|
@ -137,7 +146,7 @@ public class NearbyListFragment extends DaggerFragment {
|
|||
if (resultCode == RESULT_OK) {
|
||||
Timber.d("OnActivityResult() parameters: Req code: %d Result code: %d Data: %s",
|
||||
requestCode, resultCode, data);
|
||||
controller.handleImagePicked(requestCode, data, true);
|
||||
controller.handleImagePicked(requestCode, data, true, directPrefs.getString("WikiDataEntityId", null));
|
||||
} else {
|
||||
Timber.e("OnActivityResult() parameters: Req code: %d Result code: %d Data: %s",
|
||||
requestCode, resultCode, data);
|
||||
|
|
|
|||
|
|
@ -731,6 +731,7 @@ public class NearbyMapFragment extends DaggerFragment {
|
|||
editor.putString("Title", place.getName());
|
||||
editor.putString("Desc", place.getLongDescription());
|
||||
editor.putString("Category", place.getCategory());
|
||||
editor.putString("WikiDataEntityId", place.getWikiDataEntityId());
|
||||
editor.apply();
|
||||
}
|
||||
|
||||
|
|
@ -766,7 +767,7 @@ public class NearbyMapFragment extends DaggerFragment {
|
|||
if (resultCode == RESULT_OK) {
|
||||
Timber.d("OnActivityResult() parameters: Req code: %d Result code: %d Data: %s",
|
||||
requestCode, resultCode, data);
|
||||
controller.handleImagePicked(requestCode, data, true);
|
||||
controller.handleImagePicked(requestCode, data, true, directPrefs.getString("WikiDataEntityId", null));
|
||||
} else {
|
||||
Timber.e("OnActivityResult() parameters: Req code: %d Result code: %d Data: %s",
|
||||
requestCode, resultCode, data);
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ import java.util.regex.Pattern;
|
|||
|
||||
import fr.free.nrw.commons.Utils;
|
||||
import fr.free.nrw.commons.location.LatLng;
|
||||
import fr.free.nrw.commons.utils.FileUtils;
|
||||
import fr.free.nrw.commons.upload.FileUtils;
|
||||
import timber.log.Timber;
|
||||
|
||||
public class NearbyPlaces {
|
||||
|
|
@ -40,10 +40,9 @@ public class NearbyPlaces {
|
|||
}
|
||||
}
|
||||
|
||||
List<Place> getFromWikidataQuery(LatLng curLatLng, String lang) {
|
||||
List<Place> getFromWikidataQuery(LatLng curLatLng, String lang) throws IOException {
|
||||
List<Place> places = Collections.emptyList();
|
||||
|
||||
try {
|
||||
// increase the radius gradually to find a satisfactory number of nearby places
|
||||
while (radius <= MAX_RADIUS) {
|
||||
places = getFromWikidataQuery(curLatLng, lang, radius);
|
||||
|
|
@ -54,13 +53,6 @@ public class NearbyPlaces {
|
|||
radius *= RADIUS_MULTIPLIER;
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
Timber.d(e.toString());
|
||||
// errors tend to be caused by too many results (and time out)
|
||||
// try a small radius next time
|
||||
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;
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ package fr.free.nrw.commons.nearby;
|
|||
import android.graphics.Bitmap;
|
||||
import android.net.Uri;
|
||||
import android.support.annotation.DrawableRes;
|
||||
import android.support.annotation.Nullable;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
|
@ -50,6 +51,20 @@ public class Place {
|
|||
this.distance = distance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts the entity id from the wikidata link
|
||||
* @return returns the entity id if wikidata link exists
|
||||
*/
|
||||
@Nullable
|
||||
public String getWikiDataEntityId() {
|
||||
if (!hasWikidataLink()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
String wikiDataLink = siteLinks.getWikidataLink().toString();
|
||||
return wikiDataLink.replace("http://www.wikidata.org/entity/", "");
|
||||
}
|
||||
|
||||
public boolean hasWikipediaLink() {
|
||||
return !(siteLinks == null || Uri.EMPTY.equals(siteLinks.getWikipediaLink()));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,7 +25,6 @@ import javax.inject.Named;
|
|||
|
||||
import butterknife.BindView;
|
||||
import butterknife.ButterKnife;
|
||||
import fr.free.nrw.commons.CommonsApplication;
|
||||
import fr.free.nrw.commons.R;
|
||||
import fr.free.nrw.commons.Utils;
|
||||
import fr.free.nrw.commons.contributions.ContributionController;
|
||||
|
|
|
|||
|
|
@ -16,7 +16,6 @@ import android.widget.RelativeLayout;
|
|||
|
||||
import com.pedrogomez.renderers.RVRendererAdapter;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
|
|
@ -26,6 +25,7 @@ import butterknife.BindView;
|
|||
import butterknife.ButterKnife;
|
||||
import fr.free.nrw.commons.R;
|
||||
import fr.free.nrw.commons.Utils;
|
||||
import fr.free.nrw.commons.mwapi.MediaWikiApi;
|
||||
import fr.free.nrw.commons.theme.NavigationBaseActivity;
|
||||
import fr.free.nrw.commons.utils.NetworkUtils;
|
||||
import fr.free.nrw.commons.utils.ViewUtil;
|
||||
|
|
@ -46,6 +46,8 @@ public class NotificationActivity extends NavigationBaseActivity {
|
|||
@BindView(R.id.container) RelativeLayout relativeLayout;
|
||||
|
||||
@Inject NotificationController controller;
|
||||
@Inject
|
||||
MediaWikiApi mediaWikiApi;
|
||||
|
||||
private static final String TAG_NOTIFICATION_WORKER_FRAGMENT = "NotificationWorkerFragment";
|
||||
private NotificationWorkerFragment mNotificationWorkerFragment;
|
||||
|
|
@ -81,7 +83,6 @@ public class NotificationActivity extends NavigationBaseActivity {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
@SuppressLint("CheckResult")
|
||||
private void addNotifications() {
|
||||
Timber.d("Add notifications");
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
package fr.free.nrw.commons.notification;
|
||||
|
||||
import android.util.Log;
|
||||
import android.graphics.drawable.PictureDrawable;
|
||||
import android.text.Html;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
|
@ -8,17 +9,23 @@ import android.widget.ImageView;
|
|||
import android.widget.TextView;
|
||||
|
||||
import com.borjabravo.readmoretextview.ReadMoreTextView;
|
||||
import com.bumptech.glide.RequestBuilder;
|
||||
import com.pedrogomez.renderers.Renderer;
|
||||
|
||||
import butterknife.BindView;
|
||||
import butterknife.ButterKnife;
|
||||
import fr.free.nrw.commons.R;
|
||||
import fr.free.nrw.commons.glide.SvgSoftwareLayerSetter;
|
||||
|
||||
import static com.bumptech.glide.load.resource.drawable.DrawableTransitionOptions.withCrossFade;
|
||||
|
||||
/**
|
||||
* Created by root on 19.12.2017.
|
||||
*/
|
||||
|
||||
public class NotificationRenderer extends Renderer<Notification> {
|
||||
private RequestBuilder<PictureDrawable> requestBuilder;
|
||||
|
||||
@BindView(R.id.title) ReadMoreTextView title;
|
||||
@BindView(R.id.time) TextView time;
|
||||
@BindView(R.id.icon) ImageView icon;
|
||||
|
|
@ -41,23 +48,32 @@ public class NotificationRenderer extends Renderer<Notification> {
|
|||
protected View inflate(LayoutInflater layoutInflater, ViewGroup viewGroup) {
|
||||
View inflatedView = layoutInflater.inflate(R.layout.item_notification, viewGroup, false);
|
||||
ButterKnife.bind(this, inflatedView);
|
||||
requestBuilder = GlideApp.with(inflatedView.getContext())
|
||||
.as(PictureDrawable.class)
|
||||
.error(R.drawable.round_icon_unknown)
|
||||
.transition(withCrossFade())
|
||||
.listener(new SvgSoftwareLayerSetter());
|
||||
return inflatedView;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render() {
|
||||
Notification notification = getContent();
|
||||
String str = notification.notificationText.trim();
|
||||
str = str.concat(" ");
|
||||
title.setText(str);
|
||||
setTitle(notification.notificationText);
|
||||
time.setText(notification.date);
|
||||
switch (notification.notificationType) {
|
||||
case THANK_YOU_EDIT:
|
||||
icon.setImageResource(R.drawable.ic_edit_black_24dp);
|
||||
break;
|
||||
default:
|
||||
icon.setImageResource(R.drawable.round_icon_unknown);
|
||||
}
|
||||
requestBuilder.load(notification.iconUrl).into(icon);
|
||||
}
|
||||
|
||||
/**
|
||||
* Cleans up the notification text and sets it as the title
|
||||
* Clean up is required to fix escaped HTML string and extra white spaces at the beginning of the notification
|
||||
* @param notificationText
|
||||
*/
|
||||
private void setTitle(String notificationText) {
|
||||
notificationText = notificationText.trim().replaceAll("(^\\h*)|(\\h*$)", "");
|
||||
notificationText = Html.fromHtml(notificationText).toString();
|
||||
notificationText = notificationText.concat(" ");
|
||||
title.setText(notificationText);
|
||||
}
|
||||
|
||||
public interface NotificationClicked{
|
||||
|
|
|
|||
|
|
@ -16,12 +16,13 @@ import javax.annotation.Nullable;
|
|||
import fr.free.nrw.commons.BuildConfig;
|
||||
import fr.free.nrw.commons.R;
|
||||
|
||||
import static fr.free.nrw.commons.notification.NotificationType.THANK_YOU_EDIT;
|
||||
import static fr.free.nrw.commons.notification.NotificationType.UNKNOWN;
|
||||
|
||||
public class NotificationUtils {
|
||||
|
||||
private static final String COMMONS_WIKI = "commonswiki";
|
||||
private static final String WIKIDATA_WIKI = "wikidatawiki";
|
||||
private static final String WIKIPEDIA_WIKI = "enwiki";
|
||||
|
||||
public static boolean isCommonsNotification(Node document) {
|
||||
if (document == null || !document.hasAttributes()) {
|
||||
|
|
@ -31,6 +32,32 @@ public class NotificationUtils {
|
|||
return COMMONS_WIKI.equals(element.getAttribute("wiki"));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the wiki attribute corresponds to wikidatawiki
|
||||
* @param document
|
||||
* @return
|
||||
*/
|
||||
public static boolean isWikidataNotification(Node document) {
|
||||
if (document == null || !document.hasAttributes()) {
|
||||
return false;
|
||||
}
|
||||
Element element = (Element) document;
|
||||
return WIKIDATA_WIKI.equals(element.getAttribute("wiki"));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the wiki attribute corresponds to enwiki
|
||||
* @param document
|
||||
* @return
|
||||
*/
|
||||
public static boolean isWikipediaNotification(Node document) {
|
||||
if (document == null || !document.hasAttributes()) {
|
||||
return false;
|
||||
}
|
||||
Element element = (Element) document;
|
||||
return WIKIPEDIA_WIKI.equals(element.getAttribute("wiki"));
|
||||
}
|
||||
|
||||
public static NotificationType getNotificationType(Node document) {
|
||||
Element element = (Element) document;
|
||||
String type = element.getAttribute("type");
|
||||
|
|
@ -68,10 +95,17 @@ public class NotificationUtils {
|
|||
return notifications;
|
||||
}
|
||||
|
||||
/**
|
||||
* Currently the app is interested in showing notifications just from the following three wikis: commons, wikidata, wikipedia
|
||||
* This function returns true only if the notification belongs to any of the above wikis and is of a known notification type
|
||||
* @param node
|
||||
* @return
|
||||
*/
|
||||
private static boolean isUsefulNotification(Node node) {
|
||||
return isCommonsNotification(node)
|
||||
&& !getNotificationType(node).equals(UNKNOWN)
|
||||
&& !getNotificationType(node).equals(THANK_YOU_EDIT);
|
||||
return (isCommonsNotification(node)
|
||||
|| isWikidataNotification(node)
|
||||
|| isWikipediaNotification(node))
|
||||
&& !getNotificationType(node).equals(UNKNOWN);
|
||||
}
|
||||
|
||||
public static boolean isBundledNotification(Node document) {
|
||||
|
|
@ -97,7 +131,7 @@ public class NotificationUtils {
|
|||
|
||||
switch (type) {
|
||||
case THANK_YOU_EDIT:
|
||||
notificationText = context.getString(R.string.notifications_thank_you_edit);
|
||||
notificationText = getThankYouEditDescription(document);
|
||||
break;
|
||||
case EDIT_USER_TALK:
|
||||
notificationText = getNotificationText(document);
|
||||
|
|
@ -146,6 +180,16 @@ public class NotificationUtils {
|
|||
return body != null ? body.getTextContent() : "";
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the header node returned in the XML document to form the description for thank you edits
|
||||
* @param document
|
||||
* @return
|
||||
*/
|
||||
private static String getThankYouEditDescription(Node document) {
|
||||
Node body = getNode(getModel(document), "header");
|
||||
return body != null ? body.getTextContent() : "";
|
||||
}
|
||||
|
||||
private static String getNotificationIconUrl(Node document) {
|
||||
String format = "%s%s";
|
||||
Node iconUrl = getNode(getModel(document), "iconUrl");
|
||||
|
|
|
|||
|
|
@ -0,0 +1,35 @@
|
|||
package fr.free.nrw.commons.notification;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.drawable.PictureDrawable;
|
||||
import android.support.annotation.NonNull;
|
||||
|
||||
import com.bumptech.glide.Glide;
|
||||
import com.bumptech.glide.Registry;
|
||||
import com.bumptech.glide.annotation.GlideModule;
|
||||
import com.bumptech.glide.module.AppGlideModule;
|
||||
import com.caverock.androidsvg.SVG;
|
||||
|
||||
import java.io.InputStream;
|
||||
|
||||
import fr.free.nrw.commons.glide.SvgDecoder;
|
||||
import fr.free.nrw.commons.glide.SvgDrawableTranscoder;
|
||||
|
||||
/**
|
||||
* Module for the SVG sample app.
|
||||
*/
|
||||
@GlideModule
|
||||
public class SvgModule extends AppGlideModule {
|
||||
@Override
|
||||
public void registerComponents(@NonNull Context context, @NonNull Glide glide,
|
||||
@NonNull Registry registry) {
|
||||
registry.register(SVG.class, PictureDrawable.class, new SvgDrawableTranscoder())
|
||||
.append(InputStream.class, SVG.class, new SvgDecoder());
|
||||
}
|
||||
|
||||
// Disable manifest parsing to avoid adding similar modules twice.
|
||||
@Override
|
||||
public boolean isManifestParsingEnabled() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
@ -3,13 +3,10 @@ package fr.free.nrw.commons.settings;
|
|||
import android.Manifest;
|
||||
import android.app.AlertDialog;
|
||||
import android.content.ActivityNotFoundException;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.ResolveInfo;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
|
|
@ -24,8 +21,6 @@ import android.support.v4.content.FileProvider;
|
|||
import android.widget.Toast;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Named;
|
||||
|
|
@ -35,7 +30,7 @@ import fr.free.nrw.commons.CommonsApplication;
|
|||
import fr.free.nrw.commons.R;
|
||||
import fr.free.nrw.commons.Utils;
|
||||
import fr.free.nrw.commons.di.ApplicationlessInjection;
|
||||
import fr.free.nrw.commons.utils.FileUtils;
|
||||
import fr.free.nrw.commons.upload.FileUtils;
|
||||
|
||||
public class SettingsFragment extends PreferenceFragment {
|
||||
|
||||
|
|
|
|||
|
|
@ -1,10 +1,8 @@
|
|||
package fr.free.nrw.commons.upload;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.graphics.BitmapRegionDecoder;
|
||||
import android.net.Uri;
|
||||
import android.os.AsyncTask;
|
||||
import android.support.v7.app.AlertDialog;
|
||||
|
||||
|
|
|
|||
263
app/src/main/java/fr/free/nrw/commons/upload/FileProcessor.java
Normal file
263
app/src/main/java/fr/free/nrw/commons/upload/FileProcessor.java
Normal file
|
|
@ -0,0 +1,263 @@
|
|||
package fr.free.nrw.commons.upload;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.app.Activity;
|
||||
import android.content.ContentResolver;
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.os.ParcelFileDescriptor;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.v7.app.AppCompatActivity;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Named;
|
||||
|
||||
import fr.free.nrw.commons.caching.CacheController;
|
||||
import fr.free.nrw.commons.di.ApplicationlessInjection;
|
||||
import fr.free.nrw.commons.mwapi.CategoryApi;
|
||||
import io.reactivex.schedulers.Schedulers;
|
||||
import timber.log.Timber;
|
||||
|
||||
import static com.mapbox.mapboxsdk.Mapbox.getApplicationContext;
|
||||
|
||||
/**
|
||||
* Processing of the image file that is about to be uploaded via ShareActivity is done here
|
||||
*/
|
||||
public class FileProcessor implements SimilarImageDialogFragment.onResponse {
|
||||
|
||||
@Inject
|
||||
CacheController cacheController;
|
||||
@Inject
|
||||
GpsCategoryModel gpsCategoryModel;
|
||||
@Inject
|
||||
CategoryApi apiCall;
|
||||
@Inject
|
||||
@Named("default_preferences")
|
||||
SharedPreferences prefs;
|
||||
private Uri mediaUri;
|
||||
private ContentResolver contentResolver;
|
||||
private GPSExtractor imageObj;
|
||||
private Context context;
|
||||
private String decimalCoords;
|
||||
private boolean haveCheckedForOtherImages = false;
|
||||
private String filePath;
|
||||
private boolean useExtStorage;
|
||||
private boolean cacheFound;
|
||||
private GPSExtractor tempImageObj;
|
||||
|
||||
FileProcessor(Uri mediaUri, ContentResolver contentResolver, Context context) {
|
||||
this.mediaUri = mediaUri;
|
||||
this.contentResolver = contentResolver;
|
||||
this.context = context;
|
||||
ApplicationlessInjection.getInstance(context.getApplicationContext()).getCommonsApplicationComponent().inject(this);
|
||||
useExtStorage = prefs.getBoolean("useExternalStorage", true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets file path from media URI.
|
||||
* In older devices getPath() may fail depending on the source URI, creating and using a copy of the file seems to work instead.
|
||||
*
|
||||
* @return file path of media
|
||||
*/
|
||||
@Nullable
|
||||
private String getPathOfMediaOrCopy() {
|
||||
filePath = FileUtils.getPath(context, mediaUri);
|
||||
Timber.d("Filepath: " + filePath);
|
||||
if (filePath == null) {
|
||||
String copyPath = null;
|
||||
try {
|
||||
ParcelFileDescriptor descriptor = contentResolver.openFileDescriptor(mediaUri, "r");
|
||||
if (descriptor != null) {
|
||||
if (useExtStorage) {
|
||||
copyPath = FileUtils.createCopyPath(descriptor);
|
||||
return copyPath;
|
||||
}
|
||||
copyPath = getApplicationContext().getCacheDir().getAbsolutePath() + "/" + new Date().getTime() + ".jpg";
|
||||
FileUtils.copy(descriptor.getFileDescriptor(), copyPath);
|
||||
Timber.d("Filepath (copied): %s", copyPath);
|
||||
return copyPath;
|
||||
}
|
||||
} catch (IOException e) {
|
||||
Timber.w(e, "Error in file " + copyPath);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return filePath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes file coordinates, either from EXIF data or user location
|
||||
*
|
||||
* @param gpsEnabled if true use GPS
|
||||
*/
|
||||
GPSExtractor processFileCoordinates(boolean gpsEnabled) {
|
||||
Timber.d("Calling GPSExtractor");
|
||||
try {
|
||||
ParcelFileDescriptor descriptor = contentResolver.openFileDescriptor(mediaUri, "r");
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
||||
if (descriptor != null) {
|
||||
imageObj = new GPSExtractor(descriptor.getFileDescriptor(), context, prefs);
|
||||
}
|
||||
} else {
|
||||
String filePath = getPathOfMediaOrCopy();
|
||||
if (filePath != null) {
|
||||
imageObj = new GPSExtractor(filePath, context, prefs);
|
||||
}
|
||||
}
|
||||
|
||||
decimalCoords = imageObj.getCoords(gpsEnabled);
|
||||
if (decimalCoords == null || !imageObj.imageCoordsExists) {
|
||||
//Find other photos taken around the same time which has gps coordinates
|
||||
if (!haveCheckedForOtherImages)
|
||||
findOtherImages(gpsEnabled);// Do not do repeat the process
|
||||
} else {
|
||||
useImageCoords();
|
||||
}
|
||||
|
||||
} catch (FileNotFoundException e) {
|
||||
Timber.w("File not found: " + mediaUri, e);
|
||||
}
|
||||
return imageObj;
|
||||
}
|
||||
|
||||
String getDecimalCoords() {
|
||||
return decimalCoords;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find other images around the same location that were taken within the last 20 sec
|
||||
*
|
||||
* @param gpsEnabled True if GPS is enabled
|
||||
*/
|
||||
private void findOtherImages(boolean gpsEnabled) {
|
||||
Timber.d("filePath" + getPathOfMediaOrCopy());
|
||||
|
||||
long timeOfCreation = new File(filePath).lastModified();//Time when the original image was created
|
||||
File folder = new File(filePath.substring(0, filePath.lastIndexOf('/')));
|
||||
File[] files = folder.listFiles();
|
||||
Timber.d("folderTime Number:" + files.length);
|
||||
|
||||
|
||||
for (File file : files) {
|
||||
if (file.lastModified() - timeOfCreation <= (120 * 1000) && file.lastModified() - timeOfCreation >= -(120 * 1000)) {
|
||||
//Make sure the photos were taken within 20seconds
|
||||
Timber.d("fild date:" + file.lastModified() + " time of creation" + timeOfCreation);
|
||||
tempImageObj = null;//Temporary GPSExtractor to extract coords from these photos
|
||||
ParcelFileDescriptor descriptor = null;
|
||||
try {
|
||||
descriptor = contentResolver.openFileDescriptor(Uri.parse(file.getAbsolutePath()), "r");
|
||||
} catch (FileNotFoundException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
||||
if (descriptor != null) {
|
||||
tempImageObj = new GPSExtractor(descriptor.getFileDescriptor(), context, prefs);
|
||||
}
|
||||
} else {
|
||||
if (filePath != null) {
|
||||
tempImageObj = new GPSExtractor(file.getAbsolutePath(), context, prefs);
|
||||
}
|
||||
}
|
||||
|
||||
if (tempImageObj != null) {
|
||||
Timber.d("not null fild EXIF" + tempImageObj.imageCoordsExists + " coords" + tempImageObj.getCoords(gpsEnabled));
|
||||
if (tempImageObj.getCoords(gpsEnabled) != null && tempImageObj.imageCoordsExists) {
|
||||
// Current image has gps coordinates and it's not current gps locaiton
|
||||
Timber.d("This file has image coords:" + file.getAbsolutePath());
|
||||
SimilarImageDialogFragment newFragment = new SimilarImageDialogFragment();
|
||||
Bundle args = new Bundle();
|
||||
args.putString("originalImagePath", filePath);
|
||||
args.putString("possibleImagePath", file.getAbsolutePath());
|
||||
newFragment.setArguments(args);
|
||||
newFragment.show(((AppCompatActivity) context).getSupportFragmentManager(), "dialog");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
haveCheckedForOtherImages = true; //Finished checking for other images
|
||||
}
|
||||
|
||||
/**
|
||||
* Initiates retrieval of image coordinates or user coordinates, and caching of coordinates.
|
||||
* Then initiates the calls to MediaWiki API through an instance of CategoryApi.
|
||||
*/
|
||||
@SuppressLint("CheckResult")
|
||||
public void useImageCoords() {
|
||||
if (decimalCoords != null) {
|
||||
Timber.d("Decimal coords of image: %s", decimalCoords);
|
||||
Timber.d("is EXIF data present:" + imageObj.imageCoordsExists + " from findOther image");
|
||||
|
||||
// Only set cache for this point if image has coords
|
||||
if (imageObj.imageCoordsExists) {
|
||||
double decLongitude = imageObj.getDecLongitude();
|
||||
double decLatitude = imageObj.getDecLatitude();
|
||||
cacheController.setQtPoint(decLongitude, decLatitude);
|
||||
}
|
||||
|
||||
List<String> displayCatList = cacheController.findCategory();
|
||||
boolean catListEmpty = displayCatList.isEmpty();
|
||||
|
||||
|
||||
// If no categories found in cache, call MediaWiki API to match image coords with nearby Commons categories
|
||||
if (catListEmpty) {
|
||||
cacheFound = false;
|
||||
apiCall.request(decimalCoords)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(Schedulers.io())
|
||||
.subscribe(
|
||||
gpsCategoryModel::setCategoryList,
|
||||
throwable -> {
|
||||
Timber.e(throwable);
|
||||
gpsCategoryModel.clear();
|
||||
}
|
||||
);
|
||||
Timber.d("displayCatList size 0, calling MWAPI %s", displayCatList);
|
||||
} else {
|
||||
cacheFound = true;
|
||||
Timber.d("Cache found, setting categoryList in model to %s", displayCatList);
|
||||
gpsCategoryModel.setCategoryList(displayCatList);
|
||||
}
|
||||
} else {
|
||||
Timber.d("EXIF: no coords");
|
||||
}
|
||||
}
|
||||
|
||||
boolean isCacheFound() {
|
||||
return cacheFound;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls the async task that detects if image is fuzzy, too dark, etc
|
||||
*/
|
||||
void detectUnwantedPictures() {
|
||||
String imageMediaFilePath = FileUtils.getPath(context, mediaUri);
|
||||
DetectUnwantedPicturesAsync detectUnwantedPicturesAsync
|
||||
= new DetectUnwantedPicturesAsync(new WeakReference<Activity>((Activity) context), imageMediaFilePath);
|
||||
detectUnwantedPicturesAsync.execute();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPositiveResponse() {
|
||||
imageObj = tempImageObj;
|
||||
decimalCoords = imageObj.getCoords(false);// Not necessary to use gps as image already ha EXIF data
|
||||
Timber.d("EXIF from tempImageObj");
|
||||
useImageCoords();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNegativeResponse() {
|
||||
Timber.d("EXIF from imageObj");
|
||||
useImageCoords();
|
||||
}
|
||||
}
|
||||
|
|
@ -15,18 +15,84 @@ import android.provider.MediaStore;
|
|||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.FileDescriptor;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.math.BigInteger;
|
||||
import java.nio.channels.FileChannel;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.util.Date;
|
||||
|
||||
import timber.log.Timber;
|
||||
|
||||
public class FileUtils {
|
||||
|
||||
/**
|
||||
* Get SHA1 of file from input stream
|
||||
*/
|
||||
static String getSHA1(InputStream is) {
|
||||
|
||||
MessageDigest digest;
|
||||
try {
|
||||
digest = MessageDigest.getInstance("SHA1");
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
Timber.e(e, "Exception while getting Digest");
|
||||
return "";
|
||||
}
|
||||
|
||||
byte[] buffer = new byte[8192];
|
||||
int read;
|
||||
try {
|
||||
while ((read = is.read(buffer)) > 0) {
|
||||
digest.update(buffer, 0, read);
|
||||
}
|
||||
byte[] md5sum = digest.digest();
|
||||
BigInteger bigInt = new BigInteger(1, md5sum);
|
||||
String output = bigInt.toString(16);
|
||||
// Fill to 40 chars
|
||||
output = String.format("%40s", output).replace(' ', '0');
|
||||
Timber.i("File SHA1: %s", output);
|
||||
|
||||
return output;
|
||||
} catch (IOException e) {
|
||||
Timber.e(e, "IO Exception");
|
||||
return "";
|
||||
} finally {
|
||||
try {
|
||||
is.close();
|
||||
} catch (IOException e) {
|
||||
Timber.e(e, "Exception on closing MD5 input stream");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* In older devices getPath() may fail depending on the source URI. Creating and using a copy of the file seems to work instead.
|
||||
* @return path of copy
|
||||
*/
|
||||
@Nullable
|
||||
static String createCopyPath(ParcelFileDescriptor descriptor) {
|
||||
try {
|
||||
String copyPath = Environment.getExternalStorageDirectory().toString() + "/CommonsApp/" + new Date().getTime() + ".jpg";
|
||||
File newFile = new File(Environment.getExternalStorageDirectory().toString() + "/CommonsApp");
|
||||
newFile.mkdir();
|
||||
FileUtils.copy(descriptor.getFileDescriptor(), copyPath);
|
||||
Timber.d("Filepath (copied): %s", copyPath);
|
||||
return copyPath;
|
||||
} catch (IOException e) {
|
||||
Timber.e(e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a file path from a Uri. This will get the the path for Storage Access
|
||||
* Framework Documents, as well as the _data field for the MediaStore and
|
||||
|
|
@ -235,4 +301,80 @@ public class FileUtils {
|
|||
copy(new FileInputStream(source), new FileOutputStream(destination));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Read and return the content of a resource file as string.
|
||||
* @param fileName asset file's path (e.g. "/queries/nearby_query.rq")
|
||||
* @return the content of the file
|
||||
*/
|
||||
public static String readFromResource(String fileName) throws IOException {
|
||||
StringBuilder buffer = new StringBuilder();
|
||||
BufferedReader reader = null;
|
||||
try {
|
||||
InputStream inputStream = FileUtils.class.getResourceAsStream(fileName);
|
||||
if (inputStream == null) {
|
||||
throw new FileNotFoundException(fileName);
|
||||
}
|
||||
reader = new BufferedReader(new InputStreamReader(inputStream, "UTF-8"));
|
||||
String line;
|
||||
while ((line = reader.readLine()) != null) {
|
||||
buffer.append(line).append("\n");
|
||||
}
|
||||
} finally {
|
||||
if (reader != null) {
|
||||
reader.close();
|
||||
}
|
||||
}
|
||||
return buffer.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes files.
|
||||
* @param file context
|
||||
*/
|
||||
public static boolean deleteFile(File file) {
|
||||
boolean deletedAll = true;
|
||||
if (file != null) {
|
||||
if (file.isDirectory()) {
|
||||
String[] children = file.list();
|
||||
for (String child : children) {
|
||||
deletedAll = deleteFile(new File(file, child)) && deletedAll;
|
||||
}
|
||||
} else {
|
||||
deletedAll = file.delete();
|
||||
}
|
||||
}
|
||||
|
||||
return deletedAll;
|
||||
}
|
||||
|
||||
public static File createAndGetAppLogsFile(String logs) {
|
||||
try {
|
||||
File commonsAppDirectory = new File(Environment.getExternalStorageDirectory().toString() + "/CommonsApp");
|
||||
if (!commonsAppDirectory.exists()) {
|
||||
commonsAppDirectory.mkdir();
|
||||
}
|
||||
|
||||
File logsFile = new File(commonsAppDirectory,"logs.txt");
|
||||
if (logsFile.exists()) {
|
||||
//old logs file is useless
|
||||
logsFile.delete();
|
||||
}
|
||||
|
||||
logsFile.createNewFile();
|
||||
|
||||
FileOutputStream outputStream = new FileOutputStream(logsFile);
|
||||
OutputStreamWriter outputStreamWriter = new OutputStreamWriter(outputStream);
|
||||
outputStreamWriter.append(logs);
|
||||
outputStreamWriter.close();
|
||||
outputStream.flush();
|
||||
outputStream.close();
|
||||
|
||||
return logsFile;
|
||||
} catch (IOException ioe) {
|
||||
Timber.e(ioe);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
package fr.free.nrw.commons.upload;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
|
||||
@Singleton
|
||||
public class GpsCategoryModel {
|
||||
private Set<String> categorySet;
|
||||
|
||||
@Inject
|
||||
public GpsCategoryModel() {
|
||||
clear();
|
||||
}
|
||||
|
||||
public void clear() {
|
||||
categorySet = new HashSet<>();
|
||||
}
|
||||
|
||||
public boolean getGpsCatExists() {
|
||||
return !categorySet.isEmpty();
|
||||
}
|
||||
|
||||
public List<String> getCategoryList() {
|
||||
return new ArrayList<>(categorySet);
|
||||
}
|
||||
|
||||
public void setCategoryList(List<String> categoryList) {
|
||||
clear();
|
||||
categorySet.addAll(categoryList != null ? categoryList : new ArrayList<>());
|
||||
}
|
||||
|
||||
public void add(String categoryString) {
|
||||
categorySet.add(categoryString);
|
||||
}
|
||||
}
|
||||
|
|
@ -47,6 +47,8 @@ import fr.free.nrw.commons.modifications.TemplateRemoveModifier;
|
|||
import fr.free.nrw.commons.mwapi.MediaWikiApi;
|
||||
import timber.log.Timber;
|
||||
|
||||
//TODO: We should use this class to see how multiple uploads are handled, and then REMOVE it.
|
||||
|
||||
public class MultipleShareActivity extends AuthenticatedActivity
|
||||
implements MediaDetailPagerFragment.MediaDetailProvider,
|
||||
AdapterView.OnItemClickListener,
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
package fr.free.nrw.commons.upload;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.graphics.Point;
|
||||
import android.net.Uri;
|
||||
|
|
@ -17,7 +16,6 @@ import android.view.MenuInflater;
|
|||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.inputmethod.InputMethodManager;
|
||||
import android.widget.AdapterView;
|
||||
import android.widget.BaseAdapter;
|
||||
import android.widget.EditText;
|
||||
|
|
@ -26,6 +24,8 @@ import android.widget.GridView;
|
|||
import android.widget.RelativeLayout;
|
||||
import android.widget.TextView;
|
||||
|
||||
import butterknife.BindView;
|
||||
import butterknife.ButterKnife;
|
||||
import com.facebook.drawee.generic.GenericDraweeHierarchyBuilder;
|
||||
import com.facebook.drawee.view.SimpleDraweeView;
|
||||
|
||||
|
|
@ -41,9 +41,13 @@ public class MultipleUploadListFragment extends Fragment {
|
|||
void OnMultipleUploadInitiated();
|
||||
}
|
||||
|
||||
private GridView photosGrid;
|
||||
@BindView(R.id.multipleShareBackground)
|
||||
GridView photosGrid;
|
||||
|
||||
@BindView(R.id.multipleBaseTitle)
|
||||
EditText baseTitle;
|
||||
|
||||
private PhotoDisplayAdapter photosAdapter;
|
||||
private EditText baseTitle;
|
||||
private TitleTextWatcher textWatcher = new TitleTextWatcher();
|
||||
|
||||
private Point photoSize;
|
||||
|
|
@ -166,9 +170,7 @@ public class MultipleUploadListFragment extends Fragment {
|
|||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||
View view = inflater.inflate(R.layout.fragment_multiple_uploads_list, container, false);
|
||||
photosGrid = view.findViewById(R.id.multipleShareBackground);
|
||||
baseTitle = view.findViewById(R.id.multipleBaseTitle);
|
||||
|
||||
ButterKnife.bind(this,view);
|
||||
photosAdapter = new PhotoDisplayAdapter();
|
||||
photosGrid.setAdapter(photosAdapter);
|
||||
photosGrid.setOnItemClickListener((AdapterView.OnItemClickListener) getActivity());
|
||||
|
|
|
|||
|
|
@ -1,249 +0,0 @@
|
|||
package fr.free.nrw.commons.upload;
|
||||
|
||||
import android.content.Context;
|
||||
import android.net.Uri;
|
||||
|
||||
import com.android.volley.Cache;
|
||||
import com.android.volley.NetworkResponse;
|
||||
import com.android.volley.Request;
|
||||
import com.android.volley.RequestQueue;
|
||||
import com.android.volley.Response;
|
||||
import com.android.volley.VolleyError;
|
||||
import com.android.volley.toolbox.HttpHeaderParser;
|
||||
import com.android.volley.toolbox.JsonRequest;
|
||||
import com.android.volley.toolbox.Volley;
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import timber.log.Timber;
|
||||
|
||||
/**
|
||||
* Uses the Volley library to implement asynchronous calls to the Commons MediaWiki API to match
|
||||
* GPS coordinates with nearby Commons categories. Parses the results using GSON to obtain a list
|
||||
* of relevant categories.
|
||||
*/
|
||||
public class MwVolleyApi {
|
||||
|
||||
private static RequestQueue REQUEST_QUEUE;
|
||||
private static final Gson GSON = new GsonBuilder().create();
|
||||
|
||||
private static Set<String> categorySet;
|
||||
private static List<String> categoryList;
|
||||
|
||||
private static final String MWURL = "https://commons.wikimedia.org/";
|
||||
private final Context context;
|
||||
|
||||
public MwVolleyApi(Context context) {
|
||||
this.context = context;
|
||||
categorySet = new HashSet<>();
|
||||
}
|
||||
|
||||
public static List<String> getGpsCat() {
|
||||
return categoryList;
|
||||
}
|
||||
|
||||
public static void setGpsCat(List<String> cachedList) {
|
||||
categoryList = new ArrayList<>();
|
||||
categoryList.addAll(cachedList);
|
||||
Timber.d("Setting GPS cats from cache: %s", categoryList);
|
||||
}
|
||||
|
||||
public void request(String coords) {
|
||||
String apiUrl = buildUrl(coords);
|
||||
Timber.d("URL: %s", apiUrl);
|
||||
|
||||
JsonRequest<QueryResponse> request = new QueryRequest(apiUrl,
|
||||
new LogResponseListener<>(), new LogResponseErrorListener());
|
||||
getQueue().add(request);
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds URL with image coords for MediaWiki API calls
|
||||
* Example URL: https://commons.wikimedia.org/w/api.php?action=query&prop=categories|coordinates|pageprops&format=json&clshow=!hidden&coprop=type|name|dim|country|region|globe&codistancefrompoint=38.11386944444445|13.356263888888888&generator=geosearch&redirects=&ggscoord=38.11386944444445|1.356263888888888&ggsradius=100&ggslimit=10&ggsnamespace=6&ggsprop=type|name|dim|country|region|globe&ggsprimary=all&formatversion=2
|
||||
* @param coords Coordinates to build query with
|
||||
* @return URL for API query
|
||||
*/
|
||||
private String buildUrl(String coords) {
|
||||
|
||||
Uri.Builder builder = Uri.parse(MWURL).buildUpon();
|
||||
|
||||
builder.appendPath("w")
|
||||
.appendPath("api.php")
|
||||
.appendQueryParameter("action", "query")
|
||||
.appendQueryParameter("prop", "categories|coordinates|pageprops")
|
||||
.appendQueryParameter("format", "json")
|
||||
.appendQueryParameter("clshow", "!hidden")
|
||||
.appendQueryParameter("coprop", "type|name|dim|country|region|globe")
|
||||
.appendQueryParameter("codistancefrompoint", coords)
|
||||
.appendQueryParameter("generator", "geosearch")
|
||||
.appendQueryParameter("ggscoord", coords)
|
||||
.appendQueryParameter("ggsradius", "10000")
|
||||
.appendQueryParameter("ggslimit", "10")
|
||||
.appendQueryParameter("ggsnamespace", "6")
|
||||
.appendQueryParameter("ggsprop", "type|name|dim|country|region|globe")
|
||||
.appendQueryParameter("ggsprimary", "all")
|
||||
.appendQueryParameter("formatversion", "2");
|
||||
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
private synchronized RequestQueue getQueue() {
|
||||
if (REQUEST_QUEUE == null) {
|
||||
REQUEST_QUEUE = Volley.newRequestQueue(context);
|
||||
}
|
||||
return REQUEST_QUEUE;
|
||||
}
|
||||
|
||||
private static class LogResponseListener<T> implements Response.Listener<T> {
|
||||
|
||||
@Override
|
||||
public void onResponse(T response) {
|
||||
Timber.d(response.toString());
|
||||
}
|
||||
}
|
||||
|
||||
private static class LogResponseErrorListener implements Response.ErrorListener {
|
||||
|
||||
@Override
|
||||
public void onErrorResponse(VolleyError error) {
|
||||
Timber.e(error.toString());
|
||||
}
|
||||
}
|
||||
|
||||
private static class QueryRequest extends JsonRequest<QueryResponse> {
|
||||
|
||||
public QueryRequest(String url,
|
||||
Response.Listener<QueryResponse> listener,
|
||||
Response.ErrorListener errorListener) {
|
||||
super(Request.Method.GET, url, null, listener, errorListener);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Response<QueryResponse> parseNetworkResponse(NetworkResponse response) {
|
||||
String json = parseString(response);
|
||||
QueryResponse queryResponse = GSON.fromJson(json, QueryResponse.class);
|
||||
return Response.success(queryResponse, cacheEntry(response));
|
||||
}
|
||||
|
||||
private Cache.Entry cacheEntry(NetworkResponse response) {
|
||||
return HttpHeaderParser.parseCacheHeaders(response);
|
||||
}
|
||||
|
||||
private String parseString(NetworkResponse response) {
|
||||
try {
|
||||
return new String(response.data, HttpHeaderParser.parseCharset(response.headers));
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
return new String(response.data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static class GpsCatExists {
|
||||
private static boolean gpsCatExists;
|
||||
|
||||
public static void setGpsCatExists(boolean gpsCat) {
|
||||
gpsCatExists = gpsCat;
|
||||
}
|
||||
|
||||
public static boolean getGpsCatExists() {
|
||||
return gpsCatExists;
|
||||
}
|
||||
}
|
||||
|
||||
private static class QueryResponse {
|
||||
private Query query = new Query();
|
||||
|
||||
private String printSet() {
|
||||
if (categorySet == null || categorySet.isEmpty()) {
|
||||
GpsCatExists.setGpsCatExists(false);
|
||||
Timber.d("gpsCatExists=%b", GpsCatExists.getGpsCatExists());
|
||||
return "No collection of categories";
|
||||
} else {
|
||||
GpsCatExists.setGpsCatExists(true);
|
||||
Timber.d("gpsCatExists=%b", GpsCatExists.getGpsCatExists());
|
||||
return "CATEGORIES FOUND" + categorySet.toString();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
if (query != null) {
|
||||
return "query=" + query.toString() + "\n" + printSet();
|
||||
} else {
|
||||
return "No pages found";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static class Query {
|
||||
private Page [] pages;
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder builder = new StringBuilder("pages=" + "\n");
|
||||
if (pages != null) {
|
||||
for (Page page : pages) {
|
||||
builder.append(page.toString());
|
||||
builder.append("\n");
|
||||
}
|
||||
builder.replace(builder.length() - 1, builder.length(), "");
|
||||
return builder.toString();
|
||||
} else {
|
||||
return "No pages found";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static class Page {
|
||||
private int pageid;
|
||||
private int ns;
|
||||
private String title;
|
||||
private Category[] categories;
|
||||
private Category category;
|
||||
|
||||
public Page() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
|
||||
StringBuilder builder = new StringBuilder("PAGEID=" + pageid + " ns=" + ns + " title=" + title + "\n" + " CATEGORIES= ");
|
||||
|
||||
if (categories == null || categories.length == 0) {
|
||||
builder.append("no categories exist\n");
|
||||
} else {
|
||||
for (Category category : categories) {
|
||||
builder.append(category.toString());
|
||||
builder.append("\n");
|
||||
if (category != null) {
|
||||
String categoryString = category.toString().replace("Category:", "");
|
||||
categorySet.add(categoryString);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
categoryList = new ArrayList<>(categorySet);
|
||||
builder.replace(builder.length() - 1, builder.length(), "");
|
||||
return builder.toString();
|
||||
}
|
||||
}
|
||||
|
||||
private static class Category {
|
||||
private String title;
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return title;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load diff
|
|
@ -13,6 +13,9 @@ import android.view.ViewGroup;
|
|||
import android.view.Window;
|
||||
import android.widget.Button;
|
||||
|
||||
import butterknife.BindView;
|
||||
import butterknife.ButterKnife;
|
||||
import butterknife.OnClick;
|
||||
import com.facebook.drawee.generic.GenericDraweeHierarchyBuilder;
|
||||
import com.facebook.drawee.view.SimpleDraweeView;
|
||||
import com.facebook.imagepipeline.listener.RequestListener;
|
||||
|
|
@ -29,29 +32,33 @@ import fr.free.nrw.commons.R;
|
|||
*/
|
||||
|
||||
public class SimilarImageDialogFragment extends DialogFragment {
|
||||
|
||||
@BindView(R.id.orginalImage)
|
||||
SimpleDraweeView originalImage;
|
||||
@BindView(R.id.possibleImage)
|
||||
SimpleDraweeView possibleImage;
|
||||
@BindView(R.id.postive_button)
|
||||
Button positiveButton;
|
||||
@BindView(R.id.negative_button)
|
||||
Button negativeButton;
|
||||
onResponse mOnResponse;//Implemented interface from shareActivity
|
||||
Boolean gotResponse = false;
|
||||
|
||||
public SimilarImageDialogFragment() {
|
||||
}
|
||||
public interface onResponse{
|
||||
public void onPostiveResponse();
|
||||
public void onPositiveResponse();
|
||||
|
||||
public void onNegativeResponse();
|
||||
}
|
||||
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
|
||||
View view = inflater.inflate(R.layout.fragment_similar_image_dialog, container, false);
|
||||
ButterKnife.bind(this,view);
|
||||
Set<RequestListener> requestListeners = new HashSet<>();
|
||||
requestListeners.add(new RequestLoggingListener());
|
||||
|
||||
originalImage =(SimpleDraweeView) view.findViewById(R.id.orginalImage);
|
||||
possibleImage =(SimpleDraweeView) view.findViewById(R.id.possibleImage);
|
||||
positiveButton = (Button) view.findViewById(R.id.postive_button);
|
||||
negativeButton = (Button) view.findViewById(R.id.negative_button);
|
||||
|
||||
originalImage.setHierarchy(GenericDraweeHierarchyBuilder
|
||||
.newInstance(getResources())
|
||||
.setPlaceholderImage(VectorDrawableCompat.create(getResources(),
|
||||
|
|
@ -70,22 +77,6 @@ public class SimilarImageDialogFragment extends DialogFragment {
|
|||
originalImage.setImageURI(Uri.fromFile(new File(getArguments().getString("originalImagePath"))));
|
||||
possibleImage.setImageURI(Uri.fromFile(new File(getArguments().getString("possibleImagePath"))));
|
||||
|
||||
negativeButton.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
mOnResponse.onNegativeResponse();
|
||||
gotResponse = true;
|
||||
dismiss();
|
||||
}
|
||||
});
|
||||
positiveButton.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
mOnResponse.onPostiveResponse();
|
||||
gotResponse = true;
|
||||
dismiss();
|
||||
}
|
||||
});
|
||||
return view;
|
||||
}
|
||||
|
||||
|
|
@ -105,8 +96,23 @@ public class SimilarImageDialogFragment extends DialogFragment {
|
|||
@Override
|
||||
public void onDismiss(DialogInterface dialog) {
|
||||
// I user dismisses dialog by pressing outside the dialog.
|
||||
if(!gotResponse)
|
||||
if (!gotResponse) {
|
||||
mOnResponse.onNegativeResponse();
|
||||
}
|
||||
super.onDismiss(dialog);
|
||||
}
|
||||
|
||||
@OnClick(R.id.negative_button)
|
||||
public void onNegativeButtonClicked() {
|
||||
mOnResponse.onNegativeResponse();
|
||||
gotResponse = true;
|
||||
dismiss();
|
||||
}
|
||||
|
||||
@OnClick(R.id.postive_button)
|
||||
public void onPositiveButtonClicked() {
|
||||
mOnResponse.onPositiveResponse();
|
||||
gotResponse = true;
|
||||
dismiss();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -74,13 +74,13 @@ public class SingleUploadFragment extends CommonsDaggerSupportFragment {
|
|||
//What happens when the 'submit' icon is tapped
|
||||
case R.id.menu_upload_single:
|
||||
|
||||
if (titleEdit.getText().toString().isEmpty()) {
|
||||
if (titleEdit.getText().toString().trim().isEmpty()) {
|
||||
Toast.makeText(getContext(), R.string.add_title_toast, Toast.LENGTH_LONG).show();
|
||||
return false;
|
||||
}
|
||||
|
||||
String title = titleEdit.getText().toString();
|
||||
String desc = descEdit.getText().toString();
|
||||
String title = titleEdit.getText().toString().trim();
|
||||
String desc = descEdit.getText().toString().trim();
|
||||
|
||||
//Save the title/desc in short-lived cache so next time this fragment is loaded, we can access these
|
||||
prefs.edit()
|
||||
|
|
|
|||
|
|
@ -92,7 +92,7 @@ public class UploadController {
|
|||
* @param decimalCoords the coordinates in decimal. (e.g. "37.51136|-77.602615")
|
||||
* @param onComplete the progress tracker
|
||||
*/
|
||||
public void startUpload(String title, Uri mediaUri, String description, String mimeType, String source, String decimalCoords, ContributionUploadProgress onComplete) {
|
||||
public void startUpload(String title, Uri mediaUri, String description, String mimeType, String source, String decimalCoords, String wikiDataEntityId, ContributionUploadProgress onComplete) {
|
||||
Contribution contribution;
|
||||
|
||||
//TODO: Modify this to include coords
|
||||
|
|
@ -100,12 +100,18 @@ public class UploadController {
|
|||
null, null, sessionManager.getCurrentAccount().name,
|
||||
CommonsApplication.DEFAULT_EDIT_SUMMARY, decimalCoords);
|
||||
|
||||
|
||||
contribution.setTag("mimeType", mimeType);
|
||||
contribution.setSource(source);
|
||||
|
||||
//Calls the next overloaded method
|
||||
startUpload(contribution, onComplete);
|
||||
|
||||
contribution.setTag("mimeType", mimeType);
|
||||
contribution.setSource(source);
|
||||
contribution.setWikiDataEntityId(wikiDataEntityId);
|
||||
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -7,7 +7,6 @@ import android.app.PendingIntent;
|
|||
import android.content.ContentResolver;
|
||||
import android.content.ContentValues;
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.graphics.BitmapFactory;
|
||||
import android.os.Bundle;
|
||||
import android.support.v4.app.NotificationCompat;
|
||||
|
|
@ -23,7 +22,6 @@ import java.util.regex.Matcher;
|
|||
import java.util.regex.Pattern;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Named;
|
||||
|
||||
import fr.free.nrw.commons.HandlerService;
|
||||
import fr.free.nrw.commons.R;
|
||||
|
|
@ -36,6 +34,7 @@ import fr.free.nrw.commons.contributions.ContributionsContentProvider;
|
|||
import fr.free.nrw.commons.modifications.ModificationsContentProvider;
|
||||
import fr.free.nrw.commons.mwapi.MediaWikiApi;
|
||||
import fr.free.nrw.commons.mwapi.UploadResult;
|
||||
import fr.free.nrw.commons.wikidata.WikidataEditService;
|
||||
import timber.log.Timber;
|
||||
|
||||
public class UploadService extends HandlerService<Contribution> {
|
||||
|
|
@ -49,8 +48,8 @@ public class UploadService extends HandlerService<Contribution> {
|
|||
public static final String EXTRA_CAMPAIGN = EXTRA_PREFIX + ".campaign";
|
||||
|
||||
@Inject MediaWikiApi mwApi;
|
||||
@Inject WikidataEditService wikidataEditService;
|
||||
@Inject SessionManager sessionManager;
|
||||
@Inject @Named("default_preferences") SharedPreferences prefs;
|
||||
@Inject ContributionDao contributionDao;
|
||||
|
||||
private NotificationManager notificationManager;
|
||||
|
|
@ -137,6 +136,7 @@ public class UploadService extends HandlerService<Contribution> {
|
|||
|
||||
@Override
|
||||
public void queue(int what, Contribution contribution) {
|
||||
Timber.d("Upload service queue has contribution with wiki data entity id as %s", contribution.getWikiDataEntityId());
|
||||
switch (what) {
|
||||
case ACTION_UPLOAD_FILE:
|
||||
|
||||
|
|
@ -231,10 +231,10 @@ public class UploadService extends HandlerService<Contribution> {
|
|||
Timber.d("Successfully revalidated token!");
|
||||
} else {
|
||||
Timber.d("Unable to revalidate :(");
|
||||
// TODO: Put up a new notification, ask them to re-login
|
||||
stopForeground(true);
|
||||
Toast failureToast = Toast.makeText(this, R.string.authentication_failed, Toast.LENGTH_LONG);
|
||||
failureToast.show();
|
||||
sessionManager.forceLogin(this);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
|
@ -253,6 +253,7 @@ public class UploadService extends HandlerService<Contribution> {
|
|||
if (!resultStatus.equals("Success")) {
|
||||
showFailedNotification(contribution);
|
||||
} else {
|
||||
wikidataEditService.createClaimWithLogging(contribution.getWikiDataEntityId(), filename);
|
||||
contribution.setFilename(uploadResult.getCanonicalFilename());
|
||||
contribution.setImageUrl(uploadResult.getImageUrl());
|
||||
contribution.setState(Contribution.STATE_COMPLETED);
|
||||
|
|
|
|||
115
app/src/main/java/fr/free/nrw/commons/upload/Zoom.java
Normal file
115
app/src/main/java/fr/free/nrw/commons/upload/Zoom.java
Normal file
|
|
@ -0,0 +1,115 @@
|
|||
package fr.free.nrw.commons.upload;
|
||||
|
||||
import android.content.ContentResolver;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.BitmapRegionDecoder;
|
||||
import android.graphics.Point;
|
||||
import android.graphics.Rect;
|
||||
import android.net.Uri;
|
||||
import android.provider.MediaStore;
|
||||
import android.support.v4.graphics.BitmapCompat;
|
||||
import android.view.View;
|
||||
import android.widget.FrameLayout;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
import timber.log.Timber;
|
||||
|
||||
/**
|
||||
* Contains utility methods for the Zoom function in ShareActivity.
|
||||
*/
|
||||
public class Zoom {
|
||||
|
||||
private View thumbView;
|
||||
private ContentResolver contentResolver;
|
||||
private FrameLayout flContainer;
|
||||
|
||||
Zoom(View thumbView, FrameLayout flContainer, ContentResolver contentResolver) {
|
||||
this.thumbView = thumbView;
|
||||
this.contentResolver = contentResolver;
|
||||
this.flContainer = flContainer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a scaled bitmap to display the zoomed-in image
|
||||
* @param input the input stream corresponding to the uploaded image
|
||||
* @param imageUri the uploaded image's URI
|
||||
* @return a zoomable bitmap
|
||||
*/
|
||||
Bitmap createScaledImage(InputStream input, Uri imageUri) {
|
||||
|
||||
Bitmap scaled = null;
|
||||
BitmapRegionDecoder decoder = null;
|
||||
Bitmap bitmap = null;
|
||||
|
||||
try {
|
||||
decoder = BitmapRegionDecoder.newInstance(input, false);
|
||||
bitmap = decoder.decodeRegion(new Rect(10, 10, 50, 50), null);
|
||||
} catch (IOException e) {
|
||||
Timber.e(e);
|
||||
} catch (NullPointerException e) {
|
||||
Timber.e(e);
|
||||
}
|
||||
try {
|
||||
//Compress the Image
|
||||
System.gc();
|
||||
Runtime rt = Runtime.getRuntime();
|
||||
long maxMemory = rt.freeMemory();
|
||||
bitmap = MediaStore.Images.Media.getBitmap(contentResolver, imageUri);
|
||||
int bitmapByteCount = BitmapCompat.getAllocationByteCount(bitmap);
|
||||
long height = bitmap.getHeight();
|
||||
long width = bitmap.getWidth();
|
||||
long calHeight = (long) ((height * maxMemory) / (bitmapByteCount * 1.1));
|
||||
long calWidth = (long) ((width * maxMemory) / (bitmapByteCount * 1.1));
|
||||
scaled = Bitmap.createScaledBitmap(bitmap, (int) Math.min(width, calWidth), (int) Math.min(height, calHeight), true);
|
||||
} catch (IOException e) {
|
||||
Timber.e(e);
|
||||
} catch (NullPointerException e) {
|
||||
Timber.e(e);
|
||||
scaled = bitmap;
|
||||
}
|
||||
return scaled;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate the starting and ending bounds for the zoomed-in image.
|
||||
* Also set the container view's offset as the origin for the
|
||||
* bounds, since that's the origin for the positioning animation
|
||||
* properties (X, Y).
|
||||
* @param startBounds the global visible rectangle of the thumbnail
|
||||
* @param finalBounds the global visible rectangle of the container view
|
||||
* @param globalOffset the container view's offset
|
||||
* @return scaled start bounds
|
||||
*/
|
||||
float adjustStartEndBounds(Rect startBounds, Rect finalBounds, Point globalOffset) {
|
||||
|
||||
thumbView.getGlobalVisibleRect(startBounds);
|
||||
flContainer.getGlobalVisibleRect(finalBounds, globalOffset);
|
||||
startBounds.offset(-globalOffset.x, -globalOffset.y);
|
||||
finalBounds.offset(-globalOffset.x, -globalOffset.y);
|
||||
|
||||
// Adjust the start bounds to be the same aspect ratio as the final
|
||||
// bounds using the "center crop" technique. This prevents undesirable
|
||||
// stretching during the animation. Also calculate the start scaling
|
||||
// factor (the end scaling factor is always 1.0).
|
||||
float startScale;
|
||||
if ((float) finalBounds.width() / finalBounds.height()
|
||||
> (float) startBounds.width() / startBounds.height()) {
|
||||
// Extend start bounds horizontally
|
||||
startScale = (float) startBounds.height() / finalBounds.height();
|
||||
float startWidth = startScale * finalBounds.width();
|
||||
float deltaWidth = (startWidth - startBounds.width()) / 2;
|
||||
startBounds.left -= deltaWidth;
|
||||
startBounds.right += deltaWidth;
|
||||
} else {
|
||||
// Extend start bounds vertically
|
||||
startScale = (float) startBounds.width() / finalBounds.width();
|
||||
float startHeight = startScale * finalBounds.height();
|
||||
float deltaHeight = (startHeight - startBounds.height()) / 2;
|
||||
startBounds.top -= deltaHeight;
|
||||
startBounds.bottom += deltaHeight;
|
||||
}
|
||||
return startScale;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,92 +0,0 @@
|
|||
package fr.free.nrw.commons.utils;
|
||||
|
||||
import android.os.Environment;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.OutputStreamWriter;
|
||||
|
||||
import timber.log.Timber;
|
||||
|
||||
public class FileUtils {
|
||||
/**
|
||||
* Read and return the content of a resource file as string.
|
||||
*
|
||||
* @param fileName asset file's path (e.g. "/queries/nearby_query.rq")
|
||||
* @return the content of the file
|
||||
*/
|
||||
public static String readFromResource(String fileName) throws IOException {
|
||||
StringBuilder buffer = new StringBuilder();
|
||||
BufferedReader reader = null;
|
||||
try {
|
||||
InputStream inputStream = FileUtils.class.getResourceAsStream(fileName);
|
||||
if (inputStream == null) {
|
||||
throw new FileNotFoundException(fileName);
|
||||
}
|
||||
reader = new BufferedReader(new InputStreamReader(inputStream, "UTF-8"));
|
||||
String line;
|
||||
while ((line = reader.readLine()) != null) {
|
||||
buffer.append(line).append("\n");
|
||||
}
|
||||
} finally {
|
||||
if (reader != null) {
|
||||
reader.close();
|
||||
}
|
||||
}
|
||||
return buffer.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes files.
|
||||
* @param file context
|
||||
*/
|
||||
public static boolean deleteFile(File file) {
|
||||
boolean deletedAll = true;
|
||||
if (file != null) {
|
||||
if (file.isDirectory()) {
|
||||
String[] children = file.list();
|
||||
for (String child : children) {
|
||||
deletedAll = deleteFile(new File(file, child)) && deletedAll;
|
||||
}
|
||||
} else {
|
||||
deletedAll = file.delete();
|
||||
}
|
||||
}
|
||||
|
||||
return deletedAll;
|
||||
}
|
||||
|
||||
public static File createAndGetAppLogsFile(String logs) {
|
||||
try {
|
||||
File commonsAppDirectory = new File(Environment.getExternalStorageDirectory().toString() + "/CommonsApp");
|
||||
if (!commonsAppDirectory.exists()) {
|
||||
commonsAppDirectory.mkdir();
|
||||
}
|
||||
|
||||
File logsFile = new File(commonsAppDirectory,"logs.txt");
|
||||
if (logsFile.exists()) {
|
||||
//old logs file is useless
|
||||
logsFile.delete();
|
||||
}
|
||||
|
||||
logsFile.createNewFile();
|
||||
|
||||
FileOutputStream outputStream = new FileOutputStream(logsFile);
|
||||
OutputStreamWriter outputStreamWriter = new OutputStreamWriter(outputStream);
|
||||
outputStreamWriter.append(logs);
|
||||
outputStreamWriter.close();
|
||||
outputStream.flush();
|
||||
outputStream.close();
|
||||
|
||||
return logsFile;
|
||||
} catch (IOException ioe) {
|
||||
Timber.e(ioe);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,10 +1,27 @@
|
|||
package fr.free.nrw.commons.utils;
|
||||
|
||||
import android.app.WallpaperManager;
|
||||
import android.content.Context;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.BitmapRegionDecoder;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.Rect;
|
||||
import android.net.Uri;
|
||||
import android.support.annotation.Nullable;
|
||||
|
||||
import com.facebook.common.executors.CallerThreadExecutor;
|
||||
import com.facebook.common.references.CloseableReference;
|
||||
import com.facebook.datasource.DataSource;
|
||||
import com.facebook.drawee.backends.pipeline.Fresco;
|
||||
import com.facebook.imagepipeline.core.ImagePipeline;
|
||||
import com.facebook.imagepipeline.datasource.BaseBitmapDataSubscriber;
|
||||
import com.facebook.imagepipeline.image.CloseableImage;
|
||||
import com.facebook.imagepipeline.request.ImageRequest;
|
||||
import com.facebook.imagepipeline.request.ImageRequestBuilder;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import fr.free.nrw.commons.R;
|
||||
import timber.log.Timber;
|
||||
|
||||
/**
|
||||
|
|
@ -132,4 +149,52 @@ public class ImageUtils {
|
|||
|
||||
return isImageDark;
|
||||
}
|
||||
|
||||
/**
|
||||
* Downloads the image from the URL and sets it as the phone's wallpaper
|
||||
* Fails silently if download or setting wallpaper fails.
|
||||
* @param context
|
||||
* @param imageUrl
|
||||
*/
|
||||
public static void setWallpaperFromImageUrl(Context context, Uri imageUrl) {
|
||||
Timber.d("Trying to set wallpaper from url %s", imageUrl.toString());
|
||||
ImageRequest imageRequest = ImageRequestBuilder
|
||||
.newBuilderWithSource(imageUrl)
|
||||
.setAutoRotateEnabled(true)
|
||||
.build();
|
||||
|
||||
ImagePipeline imagePipeline = Fresco.getImagePipeline();
|
||||
final DataSource<CloseableReference<CloseableImage>>
|
||||
dataSource = imagePipeline.fetchDecodedImage(imageRequest, context);
|
||||
|
||||
dataSource.subscribe(new BaseBitmapDataSubscriber() {
|
||||
|
||||
@Override
|
||||
public void onNewResultImpl(@Nullable Bitmap bitmap) {
|
||||
if (dataSource.isFinished() && bitmap != null){
|
||||
Timber.d("Bitmap loaded from url %s", imageUrl.toString());
|
||||
setWallpaper(context, Bitmap.createBitmap(bitmap));
|
||||
dataSource.close();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailureImpl(DataSource dataSource) {
|
||||
Timber.d("Error getting bitmap from image url %s", imageUrl.toString());
|
||||
if (dataSource != null) {
|
||||
dataSource.close();
|
||||
}
|
||||
}
|
||||
}, CallerThreadExecutor.getInstance());
|
||||
}
|
||||
|
||||
private static void setWallpaper(Context context, Bitmap bitmap) {
|
||||
WallpaperManager wallpaperManager = WallpaperManager.getInstance(context);
|
||||
try {
|
||||
wallpaperManager.setBitmap(bitmap);
|
||||
ViewUtil.showLongToast(context, context.getString(R.string.wallpaper_set_successfully));
|
||||
} catch (IOException e) {
|
||||
Timber.e(e,"Error setting wallpaper");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,80 @@
|
|||
package fr.free.nrw.commons.widget;
|
||||
|
||||
import android.appwidget.AppWidgetManager;
|
||||
import android.appwidget.AppWidgetProvider;
|
||||
import android.content.Context;
|
||||
import android.widget.RemoteViews;
|
||||
|
||||
import com.prof.rssparser.Article;
|
||||
import com.prof.rssparser.Parser;
|
||||
import com.squareup.picasso.Picasso;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
import org.jsoup.Jsoup;
|
||||
import org.jsoup.nodes.Document;
|
||||
import org.jsoup.nodes.Element;
|
||||
import org.jsoup.select.Elements;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
import fr.free.nrw.commons.BuildConfig;
|
||||
import fr.free.nrw.commons.R;
|
||||
|
||||
/**
|
||||
* Implementation of App Widget functionality.
|
||||
*/
|
||||
public class PicOfDayAppWidget extends AppWidgetProvider {
|
||||
|
||||
static void updateAppWidget(Context context, AppWidgetManager appWidgetManager,
|
||||
int appWidgetId) {
|
||||
|
||||
// Construct the RemoteViews object
|
||||
RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.pic_of_day_app_widget);
|
||||
|
||||
String urlString = BuildConfig.WIKIMEDIA_API_POTD;
|
||||
Parser parser = new Parser();
|
||||
parser.execute(urlString);
|
||||
parser.onFinish(new Parser.OnTaskCompleted() {
|
||||
@Override
|
||||
public void onTaskCompleted(ArrayList<Article> list) {
|
||||
String desc = list.get(list.size() - 1).getDescription();
|
||||
if (desc != null) {
|
||||
Document document = Jsoup.parse(desc);
|
||||
Elements elements = document.select("img");
|
||||
String imageUrl = elements.get(0).attr("src");
|
||||
if (imageUrl != null && imageUrl.length() > 0) {
|
||||
Picasso.get().load(imageUrl).into(views, R.id.appwidget_image, new int[]{appWidgetId});
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError() {
|
||||
}
|
||||
});
|
||||
|
||||
// Instruct the widget manager to update the widget
|
||||
appWidgetManager.updateAppWidget(appWidgetId, views);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
|
||||
// There may be multiple widgets active, so update all of them
|
||||
for (int appWidgetId : appWidgetIds) {
|
||||
updateAppWidget(context, appWidgetManager, appWidgetId);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEnabled(Context context) {
|
||||
// Enter relevant functionality for when the first widget is created
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDisabled(Context context) {
|
||||
// Enter relevant functionality for when the last widget is disabled
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
package fr.free.nrw.commons.wikidata;
|
||||
|
||||
public abstract class WikidataEditListener {
|
||||
|
||||
protected WikidataP18EditListener wikidataP18EditListener;
|
||||
|
||||
public abstract void onSuccessfulWikidataEdit();
|
||||
|
||||
public void setAuthenticationStateListener(WikidataP18EditListener wikidataP18EditListener) {
|
||||
this.wikidataP18EditListener = wikidataP18EditListener;
|
||||
}
|
||||
|
||||
public interface WikidataP18EditListener {
|
||||
void onWikidataEditSuccessful();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
package fr.free.nrw.commons.wikidata;
|
||||
|
||||
/**
|
||||
* Listener for wikidata edits
|
||||
*/
|
||||
public class WikidataEditListenerImpl extends WikidataEditListener {
|
||||
|
||||
public WikidataEditListenerImpl() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Fired when wikidata P18 edit is successful. If there's an active listener, then it is fired
|
||||
*/
|
||||
@Override
|
||||
public void onSuccessfulWikidataEdit() {
|
||||
if (wikidataP18EditListener != null) {
|
||||
wikidataP18EditListener.onWikidataEditSuccessful();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,134 @@
|
|||
package fr.free.nrw.commons.wikidata;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Named;
|
||||
import javax.inject.Singleton;
|
||||
|
||||
import fr.free.nrw.commons.R;
|
||||
import fr.free.nrw.commons.mwapi.MediaWikiApi;
|
||||
import fr.free.nrw.commons.utils.ViewUtil;
|
||||
import io.reactivex.Observable;
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers;
|
||||
import io.reactivex.schedulers.Schedulers;
|
||||
import timber.log.Timber;
|
||||
|
||||
/**
|
||||
* This class is meant to handle the Wikidata edits made through the app
|
||||
* It will talk with MediaWikiApi to make necessary API calls, log the edits and fire listeners
|
||||
* on successful edits
|
||||
*/
|
||||
@Singleton
|
||||
public class WikidataEditService {
|
||||
|
||||
private final Context context;
|
||||
private final MediaWikiApi mediaWikiApi;
|
||||
private final WikidataEditListener wikidataEditListener;
|
||||
private final SharedPreferences directPrefs;
|
||||
|
||||
@Inject
|
||||
public WikidataEditService(Context context,
|
||||
MediaWikiApi mediaWikiApi,
|
||||
WikidataEditListener wikidataEditListener,
|
||||
@Named("direct_nearby_upload_prefs") SharedPreferences directPrefs) {
|
||||
this.context = context;
|
||||
this.mediaWikiApi = mediaWikiApi;
|
||||
this.wikidataEditListener = wikidataEditListener;
|
||||
this.directPrefs = directPrefs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a P18 claim and log the edit with custom tag
|
||||
* @param wikidataEntityId
|
||||
* @param fileName
|
||||
*/
|
||||
public void createClaimWithLogging(String wikidataEntityId, String fileName) {
|
||||
if(wikidataEntityId == null
|
||||
|| fileName == null) {
|
||||
return;
|
||||
}
|
||||
editWikidataProperty(wikidataEntityId, fileName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Edits the wikidata entity by adding the P18 property to it.
|
||||
* Adding the P18 edit requires calling the wikidata API to create a claim against the entity
|
||||
*
|
||||
* @param wikidataEntityId
|
||||
* @param fileName
|
||||
*/
|
||||
@SuppressLint("CheckResult")
|
||||
private void editWikidataProperty(String wikidataEntityId, String fileName) {
|
||||
Timber.d("Upload successful with wiki data entity id as %s", wikidataEntityId);
|
||||
Timber.d("Attempting to edit Wikidata property %s", wikidataEntityId);
|
||||
Observable.fromCallable(() -> {
|
||||
String propertyValue = getFileName(fileName);
|
||||
return mediaWikiApi.wikidatCreateClaim(wikidataEntityId, "P18", "value", propertyValue);
|
||||
})
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(revisionId -> handleClaimResult(wikidataEntityId, revisionId), throwable -> {
|
||||
Timber.e(throwable, "Error occurred while making claim");
|
||||
ViewUtil.showLongToast(context, context.getString(R.string.wikidata_edit_failure));
|
||||
});
|
||||
}
|
||||
|
||||
private void handleClaimResult(String wikidataEntityId, String revisionId) {
|
||||
if (revisionId != null) {
|
||||
wikidataEditListener.onSuccessfulWikidataEdit();
|
||||
showSuccessToast();
|
||||
logEdit(revisionId);
|
||||
} else {
|
||||
Timber.d("Unable to make wiki data edit for entity %s", wikidataEntityId);
|
||||
ViewUtil.showLongToast(context, context.getString(R.string.wikidata_edit_failure));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Log the Wikidata edit by adding Wikimedia Commons App tag to the edit
|
||||
* @param revisionId
|
||||
*/
|
||||
@SuppressLint("CheckResult")
|
||||
private void logEdit(String revisionId) {
|
||||
Observable.fromCallable(() -> mediaWikiApi.addWikidataEditTag(revisionId))
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(result -> {
|
||||
if (result) {
|
||||
Timber.d("Wikidata edit was tagged successfully");
|
||||
} else {
|
||||
Timber.d("Wikidata edit couldn't be tagged");
|
||||
}
|
||||
}, throwable -> {
|
||||
Timber.e(throwable, "Error occurred while adding tag to the edit");
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Show a success toast when the edit is made successfully
|
||||
*/
|
||||
private void showSuccessToast() {
|
||||
String title = directPrefs.getString("Title", "");
|
||||
String successStringTemplate = context.getString(R.string.successful_wikidata_edit);
|
||||
String successMessage = String.format(Locale.getDefault(), successStringTemplate, title);
|
||||
ViewUtil.showLongToast(context, successMessage);
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats and returns the filename as accepted by the wiki base API
|
||||
* https://www.mediawiki.org/wiki/Wikibase/API#wbcreateclaim
|
||||
*
|
||||
* @param fileName
|
||||
* @return
|
||||
*/
|
||||
private String getFileName(String fileName) {
|
||||
fileName = String.format("\"%s\"", fileName.replace("File:", ""));
|
||||
Timber.d("Wikidata property name is %s", fileName);
|
||||
return fileName;
|
||||
}
|
||||
}
|
||||
25
app/src/main/res/layout/pic_of_day_app_widget.xml
Normal file
25
app/src/main/res/layout/pic_of_day_app_widget.xml
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
<LinearLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="@color/black"
|
||||
android:padding="@dimen/widget_margin"
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:textColor="@color/white"
|
||||
android:textSize="20sp"
|
||||
android:gravity="center"
|
||||
android:layout_marginTop="10dp"
|
||||
android:text="@string/app_widget_heading"/>
|
||||
|
||||
<ImageView
|
||||
android:padding="15dp"
|
||||
android:id="@+id/appwidget_image"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:contentDescription="@string/appwidget_img" />
|
||||
|
||||
</LinearLayout>
|
||||
|
|
@ -15,6 +15,10 @@
|
|||
android:id="@+id/menu_download_current_image"
|
||||
android:title="@string/menu_download"
|
||||
app:showAsAction="never" />
|
||||
<item
|
||||
android:id="@+id/menu_set_as_wallpaper"
|
||||
android:title="@string/menu_set_wallpaper"
|
||||
app:showAsAction="never" />
|
||||
<item
|
||||
android:id="@+id/menu_retry_current_image"
|
||||
android:enabled="false"
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@
|
|||
<string name="login_success">Аҭалара қәҿиарала имҩаҧысит!</string>
|
||||
<string name="login_failed">Асистемахь аҭалараан агха!</string>
|
||||
<string name="upload_failed">Афаил ҧшаам. Даҽа фаилк шәахәаҧш.</string>
|
||||
<string name="authentication_failed">Аутентификациа агха!</string>
|
||||
<string name="authentication_failed" fuzzy="true">Аутентификациа агха!</string>
|
||||
<string name="uploading_started">Аҭагалара иалагоуп!</string>
|
||||
<string name="upload_completed_notification_title">%1$s иҭагалоуп!</string>
|
||||
<string name="upload_completed_notification_text">Шәақәыӷәӷәа иҭагалоу афаил ахәаҧшраз</string>
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@
|
|||
<string name="login_success">تم الدخول بشكل صحيح!</string>
|
||||
<string name="login_failed">فشل تسجيل الدخول</string>
|
||||
<string name="upload_failed">الملف غير موجود. فضلا اختر ملفا آخر.</string>
|
||||
<string name="authentication_failed">فشل الاستيقان!</string>
|
||||
<string name="authentication_failed" fuzzy="true">فشل الاستيقان!</string>
|
||||
<string name="uploading_started">بدأ الرفع!</string>
|
||||
<string name="upload_completed_notification_title">رُفع %1$s!</string>
|
||||
<string name="upload_completed_notification_text">انقر لعرض ملفك المرفوع</string>
|
||||
|
|
@ -54,8 +54,6 @@
|
|||
<string name="share_title_hint">العنوان</string>
|
||||
<string name="share_description_hint">الوصف</string>
|
||||
<string name="login_failed_network">لا يمكن تسجيل الدخول - فشل في شبكة الاتصال</string>
|
||||
<string name="login_failed_username">لا يمكن تسجيل الدخول - فضلا تحقق من اسم المستخدم</string>
|
||||
<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>
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@
|
|||
<string name="login_success">¡Identificación correuta!</string>
|
||||
<string name="login_failed">¡Falló l\'aniciu de sesión!</string>
|
||||
<string name="upload_failed">Nun s\'alcontró\'l ficheru. Tenta con otru.</string>
|
||||
<string name="authentication_failed">¡Falló la identificación!</string>
|
||||
<string name="authentication_failed">Falló la identificación, anicia sesión nuevamente</string>
|
||||
<string name="uploading_started">Principió la xuba</string>
|
||||
<string name="upload_completed_notification_title">¡%1$s xubíu!</string>
|
||||
<string name="upload_completed_notification_text">Toque pa ver la xuba</string>
|
||||
|
|
@ -49,8 +49,7 @@
|
|||
<string name="add_title_toast">Apurre un títulu pa esti ficheru</string>
|
||||
<string name="share_description_hint">Descripción</string>
|
||||
<string name="login_failed_network">Nun se pudo aniciar sesión – error de rede</string>
|
||||
<string name="login_failed_username">Nun se pudo aniciar sesión – por favor compruebe\'l so nome d\'usuariu</string>
|
||||
<string name="login_failed_password">Nun se pudo aniciar sesión – por favor compruebe la so contraseña</string>
|
||||
<string name="login_failed_wrong_credentials">Nun pudo aniciase sesión. Revisa\'l nome d\'usuariu y la contraseña</string>
|
||||
<string name="login_failed_throttled">Demasiaos intentos incorreutos. Téntalo otra vuelta n\'unos minutos.</string>
|
||||
<string name="login_failed_blocked">Sentímoslo, esti usuariu ta bloquiáu en Commons</string>
|
||||
<string name="login_failed_2fa_needed">Tienes de dar el códigu d\'identificación de dos factores.</string>
|
||||
|
|
@ -84,6 +83,7 @@
|
|||
<string name="categories_activity_title">Categoríes</string>
|
||||
<string name="title_activity_settings">Configuración</string>
|
||||
<string name="title_activity_signup">Date d\'alta</string>
|
||||
<string name="title_activity_featured_images">Imáxenes destacaes</string>
|
||||
<string name="menu_about">Tocante a</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">Crea una nueva <a href=\"https://github.com/commons-app/apps-android-commons/issues\">incidencia en GitHub</a> pa informar de problemes y suxerencies.</string>
|
||||
|
|
@ -103,7 +103,7 @@
|
|||
<string name="preference_license">Llicencia predeterminada</string>
|
||||
<string name="use_previous">Usar un títulu/descripción anterior</string>
|
||||
<string name="allow_gps">Llograr automáticamente l\'allugamientu actual</string>
|
||||
<string name="allow_gps_summary">Recuperar l\'allugamientu actual pa ufiertar suxerencies de categoríes si la imaxe nun tien etiquetes xeográfiques</string>
|
||||
<string name="allow_gps_summary">Recupera la posición actual si la imaxe nun tien etiquetes xeográfiques, y marca la imaxe con ella. Atención: Esto revelará\'l to allugamientu actual.</string>
|
||||
<string name="preference_theme">Mou nocherniegu</string>
|
||||
<string name="preference_theme_summary">Usar tema escuru</string>
|
||||
<string name="license_name_cc_by_sa_four"> Reconocimientu-CompartirIgual 4.0</string>
|
||||
|
|
@ -169,6 +169,8 @@
|
|||
<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. Esto pué ser llargo enforma, y necesitará espardese per delles llinies. Sicasí, esperamos que se vea bien.</string>
|
||||
<string name="media_detail_author">Autor</string>
|
||||
<string name="media_detail_author_explanation">El nome d\'usuariu del autor de la imaxe destacada va equí.</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>
|
||||
|
|
@ -211,6 +213,7 @@
|
|||
<string name="navigation_item_logout">Salir</string>
|
||||
<string name="navigation_item_info">Tutorial</string>
|
||||
<string name="navigation_item_notification">Avisos</string>
|
||||
<string name="navigation_item_featured_images">Destacada</string>
|
||||
<string name="nearby_needs_permissions">Los sitios cercanos nun pueden amosase ensin los permisos d\'allugamientu</string>
|
||||
<string name="no_description_found">nun s\'atoparon descripciones</string>
|
||||
<string name="nearby_info_menu_commons_article">Páxina del ficheru en Commons</string>
|
||||
|
|
@ -259,4 +262,20 @@
|
|||
<string name="about_translate_proceed">Siguir</string>
|
||||
<string name="about_translate_cancel">Encaboxar</string>
|
||||
<string name="retry">Retentar</string>
|
||||
<string name="showcase_view_got_it_button">Entendílo</string>
|
||||
<string name="showcase_view_whole_nearby_activity">Estos son sitios cercanos a ti que precisen imaxes para ilustrar los sos artículos de Wikipedia</string>
|
||||
<string name="showcase_view_list_icon">Tocando esti botón amuésase la llista d\'esos llugares</string>
|
||||
<string name="showcase_view_plus_fab">Puedes xubir una imaxe pa cualquier sitiu dende la galería o la cámara</string>
|
||||
<string name="no_images_found">Nun s\'alcontró nenguna imaxe</string>
|
||||
<string name="error_loading_images">Asocedió un error al cargar les imáxenes.</string>
|
||||
<string name="image_uploaded_by">Xubida por: %1$s</string>
|
||||
<string name="share_app_title">Compartir app</string>
|
||||
<string name="share_coordinates_not_present">Nun s\'especificaron les coordenaes al escoyer la imaxe</string>
|
||||
<string name="error_fetching_nearby_places">Error al llograr los llugares cercanos.</string>
|
||||
<string name="appwidget_img">Semeya del día</string>
|
||||
<string name="app_widget_heading">Semeya del día</string>
|
||||
<string name="successful_wikidata_edit">Añadióse correutamente la imaxe a %1$s en Wikidata.</string>
|
||||
<string name="wikidata_edit_failure">Nun pudo anovase la entidá de Wikidata correspondiente.</string>
|
||||
<string name="menu_set_wallpaper">Definir fondu</string>
|
||||
<string name="wallpaper_set_successfully">Fondu definíu correutamente</string>
|
||||
</resources>
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@
|
|||
<string name="login_success">Uspešno ste prijavljeni.</string>
|
||||
<string name="login_failed">Prijavljivanje nije uspelo.</string>
|
||||
<string name="upload_failed">Datoteka nije pronađena. Pokušajte sa drugom datotekom.</string>
|
||||
<string name="authentication_failed">Provera identiteta nije uspela.</string>
|
||||
<string name="authentication_failed" fuzzy="true">Provera identiteta nije uspela.</string>
|
||||
<string name="uploading_started">Otpremanje je započeto.</string>
|
||||
<string name="upload_completed_notification_title">Datoteka „%1$s“ je otpremljena.</string>
|
||||
<string name="upload_completed_notification_text">Tapnite da biste videli otpremanje</string>
|
||||
|
|
@ -43,8 +43,6 @@
|
|||
<string name="share_title_hint">Naslov</string>
|
||||
<string name="share_description_hint">Opis</string>
|
||||
<string name="login_failed_network">Ne mogu da vas prijavim – mreža ne radi</string>
|
||||
<string name="login_failed_username">Ne mogu da vas prijavim – proverite svoje korisničko ime</string>
|
||||
<string name="login_failed_password">Ne mogu da vas prijavim – proverite svoju lozinku</string>
|
||||
<string name="login_failed_throttled">Previše neuspešnih pokušaja. Probajte ponovo za nekoliko minuta.</string>
|
||||
<string name="login_failed_blocked">Nažalost, korisnik je blokiran na Ostavi</string>
|
||||
<string name="login_failed_2fa_needed">Morate uneti svoj dvofaktorski kod za autentifikaciju.</string>
|
||||
|
|
@ -96,7 +94,7 @@
|
|||
<string name="preference_license" fuzzy="true">Licenca</string>
|
||||
<string name="use_previous">Koristi prethodan naslov/opis</string>
|
||||
<string name="allow_gps">Automatski detektuj trenutnu lokaciju</string>
|
||||
<string name="allow_gps_summary">Primi trenutnu lokaciju da bi predložili kategoriju ako slika nije geografski označena</string>
|
||||
<string name="allow_gps_summary" fuzzy="true">Primi trenutnu lokaciju da bi predložili kategoriju ako slika nije geografski označena</string>
|
||||
<string name="preference_theme">Noćni režim</string>
|
||||
<string name="preference_theme_summary">Koristiti tamnu temu</string>
|
||||
<string name="license_name_cc_by_sa_four">Autorstvo-Deliti pod istim uslovima 4.0</string>
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@
|
|||
<string name="login_success">Танышыу уңышлы үтте</string>
|
||||
<string name="login_failed">Танылыу хатаһы</string>
|
||||
<string name="upload_failed">Файл табылманы. Башҡа файлды эҙлә.</string>
|
||||
<string name="authentication_failed">Кем икәнегеҙ танылманы!</string>
|
||||
<string name="authentication_failed" fuzzy="true">Кем икәнегеҙ танылманы!</string>
|
||||
<string name="uploading_started">Тейәү башланды!</string>
|
||||
<string name="upload_completed_notification_title">%1$s тейәлде!</string>
|
||||
<string name="upload_completed_notification_text">Ошонда баҫып тейәлгән файлды ҡара</string>
|
||||
|
|
@ -50,8 +50,6 @@
|
|||
<string name="add_title_toast">Был файлдың атамаһын күрһәт</string>
|
||||
<string name="share_description_hint">Тасуирлама</string>
|
||||
<string name="login_failed_network">Инеп булмай - интернет хатаһы</string>
|
||||
<string name="login_failed_username">Инмәнең - ҡулланыусы исемеңде тикшер</string>
|
||||
<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>
|
||||
|
|
@ -102,7 +100,7 @@
|
|||
<string name="preference_license">Нығытылған рөхсәтнамә</string>
|
||||
<string name="use_previous">Алдағы атама/һәрәтләмәне ҡулланыу</string>
|
||||
<string name="allow_gps">Автомат рәүешендә сираттағы урынды алыу</string>
|
||||
<string name="allow_gps_summary">Әгәр рәсемдең геотегтары булмаһа, категориялар үҙенән-үҙе тәҡдим ителһен өсөн сираттағы урынды алырға</string>
|
||||
<string name="allow_gps_summary" fuzzy="true">Әгәр рәсемдең геотегтары булмаһа, категориялар үҙенән-үҙе тәҡдим ителһен өсөн сираттағы урынды алырға</string>
|
||||
<string name="preference_theme">Төнгө режим</string>
|
||||
<string name="preference_theme_summary">Ҡараңғы теманы ҡулланыу</string>
|
||||
<string name="license_name_cc_by_sa_four"> Attribution-ShareAlike 4.0</string>
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@
|
|||
<string name="login_success">Успешно влизане.</string>
|
||||
<string name="login_failed">Неуспешно влизане!</string>
|
||||
<string name="upload_failed">Файлът не е намерен. Моля, опитайте с друг файл.</string>
|
||||
<string name="authentication_failed">Неуспешен опит за удостоверяване!</string>
|
||||
<string name="authentication_failed" fuzzy="true">Неуспешен опит за удостоверяване!</string>
|
||||
<string name="uploading_started">Качването започна!</string>
|
||||
<string name="upload_completed_notification_title">Файл %1$s е качен!</string>
|
||||
<string name="upload_completed_notification_text">Докоснете, за да видите качения файл</string>
|
||||
|
|
@ -32,8 +32,6 @@
|
|||
<string name="share_title_hint">Заглавие</string>
|
||||
<string name="share_description_hint">Описание</string>
|
||||
<string name="login_failed_network">Неуспешно влизане – проблем в мрежата</string>
|
||||
<string name="login_failed_username">Неуспешно влизане – моля проверете потребителското си име</string>
|
||||
<string name="login_failed_password">Неуспешно влизане – моля проверете паролата си</string>
|
||||
<string name="share_upload_button">Качване</string>
|
||||
<string name="provider_modifications">Изменения</string>
|
||||
<string name="menu_upload_single">Качване</string>
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
* Aftabuzzaman
|
||||
* Bellayet
|
||||
* Sankarshan
|
||||
* আফতাবুজ্জামান
|
||||
-->
|
||||
<resources>
|
||||
<string name="crash_dialog_title">কমন্স ক্র্যাশ করেছে</string>
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@
|
|||
* Rasal Lia
|
||||
* Sankarshan
|
||||
* Tauhid16
|
||||
* আফতাবুজ্জামান
|
||||
-->
|
||||
<resources>
|
||||
<string name="preference_category_appearance">অবয়ব</string>
|
||||
|
|
@ -29,7 +30,7 @@
|
|||
<string name="login_success">প্রবেশ সফল!</string>
|
||||
<string name="login_failed">প্রবেশ ব্যর্থ :(</string>
|
||||
<string name="upload_failed">ফাইল পাওয়া যায়নি। আরেকটি ফাইল চেষ্টা করুন।</string>
|
||||
<string name="authentication_failed">প্রমাণীকরণ ব্যর্থ হয়েছে!</string>
|
||||
<string name="authentication_failed">প্রমাণীকরণ ব্যর্থ হয়েছে, আবার প্রবেশ করুন</string>
|
||||
<string name="uploading_started"> আপলোড আরম্ভ হয়েছে!</string>
|
||||
<string name="upload_completed_notification_title">%1$s আপলোড হয়েছে!</string>
|
||||
<string name="upload_completed_notification_text">আপনার আপলোড দেখতে টোকা দিন</string>
|
||||
|
|
@ -57,8 +58,6 @@
|
|||
<string name="add_title_toast">এই ফাইলটির জন্য একটি শিরোনাম প্রদান করুন</string>
|
||||
<string name="share_description_hint">বিবরণ</string>
|
||||
<string name="login_failed_network">প্রবেশ করা যাচ্ছে না - নেটওয়ার্ক ব্যর্থতা</string>
|
||||
<string name="login_failed_username">প্রবেশ করা যাচ্ছে না - অনুগ্রহ করে আপনার ব্যবহারকারী নাম পরীক্ষা করুন।</string>
|
||||
<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>
|
||||
|
|
@ -112,7 +111,7 @@
|
|||
<string name="preference_license">পূর্বনির্ধারিত লাইসেন্স</string>
|
||||
<string name="use_previous">পূর্ববর্তী শিরোনাম/বিবরণ ব্যবহার করুন</string>
|
||||
<string name="allow_gps">স্বয়ংক্রিয়ভাবে বর্তমান অবস্থান পান</string>
|
||||
<string name="allow_gps_summary">বিষয়শ্রেণীর পরামর্শ দিতে বর্তমান অবস্থান পান যদি ছবিতে ভূ-ট্যাগ না থেকে থাকে</string>
|
||||
<string name="allow_gps_summary" fuzzy="true">বিষয়শ্রেণীর পরামর্শ দিতে বর্তমান অবস্থান পান যদি ছবিতে ভূ-ট্যাগ না থেকে থাকে</string>
|
||||
<string name="preference_theme">রাত্রি মোড</string>
|
||||
<string name="preference_theme_summary">কালো থিম ব্যবহার করুন</string>
|
||||
<string name="license_name_cc_by_sa_four">অ্যাট্রিবিউশন-শেয়ারঅ্যালাইক ৪.০</string>
|
||||
|
|
@ -262,4 +261,5 @@
|
|||
<string name="showcase_view_got_it_button">বুঝেছি!</string>
|
||||
<string name="no_images_found">কোন চিত্র পাওয়া যায়নি!</string>
|
||||
<string name="image_uploaded_by">আপলোড করেছেন: %1$s</string>
|
||||
<string name="error_fetching_nearby_places">কাছাকাছি স্থানগুলি আনতে ত্রুটি।</string>
|
||||
</resources>
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
* Dishual
|
||||
* Fohanno
|
||||
* Fulup
|
||||
* Gwendal
|
||||
* Gwenn-Ael
|
||||
* Y-M D
|
||||
-->
|
||||
|
|
@ -24,7 +25,7 @@
|
|||
<string name="login_success">Kevreet oc\'h !</string>
|
||||
<string name="login_failed">Kudenn gevreañ !</string>
|
||||
<string name="upload_failed">N\'eo ket bet kavet ar restr. Klask gant unan all.</string>
|
||||
<string name="authentication_failed">Dilesadur c\'hwitet!</string>
|
||||
<string name="authentication_failed" fuzzy="true">Dilesadur c\'hwitet!</string>
|
||||
<string name="uploading_started">Kroget da enporzhiañ!</string>
|
||||
<string name="upload_completed_notification_title">%1$s bet enporzhiet !</string>
|
||||
<string name="upload_completed_notification_text">Pouezit evit gwelet hoc\'h enporzhiadenn</string>
|
||||
|
|
@ -52,8 +53,6 @@
|
|||
<string name="add_title_toast">Roit un titl d\'ar restr-mañ, mar plij</string>
|
||||
<string name="share_description_hint">Deskrivadur</string>
|
||||
<string name="login_failed_network">Ne c\'haller ket kevreañ - rouedad sac\'het</string>
|
||||
<string name="login_failed_username">Ne c\'haller ket kevreañ - Gwiriit hoc\'h anv implijer, mar plij</string>
|
||||
<string name="login_failed_password">Ne c\'haller ket kevreañ - Gwiriit ho ker tremen, mar plij</string>
|
||||
<string name="login_failed_throttled">Re a daolioù-esae. Klaskit en-dro a-benn ur pennadig amzer.</string>
|
||||
<string name="login_failed_blocked">Hon digarezit, prennet eo bet an implijer-mañ e Commons</string>
|
||||
<string name="login_failed_2fa_needed">Rankout a rit reiñ ho kod dilesa gant daou faktor.</string>
|
||||
|
|
@ -104,7 +103,7 @@
|
|||
<string name="preference_license">Aotre-implijout dre ziouer</string>
|
||||
<string name="use_previous">Ober gant an titl/deskrivadur kent</string>
|
||||
<string name="allow_gps">Tapout al lec\'hiadur red ent emgefre</string>
|
||||
<string name="allow_gps_summary">Kavout al lec\'hiadur red evit pourchas kinnigoù rummadoù ma n\'eo ket douarlec\'hiet ar skeudenn.</string>
|
||||
<string name="allow_gps_summary" fuzzy="true">Kavout al lec\'hiadur red evit pourchas kinnigoù rummadoù ma n\'eo ket douarlec\'hiet ar skeudenn.</string>
|
||||
<string name="preference_theme">Mod noz</string>
|
||||
<string name="preference_theme_summary">Ober gant an tem teñval</string>
|
||||
<string name="license_name_cc_by_sa_four">Deroadur-RannañHeñvel 4.0</string>
|
||||
|
|
@ -219,4 +218,5 @@
|
|||
<string name="nearby_location_has_not_changed">N\'eo ket cheñchet al lec\'hiadur.</string>
|
||||
<string name="get_directions">Kaout urzhioù</string>
|
||||
<string name="read_article">Lenn ar pennad</string>
|
||||
<string name="appwidget_img">Skeudenn an deiz</string>
|
||||
</resources>
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@
|
|||
<string name="login_success">Prijavljivanje uspješno!</string>
|
||||
<string name="login_failed">Prijavljivanje nije uspjelo!</string>
|
||||
<string name="upload_failed">Datoteka nije pronađena. Pokušajte drugu.</string>
|
||||
<string name="authentication_failed">Provjera identiteta nije uspjela!</string>
|
||||
<string name="authentication_failed" fuzzy="true">Provjera identiteta nije uspjela!</string>
|
||||
<string name="uploading_started">Postavljanje je započelo!</string>
|
||||
<string name="upload_completed_notification_title">Datoteka %1$s je postavljena!</string>
|
||||
<string name="upload_completed_notification_text">Dodirnite da biste vidjeli datoteku</string>
|
||||
|
|
@ -43,8 +43,6 @@
|
|||
<string name="share_title_hint">Naslov</string>
|
||||
<string name="share_description_hint">Opis</string>
|
||||
<string name="login_failed_network">Ne mogu Vas prijaviti – mreža ne radi</string>
|
||||
<string name="login_failed_username">Ne mogu Vas prijaviti – provjerite svoje korisničko ime</string>
|
||||
<string name="login_failed_password">Ne mogu Vas prijaviti – provjerite svoje lozinku</string>
|
||||
<string name="login_failed_throttled">Napravili ste previše grešaka u prijavi. Pokušajte ponovo za nekoliko minuta.</string>
|
||||
<string name="login_failed_blocked">Žao nam je, korisnik je blokiran na Commonsu</string>
|
||||
<string name="login_failed_2fa_needed">Morate upisati kôd za potvrdu u 2 koraka.</string>
|
||||
|
|
@ -95,7 +93,7 @@
|
|||
<string name="preference_license" fuzzy="true">Licenca</string>
|
||||
<string name="use_previous">Koristi prethodni naziv/opis</string>
|
||||
<string name="allow_gps">Automatski dobavi trenutnu lokaciju</string>
|
||||
<string name="allow_gps_summary">Dobavi trenutnu lokaciju za davanje prijedloga o kategorijama ako nema geooznaku</string>
|
||||
<string name="allow_gps_summary" fuzzy="true">Dobavi trenutnu lokaciju za davanje prijedloga o kategorijama ako nema geooznaku</string>
|
||||
<string name="preference_theme">Noćni režim</string>
|
||||
<string name="preference_theme_summary">Koristi tamnu temu</string>
|
||||
<string name="license_name_cc_by_sa_four">Autorstvo-Dijeliti pod istim uslovima 4.0</string>
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@
|
|||
<string name="login_success">S\'ha iniciat sessió correctament!</string>
|
||||
<string name="login_failed">Error en iniciar la sessió!</string>
|
||||
<string name="upload_failed">No s\'ha trobat el fitxer. Proveu-ho amb un altre fitxer.</string>
|
||||
<string name="authentication_failed">L\'autenticació ha fallat!</string>
|
||||
<string name="authentication_failed">L’autenticació ha fallat. Torneu a provar d’iniciar una sessió.</string>
|
||||
<string name="uploading_started">Ha començat la càrrega!</string>
|
||||
<string name="upload_completed_notification_title">S’ha pujat %1$s.</string>
|
||||
<string name="upload_completed_notification_text">Prem per veure la teva càrrega</string>
|
||||
|
|
@ -50,8 +50,6 @@
|
|||
<string name="share_title_hint">Títol</string>
|
||||
<string name="share_description_hint">Descripció</string>
|
||||
<string name="login_failed_network">No s\'ha pogut iniciar la sessió – error de xarxa</string>
|
||||
<string name="login_failed_username">No s\'ha pogut iniciar la sessió – si et plau comprova el teu nom d\'usuari</string>
|
||||
<string name="login_failed_password">No s’ha 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>
|
||||
|
|
@ -192,4 +190,5 @@
|
|||
<string name="notifications_thank_you_edit">Gràcies per fer una modificació</string>
|
||||
<string name="notifications_mention" fuzzy="true">%1$s us ha mencionat a %2$s.</string>
|
||||
<string name="about_faq" fuzzy="true">Preguntes freqüents</string>
|
||||
<string name="no_images_found">No s’ha trobat cap imatge.</string>
|
||||
</resources>
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@
|
|||
<string name="login_success">Přihlášení uspělo!</string>
|
||||
<string name="login_failed">Přihlášení se nezdařilo!</string>
|
||||
<string name="upload_failed">Soubor nebyl nalezen. Prosím, zkuste jiný soubor.</string>
|
||||
<string name="authentication_failed">Ověření se nezdařilo!</string>
|
||||
<string name="authentication_failed">Ověření se nezdařilo, prosím přihlaste se znovu</string>
|
||||
<string name="uploading_started">Nahrávání začalo!</string>
|
||||
<string name="upload_completed_notification_title">%1$s nahráno!</string>
|
||||
<string name="upload_completed_notification_text">Klepnutím zobrazíte upload</string>
|
||||
|
|
@ -61,8 +61,6 @@
|
|||
<string name="add_title_toast">Vložte prosím název tohoto souboru</string>
|
||||
<string name="share_description_hint">Popis</string>
|
||||
<string name="login_failed_network">Nelze se přihlásit - selhání sítě</string>
|
||||
<string name="login_failed_username">Nelze se přihlásit - prosím zkontrolujte své uživatelské jméno</string>
|
||||
<string name="login_failed_password">Nelze se přihlásit - zkontrolujte prosím své heslo</string>
|
||||
<string name="login_failed_throttled">Příliš mnoho neúspěšných pokusů. Zkuste to prosím znovu za několik minut.</string>
|
||||
<string name="login_failed_blocked">Omlouváme se, tento uživatel byl na Commons zablokován</string>
|
||||
<string name="login_failed_2fa_needed">Prosím vložte kód pro své dvoufázové ověření.</string>
|
||||
|
|
@ -115,7 +113,7 @@
|
|||
<string name="preference_license">Výchozí licence</string>
|
||||
<string name="use_previous">Použít předchozí název a popis</string>
|
||||
<string name="allow_gps">Automaticky získat aktuální polohu</string>
|
||||
<string name="allow_gps_summary">Nabídnout kategorie na základě aktuální polohy (pokud není obrázek opatřen souřadnicemi)</string>
|
||||
<string name="allow_gps_summary" fuzzy="true">Nabídnout kategorie na základě aktuální polohy (pokud není obrázek opatřen souřadnicemi)</string>
|
||||
<string name="preference_theme">Noční režim</string>
|
||||
<string name="preference_theme_summary">Použít tmavý režim</string>
|
||||
<string name="license_name_cc_by_sa_four">Uveďte autora-Zachovejte licenci 4.0</string>
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@
|
|||
<string name="login_success">Ùdałi logòwanié!</string>
|
||||
<string name="login_failed">Logòwanié nie darzëło sã!</string>
|
||||
<string name="upload_failed">Felënk lopka. Proszã spróbòwac znowa.</string>
|
||||
<string name="authentication_failed">Fela ùdowierzaniô!</string>
|
||||
<string name="authentication_failed" fuzzy="true">Fela ùdowierzaniô!</string>
|
||||
<string name="uploading_started">Wladënk zrëszony!</string>
|
||||
<string name="upload_completed_notification_title">%1$s wladowóné!</string>
|
||||
<string name="upload_completed_notification_text">Tkni, abë òbôczëc ladowóny lopk</string>
|
||||
|
|
@ -41,8 +41,6 @@
|
|||
<string name="share_title_hint">Titel</string>
|
||||
<string name="share_description_hint">Òpisënk</string>
|
||||
<string name="login_failed_network">Ni mòże sã wlogòwac - fela sécë</string>
|
||||
<string name="login_failed_username">Ni mòże sã wlogòwac - sprôwdzë miono brëkòwnika</string>
|
||||
<string name="login_failed_password">Ni mòże sã wlogòwac - sprôwdzë parolã</string>
|
||||
<string name="login_failed_throttled">Za wiele nieùdałich prób wlogòwaniô. Spróbùjë znowa za czile minut.</string>
|
||||
<string name="login_failed_blocked">Nen brëkòwnik òstôł zablokòwóny na Commons</string>
|
||||
<string name="login_failed_2fa_needed">Mùszisz wpisac swój kòd dlô dwafaktorowi aùtorizacëji.</string>
|
||||
|
|
|
|||
|
|
@ -5,18 +5,25 @@
|
|||
* Robin Owain
|
||||
-->
|
||||
<resources>
|
||||
<string name="preference_category_appearance">Ymddangosiad</string>
|
||||
<string name="preference_category_general">Cyffredinol</string>
|
||||
<string name="preference_category_feedback">Adborth</string>
|
||||
<string name="preference_category_location">Lleoliad</string>
|
||||
<string name="app_name">Comin Wicimedia</string>
|
||||
<string name="bullet">•</string>
|
||||
<string name="menu_settings">Gosodiadau</string>
|
||||
<string name="username">Enw defnyddiwr</string>
|
||||
<string name="password">Cyfrinair</string>
|
||||
<string name="login_credential">Mewngofnodwch i\'ch cyfri Comin Beta</string>
|
||||
<string name="login">Mewngofnodi</string>
|
||||
<string name="forgot_password">Anghofiwyd y Cyfrinair?</string>
|
||||
<string name="signup">Cofrestru</string>
|
||||
<string name="logging_in_title">Wrthi\'n mewngofnodi</string>
|
||||
<string name="logging_in_message">Disgwyliwch…</string>
|
||||
<string name="login_success">Llwyddodd y mewngofnodi!</string>
|
||||
<string name="login_failed">Methodd y mewngofnodi!</string>
|
||||
<string name="upload_failed">Ni chafwyd hyd i\'r ffeil. Ceisiwch un arall.</string>
|
||||
<string name="authentication_failed">Methodd y dilysu!</string>
|
||||
<string name="authentication_failed">Methodd y dilysu! Mewngofnodwch eto.</string>
|
||||
<string name="uploading_started">Dechreuodd yr uwchlwytho!</string>
|
||||
<string name="upload_completed_notification_title">Uwchlwythwyd %1$s!</string>
|
||||
<string name="upload_completed_notification_text">Tapiwch i weld eich uwchlwythiad</string>
|
||||
|
|
@ -41,10 +48,10 @@
|
|||
<string name="menu_share">Rhannu</string>
|
||||
<string name="menu_open_in_browser">Agor yn y Porwr</string>
|
||||
<string name="share_title_hint">Teitl</string>
|
||||
<string name="add_title_toast">Rhowch deitl i\'r ffeil</string>
|
||||
<string name="share_description_hint">Disgrifiad</string>
|
||||
<string name="login_failed_network">Yn methu mewngofnodi - methodd y rhwydwaith</string>
|
||||
<string name="login_failed_username">Yn methu mewngofnodi - gwirwch eich enw defnyddiwr</string>
|
||||
<string name="login_failed_password">Yn methu mewngofnodi - gwirwch eich cyfrinair</string>
|
||||
<string name="login_failed_wrong_credentials">Methwyd mewngofnodi - gwirwch eich enw defnyddiwr a\'ch cyfrinair</string>
|
||||
<string name="login_failed_throttled">Cafwyd gormod o ymgeision aflwyddiannus. Oedwch ennyd cyn ceisio eto.</string>
|
||||
<string name="login_failed_blocked">Ymddiheurwn. Mae\'r defnyddiwr hwn wedi ei flocio ar Gomin Wikimedia</string>
|
||||
<string name="login_failed_2fa_needed">Mae\'n rhaid i chi roi eich cod adnabod 2 ffactor.</string>
|
||||
|
|
@ -56,6 +63,7 @@
|
|||
<string name="categories_search_text_hint">Archwilio\'r categorïau</string>
|
||||
<string name="menu_save_categories">Cadw</string>
|
||||
<string name="refresh_button">Ailgyrchu</string>
|
||||
<string name="display_list_button">Rhestr</string>
|
||||
<string name="gps_disabled">Ataliwyd GPS ar eich dyfais. Ydych chi am ei droi\'n weithredol?</string>
|
||||
<string name="enable_gps">Gweithredu\'r GPS</string>
|
||||
<string name="contributions_subtitle_zero">Heb uwchlwytho eto</string>
|
||||
|
|
@ -77,11 +85,12 @@
|
|||
<string name="categories_activity_title">Categorïau</string>
|
||||
<string name="title_activity_settings">Gosodiadau</string>
|
||||
<string name="title_activity_signup">Cofrestru</string>
|
||||
<string name="title_activity_featured_images">Delweddau nodwedd</string>
|
||||
<string name="menu_about">Amdanom</string>
|
||||
<string name="about_license">Ap Cynnwys Agored a grewyd ac a gefnogir gan wirfoddolwyr cymuned Wicimedia yw ap Comin Wicimedia. Does a wnelo Sefydliad Wicimedia ddim byd ag e (ei greu, ei gynnal na\'i ddatblygu).</string>
|
||||
<string name="about_improve">\n\nCrewch <a href=\"https://github.com/commons-app/apps-android-commons/issues\">ymholiad GitHub</a> os oes gennych fyg, broblem neu awgrym.</string>
|
||||
<string name="about_privacy_policy" fuzzy="true"><a href=\"https://github.com/commons-app/apps-android-commons/wiki/Privacy-policy\">Polisi Preifatrwydd</a></string>
|
||||
<string name="about_credits" fuzzy="true"><a href=\"https://github.com/commons-app/apps-android-commons/blob/master/CREDITS\">Clod</a></string>
|
||||
<string name="about_privacy_policy"><u>Polisi preifatrwydd</u></string>
|
||||
<string name="about_credits">Clod a bri</string>
|
||||
<string name="title_activity_about">Amdanom</string>
|
||||
<string name="menu_feedback">Danfonwch Adborth (drwy Ebost)</string>
|
||||
<string name="no_email_client">Dim ebost client wedi\'i ganfod</string>
|
||||
|
|
@ -93,10 +102,10 @@
|
|||
<string name="share_license_summary">Caiff y ddelwedd hon ei thrwyddedu yn ôl termau\'r drwydded %1$s.</string>
|
||||
<string name="media_upload_policy">Wrth gynnig y llun yma, rwy\'n datgan mai fy ngwaith i ydyw ac nad yw\'n cynnwys unrhyw beth dan hawlfrain, na hunlun, a\'i fod yn cadw at <a href=\"https://commons.wikimedia.org/wiki/Commons:Policies_and_guidelines\">Bolisiau Comin Wicimedia</a>.</string>
|
||||
<string name="menu_download">Lawrlwytho</string>
|
||||
<string name="preference_license" fuzzy="true">Trwydded</string>
|
||||
<string name="preference_license">Trwydded Ddiofyn (\'default\')</string>
|
||||
<string name="use_previous">Defnydiwch y teitl/disgrifiad blaenorol</string>
|
||||
<string name="allow_gps">Defnyddiwch y lleoliad cyfredol</string>
|
||||
<string name="allow_gps_summary">Canfyddwch eich lleoliad, er mwyn i ni gynnig categori (os nad ydych wedi nodi\'r cyfesurynnau).</string>
|
||||
<string name="allow_gps_summary">Adfer eich lleoliad presennol os nad yw\'r ddelwedd yn cynnwys cyfesurynnau. Bydd hyn yn datgelu eich lleoliad chi!</string>
|
||||
<string name="preference_theme">Modd fin nos</string>
|
||||
<string name="preference_theme_summary">Defnyddiwch thema tywyll</string>
|
||||
<string name="license_name_cc_by_sa_four"> Attribution-ShareAlike 4.0</string>
|
||||
|
|
@ -122,9 +131,19 @@
|
|||
<string name="tutorial_1_text">Cyhelir llawer o luniau ar Gomin Wicimedia sy\'n cael eu defnyddio ar Wicipedia.</string>
|
||||
<string name="tutorial_1_subtext">Mae eich lluniau\'n gymorth i addysgu pobl drwy\'r byd mawr crwn!</string>
|
||||
<string name="tutorial_2_text">Uwchlwythwch lluniau a dynnoch eich hun:</string>
|
||||
<string name="tutorial_2_subtext" fuzzy="true">- Natur (blodau, anifeiliaid, mynyddoedd)\n- Pethau defnyddiol (beic, tren, gorsaf drenau)\n- Enwogion (beirdd, athletwyr, blogwyr)</string>
|
||||
<string name="tutorial_2_subtext">Gwrthrychau byd natur (blodau, anifeiliaid, mynyddoedd)\n- Gwrthrychau defnyddiol (beics, trenau, gorsafoedd trenau)\n- Enwogion (beirdd, athletwyr, blogwyr)</string>
|
||||
<string name="tutorial_2_subtext_1">Gwrthrychau byd natur (blodau, anifeiliaid, mynyddoedd)</string>
|
||||
<string name="tutorial_2_subtext_2">Gwrthrychau defnyddiol (beics, trenau, gorsafoedd trenau)</string>
|
||||
<string name="tutorial_2_subtext_3">Enwogion (beirdd, athletwyr, blogwyr)</string>
|
||||
<string name="tutorial_3_text">Peidiwch ag uwchlwytho:</string>
|
||||
<string name="tutorial_3_subtext">- hunanluniau ohonoch chi na\'ch ffrindiau\n- lluniau a gawsoch o\'r we\n- sgrinluniau o apiau masnachol</string>
|
||||
<string name="tutorial_3_subtext_1">Hunanluniau neu luniau o\'ch ffrindiau</string>
|
||||
<string name="tutorial_3_subtext_2">Lluniau a lawrlwythwyd o\'r we gennych</string>
|
||||
<string name="tutorial_3_subtext_3">Sgrinluniau o aps</string>
|
||||
<string name="tutorial_4_text">Enghraifft o uwchlwythiad:</string>
|
||||
<string name="tutorial_4_subtext_1">Teitl:Tŷ Opera Sydney</string>
|
||||
<string name="tutorial_4_subtext_2">Disgrifiad: Golygfa o Dŷ Opera Sydney o ochr arall y bae</string>
|
||||
<string name="tutorial_4_subtext_3">Categoriau: Tŷ Opera Sydney o\'r gorllewin</string>
|
||||
<string name="welcome_wikipedia_text">Cyfranwch luniau. Cynorthwywch Wicipedia i roi bywyd yn yr erthyglau!</string>
|
||||
<string name="welcome_wikipedia_subtext">Mae\'r delweddau ar Wicipedia\'n dod o\nGomin Wikimedia.</string>
|
||||
<string name="welcome_copyright_text">Mae eich delweddau\'n cynorthwyo i addysgu pobl ledled y byd.</string>
|
||||
|
|
@ -137,6 +156,32 @@
|
|||
<string name="detail_description_empty">Dim disgrifiad</string>
|
||||
<string name="detail_license_empty">Trwydded anhysbys</string>
|
||||
<string name="menu_refresh">Adnewyddu</string>
|
||||
<string name="ok">Iawn</string>
|
||||
<string name="title_activity_nearby">Lleoedd Cyfagos</string>
|
||||
<string name="no_nearby">Ni chafwyd hyd i leoedd cyfagos</string>
|
||||
<string name="warning">Rhybudd</string>
|
||||
<string name="yes">Ydw</string>
|
||||
<string name="no">Nac ydw</string>
|
||||
<string name="media_detail_title">Teitl</string>
|
||||
<string name="media_detail_media_title">teitl y cyfrwng</string>
|
||||
<string name="media_detail_description">Disgrifiad</string>
|
||||
<string name="media_detail_author">Awdur</string>
|
||||
<string name="media_detail_uploaded_date">Dyddiad yr uwchlwythiad</string>
|
||||
<string name="media_detail_license">Trwydded</string>
|
||||
<string name="media_detail_coordinates">Cyfesurynnau</string>
|
||||
<string name="media_detail_coordinates_empty">Dim</string>
|
||||
<string name="nearby_info_menu_wikidata_article">Eitem Wicidata</string>
|
||||
<string name="nearby_info_menu_wikipedia_article">Erthygl Wicipedia</string>
|
||||
<string name="login_to_your_account">Mewngofnodwch i\'ch cyfri</string>
|
||||
<string name="send_log_file">Danfonwch y ffeil log</string>
|
||||
<string name="view_browser">Gweld yn y porwr</string>
|
||||
<string name="nearby_location_has_not_changed">Nid yw\'r lleoliad wedi newid.</string>
|
||||
<string name="nearby_location_not_available">Nid yw\'r lleoliad ar gael.</string>
|
||||
<string name="about_translate_proceed">Parhau</string>
|
||||
<string name="about_translate_cancel">Canslo</string>
|
||||
<string name="retry">Ailgeisio</string>
|
||||
<string name="showcase_view_got_it_button">Gwnaed!</string>
|
||||
<string name="appwidget_img">Llun y Dydd</string>
|
||||
<string name="app_widget_heading">Llun y Dydd</string>
|
||||
<string name="successful_wikidata_edit">Mae %1$s o luniau wedi\'u hychwanegu ar Wicidata!</string>
|
||||
</resources>
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@
|
|||
<string name="login_success">Du er nu logget på!</string>
|
||||
<string name="login_failed">Det mislykkedes at logge på!</string>
|
||||
<string name="upload_failed">Filen blev ikke fundet. Forsøg med en anden fil.</string>
|
||||
<string name="authentication_failed">Godkendelse mislykkedes!</string>
|
||||
<string name="authentication_failed" fuzzy="true">Godkendelse mislykkedes!</string>
|
||||
<string name="uploading_started">Overførsel begyndt!</string>
|
||||
<string name="upload_completed_notification_title">%1$s overført!</string>
|
||||
<string name="upload_completed_notification_text">Tryk for at få vist din upload</string>
|
||||
|
|
@ -54,8 +54,6 @@
|
|||
<string name="add_title_toast">Angiv venligt en titel for denne fil</string>
|
||||
<string name="share_description_hint">Beskrivelse</string>
|
||||
<string name="login_failed_network">Kan ikke logge på - netværksfejl</string>
|
||||
<string name="login_failed_username">Ude af stand til at logge på - tjek venligst dit brugernavn</string>
|
||||
<string name="login_failed_password">Ude af stand til at logge på - tjek venligst din adgangskode</string>
|
||||
<string name="login_failed_throttled">Alt for mange mislykkede forsøg. Prøv igen om et par minutter.</string>
|
||||
<string name="login_failed_blocked">Beklager, denne bruger er blevet blokeret på Commons</string>
|
||||
<string name="login_failed_2fa_needed">Du skal angive din tofaktorgodkendelseskode.</string>
|
||||
|
|
@ -107,7 +105,7 @@
|
|||
<string name="preference_license">Standardlicens</string>
|
||||
<string name="use_previous">Brug forrige titel/beskrivelse</string>
|
||||
<string name="allow_gps">Hent automatisk nuværende placering</string>
|
||||
<string name="allow_gps_summary">Hent nuværende placering for at tilbyde kategoriforslag hvis billedet ikke er geografisk mærket</string>
|
||||
<string name="allow_gps_summary" fuzzy="true">Hent nuværende placering for at tilbyde kategoriforslag hvis billedet ikke er geografisk mærket</string>
|
||||
<string name="preference_theme">Nat-tilstand</string>
|
||||
<string name="preference_theme_summary">Brug mørkt tema</string>
|
||||
<string name="license_name_cc_by_sa_four"> Attribution-ShareAlike 4.0</string>
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@
|
|||
<string name="login_success">Anmeldung erfolgreich!</string>
|
||||
<string name="login_failed">Anmeldung fehlgeschlagen!</string>
|
||||
<string name="upload_failed">Datei nicht gefunden. Bitte versuche es mit einer anderen.</string>
|
||||
<string name="authentication_failed">Authentifizierung fehlgeschlagen!</string>
|
||||
<string name="authentication_failed">Authentifizierung fehlgeschlagen. Bitte erneut anmelden.</string>
|
||||
<string name="uploading_started">Hochladen gestartet!</string>
|
||||
<string name="upload_completed_notification_title">„%1$s“ hochgeladen!</string>
|
||||
<string name="upload_completed_notification_text">Tippe, um deinen Upload anzusehen</string>
|
||||
|
|
@ -53,8 +53,7 @@
|
|||
<string name="add_title_toast">Bitte gib einen Titel für diese Datei an</string>
|
||||
<string name="share_description_hint">Beschreibung</string>
|
||||
<string name="login_failed_network">Anmeldung fehlgeschlagen – Netzwerkfehler</string>
|
||||
<string name="login_failed_username">Anmeldung fehlgeschlagen – Bitte Benutzernamen überprüfen</string>
|
||||
<string name="login_failed_password">Anmeldung fehlgeschlagen – Bitte Passwort überprüfen</string>
|
||||
<string name="login_failed_wrong_credentials">Anmeldung fehlgeschlagen. Bitte Benutzernamen und Passwort überprüfen.</string>
|
||||
<string name="login_failed_throttled">Zu viele erfolglose Versuche. Bitte in einigen Minuten erneut versuchen.</string>
|
||||
<string name="login_failed_blocked">Dieser Benutzer wurde leider auf Commons gesperrt</string>
|
||||
<string name="login_failed_2fa_needed">Du musst deinen Code zur Zwei-Faktor-Authentifizierung angeben.</string>
|
||||
|
|
@ -108,7 +107,7 @@
|
|||
<string name="preference_license">Standardlizenz</string>
|
||||
<string name="use_previous">Vorherige(n) Titel/Beschreibung verwenden</string>
|
||||
<string name="allow_gps">Aktuellen Standort automatisch abrufen</string>
|
||||
<string name="allow_gps_summary">Ruft den aktuellen Standort ab, um Kategorievorschläge anzubieten, falls das Bild keine Geotags hat.</string>
|
||||
<string name="allow_gps_summary">Ruft den aktuellen Standort ab, falls das Bild nicht georeferenziert ist und markiert es. Warnung: Diese Aktion verrät deinen aktuellen Standort.</string>
|
||||
<string name="preference_theme">Nachtmodus</string>
|
||||
<string name="preference_theme_summary">Dunkles Thema verwenden</string>
|
||||
<string name="license_name_cc_by_sa_four">Attribution-ShareAlike 4.0</string>
|
||||
|
|
@ -278,4 +277,11 @@
|
|||
<string name="image_uploaded_by">Hochgeladen von: %1$s</string>
|
||||
<string name="share_app_title">App teilen</string>
|
||||
<string name="share_coordinates_not_present">Während der Bildauswahl wurden keine Koordinaten angegeben</string>
|
||||
<string name="error_fetching_nearby_places">Fehler beim Abrufen der Orte in der Nähe.</string>
|
||||
<string name="appwidget_img">Bild des Tages</string>
|
||||
<string name="app_widget_heading">Bild des Tages</string>
|
||||
<string name="successful_wikidata_edit">Bild erfolgreich nach %1$s auf Wikidata hinzugefügt!</string>
|
||||
<string name="wikidata_edit_failure">Fehler bei der Aktualisierung des dazugehörigen Wikidata-Objekts!</string>
|
||||
<string name="menu_set_wallpaper">Hintergrundbild festlegen</string>
|
||||
<string name="wallpaper_set_successfully">Hintergrundbild erfolgreich festgelegt!</string>
|
||||
</resources>
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@
|
|||
<string name="preference_category_general">Bıngeh</string>
|
||||
<string name="preference_category_location">Lokasyon</string>
|
||||
<string name="app_name">Commons</string>
|
||||
<string name="bullet">•</string>
|
||||
<string name="menu_settings">Eyari</string>
|
||||
<string name="username">Namey karberi</string>
|
||||
<string name="password">Parola</string>
|
||||
|
|
@ -24,7 +25,7 @@
|
|||
<string name="login_success">Cıkewtış hewl bi.</string>
|
||||
<string name="login_failed">Nidekeweya de</string>
|
||||
<string name="upload_failed">Dosya nêvineya. Dosyê da bine bıcerebnê.</string>
|
||||
<string name="authentication_failed">Tesdiq kerdış nebı</string>
|
||||
<string name="authentication_failed" fuzzy="true">Tesdiq kerdış nebı</string>
|
||||
<string name="uploading_started">Barkerdış sertera!</string>
|
||||
<string name="upload_completed_notification_title">%1$s bıbar!</string>
|
||||
<string name="upload_completed_notification_text">Barkerdışê xo pıro bıde.</string>
|
||||
|
|
@ -51,8 +52,6 @@
|
|||
<string name="share_title_hint">Sername</string>
|
||||
<string name="share_description_hint">Şınasnayış</string>
|
||||
<string name="login_failed_network">Xırabiya kewten-network xeta</string>
|
||||
<string name="login_failed_username">Ronıştışo abeno - Namey karberi ye xo kontrol kerë</string>
|
||||
<string name="login_failed_password">Ronıştışo nêabeno - Parolay xo kontrol kerë</string>
|
||||
<string name="login_failed_throttled">Şıma xeylê rayi kerd ke cı kewê, a ser nêvıst. Şıma rê zehmet 2-3 deqey ra tepeya reyna bıcerrebnên.</string>
|
||||
<string name="login_failed_blocked">Qısur mewni rê, Karber commons dı bloqe biyo.</string>
|
||||
<string name="login_failed_generic">Nidekeweya de</string>
|
||||
|
|
@ -84,7 +83,8 @@
|
|||
<string name="title_activity_signup">Qeyd be</string>
|
||||
<string name="menu_about">Heq te cı</string>
|
||||
<string name="about_improve">Qandê yew <a href=\"https://github.com/commons-app/apps-android-commons/issues\">GitHub-cıkewtış</a>ê neweyi rê rapor û teklifan bıaferne.</string>
|
||||
<string name="about_privacy_policy" fuzzy="true"><a href=\"https://github.com/commons-app/apps-android-commons/wiki/Privacy-policy\">Politikay nımıtışi</a></string>
|
||||
<string name="about_privacy_policy"><u>Politikaya nımıtışi</u></string>
|
||||
<string name="about_credits"><u>İştırakkerdoği</u></string>
|
||||
<string name="title_activity_about">Heq te cı</string>
|
||||
<string name="menu_feedback">Peyd rışten bırış (E-posta ra)</string>
|
||||
<string name="no_email_client">E-posta eyar nêbi</string>
|
||||
|
|
@ -92,7 +92,7 @@
|
|||
<string name="menu_retry_upload">Anciya bıcerrebne</string>
|
||||
<string name="menu_cancel_upload">Bıtexelne</string>
|
||||
<string name="menu_download">Ron</string>
|
||||
<string name="preference_license" fuzzy="true">Lisans</string>
|
||||
<string name="preference_license">Lisanso hesebiyaye</string>
|
||||
<string name="license_name_cc_by_sa"> Attribution-ShareAlike 3.0</string>
|
||||
<string name="license_name_cc_by">Attribution 3.0</string>
|
||||
<string name="license_name_cc0">CC0</string>
|
||||
|
|
@ -127,6 +127,8 @@
|
|||
<string name="yes">E</string>
|
||||
<string name="no">Nê</string>
|
||||
<string name="media_detail_title">Sername</string>
|
||||
<string name="media_detail_description">Şınasnayış</string>
|
||||
<string name="media_detail_author">Nuştekar</string>
|
||||
<string name="media_detail_license">Lisans</string>
|
||||
<string name="media_detail_coordinates">Koordinati</string>
|
||||
<string name="welcome_image_tulip">Korbıze</string>
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@
|
|||
<string name="login_success">Επιτυχής σύνδεση!</string>
|
||||
<string name="login_failed">Η είσοδος απέτυχε!</string>
|
||||
<string name="upload_failed">Το αρχείο δεν βρέθηκε. Παρακαλώ δοκιμάστε ένα άλλο αρχείο.</string>
|
||||
<string name="authentication_failed">Απέτυχε ο έλεγχος ταυτότητας!</string>
|
||||
<string name="authentication_failed">Απέτυχε ο έλεγχος ταυτότητας, παρακαλώ συνδεθείτε ξανά</string>
|
||||
<string name="uploading_started">Η αποστολή ξεκίνησε!</string>
|
||||
<string name="upload_completed_notification_title">%1$s επιφορτώθηκε!</string>
|
||||
<string name="upload_completed_notification_text">Πατήστε για να προβάλλετε την αποστολή</string>
|
||||
|
|
@ -57,8 +57,7 @@
|
|||
<string name="add_title_toast">Παρακαλώ παρέχετε ένα τίτλο για αυτό το αρχείο</string>
|
||||
<string name="share_description_hint">Περιγραφή</string>
|
||||
<string name="login_failed_network">Δεν είναι δυνατή η σύνδεση - αποτυχία του δικτύου</string>
|
||||
<string name="login_failed_username">Δεν είναι δυνατή η σύνδεση - ελέγξτε το όνομα χρήστη σας</string>
|
||||
<string name="login_failed_password">Δεν είναι δυνατή η σύνδεση - παρακαλούμε ελέγξτε τον κωδικό σας</string>
|
||||
<string name="login_failed_wrong_credentials">Αποτυχία σύνδεσης - παρακαλώ ελέγξτε το όνομα χρήστη και τον κωδικό σας</string>
|
||||
<string name="login_failed_throttled">Πάρα πολλές ανεπιτυχείς προσπάθειες. Παρακαλώ δοκιμάστε ξανά σε λίγα λεπτά.</string>
|
||||
<string name="login_failed_blocked">Συγνώμη, αυτός ο χρήστης έχει αποκλειστεί από τα Commons</string>
|
||||
<string name="login_failed_2fa_needed">Πρέπει να δώσετε τον κωδικό πιστοποίησης με δύο παράγοντες</string>
|
||||
|
|
@ -112,7 +111,7 @@
|
|||
<string name="preference_license">Προεπιλεγμένη άδεια</string>
|
||||
<string name="use_previous">Χρήση προηγούμενου τίτλου/περιγραφής</string>
|
||||
<string name="allow_gps">Αυτόματη ανάκτηση τρέχουσας θέσης</string>
|
||||
<string name="allow_gps_summary">Ανάκτηση τρέχουσας τοποθεσία για να σας προσφέρουμε προτάσεις κατηγοριών αν η εικόνα δεν είναι γεωσεσημασμένη.</string>
|
||||
<string name="allow_gps_summary">Ανακτά την τρέχουσα τοποθεσία εάν η εικόνα δεν είναι γεωσεσημασμένη, και τις γεωσημάνσεις με αυτή. Προειδοποίηση: Αυτό θα αποκαλύψει την τρέχουσα τοποθεσία σας.</string>
|
||||
<string name="preference_theme">Νυχτερινή λειτουργία</string>
|
||||
<string name="preference_theme_summary">Χρήση σκοτεινού θέματος</string>
|
||||
<string name="license_name_cc_by_sa_four"> Attribution-ShareAlike 4.0</string>
|
||||
|
|
@ -282,4 +281,11 @@
|
|||
<string name="image_uploaded_by">Ανέβηκε από: %1$s</string>
|
||||
<string name="share_app_title">Κοινοποίηση εφαρμογής</string>
|
||||
<string name="share_coordinates_not_present">Οι συντεταγμένες δεν ορίστηκαν κατά την διάρκεια της επιλογής εικόνας</string>
|
||||
<string name="error_fetching_nearby_places">Σφάλμα κατά την εύρεση κοντινών μερών.</string>
|
||||
<string name="appwidget_img">Φωτογραφία της Ημέρας</string>
|
||||
<string name="app_widget_heading">Φωτογραφία της Ημέρας</string>
|
||||
<string name="successful_wikidata_edit">Η εικόνα προστέθηκε επιτυχώς στο %1$s στο Wikidata!</string>
|
||||
<string name="wikidata_edit_failure">Αποτυχία ενημέρωσης της αντιστοιχούσας οντότητας του Wikidata!</string>
|
||||
<string name="menu_set_wallpaper">Ρύθμιση ταπετσαρίας</string>
|
||||
<string name="wallpaper_set_successfully">Η ταπετσαρία ρυθμίστηκε επιτυχώς!</string>
|
||||
</resources>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Authors:
|
||||
* 2axterix2
|
||||
* Adjen
|
||||
* Benfutbol10
|
||||
* Fitoschido
|
||||
* Jduranboger
|
||||
|
|
@ -29,7 +30,7 @@
|
|||
<string name="login_success">Acceso correcto.</string>
|
||||
<string name="login_failed">Acceso fallido.</string>
|
||||
<string name="upload_failed">No se encontró el archivo. Prueba con otro.</string>
|
||||
<string name="authentication_failed">Falló la autenticación.</string>
|
||||
<string name="authentication_failed">Falló la autenticación; prueba a acceder otra vez</string>
|
||||
<string name="uploading_started">Ha comenzado la carga.</string>
|
||||
<string name="upload_completed_notification_title">Se ha cargado %1$s.</string>
|
||||
<string name="upload_completed_notification_text">Pulsa para ver tu subida</string>
|
||||
|
|
@ -57,8 +58,7 @@
|
|||
<string name="add_title_toast">Proporciona un título para este archivo</string>
|
||||
<string name="share_description_hint">Descripción</string>
|
||||
<string name="login_failed_network">No se pudo iniciar sesión: falla de red</string>
|
||||
<string name="login_failed_username">No se pudo iniciar sesión: revisa tu nombre de usuario</string>
|
||||
<string name="login_failed_password">No se pudo iniciar sesión: revisa tu contraseña</string>
|
||||
<string name="login_failed_wrong_credentials">No se puede acceder. Revisa el nombre de usuario y la contraseña</string>
|
||||
<string name="login_failed_throttled">Demasiados intentos fallidos. Inténtalo de nuevo en unos minutos.</string>
|
||||
<string name="login_failed_blocked">Lo sentimos, este usuario ha sido bloqueado en Commons</string>
|
||||
<string name="login_failed_2fa_needed">Debes proporcionar tu código de auntenticación de dos factores.</string>
|
||||
|
|
@ -112,7 +112,7 @@
|
|||
<string name="preference_license">Licencia predeterminada</string>
|
||||
<string name="use_previous">Usar título/descripción anteriores</string>
|
||||
<string name="allow_gps">Obtener ubicación actual automáticamente</string>
|
||||
<string name="allow_gps_summary">Recuperar ubicación actual para ofrecer sugerencias de categorías si la imagen no tiene etiquetas geográficas</string>
|
||||
<string name="allow_gps_summary" fuzzy="true">Recuperar ubicación actual para ofrecer sugerencias de categorías si la imagen no tiene etiquetas geográficas</string>
|
||||
<string name="preference_theme">Modo nocturno</string>
|
||||
<string name="preference_theme_summary">Usar tema oscuro</string>
|
||||
<string name="license_name_cc_by_sa_four"> Atribución-CompartirIgual 4.0</string>
|
||||
|
|
@ -275,4 +275,7 @@
|
|||
<string name="image_uploaded_by">Cargada por: %1$s</string>
|
||||
<string name="share_app_title">Compartir aplicación</string>
|
||||
<string name="share_coordinates_not_present">No se especificaron las coordenadas al seleccionar la imagen</string>
|
||||
<string name="error_fetching_nearby_places">Error al recuperar los lugares cercanos.</string>
|
||||
<string name="appwidget_img">Foto del día</string>
|
||||
<string name="app_widget_heading">Foto del día</string>
|
||||
</resources>
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@
|
|||
<string name="login_success">Saio hasiera egina</string>
|
||||
<string name="login_failed">Saio hasieran akatsa!</string>
|
||||
<string name="upload_failed">Fitxategia ez da aurkitu. Mesedez saiatu beste batekin.</string>
|
||||
<string name="authentication_failed">Autentifikazioan akatsa!</string>
|
||||
<string name="authentication_failed" fuzzy="true">Autentifikazioan akatsa!</string>
|
||||
<string name="uploading_started">Igoera hasi da!</string>
|
||||
<string name="upload_completed_notification_title">%1$s igotzen!</string>
|
||||
<string name="upload_completed_notification_text">Ukitu igotakoa ikusteko</string>
|
||||
|
|
@ -48,8 +48,6 @@
|
|||
<string name="share_title_hint">Izenburua</string>
|
||||
<string name="share_description_hint">Deskribapena</string>
|
||||
<string name="login_failed_network">Ezin izan da sartu - sarean akatsa</string>
|
||||
<string name="login_failed_username">Ezin izan da sartu - ziurtatu ezazu zure erabiltzaile izena</string>
|
||||
<string name="login_failed_password">Ezin izan da sartu - ziurta ezazu zure pasahitza</string>
|
||||
<string name="login_failed_throttled">Sartzeko saiakera txar gehiegi. Mesedez saiatu zaitez minutu batzuk barru.</string>
|
||||
<string name="login_failed_blocked">Barka, baina erabiltzaile hau blokeatuta dago Commonsen</string>
|
||||
<string name="login_failed_generic">Saio hasieran akatsa</string>
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@
|
|||
<string name="login_success">ورود موفق!</string>
|
||||
<string name="login_failed">ورود ناموفق!</string>
|
||||
<string name="upload_failed">پرونده یافت نشد لطفاً پرونده دیگری را امتحان کنید.</string>
|
||||
<string name="authentication_failed">تأیید اعتبار انجام نشد!</string>
|
||||
<string name="authentication_failed" fuzzy="true">تأیید اعتبار انجام نشد!</string>
|
||||
<string name="uploading_started">بارگذاری آغاز شد!</string>
|
||||
<string name="upload_completed_notification_title">%1$s بارگذاری شد!</string>
|
||||
<string name="upload_completed_notification_text">برای دیدن بارگذاریتان بر روی صفحه انگشت بزنید</string>
|
||||
|
|
@ -56,8 +56,6 @@
|
|||
<string name="add_title_toast">لطفاً نامی را برای این پرونده انتخاب کنید</string>
|
||||
<string name="share_description_hint">توضیحات</string>
|
||||
<string name="login_failed_network">قادر به ورود نیست - شکست شبکهای</string>
|
||||
<string name="login_failed_username">ناتوانی در ورود - لطفاً نام کاربریتان را بررسی کنید</string>
|
||||
<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>
|
||||
|
|
@ -106,7 +104,7 @@
|
|||
<string name="preference_license">مجوز پیشفرض</string>
|
||||
<string name="use_previous">از عنوان/توضیحات پیشین استفاده کنید</string>
|
||||
<string name="allow_gps">دریافت خودکار موقعیت کنونی</string>
|
||||
<string name="allow_gps_summary">درحال دریافت موقعیت برای پیشنهاد رده در صورتی که برچسب جغرافیایی وجود نداشته باشد.</string>
|
||||
<string name="allow_gps_summary" fuzzy="true">درحال دریافت موقعیت برای پیشنهاد رده در صورتی که برچسب جغرافیایی وجود نداشته باشد.</string>
|
||||
<string name="preference_theme">حالت شبانه</string>
|
||||
<string name="preference_theme_summary">استفاده از حالت تیره</string>
|
||||
<string name="license_name_cc_by_sa_four">CC Attribution-ShareAlike 4.0</string>
|
||||
|
|
|
|||
|
|
@ -2,10 +2,11 @@
|
|||
<!-- Authors:
|
||||
* Nike
|
||||
* Olli
|
||||
* Pyscowicz
|
||||
* Silvonen
|
||||
-->
|
||||
<resources>
|
||||
<string name="crash_dialog_title">Commons app on kaatunut</string>
|
||||
<string name="crash_dialog_title">Commons on kaatunut</string>
|
||||
<string name="crash_dialog_text">Pahoittelemme, virhe tapahtui.</string>
|
||||
<string name="crash_dialog_comment_prompt">Kerro meille mitä teit äsken, sähköpostitse. Se auttaa meitä korjaamaan ongelman!</string>
|
||||
<string name="crash_dialog_ok_toast">Kiitos!</string>
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@
|
|||
<string name="login_success">Kirjautuminen onnistui!</string>
|
||||
<string name="login_failed">Kirjautuminen epäonnistui!</string>
|
||||
<string name="upload_failed">Tiedostoa ei löytynyt. Yritä toista tiedostoa.</string>
|
||||
<string name="authentication_failed">Tunnistautuminen epäonnistui!</string>
|
||||
<string name="authentication_failed" fuzzy="true">Tunnistautuminen epäonnistui!</string>
|
||||
<string name="uploading_started">Tallentaminen aloitettiin!</string>
|
||||
<string name="upload_completed_notification_title">%1$s tallennettiin!</string>
|
||||
<string name="upload_completed_notification_text">Napauta katsoaksesi tallennusta</string>
|
||||
|
|
@ -58,8 +58,6 @@
|
|||
<string name="add_title_toast">Anna tälle tiedostolle otsikko</string>
|
||||
<string name="share_description_hint">Kuvaus</string>
|
||||
<string name="login_failed_network">Kirjautuminen epäonnistui - verkkovirhe</string>
|
||||
<string name="login_failed_username">Kirjautuminen epäonnistui - tarkista käyttäjätunnus</string>
|
||||
<string name="login_failed_password">Kirjautuminen epäonnistui - tarkista salasanasi</string>
|
||||
<string name="login_failed_throttled">Liikaa epäonnistuneita yrityksiä. Yritä uudelleen parin minuutin kuluttua.</string>
|
||||
<string name="login_failed_blocked">Pahoittelut, tämä käyttäjä on estetty Commonsissa</string>
|
||||
<string name="login_failed_2fa_needed">Anna kaksivaiheisen tunnistuksen koodi.</string>
|
||||
|
|
@ -112,7 +110,7 @@
|
|||
<string name="preference_license">Oletuslisenssi</string>
|
||||
<string name="use_previous">Käytä edellistä otsikkoa/kuvausta</string>
|
||||
<string name="allow_gps">Hae tämänhetkinen sijainti automaattisesti</string>
|
||||
<string name="allow_gps_summary">Nouda nykyinen sijainti asettaaksesi käyttöön luokkaehdotuksia jos kuva ei ole paikkamerkitty</string>
|
||||
<string name="allow_gps_summary" fuzzy="true">Nouda nykyinen sijainti asettaaksesi käyttöön luokkaehdotuksia jos kuva ei ole paikkamerkitty</string>
|
||||
<string name="preference_theme">Yötila</string>
|
||||
<string name="preference_theme_summary">Käytä tummaa teemaa</string>
|
||||
<string name="license_name_cc_by_sa_four">Nimeä-JaaSamoin 4.0</string>
|
||||
|
|
@ -162,8 +160,8 @@
|
|||
<string name="detail_description_empty">Ei kuvausta</string>
|
||||
<string name="detail_license_empty">Tuntematon lisenssi</string>
|
||||
<string name="menu_refresh">Päivitä</string>
|
||||
<string name="read_storage_permission_rationale">Vaadittu oikeus: Ulkoisen tallennustilan luku. Appi ei voi päästä galleriaasi ilman tätä oikeutta.</string>
|
||||
<string name="write_storage_permission_rationale">Vaadittava lupa: Kirjoita ulkoiseen tallennustilaan. Appi ei voi päästä kameraasi ilman tätä oikeutta.</string>
|
||||
<string name="read_storage_permission_rationale">Vaadittu oikeus: Ulkoisen tallennustilan luku. Sovellus ei voi päästä galleriaasi ilman tätä oikeutta.</string>
|
||||
<string name="write_storage_permission_rationale">Vaadittu oikeus: Kirjoita ulkoiseen tallennustilaan. Sovellus ei voi päästä kameraasi ilman tätä oikeutta.</string>
|
||||
<string name="location_permission_rationale">Valinnainen lupa: Saada tämänhetkinen sijainti loukkasuosituksia varten.</string>
|
||||
<string name="ok">OK</string>
|
||||
<string name="title_activity_nearby">Lähellä olevat paikat</string>
|
||||
|
|
@ -262,4 +260,8 @@
|
|||
<string name="about_translate_proceed">Jatka</string>
|
||||
<string name="about_translate_cancel">Peruuta</string>
|
||||
<string name="retry">Yritä uudelleen</string>
|
||||
<string name="showcase_view_got_it_button">Selvä!</string>
|
||||
<string name="no_images_found">Kuvia ei löytynyt!</string>
|
||||
<string name="image_uploaded_by">Tallentanut: %1$s</string>
|
||||
<string name="share_app_title">Jaa sovellus</string>
|
||||
</resources>
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@
|
|||
<string name="logging_in_message" fuzzy="true">Vinarliga bíða…</string>
|
||||
<string name="login_success">Innritan væleydnað!</string>
|
||||
<string name="login_failed">Innritan miseydnaðist</string>
|
||||
<string name="authentication_failed">Góðkenning miseydnaðist!</string>
|
||||
<string name="authentication_failed" fuzzy="true">Góðkenning miseydnaðist!</string>
|
||||
<string name="uploading_started">Upplóting er byrjað!</string>
|
||||
<string name="upload_completed_notification_title">%1$s er lagt út!</string>
|
||||
<string name="upload_completed_notification_text">Trýst fyri at síggja tað sum tú legði út</string>
|
||||
|
|
@ -34,8 +34,6 @@
|
|||
<string name="share_title_hint">Heiti</string>
|
||||
<string name="share_description_hint">Frágreiðing</string>
|
||||
<string name="login_failed_network">Ómøguligt at rita inn - feilur í netsambandinum</string>
|
||||
<string name="login_failed_username">Ómøguligt at rita inn - vinarliga eftirkanna títt brúkaranavn</string>
|
||||
<string name="login_failed_password">Ómøguligt at rita inn - vinarliga kanna eftir, um títt loyniorð er rætt</string>
|
||||
<string name="login_failed_throttled" fuzzy="true">Ov nógv miseydnaðar royndir. Vinarliga royn aftur um fáir minuttir</string>
|
||||
<string name="login_failed_blocked">Haldið okkum tilgóðar, hesin brúkari er blivin sperraður á Commons</string>
|
||||
<string name="login_failed_generic">Login miseydnaðist</string>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Authors:
|
||||
* Fitoschido
|
||||
* Friday83260
|
||||
* Gomoko
|
||||
* Happy13241
|
||||
* Jean-Frédéric
|
||||
|
|
@ -37,7 +38,7 @@
|
|||
<string name="login_success">Connexion réussie !</string>
|
||||
<string name="login_failed">Échec de la connexion !</string>
|
||||
<string name="upload_failed">Fichier non trouvé. Veuillez en essayer un autre.</string>
|
||||
<string name="authentication_failed">Échec de l’authentification !</string>
|
||||
<string name="authentication_failed">Échec de l\'authentification, veuillez vous reconnecter s\'il vous plait</string>
|
||||
<string name="uploading_started">Téléversement démarré !</string>
|
||||
<string name="upload_completed_notification_title">%1$s téléversés !</string>
|
||||
<string name="upload_completed_notification_text">Appuyer pour voir votre téléversement</string>
|
||||
|
|
@ -65,8 +66,7 @@
|
|||
<string name="add_title_toast">Veuillez donner un titre à ce fichier</string>
|
||||
<string name="share_description_hint">Description</string>
|
||||
<string name="login_failed_network">Impossible de se connecter — panne de réseau</string>
|
||||
<string name="login_failed_username">Impossible de se connecter — veuillez vérifier votre nom d’utilisateur</string>
|
||||
<string name="login_failed_password">Impossible de se connecter — veuillez vérifier votre mot de passe</string>
|
||||
<string name="login_failed_wrong_credentials">Impossible de se connecter — veuillez vérifier votre nom d’utilisateur et votre mot de passe</string>
|
||||
<string name="login_failed_throttled">Trop de tentatives infructueuses. Veuillez réessayer dans quelques minutes.</string>
|
||||
<string name="login_failed_blocked">Désolé, cet utilisateur a été bloqué dans Commons</string>
|
||||
<string name="login_failed_2fa_needed">Vous devez fournir votre code d’authentification à deux facteurs.</string>
|
||||
|
|
@ -120,7 +120,7 @@
|
|||
<string name="preference_license">Licence par défaut</string>
|
||||
<string name="use_previous">Utiliser le titre ou la description précédent</string>
|
||||
<string name="allow_gps">Obtenir automatiquement l’emplacement actuel</string>
|
||||
<string name="allow_gps_summary">Récupérer l’emplacement actuel pour proposer des suggestions de catégorie si l’image n’est pas marquée géographiquement</string>
|
||||
<string name="allow_gps_summary">Récupère l’emplacement actuel si l’image n’est pas marquée géographiquement, et marque géographiquement l’image avec. Avertissement : ceci dévoilera votre emplacement actuel.</string>
|
||||
<string name="preference_theme">Mode de nuit</string>
|
||||
<string name="preference_theme_summary">Utiliser un thème sombre</string>
|
||||
<string name="license_name_cc_by_sa_four"> Attribution-ShareAlike 4.0</string>
|
||||
|
|
@ -289,4 +289,11 @@
|
|||
<string name="image_uploaded_by">Importé par:%1$s</string>
|
||||
<string name="share_app_title">Partager les applications</string>
|
||||
<string name="share_coordinates_not_present">Les coordonnées n\'ont pas été spécifiées pendant la sélection de l\'image</string>
|
||||
<string name="error_fetching_nearby_places">Erreur durant l\'exploration du voisinage.</string>
|
||||
<string name="appwidget_img">Image du jour</string>
|
||||
<string name="app_widget_heading">Image du jour</string>
|
||||
<string name="successful_wikidata_edit">Image bien ajoutée à %1$s sur Wikidata !</string>
|
||||
<string name="wikidata_edit_failure">Échec de la mise à jour de l\'entité Wikidata correspondante !</string>
|
||||
<string name="menu_set_wallpaper">Définir le papier-peint</string>
|
||||
<string name="wallpaper_set_successfully">Papier-peint configuré avec succès!</string>
|
||||
</resources>
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@
|
|||
<string name="login_success">Uunmeldin hää loket!</string>
|
||||
<string name="login_failed">Bi\'t uunmeldin as wat skiaf gingen.</string>
|
||||
<string name="upload_failed">Datei ei fünjen. Ferschük det mä en ööder datei.</string>
|
||||
<string name="authentication_failed">Dü küdst ei gudkäänd wurd!</string>
|
||||
<string name="authentication_failed" fuzzy="true">Dü küdst ei gudkäänd wurd!</string>
|
||||
<string name="uploading_started">Huuchschüüren hää begand!</string>
|
||||
<string name="upload_completed_notification_title">%1$s huuchschüürd!</string>
|
||||
<string name="upload_completed_notification_text">Tipe, am din huuchschüürd bil uuntulukin</string>
|
||||
|
|
@ -42,8 +42,6 @@
|
|||
<string name="share_title_hint">Tiitel</string>
|
||||
<string name="share_description_hint">Beskriiwang</string>
|
||||
<string name="login_failed_network">Bi\'t uunmeldin as wat skiaf gingen - näätwerk-feeler</string>
|
||||
<string name="login_failed_username">Bi\'t uunmeldin as wat skiaf gingen - luke ans efter di brükernööm</string>
|
||||
<string name="login_failed_password">Bi\'t uunmeldin as wat skiaf gingen - luke ans efter det paaswurd</string>
|
||||
<string name="login_failed_throttled">Tu fölsis fersoocht saner lok. Ferschük det uun hög minüüten noch ans nei.</string>
|
||||
<string name="login_failed_blocked">Didiar brüker as üüb Commons speret wurden.</string>
|
||||
<string name="login_failed_2fa_needed">Dü skel dan code för\'t tau-straal-gudkäänen (2FA) uundu.</string>
|
||||
|
|
@ -94,7 +92,7 @@
|
|||
<string name="preference_license" fuzzy="true">Lisens</string>
|
||||
<string name="use_previous">Ual tiitel/beskriiwang brük</string>
|
||||
<string name="allow_gps">Aktuel plak automaatisk ufrep</string>
|
||||
<string name="allow_gps_summary">Rept di aktuel plak uf, am kategoriin föörtuslauen, wan det bil nian geotags hää.</string>
|
||||
<string name="allow_gps_summary" fuzzy="true">Rept di aktuel plak uf, am kategoriin föörtuslauen, wan det bil nian geotags hää.</string>
|
||||
<string name="preference_theme">Naacht muude</string>
|
||||
<string name="preference_theme_summary">Jonk skak brük</string>
|
||||
<string name="license_name_cc_by_sa_four">Attribution-ShareAlike 4.0</string>
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@
|
|||
<string name="login_success">Accedeu correctamente!</string>
|
||||
<string name="login_failed">Erro durante o inición de sesión!</string>
|
||||
<string name="upload_failed">Ficheiro non atopado. Por favor, probe con outro.</string>
|
||||
<string name="authentication_failed">Erro durante a autenticación!</string>
|
||||
<string name="authentication_failed" fuzzy="true">Erro durante a autenticación!</string>
|
||||
<string name="uploading_started">A carga comezou!</string>
|
||||
<string name="upload_completed_notification_title">Cargouse \"%1$s\"!</string>
|
||||
<string name="upload_completed_notification_text">Prema para ollar a súa carga</string>
|
||||
|
|
@ -55,8 +55,7 @@
|
|||
<string name="add_title_toast">Por favor, proporcione un título para este ficheiro</string>
|
||||
<string name="share_description_hint">Descrición</string>
|
||||
<string name="login_failed_network">Erro ao acceder ao sistema: Fallou a rede</string>
|
||||
<string name="login_failed_username">Erro ao acceder ao sistema: Comprobe o seu nome de usuario</string>
|
||||
<string name="login_failed_password">Erro ao acceder ao sistema: Comprobe o seu contrasinal</string>
|
||||
<string name="login_failed_wrong_credentials">Non se pode acceder. Revise o nome de usuario e o contrasinal</string>
|
||||
<string name="login_failed_throttled">Demasiados intentos incorrectos. Inténteo de novo nuns minutos.</string>
|
||||
<string name="login_failed_blocked">Sentímolo, este usuario está bloqueado en Commons</string>
|
||||
<string name="login_failed_2fa_needed">Debe proporcionar o seu código de autenticación de dous factores.</string>
|
||||
|
|
@ -110,7 +109,7 @@
|
|||
<string name="preference_license">Licenza por defecto</string>
|
||||
<string name="use_previous">Usar o título ou a descrición anterior</string>
|
||||
<string name="allow_gps">Obter automaticamente a localización actual</string>
|
||||
<string name="allow_gps_summary">Obter a localización actual para ofrecer suxestións de categoría se a imaxe non está xeolocalizada</string>
|
||||
<string name="allow_gps_summary" fuzzy="true">Obter a localización actual para ofrecer suxestións de categoría se a imaxe non está xeolocalizada</string>
|
||||
<string name="preference_theme">Modo nocturno</string>
|
||||
<string name="preference_theme_summary">Usar tema escuro</string>
|
||||
<string name="license_name_cc_by_sa_four"> Recoñecemento-CompartirIgual 4.0</string>
|
||||
|
|
@ -269,4 +268,16 @@
|
|||
<string name="about_translate_proceed">Proceder</string>
|
||||
<string name="about_translate_cancel">Cancelar</string>
|
||||
<string name="retry">Reintentar</string>
|
||||
<string name="showcase_view_got_it_button">Entendido!</string>
|
||||
<string name="showcase_view_whole_nearby_activity">Hai sitios preto de vostede que precisan fotos para ilustrar os seus artigos de Wikipedia</string>
|
||||
<string name="showcase_view_list_icon">Premendo neste botón aparecerá unha lista destes lugares</string>
|
||||
<string name="showcase_view_plus_fab">Pode cargar unha imaxe de calquera lugar dende a galería ou a cámara</string>
|
||||
<string name="no_images_found">Non se atopou ningunha imaxeǃ</string>
|
||||
<string name="error_loading_images">Houbo un erro ó subir as imaxes.</string>
|
||||
<string name="image_uploaded_by">Subida porː %1$s</string>
|
||||
<string name="share_app_title">Compartir a aplicación</string>
|
||||
<string name="share_coordinates_not_present">Non se indicaron as coordenadas ó seleccionar a imaxe</string>
|
||||
<string name="error_fetching_nearby_places">Erro ó procurar os lugares próximos.</string>
|
||||
<string name="menu_set_wallpaper">Definir imaxe de fondo</string>
|
||||
<string name="wallpaper_set_successfully">A imaxe de fondo configurouse correctamenteǃ</string>
|
||||
</resources>
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue