mirror of
https://github.com/oxen-io/session-android.git
synced 2025-12-08 06:02:13 +00:00
Menu redesign (#958)
* feat: Menu redesign * Add bottomsheet * Handle default peek height * Smooth out setting peek height * Move contacts prep to util * Dialog layout tweaks * Contact grouping tweaks * Add new message dialog * Add public key input delegate * Add create group dialog * Add join community dialog * Handle dialog back navigation * Enter community url tab tweaks * Scan QR code tab refactor * Scan qr code refactor * Direct and community tabs refactor * Add session id copy context menu item * Set dialog background colours * Set full dialog background colour * Minor tweaks * Add closed group contact search * Cleanup * Add content descriptions * Resize community chips * Fix new conversation screen paddings * Fix fade in/out of join community screen * Prevent creating conversation with empty public key * Resize and position create group loader * Fix back nav after creating direct message conversation * Fix inter-screen transitions * Fix new conversation background colours * Fix background colours * Rename contact list header for clarity * Bug fixes * Enable scrolling of Enter Session ID tab of the new message dialog * Minor refactor * Switch to child fragment manager * Fix member search on create group screen Co-authored-by: charles <charles@oxen.io>
This commit is contained in:
@@ -41,6 +41,7 @@ import org.session.libsignal.utilities.toHexString
|
||||
import org.thoughtcrime.securesms.ApplicationContext
|
||||
import org.thoughtcrime.securesms.MuteDialog
|
||||
import org.thoughtcrime.securesms.PassphraseRequiredActionBarActivity
|
||||
import org.thoughtcrime.securesms.conversation.start.NewConversationFragment
|
||||
import org.thoughtcrime.securesms.conversation.v2.ConversationActivityV2
|
||||
import org.thoughtcrime.securesms.conversation.v2.utilities.NotificationUtils
|
||||
import org.thoughtcrime.securesms.crypto.IdentityKeyUtil
|
||||
@@ -50,9 +51,6 @@ import org.thoughtcrime.securesms.database.RecipientDatabase
|
||||
import org.thoughtcrime.securesms.database.ThreadDatabase
|
||||
import org.thoughtcrime.securesms.database.model.ThreadRecord
|
||||
import org.thoughtcrime.securesms.dependencies.DatabaseComponent
|
||||
import org.thoughtcrime.securesms.dms.CreatePrivateChatActivity
|
||||
import org.thoughtcrime.securesms.groups.CreateClosedGroupActivity
|
||||
import org.thoughtcrime.securesms.groups.JoinPublicChatActivity
|
||||
import org.thoughtcrime.securesms.groups.OpenGroupManager
|
||||
import org.thoughtcrime.securesms.home.search.GlobalSearchAdapter
|
||||
import org.thoughtcrime.securesms.home.search.GlobalSearchInputLayout
|
||||
@@ -76,10 +74,9 @@ import javax.inject.Inject
|
||||
|
||||
@AndroidEntryPoint
|
||||
class HomeActivity : PassphraseRequiredActionBarActivity(),
|
||||
ConversationClickListener,
|
||||
SeedReminderViewDelegate,
|
||||
NewConversationButtonSetViewDelegate,
|
||||
GlobalSearchInputLayout.GlobalSearchInputLayoutListener {
|
||||
ConversationClickListener,
|
||||
SeedReminderViewDelegate,
|
||||
GlobalSearchInputLayout.GlobalSearchInputLayoutListener {
|
||||
|
||||
private lateinit var binding: ActivityHomeBinding
|
||||
private lateinit var glide: GlideRequests
|
||||
@@ -178,7 +175,7 @@ class HomeActivity : PassphraseRequiredActionBarActivity(),
|
||||
binding.recyclerView.adapter = homeAdapter
|
||||
binding.globalSearchRecycler.adapter = globalSearchAdapter
|
||||
// Set up empty state view
|
||||
binding.createNewPrivateChatButton.setOnClickListener { createNewPrivateChat() }
|
||||
binding.createNewPrivateChatButton.setOnClickListener { showNewConversation() }
|
||||
IP2Country.configureIfNeeded(this@HomeActivity)
|
||||
homeViewModel.getObservable(this).observe(this) { newData ->
|
||||
val manager = binding.recyclerView.layoutManager as LinearLayoutManager
|
||||
@@ -194,8 +191,8 @@ class HomeActivity : PassphraseRequiredActionBarActivity(),
|
||||
updateEmptyState()
|
||||
}
|
||||
homeViewModel.tryUpdateChannel()
|
||||
// Set up new conversation button set
|
||||
binding.newConversationButtonSet.delegate = this
|
||||
// Set up new conversation button
|
||||
binding.newConversationButton.setOnClickListener { showNewConversation() }
|
||||
// Observe blocked contacts changed events
|
||||
val broadcastReceiver = object : BroadcastReceiver() {
|
||||
override fun onReceive(context: Context, intent: Intent) {
|
||||
@@ -301,7 +298,7 @@ class HomeActivity : PassphraseRequiredActionBarActivity(),
|
||||
binding.seedReminderView.isVisible = !TextSecurePreferences.getHasViewedSeed(this) && !isShown
|
||||
binding.gradientView.isVisible = !isShown
|
||||
binding.globalSearchRecycler.isVisible = isShown
|
||||
binding.newConversationButtonSet.isVisible = !isShown
|
||||
binding.newConversationButton.isVisible = !isShown
|
||||
}
|
||||
|
||||
private fun setupMessageRequestsBanner() {
|
||||
@@ -354,13 +351,6 @@ class HomeActivity : PassphraseRequiredActionBarActivity(),
|
||||
ApplicationContext.getInstance(this).messageNotifier.setHomeScreenVisible(false)
|
||||
}
|
||||
|
||||
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
||||
super.onActivityResult(requestCode, resultCode, data)
|
||||
if (resultCode == CreateClosedGroupActivity.closedGroupCreatedResultCode) {
|
||||
createNewPrivateChat()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
val broadcastReceiver = this.broadcastReceiver
|
||||
if (broadcastReceiver != null) {
|
||||
@@ -623,19 +613,9 @@ class HomeActivity : PassphraseRequiredActionBarActivity(),
|
||||
.create().show()
|
||||
}
|
||||
|
||||
override fun createNewPrivateChat() {
|
||||
val intent = Intent(this, CreatePrivateChatActivity::class.java)
|
||||
show(intent)
|
||||
private fun showNewConversation() {
|
||||
NewConversationFragment().show(supportFragmentManager, "NewConversationFragment")
|
||||
}
|
||||
|
||||
override fun createNewClosedGroup() {
|
||||
val intent = Intent(this, CreateClosedGroupActivity::class.java)
|
||||
show(intent, true)
|
||||
}
|
||||
|
||||
override fun joinOpenGroup() {
|
||||
val intent = Intent(this, JoinPublicChatActivity::class.java)
|
||||
show(intent)
|
||||
}
|
||||
// endregion
|
||||
}
|
||||
|
||||
@@ -1,386 +0,0 @@
|
||||
package org.thoughtcrime.securesms.home
|
||||
|
||||
import android.animation.FloatEvaluator
|
||||
import android.animation.PointFEvaluator
|
||||
import android.animation.ValueAnimator
|
||||
import android.content.Context
|
||||
import android.content.res.ColorStateList
|
||||
import android.graphics.PointF
|
||||
import android.graphics.Typeface
|
||||
import android.os.Build
|
||||
import android.util.AttributeSet
|
||||
import android.util.TypedValue
|
||||
import android.view.Gravity
|
||||
import android.view.HapticFeedbackConstants
|
||||
import android.view.MotionEvent
|
||||
import android.view.View
|
||||
import android.widget.ImageView
|
||||
import android.widget.RelativeLayout
|
||||
import android.widget.TextView
|
||||
import androidx.annotation.ColorRes
|
||||
import androidx.annotation.DrawableRes
|
||||
import network.loki.messenger.R
|
||||
import org.thoughtcrime.securesms.util.GlowViewUtilities
|
||||
import org.thoughtcrime.securesms.util.NewConversationButtonImageView
|
||||
import org.thoughtcrime.securesms.util.UiModeUtilities
|
||||
import org.thoughtcrime.securesms.util.animateSizeChange
|
||||
import org.thoughtcrime.securesms.util.contains
|
||||
import org.thoughtcrime.securesms.util.disableClipping
|
||||
import org.thoughtcrime.securesms.util.distanceTo
|
||||
import org.thoughtcrime.securesms.util.getColorWithID
|
||||
import org.thoughtcrime.securesms.util.isAbove
|
||||
import org.thoughtcrime.securesms.util.isLeftOf
|
||||
import org.thoughtcrime.securesms.util.isRightOf
|
||||
import org.thoughtcrime.securesms.util.toPx
|
||||
|
||||
class NewConversationButtonSetView : RelativeLayout {
|
||||
private var expandedButton: Button? = null
|
||||
private var previousAction: Int? = null
|
||||
private var isExpanded = false
|
||||
var delegate: NewConversationButtonSetViewDelegate? = null
|
||||
|
||||
// region Convenience
|
||||
private val sessionButtonExpandedPosition: PointF get() { return PointF(width.toFloat() / 2 - sessionButton.expandedSize / 2 - sessionButton.shadowMargin, 0.0f) }
|
||||
private val sessionTooltipExpandedPosition: PointF get() {
|
||||
val x = sessionButtonExpandedPosition.x + sessionButton.width / 2 - sessionTooltip.width / 2
|
||||
val y = sessionButtonExpandedPosition.y + sessionButton.height - sessionTooltip.height / 2
|
||||
return PointF(x, y)
|
||||
}
|
||||
private val closedGroupButtonExpandedPosition: PointF get() { return PointF(width.toFloat() - closedGroupButton.expandedSize - 2 * closedGroupButton.shadowMargin, height.toFloat() - bottomMargin - closedGroupButton.expandedSize - 2 * closedGroupButton.shadowMargin) }
|
||||
private val closedGroupTooltipExpandedPosition: PointF get() {
|
||||
val x = closedGroupButtonExpandedPosition.x + closedGroupButton.width / 2 - closedGroupTooltip.width / 2
|
||||
val y = closedGroupButtonExpandedPosition.y + closedGroupButton.height - closedGroupTooltip.height / 2
|
||||
return PointF(x, y)
|
||||
}
|
||||
private val openGroupButtonExpandedPosition: PointF get() { return PointF(0.0f, height.toFloat() - bottomMargin - openGroupButton.expandedSize - 2 * openGroupButton.shadowMargin) }
|
||||
private val openGroupTooltipExpandedPosition: PointF get() {
|
||||
val x = openGroupButtonExpandedPosition.x + openGroupButton.width / 2 - openGroupTooltip.width / 2
|
||||
val y = openGroupButtonExpandedPosition.y + openGroupButton.height - openGroupTooltip.height / 2
|
||||
return PointF(x, y)
|
||||
}
|
||||
private val buttonRestPosition: PointF get() { return PointF(width.toFloat() / 2 - mainButton.expandedSize / 2 - mainButton.shadowMargin, height.toFloat() - bottomMargin - mainButton.expandedSize - 2 * mainButton.shadowMargin) }
|
||||
private fun tooltipRestPosition(viewWidth: Int): PointF {
|
||||
return PointF(width.toFloat() / 2 - viewWidth / 2, height.toFloat() - bottomMargin)
|
||||
}
|
||||
// endregion
|
||||
|
||||
// region Settings
|
||||
private val minDragDistance by lazy { toPx(40, resources).toFloat() }
|
||||
private val maxDragDistance by lazy { toPx(56, resources).toFloat() }
|
||||
private val dragMargin by lazy { toPx(16, resources).toFloat() }
|
||||
private val bottomMargin by lazy { resources.getDimension(R.dimen.new_conversation_button_bottom_offset) }
|
||||
// endregion
|
||||
|
||||
// region Components
|
||||
private val mainButton by lazy { Button(context, true, R.drawable.ic_plus) }
|
||||
private val sessionButton by lazy { Button(context, false, R.drawable.ic_message) }
|
||||
private val sessionTooltip by lazy {
|
||||
TextView(context).apply {
|
||||
setTextSize(TypedValue.COMPLEX_UNIT_SP, 12f)
|
||||
typeface = Typeface.DEFAULT_BOLD
|
||||
setText(R.string.NewConversationButton_SessionTooltip)
|
||||
isAllCaps = true
|
||||
}
|
||||
}
|
||||
private val closedGroupButton by lazy { Button(context, false, R.drawable.ic_group) }
|
||||
private val closedGroupTooltip by lazy {
|
||||
TextView(context).apply {
|
||||
setTextSize(TypedValue.COMPLEX_UNIT_SP, 12f)
|
||||
typeface = Typeface.DEFAULT_BOLD
|
||||
setText(R.string.NewConversationButton_ClosedGroupTooltip)
|
||||
isAllCaps = true
|
||||
}
|
||||
}
|
||||
private val openGroupButton by lazy { Button(context, false, R.drawable.ic_globe) }
|
||||
private val openGroupTooltip by lazy {
|
||||
TextView(context).apply {
|
||||
setTextSize(TypedValue.COMPLEX_UNIT_SP, 12f)
|
||||
typeface = Typeface.DEFAULT_BOLD
|
||||
setText(R.string.NewConversationButton_OpenGroupTooltip)
|
||||
isAllCaps = true
|
||||
}
|
||||
}
|
||||
// endregion
|
||||
|
||||
// region Button
|
||||
class Button : RelativeLayout {
|
||||
@DrawableRes private var iconID = 0
|
||||
private var isMain = false
|
||||
|
||||
fun getIconID() = iconID
|
||||
|
||||
companion object {
|
||||
val animationDuration = 250.toLong()
|
||||
}
|
||||
|
||||
val expandedSize by lazy { resources.getDimension(R.dimen.new_conversation_button_expanded_size) }
|
||||
val collapsedSize by lazy { resources.getDimension(R.dimen.new_conversation_button_collapsed_size) }
|
||||
val shadowMargin by lazy { toPx(6, resources).toFloat() }
|
||||
private val expandedImageViewPosition by lazy { PointF(shadowMargin, shadowMargin) }
|
||||
private val collapsedImageViewPosition by lazy { PointF(shadowMargin + (expandedSize - collapsedSize) / 2, shadowMargin + (expandedSize - collapsedSize) / 2) }
|
||||
|
||||
private val imageView by lazy {
|
||||
val result = NewConversationButtonImageView(context)
|
||||
val size = collapsedSize.toInt()
|
||||
result.layoutParams = LayoutParams(size, size)
|
||||
result.setBackgroundResource(R.drawable.new_conversation_button_background)
|
||||
@ColorRes val backgroundColorID = if (isMain) R.color.accent else R.color.new_conversation_button_collapsed_background
|
||||
@ColorRes val shadowColorID = if (isMain) {
|
||||
R.color.new_conversation_button_shadow
|
||||
} else {
|
||||
if (UiModeUtilities.isDayUiMode(context)) R.color.transparent_black_30 else R.color.black
|
||||
}
|
||||
result.mainColor = resources.getColorWithID(backgroundColorID, context.theme)
|
||||
result.sessionShadowColor = resources.getColorWithID(shadowColorID, context.theme)
|
||||
result.scaleType = ImageView.ScaleType.CENTER
|
||||
result.setImageResource(iconID)
|
||||
result.imageTintList = if (isMain) {
|
||||
ColorStateList.valueOf(resources.getColorWithID(android.R.color.white, context.theme))
|
||||
} else {
|
||||
ColorStateList.valueOf(resources.getColorWithID(R.color.text, context.theme))
|
||||
}
|
||||
result
|
||||
}
|
||||
|
||||
constructor(context: Context) : super(context) { throw IllegalAccessException("Use Button(context:iconID:) instead.") }
|
||||
constructor(context: Context, attrs: AttributeSet) : super(context, attrs) { throw IllegalAccessException("Use Button(context:iconID:) instead.") }
|
||||
constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : super(context, attrs, defStyleAttr) { throw IllegalAccessException("Use Button(context:iconID:) instead.") }
|
||||
constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int, defStyleRes: Int) : super(context, attrs, defStyleAttr, defStyleRes) { throw IllegalAccessException("Use Button(context:iconID:) instead.") }
|
||||
|
||||
constructor(context: Context, isMain: Boolean, @DrawableRes iconID: Int) : super(context) {
|
||||
this.iconID = iconID
|
||||
this.isMain = isMain
|
||||
disableClipping()
|
||||
val size = resources.getDimension(R.dimen.new_conversation_button_expanded_size).toInt() + 2 * shadowMargin.toInt()
|
||||
val layoutParams = LayoutParams(size, size)
|
||||
this.layoutParams = layoutParams
|
||||
addView(imageView)
|
||||
imageView.x = collapsedImageViewPosition.x
|
||||
imageView.y = collapsedImageViewPosition.y
|
||||
gravity = Gravity.TOP or Gravity.LEFT // Intentionally not Gravity.START
|
||||
}
|
||||
|
||||
fun expand() {
|
||||
GlowViewUtilities.animateColorChange(context, imageView, R.color.new_conversation_button_collapsed_background, R.color.accent)
|
||||
@ColorRes val startShadowColorID = if (UiModeUtilities.isDayUiMode(context)) R.color.transparent_black_30 else R.color.black
|
||||
GlowViewUtilities.animateShadowColorChange(context, imageView, startShadowColorID, R.color.new_conversation_button_shadow)
|
||||
imageView.animateSizeChange(R.dimen.new_conversation_button_collapsed_size, R.dimen.new_conversation_button_expanded_size, animationDuration)
|
||||
animateImageViewPositionChange(collapsedImageViewPosition, expandedImageViewPosition)
|
||||
}
|
||||
|
||||
fun collapse() {
|
||||
GlowViewUtilities.animateColorChange(context, imageView, R.color.accent, R.color.new_conversation_button_collapsed_background)
|
||||
@ColorRes val endShadowColorID = if (UiModeUtilities.isDayUiMode(context)) R.color.transparent_black_30 else R.color.black
|
||||
GlowViewUtilities.animateShadowColorChange(context, imageView, R.color.new_conversation_button_shadow, endShadowColorID)
|
||||
imageView.animateSizeChange(R.dimen.new_conversation_button_expanded_size, R.dimen.new_conversation_button_collapsed_size, animationDuration)
|
||||
animateImageViewPositionChange(expandedImageViewPosition, collapsedImageViewPosition)
|
||||
}
|
||||
|
||||
private fun animateImageViewPositionChange(startPosition: PointF, endPosition: PointF) {
|
||||
val animation = ValueAnimator.ofObject(PointFEvaluator(), startPosition, endPosition)
|
||||
animation.duration = animationDuration
|
||||
animation.addUpdateListener { animator ->
|
||||
val point = animator.animatedValue as PointF
|
||||
imageView.x = point.x
|
||||
imageView.y = point.y
|
||||
}
|
||||
animation.start()
|
||||
}
|
||||
|
||||
fun animatePositionChange(startPosition: PointF, endPosition: PointF) {
|
||||
val animation = ValueAnimator.ofObject(PointFEvaluator(), startPosition, endPosition)
|
||||
animation.duration = animationDuration
|
||||
animation.addUpdateListener { animator ->
|
||||
val point = animator.animatedValue as PointF
|
||||
x = point.x
|
||||
y = point.y
|
||||
}
|
||||
animation.start()
|
||||
}
|
||||
|
||||
fun animateAlphaChange(startAlpha: Float, endAlpha: Float) {
|
||||
val animation = ValueAnimator.ofObject(FloatEvaluator(), startAlpha, endAlpha)
|
||||
animation.duration = animationDuration
|
||||
animation.addUpdateListener { animator ->
|
||||
alpha = animator.animatedValue as Float
|
||||
}
|
||||
animation.start()
|
||||
}
|
||||
}
|
||||
// endregion
|
||||
|
||||
// region Lifecycle
|
||||
constructor(context: Context) : super(context) { setUpViewHierarchy() }
|
||||
constructor(context: Context, attrs: AttributeSet) : super(context, attrs) { setUpViewHierarchy() }
|
||||
constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : super(context, attrs, defStyleAttr) { setUpViewHierarchy() }
|
||||
constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int, defStyleRes: Int) : super(context, attrs, defStyleAttr, defStyleRes) { setUpViewHierarchy() }
|
||||
|
||||
private fun setUpViewHierarchy() {
|
||||
disableClipping()
|
||||
isHapticFeedbackEnabled = true
|
||||
// Set up session button
|
||||
addView(sessionButton)
|
||||
addView(sessionTooltip)
|
||||
sessionButton.alpha = 0.0f
|
||||
sessionTooltip.alpha = 0.0f
|
||||
val sessionButtonLayoutParams = sessionButton.layoutParams as LayoutParams
|
||||
sessionButtonLayoutParams.addRule(CENTER_IN_PARENT, TRUE)
|
||||
sessionButtonLayoutParams.addRule(ALIGN_PARENT_BOTTOM, TRUE)
|
||||
sessionButtonLayoutParams.bottomMargin = bottomMargin.toInt()
|
||||
// Set up closed group button
|
||||
addView(closedGroupButton)
|
||||
addView(closedGroupTooltip)
|
||||
closedGroupButton.alpha = 0.0f
|
||||
closedGroupTooltip.alpha = 0.0f
|
||||
val closedGroupButtonLayoutParams = closedGroupButton.layoutParams as LayoutParams
|
||||
closedGroupButtonLayoutParams.addRule(CENTER_IN_PARENT, TRUE)
|
||||
closedGroupButtonLayoutParams.addRule(ALIGN_PARENT_BOTTOM, TRUE)
|
||||
closedGroupButtonLayoutParams.bottomMargin = bottomMargin.toInt()
|
||||
// Set up open group button
|
||||
addView(openGroupButton)
|
||||
addView(openGroupTooltip)
|
||||
openGroupButton.alpha = 0.0f
|
||||
openGroupTooltip.alpha = 0.0f
|
||||
val openGroupButtonLayoutParams = openGroupButton.layoutParams as LayoutParams
|
||||
openGroupButtonLayoutParams.addRule(CENTER_IN_PARENT, TRUE)
|
||||
openGroupButtonLayoutParams.addRule(ALIGN_PARENT_BOTTOM, TRUE)
|
||||
openGroupButtonLayoutParams.bottomMargin = bottomMargin.toInt()
|
||||
// Set up main button
|
||||
addView(mainButton)
|
||||
val mainButtonLayoutParams = mainButton.layoutParams as LayoutParams
|
||||
mainButtonLayoutParams.addRule(CENTER_IN_PARENT, TRUE)
|
||||
mainButtonLayoutParams.addRule(ALIGN_PARENT_BOTTOM, TRUE)
|
||||
mainButtonLayoutParams.bottomMargin = bottomMargin.toInt()
|
||||
}
|
||||
// endregion
|
||||
|
||||
// region Interaction
|
||||
override fun onTouchEvent(event: MotionEvent): Boolean {
|
||||
val touch = PointF(event.x, event.y)
|
||||
val allButtons = listOf( mainButton, sessionButton, closedGroupButton, openGroupButton )
|
||||
val buttonsExcludingMainButton = listOf( sessionButton, closedGroupButton, openGroupButton )
|
||||
if (allButtons.none { it.contains(touch) }) { return false }
|
||||
when (event.action) {
|
||||
MotionEvent.ACTION_DOWN -> {
|
||||
if (isExpanded) {
|
||||
if (mainButton.contains(touch)) { collapse() }
|
||||
} else {
|
||||
isExpanded = true
|
||||
expand()
|
||||
}
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
performHapticFeedback(HapticFeedbackConstants.CONTEXT_CLICK)
|
||||
} else {
|
||||
performHapticFeedback(HapticFeedbackConstants.LONG_PRESS)
|
||||
}
|
||||
}
|
||||
MotionEvent.ACTION_MOVE -> {
|
||||
mainButton.x = touch.x - mainButton.expandedSize / 2
|
||||
mainButton.y = touch.y - mainButton.expandedSize / 2
|
||||
mainButton.alpha = 1 - (PointF(mainButton.x, mainButton.y).distanceTo(buttonRestPosition) / maxDragDistance)
|
||||
val buttonToExpand = buttonsExcludingMainButton.firstOrNull { button ->
|
||||
var hasUserDraggedBeyondButton = false
|
||||
if (button == openGroupButton && touch.isLeftOf(openGroupButton, dragMargin)) { hasUserDraggedBeyondButton = true }
|
||||
if (button == sessionButton && touch.isAbove(sessionButton, dragMargin)) { hasUserDraggedBeyondButton = true }
|
||||
if (button == closedGroupButton && touch.isRightOf(closedGroupButton, dragMargin)) { hasUserDraggedBeyondButton = true }
|
||||
button.contains(touch) || hasUserDraggedBeyondButton
|
||||
}
|
||||
if (buttonToExpand != null) {
|
||||
if (buttonToExpand == expandedButton) { return true }
|
||||
expandedButton?.collapse()
|
||||
buttonToExpand.expand()
|
||||
this.expandedButton = buttonToExpand
|
||||
} else {
|
||||
expandedButton?.collapse()
|
||||
this.expandedButton = null
|
||||
}
|
||||
}
|
||||
MotionEvent.ACTION_UP, MotionEvent.ACTION_CANCEL -> {
|
||||
val mainButtonCenter = PointF(width.toFloat() / 2, height.toFloat() - bottomMargin - mainButton.expandedSize / 2)
|
||||
val distanceFromMainButtonCenter = touch.distanceTo(mainButtonCenter)
|
||||
fun collapse() {
|
||||
isExpanded = false
|
||||
this.collapse()
|
||||
}
|
||||
if (distanceFromMainButtonCenter > (minDragDistance + mainButton.collapsedSize / 2)) {
|
||||
if (sessionButton.contains(touch) || touch.isAbove(sessionButton, dragMargin)) { delegate?.createNewPrivateChat(); collapse() }
|
||||
else if (closedGroupButton.contains(touch) || touch.isRightOf(closedGroupButton, dragMargin)) { delegate?.createNewClosedGroup(); collapse() }
|
||||
else if (openGroupButton.contains(touch) || touch.isLeftOf(openGroupButton, dragMargin)) { delegate?.joinOpenGroup(); collapse() }
|
||||
else { collapse() }
|
||||
} else {
|
||||
val currentPosition = PointF(mainButton.x, mainButton.y)
|
||||
mainButton.animatePositionChange(currentPosition, buttonRestPosition)
|
||||
val endAlpha = 1.0f
|
||||
mainButton.animateAlphaChange(mainButton.alpha, endAlpha)
|
||||
expandedButton?.collapse()
|
||||
this.expandedButton = null
|
||||
}
|
||||
}
|
||||
}
|
||||
previousAction = event.action
|
||||
return true
|
||||
}
|
||||
|
||||
private fun expand() {
|
||||
val buttonsExcludingMainButton = listOf( sessionButton, closedGroupButton, openGroupButton )
|
||||
val allTooltips = listOf(sessionTooltip, closedGroupTooltip, openGroupTooltip)
|
||||
|
||||
sessionButton.animatePositionChange(buttonRestPosition, sessionButtonExpandedPosition)
|
||||
sessionTooltip.animatePositionChange(tooltipRestPosition(sessionTooltip.width), sessionTooltipExpandedPosition)
|
||||
closedGroupButton.animatePositionChange(buttonRestPosition, closedGroupButtonExpandedPosition)
|
||||
closedGroupTooltip.animatePositionChange(tooltipRestPosition(closedGroupTooltip.width), closedGroupTooltipExpandedPosition)
|
||||
openGroupButton.animatePositionChange(buttonRestPosition, openGroupButtonExpandedPosition)
|
||||
openGroupTooltip.animatePositionChange(tooltipRestPosition(openGroupTooltip.width), openGroupTooltipExpandedPosition)
|
||||
buttonsExcludingMainButton.forEach { it.animateAlphaChange(0.0f, 1.0f) }
|
||||
allTooltips.forEach { it.animateAlphaChange(0.0f, 1.0f) }
|
||||
postDelayed({ isExpanded = true }, Button.animationDuration)
|
||||
}
|
||||
|
||||
private fun collapse() {
|
||||
val allButtons = listOf( mainButton, sessionButton, closedGroupButton, openGroupButton )
|
||||
val allTooltips = listOf(sessionTooltip, closedGroupTooltip, openGroupTooltip)
|
||||
|
||||
allButtons.forEach {
|
||||
val currentPosition = PointF(it.x, it.y)
|
||||
it.animatePositionChange(currentPosition, buttonRestPosition)
|
||||
val endAlpha = if (it == mainButton) 1.0f else 0.0f
|
||||
it.animateAlphaChange(it.alpha, endAlpha)
|
||||
}
|
||||
allTooltips.forEach {
|
||||
it.animateAlphaChange(1.0f, 0.0f)
|
||||
it.animatePositionChange(PointF(it.x, it.y), tooltipRestPosition(it.width))
|
||||
}
|
||||
postDelayed({ isExpanded = false }, Button.animationDuration)
|
||||
}
|
||||
// endregion
|
||||
|
||||
fun View.animatePositionChange(startPosition: PointF, endPosition: PointF) {
|
||||
val animation = ValueAnimator.ofObject(PointFEvaluator(), startPosition, endPosition)
|
||||
animation.duration = Button.animationDuration
|
||||
animation.addUpdateListener { animator ->
|
||||
val point = animator.animatedValue as PointF
|
||||
x = point.x
|
||||
y = point.y
|
||||
}
|
||||
animation.start()
|
||||
}
|
||||
|
||||
fun View.animateAlphaChange(startAlpha: Float, endAlpha: Float) {
|
||||
val animation = ValueAnimator.ofObject(FloatEvaluator(), startAlpha, endAlpha)
|
||||
animation.duration = Button.animationDuration
|
||||
animation.addUpdateListener { animator ->
|
||||
alpha = animator.animatedValue as Float
|
||||
}
|
||||
animation.start()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// region Delegate
|
||||
interface NewConversationButtonSetViewDelegate {
|
||||
|
||||
fun joinOpenGroup()
|
||||
fun createNewPrivateChat()
|
||||
fun createNewClosedGroup()
|
||||
}
|
||||
// endregion
|
||||
Reference in New Issue
Block a user