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
This commit is contained in:
Vivek Maskara 2019-01-14 00:13:21 +05:30 committed by Ashish Kumar
parent 1b7b909107
commit d4fa9cfa45
61 changed files with 908 additions and 585 deletions

View file

@ -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,41 +28,19 @@ import static org.junit.Assert.assertEquals;
@LargeTest
@RunWith(AndroidJUnit4.class)
public class SettingsActivityTest {
private SharedPreferences prefs;
private Map<String,?> prefValues;
BasicKvStore prefs;
@Rule
public ActivityTestRule<SettingsActivity> activityRule =
new ActivityTestRule<SettingsActivity>(SettingsActivity.class, false, true) {
new ActivityTestRule<>(SettingsActivity.class, false, true);
@Override
protected void afterActivityLaunched() {
// save preferences
prefs = PreferenceManager.getDefaultSharedPreferences(this.getActivity());
prefValues = prefs.getAll();
@Before
public void setup() {
Context context = InstrumentationRegistry.getTargetContext();
String storeName = context.getPackageName() + "_preferences";
prefs = new BasicKvStore(context, storeName);
}
@Override
protected void afterActivityFinished() {
// restore preferences
SharedPreferences.Editor editor = prefs.edit();
for (Map.Entry<String,?> 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();
}
};
@Test
public void setRecentUploadLimitTo100() {
// Open "Use external storage" preference
@ -72,7 +50,6 @@ public class SettingsActivityTest {
// Try setting it to 100
Espresso.onView(withId(android.R.id.edit))
.perform(replaceText("100"));
// Click "OK"

View file

@ -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();
});

View file

@ -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

View file

@ -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();
}
}

View file

@ -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();
}

View file

