Code refactor

This commit is contained in:
h4h13 2019-12-30 16:31:50 +05:30
parent c3153a9650
commit 5a51d0bfc0
50 changed files with 2137 additions and 2105 deletions

View file

@ -25,27 +25,21 @@ import code.name.monkey.retromusic.dagger.module.AppModule
import com.anjlab.android.iab.v3.BillingProcessor import com.anjlab.android.iab.v3.BillingProcessor
import com.anjlab.android.iab.v3.TransactionDetails import com.anjlab.android.iab.v3.TransactionDetails
class App : MultiDexApplication() { class App : MultiDexApplication() {
lateinit var billingProcessor: BillingProcessor lateinit var billingProcessor: BillingProcessor
override fun onCreate() { override fun onCreate() {
/* if (MissingSplitsManagerFactory.create(this).disableAppIfMissingRequiredSplits()) {
return
}*/
super.onCreate() super.onCreate()
instance = this instance = this
musicComponent = DaggerMusicComponent.builder() musicComponent = initDagger(this)
.appModule(AppModule(this))
.build()
// default theme // default theme
if (!ThemeStore.isConfigured(this, 3)) { if (!ThemeStore.isConfigured(this, 3)) {
ThemeStore.editTheme(this) ThemeStore.editTheme(this)
.accentColorRes(R.color.md_deep_purple_A200) .accentColorRes(R.color.md_deep_purple_A200)
.coloredNavigationBar(true) .coloredNavigationBar(true)
.commit() .commit()
} }
if (VersionUtils.hasNougatMR()) if (VersionUtils.hasNougatMR())
@ -53,19 +47,25 @@ class App : MultiDexApplication() {
// automatically restores purchases // automatically restores purchases
billingProcessor = BillingProcessor(this, BuildConfig.GOOGLE_PLAY_LICENSING_KEY, billingProcessor = BillingProcessor(this, BuildConfig.GOOGLE_PLAY_LICENSING_KEY,
object : BillingProcessor.IBillingHandler { object : BillingProcessor.IBillingHandler {
override fun onProductPurchased(productId: String, details: TransactionDetails?) {} override fun onProductPurchased(productId: String, details: TransactionDetails?) {}
override fun onPurchaseHistoryRestored() { override fun onPurchaseHistoryRestored() {
Toast.makeText(this@App, R.string.restored_previous_purchase_please_restart, Toast.LENGTH_LONG).show(); Toast.makeText(this@App, R.string.restored_previous_purchase_please_restart, Toast.LENGTH_LONG)
} .show()
}
override fun onBillingError(errorCode: Int, error: Throwable?) {} override fun onBillingError(errorCode: Int, error: Throwable?) {}
override fun onBillingInitialized() {} override fun onBillingInitialized() {}
}) })
} }
private fun initDagger(app: App): MusicComponent =
DaggerMusicComponent.builder()
.appModule(AppModule(app))
.build()
override fun onTerminate() { override fun onTerminate() {
super.onTerminate() super.onTerminate()
billingProcessor.release() billingProcessor.release()
@ -85,6 +85,5 @@ class App : MultiDexApplication() {
lateinit var musicComponent: MusicComponent lateinit var musicComponent: MusicComponent
const val PRO_VERSION_PRODUCT_ID = "pro_version" const val PRO_VERSION_PRODUCT_ID = "pro_version"
} }
} }

View file

@ -19,6 +19,8 @@ package code.name.monkey.retromusic
*/ */
sealed class Result<out T : Any> { sealed class Result<out T : Any> {
class Success<out T : Any>(val data: T) : Result<T>() class Success<out T : Any>(val data: T) : Result<T>()
class Error(val exception: Throwable) : Result<Nothing>() class Error(val exception: Throwable) : Result<Nothing>()
} }

View file

@ -32,11 +32,22 @@ import com.afollestad.materialdialogs.bottomsheets.BottomSheet
import com.afollestad.materialdialogs.list.listItems import com.afollestad.materialdialogs.list.listItems
import com.google.gson.Gson import com.google.gson.Gson
import com.google.gson.reflect.TypeToken import com.google.gson.reflect.TypeToken
import kotlinx.android.synthetic.main.activity_about.* import kotlinx.android.synthetic.main.activity_about.toolbar
import kotlinx.android.synthetic.main.card_credit.* import kotlinx.android.synthetic.main.card_credit.recyclerView
import kotlinx.android.synthetic.main.card_other.* import kotlinx.android.synthetic.main.card_other.changelog
import kotlinx.android.synthetic.main.card_retro_info.* import kotlinx.android.synthetic.main.card_other.openSource
import kotlinx.android.synthetic.main.card_social.* import kotlinx.android.synthetic.main.card_other.version
import kotlinx.android.synthetic.main.card_retro_info.appGithub
import kotlinx.android.synthetic.main.card_retro_info.appRate
import kotlinx.android.synthetic.main.card_retro_info.appShare
import kotlinx.android.synthetic.main.card_retro_info.appTranslation
import kotlinx.android.synthetic.main.card_retro_info.bugReportLink
import kotlinx.android.synthetic.main.card_retro_info.donateLink
import kotlinx.android.synthetic.main.card_retro_info.faqLink
import kotlinx.android.synthetic.main.card_social.instagramLink
import kotlinx.android.synthetic.main.card_social.pinterestLink
import kotlinx.android.synthetic.main.card_social.telegramLink
import kotlinx.android.synthetic.main.card_social.twitterLink
import java.io.IOException import java.io.IOException
import java.nio.charset.StandardCharsets import java.nio.charset.StandardCharsets
@ -68,7 +79,6 @@ class AboutActivity : AbsBaseActivity(), View.OnClickListener {
setNavigationbarColorAuto() setNavigationbarColorAuto()
setLightNavigationBar(true) setLightNavigationBar(true)
val toolbarColor = ATHUtil.resolveColor(this, R.attr.colorSurface) val toolbarColor = ATHUtil.resolveColor(this, R.attr.colorSurface)
toolbar.setBackgroundColor(toolbarColor) toolbar.setBackgroundColor(toolbarColor)
ToolbarContentTintHelper.colorBackButton(toolbar) ToolbarContentTintHelper.colorBackButton(toolbar)
@ -107,7 +117,6 @@ class AboutActivity : AbsBaseActivity(), View.OnClickListener {
openSource.setOnClickListener(this) openSource.setOnClickListener(this)
pinterestLink.setOnClickListener(this) pinterestLink.setOnClickListener(this)
bugReportLink.setOnClickListener(this) bugReportLink.setOnClickListener(this)
} }
override fun onClick(view: View) { override fun onClick(view: View) {
@ -153,8 +162,8 @@ class AboutActivity : AbsBaseActivity(), View.OnClickListener {
private fun shareApp() { private fun shareApp() {
ShareCompat.IntentBuilder.from(this).setType("text/plain") ShareCompat.IntentBuilder.from(this).setType("text/plain")
.setChooserTitle(R.string.share_app) .setChooserTitle(R.string.share_app)
.setText(String.format(getString(R.string.app_share), packageName)).startChooser() .setText(String.format(getString(R.string.app_share), packageName)).startChooser()
} }
private fun loadContributors() { private fun loadContributors() {

View file

@ -42,23 +42,37 @@ import code.name.monkey.retromusic.util.PreferenceUtil
import code.name.monkey.retromusic.util.RetroColorUtil import code.name.monkey.retromusic.util.RetroColorUtil
import com.afollestad.materialcab.MaterialCab import com.afollestad.materialcab.MaterialCab
import com.bumptech.glide.Glide import com.bumptech.glide.Glide
import kotlinx.android.synthetic.main.activity_album.* import kotlinx.android.synthetic.main.activity_album.albumText
import kotlinx.android.synthetic.main.activity_album_content.* import kotlinx.android.synthetic.main.activity_album.albumTitle
import java.util.* import kotlinx.android.synthetic.main.activity_album.image
import kotlinx.android.synthetic.main.activity_album.toolbar
import kotlinx.android.synthetic.main.activity_album_content.moreRecyclerView
import kotlinx.android.synthetic.main.activity_album_content.moreTitle
import kotlinx.android.synthetic.main.activity_album_content.playAction
import kotlinx.android.synthetic.main.activity_album_content.recyclerView
import kotlinx.android.synthetic.main.activity_album_content.shuffleAction
import kotlinx.android.synthetic.main.activity_album_content.songTitle
import java.util.ArrayList
import javax.inject.Inject import javax.inject.Inject
import android.util.Pair as UtilPair import android.util.Pair as UtilPair
class AlbumDetailsActivity : AbsSlidingMusicPanelActivity(), AlbumDetailsView, CabHolder { class AlbumDetailsActivity : AbsSlidingMusicPanelActivity(), AlbumDetailsView, CabHolder {
override fun openCab(menuRes: Int, callback: MaterialCab.Callback): MaterialCab { override fun openCab(menuRes: Int, callback: MaterialCab.Callback): MaterialCab {
cab?.let { cab?.let {
if (it.isActive) it.finish() if (it.isActive) it.finish()
} }
cab = MaterialCab(this, R.id.cab_stub) cab = MaterialCab(this, R.id.cab_stub)
.setMenu(menuRes) .setMenu(menuRes)
.setCloseDrawableRes(R.drawable.ic_close_white_24dp) .setCloseDrawableRes(R.drawable.ic_close_white_24dp)
.setBackgroundColor(RetroColorUtil.shiftBackgroundColorForLightText(ATHUtil.resolveColor(this, R.attr.colorSurface))) .setBackgroundColor(
.start(callback) RetroColorUtil.shiftBackgroundColorForLightText(
ATHUtil.resolveColor(
this,
R.attr.colorSurface
)
)
)
.start(callback)
return cab as MaterialCab return cab as MaterialCab
} }
@ -93,7 +107,10 @@ class AlbumDetailsActivity : AbsSlidingMusicPanelActivity(), AlbumDetailsView, C
setupRecyclerView() setupRecyclerView()
artistImage.setOnClickListener { artistImage.setOnClickListener {
val artistPairs = ActivityOptions.makeSceneTransitionAnimation(this, UtilPair.create(artistImage, getString(R.string.transition_artist_image))) val artistPairs = ActivityOptions.makeSceneTransitionAnimation(
this,
UtilPair.create(artistImage, getString(R.string.transition_artist_image))
)
NavigationUtil.goToArtistOptions(this, album.artistId, artistPairs) NavigationUtil.goToArtistOptions(this, album.artistId, artistPairs)
} }
playAction.apply { playAction.apply {
@ -142,9 +159,18 @@ class AlbumDetailsActivity : AbsSlidingMusicPanelActivity(), AlbumDetailsView, C
albumTitle.text = album.title albumTitle.text = album.title
if (MusicUtil.getYearString(album.year) == "-") { if (MusicUtil.getYearString(album.year) == "-") {
albumText.text = String.format("%s • %s", album.artistName, MusicUtil.getReadableDurationString(MusicUtil.getTotalDuration(this, album.songs))) albumText.text = String.format(
"%s • %s",
album.artistName,
MusicUtil.getReadableDurationString(MusicUtil.getTotalDuration(this, album.songs))
)
} else { } else {
albumText.text = String.format("%s • %s • %s", album.artistName, MusicUtil.getYearString(album.year), MusicUtil.getReadableDurationString(MusicUtil.getTotalDuration(this, album.songs))) albumText.text = String.format(
"%s • %s • %s",
album.artistName,
MusicUtil.getYearString(album.year),
MusicUtil.getReadableDurationString(MusicUtil.getTotalDuration(this, album.songs))
)
} }
loadAlbumCover() loadAlbumCover()
simpleSongAdapter.swapDataSet(album.songs) simpleSongAdapter.swapDataSet(album.songs)
@ -158,22 +184,20 @@ class AlbumDetailsActivity : AbsSlidingMusicPanelActivity(), AlbumDetailsView, C
val albumAdapter = HorizontalAlbumAdapter(this, albums, false, null) val albumAdapter = HorizontalAlbumAdapter(this, albums, false, null)
moreRecyclerView.layoutManager = GridLayoutManager( moreRecyclerView.layoutManager = GridLayoutManager(
this, this,
1, 1,
GridLayoutManager.HORIZONTAL, GridLayoutManager.HORIZONTAL,
false false
) )
moreRecyclerView.adapter = albumAdapter moreRecyclerView.adapter = albumAdapter
} }
override fun loadArtistImage(artist: Artist) { override fun loadArtistImage(artist: Artist) {
ArtistGlideRequest.Builder.from(Glide.with(this), artist).generatePalette(this).build() ArtistGlideRequest.Builder.from(Glide.with(this), artist).generatePalette(this).build()
.dontAnimate().dontTransform().into(object : RetroMusicColoredTarget(artistImage) { .dontAnimate().dontTransform().into(object : RetroMusicColoredTarget(artistImage) {
override fun onColorReady(color: Int) { override fun onColorReady(color: Int) {
}
} })
})
} }
private fun loadAlbumCover() { private fun loadAlbumCover() {
@ -183,15 +207,15 @@ class AlbumDetailsActivity : AbsSlidingMusicPanelActivity(), AlbumDetailsView, C
.dontAnimate().into(image)*/ .dontAnimate().into(image)*/
SongGlideRequest.Builder.from(Glide.with(this), album.safeGetFirstSong()) SongGlideRequest.Builder.from(Glide.with(this), album.safeGetFirstSong())
.checkIgnoreMediaStore(this) .checkIgnoreMediaStore(this)
.ignoreMediaStore(PreferenceUtil.getInstance(this).ignoreMediaStoreArtwork()) .ignoreMediaStore(PreferenceUtil.getInstance(this).ignoreMediaStoreArtwork())
.generatePalette(this) .generatePalette(this)
.build().dontAnimate().dontTransform() .build().dontAnimate().dontTransform()
.into(object : RetroMusicColoredTarget(image) { .into(object : RetroMusicColoredTarget(image) {
override fun onColorReady(color: Int) { override fun onColorReady(color: Int) {
setColors(color) setColors(color)
} }
}) })
} }
private fun setColors(color: Int) { private fun setColors(color: Int) {
@ -254,7 +278,11 @@ class AlbumDetailsActivity : AbsSlidingMusicPanelActivity(), AlbumDetailsView, C
R.id.action_tag_editor -> { R.id.action_tag_editor -> {
val intent = Intent(this, AlbumTagEditorActivity::class.java) val intent = Intent(this, AlbumTagEditorActivity::class.java)
intent.putExtra(AbsTagEditorActivity.EXTRA_ID, album.id) intent.putExtra(AbsTagEditorActivity.EXTRA_ID, album.id)
val options = ActivityOptions.makeSceneTransitionAnimation(this, image, getString(R.string.transition_album_art)) val options = ActivityOptions.makeSceneTransitionAnimation(
this,
image,
getString(R.string.transition_album_art)
)
startActivityForResult(intent, TAG_EDITOR_REQUEST, options.toBundle()) startActivityForResult(intent, TAG_EDITOR_REQUEST, options.toBundle())
return true return true
} }
@ -275,8 +303,10 @@ class AlbumDetailsActivity : AbsSlidingMusicPanelActivity(), AlbumDetailsView, C
when (savedSortOrder) { when (savedSortOrder) {
AlbumSongSortOrder.SONG_A_Z -> sortOrder.findItem(R.id.action_sort_order_title).isChecked = true 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_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_TRACK_LIST -> sortOrder.findItem(R.id.action_sort_order_track_list).isChecked =
AlbumSongSortOrder.SONG_DURATION -> sortOrder.findItem(R.id.action_sort_order_artist_song_duration).isChecked = true true
AlbumSongSortOrder.SONG_DURATION -> sortOrder.findItem(R.id.action_sort_order_artist_song_duration)
.isChecked = true
} }
} }

View file

@ -33,14 +33,29 @@ import code.name.monkey.retromusic.model.Artist
import code.name.monkey.retromusic.mvp.presenter.ArtistDetailsPresenter import code.name.monkey.retromusic.mvp.presenter.ArtistDetailsPresenter
import code.name.monkey.retromusic.mvp.presenter.ArtistDetailsView import code.name.monkey.retromusic.mvp.presenter.ArtistDetailsView
import code.name.monkey.retromusic.rest.model.LastFmArtist import code.name.monkey.retromusic.rest.model.LastFmArtist
import code.name.monkey.retromusic.util.* import code.name.monkey.retromusic.util.CustomArtistImageUtil
import code.name.monkey.retromusic.util.DensityUtil
import code.name.monkey.retromusic.util.MusicUtil
import code.name.monkey.retromusic.util.PreferenceUtil
import code.name.monkey.retromusic.util.RetroColorUtil
import code.name.monkey.retromusic.util.RetroUtil
import com.afollestad.materialcab.MaterialCab import com.afollestad.materialcab.MaterialCab
import com.bumptech.glide.Glide import com.bumptech.glide.Glide
import kotlinx.android.synthetic.main.activity_artist_content.* import kotlinx.android.synthetic.main.activity_artist_content.albumRecyclerView
import kotlinx.android.synthetic.main.activity_artist_details.* import kotlinx.android.synthetic.main.activity_artist_content.albumTitle
import java.util.* import kotlinx.android.synthetic.main.activity_artist_content.biographyText
import kotlinx.android.synthetic.main.activity_artist_content.biographyTitle
import kotlinx.android.synthetic.main.activity_artist_content.playAction
import kotlinx.android.synthetic.main.activity_artist_content.recyclerView
import kotlinx.android.synthetic.main.activity_artist_content.shuffleAction
import kotlinx.android.synthetic.main.activity_artist_content.songTitle
import kotlinx.android.synthetic.main.activity_artist_details.artistImage
import kotlinx.android.synthetic.main.activity_artist_details.artistTitle
import kotlinx.android.synthetic.main.activity_artist_details.imageContainer
import kotlinx.android.synthetic.main.activity_artist_details.text
import kotlinx.android.synthetic.main.activity_artist_details.toolbar
import java.util.Locale
import javax.inject.Inject import javax.inject.Inject
import kotlin.collections.ArrayList
class ArtistDetailActivity : AbsSlidingMusicPanelActivity(), ArtistDetailsView, CabHolder { class ArtistDetailActivity : AbsSlidingMusicPanelActivity(), ArtistDetailsView, CabHolder {
override fun openCab(menuRes: Int, callback: MaterialCab.Callback): MaterialCab { override fun openCab(menuRes: Int, callback: MaterialCab.Callback): MaterialCab {
@ -48,10 +63,17 @@ class ArtistDetailActivity : AbsSlidingMusicPanelActivity(), ArtistDetailsView,
if (it.isActive) it.finish() if (it.isActive) it.finish()
} }
cab = MaterialCab(this, R.id.cab_stub) cab = MaterialCab(this, R.id.cab_stub)
.setMenu(menuRes) .setMenu(menuRes)
.setCloseDrawableRes(R.drawable.ic_close_white_24dp) .setCloseDrawableRes(R.drawable.ic_close_white_24dp)
.setBackgroundColor(RetroColorUtil.shiftBackgroundColorForLightText(ATHUtil.resolveColor(this, R.attr.colorSurface))) .setBackgroundColor(
.start(callback) RetroColorUtil.shiftBackgroundColorForLightText(
ATHUtil.resolveColor(
this,
R.attr.colorSurface
)
)
)
.start(callback)
return cab as MaterialCab return cab as MaterialCab
} }
@ -155,7 +177,6 @@ class ArtistDetailActivity : AbsSlidingMusicPanelActivity(), ArtistDetailsView,
} }
override fun showEmptyView() { override fun showEmptyView() {
} }
override fun complete() { override fun complete() {
@ -174,9 +195,9 @@ class ArtistDetailActivity : AbsSlidingMusicPanelActivity(), ArtistDetailsView,
} }
artistTitle.text = artist.name artistTitle.text = artist.name
text.text = String.format( text.text = String.format(
"%s • %s", "%s • %s",
MusicUtil.getArtistInfoString(this, artist), MusicUtil.getArtistInfoString(this, artist),
MusicUtil.getReadableDurationString(MusicUtil.getTotalDuration(this, artist.songs)) MusicUtil.getReadableDurationString(MusicUtil.getTotalDuration(this, artist.songs))
) )
songAdapter.swapDataSet(artist.songs) songAdapter.swapDataSet(artist.songs)
@ -184,8 +205,8 @@ class ArtistDetailActivity : AbsSlidingMusicPanelActivity(), ArtistDetailsView,
} }
private fun loadBiography( private fun loadBiography(
name: String, name: String,
lang: String? = Locale.getDefault().language lang: String? = Locale.getDefault().language
) { ) {
biography = null biography = null
this.lang = lang this.lang = lang
@ -217,11 +238,11 @@ class ArtistDetailActivity : AbsSlidingMusicPanelActivity(), ArtistDetailsView,
private fun loadArtistImage() { private fun loadArtistImage() {
ArtistGlideRequest.Builder.from(Glide.with(this), artist).generatePalette(this).build() ArtistGlideRequest.Builder.from(Glide.with(this), artist).generatePalette(this).build()
.dontAnimate().into(object : RetroMusicColoredTarget(artistImage) { .dontAnimate().into(object : RetroMusicColoredTarget(artistImage) {
override fun onColorReady(color: Int) { override fun onColorReady(color: Int) {
setColors(color) setColors(color)
} }
}) })
} }
private fun setColors(color: Int) { private fun setColors(color: Int) {
@ -275,11 +296,15 @@ class ArtistDetailActivity : AbsSlidingMusicPanelActivity(), ArtistDetailsView,
R.id.action_set_artist_image -> { R.id.action_set_artist_image -> {
val intent = Intent(Intent.ACTION_GET_CONTENT) val intent = Intent(Intent.ACTION_GET_CONTENT)
intent.type = "image/*" intent.type = "image/*"
startActivityForResult(Intent.createChooser(intent, getString(R.string.pick_from_local_storage)), REQUEST_CODE_SELECT_IMAGE) startActivityForResult(
Intent.createChooser(intent, getString(R.string.pick_from_local_storage)),
REQUEST_CODE_SELECT_IMAGE
)
return true return true
} }
R.id.action_reset_artist_image -> { R.id.action_reset_artist_image -> {
Toast.makeText(this@ArtistDetailActivity, resources.getString(R.string.updating), Toast.LENGTH_SHORT).show() Toast.makeText(this@ArtistDetailActivity, resources.getString(R.string.updating), Toast.LENGTH_SHORT)
.show()
CustomArtistImageUtil.getInstance(this@ArtistDetailActivity).resetCustomArtistImage(artist) CustomArtistImageUtil.getInstance(this@ArtistDetailActivity).resetCustomArtistImage(artist)
forceDownload = true forceDownload = true
return true return true

View file

@ -23,8 +23,11 @@ import code.name.monkey.retromusic.util.DensityUtil
import code.name.monkey.retromusic.util.RetroColorUtil import code.name.monkey.retromusic.util.RetroColorUtil
import code.name.monkey.retromusic.util.ViewUtil import code.name.monkey.retromusic.util.ViewUtil
import com.afollestad.materialcab.MaterialCab import com.afollestad.materialcab.MaterialCab
import kotlinx.android.synthetic.main.activity_playlist_detail.* import kotlinx.android.synthetic.main.activity_playlist_detail.empty
import java.util.* import kotlinx.android.synthetic.main.activity_playlist_detail.emptyEmoji
import kotlinx.android.synthetic.main.activity_playlist_detail.recyclerView
import kotlinx.android.synthetic.main.activity_playlist_detail.toolbar
import java.util.ArrayList
import javax.inject.Inject import javax.inject.Inject
/** /**
@ -75,7 +78,6 @@ class GenreDetailsActivity : AbsSlidingMusicPanelActivity(), CabHolder, GenreDet
App.musicComponent.inject(this) App.musicComponent.inject(this)
genreDetailsPresenter.attachView(this) genreDetailsPresenter.attachView(this)
} }
private fun setUpToolBar() { private fun setUpToolBar() {
@ -98,7 +100,6 @@ class GenreDetailsActivity : AbsSlidingMusicPanelActivity(), CabHolder, GenreDet
} }
override fun showEmptyView() { override fun showEmptyView() {
} }
override fun onCreateOptionsMenu(menu: Menu): Boolean { override fun onCreateOptionsMenu(menu: Menu): Boolean {
@ -135,7 +136,15 @@ class GenreDetailsActivity : AbsSlidingMusicPanelActivity(), CabHolder, GenreDet
override fun openCab(menuRes: Int, callback: MaterialCab.Callback): MaterialCab { override fun openCab(menuRes: Int, callback: MaterialCab.Callback): MaterialCab {
if (cab != null && cab!!.isActive) cab?.finish() 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(ATHUtil.resolveColor(this, R.attr.colorSurface))).start(callback) cab = MaterialCab(this, R.id.cab_stub).setMenu(menuRes).setCloseDrawableRes(R.drawable.ic_close_white_24dp)
.setBackgroundColor(
RetroColorUtil.shiftBackgroundColorForLightText(
ATHUtil.resolveColor(
this,
R.attr.colorSurface
)
)
).start(callback)
return cab!! return cab!!
} }

View file

@ -14,47 +14,30 @@
package code.name.monkey.retromusic.activities; package code.name.monkey.retromusic.activities;
import static code.name.monkey.appthemehelper.util.ATHUtil.INSTANCE;
import android.graphics.Color; import android.graphics.Color;
import android.os.Bundle; import android.os.Bundle;
import android.view.MenuItem; import android.view.MenuItem;
import android.webkit.WebView; import android.webkit.WebView;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.appcompat.widget.Toolbar; import androidx.appcompat.widget.Toolbar;
import org.jetbrains.annotations.Nullable;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import code.name.monkey.appthemehelper.ThemeStore; import code.name.monkey.appthemehelper.ThemeStore;
import code.name.monkey.appthemehelper.util.ColorUtil; import code.name.monkey.appthemehelper.util.ColorUtil;
import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper; import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper;
import code.name.monkey.retromusic.R; import code.name.monkey.retromusic.R;
import code.name.monkey.retromusic.activities.base.AbsBaseActivity; import code.name.monkey.retromusic.activities.base.AbsBaseActivity;
import java.io.BufferedReader;
import static code.name.monkey.appthemehelper.util.ATHUtil.INSTANCE; import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import org.jetbrains.annotations.Nullable;
/** /**
* Created by hemanths on 2019-09-27. * Created by hemanths on 2019-09-27.
*/ */
public class LicenseActivity extends AbsBaseActivity { public class LicenseActivity extends AbsBaseActivity {
@Override
public boolean onOptionsItemSelected(@NonNull MenuItem item) {
if (item.getItemId() == android.R.id.home) {
onBackPressed();
return true;
}
return super.onOptionsItemSelected(item);
}
private String colorToCSS(int color) {
return String.format("rgb(%d, %d, %d)", Color.red(color), Color.green(color), Color.blue(color)); // on API 29, WebView doesn't load with hex colors
}
@Override @Override
protected void onCreate(@Nullable Bundle savedInstanceState) { protected void onCreate(@Nullable Bundle savedInstanceState) {
setDrawUnderStatusBar(); setDrawUnderStatusBar();
@ -73,19 +56,22 @@ public class LicenseActivity extends AbsBaseActivity {
InputStream json = getAssets().open("index.html"); InputStream json = getAssets().open("index.html");
BufferedReader in = new BufferedReader(new InputStreamReader(json, StandardCharsets.UTF_8)); BufferedReader in = new BufferedReader(new InputStreamReader(json, StandardCharsets.UTF_8));
String str; String str;
while ((str = in.readLine()) != null) while ((str = in.readLine()) != null) {
buf.append(str); buf.append(str);
}
in.close(); in.close();
// Inject color values for WebView body background and links // Inject color values for WebView body background and links
final boolean isDark = INSTANCE.isWindowBackgroundDark(this); final boolean isDark = INSTANCE.isWindowBackgroundDark(this);
final String backgroundColor = colorToCSS(INSTANCE.resolveColor(this, R.attr.colorSurface, Color.parseColor(isDark ? "#424242" : "#ffffff"))); final String backgroundColor = colorToCSS(INSTANCE.resolveColor(this, R.attr.colorSurface,
Color.parseColor(isDark ? "#424242" : "#ffffff")));
final String contentColor = colorToCSS(Color.parseColor(isDark ? "#ffffff" : "#000000")); final String contentColor = colorToCSS(Color.parseColor(isDark ? "#ffffff" : "#000000"));
final String changeLog = buf.toString() final String changeLog = buf.toString()
.replace("{style-placeholder}", .replace("{style-placeholder}",
String.format("body { background-color: %s; color: %s; }", backgroundColor, contentColor)) String.format("body { background-color: %s; color: %s; }", backgroundColor, contentColor))
.replace("{link-color}", colorToCSS(ThemeStore.Companion.accentColor(this))) .replace("{link-color}", colorToCSS(ThemeStore.Companion.accentColor(this)))
.replace("{link-color-active}", colorToCSS(ColorUtil.INSTANCE.lightenColor(ThemeStore.Companion.accentColor(this)))); .replace("{link-color-active}",
colorToCSS(ColorUtil.INSTANCE.lightenColor(ThemeStore.Companion.accentColor(this))));
webView.loadData(changeLog, "text/html", "UTF-8"); webView.loadData(changeLog, "text/html", "UTF-8");
} catch (Throwable e) { } catch (Throwable e) {
@ -93,4 +79,18 @@ public class LicenseActivity extends AbsBaseActivity {
} }
} }
@Override
public boolean onOptionsItemSelected(@NonNull MenuItem item) {
if (item.getItemId() == android.R.id.home) {
onBackPressed();
return true;
}
return super.onOptionsItemSelected(item);
}
private String colorToCSS(int color) {
return String.format("rgb(%d, %d, %d)", Color.red(color), Color.green(color),
Color.blue(color)); // on API 29, WebView doesn't load with hex colors
}
} }

View file

@ -18,79 +18,79 @@ import com.r0adkll.slidr.Slidr
import com.r0adkll.slidr.model.SlidrConfig import com.r0adkll.slidr.model.SlidrConfig
import com.r0adkll.slidr.model.SlidrListener import com.r0adkll.slidr.model.SlidrListener
import com.r0adkll.slidr.model.SlidrPosition import com.r0adkll.slidr.model.SlidrPosition
import kotlinx.android.synthetic.main.activity_lock_screen.* import kotlinx.android.synthetic.main.activity_lock_screen.image
class LockScreenActivity : AbsMusicServiceActivity() { class LockScreenActivity : AbsMusicServiceActivity() {
private var fragment: LockScreenPlayerControlsFragment? = null private var fragment: LockScreenPlayerControlsFragment? = null
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O_MR1) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O_MR1) {
setShowWhenLocked(true) setShowWhenLocked(true)
setTurnScreenOn(true) setTurnScreenOn(true)
} else { } else {
this.window.addFlags(WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD or WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED or WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON) this.window.addFlags(WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD or
} WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED or
setDrawUnderStatusBar() WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON)
setContentView(R.layout.activity_lock_screen) }
hideStatusBar() setDrawUnderStatusBar()
setStatusbarColorAuto() setContentView(R.layout.activity_lock_screen)
setNavigationbarColorAuto() hideStatusBar()
setTaskDescriptionColorAuto() setStatusbarColorAuto()
setLightNavigationBar(true) setNavigationbarColorAuto()
setTaskDescriptionColorAuto()
setLightNavigationBar(true)
val config = SlidrConfig.Builder().listener(object : SlidrListener { val config = SlidrConfig.Builder().listener(object : SlidrListener {
override fun onSlideStateChanged(state: Int) { override fun onSlideStateChanged(state: Int) {
}
} override fun onSlideChange(percent: Float) {
}
override fun onSlideChange(percent: Float) { override fun onSlideOpened() {
}
} override fun onSlideClosed(): Boolean {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val keyguardManager = getSystemService(Context.KEYGUARD_SERVICE) as KeyguardManager
keyguardManager.requestDismissKeyguard(this@LockScreenActivity, null)
}
finish()
return true
}
}).position(SlidrPosition.BOTTOM).build()
override fun onSlideOpened() { Slidr.attach(this, config)
} fragment =
supportFragmentManager.findFragmentById(R.id.playback_controls_fragment) as LockScreenPlayerControlsFragment?
override fun onSlideClosed(): Boolean { findViewById<View>(R.id.slide).apply {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { translationY = 100f
val keyguardManager = getSystemService(Context.KEYGUARD_SERVICE) as KeyguardManager alpha = 0f
keyguardManager.requestDismissKeyguard(this@LockScreenActivity, null) ViewCompat.animate(this).translationY(0f).alpha(1f).setDuration(1500).start()
} }
finish() }
return true
}
}).position(SlidrPosition.BOTTOM).build()
Slidr.attach(this, config) override fun onPlayingMetaChanged() {
super.onPlayingMetaChanged()
updateSongs()
}
fragment = supportFragmentManager.findFragmentById(R.id.playback_controls_fragment) as LockScreenPlayerControlsFragment? override fun onServiceConnected() {
super.onServiceConnected()
updateSongs()
}
findViewById<View>(R.id.slide).apply { private fun updateSongs() {
translationY = 100f val song = MusicPlayerRemote.currentSong
alpha = 0f SongGlideRequest.Builder.from(Glide.with(this), song).checkIgnoreMediaStore(this)
ViewCompat.animate(this).translationY(0f).alpha(1f).setDuration(1500).start() .generatePalette(this).build().dontAnimate()
} .into(object : RetroMusicColoredTarget(image) {
} override fun onColorReady(color: Int) {
fragment?.setDark(color)
override fun onPlayingMetaChanged() { }
super.onPlayingMetaChanged() })
updateSongs() }
}
override fun onServiceConnected() {
super.onServiceConnected()
updateSongs()
}
private fun updateSongs() {
val song = MusicPlayerRemote.currentSong
SongGlideRequest.Builder.from(Glide.with(this), song).checkIgnoreMediaStore(this)
.generatePalette(this).build().dontAnimate()
.into(object : RetroMusicColoredTarget(image) {
override fun onColorReady(color: Int) {
fragment?.setDark(color)
}
})
}
} }

View file

@ -7,7 +7,11 @@ import android.os.Build
import android.os.Bundle import android.os.Bundle
import android.text.InputType import android.text.InputType
import android.text.TextUtils import android.text.TextUtils
import android.view.* import android.view.LayoutInflater
import android.view.MenuItem
import android.view.View
import android.view.ViewGroup
import android.view.WindowManager
import androidx.annotation.StringRes import androidx.annotation.StringRes
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
@ -15,7 +19,12 @@ import androidx.fragment.app.FragmentManager
import androidx.fragment.app.FragmentStatePagerAdapter import androidx.fragment.app.FragmentStatePagerAdapter
import androidx.viewpager.widget.ViewPager import androidx.viewpager.widget.ViewPager
import code.name.monkey.appthemehelper.ThemeStore import code.name.monkey.appthemehelper.ThemeStore
import code.name.monkey.appthemehelper.util.* import code.name.monkey.appthemehelper.util.ATHUtil
import code.name.monkey.appthemehelper.util.ColorUtil
import code.name.monkey.appthemehelper.util.MaterialUtil
import code.name.monkey.appthemehelper.util.MaterialValueHelper
import code.name.monkey.appthemehelper.util.TintHelper
import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper
import code.name.monkey.retromusic.App import code.name.monkey.retromusic.App
import code.name.monkey.retromusic.R import code.name.monkey.retromusic.R
import code.name.monkey.retromusic.activities.base.AbsMusicServiceActivity import code.name.monkey.retromusic.activities.base.AbsMusicServiceActivity
@ -36,13 +45,15 @@ import com.afollestad.materialdialogs.MaterialDialog
import com.afollestad.materialdialogs.bottomsheets.BottomSheet import com.afollestad.materialdialogs.bottomsheets.BottomSheet
import com.afollestad.materialdialogs.input.getInputLayout import com.afollestad.materialdialogs.input.getInputLayout
import com.afollestad.materialdialogs.input.input import com.afollestad.materialdialogs.input.input
import kotlinx.android.synthetic.main.activity_lyrics.* import kotlinx.android.synthetic.main.activity_lyrics.fab
import kotlinx.android.synthetic.main.fragment_lyrics.* import kotlinx.android.synthetic.main.activity_lyrics.tabs
import kotlinx.android.synthetic.main.fragment_synced.* import kotlinx.android.synthetic.main.activity_lyrics.toolbar
import kotlinx.android.synthetic.main.activity_lyrics.viewPager
import kotlinx.android.synthetic.main.fragment_lyrics.offlineLyrics
import kotlinx.android.synthetic.main.fragment_synced.lyricsView
import org.jaudiotagger.tag.FieldKey import org.jaudiotagger.tag.FieldKey
import java.io.File import java.io.File
import java.util.* import java.util.EnumMap
import kotlin.collections.ArrayList
class LyricsActivity : AbsMusicServiceActivity(), View.OnClickListener, ViewPager.OnPageChangeListener { class LyricsActivity : AbsMusicServiceActivity(), View.OnClickListener, ViewPager.OnPageChangeListener {
override fun onPageScrollStateChanged(state: Int) { override fun onPageScrollStateChanged(state: Int) {
@ -89,11 +100,16 @@ class LyricsActivity : AbsMusicServiceActivity(), View.OnClickListener, ViewPage
setNavigationbarColorAuto() setNavigationbarColorAuto()
fab.backgroundTintList = ColorStateList.valueOf(ThemeStore.accentColor(this)) fab.backgroundTintList = ColorStateList.valueOf(ThemeStore.accentColor(this))
ColorStateList.valueOf(MaterialValueHelper.getPrimaryTextColor(this, ColorUtil.isColorLight(ThemeStore.accentColor(this)))) ColorStateList.valueOf(
.apply { MaterialValueHelper.getPrimaryTextColor(
fab.setTextColor(this) this,
fab.iconTint = this ColorUtil.isColorLight(ThemeStore.accentColor(this))
} )
)
.apply {
fab.setTextColor(this)
fab.iconTint = this
}
setupWakelock() setupWakelock()
viewPager.apply { viewPager.apply {
@ -108,8 +124,18 @@ class LyricsActivity : AbsMusicServiceActivity(), View.OnClickListener, ViewPage
ToolbarContentTintHelper.colorBackButton(toolbar) ToolbarContentTintHelper.colorBackButton(toolbar)
setSupportActionBar(toolbar) setSupportActionBar(toolbar)
tabs.setupWithViewPager(viewPager) tabs.setupWithViewPager(viewPager)
tabs.setSelectedTabIndicator(TintHelper.createTintedDrawable(ContextCompat.getDrawable(this, R.drawable.tab_indicator), ThemeStore.accentColor(this))) tabs.setSelectedTabIndicator(
tabs.setTabTextColors(ATHUtil.resolveColor(this, android.R.attr.textColorSecondary), ThemeStore.accentColor(this)) TintHelper.createTintedDrawable(
ContextCompat.getDrawable(
this,
R.drawable.tab_indicator
), ThemeStore.accentColor(this)
)
)
tabs.setTabTextColors(
ATHUtil.resolveColor(this, android.R.attr.textColorSecondary),
ThemeStore.accentColor(this)
)
tabs.setSelectedTabIndicatorColor(ThemeStore.accentColor(this)) tabs.setSelectedTabIndicatorColor(ThemeStore.accentColor(this))
fab.setOnClickListener(this) fab.setOnClickListener(this)
@ -152,29 +178,30 @@ class LyricsActivity : AbsMusicServiceActivity(), View.OnClickListener, ViewPage
} }
val materialDialog = MaterialDialog(this, BottomSheet(LayoutMode.WRAP_CONTENT)) val materialDialog = MaterialDialog(this, BottomSheet(LayoutMode.WRAP_CONTENT))
.show { .show {
cornerRadius(PreferenceUtil.getInstance(this@LyricsActivity).dialogCorner) cornerRadius(PreferenceUtil.getInstance(this@LyricsActivity).dialogCorner)
title(R.string.add_time_framed_lryics) title(R.string.add_time_framed_lryics)
negativeButton(R.string.action_search) { negativeButton(R.string.action_search) {
RetroUtil.openUrl(this@LyricsActivity, googleSearchLrcUrl) 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()
}
} }
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()
}
}
MaterialUtil.setTint(materialDialog.getInputLayout(), false) MaterialUtil.setTint(materialDialog.getInputLayout(), false)
} }
private fun updateSong() { private fun updateSong() {
val page = supportFragmentManager.findFragmentByTag("android:switcher:" + R.id.viewPager + ":" + viewPager.currentItem) val page =
supportFragmentManager.findFragmentByTag("android:switcher:" + R.id.viewPager + ":" + viewPager.currentItem)
if (viewPager.currentItem == 0 && page != null) { if (viewPager.currentItem == 0 && page != null) {
(page as BaseLyricsFragment).upDateSong() (page as BaseLyricsFragment).upDateSong()
} }
@ -188,7 +215,7 @@ class LyricsActivity : AbsMusicServiceActivity(), View.OnClickListener, ViewPage
} }
val materialDialog = MaterialDialog( val materialDialog = MaterialDialog(
this, BottomSheet(LayoutMode.WRAP_CONTENT) this, BottomSheet(LayoutMode.WRAP_CONTENT)
).show { ).show {
cornerRadius(PreferenceUtil.getInstance(this@LyricsActivity).dialogCorner) cornerRadius(PreferenceUtil.getInstance(this@LyricsActivity).dialogCorner)
title(R.string.add_lyrics) title(R.string.add_lyrics)
@ -196,16 +223,16 @@ class LyricsActivity : AbsMusicServiceActivity(), View.OnClickListener, ViewPage
RetroUtil.openUrl(this@LyricsActivity, getGoogleSearchUrl()) RetroUtil.openUrl(this@LyricsActivity, getGoogleSearchUrl())
} }
input( input(
hint = getString(R.string.paste_lyrics_here), hint = getString(R.string.paste_lyrics_here),
prefill = content, prefill = content,
inputType = InputType.TYPE_CLASS_TEXT or InputType.TYPE_TEXT_FLAG_MULTI_LINE inputType = InputType.TYPE_CLASS_TEXT or InputType.TYPE_TEXT_FLAG_MULTI_LINE
) { _, input -> ) { _, input ->
val fieldKeyValueMap = EnumMap<FieldKey, String>(FieldKey::class.java) val fieldKeyValueMap = EnumMap<FieldKey, String>(FieldKey::class.java)
fieldKeyValueMap[FieldKey.LYRICS] = input.toString() fieldKeyValueMap[FieldKey.LYRICS] = input.toString()
WriteTagsAsyncTask(this@LyricsActivity).execute( WriteTagsAsyncTask(this@LyricsActivity).execute(
WriteTagsAsyncTask.LoadingInfo( WriteTagsAsyncTask.LoadingInfo(
getSongPaths(song), fieldKeyValueMap, null getSongPaths(song), fieldKeyValueMap, null
) )
) )
} }
positiveButton(android.R.string.ok) { positiveButton(android.R.string.ok) {
@ -230,10 +257,11 @@ class LyricsActivity : AbsMusicServiceActivity(), View.OnClickListener, ViewPage
} }
class PagerAdapter(fm: FragmentManager) : FragmentStatePagerAdapter( class PagerAdapter(fm: FragmentManager) : FragmentStatePagerAdapter(
fm, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT fm, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT
) { ) {
class Tabs( class Tabs(
@StringRes val title: Int, val fragment: Fragment @StringRes val title: Int, val fragment: Fragment
) )
private var tabs = ArrayList<Tabs>() private var tabs = ArrayList<Tabs>()
@ -256,7 +284,6 @@ class LyricsActivity : AbsMusicServiceActivity(), View.OnClickListener, ViewPage
override fun getCount(): Int { override fun getCount(): Int {
return tabs.size return tabs.size
} }
} }
abstract class BaseLyricsFragment : AbsMusicServiceFragment() { abstract class BaseLyricsFragment : AbsMusicServiceFragment() {
@ -271,7 +298,6 @@ class LyricsActivity : AbsMusicServiceActivity(), View.OnClickListener, ViewPage
super.onServiceConnected() super.onServiceConnected()
upDateSong() upDateSong()
} }
} }
class OfflineLyricsFragment : BaseLyricsFragment() { class OfflineLyricsFragment : BaseLyricsFragment() {
@ -331,7 +357,7 @@ class LyricsActivity : AbsMusicServiceActivity(), View.OnClickListener, ViewPage
} }
override fun onCreateView( override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?
): View? { ): View? {
return inflater.inflate(R.layout.fragment_lyrics, container, false) return inflater.inflate(R.layout.fragment_lyrics, container, false)
} }
@ -344,7 +370,7 @@ class LyricsActivity : AbsMusicServiceActivity(), View.OnClickListener, ViewPage
private lateinit var updateHelper: MusicProgressViewUpdateHelper private lateinit var updateHelper: MusicProgressViewUpdateHelper
override fun onCreateView( override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?
): View? { ): View? {
return inflater.inflate(R.layout.fragment_synced, container, false) return inflater.inflate(R.layout.fragment_synced, container, false)
} }

View file

@ -1,6 +1,10 @@
package code.name.monkey.retromusic.activities package code.name.monkey.retromusic.activities
import android.content.* import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.content.SharedPreferences
import android.os.Bundle import android.os.Bundle
import android.provider.MediaStore import android.provider.MediaStore
import android.util.Log import android.util.Log
@ -22,7 +26,7 @@ import code.name.monkey.retromusic.service.MusicService
import code.name.monkey.retromusic.util.AppRater import code.name.monkey.retromusic.util.AppRater
import code.name.monkey.retromusic.util.PreferenceUtil import code.name.monkey.retromusic.util.PreferenceUtil
import io.reactivex.disposables.CompositeDisposable import io.reactivex.disposables.CompositeDisposable
import java.util.* import java.util.ArrayList
class MainActivity : AbsSlidingMusicPanelActivity(), SharedPreferences.OnSharedPreferenceChangeListener { class MainActivity : AbsSlidingMusicPanelActivity(), SharedPreferences.OnSharedPreferenceChangeListener {
@ -49,7 +53,7 @@ class MainActivity : AbsSlidingMusicPanelActivity(), SharedPreferences.OnSharedP
} }
override fun onCreate( override fun onCreate(
savedInstanceState: Bundle? savedInstanceState: Bundle?
) { ) {
setDrawUnderStatusBar() setDrawUnderStatusBar()
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
@ -76,14 +80,13 @@ class MainActivity : AbsSlidingMusicPanelActivity(), SharedPreferences.OnSharedP
val currentVersion = pInfo.versionCode val currentVersion = pInfo.versionCode
if (currentVersion != PreferenceUtil.getInstance(this).lastChangelogVersion) { if (currentVersion != PreferenceUtil.getInstance(this).lastChangelogVersion) {
startActivityForResult( startActivityForResult(
Intent(this, WhatsNewActivity::class.java), Intent(this, WhatsNewActivity::class.java),
APP_INTRO_REQUEST APP_INTRO_REQUEST
) )
} }
} catch (e: Throwable) { } catch (e: Throwable) {
e.printStackTrace() e.printStackTrace()
} }
} }
override fun onResume() { override fun onResume() {
@ -112,13 +115,14 @@ class MainActivity : AbsSlidingMusicPanelActivity(), SharedPreferences.OnSharedP
private fun setCurrentFragment(fragment: Fragment, tag: String) { private fun setCurrentFragment(fragment: Fragment, tag: String) {
if (tag != supportFragmentManager.findFragmentById(R.id.fragment_container)?.tag) { if (tag != supportFragmentManager.findFragmentById(R.id.fragment_container)?.tag) {
supportFragmentManager.beginTransaction() supportFragmentManager.beginTransaction()
.replace(R.id.fragment_container, fragment, tag).commit() .replace(R.id.fragment_container, fragment, tag).commit()
currentFragment = fragment as MainActivityFragmentCallbacks currentFragment = fragment as MainActivityFragmentCallbacks
} }
} }
private fun restoreCurrentFragment() { private fun restoreCurrentFragment() {
currentFragment = supportFragmentManager.findFragmentById(R.id.fragment_container) as MainActivityFragmentCallbacks currentFragment =
supportFragmentManager.findFragmentById(R.id.fragment_container) as MainActivityFragmentCallbacks
} }
private fun handlePlaybackIntent(intent: Intent?) { private fun handlePlaybackIntent(intent: Intent?) {
@ -200,7 +204,6 @@ class MainActivity : AbsSlidingMusicPanelActivity(), SharedPreferences.OnSharedP
} }
} }
} }
} }
override fun handleBackPress(): Boolean { override fun handleBackPress(): Boolean {
@ -219,8 +222,8 @@ class MainActivity : AbsSlidingMusicPanelActivity(), SharedPreferences.OnSharedP
} }
override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences, key: String) { override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences, key: String) {
if (key == PreferenceUtil.GENERAL_THEME || key == PreferenceUtil.BLACK_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.DESATURATED_COLOR || key == PreferenceUtil.TAB_TEXT_MODE || key == PreferenceUtil.LIBRARY_CATEGORIES) postRecreate() if (key == PreferenceUtil.GENERAL_THEME || key == PreferenceUtil.BLACK_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.DESATURATED_COLOR || key == PreferenceUtil.TAB_TEXT_MODE || key == PreferenceUtil.LIBRARY_CATEGORIES
) postRecreate()
} }
private fun selectedFragment(itemId: Int) { private fun selectedFragment(itemId: Int) {

View file

@ -20,7 +20,10 @@ import com.h6ah4i.android.widget.advrecyclerview.draggable.RecyclerViewDragDropM
import com.h6ah4i.android.widget.advrecyclerview.swipeable.RecyclerViewSwipeManager import com.h6ah4i.android.widget.advrecyclerview.swipeable.RecyclerViewSwipeManager
import com.h6ah4i.android.widget.advrecyclerview.touchguard.RecyclerViewTouchActionGuardManager import com.h6ah4i.android.widget.advrecyclerview.touchguard.RecyclerViewTouchActionGuardManager
import com.h6ah4i.android.widget.advrecyclerview.utils.WrapperAdapterUtils import com.h6ah4i.android.widget.advrecyclerview.utils.WrapperAdapterUtils
import kotlinx.android.synthetic.main.activity_playing_queue.* import kotlinx.android.synthetic.main.activity_playing_queue.clearQueue
import kotlinx.android.synthetic.main.activity_playing_queue.playerQueueSubHeader
import kotlinx.android.synthetic.main.activity_playing_queue.recyclerView
import kotlinx.android.synthetic.main.activity_playing_queue.toolbar
open class PlayingQueueActivity : AbsMusicServiceActivity() { open class PlayingQueueActivity : AbsMusicServiceActivity() {
@ -33,7 +36,10 @@ open class PlayingQueueActivity : AbsMusicServiceActivity() {
private fun getUpNextAndQueueTime(): String { private fun getUpNextAndQueueTime(): String {
val duration = MusicPlayerRemote.getQueueDurationMillis(MusicPlayerRemote.position) val duration = MusicPlayerRemote.getQueueDurationMillis(MusicPlayerRemote.position)
return MusicUtil.buildInfoString(resources.getString(R.string.up_next), MusicUtil.getReadableDurationString(duration)) return MusicUtil.buildInfoString(
resources.getString(R.string.up_next),
MusicUtil.getReadableDurationString(duration)
)
} }
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
@ -73,10 +79,10 @@ open class PlayingQueueActivity : AbsMusicServiceActivity() {
animator.supportsChangeAnimations = false animator.supportsChangeAnimations = false
playingQueueAdapter = PlayingQueueAdapter( playingQueueAdapter = PlayingQueueAdapter(
this, this,
MusicPlayerRemote.playingQueue, MusicPlayerRemote.playingQueue,
MusicPlayerRemote.position, MusicPlayerRemote.position,
R.layout.item_queue R.layout.item_queue
) )
wrappedAdapter = recyclerViewDragDropManager?.createWrappedAdapter(playingQueueAdapter!!) wrappedAdapter = recyclerViewDragDropManager?.createWrappedAdapter(playingQueueAdapter!!)
wrappedAdapter = wrappedAdapter?.let { recyclerViewSwipeManager?.createWrappedAdapter(it) } wrappedAdapter = wrappedAdapter?.let { recyclerViewSwipeManager?.createWrappedAdapter(it) }
@ -105,7 +111,6 @@ open class PlayingQueueActivity : AbsMusicServiceActivity() {
} }
private fun checkForPadding() { private fun checkForPadding() {
} }
override fun onQueueChanged() { override fun onQueueChanged() {
@ -178,14 +183,14 @@ open class PlayingQueueActivity : AbsMusicServiceActivity() {
applyToolbar(toolbar) applyToolbar(toolbar)
clearQueue.backgroundTintList = ColorStateList.valueOf(ThemeStore.accentColor(this)) clearQueue.backgroundTintList = ColorStateList.valueOf(ThemeStore.accentColor(this))
ColorStateList.valueOf( ColorStateList.valueOf(
MaterialValueHelper.getPrimaryTextColor( MaterialValueHelper.getPrimaryTextColor(
this, this,
ColorUtil.isColorLight( ColorUtil.isColorLight(
ThemeStore.accentColor( ThemeStore.accentColor(
this this
) )
)
) )
)
).apply { ).apply {
clearQueue.setTextColor(this) clearQueue.setTextColor(this)
clearQueue.iconTint = this clearQueue.iconTint = this

View file

@ -29,7 +29,11 @@ import com.afollestad.materialcab.MaterialCab
import com.h6ah4i.android.widget.advrecyclerview.animator.RefactoredDefaultItemAnimator import com.h6ah4i.android.widget.advrecyclerview.animator.RefactoredDefaultItemAnimator
import com.h6ah4i.android.widget.advrecyclerview.draggable.RecyclerViewDragDropManager import com.h6ah4i.android.widget.advrecyclerview.draggable.RecyclerViewDragDropManager
import com.h6ah4i.android.widget.advrecyclerview.utils.WrapperAdapterUtils import com.h6ah4i.android.widget.advrecyclerview.utils.WrapperAdapterUtils
import kotlinx.android.synthetic.main.activity_playlist_detail.* import kotlinx.android.synthetic.main.activity_playlist_detail.empty
import kotlinx.android.synthetic.main.activity_playlist_detail.emptyEmoji
import kotlinx.android.synthetic.main.activity_playlist_detail.emptyText
import kotlinx.android.synthetic.main.activity_playlist_detail.recyclerView
import kotlinx.android.synthetic.main.activity_playlist_detail.toolbar
import javax.inject.Inject import javax.inject.Inject
class PlaylistDetailActivity : AbsSlidingMusicPanelActivity(), CabHolder, PlaylistSongsView { class PlaylistDetailActivity : AbsSlidingMusicPanelActivity(), CabHolder, PlaylistSongsView {
@ -79,19 +83,25 @@ class PlaylistDetailActivity : AbsSlidingMusicPanelActivity(), CabHolder, Playli
recyclerViewDragDropManager = RecyclerViewDragDropManager() recyclerViewDragDropManager = RecyclerViewDragDropManager()
val animator = RefactoredDefaultItemAnimator() val animator = RefactoredDefaultItemAnimator()
adapter = OrderablePlaylistSongAdapter(this, adapter = OrderablePlaylistSongAdapter(this,
ArrayList(), ArrayList(),
R.layout.item_list, R.layout.item_list,
false, false,
this, this,
object : OrderablePlaylistSongAdapter.OnMoveItemListener { object : OrderablePlaylistSongAdapter.OnMoveItemListener {
override fun onMoveItem(fromPosition: Int, toPosition: Int) { override fun onMoveItem(fromPosition: Int, toPosition: Int) {
if (PlaylistsUtil.moveItem(this@PlaylistDetailActivity, playlist.id, fromPosition, toPosition)) { if (PlaylistsUtil.moveItem(
val song = adapter.dataSet.removeAt(fromPosition) this@PlaylistDetailActivity,
adapter.dataSet.add(toPosition, song) playlist.id,
adapter.notifyItemMoved(fromPosition, toPosition) fromPosition,
} toPosition
)
) {
val song = adapter.dataSet.removeAt(fromPosition)
adapter.dataSet.add(toPosition, song)
adapter.notifyItemMoved(fromPosition, toPosition)
} }
}) }
})
wrappedAdapter = recyclerViewDragDropManager!!.createWrappedAdapter(adapter) wrappedAdapter = recyclerViewDragDropManager!!.createWrappedAdapter(adapter)
recyclerView.adapter = wrappedAdapter recyclerView.adapter = wrappedAdapter
@ -120,8 +130,8 @@ class PlaylistDetailActivity : AbsSlidingMusicPanelActivity(), CabHolder, Playli
override fun onCreateOptionsMenu(menu: Menu): Boolean { override fun onCreateOptionsMenu(menu: Menu): Boolean {
menuInflater.inflate( menuInflater.inflate(
if (playlist is AbsCustomPlaylist) R.menu.menu_smart_playlist_detail if (playlist is AbsCustomPlaylist) R.menu.menu_smart_playlist_detail
else R.menu.menu_playlist_detail, menu else R.menu.menu_playlist_detail, menu
) )
return super.onCreateOptionsMenu(menu) return super.onCreateOptionsMenu(menu)
} }
@ -140,7 +150,15 @@ class PlaylistDetailActivity : AbsSlidingMusicPanelActivity(), CabHolder, Playli
if (cab != null && cab!!.isActive) { if (cab != null && cab!!.isActive) {
cab!!.finish() cab!!.finish()
} }
cab = MaterialCab(this, R.id.cab_stub).setMenu(menuRes).setCloseDrawableRes(R.drawable.ic_close_white_24dp).setBackgroundColor(RetroColorUtil.shiftBackgroundColorForLightText(ATHUtil.resolveColor(this, R.attr.colorSurface))).start(callback) cab = MaterialCab(this, R.id.cab_stub).setMenu(menuRes).setCloseDrawableRes(R.drawable.ic_close_white_24dp)
.setBackgroundColor(
RetroColorUtil.shiftBackgroundColorForLightText(
ATHUtil.resolveColor(
this,
R.attr.colorSurface
)
)
).start(callback)
return cab!! return cab!!
} }

View file

@ -14,140 +14,143 @@ import code.name.monkey.retromusic.activities.base.AbsBaseActivity
import code.name.monkey.retromusic.extensions.applyToolbar import code.name.monkey.retromusic.extensions.applyToolbar
import com.anjlab.android.iab.v3.BillingProcessor import com.anjlab.android.iab.v3.BillingProcessor
import com.anjlab.android.iab.v3.TransactionDetails import com.anjlab.android.iab.v3.TransactionDetails
import kotlinx.android.synthetic.main.activity_pro_version.* import kotlinx.android.synthetic.main.activity_pro_version.purchaseButton
import kotlinx.android.synthetic.main.activity_pro_version.restoreButton
import kotlinx.android.synthetic.main.activity_pro_version.toolbar
import java.lang.ref.WeakReference import java.lang.ref.WeakReference
class PurchaseActivity : AbsBaseActivity(), BillingProcessor.IBillingHandler { class PurchaseActivity : AbsBaseActivity(), BillingProcessor.IBillingHandler {
private lateinit var billingProcessor: BillingProcessor private lateinit var billingProcessor: BillingProcessor
private var restorePurchaseAsyncTask: AsyncTask<*, *, *>? = null private var restorePurchaseAsyncTask: AsyncTask<*, *, *>? = null
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
setDrawUnderStatusBar() setDrawUnderStatusBar()
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
setContentView(R.layout.activity_pro_version) setContentView(R.layout.activity_pro_version)
setStatusbarColorAuto() setStatusbarColorAuto()
setNavigationbarColorAuto() setNavigationbarColorAuto()
setLightNavigationBar(true) setLightNavigationBar(true)
applyToolbar(toolbar) applyToolbar(toolbar)
restoreButton.isEnabled = false restoreButton.isEnabled = false
purchaseButton.isEnabled = false purchaseButton.isEnabled = false
billingProcessor = BillingProcessor(this, BuildConfig.GOOGLE_PLAY_LICENSING_KEY, this) billingProcessor = BillingProcessor(this, BuildConfig.GOOGLE_PLAY_LICENSING_KEY, this)
MaterialUtil.setTint(restoreButton, false) MaterialUtil.setTint(restoreButton, false)
MaterialUtil.setTint(purchaseButton, true) MaterialUtil.setTint(purchaseButton, true)
restoreButton.setOnClickListener { restoreButton.setOnClickListener {
if (restorePurchaseAsyncTask == null || restorePurchaseAsyncTask!!.status != AsyncTask.Status.RUNNING) { if (restorePurchaseAsyncTask == null || restorePurchaseAsyncTask!!.status != AsyncTask.Status.RUNNING) {
restorePurchase() restorePurchase()
} }
} }
purchaseButton.setOnClickListener { purchaseButton.setOnClickListener {
billingProcessor.purchase(this@PurchaseActivity, App.PRO_VERSION_PRODUCT_ID) billingProcessor.purchase(this@PurchaseActivity, App.PRO_VERSION_PRODUCT_ID)
} }
} }
private fun restorePurchase() { private fun restorePurchase() {
if (restorePurchaseAsyncTask != null) { if (restorePurchaseAsyncTask != null) {
restorePurchaseAsyncTask!!.cancel(false) restorePurchaseAsyncTask!!.cancel(false)
} }
restorePurchaseAsyncTask = RestorePurchaseAsyncTask(this).execute() restorePurchaseAsyncTask = RestorePurchaseAsyncTask(this).execute()
} }
override fun onProductPurchased(productId: String, details: TransactionDetails?) { override fun onProductPurchased(productId: String, details: TransactionDetails?) {
Toast.makeText(this, R.string.thank_you, Toast.LENGTH_SHORT).show() Toast.makeText(this, R.string.thank_you, Toast.LENGTH_SHORT).show()
setResult(RESULT_OK) setResult(RESULT_OK)
} }
override fun onPurchaseHistoryRestored() { override fun onPurchaseHistoryRestored() {
if (App.isProVersion()) { if (App.isProVersion()) {
Toast.makeText( Toast.makeText(
this, this,
R.string.restored_previous_purchase_please_restart, R.string.restored_previous_purchase_please_restart,
Toast.LENGTH_LONG Toast.LENGTH_LONG
).show() ).show()
setResult(RESULT_OK) setResult(RESULT_OK)
} else { } else {
Toast.makeText(this, R.string.no_purchase_found, Toast.LENGTH_SHORT).show() Toast.makeText(this, R.string.no_purchase_found, Toast.LENGTH_SHORT).show()
} }
} }
override fun onBillingError(errorCode: Int, error: Throwable?) { override fun onBillingError(errorCode: Int, error: Throwable?) {
Log.e(TAG, "Billing error: code = $errorCode", error) Log.e(TAG, "Billing error: code = $errorCode", error)
} }
override fun onBillingInitialized() { override fun onBillingInitialized() {
restoreButton.isEnabled = true restoreButton.isEnabled = true
purchaseButton.isEnabled = true purchaseButton.isEnabled = true
} }
public override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { public override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
if (!billingProcessor.handleActivityResult(requestCode, resultCode, data)) { if (!billingProcessor.handleActivityResult(requestCode, resultCode, data)) {
super.onActivityResult(requestCode, resultCode, data) super.onActivityResult(requestCode, resultCode, data)
} }
} }
override fun onOptionsItemSelected(item: MenuItem): Boolean { override fun onOptionsItemSelected(item: MenuItem): Boolean {
when (item.itemId) { when (item.itemId) {
android.R.id.home -> finish() android.R.id.home -> finish()
} }
return super.onOptionsItemSelected(item) return super.onOptionsItemSelected(item)
} }
override fun onDestroy() { override fun onDestroy() {
billingProcessor.release() billingProcessor.release()
super.onDestroy() super.onDestroy()
} }
private class RestorePurchaseAsyncTask internal constructor(purchaseActivity: PurchaseActivity) : AsyncTask<Void, Void, Boolean>() { private class RestorePurchaseAsyncTask internal constructor(purchaseActivity: PurchaseActivity) :
AsyncTask<Void, Void, Boolean>() {
private val buyActivityWeakReference: WeakReference<PurchaseActivity> = WeakReference( private val buyActivityWeakReference: WeakReference<PurchaseActivity> = WeakReference(
purchaseActivity purchaseActivity
) )
override fun onPreExecute() { override fun onPreExecute() {
super.onPreExecute() super.onPreExecute()
val purchaseActivity = buyActivityWeakReference.get() val purchaseActivity = buyActivityWeakReference.get()
if (purchaseActivity != null) { if (purchaseActivity != null) {
Toast.makeText(purchaseActivity, R.string.restoring_purchase, Toast.LENGTH_SHORT) Toast.makeText(purchaseActivity, R.string.restoring_purchase, Toast.LENGTH_SHORT)
.show() .show()
} else { } else {
cancel(false) cancel(false)
} }
} }
override fun doInBackground(vararg params: Void): Boolean? { override fun doInBackground(vararg params: Void): Boolean? {
val purchaseActivity = buyActivityWeakReference.get() val purchaseActivity = buyActivityWeakReference.get()
if (purchaseActivity != null) { if (purchaseActivity != null) {
return purchaseActivity.billingProcessor.loadOwnedPurchasesFromGoogle() return purchaseActivity.billingProcessor.loadOwnedPurchasesFromGoogle()
} }
cancel(false) cancel(false)
return null return null
} }
override fun onPostExecute(b: Boolean?) { override fun onPostExecute(b: Boolean?) {
super.onPostExecute(b) super.onPostExecute(b)
val purchaseActivity = buyActivityWeakReference.get() val purchaseActivity = buyActivityWeakReference.get()
if (purchaseActivity == null || b == null) { if (purchaseActivity == null || b == null) {
return return
} }
if (b) { if (b) {
purchaseActivity.onPurchaseHistoryRestored() purchaseActivity.onPurchaseHistoryRestored()
} else { } else {
Toast.makeText( Toast.makeText(
purchaseActivity, purchaseActivity,
R.string.could_not_restore_purchase, R.string.could_not_restore_purchase,
Toast.LENGTH_SHORT Toast.LENGTH_SHORT
).show() ).show()
} }
} }
} }
companion object { companion object {
private const val TAG: String = "PurchaseActivity" private const val TAG: String = "PurchaseActivity"
} }
} }

View file

@ -29,10 +29,17 @@ import code.name.monkey.retromusic.mvp.presenter.SearchPresenter
import code.name.monkey.retromusic.mvp.presenter.SearchView import code.name.monkey.retromusic.mvp.presenter.SearchView
import code.name.monkey.retromusic.util.RetroUtil import code.name.monkey.retromusic.util.RetroUtil
import com.google.android.material.textfield.TextInputEditText import com.google.android.material.textfield.TextInputEditText
import kotlinx.android.synthetic.main.activity_search.* import kotlinx.android.synthetic.main.activity_search.appBarLayout
import java.util.* import kotlinx.android.synthetic.main.activity_search.back
import kotlinx.android.synthetic.main.activity_search.clearText
import kotlinx.android.synthetic.main.activity_search.empty
import kotlinx.android.synthetic.main.activity_search.keyboardPopup
import kotlinx.android.synthetic.main.activity_search.recyclerView
import kotlinx.android.synthetic.main.activity_search.searchContainer
import kotlinx.android.synthetic.main.activity_search.searchView
import kotlinx.android.synthetic.main.activity_search.voiceSearch
import java.util.Locale
import javax.inject.Inject import javax.inject.Inject
import kotlin.collections.ArrayList
class SearchActivity : AbsMusicServiceActivity(), OnQueryTextListener, TextWatcher, SearchView { class SearchActivity : AbsMusicServiceActivity(), OnQueryTextListener, TextWatcher, SearchView {
@Inject @Inject
@ -73,7 +80,7 @@ class SearchActivity : AbsMusicServiceActivity(), OnQueryTextListener, TextWatch
keyboardPopup.backgroundTintList = ColorStateList.valueOf(ThemeStore.accentColor(this)) keyboardPopup.backgroundTintList = ColorStateList.valueOf(ThemeStore.accentColor(this))
ColorStateList.valueOf( ColorStateList.valueOf(
MaterialValueHelper.getPrimaryTextColor(this, ColorUtil.isColorLight(ThemeStore.accentColor(this))) MaterialValueHelper.getPrimaryTextColor(this, ColorUtil.isColorLight(ThemeStore.accentColor(this)))
).apply { ).apply {
keyboardPopup.setTextColor(this) keyboardPopup.setTextColor(this)
keyboardPopup.iconTint = this keyboardPopup.iconTint = this
@ -81,7 +88,6 @@ class SearchActivity : AbsMusicServiceActivity(), OnQueryTextListener, TextWatch
if (savedInstanceState != null) { if (savedInstanceState != null) {
query = savedInstanceState.getString(QUERY) query = savedInstanceState.getString(QUERY)
} }
} }
private fun setupRecyclerView() { private fun setupRecyclerView() {
@ -181,8 +187,8 @@ class SearchActivity : AbsMusicServiceActivity(), OnQueryTextListener, TextWatch
private fun startMicSearch() { private fun startMicSearch() {
val intent = Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH) val intent = Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH)
intent.putExtra( intent.putExtra(
RecognizerIntent.EXTRA_LANGUAGE_MODEL, RecognizerIntent.EXTRA_LANGUAGE_MODEL,
RecognizerIntent.LANGUAGE_MODEL_FREE_FORM RecognizerIntent.LANGUAGE_MODEL_FREE_FORM
) )
intent.putExtra(RecognizerIntent.EXTRA_LANGUAGE, Locale.getDefault()) intent.putExtra(RecognizerIntent.EXTRA_LANGUAGE, Locale.getDefault())
intent.putExtra(RecognizerIntent.EXTRA_PROMPT, getString(R.string.speech_prompt)) intent.putExtra(RecognizerIntent.EXTRA_PROMPT, getString(R.string.speech_prompt))
@ -191,13 +197,11 @@ class SearchActivity : AbsMusicServiceActivity(), OnQueryTextListener, TextWatch
} catch (e: ActivityNotFoundException) { } catch (e: ActivityNotFoundException) {
e.printStackTrace() e.printStackTrace()
Toast.makeText(this, getString(R.string.speech_not_supported), Toast.LENGTH_SHORT) Toast.makeText(this, getString(R.string.speech_not_supported), Toast.LENGTH_SHORT)
.show() .show()
} }
} }
override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) { override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {
} }
override fun onTextChanged(newText: CharSequence, start: Int, before: Int, count: Int) { override fun onTextChanged(newText: CharSequence, start: Int, before: Int, count: Int) {
@ -205,7 +209,6 @@ class SearchActivity : AbsMusicServiceActivity(), OnQueryTextListener, TextWatch
} }
override fun afterTextChanged(s: Editable) { override fun afterTextChanged(s: Editable) {
} }
companion object { companion object {

View file

@ -5,139 +5,142 @@ import android.content.Intent
import android.content.pm.PackageManager import android.content.pm.PackageManager
import android.media.AudioManager import android.media.AudioManager
import android.net.Uri import android.net.Uri
import android.os.* import android.os.Build
import android.os.Bundle
import android.provider.Settings import android.provider.Settings
import android.view.* import android.view.KeyEvent
import android.view.View
import androidx.core.app.ActivityCompat import androidx.core.app.ActivityCompat
import code.name.monkey.appthemehelper.ThemeStore import code.name.monkey.appthemehelper.ThemeStore
import code.name.monkey.retromusic.R
import com.google.android.material.snackbar.Snackbar import com.google.android.material.snackbar.Snackbar
abstract class AbsBaseActivity : AbsThemeActivity() { abstract class AbsBaseActivity : AbsThemeActivity() {
private var hadPermissions: Boolean = false private var hadPermissions: Boolean = false
private lateinit var permissions: Array<String> private lateinit var permissions: Array<String>
private var permissionDeniedMessage: String? = null private var permissionDeniedMessage: String? = null
open fun getPermissionsToRequest(): Array<String> { open fun getPermissionsToRequest(): Array<String> {
return arrayOf() return arrayOf()
} }
protected fun setPermissionDeniedMessage(message: String) { protected fun setPermissionDeniedMessage(message: String) {
permissionDeniedMessage = message permissionDeniedMessage = message
} }
fun getPermissionDeniedMessage(): String { fun getPermissionDeniedMessage(): String {
return if (permissionDeniedMessage == null) getString(code.name.monkey.retromusic.R.string.permissions_denied) else permissionDeniedMessage!! return if (permissionDeniedMessage == null) getString(R.string.permissions_denied) else permissionDeniedMessage!!
} }
private val snackBarContainer: View private val snackBarContainer: View
get() = window.decorView get() = window.decorView
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
volumeControlStream = AudioManager.STREAM_MUSIC volumeControlStream = AudioManager.STREAM_MUSIC
permissions = getPermissionsToRequest() permissions = getPermissionsToRequest()
hadPermissions = hasPermissions() hadPermissions = hasPermissions()
permissionDeniedMessage = null permissionDeniedMessage = null
} }
override fun onPostCreate(savedInstanceState: Bundle?) { override fun onPostCreate(savedInstanceState: Bundle?) {
super.onPostCreate(savedInstanceState) super.onPostCreate(savedInstanceState)
if (!hasPermissions()) { if (!hasPermissions()) {
requestPermissions() requestPermissions()
} }
} }
override fun onResume() { override fun onResume() {
super.onResume() super.onResume()
val hasPermissions = hasPermissions() val hasPermissions = hasPermissions()
if (hasPermissions != hadPermissions) { if (hasPermissions != hadPermissions) {
hadPermissions = hasPermissions hadPermissions = hasPermissions
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
onHasPermissionsChanged(hasPermissions) onHasPermissionsChanged(hasPermissions)
} }
} }
} }
protected open fun onHasPermissionsChanged(hasPermissions: Boolean) { protected open fun onHasPermissionsChanged(hasPermissions: Boolean) {
// implemented by sub classes // implemented by sub classes
} }
override fun dispatchKeyEvent(event: KeyEvent): Boolean { override fun dispatchKeyEvent(event: KeyEvent): Boolean {
if (event.keyCode == KeyEvent.KEYCODE_MENU && event.action == KeyEvent.ACTION_UP) { if (event.keyCode == KeyEvent.KEYCODE_MENU && event.action == KeyEvent.ACTION_UP) {
showOverflowMenu() showOverflowMenu()
return true return true
} }
return super.dispatchKeyEvent(event) return super.dispatchKeyEvent(event)
} }
protected fun showOverflowMenu() { protected fun showOverflowMenu() {
}
} protected open fun requestPermissions() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
requestPermissions(permissions, PERMISSION_REQUEST)
}
}
protected open fun requestPermissions() { protected fun hasPermissions(): Boolean {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
requestPermissions(permissions, PERMISSION_REQUEST) for (permission in permissions) {
} if (checkSelfPermission(permission) != PackageManager.PERMISSION_GRANTED) {
} return false
}
}
}
return true
}
protected fun hasPermissions(): Boolean { override fun onRequestPermissionsResult(
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { requestCode: Int,
for (permission in permissions) { permissions: Array<String>,
if (checkSelfPermission(permission) != PackageManager.PERMISSION_GRANTED) { grantResults: IntArray
return false ) {
} super.onRequestPermissionsResult(requestCode, permissions, grantResults)
} if (requestCode == PERMISSION_REQUEST) {
} for (grantResult in grantResults) {
return true 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)
}
}
override fun onRequestPermissionsResult( companion object {
requestCode: Int, const val PERMISSION_REQUEST = 100
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
}
} }

View file

@ -1,167 +1,179 @@
package code.name.monkey.retromusic.activities.base package code.name.monkey.retromusic.activities.base
import android.Manifest import android.Manifest
import android.content.* import android.content.BroadcastReceiver
import android.os.* import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.content.ServiceConnection
import android.os.Bundle
import android.os.IBinder
import code.name.monkey.retromusic.R import code.name.monkey.retromusic.R
import code.name.monkey.retromusic.helper.MusicPlayerRemote import code.name.monkey.retromusic.helper.MusicPlayerRemote
import code.name.monkey.retromusic.interfaces.MusicServiceEventListener import code.name.monkey.retromusic.interfaces.MusicServiceEventListener
import code.name.monkey.retromusic.service.MusicService.* import code.name.monkey.retromusic.service.MusicService.FAVORITE_STATE_CHANGED
import code.name.monkey.retromusic.service.MusicService.MEDIA_STORE_CHANGED
import code.name.monkey.retromusic.service.MusicService.META_CHANGED
import code.name.monkey.retromusic.service.MusicService.PLAY_STATE_CHANGED
import code.name.monkey.retromusic.service.MusicService.QUEUE_CHANGED
import code.name.monkey.retromusic.service.MusicService.REPEAT_MODE_CHANGED
import code.name.monkey.retromusic.service.MusicService.SHUFFLE_MODE_CHANGED
import java.lang.ref.WeakReference import java.lang.ref.WeakReference
import java.util.* import java.util.ArrayList
abstract class AbsMusicServiceActivity : AbsBaseActivity(), MusicServiceEventListener { abstract class AbsMusicServiceActivity : AbsBaseActivity(), MusicServiceEventListener {
private val mMusicServiceEventListeners = ArrayList<MusicServiceEventListener>() private val mMusicServiceEventListeners = ArrayList<MusicServiceEventListener>()
private var serviceToken: MusicPlayerRemote.ServiceToken? = null private var serviceToken: MusicPlayerRemote.ServiceToken? = null
private var musicStateReceiver: MusicStateReceiver? = null private var musicStateReceiver: MusicStateReceiver? = null
private var receiverRegistered: Boolean = false private var receiverRegistered: Boolean = false
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
serviceToken = MusicPlayerRemote.bindToService(this, object : ServiceConnection { serviceToken = MusicPlayerRemote.bindToService(this, object : ServiceConnection {
override fun onServiceConnected(name: ComponentName, service: IBinder) { override fun onServiceConnected(name: ComponentName, service: IBinder) {
this@AbsMusicServiceActivity.onServiceConnected() this@AbsMusicServiceActivity.onServiceConnected()
} }
override fun onServiceDisconnected(name: ComponentName) { override fun onServiceDisconnected(name: ComponentName) {
this@AbsMusicServiceActivity.onServiceDisconnected() this@AbsMusicServiceActivity.onServiceDisconnected()
} }
}) })
setPermissionDeniedMessage(getString(R.string.permission_external_storage_denied)); setPermissionDeniedMessage(getString(R.string.permission_external_storage_denied))
} }
override fun onDestroy() { override fun onDestroy() {
super.onDestroy() super.onDestroy()
MusicPlayerRemote.unbindFromService(serviceToken) MusicPlayerRemote.unbindFromService(serviceToken)
if (receiverRegistered) { if (receiverRegistered) {
unregisterReceiver(musicStateReceiver) unregisterReceiver(musicStateReceiver)
receiverRegistered = false receiverRegistered = false
} }
} }
fun addMusicServiceEventListener(listener: MusicServiceEventListener?) { fun addMusicServiceEventListener(listener: MusicServiceEventListener?) {
if (listener != null) { if (listener != null) {
mMusicServiceEventListeners.add(listener) mMusicServiceEventListeners.add(listener)
} }
} }
fun removeMusicServiceEventListener(listener: MusicServiceEventListener?) { fun removeMusicServiceEventListener(listener: MusicServiceEventListener?) {
if (listener != null) { if (listener != null) {
mMusicServiceEventListeners.remove(listener) mMusicServiceEventListeners.remove(listener)
} }
} }
override fun onServiceConnected() { override fun onServiceConnected() {
if (!receiverRegistered) { if (!receiverRegistered) {
musicStateReceiver = MusicStateReceiver(this) musicStateReceiver = MusicStateReceiver(this)
val filter = IntentFilter() val filter = IntentFilter()
filter.addAction(PLAY_STATE_CHANGED) filter.addAction(PLAY_STATE_CHANGED)
filter.addAction(SHUFFLE_MODE_CHANGED) filter.addAction(SHUFFLE_MODE_CHANGED)
filter.addAction(REPEAT_MODE_CHANGED) filter.addAction(REPEAT_MODE_CHANGED)
filter.addAction(META_CHANGED) filter.addAction(META_CHANGED)
filter.addAction(QUEUE_CHANGED) filter.addAction(QUEUE_CHANGED)
filter.addAction(MEDIA_STORE_CHANGED) filter.addAction(MEDIA_STORE_CHANGED)
filter.addAction(FAVORITE_STATE_CHANGED) filter.addAction(FAVORITE_STATE_CHANGED)
registerReceiver(musicStateReceiver, filter) registerReceiver(musicStateReceiver, filter)
receiverRegistered = true receiverRegistered = true
} }
for (listener in mMusicServiceEventListeners) { for (listener in mMusicServiceEventListeners) {
listener.onServiceConnected() listener.onServiceConnected()
} }
} }
override fun onServiceDisconnected() { override fun onServiceDisconnected() {
if (receiverRegistered) { if (receiverRegistered) {
unregisterReceiver(musicStateReceiver) unregisterReceiver(musicStateReceiver)
receiverRegistered = false receiverRegistered = false
} }
for (listener in mMusicServiceEventListeners) { for (listener in mMusicServiceEventListeners) {
listener.onServiceDisconnected() listener.onServiceDisconnected()
} }
} }
override fun onPlayingMetaChanged() { override fun onPlayingMetaChanged() {
for (listener in mMusicServiceEventListeners) { for (listener in mMusicServiceEventListeners) {
listener.onPlayingMetaChanged() listener.onPlayingMetaChanged()
} }
} }
override fun onQueueChanged() { override fun onQueueChanged() {
for (listener in mMusicServiceEventListeners) { for (listener in mMusicServiceEventListeners) {
listener.onQueueChanged() listener.onQueueChanged()
} }
} }
override fun onPlayStateChanged() { override fun onPlayStateChanged() {
for (listener in mMusicServiceEventListeners) { for (listener in mMusicServiceEventListeners) {
listener.onPlayStateChanged() listener.onPlayStateChanged()
} }
} }
override fun onMediaStoreChanged() { override fun onMediaStoreChanged() {
for (listener in mMusicServiceEventListeners) { for (listener in mMusicServiceEventListeners) {
listener.onMediaStoreChanged() listener.onMediaStoreChanged()
} }
} }
override fun onRepeatModeChanged() { override fun onRepeatModeChanged() {
for (listener in mMusicServiceEventListeners) { for (listener in mMusicServiceEventListeners) {
listener.onRepeatModeChanged() listener.onRepeatModeChanged()
} }
} }
override fun onShuffleModeChanged() { override fun onShuffleModeChanged() {
for (listener in mMusicServiceEventListeners) { for (listener in mMusicServiceEventListeners) {
listener.onShuffleModeChanged() listener.onShuffleModeChanged()
} }
} }
override fun onHasPermissionsChanged(hasPermissions: Boolean) { override fun onHasPermissionsChanged(hasPermissions: Boolean) {
super.onHasPermissionsChanged(hasPermissions) super.onHasPermissionsChanged(hasPermissions)
val intent = Intent(MEDIA_STORE_CHANGED) val intent = Intent(MEDIA_STORE_CHANGED)
intent.putExtra( intent.putExtra(
"from_permissions_changed", "from_permissions_changed",
true true
) // just in case we need to know this at some point ) // just in case we need to know this at some point
sendBroadcast(intent) sendBroadcast(intent)
} }
override fun getPermissionsToRequest(): Array<String> { override fun getPermissionsToRequest(): Array<String> {
return arrayOf( return arrayOf(
Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.READ_EXTERNAL_STORAGE,
Manifest.permission.WRITE_EXTERNAL_STORAGE Manifest.permission.WRITE_EXTERNAL_STORAGE
) )
} }
private class MusicStateReceiver(activity: AbsMusicServiceActivity) : BroadcastReceiver() { private class MusicStateReceiver(activity: AbsMusicServiceActivity) : BroadcastReceiver() {
private val reference: WeakReference<AbsMusicServiceActivity> = WeakReference(activity) private val reference: WeakReference<AbsMusicServiceActivity> = WeakReference(activity)
override fun onReceive(context: Context, intent: Intent) { override fun onReceive(context: Context, intent: Intent) {
val action = intent.action val action = intent.action
val activity = reference.get() val activity = reference.get()
if (activity != null && action != null) { if (activity != null && action != null) {
when (action) { when (action) {
FAVORITE_STATE_CHANGED, META_CHANGED -> activity.onPlayingMetaChanged() FAVORITE_STATE_CHANGED, META_CHANGED -> activity.onPlayingMetaChanged()
QUEUE_CHANGED -> activity.onQueueChanged() QUEUE_CHANGED -> activity.onQueueChanged()
PLAY_STATE_CHANGED -> activity.onPlayStateChanged() PLAY_STATE_CHANGED -> activity.onPlayStateChanged()
REPEAT_MODE_CHANGED -> activity.onRepeatModeChanged() REPEAT_MODE_CHANGED -> activity.onRepeatModeChanged()
SHUFFLE_MODE_CHANGED -> activity.onShuffleModeChanged() SHUFFLE_MODE_CHANGED -> activity.onShuffleModeChanged()
MEDIA_STORE_CHANGED -> activity.onMediaStoreChanged() MEDIA_STORE_CHANGED -> activity.onMediaStoreChanged()
} }
} }
} }
} }
companion object { companion object {
val TAG: String = AbsMusicServiceActivity::class.java.simpleName val TAG: String = AbsMusicServiceActivity::class.java.simpleName
} }
} }

View file

@ -15,7 +15,20 @@ import code.name.monkey.retromusic.extensions.hide
import code.name.monkey.retromusic.extensions.show import code.name.monkey.retromusic.extensions.show
import code.name.monkey.retromusic.fragments.MiniPlayerFragment 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.NowPlayingScreen.* import code.name.monkey.retromusic.fragments.NowPlayingScreen.ADAPTIVE
import code.name.monkey.retromusic.fragments.NowPlayingScreen.BLUR
import code.name.monkey.retromusic.fragments.NowPlayingScreen.BLUR_CARD
import code.name.monkey.retromusic.fragments.NowPlayingScreen.CARD
import code.name.monkey.retromusic.fragments.NowPlayingScreen.COLOR
import code.name.monkey.retromusic.fragments.NowPlayingScreen.FIT
import code.name.monkey.retromusic.fragments.NowPlayingScreen.FLAT
import code.name.monkey.retromusic.fragments.NowPlayingScreen.FULL
import code.name.monkey.retromusic.fragments.NowPlayingScreen.MATERIAL
import code.name.monkey.retromusic.fragments.NowPlayingScreen.NORMAL
import code.name.monkey.retromusic.fragments.NowPlayingScreen.PEAK
import code.name.monkey.retromusic.fragments.NowPlayingScreen.PLAIN
import code.name.monkey.retromusic.fragments.NowPlayingScreen.SIMPLE
import code.name.monkey.retromusic.fragments.NowPlayingScreen.TINY
import code.name.monkey.retromusic.fragments.base.AbsPlayerFragment import code.name.monkey.retromusic.fragments.base.AbsPlayerFragment
import code.name.monkey.retromusic.fragments.player.adaptive.AdaptiveFragment 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.blur.BlurPlayerFragment
@ -38,7 +51,9 @@ import code.name.monkey.retromusic.util.PreferenceUtil
import code.name.monkey.retromusic.views.BottomNavigationBarTinted import code.name.monkey.retromusic.views.BottomNavigationBarTinted
import com.google.android.material.bottomsheet.BottomSheetBehavior import com.google.android.material.bottomsheet.BottomSheetBehavior
import com.google.android.material.card.MaterialCardView import com.google.android.material.card.MaterialCardView
import kotlinx.android.synthetic.main.sliding_music_panel_layout.* import kotlinx.android.synthetic.main.sliding_music_panel_layout.bottomNavigationView
import kotlinx.android.synthetic.main.sliding_music_panel_layout.dimBackground
import kotlinx.android.synthetic.main.sliding_music_panel_layout.slidingPanel
abstract class AbsSlidingMusicPanelActivity : AbsMusicServiceActivity(), AbsPlayerFragment.Callbacks { abstract class AbsSlidingMusicPanelActivity : AbsMusicServiceActivity(), AbsPlayerFragment.Callbacks {
companion object { companion object {
@ -205,7 +220,8 @@ abstract class AbsSlidingMusicPanelActivity : AbsMusicServiceActivity(), AbsPlay
slidingPanel.cardElevation = DensityUtil.dip2px(this, 10f).toFloat() slidingPanel.cardElevation = DensityUtil.dip2px(this, 10f).toFloat()
bottomNavigationView.elevation = DensityUtil.dip2px(this, 10f).toFloat() bottomNavigationView.elevation = DensityUtil.dip2px(this, 10f).toFloat()
bottomSheetBehavior.isHideable = false bottomSheetBehavior.isHideable = false
bottomSheetBehavior.peekHeight = if (bottomNavigationView.visibility == View.VISIBLE) heightOfBarWithTabs else heightOfBar bottomSheetBehavior.peekHeight =
if (bottomNavigationView.visibility == View.VISIBLE) heightOfBarWithTabs else heightOfBar
} }
} }
} }
@ -236,7 +252,7 @@ abstract class AbsSlidingMusicPanelActivity : AbsMusicServiceActivity(), AbsPlay
else -> PlayerFragment() else -> PlayerFragment()
} // must implement AbsPlayerFragment } // must implement AbsPlayerFragment
supportFragmentManager.beginTransaction().replace(R.id.playerFragmentContainer, fragment) supportFragmentManager.beginTransaction().replace(R.id.playerFragmentContainer, fragment)
.commit() .commit()
supportFragmentManager.executePendingTransactions() supportFragmentManager.executePendingTransactions()
playerFragment = supportFragmentManager.findFragmentById(R.id.playerFragmentContainer) as AbsPlayerFragment playerFragment = supportFragmentManager.findFragmentById(R.id.playerFragmentContainer) as AbsPlayerFragment
@ -292,7 +308,14 @@ abstract class AbsSlidingMusicPanelActivity : AbsMusicServiceActivity(), AbsPlay
super.setLightNavigationBar(isColorLight) super.setLightNavigationBar(isColorLight)
super.setLightStatusbar(isColorLight) super.setLightStatusbar(isColorLight)
} else { } else {
super.setLightStatusbar(ColorUtil.isColorLight(ATHUtil.resolveColor(this, android.R.attr.windowBackground))) super.setLightStatusbar(
ColorUtil.isColorLight(
ATHUtil.resolveColor(
this,
android.R.attr.windowBackground
)
)
)
super.setLightNavigationBar(true) super.setLightNavigationBar(true)
} }
} }

View file

@ -68,7 +68,8 @@ abstract class AbsThemeActivity : ATHToolbarActivity(), Runnable {
var background: Drawable? = if (PreferenceUtil.getInstance(this).isRoundCorners) var background: Drawable? = if (PreferenceUtil.getInstance(this).isRoundCorners)
ContextCompat.getDrawable(this, R.drawable.round_window) ContextCompat.getDrawable(this, R.drawable.round_window)
else ContextCompat.getDrawable(this, R.drawable.square_window) else ContextCompat.getDrawable(this, R.drawable.square_window)
background = TintHelper.createTintedDrawable(background, ATHUtil.resolveColor(this, android.R.attr.windowBackground)) background =
TintHelper.createTintedDrawable(background, ATHUtil.resolveColor(this, android.R.attr.windowBackground))
window.setBackgroundDrawable(background) window.setBackgroundDrawable(background)
} }
@ -158,7 +159,8 @@ abstract class AbsThemeActivity : ATHToolbarActivity(), Runnable {
} }
private fun setImmersiveFullscreen() { 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) 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(this).fullScreenMode) { if (PreferenceUtil.getInstance(this).fullScreenMode) {
window.decorView.systemUiVisibility = flags window.decorView.systemUiVisibility = flags
@ -190,6 +192,5 @@ abstract class AbsThemeActivity : ATHToolbarActivity(), Runnable {
handler.postDelayed(this, 500) handler.postDelayed(this, 500)
} }
return super.onKeyDown(keyCode, event) return super.onKeyDown(keyCode, event)
} }
} }

View file

@ -32,9 +32,19 @@ import com.afollestad.materialdialogs.MaterialDialog
import com.afollestad.materialdialogs.callbacks.onCancel import com.afollestad.materialdialogs.callbacks.onCancel
import com.google.android.material.floatingactionbutton.FloatingActionButton import com.google.android.material.floatingactionbutton.FloatingActionButton
import com.google.android.material.textfield.TextInputLayout import com.google.android.material.textfield.TextInputLayout
import kotlinx.android.synthetic.main.activity_bug_report.* import kotlinx.android.synthetic.main.activity_bug_report.sendFab
import kotlinx.android.synthetic.main.bug_report_card_device_info.* import kotlinx.android.synthetic.main.activity_bug_report.toolbar
import kotlinx.android.synthetic.main.bug_report_card_report.* import kotlinx.android.synthetic.main.bug_report_card_device_info.airTextDeviceInfo
import kotlinx.android.synthetic.main.bug_report_card_report.inputDescription
import kotlinx.android.synthetic.main.bug_report_card_report.inputLayoutDescription
import kotlinx.android.synthetic.main.bug_report_card_report.inputLayoutPassword
import kotlinx.android.synthetic.main.bug_report_card_report.inputLayoutTitle
import kotlinx.android.synthetic.main.bug_report_card_report.inputLayoutUsername
import kotlinx.android.synthetic.main.bug_report_card_report.inputPassword
import kotlinx.android.synthetic.main.bug_report_card_report.inputTitle
import kotlinx.android.synthetic.main.bug_report_card_report.inputUsername
import kotlinx.android.synthetic.main.bug_report_card_report.optionAnonymous
import kotlinx.android.synthetic.main.bug_report_card_report.optionUseAccount
import org.eclipse.egit.github.core.Issue import org.eclipse.egit.github.core.Issue
import org.eclipse.egit.github.core.client.GitHubClient import org.eclipse.egit.github.core.client.GitHubClient
import org.eclipse.egit.github.core.client.RequestException import org.eclipse.egit.github.core.client.RequestException
@ -48,11 +58,11 @@ private const val RESULT_ISSUES_NOT_ENABLED = "RESULT_ISSUES_NOT_ENABLED"
private const val RESULT_UNKNOWN = "RESULT_UNKNOWN" private const val RESULT_UNKNOWN = "RESULT_UNKNOWN"
@StringDef( @StringDef(
RESULT_SUCCESS, RESULT_SUCCESS,
RESULT_BAD_CREDENTIALS, RESULT_BAD_CREDENTIALS,
RESULT_INVALID_TOKEN, RESULT_INVALID_TOKEN,
RESULT_ISSUES_NOT_ENABLED, RESULT_ISSUES_NOT_ENABLED,
RESULT_UNKNOWN RESULT_UNKNOWN
) )
@Retention(AnnotationRetention.SOURCE) @Retention(AnnotationRetention.SOURCE)
private annotation class Result private annotation class Result
@ -157,9 +167,9 @@ open class BugReportActivity : AbsThemeActivity() {
val clip = ClipData.newPlainText(getString(R.string.device_info), deviceInfo?.toMarkdown()) val clip = ClipData.newPlainText(getString(R.string.device_info), deviceInfo?.toMarkdown())
clipboard.setPrimaryClip(clip) clipboard.setPrimaryClip(clip)
Toast.makeText( Toast.makeText(
this@BugReportActivity, this@BugReportActivity,
R.string.copied_device_info_to_clipboard, R.string.copied_device_info_to_clipboard,
Toast.LENGTH_LONG Toast.LENGTH_LONG
).show() ).show()
} }
@ -232,11 +242,12 @@ open class BugReportActivity : AbsThemeActivity() {
} }
private class ReportIssueAsyncTask private constructor( private class ReportIssueAsyncTask private constructor(
activity: Activity, activity: Activity,
private val report: Report, private val report: Report,
private val target: GithubTarget, private val target: GithubTarget,
private val login: GithubLogin private val login: GithubLogin
) : DialogAsyncTask<Void, Void, String>(activity) { ) : DialogAsyncTask<Void, Void, String>(activity) {
override fun createDialog(context: Context): Dialog { override fun createDialog(context: Context): Dialog {
return AlertDialog.Builder(context).show() return AlertDialog.Builder(context).show()
} }
@ -268,7 +279,6 @@ open class BugReportActivity : AbsThemeActivity() {
e.printStackTrace() e.printStackTrace()
return RESULT_UNKNOWN return RESULT_UNKNOWN
} }
} }
override fun onPostExecute(@Result result: String) { override fun onPostExecute(@Result result: String) {
@ -312,10 +322,10 @@ open class BugReportActivity : AbsThemeActivity() {
companion object { companion object {
fun report( fun report(
activity: Activity, activity: Activity,
report: Report, report: Report,
target: GithubTarget, target: GithubTarget,
login: GithubLogin login: GithubLogin
) { ) {
ReportIssueAsyncTask(activity, report, target, login).execute() ReportIssueAsyncTask(activity, report, target, login).execute()
} }

View file

@ -5,41 +5,56 @@ import android.content.Context;
import android.content.pm.PackageInfo; import android.content.pm.PackageInfo;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
import android.os.Build; import android.os.Build;
import androidx.annotation.IntRange; import androidx.annotation.IntRange;
import code.name.monkey.retromusic.util.PreferenceUtil;
import java.util.Arrays; import java.util.Arrays;
import code.name.monkey.retromusic.util.PreferenceUtil;
public class DeviceInfo { 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") @SuppressLint("NewApi")
@SuppressWarnings("deprecation") @SuppressWarnings("deprecation")
private final String[] abis = Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP ? private final String[] abis = Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP ?
Build.SUPPORTED_ABIS : new String[]{Build.CPU_ABI, Build.CPU_ABI2}; Build.SUPPORTED_ABIS : new String[]{Build.CPU_ABI, Build.CPU_ABI2};
@SuppressLint("NewApi") @SuppressLint("NewApi")
private final String[] abis32Bits = Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP ? private final String[] abis32Bits = Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP ?
Build.SUPPORTED_32_BIT_ABIS : null; Build.SUPPORTED_32_BIT_ABIS : null;
@SuppressLint("NewApi") @SuppressLint("NewApi")
private final String[] abis64Bits = Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP ? private final String[] abis64Bits = Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP ?
Build.SUPPORTED_64_BIT_ABIS : null; Build.SUPPORTED_64_BIT_ABIS : null;
private final String baseTheme;
private final String brand = Build.BRAND;
private final String buildID = Build.DISPLAY;
private final String buildVersion = Build.VERSION.INCREMENTAL;
private final String device = Build.DEVICE;
private final String hardware = Build.HARDWARE;
private final boolean isAdaptive;
private final String manufacturer = Build.MANUFACTURER;
private final String model = Build.MODEL;
private final String nowPlayingTheme;
private final String product = Build.PRODUCT;
private final String releaseVersion = Build.VERSION.RELEASE;
@IntRange(from = 0)
private final int sdkVersion = Build.VERSION.SDK_INT;
private final int versionCode;
private final String versionName;
public DeviceInfo(Context context) { public DeviceInfo(Context context) {
PackageInfo packageInfo; PackageInfo packageInfo;
try { try {

View file

@ -4,7 +4,6 @@ package code.name.monkey.retromusic.activities.bugreport.model;
import code.name.monkey.retromusic.activities.bugreport.model.github.ExtraInfo; import code.name.monkey.retromusic.activities.bugreport.model.github.ExtraInfo;
public class Report { public class Report {
private final String title;
private final String description; private final String description;
@ -12,6 +11,8 @@ public class Report {
private final ExtraInfo extraInfo; private final ExtraInfo extraInfo;
private final String title;
public Report(String title, String description, DeviceInfo deviceInfo, ExtraInfo extraInfo) { public Report(String title, String description, DeviceInfo deviceInfo, ExtraInfo extraInfo) {
this.title = title; this.title = title;
this.description = description; this.description = description;
@ -19,14 +20,14 @@ public class Report {
this.extraInfo = extraInfo; this.extraInfo = extraInfo;
} }
public String getTitle() {
return title;
}
public String getDescription() { public String getDescription() {
return description + "\n\n" return description + "\n\n"
+ "-\n\n" + "-\n\n"
+ deviceInfo.toMarkdown() + "\n\n" + deviceInfo.toMarkdown() + "\n\n"
+ extraInfo.toMarkdown(); + extraInfo.toMarkdown();
} }
public String getTitle() {
return title;
}
} }

View file

@ -4,6 +4,7 @@ import java.util.LinkedHashMap;
import java.util.Map; import java.util.Map;
public class ExtraInfo { public class ExtraInfo {
private final Map<String, String> extraInfo = new LinkedHashMap<>(); private final Map<String, String> extraInfo = new LinkedHashMap<>();
public void put(String key, String value) { public void put(String key, String value) {
@ -39,7 +40,9 @@ public class ExtraInfo {
} }
public String toMarkdown() { public String toMarkdown() {
if (extraInfo.isEmpty()) return ""; if (extraInfo.isEmpty()) {
return "";
}
StringBuilder output = new StringBuilder(); StringBuilder output = new StringBuilder();
output.append("Extra info:\n" output.append("Extra info:\n"

View file

@ -3,11 +3,12 @@ package code.name.monkey.retromusic.activities.bugreport.model.github;
import android.text.TextUtils; import android.text.TextUtils;
public class GithubLogin { public class GithubLogin {
private final String username;
private final String apiToken;
private final String password; private final String password;
private final String apiToken; private final String username;
public GithubLogin(String username, String password) { public GithubLogin(String username, String password) {
this.username = username; this.username = username;
@ -21,20 +22,20 @@ public class GithubLogin {
this.apiToken = apiToken; this.apiToken = apiToken;
} }
public String getUsername() { public String getApiToken() {
return username; return apiToken;
} }
public String getPassword() { public String getPassword() {
return password; return password;
} }
public String getUsername() {
return username;
}
public boolean shouldUseApiToken() { public boolean shouldUseApiToken() {
return TextUtils.isEmpty(username) || TextUtils.isEmpty(password); return TextUtils.isEmpty(username) || TextUtils.isEmpty(password);
} }
public String getApiToken() {
return apiToken;
}
} }

View file

@ -1,20 +1,21 @@
package code.name.monkey.retromusic.activities.bugreport.model.github; package code.name.monkey.retromusic.activities.bugreport.model.github;
public class GithubTarget { public class GithubTarget {
private final String username;
private final String repository; private final String repository;
private final String username;
public GithubTarget(String username, String repository) { public GithubTarget(String username, String repository) {
this.username = username; this.username = username;
this.repository = repository; this.repository = repository;
} }
public String getUsername() {
return username;
}
public String getRepository() { public String getRepository() {
return repository; return repository;
} }
public String getUsername() {
return username;
}
} }

View file

@ -16,18 +16,16 @@ package code.name.monkey.retromusic.activities.saf;
import android.os.Build; import android.os.Build;
import android.os.Bundle; import android.os.Bundle;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import code.name.monkey.retromusic.R;
import com.heinrichreimersoftware.materialintro.app.IntroActivity; import com.heinrichreimersoftware.materialintro.app.IntroActivity;
import com.heinrichreimersoftware.materialintro.slide.SimpleSlide; import com.heinrichreimersoftware.materialintro.slide.SimpleSlide;
import code.name.monkey.retromusic.R;
/** /**
* Created by hemanths on 2019-07-31. * Created by hemanths on 2019-07-31.
*/ */
public class SAFGuideActivity extends IntroActivity { public class SAFGuideActivity extends IntroActivity {
public static final int REQUEST_CODE_SAF_GUIDE = 98; public static final int REQUEST_CODE_SAF_GUIDE = 98;
@Override @Override
@ -44,7 +42,8 @@ public class SAFGuideActivity extends IntroActivity {
addSlide(new SimpleSlide.Builder() addSlide(new SimpleSlide.Builder()
.title(title) .title(title)
.description(Build.VERSION.SDK_INT <= Build.VERSION_CODES.N_MR1 ? R.string.saf_guide_slide1_description_before_o : R.string.saf_guide_slide1_description) .description(Build.VERSION.SDK_INT <= Build.VERSION_CODES.N_MR1
? R.string.saf_guide_slide1_description_before_o : R.string.saf_guide_slide1_description)
.image(R.drawable.saf_guide_1) .image(R.drawable.saf_guide_1)
.background(R.color.md_deep_purple_300) .background(R.color.md_deep_purple_300)
.backgroundDark(R.color.md_deep_purple_400) .backgroundDark(R.color.md_deep_purple_400)

View file

@ -1,416 +1,417 @@
package code.name.monkey.retromusic.activities.tageditor package code.name.monkey.retromusic.activities.tageditor
import android.app.* import android.app.Activity
import android.app.SearchManager
import android.content.Intent import android.content.Intent
import android.content.res.ColorStateList import android.content.res.ColorStateList
import android.graphics.* import android.graphics.Bitmap
import android.graphics.BitmapFactory
import android.net.Uri import android.net.Uri
import android.os.* import android.os.Build
import android.os.Bundle
import android.util.Log import android.util.Log
import android.view.* import android.view.MenuItem
import android.view.View
import android.view.animation.OvershootInterpolator import android.view.animation.OvershootInterpolator
import code.name.monkey.appthemehelper.ThemeStore import code.name.monkey.appthemehelper.ThemeStore
import code.name.monkey.appthemehelper.util.* import code.name.monkey.appthemehelper.util.ATHUtil
import code.name.monkey.appthemehelper.util.ColorUtil
import code.name.monkey.appthemehelper.util.MaterialValueHelper
import code.name.monkey.appthemehelper.util.TintHelper
import code.name.monkey.retromusic.R import code.name.monkey.retromusic.R
import code.name.monkey.retromusic.activities.base.AbsBaseActivity import code.name.monkey.retromusic.activities.base.AbsBaseActivity
import code.name.monkey.retromusic.activities.saf.SAFGuideActivity import code.name.monkey.retromusic.activities.saf.SAFGuideActivity
import code.name.monkey.retromusic.util.* import code.name.monkey.retromusic.util.PreferenceUtil
import com.afollestad.materialdialogs.* import code.name.monkey.retromusic.util.RetroUtil
import code.name.monkey.retromusic.util.SAFUtil
import com.afollestad.materialdialogs.LayoutMode
import com.afollestad.materialdialogs.MaterialDialog
import com.afollestad.materialdialogs.bottomsheets.BottomSheet import com.afollestad.materialdialogs.bottomsheets.BottomSheet
import com.afollestad.materialdialogs.list.listItems import com.afollestad.materialdialogs.list.listItems
import com.google.android.material.button.MaterialButton import com.google.android.material.button.MaterialButton
import kotlinx.android.synthetic.main.activity_album_tag_editor.* import kotlinx.android.synthetic.main.activity_album_tag_editor.editorImage
import org.jaudiotagger.audio.* import kotlinx.android.synthetic.main.activity_album_tag_editor.imageContainer
import org.jaudiotagger.audio.AudioFile
import org.jaudiotagger.audio.AudioFileIO
import org.jaudiotagger.tag.FieldKey import org.jaudiotagger.tag.FieldKey
import java.io.File import java.io.File
import java.util.* import java.util.Collections
abstract class AbsTagEditorActivity : AbsBaseActivity() { abstract class AbsTagEditorActivity : AbsBaseActivity() {
protected var id: Int = 0 protected var id: Int = 0
private set private set
private var paletteColorPrimary: Int = 0 private var paletteColorPrimary: Int = 0
private var isInNoImageMode: Boolean = false private var isInNoImageMode: Boolean = false
private var songPaths: List<String>? = null private var songPaths: List<String>? = null
lateinit var saveFab: MaterialButton lateinit var saveFab: MaterialButton
private var savedSongPaths: List<String>? = null private var savedSongPaths: List<String>? = null
private val currentSongPath: String? = null private val currentSongPath: String? = null
private var savedTags: Map<FieldKey, String>? = null private var savedTags: Map<FieldKey, String>? = null
private var savedArtworkInfo: ArtworkInfo? = null private var savedArtworkInfo: ArtworkInfo? = null
protected val show: MaterialDialog protected val show: MaterialDialog
get() = MaterialDialog(this, BottomSheet(LayoutMode.WRAP_CONTENT)).show { get() = MaterialDialog(this, BottomSheet(LayoutMode.WRAP_CONTENT)).show {
cornerRadius(PreferenceUtil.getInstance(this@AbsTagEditorActivity).dialogCorner) cornerRadius(PreferenceUtil.getInstance(this@AbsTagEditorActivity).dialogCorner)
title(R.string.update_image) title(R.string.update_image)
listItems(items = items) { _, position, _ -> listItems(items = items) { _, position, _ ->
when (position) { when (position) {
0 -> startImagePicker() 0 -> startImagePicker()
1 -> searchImageOnWeb() 1 -> searchImageOnWeb()
2 -> deleteImage() 2 -> deleteImage()
} }
} }
} }
protected abstract val contentViewLayout: Int protected abstract val contentViewLayout: Int
internal val albumArtist: String? internal val albumArtist: String?
get() { get() {
return try { return try {
getAudioFile(songPaths!![0]).tagOrCreateAndSetDefault.getFirst(FieldKey.ALBUM_ARTIST) getAudioFile(songPaths!![0]).tagOrCreateAndSetDefault.getFirst(FieldKey.ALBUM_ARTIST)
} catch (ignored: Exception) { } catch (ignored: Exception) {
null 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 songTitle: String? protected val albumTitle: String?
get() { get() {
return try { return try {
getAudioFile(songPaths!![0]).tagOrCreateAndSetDefault.getFirst(FieldKey.TITLE) getAudioFile(songPaths!![0]).tagOrCreateAndSetDefault.getFirst(FieldKey.ALBUM)
} catch (ignored: Exception) { } catch (ignored: Exception) {
null null
} }
}
} protected val artistName: String?
protected val composer: String? get() {
get() { return try {
return try { getAudioFile(songPaths!![0]).tagOrCreateAndSetDefault.getFirst(FieldKey.ARTIST)
getAudioFile(songPaths!![0]).tagOrCreateAndSetDefault.getFirst(FieldKey.COMPOSER) } catch (ignored: Exception) {
} catch (ignored: Exception) { null
null }
} }
} protected val albumArtistName: String?
get() {
return try {
getAudioFile(songPaths!![0]).tagOrCreateAndSetDefault.getFirst(FieldKey.ALBUM_ARTIST)
} catch (ignored: Exception) {
null
}
}
protected val albumTitle: String? protected val genreName: String?
get() { get() {
return try { return try {
getAudioFile(songPaths!![0]).tagOrCreateAndSetDefault.getFirst(FieldKey.ALBUM) getAudioFile(songPaths!![0]).tagOrCreateAndSetDefault.getFirst(FieldKey.GENRE)
} catch (ignored: Exception) { } catch (ignored: Exception) {
null null
} }
}
} protected val songYear: String?
get() {
return try {
getAudioFile(songPaths!![0]).tagOrCreateAndSetDefault.getFirst(FieldKey.YEAR)
} catch (ignored: Exception) {
null
}
}
protected val artistName: String? protected val trackNumber: String?
get() { get() {
return try { return try {
getAudioFile(songPaths!![0]).tagOrCreateAndSetDefault.getFirst(FieldKey.ARTIST) getAudioFile(songPaths!![0]).tagOrCreateAndSetDefault.getFirst(FieldKey.TRACK)
} catch (ignored: Exception) { } catch (ignored: Exception) {
null null
} }
}
} protected val lyrics: String?
get() {
return try {
getAudioFile(songPaths!![0]).tagOrCreateAndSetDefault.getFirst(FieldKey.LYRICS)
} catch (ignored: Exception) {
null
}
}
protected val albumArtistName: String? protected val albumArt: Bitmap?
get() { get() {
return try { try {
getAudioFile(songPaths!![0]).tagOrCreateAndSetDefault.getFirst(FieldKey.ALBUM_ARTIST) val artworkTag = getAudioFile(songPaths!![0]).tagOrCreateAndSetDefault.firstArtwork
} catch (ignored: Exception) { if (artworkTag != null) {
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)
protected val genreName: String? saveFab = findViewById(R.id.saveTags)
get() { getIntentExtras()
return try {
getAudioFile(songPaths!![0]).tagOrCreateAndSetDefault.getFirst(FieldKey.GENRE)
} catch (ignored: Exception) {
null
}
} songPaths = getSongPaths()
if (songPaths!!.isEmpty()) {
finish()
return
}
protected val songYear: String? setUpViews()
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() setNavigationbarColorAuto()
setTaskDescriptionColorAuto() setTaskDescriptionColorAuto()
} }
private fun setUpViews() { private fun setUpViews() {
setUpScrollView() setUpScrollView()
setUpFab() setUpFab()
setUpImageView() setUpImageView()
} }
private fun setUpScrollView() { private fun setUpScrollView() {
//observableScrollView.setScrollViewCallbacks(observableScrollViewCallbacks); //observableScrollView.setScrollViewCallbacks(observableScrollViewCallbacks);
} }
private lateinit var items: List<String> private lateinit var items: List<String>
private fun setUpImageView() { private fun setUpImageView() {
loadCurrentImage() loadCurrentImage()
items = listOf( items = listOf(
getString(code.name.monkey.retromusic.R.string.pick_from_local_storage), getString(code.name.monkey.retromusic.R.string.pick_from_local_storage),
getString(code.name.monkey.retromusic.R.string.web_search), getString(code.name.monkey.retromusic.R.string.web_search),
getString(code.name.monkey.retromusic.R.string.remove_cover) getString(code.name.monkey.retromusic.R.string.remove_cover)
) )
editorImage?.setOnClickListener { show } editorImage?.setOnClickListener { show }
} }
private fun startImagePicker() { private fun startImagePicker() {
val intent = Intent(Intent.ACTION_GET_CONTENT) val intent = Intent(Intent.ACTION_GET_CONTENT)
intent.type = "image/*" intent.type = "image/*"
startActivityForResult( startActivityForResult(
Intent.createChooser( Intent.createChooser(
intent, intent,
getString(code.name.monkey.retromusic.R.string.pick_from_local_storage) getString(code.name.monkey.retromusic.R.string.pick_from_local_storage)
), REQUEST_CODE_SELECT_IMAGE ), REQUEST_CODE_SELECT_IMAGE
) )
} }
protected abstract fun loadCurrentImage() protected abstract fun loadCurrentImage()
protected abstract fun searchImageOnWeb() protected abstract fun searchImageOnWeb()
protected abstract fun deleteImage() protected abstract fun deleteImage()
private fun setUpFab() { private fun setUpFab() {
saveFab.backgroundTintList = ColorStateList.valueOf(ThemeStore.accentColor(this)) saveFab.backgroundTintList = ColorStateList.valueOf(ThemeStore.accentColor(this))
ColorStateList.valueOf( ColorStateList.valueOf(
MaterialValueHelper.getPrimaryTextColor( MaterialValueHelper.getPrimaryTextColor(
this, this,
ColorUtil.isColorLight( ColorUtil.isColorLight(
ThemeStore.accentColor( ThemeStore.accentColor(
this this
) )
) )
) )
).apply { ).apply {
saveFab.setTextColor(this) saveFab.setTextColor(this)
saveFab.iconTint = this saveFab.iconTint = this
} }
saveFab.apply { saveFab.apply {
scaleX = 0f scaleX = 0f
scaleY = 0f scaleY = 0f
isEnabled = false isEnabled = false
setOnClickListener { save() } setOnClickListener { save() }
TintHelper.setTintAuto(this, ThemeStore.accentColor(this@AbsTagEditorActivity), true) TintHelper.setTintAuto(this, ThemeStore.accentColor(this@AbsTagEditorActivity), true)
} }
} }
protected abstract fun save() protected abstract fun save()
private fun getIntentExtras() { private fun getIntentExtras() {
val intentExtras = intent.extras val intentExtras = intent.extras
if (intentExtras != null) { if (intentExtras != null) {
id = intentExtras.getInt(EXTRA_ID) id = intentExtras.getInt(EXTRA_ID)
} }
} }
protected abstract fun getSongPaths(): List<String> protected abstract fun getSongPaths(): List<String>
protected fun searchWebFor(vararg keys: String) { protected fun searchWebFor(vararg keys: String) {
val stringBuilder = StringBuilder() val stringBuilder = StringBuilder()
for (key in keys) { for (key in keys) {
stringBuilder.append(key) stringBuilder.append(key)
stringBuilder.append(" ") stringBuilder.append(" ")
} }
val intent = Intent(Intent.ACTION_WEB_SEARCH) val intent = Intent(Intent.ACTION_WEB_SEARCH)
intent.putExtra(SearchManager.QUERY, stringBuilder.toString()) intent.putExtra(SearchManager.QUERY, stringBuilder.toString())
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
startActivity(intent) startActivity(intent)
} }
override fun onOptionsItemSelected(item: MenuItem): Boolean { override fun onOptionsItemSelected(item: MenuItem): Boolean {
when (item.itemId) { when (item.itemId) {
android.R.id.home -> { android.R.id.home -> {
super.onBackPressed() super.onBackPressed()
return true return true
} }
} }
return super.onOptionsItemSelected(item) return super.onOptionsItemSelected(item)
} }
protected fun setNoImageMode() { protected fun setNoImageMode() {
isInNoImageMode = true isInNoImageMode = true
imageContainer?.visibility = View.GONE imageContainer?.visibility = View.GONE
editorImage?.visibility = View.GONE editorImage?.visibility = View.GONE
editorImage?.isEnabled = false editorImage?.isEnabled = false
setColors( setColors(
intent.getIntExtra( intent.getIntExtra(
EXTRA_PALETTE, EXTRA_PALETTE,
ATHUtil.resolveColor(this, R.attr.colorPrimary) ATHUtil.resolveColor(this, R.attr.colorPrimary)
) )
) )
} }
protected fun dataChanged() { protected fun dataChanged() {
showFab() showFab()
} }
private fun showFab() { private fun showFab() {
saveFab.animate().setDuration(500).setInterpolator(OvershootInterpolator()).scaleX(1f) saveFab.animate().setDuration(500).setInterpolator(OvershootInterpolator()).scaleX(1f)
.scaleY(1f).start() .scaleY(1f).start()
saveFab.isEnabled = true saveFab.isEnabled = true
} }
private fun hideFab() { private fun hideFab() {
saveFab.animate().setDuration(500).setInterpolator(OvershootInterpolator()).scaleX(0.0f) saveFab.animate().setDuration(500).setInterpolator(OvershootInterpolator()).scaleX(0.0f)
.scaleY(0.0f).start() .scaleY(0.0f).start()
saveFab.isEnabled = false saveFab.isEnabled = false
} }
protected fun setImageBitmap(bitmap: Bitmap?, bgColor: Int) { protected fun setImageBitmap(bitmap: Bitmap?, bgColor: Int) {
if (bitmap == null) { if (bitmap == null) {
editorImage.setImageResource(code.name.monkey.retromusic.R.drawable.default_album_art) editorImage.setImageResource(code.name.monkey.retromusic.R.drawable.default_album_art)
} else { } else {
editorImage.setImageBitmap(bitmap) editorImage.setImageBitmap(bitmap)
} }
setColors(bgColor) setColors(bgColor)
} }
protected open fun setColors(color: Int) { protected open fun setColors(color: Int) {
paletteColorPrimary = color paletteColorPrimary = color
} }
protected fun writeValuesToFiles( protected fun writeValuesToFiles(
fieldKeyValueMap: Map<FieldKey, String>, artworkInfo: ArtworkInfo? fieldKeyValueMap: Map<FieldKey, String>, artworkInfo: ArtworkInfo?
) { ) {
RetroUtil.hideSoftKeyboard(this) RetroUtil.hideSoftKeyboard(this)
hideFab() hideFab()
savedSongPaths = getSongPaths() savedSongPaths = getSongPaths()
savedTags = fieldKeyValueMap savedTags = fieldKeyValueMap
savedArtworkInfo = artworkInfo savedArtworkInfo = artworkInfo
if (!SAFUtil.isSAFRequired(savedSongPaths)) { if (!SAFUtil.isSAFRequired(savedSongPaths)) {
writeTags(savedSongPaths) writeTags(savedSongPaths)
} else { } else {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
if (SAFUtil.isSDCardAccessGranted(this)) { if (SAFUtil.isSDCardAccessGranted(this)) {
writeTags(savedSongPaths) writeTags(savedSongPaths)
} else { } else {
startActivityForResult( startActivityForResult(
Intent(this, SAFGuideActivity::class.java), Intent(this, SAFGuideActivity::class.java),
SAFGuideActivity.REQUEST_CODE_SAF_GUIDE SAFGuideActivity.REQUEST_CODE_SAF_GUIDE
) )
} }
} }
} }
} }
private fun writeTags(paths: List<String>?) { private fun writeTags(paths: List<String>?) {
WriteTagsAsyncTask(this).execute( WriteTagsAsyncTask(this).execute(
WriteTagsAsyncTask.LoadingInfo( WriteTagsAsyncTask.LoadingInfo(
paths, paths,
savedTags, savedTags,
savedArtworkInfo savedArtworkInfo
) )
) )
} }
override fun onActivityResult(requestCode: Int, resultCode: Int, intent: Intent?) { override fun onActivityResult(requestCode: Int, resultCode: Int, intent: Intent?) {
super.onActivityResult(requestCode, resultCode, intent) super.onActivityResult(requestCode, resultCode, intent)
when (requestCode) { when (requestCode) {
REQUEST_CODE_SELECT_IMAGE -> if (resultCode == Activity.RESULT_OK) { REQUEST_CODE_SELECT_IMAGE -> if (resultCode == Activity.RESULT_OK) {
intent?.data?.let { intent?.data?.let {
loadImageFromFile(it) loadImageFromFile(it)
} }
} }
SAFGuideActivity.REQUEST_CODE_SAF_GUIDE -> { SAFGuideActivity.REQUEST_CODE_SAF_GUIDE -> {
SAFUtil.openTreePicker(this) SAFUtil.openTreePicker(this)
} }
SAFUtil.REQUEST_SAF_PICK_TREE -> { SAFUtil.REQUEST_SAF_PICK_TREE -> {
if (resultCode == Activity.RESULT_OK) { if (resultCode == Activity.RESULT_OK) {
SAFUtil.saveTreeUri(this, intent) SAFUtil.saveTreeUri(this, intent)
writeTags(savedSongPaths) writeTags(savedSongPaths)
} }
} }
SAFUtil.REQUEST_SAF_PICK_FILE -> { SAFUtil.REQUEST_SAF_PICK_FILE -> {
if (resultCode == Activity.RESULT_OK) { if (resultCode == Activity.RESULT_OK) {
writeTags(Collections.singletonList(currentSongPath + SAFUtil.SEPARATOR + intent!!.dataString)) writeTags(Collections.singletonList(currentSongPath + SAFUtil.SEPARATOR + intent!!.dataString))
} }
} }
} }
} }
protected abstract fun loadImageFromFile(selectedFile: Uri?) protected abstract fun loadImageFromFile(selectedFile: Uri?)
private fun getAudioFile(path: String): AudioFile { private fun getAudioFile(path: String): AudioFile {
return try { return try {
AudioFileIO.read(File(path)) AudioFileIO.read(File(path))
} catch (e: Exception) { } catch (e: Exception) {
Log.e(TAG, "Could not read audio file $path", e) Log.e(TAG, "Could not read audio file $path", e)
AudioFile() AudioFile()
} }
} }
class ArtworkInfo constructor(val albumId: Int, val artwork: Bitmap?) class ArtworkInfo constructor(val albumId: Int, val artwork: Bitmap?)
companion object { companion object {
const val EXTRA_ID = "extra_id" const val EXTRA_ID = "extra_id"
const val EXTRA_PALETTE = "extra_palette" const val EXTRA_PALETTE = "extra_palette"
private val TAG = AbsTagEditorActivity::class.java.simpleName private val TAG = AbsTagEditorActivity::class.java.simpleName
private const val REQUEST_CODE_SELECT_IMAGE = 1000 private const val REQUEST_CODE_SELECT_IMAGE = 1000
} }
} }

View file

@ -31,218 +31,230 @@ import com.bumptech.glide.load.engine.DiskCacheStrategy
import com.bumptech.glide.request.animation.GlideAnimation import com.bumptech.glide.request.animation.GlideAnimation
import com.bumptech.glide.request.target.SimpleTarget import com.bumptech.glide.request.target.SimpleTarget
import io.reactivex.disposables.CompositeDisposable import io.reactivex.disposables.CompositeDisposable
import kotlinx.android.synthetic.main.activity_album_tag_editor.* import kotlinx.android.synthetic.main.activity_album_tag_editor.albumArtistContainer
import kotlinx.android.synthetic.main.activity_album_tag_editor.albumArtistText
import kotlinx.android.synthetic.main.activity_album_tag_editor.albumText
import kotlinx.android.synthetic.main.activity_album_tag_editor.albumTitleContainer
import kotlinx.android.synthetic.main.activity_album_tag_editor.genreContainer
import kotlinx.android.synthetic.main.activity_album_tag_editor.genreTitle
import kotlinx.android.synthetic.main.activity_album_tag_editor.toolbar
import kotlinx.android.synthetic.main.activity_album_tag_editor.yearContainer
import kotlinx.android.synthetic.main.activity_album_tag_editor.yearTitle
import org.jaudiotagger.tag.FieldKey import org.jaudiotagger.tag.FieldKey
import java.util.* import java.util.ArrayList
import java.util.EnumMap
class AlbumTagEditorActivity : AbsTagEditorActivity(), TextWatcher { class AlbumTagEditorActivity : AbsTagEditorActivity(), TextWatcher {
override val contentViewLayout: Int override val contentViewLayout: Int
get() = R.layout.activity_album_tag_editor get() = R.layout.activity_album_tag_editor
override fun loadImageFromFile(selectedFileUri: Uri?) { override fun loadImageFromFile(selectedFileUri: Uri?) {
Glide.with(this@AlbumTagEditorActivity).load(selectedFileUri).asBitmap() Glide.with(this@AlbumTagEditorActivity).load(selectedFileUri).asBitmap()
.transcode(BitmapPaletteTranscoder(this), BitmapPaletteWrapper::class.java) .transcode(BitmapPaletteTranscoder(this), BitmapPaletteWrapper::class.java)
.diskCacheStrategy(DiskCacheStrategy.NONE).skipMemoryCache(true) .diskCacheStrategy(DiskCacheStrategy.NONE).skipMemoryCache(true)
.into(object : SimpleTarget<BitmapPaletteWrapper>() { .into(object : SimpleTarget<BitmapPaletteWrapper>() {
override fun onResourceReady( override fun onResourceReady(
resource: BitmapPaletteWrapper?, resource: BitmapPaletteWrapper?,
glideAnimation: GlideAnimation<in BitmapPaletteWrapper>? glideAnimation: GlideAnimation<in BitmapPaletteWrapper>?
) { ) {
getColor(resource?.palette, Color.TRANSPARENT) getColor(resource?.palette, Color.TRANSPARENT)
albumArtBitmap = resource?.bitmap?.let { ImageUtil.resizeBitmap(it, 2048) } albumArtBitmap = resource?.bitmap?.let { ImageUtil.resizeBitmap(it, 2048) }
setImageBitmap(albumArtBitmap, getColor(resource?.palette, ATHUtil.resolveColor(this@AlbumTagEditorActivity, R.attr.defaultFooterColor))) setImageBitmap(
deleteAlbumArt = false albumArtBitmap,
dataChanged() getColor(
setResult(Activity.RESULT_OK) resource?.palette,
} ATHUtil.resolveColor(this@AlbumTagEditorActivity, R.attr.defaultFooterColor)
)
)
deleteAlbumArt = false
dataChanged()
setResult(Activity.RESULT_OK)
}
override fun onLoadFailed(e: Exception?, errorDrawable: Drawable?) { override fun onLoadFailed(e: Exception?, errorDrawable: Drawable?) {
super.onLoadFailed(e, errorDrawable) super.onLoadFailed(e, errorDrawable)
Toast.makeText(this@AlbumTagEditorActivity, e.toString(), Toast.LENGTH_LONG) Toast.makeText(this@AlbumTagEditorActivity, e.toString(), Toast.LENGTH_LONG)
.show() .show()
} }
}) })
} }
private var albumArtBitmap: Bitmap? = null private var albumArtBitmap: Bitmap? = null
private var deleteAlbumArt: Boolean = false private var deleteAlbumArt: Boolean = false
private var lastFMRestClient: LastFMRestClient? = null private var lastFMRestClient: LastFMRestClient? = null
private val disposable = CompositeDisposable() private val disposable = CompositeDisposable()
private fun setupToolbar() { private fun setupToolbar() {
applyToolbar(toolbar) applyToolbar(toolbar)
supportActionBar?.setDisplayShowHomeEnabled(true) supportActionBar?.setDisplayShowHomeEnabled(true)
} }
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
setDrawUnderStatusBar() setDrawUnderStatusBar()
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
lastFMRestClient = LastFMRestClient(this) lastFMRestClient = LastFMRestClient(this)
setUpViews() setUpViews()
setupToolbar() setupToolbar()
} }
private fun setUpViews() { private fun setUpViews() {
fillViewsWithFileTags() fillViewsWithFileTags()
MaterialUtil.setTint(yearContainer, false) MaterialUtil.setTint(yearContainer, false)
MaterialUtil.setTint(genreContainer, false) MaterialUtil.setTint(genreContainer, false)
MaterialUtil.setTint(albumTitleContainer, false) MaterialUtil.setTint(albumTitleContainer, false)
MaterialUtil.setTint(albumArtistContainer, false) MaterialUtil.setTint(albumArtistContainer, false)
albumText.appHandleColor().addTextChangedListener(this) albumText.appHandleColor().addTextChangedListener(this)
albumArtistText.appHandleColor().addTextChangedListener(this) albumArtistText.appHandleColor().addTextChangedListener(this)
genreTitle.appHandleColor().addTextChangedListener(this) genreTitle.appHandleColor().addTextChangedListener(this)
yearTitle.appHandleColor().addTextChangedListener(this) yearTitle.appHandleColor().addTextChangedListener(this)
} }
private fun fillViewsWithFileTags() { private fun fillViewsWithFileTags() {
albumText.setText(albumTitle) albumText.setText(albumTitle)
albumArtistText.setText(albumArtistName) albumArtistText.setText(albumArtistName)
genreTitle.setText(genreName) genreTitle.setText(genreName)
yearTitle.setText(songYear) yearTitle.setText(songYear)
} }
override fun loadCurrentImage() { override fun loadCurrentImage() {
val bitmap = albumArt val bitmap = albumArt
setImageBitmap( setImageBitmap(
bitmap, bitmap,
getColor( getColor(
generatePalette(bitmap), generatePalette(bitmap),
ATHUtil.resolveColor(this, R.attr.defaultFooterColor) ATHUtil.resolveColor(this, R.attr.defaultFooterColor)
) )
) )
deleteAlbumArt = false deleteAlbumArt = false
} }
override fun onPause() { override fun onPause() {
super.onPause() super.onPause()
disposable.clear() disposable.clear()
} }
private fun extractDetails(lastFmAlbum: LastFmAlbum) { private fun extractDetails(lastFmAlbum: LastFmAlbum) {
if (lastFmAlbum.album != null) { if (lastFmAlbum.album != null) {
val url = LastFMUtil.getLargestAlbumImageUrl(lastFmAlbum.album.image) val url = LastFMUtil.getLargestAlbumImageUrl(lastFmAlbum.album.image)
if (!TextUtils.isEmpty(url) && url.trim { it <= ' ' }.isNotEmpty()) { if (!TextUtils.isEmpty(url) && url.trim { it <= ' ' }.isNotEmpty()) {
Glide.with(this@AlbumTagEditorActivity).load(url).asBitmap() Glide.with(this@AlbumTagEditorActivity).load(url).asBitmap()
.transcode(BitmapPaletteTranscoder(this), BitmapPaletteWrapper::class.java) .transcode(BitmapPaletteTranscoder(this), BitmapPaletteWrapper::class.java)
.diskCacheStrategy(DiskCacheStrategy.SOURCE).error(R.drawable.default_album_art) .diskCacheStrategy(DiskCacheStrategy.SOURCE).error(R.drawable.default_album_art)
.into(object : SimpleTarget<BitmapPaletteWrapper>() { .into(object : SimpleTarget<BitmapPaletteWrapper>() {
override fun onLoadFailed( override fun onLoadFailed(
e: java.lang.Exception?, e: java.lang.Exception?,
errorDrawable: Drawable? errorDrawable: Drawable?
) { ) {
super.onLoadFailed(e, errorDrawable) super.onLoadFailed(e, errorDrawable)
Toast.makeText( Toast.makeText(
this@AlbumTagEditorActivity, this@AlbumTagEditorActivity,
e.toString(), e.toString(),
Toast.LENGTH_LONG Toast.LENGTH_LONG
).show() ).show()
} }
override fun onResourceReady( override fun onResourceReady(
resource: BitmapPaletteWrapper?, resource: BitmapPaletteWrapper?,
glideAnimation: GlideAnimation<in BitmapPaletteWrapper>? glideAnimation: GlideAnimation<in BitmapPaletteWrapper>?
) { ) {
albumArtBitmap = resource?.bitmap?.let { albumArtBitmap = resource?.bitmap?.let {
ImageUtil.resizeBitmap( ImageUtil.resizeBitmap(
it, it,
2048 2048
) )
} }
setImageBitmap( setImageBitmap(
albumArtBitmap, albumArtBitmap,
getColor( getColor(
resource?.palette, resource?.palette,
ATHUtil.resolveColor( ATHUtil.resolveColor(
this@AlbumTagEditorActivity, this@AlbumTagEditorActivity,
R.attr.defaultFooterColor R.attr.defaultFooterColor
) )
) )
) )
deleteAlbumArt = false deleteAlbumArt = false
dataChanged() dataChanged()
setResult(RESULT_OK) setResult(RESULT_OK)
} }
}) })
return return
} }
if (lastFmAlbum.album.tags.tag.size > 0) { if (lastFmAlbum.album.tags.tag.size > 0) {
genreTitle.setText(lastFmAlbum.album.tags.tag[0].name) genreTitle.setText(lastFmAlbum.album.tags.tag[0].name)
} }
}
toastLoadingFailed()
}
} private fun toastLoadingFailed() {
toastLoadingFailed() Toast.makeText(
} this@AlbumTagEditorActivity,
R.string.could_not_download_album_cover,
Toast.LENGTH_SHORT
).show()
}
private fun toastLoadingFailed() { override fun searchImageOnWeb() {
Toast.makeText( searchWebFor(albumText.text.toString(), albumArtistText.text.toString())
this@AlbumTagEditorActivity, }
R.string.could_not_download_album_cover,
Toast.LENGTH_SHORT
).show()
}
override fun searchImageOnWeb() { override fun deleteImage() {
searchWebFor(albumText.text.toString(), albumArtistText.text.toString()) setImageBitmap(
} BitmapFactory.decodeResource(resources, R.drawable.default_album_art),
ATHUtil.resolveColor(this, R.attr.defaultFooterColor)
)
deleteAlbumArt = true
dataChanged()
}
override fun deleteImage() { override fun save() {
setImageBitmap( val fieldKeyValueMap = EnumMap<FieldKey, String>(FieldKey::class.java)
BitmapFactory.decodeResource(resources, R.drawable.default_album_art), fieldKeyValueMap[FieldKey.ALBUM] = albumText.text.toString()
ATHUtil.resolveColor(this, R.attr.defaultFooterColor) //android seems not to recognize album_artist field so we additionally write the normal artist field
) fieldKeyValueMap[FieldKey.ARTIST] = albumArtistText.text.toString()
deleteAlbumArt = true fieldKeyValueMap[FieldKey.ALBUM_ARTIST] = albumArtistText.text.toString()
dataChanged() fieldKeyValueMap[FieldKey.GENRE] = genreTitle.text.toString()
} fieldKeyValueMap[FieldKey.YEAR] = yearTitle.text.toString()
override fun save() { writeValuesToFiles(
val fieldKeyValueMap = EnumMap<FieldKey, String>(FieldKey::class.java) fieldKeyValueMap, if (deleteAlbumArt) ArtworkInfo(id, null)
fieldKeyValueMap[FieldKey.ALBUM] = albumText.text.toString() else if (albumArtBitmap == null) null else ArtworkInfo(id, albumArtBitmap!!)
//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( override fun getSongPaths(): List<String> {
fieldKeyValueMap, if (deleteAlbumArt) ArtworkInfo(id, null) val songs = AlbumLoader.getAlbum(this, id).songs
else if (albumArtBitmap == null) null else ArtworkInfo(id, albumArtBitmap!!) val paths = ArrayList<String>(songs!!.size)
) for (song in songs) {
} paths.add(song.data)
}
return paths
}
override fun getSongPaths(): List<String> { override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {
val songs = AlbumLoader.getAlbum(this, id).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 onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) { override fun setColors(color: Int) {
super.setColors(color)
saveFab.backgroundTintList = ColorStateList.valueOf(color)
}
} companion object {
override fun afterTextChanged(s: Editable) { val TAG: String = AlbumTagEditorActivity::class.java.simpleName
dataChanged() }
}
override fun setColors(color: Int) {
super.setColors(color)
saveFab.backgroundTintList = ColorStateList.valueOf(color)
}
companion object {
val TAG: String = AlbumTagEditorActivity::class.java.simpleName
}
} }

View file

@ -9,16 +9,34 @@ import code.name.monkey.retromusic.R
import code.name.monkey.retromusic.extensions.appHandleColor import code.name.monkey.retromusic.extensions.appHandleColor
import code.name.monkey.retromusic.extensions.applyToolbar import code.name.monkey.retromusic.extensions.applyToolbar
import code.name.monkey.retromusic.loaders.SongLoader import code.name.monkey.retromusic.loaders.SongLoader
import kotlinx.android.synthetic.main.activity_song_tag_editor.* import kotlinx.android.synthetic.main.activity_song_tag_editor.albumArtistContainer
import kotlinx.android.synthetic.main.activity_song_tag_editor.albumArtistText
import kotlinx.android.synthetic.main.activity_song_tag_editor.albumText
import kotlinx.android.synthetic.main.activity_song_tag_editor.albumTextContainer
import kotlinx.android.synthetic.main.activity_song_tag_editor.artistContainer
import kotlinx.android.synthetic.main.activity_song_tag_editor.artistText
import kotlinx.android.synthetic.main.activity_song_tag_editor.composerContainer
import kotlinx.android.synthetic.main.activity_song_tag_editor.genreContainer
import kotlinx.android.synthetic.main.activity_song_tag_editor.genreText
import kotlinx.android.synthetic.main.activity_song_tag_editor.lyricsContainer
import kotlinx.android.synthetic.main.activity_song_tag_editor.lyricsText
import kotlinx.android.synthetic.main.activity_song_tag_editor.songComposerText
import kotlinx.android.synthetic.main.activity_song_tag_editor.songText
import kotlinx.android.synthetic.main.activity_song_tag_editor.songTextContainer
import kotlinx.android.synthetic.main.activity_song_tag_editor.toolbar
import kotlinx.android.synthetic.main.activity_song_tag_editor.trackNumberContainer
import kotlinx.android.synthetic.main.activity_song_tag_editor.trackNumberText
import kotlinx.android.synthetic.main.activity_song_tag_editor.yearContainer
import kotlinx.android.synthetic.main.activity_song_tag_editor.yearText
import org.jaudiotagger.tag.FieldKey import org.jaudiotagger.tag.FieldKey
import java.util.* import java.util.ArrayList
import java.util.EnumMap
class SongTagEditorActivity : AbsTagEditorActivity(), TextWatcher { class SongTagEditorActivity : AbsTagEditorActivity(), TextWatcher {
override val contentViewLayout: Int override val contentViewLayout: Int
get() = R.layout.activity_song_tag_editor get() = R.layout.activity_song_tag_editor
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
@ -64,15 +82,12 @@ class SongTagEditorActivity : AbsTagEditorActivity(), TextWatcher {
} }
override fun loadCurrentImage() { override fun loadCurrentImage() {
} }
override fun searchImageOnWeb() { override fun searchImageOnWeb() {
} }
override fun deleteImage() { override fun deleteImage() {
} }
override fun save() { override fun save() {
@ -96,15 +111,12 @@ class SongTagEditorActivity : AbsTagEditorActivity(), TextWatcher {
} }
override fun loadImageFromFile(selectedFile: Uri?) { override fun loadImageFromFile(selectedFile: Uri?) {
} }
override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) { override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {
} }
override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) { override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {
} }
override fun afterTextChanged(s: Editable) { override fun afterTextChanged(s: Editable) {

View file

@ -7,19 +7,14 @@ import android.graphics.Bitmap;
import android.media.MediaScannerConnection; import android.media.MediaScannerConnection;
import android.net.Uri; import android.net.Uri;
import android.os.Build; import android.os.Build;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; 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;
import code.name.monkey.retromusic.util.SAFUtil;
import com.google.android.material.dialog.MaterialAlertDialogBuilder; import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import org.jaudiotagger.audio.AudioFile;
import org.jaudiotagger.audio.AudioFileIO;
import org.jaudiotagger.tag.FieldKey;
import org.jaudiotagger.tag.Tag;
import org.jaudiotagger.tag.images.Artwork;
import org.jaudiotagger.tag.images.ArtworkFactory;
import java.io.File; import java.io.File;
import java.io.FileOutputStream; import java.io.FileOutputStream;
import java.io.IOException; import java.io.IOException;
@ -27,16 +22,35 @@ import java.lang.ref.WeakReference;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.Map; import java.util.Map;
import org.jaudiotagger.audio.AudioFile;
import code.name.monkey.retromusic.R; import org.jaudiotagger.audio.AudioFileIO;
import code.name.monkey.retromusic.misc.DialogAsyncTask; import org.jaudiotagger.tag.FieldKey;
import code.name.monkey.retromusic.misc.UpdateToastMediaScannerCompletionListener; import org.jaudiotagger.tag.Tag;
import code.name.monkey.retromusic.util.MusicUtil; import org.jaudiotagger.tag.images.Artwork;
import code.name.monkey.retromusic.util.SAFUtil; import org.jaudiotagger.tag.images.ArtworkFactory;
public class WriteTagsAsyncTask extends public class WriteTagsAsyncTask extends
DialogAsyncTask<WriteTagsAsyncTask.LoadingInfo, Integer, String[]> { DialogAsyncTask<WriteTagsAsyncTask.LoadingInfo, Integer, String[]> {
public static class LoadingInfo {
@Nullable
final Map<FieldKey, String> fieldKeyValueMap;
final Collection<String> filePaths;
@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;
}
}
private WeakReference<Activity> activity; private WeakReference<Activity> activity;
public WriteTagsAsyncTask(@NonNull Activity activity) { public WriteTagsAsyncTask(@NonNull Activity activity) {
@ -44,6 +58,16 @@ public class WriteTagsAsyncTask extends
this.activity = new WeakReference<>(activity); this.activity = new WeakReference<>(activity);
} }
@NonNull
@Override
protected Dialog createDialog(@NonNull Context context) {
return new MaterialAlertDialogBuilder(context)
.setTitle(R.string.saving_changes)
.setCancelable(false)
.setView(R.layout.loading)
.create();
}
@Override @Override
protected String[] doInBackground(LoadingInfo... params) { protected String[] doInBackground(LoadingInfo... params) {
try { try {
@ -120,8 +144,9 @@ public class WriteTagsAsyncTask extends
if (Build.VERSION.SDK_INT == Build.VERSION_CODES.KITKAT) { if (Build.VERSION.SDK_INT == Build.VERSION_CODES.KITKAT) {
paths = new ArrayList<>(info.filePaths.size()); paths = new ArrayList<>(info.filePaths.size());
for (String path : info.filePaths) { for (String path : info.filePaths) {
if (path.contains(SAFUtil.SEPARATOR)) if (path.contains(SAFUtil.SEPARATOR)) {
path = path.split(SAFUtil.SEPARATOR)[0]; path = path.split(SAFUtil.SEPARATOR)[0];
}
paths.add(path); paths.add(path);
} }
} }
@ -133,33 +158,16 @@ public class WriteTagsAsyncTask extends
} }
} }
@Override
protected void onPostExecute(String[] toBeScanned) {
super.onPostExecute(toBeScanned);
scan(toBeScanned);
}
@Override @Override
protected void onCancelled(String[] toBeScanned) { protected void onCancelled(String[] toBeScanned) {
super.onCancelled(toBeScanned); super.onCancelled(toBeScanned);
scan(toBeScanned); scan(toBeScanned);
} }
private void scan(String[] toBeScanned) {
Activity activity = this.activity.get();
if (activity != null) {
MediaScannerConnection.scanFile(activity, toBeScanned, null, new UpdateToastMediaScannerCompletionListener(activity, toBeScanned));
}
}
@NonNull
@Override @Override
protected Dialog createDialog(@NonNull Context context) { protected void onPostExecute(String[] toBeScanned) {
return new MaterialAlertDialogBuilder(context) super.onPostExecute(toBeScanned);
.setTitle(R.string.saving_changes) scan(toBeScanned);
.setCancelable(false)
.setView(R.layout.loading)
.create();
} }
@Override @Override
@ -169,20 +177,11 @@ public class WriteTagsAsyncTask extends
//((MaterialDialog) dialog).setProgress(values[0]); //((MaterialDialog) dialog).setProgress(values[0]);
} }
public static class LoadingInfo { private void scan(String[] toBeScanned) {
Activity activity = this.activity.get();
final Collection<String> filePaths; if (activity != null) {
@Nullable MediaScannerConnection.scanFile(activity, toBeScanned, null,
final Map<FieldKey, String> fieldKeyValueMap; new UpdateToastMediaScannerCompletionListener(activity, toBeScanned));
@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;
} }
} }
} }

View file

@ -15,8 +15,11 @@
package code.name.monkey.retromusic.appwidgets package code.name.monkey.retromusic.appwidgets
import android.app.PendingIntent import android.app.PendingIntent
import android.content.* import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.graphics.Bitmap import android.graphics.Bitmap
import android.graphics.Color
import android.graphics.drawable.Drawable import android.graphics.drawable.Drawable
import android.text.TextUtils import android.text.TextUtils
import android.view.View import android.view.View
@ -28,203 +31,208 @@ import code.name.monkey.retromusic.appwidgets.base.BaseAppWidget
import code.name.monkey.retromusic.glide.SongGlideRequest import code.name.monkey.retromusic.glide.SongGlideRequest
import code.name.monkey.retromusic.glide.palette.BitmapPaletteWrapper import code.name.monkey.retromusic.glide.palette.BitmapPaletteWrapper
import code.name.monkey.retromusic.service.MusicService import code.name.monkey.retromusic.service.MusicService
import code.name.monkey.retromusic.service.MusicService.* import code.name.monkey.retromusic.service.MusicService.ACTION_REWIND
import code.name.monkey.retromusic.util.* import code.name.monkey.retromusic.service.MusicService.ACTION_SKIP
import code.name.monkey.retromusic.service.MusicService.ACTION_TOGGLE_PAUSE
import code.name.monkey.retromusic.util.ImageUtil
import code.name.monkey.retromusic.util.RetroUtil
import com.bumptech.glide.Glide import com.bumptech.glide.Glide
import com.bumptech.glide.request.animation.GlideAnimation import com.bumptech.glide.request.animation.GlideAnimation
import com.bumptech.glide.request.target.* import com.bumptech.glide.request.target.SimpleTarget
import com.bumptech.glide.request.target.Target import com.bumptech.glide.request.target.Target
class AppWidgetClassic : BaseAppWidget() { class AppWidgetClassic : BaseAppWidget() {
private var target: Target<BitmapPaletteWrapper>? = null // for cancellation private var target: Target<BitmapPaletteWrapper>? = null // for cancellation
/** /**
* Initialize given widgets to default state, where we launch Music on default click and hide * Initialize given widgets to default state, where we launch Music on default click and hide
* actions if service not running. * actions if service not running.
*/ */
override fun defaultAppWidget(context: Context, appWidgetIds: IntArray) { override fun defaultAppWidget(context: Context, appWidgetIds: IntArray) {
val appWidgetView = RemoteViews(context.packageName, R.layout.app_widget_classic) val appWidgetView = RemoteViews(context.packageName, R.layout.app_widget_classic)
appWidgetView.setViewVisibility(R.id.media_titles, View.INVISIBLE) appWidgetView.setViewVisibility(R.id.media_titles, View.INVISIBLE)
appWidgetView.setImageViewResource(R.id.image, R.drawable.default_album_art) appWidgetView.setImageViewResource(R.id.image, R.drawable.default_album_art)
appWidgetView.setImageViewBitmap( appWidgetView.setImageViewBitmap(
R.id.button_next, createBitmap( R.id.button_next,
RetroUtil.getTintedVectorDrawable( createBitmap(
context, RetroUtil.getTintedVectorDrawable(
R.drawable.ic_skip_next_white_24dp, context,
MaterialValueHelper.getSecondaryTextColor( R.drawable.ic_skip_next_white_24dp,
context, true MaterialValueHelper.getSecondaryTextColor(context, true)
) )!!, 1f
)!!, 1f )
) )
) appWidgetView.setImageViewBitmap(
appWidgetView.setImageViewBitmap( R.id.button_prev,
R.id.button_prev, createBitmap( createBitmap(
RetroUtil.getTintedVectorDrawable( RetroUtil.getTintedVectorDrawable(
context, context,
R.drawable.ic_skip_previous_white_24dp, R.drawable.ic_skip_previous_white_24dp,
MaterialValueHelper.getSecondaryTextColor( MaterialValueHelper.getSecondaryTextColor(context, true)
context, true )!!, 1f
) )
)!!, 1f )
) appWidgetView.setImageViewBitmap(
) R.id.button_toggle_play_pause,
appWidgetView.setImageViewBitmap( createBitmap(
R.id.button_toggle_play_pause, createBitmap( RetroUtil.getTintedVectorDrawable(
RetroUtil.getTintedVectorDrawable( context,
context, R.drawable.ic_play_arrow_white_32dp,
R.drawable.ic_play_arrow_white_32dp, MaterialValueHelper.getSecondaryTextColor(context, true)
MaterialValueHelper.getSecondaryTextColor( )!!, 1f
context, true )
) )
)!!, 1f
)
)
linkButtons(context, appWidgetView) linkButtons(context, appWidgetView)
pushUpdate(context, appWidgetIds, appWidgetView) pushUpdate(context, appWidgetIds, appWidgetView)
} }
/** /**
* Update all active widget instances by pushing changes * Update all active widget instances by pushing changes
*/ */
override fun performUpdate(service: MusicService, appWidgetIds: IntArray?) { override fun performUpdate(service: MusicService, appWidgetIds: IntArray?) {
val appWidgetView = RemoteViews(service.packageName, R.layout.app_widget_classic) val appWidgetView = RemoteViews(service.packageName, R.layout.app_widget_classic)
val isPlaying = service.isPlaying val isPlaying = service.isPlaying
val song = service.currentSong val song = service.currentSong
// Set the titles and artwork // Set the titles and artwork
if (TextUtils.isEmpty(song.title) && TextUtils.isEmpty(song.artistName)) { if (TextUtils.isEmpty(song.title) && TextUtils.isEmpty(song.artistName)) {
appWidgetView.setViewVisibility(R.id.media_titles, View.INVISIBLE) appWidgetView.setViewVisibility(R.id.media_titles, View.INVISIBLE)
} else { } else {
appWidgetView.setViewVisibility(R.id.media_titles, View.VISIBLE) appWidgetView.setViewVisibility(R.id.media_titles, View.VISIBLE)
appWidgetView.setTextViewText(R.id.title, song.title) appWidgetView.setTextViewText(R.id.title, song.title)
appWidgetView.setTextViewText(R.id.text, getSongArtistAndAlbum(song)) appWidgetView.setTextViewText(R.id.text, getSongArtistAndAlbum(song))
} }
// Link actions buttons to intents // Link actions buttons to intents
linkButtons(service, appWidgetView) linkButtons(service, appWidgetView)
if (imageSize == 0) { if (imageSize == 0) {
imageSize = service.resources.getDimensionPixelSize(R.dimen.app_widget_classic_image_size) imageSize = service.resources.getDimensionPixelSize(R.dimen.app_widget_classic_image_size)
} }
if (cardRadius == 0f) { if (cardRadius == 0f) {
cardRadius = service.resources.getDimension(R.dimen.app_widget_card_radius) cardRadius = service.resources.getDimension(R.dimen.app_widget_card_radius)
} }
// Load the album cover async and push the update on completion // Load the album cover async and push the update on completion
val appContext = service.applicationContext val appContext = service.applicationContext
service.runOnUiThread { service.runOnUiThread {
if (target != null) { if (target != null) {
Glide.clear(target) Glide.clear(target)
} }
target = SongGlideRequest.Builder.from(Glide.with(service), song) target = SongGlideRequest.Builder.from(Glide.with(service), song)
.checkIgnoreMediaStore(service).generatePalette(service).build().centerCrop() .checkIgnoreMediaStore(service).generatePalette(service).build().centerCrop()
.into(object : SimpleTarget<BitmapPaletteWrapper>(imageSize, imageSize) { .into(object : SimpleTarget<BitmapPaletteWrapper>(imageSize, imageSize) {
override fun onResourceReady( override fun onResourceReady(
resource: BitmapPaletteWrapper, resource: BitmapPaletteWrapper,
glideAnimation: GlideAnimation<in BitmapPaletteWrapper> glideAnimation: GlideAnimation<in BitmapPaletteWrapper>
) { ) {
val palette = resource.palette val palette = resource.palette
update( update(
resource.bitmap, palette.getVibrantColor( resource.bitmap,
palette.getMutedColor( palette.getVibrantColor(
MaterialValueHelper.getSecondaryTextColor( palette.getMutedColor(
service, true MaterialValueHelper.getSecondaryTextColor(
) service,
) true
) )
) )
} )
)
}
override fun onLoadFailed(e: Exception?, errorDrawable: Drawable?) { override fun onLoadFailed(e: Exception?, errorDrawable: Drawable?) {
super.onLoadFailed(e, errorDrawable) super.onLoadFailed(e, errorDrawable)
update(null, MaterialValueHelper.getSecondaryTextColor(service, true)) update(null, Color.WHITE)
} }
private fun update(bitmap: Bitmap?, color: Int) { private fun update(bitmap: Bitmap?, color: Int) {
// Set correct drawable for pause state // Set correct drawable for pause state
val playPauseRes = if (isPlaying) R.drawable.ic_pause_white_24dp else R.drawable.ic_play_arrow_white_24dp val playPauseRes =
appWidgetView.setImageViewBitmap( if (isPlaying) R.drawable.ic_pause_white_24dp else R.drawable.ic_play_arrow_white_24dp
R.id.button_toggle_play_pause, ImageUtil.createBitmap( appWidgetView.setImageViewBitmap(
ImageUtil.getTintedVectorDrawable( R.id.button_toggle_play_pause,
service, playPauseRes, color ImageUtil.createBitmap(ImageUtil.getTintedVectorDrawable(service, playPauseRes, color))
) )
)
)
// Set prev/next button drawables // Set prev/next button drawables
appWidgetView.setImageViewBitmap( appWidgetView.setImageViewBitmap(
R.id.button_next, ImageUtil.createBitmap( R.id.button_next,
ImageUtil.getTintedVectorDrawable( ImageUtil.createBitmap(
service, R.drawable.ic_skip_next_white_24dp, color ImageUtil.getTintedVectorDrawable(
) service,
) R.drawable.ic_skip_next_white_24dp,
) color
appWidgetView.setImageViewBitmap( )
R.id.button_prev, ImageUtil.createBitmap( )
ImageUtil.getTintedVectorDrawable( )
service, R.drawable.ic_skip_previous_white_24dp, color appWidgetView.setImageViewBitmap(
) R.id.button_prev,
) ImageUtil.createBitmap(
) ImageUtil.getTintedVectorDrawable(
service,
R.drawable.ic_skip_previous_white_24dp,
color
)
)
)
val image = getAlbumArtDrawable(service.resources, bitmap) val image = getAlbumArtDrawable(service.resources, bitmap)
val roundedBitmap = createRoundedBitmap( val roundedBitmap =
image, imageSize, imageSize, cardRadius, 0F, cardRadius, 0F createRoundedBitmap(image, imageSize, imageSize, cardRadius, 0F, cardRadius, 0F)
) appWidgetView.setImageViewBitmap(R.id.image, roundedBitmap)
appWidgetView.setImageViewBitmap(R.id.image, roundedBitmap)
pushUpdate(appContext, appWidgetIds, appWidgetView) pushUpdate(appContext, appWidgetIds, appWidgetView)
} }
}) })
} }
} }
/** /**
* Link up various button actions using [PendingIntent]. * Link up various button actions using [PendingIntent].
*/ */
private fun linkButtons(context: Context, views: RemoteViews) { private fun linkButtons(context: Context, views: RemoteViews) {
val action = Intent(context, MainActivity::class.java).putExtra("expand", true) val action = Intent(context, MainActivity::class.java).putExtra("expand", true)
var pendingIntent: PendingIntent var pendingIntent: PendingIntent
val serviceName = ComponentName(context, MusicService::class.java) val serviceName = ComponentName(context, MusicService::class.java)
// Home // Home
action.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TOP action.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TOP
pendingIntent = PendingIntent.getActivity(context, 0, action, 0) pendingIntent = PendingIntent.getActivity(context, 0, action, 0)
views.setOnClickPendingIntent(R.id.image, pendingIntent) views.setOnClickPendingIntent(R.id.image, pendingIntent)
views.setOnClickPendingIntent(R.id.media_titles, pendingIntent) views.setOnClickPendingIntent(R.id.media_titles, pendingIntent)
// Previous track // Previous track
pendingIntent = buildPendingIntent(context, ACTION_REWIND, serviceName) pendingIntent = buildPendingIntent(context, ACTION_REWIND, serviceName)
views.setOnClickPendingIntent(R.id.button_prev, pendingIntent) views.setOnClickPendingIntent(R.id.button_prev, pendingIntent)
// Play and pause // Play and pause
pendingIntent = buildPendingIntent(context, ACTION_TOGGLE_PAUSE, serviceName) pendingIntent = buildPendingIntent(context, ACTION_TOGGLE_PAUSE, serviceName)
views.setOnClickPendingIntent(R.id.button_toggle_play_pause, pendingIntent) views.setOnClickPendingIntent(R.id.button_toggle_play_pause, pendingIntent)
// Next track // Next track
pendingIntent = buildPendingIntent(context, ACTION_SKIP, serviceName) pendingIntent = buildPendingIntent(context, ACTION_SKIP, serviceName)
views.setOnClickPendingIntent(R.id.button_next, pendingIntent) views.setOnClickPendingIntent(R.id.button_next, pendingIntent)
} }
companion object { companion object {
const val NAME = "app_widget_classic" const val NAME = "app_widget_classic"
private var mInstance: AppWidgetClassic? = null private var mInstance: AppWidgetClassic? = null
private var imageSize = 0 private var imageSize = 0
private var cardRadius = 0f private var cardRadius = 0f
val instance: AppWidgetClassic val instance: AppWidgetClassic
@Synchronized get() { @Synchronized get() {
if (mInstance == null) { if (mInstance == null) {
mInstance = AppWidgetClassic() mInstance = AppWidgetClassic()
} }
return mInstance!! return mInstance!!
} }
} }
} }

View file

@ -15,7 +15,9 @@
package code.name.monkey.retromusic.appwidgets package code.name.monkey.retromusic.appwidgets
import android.app.PendingIntent import android.app.PendingIntent
import android.content.* import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.graphics.Bitmap import android.graphics.Bitmap
import android.graphics.drawable.Drawable import android.graphics.drawable.Drawable
import android.text.TextUtils import android.text.TextUtils
@ -32,207 +34,175 @@ import code.name.monkey.retromusic.service.MusicService.*
import code.name.monkey.retromusic.util.RetroUtil import code.name.monkey.retromusic.util.RetroUtil
import com.bumptech.glide.Glide import com.bumptech.glide.Glide
import com.bumptech.glide.request.animation.GlideAnimation import com.bumptech.glide.request.animation.GlideAnimation
import com.bumptech.glide.request.target.* import com.bumptech.glide.request.target.SimpleTarget
import com.bumptech.glide.request.target.Target import com.bumptech.glide.request.target.Target
class AppWidgetSmall : BaseAppWidget() { class AppWidgetSmall : BaseAppWidget() {
private var target: Target<BitmapPaletteWrapper>? = null // for cancellation private var target: Target<BitmapPaletteWrapper>? = null // for cancellation
/** /**
* Initialize given widgets to default state, where we launch Music on default click and hide * Initialize given widgets to default state, where we launch Music on default click and hide
* actions if service not running. * actions if service not running.
*/ */
override fun defaultAppWidget(context: Context, appWidgetIds: IntArray) { override fun defaultAppWidget(context: Context, appWidgetIds: IntArray) {
val appWidgetView = RemoteViews(context.packageName, R.layout.app_widget_small) val appWidgetView = RemoteViews(context.packageName, R.layout.app_widget_small)
appWidgetView.setViewVisibility(R.id.media_titles, View.INVISIBLE) appWidgetView.setViewVisibility(R.id.media_titles, View.INVISIBLE)
appWidgetView.setImageViewResource(R.id.image, R.drawable.default_album_art) appWidgetView.setImageViewResource(R.id.image, R.drawable.default_album_art)
appWidgetView.setImageViewBitmap( appWidgetView.setImageViewBitmap(R.id.button_next, createBitmap(RetroUtil.getTintedVectorDrawable(context, R.drawable.ic_skip_next_white_24dp, MaterialValueHelper.getSecondaryTextColor(context, true))!!, 1f))
R.id.button_next, createBitmap( appWidgetView.setImageViewBitmap(R.id.button_prev, createBitmap(RetroUtil.getTintedVectorDrawable(context, R.drawable.ic_skip_previous_white_24dp, MaterialValueHelper.getSecondaryTextColor(context, true))!!, 1f))
RetroUtil.getTintedVectorDrawable( appWidgetView.setImageViewBitmap(R.id.button_toggle_play_pause, createBitmap(RetroUtil.getTintedVectorDrawable(context, R.drawable.ic_play_arrow_white_32dp, MaterialValueHelper.getSecondaryTextColor(context, true))!!, 1f))
context,
R.drawable.ic_skip_next_white_24dp,
MaterialValueHelper.getSecondaryTextColor(
context, true
)
)!!, 1f
)
)
appWidgetView.setImageViewBitmap(
R.id.button_prev, createBitmap(
RetroUtil.getTintedVectorDrawable(
context,
R.drawable.ic_skip_previous_white_24dp,
MaterialValueHelper.getSecondaryTextColor(
context, true
)
)!!, 1f
)
)
appWidgetView.setImageViewBitmap(
R.id.button_toggle_play_pause, createBitmap(
RetroUtil.getTintedVectorDrawable(
context,
R.drawable.ic_play_arrow_white_32dp,
MaterialValueHelper.getSecondaryTextColor(
context, true
)
)!!, 1f
)
)
linkButtons(context, appWidgetView) linkButtons(context, appWidgetView)
pushUpdate(context, appWidgetIds, appWidgetView) pushUpdate(context, appWidgetIds, appWidgetView)
} }
/** /**
* Update all active widget instances by pushing changes * Update all active widget instances by pushing changes
*/ */
override fun performUpdate(service: MusicService, appWidgetIds: IntArray?) { override fun performUpdate(service: MusicService, appWidgetIds: IntArray?) {
val appWidgetView = RemoteViews( val appWidgetView = RemoteViews(service.packageName, R.layout.app_widget_small)
service.packageName, R.layout.app_widget_small
)
val isPlaying = service.isPlaying val isPlaying = service.isPlaying
val song = service.currentSong val song = service.currentSong
// Set the titles and artwork // Set the titles and artwork
if (TextUtils.isEmpty(song.title) && TextUtils.isEmpty(song.artistName)) { if (TextUtils.isEmpty(song.title) && TextUtils.isEmpty(song.artistName)) {
appWidgetView.setViewVisibility(R.id.media_titles, View.INVISIBLE) appWidgetView.setViewVisibility(R.id.media_titles, View.INVISIBLE)
} else { } else {
if (TextUtils.isEmpty(song.title) || TextUtils.isEmpty(song.artistName)) { if (TextUtils.isEmpty(song.title) || TextUtils.isEmpty(song.artistName)) {
appWidgetView.setTextViewText(R.id.text_separator, "") appWidgetView.setTextViewText(R.id.text_separator, "")
} else { } else {
appWidgetView.setTextViewText(R.id.text_separator, "") appWidgetView.setTextViewText(R.id.text_separator, "")
} }
appWidgetView.setViewVisibility(R.id.media_titles, View.VISIBLE) appWidgetView.setViewVisibility(R.id.media_titles, View.VISIBLE)
appWidgetView.setTextViewText(R.id.title, song.title) appWidgetView.setTextViewText(R.id.title, song.title)
appWidgetView.setTextViewText(R.id.text, song.artistName) appWidgetView.setTextViewText(R.id.text, song.artistName)
} }
// Link actions buttons to intents // Link actions buttons to intents
linkButtons(service, appWidgetView) linkButtons(service, appWidgetView)
if (imageSize == 0) { if (imageSize == 0) {
imageSize = service.resources.getDimensionPixelSize(R.dimen.app_widget_small_image_size) imageSize = service.resources.getDimensionPixelSize(R.dimen.app_widget_small_image_size)
} }
if (cardRadius == 0f) { if (cardRadius == 0f) {
cardRadius = service.resources.getDimension(R.dimen.app_widget_card_radius) cardRadius = service.resources.getDimension(R.dimen.app_widget_card_radius)
} }
// Load the album cover async and push the update on completion // Load the album cover async and push the update on completion
val appContext = service.applicationContext val appContext = service.applicationContext
service.runOnUiThread { service.runOnUiThread {
if (target != null) { if (target != null) {
Glide.clear(target) Glide.clear(target)
} }
target = SongGlideRequest.Builder.from(Glide.with(service), song) target = SongGlideRequest.Builder.from(Glide.with(service), song)
.checkIgnoreMediaStore(service).generatePalette(service).build().centerCrop() .checkIgnoreMediaStore(service).generatePalette(service).build().centerCrop()
.into(object : SimpleTarget<BitmapPaletteWrapper>(imageSize, imageSize) { .into(object : SimpleTarget<BitmapPaletteWrapper>(imageSize, imageSize) {
override fun onResourceReady( override fun onResourceReady(
resource: BitmapPaletteWrapper, resource: BitmapPaletteWrapper,
glideAnimation: GlideAnimation<in BitmapPaletteWrapper> glideAnimation: GlideAnimation<in BitmapPaletteWrapper>
) { ) {
val palette = resource.palette val palette = resource.palette
update( update(
resource.bitmap, palette.getVibrantColor( resource.bitmap, palette.getVibrantColor(
palette.getMutedColor( palette.getMutedColor(
MaterialValueHelper.getSecondaryTextColor( MaterialValueHelper.getSecondaryTextColor(
service, true service, true
) )
) )
) )
) )
} }
override fun onLoadFailed(e: Exception?, errorDrawable: Drawable?) { override fun onLoadFailed(e: Exception?, errorDrawable: Drawable?) {
super.onLoadFailed(e, errorDrawable) super.onLoadFailed(e, errorDrawable)
update(null, MaterialValueHelper.getSecondaryTextColor(service, true)) update(null, MaterialValueHelper.getSecondaryTextColor(service, true))
} }
private fun update(bitmap: Bitmap?, color: Int) { private fun update(bitmap: Bitmap?, color: Int) {
// Set correct drawable for pause state // Set correct drawable for pause state
val playPauseRes = if (isPlaying) R.drawable.ic_pause_white_24dp val playPauseRes = if (isPlaying) R.drawable.ic_pause_white_24dp
else R.drawable.ic_play_arrow_white_32dp else R.drawable.ic_play_arrow_white_32dp
appWidgetView.setImageViewBitmap( appWidgetView.setImageViewBitmap(
R.id.button_toggle_play_pause, createBitmap( R.id.button_toggle_play_pause, createBitmap(
RetroUtil.getTintedVectorDrawable( RetroUtil.getTintedVectorDrawable(
service, playPauseRes, color service, playPauseRes, color
)!!, 1f )!!, 1f
) )
) )
// Set prev/next button drawables // Set prev/next button drawables
appWidgetView.setImageViewBitmap( appWidgetView.setImageViewBitmap(
R.id.button_next, createBitmap( R.id.button_next, createBitmap(
RetroUtil.getTintedVectorDrawable( RetroUtil.getTintedVectorDrawable(
service, R.drawable.ic_skip_next_white_24dp, color service, R.drawable.ic_skip_next_white_24dp, color
)!!, 1f )!!, 1f
) )
) )
appWidgetView.setImageViewBitmap( appWidgetView.setImageViewBitmap(
R.id.button_prev, createBitmap( R.id.button_prev, createBitmap(
RetroUtil.getTintedVectorDrawable( RetroUtil.getTintedVectorDrawable(
service, R.drawable.ic_skip_previous_white_24dp, color service, R.drawable.ic_skip_previous_white_24dp, color
)!!, 1f )!!, 1f
) )
) )
val image = getAlbumArtDrawable(service.resources, bitmap) val image = getAlbumArtDrawable(service.resources, bitmap)
val roundedBitmap = createRoundedBitmap( val roundedBitmap = createRoundedBitmap(
image, imageSize, imageSize, cardRadius, 0f, 0f, 0f image, imageSize, imageSize, cardRadius, 0f, 0f, 0f
) )
appWidgetView.setImageViewBitmap(R.id.image, roundedBitmap) appWidgetView.setImageViewBitmap(R.id.image, roundedBitmap)
pushUpdate(appContext, appWidgetIds, appWidgetView) pushUpdate(appContext, appWidgetIds, appWidgetView)
} }
}) })
} }
} }
/** /**
* Link up various button actions using [PendingIntent]. * Link up various button actions using [PendingIntent].
*/ */
private fun linkButtons(context: Context, views: RemoteViews) { private fun linkButtons(context: Context, views: RemoteViews) {
val action = Intent(context, MainActivity::class.java).putExtra("expand", true) val action = Intent(context, MainActivity::class.java).putExtra("expand", true)
var pendingIntent: PendingIntent var pendingIntent: PendingIntent
val serviceName = ComponentName(context, MusicService::class.java) val serviceName = ComponentName(context, MusicService::class.java)
// Home // Home
action.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TOP action.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TOP
pendingIntent = PendingIntent.getActivity(context, 0, action, 0) pendingIntent = PendingIntent.getActivity(context, 0, action, 0)
views.setOnClickPendingIntent(R.id.image, pendingIntent) views.setOnClickPendingIntent(R.id.image, pendingIntent)
views.setOnClickPendingIntent(R.id.media_titles, pendingIntent) views.setOnClickPendingIntent(R.id.media_titles, pendingIntent)
// Previous track // Previous track
pendingIntent = buildPendingIntent(context, ACTION_REWIND, serviceName) pendingIntent = buildPendingIntent(context, ACTION_REWIND, serviceName)
views.setOnClickPendingIntent(R.id.button_prev, pendingIntent) views.setOnClickPendingIntent(R.id.button_prev, pendingIntent)
// Play and pause // Play and pause
pendingIntent = buildPendingIntent(context, ACTION_TOGGLE_PAUSE, serviceName) pendingIntent = buildPendingIntent(context, ACTION_TOGGLE_PAUSE, serviceName)
views.setOnClickPendingIntent(R.id.button_toggle_play_pause, pendingIntent) views.setOnClickPendingIntent(R.id.button_toggle_play_pause, pendingIntent)
// Next track // Next track
pendingIntent = buildPendingIntent(context, ACTION_SKIP, serviceName) pendingIntent = buildPendingIntent(context, ACTION_SKIP, serviceName)
views.setOnClickPendingIntent(R.id.button_next, pendingIntent) views.setOnClickPendingIntent(R.id.button_next, pendingIntent)
} }
companion object { companion object {
const val NAME: String = "app_widget_small" const val NAME: String = "app_widget_small"
private var mInstance: AppWidgetSmall? = null private var mInstance: AppWidgetSmall? = null
private var imageSize = 0 private var imageSize = 0
private var cardRadius = 0f private var cardRadius = 0f
val instance: AppWidgetSmall val instance: AppWidgetSmall
@Synchronized get() { @Synchronized get() {
if (mInstance == null) { if (mInstance == null) {
mInstance = AppWidgetSmall() mInstance = AppWidgetSmall()
} }
return mInstance!! return mInstance!!
} }
} }
} }

View file

@ -14,9 +14,18 @@
package code.name.monkey.retromusic.dagger package code.name.monkey.retromusic.dagger
import code.name.monkey.retromusic.activities.* import code.name.monkey.retromusic.activities.AlbumDetailsActivity
import code.name.monkey.retromusic.dagger.module.* import code.name.monkey.retromusic.activities.ArtistDetailActivity
import code.name.monkey.retromusic.fragments.mainactivity.* import code.name.monkey.retromusic.activities.GenreDetailsActivity
import code.name.monkey.retromusic.activities.PlaylistDetailActivity
import code.name.monkey.retromusic.activities.SearchActivity
import code.name.monkey.retromusic.dagger.module.AppModule
import code.name.monkey.retromusic.dagger.module.PresenterModule
import code.name.monkey.retromusic.fragments.mainactivity.AlbumsFragment
import code.name.monkey.retromusic.fragments.mainactivity.ArtistsFragment
import code.name.monkey.retromusic.fragments.mainactivity.GenresFragment
import code.name.monkey.retromusic.fragments.mainactivity.PlaylistsFragment
import code.name.monkey.retromusic.fragments.mainactivity.SongsFragment
import code.name.monkey.retromusic.fragments.mainactivity.home.BannerHomeFragment import code.name.monkey.retromusic.fragments.mainactivity.home.BannerHomeFragment
import dagger.Component import dagger.Component
import javax.inject.Singleton import javax.inject.Singleton
@ -25,17 +34,12 @@ import javax.inject.Singleton
* Created by hemanths on 2019-09-04. * Created by hemanths on 2019-09-04.
*/ */
@Singleton @Singleton
@Component(modules = [ @Component(
RepositoryModule::class, modules = [
AlbumModule::class, AppModule::class,
ArtistModule::class, PresenterModule::class
GenreModule::class, ]
HomeModule::class, )
PlaylistModule::class,
SearchModule::class,
SongModule::class,
ActivityModule::class
])
interface MusicComponent { interface MusicComponent {
fun inject(songsFragment: SongsFragment) fun inject(songsFragment: SongsFragment)

View file

@ -1,34 +0,0 @@
/*
* Copyright (c) 2019 Hemanth Savarala.
*
* Licensed under the GNU General Public License v3
*
* This is free software: you can redistribute it and/or modify it under
* the terms of the GNU General Public License as published by
* the Free Software Foundation either version 3 of the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*/
package code.name.monkey.retromusic.dagger.module
import android.app.Activity
import dagger.Module
import dagger.Provides
import javax.inject.Singleton
/**
* Created by hemanths on 2019-09-04.
*/
@Module
class ActivityModule(private val activity: Activity) {
@Provides
@Singleton
fun provideActivity(): Activity {
return activity
}
}

View file

@ -1,39 +0,0 @@
/*
* Copyright (c) 2019 Hemanth Savarala.
*
* Licensed under the GNU General Public License v3
*
* This is free software: you can redistribute it and/or modify it under
* the terms of the GNU General Public License as published by
* the Free Software Foundation either version 3 of the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*/
package code.name.monkey.retromusic.dagger.module
import code.name.monkey.retromusic.mvp.presenter.AlbumDetailsPresenter
import code.name.monkey.retromusic.mvp.presenter.AlbumDetailsPresenter.AlbumDetailsPresenterImpl
import code.name.monkey.retromusic.mvp.presenter.AlbumsPresenter
import code.name.monkey.retromusic.mvp.presenter.AlbumsPresenter.AlbumsPresenterImpl
import dagger.Module
import dagger.Provides
/**
* Created by hemanths on 2019-09-04.
*/
@Module
class AlbumModule {
@Provides
fun providesAlbumsPresenter(presenter: AlbumsPresenterImpl): AlbumsPresenter {
return presenter
}
@Provides
fun providesAlbumDetailsPresenter(presenter: AlbumDetailsPresenterImpl): AlbumDetailsPresenter {
return presenter
}
}

View file

@ -14,6 +14,7 @@
package code.name.monkey.retromusic.dagger.module package code.name.monkey.retromusic.dagger.module
import android.app.Application
import android.content.Context import android.content.Context
import dagger.Module import dagger.Module
@ -24,11 +25,9 @@ import javax.inject.Singleton
* Created by hemanths on 2019-09-04. * Created by hemanths on 2019-09-04.
*/ */
@Module @Module
class AppModule(private val context: Context) { class AppModule(private val application: Application) {
@Provides @Provides
@Singleton @Singleton
fun provideContext(): Context { fun provideContext(): Context = application
return context
}
} }

View file

@ -1,39 +0,0 @@
/*
* Copyright (c) 2019 Hemanth Savarala.
*
* Licensed under the GNU General Public License v3
*
* This is free software: you can redistribute it and/or modify it under
* the terms of the GNU General Public License as published by
* the Free Software Foundation either version 3 of the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*/
package code.name.monkey.retromusic.dagger.module
import code.name.monkey.retromusic.mvp.presenter.ArtistDetailsPresenter
import code.name.monkey.retromusic.mvp.presenter.ArtistDetailsPresenter.ArtistDetailsPresenterImpl
import code.name.monkey.retromusic.mvp.presenter.ArtistsPresenter
import code.name.monkey.retromusic.mvp.presenter.ArtistsPresenter.ArtistsPresenterImpl
import dagger.Module
import dagger.Provides
/**
* Created by hemanths on 2019-09-04.
*/
@Module
class ArtistModule {
@Provides
fun providesArtistDetailsPresenter(presenter: ArtistDetailsPresenterImpl): ArtistDetailsPresenter {
return presenter
}
@Provides
fun providesArtistsPresenter(presenter: ArtistsPresenterImpl): ArtistsPresenter {
return presenter
}
}

View file

@ -1,40 +0,0 @@
/*
* Copyright (c) 2019 Hemanth Savarala.
*
* Licensed under the GNU General Public License v3
*
* This is free software: you can redistribute it and/or modify it under
* the terms of the GNU General Public License as published by
* the Free Software Foundation either version 3 of the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*/
package code.name.monkey.retromusic.dagger.module
import code.name.monkey.retromusic.mvp.presenter.GenreDetailsPresenter
import code.name.monkey.retromusic.mvp.presenter.GenreDetailsPresenter.GenreDetailsPresenterImpl
import code.name.monkey.retromusic.mvp.presenter.GenresPresenter
import code.name.monkey.retromusic.mvp.presenter.GenresPresenter.GenresPresenterImpl
import dagger.Module
import dagger.Provides
/**
* Created by hemanths on 2019-09-04.
*/
@Module
class GenreModule {
@Provides
fun providesGenresPresenter(presenter: GenresPresenterImpl): GenresPresenter {
return presenter
}
@Provides
fun providesGenreDetailsPresenter(presenter: GenreDetailsPresenterImpl): GenreDetailsPresenter {
return presenter
}
}

View file

@ -1,33 +0,0 @@
/*
* Copyright (c) 2019 Hemanth Savarala.
*
* Licensed under the GNU General Public License v3
*
* This is free software: you can redistribute it and/or modify it under
* the terms of the GNU General Public License as published by
* the Free Software Foundation either version 3 of the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*/
package code.name.monkey.retromusic.dagger.module
import code.name.monkey.retromusic.mvp.presenter.HomePresenter
import code.name.monkey.retromusic.mvp.presenter.HomePresenter.HomePresenterImpl
import dagger.Module
import dagger.Provides
/**
* Created by hemanths on 2019-09-04.
*/
@Module
class HomeModule {
@Provides
fun providesHomePresenter(presenter: HomePresenterImpl): HomePresenter {
return presenter
}
}

View file

@ -1,38 +0,0 @@
/*
* Copyright (c) 2019 Hemanth Savarala.
*
* Licensed under the GNU General Public License v3
*
* This is free software: you can redistribute it and/or modify it under
* the terms of the GNU General Public License as published by
* the Free Software Foundation either version 3 of the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*/
package code.name.monkey.retromusic.dagger.module
import code.name.monkey.retromusic.mvp.presenter.PlaylistSongsPresenter
import code.name.monkey.retromusic.mvp.presenter.PlaylistSongsPresenter.PlaylistSongsPresenterImpl
import code.name.monkey.retromusic.mvp.presenter.PlaylistsPresenter
import code.name.monkey.retromusic.mvp.presenter.PlaylistsPresenter.PlaylistsPresenterImpl
import dagger.Module
import dagger.Provides
/**
* Created by hemanths on 2019-09-04.
*/
@Module
class PlaylistModule {
@Provides
fun providesPlaylistSongPresenter(presenter: PlaylistSongsPresenterImpl): PlaylistSongsPresenter {
return presenter
}
@Provides
fun providesPlaylistsPresenter(presenter: PlaylistsPresenterImpl): PlaylistsPresenter {
return presenter
}
}

View file

@ -0,0 +1,111 @@
/*
* Copyright (c) 2019 Hemanth Savarala.
*
* Licensed under the GNU General Public License v3
*
* This is free software: you can redistribute it and/or modify it under
* the terms of the GNU General Public License as published by
* the Free Software Foundation either version 3 of the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*/
package code.name.monkey.retromusic.dagger.module
import android.content.Context
import code.name.monkey.retromusic.mvp.presenter.AlbumDetailsPresenter
import code.name.monkey.retromusic.mvp.presenter.AlbumDetailsPresenter.AlbumDetailsPresenterImpl
import code.name.monkey.retromusic.mvp.presenter.AlbumsPresenter
import code.name.monkey.retromusic.mvp.presenter.AlbumsPresenter.AlbumsPresenterImpl
import code.name.monkey.retromusic.mvp.presenter.ArtistDetailsPresenter
import code.name.monkey.retromusic.mvp.presenter.ArtistDetailsPresenter.ArtistDetailsPresenterImpl
import code.name.monkey.retromusic.mvp.presenter.ArtistsPresenter
import code.name.monkey.retromusic.mvp.presenter.ArtistsPresenter.ArtistsPresenterImpl
import code.name.monkey.retromusic.mvp.presenter.GenreDetailsPresenter
import code.name.monkey.retromusic.mvp.presenter.GenreDetailsPresenter.GenreDetailsPresenterImpl
import code.name.monkey.retromusic.mvp.presenter.GenresPresenter
import code.name.monkey.retromusic.mvp.presenter.GenresPresenter.GenresPresenterImpl
import code.name.monkey.retromusic.mvp.presenter.HomePresenter
import code.name.monkey.retromusic.mvp.presenter.HomePresenter.HomePresenterImpl
import code.name.monkey.retromusic.mvp.presenter.PlaylistSongsPresenter
import code.name.monkey.retromusic.mvp.presenter.PlaylistSongsPresenter.PlaylistSongsPresenterImpl
import code.name.monkey.retromusic.mvp.presenter.PlaylistsPresenter
import code.name.monkey.retromusic.mvp.presenter.PlaylistsPresenter.PlaylistsPresenterImpl
import code.name.monkey.retromusic.mvp.presenter.SearchPresenter
import code.name.monkey.retromusic.mvp.presenter.SearchPresenter.SearchPresenterImpl
import code.name.monkey.retromusic.mvp.presenter.SongPresenter
import code.name.monkey.retromusic.mvp.presenter.SongPresenter.SongPresenterImpl
import code.name.monkey.retromusic.providers.RepositoryImpl
import code.name.monkey.retromusic.providers.interfaces.Repository
import dagger.Module
import dagger.Provides
/**
* Created by hemanths on 2019-12-30.
*/
@Module
class PresenterModule {
@Provides
fun providesRepository(context: Context): Repository {
return RepositoryImpl(context)
}
@Provides
fun providesAlbumsPresenter(presenter: AlbumsPresenterImpl): AlbumsPresenter {
return presenter
}
@Provides
fun providesAlbumDetailsPresenter(presenter: AlbumDetailsPresenterImpl): AlbumDetailsPresenter {
return presenter
}
@Provides
fun providesArtistDetailsPresenter(presenter: ArtistDetailsPresenterImpl): ArtistDetailsPresenter {
return presenter
}
@Provides
fun providesArtistsPresenter(presenter: ArtistsPresenterImpl): ArtistsPresenter {
return presenter
}
@Provides
fun providesGenresPresenter(presenter: GenresPresenterImpl): GenresPresenter {
return presenter
}
@Provides
fun providesGenreDetailsPresenter(presenter: GenreDetailsPresenterImpl): GenreDetailsPresenter {
return presenter
}
@Provides
fun providesHomePresenter(presenter: HomePresenterImpl): HomePresenter {
return presenter
}
@Provides
fun providesPlaylistSongPresenter(presenter: PlaylistSongsPresenterImpl): PlaylistSongsPresenter {
return presenter
}
@Provides
fun providesPlaylistsPresenter(presenter: PlaylistsPresenterImpl): PlaylistsPresenter {
return presenter
}
@Provides
fun providesSearchPresenter(presenter: SearchPresenterImpl): SearchPresenter {
return presenter
}
@Provides
fun providesSongPresenter(presenter: SongPresenterImpl): SongPresenter {
return presenter
}
}

View file

@ -1,33 +0,0 @@
/*
* Copyright (c) 2019 Hemanth Savarala.
*
* Licensed under the GNU General Public License v3
*
* This is free software: you can redistribute it and/or modify it under
* the terms of the GNU General Public License as published by
* the Free Software Foundation either version 3 of the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*/
package code.name.monkey.retromusic.dagger.module
import android.content.Context
import code.name.monkey.retromusic.providers.RepositoryImpl
import code.name.monkey.retromusic.providers.interfaces.Repository
import dagger.Module
import dagger.Provides
/**
* Created by hemanths on 2019-09-04.
*/
@Module(includes = [AppModule::class])
class RepositoryModule {
@Provides
fun providesRepository(context: Context): Repository {
return RepositoryImpl(context)
}
}

View file

@ -1,33 +0,0 @@
/*
* Copyright (c) 2019 Hemanth Savarala.
*
* Licensed under the GNU General Public License v3
*
* This is free software: you can redistribute it and/or modify it under
* the terms of the GNU General Public License as published by
* the Free Software Foundation either version 3 of the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*/
package code.name.monkey.retromusic.dagger.module
import code.name.monkey.retromusic.mvp.presenter.SearchPresenter
import code.name.monkey.retromusic.mvp.presenter.SearchPresenter.SearchPresenterImpl
import dagger.Module
import dagger.Provides
/**
* Created by hemanths on 2019-09-04.
*/
@Module
class SearchModule {
@Provides
fun providesSearchPresenter(presenter: SearchPresenterImpl): SearchPresenter {
return presenter
}
}

View file

@ -1,31 +0,0 @@
/*
* Copyright (c) 2019 Hemanth Savarala.
*
* Licensed under the GNU General Public License v3
*
* This is free software: you can redistribute it and/or modify it under
* the terms of the GNU General Public License as published by
* the Free Software Foundation either version 3 of the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*/
package code.name.monkey.retromusic.dagger.module
import code.name.monkey.retromusic.mvp.presenter.SongPresenter
import code.name.monkey.retromusic.mvp.presenter.SongPresenter.SongPresenterImpl
import dagger.Module
import dagger.Provides
/**
* Created by hemanths on 2019-09-04.
*/
@Module
class SongModule {
@Provides
fun providesSongPresenter(presenter: SongPresenterImpl): SongPresenter {
return presenter
}
}

View file

@ -13,7 +13,9 @@ import code.name.monkey.retromusic.mvp.presenter.AlbumsView
import code.name.monkey.retromusic.util.PreferenceUtil import code.name.monkey.retromusic.util.PreferenceUtil
import javax.inject.Inject import javax.inject.Inject
open class AlbumsFragment : AbsLibraryPagerRecyclerViewCustomGridSizeFragment<AlbumAdapter, GridLayoutManager>(), AlbumsView { open class AlbumsFragment : AbsLibraryPagerRecyclerViewCustomGridSizeFragment<AlbumAdapter, GridLayoutManager>(),
AlbumsView {
@Inject @Inject
lateinit var albumsPresenter: AlbumsPresenter lateinit var albumsPresenter: AlbumsPresenter
@ -73,7 +75,6 @@ open class AlbumsFragment : AbsLibraryPagerRecyclerViewCustomGridSizeFragment<Al
adapter?.notifyDataSetChanged() adapter?.notifyDataSetChanged()
} }
override fun loadSortOrder(): String { override fun loadSortOrder(): String {
return PreferenceUtil.getInstance(requireContext()).albumSortOrder return PreferenceUtil.getInstance(requireContext()).albumSortOrder
@ -117,7 +118,6 @@ open class AlbumsFragment : AbsLibraryPagerRecyclerViewCustomGridSizeFragment<Al
albumsPresenter.loadAlbums() albumsPresenter.loadAlbums()
} }
override fun showEmptyView() { override fun showEmptyView() {
adapter?.swapDataSet(ArrayList()) adapter?.swapDataSet(ArrayList())
} }
@ -133,5 +133,4 @@ open class AlbumsFragment : AbsLibraryPagerRecyclerViewCustomGridSizeFragment<Al
return fragment return fragment
} }
} }
} }

View file

@ -16,10 +16,16 @@ package code.name.monkey.retromusic.mvp.presenter
import code.name.monkey.retromusic.Result import code.name.monkey.retromusic.Result
import code.name.monkey.retromusic.model.Album import code.name.monkey.retromusic.model.Album
import code.name.monkey.retromusic.mvp.* import code.name.monkey.retromusic.mvp.BaseView
import code.name.monkey.retromusic.mvp.Presenter
import code.name.monkey.retromusic.mvp.PresenterImpl
import code.name.monkey.retromusic.providers.interfaces.Repository import code.name.monkey.retromusic.providers.interfaces.Repository
import kotlinx.coroutines.* import kotlinx.coroutines.CoroutineScope
import java.util.* import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import java.util.ArrayList
import javax.inject.Inject import javax.inject.Inject
import kotlin.coroutines.CoroutineContext import kotlin.coroutines.CoroutineContext
@ -35,7 +41,7 @@ interface AlbumsPresenter : Presenter<AlbumsView> {
fun loadAlbums() fun loadAlbums()
class AlbumsPresenterImpl @Inject constructor( class AlbumsPresenterImpl @Inject constructor(
private val repository: Repository private val repository: Repository
) : PresenterImpl<AlbumsView>(), AlbumsPresenter, CoroutineScope { ) : PresenterImpl<AlbumsView>(), AlbumsPresenter, CoroutineScope {
private val job = Job() private val job = Job()

View file

@ -20,14 +20,29 @@ import code.name.monkey.retromusic.Result
import code.name.monkey.retromusic.Result.Error import code.name.monkey.retromusic.Result.Error
import code.name.monkey.retromusic.Result.Success import code.name.monkey.retromusic.Result.Success
import code.name.monkey.retromusic.adapter.HomeAdapter import code.name.monkey.retromusic.adapter.HomeAdapter
import code.name.monkey.retromusic.loaders.* import code.name.monkey.retromusic.loaders.AlbumLoader
import code.name.monkey.retromusic.model.* import code.name.monkey.retromusic.loaders.ArtistLoader
import code.name.monkey.retromusic.loaders.GenreLoader
import code.name.monkey.retromusic.loaders.LastAddedSongsLoader
import code.name.monkey.retromusic.loaders.PlaylistLoader
import code.name.monkey.retromusic.loaders.PlaylistSongsLoader
import code.name.monkey.retromusic.loaders.SearchLoader
import code.name.monkey.retromusic.loaders.SongLoader
import code.name.monkey.retromusic.loaders.TopAndRecentlyPlayedTracksLoader
import code.name.monkey.retromusic.model.AbsCustomPlaylist
import code.name.monkey.retromusic.model.Album
import code.name.monkey.retromusic.model.Artist
import code.name.monkey.retromusic.model.Genre
import code.name.monkey.retromusic.model.Home
import code.name.monkey.retromusic.model.Playlist
import code.name.monkey.retromusic.model.Song
import code.name.monkey.retromusic.providers.interfaces.Repository import code.name.monkey.retromusic.providers.interfaces.Repository
import code.name.monkey.retromusic.rest.LastFMRestClient import code.name.monkey.retromusic.rest.LastFMRestClient
import code.name.monkey.retromusic.rest.model.LastFmArtist import code.name.monkey.retromusic.rest.model.LastFmArtist
import java.io.IOException import java.io.IOException
import javax.inject.Inject
class RepositoryImpl(private val context: Context) : Repository { class RepositoryImpl @Inject constructor(private val context: Context) : Repository {
override suspend fun allAlbums(): Result<ArrayList<Album>> { override suspend fun allAlbums(): Result<ArrayList<Album>> {
return try { return try {
@ -152,11 +167,15 @@ class RepositoryImpl(private val context: Context) : Repository {
if (artists.isEmpty()) { if (artists.isEmpty()) {
Error(Throwable("No items found")) Error(Throwable("No items found"))
} else { } else {
Success(Home(0, Success(
Home(
0,
R.string.recent_artists, R.string.recent_artists,
artists, artists,
HomeAdapter.RECENT_ARTISTS, HomeAdapter.RECENT_ARTISTS,
R.drawable.ic_artist_white_24dp)) R.drawable.ic_artist_white_24dp
)
)
} }
} catch (e: Exception) { } catch (e: Exception) {
Error(e) Error(e)
@ -169,12 +188,15 @@ class RepositoryImpl(private val context: Context) : Repository {
if (albums.isEmpty()) { if (albums.isEmpty()) {
Error(Throwable("No items found")) Error(Throwable("No items found"))
} else { } else {
Success(Home(1, Success(
Home(
1,
R.string.recent_albums, R.string.recent_albums,
albums, albums,
HomeAdapter.RECENT_ALBUMS, HomeAdapter.RECENT_ALBUMS,
R.drawable.ic_album_white_24dp R.drawable.ic_album_white_24dp
)) )
)
} }
} catch (e: Exception) { } catch (e: Exception) {
Error(e) Error(e)
@ -187,31 +209,36 @@ class RepositoryImpl(private val context: Context) : Repository {
if (albums.isEmpty()) { if (albums.isEmpty()) {
Error(Throwable("No items found")) Error(Throwable("No items found"))
} else { } else {
Success(Home(3, Success(
Home(
3,
R.string.top_albums, R.string.top_albums,
albums, albums,
HomeAdapter.TOP_ALBUMS, HomeAdapter.TOP_ALBUMS,
R.drawable.ic_album_white_24dp R.drawable.ic_album_white_24dp
)) )
)
} }
} catch (e: Exception) { } catch (e: Exception) {
Error(e) Error(e)
} }
} }
override suspend fun topArtists(): Result<Home> { override suspend fun topArtists(): Result<Home> {
return try { return try {
val artists = TopAndRecentlyPlayedTracksLoader.getTopArtists(context) val artists = TopAndRecentlyPlayedTracksLoader.getTopArtists(context)
if (artists.isEmpty()) { if (artists.isEmpty()) {
Error(Throwable("No items found")) Error(Throwable("No items found"))
} else { } else {
Success(Home(2, Success(
Home(
2,
R.string.top_artists, R.string.top_artists,
artists, artists,
HomeAdapter.TOP_ARTISTS, HomeAdapter.TOP_ARTISTS,
R.drawable.ic_artist_white_24dp R.drawable.ic_artist_white_24dp
)) )
)
} }
} catch (e: Exception) { } catch (e: Exception) {
Error(e) Error(e)
@ -224,12 +251,15 @@ class RepositoryImpl(private val context: Context) : Repository {
if (playlists.isEmpty()) { if (playlists.isEmpty()) {
Error(Throwable("No items found")) Error(Throwable("No items found"))
} else { } else {
Success(Home(4, Success(
Home(
4,
R.string.favorites, R.string.favorites,
playlists, playlists,
HomeAdapter.PLAYLISTS, HomeAdapter.PLAYLISTS,
R.drawable.ic_favorite_white_24dp R.drawable.ic_favorite_white_24dp
)) )
)
} }
} catch (e: Exception) { } catch (e: Exception) {
Error(e) Error(e)
@ -237,14 +267,14 @@ class RepositoryImpl(private val context: Context) : Repository {
} }
override suspend fun artistInfo( override suspend fun artistInfo(
name: String, name: String,
lang: String?, lang: String?,
cache: String? cache: String?
): Result<LastFmArtist> = safeApiCall( ): Result<LastFmArtist> = safeApiCall(
call = { call = {
Success(LastFMRestClient(context).apiService.artistInfo(name, lang, cache)) Success(LastFMRestClient(context).apiService.artistInfo(name, lang, cache))
}, },
errorMessage = "Error" errorMessage = "Error"
) )

View file

@ -12,24 +12,24 @@
~ See the GNU General Public License for more details. ~ See the GNU General Public License for more details.
--> -->
<layer-list xmlns:android="http://schemas.android.com/apk/res/android"> <layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item android:id="@android:id/background"> <item android:id="@android:id/background">
<shape <shape
android:dither="true" android:dither="true"
android:shape="ring" android:shape="ring"
android:thickness="3dp" android:thickness="3dp"
android:type="sweep" android:type="sweep"
android:useLevel="false"> android:useLevel="false">
<solid android:color="?android:attr/textColorSecondary" /> <solid android:color="?android:attr/textColorSecondary" />
</shape> </shape>
</item> </item>
<item android:id="@android:id/progress"> <item android:id="@android:id/progress">
<shape <shape
android:shape="ring" android:shape="ring"
android:thickness="3dp" android:thickness="3dp"
android:type="sweep" android:type="sweep"
android:useLevel="true"> android:useLevel="true">
<solid android:color="?attr/colorAccent" /> <solid android:color="?attr/colorAccent" />
<corners android:radius="4dp" /> <corners android:radius="4dp" />
</shape> </shape>
</item> </item>
</layer-list> </layer-list>

View file

@ -18,7 +18,6 @@
android:layout_height="match_parent" android:layout_height="match_parent"
android:background="?attr/colorSurface"> android:background="?attr/colorSurface">
<com.google.android.material.appbar.AppBarLayout <com.google.android.material.appbar.AppBarLayout
android:id="@+id/appBarLayout" android:id="@+id/appBarLayout"
android:layout_width="match_parent" android:layout_width="match_parent"

View file

@ -1,96 +1,96 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/content" android:id="@+id/content"
android:layout_width="match_parent"
android:layout_height="@dimen/app_widget_classic_height"
android:background="@color/md_black_1000"
android:orientation="horizontal"
tools:ignore="ContentDescription">
<ImageView
android:id="@+id/image"
android:layout_width="@dimen/app_widget_classic_image_size"
android:layout_height="@dimen/app_widget_classic_image_size"
android:scaleType="centerCrop" />
<RelativeLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="@dimen/app_widget_classic_height" android:layout_height="match_parent">
android:background="@color/md_black_1000"
android:orientation="horizontal"
tools:ignore="ContentDescription">
<ImageView <LinearLayout
android:id="@+id/image" android:id="@+id/media_actions"
android:layout_width="@dimen/app_widget_classic_image_size" android:layout_width="match_parent"
android:layout_height="@dimen/app_widget_classic_image_size" android:layout_height="wrap_content"
android:scaleType="centerCrop" /> android:layout_alignParentBottom="true"
android:layout_gravity="bottom"
android:layoutDirection="ltr"
android:orientation="horizontal"
android:paddingStart="8dp"
android:paddingEnd="8dp"
android:paddingBottom="8dp">
<RelativeLayout <ImageButton
android:id="@+id/button_prev"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent"> android:layout_height="match_parent"
android:layout_weight="1"
android:background="@drawable/widget_selector"
tools:src="@drawable/ic_skip_previous_white_24dp"
tools:tint="@color/ate_secondary_text_dark" />
<LinearLayout <ImageButton
android:id="@+id/media_actions" android:id="@+id/button_toggle_play_pause"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="match_parent"
android:layout_alignParentBottom="true" android:layout_weight="1"
android:layout_gravity="bottom" android:background="@drawable/widget_selector"
android:layoutDirection="ltr" tools:src="@drawable/ic_play_arrow_white_32dp"
android:orientation="horizontal" tools:tint="@color/ate_secondary_text_dark" />
android:paddingStart="8dp"
android:paddingEnd="8dp"
android:paddingBottom="8dp">
<ImageButton <ImageButton
android:id="@+id/button_prev" android:id="@+id/button_next"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:layout_weight="1" android:layout_weight="1"
android:background="@drawable/widget_selector" android:background="@drawable/widget_selector"
tools:src="@drawable/ic_skip_previous_white_24dp" tools:src="@drawable/ic_skip_next_white_24dp"
tools:tint="@color/ate_secondary_text_dark" /> tools:tint="@color/ate_secondary_text_dark" />
<ImageButton </LinearLayout>
android:id="@+id/button_toggle_play_pause"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1"
android:background="@drawable/widget_selector"
tools:src="@drawable/ic_play_arrow_white_32dp"
tools:tint="@color/ate_secondary_text_dark" />
<ImageButton <LinearLayout
android:id="@+id/button_next" android:id="@+id/media_titles"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="wrap_content"
android:layout_weight="1" android:layout_above="@+id/media_actions"
android:background="@drawable/widget_selector" android:layout_alignParentTop="true"
tools:src="@drawable/ic_skip_next_white_24dp" android:orientation="vertical"
tools:tint="@color/ate_secondary_text_dark" /> android:paddingStart="8dp"
android:paddingTop="4dp"
android:paddingEnd="8dp">
</LinearLayout> <TextView
android:id="@+id/title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:singleLine="true"
android:textAppearance="@style/TextViewSubtitle1"
android:textColor="@color/ate_primary_text_dark"
tools:text="Title" />
<LinearLayout <TextView
android:id="@+id/media_titles" android:id="@+id/text"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_above="@+id/media_actions" android:gravity="center_vertical"
android:layout_alignParentTop="true" android:paddingTop="4dp"
android:orientation="vertical" android:singleLine="true"
android:paddingStart="8dp" android:textAppearance="@style/TextViewNormal"
android:paddingTop="4dp" android:textColor="@color/ate_secondary_text_dark"
android:paddingEnd="8dp"> tools:text="Text" />
<TextView </LinearLayout>
android:id="@+id/title" </RelativeLayout>
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:singleLine="true"
android:textAppearance="@style/TextViewSubtitle1"
android:textColor="@color/ate_primary_text_dark"
tools:text="Title" />
<TextView
android:id="@+id/text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:paddingTop="4dp"
android:singleLine="true"
android:textAppearance="@style/TextViewNormal"
android:textColor="@color/ate_secondary_text_dark"
tools:text="Text" />
</LinearLayout>
</RelativeLayout>
</LinearLayout> </LinearLayout>

View file

@ -9,3 +9,4 @@ android.enableJetifier=true
android.debug.obsoleteApi=true android.debug.obsoleteApi=true
android.enableBuildCache=true android.enableBuildCache=true
android.jetifier.blacklist = butterknife.*\\.jar android.jetifier.blacklist = butterknife.*\\.jar
kotlin.code.style=official