Skip to content

Commit

Permalink
Merge pull request #34 from emreesen27/v1.0.0-stable
Browse files Browse the repository at this point in the history
V1.0.0 stable
  • Loading branch information
emreesen27 authored Nov 24, 2024
2 parents 5132089 + 2fc6a54 commit 369b4b6
Show file tree
Hide file tree
Showing 94 changed files with 1,990 additions and 659 deletions.
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
## v1.0.0-Stable (24.11.2024)
* Stable version !
* The interface has been completely redesigned for a sleeker and more intuitive user experience.
* Performance enhancements have been implemented to ensure a smoother and faster experience.
* The music player has been improved for a better listening experience.
* Shuffle playback feature has been added for a more dynamic music experience

## v1.0.0-Beta3 (09.10.2024)
* [FIX] The version was upgraded due to an issue in the Explode module.
* [FIX] The player now opens without waiting for the music to load.
Expand Down
4 changes: 2 additions & 2 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@ android {
applicationId = "com.snstudio.hyper"
minSdk = 26
targetSdk = 34
versionCode = 4
versionName = "1.0.0-Beta3"
versionCode = 5
versionName = "1.0.0"
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
}

Expand Down
7 changes: 6 additions & 1 deletion app/dependencies.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,9 @@ dependencies {

// Progress
implementation(libs.nested.progress)
implementation(libs.snackprogressbar)

// BottomBar
implementation(libs.smoothbottombar)

// Expo-Media
implementation(libs.media3.exoplayer.player)
Expand All @@ -54,6 +56,9 @@ dependencies {
// Toast
implementation(libs.toasty)

// Skeleton
implementation libs.skeletonlayout

// Firebase
implementation(platform(libs.firebase.bom))
implementation(libs.firebase.analytics)
Expand Down
241 changes: 147 additions & 94 deletions app/src/main/java/com/snstudio/hyper/MainActivity.kt
Original file line number Diff line number Diff line change
@@ -1,26 +1,30 @@
package com.snstudio.hyper

import android.animation.ValueAnimator
import android.os.Bundle
import android.view.View
import android.view.ViewGroup
import android.widget.FrameLayout
import android.view.ViewPropertyAnimator
import android.widget.PopupMenu
import android.widget.ProgressBar
import androidx.activity.viewModels
import androidx.annotation.OptIn
import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.widget.AppCompatImageView
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
import androidx.core.view.isVisible
import androidx.media3.common.util.UnstableApi
import androidx.navigation.NavController
import androidx.navigation.findNavController
import com.google.android.material.bottomsheet.BottomSheetBehavior
import com.google.android.material.textview.MaterialTextView
import com.snstudio.hyper.core.extension.click
import com.snstudio.hyper.core.extension.gone
import com.snstudio.hyper.core.extension.loadArtwork
import com.snstudio.hyper.core.extension.observe
import com.snstudio.hyper.core.extension.startColorAnimation
import com.snstudio.hyper.core.extension.slideDownToggle
import com.snstudio.hyper.core.extension.slideInUp
import com.snstudio.hyper.core.extension.slideOutDown
import com.snstudio.hyper.databinding.ActivityMainBinding
import com.snstudio.hyper.shared.MediaViewModel
import com.snstudio.hyper.shared.ProgressLiveData
import com.tingyik90.snackprogressbar.SnackProgressBar
import com.tingyik90.snackprogressbar.SnackProgressBarLayout
import com.tingyik90.snackprogressbar.SnackProgressBarManager
import dagger.hilt.android.AndroidEntryPoint

@AndroidEntryPoint
Expand All @@ -31,121 +35,170 @@ class MainActivity : AppCompatActivity() {
ActivityMainBinding.inflate(layoutInflater)
}

private var animator: ValueAnimator? = null
private var snackParentView: View? = null

private val snackProgressBarManager by lazy {
SnackProgressBarManager(
binding.root,
lifecycleOwner = this,
).apply {
setBackgroundColor(R.color.main_color)
setProgressBarColor(R.color.text_color)
setProgressTextColor(R.color.text_color)
setViewToMove(binding.playerView)
useRoundedCornerBackground(true)
setMessageMaxLines(1)
}
}

private val progressSnackBar by lazy {
SnackProgressBar(SnackProgressBar.TYPE_CIRCULAR, getString(R.string.downloading))
.setIsIndeterminate(false)
.setProgressMax(100)
.setAllowUserInput(true)
.setShowProgressPercentage(true)
}
private lateinit var navController: NavController
private lateinit var bottomSheetBehavior: BottomSheetBehavior<ConstraintLayout>
private var currentAnimation: ViewPropertyAnimator? = null

override fun onCreate(savedInstanceState: Bundle?) {
installSplashScreen()
super.onCreate(savedInstanceState)
setContentView(binding.root)
viewModel.createMusicFolder()
createMusicFolder()
initNavController()
setupSmoothBottomMenu()
initNavDestinationListener()
observeData()
initPlayerMenuButtonsListener()
initSnackDisplayListener()
setupBottomSheet()
}

override fun onDestroy() {
super.onDestroy()
mediaViewModel.releasePLayer()
}

private fun createMusicFolder() {
viewModel.createMusicFolder()
}

private fun setupBottomSheet() {
bottomSheetBehavior =
BottomSheetBehavior.from(binding.playerBottomSheet)

bottomSheetBehavior.apply {
isHideable = false
peekHeight = 0
state = BottomSheetBehavior.STATE_COLLAPSED
}

bottomSheetBehavior.addBottomSheetCallback(
object :
BottomSheetBehavior.BottomSheetCallback() {
@OptIn(UnstableApi::class)
override fun onStateChanged(
bottomSheet: View,
newState: Int,
) {
currentAnimation?.cancel()
if (newState == BottomSheetBehavior.STATE_EXPANDED) {
currentAnimation = binding.miniPlayerView.slideOutDown()
} else if (newState == BottomSheetBehavior.STATE_COLLAPSED) {
window.navigationBarColor = getColor(R.color.secondary_background_color)
currentAnimation = binding.miniPlayerView.slideInUp()
}
}

override fun onSlide(
bottomSheet: View,
slideOffset: Float,
) {
binding.miniPlayerView.alpha = 1 - slideOffset
}
},
)
}

@Deprecated("Deprecated in Java") // Todo
override fun onBackPressed() {
if (bottomSheetBehavior.state == BottomSheetBehavior.STATE_EXPANDED) {
bottomSheetBehavior.state = BottomSheetBehavior.STATE_COLLAPSED
} else {
super.onBackPressed()
}
}

private fun expandBottomSheet() {
with(binding) {
vm = mediaViewModel
lifecycleOwner = this@MainActivity
playerBottomSheet.apply {
visibility = View.VISIBLE
alpha = 0f
}

playerBottomSheet.animate()
.alpha(1f)
.setDuration(200)
.withStartAction {
bottomSheetBehavior.state = BottomSheetBehavior.STATE_EXPANDED
}.start()

miniPlayerView.animate()
.translationY(-miniPlayerView.height.toFloat())
.alpha(0f)
.setDuration(200)
.withEndAction {
miniPlayerView.translationY = 0f
miniPlayerView.alpha = 1f
}.start()
}
}

@OptIn(UnstableApi::class)
private fun observeData() {
with(mediaViewModel) {
observe(playerLiveData) { player ->
binding.playerView.player = player
}
observe(playbackStateLiveData) {
// binding.playerMenu.visible()
// moveSnack(380)
}
observe(showPlayerMenuLiveData) {
binding.playerMenu.isVisible = it
moveSnack(380)
}
observe(playerWhenReadyLiveData) { ready ->
if (ready) {
animator = binding.playerMenu.startColorAnimation()
with(binding) {
miniPlayerView.player = player
fullPlayerView.player = player
}
}
observe(ProgressLiveData.mediaDownloadStateLiveData) { state ->
when (state) {
is ProgressLiveData.DownloadState.Started -> {
snackProgressBarManager.show(
progressSnackBar,
SnackProgressBarManager.LENGTH_INDEFINITE,
)
progressSnackBar.setMessage(state.message)
snackProgressBarManager.updateTo(progressSnackBar)
}

is ProgressLiveData.DownloadState.InProgress -> {
snackProgressBarManager.setProgress(state.progress)
observe(progressLiveData) { value ->
binding.miniPlayerView.findViewById<ProgressBar>(R.id.progressCircular).apply {
progress = value
}
}
observe(metaDataLiveData) { metadata ->
with(binding) {
miniPlayerView.apply {
slideDownToggle(true)
findViewById<AppCompatImageView>(R.id.image).loadArtwork(metadata)
findViewById<MaterialTextView>(R.id.title).apply {
text = metadata.title
isSelected = true
}
}

is ProgressLiveData.DownloadState.Completed -> {
snackProgressBarManager.dismiss()
fullPlayerView.apply {
findViewById<MaterialTextView>(R.id.title).apply {
text = metadata.title
isSelected = true
}
findViewById<AppCompatImageView>(R.id.image).loadArtwork(metadata)
}

is ProgressLiveData.DownloadState.Failed -> {}
}
}
observe(showPlayerMenuLiveData) { visibility ->
if (visibility) binding.miniPlayerView.show() else binding.miniPlayerView.gone()
}
}
}

private fun initSnackDisplayListener() {
snackProgressBarManager.setOnDisplayListener(
object :
SnackProgressBarManager.OnDisplayListener {
override fun onLayoutInflated(
snackProgressBarLayout: SnackProgressBarLayout,
overlayLayout: FrameLayout,
snackProgressBar: SnackProgressBar,
onDisplayId: Int,
) {
snackParentView = snackProgressBarLayout.parent as View
if (binding.playerMenu.isVisible) {
moveSnack(380)
}
}
},
)
private fun initNavController() {
navController = findNavController(R.id.baseNavHost)
}

private fun moveSnack(bottom: Int) {
val params = snackParentView?.layoutParams as? ViewGroup.MarginLayoutParams
params?.setMargins(0, 0, 0, bottom)
snackParentView?.layoutParams = params
private fun setupSmoothBottomMenu() {
val popupMenu = PopupMenu(this, null)
popupMenu.inflate(R.menu.bottom_bar_menu)
val menu = popupMenu.menu
binding.bottomBar.setupWithNavController(menu, navController)
}

private fun initPlayerMenuButtonsListener() {
with(binding) {
btnClose.click {
mediaViewModel.stopPlayer()
playerMenu.gone()
moveSnack(50)
private fun initNavDestinationListener() {
navController.addOnDestinationChangedListener { _, destination, _ ->
when (destination.id) {
R.id.searchFragment, R.id.playlistDetail -> {
binding.bottomBar.slideDownToggle(false)
}

else -> binding.bottomBar.slideDownToggle(true)
}
}
}

private fun initPlayerMenuButtonsListener() {
binding.miniPlayerView.click {
window.navigationBarColor = getColor(R.color.background_color)
expandBottomSheet()
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ abstract class BaseDialog<VBinding : ViewBinding> : DialogFragment() {
binding = getViewBinding()
isCancelable = setCancelable
setStyle(STYLE_NO_TITLE, R.style.DialogTheme_transparent)
dialog?.window?.setBackgroundDrawableResource(R.drawable.dialog_rounded)
dialog?.window?.setBackgroundDrawableResource(R.drawable.bg_dialog_rounded)
}

override fun onCreateView(
Expand Down
13 changes: 9 additions & 4 deletions app/src/main/java/com/snstudio/hyper/core/base/BaseViewModel.kt
Original file line number Diff line number Diff line change
@@ -1,18 +1,23 @@
package com.snstudio.hyper.core.base

import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import io.flutter.plugin.common.MethodCall
import io.flutter.plugin.common.MethodChannel
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.asSharedFlow
import kotlinx.coroutines.launch

open class BaseViewModel(private val methodChannel: MethodChannel) : ViewModel() {
private val _receivedData: MutableLiveData<MethodCall> = MutableLiveData()
val receivedData: MutableLiveData<MethodCall> = _receivedData
private val _receivedData = MutableSharedFlow<MethodCall>()
val receivedData = _receivedData.asSharedFlow()

fun receivedData(vararg methodNames: String) {
methodChannel.setMethodCallHandler { call, result ->
if (methodNames.contains(call.method)) {
_receivedData.value = call
viewModelScope.launch {
_receivedData.emit(call)
}
result.success(null)
} else {
result.notImplemented()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ fun List<HashMap<String, String>>.toMediaList(type: MediaItemType): MutableList<
url = hashMap["url"] ?: "",
duration = hashMap["duration"]?.toLongOrNull() ?: 0L,
thumbnail = hashMap["thumbnail"] ?: "",
thumbnailMax = hashMap["thumbnailMax"] ?: "",
publishYear = hashMap["publishYear"],
uploadYear = hashMap["uploadYear"],
viewCount = hashMap["viewCount"] ?: "",
Expand Down
Loading

0 comments on commit 369b4b6

Please sign in to comment.