@ -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) {

View file

@ -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,

View file

@ -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;

View file

@ -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) {

View file

@ -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,8 +35,10 @@ public class CampaignsPresenter implements BasePresenter {
@Override public void onDetachView() {
this.view = null;
if (disposable != null) {
disposable.dispose();
}
}
/**
* make the api call to fetch the campaigns

View file

@ -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<String, ArrayList<String>> categoriesCache;
private List<CategoryItem> 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<CategoryItem> directCategories() {
String directCategory = directPrefs.getString("Category", "");
String directCategory = directKvStore.getString("Category", "");
List<String> categoryList = new ArrayList<>();
Timber.d("Direct category found: " + directCategory);

View file

@ -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);
}
/**

View file

@ -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;

View file

@ -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<Cursor> 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();
}
}

View file

@ -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;

View file

@ -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!");
}

View file

@ -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);
}

View file

@ -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

View file

@ -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

View file

@ -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;

View file

@ -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<String> categoriesAdapter;
private List<String> queryList = new ArrayList<>();

View file

@ -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<Media> imagesAdapter;
private List<Media> queryList = new ArrayList<>();

View file

@ -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<String> getKeySet() {
Map<String, ?> allContents = new HashMap<>(_store.getAll());
allContents.remove(KEY_VERSION);
return allContents.keySet();
}
@Nullable
public Map<String, ?> getAll() {
Map<String, ?> 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<String, String> keyValuePairs) {
SharedPreferences.Editor editor = _store.edit();
for (Map.Entry<String, String> 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);
}
}

View file

@ -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 <T> void putAllJsons(Map<String, T> jsonMap) {
Map<String, String> stringsMap = new HashMap<>(jsonMap.size());
for (Map.Entry<String, T> keyValuePair : jsonMap.entrySet()) {
String jsonString = gson.toJson(keyValuePair.getValue());
stringsMap.put(keyValuePair.getKey(), jsonString);
}
putAllStrings(stringsMap);
}
public <T> void putJson(String key, T object) {
putString(key, gson.toJson(object));
}
public <T> void putJsonWithTypeInfo(String key, T object, Type type) {
putString(key, gson.toJson(object, type));
}
@Nullable
public <T> T getJson(String key, Class<T> clazz) {
String jsonString = getString(key);
try {
return gson.fromJson(jsonString, clazz);
} catch (JsonSyntaxException e) {
return null;
}
}
@Nullable
public <T> T getJson(String key, Type type) {
String jsonString = getString(key);
try {
return gson.fromJson(jsonString, type);
} catch (JsonSyntaxException e) {
return null;
}
}
}

View file

@ -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();
}

View file

@ -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<LatLng> CREATOR = new Creator<LatLng>() {
@Override
public LatLng createFromParcel(Parcel in) {
return new LatLng(in);
}
@Override
public LatLng[] newArray(int size) {
return new LatLng[size];
}
};
}

View file

@ -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;

View file

@ -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) {

View file

@ -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");
}

View file

@ -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;

View file

@ -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<String, Label> 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;
}
}

View file

@ -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;
}

View file

@ -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;

View file

@ -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) {

View file

@ -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<NearbyBaseMarker> 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

View file

@ -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;

View file

@ -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),

View file

@ -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<String, Label> TEXT_TO_DESCRIPTION
= new HashMap<>(Label.values().length);
static {
for (Label label : values()) {
TEXT_TO_DESCRIPTION.put(label.text, label);
}
@Override
public int describeContents() {
return 0;
}
private final String text;
@DrawableRes private final int icon;
Label(String text, @DrawableRes int icon) {
this.text = text;
this.icon = icon;
@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 String getText() {
return text;
public static final Creator<Place> CREATOR = new Creator<Place>() {
@Override
public Place createFromParcel(Parcel in) {
return new Place(in);
}
@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 Place[] newArray(int size) {
return new Place[size];
}
};
}

View file

@ -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<Place> {
@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<Place> {
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<Place> {
});
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<Place> {
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<Place> {
});
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<Place> {
}
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){

View file

@ -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){

View file

@ -48,7 +48,7 @@ public class UnreadNotificationsCheckAsync extends AsyncTask<Void, Void, Notific
}
Date lastNotificationCheckDate = new Date(context.get()
.getSharedPreferences("prefs",0)
.getSharedPreferences("defaultKvStore",0)
.getLong("last_read_notification_date", 0));
Timber.d("You may have unread notifications since"+lastNotificationCheckDate);

View file

@ -2,11 +2,11 @@ package fr.free.nrw.commons.quiz;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.support.v7.app.AlertDialog.Builder;
import fr.free.nrw.commons.R;
import fr.free.nrw.commons.WelcomeActivity;
import fr.free.nrw.commons.kvstore.BasicKvStore;
import fr.free.nrw.commons.mwapi.MediaWikiApi;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.CompositeDisposable;
@ -29,8 +29,8 @@ public class QuizChecker {
public Context context;
private String userName;
private MediaWikiApi mediaWikiApi;
private SharedPreferences revertPref;
private SharedPreferences countPref;
private BasicKvStore revertKvStore;
private BasicKvStore countKvStore;
private static final int UPLOAD_COUNT_THRESHOLD = 5;
private static final String REVERT_PERCENTAGE_FOR_MESSAGE = "50%";
@ -43,12 +43,16 @@ public class QuizChecker {
* @param userName Commons user name
* @param mediaWikiApi instance of MediaWikiApi
*/
public QuizChecker(Context context, String userName, MediaWikiApi mediaWikiApi) {
public QuizChecker(Context context,
String userName,
MediaWikiApi mediaWikiApi,
BasicKvStore revertKvStore,
BasicKvStore countKvStore) {
this.context = context;
this.userName = userName;
this.mediaWikiApi = mediaWikiApi;
revertPref = context.getSharedPreferences(REVERT_SHARED_PREFERENCE, Context.MODE_PRIVATE);
countPref = context.getSharedPreferences(UPLOAD_SHARED_PREFERENCE,Context.MODE_PRIVATE);
this.revertKvStore = revertKvStore;
this.countKvStore = countKvStore;
setUploadCount();
setRevertCount();
}
@ -72,10 +76,10 @@ public class QuizChecker {
* @param uploadCount user's upload count
*/
private void setTotalUploadCount(int uploadCount) {
totalUploadCount = uploadCount - countPref.getInt(UPLOAD_SHARED_PREFERENCE,0);
totalUploadCount = uploadCount - countKvStore.getInt(UPLOAD_SHARED_PREFERENCE, 0);
if ( totalUploadCount < 0){
totalUploadCount = 0;
countPref.edit().putInt(UPLOAD_SHARED_PREFERENCE,0).apply();
countKvStore.putInt(UPLOAD_SHARED_PREFERENCE, 0);
}
isUploadCountFetched = true;
calculateRevertParameter();
@ -103,10 +107,10 @@ public class QuizChecker {
* @param revertCountFetched count of deleted uploads
*/
private void setRevertParameter(int revertCountFetched) {
revertCount = revertCountFetched - revertPref.getInt(REVERT_SHARED_PREFERENCE,0);
revertCount = revertCountFetched - revertKvStore.getInt(REVERT_SHARED_PREFERENCE, 0);
if (revertCount < 0){
revertCount = 0;
revertPref.edit().putInt(REVERT_SHARED_PREFERENCE, 0).apply();
revertKvStore.putInt(REVERT_SHARED_PREFERENCE, 0);
}
isRevertCountFetched = true;
calculateRevertParameter();
@ -117,8 +121,8 @@ public class QuizChecker {
*/
private void calculateRevertParameter() {
if ( revertCount < 0 || totalUploadCount < 0){
revertPref.edit().putInt(REVERT_SHARED_PREFERENCE, 0).apply();
countPref.edit().putInt(UPLOAD_SHARED_PREFERENCE,0).apply();
revertKvStore.putInt(REVERT_SHARED_PREFERENCE, 0);
countKvStore.putInt(UPLOAD_SHARED_PREFERENCE, 0);
return;
}
if (isRevertCountFetched && isUploadCountFetched &&
@ -137,10 +141,10 @@ public class QuizChecker {
alert.setMessage(context.getResources().getString(R.string.quiz_alert_message,
REVERT_PERCENTAGE_FOR_MESSAGE));
alert.setPositiveButton(R.string.about_translate_proceed, (dialog, which) -> {
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();

View file

@ -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;
});

View file

@ -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();
}

View file

@ -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:

View file

@ -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;

View file

@ -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");

View file

@ -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);
}
/**

View file

@ -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,10 +110,8 @@ 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);
}
@ -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

View file

@ -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<String> licenses;
private String license;
private final Map<String, String> licensesByName;
@ -71,7 +73,7 @@ public class UploadModel {
@Inject
UploadModel(@Named("licenses") List<String> licenses,
@Named("default_preferences") SharedPreferences prefs,
@Named("default_preferences") BasicKvStore basicKvStore,
@Named("licenses_by_name") Map<String, String> 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<Uri> mediaUri, String mimeType, String source, SimilarImageInterface similarImageInterface) {
void receive(List<Uri> mediaUri,
String mimeType,
String source,
SimilarImageInterface similarImageInterface) {
initDefaultValues();
Observable<UploadItem> 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<Integer> 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<Integer> checkDarkImage(String filePath) {
return Single.fromCallable(() ->
fileUtilsWrapper.getFileInputStream(filePath))
.map(file -> bitmapRegionDecoderWrapper.newInstance(file, false))
.map(imageUtilsWrapper::checkIfImageIsTooDark);
}
private Single<Integer> 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<Integer> 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<Contribution> buildContributions(List<String> categoryStringList) {

View file

@ -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());
}

View file

@ -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;
}

View file

@ -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);
}
}

View file

@ -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";
}

View file

@ -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);

View file

@ -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

View file

@ -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)

View file

@ -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 + "/")
}

View file

@ -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

View file

@ -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<String>? = 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<String, String>? = 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)
}

View file

@ -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)) { _, _ -> }
}
}