diff --git a/app/src/main/java/org/linphone/activities/voip/views/HorizontalScrollDotsView.kt b/app/src/main/java/org/linphone/activities/voip/views/HorizontalScrollDotsView.kt deleted file mode 100644 index 7b4cc560f..000000000 --- a/app/src/main/java/org/linphone/activities/voip/views/HorizontalScrollDotsView.kt +++ /dev/null @@ -1,190 +0,0 @@ -/* - * Copyright (c) 2010-2021 Belledonne Communications SARL. - * - * This file is part of linphone-android - * (see https://www.linphone.org). - * - * 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 . - */ -package org.linphone.activities.voip.views - -import android.content.Context -import android.graphics.Canvas -import android.graphics.Color -import android.graphics.Paint -import android.graphics.Rect -import android.util.AttributeSet -import android.view.View -import android.widget.HorizontalScrollView -import java.lang.Exception -import kotlin.math.ceil -import kotlin.math.roundToInt -import org.linphone.R -import org.linphone.core.tools.Log -import org.linphone.utils.AppUtils - -class HorizontalScrollDotsView : View { - private var count = 2 - private var selected = 0 - - private var radius: Float = 5f - private var margin: Float = 2f - private var screenWidth: Float = 0f - private var itemWidth: Float = 0f - - private lateinit var dotPaint: Paint - private lateinit var selectedDotPaint: Paint - - private var horizontalScrollViewRef = 0 - private lateinit var horizontalScrollView: HorizontalScrollView - private val scrollListener = OnScrollChangeListener { v, scrollX, _, _, _ -> - val childWidth: Int = (v as HorizontalScrollView).getChildAt(0).measuredWidth - val scrollViewWidth = v.measuredWidth - val scrollableX = childWidth - scrollViewWidth - - if (scrollableX > 0) { - val percent = (scrollX.toFloat() * 100 / scrollableX).toDouble() - if (count > 1) { - val selectedDot = percent / (100 / (count - 1)) - val dot = selectedDot.roundToInt() - if (dot != selected) { - setSelectedDot(dot) - } - } - } - } - - constructor(context: Context) : super(context) { init(context) } - - constructor(context: Context, attrs: AttributeSet) : this(context, attrs, 0) - - constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : super( - context, - attrs, - defStyleAttr - ) { - init(context) - - context.theme.obtainStyledAttributes( - attrs, - R.styleable.HorizontalScrollDot, - defStyleAttr, 0 - ).apply { - try { - radius = getDimension(R.styleable.HorizontalScrollDot_dotRadius, 5f) - - count = getInt(R.styleable.HorizontalScrollDot_dotCount, 1) - - val color = getColor(R.styleable.HorizontalScrollDot_dotColor, context.resources.getColor(R.color.voip_gray_dots)) - dotPaint.color = color - val selectedColor = getColor(R.styleable.HorizontalScrollDot_selectedDotColor, context.resources.getColor(R.color.voip_dark_gray)) - selectedDotPaint.color = selectedColor - - selected = getInt(R.styleable.HorizontalScrollDot_selectedDot, 1) - - horizontalScrollViewRef = getResourceId(R.styleable.HorizontalScrollDot_horizontalScrollView, 0) - Log.d("[Horizontal Scroll Dots] HorizontalScrollView reference set is $horizontalScrollViewRef") - - invalidate() - } catch (e: Exception) { - Log.e("[Horizontal Scroll Dots] $e") - } finally { - recycle() - } - } - } - - fun init(context: Context) { - radius = AppUtils.dpToPixels(context, 5f) - margin = AppUtils.dpToPixels(context, 5f) - - dotPaint = Paint() - dotPaint.color = Color.parseColor("#D8D8D8") - selectedDotPaint = Paint() - selectedDotPaint.color = Color.parseColor("#4B5964") - - val screenRect = Rect() - getWindowVisibleDisplayFrame(screenRect) - screenWidth = screenRect.width().toFloat() - val marginBetweenItems = context.resources.getDimension(R.dimen.voip_active_speaker_miniature_margin) - itemWidth = context.resources.getDimension(R.dimen.voip_active_speaker_miniature_size) + marginBetweenItems - Log.d("[Horizontal Scroll Dots] Screen width is $screenWidth and item width is $itemWidth") - } - - override fun onAttachedToWindow() { - super.onAttachedToWindow() - - if (horizontalScrollViewRef > 0) { - try { - horizontalScrollView = (parent as View).findViewById(horizontalScrollViewRef) - horizontalScrollView.setOnScrollChangeListener(scrollListener) - Log.d("[Horizontal Scroll Dots] HorizontalScrollView scroll listener set") - } catch (e: Exception) { - Log.e("[Horizontal Scroll Dots] Failed to find HorizontalScrollView from id $horizontalScrollViewRef: $e") - } - } else { - Log.e("[Horizontal Scroll Dots] No HorizontalScrollView reference given") - } - } - - override fun onDraw(canvas: Canvas) { - super.onDraw(canvas) - - for (i in 0 until count) { - if (i == selected) { - canvas.drawCircle( - (i + 1) * margin + (i * 2 + 1) * radius, - radius, - radius, - selectedDotPaint - ) - } else { - canvas.drawCircle( - (i + 1) * margin + (i * 2 + 1) * radius, - radius, - radius, - dotPaint - ) - } - } - } - - override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) { - super.onMeasure(widthMeasureSpec, heightMeasureSpec) - - val width = ((radius * 2 + margin) * count + margin).toInt() - val height: Int = (radius * 2).toInt() - - setMeasuredDimension(width, height) - } - - private fun setDotCount(count: Int) { - this.count = count - requestLayout() - invalidate() - } - - fun setItemCount(items: Int) { - val itemsPerScreen = (screenWidth / itemWidth) - val dots = ceil(items.toDouble() / itemsPerScreen).toInt() - - Log.d("[Horizontal Scroll Dots] Calculated $count for $items items ($itemsPerScreen items fit in screen width), given that screen width is $screenWidth and item width is $itemWidth") - setDotCount(dots) - } - - fun setSelectedDot(index: Int) { - selected = index - invalidate() - } -} diff --git a/app/src/main/java/org/linphone/activities/voip/views/ScrollDotsView.kt b/app/src/main/java/org/linphone/activities/voip/views/ScrollDotsView.kt new file mode 100644 index 000000000..c3c8e9296 --- /dev/null +++ b/app/src/main/java/org/linphone/activities/voip/views/ScrollDotsView.kt @@ -0,0 +1,245 @@ +/* + * Copyright (c) 2010-2021 Belledonne Communications SARL. + * + * This file is part of linphone-android + * (see https://www.linphone.org). + * + * 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 . + */ +package org.linphone.activities.voip.views + +import android.content.Context +import android.graphics.Canvas +import android.graphics.Color +import android.graphics.Paint +import android.graphics.Rect +import android.util.AttributeSet +import android.view.View +import android.view.View.OnScrollChangeListener +import android.widget.FrameLayout +import android.widget.HorizontalScrollView +import android.widget.ScrollView +import kotlin.math.ceil +import kotlin.math.roundToInt +import org.linphone.R +import org.linphone.core.tools.Log +import org.linphone.utils.AppUtils + +class ScrollDotsView : View { + private var count = 2 + private var selected = 0 + + private var radius: Float = 5f + private var margin: Float = 2f + + private var screenWidth: Float = 0f + private var itemWidth: Float = 0f + private var screenHeight: Float = 0f + private var itemHeight: Float = 0f + + private lateinit var dotPaint: Paint + private lateinit var selectedDotPaint: Paint + + private var scrollViewRef = 0 + private lateinit var scrollView: FrameLayout + private var isHorizontal = false + + private val scrollListener = OnScrollChangeListener { v, scrollX, _, _, _ -> + if (isHorizontal) { + if (v !is HorizontalScrollView) { + Log.e("[Scoll Dots] ScrollView reference isn't a HorizontalScrollView!") + return@OnScrollChangeListener + } + + val childWidth: Int = v.getChildAt(0).measuredWidth + val scrollViewWidth = v.measuredWidth + val scrollableX = childWidth - scrollViewWidth + + if (scrollableX > 0) { + val percent = (scrollX.toFloat() * 100 / scrollableX).toDouble() + if (count > 1) { + val selectedDot = percent / (100 / (count - 1)) + val dot = selectedDot.roundToInt() + if (dot != selected) { + setSelectedDot(dot) + } + } + } + } else { + if (v !is ScrollView) { + Log.e("[Scoll Dots] ScrollView reference isn't a ScrollView!") + return@OnScrollChangeListener + } + + val childHeight: Int = v.getChildAt(0).measuredHeight + val scrollViewHeight = v.measuredHeight + val scrollableY = childHeight - scrollViewHeight + + if (scrollableY > 0) { + val percent = (scrollY.toFloat() * 100 / scrollableY).toDouble() + if (count > 1) { + val selectedDot = percent / (100 / (count - 1)) + val dot = selectedDot.roundToInt() + if (dot != selected) { + setSelectedDot(dot) + } + } + } + } + } + + constructor(context: Context) : super(context) { init(context) } + + constructor(context: Context, attrs: AttributeSet) : this(context, attrs, 0) + + constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : super( + context, + attrs, + defStyleAttr + ) { + init(context) + + context.theme.obtainStyledAttributes( + attrs, + R.styleable.ScrollDot, + defStyleAttr, 0 + ).apply { + try { + radius = getDimension(R.styleable.ScrollDot_dotRadius, 5f) + + count = getInt(R.styleable.ScrollDot_dotCount, 1) + + val color = getColor(R.styleable.ScrollDot_dotColor, context.resources.getColor(R.color.voip_gray_dots)) + dotPaint.color = color + val selectedColor = getColor(R.styleable.ScrollDot_selectedDotColor, context.resources.getColor(R.color.voip_dark_gray)) + selectedDotPaint.color = selectedColor + + selected = getInt(R.styleable.ScrollDot_selectedDot, 1) + + scrollViewRef = getResourceId(R.styleable.ScrollDot_scrollView, 0) + Log.d("[Scroll Dots] ScrollView reference set is $scrollViewRef") + + invalidate() + } catch (e: Exception) { + Log.e("[Scroll Dots] $e") + } finally { + recycle() + } + } + } + + fun init(context: Context) { + radius = AppUtils.dpToPixels(context, 5f) + margin = AppUtils.dpToPixels(context, 5f) + + dotPaint = Paint() + dotPaint.color = Color.parseColor("#D8D8D8") + selectedDotPaint = Paint() + selectedDotPaint.color = Color.parseColor("#4B5964") + + val screenRect = Rect() + getWindowVisibleDisplayFrame(screenRect) + screenWidth = screenRect.width().toFloat() + screenHeight = screenRect.height().toFloat() + + val marginBetweenItems = context.resources.getDimension(R.dimen.voip_active_speaker_miniature_margin) + itemWidth = context.resources.getDimension(R.dimen.voip_active_speaker_miniature_size) + marginBetweenItems + itemHeight = context.resources.getDimension(R.dimen.voip_active_speaker_miniature_size) + marginBetweenItems + + Log.d("[Scroll Dots] Screen size is $screenWidth/$screenHeight and item size is $itemWidth/$itemHeight") + } + + override fun onAttachedToWindow() { + super.onAttachedToWindow() + + if (scrollViewRef > 0) { + try { + scrollView = (parent as View).findViewById(scrollViewRef) + scrollView.setOnScrollChangeListener(scrollListener) + Log.d("[Scroll Dots] ScrollView scroll listener set") + + if (scrollView is HorizontalScrollView) { + isHorizontal = true + } + } catch (e: Exception) { + Log.e("[Scroll Dots] Failed to find ScrollView from id $scrollViewRef: $e") + } + } else { + Log.e("[Scroll Dots] No ScrollView reference given") + } + } + + override fun onDraw(canvas: Canvas) { + super.onDraw(canvas) + + for (i in 0 until count) { + if (i == selected) { + val position = (i + 1) * margin + (i * 2 + 1) * radius + canvas.drawCircle( + if (isHorizontal) position else radius, + if (isHorizontal) radius else position, + radius, + selectedDotPaint + ) + } else { + val position = (i + 1) * margin + (i * 2 + 1) * radius + canvas.drawCircle( + if (isHorizontal) position else radius, + if (isHorizontal) radius else position, + radius, + dotPaint + ) + } + } + } + + override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) { + super.onMeasure(widthMeasureSpec, heightMeasureSpec) + + if (isHorizontal) { + val width = ((radius * 2 + margin) * count + margin).toInt() + val height: Int = (radius * 2).toInt() + setMeasuredDimension(width, height) + } else { + val height = ((radius * 2 + margin) * count + margin).toInt() + val width: Int = (radius * 2).toInt() + setMeasuredDimension(width, height) + } + } + + private fun setDotCount(count: Int) { + this.count = count + requestLayout() + invalidate() + } + + fun setItemCount(items: Int) { + if (isHorizontal) { + val itemsPerScreen = (screenWidth / itemWidth) + val dots = ceil(items.toDouble() / itemsPerScreen).toInt() + Log.d("[Scroll Dots] Calculated $count for $items items ($itemsPerScreen items fit in screen width), given that screen width is $screenWidth and item width is $itemWidth") + setDotCount(dots) + } else { + val itemsPerScreen = (screenHeight / itemHeight) + val dots = ceil(items.toDouble() / itemsPerScreen).toInt() + Log.d("[Vertical Scroll Dots] Calculated $count for $items items ($itemsPerScreen items fit in screen height), given that screen height is $screenHeight and item height is $itemHeight") + setDotCount(dots) + } + } + + fun setSelectedDot(index: Int) { + selected = index + invalidate() + } +} diff --git a/app/src/main/java/org/linphone/utils/DataBindingUtils.kt b/app/src/main/java/org/linphone/utils/DataBindingUtils.kt index a469dcacb..3ffca9698 100644 --- a/app/src/main/java/org/linphone/utils/DataBindingUtils.kt +++ b/app/src/main/java/org/linphone/utils/DataBindingUtils.kt @@ -50,7 +50,7 @@ import org.linphone.R import org.linphone.activities.GenericActivity import org.linphone.activities.main.settings.SettingListener import org.linphone.activities.voip.data.ConferenceParticipantDeviceData -import org.linphone.activities.voip.views.HorizontalScrollDotsView +import org.linphone.activities.voip.views.ScrollDotsView import org.linphone.contact.ContactAvatarGenerator import org.linphone.contact.ContactDataInterface import org.linphone.contact.getPictureUri @@ -641,6 +641,14 @@ fun setConstraintLayoutMargins(view: View, margins: Float) { view.layoutParams = params } +@BindingAdapter("android:layout_marginTop") +fun setConstraintLayoutTopMargin(view: View, margins: Float) { + val params = view.layoutParams as ConstraintLayout.LayoutParams + val m = margins.toInt() + params.setMargins(params.leftMargin, m, params.rightMargin, params.bottomMargin) + view.layoutParams = params +} + @BindingAdapter("android:onTouch") fun View.setTouchListener(listener: View.OnTouchListener) { setOnTouchListener(listener) @@ -691,11 +699,11 @@ fun setParticipantTextureView( } @BindingAdapter("itemCount") -fun HorizontalScrollDotsView.setItems(count: Int) { +fun ScrollDotsView.setItems(count: Int) { setItemCount(count) } @BindingAdapter("selectedDot") -fun HorizontalScrollDotsView.setSelectedIndex(index: Int) { +fun ScrollDotsView.setSelectedIndex(index: Int) { setSelectedDot(index) } diff --git a/app/src/main/res/layout-land/voip_conference_active_speaker.xml b/app/src/main/res/layout-land/voip_conference_active_speaker.xml new file mode 100644 index 000000000..d27dd6cf7 --- /dev/null +++ b/app/src/main/res/layout-land/voip_conference_active_speaker.xml @@ -0,0 +1,142 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/voip_conference_active_speaker.xml b/app/src/main/res/layout/voip_conference_active_speaker.xml index 1b3e26c1b..b0f5c194e 100644 --- a/app/src/main/res/layout/voip_conference_active_speaker.xml +++ b/app/src/main/res/layout/voip_conference_active_speaker.xml @@ -25,20 +25,6 @@ android:layout_margin="@{controlsViewModel.fullScreenMode || controlsViewModel.pipMode ? @dimen/voip_remote_margin_full_screen : @dimen/voip_remote_margin, default=@dimen/voip_remote_margin}" android:visibility="@{inflatedVisibility}"> - - - - @@ -86,7 +71,7 @@ android:contentDescription="@null" coilVoipContact="@{conferenceViewModel.speakingParticipant}" app:layout_constraintDimensionRatio="1:1" - app:layout_constraintBottom_toTopOf="@id/bottom_barrier" + app:layout_constraintBottom_toTopOf="@id/miniatures" app:layout_constraintEnd_toEndOf="@id/active_speaker_background" app:layout_constraintHeight_max="@dimen/voip_contact_avatar_max_size" app:layout_constraintStart_toStartOf="@id/active_speaker_background" @@ -114,21 +99,13 @@ app:layout_constraintBottom_toBottomOf="@id/active_speaker_background" app:layout_constraintStart_toStartOf="@id/active_speaker_background" /> - - - diff --git a/app/src/main/res/layout/voip_conference_participant_remote_active_speaker_miniature.xml b/app/src/main/res/layout/voip_conference_participant_remote_active_speaker_miniature.xml index f08059e08..a4d5921e9 100644 --- a/app/src/main/res/layout/voip_conference_participant_remote_active_speaker_miniature.xml +++ b/app/src/main/res/layout/voip_conference_participant_remote_active_speaker_miniature.xml @@ -14,8 +14,7 @@ diff --git a/app/src/main/res/values/attrs.xml b/app/src/main/res/values/attrs.xml index 7fbee54c0..c3f0bf67d 100644 --- a/app/src/main/res/values/attrs.xml +++ b/app/src/main/res/values/attrs.xml @@ -75,13 +75,13 @@ - + - + \ No newline at end of file diff --git a/app/src/main/res/values/dimen.xml b/app/src/main/res/values/dimen.xml index b655a1147..aeb8a9735 100644 --- a/app/src/main/res/values/dimen.xml +++ b/app/src/main/res/values/dimen.xml @@ -61,11 +61,13 @@ 30dp 25dp 137dp - 200dp + 180dp 80sp 60dp 80dp 40dp 1dp 280dp + 10dp + 0dp \ No newline at end of file