mirror of
https://github.com/commons-app/apps-android-commons.git
synced 2025-10-28 05:13:53 +01:00
Merge branch 'master' into notificationFixes
This commit is contained in:
commit
6eaac1de40
80 changed files with 2625 additions and 555 deletions
|
|
@ -1,6 +1,8 @@
|
|||
package fr.free.nrw.commons;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.app.AlertDialog;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
|
|
@ -10,6 +12,9 @@ import android.util.Log;
|
|||
import android.support.customtabs.CustomTabsIntent;
|
||||
import android.support.v4.content.ContextCompat;
|
||||
import android.view.View;
|
||||
import android.widget.ArrayAdapter;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.Spinner;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
|
|
@ -29,6 +34,16 @@ public class AboutActivity extends NavigationBaseActivity {
|
|||
@BindView(R.id.about_license) HtmlTextView aboutLicenseText;
|
||||
@BindView(R.id.about_faq) TextView faqText;
|
||||
|
||||
String language[] = { "Kazakh", "Afrikaans", "Arabic", "Bengali", "Asturianu", "azərbaycanca", "Bikol Central",
|
||||
"Bulgarain", "বাংলা", "Bosanski", "Brezhoneg","català","کوردی", " čeština", " kaszëbsczi", "Cymraeg", "dansk", "Deutsch"
|
||||
,"Zazaki", "डोटेली","Ελληνικά","euskara","español","فارسی","suomi", "français" ,"Nordfriisk", "galego", "Hawaiʻi"
|
||||
,"हिन्दी","Hunsrik","עברית","hornjoserbsce","magyar","interlingua","Bahasa Indonesia", "íslenska","Italian","japanese",
|
||||
"Basa Jawa", "ქართული", " ភាសាខ្មែរ","ಕನ್ನಡ", "한국어","къарачай-малкъар","Кыргызча", "latina", "Lëtzebuergesch", "lietuvių",
|
||||
"latviešu", "Malagasy", "македонски"," മലയാളം","монгол","मराठी","Bahasa Melayu","Malti", "नेपाली", "norsk bokmål",
|
||||
" Nederlands","occitan","ଓଡ଼ିଆ","ਪੰਜਾਬੀ","polsk","Piemontèis","پښتو","português","română","русский"," سنڌي", " සිංහල",
|
||||
"slovenčina"," سرائیکی", "svenska", "தமிழ்", "ತುಳು"," తెలుగు"," ไทย", "Türkçe","українська", "اردو", "Tiếng Việt",
|
||||
" მარგალური","ייִדיש",};
|
||||
|
||||
/**
|
||||
* This method helps in the creation About screen
|
||||
*
|
||||
|
|
@ -87,8 +102,43 @@ public class AboutActivity extends NavigationBaseActivity {
|
|||
Utils.handleWebUrl(this,Uri.parse("https://github.com/commons-app/apps-android-commons/wiki/Privacy-policy\\"));
|
||||
}
|
||||
|
||||
|
||||
@OnClick(R.id.about_faq)
|
||||
public void launchFrequentlyAskedQuesions(View view) {
|
||||
Utils.handleWebUrl(this,Uri.parse("https://github.com/commons-app/apps-android-commons/wiki/Frequently-Asked-Questions\\"));
|
||||
}
|
||||
|
||||
@OnClick(R.id.about_translate)
|
||||
public void launchTranslate(View view) {
|
||||
final ArrayAdapter<String> languageAdapter = new ArrayAdapter<String>(AboutActivity.this,
|
||||
android.R.layout.simple_spinner_item, language);
|
||||
final Spinner spinner = new Spinner(AboutActivity.this);
|
||||
spinner.setLayoutParams(new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT));
|
||||
spinner.setAdapter(languageAdapter);
|
||||
spinner.setGravity(17);
|
||||
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(AboutActivity.this);
|
||||
builder.setView(spinner);
|
||||
builder.setTitle(R.string.about_translate_title)
|
||||
.setMessage(R.string.about_translate_message)
|
||||
.setPositiveButton(R.string.about_translate_proceed, new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
String languageSelected = spinner.getSelectedItem().toString();
|
||||
TokensTranslations tokensTranslations = new TokensTranslations();
|
||||
tokensTranslations.initailize();
|
||||
String token = tokensTranslations.getTranslationToken(languageSelected);
|
||||
Utils.handleWebUrl(AboutActivity.this,Uri.parse("https://translatewiki.net/w/i.php?title=Special:Translate&language="+token+"&group=commons-android-strings&filter=%21translated&action=translate ?"));
|
||||
}
|
||||
});
|
||||
builder.setNegativeButton(R.string.about_translate_cancel, new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
finish();
|
||||
}
|
||||
});
|
||||
builder.create().show();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
105
app/src/main/java/fr/free/nrw/commons/TokensTranslations.java
Normal file
105
app/src/main/java/fr/free/nrw/commons/TokensTranslations.java
Normal file
|
|
@ -0,0 +1,105 @@
|
|||
package fr.free.nrw.commons;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
||||
/**
|
||||
* Created by Dell on 3/16/2018.
|
||||
*/
|
||||
|
||||
public class TokensTranslations {
|
||||
HashMap<String,String> translationToken = new HashMap<String,String>();
|
||||
|
||||
public void initailize() {
|
||||
translationToken.put("Kazakh", "ab");
|
||||
translationToken.put("Afrikaans", "af");
|
||||
translationToken.put("Arabic", "ar");
|
||||
translationToken.put("Bengali", "as");
|
||||
translationToken.put("Asturianu", "ast");
|
||||
translationToken.put("azərbaycanca", "az");
|
||||
translationToken.put("Bikol Central", "bcl");
|
||||
translationToken.put("Bulgarain","bg");
|
||||
translationToken.put("বাংলা", "bn");
|
||||
translationToken.put("Brezhoneg", "br");
|
||||
translationToken.put("Bosanski", "bs");
|
||||
translationToken.put("català", "ca");
|
||||
translationToken.put("کوردی","ckb");
|
||||
translationToken.put("čeština", "cs");
|
||||
translationToken.put("kaszëbsczi", "csb");
|
||||
translationToken.put("Cymraeg", "cy");
|
||||
translationToken.put("dansk", "da");
|
||||
translationToken.put("Deutsch", "de");
|
||||
translationToken.put("Zazaki", "diq");
|
||||
translationToken.put("डोटेली","diq");
|
||||
translationToken.put("Ελληνικά","el");
|
||||
translationToken.put("euskara","eu");
|
||||
translationToken.put("español", "es");
|
||||
translationToken.put("فارسی","fa");
|
||||
translationToken.put("suomi", "fi");
|
||||
translationToken.put("føroyskt", "fo");
|
||||
translationToken.put("français", "fr");
|
||||
translationToken.put("Nordfriisk", "frr");
|
||||
translationToken.put("galego", "gr");
|
||||
translationToken.put("Hawaiʻi", "haw");
|
||||
translationToken.put("עברית","he");
|
||||
translationToken.put("हिन्दी","hi");
|
||||
translationToken.put("Hunsrik", "hrx");
|
||||
translationToken.put("hornjoserbsce", "hsb");
|
||||
translationToken.put("magyar","hu");
|
||||
translationToken.put("interlingua","ia");
|
||||
translationToken.put("Bahasa Indonesia", "id");
|
||||
translationToken.put("íslenska","is");
|
||||
translationToken.put("Italian","it");
|
||||
translationToken.put("japanese","ja");
|
||||
translationToken.put("Basa Jawa","jv");
|
||||
translationToken.put("ქართული", "ka");
|
||||
translationToken.put("Taqbaylit","kab");
|
||||
translationToken.put(" ភាសាខ្មែរ","km");
|
||||
translationToken.put("ಕನ್ನಡ", "kn");
|
||||
translationToken.put("한국어", "ko");
|
||||
translationToken.put("къарачай-малкъар","krc");
|
||||
translationToken.put("Кыргызча","ky");
|
||||
translationToken.put("latina","la");
|
||||
translationToken.put("Lëtzebuergesch","lb");
|
||||
translationToken.put("lietuvių", "lt");
|
||||
translationToken.put("latviešu","lv");
|
||||
translationToken.put("Malagasy","mg");
|
||||
translationToken.put("македонски", "mk");
|
||||
translationToken.put("മലയാളം","ml");
|
||||
translationToken.put("монгол","mn");
|
||||
translationToken.put("मराठी","mr");
|
||||
translationToken.put("Bahasa Melayu","ms");
|
||||
translationToken.put("Malti","mt");
|
||||
translationToken.put("norsk bokmål", "nb");
|
||||
translationToken.put("नेपाली","ne");
|
||||
translationToken.put("Nederlands","nl");
|
||||
translationToken.put("occitan","oc");
|
||||
translationToken.put("ଓଡ଼ିଆ","or");
|
||||
translationToken.put("ਪੰਜਾਬੀ","pa");
|
||||
translationToken.put("polsk", "pl");
|
||||
translationToken.put("Piemontèis","pms");
|
||||
translationToken.put("پښتو","ps");
|
||||
translationToken.put("português","pt");
|
||||
translationToken.put("română","ro");
|
||||
translationToken.put("русский","ru");
|
||||
translationToken.put(" سنڌي","sd");
|
||||
translationToken.put(" සිංහල","si");
|
||||
translationToken.put("slovenčina","sk");
|
||||
translationToken.put(" سرائیکی","skr");
|
||||
translationToken.put("Basa Sunda","su");
|
||||
translationToken.put("svenska","sv");
|
||||
translationToken.put("தமிழ்", "ta");
|
||||
translationToken.put("ತುಳು", "tcy");
|
||||
translationToken.put(" తెలుగు","te");
|
||||
translationToken.put(" ไทย","th");
|
||||
translationToken.put("Türkçe","tr");
|
||||
translationToken.put("українська","uk");
|
||||
translationToken.put("اردو","ur");
|
||||
translationToken.put("Tiếng Việt","vi");
|
||||
translationToken.put(" მარგალური", "xmf");
|
||||
translationToken.put("ייִדיש","yi");
|
||||
}
|
||||
|
||||
public String getTranslationToken ( String language){
|
||||
return translationToken.get(language);
|
||||
}
|
||||
}
|
||||
|
|
@ -73,6 +73,8 @@ public class CategorizationFragment extends CommonsDaggerSupportFragment {
|
|||
|
||||
@Inject MediaWikiApi mwApi;
|
||||
@Inject @Named("default_preferences") SharedPreferences prefs;
|
||||
@Inject @Named("prefs") SharedPreferences prefsPrefs;
|
||||
@Inject @Named("direct_nearby_upload_prefs") SharedPreferences directPrefs;
|
||||
@Inject CategoryDao categoryDao;
|
||||
|
||||
private RVRendererAdapter<CategoryItem> categoriesAdapter;
|
||||
|
|
@ -80,6 +82,7 @@ public class CategorizationFragment extends CommonsDaggerSupportFragment {
|
|||
private HashMap<String, ArrayList<String>> categoriesCache;
|
||||
private List<CategoryItem> selectedCategories = new ArrayList<>();
|
||||
private TitleTextWatcher textWatcher = new TitleTextWatcher();
|
||||
private boolean hasDirectCategories = false;
|
||||
|
||||
private final CategoriesAdapterFactory adapterFactory = new CategoriesAdapterFactory(item -> {
|
||||
if (item.isSelected()) {
|
||||
|
|
@ -128,7 +131,7 @@ public class CategorizationFragment extends CommonsDaggerSupportFragment {
|
|||
}
|
||||
|
||||
public void hideKeyboard(View view) {
|
||||
InputMethodManager inputMethodManager =(InputMethodManager)getActivity().getSystemService(Activity.INPUT_METHOD_SERVICE);
|
||||
InputMethodManager inputMethodManager = (InputMethodManager) getActivity().getSystemService(Activity.INPUT_METHOD_SERVICE);
|
||||
inputMethodManager.hideSoftInputFromWindow(view.getWindowToken(), 0);
|
||||
}
|
||||
|
||||
|
|
@ -223,7 +226,7 @@ public class CategorizationFragment extends CommonsDaggerSupportFragment {
|
|||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(
|
||||
s -> categoriesAdapter.add(s),
|
||||
Timber::e,
|
||||
Timber::e,
|
||||
() -> {
|
||||
categoriesAdapter.notifyDataSetChanged();
|
||||
categoriesSearchInProgress.setVisibility(View.GONE);
|
||||
|
|
@ -258,9 +261,34 @@ public class CategorizationFragment extends CommonsDaggerSupportFragment {
|
|||
}
|
||||
|
||||
private Observable<CategoryItem> defaultCategories() {
|
||||
return gpsCategories()
|
||||
.concatWith(titleCategories())
|
||||
.concatWith(recentCategories());
|
||||
|
||||
Observable<CategoryItem> directCat = directCategories();
|
||||
if (hasDirectCategories) {
|
||||
Timber.d("Image has direct Cat");
|
||||
return directCat
|
||||
.concatWith(gpsCategories())
|
||||
.concatWith(titleCategories())
|
||||
.concatWith(recentCategories());
|
||||
}
|
||||
else {
|
||||
Timber.d("Image has no direct Cat");
|
||||
return gpsCategories()
|
||||
.concatWith(titleCategories())
|
||||
.concatWith(recentCategories());
|
||||
}
|
||||
}
|
||||
|
||||
private Observable<CategoryItem> directCategories() {
|
||||
String directCategory = directPrefs.getString("Category", "");
|
||||
List<String> categoryList = new ArrayList<>();
|
||||
Timber.d("Direct category found: " + directCategory);
|
||||
|
||||
if (!directCategory.equals("")) {
|
||||
hasDirectCategories = true;
|
||||
categoryList.add(directCategory);
|
||||
Timber.d("DirectCat does not equal emptyString. Direct Cat list has " + categoryList);
|
||||
}
|
||||
return Observable.fromIterable(categoryList).map(name -> new CategoryItem(name, false));
|
||||
}
|
||||
|
||||
private Observable<CategoryItem> gpsCategories() {
|
||||
|
|
|
|||
|
|
@ -25,14 +25,14 @@ import static fr.free.nrw.commons.contributions.Contribution.SOURCE_CAMERA;
|
|||
import static fr.free.nrw.commons.contributions.Contribution.SOURCE_GALLERY;
|
||||
import static fr.free.nrw.commons.upload.UploadService.EXTRA_SOURCE;
|
||||
|
||||
class ContributionController {
|
||||
public class ContributionController {
|
||||
|
||||
private static final int SELECT_FROM_GALLERY = 1;
|
||||
private static final int SELECT_FROM_CAMERA = 2;
|
||||
|
||||
private Fragment fragment;
|
||||
|
||||
ContributionController(Fragment fragment) {
|
||||
public ContributionController(Fragment fragment) {
|
||||
this.fragment = fragment;
|
||||
}
|
||||
|
||||
|
|
@ -61,7 +61,7 @@ class ContributionController {
|
|||
}
|
||||
}
|
||||
|
||||
void startCameraCapture() {
|
||||
public void startCameraCapture() {
|
||||
|
||||
Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
|
||||
lastGeneratedCaptureUri = reGenerateImageCaptureUriInCache();
|
||||
|
|
@ -70,6 +70,9 @@ class ContributionController {
|
|||
requestWritePermission(fragment.getContext(), takePictureIntent, lastGeneratedCaptureUri);
|
||||
|
||||
takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, lastGeneratedCaptureUri);
|
||||
if (!fragment.isAdded()) {
|
||||
return;
|
||||
}
|
||||
fragment.startActivityForResult(takePictureIntent, SELECT_FROM_CAMERA);
|
||||
}
|
||||
|
||||
|
|
@ -77,11 +80,19 @@ class ContributionController {
|
|||
//FIXME: Starts gallery (opens Google Photos)
|
||||
Intent pickImageIntent = new Intent(ACTION_GET_CONTENT);
|
||||
pickImageIntent.setType("image/*");
|
||||
// See https://stackoverflow.com/questions/22366596/android-illegalstateexception-fragment-not-attached-to-activity-webview
|
||||
if (!fragment.isAdded()) {
|
||||
Timber.d("Fragment is not added, startActivityForResult cannot be called");
|
||||
return;
|
||||
}
|
||||
Timber.d("startGalleryPick() called with pickImageIntent");
|
||||
|
||||
fragment.startActivityForResult(pickImageIntent, SELECT_FROM_GALLERY);
|
||||
}
|
||||
|
||||
void handleImagePicked(int requestCode, Intent data) {
|
||||
public void handleImagePicked(int requestCode, Intent data, boolean isDirectUpload) {
|
||||
FragmentActivity activity = fragment.getActivity();
|
||||
Timber.d("handleImagePicked() called with onActivityResult()");
|
||||
Intent shareIntent = new Intent(activity, ShareActivity.class);
|
||||
shareIntent.setAction(ACTION_SEND);
|
||||
switch (requestCode) {
|
||||
|
|
@ -91,6 +102,9 @@ class ContributionController {
|
|||
shareIntent.setType(activity.getContentResolver().getType(imageData));
|
||||
shareIntent.putExtra(EXTRA_STREAM, imageData);
|
||||
shareIntent.putExtra(EXTRA_SOURCE, SOURCE_GALLERY);
|
||||
if (isDirectUpload) {
|
||||
shareIntent.putExtra("isDirectUpload", true);
|
||||
}
|
||||
break;
|
||||
case SELECT_FROM_CAMERA:
|
||||
//FIXME: Find out appropriate mime type
|
||||
|
|
@ -99,6 +113,10 @@ class ContributionController {
|
|||
shareIntent.setType("image/jpeg");
|
||||
shareIntent.putExtra(EXTRA_STREAM, lastGeneratedCaptureUri);
|
||||
shareIntent.putExtra(EXTRA_SOURCE, SOURCE_CAMERA);
|
||||
if (isDirectUpload) {
|
||||
shareIntent.putExtra("isDirectUpload", true);
|
||||
}
|
||||
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
|
|
@ -122,5 +140,4 @@ class ContributionController {
|
|||
lastGeneratedCaptureUri = savedInstanceState.getParcelable("lastGeneratedCaptureURI");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -117,7 +117,7 @@ public class ContributionsListFragment extends CommonsDaggerSupportFragment {
|
|||
if (resultCode == RESULT_OK) {
|
||||
Timber.d("OnActivityResult() parameters: Req code: %d Result code: %d Data: %s",
|
||||
requestCode, resultCode, data);
|
||||
controller.handleImagePicked(requestCode, data);
|
||||
controller.handleImagePicked(requestCode, data, false);
|
||||
} else {
|
||||
Timber.e("OnActivityResult() parameters: Req code: %d Result code: %d Data: %s",
|
||||
requestCode, resultCode, data);
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ import fr.free.nrw.commons.contributions.ContributionsSyncAdapter;
|
|||
import fr.free.nrw.commons.delete.DeleteTask;
|
||||
import fr.free.nrw.commons.modifications.ModificationsSyncAdapter;
|
||||
import fr.free.nrw.commons.settings.SettingsFragment;
|
||||
import fr.free.nrw.commons.nearby.PlaceRenderer;
|
||||
|
||||
@Singleton
|
||||
@Component(modules = {
|
||||
|
|
@ -44,6 +45,8 @@ public interface CommonsApplicationComponent extends AndroidInjector<Application
|
|||
@Override
|
||||
void inject(ApplicationlessInjection instance);
|
||||
|
||||
void inject(PlaceRenderer placeRenderer);
|
||||
|
||||
@Component.Builder
|
||||
@SuppressWarnings({"WeakerAccess", "unused"})
|
||||
interface Builder {
|
||||
|
|
|
|||
|
|
@ -33,7 +33,6 @@ public class CommonsApplicationModule {
|
|||
public static final String CATEGORY_AUTHORITY = "fr.free.nrw.commons.categories.contentprovider";
|
||||
public static final long OK_HTTP_CACHE_SIZE = 10 * 1024 * 1024;
|
||||
|
||||
private CommonsApplication application;
|
||||
private Context applicationContext;
|
||||
|
||||
public CommonsApplicationModule(Context applicationContext) {
|
||||
|
|
@ -87,9 +86,13 @@ public class CommonsApplicationModule {
|
|||
}
|
||||
|
||||
@Provides
|
||||
public UploadController providesUploadController(Context context,
|
||||
SessionManager sessionManager,
|
||||
@Named("default_preferences") SharedPreferences sharedPreferences) {
|
||||
@Named("direct_nearby_upload_prefs")
|
||||
public SharedPreferences providesDirectNearbyUploadPreferences(Context context) {
|
||||
return context.getSharedPreferences("direct_nearby_upload_prefs", MODE_PRIVATE);
|
||||
}
|
||||
|
||||
@Provides
|
||||
public UploadController providesUploadController(SessionManager sessionManager, @Named("default_preferences") SharedPreferences sharedPreferences, Context context) {
|
||||
return new UploadController(sessionManager, context, sharedPreferences);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import fr.free.nrw.commons.contributions.ContributionsListFragment;
|
|||
import fr.free.nrw.commons.media.MediaDetailFragment;
|
||||
import fr.free.nrw.commons.media.MediaDetailPagerFragment;
|
||||
import fr.free.nrw.commons.nearby.NearbyListFragment;
|
||||
import fr.free.nrw.commons.nearby.NearbyMapFragment;
|
||||
import fr.free.nrw.commons.nearby.NoPermissionsFragment;
|
||||
import fr.free.nrw.commons.settings.SettingsFragment;
|
||||
import fr.free.nrw.commons.upload.MultipleUploadListFragment;
|
||||
|
|
@ -31,6 +32,9 @@ public abstract class FragmentBuilderModule {
|
|||
@ContributesAndroidInjector
|
||||
abstract NearbyListFragment bindNearbyListFragment();
|
||||
|
||||
@ContributesAndroidInjector
|
||||
abstract NearbyMapFragment bindNearbyMapFragment();
|
||||
|
||||
@ContributesAndroidInjector
|
||||
abstract NoPermissionsFragment bindNoPermissionsFragment();
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
package fr.free.nrw.commons.location;
|
||||
|
||||
import android.location.Location;
|
||||
import android.net.Uri;
|
||||
import android.support.annotation.NonNull;
|
||||
|
||||
/**
|
||||
|
|
@ -11,15 +12,15 @@ public class LatLng {
|
|||
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)
|
||||
|
|
@ -43,7 +44,7 @@ public class LatLng {
|
|||
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
|
||||
*/
|
||||
|
|
@ -153,4 +154,9 @@ public class LatLng {
|
|||
public double getLatitude() {
|
||||
return latitude;
|
||||
}
|
||||
|
||||
public Uri getGmmIntentUri() {
|
||||
return Uri.parse("geo:0,0?q=" + latitude + "," + longitude);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ import android.location.LocationManager;
|
|||
import android.os.Bundle;
|
||||
import android.support.v4.app.ActivityCompat;
|
||||
import android.support.v4.content.ContextCompat;
|
||||
import android.util.Log;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
|
|
@ -19,7 +20,8 @@ import timber.log.Timber;
|
|||
public class LocationServiceManager implements LocationListener {
|
||||
public static final int LOCATION_REQUEST = 1;
|
||||
|
||||
private static final long MIN_LOCATION_UPDATE_REQUEST_TIME_IN_MILLIS = 2 * 60 * 1000;
|
||||
// Maybe these values can be improved for efficiency
|
||||
private static final long MIN_LOCATION_UPDATE_REQUEST_TIME_IN_MILLIS = 2 * 60 * 100;
|
||||
private static final long MIN_LOCATION_UPDATE_REQUEST_DISTANCE_IN_METERS = 10;
|
||||
|
||||
private Context context;
|
||||
|
|
@ -120,12 +122,14 @@ public class LocationServiceManager implements LocationListener {
|
|||
*
|
||||
* @param location the location to be tested
|
||||
* @param currentBestLocation the current best location
|
||||
* @return true if the given location is better
|
||||
* @return LOCATION_SIGNIFICANTLY_CHANGED if location changed significantly
|
||||
* LOCATION_SLIGHTLY_CHANGED if location changed slightly
|
||||
*/
|
||||
protected boolean isBetterLocation(Location location, Location currentBestLocation) {
|
||||
protected LocationChangeType isBetterLocation(Location location, Location currentBestLocation) {
|
||||
|
||||
if (currentBestLocation == null) {
|
||||
// A new location is always better than no location
|
||||
return true;
|
||||
return LocationChangeType.LOCATION_SIGNIFICANTLY_CHANGED;
|
||||
}
|
||||
|
||||
// Check whether the new location fix is newer or older
|
||||
|
|
@ -134,15 +138,6 @@ public class LocationServiceManager implements LocationListener {
|
|||
boolean isSignificantlyOlder = timeDelta < -MIN_LOCATION_UPDATE_REQUEST_TIME_IN_MILLIS;
|
||||
boolean isNewer = timeDelta > 0;
|
||||
|
||||
// If it's been more than two minutes since the current location, use the new location
|
||||
// because the user has likely moved
|
||||
if (isSignificantlyNewer) {
|
||||
return true;
|
||||
// If the new location is more than two minutes older, it must be worse
|
||||
} else if (isSignificantlyOlder) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check whether the new location fix is more or less accurate
|
||||
int accuracyDelta = (int) (location.getAccuracy() - currentBestLocation.getAccuracy());
|
||||
boolean isLessAccurate = accuracyDelta > 0;
|
||||
|
|
@ -153,15 +148,28 @@ public class LocationServiceManager implements LocationListener {
|
|||
boolean isFromSameProvider = isSameProvider(location.getProvider(),
|
||||
currentBestLocation.getProvider());
|
||||
|
||||
// Determine location quality using a combination of timeliness and accuracy
|
||||
if (isMoreAccurate) {
|
||||
return true;
|
||||
} else if (isNewer && !isLessAccurate) {
|
||||
return true;
|
||||
} else if (isNewer && !isSignificantlyLessAccurate && isFromSameProvider) {
|
||||
return true;
|
||||
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;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -208,12 +216,19 @@ public class LocationServiceManager implements LocationListener {
|
|||
|
||||
@Override
|
||||
public void onLocationChanged(Location location) {
|
||||
if (isBetterLocation(location, lastLocation)) {
|
||||
lastLocation = location;
|
||||
for (LocationUpdateListener listener : locationListeners) {
|
||||
listener.onLocationChanged(LatLng.from(lastLocation));
|
||||
if (isBetterLocation(location, lastLocation)
|
||||
.equals(LocationChangeType.LOCATION_SIGNIFICANTLY_CHANGED)) {
|
||||
lastLocation = location;
|
||||
for (LocationUpdateListener listener : locationListeners) {
|
||||
listener.onLocationChangedSignificantly(LatLng.from(lastLocation));
|
||||
}
|
||||
} else if (isBetterLocation(location, lastLocation)
|
||||
.equals(LocationChangeType.LOCATION_SLIGHTLY_CHANGED)) {
|
||||
lastLocation = location;
|
||||
for (LocationUpdateListener listener : locationListeners) {
|
||||
listener.onLocationChangedSlightly(LatLng.from(lastLocation));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -230,4 +245,10 @@ public class LocationServiceManager implements LocationListener {
|
|||
public void onProviderDisabled(String provider) {
|
||||
Timber.d("Provider %s disabled", provider);
|
||||
}
|
||||
|
||||
public enum LocationChangeType{
|
||||
LOCATION_SIGNIFICANTLY_CHANGED, //Went out of borders of nearby markers
|
||||
LOCATION_SLIGHTLY_CHANGED, //User might be walking or driving
|
||||
LOCATION_NOT_CHANGED
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
package fr.free.nrw.commons.location;
|
||||
|
||||
public interface LocationUpdateListener {
|
||||
void onLocationChanged(LatLng latLng);
|
||||
void onLocationChangedSignificantly(LatLng latLng);
|
||||
void onLocationChangedSlightly(LatLng latLng);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,75 @@
|
|||
package fr.free.nrw.commons.nearby;
|
||||
|
||||
import android.content.SharedPreferences;
|
||||
import android.os.Build;
|
||||
import android.support.v4.app.Fragment;
|
||||
import android.support.v4.content.ContextCompat;
|
||||
import android.support.v7.app.AlertDialog;
|
||||
|
||||
import fr.free.nrw.commons.R;
|
||||
import fr.free.nrw.commons.contributions.ContributionController;
|
||||
|
||||
import static android.Manifest.permission.READ_EXTERNAL_STORAGE;
|
||||
import static android.Manifest.permission.WRITE_EXTERNAL_STORAGE;
|
||||
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
|
||||
|
||||
class DirectUpload {
|
||||
|
||||
private ContributionController controller;
|
||||
private Fragment fragment;
|
||||
|
||||
DirectUpload(Fragment fragment, ContributionController controller) {
|
||||
this.fragment = fragment;
|
||||
this.controller = controller;
|
||||
}
|
||||
|
||||
void initiateCameraUpload() {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
if (ContextCompat.checkSelfPermission(fragment.getActivity(), WRITE_EXTERNAL_STORAGE) != PERMISSION_GRANTED) {
|
||||
if (fragment.getActivity().shouldShowRequestPermissionRationale(WRITE_EXTERNAL_STORAGE)) {
|
||||
new AlertDialog.Builder(fragment.getActivity())
|
||||
.setMessage(fragment.getActivity().getString(R.string.write_storage_permission_rationale))
|
||||
.setPositiveButton("OK", (dialog, which) -> {
|
||||
fragment.getActivity().requestPermissions(new String[]{WRITE_EXTERNAL_STORAGE}, 3);
|
||||
dialog.dismiss();
|
||||
})
|
||||
.setNegativeButton("Cancel", null)
|
||||
.create()
|
||||
.show();
|
||||
} else {
|
||||
fragment.getActivity().requestPermissions(new String[]{WRITE_EXTERNAL_STORAGE}, 3);
|
||||
}
|
||||
} else {
|
||||
controller.startCameraCapture();
|
||||
}
|
||||
} else {
|
||||
controller.startCameraCapture();
|
||||
}
|
||||
}
|
||||
|
||||
void initiateGalleryUpload() {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
if (ContextCompat.checkSelfPermission(fragment.getActivity(), READ_EXTERNAL_STORAGE) != PERMISSION_GRANTED) {
|
||||
if (fragment.getActivity().shouldShowRequestPermissionRationale(READ_EXTERNAL_STORAGE)) {
|
||||
new AlertDialog.Builder(fragment.getActivity())
|
||||
.setMessage(fragment.getActivity().getString(R.string.read_storage_permission_rationale))
|
||||
.setPositiveButton("OK", (dialog, which) -> {
|
||||
fragment.getActivity().requestPermissions(new String[]{READ_EXTERNAL_STORAGE}, 1);
|
||||
dialog.dismiss();
|
||||
})
|
||||
.setNegativeButton("Cancel", null)
|
||||
.create()
|
||||
.show();
|
||||
} else {
|
||||
fragment.getActivity().requestPermissions(new String[]{READ_EXTERNAL_STORAGE},
|
||||
1);
|
||||
}
|
||||
} else {
|
||||
controller.startGalleryPick();
|
||||
}
|
||||
}
|
||||
else {
|
||||
controller.startGalleryPick();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,21 +1,20 @@
|
|||
package fr.free.nrw.commons.nearby;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.v4.app.Fragment;
|
||||
import android.support.design.widget.BottomSheetBehavior;
|
||||
|
||||
import android.support.v4.app.FragmentTransaction;
|
||||
import android.support.v4.widget.SwipeRefreshLayout;
|
||||
import android.support.v7.app.AlertDialog;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuInflater;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.ProgressBar;
|
||||
import android.widget.Toast;
|
||||
|
||||
|
|
@ -28,28 +27,37 @@ import javax.inject.Inject;
|
|||
|
||||
import butterknife.BindView;
|
||||
import butterknife.ButterKnife;
|
||||
|
||||
import fr.free.nrw.commons.R;
|
||||
import fr.free.nrw.commons.location.LatLng;
|
||||
import fr.free.nrw.commons.location.LocationServiceManager;
|
||||
import fr.free.nrw.commons.location.LocationUpdateListener;
|
||||
import fr.free.nrw.commons.theme.NavigationBaseActivity;
|
||||
import fr.free.nrw.commons.utils.UriSerializer;
|
||||
|
||||
import fr.free.nrw.commons.utils.ViewUtil;
|
||||
import io.reactivex.Observable;
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers;
|
||||
import io.reactivex.disposables.Disposable;
|
||||
import io.reactivex.schedulers.Schedulers;
|
||||
|
||||
import timber.log.Timber;
|
||||
|
||||
|
||||
public class NearbyActivity extends NavigationBaseActivity implements LocationUpdateListener {
|
||||
|
||||
private static final int LOCATION_REQUEST = 1;
|
||||
private static final String MAP_LAST_USED_PREFERENCE = "mapLastUsed";
|
||||
|
||||
@BindView(R.id.progressBar)
|
||||
ProgressBar progressBar;
|
||||
|
||||
@BindView(R.id.bottom_sheet)
|
||||
LinearLayout bottomSheet;
|
||||
@BindView(R.id.bottom_sheet_details)
|
||||
LinearLayout bottomSheetDetails;
|
||||
@BindView(R.id.transparentView)
|
||||
View transparentView;
|
||||
|
||||
@Inject
|
||||
LocationServiceManager locationManager;
|
||||
@Inject
|
||||
|
|
@ -57,35 +65,57 @@ public class NearbyActivity extends NavigationBaseActivity implements LocationUp
|
|||
|
||||
private LatLng curLatLang;
|
||||
private Bundle bundle;
|
||||
private SharedPreferences sharedPreferences;
|
||||
private NearbyActivityMode viewMode;
|
||||
private Disposable placesDisposable;
|
||||
private boolean lockNearbyView; //Determines if the nearby places needs to be refreshed
|
||||
@BindView(R.id.swipe_container) SwipeRefreshLayout swipeLayout;
|
||||
private BottomSheetBehavior bottomSheetBehavior; // Behavior for list bottom sheet
|
||||
private BottomSheetBehavior bottomSheetBehaviorForDetails; // Behavior for details bottom sheet
|
||||
private NearbyMapFragment nearbyMapFragment;
|
||||
private NearbyListFragment nearbyListFragment;
|
||||
private static final String TAG_RETAINED_MAP_FRAGMENT = NearbyMapFragment.class.getSimpleName();
|
||||
private static final String TAG_RETAINED_LIST_FRAGMENT = NearbyListFragment.class.getSimpleName();
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
sharedPreferences = PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
|
||||
setContentView(R.layout.activity_nearby);
|
||||
ButterKnife.bind(this);
|
||||
resumeFragment();
|
||||
bundle = new Bundle();
|
||||
|
||||
initBottomSheetBehaviour();
|
||||
initDrawer();
|
||||
initViewState();
|
||||
swipeLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
|
||||
@Override
|
||||
public void onRefresh() {
|
||||
lockNearbyView(false);
|
||||
refreshView(true);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void initViewState() {
|
||||
if (sharedPreferences.getBoolean(MAP_LAST_USED_PREFERENCE, false)) {
|
||||
viewMode = NearbyActivityMode.MAP;
|
||||
} else {
|
||||
viewMode = NearbyActivityMode.LIST;
|
||||
}
|
||||
private void resumeFragment() {
|
||||
// Find the retained fragment on activity restarts
|
||||
nearbyMapFragment = getMapFragment();
|
||||
nearbyListFragment = getListFragment();
|
||||
}
|
||||
|
||||
private void initBottomSheetBehaviour() {
|
||||
|
||||
transparentView.setAlpha(0);
|
||||
|
||||
bottomSheet.getLayoutParams().height = getWindowManager()
|
||||
.getDefaultDisplay().getHeight() / 16 * 9;
|
||||
bottomSheetBehavior = BottomSheetBehavior.from(bottomSheet);
|
||||
// TODO initProperBottomSheetBehavior();
|
||||
bottomSheetBehavior.setBottomSheetCallback(new BottomSheetBehavior.BottomSheetCallback() {
|
||||
|
||||
@Override
|
||||
public void onStateChanged(View bottomSheet, int newState) {
|
||||
prepareViewsForSheetPosition(newState);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSlide(View bottomSheet, float slideOffset) {
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
bottomSheetBehavior.setState(BottomSheetBehavior.STATE_HIDDEN);
|
||||
bottomSheetBehaviorForDetails = BottomSheetBehavior.from(bottomSheetDetails);
|
||||
bottomSheetBehaviorForDetails.setState(BottomSheetBehavior.STATE_HIDDEN);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -93,11 +123,6 @@ public class NearbyActivity extends NavigationBaseActivity implements LocationUp
|
|||
MenuInflater inflater = getMenuInflater();
|
||||
inflater.inflate(R.menu.menu_nearby, menu);
|
||||
|
||||
if (viewMode.isMap()) {
|
||||
MenuItem item = menu.findItem(R.id.action_toggle_view);
|
||||
item.setIcon(viewMode.getIcon());
|
||||
}
|
||||
|
||||
return super.onCreateOptionsMenu(menu);
|
||||
}
|
||||
|
||||
|
|
@ -105,14 +130,9 @@ public class NearbyActivity extends NavigationBaseActivity implements LocationUp
|
|||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
// Handle item selection
|
||||
switch (item.getItemId()) {
|
||||
case R.id.action_refresh:
|
||||
lockNearbyView(false);
|
||||
refreshView(true);
|
||||
return true;
|
||||
case R.id.action_toggle_view:
|
||||
viewMode = viewMode.toggle();
|
||||
item.setIcon(viewMode.getIcon());
|
||||
toggleView();
|
||||
case R.id.action_display_list:
|
||||
bottomSheetBehaviorForDetails.setState(BottomSheetBehavior.STATE_HIDDEN);
|
||||
bottomSheetBehavior.setState(BottomSheetBehavior.STATE_EXPANDED);
|
||||
return true;
|
||||
default:
|
||||
return super.onOptionsItemSelected(item);
|
||||
|
|
@ -130,7 +150,7 @@ public class NearbyActivity extends NavigationBaseActivity implements LocationUp
|
|||
switch (requestCode) {
|
||||
case LOCATION_REQUEST: {
|
||||
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
|
||||
refreshView(false);
|
||||
refreshView(LocationServiceManager.LocationChangeType.LOCATION_SIGNIFICANTLY_CHANGED);
|
||||
} else {
|
||||
//If permission not granted, go to page that says Nearby Places cannot be displayed
|
||||
hideProgressBar();
|
||||
|
|
@ -185,7 +205,7 @@ public class NearbyActivity extends NavigationBaseActivity implements LocationUp
|
|||
private void checkLocationPermission() {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
if (locationManager.isLocationPermissionGranted()) {
|
||||
refreshView(false);
|
||||
refreshView(LocationServiceManager.LocationChangeType.LOCATION_SIGNIFICANTLY_CHANGED);
|
||||
} else {
|
||||
// Should we show an explanation?
|
||||
if (locationManager.isPermissionExplanationRequired(this)) {
|
||||
|
|
@ -211,7 +231,7 @@ public class NearbyActivity extends NavigationBaseActivity implements LocationUp
|
|||
}
|
||||
}
|
||||
} else {
|
||||
refreshView(false);
|
||||
refreshView(LocationServiceManager.LocationChangeType.LOCATION_SIGNIFICANTLY_CHANGED);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -220,23 +240,15 @@ public class NearbyActivity extends NavigationBaseActivity implements LocationUp
|
|||
super.onActivityResult(requestCode, resultCode, data);
|
||||
if (requestCode == 1) {
|
||||
Timber.d("User is back from Settings page");
|
||||
refreshView(false);
|
||||
refreshView(LocationServiceManager.LocationChangeType.LOCATION_SIGNIFICANTLY_CHANGED);
|
||||
}
|
||||
}
|
||||
|
||||
private void toggleView() {
|
||||
if (viewMode.isMap()) {
|
||||
setMapFragment();
|
||||
} else {
|
||||
setListFragment();
|
||||
}
|
||||
sharedPreferences.edit().putBoolean(MAP_LAST_USED_PREFERENCE, viewMode.isMap()).apply();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onStart() {
|
||||
super.onStart();
|
||||
locationManager.addLocationListener(this);
|
||||
locationManager.registerLocationManager();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -261,21 +273,34 @@ public class NearbyActivity extends NavigationBaseActivity implements LocationUp
|
|||
checkGps();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPause() {
|
||||
super.onPause();
|
||||
// this means that this activity will not be recreated now, user is leaving it
|
||||
// or the activity is otherwise finishing
|
||||
if(isFinishing()) {
|
||||
// we will not need this fragment anymore, this may also be a good place to signal
|
||||
// to the retained fragment object to perform its own cleanup.
|
||||
removeMapFragment();
|
||||
removeListFragment();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* This method should be the single point to load/refresh nearby places
|
||||
*
|
||||
* @param isHardRefresh Should display a toast if the location hasn't changed
|
||||
* @param locationChangeType defines if location shanged significantly or slightly
|
||||
*/
|
||||
private void refreshView(boolean isHardRefresh) {
|
||||
private void refreshView(LocationServiceManager.LocationChangeType locationChangeType) {
|
||||
if (lockNearbyView) {
|
||||
return;
|
||||
}
|
||||
locationManager.registerLocationManager();
|
||||
LatLng lastLocation = locationManager.getLastLocation();
|
||||
|
||||
if (curLatLang != null && curLatLang.equals(lastLocation)) { //refresh view only if location has changed
|
||||
if (isHardRefresh) {
|
||||
ViewUtil.showSnackbar(findViewById(R.id.container), R.string.nearby_location_has_not_changed);
|
||||
}
|
||||
return;
|
||||
}
|
||||
curLatLang = lastLocation;
|
||||
|
|
@ -285,20 +310,34 @@ public class NearbyActivity extends NavigationBaseActivity implements LocationUp
|
|||
return;
|
||||
}
|
||||
|
||||
progressBar.setVisibility(View.VISIBLE);
|
||||
placesDisposable = Observable.fromCallable(() -> nearbyController
|
||||
.loadAttractionsFromLocation(curLatLang, this))
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(this::populatePlaces);
|
||||
if (locationChangeType
|
||||
.equals(LocationServiceManager.LocationChangeType.LOCATION_SIGNIFICANTLY_CHANGED)) {
|
||||
progressBar.setVisibility(View.VISIBLE);
|
||||
placesDisposable = Observable.fromCallable(() -> nearbyController
|
||||
.loadAttractionsFromLocation(curLatLang))
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(this::populatePlaces);
|
||||
} else if (locationChangeType
|
||||
.equals(LocationServiceManager.LocationChangeType.LOCATION_SLIGHTLY_CHANGED)) {
|
||||
Gson gson = new GsonBuilder()
|
||||
.registerTypeAdapter(Uri.class, new UriSerializer())
|
||||
.create();
|
||||
String gsonCurLatLng = gson.toJson(curLatLang);
|
||||
bundle.putString("CurLatLng", gsonCurLatLng);
|
||||
updateMapFragment(true);
|
||||
}
|
||||
}
|
||||
|
||||
private void populatePlaces(List<Place> placeList) {
|
||||
private void populatePlaces(NearbyController.NearbyPlacesInfo nearbyPlacesInfo) {
|
||||
List<Place> placeList = nearbyPlacesInfo.placeList;
|
||||
LatLng[] boundaryCoordinates = nearbyPlacesInfo.boundaryCoordinates;
|
||||
Gson gson = new GsonBuilder()
|
||||
.registerTypeAdapter(Uri.class, new UriSerializer())
|
||||
.create();
|
||||
String gsonPlaceList = gson.toJson(placeList);
|
||||
String gsonCurLatLng = gson.toJson(curLatLang);
|
||||
String gsonBoundaryCoordinates = gson.toJson(boundaryCoordinates);
|
||||
|
||||
if (placeList.size() == 0) {
|
||||
ViewUtil.showSnackbar(findViewById(R.id.container), R.string.no_nearby);
|
||||
|
|
@ -307,16 +346,20 @@ public class NearbyActivity extends NavigationBaseActivity implements LocationUp
|
|||
bundle.clear();
|
||||
bundle.putString("PlaceList", gsonPlaceList);
|
||||
bundle.putString("CurLatLng", gsonCurLatLng);
|
||||
bundle.putString("BoundaryCoord", gsonBoundaryCoordinates);
|
||||
|
||||
lockNearbyView(true);
|
||||
// Begin the transaction
|
||||
if (viewMode.isMap()) {
|
||||
// First time to init fragments
|
||||
if (nearbyMapFragment == null) {
|
||||
lockNearbyView(true);
|
||||
setMapFragment();
|
||||
} else {
|
||||
setListFragment();
|
||||
hideProgressBar();
|
||||
lockNearbyView(false);
|
||||
} else {
|
||||
// There are fragments, just update the map and list
|
||||
updateMapFragment(false);
|
||||
updateListFragment();
|
||||
}
|
||||
swipeLayout.setRefreshing(false);
|
||||
hideProgressBar();
|
||||
}
|
||||
|
||||
private void lockNearbyView(boolean lock) {
|
||||
|
|
@ -337,14 +380,92 @@ public class NearbyActivity extends NavigationBaseActivity implements LocationUp
|
|||
}
|
||||
}
|
||||
|
||||
private NearbyMapFragment getMapFragment() {
|
||||
return (NearbyMapFragment) getSupportFragmentManager().findFragmentByTag(TAG_RETAINED_MAP_FRAGMENT);
|
||||
}
|
||||
|
||||
private void removeMapFragment() {
|
||||
if (nearbyMapFragment != null) {
|
||||
android.support.v4.app.FragmentManager fm = getSupportFragmentManager();
|
||||
fm.beginTransaction().remove(nearbyMapFragment).commit();
|
||||
}
|
||||
}
|
||||
|
||||
private NearbyListFragment getListFragment() {
|
||||
return (NearbyListFragment) getSupportFragmentManager().findFragmentByTag(TAG_RETAINED_LIST_FRAGMENT);
|
||||
}
|
||||
|
||||
private void removeListFragment() {
|
||||
if (nearbyListFragment != null) {
|
||||
android.support.v4.app.FragmentManager fm = getSupportFragmentManager();
|
||||
fm.beginTransaction().remove(nearbyListFragment).commit();
|
||||
}
|
||||
}
|
||||
|
||||
private void updateMapFragment(boolean isSlightUpdate) {
|
||||
/*
|
||||
* Significant update means updating nearby place markers. Slightly update means only
|
||||
* updating current location marker and camera target.
|
||||
* We update our map Significantly on each 1000 meter change, but we can't never know
|
||||
* the frequency of nearby places. Thus we check if we are close to the boundaries of
|
||||
* our nearby markers, we update our map Significantly.
|
||||
* */
|
||||
|
||||
NearbyMapFragment nearbyMapFragment = getMapFragment();
|
||||
|
||||
if (nearbyMapFragment != null && curLatLang != null) {
|
||||
hideProgressBar(); // In case it is visible (this happens, not an impossible case)
|
||||
/*
|
||||
* If we are close to nearby places boundaries, we need a significant update to
|
||||
* get new nearby places. Check order is south, north, west, east
|
||||
* */
|
||||
if (nearbyMapFragment.boundaryCoordinates != null
|
||||
&& (curLatLang.getLatitude() <= nearbyMapFragment.boundaryCoordinates[0].getLatitude()
|
||||
|| curLatLang.getLatitude() >= nearbyMapFragment.boundaryCoordinates[1].getLatitude()
|
||||
|| curLatLang.getLongitude() <= nearbyMapFragment.boundaryCoordinates[2].getLongitude()
|
||||
|| curLatLang.getLongitude() >= nearbyMapFragment.boundaryCoordinates[3].getLongitude())) {
|
||||
// populate places
|
||||
placesDisposable = Observable.fromCallable(() -> nearbyController
|
||||
.loadAttractionsFromLocation(curLatLang))
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(this::populatePlaces);
|
||||
nearbyMapFragment.setArguments(bundle);
|
||||
nearbyMapFragment.updateMapSignificantly();
|
||||
updateListFragment();
|
||||
return;
|
||||
}
|
||||
|
||||
if (isSlightUpdate) {
|
||||
nearbyMapFragment.setArguments(bundle);
|
||||
nearbyMapFragment.updateMapSlightly();
|
||||
} else {
|
||||
nearbyMapFragment.setArguments(bundle);
|
||||
nearbyMapFragment.updateMapSignificantly();
|
||||
updateListFragment();
|
||||
}
|
||||
} else {
|
||||
lockNearbyView(true);
|
||||
setMapFragment();
|
||||
setListFragment();
|
||||
hideProgressBar();
|
||||
lockNearbyView(false);
|
||||
}
|
||||
}
|
||||
|
||||
private void updateListFragment() {
|
||||
nearbyListFragment.setArguments(bundle);
|
||||
nearbyListFragment.updateNearbyListSignificantly();
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls fragment for map view.
|
||||
*/
|
||||
private void setMapFragment() {
|
||||
FragmentTransaction fragmentTransaction = getSupportFragmentManager().beginTransaction();
|
||||
Fragment fragment = new NearbyMapFragment();
|
||||
fragment.setArguments(bundle);
|
||||
fragmentTransaction.replace(R.id.container, fragment, fragment.getClass().getSimpleName());
|
||||
nearbyMapFragment = new NearbyMapFragment();
|
||||
nearbyMapFragment.setArguments(bundle);
|
||||
fragmentTransaction.replace(R.id.container, nearbyMapFragment, TAG_RETAINED_MAP_FRAGMENT);
|
||||
fragmentTransaction.commitAllowingStateLoss();
|
||||
}
|
||||
|
||||
|
|
@ -353,14 +474,25 @@ public class NearbyActivity extends NavigationBaseActivity implements LocationUp
|
|||
*/
|
||||
private void setListFragment() {
|
||||
FragmentTransaction fragmentTransaction = getSupportFragmentManager().beginTransaction();
|
||||
Fragment fragment = new NearbyListFragment();
|
||||
fragment.setArguments(bundle);
|
||||
fragmentTransaction.replace(R.id.container, fragment, fragment.getClass().getSimpleName());
|
||||
nearbyListFragment = new NearbyListFragment();
|
||||
nearbyListFragment.setArguments(bundle);
|
||||
fragmentTransaction.replace(R.id.container_sheet, nearbyListFragment, TAG_RETAINED_LIST_FRAGMENT);
|
||||
initBottomSheetBehaviour();
|
||||
bottomSheetBehavior.setState(BottomSheetBehavior.STATE_HIDDEN);
|
||||
fragmentTransaction.commitAllowingStateLoss();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLocationChanged(LatLng latLng) {
|
||||
refreshView(false);
|
||||
public void onLocationChangedSignificantly(LatLng latLng) {
|
||||
refreshView(LocationServiceManager.LocationChangeType.LOCATION_SIGNIFICANTLY_CHANGED);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLocationChangedSlightly(LatLng latLng) {
|
||||
refreshView(LocationServiceManager.LocationChangeType.LOCATION_SLIGHTLY_CHANGED);
|
||||
}
|
||||
|
||||
public void prepareViewsForSheetPosition(int bottomSheetState) {
|
||||
// TODO
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,30 +0,0 @@
|
|||
package fr.free.nrw.commons.nearby;
|
||||
|
||||
import android.support.annotation.DrawableRes;
|
||||
|
||||
import fr.free.nrw.commons.R;
|
||||
|
||||
enum NearbyActivityMode {
|
||||
MAP(R.drawable.ic_list_white_24dp),
|
||||
LIST(R.drawable.ic_map_white_24dp);
|
||||
|
||||
@DrawableRes
|
||||
private final int icon;
|
||||
|
||||
NearbyActivityMode(int icon) {
|
||||
this.icon = icon;
|
||||
}
|
||||
|
||||
@DrawableRes
|
||||
public int getIcon() {
|
||||
return icon;
|
||||
}
|
||||
|
||||
public NearbyActivityMode toggle() {
|
||||
return isMap() ? LIST : MAP;
|
||||
}
|
||||
|
||||
public boolean isMap() {
|
||||
return MAP.equals(this);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
package fr.free.nrw.commons.nearby;
|
||||
|
||||
import android.support.annotation.NonNull;
|
||||
|
||||
import android.support.v4.app.Fragment;
|
||||
|
||||
import com.pedrogomez.renderers.ListAdapteeCollection;
|
||||
import com.pedrogomez.renderers.RVRendererAdapter;
|
||||
|
|
@ -9,18 +10,32 @@ import com.pedrogomez.renderers.RendererBuilder;
|
|||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
class NearbyAdapterFactory {
|
||||
private PlaceRenderer.PlaceClickedListener listener;
|
||||
import fr.free.nrw.commons.contributions.ContributionController;
|
||||
|
||||
NearbyAdapterFactory(@NonNull PlaceRenderer.PlaceClickedListener listener) {
|
||||
this.listener = listener;
|
||||
class NearbyAdapterFactory {
|
||||
|
||||
private Fragment fragment;
|
||||
private ContributionController controller;
|
||||
|
||||
NearbyAdapterFactory(){
|
||||
|
||||
}
|
||||
|
||||
NearbyAdapterFactory(Fragment fragment, ContributionController controller) {
|
||||
this.fragment = fragment;
|
||||
this.controller = controller;
|
||||
}
|
||||
|
||||
public RVRendererAdapter<Place> create(List<Place> placeList) {
|
||||
RendererBuilder<Place> builder = new RendererBuilder<Place>()
|
||||
.bind(Place.class, new PlaceRenderer(listener));
|
||||
.bind(Place.class, new PlaceRenderer(fragment, controller));
|
||||
ListAdapteeCollection<Place> collection = new ListAdapteeCollection<>(
|
||||
placeList != null ? placeList : Collections.<Place>emptyList());
|
||||
placeList != null ? placeList : Collections.emptyList());
|
||||
return new RVRendererAdapter<>(builder, collection);
|
||||
}
|
||||
}
|
||||
|
||||
public void updateAdapterData(List<Place> newPlaceList, RVRendererAdapter<Place> rendererAdapter) {
|
||||
rendererAdapter.notifyDataSetChanged();
|
||||
rendererAdapter.diffUpdate(newPlaceList);
|
||||
}
|
||||
}
|
||||
|
|
@ -37,7 +37,7 @@ public class NearbyBaseMarker extends BaseMarkerOptions<NearbyMarker, NearbyBase
|
|||
.registerTypeAdapter(Uri.class, new UriDeserializer())
|
||||
.create();
|
||||
|
||||
position((LatLng) in.readParcelable(LatLng.class.getClassLoader()));
|
||||
position(in.readParcelable(LatLng.class.getClassLoader()));
|
||||
snippet(in.readString());
|
||||
String iconId = in.readString();
|
||||
Bitmap iconBitmap = in.readParcelable(Bitmap.class.getClassLoader());
|
||||
|
|
|
|||
|
|
@ -41,30 +41,54 @@ public class NearbyController {
|
|||
* Prepares Place list to make their distance information update later.
|
||||
*
|
||||
* @param curLatLng current location for user
|
||||
* @param context context
|
||||
* @return Place list without distance information
|
||||
* @return NearbyPlacesInfo a variable holds Place list without distance information
|
||||
* and boundary coordinates of current Place List
|
||||
*/
|
||||
public List<Place> loadAttractionsFromLocation(LatLng curLatLng, Context context) {
|
||||
public NearbyPlacesInfo loadAttractionsFromLocation(LatLng curLatLng) {
|
||||
|
||||
Timber.d("Loading attractions near %s", curLatLng);
|
||||
NearbyPlacesInfo nearbyPlacesInfo = new NearbyPlacesInfo();
|
||||
|
||||
if (curLatLng == null) {
|
||||
return Collections.emptyList();
|
||||
return null;
|
||||
}
|
||||
List<Place> places = prefs.getBoolean("useWikidata", true)
|
||||
? nearbyPlaces.getFromWikidataQuery(curLatLng, Locale.getDefault().getLanguage())
|
||||
: nearbyPlaces.getFromWikiNeedsPictures();
|
||||
Timber.d("Sorting places by distance...");
|
||||
final Map<Place, Double> distances = new HashMap<>();
|
||||
for (Place place : places) {
|
||||
distances.put(place, computeDistanceBetween(place.location, curLatLng));
|
||||
}
|
||||
Collections.sort(places,
|
||||
(lhs, rhs) -> {
|
||||
double lhsDistance = distances.get(lhs);
|
||||
double rhsDistance = distances.get(rhs);
|
||||
return (int) (lhsDistance - rhsDistance);
|
||||
List<Place> places = nearbyPlaces.getFromWikidataQuery(curLatLng, Locale.getDefault().getLanguage());
|
||||
|
||||
LatLng[] boundaryCoordinates = {places.get(0).location, // south
|
||||
places.get(0).location, // north
|
||||
places.get(0).location, // west
|
||||
places.get(0).location};// east, init with a random location
|
||||
|
||||
if (curLatLng != null) {
|
||||
Timber.d("Sorting places by distance...");
|
||||
final Map<Place, Double> distances = new HashMap<>();
|
||||
for (Place place: places) {
|
||||
distances.put(place, computeDistanceBetween(place.location, curLatLng));
|
||||
// Find boundaries with basic find max approach
|
||||
if (place.location.getLatitude() < boundaryCoordinates[0].getLatitude()) {
|
||||
boundaryCoordinates[0] = place.location;
|
||||
}
|
||||
);
|
||||
return places;
|
||||
if (place.location.getLatitude() > boundaryCoordinates[1].getLatitude()) {
|
||||
boundaryCoordinates[1] = place.location;
|
||||
}
|
||||
if (place.location.getLongitude() < boundaryCoordinates[2].getLongitude()) {
|
||||
boundaryCoordinates[2] = place.location;
|
||||
}
|
||||
if (place.location.getLongitude() > boundaryCoordinates[3].getLongitude()) {
|
||||
boundaryCoordinates[3] = place.location;
|
||||
}
|
||||
}
|
||||
Collections.sort(places,
|
||||
(lhs, rhs) -> {
|
||||
double lhsDistance = distances.get(lhs);
|
||||
double rhsDistance = distances.get(rhs);
|
||||
return (int) (lhsDistance - rhsDistance);
|
||||
}
|
||||
);
|
||||
}
|
||||
nearbyPlacesInfo.placeList = places;
|
||||
nearbyPlacesInfo.boundaryCoordinates = boundaryCoordinates;
|
||||
return nearbyPlacesInfo;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -129,4 +153,9 @@ public class NearbyController {
|
|||
}
|
||||
return baseMarkerOptions;
|
||||
}
|
||||
|
||||
public class NearbyPlacesInfo {
|
||||
List<Place> placeList; // List of nearby places
|
||||
LatLng[] boundaryCoordinates; // Corners of nearby area
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,154 +0,0 @@
|
|||
package fr.free.nrw.commons.nearby;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.support.customtabs.CustomTabsIntent;
|
||||
import android.support.v4.app.FragmentActivity;
|
||||
import android.support.v4.content.ContextCompat;
|
||||
import android.support.v7.widget.PopupMenu;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import butterknife.BindView;
|
||||
import butterknife.ButterKnife;
|
||||
import butterknife.OnClick;
|
||||
import butterknife.Unbinder;
|
||||
import fr.free.nrw.commons.R;
|
||||
import fr.free.nrw.commons.Utils;
|
||||
import fr.free.nrw.commons.location.LatLng;
|
||||
import fr.free.nrw.commons.ui.widget.OverlayDialog;
|
||||
import fr.free.nrw.commons.utils.DialogUtil;
|
||||
|
||||
public class NearbyInfoDialog extends OverlayDialog {
|
||||
|
||||
private final static String ARG_TITLE = "placeTitle";
|
||||
private final static String ARG_DESC = "placeDesc";
|
||||
private final static String ARG_LATITUDE = "latitude";
|
||||
private final static String ARG_LONGITUDE = "longitude";
|
||||
private final static String ARG_SITE_LINK = "sitelink";
|
||||
|
||||
@BindView(R.id.link_preview_title) TextView placeTitle;
|
||||
@BindView(R.id.link_preview_extract) TextView placeDescription;
|
||||
@BindView(R.id.link_preview_go_button) TextView goToButton;
|
||||
@BindView(R.id.link_preview_overflow_button) ImageView overflowButton;
|
||||
|
||||
private Unbinder unbinder;
|
||||
private LatLng location;
|
||||
private Sitelinks sitelinks;
|
||||
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||
View view = inflater.inflate(R.layout.dialog_nearby_info, container, false);
|
||||
unbinder = ButterKnife.bind(this, view);
|
||||
initUi();
|
||||
return view;
|
||||
}
|
||||
|
||||
private void initUi() {
|
||||
Bundle bundle = getArguments();
|
||||
placeTitle.setText(bundle.getString(ARG_TITLE));
|
||||
placeDescription.setText(bundle.getString(ARG_DESC));
|
||||
location = new LatLng(bundle.getDouble(ARG_LATITUDE), bundle.getDouble(ARG_LONGITUDE), 0);
|
||||
getArticleLink(bundle);
|
||||
}
|
||||
|
||||
private void getArticleLink(Bundle bundle) {
|
||||
this.sitelinks = bundle.getParcelable(ARG_SITE_LINK);
|
||||
|
||||
if (sitelinks == null || Uri.EMPTY.equals(sitelinks.getWikipediaLink())) {
|
||||
goToButton.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
overflowButton.setVisibility(showMenu() ? View.VISIBLE : View.GONE);
|
||||
|
||||
overflowButton.setOnClickListener(v -> popupMenuListener());
|
||||
}
|
||||
|
||||
private void popupMenuListener() {
|
||||
PopupMenu popupMenu = new PopupMenu(getActivity(), overflowButton);
|
||||
popupMenu.inflate(R.menu.nearby_info_dialog_options);
|
||||
|
||||
MenuItem commonsArticle = popupMenu.getMenu()
|
||||
.findItem(R.id.nearby_info_menu_commons_article);
|
||||
MenuItem wikiDataArticle = popupMenu.getMenu()
|
||||
.findItem(R.id.nearby_info_menu_wikidata_article);
|
||||
|
||||
commonsArticle.setEnabled(!sitelinks.getCommonsLink().equals(Uri.EMPTY));
|
||||
wikiDataArticle.setEnabled(!sitelinks.getWikidataLink().equals(Uri.EMPTY));
|
||||
|
||||
popupMenu.setOnMenuItemClickListener(menuListener);
|
||||
popupMenu.show();
|
||||
}
|
||||
|
||||
private boolean showMenu() {
|
||||
return !sitelinks.getCommonsLink().equals(Uri.EMPTY)
|
||||
|| !sitelinks.getWikidataLink().equals(Uri.EMPTY);
|
||||
}
|
||||
|
||||
private final PopupMenu.OnMenuItemClickListener menuListener = new PopupMenu
|
||||
.OnMenuItemClickListener() {
|
||||
@Override
|
||||
public boolean onMenuItemClick(MenuItem item) {
|
||||
switch (item.getItemId()) {
|
||||
case R.id.nearby_info_menu_commons_article:
|
||||
openWebView(sitelinks.getCommonsLink());
|
||||
return true;
|
||||
case R.id.nearby_info_menu_wikidata_article:
|
||||
openWebView(sitelinks.getWikidataLink());
|
||||
return true;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
public static void showYourself(FragmentActivity fragmentActivity, Place place) {
|
||||
NearbyInfoDialog mDialog = new NearbyInfoDialog();
|
||||
Bundle bundle = new Bundle();
|
||||
bundle.putString(ARG_TITLE, place.name);
|
||||
bundle.putString(ARG_DESC, place.getLongDescription());
|
||||
bundle.putDouble(ARG_LATITUDE, place.location.getLatitude());
|
||||
bundle.putDouble(ARG_LONGITUDE, place.location.getLongitude());
|
||||
bundle.putParcelable(ARG_SITE_LINK, place.siteLinks);
|
||||
mDialog.setArguments(bundle);
|
||||
DialogUtil.showSafely(fragmentActivity, mDialog);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroyView() {
|
||||
super.onDestroyView();
|
||||
unbinder.unbind();
|
||||
}
|
||||
|
||||
@OnClick(R.id.link_preview_directions_button)
|
||||
void onDirectionsClick() {
|
||||
//Open map app at given position
|
||||
Uri gmmIntentUri = Uri.parse(
|
||||
"geo:0,0?q=" + location.getLatitude() + "," + location.getLongitude());
|
||||
Intent mapIntent = new Intent(Intent.ACTION_VIEW, gmmIntentUri);
|
||||
|
||||
if (mapIntent.resolveActivity(getActivity().getPackageManager()) != null) {
|
||||
startActivity(mapIntent);
|
||||
}
|
||||
}
|
||||
|
||||
@OnClick(R.id.link_preview_go_button)
|
||||
void onReadArticleClick() {
|
||||
openWebView(sitelinks.getWikipediaLink());
|
||||
}
|
||||
|
||||
private void openWebView(Uri link) {
|
||||
Utils.handleWebUrl(getContext(),link);
|
||||
}
|
||||
|
||||
@OnClick(R.id.emptyLayout)
|
||||
void onCloseClicked() {
|
||||
dismissAllowingStateLoss();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,9 +1,11 @@
|
|||
package fr.free.nrw.commons.nearby;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.support.v4.app.Fragment;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.v7.widget.LinearLayoutManager;
|
||||
import android.support.v7.widget.RecyclerView;
|
||||
import android.view.LayoutInflater;
|
||||
|
|
@ -13,18 +15,24 @@ import android.view.ViewGroup;
|
|||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
import com.pedrogomez.renderers.RVRendererAdapter;
|
||||
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import dagger.android.support.AndroidSupportInjection;
|
||||
import dagger.android.support.DaggerFragment;
|
||||
import fr.free.nrw.commons.R;
|
||||
import fr.free.nrw.commons.contributions.ContributionController;
|
||||
import fr.free.nrw.commons.location.LatLng;
|
||||
import fr.free.nrw.commons.utils.UriDeserializer;
|
||||
import timber.log.Timber;
|
||||
|
||||
public class NearbyListFragment extends Fragment {
|
||||
import static android.app.Activity.RESULT_OK;
|
||||
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
|
||||
|
||||
public class NearbyListFragment extends DaggerFragment {
|
||||
private static final Type LIST_TYPE = new TypeToken<List<Place>>() {
|
||||
}.getType();
|
||||
private static final Type CUR_LAT_LNG_TYPE = new TypeToken<LatLng>() {
|
||||
|
|
@ -35,6 +43,7 @@ public class NearbyListFragment extends Fragment {
|
|||
|
||||
private NearbyAdapterFactory adapterFactory;
|
||||
private RecyclerView recyclerView;
|
||||
private ContributionController controller;
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
|
|
@ -54,9 +63,11 @@ public class NearbyListFragment extends Fragment {
|
|||
Bundle savedInstanceState) {
|
||||
Timber.d("NearbyListFragment created");
|
||||
View view = inflater.inflate(R.layout.fragment_nearby, container, false);
|
||||
recyclerView = (RecyclerView) view.findViewById(R.id.listView);
|
||||
recyclerView = view.findViewById(R.id.listView);
|
||||
recyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
|
||||
adapterFactory = new NearbyAdapterFactory(place -> NearbyInfoDialog.showYourself(getActivity(), place));
|
||||
|
||||
controller = new ContributionController(this);
|
||||
adapterFactory = new NearbyAdapterFactory(this, controller);
|
||||
return view;
|
||||
}
|
||||
|
||||
|
|
@ -64,9 +75,19 @@ public class NearbyListFragment extends Fragment {
|
|||
public void onViewCreated(View view, Bundle savedInstanceState) {
|
||||
// Check that this is the first time view is created,
|
||||
// to avoid double list when screen orientation changed
|
||||
Bundle bundle = this.getArguments();
|
||||
recyclerView.setAdapter(adapterFactory.create(getPlaceListFromBundle(bundle)));
|
||||
}
|
||||
|
||||
public void updateNearbyListSignificantly() {
|
||||
Bundle bundle = this.getArguments();
|
||||
adapterFactory.updateAdapterData(getPlaceListFromBundle(bundle),
|
||||
(RVRendererAdapter<Place>) recyclerView.getAdapter());
|
||||
}
|
||||
|
||||
private List<Place> getPlaceListFromBundle(Bundle bundle) {
|
||||
List<Place> placeList = Collections.emptyList();
|
||||
|
||||
Bundle bundle = this.getArguments();
|
||||
if (bundle != null) {
|
||||
String gsonPlaceList = bundle.getString("PlaceList", "[]");
|
||||
placeList = gson.fromJson(gsonPlaceList, LIST_TYPE);
|
||||
|
|
@ -77,6 +98,46 @@ public class NearbyListFragment extends Fragment {
|
|||
placeList = NearbyController.loadAttractionsFromLocationToPlaces(curLatLng, placeList);
|
||||
}
|
||||
|
||||
recyclerView.setAdapter(adapterFactory.create(placeList));
|
||||
return placeList;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
|
||||
Timber.d("onRequestPermissionsResult: req code = " + " perm = " + permissions + " grant =" + grantResults);
|
||||
|
||||
switch (requestCode) {
|
||||
// 1 = "Read external storage" allowed when gallery selected
|
||||
case 1: {
|
||||
if (grantResults.length > 0 && grantResults[0] == PERMISSION_GRANTED) {
|
||||
Timber.d("Call controller.startGalleryPick()");
|
||||
controller.startGalleryPick();
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
// 3 = "Write external storage" allowed when camera selected
|
||||
case 3: {
|
||||
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
|
||||
Timber.d("Call controller.startCameraCapture()");
|
||||
controller.startCameraCapture();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||
super.onActivityResult(requestCode, resultCode, data);
|
||||
|
||||
if (resultCode == RESULT_OK) {
|
||||
Timber.d("OnActivityResult() parameters: Req code: %d Result code: %d Data: %s",
|
||||
requestCode, resultCode, data);
|
||||
controller.handleImagePicked(requestCode, data, true);
|
||||
} else {
|
||||
Timber.e("OnActivityResult() parameters: Req code: %d Result code: %d Data: %s",
|
||||
requestCode, resultCode, data);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,37 +1,118 @@
|
|||
package fr.free.nrw.commons.nearby;
|
||||
|
||||
import android.animation.ObjectAnimator;
|
||||
import android.animation.TypeEvaluator;
|
||||
import android.animation.ValueAnimator;
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.graphics.Color;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.design.widget.BottomSheetBehavior;
|
||||
import android.support.design.widget.CoordinatorLayout;
|
||||
import android.support.design.widget.FloatingActionButton;
|
||||
import android.view.Gravity;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.animation.Animation;
|
||||
import android.view.animation.AnimationUtils;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
import com.mapbox.mapboxsdk.Mapbox;
|
||||
import com.mapbox.mapboxsdk.annotations.Icon;
|
||||
import com.mapbox.mapboxsdk.annotations.IconFactory;
|
||||
import com.mapbox.mapboxsdk.annotations.Marker;
|
||||
import com.mapbox.mapboxsdk.annotations.MarkerOptions;
|
||||
import com.mapbox.mapboxsdk.annotations.PolygonOptions;
|
||||
import com.mapbox.mapboxsdk.camera.CameraPosition;
|
||||
import com.mapbox.mapboxsdk.camera.CameraUpdateFactory;
|
||||
import com.mapbox.mapboxsdk.constants.Style;
|
||||
import com.mapbox.mapboxsdk.geometry.LatLng;
|
||||
import com.mapbox.mapboxsdk.maps.MapView;
|
||||
import com.mapbox.mapboxsdk.maps.MapboxMap;
|
||||
import com.mapbox.mapboxsdk.maps.MapboxMapOptions;
|
||||
import com.mapbox.mapboxsdk.maps.OnMapReadyCallback;
|
||||
import com.mapbox.services.android.telemetry.MapboxTelemetry;
|
||||
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import fr.free.nrw.commons.R;
|
||||
import fr.free.nrw.commons.utils.UriDeserializer;
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Named;
|
||||
|
||||
import dagger.android.support.DaggerFragment;
|
||||
import fr.free.nrw.commons.R;
|
||||
import fr.free.nrw.commons.contributions.ContributionController;
|
||||
import fr.free.nrw.commons.utils.UriDeserializer;
|
||||
import timber.log.Timber;
|
||||
|
||||
import static android.app.Activity.RESULT_OK;
|
||||
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
|
||||
|
||||
public class NearbyMapFragment extends DaggerFragment {
|
||||
|
||||
public class NearbyMapFragment extends android.support.v4.app.Fragment {
|
||||
private MapView mapView;
|
||||
private List<NearbyBaseMarker> baseMarkerOptions;
|
||||
private fr.free.nrw.commons.location.LatLng curLatLng;
|
||||
public fr.free.nrw.commons.location.LatLng[] boundaryCoordinates;
|
||||
|
||||
private View bottomSheetList;
|
||||
private View bottomSheetDetails;
|
||||
|
||||
private BottomSheetBehavior bottomSheetListBehavior;
|
||||
private BottomSheetBehavior bottomSheetDetailsBehavior;
|
||||
private LinearLayout wikipediaButton;
|
||||
private LinearLayout wikidataButton;
|
||||
private LinearLayout directionsButton;
|
||||
private LinearLayout commonsButton;
|
||||
private FloatingActionButton fabPlus;
|
||||
private FloatingActionButton fabCamera;
|
||||
private FloatingActionButton fabGallery;
|
||||
private FloatingActionButton fabRecenter;
|
||||
private View transparentView;
|
||||
private TextView description;
|
||||
private TextView title;
|
||||
private TextView distance;
|
||||
private ImageView icon;
|
||||
|
||||
private TextView wikipediaButtonText;
|
||||
private TextView wikidataButtonText;
|
||||
private TextView commonsButtonText;
|
||||
private TextView directionsButtonText;
|
||||
|
||||
private boolean isFabOpen = false;
|
||||
private Animation rotate_backward;
|
||||
private Animation fab_close;
|
||||
private Animation fab_open;
|
||||
private Animation rotate_forward;
|
||||
private ContributionController controller;
|
||||
|
||||
private Place place;
|
||||
private Marker selected;
|
||||
private Marker currentLocationMarker;
|
||||
private MapboxMap mapboxMap;
|
||||
private PolygonOptions currentLocationPolygonOptions;
|
||||
|
||||
private boolean isBottomListSheetExpanded;
|
||||
private final double CAMERA_TARGET_SHIFT_FACTOR = 0.06;
|
||||
|
||||
@Inject
|
||||
@Named("prefs")
|
||||
SharedPreferences prefs;
|
||||
@Inject
|
||||
@Named("direct_nearby_upload_prefs")
|
||||
SharedPreferences directPrefs;
|
||||
|
||||
public NearbyMapFragment() {
|
||||
}
|
||||
|
|
@ -46,18 +127,24 @@ public class NearbyMapFragment extends android.support.v4.app.Fragment {
|
|||
if (bundle != null) {
|
||||
String gsonPlaceList = bundle.getString("PlaceList");
|
||||
String gsonLatLng = bundle.getString("CurLatLng");
|
||||
Type listType = new TypeToken<List<Place>>() {}.getType();
|
||||
Type listType = new TypeToken<List<Place>>() {
|
||||
}.getType();
|
||||
String gsonBoundaryCoordinates = bundle.getString("BoundaryCoord");
|
||||
List<Place> placeList = gson.fromJson(gsonPlaceList, listType);
|
||||
Type curLatLngType = new TypeToken<fr.free.nrw.commons.location.LatLng>() {}.getType();
|
||||
Type curLatLngType = new TypeToken<fr.free.nrw.commons.location.LatLng>() {
|
||||
}.getType();
|
||||
Type gsonBoundaryCoordinatesType = new TypeToken<fr.free.nrw.commons.location.LatLng[]>() {}.getType();
|
||||
curLatLng = gson.fromJson(gsonLatLng, curLatLngType);
|
||||
baseMarkerOptions = NearbyController
|
||||
.loadAttractionsFromLocationToBaseMarkerOptions(curLatLng,
|
||||
placeList,
|
||||
getActivity());
|
||||
boundaryCoordinates = gson.fromJson(gsonBoundaryCoordinates, gsonBoundaryCoordinatesType);
|
||||
}
|
||||
Mapbox.getInstance(getActivity(),
|
||||
getString(R.string.mapbox_commons_app_token));
|
||||
MapboxTelemetry.getInstance().setTelemetryEnabled(false);
|
||||
setRetainInstance(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -73,6 +160,252 @@ public class NearbyMapFragment extends android.support.v4.app.Fragment {
|
|||
return mapView;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
|
||||
super.onViewCreated(view, savedInstanceState);
|
||||
this.getView().setFocusableInTouchMode(true);
|
||||
this.getView().requestFocus();
|
||||
this.getView().setOnKeyListener((v, keyCode, event) -> {
|
||||
if (keyCode == KeyEvent.KEYCODE_BACK) {
|
||||
if (bottomSheetDetailsBehavior.getState() == BottomSheetBehavior
|
||||
.STATE_EXPANDED) {
|
||||
bottomSheetDetailsBehavior.setState(BottomSheetBehavior.STATE_COLLAPSED);
|
||||
return true;
|
||||
} else if (bottomSheetDetailsBehavior.getState() == BottomSheetBehavior
|
||||
.STATE_COLLAPSED) {
|
||||
bottomSheetDetailsBehavior.setState(BottomSheetBehavior.STATE_HIDDEN);
|
||||
mapView.getMapAsync(MapboxMap::deselectMarkers);
|
||||
selected = null;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
public void updateMapSlightly() {
|
||||
// Get arguments from bundle for new location
|
||||
Bundle bundle = this.getArguments();
|
||||
if (mapboxMap != null) {
|
||||
Gson gson = new GsonBuilder()
|
||||
.registerTypeAdapter(Uri.class, new UriDeserializer())
|
||||
.create();
|
||||
if (bundle != null) {
|
||||
String gsonLatLng = bundle.getString("CurLatLng");
|
||||
Type curLatLngType = new TypeToken<fr.free.nrw.commons.location.LatLng>() {}.getType();
|
||||
curLatLng = gson.fromJson(gsonLatLng, curLatLngType);
|
||||
}
|
||||
updateMapToTrackPosition();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void updateMapSignificantly() {
|
||||
|
||||
Bundle bundle = this.getArguments();
|
||||
if (mapboxMap != null) {
|
||||
if (bundle != null) {
|
||||
Gson gson = new GsonBuilder()
|
||||
.registerTypeAdapter(Uri.class, new UriDeserializer())
|
||||
.create();
|
||||
|
||||
String gsonPlaceList = bundle.getString("PlaceList");
|
||||
String gsonLatLng = bundle.getString("CurLatLng");
|
||||
String gsonBoundaryCoordinates = bundle.getString("BoundaryCoord");
|
||||
Type listType = new TypeToken<List<Place>>() {}.getType();
|
||||
List<Place> placeList = gson.fromJson(gsonPlaceList, listType);
|
||||
Type curLatLngType = new TypeToken<fr.free.nrw.commons.location.LatLng>() {}.getType();
|
||||
Type gsonBoundaryCoordinatesType = new TypeToken<fr.free.nrw.commons.location.LatLng[]>() {}.getType();
|
||||
curLatLng = gson.fromJson(gsonLatLng, curLatLngType);
|
||||
baseMarkerOptions = NearbyController
|
||||
.loadAttractionsFromLocationToBaseMarkerOptions(curLatLng,
|
||||
placeList,
|
||||
getActivity());
|
||||
boundaryCoordinates = gson.fromJson(gsonBoundaryCoordinates, gsonBoundaryCoordinatesType);
|
||||
}
|
||||
mapboxMap.clear();
|
||||
addCurrentLocationMarker(mapboxMap);
|
||||
updateMapToTrackPosition();
|
||||
addNearbyMarkerstoMapBoxMap();
|
||||
}
|
||||
}
|
||||
|
||||
// Only update current position marker and camera view
|
||||
private void updateMapToTrackPosition() {
|
||||
|
||||
if (currentLocationMarker != null) {
|
||||
LatLng curMapBoxLatLng = new LatLng(curLatLng.getLatitude(),curLatLng.getLongitude());
|
||||
ValueAnimator markerAnimator = ObjectAnimator.ofObject(currentLocationMarker, "position",
|
||||
new LatLngEvaluator(), currentLocationMarker.getPosition(),
|
||||
curMapBoxLatLng);
|
||||
markerAnimator.setDuration(1000);
|
||||
markerAnimator.start();
|
||||
|
||||
List<LatLng> circle = createCircleArray(curLatLng.getLatitude(), curLatLng.getLongitude(),
|
||||
curLatLng.getAccuracy() * 2, 100);
|
||||
if (currentLocationPolygonOptions != null){
|
||||
mapboxMap.removePolygon(currentLocationPolygonOptions.getPolygon());
|
||||
currentLocationPolygonOptions = new PolygonOptions()
|
||||
.addAll(circle)
|
||||
.strokeColor(Color.parseColor("#55000000"))
|
||||
.fillColor(Color.parseColor("#11000000"));
|
||||
mapboxMap.addPolygon(currentLocationPolygonOptions);
|
||||
}
|
||||
|
||||
// Make camera to follow user on location change
|
||||
CameraPosition position = new CameraPosition.Builder()
|
||||
.target(isBottomListSheetExpanded ?
|
||||
new LatLng(curMapBoxLatLng.getLatitude()- CAMERA_TARGET_SHIFT_FACTOR,
|
||||
curMapBoxLatLng.getLongitude())
|
||||
: curMapBoxLatLng ) // Sets the new camera position
|
||||
.zoom(mapboxMap.getCameraPosition().zoom) // Same zoom level
|
||||
.build();
|
||||
|
||||
mapboxMap.animateCamera(CameraUpdateFactory
|
||||
.newCameraPosition(position), 1000);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private void updateMapCameraAccordingToBottomSheet(boolean isBottomListSheetExpanded) {
|
||||
CameraPosition position;
|
||||
this.isBottomListSheetExpanded = isBottomListSheetExpanded;
|
||||
if (mapboxMap != null && curLatLng != null) {
|
||||
if (isBottomListSheetExpanded) {
|
||||
// Make camera to follow user on location change
|
||||
position = new CameraPosition.Builder()
|
||||
.target(new LatLng(curLatLng.getLatitude() - CAMERA_TARGET_SHIFT_FACTOR,
|
||||
curLatLng.getLongitude())) // Sets the new camera target above
|
||||
// current to make it visible when sheet is expanded
|
||||
.zoom(11) // Same zoom level
|
||||
.build();
|
||||
|
||||
} else {
|
||||
// Make camera to follow user on location change
|
||||
position = new CameraPosition.Builder()
|
||||
.target(new LatLng(curLatLng.getLatitude(),
|
||||
curLatLng.getLongitude())) // Sets the new camera target to curLatLng
|
||||
.zoom(mapboxMap.getCameraPosition().zoom) // Same zoom level
|
||||
.build();
|
||||
}
|
||||
mapboxMap.animateCamera(CameraUpdateFactory
|
||||
.newCameraPosition(position), 1000);
|
||||
}
|
||||
}
|
||||
|
||||
private void initViews() {
|
||||
bottomSheetList = getActivity().findViewById(R.id.bottom_sheet);
|
||||
bottomSheetListBehavior = BottomSheetBehavior.from(bottomSheetList);
|
||||
bottomSheetDetails = getActivity().findViewById(R.id.bottom_sheet_details);
|
||||
bottomSheetDetailsBehavior = BottomSheetBehavior.from(bottomSheetDetails);
|
||||
bottomSheetDetailsBehavior.setState(BottomSheetBehavior.STATE_HIDDEN);
|
||||
bottomSheetDetails.setVisibility(View.VISIBLE);
|
||||
|
||||
fabPlus = getActivity().findViewById(R.id.fab_plus);
|
||||
fabCamera = getActivity().findViewById(R.id.fab_camera);
|
||||
fabGallery = getActivity().findViewById(R.id.fab_galery);
|
||||
fabRecenter = getActivity().findViewById(R.id.fab_recenter);
|
||||
|
||||
fab_open = AnimationUtils.loadAnimation(getActivity(), R.anim.fab_open);
|
||||
fab_close = AnimationUtils.loadAnimation(getActivity(), R.anim.fab_close);
|
||||
rotate_forward = AnimationUtils.loadAnimation(getActivity(), R.anim.rotate_forward);
|
||||
rotate_backward = AnimationUtils.loadAnimation(getActivity(), R.anim.rotate_backward);
|
||||
|
||||
transparentView = getActivity().findViewById(R.id.transparentView);
|
||||
|
||||
description = getActivity().findViewById(R.id.description);
|
||||
title = getActivity().findViewById(R.id.title);
|
||||
distance = getActivity().findViewById(R.id.category);
|
||||
icon = getActivity().findViewById(R.id.icon);
|
||||
|
||||
wikidataButton = getActivity().findViewById(R.id.wikidataButton);
|
||||
wikipediaButton = getActivity().findViewById(R.id.wikipediaButton);
|
||||
directionsButton = getActivity().findViewById(R.id.directionsButton);
|
||||
commonsButton = getActivity().findViewById(R.id.commonsButton);
|
||||
|
||||
wikidataButtonText = getActivity().findViewById(R.id.wikidataButtonText);
|
||||
wikipediaButtonText = getActivity().findViewById(R.id.wikipediaButtonText);
|
||||
directionsButtonText = getActivity().findViewById(R.id.directionsButtonText);
|
||||
commonsButtonText = getActivity().findViewById(R.id.commonsButtonText);
|
||||
|
||||
}
|
||||
|
||||
private void setListeners() {
|
||||
fabPlus.setOnClickListener(view -> animateFAB(isFabOpen));
|
||||
|
||||
bottomSheetDetails.setOnClickListener(view -> {
|
||||
if (bottomSheetDetailsBehavior.getState() == BottomSheetBehavior.STATE_COLLAPSED) {
|
||||
bottomSheetDetailsBehavior.setState(BottomSheetBehavior.STATE_EXPANDED);
|
||||
} else {
|
||||
bottomSheetDetailsBehavior.setState(BottomSheetBehavior.STATE_COLLAPSED);
|
||||
}
|
||||
});
|
||||
|
||||
fabRecenter.setOnClickListener(view -> {
|
||||
if (curLatLng != null) {
|
||||
mapView.getMapAsync(mapboxMap -> {
|
||||
CameraPosition position = new CameraPosition.Builder()
|
||||
.target(new LatLng(curLatLng.getLatitude(), curLatLng.getLongitude())) // Sets the new camera position
|
||||
.zoom(11) // Sets the zoom
|
||||
.build(); // Creates a CameraPosition from the builder
|
||||
|
||||
mapboxMap.animateCamera(CameraUpdateFactory
|
||||
.newCameraPosition(position), 1000);
|
||||
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
bottomSheetDetailsBehavior.setBottomSheetCallback(new BottomSheetBehavior
|
||||
.BottomSheetCallback() {
|
||||
@Override
|
||||
public void onStateChanged(@NonNull View bottomSheet, int newState) {
|
||||
prepareViewsForSheetPosition(newState);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSlide(@NonNull View bottomSheet, float slideOffset) {
|
||||
if (slideOffset >= 0) {
|
||||
transparentView.setAlpha(slideOffset);
|
||||
if (slideOffset == 1) {
|
||||
transparentView.setClickable(true);
|
||||
} else if (slideOffset == 0) {
|
||||
transparentView.setClickable(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
bottomSheetListBehavior.setBottomSheetCallback(new BottomSheetBehavior
|
||||
.BottomSheetCallback() {
|
||||
@Override
|
||||
public void onStateChanged(@NonNull View bottomSheet, int newState) {
|
||||
if (newState == BottomSheetBehavior.STATE_EXPANDED) {
|
||||
bottomSheetDetailsBehavior.setState(BottomSheetBehavior.STATE_HIDDEN);
|
||||
updateMapCameraAccordingToBottomSheet(true);
|
||||
} else {
|
||||
updateMapCameraAccordingToBottomSheet(false);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSlide(@NonNull View bottomSheet, float slideOffset) {
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
// Remove texts if it doesnt fit
|
||||
if (wikipediaButtonText.getLineCount() > 1
|
||||
|| wikidataButtonText.getLineCount() > 1
|
||||
|| commonsButtonText.getLineCount() > 1
|
||||
|| directionsButtonText.getLineCount() > 1) {
|
||||
wikipediaButtonText.setVisibility(View.GONE);
|
||||
wikidataButtonText.setVisibility(View.GONE);
|
||||
commonsButtonText.setVisibility(View.GONE);
|
||||
directionsButtonText.setVisibility(View.GONE);
|
||||
}
|
||||
}
|
||||
|
||||
private void setupMapView(Bundle savedInstanceState) {
|
||||
MapboxMapOptions options = new MapboxMapOptions()
|
||||
.styleUrl(Style.OUTDOORS)
|
||||
|
|
@ -86,21 +419,13 @@ public class NearbyMapFragment extends android.support.v4.app.Fragment {
|
|||
// create map
|
||||
mapView = new MapView(getActivity(), options);
|
||||
mapView.onCreate(savedInstanceState);
|
||||
mapView.getMapAsync(mapboxMap -> {
|
||||
mapboxMap.addMarkers(baseMarkerOptions);
|
||||
|
||||
mapboxMap.setOnMarkerClickListener(marker -> {
|
||||
if (marker instanceof NearbyMarker) {
|
||||
NearbyMarker nearbyMarker = (NearbyMarker) marker;
|
||||
Place place = nearbyMarker.getNearbyBaseMarker().getPlace();
|
||||
NearbyInfoDialog.showYourself(getActivity(), place);
|
||||
}
|
||||
return false;
|
||||
});
|
||||
|
||||
addCurrentLocationMarker(mapboxMap);
|
||||
mapView.getMapAsync(new OnMapReadyCallback() {
|
||||
@Override
|
||||
public void onMapReady(MapboxMap mapboxMap) {
|
||||
NearbyMapFragment.this.mapboxMap = mapboxMap;
|
||||
updateMapSignificantly();
|
||||
}
|
||||
});
|
||||
|
||||
mapView.setStyleUrl("asset://mapstyle.json");
|
||||
}
|
||||
|
||||
|
|
@ -109,23 +434,67 @@ public class NearbyMapFragment extends android.support.v4.app.Fragment {
|
|||
* circle which uses the accuracy * 2, to draw a circle
|
||||
* which represents the user's position with an accuracy
|
||||
* of 95%.
|
||||
*
|
||||
* Should be called only on creation of mapboxMap, there
|
||||
* is other method to update markers location with users
|
||||
* move.
|
||||
*/
|
||||
private void addCurrentLocationMarker(MapboxMap mapboxMap) {
|
||||
MarkerOptions currentLocationMarker = new MarkerOptions()
|
||||
if (currentLocationMarker != null) {
|
||||
currentLocationMarker.remove(); // Remove previous marker, we are not Hansel and Gretel
|
||||
}
|
||||
|
||||
Icon icon = IconFactory.getInstance(getContext()).fromResource(R.drawable.current_location_marker);
|
||||
|
||||
MarkerOptions currentLocationMarkerOptions = new MarkerOptions()
|
||||
.position(new LatLng(curLatLng.getLatitude(), curLatLng.getLongitude()));
|
||||
mapboxMap.addMarker(currentLocationMarker);
|
||||
currentLocationMarkerOptions.setIcon(icon); // Set custom icon
|
||||
|
||||
currentLocationMarker = mapboxMap.addMarker(currentLocationMarkerOptions);
|
||||
|
||||
List<LatLng> circle = createCircleArray(curLatLng.getLatitude(), curLatLng.getLongitude(),
|
||||
curLatLng.getAccuracy() * 2, 100);
|
||||
|
||||
mapboxMap.addPolygon(
|
||||
new PolygonOptions()
|
||||
.addAll(circle)
|
||||
.strokeColor(Color.parseColor("#55000000"))
|
||||
.fillColor(Color.parseColor("#11000000"))
|
||||
);
|
||||
currentLocationPolygonOptions = new PolygonOptions()
|
||||
.addAll(circle)
|
||||
.strokeColor(Color.parseColor("#55000000"))
|
||||
.fillColor(Color.parseColor("#11000000"));
|
||||
mapboxMap.addPolygon(currentLocationPolygonOptions);
|
||||
}
|
||||
|
||||
private void addNearbyMarkerstoMapBoxMap() {
|
||||
|
||||
mapboxMap.addMarkers(baseMarkerOptions);
|
||||
mapboxMap.setOnInfoWindowCloseListener(marker -> {
|
||||
if (marker == selected) {
|
||||
bottomSheetDetailsBehavior.setState(BottomSheetBehavior.STATE_HIDDEN);
|
||||
}
|
||||
});
|
||||
mapView.getMapAsync(mapboxMap -> {
|
||||
mapboxMap.addMarkers(baseMarkerOptions);
|
||||
fabRecenter.setVisibility(View.VISIBLE);
|
||||
mapboxMap.setOnInfoWindowCloseListener(marker -> {
|
||||
if (marker == selected) {
|
||||
bottomSheetDetailsBehavior.setState(BottomSheetBehavior.STATE_HIDDEN);
|
||||
}
|
||||
});
|
||||
|
||||
mapboxMap.setOnMarkerClickListener(marker -> {
|
||||
if (marker instanceof NearbyMarker) {
|
||||
this.selected = marker;
|
||||
NearbyMarker nearbyMarker = (NearbyMarker) marker;
|
||||
Place place = nearbyMarker.getNearbyBaseMarker().getPlace();
|
||||
passInfoToSheet(place);
|
||||
bottomSheetListBehavior.setState(BottomSheetBehavior.STATE_HIDDEN);
|
||||
bottomSheetDetailsBehavior.setState(BottomSheetBehavior.STATE_COLLAPSED);
|
||||
}
|
||||
return false;
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Creates a series of points that create a circle on the map.
|
||||
* Takes the center latitude, center longitude of the circle,
|
||||
|
|
@ -147,10 +516,230 @@ public class NearbyMapFragment extends android.support.v4.app.Fragment {
|
|||
double nodeLatitude = centerLat + radiusLat * Math.sin(theta);
|
||||
circle.add(new LatLng(nodeLatitude, nodeLongitude));
|
||||
}
|
||||
|
||||
return circle;
|
||||
}
|
||||
|
||||
public void prepareViewsForSheetPosition(int bottomSheetState) {
|
||||
|
||||
switch (bottomSheetState) {
|
||||
case (BottomSheetBehavior.STATE_COLLAPSED):
|
||||
closeFabs(isFabOpen);
|
||||
if (!fabPlus.isShown()) showFAB();
|
||||
this.getView().requestFocus();
|
||||
break;
|
||||
case (BottomSheetBehavior.STATE_EXPANDED):
|
||||
this.getView().requestFocus();
|
||||
break;
|
||||
case (BottomSheetBehavior.STATE_HIDDEN):
|
||||
mapView.getMapAsync(MapboxMap::deselectMarkers);
|
||||
transparentView.setClickable(false);
|
||||
transparentView.setAlpha(0);
|
||||
closeFabs(isFabOpen);
|
||||
hideFAB();
|
||||
this.getView().requestFocus();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void hideFAB() {
|
||||
|
||||
removeAnchorFromFABs(fabPlus);
|
||||
fabPlus.hide();
|
||||
|
||||
removeAnchorFromFABs(fabCamera);
|
||||
fabCamera.hide();
|
||||
|
||||
removeAnchorFromFABs(fabGallery);
|
||||
fabGallery.hide();
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
* We are not able to hide FABs without removing anchors, this method removes anchors
|
||||
* */
|
||||
private void removeAnchorFromFABs(FloatingActionButton floatingActionButton) {
|
||||
//get rid of anchors
|
||||
//Somehow this was the only way https://stackoverflow.com/questions/32732932
|
||||
// /floatingactionbutton-visible-for-sometime-even-if-visibility-is-set-to-gone
|
||||
CoordinatorLayout.LayoutParams param = (CoordinatorLayout.LayoutParams) floatingActionButton
|
||||
.getLayoutParams();
|
||||
param.setAnchorId(View.NO_ID);
|
||||
// If we don't set them to zero, then they become visible for a moment on upper left side
|
||||
param.width = 0;
|
||||
param.height = 0;
|
||||
floatingActionButton.setLayoutParams(param);
|
||||
}
|
||||
|
||||
private void showFAB() {
|
||||
|
||||
addAnchorToBigFABs(fabPlus, getActivity().findViewById(R.id.bottom_sheet_details).getId());
|
||||
fabPlus.show();
|
||||
|
||||
addAnchorToSmallFABs(fabGallery, getActivity().findViewById(R.id.empty_view).getId());
|
||||
|
||||
addAnchorToSmallFABs(fabCamera, getActivity().findViewById(R.id.empty_view1).getId());
|
||||
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Add amnchors back before making them visible again.
|
||||
* */
|
||||
private void addAnchorToBigFABs(FloatingActionButton floatingActionButton, int anchorID) {
|
||||
CoordinatorLayout.LayoutParams params = new CoordinatorLayout.LayoutParams
|
||||
(ViewGroup.LayoutParams.WRAP_CONTENT,ViewGroup.LayoutParams.WRAP_CONTENT);
|
||||
params.setAnchorId(anchorID);
|
||||
params.anchorGravity = Gravity.TOP|Gravity.RIGHT|Gravity.END;
|
||||
floatingActionButton.setLayoutParams(params);
|
||||
}
|
||||
|
||||
/*
|
||||
* Add amnchors back before making them visible again. Big and small fabs have different anchor
|
||||
* gravities, therefore the are two methods.
|
||||
* */
|
||||
private void addAnchorToSmallFABs(FloatingActionButton floatingActionButton, int anchorID) {
|
||||
CoordinatorLayout.LayoutParams params = new CoordinatorLayout.LayoutParams
|
||||
(ViewGroup.LayoutParams.WRAP_CONTENT,ViewGroup.LayoutParams.WRAP_CONTENT);
|
||||
params.setAnchorId(anchorID);
|
||||
params.anchorGravity = Gravity.CENTER_HORIZONTAL;
|
||||
floatingActionButton.setLayoutParams(params);
|
||||
}
|
||||
|
||||
private void passInfoToSheet(Place place) {
|
||||
this.place = place;
|
||||
wikipediaButton.setEnabled(place.hasWikipediaLink());
|
||||
wikipediaButton.setOnClickListener(view -> openWebView(place.siteLinks.getWikipediaLink()));
|
||||
|
||||
wikidataButton.setEnabled(place.hasWikidataLink());
|
||||
wikidataButton.setOnClickListener(view -> openWebView(place.siteLinks.getWikidataLink()));
|
||||
|
||||
directionsButton.setOnClickListener(view -> {
|
||||
//Open map app at given position
|
||||
Intent mapIntent = new Intent(Intent.ACTION_VIEW, place.location.getGmmIntentUri());
|
||||
if (mapIntent.resolveActivity(getActivity().getPackageManager()) != null) {
|
||||
startActivity(mapIntent);
|
||||
}
|
||||
});
|
||||
|
||||
commonsButton.setEnabled(place.hasCommonsLink());
|
||||
commonsButton.setOnClickListener(view -> openWebView(place.siteLinks.getCommonsLink()));
|
||||
|
||||
icon.setImageResource(place.getLabel().getIcon());
|
||||
|
||||
title.setText(place.name);
|
||||
distance.setText(place.distance);
|
||||
description.setText(place.getLongDescription());
|
||||
title.setText(place.name.toString());
|
||||
distance.setText(place.distance.toString());
|
||||
|
||||
fabCamera.setOnClickListener(view -> {
|
||||
if (fabCamera.isShown()) {
|
||||
Timber.d("Camera button tapped. Image title: " + place.getName() + "Image desc: " + place.getLongDescription());
|
||||
controller = new ContributionController(this);
|
||||
|
||||
DirectUpload directUpload = new DirectUpload(this, controller);
|
||||
storeSharedPrefs();
|
||||
directUpload.initiateCameraUpload();
|
||||
}
|
||||
});
|
||||
|
||||
fabGallery.setOnClickListener(view -> {
|
||||
if (fabGallery.isShown()) {
|
||||
Timber.d("Gallery button tapped. Image title: " + place.getName() + "Image desc: " + place.getLongDescription());
|
||||
controller = new ContributionController(this);
|
||||
|
||||
DirectUpload directUpload = new DirectUpload(this, controller);
|
||||
storeSharedPrefs();
|
||||
directUpload.initiateGalleryUpload();
|
||||
|
||||
//TODO: App crashes after image upload completes
|
||||
//TODO: Handle onRequestPermissionsResult
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void storeSharedPrefs() {
|
||||
SharedPreferences.Editor editor = directPrefs.edit();
|
||||
editor.putString("Title", place.getName());
|
||||
editor.putString("Desc", place.getLongDescription());
|
||||
editor.putString("Category", place.getCategory());
|
||||
editor.apply();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
|
||||
Timber.d("onRequestPermissionsResult: req code = " + " perm = " + permissions + " grant =" + grantResults);
|
||||
|
||||
switch (requestCode) {
|
||||
// 1 = "Read external storage" allowed when gallery selected
|
||||
case 1: {
|
||||
if (grantResults.length > 0 && grantResults[0] == PERMISSION_GRANTED) {
|
||||
Timber.d("Call controller.startGalleryPick()");
|
||||
controller.startGalleryPick();
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
// 3 = "Write external storage" allowed when camera selected
|
||||
case 3: {
|
||||
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
|
||||
Timber.d("Call controller.startCameraCapture()");
|
||||
controller.startCameraCapture();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||
super.onActivityResult(requestCode, resultCode, data);
|
||||
|
||||
if (resultCode == RESULT_OK) {
|
||||
Timber.d("OnActivityResult() parameters: Req code: %d Result code: %d Data: %s",
|
||||
requestCode, resultCode, data);
|
||||
controller.handleImagePicked(requestCode, data, true);
|
||||
} else {
|
||||
Timber.e("OnActivityResult() parameters: Req code: %d Result code: %d Data: %s",
|
||||
requestCode, resultCode, data);
|
||||
}
|
||||
}
|
||||
|
||||
private void openWebView(Uri link) {
|
||||
Intent browserIntent = new Intent(Intent.ACTION_VIEW, link);
|
||||
startActivity(browserIntent);
|
||||
}
|
||||
|
||||
private void animateFAB(boolean isFabOpen) {
|
||||
this.isFabOpen = !isFabOpen;
|
||||
if (fabPlus.isShown()){
|
||||
if (isFabOpen) {
|
||||
fabPlus.startAnimation(rotate_backward);
|
||||
fabCamera.startAnimation(fab_close);
|
||||
fabGallery.startAnimation(fab_close);
|
||||
fabCamera.hide();
|
||||
fabGallery.hide();
|
||||
} else {
|
||||
fabPlus.startAnimation(rotate_forward);
|
||||
fabCamera.startAnimation(fab_open);
|
||||
fabGallery.startAnimation(fab_open);
|
||||
fabCamera.show();
|
||||
fabGallery.show();
|
||||
}
|
||||
this.isFabOpen=!isFabOpen;
|
||||
}
|
||||
}
|
||||
|
||||
private void closeFabs ( boolean isFabOpen){
|
||||
if (isFabOpen) {
|
||||
fabPlus.startAnimation(rotate_backward);
|
||||
fabCamera.startAnimation(fab_close);
|
||||
fabGallery.startAnimation(fab_close);
|
||||
fabCamera.hide();
|
||||
fabGallery.hide();
|
||||
this.isFabOpen = !isFabOpen;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStart() {
|
||||
if (mapView != null) {
|
||||
|
|
@ -169,10 +758,14 @@ public class NearbyMapFragment extends android.support.v4.app.Fragment {
|
|||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
if (mapView != null) {
|
||||
mapView.onResume();
|
||||
}
|
||||
super.onResume();
|
||||
initViews();
|
||||
setListeners();
|
||||
transparentView.setClickable(false);
|
||||
transparentView.setAlpha(0);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -190,4 +783,19 @@ public class NearbyMapFragment extends android.support.v4.app.Fragment {
|
|||
}
|
||||
super.onDestroyView();
|
||||
}
|
||||
|
||||
private static class LatLngEvaluator implements TypeEvaluator<LatLng> {
|
||||
// Method is used to interpolate the marker animation.
|
||||
private LatLng latLng = new LatLng();
|
||||
|
||||
@Override
|
||||
public LatLng evaluate(float fraction, LatLng startValue, LatLng endValue) {
|
||||
latLng.setLatitude(startValue.getLatitude()
|
||||
+ ((endValue.getLatitude() - startValue.getLatitude()) * fraction));
|
||||
latLng.setLongitude(startValue.getLongitude()
|
||||
+ ((endValue.getLongitude() - startValue.getLongitude()) * fraction));
|
||||
return latLng;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
package fr.free.nrw.commons.nearby;
|
||||
|
||||
import android.net.Uri;
|
||||
import android.os.StrictMode;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
|
|
@ -9,6 +8,7 @@ import java.io.InputStreamReader;
|
|||
import java.net.URL;
|
||||
import java.net.URLConnection;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
|
@ -30,7 +30,6 @@ public class NearbyPlaces {
|
|||
private static final Uri WIKIDATA_QUERY_UI_URL = Uri.parse("https://query.wikidata.org/");
|
||||
private final String wikidataQuery;
|
||||
private double radius = INITIAL_RADIUS;
|
||||
private List<Place> places;
|
||||
|
||||
public NearbyPlaces() {
|
||||
try {
|
||||
|
|
@ -102,13 +101,17 @@ public class NearbyPlaces {
|
|||
}
|
||||
|
||||
String[] fields = line.split("\t");
|
||||
Timber.v("Fields: " + Arrays.toString(fields));
|
||||
String point = fields[0];
|
||||
String wikiDataLink = Utils.stripLocalizedString(fields[1]);
|
||||
String name = Utils.stripLocalizedString(fields[2]);
|
||||
String type = Utils.stripLocalizedString(fields[4]);
|
||||
String icon = fields[5];
|
||||
String wikipediaSitelink = Utils.stripLocalizedString(fields[7]);
|
||||
String commonsSitelink = Utils.stripLocalizedString(fields[8]);
|
||||
String wikiDataLink = Utils.stripLocalizedString(fields[1]);
|
||||
String icon = fields[5];
|
||||
String category = Utils.stripLocalizedString(fields[9]);
|
||||
|
||||
Timber.v("Name: " + name + ", type: " + type + ", category: " + category + ", wikipediaSitelink: " + wikipediaSitelink + ", commonsSitelink: " + commonsSitelink);
|
||||
|
||||
double latitude;
|
||||
double longitude;
|
||||
|
|
@ -130,6 +133,7 @@ public class NearbyPlaces {
|
|||
type, // details
|
||||
Uri.parse(icon),
|
||||
new LatLng(latitude, longitude, 0),
|
||||
category,
|
||||
new Sitelinks.Builder()
|
||||
.setWikipediaLink(wikipediaSitelink)
|
||||
.setCommonsLink(commonsSitelink)
|
||||
|
|
@ -141,66 +145,4 @@ public class NearbyPlaces {
|
|||
|
||||
return places;
|
||||
}
|
||||
|
||||
List<Place> getFromWikiNeedsPictures() {
|
||||
if (places != null) {
|
||||
return places;
|
||||
} else {
|
||||
try {
|
||||
places = new ArrayList<>();
|
||||
StrictMode.ThreadPolicy policy
|
||||
= new StrictMode.ThreadPolicy.Builder().permitAll().build();
|
||||
StrictMode.setThreadPolicy(policy);
|
||||
|
||||
URL file = new URL("https://tools.wmflabs.org/wiki-needs-pictures/data/data.csv");
|
||||
|
||||
BufferedReader in = new BufferedReader(new InputStreamReader(file.openStream()));
|
||||
|
||||
boolean firstLine = true;
|
||||
String line;
|
||||
Timber.d("Reading from CSV file...");
|
||||
|
||||
while ((line = in.readLine()) != null) {
|
||||
|
||||
// Skip CSV header.
|
||||
if (firstLine) {
|
||||
firstLine = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
String[] fields = line.split(",");
|
||||
String name = Utils.stripLocalizedString(fields[0]);
|
||||
|
||||
double latitude;
|
||||
double longitude;
|
||||
try {
|
||||
latitude = Double.parseDouble(fields[1]);
|
||||
} catch (NumberFormatException e) {
|
||||
latitude = 0;
|
||||
}
|
||||
try {
|
||||
longitude = Double.parseDouble(fields[2]);
|
||||
} catch (NumberFormatException e) {
|
||||
longitude = 0;
|
||||
}
|
||||
|
||||
String type = fields[3];
|
||||
|
||||
places.add(new Place(
|
||||
name,
|
||||
Place.Label.fromText(type), // list
|
||||
type, // details
|
||||
null,
|
||||
new LatLng(latitude, longitude, 0),
|
||||
new Sitelinks.Builder().build()
|
||||
));
|
||||
}
|
||||
in.close();
|
||||
|
||||
} catch (IOException e) {
|
||||
Timber.d(e.toString());
|
||||
}
|
||||
}
|
||||
return places;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ public class Place {
|
|||
private final String longDescription;
|
||||
private final Uri secondaryImageUrl;
|
||||
public final LatLng location;
|
||||
private final String category;
|
||||
|
||||
public Bitmap image;
|
||||
public Bitmap secondaryImage;
|
||||
|
|
@ -25,27 +26,42 @@ public class Place {
|
|||
|
||||
|
||||
public Place(String name, Label label, String longDescription,
|
||||
Uri secondaryImageUrl, LatLng location, Sitelinks siteLinks) {
|
||||
Uri secondaryImageUrl, LatLng location, String category, Sitelinks siteLinks) {
|
||||
this.name = name;
|
||||
this.label = label;
|
||||
this.longDescription = longDescription;
|
||||
this.secondaryImageUrl = secondaryImageUrl;
|
||||
this.location = location;
|
||||
this.category = category;
|
||||
this.siteLinks = siteLinks;
|
||||
}
|
||||
|
||||
public String getName() { return name; }
|
||||
|
||||
public Label getLabel() {
|
||||
return label;
|
||||
}
|
||||
|
||||
public String getLongDescription() {
|
||||
return longDescription;
|
||||
}
|
||||
public String getLongDescription() { return longDescription; }
|
||||
|
||||
public String getCategory() {return category; }
|
||||
|
||||
public void setDistance(String distance) {
|
||||
this.distance = distance;
|
||||
}
|
||||
|
||||
public boolean hasWikipediaLink() {
|
||||
return !(siteLinks == null || Uri.EMPTY.equals(siteLinks.getWikipediaLink()));
|
||||
}
|
||||
|
||||
public boolean hasWikidataLink() {
|
||||
return !(siteLinks == null || Uri.EMPTY.equals(siteLinks.getWikidataLink()));
|
||||
}
|
||||
|
||||
public boolean hasCommonsLink() {
|
||||
return !(siteLinks == null || Uri.EMPTY.equals(siteLinks.getCommonsLink()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (o instanceof Place) {
|
||||
|
|
|
|||
|
|
@ -1,32 +1,79 @@
|
|||
package fr.free.nrw.commons.nearby;
|
||||
|
||||
import android.support.annotation.NonNull;
|
||||
import android.content.Intent;
|
||||
|
||||
import android.net.Uri;
|
||||
import android.content.SharedPreferences;
|
||||
import android.support.v4.app.Fragment;
|
||||
import android.support.transition.TransitionManager;
|
||||
import android.support.v7.widget.PopupMenu;
|
||||
import android.util.Log;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.pedrogomez.renderers.Renderer;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Named;
|
||||
|
||||
import butterknife.BindView;
|
||||
import butterknife.ButterKnife;
|
||||
import fr.free.nrw.commons.CommonsApplication;
|
||||
import fr.free.nrw.commons.R;
|
||||
import fr.free.nrw.commons.contributions.ContributionController;
|
||||
import fr.free.nrw.commons.di.ApplicationlessInjection;
|
||||
import timber.log.Timber;
|
||||
|
||||
public class PlaceRenderer extends Renderer<Place> {
|
||||
|
||||
class PlaceRenderer extends Renderer<Place> {
|
||||
@BindView(R.id.tvName) TextView tvName;
|
||||
@BindView(R.id.tvDesc) TextView tvDesc;
|
||||
@BindView(R.id.distance) TextView distance;
|
||||
@BindView(R.id.icon) ImageView icon;
|
||||
private final PlaceClickedListener listener;
|
||||
@BindView(R.id.buttonLayout) LinearLayout buttonLayout;
|
||||
@BindView(R.id.cameraButton) LinearLayout cameraButton;
|
||||
|
||||
PlaceRenderer(@NonNull PlaceClickedListener listener) {
|
||||
this.listener = listener;
|
||||
@BindView(R.id.galleryButton) LinearLayout galleryButton;
|
||||
@BindView(R.id.directionsButton) LinearLayout directionsButton;
|
||||
@BindView(R.id.iconOverflow) LinearLayout iconOverflow;
|
||||
@BindView(R.id.cameraButtonText) TextView cameraButtonText;
|
||||
@BindView(R.id.galleryButtonText) TextView galleryButtonText;
|
||||
|
||||
@BindView(R.id.directionsButtonText) TextView directionsButtonText;
|
||||
@BindView(R.id.iconOverflowText) TextView iconOverflowText;
|
||||
|
||||
private View view;
|
||||
private static ArrayList<LinearLayout> openedItems;
|
||||
private Place place;
|
||||
|
||||
private Fragment fragment;
|
||||
private ContributionController controller;
|
||||
|
||||
|
||||
@Inject @Named("prefs") SharedPreferences prefs;
|
||||
@Inject @Named("direct_nearby_upload_prefs") SharedPreferences directPrefs;
|
||||
|
||||
public PlaceRenderer(){
|
||||
openedItems = new ArrayList<>();
|
||||
}
|
||||
|
||||
public PlaceRenderer(Fragment fragment, ContributionController controller) {
|
||||
this.fragment = fragment;
|
||||
this.controller = controller;
|
||||
openedItems = new ArrayList<>();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected View inflate(LayoutInflater layoutInflater, ViewGroup viewGroup) {
|
||||
return layoutInflater.inflate(R.layout.item_place, viewGroup, false);
|
||||
view = layoutInflater.inflate(R.layout.item_place, viewGroup, false);
|
||||
return view;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -36,15 +83,67 @@ class PlaceRenderer extends Renderer<Place> {
|
|||
|
||||
@Override
|
||||
protected void hookListeners(View view) {
|
||||
view.setOnClickListener(v -> listener.placeClicked(getContent()));
|
||||
|
||||
final View.OnClickListener listener = view12 -> {
|
||||
Log.d("Renderer", "clicked");
|
||||
TransitionManager.beginDelayedTransition(buttonLayout);
|
||||
|
||||
if(buttonLayout.isShown()){
|
||||
closeLayout(buttonLayout);
|
||||
}else {
|
||||
openLayout(buttonLayout);
|
||||
}
|
||||
|
||||
};
|
||||
view.setOnClickListener(listener);
|
||||
view.requestFocus();
|
||||
view.setOnFocusChangeListener((view1, hasFocus) -> {
|
||||
if (!hasFocus && buttonLayout.isShown()) {
|
||||
closeLayout(buttonLayout);
|
||||
} else if (hasFocus && !buttonLayout.isShown()) {
|
||||
listener.onClick(view1);
|
||||
}
|
||||
});
|
||||
|
||||
cameraButton.setOnClickListener(view2 -> {
|
||||
Timber.d("Camera button tapped. Image title: " + place.getName() + "Image desc: " + place.getLongDescription());
|
||||
DirectUpload directUpload = new DirectUpload(fragment, controller);
|
||||
storeSharedPrefs();
|
||||
directUpload.initiateCameraUpload();
|
||||
});
|
||||
|
||||
galleryButton.setOnClickListener(view3 -> {
|
||||
Timber.d("Gallery button tapped. Image title: " + place.getName() + "Image desc: " + place.getLongDescription());
|
||||
DirectUpload directUpload = new DirectUpload(fragment, controller);
|
||||
storeSharedPrefs();
|
||||
directUpload.initiateGalleryUpload();
|
||||
});
|
||||
}
|
||||
|
||||
private void storeSharedPrefs() {
|
||||
SharedPreferences.Editor editor = directPrefs.edit();
|
||||
Timber.d("directPrefs stored");
|
||||
editor.putString("Title", place.getName());
|
||||
editor.putString("Desc", place.getLongDescription());
|
||||
editor.putString("Category", place.getCategory());
|
||||
editor.apply();
|
||||
}
|
||||
|
||||
private void closeLayout(LinearLayout buttonLayout){
|
||||
buttonLayout.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
private void openLayout(LinearLayout buttonLayout){
|
||||
buttonLayout.setVisibility(View.VISIBLE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render() {
|
||||
Place place = getContent();
|
||||
ApplicationlessInjection.getInstance(getContext().getApplicationContext())
|
||||
.getCommonsApplicationComponent().inject(this);
|
||||
place = getContent();
|
||||
tvName.setText(place.name);
|
||||
String descriptionText = place.getLongDescription();
|
||||
tvDesc.setVisibility(View.VISIBLE);
|
||||
if (descriptionText.equals("?")) {
|
||||
descriptionText = getContext().getString(R.string.no_description_found);
|
||||
tvDesc.setVisibility(View.INVISIBLE);
|
||||
|
|
@ -52,9 +151,61 @@ class PlaceRenderer extends Renderer<Place> {
|
|||
tvDesc.setText(descriptionText);
|
||||
distance.setText(place.distance);
|
||||
icon.setImageResource(place.getLabel().getIcon());
|
||||
|
||||
directionsButton.setOnClickListener(view -> {
|
||||
//Open map app at given position
|
||||
Intent mapIntent = new Intent(Intent.ACTION_VIEW, place.location.getGmmIntentUri());
|
||||
if (mapIntent.resolveActivity(view.getContext().getPackageManager()) != null) {
|
||||
view.getContext().startActivity(mapIntent);
|
||||
}
|
||||
});
|
||||
|
||||
iconOverflow.setVisibility(showMenu() ? View.VISIBLE : View.GONE);
|
||||
iconOverflow.setOnClickListener(v -> popupMenuListener());
|
||||
|
||||
}
|
||||
|
||||
interface PlaceClickedListener {
|
||||
void placeClicked(Place place);
|
||||
private void popupMenuListener() {
|
||||
PopupMenu popupMenu = new PopupMenu(view.getContext(), iconOverflow);
|
||||
popupMenu.inflate(R.menu.nearby_info_dialog_options);
|
||||
|
||||
MenuItem commonsArticle = popupMenu.getMenu()
|
||||
.findItem(R.id.nearby_info_menu_commons_article);
|
||||
MenuItem wikiDataArticle = popupMenu.getMenu()
|
||||
.findItem(R.id.nearby_info_menu_wikidata_article);
|
||||
MenuItem wikipediaArticle = popupMenu.getMenu()
|
||||
.findItem(R.id.nearby_info_menu_wikipedia_article);
|
||||
|
||||
commonsArticle.setEnabled(place.hasCommonsLink());
|
||||
wikiDataArticle.setEnabled(place.hasWikidataLink());
|
||||
wikipediaArticle.setEnabled(place.hasWikipediaLink());
|
||||
|
||||
popupMenu.setOnMenuItemClickListener(item -> {
|
||||
switch (item.getItemId()) {
|
||||
case R.id.nearby_info_menu_commons_article:
|
||||
openWebView(place.siteLinks.getCommonsLink());
|
||||
return true;
|
||||
case R.id.nearby_info_menu_wikidata_article:
|
||||
openWebView(place.siteLinks.getWikidataLink());
|
||||
return true;
|
||||
case R.id.nearby_info_menu_wikipedia_article:
|
||||
openWebView(place.siteLinks.getWikipediaLink());
|
||||
return true;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
popupMenu.show();
|
||||
}
|
||||
}
|
||||
|
||||
private void openWebView(Uri link) {
|
||||
Intent browserIntent = new Intent(Intent.ACTION_VIEW, link);
|
||||
view.getContext().startActivity(browserIntent);
|
||||
}
|
||||
|
||||
private boolean showMenu() {
|
||||
return place.hasCommonsLink() || place.hasWikidataLink();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -58,7 +58,7 @@ import fr.free.nrw.commons.modifications.ModificationsContentProvider;
|
|||
import fr.free.nrw.commons.modifications.ModifierSequence;
|
||||
import fr.free.nrw.commons.modifications.ModifierSequenceDao;
|
||||
import fr.free.nrw.commons.modifications.TemplateRemoveModifier;
|
||||
import fr.free.nrw.commons.mwapi.EventLog;
|
||||
|
||||
import fr.free.nrw.commons.utils.ImageUtils;
|
||||
import fr.free.nrw.commons.mwapi.MediaWikiApi;
|
||||
import timber.log.Timber;
|
||||
|
|
@ -116,7 +116,10 @@ public class ShareActivity
|
|||
private String description;
|
||||
private Snackbar snackbar;
|
||||
private boolean duplicateCheckPassed = false;
|
||||
|
||||
private boolean haveCheckedForOtherImages = false;
|
||||
private boolean isNearbyUpload = false;
|
||||
|
||||
/**
|
||||
* Called when user taps the submit button.
|
||||
*/
|
||||
|
|
@ -214,6 +217,10 @@ public class ShareActivity
|
|||
finish();
|
||||
}
|
||||
|
||||
protected boolean isNearbyUpload() {
|
||||
return isNearbyUpload;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
|
@ -240,6 +247,10 @@ public class ShareActivity
|
|||
} else {
|
||||
source = Contribution.SOURCE_EXTERNAL;
|
||||
}
|
||||
if (intent.hasExtra("isDirectUpload")) {
|
||||
Timber.d("This was initiated by a direct upload from Nearby");
|
||||
isNearbyUpload = true;
|
||||
}
|
||||
mimeType = intent.getType();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
package fr.free.nrw.commons.upload;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.app.Activity;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
|
|
@ -58,6 +60,7 @@ public class SingleUploadFragment extends CommonsDaggerSupportFragment {
|
|||
@BindView(R.id.licenseSpinner) Spinner licenseSpinner;
|
||||
|
||||
@Inject @Named("default_preferences") SharedPreferences prefs;
|
||||
@Inject @Named("direct_nearby_upload_prefs") SharedPreferences directPrefs;
|
||||
|
||||
private String license;
|
||||
private OnUploadActionInitiated uploadActionInitiatedHandler;
|
||||
|
|
@ -90,7 +93,6 @@ public class SingleUploadFragment extends CommonsDaggerSupportFragment {
|
|||
|
||||
uploadActionInitiatedHandler.uploadActionInitiated(title, desc);
|
||||
return true;
|
||||
|
||||
}
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
|
|
@ -118,6 +120,18 @@ public class SingleUploadFragment extends CommonsDaggerSupportFragment {
|
|||
|
||||
license = prefs.getString(Prefs.DEFAULT_LICENSE, Prefs.Licenses.CC_BY_SA_3);
|
||||
|
||||
// If this is a direct upload from Nearby, autofill title and desc fields with the Place's values
|
||||
boolean isNearbyUpload = ((ShareActivity) getActivity()).isNearbyUpload();
|
||||
|
||||
if (isNearbyUpload) {
|
||||
String imageTitle = directPrefs.getString("Title", "");
|
||||
String imageDesc = directPrefs.getString("Desc", "");
|
||||
String imageCats = directPrefs.getString("Category", "");
|
||||
Timber.d("Image title: " + imageTitle + ", image desc: " + imageDesc + ", image categories: " + imageCats);
|
||||
titleEdit.setText(imageTitle);
|
||||
descEdit.setText(imageDesc);
|
||||
}
|
||||
|
||||
// check if this is the first time we have uploaded
|
||||
if (prefs.getString("Title", "").trim().length() == 0
|
||||
&& prefs.getString("Desc", "").trim().length() == 0) {
|
||||
|
|
@ -278,6 +292,7 @@ public class SingleUploadFragment extends CommonsDaggerSupportFragment {
|
|||
return false;
|
||||
}
|
||||
|
||||
@SuppressLint("StringFormatInvalid")
|
||||
private void setLicenseSummary(String license) {
|
||||
licenseSummaryView.setText(getString(R.string.share_license_summary, getString(Utils.licenseNameFor(license))));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -54,7 +54,7 @@ public class ImageUtils {
|
|||
|
||||
while ((checkImageRightPosition <= loadImageWidth) && (checkImageLeftPosition < checkImageRightPosition)) {
|
||||
while ((checkImageBottomPosition <= loadImageHeight) && (checkImageTopPosition < checkImageBottomPosition)) {
|
||||
Timber.d("left: " + checkImageLeftPosition + " right: " + checkImageRightPosition + " top: " + checkImageTopPosition + " bottom: " + checkImageBottomPosition);
|
||||
Timber.v("left: " + checkImageLeftPosition + " right: " + checkImageRightPosition + " top: " + checkImageTopPosition + " bottom: " + checkImageBottomPosition);
|
||||
|
||||
Rect rect = new Rect(checkImageLeftPosition,checkImageTopPosition,checkImageRightPosition,checkImageBottomPosition);
|
||||
totalDividedRectangles++;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue