mirror of
https://github.com/ev-map/EVMap.git
synced 2026-04-23 23:57:08 -04:00
add gallery fullscreen view
This commit is contained in:
106
app/src/main/java/com/johan/evmap/GalleryActivity.kt
Normal file
106
app/src/main/java/com/johan/evmap/GalleryActivity.kt
Normal file
@@ -0,0 +1,106 @@
|
||||
package com.johan.evmap
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.core.app.ActivityCompat
|
||||
import androidx.core.app.SharedElementCallback
|
||||
import androidx.databinding.DataBindingUtil
|
||||
import androidx.viewpager2.widget.ViewPager2
|
||||
import com.johan.evmap.adapter.GalleryAdapter
|
||||
import com.johan.evmap.adapter.galleryTransitionName
|
||||
import com.johan.evmap.databinding.ActivityGalleryBinding
|
||||
import com.ortiz.touchview.TouchImageView
|
||||
|
||||
|
||||
class GalleryActivity : AppCompatActivity() {
|
||||
companion object {
|
||||
const val EXTRA_POSITION = "position"
|
||||
const val EXTRA_PHOTOS = "photos"
|
||||
private const val SAVED_CURRENT_PAGE_POSITION = "current_page_position"
|
||||
}
|
||||
|
||||
private lateinit var binding: ActivityGalleryBinding
|
||||
private var isReturning: Boolean = false
|
||||
private var startingPosition: Int = 0
|
||||
private var currentPosition: Int = 0
|
||||
private lateinit var galleryAdapter: GalleryAdapter
|
||||
private var currentPage: TouchImageView? = null
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
binding = DataBindingUtil.setContentView(this, R.layout.activity_gallery)
|
||||
ActivityCompat.postponeEnterTransition(this)
|
||||
ActivityCompat.setEnterSharedElementCallback(this, enterElementCallback)
|
||||
|
||||
startingPosition = intent.getIntExtra(EXTRA_POSITION, 0)
|
||||
currentPosition =
|
||||
savedInstanceState?.getInt(SAVED_CURRENT_PAGE_POSITION) ?: startingPosition
|
||||
|
||||
galleryAdapter = GalleryAdapter(this, detailView = true, pageToLoad = currentPosition) {
|
||||
ActivityCompat.startPostponedEnterTransition(this)
|
||||
}
|
||||
binding.gallery.setPageTransformer { page, position ->
|
||||
val v = page as TouchImageView
|
||||
currentPage = v
|
||||
}
|
||||
binding.gallery.adapter = galleryAdapter
|
||||
binding.photos = intent.getParcelableArrayListExtra(EXTRA_PHOTOS)
|
||||
|
||||
binding.gallery.post {
|
||||
binding.gallery.setCurrentItem(currentPosition, false)
|
||||
binding.gallery.registerOnPageChangeCallback(object :
|
||||
ViewPager2.OnPageChangeCallback() {
|
||||
override fun onPageSelected(position: Int) {
|
||||
currentPosition = position
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
override fun onSaveInstanceState(outState: Bundle) {
|
||||
super.onSaveInstanceState(outState)
|
||||
outState.putInt(SAVED_CURRENT_PAGE_POSITION, currentPosition)
|
||||
}
|
||||
|
||||
override fun finishAfterTransition() {
|
||||
isReturning = true
|
||||
val data = Intent()
|
||||
data.putExtra(MapsActivity.EXTRA_STARTING_GALLERY_POSITION, startingPosition)
|
||||
data.putExtra(MapsActivity.EXTRA_CURRENT_GALLERY_POSITION, currentPosition)
|
||||
setResult(Activity.RESULT_OK, data)
|
||||
super.finishAfterTransition()
|
||||
}
|
||||
|
||||
private val enterElementCallback: SharedElementCallback = object : SharedElementCallback() {
|
||||
override fun onMapSharedElements(
|
||||
names: MutableList<String>,
|
||||
sharedElements: MutableMap<String, View>
|
||||
) {
|
||||
if (isReturning) {
|
||||
val index = binding.gallery.currentItem
|
||||
val currentPage = currentPage ?: return
|
||||
|
||||
if (startingPosition != currentPosition) {
|
||||
names.clear()
|
||||
names.add(galleryTransitionName(index))
|
||||
|
||||
sharedElements.clear()
|
||||
sharedElements[galleryTransitionName(index)] = currentPage
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onBackPressed() {
|
||||
val image = currentPage
|
||||
if (image == null || image.currentZoom in 0.95f..1.05f) {
|
||||
super.onBackPressed()
|
||||
} else {
|
||||
image.setZoomAnimated(1f, 0.5f, 0.5f)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,14 +1,19 @@
|
||||
package com.johan.evmap
|
||||
|
||||
import android.Manifest
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Intent
|
||||
import android.content.pm.PackageManager
|
||||
import android.content.res.Configuration
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import android.view.ViewTreeObserver
|
||||
import androidx.activity.viewModels
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.core.app.ActivityCompat
|
||||
import androidx.core.app.ActivityOptionsCompat
|
||||
import androidx.core.app.SharedElementCallback
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.databinding.DataBindingUtil
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
@@ -27,6 +32,7 @@ import com.google.android.material.snackbar.Snackbar
|
||||
import com.johan.evmap.adapter.ConnectorAdapter
|
||||
import com.johan.evmap.adapter.DetailAdapter
|
||||
import com.johan.evmap.adapter.GalleryAdapter
|
||||
import com.johan.evmap.adapter.galleryTransitionName
|
||||
import com.johan.evmap.api.*
|
||||
import com.johan.evmap.databinding.ActivityMapsBinding
|
||||
import com.johan.evmap.ui.ClusterIconGenerator
|
||||
@@ -61,17 +67,21 @@ class MapsActivity : AppCompatActivity(), OnMapReadyCallback {
|
||||
private var markers: Map<Marker, ChargeLocation> = emptyMap()
|
||||
private var clusterMarkers: List<Marker> = emptyList()
|
||||
|
||||
private var reenterState: Bundle? = null
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
fusedLocationClient = LocationServices.getFusedLocationProviderClient(this)
|
||||
api = GoingElectricApi.create(getString(R.string.goingelectric_key))
|
||||
|
||||
binding = DataBindingUtil.setContentView(this, R.layout.activity_maps)
|
||||
binding.lifecycleOwner = this
|
||||
binding.vm = vm
|
||||
|
||||
fusedLocationClient = LocationServices.getFusedLocationProviderClient(this)
|
||||
ActivityCompat.setExitSharedElementCallback(this, exitElementCallback)
|
||||
|
||||
val mapFragment = supportFragmentManager.findFragmentById(R.id.map) as SupportMapFragment
|
||||
mapFragment.getMapAsync(this)
|
||||
api = GoingElectricApi.create(getString(R.string.goingelectric_key))
|
||||
|
||||
setupAdapters()
|
||||
|
||||
@@ -144,8 +154,22 @@ class MapsActivity : AppCompatActivity(), OnMapReadyCallback {
|
||||
}
|
||||
|
||||
private fun setupAdapters() {
|
||||
val galleryClickListener = object : GalleryAdapter.ItemClickListener {
|
||||
override fun onItemClick(view: View, position: Int) {
|
||||
val photos = vm.charger.value?.photos ?: return
|
||||
val intent = Intent(this@MapsActivity, GalleryActivity::class.java).apply {
|
||||
putExtra(GalleryActivity.EXTRA_PHOTOS, ArrayList<ChargerPhoto>(photos))
|
||||
putExtra(GalleryActivity.EXTRA_POSITION, position)
|
||||
}
|
||||
val options = ActivityOptionsCompat.makeSceneTransitionAnimation(
|
||||
this@MapsActivity, view, view.transitionName
|
||||
)
|
||||
startActivity(intent, options.toBundle())
|
||||
}
|
||||
}
|
||||
|
||||
binding.gallery.apply {
|
||||
adapter = GalleryAdapter(this@MapsActivity)
|
||||
adapter = GalleryAdapter(this@MapsActivity, galleryClickListener)
|
||||
layoutManager =
|
||||
LinearLayoutManager(this@MapsActivity, LinearLayoutManager.HORIZONTAL, false)
|
||||
addItemDecoration(DividerItemDecoration(
|
||||
@@ -211,6 +235,7 @@ class MapsActivity : AppCompatActivity(), OnMapReadyCallback {
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressLint("MissingPermission")
|
||||
private fun enableLocation(animate: Boolean) {
|
||||
val map = this.map ?: return
|
||||
map.isMyLocationEnabled = true
|
||||
@@ -333,4 +358,58 @@ class MapsActivity : AppCompatActivity(), OnMapReadyCallback {
|
||||
else -> super.onRequestPermissionsResult(requestCode, permissions, grantResults)
|
||||
}
|
||||
}
|
||||
|
||||
private val exitElementCallback = object : SharedElementCallback() {
|
||||
override fun onMapSharedElements(
|
||||
names: MutableList<String>,
|
||||
sharedElements: MutableMap<String, View>
|
||||
) {
|
||||
if (reenterState != null) {
|
||||
val startingPosition = reenterState!!.getInt(EXTRA_STARTING_GALLERY_POSITION)
|
||||
val currentPosition = reenterState!!.getInt(EXTRA_CURRENT_GALLERY_POSITION)
|
||||
if (startingPosition != currentPosition) {
|
||||
// Current element has changed, need to override previous exit transitions
|
||||
val newTransitionName = galleryTransitionName(currentPosition)
|
||||
val newSharedElement =
|
||||
binding.gallery.findViewHolderForAdapterPosition(currentPosition)?.itemView
|
||||
if (newSharedElement != null) {
|
||||
names.clear()
|
||||
names.add(newTransitionName)
|
||||
|
||||
sharedElements.clear()
|
||||
sharedElements[newTransitionName] = newSharedElement
|
||||
}
|
||||
}
|
||||
reenterState = null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onActivityReenter(resultCode: Int, data: Intent) {
|
||||
// returning to gallery
|
||||
super.onActivityReenter(resultCode, data)
|
||||
reenterState = Bundle(data.extras)
|
||||
reenterState?.let {
|
||||
val startingPosition = it.getInt(EXTRA_STARTING_GALLERY_POSITION)
|
||||
val currentPosition = it.getInt(EXTRA_CURRENT_GALLERY_POSITION)
|
||||
if (startingPosition != currentPosition) binding.gallery.scrollToPosition(
|
||||
currentPosition
|
||||
)
|
||||
ActivityCompat.postponeEnterTransition(this)
|
||||
|
||||
binding.gallery.viewTreeObserver.addOnPreDrawListener(object :
|
||||
ViewTreeObserver.OnPreDrawListener {
|
||||
override fun onPreDraw(): Boolean {
|
||||
binding.gallery.viewTreeObserver.removeOnPreDrawListener(this)
|
||||
ActivityCompat.startPostponedEnterTransition(this@MapsActivity)
|
||||
return true
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val EXTRA_STARTING_GALLERY_POSITION = "extra_starting_item_position"
|
||||
const val EXTRA_CURRENT_GALLERY_POSITION = "extra_current_item_position"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,41 +1,122 @@
|
||||
package com.johan.evmap.adapter
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Context
|
||||
import android.view.LayoutInflater
|
||||
import android.view.ViewGroup
|
||||
import android.view.*
|
||||
import android.widget.ImageView
|
||||
import androidx.recyclerview.widget.DiffUtil
|
||||
import androidx.recyclerview.widget.ListAdapter
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.johan.evmap.R
|
||||
import com.johan.evmap.api.ChargerPhoto
|
||||
import com.ortiz.touchview.TouchImageView
|
||||
import com.squareup.picasso.Callback
|
||||
import com.squareup.picasso.Picasso
|
||||
|
||||
class GalleryAdapter(context: Context) :
|
||||
|
||||
class GalleryAdapter(
|
||||
context: Context,
|
||||
val itemClickListener: ItemClickListener? = null,
|
||||
val detailView: Boolean = false,
|
||||
val pageToLoad: Int? = null,
|
||||
val loadedListener: (() -> Unit)? = null
|
||||
) :
|
||||
ListAdapter<ChargerPhoto, GalleryAdapter.ViewHolder>(ChargerPhotoDiffCallback()) {
|
||||
class ViewHolder(val view: ImageView) : RecyclerView.ViewHolder(view)
|
||||
|
||||
val apikey = context.getString(R.string.goingelectric_key)
|
||||
interface ItemClickListener {
|
||||
fun onItemClick(view: View, position: Int)
|
||||
}
|
||||
|
||||
val apikey = context.getString(R.string.goingelectric_key)
|
||||
var loaded = false
|
||||
|
||||
@SuppressLint("ClickableViewAccessibility")
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
|
||||
val view = LayoutInflater.from(parent.context)
|
||||
.inflate(R.layout.gallery_item, parent, false) as ImageView
|
||||
val inflater = LayoutInflater.from(parent.context)
|
||||
val view: ImageView
|
||||
if (detailView) {
|
||||
view = inflater.inflate(R.layout.gallery_item_fullscreen, parent, false) as ImageView
|
||||
view.setOnTouchListener { view, event ->
|
||||
var result = true
|
||||
//can scroll horizontally checks if there's still a part of the image
|
||||
//that can be scrolled until you reach the edge
|
||||
if (event.pointerCount >= 2 || view.canScrollHorizontally(1) && view.canScrollHorizontally(
|
||||
-1
|
||||
)
|
||||
) {
|
||||
//multi-touch event
|
||||
result = when (event.action) {
|
||||
MotionEvent.ACTION_DOWN, MotionEvent.ACTION_MOVE -> {
|
||||
// Disallow RecyclerView to intercept touch events.
|
||||
parent.requestDisallowInterceptTouchEvent(true)
|
||||
// Disable touch on view
|
||||
false
|
||||
}
|
||||
MotionEvent.ACTION_UP -> {
|
||||
// Allow RecyclerView to intercept touch events.
|
||||
parent.requestDisallowInterceptTouchEvent(false)
|
||||
true
|
||||
}
|
||||
else -> true
|
||||
}
|
||||
}
|
||||
result
|
||||
}
|
||||
} else {
|
||||
view = inflater.inflate(R.layout.gallery_item, parent, false) as ImageView
|
||||
}
|
||||
return ViewHolder(view)
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
|
||||
if (detailView) {
|
||||
(holder.view as TouchImageView).resetZoom()
|
||||
}
|
||||
Picasso.get()
|
||||
.load(
|
||||
"https://api.goingelectric.de/chargepoints/photo/?key=${apikey}&id=${getItem(
|
||||
position
|
||||
).id}&height=${holder.view.height}"
|
||||
"https://api.goingelectric.de/chargepoints/photo/?key=${apikey}" +
|
||||
"&id=${getItem(position).id}" +
|
||||
if (detailView) {
|
||||
"&size=1000"
|
||||
} else {
|
||||
"&height=${holder.view.height}"
|
||||
}
|
||||
)
|
||||
.into(holder.view)
|
||||
.into(holder.view, object : Callback {
|
||||
override fun onSuccess() {
|
||||
if (!loaded && loadedListener != null && pageToLoad == position) {
|
||||
holder.view.viewTreeObserver.addOnPreDrawListener(object :
|
||||
ViewTreeObserver.OnPreDrawListener {
|
||||
override fun onPreDraw(): Boolean {
|
||||
holder.view.viewTreeObserver.removeOnPreDrawListener(this)
|
||||
loadedListener.invoke()
|
||||
return true
|
||||
}
|
||||
})
|
||||
loaded = true
|
||||
}
|
||||
}
|
||||
|
||||
override fun onError(e: Exception?) {
|
||||
if (!loaded && loadedListener != null && pageToLoad == position) {
|
||||
loadedListener.invoke()
|
||||
loaded = true
|
||||
}
|
||||
}
|
||||
|
||||
})
|
||||
holder.view.transitionName = galleryTransitionName(position)
|
||||
if (itemClickListener != null) {
|
||||
holder.view.setOnClickListener {
|
||||
itemClickListener.onItemClick(holder.view, position)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
fun galleryTransitionName(position: Int) = "gallery_$position"
|
||||
|
||||
class ChargerPhotoDiffCallback : DiffUtil.ItemCallback<ChargerPhoto>() {
|
||||
override fun areItemsTheSame(oldItem: ChargerPhoto, newItem: ChargerPhoto): Boolean {
|
||||
return oldItem.id == newItem.id
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package com.johan.evmap.api
|
||||
|
||||
import android.content.Context
|
||||
import android.os.Parcelable
|
||||
import androidx.core.text.HtmlCompat
|
||||
import com.johan.evmap.R
|
||||
import com.johan.evmap.adapter.Equatable
|
||||
@@ -144,7 +145,8 @@ data class Hours(
|
||||
)
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class ChargerPhoto(val id: String)
|
||||
@Parcelize
|
||||
data class ChargerPhoto(val id: String) : Parcelable
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class ChargeLocationCluster(
|
||||
|
||||
@@ -7,6 +7,7 @@ import android.widget.ImageView
|
||||
import androidx.databinding.BindingAdapter
|
||||
import androidx.recyclerview.widget.ListAdapter
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import androidx.viewpager2.widget.ViewPager2
|
||||
import com.google.android.material.floatingactionbutton.FloatingActionButton
|
||||
import com.johan.evmap.R
|
||||
|
||||
@@ -39,6 +40,13 @@ fun <T> setRecyclerViewData(recyclerView: RecyclerView, items: List<T>?) {
|
||||
}
|
||||
}
|
||||
|
||||
@BindingAdapter("data")
|
||||
fun <T> setRecyclerViewData(recyclerView: ViewPager2, items: List<T>?) {
|
||||
if (recyclerView.adapter is ListAdapter<*, *>) {
|
||||
(recyclerView.adapter as ListAdapter<T, *>).submitList(items)
|
||||
}
|
||||
}
|
||||
|
||||
@BindingAdapter("connectorIcon")
|
||||
fun getConnectorItem(view: ImageView, type: String) {
|
||||
view.setImageResource(
|
||||
|
||||
Reference in New Issue
Block a user