feat: lock player controls (#372)

* feat: lock player controls

Add Lock Player feature.

This feature is similar to the feature found in the stock Jellyfin Android app and in the VLC app.

When enabled, it disables the gestures (seek, volume and brightness control), disables the default player controls and leaves only two buttons: a back button and an "unlock" button.
Pressing the unlock button reverts the player back to the initial state, e.g. gestures enabled and default buttons shown.

Works with ExoPlayer and MPV Player.

Let me know of any issues.

* Update PlayerGestureHelper.kt

* Fixed spacing issues

* fixed bug + formatting

* Simplified code

* Fixed spacing

* fixed trailing line?

* refactor: set locked layout initial visibility in xml

---------

Co-authored-by: jarnedemeulemeester <jarnedemeulemeester@gmail.com>
This commit is contained in:
004matteos 2023-05-25 10:20:30 +03:00 committed by GitHub
parent 12b819bc7a
commit a886baf907
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 399 additions and 226 deletions

View file

@ -1,11 +1,13 @@
package dev.jdtech.jellyfin package dev.jdtech.jellyfin
import android.content.Context import android.content.Context
import android.content.pm.ActivityInfo
import android.media.AudioManager import android.media.AudioManager
import android.os.Bundle import android.os.Bundle
import android.view.View import android.view.View
import android.view.WindowManager import android.view.WindowManager
import android.widget.Button import android.widget.Button
import android.widget.FrameLayout
import android.widget.ImageButton import android.widget.ImageButton
import android.widget.ImageView import android.widget.ImageView
import android.widget.TextView import android.widget.TextView
@ -29,6 +31,8 @@ import dev.jdtech.jellyfin.viewmodels.PlayerActivityViewModel
import javax.inject.Inject import javax.inject.Inject
import timber.log.Timber import timber.log.Timber
var isControlsLocked: Boolean = false
@AndroidEntryPoint @AndroidEntryPoint
class PlayerActivity : BasePlayerActivity() { class PlayerActivity : BasePlayerActivity() {
@ -67,6 +71,11 @@ class PlayerActivity : BasePlayerActivity() {
finish() finish()
} }
binding.playerView.findViewById<View>(R.id.back_button_alt).setOnClickListener {
finish()
isControlsLocked = false
}
val videoNameTextView = binding.playerView.findViewById<TextView>(R.id.video_name) val videoNameTextView = binding.playerView.findViewById<TextView>(R.id.video_name)
viewModel.currentItemTitle.observe(this) { title -> viewModel.currentItemTitle.observe(this) { title ->
@ -77,10 +86,15 @@ class PlayerActivity : BasePlayerActivity() {
val subtitleButton = binding.playerView.findViewById<ImageButton>(R.id.btn_subtitle) val subtitleButton = binding.playerView.findViewById<ImageButton>(R.id.btn_subtitle)
val speedButton = binding.playerView.findViewById<ImageButton>(R.id.btn_speed) val speedButton = binding.playerView.findViewById<ImageButton>(R.id.btn_speed)
val skipIntroButton = binding.playerView.findViewById<Button>(R.id.btn_skip_intro) val skipIntroButton = binding.playerView.findViewById<Button>(R.id.btn_skip_intro)
val lockButton = binding.playerView.findViewById<ImageButton>(R.id.btn_lockview)
val unlockButton = binding.playerView.findViewById<ImageButton>(R.id.btn_unlock)
audioButton.isEnabled = false audioButton.isEnabled = false
audioButton.imageAlpha = 75 audioButton.imageAlpha = 75
lockButton.isEnabled = false
lockButton.imageAlpha = 75
subtitleButton.isEnabled = false subtitleButton.isEnabled = false
subtitleButton.imageAlpha = 75 subtitleButton.imageAlpha = 75
@ -120,6 +134,23 @@ class PlayerActivity : BasePlayerActivity() {
} }
} }
val exoPlayerControlView = findViewById<FrameLayout>(R.id.player_controls)
val lockedLayout = findViewById<FrameLayout>(R.id.locked_player_view)
lockButton.setOnClickListener {
exoPlayerControlView.visibility = View.GONE
lockedLayout.visibility = View.VISIBLE
requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_LOCKED
isControlsLocked = true
}
unlockButton.setOnClickListener {
exoPlayerControlView.visibility = View.VISIBLE
lockedLayout.visibility = View.GONE
requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE
isControlsLocked = false
}
subtitleButton.setOnClickListener { subtitleButton.setOnClickListener {
when (viewModel.player) { when (viewModel.player) {
is MPVPlayer -> { is MPVPlayer -> {
@ -189,6 +220,8 @@ class PlayerActivity : BasePlayerActivity() {
if (it) { if (it) {
audioButton.isEnabled = true audioButton.isEnabled = true
audioButton.imageAlpha = 255 audioButton.imageAlpha = 255
lockButton.isEnabled = true
lockButton.imageAlpha = 255
subtitleButton.isEnabled = true subtitleButton.isEnabled = true
subtitleButton.imageAlpha = 255 subtitleButton.imageAlpha = 255
speedButton.isEnabled = true speedButton.isEnabled = true

View file

@ -18,6 +18,7 @@ import androidx.media3.ui.PlayerView
import dev.jdtech.jellyfin.AppPreferences import dev.jdtech.jellyfin.AppPreferences
import dev.jdtech.jellyfin.Constants import dev.jdtech.jellyfin.Constants
import dev.jdtech.jellyfin.PlayerActivity import dev.jdtech.jellyfin.PlayerActivity
import dev.jdtech.jellyfin.isControlsLocked
import dev.jdtech.jellyfin.mpv.MPVPlayer import dev.jdtech.jellyfin.mpv.MPVPlayer
import kotlin.math.abs import kotlin.math.abs
import timber.log.Timber import timber.log.Timber
@ -65,6 +66,8 @@ class PlayerGestureHelper(
} }
override fun onDoubleTap(e: MotionEvent): Boolean { override fun onDoubleTap(e: MotionEvent): Boolean {
// Disables double tap gestures if view is locked
if (isControlsLocked) return false
val viewCenterX = playerView.measuredWidth / 2 val viewCenterX = playerView.measuredWidth / 2
val currentPos = playerView.player?.currentPosition ?: 0 val currentPos = playerView.player?.currentPosition ?: 0
@ -90,6 +93,8 @@ class PlayerGestureHelper(
): Boolean { ): Boolean {
// Excludes area where app gestures conflicting with system gestures // Excludes area where app gestures conflicting with system gestures
if (inExclusionArea(firstEvent)) return false if (inExclusionArea(firstEvent)) return false
// Disables seek gestures if view is locked
if (isControlsLocked) return false
// Check whether swipe was oriented vertically // Check whether swipe was oriented vertically
if (abs(distanceY / distanceX) < 2) { if (abs(distanceY / distanceX) < 2) {
@ -128,6 +133,8 @@ class PlayerGestureHelper(
): Boolean { ): Boolean {
// Excludes area where app gestures conflicting with system gestures // Excludes area where app gestures conflicting with system gestures
if (inExclusionArea(firstEvent)) return false if (inExclusionArea(firstEvent)) return false
// Disables volume gestures when player is locked
if (isControlsLocked) return false
if (abs(distanceY / distanceX) < 2) return false if (abs(distanceY / distanceX) < 2) return false
@ -213,6 +220,8 @@ class PlayerGestureHelper(
override fun onScaleBegin(detector: ScaleGestureDetector): Boolean = true override fun onScaleBegin(detector: ScaleGestureDetector): Boolean = true
override fun onScale(detector: ScaleGestureDetector): Boolean { override fun onScale(detector: ScaleGestureDetector): Boolean {
// Disables zoom gesture if view is locked
if (isControlsLocked) return false
lastScaleEvent = SystemClock.elapsedRealtime() lastScaleEvent = SystemClock.elapsedRealtime()
val scaleFactor = detector.scaleFactor val scaleFactor = detector.scaleFactor
if (abs(scaleFactor - Constants.ZOOM_SCALE_BASE) > Constants.ZOOM_SCALE_THRESHOLD) { if (abs(scaleFactor - Constants.ZOOM_SCALE_BASE) > Constants.ZOOM_SCALE_THRESHOLD) {

View file

@ -0,0 +1,64 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout 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"
android:id="@+id/locked_player_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="gone"
tools:visibility="visible">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="8dp"
android:orientation="horizontal">
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="38dp"
android:orientation="horizontal"
app:layout_constraintEnd_toStartOf="@id/extra_buttons_alt"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<ImageButton
android:id="@+id/back_button_alt"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/rounded_corner"
android:contentDescription="@string/player_controls_exit"
android:padding="16dp"
android:src="@drawable/ic_arrow_left" />
<Space
android:layout_width="16dp"
android:layout_height="0dp" />
</LinearLayout>
<LinearLayout
android:id="@+id/extra_buttons_alt"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="16dp"
android:orientation="horizontal"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent">
<ImageButton
android:id="@+id/btn_unlock"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="end"
android:background="@drawable/rounded_corner"
android:contentDescription="@string/select_playback_speed"
android:padding="16dp"
android:src="@drawable/ic_unlock"
app:tint="@android:color/white" />
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
</FrameLayout>

View file

@ -0,0 +1,249 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout 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"
android:id="@+id/player_controls"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/player_background">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="8dp"
android:orientation="horizontal">
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:layout_constraintEnd_toStartOf="@id/extra_buttons"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<ImageButton
android:id="@+id/back_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/transparent_circle_background"
android:contentDescription="@string/player_controls_exit"
android:padding="16dp"
android:src="@drawable/ic_arrow_left" />
<Space
android:layout_width="16dp"
android:layout_height="0dp" />
<TextView
android:id="@+id/video_name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginEnd="16dp"
android:ellipsize="end"
android:maxLines="1"
android:textAppearance="@style/TextAppearance.Material3.TitleLarge"
android:textColor="@android:color/white"
tools:text="The Dawn of Despair" />
</LinearLayout>
<LinearLayout
android:id="@+id/extra_buttons"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent">
<ImageButton
android:id="@+id/btn_lockview"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="end"
android:layout_weight="1"
android:background="@drawable/transparent_circle_background"
android:contentDescription="Locks the player"
android:padding="16dp"
android:src="@drawable/ic_lock"
app:tint="@android:color/white" />
<Space
android:layout_width="16dp"
android:layout_height="0dp"
android:layout_weight="1" />
<ImageButton
android:id="@+id/btn_speed"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="end"
android:background="@drawable/transparent_circle_background"
android:contentDescription="@string/select_playback_speed"
android:padding="16dp"
android:src="@drawable/ic_gauge"
app:tint="@android:color/white" />
<Space
android:layout_width="16dp"
android:layout_height="0dp" />
<ImageButton
android:id="@+id/btn_audio_track"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="end"
android:background="@drawable/transparent_circle_background"
android:contentDescription="@string/select_audio_track"
android:padding="16dp"
android:src="@drawable/ic_speaker"
app:tint="@android:color/white" />
<Space
android:layout_width="16dp"
android:layout_height="0dp" />
<ImageButton
android:id="@+id/btn_subtitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="end"
android:background="@drawable/transparent_circle_background"
android:contentDescription="@string/select_subtile_track"
android:padding="16dp"
android:src="@drawable/ic_closed_caption"
app:tint="@android:color/white" />
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:gravity="center_vertical"
android:orientation="horizontal">
<ImageButton
android:id="@+id/exo_prev"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="32dp"
android:background="@drawable/transparent_circle_background"
android:padding="16dp"
android:src="@drawable/ic_skip_back" />
<ImageButton
android:id="@+id/exo_rew"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="32dp"
android:background="@drawable/transparent_circle_background"
android:padding="16dp"
android:src="@drawable/ic_rewind" />
<ImageButton
android:id="@+id/exo_play_pause"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/circle_background"
android:backgroundTint="@android:color/white"
android:padding="16dp"
android:src="@drawable/ic_play"
app:tint="@android:color/black" />
<ImageButton
android:id="@+id/exo_pause"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/circle_background"
android:backgroundTint="@android:color/white"
android:padding="16dp"
android:src="@drawable/ic_pause"
android:visibility="gone"
app:tint="@android:color/black" />
<ImageButton
android:id="@+id/exo_ffwd"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="32dp"
android:background="@drawable/transparent_circle_background"
android:padding="16dp"
android:src="@drawable/ic_fast_forward" />
<ImageButton
android:id="@+id/exo_next"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="32dp"
android:background="@drawable/transparent_circle_background"
android:padding="16dp"
android:src="@drawable/ic_skip_forward" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="bottom"
android:orientation="vertical"
android:padding="16dp">
<ImageView
android:id="@+id/image_preview"
android:layout_width="160dp"
android:layout_height="90dp"
android:layout_gravity="start"
android:background="@android:color/transparent"
android:visibility="gone"
tools:visibility="visible" />
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:orientation="horizontal"
android:paddingStart="8dp"
android:paddingEnd="8dp">
<TextView
android:id="@+id/exo_position"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="@android:color/white"
android:textSize="14sp"
tools:text="00:00" />
<Space
android:layout_width="8dp"
android:layout_height="0dp" />
<View
android:layout_width="4dp"
android:layout_height="1dp"
android:background="@android:color/white" />
<Space
android:layout_width="8dp"
android:layout_height="0dp" />
<TextView
android:id="@+id/exo_duration"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="@android:color/white"
android:textSize="14sp"
tools:text="24:21" />
</LinearLayout>
<androidx.media3.ui.DefaultTimeBar
android:id="@+id/exo_progress"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:played_color="?attr/colorPrimary" />
</LinearLayout>
</FrameLayout>

View file

@ -1,232 +1,14 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" <FrameLayout 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"
android:id="@+id/player_controls"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent">
android:background="@color/player_background">
<androidx.constraintlayout.widget.ConstraintLayout <include layout="@layout/exo_main_controls"/>
<include
layout="@layout/exo_locked_controls"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="match_parent"
android:layout_margin="8dp" android:layout_gravity="bottom|end" />
android:orientation="horizontal">
<LinearLayout </FrameLayout>
android:layout_width="0dp"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:layout_constraintEnd_toStartOf="@id/extra_buttons"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<ImageButton
android:id="@+id/back_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/transparent_circle_background"
android:contentDescription="@string/player_controls_exit"
android:padding="16dp"
android:src="@drawable/ic_arrow_left" />
<Space
android:layout_width="16dp"
android:layout_height="0dp" />
<TextView
android:id="@+id/video_name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginEnd="16dp"
android:ellipsize="end"
android:maxLines="1"
android:textAppearance="@style/TextAppearance.Material3.TitleLarge"
android:textColor="@android:color/white"
tools:text="The Dawn of Despair" />
</LinearLayout>
<LinearLayout
android:id="@+id/extra_buttons"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent">
<ImageButton
android:id="@+id/btn_speed"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="end"
android:background="@drawable/transparent_circle_background"
android:contentDescription="@string/select_playback_speed"
android:padding="16dp"
android:src="@drawable/ic_gauge"
app:tint="@android:color/white" />
<Space
android:layout_width="16dp"
android:layout_height="0dp" />
<ImageButton
android:id="@+id/btn_audio_track"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="end"
android:background="@drawable/transparent_circle_background"
android:contentDescription="@string/select_audio_track"
android:padding="16dp"
android:src="@drawable/ic_speaker"
app:tint="@android:color/white" />
<Space
android:layout_width="16dp"
android:layout_height="0dp" />
<ImageButton
android:id="@+id/btn_subtitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="end"
android:background="@drawable/transparent_circle_background"
android:contentDescription="@string/select_subtile_track"
android:padding="16dp"
android:src="@drawable/ic_closed_caption"
app:tint="@android:color/white" />
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:gravity="center_vertical"
android:orientation="horizontal">
<ImageButton
android:id="@+id/exo_prev"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="32dp"
android:background="@drawable/transparent_circle_background"
android:padding="16dp"
android:src="@drawable/ic_skip_back" />
<ImageButton
android:id="@+id/exo_rew"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="32dp"
android:background="@drawable/transparent_circle_background"
android:padding="16dp"
android:src="@drawable/ic_rewind" />
<ImageButton
android:id="@+id/exo_play_pause"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/circle_background"
android:backgroundTint="@android:color/white"
android:padding="16dp"
android:src="@drawable/ic_play"
app:tint="@android:color/black" />
<ImageButton
android:id="@+id/exo_pause"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/circle_background"
android:backgroundTint="@android:color/white"
android:padding="16dp"
android:src="@drawable/ic_pause"
android:visibility="gone"
app:tint="@android:color/black" />
<ImageButton
android:id="@+id/exo_ffwd"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="32dp"
android:background="@drawable/transparent_circle_background"
android:padding="16dp"
android:src="@drawable/ic_fast_forward" />
<ImageButton
android:id="@+id/exo_next"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="32dp"
android:background="@drawable/transparent_circle_background"
android:padding="16dp"
android:src="@drawable/ic_skip_forward" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="bottom"
android:orientation="vertical"
android:padding="16dp">
<ImageView
android:id="@+id/image_preview"
android:layout_width="160dp"
android:layout_height="90dp"
android:layout_gravity="start"
android:background="@android:color/transparent"
android:visibility="gone"
tools:visibility="visible" />
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:orientation="horizontal"
android:paddingStart="8dp"
android:paddingEnd="8dp">
<TextView
android:id="@+id/exo_position"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="@android:color/white"
android:textSize="14sp"
tools:text="00:00" />
<Space
android:layout_width="8dp"
android:layout_height="0dp" />
<View
android:layout_width="4dp"
android:layout_height="1dp"
android:background="@android:color/white" />
<Space
android:layout_width="8dp"
android:layout_height="0dp" />
<TextView
android:id="@+id/exo_duration"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="@android:color/white"
android:textSize="14sp"
tools:text="24:21" />
</LinearLayout>
<androidx.media3.ui.DefaultTimeBar
android:id="@+id/exo_progress"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:played_color="?attr/colorPrimary" />
</LinearLayout>
</FrameLayout>

View file

@ -0,0 +1,20 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:pathData="M5,11L19,11A2,2 0,0 1,21 13L21,20A2,2 0,0 1,19 22L5,22A2,2 0,0 1,3 20L3,13A2,2 0,0 1,5 11z"
android:strokeLineJoin="round"
android:strokeWidth="2"
android:fillColor="#00000000"
android:strokeColor="@android:color/white"
android:strokeLineCap="round"/>
<path
android:pathData="M7,11V7a5,5 0,0 1,9.9 -1"
android:strokeLineJoin="round"
android:strokeWidth="2"
android:fillColor="#00000000"
android:strokeColor="@android:color/white"
android:strokeLineCap="round"/>
</vector>

View file

@ -0,0 +1,16 @@
<?xml version="1.0" encoding="utf-8"?>
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
android:color="?attr/colorControlHighlight">
<item android:id="@android:id/mask">
<shape android:shape="oval">
<solid android:color="@android:color/white" />
</shape>
</item>
<item>
<shape>
<solid android:color="#72000000" />
<corners android:radius="10000dp" />
</shape>
</item>
</ripple>