Implement InputBarButton

This commit is contained in:
Niels Andriesse 2021-06-15 14:05:32 +10:00
parent acc472bbad
commit 5755f4150a
11 changed files with 182 additions and 58 deletions

View File

@ -4,11 +4,15 @@ import android.content.Context
import android.util.AttributeSet import android.util.AttributeSet
import android.view.LayoutInflater import android.view.LayoutInflater
import android.widget.LinearLayout import android.widget.LinearLayout
import android.widget.RelativeLayout
import kotlinx.android.synthetic.main.view_input_bar.view.* import kotlinx.android.synthetic.main.view_input_bar.view.*
import network.loki.messenger.R import network.loki.messenger.R
class InputBar : LinearLayout { class InputBar : LinearLayout {
private val attachmentsButton by lazy { InputBarButton(context, R.drawable.ic_plus_24) }
private val microphoneButton by lazy { InputBarButton(context, R.drawable.ic_microphone) }
// region Lifecycle // region Lifecycle
constructor(context: Context) : super(context) { constructor(context: Context) : super(context) {
setUpViewHierarchy() setUpViewHierarchy()
@ -24,7 +28,11 @@ class InputBar : LinearLayout {
private fun setUpViewHierarchy() { private fun setUpViewHierarchy() {
LayoutInflater.from(context).inflate(R.layout.view_input_bar, this) LayoutInflater.from(context).inflate(R.layout.view_input_bar, this)
attachmentsButtonContainer.addView(attachmentsButton)
attachmentsButton.layoutParams = RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.MATCH_PARENT, RelativeLayout.LayoutParams.MATCH_PARENT)
attachmentsButton.setOnClickListener { } attachmentsButton.setOnClickListener { }
microphoneButtonContainer.addView(microphoneButton)
microphoneButton.layoutParams = RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.MATCH_PARENT, RelativeLayout.LayoutParams.MATCH_PARENT)
microphoneButton.setOnClickListener { } microphoneButton.setOnClickListener { }
} }
// endregion // endregion

View File

@ -0,0 +1,100 @@
package org.thoughtcrime.securesms.conversation.v2.input_bar
import android.animation.PointFEvaluator
import android.animation.ValueAnimator
import android.content.Context
import android.content.res.ColorStateList
import android.graphics.PointF
import android.util.AttributeSet
import android.view.Gravity
import android.view.MotionEvent
import android.widget.ImageView
import android.widget.RelativeLayout
import androidx.annotation.DrawableRes
import network.loki.messenger.R
import org.thoughtcrime.securesms.loki.utilities.*
import org.thoughtcrime.securesms.loki.views.GlowViewUtilities
import org.thoughtcrime.securesms.loki.views.InputBarButtonImageViewContainer
class InputBarButton : RelativeLayout {
@DrawableRes private var iconID = 0
companion object {
val animationDuration = 250.toLong()
}
private val expandedImageViewPosition by lazy { PointF(0.0f, 0.0f) }
private val collapsedImageViewPosition by lazy { PointF((expandedSize - collapsedSize) / 2, (expandedSize - collapsedSize) / 2) }
val expandedSize by lazy { resources.getDimension(R.dimen.input_bar_button_expanded_size) }
val collapsedSize by lazy { resources.getDimension(R.dimen.input_bar_button_collapsed_size) }
private val imageViewContainer by lazy {
val result = InputBarButtonImageViewContainer(context)
val size = collapsedSize.toInt()
result.layoutParams = LayoutParams(size, size)
result.setBackgroundResource(R.drawable.input_bar_button_background)
result.mainColor = resources.getColorWithID(R.color.input_bar_button_background, context.theme)
result
}
private val imageView by lazy {
val result = ImageView(context)
val size = toPx(16, resources)
result.layoutParams = LayoutParams(size, size)
result.scaleType = ImageView.ScaleType.CENTER_INSIDE
result.setImageResource(iconID)
result.imageTintList = ColorStateList.valueOf(resources.getColorWithID(R.color.text, context.theme))
result
}
constructor(context: Context) : super(context) { throw IllegalAccessException("Use InputBarButton(context:iconID:) instead.") }
constructor(context: Context, attrs: AttributeSet) : super(context, attrs) { throw IllegalAccessException("Use InputBarButton(context:iconID:) instead.") }
constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : super(context, attrs, defStyleAttr) { throw IllegalAccessException("Use InputBarButton(context:iconID:) instead.") }
constructor(context: Context, @DrawableRes iconID: Int) : super(context) {
this.iconID = iconID
val size = resources.getDimension(R.dimen.input_bar_button_expanded_size).toInt()
val layoutParams = LayoutParams(size, size)
this.layoutParams = layoutParams
addView(imageViewContainer)
imageViewContainer.x = collapsedImageViewPosition.x
imageViewContainer.y = collapsedImageViewPosition.y
imageViewContainer.addView(imageView)
val imageViewLayoutParams = imageView.layoutParams as LayoutParams
imageViewLayoutParams.addRule(RelativeLayout.CENTER_IN_PARENT)
imageView.layoutParams = imageViewLayoutParams
gravity = Gravity.TOP or Gravity.LEFT // Intentionally not Gravity.START
}
fun expand() {
GlowViewUtilities.animateColorChange(context, imageViewContainer, R.color.input_bar_button_background, R.color.accent)
imageViewContainer.animateSizeChange(R.dimen.input_bar_button_collapsed_size, R.dimen.input_bar_button_expanded_size, animationDuration)
animateImageViewContainerPositionChange(collapsedImageViewPosition, expandedImageViewPosition)
}
fun collapse() {
GlowViewUtilities.animateColorChange(context, imageViewContainer, R.color.accent, R.color.input_bar_button_background)
imageViewContainer.animateSizeChange(R.dimen.input_bar_button_expanded_size, R.dimen.input_bar_button_collapsed_size, animationDuration)
animateImageViewContainerPositionChange(expandedImageViewPosition, collapsedImageViewPosition)
}
private fun animateImageViewContainerPositionChange(startPosition: PointF, endPosition: PointF) {
val animation = ValueAnimator.ofObject(PointFEvaluator(), startPosition, endPosition)
animation.duration = animationDuration
animation.addUpdateListener { animator ->
val point = animator.animatedValue as PointF
imageViewContainer.x = point.x
imageViewContainer.y = point.y
}
animation.start()
}
override fun onTouchEvent(event: MotionEvent): Boolean {
when (event.action) {
MotionEvent.ACTION_DOWN -> { expand() }
MotionEvent.ACTION_UP, MotionEvent.ACTION_CANCEL -> { collapse() }
}
return true
}
}

View File

@ -7,9 +7,12 @@ import android.graphics.*
import android.util.AttributeSet import android.util.AttributeSet
import android.view.View import android.view.View
import android.view.ViewOutlineProvider import android.view.ViewOutlineProvider
import android.widget.ImageView
import android.widget.LinearLayout import android.widget.LinearLayout
import android.widget.RelativeLayout
import androidx.annotation.ColorInt import androidx.annotation.ColorInt
import androidx.annotation.ColorRes import androidx.annotation.ColorRes
import androidx.appcompat.widget.AppCompatImageView
import network.loki.messenger.R import network.loki.messenger.R
import org.thoughtcrime.securesms.loki.utilities.getColorWithID import org.thoughtcrime.securesms.loki.utilities.getColorWithID
import org.thoughtcrime.securesms.loki.utilities.toPx import org.thoughtcrime.securesms.loki.utilities.toPx
@ -155,4 +158,36 @@ class PathDotView : View, GlowView {
super.onDraw(c) super.onDraw(c)
} }
// endregion // endregion
} }
class InputBarButtonImageViewContainer : RelativeLayout, GlowView {
@ColorInt override var mainColor: Int = 0
set(newValue) { field = newValue; paint.color = newValue }
@ColorInt override var sessionShadowColor: Int = 0 // Unused
private val paint: Paint by lazy {
val result = Paint()
result.style = Paint.Style.FILL
result.isAntiAlias = true
result
}
// region Lifecycle
constructor(context: Context) : super(context) { }
constructor(context: Context, attrs: AttributeSet) : super(context, attrs) { }
constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : super(context, attrs, defStyleAttr) { }
init {
setWillNotDraw(false)
}
// endregion
// region Updating
override fun onDraw(c: Canvas) {
val w = width.toFloat()
val h = height.toFloat()
c.drawCircle(w / 2, h / 2, w / 2, paint)
super.onDraw(c)
}
// endregion
}

