Skip to content

Instantly share code, notes, and snippets.

@AndSky90
Created November 4, 2019 14:16
Show Gist options
  • Save AndSky90/bb5cfe6e6413edb070e20547ec5a9277 to your computer and use it in GitHub Desktop.
Save AndSky90/bb5cfe6e6413edb070e20547ec5a9277 to your computer and use it in GitHub Desktop.
PhotoView ImageGallery Custom
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:fitsSystemWindows="true"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@android:color/black"
tools:context=".ui.activity.ImageViewerActivity">
<androidx.appcompat.widget.Toolbar
android:id="@+id/topToolbar"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
tools:layout_height="@dimen/toolbar_height_56dp"
android:elevation="@dimen/elevation_default_3dp"
android:titleTextAppearance="@style/ImageViewerToolbarTextAppearance"
style="@style/ImageViewerToolbar"/>
<package.ui.custom.VerticalDragLayout
android:id="@+id/dragLayout"
android:transitionName="image"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintBottom_toBottomOf="parent">
<View
android:id="@+id/backgroundColorView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@android:color/black"/>
<package.ui.custom.MultiTouchViewPager
android:id="@+id/viewPagerView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:overScrollMode="never"/>
</package.ui.custom.VerticalDragLayout>
<package.SocialsView
android:id="@+id/socials"
android:layout_width="0dp"
android:layout_height="wrap_content"
tools:layout_height="@dimen/toolbar_height_56dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintBottom_toBottomOf="parent"/>
<package.FeatureDataStateView
android:id="@+id/dataStateView"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@id/topToolbar"
app:layout_constraintBottom_toTopOf="@id/socials"
android:background="@android:color/black"
app:whiteText="true"
android:visibility="gone"/>
<package.CircleLoadingBar
android:id="@+id/ImageViewerLoadingView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@id/topToolbar"
app:layout_constraintBottom_toBottomOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
import android.os.Bundle
import android.view.Menu
import android.view.View
import androidx.core.view.isVisible
import com.arellomobile.mvp.presenter.InjectPresenter
import com.arellomobile.mvp.presenter.ProvidePresenter
import kotlinx.android.synthetic.main.image_viewer.*
import javax.inject.Inject
import kotlin.math.abs
import kotlin.math.min
class ImageViewerActivity : BaseItemViewerActivity<ImageViewerPresenter>(), IImageViewerView {
override var baseRefreshBar: View? = null
override var baseLoadingView: View? = null
companion object {
private const val STATE_LAST_CHECKED_ITEM_INDEX = "state_last_checked_item_index"
/* private const val ARG_LIST_OF_MEDIA = "arg_list_of_media"
private const val ARG_INITIALLY_CHECKED_ITEM_INDEX = "arg_initially_checked_item_index"
private const val ARG_FORCE_ROTATION = "arg_force_rotation"*/
}
private var photoList: List<Item>? = null
//индекс элемента в общем списке элементов (из бандла), требует преобразования в фото-индекс
private var positionInCommonList: Int = 0
//индекс элемента в отфильтрованном списке фотографий (фото-индекс)
private var currentImageIndex: Int = 0
private var lastCheckedImageGuid = ""
private lateinit var mediaViewController: MediaViewController
private val dismissPathLength by lazy { resources.getDimensionPixelSize(R.dimen.dismiss_path_length) }
private var toolbarVisibility = true
private var fromFile = false
@InjectPresenter
override lateinit var presenter: ImageViewerPresenter
@ProvidePresenter
fun providePresenter() = ImageViewerPresenter(ImageViewerRepository())
@Inject
lateinit var coreNetwork: ICoreNetwork
override fun onSaveInstanceState(outState: Bundle) {
outState.putInt(STATE_LAST_CHECKED_ITEM_INDEX, currentImageIndex)
super.onSaveInstanceState(outState)
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.image_viewer)
currentImageIndex = savedInstanceState?.getInt(STATE_LAST_CHECKED_ITEM_INDEX) ?: 0
window.decorView.systemUiVisibility = 0
UITools.setMIUIStatusBarDarkIcon(this, false)
createSupportActionBar(topToolbar)
supportActionBar?.setHomeAsUpIndicator(R.drawable.ic_action_close_24_white)
supportActionBar?.title = ""
setSystemBarColor(this, android.R.color.black)
baseLoadingView = ImageViewerLoadingView
dragLayout.setOnDragListener { dy ->
dragLayout.alpha = 1 - min(abs(dy / (5 * dismissPathLength)), 1f)
viewPagerView.translationY = -dy
}
dragLayout.setOnReleaseDragListener { dy ->
if (abs(dy) > dismissPathLength) {
viewPagerView.isVisible = false
finish()
} else {
dragLayout.alpha = 1f
viewPagerView.translationY = 0f
}
}
mediaViewController = MediaViewController(
viewPager = viewPagerView,
onCurrentItemChangeListener = { index ->
currentImageIndex = index
log("index: $index")
refreshToolbarsData()
},
//onPlayerControllerVisibilityListener = {},
onImageZoomListener = { isZoomed -> dragLayout.draggingIsEnabled = !isZoomed },
onImageClickListener = { changeToolbarVisibility() }/*,
soc = socials,
onErrorImage = { showError("Ошибка просмотрщика фото")}*/
)
}
/**обновляем надпись в тулбаре ( "# из $") и социальные фичи*/
private fun refreshToolbarsData() {
supportActionBar?.title =
getString(R.string.toolbar_NUMBER_of_SIZE_title, currentImageIndex + 1, photoList!!.size)
socials.setData(photoList!![currentImageIndex].features, photoList!![currentImageIndex].guid)
isActionGenericViewerVisible = !photoList!![currentImageIndex].genericItems.isNullOrEmpty()
}
override fun setData(data: Item) {
val currentItem = data.items?.get(positionInCommonList) //берем нужный итем из нефильтрованного массива
photoList = data.items?.filter { it.type.equals("card:photo", true) } //фильтруем массив
if (!photoList.isNullOrEmpty() && currentItem != null) {
currentImageIndex =
photoList!!.indexOf(currentItem) //узнаём индекс этого же итема в фильтрованном массиве
lastCheckedImageGuid = currentItem.guid
refreshToolbarsData()
mediaViewController.bind(photoList!!)
mediaViewController.setCurrentItemIndex(currentImageIndex)
} else {
showError(ItemErrorType.EMPTY_DATA)
}
}
override fun setPhotoIndex(itemIndex: Int) {
positionInCommonList = itemIndex
}
/**показываем одиночный файл*/
override fun showImageFromFilePath(path: String) {
hideError()
supportActionBar?.title = getString(R.string.title_photo_text)
val imageItem = listOf(
Item(
guid = "0",
type = "card:photo",
links = ItemLinks(image = path)
)
)
mediaViewController.apply {
bind(imageItem)
setCurrentItemIndex(0)
}
}
/**скрываем и отображаем тулбары (срабатывает по одиночному тапу)*/
private fun changeToolbarVisibility() {
val animDur = 400L
toolbarVisibility = !toolbarVisibility
topToolbar.animate().setDuration(animDur).alpha(if (toolbarVisibility) 1f else 0f)
socials.animate().setDuration(animDur).alpha(if (toolbarVisibility) 1f else 0f)
}
override fun onCreateOptionsMenu(menu: Menu?): Boolean {
if (!fromFile) {
menuInflater.inflate(R.menu.image_viewer_toolbar_menu, menu)
val action = menu?.findItem(R.id.info_generic_menu_item)
action?.isVisible = isActionGenericViewerVisible
}
return super.onCreateOptionsMenu(menu)
}
override fun onPrepareOptionsMenu(menu: Menu?): Boolean {
menu?.findItem(R.id.info_generic_menu_item)?.setOnMenuItemClickListener {
if (!photoList.isNullOrEmpty()) {
navigator.openGenericViewer(lastCheckedImageGuid, -1)
}
true
}
return true
}
override fun showError(error: ItemErrorType) {
dragLayout.visibility = View.GONE
dataStateView.showError(ItemContentType.IMAGE, error)
}
override fun hideError() {
dataStateView.visibility = View.GONE
dragLayout.visibility = View.VISIBLE
}
override fun setFlagFromFile(flag: Boolean) {
fromFile = flag
}
}
import android.content.Context
import android.util.SparseArray
import android.view.View
import android.view.ViewGroup
import androidx.core.util.valueIterator
import androidx.viewpager.widget.PagerAdapter
import com.bumptech.glide.Glide
import com.github.chrisbanes.photoview.PhotoView
/*import com.google.android.exoplayer2.ExoPlayer
import com.google.android.exoplayer2.source.ExtractorMediaSource
import com.google.android.exoplayer2.trackselection.AdaptiveTrackSelection
import com.google.android.exoplayer2.trackselection.DefaultTrackSelector
import com.google.android.exoplayer2.ui.PlayerView
import com.google.android.exoplayer2.upstream.DefaultBandwidthMeter
import com.google.android.exoplayer2.upstream.DefaultDataSourceFactory
import com.google.android.exoplayer2.util.Util
import java.util.*
internal class MediaPagerAdapter(
private val listOfImages: List<Item>,
context: Context,
// onPlayerControllerVisibilityListener: (Boolean) -> Unit,
onImageZoomListener: (isZoomed: Boolean) -> Unit,
onImageClickListener: () -> Unit/*,
soc: SocialsView*/
// , onErrorImage: () -> Unit
) : PagerAdapter() {
private val mediaPagePool = MediaPagePool(
context,
/* ExoPlayerFactory(context),
onPlayerControllerVisibilityListener,*/
onImageZoomListener,
onImageClickListener/*,
soc */
// , onErrorImage
)
private val mediaPagesInUse = SparseArray<MediaPage>()
override fun instantiateItem(container: ViewGroup, position: Int): Any {
val media = listOfImages[position]
/* val mediaPage = when (media.type) {
MediaType.VIDEO -> mediaPagePool.getVideoPage().apply { this.media = media }
MediaType.IMAGE -> mediaPagePool.getImagePage().apply { this.media = media }
}*/
val mediaPage = mediaPagePool.getImagePage().apply { this.media = media }
container.addView(mediaPage.view)
mediaPagesInUse.put(position, mediaPage)
return mediaPage
}
override fun destroyItem(container: ViewGroup, position: Int, key: Any) {
val mediaPage = (key as MediaPage)
container.removeView(mediaPage.view)
mediaPagesInUse.remove(position)
mediaPagePool.releaseMediaPage(mediaPage)
}
override fun isViewFromObject(view: View, key: Any): Boolean = ((key as MediaPage).view == view)
override fun getCount(): Int = listOfImages.size
private var lastPrimaryItem = -1 // Initially invalid.
override fun setPrimaryItem(container: ViewGroup, position: Int, key: Any) {
// It's crucial to make this method idempotent.
// It is called multiple times when the player controller is visible.
// This causes the pause button to not work.
if (position != lastPrimaryItem) {
lastPrimaryItem = position
// (key as? VideoPage)?.startOrResume()
for (mediaPage in mediaPagesInUse.valueIterator()) {
if (mediaPage is ImagePage && mediaPage !== key) {
mediaPage.resetScale()
}
}
}
}
/* fun resumeVideo(position: Int) {
if (lastPrimaryItem == position) {
(mediaPagesInUse[position] as? VideoPage)?.startOrResume()
}
}
fun pauseVideoAndHideController() {
for (mediaPage in mediaPagesInUse.valueIterator()) {
(mediaPage as? VideoPage)?.apply {
pause()
hideController()
}
}
}*/
fun clear() {
/* for (mediaPage in mediaPagesInUse.valueIterator()) {
(mediaPage as? VideoPage)?.releasePlayer()
}*/
mediaPagesInUse.clear()
mediaPagePool.clear()
}
}
/**
* Abstraction over gallery page. Subclasses are responsible for view creation, media binding/unbinding and media
* control.
*/
private sealed class MediaPage {
abstract val view: View
abstract var media: Item?
}
/*private class VideoPage(
context: Context,
private val exoPlayerWrapper: ExoPlayerWrapper,
onPlayerControllerVisibilityListener: (Boolean) -> Unit
) : MediaPage() {
override val view: PlayerView = ExoPlayerView(context).apply {
exoPlayerWrapper.attachTo(this)
controllerAutoShow = false
controllerHideOnTouch = false
hideController()
setControllerVisibilityListener { visibility ->
onPlayerControllerVisibilityListener((visibility == View.VISIBLE))
}
}
override var media: Item? = null
set(value) {
field = value
when (value) {
null -> exoPlayerWrapper.pause()
else -> exoPlayerWrapper.setMediaSource(value.settings?.self!!)
}
}
fun startOrResume() = exoPlayerWrapper.startOrResume()
fun pause() = exoPlayerWrapper.pause()
fun hideController() = view.hideController()
fun releasePlayer() = exoPlayerWrapper.release()
}*/
private class ImagePage(
context: Context,
private val onImageZoomListener: (isZoomed: Boolean) -> Unit,
private val onImageClickListener: () -> Unit/*,
private val soc: SocialsView
private val onErrorImage: () -> Unit*/
) : MediaPage() {
override val view: PhotoView = PhotoView(context).apply {
minimumScale = 1.0F
maximumScale = 2.0F
setOnScaleChangeListener { _, _, _ -> onImageZoomListener(scale > 1.05F) }
setOnClickListener { onImageClickListener() }
}
override var media: Item? = null
set(value) {
field = value
if (value == null) {
Glide.with(view).clear(view)
} else {
if (value.links?.image != null) {
GlideTools.setImageWithGlide(
imageLink = value.links!!.image,
imageView = view,
errorImage = R.drawable.error_glide,
isCenterCrop = false
)
} else {
Glide.with(view)
.load(R.drawable.error_glide)
.into(view)
}
}
}
fun resetScale() {
view.setScale(1.0F, false)
onImageZoomListener(false)
}
}
/**
* This class is a base for page view (players and other objects) recycling mechanism.
*/
private class MediaPagePool(
private val context: Context,
/*private val playerFactory: ExoPlayerFactory,
private val onPlayerControllerVisibilityListener: (Boolean) -> Unit,*/
private val onImageZoomListener: (isZoomed: Boolean) -> Unit,
private val onImageClickListener: () -> Unit/*,
private val soc: SocialsView*/
// , private val onErrorImage: () -> Unit
) {
// private val videoPagePool: Queue<VideoPage> = LinkedList()
private val imagePagePool: Queue<ImagePage> = LinkedList()
/* fun getVideoPage(): VideoPage =
videoPagePool.poll()
?: VideoPage(
context,
playerFactory.createPlayer(),
onPlayerControllerVisibilityListener
)*/
fun getImagePage(): ImagePage =
imagePagePool.poll()
?: ImagePage(context, onImageZoomListener, onImageClickListener /*, soc, onErrorImage*/)
fun releaseMediaPage(mediaPage: MediaPage) = when (mediaPage) {
/* is VideoPage -> {
mediaPage.media = null
videoPagePool.offer(mediaPage)
}*/
is ImagePage -> {
mediaPage.media = null
imagePagePool.offer(mediaPage)
}
}
fun clear() {
/* videoPagePool.forEach { it.releasePlayer() }
videoPagePool.clear()*/
imagePagePool.clear()
}
}
/**
* ExoPlayer instance and ExtractorMediaSource.Factory instance are bind with the same BandwidthMeter instance.
* We need to keep a reference to ExtractorMediaSource.Factory instance to be able to change video URL.
* Also this class gathered together all methods we need to work with the player.
* Do not expose reference to the player to not allow abuse of its usage, hence to lower code entanglement.
*/
/*private class ExoPlayerWrapper(
private val exoPlayer: ExoPlayer,
private val mediaSourceFactory: ExtractorMediaSource.Factory
) {
fun attachTo(playerView: PlayerView) {
playerView.player = exoPlayer
}
fun startOrResume() {
exoPlayer.playWhenReady = true
}
fun pause() {
exoPlayer.playWhenReady = false
}
fun setMediaSource(url: String) {
exoPlayer.prepare(mediaSourceFactory.createMediaSource(Uri.parse(url)))
}
fun release() = exoPlayer.release()
}
private class ExoPlayerFactory(private val context: Context) {
private val userAgent: String = Util.getUserAgent(context, "Gallery")
fun createPlayer(): ExoPlayerWrapper {
val bandwidthMeter = DefaultBandwidthMeter()
return ExoPlayerWrapper(
com.google.android.exoplayer2.ExoPlayerFactory.newSimpleInstance(
context,
DefaultTrackSelector(
AdaptiveTrackSelection.Factory(bandwidthMeter)
)
),
ExtractorMediaSource.Factory(
DefaultDataSourceFactory(
context,
userAgent,
bandwidthMeter
)
)
)
}
}*/
import androidx.viewpager.widget.ViewPager
internal class MediaViewController(
private val viewPager: ViewPager,
private val onCurrentItemChangeListener: (Int) -> Unit,
// private val onPlayerControllerVisibilityListener: (Boolean) -> Unit,
private val onImageZoomListener: (isZoomed: Boolean) -> Unit,
private val onImageClickListener: () -> Unit
//, private val soc: SocialsView
//, private val onErrorImage: () -> Unit
) {
private var adapter: MediaPagerAdapter? = null
fun bind(listOfImages: List<Item>) {
adapter = MediaPagerAdapter(
listOfImages,
viewPager.context,
// onPlayerControllerVisibilityListener,
onImageZoomListener,
onImageClickListener/*,
soc*/
// , onErrorImage
)
viewPager.adapter = adapter
viewPager.addOnPageChangeListener(object : ViewPager.OnPageChangeListener {
override fun onPageScrollStateChanged(state: Int) {
/* when (state) {
ViewPager.SCROLL_STATE_DRAGGING,
ViewPager.SCROLL_STATE_SETTLING -> adapter.pauseVideoAndHideController()
ViewPager.SCROLL_STATE_IDLE -> adapter.resumeVideo(viewPager.currentItem)
}*/
}
override fun onPageScrolled(
position: Int,
positionOffset: Float,
positionOffsetPixels: Int
) {
}
override fun onPageSelected(position: Int) {
// Do not use this callback to start video. There are no views instantiated on first ViewPager layout.
// Thus adapter has not player to start. Moreover if initial position is 0 this callback is not called.
onCurrentItemChangeListener(position)
}
})
}
fun setCurrentItemIndex(currentItemIndex: Int) {
viewPager.currentItem = currentItemIndex
}
//fun release() = adapter?.clear()
}
import android.annotation.SuppressLint
import android.content.Context
import android.util.AttributeSet
import android.view.MotionEvent
import androidx.viewpager.widget.ViewPager
/**
* This ViewPager copied from https://github.com/stfalcon-studio/FrescoImageViewer
*/
internal class MultiTouchViewPager @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null
) : ViewPager(context, attrs) {
private var isDisallowIntercept = true
override fun requestDisallowInterceptTouchEvent(disallowIntercept: Boolean) {
isDisallowIntercept = disallowIntercept
super.requestDisallowInterceptTouchEvent(disallowIntercept)
}
override fun dispatchTouchEvent(ev: MotionEvent) =
if (ev.pointerCount > 1 && isDisallowIntercept) {
requestDisallowInterceptTouchEvent(false)
val handled = super.dispatchTouchEvent(ev)
requestDisallowInterceptTouchEvent(true)
handled
} else {
super.dispatchTouchEvent(ev)
}
override fun onInterceptTouchEvent(ev: MotionEvent): Boolean {
return if (ev.pointerCount > 1)
false
else try {
super.onInterceptTouchEvent(ev)
} catch (ex: IllegalArgumentException) {
false
}
}
@SuppressLint("ClickableViewAccessibility")
override fun onTouchEvent(ev: MotionEvent): Boolean {
return try {
super.onTouchEvent(ev)
} catch (ex: IllegalArgumentException) {
false
}
}
}
import android.annotation.SuppressLint
import android.content.Context
import android.util.AttributeSet
import android.view.MotionEvent
import android.view.ViewConfiguration
import android.widget.FrameLayout
import kotlin.math.abs
import kotlin.math.atan2
/**
* Allow listen vertical drag motions.
*/
internal class VerticalDragLayout @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0
) : FrameLayout(context, attrs, defStyleAttr) {
var draggingIsEnabled = true
set(value) {
field = value
reset()
}
private val touchSlop = ViewConfiguration.get(context).scaledTouchSlop
//null - not detect move, true - vertical, false - horizontal
private var isDetectedVerticalMove: Boolean? = null
private var startX = 0f
private var startY = 0f
private var startInnerMoveY = 0f
private var onDragListener: (dy: Float) -> Unit = {}
private var onReleaseDragListener: (dy: Float) -> Unit = {}
fun setOnDragListener(listener: (dy: Float) -> Unit) {
onDragListener = listener
}
fun setOnReleaseDragListener(listener: (dy: Float) -> Unit) {
onReleaseDragListener = listener
}
private fun reset() {
isDetectedVerticalMove = null
startX = 0f
startY = 0f
startInnerMoveY = 0f
}
override fun dispatchTouchEvent(ev: MotionEvent): Boolean {
when (ev.action) {
MotionEvent.ACTION_DOWN -> {
if (ev.pointerCount == 1) {
startX = ev.x
startY = ev.y
} else {
isDetectedVerticalMove = null
startX = 0f
startY = 0f
}
}
MotionEvent.ACTION_UP,
MotionEvent.ACTION_CANCEL -> {
isDetectedVerticalMove = null
startX = 0f
startY = 0f
}
MotionEvent.ACTION_MOVE -> {
if (draggingIsEnabled
&& isDetectedVerticalMove == null
&& ev.pointerCount == 1
&& abs(startY - ev.y) > touchSlop
) {
val direction = getDirection(ev, startX, startY)
isDetectedVerticalMove = (direction == Direction.UP || direction == Direction.DOWN)
}
}
}
return super.dispatchTouchEvent(ev)
}
override fun onInterceptTouchEvent(ev: MotionEvent): Boolean {
return ev.pointerCount == 1 && ev.action == MotionEvent.ACTION_MOVE && isDetectedVerticalMove == true
}
@SuppressLint("ClickableViewAccessibility")
override fun onTouchEvent(ev: MotionEvent): Boolean {
when (ev.action) {
MotionEvent.ACTION_UP,
MotionEvent.ACTION_CANCEL -> {
if (ev.pointerCount == 1) {
onReleaseDragListener(startInnerMoveY - ev.y)
}
startInnerMoveY = 0f
}
MotionEvent.ACTION_MOVE -> {
if (startInnerMoveY == 0f) {
startInnerMoveY = ev.y
}
onDragListener(startInnerMoveY - ev.y)
}
}
return true
}
private fun getDirection(ev: MotionEvent, x: Float, y: Float) =
Direction[getAngle(x, y, ev.x, ev.y)]
private fun getAngle(x1: Float, y1: Float, x2: Float, y2: Float): Double {
val rad = atan2((y1 - y2).toDouble(), (x2 - x1).toDouble()) + Math.PI
return (rad * 180 / Math.PI + 180) % 360
}
private enum class Direction {
UP,
DOWN,
LEFT,
RIGHT;
companion object {
private const val V_ANGLE = 30F
operator fun get(angle: Double): Direction = when {
inRange(angle, 90f - V_ANGLE, 90f + V_ANGLE) -> UP
inRange(angle, 0f, 90f - V_ANGLE) || inRange(angle, 270f + V_ANGLE, 360f) -> RIGHT
inRange(angle, 270f - V_ANGLE, 270f + V_ANGLE) -> DOWN
else -> LEFT
}
private fun inRange(angle: Double, init: Float, end: Float) =
angle >= init && angle < end
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment