mirror of
https://github.com/commons-app/apps-android-commons.git
synced 2025-10-28 21:33:53 +01:00
Merge branch 'master' into master
This commit is contained in:
commit
b03e67dcd6
66 changed files with 535 additions and 325 deletions
|
|
@ -34,7 +34,7 @@ public class AboutActivity extends NavigationBaseActivity {
|
|||
setContentView(R.layout.activity_about);
|
||||
|
||||
ButterKnife.bind(this);
|
||||
String aboutText = getString(R.string.about_license, getString(R.string.trademarked_name));
|
||||
String aboutText = getString(R.string.about_license);
|
||||
aboutLicenseText.setHtmlText(aboutText);
|
||||
|
||||
versionText.setText(BuildConfig.VERSION_NAME);
|
||||
|
|
|
|||
|
|
@ -57,7 +57,6 @@ public class CommonsApplication extends Application {
|
|||
|
||||
public static final String FEEDBACK_EMAIL_SUBJECT = "Commons Android App (%s) Feedback";
|
||||
|
||||
private CommonsApplicationComponent component;
|
||||
private RefWatcher refWatcher;
|
||||
|
||||
|
||||
|
|
@ -136,9 +135,10 @@ public class CommonsApplication extends Application {
|
|||
.subscribe(() -> {
|
||||
Timber.d("All accounts have been removed");
|
||||
//TODO: fix preference manager
|
||||
defaultPrefs.edit().clear().commit();
|
||||
applicationPrefs.edit().clear().commit();
|
||||
applicationPrefs.edit().putBoolean("firstrun", false).apply();otherPrefs.edit().clear().commit();
|
||||
defaultPrefs.edit().clear().apply();
|
||||
applicationPrefs.edit().clear().apply();
|
||||
applicationPrefs.edit().putBoolean("firstrun", false).apply();
|
||||
otherPrefs.edit().clear().apply();
|
||||
updateAllDatabases();
|
||||
|
||||
logoutListener.onLogoutComplete();
|
||||
|
|
|
|||
|
|
@ -283,7 +283,7 @@ public class MediaDataExtractor {
|
|||
/**
|
||||
* Take our metadata and inject it into a live Media object.
|
||||
* Media object might contain stale or cached data, or emptiness.
|
||||
* @param media
|
||||
* @param media Media object to inject into
|
||||
*/
|
||||
public void fill(Media media) {
|
||||
if (!fetched) {
|
||||
|
|
|
|||
|
|
@ -65,7 +65,7 @@ public class Utils {
|
|||
/**
|
||||
* Capitalizes the first character of a string.
|
||||
*
|
||||
* @param string
|
||||
* @param string String to alter
|
||||
* @return string with capitalized first character
|
||||
*/
|
||||
public static String capitalize(String string) {
|
||||
|
|
|
|||
|
|
@ -32,7 +32,6 @@ import javax.inject.Named;
|
|||
|
||||
import butterknife.BindView;
|
||||
import butterknife.ButterKnife;
|
||||
import dagger.android.AndroidInjection;
|
||||
import fr.free.nrw.commons.BuildConfig;
|
||||
import fr.free.nrw.commons.PageTitle;
|
||||
import fr.free.nrw.commons.R;
|
||||
|
|
|
|||
|
|
@ -1,128 +0,0 @@
|
|||
package fr.free.nrw.commons.auth;
|
||||
|
||||
import android.accounts.AccountAuthenticatorResponse;
|
||||
import android.app.ProgressDialog;
|
||||
import android.content.SharedPreferences;
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Bundle;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import fr.free.nrw.commons.R;
|
||||
import fr.free.nrw.commons.mwapi.MediaWikiApi;
|
||||
import timber.log.Timber;
|
||||
|
||||
import static android.accounts.AccountManager.KEY_ACCOUNT_AUTHENTICATOR_RESPONSE;
|
||||
import static android.accounts.AccountManager.KEY_ACCOUNT_NAME;
|
||||
import static android.accounts.AccountManager.KEY_ACCOUNT_TYPE;
|
||||
import static fr.free.nrw.commons.auth.AccountUtil.ACCOUNT_TYPE;
|
||||
|
||||
class LoginTask extends AsyncTask<String, String, String> {
|
||||
|
||||
private LoginActivity loginActivity;
|
||||
private String username;
|
||||
private String password;
|
||||
private String twoFactorCode = "";
|
||||
private AccountUtil accountUtil;
|
||||
private MediaWikiApi mwApi;
|
||||
|
||||
public LoginTask(LoginActivity loginActivity, String username, String password,
|
||||
String twoFactorCode, AccountUtil accountUtil,
|
||||
MediaWikiApi mwApi, SharedPreferences prefs) {
|
||||
this.loginActivity = loginActivity;
|
||||
this.username = username;
|
||||
this.password = password;
|
||||
this.twoFactorCode = twoFactorCode;
|
||||
this.accountUtil = accountUtil;
|
||||
this.mwApi = mwApi;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPreExecute() {
|
||||
super.onPreExecute();
|
||||
loginActivity.progressDialog = new ProgressDialog(loginActivity);
|
||||
loginActivity.progressDialog.setIndeterminate(true);
|
||||
loginActivity.progressDialog.setTitle(loginActivity.getString(R.string.logging_in_title));
|
||||
loginActivity.progressDialog.setMessage(loginActivity.getString(R.string.logging_in_message));
|
||||
loginActivity.progressDialog.setCanceledOnTouchOutside(false);
|
||||
loginActivity.progressDialog.show();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String doInBackground(String... params) {
|
||||
try {
|
||||
if (twoFactorCode.isEmpty()) {
|
||||
return mwApi.login(username, password);
|
||||
} else {
|
||||
return mwApi.login(username, password, twoFactorCode);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
// Do something better!
|
||||
return "NetworkFailure";
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(String result) {
|
||||
super.onPostExecute(result);
|
||||
Timber.d("Login done!");
|
||||
|
||||
if (result.equals("PASS")) {
|
||||
handlePassResult();
|
||||
} else {
|
||||
handleOtherResults(result);
|
||||
}
|
||||
}
|
||||
|
||||
private void handlePassResult() {
|
||||
loginActivity.showSuccessAndDismissDialog();
|
||||
|
||||
AccountAuthenticatorResponse response = null;
|
||||
|
||||
Bundle extras = loginActivity.getIntent().getExtras();
|
||||
if (extras != null) {
|
||||
Timber.d("Bundle of extras: %s", extras);
|
||||
response = extras.getParcelable(KEY_ACCOUNT_AUTHENTICATOR_RESPONSE);
|
||||
if (response != null) {
|
||||
Bundle authResult = new Bundle();
|
||||
authResult.putString(KEY_ACCOUNT_NAME, username);
|
||||
authResult.putString(KEY_ACCOUNT_TYPE, ACCOUNT_TYPE);
|
||||
response.onResult(authResult);
|
||||
}
|
||||
}
|
||||
|
||||
accountUtil.createAccount(response, username, password);
|
||||
loginActivity.startMainActivity();
|
||||
}
|
||||
|
||||
/**
|
||||
* Match known failure message codes and provide messages.
|
||||
* @param result String
|
||||
*/
|
||||
private void handleOtherResults(String result) {
|
||||
if (result.equals("NetworkFailure")) {
|
||||
// Matches NetworkFailure which is created by the doInBackground method
|
||||
loginActivity.showMessageAndCancelDialog(R.string.login_failed_network);
|
||||
} else if (result.toLowerCase().contains("nosuchuser".toLowerCase()) || result.toLowerCase().contains("noname".toLowerCase())) {
|
||||
// Matches nosuchuser, nosuchusershort, noname
|
||||
loginActivity.showMessageAndCancelDialog(R.string.login_failed_username);
|
||||
loginActivity.emptySensitiveEditFields();
|
||||
} else if (result.toLowerCase().contains("wrongpassword".toLowerCase())) {
|
||||
// Matches wrongpassword, wrongpasswordempty
|
||||
loginActivity.showMessageAndCancelDialog(R.string.login_failed_password);
|
||||
loginActivity.emptySensitiveEditFields();
|
||||
} else if (result.toLowerCase().contains("throttle".toLowerCase())) {
|
||||
// Matches unknown throttle error codes
|
||||
loginActivity.showMessageAndCancelDialog(R.string.login_failed_throttled);
|
||||
} else if (result.toLowerCase().contains("userblocked".toLowerCase())) {
|
||||
// Matches login-userblocked
|
||||
loginActivity.showMessageAndCancelDialog(R.string.login_failed_blocked);
|
||||
} else if (result.equals("2FA")) {
|
||||
loginActivity.askUserForTwoFactorAuth();
|
||||
} else {
|
||||
// Occurs with unhandled login failure codes
|
||||
Timber.d("Login failed with reason: %s", result);
|
||||
loginActivity.showMessageAndCancelDialog(R.string.login_failed_generic);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,6 +1,5 @@
|
|||
package fr.free.nrw.commons.category;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.os.Bundle;
|
||||
import android.support.v7.app.AlertDialog;
|
||||
|
|
@ -298,8 +297,10 @@ public class CategorizationFragment extends CommonsDaggerSupportFragment {
|
|||
//Check if item contains a 4-digit word anywhere within the string (.* is wildcard)
|
||||
//And that item does not equal the current year or previous year
|
||||
//And if it is an irrelevant category such as Media_needing_categories_as_of_16_June_2017(Issue #750)
|
||||
//Check if the year in the form of XX(X)0s is relevant, i.e. in the 2000s or 2010s as stated in Issue #1029
|
||||
return ((item.matches(".*(19|20)\\d{2}.*") && !item.contains(yearInString) && !item.contains(prevYearInString))
|
||||
|| item.matches("(.*)needing(.*)") || item.matches("(.*)taken on(.*)"));
|
||||
|| item.matches("(.*)needing(.*)") || item.matches("(.*)taken on(.*)")
|
||||
|| (item.matches(".*0s.*") && !item.matches(".*(200|201)0s.*")));
|
||||
}
|
||||
|
||||
private void updateCategoryCount(CategoryItem item) {
|
||||
|
|
|
|||
|
|
@ -93,10 +93,15 @@ class ContributionController {
|
|||
shareIntent.putExtra(EXTRA_SOURCE, SOURCE_GALLERY);
|
||||
break;
|
||||
case SELECT_FROM_CAMERA:
|
||||
shareIntent.setType("image/jpeg"); //FIXME: Find out appropriate mime type
|
||||
//FIXME: Find out appropriate mime type
|
||||
// AFAIK this is the right type for a JPEG image
|
||||
// https://developer.android.com/training/sharing/send.html#send-binary-content
|
||||
shareIntent.setType("image/jpeg");
|
||||
shareIntent.putExtra(EXTRA_STREAM, lastGeneratedCaptureUri);
|
||||
shareIntent.putExtra(EXTRA_SOURCE, SOURCE_CAMERA);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
Timber.i("Image selected");
|
||||
try {
|
||||
|
|
|
|||
|
|
@ -86,7 +86,7 @@ public class ContributionsContentProvider extends CommonsDaggerContentProvider {
|
|||
public Uri insert(@NonNull Uri uri, ContentValues contentValues) {
|
||||
int uriType = uriMatcher.match(uri);
|
||||
SQLiteDatabase sqlDB = dbOpenHelper.getWritableDatabase();
|
||||
long id = 0;
|
||||
long id;
|
||||
switch (uriType) {
|
||||
case CONTRIBUTIONS:
|
||||
id = sqlDB.insert(TABLE_NAME, null, contentValues);
|
||||
|
|
@ -158,7 +158,7 @@ public class ContributionsContentProvider extends CommonsDaggerContentProvider {
|
|||
*/
|
||||
int uriType = uriMatcher.match(uri);
|
||||
SQLiteDatabase sqlDB = dbOpenHelper.getWritableDatabase();
|
||||
int rowsUpdated = 0;
|
||||
int rowsUpdated;
|
||||
switch (uriType) {
|
||||
case CONTRIBUTIONS:
|
||||
rowsUpdated = sqlDB.update(TABLE_NAME, contentValues, selection, selectionArgs);
|
||||
|
|
|
|||
|
|
@ -20,6 +20,8 @@ import android.widget.ListAdapter;
|
|||
import android.widget.ProgressBar;
|
||||
import android.widget.TextView;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Named;
|
||||
|
||||
|
|
@ -45,8 +47,12 @@ public class ContributionsListFragment extends CommonsDaggerSupportFragment {
|
|||
@BindView(R.id.loadingContributionsProgressBar)
|
||||
ProgressBar progressBar;
|
||||
|
||||
@Inject @Named("prefs") SharedPreferences prefs;
|
||||
@Inject @Named("default_preferences") SharedPreferences defaultPrefs;
|
||||
@Inject
|
||||
@Named("prefs")
|
||||
SharedPreferences prefs;
|
||||
@Inject
|
||||
@Named("default_preferences")
|
||||
SharedPreferences defaultPrefs;
|
||||
|
||||
private ContributionController controller;
|
||||
|
||||
|
|
@ -208,7 +214,7 @@ public class ContributionsListFragment extends CommonsDaggerSupportFragment {
|
|||
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
|
||||
@NonNull int[] grantResults) {
|
||||
Timber.d("onRequestPermissionsResult: req code = " + " perm = "
|
||||
+ permissions + " grant =" + grantResults);
|
||||
+ Arrays.toString(permissions) + " grant =" + Arrays.toString(grantResults));
|
||||
|
||||
switch (requestCode) {
|
||||
// 1 = Storage allowed when gallery selected
|
||||
|
|
|
|||
|
|
@ -23,7 +23,6 @@ import java.util.TimeZone;
|
|||
import javax.inject.Inject;
|
||||
import javax.inject.Named;
|
||||
|
||||
import fr.free.nrw.commons.CommonsApplication;
|
||||
import fr.free.nrw.commons.Utils;
|
||||
import fr.free.nrw.commons.di.ApplicationlessInjection;
|
||||
import fr.free.nrw.commons.mwapi.LogEventResult;
|
||||
|
|
|
|||
|
|
@ -1,7 +1,5 @@
|
|||
package fr.free.nrw.commons.di;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import javax.inject.Singleton;
|
||||
|
||||
import dagger.Component;
|
||||
|
|
@ -11,10 +9,7 @@ import dagger.android.support.AndroidSupportInjectionModule;
|
|||
import fr.free.nrw.commons.CommonsApplication;
|
||||
import fr.free.nrw.commons.MediaWikiImageView;
|
||||
import fr.free.nrw.commons.auth.LoginActivity;
|
||||
import fr.free.nrw.commons.category.CategoryContentProvider;
|
||||
import fr.free.nrw.commons.contributions.ContributionsContentProvider;
|
||||
import fr.free.nrw.commons.contributions.ContributionsSyncAdapter;
|
||||
import fr.free.nrw.commons.modifications.ModificationsContentProvider;
|
||||
import fr.free.nrw.commons.modifications.ModificationsSyncAdapter;
|
||||
import fr.free.nrw.commons.settings.SettingsFragment;
|
||||
|
||||
|
|
|
|||
|
|
@ -38,7 +38,7 @@ public class LatLng {
|
|||
/**
|
||||
* gets the latitude and longitude of a given non-null location
|
||||
* @param location the non-null location of the user
|
||||
* @return
|
||||
* @return LatLng the Latitude and Longitude of a given location
|
||||
*/
|
||||
public static LatLng from(@NonNull Location location) {
|
||||
return new LatLng(location.getLatitude(), location.getLongitude(), location.getAccuracy());
|
||||
|
|
@ -48,13 +48,12 @@ public class LatLng {
|
|||
* creates a hash code for the longitude and longitude
|
||||
*/
|
||||
public int hashCode() {
|
||||
boolean var1 = true;
|
||||
byte var2 = 1;
|
||||
long var3 = Double.doubleToLongBits(this.latitude);
|
||||
int var5 = 31 * var2 + (int)(var3 ^ var3 >>> 32);
|
||||
var3 = Double.doubleToLongBits(this.longitude);
|
||||
var5 = 31 * var5 + (int)(var3 ^ var3 >>> 32);
|
||||
return var5;
|
||||
byte var1 = 1;
|
||||
long var2 = Double.doubleToLongBits(this.latitude);
|
||||
int var3 = 31 * var1 + (int)(var2 ^ var2 >>> 32);
|
||||
var2 = Double.doubleToLongBits(this.longitude);
|
||||
var3 = 31 * var3 + (int)(var2 ^ var2 >>> 32);
|
||||
return var3;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -14,9 +14,6 @@ import android.support.v4.content.ContextCompat;
|
|||
import java.util.List;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
|
||||
import timber.log.Timber;
|
||||
|
||||
public class LocationServiceManager implements LocationListener {
|
||||
|
|
@ -33,6 +30,7 @@ public class LocationServiceManager implements LocationListener {
|
|||
|
||||
/**
|
||||
* Constructs a new instance of LocationServiceManager.
|
||||
*
|
||||
* @param context the context
|
||||
*/
|
||||
public LocationServiceManager(Context context) {
|
||||
|
|
@ -42,6 +40,7 @@ public class LocationServiceManager implements LocationListener {
|
|||
|
||||
/**
|
||||
* Returns the current status of the GPS provider.
|
||||
*
|
||||
* @return true if the GPS provider is enabled
|
||||
*/
|
||||
public boolean isProviderEnabled() {
|
||||
|
|
@ -50,6 +49,7 @@ public class LocationServiceManager implements LocationListener {
|
|||
|
||||
/**
|
||||
* Returns whether the location permission is granted.
|
||||
*
|
||||
* @return true if the location permission is granted
|
||||
*/
|
||||
public boolean isLocationPermissionGranted() {
|
||||
|
|
@ -59,6 +59,7 @@ public class LocationServiceManager implements LocationListener {
|
|||
|
||||
/**
|
||||
* Requests the location permission to be granted.
|
||||
*
|
||||
* @param activity the activity
|
||||
*/
|
||||
public void requestPermissions(Activity activity) {
|
||||
|
|
@ -71,11 +72,9 @@ public class LocationServiceManager implements LocationListener {
|
|||
}
|
||||
|
||||
public boolean isPermissionExplanationRequired(Activity activity) {
|
||||
if (activity.isFinishing()) {
|
||||
return false;
|
||||
}
|
||||
return ActivityCompat.shouldShowRequestPermissionRationale(activity,
|
||||
Manifest.permission.ACCESS_FINE_LOCATION);
|
||||
return !activity.isFinishing() &&
|
||||
ActivityCompat.shouldShowRequestPermissionRationale(activity,
|
||||
Manifest.permission.ACCESS_FINE_LOCATION);
|
||||
}
|
||||
|
||||
public LatLng getLastLocation() {
|
||||
|
|
@ -85,7 +84,8 @@ public class LocationServiceManager implements LocationListener {
|
|||
return LatLng.from(lastLocation);
|
||||
}
|
||||
|
||||
/** Registers a LocationManager to listen for current location.
|
||||
/**
|
||||
* Registers a LocationManager to listen for current location.
|
||||
*/
|
||||
public void registerLocationManager() {
|
||||
if (!isLocationManagerRegistered)
|
||||
|
|
@ -95,6 +95,7 @@ public class LocationServiceManager implements LocationListener {
|
|||
|
||||
/**
|
||||
* Requests location updates from the specified provider.
|
||||
*
|
||||
* @param locationProvider the location provider
|
||||
* @return true if successful
|
||||
*/
|
||||
|
|
@ -116,7 +117,8 @@ public class LocationServiceManager implements LocationListener {
|
|||
|
||||
/**
|
||||
* Returns whether a given location is better than the current best location.
|
||||
* @param location the location to be tested
|
||||
*
|
||||
* @param location the location to be tested
|
||||
* @param currentBestLocation the current best location
|
||||
* @return true if the given location is better
|
||||
*/
|
||||
|
|
@ -172,7 +174,8 @@ public class LocationServiceManager implements LocationListener {
|
|||
return provider1.equals(provider2);
|
||||
}
|
||||
|
||||
/** Unregisters location manager.
|
||||
/**
|
||||
* Unregisters location manager.
|
||||
*/
|
||||
public void unregisterLocationManager() {
|
||||
isLocationManagerRegistered = false;
|
||||
|
|
@ -185,6 +188,7 @@ public class LocationServiceManager implements LocationListener {
|
|||
|
||||
/**
|
||||
* Adds a new listener to the list of location listeners.
|
||||
*
|
||||
* @param listener the new listener
|
||||
*/
|
||||
public void addLocationListener(LocationUpdateListener listener) {
|
||||
|
|
@ -195,6 +199,7 @@ public class LocationServiceManager implements LocationListener {
|
|||
|
||||
/**
|
||||
* Removes a listener from the list of location listeners.
|
||||
*
|
||||
* @param listener the listener to be removed
|
||||
*/
|
||||
public void removeLocationListener(LocationUpdateListener listener) {
|
||||
|
|
|
|||
|
|
@ -77,7 +77,7 @@ public class MediaDetailFragment extends CommonsDaggerSupportFragment {
|
|||
private ViewTreeObserver.OnGlobalLayoutListener layoutListener; // for layout stuff, only used once!
|
||||
private ViewTreeObserver.OnScrollChangedListener scrollListener;
|
||||
private DataSetObserver dataObserver;
|
||||
private AsyncTask<Void,Void,Boolean> detailFetchTask;
|
||||
private AsyncTask<Void, Void, Boolean> detailFetchTask;
|
||||
private LicenseList licenseList;
|
||||
|
||||
@Override
|
||||
|
|
@ -96,7 +96,7 @@ public class MediaDetailFragment extends CommonsDaggerSupportFragment {
|
|||
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||
detailProvider = (MediaDetailPagerFragment.MediaDetailProvider)getActivity();
|
||||
detailProvider = (MediaDetailPagerFragment.MediaDetailProvider) getActivity();
|
||||
|
||||
if (savedInstanceState != null) {
|
||||
editable = savedInstanceState.getBoolean("editable");
|
||||
|
|
@ -157,7 +157,8 @@ public class MediaDetailFragment extends CommonsDaggerSupportFragment {
|
|||
return view;
|
||||
}
|
||||
|
||||
@Override public void onResume() {
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
Media media = detailProvider.getMediaAtPosition(index);
|
||||
if (media == null) {
|
||||
|
|
@ -239,13 +240,13 @@ public class MediaDetailFragment extends CommonsDaggerSupportFragment {
|
|||
detailFetchTask.cancel(true);
|
||||
detailFetchTask = null;
|
||||
}
|
||||
if (layoutListener != null) {
|
||||
if (layoutListener != null && getView() != null) {
|
||||
getView().getViewTreeObserver().removeGlobalOnLayoutListener(layoutListener); // old Android was on crack. CRACK IS WHACK
|
||||
layoutListener = null;
|
||||
}
|
||||
if (scrollListener != null) {
|
||||
if (scrollListener != null && getView() != null) {
|
||||
getView().getViewTreeObserver().removeOnScrollChangedListener(scrollListener);
|
||||
scrollListener = null;
|
||||
scrollListener = null;
|
||||
}
|
||||
if (dataObserver != null) {
|
||||
detailProvider.unregisterDataSetObserver(dataObserver);
|
||||
|
|
@ -290,7 +291,7 @@ public class MediaDetailFragment extends CommonsDaggerSupportFragment {
|
|||
|
||||
private View buildCatLabel(final String catName, ViewGroup categoryContainer) {
|
||||
final View item = LayoutInflater.from(getContext()).inflate(R.layout.detail_category_item, categoryContainer, false);
|
||||
final CompatTextView textView = (CompatTextView)item.findViewById(R.id.mediaDetailCategoryItemText);
|
||||
final CompatTextView textView = (CompatTextView) item.findViewById(R.id.mediaDetailCategoryItemText);
|
||||
|
||||
textView.setText(catName);
|
||||
if (categoriesLoaded && categoriesPresent) {
|
||||
|
|
@ -317,7 +318,7 @@ public class MediaDetailFragment extends CommonsDaggerSupportFragment {
|
|||
// You must face the darkness alone
|
||||
int scrollY = scrollView.getScrollY();
|
||||
int scrollMax = getView().getHeight();
|
||||
float scrollPercentage = (float)scrollY / (float)scrollMax;
|
||||
float scrollPercentage = (float) scrollY / (float) scrollMax;
|
||||
final float transparencyMax = 0.75f;
|
||||
if (scrollPercentage > transparencyMax) {
|
||||
scrollPercentage = transparencyMax;
|
||||
|
|
@ -371,7 +372,8 @@ public class MediaDetailFragment extends CommonsDaggerSupportFragment {
|
|||
}
|
||||
|
||||
|
||||
private @Nullable String licenseLink(Media media) {
|
||||
private @Nullable
|
||||
String licenseLink(Media media) {
|
||||
String licenseKey = media.getLicense();
|
||||
if (licenseKey == null || licenseKey.equals("")) {
|
||||
return null;
|
||||
|
|
|
|||
|
|
@ -44,9 +44,13 @@ import static android.content.pm.PackageManager.PERMISSION_GRANTED;
|
|||
|
||||
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")
|
||||
SharedPreferences prefs;
|
||||
|
||||
private ViewPager pager;
|
||||
private Boolean editable;
|
||||
|
|
@ -175,13 +179,19 @@ public class MediaDetailPagerFragment extends CommonsDaggerSupportFragment imple
|
|||
req.allowScanningByMediaScanner();
|
||||
req.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && !(ContextCompat.checkSelfPermission(getContext(), READ_EXTERNAL_STORAGE) == PERMISSION_GRANTED)) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M &&
|
||||
ContextCompat.checkSelfPermission(getContext(), READ_EXTERNAL_STORAGE)
|
||||
!= PERMISSION_GRANTED
|
||||
&& getView() != null) {
|
||||
Snackbar.make(getView(), R.string.read_storage_permission_rationale,
|
||||
Snackbar.LENGTH_INDEFINITE).setAction(R.string.ok,
|
||||
view -> ActivityCompat.requestPermissions(getActivity(),
|
||||
new String[]{READ_EXTERNAL_STORAGE}, 1)).show();
|
||||
} else {
|
||||
((DownloadManager) getActivity().getSystemService(DOWNLOAD_SERVICE)).enqueue(req);
|
||||
DownloadManager systemService = (DownloadManager) getActivity().getSystemService(DOWNLOAD_SERVICE);
|
||||
if (systemService != null) {
|
||||
systemService.enqueue(req);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -70,7 +70,7 @@ public class ModificationsContentProvider extends CommonsDaggerContentProvider {
|
|||
public Uri insert(@NonNull Uri uri, ContentValues contentValues) {
|
||||
int uriType = uriMatcher.match(uri);
|
||||
SQLiteDatabase sqlDB = dbOpenHelper.getWritableDatabase();
|
||||
long id = 0;
|
||||
long id;
|
||||
switch (uriType) {
|
||||
case MODIFICATIONS:
|
||||
id = sqlDB.insert(TABLE_NAME, null, contentValues);
|
||||
|
|
@ -132,7 +132,7 @@ public class ModificationsContentProvider extends CommonsDaggerContentProvider {
|
|||
*/
|
||||
int uriType = uriMatcher.match(uri);
|
||||
SQLiteDatabase sqlDB = dbOpenHelper.getWritableDatabase();
|
||||
int rowsUpdated = 0;
|
||||
int rowsUpdated;
|
||||
switch (uriType) {
|
||||
case MODIFICATIONS:
|
||||
rowsUpdated = sqlDB.update(TABLE_NAME,
|
||||
|
|
|
|||
|
|
@ -52,7 +52,7 @@ public class ModifierSequenceDao {
|
|||
|
||||
ModifierSequence fromCursor(Cursor cursor) {
|
||||
// Hardcoding column positions!
|
||||
ModifierSequence ms = null;
|
||||
ModifierSequence ms;
|
||||
try {
|
||||
ms = new ModifierSequence(Uri.parse(cursor.getString(1)),
|
||||
new JSONObject(cursor.getString(2)));
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
package fr.free.nrw.commons.nearby;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.content.pm.PackageManager;
|
||||
|
|
@ -41,8 +40,6 @@ import io.reactivex.disposables.Disposable;
|
|||
import io.reactivex.schedulers.Schedulers;
|
||||
import timber.log.Timber;
|
||||
|
||||
import static fr.free.nrw.commons.location.LocationServiceManager.LOCATION_REQUEST;
|
||||
|
||||
|
||||
public class NearbyActivity extends NavigationBaseActivity implements LocationUpdateListener {
|
||||
|
||||
|
|
@ -259,7 +256,7 @@ public class NearbyActivity extends NavigationBaseActivity implements LocationUp
|
|||
/**
|
||||
* This method should be the single point to load/refresh nearby places
|
||||
*
|
||||
* @param isHardRefresh
|
||||
* @param isHardRefresh Should display a toast if the location hasn't changed
|
||||
*/
|
||||
private void refreshView(boolean isHardRefresh) {
|
||||
if (lockNearbyView) {
|
||||
|
|
|
|||
|
|
@ -39,8 +39,9 @@ public class NearbyController {
|
|||
|
||||
/**
|
||||
* Prepares Place list to make their distance information update later.
|
||||
*
|
||||
* @param curLatLng current location for user
|
||||
* @param context context
|
||||
* @param context context
|
||||
* @return Place list without distance information
|
||||
*/
|
||||
public List<Place> loadAttractionsFromLocation(LatLng curLatLng, Context context) {
|
||||
|
|
@ -51,25 +52,24 @@ public class NearbyController {
|
|||
List<Place> places = prefs.getBoolean("useWikidata", true)
|
||||
? nearbyPlaces.getFromWikidataQuery(curLatLng, Locale.getDefault().getLanguage())
|
||||
: nearbyPlaces.getFromWikiNeedsPictures();
|
||||
if (curLatLng != null) {
|
||||
Timber.d("Sorting places by distance...");
|
||||
final Map<Place, Double> distances = new HashMap<>();
|
||||
for (Place place: places) {
|
||||
distances.put(place, computeDistanceBetween(place.location, curLatLng));
|
||||
}
|
||||
Collections.sort(places,
|
||||
(lhs, rhs) -> {
|
||||
double lhsDistance = distances.get(lhs);
|
||||
double rhsDistance = distances.get(rhs);
|
||||
return (int) (lhsDistance - rhsDistance);
|
||||
}
|
||||
);
|
||||
Timber.d("Sorting places by distance...");
|
||||
final Map<Place, Double> distances = new HashMap<>();
|
||||
for (Place place : places) {
|
||||
distances.put(place, computeDistanceBetween(place.location, curLatLng));
|
||||
}
|
||||
Collections.sort(places,
|
||||
(lhs, rhs) -> {
|
||||
double lhsDistance = distances.get(lhs);
|
||||
double rhsDistance = distances.get(rhs);
|
||||
return (int) (lhsDistance - rhsDistance);
|
||||
}
|
||||
);
|
||||
return places;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads attractions from location for list view, we need to return Place data type.
|
||||
*
|
||||
* @param curLatLng users current location
|
||||
* @param placeList list of nearby places in Place data type
|
||||
* @return Place list that holds nearby places
|
||||
|
|
@ -78,7 +78,7 @@ public class NearbyController {
|
|||
LatLng curLatLng,
|
||||
List<Place> placeList) {
|
||||
placeList = placeList.subList(0, Math.min(placeList.size(), MAX_RESULTS));
|
||||
for (Place place: placeList) {
|
||||
for (Place place : placeList) {
|
||||
String distance = formatDistanceBetween(curLatLng, place.location);
|
||||
place.setDistance(distance);
|
||||
}
|
||||
|
|
@ -86,7 +86,8 @@ public class NearbyController {
|
|||
}
|
||||
|
||||
/**
|
||||
*Loads attractions from location for map view, we need to return BaseMarkerOption data type.
|
||||
* Loads attractions from location for map view, we need to return BaseMarkerOption data type.
|
||||
*
|
||||
* @param curLatLng users current location
|
||||
* @param placeList list of nearby places in Place data type
|
||||
* @return BaseMarkerOptions list that holds nearby places
|
||||
|
|
@ -103,26 +104,28 @@ public class NearbyController {
|
|||
|
||||
placeList = placeList.subList(0, Math.min(placeList.size(), MAX_RESULTS));
|
||||
|
||||
Bitmap icon = UiUtils.getBitmap(
|
||||
VectorDrawableCompat.create(
|
||||
context.getResources(), R.drawable.ic_custom_map_marker, context.getTheme()
|
||||
));
|
||||
VectorDrawableCompat vectorDrawable = VectorDrawableCompat.create(
|
||||
context.getResources(), R.drawable.ic_custom_map_marker, context.getTheme()
|
||||
);
|
||||
if (vectorDrawable != null) {
|
||||
Bitmap icon = UiUtils.getBitmap(vectorDrawable);
|
||||
|
||||
for (Place place: placeList) {
|
||||
String distance = formatDistanceBetween(curLatLng, place.location);
|
||||
place.setDistance(distance);
|
||||
for (Place place : placeList) {
|
||||
String distance = formatDistanceBetween(curLatLng, place.location);
|
||||
place.setDistance(distance);
|
||||
|
||||
NearbyBaseMarker nearbyBaseMarker = new NearbyBaseMarker();
|
||||
nearbyBaseMarker.title(place.name);
|
||||
nearbyBaseMarker.position(
|
||||
new com.mapbox.mapboxsdk.geometry.LatLng(
|
||||
place.location.getLatitude(),
|
||||
place.location.getLongitude()));
|
||||
nearbyBaseMarker.place(place);
|
||||
nearbyBaseMarker.icon(IconFactory.getInstance(context)
|
||||
.fromBitmap(icon));
|
||||
NearbyBaseMarker nearbyBaseMarker = new NearbyBaseMarker();
|
||||
nearbyBaseMarker.title(place.name);
|
||||
nearbyBaseMarker.position(
|
||||
new com.mapbox.mapboxsdk.geometry.LatLng(
|
||||
place.location.getLatitude(),
|
||||
place.location.getLongitude()));
|
||||
nearbyBaseMarker.place(place);
|
||||
nearbyBaseMarker.icon(IconFactory.getInstance(context)
|
||||
.fromBitmap(icon));
|
||||
|
||||
baseMarkerOptions.add(nearbyBaseMarker);
|
||||
baseMarkerOptions.add(nearbyBaseMarker);
|
||||
}
|
||||
}
|
||||
return baseMarkerOptions;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,7 +19,6 @@ import java.util.Collections;
|
|||
import java.util.List;
|
||||
|
||||
import dagger.android.support.AndroidSupportInjection;
|
||||
import dagger.android.support.DaggerFragment;
|
||||
import fr.free.nrw.commons.R;
|
||||
import fr.free.nrw.commons.location.LatLng;
|
||||
import fr.free.nrw.commons.utils.UriDeserializer;
|
||||
|
|
|
|||
|
|
@ -9,7 +9,6 @@ import android.view.ViewGroup;
|
|||
|
||||
import butterknife.ButterKnife;
|
||||
import dagger.android.support.AndroidSupportInjection;
|
||||
import dagger.android.support.DaggerFragment;
|
||||
import fr.free.nrw.commons.R;
|
||||
import timber.log.Timber;
|
||||
|
||||
|
|
|
|||
|
|
@ -61,9 +61,7 @@ public class NotificationActivity extends NavigationBaseActivity {
|
|||
.subscribe(notificationList -> {
|
||||
Timber.d("Number of notifications is %d", notificationList.size());
|
||||
setAdapter(notificationList);
|
||||
}, throwable -> {
|
||||
Timber.e(throwable, "Error occurred while loading notifications");
|
||||
});
|
||||
}, throwable -> Timber.e(throwable, "Error occurred while loading notifications"));
|
||||
}
|
||||
|
||||
private void handleUrl(String url) {
|
||||
|
|
|
|||
|
|
@ -8,13 +8,9 @@ import android.widget.TextView;
|
|||
|
||||
import com.pedrogomez.renderers.Renderer;
|
||||
|
||||
import java.util.Calendar;
|
||||
import java.util.Date;
|
||||
|
||||
import butterknife.BindView;
|
||||
import butterknife.ButterKnife;
|
||||
import fr.free.nrw.commons.R;
|
||||
import fr.free.nrw.commons.utils.DateUtils;
|
||||
|
||||
/**
|
||||
* Created by root on 19.12.2017.
|
||||
|
|
|
|||
|
|
@ -25,7 +25,6 @@ import java.io.File;
|
|||
import javax.inject.Inject;
|
||||
import javax.inject.Named;
|
||||
|
||||
import dagger.android.AndroidInjection;
|
||||
import fr.free.nrw.commons.BuildConfig;
|
||||
import fr.free.nrw.commons.CommonsApplication;
|
||||
import fr.free.nrw.commons.R;
|
||||
|
|
@ -71,7 +70,12 @@ public class SettingsFragment extends PreferenceFragment {
|
|||
uploadLimit.setText(uploads + "");
|
||||
uploadLimit.setSummary(uploads + "");
|
||||
uploadLimit.setOnPreferenceChangeListener((preference, newValue) -> {
|
||||
int value = Integer.parseInt(newValue.toString());
|
||||
int value;
|
||||
try {
|
||||
value = Integer.parseInt(newValue.toString());
|
||||
} catch(Exception e) {
|
||||
value = 100; //Default number
|
||||
}
|
||||
final SharedPreferences.Editor editor = prefs.edit();
|
||||
if (value > 500) {
|
||||
new AlertDialog.Builder(getActivity())
|
||||
|
|
@ -85,9 +89,9 @@ public class SettingsFragment extends PreferenceFragment {
|
|||
uploadLimit.setSummary(500 + "");
|
||||
uploadLimit.setText(500 + "");
|
||||
} else {
|
||||
editor.putInt(Prefs.UPLOADS_SHOWING, Integer.parseInt(newValue.toString()));
|
||||
editor.putInt(Prefs.UPLOADS_SHOWING, value);
|
||||
editor.putBoolean(Prefs.IS_CONTRIBUTION_COUNT_CHANGED,true);
|
||||
uploadLimit.setSummary(newValue.toString());
|
||||
uploadLimit.setSummary(String.valueOf(value));
|
||||
}
|
||||
editor.apply();
|
||||
return true;
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
package fr.free.nrw.commons.ui.widget;
|
||||
|
||||
/**
|
||||
* Created by mikel on 07/08/2017.
|
||||
/*
|
||||
*Created by mikel on 07/08/2017.
|
||||
*/
|
||||
|
||||
import android.content.Context;
|
||||
|
|
@ -20,20 +20,22 @@ import fr.free.nrw.commons.utils.UiUtils;
|
|||
* a text view compatible with older versions of the platform
|
||||
*/
|
||||
public class CompatTextView extends AppCompatTextView {
|
||||
|
||||
|
||||
/**
|
||||
* Constructs a new instance of CompatTextView
|
||||
*
|
||||
* @param context the view context
|
||||
*/
|
||||
public CompatTextView(Context context) {
|
||||
super(context);
|
||||
init(null);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Constructs a new instance of CompatTextView
|
||||
*
|
||||
* @param context the view context
|
||||
* @param attrs the set of attributes for the view
|
||||
* @param attrs the set of attributes for the view
|
||||
*/
|
||||
public CompatTextView(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
|
|
@ -42,6 +44,7 @@ public class CompatTextView extends AppCompatTextView {
|
|||
|
||||
/**
|
||||
* Constructs a new instance of CompatTextView
|
||||
*
|
||||
* @param context
|
||||
* @param attrs
|
||||
* @param defStyleAttr
|
||||
|
|
@ -53,6 +56,7 @@ public class CompatTextView extends AppCompatTextView {
|
|||
|
||||
/**
|
||||
* initializes the view
|
||||
*
|
||||
* @param attrs the attribute set of the view, which can be null
|
||||
*/
|
||||
private void init(@Nullable AttributeSet attrs) {
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ public abstract class OverlayDialog extends DialogFragment {
|
|||
|
||||
/**
|
||||
* creates a DialogFragment with the correct style and theme
|
||||
* @param savedInstanceState
|
||||
* @param savedInstanceState bundle re-constructed from a previous saved state
|
||||
*/
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ public class FileUtils {
|
|||
* other file-based ContentProviders.
|
||||
*
|
||||
* @param context The context.
|
||||
* @param uri The Uri to query.
|
||||
* @param uri The Uri to query.
|
||||
* @author paulburke
|
||||
*/
|
||||
// Can be safely suppressed, checks for isKitKat before running isDocumentUri
|
||||
|
|
@ -49,29 +49,31 @@ public class FileUtils {
|
|||
if ("primary".equalsIgnoreCase(type)) {
|
||||
return Environment.getExternalStorageDirectory() + "/" + split[1];
|
||||
}
|
||||
}
|
||||
// DownloadsProvider
|
||||
else if (isDownloadsDocument(uri)) {
|
||||
} else if (isDownloadsDocument(uri)) { // DownloadsProvider
|
||||
|
||||
final String id = DocumentsContract.getDocumentId(uri);
|
||||
final Uri contentUri = ContentUris.withAppendedId(
|
||||
Uri.parse("content://downloads/public_downloads"), Long.valueOf(id));
|
||||
|
||||
return getDataColumn(context, contentUri, null, null);
|
||||
}
|
||||
// MediaProvider
|
||||
else if (isMediaDocument(uri)) {
|
||||
} else if (isMediaDocument(uri)) { // MediaProvider
|
||||
final String docId = DocumentsContract.getDocumentId(uri);
|
||||
final String[] split = docId.split(":");
|
||||
final String type = split[0];
|
||||
|
||||
Uri contentUri = null;
|
||||
if ("image".equals(type)) {
|
||||
contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
|
||||
} else if ("video".equals(type)) {
|
||||
contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;
|
||||
} else if ("audio".equals(type)) {
|
||||
contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
|
||||
switch (type) {
|
||||
case "image":
|
||||
contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
|
||||
break;
|
||||
case "video":
|
||||
contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;
|
||||
break;
|
||||
case "audio":
|
||||
contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
final String selection = "_id=?";
|
||||
|
|
@ -163,7 +165,8 @@ public class FileUtils {
|
|||
|
||||
/**
|
||||
* Copy content from source file to destination file.
|
||||
* @param source stream copied from
|
||||
*
|
||||
* @param source stream copied from
|
||||
* @param destination stream copied to
|
||||
* @throws IOException thrown when failing to read source or opening destination file
|
||||
*/
|
||||
|
|
@ -176,7 +179,8 @@ public class FileUtils {
|
|||
|
||||
/**
|
||||
* Copy content from source file to destination file.
|
||||
* @param source file descriptor copied from
|
||||
*
|
||||
* @param source file descriptor copied from
|
||||
* @param destination file path copied to
|
||||
* @throws IOException thrown when failing to read source or opening destination file
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -113,11 +113,11 @@ public class GPSExtractor {
|
|||
*/
|
||||
@Nullable
|
||||
public String getCoords(boolean useGPS) {
|
||||
String latitude = "";
|
||||
String longitude = "";
|
||||
String latitude_ref = "";
|
||||
String longitude_ref = "";
|
||||
String decimalCoords = "";
|
||||
String latitude;
|
||||
String longitude;
|
||||
String latitudeRef;
|
||||
String longitudeRef;
|
||||
String decimalCoords;
|
||||
|
||||
//If image has no EXIF data and user has enabled GPS setting, get user's location
|
||||
if (exif == null || exif.getAttribute(ExifInterface.TAG_GPS_LATITUDE) == null) {
|
||||
|
|
@ -150,15 +150,15 @@ public class GPSExtractor {
|
|||
Timber.d("EXIF data has location info");
|
||||
|
||||
latitude = exif.getAttribute(ExifInterface.TAG_GPS_LATITUDE);
|
||||
latitude_ref = exif.getAttribute(ExifInterface.TAG_GPS_LATITUDE_REF);
|
||||
latitudeRef = exif.getAttribute(ExifInterface.TAG_GPS_LATITUDE_REF);
|
||||
longitude = exif.getAttribute(ExifInterface.TAG_GPS_LONGITUDE);
|
||||
longitude_ref = exif.getAttribute(ExifInterface.TAG_GPS_LONGITUDE_REF);
|
||||
longitudeRef = exif.getAttribute(ExifInterface.TAG_GPS_LONGITUDE_REF);
|
||||
|
||||
if (latitude!=null && latitude_ref!=null && longitude!=null && longitude_ref!=null) {
|
||||
Timber.d("Latitude: %s %s", latitude, latitude_ref);
|
||||
Timber.d("Longitude: %s %s", longitude, longitude_ref);
|
||||
if (latitude!=null && latitudeRef!=null && longitude!=null && longitudeRef!=null) {
|
||||
Timber.d("Latitude: %s %s", latitude, latitudeRef);
|
||||
Timber.d("Longitude: %s %s", longitude, longitudeRef);
|
||||
|
||||
decimalCoords = getDecimalCoords(latitude, latitude_ref, longitude, longitude_ref);
|
||||
decimalCoords = getDecimalCoords(latitude, latitudeRef, longitude, longitudeRef);
|
||||
return decimalCoords;
|
||||
} else {
|
||||
return null;
|
||||
|
|
|
|||
|
|
@ -51,11 +51,17 @@ public class MultipleShareActivity extends AuthenticatedActivity
|
|||
MultipleUploadListFragment.OnMultipleUploadInitiatedHandler,
|
||||
OnCategoriesSaveHandler {
|
||||
|
||||
@Inject MediaWikiApi mwApi;
|
||||
@Inject SessionManager sessionManager;
|
||||
@Inject UploadController uploadController;
|
||||
@Inject ModifierSequenceDao modifierSequenceDao;
|
||||
@Inject @Named("default_preferences") SharedPreferences prefs;
|
||||
@Inject
|
||||
MediaWikiApi mwApi;
|
||||
@Inject
|
||||
SessionManager sessionManager;
|
||||
@Inject
|
||||
UploadController uploadController;
|
||||
@Inject
|
||||
ModifierSequenceDao modifierSequenceDao;
|
||||
@Inject
|
||||
@Named("default_preferences")
|
||||
SharedPreferences prefs;
|
||||
|
||||
private ArrayList<Contribution> photosList = null;
|
||||
|
||||
|
|
@ -240,7 +246,7 @@ public class MultipleShareActivity extends AuthenticatedActivity
|
|||
mwApi.setAuthCookie(authCookie);
|
||||
Intent intent = getIntent();
|
||||
|
||||
if (intent.getAction().equals(Intent.ACTION_SEND_MULTIPLE)) {
|
||||
if (Intent.ACTION_SEND_MULTIPLE.equals(intent.getAction())) {
|
||||
if (photosList == null) {
|
||||
photosList = new ArrayList<>();
|
||||
ArrayList<Uri> urisList = intent.getParcelableArrayListExtra(Intent.EXTRA_STREAM);
|
||||
|
|
@ -278,7 +284,7 @@ public class MultipleShareActivity extends AuthenticatedActivity
|
|||
|
||||
@Override
|
||||
public void onBackStackChanged() {
|
||||
getSupportActionBar().setDisplayHomeAsUpEnabled(mediaDetails != null && mediaDetails.isVisible()) ;
|
||||
getSupportActionBar().setDisplayHomeAsUpEnabled(mediaDetails != null && mediaDetails.isVisible());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -16,6 +16,7 @@ import android.support.annotation.RequiresApi;
|
|||
import android.support.design.widget.Snackbar;
|
||||
import android.support.graphics.drawable.VectorDrawableCompat;
|
||||
import android.support.v4.app.ActivityCompat;
|
||||
import android.support.v4.app.FragmentManager;
|
||||
import android.support.v4.content.ContextCompat;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
|
|
@ -61,10 +62,10 @@ import static fr.free.nrw.commons.upload.ExistingFileAsync.Result.NO_DUPLICATE;
|
|||
* Activity for the title/desc screen after image is selected. Also starts processing image
|
||||
* GPS coordinates or user location (if enabled in Settings) for category suggestions.
|
||||
*/
|
||||
public class ShareActivity
|
||||
extends AuthenticatedActivity
|
||||
public class ShareActivity
|
||||
extends AuthenticatedActivity
|
||||
implements SingleUploadFragment.OnUploadActionInitiated,
|
||||
OnCategoriesSaveHandler {
|
||||
OnCategoriesSaveHandler,SimilarImageDialogFragment.onResponse {
|
||||
|
||||
private static final int REQUEST_PERM_ON_CREATE_STORAGE = 1;
|
||||
private static final int REQUEST_PERM_ON_CREATE_LOCATION = 2;
|
||||
|
|
@ -72,12 +73,19 @@ public class ShareActivity
|
|||
private static final int REQUEST_PERM_ON_SUBMIT_STORAGE = 4;
|
||||
private CategorizationFragment categorizationFragment;
|
||||
|
||||
@Inject MediaWikiApi mwApi;
|
||||
@Inject CacheController cacheController;
|
||||
@Inject SessionManager sessionManager;
|
||||
@Inject UploadController uploadController;
|
||||
@Inject ModifierSequenceDao modifierSequenceDao;
|
||||
@Inject @Named("default_preferences") SharedPreferences prefs;
|
||||
@Inject
|
||||
MediaWikiApi mwApi;
|
||||
@Inject
|
||||
CacheController cacheController;
|
||||
@Inject
|
||||
SessionManager sessionManager;
|
||||
@Inject
|
||||
UploadController uploadController;
|
||||
@Inject
|
||||
ModifierSequenceDao modifierSequenceDao;
|
||||
@Inject
|
||||
@Named("default_preferences")
|
||||
SharedPreferences prefs;
|
||||
|
||||
private String source;
|
||||
private String mimeType;
|
||||
|
|
@ -89,6 +97,7 @@ public class ShareActivity
|
|||
private boolean cacheFound;
|
||||
|
||||
private GPSExtractor imageObj;
|
||||
private GPSExtractor tempImageObj;
|
||||
private String decimalCoords;
|
||||
|
||||
private boolean useNewPermissions = false;
|
||||
|
|
@ -99,7 +108,7 @@ public class ShareActivity
|
|||
private String description;
|
||||
private Snackbar snackbar;
|
||||
private boolean duplicateCheckPassed = false;
|
||||
|
||||
private boolean haveCheckedForOtherImages = false;
|
||||
/**
|
||||
* Called when user taps the submit button.
|
||||
*/
|
||||
|
|
@ -216,7 +225,7 @@ public class ShareActivity
|
|||
//Receive intent from ContributionController.java when user selects picture to upload
|
||||
Intent intent = getIntent();
|
||||
|
||||
if (intent.getAction().equals(Intent.ACTION_SEND)) {
|
||||
if (Intent.ACTION_SEND.equals(intent.getAction())) {
|
||||
mediaUri = intent.getParcelableExtra(Intent.EXTRA_STREAM);
|
||||
if (intent.hasExtra(UploadService.EXTRA_SOURCE)) {
|
||||
source = intent.getStringExtra(UploadService.EXTRA_SOURCE);
|
||||
|
|
@ -452,13 +461,93 @@ public class ShareActivity
|
|||
if (imageObj != null) {
|
||||
// Gets image coords from exif data or user location
|
||||
decimalCoords = imageObj.getCoords(gpsEnabled);
|
||||
useImageCoords();
|
||||
if(decimalCoords==null || !imageObj.imageCoordsExists){
|
||||
// Check if the location is from GPS or EXIF
|
||||
// Find other photos taken around the same time which has gps coordinates
|
||||
Timber.d("EXIF:false");
|
||||
Timber.d("EXIF call"+(imageObj==tempImageObj));
|
||||
if(!haveCheckedForOtherImages)
|
||||
findOtherImages(gpsEnabled);// Do not do repeat the process
|
||||
}
|
||||
else {
|
||||
// As the selected image has GPS data in EXIF go ahead with the same.
|
||||
useImageCoords();
|
||||
}
|
||||
}
|
||||
} catch (FileNotFoundException e) {
|
||||
Timber.w("File not found: " + mediaUri, e);
|
||||
}
|
||||
}
|
||||
|
||||
private void findOtherImages(boolean gpsEnabled) {
|
||||
Timber.d("filePath"+getPathOfMediaOrCopy());
|
||||
String filePath = getPathOfMediaOrCopy();
|
||||
long timeOfCreation = new File(filePath).lastModified();//Time when the original image was created
|
||||
File folder = new File(filePath.substring(0,filePath.lastIndexOf('/')));
|
||||
File[] files = folder.listFiles();
|
||||
Timber.d("folderTime Number:"+files.length);
|
||||
|
||||
for(File file : files){
|
||||
if(file.lastModified()-timeOfCreation<=(120*1000) && file.lastModified()-timeOfCreation>=-(120*1000)){
|
||||
//Make sure the photos were taken within 20seconds
|
||||
Timber.d("fild date:"+file.lastModified()+ " time of creation"+timeOfCreation);
|
||||
tempImageObj = null;//Temporary GPSExtractor to extract coords from these photos
|
||||
ParcelFileDescriptor descriptor
|
||||
= null;
|
||||
try {
|
||||
descriptor = getContentResolver().openFileDescriptor(Uri.parse(file.getAbsolutePath()), "r");
|
||||
} catch (FileNotFoundException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
||||
if (descriptor != null) {
|
||||
tempImageObj = new GPSExtractor(descriptor.getFileDescriptor(),this, prefs);
|
||||
}
|
||||
} else {
|
||||
if (filePath != null) {
|
||||
tempImageObj = new GPSExtractor(file.getAbsolutePath(), this, prefs);
|
||||
}
|
||||
}
|
||||
|
||||
if(tempImageObj!=null){
|
||||
Timber.d("not null fild EXIF"+tempImageObj.imageCoordsExists +" coords"+tempImageObj.getCoords(gpsEnabled));
|
||||
if(tempImageObj.getCoords(gpsEnabled)!=null && tempImageObj.imageCoordsExists){
|
||||
// Current image has gps coordinates and it's not current gps locaiton
|
||||
Timber.d("This fild has image coords:"+ file.getAbsolutePath());
|
||||
// Create a dialog fragment for the suggestion
|
||||
FragmentManager fragmentManager = getSupportFragmentManager();
|
||||
SimilarImageDialogFragment newFragment = new SimilarImageDialogFragment();
|
||||
Bundle args = new Bundle();
|
||||
args.putString("originalImagePath",filePath);
|
||||
args.putString("possibleImagePath",file.getAbsolutePath());
|
||||
newFragment.setArguments(args);
|
||||
newFragment.show(fragmentManager, "dialog");
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
haveCheckedForOtherImages = true; //Finished checking for other images
|
||||
return;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPostiveResponse() {
|
||||
imageObj = tempImageObj;
|
||||
decimalCoords = imageObj.getCoords(false);// Not necessary to use gps as image already ha EXIF data
|
||||
Timber.d("EXIF from tempImageObj");
|
||||
useImageCoords();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNegativeResponse() {
|
||||
Timber.d("EXIF from imageObj");
|
||||
useImageCoords();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Initiates retrieval of image coordinates or user coordinates, and caching of coordinates.
|
||||
* Then initiates the calls to MediaWiki API through an instance of MwVolleyApi.
|
||||
|
|
@ -466,6 +555,7 @@ public class ShareActivity
|
|||
public void useImageCoords() {
|
||||
if (decimalCoords != null) {
|
||||
Timber.d("Decimal coords of image: %s", decimalCoords);
|
||||
Timber.d("is EXIF data present:"+imageObj.imageCoordsExists+" from findOther image:"+(imageObj==tempImageObj));
|
||||
|
||||
// Only set cache for this point if image has coords
|
||||
if (imageObj.imageCoordsExists) {
|
||||
|
|
@ -489,7 +579,10 @@ public class ShareActivity
|
|||
Timber.d("Cache found, setting categoryList in MwVolleyApi to %s", displayCatList);
|
||||
MwVolleyApi.setGpsCat(displayCatList);
|
||||
}
|
||||
}else{
|
||||
Timber.d("EXIF: no coords");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -0,0 +1,112 @@
|
|||
package fr.free.nrw.commons.upload;
|
||||
|
||||
import android.app.Dialog;
|
||||
import android.content.DialogInterface;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.graphics.drawable.VectorDrawableCompat;
|
||||
import android.support.v4.app.DialogFragment;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.Window;
|
||||
import android.widget.Button;
|
||||
|
||||
import com.facebook.drawee.generic.GenericDraweeHierarchyBuilder;
|
||||
import com.facebook.drawee.view.SimpleDraweeView;
|
||||
import com.facebook.imagepipeline.listener.RequestListener;
|
||||
import com.facebook.imagepipeline.listener.RequestLoggingListener;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import fr.free.nrw.commons.R;
|
||||
|
||||
/**
|
||||
* Created by harisanker on 14/2/18.
|
||||
*/
|
||||
|
||||
public class SimilarImageDialogFragment extends DialogFragment {
|
||||
SimpleDraweeView originalImage;
|
||||
SimpleDraweeView possibleImage;
|
||||
Button positiveButton;
|
||||
Button negativeButton;
|
||||
onResponse mOnResponse;//Implemented interface from shareActivity
|
||||
Boolean gotResponse = false;
|
||||
public SimilarImageDialogFragment() {
|
||||
}
|
||||
public interface onResponse{
|
||||
public void onPostiveResponse();
|
||||
public void onNegativeResponse();
|
||||
}
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
|
||||
View view = inflater.inflate(R.layout.fragment_similar_image_dialog, container, false);
|
||||
Set<RequestListener> requestListeners = new HashSet<>();
|
||||
requestListeners.add(new RequestLoggingListener());
|
||||
|
||||
originalImage =(SimpleDraweeView) view.findViewById(R.id.orginalImage);
|
||||
possibleImage =(SimpleDraweeView) view.findViewById(R.id.possibleImage);
|
||||
positiveButton = (Button) view.findViewById(R.id.postive_button);
|
||||
negativeButton = (Button) view.findViewById(R.id.negative_button);
|
||||
|
||||
originalImage.setHierarchy(GenericDraweeHierarchyBuilder
|
||||
.newInstance(getResources())
|
||||
.setPlaceholderImage(VectorDrawableCompat.create(getResources(),
|
||||
R.drawable.ic_image_black_24dp,getContext().getTheme()))
|
||||
.setFailureImage(VectorDrawableCompat.create(getResources(),
|
||||
R.drawable.ic_error_outline_black_24dp, getContext().getTheme()))
|
||||
.build());
|
||||
possibleImage.setHierarchy(GenericDraweeHierarchyBuilder
|
||||
.newInstance(getResources())
|
||||
.setPlaceholderImage(VectorDrawableCompat.create(getResources(),
|
||||
R.drawable.ic_image_black_24dp,getContext().getTheme()))
|
||||
.setFailureImage(VectorDrawableCompat.create(getResources(),
|
||||
R.drawable.ic_error_outline_black_24dp, getContext().getTheme()))
|
||||
.build());
|
||||
|
||||
originalImage.setImageURI(Uri.fromFile(new File(getArguments().getString("originalImagePath"))));
|
||||
possibleImage.setImageURI(Uri.fromFile(new File(getArguments().getString("possibleImagePath"))));
|
||||
|
||||
negativeButton.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
mOnResponse.onNegativeResponse();
|
||||
gotResponse = true;
|
||||
dismiss();
|
||||
}
|
||||
});
|
||||
positiveButton.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
mOnResponse.onPostiveResponse();
|
||||
gotResponse = true;
|
||||
dismiss();
|
||||
}
|
||||
});
|
||||
return view;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate(@Nullable Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
mOnResponse = (onResponse) getActivity();//Interface Implementation
|
||||
}
|
||||
|
||||
@Override
|
||||
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
||||
Dialog dialog = super.onCreateDialog(savedInstanceState);
|
||||
dialog.requestWindowFeature(Window.FEATURE_NO_TITLE);
|
||||
return dialog;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDismiss(DialogInterface dialog) {
|
||||
// I user dismisses dialog by pressing outside the dialog.
|
||||
if(!gotResponse)
|
||||
mOnResponse.onNegativeResponse();
|
||||
super.onDismiss(dialog);
|
||||
}
|
||||
}
|
||||
|
|
@ -6,6 +6,7 @@ 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;
|
||||
|
|
@ -49,7 +50,7 @@ public class UploadController {
|
|||
private ServiceConnection uploadServiceConnection = new ServiceConnection() {
|
||||
@Override
|
||||
public void onServiceConnected(ComponentName componentName, IBinder binder) {
|
||||
uploadService = (UploadService) ((HandlerService.HandlerServiceLocalBinder)binder).getService();
|
||||
uploadService = (UploadService) ((HandlerService.HandlerServiceLocalBinder) binder).getService();
|
||||
isUploadServiceConnected = true;
|
||||
}
|
||||
|
||||
|
|
@ -81,13 +82,14 @@ public class UploadController {
|
|||
|
||||
/**
|
||||
* Starts a new upload task.
|
||||
* @param title the title of the contribution
|
||||
* @param mediaUri the media URI of the contribution
|
||||
* @param description the description of the contribution
|
||||
* @param mimeType the MIME type of the contribution
|
||||
* @param source the source of the contribution
|
||||
*
|
||||
* @param title the title of the contribution
|
||||
* @param mediaUri the media URI of the contribution
|
||||
* @param description the description of the contribution
|
||||
* @param mimeType the MIME type of the contribution
|
||||
* @param source the source of the contribution
|
||||
* @param decimalCoords the coordinates in decimal. (e.g. "37.51136|-77.602615")
|
||||
* @param onComplete the progress tracker
|
||||
* @param onComplete the progress tracker
|
||||
*/
|
||||
public void startUpload(String title, Uri mediaUri, String description, String mimeType, String source, String decimalCoords, ContributionUploadProgress onComplete) {
|
||||
Contribution contribution;
|
||||
|
|
@ -106,8 +108,9 @@ public class UploadController {
|
|||
|
||||
/**
|
||||
* Starts a new upload task.
|
||||
*
|
||||
* @param contribution the contribution object
|
||||
* @param onComplete the progress tracker
|
||||
* @param onComplete the progress tracker
|
||||
*/
|
||||
public void startUpload(final Contribution contribution, final ContributionUploadProgress onComplete) {
|
||||
//Set creator, desc, and license
|
||||
|
|
@ -134,15 +137,17 @@ public class UploadController {
|
|||
ContentResolver contentResolver = context.getContentResolver();
|
||||
try {
|
||||
if (contribution.getDataLength() <= 0) {
|
||||
length = contentResolver
|
||||
.openAssetFileDescriptor(contribution.getLocalUri(), "r")
|
||||
.getLength();
|
||||
if (length == -1) {
|
||||
// Let us find out the long way!
|
||||
length = countBytes(contentResolver
|
||||
.openInputStream(contribution.getLocalUri()));
|
||||
AssetFileDescriptor assetFileDescriptor = contentResolver
|
||||
.openAssetFileDescriptor(contribution.getLocalUri(), "r");
|
||||
if (assetFileDescriptor != null) {
|
||||
length = assetFileDescriptor.getLength();
|
||||
if (length == -1) {
|
||||
// Let us find out the long way!
|
||||
length = countBytes(contentResolver
|
||||
.openInputStream(contribution.getLocalUri()));
|
||||
}
|
||||
contribution.setDataLength(length);
|
||||
}
|
||||
contribution.setDataLength(length);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
Timber.e(e, "IO Exception: ");
|
||||
|
|
@ -152,7 +157,7 @@ public class UploadController {
|
|||
Timber.e(e, "Security Exception: ");
|
||||
}
|
||||
|
||||
String mimeType = (String)contribution.getTag("mimeType");
|
||||
String mimeType = (String) contribution.getTag("mimeType");
|
||||
Boolean imagePrefix = false;
|
||||
|
||||
if (mimeType == null || TextUtils.isEmpty(mimeType) || mimeType.endsWith("*")) {
|
||||
|
|
@ -199,6 +204,7 @@ public class UploadController {
|
|||
|
||||
/**
|
||||
* Counts the number of bytes in {@code stream}.
|
||||
*
|
||||
* @param stream the stream
|
||||
* @return the number of bytes in {@code stream}
|
||||
* @throws IOException if an I/O error occurs
|
||||
|
|
|
|||
|
|
@ -161,7 +161,7 @@ public class UploadService extends HandlerService<Contribution> {
|
|||
|
||||
@Override
|
||||
public int onStartCommand(Intent intent, int flags, int startId) {
|
||||
if (intent.getAction().equals(ACTION_START_SERVICE) && freshStart) {
|
||||
if (ACTION_START_SERVICE.equals(intent.getAction()) && freshStart) {
|
||||
ContentValues failedValues = new ContentValues();
|
||||
failedValues.put(ContributionDao.Table.COLUMN_STATE, Contribution.STATE_FAILED);
|
||||
|
||||
|
|
|
|||
|
|
@ -15,6 +15,8 @@ public class ExecutorUtils {
|
|||
}
|
||||
};
|
||||
|
||||
public static Executor uiExecutor() { return uiExecutor; }
|
||||
public static Executor uiExecutor() {
|
||||
return uiExecutor;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,7 +11,6 @@ import java.io.InputStream;
|
|||
import java.io.InputStreamReader;
|
||||
import java.io.OutputStreamWriter;
|
||||
|
||||
import fr.free.nrw.commons.CommonsApplication;
|
||||
import timber.log.Timber;
|
||||
|
||||
public class FileUtils {
|
||||
|
|
@ -32,7 +31,7 @@ public class FileUtils {
|
|||
reader = new BufferedReader(new InputStreamReader(inputStream, "UTF-8"));
|
||||
String line;
|
||||
while ((line = reader.readLine()) != null) {
|
||||
buffer.append(line + "\n");
|
||||
buffer.append(line).append("\n");
|
||||
}
|
||||
} finally {
|
||||
if (reader != null) {
|
||||
|
|
|
|||
|
|
@ -7,11 +7,6 @@ import android.widget.Toast;
|
|||
public class ViewUtil {
|
||||
|
||||
public static void showLongToast(final Context context, @StringRes final int stringResId) {
|
||||
ExecutorUtils.uiExecutor().execute(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
Toast.makeText(context, context.getString(stringResId), Toast.LENGTH_LONG).show();
|
||||
}
|
||||
});
|
||||
ExecutorUtils.uiExecutor().execute(() -> Toast.makeText(context, context.getString(stringResId), Toast.LENGTH_LONG).show());
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue