diff --git a/app/build.gradle b/app/build.gradle
index 184e756ac..eab310df4 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -23,11 +23,11 @@ dependencies {
}
- implementation "com.android.support:support-v4:${project.supportLibVersion}"
- implementation "com.android.support:appcompat-v7:${project.supportLibVersion}"
- implementation "com.android.support:design:${project.supportLibVersion}"
+ 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:cardview-v7:${project.supportLibVersion}"
+ implementation "com.android.support:cardview-v7:$SUPPORT_LIB_VERSION"
implementation "com.jakewharton:butterknife:$BUTTERKNIFE_VERSION"
kapt "com.jakewharton:butterknife-compiler:$BUTTERKNIFE_VERSION"
@@ -44,7 +44,7 @@ dependencies {
implementation 'com.jakewharton.rxbinding2:rxbinding-appcompat-v7:2.0.0'
implementation 'com.jakewharton.rxbinding2:rxbinding-design:2.0.0'
- implementation 'com.facebook.fresco:fresco:1.3.0'
+ implementation 'com.facebook.fresco:fresco:1.5.0'
implementation 'com.facebook.stetho:stetho:1.5.0'
implementation "com.google.dagger:dagger:$DAGGER_VERSION"
@@ -62,17 +62,17 @@ dependencies {
testImplementation 'com.squareup.okhttp3:mockwebserver:3.8.1'
androidTestImplementation 'com.squareup.okhttp3:mockwebserver:3.8.1'
- androidTestImplementation "com.android.support:support-annotations:${project.supportLibVersion}"
+ androidTestImplementation "com.android.support:support-annotations:$SUPPORT_LIB_VERSION"
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.1'
- debugImplementation 'com.squareup.leakcanary:leakcanary-android:1.5.1'
- releaseImplementation 'com.squareup.leakcanary:leakcanary-android-no-op:1.5.1'
- testImplementation 'com.squareup.leakcanary:leakcanary-android-no-op:1.5.1'
+ debugImplementation "com.squareup.leakcanary:leakcanary-android:$LEAK_CANARY"
+ releaseImplementation "com.squareup.leakcanary:leakcanary-android-no-op:$LEAK_CANARY"
+ testImplementation "com.squareup.leakcanary:leakcanary-android-no-op:$LEAK_CANARY"
- implementation 'com.google.dagger:dagger:2.11'
- implementation 'com.google.dagger:dagger-android-support:2.11'
- annotationProcessor 'com.google.dagger:dagger-compiler:2.11'
- annotationProcessor 'com.google.dagger:dagger-android-processor:2.11'
+ implementation "com.google.dagger:dagger:$DAGGER_VERSION"
+ implementation "com.google.dagger:dagger-android-support:$DAGGER_VERSION"
+ kapt "com.google.dagger:dagger-compiler:$DAGGER_VERSION"
+ kapt "com.google.dagger:dagger-android-processor:$DAGGER_VERSION"
}
android {
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index e262e9088..253bdaea8 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -87,6 +87,10 @@
android:label="@string/title_activity_nearby"
android:parentActivityName=".contributions.ContributionsActivity" />
+
+
categoriesAdapter;
private OnCategoriesSaveHandler onCategoriesSaveHandler;
private HashMap> categoriesCache;
private List selectedCategories = new ArrayList<>();
- private ContentProviderClient databaseClient;
private final CategoriesAdapterFactory adapterFactory = new CategoriesAdapterFactory(item -> {
if (item.isSelected()) {
selectedCategories.add(item);
- updateCategoryCount(item, databaseClient);
+ updateCategoryCount(item);
} else {
selectedCategories.remove(item);
}
@@ -140,7 +137,6 @@ public class CategorizationFragment extends DaggerFragment {
@Override
public void onDestroy() {
super.onDestroy();
- databaseClient.release();
}
@Override
@@ -178,7 +174,6 @@ public class CategorizationFragment extends DaggerFragment {
setHasOptionsMenu(true);
onCategoriesSaveHandler = (OnCategoriesSaveHandler) getActivity();
getActivity().setTitle(R.string.categories_activity_title);
- databaseClient = getActivity().getContentResolver().acquireContentProviderClient(AUTHORITY);
}
private void updateCategoryList(String filter) {
@@ -261,7 +256,7 @@ public class CategorizationFragment extends DaggerFragment {
}
private Observable recentCategories() {
- return Observable.fromIterable(Category.recentCategories(databaseClient, SEARCH_CATS_LIMIT))
+ return Observable.fromIterable(categoryDao.recentCategories(SEARCH_CATS_LIMIT))
.map(s -> new CategoryItem(s, false));
}
@@ -311,24 +306,16 @@ public class CategorizationFragment extends DaggerFragment {
|| item.matches("(.*)needing(.*)") || item.matches("(.*)taken on(.*)"));
}
- private void updateCategoryCount(CategoryItem item, ContentProviderClient client) {
- Category cat = lookupCategory(item.getName());
- cat.incTimesUsed();
- cat.save(client);
- }
+ private void updateCategoryCount(CategoryItem item) {
+ Category category = categoryDao.find(item.getName());
- private Category lookupCategory(String name) {
- Category cat = Category.find(databaseClient, name);
-
- if (cat == null) {
- // Newly used category...
- cat = new Category();
- cat.setName(name);
- cat.setLastUsed(new Date());
- cat.setTimesUsed(0);
+ // Newly used category...
+ if (category == null) {
+ category = new Category(null, item.getName(), new Date(), 0);
}
- return cat;
+ category.incTimesUsed();
+ categoryDao.save(category);
}
public int getCurrentSelectedCount() {
diff --git a/app/src/main/java/fr/free/nrw/commons/category/Category.java b/app/src/main/java/fr/free/nrw/commons/category/Category.java
new file mode 100644
index 000000000..f2d83d2e5
--- /dev/null
+++ b/app/src/main/java/fr/free/nrw/commons/category/Category.java
@@ -0,0 +1,96 @@
+package fr.free.nrw.commons.category;
+
+import android.net.Uri;
+
+import java.util.Date;
+
+/**
+ * Represents a category
+ */
+public class Category {
+ private Uri contentUri;
+ private String name;
+ private Date lastUsed;
+ private int timesUsed;
+
+ public Category() {
+ }
+
+ public Category(Uri contentUri, String name, Date lastUsed, int timesUsed) {
+ this.contentUri = contentUri;
+ this.name = name;
+ this.lastUsed = lastUsed;
+ this.timesUsed = timesUsed;
+ }
+
+ /**
+ * Gets name
+ *
+ * @return name
+ */
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * Modifies name
+ *
+ * @param name Category name
+ */
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ /**
+ * Gets last used date
+ *
+ * @return Last used date
+ */
+ public Date getLastUsed() {
+ // warning: Date objects are mutable.
+ return (Date)lastUsed.clone();
+ }
+
+ /**
+ * Generates new last used date
+ */
+ private void touch() {
+ lastUsed = new Date();
+ }
+
+ /**
+ * Gets no. of times the category is used
+ *
+ * @return no. of times used
+ */
+ public int getTimesUsed() {
+ return timesUsed;
+ }
+
+ /**
+ * Increments timesUsed by 1 and sets last used date as now.
+ */
+ public void incTimesUsed() {
+ timesUsed++;
+ touch();
+ }
+
+ /**
+ * Gets the content URI for this category
+ *
+ * @return content URI
+ */
+ public Uri getContentUri() {
+ return contentUri;
+ }
+
+ /**
+ * Modifies the content URI - marking this category as already saved in the database
+ *
+ * @param contentUri the content URI
+ */
+ public void setContentUri(Uri contentUri) {
+ this.contentUri = contentUri;
+ }
+
+}
\ No newline at end of file
diff --git a/app/src/main/java/fr/free/nrw/commons/category/CategoryContentProvider.java b/app/src/main/java/fr/free/nrw/commons/category/CategoryContentProvider.java
index ed698ec4c..dcc1bc6f2 100644
--- a/app/src/main/java/fr/free/nrw/commons/category/CategoryContentProvider.java
+++ b/app/src/main/java/fr/free/nrw/commons/category/CategoryContentProvider.java
@@ -17,9 +17,9 @@ import fr.free.nrw.commons.data.DBOpenHelper;
import timber.log.Timber;
import static android.content.UriMatcher.NO_MATCH;
-import static fr.free.nrw.commons.data.Category.Table.ALL_FIELDS;
-import static fr.free.nrw.commons.data.Category.Table.COLUMN_ID;
-import static fr.free.nrw.commons.data.Category.Table.TABLE_NAME;
+import static fr.free.nrw.commons.category.CategoryDao.Table.ALL_FIELDS;
+import static fr.free.nrw.commons.category.CategoryDao.Table.COLUMN_ID;
+import static fr.free.nrw.commons.category.CategoryDao.Table.TABLE_NAME;
public class CategoryContentProvider extends ContentProvider {
diff --git a/app/src/main/java/fr/free/nrw/commons/category/CategoryDao.java b/app/src/main/java/fr/free/nrw/commons/category/CategoryDao.java
new file mode 100644
index 000000000..e63b04c26
--- /dev/null
+++ b/app/src/main/java/fr/free/nrw/commons/category/CategoryDao.java
@@ -0,0 +1,184 @@
+package fr.free.nrw.commons.category;
+
+import android.content.ContentProviderClient;
+import android.content.ContentValues;
+import android.database.Cursor;
+import android.database.sqlite.SQLiteDatabase;
+import android.os.RemoteException;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+
+import javax.inject.Inject;
+import javax.inject.Named;
+import javax.inject.Provider;
+
+public class CategoryDao {
+
+ private final Provider clientProvider;
+
+ @Inject
+ public CategoryDao(@Named("category") Provider clientProvider) {
+ this.clientProvider = clientProvider;
+ }
+
+ public void save(Category category) {
+ ContentProviderClient db = clientProvider.get();
+ try {
+ if (category.getContentUri() == null) {
+ category.setContentUri(db.insert(CategoryContentProvider.BASE_URI, toContentValues(category)));
+ } else {
+ db.update(category.getContentUri(), toContentValues(category), null, null);
+ }
+ } catch (RemoteException e) {
+ throw new RuntimeException(e);
+ } finally {
+ db.release();
+ }
+ }
+
+ /**
+ * Find persisted category in database, based on its name.
+ *
+ * @param name Category's name
+ * @return category from database, or null if not found
+ */
+ @Nullable
+ Category find(String name) {
+ Cursor cursor = null;
+ ContentProviderClient db = clientProvider.get();
+ try {
+ cursor = db.query(
+ CategoryContentProvider.BASE_URI,
+ Table.ALL_FIELDS,
+ Table.COLUMN_NAME + "=?",
+ new String[]{name},
+ null);
+ if (cursor != null && cursor.moveToFirst()) {
+ return fromCursor(cursor);
+ }
+ } catch (RemoteException e) {
+ // This feels lazy, but to hell with checked exceptions. :)
+ throw new RuntimeException(e);
+ } finally {
+ if (cursor != null) {
+ cursor.close();
+ }
+ db.release();
+ }
+ return null;
+ }
+
+ /**
+ * Retrieve recently-used categories, ordered by descending date.
+ *
+ * @return a list containing recent categories
+ */
+ @NonNull
+ List recentCategories(int limit) {
+ List items = new ArrayList<>();
+ Cursor cursor = null;
+ ContentProviderClient db = clientProvider.get();
+ try {
+ cursor = db.query(
+ CategoryContentProvider.BASE_URI,
+ Table.ALL_FIELDS,
+ null,
+ new String[]{},
+ Table.COLUMN_LAST_USED + " DESC");
+ // fixme add a limit on the original query instead of falling out of the loop?
+ while (cursor != null && cursor.moveToNext()
+ && cursor.getPosition() < limit) {
+ items.add(fromCursor(cursor).getName());
+ }
+ } catch (RemoteException e) {
+ throw new RuntimeException(e);
+ } finally {
+ if (cursor != null) {
+ cursor.close();
+ }
+ db.release();
+ }
+ return items;
+ }
+
+ Category fromCursor(Cursor cursor) {
+ // Hardcoding column positions!
+ return new Category(
+ CategoryContentProvider.uriForId(cursor.getInt(0)),
+ cursor.getString(1),
+ new Date(cursor.getLong(2)),
+ cursor.getInt(3)
+ );
+ }
+
+ private ContentValues toContentValues(Category category) {
+ ContentValues cv = new ContentValues();
+ cv.put(CategoryDao.Table.COLUMN_NAME, category.getName());
+ cv.put(CategoryDao.Table.COLUMN_LAST_USED, category.getLastUsed().getTime());
+ cv.put(CategoryDao.Table.COLUMN_TIMES_USED, category.getTimesUsed());
+ return cv;
+ }
+
+ public static class Table {
+ public static final String TABLE_NAME = "categories";
+
+ public static final String COLUMN_ID = "_id";
+ static final String COLUMN_NAME = "name";
+ static final String COLUMN_LAST_USED = "last_used";
+ static final String COLUMN_TIMES_USED = "times_used";
+
+ // NOTE! KEEP IN SAME ORDER AS THEY ARE DEFINED UP THERE. HELPS HARD CODE COLUMN INDICES.
+ public static final String[] ALL_FIELDS = {
+ COLUMN_ID,
+ COLUMN_NAME,
+ COLUMN_LAST_USED,
+ COLUMN_TIMES_USED
+ };
+
+ static final String DROP_TABLE_STATEMENT = "DROP TABLE IF EXISTS " + TABLE_NAME;
+
+ static final String CREATE_TABLE_STATEMENT = "CREATE TABLE " + TABLE_NAME + " ("
+ + COLUMN_ID + " INTEGER PRIMARY KEY,"
+ + COLUMN_NAME + " STRING,"
+ + COLUMN_LAST_USED + " INTEGER,"
+ + COLUMN_TIMES_USED + " INTEGER"
+ + ");";
+
+ public static void onCreate(SQLiteDatabase db) {
+ db.execSQL(CREATE_TABLE_STATEMENT);
+ }
+
+ public static void onDelete(SQLiteDatabase db) {
+ db.execSQL(DROP_TABLE_STATEMENT);
+ onCreate(db);
+ }
+
+ public static void onUpdate(SQLiteDatabase db, int from, int to) {
+ if (from == to) {
+ return;
+ }
+ if (from < 4) {
+ // doesn't exist yet
+ from++;
+ onUpdate(db, from, to);
+ return;
+ }
+ if (from == 4) {
+ // table added in version 5
+ onCreate(db);
+ from++;
+ onUpdate(db, from, to);
+ return;
+ }
+ if (from == 5) {
+ from++;
+ onUpdate(db, from, to);
+ return;
+ }
+ }
+ }
+}
diff --git a/app/src/main/java/fr/free/nrw/commons/contributions/Contribution.java b/app/src/main/java/fr/free/nrw/commons/contributions/Contribution.java
index e673c7d9d..00baac847 100644
--- a/app/src/main/java/fr/free/nrw/commons/contributions/Contribution.java
+++ b/app/src/main/java/fr/free/nrw/commons/contributions/Contribution.java
@@ -1,14 +1,8 @@
package fr.free.nrw.commons.contributions;
-import android.content.ContentProviderClient;
-import android.content.ContentValues;
-import android.database.Cursor;
-import android.database.sqlite.SQLiteDatabase;
import android.net.Uri;
import android.os.Parcel;
-import android.os.RemoteException;
import android.support.annotation.NonNull;
-import android.text.TextUtils;
import java.text.SimpleDateFormat;
import java.util.Date;
@@ -43,7 +37,6 @@ public class Contribution extends Media {
public static final String SOURCE_GALLERY = "gallery";
public static final String SOURCE_EXTERNAL = "external";
- private ContentProviderClient client;
private Uri contentUri;
private String source;
private String editSummary;
@@ -51,24 +44,42 @@ public class Contribution extends Media {
private int state;
private long transferred;
private String decimalCoords;
-
private boolean isMultiple;
- public boolean getMultiple() {
- return isMultiple;
+ public Contribution(Uri contentUri, String filename, Uri localUri, String imageUrl, Date timestamp,
+ int state, long dataLength, Date dateUploaded, long transferred,
+ String source, String description, String creator, boolean isMultiple,
+ int width, int height, String license) {
+ super(localUri, imageUrl, filename, description, dataLength, timestamp, dateUploaded, creator);
+ this.contentUri = contentUri;
+ this.state = state;
+ this.timestamp = timestamp;
+ this.transferred = transferred;
+ this.source = source;
+ this.isMultiple = isMultiple;
+ this.width = width;
+ this.height = height;
+ this.license = license;
}
- public void setMultiple(boolean multiple) {
- isMultiple = multiple;
- }
-
- public Contribution(Uri localUri, String remoteUri, String filename, String description, long dataLength, Date dateCreated, Date dateUploaded, String creator, String editSummary, String decimalCoords) {
- super(localUri, remoteUri, filename, description, dataLength, dateCreated, dateUploaded, creator);
+ public Contribution(Uri localUri, String imageUrl, String filename, String description, long dataLength,
+ Date dateCreated, Date dateUploaded, String creator, String editSummary, String decimalCoords) {
+ super(localUri, imageUrl, filename, description, dataLength, dateCreated, dateUploaded, creator);
this.decimalCoords = decimalCoords;
this.editSummary = editSummary;
timestamp = new Date(System.currentTimeMillis());
}
+ public Contribution(Parcel in) {
+ super(in);
+ contentUri = in.readParcelable(Uri.class.getClassLoader());
+ source = in.readString();
+ timestamp = (Date) in.readSerializable();
+ state = in.readInt();
+ transferred = in.readLong();
+ isMultiple = in.readInt() == 1;
+ }
+
@Override
public void writeToParcel(Parcel parcel, int flags) {
super.writeToParcel(parcel, flags);
@@ -80,14 +91,12 @@ public class Contribution extends Media {
parcel.writeInt(isMultiple ? 1 : 0);
}
- public Contribution(Parcel in) {
- super(in);
- contentUri = in.readParcelable(Uri.class.getClassLoader());
- source = in.readString();
- timestamp = (Date) in.readSerializable();
- state = in.readInt();
- transferred = in.readLong();
- isMultiple = in.readInt() == 1;
+ public boolean getMultiple() {
+ return isMultiple;
+ }
+
+ public void setMultiple(boolean multiple) {
+ isMultiple = multiple;
}
public long getTransferred() {
@@ -106,10 +115,18 @@ public class Contribution extends Media {
return contentUri;
}
+ public void setContentUri(Uri contentUri) {
+ this.contentUri = contentUri;
+ }
+
public Date getTimestamp() {
return timestamp;
}
+ public void setTimestamp(Date timestamp) {
+ this.timestamp = timestamp;
+ }
+
public int getState() {
return state;
}
@@ -155,62 +172,6 @@ public class Contribution extends Media {
return buffer.toString();
}
- public void setContentProviderClient(ContentProviderClient client) {
- this.client = client;
- }
-
- public void save() {
- try {
- if (contentUri == null) {
- contentUri = client.insert(ContributionsContentProvider.BASE_URI, this.toContentValues());
- } else {
- client.update(contentUri, toContentValues(), null, null);
- }
- } catch (RemoteException e) {
- throw new RuntimeException(e);
- }
- }
-
- public void delete() {
- try {
- if (contentUri == null) {
- // noooo
- throw new RuntimeException("tried to delete item with no content URI");
- } else {
- client.delete(contentUri, null, null);
- }
- } catch (RemoteException e) {
- throw new RuntimeException(e);
- }
- }
-
-
- public ContentValues toContentValues() {
- ContentValues cv = new ContentValues();
- cv.put(Table.COLUMN_FILENAME, getFilename());
- if (getLocalUri() != null) {
- cv.put(Table.COLUMN_LOCAL_URI, getLocalUri().toString());
- }
- if (getImageUrl() != null) {
- cv.put(Table.COLUMN_IMAGE_URL, getImageUrl());
- }
- if (getDateUploaded() != null) {
- cv.put(Table.COLUMN_UPLOADED, getDateUploaded().getTime());
- }
- cv.put(Table.COLUMN_LENGTH, getDataLength());
- cv.put(Table.COLUMN_TIMESTAMP, getTimestamp().getTime());
- cv.put(Table.COLUMN_STATE, getState());
- cv.put(Table.COLUMN_TRANSFERRED, transferred);
- cv.put(Table.COLUMN_SOURCE, source);
- cv.put(Table.COLUMN_DESCRIPTION, description);
- cv.put(Table.COLUMN_CREATOR, creator);
- cv.put(Table.COLUMN_MULTIPLE, isMultiple ? 1 : 0);
- cv.put(Table.COLUMN_WIDTH, width);
- cv.put(Table.COLUMN_HEIGHT, height);
- cv.put(Table.COLUMN_LICENSE, license);
- return cv;
- }
-
@Override
public void setFilename(String filename) {
this.filename = filename;
@@ -224,33 +185,6 @@ public class Contribution extends Media {
timestamp = new Date(System.currentTimeMillis());
}
- public static Contribution fromCursor(Cursor cursor) {
- // Hardcoding column positions!
- Contribution c = new Contribution();
-
- //Check that cursor has a value to avoid CursorIndexOutOfBoundsException
- if (cursor.getCount() > 0) {
- c.contentUri = ContributionsContentProvider.uriForId(cursor.getInt(0));
- c.filename = cursor.getString(1);
- c.localUri = TextUtils.isEmpty(cursor.getString(2)) ? null : Uri.parse(cursor.getString(2));
- c.imageUrl = cursor.getString(3);
- c.timestamp = cursor.getLong(4) == 0 ? null : new Date(cursor.getLong(4));
- c.state = cursor.getInt(5);
- c.dataLength = cursor.getLong(6);
- c.dateUploaded = cursor.getLong(7) == 0 ? null : new Date(cursor.getLong(7));
- c.transferred = cursor.getLong(8);
- c.source = cursor.getString(9);
- c.description = cursor.getString(10);
- c.creator = cursor.getString(11);
- c.isMultiple = cursor.getInt(12) == 1;
- c.width = cursor.getInt(13);
- c.height = cursor.getInt(14);
- c.license = cursor.getString(15);
- }
-
- return c;
- }
-
public String getSource() {
return source;
}
@@ -263,121 +197,6 @@ public class Contribution extends Media {
this.localUri = localUri;
}
- public static class Table {
- public static final String TABLE_NAME = "contributions";
-
- public static final String COLUMN_ID = "_id";
- public static final String COLUMN_FILENAME = "filename";
- public static final String COLUMN_LOCAL_URI = "local_uri";
- public static final String COLUMN_IMAGE_URL = "image_url";
- public static final String COLUMN_TIMESTAMP = "timestamp";
- public static final String COLUMN_STATE = "state";
- public static final String COLUMN_LENGTH = "length";
- public static final String COLUMN_UPLOADED = "uploaded";
- public static final String COLUMN_TRANSFERRED = "transferred"; // Currently transferred number of bytes
- public static final String COLUMN_SOURCE = "source";
- public static final String COLUMN_DESCRIPTION = "description";
- public static final String COLUMN_CREATOR = "creator"; // Initial uploader
- public static final String COLUMN_MULTIPLE = "multiple";
- public static final String COLUMN_WIDTH = "width";
- public static final String COLUMN_HEIGHT = "height";
- public static final String COLUMN_LICENSE = "license";
-
- // NOTE! KEEP IN SAME ORDER AS THEY ARE DEFINED UP THERE. HELPS HARD CODE COLUMN INDICES.
- public static final String[] ALL_FIELDS = {
- COLUMN_ID,
- COLUMN_FILENAME,
- COLUMN_LOCAL_URI,
- COLUMN_IMAGE_URL,
- COLUMN_TIMESTAMP,
- COLUMN_STATE,
- COLUMN_LENGTH,
- COLUMN_UPLOADED,
- COLUMN_TRANSFERRED,
- COLUMN_SOURCE,
- COLUMN_DESCRIPTION,
- COLUMN_CREATOR,
- COLUMN_MULTIPLE,
- COLUMN_WIDTH,
- COLUMN_HEIGHT,
- COLUMN_LICENSE
- };
-
-
- private static final String CREATE_TABLE_STATEMENT = "CREATE TABLE " + TABLE_NAME + " ("
- + "_id INTEGER PRIMARY KEY,"
- + "filename STRING,"
- + "local_uri STRING,"
- + "image_url STRING,"
- + "uploaded INTEGER,"
- + "timestamp INTEGER,"
- + "state INTEGER,"
- + "length INTEGER,"
- + "transferred INTEGER,"
- + "source STRING,"
- + "description STRING,"
- + "creator STRING,"
- + "multiple INTEGER,"
- + "width INTEGER,"
- + "height INTEGER,"
- + "LICENSE STRING"
- + ");";
-
-
- public static void onCreate(SQLiteDatabase db) {
- db.execSQL(CREATE_TABLE_STATEMENT);
- }
-
- public static void onDelete(SQLiteDatabase db) {
- db.execSQL("DROP TABLE IF EXISTS " + TABLE_NAME);
- onCreate(db);
- }
-
- public static void onUpdate(SQLiteDatabase db, int from, int to) {
- if (from == to) {
- return;
- }
- if (from == 1) {
- db.execSQL("ALTER TABLE " + TABLE_NAME + " ADD COLUMN description STRING;");
- db.execSQL("ALTER TABLE " + TABLE_NAME + " ADD COLUMN creator STRING;");
- from++;
- onUpdate(db, from, to);
- return;
- }
- if (from == 2) {
- db.execSQL("ALTER TABLE " + TABLE_NAME + " ADD COLUMN multiple INTEGER;");
- db.execSQL("UPDATE " + TABLE_NAME + " SET multiple = 0");
- from++;
- onUpdate(db, from, to);
- return;
- }
- if (from == 3) {
- // Do nothing
- from++;
- onUpdate(db, from, to);
- return;
- }
- if (from == 4) {
- // Do nothing -- added Category
- from++;
- onUpdate(db, from, to);
- return;
- }
- if (from == 5) {
- // Added width and height fields
- db.execSQL("ALTER TABLE " + TABLE_NAME + " ADD COLUMN width INTEGER;");
- db.execSQL("UPDATE " + TABLE_NAME + " SET width = 0");
- db.execSQL("ALTER TABLE " + TABLE_NAME + " ADD COLUMN height INTEGER;");
- db.execSQL("UPDATE " + TABLE_NAME + " SET height = 0");
- db.execSQL("ALTER TABLE " + TABLE_NAME + " ADD COLUMN license STRING;");
- db.execSQL("UPDATE " + TABLE_NAME + " SET license='" + Prefs.Licenses.CC_BY_SA_3 + "';");
- from++;
- onUpdate(db, from, to);
- return;
- }
- }
- }
-
@NonNull
private String licenseTemplateFor(String license) {
switch (license) {
diff --git a/app/src/main/java/fr/free/nrw/commons/contributions/ContributionDao.java b/app/src/main/java/fr/free/nrw/commons/contributions/ContributionDao.java
new file mode 100644
index 000000000..9d3038e03
--- /dev/null
+++ b/app/src/main/java/fr/free/nrw/commons/contributions/ContributionDao.java
@@ -0,0 +1,281 @@
+package fr.free.nrw.commons.contributions;
+
+import android.content.ContentProviderClient;
+import android.content.ContentValues;
+import android.database.Cursor;
+import android.database.sqlite.SQLiteDatabase;
+import android.net.Uri;
+import android.os.RemoteException;
+import android.support.annotation.Nullable;
+import android.text.TextUtils;
+
+import java.util.Date;
+
+import javax.inject.Inject;
+import javax.inject.Named;
+import javax.inject.Provider;
+
+import fr.free.nrw.commons.settings.Prefs;
+
+import static fr.free.nrw.commons.contributions.ContributionDao.Table.ALL_FIELDS;
+import static fr.free.nrw.commons.contributions.ContributionsContentProvider.BASE_URI;
+import static fr.free.nrw.commons.contributions.ContributionsContentProvider.uriForId;
+
+public class ContributionDao {
+ /*
+ This sorts in the following order:
+ Currently Uploading
+ Failed (Sorted in ascending order of time added - FIFO)
+ Queued to Upload (Sorted in ascending order of time added - FIFO)
+ Completed (Sorted in descending order of time added)
+
+ This is why Contribution.STATE_COMPLETED is -1.
+ */
+ static final String CONTRIBUTION_SORT = Table.COLUMN_STATE + " DESC, "
+ + Table.COLUMN_UPLOADED + " DESC , ("
+ + Table.COLUMN_TIMESTAMP + " * "
+ + Table.COLUMN_STATE + ")";
+
+ private final Provider clientProvider;
+
+ @Inject
+ public ContributionDao(@Named("contribution") Provider clientProvider) {
+ this.clientProvider = clientProvider;
+ }
+
+ Cursor loadAllContributions() {
+ ContentProviderClient db = clientProvider.get();
+ try {
+ return db.query(BASE_URI, ALL_FIELDS, "", null, CONTRIBUTION_SORT);
+ } catch (RemoteException e) {
+ return null;
+ } finally {
+ db.release();
+ }
+ }
+
+ public void save(Contribution contribution) {
+ ContentProviderClient db = clientProvider.get();
+ try {
+ if (contribution.getContentUri() == null) {
+ contribution.setContentUri(db.insert(BASE_URI, toContentValues(contribution)));
+ } else {
+ db.update(contribution.getContentUri(), toContentValues(contribution), null, null);
+ }
+ } catch (RemoteException e) {
+ throw new RuntimeException(e);
+ } finally {
+ db.release();
+ }
+ }
+
+ public void delete(Contribution contribution) {
+ ContentProviderClient db = clientProvider.get();
+ try {
+ if (contribution.getContentUri() == null) {
+ // noooo
+ throw new RuntimeException("tried to delete item with no content URI");
+ } else {
+ db.delete(contribution.getContentUri(), null, null);
+ }
+ } catch (RemoteException e) {
+ throw new RuntimeException(e);
+ } finally {
+ db.release();
+ }
+ }
+
+ ContentValues toContentValues(Contribution contribution) {
+ ContentValues cv = new ContentValues();
+ cv.put(Table.COLUMN_FILENAME, contribution.getFilename());
+ if (contribution.getLocalUri() != null) {
+ cv.put(Table.COLUMN_LOCAL_URI, contribution.getLocalUri().toString());
+ }
+ if (contribution.getImageUrl() != null) {
+ cv.put(Table.COLUMN_IMAGE_URL, contribution.getImageUrl());
+ }
+ if (contribution.getDateUploaded() != null) {
+ cv.put(Table.COLUMN_UPLOADED, contribution.getDateUploaded().getTime());
+ }
+ cv.put(Table.COLUMN_LENGTH, contribution.getDataLength());
+ cv.put(Table.COLUMN_TIMESTAMP, contribution.getTimestamp().getTime());
+ cv.put(Table.COLUMN_STATE, contribution.getState());
+ cv.put(Table.COLUMN_TRANSFERRED, contribution.getTransferred());
+ cv.put(Table.COLUMN_SOURCE, contribution.getSource());
+ cv.put(Table.COLUMN_DESCRIPTION, contribution.getDescription());
+ cv.put(Table.COLUMN_CREATOR, contribution.getCreator());
+ cv.put(Table.COLUMN_MULTIPLE, contribution.getMultiple() ? 1 : 0);
+ cv.put(Table.COLUMN_WIDTH, contribution.getWidth());
+ cv.put(Table.COLUMN_HEIGHT, contribution.getHeight());
+ cv.put(Table.COLUMN_LICENSE, contribution.getLicense());
+ return cv;
+ }
+
+ public Contribution fromCursor(Cursor cursor) {
+ // Hardcoding column positions!
+ //Check that cursor has a value to avoid CursorIndexOutOfBoundsException
+ if (cursor.getCount() > 0) {
+ return new Contribution(
+ uriForId(cursor.getInt(0)),
+ cursor.getString(1),
+ parseUri(cursor.getString(2)),
+ cursor.getString(3),
+ parseTimestamp(cursor.getLong(4)),
+ cursor.getInt(5),
+ cursor.getLong(6),
+ parseTimestamp(cursor.getLong(7)),
+ cursor.getLong(8),
+ cursor.getString(9),
+ cursor.getString(10),
+ cursor.getString(11),
+ cursor.getInt(12) == 1,
+ cursor.getInt(13),
+ cursor.getInt(14),
+ cursor.getString(15));
+ }
+
+ return null;
+ }
+
+ @Nullable
+ private static Date parseTimestamp(long timestamp) {
+ return timestamp == 0 ? null : new Date(timestamp);
+ }
+
+ @Nullable
+ private static Uri parseUri(String uriString) {
+ return TextUtils.isEmpty(uriString) ? null : Uri.parse(uriString);
+ }
+
+ public static class Table {
+ public static final String TABLE_NAME = "contributions";
+
+ public static final String COLUMN_ID = "_id";
+ public static final String COLUMN_FILENAME = "filename";
+ public static final String COLUMN_LOCAL_URI = "local_uri";
+ public static final String COLUMN_IMAGE_URL = "image_url";
+ public static final String COLUMN_TIMESTAMP = "timestamp";
+ public static final String COLUMN_STATE = "state";
+ public static final String COLUMN_LENGTH = "length";
+ public static final String COLUMN_UPLOADED = "uploaded";
+ public static final String COLUMN_TRANSFERRED = "transferred"; // Currently transferred number of bytes
+ public static final String COLUMN_SOURCE = "source";
+ public static final String COLUMN_DESCRIPTION = "description";
+ public static final String COLUMN_CREATOR = "creator"; // Initial uploader
+ public static final String COLUMN_MULTIPLE = "multiple";
+ public static final String COLUMN_WIDTH = "width";
+ public static final String COLUMN_HEIGHT = "height";
+ public static final String COLUMN_LICENSE = "license";
+
+ // NOTE! KEEP IN SAME ORDER AS THEY ARE DEFINED UP THERE. HELPS HARD CODE COLUMN INDICES.
+ public static final String[] ALL_FIELDS = {
+ COLUMN_ID,
+ COLUMN_FILENAME,
+ COLUMN_LOCAL_URI,
+ COLUMN_IMAGE_URL,
+ COLUMN_TIMESTAMP,
+ COLUMN_STATE,
+ COLUMN_LENGTH,
+ COLUMN_UPLOADED,
+ COLUMN_TRANSFERRED,
+ COLUMN_SOURCE,
+ COLUMN_DESCRIPTION,
+ COLUMN_CREATOR,
+ COLUMN_MULTIPLE,
+ COLUMN_WIDTH,
+ COLUMN_HEIGHT,
+ COLUMN_LICENSE
+ };
+
+ public static final String DROP_TABLE_STATEMENT = "DROP TABLE IF EXISTS " + TABLE_NAME;
+
+ public static final String CREATE_TABLE_STATEMENT = "CREATE TABLE " + TABLE_NAME + " ("
+ + "_id INTEGER PRIMARY KEY,"
+ + "filename STRING,"
+ + "local_uri STRING,"
+ + "image_url STRING,"
+ + "uploaded INTEGER,"
+ + "timestamp INTEGER,"
+ + "state INTEGER,"
+ + "length INTEGER,"
+ + "transferred INTEGER,"
+ + "source STRING,"
+ + "description STRING,"
+ + "creator STRING,"
+ + "multiple INTEGER,"
+ + "width INTEGER,"
+ + "height INTEGER,"
+ + "LICENSE STRING"
+ + ");";
+
+ // Upgrade from version 1 ->
+ static final String ADD_CREATOR_FIELD = "ALTER TABLE " + TABLE_NAME + " ADD COLUMN creator STRING;";
+ static final String ADD_DESCRIPTION_FIELD = "ALTER TABLE " + TABLE_NAME + " ADD COLUMN description STRING;";
+
+ // Upgrade from version 2 ->
+ static final String ADD_MULTIPLE_FIELD = "ALTER TABLE " + TABLE_NAME + " ADD COLUMN multiple INTEGER;";
+ static final String SET_DEFAULT_MULTIPLE = "UPDATE " + TABLE_NAME + " SET multiple = 0";
+
+ // Upgrade from version 5 ->
+ static final String ADD_WIDTH_FIELD = "ALTER TABLE " + TABLE_NAME + " ADD COLUMN width INTEGER;";
+ static final String SET_DEFAULT_WIDTH = "UPDATE " + TABLE_NAME + " SET width = 0";
+ static final String ADD_HEIGHT_FIELD = "ALTER TABLE " + TABLE_NAME + " ADD COLUMN height INTEGER;";
+ static final String SET_DEFAULT_HEIGHT = "UPDATE " + TABLE_NAME + " SET height = 0";
+ static final String ADD_LICENSE_FIELD = "ALTER TABLE " + TABLE_NAME + " ADD COLUMN license STRING;";
+ static final String SET_DEFAULT_LICENSE = "UPDATE " + TABLE_NAME + " SET license='" + Prefs.Licenses.CC_BY_SA_3 + "';";
+
+
+ public static void onCreate(SQLiteDatabase db) {
+ db.execSQL(CREATE_TABLE_STATEMENT);
+ }
+
+ public static void onDelete(SQLiteDatabase db) {
+ db.execSQL(DROP_TABLE_STATEMENT);
+ onCreate(db);
+ }
+
+ public static void onUpdate(SQLiteDatabase db, int from, int to) {
+ if (from == to) {
+ return;
+ }
+ if (from == 1) {
+ db.execSQL(ADD_DESCRIPTION_FIELD);
+ db.execSQL(ADD_CREATOR_FIELD);
+ from++;
+ onUpdate(db, from, to);
+ return;
+ }
+ if (from == 2) {
+ db.execSQL(ADD_MULTIPLE_FIELD);
+ db.execSQL(SET_DEFAULT_MULTIPLE);
+ from++;
+ onUpdate(db, from, to);
+ return;
+ }
+ if (from == 3) {
+ // Do nothing
+ from++;
+ onUpdate(db, from, to);
+ return;
+ }
+ if (from == 4) {
+ // Do nothing -- added Category
+ from++;
+ onUpdate(db, from, to);
+ return;
+ }
+ if (from == 5) {
+ // Added width and height fields
+ db.execSQL(ADD_WIDTH_FIELD);
+ db.execSQL(SET_DEFAULT_WIDTH);
+ db.execSQL(ADD_HEIGHT_FIELD);
+ db.execSQL(SET_DEFAULT_HEIGHT);
+ db.execSQL(ADD_LICENSE_FIELD);
+ db.execSQL(SET_DEFAULT_LICENSE);
+ from++;
+ onUpdate(db, from, to);
+ return;
+ }
+ }
+ }
+}
diff --git a/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsActivity.java b/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsActivity.java
index 6cda47d2e..5c1ecfaa0 100644
--- a/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsActivity.java
+++ b/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsActivity.java
@@ -42,8 +42,7 @@ import timber.log.Timber;
import static android.content.ContentResolver.requestSync;
import static fr.free.nrw.commons.contributions.Contribution.STATE_FAILED;
-import static fr.free.nrw.commons.contributions.Contribution.Table.ALL_FIELDS;
-import static fr.free.nrw.commons.contributions.ContributionsContentProvider.AUTHORITY;
+import static fr.free.nrw.commons.contributions.ContributionDao.Table.ALL_FIELDS;
import static fr.free.nrw.commons.contributions.ContributionsContentProvider.BASE_URI;
import static fr.free.nrw.commons.settings.Prefs.UPLOADS_SHOWING;
@@ -58,6 +57,7 @@ public class ContributionsActivity
@Inject MediaWikiApi mediaWikiApi;
@Inject SessionManager sessionManager;
@Inject @Named("default_preferences") SharedPreferences prefs;
+ @Inject ContributionDao contributionDao;
private Cursor allContributions;
private ContributionsListFragment contributionsList;
@@ -65,21 +65,6 @@ public class ContributionsActivity
private UploadService uploadService;
private boolean isUploadServiceConnected;
private ArrayList observersWaitingForLoad = new ArrayList<>();
- private String CONTRIBUTION_SELECTION = "";
-
- /*
- This sorts in the following order:
- Currently Uploading
- Failed (Sorted in ascending order of time added - FIFO)
- Queued to Upload (Sorted in ascending order of time added - FIFO)
- Completed (Sorted in descending order of time added)
-
- This is why Contribution.STATE_COMPLETED is -1.
- */
- private String CONTRIBUTION_SORT = Contribution.Table.COLUMN_STATE + " DESC, "
- + Contribution.Table.COLUMN_UPLOADED + " DESC , ("
- + Contribution.Table.COLUMN_TIMESTAMP + " * "
- + Contribution.Table.COLUMN_STATE + ")";
private CompositeDisposable compositeDisposable = new CompositeDisposable();
@@ -94,7 +79,7 @@ public class ContributionsActivity
@Override
public void onServiceDisconnected(ComponentName componentName) {
// this should never happen
- throw new RuntimeException("UploadService died but the rest of the process did not!");
+ Timber.e(new RuntimeException("UploadService died but the rest of the process did not!"));
}
};
@@ -121,14 +106,13 @@ public class ContributionsActivity
@Override
protected void onAuthCookieAcquired(String authCookie) {
// Do a sync everytime we get here!
- requestSync(sessionManager.getCurrentAccount(), ContributionsContentProvider.AUTHORITY, new Bundle());
+ requestSync(sessionManager.getCurrentAccount(), ContributionsContentProvider.CONTRIBUTION_AUTHORITY, new Bundle());
Intent uploadServiceIntent = new Intent(this, UploadService.class);
uploadServiceIntent.setAction(UploadService.ACTION_START_SERVICE);
startService(uploadServiceIntent);
bindService(uploadServiceIntent, uploadServiceConnection, Context.BIND_AUTO_CREATE);
- allContributions = getContentResolver().query(BASE_URI, ALL_FIELDS,
- CONTRIBUTION_SELECTION, null, CONTRIBUTION_SORT);
+ allContributions = contributionDao.loadAllContributions();
getSupportLoaderManager().initLoader(0, null, this);
}
@@ -186,24 +170,23 @@ public class ContributionsActivity
public void retryUpload(int i) {
allContributions.moveToPosition(i);
- Contribution c = Contribution.fromCursor(allContributions);
+ Contribution c = contributionDao.fromCursor(allContributions);
if (c.getState() == STATE_FAILED) {
uploadService.queue(UploadService.ACTION_UPLOAD_FILE, c);
- Timber.d("Restarting for %s", c.toContentValues());
+ Timber.d("Restarting for %s", c.toString());
} else {
- Timber.d("Skipping re-upload for non-failed %s", c.toContentValues());
+ Timber.d("Skipping re-upload for non-failed %s", c.toString());
}
}
public void deleteUpload(int i) {
allContributions.moveToPosition(i);
- Contribution c = Contribution.fromCursor(allContributions);
+ Contribution c = contributionDao.fromCursor(allContributions);
if (c.getState() == STATE_FAILED) {
- Timber.d("Deleting failed contrib %s", c.toContentValues());
- c.setContentProviderClient(getContentResolver().acquireContentProviderClient(AUTHORITY));
- c.delete();
+ Timber.d("Deleting failed contrib %s", c.toString());
+ contributionDao.delete(c);
} else {
- Timber.d("Skipping deletion for non-failed contrib %s", c.toContentValues());
+ Timber.d("Skipping deletion for non-failed contrib %s", c.toString());
}
}
@@ -239,8 +222,8 @@ public class ContributionsActivity
public Loader onCreateLoader(int i, Bundle bundle) {
int uploads = prefs.getInt(UPLOADS_SHOWING, 100);
return new CursorLoader(this, BASE_URI,
- ALL_FIELDS, CONTRIBUTION_SELECTION, null,
- CONTRIBUTION_SORT + "LIMIT " + uploads);
+ ALL_FIELDS, "", null,
+ ContributionDao.CONTRIBUTION_SORT + "LIMIT " + uploads);
}
@Override
@@ -249,7 +232,7 @@ public class ContributionsActivity
if (contributionsList.getAdapter() == null) {
contributionsList.setAdapter(new ContributionsListAdapter(getApplicationContext(),
- cursor, 0));
+ cursor, 0, contributionDao));
} else {
((CursorAdapter) contributionsList.getAdapter()).swapCursor(cursor);
}
@@ -270,7 +253,7 @@ public class ContributionsActivity
// not yet ready to return data
return null;
} else {
- return Contribution.fromCursor((Cursor) contributionsList.getAdapter().getItem(i));
+ return contributionDao.fromCursor((Cursor) contributionsList.getAdapter().getItem(i));
}
}
diff --git a/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsContentProvider.java b/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsContentProvider.java
index 5ec290026..4d82bdfb1 100644
--- a/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsContentProvider.java
+++ b/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsContentProvider.java
@@ -17,8 +17,8 @@ import fr.free.nrw.commons.data.DBOpenHelper;
import timber.log.Timber;
import static android.content.UriMatcher.NO_MATCH;
-import static fr.free.nrw.commons.contributions.Contribution.Table.ALL_FIELDS;
-import static fr.free.nrw.commons.contributions.Contribution.Table.TABLE_NAME;
+import static fr.free.nrw.commons.contributions.ContributionDao.Table.ALL_FIELDS;
+import static fr.free.nrw.commons.contributions.ContributionDao.Table.TABLE_NAME;
public class ContributionsContentProvider extends ContentProvider {
@@ -26,13 +26,13 @@ public class ContributionsContentProvider extends ContentProvider {
private static final int CONTRIBUTIONS_ID = 2;
private static final String BASE_PATH = "contributions";
private static final UriMatcher uriMatcher = new UriMatcher(NO_MATCH);
- public static final String AUTHORITY = "fr.free.nrw.commons.contributions.contentprovider";
+ public static final String CONTRIBUTION_AUTHORITY = "fr.free.nrw.commons.contributions.contentprovider";
- public static final Uri BASE_URI = Uri.parse("content://" + AUTHORITY + "/" + BASE_PATH);
+ public static final Uri BASE_URI = Uri.parse("content://" + CONTRIBUTION_AUTHORITY + "/" + BASE_PATH);
static {
- uriMatcher.addURI(AUTHORITY, BASE_PATH, CONTRIBUTIONS);
- uriMatcher.addURI(AUTHORITY, BASE_PATH + "/#", CONTRIBUTIONS_ID);
+ uriMatcher.addURI(CONTRIBUTION_AUTHORITY, BASE_PATH, CONTRIBUTIONS);
+ uriMatcher.addURI(CONTRIBUTION_AUTHORITY, BASE_PATH + "/#", CONTRIBUTIONS_ID);
}
public static Uri uriForId(int id) {
@@ -176,7 +176,7 @@ public class ContributionsContentProvider extends ContentProvider {
if (TextUtils.isEmpty(selection)) {
rowsUpdated = sqlDB.update(TABLE_NAME,
contentValues,
- Contribution.Table.COLUMN_ID + " = ?",
+ ContributionDao.Table.COLUMN_ID + " = ?",
new String[]{String.valueOf(id)});
} else {
throw new IllegalArgumentException(
diff --git a/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsListAdapter.java b/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsListAdapter.java
index 7be6dd663..a31caf54f 100644
--- a/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsListAdapter.java
+++ b/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsListAdapter.java
@@ -11,8 +11,11 @@ import fr.free.nrw.commons.R;
class ContributionsListAdapter extends CursorAdapter {
- public ContributionsListAdapter(Context context, Cursor c, int flags) {
+ private final ContributionDao contributionDao;
+
+ public ContributionsListAdapter(Context context, Cursor c, int flags, ContributionDao contributionDao) {
super(context, c, flags);
+ this.contributionDao = contributionDao;
}
@Override
@@ -26,7 +29,7 @@ class ContributionsListAdapter extends CursorAdapter {
@Override
public void bindView(View view, Context context, Cursor cursor) {
final ContributionViewHolder views = (ContributionViewHolder)view.getTag();
- final Contribution contribution = Contribution.fromCursor(cursor);
+ final Contribution contribution = contributionDao.fromCursor(cursor);
views.imageView.setMedia(contribution);
views.titleView.setText(contribution.getDisplayTitle());
diff --git a/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsSyncAdapter.java b/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsSyncAdapter.java
index e67b164a8..f8245032b 100644
--- a/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsSyncAdapter.java
+++ b/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsSyncAdapter.java
@@ -30,7 +30,7 @@ import fr.free.nrw.commons.mwapi.MediaWikiApi;
import timber.log.Timber;
import static fr.free.nrw.commons.contributions.Contribution.STATE_COMPLETED;
-import static fr.free.nrw.commons.contributions.Contribution.Table.COLUMN_FILENAME;
+import static fr.free.nrw.commons.contributions.ContributionDao.Table.COLUMN_FILENAME;
import static fr.free.nrw.commons.contributions.ContributionsContentProvider.BASE_URI;
@SuppressWarnings("WeakerAccess")
@@ -89,6 +89,7 @@ public class ContributionsSyncAdapter extends AbstractThreadedSyncAdapter {
LogEventResult result;
Boolean done = false;
String queryContinue = null;
+ ContributionDao contributionDao = new ContributionDao(() -> contentProviderClient);
while (!done) {
try {
@@ -121,7 +122,7 @@ public class ContributionsSyncAdapter extends AbstractThreadedSyncAdapter {
"", -1, dateUpdated, dateUpdated, user,
"", "");
contrib.setState(STATE_COMPLETED);
- imageValues.add(contrib.toContentValues());
+ imageValues.add(contributionDao.toContentValues(contrib));
if (imageValues.size() % COMMIT_THRESHOLD == 0) {
try {
diff --git a/app/src/main/java/fr/free/nrw/commons/data/Category.java b/app/src/main/java/fr/free/nrw/commons/data/Category.java
deleted file mode 100644
index be4a29846..000000000
--- a/app/src/main/java/fr/free/nrw/commons/data/Category.java
+++ /dev/null
@@ -1,276 +0,0 @@
-package fr.free.nrw.commons.data;
-
-import android.content.ContentProviderClient;
-import android.content.ContentValues;
-import android.database.Cursor;
-import android.database.sqlite.SQLiteDatabase;
-import android.net.Uri;
-import android.os.RemoteException;
-import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
-
-import java.util.ArrayList;
-import java.util.Date;
-
-import fr.free.nrw.commons.category.CategoryContentProvider;
-
-/**
- * Represents a category
- */
-public class Category {
- private Uri contentUri;
-
- private String name;
- private Date lastUsed;
- private int timesUsed;
-
- // Getters/setters
- /**
- * Gets name
- *
- * @return name
- */
- public String getName() {
- return name;
- }
-
- /**
- * Modifies name
- *
- * @param name Category name
- */
- public void setName(String name) {
- this.name = name;
- }
-
- /**
- * Gets last used date
- *
- * @return Last used date
- */
- private Date getLastUsed() {
- // warning: Date objects are mutable.
- return (Date)lastUsed.clone();
- }
-
- /**
- * Modifies last used date
- *
- * @param lastUsed Category date
- */
- public void setLastUsed(Date lastUsed) {
- // warning: Date objects are mutable.
- this.lastUsed = (Date)lastUsed.clone();
- }
-
- /**
- * Generates new last used date
- */
- private void touch() {
- lastUsed = new Date();
- }
-
- /**
- * Gets no. of times the category is used
- *
- * @return no. of times used
- */
- private int getTimesUsed() {
- return timesUsed;
- }
-
- /**
- * Modifies no. of times used
- *
- * @param timesUsed Category used times
- */
- public void setTimesUsed(int timesUsed) {
- this.timesUsed = timesUsed;
- }
-
- /**
- * Increments timesUsed by 1 and sets last used date as now.
- */
- public void incTimesUsed() {
- timesUsed++;
- touch();
- }
-
- //region Database/content-provider stuff
-
- /**
- * Persist category.
- * @param client ContentProviderClient to handle DB connection
- */
- public void save(ContentProviderClient client) {
- try {
- if (contentUri == null) {
- contentUri = client.insert(CategoryContentProvider.BASE_URI, this.toContentValues());
- } else {
- client.update(contentUri, toContentValues(), null, null);
- }
- } catch (RemoteException e) {
- throw new RuntimeException(e);
- }
- }
-
- /**
- * Gets content values
- *
- * @return Content values
- */
- private ContentValues toContentValues() {
- ContentValues cv = new ContentValues();
- cv.put(Table.COLUMN_NAME, getName());
- cv.put(Table.COLUMN_LAST_USED, getLastUsed().getTime());
- cv.put(Table.COLUMN_TIMES_USED, getTimesUsed());
- return cv;
- }
-
- /**
- * Gets category from cursor
- * @param cursor Category cursor
- * @return Category from cursor
- */
- private static Category fromCursor(Cursor cursor) {
- // Hardcoding column positions!
- Category c = new Category();
- c.contentUri = CategoryContentProvider.uriForId(cursor.getInt(0));
- c.name = cursor.getString(1);
- c.lastUsed = new Date(cursor.getLong(2));
- c.timesUsed = cursor.getInt(3);
- return c;
- }
-
- /**
- * Find persisted category in database, based on its name.
- * @param client ContentProviderClient to handle DB connection
- * @param name Category's name
- * @return category from database, or null if not found
- */
- public static @Nullable Category find(ContentProviderClient client, String name) {
- Cursor cursor = null;
- try {
- cursor = client.query(
- CategoryContentProvider.BASE_URI,
- Category.Table.ALL_FIELDS,
- Category.Table.COLUMN_NAME + "=?",
- new String[]{name},
- null);
- if (cursor != null && cursor.moveToFirst()) {
- return Category.fromCursor(cursor);
- }
- } catch (RemoteException e) {
- // This feels lazy, but to hell with checked exceptions. :)
- throw new RuntimeException(e);
- } finally {
- if (cursor != null) {
- cursor.close();
- }
- }
- return null;
- }
-
- /**
- * Retrieve recently-used categories, ordered by descending date.
- * @return a list containing recent categories
- */
- public static @NonNull ArrayList recentCategories(ContentProviderClient client, int limit) {
- ArrayList items = new ArrayList<>();
- Cursor cursor = null;
- try {
- cursor = client.query(
- CategoryContentProvider.BASE_URI,
- Category.Table.ALL_FIELDS,
- null,
- new String[]{},
- Category.Table.COLUMN_LAST_USED + " DESC");
- // fixme add a limit on the original query instead of falling out of the loop?
- while (cursor != null && cursor.moveToNext()
- && cursor.getPosition() < limit) {
- Category cat = Category.fromCursor(cursor);
- items.add(cat.getName());
- }
- } catch (RemoteException e) {
- throw new RuntimeException(e);
- } finally {
- if (cursor != null) {
- cursor.close();
- }
- }
- return items;
- }
-
- public static class Table {
- public static final String TABLE_NAME = "categories";
-
- public static final String COLUMN_ID = "_id";
- public static final String COLUMN_NAME = "name";
- public static final String COLUMN_LAST_USED = "last_used";
- public static final String COLUMN_TIMES_USED = "times_used";
-
- // NOTE! KEEP IN SAME ORDER AS THEY ARE DEFINED UP THERE. HELPS HARD CODE COLUMN INDICES.
- public static final String[] ALL_FIELDS = {
- COLUMN_ID,
- COLUMN_NAME,
- COLUMN_LAST_USED,
- COLUMN_TIMES_USED
- };
-
- private static final String CREATE_TABLE_STATEMENT = "CREATE TABLE " + TABLE_NAME + " ("
- + COLUMN_ID + " INTEGER PRIMARY KEY,"
- + COLUMN_NAME + " STRING,"
- + COLUMN_LAST_USED + " INTEGER,"
- + COLUMN_TIMES_USED + " INTEGER"
- + ");";
-
- /**
- * Creates new table with provided SQLite database
- *
- * @param db Category database
- */
- public static void onCreate(SQLiteDatabase db) {
- db.execSQL(CREATE_TABLE_STATEMENT);
- }
-
- /**
- * Deletes existing table
- * @param db Category database
- */
- public static void onDelete(SQLiteDatabase db) {
- db.execSQL("DROP TABLE IF EXISTS " + TABLE_NAME);
- onCreate(db);
- }
-
- /**
- * Updates given database
- * @param db Category database
- * @param from Exiting category id
- * @param to New category id
- */
- public static void onUpdate(SQLiteDatabase db, int from, int to) {
- if (from == to) {
- return;
- }
- if (from < 4) {
- // doesn't exist yet
- from++;
- onUpdate(db, from, to);
- return;
- }
- if (from == 4) {
- // table added in version 5
- onCreate(db);
- from++;
- onUpdate(db, from, to);
- return;
- }
- if (from == 5) {
- from++;
- onUpdate(db, from, to);
- return;
- }
- }
- }
- //endregion
-}
\ No newline at end of file
diff --git a/app/src/main/java/fr/free/nrw/commons/data/DBOpenHelper.java b/app/src/main/java/fr/free/nrw/commons/data/DBOpenHelper.java
index 5e28a8a32..35305c5ba 100644
--- a/app/src/main/java/fr/free/nrw/commons/data/DBOpenHelper.java
+++ b/app/src/main/java/fr/free/nrw/commons/data/DBOpenHelper.java
@@ -4,8 +4,9 @@ import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
-import fr.free.nrw.commons.contributions.Contribution;
-import fr.free.nrw.commons.modifications.ModifierSequence;
+import fr.free.nrw.commons.category.CategoryDao;
+import fr.free.nrw.commons.contributions.ContributionDao;
+import fr.free.nrw.commons.modifications.ModifierSequenceDao;
public class DBOpenHelper extends SQLiteOpenHelper {
@@ -13,7 +14,8 @@ public class DBOpenHelper extends SQLiteOpenHelper {
private static final int DATABASE_VERSION = 6;
/**
- * Do not use, please call CommonsApplication.getDBOpenHelper()
+ * Do not use directly - @Inject an instance where it's needed and let
+ * dependency injection take care of managing this as a singleton.
*/
public DBOpenHelper(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
@@ -21,15 +23,15 @@ public class DBOpenHelper extends SQLiteOpenHelper {
@Override
public void onCreate(SQLiteDatabase sqLiteDatabase) {
- Contribution.Table.onCreate(sqLiteDatabase);
- ModifierSequence.Table.onCreate(sqLiteDatabase);
- Category.Table.onCreate(sqLiteDatabase);
+ ContributionDao.Table.onCreate(sqLiteDatabase);
+ ModifierSequenceDao.Table.onCreate(sqLiteDatabase);
+ CategoryDao.Table.onCreate(sqLiteDatabase);
}
@Override
public void onUpgrade(SQLiteDatabase sqLiteDatabase, int from, int to) {
- Contribution.Table.onUpdate(sqLiteDatabase, from, to);
- ModifierSequence.Table.onUpdate(sqLiteDatabase, from, to);
- Category.Table.onUpdate(sqLiteDatabase, from, to);
+ ContributionDao.Table.onUpdate(sqLiteDatabase, from, to);
+ ModifierSequenceDao.Table.onUpdate(sqLiteDatabase, from, to);
+ CategoryDao.Table.onUpdate(sqLiteDatabase, from, to);
}
}
diff --git a/app/src/main/java/fr/free/nrw/commons/di/ActivityBuilderModule.java b/app/src/main/java/fr/free/nrw/commons/di/ActivityBuilderModule.java
index 27e16ce23..e4fb13427 100644
--- a/app/src/main/java/fr/free/nrw/commons/di/ActivityBuilderModule.java
+++ b/app/src/main/java/fr/free/nrw/commons/di/ActivityBuilderModule.java
@@ -8,6 +8,7 @@ import fr.free.nrw.commons.auth.LoginActivity;
import fr.free.nrw.commons.auth.SignupActivity;
import fr.free.nrw.commons.contributions.ContributionsActivity;
import fr.free.nrw.commons.nearby.NearbyActivity;
+import fr.free.nrw.commons.notification.NotificationActivity;
import fr.free.nrw.commons.settings.SettingsActivity;
import fr.free.nrw.commons.upload.MultipleShareActivity;
import fr.free.nrw.commons.upload.ShareActivity;
@@ -43,4 +44,6 @@ public abstract class ActivityBuilderModule {
@ContributesAndroidInjector
abstract NearbyActivity bindNearbyActivity();
+ @ContributesAndroidInjector
+ abstract NotificationActivity bindNotificationActivity();
}
diff --git a/app/src/main/java/fr/free/nrw/commons/di/CommonsApplicationModule.java b/app/src/main/java/fr/free/nrw/commons/di/CommonsApplicationModule.java
index 99d3235e7..913eef57e 100644
--- a/app/src/main/java/fr/free/nrw/commons/di/CommonsApplicationModule.java
+++ b/app/src/main/java/fr/free/nrw/commons/di/CommonsApplicationModule.java
@@ -1,5 +1,6 @@
package fr.free.nrw.commons.di;
+import android.content.ContentProviderClient;
import android.content.SharedPreferences;
import android.preference.PreferenceManager;
import android.support.v4.util.LruCache;
@@ -22,10 +23,14 @@ import fr.free.nrw.commons.nearby.NearbyPlaces;
import fr.free.nrw.commons.upload.UploadController;
import static android.content.Context.MODE_PRIVATE;
+import static fr.free.nrw.commons.contributions.ContributionsContentProvider.CONTRIBUTION_AUTHORITY;
+import static fr.free.nrw.commons.modifications.ModificationsContentProvider.MODIFICATIONS_AUTHORITY;
@Module
@SuppressWarnings({"WeakerAccess", "unused"})
public class CommonsApplicationModule {
+ public static final String CATEGORY_AUTHORITY = "fr.free.nrw.commons.categories.contentprovider";
+
private CommonsApplication application;
public CommonsApplicationModule(CommonsApplication application) {
@@ -37,6 +42,24 @@ public class CommonsApplicationModule {
return new AccountUtil(application);
}
+ @Provides
+ @Named("category")
+ public ContentProviderClient provideCategoryContentProviderClient() {
+ return application.getContentResolver().acquireContentProviderClient(CATEGORY_AUTHORITY);
+ }
+
+ @Provides
+ @Named("contribution")
+ public ContentProviderClient provideContributionContentProviderClient() {
+ return application.getContentResolver().acquireContentProviderClient(CONTRIBUTION_AUTHORITY);
+ }
+
+ @Provides
+ @Named("modification")
+ public ContentProviderClient provideModificationContentProviderClient() {
+ return application.getContentResolver().acquireContentProviderClient(MODIFICATIONS_AUTHORITY);
+ }
+
@Provides
@Named("application_preferences")
public SharedPreferences providesApplicationSharedPreferences() {
diff --git a/app/src/main/java/fr/free/nrw/commons/location/LocationServiceManager.java b/app/src/main/java/fr/free/nrw/commons/location/LocationServiceManager.java
index 45a1a4181..d949189ed 100644
--- a/app/src/main/java/fr/free/nrw/commons/location/LocationServiceManager.java
+++ b/app/src/main/java/fr/free/nrw/commons/location/LocationServiceManager.java
@@ -31,20 +31,36 @@ public class LocationServiceManager implements LocationListener {
private final List locationListeners = new CopyOnWriteArrayList<>();
private boolean isLocationManagerRegistered = false;
+ /**
+ * Constructs a new instance of LocationServiceManager.
+ * @param context the context
+ */
public LocationServiceManager(Context context) {
this.context = context;
this.locationManager = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE);
}
+ /**
+ * Returns the current status of the GPS provider.
+ * @return true if the GPS provider is enabled
+ */
public boolean isProviderEnabled() {
return locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER);
}
+ /**
+ * Returns whether the location permission is granted.
+ * @return true if the location permission is granted
+ */
public boolean isLocationPermissionGranted() {
return ContextCompat.checkSelfPermission(context,
Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED;
}
+ /**
+ * Requests the location permission to be granted.
+ * @param activity the activity
+ */
public void requestPermissions(Activity activity) {
if (activity.isFinishing()) {
return;
@@ -77,6 +93,11 @@ public class LocationServiceManager implements LocationListener {
&& requestLocationUpdatesFromProvider(LocationManager.GPS_PROVIDER);
}
+ /**
+ * Requests location updates from the specified provider.
+ * @param locationProvider the location provider
+ * @return true if successful
+ */
private boolean requestLocationUpdatesFromProvider(String locationProvider) {
try {
locationManager.requestLocationUpdates(locationProvider,
@@ -93,6 +114,12 @@ public class LocationServiceManager implements LocationListener {
}
}
+ /**
+ * Returns whether a given location is better than the current best location.
+ * @param location the location to be tested
+ * @param currentBestLocation the current best location
+ * @return true if the given location is better
+ */
protected boolean isBetterLocation(Location location, Location currentBestLocation) {
if (currentBestLocation == null) {
// A new location is always better than no location
@@ -156,12 +183,20 @@ public class LocationServiceManager implements LocationListener {
}
}
+ /**
+ * Adds a new listener to the list of location listeners.
+ * @param listener the new listener
+ */
public void addLocationListener(LocationUpdateListener listener) {
if (!locationListeners.contains(listener)) {
locationListeners.add(listener);
}
}
+ /**
+ * Removes a listener from the list of location listeners.
+ * @param listener the listener to be removed
+ */
public void removeLocationListener(LocationUpdateListener listener) {
locationListeners.remove(listener);
}
diff --git a/app/src/main/java/fr/free/nrw/commons/modifications/ModificationsContentProvider.java b/app/src/main/java/fr/free/nrw/commons/modifications/ModificationsContentProvider.java
index 6dc993c9e..3f2c01930 100644
--- a/app/src/main/java/fr/free/nrw/commons/modifications/ModificationsContentProvider.java
+++ b/app/src/main/java/fr/free/nrw/commons/modifications/ModificationsContentProvider.java
@@ -16,20 +16,22 @@ import dagger.android.AndroidInjection;
import fr.free.nrw.commons.data.DBOpenHelper;
import timber.log.Timber;
+import static fr.free.nrw.commons.modifications.ModifierSequenceDao.Table.TABLE_NAME;
+
public class ModificationsContentProvider extends ContentProvider {
private static final int MODIFICATIONS = 1;
private static final int MODIFICATIONS_ID = 2;
- public static final String AUTHORITY = "fr.free.nrw.commons.modifications.contentprovider";
- private static final String BASE_PATH = "modifications";
+ public static final String MODIFICATIONS_AUTHORITY = "fr.free.nrw.commons.modifications.contentprovider";
+ public static final String BASE_PATH = "modifications";
- public static final Uri BASE_URI = Uri.parse("content://" + AUTHORITY + "/" + BASE_PATH);
+ public static final Uri BASE_URI = Uri.parse("content://" + MODIFICATIONS_AUTHORITY + "/" + BASE_PATH);
private static final UriMatcher uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
static {
- uriMatcher.addURI(AUTHORITY, BASE_PATH, MODIFICATIONS);
- uriMatcher.addURI(AUTHORITY, BASE_PATH + "/#", MODIFICATIONS_ID);
+ uriMatcher.addURI(MODIFICATIONS_AUTHORITY, BASE_PATH, MODIFICATIONS);
+ uriMatcher.addURI(MODIFICATIONS_AUTHORITY, BASE_PATH + "/#", MODIFICATIONS_ID);
}
public static Uri uriForId(int id) {
@@ -47,7 +49,7 @@ public class ModificationsContentProvider extends ContentProvider {
@Override
public Cursor query(@NonNull Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
SQLiteQueryBuilder queryBuilder = new SQLiteQueryBuilder();
- queryBuilder.setTables(ModifierSequence.Table.TABLE_NAME);
+ queryBuilder.setTables(TABLE_NAME);
int uriType = uriMatcher.match(uri);
@@ -78,7 +80,7 @@ public class ModificationsContentProvider extends ContentProvider {
long id = 0;
switch (uriType) {
case MODIFICATIONS:
- id = sqlDB.insert(ModifierSequence.Table.TABLE_NAME, null, contentValues);
+ id = sqlDB.insert(TABLE_NAME, null, contentValues);
break;
default:
throw new IllegalArgumentException("Unknown URI: " + uri);
@@ -94,7 +96,7 @@ public class ModificationsContentProvider extends ContentProvider {
switch (uriType) {
case MODIFICATIONS_ID:
String id = uri.getLastPathSegment();
- sqlDB.delete(ModifierSequence.Table.TABLE_NAME,
+ sqlDB.delete(TABLE_NAME,
"_id = ?",
new String[] { id }
);
@@ -114,7 +116,7 @@ public class ModificationsContentProvider extends ContentProvider {
case MODIFICATIONS:
for (ContentValues value: values) {
Timber.d("Inserting! %s", value);
- sqlDB.insert(ModifierSequence.Table.TABLE_NAME, null, value);
+ sqlDB.insert(TABLE_NAME, null, value);
}
break;
default:
@@ -140,7 +142,7 @@ public class ModificationsContentProvider extends ContentProvider {
int rowsUpdated = 0;
switch (uriType) {
case MODIFICATIONS:
- rowsUpdated = sqlDB.update(ModifierSequence.Table.TABLE_NAME,
+ rowsUpdated = sqlDB.update(TABLE_NAME,
contentValues,
selection,
selectionArgs);
@@ -149,9 +151,9 @@ public class ModificationsContentProvider extends ContentProvider {
int id = Integer.valueOf(uri.getLastPathSegment());
if (TextUtils.isEmpty(selection)) {
- rowsUpdated = sqlDB.update(ModifierSequence.Table.TABLE_NAME,
+ rowsUpdated = sqlDB.update(TABLE_NAME,
contentValues,
- ModifierSequence.Table.COLUMN_ID + " = ?",
+ ModifierSequenceDao.Table.COLUMN_ID + " = ?",
new String[] { String.valueOf(id) } );
} else {
throw new IllegalArgumentException("Parameter `selection` should be empty when updating an ID");
diff --git a/app/src/main/java/fr/free/nrw/commons/modifications/ModificationsSyncAdapter.java b/app/src/main/java/fr/free/nrw/commons/modifications/ModificationsSyncAdapter.java
index 1e886f225..f81c273d2 100644
--- a/app/src/main/java/fr/free/nrw/commons/modifications/ModificationsSyncAdapter.java
+++ b/app/src/main/java/fr/free/nrw/commons/modifications/ModificationsSyncAdapter.java
@@ -18,6 +18,7 @@ import javax.inject.Inject;
import fr.free.nrw.commons.CommonsApplication;
import fr.free.nrw.commons.contributions.Contribution;
+import fr.free.nrw.commons.contributions.ContributionDao;
import fr.free.nrw.commons.contributions.ContributionsContentProvider;
import fr.free.nrw.commons.mwapi.MediaWikiApi;
import timber.log.Timber;
@@ -25,6 +26,8 @@ import timber.log.Timber;
public class ModificationsSyncAdapter extends AbstractThreadedSyncAdapter {
@Inject MediaWikiApi mwApi;
+ @Inject ContributionDao contributionDao;
+ @Inject ModifierSequenceDao modifierSequenceDao;
public ModificationsSyncAdapter(Context context, boolean autoInitialize) {
super(context, autoInitialize);
@@ -79,11 +82,10 @@ public class ModificationsSyncAdapter extends AbstractThreadedSyncAdapter {
ContentProviderClient contributionsClient = null;
try {
- contributionsClient = getContext().getContentResolver().acquireContentProviderClient(ContributionsContentProvider.AUTHORITY);
+ contributionsClient = getContext().getContentResolver().acquireContentProviderClient(ContributionsContentProvider.CONTRIBUTION_AUTHORITY);
while (!allModifications.isAfterLast()) {
- ModifierSequence sequence = ModifierSequence.fromCursor(allModifications);
- sequence.setContentProviderClient(contentProviderClient);
+ ModifierSequence sequence = modifierSequenceDao.fromCursor(allModifications);
Contribution contrib;
Cursor contributionCursor;
@@ -93,7 +95,7 @@ public class ModificationsSyncAdapter extends AbstractThreadedSyncAdapter {
throw new RuntimeException(e);
}
contributionCursor.moveToFirst();
- contrib = Contribution.fromCursor(contributionCursor);
+ contrib = contributionDao.fromCursor(contributionCursor);
if (contrib.getState() == Contribution.STATE_COMPLETED) {
String pageContent;
@@ -121,7 +123,7 @@ public class ModificationsSyncAdapter extends AbstractThreadedSyncAdapter {
// FIXME: Log this somewhere else
Timber.d("Non success result! %s", editResult);
} else {
- sequence.delete();
+ modifierSequenceDao.delete(sequence);
}
}
allModifications.moveToNext();
diff --git a/app/src/main/java/fr/free/nrw/commons/modifications/ModifierSequence.java b/app/src/main/java/fr/free/nrw/commons/modifications/ModifierSequence.java
index 880b53313..93cb3bc3d 100644
--- a/app/src/main/java/fr/free/nrw/commons/modifications/ModifierSequence.java
+++ b/app/src/main/java/fr/free/nrw/commons/modifications/ModifierSequence.java
@@ -1,14 +1,8 @@
package fr.free.nrw.commons.modifications;
-import android.content.ContentProviderClient;
-import android.content.ContentValues;
-import android.database.Cursor;
-import android.database.sqlite.SQLiteDatabase;
import android.net.Uri;
-import android.os.RemoteException;
import org.json.JSONArray;
-import org.json.JSONException;
import org.json.JSONObject;
import java.util.ArrayList;
@@ -17,14 +11,13 @@ public class ModifierSequence {
private Uri mediaUri;
private ArrayList modifiers;
private Uri contentUri;
- private ContentProviderClient client;
public ModifierSequence(Uri mediaUri) {
this.mediaUri = mediaUri;
modifiers = new ArrayList<>();
}
- public ModifierSequence(Uri mediaUri, JSONObject data) {
+ ModifierSequence(Uri mediaUri, JSONObject data) {
this(mediaUri);
JSONArray modifiersJSON = data.optJSONArray("modifiers");
for (int i = 0; i < modifiersJSON.length(); i++) {
@@ -32,7 +25,7 @@ public class ModifierSequence {
}
}
- public Uri getMediaUri() {
+ Uri getMediaUri() {
return mediaUri;
}
@@ -40,14 +33,14 @@ public class ModifierSequence {
modifiers.add(modifier);
}
- public String executeModifications(String pageName, String pageContents) {
+ String executeModifications(String pageName, String pageContents) {
for (PageModifier modifier: modifiers) {
pageContents = modifier.doModification(pageName, pageContents);
}
return pageContents;
}
- public String getEditSummary() {
+ String getEditSummary() {
StringBuilder editSummary = new StringBuilder();
for (PageModifier modifier: modifiers) {
editSummary.append(modifier.getEditSumary()).append(" ");
@@ -56,97 +49,16 @@ public class ModifierSequence {
return editSummary.toString();
}
- public JSONObject toJSON() {
- JSONObject data = new JSONObject();
- try {
- JSONArray modifiersJSON = new JSONArray();
- for (PageModifier modifier: modifiers) {
- modifiersJSON.put(modifier.toJSON());
- }
- data.put("modifiers", modifiersJSON);
- return data;
- } catch (JSONException e) {
- throw new RuntimeException(e);
- }
+ ArrayList getModifiers() {
+ return modifiers;
}
- public ContentValues toContentValues() {
- ContentValues cv = new ContentValues();
- cv.put(Table.COLUMN_MEDIA_URI, mediaUri.toString());
- cv.put(Table.COLUMN_DATA, toJSON().toString());
- return cv;
+ Uri getContentUri() {
+ return contentUri;
}
- public static ModifierSequence fromCursor(Cursor cursor) {
- // Hardcoding column positions!
- ModifierSequence ms = null;
- try {
- ms = new ModifierSequence(Uri.parse(cursor.getString(1)),
- new JSONObject(cursor.getString(2)));
- } catch (JSONException e) {
- throw new RuntimeException(e);
- }
- ms.contentUri = ModificationsContentProvider.uriForId(cursor.getInt(0));
-
- return ms;
+ void setContentUri(Uri contentUri) {
+ this.contentUri = contentUri;
}
- public void save() {
- try {
- if (contentUri == null) {
- contentUri = client.insert(ModificationsContentProvider.BASE_URI, this.toContentValues());
- } else {
- client.update(contentUri, toContentValues(), null, null);
- }
- } catch (RemoteException e) {
- throw new RuntimeException(e);
- }
- }
-
- public void delete() {
- try {
- client.delete(contentUri, null, null);
- } catch (RemoteException e) {
- throw new RuntimeException(e);
- }
- }
-
- public void setContentProviderClient(ContentProviderClient client) {
- this.client = client;
- }
-
- public static class Table {
- public static final String TABLE_NAME = "modifications";
-
- public static final String COLUMN_ID = "_id";
- public static final String COLUMN_MEDIA_URI = "mediauri";
- public static final String COLUMN_DATA = "data";
-
- // NOTE! KEEP IN SAME ORDER AS THEY ARE DEFINED UP THERE. HELPS HARD CODE COLUMN INDICES.
- public static final String[] ALL_FIELDS = {
- COLUMN_ID,
- COLUMN_MEDIA_URI,
- COLUMN_DATA
- };
-
- private static final String CREATE_TABLE_STATEMENT = "CREATE TABLE " + TABLE_NAME + " ("
- + "_id INTEGER PRIMARY KEY,"
- + "mediauri STRING,"
- + "data STRING"
- + ");";
-
- public static void onCreate(SQLiteDatabase db) {
- db.execSQL(CREATE_TABLE_STATEMENT);
- }
-
- public static void onUpdate(SQLiteDatabase db, int from, int to) {
- db.execSQL("DROP TABLE IF EXISTS " + TABLE_NAME);
- onCreate(db);
- }
-
- public static void onDelete(SQLiteDatabase db) {
- db.execSQL("DROP TABLE IF EXISTS " + TABLE_NAME);
- onCreate(db);
- }
- }
}
diff --git a/app/src/main/java/fr/free/nrw/commons/modifications/ModifierSequenceDao.java b/app/src/main/java/fr/free/nrw/commons/modifications/ModifierSequenceDao.java
new file mode 100644
index 000000000..e6b741d7a
--- /dev/null
+++ b/app/src/main/java/fr/free/nrw/commons/modifications/ModifierSequenceDao.java
@@ -0,0 +1,124 @@
+package fr.free.nrw.commons.modifications;
+
+import android.content.ContentProviderClient;
+import android.content.ContentValues;
+import android.database.Cursor;
+import android.database.sqlite.SQLiteDatabase;
+import android.net.Uri;
+import android.os.RemoteException;
+
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import javax.inject.Inject;
+import javax.inject.Named;
+import javax.inject.Provider;
+
+public class ModifierSequenceDao {
+
+ private final Provider clientProvider;
+
+ @Inject
+ public ModifierSequenceDao(@Named("modification") Provider clientProvider) {
+ this.clientProvider = clientProvider;
+ }
+
+ public void save(ModifierSequence sequence) {
+ ContentProviderClient db = clientProvider.get();
+ try {
+ if (sequence.getContentUri() == null) {
+ sequence.setContentUri(db.insert(ModificationsContentProvider.BASE_URI, toContentValues(sequence)));
+ } else {
+ db.update(sequence.getContentUri(), toContentValues(sequence), null, null);
+ }
+ } catch (RemoteException e) {
+ throw new RuntimeException(e);
+ } finally {
+ db.release();
+ }
+ }
+
+ public void delete(ModifierSequence sequence) {
+ ContentProviderClient db = clientProvider.get();
+ try {
+ db.delete(sequence.getContentUri(), null, null);
+ } catch (RemoteException e) {
+ throw new RuntimeException(e);
+ } finally {
+ db.release();
+ }
+ }
+
+ ModifierSequence fromCursor(Cursor cursor) {
+ // Hardcoding column positions!
+ ModifierSequence ms = null;
+ try {
+ ms = new ModifierSequence(Uri.parse(cursor.getString(1)),
+ new JSONObject(cursor.getString(2)));
+ } catch (JSONException e) {
+ throw new RuntimeException(e);
+ }
+ ms.setContentUri( ModificationsContentProvider.uriForId(cursor.getInt(0)));
+
+ return ms;
+ }
+
+ private JSONObject toJSON(ModifierSequence sequence) {
+ JSONObject data = new JSONObject();
+ try {
+ JSONArray modifiersJSON = new JSONArray();
+ for (PageModifier modifier: sequence.getModifiers()) {
+ modifiersJSON.put(modifier.toJSON());
+ }
+ data.put("modifiers", modifiersJSON);
+ return data;
+ } catch (JSONException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ private ContentValues toContentValues(ModifierSequence sequence) {
+ ContentValues cv = new ContentValues();
+ cv.put(Table.COLUMN_MEDIA_URI, sequence.getMediaUri().toString());
+ cv.put(Table.COLUMN_DATA, toJSON(sequence).toString());
+ return cv;
+ }
+
+ public static class Table {
+ static final String TABLE_NAME = "modifications";
+
+ static final String COLUMN_ID = "_id";
+ static final String COLUMN_MEDIA_URI = "mediauri";
+ static final String COLUMN_DATA = "data";
+
+ // NOTE! KEEP IN SAME ORDER AS THEY ARE DEFINED UP THERE. HELPS HARD CODE COLUMN INDICES.
+ public static final String[] ALL_FIELDS = {
+ COLUMN_ID,
+ COLUMN_MEDIA_URI,
+ COLUMN_DATA
+ };
+
+ static final String DROP_TABLE_STATEMENT = "DROP TABLE IF EXISTS " + TABLE_NAME;
+
+ static final String CREATE_TABLE_STATEMENT = "CREATE TABLE " + TABLE_NAME + " ("
+ + "_id INTEGER PRIMARY KEY,"
+ + "mediauri STRING,"
+ + "data STRING"
+ + ");";
+
+ public static void onCreate(SQLiteDatabase db) {
+ db.execSQL(CREATE_TABLE_STATEMENT);
+ }
+
+ public static void onUpdate(SQLiteDatabase db, int from, int to) {
+ db.execSQL(DROP_TABLE_STATEMENT);
+ onCreate(db);
+ }
+
+ public static void onDelete(SQLiteDatabase db) {
+ db.execSQL(DROP_TABLE_STATEMENT);
+ onCreate(db);
+ }
+ }
+}
diff --git a/app/src/main/java/fr/free/nrw/commons/notification/Notification.java b/app/src/main/java/fr/free/nrw/commons/notification/Notification.java
new file mode 100644
index 000000000..cb1aa62b6
--- /dev/null
+++ b/app/src/main/java/fr/free/nrw/commons/notification/Notification.java
@@ -0,0 +1,26 @@
+package fr.free.nrw.commons.notification;
+
+/**
+ * Created by root on 18.12.2017.
+ */
+
+public class Notification {
+ public NotificationType notificationType;
+ public String notificationText;
+
+
+ Notification (NotificationType notificationType, String notificationText) {
+ this.notificationType = notificationType;
+ this.notificationText = notificationText;
+ }
+
+
+ public enum NotificationType {
+ /* Added for test purposes, needs to be rescheduled after implementing
+ fetching notifications from server */
+ edit,
+ mention,
+ message,
+ block;
+ }
+}
diff --git a/app/src/main/java/fr/free/nrw/commons/notification/NotificationActivity.java b/app/src/main/java/fr/free/nrw/commons/notification/NotificationActivity.java
new file mode 100644
index 000000000..9da69e0eb
--- /dev/null
+++ b/app/src/main/java/fr/free/nrw/commons/notification/NotificationActivity.java
@@ -0,0 +1,50 @@
+package fr.free.nrw.commons.notification;
+
+import android.os.Bundle;
+import android.support.annotation.Nullable;
+import android.support.v7.widget.LinearLayoutManager;
+import android.support.v7.widget.RecyclerView;
+
+import butterknife.BindView;
+import butterknife.ButterKnife;
+import butterknife.Optional;
+import fr.free.nrw.commons.R;
+import fr.free.nrw.commons.theme.NavigationBaseActivity;
+
+/**
+ * Created by root on 18.12.2017.
+ */
+
+public class NotificationActivity extends NavigationBaseActivity {
+ NotificationAdapterFactory notificationAdapterFactory;
+
+ @Nullable
+ @BindView(R.id.listView) RecyclerView recyclerView;
+
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_notification);
+ ButterKnife.bind(this);
+ initListView();
+ addNotifications();
+ initDrawer();
+ }
+
+ private void initListView() {
+ recyclerView = findViewById(R.id.listView);
+ recyclerView.setLayoutManager(new LinearLayoutManager(this));
+ notificationAdapterFactory = new NotificationAdapterFactory(new NotificationRenderer.NotificationClicked() {
+ @Override
+ public void notificationClicked(Notification notification) {
+
+ }
+ });
+ }
+
+ private void addNotifications() {
+
+ recyclerView.setAdapter(notificationAdapterFactory.create(NotificationController.loadNotifications()));
+ }
+}
diff --git a/app/src/main/java/fr/free/nrw/commons/notification/NotificationAdapterFactory.java b/app/src/main/java/fr/free/nrw/commons/notification/NotificationAdapterFactory.java
new file mode 100644
index 000000000..83a60dcfb
--- /dev/null
+++ b/app/src/main/java/fr/free/nrw/commons/notification/NotificationAdapterFactory.java
@@ -0,0 +1,30 @@
+package fr.free.nrw.commons.notification;
+
+import android.support.annotation.NonNull;
+
+import com.pedrogomez.renderers.ListAdapteeCollection;
+import com.pedrogomez.renderers.RVRendererAdapter;
+import com.pedrogomez.renderers.RendererBuilder;
+
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * Created by root on 19.12.2017.
+ */
+
+class NotificationAdapterFactory {
+ private NotificationRenderer.NotificationClicked listener;
+
+ NotificationAdapterFactory(@NonNull NotificationRenderer.NotificationClicked listener) {
+ this.listener = listener;
+ }
+
+ public RVRendererAdapter create(List notifications) {
+ RendererBuilder builder = new RendererBuilder()
+ .bind(Notification.class, new NotificationRenderer(listener));
+ ListAdapteeCollection collection = new ListAdapteeCollection<>(
+ notifications != null ? notifications : Collections.emptyList());
+ return new RVRendererAdapter<>(builder, collection);
+ }
+}
diff --git a/app/src/main/java/fr/free/nrw/commons/notification/NotificationController.java b/app/src/main/java/fr/free/nrw/commons/notification/NotificationController.java
new file mode 100644
index 000000000..84f5c15d8
--- /dev/null
+++ b/app/src/main/java/fr/free/nrw/commons/notification/NotificationController.java
@@ -0,0 +1,23 @@
+package fr.free.nrw.commons.notification;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Created by root on 19.12.2017.
+ */
+
+public class NotificationController {
+
+ public static List loadNotifications() {
+ List notifications = new ArrayList<>();
+ notifications.add(new Notification(Notification.NotificationType.message, "notification 1"));
+ notifications.add(new Notification(Notification.NotificationType.edit, "notification 2"));
+ notifications.add(new Notification(Notification.NotificationType.mention, "notification 3"));
+ notifications.add(new Notification(Notification.NotificationType.message, "notification 4"));
+ notifications.add(new Notification(Notification.NotificationType.edit, "notification 5"));
+ notifications.add(new Notification(Notification.NotificationType.mention, "notification 6"));
+ notifications.add(new Notification(Notification.NotificationType.message, "notification 7"));
+ return notifications;
+ }
+}
diff --git a/app/src/main/java/fr/free/nrw/commons/notification/NotificationRenderer.java b/app/src/main/java/fr/free/nrw/commons/notification/NotificationRenderer.java
new file mode 100644
index 000000000..36272d4a2
--- /dev/null
+++ b/app/src/main/java/fr/free/nrw/commons/notification/NotificationRenderer.java
@@ -0,0 +1,70 @@
+package fr.free.nrw.commons.notification;
+
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import com.pedrogomez.renderers.Renderer;
+
+import butterknife.BindView;
+import butterknife.ButterKnife;
+import fr.free.nrw.commons.R;
+
+/**
+ * Created by root on 19.12.2017.
+ */
+
+public class NotificationRenderer extends Renderer {
+ @BindView(R.id.title) TextView title;
+ @BindView(R.id.description) TextView description;
+ @BindView(R.id.time) TextView time;
+ @BindView(R.id.icon) ImageView icon;
+ private NotificationClicked listener;
+
+
+ NotificationRenderer(NotificationClicked listener) {
+ this.listener = listener;
+ }
+
+ @Override
+ protected void setUpView(View view) { }
+
+ @Override
+ protected void hookListeners(View rootView) {
+ rootView.setOnClickListener(v -> listener.notificationClicked(getContent()));
+ }
+
+ @Override
+ protected View inflate(LayoutInflater layoutInflater, ViewGroup viewGroup) {
+ View inflatedView = layoutInflater.inflate(R.layout.item_notification, viewGroup, false);
+ ButterKnife.bind(this, inflatedView);
+ return inflatedView;
+ }
+
+ @Override
+ public void render() {
+ Notification notification = getContent();
+ title.setText(notification.notificationText);
+ time.setText("3d");
+ description.setText("Example notification description");
+ switch (notification.notificationType) {
+ case edit:
+ icon.setImageResource(R.drawable.ic_edit_black_24dp);
+ break;
+ case message:
+ icon.setImageResource(R.drawable.ic_message_black_24dp);
+ break;
+ case mention:
+ icon.setImageResource(R.drawable.ic_chat_bubble_black_24px);
+ break;
+ default:
+ icon.setImageResource(R.drawable.round_icon_unknown);
+ }
+ }
+
+ public interface NotificationClicked{
+ void notificationClicked(Notification notification);
+ }
+}
diff --git a/app/src/main/java/fr/free/nrw/commons/theme/NavigationBaseActivity.java b/app/src/main/java/fr/free/nrw/commons/theme/NavigationBaseActivity.java
index 60ea325e4..4e7cf767f 100644
--- a/app/src/main/java/fr/free/nrw/commons/theme/NavigationBaseActivity.java
+++ b/app/src/main/java/fr/free/nrw/commons/theme/NavigationBaseActivity.java
@@ -27,6 +27,7 @@ import fr.free.nrw.commons.auth.AccountUtil;
import fr.free.nrw.commons.auth.LoginActivity;
import fr.free.nrw.commons.contributions.ContributionsActivity;
import fr.free.nrw.commons.nearby.NearbyActivity;
+import fr.free.nrw.commons.notification.NotificationActivity;
import fr.free.nrw.commons.settings.SettingsActivity;
import timber.log.Timber;
@@ -143,6 +144,10 @@ public abstract class NavigationBaseActivity extends BaseActivity
.setNegativeButton(R.string.no, (dialog, which) -> dialog.cancel())
.show();
return true;
+ case R.id.action_notifications:
+ drawerLayout.closeDrawer(navigationView);
+ startActivityWithFlags(this, NotificationActivity.class, Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);
+ return true;
default:
Timber.e("Unknown option [%s] selected from the navigation menu", itemId);
return false;
diff --git a/app/src/main/java/fr/free/nrw/commons/upload/MultipleShareActivity.java b/app/src/main/java/fr/free/nrw/commons/upload/MultipleShareActivity.java
index 8858a11f5..a41938cf7 100644
--- a/app/src/main/java/fr/free/nrw/commons/upload/MultipleShareActivity.java
+++ b/app/src/main/java/fr/free/nrw/commons/upload/MultipleShareActivity.java
@@ -2,7 +2,6 @@ package fr.free.nrw.commons.upload;
import android.Manifest;
import android.app.ProgressDialog;
-import android.content.ContentProviderClient;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
@@ -40,6 +39,7 @@ import fr.free.nrw.commons.media.MediaDetailPagerFragment;
import fr.free.nrw.commons.modifications.CategoryModifier;
import fr.free.nrw.commons.modifications.ModificationsContentProvider;
import fr.free.nrw.commons.modifications.ModifierSequence;
+import fr.free.nrw.commons.modifications.ModifierSequenceDao;
import fr.free.nrw.commons.modifications.TemplateRemoveModifier;
import fr.free.nrw.commons.mwapi.MediaWikiApi;
import timber.log.Timber;
@@ -54,6 +54,7 @@ public class MultipleShareActivity extends AuthenticatedActivity
@Inject MediaWikiApi mwApi;
@Inject SessionManager sessionManager;
@Inject UploadController uploadController;
+ @Inject ModifierSequenceDao modifierSequenceDao;
@Inject @Named("default_preferences") SharedPreferences prefs;
private ArrayList photosList = null;
@@ -165,20 +166,18 @@ public class MultipleShareActivity extends AuthenticatedActivity
@Override
public void onCategoriesSave(List categories) {
if (categories.size() > 0) {
- ContentProviderClient client = getContentResolver().acquireContentProviderClient(ModificationsContentProvider.AUTHORITY);
for (Contribution contribution : photosList) {
ModifierSequence categoriesSequence = new ModifierSequence(contribution.getContentUri());
categoriesSequence.queueModifier(new CategoryModifier(categories.toArray(new String[]{})));
categoriesSequence.queueModifier(new TemplateRemoveModifier("Uncategorized"));
- categoriesSequence.setContentProviderClient(client);
- categoriesSequence.save();
+ modifierSequenceDao.save(categoriesSequence);
}
}
// FIXME: Make sure that the content provider is up
// This is the wrong place for it, but bleh - better than not having it turned on by default for people who don't go throughl ogin
- ContentResolver.setSyncAutomatically(sessionManager.getCurrentAccount(), ModificationsContentProvider.AUTHORITY, true); // Enable sync by default!
+ ContentResolver.setSyncAutomatically(sessionManager.getCurrentAccount(), ModificationsContentProvider.MODIFICATIONS_AUTHORITY, true); // Enable sync by default!
finish();
}
diff --git a/app/src/main/java/fr/free/nrw/commons/upload/ShareActivity.java b/app/src/main/java/fr/free/nrw/commons/upload/ShareActivity.java
index 963ae3fd3..2f0a4977e 100644
--- a/app/src/main/java/fr/free/nrw/commons/upload/ShareActivity.java
+++ b/app/src/main/java/fr/free/nrw/commons/upload/ShareActivity.java
@@ -39,7 +39,6 @@ import javax.inject.Inject;
import javax.inject.Named;
import butterknife.ButterKnife;
-import fr.free.nrw.commons.CommonsApplication;
import fr.free.nrw.commons.R;
import fr.free.nrw.commons.auth.AuthenticatedActivity;
import fr.free.nrw.commons.auth.SessionManager;
@@ -50,8 +49,8 @@ import fr.free.nrw.commons.contributions.Contribution;
import fr.free.nrw.commons.modifications.CategoryModifier;
import fr.free.nrw.commons.modifications.ModificationsContentProvider;
import fr.free.nrw.commons.modifications.ModifierSequence;
+import fr.free.nrw.commons.modifications.ModifierSequenceDao;
import fr.free.nrw.commons.modifications.TemplateRemoveModifier;
-import fr.free.nrw.commons.mwapi.EventLog;
import fr.free.nrw.commons.mwapi.MediaWikiApi;
import timber.log.Timber;
@@ -77,6 +76,7 @@ public class ShareActivity
@Inject CacheController cacheController;
@Inject SessionManager sessionManager;
@Inject UploadController uploadController;
+ @Inject ModifierSequenceDao modifierSequenceDao;
@Inject @Named("default_preferences") SharedPreferences prefs;
private String source;
@@ -167,13 +167,12 @@ public class ShareActivity
categoriesSequence.queueModifier(new CategoryModifier(categories.toArray(new String[]{})));
categoriesSequence.queueModifier(new TemplateRemoveModifier("Uncategorized"));
- categoriesSequence.setContentProviderClient(getContentResolver().acquireContentProviderClient(ModificationsContentProvider.AUTHORITY));
- categoriesSequence.save();
+ modifierSequenceDao.save(categoriesSequence);
}
// FIXME: Make sure that the content provider is up
// This is the wrong place for it, but bleh - better than not having it turned on by default for people who don't go throughl ogin
- ContentResolver.setSyncAutomatically(sessionManager.getCurrentAccount(), ModificationsContentProvider.AUTHORITY, true); // Enable sync by default!
+ ContentResolver.setSyncAutomatically(sessionManager.getCurrentAccount(), ModificationsContentProvider.MODIFICATIONS_AUTHORITY, true); // Enable sync by default!
finish();
}
diff --git a/app/src/main/java/fr/free/nrw/commons/upload/UploadController.java b/app/src/main/java/fr/free/nrw/commons/upload/UploadController.java
index 1d080e78f..ade22ece6 100644
--- a/app/src/main/java/fr/free/nrw/commons/upload/UploadController.java
+++ b/app/src/main/java/fr/free/nrw/commons/upload/UploadController.java
@@ -36,6 +36,9 @@ public class UploadController {
void onUploadStarted(Contribution contribution);
}
+ /**
+ * Constructs a new UploadController.
+ */
public UploadController(SessionManager sessionManager, Context context, SharedPreferences sharedPreferences) {
this.sessionManager = sessionManager;
this.context = context;
@@ -53,10 +56,13 @@ public class UploadController {
@Override
public void onServiceDisconnected(ComponentName componentName) {
// this should never happen
- throw new RuntimeException("UploadService died but the rest of the process did not!");
+ Timber.e(new RuntimeException("UploadService died but the rest of the process did not!"));
}
};
+ /**
+ * Prepares the upload service.
+ */
public void prepareService() {
Intent uploadServiceIntent = new Intent(context, UploadService.class);
uploadServiceIntent.setAction(UploadService.ACTION_START_SERVICE);
@@ -64,12 +70,25 @@ public class UploadController {
context.bindService(uploadServiceIntent, uploadServiceConnection, Context.BIND_AUTO_CREATE);
}
+ /**
+ * Disconnects the upload service.
+ */
public void cleanup() {
if (isUploadServiceConnected) {
context.unbindService(uploadServiceConnection);
}
}
+ /**
+ * Starts a new upload task.
+ * @param title the title of the contribution
+ * @param mediaUri the media URI of the contribution
+ * @param description the description of the contribution
+ * @param mimeType the MIME type of the contribution
+ * @param source the source of the contribution
+ * @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) {
Contribution contribution;
@@ -85,6 +104,11 @@ public class UploadController {
startUpload(contribution, onComplete);
}
+ /**
+ * Starts a new upload task.
+ * @param contribution the contribution object
+ * @param onComplete the progress tracker
+ */
public void startUpload(final Contribution contribution, final ContributionUploadProgress onComplete) {
//Set creator, desc, and license
if (TextUtils.isEmpty(contribution.getCreator())) {
@@ -173,6 +197,12 @@ public class UploadController {
}
+ /**
+ * Counts the number of bytes in {@code stream}.
+ * @param stream the stream
+ * @return the number of bytes in {@code stream}
+ * @throws IOException if an I/O error occurs
+ */
private long countBytes(InputStream stream) throws IOException {
long count = 0;
BufferedInputStream bis = new BufferedInputStream(stream);
diff --git a/app/src/main/java/fr/free/nrw/commons/upload/UploadService.java b/app/src/main/java/fr/free/nrw/commons/upload/UploadService.java
index 8013c256c..1c8f9ac58 100644
--- a/app/src/main/java/fr/free/nrw/commons/upload/UploadService.java
+++ b/app/src/main/java/fr/free/nrw/commons/upload/UploadService.java
@@ -4,7 +4,6 @@ import android.annotation.SuppressLint;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
-import android.content.ContentProviderClient;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.Intent;
@@ -31,6 +30,7 @@ import fr.free.nrw.commons.R;
import fr.free.nrw.commons.Utils;
import fr.free.nrw.commons.auth.SessionManager;
import fr.free.nrw.commons.contributions.Contribution;
+import fr.free.nrw.commons.contributions.ContributionDao;
import fr.free.nrw.commons.contributions.ContributionsActivity;
import fr.free.nrw.commons.contributions.ContributionsContentProvider;
import fr.free.nrw.commons.modifications.ModificationsContentProvider;
@@ -51,9 +51,9 @@ public class UploadService extends HandlerService {
@Inject MediaWikiApi mwApi;
@Inject SessionManager sessionManager;
@Inject @Named("default_preferences") SharedPreferences prefs;
+ @Inject ContributionDao contributionDao;
private NotificationManager notificationManager;
- private ContentProviderClient contributionsProviderClient;
private NotificationCompat.Builder curProgressNotification;
private int toUpload;
@@ -105,7 +105,7 @@ public class UploadService extends HandlerService {
startForeground(NOTIFICATION_UPLOAD_IN_PROGRESS, curProgressNotification.build());
contribution.setTransferred(transferred);
- contribution.save();
+ contributionDao.save(contribution);
}
}
@@ -113,7 +113,6 @@ public class UploadService extends HandlerService {
@Override
public void onDestroy() {
super.onDestroy();
- contributionsProviderClient.release();
Timber.d("UploadService.onDestroy; %s are yet to be uploaded", unfinishedUploads);
}
@@ -122,7 +121,6 @@ public class UploadService extends HandlerService {
super.onCreate();
notificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
- contributionsProviderClient = this.getContentResolver().acquireContentProviderClient(ContributionsContentProvider.AUTHORITY);
}
@Override
@@ -144,9 +142,7 @@ public class UploadService extends HandlerService {
contribution.setState(Contribution.STATE_QUEUED);
contribution.setTransferred(0);
- contribution.setContentProviderClient(contributionsProviderClient);
-
- contribution.save();
+ contributionDao.save(contribution);
toUpload++;
if (curProgressNotification != null && toUpload != 1) {
curProgressNotification.setContentText(getResources().getQuantityString(R.plurals.uploads_pending_notification_indicator, toUpload, toUpload));
@@ -167,11 +163,11 @@ public class UploadService extends HandlerService {
public int onStartCommand(Intent intent, int flags, int startId) {
if (intent.getAction().equals(ACTION_START_SERVICE) && freshStart) {
ContentValues failedValues = new ContentValues();
- failedValues.put(Contribution.Table.COLUMN_STATE, Contribution.STATE_FAILED);
+ failedValues.put(ContributionDao.Table.COLUMN_STATE, Contribution.STATE_FAILED);
int updated = getContentResolver().update(ContributionsContentProvider.BASE_URI,
failedValues,
- Contribution.Table.COLUMN_STATE + " = ? OR " + Contribution.Table.COLUMN_STATE + " = ?",
+ ContributionDao.Table.COLUMN_STATE + " = ? OR " + ContributionDao.Table.COLUMN_STATE + " = ?",
new String[]{ String.valueOf(Contribution.STATE_QUEUED), String.valueOf(Contribution.STATE_IN_PROGRESS) }
);
Timber.d("Set %d uploads to failed", updated);
@@ -261,7 +257,7 @@ public class UploadService extends HandlerService {
contribution.setImageUrl(uploadResult.getImageUrl());
contribution.setState(Contribution.STATE_COMPLETED);
contribution.setDateUploaded(uploadResult.getDateUploaded());
- contribution.save();
+ contributionDao.save(contribution);
}
} catch (IOException e) {
Timber.d("I have a network fuckup");
@@ -273,7 +269,7 @@ public class UploadService extends HandlerService {
toUpload--;
if (toUpload == 0) {
// Sync modifications right after all uplaods are processed
- ContentResolver.requestSync(sessionManager.getCurrentAccount(), ModificationsContentProvider.AUTHORITY, new Bundle());
+ ContentResolver.requestSync(sessionManager.getCurrentAccount(), ModificationsContentProvider.MODIFICATIONS_AUTHORITY, new Bundle());
stopForeground(true);
}
}
@@ -292,7 +288,7 @@ public class UploadService extends HandlerService {
notificationManager.notify(NOTIFICATION_UPLOAD_FAILED, failureNotification);
contribution.setState(Contribution.STATE_FAILED);
- contribution.save();
+ contributionDao.save(contribution);
}
private String findUniqueFilename(String fileName) throws IOException {
diff --git a/app/src/main/java/fr/free/nrw/commons/utils/DialogUtil.java b/app/src/main/java/fr/free/nrw/commons/utils/DialogUtil.java
index 3992324b5..78c1ca155 100644
--- a/app/src/main/java/fr/free/nrw/commons/utils/DialogUtil.java
+++ b/app/src/main/java/fr/free/nrw/commons/utils/DialogUtil.java
@@ -11,6 +11,11 @@ import timber.log.Timber;
public class DialogUtil {
+ /**
+ * Dismisses a dialog safely.
+ * @param activity the activity
+ * @param dialog the dialog to be dismissed
+ */
public static void dismissSafely(@Nullable Activity activity, @Nullable DialogFragment dialog) {
boolean isActivityDestroyed = false;
@@ -33,6 +38,11 @@ public class DialogUtil {
}
}
+ /**
+ * Shows a dialog safely.
+ * @param activity the activity
+ * @param dialog the dialog to be shown
+ */
public static void showSafely(Activity activity, Dialog dialog) {
if (activity == null || dialog == null) {
Timber.d("Show called with null activity / dialog. Ignoring.");
@@ -54,6 +64,11 @@ public class DialogUtil {
}
}
+ /**
+ * Shows a dialog safely.
+ * @param activity the activity
+ * @param dialog the dialog to be shown
+ */
public static void showSafely(FragmentActivity activity, DialogFragment dialog) {
boolean isActivityDestroyed = false;
diff --git a/app/src/main/res/drawable/ic_chat_bubble_black_24px.xml b/app/src/main/res/drawable/ic_chat_bubble_black_24px.xml
new file mode 100644
index 000000000..8d40c6d63
--- /dev/null
+++ b/app/src/main/res/drawable/ic_chat_bubble_black_24px.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/app/src/main/res/drawable/ic_edit_black_24dp.xml b/app/src/main/res/drawable/ic_edit_black_24dp.xml
new file mode 100644
index 000000000..2ab2fb753
--- /dev/null
+++ b/app/src/main/res/drawable/ic_edit_black_24dp.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/app/src/main/res/drawable/ic_message_black_24dp.xml b/app/src/main/res/drawable/ic_message_black_24dp.xml
new file mode 100644
index 000000000..d2876bfad
--- /dev/null
+++ b/app/src/main/res/drawable/ic_message_black_24dp.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/app/src/main/res/drawable/ic_notifications_black_24dp.xml b/app/src/main/res/drawable/ic_notifications_black_24dp.xml
new file mode 100644
index 000000000..7009a6763
--- /dev/null
+++ b/app/src/main/res/drawable/ic_notifications_black_24dp.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/app/src/main/res/layout-land/activity_login.xml b/app/src/main/res/layout-land/activity_login.xml
index 7265ba671..9ecaf9855 100644
--- a/app/src/main/res/layout-land/activity_login.xml
+++ b/app/src/main/res/layout-land/activity_login.xml
@@ -8,17 +8,17 @@
android:layout_width="400sp"
android:layout_height="wrap_content"
android:layout_gravity="center"
- android:layout_marginTop="8dp">
+ android:layout_marginTop="@dimen/small_gap">
@@ -34,11 +34,11 @@
android:layout_height="wrap_content"
android:background="@color/primaryColor"
android:gravity="center"
- android:paddingBottom="32dp"
- android:paddingTop="32dp"
+ android:paddingBottom="@dimen/large_gap"
+ android:paddingTop="@dimen/large_gap"
android:text="@string/login_to_your_account"
android:textColor="@android:color/white"
- android:textSize="24sp" />
+ android:textSize="@dimen/heading_text_size" />
@@ -67,12 +67,12 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/error_message_container"
- android:layout_marginBottom="16dp"
- android:layout_marginEnd="16dp"
- android:layout_marginLeft="16dp"
- android:layout_marginRight="16dp"
- android:layout_marginStart="16dp"
- android:layout_marginTop="16dp">
+ android:layout_marginBottom="@dimen/standard_gap"
+ android:layout_marginEnd="@dimen/standard_gap"
+ android:layout_marginLeft="@dimen/standard_gap"
+ android:layout_marginRight="@dimen/standard_gap"
+ android:layout_marginStart="@dimen/standard_gap"
+ android:layout_marginTop="@dimen/standard_gap">
@@ -141,11 +141,11 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/two_factor_container"
- android:layout_marginBottom="16dp"
- android:layout_marginEnd="16dp"
- android:layout_marginLeft="16dp"
- android:layout_marginRight="16dp"
- android:layout_marginStart="16dp">
+ android:layout_marginBottom="@dimen/standard_gap"
+ android:layout_marginEnd="@dimen/standard_gap"
+ android:layout_marginLeft="@dimen/standard_gap"
+ android:layout_marginRight="@dimen/standard_gap"
+ android:layout_marginStart="@dimen/standard_gap">
@@ -163,35 +163,24 @@
android:layout_width="0dp"
style="@style/Widget.AppCompat.Button.Colored"
android:layout_height="wrap_content"
- android:layout_marginLeft="8dp"
- android:layout_marginStart="8dp"
+ android:layout_marginLeft="@dimen/small_gap"
+ android:layout_marginStart="@dimen/small_gap"
android:layout_weight="1"
android:enabled="false"
android:text="@string/login" />
-
-
-
-
-
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginBottom="@dimen/standard_gap"
+ android:layout_gravity="center_horizontal"
+ android:text="@string/about_privacy_policy"
+ android:layout_centerHorizontal="true"/>
diff --git a/app/src/main/res/layout-land/welcome_do_upload.xml b/app/src/main/res/layout-land/welcome_do_upload.xml
index e5c7bb3fb..1f02c3183 100644
--- a/app/src/main/res/layout-land/welcome_do_upload.xml
+++ b/app/src/main/res/layout-land/welcome_do_upload.xml
@@ -5,16 +5,15 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#0c609c"
- android:gravity="center"
- >
+ android:gravity="center">
@@ -69,8 +68,8 @@
android:text="@string/tutorial_2_text"
android:layout_gravity="center"
android:textStyle="bold"
+ android:textSize="@dimen/normal_text"
android:textAlignment="center"
- android:paddingTop="24dp"
android:gravity="center_horizontal"
android:textColor="@android:color/white"/>
@@ -81,7 +80,7 @@
android:text="@string/tutorial_2_subtext"
android:layout_gravity="center"
android:textAlignment="textStart"
- android:paddingTop="16dp"
+ android:paddingTop="@dimen/standard_gap"
android:gravity="start"
android:textColor="@android:color/white"
/>
diff --git a/app/src/main/res/layout-land/welcome_dont_upload.xml b/app/src/main/res/layout-land/welcome_dont_upload.xml
index 7ac63e560..1d4d0db80 100644
--- a/app/src/main/res/layout-land/welcome_dont_upload.xml
+++ b/app/src/main/res/layout-land/welcome_dont_upload.xml
@@ -12,24 +12,25 @@
android:layout_gravity="center"
android:layout_width="240dp"
android:layout_height="wrap_content"
- android:layout_marginRight="10dp"
- android:layout_marginLeft="10dp"
+ android:layout_marginEnd="@dimen/standard_gap"
+ android:layout_marginRight="@dimen/standard_gap"
>
@@ -50,7 +51,7 @@
android:layout_gravity="center"
android:textStyle="bold"
android:textAlignment="center"
- android:paddingTop="24dp"
+ android:textSize="@dimen/normal_text"
android:gravity="center_horizontal"
android:textColor="@android:color/white"/>
@@ -61,7 +62,7 @@
android:text="@string/tutorial_3_subtext"
android:layout_gravity="center"
android:textAlignment="textStart"
- android:paddingTop="16dp"
+ android:paddingTop="@dimen/standard_gap"
android:gravity="start"
android:textColor="@android:color/white"
/>
diff --git a/app/src/main/res/layout-land/welcome_final.xml b/app/src/main/res/layout-land/welcome_final.xml
index a9d8c01af..0b99b481f 100644
--- a/app/src/main/res/layout-land/welcome_final.xml
+++ b/app/src/main/res/layout-land/welcome_final.xml
@@ -43,14 +43,15 @@
android:text="@string/welcome_final_text"
android:layout_gravity="center"
android:textStyle="bold"
+ android:textSize="@dimen/normal_text"
android:textAlignment="center"
android:gravity="center_horizontal"
android:textColor="@android:color/white"/>
@@ -45,7 +45,7 @@
android:text="@string/tutorial_4_subtext"
android:layout_gravity="center"
android:textAlignment="textStart"
- android:paddingTop="16dp"
+ android:paddingTop="@dimen/standard_gap"
android:gravity="start"
android:textColor="@android:color/white"
/>
diff --git a/app/src/main/res/layout-land/welcome_wikipedia.xml b/app/src/main/res/layout-land/welcome_wikipedia.xml
index 1a1531f4c..b4fbe9048 100644
--- a/app/src/main/res/layout-land/welcome_wikipedia.xml
+++ b/app/src/main/res/layout-land/welcome_wikipedia.xml
@@ -13,6 +13,8 @@
android:layout_height="wrap_content"
android:layout_gravity="center"
android:src="@drawable/welcome_wikipedia"
+ android:layout_marginEnd="@dimen/standard_gap"
+ android:layout_marginRight="@dimen/standard_gap"
android:adjustViewBounds="true"
android:contentDescription="@string/welcome_image_welcome_wikipedia"
/>
@@ -29,8 +31,8 @@
android:text="@string/tutorial_1_text"
android:layout_gravity="center"
android:textStyle="bold"
+ android:textSize="@dimen/normal_text"
android:textAlignment="center"
- android:paddingTop="24dp"
android:gravity="center_horizontal"
android:textColor="@android:color/white"/>
@@ -41,7 +43,7 @@
android:text="@string/tutorial_1_subtext"
android:layout_gravity="center"
android:textAlignment="center"
- android:paddingTop="16dp"
+ android:paddingTop="@dimen/standard_gap"
android:gravity="center_horizontal"
android:textColor="@android:color/white"
/>
diff --git a/app/src/main/res/layout-xlarge/activity_login.xml b/app/src/main/res/layout-xlarge/activity_login.xml
index 90bef2423..32cd47451 100644
--- a/app/src/main/res/layout-xlarge/activity_login.xml
+++ b/app/src/main/res/layout-xlarge/activity_login.xml
@@ -8,17 +8,17 @@
android:layout_width="400sp"
android:layout_height="wrap_content"
android:layout_gravity="center"
- android:layout_marginTop="8dp">
+ android:layout_marginTop="@dimen/small_gap">
@@ -34,11 +34,11 @@
android:layout_height="wrap_content"
android:background="@color/primaryColor"
android:gravity="center"
- android:paddingBottom="32dp"
- android:paddingTop="32dp"
+ android:paddingBottom="@dimen/large_gap"
+ android:paddingTop="@dimen/large_gap"
android:text="@string/login_to_your_account"
android:textColor="@android:color/white"
- android:textSize="24sp" />
+ android:textSize="@dimen/heading_text_size" />
@@ -67,12 +67,12 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/error_message_container"
- android:layout_marginBottom="16dp"
- android:layout_marginEnd="16dp"
- android:layout_marginLeft="16dp"
- android:layout_marginRight="16dp"
- android:layout_marginStart="16dp"
- android:layout_marginTop="16dp">
+ android:layout_marginBottom="@dimen/standard_gap"
+ android:layout_marginEnd="@dimen/standard_gap"
+ android:layout_marginLeft="@dimen/standard_gap"
+ android:layout_marginRight="@dimen/standard_gap"
+ android:layout_marginStart="@dimen/standard_gap"
+ android:layout_marginTop="@dimen/standard_gap">
@@ -141,11 +141,11 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/two_factor_container"
- android:layout_marginBottom="16dp"
- android:layout_marginEnd="16dp"
- android:layout_marginLeft="16dp"
- android:layout_marginRight="16dp"
- android:layout_marginStart="16dp">
+ android:layout_marginBottom="@dimen/standard_gap"
+ android:layout_marginEnd="@dimen/standard_gap"
+ android:layout_marginLeft="@dimen/standard_gap"
+ android:layout_marginRight="@dimen/standard_gap"
+ android:layout_marginStart="@dimen/standard_gap">
@@ -163,35 +163,24 @@
android:layout_width="0dp"
style="@style/Widget.AppCompat.Button.Colored"
android:layout_height="wrap_content"
- android:layout_marginLeft="8dp"
- android:layout_marginStart="8dp"
+ android:layout_marginLeft="@dimen/small_gap"
+ android:layout_marginStart="@dimen/small_gap"
android:layout_weight="1"
android:enabled="false"
android:text="@string/login" />
-
-
-
-
-
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginBottom="@dimen/standard_gap"
+ android:layout_gravity="center_horizontal"
+ android:text="@string/about_privacy_policy"
+ android:layout_centerHorizontal="true"/>
diff --git a/app/src/main/res/layout/activity_about.xml b/app/src/main/res/layout/activity_about.xml
index 89049d10d..bbbced3e9 100644
--- a/app/src/main/res/layout/activity_about.xml
+++ b/app/src/main/res/layout/activity_about.xml
@@ -15,12 +15,14 @@
android:layout_width="match_parent"
android:layout_height="wrap_content" />
-
@@ -47,7 +49,7 @@
style="?android:textAppearanceSmall"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_marginTop="16dp"
+ android:layout_marginTop="@dimen/standard_gap"
android:gravity="center"
android:text="@string/about_license" />
@@ -56,7 +58,7 @@
style="?android:textAppearanceSmall"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_marginTop="8dp"
+ android:layout_marginTop="@dimen/small_gap"
android:gravity="center"
android:text="@string/about_improve" />
@@ -65,7 +67,7 @@
style="?android:textAppearanceSmall"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_marginTop="16dp"
+ android:layout_marginTop="@dimen/standard_gap"
android:gravity="center"
android:text="@string/about_privacy_policy" />
@@ -74,7 +76,7 @@
style="?android:textAppearanceSmall"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_marginTop="16dp"
+ android:layout_marginTop="@dimen/standard_gap"
android:gravity="center"
android:text="@string/about_credits" />
@@ -83,7 +85,7 @@
style="?android:textAppearanceSmall"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_marginTop="32dp"
+ android:layout_marginTop="@dimen/large_gap"
android:alpha="0.2"
android:gravity="center" />
diff --git a/app/src/main/res/layout/activity_login.xml b/app/src/main/res/layout/activity_login.xml
index b1bc7152d..278e3464f 100644
--- a/app/src/main/res/layout/activity_login.xml
+++ b/app/src/main/res/layout/activity_login.xml
@@ -8,17 +8,17 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
- android:layout_marginTop="8dp">
+ android:layout_marginTop="@dimen/small_gap">
@@ -34,11 +34,11 @@
android:layout_height="wrap_content"
android:background="@color/primaryColor"
android:gravity="center"
- android:paddingBottom="32dp"
- android:paddingTop="32dp"
+ android:paddingBottom="@dimen/large_gap"
+ android:paddingTop="@dimen/large_gap"
android:text="@string/login_to_your_account"
android:textColor="@android:color/white"
- android:textSize="24sp" />
+ android:textSize="@dimen/heading_text_size" />
@@ -67,12 +67,12 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/error_message_container"
- android:layout_marginBottom="16dp"
- android:layout_marginEnd="16dp"
- android:layout_marginLeft="16dp"
- android:layout_marginRight="16dp"
- android:layout_marginStart="16dp"
- android:layout_marginTop="16dp">
+ android:layout_marginBottom="@dimen/standard_gap"
+ android:layout_marginEnd="@dimen/standard_gap"
+ android:layout_marginLeft="@dimen/standard_gap"
+ android:layout_marginRight="@dimen/standard_gap"
+ android:layout_marginStart="@dimen/standard_gap"
+ android:layout_marginTop="@dimen/standard_gap">
@@ -141,11 +141,11 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/two_factor_container"
- android:layout_marginBottom="16dp"
- android:layout_marginEnd="16dp"
- android:layout_marginLeft="16dp"
- android:layout_marginRight="16dp"
- android:layout_marginStart="16dp">
+ android:layout_marginBottom="@dimen/standard_gap"
+ android:layout_marginEnd="@dimen/standard_gap"
+ android:layout_marginLeft="@dimen/standard_gap"
+ android:layout_marginRight="@dimen/standard_gap"
+ android:layout_marginStart="@dimen/standard_gap">
@@ -163,35 +163,24 @@
android:layout_width="0dp"
style="@style/Widget.AppCompat.Button.Colored"
android:layout_height="wrap_content"
- android:layout_marginLeft="8dp"
- android:layout_marginStart="8dp"
+ android:layout_marginLeft="@dimen/small_gap"
+ android:layout_marginStart="@dimen/small_gap"
android:layout_weight="1"
android:enabled="false"
android:text="@string/login" />
-
-
-
-
-
+ android:layout_centerHorizontal="true"
+ android:layout_gravity="center_horizontal"
+ android:layout_marginBottom="@dimen/standard_gap"
+ android:text="@string/about_privacy_policy" />
diff --git a/app/src/main/res/layout/activity_multiple_uploads.xml b/app/src/main/res/layout/activity_multiple_uploads.xml
index 8095a5dce..115c2cf89 100644
--- a/app/src/main/res/layout/activity_multiple_uploads.xml
+++ b/app/src/main/res/layout/activity_multiple_uploads.xml
@@ -20,7 +20,7 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_below="@id/toolbar"
- android:orientation="vertical">
+ android:orientation="vertical" />
-
+ android:layout_height="wrap_content" />
+ android:layout_height="match_parent" />
diff --git a/app/src/main/res/layout/activity_notification.xml b/app/src/main/res/layout/activity_notification.xml
new file mode 100644
index 000000000..b2eb38475
--- /dev/null
+++ b/app/src/main/res/layout/activity_notification.xml
@@ -0,0 +1,34 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/activity_share.xml b/app/src/main/res/layout/activity_share.xml
index a75012077..e915517b5 100644
--- a/app/src/main/res/layout/activity_share.xml
+++ b/app/src/main/res/layout/activity_share.xml
@@ -9,14 +9,13 @@
android:layout_width="match_parent"
android:layout_height="match_parent">
-
-
diff --git a/app/src/main/res/layout/detail_category_item.xml b/app/src/main/res/layout/detail_category_item.xml
index 89197db5d..51302fd0d 100644
--- a/app/src/main/res/layout/detail_category_item.xml
+++ b/app/src/main/res/layout/detail_category_item.xml
@@ -13,17 +13,17 @@
android:background="?attr/subBackground"
android:foreground="?attr/selectableItemBackground"
android:gravity="center_vertical"
- android:minHeight="48dp"
- android:padding="12dp"
+ android:minHeight="@dimen/overflow_button_dimen"
+ android:padding="@dimen/quarter_standard_height"
android:textColor="@android:color/white"
- android:textSize="14sp"
- app:drawablePadding="6dp"
+ android:textSize="@dimen/description_text_size"
+ app:drawablePadding="@dimen/small_gap"
app:drawableStart="@drawable/ic_info_outline_white_24dp"
/>
diff --git a/app/src/main/res/layout/dialog_nearby_info.xml b/app/src/main/res/layout/dialog_nearby_info.xml
index e34c01c70..4010d3615 100644
--- a/app/src/main/res/layout/dialog_nearby_info.xml
+++ b/app/src/main/res/layout/dialog_nearby_info.xml
@@ -41,16 +41,18 @@
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
- android:layout_marginLeft="12dp"
- android:layout_marginRight="12dp"
+ android:layout_marginLeft="@dimen/quarter_standard_height"
+ android:layout_marginRight="@dimen/quarter_standard_height"
+ android:layout_marginStart="@dimen/quarter_standard_height"
+ android:layout_marginEnd="@dimen/quarter_standard_height"
android:layout_weight="1"
android:ellipsize="end"
android:fontFamily="serif"
android:lineSpacingMultiplier="0.9"
android:maxLines="3"
- android:paddingBottom="4dp"
+ android:paddingBottom="@dimen/tiny_gap"
android:textColor="@android:color/black"
- android:textSize="20sp"
+ android:textSize="@dimen/subheading_text_size"
tools:text="Lorem ipsum" />
@@ -67,8 +69,8 @@
@@ -103,9 +105,9 @@
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
- android:paddingBottom="16dp"
- android:paddingTop="16dp"
- android:text="GET DIRECTIONS"
+ android:paddingBottom="@dimen/standard_gap"
+ android:paddingTop="@dimen/standard_gap"
+ android:text="@string/get_directions"
android:textColor="@android:color/black" />
diff --git a/app/src/main/res/layout/drawer_header.xml b/app/src/main/res/layout/drawer_header.xml
index c27cab1ba..9bd3ae3a3 100644
--- a/app/src/main/res/layout/drawer_header.xml
+++ b/app/src/main/res/layout/drawer_header.xml
@@ -1,28 +1,30 @@
-
+
+
+ android:paddingBottom="@dimen/small_gap"/>
\ No newline at end of file
diff --git a/app/src/main/res/layout/fragment_categorization.xml b/app/src/main/res/layout/fragment_categorization.xml
index 4cdb9604c..880d0d5be 100644
--- a/app/src/main/res/layout/fragment_categorization.xml
+++ b/app/src/main/res/layout/fragment_categorization.xml
@@ -4,12 +4,12 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="?attr/fragmentCategorisationBackground"
- android:paddingBottom="8dip"
- android:paddingLeft="16dip"
- android:paddingStart="16dip"
- android:paddingRight="16dip"
- android:paddingEnd="16dip"
- android:paddingTop="8dip"
+ android:paddingBottom="@dimen/small_gap"
+ android:paddingLeft="@dimen/standard_gap"
+ android:paddingStart="@dimen/standard_gap"
+ android:paddingRight="@dimen/standard_gap"
+ android:paddingEnd="@dimen/standard_gap"
+ android:paddingTop="@dimen/small_gap"
android:theme="@style/DarkAppTheme"
>
@@ -34,8 +34,8 @@
android:layout_height="wrap_content"
android:indeterminate="true"
android:indeterminateOnly="true"
- android:layout_marginRight="4dp"
- android:layout_marginEnd="4dp"
+ android:layout_marginRight="@dimen/tiny_gap"
+ android:layout_marginEnd="@dimen/tiny_gap"
android:layout_gravity="center_vertical|right|end"
style="?android:progressBarStyleSmall"
android:visibility="gone"
@@ -54,7 +54,7 @@
android:id="@+id/categoriesExplanation"
android:layout_height="wrap_content"
android:layout_width="match_parent"
- android:layout_marginTop="48dp"
+ android:layout_marginTop="@dimen/huge_gap"
android:gravity="center"
android:focusable="true"
android:text="@string/categories_skip_explanation"
diff --git a/app/src/main/res/layout/fragment_media_detail.xml b/app/src/main/res/layout/fragment_media_detail.xml
index 35bcade7a..cffca14c6 100644
--- a/app/src/main/res/layout/fragment_media_detail.xml
+++ b/app/src/main/res/layout/fragment_media_detail.xml
@@ -43,29 +43,29 @@
+ android:layout_height="@dimen/standard_gap" />
+ android:padding="@dimen/standard_gap">
+ android:padding="@dimen/standard_gap">
+ android:textSize="@dimen/description_text_size" />
+ android:layout_height="@dimen/small_gap" />
+ android:padding="@dimen/standard_gap">
+ android:textSize="@dimen/description_text_size" />
+ android:layout_height="@dimen/small_gap" />
+ android:padding="@dimen/standard_gap">
@@ -154,15 +154,15 @@
android:layout_height="wrap_content"
android:background="?attr/subBackground"
android:orientation="vertical"
- android:padding="16dp">
+ android:padding="@dimen/standard_gap">
+ android:layout_height="@dimen/small_gap" />
+ android:layout_height="@dimen/small_gap" />
+ android:padding="@dimen/standard_gap">
+ android:textSize="@dimen/description_text_size" />
diff --git a/app/src/main/res/layout/fragment_no_permissions.xml b/app/src/main/res/layout/fragment_no_permissions.xml
index 8ea6f3a45..6dd2a6811 100644
--- a/app/src/main/res/layout/fragment_no_permissions.xml
+++ b/app/src/main/res/layout/fragment_no_permissions.xml
@@ -9,11 +9,11 @@
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:gravity="center"
- android:paddingLeft="30dp"
- android:paddingRight="30dp"
+ android:paddingLeft="@dimen/large_gap"
+ android:paddingRight="@dimen/large_gap"
android:text="@string/nearby_needs_permissions"
android:textAlignment="center"
- android:textSize="18sp" />
+ android:textSize="@dimen/subheading_text_size" />
diff --git a/app/src/main/res/layout/fragment_single_upload.xml b/app/src/main/res/layout/fragment_single_upload.xml
index 4e65667a2..d9dc87479 100644
--- a/app/src/main/res/layout/fragment_single_upload.xml
+++ b/app/src/main/res/layout/fragment_single_upload.xml
@@ -5,12 +5,12 @@
android:layout_gravity="fill"
android:orientation="vertical"
android:background="?attr/fragmentCategorisationBackground"
- android:paddingBottom="8dip"
- android:paddingLeft="16dip"
- android:paddingStart="16dip"
- android:paddingRight="16dip"
- android:paddingEnd="16dip"
- android:paddingTop="8dip"
+ android:paddingBottom="@dimen/small_gap"
+ android:paddingLeft="@dimen/standard_gap"
+ android:paddingStart="@dimen/standard_gap"
+ android:paddingRight="@dimen/standard_gap"
+ android:paddingEnd="@dimen/standard_gap"
+ android:paddingTop="@dimen/small_gap"
android:theme="@style/DarkAppTheme"
>
@@ -56,7 +56,7 @@
android:text="@string/share_license_summary"
android:id="@+id/share_license_summary"
android:gravity="center"
- android:layout_marginTop="16dp"
+ android:layout_marginTop="@dimen/standard_gap"
/>
diff --git a/app/src/main/res/layout/item_notification.xml b/app/src/main/res/layout/item_notification.xml
new file mode 100644
index 000000000..d8f4dd8d4
--- /dev/null
+++ b/app/src/main/res/layout/item_notification.xml
@@ -0,0 +1,70 @@
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/item_place.xml b/app/src/main/res/layout/item_place.xml
index 88784857b..1a87f9d43 100644
--- a/app/src/main/res/layout/item_place.xml
+++ b/app/src/main/res/layout/item_place.xml
@@ -10,9 +10,9 @@
android:id="@+id/icon"
android:layout_width="40dp"
android:layout_height="40dp"
- android:layout_marginLeft="16dp"
- android:layout_marginStart="16dp"
- android:layout_marginTop="16dp"
+ android:layout_marginLeft="@dimen/standard_gap"
+ android:layout_marginStart="@dimen/standard_gap"
+ android:layout_marginTop="@dimen/standard_gap"
android:background="@android:color/white"
android:contentDescription="@string/no_image_found"
android:scaleType="centerCrop"
@@ -25,9 +25,9 @@
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:layout_alignParentRight="true"
- android:layout_marginLeft="16dp"
- android:layout_marginRight="16dp"
- android:layout_marginTop="16dp"
+ android:layout_marginLeft="@dimen/standard_gap"
+ android:layout_marginRight="@dimen/standard_gap"
+ android:layout_marginTop="@dimen/standard_gap"
android:textAppearance="@style/TextAppearance.AppCompat.Caption"
tools:text="@string/placeholder_place_distance"
/>
@@ -37,8 +37,8 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignTop="@id/distance"
- android:layout_marginLeft="16dp"
- android:layout_marginStart="16dp"
+ android:layout_marginLeft="@dimen/standard_gap"
+ android:layout_marginStart="@dimen/standard_gap"
android:layout_toEndOf="@id/icon"
android:layout_toLeftOf="@id/distance"
android:layout_toRightOf="@id/icon"
@@ -58,7 +58,7 @@
android:layout_alignRight="@id/distance"
android:layout_alignStart="@id/tvName"
android:layout_below="@id/tvName"
- android:layout_marginBottom="16dp"
+ android:layout_marginBottom="@dimen/standard_gap"
android:ellipsize="end"
android:maxLines="4"
android:textAppearance="@style/TextAppearance.AppCompat.Body1"
diff --git a/app/src/main/res/layout/layout_categories_item.xml b/app/src/main/res/layout/layout_categories_item.xml
index f9b84fb73..d301e9098 100644
--- a/app/src/main/res/layout/layout_categories_item.xml
+++ b/app/src/main/res/layout/layout_categories_item.xml
@@ -6,7 +6,7 @@
android:checkMark="?android:attr/textCheckMark"
android:checked="false"
android:gravity="center_vertical"
- android:padding="4dp"
+ android:padding="@dimen/tiny_gap"
android:theme="@style/DarkAppTheme">
\ No newline at end of file
diff --git a/app/src/main/res/layout/layout_contribution.xml b/app/src/main/res/layout/layout_contribution.xml
index 2baf33554..87e7fe9e3 100644
--- a/app/src/main/res/layout/layout_contribution.xml
+++ b/app/src/main/res/layout/layout_contribution.xml
@@ -31,7 +31,7 @@
android:layout_gravity="center|bottom"
android:background="#AA000000"
android:orientation="vertical"
- android:padding="8dp"
+ android:padding="@dimen/small_gap"
>
@@ -62,20 +60,21 @@
android:text="@string/tutorial_2_text"
android:layout_gravity="center"
android:textStyle="bold"
+ android:textSize="@dimen/normal_text"
android:textAlignment="center"
- android:paddingTop="24dp"
+ android:paddingTop="@dimen/standard_gap"
android:gravity="center_horizontal"
android:textColor="@android:color/white"
/>
diff --git a/app/src/main/res/layout/welcome_dont_upload.xml b/app/src/main/res/layout/welcome_dont_upload.xml
index de748ce68..8d352547e 100644
--- a/app/src/main/res/layout/welcome_dont_upload.xml
+++ b/app/src/main/res/layout/welcome_dont_upload.xml
@@ -9,10 +9,9 @@
diff --git a/app/src/main/res/layout/welcome_final.xml b/app/src/main/res/layout/welcome_final.xml
index 175ddda64..7f323253b 100644
--- a/app/src/main/res/layout/welcome_final.xml
+++ b/app/src/main/res/layout/welcome_final.xml
@@ -9,10 +9,9 @@
@@ -29,7 +27,8 @@
android:layout_width="160dp"
android:layout_height="120dp"
android:layout_gravity="center"
- android:layout_marginLeft="10dp"
+ android:layout_marginLeft="@dimen/standard_gap"
+ android:layout_marginStart="@dimen/standard_gap"
android:contentDescription="@string/welcome_image_welcome_copyright"
/>
@@ -42,6 +41,7 @@
android:text="@string/welcome_final_text"
android:layout_gravity="center"
android:textStyle="bold"
+ android:textSize="@dimen/normal_text"
android:textAlignment="center"
android:gravity="center_horizontal"
android:textColor="@android:color/white"
@@ -49,10 +49,11 @@
@@ -38,7 +39,7 @@
android:text="@string/tutorial_4_subtext"
android:layout_gravity="center"
android:textAlignment="textStart"
- android:paddingTop="16dp"
+ android:paddingTop="@dimen/standard_gap"
android:gravity="start"
android:textColor="@android:color/white"
/>
diff --git a/app/src/main/res/layout/welcome_wikipedia.xml b/app/src/main/res/layout/welcome_wikipedia.xml
index ea0b6a92a..ece52628e 100644
--- a/app/src/main/res/layout/welcome_wikipedia.xml
+++ b/app/src/main/res/layout/welcome_wikipedia.xml
@@ -24,7 +24,8 @@
android:layout_gravity="center"
android:textStyle="bold"
android:textAlignment="center"
- android:paddingTop="24dp"
+ android:textSize="@dimen/normal_text"
+ android:paddingTop="@dimen/standard_gap"
android:gravity="center_horizontal"
android:textColor="@android:color/white"
/>
@@ -36,7 +37,7 @@
android:text="@string/tutorial_1_subtext"
android:layout_gravity="center"
android:textAlignment="center"
- android:paddingTop="16dp"
+ android:paddingTop="@dimen/standard_gap"
android:gravity="center_horizontal"
android:textColor="@android:color/white"
/>
diff --git a/app/src/main/res/menu/drawer.xml b/app/src/main/res/menu/drawer.xml
index f0a0a5e29..83c1bf0ad 100644
--- a/app/src/main/res/menu/drawer.xml
+++ b/app/src/main/res/menu/drawer.xml
@@ -35,4 +35,9 @@
android:icon="@drawable/ic_exit_to_app_black_24dp"
android:title="@string/navigation_item_logout"/>
+
+
diff --git a/app/src/main/res/values-ab/error.xml b/app/src/main/res/values-ab/error.xml
new file mode 100644
index 000000000..80c41ae0f
--- /dev/null
+++ b/app/src/main/res/values-ab/error.xml
@@ -0,0 +1,5 @@
+
+
+ Аиҧҟьара
+ Иҭабуп!
+
diff --git a/app/src/main/res/values-ab/strings.xml b/app/src/main/res/values-ab/strings.xml
new file mode 100644
index 000000000..326ee585f
--- /dev/null
+++ b/app/src/main/res/values-ab/strings.xml
@@ -0,0 +1,105 @@
+
+
+ Архиарақәа
+ Ахархәаҩ ихьӡ
+ Ажәамаӡа
+ Аҭаларҭа
+ Иҟаҵатәуп арегистрациа
+ Асистемахь аҭаларҭа
+ Шәааҧшы ҧыҭрак...
+ Аҭалара қәҿиарала имҩаҧысит!
+ Асистемахь аҭалараан агха!
+ Афаил ҧшаам. Даҽа фаилк шәахәаҧш.
+ Аутентификациа агха!
+ Аҭагалара иалагоуп!
+ %1$s иҭагалоуп!
+ Шәақәыӷәӷәа иҭагалоу афаил ахәаҧшраз
+ Аҭагалара %1$s иалагоуп
+ %1$s иаҿуп аҭагалара
+ Аҭагалара ахыркәшамҭа %1$s
+ Аҭагалара %1$s амуӡеит
+ Шәақәыӷәӷәа ахәаҧшразы
+ Аҭагалара агха.
+ Инагӡоуп %1$d%%
+ Аҭагалара
+ Агалереиа аҟынтәи
+ Афото ҭыхтәуп
+ Сара сҭагаламҭақәа
+ Иаарттәуп абраузер аҟны
+ Ахьӡ
+ Ахҳәаа
+ Асистемахь аҭалара агха!
+ Аҭагалара
+ Аҧсахрақәа
+ Аҭагалара
+ Актегориа алхра
+ Еиқәырхатәуп
+ Ирҿыцтәуп
+ GPS аҿыхуп шәара шәҟны. Ишәҭахума иаҿашәкыр?
+ Иаҿактәуп GPS
+ Аҭагаламҭақәа макьана иҟам
+ Акатегориақәа
+ Архиарақәа
+ Арегистрациа ҟаҵатәуп
+ Еиҭаҟаҵатәуп
+ Аҟәыхра
+ Иҭыгатәуп
+ Алицензиа
+ Уахынлатәи арежим
+ Ихархәатәуп еиқәаҵәоу атема
+ Attribution-ShareAlike 4.0
+ Attribution 4.0
+ Attribution-ShareAlike 3.0
+ Attribution 3.0
+ CC0
+ CC BY-SA 3.0
+ CC BY-SA 3.0 (Австриа)
+ CC BY-SA 3.0 (Германиа)
+ CC BY-SA 3.0 (Естониа)
+ CC BY-SA 3.0 (Испаниа)
+ CC BY-SA 3.0 (Хорватиа)
+ CC-BY-SA 3.0 (Лиуксембург)
+ CC-BY-SA 3.0 (Нидерланды)
+ CC BY-SA 3.0 (Норвегиа)
+ CC BY-SA 3.0 (Польша)
+ CC BY-SA 3.0 (Румыниа)
+ CC BY 3.0
+ CC BY-SA 4.0
+ CC BY 4.0
+ CC Zero
+ Аҭагалара аҿырҧштәы:
+ Ари шәара еилышәкаама?
+ Ааи!
+ Акатегориақәа
+ Аҭагалара...
+ Акагь алхӡам
+ Иҟам ахҳәаа
+ Идырым алицензиа
+ Ирҿыцтәуп
+ OK
+ Агәаҽанҵара
+ Ааи
+ Мап
+ Ахьӡ
+ Ахҳәаа
+ Алицензиа
+ Акоординатқәа
+ 2ФА Акод
+ Имаксималу алимит
+ Иауам 500 иреиҳаны раарҧшра
+ Аҿаҧшыратә сахьа
+ Асахьа ҧшаам
+ Иҭагалатәуп асахьа
+ Ашьха Зао
+ Атиульпан
+ Аселфи ыҟаӡам
+ Бзиала шәаабеит Википедиа ахь
+ Сиднеи аопера атеатр
+ Аҟәыхра
+ Иаарттәуп
+ Иарктәуп
+ Иҭагалатәуп
+ Архиарақәа
+ Иҭыҵтәуп
+ ахҳаа ҧшаам
+
diff --git a/app/src/main/res/values-ar/strings.xml b/app/src/main/res/values-ar/strings.xml
index 58da29f0d..7ce350f66 100644
--- a/app/src/main/res/values-ar/strings.xml
+++ b/app/src/main/res/values-ar/strings.xml
@@ -120,5 +120,6 @@
حولالإعداداتتسجيل الخروج
+ إشعاراتصفحة ملف كومنز
diff --git a/app/src/main/res/values-ast/strings.xml b/app/src/main/res/values-ast/strings.xml
index af7db5bcd..e766151bb 100644
--- a/app/src/main/res/values-ast/strings.xml
+++ b/app/src/main/res/values-ast/strings.xml
@@ -121,7 +121,7 @@
NUN xubas:- Selfies o semeyes de los tos amigos\n- Imáxenes que descargasti d\'Internet\n- Imáxenes de pantalla d\'aplicaciones propietariesExemplu de carga:
- - Títulu: Ópera de Sidney\n- Descripción: Teatru de la Ópera de Sidney vistu dende l\'otru llau de la badea\n- Categoríes: Ópera de Sidney, Ópera de Sidney dende l\'oeste, Vistes a distancia de la Ópera de Sidney
+ - Títulu: Ópera de Sidney\n- Descripción: Teatru de la Ópera de Sidney vistu dende l\'otru llau de la badea\n- Categoríes: Ópera de Sidney dende l\'oeste, Vistes a distancia de la Ópera de SidneyCollabore coles sos imaxes. ¡Ayude a que los artículos de Wikipedia tengan vida!Les imaxes de Wikipedia vienen de\nWikimedia Commons.Les sos imaxes ayuden a educar a la xente alredor del mundu.
@@ -188,6 +188,7 @@
La to opiniónSalirTutorial
+ AvisosLos sitios cercanos nun pueden amosase ensin los permisos d\'allugamientunun s\'atoparon descripcionesPáxina del ficheru en Commons
@@ -204,4 +205,6 @@
L\'allugamientu nun camudó.L\'allugamientu nun ta disponible.Ríquese permisu p\'amosar una llista de llugares cercanos
+ CÓMO LLEGAR
+ LLEER L\'ARTÍCULU
diff --git a/app/src/main/res/values-b+sr+Latn/error.xml b/app/src/main/res/values-b+sr+Latn/error.xml
new file mode 100644
index 000000000..80d668c8b
--- /dev/null
+++ b/app/src/main/res/values-b+sr+Latn/error.xml
@@ -0,0 +1,7 @@
+
+
+ Ostava se srušila
+ Ups! Nešto je pošlo naopako.
+ Recite nam šta ste radili pa to saznanje podelite s nama, putem e-pošte. Time ćete nam pomoći da rešimo problem!
+ Hvala vam!
+
diff --git a/app/src/main/res/values-b+sr+Latn/strings.xml b/app/src/main/res/values-b+sr+Latn/strings.xml
new file mode 100644
index 000000000..3cf62f69c
--- /dev/null
+++ b/app/src/main/res/values-b+sr+Latn/strings.xml
@@ -0,0 +1,208 @@
+
+
+ Ostava
+ Podešavanja
+ Korisničko ime
+ Lozinka
+ Prijavi me
+ Otvori nalog
+ Prijavljivanje
+ Sačekajte…
+ Uspešno ste prijavljeni.
+ Prijavljivanje nije uspelo.
+ Datoteka nije pronađena. Pokušajte sa drugom datotekom.
+ Provera identiteta nije uspela.
+ Otpremanje je započeto.
+ Datoteka „%1$s“ je otpremljena.
+ Tapnite da biste videli otpremanje
+ Počinjem sa otpremanjem datoteke „%1$s“
+ Otpremanje datoteke „%1$s“
+ Završavam sa otpremanjem datoteke „%1$s“
+ Ne mogu da otpremim „%1$s“
+ Tapnite da biste videli
+
+ %d datoteka se otprema
+ %d datoteke se otpremaju
+
+ Moja skorašnja otpremanja
+ Na čekanju
+ Nije uspelo
+ %1$d%% otpremljeno
+ Otpremam
+ Iz galerije
+ Fotografiši
+ U blizini
+ Moja otpremanja
+ Deli
+ Otvori u pregledaču
+ Naslov
+ Opis
+ Ne mogu da vas prijavim – mreža ne radi
+ Ne mogu da vas prijavim – proverite svoje korisničko ime
+ Ne mogu da vas prijavim – proverite svoju lozinku
+ Previše neuspešnih pokušaja. Probajte ponovo za nekoliko minuta.
+ Nažalost, korisnik je blokiran na Ostavi
+ Morate uneti svoj dvofaktorski kod za autentifikaciju.
+ Prijava nije uspela
+ Otpremi
+ Dajte ime ovom kompletu
+ Izmene
+ Otpremi
+ Pretraži kategorije
+ Sačuvaj
+ Osveži
+ GPS je onemogućen na Vašem uređaju. Želite li ga omogućiti?
+ Omogući GPS
+ Još uvek nema otpremanja
+
+ \@string/contributions_subtitle_zero
+ %d otpremanje
+ %d otpremanja
+
+
+ Započni %d otpremanje
+ Započni %d otpremanja
+
+
+ %d otpremanje
+ %d otpremanja
+
+ Nema kategorija koje odgovaraju %1$s
+ Dodajte kategorije na slike da biste olakšali korisnicima njihovo pronalaženje na Ostavi.\n\nDa biste dodali kategoriju, počnite sa pisanjem njenog imena.
+ Kategorije
+ Postavke
+ Otvori nalog
+ O aplikaciji
+ Softver otvorenog koda dostupan pod licencom <a href=\"https://github.com/commons-app/apps-android-commons/blob/master/COPYING\">Apache ver. 2</a> Vikimedijina Ostava i njen logo su zaštitni znaci Vikimedijine Fondacije i koriste se sa dozvolom Vikimedijine Fondacine. Mi ne odobravamo ili podržavmo Vikimedijinu Fondaciju.\n\nAplikacija za Vikimedijinu ostavu je aplikacija otvorenog koda koja je napravljena i koja se održava pomoću grantova i volontera Vikimedijine zajednice. Zadužbina Vikimedija nije uključena u stvaranje, razvoj ili održavanje aplikacije.
+ <a href=\"https://github.com/commons-app/apps-android-commons\">Izvorni kôd</a> i <a href=\"https://commons-app.github.io/\">veb-sajt</a> na GitHub-u. Napravite novi <a href=\"https://github.com/commons-app/apps-android-commons/issues\">zahtev na GitHub-u</a> da biste prijavili greške ili dali predloge.
+ <a href=\"https://github.com/commons-app/apps-android-commons/wiki/Privacy-policy\">Politika privatnosti</a>
+ <a href=\"https://github.com/commons-app/apps-android-commons/blob/master/CREDITS\">Zasluge</a>
+ O aplikaciji
+ Pošaljite povratne informacije (putem e-pošte)
+ Nije instaliran imejl klijent
+ Nedavno korišćene kategorije
+ Čekam na prvu sinhronizaciju…
+ Još niste otpremili nijednu fotografiju.
+ Pokušaj ponovo
+ Otkaži
+ Slika će se voditi pod licencom %1$s
+ Slanjem ove slike, ja tvrdim da je u pitanju moj rad, da ne sadrži materijal ili selfije zaštićene autorskim pravima, te da je na ostale načine u skladu sa <a href=\"https://commons.wikimedia.org/wiki/Commons:Policies_and_guidelines\">smernicama Vikimedijine ostave</a>.
+ Preuzmi
+ Licenca
+ Koristi prethodan naslov/opis
+ Automatski detektuj trenutnu lokaciju
+ Primi trenutnu lokaciju da bi predložili kategoriju ako slika nije geografski označena
+ Noćni režim
+ Koristiti tamnu temu
+ Autorstvo-Deliti pod istim uslovima 4.0
+ Autorstvo 4.0
+ Autorstvo-Deliti pod istim uslovimau 3.0
+ Autorstvo 3.0
+ CC0
+ CC BY-SA 3.0
+ CC BY-SA 3.0 (Austrija)
+ CC BY-SA 3.0 (Nemačka)
+ CC BY-SA 3.0 (Estonija)
+ CC BY-SA 3.0 (Španija)
+ CC BY-SA 3.0 (Hrvatska)
+ CC BY-SA 3.0 (Luksemburg)
+ CC BY-SA 3.0 (Holandija)
+ CC BY-SA 3.0 (Norveška)
+ CC BY-SA 3.0 (Poljska)
+ CC BY-SA 3.0 (Rumunija)
+ CC BY 3.0
+ CC BY-SA 4.0
+ CC BY 4.0
+ CC Nula
+ Vikimedijina Ostava sadrži većinu slika koja se koristi na Vikipediji.
+ Vaše slike pomažu u obrazovanju ljudi širom sveta.
+ Molimo Vas da postavite slike koje ste preuzeli ili kreirali u potpunosti sami:
+ - Prirodne objekte (cveće, životinje, planine)\n- Korisne objekte (bicikle, železničke stanice)\n- Poznate ljude (vaš gradonačelnik, Olimpijce koje ste sreli)
+ Molimo NE otpremajte:
+ - Selfije i slike tvojih prijatelja\n- Slike koje ste preuzeli sa interneta\n- Skrinšotove iz sopstvenih aplikacija
+ Primer otpremanja:
+ — Naslov: Sidnejska opera\n— Opis: Sidnejska opera, pogled preko zaliva\n— Kategorije: Sidnejska opera sa zapada, pogledi na Sidnejksu operu iz daljine
+ Delite svoje slike. Oživite članke na Vikipediji!
+ Slike na Vikipediji dolaze iz Ostave.
+ Sa vašim slikama pomažete u obrazovanju ljudi širom sveta.
+ Izbegavajte materijale koje ste našli na internetu, kao i slike plakata, korica knjiga itd.
+ Jeste li razumeli?
+ Jesam!
+ Kategorije
+ Učitavam…
+ Ništa nije izabrano
+ Nema opisa
+ Nepoznata licenca
+ Osveži
+ Potrebna dozvola: Provera spoljašnje memorije. Aplikacija bez ovoga ne može da funkcioniše.
+ Neophodna dozvola: Pisanje spoljašnjeg skladišta. Aplikacija ne može da funkcioniše bez ovoga.
+ Opciona dozvola: Preuzmi trenutnu lokaciju za predloge kategorija
+ U redu
+ Mesta u blizini
+ Nisu pronađena obližnja mesta
+ Upozorenje
+ Ova datoteka je već dostupna na Ostavi. Da li ste sigurni da želite da nastavite?
+ Da
+ Ne
+ Naslov
+ Naslov medija
+ Opis
+ Opis datoteke ide ovde. Može da bude poprilično dug i prikazivaće se u više redova. Nadamo se da će izgledati lepo.
+ Datum otpremanja
+ Licenca
+ Koordinate
+ Ništa nije uneto
+ Postani Beta Tester
+ Priključite se na naš beta kanal na Gugl pleju i pristupajte novim informacijama i popravkama bagova
+ Koristi Vikipodatke
+ (Upozorenje: onemogućavanjem ovoga može se izazvati velika potrošnja mobilnih podataka)
+ 2FA kod
+ Moj limit za skorašnja otpremanja
+ Maksimalni limit
+ Nije moguće prikazati više od 500
+ Postavi limit za skorašnja otpremanja
+ Dvofaktorska autentifikacija trenutno nije podržana.
+ Zaista želite da se odjavite?
+ Logo Ostave
+ Pozadinska slika
+ Medijska slika neuspešna
+ Slika nije pronađena
+ Otpremi sliku
+ Planina Zao
+ Lame
+ Dugin most
+ Tulipan
+ Bez selfija
+ Vlasnička slika
+ Dobrodošlica Vikipediji
+ Dobrodošlica za autorska prava
+ Sidnejska opera
+ Otkaži
+ Otvori
+ Zatvori
+ Početna
+ Otpremanje
+ U blizini
+ O nama
+ Podešavanja
+ Povratne informacije
+ Odjavi me
+ Tutorijal
+ Obaveštenja
+ Obližnja mesta ne mogu da se prikazuju bez dozvola za lokaciju
+ opis nije pronađen
+ Stranica datoteke na Ostavi
+ Stavka na Vikipodacima
+ Greška pri keširanju slika
+ Jedinstven opisni naslov za datoteku, koji će biti ime datoteke. Možete da koristite obični jezik sa razmacima. Ne treba unositi ekstenziju datoteke
+ Molimo da opišete datoteku koliko je to moguće: Gde je napravljena? Šta prikazuje? Šta je kontekst? Opišite objekte i/ili osobe. Otkrijte informacije koje se ne mogu lako pogoditi, na primer doba dana ako je u pitanju pejzaž. Ako datoteka prikazuje nešto neobično, molimo da objasnite šta je to čini neobičnom.
+ Davanje dozvole
+ Upotreba spoljašnjeg skladišta
+ Spremanje slika napravljenih kamerom aplikacije na Vašem uređaju
+ Pošalji dnevničku datoteku
+ Pošalji dnevničku datoteku developerima preko imejla
+ Prijavite se na svoj nalog
+ Lokacija nije promenjena.
+ Lokacija nije dostupna.
+ Potrebna je dozvola za prikazivanje liste lokacija u blizini
+
diff --git a/app/src/main/res/values-bn/strings.xml b/app/src/main/res/values-bn/strings.xml
index e267c3a9a..73a5baa9d 100644
--- a/app/src/main/res/values-bn/strings.xml
+++ b/app/src/main/res/values-bn/strings.xml
@@ -121,7 +121,7 @@
দয়া করে আপলোড করবেন না:u2022 আপনার বন্ধুর সেলফি বা ছবি \nu2022 ইন্টারনেট থেকে ডাউনলোড করা ছবি \nu2022 মালিকানাযুক্ত অ্যাপসমূহের স্ক্রীনশটআপলোডের উদাহরণ:
- u2022 শিরোনাম: সিডনি অপেরা হাউস \nu2022 বিবরণ: উপসাগর থেকে দেখা সিডনি অপেরা হাউস\nu2022 বিষয়শ্রেণী: সিডনি অপেরা হাউস, পশ্চিম দিক থেকে সিডনি অপেরা হাউস, সিডনি অপেরা হাউসের দূরবর্তী দৃশ্য
+ u2022 শিরোনাম: সিডনি অপেরা হাউস \nu2022 বিবরণ: উপসাগর থেকে দেখা সিডনি অপেরা হাউস\nu2022 বিষয়শ্রেণী: সিডনি অপেরা হাউস, পশ্চিম দিক থেকে সিডনি অপেরা হাউস, সিডনি অপেরা হাউসের দূরবর্তী দৃশ্যআপনার ছবি দিয়ে অবদান রাখুন। উইকিপিডিয়ার নিবন্ধগুলিকে নতুন রূপ দিতে সাহায্য করুন!উইকিপিডিয়াতে চিত্র উইকিমিডিয়া কমন্স থেকে এসেছে।আপনার ছবি সারা বিশ্বের শিক্ষিত মানুষকে সাহায্য করবে।
@@ -204,4 +204,6 @@
অবস্থান পরিবর্তন হয়নি।অবস্থান উপলব্ধ নয়।কাছাকাছি স্থানসমূহের একটি তালিকা প্রদর্শন করতে অনুমতি প্রয়োজন
+ নির্দেশনা পান
+ নিবন্ধ পড়ুন
diff --git a/app/src/main/res/values-br/strings.xml b/app/src/main/res/values-br/strings.xml
index 6c9a7ba68..bdc554bc1 100644
--- a/app/src/main/res/values-br/strings.xml
+++ b/app/src/main/res/values-br/strings.xml
@@ -68,7 +68,7 @@
%d pellgargadennoùN\'eus bet kavet rummad ebet o klotañ gant %1$s
- Ouzhpennit rummadoù evit ma vo aesoc\'h kavout ho skeudennoù war Wikimedia Commons.\n\nKrogit da ouzhpennañ rummadoù.\nPouezit war ar c\'hemenn-mañ (pe distroit en a-raok) evit mont dreist ar bazenn-mañ.
+ Ouzhpennit rummadoù evit ma vo aesoc\'h kavout ho skeudennoù war Wikimedia Commons.\n\nKrogit da skrivañ evit ouzhpennañ rummadoù.RummadoùArventennoùEn em enskrivañ
@@ -120,7 +120,7 @@
ARABAT enporzhiañ :- Emskeudennoù pe skeudennoù eus ho mignoned\n- Skeudennoù pellgarget ganeoc\'h diwar an Internet\n- Tapadennoù-skramm arloadoù prevezSkouer un enporzhiadenn :
- - Titl : Ti opera Sydney\n- Desskrivadur : Ti opera Sydney gwelet eus tu all ar bae\n- Rummadoù : Ti opera Sydney, Ti opera Sydney gwelet eus ar c\'hornôg, Gweloù a-bell eus ti opera Sydney
+ - Titl : Ti opera Sydney\n- Desskrivadur : Ti opera Sydney gwelet eus tu all ar bae\n- Rummadoù : Ti opera Sydney, Ti opera Sydney gwelet eus ar c\'hornôg, Gweloù a-bell eus ti opera SydneyKemerit perzh gant ho skeudennoù. Sikourit pennadoù Wikipedia da zont da vev !Dont a ra skeudennoù Wikipedia eus Wikimedia Commons.Ho skeudennoù a sikour an dud da zeskiñ traoù dre ar bed a-bezh.
@@ -182,8 +182,11 @@
EvezhiadennoùDigevreañTutorial
+ KemennoùNe c\'haller ket diskwel al lec\'hioù tost ma ne rannit ket ho lec\'hiadurN\'eus bet kavet deskrivadur ebetPennad CommonsElfenn Wikidata
+ Kaout urzhioù
+ Lenn ar pennad
diff --git a/app/src/main/res/values-bs/strings.xml b/app/src/main/res/values-bs/strings.xml
index b587b2546..fccedb33e 100644
--- a/app/src/main/res/values-bs/strings.xml
+++ b/app/src/main/res/values-bs/strings.xml
@@ -120,7 +120,7 @@
NE postavljajte:– Vlastite slike ili slike svojih prijatelja\n– Slike koje ste preuzeli s interneta\n– Slike ekrana neslobodnih aplikacijaPrimjer postavljanja:
- – Naziv: Sidnejska opera\n– Opis: Pogled na Sidnejsku operu sa zaliva\n– Kategorije: Sidnejska opera, Sidnejska opera sa zapada, Sidnejska opera iz daleka
+ – Naziv: Sidnejska opera\n– Opis: Pogled na Sidnejsku operu sa zaliva\n– Kategorije: Sidnejska opera, Sidnejska opera sa zapada, Sidnejska opera iz dalekaPodijelite vaše slike. Pomozite člancima na Wikipediji da zažive!Slike na Wikipediji se uzimaju sa Wikimedia Commonsa.Vaše slike pomažu u obrazovanju ljudi širom svijeta.
diff --git a/app/src/main/res/values-ca/strings.xml b/app/src/main/res/values-ca/strings.xml
index f0e946d90..bf1c2467d 100644
--- a/app/src/main/res/values-ca/strings.xml
+++ b/app/src/main/res/values-ca/strings.xml
@@ -68,7 +68,7 @@
%d càrreguesNo s\'ha trobat cap categoria que coincideixi amb %1$s
- Afegiu categories per fer que les vostres imatges siguin més fàcils de trobar a Wikimedia Commons.
+ Afegiu categories per fer que les vostres imatges siguin més fàcils de trobar a Wikimedia Commons.CategoriesParàmetresRegistre
@@ -87,6 +87,7 @@
Aquesta imatge es llicenciarà sota %1$sBaixaLlicència
+ Utilitza títol i descripció anteriorsMode nocturnUtilitza el tema foscReconeixement-CompartirIgual 4.0
@@ -146,6 +147,8 @@
Carrega la imatgeLlamesImatge privativa
+ Benvinguts a la Viquipèdia
+ Casa d\'Òpera de SydneyCancel·laObreTanca
@@ -156,7 +159,19 @@
ParàmetresComentarisFinalitza la sessió
+ Tutorial
+ Notificacionsno s\'ha trobat cap descripcióArticle del fitxer a CommonsElement del Wikidata
+ Error mentre es carregaven les fotografies
+ Dóna permís
+ Utilitza l’emmagatzematge extern
+ Envia log arxiu
+ Envia log a desenvolupadors via correu electrònic
+ Login al vostre compte
+ La ubicació no ha canviat.
+ La ubicació no és disponible.
+ COM ANAR-HI
+ LLEGIU L’ARTICLE
diff --git a/app/src/main/res/values-cs/strings.xml b/app/src/main/res/values-cs/strings.xml
index 570afa99d..5d576636c 100644
--- a/app/src/main/res/values-cs/strings.xml
+++ b/app/src/main/res/values-cs/strings.xml
@@ -68,7 +68,7 @@
%d nahráváníŽádné kategorie neodpovídají „%1$s“
- Přidejte kategorie, aby bylo vaše obrázky možno na Wikimedia Commons najít.
+ Přidejte kategorie, aby bylo vaše obrázky možno na Wikimedia Commons najít.KategorieNastaveníZaregistrovat se
@@ -120,7 +120,7 @@
Prosím NENAHRÁVEJTE:- Selfie nebo obrázky vašich přátel\n- Obrázky stažené z Internetu\n- Screenshoty z proprietárních aplikacíPříklad nahraného souboru:
- - Název: Opera v Sydney\n- Popis: Opera v Sydney, pohled přes záliv\n- Kategorie: Sydney Opera House, Sydney Opera House from the west, Sydney Opera House remote views
+ - Název: Opera v Sydney\n- Popis: Opera v Sydney, pohled přes záliv\n- Kategorie: Sydney Opera House, Sydney Opera House from the west, Sydney Opera House remote viewsPřispějte svými obrázky. Pomozte oživit články na Wikipedii.Obrázky na Wikipedii pochází \nz Wikimedia Commons.Vaše obrázky pomáhají vzdělávat lidi po celém světě.
@@ -147,6 +147,8 @@
PopisSem patří popis média. Může být potenciálně velmi dlouhý, takže zabere několik řádků. Přesto doufáme, že to vypadá hezky.Datum nahrání souboru
+ Licence
+ SouřadniceStaňte se beta testeryPřihlásit se do našeho beta kanálu na Google Play a dostávat včasný přístup k novinkám a opravám chybPoužít Wikidata
@@ -160,6 +162,7 @@
Opravdu se chcete odhlásit?Logo Wikimedia CommonsObrázek na pozadí
+ ObrázekNebyl nalezen žádný obrázekNahrát obrázekHora Zao
@@ -186,4 +189,12 @@
nebyl nalezen žádný popisekStránka souboru na CommonsPoložka Wikidat
+ Dát povolení
+ Odeslat log
+ Odeslat log vývojářům e-mailem
+ Přihlásit se k účtu
+ Vaše umístění se nezměnilo.
+ Umístění není dostupné.
+ NAVIGOVAT
+ PŘEČÍST ČLÁNEK
diff --git a/app/src/main/res/values-da/strings.xml b/app/src/main/res/values-da/strings.xml
index 02ef6ced7..bd736ac8a 100644
--- a/app/src/main/res/values-da/strings.xml
+++ b/app/src/main/res/values-da/strings.xml
@@ -68,7 +68,7 @@
%d overførslerIngen kategorier matchende %1$s er fundet
- Tilføj kategorier for at gøre billederne mere synlig på Wikimedia Commons.
+ Tilføj kategorier for at gøre billederne mere synlig på Wikimedia Commons. Tast for at tilføje kategorier.KategorierIndstillingerOpret konto
@@ -121,7 +121,7 @@
Overfør venligst IKKE:- Selvportrætter eller billeder af dine venner\n- Billeder du har hentet fra internettet\n- Skærmbilleder af dine proprietære programmerEksempel på overførsel:
- - Titel: Sydneys Operahus\n- Beskrivelse: Operahuset i Sydney set fra anden siden af bugten\n- Kategorier: Operahuset i Sydney, Operahuset i Sydney fra vest, Operahuset i Sydney udefra
+ - Titel: Sydneys Operahus\n- Beskrivelse: Operahuset i Sydney set fra den anden siden af\n bugten\n- Kategorier: Operahuset i Sydney fra vest, Operahuset i Sydney\n udefraBidrag med dine billeder. Hjælp Wikipedias artikler med at komme til live!Billeder på Wikipedia kommer fra Wikimedia Commons.Dine billeder hjælper med til at uddanne folk rundt om i verden.
@@ -188,6 +188,7 @@
TilbagemeldingLog afØvelse
+ PåmindelserSteder i nærheden kan ikke vises uden placeringsrettighederingen beskrivelse fundetCommons-filside
@@ -204,4 +205,6 @@
Sted er ikke ændret.Sted ikke tilgængeligt.Tilladelse kræves for at vise en liste over steder i nærheden
+ FÅ RUTEHJÆLP
+ LÆS ARTIKEL
diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml
index d3c207184..01b70d9f8 100644
--- a/app/src/main/res/values-de/strings.xml
+++ b/app/src/main/res/values-de/strings.xml
@@ -121,7 +121,7 @@
Bitte NICHT hochladen:- Selfies oder Bilder deiner Freunde\n- Bilder, die du aus dem Internet heruntergeladen hast\n- Screenshots von proprietären SoftwareanwendungenBeispiel-Upload:
- - Titel: Opernhaus von Sydney\n- Beschreibung: Opernhaus von Sydney, von der Bucht aus gesehen\n- Kategorien: Opernhaus von Sydney, Opernhaus von Sydney von Westen, Opernhaus von Sydney (Fernansicht)
+ - Titel: Opernhaus von Sydney\n- Beschreibung: Opernhaus von Sydney, von der Bucht aus gesehen\n- Kategorien: Opernhaus von Sydney von Westen, Opernhaus von Sydney (Fernansicht)Teile deine Bilder. Erwecke Wikipedia-Artikel zum Leben!Die Bilder auf Wikipedia kommen von Wikimedia Commons.Deine Bilder helfen dabei, Menschen auf der ganzen Welt zu bilden.
@@ -188,6 +188,7 @@
RückmeldungenAbmeldenAnleitung
+ BenachrichtigungenOrte in der Nähe können ohne Berechtigung zur Standortbestimmung nicht ermittelt werdenKeine Beschreibung gefundenCommons-Dateiseite
@@ -204,4 +205,6 @@
Der Standort hat sich nicht geändert.Der Standort ist nicht verfügbar.Berechtigung zur Anzeige einer Liste mit Orten in der Nähe erforderlich
+ RICHTUNGEN ABRUFEN
+ ARTIKEL LESEN
diff --git a/app/src/main/res/values-el/strings.xml b/app/src/main/res/values-el/strings.xml
index a54f7772c..4bae05d77 100644
--- a/app/src/main/res/values-el/strings.xml
+++ b/app/src/main/res/values-el/strings.xml
@@ -54,21 +54,21 @@
Το GPS στην συσκευή είναι απενεργοποιημένο. Θέλετε να το ενεργοποιήσετε; Ενεργοποιήσετε το GPSΔεν υπάρχουν ακόμα φορτωμένα αρχεία
-
+ Δεν υπάρχουν επιφορτώσεις ακόμη1 επιφόρτωση%d επιφορτώσεις
-
+ Έναρξη 1 επιφόρτωσηςΈναρτξη %d επιφορτώσεων
-
+ 1 επιφόρτωση%d επιφορτώσειςΔεν βρέθηκαν κατηγορίες που ταιριάζουν %1$s
- Προσθέστε κατηγορίες για να κάνετε τις εικόνες σας πιο ανιχνεύσιμες στα Wikimedia Commons.\n\nΑρχίστε να γράφετε για να προσθέσετε κατηγορίες.\nΠατήστε αυτό το μήνυμα (ή πατήστε επιστροφή) για να παραλείψετε αυτό το βήμα.
+ Προσθέστε κατηγορίες για να κάνετε τις εικόνες σας πιο ανιχνεύσιμες στα Wikimedia Commons.\n\nΑρχίστε να γράφετε για να προσθέσετε κατηγορίες.\nΠατήστε αυτό το μήνυμα (ή πατήστε επιστροφή) για να παραλείψετε αυτό το βήμα.ΚατηγορίεςΡυθμίσειςΕγγραφή
@@ -76,7 +76,7 @@
Λογισμικό ανοικτού κωδικού που κυκλοφορεί υπό την <a href=\"https://github.com/commons-app/apps-android-commons/blob/master/COPYING\">Άδεια Apache v2</a>. Το Wikimedia Commons και το λογότυπο είναι εμπορικά σήματα του Ιδρύματος Wikimedia και χρησιμοποιούνται με άδεια από το Ίδρυμα Wikimedia. Δεν συμμετέχουμε στην δημιουργία, ανάπτυξη ή συντήρηση του Ιδρύματος Wikimedia.<a href=\"https://github.com/commons-app/apps-android-commons\">Πηγή</a> και <a href=\"https://commons-app.github.io/\">ιστοσελίδα</a> στο GitHub. Δημιουργήστε ένα νέο <a href=\"https://github.com/commons-app/apps-android-commons/issues\">GitHub θέμα</a> για αναφορές σφαλμάτων και προτάσεις.<a href=\"https://wikimediafoundation.org/wiki/Privacy_policy\">Πολιτική Απορρήτου και προσωπικών δεδομένων</a>
- <a href=\"https://github.com/commons-app/apps-android-commons/blob/master/CREDITS\">CREDITS</a>
+ <a href=\"https://github.com/commons-app/apps-android-commons/blob/master/CREDITS\">CREDITS</a>ΣχετικάΑποστολή σχολίων (μέσω Email)Δεν υπάρχει εγκατεστημένη εφαρμογή ηλεκτρονικού ταχυδρομείου
@@ -172,6 +172,7 @@
LlamasΓέφυρα Ουρανίου ΤόξουΤουλίπα
+ Όχι selfiesΙδιόκτητη Εικόνα Καλωσόρισες ΒικιπαίδειαΚαλωσορίστε το Δικαίωμα Αντιγραφής
@@ -187,6 +188,7 @@
ΣχόλιαΑποσύνδεσηΣεμινάριο
+ ΕνημερώσειςΟι κοντινές τοποθεσίες δεν μπορούν να προβληθούν δίχως τις άδειες τοποθεσίαςδεν βρέθηκε περιγραφήΣελίδα φακέλλου κοινής χρήσης
@@ -199,6 +201,10 @@
Αποθηκεύσετε εικόνες που παίρνονται στην κάμερα εφαρμογής στην συσκευή σαςΑποστείλατε τον φάκελλο σύνδεσηςΣτείλατε τον φάκελλο σύνδεσης στους δημιουργούς μέσω email
+ Συνδεθείτε στο λογαριασμό σας.Ο εντοπισμός δεν έχει αλλάξει.Ο τόπος δεν είναι διαθέσιμος.
+ Απαιτείται άδεια για την εμφάνιση λίστας κοντινών σημείων
+ Λάβετε κατευθύνσεις
+ Ανάγνωση άρθρου
diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml
index c30e5c70d..04e1c3979 100644
--- a/app/src/main/res/values-es/strings.xml
+++ b/app/src/main/res/values-es/strings.xml
@@ -121,7 +121,7 @@
NO cargues:- Autorretratos o fotos de tus amigos\n- Imágenes que hayas descargado de Internet\n- Capturas de pantalla de aplicaciones privativasEjemplo de carga:
- - Título: Casa de la Ópera de Sídney\n- Descripción: Casa de la Ópera de Sídney vista desde el otro lado de la bahía\n- Categorías: Casa de la Ópera de Sídney, Casa de la Ópera de Sídney desde el oeste, Vistas a distancia de la Casa de la Ópera de Sídney
+ - Título: Casa de la Ópera de Sídney\n- Descripción: Casa de la Ópera de Sídney vista desde el otro lado de la bahía\n- Categorías: Casa de la Ópera de Sídney desde el oeste, Vistas a distancia de la Casa de la Ópera de SídneyContribuye con tus imágenes.\n¡Ayuda a que los artículos de Wikipedia tengan vida!Las imágenes en Wikipedia proceden de\nWikimedia Commons.Tus imágenes ayudan a educar a la gente\nalrededor del mundo.
@@ -147,13 +147,13 @@
TítuloTítulo del multimediaDescripción
- Aquí va la descripción del multimedia. Potencialmente, puede ser bastante largo, y deberá agruparse en múltiples líneas. De todas formas, esperamos que se ve bien.
+ Aquí va la descripción del archivo multimedia. Esta puede ser muy extensa, en cuyo caso deberá ajustarse en varios renglones. No obstante, esperamos que se vea bien.Fecha de subidaLicenciaCoordenadasNo se proporcionaronPrueba la versión beta
- Opta por nuestro canal beta en Google Play y obtén acceso a funcionalidades nuevas y correcciones de errores
+ Apúntate a nuestro canal beta en Google Play y obtén acceso a funcionalidades nuevas y correcciones de erroresUtilizar Wikidata(Advertencia: desactivar esto puede ocasionar un gran consumo de datos del móvil)Código de autenticación de 2 pasos
@@ -188,6 +188,7 @@
ComentariosSalirTutorial
+ NotificacionesLos sitios cercanos no pueden mostrarse sin los permisos de ubicaciónno se encontró ninguna descripciónPágina del archivo en Commons
@@ -204,4 +205,6 @@
La ubicación no ha cambiado.La ubicación no está disponible.Se necesita permiso para mostrar una lista de lugares cercanos
+ CÓMO LLEGAR
+ LEER ARTÍCULO
diff --git a/app/src/main/res/values-eu/strings.xml b/app/src/main/res/values-eu/strings.xml
index da3e28e5d..348a9eb12 100644
--- a/app/src/main/res/values-eu/strings.xml
+++ b/app/src/main/res/values-eu/strings.xml
@@ -155,4 +155,6 @@
Artxibo orrialde komunaWikidata itemaArgazkiak hartzerakoan sortutako akatsa
+ NORABIDEAK JASO
+ IRAKURRI ARTIKULUA
diff --git a/app/src/main/res/values-fa/strings.xml b/app/src/main/res/values-fa/strings.xml
index bcd8e5ce2..109a84770 100644
--- a/app/src/main/res/values-fa/strings.xml
+++ b/app/src/main/res/values-fa/strings.xml
@@ -121,7 +121,7 @@
لطفاً بارگذاری نکنید:-سلفی خودتان یا تصویر دوستانتان\n-تصاویری که از اینترنت دانلود کردید\n-نماگرفت از دیگر اپلیکیشنهانمونه بارگذاری:
- -عنوان: خانهٔ اپرای سیدنی\n-توضیحات: خانهٔ اپرای سیدنی از آن طرف خلیج\n-ردهها: Sydney Opera House, Sydney Opera House from the west, Sydney Opera House remote views
+ -عنوان: خانهٔ اپرای سیدنی\n-توضیحات: خانهٔ اپرای سیدنی از آن طرف خلیج\n-ردهها: Sydney Opera House, Sydney Opera House from the west, Sydney Opera House remote viewsعکسهای خود را به اشتراک بگذارید. به ویکیپدیا کمک کنید تا مقالاتش زنده شوند!ویکیپدیا از تصویرهای ویکیانبار استفاده میکند.تصویرهای شما به مطالعهٔ مردم در سراسر دنیا کمک میکنند.
diff --git a/app/src/main/res/values-fi/strings.xml b/app/src/main/res/values-fi/strings.xml
index 85037fc61..cce5abc46 100644
--- a/app/src/main/res/values-fi/strings.xml
+++ b/app/src/main/res/values-fi/strings.xml
@@ -68,7 +68,7 @@
%d tallennustaLuokkaa %1$s ei löytynyt
- Lisää luokkia tehdäksesi kuvistasi helpommin löydettäviä.\n\nAloita kirjoittaminen lisätäksesi luokkia.\nNapauta tätä viestiä (tai paina takaisin) ohittaaksesi tämän vaiheen.
+ Lisää luokkia tehdäksesi kuvistasi enemmän löydettäviä Wikimedia Commonssissa.\nAloita kirjoittaminen lisätäksesi luokkia.LuokatAsetuksetRekisteröidy
@@ -121,7 +121,7 @@
ÄLÄ tallenna seuraavia:- Selfiet tai kuvat ystävistäsi\n- Netistä ladatut kuvat\n- Kuvakaappaukset kaupallisista sovelluksistaTallennusesimerkki:
- - Nimi: Sydneyn operatalo\n- Kuvaus: Sydneyn oopperatalo katsottuna lahden toisella puolella\n- Luokat: Sydneyn oopperatalo, Sydneyn oopperatalo lännestä, Sydneyn oopperatalo remote views
+ - Nimi: Sydneyn operatalo\n- Kuvaus: Sydneyn oopperatalo katsottuna lahden toisella puolella\n- Luokat: Sydneyn oopperatalo, Sydneyn oopperatalo lännestä, Sydneyn oopperatalo remote viewsHerätä Wikipedia-artikkelit eloon kuvillasi! Tuo kuvasi Wikipediaan.Wikipedian kuvat tulevat Wikimedia Commonsista.Kuvasi auttavat useita ihmisiä ympäri maailmaa artikkeleiden ymmärtämisessä.
@@ -153,6 +153,7 @@
Ryhdy beetatestaajaksiKäytä Wikidataa(Varoitus: poiskytkeminen voi aiheuttaa suuren mobiilidatankäytön)
+ 2FA koodiMaksimimääräEi voida näyttää enempää, kuin 500Kaksivaiheinen tunnistus ei ole vielä tuettu.
@@ -162,9 +163,11 @@
Kuvaa ei löytynytLataa kuvaZao-vuori
+ LaamatSateenkaarisiltaTulppaaniEi selfieitä
+ Patentoitu kuvaTervetuloa WikipediaanTervetuloa tekijänoikeusSydneyn oopperatalo
@@ -179,8 +182,17 @@
PalauteKirjaudu ulosOpas
+ IlmoituksetLähellä olevia paikkoja ei voida näyttää ilman sijaintilupaaCommons-tiedostosivuWikidata-kohdeTiedoston yksilöllinen ja kuvaava otsikko, jota käytetään tiedostonimenä. Voit käyttää tavallista kieltä välilyönnein. Älä sisällytä tiedoston päätettä.
+ Anna lupa
+ Käytä ulkoista tallennustilaa
+ Lähetä lokitiedosto
+ Lähetä logitiedosto kehittäjille sähköpostin kautta
+ Kirjaudu tilillesi
+ Sijainti ei ole muuttunut.
+ Sijainti ei käytettävissä.
+ LUE ARTIKKELI
diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml
index 18953d716..b5144794f 100644
--- a/app/src/main/res/values-fr/strings.xml
+++ b/app/src/main/res/values-fr/strings.xml
@@ -121,7 +121,7 @@
Veuillez ne PAS téléverser :- des selfies ou des images de vos amis \n- des images téléchargées sur Internet\n- des copies d’écran d’applications propriétairesExemple de téléversement :
- - Titre : L’opéra de Sydney\n- Description : L’opéra de Sydney vu à travers la baie\n- Catégories : Opéra de Sydney, Opéra de Sydney depuis l’ouest, vues à distance de l’Opéra de Sydney
+ - Titre : Opéra de Sydney\n- Description : L’opéra de Sydney vu à travers la baie\n- Catégories : Opéra de Sydney depuis l’ouest, vues à distance de l’Opéra de SydneyContribuez avec vos images. Aidez les articles de Wikipédia à prendre vie !Les images sur Wikipédia viennent de Wikimedia Commons.Vos images aident à éduquer les gens dans le monde entier.
@@ -188,6 +188,7 @@
CommentaireDéconnexionTutoriel
+ NotificationsLes endroits proches ne peuvent pas être affichés si vous ne partagez pas votre position géographique.aucune description trouvéePage des fichiers de Commons
@@ -204,4 +205,6 @@
L\'emplacement n\'a pas changé.Emplacement non disponible.Une permission est requise pour afficher une liste de lieux relatifs
+ OBTENIR DES DIRECTIVES
+ LIRE L’ARTICLE
diff --git a/app/src/main/res/values-frr/strings.xml b/app/src/main/res/values-frr/strings.xml
index 834aeafe0..a612d9869 100644
--- a/app/src/main/res/values-frr/strings.xml
+++ b/app/src/main/res/values-frr/strings.xml
@@ -120,7 +120,7 @@
Oober schüür EI huuch:- Selfies of bilen faan din frinjer\n- Bilen, diar dü ütj at internet deellooset heest\n- Bilskirembilen faan ünfrei softwareBispal:
- - Tiitel: Sydney Opernhüs\n- Beskriiwang: Opernhüs fan Sydney, faan\'t bocht ütj sen\n- Kategoriin: Sydney Opernhüs, Sydney Opernhüs faan waasten, Sydney Opernhüs faan widj wech
+ - Tiitel: Sydney Opernhüs\n- Beskriiwang: Opernhüs fan Sydney, faan\'t bocht ütj sen\n- Kategoriin: Sydney Opernhüs, Sydney Opernhüs faan waasten, Sydney Opernhüs faan widj wechSkaft din bilen. Halep mä an maage artiikler üüb Wikipedia labener!A bilen üüb Wikipedia kem faan Wikimedia Commons.Mä din bilen halepst dü minsken üüb a hialer welt.
diff --git a/app/src/main/res/values-gl/strings.xml b/app/src/main/res/values-gl/strings.xml
index b70db1b2d..f5aae3677 100644
--- a/app/src/main/res/values-gl/strings.xml
+++ b/app/src/main/res/values-gl/strings.xml
@@ -68,7 +68,7 @@
%d cargasNon se atopou ningunha categoría que coincidise con \"%1$s\"
- Engada categorías para facer máis accesibles as súas imaxes na Wikimedia Commons.\n\nComece a escribir para engadir categorías.\nPrema nesta mensaxe (ou no botón \"Atrás\") para saltar este paso.
+ Engada categorías para facer máis accesibles as súas imaxes na Wikimedia Commons.\nComece a escribir para engadir categorías.CategoríasConfiguraciónsRexistrarse
@@ -121,7 +121,7 @@
Por favor, NON subaː- Selfies ou imaxes dos seus amigos\n- Imaxes descargadas de Internet\n- Capturas de pantalla de aplicacións con dereitos de autorExemplo de subaː
- - Título: Ópera de Sydney\n- Descrición: A Ópera de Sydney vista dende a baía\n- Categorías: Sydney Opera House, Sydney Opera House from the west, Sydney Opera House remote views
+ - Título: Ópera de Sydney\n- Descrición: A Ópera de Sydney vista dende a baía\n- Categorías: Sydney Opera House from the west, Sydney Opera House remote viewsAchegue as súas imaxes. Axude a que os artigos da Wikipedia cobren vida!As imaxes da Wikipedia veñen da Wikimedia Commons.As súas imaxes axudan a educar xente de todo o mundo.
@@ -188,6 +188,7 @@
ComentariosSaírTitorial
+ NotificaciónsOs sitios situados preto non poden visualizarse sen permisos de localizaciónnon se atopou descriciónPáxina do ficheiro en Commons
@@ -204,4 +205,6 @@
A localización non cambiou.A localización non está dispoñible.Precísase permiso para amosar unha lista de lugares preto de aquí
+ OBTER DIRECCIÓNS
+ LER ARTIGO
diff --git a/app/src/main/res/values-hi/strings.xml b/app/src/main/res/values-hi/strings.xml
index bc08a1f55..c4ff79cd5 100644
--- a/app/src/main/res/values-hi/strings.xml
+++ b/app/src/main/res/values-hi/strings.xml
@@ -9,10 +9,10 @@
प्रवेश हो रहाप्रतीक्षा करें…प्रवेश में सफल हुआ!
- प्रवेश विफल हुआ
- फ़ाइल नहीं मिला, कृपया अन्य फ़ाइल से कोशिश करें।
+ प्रवेश विफल हुआ!
+ फ़ाइल नहीं मिली, कृपया अन्य फ़ाइल से प्रयास करें।प्रमाणीकरण विफल!
- अपलोड शुरू हुआ!
+ अपलोड आरंभ!%1$s अपलोड हुआ!अपना अपलोड देखने के लिए टैप करें%1$s का अपलोड शुरू हुआ
@@ -72,40 +72,40 @@
पसंदखाता खोलेंपरिचय
- मुक्त स्रोत सॉफ्टवेयर जो <a href=\"https://github.com/commons-app/apps-android-commons/blob/master/COPYING\">अपाचे लाइसेन्स</a> के अंतर्गत जारी किया गया है। %1$s और इसका लोगो विकिमीडिया संस्था का व्यापारिक चिह्न है और इसके मर्जी से ही उपयोग किया जाना चाहिए। हम किसी भी प्रकार से विकिमीडिया संस्था से जुड़े नहीं हैं।
+ विकिमीडिया कॉमन्स एप्प एक मुक्त स्रोत एप्प है जो कि विकिमीडिया समुदाय के अनुदानप्राप्तकर्ताओं व स्वयंसेवकों द्वारा निर्मित एवं प्रबंधित है। विकिमीडिया फॉऊण्डेशन इस एप्प के निर्माण, विकास व प्रबंधन में किसी प्रकार से भी संलग्न नहीं है।<a href=\"https://github.com/commons-app/apps-android-commons\">स्रोत</a> और <a href=\"https://commons-app.github.io/\">वेबसाइट</a> गिटहब में है और त्रुटि व सुझाव हेतु <a href=\"https://github.com/commons-app/apps-android-commons/issues\">गिटहब समस्या</a> देखें।
- <a href=\"https://wikimediafoundation.org/wiki/Privacy_policy\">गोपनियता नीति</a>
+ <a href=\"https://wikimediafoundation.org/wiki/Privacy_policy\">गोपनीयता नीति</a><a href=\"https://github.com/commons-app/apps-android-commons/blob/master/CREDITS\">श्रेय</a>परिचयप्रतिक्रिया दें (ईमेल द्वारा)
- कोई ईमेल ग्राहक स्थापित नहीं
+ कोई ईमेल साधन स्थापित नहींहाल ही उपयोग में ली गयी श्रेणियाँपहले सिंक हेतु प्रतीक्षा में…आपने अब तक कोई फोटो अपलोड नहीं किया है।फिर प्रयास करेंरद्द करें
- इस छवि का लाइसेन्स %1$s के अंतर्गत है।
+ इस छवि का लाइसेन्स %1$s के अंतर्गत होगा।इस तस्वीर को सबमिट करके, मैं घोषणा करता हूं कि यह मेरा अपना काम है, इसमें कॉपीराइट सामग्री या सेल्फी नहीं है, और अन्यथा <a href=\"https://commons.wikimedia.org/wiki/Commons:Policies_and_guidelines\">विकीमीडिया कॉमन्स नीतियां का पालन करता हूँ </a>डाउनलोडलाइसेन्सपिछले शीर्षक/विवरण का उपयोग करेंवर्तमान स्थान स्वतः ज्ञात करें
- यदि छवि जियोटैगेड नहीं है तो श्रेणियों के सुझाव हेतु वर्तमान स्थान ज्ञात करें।
+ यदि छवि पर जियोटैग नहीं है तो श्रेणियों के सुझाव हेतु वर्तमान स्थान ज्ञात करें।रात्रि मोडडार्क थीम का प्रयोग करें
- विशेषता-साझेदारी 4.0
+ एट्रीब्यूशन-शेयरअलाइक 4.0एट्रिब्यूशन 4.0
- एट्रीबुसन-शेयरअलाइक 3.0
- एट्रीबुसन 3.0
- CC0
- CC BY-SA 3.0
- CC BY-SA 3.0 (ऑस्ट्रीया)
+ एट्रीब्यूशन-शेयरअलाइक 3.0
+ एट्रीब्यूशन 3.0
+ सीसी0
+ सीसी बाय-एसए 3.0
+ सीसी बाय-एसए 3.0 (ऑस्ट्रिया)CC BY-SA 3.0 (जर्मनी)CC BY-SA 3.0 (एस्टोनिया)CC BY-SA 3.0 (स्पेन)
- CC BY-SA 3.0 (क्रोटिया)
- CC BY-SA 3.0 (लुक्सेमबौर्ग)
- CC BY-SA 3.0 (नेदरलैंड)
+ CC BY-SA 3.0 (क्रोएशिया)
+ CC BY-SA 3.0 (लक्समबर्ग)
+ CC BY-SA 3.0 (नीदरलैंड)CC BY-SA 3.0 (नॉर्वे)CC BY-SA 3.0 (पोलैंड)CC BY-SA 3.0 (रोमानिया)
@@ -113,19 +113,19 @@
CC BY-SA 4.0CC BY 4.0CC Zero
- विकिपीडिया में उपयोग होने वाले कई चित्रों को होस्ट विकिमीडिया कॉमन्स करता है।
- आपके लिए चित्र पूरे विश्व के लोगों को शिक्षित करेंगे!
- कृपया उन तस्वीरों को डालें, जो आपने ली है या केवल आपके द्वारा बनाई गई है:
- - प्राकृतिक वस्तुओं (फूल, पशु, पहाड़ों)\n- उपयोगी वस्तुओं (साइकिल, रेलवे स्टेशन)\n- प्रसिद्ध लोगों (आपके महापौर, ओलंपिक एथलीटों जिनसे आप मिले हो)
+ विकिपीडिया में उपयोग होने वाले अधिकतर चित्र विकिमीडिया कॉमन्स पर रखे जाते है।
+ आपके लिए चित्र पूरे विश्व के लोगों को शिक्षित करने में सहायता करेंगे!
+ कृपया उन तस्वीरों को डालें, जो केवल आपके द्वारा ली गई या बनाई गई है:
+ - प्राकृतिक वस्तुएँ (फूल, पशु, पहाड़)\n- उपयोगी वस्तुएँ (साइकिल, रेलवे स्टेशन)\n- प्रसिद्ध लोग (आपके महापौर, ओलंपिक एथलीट जिनसे आप मिले हों)कृपया अपलोड न करें:- सेल्फी या अपने दोस्तों की तस्वीरें\n- इंटरनेट से डाउनलोड की गई तस्वीरें\n- किसी निजी एप का स्क्रीनशॉटअपलोड का उदाहरण:
- - शीर्षक: सिडनी ओपेरा हाउस\n- विवरण: सिडनी ओपेरा हाउस खाड़ी के पार से देखा गया\n- श्रेणियाँ: सिडनी ओपेरा हाउस, पश्चिम से सिडनी ओपेरा हाउस, सिडनी ओपेरा हाउस दूर से
- अपने छवियों का योगदान करें। विकिपीडिया के लेखों को जीवित करने में सहायता करें।
+ - शीर्षक: सिडनी ओपेरा हाउस\n- विवरण: सिडनी ओपेरा हाउस का खाड़ी के पार से दृश्य\n- श्रेणियाँ: सिडनी ओपेरा हाउस, पश्चिम से सिडनी ओपेरा हाउस, सिडनी ओपेरा हाउस दूर से
+ अपने लिए चित्रों का योगदान करें। विकिपीडिया के लेखों में जान फूँकने में सहायता करें।विकिपीडिया में छवि विकिमीडिया कॉमन्स से आती है।
- आपके चित्र पूरे विश्व के लोगों को शिक्षित करेंगे।
+ आपके चित्र पूरे विश्व के लोगों को शिक्षित करने में सहायता करते हैं।इंटरनेट से मिली कोई कॉपीराइट सामग्री के साथ साथ पोस्टर, पुस्तक के खड्डे आदि को भी अपलोड करने से बचें।
- जो आपने सोचा वो मिला?
+ क्या आपको लगता है कि आप समझ गए?हाँ!श्रेणियाँलोड हो रहा है…
@@ -152,9 +152,9 @@
निर्देशांककुछ नहीं प्रदान किया गयाबीटा परीक्षक बनें
- गूगल प्ले पर हमारे बीटा चैनल में ऑप्ट-इन करें और नई सुविधाओं और बग फिक्स के लिए शीघ्र प्राप्त करें
+ गूगल प्ले पर हमारे बीटा चैनल का चयन करें और नई सुविधाओं व त्रुटिसुधारों तक पहले पहुँचेविकिडेटा का प्रयोग करें
- (चेतावनी: इसे अक्षम करने से बड़ी मोबाइल डेटा की खपत हो सकती है)
+ (चेतावनी: इसे अक्षम करने से मोबाइल डेटा की खपत अधिक हो सकती है)2 एफए कोडमेरी हाल ही की अपलोड सीमाअधिकतम सीमा
@@ -187,6 +187,7 @@
आपके सुझावप्रस्थान करेंअनुशिक्षण
+ सूचनायेंआस-पास के स्थान बिना स्थान अनुमतियों के प्रदर्शित नहीं किए जा सकते हैंकोई विवरण नहीं मिलाकॉमन्स फाइल पृष्ठ
@@ -194,6 +195,15 @@
चित्र कैशिंग करते समय त्रुटिफ़ाइल के लिए एक अद्वितीय वर्णनात्मक शीर्षक, जो एक फ़ाइल नाम के रूप में काम करेगा। आप रिक्त स्थान के साथ सादे भाषा का उपयोग कर सकते हैं। फ़ाइल विस्तार शामिल न करेंकृपया मीडिया जितना संभव हो उतना बताएं: यह कहां लिया गया? यह क्या दिखाता है? संदर्भ क्या है? कृपया वस्तुओं या व्यक्तियों का वर्णन करें। ऐसी जानकारी का खुलासा करें जिसे आसानी से अनुमानित नहीं किया जा सकता, उदाहरण के लिए दिन का समय यदि यह परिदृश्य है। अगर मीडिया कुछ असामान्य दिखाता है, तो कृपया बताएं कि इसे क्या असामान्य बनाता है।
+ अनुमति देंबाहरी स्टॉरज का पृयोग करे।आप अपने डिवाइस के इन-ऐप कैमरा से ली गई तस्वीरों को सहेजें।
+ लॉग फाइल भेजें
+ डेवेलपर्स को लॉग फाइल ई-मेल से भेजें
+ अपने खाते में प्रवेश करें
+ स्थान परिवर्तन नहीं हुआ।
+ स्थान उपलब्ध नहीं।
+ आसपास के स्थान दिखाने के लिए अनुमति चाहिए
+ दिशा - निर्देश प्राप्त करें
+ लेख पढ़ें
diff --git a/app/src/main/res/values-hu/strings.xml b/app/src/main/res/values-hu/strings.xml
index eb35185ba..6078c1d25 100644
--- a/app/src/main/res/values-hu/strings.xml
+++ b/app/src/main/res/values-hu/strings.xml
@@ -68,7 +68,7 @@
%d feltöltésNincs a(z) „%1$s” keresési kifejezésnek megfelelő kategória
- Adj kategóriákat a képekhez, hogy könnyebben meg lehessen találni őket a Commonson.\n\nKezdd el beírni a kategória nevét, hogy hozzáadd.\nKattints erre az üzenetre a lépés kihagyásához
+ Adj kategóriákat a képekhez, hogy könnyebben meg lehessen találni őket a Commonson.\nKezdd el beírni a kategória nevét, hogy hozzáadd.\nBökj erre az üzenetre (vagy a vissza gombra) a lépés kihagyásáhozKategóriákBeállításokRegisztráció
@@ -121,7 +121,7 @@
Kérjük, NE tölts fel:- Szelfiket vagy képeket a barátaidról\n- Internetröl letöltött képeket\n- Kereskedelmi alkalmazások képernyőképeitPélda feltöltés:
- - Cím: Sydney-i Operaház\n- Leírás: A Sydney-i Operaház az öböl túlpartjáról\n- Kategóriák: Sydney Opera House, Sydney Opera House from the west, Sydney Opera House remote views
+ - Cím: Sydney-i Operaház\n- Leírás: A Sydney-i Operaház az öböl túlpartjáról\n- Kategóriák: Sydney Opera House from the west, Sydney Opera House remote viewsTedd közzé a képeidet! Segíts életre kelteni a Wikipédia-szócikkeket!A Wikipédián található képek a Wikimédia Commonsből származnak.A képeid segítenek a világ minden táján élő emberek oktatásában.
diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml
index b1106e25d..1b54b023a 100644
--- a/app/src/main/res/values-it/strings.xml
+++ b/app/src/main/res/values-it/strings.xml
@@ -114,6 +114,7 @@
CC ZeroWikimedia Commons ospita la maggior parte delle immagini che vengono utilizzate in Wikipedia.Le tue immagini aiutano l\'istruzione di persone in tutto il mondo!
+ Per favore NON caricare:Esempi di caricamento:Contribuisci con le tue immagini. Aiuta a rendere vive le voci di Wikipedia!Le immagini su Wikipedia provengono da Wikimedia Commons.
@@ -146,11 +147,15 @@
Non è possibile mostrarne più di 500L\'autenticazione a due fattori non è attualmente supportata.Vuoi veramente uscire?
+ Logo di CommonsNessuna immagine trovataCarica immagine
+ Monte ZaoArcobalenoTulipanoNo autoscatti (selfie)
+ Benvenuto Wikipedia
+ Benvenuto CopyrightTeatro dell\'opera di SydneyAnnullaApri
@@ -163,10 +168,14 @@
CommentiEsciTutorial
+ Notifichenessuna descrizione trovataPagina di Commons del fileElemento Wikidata
+ Dai autorizzazioneAccedi alla tua utenzaLa posizione non è cambiata.Posizione non disponibile.
+ OTTIENI DIREZIONI
+ LEGGI VOCE
diff --git a/app/src/main/res/values-iw/strings.xml b/app/src/main/res/values-iw/strings.xml
index 056f6a437..d9e5df428 100644
--- a/app/src/main/res/values-iw/strings.xml
+++ b/app/src/main/res/values-iw/strings.xml
@@ -120,7 +120,7 @@
נא לא להעלות:- תמונות עצמיות (\"סלפי\") או תמונות של חברים שלכם\n- תמונות שהורדתם מהאינטרנט\n- צילומי מסך של יישומים קניינייםהעלאה לדוגמה:
- - כותרת: בית האופרה של סידני\n- תיאור: בית האופרה של סידני מהצד השני של המפרץ\n- קטגוריות: Sydney Opera House (בית האופרה של סידני), Sydney Opera House from the west (בית האופרה של סידני מהמערב), Sydney Opera House remote views (מראה מרחוק על בית האופרה של סידני)
+ - כותרת: בית האופרה של סידני\n- תיאור: בית האופרה של סידני מהצד השני של המפרץ\n- קטגוריות: Sydney Opera House (בית האופרה של סידני), Sydney Opera House from the west (בית האופרה של סידני מהמערב), Sydney Opera House remote views (מראה מרחוק על בית האופרה של סידני)תרמו את התמונות שלכם. עזרו לערכים בוויקיפדיה להתעורר לחיים!התמונות בוויקיפדיה מגיעות מ־Wikimedia Commons.התמונות שלכם עוזרות להעניק חינוך לאנשים מסביב לעולם.
diff --git a/app/src/main/res/values-ja/strings.xml b/app/src/main/res/values-ja/strings.xml
index f2d2369cf..60fef5f25 100644
--- a/app/src/main/res/values-ja/strings.xml
+++ b/app/src/main/res/values-ja/strings.xml
@@ -70,7 +70,7 @@
このアプリについてウィキメディア・コモンズ・アプリはウィキメディア・コミュニティの助成金受給者とボランティアによって製作・メンテナンスされているオープンソースソフトウェアです。ウィキメディア財団はこのアプリの製作・開発・メンテナンスに関与していません。ソースは <a href=\"https://github.com/commons-app/apps-android-commons\">GitHub</a> にあります。バグとアイディアは <a href=\"https://github.com/commons-app/apps-android-commons/issues\">Github</a> へ。
- <a href=\"https://wikimediafoundation.org/wiki/プライバシー・ポリシー\">プライバシー・ポリシー</a>
+ <a href=\"https://github.com/commons-app/apps-android-commons/wiki/プライバシー・ポリシー\">プライバシー・ポリシー</a><a href=\"https://github.com/commons-app/apps-android-commons/blob/master/CREDITS\">クレジット</a>このアプリについてフィードバックをメールで送信
@@ -115,7 +115,7 @@
アップロードが《禁止》のもの:- あなたの友人の自撮り写真や画像\n- インターネットからダウンロードした画像\n- 著作権のあるアプリのスクリーンショットアップロードの例:
- - 題名: シドニー・オペラハウス\n- 説明: 湾の向こうから見たシドニー・オペラハウス\n- カテゴリ: シドニー・オペラハウス、シドニー・オペラハウスの西側、遠くから見たシドニー・オペラハウス
+ - 題名: シドニー・オペラハウス\n- 説明: 湾の向こうから見たシドニー・オペラハウス\n- カテゴリ: 西側から見たシドニー・オペラハウス、遠くから見たシドニー・オペラハウス画像を投稿してください。ウィキペディアの記事に彩りを!ウィキペディアの画像はウィキメディア・コモンズに保管されています。あなたの画像は世界中の人々が学習する助けになります
@@ -155,6 +155,7 @@
最近のアップロードファイルに表示する最大件数2段階認証は現在サポートされていません。ログアウトしてもよろしいですか?
+ コモンズの商標背景画像画像がありません画像をアップロード
@@ -175,6 +176,7 @@
フィードバックログアウトチュートリアル
+ 通知場所の権限がないと、近くの場所を表示できません説明がありませんウィキデータ項目
diff --git a/app/src/main/res/values-ka/strings.xml b/app/src/main/res/values-ka/strings.xml
index 1cd0f7e2b..20977ac8d 100644
--- a/app/src/main/res/values-ka/strings.xml
+++ b/app/src/main/res/values-ka/strings.xml
@@ -113,7 +113,7 @@
გთხოვთ არ ატვირთოთ:- სელფი ან მეგობრების სურათები\n- ინტერნეტიდან ჩამოტვირთული ფოტოები\n- არათავისუფალი აპლიკაციების სკრინშოტებიატვირთვის ნიმუში:
- - სათაური: სიდნეის ოპერის თეატრი\n- აღწერა: სიდნეის ოპერის თეატრის ხედი უბიდან\n- კატეგორიები: Sydney Opera House, Sydney Opera House from the west, Sydney Opera House remote views
+ - სათაური: სიდნეის ოპერის თეატრი\n- აღწერა: სიდნეის ოპერის თეატრის ხედი უბიდან\n- კატეგორიები: Sydney Opera House, Sydney Opera House from the west, Sydney Opera House remote viewsატვირთეთ თქვენი ფოტოები. დაეხმარეთ ვიკიპედიის სტატიებს გაცოცხლებაში!ვიკიპედიის ფოტოები ვიკისაწყობში ინახება.თქვენი სურათები ხალხის განათლებას ეხმარება მთელ მსოფლიოში.
diff --git a/app/src/main/res/values-kab/strings.xml b/app/src/main/res/values-kab/strings.xml
index 94f70c1ea..20a7d52c3 100644
--- a/app/src/main/res/values-kab/strings.xml
+++ b/app/src/main/res/values-kab/strings.xml
@@ -121,7 +121,7 @@
UR salay ara:- isilfiyen neɣ tugniwin n yimdakkal-ik \n- tugniwin i d-sidreḍ si Internet\n- inɣal n ugdil n yisnasen yesɛan imawlanAmedya n usali:
- - Azwel: Tazqqa Opera n Sydney\n- Aglam : Tazeqqa Opera n Sydney seg ilel\n- Taggayin: Tazeqqa Opera n Sydney, Tazeqqa Opera n Sydney seg umalu, timeẓriyin s lebɛid n Tazeqqa Opera n Sydney.
+ - Azwel: Tazqqa Opera n Sydney\n- Aglam : Tazeqqa Opera n Sydney seg ilel\n- Taggayin: Tazeqqa Opera n Sydney, Tazeqqa Opera n Sydney seg umalu, timeẓriyin s lebɛid n Tazeqqa Opera n SydneyTtekki s tugniwin-ik. Snerni imagraden n Wikipedia!Tugniwin ɣef Wikipedia ttasent-d si Wikimedia Commons.Tugniwin-ik ad slemdent imdanen deg umaḍal.
@@ -204,4 +204,6 @@
Adeg ur ibeddel ara.Ulac adegIlaq usireg i uskan tabdart n wadigen iqerben
+ AWI IWELLIHEN
+ ƔER AMAGRAD
diff --git a/app/src/main/res/values-ko/strings.xml b/app/src/main/res/values-ko/strings.xml
index 5af2947a1..b68e1693f 100644
--- a/app/src/main/res/values-ko/strings.xml
+++ b/app/src/main/res/values-ko/strings.xml
@@ -120,7 +120,7 @@
업로드하지 마십시오:- 자기 자신 또는 친구의 사진\n- 인터넷에서 다운로드한 사진\n- 사유 앱의 스크린샷업로드 예시:
- - 제목: 시드니 오페라 하우스\n- 설명: 항만에서 바라본 시드니 오페라 하우스\n- 분류: 시드니 오페라 하우스, 서쪽에서 본 시드니 오페라 하우스, 시드니 오페라 하우스 원경
+ - 제목: 시드니 오페라 하우스\n- 설명: 항만 건너편에서 바라본 시드니 오페라 하우스\n- 분류: 시드니 오페라 하우스, 서쪽에서 본 시드니 오페라 하우스, 시드니 오페라 하우스 원경당신의 그림을 기여하세요. 위키백과 문서의 생명이 오는 데 도와주세요!위키백과의 그림은 위키미디어 공용에서 옵니다.당신의 그림은 전 세계 사람들을 교육하는 데 도움이 됩니다.
@@ -187,6 +187,7 @@
피드백로그아웃강좌
+ 알림위치 권한이 없으면 주변 장소를 표시할 수 없습니다설명이 없습니다공용 파일 문서
diff --git a/app/src/main/res/values-lb/strings.xml b/app/src/main/res/values-lb/strings.xml
index 46b927a23..919d72f63 100644
--- a/app/src/main/res/values-lb/strings.xml
+++ b/app/src/main/res/values-lb/strings.xml
@@ -67,7 +67,7 @@
%d Fichieren eropgeluedenD\'Kategorie %1$s gouf net fonnt
- Setzt Kategorien dobäi fir datt Är Biller méi einfach op Wikimedia Commons ze fanne sinn.\n\nFänkt u mat Tippe fir Kategorien dobäizesetzen.
+ Setzt Kategorien dobäi fir datt Är Biller méi einfach op Wikimedia Commons ze fanne sinn.\n\nFänkt u mat Tippe fir Kategorien dobäizesetzen.KategorienAstellungenMellt Iech un
@@ -181,6 +181,7 @@
FeedbackAusloggenUleedung
+ NotifikatiounenPlazen nobäi kënnen net gewise ginn ouni Rechter fir d\'Lokalisatiounkeng Beschreiwung fonntCommons-Fichierssäit
@@ -193,4 +194,5 @@
An Äre Benotzerkont aloggenDe Plaz huet net geännert.Plaz ass net disponibel.
+ ARTIKEL LIESEN
diff --git a/app/src/main/res/values-lez/error.xml b/app/src/main/res/values-lez/error.xml
new file mode 100644
index 000000000..b8d73c3b0
--- /dev/null
+++ b/app/src/main/res/values-lez/error.xml
@@ -0,0 +1,5 @@
+
+
+ Чlурукl хьана
+ Ёъ, вуч-ятlани масакl фена
+
diff --git a/app/src/main/res/values-lv/strings.xml b/app/src/main/res/values-lv/strings.xml
index d1492d1f3..8be6e0830 100644
--- a/app/src/main/res/values-lv/strings.xml
+++ b/app/src/main/res/values-lv/strings.xml
@@ -96,4 +96,5 @@
apraksts nav atrastsAtļautIzmantot ārējo krātuvi
+ LASĪT RAKSTU
diff --git a/app/src/main/res/values-mk/strings.xml b/app/src/main/res/values-mk/strings.xml
index 6ea041684..d2928cbba 100644
--- a/app/src/main/res/values-mk/strings.xml
+++ b/app/src/main/res/values-mk/strings.xml
@@ -68,7 +68,7 @@
%d подигањаНема категории што одговараат на %1$s
- Ставете им категории на сликите, за да можат корисниците полесно да ги најдат на Ризницата.\n\nЗа да ставите категорија, почнете со пишување на нејзиното име.\nДопрете ја поракава (или стиснете на назад) за да го прескокнете овој чекор.
+ Ставете им категории на сликите, за да можат корисниците полесно да ги најдат на Ризницата.\n\nЗа да ставите категорија, почнете со пишување на нејзиното име.КатегорииНагодувањаРегистрација
@@ -121,7 +121,7 @@
НЕ подигајте:u2022 Слики од вас или вашите пријатели \nu2022 Слики што сте ги презеле од семрежјето \nu2022 Екрански снимки на неслободни прилози (апликации)Пример за подигање:
- u2022 Наслов: Сиднејска опера \nu2022 Опис: Поглед на Сиднејската опера преку заливот\nu2022 Категорија: Сиднејска опера, Сиднејската опера од запад, Сиднејската опера од далеку
+ - Наслов: Сиднејска опера \n- Опис: Поглед на Сиднејската опера преку заливот\n- Категорија: Сиднејска опера, Сиднејската опера од запад, Сиднејската опера од далекуСподелете ги Вашите слики. Да ги оживееме статиите на Википедија!Сликите на Википедија доаѓаат од Ризницата.Со Вашите слики помагате во образованието на луѓето ширум светот.
@@ -188,6 +188,7 @@
МислењаОдјаваУпатства
+ ИзвестувањаМестата во близина не можат да се прикажат без дозволи за местоположба.не најдов описиПодатотечна страница
@@ -204,4 +205,6 @@
Местоположбата не е сменета.Местоположбата е недостапна.Се бара дозвола за приказ на список на околни места
+ ДАЈ НАСОКИ
+ ПРОЧИТАЈ СТАТИЈА
diff --git a/app/src/main/res/values-mr/strings.xml b/app/src/main/res/values-mr/strings.xml
index 488037628..9a766f31a 100644
--- a/app/src/main/res/values-mr/strings.xml
+++ b/app/src/main/res/values-mr/strings.xml
@@ -105,7 +105,7 @@
कृपया अपभारण करू \'\'\'नका\'\'\':-आपल्या किंवा मित्रांच्या सेल्फी\n-आपण आंतरजाल (इंटरनेट) वरुन अधिभारण केलेली चित्रे\n-प्रोप्रायटरी अॲप्सचे स्क्रिनशॉटउदाहरणादाखल अपभारण:
- - शीर्षक: Sydney Opera House\n- वर्णन: Sydney Opera House as viewed from across the bay\n- वर्ग: Sydney Opera House, Sydney Opera House from the west, Sydney Opera House remote views
+ - शीर्षक: Sydney Opera House\n- वर्णन: Sydney Opera House as viewed from across the bay\n- वर्ग: Sydney Opera House, Sydney Opera House from the west, Sydney Opera House remote viewsआपली चित्रे सहभागा. विकिपीडियावरील लेखात जीवंतपणा आणा!विकिपीडियावरची चित्रे विकिमिडिया कॉमन्सवरुन येतात.जगातल्या अनेक लोकांना आपल्या चित्राद्वारे शिकता येते.
diff --git a/app/src/main/res/values-nb/strings.xml b/app/src/main/res/values-nb/strings.xml
index 15a785f6d..19021a7d5 100644
--- a/app/src/main/res/values-nb/strings.xml
+++ b/app/src/main/res/values-nb/strings.xml
@@ -68,7 +68,7 @@
%d opplastingerIngen kategorier som stemte overens med %1$s funnet
- Legg til kategorier for å gjøre bildene dine lettere å finne på Wikimedia Commons.\n -\n -Begynn å skrive navnet på kategoriene.
+ Legg til kategorier for å gjøre bildene dine lettere å finne på Wikimedia Commons.\n\nBegynn å skrive navnet på kategoriene.KategorierInnstillingerRegistrer deg
@@ -121,7 +121,7 @@
Vennligst IKKE last opp:- Selfies eller bilder av vennene dine\n- Bilder som du har lastet ned fra internet\n- Skjermbilder tatt fra proprietære apperOpplastingseksempel:
- - Tittel: Sydneys Operahus\n- Beskrivelse: Operahuset i Sydney sett fra andre siden av bukten\n- Kategorier: Operahuset i Sydney, Operahuset i Sydney ifra vest, Operahuset i Sydney utenfra
+ - Tittel: Sydneys operahus\n- Beskrivelse: Operahuset i Sydney sett fra andre siden av bukten\n- Kategorier: Operahuset i Sydney ifra vest, Operahuset i Sydney utenfraBidra med dine bilder. Hjelp til med å blåse liv i Wikipedias artikler!Bilder på Wikipedia kommer fra Wikimedia Commons.Bildene dine kan være til hjelp for mennesker over hele verden som søker kunnskap og dannelse.
@@ -188,6 +188,7 @@
TilbakemeldingerLogg utVeiviser
+ VarslerSteder i nærheten kan ikke vises uten tillatelse for stedsbestemmelseingen beskrivelse funnetCommons-filside
@@ -204,4 +205,6 @@
Stedet har ikke blitt endret.Sted ikke tilgjengelig.Tillatelse kreves for å vise listen over steder i nærheten
+ FÅ VEIBESKRIVELSE
+ LES ARTIKKEL
diff --git a/app/src/main/res/values-nl/strings.xml b/app/src/main/res/values-nl/strings.xml
index 81cb1ed20..8c6d2abf3 100644
--- a/app/src/main/res/values-nl/strings.xml
+++ b/app/src/main/res/values-nl/strings.xml
@@ -116,7 +116,7 @@
Gelieve NIET te uploaden:- Selfies of foto\'s van uw vrienden\n- Foto\'s die u hebt gedownload van het Internet\n- Screenshots van eigen appsVoorbeeld van een upload:
- - Titel: Sydney Opera House\n- Beschrijving: het Sydney Opera House gezien vanaf de overkant van de baai\n- Categorieën: Sydney Opera House, Sydney Opera House from the west, Sydney Opera House remote views
+ - Titel: Sydney Opera House\n- Beschrijving: het Sydney Opera House gezien vanaf de overkant van de baai\n- Categorieën: Sydney Opera House, Sydney Opera House from the west, Sydney Opera House remote viewsDraag uw afbeelding bij. Help pagina\'s in Wikipedia tot leven te laten komen!Afbeeldingen op Wikipedia komen van Wikimedia Commons.Uw afbeeldingen helpen mensen van over de hele wereld te leren.
diff --git a/app/src/main/res/values-pa/strings.xml b/app/src/main/res/values-pa/strings.xml
index ecddde5db..4d74c2cd6 100644
--- a/app/src/main/res/values-pa/strings.xml
+++ b/app/src/main/res/values-pa/strings.xml
@@ -118,7 +118,7 @@
ਕਿਰਪਾ ਕਰਕੇ ਅਪਲੋਡ ਨਾ ਕਰੋ:- ਸੈਲਫ਼ੀਆਂ ਜਾਂ ਤੁਹਾਡੇ ਦੋਸਤਾਂ ਦੀਆਂ ਤਸਵੀਰਾਂ\n- ਜੋ ਤਸਵੀਰਾਂ ਤੁਸੀਂ ਇੰਟਰਨੈੱਟ ਤੋਂ ਡਾਊਨਲੋਡ ਕੀਤੀਆਂ ਹਨ\n- ਨਿੱਜੀ ਐਪਲੀਕੇਸ਼ਨਾਂ ਦੇ ਸਕਰੀਨਸ਼ੌਟਉਦਾਹਰਣ ਵਜੋਂ ਇਹ ਅਪਲੋਡ:
- - ਸਿਰਲੇਖ: Sydney Opera House\n- ਵੇਰਵਾ: ਸਿਡਨੀ ਓਪੇਰਾ ਹਾਊਸ ਦੀ ਤਸਵੀਰ\n- ਸ਼੍ਰੇਣੀਆਂ: Sydney Opera House, Sydney Opera House from the west, Sydney Opera House remote views
+ - ਸਿਰਲੇਖ: Sydney Opera House\n- ਵੇਰਵਾ: ਸਿਡਨੀ ਓਪੇਰਾ ਹਾਊਸ ਦੀ ਤਸਵੀਰ\n- ਸ਼੍ਰੇਣੀਆਂ: Sydney Opera House, Sydney Opera House from the west, Sydney Opera House remote viewsਆਪਣੀਆਂ ਤਸਵੀਰਾਂ ਦਾ ਯੋਗਦਾਨ ਪਾਓ। ਵਿਕੀਪੀਡੀਆ ਲੇਖਾਂ ਨੂੰ ਸੁਰਜੀਤ ਕਰ ਦਿਓ!ਵਿਕੀਪੀਡੀਆ ਉਤਲੀਆਂ ਤਸਵੀਰਾਂ ਵਿਕੀਮੀਡੀਆ ਕਾਮਨਜ਼ ਤੋਂ ਆਉਂਦੀਆਂ ਹਨਤੁਹਾਡੀਆਂ ਤਸਵੀਰਾਂ ਦੁਨੀਆਂ ਭਰ ਦੇ ਲੋਕਾਂ ਨੂੰ ਪੜ੍ਹਨ ਵਿਚ ਮਦਦ ਕਰਦੀਆਂ ਹਨ।
diff --git a/app/src/main/res/values-pl/strings.xml b/app/src/main/res/values-pl/strings.xml
index 308212f66..5d26845a7 100644
--- a/app/src/main/res/values-pl/strings.xml
+++ b/app/src/main/res/values-pl/strings.xml
@@ -68,7 +68,7 @@
%d przesłaniaNie znaleziono kategorii pasujących do %1$s
- Dodaj kategorie, aby ułatwić odnalezienie plików w Wikimedia Commons.\n\nZacznij pisać, aby dodać kategorię.\nKliknij ten komunikat lub naciśnij „cofnij”, aby pominąć ten krok.
+ Dodaj kategorie, aby ułatwić odnalezienie plików w Wikimedia Commons.\nZacznij pisać, aby dodać kategorie.KategorieUstawieniaZarejestruj się
@@ -120,7 +120,7 @@
Prosimy NIE wysyłać:- Zdjęć przedstawiających siebie lub znajomych\n- Zdjęć pobranych z Internetu\n- Zrzutów ekranów płatnych aplikacjiPrzykład przesłania pliku:
- - Nazwa: Zamek Królewski w Warszawie\n- Opis: Zamek Królewski w Warszawie, widok od strony kolumny Zygmunta\n- Kategorie: Western facade (Royal Castle, Warsaw), Southern facade (Royal Castle, Warsaw), Castle Square in Warsaw
+ - Nazwa: Zamek Królewski w Warszawie\n- Opis: Zamek Królewski w Warszawie, widok od strony kolumny Zygmunta\n- Kategorie: Western facade (Royal Castle, Warsaw), Southern facade (Royal Castle, Warsaw), Castle Square in WarsawDodaj swoje ilustracje. Pomóż ożyć artykułom w Wikipedii!Ilustracje w Wikipedii pochodzą z Wikimedia Commons.Twoje ilustracje pomagają w edukacji ludzi na całym świecie.
diff --git a/app/src/main/res/values-pms/strings.xml b/app/src/main/res/values-pms/strings.xml
index 14f9eec64..76b8ae920 100644
--- a/app/src/main/res/values-pms/strings.xml
+++ b/app/src/main/res/values-pms/strings.xml
@@ -68,7 +68,7 @@
%d cariamentGnun-e categorìe rëspondente a %1$s trovà
- Ch\'a gionta dle categorìe, për ch\'a sia pi belfé trové soe plance su Wikimedia Commons.\n\nCh\'a ancamin-a a scrive për gionté dle categorìe.\nCh\'a sgnaca an s\'ën mëssage (o ch\'a sgnaca ël boton andaré) për sauté \'s pass.
+ Ch\'a gionta dle categorìe, për ch\'a sia pi belfé trové soe plance su Wikimedia Commons.\nCh\'a ancamin-a a scrive për gionté dle categorìe.CategorìeParàmeterMarchesse
@@ -121,7 +121,7 @@
Për piasì, ch\'a caria NEN:- dj\'àuto-scat o dle fòto dij sò amis\n- dle fòto ch\'a l\'ha dëscarià d\'an sl\'Aragnà\n- Plance d\'aplicassion sota drit d\'autorEsempi ëd cariament:
- - Tìtol: Ël teatro dl\'òpera ëd Sidney\n- Descrission: Ël teatro dl\'òpera ëd Sidney s-ciairà dëdlà dl\'ansen\n- Categorìe: Teatro dl\'òpera ëd Sidney, Teatro dl\'òpera ëd Sidney da ponent, viste a distansa dël Teatro dl\'òpera ëd Sydney
+ - Tìtol: Ël teatro dl\'òpera ëd Sidney\n- Descrission: Ël teatro dl\'òpera ëd Sidney s-ciairà dëdlà dl\'ansen\n- Categorìe: Teatro dl\'òpera ëd Sidney da ponent, viste a distansa dël Teatro dl\'òpera ëd SydneyCh\'a contribuissa con soe plance. Ch\'a giuta a j\'artìcoj ëd Wikipedia a pijé vita!Le plance su Wikipedia a ven-o da Wikimedia Commons.Soe plance a giuto a eduché la gent daspërtut ant ël mond.
@@ -188,6 +188,7 @@
SugerimentSeurte dal sistemaCors d\'antrodussion
+ NotìficheIj pòst ant j\'anviron a peulo nen esse smonù sensa ij përmess ëd localisassiongnun-a descrission trovàPàgina dj\'archivi ëd Comun
@@ -204,4 +205,6 @@
Ël leu a l\'é nen cangià.Leu nen disponìbil.A-i é da manca dël përmess pr\'ësmon-e na lista dij pòst davzin
+ OTEN-E D\'ANSTRUSSION
+ LESE L\'ARTÌCOL
diff --git a/app/src/main/res/values-pt-rBR/strings.xml b/app/src/main/res/values-pt-rBR/strings.xml
index eab392b07..5442e48b1 100644
--- a/app/src/main/res/values-pt-rBR/strings.xml
+++ b/app/src/main/res/values-pt-rBR/strings.xml
@@ -68,7 +68,7 @@
%d carregamentosNenhuma categoria correspondente %1$s encontrada
- Adicione categorias para fazer suas imagens mais vistas no Wikimedia Commons. \n\nComece a digitar para adicionar categorias.\nAperte nesta mensagem (ou aperte para voltar) para pular este passo
+ Adicione categorias para fazer suas imagens mais vistas no Wikimedia Commons. \n\nComece a digitar para adicionar categorias.\nAperte nesta mensagem (ou aperte para voltar) para pular este passoCategoriasConfiguraçõesCriar conta
@@ -188,6 +188,7 @@
ComentáriosSairTutorial
+ NotificaçõesOs locais próximos não podem ser exibidos sem permissões de localizaçãoNenhuma descrição encontradaPágina de arquivo do Commons
@@ -204,4 +205,6 @@
O local não mudou.Localização não disponível.Permissão necessária para exibir uma lista de locais próximos
+ OBTER DIREÇÕES
+ LER ARTIGO
diff --git a/app/src/main/res/values-pt/strings.xml b/app/src/main/res/values-pt/strings.xml
index fcbab98dc..75abf126e 100644
--- a/app/src/main/res/values-pt/strings.xml
+++ b/app/src/main/res/values-pt/strings.xml
@@ -68,12 +68,12 @@
%d carregamentosNenhuma categoria correspondente %1$s encontrada
- Adicione categorias para tornar as suas imagens mais fáceis de encontrar no Wikimedia Commons. \n\nComece a digitar para adicionar categorias.\nCarregue nesta mensagem (ou carregue para voltar) para saltar este passo
+ Adicione categorias para tornar as suas imagens mais fáceis de encontrar no Wikimedia Commons.\nComece a digitar para adicionar categorias.CategoriasConfiguraçõesRegistar-seSobre
- A aplicação do Wikimedia Commons é uma aplicação de código aberto criada e mantida por beneficiários e voluntários da comunidade Wikimedia. A Fundação Wikimedia não está envolvida na criação, programação ou manutenção da aplicação.
+ A aplicação do Wikimedia Commons é uma aplicação de código aberto criada e mantida por bolseiros e voluntários da comunidade Wikimedia. A Wikimedia Foundation não está envolvida na criação, desenvolvimento ou manutenção da aplicação.<a href=\"https://github.com/commons-app/apps-android-commons\">Fonte</a> e <a href=\"https://commons-app.github.io/\">sítio</a> no GitHub. Criar uma nova <a href=\"https://github.com/commons-app/apps-android-commons/issues\">publicação no GitHub</a> para informar erros e sugestões.<a href=\"https://github.com/commons-app/apps-android-commons/wiki/Privacy-policy\">Política de privacidade</a><a href=\"https://github.com/commons-app/apps-android-commons/blob/master/CREDITS\">Créditos</a>
@@ -121,7 +121,7 @@
Por favor, NÃO carregue:- Autorretratos ou imagens dos seus amigos\n- Imagens descarregadas da Internet\n- Capturas de ecrã de aplicações com direitos de autorExemplo de carregamento:
- - Título: Ópera de Sydney\n- Descrição: A Ópera de Sydney vista do outro lado da baía\n- Categorias: Ópera de Sydney, Ópera de Sydney vista de ocidente, Vistas à distância da Ópera de Sydney
+ - Título: Ópera de Sydney\n- Descrição: A Ópera de Sydney vista do outro lado da baía\n- Categorias: Ópera de Sydney vista do ocidente, Vistas à distância da Ópera de SydneyContribua com as suas imagens. Ajude os artigos da Wikipédia a ganhar vida!As imagens na Wikipédia provêm do Wikimedia Commons.As suas imagens ajudam a educar as pessoas em todo o mundo.
@@ -188,6 +188,7 @@
ComentáriosSairTutorial
+ NotificaçõesOs sítios aqui perto não podem ser apresentados sem permissões de localizaçãonão foi encontrada nenhuma descriçãoPágina do ficheiro no Commons
@@ -204,4 +205,6 @@
A localização não foi alterada.A localização não está disponível.É necessária a permissão para mostrar uma lista dos sítios aqui perto
+ OBTER DIREÇÕES
+ LER ARTIGO
diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml
index 60f4a5105..c4f161062 100644
--- a/app/src/main/res/values-ru/strings.xml
+++ b/app/src/main/res/values-ru/strings.xml
@@ -72,7 +72,7 @@
%d загрузокКатегории, соответствующие %1$s, не найдены
- Добавьте категории, чтобы ваши изображения можно было легко найти на Викискладе.\n\nНачните вводить название для добавления категорий.\nНажмите на это сообщение (или за ним), чтобы пропустить этот шаг.
+ Добавьте категории, чтобы ваши изображения можно было легко найти на Викискладе.\nНачните вводить название для добавления категорий.КатегорииНастройкиЗарегистрироваться
@@ -125,7 +125,7 @@
Пожалуйста, НЕ загружайте:— Селфи или фотографии ваших друзей\n— Фотографии, которые вы скачали из Интернета\n— Скриншоты несвободных приложенийПример загрузки:
- — Название: Сиднейский оперный театр\n— Описание: Сиднейский оперный театр, вид через залив\n— Категории: Sydney Opera House, Sydney Opera House from the west, Sydney Opera House remote views
+ — Название: Сиднейский оперный театр\n— Описание: Сиднейский оперный театр, вид через залив\n— Категории: Sydney Opera House from the west, Sydney Opera House remote viewsЗагрузите свои изображения. Помогите Википедии оживить статьи!Изображения в Википедии хранятся на Викискладе.Ваши изображения помогают образованию людей во всём мире.
@@ -192,6 +192,7 @@
Обратная связьВыйтиРуководство
+ УведомленияБлижайшие места не могут быть отображены без разрешения на геолокациюописание не найденоСтраница файла на Викискладе
@@ -208,4 +209,5 @@
Местоположение не изменено.Местоположение недоступно.Необходимо разрешение для отображения списка ближайших мест
+ ЧИТАТЬ СТАТЬЮ
diff --git a/app/src/main/res/values-sd/strings.xml b/app/src/main/res/values-sd/strings.xml
index f4a785363..93cc55e57 100644
--- a/app/src/main/res/values-sd/strings.xml
+++ b/app/src/main/res/values-sd/strings.xml
@@ -52,26 +52,26 @@
تازو ڪيوجي پي ايس چالو ڪيو (اين ايبل جي پي ايس)اڃان تائين ڪو به ڄاڙهه (اَپلوڊ) نه ٿيو آهي
-
- اڃان تائين ڪو چاڙھ ناھي
- 1 چاڙھ
+
+ \@string/contributions_subtitle_zero
+ %d چاڙھ%d چاڙھَ
-
- ھڪ 1 چاڙھ شروع ڪندي
+
+ چاڙھ %d شروع ڪنديچاڙھَ %d شروع ڪندي
-
- 1 چاڙھ
+
+ %d چاڙھ%d چاڙھَ%1$s سان ملندڙ ڪوبہ زمرو نہ لڌو
- پنھنجي عڪسن ۾ زمرا وجھو تہ جيئن وڪيميڊيا العام تي وڌيڪ ڳولا لائق ٿي سگھن.\n\nزمرا وجھڻ لاءِ لکڻ شروع ڪريو.\nھن پيغام تي ٺونگو ھڻو (يا پوئتي ڌڪ ھڻو) ھي قدم ڇڏڻ لاءِ.
+ پنھنجي عڪسن ۾ زمرا وجھو تہ جيئن وڪيميڊيا العام تي وڌيڪ ڳولا لائق ٿي سگھن.\n\nزمرا وجھڻ لاءِ لکڻ شروع ڪريو.زمراترتيبونکاتو کوليوبابت
- <a href=\"https://wikimediafoundation.org/wiki/Privacy_policy\">ذاتيات پاليسي</a>
+ <a href=\"https://github.com/commons-app/apps-android-commons/wiki/Privacy-policy\">ذاتيات پاليسي</a>بابتپذيرائي موڪليو (برقٽپال ذريعي)ڪوبہ برقٽپال ڪلائينٽ تنصيبيل ناھي
@@ -115,7 +115,7 @@
براءِ مھرباني نہ چاڙھيو:u2022 سيلفيون يا پنھنجي دوستن جو تصويرون \nu2022 اھي تصويرون جيڪي توھان انٽرنيٽ تان ڊائونلوڊ ڪيون \nu2022 پروپرائيٽري ايپس جا اسڪرين شاٽمثال چاڙھ:
- u2022 عنوان: سڊني اوپيرا گھر \nu2022 تشريح: سڊني اوپيرا گھر نھر جي پاسي کان ڏيک \nu2022 زمرا: سڊني اوپيرا گھر، سڊني اوپيرا گھر اولھ کان، سڊني اوپيرا گھر ڏورانھان ڏيک
+ - عنوان: سڊني اوپيرا گھر \n- تشريح: سڊني اوپيرا گھر نھر جي پاسي کان ڏيک \n- زمرا: سڊني اوپيرا گھر، سڊني اوپيرا گھر اولھ کان، سڊني اوپيرا گھر ڏورانھان ڏيکپنھنجي عڪسن جي ڀاڱيداري ڪريو. وڪيپيڊيا ڪي مضمونن ۾ زندگي آڻيو!وڪيپيڊيا تي عڪس وڪيميڊيا العام تان اچن ٿا.توھان جا عڪس سڄي دنيا ۾ ماڻھن کي تعليم يافتا ڪرڻ ۾ مدد ڪن ٿا.
@@ -139,16 +139,19 @@
عنوانابلاغ جو عنوانتشريح
- چاڙهڻ جي تاريخ (اَپلوڊ ڊيٽ)
+ چاڙھيل تاريخلائسنس (اجازت نامو)آزمائشي آزمائيندڙ ٿيووڪيڊيٽا استعماڪ ڪريومنهنجي تازي چاڙهڻ (اَپلوڊ) جي حد (لِمٽ)وڌ ۾ وڌ حد (ميگزيمم لِمٽ)
+ 500 کان ٿي ڏيکارڻ کان قاصر آھي
+ ڇا توھان سچ ۾ خارج ٿيڻ ٿا چاھيو؟العام جي سڃاڻپ جو نشان (Commons Logo)پسمنظر جي تصويرڪوبہ عڪس نہ لڌوعڪس چاڙهيو
+ انڊلٺ پلگل لالا (ٽيولپ)ڪي به پاڻفيون نه (نو سيلفيز)سڃاڻپ واري تصوير (پروپرائٽري اميج)
@@ -157,14 +160,28 @@
سڊني اوپيرا هائوسردکوليو
+ بند ڪريومکيه صفحوچاڙهيو
+ ويجھابابتترتيبون (سيٽنگس)اوهان جي راءِٻاهر نڪروسکيا (ٽيوٽوريل)
+ ويجھيو جڳھون بغير مڪانيت اجازت جي نٿيون ڏيکاري سگھجنڪا به وضاحت نه مليڪامن فائيل جو ورقوڪيڊيٽا جزو (وڪيڊيٽا آئيٽم)
+ اجازت ڏيو
+ ٻاھري سنڀار استعمال ڪريو
+ ايپ ۾ ڪئمرا سان ڪڍيل تصويرون پنھنجي ڊوائيس تي سانڍيو
+ لاگ فائيل موڪليو
+ لاگ فائيل سرجڻھارن کي برقٽپال ذريعي موڪليو
+ پنھنجي کاتي ۾ داخل ٿيو
+ مڪانيت تبديلي ناھي ٿي.
+ مڪانيت موجود ناھي.
+ ويجھين جڳھن جي فھرست ڏيکارڻ لاءِ اجازت گھربل آھي
+ ھدايتون وٺو
+ مضمون پڙھو
diff --git a/app/src/main/res/values-sk/strings.xml b/app/src/main/res/values-sk/strings.xml
index 5225743fe..7f2136aa0 100644
--- a/app/src/main/res/values-sk/strings.xml
+++ b/app/src/main/res/values-sk/strings.xml
@@ -52,14 +52,14 @@
ObnoviťPovoliť GPSŽiadne kategórie nezodpovedajú „%1$s“
- Pridajte kategórie, aby boli vaše obrázky možné na Wikimedia Commons nájsť.\n\nNa pridanie kategórií začnite písať.\nKliknite na tento odkaz (alebo na späť) ak chcete tento krok vynechať.
+ Pridajte kategórie, aby bolo vaše obrázky možné na Wikimedia Commons nájsť.\nPre pridanie kategórií začnite písať.KategórieNastaveniaZaregistrovať saO aplikáciiOpen Source softvér dostupný za podmienok <a href=\"https://github.com/commons-app/apps-android-commons/blob/master/COPYING\">Apache License v2</a>Zdroj na <a href=\"https://github.com/commons-app/apps-android-commons\">GitHub</a>. Bugy na <a href=\" https://github.com/commons-app/apps-android-commons/issues\">Github</a>.
- <a href=\"https://wikimediafoundation.org/wiki/Privacy_policy\">Ochrana osobných údajov</a>
+ <a href=\"https://github.com/commons-app/apps-android-commons/wiki/Privacy-policy\">Zásady ochrany súkromia</a>O aplikáciiOdoslať spätnú väzbu (emailom)Nemáte nainštalovaného žiadneho e-mailového klienta
@@ -102,7 +102,7 @@
Prosím NENAHRÁVAJTE:- Selfies alebo fotky vašich priateľov\n- Obrázky prevzaté z internetu\n- Snímky obrazovky proprietárnych aplikáciiPríklad nahratia:
- - Názov: Opera v Sydney\n- Popis: Opera v Sydney - pohľad spoza zátoky\n- Kategórie: Sydney Opera House, Sydney Opera House from the west, Sydney Opera House remote views
+ - Názov: Opera v Sydney\n- Popis: Opera v Sydney - pohľad spoza zátoky\n- Kategórie: Sydney Opera House from the west, Sydney Opera House remote viewsPrispejte svojimi obrázkami. Pomôžte oživiť články na Wikipédií.Obrázky na Wikipédií pochádzajú z Wikimedia Commons.Vaše obrázky pomáhajú vzdelávať ľudí po celom svete.
diff --git a/app/src/main/res/values-skr/strings.xml b/app/src/main/res/values-skr/strings.xml
index 83f7fba67..f83eebb4a 100644
--- a/app/src/main/res/values-skr/strings.xml
+++ b/app/src/main/res/values-skr/strings.xml
@@ -14,6 +14,7 @@
تصدیق ناکام!اپ لوڈ شروع!%1$s اپ لوڈ تھی ڳیا!
+ آپݨی اپلوڈ ݙیکھݨ کیتے ٹیپ کرواپ لوڈ %1$s شروع تھیندا پئے%1$s اپ لوڈ تھیندا پئے%1$s اپ لوڈ پورا تھیندا پئے
@@ -54,6 +55,9 @@
رات آلا مزاجگھاٹا تھیم ورتوسی سی او
+ سی سی بی وائی ٣.٠
+ سی سی بی وائی۔ایس اے ٤.٠
+ سی سی بی وائی ٤.٠سی سی زیروبراہ مہربانی اپ لوڈ نہ کرومثال اپ لوڈ:
diff --git a/app/src/main/res/values-sr-el/error.xml b/app/src/main/res/values-sr-el/error.xml
new file mode 100644
index 000000000..80d668c8b
--- /dev/null
+++ b/app/src/main/res/values-sr-el/error.xml
@@ -0,0 +1,7 @@
+
+
+ Ostava se srušila
+ Ups! Nešto je pošlo naopako.
+ Recite nam šta ste radili pa to saznanje podelite s nama, putem e-pošte. Time ćete nam pomoći da rešimo problem!
+ Hvala vam!
+
diff --git a/app/src/main/res/values-sr-el/strings.xml b/app/src/main/res/values-sr-el/strings.xml
new file mode 100644
index 000000000..3cf62f69c
--- /dev/null
+++ b/app/src/main/res/values-sr-el/strings.xml
@@ -0,0 +1,208 @@
+
+
+ Ostava
+ Podešavanja
+ Korisničko ime
+ Lozinka
+ Prijavi me
+ Otvori nalog
+ Prijavljivanje
+ Sačekajte…
+ Uspešno ste prijavljeni.
+ Prijavljivanje nije uspelo.
+ Datoteka nije pronađena. Pokušajte sa drugom datotekom.
+ Provera identiteta nije uspela.
+ Otpremanje je započeto.
+ Datoteka „%1$s“ je otpremljena.
+ Tapnite da biste videli otpremanje
+ Počinjem sa otpremanjem datoteke „%1$s“
+ Otpremanje datoteke „%1$s“
+ Završavam sa otpremanjem datoteke „%1$s“
+ Ne mogu da otpremim „%1$s“
+ Tapnite da biste videli
+
+ %d datoteka se otprema
+ %d datoteke se otpremaju
+
+ Moja skorašnja otpremanja
+ Na čekanju
+ Nije uspelo
+ %1$d%% otpremljeno
+ Otpremam
+ Iz galerije
+ Fotografiši
+ U blizini
+ Moja otpremanja
+ Deli
+ Otvori u pregledaču
+ Naslov
+ Opis
+ Ne mogu da vas prijavim – mreža ne radi
+ Ne mogu da vas prijavim – proverite svoje korisničko ime
+ Ne mogu da vas prijavim – proverite svoju lozinku
+ Previše neuspešnih pokušaja. Probajte ponovo za nekoliko minuta.
+ Nažalost, korisnik je blokiran na Ostavi
+ Morate uneti svoj dvofaktorski kod za autentifikaciju.
+ Prijava nije uspela
+ Otpremi
+ Dajte ime ovom kompletu
+ Izmene
+ Otpremi
+ Pretraži kategorije
+ Sačuvaj
+ Osveži
+ GPS je onemogućen na Vašem uređaju. Želite li ga omogućiti?
+ Omogući GPS
+ Još uvek nema otpremanja
+
+ \@string/contributions_subtitle_zero
+ %d otpremanje
+ %d otpremanja
+
+
+ Započni %d otpremanje
+ Započni %d otpremanja
+
+
+ %d otpremanje
+ %d otpremanja
+
+ Nema kategorija koje odgovaraju %1$s
+ Dodajte kategorije na slike da biste olakšali korisnicima njihovo pronalaženje na Ostavi.\n\nDa biste dodali kategoriju, počnite sa pisanjem njenog imena.
+ Kategorije
+ Postavke
+ Otvori nalog
+ O aplikaciji
+ Softver otvorenog koda dostupan pod licencom <a href=\"https://github.com/commons-app/apps-android-commons/blob/master/COPYING\">Apache ver. 2</a> Vikimedijina Ostava i njen logo su zaštitni znaci Vikimedijine Fondacije i koriste se sa dozvolom Vikimedijine Fondacine. Mi ne odobravamo ili podržavmo Vikimedijinu Fondaciju.\n\nAplikacija za Vikimedijinu ostavu je aplikacija otvorenog koda koja je napravljena i koja se održava pomoću grantova i volontera Vikimedijine zajednice. Zadužbina Vikimedija nije uključena u stvaranje, razvoj ili održavanje aplikacije.
+ <a href=\"https://github.com/commons-app/apps-android-commons\">Izvorni kôd</a> i <a href=\"https://commons-app.github.io/\">veb-sajt</a> na GitHub-u. Napravite novi <a href=\"https://github.com/commons-app/apps-android-commons/issues\">zahtev na GitHub-u</a> da biste prijavili greške ili dali predloge.
+ <a href=\"https://github.com/commons-app/apps-android-commons/wiki/Privacy-policy\">Politika privatnosti</a>
+ <a href=\"https://github.com/commons-app/apps-android-commons/blob/master/CREDITS\">Zasluge</a>
+ O aplikaciji
+ Pošaljite povratne informacije (putem e-pošte)
+ Nije instaliran imejl klijent
+ Nedavno korišćene kategorije
+ Čekam na prvu sinhronizaciju…
+ Još niste otpremili nijednu fotografiju.
+ Pokušaj ponovo
+ Otkaži
+ Slika će se voditi pod licencom %1$s
+ Slanjem ove slike, ja tvrdim da je u pitanju moj rad, da ne sadrži materijal ili selfije zaštićene autorskim pravima, te da je na ostale načine u skladu sa <a href=\"https://commons.wikimedia.org/wiki/Commons:Policies_and_guidelines\">smernicama Vikimedijine ostave</a>.
+ Preuzmi
+ Licenca
+ Koristi prethodan naslov/opis
+ Automatski detektuj trenutnu lokaciju
+ Primi trenutnu lokaciju da bi predložili kategoriju ako slika nije geografski označena
+ Noćni režim
+ Koristiti tamnu temu
+ Autorstvo-Deliti pod istim uslovima 4.0
+ Autorstvo 4.0
+ Autorstvo-Deliti pod istim uslovimau 3.0
+ Autorstvo 3.0
+ CC0
+ CC BY-SA 3.0
+ CC BY-SA 3.0 (Austrija)
+ CC BY-SA 3.0 (Nemačka)
+ CC BY-SA 3.0 (Estonija)
+ CC BY-SA 3.0 (Španija)
+ CC BY-SA 3.0 (Hrvatska)
+ CC BY-SA 3.0 (Luksemburg)
+ CC BY-SA 3.0 (Holandija)
+ CC BY-SA 3.0 (Norveška)
+ CC BY-SA 3.0 (Poljska)
+ CC BY-SA 3.0 (Rumunija)
+ CC BY 3.0
+ CC BY-SA 4.0
+ CC BY 4.0
+ CC Nula
+ Vikimedijina Ostava sadrži većinu slika koja se koristi na Vikipediji.
+ Vaše slike pomažu u obrazovanju ljudi širom sveta.
+ Molimo Vas da postavite slike koje ste preuzeli ili kreirali u potpunosti sami:
+ - Prirodne objekte (cveće, životinje, planine)\n- Korisne objekte (bicikle, železničke stanice)\n- Poznate ljude (vaš gradonačelnik, Olimpijce koje ste sreli)
+ Molimo NE otpremajte:
+ - Selfije i slike tvojih prijatelja\n- Slike koje ste preuzeli sa interneta\n- Skrinšotove iz sopstvenih aplikacija
+ Primer otpremanja:
+ — Naslov: Sidnejska opera\n— Opis: Sidnejska opera, pogled preko zaliva\n— Kategorije: Sidnejska opera sa zapada, pogledi na Sidnejksu operu iz daljine
+ Delite svoje slike. Oživite članke na Vikipediji!
+ Slike na Vikipediji dolaze iz Ostave.
+ Sa vašim slikama pomažete u obrazovanju ljudi širom sveta.
+ Izbegavajte materijale koje ste našli na internetu, kao i slike plakata, korica knjiga itd.
+ Jeste li razumeli?
+ Jesam!
+ Kategorije
+ Učitavam…
+ Ništa nije izabrano
+ Nema opisa
+ Nepoznata licenca
+ Osveži
+ Potrebna dozvola: Provera spoljašnje memorije. Aplikacija bez ovoga ne može da funkcioniše.
+ Neophodna dozvola: Pisanje spoljašnjeg skladišta. Aplikacija ne može da funkcioniše bez ovoga.
+ Opciona dozvola: Preuzmi trenutnu lokaciju za predloge kategorija
+ U redu
+ Mesta u blizini
+ Nisu pronađena obližnja mesta
+ Upozorenje
+ Ova datoteka je već dostupna na Ostavi. Da li ste sigurni da želite da nastavite?
+ Da
+ Ne
+ Naslov
+ Naslov medija
+ Opis
+ Opis datoteke ide ovde. Može da bude poprilično dug i prikazivaće se u više redova. Nadamo se da će izgledati lepo.
+ Datum otpremanja
+ Licenca
+ Koordinate
+ Ništa nije uneto
+ Postani Beta Tester
+ Priključite se na naš beta kanal na Gugl pleju i pristupajte novim informacijama i popravkama bagova
+ Koristi Vikipodatke
+ (Upozorenje: onemogućavanjem ovoga može se izazvati velika potrošnja mobilnih podataka)
+ 2FA kod
+ Moj limit za skorašnja otpremanja
+ Maksimalni limit
+ Nije moguće prikazati više od 500
+ Postavi limit za skorašnja otpremanja
+ Dvofaktorska autentifikacija trenutno nije podržana.
+ Zaista želite da se odjavite?
+ Logo Ostave
+ Pozadinska slika
+ Medijska slika neuspešna
+ Slika nije pronađena
+ Otpremi sliku
+ Planina Zao
+ Lame
+ Dugin most
+ Tulipan
+ Bez selfija
+ Vlasnička slika
+ Dobrodošlica Vikipediji
+ Dobrodošlica za autorska prava
+ Sidnejska opera
+ Otkaži
+ Otvori
+ Zatvori
+ Početna
+ Otpremanje
+ U blizini
+ O nama
+ Podešavanja
+ Povratne informacije
+ Odjavi me
+ Tutorijal
+ Obaveštenja
+ Obližnja mesta ne mogu da se prikazuju bez dozvola za lokaciju
+ opis nije pronađen
+ Stranica datoteke na Ostavi
+ Stavka na Vikipodacima
+ Greška pri keširanju slika
+ Jedinstven opisni naslov za datoteku, koji će biti ime datoteke. Možete da koristite obični jezik sa razmacima. Ne treba unositi ekstenziju datoteke
+ Molimo da opišete datoteku koliko je to moguće: Gde je napravljena? Šta prikazuje? Šta je kontekst? Opišite objekte i/ili osobe. Otkrijte informacije koje se ne mogu lako pogoditi, na primer doba dana ako je u pitanju pejzaž. Ako datoteka prikazuje nešto neobično, molimo da objasnite šta je to čini neobičnom.
+ Davanje dozvole
+ Upotreba spoljašnjeg skladišta
+ Spremanje slika napravljenih kamerom aplikacije na Vašem uređaju
+ Pošalji dnevničku datoteku
+ Pošalji dnevničku datoteku developerima preko imejla
+ Prijavite se na svoj nalog
+ Lokacija nije promenjena.
+ Lokacija nije dostupna.
+ Potrebna je dozvola za prikazivanje liste lokacija u blizini
+
diff --git a/app/src/main/res/values-sr/error.xml b/app/src/main/res/values-sr/error.xml
index 0cd823ccd..f8287ca14 100644
--- a/app/src/main/res/values-sr/error.xml
+++ b/app/src/main/res/values-sr/error.xml
@@ -3,5 +3,5 @@
Остава се срушилаУпс! Нешто је пошло наопако.Реците нам шта сте радили па то сазнање поделите с нама, путем е-поште. Тиме ћете нам помоћи да решимо проблем!
- Хвала вам!
+ Хвала Вам!
diff --git a/app/src/main/res/values-sr/strings.xml b/app/src/main/res/values-sr/strings.xml
index 8b58994e8..fac230522 100644
--- a/app/src/main/res/values-sr/strings.xml
+++ b/app/src/main/res/values-sr/strings.xml
@@ -17,9 +17,9 @@
Тапните да бисте видели отпремањеПочињем са отпремањем датотеке „%1$s“Отпремање датотеке „%1$s“
- Завршавам са отпремањем датотеке „%1$s“
- Не могу да отпремим „%1$s“
- Тапните да бисте видели
+ Завршавање отпремања датотеке „%1$s“
+ Отпремање „%1$s“ неуспешно
+ Додирните да бисте видели%d датотека се отпрема%d датотеке се отпремају
@@ -28,21 +28,21 @@
На чекањуНије успело%1$d%% отпремљено
- Отпремам
+ ОтпремањеИз галеријеФотографишиУ близиниМоја отпремања
- Дели
+ ПоделиОтвори у прегледачуНасловОпис
- Не могу да вас пријавим – мрежа не ради
- Не могу да вас пријавим – проверите своје корисничко име
- Не могу да вас пријавим – проверите своју лозинку
+ Неуспешно пријављивање – грешка на мрежи
+ Неуспешно пријављивање – проверите Ваше корисничко име
+ Неуспешно пријављивање – проверите Вашу лозинкуПревише неуспешних покушаја. Пробајте поново за неколико минута.
- Нажалост, корисник је блокиран на Остави
- Морате унети свој двофакторски код за аутентификацију.
+ Нажалост, овај корисник је блокиран на Остави
+ Морате унети Ваш двофакторски код за аутентификацију.Пријава није успелаОтпремиДајте име овом комплету
@@ -68,9 +68,9 @@
%d отпремањаНема категорија које одговарају %1$s
- Додајте категорије на слике да бисте олакшали корисницима њихово проналажење на Остави.\n\nДа бисте додали категорију, почните са писањем њеног имена.\nТапните на ову поруку (или притисните назад) да бисте прескочили овај корак.
+ Додајте категорије на слике да бисте олакшали корисницима њихово проналажење на Остави.\n\nДа бисте додали категорију, почните са писањем њеног имена.Категорије
- Поставке
+ ПодешавањаОтвори налогО апликацијиСофтвер отвореног кода доступан под лиценцом <a href=\"https://github.com/commons-app/apps-android-commons/blob/master/COPYING\">Apache вер. 2</a> Викимедијина Остава и њен лого су заштитни знаци Викимедијине Фондације и користе се са дозволом Викимедијине Фондацине. Ми не одобравамо или подржавмо Викимедијину Фондацију.\n\nАпликација за Викимедијину оставу је апликација отвореног кода која је направљена и која се одржава помоћу грантова и волонтера Викимедијине заједнице. Задужбина Викимедија није укључена у стварање, развој или одржавање апликације.
@@ -81,8 +81,8 @@
Пошаљите повратне информације (путем е-поште)Није инсталиран имејл клијентНедавно коришћене категорије
- Чекам на прву синхронизацију…
- Још нисте отпремили ниједну фотографију.
+ Чека се прва синхронизација…
+ Још увек нисте отпремили ниједну фотографију.Покушај поновоОткажиСлика ће се водити под лиценцом %1$s
@@ -96,7 +96,7 @@
Користити тамну темуАуторство-Делити под истим условима 4.0Ауторство 4.0
- Ауторство-Делити под истим условимаu 3.0
+ Ауторство-Делити под истим условима 3.0Ауторство 3.0CC0CC BY-SA 3.0
@@ -115,28 +115,28 @@
CC BY 4.0CC НулаВикимедијина Остава садржи већину слика која се користи на Википедији.
- Ваше слике помажу у образовању људи широм света.
+ Ваше слике помажу у образовању људи широм света!Молимо Вас да поставите слике које сте преузели или креирали у потпуности сами:- Природне објекте (цвеће, животиње, планине)\n- Корисне објекте (бицикле, железничке станице)\n- Познате људе (ваш градоначелник, Олимпијце које сте срели)Молимо НЕ отпремајте:
- - Селфије и слике твојих пријатеља\n- Слике које сте преузели са интернета\n- Скриншотове из сопствених апликација
+ - Селфије и слике Ваших пријатеља\n- Слике које Сте преузели са интернета\n- Скриншотове из сопствених апликацијаПример отпремања:
- — Наслов: Сиднејска опера\n— Опис: Сиднејска опера, поглед преко залива\n— Категорије: Сиднејска опера, Сиднејска опера са запада, погледи на Сиднејксу оперу из даљине
- Делите своје слике. Оживите чланке на Википедији!
+ — Наслов: Сиднејска опера\n— Опис: Сиднејска опера, поглед преко залива\n— Категорије: Сиднејска опера са запада, погледи на Сиднејксу оперу из даљине
+ Делите Ваше слике. Оживите чланке на Википедији!Слике на Википедији долазе из Оставе.
- Са вашим сликама помажете у образовању људи широм света.
+ Ваше слике помажу у образовању људи широм света.Избегавајте материјале које сте нашли на интернету, као и слике плаката, корица књига итд.Јесте ли разумели?Јесам!Категорије
- Учитавам…
+ Учитавање...Ништа није изабраноНема описаНепозната лиценцаОсвежи
- Потребна дозвола: Провера спољашње меморије. Апликација без овога не може да функционише.
- Неопходна дозвола: Писање спољашњег складишта. Апликација не може да функционише без овога.
- Опциона дозвола: Преузми тренутну локацију за предлоге категорија
+ Потребна дозвола: читање спољашње меморије. \nАпликација не може да функционише без овога.
+ Потребна дозвола: писање у спољашњој меморији. \nАпликација не може да функционише без овога.
+ Опциона дозвола: преузми тренутну локацију за предлоге категоријаУ редуМеста у близиниНису пронађена оближња места
@@ -152,10 +152,10 @@
ЛиценцаКоординатеНишта није унето
- Постани Бета Тестер
- Прикључите се на наш бета канал на Гугл плеју и приступајте новим информацијама и поправкама багова
+ Постани бета тестер!
+ Прикључите се на наш бета канал на Гугл плеју и приступите новим информацијама и исправкама баговаКористи Википодатке
- (Упозорење: онемогућавањем овога може се изазвати велика потрошња мобилних података)
+ (Упозорење: онемогућавање овога може изазвати велику потрошњу мобилних података)2FA кодМој лимит за скорашња отпремањаМаксимални лимит
@@ -181,14 +181,15 @@
ОтвориЗатвориПочетна
- Отпремање
+ ОтпремиУ близиниО намаПодешавањаПовратне информацијеОдјави меТуторијал
- Оближња места не могу да се приказују без дозвола за локацију
+ Обавештења
+ Оближња места не могу да се приказују без дозволе за локацијуопис није пронађенСтраница датотеке на ОставиСтавка на Википодацима
@@ -196,9 +197,14 @@
Јединствен описни наслов за датотеку, који ће бити име датотеке. Можете да користите обични језик са размацима. Не треба уносити екстензију датотекеМолимо да опишете датотеку колико је то могуће: Где је направљена? Шта приказује? Шта је контекст? Опишите објекте и/или особе. Откријте информације које се не могу лако погодити, на пример доба дана ако је у питању пејзаж. Ако датотека приказује нешто необично, молимо да објасните шта је то чини необичном.Давање дозволе
- Употреба спољашњег складишта
+ Употреба спољашње меморијеСпремање слика направљених камером апликације на Вашем уређајуПошаљи дневничку датотекуПошаљи дневничку датотеку девелоперима преко имејла
- Пријавите се на свој налог
+ Пријавите се на Ваш налог
+ Локација није промењена.
+ Локација није доступна.
+ Потребна је дозвола за приказивање листе локација у близини
+ Добити упутства
+ Прочитај чланак
diff --git a/app/src/main/res/values-su/strings.xml b/app/src/main/res/values-su/strings.xml
index 4bce643d5..47207ed1a 100644
--- a/app/src/main/res/values-su/strings.xml
+++ b/app/src/main/res/values-su/strings.xml
@@ -121,7 +121,7 @@
Wayahna ULAH ngunjal:- Sélpi atawa poto sobat anjeun\n- Poto anu diundeur ti Internét\n- Poto layar aplikasiConto unjalan:
- - Judul: Gedung Opera Sydney\n- Pedaran: Gedung Opera Sydney (Sydney Opera House) ditempo ti basisir peuntas\n- Kategori: Sydney Opera House, Sydney Opera House from the west, Sydney Opera House remote views
+ - Judul: Gedung Opera Sydney\n- Pedaran: Gedung Opera Sydney (Sydney Opera House) ditempo ti basisir peuntas\n- Kategori: Sydney Opera House, Sydney Opera House from the west, Sydney Opera House remote viewsSumbangkeun gambar Anjeun. Bantuan ngahirupkeun artikel Wikipédia!Gambar di Wikipédia asalna ti Wikimedia Commons.Gambar-gambar anjeun ngabantu ngatik jalma di sakuliah dunya.
diff --git a/app/src/main/res/values-sv/strings.xml b/app/src/main/res/values-sv/strings.xml
index 7755b33ce..79d3c36f7 100644
--- a/app/src/main/res/values-sv/strings.xml
+++ b/app/src/main/res/values-sv/strings.xml
@@ -68,7 +68,7 @@
%d uppladdningarInga kategorier som stämmer överens med %1$s hittades
- Lägg till kategorier för att göra dina bilder synliga på Wikimedia Commons.\n\nBörja skriva för att lägga till kategorier.\nTryck på detta meddelande (eller gå tillbaka) för att hoppa över detta steg.
+ Lägg till kategorier för att göra dina bilder enklare att hitta på Wikimedia Commons.\n\nBörja skriva för att lägga till kategorier.KategorierInställningarRegistrera
@@ -121,7 +121,7 @@
Ladda INTE upp:- Selfies eller bilder på dina vänner\n- Bilder som du har laddat ned från Internet\n- Skärmdumpar på proprietära apparUppladdningsexempel:
- - Titel: Sydneys operahus\n- Beskrivning: Sydneys operahus sedd från andra sidan bukten\n- Kategorier: Sydneys operahus, Sydneys operahus från väst, Sydneys operahus utifrån
+ - Titel: Sydneys operahus\n- Beskrivning: Sydneys operahus sedd från andra sidan bukten\n- Kategorier: Sydneys operahus från väst, Sydneys operahus utifrånBidra med dina bilder. Hjälp Wikipedia-artiklar att komma till liv!Bilder på Wikipedia kommer från Wikimedia Commons.Dina bilder hjälper till att utbilda människor runt om i världen.
@@ -188,6 +188,7 @@
ÅterkopplingLogga utGuide
+ MeddelandenPlatser i närheten kan inte visas utan platsbehörigheteringen beskrivning hittadesCommons-filsida
@@ -204,4 +205,6 @@
Platsen har inte ändrats.Platsen är inte tillgänglig.Behörighet krävs för att visa en lista över platser i närheten
+ FÅ VÄGBESKRIVNINGAR
+ LÄS ARTIKEL
diff --git a/app/src/main/res/values-te/strings.xml b/app/src/main/res/values-te/strings.xml
index 2fc509d31..bfd0e79f2 100644
--- a/app/src/main/res/values-te/strings.xml
+++ b/app/src/main/res/values-te/strings.xml
@@ -1,10 +1,11 @@
- వికీమీడియా కామన్స్
+ కామన్స్అమరికలువాడుకరిపేరుసంకేతపదంలాగినవండి
+ నమోదవ్వండిలాగినవుతున్నారువేచివుండండి…లాగిన్ విజయవంతమైంది!
@@ -25,6 +26,7 @@
ఎక్కిస్తున్నాంప్రదర్శనశాల నుంచిఫోటో తీయండి
+ చుట్టుపక్కలనా ఎక్కింపులుపంచుకోండివిహారిణిలో చూపు
@@ -33,7 +35,7 @@
లాగిన్ చెయ్యలేకపోయాం - నెట్వర్కు విఫలంలాగిన్ చెయ్యలేకపోయాం - మీ వాడుకరిపేరును సరిచూసుకోండిలాగిన్ చెయ్యలేకపోయాం - మీ సంకేతపదాన్ని సరిచూసుకోండి
- మరీ ఎక్కువ విఫల యత్నాలు చేసారు. కొద్ది నిముషాలాగి ప్రయత్నించండి
+ మరీ ఎక్కువ విఫల యత్నాలు చేసారు. కొద్ది నిముషాలాగి ప్రయత్నించండిఈ వాడుకరి కామన్స్ లో నిరోధించబడ్డారు, సారీ.లాగిన్ విఫలమైందిఎక్కింపు
@@ -46,6 +48,7 @@
వికీమీడియా కామన్స్ లో వెతికేటపుడు మీ బొమ్మలు మరింత సులువుగా కనబడేందుకు వాటికి వర్గాలను చేర్చండి.\n\nవర్గాలను చేర్చేందుకు టైపండి.\nఈ అంగను దాటేసి ముందుకు పోయేందుకు, ఈ సందేశాన్ని నొక్కండి (లేదా ’బ్యాక్’ నొక్కండి)వర్గాలుఅమరికలు
+ నమోదవ్వండిగురించిఓపెన్ సోర్సు సాఫ్టువేరు <a href=\"https://github.com/commons-app/apps-android-commons/blob/master/COPYING\">Apache License v2</a> కు లోబడి విడుదలైంది<a href=\"https://wikimediafoundation.org/wiki/Privacy_policy\">గోప్యతా విధానం</a>
@@ -86,9 +89,20 @@
వివరణ లేదుతెలియని లైసెన్సుతాజాకరించు
+ సరేహెచ్చరికఅవునుకాదుశీర్షికవివరణ
+ లైసెన్సు
+ గరిష్ఠ పరిమితి
+ రద్దుచేయి
+ మూసివేయి
+ ముంగిలి
+ ఎక్కించు
+ గురించి
+ అమరికలు
+ ప్రతిస్పందన
+ గమనింపులు
diff --git a/app/src/main/res/values-tr/strings.xml b/app/src/main/res/values-tr/strings.xml
index d7288a1e8..8becb6ee7 100644
--- a/app/src/main/res/values-tr/strings.xml
+++ b/app/src/main/res/values-tr/strings.xml
@@ -188,6 +188,7 @@
Geri bildirimÇıkışEğitim
+ BildirimlerYakındaki yerler, konum izinleri olmadan görüntülenemezhiçbir açıklama bulunamadıCommons dosya sayfası
@@ -204,4 +205,6 @@
Konum değiştirilmediKonum kullanılamıyor.Yakındaki yerler listesini görüntülemek için izin vermeniz gerekiyor
+ TALİMATLAR
+ MADDE OKU
diff --git a/app/src/main/res/values-uk/strings.xml b/app/src/main/res/values-uk/strings.xml
index 686ce79fd..d2e65640e 100644
--- a/app/src/main/res/values-uk/strings.xml
+++ b/app/src/main/res/values-uk/strings.xml
@@ -129,7 +129,7 @@
Будь ласка, НЕ завантажуйте:u2022 Селфі або фото своїх друзів \nu2022 Зображення, які Ви завантажили з інтернету \nu2022 Скріншоти патентованих програмПриклад завантаження:
- u2022 Назва: Сіднейський оперний театр \nu2022 Опис: Вид на Сіднейський оперний театр з боку бухти \nu2022 Категорії: Sydney Opera House, Sydney Opera House from the west, Sydney Opera House remote views
+ u2022 Назва: Сіднейський оперний театр \nu2022 Опис: Вид на Сіднейський оперний театр з боку бухти \nu2022 Категорії: Sydney Opera House, Sydney Opera House from the west, Sydney Opera House remote viewsНадсилайте Ваші зображення. Допоможіть оживити статті Вікіпедії!Зображення у Вікіпедії надходять з Вікісховища.Ваші зображення допомагають освіті людей у всьому світі.
diff --git a/app/src/main/res/values-vi/strings.xml b/app/src/main/res/values-vi/strings.xml
index 64bdd4f59..4c81d74c0 100644
--- a/app/src/main/res/values-vi/strings.xml
+++ b/app/src/main/res/values-vi/strings.xml
@@ -111,7 +111,7 @@
Xin DỪNG tải lên:- Hình tự sướng hoặc hình bạn bè\n- Hình ảnh tải về từ Internet\n- Ảnh chụp màn hình của ứng dụng thương mạiTập tin tải lên ví dụ:
- - Tiêu đề: Nhà hát Opera Sydney\n- Miêu tả: Nhà hát Opera Sydney nhìn qua cảng\n- Thể loại: Sydney Opera House, Sydney Opera House from the west, Sydney Opera House remote views
+ - Tiêu đề: Nhà hát Opera Sydney\n- Miêu tả: Nhà hát Opera Sydney nhìn qua cảng\n- Thể loại: Sydney Opera House, Sydney Opera House from the west, Sydney Opera House remote viewsĐóng góp hình ảnh của bạn. Làm sinh động các bài viết Wikipedia!Các hình ảnh trên Wikipedia được cung cấp bởi Wikimedia Commons.Các hình ảnh của bạn giúp giáo dục người dân trên khắp thế giới.
diff --git a/app/src/main/res/values-xmf/strings.xml b/app/src/main/res/values-xmf/strings.xml
index 23933ab25..e54a7395a 100644
--- a/app/src/main/res/values-xmf/strings.xml
+++ b/app/src/main/res/values-xmf/strings.xml
@@ -105,7 +105,7 @@
ქორთხინთ ვეხარგათ:- სელფი ვარდა მაჸალეეფიშ სურათეფი\n- ინტერნეტშე გჷმოხარგილი ფოტოეფი\n- ვადუდიშული აპლიკაციეფიშ სკრინშოტეფიეხარგუაშ მინუში:
- - დუდჯოხო: სიდნეიშ ოპერაშ თეატრი\n- ეჭარუა: სიდნეიშ ოპერაშ თეატრიშ მიოჯინი ლუბაშე\n- კატეგორიეფი: Sydney Opera House, Sydney Opera House from the west, Sydney Opera House remote views
+ - დუდჯოხო: სიდნეიშ ოპერაშ თეატრი\n- ეჭარუა: სიდნეიშ ოპერაშ თეატრიშ მიოჯინი ლუბაშე\n- კატეგორიეფი: Sydney Opera House, Sydney Opera House from the west, Sydney Opera House remote viewsგეხარგეთ თქვანი ფოტოეფი. ქემეხვარით ვიკიპედიაშ სტატიეფიშ გაჭყანიერებას!ვიკიპედიაშ ფოტოეფი ვიკიოწკარუეს იჩუალუაფუ.თქვანი სურათეფი კათაშ გონათუას ოხვარუ ედომუშამ მოსოფელს.
diff --git a/app/src/main/res/values-zh-rTW/strings.xml b/app/src/main/res/values-zh-rTW/strings.xml
index da8eca4ca..7242f0f6b 100644
--- a/app/src/main/res/values-zh-rTW/strings.xml
+++ b/app/src/main/res/values-zh-rTW/strings.xml
@@ -68,13 +68,13 @@
%d 次上傳沒有發現與 %1$s 相符的分類
- 為您的圖片添加分類,使別人在維基共享資源更容易找到。\n\n開始輸入以添加分類。\n按此(或按返回)跳過此步驟。
+ 為您的圖片添加分類,使別人在維基共享資源更容易找到。\n\n開始輸入以添加分類。分類設定註冊關於
- 維基共享資源應用程式是透過維基媒體社群上的受讓人、與志願者們所建立及維護的開放原始碼應用程式。維基媒體基金會並不涉及此應用程式的建立、開發、與維護方面。
- <a href=\"https://github.com/commons-app/apps-android-commons\">原始碼</a>和<a href=\"https://commons-app.github.io/\">網站</a>位於GitHub上。建立新的<a href=\"https://github.com/commons-app/apps-android-commons/issues\">GitHub問題</a>來回報問題和提出建議。
+ 維基共享資源應用程式是透過維基媒體社群上的受讓人,與志願者們所建立及維護的開放原始碼應用程式。維基媒體基金會並不涉及此應用程式的建立、開發,與維護方面。
+ <a href=\"https://github.com/commons-app/apps-android-commons\">原始碼</a>和<a href=\"https://commons-app.github.io/\">網站</a>位於 GitHub 上。建立新的<a href=\"https://github.com/commons-app/apps-android-commons/issues\"> GitHub 問題</a>來回報問題和提出建議。<a href=\"https://github.com/commons-app/apps-android-commons/wiki/Privacy-policy\">隱私方針</a><a href=\"https://github.com/commons-app/apps-android-commons/blob/master/CREDITS\">製作群</a>關於
@@ -94,11 +94,11 @@
若圖片未有地理標記,就以目前位置來作為分類建議。夜間模式使用暗黑佈景主題
- 姓名標示-相同方式分享4.0
+ 姓名標示-相同方式分享4.0姓名標示4.0 姓名標示-相同方式分享3.0姓名標示3.0
- CC0
+ 公眾領域貢獻宣告CC姓名標示-相同方式分享3.0CC姓名標示-相同方式分享3.0(奧地利)CC姓名標示-相同方式分享3.0(德國)
@@ -113,7 +113,7 @@
CC姓名標示3.0創用CC姓名標示-相同方式分享4.0創用CC姓名標示4.0
- CC0
+ 公眾領域貢獻宣告維基共享資源管理大部份使用在維基百科的圖片。您的圖片幫助教育了世界上的人們!請上傳完全由您自己拍攝或創作的圖片:
@@ -121,7 +121,7 @@
請*不要*上傳:- 您自己的自拍照,或您朋友的照片\n- 從網路下載來的圖片\n- 專利版權應用程式的截取圖片上傳範例:
- - 標題:雪梨歌劇院\n- 說明:從對面海灣所看到的雪梨歌劇院\n- 分類:Sydney Opera House, Sydney Opera House from the west, Sydney Opera House remote views
+ - 標題:雪梨歌劇院\n- 說明:從對面海灣所看到的雪梨歌劇院\n- 分類:Sydney Opera House from the west、Sydney Opera House remote views貢獻您的圖片,使維基百科的文章更加生動!維基百科的圖片,來自維基共享資源。您的圖片可以幫助教育世界各地的人。
@@ -188,6 +188,7 @@
意見回饋登出教程
+ 通知附近地點需要位置權限才可顯示找不到說明共享資源檔案頁面
@@ -204,4 +205,6 @@
位置無法更改。位置無效。需權限來顯示附近地點清單
+ 取得導向
+ 讀取條目
diff --git a/app/src/main/res/values-zh/strings.xml b/app/src/main/res/values-zh/strings.xml
index cba13eb68..631a7df69 100644
--- a/app/src/main/res/values-zh/strings.xml
+++ b/app/src/main/res/values-zh/strings.xml
@@ -121,7 +121,7 @@
请不要上传:- 您朋友的自拍照或图片\n- 您从互联网下载的图片\n- 专利应用的截图示例上传:
- - 标题:悉尼歌剧院\n- 描述:从海湾对面看到的悉尼歌剧院\n- 分类:Sydney Opera House、Sydney Opera House from the west、Sydney Opera House remote views
+ - 标题:悉尼歌剧院\n- 描述:从海湾对面看到的悉尼歌剧院\n- 分类:Sydney Opera House from the west、Sydney Opera House remote views贡献您的图像。使维基百科的条目更加生动!维基百科上的图像来自维基共享资源。您的图像可以帮助教育世界各地的人。
@@ -188,6 +188,7 @@
反馈退出教程
+ 通知附近地点不能在没有位置权限的情况下显示找不到描述共享资源文件页面
@@ -204,4 +205,6 @@
位置没有更新。位置不可用。需要权限以显示附近地点列表
+ 获得指示
+ 阅读条目
diff --git a/app/src/main/res/values/dimens.xml b/app/src/main/res/values/dimens.xml
index 40c859118..992c1924c 100644
--- a/app/src/main/res/values/dimens.xml
+++ b/app/src/main/res/values/dimens.xml
@@ -1,4 +1,25 @@
240dp
+
+
+ 16dp
+ 16dp
+
+
+ 48dp
+ 32dp
+ 16dp
+ 8dp
+ 4dp
+
+
48dp
+ 24dp
+ 12dp
+
+
+ 24sp
+ 20sp
+ 16sp
+ 14sp
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index ca7b119c0..bcc8c3242 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -194,6 +194,7 @@
FeedbackLogoutTutorial
+ NotificationsNearby places cannot be displayed without location permissionsno description foundCommons file page
@@ -211,4 +212,6 @@
Location has not changed.Location not available.Permission required to display a list of nearby places
+ GET DIRECTIONS
+ READ ARTICLE
diff --git a/app/src/test/java/fr/free/nrw/commons/category/CategoryDaoTest.java b/app/src/test/java/fr/free/nrw/commons/category/CategoryDaoTest.java
new file mode 100644
index 000000000..1cf0c338b
--- /dev/null
+++ b/app/src/test/java/fr/free/nrw/commons/category/CategoryDaoTest.java
@@ -0,0 +1,294 @@
+package fr.free.nrw.commons.category;
+
+import android.content.ContentProviderClient;
+import android.content.ContentValues;
+import android.database.Cursor;
+import android.database.MatrixCursor;
+import android.database.sqlite.SQLiteDatabase;
+import android.net.Uri;
+import android.os.RemoteException;
+import android.support.annotation.NonNull;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.InOrder;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.annotation.Config;
+
+import java.util.Arrays;
+import java.util.Date;
+import java.util.List;
+
+import fr.free.nrw.commons.BuildConfig;
+import fr.free.nrw.commons.TestCommonsApplication;
+import fr.free.nrw.commons.category.CategoryDao.Table;
+
+import static fr.free.nrw.commons.category.CategoryContentProvider.BASE_URI;
+import static fr.free.nrw.commons.category.CategoryContentProvider.uriForId;
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertNull;
+import static junit.framework.Assert.assertTrue;
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyString;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Matchers.isA;
+import static org.mockito.Matchers.isNull;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.when;
+
+@RunWith(RobolectricTestRunner.class)
+@Config(constants = BuildConfig.class, sdk = 21, application = TestCommonsApplication.class)
+public class CategoryDaoTest {
+
+ @Mock
+ private ContentProviderClient client;
+ @Mock
+ private SQLiteDatabase database;
+ @Captor
+ private ArgumentCaptor captor;
+ @Captor
+ private ArgumentCaptor queryCaptor;
+
+ private CategoryDao testObject;
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+ testObject = new CategoryDao(() -> client);
+ }
+
+ @Test
+ public void createTable() {
+ Table.onCreate(database);
+ verify(database).execSQL(Table.CREATE_TABLE_STATEMENT);
+ }
+
+ @Test
+ public void deleteTable() {
+ Table.onDelete(database);
+ InOrder inOrder = Mockito.inOrder(database);
+ inOrder.verify(database).execSQL(Table.DROP_TABLE_STATEMENT);
+ inOrder.verify(database).execSQL(Table.CREATE_TABLE_STATEMENT);
+ }
+
+ @Test
+ public void migrateTableVersionFrom_v1_to_v2() {
+ Table.onUpdate(database, 1, 2);
+ // Table didnt exist before v5
+ verifyZeroInteractions(database);
+ }
+
+ @Test
+ public void migrateTableVersionFrom_v2_to_v3() {
+ Table.onUpdate(database, 2, 3);
+ // Table didnt exist before v5
+ verifyZeroInteractions(database);
+ }
+
+ @Test
+ public void migrateTableVersionFrom_v3_to_v4() {
+ Table.onUpdate(database, 3, 4);
+ // Table didnt exist before v5
+ verifyZeroInteractions(database);
+ }
+
+ @Test
+ public void migrateTableVersionFrom_v4_to_v5() {
+ Table.onUpdate(database, 4, 5);
+ verify(database).execSQL(Table.CREATE_TABLE_STATEMENT);
+ }
+
+ @Test
+ public void migrateTableVersionFrom_v5_to_v6() {
+ Table.onUpdate(database, 5, 6);
+ // Table didnt change in version 6
+ verifyZeroInteractions(database);
+ }
+
+ @Test
+ public void createFromCursor() {
+ MatrixCursor cursor = createCursor(1);
+ cursor.moveToFirst();
+ Category category = testObject.fromCursor(cursor);
+
+ assertEquals(uriForId(1), category.getContentUri());
+ assertEquals("foo", category.getName());
+ assertEquals(123, category.getLastUsed().getTime());
+ assertEquals(2, category.getTimesUsed());
+ }
+
+ @Test
+ public void saveExistingCategory() throws Exception {
+ MatrixCursor cursor = createCursor(1);
+ cursor.moveToFirst();
+ Category category = testObject.fromCursor(cursor);
+
+ testObject.save(category);
+
+ verify(client).update(eq(category.getContentUri()), captor.capture(), isNull(String.class), isNull(String[].class));
+ ContentValues cv = captor.getValue();
+ assertEquals(3, cv.size());
+ assertEquals(category.getName(), cv.getAsString(Table.COLUMN_NAME));
+ assertEquals(category.getLastUsed().getTime(), cv.getAsLong(Table.COLUMN_LAST_USED).longValue());
+ assertEquals(category.getTimesUsed(), cv.getAsInteger(Table.COLUMN_TIMES_USED).intValue());
+ }
+
+ @Test
+ public void saveNewCategory() throws Exception {
+ Uri contentUri = CategoryContentProvider.uriForId(111);
+ when(client.insert(isA(Uri.class), isA(ContentValues.class))).thenReturn(contentUri);
+ Category category = new Category(null, "foo", new Date(234L), 1);
+
+ testObject.save(category);
+
+ verify(client).insert(eq(BASE_URI), captor.capture());
+ ContentValues cv = captor.getValue();
+ assertEquals(3, cv.size());
+ assertEquals(category.getName(), cv.getAsString(Table.COLUMN_NAME));
+ assertEquals(category.getLastUsed().getTime(), cv.getAsLong(Table.COLUMN_LAST_USED).longValue());
+ assertEquals(category.getTimesUsed(), cv.getAsInteger(Table.COLUMN_TIMES_USED).intValue());
+ assertEquals(contentUri, category.getContentUri());
+ }
+
+ @Test(expected = RuntimeException.class)
+ public void testSaveTranslatesRemoteExceptions() throws Exception {
+ when(client.insert(isA(Uri.class), isA(ContentValues.class))).thenThrow(new RemoteException(""));
+ testObject.save(new Category());
+ }
+
+ @Test
+ public void whenTheresNoDataFindReturnsNull_nullCursor() throws Exception {
+ when(client.query(any(), any(), anyString(), any(), anyString())).thenReturn(null);
+
+ assertNull(testObject.find("foo"));
+ }
+
+ @Test
+ public void whenTheresNoDataFindReturnsNull_emptyCursor() throws Exception {
+ when(client.query(any(), any(), anyString(), any(), anyString())).thenReturn(createCursor(0));
+
+ assertNull(testObject.find("foo"));
+ }
+
+ @Test
+ public void cursorsAreClosedAfterUse() throws Exception {
+ Cursor mockCursor = mock(Cursor.class);
+ when(client.query(any(), any(), anyString(), any(), anyString())).thenReturn(mockCursor);
+ when(mockCursor.moveToFirst()).thenReturn(false);
+
+ testObject.find("foo");
+
+ verify(mockCursor).close();
+ }
+
+ @Test
+ public void findCategory() throws Exception {
+ when(client.query(any(), any(), anyString(), any(), anyString())).thenReturn(createCursor(1));
+
+ Category category = testObject.find("foo");
+
+ assertEquals(uriForId(1), category.getContentUri());
+ assertEquals("foo", category.getName());
+ assertEquals(123, category.getLastUsed().getTime());
+ assertEquals(2, category.getTimesUsed());
+
+ verify(client).query(
+ eq(BASE_URI),
+ eq(Table.ALL_FIELDS),
+ eq(Table.COLUMN_NAME + "=?"),
+ queryCaptor.capture(),
+ isNull(String.class)
+ );
+ assertEquals("foo", queryCaptor.getValue()[0]);
+ }
+
+ @Test(expected = RuntimeException.class)
+ public void findCategoryTranslatesExceptions() throws Exception {
+ when(client.query(any(), any(), anyString(), any(), anyString())).thenThrow(new RemoteException(""));
+ testObject.find("foo");
+ }
+
+ @Test(expected = RuntimeException.class)
+ public void recentCategoriesTranslatesExceptions() throws Exception {
+ when(client.query(any(), any(), anyString(), any(), anyString())).thenThrow(new RemoteException(""));
+ testObject.recentCategories(1);
+ }
+
+ @Test
+ public void recentCategoriesReturnsEmptyList_nullCursor() throws Exception {
+ when(client.query(any(), any(), anyString(), any(), anyString())).thenReturn(null);
+
+ assertTrue(testObject.recentCategories(1).isEmpty());
+ }
+
+ @Test
+ public void recentCategoriesReturnsEmptyList_emptyCursor() throws Exception {
+ when(client.query(any(), any(), anyString(), any(), anyString())).thenReturn(createCursor(0));
+
+ assertTrue(testObject.recentCategories(1).isEmpty());
+ }
+
+ @Test
+ public void cursorsAreClosedAfterRecentCategoriesQuery() throws Exception {
+ Cursor mockCursor = mock(Cursor.class);
+ when(client.query(any(), any(), anyString(), any(), anyString())).thenReturn(mockCursor);
+ when(mockCursor.moveToFirst()).thenReturn(false);
+
+ testObject.recentCategories(1);
+
+ verify(mockCursor).close();
+ }
+
+ @Test
+ public void recentCategoriesReturnsLessThanLimit() throws Exception {
+ when(client.query(any(), any(), anyString(), any(), anyString())).thenReturn(createCursor(1));
+
+ List result = testObject.recentCategories(10);
+
+ assertEquals(1, result.size());
+ assertEquals("foo", result.get(0));
+
+ verify(client).query(
+ eq(BASE_URI),
+ eq(Table.ALL_FIELDS),
+ isNull(String.class),
+ queryCaptor.capture(),
+ eq(Table.COLUMN_LAST_USED + " DESC")
+ );
+ assertEquals(0, queryCaptor.getValue().length);
+ }
+
+ @Test
+ public void recentCategoriesHomorsLimit() throws Exception {
+ when(client.query(any(), any(), anyString(), any(), anyString())).thenReturn(createCursor(10));
+
+ List result = testObject.recentCategories(5);
+
+ assertEquals(5, result.size());
+ }
+
+ @NonNull
+ private MatrixCursor createCursor(int rowCount) {
+ MatrixCursor cursor = new MatrixCursor(new String[]{
+ Table.COLUMN_ID,
+ Table.COLUMN_NAME,
+ Table.COLUMN_LAST_USED,
+ Table.COLUMN_TIMES_USED
+ }, rowCount);
+
+ for (int i = 0; i < rowCount; i++) {
+ cursor.addRow(Arrays.asList("1", "foo", "123", "2"));
+ }
+
+ return cursor;
+ }
+
+}
\ No newline at end of file
diff --git a/app/src/test/java/fr/free/nrw/commons/contributions/ContributionDaoTest.java b/app/src/test/java/fr/free/nrw/commons/contributions/ContributionDaoTest.java
new file mode 100644
index 000000000..15e37e640
--- /dev/null
+++ b/app/src/test/java/fr/free/nrw/commons/contributions/ContributionDaoTest.java
@@ -0,0 +1,370 @@
+package fr.free.nrw.commons.contributions;
+
+import android.content.ContentProviderClient;
+import android.content.ContentValues;
+import android.database.MatrixCursor;
+import android.database.sqlite.SQLiteDatabase;
+import android.net.Uri;
+import android.os.RemoteException;
+import android.support.annotation.NonNull;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.InOrder;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.annotation.Config;
+
+import java.util.Arrays;
+import java.util.Date;
+
+import fr.free.nrw.commons.BuildConfig;
+import fr.free.nrw.commons.TestCommonsApplication;
+import fr.free.nrw.commons.Utils;
+
+import static fr.free.nrw.commons.contributions.Contribution.SOURCE_CAMERA;
+import static fr.free.nrw.commons.contributions.Contribution.SOURCE_GALLERY;
+import static fr.free.nrw.commons.contributions.Contribution.STATE_COMPLETED;
+import static fr.free.nrw.commons.contributions.Contribution.STATE_QUEUED;
+import static fr.free.nrw.commons.contributions.ContributionDao.Table;
+import static fr.free.nrw.commons.contributions.ContributionsContentProvider.BASE_URI;
+import static fr.free.nrw.commons.contributions.ContributionsContentProvider.uriForId;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Matchers.isA;
+import static org.mockito.Matchers.isNull;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.when;
+
+@RunWith(RobolectricTestRunner.class)
+@Config(constants = BuildConfig.class, sdk = 21, application = TestCommonsApplication.class)
+public class ContributionDaoTest {
+
+ private static final String LOCAL_URI = "http://example.com/";
+ @Mock
+ private ContentProviderClient client;
+ @Mock
+ private SQLiteDatabase database;
+ @Captor
+ private ArgumentCaptor captor;
+
+ private Uri contentUri;
+ private ContributionDao testObject;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+
+ contentUri = uriForId(111);
+
+ testObject = new ContributionDao(() -> client);
+ }
+
+ @Test
+ public void createTable() {
+ Table.onCreate(database);
+ verify(database).execSQL(Table.CREATE_TABLE_STATEMENT);
+ }
+
+ @Test
+ public void deleteTable() {
+ Table.onDelete(database);
+
+ InOrder inOrder = Mockito.inOrder(database);
+ inOrder.verify(database).execSQL(Table.DROP_TABLE_STATEMENT);
+ inOrder.verify(database).execSQL(Table.CREATE_TABLE_STATEMENT);
+ }
+
+ @Test
+ public void upgradeDatabase_v1_to_v2() {
+ Table.onUpdate(database, 1, 2);
+
+ InOrder inOrder = Mockito.inOrder(database);
+ inOrder.verify(database).execSQL(Table.ADD_DESCRIPTION_FIELD);
+ inOrder.verify(database).execSQL(Table.ADD_CREATOR_FIELD);
+ }
+
+ @Test
+ public void upgradeDatabase_v2_to_v3() {
+ Table.onUpdate(database, 2, 3);
+
+ InOrder inOrder = Mockito.inOrder(database);
+ inOrder.verify(database).execSQL(Table.ADD_MULTIPLE_FIELD);
+ inOrder.verify(database).execSQL(Table.SET_DEFAULT_MULTIPLE);
+ }
+
+ @Test
+ public void upgradeDatabase_v3_to_v4() {
+ Table.onUpdate(database, 3, 4);
+
+ // No changes
+ verifyZeroInteractions(database);
+ }
+
+ @Test
+ public void upgradeDatabase_v4_to_v5() {
+ Table.onUpdate(database, 4, 5);
+
+ // No changes
+ verifyZeroInteractions(database);
+ }
+
+ @Test
+ public void upgradeDatabase_v5_to_v6() {
+ Table.onUpdate(database, 5, 6);
+
+ InOrder inOrder = Mockito.inOrder(database);
+ inOrder.verify(database).execSQL(Table.ADD_WIDTH_FIELD);
+ inOrder.verify(database).execSQL(Table.SET_DEFAULT_WIDTH);
+ inOrder.verify(database).execSQL(Table.ADD_HEIGHT_FIELD);
+ inOrder.verify(database).execSQL(Table.SET_DEFAULT_HEIGHT);
+ inOrder.verify(database).execSQL(Table.ADD_LICENSE_FIELD);
+ inOrder.verify(database).execSQL(Table.SET_DEFAULT_LICENSE);
+ }
+
+ @Test
+ public void saveNewContribution_nonNullFields() throws Exception {
+ when(client.insert(isA(Uri.class), isA(ContentValues.class))).thenReturn(contentUri);
+ Contribution contribution = createContribution(true, null, null, null, null);
+
+ testObject.save(contribution);
+
+ assertEquals(contentUri, contribution.getContentUri());
+ verify(client).insert(eq(BASE_URI), captor.capture());
+ ContentValues cv = captor.getValue();
+
+ // Long fields
+ assertEquals(222L, cv.getAsLong(Table.COLUMN_LENGTH).longValue());
+ assertEquals(321L, cv.getAsLong(Table.COLUMN_TIMESTAMP).longValue());
+ assertEquals(333L, cv.getAsLong(Table.COLUMN_TRANSFERRED).longValue());
+
+ // Integer fields
+ assertEquals(STATE_COMPLETED, cv.getAsInteger(Table.COLUMN_STATE).intValue());
+ assertEquals(640, cv.getAsInteger(Table.COLUMN_WIDTH).intValue());
+ assertEquals(480, cv.getAsInteger(Table.COLUMN_HEIGHT).intValue());
+
+ // String fields
+ assertEquals(SOURCE_CAMERA, cv.getAsString(Table.COLUMN_SOURCE));
+ assertEquals("desc", cv.getAsString(Table.COLUMN_DESCRIPTION));
+ assertEquals("create", cv.getAsString(Table.COLUMN_CREATOR));
+ assertEquals("007", cv.getAsString(Table.COLUMN_LICENSE));
+ }
+
+ @Test
+ public void saveNewContribution_nullableFieldsAreNull() throws Exception {
+ when(client.insert(isA(Uri.class), isA(ContentValues.class))).thenReturn(contentUri);
+ Contribution contribution = createContribution(true, null, null, null, null);
+
+ testObject.save(contribution);
+
+ assertEquals(contentUri, contribution.getContentUri());
+ verify(client).insert(eq(BASE_URI), captor.capture());
+ ContentValues cv = captor.getValue();
+
+ // Nullable fields are absent if null
+ assertFalse(cv.containsKey(Table.COLUMN_LOCAL_URI));
+ assertFalse(cv.containsKey(Table.COLUMN_IMAGE_URL));
+ assertFalse(cv.containsKey(Table.COLUMN_UPLOADED));
+ }
+
+ @Test
+ public void saveNewContribution_nullableImageUrlUsesFileAsBackup() throws Exception {
+ when(client.insert(isA(Uri.class), isA(ContentValues.class))).thenReturn(contentUri);
+ Contribution contribution = createContribution(true, null, null, null, "file");
+
+ testObject.save(contribution);
+
+ assertEquals(contentUri, contribution.getContentUri());
+ verify(client).insert(eq(BASE_URI), captor.capture());
+ ContentValues cv = captor.getValue();
+
+ // Nullable fields are absent if null
+ assertFalse(cv.containsKey(Table.COLUMN_LOCAL_URI));
+ assertFalse(cv.containsKey(Table.COLUMN_UPLOADED));
+
+ assertEquals(Utils.makeThumbBaseUrl("file"), cv.getAsString(Table.COLUMN_IMAGE_URL));
+ }
+
+ @Test
+ public void saveNewContribution_nullableFieldsAreNonNull() throws Exception {
+ when(client.insert(isA(Uri.class), isA(ContentValues.class))).thenReturn(contentUri);
+ Contribution contribution = createContribution(true, Uri.parse(LOCAL_URI),
+ "image", new Date(456L), null);
+
+ testObject.save(contribution);
+
+ assertEquals(contentUri, contribution.getContentUri());
+ verify(client).insert(eq(BASE_URI), captor.capture());
+ ContentValues cv = captor.getValue();
+
+ assertEquals(LOCAL_URI, cv.getAsString(Table.COLUMN_LOCAL_URI));
+ assertEquals("image", cv.getAsString(Table.COLUMN_IMAGE_URL));
+ assertEquals(456L, cv.getAsLong(Table.COLUMN_UPLOADED).longValue());
+ }
+
+ @Test
+ public void saveNewContribution_booleanEncodesTrue() throws Exception {
+ when(client.insert(isA(Uri.class), isA(ContentValues.class))).thenReturn(contentUri);
+ Contribution contribution = createContribution(true, null, null, null, null);
+
+ testObject.save(contribution);
+
+ assertEquals(contentUri, contribution.getContentUri());
+ verify(client).insert(eq(BASE_URI), captor.capture());
+ ContentValues cv = captor.getValue();
+
+ // Boolean true --> 1 for ths encoding scheme
+ assertEquals("Boolean true should be encoded as 1", 1,
+ cv.getAsInteger(Table.COLUMN_MULTIPLE).intValue());
+ }
+
+ @Test
+ public void saveNewContribution_booleanEncodesFalse() throws Exception {
+ when(client.insert(isA(Uri.class), isA(ContentValues.class))).thenReturn(contentUri);
+ Contribution contribution = createContribution(false, null, null, null, null);
+
+ testObject.save(contribution);
+
+ assertEquals(contentUri, contribution.getContentUri());
+ verify(client).insert(eq(BASE_URI), captor.capture());
+ ContentValues cv = captor.getValue();
+
+ // Boolean true --> 1 for ths encoding scheme
+ assertEquals("Boolean false should be encoded as 0", 0,
+ cv.getAsInteger(Table.COLUMN_MULTIPLE).intValue());
+ }
+
+ @Test
+ public void saveExistingContribution() throws Exception {
+ Contribution contribution = createContribution(false, null, null, null, null);
+ contribution.setContentUri(contentUri);
+
+ testObject.save(contribution);
+
+ verify(client).update(eq(contentUri), isA(ContentValues.class), isNull(String.class), isNull(String[].class));
+ }
+
+ @Test(expected = RuntimeException.class)
+ public void saveTranslatesExceptions() throws Exception {
+ when(client.insert(isA(Uri.class), isA(ContentValues.class))).thenThrow(new RemoteException(""));
+
+ testObject.save(createContribution(false, null, null, null, null));
+ }
+
+ @Test(expected = RuntimeException.class)
+ public void deleteTranslatesExceptions() throws Exception {
+ when(client.delete(isA(Uri.class), any(), any())).thenThrow(new RemoteException(""));
+
+ Contribution contribution = createContribution(false, null, null, null, null);
+ contribution.setContentUri(contentUri);
+ testObject.delete(contribution);
+ }
+
+ @Test(expected = RuntimeException.class)
+ public void exceptionThrownWhenAttemptingToDeleteUnsavedContribution() {
+ testObject.delete(createContribution(false, null, null, null, null));
+ }
+
+ @Test
+ public void deleteExistingContribution() throws Exception {
+ Contribution contribution = createContribution(false, null, null, null, null);
+ contribution.setContentUri(contentUri);
+
+ testObject.delete(contribution);
+
+ verify(client).delete(eq(contentUri), isNull(String.class), isNull(String[].class));
+ }
+
+ @Test
+ public void createFromCursor() {
+ long created = 321L;
+ long uploaded = 456L;
+ MatrixCursor mc = createCursor(created, uploaded, false, LOCAL_URI);
+
+ Contribution c = testObject.fromCursor(mc);
+
+ assertEquals(uriForId(111), c.getContentUri());
+ assertEquals("file", c.getFilename());
+ assertEquals(LOCAL_URI, c.getLocalUri().toString());
+ assertEquals("image", c.getImageUrl());
+ assertEquals(created, c.getTimestamp().getTime());
+ assertEquals(created, c.getDateCreated().getTime());
+ assertEquals(STATE_QUEUED, c.getState());
+ assertEquals(222L, c.getDataLength());
+ assertEquals(uploaded, c.getDateUploaded().getTime());
+ assertEquals(88L, c.getTransferred());
+ assertEquals(SOURCE_GALLERY, c.getSource());
+ assertEquals("desc", c.getDescription());
+ assertEquals("create", c.getCreator());
+ assertEquals(640, c.getWidth());
+ assertEquals(480, c.getHeight());
+ assertEquals("007", c.getLicense());
+ }
+
+ @Test
+ public void createFromCursor_nullableTimestamps() {
+ MatrixCursor mc = createCursor(0L, 0L, false, LOCAL_URI);
+
+ Contribution c = testObject.fromCursor(mc);
+
+ assertNull(c.getTimestamp());
+ assertNull(c.getDateCreated());
+ assertNull(c.getDateUploaded());
+ }
+
+ @Test
+ public void createFromCursor_nullableLocalUri() {
+ MatrixCursor mc = createCursor(0L, 0L, false, "");
+
+ Contribution c = testObject.fromCursor(mc);
+
+ assertNull(c.getLocalUri());
+ assertNull(c.getDateCreated());
+ assertNull(c.getDateUploaded());
+ }
+
+ @Test
+ public void createFromCursor_booleanEncoding() {
+ MatrixCursor mcFalse = createCursor(0L, 0L, false, LOCAL_URI);
+ assertFalse(testObject.fromCursor(mcFalse).getMultiple());
+
+ MatrixCursor mcHammer = createCursor(0L, 0L, true, LOCAL_URI);
+ assertTrue(testObject.fromCursor(mcHammer).getMultiple());
+ }
+
+ @NonNull
+ private MatrixCursor createCursor(long created, long uploaded, boolean multiple, String localUri) {
+ MatrixCursor mc = new MatrixCursor(Table.ALL_FIELDS, 1);
+ mc.addRow(Arrays.asList("111", "file", localUri, "image",
+ created, STATE_QUEUED, 222L, uploaded, 88L, SOURCE_GALLERY, "desc",
+ "create", multiple ? 1 : 0, 640, 480, "007"));
+ mc.moveToFirst();
+ return mc;
+ }
+
+ @NonNull
+ private Contribution createContribution(boolean multiple, Uri localUri,
+ String imageUrl, Date dateUploaded, String filename) {
+ Contribution contribution = new Contribution(localUri, imageUrl, filename, "desc", 222L,
+ new Date(321L), dateUploaded, "create", "edit", "coords");
+ contribution.setState(STATE_COMPLETED);
+ contribution.setTransferred(333L);
+ contribution.setSource(SOURCE_CAMERA);
+ contribution.setLicense("007");
+ contribution.setMultiple(multiple);
+ contribution.setTimestamp(new Date(321L));
+ contribution.setWidth(640);
+ contribution.setHeight(480); // VGA should be enough for anyone, right?
+ return contribution;
+ }
+}
\ No newline at end of file
diff --git a/app/src/test/java/fr/free/nrw/commons/modifications/ModifierSequenceDaoTest.java b/app/src/test/java/fr/free/nrw/commons/modifications/ModifierSequenceDaoTest.java
new file mode 100644
index 000000000..ef290500d
--- /dev/null
+++ b/app/src/test/java/fr/free/nrw/commons/modifications/ModifierSequenceDaoTest.java
@@ -0,0 +1,181 @@
+package fr.free.nrw.commons.modifications;
+
+import android.content.ContentProviderClient;
+import android.content.ContentValues;
+import android.database.MatrixCursor;
+import android.database.sqlite.SQLiteDatabase;
+import android.net.Uri;
+import android.os.RemoteException;
+import android.support.annotation.NonNull;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.InOrder;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.annotation.Config;
+
+import java.util.Arrays;
+
+import fr.free.nrw.commons.BuildConfig;
+import fr.free.nrw.commons.TestCommonsApplication;
+
+import static fr.free.nrw.commons.modifications.ModificationsContentProvider.BASE_URI;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Matchers.isA;
+import static org.mockito.Matchers.isNull;
+import static org.mockito.Mockito.inOrder;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+@RunWith(RobolectricTestRunner.class)
+@Config(constants = BuildConfig.class, sdk = 21, application = TestCommonsApplication.class)
+public class ModifierSequenceDaoTest {
+
+ private static final String EXPECTED_MEDIA_URI = "http://example.com/";
+
+ @Mock
+ ContentProviderClient client;
+ @Mock
+ SQLiteDatabase database;
+ @Captor
+ ArgumentCaptor contentValuesCaptor;
+
+ private ModifierSequenceDao testObject;
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+ testObject = new ModifierSequenceDao(() -> client);
+ }
+
+ @Test
+ public void createFromCursorWithEmptyModifiers() {
+ MatrixCursor cursor = createCursor("");
+
+ ModifierSequence seq = testObject.fromCursor(cursor);
+
+ assertEquals(EXPECTED_MEDIA_URI, seq.getMediaUri().toString());
+ assertEquals(BASE_URI.buildUpon().appendPath("1").toString(), seq.getContentUri().toString());
+ assertTrue(seq.getModifiers().isEmpty());
+ }
+
+ @Test
+ public void createFromCursorWtihCategoryModifier() {
+ MatrixCursor cursor = createCursor("{\"name\": \"CategoriesModifier\", \"data\": {}}");
+
+ ModifierSequence seq = testObject.fromCursor(cursor);
+
+ assertEquals(1, seq.getModifiers().size());
+ assertTrue(seq.getModifiers().get(0) instanceof CategoryModifier);
+ }
+
+ @Test
+ public void createFromCursorWithRemoveModifier() {
+ MatrixCursor cursor = createCursor("{\"name\": \"TemplateRemoverModifier\", \"data\": {}}");
+
+ ModifierSequence seq = testObject.fromCursor(cursor);
+
+ assertEquals(1, seq.getModifiers().size());
+ assertTrue(seq.getModifiers().get(0) instanceof TemplateRemoveModifier);
+ }
+
+ @Test
+ public void deleteSequence() throws Exception {
+ when(client.delete(isA(Uri.class), isNull(String.class), isNull(String[].class))).thenReturn(1);
+ ModifierSequence seq = testObject.fromCursor(createCursor(""));
+
+ testObject.delete(seq);
+
+ verify(client).delete(eq(seq.getContentUri()), isNull(String.class), isNull(String[].class));
+ }
+
+ @Test(expected = RuntimeException.class)
+ public void deleteTranslatesRemoteExceptions() throws Exception {
+ when(client.delete(isA(Uri.class), isNull(String.class), isNull(String[].class))).thenThrow(new RemoteException(""));
+ ModifierSequence seq = testObject.fromCursor(createCursor(""));
+
+ testObject.delete(seq);
+ }
+
+ @Test
+ public void saveExistingSequence() throws Exception {
+ String modifierJson = "{\"name\":\"CategoriesModifier\",\"data\":{}}";
+ String expectedData = "{\"modifiers\":[" + modifierJson + "]}";
+ MatrixCursor cursor = createCursor(modifierJson);
+
+ testObject.save(testObject.fromCursor(cursor));
+
+ verify(client).update(eq(testObject.fromCursor(cursor).getContentUri()), contentValuesCaptor.capture(), isNull(String.class), isNull(String[].class));
+ ContentValues cv = contentValuesCaptor.getValue();
+ assertEquals(2, cv.size());
+ assertEquals(EXPECTED_MEDIA_URI, cv.get(ModifierSequenceDao.Table.COLUMN_MEDIA_URI));
+ assertEquals(expectedData, cv.get(ModifierSequenceDao.Table.COLUMN_DATA));
+ }
+
+ @Test
+ public void saveNewSequence() throws Exception {
+ Uri expectedContentUri = BASE_URI.buildUpon().appendPath("1").build();
+ when(client.insert(isA(Uri.class), isA(ContentValues.class))).thenReturn(expectedContentUri);
+
+ ModifierSequence seq = new ModifierSequence(Uri.parse(EXPECTED_MEDIA_URI));
+ testObject.save(seq);
+
+ verify(client).insert(eq(ModificationsContentProvider.BASE_URI), contentValuesCaptor.capture());
+ ContentValues cv = contentValuesCaptor.getValue();
+ assertEquals(2, cv.size());
+ assertEquals(EXPECTED_MEDIA_URI, cv.get(ModifierSequenceDao.Table.COLUMN_MEDIA_URI));
+ assertEquals("{\"modifiers\":[]}", cv.get(ModifierSequenceDao.Table.COLUMN_DATA));
+ assertEquals(expectedContentUri.toString(), seq.getContentUri().toString());
+ }
+
+ @Test(expected = RuntimeException.class)
+ public void saveTranslatesRemoteExceptions() throws Exception {
+ when(client.insert(isA(Uri.class), isA(ContentValues.class))).thenThrow(new RemoteException(""));
+
+ testObject.save(new ModifierSequence(Uri.parse(EXPECTED_MEDIA_URI)));
+ }
+
+ @Test
+ public void createTable() {
+ ModifierSequenceDao.Table.onCreate(database);
+
+ verify(database).execSQL(ModifierSequenceDao.Table.CREATE_TABLE_STATEMENT);
+ }
+
+ @Test
+ public void updateTable() {
+ ModifierSequenceDao.Table.onUpdate(database, 1, 2);
+
+ InOrder inOrder = inOrder(database);
+ inOrder.verify(database).execSQL(ModifierSequenceDao.Table.DROP_TABLE_STATEMENT);
+ inOrder.verify(database).execSQL(ModifierSequenceDao.Table.CREATE_TABLE_STATEMENT);
+ }
+
+ @Test
+ public void deleteTable() {
+ ModifierSequenceDao.Table.onDelete(database);
+
+ InOrder inOrder = inOrder(database);
+ inOrder.verify(database).execSQL(ModifierSequenceDao.Table.DROP_TABLE_STATEMENT);
+ inOrder.verify(database).execSQL(ModifierSequenceDao.Table.CREATE_TABLE_STATEMENT);
+ }
+
+ @NonNull
+ private MatrixCursor createCursor(String modifierJson) {
+ MatrixCursor cursor = new MatrixCursor(new String[]{
+ ModifierSequenceDao.Table.COLUMN_ID,
+ ModifierSequenceDao.Table.COLUMN_MEDIA_URI,
+ ModifierSequenceDao.Table.COLUMN_DATA
+ }, 1);
+ cursor.addRow(Arrays.asList("1", EXPECTED_MEDIA_URI, "{\"modifiers\": [" + modifierJson + "]}"));
+ cursor.moveToFirst();
+ return cursor;
+ }
+}
\ No newline at end of file
diff --git a/design/screenshots/German de-DE/bus-categories.png b/design/screenshots/German de-DE/bus-categories.png
new file mode 100644
index 000000000..6eda6ff8f
Binary files /dev/null and b/design/screenshots/German de-DE/bus-categories.png differ
diff --git a/design/screenshots/German de-DE/bus-description.png b/design/screenshots/German de-DE/bus-description.png
new file mode 100644
index 000000000..116d74b14
Binary files /dev/null and b/design/screenshots/German de-DE/bus-description.png differ
diff --git a/design/screenshots/German de-DE/bus-details.png b/design/screenshots/German de-DE/bus-details.png
new file mode 100644
index 000000000..d327d4ebd
Binary files /dev/null and b/design/screenshots/German de-DE/bus-details.png differ
diff --git a/design/screenshots/German de-DE/church-categories.png b/design/screenshots/German de-DE/church-categories.png
new file mode 100644
index 000000000..2abb9dd6b
Binary files /dev/null and b/design/screenshots/German de-DE/church-categories.png differ
diff --git a/design/screenshots/German de-DE/church-description.png b/design/screenshots/German de-DE/church-description.png
new file mode 100644
index 000000000..84f3048c7
Binary files /dev/null and b/design/screenshots/German de-DE/church-description.png differ
diff --git a/design/screenshots/German de-DE/church-details.png b/design/screenshots/German de-DE/church-details.png
new file mode 100644
index 000000000..082e3bf20
Binary files /dev/null and b/design/screenshots/German de-DE/church-details.png differ
diff --git a/design/screenshots/German de-DE/drawer.png b/design/screenshots/German de-DE/drawer.png
new file mode 100644
index 000000000..3b2c8098c
Binary files /dev/null and b/design/screenshots/German de-DE/drawer.png differ
diff --git a/design/screenshots/German de-DE/gallery.png b/design/screenshots/German de-DE/gallery.png
new file mode 100644
index 000000000..6cc6e71b4
Binary files /dev/null and b/design/screenshots/German de-DE/gallery.png differ
diff --git a/design/screenshots/German de-DE/nearby-list.png b/design/screenshots/German de-DE/nearby-list.png
new file mode 100644
index 000000000..c5006e087
Binary files /dev/null and b/design/screenshots/German de-DE/nearby-list.png differ
diff --git a/design/screenshots/German de-DE/nearby-map.png b/design/screenshots/German de-DE/nearby-map.png
new file mode 100644
index 000000000..1e2573f77
Binary files /dev/null and b/design/screenshots/German de-DE/nearby-map.png differ
diff --git a/design/screenshots/German de-DE/school-categories.png b/design/screenshots/German de-DE/school-categories.png
new file mode 100644
index 000000000..882bcd226
Binary files /dev/null and b/design/screenshots/German de-DE/school-categories.png differ
diff --git a/design/screenshots/German de-DE/school-description.png b/design/screenshots/German de-DE/school-description.png
new file mode 100644
index 000000000..efa6d56c6
Binary files /dev/null and b/design/screenshots/German de-DE/school-description.png differ
diff --git a/design/screenshots/German de-DE/school-details.png b/design/screenshots/German de-DE/school-details.png
new file mode 100644
index 000000000..4fc1394cc
Binary files /dev/null and b/design/screenshots/German de-DE/school-details.png differ
diff --git a/design/screenshots/Kannada kn-IN/car-categories.png.jpeg b/design/screenshots/Kannada kn-IN/car-categories.png.jpeg
new file mode 100644
index 000000000..8c78307e0
Binary files /dev/null and b/design/screenshots/Kannada kn-IN/car-categories.png.jpeg differ
diff --git a/design/screenshots/Kannada kn-IN/car-description.png.jpeg b/design/screenshots/Kannada kn-IN/car-description.png.jpeg
new file mode 100644
index 000000000..0d6548854
Binary files /dev/null and b/design/screenshots/Kannada kn-IN/car-description.png.jpeg differ
diff --git a/design/screenshots/Kannada kn-IN/car-details.png.jpeg b/design/screenshots/Kannada kn-IN/car-details.png.jpeg
new file mode 100644
index 000000000..eb2868915
Binary files /dev/null and b/design/screenshots/Kannada kn-IN/car-details.png.jpeg differ
diff --git a/design/screenshots/Kannada kn-IN/donne.biriyani-categories.png.jpeg b/design/screenshots/Kannada kn-IN/donne.biriyani-categories.png.jpeg
new file mode 100644
index 000000000..3283e2abe
Binary files /dev/null and b/design/screenshots/Kannada kn-IN/donne.biriyani-categories.png.jpeg differ
diff --git a/design/screenshots/Kannada kn-IN/donne.biriyani-description.png.jpeg b/design/screenshots/Kannada kn-IN/donne.biriyani-description.png.jpeg
new file mode 100644
index 000000000..e04f18a68
Binary files /dev/null and b/design/screenshots/Kannada kn-IN/donne.biriyani-description.png.jpeg differ
diff --git a/design/screenshots/Kannada kn-IN/donne.biriyani-details.png.jpeg b/design/screenshots/Kannada kn-IN/donne.biriyani-details.png.jpeg
new file mode 100644
index 000000000..cb6dde5d4
Binary files /dev/null and b/design/screenshots/Kannada kn-IN/donne.biriyani-details.png.jpeg differ
diff --git a/design/screenshots/Kannada kn-IN/drawer.png.jpeg b/design/screenshots/Kannada kn-IN/drawer.png.jpeg
new file mode 100644
index 000000000..388531a53
Binary files /dev/null and b/design/screenshots/Kannada kn-IN/drawer.png.jpeg differ
diff --git a/design/screenshots/Kannada kn-IN/gallery.png.jpeg b/design/screenshots/Kannada kn-IN/gallery.png.jpeg
new file mode 100644
index 000000000..96c497b3f
Binary files /dev/null and b/design/screenshots/Kannada kn-IN/gallery.png.jpeg differ
diff --git a/design/screenshots/Kannada kn-IN/nearby-list.png.jpeg b/design/screenshots/Kannada kn-IN/nearby-list.png.jpeg
new file mode 100644
index 000000000..357c025ee
Binary files /dev/null and b/design/screenshots/Kannada kn-IN/nearby-list.png.jpeg differ
diff --git a/design/screenshots/Kannada kn-IN/nearby-map.png.jpeg b/design/screenshots/Kannada kn-IN/nearby-map.png.jpeg
new file mode 100644
index 000000000..5e377a79e
Binary files /dev/null and b/design/screenshots/Kannada kn-IN/nearby-map.png.jpeg differ
diff --git a/design/screenshots/Kannada kn-IN/restaurant-categories.png.jpeg b/design/screenshots/Kannada kn-IN/restaurant-categories.png.jpeg
new file mode 100644
index 000000000..7c187b99b
Binary files /dev/null and b/design/screenshots/Kannada kn-IN/restaurant-categories.png.jpeg differ
diff --git a/design/screenshots/Kannada kn-IN/restaurant-description.png.jpeg b/design/screenshots/Kannada kn-IN/restaurant-description.png.jpeg
new file mode 100644
index 000000000..e43095ecf
Binary files /dev/null and b/design/screenshots/Kannada kn-IN/restaurant-description.png.jpeg differ
diff --git a/design/screenshots/Kannada kn-IN/restaurant-details.png.jpeg b/design/screenshots/Kannada kn-IN/restaurant-details.png.jpeg
new file mode 100644
index 000000000..d24bfbf15
Binary files /dev/null and b/design/screenshots/Kannada kn-IN/restaurant-details.png.jpeg differ
diff --git a/design/screenshots/Polish pl-PL/car-categories.png b/design/screenshots/Polish pl-PL/car-categories.png
new file mode 100644
index 000000000..e4f1ab21e
Binary files /dev/null and b/design/screenshots/Polish pl-PL/car-categories.png differ
diff --git a/design/screenshots/Polish pl-PL/car-description.png b/design/screenshots/Polish pl-PL/car-description.png
new file mode 100644
index 000000000..083ce23d1
Binary files /dev/null and b/design/screenshots/Polish pl-PL/car-description.png differ
diff --git a/design/screenshots/Polish pl-PL/car-details.png b/design/screenshots/Polish pl-PL/car-details.png
new file mode 100644
index 000000000..749c75b4c
Binary files /dev/null and b/design/screenshots/Polish pl-PL/car-details.png differ
diff --git a/design/screenshots/Polish pl-PL/church-categories.png b/design/screenshots/Polish pl-PL/church-categories.png
new file mode 100644
index 000000000..dc63b5b67
Binary files /dev/null and b/design/screenshots/Polish pl-PL/church-categories.png differ
diff --git a/design/screenshots/Polish pl-PL/church-description.png b/design/screenshots/Polish pl-PL/church-description.png
new file mode 100644
index 000000000..c915c8e1b
Binary files /dev/null and b/design/screenshots/Polish pl-PL/church-description.png differ
diff --git a/design/screenshots/Polish pl-PL/church-details.png b/design/screenshots/Polish pl-PL/church-details.png
new file mode 100644
index 000000000..bbab8c0fe
Binary files /dev/null and b/design/screenshots/Polish pl-PL/church-details.png differ
diff --git a/design/screenshots/Polish pl-PL/drawer.png b/design/screenshots/Polish pl-PL/drawer.png
new file mode 100644
index 000000000..ad70a94b1
Binary files /dev/null and b/design/screenshots/Polish pl-PL/drawer.png differ
diff --git a/design/screenshots/Polish pl-PL/gallery.png b/design/screenshots/Polish pl-PL/gallery.png
new file mode 100644
index 000000000..9f3b3b90d
Binary files /dev/null and b/design/screenshots/Polish pl-PL/gallery.png differ
diff --git a/design/screenshots/Polish pl-PL/nearby-list.png b/design/screenshots/Polish pl-PL/nearby-list.png
new file mode 100644
index 000000000..5dcee017e
Binary files /dev/null and b/design/screenshots/Polish pl-PL/nearby-list.png differ
diff --git a/design/screenshots/Polish pl-PL/nearby-map.png b/design/screenshots/Polish pl-PL/nearby-map.png
new file mode 100644
index 000000000..f67737c34
Binary files /dev/null and b/design/screenshots/Polish pl-PL/nearby-map.png differ
diff --git a/design/screenshots/Polish pl-PL/school-categories.png b/design/screenshots/Polish pl-PL/school-categories.png
new file mode 100644
index 000000000..a9bf18b14
Binary files /dev/null and b/design/screenshots/Polish pl-PL/school-categories.png differ
diff --git a/design/screenshots/Polish pl-PL/school-description.png b/design/screenshots/Polish pl-PL/school-description.png
new file mode 100644
index 000000000..094d10d91
Binary files /dev/null and b/design/screenshots/Polish pl-PL/school-description.png differ
diff --git a/design/screenshots/Polish pl-PL/school-details.png b/design/screenshots/Polish pl-PL/school-details.png
new file mode 100644
index 000000000..c73cf19f7
Binary files /dev/null and b/design/screenshots/Polish pl-PL/school-details.png differ
diff --git a/gradle.properties b/gradle.properties
index 07148a581..21c58394c 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -1,6 +1,6 @@
gradleVersion = 3.0.0
-supportLibVersion = 26.1.0
+SUPPORT_LIB_VERSION = 26.0.2
compileSdkVersion = android-26
buildToolsVersion = 26.0.2
@@ -12,7 +12,8 @@ android.useDeprecatedNdk=true
# Library dependencies
BUTTERKNIFE_VERSION=8.6.0
-DAGGER_VERSION=2.11
+DAGGER_VERSION=2.13
+LEAK_CANARY=1.5.4
org.gradle.jvmargs=-Xmx1536M