From d4fa9cfa458a69f4cebe9a1d3d3d54c4498364b4 Mon Sep 17 00:00:00 2001 From: Vivek Maskara Date: Mon, 14 Jan 2019 00:13:21 +0530 Subject: [PATCH] Use wrapper for interacting with shared preferences (#2288) * Use wrapper for accessing shared preferences across the app * Use Json kv store for storing place object * Fix tests * Fix test failure * Fix UI tests --- .../nrw/commons/SettingsActivityTest.java | 47 +--- .../free/nrw/commons/CommonsApplication.java | 14 +- .../main/java/fr/free/nrw/commons/Utils.java | 11 - .../fr/free/nrw/commons/WelcomeActivity.java | 8 +- .../free/nrw/commons/auth/LoginActivity.java | 27 ++- .../free/nrw/commons/auth/SessionManager.java | 12 +- .../locations/BookmarkLocationsDao.java | 3 +- .../locations/BookmarkLocationsFragment.java | 7 +- .../nrw/commons/campaigns/CampaignView.java | 24 +- .../commons/campaigns/CampaignsPresenter.java | 16 +- .../nrw/commons/category/CategoriesModel.java | 15 +- .../category/CategoryImagesListFragment.java | 10 +- .../contributions/ContributionController.java | 39 ++-- .../contributions/ContributionsFragment.java | 41 ++-- .../ContributionsListFragment.java | 7 +- .../ContributionsSyncAdapter.java | 10 +- .../commons/contributions/MainActivity.java | 12 +- .../commons/di/CommonsApplicationModule.java | 43 ++-- .../free/nrw/commons/di/NetworkingModule.java | 10 +- .../nrw/commons/explore/SearchActivity.java | 2 - .../categories/SearchCategoryFragment.java | 4 +- .../explore/images/SearchImageFragment.java | 4 +- .../nrw/commons/kvstore/BasicKvStore.java | 206 ++++++++++++++++++ .../free/nrw/commons/kvstore/JsonKvStore.java | 65 ++++++ .../nrw/commons/kvstore/KeyValueStore.java | 35 +++ .../fr/free/nrw/commons/location/LatLng.java | 34 ++- .../media/MediaDetailPagerFragment.java | 19 +- .../mwapi/ApacheHttpClientMediaWikiApi.java | 58 +++-- .../fr/free/nrw/commons/mwapi/EventLog.java | 6 +- .../fr/free/nrw/commons/mwapi/LogBuilder.java | 6 +- .../fr/free/nrw/commons/nearby/Label.java | 81 +++++++ .../nrw/commons/nearby/NearbyController.java | 7 +- .../nrw/commons/nearby/NearbyFragment.java | 4 +- .../commons/nearby/NearbyListFragment.java | 7 +- .../nrw/commons/nearby/NearbyMapFragment.java | 32 +-- .../nearby/NearbyNotificationCardView.java | 4 +- .../free/nrw/commons/nearby/NearbyPlaces.java | 4 +- .../fr/free/nrw/commons/nearby/Place.java | 115 ++++------ .../nrw/commons/nearby/PlaceRenderer.java | 41 ++-- .../notification/NotificationActivity.java | 8 +- .../UnreadNotificationsCheckAsync.java | 2 +- .../fr/free/nrw/commons/quiz/QuizChecker.java | 36 +-- .../commons/settings/SettingsFragment.java | 27 +-- .../free/nrw/commons/theme/BaseActivity.java | 15 +- .../commons/theme/NavigationBaseActivity.java | 13 +- .../nrw/commons/upload/FileProcessor.java | 4 +- .../fr/free/nrw/commons/upload/FileUtils.java | 13 +- .../nrw/commons/upload/UploadActivity.java | 29 +-- .../nrw/commons/upload/UploadController.java | 30 +-- .../free/nrw/commons/upload/UploadModel.java | 97 +++++---- .../nrw/commons/upload/UploadPresenter.java | 14 +- .../fr/free/nrw/commons/utils/ImageUtils.java | 10 +- .../nrw/commons/utils/ImageUtilsWrapper.java | 6 +- .../commons/wikidata/WikidataConstants.java | 1 + .../commons/wikidata/WikidataEditService.java | 13 +- .../nrw/commons/TestCommonsApplication.kt | 23 +- .../locations/BookMarkLocationDaoTest.kt | 7 +- .../mwapi/ApacheHttpClientMediaWikiApiTest.kt | 14 +- .../commons/upload/UploadControllerTest.kt | 3 +- .../nrw/commons/upload/UploadModelTest.kt | 34 ++- .../nrw/commons/upload/UploadPresenterTest.kt | 4 +- 61 files changed, 908 insertions(+), 585 deletions(-) create mode 100644 app/src/main/java/fr/free/nrw/commons/kvstore/BasicKvStore.java create mode 100644 app/src/main/java/fr/free/nrw/commons/kvstore/JsonKvStore.java create mode 100644 app/src/main/java/fr/free/nrw/commons/kvstore/KeyValueStore.java create mode 100644 app/src/main/java/fr/free/nrw/commons/nearby/Label.java diff --git a/app/src/androidTest/java/fr/free/nrw/commons/SettingsActivityTest.java b/app/src/androidTest/java/fr/free/nrw/commons/SettingsActivityTest.java index 4efdc2953..d165b3019 100644 --- a/app/src/androidTest/java/fr/free/nrw/commons/SettingsActivityTest.java +++ b/app/src/androidTest/java/fr/free/nrw/commons/SettingsActivityTest.java @@ -1,19 +1,19 @@ package fr.free.nrw.commons; -import android.content.SharedPreferences; -import android.preference.PreferenceManager; +import android.content.Context; +import android.support.test.InstrumentationRegistry; import android.support.test.espresso.Espresso; import android.support.test.espresso.matcher.PreferenceMatchers; import android.support.test.filters.LargeTest; import android.support.test.rule.ActivityTestRule; import android.support.test.runner.AndroidJUnit4; +import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; -import java.util.Map; - +import fr.free.nrw.commons.kvstore.BasicKvStore; import fr.free.nrw.commons.settings.Prefs; import fr.free.nrw.commons.settings.SettingsActivity; @@ -28,40 +28,18 @@ import static org.junit.Assert.assertEquals; @LargeTest @RunWith(AndroidJUnit4.class) public class SettingsActivityTest { - private SharedPreferences prefs; - private Map prefValues; + BasicKvStore prefs; @Rule public ActivityTestRule activityRule = - new ActivityTestRule(SettingsActivity.class, false, true) { + new ActivityTestRule<>(SettingsActivity.class, false, true); - @Override - protected void afterActivityLaunched() { - // save preferences - prefs = PreferenceManager.getDefaultSharedPreferences(this.getActivity()); - prefValues = prefs.getAll(); - } - - @Override - protected void afterActivityFinished() { - // restore preferences - SharedPreferences.Editor editor = prefs.edit(); - for (Map.Entry entry: prefValues.entrySet()) { - String key = entry.getKey(); - Object val = entry.getValue(); - if (val instanceof String) { - editor.putString(key, (String)val); - } else if (val instanceof Boolean) { - editor.putBoolean(key, (Boolean)val); - } else if (val instanceof Integer) { - editor.putInt(key, (Integer)val); - } else { - throw new RuntimeException("type not implemented: " + entry); - } - } - editor.apply(); - } - }; + @Before + public void setup() { + Context context = InstrumentationRegistry.getTargetContext(); + String storeName = context.getPackageName() + "_preferences"; + prefs = new BasicKvStore(context, storeName); + } @Test public void setRecentUploadLimitTo100() { @@ -72,7 +50,6 @@ public class SettingsActivityTest { // Try setting it to 100 Espresso.onView(withId(android.R.id.edit)) - .perform(replaceText("100")); // Click "OK" 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 e2ef94e5e..1af160a5b 100644 --- a/app/src/main/java/fr/free/nrw/commons/CommonsApplication.java +++ b/app/src/main/java/fr/free/nrw/commons/CommonsApplication.java @@ -5,7 +5,6 @@ import android.app.Application; import android.app.NotificationChannel; import android.app.NotificationManager; import android.content.Context; -import android.content.SharedPreferences; import android.database.sqlite.SQLiteDatabase; import android.os.Build; import android.os.Process; @@ -37,6 +36,7 @@ import fr.free.nrw.commons.concurrency.ThreadPoolService; import fr.free.nrw.commons.contributions.ContributionDao; import fr.free.nrw.commons.data.DBOpenHelper; import fr.free.nrw.commons.di.ApplicationlessInjection; +import fr.free.nrw.commons.kvstore.BasicKvStore; import fr.free.nrw.commons.logging.FileLoggingTree; import fr.free.nrw.commons.logging.LogUtils; import fr.free.nrw.commons.modifications.ModifierSequenceDao; @@ -59,9 +59,8 @@ public class CommonsApplication extends Application { @Inject SessionManager sessionManager; @Inject DBOpenHelper dbOpenHelper; - @Inject @Named("default_preferences") SharedPreferences defaultPrefs; - @Inject @Named("application_preferences") SharedPreferences applicationPrefs; - @Inject @Named("prefs") SharedPreferences otherPrefs; + @Inject @Named("default_preferences") BasicKvStore defaultPrefs; + @Inject @Named("application_preferences") BasicKvStore applicationPrefs; /** * Constants begin @@ -221,10 +220,9 @@ public class CommonsApplication extends Application { .subscribe(() -> { Timber.d("All accounts have been removed"); //TODO: fix preference manager - defaultPrefs.edit().clear().apply(); - applicationPrefs.edit().clear().apply(); - applicationPrefs.edit().putBoolean("firstrun", false).apply(); - otherPrefs.edit().clear().apply(); + defaultPrefs.clearAll(); + applicationPrefs.clearAll(); + applicationPrefs.putBoolean("firstrun", false); updateAllDatabases(); logoutListener.onLogoutComplete(); }); 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 c7c8da80b..b48085670 100644 --- a/app/src/main/java/fr/free/nrw/commons/Utils.java +++ b/app/src/main/java/fr/free/nrw/commons/Utils.java @@ -1,12 +1,10 @@ package fr.free.nrw.commons; -import android.app.Activity; import android.content.ActivityNotFoundException; import android.content.Context; import android.content.Intent; import android.graphics.Bitmap; import android.net.Uri; -import android.preference.PreferenceManager; import android.support.annotation.NonNull; import android.support.customtabs.CustomTabsIntent; import android.support.v4.content.ContextCompat; @@ -165,15 +163,6 @@ public class Utils { return title; } - /** - * Tells whether dark theme is active or not - * @param context Activity context - * @return The state of dark theme - */ - public static boolean isDarkTheme(Context context) { - return PreferenceManager.getDefaultSharedPreferences(context).getBoolean("theme", false); - } - /** * Launches intent to rate app * @param context diff --git a/app/src/main/java/fr/free/nrw/commons/WelcomeActivity.java b/app/src/main/java/fr/free/nrw/commons/WelcomeActivity.java index 7c131c840..5586064c7 100644 --- a/app/src/main/java/fr/free/nrw/commons/WelcomeActivity.java +++ b/app/src/main/java/fr/free/nrw/commons/WelcomeActivity.java @@ -2,7 +2,6 @@ package fr.free.nrw.commons; import android.content.Context; import android.content.Intent; -import android.content.SharedPreferences; import android.os.Bundle; import android.support.v4.view.ViewPager; import android.view.View; @@ -15,15 +14,14 @@ import javax.inject.Named; import butterknife.BindView; import butterknife.ButterKnife; import butterknife.OnClick; +import fr.free.nrw.commons.kvstore.BasicKvStore; import fr.free.nrw.commons.quiz.QuizActivity; import fr.free.nrw.commons.theme.BaseActivity; import fr.free.nrw.commons.utils.ConfigUtils; public class WelcomeActivity extends BaseActivity { - @Inject - @Named("application_preferences") - SharedPreferences prefs; + @Inject @Named("application_preferences") BasicKvStore kvStore; @BindView(R.id.welcomePager) ViewPager pager; @@ -104,7 +102,7 @@ public class WelcomeActivity extends BaseActivity { @OnClick(R.id.finishTutorialButton) public void finishTutorial() { - prefs.edit().putBoolean("firstrun", false).apply(); + kvStore.putBoolean("firstrun", false); finish(); } } 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 9840ac3eb..4b73e228d 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 @@ -7,7 +7,6 @@ import android.accounts.AccountManager; import android.app.ProgressDialog; import android.content.Context; import android.content.Intent; -import android.content.SharedPreferences; import android.net.Uri; import android.os.Bundle; import android.support.annotation.ColorRes; @@ -45,6 +44,7 @@ import fr.free.nrw.commons.WelcomeActivity; import fr.free.nrw.commons.category.CategoryImagesActivity; import fr.free.nrw.commons.contributions.MainActivity; import fr.free.nrw.commons.di.ApplicationlessInjection; +import fr.free.nrw.commons.kvstore.BasicKvStore; import fr.free.nrw.commons.mwapi.MediaWikiApi; import fr.free.nrw.commons.theme.NavigationBaseActivity; import fr.free.nrw.commons.ui.widget.HtmlTextView; @@ -67,8 +67,12 @@ public class LoginActivity extends AccountAuthenticatorActivity { @Inject MediaWikiApi mwApi; @Inject SessionManager sessionManager; - @Inject @Named("application_preferences") SharedPreferences prefs; - @Inject @Named("default_preferences") SharedPreferences defaultPrefs; + @Inject + @Named("application_preferences") + BasicKvStore applicationKvStore; + @Inject + @Named("default_preferences") + BasicKvStore defaultKvStore; @BindView(R.id.loginButton) Button loginButton; @BindView(R.id.signupButton) Button signupButton; @@ -95,16 +99,17 @@ public class LoginActivity extends AccountAuthenticatorActivity { @Override public void onCreate(Bundle savedInstanceState) { - setTheme(Utils.isDarkTheme(this) ? R.style.DarkAppTheme : R.style.LightAppTheme); - getDelegate().installViewFactory(); - getDelegate().onCreate(savedInstanceState); - super.onCreate(savedInstanceState); ApplicationlessInjection .getInstance(this.getApplicationContext()) .getCommonsApplicationComponent() .inject(this); + boolean isDarkTheme = defaultKvStore.getBoolean("theme", false); + setTheme(isDarkTheme ? R.style.DarkAppTheme : R.style.LightAppTheme); + getDelegate().installViewFactory(); + getDelegate().onCreate(savedInstanceState); + setContentView(R.layout.activity_login); ButterKnife.bind(this); @@ -152,7 +157,7 @@ public class LoginActivity extends AccountAuthenticatorActivity { * It redirects the user to Explore Activity. */ private void skipLogin() { - prefs.edit().putBoolean("login_skipped", true).apply(); + applicationKvStore.putBoolean("login_skipped", true); CategoryImagesActivity.startYourself(this, getString(R.string.title_activity_explore), FEATURED_IMAGES_CATEGORY); finish(); @@ -176,19 +181,19 @@ public class LoginActivity extends AccountAuthenticatorActivity { @Override protected void onResume() { super.onResume(); - if (prefs.getBoolean("firstrun", true)) { + if (applicationKvStore.getBoolean("firstrun", true)) { WelcomeActivity.startYourself(this); } if (sessionManager.getCurrentAccount() != null && sessionManager.isUserLoggedIn() && sessionManager.getCachedAuthCookie() != null) { - prefs.edit().putBoolean("login_skipped", false).apply(); + applicationKvStore.putBoolean("login_skipped", false); sessionManager.revalidateAuthToken(); startMainActivity(); } - if (prefs.getBoolean("login_skipped", false)){ + if (applicationKvStore.getBoolean("login_skipped", false)) { skipLogin(); } 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 20cd06ed8..3c3e1a834 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 @@ -5,12 +5,12 @@ import android.accounts.AccountAuthenticatorResponse; import android.accounts.AccountManager; import android.content.ContentResolver; import android.content.Context; -import android.content.SharedPreferences; import android.os.Bundle; import javax.annotation.Nullable; import fr.free.nrw.commons.BuildConfig; +import fr.free.nrw.commons.kvstore.BasicKvStore; import fr.free.nrw.commons.mwapi.MediaWikiApi; import io.reactivex.Completable; import io.reactivex.Observable; @@ -27,17 +27,17 @@ public class SessionManager { private final Context context; private final MediaWikiApi mediaWikiApi; private Account currentAccount; // Unlike a savings account... ;-) - private SharedPreferences sharedPreferences; + private BasicKvStore defaultKvStore; private static final String KEY_RAWUSERNAME = "rawusername"; private Bundle userdata = new Bundle(); public SessionManager(Context context, MediaWikiApi mediaWikiApi, - SharedPreferences sharedPreferences) { + BasicKvStore defaultKvStore) { this.context = context; this.mediaWikiApi = mediaWikiApi; this.currentAccount = null; - this.sharedPreferences = sharedPreferences; + this.defaultKvStore = defaultKvStore; } /** @@ -154,11 +154,11 @@ public class SessionManager { } public String getCachedAuthCookie() { - return sharedPreferences.getString("getAuthCookie", null); + return defaultKvStore.getString("getAuthCookie", null); } public boolean isUserLoggedIn() { - return sharedPreferences.getBoolean("isUserLoggedIn", false); + return defaultKvStore.getBoolean("isUserLoggedIn", false); } public void forceLogin(Context context) { 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 06f1b26f5..ad3cdf98e 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 @@ -16,6 +16,7 @@ import javax.inject.Named; import javax.inject.Provider; import fr.free.nrw.commons.location.LatLng; +import fr.free.nrw.commons.nearby.Label; import fr.free.nrw.commons.nearby.Place; import fr.free.nrw.commons.nearby.Sitelinks; @@ -154,7 +155,7 @@ public class BookmarkLocationsDao { return new Place( cursor.getString(cursor.getColumnIndex(Table.COLUMN_NAME)), - Place.Label.fromText((cursor.getString(cursor.getColumnIndex(Table.COLUMN_LABEL_TEXT)))), + Label.fromText((cursor.getString(cursor.getColumnIndex(Table.COLUMN_LABEL_TEXT)))), cursor.getString(cursor.getColumnIndex(Table.COLUMN_DESCRIPTION)), uri, location, diff --git a/app/src/main/java/fr/free/nrw/commons/bookmarks/locations/BookmarkLocationsFragment.java b/app/src/main/java/fr/free/nrw/commons/bookmarks/locations/BookmarkLocationsFragment.java index 7591cfac3..e656de9be 100644 --- a/app/src/main/java/fr/free/nrw/commons/bookmarks/locations/BookmarkLocationsFragment.java +++ b/app/src/main/java/fr/free/nrw/commons/bookmarks/locations/BookmarkLocationsFragment.java @@ -1,7 +1,6 @@ package fr.free.nrw.commons.bookmarks.locations; import android.content.Intent; -import android.content.SharedPreferences; import android.os.Bundle; import android.support.annotation.NonNull; import android.support.annotation.Nullable; @@ -29,6 +28,8 @@ import butterknife.ButterKnife; import dagger.android.support.DaggerFragment; import fr.free.nrw.commons.R; import fr.free.nrw.commons.contributions.ContributionController; +import fr.free.nrw.commons.kvstore.BasicKvStore; +import fr.free.nrw.commons.kvstore.JsonKvStore; import fr.free.nrw.commons.nearby.NearbyAdapterFactory; import fr.free.nrw.commons.nearby.Place; import fr.free.nrw.commons.utils.ImageUtils; @@ -42,8 +43,8 @@ public class BookmarkLocationsFragment extends DaggerFragment { @BindView(R.id.parentLayout) RelativeLayout parentLayout; @Inject BookmarkLocationsController controller; - @Inject @Named("direct_nearby_upload_prefs") SharedPreferences directPrefs; - @Inject @Named("default_preferences") SharedPreferences defaultPrefs; + @Inject @Named("direct_nearby_upload_prefs") JsonKvStore directKvStore; + @Inject @Named("default_preferences") BasicKvStore defaultKvStore; private NearbyAdapterFactory adapterFactory; @Inject ContributionController contributionController; 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 dec62cc1b..c93ef6f7e 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 @@ -8,15 +8,18 @@ import android.support.annotation.Nullable; import android.util.AttributeSet; import android.view.View; import android.widget.TextView; + +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.Date; + import butterknife.BindView; import butterknife.ButterKnife; import fr.free.nrw.commons.R; import fr.free.nrw.commons.contributions.MainActivity; +import fr.free.nrw.commons.kvstore.BasicKvStore; import fr.free.nrw.commons.utils.SwipableCardView; import fr.free.nrw.commons.utils.ViewUtil; -import java.text.ParseException; -import java.text.SimpleDateFormat; -import java.util.Date; /** * A view which represents a single campaign @@ -24,20 +27,21 @@ import java.util.Date; public class CampaignView extends SwipableCardView { Campaign campaign = null; private ViewHolder viewHolder; + private BasicKvStore defaultKvStore; public CampaignView(@NonNull Context context) { super(context); - init(); + init(context); } public CampaignView(@NonNull Context context, @Nullable AttributeSet attrs) { super(context, attrs); - init(); + init(context); } public CampaignView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); - init(); + init(context); } public void setCampaign(Campaign campaign) { @@ -52,16 +56,16 @@ public class CampaignView extends SwipableCardView { @Override public boolean onSwipe(View view) { view.setVisibility(View.GONE); - ((MainActivity) getContext()).prefs.edit() - .putBoolean("displayCampaignsCardView", false) - .apply(); + ((MainActivity) getContext()).defaultKvStore + .putBoolean("displayCampaignsCardView", false); ViewUtil.showLongToast(getContext(), getResources().getString(R.string.nearby_campaign_dismiss_message)); return true; } - private void init() { + private void init(Context context) { View rootView = inflate(getContext(), R.layout.layout_campagin, this); + defaultKvStore = new BasicKvStore(context, "default_preferences"); viewHolder = new ViewHolder(rootView); setOnClickListener(view -> { if (campaign != null) { diff --git a/app/src/main/java/fr/free/nrw/commons/campaigns/CampaignsPresenter.java b/app/src/main/java/fr/free/nrw/commons/campaigns/CampaignsPresenter.java index c9dac27af..59f9d8814 100644 --- a/app/src/main/java/fr/free/nrw/commons/campaigns/CampaignsPresenter.java +++ b/app/src/main/java/fr/free/nrw/commons/campaigns/CampaignsPresenter.java @@ -1,6 +1,13 @@ package fr.free.nrw.commons.campaigns; import android.util.Log; + +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.Collections; +import java.util.Date; +import java.util.List; + import fr.free.nrw.commons.BasePresenter; import fr.free.nrw.commons.MvpView; import fr.free.nrw.commons.mwapi.MediaWikiApi; @@ -9,11 +16,6 @@ import io.reactivex.SingleObserver; import io.reactivex.android.schedulers.AndroidSchedulers; import io.reactivex.disposables.Disposable; import io.reactivex.schedulers.Schedulers; -import java.text.ParseException; -import java.text.SimpleDateFormat; -import java.util.Collections; -import java.util.Date; -import java.util.List; /** * The presenter for the campaigns view, fetches the campaigns from the api and informs the view on @@ -33,7 +35,9 @@ public class CampaignsPresenter implements BasePresenter { @Override public void onDetachView() { this.view = null; - disposable.dispose(); + if (disposable != null) { + disposable.dispose(); + } } /** diff --git a/app/src/main/java/fr/free/nrw/commons/category/CategoriesModel.java b/app/src/main/java/fr/free/nrw/commons/category/CategoriesModel.java index 8375f4972..f6caf87c1 100644 --- a/app/src/main/java/fr/free/nrw/commons/category/CategoriesModel.java +++ b/app/src/main/java/fr/free/nrw/commons/category/CategoriesModel.java @@ -1,6 +1,5 @@ package fr.free.nrw.commons.category; -import android.content.SharedPreferences; import android.text.TextUtils; import java.util.ArrayList; @@ -13,6 +12,7 @@ import java.util.List; import javax.inject.Inject; import javax.inject.Named; +import fr.free.nrw.commons.kvstore.JsonKvStore; import fr.free.nrw.commons.mwapi.MediaWikiApi; import fr.free.nrw.commons.upload.GpsCategoryModel; import fr.free.nrw.commons.utils.StringSortingUtils; @@ -24,8 +24,7 @@ public class CategoriesModel implements CategoryClickedListener { private final MediaWikiApi mwApi; private final CategoryDao categoryDao; - private final SharedPreferences prefs; - private final SharedPreferences directPrefs; + private final JsonKvStore directKvStore; private HashMap> categoriesCache; private List selectedCategories; @@ -34,12 +33,10 @@ public class CategoriesModel implements CategoryClickedListener { @Inject public CategoriesModel(MediaWikiApi mwApi, CategoryDao categoryDao, - @Named("default_preferences") SharedPreferences prefs, - @Named("direct_nearby_upload_prefs") SharedPreferences directPrefs) { + @Named("direct_nearby_upload_prefs") JsonKvStore directKvStore) { this.mwApi = mwApi; this.categoryDao = categoryDao; - this.prefs = prefs; - this.directPrefs = directPrefs; + this.directKvStore = directKvStore; this.categoriesCache = new HashMap<>(); this.selectedCategories = new ArrayList<>(); } @@ -152,11 +149,11 @@ public class CategoriesModel implements CategoryClickedListener { } private boolean hasDirectCategories() { - return !directPrefs.getString("Category", "").equals(""); + return !directKvStore.getString("Category", "").equals(""); } private Observable directCategories() { - String directCategory = directPrefs.getString("Category", ""); + String directCategory = directKvStore.getString("Category", ""); List categoryList = new ArrayList<>(); Timber.d("Direct category found: " + directCategory); diff --git a/app/src/main/java/fr/free/nrw/commons/category/CategoryImagesListFragment.java b/app/src/main/java/fr/free/nrw/commons/category/CategoryImagesListFragment.java index b8e54393b..230e79ec0 100644 --- a/app/src/main/java/fr/free/nrw/commons/category/CategoryImagesListFragment.java +++ b/app/src/main/java/fr/free/nrw/commons/category/CategoryImagesListFragment.java @@ -1,7 +1,6 @@ package fr.free.nrw.commons.category; import android.annotation.SuppressLint; -import android.content.SharedPreferences; import android.os.Bundle; import android.support.annotation.Nullable; import android.view.LayoutInflater; @@ -26,6 +25,7 @@ import butterknife.ButterKnife; import dagger.android.support.DaggerFragment; import fr.free.nrw.commons.Media; import fr.free.nrw.commons.R; +import fr.free.nrw.commons.kvstore.BasicKvStore; import fr.free.nrw.commons.utils.NetworkUtils; import fr.free.nrw.commons.utils.ViewUtil; import io.reactivex.Observable; @@ -55,7 +55,9 @@ public class CategoryImagesListFragment extends DaggerFragment { private String categoryName = null; @Inject CategoryImageController controller; - @Inject @Named("category_prefs") SharedPreferences categoryPreferences; + @Inject + @Named("category_prefs") + BasicKvStore categoryKvStore; @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { @@ -91,9 +93,7 @@ public class CategoryImagesListFragment extends DaggerFragment { * @param keyword */ private void resetQueryContinueValues(String keyword) { - SharedPreferences.Editor editor = categoryPreferences.edit(); - editor.remove(keyword); - editor.apply(); + categoryKvStore.remove(keyword); } /** 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 64f2df946..01fb4eab6 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 @@ -3,7 +3,6 @@ package fr.free.nrw.commons.contributions; import android.Manifest; import android.content.Context; import android.content.Intent; -import android.content.SharedPreferences; import android.net.Uri; import android.os.Build; import android.support.v4.app.Fragment; @@ -17,10 +16,11 @@ import javax.inject.Named; import javax.inject.Singleton; import fr.free.nrw.commons.R; +import fr.free.nrw.commons.kvstore.BasicKvStore; +import fr.free.nrw.commons.kvstore.JsonKvStore; +import fr.free.nrw.commons.nearby.Place; import fr.free.nrw.commons.upload.UploadActivity; import fr.free.nrw.commons.utils.PermissionUtils; -import fr.free.nrw.commons.utils.StringUtils; -import timber.log.Timber; import static android.content.Intent.ACTION_SEND_MULTIPLE; import static android.content.Intent.EXTRA_STREAM; @@ -28,9 +28,7 @@ import static fr.free.nrw.commons.contributions.Contribution.SOURCE_CAMERA; import static fr.free.nrw.commons.contributions.Contribution.SOURCE_EXTERNAL; import static fr.free.nrw.commons.contributions.Contribution.SOURCE_GALLERY; import static fr.free.nrw.commons.upload.UploadService.EXTRA_SOURCE; -import static fr.free.nrw.commons.wikidata.WikidataConstants.IS_DIRECT_UPLOAD; -import static fr.free.nrw.commons.wikidata.WikidataConstants.WIKIDATA_ENTITY_ID_PREF; -import static fr.free.nrw.commons.wikidata.WikidataConstants.WIKIDATA_ITEM_LOCATION; +import static fr.free.nrw.commons.wikidata.WikidataConstants.PLACE_OBJECT; @Singleton public class ContributionController { @@ -48,21 +46,21 @@ public class ContributionController { public static final int NEARBY_UPLOAD_IMAGE_LIMIT = 1; private final Context context; - private final SharedPreferences defaultPrefs; - private final SharedPreferences directPrefs; + private final BasicKvStore defaultKvStore; + private final JsonKvStore directKvStore; @Inject public ContributionController(Context context, - @Named("default_preferences") SharedPreferences defaultSharedPrefs, - @Named("direct_nearby_upload_prefs") SharedPreferences directPrefs) { + @Named("default_preferences") BasicKvStore defaultKvStore, + @Named("direct_nearby_upload_prefs") JsonKvStore directKvStore) { this.context = context; - this.defaultPrefs = defaultSharedPrefs; - this.directPrefs = directPrefs; + this.defaultKvStore = defaultKvStore; + this.directKvStore = directKvStore; } public void initiateCameraPick(Fragment fragment, int requestCode) { - boolean useExtStorage = defaultPrefs.getBoolean("useExternalStorage", true); + boolean useExtStorage = defaultKvStore.getBoolean("useExternalStorage", true); if (!useExtStorage) { initiateCameraUpload(fragment, requestCode); return; @@ -117,18 +115,9 @@ public class ContributionController { shareIntent.putExtra(EXTRA_SOURCE, getSourceFromRequestCode(requestCode)); shareIntent.putExtra(EXTRA_STREAM, uriList); shareIntent.setType("image/jpeg"); - - boolean isDirectUpload = directPrefs.getBoolean(IS_DIRECT_UPLOAD, false); - - shareIntent.putExtra("isDirectUpload", isDirectUpload); - Timber.d("Put extras into image intent, isDirectUpload is " + isDirectUpload); - - String wikiDataEntityId = directPrefs.getString(WIKIDATA_ENTITY_ID_PREF, null); - String wikiDataItemLocation = directPrefs.getString(WIKIDATA_ITEM_LOCATION, null); - - if (!StringUtils.isNullOrWhiteSpace(wikiDataEntityId)) { - shareIntent.putExtra(WIKIDATA_ENTITY_ID_PREF, wikiDataEntityId); - shareIntent.putExtra(WIKIDATA_ITEM_LOCATION, wikiDataItemLocation); + Place place = directKvStore.getJson(PLACE_OBJECT, Place.class); + if (place != null) { + shareIntent.putExtra(PLACE_OBJECT, place); } return shareIntent; 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 8bd3cd7ca..1d8d35fde 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 @@ -5,7 +5,6 @@ import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.ServiceConnection; -import android.content.SharedPreferences; import android.content.pm.PackageManager; import android.database.Cursor; import android.database.DataSetObserver; @@ -17,10 +16,9 @@ import android.support.annotation.Nullable; import android.support.v4.app.Fragment; import android.support.v4.app.FragmentManager; import android.support.v4.app.FragmentTransaction; - +import android.support.v4.app.LoaderManager; import android.support.v4.content.CursorLoader; import android.support.v4.content.Loader; -import android.support.v4.app.LoaderManager; import android.support.v4.widget.CursorAdapter; import android.support.v7.app.AlertDialog; import android.util.Log; @@ -30,24 +28,25 @@ import android.view.ViewGroup; import android.widget.Adapter; import android.widget.AdapterView; import android.widget.CheckBox; - import android.widget.Toast; -import butterknife.BindView; -import butterknife.ButterKnife; -import fr.free.nrw.commons.campaigns.Campaign; -import fr.free.nrw.commons.campaigns.CampaignView; -import fr.free.nrw.commons.campaigns.CampaignsPresenter; -import fr.free.nrw.commons.campaigns.ICampaignsView; + import java.util.ArrayList; import java.util.concurrent.CountDownLatch; import javax.inject.Inject; import javax.inject.Named; +import butterknife.BindView; +import butterknife.ButterKnife; import fr.free.nrw.commons.HandlerService; import fr.free.nrw.commons.Media; import fr.free.nrw.commons.R; +import fr.free.nrw.commons.campaigns.Campaign; +import fr.free.nrw.commons.campaigns.CampaignView; +import fr.free.nrw.commons.campaigns.CampaignsPresenter; +import fr.free.nrw.commons.campaigns.ICampaignsView; import fr.free.nrw.commons.di.CommonsDaggerSupportFragment; +import fr.free.nrw.commons.kvstore.BasicKvStore; import fr.free.nrw.commons.location.LatLng; import fr.free.nrw.commons.location.LocationServiceManager; import fr.free.nrw.commons.location.LocationUpdateListener; @@ -87,7 +86,7 @@ public class ContributionsFragment { @Inject @Named("default_preferences") - SharedPreferences prefs; + BasicKvStore defaultKvStore; @Inject ContributionDao contributionDao; @Inject @@ -163,7 +162,7 @@ public class ContributionsFragment checkBox.setOnCheckedChangeListener((buttonView, isChecked) -> { if (isChecked) { // Do not ask for permission on activity start again - prefs.edit().putBoolean("displayLocationPermissionForCardView",false).apply(); + defaultKvStore.putBoolean("displayLocationPermissionForCardView",false); } }); @@ -235,7 +234,7 @@ public class ContributionsFragment ((MainActivity)getActivity()).showTabs(); // show nearby card view on contributions list is visible if (nearbyNotificationCardView != null) { - if (prefs.getBoolean("displayNearbyCardView", true)) { + if (defaultKvStore.getBoolean("displayNearbyCardView", true)) { if (nearbyNotificationCardView.cardViewVisibilityState == NearbyNotificationCardView.CardViewVisibilityState.READY) { nearbyNotificationCardView.setVisibility(View.VISIBLE); } @@ -303,7 +302,7 @@ public class ContributionsFragment @Override public Loader onCreateLoader(int i, Bundle bundle) { - int uploads = prefs.getInt(UPLOADS_SHOWING, 100); + int uploads = defaultKvStore.getInt(UPLOADS_SHOWING, 100); return new CursorLoader(getActivity(), BASE_URI, //TODO find out the reason we pass activity here ALL_FIELDS, "", null, ContributionDao.CONTRIBUTION_SORT + "LIMIT " + uploads); @@ -374,7 +373,7 @@ public class ContributionsFragment // No need to display permission request button anymore locationManager.registerLocationManager(); } else { - if (prefs.getBoolean("displayLocationPermissionForCardView", true)) { + if (defaultKvStore.getBoolean("displayLocationPermissionForCardView", true)) { // Still ask for permission DialogUtil.showAlertDialog(getActivity(), getString(R.string.nearby_card_permission_title), @@ -518,14 +517,14 @@ public class ContributionsFragment firstLocationUpdate = true; locationManager.addLocationListener(this); - boolean isSettingsChanged = prefs.getBoolean(Prefs.IS_CONTRIBUTION_COUNT_CHANGED, false); - prefs.edit().putBoolean(Prefs.IS_CONTRIBUTION_COUNT_CHANGED, false).apply(); + boolean isSettingsChanged = defaultKvStore.getBoolean(Prefs.IS_CONTRIBUTION_COUNT_CHANGED, false); + defaultKvStore.putBoolean(Prefs.IS_CONTRIBUTION_COUNT_CHANGED, false); if (isSettingsChanged) { refreshSource(); } - if (prefs.getBoolean("displayNearbyCardView", true)) { + if (defaultKvStore.getBoolean("displayNearbyCardView", true)) { checkGPS(); if (nearbyNotificationCardView.cardViewVisibilityState == NearbyNotificationCardView.CardViewVisibilityState.READY) { nearbyNotificationCardView.setVisibility(View.VISIBLE); @@ -546,7 +545,7 @@ public class ContributionsFragment if (!locationManager.isProviderEnabled()) { Timber.d("GPS is not enabled"); nearbyNotificationCardView.permissionType = NearbyNotificationCardView.PermissionType.ENABLE_GPS; - if (prefs.getBoolean("displayLocationPermissionForCardView", true)) { + if (defaultKvStore.getBoolean("displayLocationPermissionForCardView", true)) { DialogUtil.showAlertDialog(getActivity(), getString(R.string.nearby_card_permission_title), getString(R.string.nearby_card_permission_explanation), @@ -570,7 +569,7 @@ public class ContributionsFragment nearbyNotificationCardView.permissionType = NearbyNotificationCardView.PermissionType.ENABLE_LOCATION_PERMISSION; // If user didn't selected Don't ask again if (shouldShowRequestPermissionRationale(Manifest.permission.ACCESS_FINE_LOCATION) - && prefs.getBoolean("displayLocationPermissionForCardView", true)) { + && defaultKvStore.getBoolean("displayLocationPermissionForCardView", true)) { DialogUtil.showAlertDialog(getActivity(), getString(R.string.nearby_card_permission_title), getString(R.string.nearby_card_permission_explanation), @@ -703,7 +702,7 @@ public class ContributionsFragment * ask the presenter to fetch the campaigns only if user has not manually disabled it */ private void fetchCampaigns() { - if (prefs.getBoolean("displayCampaignsCardView", true)) { + if (defaultKvStore.getBoolean("displayCampaignsCardView", true)) { presenter.getCampaigns(); } } 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 b7e1b2a0d..6c662e660 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 @@ -2,7 +2,6 @@ package fr.free.nrw.commons.contributions; import android.content.Context; import android.content.Intent; -import android.content.SharedPreferences; import android.os.Bundle; import android.support.annotation.Nullable; import android.support.design.widget.FloatingActionButton; @@ -29,6 +28,8 @@ import butterknife.BindView; import butterknife.ButterKnife; import fr.free.nrw.commons.R; import fr.free.nrw.commons.di.CommonsDaggerSupportFragment; +import fr.free.nrw.commons.kvstore.BasicKvStore; +import fr.free.nrw.commons.kvstore.JsonKvStore; import fr.free.nrw.commons.utils.ConfigUtils; import fr.free.nrw.commons.utils.ImageUtils; import fr.free.nrw.commons.utils.IntentUtils; @@ -60,8 +61,8 @@ public class ContributionsListFragment extends CommonsDaggerSupportFragment { @BindView(R.id.noDataYet) TextView noDataYet; - @Inject @Named("default_preferences") SharedPreferences defaultPrefs; - @Inject @Named("direct_nearby_upload_prefs") SharedPreferences directPrefs; + @Inject @Named("default_preferences") BasicKvStore basicKvStore; + @Inject @Named("direct_nearby_upload_prefs") JsonKvStore directKvStore; @Inject ContributionController controller; private Animation fab_close; diff --git a/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsSyncAdapter.java b/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsSyncAdapter.java index aedc3f789..083faf78e 100644 --- a/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsSyncAdapter.java +++ b/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsSyncAdapter.java @@ -5,7 +5,6 @@ import android.content.AbstractThreadedSyncAdapter; import android.content.ContentProviderClient; import android.content.ContentValues; import android.content.Context; -import android.content.SharedPreferences; import android.content.SyncResult; import android.database.Cursor; import android.os.Bundle; @@ -25,6 +24,7 @@ import javax.inject.Named; import fr.free.nrw.commons.Utils; import fr.free.nrw.commons.di.ApplicationlessInjection; +import fr.free.nrw.commons.kvstore.BasicKvStore; import fr.free.nrw.commons.mwapi.LogEventResult; import fr.free.nrw.commons.mwapi.MediaWikiApi; import timber.log.Timber; @@ -43,7 +43,9 @@ public class ContributionsSyncAdapter extends AbstractThreadedSyncAdapter { @SuppressWarnings("WeakerAccess") @Inject MediaWikiApi mwApi; - @Inject @Named("prefs") SharedPreferences prefs; + @Inject + @Named("defaultKvStore") + BasicKvStore defaultKvStore; public ContributionsSyncAdapter(Context context, boolean autoInitialize) { super(context, autoInitialize); @@ -88,7 +90,7 @@ public class ContributionsSyncAdapter extends AbstractThreadedSyncAdapter { .inject(this); // This code is fraught with possibilities of race conditions, but lalalalala I can't hear you! String user = account.name; - String lastModified = prefs.getString("lastSyncTimestamp", ""); + String lastModified = defaultKvStore.getString("lastSyncTimestamp", ""); Date curTime = new Date(); LogEventResult result; Boolean done = false; @@ -151,7 +153,7 @@ public class ContributionsSyncAdapter extends AbstractThreadedSyncAdapter { done = true; } } - prefs.edit().putString("lastSyncTimestamp", toMWDate(curTime)).apply(); + defaultKvStore.putString("lastSyncTimestamp", toMWDate(curTime)); Timber.d("Oh hai, everyone! Look, a kitty!"); } 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 ab7ae57df..0446007db 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 @@ -2,7 +2,6 @@ package fr.free.nrw.commons.contributions; import android.app.AlertDialog; import android.content.Intent; -import android.content.SharedPreferences; import android.content.pm.PackageManager; import android.os.Bundle; import android.support.design.widget.TabLayout; @@ -11,7 +10,6 @@ import android.support.v4.app.FragmentManager; import android.support.v4.app.FragmentPagerAdapter; import android.support.v4.content.ContextCompat; import android.support.v4.view.ViewPager; - import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuInflater; @@ -19,7 +17,6 @@ import android.view.MenuItem; import android.view.View; import android.widget.ImageView; - import javax.inject.Inject; import javax.inject.Named; @@ -29,15 +26,12 @@ import fr.free.nrw.commons.BuildConfig; import fr.free.nrw.commons.R; import fr.free.nrw.commons.auth.AuthenticatedActivity; import fr.free.nrw.commons.auth.SessionManager; +import fr.free.nrw.commons.kvstore.BasicKvStore; import fr.free.nrw.commons.location.LocationServiceManager; import fr.free.nrw.commons.nearby.NearbyFragment; -import fr.free.nrw.commons.nearby.NearbyMapFragment; import fr.free.nrw.commons.nearby.NearbyNotificationCardView; import fr.free.nrw.commons.notification.NotificationActivity; -import fr.free.nrw.commons.theme.NavigationBaseActivity; import fr.free.nrw.commons.upload.UploadService; -import fr.free.nrw.commons.utils.PermissionUtils; -import fr.free.nrw.commons.utils.ViewUtil; import timber.log.Timber; import static android.content.ContentResolver.requestSync; @@ -55,7 +49,7 @@ public class MainActivity extends AuthenticatedActivity implements FragmentManag public LocationServiceManager locationManager; @Inject @Named("default_preferences") - public SharedPreferences prefs; + public BasicKvStore defaultKvStore; public Intent uploadServiceIntent; @@ -247,7 +241,7 @@ public class MainActivity extends AuthenticatedActivity implements FragmentManag // Tabs were invisible when Media Details Fragment is active, make them visible again on Contrib List Fragment active showTabs(); // Nearby Notification Card View was invisible when Media Details Fragment is active, make it visible again on Contrib List Fragment active, according to preferences - if (prefs.getBoolean("displayNearbyCardView", true)) { + if (defaultKvStore.getBoolean("displayNearbyCardView", true)) { if (contributionsFragment.nearbyNotificationCardView.cardViewVisibilityState == NearbyNotificationCardView.CardViewVisibilityState.READY) { contributionsFragment.nearbyNotificationCardView.setVisibility(View.VISIBLE); } diff --git a/app/src/main/java/fr/free/nrw/commons/di/CommonsApplicationModule.java b/app/src/main/java/fr/free/nrw/commons/di/CommonsApplicationModule.java index 9229cfacb..ca4e21116 100644 --- a/app/src/main/java/fr/free/nrw/commons/di/CommonsApplicationModule.java +++ b/app/src/main/java/fr/free/nrw/commons/di/CommonsApplicationModule.java @@ -3,8 +3,6 @@ package fr.free.nrw.commons.di; import android.app.Activity; import android.content.ContentProviderClient; import android.content.Context; -import android.content.SharedPreferences; -import android.preference.PreferenceManager; import android.support.v4.util.LruCache; import android.view.inputmethod.InputMethodManager; @@ -23,6 +21,8 @@ import fr.free.nrw.commons.R; import fr.free.nrw.commons.auth.AccountUtil; import fr.free.nrw.commons.auth.SessionManager; import fr.free.nrw.commons.data.DBOpenHelper; +import fr.free.nrw.commons.kvstore.BasicKvStore; +import fr.free.nrw.commons.kvstore.JsonKvStore; import fr.free.nrw.commons.location.LocationServiceManager; import fr.free.nrw.commons.mwapi.MediaWikiApi; import fr.free.nrw.commons.nearby.NearbyPlaces; @@ -32,8 +32,6 @@ import fr.free.nrw.commons.utils.ConfigUtils; import fr.free.nrw.commons.wikidata.WikidataEditListener; import fr.free.nrw.commons.wikidata.WikidataEditListenerImpl; -import static android.content.Context.MODE_PRIVATE; - @Module @SuppressWarnings({"WeakerAccess", "unused"}) public class CommonsApplicationModule { @@ -126,20 +124,21 @@ public class CommonsApplicationModule { @Provides @Named("application_preferences") - public SharedPreferences providesApplicationSharedPreferences(Context context) { - return context.getSharedPreferences("fr.free.nrw.commons", MODE_PRIVATE); + public BasicKvStore providesApplicationKvStore(Context context) { + return new BasicKvStore(context, "fr.free.nrw.commons"); } @Provides @Named("default_preferences") - public SharedPreferences providesDefaultSharedPreferences(Context context) { - return PreferenceManager.getDefaultSharedPreferences(context); + public BasicKvStore providesDefaultKvStore(Context context) { + String storeName = context.getPackageName() + "_preferences"; + return new BasicKvStore(context, storeName); } @Provides - @Named("prefs") - public SharedPreferences providesOtherSharedPreferences(Context context) { - return context.getSharedPreferences("prefs", MODE_PRIVATE); + @Named("defaultKvStore") + public BasicKvStore providesOtherKvStore(Context context) { + return new BasicKvStore(context, "defaultKvStore"); } /** @@ -149,14 +148,14 @@ public class CommonsApplicationModule { */ @Provides @Named("category_prefs") - public SharedPreferences providesCategorySharedPreferences(Context context) { - return context.getSharedPreferences("categoryPrefs", MODE_PRIVATE); + public BasicKvStore providesCategoryKvStore(Context context) { + return new BasicKvStore(context, "categoryPrefs"); } @Provides @Named("direct_nearby_upload_prefs") - public SharedPreferences providesDirectNearbyUploadPreferences(Context context) { - return context.getSharedPreferences("direct_nearby_upload_prefs", MODE_PRIVATE); + public JsonKvStore providesDirectNearbyUploadKvStore(Context context) { + return new JsonKvStore(context, "direct_nearby_upload_prefs"); } /** @@ -166,21 +165,23 @@ public class CommonsApplicationModule { */ @Provides @Named("last_read_notification_date") - public SharedPreferences providesLastReadNotificationDatePreferences(Context context) { - return context.getSharedPreferences("last_read_notification_date", MODE_PRIVATE); + public BasicKvStore providesLastReadNotificationDateKvStore(Context context) { + return new BasicKvStore(context, "last_read_notification_date"); } @Provides - public UploadController providesUploadController(SessionManager sessionManager, @Named("default_preferences") SharedPreferences sharedPreferences, Context context) { - return new UploadController(sessionManager, context, sharedPreferences); + public UploadController providesUploadController(SessionManager sessionManager, + @Named("default_preferences") BasicKvStore kvStore, + Context context) { + return new UploadController(sessionManager, context, kvStore); } @Provides @Singleton public SessionManager providesSessionManager(Context context, MediaWikiApi mediaWikiApi, - @Named("default_preferences") SharedPreferences sharedPreferences) { - return new SessionManager(context, mediaWikiApi, sharedPreferences); + @Named("default_preferences") BasicKvStore defaultKvStore) { + return new SessionManager(context, mediaWikiApi, defaultKvStore); } @Provides diff --git a/app/src/main/java/fr/free/nrw/commons/di/NetworkingModule.java b/app/src/main/java/fr/free/nrw/commons/di/NetworkingModule.java index e894f8ab9..c982cd892 100644 --- a/app/src/main/java/fr/free/nrw/commons/di/NetworkingModule.java +++ b/app/src/main/java/fr/free/nrw/commons/di/NetworkingModule.java @@ -1,21 +1,21 @@ package fr.free.nrw.commons.di; import android.content.Context; -import android.content.SharedPreferences; import android.support.annotation.NonNull; import com.google.gson.Gson; import com.google.gson.GsonBuilder; import java.io.File; - import java.util.concurrent.TimeUnit; + import javax.inject.Named; import javax.inject.Singleton; import dagger.Module; import dagger.Provides; import fr.free.nrw.commons.BuildConfig; +import fr.free.nrw.commons.kvstore.BasicKvStore; import fr.free.nrw.commons.mwapi.ApacheHttpClientMediaWikiApi; import fr.free.nrw.commons.mwapi.MediaWikiApi; import okhttp3.Cache; @@ -41,11 +41,11 @@ public class NetworkingModule { @Provides @Singleton public MediaWikiApi provideMediaWikiApi(Context context, - @Named("default_preferences") SharedPreferences defaultPreferences, - @Named("category_prefs") SharedPreferences categoryPrefs, + @Named("default_preferences") BasicKvStore defaultKvStore, + @Named("category_prefs") BasicKvStore categoryKvStore, Gson gson, OkHttpClient okHttpClient) { - return new ApacheHttpClientMediaWikiApi(context, BuildConfig.WIKIMEDIA_API_HOST, BuildConfig.WIKIDATA_API_HOST, defaultPreferences, categoryPrefs, gson, okHttpClient); + return new ApacheHttpClientMediaWikiApi(context, BuildConfig.WIKIMEDIA_API_HOST, BuildConfig.WIKIDATA_API_HOST, defaultKvStore, categoryKvStore, gson, okHttpClient); } @Provides diff --git a/app/src/main/java/fr/free/nrw/commons/explore/SearchActivity.java b/app/src/main/java/fr/free/nrw/commons/explore/SearchActivity.java index 489cc99fd..9d7ba08ca 100644 --- a/app/src/main/java/fr/free/nrw/commons/explore/SearchActivity.java +++ b/app/src/main/java/fr/free/nrw/commons/explore/SearchActivity.java @@ -1,9 +1,7 @@ package fr.free.nrw.commons.explore; -import android.content.res.Configuration; import android.database.DataSetObserver; import android.os.Bundle; -import android.preference.PreferenceManager; import android.support.design.widget.TabLayout; import android.support.v4.app.Fragment; import android.support.v4.app.FragmentManager; diff --git a/app/src/main/java/fr/free/nrw/commons/explore/categories/SearchCategoryFragment.java b/app/src/main/java/fr/free/nrw/commons/explore/categories/SearchCategoryFragment.java index 983127c69..64983d9e5 100644 --- a/app/src/main/java/fr/free/nrw/commons/explore/categories/SearchCategoryFragment.java +++ b/app/src/main/java/fr/free/nrw/commons/explore/categories/SearchCategoryFragment.java @@ -1,7 +1,6 @@ package fr.free.nrw.commons.explore.categories; -import android.content.SharedPreferences; import android.content.res.Configuration; import android.os.Bundle; import android.os.Handler; @@ -31,6 +30,7 @@ import fr.free.nrw.commons.category.CategoryDetailsActivity; import fr.free.nrw.commons.di.CommonsDaggerSupportFragment; import fr.free.nrw.commons.explore.recentsearches.RecentSearch; import fr.free.nrw.commons.explore.recentsearches.RecentSearchesDao; +import fr.free.nrw.commons.kvstore.BasicKvStore; import fr.free.nrw.commons.mwapi.MediaWikiApi; import fr.free.nrw.commons.utils.NetworkUtils; import fr.free.nrw.commons.utils.ViewUtil; @@ -60,7 +60,7 @@ public class SearchCategoryFragment extends CommonsDaggerSupportFragment { @Inject RecentSearchesDao recentSearchesDao; @Inject MediaWikiApi mwApi; - @Inject @Named("default_preferences") SharedPreferences prefs; + @Inject @Named("default_preferences") BasicKvStore basicKvStore; private RVRendererAdapter categoriesAdapter; private List queryList = new ArrayList<>(); diff --git a/app/src/main/java/fr/free/nrw/commons/explore/images/SearchImageFragment.java b/app/src/main/java/fr/free/nrw/commons/explore/images/SearchImageFragment.java index f3beadfcf..c8130844a 100644 --- a/app/src/main/java/fr/free/nrw/commons/explore/images/SearchImageFragment.java +++ b/app/src/main/java/fr/free/nrw/commons/explore/images/SearchImageFragment.java @@ -2,7 +2,6 @@ package fr.free.nrw.commons.explore.images; import android.annotation.SuppressLint; -import android.content.SharedPreferences; import android.content.res.Configuration; import android.os.Bundle; import android.os.Handler; @@ -33,6 +32,7 @@ import fr.free.nrw.commons.di.CommonsDaggerSupportFragment; import fr.free.nrw.commons.explore.SearchActivity; import fr.free.nrw.commons.explore.recentsearches.RecentSearch; import fr.free.nrw.commons.explore.recentsearches.RecentSearchesDao; +import fr.free.nrw.commons.kvstore.BasicKvStore; import fr.free.nrw.commons.mwapi.MediaWikiApi; import fr.free.nrw.commons.utils.NetworkUtils; import fr.free.nrw.commons.utils.ViewUtil; @@ -62,7 +62,7 @@ public class SearchImageFragment extends CommonsDaggerSupportFragment { @Inject RecentSearchesDao recentSearchesDao; @Inject MediaWikiApi mwApi; - @Inject @Named("default_preferences") SharedPreferences prefs; + @Inject @Named("default_preferences") BasicKvStore defaultKvStore; private RVRendererAdapter imagesAdapter; private List queryList = new ArrayList<>(); diff --git a/app/src/main/java/fr/free/nrw/commons/kvstore/BasicKvStore.java b/app/src/main/java/fr/free/nrw/commons/kvstore/BasicKvStore.java new file mode 100644 index 000000000..4a12bdcd4 --- /dev/null +++ b/app/src/main/java/fr/free/nrw/commons/kvstore/BasicKvStore.java @@ -0,0 +1,206 @@ +package fr.free.nrw.commons.kvstore; + +import android.content.Context; +import android.content.SharedPreferences; +import android.support.annotation.Nullable; + +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + +import timber.log.Timber; + +public class BasicKvStore implements KeyValueStore { + private static final String KEY_VERSION = "__version__"; + /* + This class only performs puts, sets and clears. + A commit returns a boolean indicating whether it has succeeded, we are not throwing an exception as it will + require the dev to handle it in every usage - instead we will pass on this boolean so it can be evaluated if needed. + */ + private final SharedPreferences _store; + + public BasicKvStore(Context context, String storeName) { + _store = context.getSharedPreferences(storeName, Context.MODE_PRIVATE); + } + + /** + * If you don't want onVersionUpdate to be called on a fresh creation, the first version supplied for the kvstore should be set to 0. + */ + public BasicKvStore(Context context, String storeName, int version) { + this(context,storeName,version,false); + } + + public BasicKvStore(Context context, String storeName, int version, boolean clearAllOnUpgrade) { + _store = context.getSharedPreferences(storeName, Context.MODE_PRIVATE); + int oldVersion = getInt(KEY_VERSION); + + if (version > oldVersion) { + Timber.i("version updated from %s to %s, with clearFlag %b", oldVersion, version, clearAllOnUpgrade); + onVersionUpdate(oldVersion, version, clearAllOnUpgrade); + } + + if (version < oldVersion) { + throw new IllegalArgumentException( + "kvstore downgrade not allowed, old version:" + oldVersion + ", new version: " + + version); + } + //Keep this statement at the end so that clearing of store does not cause version also to get removed. + putIntInternal(KEY_VERSION, version); + } + + public void onVersionUpdate(int oldVersion, int version, boolean clearAllFlag) { + if(clearAllFlag) { + clearAll(); + } + } + + public Set getKeySet() { + Map allContents = new HashMap<>(_store.getAll()); + allContents.remove(KEY_VERSION); + return allContents.keySet(); + } + + @Nullable + public Map getAll() { + Map allContents = _store.getAll(); + if (allContents == null || allContents.size() == 0) { + return null; + } + allContents.remove(KEY_VERSION); + return new HashMap<>(allContents); + } + + @Override + public String getString(String key) { + return getString(key, null); + } + + @Override + public boolean getBoolean(String key) { + return getBoolean(key, false); + } + + @Override + public long getLong(String key) { + return getLong(key, 0); + } + + @Override + public int getInt(String key) { + return getInt(key, 0); + } + + @Override + public String getString(String key, String defaultValue) { + return _store.getString(key, defaultValue); + } + + @Override + public boolean getBoolean(String key, boolean defaultValue) { + return _store.getBoolean(key, defaultValue); + } + + @Override + public long getLong(String key, long defaultValue) { + return _store.getLong(key, defaultValue); + } + + @Override + public int getInt(String key, int defaultValue) { + return _store.getInt(key, defaultValue); + } + + public void putAllStrings(Map keyValuePairs) { + SharedPreferences.Editor editor = _store.edit(); + for (Map.Entry keyValuePair : keyValuePairs.entrySet()) { + putString(editor, keyValuePair.getKey(), keyValuePair.getValue(), false); + } + editor.apply(); + } + + @Override + public void putString(String key, String value) { + SharedPreferences.Editor editor = _store.edit(); + putString(editor, key, value, true); + } + + private void putString(SharedPreferences.Editor editor, String key, String value, + boolean commit) { + assertKeyNotReserved(key); + editor.putString(key, value); + if(commit) { + editor.apply(); + } + } + + @Override + public void putBoolean(String key, boolean value) { + assertKeyNotReserved(key); + SharedPreferences.Editor editor = _store.edit(); + editor.putBoolean(key, value); + editor.apply(); + } + + @Override + public void putLong(String key, long value) { + assertKeyNotReserved(key); + SharedPreferences.Editor editor = _store.edit(); + editor.putLong(key, value); + editor.apply(); + } + + @Override + public void putInt(String key, int value) { + assertKeyNotReserved(key); + putIntInternal(key, value); + } + + @Override + public boolean contains(String key) { + return _store.contains(key); + } + + @Override + public void remove(String key) { + SharedPreferences.Editor editor = _store.edit(); + editor.remove(key); + editor.apply(); + } + + @Override + public void clearAll() { + int version = getInt(KEY_VERSION); + SharedPreferences.Editor editor = _store.edit(); + editor.clear(); + editor.apply(); + putIntInternal(KEY_VERSION, version); + } + + @Override + public void clearAllWithVersion() { + SharedPreferences.Editor editor = _store.edit(); + editor.clear(); + editor.apply(); + } + + private void putIntInternal(String key, int value) { + SharedPreferences.Editor editor = _store.edit(); + editor.putInt(key, value); + editor.apply(); + } + + private void assertKeyNotReserved(String key) { + if (key.equals(KEY_VERSION)) { + throw new IllegalArgumentException(key + "is a reserved key"); + } + } + + public void registerChangeListener(SharedPreferences.OnSharedPreferenceChangeListener l) { + _store.registerOnSharedPreferenceChangeListener(l); + } + + public void unregisterChangeListener(SharedPreferences.OnSharedPreferenceChangeListener l) { + _store.unregisterOnSharedPreferenceChangeListener(l); + } + +} \ No newline at end of file diff --git a/app/src/main/java/fr/free/nrw/commons/kvstore/JsonKvStore.java b/app/src/main/java/fr/free/nrw/commons/kvstore/JsonKvStore.java new file mode 100644 index 000000000..864437bc3 --- /dev/null +++ b/app/src/main/java/fr/free/nrw/commons/kvstore/JsonKvStore.java @@ -0,0 +1,65 @@ +package fr.free.nrw.commons.kvstore; + +import android.content.Context; + +import com.google.gson.Gson; +import com.google.gson.JsonSyntaxException; + +import java.lang.reflect.Type; +import java.util.HashMap; +import java.util.Map; + +import javax.annotation.Nullable; + +public class JsonKvStore extends BasicKvStore { + private final Gson gson = new Gson(); + + public JsonKvStore(Context context, String storeName) { + super(context, storeName); + } + + public JsonKvStore(Context context, String storeName, int version) { + super(context, storeName, version); + } + + public JsonKvStore(Context context, String storeName, int version, boolean clearAllOnUpgrade) { + super(context, storeName, version, clearAllOnUpgrade); + } + + public void putAllJsons(Map jsonMap) { + Map stringsMap = new HashMap<>(jsonMap.size()); + for (Map.Entry keyValuePair : jsonMap.entrySet()) { + String jsonString = gson.toJson(keyValuePair.getValue()); + stringsMap.put(keyValuePair.getKey(), jsonString); + } + putAllStrings(stringsMap); + } + + public void putJson(String key, T object) { + putString(key, gson.toJson(object)); + } + + public void putJsonWithTypeInfo(String key, T object, Type type) { + putString(key, gson.toJson(object, type)); + } + + @Nullable + public T getJson(String key, Class clazz) { + String jsonString = getString(key); + try { + return gson.fromJson(jsonString, clazz); + } catch (JsonSyntaxException e) { + return null; + } + } + + @Nullable + public T getJson(String key, Type type) { + String jsonString = getString(key); + try { + return gson.fromJson(jsonString, type); + } catch (JsonSyntaxException e) { + return null; + } + } +} \ No newline at end of file diff --git a/app/src/main/java/fr/free/nrw/commons/kvstore/KeyValueStore.java b/app/src/main/java/fr/free/nrw/commons/kvstore/KeyValueStore.java new file mode 100644 index 000000000..46d6d8f81 --- /dev/null +++ b/app/src/main/java/fr/free/nrw/commons/kvstore/KeyValueStore.java @@ -0,0 +1,35 @@ +package fr.free.nrw.commons.kvstore; + +public interface KeyValueStore { + String getString(String key); + + boolean getBoolean(String key); + + long getLong(String key); + + int getInt(String key); + + String getString(String key, String defaultValue); + + boolean getBoolean(String key, boolean defaultValue); + + long getLong(String key, long defaultValue); + + int getInt(String key, int defaultValue); + + void putString(String key, String value); + + void putBoolean(String key, boolean value); + + void putLong(String key, long value); + + void putInt(String key, int value); + + boolean contains(String key); + + void remove(String key); + + void clearAll(); + + void clearAllWithVersion(); +} \ No newline at end of file diff --git a/app/src/main/java/fr/free/nrw/commons/location/LatLng.java b/app/src/main/java/fr/free/nrw/commons/location/LatLng.java index 81e9ec707..35233aed7 100644 --- a/app/src/main/java/fr/free/nrw/commons/location/LatLng.java +++ b/app/src/main/java/fr/free/nrw/commons/location/LatLng.java @@ -2,12 +2,14 @@ package fr.free.nrw.commons.location; import android.location.Location; import android.net.Uri; +import android.os.Parcel; +import android.os.Parcelable; import android.support.annotation.NonNull; /** * a latitude and longitude point with accuracy information, often of a picture */ -public class LatLng { +public class LatLng implements Parcelable { private final double latitude; private final double longitude; @@ -36,6 +38,12 @@ public class LatLng { this.accuracy = accuracy; } + public LatLng(Parcel in) { + latitude = in.readDouble(); + longitude = in.readDouble(); + accuracy = in.readFloat(); + } + /** * gets the latitude and longitude of a given non-null location * @param location the non-null location of the user @@ -158,5 +166,29 @@ public class LatLng { public Uri getGmmIntentUri() { return Uri.parse("geo:0,0?q=" + latitude + "," + longitude); } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeDouble(latitude); + dest.writeDouble(longitude); + dest.writeFloat(accuracy); + } + + public static final Creator CREATOR = new Creator() { + @Override + public LatLng createFromParcel(Parcel in) { + return new LatLng(in); + } + + @Override + public LatLng[] newArray(int size) { + return new LatLng[size]; + } + }; } diff --git a/app/src/main/java/fr/free/nrw/commons/media/MediaDetailPagerFragment.java b/app/src/main/java/fr/free/nrw/commons/media/MediaDetailPagerFragment.java index a3c941788..2ef99fbd6 100644 --- a/app/src/main/java/fr/free/nrw/commons/media/MediaDetailPagerFragment.java +++ b/app/src/main/java/fr/free/nrw/commons/media/MediaDetailPagerFragment.java @@ -3,7 +3,6 @@ package fr.free.nrw.commons.media; import android.annotation.SuppressLint; import android.app.DownloadManager; import android.content.Intent; -import android.content.SharedPreferences; import android.database.DataSetObserver; import android.net.Uri; import android.os.Build; @@ -40,6 +39,7 @@ import fr.free.nrw.commons.category.CategoryImagesActivity; import fr.free.nrw.commons.contributions.Contribution; import fr.free.nrw.commons.di.CommonsDaggerSupportFragment; import fr.free.nrw.commons.explore.SearchActivity; +import fr.free.nrw.commons.kvstore.BasicKvStore; import fr.free.nrw.commons.mwapi.MediaWikiApi; import fr.free.nrw.commons.utils.ImageUtils; import timber.log.Timber; @@ -52,19 +52,12 @@ import static android.widget.Toast.LENGTH_SHORT; public class MediaDetailPagerFragment extends CommonsDaggerSupportFragment implements ViewPager.OnPageChangeListener { - @Inject - MediaWikiApi mwApi; - @Inject - SessionManager sessionManager; - @Inject - @Named("default_preferences") - SharedPreferences prefs; + @Inject MediaWikiApi mwApi; + @Inject SessionManager sessionManager; + @Inject @Named("default_preferences") BasicKvStore basicKvStore; + @Inject BookmarkPicturesDao bookmarkDao; - @Inject - BookmarkPicturesDao bookmarkDao; - - @BindView(R.id.mediaDetailsPager) - ViewPager pager; + @BindView(R.id.mediaDetailsPager) ViewPager pager; private Boolean editable; private boolean isFeaturedImage; MediaDetailAdapter adapter; diff --git a/app/src/main/java/fr/free/nrw/commons/mwapi/ApacheHttpClientMediaWikiApi.java b/app/src/main/java/fr/free/nrw/commons/mwapi/ApacheHttpClientMediaWikiApi.java index bff4398f7..e1cb86960 100644 --- a/app/src/main/java/fr/free/nrw/commons/mwapi/ApacheHttpClientMediaWikiApi.java +++ b/app/src/main/java/fr/free/nrw/commons/mwapi/ApacheHttpClientMediaWikiApi.java @@ -1,18 +1,15 @@ package fr.free.nrw.commons.mwapi; import android.content.Context; -import android.content.SharedPreferences; import android.net.Uri; import android.os.Build; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.support.annotation.VisibleForTesting; import android.text.TextUtils; -import android.util.Log; import com.google.gson.Gson; -import fr.free.nrw.commons.campaigns.CampaignResponseDTO; import org.apache.http.HttpResponse; import org.apache.http.conn.ClientConnectionManager; import org.apache.http.conn.scheme.PlainSocketFactory; @@ -45,12 +42,14 @@ import java.util.concurrent.Callable; import fr.free.nrw.commons.BuildConfig; import fr.free.nrw.commons.Media; import fr.free.nrw.commons.PageTitle; -import fr.free.nrw.commons.achievements.FeaturedImages; import fr.free.nrw.commons.R; +import fr.free.nrw.commons.achievements.FeaturedImages; import fr.free.nrw.commons.achievements.FeedbackResponse; import fr.free.nrw.commons.auth.AccountUtil; +import fr.free.nrw.commons.campaigns.CampaignResponseDTO; import fr.free.nrw.commons.category.CategoryImageUtils; import fr.free.nrw.commons.category.QueryContinue; +import fr.free.nrw.commons.kvstore.BasicKvStore; import fr.free.nrw.commons.notification.Notification; import fr.free.nrw.commons.notification.NotificationUtils; import fr.free.nrw.commons.utils.ContributionUtils; @@ -64,6 +63,7 @@ import okhttp3.OkHttpClient; import okhttp3.Request; import okhttp3.Response; import timber.log.Timber; + import static fr.free.nrw.commons.utils.ContinueUtils.getQueryContinue; /** @@ -77,8 +77,8 @@ public class ApacheHttpClientMediaWikiApi implements MediaWikiApi { private CustomMwApi api; private CustomMwApi wikidataApi; private Context context; - private SharedPreferences defaultPreferences; - private SharedPreferences categoryPreferences; + private BasicKvStore defaultKvStore; + private BasicKvStore categoryKvStore; private Gson gson; private final OkHttpClient okHttpClient; private final String WIKIMEDIA_CAMPAIGNS_BASE_URL = @@ -89,8 +89,8 @@ public class ApacheHttpClientMediaWikiApi implements MediaWikiApi { public ApacheHttpClientMediaWikiApi(Context context, String apiURL, String wikidatApiURL, - SharedPreferences defaultPreferences, - SharedPreferences categoryPreferences, + BasicKvStore defaultKvStore, + BasicKvStore categoryKvStore, Gson gson, OkHttpClient okHttpClient) { this.context = context; @@ -108,8 +108,8 @@ public class ApacheHttpClientMediaWikiApi implements MediaWikiApi { } api = new CustomMwApi(apiURL, httpClient); wikidataApi = new CustomMwApi(wikidatApiURL, httpClient); - this.defaultPreferences = defaultPreferences; - this.categoryPreferences = categoryPreferences; + this.defaultKvStore = defaultKvStore; + this.categoryKvStore = categoryKvStore; this.gson = gson; } @@ -201,15 +201,13 @@ public class ApacheHttpClientMediaWikiApi implements MediaWikiApi { } private void setAuthCookieOnLogin(boolean isLoggedIn) { - SharedPreferences.Editor editor = defaultPreferences.edit(); if (isLoggedIn) { - editor.putBoolean("isUserLoggedIn", true); - editor.putString("getAuthCookie", api.getAuthCookie()); + defaultKvStore.putBoolean("isUserLoggedIn", true); + defaultKvStore.putString("getAuthCookie", api.getAuthCookie()); } else { - editor.putBoolean("isUserLoggedIn", false); - editor.remove("getAuthCookie"); + defaultKvStore.putBoolean("isUserLoggedIn", false); + defaultKvStore.remove("getAuthCookie"); } - editor.apply(); } @Override @@ -369,7 +367,7 @@ public class ApacheHttpClientMediaWikiApi implements MediaWikiApi { .get() .getNodes("/api/query/search/p/@title"); } catch (IOException e) { - Timber.e("Failed to obtain searchCategories", e); + Timber.e(e, "Failed to obtain searchCategories"); } if (categoryNodes == null) { @@ -401,7 +399,7 @@ public class ApacheHttpClientMediaWikiApi implements MediaWikiApi { .get() .getNodes("/api/query/allcategories/c"); } catch (IOException e) { - Timber.e("Failed to obtain allCategories", e); + Timber.e(e, "Failed to obtain allCategories"); } if (categoryNodes == null) { @@ -515,7 +513,7 @@ public class ApacheHttpClientMediaWikiApi implements MediaWikiApi { .get() .getNodes("/api/query/search/p/@title"); } catch (IOException e) { - Timber.e("Failed to obtain searchTitles", e); + Timber.e(e, "Failed to obtain searchTitles"); return Collections.emptyList(); } @@ -596,7 +594,7 @@ public class ApacheHttpClientMediaWikiApi implements MediaWikiApi { .get() .getNode("/api/query/notifications/list"); } catch (IOException e) { - Timber.e("Failed to obtain searchCategories", e); + Timber.e(e, "Failed to obtain searchCategories"); } if (notificationNode == null @@ -633,7 +631,7 @@ public class ApacheHttpClientMediaWikiApi implements MediaWikiApi { apiResult = requestBuilder.get(); } catch (IOException e) { - Timber.e("Failed to obtain searchCategories", e); + Timber.e(e, "Failed to obtain searchCategories"); } if (apiResult == null) { @@ -673,7 +671,7 @@ public class ApacheHttpClientMediaWikiApi implements MediaWikiApi { apiResult = requestBuilder.get(); } catch (IOException e) { - Timber.e("Failed to obtain parent Categories", e); + Timber.e(e, "Failed to obtain parent Categories"); } if (apiResult == null) { @@ -723,7 +721,7 @@ public class ApacheHttpClientMediaWikiApi implements MediaWikiApi { } apiResult = requestBuilder.get(); } catch (IOException e) { - Timber.e("Failed to obtain searchCategories", e); + Timber.e(e, "Failed to obtain searchCategories"); } if (apiResult == null) { @@ -775,7 +773,7 @@ public class ApacheHttpClientMediaWikiApi implements MediaWikiApi { imageNodes= customApiResult.getNodes("/api/query/pages/page/@title"); authorNodes= customApiResult.getNodes("/api/query/pages/page/imageinfo/ii/@user"); } catch (IOException e) { - Timber.e("Failed to obtain searchImages", e); + Timber.e(e, "Failed to obtain searchImages"); } if (imageNodes == null) { @@ -815,7 +813,7 @@ public class ApacheHttpClientMediaWikiApi implements MediaWikiApi { .get() .getNodes("/api/query/search/p/@title"); } catch (IOException e) { - Timber.e("Failed to obtain searchCategories", e); + Timber.e(e, "Failed to obtain searchCategories"); } if (categoryNodes == null) { @@ -834,14 +832,12 @@ public class ApacheHttpClientMediaWikiApi implements MediaWikiApi { /** * For APIs that return paginated responses, MediaWiki APIs uses the QueryContinue to facilitate fetching of subsequent pages * https://www.mediawiki.org/wiki/API:Raw_query_continue - * After fetching images a page of image for a particular category, shared prefs are updated with the latest QueryContinue Values + * After fetching images a page of image for a particular category, shared defaultKvStore are updated with the latest QueryContinue Values * @param keyword * @param queryContinue */ private void setQueryContinueValues(String keyword, QueryContinue queryContinue) { - SharedPreferences.Editor editor = categoryPreferences.edit(); - editor.putString(keyword, gson.toJson(queryContinue)); - editor.apply(); + categoryKvStore.putString(keyword, gson.toJson(queryContinue)); } /** @@ -851,7 +847,7 @@ public class ApacheHttpClientMediaWikiApi implements MediaWikiApi { */ @Nullable private QueryContinue getQueryContinueValues(String keyword) { - String queryContinueString = categoryPreferences.getString(keyword, null); + String queryContinueString = categoryKvStore.getString(keyword, null); return gson.fromJson(queryContinueString, QueryContinue.class); } @@ -1038,7 +1034,7 @@ public class ApacheHttpClientMediaWikiApi implements MediaWikiApi { apiResult = requestBuilder.get(); } catch (IOException e) { - Timber.e("Failed to obtain searchCategories", e); + Timber.e(e, "Failed to obtain searchCategories"); } if (apiResult == null) { diff --git a/app/src/main/java/fr/free/nrw/commons/mwapi/EventLog.java b/app/src/main/java/fr/free/nrw/commons/mwapi/EventLog.java index 4446da738..c3439a643 100644 --- a/app/src/main/java/fr/free/nrw/commons/mwapi/EventLog.java +++ b/app/src/main/java/fr/free/nrw/commons/mwapi/EventLog.java @@ -1,9 +1,9 @@ package fr.free.nrw.commons.mwapi; -import android.content.SharedPreferences; import android.os.Build; import fr.free.nrw.commons.Utils; +import fr.free.nrw.commons.kvstore.BasicKvStore; public class EventLog { static final String DEVICE; @@ -16,11 +16,11 @@ public class EventLog { } } - private static LogBuilder schema(String schema, long revision, MediaWikiApi mwApi, SharedPreferences prefs) { + private static LogBuilder schema(String schema, long revision, MediaWikiApi mwApi, BasicKvStore prefs) { return new LogBuilder(schema, revision, mwApi, prefs); } - public static LogBuilder schema(Object[] scid, MediaWikiApi mwApi, SharedPreferences prefs) { + public static LogBuilder schema(Object[] scid, MediaWikiApi mwApi, BasicKvStore prefs) { if (scid.length != 2) { throw new IllegalArgumentException("Needs an object array with schema as first param and revision as second"); } diff --git a/app/src/main/java/fr/free/nrw/commons/mwapi/LogBuilder.java b/app/src/main/java/fr/free/nrw/commons/mwapi/LogBuilder.java index 2a2456cc3..228725e71 100644 --- a/app/src/main/java/fr/free/nrw/commons/mwapi/LogBuilder.java +++ b/app/src/main/java/fr/free/nrw/commons/mwapi/LogBuilder.java @@ -1,6 +1,5 @@ package fr.free.nrw.commons.mwapi; -import android.content.SharedPreferences; import android.os.AsyncTask; import android.os.Build; @@ -12,6 +11,7 @@ import java.net.URL; import fr.free.nrw.commons.BuildConfig; import fr.free.nrw.commons.Utils; +import fr.free.nrw.commons.kvstore.BasicKvStore; import fr.free.nrw.commons.settings.Prefs; @SuppressWarnings("WeakerAccess") @@ -20,7 +20,7 @@ public class LogBuilder { private final JSONObject data; private final long rev; private final String schema; - private final SharedPreferences prefs; + private final BasicKvStore prefs; /** * Main constructor of LogBuilder @@ -30,7 +30,7 @@ public class LogBuilder { * @param mwApi Wiki media API instance * @param prefs Instance of SharedPreferences */ - LogBuilder(String schema, long revision, MediaWikiApi mwApi, SharedPreferences prefs) { + LogBuilder(String schema, long revision, MediaWikiApi mwApi, BasicKvStore prefs) { this.prefs = prefs; this.data = new JSONObject(); this.schema = schema; diff --git a/app/src/main/java/fr/free/nrw/commons/nearby/Label.java b/app/src/main/java/fr/free/nrw/commons/nearby/Label.java new file mode 100644 index 000000000..d000f242f --- /dev/null +++ b/app/src/main/java/fr/free/nrw/commons/nearby/Label.java @@ -0,0 +1,81 @@ +package fr.free.nrw.commons.nearby; + +import android.os.Parcel; +import android.support.annotation.DrawableRes; + +import java.util.HashMap; +import java.util.Map; + +import fr.free.nrw.commons.R; + +/** + * See https://github.com/commons-app/apps-android-commons/issues/250 + * Most common types of desc: building, house, cottage, farmhouse, + * village, civil parish, church, railway station, + * gatehouse, milestone, inn, secondary school, hotel + */ +public enum Label { + + BUILDING("Q41176", R.drawable.round_icon_generic_building), + HOUSE("Q3947", R.drawable.round_icon_house), + COTTAGE("Q5783996", R.drawable.round_icon_house), + FARMHOUSE("Q489357", R.drawable.round_icon_house), + CHURCH("Q16970", R.drawable.round_icon_church), //changed from church to church building + RAILWAY_STATION("Q55488", R.drawable.round_icon_railway_station), + GATEHOUSE("Q277760", R.drawable.round_icon_gatehouse), + MILESTONE("Q10145", R.drawable.round_icon_milestone), + INN("Q256020", R.drawable.round_icon_house), //Q27686 + HOTEL("Q27686", R.drawable.round_icon_house), + CITY("Q515", R.drawable.round_icon_city), + UNIVERSITY("Q3918", R.drawable.round_icon_school), //added university + SCHOOL("Q3914", R.drawable.round_icon_school), //changed from "secondary school" to school + EDUCATION("Q8434", R.drawable.round_icon_school), //changed from edu to education, there is no id for "edu" + ISLE("Q23442", R.drawable.round_icon_island), + MOUNTAIN("Q8502", R.drawable.round_icon_mountain), + AIRPORT("Q1248784", R.drawable.round_icon_airport), + BRIDGE("Q12280", R.drawable.round_icon_bridge), + ROAD("Q34442", R.drawable.round_icon_road), + FOREST("Q4421", R.drawable.round_icon_forest), + PARK("Q22698", R.drawable.round_icon_park), + RIVER("Q4022", R.drawable.round_icon_river), + WATERFALL("Q34038", R.drawable.round_icon_waterfall), + TEMPLE("Q44539", R.drawable.round_icon_church), + UNKNOWN("?", R.drawable.round_icon_unknown); + + private static final Map TEXT_TO_DESCRIPTION + = new HashMap<>(Label.values().length); + + static { + for (Label label : values()) { + TEXT_TO_DESCRIPTION.put(label.text, label); + } + } + + private final String text; + @DrawableRes + private final int icon; + + Label(String text, @DrawableRes int icon) { + this.text = text; + this.icon = icon; + } + + Label(Parcel in) { + this.text = in.readString(); + this.icon = in.readInt(); + } + + public String getText() { + return text; + } + + @DrawableRes + public int getIcon() { + return icon; + } + + public static Label fromText(String text) { + Label label = TEXT_TO_DESCRIPTION.get(text); + return label == null ? UNKNOWN : label; + } +} \ No newline at end of file diff --git a/app/src/main/java/fr/free/nrw/commons/nearby/NearbyController.java b/app/src/main/java/fr/free/nrw/commons/nearby/NearbyController.java index e5ca4854c..5fc99d774 100644 --- a/app/src/main/java/fr/free/nrw/commons/nearby/NearbyController.java +++ b/app/src/main/java/fr/free/nrw/commons/nearby/NearbyController.java @@ -1,7 +1,6 @@ package fr.free.nrw.commons.nearby; import android.content.Context; -import android.content.SharedPreferences; import android.content.res.Resources; import android.graphics.Bitmap; import android.support.graphics.drawable.VectorDrawableCompat; @@ -17,7 +16,6 @@ import java.util.Locale; import java.util.Map; import javax.inject.Inject; -import javax.inject.Named; import fr.free.nrw.commons.R; import fr.free.nrw.commons.location.LatLng; @@ -30,15 +28,12 @@ import static fr.free.nrw.commons.utils.LengthUtils.formatDistanceBetween; public class NearbyController { private static final int MAX_RESULTS = 1000; private final NearbyPlaces nearbyPlaces; - private final SharedPreferences prefs; public static double searchedRadius = 10.0; //in kilometers public static LatLng currentLocation; @Inject - public NearbyController(NearbyPlaces nearbyPlaces, - @Named("default_preferences") SharedPreferences prefs) { + public NearbyController(NearbyPlaces nearbyPlaces) { this.nearbyPlaces = nearbyPlaces; - this.prefs = prefs; } diff --git a/app/src/main/java/fr/free/nrw/commons/nearby/NearbyFragment.java b/app/src/main/java/fr/free/nrw/commons/nearby/NearbyFragment.java index 41d2105e3..c9e286d33 100644 --- a/app/src/main/java/fr/free/nrw/commons/nearby/NearbyFragment.java +++ b/app/src/main/java/fr/free/nrw/commons/nearby/NearbyFragment.java @@ -4,7 +4,6 @@ import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; -import android.content.SharedPreferences; import android.net.Uri; import android.os.Build; import android.os.Bundle; @@ -33,6 +32,7 @@ import butterknife.ButterKnife; import fr.free.nrw.commons.R; import fr.free.nrw.commons.contributions.MainActivity; import fr.free.nrw.commons.di.CommonsDaggerSupportFragment; +import fr.free.nrw.commons.kvstore.BasicKvStore; import fr.free.nrw.commons.location.LatLng; import fr.free.nrw.commons.location.LocationServiceManager; import fr.free.nrw.commons.location.LocationUpdateListener; @@ -73,7 +73,7 @@ public class NearbyFragment extends CommonsDaggerSupportFragment WikidataEditListener wikidataEditListener; @Inject @Named("application_preferences") - SharedPreferences applicationPrefs; + BasicKvStore applicationKvStore; public NearbyMapFragment nearbyMapFragment; private NearbyListFragment nearbyListFragment; diff --git a/app/src/main/java/fr/free/nrw/commons/nearby/NearbyListFragment.java b/app/src/main/java/fr/free/nrw/commons/nearby/NearbyListFragment.java index b691cebd3..f3dabb07f 100644 --- a/app/src/main/java/fr/free/nrw/commons/nearby/NearbyListFragment.java +++ b/app/src/main/java/fr/free/nrw/commons/nearby/NearbyListFragment.java @@ -2,7 +2,6 @@ package fr.free.nrw.commons.nearby; import android.content.Context; import android.content.Intent; -import android.content.SharedPreferences; import android.net.Uri; import android.os.Bundle; import android.support.v7.widget.LinearLayoutManager; @@ -29,6 +28,8 @@ import dagger.android.support.AndroidSupportInjection; import dagger.android.support.DaggerFragment; import fr.free.nrw.commons.R; import fr.free.nrw.commons.contributions.ContributionController; +import fr.free.nrw.commons.kvstore.BasicKvStore; +import fr.free.nrw.commons.kvstore.JsonKvStore; import fr.free.nrw.commons.location.LatLng; import fr.free.nrw.commons.utils.ImageUtils; import fr.free.nrw.commons.utils.UriDeserializer; @@ -51,8 +52,8 @@ public class NearbyListFragment extends DaggerFragment { private RecyclerView recyclerView; @Inject ContributionController controller; - @Inject @Named("direct_nearby_upload_prefs") SharedPreferences directPrefs; - @Inject @Named("default_preferences") SharedPreferences defaultPrefs; + @Inject @Named("direct_nearby_upload_prefs") JsonKvStore directKvStore; + @Inject @Named("default_preferences") BasicKvStore defaultKvStore; @Override public void onCreate(Bundle savedInstanceState) { diff --git a/app/src/main/java/fr/free/nrw/commons/nearby/NearbyMapFragment.java b/app/src/main/java/fr/free/nrw/commons/nearby/NearbyMapFragment.java index ca4db74a3..7e436a215 100644 --- a/app/src/main/java/fr/free/nrw/commons/nearby/NearbyMapFragment.java +++ b/app/src/main/java/fr/free/nrw/commons/nearby/NearbyMapFragment.java @@ -4,7 +4,6 @@ import android.animation.ObjectAnimator; import android.animation.TypeEvaluator; import android.animation.ValueAnimator; import android.content.Intent; -import android.content.SharedPreferences; import android.graphics.Color; import android.net.Uri; import android.os.Bundle; @@ -60,10 +59,11 @@ import fr.free.nrw.commons.Utils; import fr.free.nrw.commons.auth.LoginActivity; import fr.free.nrw.commons.bookmarks.locations.BookmarkLocationsDao; import fr.free.nrw.commons.contributions.ContributionController; +import fr.free.nrw.commons.kvstore.BasicKvStore; +import fr.free.nrw.commons.kvstore.JsonKvStore; import fr.free.nrw.commons.utils.ImageUtils; import fr.free.nrw.commons.utils.IntentUtils; import fr.free.nrw.commons.utils.LocationUtils; -import fr.free.nrw.commons.utils.PlaceUtils; import fr.free.nrw.commons.utils.UriDeserializer; import fr.free.nrw.commons.utils.ViewUtil; import timber.log.Timber; @@ -71,14 +71,10 @@ import timber.log.Timber; import static fr.free.nrw.commons.contributions.ContributionController.NEARBY_CAMERA_UPLOAD_REQUEST_CODE; import static fr.free.nrw.commons.contributions.ContributionController.NEARBY_GALLERY_UPLOAD_REQUEST_CODE; import static fr.free.nrw.commons.contributions.ContributionController.NEARBY_UPLOAD_IMAGE_LIMIT; -import static fr.free.nrw.commons.wikidata.WikidataConstants.IS_DIRECT_UPLOAD; -import static fr.free.nrw.commons.wikidata.WikidataConstants.WIKIDATA_ENTITY_ID_PREF; -import static fr.free.nrw.commons.wikidata.WikidataConstants.WIKIDATA_ITEM_LOCATION; +import static fr.free.nrw.commons.wikidata.WikidataConstants.PLACE_OBJECT; public class NearbyMapFragment extends DaggerFragment { - @Inject - @Named("application_preferences") SharedPreferences applicationPrefs; public MapView mapView; private List baseMarkerOptions; private fr.free.nrw.commons.location.LatLng curLatLng; @@ -135,11 +131,13 @@ public class NearbyMapFragment extends DaggerFragment { private Bundle bundleForUpdates;// Carry information from activity about changed nearby places and current location private boolean searchedAroundCurrentLocation = true; - @Inject ContributionController controller; - @Inject @Named("prefs") SharedPreferences prefs; - @Inject @Named("default_preferences") SharedPreferences defaultPrefs; - @Inject @Named("direct_nearby_upload_prefs") SharedPreferences directPrefs; + @Inject @Named("application_preferences") BasicKvStore applicationKvStore; + @Inject @Named("defaultKvStore") BasicKvStore prefs; + @Inject @Named("direct_nearby_upload_prefs") JsonKvStore directKvStore; + @Inject @Named("default_preferences") BasicKvStore defaultKvStore; @Inject BookmarkLocationsDao bookmarkLocationDao; + @Inject + ContributionController controller; private static final double ZOOM_LEVEL = 14f; @@ -400,7 +398,7 @@ public class NearbyMapFragment extends DaggerFragment { */ private void setListeners() { fabPlus.setOnClickListener(view -> { - if (applicationPrefs.getBoolean("login_skipped", false)) { + if (applicationKvStore.getBoolean("login_skipped", false)) { // prompt the user to login new AlertDialog.Builder(getContext()) .setMessage(R.string.login_alert_message) @@ -884,14 +882,8 @@ public class NearbyMapFragment extends DaggerFragment { } void storeSharedPrefs() { - SharedPreferences.Editor editor = directPrefs.edit(); - editor.putString("Title", place.getName()); - editor.putString("Desc", place.getLongDescription()); - editor.putString("Category", place.getCategory()); - editor.putString(WIKIDATA_ENTITY_ID_PREF, place.getWikiDataEntityId()); - editor.putString(WIKIDATA_ITEM_LOCATION, PlaceUtils.latLangToString(place.location)); - editor.putBoolean(IS_DIRECT_UPLOAD, true); - editor.apply(); + Timber.d("Store place object %s", place.toString()); + directKvStore.putJson(PLACE_OBJECT, place); } @Override diff --git a/app/src/main/java/fr/free/nrw/commons/nearby/NearbyNotificationCardView.java b/app/src/main/java/fr/free/nrw/commons/nearby/NearbyNotificationCardView.java index 19dda2033..1b43bdf35 100644 --- a/app/src/main/java/fr/free/nrw/commons/nearby/NearbyNotificationCardView.java +++ b/app/src/main/java/fr/free/nrw/commons/nearby/NearbyNotificationCardView.java @@ -79,7 +79,7 @@ public class NearbyNotificationCardView extends SwipableCardView { protected void onAttachedToWindow() { super.onAttachedToWindow(); // If you don't setVisibility after getting layout params, then you will se an empty space in place of nearby NotificationCardView - if (((MainActivity)context).prefs.getBoolean("displayNearbyCardView", true) && this.cardViewVisibilityState == NearbyNotificationCardView.CardViewVisibilityState.READY) { + if (((MainActivity)context).defaultKvStore.getBoolean("displayNearbyCardView", true) && this.cardViewVisibilityState == NearbyNotificationCardView.CardViewVisibilityState.READY) { this.setVisibility(VISIBLE); } else { this.setVisibility(GONE); @@ -94,7 +94,7 @@ public class NearbyNotificationCardView extends SwipableCardView { @Override public boolean onSwipe(View view) { view.setVisibility(GONE); // Save shared preference for nearby card view accordingly - ((MainActivity) context).prefs.edit().putBoolean("displayNearbyCardView", false).apply(); + ((MainActivity) context).defaultKvStore.putBoolean("displayNearbyCardView", false); ViewUtil.showLongToast(context, getResources().getString(R.string.nearby_notification_dismiss_message)); return true; diff --git a/app/src/main/java/fr/free/nrw/commons/nearby/NearbyPlaces.java b/app/src/main/java/fr/free/nrw/commons/nearby/NearbyPlaces.java index 925149093..f4c733708 100644 --- a/app/src/main/java/fr/free/nrw/commons/nearby/NearbyPlaces.java +++ b/app/src/main/java/fr/free/nrw/commons/nearby/NearbyPlaces.java @@ -78,7 +78,7 @@ public class NearbyPlaces { try { places = getFromWikidataQuery(curLatLng, lang, radius); } catch (InterruptedIOException e) { - Timber.d("exception in fetching nearby places", e.getLocalizedMessage()); + Timber.e(e,"exception in fetching nearby places"); return places; } Timber.d("%d results at radius: %f", places.size(), radius); @@ -165,7 +165,7 @@ public class NearbyPlaces { places.add(new Place( name, - Place.Label.fromText(identifier), // list + Label.fromText(identifier), // list type, // details Uri.parse(icon), new LatLng(latitude, longitude, 0), diff --git a/app/src/main/java/fr/free/nrw/commons/nearby/Place.java b/app/src/main/java/fr/free/nrw/commons/nearby/Place.java index d3eca0465..cccfa88c1 100644 --- a/app/src/main/java/fr/free/nrw/commons/nearby/Place.java +++ b/app/src/main/java/fr/free/nrw/commons/nearby/Place.java @@ -2,20 +2,17 @@ package fr.free.nrw.commons.nearby; import android.graphics.Bitmap; import android.net.Uri; -import android.support.annotation.DrawableRes; +import android.os.Parcel; +import android.os.Parcelable; import android.support.annotation.Nullable; -import java.util.HashMap; -import java.util.Map; - -import fr.free.nrw.commons.R; import fr.free.nrw.commons.location.LatLng; import timber.log.Timber; /** * A single geolocated Wikidata item */ -public class Place { +public class Place implements Parcelable { public final String name; private final Label label; @@ -41,6 +38,16 @@ public class Place { this.siteLinks = siteLinks; } + public Place(Parcel in) { + this.name = in.readString(); + this.label = (Label) in.readSerializable(); + this.longDescription = in.readString(); + this.secondaryImageUrl = in.readParcelable(Uri.class.getClassLoader()); + this.location = in.readParcelable(LatLng.class.getClassLoader()); + this.category = in.readString(); + this.siteLinks = in.readParcelable(Sitelinks.class.getClassLoader()); + } + /** * Gets the name of the place * @return name @@ -55,6 +62,10 @@ public class Place { return label; } + public LatLng getLocation() { + return location; + } + /** * Gets the long description of the place * @return long description @@ -86,7 +97,7 @@ public class Place { * @return returns the entity id if wikidata link exists */ @Nullable - String getWikiDataEntityId() { + public String getWikiDataEntityId() { if (!hasWikidataLink()) { Timber.d("Wikidata entity ID is null for place with sitelink %s", siteLinks.toString()); return null; @@ -157,69 +168,31 @@ public class Place { '}'; } - /** - * See https://github.com/commons-app/apps-android-commons/issues/250 - * Most common types of desc: building, house, cottage, farmhouse, - * village, civil parish, church, railway station, - * gatehouse, milestone, inn, secondary school, hotel - */ - public enum Label { - - BUILDING("Q41176", R.drawable.round_icon_generic_building), - HOUSE("Q3947", R.drawable.round_icon_house), - COTTAGE("Q5783996", R.drawable.round_icon_house), - FARMHOUSE("Q489357", R.drawable.round_icon_house), - CHURCH("Q16970", R.drawable.round_icon_church), //changed from church to church building - RAILWAY_STATION("Q55488", R.drawable.round_icon_railway_station), - GATEHOUSE("Q277760", R.drawable.round_icon_gatehouse), - MILESTONE("Q10145", R.drawable.round_icon_milestone), - INN("Q256020", R.drawable.round_icon_house), //Q27686 - HOTEL("Q27686", R.drawable.round_icon_house), - CITY("Q515", R.drawable.round_icon_city), - UNIVERSITY("Q3918",R.drawable.round_icon_school), //added university - SCHOOL("Q3914", R.drawable.round_icon_school), //changed from "secondary school" to school - EDUCATION("Q8434", R.drawable.round_icon_school), //changed from edu to education, there is no id for "edu" - ISLE("Q23442", R.drawable.round_icon_island), - MOUNTAIN("Q8502", R.drawable.round_icon_mountain), - AIRPORT("Q1248784", R.drawable.round_icon_airport), - BRIDGE("Q12280", R.drawable.round_icon_bridge), - ROAD("Q34442", R.drawable.round_icon_road), - FOREST("Q4421", R.drawable.round_icon_forest), - PARK("Q22698", R.drawable.round_icon_park), - RIVER("Q4022", R.drawable.round_icon_river), - WATERFALL("Q34038", R.drawable.round_icon_waterfall), - TEMPLE("Q44539",R.drawable.round_icon_church), - UNKNOWN("?", R.drawable.round_icon_unknown); - - private static final Map TEXT_TO_DESCRIPTION - = new HashMap<>(Label.values().length); - - static { - for (Label label : values()) { - TEXT_TO_DESCRIPTION.put(label.text, label); - } - } - - private final String text; - @DrawableRes private final int icon; - - Label(String text, @DrawableRes int icon) { - this.text = text; - this.icon = icon; - } - - public String getText() { - return text; - } - - @DrawableRes - public int getIcon() { - return icon; - } - - public static Label fromText(String text) { - Label label = TEXT_TO_DESCRIPTION.get(text); - return label == null ? UNKNOWN : label; - } + @Override + public int describeContents() { + return 0; } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeString(name); + dest.writeSerializable(label); + dest.writeString(longDescription); + dest.writeParcelable(secondaryImageUrl, 0); + dest.writeParcelable(location, 0); + dest.writeString(category); + dest.writeParcelable(siteLinks, 0); + } + + public static final Creator CREATOR = new Creator() { + @Override + public Place createFromParcel(Parcel in) { + return new Place(in); + } + + @Override + public Place[] newArray(int size) { + return new Place[size]; + } + }; } diff --git a/app/src/main/java/fr/free/nrw/commons/nearby/PlaceRenderer.java b/app/src/main/java/fr/free/nrw/commons/nearby/PlaceRenderer.java index eab11aef0..52c30013a 100644 --- a/app/src/main/java/fr/free/nrw/commons/nearby/PlaceRenderer.java +++ b/app/src/main/java/fr/free/nrw/commons/nearby/PlaceRenderer.java @@ -1,7 +1,6 @@ package fr.free.nrw.commons.nearby; import android.content.Intent; -import android.content.SharedPreferences; import android.net.Uri; import android.support.transition.TransitionManager; import android.support.v4.app.Fragment; @@ -30,6 +29,8 @@ import fr.free.nrw.commons.auth.LoginActivity; import fr.free.nrw.commons.bookmarks.locations.BookmarkLocationsDao; import fr.free.nrw.commons.contributions.ContributionController; import fr.free.nrw.commons.di.ApplicationlessInjection; +import fr.free.nrw.commons.kvstore.BasicKvStore; +import fr.free.nrw.commons.kvstore.JsonKvStore; import fr.free.nrw.commons.utils.PlaceUtils; import timber.log.Timber; @@ -38,13 +39,12 @@ import static fr.free.nrw.commons.contributions.ContributionController.NEARBY_GA import static fr.free.nrw.commons.contributions.ContributionController.NEARBY_UPLOAD_IMAGE_LIMIT; import static fr.free.nrw.commons.theme.NavigationBaseActivity.startActivityWithFlags; import static fr.free.nrw.commons.wikidata.WikidataConstants.IS_DIRECT_UPLOAD; +import static fr.free.nrw.commons.wikidata.WikidataConstants.PLACE_OBJECT; import static fr.free.nrw.commons.wikidata.WikidataConstants.WIKIDATA_ENTITY_ID_PREF; import static fr.free.nrw.commons.wikidata.WikidataConstants.WIKIDATA_ITEM_LOCATION; public class PlaceRenderer extends Renderer { - @Inject - @Named("application_preferences") SharedPreferences applicationPrefs; @BindView(R.id.tvName) TextView tvName; @BindView(R.id.tvDesc) TextView tvDesc; @BindView(R.id.distance) TextView distance; @@ -72,13 +72,11 @@ public class PlaceRenderer extends Renderer { private ContributionController controller; private OnBookmarkClick onBookmarkClick; - @Inject - BookmarkLocationsDao bookmarkLocationDao; - @Inject @Named("prefs") SharedPreferences prefs; - @Inject @Named("direct_nearby_upload_prefs") SharedPreferences directPrefs; - @Inject - @Named("default_preferences") - SharedPreferences defaultPrefs; + @Inject BookmarkLocationsDao bookmarkLocationDao; + @Inject @Named("application_preferences") BasicKvStore applicationKvStore; + @Inject @Named("defaultKvStore") BasicKvStore prefs; + @Inject @Named("direct_nearby_upload_prefs") JsonKvStore directKvStore; + @Inject @Named("default_preferences") BasicKvStore defaultKvStore; public PlaceRenderer(){ openedItems = new ArrayList<>(); @@ -132,14 +130,14 @@ public class PlaceRenderer extends Renderer { }); cameraButton.setOnClickListener(view2 -> { - if (applicationPrefs.getBoolean("login_skipped", false)) { + if (applicationKvStore.getBoolean("login_skipped", false)) { // prompt the user to login new AlertDialog.Builder(getContext()) .setMessage(R.string.login_alert_message) .setPositiveButton(R.string.login, (dialog, which) -> { startActivityWithFlags( getContext(), LoginActivity.class, Intent.FLAG_ACTIVITY_CLEAR_TOP, Intent.FLAG_ACTIVITY_SINGLE_TOP); - prefs.edit().putBoolean("login_skipped", false).apply(); + prefs.putBoolean("login_skipped", false); fragment.getActivity().finish(); }) .show(); @@ -152,14 +150,14 @@ public class PlaceRenderer extends Renderer { galleryButton.setOnClickListener(view3 -> { - if (applicationPrefs.getBoolean("login_skipped", false)) { + if (applicationKvStore.getBoolean("login_skipped", false)) { // prompt the user to login new AlertDialog.Builder(getContext()) .setMessage(R.string.login_alert_message) .setPositiveButton(R.string.login, (dialog, which) -> { startActivityWithFlags( getContext(), LoginActivity.class, Intent.FLAG_ACTIVITY_CLEAR_TOP, Intent.FLAG_ACTIVITY_SINGLE_TOP); - prefs.edit().putBoolean("login_skipped", false).apply(); + prefs.putBoolean("login_skipped", false); fragment.getActivity().finish(); }) .show(); @@ -171,14 +169,14 @@ public class PlaceRenderer extends Renderer { }); bookmarkButton.setOnClickListener(view4 -> { - if (applicationPrefs.getBoolean("login_skipped", false)) { + if (applicationKvStore.getBoolean("login_skipped", false)) { // prompt the user to login new AlertDialog.Builder(getContext()) .setMessage(R.string.login_alert_message) .setPositiveButton(R.string.login, (dialog, which) -> { startActivityWithFlags( getContext(), LoginActivity.class, Intent.FLAG_ACTIVITY_CLEAR_TOP, Intent.FLAG_ACTIVITY_SINGLE_TOP); - prefs.edit().putBoolean("login_skipped", false).apply(); + prefs.putBoolean("login_skipped", false); fragment.getActivity().finish(); }) .show(); @@ -194,15 +192,8 @@ public class PlaceRenderer extends Renderer { } private void storeSharedPrefs() { - SharedPreferences.Editor editor = directPrefs.edit(); - Timber.d("directPrefs stored"); - editor.putString("Title", place.getName()); - editor.putString("Desc", place.getLongDescription()); - editor.putString("Category", place.getCategory()); - editor.putString(WIKIDATA_ENTITY_ID_PREF, place.getWikiDataEntityId()); - editor.putString(WIKIDATA_ITEM_LOCATION, PlaceUtils.latLangToString(place.location)); - editor.putBoolean(IS_DIRECT_UPLOAD, true); - editor.apply(); + Timber.d("Store place object %s", place.toString()); + directKvStore.putJson(PLACE_OBJECT, place); } private void closeLayout(LinearLayout buttonLayout){ diff --git a/app/src/main/java/fr/free/nrw/commons/notification/NotificationActivity.java b/app/src/main/java/fr/free/nrw/commons/notification/NotificationActivity.java index 499bbd489..879e7cb99 100644 --- a/app/src/main/java/fr/free/nrw/commons/notification/NotificationActivity.java +++ b/app/src/main/java/fr/free/nrw/commons/notification/NotificationActivity.java @@ -21,12 +21,14 @@ import java.util.Date; import java.util.List; import javax.inject.Inject; +import javax.inject.Named; import butterknife.BindView; import butterknife.ButterKnife; import fr.free.nrw.commons.R; import fr.free.nrw.commons.Utils; import fr.free.nrw.commons.contributions.MainActivity; +import fr.free.nrw.commons.kvstore.BasicKvStore; import fr.free.nrw.commons.mwapi.MediaWikiApi; import fr.free.nrw.commons.theme.NavigationBaseActivity; import fr.free.nrw.commons.utils.NetworkUtils; @@ -48,8 +50,8 @@ public class NotificationActivity extends NavigationBaseActivity { @BindView(R.id.container) RelativeLayout relativeLayout; @Inject NotificationController controller; - @Inject - MediaWikiApi mediaWikiApi; + @Inject MediaWikiApi mediaWikiApi; + @Inject @Named("last_read_notification_date") BasicKvStore kvStore; private static final String TAG_NOTIFICATION_WORKER_FRAGMENT = "NotificationWorkerFragment"; private NotificationWorkerFragment mNotificationWorkerFragment; @@ -89,7 +91,7 @@ public class NotificationActivity extends NavigationBaseActivity { // Store when add notification is called last long currentDate = new Date(System.currentTimeMillis()).getTime(); - getSharedPreferences("prefs", MODE_PRIVATE).edit().putLong("last_read_notification_date", currentDate).apply(); + kvStore.putLong("last_read_notification_date", currentDate); Timber.d("Set last notification read date to current date:"+ currentDate); if(mNotificationWorkerFragment == null){ diff --git a/app/src/main/java/fr/free/nrw/commons/notification/UnreadNotificationsCheckAsync.java b/app/src/main/java/fr/free/nrw/commons/notification/UnreadNotificationsCheckAsync.java index cb504f9c5..690754b57 100644 --- a/app/src/main/java/fr/free/nrw/commons/notification/UnreadNotificationsCheckAsync.java +++ b/app/src/main/java/fr/free/nrw/commons/notification/UnreadNotificationsCheckAsync.java @@ -48,7 +48,7 @@ public class UnreadNotificationsCheckAsync extends AsyncTask { - int newRevetSharedPrefs = revertCount + revertPref.getInt(REVERT_SHARED_PREFERENCE, 0); - revertPref.edit().putInt(REVERT_SHARED_PREFERENCE, newRevetSharedPrefs).apply(); - int newUploadCount = totalUploadCount + countPref.getInt(UPLOAD_SHARED_PREFERENCE, 0); - countPref.edit().putInt(UPLOAD_SHARED_PREFERENCE, newUploadCount).apply(); + int newRevetSharedPrefs = revertCount + revertKvStore.getInt(REVERT_SHARED_PREFERENCE, 0); + revertKvStore.putInt(REVERT_SHARED_PREFERENCE, newRevetSharedPrefs); + int newUploadCount = totalUploadCount + countKvStore.getInt(UPLOAD_SHARED_PREFERENCE, 0); + countKvStore.putInt(UPLOAD_SHARED_PREFERENCE, newUploadCount); Intent i = new Intent(context, WelcomeActivity.class); i.putExtra("isQuiz", true); dialog.dismiss(); diff --git a/app/src/main/java/fr/free/nrw/commons/settings/SettingsFragment.java b/app/src/main/java/fr/free/nrw/commons/settings/SettingsFragment.java index 7b020d63e..de188b1e8 100644 --- a/app/src/main/java/fr/free/nrw/commons/settings/SettingsFragment.java +++ b/app/src/main/java/fr/free/nrw/commons/settings/SettingsFragment.java @@ -2,18 +2,12 @@ package fr.free.nrw.commons.settings; import android.Manifest; import android.app.AlertDialog; -import android.content.SharedPreferences; import android.net.Uri; import android.os.Bundle; import android.preference.EditTextPreference; -import android.preference.ListPreference; import android.preference.Preference; import android.preference.PreferenceFragment; import android.preference.SwitchPreference; -import android.support.annotation.NonNull; -import android.support.v4.content.ContextCompat; -import android.support.v4.content.FileProvider; -import android.widget.Toast; import com.karumi.dexter.Dexter; import com.karumi.dexter.listener.PermissionGrantedResponse; @@ -25,15 +19,16 @@ import javax.inject.Named; import fr.free.nrw.commons.R; import fr.free.nrw.commons.Utils; import fr.free.nrw.commons.di.ApplicationlessInjection; +import fr.free.nrw.commons.kvstore.BasicKvStore; import fr.free.nrw.commons.logging.CommonsLogSender; import fr.free.nrw.commons.utils.PermissionUtils; import fr.free.nrw.commons.utils.ViewUtil; public class SettingsFragment extends PreferenceFragment { - private static final int REQUEST_CODE_WRITE_EXTERNAL_STORAGE = 100; - - @Inject @Named("default_preferences") SharedPreferences prefs; + @Inject + @Named("default_preferences") + BasicKvStore defaultKvStore; @Inject CommonsLogSender commonsLogSender; @Override @@ -56,14 +51,14 @@ public class SettingsFragment extends PreferenceFragment { //Check if the Author Name switch is enabled and appropriately handle the author name usage SwitchPreference useAuthorName = (SwitchPreference) findPreference("useAuthorName"); EditTextPreference authorName = (EditTextPreference) findPreference("authorName"); - authorName.setEnabled(prefs.getBoolean("useAuthorName", false)); + authorName.setEnabled(defaultKvStore.getBoolean("useAuthorName", false)); useAuthorName.setOnPreferenceChangeListener((preference, newValue) -> { authorName.setEnabled((Boolean)newValue); return true; }); final EditTextPreference uploadLimit = (EditTextPreference) findPreference("uploads"); - int uploads = prefs.getInt(Prefs.UPLOADS_SHOWING, 100); + int uploads = defaultKvStore.getInt(Prefs.UPLOADS_SHOWING, 100); uploadLimit.setText(uploads + ""); uploadLimit.setSummary(uploads + ""); uploadLimit.setOnPreferenceChangeListener((preference, newValue) -> { @@ -73,7 +68,6 @@ public class SettingsFragment extends PreferenceFragment { } catch(Exception e) { value = 100; //Default number } - final SharedPreferences.Editor editor = prefs.edit(); if (value > 500) { new AlertDialog.Builder(getActivity()) .setTitle(R.string.maximum_limit) @@ -81,16 +75,15 @@ public class SettingsFragment extends PreferenceFragment { .setPositiveButton(android.R.string.yes, (dialog, which) -> {}) .setIcon(android.R.drawable.ic_dialog_alert) .show(); - editor.putInt(Prefs.UPLOADS_SHOWING, 500); - editor.putBoolean(Prefs.IS_CONTRIBUTION_COUNT_CHANGED,true); + defaultKvStore.putInt(Prefs.UPLOADS_SHOWING, 500); + defaultKvStore.putBoolean(Prefs.IS_CONTRIBUTION_COUNT_CHANGED, true); uploadLimit.setSummary(500 + ""); uploadLimit.setText(500 + ""); } else { - editor.putInt(Prefs.UPLOADS_SHOWING, value); - editor.putBoolean(Prefs.IS_CONTRIBUTION_COUNT_CHANGED,true); + defaultKvStore.putInt(Prefs.UPLOADS_SHOWING, value); + defaultKvStore.putBoolean(Prefs.IS_CONTRIBUTION_COUNT_CHANGED, true); uploadLimit.setSummary(String.valueOf(value)); } - editor.apply(); return true; }); diff --git a/app/src/main/java/fr/free/nrw/commons/theme/BaseActivity.java b/app/src/main/java/fr/free/nrw/commons/theme/BaseActivity.java index cd4d2541f..769a8afa4 100644 --- a/app/src/main/java/fr/free/nrw/commons/theme/BaseActivity.java +++ b/app/src/main/java/fr/free/nrw/commons/theme/BaseActivity.java @@ -2,24 +2,31 @@ package fr.free.nrw.commons.theme; import android.os.Bundle; +import javax.inject.Inject; +import javax.inject.Named; + import fr.free.nrw.commons.R; -import fr.free.nrw.commons.Utils; import fr.free.nrw.commons.di.CommonsDaggerAppCompatActivity; +import fr.free.nrw.commons.kvstore.BasicKvStore; public abstract class BaseActivity extends CommonsDaggerAppCompatActivity { + @Inject + @Named("default_preferences") + BasicKvStore defaultKvStore; + protected boolean wasPreviouslyDarkTheme; @Override protected void onCreate(Bundle savedInstanceState) { - wasPreviouslyDarkTheme = Utils.isDarkTheme(this); - setTheme(wasPreviouslyDarkTheme ? R.style.DarkAppTheme : R.style.LightAppTheme); super.onCreate(savedInstanceState); + wasPreviouslyDarkTheme = defaultKvStore.getBoolean("theme", false); + setTheme(wasPreviouslyDarkTheme ? R.style.DarkAppTheme : R.style.LightAppTheme); } @Override protected void onResume() { // Restart activity if theme is changed - if (wasPreviouslyDarkTheme != Utils.isDarkTheme(this)) { + if (wasPreviouslyDarkTheme != defaultKvStore.getBoolean("theme", false)) { recreate(); } diff --git a/app/src/main/java/fr/free/nrw/commons/theme/NavigationBaseActivity.java b/app/src/main/java/fr/free/nrw/commons/theme/NavigationBaseActivity.java index 06c654b0f..a2a123b18 100644 --- a/app/src/main/java/fr/free/nrw/commons/theme/NavigationBaseActivity.java +++ b/app/src/main/java/fr/free/nrw/commons/theme/NavigationBaseActivity.java @@ -6,7 +6,6 @@ import android.app.ActivityManager; import android.content.ActivityNotFoundException; import android.content.Context; import android.content.Intent; -import android.content.SharedPreferences; import android.net.Uri; import android.os.Build; import android.support.annotation.NonNull; @@ -15,7 +14,6 @@ import android.support.v4.widget.DrawerLayout; import android.support.v7.app.ActionBarDrawerToggle; import android.support.v7.app.AlertDialog; import android.support.v7.widget.Toolbar; -import android.util.Log; import android.view.Menu; import android.view.MenuItem; import android.view.View; @@ -35,9 +33,10 @@ import fr.free.nrw.commons.R; import fr.free.nrw.commons.WelcomeActivity; import fr.free.nrw.commons.achievements.AchievementsActivity; import fr.free.nrw.commons.auth.LoginActivity; -import fr.free.nrw.commons.contributions.MainActivity; -import fr.free.nrw.commons.category.CategoryImagesActivity; import fr.free.nrw.commons.bookmarks.BookmarksActivity; +import fr.free.nrw.commons.category.CategoryImagesActivity; +import fr.free.nrw.commons.contributions.MainActivity; +import fr.free.nrw.commons.kvstore.BasicKvStore; import fr.free.nrw.commons.notification.NotificationActivity; import fr.free.nrw.commons.settings.SettingsActivity; import timber.log.Timber; @@ -54,7 +53,7 @@ public abstract class NavigationBaseActivity extends BaseActivity NavigationView navigationView; @BindView(R.id.drawer_layout) DrawerLayout drawerLayout; - @Inject @Named("application_preferences") SharedPreferences prefs; + @Inject @Named("application_preferences") BasicKvStore applicationKvStore; private ActionBarDrawerToggle toggle; @@ -74,7 +73,7 @@ public abstract class NavigationBaseActivity extends BaseActivity Menu nav_Menu = navigationView.getMenu(); View headerLayout = navigationView.getHeaderView(0); ImageView userIcon = headerLayout.findViewById(R.id.user_icon); - if (prefs.getBoolean("login_skipped", false)) { + if (applicationKvStore.getBoolean("login_skipped", false)) { userIcon.setVisibility(View.GONE); nav_Menu.findItem(R.id.action_login).setVisible(true); nav_Menu.findItem(R.id.action_home).setVisible(false); @@ -164,7 +163,7 @@ public abstract class NavigationBaseActivity extends BaseActivity startActivityWithFlags( this, LoginActivity.class, Intent.FLAG_ACTIVITY_CLEAR_TOP, Intent.FLAG_ACTIVITY_SINGLE_TOP); - prefs.edit().putBoolean("login_skipped", false).apply(); + applicationKvStore.putBoolean("login_skipped", false); finish(); return true; case R.id.action_home: diff --git a/app/src/main/java/fr/free/nrw/commons/upload/FileProcessor.java b/app/src/main/java/fr/free/nrw/commons/upload/FileProcessor.java index 85205f079..fa7a49f5e 100644 --- a/app/src/main/java/fr/free/nrw/commons/upload/FileProcessor.java +++ b/app/src/main/java/fr/free/nrw/commons/upload/FileProcessor.java @@ -2,7 +2,6 @@ package fr.free.nrw.commons.upload; import android.annotation.SuppressLint; import android.content.ContentResolver; -import android.content.SharedPreferences; import android.media.ExifInterface; import android.net.Uri; import android.os.Build; @@ -19,6 +18,7 @@ import javax.inject.Named; import javax.inject.Singleton; import fr.free.nrw.commons.caching.CacheController; +import fr.free.nrw.commons.kvstore.BasicKvStore; import fr.free.nrw.commons.mwapi.CategoryApi; import io.reactivex.schedulers.Schedulers; import timber.log.Timber; @@ -37,7 +37,7 @@ public class FileProcessor implements SimilarImageDialogFragment.onResponse { CategoryApi apiCall; @Inject @Named("default_preferences") - SharedPreferences prefs; + BasicKvStore defaultKvStore; private String filePath; private ContentResolver contentResolver; private GPSExtractor imageObj; diff --git a/app/src/main/java/fr/free/nrw/commons/upload/FileUtils.java b/app/src/main/java/fr/free/nrw/commons/upload/FileUtils.java index 609f7ba92..2e6752230 100644 --- a/app/src/main/java/fr/free/nrw/commons/upload/FileUtils.java +++ b/app/src/main/java/fr/free/nrw/commons/upload/FileUtils.java @@ -4,14 +4,12 @@ import android.annotation.SuppressLint; import android.content.ContentResolver; import android.content.ContentUris; import android.content.Context; -import android.content.SharedPreferences; import android.database.Cursor; import android.media.ExifInterface; import android.net.Uri; import android.os.Build; import android.os.Environment; import android.os.ParcelFileDescriptor; -import android.preference.PreferenceManager; import android.provider.DocumentsContract; import android.provider.MediaStore; import android.provider.OpenableColumns; @@ -36,8 +34,6 @@ import java.util.Date; import timber.log.Timber; -import static com.mapbox.mapboxsdk.Mapbox.getApplicationContext; - public class FileUtils { /** @@ -153,7 +149,9 @@ public class FileUtils { // Can be safely suppressed, checks for isKitKat before running isDocumentUri @SuppressLint("NewApi") @Nullable - public static String getPath(Context context, Uri uri) { + public static String getPath(Context context, + Uri uri, + boolean useExternalStorage) { String returnPath = null; final boolean isKitKat = Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT; @@ -223,10 +221,7 @@ public class FileUtils { = context.getContentResolver().openFileDescriptor(uri, "r"); if (descriptor != null) { - SharedPreferences sharedPref = PreferenceManager - .getDefaultSharedPreferences(context); - boolean useExtStorage = sharedPref.getBoolean("useExternalStorage", true); - if (useExtStorage) { + if (useExternalStorage) { copyPath = Environment.getExternalStorageDirectory().toString() + "/CommonsApp/" + new Date().getTime() + ".jpg"; File newFile = new File(Environment.getExternalStorageDirectory().toString() + "/CommonsApp"); diff --git a/app/src/main/java/fr/free/nrw/commons/upload/UploadActivity.java b/app/src/main/java/fr/free/nrw/commons/upload/UploadActivity.java index 6f4410649..b42b8cd69 100644 --- a/app/src/main/java/fr/free/nrw/commons/upload/UploadActivity.java +++ b/app/src/main/java/fr/free/nrw/commons/upload/UploadActivity.java @@ -4,7 +4,6 @@ import android.Manifest; import android.animation.LayoutTransition; import android.annotation.SuppressLint; import android.content.Intent; -import android.content.SharedPreferences; import android.net.Uri; import android.os.Build; import android.os.Bundle; @@ -55,7 +54,9 @@ import fr.free.nrw.commons.auth.LoginActivity; import fr.free.nrw.commons.category.CategoriesModel; import fr.free.nrw.commons.category.CategoryItem; import fr.free.nrw.commons.contributions.Contribution; +import fr.free.nrw.commons.kvstore.JsonKvStore; import fr.free.nrw.commons.mwapi.MediaWikiApi; +import fr.free.nrw.commons.nearby.Place; import fr.free.nrw.commons.utils.DialogUtil; import fr.free.nrw.commons.utils.PermissionUtils; import fr.free.nrw.commons.utils.StringUtils; @@ -69,12 +70,15 @@ import timber.log.Timber; import static fr.free.nrw.commons.utils.ImageUtils.Result; import static fr.free.nrw.commons.utils.ImageUtils.getErrorMessageForResult; import static fr.free.nrw.commons.wikidata.WikidataConstants.IS_DIRECT_UPLOAD; +import static fr.free.nrw.commons.wikidata.WikidataConstants.PLACE_OBJECT; import static fr.free.nrw.commons.wikidata.WikidataConstants.WIKIDATA_ENTITY_ID_PREF; import static fr.free.nrw.commons.wikidata.WikidataConstants.WIKIDATA_ITEM_LOCATION; public class UploadActivity extends AuthenticatedActivity implements UploadView, SimilarImageInterface { @Inject MediaWikiApi mwApi; - @Inject @Named("direct_nearby_upload_prefs") SharedPreferences directPrefs; + @Inject + @Named("direct_nearby_upload_prefs") + JsonKvStore directKvStore; @Inject UploadPresenter presenter; @Inject CategoriesModel categoriesModel; @@ -373,7 +377,7 @@ public class UploadActivity extends AuthenticatedActivity implements UploadView, @Override public void showBadPicturePopup(@Result int result) { if (result >= 8 ) { // If location of image and nearby does not match, then set shared preferences to disable wikidata edits - directPrefs.edit().putBoolean("Picture_Has_Correct_Location",false); + directKvStore.putBoolean("Picture_Has_Correct_Location", false); } String errorMessageForResult = getErrorMessageForResult(this, result); if (StringUtils.isNullOrWhiteSpace(errorMessageForResult)) { @@ -639,13 +643,9 @@ public class UploadActivity extends AuthenticatedActivity implements UploadView, return; } - if (intent.getBooleanExtra("isDirectUpload", false)) { - String imageTitle = directPrefs.getString("Title", ""); - String imageDesc = directPrefs.getString("Desc", ""); - Timber.i("Received direct upload with title %s and description %s", imageTitle, imageDesc); - String wikiDataEntityIdPref = intent.getStringExtra(WIKIDATA_ENTITY_ID_PREF); - String wikiDataItemLocation = intent.getStringExtra(WIKIDATA_ITEM_LOCATION); - presenter.receiveDirect(urisList.get(0), mimeType, source, wikiDataEntityIdPref, imageTitle, imageDesc, wikiDataItemLocation); + if (intent.hasExtra(PLACE_OBJECT)) { + Place place = intent.getParcelableExtra(PLACE_OBJECT); + presenter.receiveDirect(urisList.get(0), mimeType, source, place); } else { presenter.receive(urisList, mimeType, source); } @@ -654,14 +654,7 @@ public class UploadActivity extends AuthenticatedActivity implements UploadView, } public void resetDirectPrefs() { - SharedPreferences.Editor editor = directPrefs.edit(); - editor.remove("Title"); - editor.remove("Desc"); - editor.remove("Category"); - editor.remove(WIKIDATA_ENTITY_ID_PREF); - editor.remove(WIKIDATA_ITEM_LOCATION); - editor.remove(IS_DIRECT_UPLOAD); - editor.apply(); + directKvStore.remove(PLACE_OBJECT); } /** diff --git a/app/src/main/java/fr/free/nrw/commons/upload/UploadController.java b/app/src/main/java/fr/free/nrw/commons/upload/UploadController.java index 6b63eb0c9..cbe2c4661 100644 --- a/app/src/main/java/fr/free/nrw/commons/upload/UploadController.java +++ b/app/src/main/java/fr/free/nrw/commons/upload/UploadController.java @@ -7,13 +7,11 @@ import android.content.ContentResolver; import android.content.Context; import android.content.Intent; import android.content.ServiceConnection; -import android.content.SharedPreferences; import android.content.res.AssetFileDescriptor; import android.database.Cursor; import android.net.Uri; import android.os.AsyncTask; import android.os.IBinder; -import android.preference.PreferenceManager; import android.provider.MediaStore; import android.text.TextUtils; @@ -24,31 +22,37 @@ import java.io.InputStream; import java.util.Date; import java.util.concurrent.Executors; +import javax.inject.Inject; +import javax.inject.Singleton; + import fr.free.nrw.commons.HandlerService; import fr.free.nrw.commons.R; import fr.free.nrw.commons.auth.SessionManager; import fr.free.nrw.commons.contributions.Contribution; +import fr.free.nrw.commons.kvstore.BasicKvStore; import fr.free.nrw.commons.settings.Prefs; import fr.free.nrw.commons.utils.ViewUtil; import timber.log.Timber; +@Singleton public class UploadController { private UploadService uploadService; private SessionManager sessionManager; private Context context; - private SharedPreferences prefs; + private BasicKvStore defaultKvStore; public interface ContributionUploadProgress { void onUploadStarted(Contribution contribution); } - /** - * Constructs a new UploadController. - */ - public UploadController(SessionManager sessionManager, Context context, SharedPreferences sharedPreferences) { + + @Inject + public UploadController(SessionManager sessionManager, + Context context, + BasicKvStore store) { this.sessionManager = sessionManager; this.context = context; - this.prefs = sharedPreferences; + this.defaultKvStore = store; } private boolean isUploadServiceConnected; @@ -106,13 +110,11 @@ public class UploadController { //Set creator, desc, and license // If author name is enabled and set, use it - SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context); - - if(preferences != null && preferences.getBoolean("useAuthorName", false)) { - String authorName = preferences.getString("authorName", ""); + if (defaultKvStore.getBoolean("useAuthorName", false)) { + String authorName = defaultKvStore.getString("authorName", ""); contribution.setCreator(authorName); } - + if (TextUtils.isEmpty(contribution.getCreator())) { Account currentAccount = sessionManager.getCurrentAccount(); if (currentAccount == null) { @@ -128,7 +130,7 @@ public class UploadController { contribution.setDescription(""); } - String license = prefs.getString(Prefs.DEFAULT_LICENSE, Prefs.Licenses.CC_BY_SA_3); + String license = defaultKvStore.getString(Prefs.DEFAULT_LICENSE, Prefs.Licenses.CC_BY_SA_3); contribution.setLicense(license); //FIXME: Add permission request here. Only executeAsyncTask if permission has been granted diff --git a/app/src/main/java/fr/free/nrw/commons/upload/UploadModel.java b/app/src/main/java/fr/free/nrw/commons/upload/UploadModel.java index 95666f39a..aff7429f5 100644 --- a/app/src/main/java/fr/free/nrw/commons/upload/UploadModel.java +++ b/app/src/main/java/fr/free/nrw/commons/upload/UploadModel.java @@ -3,14 +3,11 @@ package fr.free.nrw.commons.upload; import android.annotation.SuppressLint; import android.content.ContentResolver; import android.content.Context; -import android.content.SharedPreferences; import android.database.Cursor; -import android.graphics.BitmapRegionDecoder; import android.net.Uri; import android.support.annotation.Nullable; import java.io.File; -import java.io.IOException; import java.util.ArrayList; import java.util.Date; import java.util.List; @@ -22,7 +19,10 @@ import javax.inject.Named; import fr.free.nrw.commons.CommonsApplication; import fr.free.nrw.commons.auth.SessionManager; import fr.free.nrw.commons.contributions.Contribution; +import fr.free.nrw.commons.kvstore.BasicKvStore; +import fr.free.nrw.commons.location.LatLng; import fr.free.nrw.commons.mwapi.MediaWikiApi; +import fr.free.nrw.commons.nearby.Place; import fr.free.nrw.commons.settings.Prefs; import fr.free.nrw.commons.utils.BitmapRegionDecoderWrapper; import fr.free.nrw.commons.utils.ImageUtils; @@ -36,6 +36,8 @@ import io.reactivex.schedulers.Schedulers; import io.reactivex.subjects.BehaviorSubject; import timber.log.Timber; +import static fr.free.nrw.commons.utils.ImageUtils.IMAGE_OK; + public class UploadModel { private MediaWikiApi mwApi; @@ -48,7 +50,7 @@ public class UploadModel { null, -1L) { }; - private final SharedPreferences prefs; + private final BasicKvStore basicKvStore; private final List licenses; private String license; private final Map licensesByName; @@ -71,7 +73,7 @@ public class UploadModel { @Inject UploadModel(@Named("licenses") List licenses, - @Named("default_preferences") SharedPreferences prefs, + @Named("default_preferences") BasicKvStore basicKvStore, @Named("licenses_by_name") Map licensesByName, Context context, MediaWikiApi mwApi, @@ -81,8 +83,8 @@ public class UploadModel { BitmapRegionDecoderWrapper bitmapRegionDecoderWrapper, FileProcessor fileProcessor) { this.licenses = licenses; - this.prefs = prefs; - this.license = prefs.getString(Prefs.DEFAULT_LICENSE, Prefs.Licenses.CC_BY_SA_3); + this.basicKvStore = basicKvStore; + this.license = basicKvStore.getString(Prefs.DEFAULT_LICENSE, Prefs.Licenses.CC_BY_SA_3); this.bitmapRegionDecoderWrapper = bitmapRegionDecoderWrapper; this.licensesByName = licensesByName; this.context = context; @@ -92,12 +94,16 @@ public class UploadModel { this.fileUtilsWrapper = fileUtilsWrapper; this.fileProcessor = fileProcessor; this.imageUtilsWrapper = imageUtilsWrapper; - useExtStorage = this.prefs.getBoolean("useExternalStorage", false); + useExtStorage = this.basicKvStore.getBoolean("useExternalStorage", false); } @SuppressLint("CheckResult") - void receive(List mediaUri, String mimeType, String source, SimilarImageInterface similarImageInterface) { + void receive(List mediaUri, + String mimeType, + String source, + SimilarImageInterface similarImageInterface) { initDefaultValues(); + Observable itemObservable = Observable.fromIterable(mediaUri) .map(media -> { currentMediaUri = media; @@ -109,17 +115,7 @@ public class UploadModel { fileProcessor.initFileDetails(filePath, context.getContentResolver()); UploadItem item = new UploadItem(uri, mimeType, source, fileProcessor.processFileCoordinates(similarImageInterface), fileUtilsWrapper.getFileExt(filePath), null, fileCreatedDate); - Single.zip( - Single.fromCallable(() -> - fileUtilsWrapper.getFileInputStream(filePath)) - .map(fileUtilsWrapper::getSHA1) - .map(mwApi::existingFile) - .map(b -> b ? ImageUtils.IMAGE_DUPLICATE : ImageUtils.IMAGE_OK), - Single.fromCallable(() -> - fileUtilsWrapper.getFileInputStream(filePath)) - .map(file -> bitmapRegionDecoderWrapper.newInstance(file, false)) - .map(imageUtilsWrapper::checkIfImageIsTooDark), //Returns IMAGE_DARK or IMAGE_OK - (dupe, dark) -> dupe | dark) + checkImageQuality(null, null, filePath) .observeOn(Schedulers.io()) .subscribe(item.imageQuality::onNext, Timber::e); return item; @@ -130,38 +126,59 @@ public class UploadModel { } @SuppressLint("CheckResult") - void receiveDirect(Uri media, String mimeType, String source, String wikidataEntityIdPref, String title, String desc, SimilarImageInterface similarImageInterface, String wikidataItemLocation) { + void receiveDirect(Uri media, String mimeType, String source, Place place, SimilarImageInterface similarImageInterface) { initDefaultValues(); long fileCreatedDate = getFileCreatedDate(media); String filePath = this.cacheFileUpload(media); Uri uri = Uri.fromFile(new File(filePath)); fileProcessor.initFileDetails(filePath, context.getContentResolver()); UploadItem item = new UploadItem(uri, mimeType, source, fileProcessor.processFileCoordinates(similarImageInterface), - fileUtilsWrapper.getFileExt(filePath), wikidataEntityIdPref, fileCreatedDate); - item.title.setTitleText(title); - item.descriptions.get(0).setDescriptionText(desc); + fileUtilsWrapper.getFileExt(filePath), place.getWikiDataEntityId(), fileCreatedDate); + item.title.setTitleText(place.getName()); + item.descriptions.get(0).setDescriptionText(place.getLongDescription()); //TODO figure out if default descriptions in other languages exist item.descriptions.get(0).setLanguageCode("en"); - Single.zip( - Single.fromCallable(() -> - fileUtilsWrapper.getFileInputStream(filePath)) - .map(fileUtilsWrapper::getSHA1) - .map(mwApi::existingFile) - .map(b -> b ? ImageUtils.IMAGE_DUPLICATE : ImageUtils.IMAGE_OK), - Single.fromCallable(() -> filePath) - .map(fileUtilsWrapper::getGeolocationOfFile) - .map(geoLocation -> imageUtilsWrapper.checkImageGeolocationIsDifferent(geoLocation, wikidataItemLocation)) - .map(r -> r ? ImageUtils.IMAGE_GEOLOCATION_DIFFERENT : ImageUtils.IMAGE_OK), - Single.fromCallable(() -> - fileUtilsWrapper.getFileInputStream(filePath)) - .map(file -> bitmapRegionDecoderWrapper.newInstance(file, false)) - .map(imageUtilsWrapper::checkIfImageIsTooDark), //Returns IMAGE_DARK or IMAGE_OK - (dupe, wrongGeo, dark) -> dupe | wrongGeo | dark).subscribe(item.imageQuality::onNext); + checkImageQuality(place.getWikiDataEntityId(), place.getLocation(), filePath) + .observeOn(Schedulers.io()) + .subscribe(item.imageQuality::onNext, Timber::e); items.add(item); items.get(0).selected = true; items.get(0).first = true; } + private Single checkImageQuality(String wikiDataEntityId, LatLng latLng, String filePath) { + return Single.zip( + checkDuplicateFile(filePath), + checkImageCoordinates(wikiDataEntityId, latLng, filePath), + checkDarkImage(filePath), //Returns IMAGE_DARK or IMAGE_OK + (dupe, wrongGeo, dark) -> dupe | wrongGeo | dark); + } + + private Single checkDarkImage(String filePath) { + return Single.fromCallable(() -> + fileUtilsWrapper.getFileInputStream(filePath)) + .map(file -> bitmapRegionDecoderWrapper.newInstance(file, false)) + .map(imageUtilsWrapper::checkIfImageIsTooDark); + } + + private Single checkImageCoordinates(String wikiDataEntityId, LatLng latLng, String filePath) { + if (StringUtils.isNullOrWhiteSpace(wikiDataEntityId)) { + return Single.just(IMAGE_OK); + } + return Single.fromCallable(() -> filePath) + .map(fileUtilsWrapper::getGeolocationOfFile) + .map(geoLocation -> imageUtilsWrapper.checkImageGeolocationIsDifferent(geoLocation, latLng)) + .map(r -> r ? ImageUtils.IMAGE_GEOLOCATION_DIFFERENT : IMAGE_OK); + } + + private Single checkDuplicateFile(String filePath) { + return Single.fromCallable(() -> + fileUtilsWrapper.getFileInputStream(filePath)) + .map(fileUtilsWrapper::getSHA1) + .map(mwApi::existingFile) + .map(b -> b ? ImageUtils.IMAGE_DUPLICATE : IMAGE_OK); + } + private void initDefaultValues() { currentStepIndex = 0; topCardState = true; @@ -332,7 +349,7 @@ public class UploadModel { void setSelectedLicense(String licenseName) { this.license = licensesByName.get(licenseName); - prefs.edit().putString(Prefs.DEFAULT_LICENSE, license).commit(); + basicKvStore.putString(Prefs.DEFAULT_LICENSE, license); } Observable buildContributions(List categoryStringList) { diff --git a/app/src/main/java/fr/free/nrw/commons/upload/UploadPresenter.java b/app/src/main/java/fr/free/nrw/commons/upload/UploadPresenter.java index 691606d84..7b03e1628 100644 --- a/app/src/main/java/fr/free/nrw/commons/upload/UploadPresenter.java +++ b/app/src/main/java/fr/free/nrw/commons/upload/UploadPresenter.java @@ -1,9 +1,7 @@ package fr.free.nrw.commons.upload; import android.annotation.SuppressLint; -import android.content.SharedPreferences; import android.net.Uri; -import android.util.Log; import java.lang.reflect.Proxy; import java.util.ArrayList; @@ -17,7 +15,9 @@ import javax.inject.Singleton; import fr.free.nrw.commons.R; import fr.free.nrw.commons.category.CategoriesModel; import fr.free.nrw.commons.contributions.Contribution; +import fr.free.nrw.commons.kvstore.BasicKvStore; import fr.free.nrw.commons.mwapi.MediaWikiApi; +import fr.free.nrw.commons.nearby.Place; import fr.free.nrw.commons.settings.Prefs; import fr.free.nrw.commons.utils.ImageUtils; import io.reactivex.Completable; @@ -53,7 +53,7 @@ public class UploadPresenter { @UploadView.UploadPage private int currentPage = UploadView.PLEASE_WAIT; - @Inject @Named("default_preferences")SharedPreferences prefs; + @Inject @Named("default_preferences") BasicKvStore defaultKvStore; @Inject UploadPresenter(UploadModel uploadModel, @@ -99,10 +99,8 @@ public class UploadPresenter { @SuppressLint("CheckResult") void receiveDirect(Uri media, String mimeType, @Contribution.FileSource String source, - String wikidataEntityIdPref, - String title, String desc, - String wikidataItemLocation) { - Completable.fromRunnable(() -> uploadModel.receiveDirect(media, mimeType, source, wikidataEntityIdPref, title, desc, similarImageInterface, wikidataItemLocation)) + Place place) { + Completable.fromRunnable(() -> uploadModel.receiveDirect(media, mimeType, source, place, similarImageInterface)) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(() -> { @@ -363,7 +361,7 @@ public class UploadPresenter { * Sets the list of licences and the default license. */ private void updateLicenses() { - String selectedLicense = prefs.getString(Prefs.DEFAULT_LICENSE, Prefs.Licenses.CC_BY_SA_3); + String selectedLicense = defaultKvStore.getString(Prefs.DEFAULT_LICENSE, Prefs.Licenses.CC_BY_SA_3); view.updateLicenses(uploadModel.getLicenses(), selectedLicense); view.updateLicenseSummary(selectedLicense, uploadModel.getCount()); } diff --git a/app/src/main/java/fr/free/nrw/commons/utils/ImageUtils.java b/app/src/main/java/fr/free/nrw/commons/utils/ImageUtils.java index 1ab562708..e9b42de78 100644 --- a/app/src/main/java/fr/free/nrw/commons/utils/ImageUtils.java +++ b/app/src/main/java/fr/free/nrw/commons/utils/ImageUtils.java @@ -100,22 +100,20 @@ public class ImageUtils { /** * @param geolocationOfFileString Geolocation of image. If geotag doesn't exists, then this will be an empty string - * @param wikidataItemLocationString Location of wikidata item will be edited after upload + * @param latLng Location of wikidata item will be edited after upload * @return false if image is neither dark nor blurry or if the input bitmapRegionDecoder provided is null * true if geolocation of the image and wikidata item are different */ - static boolean checkImageGeolocationIsDifferent(String geolocationOfFileString, String wikidataItemLocationString) { + static boolean checkImageGeolocationIsDifferent(String geolocationOfFileString, LatLng latLng) { Timber.d("Comparing geolocation of file with nearby place location"); - if (geolocationOfFileString == null || geolocationOfFileString == "") { // Means that geolocation for this image is not given + if (latLng == null) { // Means that geolocation for this image is not given return false; // Since we don't know geolocation of file, we choose letting upload } String[] geolocationOfFile = geolocationOfFileString.split("\\|"); - String[] wikidataItemLocation = wikidataItemLocationString.split("/"); - Double distance = LengthUtils.computeDistanceBetween( new LatLng(Double.parseDouble(geolocationOfFile[0]),Double.parseDouble(geolocationOfFile[1]),0) - , new LatLng(Double.parseDouble(wikidataItemLocation[0]), Double.parseDouble(wikidataItemLocation[1]),0)); + , latLng); // Distance is more than 1 km, means that geolocation is wrong return distance >= 1000; } diff --git a/app/src/main/java/fr/free/nrw/commons/utils/ImageUtilsWrapper.java b/app/src/main/java/fr/free/nrw/commons/utils/ImageUtilsWrapper.java index d5b905e9d..df8a9cbb1 100644 --- a/app/src/main/java/fr/free/nrw/commons/utils/ImageUtilsWrapper.java +++ b/app/src/main/java/fr/free/nrw/commons/utils/ImageUtilsWrapper.java @@ -5,6 +5,8 @@ import android.graphics.BitmapRegionDecoder; import javax.inject.Inject; import javax.inject.Singleton; +import fr.free.nrw.commons.location.LatLng; + import static fr.free.nrw.commons.utils.ImageUtils.*; @Singleton @@ -19,7 +21,7 @@ public class ImageUtilsWrapper { return ImageUtils.checkIfImageIsTooDark(bitmapRegionDecoder); } - public boolean checkImageGeolocationIsDifferent(String geolocationOfFileString, String wikidataItemLocationString) { - return ImageUtils.checkImageGeolocationIsDifferent(geolocationOfFileString, wikidataItemLocationString); + public boolean checkImageGeolocationIsDifferent(String geolocationOfFileString, LatLng latLng) { + return ImageUtils.checkImageGeolocationIsDifferent(geolocationOfFileString, latLng); } } diff --git a/app/src/main/java/fr/free/nrw/commons/wikidata/WikidataConstants.java b/app/src/main/java/fr/free/nrw/commons/wikidata/WikidataConstants.java index 5ecad6bb2..e198ba959 100644 --- a/app/src/main/java/fr/free/nrw/commons/wikidata/WikidataConstants.java +++ b/app/src/main/java/fr/free/nrw/commons/wikidata/WikidataConstants.java @@ -4,4 +4,5 @@ public class WikidataConstants { public static final String WIKIDATA_ENTITY_ID_PREF = "WikiDataEntityId"; public static final String WIKIDATA_ITEM_LOCATION = "WikiDataItemLocation"; public static final String IS_DIRECT_UPLOAD = "isDirectUpload"; + public static final String PLACE_OBJECT = "place"; } diff --git a/app/src/main/java/fr/free/nrw/commons/wikidata/WikidataEditService.java b/app/src/main/java/fr/free/nrw/commons/wikidata/WikidataEditService.java index 3d52cf85c..eaa779744 100644 --- a/app/src/main/java/fr/free/nrw/commons/wikidata/WikidataEditService.java +++ b/app/src/main/java/fr/free/nrw/commons/wikidata/WikidataEditService.java @@ -2,7 +2,6 @@ package fr.free.nrw.commons.wikidata; import android.annotation.SuppressLint; import android.content.Context; -import android.content.SharedPreferences; import java.util.Locale; @@ -11,6 +10,8 @@ import javax.inject.Named; import javax.inject.Singleton; import fr.free.nrw.commons.R; +import fr.free.nrw.commons.kvstore.BasicKvStore; +import fr.free.nrw.commons.kvstore.JsonKvStore; import fr.free.nrw.commons.mwapi.MediaWikiApi; import fr.free.nrw.commons.utils.ViewUtil; import io.reactivex.Observable; @@ -29,17 +30,17 @@ public class WikidataEditService { private final Context context; private final MediaWikiApi mediaWikiApi; private final WikidataEditListener wikidataEditListener; - private final SharedPreferences directPrefs; + private final BasicKvStore directKvStore; @Inject public WikidataEditService(Context context, MediaWikiApi mediaWikiApi, WikidataEditListener wikidataEditListener, - @Named("direct_nearby_upload_prefs") SharedPreferences directPrefs) { + @Named("direct_nearby_upload_prefs") JsonKvStore directKvStore) { this.context = context; this.mediaWikiApi = mediaWikiApi; this.wikidataEditListener = wikidataEditListener; - this.directPrefs = directPrefs; + this.directKvStore = directKvStore; } /** @@ -58,7 +59,7 @@ public class WikidataEditService { return; } - if (!(directPrefs.getBoolean("Picture_Has_Correct_Location",true))) { + if (!(directKvStore.getBoolean("Picture_Has_Correct_Location", true))) { Timber.d("Image location and nearby place location mismatched, so Wikidata item won't be edited"); return; } @@ -122,7 +123,7 @@ public class WikidataEditService { * Show a success toast when the edit is made successfully */ private void showSuccessToast() { - String title = directPrefs.getString("Title", ""); + String title = directKvStore.getString("Title", ""); String successStringTemplate = context.getString(R.string.successful_wikidata_edit); String successMessage = String.format(Locale.getDefault(), successStringTemplate, title); ViewUtil.showLongToast(context, successMessage); diff --git a/app/src/test/kotlin/fr/free/nrw/commons/TestCommonsApplication.kt b/app/src/test/kotlin/fr/free/nrw/commons/TestCommonsApplication.kt index 0af023cae..dbdbb67a4 100644 --- a/app/src/test/kotlin/fr/free/nrw/commons/TestCommonsApplication.kt +++ b/app/src/test/kotlin/fr/free/nrw/commons/TestCommonsApplication.kt @@ -2,7 +2,6 @@ package fr.free.nrw.commons import android.content.ContentProviderClient import android.content.Context -import android.content.SharedPreferences import android.support.v4.util.LruCache import com.google.gson.Gson import com.nhaarman.mockito_kotlin.mock @@ -13,6 +12,8 @@ import fr.free.nrw.commons.data.DBOpenHelper import fr.free.nrw.commons.di.CommonsApplicationComponent import fr.free.nrw.commons.di.CommonsApplicationModule import fr.free.nrw.commons.di.DaggerCommonsApplicationComponent +import fr.free.nrw.commons.kvstore.BasicKvStore +import fr.free.nrw.commons.kvstore.JsonKvStore import fr.free.nrw.commons.location.LocationServiceManager import fr.free.nrw.commons.mwapi.MediaWikiApi import fr.free.nrw.commons.nearby.NearbyPlaces @@ -37,9 +38,9 @@ class TestCommonsApplication : CommonsApplication() { @Suppress("MemberVisibilityCanBePrivate") class MockCommonsApplicationModule(appContext: Context) : CommonsApplicationModule(appContext) { val accountUtil: AccountUtil = mock() - val appSharedPreferences: SharedPreferences = mock() - val defaultSharedPreferences: SharedPreferences = mock() - val otherSharedPreferences: SharedPreferences = mock() + val appSharedPreferences: BasicKvStore = mock() + val defaultSharedPreferences: BasicKvStore = mock() + val otherSharedPreferences: BasicKvStore = mock() val uploadController: UploadController = mock() val mockSessionManager: SessionManager = mock() val locationServiceManager: LocationServiceManager = mock() @@ -50,7 +51,7 @@ class MockCommonsApplicationModule(appContext: Context) : CommonsApplicationModu val categoryClient: ContentProviderClient = mock() val contributionClient: ContentProviderClient = mock() val modificationClient: ContentProviderClient = mock() - val uploadPrefs: SharedPreferences = mock() + val uploadPrefs: JsonKvStore = mock() override fun provideCategoryContentProviderClient(context: Context?): ContentProviderClient = categoryClient @@ -58,19 +59,19 @@ class MockCommonsApplicationModule(appContext: Context) : CommonsApplicationModu override fun provideModificationContentProviderClient(context: Context?): ContentProviderClient = modificationClient - override fun providesDirectNearbyUploadPreferences(context: Context?): SharedPreferences = uploadPrefs + override fun providesDirectNearbyUploadKvStore(context: Context?): JsonKvStore = uploadPrefs override fun providesAccountUtil(context: Context): AccountUtil = accountUtil - override fun providesApplicationSharedPreferences(context: Context): SharedPreferences = appSharedPreferences + override fun providesApplicationKvStore(context: Context): BasicKvStore = appSharedPreferences - override fun providesDefaultSharedPreferences(context: Context): SharedPreferences = defaultSharedPreferences + override fun providesDefaultKvStore(context: Context): BasicKvStore = defaultSharedPreferences - override fun providesOtherSharedPreferences(context: Context): SharedPreferences = otherSharedPreferences + override fun providesOtherKvStore(context: Context): BasicKvStore = otherSharedPreferences - override fun providesUploadController(sessionManager: SessionManager, sharedPreferences: SharedPreferences, context: Context): UploadController = uploadController + override fun providesUploadController(sessionManager: SessionManager, sharedPreferences: BasicKvStore, context: Context): UploadController = uploadController - override fun providesSessionManager(context: Context, mediaWikiApi: MediaWikiApi, sharedPreferences: SharedPreferences): SessionManager = mockSessionManager + override fun providesSessionManager(context: Context, mediaWikiApi: MediaWikiApi, sharedPreferences: BasicKvStore): SessionManager = mockSessionManager override fun provideLocationServiceManager(context: Context): LocationServiceManager = locationServiceManager diff --git a/app/src/test/kotlin/fr/free/nrw/commons/bookmarks/locations/BookMarkLocationDaoTest.kt b/app/src/test/kotlin/fr/free/nrw/commons/bookmarks/locations/BookMarkLocationDaoTest.kt index 80cbc09d7..a60eaa4c9 100644 --- a/app/src/test/kotlin/fr/free/nrw/commons/bookmarks/locations/BookMarkLocationDaoTest.kt +++ b/app/src/test/kotlin/fr/free/nrw/commons/bookmarks/locations/BookMarkLocationDaoTest.kt @@ -13,6 +13,7 @@ import fr.free.nrw.commons.TestCommonsApplication import fr.free.nrw.commons.bookmarks.locations.BookmarkLocationsContentProvider.BASE_URI import fr.free.nrw.commons.bookmarks.locations.BookmarkLocationsDao.Table.* import fr.free.nrw.commons.location.LatLng +import fr.free.nrw.commons.nearby.Label import fr.free.nrw.commons.nearby.Place import fr.free.nrw.commons.nearby.Sitelinks import junit.framework.Assert.* @@ -42,14 +43,14 @@ class BookMarkLocationDaoTest { private lateinit var testObject: BookmarkLocationsDao private lateinit var examplePlaceBookmark: Place - private lateinit var exampleLabel: Place.Label + private lateinit var exampleLabel: Label private lateinit var exampleUri: Uri private lateinit var exampleLocation: LatLng private lateinit var builder: Sitelinks.Builder @Before fun setUp() { - exampleLabel = Place.Label.FOREST + exampleLabel = Label.FOREST exampleUri = Uri.parse("wikimedia/uri") exampleLocation = LatLng(40.0,51.4, 1f) @@ -85,7 +86,7 @@ class BookMarkLocationDaoTest { cursor.moveToFirst() testObject.fromCursor(cursor).let { assertEquals("placeName", it.name) - assertEquals(Place.Label.FOREST, it.label) + assertEquals(Label.FOREST, it.label) assertEquals("placeDescription", it.longDescription) assertEquals(exampleUri, it.secondaryImageUrl) assertEquals(40.0, it.location.latitude) diff --git a/app/src/test/kotlin/fr/free/nrw/commons/mwapi/ApacheHttpClientMediaWikiApiTest.kt b/app/src/test/kotlin/fr/free/nrw/commons/mwapi/ApacheHttpClientMediaWikiApiTest.kt index 7da8765b4..89e6e68c6 100644 --- a/app/src/test/kotlin/fr/free/nrw/commons/mwapi/ApacheHttpClientMediaWikiApiTest.kt +++ b/app/src/test/kotlin/fr/free/nrw/commons/mwapi/ApacheHttpClientMediaWikiApiTest.kt @@ -1,12 +1,10 @@ package fr.free.nrw.commons.mwapi -import android.content.SharedPreferences -import android.net.Uri import android.os.Build -import android.preference.PreferenceManager import com.google.gson.Gson import fr.free.nrw.commons.BuildConfig import fr.free.nrw.commons.TestCommonsApplication +import fr.free.nrw.commons.kvstore.BasicKvStore import okhttp3.OkHttpClient import okhttp3.mockwebserver.MockResponse import okhttp3.mockwebserver.MockWebServer @@ -20,8 +18,6 @@ import org.mockito.Mockito.mock import org.robolectric.RobolectricTestRunner import org.robolectric.RuntimeEnvironment import org.robolectric.annotation.Config -import timber.log.Timber -import java.io.InputStream import java.net.URLDecoder import java.text.SimpleDateFormat import java.util.* @@ -33,8 +29,8 @@ class ApacheHttpClientMediaWikiApiTest { private lateinit var testObject: ApacheHttpClientMediaWikiApi private lateinit var server: MockWebServer private lateinit var wikidataServer: MockWebServer - private lateinit var sharedPreferences: SharedPreferences - private lateinit var categoryPreferences: SharedPreferences + private lateinit var sharedPreferences: BasicKvStore + private lateinit var categoryPreferences: BasicKvStore private lateinit var okHttpClient: OkHttpClient @Before @@ -42,8 +38,8 @@ class ApacheHttpClientMediaWikiApiTest { server = MockWebServer() wikidataServer = MockWebServer() okHttpClient = OkHttpClient() - sharedPreferences = PreferenceManager.getDefaultSharedPreferences(RuntimeEnvironment.application) - categoryPreferences = PreferenceManager.getDefaultSharedPreferences(RuntimeEnvironment.application) + sharedPreferences = mock(BasicKvStore::class.java) + categoryPreferences = mock(BasicKvStore::class.java) testObject = ApacheHttpClientMediaWikiApi(RuntimeEnvironment.application, "http://" + server.hostName + ":" + server.port + "/", "http://" + wikidataServer.hostName + ":" + wikidataServer.port + "/", sharedPreferences, categoryPreferences, Gson(), okHttpClient) testObject.setWikiMediaToolforgeUrl("http://" + server.hostName + ":" + server.port + "/") } diff --git a/app/src/test/kotlin/fr/free/nrw/commons/upload/UploadControllerTest.kt b/app/src/test/kotlin/fr/free/nrw/commons/upload/UploadControllerTest.kt index a62f857be..4c624ec4f 100644 --- a/app/src/test/kotlin/fr/free/nrw/commons/upload/UploadControllerTest.kt +++ b/app/src/test/kotlin/fr/free/nrw/commons/upload/UploadControllerTest.kt @@ -6,6 +6,7 @@ import android.content.SharedPreferences import fr.free.nrw.commons.HandlerService import fr.free.nrw.commons.auth.SessionManager import fr.free.nrw.commons.contributions.Contribution +import fr.free.nrw.commons.kvstore.BasicKvStore import org.junit.Before import org.junit.Test import org.mockito.InjectMocks @@ -21,7 +22,7 @@ class UploadControllerTest { @Mock internal var context: Context? = null @Mock - internal var prefs: SharedPreferences? = null + internal var prefs: BasicKvStore? = null @InjectMocks var uploadController: UploadController? = null diff --git a/app/src/test/kotlin/fr/free/nrw/commons/upload/UploadModelTest.kt b/app/src/test/kotlin/fr/free/nrw/commons/upload/UploadModelTest.kt index 9b3308817..4baabce42 100644 --- a/app/src/test/kotlin/fr/free/nrw/commons/upload/UploadModelTest.kt +++ b/app/src/test/kotlin/fr/free/nrw/commons/upload/UploadModelTest.kt @@ -7,7 +7,10 @@ import android.content.SharedPreferences import android.graphics.BitmapRegionDecoder import android.net.Uri import fr.free.nrw.commons.auth.SessionManager +import fr.free.nrw.commons.kvstore.BasicKvStore +import fr.free.nrw.commons.location.LatLng import fr.free.nrw.commons.mwapi.MediaWikiApi +import fr.free.nrw.commons.nearby.Place import fr.free.nrw.commons.utils.BitmapRegionDecoderWrapper import fr.free.nrw.commons.utils.ImageUtils.IMAGE_OK import fr.free.nrw.commons.utils.ImageUtilsWrapper @@ -35,7 +38,7 @@ class UploadModelTest { internal var licenses: List? = null @Mock @field:[Inject Named("default_preferences")] - internal var prefs: SharedPreferences? = null + internal var prefs: BasicKvStore? = null @Mock @field:[Inject Named("licenses_by_name")] internal var licensesByName: Map? = null @@ -76,7 +79,7 @@ class UploadModelTest { .thenReturn("") `when`(imageUtilsWrapper!!.checkIfImageIsTooDark(any(BitmapRegionDecoder::class.java))) .thenReturn(IMAGE_OK) - `when`(imageUtilsWrapper!!.checkImageGeolocationIsDifferent(anyString(), anyString())) + `when`(imageUtilsWrapper!!.checkImageGeolocationIsDifferent(anyString(), any(LatLng::class.java))) .thenReturn(false) `when`(bitmapRegionDecoderWrapper!!.newInstance(any(FileInputStream::class.java), anyBoolean())) .thenReturn(mock(BitmapRegionDecoder::class.java)) @@ -100,24 +103,21 @@ class UploadModelTest { @Test fun receiveDirect() { val element = mock(Uri::class.java) - uploadModel!!.receiveDirect(element, "image/jpeg", "external", "Q1", "Test", "Test", { _, _ -> } - , "") + uploadModel!!.receiveDirect(element, "image/jpeg", "external", mock(Place::class.java)) { _, _ -> } assertTrue(uploadModel!!.items.size == 1) } @Test fun verifyPreviousNotAvailableForDirectUpload() { val element = mock(Uri::class.java) - uploadModel!!.receiveDirect(element, "image/jpeg", "external", "Q1", "Test", "Test", { _, _ -> } - , "") + uploadModel!!.receiveDirect(element, "image/jpeg", "external", mock(Place::class.java)) { _, _ -> } assertFalse(uploadModel!!.isPreviousAvailable) } @Test fun verifyNextAvailableForDirectUpload() { val element = mock(Uri::class.java) - uploadModel!!.receiveDirect(element, "image/jpeg", "external", "Q1", "Test", "Test", { _, _ -> } - , "") + uploadModel!!.receiveDirect(element, "image/jpeg", "external", mock(Place::class.java)) { _, _ -> } assertTrue(uploadModel!!.isNextAvailable) } @@ -151,16 +151,14 @@ class UploadModelTest { @Test fun isSubmitAvailableForDirectUpload() { val element = mock(Uri::class.java) - uploadModel!!.receiveDirect(element, "image/jpeg", "external", "Q1", "Test", "Test", { _, _ -> } - , "") + uploadModel!!.receiveDirect(element, "image/jpeg", "external", mock(Place::class.java)) { _, _ -> } assertTrue(uploadModel!!.isNextAvailable) } @Test fun getCurrentStepForDirectUpload() { val element = mock(Uri::class.java) - uploadModel!!.receiveDirect(element, "image/jpeg", "external", "Q1", "Test", "Test", { _, _ -> } - , "") + uploadModel!!.receiveDirect(element, "image/jpeg", "external", mock(Place::class.java)) { _, _ -> } assertTrue(uploadModel!!.currentStep == 1) } @@ -185,16 +183,14 @@ class UploadModelTest { @Test fun getStepCountForDirectUpload() { val element = mock(Uri::class.java) - uploadModel!!.receiveDirect(element, "image/jpeg", "external", "Q1", "Test", "Test", { _, _ -> } - , "") + uploadModel!!.receiveDirect(element, "image/jpeg", "external", mock(Place::class.java)) { _, _ -> } assertTrue(uploadModel!!.stepCount == 3) } @Test fun getDirectCount() { val element = mock(Uri::class.java) - uploadModel!!.receiveDirect(element, "image/jpeg", "external", "Q1", "Test", "Test", { _, _ -> } - , "") + uploadModel!!.receiveDirect(element, "image/jpeg", "external", mock(Place::class.java)) { _, _ -> } assertTrue(uploadModel!!.count == 1) } @@ -219,8 +215,7 @@ class UploadModelTest { @Test fun getDirectUploads() { val element = mock(Uri::class.java) - uploadModel!!.receiveDirect(element, "image/jpeg", "external", "Q1", "Test", "Test", { _, _ -> } - , "") + uploadModel!!.receiveDirect(element, "image/jpeg", "external", mock(Place::class.java)) { _, _ -> } assertTrue(uploadModel!!.uploads.size == 1) } @@ -236,8 +231,7 @@ class UploadModelTest { @Test fun isTopCardStateForDirectUpload() { val element = mock(Uri::class.java) - uploadModel!!.receiveDirect(element, "image/jpeg", "external", "Q1", "Test", "Test", { _, _ -> } - , "") + uploadModel!!.receiveDirect(element, "image/jpeg", "external", mock(Place::class.java)) { _, _ -> } assertTrue(uploadModel!!.isTopCardState) } diff --git a/app/src/test/kotlin/fr/free/nrw/commons/upload/UploadPresenterTest.kt b/app/src/test/kotlin/fr/free/nrw/commons/upload/UploadPresenterTest.kt index 73519e310..df63ca8d2 100644 --- a/app/src/test/kotlin/fr/free/nrw/commons/upload/UploadPresenterTest.kt +++ b/app/src/test/kotlin/fr/free/nrw/commons/upload/UploadPresenterTest.kt @@ -2,6 +2,7 @@ package fr.free.nrw.commons.upload import android.net.Uri import fr.free.nrw.commons.mwapi.MediaWikiApi +import fr.free.nrw.commons.nearby.Place import org.junit.Before import org.junit.Test import org.mockito.InjectMocks @@ -44,7 +45,6 @@ class UploadPresenterTest { @Test fun receiveDirect() { val element = Mockito.mock(Uri::class.java) - uploadModel!!.receiveDirect(element, "image/jpeg", "external", "Q1", "Test", "Test", { _, _ -> } - , "") + uploadModel!!.receiveDirect(element, "image/jpeg", "external", Mockito.mock(Place::class.java)) { _, _ -> } } } \ No newline at end of file