Refactor packages
This commit is contained in:
parent
c386e1fe93
commit
39b0b4c931
188 changed files with 440 additions and 835 deletions
|
@ -0,0 +1,179 @@
|
|||
package code.name.monkey.retromusic.activities
|
||||
|
||||
import android.content.Intent
|
||||
import android.content.pm.PackageManager
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
import android.view.MenuItem
|
||||
import android.view.View
|
||||
import androidx.core.app.ShareCompat
|
||||
import androidx.recyclerview.widget.DefaultItemAnimator
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import code.name.monkey.appthemehelper.ThemeStore
|
||||
import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper
|
||||
import code.name.monkey.retromusic.Constants.APP_INSTAGRAM_LINK
|
||||
import code.name.monkey.retromusic.Constants.APP_TELEGRAM_LINK
|
||||
import code.name.monkey.retromusic.Constants.APP_TWITTER_LINK
|
||||
import code.name.monkey.retromusic.Constants.DISCORD_LINK
|
||||
import code.name.monkey.retromusic.Constants.FAQ_LINK
|
||||
import code.name.monkey.retromusic.Constants.GITHUB_PROJECT
|
||||
import code.name.monkey.retromusic.Constants.PINTEREST
|
||||
import code.name.monkey.retromusic.Constants.RATE_ON_GOOGLE_PLAY
|
||||
import code.name.monkey.retromusic.Constants.TELEGRAM_CHANGE_LOG
|
||||
import code.name.monkey.retromusic.Constants.TRANSLATE
|
||||
import code.name.monkey.retromusic.R
|
||||
import code.name.monkey.retromusic.model.Contributor
|
||||
import code.name.monkey.retromusic.activities.base.AbsBaseActivity
|
||||
import code.name.monkey.retromusic.adapter.ContributorAdapter
|
||||
import code.name.monkey.retromusic.util.NavigationUtil
|
||||
import com.afollestad.materialdialogs.MaterialDialog
|
||||
import com.afollestad.materialdialogs.list.listItems
|
||||
import com.google.gson.Gson
|
||||
import com.google.gson.reflect.TypeToken
|
||||
import kotlinx.android.synthetic.main.activity_about.*
|
||||
import kotlinx.android.synthetic.main.card_credit.*
|
||||
import kotlinx.android.synthetic.main.card_other.*
|
||||
import kotlinx.android.synthetic.main.card_retro_info.*
|
||||
import kotlinx.android.synthetic.main.card_social.*
|
||||
import java.io.IOException
|
||||
import java.nio.charset.StandardCharsets
|
||||
|
||||
class AboutActivity : AbsBaseActivity(), View.OnClickListener {
|
||||
|
||||
private val assetJsonData: String?
|
||||
get() {
|
||||
val json: String
|
||||
try {
|
||||
val inputStream = assets.open("contributors.json")
|
||||
val size = inputStream.available()
|
||||
val buffer = ByteArray(size)
|
||||
inputStream.read(buffer)
|
||||
inputStream.close()
|
||||
json = String(buffer, StandardCharsets.UTF_8)
|
||||
} catch (ex: IOException) {
|
||||
ex.printStackTrace()
|
||||
return null
|
||||
}
|
||||
|
||||
return json
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(R.layout.activity_about)
|
||||
|
||||
setStatusbarColorAuto()
|
||||
setNavigationbarColorAuto()
|
||||
setLightNavigationBar(true)
|
||||
|
||||
loadContributors()
|
||||
setUpToolbar()
|
||||
|
||||
appVersion.text = getAppVersion()
|
||||
|
||||
setUpView()
|
||||
|
||||
}
|
||||
|
||||
|
||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||
if (item.itemId == android.R.id.home) {
|
||||
onBackPressed()
|
||||
return true
|
||||
}
|
||||
return super.onOptionsItemSelected(item)
|
||||
}
|
||||
|
||||
private fun setUpToolbar() {
|
||||
appBarLayout.setBackgroundColor(ThemeStore.primaryColor(this))
|
||||
toolbar.setBackgroundColor(ThemeStore.primaryColor(this))
|
||||
setSupportActionBar(toolbar)
|
||||
title = null
|
||||
ToolbarContentTintHelper.colorBackButton(toolbar, ThemeStore.textColorSecondary(this))
|
||||
}
|
||||
|
||||
private fun openUrl(url: String) {
|
||||
val i = Intent(Intent.ACTION_VIEW)
|
||||
i.data = Uri.parse(url)
|
||||
i.flags = Intent.FLAG_ACTIVITY_NEW_TASK
|
||||
startActivity(i)
|
||||
}
|
||||
|
||||
private fun setUpView() {
|
||||
appGithub.setOnClickListener(this)
|
||||
faqLink.setOnClickListener(this)
|
||||
telegramLink.setOnClickListener(this)
|
||||
appRate.setOnClickListener(this)
|
||||
appTranslation.setOnClickListener(this)
|
||||
appShare.setOnClickListener(this)
|
||||
donateLink.setOnClickListener(this)
|
||||
instagramLink.setOnClickListener(this)
|
||||
twitterLink.setOnClickListener(this)
|
||||
changelog.setOnClickListener(this)
|
||||
openSource.setOnClickListener(this)
|
||||
pinterestLink.setOnClickListener(this)
|
||||
}
|
||||
|
||||
override fun onClick(view: View) {
|
||||
when (view.id) {
|
||||
R.id.pinterestLink -> openUrl(PINTEREST)
|
||||
R.id.faqLink -> openUrl(FAQ_LINK)
|
||||
R.id.telegramLink -> openUrl(APP_TELEGRAM_LINK)
|
||||
R.id.discordLink -> openUrl(DISCORD_LINK)
|
||||
R.id.appGithub -> openUrl(GITHUB_PROJECT)
|
||||
R.id.appTranslation -> openUrl(TRANSLATE)
|
||||
R.id.appRate -> openUrl(RATE_ON_GOOGLE_PLAY)
|
||||
R.id.appShare -> shareApp()
|
||||
R.id.donateLink -> NavigationUtil.goToSupportDevelopment(this)
|
||||
R.id.instagramLink -> openUrl(APP_INSTAGRAM_LINK)
|
||||
R.id.twitterLink -> openUrl(APP_TWITTER_LINK)
|
||||
R.id.changelog -> showChangeLogOptions()
|
||||
R.id.openSource -> NavigationUtil.goToOpenSource(this)
|
||||
}
|
||||
}
|
||||
|
||||
private fun showChangeLogOptions() {
|
||||
MaterialDialog(this).show {
|
||||
listItems(items = listOf("Telegram Channel", "App")) { _, position, _ ->
|
||||
if (position == 0) {
|
||||
openUrl(TELEGRAM_CHANGE_LOG)
|
||||
} else {
|
||||
NavigationUtil.gotoWhatNews(this@AboutActivity)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun getAppVersion(): String {
|
||||
return try {
|
||||
val packageInfo = packageManager.getPackageInfo(packageName, 0)
|
||||
packageInfo.versionName
|
||||
} catch (e: PackageManager.NameNotFoundException) {
|
||||
e.printStackTrace()
|
||||
"0.0.0"
|
||||
}
|
||||
}
|
||||
|
||||
private fun shareApp() {
|
||||
val shareIntent = ShareCompat.IntentBuilder.from(this)
|
||||
.setType("songText/plain")
|
||||
.setText(String.format(getString(R.string.app_share), packageName))
|
||||
.intent
|
||||
if (shareIntent.resolveActivity(packageManager) != null) {
|
||||
startActivity(Intent.createChooser(shareIntent, resources.getText(R.string.action_share)))
|
||||
}
|
||||
}
|
||||
|
||||
private fun loadContributors() {
|
||||
val data = assetJsonData
|
||||
val type = object : TypeToken<List<Contributor>>() {
|
||||
|
||||
}.type
|
||||
val contributors = Gson().fromJson<List<Contributor>>(data, type)
|
||||
|
||||
val contributorAdapter = ContributorAdapter(contributors)
|
||||
recyclerView.layoutManager = LinearLayoutManager(this)
|
||||
recyclerView.itemAnimator = DefaultItemAnimator()
|
||||
recyclerView.adapter = contributorAdapter
|
||||
}
|
||||
}
|
|
@ -0,0 +1,356 @@
|
|||
package code.name.monkey.retromusic.activities
|
||||
|
||||
import android.content.Intent
|
||||
import android.content.res.ColorStateList
|
||||
import android.graphics.Color
|
||||
import android.os.Bundle
|
||||
import android.transition.Slide
|
||||
import android.view.*
|
||||
import android.view.animation.AnimationUtils
|
||||
import android.widget.ImageView
|
||||
import androidx.core.app.ActivityCompat
|
||||
import androidx.core.util.Pair
|
||||
import androidx.core.widget.NestedScrollView
|
||||
import androidx.recyclerview.widget.DefaultItemAnimator
|
||||
import androidx.recyclerview.widget.GridLayoutManager
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import code.name.monkey.appthemehelper.ThemeStore
|
||||
import code.name.monkey.appthemehelper.util.ColorUtil
|
||||
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.dialogs.AddToPlaylistDialog
|
||||
import code.name.monkey.retromusic.dialogs.DeleteSongsDialog
|
||||
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.SortOrder.AlbumSongSortOrder
|
||||
import code.name.monkey.retromusic.loaders.ArtistLoader
|
||||
import code.name.monkey.retromusic.misc.AppBarStateChangeListener
|
||||
import code.name.monkey.retromusic.model.Album
|
||||
import code.name.monkey.retromusic.mvp.contract.AlbumDetailsContract
|
||||
import code.name.monkey.retromusic.mvp.presenter.AlbumDetailsPresenter
|
||||
import code.name.monkey.retromusic.activities.base.AbsSlidingMusicPanelActivity
|
||||
import code.name.monkey.retromusic.activities.tageditor.AbsTagEditorActivity
|
||||
import code.name.monkey.retromusic.activities.tageditor.AlbumTagEditorActivity
|
||||
import code.name.monkey.retromusic.adapter.album.HorizontalAlbumAdapter
|
||||
import code.name.monkey.retromusic.adapter.song.SimpleSongAdapter
|
||||
import code.name.monkey.retromusic.util.MusicUtil
|
||||
import code.name.monkey.retromusic.util.NavigationUtil
|
||||
import code.name.monkey.retromusic.util.PreferenceUtil
|
||||
import code.name.monkey.retromusic.util.RetroUtil
|
||||
import com.google.android.material.appbar.AppBarLayout
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers
|
||||
import io.reactivex.disposables.CompositeDisposable
|
||||
import io.reactivex.schedulers.Schedulers
|
||||
import kotlinx.android.synthetic.main.activity_album.*
|
||||
import kotlinx.android.synthetic.main.activity_album_content.*
|
||||
import java.util.*
|
||||
|
||||
class AlbumDetailsActivity : AbsSlidingMusicPanelActivity(), AlbumDetailsContract.AlbumDetailsView {
|
||||
|
||||
private lateinit var albumDetailsPresenter: AlbumDetailsPresenter
|
||||
private lateinit var simpleSongAdapter: SimpleSongAdapter
|
||||
private var disposable = CompositeDisposable()
|
||||
|
||||
var album: Album? = null
|
||||
private set
|
||||
|
||||
private val savedSortOrder: String
|
||||
get() = PreferenceUtil.getInstance().albumDetailSongSortOrder
|
||||
|
||||
override fun createContentView(): View {
|
||||
return wrapSlidingMusicPanel(R.layout.activity_album)
|
||||
}
|
||||
|
||||
private fun setupWindowTransition() {
|
||||
val slide = Slide(Gravity.BOTTOM)
|
||||
slide.interpolator = AnimationUtils.loadInterpolator(this, android.R.interpolator.linear_out_slow_in)
|
||||
window.enterTransition = slide
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
setDrawUnderStatusBar()
|
||||
setupWindowTransition()
|
||||
super.onCreate(savedInstanceState)
|
||||
toggleBottomNavigationView(true)
|
||||
collapsingToolbarLayout?.setBackgroundColor(ThemeStore.primaryColor(this))
|
||||
setLightNavigationBar(true)
|
||||
setNavigationbarColorAuto()
|
||||
|
||||
|
||||
ActivityCompat.postponeEnterTransition(this)
|
||||
|
||||
val albumId = intent.getIntExtra(EXTRA_ALBUM_ID, -1)
|
||||
albumDetailsPresenter = AlbumDetailsPresenter(this, albumId)
|
||||
albumDetailsPresenter.subscribe()
|
||||
|
||||
setupRecyclerView()
|
||||
setupToolbarMarginHeight()
|
||||
|
||||
contentContainer.setOnScrollChangeListener { _: NestedScrollView?, _: Int, scrollY: Int, _: Int, oldScrollY: Int ->
|
||||
run {
|
||||
if (scrollY > oldScrollY) {
|
||||
actionShuffleAll.shrink(true)
|
||||
}
|
||||
if (scrollY < oldScrollY) {
|
||||
actionShuffleAll.extend(true)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
actionShuffleAll.setOnClickListener { MusicPlayerRemote.openAndShuffleQueue(album!!.songs!!, true) }
|
||||
|
||||
artistImage = findViewById(R.id.artistImage)
|
||||
artistImage.setOnClickListener {
|
||||
val artistPairs = arrayOf<Pair<*, *>>(Pair.create(image, resources.getString(R.string.transition_artist_image)))
|
||||
NavigationUtil.goToArtist(this, album!!.artistId, *artistPairs)
|
||||
}
|
||||
}
|
||||
|
||||
private fun setupRecyclerView() {
|
||||
simpleSongAdapter = SimpleSongAdapter(this, ArrayList(), R.layout.item_song, false)
|
||||
recyclerView.apply {
|
||||
layoutManager = LinearLayoutManager(this@AlbumDetailsActivity)
|
||||
itemAnimator = DefaultItemAnimator()
|
||||
isNestedScrollingEnabled = false
|
||||
adapter = simpleSongAdapter
|
||||
}
|
||||
}
|
||||
|
||||
private fun setupToolbarMarginHeight() {
|
||||
setSupportActionBar(toolbar)
|
||||
supportActionBar!!.title = null
|
||||
|
||||
val primaryColor = ThemeStore.primaryColor(this)
|
||||
TintHelper.setTintAuto(contentContainer!!, primaryColor, true)
|
||||
|
||||
if (collapsingToolbarLayout != null) {
|
||||
collapsingToolbarLayout!!.apply {
|
||||
setContentScrimColor(primaryColor)
|
||||
setStatusBarScrimColor(ColorUtil.darkenColor(primaryColor))
|
||||
}
|
||||
}
|
||||
|
||||
toolbar.setNavigationIcon(R.drawable.ic_keyboard_backspace_black_24dp)
|
||||
|
||||
if (toolbar != null && !PreferenceUtil.getInstance().fullScreenMode) {
|
||||
val params = toolbar!!.layoutParams as ViewGroup.MarginLayoutParams
|
||||
params.topMargin = RetroUtil.getStatusBarHeight()
|
||||
toolbar!!.layoutParams = params
|
||||
}
|
||||
|
||||
appBarLayout?.apply {
|
||||
addOnOffsetChangedListener(object : AppBarStateChangeListener() {
|
||||
override fun onStateChanged(appBarLayout: AppBarLayout, state: AppBarStateChangeListener.State) {
|
||||
val color: Int = when (state) {
|
||||
AppBarStateChangeListener.State.COLLAPSED -> {
|
||||
setLightStatusbar(ColorUtil.isColorLight(ThemeStore.primaryColor(this@AlbumDetailsActivity)))
|
||||
ThemeStore.primaryColor(this@AlbumDetailsActivity)
|
||||
}
|
||||
AppBarStateChangeListener.State.EXPANDED, AppBarStateChangeListener.State.IDLE -> {
|
||||
setLightStatusbar(false)
|
||||
Color.TRANSPARENT
|
||||
}
|
||||
}
|
||||
ToolbarContentTintHelper.setToolbarContentColorBasedOnToolbarColor(this@AlbumDetailsActivity, toolbar, color)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
override fun onPause() {
|
||||
super.onPause()
|
||||
albumDetailsPresenter.unsubscribe()
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
super.onDestroy()
|
||||
disposable.dispose()
|
||||
}
|
||||
|
||||
override fun loading() {
|
||||
|
||||
}
|
||||
|
||||
override fun showEmptyView() {
|
||||
|
||||
}
|
||||
|
||||
override fun completed() {
|
||||
ActivityCompat.startPostponedEnterTransition(this)
|
||||
}
|
||||
|
||||
override fun showData(list: Album) {
|
||||
if (list.songs!!.isEmpty()) {
|
||||
finish()
|
||||
return
|
||||
}
|
||||
this.album = list
|
||||
|
||||
albumTitle.text = list.title
|
||||
albumText.text = String.format("%s • %s • %s", list.artistName, MusicUtil.getYearString(list.year), MusicUtil.getReadableDurationString(MusicUtil.getTotalDuration(this, list.songs)))
|
||||
|
||||
loadAlbumCover()
|
||||
loadMoreFrom(list)
|
||||
|
||||
simpleSongAdapter.swapDataSet(list.songs)
|
||||
}
|
||||
|
||||
private lateinit var artistImage: ImageView
|
||||
|
||||
private fun loadMoreFrom(album: Album) {
|
||||
disposable.add(ArtistLoader.getArtist(this, album.artistId)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.map {
|
||||
GlideApp.with(this@AlbumDetailsActivity)
|
||||
.asBitmapPalette()
|
||||
.load(RetroGlideExtension.getArtistModel(it))
|
||||
.transition(RetroGlideExtension.getDefaultTransition())
|
||||
.artistOptions(it)
|
||||
.dontAnimate()
|
||||
.into(object : RetroMusicColoredTarget(artistImage) {
|
||||
override fun onColorReady(color: Int) {
|
||||
|
||||
}
|
||||
})
|
||||
return@map it.albums!!
|
||||
}
|
||||
.map { it.filter { albumSearch -> albumSearch.id != album.id } }
|
||||
.subscribe {
|
||||
for (albumFinal in it) {
|
||||
if (albumFinal.id == album.id)
|
||||
println("$albumFinal -> $album")
|
||||
}
|
||||
if (it.isEmpty()) {
|
||||
return@subscribe
|
||||
}
|
||||
moreTitle.visibility = View.VISIBLE
|
||||
moreRecyclerView.visibility = View.VISIBLE
|
||||
moreTitle.text = String.format("More from %s", album.artistName)
|
||||
|
||||
val albumAdapter = HorizontalAlbumAdapter(this, it as ArrayList<Album>, false, null)
|
||||
moreRecyclerView.layoutManager = GridLayoutManager(this, 1, GridLayoutManager.HORIZONTAL, false)
|
||||
moreRecyclerView.adapter = albumAdapter
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
private fun loadAlbumCover() {
|
||||
GlideApp.with(this)
|
||||
.asBitmapPalette()
|
||||
.load(RetroGlideExtension.getSongModel(album!!.safeGetFirstSong()))
|
||||
.transition(RetroGlideExtension.getDefaultTransition())
|
||||
.songOptions(album!!.safeGetFirstSong())
|
||||
.dontAnimate()
|
||||
.into(object : RetroMusicColoredTarget(image as ImageView) {
|
||||
override fun onColorReady(color: Int) {
|
||||
setColors(color)
|
||||
}
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
private fun setColors(color: Int) {
|
||||
val themeColor = if (PreferenceUtil.getInstance().adaptiveColor) color else ThemeStore.accentColor(this)
|
||||
songTitle.setTextColor(themeColor)
|
||||
moreTitle.setTextColor(themeColor)
|
||||
|
||||
actionShuffleAll.backgroundTintList = ColorStateList.valueOf(themeColor)
|
||||
ColorStateList.valueOf(MaterialValueHelper.getPrimaryTextColor(this, ColorUtil.isColorLight(themeColor))).apply {
|
||||
actionShuffleAll.setTextColor(this)
|
||||
actionShuffleAll.iconTint = this
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
override fun onCreateOptionsMenu(menu: Menu): Boolean {
|
||||
menuInflater.inflate(R.menu.menu_album_detail, menu)
|
||||
val sortOrder = menu.findItem(R.id.action_sort_order)
|
||||
setUpSortOrderMenu(sortOrder.subMenu)
|
||||
return super.onCreateOptionsMenu(menu)
|
||||
}
|
||||
|
||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||
return handleSortOrderMenuItem(item)
|
||||
}
|
||||
|
||||
private fun handleSortOrderMenuItem(item: MenuItem): Boolean {
|
||||
var sortOrder: String? = null
|
||||
val songs = simpleSongAdapter.dataSet
|
||||
when (item.itemId) {
|
||||
R.id.action_play_next -> {
|
||||
MusicPlayerRemote.playNext(songs)
|
||||
return true
|
||||
}
|
||||
R.id.action_add_to_current_playing -> {
|
||||
MusicPlayerRemote.enqueue(songs)
|
||||
return true
|
||||
}
|
||||
R.id.action_add_to_playlist -> {
|
||||
AddToPlaylistDialog.create(songs).show(supportFragmentManager, "ADD_PLAYLIST")
|
||||
return true
|
||||
}
|
||||
R.id.action_delete_from_device -> {
|
||||
DeleteSongsDialog.create(songs).show(supportFragmentManager, "DELETE_SONGS")
|
||||
return true
|
||||
}
|
||||
android.R.id.home -> {
|
||||
super.onBackPressed()
|
||||
return true
|
||||
}
|
||||
R.id.action_tag_editor -> {
|
||||
val intent = Intent(this, AlbumTagEditorActivity::class.java)
|
||||
intent.putExtra(AbsTagEditorActivity.EXTRA_ID, album!!.id)
|
||||
startActivityForResult(intent, TAG_EDITOR_REQUEST)
|
||||
return true
|
||||
}
|
||||
R.id.action_go_to_artist -> {
|
||||
NavigationUtil.goToArtist(this, album!!.artistId)
|
||||
return true
|
||||
}
|
||||
/*Sort*/
|
||||
R.id.action_sort_order_title -> sortOrder = AlbumSongSortOrder.SONG_A_Z
|
||||
R.id.action_sort_order_title_desc -> sortOrder = AlbumSongSortOrder.SONG_Z_A
|
||||
R.id.action_sort_order_track_list -> sortOrder = AlbumSongSortOrder.SONG_TRACK_LIST
|
||||
R.id.action_sort_order_artist_song_duration -> sortOrder = AlbumSongSortOrder.SONG_DURATION
|
||||
}
|
||||
if (sortOrder != null) {
|
||||
item.isChecked = true
|
||||
setSaveSortOrder(sortOrder)
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
private fun setUpSortOrderMenu(sortOrder: SubMenu) {
|
||||
when (savedSortOrder) {
|
||||
AlbumSongSortOrder.SONG_A_Z -> sortOrder.findItem(R.id.action_sort_order_title).isChecked = true
|
||||
AlbumSongSortOrder.SONG_Z_A -> sortOrder.findItem(R.id.action_sort_order_title_desc).isChecked = true
|
||||
AlbumSongSortOrder.SONG_TRACK_LIST -> sortOrder.findItem(R.id.action_sort_order_track_list).isChecked = true
|
||||
AlbumSongSortOrder.SONG_DURATION -> sortOrder.findItem(R.id.action_sort_order_artist_song_duration).isChecked = true
|
||||
}
|
||||
}
|
||||
|
||||
private fun setSaveSortOrder(sortOrder: String?) {
|
||||
PreferenceUtil.getInstance().albumDetailSongSortOrder = sortOrder
|
||||
reload()
|
||||
}
|
||||
|
||||
override fun onMediaStoreChanged() {
|
||||
super.onMediaStoreChanged()
|
||||
reload()
|
||||
}
|
||||
|
||||
private fun reload() {
|
||||
albumDetailsPresenter.subscribe()
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
const val EXTRA_ALBUM_ID = "extra_album_id"
|
||||
private const val TAG_EDITOR_REQUEST = 2001
|
||||
}
|
||||
}
|
368
app/src/main/java/code/name/monkey/retromusic/activities/ArtistDetailActivity.kt
Executable file
368
app/src/main/java/code/name/monkey/retromusic/activities/ArtistDetailActivity.kt
Executable file
|
@ -0,0 +1,368 @@
|
|||
package code.name.monkey.retromusic.activities
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.Intent
|
||||
import android.content.res.ColorStateList
|
||||
import android.graphics.Color
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.text.Html
|
||||
import android.text.Spanned
|
||||
import android.transition.Slide
|
||||
import android.view.*
|
||||
import android.view.animation.AnimationUtils
|
||||
import android.widget.Toast
|
||||
import androidx.core.app.ActivityCompat
|
||||
import androidx.core.widget.NestedScrollView
|
||||
import androidx.recyclerview.widget.DefaultItemAnimator
|
||||
import androidx.recyclerview.widget.GridLayoutManager
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import code.name.monkey.appthemehelper.ThemeStore
|
||||
import code.name.monkey.appthemehelper.util.ColorUtil
|
||||
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.dialogs.AddToPlaylistDialog
|
||||
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.misc.AppBarStateChangeListener
|
||||
import code.name.monkey.retromusic.model.Artist
|
||||
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.*
|
||||
import kotlinx.android.synthetic.main.activity_artist_details.*
|
||||
import retrofit2.Call
|
||||
import retrofit2.Callback
|
||||
import retrofit2.Response
|
||||
import java.util.*
|
||||
import kotlin.collections.ArrayList
|
||||
|
||||
class ArtistDetailActivity : AbsSlidingMusicPanelActivity(), ArtistDetailContract.ArtistsDetailsView {
|
||||
|
||||
private var biography: Spanned? = null
|
||||
private lateinit var artist: Artist
|
||||
private var lastFMRestClient: LastFMRestClient? = null
|
||||
private var artistDetailsPresenter: ArtistDetailsPresenter? = null
|
||||
private lateinit var songAdapter: SimpleSongAdapter
|
||||
private lateinit var albumAdapter: AlbumAdapter
|
||||
private var forceDownload: Boolean = false
|
||||
|
||||
private fun setupWindowTransitions() {
|
||||
val slide = Slide(Gravity.BOTTOM)
|
||||
slide.interpolator = AnimationUtils.loadInterpolator(this, android.R.interpolator.linear_out_slow_in)
|
||||
window.enterTransition = slide
|
||||
}
|
||||
|
||||
|
||||
override fun createContentView(): View {
|
||||
return wrapSlidingMusicPanel(R.layout.activity_artist_details)
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
setDrawUnderStatusBar()
|
||||
setupWindowTransitions()
|
||||
super.onCreate(savedInstanceState)
|
||||
collapsingToolbarLayout?.setBackgroundColor(ThemeStore.primaryColor(this))
|
||||
toggleBottomNavigationView(true)
|
||||
setNavigationbarColorAuto()
|
||||
setLightNavigationBar(true)
|
||||
|
||||
ActivityCompat.postponeEnterTransition(this)
|
||||
|
||||
lastFMRestClient = LastFMRestClient(this)
|
||||
|
||||
setUpViews()
|
||||
|
||||
artistDetailsPresenter = ArtistDetailsPresenter(this, intent.extras!!)
|
||||
artistDetailsPresenter!!.subscribe()
|
||||
|
||||
contentContainer.setOnScrollChangeListener { _: NestedScrollView?, _: Int, scrollY: Int, _: Int, oldScrollY: Int ->
|
||||
run {
|
||||
if (scrollY > oldScrollY) {
|
||||
actionShuffleAll.shrink(true)
|
||||
}
|
||||
if (scrollY < oldScrollY) {
|
||||
actionShuffleAll.extend(true)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
biographyText.setOnClickListener {
|
||||
if (biographyText.maxLines == 4) {
|
||||
biographyText.maxLines = Integer.MAX_VALUE
|
||||
} else {
|
||||
biographyText.maxLines = 4
|
||||
}
|
||||
}
|
||||
actionShuffleAll.setOnClickListener { MusicPlayerRemote.openAndShuffleQueue(getArtist().songs, true) }
|
||||
}
|
||||
|
||||
private fun setUpViews() {
|
||||
setupRecyclerView()
|
||||
setupToolbarMarginHeight()
|
||||
setupContainerHeight()
|
||||
}
|
||||
|
||||
private fun setupContainerHeight() {
|
||||
if (imageContainer != null) {
|
||||
val params = imageContainer!!.layoutParams
|
||||
params.width = DensityUtil.getScreenHeight(this) / 2
|
||||
imageContainer!!.layoutParams = params
|
||||
}
|
||||
}
|
||||
|
||||
private fun setupToolbarMarginHeight() {
|
||||
val primaryColor = ThemeStore.primaryColor(this)
|
||||
TintHelper.setTintAuto(contentContainer!!, primaryColor, true)
|
||||
if (collapsingToolbarLayout != null) {
|
||||
collapsingToolbarLayout!!.setContentScrimColor(primaryColor)
|
||||
collapsingToolbarLayout!!.setStatusBarScrimColor(ColorUtil.darkenColor(primaryColor))
|
||||
}
|
||||
|
||||
toolbar!!.setNavigationIcon(R.drawable.ic_keyboard_backspace_black_24dp)
|
||||
setSupportActionBar(toolbar)
|
||||
|
||||
supportActionBar!!.title = null
|
||||
|
||||
|
||||
if (toolbar != null && !PreferenceUtil.getInstance().fullScreenMode) {
|
||||
val params = toolbar!!.layoutParams as ViewGroup.MarginLayoutParams
|
||||
params.topMargin = RetroUtil.getStatusBarHeight()
|
||||
toolbar!!.layoutParams = params
|
||||
}
|
||||
|
||||
appBarLayout?.addOnOffsetChangedListener(object : AppBarStateChangeListener() {
|
||||
override fun onStateChanged(appBarLayout: AppBarLayout, state: AppBarStateChangeListener.State) {
|
||||
val color: Int = when (state) {
|
||||
AppBarStateChangeListener.State.COLLAPSED -> {
|
||||
setLightStatusbar(ColorUtil.isColorLight(ThemeStore.primaryColor(appBarLayout.context)))
|
||||
ThemeStore.primaryColor(appBarLayout.context)
|
||||
}
|
||||
AppBarStateChangeListener.State.EXPANDED, AppBarStateChangeListener.State.IDLE -> {
|
||||
setLightStatusbar(false)
|
||||
Color.TRANSPARENT
|
||||
}
|
||||
|
||||
}
|
||||
ToolbarContentTintHelper.setToolbarContentColorBasedOnToolbarColor(appBarLayout.context, toolbar, color)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
private fun setupRecyclerView() {
|
||||
albumAdapter = HorizontalAlbumAdapter(this, ArrayList(), false, null)
|
||||
albumRecyclerView.apply {
|
||||
itemAnimator = DefaultItemAnimator()
|
||||
layoutManager = GridLayoutManager(this.context, 1, GridLayoutManager.HORIZONTAL, false)
|
||||
adapter = albumAdapter
|
||||
}
|
||||
songAdapter = SimpleSongAdapter(this, ArrayList(), R.layout.item_song, false)
|
||||
recyclerView.apply {
|
||||
itemAnimator = DefaultItemAnimator()
|
||||
layoutManager = LinearLayoutManager(this.context)
|
||||
adapter = songAdapter
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
||||
super.onActivityResult(requestCode, resultCode, data)
|
||||
when (requestCode) {
|
||||
REQUEST_CODE_SELECT_IMAGE -> if (resultCode == Activity.RESULT_OK) {
|
||||
CustomArtistImageUtil.getInstance(this).setCustomArtistImage(artist!!, data!!.data!!)
|
||||
}
|
||||
else -> if (resultCode == Activity.RESULT_OK) {
|
||||
reload()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onPause() {
|
||||
super.onPause()
|
||||
artistDetailsPresenter!!.unsubscribe()
|
||||
}
|
||||
|
||||
override fun loading() {}
|
||||
|
||||
override fun showEmptyView() {
|
||||
|
||||
}
|
||||
|
||||
override fun completed() {
|
||||
ActivityCompat.startPostponedEnterTransition(this)
|
||||
}
|
||||
|
||||
override fun showData(list: Artist) {
|
||||
setArtist(list)
|
||||
}
|
||||
|
||||
private fun getArtist(): Artist {
|
||||
if (artist == null) {
|
||||
artist = Artist()
|
||||
}
|
||||
return this.artist!!
|
||||
}
|
||||
|
||||
private fun setArtist(artist: Artist) {
|
||||
if (artist.songCount <= 0) {
|
||||
finish()
|
||||
}
|
||||
this.artist = artist
|
||||
loadArtistImage()
|
||||
|
||||
if (RetroUtil.isAllowedToDownloadMetadata(this)) {
|
||||
loadBiography()
|
||||
}
|
||||
artistTitle.text = artist.name
|
||||
text.text = String.format("%s • %s", MusicUtil.getArtistInfoString(this, artist), MusicUtil
|
||||
.getReadableDurationString(MusicUtil.getTotalDuration(this, artist.songs)))
|
||||
//val songs = artist.songs.sortedWith(compareBy { it.title }) as ArrayList<Song>
|
||||
songAdapter.swapDataSet(artist.songs)
|
||||
|
||||
//val albums = artist.albums?.sortedWith(compareBy { it.artistName }) as ArrayList<Album>
|
||||
albumAdapter.swapDataSet(artist.albums!!)
|
||||
}
|
||||
|
||||
private fun loadBiography(lang: String? = Locale.getDefault().language) {
|
||||
biography = null
|
||||
|
||||
lastFMRestClient!!.apiService
|
||||
.getArtistInfo(getArtist().name, lang, null)
|
||||
.enqueue(object : Callback<LastFmArtist> {
|
||||
override fun onResponse(call: Call<LastFmArtist>,
|
||||
response: Response<LastFmArtist>) {
|
||||
val lastFmArtist = response.body()
|
||||
if (lastFmArtist != null && lastFmArtist.artist != null) {
|
||||
val bioContent = lastFmArtist.artist.bio.content
|
||||
if (bioContent != null && !bioContent.trim { it <= ' ' }.isEmpty()) {
|
||||
//TransitionManager.beginDelayedTransition(titleContainer);
|
||||
biographyText.visibility = View.VISIBLE
|
||||
biographyTitle.visibility = View.VISIBLE
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
||||
biography = Html.fromHtml(bioContent, Html.FROM_HTML_MODE_LEGACY)
|
||||
} else {
|
||||
biography = Html.fromHtml(bioContent)
|
||||
}
|
||||
biographyText!!.text = biography
|
||||
}
|
||||
}
|
||||
|
||||
// If the "lang" parameter is set and no biography is given, retry with default language
|
||||
if (biography == null && lang != null) {
|
||||
loadBiography(null)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onFailure(call: Call<LastFmArtist>, t: Throwable) {
|
||||
t.printStackTrace()
|
||||
biography = null
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
private fun loadArtistImage() {
|
||||
GlideApp.with(this)
|
||||
.asBitmapPalette()
|
||||
.load(RetroGlideExtension.getArtistModel(artist, forceDownload))
|
||||
.transition(RetroGlideExtension.getDefaultTransition())
|
||||
.artistOptions(artist)
|
||||
.dontAnimate()
|
||||
.into(object : RetroMusicColoredTarget(artistImage) {
|
||||
override fun onColorReady(color: Int) {
|
||||
setColors(color)
|
||||
}
|
||||
})
|
||||
forceDownload = false;
|
||||
}
|
||||
|
||||
private fun setColors(color: Int) {
|
||||
|
||||
val textColor = if (PreferenceUtil.getInstance().adaptiveColor) color else ThemeStore.accentColor(this)
|
||||
|
||||
albumTitle.setTextColor(textColor)
|
||||
songTitle.setTextColor(textColor)
|
||||
biographyTitle.setTextColor(textColor)
|
||||
|
||||
actionShuffleAll.backgroundTintList = ColorStateList.valueOf(textColor)
|
||||
ColorStateList.valueOf(MaterialValueHelper.getPrimaryTextColor(this, ColorUtil.isColorLight(textColor))).apply {
|
||||
actionShuffleAll.setTextColor(this)
|
||||
actionShuffleAll.iconTint = this
|
||||
}
|
||||
|
||||
findViewById<View>(R.id.root).setBackgroundColor(ThemeStore.primaryColor(this))
|
||||
}
|
||||
|
||||
|
||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||
return handleSortOrderMenuItem(item)
|
||||
}
|
||||
|
||||
private fun handleSortOrderMenuItem(item: MenuItem): Boolean {
|
||||
val songs = getArtist().songs
|
||||
when (item.itemId) {
|
||||
android.R.id.home -> {
|
||||
super.onBackPressed()
|
||||
return true
|
||||
}
|
||||
R.id.action_play_next -> {
|
||||
MusicPlayerRemote.playNext(songs)
|
||||
return true
|
||||
}
|
||||
R.id.action_add_to_current_playing -> {
|
||||
MusicPlayerRemote.enqueue(songs)
|
||||
return true
|
||||
}
|
||||
R.id.action_add_to_playlist -> {
|
||||
AddToPlaylistDialog.create(songs).show(supportFragmentManager, "ADD_PLAYLIST")
|
||||
return true
|
||||
}
|
||||
R.id.action_set_artist_image -> {
|
||||
val intent = Intent(Intent.ACTION_GET_CONTENT)
|
||||
intent.type = "image/*"
|
||||
startActivityForResult(Intent.createChooser(intent, getString(R.string.pick_from_local_storage)), REQUEST_CODE_SELECT_IMAGE)
|
||||
return true
|
||||
}
|
||||
R.id.action_reset_artist_image -> {
|
||||
Toast.makeText(this@ArtistDetailActivity, resources.getString(R.string.updating),
|
||||
Toast.LENGTH_SHORT).show()
|
||||
CustomArtistImageUtil.getInstance(this@ArtistDetailActivity).resetCustomArtistImage(artist!!)
|
||||
forceDownload = true
|
||||
return true
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
override fun onCreateOptionsMenu(menu: Menu): Boolean {
|
||||
menuInflater.inflate(R.menu.menu_artist_detail, menu)
|
||||
return super.onCreateOptionsMenu(menu)
|
||||
}
|
||||
|
||||
override fun onMediaStoreChanged() {
|
||||
super.onMediaStoreChanged()
|
||||
reload()
|
||||
}
|
||||
|
||||
private fun reload() {
|
||||
artistDetailsPresenter!!.unsubscribe()
|
||||
artistDetailsPresenter!!.subscribe()
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
const val EXTRA_ARTIST_ID = "extra_artist_id"
|
||||
const val REQUEST_CODE_SELECT_IMAGE = 9003
|
||||
}
|
||||
}
|
|
@ -0,0 +1,190 @@
|
|||
package code.name.monkey.retromusic.activities
|
||||
|
||||
import android.os.Bundle
|
||||
import android.transition.TransitionManager
|
||||
import android.view.LayoutInflater
|
||||
import android.view.MenuItem
|
||||
import android.view.View
|
||||
import android.widget.AdapterView
|
||||
import android.widget.ArrayAdapter
|
||||
import android.widget.SeekBar
|
||||
import android.widget.TextView
|
||||
import code.name.monkey.appthemehelper.ThemeStore
|
||||
import code.name.monkey.appthemehelper.util.ColorUtil
|
||||
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.helper.EqualizerHelper
|
||||
import code.name.monkey.retromusic.activities.base.AbsMusicServiceActivity
|
||||
import code.name.monkey.retromusic.util.ViewUtil
|
||||
import kotlinx.android.synthetic.main.activity_equalizer.*
|
||||
|
||||
/**
|
||||
* @author Hemanth S (h4h13).
|
||||
*/
|
||||
|
||||
class EqualizerActivity : AbsMusicServiceActivity(), AdapterView.OnItemSelectedListener {
|
||||
|
||||
private val seekBarChangeListener = object : SeekBar.OnSeekBarChangeListener {
|
||||
override fun onProgressChanged(seekBar: SeekBar, progress: Int, fromUser: Boolean) {
|
||||
if (fromUser) {
|
||||
if (seekBar === bassBoostStrength) {
|
||||
bassBoost.isEnabled = progress > 0
|
||||
EqualizerHelper.instance!!.bassBoostStrength = progress
|
||||
EqualizerHelper.instance!!.isBassBoostEnabled = progress > 0
|
||||
} else if (seekBar === virtualizerStrength) {
|
||||
virtualizer.isEnabled = progress > 0
|
||||
EqualizerHelper.instance!!.isVirtualizerEnabled = progress > 0
|
||||
EqualizerHelper.instance!!.virtualizerStrength = progress
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onStartTrackingTouch(seekBar: SeekBar) {
|
||||
|
||||
}
|
||||
|
||||
override fun onStopTrackingTouch(seekBar: SeekBar) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private var presetsNamesAdapter: ArrayAdapter<String>? = null
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(R.layout.activity_equalizer)
|
||||
|
||||
setStatusbarColorAuto()
|
||||
setNavigationbarColorAuto()
|
||||
setTaskDescriptionColorAuto()
|
||||
setLightNavigationBar(true)
|
||||
|
||||
setupToolbar()
|
||||
|
||||
equalizerSwitch.isChecked = EqualizerHelper.instance!!.equalizer.enabled
|
||||
equalizerSwitch.setBackgroundColor(ThemeStore.accentColor(this))
|
||||
val widgetColor = MaterialValueHelper.getPrimaryTextColor(this, ColorUtil.isColorLight(ThemeStore.accentColor(this)))
|
||||
equalizerSwitch.setTextColor(widgetColor)
|
||||
TintHelper.setTintAuto(equalizerSwitch, widgetColor, false)
|
||||
equalizerSwitch.setOnCheckedChangeListener { buttonView, isChecked ->
|
||||
when (buttonView.id) {
|
||||
R.id.equalizerSwitch -> {
|
||||
EqualizerHelper.instance!!.equalizer.enabled = isChecked
|
||||
TransitionManager.beginDelayedTransition(content)
|
||||
content.visibility = if (isChecked) View.VISIBLE else View.GONE
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
presetsNamesAdapter = ArrayAdapter(this, android.R.layout.simple_list_item_1)
|
||||
presets.adapter = presetsNamesAdapter
|
||||
presets.onItemSelectedListener = this
|
||||
|
||||
bassBoostStrength.progress = EqualizerHelper.instance!!.bassBoostStrength
|
||||
ViewUtil.setProgressDrawable(bassBoostStrength, ThemeStore.accentColor(this))
|
||||
bassBoostStrength.setOnSeekBarChangeListener(seekBarChangeListener)
|
||||
|
||||
virtualizerStrength.progress = EqualizerHelper.instance!!.virtualizerStrength
|
||||
ViewUtil.setProgressDrawable(virtualizerStrength, ThemeStore.accentColor(this))
|
||||
virtualizerStrength.setOnSeekBarChangeListener(seekBarChangeListener)
|
||||
|
||||
setupUI()
|
||||
addPresets()
|
||||
}
|
||||
|
||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||
if (item.itemId == android.R.id.home) {
|
||||
onBackPressed()
|
||||
return true
|
||||
}
|
||||
return super.onOptionsItemSelected(item)
|
||||
}
|
||||
|
||||
private fun setupToolbar() {
|
||||
bannerTitle.setTextColor(ThemeStore.textColorPrimary(this))
|
||||
val primaryColor = ThemeStore.primaryColor(this)
|
||||
appBarLayout.setBackgroundColor(primaryColor)
|
||||
toolbar.apply {
|
||||
setBackgroundColor(primaryColor)
|
||||
setNavigationIcon(R.drawable.ic_keyboard_backspace_black_24dp)
|
||||
setNavigationOnClickListener { onBackPressed() }
|
||||
setSupportActionBar(this)
|
||||
ToolbarContentTintHelper.colorBackButton(this, ThemeStore.textColorSecondary(context))
|
||||
}
|
||||
title = null
|
||||
}
|
||||
|
||||
private fun addPresets() {
|
||||
presetsNamesAdapter!!.clear()
|
||||
presetsNamesAdapter!!.add("Custom")
|
||||
for (j in 0 until EqualizerHelper.instance!!.equalizer.numberOfPresets) {
|
||||
presetsNamesAdapter!!
|
||||
.add(EqualizerHelper.instance!!.equalizer.getPresetName(j.toShort()))
|
||||
presetsNamesAdapter!!.notifyDataSetChanged()
|
||||
}
|
||||
presets.setSelection(EqualizerHelper.instance!!.equalizer.currentPreset.toInt() + 1)
|
||||
}
|
||||
|
||||
private fun setupUI() {
|
||||
frequencyBands.removeAllViews()
|
||||
val bands: Short
|
||||
try {
|
||||
// get number of supported bands
|
||||
bands = EqualizerHelper.instance!!.numberOfBands.toShort()
|
||||
|
||||
// for each of the supported bands, we will set up a slider from -10dB to 10dB boost/attenuation,
|
||||
// as well as text labels to assist the user
|
||||
for (i in 0 until bands) {
|
||||
|
||||
val view = LayoutInflater.from(this).inflate(R.layout.retro_seekbar, frequencyBands, false)
|
||||
val freqTextView = view.findViewById<TextView>(R.id.hurtz)
|
||||
freqTextView.text = String.format("%d Hz", EqualizerHelper.instance!!.getCenterFreq(i) / 1000)
|
||||
|
||||
val minDbTextView = view.findViewById<TextView>(R.id.minus_db)
|
||||
minDbTextView.text = String.format("%d dB", EqualizerHelper.instance!!.bandLevelLow / 100)
|
||||
|
||||
val maxDbTextView = view.findViewById<TextView>(R.id.plus_db)
|
||||
maxDbTextView.text = String.format("%d dB", EqualizerHelper.instance!!.bandLevelHigh / 100)
|
||||
|
||||
val bar = view.findViewById<SeekBar>(R.id.seekbar)
|
||||
ViewUtil.setProgressDrawable(bar, ThemeStore.accentColor(this))
|
||||
bar.max = EqualizerHelper.instance!!.bandLevelHigh - EqualizerHelper.instance!!
|
||||
.bandLevelLow
|
||||
bar.progress = EqualizerHelper.instance!!.getBandLevel(i) - EqualizerHelper.instance!!
|
||||
.bandLevelLow
|
||||
bar.setOnSeekBarChangeListener(object : SeekBar.OnSeekBarChangeListener {
|
||||
override fun onProgressChanged(seekBar: SeekBar, progress: Int, fromUser: Boolean) {
|
||||
EqualizerHelper.instance!!.setBandLevel(i,
|
||||
progress + EqualizerHelper.instance!!.bandLevelLow)
|
||||
if (fromUser) {
|
||||
presets.setSelection(0)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onStartTrackingTouch(seekBar: SeekBar) {}
|
||||
|
||||
override fun onStopTrackingTouch(seekBar: SeekBar) {}
|
||||
})
|
||||
|
||||
frequencyBands.addView(view)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
override fun onItemSelected(parent: AdapterView<*>, view: View, position: Int, id: Long) {
|
||||
if (position == 0) {
|
||||
return
|
||||
}
|
||||
EqualizerHelper.instance!!.equalizer.usePreset((position - 1).toShort())
|
||||
setupUI()
|
||||
}
|
||||
|
||||
override fun onNothingSelected(parent: AdapterView<*>) {
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,182 @@
|
|||
package code.name.monkey.retromusic.activities
|
||||
|
||||
import android.content.res.ColorStateList
|
||||
import android.graphics.Color
|
||||
import android.os.Bundle
|
||||
import android.view.Menu
|
||||
import android.view.MenuItem
|
||||
import android.view.View
|
||||
import androidx.recyclerview.widget.DefaultItemAnimator
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
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.appthemehelper.util.ToolbarContentTintHelper
|
||||
import code.name.monkey.retromusic.R
|
||||
import code.name.monkey.retromusic.helper.MusicPlayerRemote
|
||||
import code.name.monkey.retromusic.helper.menu.GenreMenuHelper
|
||||
import code.name.monkey.retromusic.interfaces.CabHolder
|
||||
import code.name.monkey.retromusic.model.Genre
|
||||
import code.name.monkey.retromusic.model.Song
|
||||
import code.name.monkey.retromusic.mvp.contract.GenreDetailsContract
|
||||
import code.name.monkey.retromusic.mvp.presenter.GenreDetailsPresenter
|
||||
import code.name.monkey.retromusic.activities.base.AbsSlidingMusicPanelActivity
|
||||
import code.name.monkey.retromusic.adapter.song.SongAdapter
|
||||
import code.name.monkey.retromusic.util.RetroColorUtil
|
||||
import code.name.monkey.retromusic.util.ViewUtil
|
||||
import com.afollestad.materialcab.MaterialCab
|
||||
import kotlinx.android.synthetic.main.activity_playlist_detail.*
|
||||
import java.util.*
|
||||
|
||||
/**
|
||||
* @author Hemanth S (h4h13).
|
||||
*/
|
||||
|
||||
class GenreDetailsActivity : AbsSlidingMusicPanelActivity(), GenreDetailsContract.GenreDetailsView, CabHolder {
|
||||
|
||||
private var genre: Genre? = null
|
||||
private var presenter: GenreDetailsPresenter? = null
|
||||
private var songAdapter: SongAdapter? = null
|
||||
private var cab: MaterialCab? = null
|
||||
|
||||
private fun checkIsEmpty() {
|
||||
empty!!.visibility = if (songAdapter!!.itemCount == 0) View.VISIBLE else View.GONE
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
setDrawUnderStatusBar()
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
|
||||
setStatusbarColor(Color.TRANSPARENT)
|
||||
setNavigationbarColorAuto()
|
||||
setTaskDescriptionColorAuto()
|
||||
setLightNavigationBar(true)
|
||||
setLightStatusbar(ColorUtil.isColorLight(ThemeStore.primaryColor(this)))
|
||||
toggleBottomNavigationView(true)
|
||||
|
||||
genre = intent.extras!!.getParcelable(EXTRA_GENRE_ID)
|
||||
presenter = GenreDetailsPresenter(this, genre!!.id)
|
||||
|
||||
setUpToolBar()
|
||||
setupRecyclerView()
|
||||
actionShuffleAll.setOnClickListener { MusicPlayerRemote.openAndShuffleQueue(songAdapter!!.dataSet, true) }
|
||||
}
|
||||
|
||||
private fun setUpToolBar() {
|
||||
bannerTitle!!.text = genre!!.name
|
||||
bannerTitle!!.setTextColor(ThemeStore.textColorPrimary(this))
|
||||
|
||||
val primaryColor = ThemeStore.primaryColor(this)
|
||||
appBarLayout.setBackgroundColor(primaryColor)
|
||||
toolbar.apply {
|
||||
setNavigationIcon(R.drawable.ic_keyboard_backspace_black_24dp)
|
||||
setBackgroundColor(primaryColor)
|
||||
setSupportActionBar(this)
|
||||
ToolbarContentTintHelper.colorBackButton(this, ThemeStore.textColorSecondary(this@GenreDetailsActivity))
|
||||
}
|
||||
actionShuffleAll.backgroundTintList = ColorStateList.valueOf(ThemeStore.accentColor(this))
|
||||
ColorStateList.valueOf(MaterialValueHelper.getPrimaryTextColor(this, ColorUtil.isColorLight(ThemeStore.accentColor(this)))).apply {
|
||||
actionShuffleAll.setTextColor(this)
|
||||
actionShuffleAll.iconTint = this
|
||||
}
|
||||
title = null
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
presenter!!.subscribe()
|
||||
}
|
||||
|
||||
override fun onPause() {
|
||||
super.onPause()
|
||||
presenter!!.unsubscribe()
|
||||
}
|
||||
|
||||
override fun createContentView(): View {
|
||||
return wrapSlidingMusicPanel(R.layout.activity_playlist_detail)
|
||||
}
|
||||
|
||||
|
||||
override fun loading() {
|
||||
|
||||
}
|
||||
|
||||
override fun showEmptyView() {
|
||||
|
||||
}
|
||||
|
||||
override fun completed() {
|
||||
|
||||
}
|
||||
|
||||
override fun onCreateOptionsMenu(menu: Menu): Boolean {
|
||||
menuInflater.inflate(R.menu.menu_genre_detail, menu)
|
||||
return super.onCreateOptionsMenu(menu)
|
||||
}
|
||||
|
||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||
if (item.itemId == android.R.id.home) {
|
||||
onBackPressed()
|
||||
}
|
||||
return GenreMenuHelper.handleMenuClick(this, genre!!, item)
|
||||
}
|
||||
|
||||
private fun setupRecyclerView() {
|
||||
ViewUtil.setUpFastScrollRecyclerViewColor(this, recyclerView, ThemeStore.accentColor(this))
|
||||
songAdapter = SongAdapter(this, ArrayList(), R.layout.item_list, false, this)
|
||||
recyclerView.apply {
|
||||
itemAnimator = DefaultItemAnimator()
|
||||
layoutManager = LinearLayoutManager(this@GenreDetailsActivity)
|
||||
adapter = songAdapter
|
||||
}.addOnScrollListener(object : RecyclerView.OnScrollListener() {
|
||||
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
|
||||
super.onScrolled(recyclerView, dx, dy)
|
||||
if (dy > 0) {
|
||||
actionShuffleAll.shrink(true)
|
||||
} else if (dy < 0) {
|
||||
actionShuffleAll.extend(true)
|
||||
}
|
||||
}
|
||||
})
|
||||
songAdapter!!.registerAdapterDataObserver(object : RecyclerView.AdapterDataObserver() {
|
||||
override fun onChanged() {
|
||||
super.onChanged()
|
||||
checkIsEmpty()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
override fun showData(list: ArrayList<Song>) {
|
||||
songAdapter!!.swapDataSet(list)
|
||||
}
|
||||
|
||||
override fun openCab(menuRes: Int, callback: MaterialCab.Callback): MaterialCab {
|
||||
if (cab != null && cab!!.isActive) cab!!.finish()
|
||||
cab = MaterialCab(this, R.id.cab_stub)
|
||||
.setMenu(menuRes)
|
||||
.setCloseDrawableRes(R.drawable.ic_close_white_24dp)
|
||||
.setBackgroundColor(RetroColorUtil.shiftBackgroundColorForLightText(ThemeStore.primaryColor(this)))
|
||||
.start(callback)
|
||||
return cab!!
|
||||
}
|
||||
|
||||
override fun onBackPressed() {
|
||||
if (cab != null && cab!!.isActive)
|
||||
cab!!.finish()
|
||||
else {
|
||||
recyclerView!!.stopScroll()
|
||||
super.onBackPressed()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onMediaStoreChanged() {
|
||||
super.onMediaStoreChanged()
|
||||
presenter!!.subscribe()
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val EXTRA_GENRE_ID = "extra_genre_id"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,44 @@
|
|||
package code.name.monkey.retromusic.activities
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.MenuItem
|
||||
import code.name.monkey.appthemehelper.ThemeStore
|
||||
import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper
|
||||
import code.name.monkey.retromusic.R
|
||||
import code.name.monkey.retromusic.activities.base.AbsBaseActivity
|
||||
import kotlinx.android.synthetic.main.activity_license.*
|
||||
|
||||
class LicenseActivity : AbsBaseActivity() {
|
||||
|
||||
|
||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||
if (item.itemId == android.R.id.home) {
|
||||
onBackPressed()
|
||||
return true
|
||||
}
|
||||
return super.onOptionsItemSelected(item)
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(R.layout.activity_license)
|
||||
|
||||
|
||||
setStatusbarColorAuto()
|
||||
setNavigationbarColorAuto()
|
||||
setTaskDescriptionColorAuto()
|
||||
setLightNavigationBar(true)
|
||||
|
||||
license.loadUrl("file:///android_asset/index.html")
|
||||
bannerTitle.setTextColor(ThemeStore.textColorPrimary(this))
|
||||
toolbar!!.apply {
|
||||
setNavigationIcon(R.drawable.ic_keyboard_backspace_black_24dp)
|
||||
setNavigationOnClickListener { onBackPressed() }
|
||||
setBackgroundColor(ThemeStore.primaryColor(this@LicenseActivity))
|
||||
}
|
||||
appBarLayout.setBackgroundColor(ThemeStore.primaryColor(this))
|
||||
title = null
|
||||
setSupportActionBar(toolbar)
|
||||
ToolbarContentTintHelper.colorBackButton(toolbar!!, ThemeStore.accentColor(this))
|
||||
}
|
||||
}
|
|
@ -0,0 +1,111 @@
|
|||
package code.name.monkey.retromusic.activities
|
||||
|
||||
import android.app.KeyguardManager
|
||||
import android.content.Context
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import android.view.WindowManager
|
||||
import androidx.core.view.ViewCompat
|
||||
import code.name.monkey.appthemehelper.ThemeStore
|
||||
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.activities.base.AbsMusicServiceActivity
|
||||
import code.name.monkey.retromusic.fragments.player.lockscreen.LockScreenPlayerControlsFragment
|
||||
import com.r0adkll.slidr.Slidr
|
||||
import com.r0adkll.slidr.model.SlidrConfig
|
||||
import com.r0adkll.slidr.model.SlidrListener
|
||||
import com.r0adkll.slidr.model.SlidrPosition
|
||||
import kotlinx.android.synthetic.main.activity_album.*
|
||||
|
||||
class LockScreenActivity : AbsMusicServiceActivity() {
|
||||
private var fragment: LockScreenPlayerControlsFragment? = null
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O_MR1) {
|
||||
setShowWhenLocked(true)
|
||||
setTurnScreenOn(true)
|
||||
} else {
|
||||
this.window.addFlags(WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD or
|
||||
WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED or
|
||||
WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON)
|
||||
}
|
||||
setDrawUnderStatusBar()
|
||||
setContentView(R.layout.activity_lock_screen_old_style)
|
||||
|
||||
hideStatusBar()
|
||||
setStatusbarColorAuto()
|
||||
setNavigationbarColorAuto()
|
||||
setTaskDescriptionColorAuto()
|
||||
setLightNavigationBar(true)
|
||||
|
||||
val config = SlidrConfig.Builder()
|
||||
.listener(object : SlidrListener {
|
||||
override fun onSlideStateChanged(state: Int) {
|
||||
|
||||
}
|
||||
|
||||
override fun onSlideChange(percent: Float) {
|
||||
|
||||
}
|
||||
|
||||
override fun onSlideOpened() {
|
||||
|
||||
}
|
||||
|
||||
override fun onSlideClosed() {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
val keyguardManager = getSystemService(Context.KEYGUARD_SERVICE) as KeyguardManager
|
||||
keyguardManager.requestDismissKeyguard(this@LockScreenActivity, null)
|
||||
}
|
||||
}
|
||||
})
|
||||
.position(SlidrPosition.BOTTOM)
|
||||
.build()
|
||||
|
||||
Slidr.attach(this, config)
|
||||
|
||||
fragment = supportFragmentManager.findFragmentById(R.id.playback_controls_fragment) as LockScreenPlayerControlsFragment?
|
||||
|
||||
findViewById<View>(R.id.slide).apply {
|
||||
translationY = 100f
|
||||
alpha = 0f
|
||||
ViewCompat.animate(this)
|
||||
.translationY(0f)
|
||||
.alpha(1f)
|
||||
.setDuration(1500)
|
||||
.start()
|
||||
}
|
||||
|
||||
findViewById<View>(R.id.root_layout).setBackgroundColor(ThemeStore.primaryColor(this))
|
||||
}
|
||||
|
||||
override fun onPlayingMetaChanged() {
|
||||
super.onPlayingMetaChanged()
|
||||
updateSongs()
|
||||
}
|
||||
|
||||
override fun onServiceConnected() {
|
||||
super.onServiceConnected()
|
||||
updateSongs()
|
||||
}
|
||||
|
||||
private fun updateSongs() {
|
||||
val song = MusicPlayerRemote.currentSong
|
||||
GlideApp.with(this)
|
||||
.asBitmapPalette()
|
||||
.load(RetroGlideExtension.getSongModel(song))
|
||||
.transition(RetroGlideExtension.getDefaultTransition())
|
||||
.songOptions(song)
|
||||
.dontAnimate()
|
||||
.into(object : RetroMusicColoredTarget(image) {
|
||||
override fun onColorReady(color: Int) {
|
||||
fragment!!.setDark(color)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
|
@ -0,0 +1,374 @@
|
|||
package code.name.monkey.retromusic.activities
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.res.ColorStateList
|
||||
import android.os.AsyncTask
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.text.InputType
|
||||
import android.text.TextUtils
|
||||
import android.view.*
|
||||
import androidx.annotation.StringRes
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.fragment.app.FragmentManager
|
||||
import androidx.fragment.app.FragmentStatePagerAdapter
|
||||
import androidx.viewpager.widget.ViewPager
|
||||
import code.name.monkey.appthemehelper.ThemeStore
|
||||
import code.name.monkey.appthemehelper.util.ColorUtil
|
||||
import code.name.monkey.appthemehelper.util.MaterialValueHelper
|
||||
import code.name.monkey.appthemehelper.util.TintHelper
|
||||
import code.name.monkey.retromusic.App
|
||||
import code.name.monkey.retromusic.R
|
||||
import code.name.monkey.retromusic.helper.MusicPlayerRemote
|
||||
import code.name.monkey.retromusic.helper.MusicProgressViewUpdateHelper
|
||||
import code.name.monkey.retromusic.lyrics.LrcHelper
|
||||
import code.name.monkey.retromusic.lyrics.LrcView
|
||||
import code.name.monkey.retromusic.model.Song
|
||||
import code.name.monkey.retromusic.model.lyrics.Lyrics
|
||||
import code.name.monkey.retromusic.activities.base.AbsMusicServiceActivity
|
||||
import code.name.monkey.retromusic.activities.tageditor.WriteTagsAsyncTask
|
||||
import code.name.monkey.retromusic.fragments.base.AbsMusicServiceFragment
|
||||
import code.name.monkey.retromusic.util.LyricUtil
|
||||
import code.name.monkey.retromusic.util.MusicUtil
|
||||
import code.name.monkey.retromusic.util.PreferenceUtil
|
||||
import code.name.monkey.retromusic.util.RetroUtil
|
||||
import com.afollestad.materialdialogs.MaterialDialog
|
||||
import com.afollestad.materialdialogs.input.input
|
||||
import kotlinx.android.synthetic.main.activity_lyrics.*
|
||||
import kotlinx.android.synthetic.main.fragment_lyrics.*
|
||||
import kotlinx.android.synthetic.main.fragment_synced.*
|
||||
import org.jaudiotagger.tag.FieldKey
|
||||
import java.io.File
|
||||
import java.util.*
|
||||
import kotlin.collections.ArrayList
|
||||
|
||||
class LyricsActivity : AbsMusicServiceActivity(), View.OnClickListener, ViewPager.OnPageChangeListener {
|
||||
override fun onPageScrollStateChanged(state: Int) {
|
||||
when (state) {
|
||||
ViewPager.SCROLL_STATE_IDLE ->
|
||||
fab.show(true)
|
||||
ViewPager.SCROLL_STATE_DRAGGING,
|
||||
ViewPager.SCROLL_STATE_SETTLING ->
|
||||
fab.hide(true)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int) {
|
||||
}
|
||||
|
||||
override fun onPageSelected(position: Int) {
|
||||
PreferenceUtil.getInstance().lyricsOptions = position
|
||||
if (position == 0) fab.text = getString(R.string.synced_lyrics)
|
||||
else if (position == 1) fab.text = getString(R.string.lyrics)
|
||||
}
|
||||
|
||||
override fun onClick(v: View?) {
|
||||
when (viewPager.currentItem) {
|
||||
0 -> showSyncedLyrics()
|
||||
1 -> showLyricsSaveDialog()
|
||||
}
|
||||
}
|
||||
|
||||
private lateinit var song: Song
|
||||
private var lyricsString: String? = null
|
||||
|
||||
private val googleSearchLrcUrl: String
|
||||
get() {
|
||||
var baseUrl = "http://www.google.com/search?"
|
||||
var query = song.title + "+" + song.artistName
|
||||
query = "q=" + query.replace(" ", "+") + " .lrc"
|
||||
baseUrl += query
|
||||
return baseUrl
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(R.layout.activity_lyrics)
|
||||
|
||||
setStatusbarColorAuto()
|
||||
setTaskDescriptionColorAuto()
|
||||
setNavigationbarColorAuto()
|
||||
|
||||
appBarLayout.setBackgroundColor(ThemeStore.primaryColor(this))
|
||||
toolbar.apply {
|
||||
setBackgroundColor(ThemeStore.primaryColor(this@LyricsActivity))
|
||||
navigationIcon = TintHelper.createTintedDrawable(ContextCompat.getDrawable(this@LyricsActivity, R.drawable.ic_keyboard_backspace_black_24dp), ThemeStore.textColorSecondary(this@LyricsActivity))
|
||||
setSupportActionBar(toolbar)
|
||||
}
|
||||
|
||||
|
||||
fab.backgroundTintList = ColorStateList.valueOf(ThemeStore.accentColor(this))
|
||||
ColorStateList.valueOf(MaterialValueHelper.getPrimaryTextColor(this, ColorUtil.isColorLight(ThemeStore.accentColor(this)))).apply {
|
||||
fab.setTextColor(this)
|
||||
fab.iconTint = this
|
||||
}
|
||||
setupWakelock()
|
||||
|
||||
viewPager.apply {
|
||||
adapter = PagerAdapter(supportFragmentManager)
|
||||
currentItem = PreferenceUtil.getInstance().lyricsOptions
|
||||
addOnPageChangeListener(this@LyricsActivity)
|
||||
}
|
||||
|
||||
tabs.apply {
|
||||
setupWithViewPager(viewPager)
|
||||
setSelectedTabIndicator(TintHelper.createTintedDrawable(ContextCompat.getDrawable(this@LyricsActivity, R.drawable.tab_indicator), ThemeStore.accentColor(this@LyricsActivity)))
|
||||
setTabTextColors(ThemeStore.textColorSecondary(this@LyricsActivity), ThemeStore.accentColor(this@LyricsActivity))
|
||||
setSelectedTabIndicatorColor(ThemeStore.accentColor(context))
|
||||
}
|
||||
fab.setOnClickListener(this)
|
||||
}
|
||||
|
||||
override fun onPlayingMetaChanged() {
|
||||
super.onPlayingMetaChanged()
|
||||
song = MusicPlayerRemote.currentSong
|
||||
toolbar.title = song.title
|
||||
toolbar.subtitle = song.artistName
|
||||
}
|
||||
|
||||
override fun onServiceConnected() {
|
||||
super.onServiceConnected()
|
||||
song = MusicPlayerRemote.currentSong
|
||||
toolbar.title = song.title
|
||||
toolbar.subtitle = song.artistName
|
||||
}
|
||||
|
||||
private fun setupWakelock() {
|
||||
window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
|
||||
}
|
||||
|
||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||
if (item.itemId == android.R.id.home) {
|
||||
onBackPressed()
|
||||
}
|
||||
return super.onOptionsItemSelected(item)
|
||||
}
|
||||
|
||||
private fun showSyncedLyrics() {
|
||||
var content = ""
|
||||
try {
|
||||
content = LyricUtil.getStringFromFile(song.title, song.artistName)
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
|
||||
MaterialDialog(this).show {
|
||||
title(R.string.add_time_framed_lryics)
|
||||
negativeButton(R.string.action_search) { RetroUtil.openUrl(this@LyricsActivity, googleSearchLrcUrl) }
|
||||
input(hint = getString(R.string.paste_lyrics_here),
|
||||
prefill = content,
|
||||
inputType = InputType.TYPE_CLASS_TEXT or InputType.TYPE_TEXT_FLAG_MULTI_LINE) { _, input ->
|
||||
LyricUtil.writeLrcToLoc(song.title, song.artistName, input.toString())
|
||||
}
|
||||
positiveButton(android.R.string.ok) {
|
||||
updateSong()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateSong() {
|
||||
val page = supportFragmentManager.findFragmentByTag("android:switcher:" + R.id.viewPager + ":" + viewPager.currentItem)
|
||||
if (viewPager.currentItem == 0 && page != null) {
|
||||
(page as BaseLyricsFragment).upDateSong()
|
||||
}
|
||||
}
|
||||
|
||||
private fun showLyricsSaveDialog() {
|
||||
val content: String = if (lyricsString == null) {
|
||||
""
|
||||
} else {
|
||||
lyricsString!!
|
||||
}
|
||||
|
||||
MaterialDialog(this).show {
|
||||
title(R.string.add_lyrics)
|
||||
negativeButton(R.string.action_search) { RetroUtil.openUrl(this@LyricsActivity, getGoogleSearchUrl()) }
|
||||
input(hint = getString(R.string.paste_lyrics_here),
|
||||
prefill = content,
|
||||
inputType = InputType.TYPE_CLASS_TEXT or InputType.TYPE_TEXT_FLAG_MULTI_LINE) { _, input ->
|
||||
val fieldKeyValueMap = EnumMap<FieldKey, String>(FieldKey::class.java)
|
||||
fieldKeyValueMap[FieldKey.LYRICS] = input.toString()
|
||||
WriteTagsAsyncTask(this@LyricsActivity).execute(WriteTagsAsyncTask.LoadingInfo(getSongPaths(song), fieldKeyValueMap, null))
|
||||
}
|
||||
positiveButton(android.R.string.ok) {
|
||||
updateSong()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun getSongPaths(song: Song): ArrayList<String> {
|
||||
val paths = ArrayList<String>(1)
|
||||
paths.add(song.data)
|
||||
return paths
|
||||
}
|
||||
|
||||
private fun getGoogleSearchUrl(): String {
|
||||
var baseUrl = "http://www.google.com/search?"
|
||||
var query = song.title + "+" + song.artistName
|
||||
query = "q=" + query.replace(" ", "+") + " lyrics"
|
||||
baseUrl += query
|
||||
return baseUrl
|
||||
}
|
||||
|
||||
class PagerAdapter(fm: FragmentManager) : FragmentStatePagerAdapter(fm) {
|
||||
class Tabs(@StringRes val title: Int,
|
||||
val fragment: Fragment)
|
||||
|
||||
private var tabs = ArrayList<Tabs>()
|
||||
|
||||
init {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP_MR1) {
|
||||
tabs.add(Tabs(R.string.synced_lyrics, SyncedLyricsFragment()))
|
||||
}
|
||||
tabs.add(Tabs(R.string.normal_lyrics, OfflineLyricsFragment()))
|
||||
}
|
||||
|
||||
override fun getItem(position: Int): Fragment {
|
||||
return tabs[position].fragment
|
||||
}
|
||||
|
||||
override fun getPageTitle(position: Int): CharSequence? {
|
||||
return App.context.getString(tabs[position].title)
|
||||
}
|
||||
|
||||
override fun getCount(): Int {
|
||||
return tabs.size
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
abstract class BaseLyricsFragment : AbsMusicServiceFragment() {
|
||||
abstract fun upDateSong()
|
||||
|
||||
override fun onPlayingMetaChanged() {
|
||||
super.onPlayingMetaChanged()
|
||||
upDateSong()
|
||||
}
|
||||
|
||||
override fun onServiceConnected() {
|
||||
super.onServiceConnected()
|
||||
upDateSong()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class OfflineLyricsFragment : BaseLyricsFragment() {
|
||||
override fun upDateSong() {
|
||||
loadSongLyrics()
|
||||
}
|
||||
|
||||
private var updateLyricsAsyncTask: AsyncTask<*, *, *>? = null
|
||||
private var lyrics: Lyrics? = null
|
||||
|
||||
@SuppressLint("StaticFieldLeak")
|
||||
private fun loadSongLyrics() {
|
||||
if (updateLyricsAsyncTask != null) {
|
||||
updateLyricsAsyncTask!!.cancel(false)
|
||||
}
|
||||
val song = MusicPlayerRemote.currentSong
|
||||
updateLyricsAsyncTask = object : AsyncTask<Void?, Void?, Lyrics?>() {
|
||||
override fun doInBackground(vararg params: Void?): Lyrics? {
|
||||
val data = MusicUtil.getLyrics(song)
|
||||
return if (TextUtils.isEmpty(data)) {
|
||||
null
|
||||
} else Lyrics.parse(song, data!!)
|
||||
}
|
||||
|
||||
override fun onPreExecute() {
|
||||
super.onPreExecute()
|
||||
lyrics = null
|
||||
}
|
||||
|
||||
override fun onPostExecute(l: Lyrics?) {
|
||||
lyrics = l
|
||||
offlineLyrics?.visibility = View.VISIBLE
|
||||
if (l == null) {
|
||||
offlineLyrics?.setText(R.string.no_lyrics_found)
|
||||
return
|
||||
}
|
||||
(activity as LyricsActivity).lyricsString = l.text
|
||||
offlineLyrics?.text = l.text
|
||||
}
|
||||
|
||||
override fun onCancelled(s: Lyrics?) {
|
||||
onPostExecute(null)
|
||||
}
|
||||
}.execute()
|
||||
}
|
||||
|
||||
override fun onDestroyView() {
|
||||
super.onDestroyView()
|
||||
if (updateLyricsAsyncTask != null && !updateLyricsAsyncTask!!.isCancelled) {
|
||||
updateLyricsAsyncTask!!.cancel(true)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
||||
return inflater.inflate(R.layout.fragment_lyrics, container, false)
|
||||
}
|
||||
}
|
||||
|
||||
class SyncedLyricsFragment : BaseLyricsFragment(), MusicProgressViewUpdateHelper.Callback {
|
||||
override fun upDateSong() {
|
||||
loadLRCLyrics()
|
||||
}
|
||||
|
||||
private lateinit var updateHelper: MusicProgressViewUpdateHelper
|
||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
||||
return inflater.inflate(R.layout.fragment_synced, container, false)
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
updateHelper = MusicProgressViewUpdateHelper(this, 500, 1000)
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
setupLyricsView()
|
||||
}
|
||||
|
||||
private fun setupLyricsView() {
|
||||
lyricsView.apply {
|
||||
val context = activity!!
|
||||
setCurrentPlayLineColor(ThemeStore.accentColor(context))
|
||||
setIndicatorTextColor(ThemeStore.accentColor(context))
|
||||
setCurrentIndicateLineTextColor(ThemeStore.textColorPrimary(context))
|
||||
setNoLrcTextColor(ThemeStore.textColorPrimary(context))
|
||||
setOnPlayIndicatorLineListener(object : LrcView.OnPlayIndicatorLineListener {
|
||||
override fun onPlay(time: Long, content: String) {
|
||||
MusicPlayerRemote.seekTo(time.toInt())
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
updateHelper.start()
|
||||
}
|
||||
|
||||
override fun onPause() {
|
||||
super.onPause()
|
||||
updateHelper.stop()
|
||||
}
|
||||
|
||||
override fun onUpdateProgressViews(progress: Int, total: Int) {
|
||||
lyricsView.updateTime(progress.toLong())
|
||||
}
|
||||
|
||||
private fun loadLRCLyrics() {
|
||||
val song = MusicPlayerRemote.currentSong
|
||||
if (LyricUtil.isLrcFileExist(song.title, song.artistName)) {
|
||||
showLyricsLocal(LyricUtil.getLocalLyricFile(song.title, song.artistName))
|
||||
}
|
||||
}
|
||||
|
||||
private fun showLyricsLocal(file: File?) {
|
||||
if (file != null) {
|
||||
lyricsView.setLrcData(LrcHelper.parseLrcFromFile(file))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,307 @@
|
|||
package code.name.monkey.retromusic.activities
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.*
|
||||
import android.content.pm.PackageManager
|
||||
import android.os.Bundle
|
||||
import android.provider.MediaStore
|
||||
import android.util.Log
|
||||
import android.view.MenuItem
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.core.app.ActivityCompat
|
||||
import androidx.fragment.app.Fragment
|
||||
import code.name.monkey.retromusic.R
|
||||
import code.name.monkey.retromusic.helper.MusicPlayerRemote
|
||||
import code.name.monkey.retromusic.helper.SearchQueryHelper
|
||||
import code.name.monkey.retromusic.interfaces.MainActivityFragmentCallbacks
|
||||
import code.name.monkey.retromusic.loaders.AlbumLoader
|
||||
import code.name.monkey.retromusic.loaders.ArtistLoader
|
||||
import code.name.monkey.retromusic.loaders.PlaylistSongsLoader
|
||||
import code.name.monkey.retromusic.service.MusicService
|
||||
import code.name.monkey.retromusic.activities.base.AbsSlidingMusicPanelActivity
|
||||
import code.name.monkey.retromusic.fragments.mainactivity.LibraryFragment
|
||||
import code.name.monkey.retromusic.fragments.mainactivity.home.BannerHomeFragment
|
||||
import code.name.monkey.retromusic.util.NavigationUtil
|
||||
import code.name.monkey.retromusic.util.PreferenceUtil
|
||||
import io.reactivex.disposables.CompositeDisposable
|
||||
import java.util.*
|
||||
|
||||
|
||||
class MainActivity : AbsSlidingMusicPanelActivity(), SharedPreferences.OnSharedPreferenceChangeListener {
|
||||
|
||||
private lateinit var currentFragment: MainActivityFragmentCallbacks
|
||||
|
||||
private var blockRequestPermissions: Boolean = false
|
||||
private val disposable = CompositeDisposable()
|
||||
private val broadcastReceiver = object : BroadcastReceiver() {
|
||||
override fun onReceive(context: Context, intent: Intent) {
|
||||
val action = intent.action
|
||||
if (action != null && action == Intent.ACTION_SCREEN_OFF) {
|
||||
if (PreferenceUtil.getInstance().lockScreen && MusicPlayerRemote.isPlaying) {
|
||||
val activity = Intent(context, LockScreenActivity::class.java)
|
||||
activity.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||
activity.addFlags(Intent.FLAG_ACTIVITY_NO_HISTORY)
|
||||
ActivityCompat.startActivity(context, activity, null)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun createContentView(): View {
|
||||
@SuppressLint("InflateParams")
|
||||
val contentView = layoutInflater.inflate(R.layout.activity_main_drawer_layout, null)
|
||||
val drawerContent = contentView.findViewById<ViewGroup>(R.id.drawer_content_container)
|
||||
drawerContent.addView(wrapSlidingMusicPanel(R.layout.activity_main_content))
|
||||
return contentView
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
setDrawUnderStatusBar()
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
getBottomNavigationView().setOnNavigationItemSelectedListener {
|
||||
PreferenceUtil.getInstance().lastPage = it.itemId
|
||||
selectedFragment(it.itemId)
|
||||
true
|
||||
}
|
||||
|
||||
//setUpDrawerLayout()
|
||||
|
||||
if (savedInstanceState == null) {
|
||||
selectedFragment(PreferenceUtil.getInstance().lastPage);
|
||||
} else {
|
||||
restoreCurrentFragment();
|
||||
}
|
||||
|
||||
checkShowChangelog()
|
||||
|
||||
/*if (!App.isProVersion && !PreferenceManager.getDefaultSharedPreferences(this).getBoolean("shown", false)) {
|
||||
showPromotionalOffer()
|
||||
}*/
|
||||
}
|
||||
|
||||
private fun checkShowChangelog() {
|
||||
try {
|
||||
val pInfo = packageManager.getPackageInfo(packageName, 0)
|
||||
val currentVersion = pInfo.versionCode
|
||||
if (currentVersion != PreferenceUtil.getInstance().lastChangelogVersion) {
|
||||
startActivityForResult(Intent(this, WhatsNewActivity::class.java), APP_INTRO_REQUEST)
|
||||
}
|
||||
} catch (e: PackageManager.NameNotFoundException) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
val screenOnOff = IntentFilter()
|
||||
screenOnOff.addAction(Intent.ACTION_SCREEN_OFF)
|
||||
registerReceiver(broadcastReceiver, screenOnOff)
|
||||
|
||||
PreferenceUtil.getInstance().registerOnSharedPreferenceChangedListener(this)
|
||||
|
||||
if (intent.hasExtra("expand")) {
|
||||
if (intent.getBooleanExtra("expand", false)) {
|
||||
expandPanel()
|
||||
intent.putExtra("expand", false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
super.onDestroy()
|
||||
disposable.clear()
|
||||
unregisterReceiver(broadcastReceiver)
|
||||
PreferenceUtil.getInstance().unregisterOnSharedPreferenceChangedListener(this)
|
||||
}
|
||||
|
||||
fun setCurrentFragment(fragment: Fragment, b: Boolean) {
|
||||
val trans = supportFragmentManager.beginTransaction()
|
||||
trans.replace(R.id.fragment_container, fragment, null)
|
||||
if (b) {
|
||||
trans.addToBackStack(null)
|
||||
}
|
||||
trans.commit()
|
||||
currentFragment = fragment as MainActivityFragmentCallbacks
|
||||
}
|
||||
|
||||
|
||||
private fun restoreCurrentFragment() {
|
||||
currentFragment = supportFragmentManager.findFragmentById(R.id.fragment_container) as MainActivityFragmentCallbacks
|
||||
}
|
||||
|
||||
private fun handlePlaybackIntent(intent: Intent?) {
|
||||
if (intent == null) {
|
||||
return
|
||||
}
|
||||
|
||||
val uri = intent.data
|
||||
val mimeType = intent.type
|
||||
var handled = false
|
||||
|
||||
if (intent.action != null && intent.action == MediaStore.INTENT_ACTION_MEDIA_PLAY_FROM_SEARCH) {
|
||||
val songs = SearchQueryHelper.getSongs(this, intent.extras!!)
|
||||
if (MusicPlayerRemote.shuffleMode == MusicService.SHUFFLE_MODE_SHUFFLE) {
|
||||
MusicPlayerRemote.openAndShuffleQueue(songs, true)
|
||||
} else {
|
||||
MusicPlayerRemote.openQueue(songs, 0, true)
|
||||
}
|
||||
handled = true
|
||||
}
|
||||
|
||||
if (uri != null && uri.toString().isNotEmpty()) {
|
||||
MusicPlayerRemote.playFromUri(uri)
|
||||
handled = true
|
||||
} else if (MediaStore.Audio.Playlists.CONTENT_TYPE == mimeType) {
|
||||
val id = parseIdFromIntent(intent, "playlistId", "playlist").toInt()
|
||||
if (id >= 0) {
|
||||
val position = intent.getIntExtra("position", 0)
|
||||
val songs = ArrayList(PlaylistSongsLoader.getPlaylistSongList(this, id).blockingFirst())
|
||||
MusicPlayerRemote.openQueue(songs, position, true)
|
||||
handled = true
|
||||
}
|
||||
} else if (MediaStore.Audio.Albums.CONTENT_TYPE == mimeType) {
|
||||
val id = parseIdFromIntent(intent, "albumId", "album").toInt()
|
||||
if (id >= 0) {
|
||||
val position = intent.getIntExtra("position", 0)
|
||||
MusicPlayerRemote.openQueue(AlbumLoader.getAlbum(this, id).blockingFirst().songs!!, position, true)
|
||||
handled = true
|
||||
}
|
||||
} else if (MediaStore.Audio.Artists.CONTENT_TYPE == mimeType) {
|
||||
val id = parseIdFromIntent(intent, "artistId", "artist").toInt()
|
||||
if (id >= 0) {
|
||||
val position = intent.getIntExtra("position", 0)
|
||||
MusicPlayerRemote.openQueue(ArtistLoader.getArtist(this, id).blockingFirst().songs, position, true)
|
||||
handled = true
|
||||
}
|
||||
}
|
||||
if (handled) {
|
||||
setIntent(Intent())
|
||||
}
|
||||
}
|
||||
|
||||
private fun parseIdFromIntent(intent: Intent, longKey: String, stringKey: String): Long {
|
||||
var id = intent.getLongExtra(longKey, -1)
|
||||
if (id < 0) {
|
||||
val idString = intent.getStringExtra(stringKey)
|
||||
if (idString != null) {
|
||||
try {
|
||||
id = java.lang.Long.parseLong(idString)
|
||||
} catch (e: NumberFormatException) {
|
||||
Log.e(TAG, e.message)
|
||||
}
|
||||
}
|
||||
}
|
||||
return id
|
||||
}
|
||||
|
||||
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
||||
super.onActivityResult(requestCode, resultCode, data)
|
||||
when (requestCode) {
|
||||
APP_INTRO_REQUEST -> {
|
||||
blockRequestPermissions = false
|
||||
if (!hasPermissions()) {
|
||||
requestPermissions()
|
||||
}
|
||||
}
|
||||
REQUEST_CODE_THEME, APP_USER_INFO_REQUEST -> postRecreate()
|
||||
PURCHASE_REQUEST -> {
|
||||
if (resultCode == RESULT_OK) {
|
||||
//checkSetUpPro();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
override fun handleBackPress(): Boolean {
|
||||
return super.handleBackPress() || currentFragment.handleBackPress()
|
||||
}
|
||||
|
||||
override fun onServiceConnected() {
|
||||
super.onServiceConnected()
|
||||
handlePlaybackIntent(intent)
|
||||
}
|
||||
|
||||
override fun requestPermissions() {
|
||||
if (!blockRequestPermissions) {
|
||||
super.requestPermissions()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences, key: String) {
|
||||
if (key == PreferenceUtil.GENERAL_THEME ||
|
||||
key == PreferenceUtil.ADAPTIVE_COLOR_APP ||
|
||||
key == PreferenceUtil.DOMINANT_COLOR ||
|
||||
key == PreferenceUtil.USER_NAME ||
|
||||
key == PreferenceUtil.TOGGLE_FULL_SCREEN ||
|
||||
key == PreferenceUtil.TOGGLE_VOLUME ||
|
||||
key == PreferenceUtil.ROUND_CORNERS ||
|
||||
key == PreferenceUtil.CAROUSEL_EFFECT ||
|
||||
key == PreferenceUtil.NOW_PLAYING_SCREEN_ID ||
|
||||
key == PreferenceUtil.TOGGLE_GENRE ||
|
||||
key == PreferenceUtil.BANNER_IMAGE_PATH ||
|
||||
key == PreferenceUtil.PROFILE_IMAGE_PATH ||
|
||||
key == PreferenceUtil.CIRCULAR_ALBUM_ART ||
|
||||
key == PreferenceUtil.KEEP_SCREEN_ON ||
|
||||
key == PreferenceUtil.TOGGLE_SEPARATE_LINE ||
|
||||
key == PreferenceUtil.ALBUM_GRID_STYLE ||
|
||||
key == PreferenceUtil.ARTIST_GRID_STYLE ||
|
||||
key == PreferenceUtil.TOGGLE_HOME_BANNER ||
|
||||
key == PreferenceUtil.TOGGLE_ADD_CONTROLS ||
|
||||
key == PreferenceUtil.ALBUM_COVER_STYLE ||
|
||||
key == PreferenceUtil.HOME_ARTIST_GRID_STYLE ||
|
||||
key == PreferenceUtil.ALBUM_COVER_TRANSFORM ||
|
||||
key == PreferenceUtil.TAB_TEXT_MODE)
|
||||
postRecreate()
|
||||
}
|
||||
|
||||
private fun showPromotionalOffer() {
|
||||
/*MaterialDialog(this).show {
|
||||
positiveButton(text = "Buy") { startActivity(Intent(this@MainActivity, PurchaseActivity::class.java)) }
|
||||
negativeButton(android.R.string.cancel)
|
||||
customView(R.layout.dialog_promotional_offer)
|
||||
onDismiss {
|
||||
PreferenceManager.getDefaultSharedPreferences(this@MainActivity)
|
||||
.edit()
|
||||
.putBoolean("shown", true)
|
||||
.apply()
|
||||
}
|
||||
}*/
|
||||
}
|
||||
|
||||
private fun selectedFragment(itemId: Int) {
|
||||
when (itemId) {
|
||||
R.id.action_album,
|
||||
R.id.action_artist,
|
||||
R.id.action_playlist,
|
||||
R.id.action_song -> setCurrentFragment(LibraryFragment.newInstance(itemId), false)
|
||||
R.id.action_home -> setCurrentFragment(BannerHomeFragment.newInstance(), false)
|
||||
else -> {
|
||||
setCurrentFragment(BannerHomeFragment.newInstance(), false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||
if (item.itemId == android.R.id.home) {
|
||||
NavigationUtil.goToSearch(this);
|
||||
return true
|
||||
}
|
||||
return super.onOptionsItemSelected(item)
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val APP_INTRO_REQUEST = 2323
|
||||
const val LIBRARY = 1
|
||||
const val FOLDERS = 3
|
||||
const val HOME = 0
|
||||
private const val TAG = "MainActivity"
|
||||
private const val APP_USER_INFO_REQUEST = 9003
|
||||
private const val REQUEST_CODE_THEME = 9002
|
||||
private const val PURCHASE_REQUEST = 101
|
||||
}
|
||||
}
|
|
@ -0,0 +1,160 @@
|
|||
package code.name.monkey.retromusic.activities
|
||||
|
||||
import android.content.res.ColorStateList
|
||||
import android.os.Bundle
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
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.appthemehelper.util.ToolbarContentTintHelper
|
||||
import code.name.monkey.retromusic.R
|
||||
import code.name.monkey.retromusic.helper.MusicPlayerRemote
|
||||
import code.name.monkey.retromusic.activities.base.AbsMusicServiceActivity
|
||||
import code.name.monkey.retromusic.adapter.song.PlayingQueueAdapter
|
||||
import code.name.monkey.retromusic.util.MusicUtil
|
||||
import com.h6ah4i.android.widget.advrecyclerview.animator.RefactoredDefaultItemAnimator
|
||||
import com.h6ah4i.android.widget.advrecyclerview.draggable.RecyclerViewDragDropManager
|
||||
import com.h6ah4i.android.widget.advrecyclerview.utils.WrapperAdapterUtils
|
||||
import kotlinx.android.synthetic.main.activity_playing_queue.*
|
||||
|
||||
|
||||
class PlayingQueueActivity : AbsMusicServiceActivity() {
|
||||
|
||||
private var wrappedAdapter: RecyclerView.Adapter<*>? = null
|
||||
private var recyclerViewDragDropManager: RecyclerViewDragDropManager? = null
|
||||
private var playingQueueAdapter: PlayingQueueAdapter? = null
|
||||
private lateinit var linearLayoutManager: LinearLayoutManager
|
||||
|
||||
private val upNextAndQueueTime: String
|
||||
get() = resources.getString(R.string.up_next) + " • " + MusicUtil.getReadableDurationString(MusicPlayerRemote.getQueueDurationMillis(MusicPlayerRemote.position))
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(R.layout.activity_playing_queue)
|
||||
|
||||
setStatusbarColorAuto()
|
||||
setNavigationbarColorAuto()
|
||||
setTaskDescriptionColorAuto()
|
||||
setLightNavigationBar(true)
|
||||
|
||||
setupToolbar()
|
||||
setUpRecyclerView()
|
||||
|
||||
clearQueue.setOnClickListener {
|
||||
MusicPlayerRemote.clearQueue()
|
||||
}
|
||||
}
|
||||
|
||||
private fun setUpRecyclerView() {
|
||||
recyclerViewDragDropManager = RecyclerViewDragDropManager()
|
||||
val animator = RefactoredDefaultItemAnimator()
|
||||
|
||||
playingQueueAdapter = PlayingQueueAdapter(
|
||||
this,
|
||||
MusicPlayerRemote.playingQueue,
|
||||
MusicPlayerRemote.position,
|
||||
R.layout.item_queue)
|
||||
wrappedAdapter = recyclerViewDragDropManager!!.createWrappedAdapter(playingQueueAdapter!!)
|
||||
|
||||
linearLayoutManager = LinearLayoutManager(this)
|
||||
|
||||
recyclerView.apply {
|
||||
layoutManager = linearLayoutManager
|
||||
adapter = wrappedAdapter
|
||||
itemAnimator = animator
|
||||
recyclerViewDragDropManager!!.attachRecyclerView(this)
|
||||
}
|
||||
|
||||
linearLayoutManager.scrollToPositionWithOffset(MusicPlayerRemote.position + 1, 0)
|
||||
|
||||
recyclerView.addOnScrollListener(object : RecyclerView.OnScrollListener() {
|
||||
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
|
||||
super.onScrolled(recyclerView, dx, dy)
|
||||
if (dy > 0) {
|
||||
clearQueue.shrink(true)
|
||||
} else if (dy < 0) {
|
||||
clearQueue.extend(true)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
override fun onQueueChanged() {
|
||||
if (MusicPlayerRemote.playingQueue.isEmpty()) {
|
||||
finish()
|
||||
return
|
||||
}
|
||||
updateQueue()
|
||||
updateCurrentSong()
|
||||
}
|
||||
|
||||
override fun onMediaStoreChanged() {
|
||||
updateQueue()
|
||||
updateCurrentSong()
|
||||
}
|
||||
|
||||
private fun updateCurrentSong() {}
|
||||
|
||||
override fun onPlayingMetaChanged() {
|
||||
updateQueuePosition()
|
||||
}
|
||||
|
||||
private fun updateQueuePosition() {
|
||||
playingQueueAdapter!!.setCurrent(MusicPlayerRemote.position)
|
||||
resetToCurrentPosition()
|
||||
}
|
||||
|
||||
private fun updateQueue() {
|
||||
playingQueueAdapter!!.swapDataSet(MusicPlayerRemote.playingQueue, MusicPlayerRemote.position)
|
||||
resetToCurrentPosition()
|
||||
}
|
||||
|
||||
private fun resetToCurrentPosition() {
|
||||
recyclerView.stopScroll()
|
||||
linearLayoutManager.scrollToPositionWithOffset(MusicPlayerRemote.position + 1, 0)
|
||||
}
|
||||
|
||||
override fun onPause() {
|
||||
if (recyclerViewDragDropManager != null) {
|
||||
recyclerViewDragDropManager!!.cancelDrag()
|
||||
}
|
||||
super.onPause()
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
if (recyclerViewDragDropManager != null) {
|
||||
recyclerViewDragDropManager!!.release()
|
||||
recyclerViewDragDropManager = null
|
||||
}
|
||||
|
||||
|
||||
|
||||
if (wrappedAdapter != null) {
|
||||
WrapperAdapterUtils.releaseAll(wrappedAdapter)
|
||||
wrappedAdapter = null
|
||||
}
|
||||
playingQueueAdapter = null
|
||||
super.onDestroy()
|
||||
}
|
||||
|
||||
private fun setupToolbar() {
|
||||
bannerTitle.setTextColor(ThemeStore.textColorPrimary(this))
|
||||
playerQueueSubHeader.text = upNextAndQueueTime
|
||||
playerQueueSubHeader.setTextColor(ThemeStore.accentColor(this))
|
||||
|
||||
appBarLayout.setBackgroundColor(ThemeStore.primaryColor(this))
|
||||
toolbar.setBackgroundColor(ThemeStore.primaryColor(this))
|
||||
toolbar.setNavigationIcon(R.drawable.ic_close_white_24dp)
|
||||
setSupportActionBar(toolbar)
|
||||
title = null
|
||||
toolbar.setNavigationOnClickListener { onBackPressed() }
|
||||
|
||||
ToolbarContentTintHelper.colorBackButton(toolbar, ThemeStore.textColorSecondary(this))
|
||||
clearQueue.backgroundTintList = ColorStateList.valueOf(ThemeStore.accentColor(this))
|
||||
ColorStateList.valueOf(MaterialValueHelper.getPrimaryTextColor(this, ColorUtil.isColorLight(ThemeStore.accentColor(this)))).apply {
|
||||
clearQueue.setTextColor(this)
|
||||
clearQueue.iconTint = this
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,254 @@
|
|||
package code.name.monkey.retromusic.activities
|
||||
|
||||
import android.content.res.ColorStateList
|
||||
import android.graphics.Color
|
||||
import android.os.Bundle
|
||||
import android.view.Menu
|
||||
import android.view.MenuItem
|
||||
import android.view.View
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
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.appthemehelper.util.ToolbarContentTintHelper
|
||||
import code.name.monkey.retromusic.R
|
||||
import code.name.monkey.retromusic.helper.MusicPlayerRemote
|
||||
import code.name.monkey.retromusic.helper.menu.PlaylistMenuHelper
|
||||
import code.name.monkey.retromusic.interfaces.CabHolder
|
||||
import code.name.monkey.retromusic.loaders.PlaylistLoader
|
||||
import code.name.monkey.retromusic.model.AbsCustomPlaylist
|
||||
import code.name.monkey.retromusic.model.Playlist
|
||||
import code.name.monkey.retromusic.model.Song
|
||||
import code.name.monkey.retromusic.mvp.contract.PlaylistSongsContract
|
||||
import code.name.monkey.retromusic.mvp.presenter.PlaylistSongsPresenter
|
||||
import code.name.monkey.retromusic.activities.base.AbsSlidingMusicPanelActivity
|
||||
import code.name.monkey.retromusic.adapter.song.OrderablePlaylistSongAdapter
|
||||
import code.name.monkey.retromusic.adapter.song.PlaylistSongAdapter
|
||||
import code.name.monkey.retromusic.adapter.song.SongAdapter
|
||||
import code.name.monkey.retromusic.util.PlaylistsUtil
|
||||
import code.name.monkey.retromusic.util.RetroColorUtil
|
||||
import code.name.monkey.retromusic.util.ViewUtil
|
||||
import com.afollestad.materialcab.MaterialCab
|
||||
import com.h6ah4i.android.widget.advrecyclerview.animator.RefactoredDefaultItemAnimator
|
||||
import com.h6ah4i.android.widget.advrecyclerview.draggable.RecyclerViewDragDropManager
|
||||
import com.h6ah4i.android.widget.advrecyclerview.utils.WrapperAdapterUtils
|
||||
import kotlinx.android.synthetic.main.activity_playlist_detail.*
|
||||
import java.util.*
|
||||
|
||||
class PlaylistDetailActivity : AbsSlidingMusicPanelActivity(), CabHolder, PlaylistSongsContract.PlaylistSongsView {
|
||||
|
||||
private var playlist: Playlist? = null
|
||||
private var cab: MaterialCab? = null
|
||||
private lateinit var adapter: SongAdapter
|
||||
private var wrappedAdapter: RecyclerView.Adapter<*>? = null
|
||||
private var recyclerViewDragDropManager: RecyclerViewDragDropManager? = null
|
||||
private var songsPresenter: PlaylistSongsPresenter? = null
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
setDrawUnderStatusBar()
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
setStatusbarColor(Color.TRANSPARENT)
|
||||
setNavigationbarColorAuto()
|
||||
setTaskDescriptionColorAuto()
|
||||
setLightNavigationBar(true)
|
||||
setLightStatusbar(ColorUtil.isColorLight(ThemeStore.primaryColor(this)))
|
||||
toggleBottomNavigationView(true)
|
||||
|
||||
playlist = intent.extras!!.getParcelable(EXTRA_PLAYLIST)
|
||||
songsPresenter = PlaylistSongsPresenter(this, playlist!!)
|
||||
|
||||
setUpToolBar()
|
||||
setUpRecyclerView()
|
||||
}
|
||||
|
||||
override fun createContentView(): View {
|
||||
return wrapSlidingMusicPanel(R.layout.activity_playlist_detail)
|
||||
}
|
||||
|
||||
private fun setUpRecyclerView() {
|
||||
ViewUtil.setUpFastScrollRecyclerViewColor(this, recyclerView, ThemeStore.accentColor(this))
|
||||
recyclerView.layoutManager = LinearLayoutManager(this)
|
||||
if (playlist is AbsCustomPlaylist) {
|
||||
adapter = PlaylistSongAdapter(this, ArrayList(), R.layout.item_list, false, this)
|
||||
recyclerView!!.adapter = adapter
|
||||
} else {
|
||||
recyclerViewDragDropManager = RecyclerViewDragDropManager()
|
||||
val animator = RefactoredDefaultItemAnimator()
|
||||
adapter = OrderablePlaylistSongAdapter(this, ArrayList(), R.layout.item_list, false, this,
|
||||
object : OrderablePlaylistSongAdapter.OnMoveItemListener {
|
||||
override fun onMoveItem(fromPosition: Int, toPosition: Int) {
|
||||
if (PlaylistsUtil.moveItem(this@PlaylistDetailActivity, playlist!!.id, fromPosition, toPosition)) {
|
||||
val song = adapter.dataSet.removeAt(fromPosition)
|
||||
adapter.dataSet.add(toPosition, song)
|
||||
adapter.notifyItemMoved(fromPosition, toPosition)
|
||||
}
|
||||
}
|
||||
})
|
||||
wrappedAdapter = recyclerViewDragDropManager!!.createWrappedAdapter(adapter)
|
||||
|
||||
recyclerView.adapter = wrappedAdapter
|
||||
recyclerView.itemAnimator = animator
|
||||
|
||||
recyclerViewDragDropManager!!.attachRecyclerView(recyclerView!!)
|
||||
}
|
||||
adapter.registerAdapterDataObserver(object : RecyclerView.AdapterDataObserver() {
|
||||
override fun onChanged() {
|
||||
super.onChanged()
|
||||
checkIsEmpty()
|
||||
}
|
||||
})
|
||||
recyclerView!!.addOnScrollListener(object : RecyclerView.OnScrollListener() {
|
||||
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
|
||||
super.onScrolled(recyclerView, dx, dy)
|
||||
if (dy > 0) {
|
||||
actionShuffleAll.shrink(true)
|
||||
} else if (dy < 0) {
|
||||
actionShuffleAll.extend(true)
|
||||
}
|
||||
}
|
||||
})
|
||||
actionShuffleAll.setOnClickListener {
|
||||
if (adapter.dataSet.isEmpty()) {
|
||||
return@setOnClickListener
|
||||
}
|
||||
MusicPlayerRemote.openAndShuffleQueue(adapter.dataSet, true)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
songsPresenter!!.subscribe()
|
||||
}
|
||||
|
||||
private fun setUpToolBar() {
|
||||
bannerTitle.text = playlist!!.name
|
||||
bannerTitle.setTextColor(ThemeStore.textColorPrimary(this))
|
||||
|
||||
actionShuffleAll.backgroundTintList = ColorStateList.valueOf(ThemeStore.accentColor(this))
|
||||
ColorStateList.valueOf(MaterialValueHelper.getPrimaryTextColor(this, ColorUtil.isColorLight(ThemeStore.accentColor(this)))).apply {
|
||||
actionShuffleAll.setTextColor(this)
|
||||
actionShuffleAll.iconTint = this
|
||||
}
|
||||
|
||||
val primaryColor = ThemeStore.primaryColor(this)
|
||||
toolbar!!.apply {
|
||||
setBackgroundColor(primaryColor)
|
||||
setSupportActionBar(toolbar)
|
||||
setNavigationIcon(R.drawable.ic_keyboard_backspace_black_24dp)
|
||||
ToolbarContentTintHelper.colorBackButton(this, ThemeStore.textColorSecondary(this@PlaylistDetailActivity))
|
||||
}
|
||||
title = null
|
||||
}
|
||||
|
||||
override fun onCreateOptionsMenu(menu: Menu): Boolean {
|
||||
menuInflater.inflate(if (playlist is AbsCustomPlaylist) R.menu.menu_smart_playlist_detail else R.menu.menu_playlist_detail, menu)
|
||||
return super.onCreateOptionsMenu(menu)
|
||||
}
|
||||
|
||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||
val id = item.itemId
|
||||
when (id) {
|
||||
android.R.id.home -> {
|
||||
onBackPressed()
|
||||
return true
|
||||
}
|
||||
}
|
||||
return PlaylistMenuHelper.handleMenuClick(this, playlist!!, item)
|
||||
}
|
||||
|
||||
override fun openCab(menuRes: Int, callback: MaterialCab.Callback): MaterialCab {
|
||||
if (cab != null && cab!!.isActive) {
|
||||
cab!!.finish()
|
||||
}
|
||||
cab = MaterialCab(this, R.id.cab_stub)
|
||||
.setMenu(menuRes)
|
||||
.setCloseDrawableRes(R.drawable.ic_close_white_24dp)
|
||||
.setBackgroundColor(
|
||||
RetroColorUtil.shiftBackgroundColorForLightText(ThemeStore.primaryColor(this)))
|
||||
.start(callback)
|
||||
return cab!!
|
||||
}
|
||||
|
||||
override fun onBackPressed() {
|
||||
if (cab != null && cab!!.isActive) {
|
||||
cab!!.finish()
|
||||
} else {
|
||||
recyclerView!!.stopScroll()
|
||||
super.onBackPressed()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onMediaStoreChanged() {
|
||||
super.onMediaStoreChanged()
|
||||
|
||||
if (playlist !is AbsCustomPlaylist) {
|
||||
// Playlist deleted
|
||||
if (!PlaylistsUtil.doesPlaylistExist(this, playlist!!.id)) {
|
||||
finish()
|
||||
return
|
||||
}
|
||||
|
||||
// Playlist renamed
|
||||
val playlistName = PlaylistsUtil.getNameForPlaylist(this, playlist!!.id.toLong())
|
||||
if (playlistName != playlist!!.name) {
|
||||
playlist = PlaylistLoader.getPlaylist(this, playlist!!.id).blockingFirst()
|
||||
setToolbarTitle(playlist!!.name)
|
||||
}
|
||||
}
|
||||
songsPresenter!!.subscribe()
|
||||
}
|
||||
|
||||
private fun setToolbarTitle(title: String) {
|
||||
supportActionBar!!.title = title
|
||||
}
|
||||
|
||||
private fun checkIsEmpty() {
|
||||
empty.visibility = if (adapter.itemCount == 0) View.VISIBLE else View.GONE
|
||||
emptyText.visibility = if (adapter.itemCount == 0) View.VISIBLE else View.GONE
|
||||
}
|
||||
|
||||
public override fun onPause() {
|
||||
if (recyclerViewDragDropManager != null) {
|
||||
recyclerViewDragDropManager!!.cancelDrag()
|
||||
}
|
||||
super.onPause()
|
||||
songsPresenter!!.unsubscribe()
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
if (recyclerViewDragDropManager != null) {
|
||||
recyclerViewDragDropManager!!.release()
|
||||
recyclerViewDragDropManager = null
|
||||
}
|
||||
|
||||
if (recyclerView != null) {
|
||||
recyclerView!!.itemAnimator = null
|
||||
recyclerView!!.adapter = null
|
||||
}
|
||||
|
||||
if (wrappedAdapter != null) {
|
||||
WrapperAdapterUtils.releaseAll(wrappedAdapter)
|
||||
wrappedAdapter = null
|
||||
}
|
||||
super.onDestroy()
|
||||
}
|
||||
|
||||
override fun loading() {}
|
||||
|
||||
override fun showEmptyView() {
|
||||
empty.visibility = View.VISIBLE
|
||||
emptyText.visibility = View.VISIBLE
|
||||
}
|
||||
|
||||
override fun completed() {}
|
||||
|
||||
override fun showData(list: ArrayList<Song>) {
|
||||
adapter.swapDataSet(list)
|
||||
}
|
||||
|
||||
companion object {
|
||||
var EXTRA_PLAYLIST = "extra_playlist"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,160 @@
|
|||
package code.name.monkey.retromusic.activities
|
||||
|
||||
import android.content.Intent
|
||||
import android.os.AsyncTask
|
||||
import android.os.Bundle
|
||||
import android.util.Log
|
||||
import android.view.MenuItem
|
||||
import android.widget.Toast
|
||||
import code.name.monkey.appthemehelper.ThemeStore
|
||||
import code.name.monkey.appthemehelper.util.MaterialUtil
|
||||
import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper
|
||||
import code.name.monkey.retromusic.App
|
||||
import code.name.monkey.retromusic.BuildConfig
|
||||
import code.name.monkey.retromusic.R
|
||||
import code.name.monkey.retromusic.activities.base.AbsBaseActivity
|
||||
import com.anjlab.android.iab.v3.BillingProcessor
|
||||
import com.anjlab.android.iab.v3.TransactionDetails
|
||||
import kotlinx.android.synthetic.main.activity_pro_version.*
|
||||
import java.lang.ref.WeakReference
|
||||
|
||||
|
||||
class PurchaseActivity : AbsBaseActivity(), BillingProcessor.IBillingHandler {
|
||||
|
||||
|
||||
private lateinit var billingProcessor: BillingProcessor
|
||||
private var restorePurchaseAsyncTask: AsyncTask<*, *, *>? = null
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(R.layout.activity_pro_version)
|
||||
setDrawUnderStatusBar()
|
||||
|
||||
setStatusbarColorAuto()
|
||||
setNavigationbarColorAuto()
|
||||
setTaskDescriptionColorAuto()
|
||||
setLightNavigationBar(true)
|
||||
|
||||
val primaryColor = ThemeStore.primaryColor(this)
|
||||
toolbar.setBackgroundColor(primaryColor)
|
||||
appBarLayout.setBackgroundColor(primaryColor)
|
||||
|
||||
toolbar.setNavigationIcon(R.drawable.ic_keyboard_backspace_black_24dp)
|
||||
toolbar.setNavigationOnClickListener { onBackPressed() }
|
||||
bannerTitle.setTextColor(ThemeStore.textColorPrimary(this))
|
||||
setSupportActionBar(toolbar)
|
||||
title = null
|
||||
ToolbarContentTintHelper.colorBackButton(toolbar, ThemeStore.accentColor(this))
|
||||
|
||||
restoreButton.isEnabled = false
|
||||
purchaseButton.isEnabled = false
|
||||
|
||||
billingProcessor = BillingProcessor(this, BuildConfig.GOOGLE_PLAY_LICENSING_KEY, this)
|
||||
|
||||
MaterialUtil.setTint(restoreButton, false)
|
||||
MaterialUtil.setTint(purchaseButton, true)
|
||||
|
||||
restoreButton.setOnClickListener {
|
||||
if (restorePurchaseAsyncTask == null || restorePurchaseAsyncTask!!.status != AsyncTask.Status.RUNNING) {
|
||||
restorePurchase()
|
||||
}
|
||||
|
||||
}
|
||||
purchaseButton.setOnClickListener {
|
||||
billingProcessor.purchase(this@PurchaseActivity, App.PRO_VERSION_PRODUCT_ID)
|
||||
}
|
||||
}
|
||||
|
||||
private fun restorePurchase() {
|
||||
if (restorePurchaseAsyncTask != null) {
|
||||
restorePurchaseAsyncTask!!.cancel(false)
|
||||
}
|
||||
restorePurchaseAsyncTask = RestorePurchaseAsyncTask(this).execute()
|
||||
}
|
||||
|
||||
|
||||
override fun onProductPurchased(productId: String, details: TransactionDetails?) {
|
||||
Toast.makeText(this, R.string.thank_you, Toast.LENGTH_SHORT).show()
|
||||
setResult(RESULT_OK)
|
||||
}
|
||||
|
||||
override fun onPurchaseHistoryRestored() {
|
||||
if (App.isProVersion) {
|
||||
Toast.makeText(this, R.string.restored_previous_purchase_please_restart, Toast.LENGTH_LONG).show()
|
||||
setResult(RESULT_OK)
|
||||
} else {
|
||||
Toast.makeText(this, R.string.no_purchase_found, Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onBillingError(errorCode: Int, error: Throwable?) {
|
||||
Log.e(TAG, "Billing error: code = $errorCode", error)
|
||||
}
|
||||
|
||||
|
||||
override fun onBillingInitialized() {
|
||||
restoreButton.isEnabled = true
|
||||
purchaseButton.isEnabled = true
|
||||
}
|
||||
|
||||
|
||||
public override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
||||
if (!billingProcessor.handleActivityResult(requestCode, resultCode, data)) {
|
||||
super.onActivityResult(requestCode, resultCode, data)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||
when (item.itemId) {
|
||||
android.R.id.home -> finish()
|
||||
}
|
||||
return super.onOptionsItemSelected(item)
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
billingProcessor.release()
|
||||
super.onDestroy()
|
||||
}
|
||||
|
||||
private class RestorePurchaseAsyncTask internal constructor(purchaseActivity: PurchaseActivity) : AsyncTask<Void, Void, Boolean>() {
|
||||
|
||||
private val buyActivityWeakReference: WeakReference<PurchaseActivity> = WeakReference(purchaseActivity)
|
||||
|
||||
override fun onPreExecute() {
|
||||
super.onPreExecute()
|
||||
val purchaseActivity = buyActivityWeakReference.get()
|
||||
if (purchaseActivity != null) {
|
||||
Toast.makeText(purchaseActivity, R.string.restoring_purchase, Toast.LENGTH_SHORT).show()
|
||||
} else {
|
||||
cancel(false)
|
||||
}
|
||||
}
|
||||
|
||||
override fun doInBackground(vararg params: Void): Boolean? {
|
||||
val purchaseActivity = buyActivityWeakReference.get()
|
||||
if (purchaseActivity != null) {
|
||||
return purchaseActivity.billingProcessor.loadOwnedPurchasesFromGoogle()
|
||||
}
|
||||
cancel(false)
|
||||
return null
|
||||
}
|
||||
|
||||
override fun onPostExecute(b: Boolean?) {
|
||||
super.onPostExecute(b)
|
||||
val purchaseActivity = buyActivityWeakReference.get()
|
||||
if (purchaseActivity == null || b == null) {
|
||||
return
|
||||
}
|
||||
|
||||
if (b) {
|
||||
purchaseActivity.onPurchaseHistoryRestored()
|
||||
} else {
|
||||
Toast.makeText(purchaseActivity, R.string.could_not_restore_purchase, Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val TAG: String = "PurchaseActivity"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,223 @@
|
|||
package code.name.monkey.retromusic.activities
|
||||
|
||||
import android.app.Activity
|
||||
import android.app.SearchManager
|
||||
import android.app.Service
|
||||
import android.content.ActivityNotFoundException
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.res.ColorStateList
|
||||
import android.os.Bundle
|
||||
import android.speech.RecognizerIntent
|
||||
import android.text.Editable
|
||||
import android.text.TextWatcher
|
||||
import android.view.View
|
||||
import android.view.inputmethod.InputMethodManager
|
||||
import android.widget.TextView.BufferType
|
||||
import android.widget.Toast
|
||||
import androidx.appcompat.widget.SearchView.OnQueryTextListener
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
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.mvp.contract.SearchContract
|
||||
import code.name.monkey.retromusic.mvp.presenter.SearchPresenter
|
||||
import code.name.monkey.retromusic.activities.base.AbsMusicServiceActivity
|
||||
import code.name.monkey.retromusic.adapter.SearchAdapter
|
||||
import code.name.monkey.retromusic.util.RetroUtil
|
||||
import kotlinx.android.synthetic.main.activity_search.*
|
||||
import java.util.*
|
||||
|
||||
class SearchActivity : AbsMusicServiceActivity(), OnQueryTextListener, SearchContract.SearchView, TextWatcher {
|
||||
|
||||
private lateinit var searchPresenter: SearchPresenter
|
||||
private var searchAdapter: SearchAdapter? = null
|
||||
private var query: String? = null
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
setDrawUnderStatusBar()
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(R.layout.activity_search)
|
||||
|
||||
searchPresenter = SearchPresenter(this)
|
||||
|
||||
setStatusbarColorAuto()
|
||||
setNavigationbarColorAuto()
|
||||
setTaskDescriptionColorAuto()
|
||||
setLightNavigationBar(true)
|
||||
|
||||
setupRecyclerView()
|
||||
setUpToolBar()
|
||||
setupSearchView()
|
||||
|
||||
if (intent.getBooleanExtra("mic_search", false)) {
|
||||
startMicSearch()
|
||||
}
|
||||
back.setOnClickListener { onBackPressed() }
|
||||
voiceSearch.setOnClickListener { startMicSearch() }
|
||||
|
||||
searchContainer.setCardBackgroundColor(ColorStateList.valueOf(ColorUtil.darkenColor(ThemeStore.primaryColor(this))))
|
||||
|
||||
keyboardPopup.setOnClickListener {
|
||||
val inputManager = getSystemService(Service.INPUT_METHOD_SERVICE) as InputMethodManager
|
||||
inputManager.showSoftInput(searchView,0)
|
||||
}
|
||||
|
||||
keyboardPopup.backgroundTintList = ColorStateList.valueOf(ThemeStore.accentColor(this))
|
||||
ColorStateList.valueOf(MaterialValueHelper.getPrimaryTextColor(this, ColorUtil.isColorLight(ThemeStore.accentColor(this)))).apply {
|
||||
keyboardPopup.setTextColor(this)
|
||||
keyboardPopup.iconTint = this
|
||||
}
|
||||
}
|
||||
|
||||
private fun setupRecyclerView() {
|
||||
searchAdapter = SearchAdapter(this, emptyList())
|
||||
searchAdapter!!.registerAdapterDataObserver(object : RecyclerView.AdapterDataObserver() {
|
||||
override fun onChanged() {
|
||||
super.onChanged()
|
||||
empty.visibility = if (searchAdapter!!.itemCount < 1) View.VISIBLE else View.GONE
|
||||
}
|
||||
})
|
||||
recyclerView.apply {
|
||||
layoutManager = LinearLayoutManager(this@SearchActivity)
|
||||
adapter = searchAdapter
|
||||
}
|
||||
recyclerView.addOnScrollListener(object : RecyclerView.OnScrollListener() {
|
||||
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
|
||||
super.onScrolled(recyclerView, dx, dy)
|
||||
if (dy > 0) {
|
||||
keyboardPopup.shrink(true)
|
||||
} else if (dy < 0) {
|
||||
keyboardPopup.extend(true)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
private fun setupSearchView() {
|
||||
getSystemService(Context.SEARCH_SERVICE) as SearchManager
|
||||
searchView.addTextChangedListener(this)
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
searchPresenter.subscribe()
|
||||
searchPresenter.search(query)
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
super.onDestroy()
|
||||
searchPresenter.unsubscribe()
|
||||
}
|
||||
|
||||
override fun onSaveInstanceState(outState: Bundle) {
|
||||
super.onSaveInstanceState(outState)
|
||||
outState.putString(QUERY, query)
|
||||
}
|
||||
|
||||
override fun onRestoreInstanceState(savedInstanceState: Bundle) {
|
||||
super.onRestoreInstanceState(savedInstanceState)
|
||||
searchPresenter.search(savedInstanceState.getString(QUERY, ""))
|
||||
}
|
||||
|
||||
private fun setUpToolBar() {
|
||||
title = null
|
||||
appBarLayout.setBackgroundColor(ThemeStore.primaryColor(this))
|
||||
}
|
||||
|
||||
|
||||
private fun search(query: String) {
|
||||
this.query = query.trim { it <= ' ' }
|
||||
voiceSearch.visibility = if (query.isNotEmpty()) View.GONE else View.VISIBLE
|
||||
searchPresenter.search(query)
|
||||
}
|
||||
|
||||
override fun onMediaStoreChanged() {
|
||||
super.onMediaStoreChanged()
|
||||
searchPresenter.search(query!!)
|
||||
}
|
||||
|
||||
override fun onQueryTextSubmit(query: String): Boolean {
|
||||
hideSoftKeyboard()
|
||||
return false
|
||||
}
|
||||
|
||||
override fun onQueryTextChange(newText: String): Boolean {
|
||||
search(newText)
|
||||
return false
|
||||
}
|
||||
|
||||
private fun hideSoftKeyboard() {
|
||||
RetroUtil.hideSoftKeyboard(this@SearchActivity)
|
||||
if (searchView != null) {
|
||||
searchView.clearFocus()
|
||||
}
|
||||
}
|
||||
|
||||
override fun loading() {
|
||||
|
||||
}
|
||||
|
||||
override fun showEmptyView() {
|
||||
searchAdapter!!.swapDataSet(ArrayList())
|
||||
}
|
||||
|
||||
override fun completed() {
|
||||
|
||||
}
|
||||
|
||||
override fun showData(list: ArrayList<Any>) {
|
||||
searchAdapter!!.swapDataSet(list)
|
||||
}
|
||||
|
||||
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
||||
super.onActivityResult(requestCode, resultCode, data)
|
||||
when (requestCode) {
|
||||
REQ_CODE_SPEECH_INPUT -> {
|
||||
if (resultCode == Activity.RESULT_OK && null != data) {
|
||||
|
||||
val result = data
|
||||
.getStringArrayListExtra(RecognizerIntent.EXTRA_RESULTS)
|
||||
query = result[0]
|
||||
searchView.setText(query, BufferType.EDITABLE)
|
||||
searchPresenter.search(query!!)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun startMicSearch() {
|
||||
val intent = Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH)
|
||||
intent.putExtra(RecognizerIntent.EXTRA_LANGUAGE_MODEL, RecognizerIntent.LANGUAGE_MODEL_FREE_FORM)
|
||||
intent.putExtra(RecognizerIntent.EXTRA_LANGUAGE, Locale.getDefault())
|
||||
intent.putExtra(RecognizerIntent.EXTRA_PROMPT, getString(R.string.speech_prompt))
|
||||
try {
|
||||
startActivityForResult(intent, REQ_CODE_SPEECH_INPUT)
|
||||
} catch (e: ActivityNotFoundException) {
|
||||
e.printStackTrace()
|
||||
Toast.makeText(this, getString(R.string.speech_not_supported), Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {
|
||||
|
||||
}
|
||||
|
||||
override fun onTextChanged(newText: CharSequence, start: Int, before: Int, count: Int) {
|
||||
search(newText.toString())
|
||||
}
|
||||
|
||||
override fun afterTextChanged(s: Editable) {
|
||||
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
val TAG: String = SearchActivity::class.java.simpleName
|
||||
const val QUERY: String = "query"
|
||||
private const val REQ_CODE_SPEECH_INPUT = 9002
|
||||
}
|
||||
}
|
125
app/src/main/java/code/name/monkey/retromusic/activities/SettingsActivity.kt
Executable file
125
app/src/main/java/code/name/monkey/retromusic/activities/SettingsActivity.kt
Executable file
|
@ -0,0 +1,125 @@
|
|||
package code.name.monkey.retromusic.activities
|
||||
|
||||
import android.content.SharedPreferences
|
||||
import android.os.Bundle
|
||||
import android.view.MenuItem
|
||||
import androidx.annotation.StringRes
|
||||
import androidx.fragment.app.Fragment
|
||||
import code.name.monkey.appthemehelper.ThemeStore
|
||||
import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper
|
||||
import code.name.monkey.retromusic.R
|
||||
import code.name.monkey.retromusic.activities.base.AbsBaseActivity
|
||||
import code.name.monkey.retromusic.fragments.settings.MainSettingsFragment
|
||||
import code.name.monkey.retromusic.util.PreferenceUtil
|
||||
import kotlinx.android.synthetic.main.activity_settings.*
|
||||
|
||||
|
||||
class SettingsActivity : AbsBaseActivity(), /*ColorChooserDialog.ColorCallback,*/ SharedPreferences.OnSharedPreferenceChangeListener {
|
||||
|
||||
private val fragmentManager = supportFragmentManager
|
||||
|
||||
/* override fun onColorSelection(dialog: ColorChooserDialog, @ColorInt selectedColor: Int) {
|
||||
when (dialog.title) {
|
||||
R.string.primary_color -> {
|
||||
val theme = if (ColorUtil.isColorLight(selectedColor))
|
||||
PreferenceUtil.getThemeResFromPrefValue("light")
|
||||
else
|
||||
PreferenceUtil.getThemeResFromPrefValue("dark")
|
||||
|
||||
ThemeStore.editTheme(this).activityTheme(theme).primaryColor(selectedColor).commit()
|
||||
}
|
||||
R.string.accent_color -> ThemeStore.editTheme(this).accentColor(selectedColor).commit()
|
||||
}
|
||||
if (VersionUtils.hasNougatMR())
|
||||
DynamicShortcutManager(this).updateDynamicShortcuts()
|
||||
recreate()
|
||||
}
|
||||
|
||||
override fun onColorChooserDismissed(dialog: ColorChooserDialog) {
|
||||
|
||||
}*/
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(R.layout.activity_settings)
|
||||
|
||||
setStatusbarColorAuto()
|
||||
setNavigationbarColorAuto()
|
||||
|
||||
setLightNavigationBar(true)
|
||||
|
||||
setupToolbar()
|
||||
|
||||
if (savedInstanceState == null) {
|
||||
fragmentManager.beginTransaction().replace(R.id.contentFrame, MainSettingsFragment())
|
||||
.commit()
|
||||
}
|
||||
}
|
||||
|
||||
private fun setupToolbar() {
|
||||
setSupportActionBar(toolbar)
|
||||
title = null
|
||||
toolbar.apply {
|
||||
setBackgroundColor(ThemeStore.primaryColor(context))
|
||||
setNavigationOnClickListener { onBackPressed() }
|
||||
ToolbarContentTintHelper.colorBackButton(toolbar, ThemeStore.textColorSecondary(context))
|
||||
}
|
||||
appBarLayout.setBackgroundColor(ThemeStore.primaryColor(this))
|
||||
settingsTitle.setTextColor(ThemeStore.textColorPrimary(this))
|
||||
}
|
||||
|
||||
fun setupFragment(fragment: Fragment, @StringRes titleName: Int) {
|
||||
val fragmentTransaction = fragmentManager
|
||||
.beginTransaction()
|
||||
.setCustomAnimations(R.anim.sliding_in_left, R.anim.sliding_out_right, android.R.anim.slide_in_left, android.R.anim.slide_out_right)
|
||||
|
||||
settingsTitle.setText(titleName)
|
||||
|
||||
if (detailContentFrame == null) {
|
||||
fragmentTransaction.replace(R.id.contentFrame, fragment, fragment.tag)
|
||||
fragmentTransaction.addToBackStack(null)
|
||||
fragmentTransaction.commit()
|
||||
} else {
|
||||
fragmentTransaction.replace(R.id.detailContentFrame, fragment, fragment.tag)
|
||||
fragmentTransaction.commit()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onBackPressed() {
|
||||
if (fragmentManager.backStackEntryCount == 0) {
|
||||
super.onBackPressed()
|
||||
} else {
|
||||
settingsTitle.setText(R.string.action_settings)
|
||||
fragmentManager.popBackStack()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||
if (item.itemId == android.R.id.home) {
|
||||
onBackPressed()
|
||||
return true
|
||||
}
|
||||
return super.onOptionsItemSelected(item)
|
||||
}
|
||||
|
||||
public override fun onPause() {
|
||||
super.onPause()
|
||||
PreferenceUtil.getInstance().unregisterOnSharedPreferenceChangedListener(this)
|
||||
}
|
||||
|
||||
public override fun onResume() {
|
||||
super.onResume()
|
||||
PreferenceUtil.getInstance().registerOnSharedPreferenceChangedListener(this)
|
||||
}
|
||||
|
||||
override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences, key: String) {
|
||||
if (key == PreferenceUtil.PROFILE_IMAGE_PATH) {
|
||||
recreate()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
companion object {
|
||||
const val TAG: String = "SettingsActivity"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,246 @@
|
|||
package code.name.monkey.retromusic.activities
|
||||
|
||||
import android.content.Intent
|
||||
import android.graphics.Paint
|
||||
import android.os.AsyncTask
|
||||
import android.os.Bundle
|
||||
import android.util.Log
|
||||
import android.view.LayoutInflater
|
||||
import android.view.MenuItem
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.TextView
|
||||
import android.widget.Toast
|
||||
import androidx.annotation.LayoutRes
|
||||
import androidx.recyclerview.widget.DefaultItemAnimator
|
||||
import androidx.recyclerview.widget.GridLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import code.name.monkey.appthemehelper.ThemeStore
|
||||
import code.name.monkey.appthemehelper.util.ATHUtil
|
||||
import code.name.monkey.appthemehelper.util.TintHelper
|
||||
import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper
|
||||
import code.name.monkey.retromusic.BuildConfig
|
||||
import code.name.monkey.retromusic.R
|
||||
import code.name.monkey.retromusic.activities.base.AbsBaseActivity
|
||||
import code.name.monkey.retromusic.views.IconImageView
|
||||
import com.anjlab.android.iab.v3.BillingProcessor
|
||||
import com.anjlab.android.iab.v3.SkuDetails
|
||||
import com.anjlab.android.iab.v3.TransactionDetails
|
||||
import kotlinx.android.synthetic.main.activity_donation.*
|
||||
import java.lang.ref.WeakReference
|
||||
import java.util.*
|
||||
|
||||
class SupportDevelopmentActivity : AbsBaseActivity(), BillingProcessor.IBillingHandler {
|
||||
|
||||
|
||||
companion object {
|
||||
val TAG: String = SupportDevelopmentActivity::class.java.simpleName
|
||||
const val DONATION_PRODUCT_IDS = R.array.donation_ids
|
||||
private const val TEZ_REQUEST_CODE = 123
|
||||
}
|
||||
|
||||
var billingProcessor: BillingProcessor? = null
|
||||
private var skuDetailsLoadAsyncTask: AsyncTask<*, *, *>? = null
|
||||
|
||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||
if (item.itemId == android.R.id.home) {
|
||||
onBackPressed()
|
||||
return true
|
||||
}
|
||||
return super.onOptionsItemSelected(item)
|
||||
}
|
||||
|
||||
fun donate(i: Int) {
|
||||
val ids = resources.getStringArray(DONATION_PRODUCT_IDS)
|
||||
billingProcessor!!.purchase(this, ids[i])
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(R.layout.activity_donation)
|
||||
|
||||
|
||||
|
||||
setStatusbarColorAuto()
|
||||
setNavigationbarColorAuto()
|
||||
setTaskDescriptionColorAuto()
|
||||
setLightNavigationBar(true)
|
||||
|
||||
setupToolbar()
|
||||
|
||||
billingProcessor = BillingProcessor(this, BuildConfig.GOOGLE_PLAY_LICENSING_KEY, this)
|
||||
TintHelper.setTint(progress, ThemeStore.accentColor(this))
|
||||
donation.setTextColor(ThemeStore.accentColor(this))
|
||||
}
|
||||
|
||||
private fun setupToolbar() {
|
||||
bannerTitle.setTextColor(ThemeStore.textColorPrimary(this))
|
||||
val primaryColor = ThemeStore.primaryColor(this)
|
||||
appBarLayout.setBackgroundColor(primaryColor)
|
||||
toolbar.setBackgroundColor(primaryColor)
|
||||
|
||||
toolbar.setNavigationOnClickListener { onBackPressed() }
|
||||
toolbar.setNavigationIcon(R.drawable.ic_keyboard_backspace_black_24dp)
|
||||
setSupportActionBar(toolbar)
|
||||
title = null
|
||||
|
||||
ToolbarContentTintHelper.colorBackButton(toolbar, ThemeStore.textColorSecondary(this))
|
||||
}
|
||||
|
||||
override fun onBillingInitialized() {
|
||||
loadSkuDetails()
|
||||
}
|
||||
|
||||
private fun loadSkuDetails() {
|
||||
if (skuDetailsLoadAsyncTask != null) {
|
||||
skuDetailsLoadAsyncTask!!.cancel(false)
|
||||
}
|
||||
skuDetailsLoadAsyncTask = SkuDetailsLoadAsyncTask(this).execute()
|
||||
|
||||
}
|
||||
|
||||
override fun onProductPurchased(productId: String, details: TransactionDetails?) {
|
||||
//loadSkuDetails();
|
||||
Toast.makeText(this, R.string.thank_you, Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
|
||||
override fun onBillingError(errorCode: Int, error: Throwable?) {
|
||||
Log.e(TAG, "Billing error: code = $errorCode", error)
|
||||
}
|
||||
|
||||
override fun onPurchaseHistoryRestored() {
|
||||
//loadSkuDetails();
|
||||
Toast.makeText(this, R.string.restored_previous_purchases, Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
|
||||
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
||||
if (!billingProcessor!!.handleActivityResult(requestCode, resultCode, data)) {
|
||||
super.onActivityResult(requestCode, resultCode, data)
|
||||
}
|
||||
if (requestCode == TEZ_REQUEST_CODE) {
|
||||
// Process based on the data in response.
|
||||
Log.d("result", data!!.getStringExtra("Status"))
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
if (billingProcessor != null) {
|
||||
billingProcessor!!.release()
|
||||
}
|
||||
if (skuDetailsLoadAsyncTask != null) {
|
||||
skuDetailsLoadAsyncTask!!.cancel(true)
|
||||
}
|
||||
super.onDestroy()
|
||||
}
|
||||
}
|
||||
|
||||
private class SkuDetailsLoadAsyncTask internal constructor(supportDevelopmentActivity: SupportDevelopmentActivity) : AsyncTask<Void, Void, List<SkuDetails>>() {
|
||||
private val weakReference: WeakReference<SupportDevelopmentActivity> = WeakReference(supportDevelopmentActivity)
|
||||
|
||||
override fun onPreExecute() {
|
||||
super.onPreExecute()
|
||||
val supportDevelopmentActivity = weakReference.get() ?: return
|
||||
|
||||
supportDevelopmentActivity.progressContainer.visibility = View.VISIBLE
|
||||
supportDevelopmentActivity.recyclerView.visibility = View.GONE
|
||||
}
|
||||
|
||||
override fun doInBackground(vararg params: Void): List<SkuDetails>? {
|
||||
val dialog = weakReference.get()
|
||||
if (dialog != null) {
|
||||
val ids = dialog.resources.getStringArray(SupportDevelopmentActivity.DONATION_PRODUCT_IDS)
|
||||
return dialog.billingProcessor!!.getPurchaseListingDetails(ArrayList(Arrays.asList(*ids)))
|
||||
}
|
||||
cancel(false)
|
||||
return null
|
||||
}
|
||||
|
||||
override fun onPostExecute(skuDetails: List<SkuDetails>?) {
|
||||
super.onPostExecute(skuDetails)
|
||||
val dialog = weakReference.get() ?: return
|
||||
|
||||
if (skuDetails == null || skuDetails.isEmpty()) {
|
||||
dialog.progressContainer.visibility = View.GONE
|
||||
return
|
||||
}
|
||||
|
||||
dialog.progressContainer.visibility = View.GONE
|
||||
dialog.recyclerView.itemAnimator = DefaultItemAnimator()
|
||||
dialog.recyclerView.layoutManager = GridLayoutManager(dialog, 2)
|
||||
dialog.recyclerView.adapter = SkuDetailsAdapter(dialog, skuDetails)
|
||||
dialog.recyclerView.visibility = View.VISIBLE
|
||||
}
|
||||
}
|
||||
|
||||
class SkuDetailsAdapter(private var donationsDialog: SupportDevelopmentActivity, objects: List<SkuDetails>) : RecyclerView.Adapter<SkuDetailsAdapter.ViewHolder>() {
|
||||
private var skuDetailsList: List<SkuDetails> = ArrayList()
|
||||
|
||||
init {
|
||||
skuDetailsList = objects
|
||||
}
|
||||
|
||||
private fun getIcon(position: Int): Int {
|
||||
return when (position) {
|
||||
0 -> R.drawable.ic_cookie_white_24dp
|
||||
1 -> R.drawable.ic_take_away_white_24dp
|
||||
2 -> R.drawable.ic_take_away_coffe_white_24dp
|
||||
3 -> R.drawable.ic_beer_white_24dp
|
||||
4 -> R.drawable.ic_fast_food_meal_white_24dp
|
||||
5 -> R.drawable.ic_popcorn_white_24dp
|
||||
6 -> R.drawable.ic_card_giftcard_white_24dp
|
||||
else -> R.drawable.ic_card_giftcard_white_24dp
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreateViewHolder(viewGroup: ViewGroup, i: Int): ViewHolder {
|
||||
return ViewHolder(LayoutInflater.from(donationsDialog).inflate(LAYOUT_RES_ID, viewGroup, false))
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(viewHolder: ViewHolder, i: Int) {
|
||||
val skuDetails = skuDetailsList[i]
|
||||
viewHolder.title.text = skuDetails.title.replace("(Retro Music Player)", "").trim { it <= ' ' }
|
||||
viewHolder.text.text = skuDetails.description
|
||||
viewHolder.text.visibility = View.GONE
|
||||
viewHolder.price.text = skuDetails.priceText
|
||||
viewHolder.image.setImageResource(getIcon(i))
|
||||
|
||||
val purchased = donationsDialog.billingProcessor!!.isPurchased(skuDetails.productId)
|
||||
val titleTextColor = if (purchased) ATHUtil.resolveColor(donationsDialog, android.R.attr.textColorHint) else ThemeStore.textColorPrimary(donationsDialog)
|
||||
val contentTextColor = if (purchased) titleTextColor else ThemeStore.textColorSecondary(donationsDialog)
|
||||
|
||||
|
||||
viewHolder.title.setTextColor(titleTextColor)
|
||||
viewHolder.text.setTextColor(contentTextColor)
|
||||
viewHolder.price.setTextColor(titleTextColor)
|
||||
|
||||
strikeThrough(viewHolder.title, purchased)
|
||||
strikeThrough(viewHolder.text, purchased)
|
||||
strikeThrough(viewHolder.price, purchased)
|
||||
|
||||
viewHolder.itemView.setOnTouchListener { _, _ -> purchased }
|
||||
viewHolder.itemView.setOnClickListener { donationsDialog.donate(i) }
|
||||
}
|
||||
|
||||
override fun getItemCount(): Int {
|
||||
return skuDetailsList.size
|
||||
}
|
||||
|
||||
class ViewHolder(view: View) : RecyclerView.ViewHolder(view) {
|
||||
var title: TextView = view.findViewById(R.id.itemTitle)
|
||||
var text: TextView = view.findViewById(R.id.itemText)
|
||||
var price: TextView = view.findViewById(R.id.itemPrice)
|
||||
var image: IconImageView = view.findViewById(R.id.itemImage)
|
||||
}
|
||||
|
||||
companion object {
|
||||
@LayoutRes
|
||||
private val LAYOUT_RES_ID = R.layout.item_donation_option
|
||||
|
||||
private fun strikeThrough(textView: TextView, strikeThrough: Boolean) {
|
||||
textView.paintFlags = if (strikeThrough)
|
||||
textView.paintFlags or Paint.STRIKE_THRU_TEXT_FLAG
|
||||
else
|
||||
textView.paintFlags and Paint.STRIKE_THRU_TEXT_FLAG.inv()
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,368 @@
|
|||
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.*
|
||||
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.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.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()
|
||||
|
||||
bannerTitle.setTextColor(ThemeStore.textColorPrimary(this))
|
||||
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).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)
|
||||
toolbar.apply {
|
||||
setNavigationIcon(R.drawable.ic_keyboard_backspace_black_24dp)
|
||||
setBackgroundColor(primaryColor)
|
||||
ToolbarContentTintHelper.colorBackButton(this, ThemeStore.textColorSecondary(this@UserInfoActivity))
|
||||
setSupportActionBar(this)
|
||||
}
|
||||
appBarLayout.setBackgroundColor(primaryColor)
|
||||
title = null
|
||||
}
|
||||
|
||||
private fun showBannerOptions() {
|
||||
MaterialDialog(this).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) {
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
/*if (requestCode == PICK_IMAGE_REQUEST && resultCode == Activity.RESULT_OK && data != null && data.data != null) {
|
||||
val picturePath = data.data
|
||||
performCrop(picturePath)
|
||||
} else if (requestCode == CROP_IMAGE_REQUEST && resultCode == Activity.RESULT_OK && data != null && data.data != null) {
|
||||
val uri = data.data
|
||||
try {
|
||||
val bitmap = ImageUtil.getResizedBitmap(Media.getBitmap(contentResolver, uri), PROFILE_ICON_SIZE)
|
||||
val profileImagePath = saveToInternalStorage(bitmap, USER_PROFILE)
|
||||
PreferenceUtil.getInstance().saveProfileImage(profileImagePath)
|
||||
loadImageFromStorage(profileImagePath)
|
||||
} catch (e: IOException) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
} else if (requestCode == PICK_BANNER_REQUEST && resultCode == Activity.RESULT_OK && data != null &&
|
||||
data.data != null) {
|
||||
val uri = data.data
|
||||
try {
|
||||
val bitmap = Media.getBitmap(contentResolver, uri)
|
||||
val profileImagePath = saveToInternalStorage(bitmap, USER_BANNER)
|
||||
PreferenceUtil.getInstance().setBannerImagePath(profileImagePath)
|
||||
loadBannerFromStorage(profileImagePath)
|
||||
} catch (e: IOException) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
|
||||
}*/
|
||||
}
|
||||
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,95 @@
|
|||
package code.name.monkey.retromusic.activities;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.pm.PackageInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.os.Bundle;
|
||||
import android.webkit.WebView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.google.android.material.appbar.AppBarLayout;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.appcompat.widget.Toolbar;
|
||||
|
||||
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.ToolbarContentTintHelper;
|
||||
import code.name.monkey.retromusic.R;
|
||||
import code.name.monkey.retromusic.activities.base.AbsBaseActivity;
|
||||
import code.name.monkey.retromusic.util.PreferenceUtil;
|
||||
|
||||
public class WhatsNewActivity extends AbsBaseActivity {
|
||||
WebView webView;
|
||||
TextView title;
|
||||
Toolbar toolbar;
|
||||
AppBarLayout appBarLayout;
|
||||
|
||||
|
||||
private static void setChangelogRead(@NonNull Context context) {
|
||||
try {
|
||||
PackageInfo pInfo = context.getPackageManager().getPackageInfo(context.getPackageName(), 0);
|
||||
int currentVersion = pInfo.versionCode;
|
||||
PreferenceUtil.getInstance().setLastChangeLogVersion(currentVersion);
|
||||
} catch (PackageManager.NameNotFoundException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
private static String colorToHex(int color) {
|
||||
return Integer.toHexString(color).substring(2);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_whats_new);
|
||||
|
||||
webView = findViewById(R.id.webView);
|
||||
title = findViewById(R.id.bannerTitle);
|
||||
toolbar = findViewById(R.id.toolbar);
|
||||
appBarLayout = findViewById(R.id.appBarLayout);
|
||||
|
||||
|
||||
setStatusbarColorAuto();
|
||||
setNavigationbarColorAuto();
|
||||
setTaskDescriptionColorAuto();
|
||||
|
||||
toolbar.setBackgroundColor(ThemeStore.Companion.primaryColor(this));
|
||||
appBarLayout.setBackgroundColor(ThemeStore.Companion.primaryColor(this));
|
||||
setSupportActionBar(toolbar);
|
||||
setTitle(null);
|
||||
toolbar.setNavigationOnClickListener(v -> onBackPressed());
|
||||
title.setTextColor(ThemeStore.Companion.textColorPrimary(this));
|
||||
ToolbarContentTintHelper.colorBackButton(toolbar, ThemeStore.Companion.textColorSecondary(this));
|
||||
|
||||
try {
|
||||
// Load from phonograph-changelog.html in the assets folder
|
||||
StringBuilder buf = new StringBuilder();
|
||||
InputStream json = getAssets().open("retro-changelog.html");
|
||||
BufferedReader in = new BufferedReader(new InputStreamReader(json, "UTF-8"));
|
||||
String str;
|
||||
while ((str = in.readLine()) != null)
|
||||
buf.append(str);
|
||||
in.close();
|
||||
|
||||
// Inject color values for WebView body background and links
|
||||
final String backgroundColor = colorToHex(ThemeStore.Companion.primaryColor(this));
|
||||
final String contentColor = ATHUtil.INSTANCE.isWindowBackgroundDark(this) ? "#ffffff" : "#000000";
|
||||
webView.loadData(buf.toString()
|
||||
.replace("{style-placeholder}",
|
||||
String.format("body { background-color: %s; color: %s; }", backgroundColor, contentColor))
|
||||
.replace("{link-color}", colorToHex(ThemeStore.Companion.accentColor(this)))
|
||||
.replace("{link-color-active}", colorToHex(ColorUtil.INSTANCE.lightenColor(ThemeStore.Companion.accentColor(this))))
|
||||
, "text/html", "UTF-8");
|
||||
} catch (Throwable e) {
|
||||
webView.loadData("<h1>Unable to load</h1><p>" + e.getLocalizedMessage() + "</p>", "text/html", "UTF-8");
|
||||
}
|
||||
setChangelogRead(this);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,138 @@
|
|||
package code.name.monkey.retromusic.activities.base
|
||||
|
||||
import android.Manifest
|
||||
import android.content.Intent
|
||||
import android.content.pm.PackageManager
|
||||
import android.media.AudioManager
|
||||
import android.net.Uri
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.provider.Settings
|
||||
import android.view.KeyEvent
|
||||
import android.view.View
|
||||
import androidx.core.app.ActivityCompat
|
||||
import code.name.monkey.appthemehelper.ThemeStore
|
||||
import com.google.android.material.snackbar.Snackbar
|
||||
|
||||
|
||||
abstract class AbsBaseActivity : AbsThemeActivity() {
|
||||
private var hadPermissions: Boolean = false
|
||||
private lateinit var permissions: Array<String>
|
||||
private var permissionDeniedMessage: String? = null
|
||||
|
||||
|
||||
open fun getPermissionsToRequest(): Array<String> {
|
||||
return arrayOf()
|
||||
}
|
||||
|
||||
protected fun setPermissionDeniedMessage(message: String) {
|
||||
permissionDeniedMessage = message
|
||||
}
|
||||
|
||||
fun getPermissionDeniedMessage(): String {
|
||||
return if (permissionDeniedMessage == null) getString(code.name.monkey.retromusic.R.string.permissions_denied) else permissionDeniedMessage!!
|
||||
}
|
||||
|
||||
|
||||
private val snackBarContainer: View
|
||||
get() = window.decorView
|
||||
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
volumeControlStream = AudioManager.STREAM_MUSIC
|
||||
permissions = getPermissionsToRequest()
|
||||
hadPermissions = hasPermissions()
|
||||
permissionDeniedMessage = null
|
||||
}
|
||||
|
||||
override fun onPostCreate(savedInstanceState: Bundle?) {
|
||||
super.onPostCreate(savedInstanceState)
|
||||
if (!hasPermissions()) {
|
||||
requestPermissions()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
val hasPermissions = hasPermissions()
|
||||
if (hasPermissions != hadPermissions) {
|
||||
hadPermissions = hasPermissions
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
onHasPermissionsChanged(hasPermissions)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected open fun onHasPermissionsChanged(hasPermissions: Boolean) {
|
||||
// implemented by sub classes
|
||||
}
|
||||
|
||||
override fun dispatchKeyEvent(event: KeyEvent): Boolean {
|
||||
if (event.keyCode == KeyEvent.KEYCODE_MENU && event.action == KeyEvent.ACTION_UP) {
|
||||
showOverflowMenu()
|
||||
return true
|
||||
}
|
||||
return super.dispatchKeyEvent(event)
|
||||
}
|
||||
|
||||
protected fun showOverflowMenu() {
|
||||
|
||||
}
|
||||
|
||||
protected open fun requestPermissions() {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
requestPermissions(permissions, PERMISSION_REQUEST)
|
||||
}
|
||||
}
|
||||
|
||||
protected fun hasPermissions(): Boolean {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
for (permission in permissions) {
|
||||
if (checkSelfPermission(permission) != PackageManager.PERMISSION_GRANTED) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<String>, grantResults: IntArray) {
|
||||
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
|
||||
if (requestCode == PERMISSION_REQUEST) {
|
||||
for (grantResult in grantResults) {
|
||||
if (grantResult != PackageManager.PERMISSION_GRANTED) {
|
||||
if (ActivityCompat.shouldShowRequestPermissionRationale(this@AbsBaseActivity,
|
||||
Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
|
||||
//User has deny from permission dialog
|
||||
Snackbar.make(snackBarContainer, permissionDeniedMessage!!,
|
||||
Snackbar.LENGTH_INDEFINITE)
|
||||
.setAction(code.name.monkey.retromusic.R.string.action_grant) { requestPermissions() }
|
||||
.setActionTextColor(ThemeStore.accentColor(this))
|
||||
.show()
|
||||
} else {
|
||||
// User has deny permission and checked never show permission dialog so you can redirect to Application settings page
|
||||
Snackbar.make(snackBarContainer, permissionDeniedMessage!!,
|
||||
Snackbar.LENGTH_INDEFINITE)
|
||||
.setAction(code.name.monkey.retromusic.R.string.action_settings) {
|
||||
val intent = Intent()
|
||||
intent.action = Settings.ACTION_APPLICATION_DETAILS_SETTINGS
|
||||
val uri = Uri.fromParts("package", this@AbsBaseActivity.packageName, null)
|
||||
intent.data = uri
|
||||
startActivity(intent)
|
||||
}
|
||||
.setActionTextColor(ThemeStore.accentColor(this))
|
||||
.show()
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
hadPermissions = true
|
||||
onHasPermissionsChanged(true)
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val PERMISSION_REQUEST = 100
|
||||
}
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
package code.name.monkey.retromusic.activities.base
|
||||
|
||||
import android.os.Bundle
|
||||
import code.name.monkey.appthemehelper.ATHActivity
|
||||
import code.name.monkey.retromusic.helper.TopExceptionHandler
|
||||
|
||||
abstract class AbsCrashCollector : ATHActivity() {
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
Thread.setDefaultUncaughtExceptionHandler(TopExceptionHandler())
|
||||
}
|
||||
}
|
|
@ -0,0 +1,168 @@
|
|||
package code.name.monkey.retromusic.activities.base
|
||||
|
||||
import android.Manifest
|
||||
import android.content.*
|
||||
import android.os.Bundle
|
||||
import android.os.IBinder
|
||||
import code.name.monkey.retromusic.Constants.MEDIA_STORE_CHANGED
|
||||
import code.name.monkey.retromusic.Constants.META_CHANGED
|
||||
import code.name.monkey.retromusic.Constants.PLAY_STATE_CHANGED
|
||||
import code.name.monkey.retromusic.Constants.QUEUE_CHANGED
|
||||
import code.name.monkey.retromusic.Constants.REPEAT_MODE_CHANGED
|
||||
import code.name.monkey.retromusic.Constants.SHUFFLE_MODE_CHANGED
|
||||
import code.name.monkey.retromusic.R
|
||||
import code.name.monkey.retromusic.helper.MusicPlayerRemote
|
||||
import code.name.monkey.retromusic.interfaces.MusicServiceEventListener
|
||||
import java.lang.ref.WeakReference
|
||||
import java.util.*
|
||||
|
||||
|
||||
abstract class AbsMusicServiceActivity : AbsBaseActivity(), MusicServiceEventListener {
|
||||
|
||||
private val mMusicServiceEventListeners = ArrayList<MusicServiceEventListener>()
|
||||
|
||||
private var serviceToken: MusicPlayerRemote.ServiceToken? = null
|
||||
private var musicStateReceiver: MusicStateReceiver? = null
|
||||
private var receiverRegistered: Boolean = false
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
serviceToken = MusicPlayerRemote.bindToService(this, object : ServiceConnection {
|
||||
override fun onServiceConnected(name: ComponentName, service: IBinder) {
|
||||
this@AbsMusicServiceActivity.onServiceConnected()
|
||||
}
|
||||
|
||||
override fun onServiceDisconnected(name: ComponentName) {
|
||||
this@AbsMusicServiceActivity.onServiceDisconnected()
|
||||
}
|
||||
})
|
||||
|
||||
setPermissionDeniedMessage(getString(R.string.permission_external_storage_denied));
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
super.onDestroy()
|
||||
MusicPlayerRemote.unbindFromService(serviceToken)
|
||||
if (receiverRegistered) {
|
||||
unregisterReceiver(musicStateReceiver)
|
||||
receiverRegistered = false
|
||||
}
|
||||
}
|
||||
|
||||
fun addMusicServiceEventListener(listener: MusicServiceEventListener?) {
|
||||
if (listener != null) {
|
||||
mMusicServiceEventListeners.add(listener)
|
||||
}
|
||||
}
|
||||
|
||||
fun removeMusicServiceEventListener(listener: MusicServiceEventListener?) {
|
||||
if (listener != null) {
|
||||
mMusicServiceEventListeners.remove(listener)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onServiceConnected() {
|
||||
if (!receiverRegistered) {
|
||||
musicStateReceiver = MusicStateReceiver(this)
|
||||
|
||||
val filter = IntentFilter()
|
||||
filter.addAction(PLAY_STATE_CHANGED)
|
||||
filter.addAction(SHUFFLE_MODE_CHANGED)
|
||||
filter.addAction(REPEAT_MODE_CHANGED)
|
||||
filter.addAction(META_CHANGED)
|
||||
filter.addAction(QUEUE_CHANGED)
|
||||
filter.addAction(MEDIA_STORE_CHANGED)
|
||||
|
||||
registerReceiver(musicStateReceiver, filter)
|
||||
|
||||
receiverRegistered = true
|
||||
}
|
||||
|
||||
for (listener in mMusicServiceEventListeners) {
|
||||
listener.onServiceConnected()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onServiceDisconnected() {
|
||||
if (receiverRegistered) {
|
||||
unregisterReceiver(musicStateReceiver)
|
||||
receiverRegistered = false
|
||||
}
|
||||
|
||||
for (listener in mMusicServiceEventListeners) {
|
||||
listener.onServiceDisconnected()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onPlayingMetaChanged() {
|
||||
for (listener in mMusicServiceEventListeners) {
|
||||
listener.onPlayingMetaChanged()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onQueueChanged() {
|
||||
for (listener in mMusicServiceEventListeners) {
|
||||
listener.onQueueChanged()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onPlayStateChanged() {
|
||||
for (listener in mMusicServiceEventListeners) {
|
||||
listener.onPlayStateChanged()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onMediaStoreChanged() {
|
||||
for (listener in mMusicServiceEventListeners) {
|
||||
listener.onMediaStoreChanged()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onRepeatModeChanged() {
|
||||
for (listener in mMusicServiceEventListeners) {
|
||||
listener.onRepeatModeChanged()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onShuffleModeChanged() {
|
||||
for (listener in mMusicServiceEventListeners) {
|
||||
listener.onShuffleModeChanged()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onHasPermissionsChanged(hasPermissions: Boolean) {
|
||||
super.onHasPermissionsChanged(hasPermissions)
|
||||
val intent = Intent(MEDIA_STORE_CHANGED)
|
||||
intent.putExtra("from_permissions_changed", true) // just in case we need to know this at some point
|
||||
sendBroadcast(intent)
|
||||
}
|
||||
|
||||
|
||||
override fun getPermissionsToRequest(): Array<String> {
|
||||
return arrayOf(Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE)
|
||||
}
|
||||
|
||||
private class MusicStateReceiver(activity: AbsMusicServiceActivity) : BroadcastReceiver() {
|
||||
|
||||
private val reference: WeakReference<AbsMusicServiceActivity> = WeakReference(activity)
|
||||
|
||||
override fun onReceive(context: Context, intent: Intent) {
|
||||
val action = intent.action
|
||||
val activity = reference.get()
|
||||
if (activity != null && action != null) {
|
||||
when (action) {
|
||||
META_CHANGED -> activity.onPlayingMetaChanged()
|
||||
QUEUE_CHANGED -> activity.onQueueChanged()
|
||||
PLAY_STATE_CHANGED -> activity.onPlayStateChanged()
|
||||
REPEAT_MODE_CHANGED -> activity.onRepeatModeChanged()
|
||||
SHUFFLE_MODE_CHANGED -> activity.onShuffleModeChanged()
|
||||
MEDIA_STORE_CHANGED -> activity.onMediaStoreChanged()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
val TAG: String = AbsMusicServiceActivity::class.java.simpleName
|
||||
}
|
||||
}
|
|
@ -0,0 +1,309 @@
|
|||
package code.name.monkey.retromusic.activities.base
|
||||
|
||||
import android.animation.ValueAnimator
|
||||
import android.annotation.SuppressLint
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.view.ViewTreeObserver
|
||||
import androidx.annotation.LayoutRes
|
||||
import androidx.fragment.app.Fragment
|
||||
import code.name.monkey.appthemehelper.ThemeStore
|
||||
import code.name.monkey.appthemehelper.util.ColorUtil
|
||||
import code.name.monkey.retromusic.R
|
||||
import code.name.monkey.retromusic.helper.MusicPlayerRemote
|
||||
import code.name.monkey.retromusic.fragments.MiniPlayerFragment
|
||||
import code.name.monkey.retromusic.fragments.NowPlayingScreen
|
||||
import code.name.monkey.retromusic.fragments.NowPlayingScreen.*
|
||||
import code.name.monkey.retromusic.fragments.base.AbsPlayerFragment
|
||||
import code.name.monkey.retromusic.fragments.player.adaptive.AdaptiveFragment
|
||||
import code.name.monkey.retromusic.fragments.player.blur.BlurPlayerFragment
|
||||
import code.name.monkey.retromusic.fragments.player.card.CardFragment
|
||||
import code.name.monkey.retromusic.fragments.player.cardblur.CardBlurFragment
|
||||
import code.name.monkey.retromusic.fragments.player.color.ColorFragment
|
||||
import code.name.monkey.retromusic.fragments.player.fit.FitFragment
|
||||
import code.name.monkey.retromusic.fragments.player.flat.FlatPlayerFragment
|
||||
import code.name.monkey.retromusic.fragments.player.full.FullPlayerFragment
|
||||
import code.name.monkey.retromusic.fragments.player.material.MaterialFragment
|
||||
import code.name.monkey.retromusic.fragments.player.normal.PlayerFragment
|
||||
import code.name.monkey.retromusic.fragments.player.plain.PlainPlayerFragment
|
||||
import code.name.monkey.retromusic.fragments.player.simple.SimplePlayerFragment
|
||||
import code.name.monkey.retromusic.fragments.player.tiny.TinyPlayerFragment
|
||||
import code.name.monkey.retromusic.util.PreferenceUtil
|
||||
import code.name.monkey.retromusic.views.BottomNavigationBarTinted
|
||||
import com.sothree.slidinguppanel.SlidingUpPanelLayout
|
||||
import kotlinx.android.synthetic.main.sliding_music_panel_layout.*
|
||||
|
||||
abstract class AbsSlidingMusicPanelActivity : AbsMusicServiceActivity(), SlidingUpPanelLayout.PanelSlideListener, AbsPlayerFragment.Callbacks {
|
||||
companion object {
|
||||
val TAG: String = AbsSlidingMusicPanelActivity::class.java.simpleName
|
||||
}
|
||||
|
||||
private var miniPlayerFragment: MiniPlayerFragment? = null
|
||||
private var playerFragment: AbsPlayerFragment? = null
|
||||
private var currentNowPlayingScreen: NowPlayingScreen? = null
|
||||
private var navigationBarColor: Int = 0
|
||||
private var taskColor: Int = 0
|
||||
private var lightStatusBar: Boolean = false
|
||||
private var lightNavigationBar: Boolean = false
|
||||
private var navigationBarColorAnimator: ValueAnimator? = null
|
||||
|
||||
protected abstract fun createContentView(): View
|
||||
|
||||
val panelState: SlidingUpPanelLayout.PanelState?
|
||||
get() = slidingLayout.panelState
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(createContentView())
|
||||
|
||||
|
||||
chooseFragmentForTheme()
|
||||
setupSlidingUpPanel()
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
if (currentNowPlayingScreen != PreferenceUtil.getInstance().nowPlayingScreen) {
|
||||
postRecreate()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
super.onDestroy()
|
||||
if (navigationBarColorAnimator != null) navigationBarColorAnimator!!.cancel() // just in case
|
||||
}
|
||||
|
||||
|
||||
protected fun wrapSlidingMusicPanel(@LayoutRes resId: Int): View {
|
||||
@SuppressLint("InflateParams")
|
||||
val slidingMusicPanelLayout = layoutInflater.inflate(R.layout.sliding_music_panel_layout, null)
|
||||
val contentContainer = slidingMusicPanelLayout.findViewById<ViewGroup>(R.id.mainContentFrame)
|
||||
layoutInflater.inflate(resId, contentContainer)
|
||||
return slidingMusicPanelLayout
|
||||
}
|
||||
|
||||
fun setAntiDragView(antiDragView: View) {
|
||||
slidingLayout.setAntiDragView(antiDragView)
|
||||
}
|
||||
|
||||
private fun collapsePanel() {
|
||||
slidingLayout.panelState = SlidingUpPanelLayout.PanelState.COLLAPSED
|
||||
}
|
||||
|
||||
fun expandPanel() {
|
||||
slidingLayout.panelState = SlidingUpPanelLayout.PanelState.EXPANDED
|
||||
}
|
||||
|
||||
private fun setMiniPlayerAlphaProgress(progress: Float) {
|
||||
if (miniPlayerFragment!!.view == null) return
|
||||
val alpha = 1 - progress
|
||||
miniPlayerFragment!!.view!!.alpha = alpha
|
||||
// necessary to make the views below clickable
|
||||
miniPlayerFragment!!.view!!.visibility = if (alpha == 0f) View.GONE else View.VISIBLE
|
||||
|
||||
bottomNavigationView.translationY = progress * 500
|
||||
bottomNavigationView.alpha = alpha
|
||||
}
|
||||
|
||||
open fun onPanelCollapsed() {
|
||||
// restore values
|
||||
super.setLightStatusbar(lightStatusBar)
|
||||
super.setTaskDescriptionColor(taskColor)
|
||||
super.setNavigationbarColor(navigationBarColor)
|
||||
super.setLightNavigationBar(lightNavigationBar)
|
||||
|
||||
|
||||
playerFragment!!.setMenuVisibility(false)
|
||||
playerFragment!!.userVisibleHint = false
|
||||
playerFragment!!.onHide()
|
||||
}
|
||||
|
||||
open fun onPanelExpanded() {
|
||||
val playerFragmentColor = playerFragment!!.paletteColor
|
||||
super.setTaskDescriptionColor(playerFragmentColor)
|
||||
|
||||
playerFragment!!.setMenuVisibility(true)
|
||||
playerFragment!!.userVisibleHint = true
|
||||
playerFragment!!.onShow()
|
||||
onPaletteColorChanged()
|
||||
}
|
||||
|
||||
private fun setupSlidingUpPanel() {
|
||||
slidingLayout.viewTreeObserver
|
||||
.addOnGlobalLayoutListener(object : ViewTreeObserver.OnGlobalLayoutListener {
|
||||
override fun onGlobalLayout() {
|
||||
slidingLayout.viewTreeObserver.removeOnGlobalLayoutListener(this)
|
||||
|
||||
if (panelState == SlidingUpPanelLayout.PanelState.EXPANDED) {
|
||||
onPanelSlide(slidingLayout, 1f)
|
||||
onPanelExpanded()
|
||||
} else if (panelState == SlidingUpPanelLayout.PanelState.COLLAPSED) {
|
||||
onPanelCollapsed()
|
||||
} else {
|
||||
playerFragment!!.onHide()
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
slidingLayout.addPanelSlideListener(this)
|
||||
|
||||
}
|
||||
|
||||
fun toggleBottomNavigationView(toggle: Boolean) {
|
||||
bottomNavigationView.visibility = if (toggle) View.GONE else View.VISIBLE
|
||||
}
|
||||
|
||||
fun getBottomNavigationView(): BottomNavigationBarTinted {
|
||||
return bottomNavigationView
|
||||
}
|
||||
|
||||
fun hideBottomBar(hide: Boolean) {
|
||||
val heightOfBar = resources.getDimensionPixelSize(R.dimen.mini_player_height)
|
||||
val heightOfBarWithTabs = resources.getDimensionPixelSize(R.dimen.mini_player_height_expanded)
|
||||
|
||||
if (hide) {
|
||||
slidingLayout.panelHeight = 0
|
||||
collapsePanel()
|
||||
} else {
|
||||
if (!MusicPlayerRemote.playingQueue.isEmpty()) {
|
||||
slidingLayout.panelHeight = if (bottomNavigationView.visibility == View.VISIBLE) heightOfBarWithTabs else heightOfBar
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun setBottomBarVisibility(gone: Int) {
|
||||
bottomNavigationView.visibility = gone
|
||||
hideBottomBar(false)
|
||||
}
|
||||
|
||||
private fun chooseFragmentForTheme() {
|
||||
currentNowPlayingScreen = PreferenceUtil.getInstance().nowPlayingScreen
|
||||
|
||||
val fragment: Fragment = when (currentNowPlayingScreen) {
|
||||
BLUR -> BlurPlayerFragment()
|
||||
ADAPTIVE -> AdaptiveFragment()
|
||||
NORMAL -> PlayerFragment()
|
||||
CARD -> CardFragment()
|
||||
BLUR_CARD -> CardBlurFragment()
|
||||
FIT -> FitFragment()
|
||||
FLAT -> FlatPlayerFragment()
|
||||
FULL -> FullPlayerFragment()
|
||||
PLAIN -> PlainPlayerFragment()
|
||||
SIMPLE -> SimplePlayerFragment()
|
||||
MATERIAL -> MaterialFragment()
|
||||
COLOR -> ColorFragment()
|
||||
TINY -> TinyPlayerFragment()
|
||||
//SLIDE -> SlidePlayerFragment()
|
||||
else -> PlayerFragment()
|
||||
} // must implement AbsPlayerFragment
|
||||
supportFragmentManager.beginTransaction().replace(R.id.playerFragmentContainer, fragment).commit()
|
||||
supportFragmentManager.executePendingTransactions()
|
||||
|
||||
playerFragment = supportFragmentManager.findFragmentById(R.id.playerFragmentContainer) as AbsPlayerFragment
|
||||
miniPlayerFragment = supportFragmentManager.findFragmentById(R.id.miniPlayerFragment) as MiniPlayerFragment
|
||||
miniPlayerFragment!!.view!!.setOnClickListener { expandPanel() }
|
||||
}
|
||||
|
||||
override fun onServiceConnected() {
|
||||
super.onServiceConnected()
|
||||
if (!MusicPlayerRemote.playingQueue.isEmpty()) {
|
||||
slidingLayout.viewTreeObserver.addOnGlobalLayoutListener(object : ViewTreeObserver.OnGlobalLayoutListener {
|
||||
override fun onGlobalLayout() {
|
||||
slidingLayout.viewTreeObserver.removeOnGlobalLayoutListener(this)
|
||||
hideBottomBar(false)
|
||||
}
|
||||
})
|
||||
} // don't call hideBottomBar(true) here as it causes a bug with the SlidingUpPanelLayout
|
||||
}
|
||||
|
||||
override fun onQueueChanged() {
|
||||
super.onQueueChanged()
|
||||
hideBottomBar(MusicPlayerRemote.playingQueue.isEmpty())
|
||||
}
|
||||
|
||||
override fun onBackPressed() {
|
||||
if (!handleBackPress())
|
||||
super.onBackPressed()
|
||||
}
|
||||
|
||||
open fun handleBackPress(): Boolean {
|
||||
if (slidingLayout.panelHeight != 0 && playerFragment!!.onBackPressed())
|
||||
return true
|
||||
if (panelState == SlidingUpPanelLayout.PanelState.EXPANDED) {
|
||||
collapsePanel()
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
override fun onPanelSlide(panel: View?, slideOffset: Float) {
|
||||
setMiniPlayerAlphaProgress(slideOffset)
|
||||
}
|
||||
|
||||
override fun onPanelStateChanged(panel: View, previousState: SlidingUpPanelLayout.PanelState, newState: SlidingUpPanelLayout.PanelState) {
|
||||
when (newState) {
|
||||
SlidingUpPanelLayout.PanelState.COLLAPSED -> onPanelCollapsed()
|
||||
SlidingUpPanelLayout.PanelState.EXPANDED -> onPanelExpanded()
|
||||
SlidingUpPanelLayout.PanelState.ANCHORED -> collapsePanel() // this fixes a bug where the panel would get stuck for some reason
|
||||
else -> {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onPaletteColorChanged() {
|
||||
if (panelState == SlidingUpPanelLayout.PanelState.EXPANDED) {
|
||||
val paletteColor = playerFragment!!.paletteColor
|
||||
super.setTaskDescriptionColor(paletteColor)
|
||||
|
||||
val isColorLight = ColorUtil.isColorLight(paletteColor)
|
||||
if (PreferenceUtil.getInstance().adaptiveColor &&
|
||||
(currentNowPlayingScreen == NORMAL || currentNowPlayingScreen == FLAT)) {
|
||||
super.setLightNavigationBar(true)
|
||||
super.setLightStatusbar(isColorLight)
|
||||
} else if (currentNowPlayingScreen == FULL || currentNowPlayingScreen == CARD ||
|
||||
currentNowPlayingScreen == FIT || /*currentNowPlayingScreen == NowPlayingScreen.CLASSIC ||*/
|
||||
currentNowPlayingScreen == BLUR || currentNowPlayingScreen == BLUR_CARD) {
|
||||
super.setLightStatusbar(false)
|
||||
super.setLightNavigationBar(true)
|
||||
} else if (currentNowPlayingScreen == COLOR) {
|
||||
super.setNavigationbarColor(paletteColor)
|
||||
super.setLightNavigationBar(isColorLight)
|
||||
super.setLightStatusbar(isColorLight)
|
||||
} else {
|
||||
super.setLightStatusbar(ColorUtil.isColorLight(ThemeStore.primaryColor(this)))
|
||||
super.setLightNavigationBar(true)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun setLightStatusbar(enabled: Boolean) {
|
||||
lightStatusBar = enabled
|
||||
if (panelState == SlidingUpPanelLayout.PanelState.COLLAPSED) {
|
||||
super.setLightStatusbar(enabled)
|
||||
}
|
||||
}
|
||||
|
||||
override fun setLightNavigationBar(enabled: Boolean) {
|
||||
lightNavigationBar = enabled
|
||||
if (panelState == SlidingUpPanelLayout.PanelState.COLLAPSED) {
|
||||
super.setLightNavigationBar(enabled)
|
||||
}
|
||||
}
|
||||
|
||||
override fun setNavigationbarColor(color: Int) {
|
||||
navigationBarColor = color
|
||||
if (panelState == SlidingUpPanelLayout.PanelState.COLLAPSED) {
|
||||
if (navigationBarColorAnimator != null) navigationBarColorAnimator!!.cancel()
|
||||
super.setNavigationbarColor(color)
|
||||
}
|
||||
}
|
||||
|
||||
override fun setTaskDescriptionColor(color: Int) {
|
||||
taskColor = color
|
||||
if (panelState == SlidingUpPanelLayout.PanelState.COLLAPSED) {
|
||||
super.setTaskDescriptionColor(color)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,205 @@
|
|||
package code.name.monkey.retromusic.activities.base
|
||||
|
||||
import android.graphics.Color
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.os.Bundle
|
||||
import android.os.Handler
|
||||
import android.view.KeyEvent
|
||||
import android.view.View
|
||||
import android.view.WindowManager
|
||||
import androidx.annotation.ColorInt
|
||||
import androidx.core.content.ContextCompat
|
||||
import code.name.monkey.appthemehelper.ATH
|
||||
import code.name.monkey.appthemehelper.ThemeStore
|
||||
import code.name.monkey.appthemehelper.util.*
|
||||
import code.name.monkey.retromusic.R
|
||||
import code.name.monkey.retromusic.util.PreferenceUtil
|
||||
import code.name.monkey.retromusic.util.RetroUtil
|
||||
|
||||
abstract class AbsThemeActivity : AbsCrashCollector(), Runnable {
|
||||
|
||||
private val handler = Handler()
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
setTheme(PreferenceUtil.getInstance().generalTheme)
|
||||
hideStatusBar()
|
||||
super.onCreate(savedInstanceState)
|
||||
//MaterialDialogsUtil.updateMaterialDialogsThemeSingleton(this)
|
||||
|
||||
changeBackgroundShape()
|
||||
setImmersiveFullscreen()
|
||||
registerSystemUiVisibility()
|
||||
toggleScreenOn()
|
||||
}
|
||||
|
||||
private fun toggleScreenOn() {
|
||||
if (PreferenceUtil.getInstance().isScreenOnEnabled) {
|
||||
window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
|
||||
} else {
|
||||
window.clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onWindowFocusChanged(hasFocus: Boolean) {
|
||||
super.onWindowFocusChanged(hasFocus)
|
||||
if (hasFocus) {
|
||||
hideStatusBar()
|
||||
handler.removeCallbacks(this)
|
||||
handler.postDelayed(this, 300)
|
||||
} else {
|
||||
handler.removeCallbacks(this)
|
||||
}
|
||||
}
|
||||
|
||||
fun hideStatusBar() {
|
||||
hideStatusBar(PreferenceUtil.getInstance().fullScreenMode)
|
||||
}
|
||||
|
||||
private fun hideStatusBar(fullscreen: Boolean) {
|
||||
val statusBar = window.decorView.rootView.findViewById<View>(R.id.status_bar)
|
||||
if (statusBar != null) {
|
||||
statusBar.visibility = if (fullscreen) View.GONE else View.VISIBLE
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private fun changeBackgroundShape() {
|
||||
var background: Drawable? = if (PreferenceUtil.getInstance().isRoundCorners)
|
||||
ContextCompat.getDrawable(this, R.drawable.round_window)
|
||||
else
|
||||
ContextCompat.getDrawable(this, R.drawable.square_window)
|
||||
background = TintHelper.createTintedDrawable(background, ThemeStore.primaryColor(this))
|
||||
window.setBackgroundDrawable(background)
|
||||
}
|
||||
|
||||
fun setDrawUnderStatusBar() {
|
||||
if (VersionUtils.hasLollipop()) {
|
||||
RetroUtil.setAllowDrawUnderStatusBar(window)
|
||||
} else if (VersionUtils.hasKitKat()) {
|
||||
RetroUtil.setStatusBarTranslucent(window)
|
||||
}
|
||||
}
|
||||
|
||||
fun setDrawUnderNavigationBar() {
|
||||
RetroUtil.setAllowDrawUnderNavigationBar(window)
|
||||
}
|
||||
|
||||
/**
|
||||
* This will set the color of the view with the id "status_bar" on KitKat and Lollipop. On
|
||||
* Lollipop if no such view is found it will set the statusbar color using the native method.
|
||||
*
|
||||
* @param color the new statusbar color (will be shifted down on Lollipop and above)
|
||||
*/
|
||||
fun setStatusbarColor(color: Int) {
|
||||
if (VersionUtils.hasKitKat()) {
|
||||
val statusBar = window.decorView.rootView.findViewById<View>(R.id.status_bar)
|
||||
if (statusBar != null) {
|
||||
when {
|
||||
VersionUtils.hasMarshmallow() -> window.statusBarColor = color
|
||||
VersionUtils.hasLollipop() -> statusBar.setBackgroundColor(ColorUtil.darkenColor(color))
|
||||
else -> statusBar.setBackgroundColor(color)
|
||||
}
|
||||
} else {
|
||||
when {
|
||||
VersionUtils.hasMarshmallow() -> window.statusBarColor = color
|
||||
else -> window.statusBarColor = ColorUtil.darkenColor(color)
|
||||
}
|
||||
}
|
||||
}
|
||||
setLightStatusbarAuto(color)
|
||||
}
|
||||
|
||||
fun setStatusbarColorAuto() {
|
||||
// we don't want to use statusbar color because we are doing the color darkening on our own to support KitKat
|
||||
setStatusbarColor(ThemeStore.primaryColor(this))
|
||||
}
|
||||
|
||||
open fun setTaskDescriptionColor(@ColorInt color: Int) {
|
||||
ATH.setTaskDescriptionColor(this, color)
|
||||
}
|
||||
|
||||
fun setTaskDescriptionColorAuto() {
|
||||
setTaskDescriptionColor(ThemeStore.primaryColor(this))
|
||||
}
|
||||
|
||||
open fun setNavigationbarColor(color: Int) {
|
||||
if (ThemeStore.coloredNavigationBar(this)) {
|
||||
ATH.setNavigationbarColor(this, color)
|
||||
} else {
|
||||
ATH.setNavigationbarColor(this, Color.BLACK)
|
||||
}
|
||||
}
|
||||
|
||||
fun setNavigationbarColorAuto() {
|
||||
setNavigationbarColor(ThemeStore.navigationBarColor(this))
|
||||
}
|
||||
|
||||
open fun setLightStatusbar(enabled: Boolean) {
|
||||
ATH.setLightStatusbar(this, enabled)
|
||||
}
|
||||
|
||||
fun setLightStatusbarAuto(bgColor: Int) {
|
||||
setLightStatusbar(ColorUtil.isColorLight(bgColor))
|
||||
}
|
||||
|
||||
open fun setLightNavigationBar(enabled: Boolean) {
|
||||
if (!ATHUtil.isWindowBackgroundDark(this) and ThemeStore.coloredNavigationBar(this)) {
|
||||
ATH.setLightNavigationbar(this, enabled)
|
||||
}
|
||||
}
|
||||
|
||||
private fun registerSystemUiVisibility() {
|
||||
val decorView = window.decorView
|
||||
decorView.setOnSystemUiVisibilityChangeListener { visibility ->
|
||||
if (visibility and View.SYSTEM_UI_FLAG_FULLSCREEN == 0) {
|
||||
setImmersiveFullscreen()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun unregisterSystemUiVisibility() {
|
||||
val decorView = window.decorView
|
||||
decorView.setOnSystemUiVisibilityChangeListener(null)
|
||||
}
|
||||
|
||||
private fun setImmersiveFullscreen() {
|
||||
val flags = (View.SYSTEM_UI_FLAG_LAYOUT_STABLE
|
||||
or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
|
||||
or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
|
||||
or View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
|
||||
or View.SYSTEM_UI_FLAG_FULLSCREEN
|
||||
or View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY)
|
||||
if (PreferenceUtil.getInstance().fullScreenMode) {
|
||||
window.decorView.systemUiVisibility = flags
|
||||
}
|
||||
}
|
||||
|
||||
private fun exitFullscreen() {
|
||||
window.decorView.systemUiVisibility = View.SYSTEM_UI_FLAG_VISIBLE
|
||||
}
|
||||
|
||||
override fun run() {
|
||||
setImmersiveFullscreen()
|
||||
}
|
||||
|
||||
override fun onStop() {
|
||||
handler.removeCallbacks(this)
|
||||
super.onStop()
|
||||
}
|
||||
|
||||
public override fun onDestroy() {
|
||||
super.onDestroy()
|
||||
unregisterSystemUiVisibility()
|
||||
exitFullscreen()
|
||||
}
|
||||
|
||||
|
||||
override fun onKeyDown(keyCode: Int, event: KeyEvent): Boolean {
|
||||
if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN || keyCode == KeyEvent.KEYCODE_VOLUME_UP) {
|
||||
handler.removeCallbacks(this)
|
||||
handler.postDelayed(this, 500)
|
||||
}
|
||||
return super.onKeyDown(keyCode, event)
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,315 @@
|
|||
package code.name.monkey.retromusic.activities.bugreport
|
||||
|
||||
import android.app.Activity
|
||||
import android.app.Dialog
|
||||
import android.content.ClipData
|
||||
import android.content.ClipboardManager
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
import android.text.TextUtils
|
||||
import android.view.MenuItem
|
||||
import android.view.inputmethod.EditorInfo
|
||||
import android.widget.Toast
|
||||
import androidx.annotation.StringDef
|
||||
import androidx.annotation.StringRes
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import code.name.monkey.appthemehelper.ThemeStore
|
||||
import code.name.monkey.appthemehelper.util.TintHelper
|
||||
import code.name.monkey.retromusic.R
|
||||
import code.name.monkey.retromusic.misc.DialogAsyncTask
|
||||
import code.name.monkey.retromusic.activities.base.AbsThemeActivity
|
||||
import code.name.monkey.retromusic.activities.bugreport.model.DeviceInfo
|
||||
import code.name.monkey.retromusic.activities.bugreport.model.Report
|
||||
import code.name.monkey.retromusic.activities.bugreport.model.github.ExtraInfo
|
||||
import code.name.monkey.retromusic.activities.bugreport.model.github.GithubLogin
|
||||
import code.name.monkey.retromusic.activities.bugreport.model.github.GithubTarget
|
||||
import com.afollestad.materialdialogs.MaterialDialog
|
||||
import com.afollestad.materialdialogs.callbacks.onCancel
|
||||
import com.google.android.material.floatingactionbutton.FloatingActionButton
|
||||
import com.google.android.material.textfield.TextInputLayout
|
||||
import kotlinx.android.synthetic.main.activity_bug_report.*
|
||||
import kotlinx.android.synthetic.main.bug_report_card_device_info.*
|
||||
import kotlinx.android.synthetic.main.bug_report_card_report.*
|
||||
import org.eclipse.egit.github.core.Issue
|
||||
import org.eclipse.egit.github.core.client.GitHubClient
|
||||
import org.eclipse.egit.github.core.client.RequestException
|
||||
import org.eclipse.egit.github.core.service.IssueService
|
||||
import java.io.IOException
|
||||
|
||||
private const val RESULT_SUCCESS = "RESULT_OK"
|
||||
private const val RESULT_BAD_CREDENTIALS = "RESULT_BAD_CREDENTIALS"
|
||||
private const val RESULT_INVALID_TOKEN = "RESULT_INVALID_TOKEN"
|
||||
private const val RESULT_ISSUES_NOT_ENABLED = "RESULT_ISSUES_NOT_ENABLED"
|
||||
private const val RESULT_UNKNOWN = "RESULT_UNKNOWN"
|
||||
|
||||
@StringDef(RESULT_SUCCESS, RESULT_BAD_CREDENTIALS, RESULT_INVALID_TOKEN, RESULT_ISSUES_NOT_ENABLED, RESULT_UNKNOWN)
|
||||
@Retention(AnnotationRetention.SOURCE)
|
||||
private annotation class Result
|
||||
|
||||
open class BugReportActivity : AbsThemeActivity() {
|
||||
|
||||
private var deviceInfo: DeviceInfo? = null
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(R.layout.activity_bug_report)
|
||||
|
||||
|
||||
setStatusbarColorAuto()
|
||||
setNavigationbarColorAuto()
|
||||
setTaskDescriptionColorAuto()
|
||||
|
||||
initViews()
|
||||
|
||||
if (TextUtils.isEmpty(title))
|
||||
setTitle(R.string.report_an_issue)
|
||||
|
||||
|
||||
deviceInfo = DeviceInfo(this)
|
||||
airTextDeviceInfo!!.text = deviceInfo!!.toString()
|
||||
}
|
||||
|
||||
private fun initViews() {
|
||||
val accentColor = ThemeStore.accentColor(this)
|
||||
val primaryColor = ThemeStore.primaryColor(this)
|
||||
toolbar!!.setBackgroundColor(primaryColor)
|
||||
setSupportActionBar(toolbar)
|
||||
|
||||
supportActionBar!!.setDisplayHomeAsUpEnabled(true)
|
||||
|
||||
TintHelper.setTintAuto(optionUseAccount!!, accentColor, false)
|
||||
optionUseAccount!!.setOnClickListener {
|
||||
inputTitle!!.isEnabled = true
|
||||
inputDescription!!.isEnabled = true
|
||||
inputUsername!!.isEnabled = true
|
||||
inputPassword!!.isEnabled = true
|
||||
|
||||
optionAnonymous!!.isChecked = false
|
||||
sendFab!!.hide(object : FloatingActionButton.OnVisibilityChangedListener() {
|
||||
override fun onHidden(fab: FloatingActionButton?) {
|
||||
super.onHidden(fab)
|
||||
sendFab!!.setImageResource(R.drawable.ic_send_white_24dp)
|
||||
sendFab!!.show()
|
||||
}
|
||||
})
|
||||
}
|
||||
TintHelper.setTintAuto(optionAnonymous!!, accentColor, false)
|
||||
optionAnonymous!!.setOnClickListener {
|
||||
inputTitle!!.isEnabled = false
|
||||
inputDescription!!.isEnabled = false
|
||||
inputUsername!!.isEnabled = false
|
||||
inputPassword!!.isEnabled = false
|
||||
|
||||
optionUseAccount!!.isChecked = false
|
||||
sendFab!!.hide(object : FloatingActionButton.OnVisibilityChangedListener() {
|
||||
override fun onHidden(fab: FloatingActionButton?) {
|
||||
super.onHidden(fab)
|
||||
sendFab!!.setImageResource(R.drawable.ic_open_in_browser_white_24dp)
|
||||
sendFab!!.show()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
inputPassword!!.setOnEditorActionListener { _, actionId, _ ->
|
||||
if (actionId == EditorInfo.IME_ACTION_SEND) {
|
||||
reportIssue()
|
||||
return@setOnEditorActionListener true
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
airTextDeviceInfo!!.setOnClickListener { copyDeviceInfoToClipBoard() }
|
||||
|
||||
TintHelper.setTintAuto(sendFab!!, accentColor, true)
|
||||
sendFab!!.setOnClickListener { reportIssue() }
|
||||
|
||||
TintHelper.setTintAuto(inputTitle!!, accentColor, false)
|
||||
TintHelper.setTintAuto(inputDescription!!, accentColor, false)
|
||||
TintHelper.setTintAuto(inputUsername!!, accentColor, false)
|
||||
TintHelper.setTintAuto(inputPassword!!, accentColor, false)
|
||||
}
|
||||
|
||||
private fun reportIssue() {
|
||||
if (optionUseAccount!!.isChecked) {
|
||||
if (!validateInput()) return
|
||||
val username = inputUsername!!.text!!.toString()
|
||||
val password = inputPassword!!.text!!.toString()
|
||||
sendBugReport(GithubLogin(username, password))
|
||||
} else {
|
||||
copyDeviceInfoToClipBoard()
|
||||
|
||||
val i = Intent(Intent.ACTION_VIEW)
|
||||
i.data = Uri.parse(ISSUE_TRACKER_LINK)
|
||||
i.flags = Intent.FLAG_ACTIVITY_NEW_TASK
|
||||
startActivity(i)
|
||||
}
|
||||
}
|
||||
|
||||
private fun copyDeviceInfoToClipBoard() {
|
||||
val clipboard = getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
|
||||
val clip = ClipData.newPlainText(getString(R.string.device_info), deviceInfo!!.toMarkdown())
|
||||
clipboard.primaryClip = clip
|
||||
|
||||
Toast.makeText(this@BugReportActivity, R.string.copied_device_info_to_clipboard, Toast.LENGTH_LONG).show()
|
||||
}
|
||||
|
||||
private fun validateInput(): Boolean {
|
||||
var hasErrors = false
|
||||
|
||||
if (optionUseAccount!!.isChecked) {
|
||||
if (TextUtils.isEmpty(inputUsername!!.text)) {
|
||||
setError(inputLayoutUsername!!, R.string.bug_report_no_username)
|
||||
hasErrors = true
|
||||
} else {
|
||||
removeError(inputLayoutUsername!!)
|
||||
}
|
||||
|
||||
if (TextUtils.isEmpty(inputPassword!!.text)) {
|
||||
setError(inputLayoutPassword!!, R.string.bug_report_no_password)
|
||||
hasErrors = true
|
||||
} else {
|
||||
removeError(inputLayoutPassword!!)
|
||||
}
|
||||
}
|
||||
|
||||
if (TextUtils.isEmpty(inputTitle!!.text)) {
|
||||
setError(inputLayoutTitle!!, R.string.bug_report_no_title)
|
||||
hasErrors = true
|
||||
} else {
|
||||
removeError(inputLayoutTitle!!)
|
||||
}
|
||||
|
||||
if (TextUtils.isEmpty(inputDescription!!.text)) {
|
||||
setError(inputLayoutDescription!!, R.string.bug_report_no_description)
|
||||
hasErrors = true
|
||||
} else {
|
||||
removeError(inputLayoutDescription!!)
|
||||
}
|
||||
|
||||
return !hasErrors
|
||||
}
|
||||
|
||||
private fun setError(editTextLayout: TextInputLayout, @StringRes errorRes: Int) {
|
||||
editTextLayout.error = getString(errorRes)
|
||||
}
|
||||
|
||||
private fun removeError(editTextLayout: TextInputLayout) {
|
||||
editTextLayout.error = null
|
||||
}
|
||||
|
||||
private fun sendBugReport(login: GithubLogin) {
|
||||
if (!validateInput()) return
|
||||
|
||||
val bugTitle = inputTitle!!.text!!.toString()
|
||||
val bugDescription = inputDescription!!.text!!.toString()
|
||||
|
||||
val extraInfo = ExtraInfo()
|
||||
onSaveExtraInfo()
|
||||
|
||||
val report = Report(bugTitle, bugDescription, deviceInfo, extraInfo)
|
||||
val target = GithubTarget("h4h13", "RetroMusicPlayer")
|
||||
|
||||
ReportIssueAsyncTask.report(this, report, target, login)
|
||||
}
|
||||
|
||||
private fun onSaveExtraInfo() {}
|
||||
|
||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||
if (item.itemId == android.R.id.home) {
|
||||
onBackPressed()
|
||||
}
|
||||
return super.onOptionsItemSelected(item)
|
||||
}
|
||||
|
||||
private class ReportIssueAsyncTask private constructor(activity: Activity, private val report: Report, private val target: GithubTarget,
|
||||
private val login: GithubLogin) : DialogAsyncTask<Void, Void, String>(activity) {
|
||||
override fun createDialog(context: Context): Dialog {
|
||||
return AlertDialog.Builder(context)
|
||||
.show()
|
||||
}
|
||||
|
||||
@Result
|
||||
override fun doInBackground(vararg params: Void): String {
|
||||
val client: GitHubClient = if (login.shouldUseApiToken()) {
|
||||
GitHubClient().setOAuth2Token(login.apiToken)
|
||||
} else {
|
||||
GitHubClient().setCredentials(login.username, login.password)
|
||||
}
|
||||
|
||||
val issue = Issue().setTitle(report.title).setBody(report.description)
|
||||
try {
|
||||
IssueService(client).createIssue(target.username, target.repository, issue)
|
||||
return RESULT_SUCCESS
|
||||
} catch (e: RequestException) {
|
||||
return when (e.status) {
|
||||
STATUS_BAD_CREDENTIALS -> {
|
||||
if (login.shouldUseApiToken()) RESULT_INVALID_TOKEN else RESULT_BAD_CREDENTIALS
|
||||
}
|
||||
STATUS_ISSUES_NOT_ENABLED -> RESULT_ISSUES_NOT_ENABLED
|
||||
else -> {
|
||||
e.printStackTrace()
|
||||
RESULT_UNKNOWN
|
||||
}
|
||||
}
|
||||
} catch (e: IOException) {
|
||||
e.printStackTrace()
|
||||
return RESULT_UNKNOWN
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
override fun onPostExecute(@Result result: String) {
|
||||
super.onPostExecute(result)
|
||||
|
||||
val context = context ?: return
|
||||
|
||||
when (result) {
|
||||
RESULT_SUCCESS -> tryToFinishActivity()
|
||||
RESULT_BAD_CREDENTIALS -> MaterialDialog(context).show {
|
||||
title(R.string.bug_report_failed)
|
||||
message(R.string.bug_report_failed_wrong_credentials)
|
||||
positiveButton(android.R.string.ok)
|
||||
}
|
||||
RESULT_INVALID_TOKEN -> MaterialDialog(context).show {
|
||||
title(R.string.bug_report_failed)
|
||||
message(R.string.bug_report_failed_invalid_token)
|
||||
positiveButton(android.R.string.ok)
|
||||
}
|
||||
RESULT_ISSUES_NOT_ENABLED -> MaterialDialog(context).show {
|
||||
title(R.string.bug_report_failed)
|
||||
message(R.string.bug_report_failed_issues_not_available)
|
||||
positiveButton(android.R.string.ok)
|
||||
}
|
||||
else -> MaterialDialog(context).show {
|
||||
title(R.string.bug_report_failed)
|
||||
message(R.string.bug_report_failed_unknown)
|
||||
positiveButton(android.R.string.ok) { tryToFinishActivity() }
|
||||
onCancel { tryToFinishActivity() }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun tryToFinishActivity() {
|
||||
val context = context
|
||||
if (context is Activity && !context.isFinishing) {
|
||||
context.finish()
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
fun report(activity: Activity, report: Report, target: GithubTarget, login: GithubLogin) {
|
||||
ReportIssueAsyncTask(activity, report, target, login).execute()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
private const val STATUS_BAD_CREDENTIALS = 401
|
||||
private const val STATUS_ISSUES_NOT_ENABLED = 410
|
||||
private const val ISSUE_TRACKER_LINK = "https://github.com/h4h13/RetroMusicPlayer"
|
||||
}
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
package code.name.monkey.retromusic.activities.bugreport
import android.content.ActivityNotFoundException
import android.content.Intent
import android.net.Uri
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import code.name.monkey.retromusic.R
import code.name.monkey.retromusic.activities.bugreport.model.DeviceInfo
import kotlinx.android.synthetic.main.activity_error_handler.*
class ErrorHandlerActivity : AppCompatActivity() {
private var deviceInfo: DeviceInfo? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_error_handler)
deviceInfo = DeviceInfo(this)
clearAppData.setOnClickListener {
try {
val intent = Intent(android.provider.Settings.ACTION_APPLICATION_DETAILS_SETTINGS)
intent.data = Uri.parse("package:$packageName")
startActivity(intent)
} catch (e: ActivityNotFoundException) {
val intent = Intent(android.provider.Settings.ACTION_MANAGE_APPLICATIONS_SETTINGS)
startActivity(intent)
}
}
sendCrashLog.setOnClickListener {
val sendIntent = Intent(Intent.ACTION_SEND)
val subject = "Error report"
val body = intent.getStringExtra("error") + "\n" + deviceInfo!!.toString()
sendIntent.putExtra(Intent.EXTRA_EMAIL, arrayOf("monkeycodeapp@gmail.com"))
sendIntent.putExtra(Intent.EXTRA_TEXT, body)
sendIntent.putExtra(Intent.EXTRA_SUBJECT, subject)
sendIntent.type = "message/rfc822"
startActivity(Intent.createChooser(sendIntent, "Send crash log"))
deleteFile("stack.trace")
}
showCrashError.text = String.format("%s", intent.getStringExtra("error"))
showCrashError.append(deviceInfo!!.toString())
}
}
|
|
@ -0,0 +1,105 @@
|
|||
package code.name.monkey.retromusic.activities.bugreport.model;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.Context;
|
||||
import android.content.pm.PackageInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.os.Build;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
import androidx.annotation.IntRange;
|
||||
import code.name.monkey.retromusic.util.PreferenceUtil;
|
||||
|
||||
public class DeviceInfo {
|
||||
private final int versionCode;
|
||||
private final String versionName;
|
||||
private final String buildVersion = Build.VERSION.INCREMENTAL;
|
||||
private final String releaseVersion = Build.VERSION.RELEASE;
|
||||
@IntRange(from = 0)
|
||||
private final int sdkVersion = Build.VERSION.SDK_INT;
|
||||
private final String buildID = Build.DISPLAY;
|
||||
private final String brand = Build.BRAND;
|
||||
private final String manufacturer = Build.MANUFACTURER;
|
||||
private final String device = Build.DEVICE;
|
||||
private final String model = Build.MODEL;
|
||||
private final String product = Build.PRODUCT;
|
||||
private final String hardware = Build.HARDWARE;
|
||||
private final String baseTheme;
|
||||
private final String nowPlayingTheme;
|
||||
private final boolean isAdaptive;
|
||||
@SuppressLint("NewApi")
|
||||
@SuppressWarnings("deprecation")
|
||||
private final String[] abis = Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP ?
|
||||
Build.SUPPORTED_ABIS : new String[]{Build.CPU_ABI, Build.CPU_ABI2};
|
||||
@SuppressLint("NewApi")
|
||||
private final String[] abis32Bits = Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP ?
|
||||
Build.SUPPORTED_32_BIT_ABIS : null;
|
||||
@SuppressLint("NewApi")
|
||||
private final String[] abis64Bits = Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP ?
|
||||
Build.SUPPORTED_64_BIT_ABIS : null;
|
||||
|
||||
public DeviceInfo(Context context) {
|
||||
PackageInfo packageInfo;
|
||||
try {
|
||||
packageInfo = context.getPackageManager()
|
||||
.getPackageInfo(context.getPackageName(), 0);
|
||||
} catch (PackageManager.NameNotFoundException e) {
|
||||
packageInfo = null;
|
||||
}
|
||||
if (packageInfo != null) {
|
||||
versionCode = packageInfo.versionCode;
|
||||
versionName = packageInfo.versionName;
|
||||
} else {
|
||||
versionCode = -1;
|
||||
versionName = null;
|
||||
}
|
||||
baseTheme = PreferenceUtil.getInstance().getBaseTheme();
|
||||
nowPlayingTheme = context.getString(PreferenceUtil.getInstance().getNowPlayingScreen().getTitleRes());
|
||||
isAdaptive = PreferenceUtil.getInstance().getAdaptiveColor();
|
||||
}
|
||||
|
||||
public String toMarkdown() {
|
||||
return "Device info:\n"
|
||||
+ "---\n"
|
||||
+ "<table>\n"
|
||||
+ "<tr><td>App version</td><td>" + versionName + "</td></tr>\n"
|
||||
+ "<tr><td>App version code</td><td>" + versionCode + "</td></tr>\n"
|
||||
+ "<tr><td>Android build version</td><td>" + buildVersion + "</td></tr>\n"
|
||||
+ "<tr><td>Android release version</td><td>" + releaseVersion + "</td></tr>\n"
|
||||
+ "<tr><td>Android SDK version</td><td>" + sdkVersion + "</td></tr>\n"
|
||||
+ "<tr><td>Android build ID</td><td>" + buildID + "</td></tr>\n"
|
||||
+ "<tr><td>Device brand</td><td>" + brand + "</td></tr>\n"
|
||||
+ "<tr><td>Device manufacturer</td><td>" + manufacturer + "</td></tr>\n"
|
||||
+ "<tr><td>Device name</td><td>" + device + "</td></tr>\n"
|
||||
+ "<tr><td>Device model</td><td>" + model + "</td></tr>\n"
|
||||
+ "<tr><td>Device product name</td><td>" + product + "</td></tr>\n"
|
||||
+ "<tr><td>Device hardware name</td><td>" + hardware + "</td></tr>\n"
|
||||
+ "<tr><td>ABIs</td><td>" + Arrays.toString(abis) + "</td></tr>\n"
|
||||
+ "<tr><td>ABIs (32bit)</td><td>" + Arrays.toString(abis32Bits) + "</td></tr>\n"
|
||||
+ "<tr><td>ABIs (64bit)</td><td>" + Arrays.toString(abis64Bits) + "</td></tr>\n"
|
||||
+ "</table>\n";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "App version: " + versionName + "\n"
|
||||
+ "App version code: " + versionCode + "\n"
|
||||
+ "Android build version: " + buildVersion + "\n"
|
||||
+ "Android release version: " + releaseVersion + "\n"
|
||||
+ "Android SDK version: " + sdkVersion + "\n"
|
||||
+ "Android build ID: " + buildID + "\n"
|
||||
+ "Device brand: " + brand + "\n"
|
||||
+ "Device manufacturer: " + manufacturer + "\n"
|
||||
+ "Device name: " + device + "\n"
|
||||
+ "Device model: " + model + "\n"
|
||||
+ "Device product name: " + product + "\n"
|
||||
+ "Device hardware name: " + hardware + "\n"
|
||||
+ "ABIs: " + Arrays.toString(abis) + "\n"
|
||||
+ "ABIs (32bit): " + Arrays.toString(abis32Bits) + "\n"
|
||||
+ "ABIs (64bit): " + Arrays.toString(abis64Bits) + "\n"
|
||||
+ "Base theme: " + baseTheme + "\n"
|
||||
+ "Now playing theme: " + nowPlayingTheme + "\n"
|
||||
+ "Adaptive: " + isAdaptive;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
package code.name.monkey.retromusic.activities.bugreport.model;
|
||||
|
||||
|
||||
import code.name.monkey.retromusic.activities.bugreport.model.github.ExtraInfo;
|
||||
|
||||
public class Report {
|
||||
private final String title;
|
||||
|
||||
private final String description;
|
||||
|
||||
private final DeviceInfo deviceInfo;
|
||||
|
||||
private final ExtraInfo extraInfo;
|
||||
|
||||
public Report(String title, String description, DeviceInfo deviceInfo, ExtraInfo extraInfo) {
|
||||
this.title = title;
|
||||
this.description = description;
|
||||
this.deviceInfo = deviceInfo;
|
||||
this.extraInfo = extraInfo;
|
||||
}
|
||||
|
||||
public String getTitle() {
|
||||
return title;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return description + "\n\n"
|
||||
+ "-\n\n"
|
||||
+ deviceInfo.toMarkdown() + "\n\n"
|
||||
+ extraInfo.toMarkdown();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,59 @@
|
|||
package code.name.monkey.retromusic.activities.bugreport.model.github;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class ExtraInfo {
|
||||
private final Map<String, String> extraInfo = new LinkedHashMap<>();
|
||||
|
||||
public void put(String key, String value) {
|
||||
extraInfo.put(key, value);
|
||||
}
|
||||
|
||||
public void put(String key, boolean value) {
|
||||
extraInfo.put(key, Boolean.toString(value));
|
||||
}
|
||||
|
||||
public void put(String key, double value) {
|
||||
extraInfo.put(key, Double.toString(value));
|
||||
}
|
||||
|
||||
public void put(String key, float value) {
|
||||
extraInfo.put(key, Float.toString(value));
|
||||
}
|
||||
|
||||
public void put(String key, long value) {
|
||||
extraInfo.put(key, Long.toString(value));
|
||||
}
|
||||
|
||||
public void put(String key, int value) {
|
||||
extraInfo.put(key, Integer.toString(value));
|
||||
}
|
||||
|
||||
public void put(String key, Object value) {
|
||||
extraInfo.put(key, String.valueOf(value));
|
||||
}
|
||||
|
||||
public void remove(String key) {
|
||||
extraInfo.remove(key);
|
||||
}
|
||||
|
||||
public String toMarkdown() {
|
||||
if (extraInfo.isEmpty()) return "";
|
||||
|
||||
StringBuilder output = new StringBuilder();
|
||||
output.append("Extra info:\n"
|
||||
+ "---\n"
|
||||
+ "<table>\n");
|
||||
for (String key : extraInfo.keySet()) {
|
||||
output.append("<tr><td>")
|
||||
.append(key)
|
||||
.append("</td><td>")
|
||||
.append(extraInfo.get(key))
|
||||
.append("</td></tr>\n");
|
||||
}
|
||||
output.append("</table>\n");
|
||||
|
||||
return output.toString();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,40 @@
|
|||
package code.name.monkey.retromusic.activities.bugreport.model.github;
|
||||
|
||||
import android.text.TextUtils;
|
||||
|
||||
public class GithubLogin {
|
||||
private final String username;
|
||||
|
||||
private final String password;
|
||||
|
||||
private final String apiToken;
|
||||
|
||||
public GithubLogin(String username, String password) {
|
||||
this.username = username;
|
||||
this.password = password;
|
||||
this.apiToken = null;
|
||||
}
|
||||
|
||||
public GithubLogin(String apiToken) {
|
||||
this.username = null;
|
||||
this.password = null;
|
||||
this.apiToken = apiToken;
|
||||
}
|
||||
|
||||
public String getUsername() {
|
||||
return username;
|
||||
}
|
||||
|
||||
public String getPassword() {
|
||||
return password;
|
||||
}
|
||||
|
||||
public boolean shouldUseApiToken() {
|
||||
return TextUtils.isEmpty(username) || TextUtils.isEmpty(password);
|
||||
}
|
||||
|
||||
public String getApiToken() {
|
||||
return apiToken;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
package code.name.monkey.retromusic.activities.bugreport.model.github;
|
||||
|
||||
public class GithubTarget {
|
||||
private final String username;
|
||||
|
||||
private final String repository;
|
||||
|
||||
public GithubTarget(String username, String repository) {
|
||||
this.username = username;
|
||||
this.repository = repository;
|
||||
}
|
||||
|
||||
public String getUsername() {
|
||||
return username;
|
||||
}
|
||||
|
||||
public String getRepository() {
|
||||
return repository;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,350 @@
|
|||
package code.name.monkey.retromusic.activities.tageditor
|
||||
|
||||
import android.app.Activity
|
||||
import android.app.SearchManager
|
||||
import android.content.Intent
|
||||
import android.content.res.ColorStateList
|
||||
import android.graphics.Bitmap
|
||||
import android.graphics.BitmapFactory
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
import android.util.Log
|
||||
import android.view.MenuItem
|
||||
import android.view.View
|
||||
import android.view.animation.OvershootInterpolator
|
||||
import code.name.monkey.appthemehelper.ThemeStore
|
||||
import code.name.monkey.appthemehelper.util.ColorUtil
|
||||
import code.name.monkey.appthemehelper.util.MaterialValueHelper
|
||||
import code.name.monkey.appthemehelper.util.TintHelper
|
||||
import code.name.monkey.retromusic.R
|
||||
import code.name.monkey.retromusic.activities.base.AbsBaseActivity
|
||||
import code.name.monkey.retromusic.util.RetroUtil
|
||||
import com.afollestad.materialdialogs.MaterialDialog
|
||||
import com.afollestad.materialdialogs.list.listItems
|
||||
import com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton
|
||||
import kotlinx.android.synthetic.main.activity_album_tag_editor.*
|
||||
import org.jaudiotagger.audio.AudioFile
|
||||
import org.jaudiotagger.audio.AudioFileIO
|
||||
import org.jaudiotagger.tag.FieldKey
|
||||
import java.io.File
|
||||
|
||||
abstract class AbsTagEditorActivity : AbsBaseActivity() {
|
||||
|
||||
|
||||
protected var id: Int = 0
|
||||
private set
|
||||
private var paletteColorPrimary: Int = 0
|
||||
private var isInNoImageMode: Boolean = false
|
||||
private var songPaths: List<String>? = null
|
||||
lateinit var saveFab: ExtendedFloatingActionButton
|
||||
|
||||
protected val show: MaterialDialog
|
||||
get() = MaterialDialog(this@AbsTagEditorActivity).show {
|
||||
title(R.string.update_image)
|
||||
listItems(items = items) { _, position, _ ->
|
||||
when (position) {
|
||||
0 -> getImageFromLastFM()
|
||||
1 -> startImagePicker()
|
||||
2 -> searchImageOnWeb()
|
||||
3 -> deleteImage()
|
||||
}
|
||||
}
|
||||
}
|
||||
protected abstract val contentViewLayout: Int
|
||||
|
||||
internal val albumArtist: String?
|
||||
get() {
|
||||
return try {
|
||||
getAudioFile(songPaths!![0]).tagOrCreateAndSetDefault
|
||||
.getFirst(FieldKey.ALBUM_ARTIST)
|
||||
} catch (ignored: Exception) {
|
||||
null
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
protected val songTitle: String?
|
||||
get() {
|
||||
return try {
|
||||
getAudioFile(songPaths!![0]).tagOrCreateAndSetDefault.getFirst(FieldKey.TITLE)
|
||||
} catch (ignored: Exception) {
|
||||
null
|
||||
}
|
||||
|
||||
}
|
||||
protected val composer: String?
|
||||
get() {
|
||||
return try {
|
||||
getAudioFile(songPaths!![0]).tagOrCreateAndSetDefault.getFirst(FieldKey.COMPOSER)
|
||||
} catch (ignored: Exception) {
|
||||
null
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
protected val albumTitle: String?
|
||||
get() {
|
||||
return try {
|
||||
getAudioFile(songPaths!![0]).tagOrCreateAndSetDefault.getFirst(FieldKey.ALBUM)
|
||||
} catch (ignored: Exception) {
|
||||
null
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
protected val artistName: String?
|
||||
get() {
|
||||
return try {
|
||||
getAudioFile(songPaths!![0]).tagOrCreateAndSetDefault.getFirst(FieldKey.ARTIST)
|
||||
} catch (ignored: Exception) {
|
||||
null
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
protected val albumArtistName: String?
|
||||
get() {
|
||||
return try {
|
||||
getAudioFile(songPaths!![0]).tagOrCreateAndSetDefault
|
||||
.getFirst(FieldKey.ALBUM_ARTIST)
|
||||
} catch (ignored: Exception) {
|
||||
null
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
protected val genreName: String?
|
||||
get() {
|
||||
return try {
|
||||
getAudioFile(songPaths!![0]).tagOrCreateAndSetDefault.getFirst(FieldKey.GENRE)
|
||||
} catch (ignored: Exception) {
|
||||
null
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
protected val songYear: String?
|
||||
get() {
|
||||
return try {
|
||||
getAudioFile(songPaths!![0]).tagOrCreateAndSetDefault.getFirst(FieldKey.YEAR)
|
||||
} catch (ignored: Exception) {
|
||||
null
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
protected val trackNumber: String?
|
||||
get() {
|
||||
return try {
|
||||
getAudioFile(songPaths!![0]).tagOrCreateAndSetDefault.getFirst(FieldKey.TRACK)
|
||||
} catch (ignored: Exception) {
|
||||
null
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
protected val lyrics: String?
|
||||
get() {
|
||||
return try {
|
||||
getAudioFile(songPaths!![0]).tagOrCreateAndSetDefault.getFirst(FieldKey.LYRICS)
|
||||
} catch (ignored: Exception) {
|
||||
null
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
protected val albumArt: Bitmap?
|
||||
get() {
|
||||
try {
|
||||
val artworkTag = getAudioFile(songPaths!![0]).tagOrCreateAndSetDefault
|
||||
.firstArtwork
|
||||
if (artworkTag != null) {
|
||||
val artworkBinaryData = artworkTag.binaryData
|
||||
return BitmapFactory.decodeByteArray(artworkBinaryData, 0, artworkBinaryData.size)
|
||||
}
|
||||
return null
|
||||
} catch (ignored: Exception) {
|
||||
return null
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(contentViewLayout)
|
||||
|
||||
saveFab = findViewById(R.id.saveTags)
|
||||
getIntentExtras()
|
||||
|
||||
songPaths = getSongPaths()
|
||||
if (songPaths!!.isEmpty()) {
|
||||
finish()
|
||||
return
|
||||
}
|
||||
|
||||
setUpViews()
|
||||
|
||||
|
||||
setNavigationbarColorAuto()
|
||||
setTaskDescriptionColorAuto()
|
||||
}
|
||||
|
||||
private fun setUpViews() {
|
||||
setUpScrollView()
|
||||
setUpFab()
|
||||
setUpImageView()
|
||||
}
|
||||
|
||||
private fun setUpScrollView() {
|
||||
//observableScrollView.setScrollViewCallbacks(observableScrollViewCallbacks);
|
||||
}
|
||||
|
||||
private lateinit var items: List<String>
|
||||
|
||||
private fun setUpImageView() {
|
||||
loadCurrentImage()
|
||||
items = listOf(getString(R.string.download_from_last_fm), getString(R.string.pick_from_local_storage), getString(R.string.web_search), getString(R.string.remove_cover))
|
||||
editorImage.setOnClickListener { show }
|
||||
}
|
||||
|
||||
private fun startImagePicker() {
|
||||
val intent = Intent(Intent.ACTION_GET_CONTENT)
|
||||
intent.type = "image/*"
|
||||
startActivityForResult(Intent.createChooser(intent, getString(R.string.pick_from_local_storage)), REQUEST_CODE_SELECT_IMAGE)
|
||||
}
|
||||
|
||||
protected abstract fun loadCurrentImage()
|
||||
|
||||
protected abstract fun getImageFromLastFM()
|
||||
|
||||
protected abstract fun searchImageOnWeb()
|
||||
|
||||
protected abstract fun deleteImage()
|
||||
|
||||
private fun setUpFab() {
|
||||
saveFab.backgroundTintList = ColorStateList.valueOf(ThemeStore.accentColor(this))
|
||||
ColorStateList.valueOf(MaterialValueHelper.getPrimaryTextColor(this, ColorUtil.isColorLight(ThemeStore.accentColor(this)))).apply {
|
||||
saveFab.setTextColor(this)
|
||||
saveFab.iconTint = this
|
||||
}
|
||||
saveFab.apply {
|
||||
scaleX = 0f
|
||||
scaleY = 0f
|
||||
isEnabled = false
|
||||
setOnClickListener { save() }
|
||||
TintHelper.setTintAuto(this, ThemeStore.accentColor(this@AbsTagEditorActivity), true)
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract fun save()
|
||||
|
||||
private fun getIntentExtras() {
|
||||
val intentExtras = intent.extras
|
||||
if (intentExtras != null) {
|
||||
id = intentExtras.getInt(EXTRA_ID)
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract fun getSongPaths(): List<String>
|
||||
|
||||
protected fun searchWebFor(vararg keys: String) {
|
||||
val stringBuilder = StringBuilder()
|
||||
for (key in keys) {
|
||||
stringBuilder.append(key)
|
||||
stringBuilder.append(" ")
|
||||
}
|
||||
val intent = Intent(Intent.ACTION_WEB_SEARCH)
|
||||
intent.putExtra(SearchManager.QUERY, stringBuilder.toString())
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||
|
||||
startActivity(intent)
|
||||
}
|
||||
|
||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||
val id = item.itemId
|
||||
when (id) {
|
||||
android.R.id.home -> {
|
||||
super.onBackPressed()
|
||||
return true
|
||||
}
|
||||
}
|
||||
return super.onOptionsItemSelected(item)
|
||||
}
|
||||
|
||||
protected fun setNoImageMode() {
|
||||
isInNoImageMode = true
|
||||
imageContainer!!.visibility = View.GONE
|
||||
editorImage.visibility = View.GONE
|
||||
editorImage.isEnabled = false
|
||||
|
||||
setColors(intent.getIntExtra(EXTRA_PALETTE, ThemeStore.primaryColor(this)))
|
||||
}
|
||||
|
||||
protected fun dataChanged() {
|
||||
showFab()
|
||||
}
|
||||
|
||||
private fun showFab() {
|
||||
saveFab.animate()
|
||||
.setDuration(500)
|
||||
.setInterpolator(OvershootInterpolator())
|
||||
.scaleX(1f)
|
||||
.scaleY(1f)
|
||||
.start()
|
||||
saveFab.isEnabled = true
|
||||
}
|
||||
|
||||
protected fun setImageBitmap(bitmap: Bitmap?, bgColor: Int) {
|
||||
if (bitmap == null) {
|
||||
editorImage.setImageResource(R.drawable.default_album_art)
|
||||
} else {
|
||||
editorImage.setImageBitmap(bitmap)
|
||||
}
|
||||
setColors(bgColor)
|
||||
}
|
||||
|
||||
protected open fun setColors(color: Int) {
|
||||
paletteColorPrimary = color
|
||||
}
|
||||
|
||||
protected fun writeValuesToFiles(fieldKeyValueMap: Map<FieldKey, String>,
|
||||
artworkInfo: ArtworkInfo?) {
|
||||
RetroUtil.hideSoftKeyboard(this)
|
||||
|
||||
WriteTagsAsyncTask(this)
|
||||
.execute(WriteTagsAsyncTask.LoadingInfo(getSongPaths(), fieldKeyValueMap, artworkInfo))
|
||||
}
|
||||
|
||||
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
||||
super.onActivityResult(requestCode, resultCode, data)
|
||||
when (requestCode) {
|
||||
REQUEST_CODE_SELECT_IMAGE -> if (resultCode == Activity.RESULT_OK) {
|
||||
val selectedImage = data!!.data
|
||||
loadImageFromFile(selectedImage)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract fun loadImageFromFile(selectedFile: Uri?)
|
||||
|
||||
private fun getAudioFile(path: String): AudioFile {
|
||||
return try {
|
||||
AudioFileIO.read(File(path))
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "Could not read audio file $path", e)
|
||||
AudioFile()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class ArtworkInfo constructor(val albumId: Int, val artwork: Bitmap?)
|
||||
|
||||
companion object {
|
||||
|
||||
const val EXTRA_ID = "extra_id"
|
||||
const val EXTRA_PALETTE = "extra_palette"
|
||||
private val TAG = AbsTagEditorActivity::class.java.simpleName
|
||||
private const val REQUEST_CODE_SELECT_IMAGE = 1000
|
||||
}
|
||||
}
|
|
@ -0,0 +1,228 @@
|
|||
package code.name.monkey.retromusic.activities.tageditor
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.res.ColorStateList
|
||||
import android.graphics.Bitmap
|
||||
import android.graphics.BitmapFactory
|
||||
import android.graphics.Color
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
import android.text.Editable
|
||||
import android.text.TextUtils
|
||||
import android.text.TextWatcher
|
||||
import android.widget.Toast
|
||||
import androidx.core.content.ContextCompat
|
||||
import code.name.monkey.appthemehelper.ThemeStore
|
||||
import code.name.monkey.appthemehelper.util.ATHUtil
|
||||
import code.name.monkey.appthemehelper.util.MaterialUtil
|
||||
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.glide.GlideApp
|
||||
import code.name.monkey.retromusic.glide.RetroSimpleTarget
|
||||
import code.name.monkey.retromusic.glide.palette.BitmapPaletteWrapper
|
||||
import code.name.monkey.retromusic.loaders.AlbumLoader
|
||||
import code.name.monkey.retromusic.rest.LastFMRestClient
|
||||
import code.name.monkey.retromusic.rest.model.LastFmAlbum
|
||||
import code.name.monkey.retromusic.util.ImageUtil
|
||||
import code.name.monkey.retromusic.util.LastFMUtil
|
||||
import code.name.monkey.retromusic.util.RetroColorUtil
|
||||
import code.name.monkey.retromusic.util.RetroColorUtil.generatePalette
|
||||
import code.name.monkey.retromusic.util.RetroColorUtil.getColor
|
||||
import com.bumptech.glide.GenericTransitionOptions
|
||||
import com.bumptech.glide.load.engine.DiskCacheStrategy
|
||||
import com.bumptech.glide.request.RequestOptions
|
||||
import com.bumptech.glide.request.transition.Transition
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers
|
||||
import io.reactivex.disposables.CompositeDisposable
|
||||
import io.reactivex.schedulers.Schedulers
|
||||
import kotlinx.android.synthetic.main.activity_album_tag_editor.*
|
||||
import org.jaudiotagger.tag.FieldKey
|
||||
import java.util.*
|
||||
|
||||
|
||||
class AlbumTagEditorActivity : AbsTagEditorActivity(), TextWatcher {
|
||||
override val contentViewLayout: Int
|
||||
get() = R.layout.activity_album_tag_editor
|
||||
|
||||
override fun loadImageFromFile(selectedFile: Uri?) {
|
||||
GlideApp.with(this).`as`(BitmapPaletteWrapper::class.java)
|
||||
.load(selectedFile)
|
||||
.transition(GenericTransitionOptions<BitmapPaletteWrapper>().transition(android.R.anim.fade_in))
|
||||
.apply(RequestOptions()
|
||||
.diskCacheStrategy(DiskCacheStrategy.NONE)
|
||||
.skipMemoryCache(true))
|
||||
.into(object : RetroSimpleTarget<BitmapPaletteWrapper>() {
|
||||
override fun onResourceReady(resource: BitmapPaletteWrapper, transition: Transition<in BitmapPaletteWrapper>?) {
|
||||
RetroColorUtil.getColor(resource.palette, Color.TRANSPARENT)
|
||||
albumArtBitmap = ImageUtil.resizeBitmap(resource.bitmap, 2048)
|
||||
setImageBitmap(albumArtBitmap, RetroColorUtil.getColor(resource.palette, ATHUtil.resolveColor(this@AlbumTagEditorActivity, R.attr.defaultFooterColor)))
|
||||
deleteAlbumArt = false
|
||||
dataChanged()
|
||||
setResult(Activity.RESULT_OK)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
private var albumArtBitmap: Bitmap? = null
|
||||
private var deleteAlbumArt: Boolean = false
|
||||
private var lastFMRestClient: LastFMRestClient? = null
|
||||
private val disposable = CompositeDisposable()
|
||||
|
||||
private fun setupToolbar() {
|
||||
bannerTitle.setTextColor(Color.WHITE)
|
||||
toolbar.setNavigationOnClickListener { onBackPressed() }
|
||||
ToolbarContentTintHelper.setToolbarContentColorBasedOnToolbarColor(this, toolbar, Color.TRANSPARENT)
|
||||
title = null
|
||||
setSupportActionBar(toolbar)
|
||||
TintHelper.setTintAuto(content, ThemeStore.primaryColor(this), true)
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
setDrawUnderStatusBar()
|
||||
super.onCreate(savedInstanceState)
|
||||
lastFMRestClient = LastFMRestClient(this)
|
||||
|
||||
setUpViews()
|
||||
setupToolbar()
|
||||
}
|
||||
|
||||
|
||||
private fun setUpViews() {
|
||||
fillViewsWithFileTags()
|
||||
|
||||
MaterialUtil.setTint(yearContainer, false)
|
||||
MaterialUtil.setTint(genreContainer, false)
|
||||
MaterialUtil.setTint(albumTitleContainer, false)
|
||||
MaterialUtil.setTint(albumArtistContainer, false)
|
||||
|
||||
albumText.addTextChangedListener(this)
|
||||
albumArtistText.addTextChangedListener(this)
|
||||
genreTitle.addTextChangedListener(this)
|
||||
yearTitle.addTextChangedListener(this)
|
||||
}
|
||||
|
||||
private fun fillViewsWithFileTags() {
|
||||
albumText.setText(albumTitle)
|
||||
albumArtistText.setText(albumArtistName)
|
||||
genreTitle.setText(genreName)
|
||||
yearTitle.setText(songYear)
|
||||
}
|
||||
|
||||
override fun loadCurrentImage() {
|
||||
val bitmap = albumArt
|
||||
setImageBitmap(bitmap, getColor(generatePalette(bitmap), ATHUtil.resolveColor(this, R.attr.defaultFooterColor)))
|
||||
deleteAlbumArt = false
|
||||
}
|
||||
|
||||
override fun getImageFromLastFM() {
|
||||
val albumTitleStr = albumText.text.toString()
|
||||
val albumArtistNameStr = albumArtistText.text.toString()
|
||||
if (albumArtistNameStr.trim { it <= ' ' } == "" || albumTitleStr.trim { it <= ' ' } == "") {
|
||||
Toast.makeText(this, resources.getString(R.string.album_or_artist_empty), Toast.LENGTH_SHORT).show()
|
||||
return
|
||||
}
|
||||
|
||||
disposable.add(lastFMRestClient!!.apiService
|
||||
.getAlbumInfo(albumTitleStr, albumArtistNameStr, null)
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribeOn(Schedulers.computation())
|
||||
.subscribe { this.extractDetails(it) })
|
||||
}
|
||||
|
||||
override fun onPause() {
|
||||
super.onPause()
|
||||
disposable.clear()
|
||||
}
|
||||
|
||||
private fun extractDetails(lastFmAlbum: LastFmAlbum) {
|
||||
if (lastFmAlbum.album != null) {
|
||||
|
||||
val url = LastFMUtil.getLargestAlbumImageUrl(lastFmAlbum.album.image)
|
||||
|
||||
if (!TextUtils.isEmpty(url) && url.trim { it <= ' ' }.isNotEmpty()) {
|
||||
GlideApp.with(this)
|
||||
.`as`(BitmapPaletteWrapper::class.java)
|
||||
.load(url)
|
||||
.apply(RequestOptions()
|
||||
.diskCacheStrategy(DiskCacheStrategy.RESOURCE)
|
||||
.error(R.drawable.default_album_art))
|
||||
.into(object : RetroSimpleTarget<BitmapPaletteWrapper>() {
|
||||
override fun onResourceReady(resource: BitmapPaletteWrapper, transition: Transition<in BitmapPaletteWrapper>?) {
|
||||
albumArtBitmap = ImageUtil.resizeBitmap(resource.bitmap, 2048)
|
||||
setImageBitmap(albumArtBitmap, getColor(resource.palette,
|
||||
ContextCompat.getColor(this@AlbumTagEditorActivity, R.color.md_grey_500)))
|
||||
deleteAlbumArt = false
|
||||
dataChanged()
|
||||
setResult(Activity.RESULT_OK)
|
||||
}
|
||||
})
|
||||
return
|
||||
}
|
||||
if (lastFmAlbum.album.tags.tag.size > 0) {
|
||||
genreTitle.setText(lastFmAlbum.album.tags.tag[0].name)
|
||||
}
|
||||
|
||||
}
|
||||
toastLoadingFailed()
|
||||
}
|
||||
|
||||
private fun toastLoadingFailed() {
|
||||
Toast.makeText(this@AlbumTagEditorActivity, R.string.could_not_download_album_cover, Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
|
||||
override fun searchImageOnWeb() {
|
||||
searchWebFor(albumText.text.toString(), albumArtistText.text.toString())
|
||||
}
|
||||
|
||||
override fun deleteImage() {
|
||||
setImageBitmap(BitmapFactory.decodeResource(resources, R.drawable.default_album_art), ATHUtil.resolveColor(this, R.attr.defaultFooterColor))
|
||||
deleteAlbumArt = true
|
||||
dataChanged()
|
||||
}
|
||||
|
||||
override fun save() {
|
||||
val fieldKeyValueMap = EnumMap<FieldKey, String>(FieldKey::class.java)
|
||||
fieldKeyValueMap[FieldKey.ALBUM] = albumText.text.toString()
|
||||
//android seems not to recognize album_artist field so we additionally write the normal artist field
|
||||
fieldKeyValueMap[FieldKey.ARTIST] = albumArtistText.text.toString()
|
||||
fieldKeyValueMap[FieldKey.ALBUM_ARTIST] = albumArtistText.text.toString()
|
||||
fieldKeyValueMap[FieldKey.GENRE] = genreTitle.text.toString()
|
||||
fieldKeyValueMap[FieldKey.YEAR] = yearTitle.text.toString()
|
||||
|
||||
writeValuesToFiles(fieldKeyValueMap, if (deleteAlbumArt) AbsTagEditorActivity.ArtworkInfo(id, null)
|
||||
else if (albumArtBitmap == null) null else AbsTagEditorActivity.ArtworkInfo(id, albumArtBitmap!!))
|
||||
}
|
||||
|
||||
override fun getSongPaths(): List<String> {
|
||||
val songs = AlbumLoader.getAlbum(this, id).blockingFirst().songs
|
||||
val paths = ArrayList<String>(songs!!.size)
|
||||
for (song in songs) {
|
||||
paths.add(song.data!!)
|
||||
}
|
||||
return paths
|
||||
}
|
||||
|
||||
|
||||
override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {
|
||||
|
||||
}
|
||||
|
||||
override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {
|
||||
|
||||
}
|
||||
|
||||
override fun afterTextChanged(s: Editable) {
|
||||
dataChanged()
|
||||
}
|
||||
|
||||
override fun setColors(color: Int) {
|
||||
super.setColors(color)
|
||||
saveFab.backgroundTintList = ColorStateList.valueOf(color)
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
val TAG: String = AlbumTagEditorActivity::class.java.simpleName
|
||||
}
|
||||
}
|
|
@ -0,0 +1,135 @@
|
|||
package code.name.monkey.retromusic.activities.tageditor
|
||||
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
import android.text.Editable
|
||||
import android.text.TextWatcher
|
||||
import androidx.core.content.ContextCompat
|
||||
import code.name.monkey.appthemehelper.ThemeStore
|
||||
import code.name.monkey.appthemehelper.util.MaterialUtil
|
||||
import code.name.monkey.appthemehelper.util.TintHelper
|
||||
import code.name.monkey.retromusic.R
|
||||
import code.name.monkey.retromusic.loaders.SongLoader
|
||||
import kotlinx.android.synthetic.main.activity_song_tag_editor.*
|
||||
import org.jaudiotagger.tag.FieldKey
|
||||
import java.util.*
|
||||
|
||||
|
||||
class SongTagEditorActivity : AbsTagEditorActivity(), TextWatcher {
|
||||
|
||||
override val contentViewLayout: Int
|
||||
get() = R.layout.activity_song_tag_editor
|
||||
|
||||
private fun setupToolbar() {
|
||||
appBarLayout.setBackgroundColor(ThemeStore.primaryColor(this))
|
||||
toolbar.apply {
|
||||
setBackgroundColor(ThemeStore.primaryColor(context))
|
||||
navigationIcon = TintHelper.createTintedDrawable(ContextCompat.getDrawable(context, R.drawable.ic_keyboard_backspace_black_24dp), ThemeStore.textColorPrimary(context))
|
||||
setNavigationOnClickListener { onBackPressed() }
|
||||
setSupportActionBar(toolbar)
|
||||
}
|
||||
title = null
|
||||
bannerTitle.setTextColor(ThemeStore.textColorPrimary(this))
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
|
||||
setNoImageMode()
|
||||
setUpViews()
|
||||
setupToolbar()
|
||||
setStatusbarColorAuto()
|
||||
}
|
||||
|
||||
private fun setUpViews() {
|
||||
fillViewsWithFileTags()
|
||||
MaterialUtil.setTint(songTextContainer,false)
|
||||
MaterialUtil.setTint(composerContainer, false)
|
||||
MaterialUtil.setTint(albumTextContainer, false)
|
||||
MaterialUtil.setTint(artistContainer, false)
|
||||
MaterialUtil.setTint(albumArtistContainer, false)
|
||||
MaterialUtil.setTint(yearContainer, false)
|
||||
MaterialUtil.setTint(genreContainer, false)
|
||||
MaterialUtil.setTint(trackNumberContainer, false)
|
||||
MaterialUtil.setTint(lyricsContainer, false)
|
||||
|
||||
albumText.addTextChangedListener(this)
|
||||
songText.addTextChangedListener(this)
|
||||
albumText.addTextChangedListener(this)
|
||||
artistText.addTextChangedListener(this)
|
||||
genreText.addTextChangedListener(this)
|
||||
yearText.addTextChangedListener(this)
|
||||
trackNumberText.addTextChangedListener(this)
|
||||
lyricsText.addTextChangedListener(this)
|
||||
songComposerText.addTextChangedListener(this)
|
||||
}
|
||||
|
||||
private fun fillViewsWithFileTags() {
|
||||
songText.setText(songTitle)
|
||||
albumArtistText.setText(albumArtist)
|
||||
albumText.setText(albumTitle)
|
||||
artistText.setText(artistName)
|
||||
genreText.setText(genreName)
|
||||
yearText.setText(songYear)
|
||||
trackNumberText.setText(trackNumber)
|
||||
lyricsText.setText(lyrics)
|
||||
songComposerText.setText(composer)
|
||||
}
|
||||
|
||||
override fun loadCurrentImage() {
|
||||
|
||||
}
|
||||
|
||||
override fun getImageFromLastFM() {
|
||||
|
||||
}
|
||||
|
||||
override fun searchImageOnWeb() {
|
||||
|
||||
}
|
||||
|
||||
override fun deleteImage() {
|
||||
|
||||
}
|
||||
|
||||
override fun save() {
|
||||
val fieldKeyValueMap = EnumMap<FieldKey, String>(FieldKey::class.java)
|
||||
fieldKeyValueMap[FieldKey.TITLE] = songText.text.toString()
|
||||
fieldKeyValueMap[FieldKey.ALBUM] = albumText.text.toString()
|
||||
fieldKeyValueMap[FieldKey.ARTIST] = artistText.text.toString()
|
||||
fieldKeyValueMap[FieldKey.GENRE] = genreText.text.toString()
|
||||
fieldKeyValueMap[FieldKey.YEAR] = yearText.text.toString()
|
||||
fieldKeyValueMap[FieldKey.TRACK] = trackNumberText.text.toString()
|
||||
fieldKeyValueMap[FieldKey.LYRICS] = lyricsText.text.toString()
|
||||
fieldKeyValueMap[FieldKey.ALBUM_ARTIST] = albumArtistText.text.toString()
|
||||
fieldKeyValueMap[FieldKey.COMPOSER] = songComposerText.text.toString()
|
||||
writeValuesToFiles(fieldKeyValueMap, null)
|
||||
}
|
||||
|
||||
override fun getSongPaths(): List<String> {
|
||||
val paths = ArrayList<String>(1)
|
||||
paths.add(SongLoader.getSong(this, id).blockingFirst().data)
|
||||
return paths
|
||||
}
|
||||
|
||||
override fun loadImageFromFile(selectedFile: Uri?) {
|
||||
|
||||
}
|
||||
|
||||
override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {
|
||||
|
||||
}
|
||||
|
||||
override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {
|
||||
|
||||
}
|
||||
|
||||
override fun afterTextChanged(s: Editable) {
|
||||
dataChanged()
|
||||
}
|
||||
|
||||
companion object {
|
||||
val TAG: String = SongTagEditorActivity::class.java.simpleName
|
||||
}
|
||||
}
|
|
@ -0,0 +1,165 @@
|
|||
package code.name.monkey.retromusic.activities.tageditor;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.Dialog;
|
||||
import android.content.Context;
|
||||
import android.graphics.Bitmap;
|
||||
import android.media.MediaScannerConnection;
|
||||
|
||||
import com.afollestad.materialdialogs.MaterialDialog;
|
||||
|
||||
import org.jaudiotagger.audio.AudioFile;
|
||||
import org.jaudiotagger.audio.AudioFileIO;
|
||||
import org.jaudiotagger.audio.exceptions.CannotReadException;
|
||||
import org.jaudiotagger.audio.exceptions.CannotWriteException;
|
||||
import org.jaudiotagger.audio.exceptions.InvalidAudioFrameException;
|
||||
import org.jaudiotagger.audio.exceptions.ReadOnlyFileException;
|
||||
import org.jaudiotagger.tag.FieldKey;
|
||||
import org.jaudiotagger.tag.Tag;
|
||||
import org.jaudiotagger.tag.TagException;
|
||||
import org.jaudiotagger.tag.images.Artwork;
|
||||
import org.jaudiotagger.tag.images.ArtworkFactory;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import code.name.monkey.retromusic.R;
|
||||
import code.name.monkey.retromusic.misc.DialogAsyncTask;
|
||||
import code.name.monkey.retromusic.misc.UpdateToastMediaScannerCompletionListener;
|
||||
import code.name.monkey.retromusic.util.MusicUtil;
|
||||
|
||||
public class WriteTagsAsyncTask extends
|
||||
DialogAsyncTask<WriteTagsAsyncTask.LoadingInfo, Integer, String[]> {
|
||||
|
||||
private Context applicationContext;
|
||||
|
||||
public WriteTagsAsyncTask(Context context) {
|
||||
super(context);
|
||||
applicationContext = context;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String[] doInBackground(LoadingInfo... params) {
|
||||
try {
|
||||
LoadingInfo info = params[0];
|
||||
|
||||
Artwork artwork = null;
|
||||
File albumArtFile = null;
|
||||
if (info.artworkInfo != null && info.artworkInfo.getArtwork() != null) {
|
||||
try {
|
||||
albumArtFile = MusicUtil.createAlbumArtFile().getCanonicalFile();
|
||||
info.artworkInfo.getArtwork()
|
||||
.compress(Bitmap.CompressFormat.PNG, 0, new FileOutputStream(albumArtFile));
|
||||
artwork = ArtworkFactory.createArtworkFromFile(albumArtFile);
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
int counter = 0;
|
||||
boolean wroteArtwork = false;
|
||||
boolean deletedArtwork = false;
|
||||
for (String filePath : info.filePaths) {
|
||||
publishProgress(++counter, info.filePaths.size());
|
||||
try {
|
||||
AudioFile audioFile = AudioFileIO.read(new File(filePath));
|
||||
Tag tag = audioFile.getTagOrCreateAndSetDefault();
|
||||
|
||||
if (info.fieldKeyValueMap != null) {
|
||||
for (Map.Entry<FieldKey, String> entry : info.fieldKeyValueMap.entrySet()) {
|
||||
try {
|
||||
tag.setField(entry.getKey(), entry.getValue());
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (info.artworkInfo != null) {
|
||||
if (info.artworkInfo.getArtwork() == null) {
|
||||
tag.deleteArtworkField();
|
||||
deletedArtwork = true;
|
||||
} else if (artwork != null) {
|
||||
tag.deleteArtworkField();
|
||||
tag.setField(artwork);
|
||||
wroteArtwork = true;
|
||||
}
|
||||
}
|
||||
|
||||
audioFile.commit();
|
||||
} catch (@NonNull CannotReadException | IOException | CannotWriteException | TagException | ReadOnlyFileException | InvalidAudioFrameException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
Context context = getContext();
|
||||
if (context != null) {
|
||||
if (wroteArtwork) {
|
||||
MusicUtil.insertAlbumArt(context, info.artworkInfo.getAlbumId(), albumArtFile.getPath());
|
||||
} else if (deletedArtwork) {
|
||||
MusicUtil.deleteAlbumArt(context, info.artworkInfo.getAlbumId());
|
||||
}
|
||||
}
|
||||
|
||||
return info.filePaths.toArray(new String[info.filePaths.size()]);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(String[] toBeScanned) {
|
||||
super.onPostExecute(toBeScanned);
|
||||
scan(toBeScanned);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCancelled(String[] toBeScanned) {
|
||||
super.onCancelled(toBeScanned);
|
||||
scan(toBeScanned);
|
||||
}
|
||||
|
||||
private void scan(String[] toBeScanned) {
|
||||
Context context = getContext();
|
||||
MediaScannerConnection.scanFile(applicationContext, toBeScanned, null,
|
||||
context instanceof Activity ? new UpdateToastMediaScannerCompletionListener(
|
||||
(Activity) context, toBeScanned) : null);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Dialog createDialog(@NonNull Context context) {
|
||||
return new MaterialDialog(context)
|
||||
.title(R.string.saving_changes, "")
|
||||
.cancelable(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onProgressUpdate(@NonNull Dialog dialog, Integer... values) {
|
||||
super.onProgressUpdate(dialog, values);
|
||||
//((MaterialDialog) dialog).setMaxProgress(values[1]);
|
||||
//((MaterialDialog) dialog).setProgress(values[0]);
|
||||
}
|
||||
|
||||
public static class LoadingInfo {
|
||||
|
||||
final Collection<String> filePaths;
|
||||
@Nullable
|
||||
final Map<FieldKey, String> fieldKeyValueMap;
|
||||
@Nullable
|
||||
private AbsTagEditorActivity.ArtworkInfo artworkInfo;
|
||||
|
||||
public LoadingInfo(Collection<String> filePaths,
|
||||
@Nullable Map<FieldKey, String> fieldKeyValueMap,
|
||||
@Nullable AbsTagEditorActivity.ArtworkInfo artworkInfo) {
|
||||
this.filePaths = filePaths;
|
||||
this.fieldKeyValueMap = fieldKeyValueMap;
|
||||
this.artworkInfo = artworkInfo;
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue