diff --git a/app/build.gradle b/app/build.gradle index 9878b367..c7ba6d60 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -125,7 +125,7 @@ dependencies { implementation 'moe.banana:moshi-jsonapi:3.5.0' implementation 'moe.banana:moshi-jsonapi-retrofit-converter:3.5.0' implementation 'io.coil-kt:coil:1.1.0' - implementation 'com.github.MikeOrtiz:TouchImageView:3.0.3' + implementation 'com.github.johan12345:StfalconImageViewer:5082ebd392' implementation "com.mikepenz:aboutlibraries-core:$about_libs_version" implementation "com.mikepenz:aboutlibraries:$about_libs_version" implementation 'com.airbnb.android:lottie:3.4.0' diff --git a/app/src/main/java/net/vonforst/evmap/adapter/GalleryAdapter.kt b/app/src/main/java/net/vonforst/evmap/adapter/GalleryAdapter.kt index 8a48dde9..bcf85eab 100644 --- a/app/src/main/java/net/vonforst/evmap/adapter/GalleryAdapter.kt +++ b/app/src/main/java/net/vonforst/evmap/adapter/GalleryAdapter.kt @@ -2,7 +2,9 @@ package net.vonforst.evmap.adapter import android.annotation.SuppressLint import android.content.Context -import android.view.* +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup import android.widget.ImageView import androidx.recyclerview.widget.DiffUtil import androidx.recyclerview.widget.ListAdapter @@ -11,19 +13,11 @@ import coil.load import coil.memory.MemoryCache import coil.size.OriginalSize import coil.size.SizeResolver -import com.ortiz.touchview.TouchImageView import net.vonforst.evmap.R import net.vonforst.evmap.model.ChargerPhoto -class GalleryAdapter( - context: Context, - val itemClickListener: ItemClickListener? = null, - val detailView: Boolean = false, - val pageToLoad: Int? = null, - val imageCacheKey: MemoryCache.Key? = null, - val loadedListener: (() -> Unit)? = null -) : +class GalleryAdapter(context: Context, val itemClickListener: ItemClickListener? = null) : ListAdapter(ChargerPhotoDiffCallback()) { class ViewHolder(val view: ImageView) : RecyclerView.ViewHolder(view) @@ -38,104 +32,34 @@ class GalleryAdapter( @SuppressLint("ClickableViewAccessibility") override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { 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 { v, 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 || v.canScrollHorizontally(1) && v.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 - } + val 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() - } val id = getItem(position).id - val url = if (detailView) { - getItem(position).getUrl(size = 1000) - } else { - getItem(position).getUrl(height = holder.view.height) - } + val url = getItem(position).getUrl(height = holder.view.height) holder.view.load( url ) { - if (pageToLoad == position && imageCacheKey != null) { - placeholderMemoryCacheKey(imageCacheKey) - } size(SizeResolver(OriginalSize)) allowHardware(false) listener( onSuccess = { _, metadata -> memoryKeys[id] = metadata.memoryCacheKey - if (pageToLoad == position) invokeLoadedListener(holder.view) - }, - onError = { _, _ -> - if (!loaded && loadedListener != null && pageToLoad == position) { - loadedListener.invoke() - loaded = true - } } ) } - if (pageToLoad == position && imageCacheKey != null) { - // start transition immediately - if (pageToLoad == position) invokeLoadedListener(holder.view) - } - holder.view.transitionName = galleryTransitionName(position) + if (itemClickListener != null) { holder.view.setOnClickListener { itemClickListener.onItemClick(holder.view, position, memoryKeys[id]) } } } - - private fun invokeLoadedListener( - view: ImageView - ) { - if (!loaded && loadedListener != null) { - view.viewTreeObserver.addOnPreDrawListener(object : - ViewTreeObserver.OnPreDrawListener { - override fun onPreDraw(): Boolean { - view.viewTreeObserver.removeOnPreDrawListener(this) - loadedListener.invoke() - return true - } - }) - loaded = true - } - } } -fun galleryTransitionName(position: Int) = "gallery_$position" - class ChargerPhotoDiffCallback : DiffUtil.ItemCallback() { override fun areItemsTheSame(oldItem: ChargerPhoto, newItem: ChargerPhoto): Boolean { return oldItem.id == newItem.id diff --git a/app/src/main/java/net/vonforst/evmap/fragment/GalleryFragment.kt b/app/src/main/java/net/vonforst/evmap/fragment/GalleryFragment.kt deleted file mode 100644 index 10c16c91..00000000 --- a/app/src/main/java/net/vonforst/evmap/fragment/GalleryFragment.kt +++ /dev/null @@ -1,136 +0,0 @@ -package net.vonforst.evmap.fragment - -import android.os.Bundle -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import androidx.activity.OnBackPressedCallback -import androidx.core.app.SharedElementCallback -import androidx.databinding.DataBindingUtil -import androidx.fragment.app.Fragment -import androidx.fragment.app.activityViewModels -import androidx.navigation.fragment.findNavController -import androidx.transition.TransitionInflater -import androidx.viewpager2.widget.ViewPager2 -import coil.memory.MemoryCache -import com.ortiz.touchview.TouchImageView -import net.vonforst.evmap.R -import net.vonforst.evmap.adapter.GalleryAdapter -import net.vonforst.evmap.databinding.FragmentGalleryBinding -import net.vonforst.evmap.model.ChargerPhoto -import net.vonforst.evmap.viewmodel.GalleryViewModel - - -class GalleryFragment : Fragment() { - companion object { - private const val EXTRA_POSITION = "position" - private const val EXTRA_PHOTOS = "photos" - private const val EXTRA_IMAGE_CACHE_KEY = "image_cache_key" - private const val SAVED_CURRENT_PAGE_POSITION = "current_page_position" - - fun buildArgs( - photos: List, - position: Int, - imageCacheKey: MemoryCache.Key? - ): Bundle { - return Bundle().apply { - putParcelableArrayList(EXTRA_PHOTOS, ArrayList(photos)) - putInt(EXTRA_POSITION, position) - putParcelable(EXTRA_IMAGE_CACHE_KEY, imageCacheKey) - } - } - } - - private lateinit var binding: FragmentGalleryBinding - private var startingPosition: Int = 0 - private var currentPosition: Int = 0 - private lateinit var galleryAdapter: GalleryAdapter - private var currentPage: TouchImageView? = null - private val galleryVm: GalleryViewModel by activityViewModels() - - private val backPressedCallback = object : - OnBackPressedCallback(true) { - override fun handleOnBackPressed() { - val image = currentPage - if (image != null && image.currentZoom !in 0.95f..1.05f) { - image.setZoomAnimated(1f, 0.5f, 0.5f) - } else { - galleryVm.galleryPosition.value = currentPosition - findNavController().popBackStack() - } - } - } - - override fun onCreateView( - inflater: LayoutInflater, - container: ViewGroup?, - savedInstanceState: Bundle? - ): View { - binding = DataBindingUtil.inflate( - inflater, - R.layout.fragment_gallery, container, false - ) - binding.lifecycleOwner = this - - val args = requireArguments() - startingPosition = args.getInt(EXTRA_POSITION, 0) - currentPosition = - savedInstanceState?.getInt(SAVED_CURRENT_PAGE_POSITION) ?: startingPosition - - galleryAdapter = - GalleryAdapter( - requireContext(), detailView = true, pageToLoad = currentPosition, - imageCacheKey = args.getParcelable(EXTRA_IMAGE_CACHE_KEY) - ) { - startPostponedEnterTransition() - } - binding.gallery.setPageTransformer { page, _ -> - val v = page as TouchImageView - currentPage = v - } - binding.gallery.adapter = galleryAdapter - binding.photos = args.getParcelableArrayList(EXTRA_PHOTOS) - - binding.gallery.post { - binding.gallery.setCurrentItem(currentPosition, false) - binding.gallery.registerOnPageChangeCallback(object : - ViewPager2.OnPageChangeCallback() { - override fun onPageSelected(position: Int) { - currentPosition = position - } - }) - } - - sharedElementEnterTransition = TransitionInflater.from(context) - .inflateTransition(R.transition.image_shared_element_transition) - sharedElementReturnTransition = TransitionInflater.from(context) - .inflateTransition(R.transition.image_shared_element_transition) - setEnterSharedElementCallback(enterElementCallback) - if (savedInstanceState == null) { - postponeEnterTransition(); - } - - requireActivity().onBackPressedDispatcher.addCallback( - viewLifecycleOwner, - backPressedCallback - ) - - return binding.root - } - - override fun onSaveInstanceState(outState: Bundle) { - super.onSaveInstanceState(outState) - outState.putInt(SAVED_CURRENT_PAGE_POSITION, currentPosition) - } - - private val enterElementCallback: SharedElementCallback = object : SharedElementCallback() { - override fun onMapSharedElements( - names: MutableList, - sharedElements: MutableMap - ) { - val currentPage = currentPage ?: return - sharedElements[names[0]] = currentPage - } - } - -} \ No newline at end of file diff --git a/app/src/main/java/net/vonforst/evmap/fragment/MapFragment.kt b/app/src/main/java/net/vonforst/evmap/fragment/MapFragment.kt index 7d3ca0f4..10ee61b2 100644 --- a/app/src/main/java/net/vonforst/evmap/fragment/MapFragment.kt +++ b/app/src/main/java/net/vonforst/evmap/fragment/MapFragment.kt @@ -10,8 +10,8 @@ import android.graphics.Color import android.location.Geocoder import android.location.Location import android.os.Bundle -import android.os.Handler import android.view.* +import android.widget.ImageView import android.widget.TextView import android.widget.Toast import androidx.activity.OnBackPressedCallback @@ -19,7 +19,6 @@ import androidx.annotation.RequiresPermission import androidx.appcompat.app.AlertDialog import androidx.appcompat.widget.PopupMenu import androidx.core.app.ActivityCompat -import androidx.core.app.SharedElementCallback import androidx.core.content.ContextCompat import androidx.core.view.MenuCompat import androidx.core.view.updateLayoutParams @@ -31,14 +30,16 @@ import androidx.lifecycle.Lifecycle import androidx.lifecycle.Observer import androidx.lifecycle.lifecycleScope import androidx.navigation.findNavController -import androidx.navigation.fragment.FragmentNavigatorExtras import androidx.navigation.fragment.findNavController import androidx.navigation.ui.setupWithNavController import androidx.recyclerview.widget.DividerItemDecoration import androidx.recyclerview.widget.LinearLayoutManager import androidx.transition.TransitionInflater import androidx.transition.TransitionManager +import coil.load import coil.memory.MemoryCache +import coil.size.OriginalSize +import coil.size.SizeResolver import com.car2go.maps.AnyMap import com.car2go.maps.MapFragment import com.car2go.maps.OnMapReadyCallback @@ -57,6 +58,7 @@ import com.mapzen.android.lost.api.LocationListener import com.mapzen.android.lost.api.LocationRequest import com.mapzen.android.lost.api.LocationServices import com.mapzen.android.lost.api.LostApiClient +import com.stfalcon.imageviewer.StfalconImageViewer import io.michaelrocks.bimap.HashBiMap import io.michaelrocks.bimap.MutableBiMap import kotlinx.coroutines.Dispatchers @@ -173,7 +175,6 @@ class MapFragment : Fragment(), OnMapReadyCallback, MapsActivity.FragmentCallbac } setHasOptionsMenu(true) - postponeEnterTransition() binding.root.setOnApplyWindowInsetsListener { _, insets -> binding.detailAppBar.toolbar.updateLayoutParams { @@ -194,7 +195,6 @@ class MapFragment : Fragment(), OnMapReadyCallback, MapsActivity.FragmentCallbac insets } - setExitSharedElementCallback(reenterSharedElementCallback) exitTransition = TransitionInflater.from(requireContext()) .inflateTransition(R.transition.map_exit_transition) @@ -537,24 +537,35 @@ class MapFragment : Fragment(), OnMapReadyCallback, MapsActivity.FragmentCallbac } private fun setupAdapters() { + var viewer: StfalconImageViewer? = null val galleryClickListener = object : GalleryAdapter.ItemClickListener { override fun onItemClick(view: View, position: Int, imageCacheKey: MemoryCache.Key?) { val photos = vm.charger.value?.data?.photos ?: return - val extras = FragmentNavigatorExtras(view to view.transitionName) - view.findNavController().navigate( - R.id.action_map_to_galleryFragment, - GalleryFragment.buildArgs(photos, position, imageCacheKey), - null, - extras - ) + + viewer = StfalconImageViewer.Builder(context, photos) { imageView, photo -> + imageView.load(photo.getUrl(size = 1000)) { + if (photo == photos[position] && imageCacheKey != null) { + placeholderMemoryCacheKey(imageCacheKey) + } + size(SizeResolver(OriginalSize)) + allowHardware(false) + } + } + .withTransitionFrom(view as ImageView) + .withImageChangeListener { + binding.gallery.layoutManager!!.scrollToPosition(it) + binding.gallery.layoutManager!!.findViewByPosition(it)?.let { + viewer?.updateTransitionImage(it as ImageView) + } + } + .withStartPosition(position) + .show() + } } - val galleryPosition = galleryVm.galleryPosition.value binding.gallery.apply { - adapter = GalleryAdapter(context, galleryClickListener, pageToLoad = galleryPosition) { - startPostponedEnterTransition() - } + adapter = GalleryAdapter(context, galleryClickListener) itemAnimator = null layoutManager = LinearLayoutManager(context, LinearLayoutManager.HORIZONTAL, false) @@ -565,41 +576,6 @@ class MapFragment : Fragment(), OnMapReadyCallback, MapsActivity.FragmentCallbac setDrawable(ContextCompat.getDrawable(context, R.drawable.gallery_divider)!!) }) } - if (galleryPosition == null) { - startPostponedEnterTransition() - } else { - binding.gallery.addOnLayoutChangeListener(object : View.OnLayoutChangeListener { - override fun onLayoutChange( - v: View, - left: Int, - top: Int, - right: Int, - bottom: Int, - oldLeft: Int, - oldTop: Int, - oldRight: Int, - oldBottom: Int - ) { - v.removeOnLayoutChangeListener(this) - val layoutManager = binding.gallery.layoutManager!! - val viewAtPosition = layoutManager.findViewByPosition(galleryPosition) - if (viewAtPosition == null || layoutManager.isViewPartiallyVisible( - viewAtPosition, - false, - true - ) - ) { - binding.gallery.post { - layoutManager.scrollToPosition(galleryPosition) - } - } - } - }) - // make sure that the app does not freeze waiting for a picture to load - Handler().postDelayed({ - startPostponedEnterTransition() - }, 100) - } binding.detailView.connectors.apply { adapter = ConnectorAdapter() @@ -1111,23 +1087,6 @@ class MapFragment : Fragment(), OnMapReadyCallback, MapsActivity.FragmentCallbac return binding.root } - private val reenterSharedElementCallback: SharedElementCallback = - object : SharedElementCallback() { - override fun onMapSharedElements( - names: MutableList, - sharedElements: MutableMap - ) { - // Locate the ViewHolder for the clicked position. - val position = galleryVm.galleryPosition.value ?: return - - val vh = binding.gallery.findViewHolderForAdapterPosition(position) - if (vh?.itemView == null) return - - // Map the first shared element name to the child ImageView. - sharedElements[names[0]] = vh.itemView - } - } - companion object { fun showCharger(charger: ChargeLocation): Bundle { return Bundle().apply { diff --git a/app/src/main/res/layout/fragment_gallery.xml b/app/src/main/res/layout/fragment_gallery.xml deleted file mode 100644 index 819c3789..00000000 --- a/app/src/main/res/layout/fragment_gallery.xml +++ /dev/null @@ -1,22 +0,0 @@ - - - - - - - - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/layout/gallery_item_fullscreen.xml b/app/src/main/res/layout/gallery_item_fullscreen.xml deleted file mode 100644 index d6cbc35c..00000000 --- a/app/src/main/res/layout/gallery_item_fullscreen.xml +++ /dev/null @@ -1,8 +0,0 @@ - - \ No newline at end of file diff --git a/app/src/main/res/navigation/nav_graph.xml b/app/src/main/res/navigation/nav_graph.xml index c8364e51..8a26722d 100644 --- a/app/src/main/res/navigation/nav_graph.xml +++ b/app/src/main/res/navigation/nav_graph.xml @@ -9,13 +9,6 @@ android:name="net.vonforst.evmap.fragment.MapFragment" android:label="MapFragment" tools:layout="@layout/fragment_map"> - -