committed by
Duncan McNamara
11 changed files with 491 additions and 4 deletions
|
After Width: | Height: | Size: 160 KiB |
|
After Width: | Height: | Size: 64 KiB |
|
After Width: | Height: | Size: 61 KiB |
|
After Width: | Height: | Size: 26 KiB |
@ -0,0 +1,200 @@ |
|||
<?xml version="1.0" encoding="utf-8"?><!-- |
|||
~ ************************************************************************* |
|||
~ dialog_widget_explanation.xml |
|||
~ ************************************************************************** |
|||
~ Copyright © 2022 VLC authors and VideoLAN |
|||
~ Author: Nicolas POMEPUY |
|||
~ 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 2 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, write to the Free Software |
|||
~ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. |
|||
~ *************************************************************************** |
|||
~ |
|||
~ |
|||
--> |
|||
|
|||
<layout xmlns:android="http://schemas.android.com/apk/res/android" |
|||
xmlns:app="http://schemas.android.com/apk/res-auto" |
|||
xmlns:tools="http://schemas.android.com/tools"> |
|||
|
|||
<data> |
|||
|
|||
<import type="android.view.View" /> |
|||
|
|||
</data> |
|||
|
|||
<androidx.core.widget.NestedScrollView |
|||
android:layout_width="match_parent" |
|||
android:layout_height="match_parent"> |
|||
|
|||
<androidx.constraintlayout.widget.ConstraintLayout |
|||
android:layout_width="match_parent" |
|||
android:layout_height="match_parent"> |
|||
|
|||
|
|||
<androidx.constraintlayout.widget.Group |
|||
android:id="@+id/step1" |
|||
android:layout_width="wrap_content" |
|||
android:layout_height="wrap_content" |
|||
app:constraint_referenced_ids="widget_sizes,textView32" |
|||
tools:visibility="visible" /> |
|||
|
|||
<androidx.constraintlayout.widget.Group |
|||
android:id="@+id/step2" |
|||
android:layout_width="wrap_content" |
|||
android:layout_height="wrap_content" |
|||
android:visibility="gone" |
|||
app:constraint_referenced_ids="widget_resize,widget_resize_handle,resizeLongTapIcon,widget_resize_text" /> |
|||
|
|||
<androidx.constraintlayout.widget.Group |
|||
android:id="@+id/step3" |
|||
android:layout_width="wrap_content" |
|||
android:layout_height="wrap_content" |
|||
android:visibility="gone" |
|||
app:constraint_referenced_ids="widget_explanation_step3_text,imageView19" /> |
|||
|
|||
<TextView |
|||
android:id="@+id/title" |
|||
style="@style/Theme.VLC.BottomSheetTitle" |
|||
android:layout_width="0dp" |
|||
android:layout_height="wrap_content" |
|||
android:paddingLeft="16dp" |
|||
android:paddingRight="16dp" |
|||
android:text="@string/widget_explanation_title" |
|||
app:layout_constraintStart_toStartOf="parent" |
|||
app:layout_constraintTop_toTopOf="parent" /> |
|||
|
|||
<TextView |
|||
android:id="@+id/textView32" |
|||
android:layout_width="0dp" |
|||
android:layout_height="wrap_content" |
|||
android:layout_marginStart="16dp" |
|||
android:layout_marginTop="16dp" |
|||
android:layout_marginEnd="16dp" |
|||
android:fontFamily="sans-serif-medium" |
|||
android:text="@string/widget_explanation_size" |
|||
android:textSize="16sp" |
|||
app:layout_constraintEnd_toEndOf="parent" |
|||
app:layout_constraintStart_toStartOf="parent" |
|||
app:layout_constraintTop_toBottomOf="@+id/title" /> |
|||
|
|||
<ImageView |
|||
android:id="@+id/widget_sizes" |
|||
android:layout_width="0dp" |
|||
android:layout_height="256dp" |
|||
android:layout_marginStart="16dp" |
|||
android:layout_marginTop="16dp" |
|||
android:layout_marginEnd="16dp" |
|||
android:padding="16dp" |
|||
android:scaleType="centerInside" |
|||
app:layout_constraintBottom_toTopOf="@+id/widget_next_button" |
|||
app:layout_constraintEnd_toEndOf="parent" |
|||
app:layout_constraintStart_toStartOf="parent" |
|||
app:layout_constraintTop_toBottomOf="@+id/textView32" |
|||
app:srcCompat="@drawable/widget_preview_mini" /> |
|||
|
|||
|
|||
<TextView |
|||
android:id="@+id/widget_resize_text" |
|||
android:layout_width="0dp" |
|||
android:layout_height="wrap_content" |
|||
android:layout_marginStart="16dp" |
|||
android:layout_marginTop="16dp" |
|||
android:layout_marginEnd="16dp" |
|||
android:fontFamily="sans-serif-medium" |
|||
android:text="@string/widget_explanation_resize" |
|||
android:textSize="16sp" |
|||
app:layout_constraintEnd_toEndOf="parent" |
|||
app:layout_constraintHorizontal_bias="0.0" |
|||
app:layout_constraintStart_toStartOf="parent" |
|||
app:layout_constraintTop_toBottomOf="@+id/title" /> |
|||
|
|||
|
|||
<ImageView |
|||
android:id="@+id/widget_resize" |
|||
android:layout_width="0dp" |
|||
android:layout_height="wrap_content" |
|||
android:layout_marginStart="16dp" |
|||
android:layout_marginTop="16dp" |
|||
android:layout_marginEnd="16dp" |
|||
android:layout_marginBottom="16dp" |
|||
android:padding="8dp" |
|||
android:src="@drawable/vlc_widget_mini" |
|||
app:layout_constraintBottom_toTopOf="@+id/widget_next_button" |
|||
app:layout_constraintEnd_toEndOf="parent" |
|||
app:layout_constraintStart_toStartOf="parent" |
|||
app:layout_constraintTop_toBottomOf="@+id/widget_resize_text" |
|||
app:srcCompat="@drawable/widget_preview_mini" /> |
|||
|
|||
<org.videolan.vlc.gui.view.WidgetHandleView |
|||
android:id="@+id/widget_resize_handle" |
|||
android:layout_width="0dp" |
|||
android:layout_height="0dp" |
|||
android:alpha="0" |
|||
app:layout_constraintBottom_toBottomOf="@+id/widget_resize" |
|||
app:layout_constraintLeft_toLeftOf="@+id/widget_resize" |
|||
app:layout_constraintRight_toRightOf="@+id/widget_resize" |
|||
app:layout_constraintTop_toTopOf="@+id/widget_resize" /> |
|||
|
|||
<ImageView |
|||
android:id="@+id/resizeLongTapIcon" |
|||
android:layout_width="52dp" |
|||
android:layout_height="52dp" |
|||
android:src="?attr/ic_tips_tap" |
|||
app:layout_constraintBottom_toBottomOf="@+id/widget_resize" |
|||
app:layout_constraintEnd_toEndOf="@+id/widget_resize" |
|||
app:layout_constraintStart_toStartOf="@+id/widget_resize" |
|||
app:layout_constraintTop_toTopOf="@+id/widget_resize" /> |
|||
|
|||
<ImageView |
|||
android:id="@+id/imageView19" |
|||
android:layout_width="wrap_content" |
|||
android:layout_height="wrap_content" |
|||
android:tint="?attr/colorPrimary" |
|||
android:layout_marginStart="16dp" |
|||
app:layout_constraintBottom_toBottomOf="@+id/widget_explanation_step3_text" |
|||
app:layout_constraintStart_toStartOf="parent" |
|||
app:layout_constraintTop_toTopOf="@+id/widget_explanation_step3_text" |
|||
app:srcCompat="@drawable/ic_theme" /> |
|||
|
|||
<TextView |
|||
android:id="@+id/widget_explanation_step3_text" |
|||
android:layout_width="0dp" |
|||
android:layout_height="wrap_content" |
|||
android:layout_marginStart="16dp" |
|||
android:layout_marginTop="16dp" |
|||
android:layout_marginEnd="16dp" |
|||
android:layout_marginBottom="16dp" |
|||
android:fontFamily="sans-serif-medium" |
|||
android:text="@string/widget_explanation_end" |
|||
android:textSize="16sp" |
|||
app:layout_constraintBottom_toTopOf="@+id/widget_next_button" |
|||
app:layout_constraintEnd_toEndOf="parent" |
|||
app:layout_constraintStart_toEndOf="@+id/imageView19" |
|||
app:layout_constraintTop_toBottomOf="@+id/title" /> |
|||
|
|||
<Button |
|||
android:id="@+id/widget_next_button" |
|||
style="@style/Widget.MaterialComponents.Button.TextButton.Dialog" |
|||
android:layout_width="wrap_content" |
|||
android:layout_height="wrap_content" |
|||
android:layout_marginEnd="16dp" |
|||
android:layout_marginBottom="16dp" |
|||
android:text="@string/next" |
|||
app:layout_constraintBottom_toBottomOf="parent" |
|||
app:layout_constraintEnd_toEndOf="parent" /> |
|||
|
|||
|
|||
</androidx.constraintlayout.widget.ConstraintLayout> |
|||
|
|||
</androidx.core.widget.NestedScrollView> |
|||
</layout> |
|||
@ -0,0 +1,183 @@ |
|||
/* |
|||
* ************************************************************************ |
|||
* WidgetExplanationDialog.kt |
|||
* ************************************************************************* |
|||
* Copyright © 2022 VLC authors and VideoLAN |
|||
* Author: Nicolas POMEPUY |
|||
* 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 2 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, write to the Free Software |
|||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. |
|||
* ************************************************************************** |
|||
* |
|||
* |
|||
*/ |
|||
|
|||
package org.videolan.vlc.gui.dialogs |
|||
|
|||
import android.animation.AnimatorSet |
|||
import android.animation.ObjectAnimator |
|||
import android.animation.PropertyValuesHolder |
|||
import android.animation.ValueAnimator |
|||
import android.os.Bundle |
|||
import android.os.Message |
|||
import android.view.LayoutInflater |
|||
import android.view.View |
|||
import android.view.ViewGroup |
|||
import android.view.animation.DecelerateInterpolator |
|||
import androidx.annotation.DrawableRes |
|||
import androidx.core.animation.doOnEnd |
|||
import androidx.core.content.ContextCompat |
|||
import com.google.android.material.bottomsheet.BottomSheetBehavior.STATE_EXPANDED |
|||
import org.videolan.tools.* |
|||
import org.videolan.vlc.R |
|||
import org.videolan.vlc.databinding.DialogWidgetExplanationBinding |
|||
|
|||
|
|||
class WidgetExplanationDialog : VLCBottomSheetDialogFragment() { |
|||
override fun getDefaultState(): Int = STATE_EXPANDED |
|||
|
|||
override fun needToManageOrientation(): Boolean = true |
|||
|
|||
private lateinit var resizeAnimation: AnimatorSet |
|||
internal lateinit var binding: DialogWidgetExplanationBinding |
|||
|
|||
private val handler = ResizeHandler(this) |
|||
|
|||
var currentStep = 1 |
|||
|
|||
|
|||
override fun initialFocusedView(): View = binding.title |
|||
|
|||
|
|||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View { |
|||
binding = DialogWidgetExplanationBinding.inflate(layoutInflater, container, false) |
|||
return binding.root |
|||
} |
|||
|
|||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { |
|||
super.onViewCreated(view, savedInstanceState) |
|||
|
|||
val sizeDrawbles = listOf(R.drawable.vlc_widget_mini, R.drawable.vlc_widget_micro, R.drawable.vlc_widget_pill, R.drawable.vlc_widget_macro) |
|||
displaySizeImage(sizeDrawbles[0]) |
|||
handler.sendEmptyMessageDelayed(ACTION_DISPLAY_NEXT, 1000) |
|||
binding.widgetNextButton.setOnClickListener { |
|||
when (currentStep) { |
|||
1 -> { |
|||
binding.step1.setGone() |
|||
binding.step2.setVisible() |
|||
binding.step3.setGone() |
|||
animateLongTap() |
|||
} |
|||
2 -> { |
|||
binding.step1.setGone() |
|||
binding.step2.setGone() |
|||
binding.step3.setVisible() |
|||
binding.widgetNextButton.text = getString(R.string.close) |
|||
resizeAnimation.cancel() |
|||
} |
|||
else -> dismiss() |
|||
} |
|||
currentStep++ |
|||
|
|||
} |
|||
} |
|||
|
|||
/** |
|||
* Display a new image in the UI showing the different widget sizes |
|||
* |
|||
* @param drawable |
|||
*/ |
|||
private fun displaySizeImage(@DrawableRes drawable: Int) { |
|||
binding.widgetSizes.setImageDrawable(ContextCompat.getDrawable(requireActivity(), drawable)) |
|||
} |
|||
|
|||
override fun dismiss() { |
|||
handler.removeMessages(ACTION_DISPLAY_NEXT) |
|||
super.dismiss() |
|||
} |
|||
|
|||
/** |
|||
* Animate the view showing how to resize a widget |
|||
* |
|||
*/ |
|||
private fun animateLongTap() { |
|||
val show = ObjectAnimator.ofFloat(binding.resizeLongTapIcon, View.ALPHA, 1F) |
|||
show.duration = 200 |
|||
show.startDelay = 2500 |
|||
|
|||
val tapScaleX: PropertyValuesHolder = PropertyValuesHolder.ofFloat(View.SCALE_X, 0.9F) |
|||
val tapScaleY: PropertyValuesHolder = PropertyValuesHolder.ofFloat(View.SCALE_Y, 0.9F) |
|||
val tap: ObjectAnimator = ObjectAnimator.ofPropertyValuesHolder(binding.resizeLongTapIcon, tapScaleX, tapScaleY) |
|||
tap.duration = 800 |
|||
|
|||
val showHandle: ObjectAnimator = ObjectAnimator.ofFloat(binding.widgetResizeHandle, View.ALPHA, 1F) |
|||
showHandle.duration = 200 |
|||
|
|||
|
|||
val widthAnimator = ValueAnimator.ofInt(binding.widgetResize.width, 128.dp) |
|||
widthAnimator.duration = 1500 |
|||
widthAnimator.interpolator = DecelerateInterpolator() |
|||
|
|||
|
|||
val untapScaleX: PropertyValuesHolder = PropertyValuesHolder.ofFloat(View.SCALE_X, 1F) |
|||
val untapScaleY: PropertyValuesHolder = PropertyValuesHolder.ofFloat(View.SCALE_Y, 1F) |
|||
val untap: ObjectAnimator = ObjectAnimator.ofPropertyValuesHolder(binding.resizeLongTapIcon, untapScaleX, untapScaleY) |
|||
untap.duration = 300 |
|||
|
|||
val hide = ObjectAnimator.ofFloat(binding.resizeLongTapIcon, View.ALPHA, 0F) |
|||
hide.duration = 200 |
|||
|
|||
resizeAnimation = AnimatorSet() |
|||
resizeAnimation.playSequentially(show, tap, showHandle, untap, hide, widthAnimator) |
|||
|
|||
resizeAnimation.doOnEnd { resizeAnimation.start() } |
|||
resizeAnimation.start() |
|||
} |
|||
|
|||
|
|||
} |
|||
|
|||
private const val ACTION_DISPLAY_NEXT = 1 |
|||
|
|||
/** |
|||
* Manages the widget size UI loop |
|||
* |
|||
* @param owner the dialog itself |
|||
*/ |
|||
private class ResizeHandler(owner: WidgetExplanationDialog) : WeakHandler<WidgetExplanationDialog>(owner) { |
|||
private val sizeDrawables = listOf(R.drawable.vlc_widget_mini, R.drawable.vlc_widget_micro, R.drawable.vlc_widget_pill, R.drawable.vlc_widget_macro) |
|||
var currentDrawable = R.drawable.vlc_widget_macro |
|||
|
|||
private fun displaySizeImage(@DrawableRes drawable: Int) { |
|||
owner?.let { owner -> |
|||
owner.activity?.let { |
|||
owner.binding.widgetSizes.setImageDrawable(ContextCompat.getDrawable(it, drawable)) |
|||
} |
|||
} |
|||
} |
|||
|
|||
override fun handleMessage(msg: Message) { |
|||
super.handleMessage(msg) |
|||
when (msg.what) { |
|||
ACTION_DISPLAY_NEXT -> { |
|||
removeMessages(ACTION_DISPLAY_NEXT) |
|||
val nextIndex = (sizeDrawables.indexOf(currentDrawable) + 1).coerceInOrDefault(0, sizeDrawables.size - 1, 0) |
|||
currentDrawable = sizeDrawables[nextIndex] |
|||
displaySizeImage(currentDrawable) |
|||
removeMessages(ACTION_DISPLAY_NEXT) |
|||
sendEmptyMessageDelayed(ACTION_DISPLAY_NEXT, 2000) |
|||
|
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,79 @@ |
|||
/* |
|||
* ************************************************************************ |
|||
* WidgetHandleView.kt |
|||
* ************************************************************************* |
|||
* Copyright © 2022 VLC authors and VideoLAN |
|||
* Author: Nicolas POMEPUY |
|||
* 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 2 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, write to the Free Software |
|||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. |
|||
* ************************************************************************** |
|||
* |
|||
* |
|||
*/ |
|||
|
|||
package org.videolan.vlc.gui.view |
|||
|
|||
import android.content.Context |
|||
import android.content.res.Resources |
|||
import android.graphics.Canvas |
|||
import android.graphics.Paint |
|||
import android.util.AttributeSet |
|||
import android.util.TypedValue |
|||
import android.view.View |
|||
import org.videolan.tools.dp |
|||
import org.videolan.vlc.R |
|||
|
|||
|
|||
class WidgetHandleView : View { |
|||
private val paint = Paint() |
|||
|
|||
constructor(context: Context) : super(context) { |
|||
initialize() |
|||
} |
|||
|
|||
constructor(context: Context, attrs: AttributeSet) : super(context, attrs) { |
|||
initialize() |
|||
} |
|||
|
|||
constructor(context: Context, attrs: AttributeSet, defStyle: Int) : super(context, attrs, defStyle) { |
|||
initialize() |
|||
} |
|||
|
|||
|
|||
private fun initialize() { |
|||
val typedValue = TypedValue() |
|||
val theme: Resources.Theme = context.theme |
|||
theme.resolveAttribute(R.attr.colorPrimary, typedValue, true) |
|||
paint.color = typedValue.data |
|||
paint.isAntiAlias = true |
|||
} |
|||
|
|||
override fun onDraw(canvas: Canvas?) { |
|||
val padding = 8.dp.toFloat() |
|||
val viewWidth = width |
|||
//3.56 is the known ratio of the [R.drawable.vlc_widget_mini] image. 6.dp is two times the stroke size |
|||
val width = ((height - 16.dp) * 3.56) - 6.dp |
|||
val hPadding = ((viewWidth.toFloat() - width) / 2).toFloat() |
|||
val height = height - padding |
|||
paint.strokeWidth = 3.dp.toFloat() |
|||
paint.style = Paint.Style.STROKE |
|||
canvas?.drawRoundRect(hPadding, padding, viewWidth - hPadding, height, 12.dp.toFloat(), 12.dp.toFloat(), paint) |
|||
paint.style = Paint.Style.FILL |
|||
canvas?.drawCircle(hPadding, padding + (height / 2), 6.dp.toFloat(), paint) |
|||
canvas?.drawCircle(hPadding + (width.toFloat() / 2), padding, 6.dp.toFloat(), paint) |
|||
canvas?.drawCircle(hPadding + width.toFloat(), padding + (height / 2), 6.dp.toFloat(), paint) |
|||
canvas?.drawCircle(hPadding + (width.toFloat() / 2), height, 6.dp.toFloat(), paint) |
|||
super.onDraw(canvas) |
|||
} |
|||
} |
|||
Loading…
Reference in new issue