mirror of
https://github.com/commons-app/apps-android-commons.git
synced 2025-10-29 22:03:55 +01:00
Merge branch 'main' into psh/convert-profile-to-kotlin
This commit is contained in:
commit
0d114db5cd
65 changed files with 2859 additions and 3162 deletions
|
|
@ -1,77 +0,0 @@
|
|||
package fr.free.nrw.commons.LocationPicker;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Intent;
|
||||
import fr.free.nrw.commons.CameraPosition;
|
||||
import fr.free.nrw.commons.Media;
|
||||
|
||||
/**
|
||||
* Helper class for starting the activity
|
||||
*/
|
||||
public final class LocationPicker {
|
||||
|
||||
/**
|
||||
* Getting camera position from the intent using constants
|
||||
*
|
||||
* @param data intent
|
||||
* @return CameraPosition
|
||||
*/
|
||||
public static CameraPosition getCameraPosition(final Intent data) {
|
||||
return data.getParcelableExtra(LocationPickerConstants.MAP_CAMERA_POSITION);
|
||||
}
|
||||
|
||||
public static class IntentBuilder {
|
||||
|
||||
private final Intent intent;
|
||||
|
||||
/**
|
||||
* Creates a new builder that creates an intent to launch the place picker activity.
|
||||
*/
|
||||
public IntentBuilder() {
|
||||
intent = new Intent();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets and puts location in intent
|
||||
* @param position CameraPosition
|
||||
* @return LocationPicker.IntentBuilder
|
||||
*/
|
||||
public LocationPicker.IntentBuilder defaultLocation(
|
||||
final CameraPosition position) {
|
||||
intent.putExtra(LocationPickerConstants.MAP_CAMERA_POSITION, position);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets and puts activity name in intent
|
||||
* @param activity activity key
|
||||
* @return LocationPicker.IntentBuilder
|
||||
*/
|
||||
public LocationPicker.IntentBuilder activityKey(
|
||||
final String activity) {
|
||||
intent.putExtra(LocationPickerConstants.ACTIVITY_KEY, activity);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets and puts media in intent
|
||||
* @param media Media
|
||||
* @return LocationPicker.IntentBuilder
|
||||
*/
|
||||
public LocationPicker.IntentBuilder media(
|
||||
final Media media) {
|
||||
intent.putExtra(LocationPickerConstants.MEDIA, media);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets and sets the activity
|
||||
* @param activity Activity
|
||||
* @return Intent
|
||||
*/
|
||||
public Intent build(final Activity activity) {
|
||||
intent.setClass(activity, LocationPickerActivity.class);
|
||||
return intent;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,72 @@
|
|||
package fr.free.nrw.commons.LocationPicker
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.Intent
|
||||
import fr.free.nrw.commons.CameraPosition
|
||||
import fr.free.nrw.commons.Media
|
||||
|
||||
|
||||
/**
|
||||
* Helper class for starting the activity
|
||||
*/
|
||||
object LocationPicker {
|
||||
|
||||
/**
|
||||
* Getting camera position from the intent using constants
|
||||
*
|
||||
* @param data intent
|
||||
* @return CameraPosition
|
||||
*/
|
||||
@JvmStatic
|
||||
fun getCameraPosition(data: Intent): CameraPosition? {
|
||||
return data.getParcelableExtra(LocationPickerConstants.MAP_CAMERA_POSITION)
|
||||
}
|
||||
|
||||
class IntentBuilder
|
||||
/**
|
||||
* Creates a new builder that creates an intent to launch the place picker activity.
|
||||
*/() {
|
||||
|
||||
private val intent: Intent = Intent()
|
||||
|
||||
/**
|
||||
* Gets and puts location in intent
|
||||
* @param position CameraPosition
|
||||
* @return LocationPicker.IntentBuilder
|
||||
*/
|
||||
fun defaultLocation(position: CameraPosition): IntentBuilder {
|
||||
intent.putExtra(LocationPickerConstants.MAP_CAMERA_POSITION, position)
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets and puts activity name in intent
|
||||
* @param activity activity key
|
||||
* @return LocationPicker.IntentBuilder
|
||||
*/
|
||||
fun activityKey(activity: String): IntentBuilder {
|
||||
intent.putExtra(LocationPickerConstants.ACTIVITY_KEY, activity)
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets and puts media in intent
|
||||
* @param media Media
|
||||
* @return LocationPicker.IntentBuilder
|
||||
*/
|
||||
fun media(media: Media): IntentBuilder {
|
||||
intent.putExtra(LocationPickerConstants.MEDIA, media)
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets and sets the activity
|
||||
* @param activity Activity
|
||||
* @return Intent
|
||||
*/
|
||||
fun build(activity: Activity): Intent {
|
||||
intent.setClass(activity, LocationPickerActivity::class.java)
|
||||
return intent
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,681 +0,0 @@
|
|||
package fr.free.nrw.commons.LocationPicker;
|
||||
|
||||
import static fr.free.nrw.commons.upload.mediaDetails.UploadMediaDetailFragment.LAST_LOCATION;
|
||||
import static fr.free.nrw.commons.upload.mediaDetails.UploadMediaDetailFragment.LAST_ZOOM;
|
||||
import static fr.free.nrw.commons.utils.MapUtils.ZOOM_LEVEL;
|
||||
|
||||
import android.Manifest.permission;
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.location.LocationManager;
|
||||
import android.os.Bundle;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.text.Html;
|
||||
import android.text.method.LinkMovementMethod;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
import android.view.Window;
|
||||
import android.view.animation.OvershootInterpolator;
|
||||
import android.widget.Button;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.appcompat.app.ActionBar;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.appcompat.widget.AppCompatTextView;
|
||||
import androidx.constraintlayout.widget.ConstraintLayout;
|
||||
import androidx.core.app.ActivityCompat;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import com.google.android.material.floatingactionbutton.FloatingActionButton;
|
||||
import fr.free.nrw.commons.CameraPosition;
|
||||
import fr.free.nrw.commons.CommonsApplication;
|
||||
import fr.free.nrw.commons.Media;
|
||||
import fr.free.nrw.commons.R;
|
||||
import fr.free.nrw.commons.Utils;
|
||||
import fr.free.nrw.commons.auth.SessionManager;
|
||||
import fr.free.nrw.commons.auth.csrf.CsrfTokenClient;
|
||||
import fr.free.nrw.commons.coordinates.CoordinateEditHelper;
|
||||
import fr.free.nrw.commons.filepicker.Constants;
|
||||
import fr.free.nrw.commons.kvstore.BasicKvStore;
|
||||
import fr.free.nrw.commons.kvstore.JsonKvStore;
|
||||
import fr.free.nrw.commons.location.LocationPermissionsHelper;
|
||||
import fr.free.nrw.commons.location.LocationPermissionsHelper.LocationPermissionCallback;
|
||||
import fr.free.nrw.commons.location.LocationServiceManager;
|
||||
import fr.free.nrw.commons.theme.BaseActivity;
|
||||
import fr.free.nrw.commons.utils.DialogUtil;
|
||||
import fr.free.nrw.commons.utils.SystemThemeUtils;
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers;
|
||||
import io.reactivex.schedulers.Schedulers;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Named;
|
||||
import org.osmdroid.tileprovider.tilesource.TileSourceFactory;
|
||||
import org.osmdroid.util.GeoPoint;
|
||||
import org.osmdroid.util.constants.GeoConstants;
|
||||
import org.osmdroid.views.CustomZoomButtonsController;
|
||||
import org.osmdroid.views.overlay.Marker;
|
||||
import org.osmdroid.views.overlay.Overlay;
|
||||
import org.osmdroid.views.overlay.ScaleDiskOverlay;
|
||||
import org.osmdroid.views.overlay.TilesOverlay;
|
||||
import timber.log.Timber;
|
||||
|
||||
/**
|
||||
* Helps to pick location and return the result with an intent
|
||||
*/
|
||||
public class LocationPickerActivity extends BaseActivity implements
|
||||
LocationPermissionCallback {
|
||||
/**
|
||||
* coordinateEditHelper: helps to edit coordinates
|
||||
*/
|
||||
@Inject
|
||||
CoordinateEditHelper coordinateEditHelper;
|
||||
/**
|
||||
* media : Media object
|
||||
*/
|
||||
private Media media;
|
||||
/**
|
||||
* cameraPosition : position of picker
|
||||
*/
|
||||
private CameraPosition cameraPosition;
|
||||
/**
|
||||
* markerImage : picker image
|
||||
*/
|
||||
private ImageView markerImage;
|
||||
/**
|
||||
* mapView : OSM Map
|
||||
*/
|
||||
private org.osmdroid.views.MapView mapView;
|
||||
/**
|
||||
* tvAttribution : credit
|
||||
*/
|
||||
private AppCompatTextView tvAttribution;
|
||||
/**
|
||||
* activity : activity key
|
||||
*/
|
||||
private String activity;
|
||||
/**
|
||||
* modifyLocationButton : button for start editing location
|
||||
*/
|
||||
Button modifyLocationButton;
|
||||
/**
|
||||
* removeLocationButton : button to remove location metadata
|
||||
*/
|
||||
Button removeLocationButton;
|
||||
/**
|
||||
* showInMapButton : button for showing in map
|
||||
*/
|
||||
TextView showInMapButton;
|
||||
/**
|
||||
* placeSelectedButton : fab for selecting location
|
||||
*/
|
||||
FloatingActionButton placeSelectedButton;
|
||||
/**
|
||||
* fabCenterOnLocation: button for center on location;
|
||||
*/
|
||||
FloatingActionButton fabCenterOnLocation;
|
||||
/**
|
||||
* shadow : imageview of shadow
|
||||
*/
|
||||
private ImageView shadow;
|
||||
/**
|
||||
* largeToolbarText : textView of shadow
|
||||
*/
|
||||
private TextView largeToolbarText;
|
||||
/**
|
||||
* smallToolbarText : textView of shadow
|
||||
*/
|
||||
private TextView smallToolbarText;
|
||||
/**
|
||||
* applicationKvStore : for storing values
|
||||
*/
|
||||
@Inject
|
||||
@Named("default_preferences")
|
||||
public
|
||||
JsonKvStore applicationKvStore;
|
||||
BasicKvStore store;
|
||||
/**
|
||||
* isDarkTheme: for keeping a track of the device theme and modifying the map theme accordingly
|
||||
*/
|
||||
@Inject
|
||||
SystemThemeUtils systemThemeUtils;
|
||||
private boolean isDarkTheme;
|
||||
private boolean moveToCurrentLocation;
|
||||
|
||||
@Inject
|
||||
LocationServiceManager locationManager;
|
||||
LocationPermissionsHelper locationPermissionsHelper;
|
||||
|
||||
@Inject
|
||||
SessionManager sessionManager;
|
||||
|
||||
/**
|
||||
* Constants
|
||||
*/
|
||||
private static final String CAMERA_POS = "cameraPosition";
|
||||
private static final String ACTIVITY = "activity";
|
||||
|
||||
|
||||
@SuppressLint("ClickableViewAccessibility")
|
||||
@Override
|
||||
protected void onCreate(@Nullable final Bundle savedInstanceState) {
|
||||
getWindow().requestFeature(Window.FEATURE_ACTION_BAR);
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
isDarkTheme = systemThemeUtils.isDeviceInNightMode();
|
||||
moveToCurrentLocation = false;
|
||||
store = new BasicKvStore(this, "LocationPermissions");
|
||||
|
||||
getWindow().requestFeature(Window.FEATURE_ACTION_BAR);
|
||||
final ActionBar actionBar = getSupportActionBar();
|
||||
if (actionBar != null) {
|
||||
actionBar.hide();
|
||||
}
|
||||
setContentView(R.layout.activity_location_picker);
|
||||
|
||||
if (savedInstanceState == null) {
|
||||
cameraPosition = getIntent()
|
||||
.getParcelableExtra(LocationPickerConstants.MAP_CAMERA_POSITION);
|
||||
activity = getIntent().getStringExtra(LocationPickerConstants.ACTIVITY_KEY);
|
||||
media = getIntent().getParcelableExtra(LocationPickerConstants.MEDIA);
|
||||
}else{
|
||||
cameraPosition = savedInstanceState.getParcelable(CAMERA_POS);
|
||||
activity = savedInstanceState.getString(ACTIVITY);
|
||||
media = savedInstanceState.getParcelable("sMedia");
|
||||
}
|
||||
bindViews();
|
||||
addBackButtonListener();
|
||||
addPlaceSelectedButton();
|
||||
addCredits();
|
||||
getToolbarUI();
|
||||
addCenterOnGPSButton();
|
||||
|
||||
org.osmdroid.config.Configuration.getInstance().load(getApplicationContext(),
|
||||
PreferenceManager.getDefaultSharedPreferences(getApplicationContext()));
|
||||
|
||||
mapView.setTileSource(TileSourceFactory.WIKIMEDIA);
|
||||
mapView.setTilesScaledToDpi(true);
|
||||
mapView.setMultiTouchControls(true);
|
||||
|
||||
org.osmdroid.config.Configuration.getInstance().getAdditionalHttpRequestProperties().put(
|
||||
"Referer", "http://maps.wikimedia.org/"
|
||||
);
|
||||
mapView.getZoomController().setVisibility(CustomZoomButtonsController.Visibility.NEVER);
|
||||
mapView.getController().setZoom(ZOOM_LEVEL);
|
||||
mapView.setOnTouchListener((v, event) -> {
|
||||
if (event.getAction() == MotionEvent.ACTION_MOVE) {
|
||||
if (markerImage.getTranslationY() == 0) {
|
||||
markerImage.animate().translationY(-75)
|
||||
.setInterpolator(new OvershootInterpolator()).setDuration(250).start();
|
||||
}
|
||||
} else if (event.getAction() == MotionEvent.ACTION_UP) {
|
||||
markerImage.animate().translationY(0)
|
||||
.setInterpolator(new OvershootInterpolator()).setDuration(250).start();
|
||||
}
|
||||
return false;
|
||||
});
|
||||
|
||||
if ("UploadActivity".equals(activity)) {
|
||||
placeSelectedButton.setVisibility(View.GONE);
|
||||
modifyLocationButton.setVisibility(View.VISIBLE);
|
||||
removeLocationButton.setVisibility(View.VISIBLE);
|
||||
showInMapButton.setVisibility(View.VISIBLE);
|
||||
largeToolbarText.setText(getResources().getString(R.string.image_location));
|
||||
smallToolbarText.setText(getResources().
|
||||
getString(R.string.check_whether_location_is_correct));
|
||||
fabCenterOnLocation.setVisibility(View.GONE);
|
||||
markerImage.setVisibility(View.GONE);
|
||||
shadow.setVisibility(View.GONE);
|
||||
assert cameraPosition != null;
|
||||
showSelectedLocationMarker(new GeoPoint(cameraPosition.getLatitude(),
|
||||
cameraPosition.getLongitude()));
|
||||
}
|
||||
setupMapView();
|
||||
}
|
||||
|
||||
/**
|
||||
* Moves the center of the map to the specified coordinates
|
||||
*
|
||||
*/
|
||||
private void moveMapTo(double latitude, double longitude){
|
||||
if(mapView != null && mapView.getController() != null){
|
||||
GeoPoint point = new GeoPoint(latitude, longitude);
|
||||
|
||||
mapView.getController().setCenter(point);
|
||||
mapView.getController().animateTo(point);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Moves the center of the map to the specified coordinates
|
||||
* @param point The GeoPoint object which contains the coordinates to move to
|
||||
*/
|
||||
private void moveMapTo(GeoPoint point){
|
||||
if(point != null){
|
||||
moveMapTo(point.getLatitude(), point.getLongitude());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* For showing credits
|
||||
*/
|
||||
private void addCredits() {
|
||||
tvAttribution.setText(Html.fromHtml(getString(R.string.map_attribution)));
|
||||
tvAttribution.setMovementMethod(LinkMovementMethod.getInstance());
|
||||
}
|
||||
|
||||
/**
|
||||
* For setting up Dark Theme
|
||||
*/
|
||||
private void darkThemeSetup() {
|
||||
if (isDarkTheme) {
|
||||
shadow.setColorFilter(Color.argb(255, 255, 255, 255));
|
||||
mapView.getOverlayManager().getTilesOverlay()
|
||||
.setColorFilter(TilesOverlay.INVERT_COLORS);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Clicking back button destroy locationPickerActivity
|
||||
*/
|
||||
private void addBackButtonListener() {
|
||||
final ImageView backButton = findViewById(R.id.maplibre_place_picker_toolbar_back_button);
|
||||
backButton.setOnClickListener(v -> {
|
||||
finish();
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Binds mapView and location picker icon
|
||||
*/
|
||||
private void bindViews() {
|
||||
mapView = findViewById(R.id.map_view);
|
||||
markerImage = findViewById(R.id.location_picker_image_view_marker);
|
||||
tvAttribution = findViewById(R.id.tv_attribution);
|
||||
modifyLocationButton = findViewById(R.id.modify_location);
|
||||
removeLocationButton = findViewById(R.id.remove_location);
|
||||
showInMapButton = findViewById(R.id.show_in_map);
|
||||
showInMapButton.setText(getResources().getString(R.string.show_in_map_app).toUpperCase(
|
||||
Locale.ROOT));
|
||||
shadow = findViewById(R.id.location_picker_image_view_shadow);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets toolbar color
|
||||
*/
|
||||
private void getToolbarUI() {
|
||||
final ConstraintLayout toolbar = findViewById(R.id.location_picker_toolbar);
|
||||
largeToolbarText = findViewById(R.id.location_picker_toolbar_primary_text_view);
|
||||
smallToolbarText = findViewById(R.id.location_picker_toolbar_secondary_text_view);
|
||||
toolbar.setBackgroundColor(getResources().getColor(R.color.primaryColor));
|
||||
}
|
||||
|
||||
private void setupMapView() {
|
||||
requestLocationPermissions();
|
||||
|
||||
//If location metadata is available, move map to that location.
|
||||
if(activity.equals("UploadActivity") || activity.equals("MediaActivity")){
|
||||
moveMapToMediaLocation();
|
||||
} else {
|
||||
//If location metadata is not available, move map to device GPS location.
|
||||
moveMapToGPSLocation();
|
||||
}
|
||||
|
||||
modifyLocationButton.setOnClickListener(v -> onClickModifyLocation());
|
||||
removeLocationButton.setOnClickListener(v -> onClickRemoveLocation());
|
||||
showInMapButton.setOnClickListener(v -> showInMapApp());
|
||||
darkThemeSetup();
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles onclick event of modifyLocationButton
|
||||
*/
|
||||
private void onClickModifyLocation() {
|
||||
placeSelectedButton.setVisibility(View.VISIBLE);
|
||||
modifyLocationButton.setVisibility(View.GONE);
|
||||
removeLocationButton.setVisibility(View.GONE);
|
||||
showInMapButton.setVisibility(View.GONE);
|
||||
markerImage.setVisibility(View.VISIBLE);
|
||||
shadow.setVisibility(View.VISIBLE);
|
||||
largeToolbarText.setText(getResources().getString(R.string.choose_a_location));
|
||||
smallToolbarText.setText(getResources().getString(R.string.pan_and_zoom_to_adjust));
|
||||
fabCenterOnLocation.setVisibility(View.VISIBLE);
|
||||
removeSelectedLocationMarker();
|
||||
moveMapToMediaLocation();
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles onclick event of removeLocationButton
|
||||
*/
|
||||
private void onClickRemoveLocation() {
|
||||
DialogUtil.showAlertDialog(this,
|
||||
getString(R.string.remove_location_warning_title),
|
||||
getString(R.string.remove_location_warning_desc),
|
||||
getString(R.string.continue_message),
|
||||
getString(R.string.cancel), () -> removeLocationFromImage(), null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Method to remove the location from the picture
|
||||
*/
|
||||
private void removeLocationFromImage() {
|
||||
if (media != null) {
|
||||
getCompositeDisposable().add(coordinateEditHelper.makeCoordinatesEdit(getApplicationContext()
|
||||
, media, "0.0", "0.0", "0.0f")
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(s -> {
|
||||
Timber.d("Coordinates are removed from the image");
|
||||
}));
|
||||
}
|
||||
final Intent returningIntent = new Intent();
|
||||
setResult(AppCompatActivity.RESULT_OK, returningIntent);
|
||||
finish();
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the location in map app. Map will center on the location metadata, if available.
|
||||
* If there is no location metadata, the map will center on the commons app map center.
|
||||
*/
|
||||
private void showInMapApp() {
|
||||
fr.free.nrw.commons.location.LatLng position = null;
|
||||
|
||||
if(activity.equals("UploadActivity") && cameraPosition != null){
|
||||
//location metadata is available
|
||||
position = new fr.free.nrw.commons.location.LatLng(cameraPosition.getLatitude(),
|
||||
cameraPosition.getLongitude(), 0.0f);
|
||||
} else if(mapView != null){
|
||||
//location metadata is not available
|
||||
position = new fr.free.nrw.commons.location.LatLng(mapView.getMapCenter().getLatitude(),
|
||||
mapView.getMapCenter().getLongitude(), 0.0f);
|
||||
}
|
||||
|
||||
if(position != null){
|
||||
Utils.handleGeoCoordinates(this, position);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Moves the center of the map to the media's location, if that data
|
||||
* is available.
|
||||
*/
|
||||
private void moveMapToMediaLocation() {
|
||||
if (cameraPosition != null) {
|
||||
|
||||
GeoPoint point = new GeoPoint(cameraPosition.getLatitude(),
|
||||
cameraPosition.getLongitude());
|
||||
|
||||
moveMapTo(point);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Moves the center of the map to the device's GPS location, if that data is available.
|
||||
*/
|
||||
private void moveMapToGPSLocation(){
|
||||
if(locationManager != null){
|
||||
fr.free.nrw.commons.location.LatLng location = locationManager.getLastLocation();
|
||||
|
||||
if(location != null){
|
||||
GeoPoint point = new GeoPoint(location.getLatitude(), location.getLongitude());
|
||||
|
||||
moveMapTo(point);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Select the preferable location
|
||||
*/
|
||||
private void addPlaceSelectedButton() {
|
||||
placeSelectedButton = findViewById(R.id.location_chosen_button);
|
||||
placeSelectedButton.setOnClickListener(view -> placeSelected());
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the intent with required data
|
||||
*/
|
||||
void placeSelected() {
|
||||
if (activity.equals("NoLocationUploadActivity")) {
|
||||
applicationKvStore.putString(LAST_LOCATION,
|
||||
mapView.getMapCenter().getLatitude()
|
||||
+ ","
|
||||
+ mapView.getMapCenter().getLongitude());
|
||||
applicationKvStore.putString(LAST_ZOOM, mapView.getZoomLevel() + "");
|
||||
}
|
||||
|
||||
if (media == null) {
|
||||
final Intent returningIntent = new Intent();
|
||||
returningIntent.putExtra(LocationPickerConstants.MAP_CAMERA_POSITION,
|
||||
new CameraPosition(mapView.getMapCenter().getLatitude(),
|
||||
mapView.getMapCenter().getLongitude(), 14.0));
|
||||
setResult(AppCompatActivity.RESULT_OK, returningIntent);
|
||||
} else {
|
||||
updateCoordinates(String.valueOf(mapView.getMapCenter().getLatitude()),
|
||||
String.valueOf(mapView.getMapCenter().getLongitude()),
|
||||
String.valueOf(0.0f));
|
||||
}
|
||||
|
||||
finish();
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetched coordinates are replaced with existing coordinates by a POST API call.
|
||||
* @param Latitude to be added
|
||||
* @param Longitude to be added
|
||||
* @param Accuracy to be added
|
||||
*/
|
||||
public void updateCoordinates(final String Latitude, final String Longitude,
|
||||
final String Accuracy) {
|
||||
if (media == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
getCompositeDisposable().add(
|
||||
coordinateEditHelper.makeCoordinatesEdit(getApplicationContext(), media,
|
||||
Latitude, Longitude, Accuracy)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(s -> {
|
||||
Timber.d("Coordinates are added.");
|
||||
}));
|
||||
} catch (Exception e) {
|
||||
if (e.getLocalizedMessage().equals(CsrfTokenClient.ANONYMOUS_TOKEN_MESSAGE)) {
|
||||
final String username = sessionManager.getUserName();
|
||||
final CommonsApplication.BaseLogoutListener logoutListener = new CommonsApplication.BaseLogoutListener(
|
||||
this,
|
||||
getString(R.string.invalid_login_message),
|
||||
username
|
||||
);
|
||||
|
||||
CommonsApplication.getInstance().clearApplicationData(
|
||||
this, logoutListener);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Center the camera on the last saved location
|
||||
*/
|
||||
private void addCenterOnGPSButton() {
|
||||
fabCenterOnLocation = findViewById(R.id.center_on_gps);
|
||||
fabCenterOnLocation.setOnClickListener(view -> {
|
||||
moveToCurrentLocation = true;
|
||||
requestLocationPermissions();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds selected location marker on the map
|
||||
*/
|
||||
private void showSelectedLocationMarker(GeoPoint point) {
|
||||
Drawable icon = ContextCompat.getDrawable(this, R.drawable.map_default_map_marker);
|
||||
Marker marker = new Marker(mapView);
|
||||
marker.setPosition(point);
|
||||
marker.setAnchor(Marker.ANCHOR_CENTER, Marker.ANCHOR_BOTTOM);
|
||||
marker.setIcon(icon);
|
||||
marker.setInfoWindow(null);
|
||||
mapView.getOverlays().add(marker);
|
||||
mapView.invalidate();
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes selected location marker from the map
|
||||
*/
|
||||
private void removeSelectedLocationMarker() {
|
||||
List<Overlay> overlays = mapView.getOverlays();
|
||||
for (int i = 0; i < overlays.size(); i++) {
|
||||
if (overlays.get(i) instanceof Marker) {
|
||||
Marker item = (Marker) overlays.get(i);
|
||||
if (cameraPosition.getLatitude() == item.getPosition().getLatitude()
|
||||
&& cameraPosition.getLongitude() == item.getPosition().getLongitude()) {
|
||||
mapView.getOverlays().remove(i);
|
||||
mapView.invalidate();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Center the map at user's current location
|
||||
*/
|
||||
private void requestLocationPermissions() {
|
||||
locationPermissionsHelper = new LocationPermissionsHelper(
|
||||
this, locationManager, this);
|
||||
locationPermissionsHelper.requestForLocationAccess(R.string.location_permission_title,
|
||||
R.string.upload_map_location_access);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRequestPermissionsResult(final int requestCode,
|
||||
@NonNull final String[] permissions,
|
||||
@NonNull final int[] grantResults) {
|
||||
if (requestCode == Constants.RequestCodes.LOCATION
|
||||
&& grantResults[0] == PackageManager.PERMISSION_GRANTED) {
|
||||
onLocationPermissionGranted();
|
||||
} else {
|
||||
onLocationPermissionDenied(getString(R.string.upload_map_location_access));
|
||||
}
|
||||
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
mapView.onResume();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPause() {
|
||||
super.onPause();
|
||||
mapView.onPause();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLocationPermissionDenied(String toastMessage) {
|
||||
if (!ActivityCompat.shouldShowRequestPermissionRationale(this,
|
||||
permission.ACCESS_FINE_LOCATION)) {
|
||||
if (!locationPermissionsHelper.checkLocationPermission(this)) {
|
||||
if (store.getBoolean("isPermissionDenied", false)) {
|
||||
// means user has denied location permission twice or checked the "Don't show again"
|
||||
locationPermissionsHelper.showAppSettingsDialog(this,
|
||||
R.string.upload_map_location_access);
|
||||
} else {
|
||||
Toast.makeText(getBaseContext(), toastMessage, Toast.LENGTH_LONG).show();
|
||||
}
|
||||
store.putBoolean("isPermissionDenied", true);
|
||||
}
|
||||
} else {
|
||||
Toast.makeText(getBaseContext(), toastMessage, Toast.LENGTH_LONG).show();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLocationPermissionGranted() {
|
||||
if (moveToCurrentLocation || !(activity.equals("MediaActivity"))) {
|
||||
if (locationPermissionsHelper.isLocationAccessToAppsTurnedOn()) {
|
||||
locationManager.requestLocationUpdatesFromProvider(
|
||||
LocationManager.NETWORK_PROVIDER);
|
||||
locationManager.requestLocationUpdatesFromProvider(LocationManager.GPS_PROVIDER);
|
||||
addMarkerAtGPSLocation();
|
||||
} else {
|
||||
addMarkerAtGPSLocation();
|
||||
locationPermissionsHelper.showLocationOffDialog(this,
|
||||
R.string.ask_to_turn_location_on_text);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a marker to the map at the most recent GPS location
|
||||
* (which may be the current GPS location).
|
||||
*/
|
||||
private void addMarkerAtGPSLocation() {
|
||||
fr.free.nrw.commons.location.LatLng currLocation = locationManager.getLastLocation();
|
||||
if (currLocation != null) {
|
||||
GeoPoint currLocationGeopoint = new GeoPoint(currLocation.getLatitude(),
|
||||
currLocation.getLongitude());
|
||||
addLocationMarker(currLocationGeopoint);
|
||||
markerImage.setTranslationY(0);
|
||||
}
|
||||
}
|
||||
|
||||
private void addLocationMarker(GeoPoint geoPoint) {
|
||||
if (moveToCurrentLocation) {
|
||||
mapView.getOverlays().clear();
|
||||
}
|
||||
ScaleDiskOverlay diskOverlay =
|
||||
new ScaleDiskOverlay(this,
|
||||
geoPoint, 2000, GeoConstants.UnitOfMeasure.foot);
|
||||
Paint circlePaint = new Paint();
|
||||
circlePaint.setColor(Color.rgb(128, 128, 128));
|
||||
circlePaint.setStyle(Paint.Style.STROKE);
|
||||
circlePaint.setStrokeWidth(2f);
|
||||
diskOverlay.setCirclePaint2(circlePaint);
|
||||
Paint diskPaint = new Paint();
|
||||
diskPaint.setColor(Color.argb(40, 128, 128, 128));
|
||||
diskPaint.setStyle(Paint.Style.FILL_AND_STROKE);
|
||||
diskOverlay.setCirclePaint1(diskPaint);
|
||||
diskOverlay.setDisplaySizeMin(900);
|
||||
diskOverlay.setDisplaySizeMax(1700);
|
||||
mapView.getOverlays().add(diskOverlay);
|
||||
org.osmdroid.views.overlay.Marker startMarker = new org.osmdroid.views.overlay.Marker(
|
||||
mapView);
|
||||
startMarker.setPosition(geoPoint);
|
||||
startMarker.setAnchor(org.osmdroid.views.overlay.Marker.ANCHOR_CENTER,
|
||||
org.osmdroid.views.overlay.Marker.ANCHOR_BOTTOM);
|
||||
startMarker.setIcon(
|
||||
ContextCompat.getDrawable(this, R.drawable.current_location_marker));
|
||||
startMarker.setTitle("Your Location");
|
||||
startMarker.setTextLabelFontSize(24);
|
||||
mapView.getOverlays().add(startMarker);
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves the state of the activity
|
||||
* @param outState Bundle
|
||||
*/
|
||||
@Override
|
||||
public void onSaveInstanceState(@NonNull final Bundle outState) {
|
||||
super.onSaveInstanceState(outState);
|
||||
if(cameraPosition!=null){
|
||||
outState.putParcelable(CAMERA_POS, cameraPosition);
|
||||
}
|
||||
if(activity!=null){
|
||||
outState.putString(ACTIVITY, activity);
|
||||
}
|
||||
|
||||
if(media!=null){
|
||||
outState.putParcelable("sMedia", media);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,678 @@
|
|||
package fr.free.nrw.commons.LocationPicker
|
||||
|
||||
import android.Manifest.permission
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Intent
|
||||
import android.content.pm.PackageManager
|
||||
import android.graphics.Color
|
||||
import android.graphics.Paint
|
||||
import android.location.LocationManager
|
||||
import android.os.Bundle
|
||||
import android.preference.PreferenceManager
|
||||
import android.text.Html
|
||||
import android.text.method.LinkMovementMethod
|
||||
import android.view.MotionEvent
|
||||
import android.view.View
|
||||
import android.view.Window
|
||||
import android.view.animation.OvershootInterpolator
|
||||
import android.widget.Button
|
||||
import android.widget.ImageView
|
||||
import android.widget.TextView
|
||||
import android.widget.Toast
|
||||
import androidx.appcompat.widget.AppCompatTextView
|
||||
import androidx.constraintlayout.widget.ConstraintLayout
|
||||
import androidx.core.app.ActivityCompat
|
||||
import androidx.core.content.ContextCompat
|
||||
import com.google.android.material.floatingactionbutton.FloatingActionButton
|
||||
import fr.free.nrw.commons.CameraPosition
|
||||
import fr.free.nrw.commons.CommonsApplication
|
||||
import fr.free.nrw.commons.Media
|
||||
import fr.free.nrw.commons.R
|
||||
import fr.free.nrw.commons.Utils
|
||||
import fr.free.nrw.commons.auth.SessionManager
|
||||
import fr.free.nrw.commons.auth.csrf.CsrfTokenClient
|
||||
import fr.free.nrw.commons.coordinates.CoordinateEditHelper
|
||||
import fr.free.nrw.commons.filepicker.Constants
|
||||
import fr.free.nrw.commons.kvstore.BasicKvStore
|
||||
import fr.free.nrw.commons.kvstore.JsonKvStore
|
||||
import fr.free.nrw.commons.location.LocationPermissionsHelper
|
||||
import fr.free.nrw.commons.location.LocationPermissionsHelper.LocationPermissionCallback
|
||||
import fr.free.nrw.commons.location.LocationServiceManager
|
||||
import fr.free.nrw.commons.theme.BaseActivity
|
||||
import fr.free.nrw.commons.upload.mediaDetails.UploadMediaDetailFragment.LAST_LOCATION
|
||||
import fr.free.nrw.commons.upload.mediaDetails.UploadMediaDetailFragment.LAST_ZOOM
|
||||
import fr.free.nrw.commons.utils.DialogUtil
|
||||
import fr.free.nrw.commons.utils.MapUtils.ZOOM_LEVEL
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers
|
||||
import io.reactivex.schedulers.Schedulers
|
||||
import org.osmdroid.tileprovider.tilesource.TileSourceFactory
|
||||
import org.osmdroid.util.GeoPoint
|
||||
import org.osmdroid.util.constants.GeoConstants
|
||||
import org.osmdroid.views.CustomZoomButtonsController
|
||||
import org.osmdroid.views.overlay.Marker
|
||||
import org.osmdroid.views.overlay.ScaleDiskOverlay
|
||||
import org.osmdroid.views.overlay.TilesOverlay
|
||||
import timber.log.Timber
|
||||
import java.util.Locale
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Named
|
||||
|
||||
|
||||
/**
|
||||
* Helps to pick location and return the result with an intent
|
||||
*/
|
||||
class LocationPickerActivity : BaseActivity(), LocationPermissionCallback {
|
||||
/**
|
||||
* coordinateEditHelper: helps to edit coordinates
|
||||
*/
|
||||
@Inject
|
||||
lateinit var coordinateEditHelper: CoordinateEditHelper
|
||||
|
||||
/**
|
||||
* media : Media object
|
||||
*/
|
||||
private var media: Media? = null
|
||||
|
||||
/**
|
||||
* cameraPosition : position of picker
|
||||
*/
|
||||
private var cameraPosition: CameraPosition? = null
|
||||
|
||||
/**
|
||||
* markerImage : picker image
|
||||
*/
|
||||
private lateinit var markerImage: ImageView
|
||||
|
||||
/**
|
||||
* mapView : OSM Map
|
||||
*/
|
||||
private var mapView: org.osmdroid.views.MapView? = null
|
||||
|
||||
/**
|
||||
* tvAttribution : credit
|
||||
*/
|
||||
private lateinit var tvAttribution: AppCompatTextView
|
||||
|
||||
/**
|
||||
* activity : activity key
|
||||
*/
|
||||
private var activity: String? = null
|
||||
|
||||
/**
|
||||
* modifyLocationButton : button for start editing location
|
||||
*/
|
||||
private lateinit var modifyLocationButton: Button
|
||||
|
||||
/**
|
||||
* removeLocationButton : button to remove location metadata
|
||||
*/
|
||||
private lateinit var removeLocationButton: Button
|
||||
|
||||
/**
|
||||
* showInMapButton : button for showing in map
|
||||
*/
|
||||
private lateinit var showInMapButton: TextView
|
||||
|
||||
/**
|
||||
* placeSelectedButton : fab for selecting location
|
||||
*/
|
||||
private lateinit var placeSelectedButton: FloatingActionButton
|
||||
|
||||
/**
|
||||
* fabCenterOnLocation: button for center on location;
|
||||
*/
|
||||
private lateinit var fabCenterOnLocation: FloatingActionButton
|
||||
|
||||
/**
|
||||
* shadow : imageview of shadow
|
||||
*/
|
||||
private lateinit var shadow: ImageView
|
||||
|
||||
/**
|
||||
* largeToolbarText : textView of shadow
|
||||
*/
|
||||
private lateinit var largeToolbarText: TextView
|
||||
|
||||
/**
|
||||
* smallToolbarText : textView of shadow
|
||||
*/
|
||||
private lateinit var smallToolbarText: TextView
|
||||
|
||||
/**
|
||||
* applicationKvStore : for storing values
|
||||
*/
|
||||
@Inject
|
||||
@field: Named("default_preferences")
|
||||
lateinit var applicationKvStore: JsonKvStore
|
||||
private lateinit var store: BasicKvStore
|
||||
|
||||
/**
|
||||
* isDarkTheme: for keeping a track of the device theme and modifying the map theme accordingly
|
||||
*/
|
||||
private var isDarkTheme: Boolean = false
|
||||
private var moveToCurrentLocation: Boolean = false
|
||||
|
||||
@Inject
|
||||
lateinit var locationManager: LocationServiceManager
|
||||
private lateinit var locationPermissionsHelper: LocationPermissionsHelper
|
||||
|
||||
@Inject
|
||||
lateinit var sessionManager: SessionManager
|
||||
|
||||
/**
|
||||
* Constants
|
||||
*/
|
||||
companion object {
|
||||
private const val CAMERA_POS = "cameraPosition"
|
||||
private const val ACTIVITY = "activity"
|
||||
}
|
||||
|
||||
@SuppressLint("ClickableViewAccessibility")
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
requestWindowFeature(Window.FEATURE_ACTION_BAR)
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
isDarkTheme = systemThemeUtils.isDeviceInNightMode()
|
||||
moveToCurrentLocation = false
|
||||
store = BasicKvStore(this, "LocationPermissions")
|
||||
|
||||
requestWindowFeature(Window.FEATURE_ACTION_BAR)
|
||||
supportActionBar?.hide()
|
||||
setContentView(R.layout.activity_location_picker)
|
||||
|
||||
if (savedInstanceState == null) {
|
||||
cameraPosition = intent.getParcelableExtra(LocationPickerConstants.MAP_CAMERA_POSITION)
|
||||
activity = intent.getStringExtra(LocationPickerConstants.ACTIVITY_KEY)
|
||||
media = intent.getParcelableExtra(LocationPickerConstants.MEDIA)
|
||||
} else {
|
||||
cameraPosition = savedInstanceState.getParcelable(CAMERA_POS)
|
||||
activity = savedInstanceState.getString(ACTIVITY)
|
||||
media = savedInstanceState.getParcelable("sMedia")
|
||||
}
|
||||
|
||||
bindViews()
|
||||
addBackButtonListener()
|
||||
addPlaceSelectedButton()
|
||||
addCredits()
|
||||
getToolbarUI()
|
||||
addCenterOnGPSButton()
|
||||
|
||||
org.osmdroid.config.Configuration.getInstance()
|
||||
.load(
|
||||
applicationContext, PreferenceManager.getDefaultSharedPreferences(
|
||||
applicationContext
|
||||
)
|
||||
)
|
||||
|
||||
mapView?.setTileSource(TileSourceFactory.WIKIMEDIA)
|
||||
mapView?.setTilesScaledToDpi(true)
|
||||
mapView?.setMultiTouchControls(true)
|
||||
|
||||
org.osmdroid.config.Configuration.getInstance().additionalHttpRequestProperties["Referer"] =
|
||||
"http://maps.wikimedia.org/"
|
||||
mapView?.zoomController?.setVisibility(CustomZoomButtonsController.Visibility.NEVER)
|
||||
mapView?.controller?.setZoom(ZOOM_LEVEL.toDouble())
|
||||
mapView?.setOnTouchListener { _, event ->
|
||||
when (event.action) {
|
||||
MotionEvent.ACTION_MOVE -> {
|
||||
if (markerImage.translationY == 0f) {
|
||||
markerImage.animate().translationY(-75f)
|
||||
.setInterpolator(OvershootInterpolator()).duration = 250
|
||||
}
|
||||
}
|
||||
MotionEvent.ACTION_UP -> {
|
||||
markerImage.animate().translationY(0f)
|
||||
.setInterpolator(OvershootInterpolator()).duration = 250
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
if (activity == "UploadActivity") {
|
||||
placeSelectedButton.visibility = View.GONE
|
||||
modifyLocationButton.visibility = View.VISIBLE
|
||||
removeLocationButton.visibility = View.VISIBLE
|
||||
showInMapButton.visibility = View.VISIBLE
|
||||
largeToolbarText.text = getString(R.string.image_location)
|
||||
smallToolbarText.text = getString(R.string.check_whether_location_is_correct)
|
||||
fabCenterOnLocation.visibility = View.GONE
|
||||
markerImage.visibility = View.GONE
|
||||
shadow.visibility = View.GONE
|
||||
cameraPosition?.let {
|
||||
showSelectedLocationMarker(GeoPoint(it.latitude, it.longitude))
|
||||
}
|
||||
}
|
||||
setupMapView()
|
||||
}
|
||||
|
||||
/**
|
||||
* Moves the center of the map to the specified coordinates
|
||||
*/
|
||||
private fun moveMapTo(latitude: Double, longitude: Double) {
|
||||
mapView?.controller?.let {
|
||||
val point = GeoPoint(latitude, longitude)
|
||||
it.setCenter(point)
|
||||
it.animateTo(point)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Moves the center of the map to the specified coordinates
|
||||
* @param point The GeoPoint object which contains the coordinates to move to
|
||||
*/
|
||||
private fun moveMapTo(point: GeoPoint?) {
|
||||
point?.let {
|
||||
moveMapTo(it.latitude, it.longitude)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* For showing credits
|
||||
*/
|
||||
private fun addCredits() {
|
||||
tvAttribution.text = Html.fromHtml(getString(R.string.map_attribution))
|
||||
tvAttribution.movementMethod = LinkMovementMethod.getInstance()
|
||||
}
|
||||
|
||||
/**
|
||||
* For setting up Dark Theme
|
||||
*/
|
||||
private fun darkThemeSetup() {
|
||||
if (isDarkTheme) {
|
||||
shadow.setColorFilter(Color.argb(255, 255, 255, 255))
|
||||
mapView?.overlayManager?.tilesOverlay?.setColorFilter(TilesOverlay.INVERT_COLORS)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Clicking back button destroy locationPickerActivity
|
||||
*/
|
||||
private fun addBackButtonListener() {
|
||||
val backButton = findViewById<ImageView>(R.id.maplibre_place_picker_toolbar_back_button)
|
||||
backButton.setOnClickListener {
|
||||
finish()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Binds mapView and location picker icon
|
||||
*/
|
||||
private fun bindViews() {
|
||||
mapView = findViewById(R.id.map_view)
|
||||
markerImage = findViewById(R.id.location_picker_image_view_marker)
|
||||
tvAttribution = findViewById(R.id.tv_attribution)
|
||||
modifyLocationButton = findViewById(R.id.modify_location)
|
||||
removeLocationButton = findViewById(R.id.remove_location)
|
||||
showInMapButton = findViewById(R.id.show_in_map)
|
||||
showInMapButton.text = getString(R.string.show_in_map_app).uppercase(Locale.ROOT)
|
||||
shadow = findViewById(R.id.location_picker_image_view_shadow)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets toolbar color
|
||||
*/
|
||||
private fun getToolbarUI() {
|
||||
val toolbar: ConstraintLayout = findViewById(R.id.location_picker_toolbar)
|
||||
largeToolbarText = findViewById(R.id.location_picker_toolbar_primary_text_view)
|
||||
smallToolbarText = findViewById(R.id.location_picker_toolbar_secondary_text_view)
|
||||
toolbar.setBackgroundColor(ContextCompat.getColor(this, R.color.primaryColor))
|
||||
}
|
||||
|
||||
private fun setupMapView() {
|
||||
requestLocationPermissions()
|
||||
|
||||
//If location metadata is available, move map to that location.
|
||||
if (activity == "UploadActivity" || activity == "MediaActivity") {
|
||||
moveMapToMediaLocation()
|
||||
} else {
|
||||
//If location metadata is not available, move map to device GPS location.
|
||||
moveMapToGPSLocation()
|
||||
}
|
||||
|
||||
modifyLocationButton.setOnClickListener { onClickModifyLocation() }
|
||||
removeLocationButton.setOnClickListener { onClickRemoveLocation() }
|
||||
showInMapButton.setOnClickListener { showInMapApp() }
|
||||
darkThemeSetup()
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles onClick event of modifyLocationButton
|
||||
*/
|
||||
private fun onClickModifyLocation() {
|
||||
placeSelectedButton.visibility = View.VISIBLE
|
||||
modifyLocationButton.visibility = View.GONE
|
||||
removeLocationButton.visibility = View.GONE
|
||||
showInMapButton.visibility = View.GONE
|
||||
markerImage.visibility = View.VISIBLE
|
||||
shadow.visibility = View.VISIBLE
|
||||
largeToolbarText.text = getString(R.string.choose_a_location)
|
||||
smallToolbarText.text = getString(R.string.pan_and_zoom_to_adjust)
|
||||
fabCenterOnLocation.visibility = View.VISIBLE
|
||||
removeSelectedLocationMarker()
|
||||
moveMapToMediaLocation()
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles onClick event of removeLocationButton
|
||||
*/
|
||||
private fun onClickRemoveLocation() {
|
||||
DialogUtil.showAlertDialog(
|
||||
this,
|
||||
getString(R.string.remove_location_warning_title),
|
||||
getString(R.string.remove_location_warning_desc),
|
||||
getString(R.string.continue_message),
|
||||
getString(R.string.cancel),
|
||||
{ removeLocationFromImage() },
|
||||
null
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes location metadata from the image
|
||||
*/
|
||||
private fun removeLocationFromImage() {
|
||||
media?.let {
|
||||
compositeDisposable.add(
|
||||
coordinateEditHelper.makeCoordinatesEdit(
|
||||
applicationContext, it, "0.0", "0.0", "0.0f"
|
||||
)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe { _ ->
|
||||
Timber.d("Coordinates removed from the image")
|
||||
}
|
||||
)
|
||||
}
|
||||
setResult(RESULT_OK, Intent())
|
||||
finish()
|
||||
}
|
||||
|
||||
/**
|
||||
* Show location in map app
|
||||
*/
|
||||
private fun showInMapApp() {
|
||||
val position = when {
|
||||
//location metadata is available
|
||||
activity == "UploadActivity" && cameraPosition != null -> {
|
||||
fr.free.nrw.commons.location.LatLng(cameraPosition!!.latitude, cameraPosition!!.longitude, 0.0f)
|
||||
}
|
||||
//location metadata is not available
|
||||
mapView != null -> {
|
||||
fr.free.nrw.commons.location.LatLng(
|
||||
mapView?.mapCenter?.latitude!!,
|
||||
mapView?.mapCenter?.longitude!!,
|
||||
0.0f
|
||||
)
|
||||
}
|
||||
else -> null
|
||||
}
|
||||
|
||||
position?.let { Utils.handleGeoCoordinates(this, it) }
|
||||
}
|
||||
|
||||
/**
|
||||
* Moves map to media's location
|
||||
*/
|
||||
private fun moveMapToMediaLocation() {
|
||||
cameraPosition?.let {
|
||||
moveMapTo(GeoPoint(it.latitude, it.longitude))
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Moves map to GPS location
|
||||
*/
|
||||
private fun moveMapToGPSLocation() {
|
||||
locationManager.getLastLocation()?.let {
|
||||
moveMapTo(GeoPoint(it.latitude, it.longitude))
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds "Place Selected" button
|
||||
*/
|
||||
private fun addPlaceSelectedButton() {
|
||||
placeSelectedButton = findViewById(R.id.location_chosen_button)
|
||||
placeSelectedButton.setOnClickListener { placeSelected() }
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles "Place Selected" action
|
||||
*/
|
||||
private fun placeSelected() {
|
||||
if (activity == "NoLocationUploadActivity") {
|
||||
applicationKvStore.putString(
|
||||
LAST_LOCATION,
|
||||
"${mapView?.mapCenter?.latitude},${mapView?.mapCenter?.longitude}"
|
||||
)
|
||||
applicationKvStore.putString(LAST_ZOOM, mapView?.zoomLevel?.toString()!!)
|
||||
}
|
||||
|
||||
if (media == null) {
|
||||
val intent = Intent().apply {
|
||||
putExtra(
|
||||
LocationPickerConstants.MAP_CAMERA_POSITION,
|
||||
CameraPosition(mapView?.mapCenter?.latitude!!, mapView?.mapCenter?.longitude!!, 14.0)
|
||||
)
|
||||
}
|
||||
setResult(RESULT_OK, intent)
|
||||
} else {
|
||||
updateCoordinates(
|
||||
mapView?.mapCenter?.latitude.toString(),
|
||||
mapView?.mapCenter?.longitude.toString(),
|
||||
"0.0f"
|
||||
)
|
||||
}
|
||||
|
||||
finish()
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates image with new coordinates
|
||||
*/
|
||||
fun updateCoordinates(latitude: String, longitude: String, accuracy: String) {
|
||||
media?.let {
|
||||
try {
|
||||
compositeDisposable.add(
|
||||
coordinateEditHelper.makeCoordinatesEdit(
|
||||
applicationContext,
|
||||
it,
|
||||
latitude,
|
||||
longitude,
|
||||
accuracy
|
||||
).subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe { _ ->
|
||||
Timber.d("Coordinates updated")
|
||||
}
|
||||
)
|
||||
} catch (e: Exception) {
|
||||
if (e.localizedMessage == CsrfTokenClient.ANONYMOUS_TOKEN_MESSAGE) {
|
||||
val username = sessionManager.userName
|
||||
CommonsApplication.BaseLogoutListener(
|
||||
this,
|
||||
getString(R.string.invalid_login_message)
|
||||
, username
|
||||
).let {
|
||||
CommonsApplication.instance.clearApplicationData(this, it)
|
||||
}
|
||||
} else { }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a button to center the map at user's location
|
||||
*/
|
||||
private fun addCenterOnGPSButton() {
|
||||
fabCenterOnLocation = findViewById(R.id.center_on_gps)
|
||||
fabCenterOnLocation.setOnClickListener {
|
||||
moveToCurrentLocation = true
|
||||
requestLocationPermissions()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows a selected location marker
|
||||
*/
|
||||
private fun showSelectedLocationMarker(point: GeoPoint) {
|
||||
val icon = ContextCompat.getDrawable(this, R.drawable.map_default_map_marker)
|
||||
Marker(mapView).apply {
|
||||
position = point
|
||||
setAnchor(Marker.ANCHOR_CENTER, Marker.ANCHOR_BOTTOM)
|
||||
setIcon(icon)
|
||||
infoWindow = null
|
||||
mapView?.overlays?.add(this)
|
||||
}
|
||||
mapView?.invalidate()
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes selected location marker
|
||||
*/
|
||||
private fun removeSelectedLocationMarker() {
|
||||
val overlays = mapView?.overlays
|
||||
overlays?.filterIsInstance<Marker>()?.firstOrNull {
|
||||
it.position.latitude ==
|
||||
cameraPosition?.latitude && it.position.longitude == cameraPosition?.longitude
|
||||
}?.let {
|
||||
overlays.remove(it)
|
||||
mapView?.invalidate()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Centers map at user's location
|
||||
*/
|
||||
private fun requestLocationPermissions() {
|
||||
locationPermissionsHelper = LocationPermissionsHelper(this, locationManager, this)
|
||||
locationPermissionsHelper.requestForLocationAccess(
|
||||
R.string.location_permission_title,
|
||||
R.string.upload_map_location_access
|
||||
)
|
||||
}
|
||||
|
||||
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) {
|
||||
if (requestCode == Constants.RequestCodes.LOCATION && grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
|
||||
onLocationPermissionGranted()
|
||||
} else {
|
||||
onLocationPermissionDenied(getString(R.string.upload_map_location_access))
|
||||
}
|
||||
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
mapView?.onResume()
|
||||
}
|
||||
|
||||
override fun onPause() {
|
||||
super.onPause()
|
||||
mapView?.onPause()
|
||||
}
|
||||
|
||||
override fun onLocationPermissionDenied(toastMessage: String) {
|
||||
val isDeniedBefore = store.getBoolean("isPermissionDenied", false)
|
||||
val showRationale = ActivityCompat.shouldShowRequestPermissionRationale(this, permission.ACCESS_FINE_LOCATION)
|
||||
|
||||
if (!showRationale) {
|
||||
if (!locationPermissionsHelper.checkLocationPermission(this)) {
|
||||
if (isDeniedBefore) {
|
||||
locationPermissionsHelper.showAppSettingsDialog(this, R.string.upload_map_location_access)
|
||||
} else {
|
||||
Toast.makeText(this, toastMessage, Toast.LENGTH_LONG).show()
|
||||
}
|
||||
store.putBoolean("isPermissionDenied", true)
|
||||
}
|
||||
} else {
|
||||
Toast.makeText(this, toastMessage, Toast.LENGTH_LONG).show()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onLocationPermissionGranted() {
|
||||
if (moveToCurrentLocation || activity != "MediaActivity") {
|
||||
if (locationPermissionsHelper.isLocationAccessToAppsTurnedOn()) {
|
||||
locationManager.requestLocationUpdatesFromProvider(LocationManager.NETWORK_PROVIDER)
|
||||
locationManager.requestLocationUpdatesFromProvider(LocationManager.GPS_PROVIDER)
|
||||
addMarkerAtGPSLocation()
|
||||
} else {
|
||||
addMarkerAtGPSLocation()
|
||||
locationPermissionsHelper.showLocationOffDialog(this, R.string.ask_to_turn_location_on_text)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a marker at the user's GPS location
|
||||
*/
|
||||
private fun addMarkerAtGPSLocation() {
|
||||
locationManager.getLastLocation()?.let {
|
||||
addLocationMarker(GeoPoint(it.latitude, it.longitude))
|
||||
markerImage.translationY = 0f
|
||||
}
|
||||
}
|
||||
|
||||
private fun addLocationMarker(geoPoint: GeoPoint) {
|
||||
if (moveToCurrentLocation) {
|
||||
mapView?.overlays?.clear()
|
||||
}
|
||||
|
||||
val diskOverlay = ScaleDiskOverlay(
|
||||
this,
|
||||
geoPoint,
|
||||
2000,
|
||||
GeoConstants.UnitOfMeasure.foot
|
||||
)
|
||||
|
||||
val circlePaint = Paint().apply {
|
||||
color = Color.rgb(128, 128, 128)
|
||||
style = Paint.Style.STROKE
|
||||
strokeWidth = 2f
|
||||
}
|
||||
diskOverlay.setCirclePaint2(circlePaint)
|
||||
|
||||
val diskPaint = Paint().apply {
|
||||
color = Color.argb(40, 128, 128, 128)
|
||||
style = Paint.Style.FILL_AND_STROKE
|
||||
}
|
||||
diskOverlay.setCirclePaint1(diskPaint)
|
||||
|
||||
diskOverlay.setDisplaySizeMin(900)
|
||||
diskOverlay.setDisplaySizeMax(1700)
|
||||
|
||||
mapView?.overlays?.add(diskOverlay)
|
||||
|
||||
val startMarker = Marker(mapView).apply {
|
||||
position = geoPoint
|
||||
setAnchor(
|
||||
Marker.ANCHOR_CENTER,
|
||||
Marker.ANCHOR_BOTTOM
|
||||
)
|
||||
icon = ContextCompat.getDrawable(this@LocationPickerActivity, R.drawable.current_location_marker)
|
||||
title = "Your Location"
|
||||
textLabelFontSize = 24
|
||||
}
|
||||
|
||||
mapView?.overlays?.add(startMarker)
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves the state of the activity
|
||||
* @param outState Bundle
|
||||
*/
|
||||
override fun onSaveInstanceState(outState: Bundle) {
|
||||
super.onSaveInstanceState(outState)
|
||||
|
||||
cameraPosition?.let {
|
||||
outState.putParcelable(CAMERA_POS, it)
|
||||
}
|
||||
|
||||
activity?.let {
|
||||
outState.putString(ACTIVITY, it)
|
||||
}
|
||||
|
||||
media?.let {
|
||||
outState.putParcelable("sMedia", it)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,20 +0,0 @@
|
|||
package fr.free.nrw.commons.LocationPicker;
|
||||
|
||||
/**
|
||||
* Constants need for location picking
|
||||
*/
|
||||
public final class LocationPickerConstants {
|
||||
|
||||
public static final String ACTIVITY_KEY
|
||||
= "location.picker.activity";
|
||||
|
||||
public static final String MAP_CAMERA_POSITION
|
||||
= "location.picker.cameraPosition";
|
||||
|
||||
public static final String MEDIA
|
||||
= "location.picker.media";
|
||||
|
||||
|
||||
private LocationPickerConstants() {
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
package fr.free.nrw.commons.LocationPicker
|
||||
|
||||
/**
|
||||
* Constants need for location picking
|
||||
*/
|
||||
object LocationPickerConstants {
|
||||
|
||||
const val ACTIVITY_KEY = "location.picker.activity"
|
||||
|
||||
const val MAP_CAMERA_POSITION = "location.picker.cameraPosition"
|
||||
|
||||
const val MEDIA = "location.picker.media"
|
||||
}
|
||||
|
|
@ -1,63 +0,0 @@
|
|||
package fr.free.nrw.commons.LocationPicker;
|
||||
|
||||
import android.app.Application;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.lifecycle.AndroidViewModel;
|
||||
import androidx.lifecycle.MutableLiveData;
|
||||
import fr.free.nrw.commons.CameraPosition;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import retrofit2.Call;
|
||||
import retrofit2.Callback;
|
||||
import retrofit2.Response;
|
||||
import timber.log.Timber;
|
||||
|
||||
/**
|
||||
* Observes live camera position data
|
||||
*/
|
||||
public class LocationPickerViewModel extends AndroidViewModel implements Callback<CameraPosition> {
|
||||
|
||||
/**
|
||||
* Wrapping CameraPosition with MutableLiveData
|
||||
*/
|
||||
private final MutableLiveData<CameraPosition> result = new MutableLiveData<>();
|
||||
|
||||
/**
|
||||
* Constructor for this class
|
||||
*
|
||||
* @param application Application
|
||||
*/
|
||||
public LocationPickerViewModel(@NonNull final Application application) {
|
||||
super(application);
|
||||
}
|
||||
|
||||
/**
|
||||
* Responses on camera position changing
|
||||
*
|
||||
* @param call Call<CameraPosition>
|
||||
* @param response Response<CameraPosition>
|
||||
*/
|
||||
@Override
|
||||
public void onResponse(final @NotNull Call<CameraPosition> call,
|
||||
final Response<CameraPosition> response) {
|
||||
if (response.body() == null) {
|
||||
result.setValue(null);
|
||||
return;
|
||||
}
|
||||
result.setValue(response.body());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(final @NotNull Call<CameraPosition> call, final @NotNull Throwable t) {
|
||||
Timber.e(t);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets live CameraPosition
|
||||
*
|
||||
* @return MutableLiveData<CameraPosition>
|
||||
*/
|
||||
public MutableLiveData<CameraPosition> getResult() {
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,44 @@
|
|||
package fr.free.nrw.commons.LocationPicker
|
||||
|
||||
import android.app.Application
|
||||
import androidx.lifecycle.AndroidViewModel
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import fr.free.nrw.commons.CameraPosition
|
||||
import retrofit2.Call
|
||||
import retrofit2.Callback
|
||||
import retrofit2.Response
|
||||
import timber.log.Timber
|
||||
|
||||
/**
|
||||
* Observes live camera position data
|
||||
*/
|
||||
class LocationPickerViewModel(
|
||||
application: Application
|
||||
): AndroidViewModel(application), Callback<CameraPosition> {
|
||||
|
||||
/**
|
||||
* Wrapping CameraPosition with MutableLiveData
|
||||
*/
|
||||
val result = MutableLiveData<CameraPosition?>()
|
||||
|
||||
/**
|
||||
* Responses on camera position changing
|
||||
*
|
||||
* @param call Call<CameraPosition>
|
||||
* @param response Response<CameraPosition>
|
||||
*/
|
||||
override fun onResponse(
|
||||
call: Call<CameraPosition>,
|
||||
response: Response<CameraPosition>
|
||||
) {
|
||||
if(response.body() == null) {
|
||||
result.value = null
|
||||
return
|
||||
}
|
||||
result.value = response.body()
|
||||
}
|
||||
|
||||
override fun onFailure(call: Call<CameraPosition>, t: Throwable) {
|
||||
Timber.e(t)
|
||||
}
|
||||
}
|
||||
|
|
@ -3,7 +3,7 @@ package fr.free.nrw.commons.actions
|
|||
import fr.free.nrw.commons.CommonsApplication
|
||||
import fr.free.nrw.commons.auth.csrf.CsrfTokenClient
|
||||
import fr.free.nrw.commons.auth.csrf.InvalidLoginTokenException
|
||||
import fr.free.nrw.commons.di.NetworkingModule.NAMED_COMMONS_CSRF
|
||||
import fr.free.nrw.commons.di.NetworkingModule.Companion.NAMED_COMMONS_CSRF
|
||||
import io.reactivex.Observable
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Named
|
||||
|
|
|
|||
|
|
@ -1,32 +0,0 @@
|
|||
package fr.free.nrw.commons.bookmarks;
|
||||
|
||||
import androidx.fragment.app.Fragment;
|
||||
|
||||
/**
|
||||
* Data class for handling a bookmark fragment and it title
|
||||
*/
|
||||
public class BookmarkPages {
|
||||
private Fragment page;
|
||||
private String title;
|
||||
|
||||
BookmarkPages(Fragment fragment, String title) {
|
||||
this.title = title;
|
||||
this.page = fragment;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the fragment
|
||||
* @return fragment object
|
||||
*/
|
||||
public Fragment getPage() {
|
||||
return page;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the fragment title
|
||||
* @return title
|
||||
*/
|
||||
public String getTitle() {
|
||||
return title;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
package fr.free.nrw.commons.bookmarks
|
||||
|
||||
import androidx.fragment.app.Fragment
|
||||
|
||||
data class BookmarkPages (
|
||||
val page: Fragment? = null,
|
||||
val title: String? = null
|
||||
)
|
||||
|
|
@ -3,9 +3,8 @@ package fr.free.nrw.commons.campaigns
|
|||
import android.annotation.SuppressLint
|
||||
import fr.free.nrw.commons.BasePresenter
|
||||
import fr.free.nrw.commons.campaigns.models.Campaign
|
||||
import fr.free.nrw.commons.di.CommonsApplicationModule
|
||||
import fr.free.nrw.commons.di.CommonsApplicationModule.IO_THREAD
|
||||
import fr.free.nrw.commons.di.CommonsApplicationModule.MAIN_THREAD
|
||||
import fr.free.nrw.commons.di.CommonsApplicationModule.Companion.IO_THREAD
|
||||
import fr.free.nrw.commons.di.CommonsApplicationModule.Companion.MAIN_THREAD
|
||||
import fr.free.nrw.commons.mwapi.OkHttpJsonApiClient
|
||||
import fr.free.nrw.commons.utils.CommonsDateUtil.getIso8601DateFormatShort
|
||||
import io.reactivex.Scheduler
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ package fr.free.nrw.commons.contributions
|
|||
import androidx.paging.PagedList.BoundaryCallback
|
||||
import fr.free.nrw.commons.auth.SessionManager
|
||||
import fr.free.nrw.commons.di.CommonsApplicationModule
|
||||
import fr.free.nrw.commons.di.CommonsApplicationModule.Companion.IO_THREAD
|
||||
import fr.free.nrw.commons.media.MediaClient
|
||||
import io.reactivex.Scheduler
|
||||
import io.reactivex.disposables.CompositeDisposable
|
||||
|
|
@ -20,7 +21,7 @@ class ContributionBoundaryCallback
|
|||
private val repository: ContributionsRepository,
|
||||
private val sessionManager: SessionManager,
|
||||
private val mediaClient: MediaClient,
|
||||
@param:Named(CommonsApplicationModule.IO_THREAD) private val ioThreadScheduler: Scheduler,
|
||||
@param:Named(IO_THREAD) private val ioThreadScheduler: Scheduler,
|
||||
) : BoundaryCallback<Contribution>() {
|
||||
private val compositeDisposable: CompositeDisposable = CompositeDisposable()
|
||||
var userName: String? = null
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
package fr.free.nrw.commons.contributions;
|
||||
|
||||
import static fr.free.nrw.commons.di.CommonsApplicationModule.IO_THREAD;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.lifecycle.LiveData;
|
||||
import androidx.paging.DataSource;
|
||||
|
|
@ -34,7 +36,7 @@ public class ContributionsListPresenter implements UserActionListener {
|
|||
final ContributionBoundaryCallback contributionBoundaryCallback,
|
||||
final ContributionsRemoteDataSource contributionsRemoteDataSource,
|
||||
final ContributionsRepository repository,
|
||||
@Named(CommonsApplicationModule.IO_THREAD) final Scheduler ioThreadScheduler) {
|
||||
@Named(IO_THREAD) final Scheduler ioThreadScheduler) {
|
||||
this.contributionBoundaryCallback = contributionBoundaryCallback;
|
||||
this.repository = repository;
|
||||
this.ioThreadScheduler = ioThreadScheduler;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
package fr.free.nrw.commons.contributions;
|
||||
|
||||
import static fr.free.nrw.commons.di.CommonsApplicationModule.IO_THREAD;
|
||||
import static fr.free.nrw.commons.utils.ImageUtils.IMAGE_OK;
|
||||
|
||||
import androidx.work.ExistingWorkPolicy;
|
||||
|
|
@ -31,7 +32,7 @@ public class ContributionsPresenter implements UserActionListener {
|
|||
@Inject
|
||||
ContributionsPresenter(ContributionsRepository repository,
|
||||
UploadRepository uploadRepository,
|
||||
@Named(CommonsApplicationModule.IO_THREAD) Scheduler ioThreadScheduler) {
|
||||
@Named(IO_THREAD) Scheduler ioThreadScheduler) {
|
||||
this.contributionsRepository = repository;
|
||||
this.uploadRepository = uploadRepository;
|
||||
this.ioThreadScheduler = ioThreadScheduler;
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
package fr.free.nrw.commons.contributions
|
||||
|
||||
import androidx.paging.ItemKeyedDataSource
|
||||
import fr.free.nrw.commons.di.CommonsApplicationModule
|
||||
import fr.free.nrw.commons.di.CommonsApplicationModule.Companion.IO_THREAD
|
||||
import fr.free.nrw.commons.media.MediaClient
|
||||
import io.reactivex.Scheduler
|
||||
import io.reactivex.disposables.CompositeDisposable
|
||||
|
|
@ -16,7 +16,7 @@ class ContributionsRemoteDataSource
|
|||
@Inject
|
||||
constructor(
|
||||
private val mediaClient: MediaClient,
|
||||
@param:Named(CommonsApplicationModule.IO_THREAD) private val ioThreadScheduler: Scheduler,
|
||||
@param:Named(IO_THREAD) private val ioThreadScheduler: Scheduler,
|
||||
) : ItemKeyedDataSource<Int, Contribution>() {
|
||||
private val compositeDisposable: CompositeDisposable = CompositeDisposable()
|
||||
var userName: String? = null
|
||||
|
|
|
|||
|
|
@ -22,7 +22,10 @@ import java.util.Map;
|
|||
public class Converters {
|
||||
|
||||
public static Gson getGson() {
|
||||
return ApplicationlessInjection.getInstance(CommonsApplication.getInstance()).getCommonsApplicationComponent().gson();
|
||||
return ApplicationlessInjection
|
||||
.getInstance(CommonsApplication.getInstance())
|
||||
.getCommonsApplicationComponent()
|
||||
.gson();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -1,90 +0,0 @@
|
|||
package fr.free.nrw.commons.di;
|
||||
|
||||
import dagger.Module;
|
||||
import dagger.android.ContributesAndroidInjector;
|
||||
import fr.free.nrw.commons.AboutActivity;
|
||||
import fr.free.nrw.commons.LocationPicker.LocationPickerActivity;
|
||||
import fr.free.nrw.commons.WelcomeActivity;
|
||||
import fr.free.nrw.commons.auth.LoginActivity;
|
||||
import fr.free.nrw.commons.auth.SignupActivity;
|
||||
import fr.free.nrw.commons.category.CategoryDetailsActivity;
|
||||
import fr.free.nrw.commons.contributions.MainActivity;
|
||||
import fr.free.nrw.commons.customselector.ui.selector.CustomSelectorActivity;
|
||||
import fr.free.nrw.commons.description.DescriptionEditActivity;
|
||||
import fr.free.nrw.commons.explore.depictions.WikidataItemDetailsActivity;
|
||||
import fr.free.nrw.commons.explore.SearchActivity;
|
||||
import fr.free.nrw.commons.media.ZoomableActivity;
|
||||
import fr.free.nrw.commons.nearby.WikidataFeedback;
|
||||
import fr.free.nrw.commons.notification.NotificationActivity;
|
||||
import fr.free.nrw.commons.profile.ProfileActivity;
|
||||
import fr.free.nrw.commons.review.ReviewActivity;
|
||||
import fr.free.nrw.commons.settings.SettingsActivity;
|
||||
import fr.free.nrw.commons.upload.UploadActivity;
|
||||
import fr.free.nrw.commons.upload.UploadProgressActivity;
|
||||
|
||||
/**
|
||||
* This Class handles the dependency injection (using dagger)
|
||||
* so, if a developer needs to add a new activity to the commons app
|
||||
* then that must be mentioned here to inject the dependencies
|
||||
*/
|
||||
@Module
|
||||
@SuppressWarnings({"WeakerAccess", "unused"})
|
||||
public abstract class ActivityBuilderModule {
|
||||
|
||||
@ContributesAndroidInjector
|
||||
abstract LoginActivity bindLoginActivity();
|
||||
|
||||
@ContributesAndroidInjector
|
||||
abstract WelcomeActivity bindWelcomeActivity();
|
||||
|
||||
@ContributesAndroidInjector
|
||||
abstract MainActivity bindContributionsActivity();
|
||||
|
||||
@ContributesAndroidInjector
|
||||
abstract CustomSelectorActivity bindCustomSelectorActivity();
|
||||
|
||||
@ContributesAndroidInjector
|
||||
abstract SettingsActivity bindSettingsActivity();
|
||||
|
||||
@ContributesAndroidInjector
|
||||
abstract AboutActivity bindAboutActivity();
|
||||
|
||||
@ContributesAndroidInjector
|
||||
abstract LocationPickerActivity bindLocationPickerActivity();
|
||||
|
||||
@ContributesAndroidInjector
|
||||
abstract SignupActivity bindSignupActivity();
|
||||
|
||||
@ContributesAndroidInjector
|
||||
abstract NotificationActivity bindNotificationActivity();
|
||||
|
||||
@ContributesAndroidInjector
|
||||
abstract UploadActivity bindUploadActivity();
|
||||
|
||||
@ContributesAndroidInjector
|
||||
abstract SearchActivity bindSearchActivity();
|
||||
|
||||
@ContributesAndroidInjector
|
||||
abstract CategoryDetailsActivity bindCategoryDetailsActivity();
|
||||
|
||||
@ContributesAndroidInjector
|
||||
abstract WikidataItemDetailsActivity bindDepictionDetailsActivity();
|
||||
|
||||
@ContributesAndroidInjector
|
||||
abstract ProfileActivity bindAchievementsActivity();
|
||||
|
||||
@ContributesAndroidInjector
|
||||
abstract ReviewActivity bindReviewActivity();
|
||||
|
||||
@ContributesAndroidInjector
|
||||
abstract DescriptionEditActivity bindDescriptionEditActivity();
|
||||
|
||||
@ContributesAndroidInjector
|
||||
abstract ZoomableActivity bindZoomableActivity();
|
||||
|
||||
@ContributesAndroidInjector
|
||||
abstract UploadProgressActivity bindUploadProgressActivity();
|
||||
|
||||
@ContributesAndroidInjector
|
||||
abstract WikidataFeedback bindWikiFeedback();
|
||||
}
|
||||
|
|
@ -0,0 +1,89 @@
|
|||
package fr.free.nrw.commons.di
|
||||
|
||||
import dagger.Module
|
||||
import dagger.android.ContributesAndroidInjector
|
||||
import fr.free.nrw.commons.AboutActivity
|
||||
import fr.free.nrw.commons.LocationPicker.LocationPickerActivity
|
||||
import fr.free.nrw.commons.WelcomeActivity
|
||||
import fr.free.nrw.commons.auth.LoginActivity
|
||||
import fr.free.nrw.commons.auth.SignupActivity
|
||||
import fr.free.nrw.commons.category.CategoryDetailsActivity
|
||||
import fr.free.nrw.commons.contributions.MainActivity
|
||||
import fr.free.nrw.commons.customselector.ui.selector.CustomSelectorActivity
|
||||
import fr.free.nrw.commons.description.DescriptionEditActivity
|
||||
import fr.free.nrw.commons.explore.SearchActivity
|
||||
import fr.free.nrw.commons.explore.depictions.WikidataItemDetailsActivity
|
||||
import fr.free.nrw.commons.media.ZoomableActivity
|
||||
import fr.free.nrw.commons.nearby.WikidataFeedback
|
||||
import fr.free.nrw.commons.notification.NotificationActivity
|
||||
import fr.free.nrw.commons.profile.ProfileActivity
|
||||
import fr.free.nrw.commons.review.ReviewActivity
|
||||
import fr.free.nrw.commons.settings.SettingsActivity
|
||||
import fr.free.nrw.commons.upload.UploadActivity
|
||||
import fr.free.nrw.commons.upload.UploadProgressActivity
|
||||
|
||||
/**
|
||||
* This Class handles the dependency injection (using dagger)
|
||||
* so, if a developer needs to add a new activity to the commons app
|
||||
* then that must be mentioned here to inject the dependencies
|
||||
*/
|
||||
@Module
|
||||
@Suppress("unused")
|
||||
abstract class ActivityBuilderModule {
|
||||
@ContributesAndroidInjector
|
||||
abstract fun bindLoginActivity(): LoginActivity
|
||||
|
||||
@ContributesAndroidInjector
|
||||
abstract fun bindWelcomeActivity(): WelcomeActivity
|
||||
|
||||
@ContributesAndroidInjector
|
||||
abstract fun bindContributionsActivity(): MainActivity
|
||||
|
||||
@ContributesAndroidInjector
|
||||
abstract fun bindCustomSelectorActivity(): CustomSelectorActivity
|
||||
|
||||
@ContributesAndroidInjector
|
||||
abstract fun bindSettingsActivity(): SettingsActivity
|
||||
|
||||
@ContributesAndroidInjector
|
||||
abstract fun bindAboutActivity(): AboutActivity
|
||||
|
||||
@ContributesAndroidInjector
|
||||
abstract fun bindLocationPickerActivity(): LocationPickerActivity
|
||||
|
||||
@ContributesAndroidInjector
|
||||
abstract fun bindSignupActivity(): SignupActivity
|
||||
|
||||
@ContributesAndroidInjector
|
||||
abstract fun bindNotificationActivity(): NotificationActivity
|
||||
|
||||
@ContributesAndroidInjector
|
||||
abstract fun bindUploadActivity(): UploadActivity
|
||||
|
||||
@ContributesAndroidInjector
|
||||
abstract fun bindSearchActivity(): SearchActivity
|
||||
|
||||
@ContributesAndroidInjector
|
||||
abstract fun bindCategoryDetailsActivity(): CategoryDetailsActivity
|
||||
|
||||
@ContributesAndroidInjector
|
||||
abstract fun bindDepictionDetailsActivity(): WikidataItemDetailsActivity
|
||||
|
||||
@ContributesAndroidInjector
|
||||
abstract fun bindAchievementsActivity(): ProfileActivity
|
||||
|
||||
@ContributesAndroidInjector
|
||||
abstract fun bindReviewActivity(): ReviewActivity
|
||||
|
||||
@ContributesAndroidInjector
|
||||
abstract fun bindDescriptionEditActivity(): DescriptionEditActivity
|
||||
|
||||
@ContributesAndroidInjector
|
||||
abstract fun bindZoomableActivity(): ZoomableActivity
|
||||
|
||||
@ContributesAndroidInjector
|
||||
abstract fun bindUploadProgressActivity(): UploadProgressActivity
|
||||
|
||||
@ContributesAndroidInjector
|
||||
abstract fun bindWikiFeedback(): WikidataFeedback
|
||||
}
|
||||
|
|
@ -1,105 +0,0 @@
|
|||
package fr.free.nrw.commons.di;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.Service;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.ContentProvider;
|
||||
import android.content.Context;
|
||||
|
||||
import androidx.fragment.app.Fragment;
|
||||
|
||||
import dagger.android.HasAndroidInjector;
|
||||
import javax.inject.Inject;
|
||||
|
||||
import dagger.android.AndroidInjector;
|
||||
import dagger.android.DispatchingAndroidInjector;
|
||||
import dagger.android.HasActivityInjector;
|
||||
import dagger.android.HasBroadcastReceiverInjector;
|
||||
import dagger.android.HasContentProviderInjector;
|
||||
import dagger.android.HasFragmentInjector;
|
||||
import dagger.android.HasServiceInjector;
|
||||
import dagger.android.support.HasSupportFragmentInjector;
|
||||
|
||||
/**
|
||||
* Provides injectors for all sorts of components
|
||||
* Ex: Activities, Fragments, Services, ContentProviders
|
||||
*/
|
||||
public class ApplicationlessInjection
|
||||
implements
|
||||
HasAndroidInjector,
|
||||
HasActivityInjector,
|
||||
HasFragmentInjector,
|
||||
HasSupportFragmentInjector,
|
||||
HasServiceInjector,
|
||||
HasBroadcastReceiverInjector,
|
||||
HasContentProviderInjector {
|
||||
|
||||
private static ApplicationlessInjection instance = null;
|
||||
|
||||
@Inject DispatchingAndroidInjector<Object> androidInjector;
|
||||
@Inject DispatchingAndroidInjector<Activity> activityInjector;
|
||||
@Inject DispatchingAndroidInjector<BroadcastReceiver> broadcastReceiverInjector;
|
||||
@Inject DispatchingAndroidInjector<android.app.Fragment> fragmentInjector;
|
||||
@Inject DispatchingAndroidInjector<Fragment> supportFragmentInjector;
|
||||
@Inject DispatchingAndroidInjector<Service> serviceInjector;
|
||||
@Inject DispatchingAndroidInjector<ContentProvider> contentProviderInjector;
|
||||
|
||||
private CommonsApplicationComponent commonsApplicationComponent;
|
||||
|
||||
public ApplicationlessInjection(Context applicationContext) {
|
||||
commonsApplicationComponent = DaggerCommonsApplicationComponent.builder()
|
||||
.appModule(new CommonsApplicationModule(applicationContext)).build();
|
||||
commonsApplicationComponent.inject(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AndroidInjector<Object> androidInjector() {
|
||||
return androidInjector;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DispatchingAndroidInjector<Activity> activityInjector() {
|
||||
return activityInjector;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DispatchingAndroidInjector<android.app.Fragment> fragmentInjector() {
|
||||
return fragmentInjector;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DispatchingAndroidInjector<Fragment> supportFragmentInjector() {
|
||||
return supportFragmentInjector;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DispatchingAndroidInjector<BroadcastReceiver> broadcastReceiverInjector() {
|
||||
return broadcastReceiverInjector;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DispatchingAndroidInjector<Service> serviceInjector() {
|
||||
return serviceInjector;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AndroidInjector<ContentProvider> contentProviderInjector() {
|
||||
return contentProviderInjector;
|
||||
}
|
||||
|
||||
public CommonsApplicationComponent getCommonsApplicationComponent() {
|
||||
return commonsApplicationComponent;
|
||||
}
|
||||
|
||||
public static ApplicationlessInjection getInstance(Context applicationContext) {
|
||||
if (instance == null) {
|
||||
synchronized (ApplicationlessInjection.class) {
|
||||
if (instance == null) {
|
||||
instance = new ApplicationlessInjection(applicationContext);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return instance;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,98 @@
|
|||
package fr.free.nrw.commons.di
|
||||
|
||||
import android.app.Activity
|
||||
import android.app.Fragment
|
||||
import android.app.Service
|
||||
import android.content.BroadcastReceiver
|
||||
import android.content.ContentProvider
|
||||
import android.content.Context
|
||||
import dagger.android.AndroidInjector
|
||||
import dagger.android.DispatchingAndroidInjector
|
||||
import dagger.android.HasActivityInjector
|
||||
import dagger.android.HasAndroidInjector
|
||||
import dagger.android.HasBroadcastReceiverInjector
|
||||
import dagger.android.HasContentProviderInjector
|
||||
import dagger.android.HasFragmentInjector
|
||||
import dagger.android.HasServiceInjector
|
||||
import dagger.android.support.HasSupportFragmentInjector
|
||||
import javax.inject.Inject
|
||||
import androidx.fragment.app.Fragment as AndroidXFragmen
|
||||
|
||||
/**
|
||||
* Provides injectors for all sorts of components
|
||||
* Ex: Activities, Fragments, Services, ContentProviders
|
||||
*/
|
||||
class ApplicationlessInjection(applicationContext: Context) : HasAndroidInjector,
|
||||
HasActivityInjector, HasFragmentInjector, HasSupportFragmentInjector, HasServiceInjector,
|
||||
HasBroadcastReceiverInjector, HasContentProviderInjector {
|
||||
@Inject @JvmField
|
||||
var androidInjector: DispatchingAndroidInjector<Any>? = null
|
||||
|
||||
@Inject @JvmField
|
||||
var activityInjector: DispatchingAndroidInjector<Activity>? = null
|
||||
|
||||
@Inject @JvmField
|
||||
var broadcastReceiverInjector: DispatchingAndroidInjector<BroadcastReceiver>? = null
|
||||
|
||||
@Inject @JvmField
|
||||
var fragmentInjector: DispatchingAndroidInjector<Fragment>? = null
|
||||
|
||||
@Inject @JvmField
|
||||
var supportFragmentInjector: DispatchingAndroidInjector<AndroidXFragmen>? = null
|
||||
|
||||
@Inject @JvmField
|
||||
var serviceInjector: DispatchingAndroidInjector<Service>? = null
|
||||
|
||||
@Inject @JvmField
|
||||
var contentProviderInjector: DispatchingAndroidInjector<ContentProvider>? = null
|
||||
|
||||
val instance: ApplicationlessInjection get() = _instance!!
|
||||
|
||||
val commonsApplicationComponent: CommonsApplicationComponent =
|
||||
DaggerCommonsApplicationComponent
|
||||
.builder()
|
||||
.appModule(CommonsApplicationModule(applicationContext))
|
||||
.build()
|
||||
|
||||
init {
|
||||
commonsApplicationComponent.inject(this)
|
||||
}
|
||||
|
||||
override fun androidInjector(): AndroidInjector<Any>? =
|
||||
androidInjector
|
||||
|
||||
override fun activityInjector(): DispatchingAndroidInjector<Activity>? =
|
||||
activityInjector
|
||||
|
||||
override fun fragmentInjector(): DispatchingAndroidInjector<Fragment>? =
|
||||
fragmentInjector
|
||||
|
||||
override fun supportFragmentInjector(): DispatchingAndroidInjector<AndroidXFragmen>? =
|
||||
supportFragmentInjector
|
||||
|
||||
override fun broadcastReceiverInjector(): DispatchingAndroidInjector<BroadcastReceiver>? =
|
||||
broadcastReceiverInjector
|
||||
|
||||
override fun serviceInjector(): DispatchingAndroidInjector<Service>? =
|
||||
serviceInjector
|
||||
|
||||
override fun contentProviderInjector(): AndroidInjector<ContentProvider>? =
|
||||
contentProviderInjector
|
||||
|
||||
companion object {
|
||||
private var _instance: ApplicationlessInjection? = null
|
||||
|
||||
@JvmStatic
|
||||
fun getInstance(applicationContext: Context): ApplicationlessInjection {
|
||||
if (_instance == null) {
|
||||
synchronized(ApplicationlessInjection::class.java) {
|
||||
if (_instance == null) {
|
||||
_instance = ApplicationlessInjection(applicationContext)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return _instance!!
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,85 +0,0 @@
|
|||
package fr.free.nrw.commons.di;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
|
||||
import fr.free.nrw.commons.explore.categories.CategoriesModule;
|
||||
import fr.free.nrw.commons.navtab.MoreBottomSheetFragment;
|
||||
import fr.free.nrw.commons.navtab.MoreBottomSheetLoggedOutFragment;
|
||||
import fr.free.nrw.commons.nearby.NearbyController;
|
||||
import fr.free.nrw.commons.upload.worker.UploadWorker;
|
||||
import javax.inject.Singleton;
|
||||
|
||||
import dagger.Component;
|
||||
import dagger.android.AndroidInjectionModule;
|
||||
import dagger.android.AndroidInjector;
|
||||
import dagger.android.support.AndroidSupportInjectionModule;
|
||||
import fr.free.nrw.commons.CommonsApplication;
|
||||
import fr.free.nrw.commons.auth.LoginActivity;
|
||||
import fr.free.nrw.commons.contributions.ContributionsModule;
|
||||
import fr.free.nrw.commons.explore.depictions.DepictionModule;
|
||||
import fr.free.nrw.commons.explore.SearchModule;
|
||||
import fr.free.nrw.commons.review.ReviewController;
|
||||
import fr.free.nrw.commons.settings.SettingsFragment;
|
||||
import fr.free.nrw.commons.upload.FileProcessor;
|
||||
import fr.free.nrw.commons.upload.UploadModule;
|
||||
import fr.free.nrw.commons.widget.PicOfDayAppWidget;
|
||||
|
||||
|
||||
/**
|
||||
* Facilitates Injection from CommonsApplicationModule to all the
|
||||
* classes seeking a dependency to be injected
|
||||
*/
|
||||
@Singleton
|
||||
@Component(modules = {
|
||||
CommonsApplicationModule.class,
|
||||
NetworkingModule.class,
|
||||
AndroidInjectionModule.class,
|
||||
AndroidSupportInjectionModule.class,
|
||||
ActivityBuilderModule.class,
|
||||
FragmentBuilderModule.class,
|
||||
ServiceBuilderModule.class,
|
||||
ContentProviderBuilderModule.class,
|
||||
UploadModule.class,
|
||||
ContributionsModule.class,
|
||||
SearchModule.class,
|
||||
DepictionModule.class,
|
||||
CategoriesModule.class
|
||||
})
|
||||
public interface CommonsApplicationComponent extends AndroidInjector<ApplicationlessInjection> {
|
||||
void inject(CommonsApplication application);
|
||||
|
||||
void inject(UploadWorker worker);
|
||||
|
||||
void inject(LoginActivity activity);
|
||||
|
||||
void inject(SettingsFragment fragment);
|
||||
|
||||
void inject(MoreBottomSheetFragment fragment);
|
||||
|
||||
void inject(MoreBottomSheetLoggedOutFragment fragment);
|
||||
|
||||
void inject(ReviewController reviewController);
|
||||
|
||||
//void inject(NavTabLayout view);
|
||||
|
||||
@Override
|
||||
void inject(ApplicationlessInjection instance);
|
||||
|
||||
void inject(FileProcessor fileProcessor);
|
||||
|
||||
void inject(PicOfDayAppWidget picOfDayAppWidget);
|
||||
|
||||
@Singleton
|
||||
void inject(NearbyController nearbyController);
|
||||
|
||||
Gson gson();
|
||||
|
||||
@Component.Builder
|
||||
@SuppressWarnings({"WeakerAccess", "unused"})
|
||||
interface Builder {
|
||||
|
||||
Builder appModule(CommonsApplicationModule applicationModule);
|
||||
|
||||
CommonsApplicationComponent build();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,80 @@
|
|||
package fr.free.nrw.commons.di
|
||||
|
||||
import com.google.gson.Gson
|
||||
import dagger.Component
|
||||
import dagger.android.AndroidInjectionModule
|
||||
import dagger.android.AndroidInjector
|
||||
import dagger.android.support.AndroidSupportInjectionModule
|
||||
import fr.free.nrw.commons.CommonsApplication
|
||||
import fr.free.nrw.commons.auth.LoginActivity
|
||||
import fr.free.nrw.commons.contributions.ContributionsModule
|
||||
import fr.free.nrw.commons.explore.SearchModule
|
||||
import fr.free.nrw.commons.explore.categories.CategoriesModule
|
||||
import fr.free.nrw.commons.explore.depictions.DepictionModule
|
||||
import fr.free.nrw.commons.navtab.MoreBottomSheetFragment
|
||||
import fr.free.nrw.commons.navtab.MoreBottomSheetLoggedOutFragment
|
||||
import fr.free.nrw.commons.nearby.NearbyController
|
||||
import fr.free.nrw.commons.review.ReviewController
|
||||
import fr.free.nrw.commons.settings.SettingsFragment
|
||||
import fr.free.nrw.commons.upload.FileProcessor
|
||||
import fr.free.nrw.commons.upload.UploadModule
|
||||
import fr.free.nrw.commons.upload.worker.UploadWorker
|
||||
import fr.free.nrw.commons.widget.PicOfDayAppWidget
|
||||
import javax.inject.Singleton
|
||||
|
||||
/**
|
||||
* Facilitates Injection from CommonsApplicationModule to all the
|
||||
* classes seeking a dependency to be injected
|
||||
*/
|
||||
@Singleton
|
||||
@Component(
|
||||
modules = [
|
||||
CommonsApplicationModule::class,
|
||||
NetworkingModule::class,
|
||||
AndroidInjectionModule::class,
|
||||
AndroidSupportInjectionModule::class,
|
||||
ActivityBuilderModule::class,
|
||||
FragmentBuilderModule::class,
|
||||
ServiceBuilderModule::class,
|
||||
ContentProviderBuilderModule::class,
|
||||
UploadModule::class,
|
||||
ContributionsModule::class,
|
||||
SearchModule::class,
|
||||
DepictionModule::class,
|
||||
CategoriesModule::class
|
||||
]
|
||||
)
|
||||
interface CommonsApplicationComponent : AndroidInjector<ApplicationlessInjection> {
|
||||
fun inject(application: CommonsApplication)
|
||||
|
||||
fun inject(worker: UploadWorker)
|
||||
|
||||
fun inject(activity: LoginActivity)
|
||||
|
||||
fun inject(fragment: SettingsFragment)
|
||||
|
||||
fun inject(fragment: MoreBottomSheetFragment)
|
||||
|
||||
fun inject(fragment: MoreBottomSheetLoggedOutFragment)
|
||||
|
||||
fun inject(reviewController: ReviewController)
|
||||
|
||||
override fun inject(instance: ApplicationlessInjection)
|
||||
|
||||
fun inject(fileProcessor: FileProcessor)
|
||||
|
||||
fun inject(picOfDayAppWidget: PicOfDayAppWidget)
|
||||
|
||||
@Singleton
|
||||
fun inject(nearbyController: NearbyController)
|
||||
|
||||
fun gson(): Gson
|
||||
|
||||
@Component.Builder
|
||||
@Suppress("unused")
|
||||
interface Builder {
|
||||
fun appModule(applicationModule: CommonsApplicationModule): Builder
|
||||
|
||||
fun build(): CommonsApplicationComponent
|
||||
}
|
||||
}
|
||||
|
|
@ -1,314 +0,0 @@
|
|||
package fr.free.nrw.commons.di;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.ContentProviderClient;
|
||||
import android.content.ContentResolver;
|
||||
import android.content.Context;
|
||||
import android.view.inputmethod.InputMethodManager;
|
||||
import androidx.collection.LruCache;
|
||||
import androidx.room.Room;
|
||||
import androidx.room.migration.Migration;
|
||||
import androidx.sqlite.db.SupportSQLiteDatabase;
|
||||
import com.google.gson.Gson;
|
||||
import dagger.Module;
|
||||
import dagger.Provides;
|
||||
import fr.free.nrw.commons.BuildConfig;
|
||||
import fr.free.nrw.commons.R;
|
||||
import fr.free.nrw.commons.auth.SessionManager;
|
||||
import fr.free.nrw.commons.contributions.ContributionDao;
|
||||
import fr.free.nrw.commons.customselector.database.NotForUploadStatusDao;
|
||||
import fr.free.nrw.commons.customselector.database.UploadedStatusDao;
|
||||
import fr.free.nrw.commons.customselector.ui.selector.ImageFileLoader;
|
||||
import fr.free.nrw.commons.data.DBOpenHelper;
|
||||
import fr.free.nrw.commons.db.AppDatabase;
|
||||
import fr.free.nrw.commons.kvstore.JsonKvStore;
|
||||
import fr.free.nrw.commons.location.LocationServiceManager;
|
||||
import fr.free.nrw.commons.nearby.PlaceDao;
|
||||
import fr.free.nrw.commons.review.ReviewDao;
|
||||
import fr.free.nrw.commons.settings.Prefs;
|
||||
import fr.free.nrw.commons.upload.UploadController;
|
||||
import fr.free.nrw.commons.upload.depicts.DepictsDao;
|
||||
import fr.free.nrw.commons.utils.ConfigUtils;
|
||||
import fr.free.nrw.commons.wikidata.WikidataEditListener;
|
||||
import fr.free.nrw.commons.wikidata.WikidataEditListenerImpl;
|
||||
import io.reactivex.Scheduler;
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers;
|
||||
import io.reactivex.schedulers.Schedulers;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import javax.inject.Named;
|
||||
import javax.inject.Singleton;
|
||||
|
||||
/**
|
||||
* The Dependency Provider class for Commons Android.
|
||||
*
|
||||
* Provides all sorts of ContentProviderClients used by the app
|
||||
* along with the Liscences, AccountUtility, UploadController, Logged User,
|
||||
* Location manager etc
|
||||
*/
|
||||
@Module
|
||||
@SuppressWarnings({"WeakerAccess", "unused"})
|
||||
public class CommonsApplicationModule {
|
||||
private Context applicationContext;
|
||||
public static final String IO_THREAD="io_thread";
|
||||
public static final String MAIN_THREAD="main_thread";
|
||||
private AppDatabase appDatabase;
|
||||
|
||||
static final Migration MIGRATION_1_2 = new Migration(1, 2) {
|
||||
@Override
|
||||
public void migrate(SupportSQLiteDatabase database) {
|
||||
database.execSQL("ALTER TABLE contribution "
|
||||
+ " ADD COLUMN hasInvalidLocation INTEGER NOT NULL DEFAULT 0");
|
||||
}
|
||||
};
|
||||
|
||||
public CommonsApplicationModule(Context applicationContext) {
|
||||
this.applicationContext = applicationContext;
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides ImageFileLoader used to fetch device images.
|
||||
* @param context
|
||||
* @return
|
||||
*/
|
||||
@Provides
|
||||
public ImageFileLoader providesImageFileLoader(Context context) {
|
||||
return new ImageFileLoader(context);
|
||||
}
|
||||
|
||||
@Provides
|
||||
public Context providesApplicationContext() {
|
||||
return this.applicationContext;
|
||||
}
|
||||
|
||||
@Provides
|
||||
public InputMethodManager provideInputMethodManager() {
|
||||
return (InputMethodManager) applicationContext.getSystemService(Activity.INPUT_METHOD_SERVICE);
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Named("licenses")
|
||||
public List<String> provideLicenses(Context context) {
|
||||
List<String> licenseItems = new ArrayList<>();
|
||||
licenseItems.add(context.getString(R.string.license_name_cc0));
|
||||
licenseItems.add(context.getString(R.string.license_name_cc_by));
|
||||
licenseItems.add(context.getString(R.string.license_name_cc_by_sa));
|
||||
licenseItems.add(context.getString(R.string.license_name_cc_by_four));
|
||||
licenseItems.add(context.getString(R.string.license_name_cc_by_sa_four));
|
||||
return licenseItems;
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Named("licenses_by_name")
|
||||
public Map<String, String> provideLicensesByName(Context context) {
|
||||
Map<String, String> byName = new HashMap<>();
|
||||
byName.put(context.getString(R.string.license_name_cc0), Prefs.Licenses.CC0);
|
||||
byName.put(context.getString(R.string.license_name_cc_by), Prefs.Licenses.CC_BY_3);
|
||||
byName.put(context.getString(R.string.license_name_cc_by_sa), Prefs.Licenses.CC_BY_SA_3);
|
||||
byName.put(context.getString(R.string.license_name_cc_by_four), Prefs.Licenses.CC_BY_4);
|
||||
byName.put(context.getString(R.string.license_name_cc_by_sa_four), Prefs.Licenses.CC_BY_SA_4);
|
||||
return byName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides an instance of CategoryContentProviderClient i.e. the categories
|
||||
* that are there in local storage
|
||||
*/
|
||||
@Provides
|
||||
@Named("category")
|
||||
public ContentProviderClient provideCategoryContentProviderClient(Context context) {
|
||||
return context.getContentResolver().acquireContentProviderClient(BuildConfig.CATEGORY_AUTHORITY);
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is used to provide instance of RecentSearchContentProviderClient
|
||||
* which provides content of Recent Searches from database
|
||||
* @param context
|
||||
* @return returns RecentSearchContentProviderClient
|
||||
*/
|
||||
@Provides
|
||||
@Named("recentsearch")
|
||||
public ContentProviderClient provideRecentSearchContentProviderClient(Context context) {
|
||||
return context.getContentResolver().acquireContentProviderClient(BuildConfig.RECENT_SEARCH_AUTHORITY);
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Named("contribution")
|
||||
public ContentProviderClient provideContributionContentProviderClient(Context context) {
|
||||
return context.getContentResolver().acquireContentProviderClient(BuildConfig.CONTRIBUTION_AUTHORITY);
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Named("modification")
|
||||
public ContentProviderClient provideModificationContentProviderClient(Context context) {
|
||||
return context.getContentResolver().acquireContentProviderClient(BuildConfig.MODIFICATION_AUTHORITY);
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Named("bookmarks")
|
||||
public ContentProviderClient provideBookmarkContentProviderClient(Context context) {
|
||||
return context.getContentResolver().acquireContentProviderClient(BuildConfig.BOOKMARK_AUTHORITY);
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Named("bookmarksLocation")
|
||||
public ContentProviderClient provideBookmarkLocationContentProviderClient(Context context) {
|
||||
return context.getContentResolver().acquireContentProviderClient(BuildConfig.BOOKMARK_LOCATIONS_AUTHORITY);
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Named("bookmarksItem")
|
||||
public ContentProviderClient provideBookmarkItemContentProviderClient(Context context) {
|
||||
return context.getContentResolver().acquireContentProviderClient(BuildConfig.BOOKMARK_ITEMS_AUTHORITY);
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is used to provide instance of RecentLanguagesContentProvider
|
||||
* which provides content of recent used languages from database
|
||||
* @param context Context
|
||||
* @return returns RecentLanguagesContentProvider
|
||||
*/
|
||||
@Provides
|
||||
@Named("recent_languages")
|
||||
public ContentProviderClient provideRecentLanguagesContentProviderClient(final Context context) {
|
||||
return context.getContentResolver()
|
||||
.acquireContentProviderClient(BuildConfig.RECENT_LANGUAGE_AUTHORITY);
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides a Json store instance(JsonKvStore) which keeps
|
||||
* the provided Gson in it's instance
|
||||
* @param gson stored inside the store instance
|
||||
*/
|
||||
@Provides
|
||||
@Named("default_preferences")
|
||||
public JsonKvStore providesDefaultKvStore(Context context, Gson gson) {
|
||||
String storeName = context.getPackageName() + "_preferences";
|
||||
return new JsonKvStore(context, storeName, gson);
|
||||
}
|
||||
|
||||
@Provides
|
||||
public UploadController providesUploadController(SessionManager sessionManager,
|
||||
@Named("default_preferences") JsonKvStore kvStore,
|
||||
Context context, ContributionDao contributionDao) {
|
||||
return new UploadController(sessionManager, context, kvStore);
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
public LocationServiceManager provideLocationServiceManager(Context context) {
|
||||
return new LocationServiceManager(context);
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
public DBOpenHelper provideDBOpenHelper(Context context) {
|
||||
return new DBOpenHelper(context);
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
@Named("thumbnail-cache")
|
||||
public LruCache<String, String> provideLruCache() {
|
||||
return new LruCache<>(1024);
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
public WikidataEditListener provideWikidataEditListener() {
|
||||
return new WikidataEditListenerImpl();
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides app flavour. Can be used to alter flows in the app
|
||||
* @return
|
||||
*/
|
||||
@Named("isBeta")
|
||||
@Provides
|
||||
@Singleton
|
||||
public boolean provideIsBetaVariant() {
|
||||
return ConfigUtils.isBetaFlavour();
|
||||
}
|
||||
|
||||
/**
|
||||
* Provide JavaRx IO scheduler which manages IO operations
|
||||
* across various Threads
|
||||
*/
|
||||
@Named(IO_THREAD)
|
||||
@Provides
|
||||
public Scheduler providesIoThread(){
|
||||
return Schedulers.io();
|
||||
}
|
||||
|
||||
@Named(MAIN_THREAD)
|
||||
@Provides
|
||||
public Scheduler providesMainThread() {
|
||||
return AndroidSchedulers.mainThread();
|
||||
}
|
||||
|
||||
@Named("username")
|
||||
@Provides
|
||||
public String provideLoggedInUsername(SessionManager sessionManager) {
|
||||
return Objects.toString(sessionManager.getUserName(), "");
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
public AppDatabase provideAppDataBase() {
|
||||
appDatabase = Room.databaseBuilder(applicationContext, AppDatabase.class, "commons_room.db")
|
||||
.addMigrations(MIGRATION_1_2)
|
||||
.fallbackToDestructiveMigration()
|
||||
.build();
|
||||
return appDatabase;
|
||||
}
|
||||
|
||||
@Provides
|
||||
public ContributionDao providesContributionsDao(AppDatabase appDatabase) {
|
||||
return appDatabase.contributionDao();
|
||||
}
|
||||
|
||||
@Provides
|
||||
public PlaceDao providesPlaceDao(AppDatabase appDatabase) {
|
||||
return appDatabase.PlaceDao();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the reference of DepictsDao class.
|
||||
*/
|
||||
@Provides
|
||||
public DepictsDao providesDepictDao(AppDatabase appDatabase) {
|
||||
return appDatabase.DepictsDao();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the reference of UploadedStatus class.
|
||||
*/
|
||||
@Provides
|
||||
public UploadedStatusDao providesUploadedStatusDao(AppDatabase appDatabase) {
|
||||
return appDatabase.UploadedStatusDao();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the reference of NotForUploadStatus class.
|
||||
*/
|
||||
@Provides
|
||||
public NotForUploadStatusDao providesNotForUploadStatusDao(AppDatabase appDatabase) {
|
||||
return appDatabase.NotForUploadStatusDao();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the reference of ReviewDao class
|
||||
*/
|
||||
@Provides
|
||||
public ReviewDao providesReviewDao(AppDatabase appDatabase){
|
||||
return appDatabase.ReviewDao();
|
||||
}
|
||||
|
||||
@Provides
|
||||
public ContentResolver providesContentResolver(Context context){
|
||||
return context.getContentResolver();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,239 @@
|
|||
package fr.free.nrw.commons.di
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.ContentProviderClient
|
||||
import android.content.ContentResolver
|
||||
import android.content.Context
|
||||
import android.view.inputmethod.InputMethodManager
|
||||
import androidx.collection.LruCache
|
||||
import androidx.room.Room.databaseBuilder
|
||||
import androidx.room.migration.Migration
|
||||
import androidx.sqlite.db.SupportSQLiteDatabase
|
||||
import com.google.gson.Gson
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
import fr.free.nrw.commons.BuildConfig
|
||||
import fr.free.nrw.commons.R
|
||||
import fr.free.nrw.commons.auth.SessionManager
|
||||
import fr.free.nrw.commons.contributions.ContributionDao
|
||||
import fr.free.nrw.commons.customselector.database.NotForUploadStatusDao
|
||||
import fr.free.nrw.commons.customselector.database.UploadedStatusDao
|
||||
import fr.free.nrw.commons.customselector.ui.selector.ImageFileLoader
|
||||
import fr.free.nrw.commons.data.DBOpenHelper
|
||||
import fr.free.nrw.commons.db.AppDatabase
|
||||
import fr.free.nrw.commons.kvstore.JsonKvStore
|
||||
import fr.free.nrw.commons.location.LocationServiceManager
|
||||
import fr.free.nrw.commons.nearby.PlaceDao
|
||||
import fr.free.nrw.commons.review.ReviewDao
|
||||
import fr.free.nrw.commons.settings.Prefs
|
||||
import fr.free.nrw.commons.upload.UploadController
|
||||
import fr.free.nrw.commons.upload.depicts.DepictsDao
|
||||
import fr.free.nrw.commons.utils.ConfigUtils.isBetaFlavour
|
||||
import fr.free.nrw.commons.wikidata.WikidataEditListener
|
||||
import fr.free.nrw.commons.wikidata.WikidataEditListenerImpl
|
||||
import io.reactivex.Scheduler
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers
|
||||
import io.reactivex.schedulers.Schedulers
|
||||
import java.util.Objects
|
||||
import javax.inject.Named
|
||||
import javax.inject.Singleton
|
||||
|
||||
/**
|
||||
* The Dependency Provider class for Commons Android.
|
||||
* Provides all sorts of ContentProviderClients used by the app
|
||||
* along with the Liscences, AccountUtility, UploadController, Logged User,
|
||||
* Location manager etc
|
||||
*/
|
||||
@Module
|
||||
@Suppress("unused")
|
||||
open class CommonsApplicationModule(private val applicationContext: Context) {
|
||||
@Provides
|
||||
fun providesImageFileLoader(context: Context): ImageFileLoader =
|
||||
ImageFileLoader(context)
|
||||
|
||||
@Provides
|
||||
fun providesApplicationContext(): Context =
|
||||
applicationContext
|
||||
|
||||
@Provides
|
||||
fun provideInputMethodManager(): InputMethodManager =
|
||||
applicationContext.getSystemService(Activity.INPUT_METHOD_SERVICE) as InputMethodManager
|
||||
|
||||
@Provides
|
||||
@Named("licenses")
|
||||
fun provideLicenses(context: Context): List<String> = listOf(
|
||||
context.getString(R.string.license_name_cc0),
|
||||
context.getString(R.string.license_name_cc_by),
|
||||
context.getString(R.string.license_name_cc_by_sa),
|
||||
context.getString(R.string.license_name_cc_by_four),
|
||||
context.getString(R.string.license_name_cc_by_sa_four)
|
||||
)
|
||||
|
||||
@Provides
|
||||
@Named("licenses_by_name")
|
||||
fun provideLicensesByName(context: Context): Map<String, String> = mapOf(
|
||||
context.getString(R.string.license_name_cc0) to Prefs.Licenses.CC0,
|
||||
context.getString(R.string.license_name_cc_by) to Prefs.Licenses.CC_BY_3,
|
||||
context.getString(R.string.license_name_cc_by_sa) to Prefs.Licenses.CC_BY_SA_3,
|
||||
context.getString(R.string.license_name_cc_by_four) to Prefs.Licenses.CC_BY_4,
|
||||
context.getString(R.string.license_name_cc_by_sa_four) to Prefs.Licenses.CC_BY_SA_4
|
||||
)
|
||||
|
||||
/**
|
||||
* Provides an instance of CategoryContentProviderClient i.e. the categories
|
||||
* that are there in local storage
|
||||
*/
|
||||
@Provides
|
||||
@Named("category")
|
||||
open fun provideCategoryContentProviderClient(context: Context): ContentProviderClient? =
|
||||
context.contentResolver.acquireContentProviderClient(BuildConfig.CATEGORY_AUTHORITY)
|
||||
|
||||
@Provides
|
||||
@Named("recentsearch")
|
||||
fun provideRecentSearchContentProviderClient(context: Context): ContentProviderClient? =
|
||||
context.contentResolver.acquireContentProviderClient(BuildConfig.RECENT_SEARCH_AUTHORITY)
|
||||
|
||||
@Provides
|
||||
@Named("contribution")
|
||||
open fun provideContributionContentProviderClient(context: Context): ContentProviderClient? =
|
||||
context.contentResolver.acquireContentProviderClient(BuildConfig.CONTRIBUTION_AUTHORITY)
|
||||
|
||||
@Provides
|
||||
@Named("modification")
|
||||
open fun provideModificationContentProviderClient(context: Context): ContentProviderClient? =
|
||||
context.contentResolver.acquireContentProviderClient(BuildConfig.MODIFICATION_AUTHORITY)
|
||||
|
||||
@Provides
|
||||
@Named("bookmarks")
|
||||
fun provideBookmarkContentProviderClient(context: Context): ContentProviderClient? =
|
||||
context.contentResolver.acquireContentProviderClient(BuildConfig.BOOKMARK_AUTHORITY)
|
||||
|
||||
@Provides
|
||||
@Named("bookmarksLocation")
|
||||
fun provideBookmarkLocationContentProviderClient(context: Context): ContentProviderClient? =
|
||||
context.contentResolver.acquireContentProviderClient(BuildConfig.BOOKMARK_LOCATIONS_AUTHORITY)
|
||||
|
||||
@Provides
|
||||
@Named("bookmarksItem")
|
||||
fun provideBookmarkItemContentProviderClient(context: Context): ContentProviderClient? =
|
||||
context.contentResolver.acquireContentProviderClient(BuildConfig.BOOKMARK_ITEMS_AUTHORITY)
|
||||
|
||||
/**
|
||||
* This method is used to provide instance of RecentLanguagesContentProvider
|
||||
* which provides content of recent used languages from database
|
||||
* @param context Context
|
||||
* @return returns RecentLanguagesContentProvider
|
||||
*/
|
||||
@Provides
|
||||
@Named("recent_languages")
|
||||
fun provideRecentLanguagesContentProviderClient(context: Context): ContentProviderClient? =
|
||||
context.contentResolver.acquireContentProviderClient(BuildConfig.RECENT_LANGUAGE_AUTHORITY)
|
||||
|
||||
/**
|
||||
* Provides a Json store instance(JsonKvStore) which keeps
|
||||
* the provided Gson in it's instance
|
||||
* @param gson stored inside the store instance
|
||||
*/
|
||||
@Provides
|
||||
@Named("default_preferences")
|
||||
open fun providesDefaultKvStore(context: Context, gson: Gson): JsonKvStore =
|
||||
JsonKvStore(context, "${context.packageName}_preferences", gson)
|
||||
|
||||
@Provides
|
||||
fun providesUploadController(
|
||||
sessionManager: SessionManager,
|
||||
@Named("default_preferences") kvStore: JsonKvStore,
|
||||
context: Context
|
||||
): UploadController = UploadController(sessionManager, context, kvStore)
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
open fun provideLocationServiceManager(context: Context): LocationServiceManager =
|
||||
LocationServiceManager(context)
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
open fun provideDBOpenHelper(context: Context): DBOpenHelper =
|
||||
DBOpenHelper(context)
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
@Named("thumbnail-cache")
|
||||
open fun provideLruCache(): LruCache<String, String> =
|
||||
LruCache(1024)
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideWikidataEditListener(): WikidataEditListener =
|
||||
WikidataEditListenerImpl()
|
||||
|
||||
@Named("isBeta")
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideIsBetaVariant(): Boolean =
|
||||
isBetaFlavour
|
||||
|
||||
@Named(IO_THREAD)
|
||||
@Provides
|
||||
fun providesIoThread(): Scheduler =
|
||||
Schedulers.io()
|
||||
|
||||
@Named(MAIN_THREAD)
|
||||
@Provides
|
||||
fun providesMainThread(): Scheduler =
|
||||
AndroidSchedulers.mainThread()
|
||||
|
||||
@Named("username")
|
||||
@Provides
|
||||
fun provideLoggedInUsername(sessionManager: SessionManager): String =
|
||||
Objects.toString(sessionManager.userName, "")
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideAppDataBase(): AppDatabase = databaseBuilder(
|
||||
applicationContext,
|
||||
AppDatabase::class.java,
|
||||
"commons_room.db"
|
||||
).addMigrations(MIGRATION_1_2).fallbackToDestructiveMigration().build()
|
||||
|
||||
@Provides
|
||||
fun providesContributionsDao(appDatabase: AppDatabase): ContributionDao =
|
||||
appDatabase.contributionDao()
|
||||
|
||||
@Provides
|
||||
fun providesPlaceDao(appDatabase: AppDatabase): PlaceDao =
|
||||
appDatabase.PlaceDao()
|
||||
|
||||
@Provides
|
||||
fun providesDepictDao(appDatabase: AppDatabase): DepictsDao =
|
||||
appDatabase.DepictsDao()
|
||||
|
||||
@Provides
|
||||
fun providesUploadedStatusDao(appDatabase: AppDatabase): UploadedStatusDao =
|
||||
appDatabase.UploadedStatusDao()
|
||||
|
||||
@Provides
|
||||
fun providesNotForUploadStatusDao(appDatabase: AppDatabase): NotForUploadStatusDao =
|
||||
appDatabase.NotForUploadStatusDao()
|
||||
|
||||
@Provides
|
||||
fun providesReviewDao(appDatabase: AppDatabase): ReviewDao =
|
||||
appDatabase.ReviewDao()
|
||||
|
||||
@Provides
|
||||
fun providesContentResolver(context: Context): ContentResolver =
|
||||
context.contentResolver
|
||||
|
||||
companion object {
|
||||
const val IO_THREAD: String = "io_thread"
|
||||
const val MAIN_THREAD: String = "main_thread"
|
||||
|
||||
val MIGRATION_1_2: Migration = object : Migration(1, 2) {
|
||||
override fun migrate(db: SupportSQLiteDatabase) {
|
||||
db.execSQL(
|
||||
"ALTER TABLE contribution " + " ADD COLUMN hasInvalidLocation INTEGER NOT NULL DEFAULT 0"
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,48 +0,0 @@
|
|||
package fr.free.nrw.commons.di;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.os.Bundle;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.fragment.app.Fragment;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import dagger.android.AndroidInjector;
|
||||
import dagger.android.DispatchingAndroidInjector;
|
||||
import dagger.android.support.HasSupportFragmentInjector;
|
||||
|
||||
public abstract class CommonsDaggerAppCompatActivity extends AppCompatActivity implements HasSupportFragmentInjector {
|
||||
|
||||
@Inject
|
||||
DispatchingAndroidInjector<Fragment> supportFragmentInjector;
|
||||
|
||||
@Override
|
||||
protected void onCreate(@Nullable Bundle savedInstanceState) {
|
||||
inject();
|
||||
super.onCreate(savedInstanceState);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AndroidInjector<Fragment> supportFragmentInjector() {
|
||||
return supportFragmentInjector;
|
||||
}
|
||||
|
||||
/**
|
||||
* when this Activity is created it injects an instance of this class inside
|
||||
* activityInjector method of ApplicationlessInjection
|
||||
*/
|
||||
private void inject() {
|
||||
ApplicationlessInjection injection = ApplicationlessInjection.getInstance(getApplicationContext());
|
||||
|
||||
AndroidInjector<Activity> activityInjector = injection.activityInjector();
|
||||
|
||||
if (activityInjector == null) {
|
||||
throw new NullPointerException("ApplicationlessInjection.activityInjector() returned null");
|
||||
}
|
||||
|
||||
activityInjector.inject(this);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
package fr.free.nrw.commons.di
|
||||
|
||||
import android.os.Bundle
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.fragment.app.Fragment
|
||||
import dagger.android.AndroidInjector
|
||||
import dagger.android.DispatchingAndroidInjector
|
||||
import dagger.android.support.HasSupportFragmentInjector
|
||||
import fr.free.nrw.commons.di.ApplicationlessInjection.Companion.getInstance
|
||||
import javax.inject.Inject
|
||||
|
||||
abstract class CommonsDaggerAppCompatActivity : AppCompatActivity(), HasSupportFragmentInjector {
|
||||
@Inject @JvmField
|
||||
var supportFragmentInjector: DispatchingAndroidInjector<Fragment>? = null
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
inject()
|
||||
super.onCreate(savedInstanceState)
|
||||
}
|
||||
|
||||
override fun supportFragmentInjector(): AndroidInjector<Fragment> {
|
||||
return supportFragmentInjector!!
|
||||
}
|
||||
|
||||
/**
|
||||
* when this Activity is created it injects an instance of this class inside
|
||||
* activityInjector method of ApplicationlessInjection
|
||||
*/
|
||||
private fun inject() {
|
||||
val injection = getInstance(applicationContext)
|
||||
|
||||
val activityInjector = injection.activityInjector()
|
||||
?: throw NullPointerException("ApplicationlessInjection.activityInjector() returned null")
|
||||
|
||||
activityInjector.inject(this)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,35 +0,0 @@
|
|||
package fr.free.nrw.commons.di;
|
||||
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
|
||||
import dagger.android.AndroidInjector;
|
||||
|
||||
/**
|
||||
* Receives broadcast then injects it's instance to the broadcastReceiverInjector method of
|
||||
* ApplicationlessInjection class
|
||||
*/
|
||||
public abstract class CommonsDaggerBroadcastReceiver extends BroadcastReceiver {
|
||||
|
||||
public CommonsDaggerBroadcastReceiver() {
|
||||
super();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
inject(context);
|
||||
}
|
||||
|
||||
private void inject(Context context) {
|
||||
ApplicationlessInjection injection = ApplicationlessInjection.getInstance(context.getApplicationContext());
|
||||
|
||||
AndroidInjector<BroadcastReceiver> serviceInjector = injection.broadcastReceiverInjector();
|
||||
|
||||
if (serviceInjector == null) {
|
||||
throw new NullPointerException("ApplicationlessInjection.broadcastReceiverInjector() returned null");
|
||||
}
|
||||
serviceInjector.inject(this);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
package fr.free.nrw.commons.di
|
||||
|
||||
import android.content.BroadcastReceiver
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import fr.free.nrw.commons.di.ApplicationlessInjection.Companion.getInstance
|
||||
|
||||
/**
|
||||
* Receives broadcast then injects it's instance to the broadcastReceiverInjector method of
|
||||
* ApplicationlessInjection class
|
||||
*/
|
||||
abstract class CommonsDaggerBroadcastReceiver : BroadcastReceiver() {
|
||||
override fun onReceive(context: Context, intent: Intent) {
|
||||
inject(context)
|
||||
}
|
||||
|
||||
private fun inject(context: Context) {
|
||||
val injection = getInstance(context.applicationContext)
|
||||
|
||||
val serviceInjector = injection.broadcastReceiverInjector()
|
||||
?: throw NullPointerException("ApplicationlessInjection.broadcastReceiverInjector() returned null")
|
||||
|
||||
serviceInjector.inject(this)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,32 +0,0 @@
|
|||
package fr.free.nrw.commons.di;
|
||||
|
||||
import android.content.ContentProvider;
|
||||
|
||||
import dagger.android.AndroidInjector;
|
||||
|
||||
|
||||
public abstract class CommonsDaggerContentProvider extends ContentProvider {
|
||||
|
||||
public CommonsDaggerContentProvider() {
|
||||
super();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCreate() {
|
||||
inject();
|
||||
return true;
|
||||
}
|
||||
|
||||
private void inject() {
|
||||
ApplicationlessInjection injection = ApplicationlessInjection.getInstance(getContext());
|
||||
|
||||
AndroidInjector<ContentProvider> serviceInjector = injection.contentProviderInjector();
|
||||
|
||||
if (serviceInjector == null) {
|
||||
throw new NullPointerException("ApplicationlessInjection.contentProviderInjector() returned null");
|
||||
}
|
||||
|
||||
serviceInjector.inject(this);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
package fr.free.nrw.commons.di
|
||||
|
||||
import android.content.ContentProvider
|
||||
import fr.free.nrw.commons.di.ApplicationlessInjection.Companion.getInstance
|
||||
|
||||
abstract class CommonsDaggerContentProvider : ContentProvider() {
|
||||
override fun onCreate(): Boolean {
|
||||
inject()
|
||||
return true
|
||||
}
|
||||
|
||||
private fun inject() {
|
||||
val injection = getInstance(context!!)
|
||||
|
||||
val serviceInjector = injection.contentProviderInjector()
|
||||
?: throw NullPointerException("ApplicationlessInjection.contentProviderInjector() returned null")
|
||||
|
||||
serviceInjector.inject(this)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,32 +0,0 @@
|
|||
package fr.free.nrw.commons.di;
|
||||
|
||||
import android.app.IntentService;
|
||||
import android.app.Service;
|
||||
|
||||
import dagger.android.AndroidInjector;
|
||||
|
||||
public abstract class CommonsDaggerIntentService extends IntentService {
|
||||
|
||||
public CommonsDaggerIntentService(String name) {
|
||||
super(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate() {
|
||||
inject();
|
||||
super.onCreate();
|
||||
}
|
||||
|
||||
private void inject() {
|
||||
ApplicationlessInjection injection = ApplicationlessInjection.getInstance(getApplicationContext());
|
||||
|
||||
AndroidInjector<Service> serviceInjector = injection.serviceInjector();
|
||||
|
||||
if (serviceInjector == null) {
|
||||
throw new NullPointerException("ApplicationlessInjection.serviceInjector() returned null");
|
||||
}
|
||||
|
||||
serviceInjector.inject(this);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
package fr.free.nrw.commons.di
|
||||
|
||||
import android.app.IntentService
|
||||
import fr.free.nrw.commons.di.ApplicationlessInjection.Companion.getInstance
|
||||
|
||||
abstract class CommonsDaggerIntentService(name: String?) : IntentService(name) {
|
||||
override fun onCreate() {
|
||||
inject()
|
||||
super.onCreate()
|
||||
}
|
||||
|
||||
private fun inject() {
|
||||
val injection = getInstance(applicationContext)
|
||||
|
||||
val serviceInjector = injection.serviceInjector()
|
||||
?: throw NullPointerException("ApplicationlessInjection.serviceInjector() returned null")
|
||||
|
||||
serviceInjector.inject(this)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,31 +0,0 @@
|
|||
package fr.free.nrw.commons.di;
|
||||
|
||||
import android.app.Service;
|
||||
|
||||
import dagger.android.AndroidInjector;
|
||||
|
||||
public abstract class CommonsDaggerService extends Service {
|
||||
|
||||
public CommonsDaggerService() {
|
||||
super();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate() {
|
||||
inject();
|
||||
super.onCreate();
|
||||
}
|
||||
|
||||
private void inject() {
|
||||
ApplicationlessInjection injection = ApplicationlessInjection.getInstance(getApplicationContext());
|
||||
|
||||
AndroidInjector<Service> serviceInjector = injection.serviceInjector();
|
||||
|
||||
if (serviceInjector == null) {
|
||||
throw new NullPointerException("ApplicationlessInjection.serviceInjector() returned null");
|
||||
}
|
||||
|
||||
serviceInjector.inject(this);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
package fr.free.nrw.commons.di
|
||||
|
||||
import android.app.Service
|
||||
import fr.free.nrw.commons.di.ApplicationlessInjection.Companion.getInstance
|
||||
|
||||
abstract class CommonsDaggerService : Service() {
|
||||
override fun onCreate() {
|
||||
inject()
|
||||
super.onCreate()
|
||||
}
|
||||
|
||||
private fun inject() {
|
||||
val injection = getInstance(applicationContext)
|
||||
|
||||
val serviceInjector = injection.serviceInjector()
|
||||
?: throw NullPointerException("ApplicationlessInjection.serviceInjector() returned null")
|
||||
|
||||
serviceInjector.inject(this)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,75 +0,0 @@
|
|||
package fr.free.nrw.commons.di;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
|
||||
import androidx.fragment.app.Fragment;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import dagger.android.AndroidInjector;
|
||||
import dagger.android.DispatchingAndroidInjector;
|
||||
import dagger.android.support.HasSupportFragmentInjector;
|
||||
import io.reactivex.disposables.CompositeDisposable;
|
||||
|
||||
public abstract class CommonsDaggerSupportFragment extends Fragment implements HasSupportFragmentInjector {
|
||||
|
||||
@Inject
|
||||
DispatchingAndroidInjector<Fragment> childFragmentInjector;
|
||||
|
||||
protected CompositeDisposable compositeDisposable = new CompositeDisposable();
|
||||
|
||||
@Override
|
||||
public void onAttach(Context context) {
|
||||
inject();
|
||||
super.onAttach(context);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
super.onDestroy();
|
||||
compositeDisposable.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public AndroidInjector<Fragment> supportFragmentInjector() {
|
||||
return childFragmentInjector;
|
||||
}
|
||||
|
||||
|
||||
public void inject() {
|
||||
HasSupportFragmentInjector hasSupportFragmentInjector = findHasFragmentInjector();
|
||||
|
||||
AndroidInjector<Fragment> fragmentInjector = hasSupportFragmentInjector.supportFragmentInjector();
|
||||
|
||||
if (fragmentInjector == null) {
|
||||
throw new NullPointerException(String.format("%s.supportFragmentInjector() returned null", hasSupportFragmentInjector.getClass().getCanonicalName()));
|
||||
}
|
||||
|
||||
fragmentInjector.inject(this);
|
||||
}
|
||||
|
||||
private HasSupportFragmentInjector findHasFragmentInjector() {
|
||||
Fragment parentFragment = this;
|
||||
|
||||
while ((parentFragment = parentFragment.getParentFragment()) != null) {
|
||||
if (parentFragment instanceof HasSupportFragmentInjector) {
|
||||
return (HasSupportFragmentInjector) parentFragment;
|
||||
}
|
||||
}
|
||||
|
||||
Activity activity = getActivity();
|
||||
|
||||
if (activity instanceof HasSupportFragmentInjector) {
|
||||
return (HasSupportFragmentInjector) activity;
|
||||
}
|
||||
|
||||
ApplicationlessInjection injection = ApplicationlessInjection.getInstance(activity.getApplicationContext());
|
||||
if (injection != null) {
|
||||
return injection;
|
||||
}
|
||||
|
||||
throw new IllegalArgumentException(String.format("No injector was found for %s", getClass().getCanonicalName()));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,66 @@
|
|||
package fr.free.nrw.commons.di
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.Context
|
||||
import androidx.fragment.app.Fragment
|
||||
import dagger.android.AndroidInjector
|
||||
import dagger.android.DispatchingAndroidInjector
|
||||
import dagger.android.support.HasSupportFragmentInjector
|
||||
import fr.free.nrw.commons.di.ApplicationlessInjection.Companion.getInstance
|
||||
import io.reactivex.disposables.CompositeDisposable
|
||||
import javax.inject.Inject
|
||||
|
||||
abstract class CommonsDaggerSupportFragment : Fragment(), HasSupportFragmentInjector {
|
||||
|
||||
@Inject @JvmField
|
||||
var childFragmentInjector: DispatchingAndroidInjector<Fragment>? = null
|
||||
|
||||
@JvmField
|
||||
protected var compositeDisposable: CompositeDisposable = CompositeDisposable()
|
||||
|
||||
override fun onAttach(context: Context) {
|
||||
inject()
|
||||
super.onAttach(context)
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
super.onDestroy()
|
||||
compositeDisposable.clear()
|
||||
}
|
||||
|
||||
override fun supportFragmentInjector(): AndroidInjector<Fragment> =
|
||||
childFragmentInjector!!
|
||||
|
||||
|
||||
fun inject() {
|
||||
val hasSupportFragmentInjector = findHasFragmentInjector()
|
||||
|
||||
val fragmentInjector = hasSupportFragmentInjector.supportFragmentInjector()
|
||||
?: throw NullPointerException(
|
||||
String.format(
|
||||
"%s.supportFragmentInjector() returned null",
|
||||
hasSupportFragmentInjector.javaClass.canonicalName
|
||||
)
|
||||
)
|
||||
|
||||
fragmentInjector.inject(this)
|
||||
}
|
||||
|
||||
private fun findHasFragmentInjector(): HasSupportFragmentInjector {
|
||||
var parentFragment: Fragment? = this
|
||||
|
||||
while ((parentFragment!!.parentFragment.also { parentFragment = it }) != null) {
|
||||
if (parentFragment is HasSupportFragmentInjector) {
|
||||
return parentFragment as HasSupportFragmentInjector
|
||||
}
|
||||
}
|
||||
|
||||
val activity: Activity = requireActivity()
|
||||
|
||||
if (activity is HasSupportFragmentInjector) {
|
||||
return activity
|
||||
}
|
||||
|
||||
return getInstance(activity.applicationContext)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,38 +0,0 @@
|
|||
package fr.free.nrw.commons.di;
|
||||
|
||||
import dagger.Module;
|
||||
import dagger.android.ContributesAndroidInjector;
|
||||
import fr.free.nrw.commons.bookmarks.items.BookmarkItemsContentProvider;
|
||||
import fr.free.nrw.commons.bookmarks.locations.BookmarkLocationsContentProvider;
|
||||
import fr.free.nrw.commons.bookmarks.pictures.BookmarkPicturesContentProvider;
|
||||
import fr.free.nrw.commons.category.CategoryContentProvider;
|
||||
import fr.free.nrw.commons.explore.recentsearches.RecentSearchesContentProvider;
|
||||
import fr.free.nrw.commons.recentlanguages.RecentLanguagesContentProvider;
|
||||
|
||||
/**
|
||||
* This Class Represents the Module for dependency injection (using dagger)
|
||||
* so, if a developer needs to add a new ContentProvider to the commons app
|
||||
* then that must be mentioned here to inject the dependencies
|
||||
*/
|
||||
@Module
|
||||
@SuppressWarnings({ "WeakerAccess", "unused" })
|
||||
public abstract class ContentProviderBuilderModule {
|
||||
|
||||
@ContributesAndroidInjector
|
||||
abstract CategoryContentProvider bindCategoryContentProvider();
|
||||
|
||||
@ContributesAndroidInjector
|
||||
abstract RecentSearchesContentProvider bindRecentSearchesContentProvider();
|
||||
|
||||
@ContributesAndroidInjector
|
||||
abstract BookmarkPicturesContentProvider bindBookmarkContentProvider();
|
||||
|
||||
@ContributesAndroidInjector
|
||||
abstract BookmarkLocationsContentProvider bindBookmarkLocationContentProvider();
|
||||
|
||||
@ContributesAndroidInjector
|
||||
abstract BookmarkItemsContentProvider bindBookmarkItemContentProvider();
|
||||
|
||||
@ContributesAndroidInjector
|
||||
abstract RecentLanguagesContentProvider bindRecentLanguagesContentProvider();
|
||||
}
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
package fr.free.nrw.commons.di
|
||||
|
||||
import dagger.Module
|
||||
import dagger.android.ContributesAndroidInjector
|
||||
import fr.free.nrw.commons.bookmarks.items.BookmarkItemsContentProvider
|
||||
import fr.free.nrw.commons.bookmarks.locations.BookmarkLocationsContentProvider
|
||||
import fr.free.nrw.commons.bookmarks.pictures.BookmarkPicturesContentProvider
|
||||
import fr.free.nrw.commons.category.CategoryContentProvider
|
||||
import fr.free.nrw.commons.explore.recentsearches.RecentSearchesContentProvider
|
||||
import fr.free.nrw.commons.recentlanguages.RecentLanguagesContentProvider
|
||||
|
||||
/**
|
||||
* This Class Represents the Module for dependency injection (using dagger)
|
||||
* so, if a developer needs to add a new ContentProvider to the commons app
|
||||
* then that must be mentioned here to inject the dependencies
|
||||
*/
|
||||
@Module
|
||||
@Suppress("unused")
|
||||
abstract class ContentProviderBuilderModule {
|
||||
@ContributesAndroidInjector
|
||||
abstract fun bindCategoryContentProvider(): CategoryContentProvider
|
||||
|
||||
@ContributesAndroidInjector
|
||||
abstract fun bindRecentSearchesContentProvider(): RecentSearchesContentProvider
|
||||
|
||||
@ContributesAndroidInjector
|
||||
abstract fun bindBookmarkContentProvider(): BookmarkPicturesContentProvider
|
||||
|
||||
@ContributesAndroidInjector
|
||||
abstract fun bindBookmarkLocationContentProvider(): BookmarkLocationsContentProvider
|
||||
|
||||
@ContributesAndroidInjector
|
||||
abstract fun bindBookmarkItemContentProvider(): BookmarkItemsContentProvider
|
||||
|
||||
@ContributesAndroidInjector
|
||||
abstract fun bindRecentLanguagesContentProvider(): RecentLanguagesContentProvider
|
||||
}
|
||||
|
|
@ -1,166 +0,0 @@
|
|||
package fr.free.nrw.commons.di;
|
||||
|
||||
import dagger.Module;
|
||||
import dagger.android.ContributesAndroidInjector;
|
||||
import fr.free.nrw.commons.bookmarks.BookmarkFragment;
|
||||
import fr.free.nrw.commons.bookmarks.BookmarkListRootFragment;
|
||||
import fr.free.nrw.commons.bookmarks.items.BookmarkItemsFragment;
|
||||
import fr.free.nrw.commons.bookmarks.locations.BookmarkLocationsFragment;
|
||||
import fr.free.nrw.commons.bookmarks.pictures.BookmarkPicturesFragment;
|
||||
import fr.free.nrw.commons.contributions.ContributionsFragment;
|
||||
import fr.free.nrw.commons.contributions.ContributionsListFragment;
|
||||
import fr.free.nrw.commons.customselector.ui.selector.FolderFragment;
|
||||
import fr.free.nrw.commons.customselector.ui.selector.ImageFragment;
|
||||
import fr.free.nrw.commons.explore.ExploreFragment;
|
||||
import fr.free.nrw.commons.explore.ExploreListRootFragment;
|
||||
import fr.free.nrw.commons.explore.ExploreMapRootFragment;
|
||||
import fr.free.nrw.commons.explore.map.ExploreMapFragment;
|
||||
import fr.free.nrw.commons.explore.categories.media.CategoriesMediaFragment;
|
||||
import fr.free.nrw.commons.explore.categories.parent.ParentCategoriesFragment;
|
||||
import fr.free.nrw.commons.explore.categories.search.SearchCategoryFragment;
|
||||
import fr.free.nrw.commons.explore.categories.sub.SubCategoriesFragment;
|
||||
import fr.free.nrw.commons.explore.depictions.child.ChildDepictionsFragment;
|
||||
import fr.free.nrw.commons.explore.depictions.media.DepictedImagesFragment;
|
||||
import fr.free.nrw.commons.explore.depictions.parent.ParentDepictionsFragment;
|
||||
import fr.free.nrw.commons.explore.depictions.search.SearchDepictionsFragment;
|
||||
import fr.free.nrw.commons.explore.media.SearchMediaFragment;
|
||||
import fr.free.nrw.commons.explore.recentsearches.RecentSearchesFragment;
|
||||
import fr.free.nrw.commons.media.MediaDetailFragment;
|
||||
import fr.free.nrw.commons.media.MediaDetailPagerFragment;
|
||||
import fr.free.nrw.commons.navtab.MoreBottomSheetFragment;
|
||||
import fr.free.nrw.commons.navtab.MoreBottomSheetLoggedOutFragment;
|
||||
import fr.free.nrw.commons.nearby.fragments.NearbyParentFragment;
|
||||
import fr.free.nrw.commons.profile.achievements.AchievementsFragment;
|
||||
import fr.free.nrw.commons.profile.leaderboard.LeaderboardFragment;
|
||||
import fr.free.nrw.commons.review.ReviewImageFragment;
|
||||
import fr.free.nrw.commons.settings.SettingsFragment;
|
||||
import fr.free.nrw.commons.upload.FailedUploadsFragment;
|
||||
import fr.free.nrw.commons.upload.PendingUploadsFragment;
|
||||
import fr.free.nrw.commons.upload.categories.UploadCategoriesFragment;
|
||||
import fr.free.nrw.commons.upload.depicts.DepictsFragment;
|
||||
import fr.free.nrw.commons.upload.license.MediaLicenseFragment;
|
||||
import fr.free.nrw.commons.upload.mediaDetails.UploadMediaDetailFragment;
|
||||
|
||||
/**
|
||||
* This Class Represents the Module for dependency injection (using dagger)
|
||||
* so, if a developer needs to add a new Fragment to the commons app
|
||||
* then that must be mentioned here to inject the dependencies
|
||||
*/
|
||||
@Module
|
||||
@SuppressWarnings({"WeakerAccess", "unused"})
|
||||
public abstract class FragmentBuilderModule {
|
||||
|
||||
@ContributesAndroidInjector
|
||||
abstract ContributionsListFragment bindContributionsListFragment();
|
||||
|
||||
@ContributesAndroidInjector
|
||||
abstract MediaDetailFragment bindMediaDetailFragment();
|
||||
|
||||
@ContributesAndroidInjector
|
||||
abstract FolderFragment bindFolderFragment();
|
||||
|
||||
@ContributesAndroidInjector
|
||||
abstract ImageFragment bindImageFragment();
|
||||
|
||||
@ContributesAndroidInjector
|
||||
abstract MediaDetailPagerFragment bindMediaDetailPagerFragment();
|
||||
|
||||
@ContributesAndroidInjector
|
||||
abstract SettingsFragment bindSettingsFragment();
|
||||
|
||||
@ContributesAndroidInjector
|
||||
abstract DepictedImagesFragment bindDepictedImagesFragment();
|
||||
|
||||
@ContributesAndroidInjector
|
||||
abstract SearchMediaFragment bindBrowseImagesListFragment();
|
||||
|
||||
@ContributesAndroidInjector
|
||||
abstract SearchCategoryFragment bindSearchCategoryListFragment();
|
||||
|
||||
@ContributesAndroidInjector
|
||||
abstract SearchDepictionsFragment bindSearchDepictionListFragment();
|
||||
|
||||
@ContributesAndroidInjector
|
||||
abstract RecentSearchesFragment bindRecentSearchesFragment();
|
||||
|
||||
@ContributesAndroidInjector
|
||||
abstract ContributionsFragment bindContributionsFragment();
|
||||
|
||||
@ContributesAndroidInjector(modules = NearbyParentFragmentModule.class)
|
||||
abstract NearbyParentFragment bindNearbyParentFragment();
|
||||
|
||||
@ContributesAndroidInjector
|
||||
abstract BookmarkPicturesFragment bindBookmarkPictureListFragment();
|
||||
|
||||
@ContributesAndroidInjector(modules = BookmarkLocationsFragmentModule.class)
|
||||
abstract BookmarkLocationsFragment bindBookmarkLocationListFragment();
|
||||
|
||||
@ContributesAndroidInjector(modules = BookmarkItemsFragmentModule.class)
|
||||
abstract BookmarkItemsFragment bindBookmarkItemListFragment();
|
||||
|
||||
@ContributesAndroidInjector
|
||||
abstract ReviewImageFragment bindReviewOutOfContextFragment();
|
||||
|
||||
@ContributesAndroidInjector
|
||||
abstract UploadMediaDetailFragment bindUploadMediaDetailFragment();
|
||||
|
||||
@ContributesAndroidInjector
|
||||
abstract UploadCategoriesFragment bindUploadCategoriesFragment();
|
||||
|
||||
@ContributesAndroidInjector
|
||||
abstract DepictsFragment bindDepictsFragment();
|
||||
|
||||
@ContributesAndroidInjector
|
||||
abstract MediaLicenseFragment bindMediaLicenseFragment();
|
||||
|
||||
@ContributesAndroidInjector
|
||||
abstract ParentDepictionsFragment bindParentDepictionsFragment();
|
||||
|
||||
@ContributesAndroidInjector
|
||||
abstract ChildDepictionsFragment bindChildDepictionsFragment();
|
||||
|
||||
@ContributesAndroidInjector
|
||||
abstract CategoriesMediaFragment bindCategoriesMediaFragment();
|
||||
|
||||
@ContributesAndroidInjector
|
||||
abstract SubCategoriesFragment bindSubCategoriesFragment();
|
||||
|
||||
@ContributesAndroidInjector
|
||||
abstract ParentCategoriesFragment bindParentCategoriesFragment();
|
||||
|
||||
@ContributesAndroidInjector
|
||||
abstract ExploreFragment bindExploreFragmentFragment();
|
||||
|
||||
@ContributesAndroidInjector
|
||||
abstract ExploreListRootFragment bindExploreFeaturedRootFragment();
|
||||
|
||||
@ContributesAndroidInjector(modules = ExploreMapFragmentModule.class)
|
||||
abstract ExploreMapFragment bindExploreNearbyUploadsFragment();
|
||||
|
||||
@ContributesAndroidInjector
|
||||
abstract ExploreMapRootFragment bindExploreNearbyUploadsRootFragment();
|
||||
|
||||
@ContributesAndroidInjector
|
||||
abstract BookmarkListRootFragment bindBookmarkListRootFragment();
|
||||
|
||||
@ContributesAndroidInjector
|
||||
abstract BookmarkFragment bindBookmarkFragmentFragment();
|
||||
|
||||
@ContributesAndroidInjector
|
||||
abstract MoreBottomSheetFragment bindMoreBottomSheetFragment();
|
||||
|
||||
@ContributesAndroidInjector
|
||||
abstract MoreBottomSheetLoggedOutFragment bindMoreBottomSheetLoggedOutFragment();
|
||||
|
||||
@ContributesAndroidInjector
|
||||
abstract AchievementsFragment bindAchievementsFragment();
|
||||
|
||||
@ContributesAndroidInjector
|
||||
abstract LeaderboardFragment bindLeaderboardFragment();
|
||||
|
||||
@ContributesAndroidInjector
|
||||
abstract PendingUploadsFragment bindPendingUploadsFragment();
|
||||
|
||||
@ContributesAndroidInjector
|
||||
abstract FailedUploadsFragment bindFailedUploadsFragment();
|
||||
}
|
||||
|
|
@ -0,0 +1,165 @@
|
|||
package fr.free.nrw.commons.di
|
||||
|
||||
import dagger.Module
|
||||
import dagger.android.ContributesAndroidInjector
|
||||
import fr.free.nrw.commons.bookmarks.BookmarkFragment
|
||||
import fr.free.nrw.commons.bookmarks.BookmarkListRootFragment
|
||||
import fr.free.nrw.commons.bookmarks.items.BookmarkItemsFragment
|
||||
import fr.free.nrw.commons.bookmarks.locations.BookmarkLocationsFragment
|
||||
import fr.free.nrw.commons.bookmarks.pictures.BookmarkPicturesFragment
|
||||
import fr.free.nrw.commons.contributions.ContributionsFragment
|
||||
import fr.free.nrw.commons.contributions.ContributionsListFragment
|
||||
import fr.free.nrw.commons.customselector.ui.selector.FolderFragment
|
||||
import fr.free.nrw.commons.customselector.ui.selector.ImageFragment
|
||||
import fr.free.nrw.commons.explore.ExploreFragment
|
||||
import fr.free.nrw.commons.explore.ExploreListRootFragment
|
||||
import fr.free.nrw.commons.explore.ExploreMapRootFragment
|
||||
import fr.free.nrw.commons.explore.categories.media.CategoriesMediaFragment
|
||||
import fr.free.nrw.commons.explore.categories.parent.ParentCategoriesFragment
|
||||
import fr.free.nrw.commons.explore.categories.search.SearchCategoryFragment
|
||||
import fr.free.nrw.commons.explore.categories.sub.SubCategoriesFragment
|
||||
import fr.free.nrw.commons.explore.depictions.child.ChildDepictionsFragment
|
||||
import fr.free.nrw.commons.explore.depictions.media.DepictedImagesFragment
|
||||
import fr.free.nrw.commons.explore.depictions.parent.ParentDepictionsFragment
|
||||
import fr.free.nrw.commons.explore.depictions.search.SearchDepictionsFragment
|
||||
import fr.free.nrw.commons.explore.map.ExploreMapFragment
|
||||
import fr.free.nrw.commons.explore.media.SearchMediaFragment
|
||||
import fr.free.nrw.commons.explore.recentsearches.RecentSearchesFragment
|
||||
import fr.free.nrw.commons.media.MediaDetailFragment
|
||||
import fr.free.nrw.commons.media.MediaDetailPagerFragment
|
||||
import fr.free.nrw.commons.navtab.MoreBottomSheetFragment
|
||||
import fr.free.nrw.commons.navtab.MoreBottomSheetLoggedOutFragment
|
||||
import fr.free.nrw.commons.nearby.fragments.NearbyParentFragment
|
||||
import fr.free.nrw.commons.profile.achievements.AchievementsFragment
|
||||
import fr.free.nrw.commons.profile.leaderboard.LeaderboardFragment
|
||||
import fr.free.nrw.commons.review.ReviewImageFragment
|
||||
import fr.free.nrw.commons.settings.SettingsFragment
|
||||
import fr.free.nrw.commons.upload.FailedUploadsFragment
|
||||
import fr.free.nrw.commons.upload.PendingUploadsFragment
|
||||
import fr.free.nrw.commons.upload.categories.UploadCategoriesFragment
|
||||
import fr.free.nrw.commons.upload.depicts.DepictsFragment
|
||||
import fr.free.nrw.commons.upload.license.MediaLicenseFragment
|
||||
import fr.free.nrw.commons.upload.mediaDetails.UploadMediaDetailFragment
|
||||
|
||||
/**
|
||||
* This Class Represents the Module for dependency injection (using dagger)
|
||||
* so, if a developer needs to add a new Fragment to the commons app
|
||||
* then that must be mentioned here to inject the dependencies
|
||||
*/
|
||||
@Module
|
||||
@Suppress("unused")
|
||||
abstract class FragmentBuilderModule {
|
||||
@ContributesAndroidInjector
|
||||
abstract fun bindContributionsListFragment(): ContributionsListFragment
|
||||
|
||||
@ContributesAndroidInjector
|
||||
abstract fun bindMediaDetailFragment(): MediaDetailFragment
|
||||
|
||||
@ContributesAndroidInjector
|
||||
abstract fun bindFolderFragment(): FolderFragment
|
||||
|
||||
@ContributesAndroidInjector
|
||||
abstract fun bindImageFragment(): ImageFragment
|
||||
|
||||
@ContributesAndroidInjector
|
||||
abstract fun bindMediaDetailPagerFragment(): MediaDetailPagerFragment
|
||||
|
||||
@ContributesAndroidInjector
|
||||
abstract fun bindSettingsFragment(): SettingsFragment
|
||||
|
||||
@ContributesAndroidInjector
|
||||
abstract fun bindDepictedImagesFragment(): DepictedImagesFragment
|
||||
|
||||
@ContributesAndroidInjector
|
||||
abstract fun bindBrowseImagesListFragment(): SearchMediaFragment
|
||||
|
||||
@ContributesAndroidInjector
|
||||
abstract fun bindSearchCategoryListFragment(): SearchCategoryFragment
|
||||
|
||||
@ContributesAndroidInjector
|
||||
abstract fun bindSearchDepictionListFragment(): SearchDepictionsFragment
|
||||
|
||||
@ContributesAndroidInjector
|
||||
abstract fun bindRecentSearchesFragment(): RecentSearchesFragment
|
||||
|
||||
@ContributesAndroidInjector
|
||||
abstract fun bindContributionsFragment(): ContributionsFragment
|
||||
|
||||
@ContributesAndroidInjector(modules = [NearbyParentFragmentModule::class])
|
||||
abstract fun bindNearbyParentFragment(): NearbyParentFragment
|
||||
|
||||
@ContributesAndroidInjector
|
||||
abstract fun bindBookmarkPictureListFragment(): BookmarkPicturesFragment
|
||||
|
||||
@ContributesAndroidInjector(modules = [BookmarkLocationsFragmentModule::class])
|
||||
abstract fun bindBookmarkLocationListFragment(): BookmarkLocationsFragment
|
||||
|
||||
@ContributesAndroidInjector(modules = [BookmarkItemsFragmentModule::class])
|
||||
abstract fun bindBookmarkItemListFragment(): BookmarkItemsFragment
|
||||
|
||||
@ContributesAndroidInjector
|
||||
abstract fun bindReviewOutOfContextFragment(): ReviewImageFragment
|
||||
|
||||
@ContributesAndroidInjector
|
||||
abstract fun bindUploadMediaDetailFragment(): UploadMediaDetailFragment
|
||||
|
||||
@ContributesAndroidInjector
|
||||
abstract fun bindUploadCategoriesFragment(): UploadCategoriesFragment
|
||||
|
||||
@ContributesAndroidInjector
|
||||
abstract fun bindDepictsFragment(): DepictsFragment
|
||||
|
||||
@ContributesAndroidInjector
|
||||
abstract fun bindMediaLicenseFragment(): MediaLicenseFragment
|
||||
|
||||
@ContributesAndroidInjector
|
||||
abstract fun bindParentDepictionsFragment(): ParentDepictionsFragment
|
||||
|
||||
@ContributesAndroidInjector
|
||||
abstract fun bindChildDepictionsFragment(): ChildDepictionsFragment
|
||||
|
||||
@ContributesAndroidInjector
|
||||
abstract fun bindCategoriesMediaFragment(): CategoriesMediaFragment
|
||||
|
||||
@ContributesAndroidInjector
|
||||
abstract fun bindSubCategoriesFragment(): SubCategoriesFragment
|
||||
|
||||
@ContributesAndroidInjector
|
||||
abstract fun bindParentCategoriesFragment(): ParentCategoriesFragment
|
||||
|
||||
@ContributesAndroidInjector
|
||||
abstract fun bindExploreFragmentFragment(): ExploreFragment
|
||||
|
||||
@ContributesAndroidInjector
|
||||
abstract fun bindExploreFeaturedRootFragment(): ExploreListRootFragment
|
||||
|
||||
@ContributesAndroidInjector(modules = [ExploreMapFragmentModule::class])
|
||||
abstract fun bindExploreNearbyUploadsFragment(): ExploreMapFragment
|
||||
|
||||
@ContributesAndroidInjector
|
||||
abstract fun bindExploreNearbyUploadsRootFragment(): ExploreMapRootFragment
|
||||
|
||||
@ContributesAndroidInjector
|
||||
abstract fun bindBookmarkListRootFragment(): BookmarkListRootFragment
|
||||
|
||||
@ContributesAndroidInjector
|
||||
abstract fun bindBookmarkFragmentFragment(): BookmarkFragment
|
||||
|
||||
@ContributesAndroidInjector
|
||||
abstract fun bindMoreBottomSheetFragment(): MoreBottomSheetFragment
|
||||
|
||||
@ContributesAndroidInjector
|
||||
abstract fun bindMoreBottomSheetLoggedOutFragment(): MoreBottomSheetLoggedOutFragment
|
||||
|
||||
@ContributesAndroidInjector
|
||||
abstract fun bindAchievementsFragment(): AchievementsFragment
|
||||
|
||||
@ContributesAndroidInjector
|
||||
abstract fun bindLeaderboardFragment(): LeaderboardFragment
|
||||
|
||||
@ContributesAndroidInjector
|
||||
abstract fun bindPendingUploadsFragment(): PendingUploadsFragment
|
||||
|
||||
@ContributesAndroidInjector
|
||||
abstract fun bindFailedUploadsFragment(): FailedUploadsFragment
|
||||
}
|
||||
|
|
@ -1,350 +0,0 @@
|
|||
package fr.free.nrw.commons.di;
|
||||
|
||||
import android.content.Context;
|
||||
import androidx.annotation.NonNull;
|
||||
import com.google.gson.Gson;
|
||||
import dagger.Module;
|
||||
import dagger.Provides;
|
||||
import fr.free.nrw.commons.BetaConstants;
|
||||
import fr.free.nrw.commons.BuildConfig;
|
||||
import fr.free.nrw.commons.OkHttpConnectionFactory;
|
||||
import fr.free.nrw.commons.actions.PageEditClient;
|
||||
import fr.free.nrw.commons.actions.PageEditInterface;
|
||||
import fr.free.nrw.commons.actions.ThanksInterface;
|
||||
import fr.free.nrw.commons.auth.SessionManager;
|
||||
import fr.free.nrw.commons.auth.csrf.CsrfTokenClient;
|
||||
import fr.free.nrw.commons.auth.csrf.CsrfTokenInterface;
|
||||
import fr.free.nrw.commons.auth.csrf.LogoutClient;
|
||||
import fr.free.nrw.commons.auth.login.LoginClient;
|
||||
import fr.free.nrw.commons.auth.login.LoginInterface;
|
||||
import fr.free.nrw.commons.category.CategoryInterface;
|
||||
import fr.free.nrw.commons.explore.depictions.DepictsClient;
|
||||
import fr.free.nrw.commons.kvstore.JsonKvStore;
|
||||
import fr.free.nrw.commons.media.MediaDetailInterface;
|
||||
import fr.free.nrw.commons.media.MediaInterface;
|
||||
import fr.free.nrw.commons.media.PageMediaInterface;
|
||||
import fr.free.nrw.commons.media.WikidataMediaInterface;
|
||||
import fr.free.nrw.commons.mwapi.OkHttpJsonApiClient;
|
||||
import fr.free.nrw.commons.mwapi.UserInterface;
|
||||
import fr.free.nrw.commons.notification.NotificationInterface;
|
||||
import fr.free.nrw.commons.review.ReviewInterface;
|
||||
import fr.free.nrw.commons.upload.UploadInterface;
|
||||
import fr.free.nrw.commons.upload.WikiBaseInterface;
|
||||
import fr.free.nrw.commons.upload.depicts.DepictsInterface;
|
||||
import fr.free.nrw.commons.wikidata.CommonsServiceFactory;
|
||||
import fr.free.nrw.commons.wikidata.WikidataInterface;
|
||||
import fr.free.nrw.commons.wikidata.cookies.CommonsCookieJar;
|
||||
import fr.free.nrw.commons.wikidata.cookies.CommonsCookieStorage;
|
||||
import java.io.File;
|
||||
import java.util.Locale;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import javax.inject.Named;
|
||||
import javax.inject.Singleton;
|
||||
import okhttp3.Cache;
|
||||
import okhttp3.HttpUrl;
|
||||
import okhttp3.OkHttpClient;
|
||||
import okhttp3.logging.HttpLoggingInterceptor;
|
||||
import okhttp3.logging.HttpLoggingInterceptor.Level;
|
||||
import fr.free.nrw.commons.wikidata.model.WikiSite;
|
||||
import fr.free.nrw.commons.wikidata.GsonUtil;
|
||||
import timber.log.Timber;
|
||||
|
||||
@Module
|
||||
@SuppressWarnings({"WeakerAccess", "unused"})
|
||||
public class NetworkingModule {
|
||||
private static final String WIKIDATA_SPARQL_QUERY_URL = "https://query.wikidata.org/sparql";
|
||||
private static final String TOOLS_FORGE_URL = "https://tools.wmflabs.org/commons-android-app/tool-commons-android-app";
|
||||
|
||||
public static final long OK_HTTP_CACHE_SIZE = 10 * 1024 * 1024;
|
||||
|
||||
private static final String NAMED_WIKI_DATA_WIKI_SITE = "wikidata-wikisite";
|
||||
private static final String NAMED_WIKI_PEDIA_WIKI_SITE = "wikipedia-wikisite";
|
||||
|
||||
public static final String NAMED_LANGUAGE_WIKI_PEDIA_WIKI_SITE = "language-wikipedia-wikisite";
|
||||
|
||||
public static final String NAMED_COMMONS_CSRF = "commons-csrf";
|
||||
public static final String NAMED_WIKI_CSRF = "wiki-csrf";
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
public OkHttpClient provideOkHttpClient(Context context,
|
||||
HttpLoggingInterceptor httpLoggingInterceptor) {
|
||||
File dir = new File(context.getCacheDir(), "okHttpCache");
|
||||
return new OkHttpClient.Builder()
|
||||
.connectTimeout(120, TimeUnit.SECONDS)
|
||||
.writeTimeout(120, TimeUnit.SECONDS)
|
||||
.addInterceptor(httpLoggingInterceptor)
|
||||
.readTimeout(120, TimeUnit.SECONDS)
|
||||
.cache(new Cache(dir, OK_HTTP_CACHE_SIZE))
|
||||
.build();
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
public CommonsServiceFactory serviceFactory(CommonsCookieJar cookieJar) {
|
||||
return new CommonsServiceFactory(OkHttpConnectionFactory.getClient(cookieJar));
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
public HttpLoggingInterceptor provideHttpLoggingInterceptor() {
|
||||
HttpLoggingInterceptor httpLoggingInterceptor = new HttpLoggingInterceptor(message -> {
|
||||
Timber.tag("OkHttp").v(message);
|
||||
});
|
||||
httpLoggingInterceptor.setLevel(BuildConfig.DEBUG ? Level.BODY: Level.BASIC);
|
||||
return httpLoggingInterceptor;
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
public OkHttpJsonApiClient provideOkHttpJsonApiClient(OkHttpClient okHttpClient,
|
||||
DepictsClient depictsClient,
|
||||
@Named("tools_forge") HttpUrl toolsForgeUrl,
|
||||
@Named("default_preferences") JsonKvStore defaultKvStore,
|
||||
Gson gson) {
|
||||
return new OkHttpJsonApiClient(okHttpClient,
|
||||
depictsClient,
|
||||
toolsForgeUrl,
|
||||
WIKIDATA_SPARQL_QUERY_URL,
|
||||
BuildConfig.WIKIMEDIA_CAMPAIGNS_URL,
|
||||
gson);
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
public CommonsCookieStorage provideCookieStorage(
|
||||
@Named("default_preferences") JsonKvStore preferences) {
|
||||
CommonsCookieStorage cookieStorage = new CommonsCookieStorage(preferences);
|
||||
cookieStorage.load();
|
||||
return cookieStorage;
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
public CommonsCookieJar provideCookieJar(CommonsCookieStorage storage) {
|
||||
return new CommonsCookieJar(storage);
|
||||
}
|
||||
|
||||
@Named(NAMED_COMMONS_CSRF)
|
||||
@Provides
|
||||
@Singleton
|
||||
public CsrfTokenClient provideCommonsCsrfTokenClient(SessionManager sessionManager,
|
||||
@Named("commons-csrf-interface") CsrfTokenInterface tokenInterface, LoginClient loginClient, LogoutClient logoutClient) {
|
||||
return new CsrfTokenClient(sessionManager, tokenInterface, loginClient, logoutClient);
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides a singleton instance of CsrfTokenClient for Wikidata.
|
||||
*
|
||||
* @param sessionManager The session manager to manage user sessions.
|
||||
* @param tokenInterface The interface for obtaining CSRF tokens.
|
||||
* @param loginClient The client for handling login operations.
|
||||
* @param logoutClient The client for handling logout operations.
|
||||
* @return A singleton instance of CsrfTokenClient.
|
||||
*/
|
||||
@Named(NAMED_WIKI_CSRF)
|
||||
@Provides
|
||||
@Singleton
|
||||
public CsrfTokenClient provideWikiCsrfTokenClient(SessionManager sessionManager,
|
||||
@Named("wikidata-csrf-interface") CsrfTokenInterface tokenInterface, LoginClient loginClient, LogoutClient logoutClient) {
|
||||
return new CsrfTokenClient(sessionManager, tokenInterface, loginClient, logoutClient);
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides a singleton instance of CsrfTokenInterface for Wikidata.
|
||||
*
|
||||
* @param serviceFactory The factory used to create service interfaces.
|
||||
* @return A singleton instance of CsrfTokenInterface for Wikidata.
|
||||
*/
|
||||
@Named("wikidata-csrf-interface")
|
||||
@Provides
|
||||
@Singleton
|
||||
public CsrfTokenInterface provideWikidataCsrfTokenInterface(CommonsServiceFactory serviceFactory) {
|
||||
return serviceFactory.create(BuildConfig.WIKIDATA_URL, CsrfTokenInterface.class);
|
||||
}
|
||||
|
||||
@Named("commons-csrf-interface")
|
||||
@Provides
|
||||
@Singleton
|
||||
public CsrfTokenInterface provideCsrfTokenInterface(CommonsServiceFactory serviceFactory) {
|
||||
return serviceFactory.create(BuildConfig.COMMONS_URL, CsrfTokenInterface.class);
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
public LoginInterface provideLoginInterface(CommonsServiceFactory serviceFactory) {
|
||||
return serviceFactory.create(BuildConfig.COMMONS_URL, LoginInterface.class);
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
public LoginClient provideLoginClient(LoginInterface loginInterface) {
|
||||
return new LoginClient(loginInterface);
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Named("wikimedia_api_host")
|
||||
@NonNull
|
||||
@SuppressWarnings("ConstantConditions")
|
||||
public String provideMwApiUrl() {
|
||||
return BuildConfig.WIKIMEDIA_API_HOST;
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Named("tools_forge")
|
||||
@NonNull
|
||||
@SuppressWarnings("ConstantConditions")
|
||||
public HttpUrl provideToolsForgeUrl() {
|
||||
return HttpUrl.parse(TOOLS_FORGE_URL);
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
@Named(NAMED_WIKI_DATA_WIKI_SITE)
|
||||
public WikiSite provideWikidataWikiSite() {
|
||||
return new WikiSite(BuildConfig.WIKIDATA_URL);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Gson objects are very heavy. The app should ideally be using just one instance of it instead of creating new instances everywhere.
|
||||
* @return returns a singleton Gson instance
|
||||
*/
|
||||
@Provides
|
||||
@Singleton
|
||||
public Gson provideGson() {
|
||||
return GsonUtil.getDefaultGson();
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
public ReviewInterface provideReviewInterface(CommonsServiceFactory serviceFactory) {
|
||||
return serviceFactory.create(BuildConfig.COMMONS_URL, ReviewInterface.class);
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
public DepictsInterface provideDepictsInterface(CommonsServiceFactory serviceFactory) {
|
||||
return serviceFactory.create(BuildConfig.WIKIDATA_URL, DepictsInterface.class);
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
public WikiBaseInterface provideWikiBaseInterface(CommonsServiceFactory serviceFactory) {
|
||||
return serviceFactory.create(BuildConfig.COMMONS_URL, WikiBaseInterface.class);
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
public UploadInterface provideUploadInterface(CommonsServiceFactory serviceFactory) {
|
||||
return serviceFactory.create(BuildConfig.COMMONS_URL, UploadInterface.class);
|
||||
}
|
||||
|
||||
@Named("commons-page-edit-service")
|
||||
@Provides
|
||||
@Singleton
|
||||
public PageEditInterface providePageEditService(CommonsServiceFactory serviceFactory) {
|
||||
return serviceFactory.create(BuildConfig.COMMONS_URL, PageEditInterface.class);
|
||||
}
|
||||
|
||||
@Named("wikidata-page-edit-service")
|
||||
@Provides
|
||||
@Singleton
|
||||
public PageEditInterface provideWikiDataPageEditService(CommonsServiceFactory serviceFactory) {
|
||||
return serviceFactory.create(BuildConfig.WIKIDATA_URL, PageEditInterface.class);
|
||||
}
|
||||
|
||||
@Named("commons-page-edit")
|
||||
@Provides
|
||||
@Singleton
|
||||
public PageEditClient provideCommonsPageEditClient(@Named(NAMED_COMMONS_CSRF) CsrfTokenClient csrfTokenClient,
|
||||
@Named("commons-page-edit-service") PageEditInterface pageEditInterface) {
|
||||
return new PageEditClient(csrfTokenClient, pageEditInterface);
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides a singleton instance of PageEditClient for Wikidata.
|
||||
*
|
||||
* @param csrfTokenClient The client used to manage CSRF tokens.
|
||||
* @param pageEditInterface The interface for page edit operations.
|
||||
* @return A singleton instance of PageEditClient for Wikidata.
|
||||
*/
|
||||
@Named("wikidata-page-edit")
|
||||
@Provides
|
||||
@Singleton
|
||||
public PageEditClient provideWikidataPageEditClient(@Named(NAMED_WIKI_CSRF) CsrfTokenClient csrfTokenClient,
|
||||
@Named("wikidata-page-edit-service") PageEditInterface pageEditInterface) {
|
||||
return new PageEditClient(csrfTokenClient, pageEditInterface);
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
public MediaInterface provideMediaInterface(CommonsServiceFactory serviceFactory) {
|
||||
return serviceFactory.create(BuildConfig.COMMONS_URL, MediaInterface.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add provider for WikidataMediaInterface
|
||||
* It creates a retrofit service for the commons wiki site
|
||||
* @param commonsWikiSite commonsWikiSite
|
||||
* @return WikidataMediaInterface
|
||||
*/
|
||||
@Provides
|
||||
@Singleton
|
||||
public WikidataMediaInterface provideWikidataMediaInterface(CommonsServiceFactory serviceFactory) {
|
||||
return serviceFactory.create(BetaConstants.COMMONS_URL, WikidataMediaInterface.class);
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
public MediaDetailInterface providesMediaDetailInterface(CommonsServiceFactory serviceFactory) {
|
||||
return serviceFactory.create(BuildConfig.COMMONS_URL, MediaDetailInterface.class);
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
public CategoryInterface provideCategoryInterface(CommonsServiceFactory serviceFactory) {
|
||||
return serviceFactory.create(BuildConfig.COMMONS_URL, CategoryInterface.class);
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
public ThanksInterface provideThanksInterface(CommonsServiceFactory serviceFactory) {
|
||||
return serviceFactory.create(BuildConfig.COMMONS_URL, ThanksInterface.class);
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
public NotificationInterface provideNotificationInterface(CommonsServiceFactory serviceFactory) {
|
||||
return serviceFactory.create(BuildConfig.COMMONS_URL, NotificationInterface.class);
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
public UserInterface provideUserInterface(CommonsServiceFactory serviceFactory) {
|
||||
return serviceFactory.create(BuildConfig.COMMONS_URL, UserInterface.class);
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
public WikidataInterface provideWikidataInterface(CommonsServiceFactory serviceFactory) {
|
||||
return serviceFactory.create(BuildConfig.WIKIDATA_URL, WikidataInterface.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add provider for PageMediaInterface
|
||||
* It creates a retrofit service for the wiki site using device's current language
|
||||
*/
|
||||
@Provides
|
||||
@Singleton
|
||||
public PageMediaInterface providePageMediaInterface(@Named(NAMED_LANGUAGE_WIKI_PEDIA_WIKI_SITE) WikiSite wikiSite, CommonsServiceFactory serviceFactory) {
|
||||
return serviceFactory.create(wikiSite.url(), PageMediaInterface.class);
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
@Named(NAMED_LANGUAGE_WIKI_PEDIA_WIKI_SITE)
|
||||
public WikiSite provideLanguageWikipediaSite() {
|
||||
return WikiSite.forLanguageCode(Locale.getDefault().getLanguage());
|
||||
}
|
||||
}
|
||||
316
app/src/main/java/fr/free/nrw/commons/di/NetworkingModule.kt
Normal file
316
app/src/main/java/fr/free/nrw/commons/di/NetworkingModule.kt
Normal file
|
|
@ -0,0 +1,316 @@
|
|||
package fr.free.nrw.commons.di
|
||||
|
||||
import android.content.Context
|
||||
import com.google.gson.Gson
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
import fr.free.nrw.commons.BetaConstants
|
||||
import fr.free.nrw.commons.BuildConfig
|
||||
import fr.free.nrw.commons.OkHttpConnectionFactory
|
||||
import fr.free.nrw.commons.actions.PageEditClient
|
||||
import fr.free.nrw.commons.actions.PageEditInterface
|
||||
import fr.free.nrw.commons.actions.ThanksInterface
|
||||
import fr.free.nrw.commons.auth.SessionManager
|
||||
import fr.free.nrw.commons.auth.csrf.CsrfTokenClient
|
||||
import fr.free.nrw.commons.auth.csrf.CsrfTokenInterface
|
||||
import fr.free.nrw.commons.auth.csrf.LogoutClient
|
||||
import fr.free.nrw.commons.auth.login.LoginClient
|
||||
import fr.free.nrw.commons.auth.login.LoginInterface
|
||||
import fr.free.nrw.commons.category.CategoryInterface
|
||||
import fr.free.nrw.commons.explore.depictions.DepictsClient
|
||||
import fr.free.nrw.commons.kvstore.JsonKvStore
|
||||
import fr.free.nrw.commons.media.MediaDetailInterface
|
||||
import fr.free.nrw.commons.media.MediaInterface
|
||||
import fr.free.nrw.commons.media.PageMediaInterface
|
||||
import fr.free.nrw.commons.media.WikidataMediaInterface
|
||||
import fr.free.nrw.commons.mwapi.OkHttpJsonApiClient
|
||||
import fr.free.nrw.commons.mwapi.UserInterface
|
||||
import fr.free.nrw.commons.notification.NotificationInterface
|
||||
import fr.free.nrw.commons.review.ReviewInterface
|
||||
import fr.free.nrw.commons.upload.UploadInterface
|
||||
import fr.free.nrw.commons.upload.WikiBaseInterface
|
||||
import fr.free.nrw.commons.upload.depicts.DepictsInterface
|
||||
import fr.free.nrw.commons.wikidata.CommonsServiceFactory
|
||||
import fr.free.nrw.commons.wikidata.GsonUtil
|
||||
import fr.free.nrw.commons.wikidata.WikidataInterface
|
||||
import fr.free.nrw.commons.wikidata.cookies.CommonsCookieJar
|
||||
import fr.free.nrw.commons.wikidata.cookies.CommonsCookieStorage
|
||||
import fr.free.nrw.commons.wikidata.model.WikiSite
|
||||
import okhttp3.Cache
|
||||
import okhttp3.HttpUrl
|
||||
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
|
||||
import okhttp3.OkHttpClient
|
||||
import okhttp3.logging.HttpLoggingInterceptor
|
||||
import okhttp3.logging.HttpLoggingInterceptor.Level
|
||||
import timber.log.Timber
|
||||
import java.io.File
|
||||
import java.util.Locale
|
||||
import java.util.concurrent.TimeUnit
|
||||
import javax.inject.Named
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Module
|
||||
@Suppress("unused")
|
||||
class NetworkingModule {
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideOkHttpClient(
|
||||
context: Context,
|
||||
httpLoggingInterceptor: HttpLoggingInterceptor
|
||||
): OkHttpClient = OkHttpClient.Builder()
|
||||
.connectTimeout(120, TimeUnit.SECONDS)
|
||||
.writeTimeout(120, TimeUnit.SECONDS)
|
||||
.addInterceptor(httpLoggingInterceptor)
|
||||
.readTimeout(120, TimeUnit.SECONDS)
|
||||
.cache(Cache(File(context.cacheDir, "okHttpCache"), OK_HTTP_CACHE_SIZE))
|
||||
.build()
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun serviceFactory(cookieJar: CommonsCookieJar): CommonsServiceFactory =
|
||||
CommonsServiceFactory(OkHttpConnectionFactory.getClient(cookieJar))
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideHttpLoggingInterceptor(): HttpLoggingInterceptor =
|
||||
HttpLoggingInterceptor { message: String? ->
|
||||
Timber.tag("OkHttp").v(message)
|
||||
}.apply {
|
||||
level = if (BuildConfig.DEBUG) Level.BODY else Level.BASIC
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideOkHttpJsonApiClient(
|
||||
okHttpClient: OkHttpClient,
|
||||
depictsClient: DepictsClient,
|
||||
@Named("tools_forge") toolsForgeUrl: HttpUrl,
|
||||
gson: Gson
|
||||
): OkHttpJsonApiClient = OkHttpJsonApiClient(
|
||||
okHttpClient, depictsClient, toolsForgeUrl, WIKIDATA_SPARQL_QUERY_URL,
|
||||
BuildConfig.WIKIMEDIA_CAMPAIGNS_URL, gson
|
||||
)
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideCookieStorage(
|
||||
@Named("default_preferences") preferences: JsonKvStore
|
||||
): CommonsCookieStorage = CommonsCookieStorage(preferences).also {
|
||||
it.load()
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideCookieJar(storage: CommonsCookieStorage): CommonsCookieJar =
|
||||
CommonsCookieJar(storage)
|
||||
|
||||
@Named(NAMED_COMMONS_CSRF)
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideCommonsCsrfTokenClient(
|
||||
sessionManager: SessionManager,
|
||||
@Named("commons-csrf-interface") tokenInterface: CsrfTokenInterface,
|
||||
loginClient: LoginClient,
|
||||
logoutClient: LogoutClient
|
||||
): CsrfTokenClient = CsrfTokenClient(sessionManager, tokenInterface, loginClient, logoutClient)
|
||||
|
||||
/**
|
||||
* Provides a singleton instance of CsrfTokenClient for Wikidata.
|
||||
*
|
||||
* @param sessionManager The session manager to manage user sessions.
|
||||
* @param tokenInterface The interface for obtaining CSRF tokens.
|
||||
* @param loginClient The client for handling login operations.
|
||||
* @param logoutClient The client for handling logout operations.
|
||||
* @return A singleton instance of CsrfTokenClient.
|
||||
*/
|
||||
@Named(NAMED_WIKI_CSRF)
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideWikiCsrfTokenClient(
|
||||
sessionManager: SessionManager,
|
||||
@Named("wikidata-csrf-interface") tokenInterface: CsrfTokenInterface,
|
||||
loginClient: LoginClient,
|
||||
logoutClient: LogoutClient
|
||||
): CsrfTokenClient = CsrfTokenClient(sessionManager, tokenInterface, loginClient, logoutClient)
|
||||
|
||||
/**
|
||||
* Provides a singleton instance of CsrfTokenInterface for Wikidata.
|
||||
*
|
||||
* @param factory The factory used to create service interfaces.
|
||||
* @return A singleton instance of CsrfTokenInterface for Wikidata.
|
||||
*/
|
||||
@Named("wikidata-csrf-interface")
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideWikidataCsrfTokenInterface(factory: CommonsServiceFactory): CsrfTokenInterface =
|
||||
factory.create(BuildConfig.WIKIDATA_URL)
|
||||
|
||||
@Named("commons-csrf-interface")
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideCsrfTokenInterface(factory: CommonsServiceFactory): CsrfTokenInterface =
|
||||
factory.create(BuildConfig.COMMONS_URL)
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideLoginInterface(factory: CommonsServiceFactory): LoginInterface =
|
||||
factory.create(BuildConfig.COMMONS_URL)
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideLoginClient(loginInterface: LoginInterface): LoginClient =
|
||||
LoginClient(loginInterface)
|
||||
|
||||
@Provides
|
||||
@Named("tools_forge")
|
||||
fun provideToolsForgeUrl(): HttpUrl = TOOLS_FORGE_URL.toHttpUrlOrNull()!!
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
@Named(NAMED_WIKI_DATA_WIKI_SITE)
|
||||
fun provideWikidataWikiSite(): WikiSite = WikiSite(BuildConfig.WIKIDATA_URL)
|
||||
|
||||
|
||||
/**
|
||||
* Gson objects are very heavy. The app should ideally be using just one instance of it instead of creating new instances everywhere.
|
||||
* @return returns a singleton Gson instance
|
||||
*/
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideGson(): Gson = GsonUtil.getDefaultGson()
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideReviewInterface(factory: CommonsServiceFactory): ReviewInterface =
|
||||
factory.create(BuildConfig.COMMONS_URL)
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideDepictsInterface(factory: CommonsServiceFactory): DepictsInterface =
|
||||
factory.create(BuildConfig.WIKIDATA_URL)
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideWikiBaseInterface(factory: CommonsServiceFactory): WikiBaseInterface =
|
||||
factory.create(BuildConfig.COMMONS_URL)
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideUploadInterface(factory: CommonsServiceFactory): UploadInterface =
|
||||
factory.create(BuildConfig.COMMONS_URL)
|
||||
|
||||
@Named("commons-page-edit-service")
|
||||
@Provides
|
||||
@Singleton
|
||||
fun providePageEditService(factory: CommonsServiceFactory): PageEditInterface =
|
||||
factory.create(BuildConfig.COMMONS_URL)
|
||||
|
||||
@Named("wikidata-page-edit-service")
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideWikiDataPageEditService(factory: CommonsServiceFactory): PageEditInterface =
|
||||
factory.create(BuildConfig.WIKIDATA_URL)
|
||||
|
||||
@Named("commons-page-edit")
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideCommonsPageEditClient(
|
||||
@Named(NAMED_COMMONS_CSRF) csrfTokenClient: CsrfTokenClient,
|
||||
@Named("commons-page-edit-service") pageEditInterface: PageEditInterface
|
||||
): PageEditClient = PageEditClient(csrfTokenClient, pageEditInterface)
|
||||
|
||||
/**
|
||||
* Provides a singleton instance of PageEditClient for Wikidata.
|
||||
*
|
||||
* @param csrfTokenClient The client used to manage CSRF tokens.
|
||||
* @param pageEditInterface The interface for page edit operations.
|
||||
* @return A singleton instance of PageEditClient for Wikidata.
|
||||
*/
|
||||
@Named("wikidata-page-edit")
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideWikidataPageEditClient(
|
||||
@Named(NAMED_WIKI_CSRF) csrfTokenClient: CsrfTokenClient,
|
||||
@Named("wikidata-page-edit-service") pageEditInterface: PageEditInterface
|
||||
): PageEditClient = PageEditClient(csrfTokenClient, pageEditInterface)
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideMediaInterface(factory: CommonsServiceFactory): MediaInterface =
|
||||
factory.create(BuildConfig.COMMONS_URL)
|
||||
|
||||
/**
|
||||
* Add provider for WikidataMediaInterface
|
||||
* It creates a retrofit service for the commons wiki site
|
||||
* @param commonsWikiSite commonsWikiSite
|
||||
* @return WikidataMediaInterface
|
||||
*/
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideWikidataMediaInterface(factory: CommonsServiceFactory): WikidataMediaInterface =
|
||||
factory.create(BetaConstants.COMMONS_URL)
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun providesMediaDetailInterface(factory: CommonsServiceFactory): MediaDetailInterface =
|
||||
factory.create(BuildConfig.COMMONS_URL)
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideCategoryInterface(factory: CommonsServiceFactory): CategoryInterface =
|
||||
factory.create(BuildConfig.COMMONS_URL)
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideThanksInterface(factory: CommonsServiceFactory): ThanksInterface =
|
||||
factory.create(BuildConfig.COMMONS_URL)
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideNotificationInterface(factory: CommonsServiceFactory): NotificationInterface =
|
||||
factory.create(BuildConfig.COMMONS_URL)
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideUserInterface(factory: CommonsServiceFactory): UserInterface =
|
||||
factory.create(BuildConfig.COMMONS_URL)
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideWikidataInterface(factory: CommonsServiceFactory): WikidataInterface =
|
||||
factory.create(BuildConfig.WIKIDATA_URL)
|
||||
|
||||
/**
|
||||
* Add provider for PageMediaInterface
|
||||
* It creates a retrofit service for the wiki site using device's current language
|
||||
*/
|
||||
@Provides
|
||||
@Singleton
|
||||
fun providePageMediaInterface(
|
||||
@Named(NAMED_LANGUAGE_WIKI_PEDIA_WIKI_SITE) wikiSite: WikiSite,
|
||||
factory: CommonsServiceFactory
|
||||
): PageMediaInterface = factory.create(wikiSite.url())
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
@Named(NAMED_LANGUAGE_WIKI_PEDIA_WIKI_SITE)
|
||||
fun provideLanguageWikipediaSite(): WikiSite {
|
||||
return WikiSite.forLanguageCode(Locale.getDefault().language)
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val WIKIDATA_SPARQL_QUERY_URL = "https://query.wikidata.org/sparql"
|
||||
private const val TOOLS_FORGE_URL =
|
||||
"https://tools.wmflabs.org/commons-android-app/tool-commons-android-app"
|
||||
|
||||
const val OK_HTTP_CACHE_SIZE: Long = (10 * 1024 * 1024).toLong()
|
||||
|
||||
private const val NAMED_WIKI_DATA_WIKI_SITE = "wikidata-wikisite"
|
||||
private const val NAMED_WIKI_PEDIA_WIKI_SITE = "wikipedia-wikisite"
|
||||
|
||||
const val NAMED_LANGUAGE_WIKI_PEDIA_WIKI_SITE: String = "language-wikipedia-wikisite"
|
||||
|
||||
const val NAMED_COMMONS_CSRF: String = "commons-csrf"
|
||||
const val NAMED_WIKI_CSRF: String = "wiki-csrf"
|
||||
}
|
||||
}
|
||||
|
|
@ -1,19 +0,0 @@
|
|||
package fr.free.nrw.commons.di;
|
||||
|
||||
import dagger.Module;
|
||||
import dagger.android.ContributesAndroidInjector;
|
||||
import fr.free.nrw.commons.auth.WikiAccountAuthenticatorService;
|
||||
|
||||
/**
|
||||
* This Class Represents the Module for dependency injection (using dagger)
|
||||
* so, if a developer needs to add a new Service to the commons app
|
||||
* then that must be mentioned here to inject the dependencies
|
||||
*/
|
||||
@Module
|
||||
@SuppressWarnings({"WeakerAccess", "unused"})
|
||||
public abstract class ServiceBuilderModule {
|
||||
|
||||
@ContributesAndroidInjector
|
||||
abstract WikiAccountAuthenticatorService bindWikiAccountAuthenticatorService();
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
package fr.free.nrw.commons.di
|
||||
|
||||
import dagger.Module
|
||||
import dagger.android.ContributesAndroidInjector
|
||||
import fr.free.nrw.commons.auth.WikiAccountAuthenticatorService
|
||||
|
||||
/**
|
||||
* This Class Represents the Module for dependency injection (using dagger)
|
||||
* so, if a developer needs to add a new Service to the commons app
|
||||
* then that must be mentioned here to inject the dependencies
|
||||
*/
|
||||
@Module
|
||||
@Suppress("unused")
|
||||
abstract class ServiceBuilderModule {
|
||||
@ContributesAndroidInjector
|
||||
abstract fun bindWikiAccountAuthenticatorService(): WikiAccountAuthenticatorService
|
||||
}
|
||||
|
|
@ -1,141 +0,0 @@
|
|||
package fr.free.nrw.commons.language;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.Resources;
|
||||
import android.text.TextUtils;
|
||||
|
||||
import androidx.annotation.ArrayRes;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import fr.free.nrw.commons.R;
|
||||
import java.lang.ref.SoftReference;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
/** Immutable look up table for all app supported languages. All article languages may not be
|
||||
* present in this table as it is statically bundled with the app. */
|
||||
public class AppLanguageLookUpTable {
|
||||
public static final String SIMPLIFIED_CHINESE_LANGUAGE_CODE = "zh-hans";
|
||||
public static final String TRADITIONAL_CHINESE_LANGUAGE_CODE = "zh-hant";
|
||||
public static final String CHINESE_CN_LANGUAGE_CODE = "zh-cn";
|
||||
public static final String CHINESE_HK_LANGUAGE_CODE = "zh-hk";
|
||||
public static final String CHINESE_MO_LANGUAGE_CODE = "zh-mo";
|
||||
public static final String CHINESE_SG_LANGUAGE_CODE = "zh-sg";
|
||||
public static final String CHINESE_TW_LANGUAGE_CODE = "zh-tw";
|
||||
public static final String CHINESE_YUE_LANGUAGE_CODE = "zh-yue";
|
||||
public static final String CHINESE_LANGUAGE_CODE = "zh";
|
||||
public static final String NORWEGIAN_LEGACY_LANGUAGE_CODE = "no";
|
||||
public static final String NORWEGIAN_BOKMAL_LANGUAGE_CODE = "nb";
|
||||
public static final String TEST_LANGUAGE_CODE = "test";
|
||||
public static final String FALLBACK_LANGUAGE_CODE = "en"; // Must exist in preference_language_keys.
|
||||
|
||||
@NonNull private final Resources resources;
|
||||
|
||||
// Language codes for all app supported languages in fixed order. The special code representing
|
||||
// the dynamic system language is null.
|
||||
@NonNull private SoftReference<List<String>> codesRef = new SoftReference<>(null);
|
||||
|
||||
// English names for all app supported languages in fixed order.
|
||||
@NonNull private SoftReference<List<String>> canonicalNamesRef = new SoftReference<>(null);
|
||||
|
||||
// Native names for all app supported languages in fixed order.
|
||||
@NonNull private SoftReference<List<String>> localizedNamesRef = new SoftReference<>(null);
|
||||
|
||||
public AppLanguageLookUpTable(@NonNull Context context) {
|
||||
resources = context.getResources();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Nonnull immutable list. The special code representing the dynamic system language is
|
||||
* null.
|
||||
*/
|
||||
@NonNull
|
||||
public List<String> getCodes() {
|
||||
List<String> codes = codesRef.get();
|
||||
if (codes == null) {
|
||||
codes = getStringList(R.array.preference_language_keys);
|
||||
codesRef = new SoftReference<>(codes);
|
||||
}
|
||||
return codes;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public String getCanonicalName(@Nullable String code) {
|
||||
String name = defaultIndex(getCanonicalNames(), indexOfCode(code), null);
|
||||
if (TextUtils.isEmpty(name) && !TextUtils.isEmpty(code)) {
|
||||
if (code.equals(Locale.CHINESE.getLanguage())) {
|
||||
name = Locale.CHINESE.getDisplayName(Locale.ENGLISH);
|
||||
} else if (code.equals(NORWEGIAN_LEGACY_LANGUAGE_CODE)) {
|
||||
name = defaultIndex(getCanonicalNames(), indexOfCode(NORWEGIAN_BOKMAL_LANGUAGE_CODE), null);
|
||||
}
|
||||
}
|
||||
return name;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public String getLocalizedName(@Nullable String code) {
|
||||
String name = defaultIndex(getLocalizedNames(), indexOfCode(code), null);
|
||||
if (TextUtils.isEmpty(name) && !TextUtils.isEmpty(code)) {
|
||||
if (code.equals(Locale.CHINESE.getLanguage())) {
|
||||
name = Locale.CHINESE.getDisplayName(Locale.CHINESE);
|
||||
} else if (code.equals(NORWEGIAN_LEGACY_LANGUAGE_CODE)) {
|
||||
name = defaultIndex(getLocalizedNames(), indexOfCode(NORWEGIAN_BOKMAL_LANGUAGE_CODE), null);
|
||||
}
|
||||
}
|
||||
return name;
|
||||
}
|
||||
|
||||
public List<String> getCanonicalNames() {
|
||||
List<String> names = canonicalNamesRef.get();
|
||||
if (names == null) {
|
||||
names = getStringList(R.array.preference_language_canonical_names);
|
||||
canonicalNamesRef = new SoftReference<>(names);
|
||||
}
|
||||
return names;
|
||||
}
|
||||
|
||||
public List<String> getLocalizedNames() {
|
||||
List<String> names = localizedNamesRef.get();
|
||||
if (names == null) {
|
||||
names = getStringList(R.array.preference_language_local_names);
|
||||
localizedNamesRef = new SoftReference<>(names);
|
||||
}
|
||||
return names;
|
||||
}
|
||||
|
||||
public boolean isSupportedCode(@Nullable String code) {
|
||||
return getCodes().contains(code);
|
||||
}
|
||||
|
||||
private <T> T defaultIndex(List<T> list, int index, T defaultValue) {
|
||||
return inBounds(list, index) ? list.get(index) : defaultValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Searches #codes for the specified language code and returns the index for use in
|
||||
* #canonicalNames and #localizedNames.
|
||||
*
|
||||
* @param code The language code to search for. The special code representing the dynamic system
|
||||
* language is null.
|
||||
* @return The index of the language code or -1 if the code is not supported.
|
||||
*/
|
||||
private int indexOfCode(@Nullable String code) {
|
||||
return getCodes().indexOf(code);
|
||||
}
|
||||
|
||||
/** @return Nonnull immutable list. */
|
||||
@NonNull
|
||||
private List<String> getStringList(int id) {
|
||||
return Arrays.asList(getStringArray(id));
|
||||
}
|
||||
|
||||
private boolean inBounds(List<?> list, int index) {
|
||||
return index >= 0 && index < list.size();
|
||||
}
|
||||
|
||||
public String[] getStringArray(@ArrayRes int id) {
|
||||
return resources.getStringArray(id);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,135 @@
|
|||
package fr.free.nrw.commons.language
|
||||
|
||||
import android.content.Context
|
||||
import android.content.res.Resources
|
||||
import android.text.TextUtils
|
||||
|
||||
import androidx.annotation.ArrayRes
|
||||
import fr.free.nrw.commons.R
|
||||
import java.lang.ref.SoftReference
|
||||
import java.util.Arrays
|
||||
import java.util.Locale
|
||||
|
||||
|
||||
/** Immutable look up table for all app supported languages. All article languages may not be
|
||||
* present in this table as it is statically bundled with the app. */
|
||||
class AppLanguageLookUpTable(context: Context) {
|
||||
|
||||
companion object {
|
||||
const val SIMPLIFIED_CHINESE_LANGUAGE_CODE = "zh-hans"
|
||||
const val TRADITIONAL_CHINESE_LANGUAGE_CODE = "zh-hant"
|
||||
const val CHINESE_CN_LANGUAGE_CODE = "zh-cn"
|
||||
const val CHINESE_HK_LANGUAGE_CODE = "zh-hk"
|
||||
const val CHINESE_MO_LANGUAGE_CODE = "zh-mo"
|
||||
const val CHINESE_SG_LANGUAGE_CODE = "zh-sg"
|
||||
const val CHINESE_TW_LANGUAGE_CODE = "zh-tw"
|
||||
const val CHINESE_YUE_LANGUAGE_CODE = "zh-yue"
|
||||
const val CHINESE_LANGUAGE_CODE = "zh"
|
||||
const val NORWEGIAN_LEGACY_LANGUAGE_CODE = "no"
|
||||
const val NORWEGIAN_BOKMAL_LANGUAGE_CODE = "nb"
|
||||
const val TEST_LANGUAGE_CODE = "test"
|
||||
const val FALLBACK_LANGUAGE_CODE = "en" // Must exist in preference_language_keys.
|
||||
}
|
||||
|
||||
private val resources: Resources = context.resources
|
||||
|
||||
// Language codes for all app supported languages in fixed order. The special code representing
|
||||
// the dynamic system language is null.
|
||||
private var codesRef = SoftReference<List<String>>(null)
|
||||
|
||||
// English names for all app supported languages in fixed order.
|
||||
private var canonicalNamesRef = SoftReference<List<String>>(null)
|
||||
|
||||
// Native names for all app supported languages in fixed order.
|
||||
private var localizedNamesRef = SoftReference<List<String>>(null)
|
||||
|
||||
/**
|
||||
* @return Nonnull immutable list. The special code representing the dynamic system language is
|
||||
* null.
|
||||
*/
|
||||
fun getCodes(): List<String> {
|
||||
var codes = codesRef.get()
|
||||
if (codes == null) {
|
||||
codes = getStringList(R.array.preference_language_keys)
|
||||
codesRef = SoftReference(codes)
|
||||
}
|
||||
return codes
|
||||
}
|
||||
|
||||
fun getCanonicalName(code: String?): String? {
|
||||
var name = defaultIndex(getCanonicalNames(), indexOfCode(code), null)
|
||||
if (name.isNullOrEmpty() && !code.isNullOrEmpty()) {
|
||||
name = when (code) {
|
||||
Locale.CHINESE.language -> Locale.CHINESE.getDisplayName(Locale.ENGLISH)
|
||||
NORWEGIAN_LEGACY_LANGUAGE_CODE ->
|
||||
defaultIndex(getCanonicalNames(), indexOfCode(NORWEGIAN_BOKMAL_LANGUAGE_CODE), null)
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
return name
|
||||
}
|
||||
|
||||
fun getLocalizedName(code: String?): String? {
|
||||
var name = defaultIndex(getLocalizedNames(), indexOfCode(code), null)
|
||||
if (name.isNullOrEmpty() && !code.isNullOrEmpty()) {
|
||||
name = when (code) {
|
||||
Locale.CHINESE.language -> Locale.CHINESE.getDisplayName(Locale.CHINESE)
|
||||
NORWEGIAN_LEGACY_LANGUAGE_CODE ->
|
||||
defaultIndex(getLocalizedNames(), indexOfCode(NORWEGIAN_BOKMAL_LANGUAGE_CODE), null)
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
return name
|
||||
}
|
||||
|
||||
fun getCanonicalNames(): List<String> {
|
||||
var names = canonicalNamesRef.get()
|
||||
if (names == null) {
|
||||
names = getStringList(R.array.preference_language_canonical_names)
|
||||
canonicalNamesRef = SoftReference(names)
|
||||
}
|
||||
return names
|
||||
}
|
||||
|
||||
fun getLocalizedNames(): List<String> {
|
||||
var names = localizedNamesRef.get()
|
||||
if (names == null) {
|
||||
names = getStringList(R.array.preference_language_local_names)
|
||||
localizedNamesRef = SoftReference(names)
|
||||
}
|
||||
return names
|
||||
}
|
||||
|
||||
fun isSupportedCode(code: String?): Boolean {
|
||||
return getCodes().contains(code)
|
||||
}
|
||||
|
||||
private fun <T> defaultIndex(list: List<T>, index: Int, defaultValue: T?): T? {
|
||||
return if (inBounds(list, index)) list[index] else defaultValue
|
||||
}
|
||||
|
||||
/**
|
||||
* Searches #codes for the specified language code and returns the index for use in
|
||||
* #canonicalNames and #localizedNames.
|
||||
*
|
||||
* @param code The language code to search for. The special code representing the dynamic system
|
||||
* language is null.
|
||||
* @return The index of the language code or -1 if the code is not supported.
|
||||
*/
|
||||
private fun indexOfCode(code: String?): Int {
|
||||
return getCodes().indexOf(code)
|
||||
}
|
||||
|
||||
/** @return Nonnull immutable list. */
|
||||
private fun getStringList(id: Int): List<String> {
|
||||
return getStringArray(id).toList()
|
||||
}
|
||||
|
||||
private fun inBounds(list: List<*>, index: Int): Boolean {
|
||||
return index in list.indices
|
||||
}
|
||||
|
||||
fun getStringArray(@ArrayRes id: Int): Array<String> {
|
||||
return resources.getStringArray(id)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,198 +0,0 @@
|
|||
package fr.free.nrw.commons.location;
|
||||
|
||||
import android.location.Location;
|
||||
import android.net.Uri;
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
/**
|
||||
* a latitude and longitude point with accuracy information, often of a picture
|
||||
*/
|
||||
public class LatLng implements Parcelable {
|
||||
|
||||
private final double latitude;
|
||||
private final double longitude;
|
||||
private final float accuracy;
|
||||
|
||||
/**
|
||||
* Accepts latitude and longitude.
|
||||
* North and South values are cut off at 90°
|
||||
*
|
||||
* @param latitude the latitude
|
||||
* @param longitude the longitude
|
||||
* @param accuracy the accuracy
|
||||
*
|
||||
* Examples:
|
||||
* the Statue of Liberty is located at 40.69° N, 74.04° W
|
||||
* The Statue of Liberty could be constructed as LatLng(40.69, -74.04, 1.0)
|
||||
* where positive signifies north, east and negative signifies south, west.
|
||||
*/
|
||||
public LatLng(double latitude, double longitude, float accuracy) {
|
||||
if (-180.0D <= longitude && longitude < 180.0D) {
|
||||
this.longitude = longitude;
|
||||
} else {
|
||||
this.longitude = ((longitude - 180.0D) % 360.0D + 360.0D) % 360.0D - 180.0D;
|
||||
}
|
||||
this.latitude = Math.max(-90.0D, Math.min(90.0D, latitude));
|
||||
this.accuracy = accuracy;
|
||||
}
|
||||
/**
|
||||
* An alternate constructor for this class.
|
||||
* @param in A parcelable which contains the latitude, longitude, and 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
|
||||
* @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());
|
||||
}
|
||||
|
||||
/**
|
||||
* creates a hash code for the longitude and longitude
|
||||
*/
|
||||
public int hashCode() {
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* checks for equality of two LatLng objects
|
||||
* @param o the second LatLng object
|
||||
*/
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
} else if (!(o instanceof LatLng)) {
|
||||
return false;
|
||||
} else {
|
||||
LatLng var2 = (LatLng)o;
|
||||
return Double.doubleToLongBits(this.latitude) == Double.doubleToLongBits(var2.latitude) && Double.doubleToLongBits(this.longitude) == Double.doubleToLongBits(var2.longitude);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* returns a string representation of the latitude and longitude
|
||||
*/
|
||||
public String toString() {
|
||||
return "lat/lng: (" + this.latitude + "," + this.longitude + ")";
|
||||
}
|
||||
|
||||
/**
|
||||
* Rounds the float to 4 digits and returns absolute value.
|
||||
*
|
||||
* @param coordinate A coordinate value as string.
|
||||
* @return String of the rounded number.
|
||||
*/
|
||||
private String formatCoordinate(double coordinate) {
|
||||
double roundedNumber = Math.round(coordinate * 10000d) / 10000d;
|
||||
double absoluteNumber = Math.abs(roundedNumber);
|
||||
return String.valueOf(absoluteNumber);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns "N" or "S" depending on the latitude.
|
||||
*
|
||||
* @return "N" or "S".
|
||||
*/
|
||||
private String getNorthSouth() {
|
||||
if (this.latitude < 0) {
|
||||
return "S";
|
||||
}
|
||||
|
||||
return "N";
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns "E" or "W" depending on the longitude.
|
||||
*
|
||||
* @return "E" or "W".
|
||||
*/
|
||||
private String getEastWest() {
|
||||
if (this.longitude >= 0 && this.longitude < 180) {
|
||||
return "E";
|
||||
}
|
||||
|
||||
return "W";
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a nicely formatted coordinate string. Used e.g. in
|
||||
* the detail view.
|
||||
*
|
||||
* @return The formatted string.
|
||||
*/
|
||||
public String getPrettyCoordinateString() {
|
||||
return formatCoordinate(this.latitude) + " " + this.getNorthSouth() + ", "
|
||||
+ formatCoordinate(this.longitude) + " " + this.getEastWest();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the location accuracy in meter.
|
||||
*
|
||||
* @return float
|
||||
*/
|
||||
public float getAccuracy() {
|
||||
return accuracy;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the longitude in degrees.
|
||||
*
|
||||
* @return double
|
||||
*/
|
||||
public double getLongitude() {
|
||||
return longitude;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the latitude in degrees.
|
||||
*
|
||||
* @return double
|
||||
*/
|
||||
public double getLatitude() {
|
||||
return latitude;
|
||||
}
|
||||
|
||||
public Uri getGmmIntentUri() {
|
||||
return Uri.parse("geo:" + latitude + "," + longitude + "?z=16");
|
||||
}
|
||||
|
||||
@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];
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
150
app/src/main/java/fr/free/nrw/commons/location/LatLng.kt
Normal file
150
app/src/main/java/fr/free/nrw/commons/location/LatLng.kt
Normal file
|
|
@ -0,0 +1,150 @@
|
|||
package fr.free.nrw.commons.location
|
||||
|
||||
import android.location.Location
|
||||
import android.net.Uri
|
||||
import android.os.Parcel
|
||||
import android.os.Parcelable
|
||||
import kotlin.math.abs
|
||||
import kotlin.math.max
|
||||
import kotlin.math.min
|
||||
import kotlin.math.round
|
||||
|
||||
|
||||
/**
|
||||
* A latitude and longitude point with accuracy information, often of a picture.
|
||||
*/
|
||||
data class LatLng(
|
||||
var latitude: Double,
|
||||
var longitude: Double,
|
||||
val accuracy: Float
|
||||
) : Parcelable {
|
||||
|
||||
/**
|
||||
* Accepts latitude and longitude.
|
||||
* North and South values are cut off at 90°
|
||||
*
|
||||
* Examples:
|
||||
* the Statue of Liberty is located at 40.69° N, 74.04° W
|
||||
* The Statue of Liberty could be constructed as LatLng(40.69, -74.04, 1.0)
|
||||
* where positive signifies north, east and negative signifies south, west.
|
||||
*/
|
||||
init {
|
||||
val adjustedLongitude = when {
|
||||
longitude in -180.0..180.0 -> longitude
|
||||
else -> ((longitude - 180.0) % 360.0 + 360.0) % 360.0 - 180.0
|
||||
}
|
||||
latitude = max(-90.0, min(90.0, latitude))
|
||||
longitude = adjustedLongitude
|
||||
}
|
||||
|
||||
/**
|
||||
* Accepts a non-null [Location] and converts it to a [LatLng].
|
||||
*/
|
||||
companion object {
|
||||
/**
|
||||
* gets the latitude and longitude of a given non-null location
|
||||
* @param location the non-null location of the user
|
||||
* @return LatLng the Latitude and Longitude of a given location
|
||||
*/
|
||||
@JvmStatic
|
||||
fun from(location: Location): LatLng {
|
||||
return LatLng(location.latitude, location.longitude, location.accuracy)
|
||||
}
|
||||
|
||||
@JvmField
|
||||
val CREATOR: Parcelable.Creator<LatLng> = object : Parcelable.Creator<LatLng> {
|
||||
override fun createFromParcel(parcel: Parcel): LatLng {
|
||||
return LatLng(parcel)
|
||||
}
|
||||
|
||||
override fun newArray(size: Int): Array<LatLng?> {
|
||||
return arrayOfNulls(size)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An alternate constructor for this class.
|
||||
* @param parcel A parcelable which contains the latitude, longitude, and accuracy
|
||||
*/
|
||||
private constructor(parcel: Parcel) : this(
|
||||
latitude = parcel.readDouble(),
|
||||
longitude = parcel.readDouble(),
|
||||
accuracy = parcel.readFloat()
|
||||
)
|
||||
|
||||
/**
|
||||
* Creates a hash code for the latitude and longitude.
|
||||
*/
|
||||
override fun hashCode(): Int {
|
||||
var result = 1
|
||||
val latitudeBits = latitude.toBits()
|
||||
result = 31 * result + (latitudeBits xor (latitudeBits ushr 32)).toInt()
|
||||
val longitudeBits = longitude.toBits()
|
||||
result = 31 * result + (longitudeBits xor (longitudeBits ushr 32)).toInt()
|
||||
return result
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks for equality of two LatLng objects.
|
||||
* @param other the second LatLng object
|
||||
*/
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
if (other !is LatLng) return false
|
||||
return latitude.toBits() == other.latitude.toBits() &&
|
||||
longitude.toBits() == other.longitude.toBits()
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a string representation of the latitude and longitude.
|
||||
*/
|
||||
override fun toString(): String {
|
||||
return "lat/lng: ($latitude,$longitude)"
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a nicely formatted coordinate string. Used e.g. in
|
||||
* the detail view.
|
||||
*
|
||||
* @return The formatted string.
|
||||
*/
|
||||
fun getPrettyCoordinateString(): String {
|
||||
return "${formatCoordinate(latitude)} ${getNorthSouth()}, " +
|
||||
"${formatCoordinate(longitude)} ${getEastWest()}"
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a URI for a Google Maps intent at the location.
|
||||
*/
|
||||
fun getGmmIntentUri(): Uri {
|
||||
return Uri.parse("geo:$latitude,$longitude?z=16")
|
||||
}
|
||||
|
||||
override fun writeToParcel(parcel: Parcel, flags: Int) {
|
||||
parcel.writeDouble(latitude)
|
||||
parcel.writeDouble(longitude)
|
||||
parcel.writeFloat(accuracy)
|
||||
}
|
||||
|
||||
override fun describeContents(): Int = 0
|
||||
|
||||
private fun formatCoordinate(coordinate: Double): String {
|
||||
val roundedNumber = round(coordinate * 10000) / 10000
|
||||
return abs(roundedNumber).toString()
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns "N" or "S" depending on the latitude.
|
||||
*
|
||||
* @return "N" or "S".
|
||||
*/
|
||||
private fun getNorthSouth(): String = if (latitude < 0) "S" else "N"
|
||||
|
||||
/**
|
||||
* Returns "E" or "W" depending on the longitude.
|
||||
*
|
||||
* @return "E" or "W".
|
||||
*/
|
||||
private fun getEastWest(): String = if (longitude in 0.0..179.999) "E" else "W"
|
||||
}
|
||||
|
|
@ -1,186 +0,0 @@
|
|||
package fr.free.nrw.commons.location;
|
||||
|
||||
import android.Manifest;
|
||||
import android.Manifest.permission;
|
||||
import android.app.Activity;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.net.Uri;
|
||||
import android.provider.Settings;
|
||||
import android.widget.Toast;
|
||||
import androidx.core.app.ActivityCompat;
|
||||
import fr.free.nrw.commons.R;
|
||||
import fr.free.nrw.commons.filepicker.Constants.RequestCodes;
|
||||
import fr.free.nrw.commons.utils.DialogUtil;
|
||||
import fr.free.nrw.commons.utils.PermissionUtils;
|
||||
|
||||
/**
|
||||
* Helper class to handle location permissions.
|
||||
*
|
||||
* Location flow for fragments containing a map is as follows:
|
||||
* Case 1: When location permission has never been asked for or denied before
|
||||
* Check if permission is already granted or not.
|
||||
* If not already granted, ask for it (if it isn't denied twice before).
|
||||
* If now user grants permission, go to Case 3/4, else go to Case 2.
|
||||
*
|
||||
* Case 2: When location permission is just asked but has been denied
|
||||
* Shows a toast to tell the user why location permission is needed.
|
||||
* Also shows a rationale to the user, on agreeing to which, we go back to Case 1.
|
||||
* Show current location / nearby pins / nearby images according to the default location.
|
||||
*
|
||||
* Case 3: When location permission are already granted, but location services are off
|
||||
* Asks the user to turn on the location service, using a dialog.
|
||||
* If the user rejects, checks for the last known location and shows stuff using that location.
|
||||
* Also displays a toast telling the user why location should be turned on.
|
||||
*
|
||||
* Case 4: When location permission has been granted and location services are also on
|
||||
* Do whatever is required by that particular activity / fragment using current location.
|
||||
*
|
||||
*/
|
||||
public class LocationPermissionsHelper {
|
||||
|
||||
Activity activity;
|
||||
LocationServiceManager locationManager;
|
||||
LocationPermissionCallback callback;
|
||||
|
||||
public LocationPermissionsHelper(Activity activity, LocationServiceManager locationManager,
|
||||
LocationPermissionCallback callback) {
|
||||
this.activity = activity;
|
||||
this.locationManager = locationManager;
|
||||
this.callback = callback;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ask for location permission if the user agrees on attaching location with pictures and the
|
||||
* app does not have the access to location
|
||||
*
|
||||
* @param dialogTitleResource Resource id of the title of the dialog
|
||||
* @param dialogTextResource Resource id of the text of the dialog
|
||||
*/
|
||||
public void requestForLocationAccess(
|
||||
int dialogTitleResource,
|
||||
int dialogTextResource
|
||||
) {
|
||||
if (checkLocationPermission(activity)) {
|
||||
callback.onLocationPermissionGranted();
|
||||
} else {
|
||||
if (ActivityCompat.shouldShowRequestPermissionRationale(activity,
|
||||
permission.ACCESS_FINE_LOCATION)) {
|
||||
DialogUtil.showAlertDialog(activity, activity.getString(dialogTitleResource),
|
||||
activity.getString(dialogTextResource),
|
||||
activity.getString(android.R.string.ok),
|
||||
activity.getString(android.R.string.cancel),
|
||||
() -> {
|
||||
ActivityCompat.requestPermissions(activity,
|
||||
new String[]{permission.ACCESS_FINE_LOCATION}, 1);
|
||||
},
|
||||
() -> callback.onLocationPermissionDenied(
|
||||
activity.getString(R.string.upload_map_location_access)),
|
||||
null,
|
||||
false);
|
||||
} else {
|
||||
ActivityCompat.requestPermissions(activity,
|
||||
new String[]{permission.ACCESS_FINE_LOCATION},
|
||||
RequestCodes.LOCATION);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows a dialog for user to open the settings page and turn on location services
|
||||
*
|
||||
* @param activity Activity object
|
||||
* @param dialogTextResource int id of the required string resource
|
||||
*/
|
||||
public void showLocationOffDialog(Activity activity, int dialogTextResource) {
|
||||
DialogUtil
|
||||
.showAlertDialog(activity,
|
||||
activity.getString(R.string.ask_to_turn_location_on),
|
||||
activity.getString(dialogTextResource),
|
||||
activity.getString(R.string.title_app_shortcut_setting),
|
||||
activity.getString(R.string.cancel),
|
||||
() -> openLocationSettings(activity),
|
||||
() -> Toast.makeText(activity, activity.getString(dialogTextResource),
|
||||
Toast.LENGTH_LONG).show()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens the location access page in settings, for user to turn on location services
|
||||
*
|
||||
* @param activity Activtiy object
|
||||
*/
|
||||
public void openLocationSettings(Activity activity) {
|
||||
final Intent intent = new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS);
|
||||
final PackageManager packageManager = activity.getPackageManager();
|
||||
|
||||
if (intent.resolveActivity(packageManager) != null) {
|
||||
activity.startActivity(intent);
|
||||
} else {
|
||||
Toast.makeText(activity, R.string.cannot_open_location_settings, Toast.LENGTH_LONG)
|
||||
.show();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows a dialog for user to open the app's settings page and give location permission
|
||||
*
|
||||
* @param activity Activity object
|
||||
* @param dialogTextResource int id of the required string resource
|
||||
*/
|
||||
public void showAppSettingsDialog(Activity activity, int dialogTextResource) {
|
||||
DialogUtil
|
||||
.showAlertDialog(activity, activity.getString(R.string.location_permission_title),
|
||||
activity.getString(dialogTextResource),
|
||||
activity.getString(R.string.title_app_shortcut_setting),
|
||||
activity.getString(R.string.cancel),
|
||||
() -> openAppSettings(activity),
|
||||
() -> Toast.makeText(activity, activity.getString(dialogTextResource),
|
||||
Toast.LENGTH_LONG).show()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens detailed settings page of the app for the user to turn on location services
|
||||
*
|
||||
* @param activity Activity object
|
||||
*/
|
||||
public void openAppSettings(Activity activity) {
|
||||
Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
|
||||
Uri uri = Uri.fromParts("package", activity.getPackageName(), null);
|
||||
intent.setData(uri);
|
||||
activity.startActivity(intent);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Check if apps have access to location even after having individual access
|
||||
*
|
||||
* @return Returns true if location services are on and false otherwise
|
||||
*/
|
||||
public boolean isLocationAccessToAppsTurnedOn() {
|
||||
return (locationManager.isNetworkProviderEnabled()
|
||||
|| locationManager.isGPSProviderEnabled());
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if location permission is already granted or not
|
||||
*
|
||||
* @param activity Activity object
|
||||
* @return Returns true if location permission is granted and false otherwise
|
||||
*/
|
||||
public boolean checkLocationPermission(Activity activity) {
|
||||
return PermissionUtils.hasPermission(activity,
|
||||
new String[]{Manifest.permission.ACCESS_FINE_LOCATION});
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle onPermissionDenied within individual classes based on the requirements
|
||||
*/
|
||||
public interface LocationPermissionCallback {
|
||||
|
||||
void onLocationPermissionDenied(String toastMessage);
|
||||
|
||||
void onLocationPermissionGranted();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,200 @@
|
|||
package fr.free.nrw.commons.location
|
||||
|
||||
import android.Manifest.permission
|
||||
import android.app.Activity
|
||||
import android.content.Intent
|
||||
import android.net.Uri
|
||||
import android.provider.Settings
|
||||
import android.widget.Toast
|
||||
import androidx.core.app.ActivityCompat
|
||||
import fr.free.nrw.commons.R
|
||||
import fr.free.nrw.commons.filepicker.Constants.RequestCodes
|
||||
import fr.free.nrw.commons.utils.DialogUtil
|
||||
import fr.free.nrw.commons.utils.PermissionUtils
|
||||
|
||||
/**
|
||||
* Helper class to handle location permissions.
|
||||
*
|
||||
* Location flow for fragments containing a map is as follows:
|
||||
* Case 1: When location permission has never been asked for or denied before
|
||||
* Check if permission is already granted or not.
|
||||
* If not already granted, ask for it (if it isn't denied twice before).
|
||||
* If now user grants permission, go to Case 3/4, else go to Case 2.
|
||||
*
|
||||
* Case 2: When location permission is just asked but has been denied
|
||||
* Shows a toast to tell the user why location permission is needed.
|
||||
* Also shows a rationale to the user, on agreeing to which, we go back to Case 1.
|
||||
* Show current location / nearby pins / nearby images according to the default location.
|
||||
*
|
||||
* Case 3: When location permission are already granted, but location services are off
|
||||
* Asks the user to turn on the location service, using a dialog.
|
||||
* If the user rejects, checks for the last known location and shows stuff using that location.
|
||||
* Also displays a toast telling the user why location should be turned on.
|
||||
*
|
||||
* Case 4: When location permission has been granted and location services are also on
|
||||
* Do whatever is required by that particular activity / fragment using current location.
|
||||
*
|
||||
*/
|
||||
class LocationPermissionsHelper(
|
||||
private val activity: Activity,
|
||||
private val locationManager: LocationServiceManager,
|
||||
private val callback: LocationPermissionCallback?
|
||||
) {
|
||||
|
||||
/**
|
||||
* Ask for location permission if the user agrees on attaching location with pictures and the
|
||||
* app does not have the access to location
|
||||
*
|
||||
* @param dialogTitleResource Resource id of the title of the dialog
|
||||
* @param dialogTextResource Resource id of the text of the dialog
|
||||
*/
|
||||
fun requestForLocationAccess(
|
||||
dialogTitleResource: Int,
|
||||
dialogTextResource: Int
|
||||
) {
|
||||
if (checkLocationPermission(activity)) {
|
||||
callback?.onLocationPermissionGranted()
|
||||
} else {
|
||||
if (ActivityCompat.shouldShowRequestPermissionRationale(
|
||||
activity,
|
||||
permission.ACCESS_FINE_LOCATION
|
||||
)
|
||||
) {
|
||||
DialogUtil.showAlertDialog(
|
||||
activity,
|
||||
activity.getString(dialogTitleResource),
|
||||
activity.getString(dialogTextResource),
|
||||
activity.getString(android.R.string.ok),
|
||||
activity.getString(android.R.string.cancel),
|
||||
{
|
||||
ActivityCompat.requestPermissions(
|
||||
activity,
|
||||
arrayOf(permission.ACCESS_FINE_LOCATION),
|
||||
1
|
||||
)
|
||||
},
|
||||
{
|
||||
callback?.onLocationPermissionDenied(
|
||||
activity.getString(R.string.upload_map_location_access)
|
||||
)
|
||||
},
|
||||
null,
|
||||
false
|
||||
)
|
||||
} else {
|
||||
ActivityCompat.requestPermissions(
|
||||
activity,
|
||||
arrayOf(permission.ACCESS_FINE_LOCATION),
|
||||
RequestCodes.LOCATION
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows a dialog for user to open the settings page and turn on location services
|
||||
*
|
||||
* @param activity Activity object
|
||||
* @param dialogTextResource int id of the required string resource
|
||||
*/
|
||||
fun showLocationOffDialog(activity: Activity, dialogTextResource: Int) {
|
||||
DialogUtil.showAlertDialog(
|
||||
activity,
|
||||
activity.getString(R.string.ask_to_turn_location_on),
|
||||
activity.getString(dialogTextResource),
|
||||
activity.getString(R.string.title_app_shortcut_setting),
|
||||
activity.getString(R.string.cancel),
|
||||
{ openLocationSettings(activity) },
|
||||
{
|
||||
Toast.makeText(
|
||||
activity,
|
||||
activity.getString(dialogTextResource),
|
||||
Toast.LENGTH_LONG
|
||||
).show()
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens the location access page in settings, for user to turn on location services
|
||||
*
|
||||
* @param activity Activity object
|
||||
*/
|
||||
fun openLocationSettings(activity: Activity) {
|
||||
val intent = Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS)
|
||||
val packageManager = activity.packageManager
|
||||
|
||||
if (intent.resolveActivity(packageManager) != null) {
|
||||
activity.startActivity(intent)
|
||||
} else {
|
||||
Toast.makeText(activity, R.string.cannot_open_location_settings, Toast.LENGTH_LONG)
|
||||
.show()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows a dialog for user to open the app's settings page and give location permission
|
||||
*
|
||||
* @param activity Activity object
|
||||
* @param dialogTextResource int id of the required string resource
|
||||
*/
|
||||
fun showAppSettingsDialog(activity: Activity, dialogTextResource: Int) {
|
||||
DialogUtil.showAlertDialog(
|
||||
activity,
|
||||
activity.getString(R.string.location_permission_title),
|
||||
activity.getString(dialogTextResource),
|
||||
activity.getString(R.string.title_app_shortcut_setting),
|
||||
activity.getString(R.string.cancel),
|
||||
{ openAppSettings(activity) },
|
||||
{
|
||||
Toast.makeText(
|
||||
activity,
|
||||
activity.getString(dialogTextResource),
|
||||
Toast.LENGTH_LONG
|
||||
).show()
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens detailed settings page of the app for the user to turn on location services
|
||||
*
|
||||
* @param activity Activity object
|
||||
*/
|
||||
private fun openAppSettings(activity: Activity) {
|
||||
val intent = Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS)
|
||||
val uri = Uri.fromParts("package", activity.packageName, null)
|
||||
intent.data = uri
|
||||
activity.startActivity(intent)
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if apps have access to location even after having individual access
|
||||
*
|
||||
* @return Returns true if location services are on and false otherwise
|
||||
*/
|
||||
fun isLocationAccessToAppsTurnedOn(): Boolean {
|
||||
return locationManager.isNetworkProviderEnabled() || locationManager.isGPSProviderEnabled()
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if location permission is already granted or not
|
||||
*
|
||||
* @param activity Activity object
|
||||
* @return Returns true if location permission is granted and false otherwise
|
||||
*/
|
||||
fun checkLocationPermission(activity: Activity): Boolean {
|
||||
return PermissionUtils.hasPermission(
|
||||
activity,
|
||||
arrayOf(permission.ACCESS_FINE_LOCATION)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle onPermissionDenied within individual classes based on the requirements
|
||||
*/
|
||||
interface LocationPermissionCallback {
|
||||
fun onLocationPermissionDenied(toastMessage: String)
|
||||
fun onLocationPermissionGranted()
|
||||
}
|
||||
}
|
||||
|
|
@ -1,274 +0,0 @@
|
|||
package fr.free.nrw.commons.location;
|
||||
|
||||
import android.Manifest.permission;
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.location.Location;
|
||||
import android.location.LocationListener;
|
||||
import android.location.LocationManager;
|
||||
import android.os.Bundle;
|
||||
import androidx.core.app.ActivityCompat;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
|
||||
import timber.log.Timber;
|
||||
|
||||
public class LocationServiceManager implements LocationListener {
|
||||
|
||||
// Maybe these values can be improved for efficiency
|
||||
private static final long MIN_LOCATION_UPDATE_REQUEST_TIME_IN_MILLIS = 10 * 100;
|
||||
private static final long MIN_LOCATION_UPDATE_REQUEST_DISTANCE_IN_METERS = 1;
|
||||
|
||||
private LocationManager locationManager;
|
||||
private Location lastLocation;
|
||||
//private Location lastLocationDuplicate; // Will be used for nearby card view on contributions activity
|
||||
private final List<LocationUpdateListener> locationListeners = new CopyOnWriteArrayList<>();
|
||||
private boolean isLocationManagerRegistered = false;
|
||||
private Set<Activity> locationExplanationDisplayed = new HashSet<>();
|
||||
private Context context;
|
||||
|
||||
/**
|
||||
* Constructs a new instance of LocationServiceManager.
|
||||
*
|
||||
* @param context the context
|
||||
*/
|
||||
public LocationServiceManager(Context context) {
|
||||
this.context = context;
|
||||
this.locationManager = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE);
|
||||
}
|
||||
|
||||
public LatLng getLastLocation() {
|
||||
if (lastLocation == null) {
|
||||
lastLocation = getLastKnownLocation();
|
||||
if(lastLocation != null) {
|
||||
return LatLng.from(lastLocation);
|
||||
}
|
||||
else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return LatLng.from(lastLocation);
|
||||
}
|
||||
|
||||
private Location getLastKnownLocation() {
|
||||
List<String> providers = locationManager.getProviders(true);
|
||||
Location bestLocation = null;
|
||||
for (String provider : providers) {
|
||||
Location l=null;
|
||||
if (ActivityCompat.checkSelfPermission(context, permission.ACCESS_FINE_LOCATION)
|
||||
== PackageManager.PERMISSION_GRANTED
|
||||
&& ActivityCompat.checkSelfPermission(context, permission.ACCESS_COARSE_LOCATION)
|
||||
== PackageManager.PERMISSION_GRANTED) {
|
||||
l = locationManager.getLastKnownLocation(provider);
|
||||
}
|
||||
if (l == null) {
|
||||
continue;
|
||||
}
|
||||
if (bestLocation == null
|
||||
|| l.getAccuracy() < bestLocation.getAccuracy()) {
|
||||
bestLocation = l;
|
||||
}
|
||||
}
|
||||
if (bestLocation == null) {
|
||||
return null;
|
||||
}
|
||||
return bestLocation;
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a LocationManager to listen for current location.
|
||||
*/
|
||||
public void registerLocationManager() {
|
||||
if (!isLocationManagerRegistered) {
|
||||
isLocationManagerRegistered = requestLocationUpdatesFromProvider(LocationManager.NETWORK_PROVIDER)
|
||||
&& requestLocationUpdatesFromProvider(LocationManager.GPS_PROVIDER);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Requests location updates from the specified provider.
|
||||
*
|
||||
* @param locationProvider the location provider
|
||||
* @return true if successful
|
||||
*/
|
||||
public boolean requestLocationUpdatesFromProvider(String locationProvider) {
|
||||
try {
|
||||
// If both providers are not available
|
||||
if (locationManager == null || !(locationManager.getAllProviders().contains(locationProvider))) {
|
||||
return false;
|
||||
}
|
||||
locationManager.requestLocationUpdates(locationProvider,
|
||||
MIN_LOCATION_UPDATE_REQUEST_TIME_IN_MILLIS,
|
||||
MIN_LOCATION_UPDATE_REQUEST_DISTANCE_IN_METERS,
|
||||
this);
|
||||
return true;
|
||||
} catch (IllegalArgumentException e) {
|
||||
Timber.e(e, "Illegal argument exception");
|
||||
return false;
|
||||
} catch (SecurityException e) {
|
||||
Timber.e(e, "Security exception");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether a given location is better than the current best location.
|
||||
*
|
||||
* @param location the location to be tested
|
||||
* @param currentBestLocation the current best location
|
||||
* @return LOCATION_SIGNIFICANTLY_CHANGED if location changed significantly
|
||||
* LOCATION_SLIGHTLY_CHANGED if location changed slightly
|
||||
*/
|
||||
private LocationChangeType isBetterLocation(Location location, Location currentBestLocation) {
|
||||
|
||||
if (currentBestLocation == null) {
|
||||
// A new location is always better than no location
|
||||
return LocationChangeType.LOCATION_SIGNIFICANTLY_CHANGED;
|
||||
}
|
||||
|
||||
// Check whether the new location fix is newer or older
|
||||
long timeDelta = location.getTime() - currentBestLocation.getTime();
|
||||
boolean isSignificantlyNewer = timeDelta > MIN_LOCATION_UPDATE_REQUEST_TIME_IN_MILLIS;
|
||||
boolean isNewer = timeDelta > 0;
|
||||
|
||||
// Check whether the new location fix is more or less accurate
|
||||
int accuracyDelta = (int) (location.getAccuracy() - currentBestLocation.getAccuracy());
|
||||
boolean isLessAccurate = accuracyDelta > 0;
|
||||
boolean isMoreAccurate = accuracyDelta < 0;
|
||||
boolean isSignificantlyLessAccurate = accuracyDelta > 200;
|
||||
|
||||
// Check if the old and new location are from the same provider
|
||||
boolean isFromSameProvider = isSameProvider(location.getProvider(),
|
||||
currentBestLocation.getProvider());
|
||||
|
||||
float[] results = new float[5];
|
||||
Location.distanceBetween(
|
||||
currentBestLocation.getLatitude(),
|
||||
currentBestLocation.getLongitude(),
|
||||
location.getLatitude(),
|
||||
location.getLongitude(),
|
||||
results);
|
||||
|
||||
// If it's been more than two minutes since the current location, use the new location
|
||||
// because the user has likely moved
|
||||
if (isSignificantlyNewer
|
||||
|| isMoreAccurate
|
||||
|| (isNewer && !isLessAccurate)
|
||||
|| (isNewer && !isSignificantlyLessAccurate && isFromSameProvider)) {
|
||||
if (results[0] < 1000) { // Means change is smaller than 1000 meter
|
||||
return LocationChangeType.LOCATION_SLIGHTLY_CHANGED;
|
||||
} else {
|
||||
return LocationChangeType.LOCATION_SIGNIFICANTLY_CHANGED;
|
||||
}
|
||||
} else{
|
||||
return LocationChangeType.LOCATION_NOT_CHANGED;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether two providers are the same
|
||||
*/
|
||||
private boolean isSameProvider(String provider1, String provider2) {
|
||||
if (provider1 == null) {
|
||||
return provider2 == null;
|
||||
}
|
||||
return provider1.equals(provider2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Unregisters location manager.
|
||||
*/
|
||||
public void unregisterLocationManager() {
|
||||
isLocationManagerRegistered = false;
|
||||
locationExplanationDisplayed.clear();
|
||||
try {
|
||||
locationManager.removeUpdates(this);
|
||||
} catch (SecurityException e) {
|
||||
Timber.e(e, "Security exception");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a new listener to the list of location listeners.
|
||||
*
|
||||
* @param listener the new listener
|
||||
*/
|
||||
public void addLocationListener(LocationUpdateListener listener) {
|
||||
if (!locationListeners.contains(listener)) {
|
||||
locationListeners.add(listener);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a listener from the list of location listeners.
|
||||
*
|
||||
* @param listener the listener to be removed
|
||||
*/
|
||||
public void removeLocationListener(LocationUpdateListener listener) {
|
||||
locationListeners.remove(listener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLocationChanged(Location location) {
|
||||
Timber.d("on location changed");
|
||||
if (isBetterLocation(location, lastLocation)
|
||||
.equals(LocationChangeType.LOCATION_SIGNIFICANTLY_CHANGED)) {
|
||||
lastLocation = location;
|
||||
//lastLocationDuplicate = location;
|
||||
for (LocationUpdateListener listener : locationListeners) {
|
||||
listener.onLocationChangedSignificantly(LatLng.from(lastLocation));
|
||||
}
|
||||
} else if (location.distanceTo(lastLocation) >= 500) {
|
||||
// Update nearby notification card at every 500 meters.
|
||||
for (LocationUpdateListener listener : locationListeners) {
|
||||
listener.onLocationChangedMedium(LatLng.from(lastLocation));
|
||||
}
|
||||
}
|
||||
|
||||
else if (isBetterLocation(location, lastLocation)
|
||||
.equals(LocationChangeType.LOCATION_SLIGHTLY_CHANGED)) {
|
||||
lastLocation = location;
|
||||
//lastLocationDuplicate = location;
|
||||
for (LocationUpdateListener listener : locationListeners) {
|
||||
listener.onLocationChangedSlightly(LatLng.from(lastLocation));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStatusChanged(String provider, int status, Bundle extras) {
|
||||
Timber.d("%s's status changed to %d", provider, status);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onProviderEnabled(String provider) {
|
||||
Timber.d("Provider %s enabled", provider);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onProviderDisabled(String provider) {
|
||||
Timber.d("Provider %s disabled", provider);
|
||||
}
|
||||
|
||||
public boolean isNetworkProviderEnabled() {
|
||||
return locationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER);
|
||||
}
|
||||
|
||||
public boolean isGPSProviderEnabled() {
|
||||
return locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER);
|
||||
}
|
||||
|
||||
public enum LocationChangeType{
|
||||
LOCATION_SIGNIFICANTLY_CHANGED, //Went out of borders of nearby markers
|
||||
LOCATION_SLIGHTLY_CHANGED, //User might be walking or driving
|
||||
LOCATION_MEDIUM_CHANGED, //Between slight and significant changes, will be used for nearby card view updates.
|
||||
LOCATION_NOT_CHANGED,
|
||||
PERMISSION_JUST_GRANTED,
|
||||
MAP_UPDATED,
|
||||
SEARCH_CUSTOM_AREA,
|
||||
CUSTOM_QUERY
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,255 @@
|
|||
package fr.free.nrw.commons.location
|
||||
|
||||
import android.Manifest
|
||||
import android.app.Activity
|
||||
import android.content.Context
|
||||
import android.content.pm.PackageManager
|
||||
import android.location.Location
|
||||
import android.location.LocationListener
|
||||
import android.location.LocationManager
|
||||
import android.os.Bundle
|
||||
import androidx.core.app.ActivityCompat
|
||||
import timber.log.Timber
|
||||
import java.util.concurrent.CopyOnWriteArrayList
|
||||
|
||||
|
||||
class LocationServiceManager(private val context: Context) : LocationListener {
|
||||
|
||||
companion object {
|
||||
// Maybe these values can be improved for efficiency
|
||||
private const val MIN_LOCATION_UPDATE_REQUEST_TIME_IN_MILLIS = 10 * 100L
|
||||
private const val MIN_LOCATION_UPDATE_REQUEST_DISTANCE_IN_METERS = 1f
|
||||
}
|
||||
|
||||
private val locationManager: LocationManager =
|
||||
context.getSystemService(Context.LOCATION_SERVICE) as LocationManager
|
||||
private var lastLocationVar: Location? = null
|
||||
private val locationListeners = CopyOnWriteArrayList<LocationUpdateListener>()
|
||||
private var isLocationManagerRegistered = false
|
||||
private val locationExplanationDisplayed = mutableSetOf<Activity>()
|
||||
|
||||
/**
|
||||
* Constructs a new instance of LocationServiceManager.
|
||||
*
|
||||
*/
|
||||
fun getLastLocation(): LatLng? {
|
||||
if (lastLocationVar == null) {
|
||||
lastLocationVar = getLastKnownLocation()
|
||||
return lastLocationVar?.let { LatLng.from(it) }
|
||||
}
|
||||
return LatLng.from(lastLocationVar!!)
|
||||
}
|
||||
|
||||
private fun getLastKnownLocation(): Location? {
|
||||
val providers = locationManager.getProviders(true)
|
||||
var bestLocation: Location? = null
|
||||
for (provider in providers) {
|
||||
val location: Location? = if (
|
||||
ActivityCompat.checkSelfPermission(
|
||||
context,
|
||||
Manifest.permission.ACCESS_FINE_LOCATION)
|
||||
==
|
||||
PackageManager.PERMISSION_GRANTED &&
|
||||
ActivityCompat.checkSelfPermission(
|
||||
context,
|
||||
Manifest.permission.ACCESS_COARSE_LOCATION)
|
||||
==
|
||||
PackageManager.PERMISSION_GRANTED
|
||||
) {
|
||||
locationManager.getLastKnownLocation(provider)
|
||||
} else {
|
||||
null
|
||||
}
|
||||
|
||||
if (
|
||||
location != null
|
||||
&&
|
||||
(bestLocation == null || location.accuracy < bestLocation.accuracy)
|
||||
) {
|
||||
bestLocation = location
|
||||
}
|
||||
}
|
||||
return bestLocation
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a LocationManager to listen for current location.
|
||||
*/
|
||||
fun registerLocationManager() {
|
||||
if (!isLocationManagerRegistered) {
|
||||
isLocationManagerRegistered =
|
||||
requestLocationUpdatesFromProvider(LocationManager.NETWORK_PROVIDER) &&
|
||||
requestLocationUpdatesFromProvider(LocationManager.GPS_PROVIDER)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Requests location updates from the specified provider.
|
||||
*
|
||||
* @param locationProvider the location provider
|
||||
* @return true if successful
|
||||
*/
|
||||
fun requestLocationUpdatesFromProvider(locationProvider: String): Boolean {
|
||||
return try {
|
||||
if (locationManager.allProviders.contains(locationProvider)) {
|
||||
locationManager.requestLocationUpdates(
|
||||
locationProvider,
|
||||
MIN_LOCATION_UPDATE_REQUEST_TIME_IN_MILLIS,
|
||||
MIN_LOCATION_UPDATE_REQUEST_DISTANCE_IN_METERS,
|
||||
this
|
||||
)
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
} catch (e: IllegalArgumentException) {
|
||||
Timber.e(e, "Illegal argument exception")
|
||||
false
|
||||
} catch (e: SecurityException) {
|
||||
Timber.e(e, "Security exception")
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether a given location is better than the current best location.
|
||||
*
|
||||
* @param location the location to be tested
|
||||
* @param currentBestLocation the current best location
|
||||
* @return LOCATION_SIGNIFICANTLY_CHANGED if location changed significantly
|
||||
* LOCATION_SLIGHTLY_CHANGED if location changed slightly
|
||||
*/
|
||||
private fun isBetterLocation(location: Location, currentBestLocation: Location?): LocationChangeType {
|
||||
if (currentBestLocation == null) {
|
||||
return LocationChangeType.LOCATION_SIGNIFICANTLY_CHANGED
|
||||
}
|
||||
|
||||
val timeDelta = location.time - currentBestLocation.time
|
||||
val isSignificantlyNewer = timeDelta > MIN_LOCATION_UPDATE_REQUEST_TIME_IN_MILLIS
|
||||
val isNewer = timeDelta > 0
|
||||
val accuracyDelta = (location.accuracy - currentBestLocation.accuracy).toInt()
|
||||
val isMoreAccurate = accuracyDelta < 0
|
||||
val isSignificantlyLessAccurate = accuracyDelta > 200
|
||||
val isFromSameProvider = isSameProvider(location.provider, currentBestLocation.provider)
|
||||
|
||||
val results = FloatArray(5)
|
||||
Location.distanceBetween(
|
||||
currentBestLocation.latitude, currentBestLocation.longitude,
|
||||
location.latitude, location.longitude,
|
||||
results
|
||||
)
|
||||
|
||||
return when {
|
||||
isSignificantlyNewer
|
||||
||
|
||||
isMoreAccurate
|
||||
||
|
||||
(isNewer && !isSignificantlyLessAccurate && isFromSameProvider) -> {
|
||||
if (results[0] < 1000) LocationChangeType.LOCATION_SLIGHTLY_CHANGED
|
||||
else LocationChangeType.LOCATION_SIGNIFICANTLY_CHANGED
|
||||
}
|
||||
else -> LocationChangeType.LOCATION_NOT_CHANGED
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether two providers are the same
|
||||
*/
|
||||
private fun isSameProvider(provider1: String?, provider2: String?): Boolean {
|
||||
return provider1 == provider2
|
||||
}
|
||||
|
||||
/**
|
||||
* Unregisters location manager.
|
||||
*/
|
||||
fun unregisterLocationManager() {
|
||||
isLocationManagerRegistered = false
|
||||
locationExplanationDisplayed.clear()
|
||||
try {
|
||||
locationManager.removeUpdates(this)
|
||||
} catch (e: SecurityException) {
|
||||
Timber.e(e, "Security exception")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a new listener to the list of location listeners.
|
||||
*
|
||||
* @param listener the new listener
|
||||
*/
|
||||
fun addLocationListener(listener: LocationUpdateListener) {
|
||||
if (!locationListeners.contains(listener)) {
|
||||
locationListeners.add(listener)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a listener from the list of location listeners.
|
||||
*
|
||||
* @param listener the listener to be removed
|
||||
*/
|
||||
fun removeLocationListener(listener: LocationUpdateListener) {
|
||||
locationListeners.remove(listener)
|
||||
}
|
||||
|
||||
override fun onLocationChanged(location: Location) {
|
||||
Timber.d("on location changed")
|
||||
val changeType = isBetterLocation(location, lastLocationVar)
|
||||
if (changeType == LocationChangeType.LOCATION_SIGNIFICANTLY_CHANGED) {
|
||||
lastLocationVar = location
|
||||
locationListeners.forEach { it.onLocationChangedSignificantly(LatLng.from(location)) }
|
||||
} else if (lastLocationVar?.let { location.distanceTo(it) }!! >= 500) {
|
||||
locationListeners.forEach { it.onLocationChangedMedium(LatLng.from(location)) }
|
||||
} else if (changeType == LocationChangeType.LOCATION_SLIGHTLY_CHANGED) {
|
||||
lastLocationVar = location
|
||||
locationListeners.forEach { it.onLocationChangedSlightly(LatLng.from(location)) }
|
||||
}
|
||||
}
|
||||
|
||||
@Deprecated("Deprecated in Java", ReplaceWith(
|
||||
"Timber.d(\"%s's status changed to %d\", provider, status)",
|
||||
"timber.log.Timber"
|
||||
)
|
||||
)
|
||||
override fun onStatusChanged(provider: String?, status: Int, extras: Bundle?) {
|
||||
Timber.d("%s's status changed to %d", provider, status)
|
||||
}
|
||||
|
||||
|
||||
|
||||
override fun onProviderEnabled(provider: String) {
|
||||
Timber.d("Provider %s enabled", provider)
|
||||
}
|
||||
|
||||
override fun onProviderDisabled(provider: String) {
|
||||
Timber.d("Provider %s disabled", provider)
|
||||
}
|
||||
|
||||
fun isNetworkProviderEnabled(): Boolean {
|
||||
return locationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER)
|
||||
}
|
||||
|
||||
fun isGPSProviderEnabled(): Boolean {
|
||||
return locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER)
|
||||
}
|
||||
|
||||
enum class LocationChangeType {
|
||||
LOCATION_SIGNIFICANTLY_CHANGED,
|
||||
LOCATION_SLIGHTLY_CHANGED,
|
||||
LOCATION_MEDIUM_CHANGED,
|
||||
LOCATION_NOT_CHANGED,
|
||||
PERMISSION_JUST_GRANTED,
|
||||
MAP_UPDATED,
|
||||
SEARCH_CUSTOM_AREA,
|
||||
CUSTOM_QUERY
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
@ -1,7 +0,0 @@
|
|||
package fr.free.nrw.commons.location;
|
||||
|
||||
public interface LocationUpdateListener {
|
||||
void onLocationChangedSignificantly(LatLng latLng); // Will be used to update all nearby markers on the map
|
||||
void onLocationChangedSlightly(LatLng latLng); // Will be used to track users motion
|
||||
void onLocationChangedMedium(LatLng latLng); // Will be used updating nearby card view notification
|
||||
}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
package fr.free.nrw.commons.location
|
||||
|
||||
interface LocationUpdateListener {
|
||||
// Will be used to update all nearby markers on the map
|
||||
fun onLocationChangedSignificantly(latLng: LatLng)
|
||||
|
||||
// Will be used to track users motion
|
||||
fun onLocationChangedSlightly(latLng: LatLng)
|
||||
|
||||
// Will be used updating nearby card view notification
|
||||
fun onLocationChangedMedium(latLng: LatLng)
|
||||
}
|
||||
|
|
@ -3,7 +3,10 @@ package fr.free.nrw.commons.mwapi;
|
|||
import static fr.free.nrw.commons.category.CategoryClientKt.CATEGORY_PREFIX;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import fr.free.nrw.commons.BuildConfig;
|
||||
import fr.free.nrw.commons.category.CategoryItem;
|
||||
import fr.free.nrw.commons.wikidata.mwapi.MwQueryPage;
|
||||
import fr.free.nrw.commons.wikidata.mwapi.MwQueryResponse;
|
||||
import io.reactivex.Single;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
|
|
@ -11,14 +14,11 @@ import java.util.LinkedHashSet;
|
|||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Named;
|
||||
import okhttp3.HttpUrl;
|
||||
import okhttp3.OkHttpClient;
|
||||
import okhttp3.Request;
|
||||
import okhttp3.Response;
|
||||
import okhttp3.ResponseBody;
|
||||
import fr.free.nrw.commons.wikidata.mwapi.MwQueryPage;
|
||||
import fr.free.nrw.commons.wikidata.mwapi.MwQueryResponse;
|
||||
import timber.log.Timber;
|
||||
|
||||
/**
|
||||
|
|
@ -30,14 +30,11 @@ import timber.log.Timber;
|
|||
public class CategoryApi {
|
||||
|
||||
private final OkHttpClient okHttpClient;
|
||||
private final String commonsBaseUrl;
|
||||
private final Gson gson;
|
||||
|
||||
@Inject
|
||||
public CategoryApi(OkHttpClient okHttpClient, Gson gson,
|
||||
@Named("wikimedia_api_host") String commonsBaseUrl) {
|
||||
public CategoryApi(final OkHttpClient okHttpClient, final Gson gson) {
|
||||
this.okHttpClient = okHttpClient;
|
||||
this.commonsBaseUrl = commonsBaseUrl;
|
||||
this.gson = gson;
|
||||
}
|
||||
|
||||
|
|
@ -75,9 +72,9 @@ public class CategoryApi {
|
|||
* @param coords Coordinates to build query with
|
||||
* @return URL for API query
|
||||
*/
|
||||
private HttpUrl buildUrl(String coords) {
|
||||
private HttpUrl buildUrl(final String coords) {
|
||||
return HttpUrl
|
||||
.parse(commonsBaseUrl)
|
||||
.parse(BuildConfig.WIKIMEDIA_API_HOST)
|
||||
.newBuilder()
|
||||
.addQueryParameter("action", "query")
|
||||
.addQueryParameter("prop", "categories|coordinates|pageprops")
|
||||
|
|
|
|||
|
|
@ -42,8 +42,8 @@ class LanguagesAdapter constructor(
|
|||
AppLanguageLookUpTable(context)
|
||||
|
||||
init {
|
||||
languageNamesList = language.localizedNames
|
||||
languageCodesList = language.codes
|
||||
languageNamesList = language.getLocalizedNames()
|
||||
languageCodesList = language.getCodes()
|
||||
}
|
||||
|
||||
private val filter = LanguageFilter()
|
||||
|
|
@ -117,7 +117,7 @@ class LanguagesAdapter constructor(
|
|||
*/
|
||||
fun getIndexOfUserDefaultLocale(context: Context): Int {
|
||||
val userLanguageCode = context.locale?.language ?: return DEFAULT_INDEX
|
||||
return language.codes.indexOf(userLanguageCode).takeIf { it >= 0 } ?: DEFAULT_INDEX
|
||||
return language.getCodes().indexOf(userLanguageCode).takeIf { it >= 0 } ?: DEFAULT_INDEX
|
||||
}
|
||||
|
||||
fun getIndexOfLanguageCode(languageCode: String): Int = languageCodesList.indexOf(languageCode)
|
||||
|
|
@ -128,17 +128,17 @@ class LanguagesAdapter constructor(
|
|||
override fun performFiltering(constraint: CharSequence?): FilterResults {
|
||||
val filterResults = FilterResults()
|
||||
val temp: LinkedHashMap<String, String> = LinkedHashMap()
|
||||
if (constraint != null && language.localizedNames != null) {
|
||||
val length: Int = language.localizedNames.size
|
||||
if (constraint != null) {
|
||||
val length: Int = language.getLocalizedNames().size
|
||||
var i = 0
|
||||
while (i < length) {
|
||||
val key: String = language.codes[i]
|
||||
val value: String = language.localizedNames[i]
|
||||
val key: String = language.getCodes()[i]
|
||||
val value: String = language.getLocalizedNames()[i]
|
||||
val defaultlanguagecode = getIndexOfUserDefaultLocale(context)
|
||||
if (value.contains(constraint, true) ||
|
||||
Locale(key)
|
||||
.getDisplayName(
|
||||
Locale(language.codes[defaultlanguagecode]),
|
||||
Locale(language.getCodes()[defaultlanguagecode]),
|
||||
).contains(constraint, true)
|
||||
) {
|
||||
temp[key] = value
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
package fr.free.nrw.commons.upload;
|
||||
|
||||
|
||||
import static fr.free.nrw.commons.di.CommonsApplicationModule.IO_THREAD;
|
||||
import static fr.free.nrw.commons.utils.ImageUtils.IMAGE_OK;
|
||||
|
||||
import android.content.Context;
|
||||
|
|
@ -53,7 +54,7 @@ public class PendingUploadsPresenter implements UserActionListener {
|
|||
final ContributionsRemoteDataSource contributionsRemoteDataSource,
|
||||
final ContributionsRepository contributionsRepository,
|
||||
final UploadRepository uploadRepository,
|
||||
@Named(CommonsApplicationModule.IO_THREAD) final Scheduler ioThreadScheduler) {
|
||||
@Named(IO_THREAD) final Scheduler ioThreadScheduler) {
|
||||
this.contributionBoundaryCallback = contributionBoundaryCallback;
|
||||
this.contributionsRepository = contributionsRepository;
|
||||
this.uploadRepository = uploadRepository;
|
||||
|
|
|
|||
|
|
@ -9,6 +9,8 @@ import fr.free.nrw.commons.auth.csrf.InvalidLoginTokenException
|
|||
import fr.free.nrw.commons.category.CategoryEditHelper
|
||||
import fr.free.nrw.commons.category.CategoryItem
|
||||
import fr.free.nrw.commons.di.CommonsApplicationModule
|
||||
import fr.free.nrw.commons.di.CommonsApplicationModule.Companion.IO_THREAD
|
||||
import fr.free.nrw.commons.di.CommonsApplicationModule.Companion.MAIN_THREAD
|
||||
import fr.free.nrw.commons.repository.UploadRepository
|
||||
import fr.free.nrw.commons.upload.depicts.proxy
|
||||
import io.reactivex.Observable
|
||||
|
|
@ -30,8 +32,8 @@ class CategoriesPresenter
|
|||
@Inject
|
||||
constructor(
|
||||
private val repository: UploadRepository,
|
||||
@param:Named(CommonsApplicationModule.IO_THREAD) private val ioScheduler: Scheduler,
|
||||
@param:Named(CommonsApplicationModule.MAIN_THREAD) private val mainThreadScheduler: Scheduler,
|
||||
@param:Named(IO_THREAD) private val ioScheduler: Scheduler,
|
||||
@param:Named(MAIN_THREAD) private val mainThreadScheduler: Scheduler,
|
||||
) : CategoriesContract.UserActionListener {
|
||||
companion object {
|
||||
private val DUMMY: CategoriesContract.View = proxy()
|
||||
|
|
|
|||
|
|
@ -7,6 +7,8 @@ import fr.free.nrw.commons.Media
|
|||
import fr.free.nrw.commons.auth.csrf.InvalidLoginTokenException
|
||||
import fr.free.nrw.commons.bookmarks.items.BookmarkItemsController
|
||||
import fr.free.nrw.commons.di.CommonsApplicationModule
|
||||
import fr.free.nrw.commons.di.CommonsApplicationModule.Companion.IO_THREAD
|
||||
import fr.free.nrw.commons.di.CommonsApplicationModule.Companion.MAIN_THREAD
|
||||
import fr.free.nrw.commons.repository.UploadRepository
|
||||
import fr.free.nrw.commons.upload.structure.depictions.DepictedItem
|
||||
import fr.free.nrw.commons.wikidata.WikidataDisambiguationItems
|
||||
|
|
@ -31,8 +33,8 @@ class DepictsPresenter
|
|||
@Inject
|
||||
constructor(
|
||||
private val repository: UploadRepository,
|
||||
@param:Named(CommonsApplicationModule.IO_THREAD) private val ioScheduler: Scheduler,
|
||||
@param:Named(CommonsApplicationModule.MAIN_THREAD) private val mainThreadScheduler: Scheduler,
|
||||
@param:Named(IO_THREAD) private val ioScheduler: Scheduler,
|
||||
@param:Named(MAIN_THREAD) private val mainThreadScheduler: Scheduler,
|
||||
) : DepictsContract.UserActionListener {
|
||||
companion object {
|
||||
private val DUMMY = proxy<DepictsContract.View>()
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ import retrofit2.converter.gson.GsonConverterFactory
|
|||
class CommonsServiceFactory(
|
||||
private val okHttpClient: OkHttpClient,
|
||||
) {
|
||||
private val builder: Retrofit.Builder by lazy {
|
||||
val builder: Retrofit.Builder by lazy {
|
||||
// All instances of retrofit share this configuration, but create it lazily
|
||||
Retrofit
|
||||
.Builder()
|
||||
|
|
@ -17,15 +17,11 @@ class CommonsServiceFactory(
|
|||
.addConverterFactory(GsonConverterFactory.create(GsonUtil.getDefaultGson()))
|
||||
}
|
||||
|
||||
private val retrofitCache: MutableMap<String, Retrofit> = mutableMapOf()
|
||||
val retrofitCache: MutableMap<String, Retrofit> = mutableMapOf()
|
||||
|
||||
fun <T : Any> create(
|
||||
baseUrl: String,
|
||||
service: Class<T>,
|
||||
): T =
|
||||
retrofitCache
|
||||
.getOrPut(baseUrl) {
|
||||
// Cache instances of retrofit based on API backend
|
||||
builder.baseUrl(baseUrl).build()
|
||||
}.create(service)
|
||||
inline fun <reified T: Any> create(baseUrl: String): T =
|
||||
retrofitCache.getOrPut(baseUrl) {
|
||||
// Cache instances of retrofit based on API backend
|
||||
builder.baseUrl(baseUrl).build()
|
||||
}.create(T::class.java)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -62,5 +62,5 @@ class LatLngTests {
|
|||
private fun assertPrettyCoordinateString(
|
||||
expected: String,
|
||||
place: LatLng,
|
||||
) = assertEquals(expected, place.prettyCoordinateString)
|
||||
) = assertEquals(expected, place.getPrettyCoordinateString())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -37,9 +37,8 @@ class TestCommonsApplication : Application() {
|
|||
}
|
||||
|
||||
@Suppress("MemberVisibilityCanBePrivate")
|
||||
class MockCommonsApplicationModule(
|
||||
appContext: Context,
|
||||
) : CommonsApplicationModule(appContext) {
|
||||
class MockCommonsApplicationModule(appContext: Context) : CommonsApplicationModule(appContext) {
|
||||
|
||||
val defaultSharedPreferences: JsonKvStore = mock()
|
||||
val locationServiceManager: LocationServiceManager = mock()
|
||||
val mockDbOpenHelper: DBOpenHelper = mock()
|
||||
|
|
@ -50,16 +49,13 @@ class MockCommonsApplicationModule(
|
|||
val modificationClient: ContentProviderClient = mock()
|
||||
val uploadPrefs: JsonKvStore = mock()
|
||||
|
||||
override fun provideCategoryContentProviderClient(context: Context?): ContentProviderClient = categoryClient
|
||||
override fun provideCategoryContentProviderClient(context: Context): ContentProviderClient = categoryClient
|
||||
|
||||
override fun provideContributionContentProviderClient(context: Context?): ContentProviderClient = contributionClient
|
||||
override fun provideContributionContentProviderClient(context: Context): ContentProviderClient = contributionClient
|
||||
|
||||
override fun provideModificationContentProviderClient(context: Context?): ContentProviderClient = modificationClient
|
||||
override fun provideModificationContentProviderClient(context: Context): ContentProviderClient = modificationClient
|
||||
|
||||
override fun providesDefaultKvStore(
|
||||
context: Context,
|
||||
gson: Gson,
|
||||
): JsonKvStore = defaultSharedPreferences
|
||||
override fun providesDefaultKvStore(context: Context, gson: Gson): JsonKvStore = defaultSharedPreferences
|
||||
|
||||
override fun provideLocationServiceManager(context: Context): LocationServiceManager = locationServiceManager
|
||||
|
||||
|
|
|
|||
|
|
@ -248,11 +248,11 @@ class MediaDetailFragmentUnitTests {
|
|||
@Throws(Exception::class)
|
||||
fun testOnUpdateCoordinatesClickedCurrentLocationNull() {
|
||||
`when`(media.coordinates).thenReturn(null)
|
||||
`when`(locationManager.lastLocation).thenReturn(null)
|
||||
`when`(locationManager.getLastLocation()).thenReturn(null)
|
||||
`when`(applicationKvStore.getString(lastLocation)).thenReturn("37.773972,-122.431297")
|
||||
fragment.onUpdateCoordinatesClicked()
|
||||
Mockito.verify(media, Mockito.times(1)).coordinates
|
||||
Mockito.verify(locationManager, Mockito.times(1)).lastLocation
|
||||
Mockito.verify(locationManager, Mockito.times(1)).getLastLocation()
|
||||
val shadowActivity: ShadowActivity = shadowOf(activity)
|
||||
val startedIntent = shadowActivity.nextStartedActivity
|
||||
val shadowIntent: ShadowIntent = shadowOf(startedIntent)
|
||||
|
|
@ -276,11 +276,11 @@ class MediaDetailFragmentUnitTests {
|
|||
@Throws(Exception::class)
|
||||
fun testOnUpdateCoordinatesClickedCurrentLocationNotNull() {
|
||||
`when`(media.coordinates).thenReturn(null)
|
||||
`when`(locationManager.lastLocation).thenReturn(LatLng(-0.000001, -0.999999, 0f))
|
||||
`when`(locationManager.getLastLocation()).thenReturn(LatLng(-0.000001, -0.999999, 0f))
|
||||
`when`(applicationKvStore.getString(lastLocation)).thenReturn("37.773972,-122.431297")
|
||||
|
||||
fragment.onUpdateCoordinatesClicked()
|
||||
Mockito.verify(locationManager, Mockito.times(3)).lastLocation
|
||||
Mockito.verify(locationManager, Mockito.times(3)).getLastLocation()
|
||||
val shadowActivity: ShadowActivity = shadowOf(activity)
|
||||
val startedIntent = shadowActivity.nextStartedActivity
|
||||
val shadowIntent: ShadowIntent = shadowOf(startedIntent)
|
||||
|
|
|
|||
|
|
@ -54,8 +54,8 @@ class LanguagesAdapterTest {
|
|||
.from(context)
|
||||
.inflate(R.layout.row_item_languages_spinner, null) as View
|
||||
|
||||
languageNamesList = language.localizedNames
|
||||
languageCodesList = language.codes
|
||||
languageNamesList = language.getLocalizedNames()
|
||||
languageCodesList = language.getCodes()
|
||||
|
||||
languagesAdapter = LanguagesAdapter(context, selectedLanguages)
|
||||
}
|
||||
|
|
@ -124,12 +124,12 @@ class LanguagesAdapterTest {
|
|||
var i = 0
|
||||
var s = 0
|
||||
while (i < length) {
|
||||
val key: String = language.codes[i]
|
||||
val value: String = language.localizedNames[i]
|
||||
val key: String = language.getCodes()[i]
|
||||
val value: String = language.getLocalizedNames()[i]
|
||||
if (value.contains(constraint, true) ||
|
||||
Locale(key)
|
||||
.getDisplayName(
|
||||
Locale(language.codes[defaultlanguagecode!!]),
|
||||
Locale(language.getCodes()[defaultlanguagecode!!]),
|
||||
).contains(constraint, true)
|
||||
) {
|
||||
s++
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue