#3468 Switch from RvRenderer to AdapterDelegates (#3750)

* #3468 Switch from RvRenderer to AdapterDelegates - replace SearchDepictionsRenderer

* #3468 Switch from RvRenderer to AdapterDelegates - replace UploadCategoryDepictionsRenderer

* #3468 Switch from RvRenderer to AdapterDelegates - update BaseAdapter to be easier to use

* #3468 Switch from RvRenderer to AdapterDelegates - replace SearchImagesRenderer

* #3468 Switch from RvRenderer to AdapterDelegates - replace SearchCategoriesRenderer

* #3468 Switch from RvRenderer to AdapterDelegates - replace NotificationRenderer

* #3468 Switch from RvRenderer to AdapterDelegates - replace UploadDepictsRenderer

* #3468 Switch from RvRenderer to AdapterDelegates - replace PlaceRenderer

* #3468 fix constant import

* #3468 Switch from RvRenderer to AdapterDelegates - resolve id conflict
This commit is contained in:
Seán Mac Gillicuddy 2020-05-27 10:44:21 +01:00 committed by GitHub
parent 82d662c8ef
commit b063d6bdfd
61 changed files with 851 additions and 1609 deletions

View file

@ -11,7 +11,7 @@ apply from: 'quality.gradle'
def isRunningOnTravisAndIsNotPRBuild = System.getenv("CI") == "true" && file('../play.p12').exists()
if(isRunningOnTravisAndIsNotPRBuild) {
if (isRunningOnTravisAndIsNotPRBuild) {
apply plugin: 'com.github.triplet.play'
}
@ -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'
@ -43,8 +42,9 @@ dependencies {
implementation 'com.dinuscxj:circleprogressbar:1.1.1'
implementation 'com.karumi:dexter:5.0.0'
implementation "com.jakewharton:butterknife:$BUTTERKNIFE_VERSION"
kapt "com.jakewharton:butterknife-compiler:$BUTTERKNIFE_VERSION"
implementation "com.hannesdorfmann:adapterdelegates4-kotlin-dsl-layoutcontainer:$ADAPTER_DELEGATES_VERSION"
implementation "com.hannesdorfmann:adapterdelegates4-pagination:$ADAPTER_DELEGATES_VERSION"
// Logging
implementation 'ch.acra:acra-dialog:5.3.0'
@ -104,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'
@ -112,7 +113,8 @@ dependencies {
implementation "androidx.room:room-runtime:$ROOM_VERSION"
implementation "androidx.room:room-ktx:$ROOM_VERSION"
implementation "androidx.room:room-rxjava2:$ROOM_VERSION"
kapt "androidx.room:room-compiler:$ROOM_VERSION" // For Kotlin use kapt instead of annotationProcessor
kapt "androidx.room:room-compiler:$ROOM_VERSION"
// For Kotlin use kapt instead of annotationProcessor
implementation 'com.squareup.retrofit2:retrofit:2.8.1'
testImplementation "androidx.arch.core:core-testing:2.1.0"
@ -145,7 +147,7 @@ android {
testOptions {
execution 'ANDROIDX_TEST_ORCHESTRATOR'
}
multiDexEnabled true
vectorDrawables.useSupportLibrary = true
}
@ -181,7 +183,7 @@ android {
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt'
testProguardFile 'test-proguard-rules.txt'
if(isRunningOnTravisAndIsNotPRBuild) {
if (isRunningOnTravisAndIsNotPRBuild) {
signingConfig signingConfigs.release
}
}
@ -291,7 +293,7 @@ android {
buildToolsVersion buildToolsVersion
}
if(isRunningOnTravisAndIsNotPRBuild) {
if (isRunningOnTravisAndIsNotPRBuild) {
play {
track = "alpha"
userFraction = 1

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;
@ -25,7 +24,6 @@ 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;

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

@ -1,55 +0,0 @@
package fr.free.nrw.commons.category;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.CheckedTextView;
import com.pedrogomez.renderers.Renderer;
import butterknife.BindView;
import butterknife.ButterKnife;
import fr.free.nrw.commons.R;
import timber.log.Timber;
/**
* Renders the Categories view
*/
public class CategoriesRenderer extends Renderer<CategoryItem> {
@BindView(R.id.tvName) CheckedTextView checkedView;
private final CategoryClickedListener listener;
CategoriesRenderer(CategoryClickedListener listener) {
this.listener = listener;
}
@Override
protected View inflate(LayoutInflater layoutInflater, ViewGroup viewGroup) {
return layoutInflater.inflate(R.layout.layout_categories_item, viewGroup, false);
}
@Override
protected void setUpView(View view) {
ButterKnife.bind(this, view);
}
@Override
protected void hookListeners(View view) {
view.setOnClickListener(v -> {
CategoryItem item = getContent();
item.setSelected(!item.isSelected());
checkedView.setChecked(item.isSelected());
if (listener != null) {
listener.categoryClicked(item);
}
});
}
@Override
public void render() {
CategoryItem item = getContent();
Timber.e("Rendering: %s", item);
checkedView.setChecked(item.isSelected());
checkedView.setText(item.getName());
}
}

View file

@ -5,6 +5,7 @@ import org.wikipedia.dataclient.mwapi.MwQueryResponse
import javax.inject.Inject
import javax.inject.Singleton
const val CATEGORY_PREFIX = "Category:"
/**
* Category Client to handle custom calls to Commons MediaWiki APIs
*/
@ -72,7 +73,7 @@ class CategoryClient @Inject constructor(private val categoryInterface: Category
return responseObservable
.map { it.query()?.pages() ?: emptyList() }
.map {
it.map { page -> page.title().replace("Category:", "") }
it.map { page -> page.title().replace(CATEGORY_PREFIX, "") }
}
}

View file

@ -3,6 +3,7 @@ package fr.free.nrw.commons.category;
import static android.view.View.GONE;
import static android.view.View.VISIBLE;
import static fr.free.nrw.commons.category.CategoryClientKt.CATEGORY_PREFIX;
import android.content.Intent;
import android.content.res.Configuration;
@ -17,17 +18,16 @@ import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import butterknife.BindView;
import butterknife.ButterKnife;
import com.pedrogomez.renderers.RVRendererAdapter;
import fr.free.nrw.commons.R;
import fr.free.nrw.commons.di.CommonsDaggerSupportFragment;
import fr.free.nrw.commons.explore.categories.SearchCategoriesAdapterFactory;
import fr.free.nrw.commons.explore.categories.SearchCategoriesAdapter;
import fr.free.nrw.commons.utils.NetworkUtils;
import fr.free.nrw.commons.utils.ViewUtil;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.schedulers.Schedulers;
import java.util.ArrayList;
import java.util.List;
import javax.inject.Inject;
import kotlin.Unit;
import timber.log.Timber;
/**
@ -46,16 +46,9 @@ public class SubCategoryListFragment extends CommonsDaggerSupportFragment {
private String categoryName = null;
@Inject CategoryClient categoryClient;
private RVRendererAdapter<String> categoriesAdapter;
private SearchCategoriesAdapter categoriesAdapter;
private boolean isParentCategory = true;
private final SearchCategoriesAdapterFactory adapterFactory = new SearchCategoriesAdapterFactory(item -> {
// Open SubCategory Details page
Intent intent = new Intent(getContext(), CategoryDetailsActivity.class);
intent.putExtra("categoryName", item);
getContext().startActivity(intent);
});
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,Bundle savedInstanceState) {
@ -70,8 +63,12 @@ public class SubCategoryListFragment extends CommonsDaggerSupportFragment {
else{
categoriesRecyclerView.setLayoutManager(new GridLayoutManager(getContext(), 2));
}
ArrayList<String> items = new ArrayList<>();
categoriesAdapter = adapterFactory.create(items);
categoriesAdapter = new SearchCategoriesAdapter(item->{
Intent intent = new Intent(getContext(), CategoryDetailsActivity.class);
intent.putExtra("categoryName", item);
getContext().startActivity(intent);
return Unit.INSTANCE;
});
categoriesRecyclerView.setAdapter(categoriesAdapter);
return rootView;
}
@ -88,12 +85,14 @@ public class SubCategoryListFragment extends CommonsDaggerSupportFragment {
}
progressBar.setVisibility(View.VISIBLE);
if (isParentCategory) {
compositeDisposable.add(categoryClient.getParentCategoryList("Category:"+categoryName)
compositeDisposable.add(categoryClient.getParentCategoryList(
CATEGORY_PREFIX +categoryName)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(this::handleSuccess, this::handleError));
} else {
compositeDisposable.add(categoryClient.getSubCategoryList("Category:"+categoryName)
compositeDisposable.add(categoryClient.getSubCategoryList(
CATEGORY_PREFIX +categoryName)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(this::handleSuccess, this::handleError));

View file

@ -40,6 +40,7 @@ public class DepictedImagesPresenter implements DepictedImagesContract.UserActio
* Ex: Q9394
*/
private List<Media> queryList = new ArrayList<>();
private String entityId;
@Inject
public DepictedImagesPresenter(@Named("default_preferences") JsonKvStore depictionKvStore, DepictsClient depictsClient, MediaClient mediaClient, @Named(IO_THREAD) Scheduler ioScheduler,
@ -67,6 +68,7 @@ public class DepictedImagesPresenter implements DepictedImagesContract.UserActio
@SuppressLint("CheckResult")
@Override
public void initList(String entityId) {
this.entityId = entityId;
view.setLoadingStatus(true);
view.progressBarVisible(true);
view.setIsLastPage(false);

View file

@ -17,12 +17,10 @@ import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import butterknife.BindView;
import butterknife.ButterKnife;
import com.pedrogomez.renderers.RVRendererAdapter;
import dagger.android.support.DaggerFragment;
import fr.free.nrw.commons.R;
import fr.free.nrw.commons.depictions.WikidataItemDetailsActivity;
import fr.free.nrw.commons.explore.depictions.SearchDepictionsAdapterFactory;
import fr.free.nrw.commons.explore.depictions.SearchDepictionsRenderer;
import fr.free.nrw.commons.explore.depictions.DepictionAdapter;
import fr.free.nrw.commons.upload.structure.depictions.DepictedItem;
import fr.free.nrw.commons.utils.NetworkUtils;
import fr.free.nrw.commons.utils.ViewUtil;
@ -30,6 +28,7 @@ import java.io.IOException;
import java.util.List;
import java.util.Locale;
import javax.inject.Inject;
import kotlin.Unit;
/**
* Fragment for parent classes and child classes of Depicted items in Explore
@ -48,7 +47,7 @@ public class SubDepictionListFragment extends DaggerFragment implements SubDepic
* Keeps a record of whether current instance of the fragment if of SubClass or ParentClass
*/
private boolean isParentClass = false;
private RVRendererAdapter<DepictedItem> depictionsAdapter;
private DepictionAdapter depictionsAdapter;
RecyclerView.LayoutManager layoutManager;
/**
* Stores entityId for the depiction
@ -61,20 +60,6 @@ public class SubDepictionListFragment extends DaggerFragment implements SubDepic
@Inject SubDepictionListPresenter presenter;
private final SearchDepictionsAdapterFactory adapterFactory = new SearchDepictionsAdapterFactory(new SearchDepictionsRenderer.DepictCallback() {
@Override
public void depictsClicked(DepictedItem item) {
// Open SubDepiction Details page
getActivity().finish();
WikidataItemDetailsActivity.startYourself(getContext(), item);
}
});
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
}
private void initViews() {
if (getArguments() != null) {
depictsName = getArguments().getString("wikidataItemName");
@ -115,7 +100,12 @@ public class SubDepictionListFragment extends DaggerFragment implements SubDepic
}
initViews();
depictionsRecyclerView.setLayoutManager(layoutManager);
depictionsAdapter = adapterFactory.create();
depictionsAdapter = new DepictionAdapter(depictedItem -> {
// Open SubDepiction Details page
getActivity().finish();
WikidataItemDetailsActivity.startYourself(getContext(), depictedItem);
return Unit.INSTANCE;
});
depictionsRecyclerView.setAdapter(depictionsAdapter);
return v;
}
@ -130,14 +120,7 @@ public class SubDepictionListFragment extends DaggerFragment implements SubDepic
progressBar.setVisibility(View.GONE);
depictionNotFound.setVisibility(GONE);
bottomProgressBar.setVisibility(GONE);
int itemCount=layoutManager.getItemCount();
depictionsAdapter.addAll(mediaList);
depictionsRecyclerView.getRecycledViewPool().clear();
if(itemCount!=0) {
depictionsAdapter.notifyItemRangeInserted(itemCount, mediaList.size()-1);
}else{
depictionsAdapter.notifyDataSetChanged();
}
}
@Override

View file

@ -9,7 +9,8 @@ data class Binding(
val itemLabel: SparqInfo,
val itemDescription: SparqInfo? = null
) {
val id: String by lazy { item.value.substringAfterLast("/") }
val id: String
get() = item.value.substringAfterLast("/")
}
data class SparqInfo(val type: String, val value: String)

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

@ -5,23 +5,16 @@ import android.text.TextUtils;
import android.view.View;
import android.widget.FrameLayout;
import android.widget.SearchView;
import androidx.appcompat.widget.Toolbar;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentTransaction;
import androidx.viewpager.widget.ViewPager;
import butterknife.BindView;
import butterknife.ButterKnife;
import com.google.android.material.tabs.TabLayout;
import com.jakewharton.rxbinding2.view.RxView;
import com.jakewharton.rxbinding2.widget.RxSearchView;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;
import butterknife.BindView;
import butterknife.ButterKnife;
import fr.free.nrw.commons.Media;
import fr.free.nrw.commons.R;
import fr.free.nrw.commons.category.CategoryImagesCallback;
@ -34,6 +27,10 @@ import fr.free.nrw.commons.theme.NavigationBaseActivity;
import fr.free.nrw.commons.utils.FragmentUtils;
import fr.free.nrw.commons.utils.ViewUtil;
import io.reactivex.android.schedulers.AndroidSchedulers;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;
import timber.log.Timber;
/**
* Represents search screen of this app
@ -111,7 +108,7 @@ public class SearchActivity extends NavigationBaseActivity
.takeUntil(RxView.detaches(searchView))
.debounce(500, TimeUnit.MILLISECONDS)
.observeOn(AndroidSchedulers.mainThread())
.subscribe( query -> {
.subscribe(query -> {
this.query = query.toString();
//update image list
if (!TextUtils.isEmpty(query)) {
@ -131,7 +128,7 @@ public class SearchActivity extends NavigationBaseActivity
searchCategoryFragment.updateCategoryList(query.toString());
}
}else {
} else {
//Open RecentSearchesFragment
recentSearchesFragment.updateRecentSearches();
viewPager.setVisibility(View.GONE);
@ -139,7 +136,7 @@ public class SearchActivity extends NavigationBaseActivity
setSearchHistoryFragment();
searchHistoryContainer.setVisibility(View.VISIBLE);
}
}
}, Timber::e
));
}

View file

@ -0,0 +1,9 @@
package fr.free.nrw.commons.explore.categories
import fr.free.nrw.commons.upload.categories.BaseDelegateAdapter
class SearchCategoriesAdapter(onCateoryClicked: (String) -> Unit) : BaseDelegateAdapter<String>(
searchCategoryDelegate(onCateoryClicked),
areItemsTheSame = { oldItem, newItem -> oldItem == newItem }
)

View file

@ -0,0 +1,15 @@
package fr.free.nrw.commons.explore.categories
import com.hannesdorfmann.adapterdelegates4.dsl.adapterDelegateLayoutContainer
import fr.free.nrw.commons.R
import fr.free.nrw.commons.category.CATEGORY_PREFIX
import kotlinx.android.synthetic.main.item_recent_searches.*
fun searchCategoryDelegate(onCategoryClicked: (String) -> Unit) =
adapterDelegateLayoutContainer<String, String>(R.layout.item_recent_searches) {
containerView.setOnClickListener { onCategoryClicked(item) }
bind {
textView1.text = item.substringAfter(CATEGORY_PREFIX)
}
}

View file

@ -1,32 +0,0 @@
package fr.free.nrw.commons.explore.categories;
import com.pedrogomez.renderers.ListAdapteeCollection;
import com.pedrogomez.renderers.RVRendererAdapter;
import com.pedrogomez.renderers.RendererBuilder;
import java.util.Collections;
import java.util.List;
/**
* This class helps in creating adapter for categoriesRecyclerView in SearchCategoryFragment,
* implementing onClicks on categoriesRecyclerView Items
**/
public class SearchCategoriesAdapterFactory {
private final SearchCategoriesRenderer.CategoryClickedListener listener;
public SearchCategoriesAdapterFactory(SearchCategoriesRenderer.CategoryClickedListener listener) {
this.listener = listener;
}
/**
* This method creates a recyclerViewAdapter for Categories.
* @param searchImageItemList List of category name to be displayed
* @return categoriesAdapter
**/
public RVRendererAdapter<String> create(List<String> searchImageItemList) {
RendererBuilder<String> builder = new RendererBuilder<String>().bind(String.class, new SearchCategoriesRenderer(listener));
ListAdapteeCollection<String> collection = new ListAdapteeCollection<>(
searchImageItemList != null ? searchImageItemList : Collections.<String>emptyList());
return new RVRendererAdapter<>(builder, collection);
}
}

View file

@ -1,56 +0,0 @@
package fr.free.nrw.commons.explore.categories;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import com.pedrogomez.renderers.Renderer;
import butterknife.BindView;
import butterknife.ButterKnife;
import fr.free.nrw.commons.R;
/**
* presentation logic of individual category in search is handled here
**/
class SearchCategoriesRenderer extends Renderer<String> {
@BindView(R.id.textView1) TextView tvCategoryName;
private final CategoryClickedListener listener;
SearchCategoriesRenderer(CategoryClickedListener listener) {
this.listener = listener;
}
@Override
protected View inflate(LayoutInflater layoutInflater, ViewGroup viewGroup) {
return layoutInflater.inflate(R.layout.item_recent_searches, viewGroup, false);
}
@Override
protected void setUpView(View view) {
ButterKnife.bind(this, view);
}
@Override
protected void hookListeners(View view) {
view.setOnClickListener(v -> {
String item = getContent();
if (listener != null) {
listener.categoryClicked(item);
}
});
}
@Override
public void render() {
String item = getContent();
tvCategoryName.setText(item.replaceFirst("^Category:", ""));
}
public interface CategoryClickedListener {
void categoryClicked(String item);
}
}

View file

@ -16,7 +16,6 @@ import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import butterknife.BindView;
import butterknife.ButterKnife;
import com.pedrogomez.renderers.RVRendererAdapter;
import fr.free.nrw.commons.R;
import fr.free.nrw.commons.category.CategoryClient;
import fr.free.nrw.commons.category.CategoryDetailsActivity;
@ -33,6 +32,7 @@ import java.util.Date;
import java.util.List;
import javax.inject.Inject;
import javax.inject.Named;
import kotlin.Unit;
import timber.log.Timber;
/**
@ -59,16 +59,9 @@ public class SearchCategoryFragment extends CommonsDaggerSupportFragment {
@Named("default_preferences")
JsonKvStore basicKvStore;
private RVRendererAdapter<String> categoriesAdapter;
private SearchCategoriesAdapter categoriesAdapter;
private List<String> queryList = new ArrayList<>();
private final SearchCategoriesAdapterFactory adapterFactory = new SearchCategoriesAdapterFactory(item -> {
// Called on Click of a individual category Item
// Open Category Details activity
CategoryDetailsActivity.startYourself(getContext(), item);
saveQuery(query);
});
/**
* This method saves Search Query in the Recent Searches Database.
* @param query
@ -98,8 +91,11 @@ public class SearchCategoryFragment extends CommonsDaggerSupportFragment {
else{
categoriesRecyclerView.setLayoutManager(new GridLayoutManager(getContext(), 2));
}
ArrayList<String> items = new ArrayList<>();
categoriesAdapter = adapterFactory.create(items);
categoriesAdapter = new SearchCategoriesAdapter(item -> {
CategoryDetailsActivity.startYourself(getContext(), item);
saveQuery(query);
return Unit.INSTANCE;
});
categoriesRecyclerView.setAdapter(categoriesAdapter);
categoriesRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
@Override
@ -141,7 +137,9 @@ public class SearchCategoryFragment extends CommonsDaggerSupportFragment {
* Adds 25 more results to existing search results
*/
public void addCategoriesToList(String query) {
if(isLoadingCategories) return;
if(isLoadingCategories) {
return;
}
isLoadingCategories=true;
this.query = query;
bottomProgressBar.setVisibility(View.VISIBLE);
@ -161,7 +159,6 @@ public class SearchCategoryFragment extends CommonsDaggerSupportFragment {
progressBar.setVisibility(View.GONE);
bottomProgressBar.setVisibility(GONE);
categoriesAdapter.addAll(mediaList);
categoriesAdapter.notifyDataSetChanged();
isLoadingCategories=false;
}
@ -181,7 +178,6 @@ public class SearchCategoryFragment extends CommonsDaggerSupportFragment {
bottomProgressBar.setVisibility(View.GONE);
progressBar.setVisibility(GONE);
categoriesAdapter.addAll(mediaList);
categoriesAdapter.notifyDataSetChanged();
}
}

View file

@ -0,0 +1,12 @@
package fr.free.nrw.commons.explore.depictions
import fr.free.nrw.commons.upload.categories.BaseDelegateAdapter
import fr.free.nrw.commons.upload.structure.depictions.DepictedItem
class DepictionAdapter(clickListener: (DepictedItem) -> Unit) : BaseDelegateAdapter<DepictedItem>(
depictionDelegate(clickListener),
areItemsTheSame = { oldItem, newItem -> oldItem.id == newItem.id }
)

View file

@ -0,0 +1,21 @@
package fr.free.nrw.commons.explore.depictions
import com.hannesdorfmann.adapterdelegates4.dsl.adapterDelegateLayoutContainer
import fr.free.nrw.commons.R
import fr.free.nrw.commons.upload.structure.depictions.DepictedItem
import kotlinx.android.synthetic.main.item_depictions.*
fun depictionDelegate(onDepictionClicked: (DepictedItem) -> Unit) =
adapterDelegateLayoutContainer<DepictedItem, DepictedItem>(R.layout.item_depictions) {
containerView.setOnClickListener { onDepictionClicked(item) }
bind {
depicts_label.text = item.name
description.text = item.description
if (item.imageUrl?.isNotBlank() == true) {
depicts_image.setImageURI(item.imageUrl)
} else {
depicts_image.setActualImageResource(R.drawable.ic_wikidata_logo_24dp)
}
}
}

View file

@ -3,7 +3,6 @@ package fr.free.nrw.commons.explore.depictions
import fr.free.nrw.commons.BuildConfig
import fr.free.nrw.commons.Media
import fr.free.nrw.commons.depictions.models.DepictionResponse
import fr.free.nrw.commons.depictions.subClass.models.Binding
import fr.free.nrw.commons.depictions.subClass.models.SparqlResponse
import fr.free.nrw.commons.media.MediaInterface
import fr.free.nrw.commons.upload.depicts.DepictsInterface
@ -20,8 +19,9 @@ import java.util.*
import javax.inject.Inject
import javax.inject.Singleton
const val LARGE_IMAGE_SIZE="640px"
const val THUMB_IMAGE_SIZE="70px"
const val LARGE_IMAGE_SIZE = "640px"
const val THUMB_IMAGE_SIZE = "70px"
/**
* Depicts Client to handle custom calls to Commons Wikibase APIs
*/
@ -79,7 +79,11 @@ class DepictsClient @Inject constructor(
}
fun toDepictions(sparqlResponse: Observable<SparqlResponse>): Observable<List<DepictedItem>> {
return sparqlResponse.map { it.results.bindings.joinToString("|", transform = Binding::id) }
return sparqlResponse.map {
it.results.bindings.joinToString("|") { binding ->
binding.id
}
}
.flatMap { getEntities(it).toObservable() }
.map { it.entities().values.map(::DepictedItem) }
}

View file

@ -1,31 +0,0 @@
package fr.free.nrw.commons.explore.depictions;
import com.pedrogomez.renderers.ListAdapteeCollection;
import com.pedrogomez.renderers.RVRendererAdapter;
import com.pedrogomez.renderers.RendererBuilder;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import fr.free.nrw.commons.upload.structure.depictions.DepictedItem;
/**
* Adapter factory for Items in Explore
*/
public class SearchDepictionsAdapterFactory {
private final SearchDepictionsRenderer.DepictCallback listener;
public SearchDepictionsAdapterFactory(SearchDepictionsRenderer.DepictCallback listener) {
this.listener = listener;
}
public RVRendererAdapter<DepictedItem> create() {
List<DepictedItem> searchImageItemList = new ArrayList<>();
RendererBuilder<DepictedItem> builder = new RendererBuilder<DepictedItem>().bind(DepictedItem.class, new SearchDepictionsRenderer(listener));
ListAdapteeCollection<DepictedItem> collection = new ListAdapteeCollection<>(
searchImageItemList != null ? searchImageItemList : Collections.<DepictedItem>emptyList());
return new RVRendererAdapter<>(builder, collection);
}
}

View file

@ -2,6 +2,7 @@ package fr.free.nrw.commons.explore.depictions;
import static android.view.View.GONE;
import static android.view.View.VISIBLE;
import static fr.free.nrw.commons.explore.depictions.DepictionAdapterDelegatesKt.depictionDelegate;
import android.content.Context;
import android.content.res.Configuration;
@ -12,12 +13,13 @@ import android.view.ViewGroup;
import android.widget.ProgressBar;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.DiffUtil.ItemCallback;
import androidx.recyclerview.widget.GridLayoutManager;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import butterknife.BindView;
import butterknife.ButterKnife;
import com.pedrogomez.renderers.RVRendererAdapter;
import com.hannesdorfmann.adapterdelegates4.AsyncListDifferDelegationAdapter;
import fr.free.nrw.commons.R;
import fr.free.nrw.commons.depictions.WikidataItemDetailsActivity;
import fr.free.nrw.commons.di.CommonsDaggerSupportFragment;
@ -28,6 +30,7 @@ import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import javax.inject.Inject;
import kotlin.Unit;
/**
* Display depictions in search fragment
@ -42,24 +45,17 @@ public class SearchDepictionsFragment extends CommonsDaggerSupportFragment imple
TextView depictionNotFound;
@BindView(R.id.bottomProgressBar)
ProgressBar bottomProgressBar;
RecyclerView.LayoutManager layoutManager;
private RecyclerView.LayoutManager layoutManager;
private boolean isLoading = true;
private int PAGE_SIZE = 25;
private final int PAGE_SIZE = 25;
@Inject
SearchDepictionsFragmentPresenter presenter;
private final SearchDepictionsAdapterFactory adapterFactory = new SearchDepictionsAdapterFactory(new SearchDepictionsRenderer.DepictCallback() {
@Override
public void depictsClicked(DepictedItem item) {
WikidataItemDetailsActivity.startYourself(getContext(), item);
presenter.saveQuery();
}
});
private RVRendererAdapter<DepictedItem> depictionsAdapter;
private DepictionAdapter depictionsAdapter;
private boolean isLastPage;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_browse_image, container, false);
public View onCreateView(final LayoutInflater inflater, final ViewGroup container, final Bundle savedInstanceState) {
final View rootView = inflater.inflate(R.layout.fragment_browse_image, container, false);
ButterKnife.bind(this, rootView);
if (getActivity().getResources().getConfiguration().orientation
== Configuration.ORIENTATION_PORTRAIT) {
@ -68,20 +64,25 @@ public class SearchDepictionsFragment extends CommonsDaggerSupportFragment imple
layoutManager = new GridLayoutManager(getContext(), 2);
}
depictionsRecyclerView.setLayoutManager(layoutManager);
depictionsAdapter = adapterFactory.create();
depictionsAdapter = new DepictionAdapter(
depictedItem -> {
WikidataItemDetailsActivity.startYourself(getContext(), depictedItem);
presenter.saveQuery();
return Unit.INSTANCE;
});
depictionsRecyclerView.setAdapter(depictionsAdapter);
depictionsRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
@Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
public void onScrollStateChanged(final RecyclerView recyclerView, final int newState) {
super.onScrollStateChanged(recyclerView, newState);
}
@Override
public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) {
public void onScrolled(@NonNull final RecyclerView recyclerView, final int dx, final int dy) {
super.onScrolled(recyclerView, dx, dy);
int visibleItemCount = layoutManager.getChildCount();
int totalItemCount = layoutManager.getItemCount();
final int visibleItemCount = layoutManager.getChildCount();
final int totalItemCount = layoutManager.getItemCount();
int firstVisibleItemPosition=0;
if(layoutManager instanceof GridLayoutManager){
firstVisibleItemPosition=((GridLayoutManager) layoutManager).findFirstVisibleItemPosition();
@ -108,12 +109,12 @@ public class SearchDepictionsFragment extends CommonsDaggerSupportFragment imple
/**
* Fetch PAGE_SIZE number of items
*/
private void loadMoreItems(boolean reInitialise) {
private void loadMoreItems(final boolean reInitialise) {
presenter.updateDepictionList(presenter.getQuery(),PAGE_SIZE, reInitialise);
}
@Override
public void onAttach(Context context) {
public void onAttach(final Context context) {
super.onAttach(context);
presenter.onAttachView(this);
}
@ -124,7 +125,7 @@ public class SearchDepictionsFragment extends CommonsDaggerSupportFragment imple
*
* @param query string searched in the Explore Activity
*/
public void updateDepictionList(String query) {
public void updateDepictionList(final String query) {
presenter.initializeQuery(query);
if (!NetworkUtils.isInternetConnectionEstablished(getContext())) {
handleNoInternet();
@ -142,22 +143,10 @@ public class SearchDepictionsFragment extends CommonsDaggerSupportFragment imple
progressBar.setVisibility(GONE);
bottomProgressBar.setVisibility(GONE);
depictionNotFound.setVisibility(VISIBLE);
String no_depiction = getString(R.string.depictions_not_found);
final String no_depiction = getString(R.string.depictions_not_found);
depictionNotFound.setText(String.format(Locale.getDefault(), no_depiction, presenter.getQuery()));
}
@Override
public void onDestroy() {
super.onDestroy();
}
@Override
public void onResume() {
super.onResume();
depictionsAdapter.clear();
depictionsRecyclerView.cancelPendingInputEvents();
}
/**
* Handles the UI updates for no internet scenario
*/
@ -172,24 +161,18 @@ public class SearchDepictionsFragment extends CommonsDaggerSupportFragment imple
* like hiding empty labels, hiding progressbar and notifying the apdapter that list of items has been fetched from the API
*/
@Override
public void onSuccess(List<DepictedItem> mediaList) {
public void onSuccess(final List<DepictedItem> mediaList) {
isLoading = false;
progressBar.setVisibility(View.GONE);
progressBar.setVisibility(GONE);
depictionNotFound.setVisibility(GONE);
bottomProgressBar.setVisibility(GONE);
int itemCount = layoutManager.getItemCount();
depictionsAdapter.addAll(mediaList);
if(itemCount!=0) {
depictionsAdapter.notifyItemRangeInserted(itemCount, mediaList.size()-1);
}else{
depictionsAdapter.notifyDataSetChanged();
}
}
@Override
public void loadingDepictions(boolean isLoading) {
public void loadingDepictions(final boolean isLoading) {
depictionNotFound.setVisibility(GONE);
bottomProgressBar.setVisibility(View.VISIBLE);
bottomProgressBar.setVisibility(VISIBLE);
progressBar.setVisibility(GONE);
this.isLoading = isLoading;
}
@ -204,18 +187,13 @@ public class SearchDepictionsFragment extends CommonsDaggerSupportFragment imple
ViewUtil.showShortSnackbar(depictionsRecyclerView, R.string.error_loading_depictions);
}
@Override
public RVRendererAdapter<DepictedItem> getAdapter() {
return depictionsAdapter;
}
/**
* Inform the view that there are no more items to be loaded for this search query
* or reset the isLastPage for the current query
* @param isLastPage
*/
@Override
public void setIsLastPage(boolean isLastPage) {
public void setIsLastPage(final boolean isLastPage) {
this.isLastPage=isLastPage;
progressBar.setVisibility(GONE);
}

View file

@ -1,11 +1,8 @@
package fr.free.nrw.commons.explore.depictions;
import com.pedrogomez.renderers.RVRendererAdapter;
import java.util.List;
import fr.free.nrw.commons.BasePresenter;
import fr.free.nrw.commons.upload.structure.depictions.DepictedItem;
import java.util.List;
/**
* The contract with with SearchDepictionsFragment and its presenter would talk to each other
@ -44,11 +41,6 @@ public interface SearchDepictionsFragmentContract {
*/
void showSnackbar();
/**
* @return adapter
*/
RVRendererAdapter<DepictedItem> getAdapter();
/**
* Inform the view that there are no more items to be loaded for this search query
* or reset the isLastPage for the current query

View file

@ -1,112 +0,0 @@
package fr.free.nrw.commons.explore.depictions;
import android.graphics.Bitmap;
import android.net.Uri;
import android.text.TextUtils;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import androidx.annotation.Nullable;
import butterknife.BindView;
import butterknife.ButterKnife;
import com.facebook.common.executors.CallerThreadExecutor;
import com.facebook.common.references.CloseableReference;
import com.facebook.datasource.DataSource;
import com.facebook.drawee.backends.pipeline.Fresco;
import com.facebook.imagepipeline.core.ImagePipeline;
import com.facebook.imagepipeline.datasource.BaseBitmapDataSubscriber;
import com.facebook.imagepipeline.image.CloseableImage;
import com.facebook.imagepipeline.request.ImageRequest;
import com.facebook.imagepipeline.request.ImageRequestBuilder;
import com.pedrogomez.renderers.Renderer;
import fr.free.nrw.commons.R;
import fr.free.nrw.commons.upload.structure.depictions.DepictedItem;
/**
* Renderer for DepictedItem
*/
public class SearchDepictionsRenderer extends Renderer<DepictedItem> {
@BindView(R.id.depicts_label)
TextView tvDepictionLabel;
@BindView(R.id.description)
TextView tvDepictionDesc;
@BindView(R.id.depicts_image)
ImageView imageView;
private DepictCallback listener;
public SearchDepictionsRenderer(DepictCallback 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();
if (listener != null) {
listener.depictsClicked(item);
}
});
}
@Override
protected View inflate(LayoutInflater inflater, ViewGroup parent) {
return inflater.inflate(R.layout.item_depictions, parent, false);
}
/**
* Render value to all the items in the search depictions list
*/
@Override
public void render() {
DepictedItem item = getContent();
tvDepictionLabel.setText(item.getName());
tvDepictionDesc.setText(item.getDescription());
imageView.setImageDrawable(getContext().getResources().getDrawable(R.drawable.ic_wikidata_logo_24dp));
if (!TextUtils.isEmpty(item.getImageUrl())) {
ImageRequest imageRequest = ImageRequestBuilder
.newBuilderWithSource(Uri.parse(item.getImageUrl()))
.setAutoRotateEnabled(true)
.build();
ImagePipeline imagePipeline = Fresco.getImagePipeline();
final DataSource<CloseableReference<CloseableImage>>
dataSource = imagePipeline.fetchDecodedImage(imageRequest, getContext());
dataSource.subscribe(new BaseBitmapDataSubscriber() {
@Override
public void onNewResultImpl(@Nullable Bitmap bitmap) {
if (dataSource.isFinished() && bitmap != null) {
//imageView.setImageBitmap(Bitmap.createBitmap(bitmap));
imageView.post(() -> imageView.setImageBitmap(Bitmap.createBitmap(bitmap)));
dataSource.close();
}
}
@Override
public void onFailureImpl(DataSource dataSource) {
if (dataSource != null) {
dataSource.close();
}
}
}, CallerThreadExecutor.getInstance());
}
}
public interface DepictCallback {
void depictsClicked(DepictedItem item);
}
}

View file

@ -17,7 +17,6 @@ import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import butterknife.BindView;
import butterknife.ButterKnife;
import com.pedrogomez.renderers.RVRendererAdapter;
import fr.free.nrw.commons.Media;
import fr.free.nrw.commons.R;
import fr.free.nrw.commons.di.CommonsDaggerSupportFragment;
@ -33,9 +32,9 @@ import io.reactivex.schedulers.Schedulers;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.concurrent.TimeUnit;
import javax.inject.Inject;
import javax.inject.Named;
import kotlin.Unit;
import timber.log.Timber;
/**
@ -66,16 +65,9 @@ public class SearchImageFragment extends CommonsDaggerSupportFragment {
*/
private int mediaSize = 0;
private RVRendererAdapter<Media> imagesAdapter;
private SearchImagesAdapter imagesAdapter;
private List<Media> queryList = new ArrayList<>();
private final SearchImagesAdapterFactory adapterFactory = new SearchImagesAdapterFactory(item -> {
// Called on Click of a individual media Item
int index = queryList.indexOf(item);
((SearchActivity)getContext()).onSearchImageClicked(index);
saveQuery(query);
});
/**
* This method saves Search Query in the Recent Searches Database.
* @param query
@ -106,8 +98,11 @@ public class SearchImageFragment extends CommonsDaggerSupportFragment {
else{
imagesRecyclerView.setLayoutManager(new GridLayoutManager(getContext(), 2));
}
ArrayList<Media> items = new ArrayList<>();
imagesAdapter = adapterFactory.create(items);
imagesAdapter =new SearchImagesAdapter(media -> {
((SearchActivity)getContext()).onSearchImageClicked(imagesAdapter.getItems().indexOf(media));
saveQuery(query);
return Unit.INSTANCE;
});
imagesRecyclerView.setAdapter(imagesAdapter);
imagesRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
@Override
@ -173,7 +168,6 @@ public class SearchImageFragment extends CommonsDaggerSupportFragment {
if (mediaList.size() != 0 && !queryList.get(queryList.size() - 1).getFilename().equals(mediaList.get(mediaList.size() - 1).getFilename())) {
queryList.addAll(mediaList);
imagesAdapter.addAll(mediaList);
imagesAdapter.notifyDataSetChanged();
((SearchActivity) getContext()).viewPagerNotifyDataSetChanged();
}
}
@ -222,8 +216,7 @@ public class SearchImageFragment extends CommonsDaggerSupportFragment {
private void handleLabelforImage(String s, int position) {
if (!s.trim().equals(getString(R.string.detail_caption_empty))) {
imagesAdapter.getItem(position).setThumbnailTitle(s);
imagesAdapter.notifyDataSetChanged();
imagesAdapter.updateThumbnail(position, s);
}
}
@ -281,13 +274,11 @@ public class SearchImageFragment extends CommonsDaggerSupportFragment {
* @param i position of Media in the recyclerview adapter.
*/
public Media getImageAtPosition(int i) {
if (imagesAdapter.getItem(i).getFilename() == null) {
if (imagesAdapter.getItemAt(i).getFilename() == null) {
// not yet ready to return data
return null;
}
else {
return imagesAdapter.getItem(i);
}
return imagesAdapter.getItemAt(i);
}
@Override public void onDestroyView() {

View file

@ -0,0 +1,27 @@
package fr.free.nrw.commons.explore.images
import com.hannesdorfmann.adapterdelegates4.ListDelegationAdapter
import fr.free.nrw.commons.Media
class SearchImagesAdapter(onImageClicked: (Media) -> Unit) : ListDelegationAdapter<List<Media>>(
searchImagesAdapter(onImageClicked)
) {
fun getItemAt(position: Int) = items[position]
init {
items = emptyList()
}
fun clear() {
items = emptyList()
}
fun addAll(mediaList: List<Media>) {
items = items + mediaList
}
fun updateThumbnail(position: Int, thumbnailTitle: String) {
items[position].thumbnailTitle = thumbnailTitle
notifyItemChanged(position)
}
}

View file

@ -0,0 +1,24 @@
package fr.free.nrw.commons.explore.images
import android.view.View
import com.hannesdorfmann.adapterdelegates4.dsl.adapterDelegateLayoutContainer
import fr.free.nrw.commons.Media
import fr.free.nrw.commons.R
import kotlinx.android.synthetic.main.layout_category_images.*
fun searchImagesAdapter(onImageClicked: (Media) -> Unit) =
adapterDelegateLayoutContainer<Media, Media>(R.layout.layout_category_images) {
categoryImageView.setOnClickListener { onImageClicked(item) }
bind {
categoryImageTitle.text = item.thumbnailTitle
categoryImageView.setImageURI(item.thumbUrl)
if (item.creator?.isNotEmpty() == true) {
categoryImageAuthor.visibility = View.VISIBLE
categoryImageAuthor.text = getString(R.string.image_uploaded_by, item.creator)
} else {
categoryImageAuthor.visibility = View.GONE
}
}
}

View file

@ -1,35 +0,0 @@
package fr.free.nrw.commons.explore.images;
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.Media;
/**
* This class helps in creating adapter for imagesRecyclerView in SearchImagesFragment,
* implementing onClicks on imagesRecyclerView Items
**/
class SearchImagesAdapterFactory {
private final SearchImagesRenderer.ImageClickedListener listener;
SearchImagesAdapterFactory(SearchImagesRenderer.ImageClickedListener listener) {
this.listener = listener;
}
/**
* This method creates a recyclerViewAdapter for Media.
* @param searchImageItemList List of Media objects to be displayed
* @return imagesAdapter
**/
public RVRendererAdapter<Media> create(List<Media> searchImageItemList) {
RendererBuilder<Media> builder = new RendererBuilder<Media>()
.bind(Media.class, new SearchImagesRenderer(listener));
ListAdapteeCollection<Media> collection = new ListAdapteeCollection<>(
searchImageItemList != null ? searchImageItemList : Collections.<Media>emptyList());
return new RVRendererAdapter<>(builder, collection);
}
}

View file

@ -1,74 +0,0 @@
package fr.free.nrw.commons.explore.images;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import com.facebook.drawee.view.SimpleDraweeView;
import com.pedrogomez.renderers.Renderer;
import butterknife.BindView;
import butterknife.ButterKnife;
import fr.free.nrw.commons.Media;
import fr.free.nrw.commons.R;
/**
* presentation logic of individual image in search is handled here
**/
class SearchImagesRenderer extends Renderer<Media> {
@BindView(R.id.categoryImageTitle) TextView tvImageName;
@BindView(R.id.categoryImageAuthor) TextView categoryImageAuthor;
@BindView(R.id.categoryImageView) SimpleDraweeView browseImage;
private final ImageClickedListener listener;
SearchImagesRenderer(ImageClickedListener listener) {
this.listener = listener;
}
@Override
protected View inflate(LayoutInflater layoutInflater, ViewGroup viewGroup) {
return layoutInflater.inflate(R.layout.layout_category_images, viewGroup, false);
}
@Override
protected void setUpView(View view) {
ButterKnife.bind(this, view);
}
@Override
protected void hookListeners(View view) {
view.setOnClickListener(v -> {
Media item = getContent();
if (listener != null) {
listener.imageClicked(item);
}
});
}
@Override
public void render() {
Media item = getContent();
tvImageName.setText(item.getThumbnailTitle());
browseImage.setImageURI(item.getThumbUrl());
setAuthorView(item, categoryImageAuthor);
}
interface ImageClickedListener {
void imageClicked(Media item);
}
/**
* formats author name as "Uploaded by: authorName" and sets it in textview
*/
private void setAuthorView(Media item, TextView author) {
if (item.getCreator() != null && !item.getCreator().equals("")) {
author.setVisibility(View.VISIBLE);
String uploadedByTemplate = getContext().getString(R.string.image_uploaded_by);
author.setText(String.format(uploadedByTemplate, item.getCreator()));
} else {
author.setVisibility(View.GONE);
}
}
}

View file

@ -2,6 +2,7 @@ package fr.free.nrw.commons.media;
import static android.view.View.GONE;
import static android.view.View.VISIBLE;
import static fr.free.nrw.commons.category.CategoryClientKt.CATEGORY_PREFIX;
import android.annotation.SuppressLint;
import android.app.AlertDialog;
@ -49,7 +50,6 @@ import fr.free.nrw.commons.delete.ReasonBuilder;
import fr.free.nrw.commons.depictions.WikidataItemDetailsActivity;
import fr.free.nrw.commons.di.CommonsDaggerSupportFragment;
import fr.free.nrw.commons.ui.widget.HtmlTextView;
import fr.free.nrw.commons.upload.structure.depictions.DepictedItem;
import fr.free.nrw.commons.utils.ViewUtilWrapper;
import io.reactivex.Single;
import io.reactivex.android.schedulers.AndroidSchedulers;
@ -619,7 +619,7 @@ public class MediaDetailFragment extends CommonsDaggerSupportFragment {
if (categoriesLoaded && categoriesPresent) {
textView.setOnClickListener(view -> {
// Open Category Details page
String selectedCategoryTitle = "Category:" + catName;
String selectedCategoryTitle = CATEGORY_PREFIX + catName;
Intent intent = new Intent(getContext(), CategoryDetailsActivity.class);
intent.putExtra("categoryName", selectedCategoryTitle);
getContext().startActivity(intent);

View file

@ -1,25 +1,23 @@
package fr.free.nrw.commons.mwapi;
import static fr.free.nrw.commons.category.CategoryClientKt.CATEGORY_PREFIX;
import com.google.gson.Gson;
import org.wikipedia.dataclient.mwapi.MwQueryPage;
import org.wikipedia.dataclient.mwapi.MwQueryResponse;
import io.reactivex.Single;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import javax.inject.Inject;
import javax.inject.Named;
import io.reactivex.Single;
import okhttp3.HttpUrl;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import okhttp3.ResponseBody;
import org.wikipedia.dataclient.mwapi.MwQueryPage;
import org.wikipedia.dataclient.mwapi.MwQueryResponse;
import timber.log.Timber;
/**
@ -60,7 +58,7 @@ public class CategoryApi {
for (MwQueryPage page : apiResponse.query().pages()) {
if (page.categories() != null) {
for (MwQueryPage.Category category : page.categories()) {
categories.add(category.title().replace("Category:", ""));
categories.add(category.title().replace(CATEGORY_PREFIX, ""));
}
}
}

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) }
bookmarkButtonImage.setOnClickListener {
val isBookmarked = bookmarkLocationDao.updateBookmarkLocation(item)
bookmarkButtonImage.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
bookmarkButtonImage.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,289 +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.bookmarkButtonImage) 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);
}
});
bookmarkButtonImage.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)).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

@ -9,7 +9,6 @@ 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;
@ -39,6 +38,8 @@ import android.widget.Toast;
import androidx.annotation.DrawableRes;
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;
@ -68,7 +69,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.CommonsApplication;
import fr.free.nrw.commons.R;
import fr.free.nrw.commons.Utils;
@ -76,6 +76,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 +84,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;
@ -105,12 +105,14 @@ 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;
@ -163,6 +165,8 @@ public class NearbyParentFragment extends CommonsDaggerSupportFragment
@Inject
SystemThemeUtils systemThemeUtils;
@Inject
CommonPlaceClickActions commonPlaceClickActions;
private NearbyFilterSearchRecyclerViewAdapter nearbyFilterSearchRecyclerViewAdapter;
@ -176,7 +180,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;
@ -188,25 +192,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();
@ -217,7 +218,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);
@ -227,28 +228,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)
@ -280,8 +281,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() {
@ -291,13 +302,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();
@ -324,9 +335,10 @@ public class NearbyParentFragment extends CommonsDaggerSupportFragment
}
private void registerNetworkReceiver() {
if (getActivity() != null)
if (getActivity() != null) {
getActivity().registerReceiver(broadcastReceiver, intentFilter);
}
}
@Override
@ -348,7 +360,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
}
@ -361,7 +373,7 @@ public class NearbyParentFragment extends CommonsDaggerSupportFragment
}
@Override
public void onSaveInstanceState(Bundle outState) {
public void onSaveInstanceState(final Bundle outState) {
super.onSaveInstanceState(outState);
mapView.onSaveInstanceState(outState);
}
@ -414,7 +426,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);
@ -426,7 +438,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);
}
@ -456,7 +468,7 @@ public class NearbyParentFragment extends CommonsDaggerSupportFragment
}
@Override
public void setCheckBoxState(int state) {
public void setCheckBoxState(final int state) {
checkBoxTriStates.setState(state);
}
@ -511,12 +523,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) {
}
});
@ -536,14 +548,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) {
}
});
@ -597,21 +609,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(),
@ -623,22 +635,17 @@ public class NearbyParentFragment extends CommonsDaggerSupportFragment
}
@Override
public void updateListFragment(List<Place> placeList) {
adapterFactory.updateAdapterData(placeList, (RVRendererAdapter<Place>) rvNearbyList.getAdapter());
noResultsView.setVisibility(placeList.size() <= 0 ? View.VISIBLE : View.GONE);
public void updateListFragment(final List<Place> placeList) {
adapter.setItems(placeList);
noResultsView.setVisibility(placeList.isEmpty() ? View.VISIBLE : View.GONE);
}
public void clearNearbyList() {
adapterFactory.clear((RVRendererAdapter<Place>) rvNearbyList.getAdapter());
adapter.clear();
}
public void updateNearbyList() {
adapterFactory.update((RVRendererAdapter<Place>) rvNearbyList.getAdapter());
noResultsView.setVisibility(rvNearbyList.getAdapter().getItemCount() <= 0 ? View.VISIBLE : View.GONE);
}
public void addPlaceToNearbyList(Place place) {
adapterFactory.add(place, (RVRendererAdapter<Place>) rvNearbyList.getAdapter());
public void addPlaceToNearbyList(final Place place) {
adapter.add(place);
}
@Override
@ -678,7 +685,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) {
@ -719,10 +726,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 {
@ -730,8 +734,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())
@ -749,8 +753,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())
@ -772,8 +776,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();
}
@ -804,7 +807,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 {
@ -814,11 +817,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
@ -836,7 +835,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 {
@ -850,7 +849,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,
@ -885,7 +884,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);
@ -913,7 +912,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);
@ -940,19 +939,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);
@ -960,7 +959,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);
@ -968,7 +967,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);
@ -986,7 +985,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);
@ -995,12 +994,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);
}
@ -1026,27 +1025,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));
@ -1071,9 +1070,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);
@ -1083,7 +1082,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);
@ -1096,7 +1095,6 @@ public class NearbyParentFragment extends CommonsDaggerSupportFragment
@Override
public void filterOutAllMarkers() {
hideAllMarkers();
updateNearbyList();
}
/**
@ -1104,10 +1102,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();
}
/**
@ -1119,17 +1116,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
@ -1159,7 +1156,6 @@ public class NearbyParentFragment extends CommonsDaggerSupportFragment
updateMarker(markerPlaceGroup.getIsBookmarked(), place, NearbyController.currentLocation);
}
}
updateNearbyList();
}
@Override
@ -1173,7 +1169,7 @@ 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 = VectorDrawableCompat.create(
@ -1182,13 +1178,13 @@ public class NearbyParentFragment extends CommonsDaggerSupportFragment
for (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(
@ -1224,11 +1220,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));
}
@ -1237,7 +1233,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);
@ -1245,7 +1241,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) {
@ -1263,7 +1259,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();
@ -1271,7 +1267,7 @@ public class NearbyParentFragment extends CommonsDaggerSupportFragment
return;
}
addCurrentLocationMarker(curLatLng);
CameraPosition position;
final CameraPosition position;
if (ViewUtil.isPortrait(getActivity())) {
position = new CameraPosition.Builder()
@ -1309,8 +1305,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);
@ -1331,10 +1327,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);
@ -1351,16 +1347,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) {
@ -1370,8 +1368,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;
}
@ -1382,36 +1380,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());
}
@ -1419,20 +1418,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 {
@ -1444,7 +1443,7 @@ public class NearbyParentFragment extends CommonsDaggerSupportFragment
}
@Override
public void onAttach(Context context) {
public void onAttach(final Context context) {
super.onAttach(context);
wikidataEditListener.setAuthenticationStateListener(this);
}
@ -1462,11 +1461,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();
@ -1477,14 +1476,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

@ -0,0 +1,9 @@
package fr.free.nrw.commons.notification
import fr.free.nrw.commons.upload.categories.BaseDelegateAdapter
internal class NotificatinAdapter(onNotificationClicked: (Notification) -> Unit) :
BaseDelegateAdapter<Notification>(
notificationDelegate(onNotificationClicked),
areItemsTheSame = { oldItem, newItem -> oldItem.notificationId == newItem.notificationId }
)

View file

@ -11,16 +11,6 @@ data class Notification(var notificationType: NotificationType,
var link: String,
var iconUrl: String,
var notificationId: String) {
override fun toString(): String {
return "Notification" +
"notificationType='" + notificationType + '\'' +
", notificationText='" + notificationText + '\'' +
", date='" + date + '\'' +
", link='" + link + '\'' +
", iconUrl='" + iconUrl + '\'' +
", notificationId='" + notificationId + '\'' +
'}'
}
companion object {
@JvmStatic

View file

@ -13,23 +13,13 @@ import android.widget.ProgressBar;
import android.widget.RelativeLayout;
import android.widget.TextView;
import android.widget.Toast;
import androidx.constraintlayout.widget.ConstraintLayout;
import androidx.recyclerview.widget.DividerItemDecoration;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import com.google.android.material.snackbar.Snackbar;
import com.pedrogomez.renderers.RVRendererAdapter;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.Callable;
import javax.inject.Inject;
import butterknife.BindView;
import butterknife.ButterKnife;
import com.google.android.material.snackbar.Snackbar;
import fr.free.nrw.commons.R;
import fr.free.nrw.commons.Utils;
import fr.free.nrw.commons.theme.NavigationBaseActivity;
@ -40,6 +30,11 @@ import io.reactivex.ObservableSource;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.Disposable;
import io.reactivex.schedulers.Schedulers;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.Callable;
import javax.inject.Inject;
import kotlin.Unit;
import timber.log.Timber;
/**
@ -47,7 +42,6 @@ import timber.log.Timber;
*/
public class NotificationActivity extends NavigationBaseActivity {
NotificationAdapterFactory notificationAdapterFactory;
@BindView(R.id.listView)
RecyclerView recyclerView;
@BindView(R.id.progressBar)
@ -64,7 +58,7 @@ public class NotificationActivity extends NavigationBaseActivity {
private static final String TAG_NOTIFICATION_WORKER_FRAGMENT = "NotificationWorkerFragment";
private NotificationWorkerFragment mNotificationWorkerFragment;
private RVRendererAdapter<Notification> adapter;
private NotificatinAdapter adapter;
private List<Notification> notificationList;
MenuItem notificationMenuItem;
@ -89,7 +83,7 @@ public class NotificationActivity extends NavigationBaseActivity {
.subscribe(result -> {
if (result) {
notificationList.remove(notification);
setAdapter(notificationList);
setItems(notificationList);
adapter.notifyDataSetChanged();
Snackbar snackbar = Snackbar
.make(relativeLayout, getString(R.string.notification_mark_read), Snackbar.LENGTH_LONG);
@ -102,7 +96,7 @@ public class NotificationActivity extends NavigationBaseActivity {
}
} else {
adapter.notifyDataSetChanged();
setAdapter(notificationList);
setItems(notificationList);
Toast.makeText(NotificationActivity.this, getString(R.string.some_error), Toast.LENGTH_SHORT).show();
}
}, throwable -> {
@ -126,6 +120,13 @@ public class NotificationActivity extends NavigationBaseActivity {
} else {
refresh(false);
}
adapter = new NotificatinAdapter(item -> {
Timber.d("Notification clicked %s", item.getLink());
handleUrl(item.getLink());
removeNotification(item);
return Unit.INSTANCE;
});
recyclerView.setAdapter(this.adapter);
}
private void refresh(boolean archived) {
@ -158,7 +159,7 @@ public class NotificationActivity extends NavigationBaseActivity {
relativeLayout.setVisibility(View.GONE);
no_notification.setVisibility(View.VISIBLE);
} else {
setAdapter(notificationList);
setItems(notificationList);
}
progressBar.setVisibility(View.GONE);
}, throwable -> {
@ -168,7 +169,7 @@ public class NotificationActivity extends NavigationBaseActivity {
}));
} else {
notificationList = mNotificationWorkerFragment.getNotificationList();
setAdapter(notificationList);
setItems(notificationList);
}
}
@ -204,7 +205,7 @@ public class NotificationActivity extends NavigationBaseActivity {
Utils.handleWebUrl(this, Uri.parse(url));
}
private void setAdapter(List<Notification> notificationList) {
private void setItems(List<Notification> notificationList) {
if (notificationList == null || notificationList.isEmpty()) {
ViewUtil.showShortSnackbar(relativeLayout, R.string.no_notifications);
/*progressBar.setVisibility(View.GONE);
@ -214,32 +215,9 @@ public class NotificationActivity extends NavigationBaseActivity {
no_notification.setVisibility(View.VISIBLE);
return;
}
boolean isarchivedvisible;
if (getIntent().getStringExtra("title").equals("read")) {
isarchivedvisible = true;
} else {
isarchivedvisible = false;
}
notificationAdapterFactory = new NotificationAdapterFactory(new NotificationRenderer.NotificationClicked() {
@Override
public void notificationClicked(Notification notification) {
Timber.d("Notification clicked %s", notification.getLink());
handleUrl(notification.getLink());
removeNotification(notification);
}
@Override
public void markNotificationAsRead(Notification notification) {
Timber.d("Notification to mark as read %s", notification.getNotificationId());
removeNotification(notification);
}
}, isarchivedvisible);
adapter = notificationAdapterFactory.create(notificationList);
relativeLayout.setVisibility(View.VISIBLE);
no_notification.setVisibility(View.GONE);
recyclerView.setAdapter(adapter);
adapter.setItems(notificationList);
}
public static void startYourself(Context context, String title) {

View file

@ -0,0 +1,24 @@
package fr.free.nrw.commons.notification
import com.hannesdorfmann.adapterdelegates4.dsl.adapterDelegateLayoutContainer
import fr.free.nrw.commons.R
import kotlinx.android.synthetic.main.activity_login.title
import kotlinx.android.synthetic.main.item_notification.*
import org.wikipedia.util.StringUtil
fun notificationDelegate(onNotificationClicked: (Notification) -> Unit) =
adapterDelegateLayoutContainer<Notification, Notification>(R.layout.item_notification) {
containerView.setOnClickListener { onNotificationClicked(item) }
bind {
title.text = item.processedNotificationText
time.text = item.date
}
}
private val Notification.processedNotificationText: CharSequence
get() = notificationText.trim()
.replace("(^\\s*)|(\\s*$)".toRegex(), "")
.let { StringUtil.fromHtml(it).toString() }
.let { if (it.length > 280) "${it.substring(0, 279)}..." else it } + " "

View file

@ -1,32 +0,0 @@
package fr.free.nrw.commons.notification;
import androidx.annotation.NonNull;
import com.pedrogomez.renderers.ListAdapteeCollection;
import com.pedrogomez.renderers.RVRendererAdapter;
import com.pedrogomez.renderers.RendererBuilder;
import java.util.Collections;
import java.util.List;
/**
* Created by root on 19.12.2017.
*/
class NotificationAdapterFactory {
private NotificationRenderer.NotificationClicked listener;
private boolean isarchivedvisible = false;
NotificationAdapterFactory(@NonNull NotificationRenderer.NotificationClicked listener, boolean isarchivedvisible) {
this.listener = listener;
this.isarchivedvisible = isarchivedvisible;
}
public RVRendererAdapter<Notification> create(List<Notification> notifications) {
RendererBuilder<Notification> builder = new RendererBuilder<Notification>()
.bind(Notification.class, new NotificationRenderer(listener, isarchivedvisible));
ListAdapteeCollection<Notification> collection = new ListAdapteeCollection<>(
notifications != null ? notifications : Collections.<Notification>emptyList());
return new RVRendererAdapter<>(builder, collection);
}
}

View file

@ -1,91 +0,0 @@
package fr.free.nrw.commons.notification;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import com.pedrogomez.renderers.Renderer;
import org.wikipedia.util.StringUtil;
import butterknife.BindView;
import butterknife.ButterKnife;
import butterknife.OnClick;
import fr.free.nrw.commons.R;
/**
* Created by root on 19.12.2017.
*/
public class NotificationRenderer extends Renderer<Notification> {
@BindView(R.id.title)
TextView title;
@BindView(R.id.time)
TextView time;
@BindView(R.id.icon)
ImageView icon;
/*@BindView(R.id.bottom)
LinearLayout bottomLayout;*/
private NotificationClicked listener;
private boolean isarchivedvisible = false;
NotificationRenderer(NotificationClicked listener, boolean isarchivedvisible) {
this.listener = listener;
this.isarchivedvisible = isarchivedvisible;
}
@OnClick(R.id.notification_view)
void onNotificationClicked() {
listener.notificationClicked(getContent());
}
@Override
protected void setUpView(View rootView) {
}
@Override
protected void hookListeners(View rootView) {
}
@Override
protected View inflate(LayoutInflater layoutInflater, ViewGroup viewGroup) {
View inflatedView = layoutInflater.inflate(R.layout.item_notification, viewGroup, false);
ButterKnife.bind(this, inflatedView);
return inflatedView;
}
@Override
public void render() {
Notification notification = getContent();
setTitle(notification.getNotificationText());
time.setText(notification.getDate());
}
/**
* Cleans up the notification text and sets it as the title
* Clean up is required to fix escaped HTML string and extra white spaces at the beginning of the notification
*
* @param notificationText
*/
private void setTitle(String notificationText) {
notificationText = notificationText.trim().replaceAll("(^\\s*)|(\\s*$)", "");
notificationText = StringUtil.fromHtml(notificationText).toString();
if (notificationText.length() > 280) {
notificationText = notificationText.substring(0, 279);
notificationText = notificationText.concat("...");
}
notificationText = notificationText.concat(" ");
title.setText(notificationText);
}
public interface NotificationClicked {
void notificationClicked(Notification notification);
void markNotificationAsRead(Notification notification);
}
}

View file

@ -1,27 +0,0 @@
package fr.free.nrw.commons.upload;
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.category.CategoryClickedListener;
import fr.free.nrw.commons.category.CategoryItem;
public class UploadCategoriesAdapterFactory {
private final CategoryClickedListener listener;
public UploadCategoriesAdapterFactory(CategoryClickedListener listener) {
this.listener = listener;
}
public RVRendererAdapter<CategoryItem> create(List<CategoryItem> placeList) {
RendererBuilder<CategoryItem> builder = new RendererBuilder<CategoryItem>()
.bind(CategoryItem.class, new UploadCategoriesRenderer(listener));
ListAdapteeCollection<CategoryItem> collection = new ListAdapteeCollection<>(
placeList != null ? placeList : Collections.emptyList());
return new RVRendererAdapter<>(builder, collection);
}
}

View file

@ -1,59 +0,0 @@
package fr.free.nrw.commons.upload;
import android.annotation.SuppressLint;
import android.content.res.Configuration;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.CheckBox;
import com.pedrogomez.renderers.Renderer;
import butterknife.BindView;
import butterknife.ButterKnife;
import fr.free.nrw.commons.R;
import fr.free.nrw.commons.category.CategoryClickedListener;
import fr.free.nrw.commons.category.CategoryItem;
public class UploadCategoriesRenderer extends Renderer<CategoryItem> {
@BindView(R.id.tvName) CheckBox checkedView;
private final CategoryClickedListener listener;
UploadCategoriesRenderer(CategoryClickedListener listener) {
this.listener = listener;
}
@Override
protected View inflate(LayoutInflater layoutInflater, ViewGroup viewGroup) {
return layoutInflater.inflate(R.layout.layout_upload_categories_item, viewGroup, false);
}
@Override
protected void setUpView(View view) {
ButterKnife.bind(this, view);
Configuration config = getContext().getResources().getConfiguration();
if (config.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL) {
checkedView.setLayoutDirection(View.LAYOUT_DIRECTION_LTR);
}
}
@SuppressLint("WrongConstant")
@Override
protected void hookListeners(View view) {
view.setOnClickListener(v -> {
CategoryItem item = getContent();
item.setSelected(!item.isSelected());
checkedView.setChecked(item.isSelected());
if (listener != null) {
listener.categoryClicked(item);
}
});
}
@Override
public void render() {
CategoryItem item = getContent();
checkedView.setChecked(item.isSelected());
checkedView.setText(item.getName());
}
}

View file

@ -1,31 +0,0 @@
package fr.free.nrw.commons.upload;
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.upload.structure.depictions.DepictedItem;
import fr.free.nrw.commons.upload.structure.depictions.UploadDepictsCallback;
/**
* Adapter Factory for DepictsClicked Listener
*/
public class UploadDepictsAdapterFactory {
private final UploadDepictsCallback listener;
public UploadDepictsAdapterFactory(UploadDepictsCallback listener) {
this.listener = listener;
}
public RVRendererAdapter<DepictedItem> create(List<DepictedItem> itemList) {
RendererBuilder<DepictedItem> builder = new RendererBuilder<DepictedItem>()
.bind(DepictedItem.class, new UploadDepictsRenderer(listener));
ListAdapteeCollection<DepictedItem> collection = new ListAdapteeCollection<>(
itemList != null ? itemList : Collections.emptyList());
return new RVRendererAdapter<>(builder, collection);
}
}

View file

@ -1,87 +0,0 @@
package fr.free.nrw.commons.upload;
import android.net.Uri;
import android.text.TextUtils;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.CheckBox;
import android.widget.TextView;
import butterknife.BindView;
import butterknife.ButterKnife;
import com.facebook.common.util.UriUtil;
import com.facebook.drawee.view.SimpleDraweeView;
import com.pedrogomez.renderers.Renderer;
import fr.free.nrw.commons.R;
import fr.free.nrw.commons.upload.structure.depictions.DepictedItem;
import fr.free.nrw.commons.upload.structure.depictions.UploadDepictsCallback;
/**
* Depicts Renderer for setting up inflating layout,
* and setting views for the layout of each depicted Item
*/
public class UploadDepictsRenderer extends Renderer<DepictedItem> {
private final UploadDepictsCallback listener;
@BindView(R.id.depict_checkbox)
CheckBox checkedView;
@BindView(R.id.depicts_label)
TextView depictsLabel;
@BindView(R.id.description) TextView description;
@BindView(R.id.depicted_image)
SimpleDraweeView imageView;
public UploadDepictsRenderer(UploadDepictsCallback listener) {
this.listener = listener;
}
@Override
protected void setUpView(View rootView) {
ButterKnife.bind(this, rootView);
}
/**
* Setup OnClicklisteners on the views
*/
@Override
protected void hookListeners(View rootView) {
rootView.setOnClickListener(v -> {
DepictedItem item = getContent();
item.setSelected(!item.isSelected());
checkedView.setChecked(item.isSelected());
if (listener != null) {
listener.depictsClicked(item);
}
});
checkedView.setOnClickListener(v -> {
DepictedItem item = getContent();
item.setSelected(!item.isSelected());
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);
}
/**
* initialise views for every item in the adapter
*/
@Override
public void render() {
DepictedItem item = getContent();
checkedView.setChecked(item.isSelected());
depictsLabel.setText(item.getName());
description.setText(item.getDescription());
final String imageUrl = item.getImageUrl();
if (TextUtils.isEmpty(imageUrl)) {
imageView.setImageURI(UriUtil.getUriForResourceId(R.drawable.ic_wikidata_logo_24dp));
} else {
imageView.setImageURI(Uri.parse(imageUrl));
}
}
}

View file

@ -0,0 +1,41 @@
package fr.free.nrw.commons.upload.categories
import androidx.recyclerview.widget.DiffUtil
import com.hannesdorfmann.adapterdelegates4.AdapterDelegate
import com.hannesdorfmann.adapterdelegates4.AsyncListDifferDelegationAdapter
abstract class BaseDelegateAdapter<T>(
vararg delegates: AdapterDelegate<List<T>>,
areItemsTheSame: (T, T) -> Boolean,
areContentsTheSame: (T, T) -> Boolean = { old, new -> old == new }
) : AsyncListDifferDelegationAdapter<T>(
object : DiffUtil.ItemCallback<T>() {
override fun areItemsTheSame(oldItem: T, newItem: T) =
areItemsTheSame(oldItem, newItem)
override fun areContentsTheSame(oldItem: T, newItem: T) =
areContentsTheSame(oldItem, newItem)
},
*delegates
) {
fun addAll(newResults: List<T>) {
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

@ -18,23 +18,19 @@ import com.google.android.material.textfield.TextInputEditText;
import com.google.android.material.textfield.TextInputLayout;
import com.jakewharton.rxbinding2.view.RxView;
import com.jakewharton.rxbinding2.widget.RxTextView;
import com.pedrogomez.renderers.RVRendererAdapter;
import fr.free.nrw.commons.R;
import fr.free.nrw.commons.category.CategoryClickedListener;
import fr.free.nrw.commons.category.CategoryItem;
import fr.free.nrw.commons.upload.UploadBaseFragment;
import fr.free.nrw.commons.upload.UploadCategoriesAdapterFactory;
import fr.free.nrw.commons.utils.DialogUtil;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.Disposable;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;
import javax.inject.Inject;
import kotlin.Unit;
import timber.log.Timber;
public class UploadCategoriesFragment extends UploadBaseFragment implements CategoriesContract.View,
CategoryClickedListener {
public class UploadCategoriesFragment extends UploadBaseFragment implements CategoriesContract.View {
@BindView(R.id.tv_title)
TextView tvTitle;
@ -49,14 +45,9 @@ public class UploadCategoriesFragment extends UploadBaseFragment implements Cate
@Inject
CategoriesContract.UserActionListener presenter;
private RVRendererAdapter<CategoryItem> adapter;
private UploadCategoryAdapter adapter;
private Disposable subscribe;
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
@ -93,8 +84,10 @@ public class UploadCategoriesFragment extends UploadBaseFragment implements Cate
}
private void initRecyclerView() {
adapter = new UploadCategoriesAdapterFactory(this)
.create(new ArrayList<>());
adapter = new UploadCategoryAdapter(categoryItem -> {
presenter.onCategoryItemClicked(categoryItem);
return Unit.INSTANCE;
});
rvCategories.setLayoutManager(new LinearLayoutManager(getContext()));
rvCategories.setAdapter(adapter);
}
@ -123,10 +116,11 @@ public class UploadCategoriesFragment extends UploadBaseFragment implements Cate
@Override
public void setCategories(List<CategoryItem> categories) {
if(categories==null) {
adapter.clear();
if (categories != null) {
adapter.addAll(categories);
adapter.notifyDataSetChanged();
}
else{
adapter.setItems(categories);
}
}
@ -157,11 +151,6 @@ public class UploadCategoriesFragment extends UploadBaseFragment implements Cate
callback.onPreviousButtonClicked(callback.getIndexInViewFlipper(this));
}
@Override
public void categoryClicked(CategoryItem item) {
presenter.onCategoryItemClicked(item);
}
@Override
protected void onBecameVisible() {
super.onBecameVisible();

View file

@ -0,0 +1,14 @@
package fr.free.nrw.commons.upload.categories
import fr.free.nrw.commons.category.CategoryItem
class UploadCategoryAdapter(onCategoryClicked: (CategoryItem) -> Unit) :
BaseDelegateAdapter<CategoryItem>(
uploadCategoryDelegate(onCategoryClicked),
areItemsTheSame = { oldItem, newItem -> oldItem.name == newItem.name },
areContentsTheSame = { oldItem, newItem ->
oldItem.name == newItem.name && oldItem.isSelected == newItem.isSelected
}
)

View file

@ -0,0 +1,19 @@
package fr.free.nrw.commons.upload.categories
import com.hannesdorfmann.adapterdelegates4.dsl.adapterDelegateLayoutContainer
import fr.free.nrw.commons.R
import fr.free.nrw.commons.category.CategoryItem
import kotlinx.android.synthetic.main.layout_upload_categories_item.*
fun uploadCategoryDelegate(onCategoryClicked: (CategoryItem) -> Unit) =
adapterDelegateLayoutContainer<CategoryItem, CategoryItem>(R.layout.layout_upload_categories_item) {
containerView.setOnClickListener {
item.isSelected = !item.isSelected
uploadCategoryCheckbox.isChecked = item.isSelected
onCategoryClicked(item)
}
bind {
uploadCategoryCheckbox.isChecked = item.isSelected
uploadCategoryCheckbox.text = item.name
}
}

View file

@ -1,7 +1,6 @@
package fr.free.nrw.commons.upload.depicts;
import android.os.Bundle;
import android.util.Pair;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
@ -18,27 +17,23 @@ import com.google.android.material.textfield.TextInputEditText;
import com.google.android.material.textfield.TextInputLayout;
import com.jakewharton.rxbinding2.view.RxView;
import com.jakewharton.rxbinding2.widget.RxTextView;
import com.pedrogomez.renderers.RVRendererAdapter;
import fr.free.nrw.commons.R;
import fr.free.nrw.commons.upload.UploadBaseFragment;
import fr.free.nrw.commons.upload.UploadDepictsAdapterFactory;
import fr.free.nrw.commons.upload.structure.depictions.DepictedItem;
import fr.free.nrw.commons.upload.structure.depictions.UploadDepictsCallback;
import fr.free.nrw.commons.utils.DialogUtil;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.Disposable;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;
import javax.inject.Inject;
import org.jetbrains.annotations.NotNull;
import kotlin.Unit;
import timber.log.Timber;
/**
* Fragment for showing depicted items list in Upload activity after media details
*/
public class DepictsFragment extends UploadBaseFragment implements DepictsContract.View, UploadDepictsCallback {
public class DepictsFragment extends UploadBaseFragment implements DepictsContract.View {
@BindView(R.id.depicts_title)
TextView depictsTitle;
@ -53,7 +48,7 @@ public class DepictsFragment extends UploadBaseFragment implements DepictsContra
@Inject
DepictsContract.UserActionListener presenter;
private RVRendererAdapter<DepictedItem> adapter;
private UploadDepictsAdapter adapter;
private Disposable subscribe;
@Nullable
@ -86,8 +81,10 @@ public class DepictsFragment extends UploadBaseFragment implements DepictsContra
* Initialise recyclerView and set adapter
*/
private void initRecyclerView() {
adapter = new UploadDepictsAdapterFactory(this)
.create(new ArrayList<>());
adapter = new UploadDepictsAdapter(item -> {
presenter.onDepictItemClicked(item);
return Unit.INSTANCE;
});
depictsRecyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
depictsRecyclerView.setAdapter(adapter);
}
@ -137,22 +134,7 @@ public class DepictsFragment extends UploadBaseFragment implements DepictsContra
@Override
public void setDepictsList(List<DepictedItem> depictedItemList) {
adapter.clear();
if (depictedItemList != null) {
adapter.addAll(depictedItemList);
adapter.notifyDataSetChanged();
}
}
@Nullable
private Pair<DepictedItem,Integer> returnItemAndPosition(@NotNull DepictedItem depictedItem) {
for (int i = 0; i < adapter.getItemCount(); i++) {
final DepictedItem item = adapter.getItem(i);
if(item.getId().equals(depictedItem.getId())){
return new Pair<>(item, i);
}
}
return null;
adapter.setItems(depictedItemList);
}
@OnClick(R.id.depicts_next)
@ -165,11 +147,6 @@ public class DepictsFragment extends UploadBaseFragment implements DepictsContra
callback.onPreviousButtonClicked(callback.getIndexInViewFlipper(this));
}
@Override
public void depictsClicked(DepictedItem item) {
presenter.onDepictItemClicked(item);
}
/**
* Text change listener for the edit text view of depicts
*/

View file

@ -0,0 +1,10 @@
package fr.free.nrw.commons.upload.depicts
import fr.free.nrw.commons.upload.categories.BaseDelegateAdapter
import fr.free.nrw.commons.upload.structure.depictions.DepictedItem
class UploadDepictsAdapter(onDepictsClicked: (DepictedItem) -> Unit) :
BaseDelegateAdapter<DepictedItem>(
uploadDepictsDelegate(onDepictsClicked),
areItemsTheSame = { oldItem, newItem -> oldItem.id == newItem.id }
)

View file

@ -0,0 +1,32 @@
package fr.free.nrw.commons.upload.depicts
import android.net.Uri
import android.text.TextUtils
import android.view.View
import com.hannesdorfmann.adapterdelegates4.dsl.adapterDelegateLayoutContainer
import fr.free.nrw.commons.R
import fr.free.nrw.commons.upload.structure.depictions.DepictedItem
import kotlinx.android.synthetic.main.layout_upload_depicts_item.*
fun uploadDepictsDelegate(onDepictClicked: (DepictedItem) -> Unit) =
adapterDelegateLayoutContainer<DepictedItem, DepictedItem>(R.layout.layout_upload_depicts_item) {
val onClickListener = { _: View? ->
item.isSelected = !item.isSelected
depict_checkbox.isChecked = item.isSelected
onDepictClicked(item)
}
containerView.setOnClickListener(onClickListener)
depict_checkbox.setOnClickListener(onClickListener)
bind {
depict_checkbox.isChecked = item.isSelected
depicts_label.text = item.name
description.text = item.description
val imageUrl = item.imageUrl
if (TextUtils.isEmpty(imageUrl)) {
depicted_image.setActualImageResource(R.drawable.ic_wikidata_logo_24dp)
} else {
depicted_image.setImageURI(Uri.parse(imageUrl))
}
}
}

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

@ -5,12 +5,12 @@
android:layout_height="wrap_content"
android:padding="@dimen/tiny_gap">
<ImageView
<com.facebook.drawee.view.SimpleDraweeView
android:id="@+id/depicts_image"
android:layout_width="70dp"
android:layout_height="70dp"
android:paddingRight="@dimen/tiny_gap"
app:srcCompat="@drawable/ic_wikidata_logo_24dp"
app:placeholderImage="@drawable/ic_wikidata_logo_24dp"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toTopOf="parent"/>

View file

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<CheckBox xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/tvName"
android:id="@+id/uploadCategoryCheckbox"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:checkMark="?android:attr/textCheckMark"

View file

@ -1,14 +1,14 @@
<LinearLayout
android:layout_width="match_parent"
<LinearLayout android:layout_width="match_parent"
android:layout_height="wrap_content"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/buttonLayout"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:orientation="horizontal"
android:visibility="gone"
tools:visibility="visible"
android:layout_marginTop="@dimen/standard_gap"
android:layout_below="@+id/icon"
xmlns:android="http://schemas.android.com/apk/res/android"
>
xmlns:android="http://schemas.android.com/apk/res/android">
<LinearLayout
android:id="@+id/cameraButton"

View file

@ -16,13 +16,14 @@
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
ROOM_VERSION=2.2.3
PREFERENCE_VERSION=1.1.0
CORE_KTX_VERSION=1.2.0
ADAPTER_DELEGATES_VERSION=4.3.0
MULTIDEX_VERSION=2.0.1
systemProp.http.proxyPort=0