Fix banner

main
h4h13 2019-06-03 21:53:37 +05:30
parent 4d71252e76
commit ccde2d91ec
71 changed files with 3059 additions and 72 deletions

View File

@ -16,6 +16,8 @@ package code.name.monkey.retromusic
import code.name.monkey.retromusic.providers.RepositoryImpl
import code.name.monkey.retromusic.providers.interfaces.Repository
import code.name.monkey.retromusic.rest.KogouClient
import code.name.monkey.retromusic.rest.service.KuGouApiService
import code.name.monkey.retromusic.util.schedulers.BaseSchedulerProvider
import code.name.monkey.retromusic.util.schedulers.SchedulerProvider
@ -29,4 +31,7 @@ object Injection {
return SchedulerProvider.getInstance()
}
fun provideKuGouApiService(): KuGouApiService {
return KogouClient().apiService
}
}

View File

@ -23,10 +23,6 @@ import code.name.monkey.appthemehelper.util.MaterialValueHelper
import code.name.monkey.appthemehelper.util.TintHelper
import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper
import code.name.monkey.retromusic.R
import code.name.monkey.retromusic.activities.base.AbsSlidingMusicPanelActivity
import code.name.monkey.retromusic.adapter.album.AlbumAdapter
import code.name.monkey.retromusic.adapter.album.HorizontalAlbumAdapter
import code.name.monkey.retromusic.adapter.song.SimpleSongAdapter
import code.name.monkey.retromusic.dialogs.AddToPlaylistDialog
import code.name.monkey.retromusic.glide.GlideApp
import code.name.monkey.retromusic.glide.RetroGlideExtension
@ -38,6 +34,10 @@ import code.name.monkey.retromusic.mvp.contract.ArtistDetailContract
import code.name.monkey.retromusic.mvp.presenter.ArtistDetailsPresenter
import code.name.monkey.retromusic.rest.LastFMRestClient
import code.name.monkey.retromusic.rest.model.LastFmArtist
import code.name.monkey.retromusic.activities.base.AbsSlidingMusicPanelActivity
import code.name.monkey.retromusic.adapter.album.AlbumAdapter
import code.name.monkey.retromusic.adapter.album.HorizontalAlbumAdapter
import code.name.monkey.retromusic.adapter.song.SimpleSongAdapter
import code.name.monkey.retromusic.util.*
import com.google.android.material.appbar.AppBarLayout
import kotlinx.android.synthetic.main.activity_artist_content.*
@ -180,7 +180,7 @@ class ArtistDetailActivity : AbsSlidingMusicPanelActivity(), ArtistDetailContrac
super.onActivityResult(requestCode, resultCode, data)
when (requestCode) {
REQUEST_CODE_SELECT_IMAGE -> if (resultCode == Activity.RESULT_OK) {
CustomArtistImageUtil.getInstance(this).setCustomArtistImage(artist, data!!.data!!)
CustomArtistImageUtil.getInstance(this).setCustomArtistImage(artist!!, data!!.data!!)
}
else -> if (resultCode == Activity.RESULT_OK) {
reload()
@ -208,7 +208,10 @@ class ArtistDetailActivity : AbsSlidingMusicPanelActivity(), ArtistDetailContrac
}
private fun getArtist(): Artist {
return this.artist
if (artist == null) {
artist = Artist()
}
return this.artist!!
}
private fun setArtist(artist: Artist) {

View File

@ -216,7 +216,7 @@ class LyricsActivity : AbsMusicServiceActivity(), View.OnClickListener, ViewPage
return baseUrl
}
class PagerAdapter(fm: FragmentManager) : FragmentStatePagerAdapter(fm, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT) {
class PagerAdapter(fm: FragmentManager) : FragmentStatePagerAdapter(fm) {
class Tabs(@StringRes val title: Int,
val fragment: Fragment)

View File

@ -3,7 +3,6 @@ package code.name.monkey.retromusic.activities
import android.annotation.SuppressLint
import android.content.*
import android.content.pm.PackageManager
import android.os.Build
import android.os.Bundle
import android.provider.MediaStore
import android.util.Log
@ -85,12 +84,7 @@ class MainActivity : AbsSlidingMusicPanelActivity(), SharedPreferences.OnSharedP
private fun checkShowChangelog() {
try {
val pInfo = packageManager.getPackageInfo(packageName, 0)
val currentVersion = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
pInfo.longVersionCode.toInt()// avoid huge version numbers and you will be ok
} else {
//noinspection deprecation
pInfo.versionCode
}
val currentVersion = pInfo.versionCode
if (currentVersion != PreferenceUtil.getInstance().lastChangelogVersion) {
startActivityForResult(Intent(this, WhatsNewActivity::class.java), APP_INTRO_REQUEST)
}

View File

@ -0,0 +1,339 @@
package code.name.monkey.retromusic.activities
import android.app.Activity
import android.content.*
import android.content.res.ColorStateList
import android.graphics.Bitmap
import android.net.Uri
import android.os.Bundle
import android.provider.DocumentsContract
import android.provider.MediaStore
import android.provider.MediaStore.Images.Media.getBitmap
import android.text.TextUtils
import android.view.MenuItem
import android.widget.Toast
import androidx.core.content.FileProvider
import code.name.monkey.appthemehelper.ThemeStore
import code.name.monkey.appthemehelper.util.ColorUtil
import code.name.monkey.appthemehelper.util.MaterialUtil
import code.name.monkey.appthemehelper.util.MaterialValueHelper
import code.name.monkey.retromusic.App
import code.name.monkey.retromusic.Constants.USER_BANNER
import code.name.monkey.retromusic.Constants.USER_PROFILE
import code.name.monkey.retromusic.R
import code.name.monkey.retromusic.activities.base.AbsBaseActivity
import code.name.monkey.retromusic.extensions.applyToolbar
import code.name.monkey.retromusic.util.Compressor
import code.name.monkey.retromusic.util.ImageUtil.getResizedBitmap
import code.name.monkey.retromusic.util.PreferenceUtil
import com.afollestad.materialdialogs.MaterialDialog
import com.afollestad.materialdialogs.bottomsheets.BottomSheet
import com.afollestad.materialdialogs.list.listItems
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.disposables.CompositeDisposable
import io.reactivex.schedulers.Schedulers
import kotlinx.android.synthetic.main.activity_user_info.*
import java.io.File
import java.io.FileOutputStream
import java.io.IOException
class UserInfoActivity : AbsBaseActivity() {
private var disposable = CompositeDisposable()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_user_info)
setStatusbarColorAuto()
setNavigationbarColorAuto()
setTaskDescriptionColorAuto()
setLightNavigationBar(true)
setupToolbar()
MaterialUtil.setTint(nameContainer, false)
MaterialUtil.setTint(bioContainer, false)
name.setText(PreferenceUtil.getInstance().userName)
bio.setText(PreferenceUtil.getInstance().userBio)
if (!PreferenceUtil.getInstance().profileImage.isEmpty()) {
loadImageFromStorage(PreferenceUtil.getInstance().profileImage)
}
if (!PreferenceUtil.getInstance().bannerImage.isEmpty()) {
loadBannerFromStorage(PreferenceUtil.getInstance().bannerImage)
}
userImage.setOnClickListener {
MaterialDialog(this, BottomSheet()).show {
title(text = getString(R.string.set_photo))
listItems(items = listOf(getString(R.string.new_profile_photo), getString(R.string.remove_profile_photo))) { _, position, _ ->
when (position) {
0 -> pickNewPhoto()
1 -> PreferenceUtil.getInstance().saveProfileImage("")
}
}
}
}
bannerSelect.setOnClickListener {
showBannerOptions()
}
next.setOnClickListener {
val nameString = name.text.toString().trim { it <= ' ' }
if (TextUtils.isEmpty(nameString)) {
Toast.makeText(this, "Umm name is empty", Toast.LENGTH_SHORT).show()
return@setOnClickListener
}
val bioString = bio.text.toString().trim() { it <= ' ' }
if (TextUtils.isEmpty(bioString)) {
Toast.makeText(this, "Umm bio is empty", Toast.LENGTH_SHORT).show()
return@setOnClickListener
}
PreferenceUtil.getInstance().userName = nameString
PreferenceUtil.getInstance().userBio = bioString
setResult(Activity.RESULT_OK)
finish()
}
next.backgroundTintList = ColorStateList.valueOf(ThemeStore.accentColor(this))
ColorStateList.valueOf(MaterialValueHelper.getPrimaryTextColor(this, ColorUtil.isColorLight(ThemeStore.accentColor(this)))).apply {
next.setTextColor(this)
next.iconTint = this
}
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
if (item.itemId == android.R.id.home) {
onBackPressed()
}
return super.onOptionsItemSelected(item)
}
private fun setupToolbar() {
val primaryColor = ThemeStore.primaryColor(this)
applyToolbar(toolbar)
appBarLayout.setBackgroundColor(primaryColor)
}
private fun showBannerOptions() {
MaterialDialog(this, BottomSheet()).show {
title(R.string.select_banner_photo)
listItems(items = listOf(getString(R.string.new_banner_photo), getString(R.string.remove_banner_photo)))
{ _, position, _ ->
when (position) {
0 -> selectBannerImage()
1 -> PreferenceUtil.getInstance().setBannerImagePath("")
}
}
}
}
private fun selectBannerImage() {
if (PreferenceUtil.getInstance().bannerImage.isEmpty()) {
val pickImageIntent = Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI)
pickImageIntent.type = "image/*"
//pickImageIntent.putExtra("crop", "true")
pickImageIntent.putExtra("outputX", 1290)
pickImageIntent.putExtra("outputY", 720)
pickImageIntent.putExtra("aspectX", 16)
pickImageIntent.putExtra("aspectY", 9)
pickImageIntent.putExtra("scale", true)
//intent.setAction(Intent.ACTION_GET_CONTENT);
startActivityForResult(Intent.createChooser(pickImageIntent,
"Select Picture"), PICK_BANNER_REQUEST)
} else {
PreferenceUtil.getInstance().setBannerImagePath("")
bannerImage.setImageResource(android.R.color.transparent)
}
}
private fun pickNewPhoto() {
val pickImageIntent = Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI)
pickImageIntent.type = "image/*"
pickImageIntent.putExtra("crop", "true")
pickImageIntent.putExtra("outputX", 512)
pickImageIntent.putExtra("outputY", 512)
pickImageIntent.putExtra("aspectX", 1)
pickImageIntent.putExtra("aspectY", 1)
pickImageIntent.putExtra("scale", true)
startActivityForResult(Intent.createChooser(pickImageIntent, "Select Picture"), PICK_IMAGE_REQUEST)
}
public override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (resultCode == Activity.RESULT_OK && data != null) {
when (requestCode) {
PICK_IMAGE_REQUEST -> {
val uri = data.data
try {
val bitmap = getResizedBitmap(getBitmap(contentResolver, uri), PROFILE_ICON_SIZE)
val profileImagePath = saveToInternalStorage(bitmap, USER_PROFILE)
PreferenceUtil.getInstance().saveProfileImage(profileImagePath)
loadImageFromStorage(profileImagePath)
} catch (e: IOException) {
e.printStackTrace()
}
}
CROP_IMAGE_REQUEST -> {
val extras: Bundle = data.extras!!
val selectedBitmap: Bitmap = extras.getParcelable("data")
val profileImagePath = saveToInternalStorage(selectedBitmap, USER_PROFILE)
PreferenceUtil.getInstance().saveProfileImage(profileImagePath)
loadImageFromStorage(profileImagePath)
}
PICK_BANNER_REQUEST -> {
val uri = data.data
try {
val bitmap = getBitmap(contentResolver, uri)
val profileImagePath = saveToInternalStorage(bitmap, USER_BANNER)
PreferenceUtil.getInstance().setBannerImagePath(profileImagePath)
loadBannerFromStorage(profileImagePath)
} catch (e: IOException) {
e.printStackTrace()
}
}
CROP_BANNER_REQUEST -> {
val extras: Bundle = data.extras!!
val selectedBitmap: Bitmap = extras.getParcelable("data")
val profileImagePath = saveToInternalStorage(selectedBitmap, USER_BANNER)
PreferenceUtil.getInstance().saveProfileImage(profileImagePath)
loadImageFromStorage(profileImagePath)
}
}
}
}
private fun getImagePathFromUri(aUri: Uri?): String? {
var imagePath: String? = null
if (aUri == null) {
return imagePath
}
if (DocumentsContract.isDocumentUri(App.context, aUri)) {
val documentId = DocumentsContract.getDocumentId(aUri)
if ("com.android.providers.media.documents" == aUri.authority) {
val id = documentId.split(":".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()[1]
val selection = MediaStore.Images.Media._ID + "=" + id
imagePath = getImagePath(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, selection)
} else if ("com.android.providers.downloads.documents" == aUri.authority) {
val contentUri = ContentUris.withAppendedId(Uri.parse("content://downloads/public_downloads"),
java.lang.Long.valueOf(documentId))
imagePath = getImagePath(contentUri, null)
}
} else if ("content".equals(aUri.scheme!!, ignoreCase = true)) {
imagePath = getImagePath(aUri, null)
} else if ("file".equals(aUri.scheme!!, ignoreCase = true)) {
imagePath = aUri.path
}
return imagePath
}
private fun getImagePath(aUri: Uri, aSelection: String?): String? {
var path: String? = null
val cursor = App.context.contentResolver.query(aUri, null, aSelection, null, null)
if (cursor != null) {
if (cursor.moveToFirst()) {
path = cursor.getString(cursor.getColumnIndex(MediaStore.Images.Media.DATA))
}
cursor.close()
}
return path
}
private fun performBannerCrop(picturePath: Uri?) {
val photoUri = FileProvider.getUriForFile(this, "$packageName.provider", File(getImagePathFromUri(picturePath)))
try {
val cropIntent = Intent("com.android.camera.action.CROP")
cropIntent.setDataAndType(photoUri, "image/*")
cropIntent.putExtra("crop", "true")
cropIntent.putExtra("aspectX", 1)
cropIntent.putExtra("aspectY", 1)
cropIntent.putExtra("return-data", true)
cropIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
startActivityForResult(cropIntent, CROP_BANNER_REQUEST)
} catch (anfe: ActivityNotFoundException) {
val errorMessage = "your device doesn't support the crop action!"
Toast.makeText(this, errorMessage, Toast.LENGTH_SHORT).show()
}
}
private fun performCrop(imageUri: Uri) {
val photoUri = FileProvider.getUriForFile(this, "$packageName.provider", File(getImagePathFromUri(imageUri)))
try {
val cropIntent = Intent("com.android.camera.action.CROP")
cropIntent.setDataAndType(photoUri, "image/*")
cropIntent.putExtra("crop", "true")
cropIntent.putExtra("aspectX", 1)
cropIntent.putExtra("aspectY", 1)
cropIntent.putExtra("outputX", 280)
cropIntent.putExtra("outputY", 280)
cropIntent.putExtra("return-data", true)
cropIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
startActivityForResult(cropIntent, CROP_IMAGE_REQUEST)
} catch (anfe: ActivityNotFoundException) {
val errorMessage = "your device doesn't support the crop action!"
Toast.makeText(this, errorMessage, Toast.LENGTH_SHORT).show()
}
}
private fun loadBannerFromStorage(profileImagePath: String) {
disposable.add(Compressor(this)
.setQuality(100)
.setCompressFormat(Bitmap.CompressFormat.WEBP)
.compressToBitmapAsFlowable(File(profileImagePath, USER_BANNER))
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe { bitmap -> bannerImage.setImageBitmap(bitmap) })
}
private fun loadImageFromStorage(path: String) {
disposable.add(Compressor(this)
.setMaxHeight(300)
.setMaxWidth(300)
.setQuality(75)
.setCompressFormat(Bitmap.CompressFormat.WEBP)
.compressToBitmapAsFlowable(File(path, USER_PROFILE))
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe { bitmap -> userImage!!.setImageBitmap(bitmap) })
}
private fun saveToInternalStorage(bitmapImage: Bitmap, userBanner: String): String {
val cw = ContextWrapper(this)
val directory = cw.getDir("imageDir", Context.MODE_PRIVATE)
val myPath = File(directory, userBanner)
var fos: FileOutputStream? = null
try {
fos = FileOutputStream(myPath)
// Use the compress method on the BitMap object to write image to the OutputStream
bitmapImage.compress(Bitmap.CompressFormat.WEBP, 100, fos)
} catch (e: Exception) {
e.printStackTrace()
} finally {
try {
fos?.close()
} catch (e: IOException) {
e.printStackTrace()
}
}
return directory.absolutePath
}
companion object {
private const val PICK_IMAGE_REQUEST = 9002
private const val CROP_IMAGE_REQUEST = 9003
private const val PICK_BANNER_REQUEST = 9004
private const val CROP_BANNER_REQUEST = 9005
private const val PROFILE_ICON_SIZE = 400
}
}
fun Activity.pickImage(requestCode: Int) {
Intent(Intent.ACTION_GET_CONTENT).apply {
addCategory(Intent.CATEGORY_OPENABLE)
type = "image/*"
startActivityForResult(this, requestCode)
}
}