View File

@ -1,17 +1,4 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<ripple <shape
xmlns:android="http://schemas.android.com/apk/res/android" xmlns:android="http://schemas.android.com/apk/res/android"
android:color="@color/accent"> android:shape="oval" />
<item
android:id="@android:id/mask">
<shape android:shape="oval">
<solid android:color="@color/white" />
</shape>
</item>
<item>
<shape android:shape="oval">
<solid android:color="@color/input_bar_button_background" />
</shape>
</item>
</ripple>

View File

@ -19,6 +19,8 @@
android:text="Elon" android:text="Elon"
android:textColor="@color/text" android:textColor="@color/text"
android:textStyle="bold" android:textStyle="bold"
android:textSize="@dimen/very_large_font_size" /> android:textSize="@dimen/very_large_font_size"
android:maxLines="1"
android:ellipsize="end" />
</LinearLayout> </LinearLayout>

View File

@ -1,5 +1,6 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" <LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
@ -116,13 +117,6 @@
</LinearLayout> </LinearLayout>
<View
android:layout_width="match_parent"
android:layout_height="1px"
android:layout_gravity="bottom"
android:layout_marginStart="@dimen/medium_spacing"
android:background="?android:dividerHorizontal" />
</FrameLayout> </FrameLayout>
</LinearLayout> </LinearLayout>

View File

@ -1,7 +1,6 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<LinearLayout <LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android" xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="@dimen/input_bar_height" android:layout_height="@dimen/input_bar_height"
android:orientation="vertical"> android:orientation="vertical">
@ -16,23 +15,12 @@
android:layout_height="match_parent"> android:layout_height="match_parent">
<RelativeLayout <RelativeLayout
android:id="@+id/attachmentsButton" android:id="@+id/attachmentsButtonContainer"
android:layout_width="40dp" android:layout_width="@dimen/input_bar_button_expanded_size"
android:layout_height="40dp" android:layout_height="@dimen/input_bar_button_expanded_size"
android:layout_alignParentStart="true" android:layout_alignParentStart="true"
android:layout_centerVertical="true" android:layout_centerVertical="true"
android:layout_marginStart="@dimen/small_spacing" android:layout_marginStart="@dimen/small_spacing" />
android:background="@drawable/input_bar_button_background">
<ImageView
android:layout_width="16dp"
android:layout_height="16dp"
android:scaleType="centerInside"
android:layout_centerInParent="true"
android:src="@drawable/ic_plus_24"
app:tint="@color/text" />
</RelativeLayout>
<TextView <TextView
android:layout_width="wrap_content" android:layout_width="wrap_content"
@ -43,23 +31,12 @@
android:background="@color/red" /> android:background="@color/red" />
<RelativeLayout <RelativeLayout
android:id="@+id/microphoneButton" android:id="@+id/microphoneButtonContainer"
android:layout_width="40dp" android:layout_width="@dimen/input_bar_button_expanded_size"
android:layout_height="40dp" android:layout_height="@dimen/input_bar_button_expanded_size"
android:layout_alignParentEnd="true" android:layout_alignParentEnd="true"
android:layout_centerVertical="true" android:layout_centerVertical="true"
android:layout_marginEnd="@dimen/small_spacing" android:layout_marginEnd="@dimen/small_spacing" />
android:background="@drawable/input_bar_button_background">
<ImageView
android:layout_width="16dp"
android:layout_height="16dp"
android:scaleType="centerInside"
android:layout_centerInParent="true"
android:src="@drawable/ic_microphone"
app:tint="@color/text" />
</RelativeLayout>
</RelativeLayout> </RelativeLayout>

View File

@ -0,0 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/inputBarButton"
android:layout_width="40dp"
android:layout_height="40dp"
android:background="@drawable/input_bar_button_background">
<ImageView
android:id="@+id/inputBarButtonImageView"
android:layout_width="16dp"
android:layout_height="16dp"
android:scaleType="centerInside"
android:layout_centerInParent="true"
android:src="@drawable/ic_plus_24"
app:tint="@color/text" />
</RelativeLayout>

View File

@ -21,7 +21,7 @@
<color name="open_group_chip_color">#0D000000</color> <color name="open_group_chip_color">#0D000000</color>
<color name="message_selected">#FFFFFF</color> <color name="message_selected">#FFFFFF</color>
<color name="input_bar_background">#FCFCFC</color> <color name="input_bar_background">#FCFCFC</color>
<color name="input_bar_button_background">#0D000000</color> <color name="input_bar_button_background">#F2F2F2</color>
<color name="default_background_start">#ffffff</color> <color name="default_background_start">#ffffff</color>
<color name="default_background_end">#fcfcfc</color> <color name="default_background_end">#fcfcfc</color>

View File

@ -28,7 +28,7 @@
<color name="open_group_chip_color">#0DFFFFFF</color> <color name="open_group_chip_color">#0DFFFFFF</color>
<color name="message_selected">#000000</color> <color name="message_selected">#000000</color>
<color name="input_bar_background">#171717</color> <color name="input_bar_background">#171717</color>
<color name="input_bar_button_background">#0DFFFFFF</color> <color name="input_bar_button_background">#0D0D0D</color>
<array name="profile_picture_placeholder_colors"> <array name="profile_picture_placeholder_colors">
<item>#5ff8b0</item> <item>#5ff8b0</item>

View File

@ -37,6 +37,8 @@
<dimen name="path_row_dot_size">8dp</dimen> <dimen name="path_row_dot_size">8dp</dimen>
<dimen name="path_row_expanded_dot_size">16dp</dimen> <dimen name="path_row_expanded_dot_size">16dp</dimen>
<dimen name="input_bar_height">56dp</dimen> <dimen name="input_bar_height">56dp</dimen>
<dimen name="input_bar_button_collapsed_size">40dp</dimen>
<dimen name="input_bar_button_expanded_size">48dp</dimen>
<!-- Distances --> <!-- Distances -->
<dimen name="small_spacing">8dp</dimen> <dimen name="small_spacing">8dp</dimen>