Fixed video player

This commit fixes the issue n. 122 of the list of issues reported in the
previous repository (https://git.mentality.rip/FWGS/Husky/issues/122).
This commit is contained in:
Adolfo Santiago 2021-09-04 16:00:39 +02:00
parent c7163dbaff
commit 41b96beb1c
No known key found for this signature in database
GPG key ID: 244D6F9A317B4A65
8 changed files with 329 additions and 254 deletions

View file

@ -172,7 +172,8 @@ dependencies {
implementation(ApplicationLibs.Glide.glideOkhttp)
kapt(ApplicationLibs.Glide.glideCompiler)
implementation(ApplicationLibs.Google.flexBox)
implementation(ApplicationLibs.Google.flexbox)
implementation(ApplicationLibs.Google.exoplayer)
implementation(ApplicationLibs.Google.materialDesign)
implementation(ApplicationLibs.RxJava.rxAndroid)

View file

@ -39,6 +39,8 @@ import io.reactivex.plugins.RxJavaPlugins
import org.conscrypt.Conscrypt
import java.security.Security
import javax.inject.Inject
import timber.log.Timber
import timber.log.Timber.DebugTree
class TuskyApplication : Application(), HasAndroidInjector {
@ -93,6 +95,8 @@ class TuskyApplication : Application(), HasAndroidInjector {
.setWorkerFactory(notificationWorkerFactory)
.build()
)
Timber.plant(DebugTree())
}
override fun attachBaseContext(base: Context) {

View file

@ -177,10 +177,10 @@ class ComposeActivity : BaseActivity(),
val composeOptions = intent.getParcelableExtra<ComposeOptions?>(COMPOSE_OPTIONS_EXTRA)
if (!composeOptions?.formattingSyntax.isNullOrEmpty()) {
suggestFormattingSyntax = composeOptions?.formattingSyntax!!
suggestFormattingSyntax = if (!composeOptions?.formattingSyntax.isNullOrEmpty()) {
composeOptions?.formattingSyntax!!
} else {
suggestFormattingSyntax = activeAccount.defaultFormattingSyntax
activeAccount.defaultFormattingSyntax
}
viewModel.setup(composeOptions)
@ -1060,7 +1060,6 @@ class ComposeActivity : BaseActivity(),
private fun initiateMediaPicking() {
val intent = Intent(Intent.ACTION_GET_CONTENT)
intent.addCategory(Intent.CATEGORY_OPENABLE)
if(!viewModel.hasNoAttachmentLimits) {
val mimeTypes = arrayOf("image/*", "video/*", "audio/*")
@ -1068,6 +1067,7 @@ class ComposeActivity : BaseActivity(),
}
intent.type = "*/*"
intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true)
intent.addCategory(Intent.CATEGORY_OPENABLE)
startActivityForResult(intent, MEDIA_PICK_RESULT)
}

View file

@ -21,6 +21,7 @@ import com.keylesspalace.tusky.ViewMediaActivity
import com.keylesspalace.tusky.entity.Attachment
abstract class ViewMediaFragment : BaseFragment() {
private var toolbarVisibiltyDisposable: Function0<Boolean>? = null
abstract fun setupMediaView(

View file

@ -18,6 +18,7 @@ package com.keylesspalace.tusky.fragment
import android.animation.Animator
import android.animation.AnimatorListenerAdapter
import android.annotation.SuppressLint
import android.net.Uri
import android.os.Bundle
import android.os.Handler
import android.os.Looper
@ -26,182 +27,219 @@ import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.MediaController
import com.google.android.exoplayer2.MediaItem
import com.google.android.exoplayer2.PlaybackException
import com.google.android.exoplayer2.Player
import com.google.android.exoplayer2.SimpleExoPlayer
import com.google.android.exoplayer2.trackselection.DefaultTrackSelector
import com.keylesspalace.tusky.R
import com.keylesspalace.tusky.ViewMediaActivity
import com.keylesspalace.tusky.entity.Attachment
import com.keylesspalace.tusky.util.hide
import com.keylesspalace.tusky.util.visible
import com.keylesspalace.tusky.view.ExposedPlayPauseVideoView
import kotlinx.android.synthetic.main.activity_view_media.*
import kotlinx.android.synthetic.main.fragment_view_video.*
import kotlinx.android.synthetic.main.activity_view_media.toolbar
import kotlinx.android.synthetic.main.fragment_view_video.mediaDescription
import kotlinx.android.synthetic.main.fragment_view_video.progressBar
import kotlinx.android.synthetic.main.fragment_view_video.videoView
import timber.log.Timber
class ViewVideoFragment : ViewMediaFragment() {
private lateinit var toolbar: View
private val handler = Handler(Looper.getMainLooper())
private val hideToolbar = Runnable {
// Hoist toolbar hiding to activity so it can track state across different fragments
// This is explicitly stored as runnable so that we pass it to the handler later for cancellation
mediaActivity.onPhotoTap()
mediaController.hide()
}
private lateinit var mediaActivity: ViewMediaActivity
private val TOOLBAR_HIDE_DELAY_MS = 3000L
private lateinit var mediaController : MediaController
private var isAudio = false
override fun setUserVisibleHint(isVisibleToUser: Boolean) {
// Start/pause/resume video playback as fragment is shown/hidden
super.setUserVisibleHint(isVisibleToUser)
if (videoView == null) {
return
}
private lateinit var toolbar: View
private val handler = Handler(Looper.getMainLooper())
private val hideToolbar = Runnable {
// Hoist toolbar hiding to activity so it can track state across different fragments
// This is explicitly stored as runnable so that we pass it to the handler later for cancellation
mediaActivity.onPhotoTap()
mediaController.hide()
}
private lateinit var mediaActivity: ViewMediaActivity
private val TOOLBAR_HIDE_DELAY_MS = 3000L
private lateinit var mediaController: MediaController
private var isAudio = false
if (isVisibleToUser) {
if (mediaActivity.isToolbarVisible) {
handler.postDelayed(hideToolbar, TOOLBAR_HIDE_DELAY_MS)
}
videoView.start()
} else {
handler.removeCallbacks(hideToolbar)
videoView.pause()
mediaController.hide()
}
}
private var exoPlayer: SimpleExoPlayer? = null
private val playbackStateListener: Player.Listener = playbackStateListener()
private var playWhenReady = true
private var currentWindow = 0
private var playbackPosition = 0L
@SuppressLint("ClickableViewAccessibility")
override fun setupMediaView(
url: String,
previewUrl: String?,
description: String?,
showingDescription: Boolean
) {
mediaDescription.text = description
mediaDescription.visible(showingDescription)
override fun setUserVisibleHint(isVisibleToUser: Boolean) {
// Start/pause/resume video playback as fragment is shown/hidden
super.setUserVisibleHint(isVisibleToUser)
videoView.transitionName = url
videoView.setVideoPath(url)
mediaController = object : MediaController(mediaActivity) {
override fun show(timeout: Int) {
// We're doing manual auto-close management.
// Also, take focus back from the pause button so we can use the back button.
super.show(0)
mediaController.requestFocus()
}
if(videoView == null) {
return
}
override fun dispatchKeyEvent(event: KeyEvent?): Boolean {
if (event?.keyCode == KeyEvent.KEYCODE_BACK) {
if (event.action == KeyEvent.ACTION_UP) {
hide()
activity?.supportFinishAfterTransition()
}
return true
}
return super.dispatchKeyEvent(event)
}
}
if(isVisibleToUser) {
if(mediaActivity.isToolbarVisible) {
handler.postDelayed(hideToolbar, TOOLBAR_HIDE_DELAY_MS)
}
exoPlayer?.play()
} else {
handler.removeCallbacks(hideToolbar)
exoPlayer?.pause()
mediaController.hide()
}
}
mediaController.setMediaPlayer(videoView)
videoView.setMediaController(mediaController)
videoView.requestFocus()
videoView.setPlayPauseListener(object: ExposedPlayPauseVideoView.PlayPauseListener {
override fun onPause() {
handler.removeCallbacks(hideToolbar)
}
override fun onPlay() {
// Audio doesn't cause the controller to show automatically,
// and we only want to hide the toolbar if it's a video.
if (isAudio) {
mediaController.show()
} else {
hideToolbarAfterDelay(TOOLBAR_HIDE_DELAY_MS)
}
}
})
videoView.setOnPreparedListener { mp ->
val containerWidth = videoContainer.measuredWidth.toFloat()
val containerHeight = videoContainer.measuredHeight.toFloat()
val videoWidth = mp.videoWidth.toFloat()
val videoHeight = mp.videoHeight.toFloat()
@SuppressLint("ClickableViewAccessibility")
override fun setupMediaView(
url: String,
previewUrl: String?,
description: String?,
showingDescription: Boolean
) {
mediaDescription.text = description
mediaDescription.visible(showingDescription)
if(containerWidth/containerHeight > videoWidth/videoHeight) {
videoView.layoutParams.height = ViewGroup.LayoutParams.MATCH_PARENT
videoView.layoutParams.width = ViewGroup.LayoutParams.WRAP_CONTENT
} else {
videoView.layoutParams.height = ViewGroup.LayoutParams.WRAP_CONTENT
videoView.layoutParams.width = ViewGroup.LayoutParams.MATCH_PARENT
}
videoView.transitionName = url
mediaController = object : MediaController(mediaActivity) {
override fun show(timeout: Int) {
// We're doing manual auto-close management.
// Also, take focus back from the pause button so we can use the back button.
super.show(0)
mediaController.requestFocus()
}
// Wait until the media is loaded before accepting taps as we don't want toolbar to
// be hidden until then.
videoView.setOnTouchListener { _, _ ->
mediaActivity.onPhotoTap()
false
}
override fun dispatchKeyEvent(event: KeyEvent?): Boolean {
if(event?.keyCode == KeyEvent.KEYCODE_BACK) {
if(event.action == KeyEvent.ACTION_UP) {
hide()
activity?.supportFinishAfterTransition()
}
return true
}
return super.dispatchKeyEvent(event)
}
}
progressBar.hide()
mp.isLooping = true
if (arguments!!.getBoolean(ARG_START_POSTPONED_TRANSITION)) {
videoView.start()
}
}
val trackSelector = DefaultTrackSelector(requireActivity()).apply {
setParameters(buildUponParameters().setMaxVideoSizeSd())
}
if (arguments!!.getBoolean(ARG_START_POSTPONED_TRANSITION)) {
mediaActivity.onBringUp()
}
}
exoPlayer = SimpleExoPlayer.Builder(requireActivity())
.setTrackSelector(trackSelector)
.build()
.also { player ->
videoView.player = player
private fun hideToolbarAfterDelay(delayMilliseconds: Long) {
handler.postDelayed(hideToolbar, delayMilliseconds)
}
val mediaItem = MediaItem.Builder()
.setUri(Uri.parse(url))
.build()
player.setMediaItem(mediaItem)
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
toolbar = activity!!.toolbar
mediaActivity = activity as ViewMediaActivity
return inflater.inflate(R.layout.fragment_view_video, container, false)
}
player.addListener(playbackStateListener)
player.seekTo(currentWindow, playbackPosition)
player.playWhenReady = playWhenReady
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val attachment = arguments?.getParcelable<Attachment>(ARG_ATTACHMENT)
val url: String
player.prepare()
}
if (attachment == null) {
throw IllegalArgumentException("attachment has to be set")
}
url = attachment.url
isAudio = attachment.type == Attachment.Type.AUDIO
finalizeViewSetup(url, attachment.previewUrl, attachment.description)
}
videoView.requestFocus()
override fun onToolbarVisibilityChange(visible: Boolean) {
if (videoView == null || mediaDescription == null || !userVisibleHint) {
return
}
if(arguments!!.getBoolean(ARG_START_POSTPONED_TRANSITION)) {
mediaActivity.onBringUp()
}
}
isDescriptionVisible = showingDescription && visible
val alpha = if (isDescriptionVisible) 1.0f else 0.0f
if (isDescriptionVisible) {
// If to be visible, need to make visible immediately and animate alpha
mediaDescription.alpha = 0.0f
mediaDescription.visible(isDescriptionVisible)
}
private fun hideToolbarAfterDelay(delayMilliseconds: Long) {
handler.postDelayed(hideToolbar, delayMilliseconds)
}
mediaDescription.animate().alpha(alpha)
.setListener(object : AnimatorListenerAdapter() {
override fun onAnimationEnd(animation: Animator) {
mediaDescription?.visible(isDescriptionVisible)
animation.removeListener(this)
}
})
.start()
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
toolbar = activity!!.toolbar
mediaActivity = activity as ViewMediaActivity
return inflater.inflate(R.layout.fragment_view_video, container, false)
}
if (visible && videoView.isPlaying && !isAudio) {
hideToolbarAfterDelay(TOOLBAR_HIDE_DELAY_MS)
} else {
handler.removeCallbacks(hideToolbar)
}
}
override fun onStart() {
super.onStart()
override fun onTransitionEnd() {
}
val attachment = arguments?.getParcelable<Attachment>(ARG_ATTACHMENT)
?: throw IllegalArgumentException("attachment has to be set")
isAudio = (attachment.type == Attachment.Type.AUDIO)
finalizeViewSetup(attachment.url, attachment.previewUrl, attachment.description)
}
override fun onToolbarVisibilityChange(visible: Boolean) {
if(videoView == null || mediaDescription == null || !userVisibleHint) {
return
}
isDescriptionVisible = showingDescription && visible
val alpha = if(isDescriptionVisible) 1.0f else 0.0f
if(isDescriptionVisible) {
// If to be visible, need to make visible immediately and animate alpha
mediaDescription.alpha = 0.0f
mediaDescription.visible(isDescriptionVisible)
}
mediaDescription.animate().alpha(alpha)
.setListener(object : AnimatorListenerAdapter() {
override fun onAnimationEnd(animation: Animator) {
mediaDescription?.visible(isDescriptionVisible)
animation.removeListener(this)
}
})
.start()
if(visible && (videoView.player?.isPlaying == true) && !isAudio) {
hideToolbarAfterDelay(TOOLBAR_HIDE_DELAY_MS)
} else {
handler.removeCallbacks(hideToolbar)
}
}
override fun onTransitionEnd() {
}
override fun onPause() {
super.onPause()
releasePlayer()
}
override fun onStop() {
super.onStop()
releasePlayer()
}
private fun releasePlayer() {
exoPlayer?.run {
playbackPosition = this.currentPosition
currentWindow = this.currentWindowIndex
playWhenReady = this.playWhenReady
removeListener(playbackStateListener)
release()
}
exoPlayer = null
}
private fun playbackStateListener() = object : Player.Listener {
override fun onPlaybackStateChanged(playbackState: Int) {
when(playbackState) {
Player.STATE_BUFFERING -> {
progressBar.visibility = View.VISIBLE
}
Player.STATE_READY,
Player.STATE_ENDED -> {
progressBar.visibility = View.GONE
}
else -> {
}
}
}
override fun onPlayerError(error: PlaybackException) {
Timber.e(error.errorCodeName)
}
}
}

View file

@ -1,44 +1,44 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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/videoContainer"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clickable="true"
android:focusable="true">
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/videoContainer"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clickable="true"
android:focusable="true">
<TextView
android:id="@+id/mediaDescription"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="?attr/actionBarSize"
android:background="#60000000"
android:hyphenationFrequency="full"
android:lineSpacingMultiplier="1.1"
android:padding="8dp"
android:textAlignment="center"
android:textColor="#eee"
android:textSize="?attr/status_text_medium"
app:layout_constraintTop_toTopOf="parent"
tools:text="Some media description" />
<TextView
android:id="@+id/mediaDescription"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="?attr/actionBarSize"
android:background="#60000000"
android:hyphenationFrequency="full"
android:lineSpacingMultiplier="1.1"
android:padding="8dp"
android:textAlignment="center"
android:textColor="#eee"
android:textSize="?attr/status_text_medium"
app:layout_constraintTop_toTopOf="parent"
tools:text="Some media description" />
<com.keylesspalace.tusky.view.ExposedPlayPauseVideoView
android:id="@+id/videoView"
android:layout_width="wrap_content"
android:layout_height="match_parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<com.google.android.exoplayer2.ui.PlayerView
android:id="@+id/videoView"
android:layout_width="0dp"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<ProgressBar
android:id="@+id/progressBar"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<ProgressBar
android:id="@+id/progressBar"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

View file

@ -1,44 +1,72 @@
object ApplicationLibs {
private object Versions {
const val androidImageCropper = "2.8.0"
const val appcompat = "1.2.0"
const val autodispose = "1.4.0"
const val bigImageViewer = "1.7.0"
const val browser = "1.3.0"
const val cardView = "1.0.0"
const val conscryptAndroid = "2.5.1"
const val constraintlayout = "2.1.0"
const val coreKtx = "1.3.2"
const val dagger = "2.30.1"
const val emoji = "1.1.0"
const val exifInterface = "1.3.2"
const val exoplayer = "2.15.0"
const val filemojiCompat = "1.0.17"
const val flexbox = "2.0.1"
const val fragmentKtx = "1.2.5"
const val glide = "4.11.0"
const val glideImage = "1.8.0"
const val lifecycle = "2.2.0"
const val markdownEdit = "1.0.0"
const val materialDesign = "1.4.0"
const val materialDrawer = "8.2.0"
const val materialDrawerTypeface = "3.0.1.4.original-kotlin@aar"
const val pagingRuntimeKtx = "2.1.2"
const val preferenceKtx = "1.1.1"
const val okhttpVersion = "4.9.0"
const val recyclerView = "1.1.0"
const val retrofit = "2.9.0"
const val room = "2.2.5"
const val rxAndroid = "2.1.1"
const val rxJava = "2.2.20"
const val rxKotlin = "2.4.0"
const val shareTarget = "1.0.0"
const val simplestack = "2.6.2"
const val simplestackExt = "2.2.2"
const val sparkButton = "4.1.0"
const val swipeRefreshLayout = "1.1.0"
const val timber = "4.7.1"
const val viewpager2 = "1.0.0"
const val workRuntime = "2.4.0"
}
object AndroidX {
const val appCompat = "androidx.appcompat:appcompat:${Versions.appcompat}"
const val browser = "androidx.browser:browser:1.3.0"
const val cardView = "androidx.cardview:cardview:1.0.0"
const val browser = "androidx.browser:browser:${Versions.browser}"
const val cardView = "androidx.cardview:cardview:${Versions.cardView}"
const val constraintLayout =
"androidx.constraintlayout:constraintlayout:${Versions.constraintlayout}"
const val coreKtx = "androidx.core:core-ktx:${Versions.coreKtx}"
const val exifInterface = "androidx.exifinterface:exifinterface:1.3.2"
const val emoji = "androidx.emoji:emoji:1.1.0"
const val emojiAppCompat = "androidx.emoji:emoji-appcompat:1.1.0"
const val emojiBundled = "androidx.emoji:emoji-bundled:1.1.0"
const val fragmentKtx = "androidx.fragment:fragment-ktx:1.2.5"
const val pagingRuntimeKtx = "androidx.paging:paging-runtime-ktx:2.1.2"
const val preferenceKtx = "androidx.preference:preference-ktx:1.1.1"
const val recyclerView = "androidx.recyclerview:recyclerview:1.1.0"
const val emoji = "androidx.emoji:emoji:${Versions.emoji}"
const val emojiAppCompat = "androidx.emoji:emoji-appcompat:${Versions.emoji}"
const val emojiBundled = "androidx.emoji:emoji-bundled:${Versions.emoji}"
const val exifInterface = "androidx.exifinterface:exifinterface:${Versions.exifInterface}"
const val fragmentKtx = "androidx.fragment:fragment-ktx:${Versions.fragmentKtx}"
const val pagingRuntimeKtx =
"androidx.paging:paging-runtime-ktx:${Versions.pagingRuntimeKtx}"
const val preferenceKtx = "androidx.preference:preference-ktx:${Versions.preferenceKtx}"
const val recyclerView = "androidx.recyclerview:recyclerview:${Versions.recyclerView}"
const val roomCompiler = "androidx.room:room-compiler:${Versions.room}"
const val roomRuntime = "androidx.room:room-runtime:${Versions.room}"
const val roomRxJava = "androidx.room:room-rxjava2:${Versions.room}"
const val shareTarget = "androidx.sharetarget:sharetarget:1.0.0"
const val swipeRefreshLayout = "androidx.swiperefreshlayout:swiperefreshlayout:1.1.0"
const val viewpager2 = "androidx.viewpager2:viewpager2:1.0.0"
const val workRuntime = "androidx.work:work-runtime:2.4.0"
const val shareTarget = "androidx.sharetarget:sharetarget:${Versions.shareTarget}"
const val swipeRefreshLayout =
"androidx.swiperefreshlayout:swiperefreshlayout:${Versions.swipeRefreshLayout}"
const val viewpager2 = "androidx.viewpager2:viewpager2:${Versions.viewpager2}"
const val workRuntime = "androidx.work:work-runtime:${Versions.workRuntime}"
object Lifecycle {
const val liveDataKtx =
@ -65,7 +93,8 @@ object ApplicationLibs {
}
object Google {
const val flexBox = "com.google.android:flexbox:2.0.1"
const val flexbox = "com.google.android:flexbox:${Versions.flexbox}"
const val exoplayer = "com.google.android.exoplayer:exoplayer:${Versions.exoplayer}"
const val materialDesign = "com.google.android.material:material:${Versions.materialDesign}"
}
@ -76,9 +105,9 @@ object ApplicationLibs {
}
object RxJava {
const val rxAndroid = "io.reactivex.rxjava2:rxandroid:2.1.1"
const val rxJava = "io.reactivex.rxjava2:rxjava:2.2.20"
const val rxKotlin = "io.reactivex.rxjava2:rxkotlin:2.4.0"
const val rxAndroid = "io.reactivex.rxjava2:rxandroid:${Versions.rxAndroid}"
const val rxJava = "io.reactivex.rxjava2:rxjava:${Versions.rxJava}"
const val rxKotlin = "io.reactivex.rxjava2:rxkotlin:${Versions.rxKotlin}"
}
object SimpleStack {
@ -98,21 +127,23 @@ object ApplicationLibs {
const val okhttpBrotli = "com.squareup.okhttp3:okhttp-brotli:${Versions.okhttpVersion}"
}
const val androidImageCropper = "com.theartofdev.edmodo:android-image-cropper:2.8.0"
const val autodispose = "com.uber.autodispose:autodispose:1.4.0"
const val androidImageCropper =
"com.theartofdev.edmodo:android-image-cropper:${Versions.androidImageCropper}"
const val autodispose = "com.uber.autodispose:autodispose:${Versions.autodispose}"
const val autodisposeAndroidArchComp =
"com.uber.autodispose:autodispose-android-archcomponents:1.4.0"
const val bigImageViewer = "com.github.piasy:BigImageViewer:1.7.0"
const val conscryptAndroid = "org.conscrypt:conscrypt-android:2.5.1"
const val filemojiCompat = "de.c1710:filemojicompat:1.0.17"
const val glideImage = "com.github.piasy:GlideImageLoader:1.8.0"
const val glideImageViewFactory = "com.github.piasy:GlideImageViewFactory:1.8.0"
const val markdownEdit = "com.github.Tunous:MarkdownEdit:1.0.0"
"com.uber.autodispose:autodispose-android-archcomponents:${Versions.autodispose}"
const val bigImageViewer = "com.github.piasy:BigImageViewer:${Versions.bigImageViewer}"
const val conscryptAndroid = "org.conscrypt:conscrypt-android:${Versions.conscryptAndroid}"
const val filemojiCompat = "de.c1710:filemojicompat:${Versions.filemojiCompat}"
const val glideImage = "com.github.piasy:GlideImageLoader:${Versions.glideImage}"
const val glideImageViewFactory =
"com.github.piasy:GlideImageViewFactory:${Versions.glideImage}"
const val markdownEdit = "com.github.Tunous:MarkdownEdit:${Versions.markdownEdit}"
const val materialDrawer = "com.mikepenz:materialdrawer:${Versions.materialDrawer}"
const val materialDrawerIconics =
"com.mikepenz:materialdrawer-iconics:${Versions.materialDrawer}"
const val materialDrawerTypeface =
"com.mikepenz:google-material-typeface:3.0.1.4.original-kotlin@aar"
const val sparkButton = "com.github.connyduck:sparkbutton:4.1.0"
"com.mikepenz:google-material-typeface:${Versions.materialDrawerTypeface}"
const val sparkButton = "com.github.connyduck:sparkbutton:${Versions.sparkButton}"
const val timber = "com.jakewharton.timber:timber:${Versions.timber}"
}

0
husky/gradlew vendored Executable file → Normal file
View file