diff --git a/README.md b/README.md
index 07beeec8d..3e5774873 100644
--- a/README.md
+++ b/README.md
@@ -25,13 +25,13 @@ We try to have an extensive documentation at our [documentation repository][4]:
Thank you all for your work!
-| [
misaochan](https://github.com/misaochan) | [
yuvipanda](https://github.com/yuvipanda) | [
neslihanturan](https://github.com/neslihanturan) | [
maskaravivek](https://github.com/maskaravivek) | [
translatewiki](https://github.com/translatewiki) |
+| [
misaochan](https://github.com/misaochan) | [
translatewiki](https://github.com/translatewiki) | [
neslihanturan](https://github.com/neslihanturan) | [
yuvipanda](https://github.com/yuvipanda) | [
nicolas-raoul](https://github.com/nicolas-raoul) |
| :---: | :---: | :---: | :---: | :---: |
-| [
nicolas-raoul](https://github.com/nicolas-raoul) | [
psh](https://github.com/psh) | [
brion](https://github.com/brion) | [
whym](https://github.com/whym) | [
domdomegg](https://github.com/domdomegg) |
-| [
akaita](https://github.com/akaita) | [
veyndan](https://github.com/veyndan) | [
ujjwalagrawal17](https://github.com/ujjwalagrawal17) | [
dbrant](https://github.com/dbrant) | [
sandarumk](https://github.com/sandarumk) |
-| [
tobias47n9e](https://github.com/tobias47n9e) | [
tanvidadu](https://github.com/tanvidadu) | [
hismaeel](https://github.com/hismaeel) | [
tshradheya](https://github.com/tshradheya) | [
diddypod](https://github.com/diddypod) |
-| [
prxjeen](https://github.com/prxjeen) | [
addshore](https://github.com/addshore) | [
knight-shade](https://github.com/knight-shade) | [
ashishkumar468](https://github.com/ashishkumar468) | [
siebrand](https://github.com/siebrand) |
-| [
Bluesir9](https://github.com/Bluesir9) | [
Jatin0312](https://github.com/Jatin0312) | [
mashawan](https://github.com/mashawan) | [
ford-prefect](https://github.com/ford-prefect) | [
seantnemann](https://github.com/seantnemann) |
+| [
domdomegg](https://github.com/domdomegg) | [
maskaravivek](https://github.com/maskaravivek) | [
psh](https://github.com/psh) | [
brion](https://github.com/brion) | [
ashishkumar468](https://github.com/ashishkumar468) |
+| [
whym](https://github.com/whym) | [
akaita](https://github.com/akaita) | [
madhurgupta10](https://github.com/madhurgupta10) | [
veyndan](https://github.com/veyndan) | [
ujjwalagrawal17](https://github.com/ujjwalagrawal17) |
+| [
macgills](https://github.com/macgills) | [
dbrant](https://github.com/dbrant) | [
vanshikaarora](https://github.com/vanshikaarora) | [
sandarumk](https://github.com/sandarumk) | [
tanvidadu](https://github.com/tanvidadu) |
+| [
cypherop](https://github.com/cypherop) | [
tobias47n9e](https://github.com/tobias47n9e) | [
hismaeel](https://github.com/hismaeel) | [
tshradheya](https://github.com/tshradheya) | [
addshore](https://github.com/addshore) |
+| [
knight-shade](https://github.com/knight-shade) | [
siebrand](https://github.com/siebrand) | [
sivaraam](https://github.com/sivaraam) | [
Bluesir9](https://github.com/Bluesir9) | [
kbhardwaj123](https://github.com/kbhardwaj123) |
.. and [many more](https://github.com/commons-app/apps-android-commons/graphs/contributors).
diff --git a/app/build.gradle b/app/build.gradle
index 3d1ab456f..4c562e7de 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -28,6 +28,7 @@ dependencies {
implementation 'io.reactivex.rxjava2:rxandroid:2.1.0'
implementation 'io.reactivex.rxjava2:rxjava:2.2.3'
implementation 'com.jakewharton.rxbinding2:rxbinding:2.1.1'
+ implementation 'com.jakewharton.rxbinding3:rxbinding-appcompat:3.0.0'
implementation 'com.jakewharton.rxbinding2:rxbinding-support-v4:2.1.1'
implementation 'com.jakewharton.rxbinding2:rxbinding-appcompat-v7:2.1.1'
implementation 'com.jakewharton.rxbinding2:rxbinding-design:2.1.1'
@@ -66,9 +67,11 @@ dependencies {
implementation "com.squareup.okhttp3:logging-interceptor:$OKHTTP_VERSION"
// Dependency injector
+ implementation "com.google.dagger:dagger-android:$DAGGER_VERSION"
implementation "com.google.dagger:dagger-android-support:$DAGGER_VERSION"
kapt "com.google.dagger:dagger-android-processor:$DAGGER_VERSION"
kapt "com.google.dagger:dagger-compiler:$DAGGER_VERSION"
+ annotationProcessor "com.google.dagger:dagger-android-processor:$DAGGER_VERSION"
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$KOTLIN_VERSION"
implementation "org.jetbrains.kotlin:kotlin-reflect:$KOTLIN_VERSION"
@@ -76,28 +79,20 @@ dependencies {
//Mocking
testImplementation 'com.nhaarman.mockitokotlin2:mockito-kotlin:2.2.0'
testImplementation 'org.mockito:mockito-inline:2.13.0'
- testImplementation 'org.mockito:mockito-core:2.23.0'
- testImplementation "org.powermock:powermock-module-junit4:2.0.0-beta.5"
- testImplementation "org.powermock:powermock-api-mockito2:2.0.0-beta.5"
+ testImplementation 'org.mockito:mockito-core:2.25.1'
+ testImplementation "org.powermock:powermock-module-junit4:2.0.2"
+ testImplementation "org.powermock:powermock-api-mockito2:2.0.2"
// Unit testing
- testImplementation 'junit:junit:4.13.1'
- testImplementation 'org.robolectric:robolectric:4.3'
+ testImplementation 'junit:junit:4.13.2'
+ testImplementation 'org.robolectric:robolectric:4.5.1'
testImplementation 'androidx.test:core:1.3.0'
testImplementation "com.squareup.okhttp3:mockwebserver:$OKHTTP_VERSION"
- testImplementation "org.powermock:powermock-module-junit4:2.0.0-beta.5"
- testImplementation "org.powermock:powermock-api-mockito2:2.0.0-beta.5"
- testImplementation 'org.mockito:mockito-core:2.23.0'
testImplementation "com.jraska.livedata:testing-ktx:1.1.2"
testImplementation "androidx.arch.core:core-testing:2.1.0"
testImplementation "org.junit.jupiter:junit-jupiter-api:5.3.1"
testRuntimeOnly "org.junit.jupiter:junit-jupiter-engine:5.3.1"
-
- // Mockito and PowerMock
- androidTestCompile ('org.powermock:powermock-mockito-release-full:1.6.0') {
- exclude module: 'hamcrest-core'
- exclude module: 'objenesis'
- }
+ testImplementation 'com.facebook.soloader:soloader:0.9.0'
// Android testing
androidTestImplementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$KOTLIN_VERSION"
@@ -117,10 +112,10 @@ dependencies {
// Support libraries
implementation "com.google.android.material:material:1.1.0-alpha04"
- implementation "androidx.browser:browser:1.0.0"
+ implementation "androidx.browser:browser:1.3.0"
implementation "androidx.cardview:cardview:1.0.0"
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
- implementation "androidx.exifinterface:exifinterface:1.0.0"
+ implementation "androidx.exifinterface:exifinterface:1.3.2"
implementation "androidx.core:core-ktx:$CORE_KTX_VERSION"
implementation "androidx.multidex:multidex:2.0.1"
@@ -143,6 +138,10 @@ dependencies {
implementation "androidx.preference:preference-ktx:$PREFERENCE_VERSION"
implementation "androidx.multidex:multidex:$MULTIDEX_VERSION"
+
+ def work_version = "2.4.0"
+ // Kotlin + coroutines
+ implementation "androidx.work:work-runtime-ktx:$work_version"
}
android {
@@ -150,6 +149,7 @@ android {
defaultConfig {
//applicationId 'fr.free.nrw.commons'
+
versionCode 1016
versionName '3.0.1'
setProperty("archivesBaseName", "app-commons-v$versionName-" + getBranchName())
diff --git a/app/src/androidTest/java/fr/free/nrw/commons/AchievementsActivityTest.kt b/app/src/androidTest/java/fr/free/nrw/commons/AchievementsActivityTest.kt
index 6bded4351..16c27dd77 100644
--- a/app/src/androidTest/java/fr/free/nrw/commons/AchievementsActivityTest.kt
+++ b/app/src/androidTest/java/fr/free/nrw/commons/AchievementsActivityTest.kt
@@ -29,7 +29,6 @@ class AchievementsActivityTest {
@Test
fun testAchievements() {
onView(withId(R.id.drawer_layout)).perform(DrawerActions.open())
- onView(withId(R.id.user_icon)).perform(click())
Intents.intended(hasComponent(ProfileActivity::class.java.name))
}
diff --git a/app/src/androidTest/java/fr/free/nrw/commons/CategoryImagesActivityTest.kt b/app/src/androidTest/java/fr/free/nrw/commons/CategoryImagesActivityTest.kt
deleted file mode 100644
index 3b51cd85b..000000000
--- a/app/src/androidTest/java/fr/free/nrw/commons/CategoryImagesActivityTest.kt
+++ /dev/null
@@ -1,19 +0,0 @@
-package fr.free.nrw.commons
-
-import androidx.test.rule.ActivityTestRule
-import androidx.test.runner.AndroidJUnit4
-import fr.free.nrw.commons.category.CategoryImagesActivity
-import org.junit.Rule
-import org.junit.Test
-import org.junit.runner.RunWith
-
-@RunWith(AndroidJUnit4::class)
-class CategoryImagesActivityTest {
- @get:Rule
- var activityRule = ActivityTestRule(CategoryImagesActivity::class.java)
-
- @Test
- fun orientationChange() {
- UITestHelper.changeOrientation(activityRule)
- }
-}
\ No newline at end of file
diff --git a/app/src/androidTest/java/fr/free/nrw/commons/LeaderboardActivityTest.kt b/app/src/androidTest/java/fr/free/nrw/commons/LeaderboardActivityTest.kt
index 7f613fea0..5b6a2c255 100644
--- a/app/src/androidTest/java/fr/free/nrw/commons/LeaderboardActivityTest.kt
+++ b/app/src/androidTest/java/fr/free/nrw/commons/LeaderboardActivityTest.kt
@@ -45,7 +45,6 @@ class LeaderboardActivityTest {
@Test
fun testScrollToRankFromAbove() {
Espresso.onView(ViewMatchers.withId(R.id.drawer_layout)).perform(DrawerActions.open())
- Espresso.onView(ViewMatchers.withId(R.id.user_icon)).perform(ViewActions.click())
Espresso.onView(ViewMatchers.withId(R.id.tab_layout)).perform(ViewActions.click())
Espresso.onView(ViewMatchers.withId(R.id.tab_layout)).perform(selectTabAtPosition(1))
@@ -58,7 +57,6 @@ class LeaderboardActivityTest {
@Test
fun testScrollToRankFromBelow() {
Espresso.onView(ViewMatchers.withId(R.id.drawer_layout)).perform(DrawerActions.open())
- Espresso.onView(ViewMatchers.withId(R.id.user_icon)).perform(ViewActions.click())
Espresso.onView(ViewMatchers.withId(R.id.tab_layout)).perform(ViewActions.click())
Espresso.onView(ViewMatchers.withId(R.id.tab_layout)).perform(selectTabAtPosition(1))
diff --git a/app/src/androidTest/java/fr/free/nrw/commons/NavigationBaseActivityTest.kt b/app/src/androidTest/java/fr/free/nrw/commons/NavigationBaseActivityTest.kt
deleted file mode 100644
index f78142c9f..000000000
--- a/app/src/androidTest/java/fr/free/nrw/commons/NavigationBaseActivityTest.kt
+++ /dev/null
@@ -1,66 +0,0 @@
-package fr.free.nrw.commons
-
-import androidx.test.espresso.Espresso
-import androidx.test.espresso.Espresso.onView
-import androidx.test.espresso.contrib.DrawerActions
-import androidx.test.espresso.contrib.NavigationViewActions
-import androidx.test.espresso.matcher.ViewMatchers.withId
-import androidx.test.filters.LargeTest
-import androidx.test.rule.ActivityTestRule
-import androidx.test.runner.AndroidJUnit4
-import org.junit.Rule
-import org.junit.Test
-import org.junit.runner.RunWith
-
-@LargeTest
-@RunWith(AndroidJUnit4::class)
-class NavigationBaseActivityTest {
- @get:Rule
- var activityRule: ActivityTestRule<*> = ActivityTestRule(AboutActivity::class.java)
-
- /**
- * Goes through all the activities in the app and checks we don't crash
- * NB: This is not realistic if we're not logged in; we can access 'home', 'notifications', 'settings' and 'achievements' which we wouldn't otherwise be able to.
- */
- @Test
- fun goThroughNavigationBaseActivityActivities() {
- // Home
- openNavigationDrawerAndNavigateTo(R.id.action_home)
-
- // Explore
- openNavigationDrawerAndNavigateTo(R.id.action_explore)
-
- // Bookmarks
- openNavigationDrawerAndNavigateTo(R.id.action_bookmarks)
-
- // Reviews
- openNavigationDrawerAndNavigateTo(R.id.action_review)
-
- // Settings
- openNavigationDrawerAndNavigateTo(R.id.action_settings)
-
- // About
- openNavigationDrawerAndNavigateTo(R.id.action_about)
-
- // Tutorial
- openNavigationDrawerAndNavigateTo(R.id.action_introduction)
- Espresso.pressBack()
-
- // Achievements
- openNavigationDrawerAndNavigateTo(R.id.action_login)
-
- // Feedback
- openNavigationDrawerAndNavigateTo(R.id.action_feedback)
- }
-
- private fun openNavigationDrawerAndNavigateTo(menuItemId: Int) {
- onView(withId(R.id.drawer_layout)).perform(DrawerActions.open())
- UITestHelper.sleep(500)
- onView(withId(R.id.navigation_view)).perform(NavigationViewActions.navigateTo(menuItemId))
- }
-
- @Test
- fun orientationChange() {
- UITestHelper.changeOrientation(activityRule)
- }
-}
\ No newline at end of file
diff --git a/app/src/beta/res/xml/shortcuts.xml b/app/src/beta/res/xml/shortcuts.xml
new file mode 100644
index 000000000..65a51995e
--- /dev/null
+++ b/app/src/beta/res/xml/shortcuts.xml
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
+
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 81b9f7153..47d102fdc 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -108,20 +108,16 @@
-
-
-
-
diff --git a/app/src/main/java/fr/free/nrw/commons/CommonsApplication.java b/app/src/main/java/fr/free/nrw/commons/CommonsApplication.java
index f2fdfbd4f..48967e3ff 100644
--- a/app/src/main/java/fr/free/nrw/commons/CommonsApplication.java
+++ b/app/src/main/java/fr/free/nrw/commons/CommonsApplication.java
@@ -47,7 +47,9 @@ import io.reactivex.internal.functions.Functions;
import io.reactivex.plugins.RxJavaPlugins;
import io.reactivex.schedulers.Schedulers;
import java.io.File;
+import java.util.HashMap;
import java.util.HashSet;
+import java.util.Map;
import java.util.Set;
import javax.inject.Inject;
import javax.inject.Named;
@@ -127,7 +129,12 @@ public class CommonsApplication extends MultiDexApplication {
@Inject ContributionDao contributionDao;
- /**
+ /**
+ * In memory list of contributios whose uploads ahve been paused by the user
+ */
+ public static Map pauseUploads = new HashMap<>();
+
+ /**
* Used to declare and initialize various components and dependencies
*/
@Override
diff --git a/app/src/main/java/fr/free/nrw/commons/Utils.java b/app/src/main/java/fr/free/nrw/commons/Utils.java
index 5f75c86ba..587a65467 100644
--- a/app/src/main/java/fr/free/nrw/commons/Utils.java
+++ b/app/src/main/java/fr/free/nrw/commons/Utils.java
@@ -13,6 +13,7 @@ import android.widget.TextView;
import android.widget.Toast;
import androidx.annotation.NonNull;
+import androidx.browser.customtabs.CustomTabColorSchemeParams;
import androidx.browser.customtabs.CustomTabsIntent;
import androidx.core.content.ContextCompat;
@@ -139,9 +140,13 @@ public class Utils {
return;
}
+ final CustomTabColorSchemeParams color = new CustomTabColorSchemeParams.Builder()
+ .setToolbarColor(ContextCompat.getColor(context, R.color.primaryColor))
+ .setSecondaryToolbarColor(ContextCompat.getColor(context, R.color.primaryDarkColor))
+ .build();
+
CustomTabsIntent.Builder builder = new CustomTabsIntent.Builder();
- builder.setToolbarColor(ContextCompat.getColor(context, R.color.primaryColor));
- builder.setSecondaryToolbarColor(ContextCompat.getColor(context, R.color.primaryDarkColor));
+ builder.setDefaultColorSchemeParams(color);
builder.setExitAnimations(context, android.R.anim.slide_in_left, android.R.anim.slide_out_right);
CustomTabsIntent customTabsIntent = builder.build();
// Clear previous browser tasks, so that back/exit buttons work as intended.
diff --git a/app/src/main/java/fr/free/nrw/commons/explore/ViewPagerAdapter.java b/app/src/main/java/fr/free/nrw/commons/ViewPagerAdapter.java
old mode 100755
new mode 100644
similarity index 94%
rename from app/src/main/java/fr/free/nrw/commons/explore/ViewPagerAdapter.java
rename to app/src/main/java/fr/free/nrw/commons/ViewPagerAdapter.java
index 51bf77d2b..5ca20372a
--- a/app/src/main/java/fr/free/nrw/commons/explore/ViewPagerAdapter.java
+++ b/app/src/main/java/fr/free/nrw/commons/ViewPagerAdapter.java
@@ -1,57 +1,57 @@
-package fr.free.nrw.commons.explore;
-
-import androidx.fragment.app.Fragment;
-import androidx.fragment.app.FragmentManager;
-import androidx.fragment.app.FragmentPagerAdapter;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * This adapter will be used to display fragments in a ViewPager
- */
-public class ViewPagerAdapter extends FragmentPagerAdapter {
- private List fragmentList = new ArrayList<>();
- private List fragmentTitleList = new ArrayList<>();
-
- public ViewPagerAdapter(FragmentManager manager) {
- super(manager);
- }
-
- /**
- * This method returns the fragment of the viewpager at a particular position
- * @param position
- */
- @Override
- public Fragment getItem(int position) {
- return fragmentList.get(position);
- }
-
- /**
- * This method returns the total number of fragments in the viewpager.
- * @return size
- */
- @Override
- public int getCount() {
- return fragmentList.size();
- }
-
- /**
- * This method sets the fragment and title list in the viewpager
- * @param fragmentList List of all fragments to be displayed in the viewpager
- * @param fragmentTitleList List of all titles of the fragments
- */
- public void setTabData(List fragmentList, List fragmentTitleList) {
- this.fragmentList = fragmentList;
- this.fragmentTitleList = fragmentTitleList;
- }
-
- /**
- * This method returns the title of the page at a particular position
- * @param position
- */
- @Override
- public CharSequence getPageTitle(int position) {
- return fragmentTitleList.get(position);
- }
-}
+package fr.free.nrw.commons;
+
+import androidx.fragment.app.Fragment;
+import androidx.fragment.app.FragmentManager;
+import androidx.fragment.app.FragmentPagerAdapter;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * This adapter will be used to display fragments in a ViewPager
+ */
+public class ViewPagerAdapter extends FragmentPagerAdapter {
+ private List fragmentList = new ArrayList<>();
+ private List fragmentTitleList = new ArrayList<>();
+
+ public ViewPagerAdapter(FragmentManager manager) {
+ super(manager);
+ }
+
+ /**
+ * This method returns the fragment of the viewpager at a particular position
+ * @param position
+ */
+ @Override
+ public Fragment getItem(int position) {
+ return fragmentList.get(position);
+ }
+
+ /**
+ * This method returns the total number of fragments in the viewpager.
+ * @return size
+ */
+ @Override
+ public int getCount() {
+ return fragmentList.size();
+ }
+
+ /**
+ * This method sets the fragment and title list in the viewpager
+ * @param fragmentList List of all fragments to be displayed in the viewpager
+ * @param fragmentTitleList List of all titles of the fragments
+ */
+ public void setTabData(List fragmentList, List fragmentTitleList) {
+ this.fragmentList = fragmentList;
+ this.fragmentTitleList = fragmentTitleList;
+ }
+
+ /**
+ * This method returns the title of the page at a particular position
+ * @param position
+ */
+ @Override
+ public CharSequence getPageTitle(int position) {
+ return fragmentTitleList.get(position);
+ }
+}
diff --git a/app/src/main/java/fr/free/nrw/commons/actions/PageEditClient.java b/app/src/main/java/fr/free/nrw/commons/actions/PageEditClient.java
deleted file mode 100644
index a2d7bd543..000000000
--- a/app/src/main/java/fr/free/nrw/commons/actions/PageEditClient.java
+++ /dev/null
@@ -1,80 +0,0 @@
-package fr.free.nrw.commons.actions;
-
-import io.reactivex.Single;
-import io.reactivex.SingleOnSubscribe;
-import org.wikipedia.csrf.CsrfTokenClient;
-import org.wikipedia.dataclient.Service;
-
-import io.reactivex.Observable;
-
-/**
- * This class acts as a Client to facilitate wiki page editing
- * services to various dependency providing modules such as the Network module, the Review Controller ,etc
- *
- * The methods provided by this class will post to the Media wiki api
- * documented at: https://commons.wikimedia.org/w/api.php?action=help&modules=edit
- */
-public class PageEditClient {
-
- private final CsrfTokenClient csrfTokenClient;
- private final PageEditInterface pageEditInterface;
- private final Service service;
-
- public PageEditClient(CsrfTokenClient csrfTokenClient,
- PageEditInterface pageEditInterface,
- Service service) {
- this.csrfTokenClient = csrfTokenClient;
- this.pageEditInterface = pageEditInterface;
- this.service = service;
- }
-
- /**
- * This method is used when the content of the page is to be replaced by new content received
- * @param pagetitle Title of the page to edit
- * @param text Holds the page content
- * @param summary Edit summary
- */
- public Observable edit(String pageTitle, String text, String summary) {
- try {
- return pageEditInterface.postEdit(pageTitle, summary, text, csrfTokenClient.getTokenBlocking())
- .map(editResponse -> editResponse.edit().editSucceeded());
- } catch (Throwable throwable) {
- return Observable.just(false);
- }
- }
-
- /**
- * This method is used when we need to append something to the end of wiki page content
- * @param pagetitle Title of the page to edit
- * @param appendText The received page content is added to beginning of the page
- * @param summary Edit summary
- */
- public Observable appendEdit(String pageTitle, String appendText, String summary) {
- return Single.create((SingleOnSubscribe) emitter -> {
- try {
- emitter.onSuccess(csrfTokenClient.getTokenBlocking());
- } catch (Throwable throwable) {
- emitter.onError(throwable);
- throwable.printStackTrace();
- }
- }).flatMapObservable(token -> pageEditInterface.postAppendEdit(pageTitle, summary, appendText, token)
- .map(editResponse -> editResponse.edit().editSucceeded()));
-
- }
-
- /**
- * This method is used when we need to add something to the starting of the page
- * @param pagetitle Title of the page to edit
- * @param prependText The received page content is added to beginning of the page
- * @param summary Edit summary
- */
- public Observable prependEdit(String pageTitle, String prependText, String summary) {
- try {
- return pageEditInterface.postPrependEdit(pageTitle, summary, prependText, csrfTokenClient.getTokenBlocking())
- .map(editResponse -> editResponse.edit().editSucceeded());
- } catch (Throwable throwable) {
- return Observable.just(false);
- }
- }
-
-}
diff --git a/app/src/main/java/fr/free/nrw/commons/actions/PageEditClient.kt b/app/src/main/java/fr/free/nrw/commons/actions/PageEditClient.kt
new file mode 100644
index 000000000..0ae7e42de
--- /dev/null
+++ b/app/src/main/java/fr/free/nrw/commons/actions/PageEditClient.kt
@@ -0,0 +1,65 @@
+package fr.free.nrw.commons.actions
+
+import io.reactivex.Observable
+import org.wikipedia.csrf.CsrfTokenClient
+
+/**
+ * This class acts as a Client to facilitate wiki page editing
+ * services to various dependency providing modules such as the Network module, the Review Controller ,etc
+ *
+ * The methods provided by this class will post to the Media wiki api
+ * documented at: https://commons.wikimedia.org/w/api.php?action=help&modules=edit
+ */
+class PageEditClient(
+ private val csrfTokenClient: CsrfTokenClient,
+ private val pageEditInterface: PageEditInterface
+) {
+
+ /**
+ * Replace the content of a wiki page
+ * @param pageTitle Title of the page to edit
+ * @param text Holds the page content
+ * @param summary Edit summary
+ * @return whether the edit was successful
+ */
+ fun edit(pageTitle: String, text: String, summary: String): Observable {
+ return try {
+ pageEditInterface.postEdit(pageTitle, summary, text, csrfTokenClient.tokenBlocking)
+ .map { editResponse -> editResponse.edit()!!.editSucceeded() }
+ } catch (throwable: Throwable) {
+ Observable.just(false)
+ }
+ }
+
+ /**
+ * Append text to the end of a wiki page
+ * @param pageTitle Title of the page to edit
+ * @param appendText The received page content is added to the end of the page
+ * @param summary Edit summary
+ * @return whether the edit was successful
+ */
+ fun appendEdit(pageTitle: String, appendText: String, summary: String): Observable {
+ return try {
+ pageEditInterface.postAppendEdit(pageTitle, summary, appendText, csrfTokenClient.tokenBlocking)
+ .map { editResponse -> editResponse.edit()!!.editSucceeded() }
+ } catch (throwable: Throwable) {
+ Observable.just(false)
+ }
+ }
+
+ /**
+ * Prepend text to the beginning of a wiki page
+ * @param pageTitle Title of the page to edit
+ * @param prependText The received page content is added to the beginning of the page
+ * @param summary Edit summary
+ * @return whether the edit was successful
+ */
+ fun prependEdit(pageTitle: String, prependText: String, summary: String): Observable {
+ return try {
+ pageEditInterface.postPrependEdit(pageTitle, summary, prependText, csrfTokenClient.tokenBlocking)
+ .map { editResponse -> editResponse.edit()!!.editSucceeded() }
+ } catch (throwable: Throwable) {
+ Observable.just(false)
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/fr/free/nrw/commons/actions/PageEditInterface.java b/app/src/main/java/fr/free/nrw/commons/actions/PageEditInterface.java
deleted file mode 100644
index 8f4faf01f..000000000
--- a/app/src/main/java/fr/free/nrw/commons/actions/PageEditInterface.java
+++ /dev/null
@@ -1,76 +0,0 @@
-package fr.free.nrw.commons.actions;
-
-import androidx.annotation.NonNull;
-
-import org.wikipedia.edit.Edit;
-
-import io.reactivex.Observable;
-import retrofit2.http.Field;
-import retrofit2.http.FormUrlEncoded;
-import retrofit2.http.Headers;
-import retrofit2.http.POST;
-
-import static org.wikipedia.dataclient.Service.MW_API_PREFIX;
-
-/**
- * This interface facilitates wiki commons page editing services to the Networking module
- * which provides all network related services used by the app.
- *
- * This interface posts a form encoded request to the wikimedia API
- * with editing action as argument to edit a particular page
- */
-public interface PageEditInterface {
-
- /**
- * This method posts such that the Content which the page
- * has will be completely replaced by the value being passed to the
- * "text" field of the encoded form data
- * @param title Title of the page to edit. Cannot be used together with pageid.
- * @param summary Edit summary. Also section title when section=new and sectiontitle is not set
- * @param text Holds the page content
- * @param token A "csrf" token
- */
- @FormUrlEncoded
- @Headers("Cache-Control: no-cache")
- @POST(MW_API_PREFIX + "action=edit")
- @NonNull
- Observable postEdit(@NonNull @Field("title") String title,
- @NonNull @Field("summary") String summary,
- @NonNull @Field("text") String text,
- // NOTE: This csrf shold always be sent as the last field of form data
- @NonNull @Field("token") String token);
-
- /**
- * This method posts such that the Content which the page
- * has will be completely replaced by the value being passed to the
- * "text" field of the encoded form data
- * @param title Title of the page to edit. Cannot be used together with pageid.
- * @param summary Edit summary. Also section title when section=new and sectiontitle is not set
- * @param text The received page content is added to beginning of the page
- * @param token A "csrf" token
- */
- @FormUrlEncoded
- @Headers("Cache-Control: no-cache")
- @POST(MW_API_PREFIX + "action=edit")
- @NonNull Observable postAppendEdit(@NonNull @Field("title") String title,
- @NonNull @Field("summary") String summary,
- @NonNull @Field("appendtext") String text,
- @NonNull @Field("token") String token);
-
- /**
- * This method posts such that the Content which the page
- * has will be completely replaced by the value being passed to the
- * "text" field of the encoded form data
- * @param title Title of the page to edit. Cannot be used together with pageid.
- * @param summary Edit summary. Also section title when section=new and sectiontitle is not set
- * @param text The received page content is added to beginning of the page
- * @param token A "csrf" token
- */
- @FormUrlEncoded
- @Headers("Cache-Control: no-cache")
- @POST(MW_API_PREFIX + "action=edit")
- @NonNull Observable postPrependEdit(@NonNull @Field("title") String title,
- @NonNull @Field("summary") String summary,
- @NonNull @Field("prependtext") String text,
- @NonNull @Field("token") String token);
-}
diff --git a/app/src/main/java/fr/free/nrw/commons/actions/PageEditInterface.kt b/app/src/main/java/fr/free/nrw/commons/actions/PageEditInterface.kt
new file mode 100644
index 000000000..8b27d1288
--- /dev/null
+++ b/app/src/main/java/fr/free/nrw/commons/actions/PageEditInterface.kt
@@ -0,0 +1,76 @@
+package fr.free.nrw.commons.actions
+
+import io.reactivex.Observable
+import org.wikipedia.dataclient.Service
+import org.wikipedia.edit.Edit
+import retrofit2.http.Field
+import retrofit2.http.FormUrlEncoded
+import retrofit2.http.Headers
+import retrofit2.http.POST
+
+/**
+ * This interface facilitates wiki commons page editing services to the Networking module
+ * which provides all network related services used by the app.
+ *
+ * This interface posts a form encoded request to the wikimedia API
+ * with editing action as argument to edit a particular page
+ */
+interface PageEditInterface {
+ /**
+ * This method posts such that the Content which the page
+ * has will be completely replaced by the value being passed to the
+ * "text" field of the encoded form data
+ * @param title Title of the page to edit. Cannot be used together with pageid.
+ * @param summary Edit summary. Also section title when section=new and sectiontitle is not set
+ * @param text Holds the page content
+ * @param token A "csrf" token
+ */
+ @FormUrlEncoded
+ @Headers("Cache-Control: no-cache")
+ @POST(Service.MW_API_PREFIX + "action=edit")
+ fun postEdit(
+ @Field("title") title: String,
+ @Field("summary") summary: String,
+ @Field("text") text: String,
+ // NOTE: This csrf shold always be sent as the last field of form data
+ @Field("token") token: String
+ ): Observable
+
+ /**
+ * This method posts such that the Content which the page
+ * has will be appended with the value being passed to the
+ * "appendText" field of the encoded form data
+ * @param title Title of the page to edit. Cannot be used together with pageid.
+ * @param summary Edit summary. Also section title when section=new and sectiontitle is not set
+ * @param appendText Text to add to the end of the page
+ * @param token A "csrf" token
+ */
+ @FormUrlEncoded
+ @Headers("Cache-Control: no-cache")
+ @POST(Service.MW_API_PREFIX + "action=edit")
+ fun postAppendEdit(
+ @Field("title") title: String,
+ @Field("summary") summary: String,
+ @Field("appendtext") appendText: String,
+ @Field("token") token: String
+ ): Observable
+
+ /**
+ * This method posts such that the Content which the page
+ * has will be prepended with the value being passed to the
+ * "prependText" field of the encoded form data
+ * @param title Title of the page to edit. Cannot be used together with pageid.
+ * @param summary Edit summary. Also section title when section=new and sectiontitle is not set
+ * @param prependText Text to add to the beginning of the page
+ * @param token A "csrf" token
+ */
+ @FormUrlEncoded
+ @Headers("Cache-Control: no-cache")
+ @POST(Service.MW_API_PREFIX + "action=edit")
+ fun postPrependEdit(
+ @Field("title") title: String,
+ @Field("summary") summary: String,
+ @Field("prependtext") prependText: String,
+ @Field("token") token: String
+ ): Observable
+}
\ No newline at end of file
diff --git a/app/src/main/java/fr/free/nrw/commons/actions/ThanksClient.java b/app/src/main/java/fr/free/nrw/commons/actions/ThanksClient.java
deleted file mode 100644
index aa606a93a..000000000
--- a/app/src/main/java/fr/free/nrw/commons/actions/ThanksClient.java
+++ /dev/null
@@ -1,48 +0,0 @@
-package fr.free.nrw.commons.actions;
-
-import org.wikipedia.csrf.CsrfTokenClient;
-import org.wikipedia.dataclient.Service;
-
-import javax.inject.Inject;
-import javax.inject.Named;
-import javax.inject.Singleton;
-
-import fr.free.nrw.commons.CommonsApplication;
-import io.reactivex.Observable;
-
-/**
- * Facilitates the Wkikimedia Thanks api extention, as described in the
- * api documentation: "The Thanks extension includes an API for sending thanks"
- *
- * In simple terms this class is used by a user to thank someone for adding
- * contribution to the commons platform
- */
-@Singleton
-public class ThanksClient {
-
- private final CsrfTokenClient csrfTokenClient;
- private final Service service;
-
- @Inject
- public ThanksClient(@Named("commons-csrf") CsrfTokenClient csrfTokenClient,
- @Named("commons-service") Service service) {
- this.csrfTokenClient = csrfTokenClient;
- this.service = service;
- }
-
- /**
- * Handles the Thanking logic
- * @param revesionID The revision ID you would like to thank someone for
- * @return if thanks was successfully sent to intended recepient, returned as a boolean observable
- */
- public Observable thank(long revisionId) {
- try {
- return service.thank(String.valueOf(revisionId), null,
- csrfTokenClient.getTokenBlocking(),
- CommonsApplication.getInstance().getUserAgent())
- .map(mwQueryResponse -> mwQueryResponse.getSuccessVal() == 1);
- } catch (Throwable throwable) {
- return Observable.just(false);
- }
- }
-}
diff --git a/app/src/main/java/fr/free/nrw/commons/actions/ThanksClient.kt b/app/src/main/java/fr/free/nrw/commons/actions/ThanksClient.kt
new file mode 100644
index 000000000..d0ff6629c
--- /dev/null
+++ b/app/src/main/java/fr/free/nrw/commons/actions/ThanksClient.kt
@@ -0,0 +1,36 @@
+package fr.free.nrw.commons.actions
+
+import fr.free.nrw.commons.CommonsApplication
+import fr.free.nrw.commons.di.NetworkingModule.NAMED_COMMONS_CSRF
+import io.reactivex.Observable
+import org.wikipedia.csrf.CsrfTokenClient
+import org.wikipedia.dataclient.Service
+import org.wikipedia.dataclient.mwapi.MwPostResponse
+import javax.inject.Inject
+import javax.inject.Named
+import javax.inject.Singleton
+
+/**
+ * Client for the Wkikimedia Thanks API extension
+ * Thanks are used by a user to show gratitude to another user for their contributions
+ */
+@Singleton
+class ThanksClient @Inject constructor(
+ @param:Named(NAMED_COMMONS_CSRF) private val csrfTokenClient: CsrfTokenClient,
+ @param:Named("commons-service") private val service: Service
+) {
+ /**
+ * Thanks a user for a particular revision
+ * @param revisionId The revision ID the user would like to thank someone for
+ * @return if thanks was successfully sent to intended recipient
+ */
+ fun thank(revisionId: Long): Observable {
+ return try {
+ service.thank(revisionId.toString(), null, csrfTokenClient.tokenBlocking, CommonsApplication.getInstance().userAgent)
+ .map { mwThankPostResponse -> mwThankPostResponse.result.success== 1 }
+ } catch (throwable: Throwable) {
+ Observable.just(false)
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/app/src/main/java/fr/free/nrw/commons/auth/LoginActivity.java b/app/src/main/java/fr/free/nrw/commons/auth/LoginActivity.java
index 8b260baa6..64563ca0e 100644
--- a/app/src/main/java/fr/free/nrw/commons/auth/LoginActivity.java
+++ b/app/src/main/java/fr/free/nrw/commons/auth/LoginActivity.java
@@ -30,6 +30,7 @@ import androidx.core.content.ContextCompat;
import com.google.android.material.textfield.TextInputLayout;
import fr.free.nrw.commons.utils.ActivityUtils;
+import java.util.Locale;
import org.wikipedia.AppAdapter;
import org.wikipedia.dataclient.ServiceFactory;
import org.wikipedia.dataclient.WikiSite;
@@ -265,7 +266,7 @@ public class LoginActivity extends AccountAuthenticatorActivity {
public void onResponse(Call call,
Response response) {
loginClient.login(commonsWikiSite, username, password, null, twoFactorCode,
- response.body().query().loginToken(), new LoginCallback() {
+ response.body().query().loginToken(), Locale.getDefault().getLanguage(), new LoginCallback() {
@Override
public void success(@NonNull LoginResult result) {
Timber.d("Login Success");
@@ -338,6 +339,7 @@ public class LoginActivity extends AccountAuthenticatorActivity {
// no longer attached to activity!
return;
}
+ compositeDisposable.clear();
sessionManager.setUserLoggedIn(true);
AppAdapter.get().updateAccount(loginResult);
progressDialog.dismiss();
diff --git a/app/src/main/java/fr/free/nrw/commons/auth/SessionManager.java b/app/src/main/java/fr/free/nrw/commons/auth/SessionManager.java
index 9861a3007..a7905f8ea 100644
--- a/app/src/main/java/fr/free/nrw/commons/auth/SessionManager.java
+++ b/app/src/main/java/fr/free/nrw/commons/auth/SessionManager.java
@@ -136,4 +136,14 @@ public class SessionManager {
currentAccount = null;
});
}
+
+ /**
+ * Return a corresponding boolean preference
+ *
+ * @param key
+ * @return
+ */
+ public boolean getPreference(String key) {
+ return defaultKvStore.getBoolean(key);
+ }
}
diff --git a/app/src/main/java/fr/free/nrw/commons/bookmarks/BookmarkFragment.java b/app/src/main/java/fr/free/nrw/commons/bookmarks/BookmarkFragment.java
index c7738ddc1..c3b2604ec 100644
--- a/app/src/main/java/fr/free/nrw/commons/bookmarks/BookmarkFragment.java
+++ b/app/src/main/java/fr/free/nrw/commons/bookmarks/BookmarkFragment.java
@@ -4,35 +4,33 @@ import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
-import android.widget.AdapterView;
import android.widget.FrameLayout;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.FragmentManager;
-import androidx.viewpager.widget.ViewPager;
import com.google.android.material.tabs.TabLayout;
import fr.free.nrw.commons.contributions.MainActivity;
import fr.free.nrw.commons.di.CommonsDaggerSupportFragment;
+import fr.free.nrw.commons.explore.ParentViewPager;
+import fr.free.nrw.commons.kvstore.JsonKvStore;
import fr.free.nrw.commons.theme.BaseActivity;
import javax.inject.Inject;
import butterknife.BindView;
import butterknife.ButterKnife;
-import fr.free.nrw.commons.Media;
import fr.free.nrw.commons.R;
-import fr.free.nrw.commons.category.CategoryImagesCallback;
import fr.free.nrw.commons.contributions.ContributionController;
-import fr.free.nrw.commons.media.MediaDetailPagerFragment;
+import javax.inject.Named;
public class BookmarkFragment extends CommonsDaggerSupportFragment {
private FragmentManager supportFragmentManager;
private BookmarksPagerAdapter adapter;
@BindView(R.id.viewPagerBookmarks)
- ViewPager viewPager;
+ ParentViewPager viewPager;
@BindView(R.id.tab_layout)
TabLayout tabLayout;
@BindView(R.id.fragmentContainer)
@@ -40,6 +38,13 @@ public class BookmarkFragment extends CommonsDaggerSupportFragment {
@Inject
ContributionController controller;
+ /**
+ * To check if the user is loggedIn or not.
+ */
+ @Inject
+ @Named("default_preferences")
+ public
+ JsonKvStore applicationKvStore;
@NonNull
public static BookmarkFragment newInstance() {
@@ -48,6 +53,10 @@ public class BookmarkFragment extends CommonsDaggerSupportFragment {
return fragment;
}
+ public void setScroll(boolean canScroll){
+ viewPager.setCanScroll(canScroll);
+ }
+
@Override
public void onCreate(@Nullable final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@@ -66,15 +75,36 @@ public class BookmarkFragment extends CommonsDaggerSupportFragment {
// reference to the Fragment from FragmentManager, using findFragmentById()
supportFragmentManager = getChildFragmentManager();
- adapter = new BookmarksPagerAdapter(supportFragmentManager, getContext());
+ adapter = new BookmarksPagerAdapter(supportFragmentManager, getContext(),
+ applicationKvStore.getBoolean("login_skipped"));
viewPager.setAdapter(adapter);
tabLayout.setupWithViewPager(viewPager);
+
+ ((MainActivity)getActivity()).showTabs();
+ ((BaseActivity) getActivity()).getSupportActionBar().setDisplayHomeAsUpEnabled(false);
+
+ setupTabLayout();
return view;
}
+ /**
+ * This method sets up the tab layout.
+ * If the adapter has only one element it sets the visibility of tabLayout to gone.
+ */
+ public void setupTabLayout(){
+ tabLayout.setVisibility(View.VISIBLE);
+ if (adapter.getCount() == 1) {
+ tabLayout.setVisibility(View.GONE);
+ }
+ }
+
+
public void onBackPressed() {
- ((BookmarkListRootFragment) (adapter.getItem(tabLayout.getSelectedTabPosition())))
- .backPressed();
+ if(((BookmarkListRootFragment)(adapter.getItem(tabLayout.getSelectedTabPosition()))).backPressed()) {
+ // The event is handled internally by the adapter , no further action required.
+ return;
+ }
+ // Event is not handled by the adapter ( performed back action ) change action bar.
((BaseActivity)getActivity()).getSupportActionBar().setDisplayHomeAsUpEnabled(false);
}
}
diff --git a/app/src/main/java/fr/free/nrw/commons/bookmarks/BookmarkListRootFragment.java b/app/src/main/java/fr/free/nrw/commons/bookmarks/BookmarkListRootFragment.java
index 3ee03218a..1202a6bef 100644
--- a/app/src/main/java/fr/free/nrw/commons/bookmarks/BookmarkListRootFragment.java
+++ b/app/src/main/java/fr/free/nrw/commons/bookmarks/BookmarkListRootFragment.java
@@ -19,10 +19,13 @@ import fr.free.nrw.commons.R;
import fr.free.nrw.commons.bookmarks.locations.BookmarkLocationsFragment;
import fr.free.nrw.commons.bookmarks.pictures.BookmarkPicturesFragment;
import fr.free.nrw.commons.category.CategoryImagesCallback;
+import fr.free.nrw.commons.category.GridViewAdapter;
import fr.free.nrw.commons.contributions.MainActivity;
import fr.free.nrw.commons.di.CommonsDaggerSupportFragment;
import fr.free.nrw.commons.media.MediaDetailPagerFragment;
import fr.free.nrw.commons.navtab.NavTab;
+import java.util.ArrayList;
+import java.util.Iterator;
public class BookmarkListRootFragment extends CommonsDaggerSupportFragment implements
FragmentManager.OnBackStackChangedListener,
@@ -166,6 +169,22 @@ public class BookmarkListRootFragment extends CommonsDaggerSupportFragment imple
return null;
}
+ /**
+ * Reload media detail fragment once media is nominated
+ *
+ * @param index item position that has been nominated
+ */
+ @Override
+ public void refreshNominatedMedia(int index) {
+ if(mediaDetails != null && !listFragment.isVisible()) {
+ removeFragment(mediaDetails);
+ mediaDetails = new MediaDetailPagerFragment(false, true);
+ ((BookmarkFragment) getParentFragment()).setScroll(false);
+ setFragment(mediaDetails, listFragment);
+ mediaDetails.showImage(index);
+ }
+ }
+
/**
* This method is called on success of API call for featured images or mobile uploads. The
* viewpager will notified that number of items have changed.
@@ -189,12 +208,17 @@ public class BookmarkListRootFragment extends CommonsDaggerSupportFragment imple
((MainActivity) getActivity()).showTabs();
}
+ void moveToContributionsFragment(){
+ ((MainActivity) getActivity()).setSelectedItemId(NavTab.CONTRIBUTIONS.code());
+ ((MainActivity) getActivity()).showTabs();
+ }
@Override
public void onItemClick(AdapterView> parent, View view, int position, long id) {
Log.d("deneme8","on media clicked");
container.setVisibility(View.VISIBLE);
((BookmarkFragment)getParentFragment()).tabLayout.setVisibility(View.GONE);
mediaDetails = new MediaDetailPagerFragment(false, true);
+ ((BookmarkFragment) getParentFragment()).setScroll(false);
setFragment(mediaDetails, listFragment);
mediaDetails.showImage(position);
}
diff --git a/app/src/main/java/fr/free/nrw/commons/bookmarks/BookmarksPagerAdapter.java b/app/src/main/java/fr/free/nrw/commons/bookmarks/BookmarksPagerAdapter.java
index 1f5529105..1c308d365 100644
--- a/app/src/main/java/fr/free/nrw/commons/bookmarks/BookmarksPagerAdapter.java
+++ b/app/src/main/java/fr/free/nrw/commons/bookmarks/BookmarksPagerAdapter.java
@@ -19,7 +19,14 @@ public class BookmarksPagerAdapter extends FragmentPagerAdapter {
private ArrayList pages;
- BookmarksPagerAdapter(FragmentManager fm, Context context) {
+ /**
+ * Default Constructor
+ * @param fm
+ * @param context
+ * @param onlyPictures is true if the fragment requires only BookmarkPictureFragment
+ * (i.e. when no user is logged in).
+ */
+ BookmarksPagerAdapter(FragmentManager fm, Context context,boolean onlyPictures) {
super(fm);
pages = new ArrayList<>();
Bundle picturesBundle = new Bundle();
@@ -28,13 +35,16 @@ public class BookmarksPagerAdapter extends FragmentPagerAdapter {
pages.add(new BookmarkPages(
new BookmarkListRootFragment(picturesBundle, this),
context.getString(R.string.title_page_bookmarks_pictures)));
-
- Bundle locationBundle = new Bundle();
- locationBundle.putString("categoryName", context.getString(R.string.title_page_bookmarks_locations));
- locationBundle.putInt("order", 1);
- pages.add(new BookmarkPages(
- new BookmarkListRootFragment(locationBundle, this),
- context.getString(R.string.title_page_bookmarks_locations)));
+ if (!onlyPictures) {
+ // if onlyPictures is false we also add the location fragment.
+ Bundle locationBundle = new Bundle();
+ locationBundle.putString("categoryName",
+ context.getString(R.string.title_page_bookmarks_locations));
+ locationBundle.putInt("order", 1);
+ pages.add(new BookmarkPages(
+ new BookmarkListRootFragment(locationBundle, this),
+ context.getString(R.string.title_page_bookmarks_locations)));
+ }
notifyDataSetChanged();
}
diff --git a/app/src/main/java/fr/free/nrw/commons/bookmarks/locations/BookmarkLocationsDao.java b/app/src/main/java/fr/free/nrw/commons/bookmarks/locations/BookmarkLocationsDao.java
index ff97b5119..e1a8b5b5c 100644
--- a/app/src/main/java/fr/free/nrw/commons/bookmarks/locations/BookmarkLocationsDao.java
+++ b/app/src/main/java/fr/free/nrw/commons/bookmarks/locations/BookmarkLocationsDao.java
@@ -157,6 +157,7 @@ public class BookmarkLocationsDao {
builder.setCommonsLink(cursor.getString(cursor.getColumnIndex(Table.COLUMN_COMMONS_LINK)));
return new Place(
+ cursor.getString(cursor.getColumnIndex(Table.COLUMN_LANGUAGE)),
cursor.getString(cursor.getColumnIndex(Table.COLUMN_NAME)),
Label.fromText((cursor.getString(cursor.getColumnIndex(Table.COLUMN_LABEL_TEXT)))),
cursor.getString(cursor.getColumnIndex(Table.COLUMN_DESCRIPTION)),
@@ -164,13 +165,14 @@ public class BookmarkLocationsDao {
cursor.getString(cursor.getColumnIndex(Table.COLUMN_CATEGORY)),
builder.build(),
cursor.getString(cursor.getColumnIndex(Table.COLUMN_PIC)),
- cursor.getString(cursor.getColumnIndex(Table.COLUMN_DESTROYED))
+ Boolean.parseBoolean(cursor.getString(cursor.getColumnIndex(Table.COLUMN_EXISTS)))
);
}
private ContentValues toContentValues(Place bookmarkLocation) {
ContentValues cv = new ContentValues();
cv.put(BookmarkLocationsDao.Table.COLUMN_NAME, bookmarkLocation.getName());
+ cv.put(BookmarkLocationsDao.Table.COLUMN_LANGUAGE, bookmarkLocation.getLanguage());
cv.put(BookmarkLocationsDao.Table.COLUMN_DESCRIPTION, bookmarkLocation.getLongDescription());
cv.put(BookmarkLocationsDao.Table.COLUMN_CATEGORY, bookmarkLocation.getCategory());
cv.put(BookmarkLocationsDao.Table.COLUMN_LABEL_TEXT, bookmarkLocation.getLabel().getText());
@@ -181,7 +183,7 @@ public class BookmarkLocationsDao {
cv.put(BookmarkLocationsDao.Table.COLUMN_LAT, bookmarkLocation.location.getLatitude());
cv.put(BookmarkLocationsDao.Table.COLUMN_LONG, bookmarkLocation.location.getLongitude());
cv.put(BookmarkLocationsDao.Table.COLUMN_PIC, bookmarkLocation.pic);
- cv.put(BookmarkLocationsDao.Table.COLUMN_DESTROYED, bookmarkLocation.destroyed);
+ cv.put(BookmarkLocationsDao.Table.COLUMN_EXISTS, bookmarkLocation.exists.toString());
return cv;
}
@@ -189,6 +191,7 @@ public class BookmarkLocationsDao {
public static final String TABLE_NAME = "bookmarksLocations";
static final String COLUMN_NAME = "location_name";
+ static final String COLUMN_LANGUAGE = "location_language";
static final String COLUMN_DESCRIPTION = "location_description";
static final String COLUMN_LAT = "location_lat";
static final String COLUMN_LONG = "location_long";
@@ -200,11 +203,12 @@ public class BookmarkLocationsDao {
static final String COLUMN_WIKIDATA_LINK = "location_wikidata_link";
static final String COLUMN_COMMONS_LINK = "location_commons_link";
static final String COLUMN_PIC = "location_pic";
- static final String COLUMN_DESTROYED = "location_destroyed";
+ static final String COLUMN_EXISTS = "location_exists";
// NOTE! KEEP IN SAME ORDER AS THEY ARE DEFINED UP THERE. HELPS HARD CODE COLUMN INDICES.
public static final String[] ALL_FIELDS = {
COLUMN_NAME,
+ COLUMN_LANGUAGE,
COLUMN_DESCRIPTION,
COLUMN_CATEGORY,
COLUMN_LABEL_TEXT,
@@ -216,13 +220,14 @@ public class BookmarkLocationsDao {
COLUMN_WIKIDATA_LINK,
COLUMN_COMMONS_LINK,
COLUMN_PIC,
- COLUMN_DESTROYED
+ COLUMN_EXISTS
};
static final String DROP_TABLE_STATEMENT = "DROP TABLE IF EXISTS " + TABLE_NAME;
static final String CREATE_TABLE_STATEMENT = "CREATE TABLE " + TABLE_NAME + " ("
+ COLUMN_NAME + " STRING PRIMARY KEY,"
+ + COLUMN_LANGUAGE + " STRING,"
+ COLUMN_DESCRIPTION + " STRING,"
+ COLUMN_CATEGORY + " STRING,"
+ COLUMN_LABEL_TEXT + " STRING,"
@@ -234,7 +239,7 @@ public class BookmarkLocationsDao {
+ COLUMN_WIKIDATA_LINK + " STRING,"
+ COLUMN_COMMONS_LINK + " STRING,"
+ COLUMN_PIC + " STRING,"
- + COLUMN_DESTROYED + " STRING"
+ + COLUMN_EXISTS + " STRING"
+ ");";
public static void onCreate(SQLiteDatabase db) {
@@ -287,6 +292,20 @@ public class BookmarkLocationsDao {
Timber.e(exception);
}
}
+ if (from == 13){
+ try {
+ db.execSQL("ALTER TABLE bookmarksLocations ADD COLUMN location_language STRING;");
+ } catch (SQLiteException exception){
+ Timber.e(exception);
+ }
+ }
+ if (from == 14){
+ try {
+ db.execSQL("ALTER TABLE bookmarksLocations ADD COLUMN location_exists STRING;");
+ } catch (SQLiteException exception){
+ Timber.e(exception);
+ }
+ }
}
}
}
diff --git a/app/src/main/java/fr/free/nrw/commons/campaigns/CampaignView.java b/app/src/main/java/fr/free/nrw/commons/campaigns/CampaignView.java
index f644d75e6..fed6617b4 100644
--- a/app/src/main/java/fr/free/nrw/commons/campaigns/CampaignView.java
+++ b/app/src/main/java/fr/free/nrw/commons/campaigns/CampaignView.java
@@ -57,8 +57,8 @@ public class CampaignView extends SwipableCardView {
@Override public boolean onSwipe(View view) {
view.setVisibility(View.GONE);
- /*((MainActivity) getContext()).defaultKvStore
- .putBoolean("displayCampaignsCardView", false);*/
+ ((MainActivity) getContext()).defaultKvStore
+ .putBoolean("displayCampaignsCardView", false);
ViewUtil.showLongToast(getContext(),
getResources().getString(R.string.nearby_campaign_dismiss_message));
return true;
diff --git a/app/src/main/java/fr/free/nrw/commons/category/CategoryDetailsActivity.java b/app/src/main/java/fr/free/nrw/commons/category/CategoryDetailsActivity.java
index 87704bf35..3dd3bce52 100644
--- a/app/src/main/java/fr/free/nrw/commons/category/CategoryDetailsActivity.java
+++ b/app/src/main/java/fr/free/nrw/commons/category/CategoryDetailsActivity.java
@@ -11,6 +11,7 @@ import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.widget.FrameLayout;
+import androidx.appcompat.widget.Toolbar;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
import androidx.viewpager.widget.ViewPager;
@@ -20,7 +21,7 @@ import com.google.android.material.tabs.TabLayout;
import fr.free.nrw.commons.Media;
import fr.free.nrw.commons.R;
import fr.free.nrw.commons.Utils;
-import fr.free.nrw.commons.explore.ViewPagerAdapter;
+import fr.free.nrw.commons.ViewPagerAdapter;
import fr.free.nrw.commons.explore.categories.media.CategoriesMediaFragment;
import fr.free.nrw.commons.explore.categories.parent.ParentCategoriesFragment;
import fr.free.nrw.commons.explore.categories.sub.SubCategoriesFragment;
@@ -47,7 +48,7 @@ public class CategoryDetailsActivity extends BaseActivity
@BindView(R.id.mediaContainer) FrameLayout mediaContainer;
@BindView(R.id.tab_layout) TabLayout tabLayout;
@BindView(R.id.viewPager) ViewPager viewPager;
-
+ @BindView(R.id.toolbar) Toolbar toolbar;
ViewPagerAdapter viewPagerAdapter;
@Override
@@ -60,6 +61,8 @@ public class CategoryDetailsActivity extends BaseActivity
viewPager.setAdapter(viewPagerAdapter);
viewPager.setOffscreenPageLimit(2);
tabLayout.setupWithViewPager(viewPager);
+ setSupportActionBar(toolbar);
+ getSupportActionBar().setDisplayHomeAsUpEnabled(true);
setTabs();
setPageTitle();
}
@@ -162,6 +165,19 @@ public class CategoryDetailsActivity extends BaseActivity
return null;
}
+ /**
+ * Reload media detail fragment once media is nominated
+ *
+ * @param index item position that has been nominated
+ */
+ @Override
+ public void refreshNominatedMedia(int index) {
+ if (getSupportFragmentManager().getBackStackEntryCount() == 1) {
+ onBackPressed();
+ onMediaClicked(index);
+ }
+ }
+
/**
* This method inflates the menu in the toolbar
*/
@@ -185,6 +201,9 @@ public class CategoryDetailsActivity extends BaseActivity
PageTitle title = Utils.getPageTitle(CATEGORY_PREFIX + categoryName);
Utils.handleWebUrl(this, Uri.parse(title.getCanonicalUri()));
return true;
+ case android.R.id.home:
+ onBackPressed();
+ return true;
default:
return super.onOptionsItemSelected(item);
}
@@ -197,7 +216,12 @@ public class CategoryDetailsActivity extends BaseActivity
@Override
public void onBackPressed() {
if (supportFragmentManager.getBackStackEntryCount() == 1){
- // back to search so show search toolbar and hide navigation toolbar
+
+ // the back press is handled by the mediaDetails , no further action required.
+ if(mediaDetails.backButtonClicked()){
+ return;
+ }
+
tabLayout.setVisibility(View.VISIBLE);
viewPager.setVisibility(View.VISIBLE);
mediaContainer.setVisibility(View.GONE);
diff --git a/app/src/main/java/fr/free/nrw/commons/category/CategoryImagesActivity.java b/app/src/main/java/fr/free/nrw/commons/category/CategoryImagesActivity.java
deleted file mode 100644
index de4fa1ebc..000000000
--- a/app/src/main/java/fr/free/nrw/commons/category/CategoryImagesActivity.java
+++ /dev/null
@@ -1,197 +0,0 @@
-package fr.free.nrw.commons.category;
-
-import android.content.Context;
-import android.content.Intent;
-import android.os.Bundle;
-import android.view.Menu;
-import android.view.MenuInflater;
-import android.view.MenuItem;
-import android.view.View;
-import android.widget.AdapterView;
-import androidx.fragment.app.FragmentManager;
-import androidx.fragment.app.FragmentTransaction;
-import butterknife.ButterKnife;
-import fr.free.nrw.commons.Media;
-import fr.free.nrw.commons.R;
-import fr.free.nrw.commons.explore.SearchActivity;
-import fr.free.nrw.commons.explore.categories.media.CategoriesMediaFragment;
-import fr.free.nrw.commons.media.MediaDetailPagerFragment;
-import fr.free.nrw.commons.theme.BaseActivity;
-import fr.free.nrw.commons.utils.ActivityUtils;
-
-/**
- * This activity displays pictures of a particular category
- * Its generic and simply takes the name of category name in its start intent to load all images in
- * a particular category. This activity is currently being used to display a list of featured images,
- * which is nothing but another category on wikimedia commons.
- */
-
-public class CategoryImagesActivity
- extends BaseActivity
- implements FragmentManager.OnBackStackChangedListener,
- MediaDetailPagerFragment.MediaDetailProvider,
- AdapterView.OnItemClickListener, CategoryImagesCallback {
-
-
- private FragmentManager supportFragmentManager;
- private CategoriesMediaFragment categoriesMediaFragment;
- private MediaDetailPagerFragment mediaDetails;
-
- /**
- * This method is called on backPressed of anyFragment in the activity.
- * We are changing the icon here from back to hamburger icon.
- */
- @Override
- public void onBackPressed() {
- super.onBackPressed();
- }
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_category_images);
- ButterKnife.bind(this);
-
- // Activity can call methods in the fragment by acquiring a
- // reference to the Fragment from FragmentManager, using findFragmentById()
- supportFragmentManager = getSupportFragmentManager();
- setCategoryImagesFragment();
- supportFragmentManager.addOnBackStackChangedListener(this);
- setPageTitle();
- }
-
- /**
- * Gets the categoryName from the intent and initializes the fragment for showing images of that category
- */
- private void setCategoryImagesFragment() {
- categoriesMediaFragment = new CategoriesMediaFragment();
- String categoryName = getIntent().getStringExtra("categoryName");
- if (getIntent() != null && categoryName != null) {
- Bundle arguments = new Bundle();
- arguments.putString("categoryName", categoryName);
- categoriesMediaFragment.setArguments(arguments);
- FragmentTransaction transaction = supportFragmentManager.beginTransaction();
- transaction
- .add(R.id.fragmentContainer, categoriesMediaFragment)
- .commit();
- }
- }
-
- /**
- * Gets the passed title from the intents and displays it as the page title
- */
- private void setPageTitle() {
- if (getIntent() != null && getIntent().getStringExtra("title") != null) {
- setTitle(getIntent().getStringExtra("title"));
- }
- }
-
- @Override
- public void onBackStackChanged() {
- }
-
- /**
- * This method is called onClick of media inside category details (CategoryImageListFragment).
- */
- @Override
- public void onItemClick(AdapterView> adapterView, View view, int i, long l) {
- if (mediaDetails == null || !mediaDetails.isVisible()) {
- // set isFeaturedImage true for featured images, to include author field on media detail
- mediaDetails = new MediaDetailPagerFragment(false, true);
- FragmentManager supportFragmentManager = getSupportFragmentManager();
- supportFragmentManager
- .beginTransaction()
- .hide(supportFragmentManager.getFragments().get(supportFragmentManager.getBackStackEntryCount()))
- .add(R.id.fragmentContainer, mediaDetails)
- .addToBackStack(null)
- .commit();
- // Reason for using hide, add instead of replace is to maintain scroll position after
- // coming back to the search activity. See https://github.com/commons-app/apps-android-commons/issues/1631
- // https://stackoverflow.com/questions/11353075/how-can-i-maintain-fragment-state-when-added-to-the-back-stack/19022550#19022550 supportFragmentManager.executePendingTransactions();
- }
- mediaDetails.showImage(i);
- }
-
- /**
- * Consumers should be simply using this method to use this activity.
- * @param context A Context of the application package implementing this class.
- * @param title Page title
- * @param categoryName Name of the category for displaying its images
- */
- public static void startYourself(Context context, String title, String categoryName) {
- Intent intent = new Intent(context, CategoryImagesActivity.class);
- intent.addFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT | Intent.FLAG_ACTIVITY_SINGLE_TOP);
- intent.putExtra("title", title);
- intent.putExtra("categoryName", categoryName);
- context.startActivity(intent);
- }
-
- /**
- * This method is called mediaDetailPagerFragment. It returns the Media Object at that Index
- * @param i It is the index of which media object is to be returned which is same as
- * current index of viewPager.
- * @return Media Object
- */
- @Override
- public Media getMediaAtPosition(int i) {
- return categoriesMediaFragment.getMediaAtPosition(i);
- }
-
- /**
- * This method is called on success of API call for featured Images.
- * The viewpager will notified that number of items have changed.
- */
- @Override
- public void viewPagerNotifyDataSetChanged() {
- if (mediaDetails!=null){
- mediaDetails.notifyDataSetChanged();
- }
- }
-
- /**
- * This method is called on from getCount of MediaDetailPagerFragment
- * The viewpager will contain same number of media items as that of media elements in adapter.
- * @return Total Media count in the adapter
- */
- @Override
- public int getTotalMediaCount() {
- return categoriesMediaFragment.getTotalMediaCount();
- }
-
- @Override
- public Integer getContributionStateAt(int position) {
- return null;
- }
-
- /**
- * This method inflates the menu in the toolbar
- */
- @Override
- public boolean onCreateOptionsMenu(Menu menu) {
- MenuInflater inflater = getMenuInflater();
- inflater.inflate(R.menu.menu_search, menu);
- return super.onCreateOptionsMenu(menu);
- }
-
- /**
- * This method handles the logic on ItemSelect in toolbar menu
- * Currently only 1 choice is available to open search page of the app
- */
- @Override
- public boolean onOptionsItemSelected(MenuItem item) {
-
- // Handle item selection
- switch (item.getItemId()) {
- case R.id.action_search:
- ActivityUtils.startActivityWithFlags(this, SearchActivity.class);
- return true;
- default:
- return super.onOptionsItemSelected(item);
- }
- }
-
- @Override
- public void onMediaClicked(int position) {
- // this class is unused and will be deleted
- }
-}
diff --git a/app/src/main/java/fr/free/nrw/commons/category/ContinuationClient.kt b/app/src/main/java/fr/free/nrw/commons/category/ContinuationClient.kt
index cbf9017b2..a85eee79d 100644
--- a/app/src/main/java/fr/free/nrw/commons/category/ContinuationClient.kt
+++ b/app/src/main/java/fr/free/nrw/commons/category/ContinuationClient.kt
@@ -38,4 +38,15 @@ abstract class ContinuationClient {
continuationStore.remove("$prefix$category")
}
+ /**
+ * Remove the existing the key from continuationExists and continuationStore
+ *
+ * @param prefix
+ * @param userName the username
+ */
+ protected fun resetUserContinuation(prefix: String, userName: String) {
+ continuationExists.remove("$prefix$userName")
+ continuationStore.remove("$prefix$userName")
+ }
+
}
diff --git a/app/src/main/java/fr/free/nrw/commons/contributions/ChunkInfo.kt b/app/src/main/java/fr/free/nrw/commons/contributions/ChunkInfo.kt
index 8312820d9..0ef4066a2 100644
--- a/app/src/main/java/fr/free/nrw/commons/contributions/ChunkInfo.kt
+++ b/app/src/main/java/fr/free/nrw/commons/contributions/ChunkInfo.kt
@@ -5,7 +5,7 @@ import android.os.Parcelable
import fr.free.nrw.commons.upload.UploadResult
data class ChunkInfo(
- val uploadResult: UploadResult,
+ val uploadResult: UploadResult?,
val indexOfNextChunkToUpload: Int,
val totalChunks: Int
) : Parcelable {
diff --git a/app/src/main/java/fr/free/nrw/commons/contributions/ContributionBoundaryCallback.kt b/app/src/main/java/fr/free/nrw/commons/contributions/ContributionBoundaryCallback.kt
index 613eb7046..00f002176 100644
--- a/app/src/main/java/fr/free/nrw/commons/contributions/ContributionBoundaryCallback.kt
+++ b/app/src/main/java/fr/free/nrw/commons/contributions/ContributionBoundaryCallback.kt
@@ -27,6 +27,9 @@ class ContributionBoundaryCallback @Inject constructor(
* network
*/
override fun onZeroItemsLoaded() {
+ if (sessionManager.userName != null) {
+ mediaClient.resetUserNameContinuation(sessionManager.userName!!)
+ }
fetchContributions()
}
@@ -50,21 +53,25 @@ class ContributionBoundaryCallback @Inject constructor(
* Fetches contributions using the MediaWiki API
*/
fun fetchContributions() {
- compositeDisposable.add(
- mediaClient.getMediaListForUser(sessionManager.userName!!)
- .map { mediaList ->
- mediaList.map {
- Contribution(media=it, state=Contribution.STATE_COMPLETED)
+ if (sessionManager.userName != null) {
+ compositeDisposable.add(
+ mediaClient.getMediaListForUser(sessionManager.userName!!)
+ .map { mediaList ->
+ mediaList.map {
+ Contribution(media = it, state = Contribution.STATE_COMPLETED)
+ }
}
- }
- .subscribeOn(ioThreadScheduler)
- .subscribe(::saveContributionsToDB) { error: Throwable ->
- Timber.e(
- "Failed to fetch contributions: %s",
- error.message
- )
- }
- )
+ .subscribeOn(ioThreadScheduler)
+ .subscribe(::saveContributionsToDB) { error: Throwable ->
+ Timber.e(
+ "Failed to fetch contributions: %s",
+ error.message
+ )
+ }
+ )
+ }else {
+ compositeDisposable.clear()
+ }
}
/**
diff --git a/app/src/main/java/fr/free/nrw/commons/contributions/ContributionController.java b/app/src/main/java/fr/free/nrw/commons/contributions/ContributionController.java
index 6f5fca967..15c61d836 100644
--- a/app/src/main/java/fr/free/nrw/commons/contributions/ContributionController.java
+++ b/app/src/main/java/fr/free/nrw/commons/contributions/ContributionController.java
@@ -1,13 +1,11 @@
package fr.free.nrw.commons.contributions;
-import static fr.free.nrw.commons.upload.UploadService.EXTRA_FILES;
import static fr.free.nrw.commons.wikidata.WikidataConstants.PLACE_OBJECT;
import android.Manifest;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
-import android.util.Log;
import androidx.annotation.NonNull;
import fr.free.nrw.commons.R;
import fr.free.nrw.commons.filepicker.DefaultCallback;
@@ -29,7 +27,6 @@ import javax.inject.Singleton;
public class ContributionController {
public static final String ACTION_INTERNAL_UPLOADS = "internalImageUploads";
-
private final JsonKvStore defaultKvStore;
@Inject
@@ -55,20 +52,16 @@ public class ContributionController {
}
/**
- * Check for permissions and initiate gallery picker
+ * Initiate gallery picker
*/
- public void initiateGalleryPick(Activity activity, boolean allowMultipleUploads) {
- PermissionUtils.checkPermissionsAndPerformAction(activity,
- Manifest.permission.READ_EXTERNAL_STORAGE,
- () -> initiateGalleryUpload(activity, allowMultipleUploads),
- R.string.storage_permission_title,
- R.string.read_storage_permission_rationale);
+ public void initiateGalleryPick(final Activity activity, final boolean allowMultipleUploads) {
+ initiateGalleryUpload(activity, allowMultipleUploads);
}
/**
* Open chooser for gallery uploads
*/
- private void initiateGalleryUpload(Activity activity, boolean allowMultipleUploads) {
+ private void initiateGalleryUpload(final Activity activity, final boolean allowMultipleUploads) {
setPickerConfiguration(activity, allowMultipleUploads);
FilePicker.openGallery(activity, 0);
}
@@ -130,7 +123,8 @@ public class ContributionController {
List imagesFiles) {
Intent shareIntent = new Intent(context, UploadActivity.class);
shareIntent.setAction(ACTION_INTERNAL_UPLOADS);
- shareIntent.putParcelableArrayListExtra(EXTRA_FILES, new ArrayList<>(imagesFiles));
+ shareIntent
+ .putParcelableArrayListExtra(UploadActivity.EXTRA_FILES, new ArrayList<>(imagesFiles));
Place place = defaultKvStore.getJson(PLACE_OBJECT, Place.class);
if (place != null) {
diff --git a/app/src/main/java/fr/free/nrw/commons/contributions/ContributionDao.java b/app/src/main/java/fr/free/nrw/commons/contributions/ContributionDao.java
index 7aeb7558b..a5b3c6a1b 100644
--- a/app/src/main/java/fr/free/nrw/commons/contributions/ContributionDao.java
+++ b/app/src/main/java/fr/free/nrw/commons/contributions/ContributionDao.java
@@ -39,12 +39,6 @@ public abstract class ContributionDao {
saveSynchronous(newContribution);
}
- public Completable saveAndDelete(final Contribution oldContribution,
- final Contribution newContribution) {
- return Completable
- .fromAction(() -> deleteAndSaveContribution(oldContribution, newContribution));
- }
-
@Insert(onConflict = OnConflictStrategy.REPLACE)
public abstract Single> save(List contribution);
@@ -62,11 +56,8 @@ public abstract class ContributionDao {
@Query("SELECT * from contribution WHERE pageId=:pageId")
public abstract Contribution getContribution(String pageId);
- @Query("SELECT * from contribution WHERE state=:state")
- public abstract Single> getContribution(int state);
-
- @Query("UPDATE contribution SET state=:state WHERE state in (:toUpdateStates)")
- public abstract Single updateStates(int state, int[] toUpdateStates);
+ @Query("SELECT * from contribution WHERE state IN (:states) order by media_dateUploaded DESC")
+ public abstract Single> getContribution(List states);
@Query("SELECT COUNT(*) from contribution WHERE state in (:toUpdateStates)")
public abstract Single getPendingUploads(int[] toUpdateStates);
diff --git a/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsContract.java b/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsContract.java
index f73ce5501..b8a2488b2 100644
--- a/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsContract.java
+++ b/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsContract.java
@@ -1,5 +1,6 @@
package fr.free.nrw.commons.contributions;
+import android.content.Context;
import fr.free.nrw.commons.BasePresenter;
/**
@@ -10,6 +11,8 @@ public class ContributionsContract {
public interface View {
void showMessage(String localizedMessage);
+
+ Context getContext();
}
public interface UserActionListener extends BasePresenter {
@@ -18,5 +21,6 @@ public class ContributionsContract {
void deleteUpload(Contribution contribution);
+ void saveContribution(Contribution contribution);
}
}
diff --git a/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsFragment.java b/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsFragment.java
index 3a2e29853..aee11028a 100644
--- a/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsFragment.java
+++ b/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsFragment.java
@@ -8,10 +8,7 @@ import android.Manifest;
import android.annotation.SuppressLint;
import android.content.ComponentName;
import android.content.Context;
-import android.content.Intent;
-import android.content.ServiceConnection;
import android.os.Bundle;
-import android.os.IBinder;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
@@ -28,21 +25,20 @@ import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager.OnBackStackChangedListener;
import androidx.fragment.app.FragmentTransaction;
-
import fr.free.nrw.commons.CommonsApplication;
import fr.free.nrw.commons.auth.SessionManager;
import fr.free.nrw.commons.notification.Notification;
import fr.free.nrw.commons.notification.NotificationController;
import fr.free.nrw.commons.theme.BaseActivity;
import java.util.List;
-
import javax.inject.Inject;
import javax.inject.Named;
-
import butterknife.BindView;
import butterknife.ButterKnife;
+import fr.free.nrw.commons.CommonsApplication;
import fr.free.nrw.commons.Media;
import fr.free.nrw.commons.R;
+import fr.free.nrw.commons.auth.SessionManager;
import fr.free.nrw.commons.campaigns.Campaign;
import fr.free.nrw.commons.campaigns.CampaignView;
import fr.free.nrw.commons.campaigns.CampaignsPresenter;
@@ -60,8 +56,10 @@ import fr.free.nrw.commons.mwapi.OkHttpJsonApiClient;
import fr.free.nrw.commons.nearby.NearbyController;
import fr.free.nrw.commons.nearby.NearbyNotificationCardView;
import fr.free.nrw.commons.nearby.Place;
+import fr.free.nrw.commons.notification.Notification;
import fr.free.nrw.commons.notification.NotificationActivity;
-import fr.free.nrw.commons.upload.UploadService;
+import fr.free.nrw.commons.notification.NotificationController;
+import fr.free.nrw.commons.theme.BaseActivity;
import fr.free.nrw.commons.utils.ConfigUtils;
import fr.free.nrw.commons.utils.DialogUtil;
import fr.free.nrw.commons.utils.NetworkUtils;
@@ -71,6 +69,9 @@ import io.reactivex.Observable;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.CompositeDisposable;
import io.reactivex.schedulers.Schedulers;
+import java.util.List;
+import javax.inject.Inject;
+import javax.inject.Named;
import timber.log.Timber;
public class ContributionsFragment
@@ -79,7 +80,7 @@ public class ContributionsFragment
OnBackStackChangedListener,
LocationUpdateListener,
MediaDetailProvider,
- ICampaignsView, ContributionsContract.View, Callback {
+ ICampaignsView, ContributionsContract.View, Callback{
@Inject @Named("default_preferences") JsonKvStore store;
@Inject NearbyController nearbyController;
@Inject OkHttpJsonApiClient okHttpJsonApiClient;
@@ -87,8 +88,6 @@ public class ContributionsFragment
@Inject LocationServiceManager locationManager;
@Inject NotificationController notificationController;
- private UploadService uploadService;
- private boolean isUploadServiceConnected;
private CompositeDisposable compositeDisposable = new CompositeDisposable();
private ContributionsListFragment contributionsListFragment;
@@ -99,6 +98,7 @@ public class ContributionsFragment
@BindView(R.id.card_view_nearby) public NearbyNotificationCardView nearbyNotificationCardView;
@BindView(R.id.campaigns_view) CampaignView campaignView;
@BindView(R.id.limited_connection_enabled_layout) LinearLayout limitedConnectionEnabledLayout;
+ @BindView(R.id.limited_connection_description_text_view) TextView limitedConnectionDescriptionTextView;
@Inject ContributionsPresenter contributionsPresenter;
@@ -121,32 +121,7 @@ public class ContributionsFragment
return fragment;
}
- /**
- * Since we will need to use parent activity on onAuthCookieAcquired, we have to wait
- * fragment to be attached. Latch will be responsible for this sync.
- */
- private ServiceConnection uploadServiceConnection = new ServiceConnection() {
- @Override
- public void onServiceConnected(ComponentName componentName, IBinder binder) {
- uploadService = (UploadService) ((UploadService.UploadServiceLocalBinder) binder)
- .getService();
- isUploadServiceConnected = true;
- }
-
- @Override
- public void onServiceDisconnected(ComponentName componentName) {
- // this should never happen
- Timber.e(new RuntimeException("UploadService died but the rest of the process did not!"));
- isUploadServiceConnected = false;
- }
-
- @Override
- public void onBindingDied(final ComponentName name) {
- isUploadServiceConnected = false;
- }
- };
private boolean shouldShowMediaDetailsFragment;
- private boolean isAuthCookieAcquired;
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
@@ -183,6 +158,9 @@ public class ContributionsFragment
if(shouldShowMediaDetailsFragment){
showMediaDetailPagerFragment();
}else{
+ if (mediaDetailPagerFragment != null) {
+ removeFragment(mediaDetailPagerFragment);
+ }
showContributionsListFragment();
}
@@ -190,6 +168,7 @@ public class ContributionsFragment
&& sessionManager.getCurrentAccount() != null) {
setUploadCount();
}
+ limitedConnectionEnabledLayout.setOnClickListener(toggleDescriptionListener);
setHasOptionsMenu(true);
return view;
}
@@ -265,7 +244,6 @@ public class ContributionsFragment
until fragment life time ends.
*/
if (!isFragmentAttachedBefore && getActivity() != null) {
- onAuthCookieAcquired();
isFragmentAttachedBefore = true;
}
}
@@ -305,19 +283,6 @@ public class ContributionsFragment
fetchCampaigns();
}
- /**
- * Called when onAuthCookieAcquired is called on authenticated parent activity
- */
- void onAuthCookieAcquired() {
- // Since we call onAuthCookieAcquired method from onAttach, isAdded is still false. So don't use it
- isAuthCookieAcquired=true;
- if (getActivity() != null) { // If fragment is attached to parent activity
- getActivity().bindService(getUploadServiceIntent(), uploadServiceConnection, Context.BIND_AUTO_CREATE);
- isUploadServiceConnected = true;
- }
-
- }
-
private void initFragments() {
if (null == contributionsListFragment) {
contributionsListFragment = new ContributionsListFragment();
@@ -374,7 +339,6 @@ public class ContributionsFragment
getChildFragmentManager().executePendingTransactions();
}
-
public Intent getUploadServiceIntent(){
Intent intent = new Intent(getActivity(), UploadService.class);
intent.setAction(UploadService.ACTION_START_SERVICE);
@@ -424,19 +388,22 @@ public class ContributionsFragment
showNearbyCardPermissionRationale();
});
- if (store.getBoolean("displayNearbyCardView", true)) {
- checkPermissionsAndShowNearbyCardView();
- if (nearbyNotificationCardView.cardViewVisibilityState == NearbyNotificationCardView.CardViewVisibilityState.READY) {
- nearbyNotificationCardView.setVisibility(View.VISIBLE);
+ // Notification cards should only be seen on contributions list, not in media details
+ if (mediaDetailPagerFragment == null) {
+ if (store.getBoolean("displayNearbyCardView", true)) {
+ checkPermissionsAndShowNearbyCardView();
+ if (nearbyNotificationCardView.cardViewVisibilityState == NearbyNotificationCardView.CardViewVisibilityState.READY) {
+ nearbyNotificationCardView.setVisibility(View.VISIBLE);
+ }
+
+ } else {
+ // Hide nearby notification card view if related shared preferences is false
+ nearbyNotificationCardView.setVisibility(View.GONE);
}
- } else {
- // Hide nearby notification card view if related shared preferences is false
- nearbyNotificationCardView.setVisibility(View.GONE);
+ setNotificationCount();
+ fetchCampaigns();
}
-
- setNotificationCount();
- fetchCampaigns();
}
private void checkPermissionsAndShowNearbyCardView() {
@@ -444,6 +411,7 @@ public class ContributionsFragment
onLocationPermissionGranted();
} else if (shouldShowRequestPermissionRationale(Manifest.permission.ACCESS_FINE_LOCATION)
&& store.getBoolean("displayLocationPermissionForCardView", true)
+ && !store.getBoolean("doNotAskForLocationPermission", false)
&& (((MainActivity) getActivity()).activeFragment == ActiveFragment.CONTRIBUTIONS)) {
nearbyNotificationCardView.permissionType = NearbyNotificationCardView.PermissionType.ENABLE_LOCATION_PERMISSION;
showNearbyCardPermissionRationale();
@@ -476,6 +444,7 @@ public class ContributionsFragment
private void displayYouWontSeeNearbyMessage() {
ViewUtil.showLongToast(getActivity(), getResources().getString(R.string.unable_to_display_nearest_place));
+ store.putBoolean("doNotAskForLocationPermission", true);
}
@@ -512,13 +481,6 @@ public class ContributionsFragment
locationManager.unregisterLocationManager();
locationManager.removeLocationListener(this);
super.onDestroy();
-
- if (isUploadServiceConnected) {
- if (getActivity() != null) {
- getActivity().unbindService(uploadServiceConnection);
- isUploadServiceConnected = false;
- }
- }
} catch (IllegalArgumentException | IllegalStateException exception) {
Timber.e(exception);
}
@@ -564,6 +526,9 @@ public class ContributionsFragment
if (store.getBoolean("displayCampaignsCardView", true)) {
presenter.getCampaigns();
}
+ else{
+ campaignView.setVisibility(View.GONE);
+ }
}
@Override public void showMessage(String message) {
@@ -578,7 +543,6 @@ public class ContributionsFragment
@Override public void onDestroyView() {
super.onDestroyView();
- isUploadServiceConnected = false;
presenter.onDetachView();
}
@@ -597,8 +561,9 @@ public class ContributionsFragment
@Override
public void retryUpload(Contribution contribution) {
if (NetworkUtils.isInternetConnectionEstablished(getContext())) {
- if (contribution.getState() == STATE_FAILED || contribution.getState() == STATE_PAUSED || contribution.getState()==Contribution.STATE_QUEUED_LIMITED_CONNECTION_MODE && null != uploadService) {
- uploadService.queue(contribution);
+ if (contribution.getState() == STATE_FAILED || contribution.getState() == STATE_PAUSED || contribution.getState()==Contribution.STATE_QUEUED_LIMITED_CONNECTION_MODE) {
+ contribution.setState(Contribution.STATE_QUEUED);
+ contributionsPresenter.saveContribution(contribution);
Timber.d("Restarting for %s", contribution.toString());
} else {
Timber.d("Skipping re-upload for non-failed %s", contribution.toString());
@@ -615,7 +580,21 @@ public class ContributionsFragment
*/
@Override
public void pauseUpload(Contribution contribution) {
- uploadService.pauseUpload(contribution);
+ //Pause the upload in the global singleton
+ CommonsApplication.pauseUploads.put(contribution.getPageId(), true);
+ //Retain the paused state in DB
+ contribution.setState(STATE_PAUSED);
+ contributionsPresenter.saveContribution(contribution);
+ }
+
+ /**
+ * Notify the viewpager that number of items have changed.
+ */
+ @Override
+ public void viewPagerNotifyDataSetChanged() {
+ if (mediaDetailPagerFragment != null) {
+ mediaDetailPagerFragment.notifyDataSetChanged();
+ }
}
/**
@@ -665,5 +644,41 @@ public class ContributionsFragment
}
return false;
}
+
+ // Getter for mediaDetailPagerFragment
+ public MediaDetailPagerFragment getMediaDetailPagerFragment() {
+ return mediaDetailPagerFragment;
+ }
+
+ /**
+ * Reload media detail fragment once media is nominated
+ *
+ * @param index item position that has been nominated
+ */
+ @Override
+ public void refreshNominatedMedia(int index) {
+ if(mediaDetailPagerFragment != null && !contributionsListFragment.isVisible()) {
+ removeFragment(mediaDetailPagerFragment);
+ mediaDetailPagerFragment = new MediaDetailPagerFragment(false, true);
+ mediaDetailPagerFragment.showImage(index);
+ showMediaDetailPagerFragment();
+ }
+ }
+
+ // click listener to toggle description that means uses can press the limited connection
+ // banner and description will hide. Tap again to show description.
+ private View.OnClickListener toggleDescriptionListener = new View.OnClickListener() {
+
+ @Override
+ public void onClick(View view) {
+ View view2 = limitedConnectionDescriptionTextView;
+ if (view2.getVisibility() == View.GONE) {
+ view2.setVisibility(View.VISIBLE);
+ } else {
+ view2.setVisibility(View.GONE);
+ }
+ }
+ };
+
}
diff --git a/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsListFragment.java b/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsListFragment.java
index 3147c73ac..5b04468d8 100644
--- a/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsListFragment.java
+++ b/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsListFragment.java
@@ -10,6 +10,7 @@ import android.net.Uri;
import android.os.Bundle;
import android.os.Parcelable;
import android.view.LayoutInflater;
+import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.Animation;
@@ -24,6 +25,7 @@ import androidx.recyclerview.widget.GridLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import androidx.recyclerview.widget.RecyclerView.AdapterDataObserver;
import androidx.recyclerview.widget.RecyclerView.ItemAnimator;
+import androidx.recyclerview.widget.RecyclerView.OnItemTouchListener;
import androidx.recyclerview.widget.SimpleItemAnimator;
import butterknife.BindView;
import butterknife.ButterKnife;
@@ -161,6 +163,60 @@ public class ContributionsListFragment extends CommonsDaggerSupportFragment impl
}
}
}
+
+ /**
+ * Called whenever items in the list have changed
+ * Calls viewPagerNotifyDataSetChanged() that will notify the viewpager
+ */
+ @Override
+ public void onItemRangeChanged(final int positionStart, final int itemCount) {
+ super.onItemRangeChanged(positionStart, itemCount);
+ callback.viewPagerNotifyDataSetChanged();
+ }
+ });
+
+ //Fab close on touch outside (Scrolling or taping on item triggers this action).
+ rvContributionsList.addOnItemTouchListener(new OnItemTouchListener() {
+
+ /**
+ * Silently observe and/or take over touch events sent to the RecyclerView before
+ * they are handled by either the RecyclerView itself or its child views.
+ */
+ @Override
+ public boolean onInterceptTouchEvent(@NonNull RecyclerView rv, @NonNull MotionEvent e) {
+ if (e.getAction() == MotionEvent.ACTION_DOWN) {
+ if (isFabOpen) {
+ animateFAB(isFabOpen);
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Process a touch event as part of a gesture that was claimed by returning true
+ * from a previous call to {@link #onInterceptTouchEvent}.
+ *
+ * @param rv
+ * @param e MotionEvent describing the touch event. All coordinates are in the
+ * RecyclerView's coordinate system.
+ */
+ @Override
+ public void onTouchEvent(@NonNull RecyclerView rv, @NonNull MotionEvent e) {
+ //required abstract method DO NOT DELETE
+ }
+
+ /**
+ * Called when a child of RecyclerView does not want RecyclerView and its ancestors
+ * to intercept touch events with {@link ViewGroup#onInterceptTouchEvent(MotionEvent)}.
+ *
+ * @param disallowIntercept True if the child does not want the parent to intercept
+ * touch events.
+ */
+ @Override
+ public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) {
+ //required abstract method DO NOT DELETE
+ }
+
});
}
@@ -330,7 +386,10 @@ public class ContributionsListFragment extends CommonsDaggerSupportFragment impl
public Media getMediaAtPosition(final int i) {
- return adapter.getContributionForPosition(i).getMedia();
+ if(adapter.getContributionForPosition(i) != null) {
+ return adapter.getContributionForPosition(i).getMedia();
+ }
+ return null;
}
public int getTotalMediaCount() {
@@ -368,5 +427,8 @@ public class ContributionsListFragment extends CommonsDaggerSupportFragment impl
void showDetail(int position, boolean isWikipediaButtonDisplayed);
void pauseUpload(Contribution contribution);
+
+ // Notify the viewpager that number of items have changed.
+ void viewPagerNotifyDataSetChanged();
}
}
diff --git a/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsLocalDataSource.java b/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsLocalDataSource.java
index 93499e363..dcfca2519 100644
--- a/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsLocalDataSource.java
+++ b/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsLocalDataSource.java
@@ -3,7 +3,6 @@ package fr.free.nrw.commons.contributions;
import androidx.paging.DataSource.Factory;
import io.reactivex.Completable;
import java.util.ArrayList;
-import java.util.Date;
import java.util.List;
import javax.inject.Inject;
@@ -22,8 +21,8 @@ class ContributionsLocalDataSource {
@Inject
public ContributionsLocalDataSource(
- @Named("default_preferences") JsonKvStore defaultKVStore,
- ContributionDao contributionDao) {
+ @Named("default_preferences") final JsonKvStore defaultKVStore,
+ final ContributionDao contributionDao) {
this.defaultKVStore = defaultKVStore;
this.contributionDao = contributionDao;
}
@@ -31,14 +30,14 @@ class ContributionsLocalDataSource {
/**
* Fetch default number of contributions to be show, based on user preferences
*/
- public String getString(String key) {
+ public String getString(final String key) {
return defaultKVStore.getString(key);
}
/**
* Fetch default number of contributions to be show, based on user preferences
*/
- public long getLong(String key) {
+ public long getLong(final String key) {
return defaultKVStore.getLong(key);
}
@@ -47,8 +46,8 @@ class ContributionsLocalDataSource {
* @param uri
* @return
*/
- public Contribution getContributionWithFileName(String uri) {
- List contributionWithUri = contributionDao.getContributionWithTitle(uri);
+ public Contribution getContributionWithFileName(final String uri) {
+ final List contributionWithUri = contributionDao.getContributionWithTitle(uri);
if(!contributionWithUri.isEmpty()){
return contributionWithUri.get(0);
}
@@ -60,7 +59,7 @@ class ContributionsLocalDataSource {
* @param contribution
* @return
*/
- public Completable deleteContribution(Contribution contribution) {
+ public Completable deleteContribution(final Contribution contribution) {
return contributionDao.delete(contribution);
}
@@ -68,10 +67,10 @@ class ContributionsLocalDataSource {
return contributionDao.fetchContributions();
}
- public Single> saveContributions(List contributions) {
- List contributionList = new ArrayList<>();
- for(Contribution contribution: contributions) {
- Contribution oldContribution = contributionDao.getContribution(contribution.getPageId());
+ public Single> saveContributions(final List contributions) {
+ final List contributionList = new ArrayList<>();
+ for(final Contribution contribution: contributions) {
+ final Contribution oldContribution = contributionDao.getContribution(contribution.getPageId());
if(oldContribution != null) {
contribution.setWikidataPlace(oldContribution.getWikidataPlace());
}
@@ -80,11 +79,15 @@ class ContributionsLocalDataSource {
return contributionDao.save(contributionList);
}
- public void set(String key, long value) {
+ public Completable saveContributions(Contribution contribution) {
+ return contributionDao.save(contribution);
+ }
+
+ public void set(final String key, final long value) {
defaultKVStore.putLong(key,value);
}
- public Completable updateContribution(Contribution contribution) {
+ public Completable updateContribution(final Contribution contribution) {
return contributionDao.update(contribution);
}
}
diff --git a/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsPresenter.java b/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsPresenter.java
index 2cae4f04c..002e8bc95 100644
--- a/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsPresenter.java
+++ b/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsPresenter.java
@@ -1,10 +1,18 @@
package fr.free.nrw.commons.contributions;
+import androidx.work.ExistingWorkPolicy;
+import androidx.work.OneTimeWorkRequest;
+import androidx.work.WorkManager;
import fr.free.nrw.commons.MediaDataExtractor;
import fr.free.nrw.commons.contributions.ContributionsContract.UserActionListener;
import fr.free.nrw.commons.di.CommonsApplicationModule;
+import fr.free.nrw.commons.upload.worker.UploadWorker;
import io.reactivex.Scheduler;
import io.reactivex.disposables.CompositeDisposable;
+import io.reactivex.functions.Action;
+import io.reactivex.functions.Consumer;
+import java.util.Collections;
+import java.util.List;
import javax.inject.Inject;
import javax.inject.Named;
@@ -14,7 +22,6 @@ import javax.inject.Named;
public class ContributionsPresenter implements UserActionListener {
private final ContributionsRepository repository;
- private final Scheduler mainThreadScheduler;
private final Scheduler ioThreadScheduler;
private CompositeDisposable compositeDisposable;
private ContributionsContract.View view;
@@ -23,9 +30,9 @@ public class ContributionsPresenter implements UserActionListener {
MediaDataExtractor mediaDataExtractor;
@Inject
- ContributionsPresenter(ContributionsRepository repository, @Named(CommonsApplicationModule.MAIN_THREAD) Scheduler mainThreadScheduler,@Named(CommonsApplicationModule.IO_THREAD) Scheduler ioThreadScheduler) {
+ ContributionsPresenter(ContributionsRepository repository,
+ @Named(CommonsApplicationModule.IO_THREAD) Scheduler ioThreadScheduler) {
this.repository = repository;
- this.mainThreadScheduler=mainThreadScheduler;
this.ioThreadScheduler=ioThreadScheduler;
}
@@ -57,4 +64,23 @@ public class ContributionsPresenter implements UserActionListener {
.subscribeOn(ioThreadScheduler)
.subscribe());
}
+
+ /**
+ * Update the contribution's state in the databse, upon completion, trigger the workmanager to
+ * process this contribution
+ *
+ * @param contribution
+ */
+ @Override
+ public void saveContribution(Contribution contribution) {
+ compositeDisposable.add(repository
+ .save(contribution)
+ .subscribeOn(ioThreadScheduler)
+ .subscribe(() -> {
+ WorkManager.getInstance(view.getContext().getApplicationContext())
+ .enqueueUniqueWork(
+ UploadWorker.class.getSimpleName(),
+ ExistingWorkPolicy.KEEP, OneTimeWorkRequest.from(UploadWorker.class));
+ }));
+ }
}
diff --git a/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsRepository.java b/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsRepository.java
index 17b004802..8054cfb4a 100644
--- a/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsRepository.java
+++ b/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsRepository.java
@@ -53,6 +53,10 @@ public class ContributionsRepository {
return localDataSource.saveContributions(contributions);
}
+ public Completable save(Contribution contributions){
+ return localDataSource.saveContributions(contributions);
+ }
+
public void set(String key, long value) {
localDataSource.set(key,value);
}
diff --git a/app/src/main/java/fr/free/nrw/commons/contributions/MainActivity.java b/app/src/main/java/fr/free/nrw/commons/contributions/MainActivity.java
index 24c2a1b16..059448031 100644
--- a/app/src/main/java/fr/free/nrw/commons/contributions/MainActivity.java
+++ b/app/src/main/java/fr/free/nrw/commons/contributions/MainActivity.java
@@ -1,7 +1,9 @@
package fr.free.nrw.commons.contributions;
+import android.app.Activity;
import android.content.Context;
import android.content.Intent;
+import android.content.SharedPreferences;
import android.os.Build.VERSION;
import android.os.Build.VERSION_CODES;
import android.os.Bundle;
@@ -13,13 +15,15 @@ import androidx.annotation.Nullable;
import androidx.appcompat.widget.Toolbar;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
+import androidx.work.ExistingWorkPolicy;
+import androidx.work.OneTimeWorkRequest;
+import androidx.work.WorkManager;
import butterknife.BindView;
import butterknife.ButterKnife;
import fr.free.nrw.commons.CommonsApplication;
import fr.free.nrw.commons.R;
import fr.free.nrw.commons.auth.SessionManager;
import fr.free.nrw.commons.bookmarks.BookmarkFragment;
-import fr.free.nrw.commons.category.CategoryImagesCallback;
import fr.free.nrw.commons.explore.ExploreFragment;
import fr.free.nrw.commons.kvstore.JsonKvStore;
import fr.free.nrw.commons.location.LocationServiceManager;
@@ -29,15 +33,15 @@ import fr.free.nrw.commons.navtab.MoreBottomSheetLoggedOutFragment;
import fr.free.nrw.commons.navtab.NavTab;
import fr.free.nrw.commons.navtab.NavTabLayout;
import fr.free.nrw.commons.navtab.NavTabLoggedOut;
-import fr.free.nrw.commons.nearby.NearbyNotificationCardView;
import fr.free.nrw.commons.nearby.Place;
import fr.free.nrw.commons.nearby.fragments.NearbyParentFragment;
import fr.free.nrw.commons.nearby.fragments.NearbyParentFragment.NearbyParentFragmentInstanceReadyCallback;
import fr.free.nrw.commons.notification.NotificationActivity;
import fr.free.nrw.commons.notification.NotificationController;
import fr.free.nrw.commons.quiz.QuizChecker;
+import fr.free.nrw.commons.settings.SettingsFragment;
import fr.free.nrw.commons.theme.BaseActivity;
-import fr.free.nrw.commons.upload.UploadService;
+import fr.free.nrw.commons.upload.worker.UploadWorker;
import fr.free.nrw.commons.utils.ViewUtilWrapper;
import javax.inject.Inject;
import javax.inject.Named;
@@ -64,6 +68,7 @@ public class MainActivity extends BaseActivity
private ExploreFragment exploreFragment;
private BookmarkFragment bookmarkFragment;
public ActiveFragment activeFragment;
+ private MediaDetailPagerFragment mediaDetailPagerFragment;
@Inject
public LocationServiceManager locationManager;
@@ -107,6 +112,7 @@ public class MainActivity extends BaseActivity
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
+ loadLocale();
setContentView(R.layout.main);
ButterKnife.bind(this);
setSupportActionBar(toolbar);
@@ -114,7 +120,7 @@ public class MainActivity extends BaseActivity
onSupportNavigateUp();
});
if (applicationKvStore.getBoolean("login_skipped") == true) {
- setTitle(getString(R.string.explore_tab_title_mobile));
+ setTitle(getString(R.string.navigation_item_explore));
setUpLoggedOutPager();
} else {
if(savedInstanceState == null){
@@ -123,7 +129,6 @@ public class MainActivity extends BaseActivity
loadFragment(ContributionsFragment.newInstance(),false);
}
setUpPager();
- initMain();
}
}
@@ -236,13 +241,26 @@ public class MainActivity extends BaseActivity
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putInt("viewPagerCurrentItem", viewPager.getCurrentItem());
+ outState.putString("activeFragment", activeFragment.name());
}
- private void initMain() {
- //Do not remove this, this triggers the sync service
- Intent uploadServiceIntent = new Intent(this, UploadService.class);
- uploadServiceIntent.setAction(UploadService.ACTION_START_SERVICE);
- startService(uploadServiceIntent);
+ @Override
+ protected void onRestoreInstanceState(Bundle savedInstanceState) {
+ super.onRestoreInstanceState(savedInstanceState);
+ String currentFragmentName = savedInstanceState.getString("activeFragment");
+ if(currentFragmentName == ActiveFragment.CONTRIBUTIONS.name()) {
+ setTitle(getString(R.string.contributions_fragment));
+ loadFragment(ContributionsFragment.newInstance(),false);
+ }else if(currentFragmentName == ActiveFragment.NEARBY.name()) {
+ setTitle(getString(R.string.nearby_fragment));
+ loadFragment(NearbyParentFragment.newInstance(),false);
+ }else if(currentFragmentName == ActiveFragment.EXPLORE.name()) {
+ setTitle(getString(R.string.navigation_item_explore));
+ loadFragment(ExploreFragment.newInstance(),false);
+ }else if(currentFragmentName == ActiveFragment.BOOKMARK.name()) {
+ setTitle(getString(R.string.favorites));
+ loadFragment(BookmarkFragment.newInstance(),false);
+ }
}
@Override
@@ -298,13 +316,10 @@ public class MainActivity extends BaseActivity
viewUtilWrapper
.showShortToast(getBaseContext(), getString(R.string.limited_connection_enabled));
} else {
- Intent intent = new Intent(this, UploadService.class);
- intent.setAction(UploadService.PROCESS_PENDING_LIMITED_CONNECTION_MODE_UPLOADS);
- if (VERSION.SDK_INT >= VERSION_CODES.O) {
- startForegroundService(intent);
- } else {
- startService(intent);
- }
+ WorkManager.getInstance(getApplicationContext()).enqueueUniqueWork(
+ UploadWorker.class.getSimpleName(),
+ ExistingWorkPolicy.KEEP, OneTimeWorkRequest.from(UploadWorker.class));
+
viewUtilWrapper
.showShortToast(getBaseContext(), getString(R.string.limited_connection_disabled));
}
@@ -350,4 +365,14 @@ public class MainActivity extends BaseActivity
BOOKMARK,
MORE
}
+
+ /**
+ * Load default language in onCreate from SharedPreferences
+ */
+ private void loadLocale(){
+ final SharedPreferences preferences = getSharedPreferences("Settings", Activity.MODE_PRIVATE);
+ final String language = preferences.getString("language", "");
+ final SettingsFragment settingsFragment = new SettingsFragment();
+ settingsFragment.setLocale(this, language);
+ }
}
diff --git a/app/src/main/java/fr/free/nrw/commons/contributions/WikipediaInstructionsDialogFragment.kt b/app/src/main/java/fr/free/nrw/commons/contributions/WikipediaInstructionsDialogFragment.kt
index 21f2c26d4..67f4024e0 100644
--- a/app/src/main/java/fr/free/nrw/commons/contributions/WikipediaInstructionsDialogFragment.kt
+++ b/app/src/main/java/fr/free/nrw/commons/contributions/WikipediaInstructionsDialogFragment.kt
@@ -39,7 +39,7 @@ class WikipediaInstructionsDialogFragment : DialogFragment() {
callback?.onConfirmClicked(contribution, checkbox_copy_wikicode.isChecked)
}
- dialog!!.window.setSoftInputMode(
+ dialog!!.window?.setSoftInputMode(
WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN
)
}
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 04e103cd0..e37f1942b 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
@@ -50,6 +50,7 @@ public class DBOpenHelper extends SQLiteOpenHelper {
public void deleteTable(SQLiteDatabase db, String tableName) {
try {
db.execSQL(String.format(DROP_TABLE_STATEMENT, tableName));
+ onCreate(db);
} catch (SQLiteException e) {
e.printStackTrace();
}
diff --git a/app/src/main/java/fr/free/nrw/commons/db/AppDatabase.kt b/app/src/main/java/fr/free/nrw/commons/db/AppDatabase.kt
index e83869395..f06e15da8 100644
--- a/app/src/main/java/fr/free/nrw/commons/db/AppDatabase.kt
+++ b/app/src/main/java/fr/free/nrw/commons/db/AppDatabase.kt
@@ -5,13 +5,16 @@ import androidx.room.RoomDatabase
import androidx.room.TypeConverters
import fr.free.nrw.commons.contributions.Contribution
import fr.free.nrw.commons.contributions.ContributionDao
+import fr.free.nrw.commons.upload.depicts.Depicts
+import fr.free.nrw.commons.upload.depicts.DepictsDao
/**
* The database for accessing the respective DAOs
*
*/
-@Database(entities = [Contribution::class], version = 7, exportSchema = false)
+@Database(entities = [Contribution::class,Depicts::class], version = 7, exportSchema = false)
@TypeConverters(Converters::class)
abstract class AppDatabase : RoomDatabase() {
abstract fun contributionDao(): ContributionDao
+ abstract fun DepictsDao (): DepictsDao;
}
diff --git a/app/src/main/java/fr/free/nrw/commons/db/Converters.java b/app/src/main/java/fr/free/nrw/commons/db/Converters.java
index 3156f5e2d..59b06a675 100644
--- a/app/src/main/java/fr/free/nrw/commons/db/Converters.java
+++ b/app/src/main/java/fr/free/nrw/commons/db/Converters.java
@@ -23,6 +23,27 @@ public class Converters {
return ApplicationlessInjection.getInstance(CommonsApplication.getInstance()).getCommonsApplicationComponent().gson();
}
+ /**
+ * convert DepictedItem object to string
+ * input Example -> DepictedItem depictedItem=new DepictedItem ()
+ * output Example -> string
+ */
+ @TypeConverter
+ public static String depictsItemToString(DepictedItem objects) {
+ return writeObjectToString(objects);
+ }
+
+ /**
+ * convert string to DepictedItem object
+ * output Example -> DepictedItem depictedItem=new DepictedItem ()
+ * input Example -> string
+ */
+ @TypeConverter
+ public static DepictedItem stringToDepicts(String objectList) {
+ return readObjectWithTypeToken(objectList, new TypeToken() {
+ });
+ }
+
@TypeConverter
public static Date fromTimestamp(Long value) {
return value == null ? null : new Date(value);
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 325a5a5e5..28c79c612 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
@@ -7,7 +7,6 @@ import fr.free.nrw.commons.WelcomeActivity;
import fr.free.nrw.commons.auth.LoginActivity;
import fr.free.nrw.commons.auth.SignupActivity;
import fr.free.nrw.commons.category.CategoryDetailsActivity;
-import fr.free.nrw.commons.category.CategoryImagesActivity;
import fr.free.nrw.commons.contributions.MainActivity;
import fr.free.nrw.commons.explore.depictions.WikidataItemDetailsActivity;
import fr.free.nrw.commons.explore.SearchActivity;
@@ -47,9 +46,6 @@ public abstract class ActivityBuilderModule {
@ContributesAndroidInjector
abstract NotificationActivity bindNotificationActivity();
- @ContributesAndroidInjector
- abstract CategoryImagesActivity bindFeaturedImagesActivity();
-
@ContributesAndroidInjector
abstract UploadActivity bindUploadActivity();
diff --git a/app/src/main/java/fr/free/nrw/commons/di/ApplicationlessInjection.java b/app/src/main/java/fr/free/nrw/commons/di/ApplicationlessInjection.java
index b4f4e5db8..f2bff5db7 100644
--- a/app/src/main/java/fr/free/nrw/commons/di/ApplicationlessInjection.java
+++ b/app/src/main/java/fr/free/nrw/commons/di/ApplicationlessInjection.java
@@ -8,6 +8,7 @@ import android.content.Context;
import androidx.fragment.app.Fragment;
+import dagger.android.HasAndroidInjector;
import javax.inject.Inject;
import dagger.android.AndroidInjector;
@@ -25,6 +26,7 @@ import dagger.android.support.HasSupportFragmentInjector;
*/
public class ApplicationlessInjection
implements
+ HasAndroidInjector,
HasActivityInjector,
HasFragmentInjector,
HasSupportFragmentInjector,
@@ -34,6 +36,7 @@ public class ApplicationlessInjection
private static ApplicationlessInjection instance = null;
+ @Inject DispatchingAndroidInjector