Skip to content

Commit

Permalink
Cleanup lock position code
Browse files Browse the repository at this point in the history
  • Loading branch information
Nain57 committed Sep 20, 2023
1 parent ac34690 commit b1a61bd
Show file tree
Hide file tree
Showing 9 changed files with 285 additions and 240 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ import com.buzbuz.smartautoclicker.core.ui.overlays.Overlay
import com.buzbuz.smartautoclicker.core.ui.overlays.FullscreenOverlay
import com.buzbuz.smartautoclicker.core.ui.overlays.manager.navigation.OverlayNavigationRequest
import com.buzbuz.smartautoclicker.core.ui.overlays.manager.navigation.OverlayNavigationRequestStack
import com.buzbuz.smartautoclicker.core.ui.overlays.menu.OverlayMenu
import com.buzbuz.smartautoclicker.core.ui.overlays.menu.OverlayMenuPositionDataSource
import com.buzbuz.smartautoclicker.core.ui.utils.internal.LifoStack

import kotlinx.coroutines.flow.Flow
Expand Down Expand Up @@ -67,6 +67,8 @@ class OverlayManager internal constructor(context: Context) {

/** The metrics of the device screen. */
private val displayMetrics = DisplayMetrics.getInstance(context)
/** Save/load and lock the position of the overlay menus. */
private val menuPositionDataSource = OverlayMenuPositionDataSource.getInstance(context)
/** The listener upon screen rotation. */
private val orientationListener: (Context) -> Unit = { onOrientationChanged() }

Expand All @@ -86,11 +88,6 @@ class OverlayManager internal constructor(context: Context) {
private var closingChildren: Boolean = false
/** The overlay at the top of the stack (the top visible one). Null if the stack is empty. */
private var topOverlay: Overlay? = null
/**
* When defined with [lockMenuPosition], all [OverlayMenu] will be displayed at this position with the move button
* removed. Use [unlockMenuPosition] to restore the user position and allow menu moving.
*/
private var menuLockedPosition: Point? = null
/** Notifies the caller of [navigateUpToRoot] once the all overlays above the root are destroyed. */
private var navigateUpToRootCompletionListener: (() -> Unit)? = null

Expand Down Expand Up @@ -230,20 +227,12 @@ class OverlayManager internal constructor(context: Context) {

fun lockMenuPosition(position: Point) {
Log.d(TAG, "Locking menu position to $position")
menuLockedPosition = position

overlayBackStack.forEach { overlay ->
if (overlay is OverlayMenu) overlay.lockPosition(position)
}
menuPositionDataSource.lockPosition(position)
}

fun unlockMenuPosition() {
Log.d(TAG, "Unlocking menu position")
menuLockedPosition = null

overlayBackStack.forEach { overlay ->
if (overlay is OverlayMenu) overlay.unlockPosition()
}
menuPositionDataSource.unlockPosition()
}

private fun executeNextNavigationRequest(context: Context) {
Expand Down Expand Up @@ -292,17 +281,6 @@ class OverlayManager internal constructor(context: Context) {
appContext = context,
dismissListener = ::onOverlayDismissed,
)
// Lock this new overlay position if its a menu and if the lock has been requested
menuLockedPosition?.let { position ->
if (request.overlay is OverlayMenu) {
// We do not save the position because it has already been done if we reach this, and the position is
// shared among all OverlayMenu
request.overlay.lockPosition(
position = position,
savePosition = false,
)
}
}

// Update current lifecycle
currentOverlay?.apply {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ import android.graphics.Point
import android.util.Log
import android.util.Size
import android.view.Gravity
import android.view.HapticFeedbackConstants
import android.view.LayoutInflater
import android.view.MotionEvent
import android.view.View
Expand All @@ -35,7 +34,6 @@ import androidx.annotation.CallSuper
import androidx.annotation.IdRes
import androidx.core.view.children
import androidx.core.view.forEach
import androidx.core.view.size
import androidx.lifecycle.Lifecycle

import com.buzbuz.smartautoclicker.core.ui.overlays.BaseOverlay
Expand Down Expand Up @@ -97,9 +95,11 @@ abstract class OverlayMenu : BaseOverlay(recreateOnRotation = false) {
/** The view containing the buttons as direct children. */
private lateinit var buttonsContainer: ViewGroup
/** Handles the window size computing when animating a resize of the overlay. */
private lateinit var resizeController: OverlayWindowResizeController
/** Handles the position of the menu. */
private lateinit var positionController: OverlayMenuPositionController
private lateinit var resizeController: OverlayMenuResizeController
/** Handles the save/load of the position of the menus. */
private lateinit var positionDataSource: OverlayMenuPositionDataSource
/** Handles the touch events on the move button. */
private lateinit var moveTouchEventHandler: OverlayMenuMoveTouchEventHandler

/** Value of the alpha for a disabled item view in the menu. */
private var disabledItemAlpha: Float = 1f
Expand All @@ -118,6 +118,8 @@ abstract class OverlayMenu : BaseOverlay(recreateOnRotation = false) {
/** The layout parameters of the overlay view. */
private var overlayLayoutParams: WindowManager.LayoutParams? = null

private val onLockedPositionChangedListener: (Point?) -> Unit = ::onLockedPositionChanged

/**
* Creates the root view of the menu overlay.
*
Expand Down Expand Up @@ -170,18 +172,18 @@ abstract class OverlayMenu : BaseOverlay(recreateOnRotation = false) {
buttonsContainer = menuLayout.findViewById<ViewGroup>(R.id.menu_items)
setupButtons(buttonsContainer)

// Setup the touch event handler for the move button
moveTouchEventHandler = OverlayMenuMoveTouchEventHandler(::updateMenuPosition)

// Restore the last menu position, if any.
menuLayoutParams.gravity = Gravity.TOP or Gravity.START
overlayLayoutParams?.gravity = Gravity.TOP or Gravity.START
positionController = OverlayMenuPositionController(
menuLayout = menuLayout,
displayMetrics = displayMetrics,
onMenuPositionChanged = ::onNewMenuPosition,
)
positionController.loadMenuPosition(displayMetrics.orientation)
positionDataSource = OverlayMenuPositionDataSource.getInstance(context)
positionDataSource.addOnLockedPositionChangedListener(onLockedPositionChangedListener)
loadMenuPosition(displayMetrics.orientation)

// Handle window resize animations
resizeController = OverlayWindowResizeController(
resizeController = OverlayMenuResizeController(
backgroundViewGroup = menuBackground,
resizedContainer = buttonsContainer,
maximumSize = getWindowMaximumSize(menuBackground),
Expand Down Expand Up @@ -290,13 +292,14 @@ abstract class OverlayMenu : BaseOverlay(recreateOnRotation = false) {
return
}

// Save last user position
positionDataSource.removeOnLockedPositionChangedListener(onLockedPositionChangedListener)
saveMenuPosition(displayMetrics.orientation)

windowManager.removeView(menuLayout)
screenOverlayView?.let { windowManager.removeView(it) }
screenOverlayView = null

// Save last user position
positionController.saveMenuPosition(displayMetrics.orientation)

resizeController.release()
super@OverlayMenu.destroy()
}
Expand All @@ -307,12 +310,11 @@ abstract class OverlayMenu : BaseOverlay(recreateOnRotation = false) {
* orientation.
*/
override fun onOrientationChanged() {
positionController.saveMenuPosition(if (displayMetrics.orientation == Configuration.ORIENTATION_LANDSCAPE)
Configuration.ORIENTATION_PORTRAIT
else
Configuration.ORIENTATION_LANDSCAPE
saveMenuPosition(
if (displayMetrics.orientation == Configuration.ORIENTATION_LANDSCAPE) Configuration.ORIENTATION_PORTRAIT
else Configuration.ORIENTATION_LANDSCAPE
)
positionController.loadMenuPosition(displayMetrics.orientation)
loadMenuPosition(displayMetrics.orientation)

if (lifecycle.currentState.isAtLeast(Lifecycle.State.STARTED)) {
windowManager.updateViewLayout(menuLayout, menuLayoutParams)
Expand Down Expand Up @@ -436,7 +438,7 @@ abstract class OverlayMenu : BaseOverlay(recreateOnRotation = false) {
protected fun setOverlayViewVisibility(newVisibility: Int) {
screenOverlayView?.apply {

Log.d(TAG, "setOverlayViewVisibility for ${hashCode()} with visibility $newVisibility")
Log.d(TAG, "setOverlayViewVisibility for ${this@OverlayMenu.hashCode()} with visibility $newVisibility")
visibility = newVisibility
hideOverlayButton?.let {
setMenuItemViewEnabled(it, visibility == View.GONE , true)
Expand All @@ -455,35 +457,45 @@ abstract class OverlayMenu : BaseOverlay(recreateOnRotation = false) {
private fun onMoveTouched(event: MotionEvent) : Boolean {
if (resizeController.isAnimating) return false

if (event.action == MotionEvent.ACTION_DOWN)
menuLayout.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS)

return positionController.onMoveButtonTouchEvent(event)
return moveTouchEventHandler.onTouchEvent(menuLayout, event)
}


/** Safe setter for the position of the overlay menu ensuring it will not be displayed outside the screen. */
private fun onNewMenuPosition(position: Point) {
menuLayoutParams.x = position.x
menuLayoutParams.y = position.y
private fun updateMenuPosition(position: Point) {
menuLayoutParams.x = position.x.coerceIn(0, displayMetrics.screenSize.x - menuLayout.width)
menuLayoutParams.y = position.y.coerceIn(0, displayMetrics.screenSize.y - menuLayout.height)

if (lifecycle.currentState.isAtLeast(Lifecycle.State.CREATED)) {
Log.d(TAG, "Updating menu window position: ${position.x}/${position.y}")
Log.d(TAG, "Updating menu window position: ${menuLayoutParams.x}/${menuLayoutParams.y}")
windowManager.updateViewLayout(menuLayout, menuLayoutParams)
}
}

internal fun lockPosition(position: Point, savePosition: Boolean = true) {
Log.d(TAG, "Locking menu position of overlay ${hashCode()}")

moveButton?.let { setMenuItemVisibility(it, false) }
positionController.lockPosition(position, displayMetrics.orientation, savePosition)
private fun loadMenuPosition(orientation: Int) {
positionDataSource.loadMenuPosition(orientation)?.let { savedPosition ->
updateMenuPosition(savedPosition)
}
}

internal fun unlockPosition() {
Log.d(TAG, "Unlocking menu position of overlay ${hashCode()}")
private fun saveMenuPosition(orientation: Int) {
positionDataSource.saveMenuPosition(
position = Point(menuLayoutParams.x, menuLayoutParams.y),
orientation = orientation,
)
}

positionController.unlockPosition(displayMetrics.orientation)
moveButton?.let { setMenuItemVisibility(it, true) }
private fun onLockedPositionChanged(lockedPosition: Point?) {
if (lockedPosition != null) {
Log.d(TAG, "Locking menu position of overlay ${hashCode()}")
moveButton?.let { setMenuItemVisibility(it, false) }
saveMenuPosition(displayMetrics.orientation)
updateMenuPosition(lockedPosition)
} else {
Log.d(TAG, "Unlocking menu position of overlay ${hashCode()}")
moveButton?.let { setMenuItemVisibility(it, true) }
loadMenuPosition(displayMetrics.orientation)
}
}
}
/** Tag for logs */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ package com.buzbuz.smartautoclicker.core.ui.overlays.menu

import android.util.Log
import android.view.View
import android.view.View.MeasureSpec
import android.view.ViewGroup
import android.view.animation.AlphaAnimation
import android.view.animation.Animation
Expand Down Expand Up @@ -55,22 +56,23 @@ internal class OverlayMenuAnimations {
fun startShowAnimation(view: View, overlayView: View? = null, onAnimationEnded: () -> Unit) {
if (showAnimationIsRunning) return

Log.d(TAG, "start show animation")
Log.d(TAG, "Start show animation on view ${view} with visibility ${view.visibility}")

showAnimationIsRunning = true
showOverlayMenuAnimation.setOnEndListener {
Log.d(TAG, "show animation ended")
Log.d(TAG, "Show animation ended")
showAnimationIsRunning = false
onAnimationEnded()
}

if (hideAnimationIsRunning) {
Log.d(TAG, "hide animation is running, stopping it first.")
Log.d(TAG, "Hide animation is running, stopping it first.")
hideOverlayMenuAnimation.cancel()
hideOverlayViewAnimation.cancel()
hideAnimationIsRunning = false
}

view.measure(MeasureSpec.EXACTLY, MeasureSpec.EXACTLY)
view.startAnimation(showOverlayMenuAnimation)
if (overlayView is ViewGroup && overlayView.childCount == 1) {
overlayView.children.first().startAnimation(showOverlayViewAnimation)
Expand All @@ -80,17 +82,17 @@ internal class OverlayMenuAnimations {
fun startHideAnimation(view: View, overlayView: View? = null, onAnimationEnded: () -> Unit) {
if (hideAnimationIsRunning) return

Log.d(TAG, "start hide animation")
Log.d(TAG, "Start hide animation")

hideAnimationIsRunning = true
hideOverlayMenuAnimation.setOnEndListener {
Log.d(TAG, "hide animation ended")
Log.d(TAG, "Hide animation ended")
hideAnimationIsRunning = false
onAnimationEnded()
}

if (showAnimationIsRunning) {
Log.d(TAG, "show animation is running, stopping it first.")
Log.d(TAG, "Show animation is running, stopping it first.")

showOverlayMenuAnimation.cancel()
showOverlayViewAnimation.cancel()
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
/*
* Copyright (C) 2023 Kevin Buzeau
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.buzbuz.smartautoclicker.core.ui.overlays.menu

import android.graphics.Point
import android.view.HapticFeedbackConstants
import android.view.MotionEvent
import android.view.View
import android.view.WindowManager

internal class OverlayMenuMoveTouchEventHandler(
private val onMenuMoved: (Point) -> Unit,
) {

/** The initial position of the overlay menu when pressing the move menu item. */
private var moveInitialViewPosition: Point = Point(0, 0)
/** The initial position of the touch event that as initiated the move of the overlay menu. */
private var moveInitialTouchPosition: Point = Point(0, 0)

fun onTouchEvent(viewToMove: View, event: MotionEvent): Boolean =
when (event.action) {
MotionEvent.ACTION_DOWN -> {
viewToMove.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS)
onDownEvent(viewToMove, event)
true
}

MotionEvent.ACTION_MOVE -> {
onMoveEvent(event)
true
}

else -> false
}

private fun onDownEvent(viewToMove: View, event: MotionEvent) {
val layoutParams = (viewToMove.layoutParams as WindowManager.LayoutParams)
moveInitialViewPosition = Point(layoutParams.x, layoutParams.y)
moveInitialTouchPosition = Point(event.rawX.toInt(), event.rawY.toInt())
}

private fun onMoveEvent(event: MotionEvent) {
onMenuMoved(
Point(
moveInitialViewPosition.x + (event.rawX.toInt() - moveInitialTouchPosition.x),
moveInitialViewPosition.y + (event.rawY.toInt() - moveInitialTouchPosition.y),
)
)
}
}
Loading

0 comments on commit b1a61bd

Please sign in to comment.