#3468 Switch from RvRenderer to AdapterDelegates - replace PlaceRenderer

This commit is contained in:
Sean Mac Gillicuddy 2020-05-14 07:23:37 +01:00
parent 456b86454e
commit 3b422963a4
16 changed files with 430 additions and 599 deletions

View file

@ -35,7 +35,6 @@ dependencies {
// UI
implementation 'fr.avianey.com.viewpagerindicator:library:2.4.1.1@aar'
implementation 'com.github.chrisbanes:PhotoView:2.0.0'
implementation 'com.github.pedrovgs:renderers:3.3.3'
implementation 'com.mapbox.mapboxsdk:mapbox-android-sdk:8.6.2'
implementation 'com.mapbox.mapboxsdk:mapbox-android-plugin-localization-v8:0.11.0'
implementation 'com.mapbox.mapboxsdk:mapbox-android-plugin-scalebar-v9:0.4.0'
@ -105,6 +104,7 @@ dependencies {
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
implementation "androidx.exifinterface:exifinterface:1.0.0"
implementation "androidx.core:core-ktx:$CORE_KTX_VERSION"
implementation "androidx.multidex:multidex:2.0.1"
//swipe_layout
implementation 'com.daimajia.swipelayout:library:1.2.0@aar'
@ -143,7 +143,7 @@ android {
testOptions {
execution 'ANDROIDX_TEST_ORCHESTRATOR'
}
multiDexEnabled true
vectorDrawables.useSupportLibrary = true
}

View file

@ -9,7 +9,6 @@ import static org.acra.ReportField.STACK_TRACE;
import static org.acra.ReportField.USER_COMMENT;
import android.annotation.SuppressLint;
import android.app.Application;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.content.Context;
@ -18,13 +17,13 @@ import android.os.Build;
import android.os.Process;
import android.util.Log;
import androidx.annotation.NonNull;
import androidx.multidex.MultiDexApplication;
import com.facebook.drawee.backends.pipeline.Fresco;
import com.facebook.imagepipeline.core.ImagePipeline;
import com.facebook.imagepipeline.core.ImagePipelineConfig;
import com.mapbox.mapboxsdk.Mapbox;
import com.squareup.leakcanary.LeakCanary;
import com.squareup.leakcanary.RefWatcher;
import fr.free.nrw.commons.auth.SessionManager;
import fr.free.nrw.commons.bookmarks.locations.BookmarkLocationsDao;
import fr.free.nrw.commons.bookmarks.pictures.BookmarkPicturesDao;
@ -77,7 +76,7 @@ import timber.log.Timber;
resCommentPrompt = R.string.crash_dialog_comment_prompt
)
public class CommonsApplication extends Application {
public class CommonsApplication extends MultiDexApplication {
@Inject SessionManager sessionManager;
@Inject DBOpenHelper dbOpenHelper;

View file

@ -8,26 +8,21 @@ import android.view.ViewGroup;
import android.widget.ProgressBar;
import android.widget.RelativeLayout;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import com.pedrogomez.renderers.RVRendererAdapter;
import java.util.ArrayList;
import java.util.List;
import javax.inject.Inject;
import butterknife.BindView;
import butterknife.ButterKnife;
import dagger.android.support.DaggerFragment;
import fr.free.nrw.commons.R;
import fr.free.nrw.commons.contributions.ContributionController;
import fr.free.nrw.commons.nearby.NearbyAdapterFactory;
import fr.free.nrw.commons.nearby.Place;
import fr.free.nrw.commons.nearby.fragments.CommonPlaceClickActions;
import fr.free.nrw.commons.nearby.fragments.PlaceAdapter;
import java.util.List;
import javax.inject.Inject;
import kotlin.Unit;
public class BookmarkLocationsFragment extends DaggerFragment {
@ -37,8 +32,10 @@ public class BookmarkLocationsFragment extends DaggerFragment {
@BindView(R.id.parentLayout) RelativeLayout parentLayout;
@Inject BookmarkLocationsController controller;
private NearbyAdapterFactory adapterFactory;
@Inject ContributionController contributionController;
@Inject BookmarkLocationsDao bookmarkLocationDao;
@Inject CommonPlaceClickActions commonPlaceClickActions;
private PlaceAdapter adapter;
/**
* Create an instance of the fragment with the right bundle parameters
@ -56,7 +53,6 @@ public class BookmarkLocationsFragment extends DaggerFragment {
) {
View v = inflater.inflate(R.layout.fragment_bookmarks_locations, container, false);
ButterKnife.bind(this, v);
adapterFactory = new NearbyAdapterFactory(this, contributionController);
return v;
}
@ -65,7 +61,15 @@ public class BookmarkLocationsFragment extends DaggerFragment {
super.onViewCreated(view, savedInstanceState);
progressBar.setVisibility(View.VISIBLE);
recyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
recyclerView.setAdapter(adapterFactory.create(new ArrayList<>(), this::initList));
adapter = new PlaceAdapter(bookmarkLocationDao,
place -> Unit.INSTANCE,
(place, isBookmarked) -> {
adapter.remove(place);
return Unit.INSTANCE;
},
commonPlaceClickActions
);
recyclerView.setAdapter(adapter);
}
@Override
@ -79,7 +83,7 @@ public class BookmarkLocationsFragment extends DaggerFragment {
*/
private void initList() {
List<Place> places = controller.loadFavoritesLocations();
adapterFactory.updateAdapterData(places, (RVRendererAdapter<Place>) recyclerView.getAdapter());
adapter.setItems(places);
progressBar.setVisibility(View.GONE);
if (places.size() <= 0) {
statusTextView.setText(R.string.bookmark_empty);

View file

@ -0,0 +1,12 @@
package fr.free.nrw.commons.di
import android.app.Activity
import dagger.Module
import dagger.Provides
import fr.free.nrw.commons.bookmarks.locations.BookmarkLocationsFragment
@Module
class BookmarkLocationsFragmentModule {
@Provides
fun BookmarkLocationsFragment.providesActivity(): Activity = activity!!
}

View file

@ -10,11 +10,9 @@ import dagger.android.AndroidInjector;
import dagger.android.support.AndroidSupportInjectionModule;
import fr.free.nrw.commons.CommonsApplication;
import fr.free.nrw.commons.auth.LoginActivity;
import fr.free.nrw.commons.contributions.ContributionViewHolder;
import fr.free.nrw.commons.contributions.ContributionsModule;
import fr.free.nrw.commons.depictions.DepictionModule;
import fr.free.nrw.commons.explore.SearchModule;
import fr.free.nrw.commons.nearby.PlaceRenderer;
import fr.free.nrw.commons.review.ReviewController;
import fr.free.nrw.commons.settings.SettingsFragment;
import fr.free.nrw.commons.upload.FileProcessor;
@ -49,8 +47,6 @@ public interface CommonsApplicationComponent extends AndroidInjector<Application
@Override
void inject(ApplicationlessInjection instance);
void inject(PlaceRenderer placeRenderer);
void inject(FileProcessor fileProcessor);
void inject(PicOfDayAppWidget picOfDayAppWidget);

View file

@ -72,13 +72,13 @@ public abstract class FragmentBuilderModule {
@ContributesAndroidInjector
abstract ContributionsFragment bindContributionsFragment();
@ContributesAndroidInjector
@ContributesAndroidInjector(modules = NearbyParentFragmentModule.class)
abstract NearbyParentFragment bindNearbyParentFragment();
@ContributesAndroidInjector
abstract BookmarkPicturesFragment bindBookmarkPictureListFragment();
@ContributesAndroidInjector
@ContributesAndroidInjector(modules = BookmarkLocationsFragmentModule.class)
abstract BookmarkLocationsFragment bindBookmarkLocationListFragment();
@ContributesAndroidInjector

View file

@ -0,0 +1,13 @@
package fr.free.nrw.commons.di
import android.app.Activity
import dagger.Module
import dagger.Provides
import fr.free.nrw.commons.nearby.fragments.NearbyParentFragment
@Module
class NearbyParentFragmentModule{
@Provides
fun NearbyParentFragment.providesActivity(): Activity = activity!!
}

View file

@ -1,57 +0,0 @@
package fr.free.nrw.commons.nearby;
import androidx.fragment.app.Fragment;
import com.pedrogomez.renderers.ListAdapteeCollection;
import com.pedrogomez.renderers.RVRendererAdapter;
import com.pedrogomez.renderers.RendererBuilder;
import java.util.Collections;
import java.util.List;
import fr.free.nrw.commons.contributions.ContributionController;
public class NearbyAdapterFactory {
private Fragment fragment;
private ContributionController controller;
public NearbyAdapterFactory(Fragment fragment, ContributionController controller) {
this.fragment = fragment;
this.controller = controller;
}
public RVRendererAdapter<Place> create(List<Place> placeList) {
return create(placeList, null);
}
public RVRendererAdapter<Place> create(
List<Place> placeList,
PlaceRenderer.OnBookmarkClick onBookmarkClick
) {
RendererBuilder<Place> builder = new RendererBuilder<Place>()
.bind(Place.class, new PlaceRenderer(fragment, controller, onBookmarkClick));
ListAdapteeCollection<Place> collection = new ListAdapteeCollection<>(
placeList != null ? placeList : Collections.emptyList());
return new RVRendererAdapter<>(builder, collection);
}
public void updateAdapterData(List<Place> newPlaceList, RVRendererAdapter<Place> rendererAdapter) {
rendererAdapter.notifyDataSetChanged();
rendererAdapter.diffUpdate(newPlaceList);
}
public void clear(RVRendererAdapter<Place> rendererAdapter){
rendererAdapter.clear();
}
public void add(Place place, RVRendererAdapter<Place> rendererAdapter){
rendererAdapter.add(place);
}
public void update(RVRendererAdapter<Place> rendererAdapter){
rendererAdapter.notifyDataSetChanged();
}
}

View file

@ -0,0 +1,84 @@
package fr.free.nrw.commons.nearby
import android.view.View
import android.view.View.*
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import androidx.transition.TransitionManager
import com.hannesdorfmann.adapterdelegates4.dsl.AdapterDelegateLayoutContainerViewHolder
import com.hannesdorfmann.adapterdelegates4.dsl.adapterDelegateLayoutContainer
import fr.free.nrw.commons.R
import fr.free.nrw.commons.bookmarks.locations.BookmarkLocationsDao
import kotlinx.android.synthetic.main.item_place.*
import kotlinx.android.synthetic.main.nearby_row_button.*
fun placeAdapterDelegate(
bookmarkLocationDao: BookmarkLocationsDao,
onItemClick: ((Place) -> Unit)? = null,
onCameraClicked: (Place) -> Unit,
onGalleryClicked: (Place) -> Unit,
onBookmarkClicked: (Place, Boolean) -> Unit,
onOverflowIconClicked: (Place, View) -> Unit,
onDirectionsClicked: (Place) -> Unit
) =
adapterDelegateLayoutContainer<Place, Place>(R.layout.item_place) {
containerView.setOnClickListener { _: View? ->
showOrHideAndScrollToIfLast()
onItemClick?.invoke(item)
}
containerView.setOnFocusChangeListener { view1: View?, hasFocus: Boolean ->
if (!hasFocus && buttonLayout.isShown) {
buttonLayout.visibility = GONE
} else if (hasFocus && !buttonLayout.isShown) {
showOrHideAndScrollToIfLast()
onItemClick?.invoke(item)
}
}
cameraButton.setOnClickListener { onCameraClicked(item) }
galleryButton.setOnClickListener { onGalleryClicked(item) }
bookmarkRowButton.setOnClickListener {
val isBookmarked = bookmarkLocationDao.updateBookmarkLocation(item)
bookmarkRowButtonImage.setImageResource(if (isBookmarked) R.drawable.ic_round_star_filled_24px else R.drawable.ic_round_star_border_24px)
onBookmarkClicked(item, isBookmarked)
}
iconOverflow.setOnClickListener { onOverflowIconClicked(item, it) }
directionsButton.setOnClickListener { onDirectionsClicked(item) }
bind {
tvName.text = item.name
val descriptionText: String = item.longDescription
if (descriptionText == "?") {
tvDesc.setText(R.string.no_description_found)
tvDesc.visibility = INVISIBLE
} else {
tvDesc.text = descriptionText
}
distance.text = item.distance
icon.setImageResource(item.label.icon)
iconOverflow.visibility =
if (item.hasCommonsLink() || item.hasWikidataLink()) VISIBLE
else GONE
bookmarkRowButtonImage.setImageResource(
if (bookmarkLocationDao.findBookmarkLocation(item))
R.drawable.ic_round_star_filled_24px
else
R.drawable.ic_round_star_border_24px
)
}
}
private fun AdapterDelegateLayoutContainerViewHolder<Place>.showOrHideAndScrollToIfLast() {
TransitionManager.beginDelayedTransition(buttonLayout)
if (buttonLayout.isShown) {
buttonLayout.visibility = GONE
} else {
buttonLayout.visibility = VISIBLE
val recyclerView = containerView.parent as RecyclerView
val lastPosition = recyclerView.adapter!!.itemCount - 1
if (recyclerView.getChildLayoutPosition(containerView) == lastPosition) {
(recyclerView.layoutManager as LinearLayoutManager?)
?.scrollToPositionWithOffset(lastPosition, buttonLayout.height)
}
}
}

View file

@ -1,292 +0,0 @@
package fr.free.nrw.commons.nearby;
import android.content.Intent;
import android.net.Uri;
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 androidx.appcompat.app.AlertDialog;
import androidx.appcompat.widget.PopupMenu;
import androidx.fragment.app.Fragment;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import androidx.transition.TransitionManager;
import com.facebook.drawee.view.SimpleDraweeView;
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.R;
import fr.free.nrw.commons.Utils;
import fr.free.nrw.commons.auth.LoginActivity;
import fr.free.nrw.commons.bookmarks.locations.BookmarkLocationsDao;
import fr.free.nrw.commons.contributions.ContributionController;
import fr.free.nrw.commons.di.ApplicationlessInjection;
import fr.free.nrw.commons.kvstore.JsonKvStore;
import fr.free.nrw.commons.nearby.fragments.NearbyParentFragment;
import timber.log.Timber;
import static fr.free.nrw.commons.theme.NavigationBaseActivity.startActivityWithFlags;
import static fr.free.nrw.commons.wikidata.WikidataConstants.PLACE_OBJECT;
public 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) SimpleDraweeView icon;
@BindView(R.id.buttonLayout) LinearLayout buttonLayout;
@BindView(R.id.cameraButton) LinearLayout cameraButton;
@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.bookmarkRowButton) LinearLayout bookmarkButton;
@BindView(R.id.bookmarkButtonText) TextView bookmarkButtonText;
@BindView(R.id.bookmarkRowButtonImage) ImageView bookmarkButtonImage;
@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;
private OnBookmarkClick onBookmarkClick;
@Inject BookmarkLocationsDao bookmarkLocationDao;
@Inject
@Named("default_preferences")
JsonKvStore applicationKvStore;
public PlaceRenderer(){
openedItems = new ArrayList<>();
}
public PlaceRenderer(
Fragment fragment,
ContributionController controller,
OnBookmarkClick onBookmarkClick
) {
this.fragment = fragment;
this.controller = controller;
openedItems = new ArrayList<>();
this.onBookmarkClick = onBookmarkClick;
}
@Override
protected View inflate(LayoutInflater layoutInflater, ViewGroup viewGroup) {
view = layoutInflater.inflate(R.layout.item_place, viewGroup, false);
return view;
}
@Override
protected void setUpView(View view) {
ButterKnife.bind(this, view);
closeLayout(buttonLayout);
}
@Override
protected void hookListeners(View view) {
final View.OnClickListener listener = view12 -> {
Timber.d("Renderer clicked");
TransitionManager.beginDelayedTransition(buttonLayout);
if (buttonLayout.isShown()) {
closeLayout(buttonLayout);
} else {
openLayout(buttonLayout);
RecyclerView recyclerView = (RecyclerView) view.getParent();
int lastPosition = recyclerView.getAdapter().getItemCount() - 1;
if (recyclerView.getChildLayoutPosition(view) == lastPosition) {
((LinearLayoutManager) recyclerView.getLayoutManager()).scrollToPositionWithOffset(lastPosition, buttonLayout.getHeight());
}
}
if (onBookmarkClick == null) {
((NearbyParentFragment) fragment).centerMapToPlace(place);
}
};
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 -> {
if (applicationKvStore.getBoolean("login_skipped", false)) {
// prompt the user to login
new AlertDialog.Builder(getContext())
.setMessage(R.string.login_alert_message)
.setPositiveButton(R.string.login, (dialog, which) -> {
startActivityWithFlags( getContext(), LoginActivity.class, Intent.FLAG_ACTIVITY_CLEAR_TOP,
Intent.FLAG_ACTIVITY_SINGLE_TOP);
applicationKvStore.putBoolean("login_skipped", false);
fragment.getActivity().finish();
})
.show();
} else {
Timber.d("Camera button tapped. Image title: " + place.getName() + "Image desc: " + place.getLongDescription());
storeSharedPrefs();
controller.initiateCameraPick(fragment.getActivity());
}
});
galleryButton.setOnClickListener(view3 -> {
if (applicationKvStore.getBoolean("login_skipped", false)) {
// prompt the user to login
new AlertDialog.Builder(getContext())
.setMessage(R.string.login_alert_message)
.setPositiveButton(R.string.login, (dialog, which) -> {
startActivityWithFlags( getContext(), LoginActivity.class, Intent.FLAG_ACTIVITY_CLEAR_TOP,
Intent.FLAG_ACTIVITY_SINGLE_TOP);
applicationKvStore.putBoolean("login_skipped", false);
fragment.getActivity().finish();
})
.show();
}else {
Timber.d("Gallery button tapped. Image title: " + place.getName() + "Image desc: " + place.getLongDescription());
storeSharedPrefs();
controller.initiateGalleryPick(fragment.getActivity(), false);
}
});
bookmarkButton.setOnClickListener(view4 -> {
if (applicationKvStore.getBoolean("login_skipped", false)) {
// prompt the user to login
new AlertDialog.Builder(getContext())
.setMessage(R.string.login_alert_message)
.setPositiveButton(R.string.login, (dialog, which) -> {
startActivityWithFlags( getContext(), LoginActivity.class, Intent.FLAG_ACTIVITY_CLEAR_TOP,
Intent.FLAG_ACTIVITY_SINGLE_TOP);
applicationKvStore.putBoolean("login_skipped", false);
fragment.getActivity().finish();
})
.show();
} else {
boolean isBookmarked = bookmarkLocationDao.updateBookmarkLocation(place);
int icon = isBookmarked ? R.drawable.ic_round_star_filled_24px : R.drawable.ic_round_star_border_24px;
bookmarkButtonImage.setImageResource(icon);
if (onBookmarkClick != null) {
onBookmarkClick.onClick();
}
else {
((NearbyParentFragment) (fragment.getParentFragment())).
updateMarker(isBookmarked, place, null);
}
}
});
}
private void storeSharedPrefs() {
Timber.d("Store place object %s", place.toString());
applicationKvStore.putJson(PLACE_OBJECT, place);
}
private void closeLayout(LinearLayout buttonLayout){
buttonLayout.setVisibility(View.GONE);
}
private void openLayout(LinearLayout buttonLayout){
buttonLayout.setVisibility(View.VISIBLE);
}
@Override
public void render() {
ApplicationlessInjection.getInstance(getContext().getApplicationContext())
.getCommonsApplicationComponent().inject(this);
place = getContent();
tvName.setText(place.name);
String descriptionText = place.getLongDescription();
if (descriptionText.equals("?")) {
descriptionText = getContext().getString(R.string.no_description_found);
tvDesc.setVisibility(View.INVISIBLE);
}
tvDesc.setText(descriptionText);
distance.setText(place.distance);
icon.setImageResource(place.getLabel().getIcon());
directionsButton.setOnClickListener(view -> Utils.handleGeoCoordinates(getContext(), this.place.getLocation()));
iconOverflow.setVisibility(showMenu() ? View.VISIBLE : View.GONE);
iconOverflow.setOnClickListener(v -> popupMenuListener());
int icon;
if (bookmarkLocationDao.findBookmarkLocation(place)) {
icon = R.drawable.ic_round_star_filled_24px;
} else {
icon = R.drawable.ic_round_star_border_24px;
}
bookmarkButtonImage.setImageResource(icon);
}
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) {
Utils.handleWebUrl(getContext(), link);
}
private boolean showMenu() {
return place.hasCommonsLink() || place.hasWikidataLink();
}
public interface OnBookmarkClick {
void onClick();
}
}

View file

@ -0,0 +1,99 @@
package fr.free.nrw.commons.nearby.fragments
import android.app.Activity
import android.content.Intent
import android.net.Uri
import android.view.MenuItem
import android.view.View
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.widget.PopupMenu
import fr.free.nrw.commons.R
import fr.free.nrw.commons.Utils
import fr.free.nrw.commons.auth.LoginActivity
import fr.free.nrw.commons.contributions.ContributionController
import fr.free.nrw.commons.kvstore.JsonKvStore
import fr.free.nrw.commons.nearby.Place
import fr.free.nrw.commons.theme.NavigationBaseActivity
import fr.free.nrw.commons.wikidata.WikidataConstants
import timber.log.Timber
import javax.inject.Inject
import javax.inject.Named
class CommonPlaceClickActions @Inject constructor(
@Named("default_preferences") private val applicationKvStore: JsonKvStore,
private val activity: Activity,
private val contributionController: ContributionController
) {
fun onCameraClicked(): (Place) -> Unit = {
if (applicationKvStore.getBoolean("login_skipped", false)) {
showLoginDialog()
} else {
Timber.d("Camera button tapped. Image title: ${it.getName()}Image desc: ${it.longDescription}")
storeSharedPrefs(it)
contributionController.initiateCameraPick(activity)
}
}
fun onGalleryClicked(): (Place) -> Unit = {
if (applicationKvStore.getBoolean("login_skipped", false)) {
showLoginDialog()
} else {
Timber.d("Gallery button tapped. Image title: ${it.getName()}Image desc: ${it.getLongDescription()}")
storeSharedPrefs(it)
contributionController.initiateGalleryPick(activity, false)
}
}
fun onOverflowClicked(): (Place, View) -> Unit = { place, view ->
PopupMenu(view.context, view).apply {
inflate(R.menu.nearby_info_dialog_options)
enableBy(R.id.nearby_info_menu_commons_article, place.hasCommonsLink())
enableBy(R.id.nearby_info_menu_wikidata_article, place.hasWikidataLink())
enableBy(R.id.nearby_info_menu_wikipedia_article, place.hasWikipediaLink())
setOnMenuItemClickListener { item: MenuItem ->
when (item.itemId) {
R.id.nearby_info_menu_commons_article -> openWebView(place.siteLinks.commonsLink)
R.id.nearby_info_menu_wikidata_article -> openWebView(place.siteLinks.wikidataLink)
R.id.nearby_info_menu_wikipedia_article -> openWebView(place.siteLinks.wikipediaLink)
else -> false
}
}
}.show()
}
fun onDirectionsClicked(): (Place) -> Unit = {
Utils.handleGeoCoordinates(activity, it.getLocation())
}
private fun storeSharedPrefs(selectedPlace: Place) {
Timber.d("Store place object %s", selectedPlace.toString())
applicationKvStore.putJson(WikidataConstants.PLACE_OBJECT, selectedPlace)
}
private fun openWebView(link: Uri): Boolean {
Utils.handleWebUrl(activity, link)
return true;
}
private fun PopupMenu.enableBy(menuId: Int, hasLink: Boolean) {
menu.findItem(menuId).isEnabled = hasLink
}
private fun showLoginDialog() {
AlertDialog.Builder(activity)
.setMessage(R.string.login_alert_message)
.setPositiveButton(R.string.login) { dialog, which ->
NavigationBaseActivity.startActivityWithFlags(
activity,
LoginActivity::class.java,
Intent.FLAG_ACTIVITY_CLEAR_TOP,
Intent.FLAG_ACTIVITY_SINGLE_TOP
)
applicationKvStore.putBoolean("login_skipped", false)
activity.finish()
}
.show()
}
}

View file

@ -1,7 +1,14 @@
package fr.free.nrw.commons.nearby.fragments;
import static fr.free.nrw.commons.contributions.MainActivity.CONTRIBUTIONS_TAB_POSITION;
import static fr.free.nrw.commons.location.LocationServiceManager.LocationChangeType.LOCATION_SIGNIFICANTLY_CHANGED;
import static fr.free.nrw.commons.location.LocationServiceManager.LocationChangeType.LOCATION_SLIGHTLY_CHANGED;
import static fr.free.nrw.commons.location.LocationServiceManager.LocationChangeType.MAP_UPDATED;
import static fr.free.nrw.commons.nearby.Label.TEXT_TO_DESCRIPTION;
import static fr.free.nrw.commons.utils.LengthUtils.formatDistanceBetween;
import static fr.free.nrw.commons.wikidata.WikidataConstants.PLACE_OBJECT;
import android.Manifest;
import android.app.AlertDialog;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
@ -26,14 +33,16 @@ import android.widget.RelativeLayout;
import android.widget.SearchView;
import android.widget.TextView;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AlertDialog;
import androidx.fragment.app.Fragment;
import androidx.recyclerview.widget.DividerItemDecoration;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import androidx.vectordrawable.graphics.drawable.VectorDrawableCompat;
import butterknife.BindView;
import butterknife.ButterKnife;
import com.google.android.material.bottomsheet.BottomSheetBehavior;
import com.google.android.material.chip.Chip;
import com.google.android.material.chip.ChipGroup;
@ -57,18 +66,6 @@ import com.mapbox.mapboxsdk.maps.Style;
import com.mapbox.mapboxsdk.maps.UiSettings;
import com.mapbox.pluginscalebar.ScaleBarOptions;
import com.mapbox.pluginscalebar.ScaleBarPlugin;
import com.pedrogomez.renderers.RVRendererAdapter;
import fr.free.nrw.commons.utils.DialogUtil;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;
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.Utils;
@ -76,6 +73,7 @@ import fr.free.nrw.commons.auth.LoginActivity;
import fr.free.nrw.commons.bookmarks.locations.BookmarkLocationsDao;
import fr.free.nrw.commons.contributions.ContributionController;
import fr.free.nrw.commons.contributions.MainActivity;
import fr.free.nrw.commons.di.ApplicationlessInjection;
import fr.free.nrw.commons.di.CommonsDaggerSupportFragment;
import fr.free.nrw.commons.kvstore.JsonKvStore;
import fr.free.nrw.commons.location.LocationServiceManager;
@ -83,7 +81,6 @@ import fr.free.nrw.commons.location.LocationUpdateListener;
import fr.free.nrw.commons.nearby.CheckBoxTriStates;
import fr.free.nrw.commons.nearby.Label;
import fr.free.nrw.commons.nearby.MarkerPlaceGroup;
import fr.free.nrw.commons.nearby.NearbyAdapterFactory;
import fr.free.nrw.commons.nearby.NearbyBaseMarker;
import fr.free.nrw.commons.nearby.NearbyController;
import fr.free.nrw.commons.nearby.NearbyFilterSearchRecyclerViewAdapter;
@ -92,6 +89,7 @@ import fr.free.nrw.commons.nearby.NearbyMarker;
import fr.free.nrw.commons.nearby.Place;
import fr.free.nrw.commons.nearby.contract.NearbyParentFragmentContract;
import fr.free.nrw.commons.nearby.presenter.NearbyParentFragmentPresenter;
import fr.free.nrw.commons.utils.DialogUtil;
import fr.free.nrw.commons.utils.ExecutorUtils;
import fr.free.nrw.commons.utils.LayoutUtils;
import fr.free.nrw.commons.utils.LocationUtils;
@ -104,17 +102,16 @@ import fr.free.nrw.commons.utils.ViewUtil;
import fr.free.nrw.commons.wikidata.WikidataEditListener;
import io.reactivex.Observable;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.CompositeDisposable;
import io.reactivex.schedulers.Schedulers;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;
import javax.inject.Inject;
import javax.inject.Named;
import kotlin.Unit;
import timber.log.Timber;
import static fr.free.nrw.commons.contributions.MainActivity.CONTRIBUTIONS_TAB_POSITION;
import static fr.free.nrw.commons.location.LocationServiceManager.LocationChangeType.LOCATION_SIGNIFICANTLY_CHANGED;
import static fr.free.nrw.commons.location.LocationServiceManager.LocationChangeType.LOCATION_SLIGHTLY_CHANGED;
import static fr.free.nrw.commons.location.LocationServiceManager.LocationChangeType.MAP_UPDATED;
import static fr.free.nrw.commons.nearby.Label.TEXT_TO_DESCRIPTION;
import static fr.free.nrw.commons.utils.LengthUtils.formatDistanceBetween;
import static fr.free.nrw.commons.wikidata.WikidataConstants.PLACE_OBJECT;
public class NearbyParentFragment extends CommonsDaggerSupportFragment
implements NearbyParentFragmentContract.View,
@ -164,6 +161,8 @@ public class NearbyParentFragment extends CommonsDaggerSupportFragment
@Inject
SystemThemeUtils systemThemeUtils;
@Inject
CommonPlaceClickActions commonPlaceClickActions;
private NearbyFilterSearchRecyclerViewAdapter nearbyFilterSearchRecyclerViewAdapter;
@ -177,7 +176,7 @@ public class NearbyParentFragment extends CommonsDaggerSupportFragment
private static final float ZOOM_LEVEL = 14f;
private final String NETWORK_INTENT_ACTION = "android.net.conn.CONNECTIVITY_CHANGE";
private BroadcastReceiver broadcastReceiver;
private boolean isNetworkErrorOccurred = false;
private boolean isNetworkErrorOccurred;
private Snackbar snackbar;
private View view;
private NearbyParentFragmentPresenter presenter;
@ -189,25 +188,22 @@ public class NearbyParentFragment extends CommonsDaggerSupportFragment
private final double CAMERA_TARGET_SHIFT_FACTOR_PORTRAIT = 0.005;
private final double CAMERA_TARGET_SHIFT_FACTOR_LANDSCAPE = 0.004;
private boolean isMapBoxReady=false;
private boolean isMapBoxReady;
private MapboxMap mapBox;
IntentFilter intentFilter = new IntentFilter(NETWORK_INTENT_ACTION);
private Marker currentLocationMarker;
private Polygon currentLocationPolygon;
private Place lastPlaceToCenter;
private fr.free.nrw.commons.location.LatLng lastKnownLocation;
private fr.free.nrw.commons.location.LatLng currentLocation=null;
private NearbyController.NearbyPlacesInfo nearbyPlacesInfo;
private NearbyAdapterFactory adapterFactory;
private boolean isVisibleToUser;
private MapboxMap.OnCameraMoveListener cameraMoveListener;
private fr.free.nrw.commons.location.LatLng lastFocusLocation;
private LatLngBounds latLngBounds;
private PlaceAdapter adapter;
@Override
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
public View onCreateView(@NonNull final LayoutInflater inflater, final ViewGroup container,
final Bundle savedInstanceState) {
view = inflater.inflate(R.layout.fragment_nearby_parent, container, false);
ButterKnife.bind(this, view);
initNetworkBroadCastReceiver();
@ -218,7 +214,7 @@ public class NearbyParentFragment extends CommonsDaggerSupportFragment
}
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
public void onViewCreated(@NonNull final View view, @Nullable final Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
isDarkTheme = systemThemeUtils.isDeviceInNightMode();
cameraMoveListener= () -> presenter.onCameraMove(mapBox.getCameraPosition().target);
@ -228,28 +224,28 @@ public class NearbyParentFragment extends CommonsDaggerSupportFragment
initThemePreferences();
mapView.onCreate(savedInstanceState);
mapView.getMapAsync(mapBoxMap -> {
this.mapBox=mapBoxMap;
mapBox =mapBoxMap;
initViews();
presenter.setActionListeners(applicationKvStore);
initNearbyFilter();
mapBoxMap.setStyle(isDarkTheme?Style.DARK:Style.OUTDOORS, style -> {
UiSettings uiSettings = mapBoxMap.getUiSettings();
final UiSettings uiSettings = mapBoxMap.getUiSettings();
uiSettings.setCompassGravity(Gravity.BOTTOM | Gravity.LEFT);
uiSettings.setCompassMargins(12, 0, 0, 24);
uiSettings.setLogoEnabled(true);
uiSettings.setAttributionEnabled(true);
uiSettings.setRotateGesturesEnabled(false);
NearbyParentFragment.this.isMapBoxReady=true;
isMapBoxReady =true;
performMapReadyActions();
CameraPosition cameraPosition = new CameraPosition.Builder()
final CameraPosition cameraPosition = new CameraPosition.Builder()
.target(new LatLng(51.50550, -0.07520))
.zoom(ZOOM_LEVEL)
.build();
mapBoxMap.setCameraPosition(cameraPosition);
ScaleBarPlugin scaleBarPlugin = new ScaleBarPlugin(mapView, mapBoxMap);
int color = isDarkTheme ? R.color.bottom_bar_light : R.color.bottom_bar_dark;
ScaleBarOptions scaleBarOptions = new ScaleBarOptions(getContext())
final ScaleBarPlugin scaleBarPlugin = new ScaleBarPlugin(mapView, mapBoxMap);
final int color = isDarkTheme ? R.color.bottom_bar_light : R.color.bottom_bar_dark;
final ScaleBarOptions scaleBarOptions = new ScaleBarOptions(getContext())
.setTextColor(color)
.setTextSize(R.dimen.description_text_size)
.setBarHeight(R.dimen.tiny_gap)
@ -281,8 +277,18 @@ public class NearbyParentFragment extends CommonsDaggerSupportFragment
private void initRvNearbyList() {
rvNearbyList.setLayoutManager(new LinearLayoutManager(getContext()));
adapterFactory = new NearbyAdapterFactory(this, controller);
rvNearbyList.setAdapter(adapterFactory.create(null));
adapter = new PlaceAdapter(bookmarkLocationDao,
place -> {
centerMapToPlace(place);
return Unit.INSTANCE;
},
(place, isBookmarked) -> {
updateMarker(isBookmarked, place, null);
return Unit.INSTANCE;
},
commonPlaceClickActions
);
rvNearbyList.setAdapter(adapter);
}
private void addCheckBoxCallback() {
@ -292,13 +298,13 @@ public class NearbyParentFragment extends CommonsDaggerSupportFragment
private void performMapReadyActions() {
if (isVisible() && isVisibleToUser && isMapBoxReady) {
checkPermissionsAndPerformAction(() -> {
this.lastKnownLocation = locationManager.getLastLocation();
lastKnownLocation = locationManager.getLastLocation();
fr.free.nrw.commons.location.LatLng target=lastFocusLocation;
if(null==lastFocusLocation){
target=lastKnownLocation;
}
if (lastKnownLocation != null) {
CameraPosition position = new CameraPosition.Builder()
final CameraPosition position = new CameraPosition.Builder()
.target(LocationUtils.commonsLatLngToMapBoxLatLng(target)) // Sets the new camera position
.zoom(ZOOM_LEVEL) // Same zoom level
.build();
@ -325,8 +331,9 @@ public class NearbyParentFragment extends CommonsDaggerSupportFragment
}
private void registerNetworkReceiver() {
if (getActivity() != null)
if (getActivity() != null) {
getActivity().registerReceiver(broadcastReceiver, intentFilter);
}
}
@ -349,7 +356,7 @@ public class NearbyParentFragment extends CommonsDaggerSupportFragment
if (null != mapBox) {
mapBox.removeOnCameraMoveListener(cameraMoveListener);
}
}catch (Exception e){
}catch (final Exception e){
Timber.e(e);
//Broadcast receivers should always be unregistered inside catch, you never know if they were already registered or not
}
@ -362,7 +369,7 @@ public class NearbyParentFragment extends CommonsDaggerSupportFragment
}
@Override
public void onSaveInstanceState(Bundle outState) {
public void onSaveInstanceState(final Bundle outState) {
super.onSaveInstanceState(outState);
mapView.onSaveInstanceState(outState);
}
@ -415,7 +422,7 @@ public class NearbyParentFragment extends CommonsDaggerSupportFragment
recyclerView.addItemDecoration(new DividerItemDecoration(getContext(),
DividerItemDecoration.VERTICAL));
LinearLayoutManager linearLayoutManager = new LinearLayoutManager(getActivity());
final LinearLayoutManager linearLayoutManager = new LinearLayoutManager(getActivity());
linearLayoutManager.setOrientation(LinearLayoutManager.VERTICAL);
recyclerView.setLayoutManager(linearLayoutManager);
@ -427,7 +434,7 @@ public class NearbyParentFragment extends CommonsDaggerSupportFragment
}
@Override
public void filterByMarkerType(ArrayList<Label> selectedLabels, int i, boolean b, boolean b1) {
public void filterByMarkerType(final ArrayList<Label> selectedLabels, final int i, final boolean b, final boolean b1) {
presenter.filterByMarkerType(selectedLabels,i,b,b1);
}
@ -457,7 +464,7 @@ public class NearbyParentFragment extends CommonsDaggerSupportFragment
}
@Override
public void setCheckBoxState(int state) {
public void setCheckBoxState(final int state) {
checkBoxTriStates.setState(state);
}
@ -512,12 +519,12 @@ public class NearbyParentFragment extends CommonsDaggerSupportFragment
bottomSheetDetailsBehavior.setBottomSheetCallback(new BottomSheetBehavior
.BottomSheetCallback() {
@Override
public void onStateChanged(@NonNull View bottomSheet, int newState) {
public void onStateChanged(@NonNull final View bottomSheet, final int newState) {
prepareViewsForSheetPosition(newState);
}
@Override
public void onSlide(@NonNull View bottomSheet, float slideOffset) {
public void onSlide(@NonNull final View bottomSheet, final float slideOffset) {
}
});
@ -537,14 +544,14 @@ public class NearbyParentFragment extends CommonsDaggerSupportFragment
bottomSheetListBehavior.setBottomSheetCallback(new BottomSheetBehavior
.BottomSheetCallback() {
@Override
public void onStateChanged(@NonNull View bottomSheet, int newState) {
public void onStateChanged(@NonNull final View bottomSheet, final int newState) {
if (newState == BottomSheetBehavior.STATE_EXPANDED) {
bottomSheetDetailsBehavior.setState(BottomSheetBehavior.STATE_HIDDEN);
}
}
@Override
public void onSlide(@NonNull View bottomSheet, float slideOffset) {
public void onSlide(@NonNull final View bottomSheet, final float slideOffset) {
}
});
@ -598,21 +605,21 @@ public class NearbyParentFragment extends CommonsDaggerSupportFragment
* Centers the map in nearby fragment to a given place
* @param place is new center of the map
*/
public void centerMapToPlace(Place place) {
public void centerMapToPlace(final Place place) {
Timber.d("Map is centered to place");
double cameraShift;
final double cameraShift;
if(null!=place){
lastPlaceToCenter=place;
}
if (null != lastPlaceToCenter) {
Configuration configuration = getActivity().getResources().getConfiguration();
final Configuration configuration = getActivity().getResources().getConfiguration();
if (configuration.orientation == Configuration.ORIENTATION_PORTRAIT) {
cameraShift = CAMERA_TARGET_SHIFT_FACTOR_PORTRAIT;
} else {
cameraShift = CAMERA_TARGET_SHIFT_FACTOR_LANDSCAPE;
}
CameraPosition position = new CameraPosition.Builder()
final CameraPosition position = new CameraPosition.Builder()
.target(LocationUtils.commonsLatLngToMapBoxLatLng(
new fr.free.nrw.commons.location.LatLng(lastPlaceToCenter.location.getLatitude() - cameraShift,
lastPlaceToCenter.getLocation().getLongitude(),
@ -624,20 +631,17 @@ public class NearbyParentFragment extends CommonsDaggerSupportFragment
}
@Override
public void updateListFragment(List<Place> placeList) {
adapterFactory.updateAdapterData(placeList, (RVRendererAdapter<Place>) rvNearbyList.getAdapter());
public void updateListFragment(final List<Place> placeList) {
adapter.setItems(placeList);
}
public void clearNearbyList() {
adapterFactory.clear((RVRendererAdapter<Place>) rvNearbyList.getAdapter());
adapter.clear();
}
public void updateNearbyList() {
adapterFactory.update((RVRendererAdapter<Place>) rvNearbyList.getAdapter());
}
public void addPlaceToNearbyList(Place place) {
adapterFactory.add(place, (RVRendererAdapter<Place>) rvNearbyList.getAdapter());
public void addPlaceToNearbyList(final Place place) {
adapter.add(place);
}
@Override
@ -677,7 +681,7 @@ public class NearbyParentFragment extends CommonsDaggerSupportFragment
private void initNetworkBroadCastReceiver() {
broadcastReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
public void onReceive(final Context context, final Intent intent) {
if (getActivity() != null) {
if (NetworkUtils.isInternetConnectionEstablished(getActivity())) {
if (isNetworkErrorOccurred) {
@ -718,10 +722,7 @@ public class NearbyParentFragment extends CommonsDaggerSupportFragment
}
@Override
public void populatePlaces(fr.free.nrw.commons.location.LatLng curlatLng) {
if (lastKnownLocation == null) {
lastKnownLocation = currentLocation;
}
public void populatePlaces(final fr.free.nrw.commons.location.LatLng curlatLng) {
if (curlatLng.equals(lastFocusLocation)|| lastFocusLocation==null) { // Means we are checking around current location
populatePlacesForCurrentLocation(lastKnownLocation, curlatLng);
} else {
@ -729,8 +730,8 @@ public class NearbyParentFragment extends CommonsDaggerSupportFragment
}
}
private void populatePlacesForCurrentLocation(fr.free.nrw.commons.location.LatLng curlatLng,
fr.free.nrw.commons.location.LatLng searchLatLng) {
private void populatePlacesForCurrentLocation(final fr.free.nrw.commons.location.LatLng curlatLng,
final fr.free.nrw.commons.location.LatLng searchLatLng) {
compositeDisposable.add(Observable.fromCallable(() -> nearbyController
.loadAttractionsFromLocation(curlatLng, searchLatLng, false, true))
.subscribeOn(Schedulers.io())
@ -748,8 +749,8 @@ public class NearbyParentFragment extends CommonsDaggerSupportFragment
}));
}
private void populatePlacesForAnotherLocation(fr.free.nrw.commons.location.LatLng curlatLng,
fr.free.nrw.commons.location.LatLng searchLatLng) {
private void populatePlacesForAnotherLocation(final fr.free.nrw.commons.location.LatLng curlatLng,
final fr.free.nrw.commons.location.LatLng searchLatLng) {
compositeDisposable.add(Observable.fromCallable(() -> nearbyController
.loadAttractionsFromLocation(curlatLng, searchLatLng, false, false))
.subscribeOn(Schedulers.io())
@ -771,8 +772,7 @@ public class NearbyParentFragment extends CommonsDaggerSupportFragment
* location where you are.
* @param nearbyPlacesInfo This variable has place list information and distances.
*/
private void updateMapMarkers(NearbyController.NearbyPlacesInfo nearbyPlacesInfo,boolean shouldUpdateSelectedMarker) {
this.nearbyPlacesInfo=nearbyPlacesInfo;
private void updateMapMarkers(final NearbyController.NearbyPlacesInfo nearbyPlacesInfo, final boolean shouldUpdateSelectedMarker) {
presenter.updateMapMarkers(nearbyPlacesInfo, selectedMarker,shouldUpdateSelectedMarker);
setFilterState();
}
@ -803,7 +803,7 @@ public class NearbyParentFragment extends CommonsDaggerSupportFragment
}
@Override
public void setSearchThisAreaButtonVisibility(boolean isVisible) {
public void setSearchThisAreaButtonVisibility(final boolean isVisible) {
if (isVisible) {
searchThisAreaButton.setVisibility(View.VISIBLE);
} else {
@ -813,11 +813,7 @@ public class NearbyParentFragment extends CommonsDaggerSupportFragment
@Override
public boolean isSearchThisAreaButtonVisible() {
if (searchThisAreaButton.getVisibility() == View.VISIBLE) {
return true;
} else {
return false;
}
return searchThisAreaButton.getVisibility() == View.VISIBLE;
}
@Override
@ -835,7 +831,7 @@ public class NearbyParentFragment extends CommonsDaggerSupportFragment
}
@Override
public void setProgressBarVisibility(boolean isVisible) {
public void setProgressBarVisibility(final boolean isVisible) {
if (isVisible) {
progressBar.setVisibility(View.VISIBLE);
} else {
@ -849,7 +845,7 @@ public class NearbyParentFragment extends CommonsDaggerSupportFragment
}
@Override
public void checkPermissionsAndPerformAction(Runnable runnable) {
public void checkPermissionsAndPerformAction(final Runnable runnable) {
Timber.d("Checking permission and perfoming action");
PermissionUtils.checkPermissionsAndPerformAction(getActivity(),
Manifest.permission.ACCESS_FINE_LOCATION,
@ -884,7 +880,7 @@ public class NearbyParentFragment extends CommonsDaggerSupportFragment
* Expands camera and gallery FABs, turn forward plus FAB
* @param isFABsExpanded true if they are already expanded
*/
private void expandFABs(boolean isFABsExpanded){
private void expandFABs(final boolean isFABsExpanded){
if (!isFABsExpanded) {
showFABs();
fabPlus.startAnimation(rotate_forward);
@ -912,7 +908,7 @@ public class NearbyParentFragment extends CommonsDaggerSupportFragment
* Collapses camera and gallery FABs, turn back plus FAB
* @param isFABsExpanded
*/
private void collapseFABs(boolean isFABsExpanded){
private void collapseFABs(final boolean isFABsExpanded){
if (isFABsExpanded) {
fabPlus.startAnimation(rotate_backward);
fabCamera.startAnimation(fab_close);
@ -939,19 +935,19 @@ public class NearbyParentFragment extends CommonsDaggerSupportFragment
}
}
private void handleLocationUpdate(fr.free.nrw.commons.location.LatLng latLng, LocationServiceManager.LocationChangeType locationChangeType){
this.lastKnownLocation = latLng;
private void handleLocationUpdate(final fr.free.nrw.commons.location.LatLng latLng, final LocationServiceManager.LocationChangeType locationChangeType){
lastKnownLocation = latLng;
NearbyController.currentLocation = lastKnownLocation;
presenter.updateMapAndList(locationChangeType);
}
private boolean isUserBrowsing() {
boolean isUserBrowsing = lastKnownLocation!=null && !presenter.areLocationsClose(getCameraTarget(), lastKnownLocation);
final boolean isUserBrowsing = lastKnownLocation!=null && !presenter.areLocationsClose(getCameraTarget(), lastKnownLocation);
return isUserBrowsing;
}
@Override
public void onLocationChangedSignificantly(fr.free.nrw.commons.location.LatLng latLng) {
public void onLocationChangedSignificantly(final fr.free.nrw.commons.location.LatLng latLng) {
Timber.d("Location significantly changed");
if (isMapBoxReady && latLng != null &&!isUserBrowsing()) {
handleLocationUpdate(latLng,LOCATION_SIGNIFICANTLY_CHANGED);
@ -959,7 +955,7 @@ public class NearbyParentFragment extends CommonsDaggerSupportFragment
}
@Override
public void onLocationChangedSlightly(fr.free.nrw.commons.location.LatLng latLng) {
public void onLocationChangedSlightly(final fr.free.nrw.commons.location.LatLng latLng) {
Timber.d("Location slightly changed");
if (isMapBoxReady && latLng != null &&!isUserBrowsing()) {//If the map has never ever shown the current location, lets do it know
handleLocationUpdate(latLng,LOCATION_SLIGHTLY_CHANGED);
@ -967,7 +963,7 @@ public class NearbyParentFragment extends CommonsDaggerSupportFragment
}
@Override
public void onLocationChangedMedium(fr.free.nrw.commons.location.LatLng latLng) {
public void onLocationChangedMedium(final fr.free.nrw.commons.location.LatLng latLng) {
Timber.d("Location changed medium");
if (isMapBoxReady && latLng != null && !isUserBrowsing()) {//If the map has never ever shown the current location, lets do it know
handleLocationUpdate(latLng, LOCATION_SIGNIFICANTLY_CHANGED);
@ -985,7 +981,7 @@ public class NearbyParentFragment extends CommonsDaggerSupportFragment
@Override
public void onLogoutComplete() {
Timber.d("Logout complete callback received.");
Intent nearbyIntent = new Intent( getActivity(), LoginActivity.class);
final Intent nearbyIntent = new Intent( getActivity(), LoginActivity.class);
nearbyIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
nearbyIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(nearbyIntent);
@ -994,12 +990,12 @@ public class NearbyParentFragment extends CommonsDaggerSupportFragment
}
@Override
public void setFABPlusAction(View.OnClickListener onClickListener) {
public void setFABPlusAction(final View.OnClickListener onClickListener) {
fabPlus.setOnClickListener(onClickListener);
}
@Override
public void setFABRecenterAction(View.OnClickListener onClickListener) {
public void setFABRecenterAction(final View.OnClickListener onClickListener) {
fabRecenter.setOnClickListener(onClickListener);
}
@ -1025,27 +1021,27 @@ public class NearbyParentFragment extends CommonsDaggerSupportFragment
* @param curLatLng current location
*/
@Override
public void addCurrentLocationMarker(fr.free.nrw.commons.location.LatLng curLatLng) {
public void addCurrentLocationMarker(final fr.free.nrw.commons.location.LatLng curLatLng) {
if (null != curLatLng) {
ExecutorUtils.get().submit(() -> {
mapView.post(() -> removeCurrentLocationMarker());
Timber.d("Adds current location marker");
Icon icon = IconFactory.getInstance(getContext())
final Icon icon = IconFactory.getInstance(getContext())
.fromResource(R.drawable.current_location_marker);
MarkerOptions currentLocationMarkerOptions = new MarkerOptions()
final MarkerOptions currentLocationMarkerOptions = new MarkerOptions()
.position(new LatLng(curLatLng.getLatitude(),
curLatLng.getLongitude()));
currentLocationMarkerOptions.setIcon(icon); // Set custom icon
mapView.post(() -> currentLocationMarker = mapBox.addMarker(currentLocationMarkerOptions));
List<LatLng> circle = UiUtils
final List<LatLng> circle = UiUtils
.createCircleArray(curLatLng.getLatitude(), curLatLng.getLongitude(),
curLatLng.getAccuracy() * 2, 100);
PolygonOptions currentLocationPolygonOptions = new PolygonOptions()
final PolygonOptions currentLocationPolygonOptions = new PolygonOptions()
.addAll(circle)
.strokeColor(getResources().getColor(R.color.current_marker_stroke))
.fillColor(getResources().getColor(R.color.current_marker_fill));
@ -1070,9 +1066,9 @@ public class NearbyParentFragment extends CommonsDaggerSupportFragment
* @param curLatLng current location of user
*/
@Override
public void updateMapToTrackPosition(fr.free.nrw.commons.location.LatLng curLatLng) {
public void updateMapToTrackPosition(final fr.free.nrw.commons.location.LatLng curLatLng) {
Timber.d("Updates map camera to track user position");
CameraPosition cameraPosition = new CameraPosition.Builder().target
final CameraPosition cameraPosition = new CameraPosition.Builder().target
(LocationUtils.commonsLatLngToMapBoxLatLng(curLatLng)).build();
if(null!=mapBox) {
mapBox.setCameraPosition(cameraPosition);
@ -1082,7 +1078,7 @@ public class NearbyParentFragment extends CommonsDaggerSupportFragment
}
@Override
public void updateMapMarkers(List<NearbyBaseMarker> nearbyBaseMarkers, Marker selectedMarker) {
public void updateMapMarkers(final List<NearbyBaseMarker> nearbyBaseMarkers, final Marker selectedMarker) {
if(mapBox!=null && isMapBoxReady){
mapBox.clear();
addNearbyMarkersToMapBoxMap(nearbyBaseMarkers, selectedMarker);
@ -1095,7 +1091,6 @@ public class NearbyParentFragment extends CommonsDaggerSupportFragment
@Override
public void filterOutAllMarkers() {
hideAllMarkers();
updateNearbyList();
}
/**
@ -1103,10 +1098,9 @@ public class NearbyParentFragment extends CommonsDaggerSupportFragment
*/
@Override
public void displayAllMarkers() {
for (MarkerPlaceGroup markerPlaceGroup : NearbyController.markerLabelList) {
for (final MarkerPlaceGroup markerPlaceGroup : NearbyController.markerLabelList) {
updateMarker(markerPlaceGroup.getIsBookmarked(), markerPlaceGroup.getPlace(), NearbyController.currentLocation);
}
updateNearbyList();
}
/**
@ -1118,17 +1112,17 @@ public class NearbyParentFragment extends CommonsDaggerSupportFragment
* @param filterForAllNoneType true if we filter places with all none button
*/
@Override
public void filterMarkersByLabels(List<Label> selectedLabels,
boolean displayExists,
boolean displayNeedsPhoto,
boolean filterForPlaceState,
boolean filterForAllNoneType) {
public void filterMarkersByLabels(final List<Label> selectedLabels,
final boolean displayExists,
final boolean displayNeedsPhoto,
final boolean filterForPlaceState,
final boolean filterForAllNoneType) {
// Remove the previous markers before updating them
hideAllMarkers();
for (MarkerPlaceGroup markerPlaceGroup : NearbyController.markerLabelList) {
Place place = markerPlaceGroup.getPlace();
for (final MarkerPlaceGroup markerPlaceGroup : NearbyController.markerLabelList) {
final Place place = markerPlaceGroup.getPlace();
// When label filter is engaged
// then compare it against place's label
@ -1157,7 +1151,6 @@ public class NearbyParentFragment extends CommonsDaggerSupportFragment
updateMarker(markerPlaceGroup.getIsBookmarked(), place, NearbyController.currentLocation);
}
}
updateNearbyList();
}
@Override
@ -1171,10 +1164,10 @@ public class NearbyParentFragment extends CommonsDaggerSupportFragment
* @param place
* @param curLatLng current location
*/
public void updateMarker(boolean isBookmarked, Place place, @Nullable fr.free.nrw.commons.location.LatLng curLatLng) {
public void updateMarker(final boolean isBookmarked, final Place place, @Nullable final fr.free.nrw.commons.location.LatLng curLatLng) {
addPlaceToNearbyList(place);
VectorDrawableCompat vectorDrawable;
final VectorDrawableCompat vectorDrawable;
if (isBookmarked) {
vectorDrawable = VectorDrawableCompat.create(
getContext().getResources(), R.drawable.ic_custom_bookmark_marker, getContext().getTheme()
@ -1192,16 +1185,16 @@ public class NearbyParentFragment extends CommonsDaggerSupportFragment
getContext().getResources(), R.drawable.ic_custom_map_marker, getContext().getTheme()
);
}
for (Marker marker : mapBox.getMarkers()) {
for (final Marker marker : mapBox.getMarkers()) {
if (marker.getTitle() != null && marker.getTitle().equals(place.getName())) {
Bitmap icon = UiUtils.getBitmap(vectorDrawable);
final Bitmap icon = UiUtils.getBitmap(vectorDrawable);
if (curLatLng != null) {
String distance = formatDistanceBetween(curLatLng, place.location);
final String distance = formatDistanceBetween(curLatLng, place.location);
place.setDistance(distance);
}
NearbyBaseMarker nearbyBaseMarker = new NearbyBaseMarker();
final NearbyBaseMarker nearbyBaseMarker = new NearbyBaseMarker();
nearbyBaseMarker.title(place.name);
nearbyBaseMarker.position(
new com.mapbox.mapboxsdk.geometry.LatLng(
@ -1221,11 +1214,11 @@ public class NearbyParentFragment extends CommonsDaggerSupportFragment
* since grey icon may lead the users to believe that it is disabled or prohibited contribution
*/
private void hideAllMarkers() {
VectorDrawableCompat vectorDrawable;
final VectorDrawableCompat vectorDrawable;
vectorDrawable = VectorDrawableCompat.create(
getContext().getResources(), R.drawable.ic_custom_greyed_out_marker, getContext().getTheme());
Bitmap icon = UiUtils.getBitmap(vectorDrawable);
for (Marker marker : mapBox.getMarkers()) {
final Bitmap icon = UiUtils.getBitmap(vectorDrawable);
for (final Marker marker : mapBox.getMarkers()) {
if (!marker.equals(currentLocationMarker)) {
marker.setIcon(IconFactory.getInstance(getContext()).fromBitmap(icon));
}
@ -1234,7 +1227,7 @@ public class NearbyParentFragment extends CommonsDaggerSupportFragment
clearNearbyList();
}
private void addNearbyMarkersToMapBoxMap(List<NearbyBaseMarker> nearbyBaseMarkers, Marker selectedMarker) {
private void addNearbyMarkersToMapBoxMap(final List<NearbyBaseMarker> nearbyBaseMarkers, final Marker selectedMarker) {
if (isMapBoxReady && mapBox != null) {
mapBox.addMarkers(nearbyBaseMarkers);
setMapMarkerActions(selectedMarker);
@ -1242,7 +1235,7 @@ public class NearbyParentFragment extends CommonsDaggerSupportFragment
}
}
private void setMapMarkerActions(Marker selectedMarker) {
private void setMapMarkerActions(final Marker selectedMarker) {
if (mapBox != null) {
mapBox.setOnInfoWindowCloseListener(marker -> {
if (marker == selectedMarker) {
@ -1260,7 +1253,7 @@ public class NearbyParentFragment extends CommonsDaggerSupportFragment
}
@Override
public void recenterMap(fr.free.nrw.commons.location.LatLng curLatLng) {
public void recenterMap(final fr.free.nrw.commons.location.LatLng curLatLng) {
if (curLatLng == null) {
if (!(locationManager.isNetworkProviderEnabled() || locationManager.isGPSProviderEnabled())) {
showLocationOffDialog();
@ -1268,7 +1261,7 @@ public class NearbyParentFragment extends CommonsDaggerSupportFragment
return;
}
addCurrentLocationMarker(curLatLng);
CameraPosition position;
final CameraPosition position;
if (ViewUtil.isPortrait(getActivity())) {
position = new CameraPosition.Builder()
@ -1306,8 +1299,8 @@ public class NearbyParentFragment extends CommonsDaggerSupportFragment
@Override
public void openLocationSettings() {
// This method opens the location settings of the device along with a followup toast.
Intent intent = new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS);
PackageManager packageManager = getActivity().getPackageManager();
final Intent intent = new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS);
final PackageManager packageManager = getActivity().getPackageManager();
if (intent.resolveActivity(packageManager)!= null) {
startActivity(intent);
@ -1328,10 +1321,10 @@ public class NearbyParentFragment extends CommonsDaggerSupportFragment
}
@Override
public void displayBottomSheetWithInfo(Marker marker) {
this.selectedMarker = marker;
NearbyMarker nearbyMarker = (NearbyMarker) marker;
Place place = nearbyMarker.getNearbyBaseMarker().getPlace();
public void displayBottomSheetWithInfo(final Marker marker) {
selectedMarker = marker;
final NearbyMarker nearbyMarker = (NearbyMarker) marker;
final Place place = nearbyMarker.getNearbyBaseMarker().getPlace();
passInfoToSheet(place);
hideBottomSheet();
bottomSheetDetailsBehavior.setState(BottomSheetBehavior.STATE_COLLAPSED);
@ -1348,16 +1341,18 @@ public class NearbyParentFragment extends CommonsDaggerSupportFragment
* If nearby details bottom sheet state is hidden: hide all fabs
* @param bottomSheetState see bottom sheet states
*/
public void prepareViewsForSheetPosition(int bottomSheetState) {
public void prepareViewsForSheetPosition(final int bottomSheetState) {
switch (bottomSheetState) {
case (BottomSheetBehavior.STATE_COLLAPSED):
collapseFABs(isFABsExpanded);
if (!fabPlus.isShown()) showFABs();
this.getView().requestFocus();
if (!fabPlus.isShown()) {
showFABs();
}
getView().requestFocus();
break;
case (BottomSheetBehavior.STATE_EXPANDED):
this.getView().requestFocus();
getView().requestFocus();
break;
case (BottomSheetBehavior.STATE_HIDDEN):
if (null != mapBox) {
@ -1367,8 +1362,8 @@ public class NearbyParentFragment extends CommonsDaggerSupportFragment
transparentView.setAlpha(0);
collapseFABs(isFABsExpanded);
hideFABs();
if (this.getView() != null) {
this.getView().requestFocus();
if (getView() != null) {
getView().requestFocus();
}
break;
}
@ -1379,36 +1374,37 @@ public class NearbyParentFragment extends CommonsDaggerSupportFragment
* (title, description, distance and links) to view on nearby marker click
* @param place Place of clicked nearby marker
*/
private void passInfoToSheet(Place place) {
this.selectedPlace = place;
updateBookmarkButtonImage(this.selectedPlace);
private void passInfoToSheet(final Place place) {
selectedPlace = place;
updateBookmarkButtonImage(selectedPlace);
bookmarkButton.setOnClickListener(view -> {
boolean isBookmarked = bookmarkLocationDao.updateBookmarkLocation(this.selectedPlace);
updateBookmarkButtonImage(this.selectedPlace);
updateMarker(isBookmarked, this.selectedPlace, locationManager.getLastLocation());
final boolean isBookmarked = bookmarkLocationDao.updateBookmarkLocation(selectedPlace);
updateBookmarkButtonImage(selectedPlace);
updateMarker(isBookmarked, selectedPlace, locationManager.getLastLocation());
});
wikipediaButton.setVisibility(place.hasWikipediaLink()?View.VISIBLE:View.GONE);
wikipediaButton.setOnClickListener(view -> Utils.handleWebUrl(getContext(), this.selectedPlace.siteLinks.getWikipediaLink()));
wikipediaButton.setOnClickListener(view -> Utils.handleWebUrl(getContext(), selectedPlace.siteLinks.getWikipediaLink()));
wikidataButton.setVisibility(place.hasWikidataLink()?View.VISIBLE:View.GONE);
wikidataButton.setOnClickListener(view -> Utils.handleWebUrl(getContext(), this.selectedPlace.siteLinks.getWikidataLink()));
wikidataButton.setOnClickListener(view -> Utils.handleWebUrl(getContext(), selectedPlace.siteLinks.getWikidataLink()));
directionsButton.setOnClickListener(view -> Utils.handleGeoCoordinates(getActivity(), this.selectedPlace.getLocation()));
directionsButton.setOnClickListener(view -> Utils.handleGeoCoordinates(getActivity(),
selectedPlace.getLocation()));
commonsButton.setVisibility(this.selectedPlace.hasCommonsLink()?View.VISIBLE:View.GONE);
commonsButton.setOnClickListener(view -> Utils.handleWebUrl(getContext(), this.selectedPlace.siteLinks.getCommonsLink()));
commonsButton.setVisibility(selectedPlace.hasCommonsLink()?View.VISIBLE:View.GONE);
commonsButton.setOnClickListener(view -> Utils.handleWebUrl(getContext(), selectedPlace.siteLinks.getCommonsLink()));
icon.setImageResource(this.selectedPlace.getLabel().getIcon());
icon.setImageResource(selectedPlace.getLabel().getIcon());
title.setText(this.selectedPlace.name);
distance.setText(this.selectedPlace.distance);
description.setText(this.selectedPlace.getLongDescription());
title.setText(selectedPlace.name);
distance.setText(selectedPlace.distance);
description.setText(selectedPlace.getLongDescription());
fabCamera.setOnClickListener(view -> {
if (fabCamera.isShown()) {
Timber.d("Camera button tapped. Place: %s", this.selectedPlace.toString());
Timber.d("Camera button tapped. Place: %s", selectedPlace.toString());
storeSharedPrefs(selectedPlace);
controller.initiateCameraPick(getActivity());
}
@ -1416,20 +1412,20 @@ public class NearbyParentFragment extends CommonsDaggerSupportFragment
fabGallery.setOnClickListener(view -> {
if (fabGallery.isShown()) {
Timber.d("Gallery button tapped. Place: %s", this.selectedPlace.toString());
Timber.d("Gallery button tapped. Place: %s", selectedPlace.toString());
storeSharedPrefs(selectedPlace);
controller.initiateGalleryPick(getActivity(), false);
}
});
}
private void storeSharedPrefs(Place selectedPlace) {
private void storeSharedPrefs(final Place selectedPlace) {
Timber.d("Store place object %s", selectedPlace.toString());
applicationKvStore.putJson(PLACE_OBJECT, selectedPlace);
}
private void updateBookmarkButtonImage(Place place) {
int bookmarkIcon;
private void updateBookmarkButtonImage(final Place place) {
final int bookmarkIcon;
if (bookmarkLocationDao.findBookmarkLocation(place)) {
bookmarkIcon = R.drawable.ic_round_star_filled_24px;
} else {
@ -1441,7 +1437,7 @@ public class NearbyParentFragment extends CommonsDaggerSupportFragment
}
@Override
public void onAttach(Context context) {
public void onAttach(final Context context) {
super.onAttach(context);
wikidataEditListener.setAuthenticationStateListener(this);
}
@ -1459,11 +1455,11 @@ public class NearbyParentFragment extends CommonsDaggerSupportFragment
}
}
private void showErrorMessage(String message) {
private void showErrorMessage(final String message) {
ViewUtil.showLongToast(getActivity(), message);
}
public void registerUnregisterLocationListener(boolean removeLocationListener) {
public void registerUnregisterLocationListener(final boolean removeLocationListener) {
try {
if (removeLocationListener) {
locationManager.unregisterLocationManager();
@ -1474,14 +1470,14 @@ public class NearbyParentFragment extends CommonsDaggerSupportFragment
locationManager.registerLocationManager();
Timber.d("Location service manager added and registered");
}
}catch (Exception e){
}catch (final Exception e){
Timber.e(e);
//Broadcasts are tricky, should be catchedonR
}
}
@Override
public void setUserVisibleHint(boolean isVisibleToUser) {
public void setUserVisibleHint(final boolean isVisibleToUser) {
super.setUserVisibleHint(isVisibleToUser);
this.isVisibleToUser=isVisibleToUser;
if (isResumed() && isVisibleToUser) {

View file

@ -0,0 +1,25 @@
package fr.free.nrw.commons.nearby.fragments
import fr.free.nrw.commons.bookmarks.locations.BookmarkLocationsDao
import fr.free.nrw.commons.nearby.Place
import fr.free.nrw.commons.nearby.placeAdapterDelegate
import fr.free.nrw.commons.upload.categories.BaseDelegateAdapter
class PlaceAdapter(
bookmarkLocationsDao: BookmarkLocationsDao,
onPlaceClicked: ((Place) -> Unit)? = null,
onBookmarkClicked: (Place, Boolean) -> Unit,
commonPlaceClickActions: CommonPlaceClickActions
) :
BaseDelegateAdapter<Place>(
placeAdapterDelegate(
bookmarkLocationsDao,
onPlaceClicked,
commonPlaceClickActions.onCameraClicked(),
commonPlaceClickActions.onGalleryClicked(),
onBookmarkClicked,
commonPlaceClickActions.onOverflowClicked(),
commonPlaceClickActions.onDirectionsClicked()
),
areItemsTheSame = {oldItem, newItem -> oldItem.wikiDataEntityId == newItem.wikiDataEntityId }
)

View file

@ -20,14 +20,22 @@ abstract class BaseDelegateAdapter<T>(
*delegates
) {
fun getItemAt(position: Int) = items[position]
fun addAll(newResults: List<T>) {
items = (items ?: emptyList<T>()) + newResults
items = itemsOrEmpty + newResults
}
fun clear() {
items = emptyList()
}
fun add(item: T) {
items = itemsOrEmpty + item
}
fun remove(item: T) {
items = itemsOrEmpty - item
}
private val itemsOrEmpty get() = items ?: emptyList<T>()
}

View file

@ -1,56 +0,0 @@
package fr.free.nrw.commons.upload.structure.depictions;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.CheckedTextView;
import android.widget.TextView;
import com.pedrogomez.renderers.Renderer;
import butterknife.BindView;
import butterknife.ButterKnife;
import fr.free.nrw.commons.R;
public class DepictionRenderer extends Renderer<DepictedItem> {
@BindView(R.id.depict_checkbox)
CheckedTextView checkedView;
private final UploadDepictsCallback listener;
@BindView(R.id.depicts_label)
TextView depictsLabel;
@BindView(R.id.description) TextView description;
public DepictionRenderer(UploadDepictsCallback listener) {
this.listener = listener;
}
@Override
protected void setUpView(View rootView) {
ButterKnife.bind(this, rootView);
}
@Override
protected void hookListeners(View rootView) {
rootView.setOnClickListener( v -> {
DepictedItem item = getContent();
item.setSelected(true);
checkedView.setChecked(item.isSelected());
if (listener != null) {
listener.depictsClicked(item);
}
});
}
@Override
protected View inflate(LayoutInflater inflater, ViewGroup parent) {
return inflater.inflate(R.layout.layout_upload_depicts_item, parent, false);
}
@Override
public void render() {
DepictedItem item = getContent();
checkedView.setChecked(item.isSelected());
depictsLabel.setText(item.getName());
description.setText(item.getDescription());
}
}

View file

@ -16,7 +16,7 @@
org.gradle.jvmargs=-Xmx1536M
android.enableBuildCache=true
KOTLIN_VERSION=1.3.21
KOTLIN_VERSION=1.3.72
BUTTERKNIFE_VERSION=10.1.0
LEAK_CANARY_VERSION=1.6.2
DAGGER_VERSION=2.21