From 5f8592eb0ad1d31268894b94af33039649752586 Mon Sep 17 00:00:00 2001 From: Kanahia Date: Thu, 23 May 2024 20:07:07 +0530 Subject: [PATCH] Added wikitalk page and improved bottomsheet for landscape mode --- .../nrw/commons/di/ActivityBuilderModule.java | 4 + .../di/CommonsApplicationComponent.java | 5 + .../commons/mwapi/OkHttpJsonApiClient.java | 29 ++- .../nrw/commons/nearby/BottomSheetAdapter.kt | 92 +++++++++ .../nrw/commons/nearby/DescriptionAdapter.kt | 35 ++++ .../nrw/commons/nearby/NearbyController.java | 4 + .../free/nrw/commons/nearby/NearbyPlaces.java | 4 + .../free/nrw/commons/nearby/TitleAdapter.kt | 38 ++++ .../nrw/commons/nearby/WikidataFeedback.kt | 142 ++++++++++++++ .../fragments/NearbyParentFragment.java | 181 ++++++++++++------ .../commons/nearby/model/BottomSheetItem.kt | 3 + .../commons/nearby/model/WikiTalkResponse.kt | 12 ++ .../res/layout/activity_wikidata_feedback.xml | 132 +++++++++++++ .../main/res/layout/bottom_sheet_details.xml | 172 ++--------------- .../res/layout/bottom_sheet_item_layout.xml | 27 +++ .../main/res/layout/layout_wikitalk_item.xml | 22 +++ .../layout_wikitalk_item_description.xml | 35 ++++ app/src/main/res/values/strings.xml | 1 + 18 files changed, 713 insertions(+), 225 deletions(-) create mode 100644 app/src/main/java/fr/free/nrw/commons/nearby/BottomSheetAdapter.kt create mode 100644 app/src/main/java/fr/free/nrw/commons/nearby/DescriptionAdapter.kt create mode 100644 app/src/main/java/fr/free/nrw/commons/nearby/TitleAdapter.kt create mode 100644 app/src/main/java/fr/free/nrw/commons/nearby/WikidataFeedback.kt create mode 100644 app/src/main/java/fr/free/nrw/commons/nearby/model/BottomSheetItem.kt create mode 100644 app/src/main/java/fr/free/nrw/commons/nearby/model/WikiTalkResponse.kt create mode 100644 app/src/main/res/layout/activity_wikidata_feedback.xml create mode 100644 app/src/main/res/layout/bottom_sheet_item_layout.xml create mode 100644 app/src/main/res/layout/layout_wikitalk_item.xml create mode 100644 app/src/main/res/layout/layout_wikitalk_item_description.xml diff --git a/app/src/main/java/fr/free/nrw/commons/di/ActivityBuilderModule.java b/app/src/main/java/fr/free/nrw/commons/di/ActivityBuilderModule.java index 6a9277906..0df9685c1 100644 --- a/app/src/main/java/fr/free/nrw/commons/di/ActivityBuilderModule.java +++ b/app/src/main/java/fr/free/nrw/commons/di/ActivityBuilderModule.java @@ -14,6 +14,7 @@ import fr.free.nrw.commons.description.DescriptionEditActivity; import fr.free.nrw.commons.explore.depictions.WikidataItemDetailsActivity; import fr.free.nrw.commons.explore.SearchActivity; import fr.free.nrw.commons.media.ZoomableActivity; +import fr.free.nrw.commons.nearby.WikidataFeedback; import fr.free.nrw.commons.notification.NotificationActivity; import fr.free.nrw.commons.profile.ProfileActivity; import fr.free.nrw.commons.review.ReviewActivity; @@ -79,4 +80,7 @@ public abstract class ActivityBuilderModule { @ContributesAndroidInjector abstract ZoomableActivity bindZoomableActivity(); + + @ContributesAndroidInjector + abstract WikidataFeedback bindWikiFeedback(); } diff --git a/app/src/main/java/fr/free/nrw/commons/di/CommonsApplicationComponent.java b/app/src/main/java/fr/free/nrw/commons/di/CommonsApplicationComponent.java index cbbf1f0c5..1390bd8ef 100644 --- a/app/src/main/java/fr/free/nrw/commons/di/CommonsApplicationComponent.java +++ b/app/src/main/java/fr/free/nrw/commons/di/CommonsApplicationComponent.java @@ -2,9 +2,11 @@ package fr.free.nrw.commons.di; import com.google.gson.Gson; +import fr.free.nrw.commons.actions.PageEditClient; import fr.free.nrw.commons.explore.categories.CategoriesModule; import fr.free.nrw.commons.navtab.MoreBottomSheetFragment; import fr.free.nrw.commons.navtab.MoreBottomSheetLoggedOutFragment; +import fr.free.nrw.commons.nearby.NearbyController; import fr.free.nrw.commons.upload.worker.UploadWorker; import javax.inject.Singleton; @@ -68,6 +70,9 @@ public interface CommonsApplicationComponent extends AndroidInjector" + "\n"; - List placeBindings = runQuery(leftLatLng,rightLatLng); + List placeBindings = runQuery(leftLatLng, rightLatLng); if (placeBindings != null) { for (PlaceBindings item : placeBindings) { if (item.getItem() != null && item.getLabel() != null && item.getClas() != null) { diff --git a/app/src/main/java/fr/free/nrw/commons/nearby/BottomSheetAdapter.kt b/app/src/main/java/fr/free/nrw/commons/nearby/BottomSheetAdapter.kt new file mode 100644 index 000000000..a93b9eeeb --- /dev/null +++ b/app/src/main/java/fr/free/nrw/commons/nearby/BottomSheetAdapter.kt @@ -0,0 +1,92 @@ +package fr.free.nrw.commons.nearby + +import android.content.Context +import android.view.LayoutInflater +import android.view.View +import android.view.View.OnLongClickListener +import android.view.ViewGroup +import android.widget.ImageView +import android.widget.TextView +import androidx.annotation.NonNull +import androidx.core.content.ContextCompat +import androidx.recyclerview.widget.RecyclerView +import fr.free.nrw.commons.R +import fr.free.nrw.commons.nearby.model.BottomSheetItem + + +class BottomSheetAdapter(context: Context?, private val itemList: List) : + RecyclerView.Adapter() { + private val layoutInflater: LayoutInflater = LayoutInflater.from(context) + private var itemClickListener: ItemClickListener? = null + + @NonNull + override fun onCreateViewHolder(@NonNull parent: ViewGroup, viewType: Int): ViewHolder { + val view: View = layoutInflater.inflate(R.layout.bottom_sheet_item_layout, parent, false) + return ViewHolder(view) + } + + override fun onBindViewHolder(@NonNull holder: ViewHolder, position: Int) { + val item = itemList[position] + holder.imageView.setImageDrawable( + ContextCompat.getDrawable( + getContext(), + item.imageResourceId + ) + ) + holder.title.setText(item.title) + } + + override fun getItemCount(): Int { + return itemList.size + } + + fun updateBookmarkIcon(icon: Int) { + itemList.forEachIndexed { index, item -> + if (item.imageResourceId == R.drawable.ic_round_star_filled_24px || item.imageResourceId == R.drawable.ic_round_star_border_24px) { + item.imageResourceId = icon + this.notifyItemChanged(index) + return + } + } + } + + inner class ViewHolder internal constructor(itemView: View) : RecyclerView.ViewHolder(itemView), + View.OnClickListener, OnLongClickListener { + var imageView: ImageView = itemView.findViewById(R.id.buttonImage) + var title: TextView = itemView.findViewById(R.id.buttonText) + + init { + itemView.setOnClickListener(this) + itemView.setOnLongClickListener(this) + } + + override fun onClick(view: View) { + if (itemClickListener != null) itemClickListener!!.onBottomSheetItemClick( + view, + adapterPosition + ) + } + + override fun onLongClick(view: View): Boolean { + if (itemClickListener != null) itemClickListener!!.onBottomSheetItemLongClick( + view, + adapterPosition + ) + return true + } + } + + fun setClickListener(itemClickListener: ItemClickListener?) { + this.itemClickListener = itemClickListener + } + + fun getContext(): Context { + return layoutInflater.context + } + + interface ItemClickListener { + fun onBottomSheetItemClick(view: View?, position: Int) + fun onBottomSheetItemLongClick(view: View?, position: Int) + } +} + diff --git a/app/src/main/java/fr/free/nrw/commons/nearby/DescriptionAdapter.kt b/app/src/main/java/fr/free/nrw/commons/nearby/DescriptionAdapter.kt new file mode 100644 index 000000000..27fd59005 --- /dev/null +++ b/app/src/main/java/fr/free/nrw/commons/nearby/DescriptionAdapter.kt @@ -0,0 +1,35 @@ +package fr.free.nrw.commons.nearby + +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.TextView +import androidx.recyclerview.widget.RecyclerView +import fr.free.nrw.commons.R +import fr.free.nrw.commons.nearby.model.Description + +class DescriptionAdapter(private val descriptions: List) : + RecyclerView.Adapter() { + + class DescriptionViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { + val descTextView: TextView = itemView.findViewById(R.id.descTextView) + val userTextView: TextView = itemView.findViewById(R.id.userTextView) + val dateTextView: TextView = itemView.findViewById(R.id.dateTextView) + } + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): DescriptionViewHolder { + val view = LayoutInflater.from(parent.context).inflate(R.layout.layout_wikitalk_item_description, parent, false) + return DescriptionViewHolder(view) + } + + override fun onBindViewHolder(holder: DescriptionViewHolder, position: Int) { + val description = descriptions[position] + holder.descTextView.text = description.text + holder.userTextView.text = description.user + holder.dateTextView.text = description.time + } + + override fun getItemCount(): Int { + return descriptions.size + } +} diff --git a/app/src/main/java/fr/free/nrw/commons/nearby/NearbyController.java b/app/src/main/java/fr/free/nrw/commons/nearby/NearbyController.java index d650f2869..29661c76e 100644 --- a/app/src/main/java/fr/free/nrw/commons/nearby/NearbyController.java +++ b/app/src/main/java/fr/free/nrw/commons/nearby/NearbyController.java @@ -131,6 +131,10 @@ public class NearbyController extends MapController { ); } + public String getWikiTalk(String s) throws Exception { + return nearbyPlaces.getWikiTalk(s); + } + public static LatLng calculateNorthEast(double latitude, double longitude, double distance) { double lat1 = Math.toRadians(latitude); double deltaLat = distance * 0.008; diff --git a/app/src/main/java/fr/free/nrw/commons/nearby/NearbyPlaces.java b/app/src/main/java/fr/free/nrw/commons/nearby/NearbyPlaces.java index 787cc35e2..6a01eaaf1 100644 --- a/app/src/main/java/fr/free/nrw/commons/nearby/NearbyPlaces.java +++ b/app/src/main/java/fr/free/nrw/commons/nearby/NearbyPlaces.java @@ -120,6 +120,10 @@ public class NearbyPlaces { customQuery); } + public String getWikiTalk(String QID) throws Exception { + return okHttpJsonApiClient.getWikidataTalk(QID); + } + /** * Runs the Wikidata query to retrieve the KML String * diff --git a/app/src/main/java/fr/free/nrw/commons/nearby/TitleAdapter.kt b/app/src/main/java/fr/free/nrw/commons/nearby/TitleAdapter.kt new file mode 100644 index 000000000..2a0dbdee6 --- /dev/null +++ b/app/src/main/java/fr/free/nrw/commons/nearby/TitleAdapter.kt @@ -0,0 +1,38 @@ +package fr.free.nrw.commons.nearby + +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.TextView +import androidx.recyclerview.widget.LinearLayoutManager +import androidx.recyclerview.widget.RecyclerView +import fr.free.nrw.commons.R +import fr.free.nrw.commons.nearby.model.Title + +class TitleAdapter(private val titles: List) : + RecyclerView.Adapter<TitleAdapter.TitleViewHolder>() { + + class TitleViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { + val titleTextView: TextView = itemView.findViewById(R.id.titleTextView) + val descriptionsRecyclerView: RecyclerView = itemView.findViewById(R.id.descriptionsRecyclerView) + } + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): TitleViewHolder { + val view = LayoutInflater.from(parent.context).inflate(R.layout.layout_wikitalk_item, parent, false) + return TitleViewHolder(view) + } + + override fun onBindViewHolder(holder: TitleViewHolder, position: Int) { + val title = titles[position] + holder.titleTextView.text = title.title + + holder.descriptionsRecyclerView.apply { + layoutManager = LinearLayoutManager(holder.itemView.context) + adapter = DescriptionAdapter(title.descriptions) + } + } + + override fun getItemCount(): Int { + return titles.size + } +} diff --git a/app/src/main/java/fr/free/nrw/commons/nearby/WikidataFeedback.kt b/app/src/main/java/fr/free/nrw/commons/nearby/WikidataFeedback.kt new file mode 100644 index 000000000..a54bb49a0 --- /dev/null +++ b/app/src/main/java/fr/free/nrw/commons/nearby/WikidataFeedback.kt @@ -0,0 +1,142 @@ +package fr.free.nrw.commons.nearby + +import android.annotation.SuppressLint +import android.os.Build +import android.os.Bundle +import android.view.View +import android.widget.Toast +import androidx.annotation.RequiresApi +import androidx.recyclerview.widget.LinearLayoutManager +import androidx.recyclerview.widget.RecyclerView +import fr.free.nrw.commons.databinding.ActivityWikidataFeedbackBinding +import fr.free.nrw.commons.nearby.model.Description +import fr.free.nrw.commons.nearby.model.Title +import fr.free.nrw.commons.theme.BaseActivity +import io.reactivex.Observable +import io.reactivex.android.schedulers.AndroidSchedulers +import io.reactivex.schedulers.Schedulers +import okhttp3.Call +import okhttp3.Callback +import okhttp3.FormBody +import okhttp3.OkHttpClient +import okhttp3.Request +import okhttp3.Response +import okio.IOException +import timber.log.Timber +import javax.inject.Inject + + +class WikidataFeedback : BaseActivity() { + private lateinit var binding: ActivityWikidataFeedbackBinding + var place: String = "" + var wikidataQId: String = "" + + @Inject + lateinit var nearbyController: NearbyController + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + binding = ActivityWikidataFeedbackBinding.inflate(layoutInflater) + setContentView(binding.root) + + place = intent.getStringExtra("place") ?: "" + wikidataQId = intent.getStringExtra("qid") ?: "" + binding.toolbarBinding.toolbar.title = "Talk:" + wikidataQId + binding.textHeader.text = "Write something about the "+"'$place'"+" item. It will be publicly visible." + setSupportActionBar(binding.toolbarBinding.toolbar) + supportActionBar!!.setDisplayHomeAsUpEnabled(true) + getWikidataFeedback(place, wikidataQId) + + } + + override fun onSupportNavigateUp(): Boolean { + onBackPressed() + return true + } + + /** + * This function starts the Wikidata feedback activity of the selected place + * The API returns feedback given by other users + */ + @SuppressLint("CheckResult") + fun getWikidataFeedback(name: String, wikidataQID: String?) { + try { + val wikiTalkObservable = Observable + .fromCallable { + nearbyController.getWikiTalk("Talk:"+wikidataQID) + } + compositeDisposable.add( + wikiTalkObservable + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe({ result -> + if (result != null) { + Timber.tag("PRINT").d("$result") + val key = "\"*\":\"" + var startIndex = result.indexOf(key) + startIndex += key.length + var endIndex = result.indexOf("\"", startIndex) + while (endIndex != -1 && result[endIndex - 1] == '\\') { + endIndex = result.indexOf("\"", endIndex + 1) + } + var value = result.substring(startIndex, endIndex) + + value = value.replace("\\n", "\n").replace("\\\"", "\"") + Timber.tag("PRINT").e(value); + updateUi(name, value, extractData(value)) + } else { + Timber.d("result is null") + Toast.makeText(this, "Failed", Toast.LENGTH_SHORT).show() + } + }, { throwable -> + Timber.e(throwable, "Error occurred while loading notifications") + throwable.printStackTrace() + }) + ) + } catch (e: Exception) { + throw RuntimeException(e) + } + } + + private fun updateUi(place: String, feedback: String,titles: List<Title> ) { + binding.recyclerView.layoutManager = LinearLayoutManager(this) + binding.recyclerView.adapter = TitleAdapter(titles) + binding.progressBar.visibility = View.GONE + binding.activityLayout.visibility = View.VISIBLE + binding.descText.text = if (feedback.isNotEmpty()) feedback else "No Feedback" + } + fun extractData(input: String): List<Title> { + val titlePattern = Regex("""==\s*(.*?)\s*==""") + val descriptionPattern = Regex("==\\n(.*?\\.)") + val userPattern = Regex("""\[\[User:(.*?)\|""") + val timestampPattern = Regex("""\d{2}:\d{2}, \d+ \w+ \d{4} \(UTC\)""") + + val titles = titlePattern.findAll(input).map { it.groupValues[1] }.toList() + val descriptions = descriptionPattern.findAll(input).map { it.groupValues[1] }.toList() + val users = userPattern.findAll(input).map { it.groupValues[1] }.toList() + val timestamps = timestampPattern.findAll(input).map { it.value }.toList() + + val groupedDescriptions = mutableListOf<Description>() + for (i in 0 until minOf(descriptions.size, users.size, timestamps.size)) { + groupedDescriptions.add(Description(descriptions[i], users[i], timestamps[i])) + } + + val titleToDescriptions = mutableMapOf<String, MutableList<Description>>() + var currentTitle: String? = null + + input.lines().forEach { line -> + val titleMatch = titlePattern.matchEntire(line) + if (titleMatch != null) { + currentTitle = titleMatch.groupValues.getOrNull(1) + currentTitle?.let { + titleToDescriptions[it] = mutableListOf() + } + } else if (!currentTitle.isNullOrBlank()) { + groupedDescriptions.removeFirstOrNull() + ?.let { titleToDescriptions[currentTitle]?.add(it) } + } + } + + return titleToDescriptions.map { (title, descriptions) -> Title(title, descriptions) } + } +} \ No newline at end of file diff --git a/app/src/main/java/fr/free/nrw/commons/nearby/fragments/NearbyParentFragment.java b/app/src/main/java/fr/free/nrw/commons/nearby/fragments/NearbyParentFragment.java index d3f555b4d..cb724e40b 100644 --- a/app/src/main/java/fr/free/nrw/commons/nearby/fragments/NearbyParentFragment.java +++ b/app/src/main/java/fr/free/nrw/commons/nearby/fragments/NearbyParentFragment.java @@ -48,6 +48,7 @@ import androidx.appcompat.app.AlertDialog; import androidx.core.content.ContextCompat; import androidx.core.content.FileProvider; import androidx.recyclerview.widget.DividerItemDecoration; +import androidx.recyclerview.widget.GridLayoutManager; import androidx.recyclerview.widget.LinearLayoutManager; import com.google.android.material.bottomsheet.BottomSheetBehavior; import com.google.android.material.snackbar.Snackbar; @@ -68,9 +69,9 @@ import fr.free.nrw.commons.di.CommonsDaggerSupportFragment; import fr.free.nrw.commons.kvstore.JsonKvStore; import fr.free.nrw.commons.location.LocationPermissionsHelper; import fr.free.nrw.commons.location.LocationPermissionsHelper.LocationPermissionCallback; -import fr.free.nrw.commons.location.LatLng; import fr.free.nrw.commons.location.LocationServiceManager; import fr.free.nrw.commons.location.LocationUpdateListener; +import fr.free.nrw.commons.nearby.BottomSheetAdapter; import fr.free.nrw.commons.nearby.CheckBoxTriStates; import fr.free.nrw.commons.nearby.Label; import fr.free.nrw.commons.nearby.MarkerPlaceGroup; @@ -78,8 +79,10 @@ import fr.free.nrw.commons.nearby.NearbyController; import fr.free.nrw.commons.nearby.NearbyFilterSearchRecyclerViewAdapter; import fr.free.nrw.commons.nearby.NearbyFilterState; import fr.free.nrw.commons.nearby.Place; +import fr.free.nrw.commons.nearby.WikidataFeedback; import fr.free.nrw.commons.nearby.contract.NearbyParentFragmentContract; import fr.free.nrw.commons.nearby.fragments.AdvanceQueryFragment.Callback; +import fr.free.nrw.commons.nearby.model.BottomSheetItem; import fr.free.nrw.commons.nearby.presenter.NearbyParentFragmentPresenter; import fr.free.nrw.commons.upload.FileUtils; import fr.free.nrw.commons.utils.DialogUtil; @@ -132,7 +135,7 @@ import timber.log.Timber; public class NearbyParentFragment extends CommonsDaggerSupportFragment implements NearbyParentFragmentContract.View, WikidataEditListener.WikidataP18EditListener, LocationUpdateListener, - LocationPermissionCallback { + LocationPermissionCallback, BottomSheetAdapter.ItemClickListener { FragmentNearbyParentBinding binding; @@ -190,6 +193,9 @@ public class NearbyParentFragment extends CommonsDaggerSupportFragment private NearbyParentFragmentInstanceReadyCallback nearbyParentFragmentInstanceReadyCallback; private boolean isAdvancedQueryFragmentVisible = false; private Place nearestPlace; + private GridLayoutManager gridLayoutManager; + private List<BottomSheetItem> dataList; + private BottomSheetAdapter bottomSheetAdapter; private ActivityResultLauncher<String[]> inAppCameraLocationPermissionLauncher = registerForActivityResult( new ActivityResultContracts.RequestMultiplePermissions(), new ActivityResultCallback<Map<String, Boolean>>() { @@ -650,7 +656,6 @@ public class NearbyParentFragment extends CommonsDaggerSupportFragment initBottomSheets(); loadAnimations(); setBottomSheetCallbacks(); - decideButtonVisibilities(); addActionToTitle(); if (!Utils.isMonumentsEnabled(new Date())) { NearbyFilterState.setWlmSelected(false); @@ -674,6 +679,15 @@ public class NearbyParentFragment extends CommonsDaggerSupportFragment bottomSheetListBehavior.setState(BottomSheetBehavior.STATE_HIDDEN); } + private int getSpanCount() { + int orientation = getResources().getConfiguration().orientation; + if (bottomSheetAdapter != null){ + return (orientation == Configuration.ORIENTATION_PORTRAIT) ? 3 : bottomSheetAdapter.getItemCount(); + }else { + return (orientation == Configuration.ORIENTATION_PORTRAIT) ? 3 : 6; + } + } + public void initNearbyFilter() { binding.nearbyFilterList.getRoot().setVisibility(View.GONE); hideBottomSheet(); @@ -898,20 +912,6 @@ public class NearbyParentFragment extends CommonsDaggerSupportFragment rotate_backward = AnimationUtils.loadAnimation(getActivity(), R.anim.rotate_backward); } - /** - * Fits buttons according to our layout - */ - private void decideButtonVisibilities() { - // Remove button text if they exceed 1 line or if internal layout has not been built - // Only need to check for directions button because it is the longest - if ( binding.bottomSheetDetails.directionsButtonText.getLineCount() > 1 || binding.bottomSheetDetails.directionsButtonText.getLineCount() == 0) { - binding.bottomSheetDetails.wikipediaButtonText.setVisibility(View.GONE); - binding.bottomSheetDetails.wikidataButtonText.setVisibility(View.GONE); - binding.bottomSheetDetails.commonsButtonText.setVisibility(View.GONE); - binding.bottomSheetDetails.directionsButtonText.setVisibility(View.GONE); - } - } - /** * */ @@ -2006,50 +2006,33 @@ public class NearbyParentFragment extends CommonsDaggerSupportFragment */ private void passInfoToSheet(final Place place) { selectedPlace = place; + dataList = new ArrayList<>(); + // TODO: Decide button text for fitting in the screen + dataList.add(new BottomSheetItem(R.drawable.ic_round_star_border_24px, "")); + dataList.add(new BottomSheetItem(R.drawable.ic_directions_black_24dp, + getResources().getString(R.string.nearby_directions))); + if (place.hasWikidataLink()) { + dataList.add(new BottomSheetItem(R.drawable.ic_wikidata_logo_24dp, + getResources().getString(R.string.nearby_wikidata))); + } + dataList.add(new BottomSheetItem(R.drawable.ic_feedback_black_24dp, + getResources().getString(R.string.nearby_wikitalk))); + if (place.hasWikipediaLink()) { + dataList.add(new BottomSheetItem(R.drawable.ic_wikipedia_logo_24dp, + getResources().getString(R.string.nearby_wikipedia))); + } + if (selectedPlace.hasCommonsLink()) { + dataList.add(new BottomSheetItem(R.drawable.ic_commons_icon_vector, + getResources().getString(R.string.nearby_commons))); + } + int spanCount = getSpanCount(); + gridLayoutManager = new GridLayoutManager(this.getContext(), spanCount); + binding.bottomSheetDetails.bottomSheetRecyclerView.setLayoutManager(gridLayoutManager); + bottomSheetAdapter = new BottomSheetAdapter(this.getContext(), dataList); + bottomSheetAdapter.setClickListener(this); + binding.bottomSheetDetails.bottomSheetRecyclerView.setAdapter(bottomSheetAdapter); updateBookmarkButtonImage(selectedPlace); - binding.bottomSheetDetails.bookmarkButton.setOnClickListener(view -> { - final boolean isBookmarked = bookmarkLocationDao.updateBookmarkLocation(selectedPlace); - updateBookmarkButtonImage(selectedPlace); - updateMarker(isBookmarked, selectedPlace, locationManager.getLastLocation()); - binding.map.invalidate(); - }); - binding.bottomSheetDetails.bookmarkButton.setOnLongClickListener(view -> { - Toast.makeText(getContext(), R.string.menu_bookmark, Toast.LENGTH_SHORT).show(); - return true; - }); - - binding.bottomSheetDetails.wikipediaButton.setVisibility(place.hasWikipediaLink() ? View.VISIBLE : View.GONE); - binding.bottomSheetDetails.wikipediaButton.setOnClickListener( - view -> Utils.handleWebUrl(getContext(), selectedPlace.siteLinks.getWikipediaLink())); - binding.bottomSheetDetails.wikipediaButton.setOnLongClickListener(view -> { - Toast.makeText(getContext(), R.string.nearby_wikipedia, Toast.LENGTH_SHORT).show(); - return true; - }); - - binding.bottomSheetDetails.wikidataButton.setVisibility(place.hasWikidataLink() ? View.VISIBLE : View.GONE); - binding.bottomSheetDetails.wikidataButton.setOnClickListener( - view -> Utils.handleWebUrl(getContext(), selectedPlace.siteLinks.getWikidataLink())); - binding.bottomSheetDetails.wikidataButton.setOnLongClickListener(view -> { - Toast.makeText(getContext(), R.string.nearby_wikidata, Toast.LENGTH_SHORT).show(); - return true; - }); - - binding.bottomSheetDetails.directionsButton.setOnClickListener(view -> Utils.handleGeoCoordinates(getActivity(), - selectedPlace.getLocation())); - binding.bottomSheetDetails.directionsButton.setOnLongClickListener(view -> { - Toast.makeText(getContext(), R.string.nearby_directions, Toast.LENGTH_SHORT).show(); - return true; - }); - - binding.bottomSheetDetails.commonsButton.setVisibility(selectedPlace.hasCommonsLink() ? View.VISIBLE : View.GONE); - binding.bottomSheetDetails.commonsButton.setOnClickListener( - view -> Utils.handleWebUrl(getContext(), selectedPlace.siteLinks.getCommonsLink())); - binding.bottomSheetDetails.commonsButton.setOnLongClickListener(view -> { - Toast.makeText(getContext(), R.string.nearby_commons, Toast.LENGTH_SHORT).show(); - return true; - }); - binding.bottomSheetDetails.icon.setImageResource(selectedPlace.getLabel().getIcon()); binding.bottomSheetDetails.title.setText(selectedPlace.name); @@ -2101,9 +2084,7 @@ public class NearbyParentFragment extends CommonsDaggerSupportFragment } else { bookmarkIcon = R.drawable.ic_round_star_border_24px; } - if ( binding.bottomSheetDetails.bookmarkButtonImage != null) { - binding.bottomSheetDetails.bookmarkButtonImage.setImageResource(bookmarkIcon); - } + bottomSheetAdapter.updateBookmarkIcon(bookmarkIcon); } @Override @@ -2281,6 +2262,77 @@ public class NearbyParentFragment extends CommonsDaggerSupportFragment binding.map.getController().animateTo(geoPoint); } + @Override + public void onBottomSheetItemClick(@Nullable View view, int position) { + BottomSheetItem item = dataList.get(position); + final boolean isBookmarked = bookmarkLocationDao.updateBookmarkLocation(selectedPlace); + switch (item.getImageResourceId()) { + case R.drawable.ic_round_star_border_24px: + updateBookmarkButtonImage(selectedPlace); + updateMarker(isBookmarked, selectedPlace, locationManager.getLastLocation()); + binding.map.invalidate(); + break; + case R.drawable.ic_round_star_filled_24px: + updateBookmarkButtonImage(selectedPlace); + updateMarker(isBookmarked, selectedPlace, locationManager.getLastLocation()); + binding.map.invalidate(); + break; + case R.drawable.ic_directions_black_24dp: + Utils.handleGeoCoordinates(this.getContext(), selectedPlace.getLocation()); + break; + case R.drawable.ic_wikidata_logo_24dp: + Utils.handleWebUrl(this.getContext(), selectedPlace.siteLinks.getWikidataLink()); + break; + case R.drawable.ic_feedback_black_24dp: + Intent intent = new Intent(this.getContext(), WikidataFeedback.class); + intent.putExtra("place", selectedPlace.name); + intent.putExtra("qid", selectedPlace.getWikiDataEntityId()); + startActivity(intent); + break; + case R.drawable.ic_wikipedia_logo_24dp: + Utils.handleWebUrl(this.getContext(), selectedPlace.siteLinks.getWikipediaLink()); + break; + case R.drawable.ic_commons_icon_vector: + Utils.handleWebUrl(this.getContext(), selectedPlace.siteLinks.getCommonsLink()); + break; + default: + break; + } + } + + @Override + public void onBottomSheetItemLongClick(@Nullable View view, int position) { + BottomSheetItem item = dataList.get(position); + String message; + switch (item.getImageResourceId()) { + case R.drawable.ic_round_star_border_24px: + message = getString(R.string.menu_bookmark); + break; + case R.drawable.ic_round_star_filled_24px: + message = getString(R.string.menu_bookmark); + break; + case R.drawable.ic_directions_black_24dp: + message = getString(R.string.nearby_directions); + break; + case R.drawable.ic_wikidata_logo_24dp: + message = getString(R.string.nearby_wikidata); + break; + case R.drawable.ic_feedback_black_24dp: + message = getString(R.string.nearby_wikitalk); + break; + case R.drawable.ic_wikipedia_logo_24dp: + message = getString(R.string.nearby_wikipedia); + break; + case R.drawable.ic_commons_icon_vector: + message = getString(R.string.nearby_commons); + break; + default: + message = "Long click"; + break; + } + Toast.makeText(this.getContext(), message, Toast.LENGTH_SHORT).show(); + } + public interface NearbyParentFragmentInstanceReadyCallback { void onReady(); @@ -2298,9 +2350,12 @@ public class NearbyParentFragment extends CommonsDaggerSupportFragment rlBottomSheetLayoutParams.height = getActivity().getWindowManager().getDefaultDisplay().getHeight() / 16 * 9; binding.bottomSheetNearby.bottomSheet.setLayoutParams(rlBottomSheetLayoutParams); + int spanCount = getSpanCount(); + if (gridLayoutManager != null) { + gridLayoutManager.setSpanCount(spanCount); + } } - public void onLearnMoreClicked() { Intent intent = new Intent(Intent.ACTION_VIEW); intent.setData(Uri.parse(WLM_URL)); diff --git a/app/src/main/java/fr/free/nrw/commons/nearby/model/BottomSheetItem.kt b/app/src/main/java/fr/free/nrw/commons/nearby/model/BottomSheetItem.kt new file mode 100644 index 000000000..bf30be6ec --- /dev/null +++ b/app/src/main/java/fr/free/nrw/commons/nearby/model/BottomSheetItem.kt @@ -0,0 +1,3 @@ +package fr.free.nrw.commons.nearby.model + +class BottomSheetItem(var imageResourceId: Int, val title: String) diff --git a/app/src/main/java/fr/free/nrw/commons/nearby/model/WikiTalkResponse.kt b/app/src/main/java/fr/free/nrw/commons/nearby/model/WikiTalkResponse.kt new file mode 100644 index 000000000..02399e212 --- /dev/null +++ b/app/src/main/java/fr/free/nrw/commons/nearby/model/WikiTalkResponse.kt @@ -0,0 +1,12 @@ +package fr.free.nrw.commons.nearby.model + +data class Description( + val text: String, + val user: String, + val time: String +) + +data class Title( + val title: String, + val descriptions: List<Description> +) \ No newline at end of file diff --git a/app/src/main/res/layout/activity_wikidata_feedback.xml b/app/src/main/res/layout/activity_wikidata_feedback.xml new file mode 100644 index 000000000..c0fd60a76 --- /dev/null +++ b/app/src/main/res/layout/activity_wikidata_feedback.xml @@ -0,0 +1,132 @@ +<?xml version="1.0" encoding="utf-8"?> +<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:app="http://schemas.android.com/apk/res-auto" + xmlns:tools="http://schemas.android.com/tools" + android:layout_width="match_parent" + android:layout_height="match_parent" + tools:context=".nearby.WikidataFeedback"> + + <LinearLayout + android:id="@+id/toolbarLayout" + android:layout_width="0dp" + android:layout_height="?attr/actionBarSize" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toTopOf="parent"> + + <include + android:id="@+id/toolbarBinding" + layout="@layout/toolbar" /> + </LinearLayout> + + <ProgressBar + android:id="@+id/progress_bar" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintLeft_toLeftOf="parent" + app:layout_constraintRight_toRightOf="parent" + app:layout_constraintTop_toTopOf="parent" /> + + <androidx.constraintlayout.widget.ConstraintLayout + android:id="@+id/activity_layout" + android:layout_width="match_parent" + android:layout_height="0dp" + android:visibility="visible" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toBottomOf="@+id/toolbarLayout"> + + <androidx.recyclerview.widget.RecyclerView + android:id="@+id/recyclerView" + android:layout_width="match_parent" + android:layout_height="0dp" + android:layout_marginStart="8dp" + android:layout_marginEnd="8dp" + app:layout_constraintBottom_toTopOf="@+id/textHeader" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintHorizontal_bias="0.0" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toBottomOf="@+id/currentTextLabel" /> + + <androidx.appcompat.widget.AppCompatTextView + android:id="@+id/textHeader" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginStart="8dp" + android:layout_marginEnd="8dp" + android:text="Write something about the 'GS1 Japan' item. It will be publicly visible." + android:textSize="@dimen/normal_text" + android:visibility="visible" + app:layout_constraintBottom_toTopOf="@+id/descriptionEditText" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" /> + + <androidx.appcompat.widget.AppCompatEditText + android:id="@+id/subjectEditText" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_marginStart="8dp" + android:layout_marginTop="16dp" + android:layout_marginEnd="8dp" + android:hint="Subject" + android:singleLine="false" + android:visibility="gone" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toBottomOf="@+id/textHeader" /> + + <androidx.appcompat.widget.AppCompatEditText + android:id="@+id/descriptionEditText" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_marginStart="8dp" + android:layout_marginEnd="8dp" + android:hint="Description" + android:singleLine="false" + android:visibility="visible" + app:layout_constraintBottom_toTopOf="@+id/appCompatButton" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" /> + + <TextView + android:id="@+id/descText" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginStart="8dp" + android:layout_marginTop="8dp" + android:layout_marginEnd="8dp" + android:inputType="textMultiLine" + android:text="Discussion" + android:visibility="gone" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toBottomOf="@+id/currentTextLabel" /> + + <androidx.appcompat.widget.AppCompatButton + android:id="@+id/appCompatButton" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginEnd="8dp" + android:text="SEND" + android:visibility="visible" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintEnd_toEndOf="@+id/textHeader" + app:layout_constraintHorizontal_bias="1.0" + app:layout_constraintStart_toStartOf="@+id/textHeader" /> + + <androidx.appcompat.widget.AppCompatTextView + android:id="@+id/currentTextLabel" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginStart="8dp" + android:layout_marginEnd="8dp" + android:text="Current talk page:" + android:textSize="@dimen/heading_text_size" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toTopOf="parent" /> + + </androidx.constraintlayout.widget.ConstraintLayout> +</androidx.constraintlayout.widget.ConstraintLayout> \ No newline at end of file diff --git a/app/src/main/res/layout/bottom_sheet_details.xml b/app/src/main/res/layout/bottom_sheet_details.xml index a8889aafa..8c354daae 100644 --- a/app/src/main/res/layout/bottom_sheet_details.xml +++ b/app/src/main/res/layout/bottom_sheet_details.xml @@ -53,169 +53,21 @@ android:layout_width="match_parent" android:layout_height="@dimen/tiny_height" android:layout_marginTop="@dimen/small_height" - android:layout_marginBottom="@dimen/activity_margin_horizontal" - android:background="@android:color/darker_gray"/> - <LinearLayout - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:orientation="horizontal" - > + android:layout_marginBottom="@dimen/activity_margin_horizontal" + android:background="@android:color/darker_gray" /> - <LinearLayout - android:id="@+id/bookmarkButton" - android:layout_width="@dimen/dimen_0" - android:layout_height="wrap_content" - android:layout_weight="1" - android:padding="@dimen/standard_gap" - android:clickable="true" - android:orientation="vertical" - android:background="@drawable/button_background_selector" - > - <ImageView - android:id="@+id/bookmarkButtonImage" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_gravity="center_horizontal" - app:srcCompat="@drawable/ic_round_star_border_24px" - android:tint="?attr/rowButtonColor"/> - <TextView - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:id="@+id/bookmarkButtonText" - android:paddingTop="@dimen/activity_margin_horizontal" - android:layout_gravity="center_horizontal" - android:text="CAMERA" - android:visibility="gone" - /> - </LinearLayout> + <androidx.recyclerview.widget.RecyclerView + android:id="@+id/bottom_sheet_recycler_view" + android:layout_width="match_parent" + android:layout_height="wrap_content" /> - <LinearLayout - android:id="@+id/directionsButton" - android:layout_width="@dimen/dimen_0" - android:layout_height="wrap_content" - android:layout_weight="1" - android:padding="@dimen/standard_gap" - android:clickable="true" - android:background="@drawable/button_background_selector" - android:orientation="vertical" - > - <ImageView - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_gravity="center_horizontal" - android:duplicateParentState="true" - app:srcCompat="@drawable/ic_directions_black_24dp" - android:tint="?attr/rowButtonColor" - /> - <TextView - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:id="@+id/directionsButtonText" - android:paddingTop="@dimen/activity_margin_horizontal" - android:duplicateParentState="true" - android:textColor="@color/text_color_selector" - android:layout_gravity="center_horizontal" - android:text="@string/nearby_directions" - android:textAllCaps="true" - /> - </LinearLayout> - <LinearLayout - android:id="@+id/wikidataButton" - android:layout_width="@dimen/dimen_0" - android:layout_height="wrap_content" - android:layout_weight="1" - android:padding="@dimen/standard_gap" - android:clickable="true" - android:background="@drawable/button_background_selector" - android:orientation="vertical" - > - <ImageView - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_gravity="center_horizontal" - android:duplicateParentState="true" - app:srcCompat="@drawable/ic_wikidata_logo_24dp" - android:tint="?attr/rowButtonColor"/> - <TextView - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:id="@+id/wikidataButtonText" - android:paddingTop="@dimen/activity_margin_horizontal" - android:duplicateParentState="true" - android:textColor="@color/text_color_selector" - android:layout_gravity="center_horizontal" - android:text="@string/nearby_wikidata" - android:textAllCaps="true" - /> - </LinearLayout> - - <LinearLayout - android:id="@+id/wikipediaButton" - android:layout_width="@dimen/dimen_0" - android:layout_height="wrap_content" - android:layout_weight="1" - android:padding="@dimen/standard_gap" - android:clickable="true" - android:background="@drawable/button_background_selector" - android:orientation="vertical" - > - <ImageView - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_gravity="center_horizontal" - android:duplicateParentState="true" - app:srcCompat="@drawable/ic_wikipedia_logo_24dp" - android:tint="?attr/rowButtonColor" - /> - <TextView - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:id="@+id/wikipediaButtonText" - android:paddingTop="@dimen/activity_margin_horizontal" - android:duplicateParentState="true" - android:textColor="@color/text_color_selector" - android:layout_gravity="center_horizontal" - android:text="@string/nearby_wikipedia" - android:textAllCaps="true" - /> - </LinearLayout> - - <LinearLayout - android:id="@+id/commonsButton" - android:layout_width="@dimen/dimen_0" - android:layout_height="wrap_content" - android:layout_weight="1" - android:padding="@dimen/standard_gap" - android:clickable="true" - android:background="@drawable/button_background_selector" - android:orientation="vertical" - > - <ImageView - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_gravity="center_horizontal" - android:duplicateParentState="true" - app:srcCompat="@drawable/ic_commons_icon_vector" - /> - <TextView - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:id="@+id/commonsButtonText" - android:paddingTop="@dimen/activity_margin_horizontal" - android:duplicateParentState="true" - android:textColor="@color/text_color_selector" - android:layout_gravity="center_horizontal" - android:text="@string/nearby_commons" - android:textAllCaps="true" - /> - </LinearLayout> - </LinearLayout> <View - android:layout_width="match_parent" - android:layout_height="@dimen/tiny_height" - android:layout_marginTop="@dimen/small_height" - android:layout_marginBottom="@dimen/activity_margin_horizontal" - android:background="@android:color/darker_gray"/> + android:layout_width="match_parent" + android:layout_height="@dimen/tiny_height" + android:layout_marginTop="@dimen/small_height" + android:layout_marginBottom="@dimen/activity_margin_horizontal" + android:background="@android:color/darker_gray" /> + <TextView android:id="@+id/description" android:layout_width="match_parent" diff --git a/app/src/main/res/layout/bottom_sheet_item_layout.xml b/app/src/main/res/layout/bottom_sheet_item_layout.xml new file mode 100644 index 000000000..4f4c2c854 --- /dev/null +++ b/app/src/main/res/layout/bottom_sheet_item_layout.xml @@ -0,0 +1,27 @@ +<?xml version="1.0" encoding="utf-8"?> +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/bookmarkButton" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_columnWeight="1" + android:background="@drawable/button_background_selector" + android:clickable="true" + android:orientation="vertical" + android:padding="@dimen/standard_gap"> + + <ImageView + android:id="@+id/buttonImage" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_gravity="center_horizontal" + android:tint="?attr/rowButtonColor" /> + + <TextView + android:id="@+id/buttonText" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_gravity="center_horizontal" + android:paddingTop="@dimen/activity_margin_horizontal" + android:text="CAMERA" + android:visibility="gone" /> +</LinearLayout> \ No newline at end of file diff --git a/app/src/main/res/layout/layout_wikitalk_item.xml b/app/src/main/res/layout/layout_wikitalk_item.xml new file mode 100644 index 000000000..5d046f7b8 --- /dev/null +++ b/app/src/main/res/layout/layout_wikitalk_item.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="vertical" + > + + <TextView + android:id="@+id/titleTextView" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:paddingTop="@dimen/dimen_10" + android:paddingBottom="8dp" + android:textSize="20sp" + android:text="Title"/> + + <androidx.recyclerview.widget.RecyclerView + android:id="@+id/descriptionsRecyclerView" + android:layout_width="match_parent" + android:layout_height="wrap_content" /> + +</LinearLayout> \ No newline at end of file diff --git a/app/src/main/res/layout/layout_wikitalk_item_description.xml b/app/src/main/res/layout/layout_wikitalk_item_description.xml new file mode 100644 index 000000000..44d948425 --- /dev/null +++ b/app/src/main/res/layout/layout_wikitalk_item_description.xml @@ -0,0 +1,35 @@ +<?xml version="1.0" encoding="utf-8"?> +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginBottom="2dp" + android:orientation="vertical"> + + + <TextView + android:id="@+id/descTextView" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:text="TextView" + android:textSize="16sp" /> + + <LinearLayout + android:layout_width="match_parent" + android:layout_height="match_parent" + android:orientation="horizontal"> + + <TextView + android:id="@+id/userTextView" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_weight="1" + android:text="TextView" /> + + <TextView + android:id="@+id/dateTextView" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_weight="1" + android:text="TextView" /> + </LinearLayout> +</LinearLayout> \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 017404d2c..3beaa5b50 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -818,4 +818,5 @@ Upload your first media by tapping on the add button.</string> </plurals> <string name="multiple_files_depiction">Please remember that all images in a multi-upload get the same categories and depictions. If the images do not share depictions and categories, please perform several separate uploads.</string> <string name="multiple_files_depiction_header">Note about multi-uploads</string> + <string name="nearby_wikitalk">wikitalk</string> </resources>