View File

@ -24,6 +24,7 @@ abstract class AbsThemeActivity : AbsCrashCollector(), Runnable {
setTheme(PreferenceUtil.getInstance().generalTheme)
hideStatusBar()
super.onCreate(savedInstanceState)
//MaterialDialogsUtil.updateMaterialDialogsThemeSingleton(this)
changeBackgroundShape()
setImmersiveFullscreen()

View File

@ -198,7 +198,7 @@ class AlbumTagEditorActivity : AbsTagEditorActivity(), TextWatcher {
val songs = AlbumLoader.getAlbum(this, id).blockingFirst().songs
val paths = ArrayList<String>(songs!!.size)
for (song in songs) {
paths.add(song.data)
paths.add(song.data!!)
}
return paths
}

View File

@ -0,0 +1,75 @@
package code.name.monkey.retromusic.adapter
import android.app.Activity
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
import android.widget.TextView
import androidx.recyclerview.widget.RecyclerView
import code.name.monkey.appthemehelper.ThemeStore
import code.name.monkey.appthemehelper.util.ColorUtil
import code.name.monkey.appthemehelper.util.MaterialValueHelper
import code.name.monkey.retromusic.R
import code.name.monkey.retromusic.glide.GlideApp
import code.name.monkey.retromusic.glide.RetroGlideExtension
import code.name.monkey.retromusic.glide.RetroMusicColoredTarget
import code.name.monkey.retromusic.helper.MusicPlayerRemote
import code.name.monkey.retromusic.model.Song
import code.name.monkey.retromusic.adapter.CollageSongAdapter.CollageSongViewHolder
import code.name.monkey.retromusic.adapter.base.MediaEntryViewHolder
import java.util.*
/**
* @author Hemanth S (h4h13).
*/
class CollageSongAdapter(private val activity: Activity, private val dataSet: ArrayList<Song>) : RecyclerView.Adapter<CollageSongViewHolder>() {
override fun onBindViewHolder(holder: CollageSongViewHolder, position: Int) {
holder.bindSongs()
if (dataSet.size > 8) {
for (i in 0 until dataSet.subList(0, 8).size) {
GlideApp.with(activity)
.asBitmapPalette()
.load(RetroGlideExtension.getSongModel(dataSet[i]))
.transition(RetroGlideExtension.getDefaultTransition())
.songOptions(dataSet[i])
.into(object : RetroMusicColoredTarget(holder.itemView.findViewById(holder.ids[i]) as ImageView) {
override fun onColorReady(color: Int) {
}
})
}
}
}
override fun getItemCount(): Int {
return 1
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): CollageSongViewHolder {
return CollageSongViewHolder(LayoutInflater.from(activity).inflate(R.layout.item_collage, parent, false))
}
inner class CollageSongViewHolder(itemView: View) : MediaEntryViewHolder(itemView) {
val ids = arrayListOf(R.id.image_2, R.id.image_3, R.id.image_4, R.id.image_5, R.id.image_6, R.id.image_7, R.id.image_8, R.id.image_9)
private var textView: TextView = itemView.findViewById(R.id.image_1)
fun bindSongs() {
for (i in ids) {
val imageView = itemView.findViewById<ImageView>(i)
imageView.setOnClickListener {
textView.setOnClickListener { MusicPlayerRemote.openQueue(dataSet, 0, true) }
}
}
val context = itemView.context
val color = ThemeStore.accentColor(context);
textView.setOnClickListener { MusicPlayerRemote.openQueue(dataSet, 0, true) }
textView.setBackgroundColor(color);
textView.setTextColor(MaterialValueHelper.getPrimaryTextColor(context, ColorUtil.isColorLight(color)))
}
}
}

View File

@ -25,11 +25,11 @@ class GenreAdapter(private val mActivity: Activity, dataSet: ArrayList<Genre>, p
this.dataSet = dataSet
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): GenreAdapter.ViewHolder {
return ViewHolder(LayoutInflater.from(mActivity).inflate(mItemLayoutRes, parent, false))
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
override fun onBindViewHolder(holder: GenreAdapter.ViewHolder, position: Int) {
val genre = dataSet[position]
if (holder.title != null) {
holder.title!!.text = genre.name

View File

@ -179,7 +179,7 @@ class SongFileAdapter(private val activity: AppCompatActivity, private var dataS
private const val FOLDER = 1
fun readableFileSize(size: Long): String {
if (size <= 0) return "$size B"
if (size <= 0) return size.toString() + " B"
val units = arrayOf("B", "KB", "MB", "GB", "TB")
val digitGroups = (Math.log10(size.toDouble()) / Math.log10(1024.0)).toInt()
return DecimalFormat("#,##0.##").format(size / Math.pow(1024.0, digitGroups.toDouble())) + " " + units[digitGroups]

View File

@ -0,0 +1,20 @@
package code.name.monkey.retromusic.adapter
import androidx.appcompat.app.AppCompatActivity
import androidx.recyclerview.widget.StaggeredGridLayoutManager
import code.name.monkey.retromusic.interfaces.CabHolder
import code.name.monkey.retromusic.model.Song
import code.name.monkey.retromusic.adapter.song.SongAdapter
import java.util.*
class SpanSongsAdapter(activity: AppCompatActivity, dataSet: ArrayList<Song>, itemLayoutRes: Int, usePalette: Boolean, cabHolder: CabHolder?) : SongAdapter(activity, dataSet, itemLayoutRes, usePalette, cabHolder) {
override fun onBindViewHolder(holder: SongAdapter.ViewHolder, position: Int) {
super.onBindViewHolder(holder, position)
if (position == 0) {
val params = StaggeredGridLayoutManager.LayoutParams(StaggeredGridLayoutManager.LayoutParams.WRAP_CONTENT, StaggeredGridLayoutManager.LayoutParams.MATCH_PARENT)
params.isFullSpan = true
holder.itemView.layoutParams = params
}
}
}

View File

@ -12,8 +12,6 @@ import code.name.monkey.appthemehelper.ThemeStore
import code.name.monkey.appthemehelper.util.ATHUtil
import code.name.monkey.appthemehelper.util.TintHelper
import code.name.monkey.retromusic.R
import code.name.monkey.retromusic.adapter.base.AbsMultiSelectAdapter
import code.name.monkey.retromusic.adapter.base.MediaEntryViewHolder
import code.name.monkey.retromusic.dialogs.ClearSmartPlaylistDialog
import code.name.monkey.retromusic.dialogs.DeletePlaylistDialog
import code.name.monkey.retromusic.helper.menu.PlaylistMenuHelper
@ -25,6 +23,8 @@ import code.name.monkey.retromusic.model.Playlist
import code.name.monkey.retromusic.model.Song
import code.name.monkey.retromusic.model.smartplaylist.AbsSmartPlaylist
import code.name.monkey.retromusic.model.smartplaylist.LastAddedPlaylist
import code.name.monkey.retromusic.adapter.base.AbsMultiSelectAdapter
import code.name.monkey.retromusic.adapter.base.MediaEntryViewHolder
import code.name.monkey.retromusic.util.MusicUtil
import code.name.monkey.retromusic.util.NavigationUtil
import java.util.*
@ -61,6 +61,17 @@ class PlaylistAdapter(protected val activity: AppCompatActivity, dataSet: ArrayL
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
/* if (getItemViewType(position) == SMART_PLAYLIST) {
if (holder.viewList != null) {
holder.viewList.get(0).setOnClickListener(
v -> NavigationUtil.goToPlaylistNew(activity, new HistoryPlaylist(activity)));
holder.viewList.get(1).setOnClickListener(
v -> NavigationUtil.goToPlaylistNew(activity, new LastAddedPlaylist(activity)));
holder.viewList.get(2).setOnClickListener(
v -> NavigationUtil.goToPlaylistNew(activity, new MyTopTracksPlaylist(activity)));
}
return;
}*/
val playlist = dataSet[position]
val songs = getSongs(playlist)
holder.itemView.isActivated = isChecked(playlist)

View File

@ -24,7 +24,7 @@ class OrderablePlaylistSongAdapter(activity: AppCompatActivity,
private val onMoveItemListener: OnMoveItemListener?) : PlaylistSongAdapter(activity, dataSet, itemLayoutRes, usePalette, cabHolder), DraggableItemAdapter<OrderablePlaylistSongAdapter.ViewHolder> {
init {
setMultiSelectMenuRes(R.menu.menu_playlists_songs_selection)
setMultiSelectMenuRes(code.name.monkey.retromusic.R.menu.menu_playlists_songs_selection)
}
override fun createViewHolder(view: View): SongAdapter.ViewHolder {
@ -92,7 +92,7 @@ class OrderablePlaylistSongAdapter(activity: AppCompatActivity,
private var mDragStateFlags: Int = 0
override var songMenuRes: Int
get() = R.menu.menu_item_playlist_song
get() = code.name.monkey.retromusic.R.menu.menu_item_playlist_song
set(value) {
super.songMenuRes = value
}
@ -109,7 +109,7 @@ class OrderablePlaylistSongAdapter(activity: AppCompatActivity,
override fun onSongMenuItemClick(item: MenuItem): Boolean {
when (item.itemId) {
R.id.action_remove_from_playlist -> {
code.name.monkey.retromusic.R.id.action_remove_from_playlist -> {
RemoveFromPlaylistDialog.create(song as PlaylistSong).show(activity.supportFragmentManager, "REMOVE_FROM_PLAYLIST")
return true
}

View File

@ -32,6 +32,7 @@ open class PlaylistSongAdapter(activity: AppCompatActivity, dataSet: ArrayList<S
holder.title!!.setTextColor(textColor)
}
if (holder.text != null) {
holder.text!!.visibility = View.GONE
}

View File

@ -22,7 +22,7 @@ class ShuffleButtonSongAdapter(activity: AppCompatActivity,
}
override fun onBindViewHolder(holder: SongAdapter.ViewHolder, position: Int) {
if (holder.itemViewType == OFFSET_ITEM) {
if (holder.itemViewType == AbsOffsetSongAdapter.OFFSET_ITEM) {
val accentColor = ThemeStore.accentColor(activity.applicationContext)
if (holder.title != null) {
holder.title!!.text = activity.resources.getString(R.string.action_shuffle_all)
@ -54,7 +54,7 @@ class ShuffleButtonSongAdapter(activity: AppCompatActivity,
inner class ViewHolder(itemView: View) : AbsOffsetSongAdapter.ViewHolder(itemView) {
override fun onClick(v: View?) {
if (itemViewType == OFFSET_ITEM) {
if (itemViewType == AbsOffsetSongAdapter.OFFSET_ITEM) {
MusicPlayerRemote.openAndShuffleQueue(dataSet, true)
return
}

View File

@ -10,9 +10,8 @@ import androidx.appcompat.app.AppCompatActivity
import androidx.core.util.Pair
import code.name.monkey.appthemehelper.util.ColorUtil
import code.name.monkey.appthemehelper.util.MaterialValueHelper
import code.name.monkey.retromusic.R
import code.name.monkey.retromusic.adapter.base.AbsMultiSelectAdapter
import code.name.monkey.retromusic.adapter.base.MediaEntryViewHolder
import code.name.monkey.retromusic.glide.GlideApp
import code.name.monkey.retromusic.glide.RetroGlideExtension
import code.name.monkey.retromusic.glide.RetroMusicColoredTarget
@ -22,6 +21,8 @@ import code.name.monkey.retromusic.helper.menu.SongMenuHelper
import code.name.monkey.retromusic.helper.menu.SongsMenuHelper
import code.name.monkey.retromusic.interfaces.CabHolder
import code.name.monkey.retromusic.model.Song
import code.name.monkey.retromusic.adapter.base.AbsMultiSelectAdapter
import code.name.monkey.retromusic.adapter.base.MediaEntryViewHolder
import code.name.monkey.retromusic.util.MusicUtil
import code.name.monkey.retromusic.util.NavigationUtil
import code.name.monkey.retromusic.util.PreferenceUtil

View File

@ -27,7 +27,7 @@ class SearchShortCutType(context: Context) : BaseShortcutType(context) {
companion object {
val id: String
get() = ID_PREFIX + "search"
get() = BaseShortcutType.ID_PREFIX + "search"
}
override val shortcutInfo: ShortcutInfo

View File

@ -37,6 +37,45 @@ import com.google.android.material.textfield.TextInputLayout
class RenamePlaylistDialog : DialogFragment() {
/*override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
return inflater.inflate(R.layout.dialog_playlist, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
MaterialUtil.setTint(actionNewPlaylistContainer, false)
val accentColor = ThemeStore.accentColor(context!!)
actionNewPlaylist.setHintTextColor(ColorStateList.valueOf(accentColor))
actionNewPlaylist.setTextColor(ThemeStore.textColorPrimary(context!!))
actionNewPlaylist.apply {
var playlistId: Long = 0
if (arguments != null) {
playlistId = arguments!!.getLong("playlist_id")
}
setText(PlaylistsUtil.getNameForPlaylist(activity!!, playlistId))
}
actionCancel.apply {
MaterialUtil.setTint(this, false)
setOnClickListener { dismiss() }
icon = ContextCompat.getDrawable(context, R.drawable.ic_close_white_24dp)
}
actionCreate.apply {
setText(R.string.action_rename)
setOnClickListener {
if (actionNewPlaylist.toString().trim { it <= ' ' } != "") {
val playlistId = arguments!!.getLong("playlist_id")
PlaylistsUtil.renamePlaylist(context!!, playlistId, actionNewPlaylist.text!!.toString())
}
}
MaterialUtil.setTint(this)
icon = ContextCompat.getDrawable(context, R.drawable.ic_edit_white_24dp)
}
}*/
private lateinit var playlistView: TextInputEditText
private lateinit var actionNewPlaylistContainer: TextInputLayout

View File

@ -18,6 +18,7 @@ import android.app.AlarmManager
import android.app.Dialog
import android.app.PendingIntent
import android.content.Context
import android.content.DialogInterface
import android.content.Intent
import android.os.Bundle
import android.os.CountDownTimer
@ -179,7 +180,77 @@ class SleepTimerDialog : DialogFragment() {
}
}
/* override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
return inflater.inflate(R.layout.dialog_sleep_timer, container, false)
}*/
private fun setProgressBarColor(dark: Int) {
ViewUtil.setProgressDrawable(progressSlider = seekBar, newColor = dark)
}
/*override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
MaterialUtil.setTint(actionCancel, false)
title.setTextColor(ThemeStore.textColorPrimary(context!!))
timerDisplay!!.setTextColor(ThemeStore.textColorSecondary(context!!))
timerUpdater = TimerUpdater()
seekArcProgress = PreferenceUtil.getInstance().lastSleepTimerValue
updateTimeDisplayTime()
seekBar.progress = seekArcProgress
setProgressBarColor(ThemeStore.accentColor(context!!))
seekBar.setOnSeekBarChangeListener(object : SeekBar.OnSeekBarChangeListener {
override fun onProgressChanged(seekBar: SeekBar, i: Int, b: Boolean) {
if (i < 1) {
seekBar.progress = 1
return
}
seekArcProgress = i
updateTimeDisplayTime()
}
override fun onStartTrackingTouch(seekBar: SeekBar) {
}
override fun onStopTrackingTouch(seekBar: SeekBar) {
PreferenceUtil.getInstance().lastSleepTimerValue = seekArcProgress
}
})
actionCancel.apply {
icon = ContextCompat.getDrawable(context, R.drawable.ic_close_white_24dp)
setOnClickListener {
val previous = makeTimerPendingIntent(PendingIntent.FLAG_NO_CREATE)
if (previous != null) {
val am = activity!!.getSystemService(Context.ALARM_SERVICE) as AlarmManager
am.cancel(previous)
previous.cancel()
Toast.makeText(activity, activity!!.resources.getString(R.string.sleep_timer_canceled), Toast.LENGTH_SHORT).show()
}
dismiss()
}
}
actionSet.apply {
icon = ContextCompat.getDrawable(context, R.drawable.ic_time_lapse_white_24dp)
MaterialUtil.setTint(actionSet)
setOnClickListener {
val minutes = seekArcProgress
val pi = makeTimerPendingIntent(PendingIntent.FLAG_CANCEL_CURRENT)
val nextSleepTimerElapsedTime = SystemClock.elapsedRealtime() + minutes * 60 * 1000
PreferenceUtil.getInstance().setNextSleepTimerElapsedRealtime(nextSleepTimerElapsedTime)
val am = activity!!.getSystemService(Context.ALARM_SERVICE) as AlarmManager
am.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, nextSleepTimerElapsedTime, pi)
Toast.makeText(activity, activity!!.resources.getString(R.string.sleep_timer_set, minutes), Toast.LENGTH_SHORT).show()
dismiss()
}
}
}*/
}

View File

@ -27,6 +27,42 @@ import com.afollestad.materialdialogs.list.listItems
class SongShareDialog : DialogFragment() {
/* override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
return inflater.inflate(R.layout.dialog_file_share, container, false)
}
@SuppressLint("StringFormatInvalid")
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val song: Song = arguments!!.getParcelable("song") ?: return
dialogTitle.setTextColor(ThemeStore.textColorPrimary(context!!))
audioText.apply {
text = getString(R.string.currently_listening_to_x_by_x, song.title, song.artistName)
setTextColor(ThemeStore.textColorSecondary(context!!))
setOnClickListener {
val currentlyListening = getString(code.name.monkey.retromusic.R.string.currently_listening_to_x_by_x, song.title, song.artistName)
activity!!.startActivity(Intent.createChooser(Intent().setAction(Intent.ACTION_SEND)
.putExtra(Intent.EXTRA_TEXT, currentlyListening)
.setType("text/plain"), null))
dismiss()
}
icon = ContextCompat.getDrawable(context, code.name.monkey.retromusic.R.drawable.ic_text_fields_black_24dp)
MaterialUtil.setTint(this)
}
audioFile.apply {
setTextColor(ThemeStore.textColorSecondary(context!!))
setOnClickListener {
activity!!.startActivity(Intent.createChooser(MusicUtil.createShareSongFileIntent(song, context), null))
dismiss()
}
icon = ContextCompat.getDrawable(context, code.name.monkey.retromusic.R.drawable.ic_share_white_24dp)
MaterialUtil.setTint(this, false)
}
}*/
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
val song: Song = arguments!!.getParcelable("song")
val currentlyListening: String = getString(R.string.currently_listening_to_x_by_x, song.title, song.artistName)

View File

@ -23,4 +23,6 @@ enum class NowPlayingScreen constructor(@param:StringRes @field:StringRes
TINY(R.string.tiny, R.drawable.np_tiny, 7),
SIMPLE(R.string.simple, R.drawable.np_simple, 8),
CLASSIC(R.string.classic, R.drawable.np_classic, 13);
//SLIDE(R.string.slide, R.drawable.np_slide, 13)
}

View File

@ -113,6 +113,10 @@ class VolumeFragment : Fragment(), SeekBar.OnSeekBarChangeListener, OnAudioVolum
ViewUtil.setProgressDrawable(volumeSeekBar, color, true)
}
fun removeThumb() {
volumeSeekBar.thumb = null
}
private fun setPauseWhenZeroVolume(pauseWhenZeroVolume: Boolean) {
if (PreferenceUtil.getInstance().pauseOnZeroVolume())
if (MusicPlayerRemote.isPlaying && pauseWhenZeroVolume) {
@ -125,6 +129,7 @@ class VolumeFragment : Fragment(), SeekBar.OnSeekBarChangeListener, OnAudioVolum
fun setTintableColor(color: Int) {
volumeDown.setColorFilter(color, PorterDuff.Mode.SRC_IN)
volumeUp.setColorFilter(color, PorterDuff.Mode.SRC_IN)
//TintHelper.setTintAuto(volumeSeekBar, color, false)
ViewUtil.setProgressDrawable(volumeSeekBar, color, true)
}

View File

@ -0,0 +1,91 @@
package code.name.monkey.retromusic.fragments.mainactivity
import android.os.Bundle
import android.view.Menu
import android.view.MenuInflater
import androidx.recyclerview.widget.LinearLayoutManager
import code.name.monkey.retromusic.R
import code.name.monkey.retromusic.model.Genre
import code.name.monkey.retromusic.mvp.contract.GenreContract
import code.name.monkey.retromusic.mvp.presenter.GenrePresenter
import code.name.monkey.retromusic.adapter.GenreAdapter
import code.name.monkey.retromusic.fragments.base.AbsLibraryPagerRecyclerViewFragment
import code.name.monkey.retromusic.util.PreferenceUtil
import java.util.*
class GenreFragment : AbsLibraryPagerRecyclerViewFragment<GenreAdapter, LinearLayoutManager>(), GenreContract.GenreView {
private var mPresenter: GenrePresenter? = null
override val emptyMessage: Int
get() = R.string.no_genres
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setHasOptionsMenu(true)
mPresenter = GenrePresenter(this)
}
override fun setMenuVisibility(menuVisible: Boolean) {
super.setMenuVisibility(menuVisible)
if (menuVisible) {
libraryFragment.setTitle(if (PreferenceUtil.getInstance().tabTitles()) R.string.library else R.string.genres)
}
}
override fun onResume() {
super.onResume()
libraryFragment.setTitle(if (PreferenceUtil.getInstance().tabTitles()) R.string.library else R.string.genres)
if (adapter!!.dataSet.isEmpty()) {
mPresenter!!.subscribe()
}
}
override fun onDestroy() {
super.onDestroy()
mPresenter!!.unsubscribe()
}
override fun createLayoutManager(): LinearLayoutManager {
return LinearLayoutManager(activity)
}
override fun createAdapter(): GenreAdapter {
val dataSet = adapter!!.dataSet
return GenreAdapter(libraryFragment.mainActivity, dataSet, R.layout.item_list)
}
override fun loading() {
}
override fun showData(list: ArrayList<Genre>) {
adapter!!.swapDataSet(list)
}
override fun showEmptyView() {
adapter!!.swapDataSet(ArrayList())
}
override fun completed() {
}
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
super.onCreateOptionsMenu(menu, inflater)
menu.removeItem(R.id.action_sort_order)
menu.removeItem(R.id.action_grid_size)
menu.removeItem(R.id.action_new_playlist)
}
companion object {
fun newInstance(): GenreFragment {
val args = Bundle()
val fragment = GenreFragment()
fragment.arguments = args
return fragment
}
}
}

View File

@ -148,7 +148,6 @@ public class LibraryFragment extends AbsMainActivityFragment implements CabHolde
appBarLayout.addOnOffsetChangedListener((appBarLayout, verticalOffset) ->
getMainActivity().setLightStatusbar(!ATHUtil.INSTANCE.isWindowBackgroundDark(getContext())));
getMainActivity().setSupportActionBar(toolbar);
}
private Fragment getCurrentFragment() {

View File

@ -2,13 +2,14 @@ package code.name.monkey.retromusic.fragments.mainactivity
import android.os.Bundle
import androidx.recyclerview.widget.GridLayoutManager
import code.name.monkey.retromusic.R
import code.name.monkey.retromusic.adapter.song.ShuffleButtonSongAdapter
import code.name.monkey.retromusic.adapter.song.SongAdapter
import code.name.monkey.retromusic.fragments.base.AbsLibraryPagerRecyclerViewCustomGridSizeFragment
import code.name.monkey.retromusic.model.Song
import code.name.monkey.retromusic.mvp.contract.SongContract
import code.name.monkey.retromusic.mvp.presenter.SongPresenter
import code.name.monkey.retromusic.adapter.song.ShuffleButtonSongAdapter
import code.name.monkey.retromusic.adapter.song.SongAdapter
import code.name.monkey.retromusic.fragments.base.AbsLibraryPagerRecyclerViewCustomGridSizeFragment
import code.name.monkey.retromusic.util.PreferenceUtil
import java.util.*
@ -127,7 +128,7 @@ class SongsFragment : AbsLibraryPagerRecyclerViewCustomGridSizeFragment<SongAdap
}
override fun setSortOrder(sortOrder: String) {
presenter.loadSongs()
presenter!!.loadSongs()
}
companion object {

View File

@ -721,6 +721,10 @@ public class FoldersFragment extends AbsMainActivityFragment implements
super(context);
}
public ListingFilesDialogAsyncTask(Context context, int showDelay) {
super(context, showDelay);
}
@Override
protected Dialog createDialog(@NonNull Context context) {
ProgressDialog dialog = new ProgressDialog(context);

View File

@ -1,6 +1,6 @@
package code.name.monkey.retromusic.fragments.mainactivity.home
import android.graphics.Color
import android.graphics.Bitmap
import android.os.Bundle
import android.util.DisplayMetrics
import android.view.*
@ -9,9 +9,11 @@ import androidx.recyclerview.widget.LinearLayoutManager
import code.name.monkey.appthemehelper.ThemeStore
import code.name.monkey.appthemehelper.common.ATHToolbarActivity
import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper
import code.name.monkey.retromusic.Constants.USER_BANNER
import code.name.monkey.retromusic.R
import code.name.monkey.retromusic.adapter.HomeAdapter
import code.name.monkey.retromusic.fragments.base.AbsMainActivityFragment
import code.name.monkey.retromusic.glide.GlideApp
import code.name.monkey.retromusic.helper.MusicPlayerRemote
import code.name.monkey.retromusic.interfaces.MainActivityFragmentCallbacks
import code.name.monkey.retromusic.loaders.SongLoader
@ -21,11 +23,19 @@ import code.name.monkey.retromusic.model.smartplaylist.LastAddedPlaylist
import code.name.monkey.retromusic.model.smartplaylist.MyTopTracksPlaylist
import code.name.monkey.retromusic.mvp.contract.HomeContract
import code.name.monkey.retromusic.mvp.presenter.HomePresenter
import code.name.monkey.retromusic.util.Compressor
import code.name.monkey.retromusic.util.NavigationUtil
import code.name.monkey.retromusic.util.PreferenceUtil
import code.name.monkey.retromusic.util.RetroUtil
import com.bumptech.glide.load.engine.DiskCacheStrategy
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.disposables.CompositeDisposable
import kotlinx.android.synthetic.main.fragment_home.*
import io.reactivex.schedulers.Schedulers
import kotlinx.android.synthetic.main.fragment_banner_home.*
import kotlinx.android.synthetic.main.fragment_home.recyclerView
import java.io.File
import java.util.*
import kotlin.collections.ArrayList
class BannerHomeFragment : AbsMainActivityFragment(), MainActivityFragmentCallbacks, HomeContract.HomeView {
override fun showEmpty() {
@ -42,7 +52,7 @@ class BannerHomeFragment : AbsMainActivityFragment(), MainActivityFragmentCallba
private lateinit var toolbar: Toolbar
override fun onCreateView(inflater: LayoutInflater, viewGroup: ViewGroup?, savedInstanceState: Bundle?): View? {
return inflater.inflate(R.layout.fragment_home, viewGroup, false)
return inflater.inflate(if (PreferenceUtil.getInstance().isHomeBanner) R.layout.fragment_banner_home else R.layout.fragment_home, viewGroup, false)
}
private val displayMetrics: DisplayMetrics
@ -110,7 +120,7 @@ class BannerHomeFragment : AbsMainActivityFragment(), MainActivityFragmentCallba
private fun setupToolbar() {
toolbar.apply {
setBackgroundColor(if (PreferenceUtil.getInstance().isHomeBanner) Color.TRANSPARENT else ThemeStore.primaryColor(context))
setBackgroundColor(ThemeStore.primaryColor(context))
setNavigationIcon(R.drawable.ic_menu_white_24dp)
setOnClickListener { showMainMenu() }
}
@ -124,6 +134,7 @@ class BannerHomeFragment : AbsMainActivityFragment(), MainActivityFragmentCallba
override fun onResume() {
super.onResume()
disposable = CompositeDisposable()
getTimeOfTheDay()
}
override fun onDestroyView() {
@ -186,6 +197,44 @@ class BannerHomeFragment : AbsMainActivityFragment(), MainActivityFragmentCallba
return super.onOptionsItemSelected(item)
}
private fun getTimeOfTheDay() {
val c = Calendar.getInstance()
val timeOfDay = c.get(Calendar.HOUR_OF_DAY)
var images = arrayOf<String>()
when (timeOfDay) {
in 0..5 -> images = resources.getStringArray(R.array.night)
in 6..11 -> images = resources.getStringArray(R.array.morning)
in 12..15 -> images = resources.getStringArray(R.array.after_noon)
in 16..19 -> images = resources.getStringArray(R.array.evening)
in 20..23 -> images = resources.getStringArray(R.array.night)
}
val day = images[Random().nextInt(images.size)]
loadTimeImage(day)
}
private fun loadTimeImage(day: String) {
if (bannerImage != null) {
if (PreferenceUtil.getInstance().bannerImage.isEmpty()) {
GlideApp.with(activity!!)
.load(day)
.placeholder(R.drawable.material_design_default)
.diskCacheStrategy(DiskCacheStrategy.ALL)
.into(bannerImage!!)
} else {
disposable.add(Compressor(context!!)
.setQuality(100)
.setCompressFormat(Bitmap.CompressFormat.WEBP)
.compressToBitmapAsFlowable(File(PreferenceUtil.getInstance().bannerImage, USER_BANNER))
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe { bannerImage!!.setImageBitmap(it) })
}
}
}
companion object {
const val TAG: String = "BannerHomeFragment"

View File

@ -19,8 +19,6 @@ import code.name.monkey.appthemehelper.util.ColorUtil
import code.name.monkey.appthemehelper.util.MaterialValueHelper
import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper
import code.name.monkey.retromusic.R
import code.name.monkey.retromusic.activities.LyricsActivity
import code.name.monkey.retromusic.fragments.base.AbsPlayerFragment
import code.name.monkey.retromusic.glide.GlideApp
import code.name.monkey.retromusic.glide.RetroGlideExtension
import code.name.monkey.retromusic.glide.RetroMusicColoredTarget
@ -28,6 +26,8 @@ import code.name.monkey.retromusic.glide.palette.BitmapPaletteWrapper
import code.name.monkey.retromusic.helper.MusicPlayerRemote
import code.name.monkey.retromusic.model.Song
import code.name.monkey.retromusic.model.lyrics.Lyrics
import code.name.monkey.retromusic.activities.LyricsActivity
import code.name.monkey.retromusic.fragments.base.AbsPlayerFragment
import code.name.monkey.retromusic.util.MusicUtil
import code.name.monkey.retromusic.util.RetroColorUtil
import code.name.monkey.retromusic.util.ViewUtil

View File

@ -101,6 +101,8 @@ class FlatPlayerFragment : AbsPlayerFragment(), PlayerAlbumCoverFragment.Callbac
callbacks!!.onPaletteColorChanged()
val isLight = ColorUtil.isColorLight(color)
//TransitionManager.beginDelayedTransition(mToolbar);
val iconColor = if (PreferenceUtil.getInstance().adaptiveColor)
MaterialValueHelper.getPrimaryTextColor(context!!, isLight)
else

View File

@ -12,10 +12,10 @@ import androidx.appcompat.widget.Toolbar
import code.name.monkey.appthemehelper.util.ATHUtil
import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper
import code.name.monkey.retromusic.R
import code.name.monkey.retromusic.fragments.base.AbsPlayerFragment
import code.name.monkey.retromusic.fragments.player.PlayerAlbumCoverFragment
import code.name.monkey.retromusic.helper.MusicPlayerRemote
import code.name.monkey.retromusic.model.Song
import code.name.monkey.retromusic.fragments.base.AbsPlayerFragment
import code.name.monkey.retromusic.fragments.player.PlayerAlbumCoverFragment
import code.name.monkey.retromusic.util.PreferenceUtil
import code.name.monkey.retromusic.util.ViewUtil
import code.name.monkey.retromusic.views.DrawableGradient
@ -101,8 +101,20 @@ class PlayerFragment : AbsPlayerFragment(), PlayerAlbumCoverFragment.Callbacks {
super.onViewCreated(view, savedInstanceState)
setUpSubFragments()
setUpPlayerToolbar()
snowfall.visibility = if (PreferenceUtil.getInstance().isSnowFall) View.VISIBLE else View.GONE
//val display = activity?.windowManager?.defaultDisplay
//val outMetrics = DisplayMetrics()
//display?.getMetrics(outMetrics)
//val density = resources.displayMetrics.density
//val dpWidth = outMetrics.widthPixels / density
//playerAlbumCoverContainer?.layoutParams?.height = RetroUtil.convertDpToPixel((dpWidth - getCutOff()), context!!).toInt()
}
private fun setUpSubFragments() {
playbackControlsFragment = childFragmentManager.findFragmentById(R.id.playbackControlsFragment) as PlayerPlaybackControlsFragment

View File

@ -0,0 +1,332 @@
/*
* Copyright (c) 2019 Hemanth Savarala.
*
* Licensed under the GNU General Public License v3
*
* This 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 software 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.
*/
package code.name.monkey.retromusic.fragments.player.slide
import android.animation.ObjectAnimator
import android.graphics.Color
import android.graphics.PorterDuff
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.view.animation.LinearInterpolator
import android.widget.SeekBar
import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.widget.Toolbar
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.LinearSmoothScroller
import androidx.recyclerview.widget.RecyclerView
import code.name.monkey.appthemehelper.ThemeStore
import code.name.monkey.appthemehelper.util.*
import code.name.monkey.retromusic.R
import code.name.monkey.retromusic.glide.GlideApp
import code.name.monkey.retromusic.glide.RetroGlideExtension
import code.name.monkey.retromusic.glide.RetroMusicColoredTarget
import code.name.monkey.retromusic.helper.MusicPlayerRemote
import code.name.monkey.retromusic.helper.MusicProgressViewUpdateHelper
import code.name.monkey.retromusic.helper.PlayPauseButtonOnClickHandler
import code.name.monkey.retromusic.misc.SimpleOnSeekbarChangeListener
import code.name.monkey.retromusic.model.Song
import code.name.monkey.retromusic.service.MusicService
import code.name.monkey.retromusic.activities.base.AbsSlidingMusicPanelActivity
import code.name.monkey.retromusic.adapter.song.SimpleSongAdapter
import code.name.monkey.retromusic.fragments.base.AbsPlayerControlsFragment
import code.name.monkey.retromusic.fragments.base.AbsPlayerFragment
import code.name.monkey.retromusic.util.MusicUtil
import code.name.monkey.retromusic.util.PreferenceUtil
import code.name.monkey.retromusic.util.ViewUtil
import kotlinx.android.synthetic.main.fragment_slide_player.*
/**
* Created by hemanths on 3/15/19
*/
class SlidePlayerFragment : AbsPlayerFragment(), MusicProgressViewUpdateHelper.Callback {
private var lastColor: Int = 0
override val paletteColor: Int
get() = lastColor
override fun playerToolbar(): Toolbar {
return playerToolbar
}
override fun onShow() {
}
override fun onHide() {
}
override fun onBackPressed(): Boolean {
return false
}
override fun toolbarIconColor(): Int {
return Color.WHITE
}
override fun onColorChanged(color: Int) {
}
override fun onFavoriteToggled() {
toggleFavorite(MusicPlayerRemote.currentSong)
}
override fun toggleFavorite(song: Song) {
super.toggleFavorite(song)
if (song.id == MusicPlayerRemote.currentSong.id) {
updateIsFavorite()
}
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
return inflater.inflate(R.layout.fragment_slide_player, container, false)
}
override fun onResume() {
super.onResume()
progressViewUpdateHelper.start()
}
override fun onPause() {
super.onPause()
progressViewUpdateHelper.stop()
}
override fun onPlayingMetaChanged() {
super.onPlayingMetaChanged()
updateSong()
updateIsFavorite()
}
override fun onQueueChanged() {
super.onQueueChanged()
updateQueue()
}
override fun onServiceConnected() {
updatePlayPauseDrawableState()
updateRepeatState()
updateShuffleState()
updateSong()
updateIsFavorite()
updateQueue()
}
private fun updateQueue() {
songAdapter.swapDataSet(MusicPlayerRemote.playingQueue)
}
private fun updatePlayPauseDrawableState() {
if (MusicPlayerRemote.isPlaying) {
albumCoverContainer.cardElevation = 24.0f
playPauseButton.setImageResource(R.drawable.ic_pause_white_24dp)
} else {
albumCoverContainer.cardElevation = 0.0f
playPauseButton.setImageResource(R.drawable.ic_play_arrow_white_24dp)
}
}
fun updateRepeatState() {
when (MusicPlayerRemote.repeatMode) {
MusicService.REPEAT_MODE_NONE -> {
repeatButton.setImageResource(R.drawable.ic_repeat_white_24dp)
repeatButton.setColorFilter(lastDisabledPlaybackControlsColor, PorterDuff.Mode.SRC_IN)
}
MusicService.REPEAT_MODE_ALL -> {
repeatButton.setImageResource(R.drawable.ic_repeat_white_24dp)
repeatButton.setColorFilter(lastPlaybackControlsColor, PorterDuff.Mode.SRC_IN)
}
MusicService.REPEAT_MODE_THIS -> {
repeatButton.setImageResource(R.drawable.ic_repeat_one_white_24dp)
repeatButton.setColorFilter(lastPlaybackControlsColor, PorterDuff.Mode.SRC_IN)
}
}
}
fun updateShuffleState() {
when (MusicPlayerRemote.shuffleMode) {
MusicService.SHUFFLE_MODE_SHUFFLE -> shuffleButton.setColorFilter(lastPlaybackControlsColor, PorterDuff.Mode.SRC_IN)
else -> shuffleButton.setColorFilter(lastDisabledPlaybackControlsColor, PorterDuff.Mode.SRC_IN)
}
}
private fun updateSong() {
val song = MusicPlayerRemote.currentSong
title.text = song.title
text.text = song.artistName
GlideApp.with(activity!!).asBitmapPalette()
.load(RetroGlideExtension.getSongModel(song))
.songOptions(song)
.transition(RetroGlideExtension.getDefaultTransition())
.into(object : RetroMusicColoredTarget(playerImage) {
override fun onColorReady(color: Int) {
setColor(color)
}
})
}
private fun setColor(color: Int) {
lastColor = color
val colorBg = ATHUtil.resolveColor(context!!, android.R.attr.colorBackground)
if (ColorUtil.isColorLight(colorBg)) {
lastPlaybackControlsColor = MaterialValueHelper.getSecondaryTextColor(context!!, true)
lastDisabledPlaybackControlsColor = MaterialValueHelper.getSecondaryDisabledTextColor(context!!, true)
} else {
lastPlaybackControlsColor = MaterialValueHelper.getPrimaryTextColor(context!!, false)
lastDisabledPlaybackControlsColor = MaterialValueHelper.getPrimaryDisabledTextColor(context!!, false)
}
val colorFinal = if (PreferenceUtil.getInstance().adaptiveColor) {
color
} else {
ThemeStore.accentColor(context!!)
}
text.setTextColor(colorFinal)
playerQueueSubHeader.setTextColor(colorFinal)
TintHelper.setTintAuto(playPauseButton, lastPlaybackControlsColor, false)
ViewUtil.setProgressDrawable(progressSlider, colorFinal)
updateRepeatState()
updateShuffleState()
updatePrevNextColor()
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
setUpMusicControllers()
setUpPlayerToolbar()
(activity as AbsSlidingMusicPanelActivity).setAntiDragView(recyclerView)
playerQueueSubHeader.setTextColor(ThemeStore.accentColor(context!!))
}
private fun setUpMusicControllers() {
setUpPlayPauseFab()
setUpPrevNext()
setUpRepeatButton()
setUpShuffleButton()
setUpProgressSlider()
setUpRecyclerView()
}
private lateinit var songAdapter: SimpleSongAdapter
private fun setUpRecyclerView() {
songAdapter = SimpleSongAdapter(context = activity as AppCompatActivity,
songs = ArrayList(), i = R.layout.item_song, useNumbers = true)
recyclerView.apply {
adapter = songAdapter
layoutManager = LinearLayoutManager(context)
}
}
private fun setUpProgressSlider() {
progressSlider.setOnSeekBarChangeListener(object : SimpleOnSeekbarChangeListener() {
override fun onProgressChanged(seekBar: SeekBar, progress: Int, fromUser: Boolean) {
if (fromUser) {
MusicPlayerRemote.seekTo(progress)
onUpdateProgressViews(MusicPlayerRemote.songProgressMillis, MusicPlayerRemote.songDurationMillis)
}
}
})
}
override fun onUpdateProgressViews(progress: Int, total: Int) {
progressSlider.max = total
val animator = ObjectAnimator.ofInt(progressSlider, "progress", progress)
animator.duration = AbsPlayerControlsFragment.SLIDER_ANIMATION_TIME
animator.interpolator = LinearInterpolator()
animator.start()
songTotalTime.text = MusicUtil.getReadableDurationString(total.toLong())
songCurrentProgress.text = MusicUtil.getReadableDurationString(progress.toLong())
}
private fun setUpPlayPauseFab() {
playPauseButton.setOnClickListener(PlayPauseButtonOnClickHandler())
}
private fun setUpRepeatButton() {
repeatButton.setOnClickListener { MusicPlayerRemote.cycleRepeatMode() }
}
private fun setUpShuffleButton() {
shuffleButton.setOnClickListener { MusicPlayerRemote.toggleShuffleMode() }
}
private fun setUpPrevNext() {
updatePrevNextColor()
nextButton.setOnClickListener { MusicPlayerRemote.playNextSong() }
previousButton.setOnClickListener { MusicPlayerRemote.back() }
}
private fun updatePrevNextColor() {
nextButton.setColorFilter(lastPlaybackControlsColor, PorterDuff.Mode.SRC_IN)
previousButton.setColorFilter(lastPlaybackControlsColor, PorterDuff.Mode.SRC_IN)
}
private var lastPlaybackControlsColor: Int = 0
private var lastDisabledPlaybackControlsColor: Int = 0
private lateinit var progressViewUpdateHelper: MusicProgressViewUpdateHelper
override fun onPlayStateChanged() {
updatePlayPauseDrawableState()
}
override fun onRepeatModeChanged() {
updateRepeatState()
}
override fun onShuffleModeChanged() {
updateShuffleState()
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
progressViewUpdateHelper = MusicProgressViewUpdateHelper(this)
}
private fun setUpPlayerToolbar() {
playerToolbar.inflateMenu(R.menu.menu_player)
playerToolbar.setNavigationOnClickListener { activity!!.onBackPressed() }
playerToolbar.setOnMenuItemClickListener(this)
ToolbarContentTintHelper.colorizeToolbar(playerToolbar, Color.WHITE, activity)
}
fun RecyclerView.smoothSnapToPosition(position: Int, snapMode: Int = LinearSmoothScroller.SNAP_TO_START) {
val smoothScroller = object : LinearSmoothScroller(this.context) {
override fun getVerticalSnapPreference(): Int {
return snapMode
}
override fun getHorizontalSnapPreference(): Int {
return snapMode
}
}
smoothScroller.targetPosition = position
this.layoutManager?.startSmoothScroll(smoothScroller)
}
}

View File

@ -8,9 +8,9 @@ import android.view.ViewGroup
import code.name.monkey.appthemehelper.util.ColorUtil
import code.name.monkey.appthemehelper.util.MaterialValueHelper
import code.name.monkey.retromusic.R
import code.name.monkey.retromusic.fragments.base.AbsPlayerControlsFragment
import code.name.monkey.retromusic.helper.MusicPlayerRemote
import code.name.monkey.retromusic.service.MusicService
import code.name.monkey.retromusic.fragments.base.AbsPlayerControlsFragment
import kotlinx.android.synthetic.main.fragment_tiny_controls_fragment.*
class TinyPlaybackControlsFragment : AbsPlayerControlsFragment() {

View File

@ -14,13 +14,13 @@ import code.name.monkey.appthemehelper.util.MaterialValueHelper
import code.name.monkey.appthemehelper.util.TintHelper
import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper
import code.name.monkey.retromusic.R
import code.name.monkey.retromusic.fragments.MiniPlayerFragment
import code.name.monkey.retromusic.fragments.base.AbsPlayerFragment
import code.name.monkey.retromusic.fragments.player.PlayerAlbumCoverFragment
import code.name.monkey.retromusic.helper.MusicPlayerRemote
import code.name.monkey.retromusic.helper.MusicProgressViewUpdateHelper
import code.name.monkey.retromusic.helper.PlayPauseButtonOnClickHandler
import code.name.monkey.retromusic.model.Song
import code.name.monkey.retromusic.fragments.MiniPlayerFragment
import code.name.monkey.retromusic.fragments.base.AbsPlayerFragment
import code.name.monkey.retromusic.fragments.player.PlayerAlbumCoverFragment
import code.name.monkey.retromusic.util.MusicUtil
import code.name.monkey.retromusic.util.PreferenceUtil
import kotlinx.android.synthetic.main.fragment_tiny_player.*

View File

@ -20,4 +20,6 @@ package code.name.monkey.retromusic.interfaces
interface MainActivityFragmentCallbacks {
fun handleBackPress(): Boolean
//void selectedFragment(Fragment fragment);
}

View File

@ -102,6 +102,7 @@ open class AlbumLoader {
private fun getSongLoaderSortOrder(): String {
return PreferenceUtil.getInstance().albumSortOrder + ", " +
//PreferenceUtil.getInstance().getAlbumSongSortOrder() + "," +
PreferenceUtil.getInstance().albumDetailSongSortOrder
}
}

View File

@ -16,10 +16,6 @@ package code.name.monkey.retromusic.misc;
import android.os.Bundle;
import android.os.Parcelable;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
@ -27,6 +23,10 @@ import androidx.fragment.app.FragmentPagerAdapter;
import androidx.fragment.app.FragmentTransaction;
import androidx.viewpager.widget.PagerAdapter;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import java.util.ArrayList;
/**

View File

@ -0,0 +1,38 @@
/*
* Copyright (c) 2019 Hemanth Savarala.
*
* Licensed under the GNU General Public License v3
*
* This 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 software 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.
*/
package code.name.monkey.retromusic.misc
import io.reactivex.disposables.CompositeDisposable
import io.reactivex.disposables.Disposable
object DisposableManager {
private var compositeDisposable: CompositeDisposable? = null
fun add(disposable: Disposable) {
getCompositeDisposable().add(disposable)
}
fun dispose() {
getCompositeDisposable().dispose()
}
private fun getCompositeDisposable(): CompositeDisposable {
if (compositeDisposable == null || compositeDisposable!!.isDisposed) {
compositeDisposable = CompositeDisposable()
}
return compositeDisposable!!
}
}

View File

@ -0,0 +1,32 @@
/*
* Copyright (c) 2019 Hemanth Savarala.
*
* Licensed under the GNU General Public License v3
*
* This 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 software 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.
*/
package code.name.monkey.retromusic.misc
import androidx.annotation.CallSuper
import io.reactivex.Observer
import io.reactivex.disposables.Disposable
class DisposingObserver<T> : Observer<T> {
@CallSuper
override fun onSubscribe(d: Disposable) {
DisposableManager.add(d)
}
override fun onNext(next: T) {}
override fun onError(e: Throwable) {}
override fun onComplete() {}
}

View File

@ -0,0 +1,90 @@
/*
* Copyright (c) 2019 Hemanth Savarala.
*
* Licensed under the GNU General Public License v3
*
* This 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 software 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.
*/
package code.name.monkey.retromusic.misc;
import android.content.Context;
import android.os.Handler;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
import android.view.animation.LinearInterpolator;
import com.google.android.material.floatingactionbutton.FloatingActionButton;
import androidx.annotation.NonNull;
import androidx.coordinatorlayout.widget.CoordinatorLayout;
import androidx.core.view.ViewCompat;
/*Don't delete even if its not showing not using*/
public class ScrollAwareFABBehavior extends CoordinatorLayout.Behavior<FloatingActionButton> {
private static final String TAG = "ScrollingFABBehavior";
Handler mHandler;
public ScrollAwareFABBehavior(Context context, AttributeSet attrs) {
super();
}
@Override
public void onStopNestedScroll(@NonNull CoordinatorLayout coordinatorLayout,
@NonNull FloatingActionButton child,
@NonNull View target) {
super.onStopNestedScroll(coordinatorLayout, child, target);
if (mHandler == null)
mHandler = new Handler();
mHandler.postDelayed(() -> {
child.animate().translationY(0).setInterpolator(new LinearInterpolator()).start();
Log.d("FabAnim", "startHandler()");
}, 1000);
}
@Override
public void onNestedScroll(@NonNull CoordinatorLayout coordinatorLayout,
@NonNull FloatingActionButton child,
@NonNull View target,
int dxConsumed,
int dyConsumed,
int dxUnconsumed,
int dyUnconsumed) {
super.onNestedScroll(coordinatorLayout, child, target, dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed);
//child -> Floating Action Button
if (dyConsumed > 0) {
Log.d("Scrolling", "Up");
CoordinatorLayout.LayoutParams layoutParams = (CoordinatorLayout.LayoutParams) child.getLayoutParams();
int fab_bottomMargin = layoutParams.bottomMargin;
child.animate().translationY(child.getHeight() + fab_bottomMargin).setInterpolator(new LinearInterpolator()).start();
} else if (dyConsumed < 0) {
Log.d("Scrolling", "down");
child.animate().translationY(0).setInterpolator(new LinearInterpolator()).start();
}
}
@Override
public boolean onStartNestedScroll(@NonNull CoordinatorLayout coordinatorLayout,
@NonNull FloatingActionButton child,
@NonNull View directTargetChild,
@NonNull View target,
int nestedScrollAxes) {
if (mHandler != null) {
mHandler.removeMessages(0);
Log.d("Scrolling", "stopHandler()");
}
return nestedScrollAxes == ViewCompat.SCROLL_AXIS_VERTICAL;
}
}

View File

@ -0,0 +1,36 @@
/*
* Copyright (c) 2019 Hemanth Savarala.
*
* Licensed under the GNU General Public License v3
*
* This 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 software 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.
*/
package code.name.monkey.retromusic.misc
import android.animation.Animator
abstract class SimpleAnimatorListener : Animator.AnimatorListener {
override fun onAnimationStart(animation: Animator) {
}
override fun onAnimationEnd(animation: Animator) {
}
override fun onAnimationCancel(animation: Animator) {
}
override fun onAnimationRepeat(animation: Animator) {
}
}

View File

@ -0,0 +1,35 @@
/*
* Copyright (c) 2019 Hemanth Savarala.
*
* Licensed under the GNU General Public License v3
*
* This 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 software 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.
*/
package code.name.monkey.retromusic.misc;
import android.graphics.Rect;
import android.view.View;
import androidx.recyclerview.widget.RecyclerView;
public class SpacesItemDecoration extends RecyclerView.ItemDecoration {
private int space;
public SpacesItemDecoration(int space) {
this.space = space;
}
@Override
public void getItemOffsets(Rect outRect, View view,
RecyclerView parent, RecyclerView.State state) {
outRect.right = space;
outRect.bottom = space;
}
}

View File

@ -18,9 +18,6 @@ import code.name.monkey.retromusic.model.Album
import code.name.monkey.retromusic.mvp.BasePresenter
import code.name.monkey.retromusic.mvp.BaseView
import java.util.ArrayList
/**
* Created by hemanths on 16/08/17.
*/
interface AlbumContract {

View File

@ -19,10 +19,6 @@ import code.name.monkey.retromusic.model.Album
import code.name.monkey.retromusic.mvp.BasePresenter
import code.name.monkey.retromusic.mvp.BaseView
/**
* Created by hemanths on 16/08/17.
*/
interface AlbumDetailsContract {
interface AlbumDetailsView : BaseView<Album>

View File

@ -17,9 +17,6 @@ package code.name.monkey.retromusic.mvp.contract
import code.name.monkey.retromusic.model.Home
import code.name.monkey.retromusic.mvp.BasePresenter
import code.name.monkey.retromusic.mvp.BaseView
/**
* Created by hemanths on 16/08/17.
*/
interface HomeContract {

View File

@ -19,10 +19,6 @@ import code.name.monkey.retromusic.mvp.Presenter
import code.name.monkey.retromusic.mvp.contract.ArtistContract
import java.util.*
/**
* Created by hemanths on 16/08/17.
*/
class ArtistPresenter(private val mView: ArtistContract.ArtistView) : Presenter(), ArtistContract.Presenter {
override fun subscribe() {

View File

@ -0,0 +1,82 @@
/*
* Copyright (c) 2019 Hemanth Savarala.
*
* Licensed under the GNU General Public License v3
*
* This 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 software 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.
*/
package code.name.monkey.retromusic.providers
import android.content.ContentValues
import android.content.Context
import android.database.sqlite.SQLiteDatabase
import android.database.sqlite.SQLiteOpenHelper
import code.name.monkey.retromusic.loaders.SongLoader
import code.name.monkey.retromusic.model.Song
import io.reactivex.schedulers.Schedulers
class NotPlayedStore(val context: Context) : SQLiteOpenHelper(context, DATABASE_NAME, null, VERSION) {
private val dataBaseCreate = "CREATE TABLE IF NOT EXISTS $NAME ( $ID LONG PRIMARY KEY )"
override fun onCreate(db: SQLiteDatabase) {
db.execSQL(dataBaseCreate)
}
override fun onUpgrade(db: SQLiteDatabase, oldVersion: Int, newVersion: Int) {
db.execSQL("DROP TABLE IF EXISTS $NAME")
}
fun removeSong(id: Long) {
val db = writableDatabase
db.apply {
beginTransaction()
delete(NAME, "$ID = $id", null)
setTransactionSuccessful()
endTransaction()
close()
}
}
fun addAllSongs(songs: ArrayList<Song>) {
SongLoader.getAllSongs(context)
.map {
val database = writableDatabase;
database.apply {
val contentValues = ContentValues()
for (song in songs) {
contentValues.put(ID, song.id)
insert(NAME, null, contentValues)
}
setTransactionSuccessful()
endTransaction()
}
return@map true
}
.subscribeOn(Schedulers.io())
.subscribe()
}
companion object {
const val NAME = "not_played_songs"
const val ID = "song_id"
const val DATABASE_NAME = "not_played.db"
private const val VERSION = 1
private var sInstance: NotPlayedStore? = null
@Synchronized
fun getInstance(context: Context): NotPlayedStore {
if (sInstance == null) {
sInstance = NotPlayedStore(context.applicationContext)
}
return sInstance!!
}
}
}

View File

@ -0,0 +1,89 @@
/*
* Copyright (c) 2019 Hemanth Savarala.
*
* Licensed under the GNU General Public License v3
*
* This 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 software 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.
*/
package code.name.monkey.retromusic.rest;
import android.content.Context;
import java.io.File;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import code.name.monkey.retromusic.App;
import code.name.monkey.retromusic.rest.service.KuGouApiService;
import okhttp3.Cache;
import okhttp3.Call;
import okhttp3.Interceptor;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import retrofit2.Retrofit;
import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory;
import retrofit2.converter.gson.GsonConverterFactory;
import static code.name.monkey.retromusic.Constants.BASE_API_URL_KUGOU;
/**
* Created by hemanths on 23/08/17.
*/
public class KogouClient {
private static final String BASE_URL = BASE_API_URL_KUGOU;
private KuGouApiService apiService;
public KogouClient() {
this(createDefaultOkHttpClientBuilder().build());
}
private KogouClient(@NonNull Call.Factory client) {
Retrofit restAdapter = new Retrofit.Builder()
.baseUrl(BASE_URL)
.callFactory(client)
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.build();
apiService = restAdapter.create(KuGouApiService.class);
}
@Nullable
private static Cache createDefaultCache(Context context) {
File cacheDir = new File(context.getCacheDir().getAbsolutePath(), "/okhttp-lastfm/");
if (cacheDir.mkdirs() || cacheDir.isDirectory()) {
return new Cache(cacheDir, 1024 * 1024 * 10);
}
return null;
}
private static Interceptor createCacheControlInterceptor() {
return chain -> {
Request modifiedRequest = chain.request().newBuilder()
.addHeader("Cache-Control", String.format("max-age=%d, max-stale=%d", 31536000, 31536000))
.build();
return chain.proceed(modifiedRequest);
};
}
private static OkHttpClient.Builder createDefaultOkHttpClientBuilder() {
return new OkHttpClient.Builder()
.cache(createDefaultCache(App.Companion.getInstance()))
.addInterceptor(createCacheControlInterceptor());
}
public KuGouApiService getApiService() {
return apiService;
}
}

View File

@ -0,0 +1,55 @@
/*
* Copyright (c) 2019 Hemanth Savarala.
*
* Licensed under the GNU General Public License v3
*
* This 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 software 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.
*/
package code.name.monkey.retromusic.rest.model;
import com.google.gson.annotations.SerializedName;
/**
* Created by hefuyi on 2017/1/20.
*/
public class KuGouRawLyric {
private static final String CHARSET = "charset";
private static final String CONTENT = "content";
private static final String FMT = "fmt";
private static final String INFO = "info";
private static final String STATUS = "status";
@SerializedName(CHARSET)
public String charset;
@SerializedName(CONTENT)
public String content;
@SerializedName(FMT)
public String fmt;
@SerializedName(INFO)
public String info;
@SerializedName(STATUS)
public int status;
@Override
public String toString() {
return "KuGouRawLyric{" +
"charset='" + charset + '\'' +
", content='" + content + '\'' +
", fmt='" + fmt + '\'' +
", info='" + info + '\'' +
", status=" + status +
'}';
}
}

View File

@ -0,0 +1,110 @@
/*
* Copyright (c) 2019 Hemanth Savarala.
*
* Licensed under the GNU General Public License v3
*
* This 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 software 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.
*/
package code.name.monkey.retromusic.rest.model;
import com.google.gson.annotations.SerializedName;
import java.util.List;
import androidx.annotation.NonNull;
/**
* Created by hefuyi on 2017/1/20.
*/
public class KuGouSearchLyricResult {
private static final String INFO = "info";
private static final String STATUS = "status";
private static final String PROPOSAL = "proposal";
private static final String KEYWORD = "keyword";
private static final String CANDIDATES = "candidates";
@NonNull
@SerializedName(INFO)
public String info;
@SerializedName(STATUS)
public int status;
@NonNull
@SerializedName(PROPOSAL)
public String proposal;
@NonNull
@SerializedName(KEYWORD)
public String keyword;
@NonNull
@SerializedName(CANDIDATES)
public List<Candidates> candidates;
@Override
public String toString() {
return "KuGouSearchLyricResult{" +
"info='" + info + '\'' +
", status=" + status +
", proposal='" + proposal + '\'' +
", keyword='" + keyword + '\'' +
", candidates=" + candidates +
'}';
}
public static class Candidates {
private static final String NICKNAME = "nickname";
private static final String ACCESSKEY = "accesskey";
private static final String SCORE = "score";
private static final String DURATION = "duration";
private static final String UID = "uid";
private static final String SONG = "song";
private static final String ID = "id";
private static final String SINGER = "singer";
private static final String LANGUAGE = "language";
@SerializedName(NICKNAME)
public String nickname;
@SerializedName(ACCESSKEY)
public String accesskey;
@SerializedName(SCORE)
public int score;
@SerializedName(DURATION)
public long duration;
@SerializedName(UID)
public String uid;
@SerializedName(SONG)
public String songName;
@SerializedName(ID)
public String id;
@SerializedName(SINGER)
public String singer;
@SerializedName(LANGUAGE)
public String language;
@Override
public String toString() {
return "Candidates{" +
"nickname='" + nickname + '\'' +
", accesskey='" + accesskey + '\'' +
", score=" + score +
", duration=" + duration +
", uid='" + uid + '\'' +
", songName='" + songName + '\'' +
", id='" + id + '\'' +
", singer='" + singer + '\'' +
", language='" + language + '\'' +
'}';
}
}
}

View File

@ -0,0 +1,38 @@
/*
* Copyright (c) 2019 Hemanth Savarala.
*
* Licensed under the GNU General Public License v3
*
* This 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 software 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.
*/
package code.name.monkey.retromusic.rest.service;
import androidx.annotation.NonNull;
import code.name.monkey.retromusic.rest.model.KuGouRawLyric;
import code.name.monkey.retromusic.rest.model.KuGouSearchLyricResult;
import io.reactivex.Observable;
import retrofit2.http.GET;
import retrofit2.http.Query;
/**
* Created by hemanths on 28/07/17.
*/
public interface KuGouApiService {
@NonNull
@GET("search?ver=1&man=yes&client=pc")
Observable<KuGouSearchLyricResult> searchLyric(@Query("keyword") @NonNull String songName, @Query("duration") @NonNull String duration);
@NonNull
@GET("download?ver=1&client=pc&fmt=lrc&charset=utf8")
Observable<KuGouRawLyric> getRawLyric(@Query("id") @NonNull String id, @Query("accesskey") @NonNull String accesskey);
}

View File

@ -0,0 +1,30 @@
/*
* Copyright (c) 2019 Hemanth Savarala.
*
* Licensed under the GNU General Public License v3
*
* This 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 software 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.
*/
package code.name.monkey.retromusic.transform;
import android.view.View;
import androidx.annotation.NonNull;
import androidx.viewpager.widget.ViewPager;
/**
* Created by hemanths on 3/9/19
*/
public class RoundStackTransformer implements ViewPager.PageTransformer {
@Override
public void transformPage(@NonNull View page, float position) {
}
}

View File

@ -0,0 +1,47 @@
/*
* Copyright (c) 2019 Hemanth Savarala.
*
* Licensed under the GNU General Public License v3
*
* This 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 software 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.
*/
package code.name.monkey.retromusic.transform
import android.view.View
import androidx.viewpager.widget.ViewPager
class StackPagerTransformer : ViewPager.PageTransformer {
override fun transformPage(view: View, position: Float) {
if (position < -1f) {
view.translationX = view.width * position
}
if (position < 0f) {
view.translationX = 0f
view.scaleX = 1f
view.scaleY = 1f
} else if (position <= 1f) {
val scaleFactor = MIN_SCALE + (1 - MIN_SCALE) * (1 - Math.abs(position))
view.pivotY = 0.5f * view.height
view.translationX = view.width * -position
view.scaleX = scaleFactor
view.scaleY = scaleFactor
}
}
companion object {
private val MIN_SCALE = 0.75f
}
}

View File

@ -0,0 +1,104 @@
/*
* Copyright (c) 2019 Hemanth Savarala.
*
* Licensed under the GNU General Public License v3
*
* This 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 software 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.
*/
package code.name.monkey.retromusic.util;
import android.content.Context;
import android.graphics.Bitmap;
import java.io.File;
import java.io.IOException;
import io.reactivex.Flowable;
/**
* Created on : June 18, 2016
* Author : zetbaitsu
* Name : Zetra
* GitHub : https://github.com/zetbaitsu
*/
public class Compressor {
//max width and height values of the compressed image is taken as 612x816
private int maxWidth = 612;
private int maxHeight = 816;
private Bitmap.CompressFormat compressFormat = Bitmap.CompressFormat.JPEG;
private int quality = 80;
private String destinationDirectoryPath;
public Compressor(Context context) {
destinationDirectoryPath = context.getCacheDir().getPath() + File.separator + "images";
}
public Compressor setMaxWidth(int maxWidth) {
this.maxWidth = maxWidth;
return this;
}
public Compressor setMaxHeight(int maxHeight) {
this.maxHeight = maxHeight;
return this;
}
public Compressor setCompressFormat(Bitmap.CompressFormat compressFormat) {
this.compressFormat = compressFormat;
return this;
}
public Compressor setQuality(int quality) {
this.quality = quality;
return this;
}
public Compressor setDestinationDirectoryPath(String destinationDirectoryPath) {
this.destinationDirectoryPath = destinationDirectoryPath;
return this;
}
public File compressToFile(File imageFile) throws IOException {
return compressToFile(imageFile, imageFile.getName());
}
public File compressToFile(File imageFile, String compressedFileName) throws IOException {
return ImageUtil.compressImage(imageFile, maxWidth, maxHeight, compressFormat, quality,
destinationDirectoryPath + File.separator + compressedFileName);
}
public Bitmap compressToBitmap(File imageFile) throws IOException {
return ImageUtil.decodeSampledBitmapFromFile(imageFile, maxWidth, maxHeight);
}
public Flowable<File> compressToFileAsFlowable(final File imageFile) {
return compressToFileAsFlowable(imageFile, imageFile.getName());
}
public Flowable<File> compressToFileAsFlowable(final File imageFile, final String compressedFileName) {
return Flowable.defer(() -> {
try {
return Flowable.just(compressToFile(imageFile, compressedFileName));
} catch (IOException e) {
return Flowable.error(e);
}
});
}
public Flowable<Bitmap> compressToBitmapAsFlowable(final File imageFile) {
return Flowable.defer(() -> {
try {
return Flowable.just(compressToBitmap(imageFile));
} catch (IOException e) {
return Flowable.error(e);
}
});
}
}

View File

@ -26,8 +26,10 @@ import androidx.annotation.Nullable;
import androidx.core.app.ActivityCompat;
import androidx.core.app.ActivityOptionsCompat;
import androidx.core.util.Pair;
import code.name.monkey.retromusic.R;
import code.name.monkey.retromusic.helper.MusicPlayerRemote;
import code.name.monkey.retromusic.model.Genre;
import code.name.monkey.retromusic.model.Playlist;
import code.name.monkey.retromusic.activities.AboutActivity;
import code.name.monkey.retromusic.activities.AlbumDetailsActivity;
import code.name.monkey.retromusic.activities.ArtistDetailActivity;
@ -41,10 +43,8 @@ import code.name.monkey.retromusic.activities.PurchaseActivity;
import code.name.monkey.retromusic.activities.SearchActivity;
import code.name.monkey.retromusic.activities.SettingsActivity;
import code.name.monkey.retromusic.activities.SupportDevelopmentActivity;
import code.name.monkey.retromusic.activities.UserInfoActivity;
import code.name.monkey.retromusic.activities.WhatsNewActivity;
import code.name.monkey.retromusic.helper.MusicPlayerRemote;
import code.name.monkey.retromusic.model.Genre;
import code.name.monkey.retromusic.model.Playlist;
import static code.name.monkey.retromusic.Constants.RATE_ON_GOOGLE_PLAY;
import static code.name.monkey.retromusic.util.RetroUtil.openUrl;
@ -138,6 +138,10 @@ public class NavigationUtil {
ActivityCompat.startActivity(activity, new Intent(activity, AboutActivity.class), null);
}
public static void goToUserInfo(@NonNull Activity activity) {
ActivityCompat.startActivity(activity, new Intent(activity, UserInfoActivity.class), null);
}
public static void goToOpenSource(@NonNull Activity activity) {
ActivityCompat.startActivity(activity, new Intent(activity, LicenseActivity.class), null);
}

View File

@ -0,0 +1,113 @@
/*
* Copyright (c) 2019 Hemanth Savarala.
*
* Licensed under the GNU General Public License v3
*
* This 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 software 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.
*/
package code.name.monkey.retromusic.util;
import android.app.Activity;
import android.content.Context;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.util.DisplayMetrics;
import android.view.ViewGroup;
import code.name.monkey.retromusic.App;
public class SystemUtils {
private static final String STATUS_BAR_HEIGHT_RES_NAME = "status_bar_height";
private static final String NAV_BAR_HEIGHT_RES_NAME = "navigation_bar_height";
private static final String NAV_BAR_HEIGHT_LANDSCAPE_RES_NAME = "navigation_bar_height_landscape";
private static final String NAV_BAR_WIDTH_RES_NAME = "navigation_bar_width";
private static final String SHOW_NAV_BAR_RES_NAME = "config_showNavigationBar";
private final float mSmallestWidthDp;
private final boolean mInPortrait;
private Activity activity;
public SystemUtils(Activity activity) {
this.activity = activity;
Resources resources = activity.getResources();
mSmallestWidthDp = getSmallestWidthDp(activity);
mInPortrait = (resources.getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT);
}
private static boolean hasNavBar(Resources resources) {
int id = resources.getIdentifier(SHOW_NAV_BAR_RES_NAME, "bool", "android");
if (id > 0)
return resources.getBoolean(id);
else
return false;
}
public static int getNavigationBarHeight() {
int result = 0;
int resourceId = App.Companion.getContext().getResources().getIdentifier("navigation_bar_height", "dimen", "android");
if (resourceId > 0) {
result = App.Companion.getContext().getResources().getDimensionPixelSize(resourceId);
}
return result;
}
public int getComboHeight() {
if (isNavigationAtBottom()) {
return getNavigationBarWidth();
} else {
return getNavigationBarHeight();
}
}
public int getNavigationBarWidth() {
Resources res = activity.getResources();
int result = 0;
if (hasNavBar(activity.getResources())) {
if (!isNavigationAtBottom())
return getInternalDimensionSize(res, NAV_BAR_WIDTH_RES_NAME);
}
return result;
}
public void addPadding(ViewGroup viewGroup) {
Context context = viewGroup.getContext();
Resources resources = context.getResources();
ViewGroup.MarginLayoutParams params = (ViewGroup.MarginLayoutParams) viewGroup.getLayoutParams();
if (isNavigationAtBottom()) {
params.leftMargin = getNavigationBarWidth();
params.rightMargin = getNavigationBarWidth();
} else {
params.bottomMargin = getNavigationBarHeight();
}
}
private int getInternalDimensionSize(Resources res, String key) {
int result = 0;
int resourceId = res.getIdentifier(key, "dimen", "android");
if (resourceId > 0) {
result = res.getDimensionPixelSize(resourceId);
}
return result;
}
private float getSmallestWidthDp(Activity activity) {
DisplayMetrics metrics = new DisplayMetrics();
activity.getWindowManager().getDefaultDisplay().getRealMetrics(metrics);
float widthDp = metrics.widthPixels / metrics.density;
float heightDp = metrics.heightPixels / metrics.density;
return Math.min(widthDp, heightDp);
}
boolean isNavigationAtBottom() {
return (mSmallestWidthDp >= 600 || mInPortrait);
}
}

View File

@ -0,0 +1,82 @@
/*
* Copyright (c) 2019 Hemanth Savarala.
*
* Licensed under the GNU General Public License v3
*
* This 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 software 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.
*/
package code.name.monkey.retromusic.util;
/**
* @author Hemanth S (h4h13).
*/
public class TempUtils {
// Enums
public static final int TEMPO_STROLL = 0;
public static final int TEMPO_WALK = 1;
public static final int TEMPO_LIGHT_JOG = 2;
public static final int TEMPO_JOG = 3;
public static final int TEMPO_RUN = 4;
public static final int TEMPO_SPRINT = 5;
public static final int TEMPO_UNKNOWN = 6;
// take BPM as an int
public static int getTempoFromBPM(int bpm) {
// STROLL less than 60
if (bpm < 60) {
return TEMPO_STROLL;
}
// WALK between 60 and 70, or between 120 and 140
else if (bpm < 70 || bpm >= 120 && bpm < 140) {
return TEMPO_WALK;
}
// LIGHT_JOG between 70 and 80, or between 140 and 160
else if (bpm < 80 || bpm >= 140 && bpm < 160) {
return TEMPO_LIGHT_JOG;
}
// JOG between 80 and 90, or between 160 and 180
else if (bpm < 90 || bpm >= 160 && bpm < 180) {
return TEMPO_JOG;
}
// RUN between 90 and 100, or between 180 and 200
else if (bpm < 100 || bpm >= 180 && bpm < 200) {
return TEMPO_RUN;
}
// SPRINT between 100 and 120
else if (bpm < 120) {
return TEMPO_SPRINT;
}
// UNKNOWN
else {
return TEMPO_UNKNOWN;
}
}
// take BPM as a string
public static int getTempoFromBPM(String bpm) {
// cast to an int from string
try {
// convert the string to an int
return getTempoFromBPM(Integer.parseInt(bpm.trim()));
} catch (NumberFormatException nfe) {
//
return TEMPO_UNKNOWN;
}
}
}

View File

@ -0,0 +1,37 @@
/*
* Copyright (c) 2019 Hemanth Savarala.
*
* Licensed under the GNU General Public License v3
*
* This 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 software 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.
*/
package code.name.monkey.retromusic.views
import android.content.Context
import android.content.res.ColorStateList
import android.util.AttributeSet
import code.name.monkey.appthemehelper.ThemeStore
import code.name.monkey.appthemehelper.util.ATHUtil
import code.name.monkey.appthemehelper.util.ColorUtil
import code.name.monkey.appthemehelper.util.MaterialValueHelper
import code.name.monkey.retromusic.R
import code.name.monkey.retromusic.util.RetroUtil
import com.google.android.material.button.MaterialButton
class MaterialButtonTextColor @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = -1) : MaterialButton(context, attrs, defStyleAttr) {
init {
setTextColor(MaterialValueHelper.getPrimaryTextColor(getContext(), ColorUtil.isColorLight(ThemeStore.primaryColor(getContext()))))
iconTint = ColorStateList.valueOf(ATHUtil.resolveColor(context, R.attr.iconColor))
rippleColor = ColorStateList.valueOf(ColorUtil.withAlpha(ThemeStore.accentColor(context), 0.4f))
//minHeight = RetroUtil.convertDpToPixel(42f, context).toInt()
iconSize = RetroUtil.convertDpToPixel(20f, context).toInt()
}
}

View File

@ -0,0 +1,225 @@
/*
* Copyright (c) 2019 Hemanth Savarala.
*
* Licensed under the GNU General Public License v3
*
* This 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 software 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.
*/
package code.name.monkey.retromusic.views;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ObjectAnimator;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.ColorFilter;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import androidx.annotation.NonNull;
import android.util.Property;
import android.view.animation.DecelerateInterpolator;
import code.name.monkey.retromusic.R;
public class PlayPauseDrawable extends Drawable {
private static final long PLAY_PAUSE_ANIMATION_DURATION = 250;
private static final Property<PlayPauseDrawable, Float> PROGRESS =
new Property<PlayPauseDrawable, Float>(Float.class, "progress") {
@Override
public Float get(@NonNull PlayPauseDrawable d) {
return d.getProgress();
}
@Override
public void set(@NonNull PlayPauseDrawable d, Float value) {
d.setProgress(value);
}
};
private final Path leftPauseBar = new Path();
private final Path rightPauseBar = new Path();
private final Paint paint = new Paint();
private final float pauseBarWidth;
private final float pauseBarHeight;
private final float pauseBarDistance;
private float width;
private float height;
private float progress;
private boolean isPlay;
private boolean isPlaySet;
private Animator animator;
public PlayPauseDrawable(@NonNull Context context) {
final Resources res = context.getResources();
paint.setAntiAlias(true);
paint.setStyle(Paint.Style.FILL);
paint.setColor(Color.WHITE);
pauseBarWidth = res.getDimensionPixelSize(R.dimen.pause_bar_width);
pauseBarHeight = res.getDimensionPixelSize(R.dimen.pause_bar_height);
pauseBarDistance = res.getDimensionPixelSize(R.dimen.pause_bar_distance);
}
/**
* Linear interpolate between a and b with parameter t.
*/
private static float lerp(float a, float b, float t) {
return a + (b - a) * t;
}
@Override
protected void onBoundsChange(@NonNull final Rect bounds) {
super.onBoundsChange(bounds);
if (bounds.width() > 0 && bounds.height() > 0) {
width = bounds.width();
height = bounds.height();
}
}
@Override
public void draw(@NonNull Canvas canvas) {
leftPauseBar.rewind();
rightPauseBar.rewind();
// The current distance between the two pause bars.
final float barDist = lerp(pauseBarDistance, 0f, progress);
// The current width of each pause bar.
float rawBarWidth = lerp(pauseBarWidth, pauseBarHeight / 1.75f, progress);
// We have to round the bar width when finishing the progress to prevent the gap
// that might occur onDraw because of a pixel is lost when casting float to int instead of rounding it.
final float barWidth = progress == 1f ? Math.round(rawBarWidth) : rawBarWidth;
// The current position of the left pause bar's top left coordinate.
final float firstBarTopLeft = lerp(0f, barWidth, progress);
// The current position of the right pause bar's top right coordinate.
final float secondBarTopRight = lerp(2f * barWidth + barDist, barWidth + barDist, progress);
// Draw the left pause bar. The left pause bar transforms into the
// top half of the play button triangle by animating the position of the
// rectangle's top left coordinate and expanding its bottom width.
leftPauseBar.moveTo(0f, 0f);
leftPauseBar.lineTo(firstBarTopLeft, -pauseBarHeight);
leftPauseBar.lineTo(barWidth, -pauseBarHeight);
leftPauseBar.lineTo(barWidth, 0f);
leftPauseBar.close();
// Draw the right pause bar. The right pause bar transforms into the
// bottom half of the play button triangle by animating the position of the
// rectangle's top right coordinate and expanding its bottom width.
rightPauseBar.moveTo(barWidth + barDist, 0f);
rightPauseBar.lineTo(barWidth + barDist, -pauseBarHeight);
rightPauseBar.lineTo(secondBarTopRight, -pauseBarHeight);
rightPauseBar.lineTo(2 * barWidth + barDist, 0f);
rightPauseBar.close();
final int saveCount = canvas.save();
// Translate the play button a tiny bit to the right so it looks more centered.
canvas.translate(lerp(0f, pauseBarHeight / 8f, progress), 0f);
// (1) Pause --> Play: rotate 0 to 90 degrees clockwise.
// (2) Play --> Pause: rotate 90 to 180 degrees clockwise.
final float rotationProgress = isPlay ? 1f - progress : progress;
final float startingRotation = isPlay ? 90f : 0f;
canvas.rotate(lerp(startingRotation, startingRotation + 90f, rotationProgress), width / 2f, height / 2f);
// Position the pause/play button in the center of the drawable's bounds.
canvas.translate(Math.round(width / 2f - ((2f * barWidth + barDist) / 2f)), Math.round(height / 2f + (pauseBarHeight / 2f)));
// Draw the two bars that form the animated pause/play button.
canvas.drawPath(leftPauseBar, paint);
canvas.drawPath(rightPauseBar, paint);
canvas.restoreToCount(saveCount);
}
@NonNull
private Animator getPausePlayAnimator() {
isPlaySet = !isPlaySet;
final Animator anim = ObjectAnimator.ofFloat(this, PROGRESS, isPlay ? 1f : 0f, isPlay ? 0f : 1f);
anim.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
isPlay = !isPlay;
}
});
return anim;
}
private float getProgress() {
return progress;
}
private void setProgress(float progress) {
this.progress = progress;
invalidateSelf();
}
@Override
public void setAlpha(int alpha) {
paint.setAlpha(alpha);
invalidateSelf();
}
@Override
public void setColorFilter(ColorFilter cf) {
paint.setColorFilter(cf);
invalidateSelf();
}
@Override
public int getOpacity() {
return PixelFormat.TRANSLUCENT;
}
public void setPlay(boolean animate) {
if (animate) {
if (!isPlaySet) {
togglePlayPause();
}
} else {
isPlaySet = true;
isPlay = true;
setProgress(1f);
}
}
public void setPause(boolean animate) {
if (animate) {
if (isPlaySet) {
togglePlayPause();
}
} else {
isPlaySet = false;
isPlay = false;
setProgress(0f);
}
}
public void togglePlayPause() {
if (animator != null) {
animator.cancel();
}
animator = getPausePlayAnimator();
animator.setInterpolator(new DecelerateInterpolator());
animator.setDuration(PLAY_PAUSE_ANIMATION_DURATION);
animator.start();
}
}

View File

@ -0,0 +1,36 @@
/*
* Copyright (c) 2019 Hemanth Savarala.
*
* Licensed under the GNU General Public License v3
*
* This 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 software 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.
*/
package code.name.monkey.retromusic.views
import android.content.Context
import android.graphics.PorterDuff
import android.graphics.drawable.Drawable
import android.util.AttributeSet
import androidx.appcompat.widget.Toolbar
import code.name.monkey.appthemehelper.ThemeStore
class TintIconColorToolbar : Toolbar {
constructor(context: Context) : super(context)
constructor(context: Context, attrs: AttributeSet?) : super(context, attrs)
constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr)
override fun setNavigationIcon(icon: Drawable?) {
super.setNavigationIcon(icon)
icon?.setColorFilter(ThemeStore.textColorSecondary(context), PorterDuff.Mode.SRC_IN)
}
}

View File

@ -0,0 +1,147 @@
/*
* Copyright (c) 2019 Hemanth Savarala.
*
* Licensed under the GNU General Public License v3
*
* This 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 software 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.
*/
package code.name.monkey.retromusic.views;
import android.content.Context;
import android.content.SharedPreferences;
import android.graphics.Color;
import android.graphics.drawable.Drawable;
import android.preference.PreferenceManager;
import android.util.AttributeSet;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.bumptech.glide.request.Request;
import com.bumptech.glide.request.target.SizeReadyCallback;
import com.bumptech.glide.request.target.Target;
import com.bumptech.glide.request.transition.Transition;
import java.io.File;
import code.name.monkey.retromusic.R;
import code.name.monkey.retromusic.glide.GlideApp;
import code.name.monkey.retromusic.util.PreferenceUtil;
import static code.name.monkey.retromusic.Constants.USER_PROFILE;
public class UserImageView extends CircularImageView implements SharedPreferences.OnSharedPreferenceChangeListener {
public UserImageView(@NonNull Context context) {
super(context);
init(context);
}
public UserImageView(@NonNull Context context, @NonNull AttributeSet attrs) {
super(context, attrs);
init(context);
}
public UserImageView(@NonNull Context context, @NonNull AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context);
}
void init(@NonNull Context context) {
resetImage(context);
}
private void resetImage(@NonNull Context context) {
GlideApp.with(context)
.asDrawable()
.placeholder(R.drawable.ic_account_white_24dp)
.fallback(R.drawable.ic_account_white_24dp)
.load(new File(PreferenceUtil.getInstance().getProfileImage(), USER_PROFILE))
.into(new Target<Drawable>() {
@Override
public void onLoadStarted(@Nullable Drawable placeholder) {
setImageDrawable(placeholder);
setBackgroundColor(Color.TRANSPARENT);
}
@Override
public void onLoadFailed(@Nullable Drawable errorDrawable) {
setImageDrawable(errorDrawable);
setBackgroundColor(Color.TRANSPARENT);
}
@Override
public void onResourceReady(@NonNull Drawable resource, @Nullable Transition<? super Drawable> transition) {
setImageDrawable(resource);
setBackgroundColor(Color.TRANSPARENT);
}
@Override
public void onLoadCleared(@Nullable Drawable placeholder) {
}
@Override
public void getSize(@NonNull SizeReadyCallback cb) {
cb.onSizeReady(96, 96);
}
@Override
public void removeCallback(@NonNull SizeReadyCallback cb) {
}
@Nullable
@Override
public Request getRequest() {
return null;
}
@Override
public void setRequest(@Nullable Request request) {
}
@Override
public void onStart() {
}
@Override
public void onStop() {
}
@Override
public void onDestroy() {
}
});
}
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
PreferenceManager.getDefaultSharedPreferences(getContext()).registerOnSharedPreferenceChangeListener(this);
}
@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
PreferenceManager.getDefaultSharedPreferences(getContext()).unregisterOnSharedPreferenceChangeListener(this);
}
@Override
public void onSharedPreferenceChanged(@NonNull SharedPreferences sharedPreferences, @NonNull String key) {
if (key.equals(PreferenceUtil.PROFILE_IMAGE_PATH)) {
resetImage(getContext());
}
}
}

View File

@ -35,11 +35,11 @@ class WidthFitSquareCardView : MaterialCardView {
}
override fun onMeasure(i: Int, i2: Int) {
var i2Final = i2
var i2 = i2
if (this.forceSquare) {
i2Final = i
i2 = i
}
super.onMeasure(i, i2Final)
super.onMeasure(i, i2)
}
private var forceSquare = true

View File

@ -0,0 +1,114 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<androidx.coordinatorlayout.widget.CoordinatorLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.google.android.material.appbar.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@android:color/transparent"
android:elevation="0dp"
app:elevation="0dp">
<com.google.android.material.appbar.CollapsingToolbarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@android:color/transparent"
app:contentScrim="@android:color/transparent"
app:layout_scrollFlags="scroll|exitUntilCollapsed"
app:statusBarScrim="@color/md_black_1000"
app:titleEnabled="false">
<FrameLayout
android:id="@+id/imageContainer"
android:layout_width="match_parent"
android:layout_height="182dp"
app:layout_collapseMode="pin">
<ImageView
android:id="@+id/bannerImage"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="centerCrop"
app:layout_collapseMode="parallax"
tools:background="@color/md_red_400"
tools:ignore="ContentDescription" />
</FrameLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_collapseMode="pin"
app:layout_scrollFlags="scroll|enterAlways">
<FrameLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<include layout="@layout/status_bar" />
</FrameLayout>
<com.google.android.material.card.MaterialCardView
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:cardCornerRadius="8dp"
app:cardElevation="6dp"
app:cardUseCompatPadding="true"
app:layout_scrollFlags="scroll|enterAlways">
<androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar"
style="@style/Toolbar"
app:title="@string/app_name"
app:titleMarginStart="0dp"
tools:ignore="UnusedAttribute" />
</com.google.android.material.card.MaterialCardView>
</LinearLayout>
</com.google.android.material.appbar.CollapsingToolbarLayout>
</com.google.android.material.appbar.AppBarLayout>
<androidx.core.widget.NestedScrollView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginStart="64dp"
android:layout_marginEnd="64dp"
android:elevation="@dimen/card_elevation"
app:behavior_overlapTop="72dp"
app:layout_behavior="@string/appbar_scrolling_view_behavior">
<com.google.android.material.card.MaterialCardView
android:layout_width="match_parent"
android:layout_height="match_parent"
app:cardCornerRadius="12dp">
<LinearLayout
android:id="@+id/contentContainer"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:descendantFocusability="blocksDescendants"
android:focusable="true"
android:focusableInTouchMode="true"
android:orientation="vertical">
<include layout="@layout/abs_playlists" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:nestedScrollingEnabled="false" />
</LinearLayout>
</com.google.android.material.card.MaterialCardView>
</androidx.core.widget.NestedScrollView>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
</LinearLayout>

View File

@ -12,6 +12,10 @@
android:layout_width="match_parent"
android:layout_height="match_parent" />
<com.jetradarmobile.snowfall.SnowfallView
android:id="@+id/snowfall"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<include layout="@layout/shadow_statusbar_toolbar" />

View File

@ -0,0 +1,113 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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/main_fragment_content"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<androidx.coordinatorlayout.widget.CoordinatorLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.google.android.material.appbar.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@android:color/transparent"
android:elevation="0dp"
app:elevation="0dp">
<com.google.android.material.appbar.CollapsingToolbarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@android:color/transparent"
app:contentScrim="@android:color/transparent"
app:layout_scrollFlags="scroll|exitUntilCollapsed"
app:statusBarScrim="@color/md_black_1000"
app:titleEnabled="false">
<FrameLayout
android:id="@+id/imageContainer"
android:layout_width="match_parent"
android:layout_height="228dp"
app:layout_collapseMode="parallax">
<ImageView
android:id="@+id/bannerImage"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="centerCrop"
app:layout_collapseMode="parallax"
tools:background="@color/md_red_400"
tools:ignore="ContentDescription" />
</FrameLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_collapseMode="pin"
app:layout_scrollFlags="scroll|enterAlways">
<FrameLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<include layout="@layout/status_bar" />
</FrameLayout>
<com.google.android.material.card.MaterialCardView
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:cardCornerRadius="8dp"
app:cardElevation="6dp"
app:cardUseCompatPadding="true"
app:layout_scrollFlags="scroll|enterAlways">
<androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar"
style="@style/Toolbar"
app:title="@string/app_name"
app:titleMarginStart="0dp"
tools:ignore="UnusedAttribute" />
</com.google.android.material.card.MaterialCardView>
</LinearLayout>
</com.google.android.material.appbar.CollapsingToolbarLayout>
</com.google.android.material.appbar.AppBarLayout>
<androidx.core.widget.NestedScrollView
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:behavior_overlapTop="52dp"
app:layout_behavior="@string/appbar_scrolling_view_behavior">
<com.google.android.material.card.MaterialCardView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="128dp"
android:layout_marginEnd="128dp"
app:cardCornerRadius="12dp">
<LinearLayout
android:id="@+id/contentContainer"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:descendantFocusability="blocksDescendants"
android:focusable="true"
android:focusableInTouchMode="true"
android:orientation="vertical">
<include layout="@layout/abs_playlists" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:nestedScrollingEnabled="false" />
</LinearLayout>
</com.google.android.material.card.MaterialCardView>
</androidx.core.widget.NestedScrollView>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
</LinearLayout>

View File

@ -12,6 +12,11 @@
android:layout_width="match_parent"
android:layout_height="match_parent" />
<com.jetradarmobile.snowfall.SnowfallView
android:id="@+id/snowfall"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<include layout="@layout/shadow_statusbar_toolbar" />
<code.name.monkey.retromusic.views.FitSystemWindowsLayout

View File

@ -0,0 +1,112 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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/main_fragment_content"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<androidx.coordinatorlayout.widget.CoordinatorLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.google.android.material.appbar.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@android:color/transparent"
android:elevation="0dp"
app:elevation="0dp">
<com.google.android.material.appbar.CollapsingToolbarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@android:color/transparent"
app:contentScrim="@android:color/transparent"
app:layout_scrollFlags="scroll|exitUntilCollapsed"
app:statusBarScrim="@color/md_black_1000"
app:titleEnabled="false">
<FrameLayout
android:id="@+id/imageContainer"
android:layout_width="match_parent"
android:layout_height="196dp"
app:layout_collapseMode="parallax">
<ImageView
android:id="@+id/bannerImage"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="centerCrop"
app:layout_collapseMode="parallax"
tools:background="@color/md_red_400"
tools:ignore="ContentDescription" />
</FrameLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_collapseMode="pin"
app:layout_scrollFlags="scroll|enterAlways">
<FrameLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<include layout="@layout/status_bar" />
</FrameLayout>
<com.google.android.material.card.MaterialCardView
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:cardCornerRadius="8dp"
app:cardElevation="6dp"
app:cardUseCompatPadding="true"
app:layout_scrollFlags="scroll|enterAlways">
<androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar"
style="@style/Toolbar"
app:title="@string/app_name"
app:titleMarginStart="0dp"
tools:ignore="UnusedAttribute" />
</com.google.android.material.card.MaterialCardView>
</LinearLayout>
</com.google.android.material.appbar.CollapsingToolbarLayout>
</com.google.android.material.appbar.AppBarLayout>
<androidx.core.widget.NestedScrollView
android:layout_width="match_parent"
android:layout_height="match_parent"
app:behavior_overlapTop="52dp"
app:layout_behavior="@string/appbar_scrolling_view_behavior">
<com.google.android.material.card.MaterialCardView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="64dp"
android:layout_marginEnd="64dp"
app:cardCornerRadius="12dp">
<LinearLayout
android:id="@+id/contentContainer"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:descendantFocusability="blocksDescendants"
android:focusable="true"
android:focusableInTouchMode="true"
android:orientation="vertical">
<include layout="@layout/abs_playlists" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:nestedScrollingEnabled="false" />
</LinearLayout>
</com.google.android.material.card.MaterialCardView>
</androidx.core.widget.NestedScrollView>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
</LinearLayout>

View File

@ -11,8 +11,10 @@
android:id="@+id/colorGradientBackground"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<com.jetradarmobile.snowfall.SnowfallView
android:id="@+id/snowfall"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<include layout="@layout/shadow_statusbar_toolbar" />
<code.name.monkey.retromusic.views.FitSystemWindowsLayout

View File

@ -0,0 +1,105 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout 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:layout_width="match_parent"
android:layout_height="match_parent">
<com.google.android.material.appbar.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@android:color/transparent"
android:elevation="0dp"
app:elevation="0dp">
<com.google.android.material.appbar.CollapsingToolbarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@android:color/transparent"
app:contentScrim="@android:color/transparent"
app:layout_scrollFlags="scroll|exitUntilCollapsed"
app:statusBarScrim="@color/md_black_1000"
app:titleEnabled="false">
<FrameLayout
android:id="@+id/imageContainer"
android:layout_width="match_parent"
android:layout_height="228dp"
app:layout_collapseMode="parallax">
<ImageView
android:id="@+id/bannerImage"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="centerCrop"
app:layout_collapseMode="parallax"
tools:background="@color/md_red_400"
tools:ignore="ContentDescription" />
</FrameLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_collapseMode="pin"
app:layout_scrollFlags="scroll|enterAlways">
<FrameLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<include layout="@layout/status_bar" />
</FrameLayout>
<com.google.android.material.card.MaterialCardView
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:cardCornerRadius="8dp"
app:cardElevation="6dp"
app:cardUseCompatPadding="true"
app:layout_scrollFlags="scroll|enterAlways">
<androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar"
style="@style/Toolbar"
app:title="@string/app_name"
app:titleMarginStart="0dp"
tools:ignore="UnusedAttribute" />
</com.google.android.material.card.MaterialCardView>
</LinearLayout>
</com.google.android.material.appbar.CollapsingToolbarLayout>
</com.google.android.material.appbar.AppBarLayout>
<androidx.core.widget.NestedScrollView
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:behavior_overlapTop="24dp"
app:layout_behavior="com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior">
<com.google.android.material.card.MaterialCardView
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:cardCornerRadius="12dp">
<LinearLayout
android:id="@+id/contentContainer"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:descendantFocusability="blocksDescendants"
android:focusable="true"
android:focusableInTouchMode="true"
android:orientation="vertical">
<include layout="@layout/abs_playlists" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:nestedScrollingEnabled="false" />
</LinearLayout>
</com.google.android.material.card.MaterialCardView>
</androidx.core.widget.NestedScrollView>
</androidx.coordinatorlayout.widget.CoordinatorLayout>

View File

@ -12,6 +12,11 @@
android:layout_width="match_parent"
android:layout_height="match_parent" />
<com.jetradarmobile.snowfall.SnowfallView
android:id="@+id/snowfall"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<include layout="@layout/shadow_statusbar_toolbar" />
<code.name.monkey.retromusic.views.FitSystemWindowsLayout

View File

@ -95,6 +95,8 @@
<item name="android:fontFamily">@font/circular</item>
<item name="android:textColorPrimary">@color/md_white_1000</item>
<item name="windowActionBar">false</item>
</style>
<style name="Theme.RetroMusic.Base.Color" parent="@style/Theme.RetroMusic.Base">
@ -103,6 +105,8 @@
<item name="android:windowBackground">?colorPrimary</item>
<item name="md_color_button_text">@color/md_white_1000</item>
<item name="windowActionBar">false</item>
</style>
@ -128,6 +132,8 @@
<item name="android:windowSharedElementEnterTransition">@transition/grid_exit</item>
<item name="android:windowSharedElementExitTransition">@transition/grid_exit</item>
<item name="android:fontFamily">@font/circular</item>
<item name="windowActionBar">false</item>
</style>
<style name="Theme.RetroMusic.Base.Light" parent="Theme.MaterialComponents.Light.NoActionBar">
@ -176,6 +182,8 @@
<item name="android:fontFamily">@font/circular</item>
<item name="android:textColorPrimary">@color/md_grey_900</item>
<item name="windowActionBar">false</item>
</style>

View File

@ -1,6 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.preference.PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<code.name.monkey.appthemehelper.common.prefs.supportv7.ATEPreferenceCategory app:title="@string/grid_style_label">
<code.name.monkey.retromusic.preferences.MaterialListPreference
android:defaultValue="0"
@ -46,6 +47,12 @@
android:title="@string/pref_title_tab_text_mode"
app:enableCopying="true" />
<code.name.monkey.appthemehelper.common.prefs.supportv7.ATESwitchPreference
android:defaultValue="false"
android:key="toggle_home_banner"
android:summary="@string/pref_summary_home_banner"
android:title="@string/pref_title_home_banner" />
<code.name.monkey.appthemehelper.common.prefs.supportv7.ATESwitchPreference
android:defaultValue="false"
android:key="toggle_genre"