My initial commit
Removed Google play dependencies
This commit is contained in:
parent
fd582fff69
commit
301ac10570
430 changed files with 2210 additions and 3137 deletions
|
@ -1,94 +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
|
||||
|
||||
import android.widget.Toast
|
||||
import androidx.multidex.MultiDexApplication
|
||||
import code.name.monkey.appthemehelper.ThemeStore
|
||||
import code.name.monkey.appthemehelper.util.VersionUtils
|
||||
import code.name.monkey.retromusic.appshortcuts.DynamicShortcutManager
|
||||
import code.name.monkey.retromusic.dagger.DaggerMusicComponent
|
||||
import code.name.monkey.retromusic.dagger.MusicComponent
|
||||
import code.name.monkey.retromusic.dagger.module.AppModule
|
||||
import com.anjlab.android.iab.v3.BillingProcessor
|
||||
import com.anjlab.android.iab.v3.TransactionDetails
|
||||
|
||||
class App : MultiDexApplication() {
|
||||
|
||||
lateinit var billingProcessor: BillingProcessor
|
||||
|
||||
override fun onCreate() {
|
||||
super.onCreate()
|
||||
instance = this
|
||||
musicComponent = initDagger(this)
|
||||
|
||||
// default theme
|
||||
if (!ThemeStore.isConfigured(this, 3)) {
|
||||
ThemeStore.editTheme(this)
|
||||
.accentColorRes(R.color.md_deep_purple_A200)
|
||||
.coloredNavigationBar(true)
|
||||
.commit()
|
||||
}
|
||||
|
||||
if (VersionUtils.hasNougatMR())
|
||||
DynamicShortcutManager(this).initDynamicShortcuts()
|
||||
|
||||
// automatically restores purchases
|
||||
billingProcessor = BillingProcessor(this, BuildConfig.GOOGLE_PLAY_LICENSING_KEY,
|
||||
object : BillingProcessor.IBillingHandler {
|
||||
override fun onProductPurchased(productId: String, details: TransactionDetails?) {}
|
||||
|
||||
override fun onPurchaseHistoryRestored() {
|
||||
Toast.makeText(
|
||||
this@App,
|
||||
R.string.restored_previous_purchase_please_restart,
|
||||
Toast.LENGTH_LONG
|
||||
).show()
|
||||
}
|
||||
|
||||
override fun onBillingError(errorCode: Int, error: Throwable?) {}
|
||||
|
||||
override fun onBillingInitialized() {}
|
||||
})
|
||||
}
|
||||
|
||||
private fun initDagger(app: App): MusicComponent =
|
||||
DaggerMusicComponent.builder()
|
||||
.appModule(AppModule(app))
|
||||
.build()
|
||||
|
||||
override fun onTerminate() {
|
||||
super.onTerminate()
|
||||
billingProcessor.release()
|
||||
}
|
||||
|
||||
companion object {
|
||||
private var instance: App? = null
|
||||
|
||||
fun getContext(): App {
|
||||
return instance!!
|
||||
}
|
||||
|
||||
fun isProVersion(): Boolean {
|
||||
return BuildConfig.DEBUG || instance?.billingProcessor!!.isPurchased(
|
||||
PRO_VERSION_PRODUCT_ID
|
||||
)
|
||||
}
|
||||
|
||||
lateinit var musicComponent: MusicComponent
|
||||
|
||||
const val PRO_VERSION_PRODUCT_ID = "pro_version"
|
||||
}
|
||||
}
|
|
@ -1,127 +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
|
||||
|
||||
import android.provider.BaseColumns
|
||||
import android.provider.MediaStore
|
||||
|
||||
object Constants {
|
||||
|
||||
const val RATE_ON_GOOGLE_PLAY =
|
||||
"https://play.google.com/store/apps/details?id=code.name.monkey.retromusic"
|
||||
const val TRANSLATE = "http://monkeycodeapp.oneskyapp.com/collaboration/project?id=238534"
|
||||
const val GITHUB_PROJECT = "https://github.com/h4h13/RetroMusicPlayer"
|
||||
const val TELEGRAM_CHANGE_LOG = "https://t.me/retromusiclog"
|
||||
const val USER_PROFILE = "profile.jpg"
|
||||
const val USER_BANNER = "banner.jpg"
|
||||
const val APP_INSTAGRAM_LINK = "https://www.instagram.com/retromusicapp/"
|
||||
const val APP_TELEGRAM_LINK = "https://t.me/retromusicapp/"
|
||||
const val APP_TWITTER_LINK = "https://twitter.com/retromusicapp"
|
||||
const val FAQ_LINK = "https://github.com/h4h13/RetroMusicPlayer/blob/master/FAQ.md"
|
||||
const val PINTEREST = "https://in.pinterest.com/retromusicapp/"
|
||||
|
||||
const val IS_MUSIC =
|
||||
MediaStore.Audio.AudioColumns.IS_MUSIC + "=1" + " AND " + MediaStore.Audio.AudioColumns.TITLE + " != ''"
|
||||
|
||||
val baseProjection = arrayOf(
|
||||
BaseColumns._ID, // 0
|
||||
MediaStore.Audio.AudioColumns.TITLE, // 1
|
||||
MediaStore.Audio.AudioColumns.TRACK, // 2
|
||||
MediaStore.Audio.AudioColumns.YEAR, // 3
|
||||
MediaStore.Audio.AudioColumns.DURATION, // 4
|
||||
MediaStore.Audio.AudioColumns.DATA, // 5
|
||||
MediaStore.Audio.AudioColumns.DATE_MODIFIED, // 6
|
||||
MediaStore.Audio.AudioColumns.ALBUM_ID, // 7
|
||||
MediaStore.Audio.AudioColumns.ALBUM, // 8
|
||||
MediaStore.Audio.AudioColumns.ARTIST_ID, // 9
|
||||
MediaStore.Audio.AudioColumns.ARTIST,// 10
|
||||
MediaStore.Audio.AudioColumns.COMPOSER// 11
|
||||
)
|
||||
const val NUMBER_OF_TOP_TRACKS = 99
|
||||
}
|
||||
|
||||
const val EXTRA_SONG = "extra_songs"
|
||||
const val EXTRA_PLAYLIST = "extra_list"
|
||||
const val LIBRARY_CATEGORIES = "library_categories"
|
||||
const val EXTRA_SONG_INFO = "extra_song_info"
|
||||
const val DESATURATED_COLOR = "desaturated_color"
|
||||
const val BLACK_THEME = "black_theme"
|
||||
const val KEEP_SCREEN_ON = "keep_screen_on"
|
||||
const val TOGGLE_HOME_BANNER = "toggle_home_banner"
|
||||
const val NOW_PLAYING_SCREEN_ID = "now_playing_screen_id"
|
||||
const val CAROUSEL_EFFECT = "carousel_effect"
|
||||
const val COLORED_NOTIFICATION = "colored_notification"
|
||||
const val CLASSIC_NOTIFICATION = "classic_notification"
|
||||
const val GAPLESS_PLAYBACK = "gapless_playback"
|
||||
const val ALBUM_ART_ON_LOCKSCREEN = "album_art_on_lockscreen"
|
||||
const val BLURRED_ALBUM_ART = "blurred_album_art"
|
||||
const val NEW_BLUR_AMOUNT = "new_blur_amount"
|
||||
const val TOGGLE_HEADSET = "toggle_headset"
|
||||
const val GENERAL_THEME = "general_theme"
|
||||
const val CIRCULAR_ALBUM_ART = "circular_album_art"
|
||||
const val USER_NAME = "user_name"
|
||||
const val TOGGLE_FULL_SCREEN = "toggle_full_screen"
|
||||
const val TOGGLE_VOLUME = "toggle_volume"
|
||||
const val ROUND_CORNERS = "corner_window"
|
||||
const val TOGGLE_GENRE = "toggle_genre"
|
||||
const val PROFILE_IMAGE_PATH = "profile_image_path"
|
||||
const val BANNER_IMAGE_PATH = "banner_image_path"
|
||||
const val ADAPTIVE_COLOR_APP = "adaptive_color_app"
|
||||
const val TOGGLE_SEPARATE_LINE = "toggle_separate_line"
|
||||
const val HOME_ARTIST_GRID_STYLE = "home_artist_grid_style"
|
||||
const val TOGGLE_ADD_CONTROLS = "toggle_add_controls"
|
||||
const val ALBUM_COVER_STYLE = "album_cover_style_id"
|
||||
const val ALBUM_COVER_TRANSFORM = "album_cover_transform"
|
||||
const val TAB_TEXT_MODE = "tab_text_mode"
|
||||
const val LANGUAGE_NAME = "language_name"
|
||||
const val DIALOG_CORNER = "dialog_corner"
|
||||
const val SLEEP_TIMER_FINISH_SONG = "sleep_timer_finish_song"
|
||||
const val ALBUM_GRID_STYLE = "album_grid_style_home"
|
||||
const val ARTIST_GRID_STYLE = "artist_grid_style_home"
|
||||
const val SAF_SDCARD_URI = "saf_sdcard_uri"
|
||||
const val SONG_SORT_ORDER = "song_sort_order"
|
||||
const val SONG_GRID_SIZE = "song_grid_size"
|
||||
const val GENRE_SORT_ORDER = "genre_sort_order"
|
||||
const val LAST_PAGE = "last_start_page"
|
||||
const val BLUETOOTH_PLAYBACK = "bluetooth_playback"
|
||||
const val INITIALIZED_BLACKLIST = "initialized_blacklist"
|
||||
const val ARTIST_SORT_ORDER = "artist_sort_order"
|
||||
const val ARTIST_ALBUM_SORT_ORDER = "artist_album_sort_order"
|
||||
const val ALBUM_SORT_ORDER = "album_sort_order"
|
||||
const val ALBUM_SONG_SORT_ORDER = "album_song_sort_order"
|
||||
const val ARTIST_SONG_SORT_ORDER = "artist_song_sort_order"
|
||||
const val ALBUM_GRID_SIZE = "album_grid_size"
|
||||
const val ALBUM_GRID_SIZE_LAND = "album_grid_size_land"
|
||||
const val SONG_GRID_SIZE_LAND = "song_grid_size_land"
|
||||
const val ARTIST_GRID_SIZE = "artist_grid_size"
|
||||
const val ARTIST_GRID_SIZE_LAND = "artist_grid_size_land"
|
||||
const val COLORED_APP_SHORTCUTS = "colored_app_shortcuts"
|
||||
const val AUDIO_DUCKING = "audio_ducking"
|
||||
const val LAST_ADDED_CUTOFF = "last_added_interval"
|
||||
const val LAST_SLEEP_TIMER_VALUE = "last_sleep_timer_value"
|
||||
const val NEXT_SLEEP_TIMER_ELAPSED_REALTIME = "next_sleep_timer_elapsed_real_time"
|
||||
const val IGNORE_MEDIA_STORE_ARTWORK = "ignore_media_store_artwork"
|
||||
const val LAST_CHANGELOG_VERSION = "last_changelog_version"
|
||||
const val AUTO_DOWNLOAD_IMAGES_POLICY = "auto_download_images_policy"
|
||||
const val START_DIRECTORY = "start_directory"
|
||||
const val LOCK_SCREEN = "lock_screen"
|
||||
const val ALBUM_DETAIL_SONG_SORT_ORDER = "album_detail_song_sort_order"
|
||||
const val LYRICS_OPTIONS = "lyrics_tab_position"
|
||||
const val CHOOSE_EQUALIZER = "choose_equalizer"
|
||||
const val TOGGLE_SHUFFLE = "toggle_shuffle"
|
||||
const val SONG_GRID_STYLE = "song_grid_style"
|
||||
const val PAUSE_ON_ZERO_VOLUME = "pause_on_zero_volume"
|
||||
const val FILTER_SONG = "filter_song"
|
||||
const val EXPAND_NOW_PLAYING_PANEL = "expand_now_playing_panel"
|
|
@ -1,43 +0,0 @@
|
|||
package code.name.monkey.retromusic;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.ContextWrapper;
|
||||
import android.content.res.Configuration;
|
||||
import android.content.res.Resources;
|
||||
import android.os.LocaleList;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
import code.name.monkey.appthemehelper.util.VersionUtils;
|
||||
|
||||
public class LanguageContextWrapper extends ContextWrapper {
|
||||
|
||||
public LanguageContextWrapper(Context base) {
|
||||
super(base);
|
||||
}
|
||||
|
||||
public static LanguageContextWrapper wrap(Context context, Locale newLocale) {
|
||||
Resources res = context.getResources();
|
||||
Configuration configuration = res.getConfiguration();
|
||||
|
||||
if (VersionUtils.INSTANCE.hasNougatMR()) {
|
||||
configuration.setLocale(newLocale);
|
||||
|
||||
LocaleList localeList = new LocaleList(newLocale);
|
||||
LocaleList.setDefault(localeList);
|
||||
configuration.setLocales(localeList);
|
||||
|
||||
context = context.createConfigurationContext(configuration);
|
||||
|
||||
} else if (VersionUtils.INSTANCE.hasLollipop()) {
|
||||
configuration.setLocale(newLocale);
|
||||
context = context.createConfigurationContext(configuration);
|
||||
|
||||
} else {
|
||||
configuration.locale = newLocale;
|
||||
res.updateConfiguration(configuration, res.getDisplayMetrics());
|
||||
}
|
||||
|
||||
return new LanguageContextWrapper(context);
|
||||
}
|
||||
}
|
|
@ -1,26 +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
|
||||
|
||||
/**
|
||||
* Created by hemanths on 2019-10-23.
|
||||
*/
|
||||
|
||||
sealed class Result<out T : Any> {
|
||||
|
||||
class Success<out T : Any>(val data: T) : Result<T>()
|
||||
|
||||
class Error(val exception: Throwable) : Result<Nothing>()
|
||||
}
|
|
@ -1,39 +0,0 @@
|
|||
package code.name.monkey.retromusic;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
|
||||
import androidx.coordinatorlayout.widget.CoordinatorLayout;
|
||||
|
||||
import com.google.android.material.bottomsheet.BottomSheetBehavior;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
|
||||
public class RetroBottomSheetBehavior<V extends View> extends BottomSheetBehavior<V> {
|
||||
|
||||
private static final String TAG = "RetroBottomSheetBehavior";
|
||||
|
||||
private boolean allowDragging = true;
|
||||
|
||||
public RetroBottomSheetBehavior() {
|
||||
}
|
||||
|
||||
public RetroBottomSheetBehavior(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
}
|
||||
|
||||
public void setAllowDragging(boolean allowDragging) {
|
||||
this.allowDragging = allowDragging;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onInterceptTouchEvent(@NotNull CoordinatorLayout parent, @NotNull V child, @NotNull MotionEvent event) {
|
||||
if (!allowDragging) {
|
||||
return false;
|
||||
}
|
||||
return super.onInterceptTouchEvent(parent, child, event);
|
||||
}
|
||||
}
|
|
@ -1,148 +0,0 @@
|
|||
package code.name.monkey.retromusic.activities
|
||||
|
||||
import android.content.Intent
|
||||
import android.content.pm.PackageManager
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
import android.view.MenuItem
|
||||
import android.view.View
|
||||
import androidx.core.app.ShareCompat
|
||||
import androidx.recyclerview.widget.DefaultItemAnimator
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import code.name.monkey.retromusic.App
|
||||
import code.name.monkey.retromusic.Constants.APP_INSTAGRAM_LINK
|
||||
import code.name.monkey.retromusic.Constants.APP_TELEGRAM_LINK
|
||||
import code.name.monkey.retromusic.Constants.APP_TWITTER_LINK
|
||||
import code.name.monkey.retromusic.Constants.FAQ_LINK
|
||||
import code.name.monkey.retromusic.Constants.GITHUB_PROJECT
|
||||
import code.name.monkey.retromusic.Constants.PINTEREST
|
||||
import code.name.monkey.retromusic.Constants.RATE_ON_GOOGLE_PLAY
|
||||
import code.name.monkey.retromusic.Constants.TELEGRAM_CHANGE_LOG
|
||||
import code.name.monkey.retromusic.Constants.TRANSLATE
|
||||
import code.name.monkey.retromusic.R
|
||||
import code.name.monkey.retromusic.activities.base.AbsBaseActivity
|
||||
import code.name.monkey.retromusic.adapter.ContributorAdapter
|
||||
import code.name.monkey.retromusic.extensions.applyToolbar
|
||||
import code.name.monkey.retromusic.model.Contributor
|
||||
import code.name.monkey.retromusic.util.NavigationUtil
|
||||
import com.google.gson.Gson
|
||||
import com.google.gson.reflect.TypeToken
|
||||
import kotlinx.android.synthetic.main.activity_about.*
|
||||
import kotlinx.android.synthetic.main.card_credit.*
|
||||
import kotlinx.android.synthetic.main.card_other.*
|
||||
import kotlinx.android.synthetic.main.card_retro_info.*
|
||||
import kotlinx.android.synthetic.main.card_social.*
|
||||
import java.io.IOException
|
||||
import java.nio.charset.StandardCharsets
|
||||
|
||||
class AboutActivity : AbsBaseActivity(), View.OnClickListener {
|
||||
|
||||
private val contributorsJson: String?
|
||||
get() {
|
||||
val json: String
|
||||
try {
|
||||
val inputStream = assets.open("contributors.json")
|
||||
val size = inputStream.available()
|
||||
val buffer = ByteArray(size)
|
||||
inputStream.read(buffer)
|
||||
inputStream.close()
|
||||
json = String(buffer, StandardCharsets.UTF_8)
|
||||
} catch (ex: IOException) {
|
||||
ex.printStackTrace()
|
||||
return null
|
||||
}
|
||||
return json
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
setDrawUnderStatusBar()
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(R.layout.activity_about)
|
||||
setStatusbarColorAuto()
|
||||
setNavigationbarColorAuto()
|
||||
setLightNavigationBar(true)
|
||||
|
||||
applyToolbar(toolbar)
|
||||
version.setSummary(getAppVersion())
|
||||
setUpView()
|
||||
loadContributors()
|
||||
}
|
||||
|
||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||
if (item.itemId == android.R.id.home) {
|
||||
onBackPressed()
|
||||
return true
|
||||
}
|
||||
return super.onOptionsItemSelected(item)
|
||||
}
|
||||
|
||||
private fun openUrl(url: String) {
|
||||
val i = Intent(Intent.ACTION_VIEW)
|
||||
i.data = Uri.parse(url)
|
||||
i.flags = Intent.FLAG_ACTIVITY_NEW_TASK
|
||||
startActivity(i)
|
||||
}
|
||||
|
||||
private fun setUpView() {
|
||||
appGithub.setOnClickListener(this)
|
||||
faqLink.setOnClickListener(this)
|
||||
telegramLink.setOnClickListener(this)
|
||||
appRate.setOnClickListener(this)
|
||||
appTranslation.setOnClickListener(this)
|
||||
appShare.setOnClickListener(this)
|
||||
donateLink.setOnClickListener(this)
|
||||
instagramLink.setOnClickListener(this)
|
||||
twitterLink.setOnClickListener(this)
|
||||
changelog.setOnClickListener(this)
|
||||
openSource.setOnClickListener(this)
|
||||
pinterestLink.setOnClickListener(this)
|
||||
bugReportLink.setOnClickListener(this)
|
||||
}
|
||||
|
||||
override fun onClick(view: View) {
|
||||
when (view.id) {
|
||||
R.id.pinterestLink -> openUrl(PINTEREST)
|
||||
R.id.faqLink -> openUrl(FAQ_LINK)
|
||||
R.id.telegramLink -> openUrl(APP_TELEGRAM_LINK)
|
||||
R.id.appGithub -> openUrl(GITHUB_PROJECT)
|
||||
R.id.appTranslation -> openUrl(TRANSLATE)
|
||||
R.id.appRate -> openUrl(RATE_ON_GOOGLE_PLAY)
|
||||
R.id.appShare -> shareApp()
|
||||
R.id.donateLink -> NavigationUtil.goToSupportDevelopment(this)
|
||||
R.id.instagramLink -> openUrl(APP_INSTAGRAM_LINK)
|
||||
R.id.twitterLink -> openUrl(APP_TWITTER_LINK)
|
||||
R.id.changelog -> openUrl(TELEGRAM_CHANGE_LOG)
|
||||
R.id.openSource -> NavigationUtil.goToOpenSource(this)
|
||||
R.id.bugReportLink -> NavigationUtil.bugReport(this)
|
||||
}
|
||||
}
|
||||
|
||||
private fun getAppVersion(): String {
|
||||
return try {
|
||||
val isPro = if (App.isProVersion()) "Pro" else "Free"
|
||||
val packageInfo = packageManager.getPackageInfo(packageName, 0)
|
||||
"${packageInfo.versionName} $isPro"
|
||||
} catch (e: PackageManager.NameNotFoundException) {
|
||||
e.printStackTrace()
|
||||
"0.0.0"
|
||||
}
|
||||
}
|
||||
|
||||
private fun shareApp() {
|
||||
ShareCompat.IntentBuilder.from(this).setType("text/plain")
|
||||
.setChooserTitle(R.string.share_app)
|
||||
.setText(String.format(getString(R.string.app_share), packageName)).startChooser()
|
||||
}
|
||||
|
||||
private fun loadContributors() {
|
||||
val type = object : TypeToken<List<Contributor>>() {
|
||||
|
||||
}.type
|
||||
val contributors = Gson().fromJson<List<Contributor>>(contributorsJson, type)
|
||||
|
||||
val contributorAdapter = ContributorAdapter(contributors)
|
||||
recyclerView.layoutManager = LinearLayoutManager(this)
|
||||
recyclerView.itemAnimator = DefaultItemAnimator()
|
||||
recyclerView.adapter = contributorAdapter
|
||||
}
|
||||
}
|
|
@ -1,237 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2020 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.activities
|
||||
|
||||
import android.animation.ObjectAnimator
|
||||
import android.graphics.Color
|
||||
import android.graphics.PorterDuff
|
||||
import android.os.Bundle
|
||||
import android.view.animation.LinearInterpolator
|
||||
import android.widget.SeekBar
|
||||
import code.name.monkey.appthemehelper.ThemeStore
|
||||
import code.name.monkey.retromusic.R
|
||||
import code.name.monkey.retromusic.activities.base.AbsMusicServiceActivity
|
||||
import code.name.monkey.retromusic.fragments.base.AbsPlayerControlsFragment
|
||||
import code.name.monkey.retromusic.glide.BlurTransformation
|
||||
import code.name.monkey.retromusic.glide.RetroMusicColoredTarget
|
||||
import code.name.monkey.retromusic.glide.SongGlideRequest
|
||||
import code.name.monkey.retromusic.helper.MusicPlayerRemote
|
||||
import code.name.monkey.retromusic.helper.MusicProgressViewUpdateHelper
|
||||
import code.name.monkey.retromusic.helper.MusicProgressViewUpdateHelper.Callback
|
||||
import code.name.monkey.retromusic.helper.PlayPauseButtonOnClickHandler
|
||||
import code.name.monkey.retromusic.misc.SimpleOnSeekbarChangeListener
|
||||
import code.name.monkey.retromusic.service.MusicService
|
||||
import code.name.monkey.retromusic.util.MusicUtil
|
||||
import code.name.monkey.retromusic.util.color.MediaNotificationProcessor
|
||||
import com.bumptech.glide.Glide
|
||||
import kotlinx.android.synthetic.main.activity_drive_mode.*
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
|
||||
/**
|
||||
* Created by hemanths on 2020-02-02.
|
||||
*/
|
||||
|
||||
class DriveModeActivity : AbsMusicServiceActivity(), Callback {
|
||||
|
||||
private var lastPlaybackControlsColor: Int = Color.GRAY
|
||||
private var lastDisabledPlaybackControlsColor: Int = Color.GRAY
|
||||
private lateinit var progressViewUpdateHelper: MusicProgressViewUpdateHelper
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
setDrawUnderStatusBar()
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(R.layout.activity_drive_mode)
|
||||
setUpMusicControllers()
|
||||
|
||||
progressViewUpdateHelper = MusicProgressViewUpdateHelper(this)
|
||||
lastPlaybackControlsColor = ThemeStore.accentColor(this)
|
||||
close.setOnClickListener {
|
||||
onBackPressed()
|
||||
}
|
||||
}
|
||||
|
||||
private fun setUpMusicControllers() {
|
||||
setUpPlayPauseFab()
|
||||
setUpPrevNext()
|
||||
setUpRepeatButton()
|
||||
setUpShuffleButton()
|
||||
setUpProgressSlider()
|
||||
setupFavouriteToggle()
|
||||
}
|
||||
|
||||
private fun setupFavouriteToggle() {
|
||||
songFavourite.setOnClickListener {
|
||||
MusicUtil.toggleFavorite(
|
||||
this@DriveModeActivity,
|
||||
MusicPlayerRemote.currentSong
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun toggleFavourite() {
|
||||
CoroutineScope(Dispatchers.IO).launch {
|
||||
val isFavourite =
|
||||
MusicUtil.isFavorite(this@DriveModeActivity, MusicPlayerRemote.currentSong)
|
||||
withContext(Dispatchers.Main) {
|
||||
songFavourite.setImageResource(if (isFavourite) R.drawable.ic_favorite_white_24dp else R.drawable.ic_favorite_border_white_24dp)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun setUpProgressSlider() {
|
||||
progressSlider.setOnSeekBarChangeListener(object : SimpleOnSeekbarChangeListener() {
|
||||
override fun onProgressChanged(seekBar: SeekBar, progress: Int, fromUser: Boolean) {
|
||||
if (fromUser) {
|
||||
MusicPlayerRemote.seekTo(progress)
|
||||
onUpdateProgressViews(
|
||||
MusicPlayerRemote.songProgressMillis,
|
||||
MusicPlayerRemote.songDurationMillis
|
||||
)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
override fun onPause() {
|
||||
super.onPause()
|
||||
progressViewUpdateHelper.stop()
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
progressViewUpdateHelper.start()
|
||||
}
|
||||
|
||||
private fun setUpPrevNext() {
|
||||
|
||||
nextButton.setOnClickListener { MusicPlayerRemote.playNextSong() }
|
||||
previousButton.setOnClickListener { MusicPlayerRemote.back() }
|
||||
}
|
||||
|
||||
private fun setUpShuffleButton() {
|
||||
shuffleButton.setOnClickListener { MusicPlayerRemote.toggleShuffleMode() }
|
||||
}
|
||||
|
||||
private fun setUpRepeatButton() {
|
||||
repeatButton.setOnClickListener { MusicPlayerRemote.cycleRepeatMode() }
|
||||
}
|
||||
|
||||
private fun setUpPlayPauseFab() {
|
||||
playPauseButton.setOnClickListener(PlayPauseButtonOnClickHandler())
|
||||
}
|
||||
|
||||
override fun onRepeatModeChanged() {
|
||||
super.onRepeatModeChanged()
|
||||
updateRepeatState()
|
||||
}
|
||||
|
||||
override fun onShuffleModeChanged() {
|
||||
super.onShuffleModeChanged()
|
||||
updateShuffleState()
|
||||
}
|
||||
|
||||
override fun onPlayStateChanged() {
|
||||
super.onPlayStateChanged()
|
||||
updatePlayPauseDrawableState()
|
||||
}
|
||||
|
||||
override fun onServiceConnected() {
|
||||
super.onServiceConnected()
|
||||
updatePlayPauseDrawableState()
|
||||
updateSong()
|
||||
updateRepeatState()
|
||||
updateShuffleState()
|
||||
toggleFavourite()
|
||||
}
|
||||
|
||||
private fun updatePlayPauseDrawableState() {
|
||||
if (MusicPlayerRemote.isPlaying) {
|
||||
playPauseButton.setImageResource(R.drawable.ic_pause_white_24dp)
|
||||
} else {
|
||||
playPauseButton.setImageResource(R.drawable.ic_play_arrow_white_24dp)
|
||||
}
|
||||
}
|
||||
|
||||
fun updateShuffleState() {
|
||||
when (MusicPlayerRemote.shuffleMode) {
|
||||
MusicService.SHUFFLE_MODE_SHUFFLE -> shuffleButton.setColorFilter(
|
||||
lastPlaybackControlsColor,
|
||||
PorterDuff.Mode.SRC_IN
|
||||
)
|
||||
else -> shuffleButton.setColorFilter(
|
||||
lastDisabledPlaybackControlsColor,
|
||||
PorterDuff.Mode.SRC_IN
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateRepeatState() {
|
||||
when (MusicPlayerRemote.repeatMode) {
|
||||
MusicService.REPEAT_MODE_NONE -> {
|
||||
repeatButton.setImageResource(R.drawable.ic_repeat_white_24dp)
|
||||
repeatButton.setColorFilter(
|
||||
lastDisabledPlaybackControlsColor,
|
||||
PorterDuff.Mode.SRC_IN
|
||||
)
|
||||
}
|
||||
MusicService.REPEAT_MODE_ALL -> {
|
||||
repeatButton.setImageResource(R.drawable.ic_repeat_white_24dp)
|
||||
repeatButton.setColorFilter(lastPlaybackControlsColor, PorterDuff.Mode.SRC_IN)
|
||||
}
|
||||
MusicService.REPEAT_MODE_THIS -> {
|
||||
repeatButton.setImageResource(R.drawable.ic_repeat_one_white_24dp)
|
||||
repeatButton.setColorFilter(lastPlaybackControlsColor, PorterDuff.Mode.SRC_IN)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onPlayingMetaChanged() {
|
||||
super.onPlayingMetaChanged()
|
||||
updateSong()
|
||||
toggleFavourite()
|
||||
}
|
||||
|
||||
private fun updateSong() {
|
||||
val song = MusicPlayerRemote.currentSong
|
||||
|
||||
songTitle.text = song.title
|
||||
songText.text = song.artistName
|
||||
|
||||
SongGlideRequest.Builder.from(Glide.with(this), song)
|
||||
.checkIgnoreMediaStore(this)
|
||||
.generatePalette(this)
|
||||
.build()
|
||||
.transform(BlurTransformation.Builder(this).build())
|
||||
.into(object : RetroMusicColoredTarget(image) {
|
||||
override fun onColorReady(color: MediaNotificationProcessor) {
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
override fun onUpdateProgressViews(progress: Int, total: Int) {
|
||||
progressSlider.max = total
|
||||
|
||||
val animator = ObjectAnimator.ofInt(progressSlider, "progress", progress)
|
||||
animator.duration = AbsPlayerControlsFragment.SLIDER_ANIMATION_TIME
|
||||
animator.interpolator = LinearInterpolator()
|
||||
animator.start()
|
||||
|
||||
songTotalTime.text = MusicUtil.getReadableDurationString(total.toLong())
|
||||
songCurrentProgress.text = MusicUtil.getReadableDurationString(progress.toLong())
|
||||
}
|
||||
}
|
|
@ -1,162 +0,0 @@
|
|||
package code.name.monkey.retromusic.activities
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.Menu
|
||||
import android.view.MenuItem
|
||||
import android.view.View
|
||||
import androidx.recyclerview.widget.DefaultItemAnimator
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import code.name.monkey.appthemehelper.util.ATHUtil
|
||||
import code.name.monkey.retromusic.App
|
||||
import code.name.monkey.retromusic.R
|
||||
import code.name.monkey.retromusic.activities.base.AbsSlidingMusicPanelActivity
|
||||
import code.name.monkey.retromusic.adapter.song.ShuffleButtonSongAdapter
|
||||
import code.name.monkey.retromusic.extensions.applyToolbar
|
||||
import code.name.monkey.retromusic.helper.menu.GenreMenuHelper
|
||||
import code.name.monkey.retromusic.interfaces.CabHolder
|
||||
import code.name.monkey.retromusic.model.Genre
|
||||
import code.name.monkey.retromusic.model.Song
|
||||
import code.name.monkey.retromusic.mvp.presenter.GenreDetailsPresenter
|
||||
import code.name.monkey.retromusic.mvp.presenter.GenreDetailsView
|
||||
import code.name.monkey.retromusic.util.DensityUtil
|
||||
import code.name.monkey.retromusic.util.RetroColorUtil
|
||||
import com.afollestad.materialcab.MaterialCab
|
||||
import kotlinx.android.synthetic.main.activity_playlist_detail.*
|
||||
import java.util.*
|
||||
import javax.inject.Inject
|
||||
|
||||
/**
|
||||
* @author Hemanth S (h4h13).
|
||||
*/
|
||||
|
||||
class GenreDetailsActivity : AbsSlidingMusicPanelActivity(), CabHolder, GenreDetailsView {
|
||||
|
||||
@Inject
|
||||
lateinit var genreDetailsPresenter: GenreDetailsPresenter
|
||||
|
||||
private lateinit var genre: Genre
|
||||
private lateinit var songAdapter: ShuffleButtonSongAdapter
|
||||
private var cab: MaterialCab? = null
|
||||
|
||||
private fun getEmojiByUnicode(unicode: Int): String {
|
||||
return String(Character.toChars(unicode))
|
||||
}
|
||||
|
||||
private fun checkIsEmpty() {
|
||||
checkForPadding()
|
||||
emptyEmoji.text = getEmojiByUnicode(0x1F631)
|
||||
empty?.visibility = if (songAdapter.itemCount == 0) View.VISIBLE else View.GONE
|
||||
}
|
||||
|
||||
private fun checkForPadding() {
|
||||
val height = DensityUtil.dip2px(this, 52f)
|
||||
recyclerView.setPadding(0, 0, 0, (height))
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
setDrawUnderStatusBar()
|
||||
super.onCreate(savedInstanceState)
|
||||
setStatusbarColorAuto()
|
||||
setNavigationbarColorAuto()
|
||||
setTaskDescriptionColorAuto()
|
||||
setLightNavigationBar(true)
|
||||
setBottomBarVisibility(View.GONE)
|
||||
if (intent.extras != null) {
|
||||
genre = intent?.extras?.getParcelable(EXTRA_GENRE_ID)!!
|
||||
} else {
|
||||
finish()
|
||||
}
|
||||
|
||||
setUpToolBar()
|
||||
setupRecyclerView()
|
||||
|
||||
App.musicComponent.inject(this)
|
||||
genreDetailsPresenter.attachView(this)
|
||||
}
|
||||
|
||||
private fun setUpToolBar() {
|
||||
applyToolbar(toolbar)
|
||||
title = genre.name
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
genreDetailsPresenter.loadGenreSongs(genre.id)
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
super.onDestroy()
|
||||
genreDetailsPresenter.detachView()
|
||||
}
|
||||
|
||||
override fun createContentView(): View {
|
||||
return wrapSlidingMusicPanel(R.layout.activity_playlist_detail)
|
||||
}
|
||||
|
||||
override fun showEmptyView() {
|
||||
}
|
||||
|
||||
override fun onCreateOptionsMenu(menu: Menu): Boolean {
|
||||
menuInflater.inflate(R.menu.menu_genre_detail, menu)
|
||||
return super.onCreateOptionsMenu(menu)
|
||||
}
|
||||
|
||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||
if (item.itemId == android.R.id.home) {
|
||||
onBackPressed()
|
||||
}
|
||||
return GenreMenuHelper.handleMenuClick(this, genre, item)
|
||||
}
|
||||
|
||||
private fun setupRecyclerView() {
|
||||
songAdapter = ShuffleButtonSongAdapter(this, ArrayList(), R.layout.item_list, this)
|
||||
recyclerView.apply {
|
||||
itemAnimator = DefaultItemAnimator()
|
||||
layoutManager = LinearLayoutManager(this@GenreDetailsActivity)
|
||||
adapter = songAdapter
|
||||
}
|
||||
songAdapter.registerAdapterDataObserver(object : RecyclerView.AdapterDataObserver() {
|
||||
override fun onChanged() {
|
||||
super.onChanged()
|
||||
checkIsEmpty()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
override fun songs(songs: List<Song>) {
|
||||
songAdapter.swapDataSet(songs)
|
||||
}
|
||||
|
||||
override fun openCab(menuRes: Int, callback: MaterialCab.Callback): MaterialCab {
|
||||
if (cab != null && cab!!.isActive) cab?.finish()
|
||||
cab = MaterialCab(this, R.id.cab_stub).setMenu(menuRes)
|
||||
.setCloseDrawableRes(R.drawable.ic_close_white_24dp)
|
||||
.setBackgroundColor(
|
||||
RetroColorUtil.shiftBackgroundColorForLightText(
|
||||
ATHUtil.resolveColor(
|
||||
this,
|
||||
R.attr.colorSurface
|
||||
)
|
||||
)
|
||||
).start(callback)
|
||||
return cab!!
|
||||
}
|
||||
|
||||
override fun onBackPressed() {
|
||||
if (cab != null && cab!!.isActive) cab!!.finish()
|
||||
else {
|
||||
recyclerView!!.stopScroll()
|
||||
super.onBackPressed()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onMediaStoreChanged() {
|
||||
super.onMediaStoreChanged()
|
||||
genreDetailsPresenter.loadGenreSongs(genre.id)
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val EXTRA_GENRE_ID = "extra_genre_id"
|
||||
}
|
||||
}
|
|
@ -1,99 +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.activities;
|
||||
|
||||
import android.graphics.Color;
|
||||
import android.os.Bundle;
|
||||
import android.view.MenuItem;
|
||||
import android.webkit.WebView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
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.util.ATHUtil;
|
||||
import code.name.monkey.appthemehelper.util.ColorUtil;
|
||||
import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper;
|
||||
import code.name.monkey.retromusic.R;
|
||||
import code.name.monkey.retromusic.activities.base.AbsBaseActivity;
|
||||
|
||||
/**
|
||||
* Created by hemanths on 2019-09-27.
|
||||
*/
|
||||
public class LicenseActivity extends AbsBaseActivity {
|
||||
|
||||
@Override
|
||||
protected void onCreate(@Nullable Bundle savedInstanceState) {
|
||||
setDrawUnderStatusBar();
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_license);
|
||||
setStatusbarColorAuto();
|
||||
setNavigationbarColorAuto();
|
||||
setLightNavigationBar(true);
|
||||
Toolbar toolbar = findViewById(R.id.toolbar);
|
||||
setSupportActionBar(toolbar);
|
||||
ToolbarContentTintHelper.colorBackButton(toolbar);
|
||||
toolbar.setBackgroundColor(ATHUtil.INSTANCE.resolveColor(this, R.attr.colorSurface));
|
||||
WebView webView = findViewById(R.id.license);
|
||||
try {
|
||||
StringBuilder buf = new StringBuilder();
|
||||
InputStream json = getAssets().open("oldindex.html");
|
||||
BufferedReader in = new BufferedReader(new InputStreamReader(json, StandardCharsets.UTF_8));
|
||||
String str;
|
||||
while ((str = in.readLine()) != null) {
|
||||
buf.append(str);
|
||||
}
|
||||
in.close();
|
||||
|
||||
// Inject color values for WebView body background and links
|
||||
final boolean isDark = ATHUtil.INSTANCE.isWindowBackgroundDark(this);
|
||||
final String backgroundColor = colorToCSS(ATHUtil.INSTANCE.resolveColor(this, R.attr.colorSurface,
|
||||
Color.parseColor(isDark ? "#424242" : "#ffffff")));
|
||||
final String contentColor = colorToCSS(Color.parseColor(isDark ? "#ffffff" : "#000000"));
|
||||
final String changeLog = buf.toString()
|
||||
.replace("{style-placeholder}",
|
||||
String.format("body { background-color: %s; color: %s; }", backgroundColor, contentColor))
|
||||
.replace("{link-color}", colorToCSS(ThemeStore.Companion.accentColor(this)))
|
||||
.replace("{link-color-active}",
|
||||
colorToCSS(ColorUtil.INSTANCE.lightenColor(ThemeStore.Companion.accentColor(this))));
|
||||
|
||||
webView.loadData(changeLog, "text/html", "UTF-8");
|
||||
} catch (Throwable e) {
|
||||
webView.loadData("<h1>Unable to load</h1><p>" + e.getLocalizedMessage() + "</p>", "text/html", "UTF-8");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@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
|
||||
}
|
||||
}
|
|
@ -1,100 +0,0 @@
|
|||
package code.name.monkey.retromusic.activities
|
||||
|
||||
import android.app.KeyguardManager
|
||||
import android.content.Context
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import android.view.WindowManager
|
||||
import androidx.core.view.ViewCompat
|
||||
import code.name.monkey.retromusic.R
|
||||
import code.name.monkey.retromusic.activities.base.AbsMusicServiceActivity
|
||||
import code.name.monkey.retromusic.fragments.player.lockscreen.LockScreenPlayerControlsFragment
|
||||
import code.name.monkey.retromusic.glide.RetroMusicColoredTarget
|
||||
import code.name.monkey.retromusic.glide.SongGlideRequest
|
||||
import code.name.monkey.retromusic.helper.MusicPlayerRemote
|
||||
import code.name.monkey.retromusic.util.color.MediaNotificationProcessor
|
||||
import com.bumptech.glide.Glide
|
||||
import com.r0adkll.slidr.Slidr
|
||||
import com.r0adkll.slidr.model.SlidrConfig
|
||||
import com.r0adkll.slidr.model.SlidrListener
|
||||
import com.r0adkll.slidr.model.SlidrPosition
|
||||
import kotlinx.android.synthetic.main.activity_lock_screen.*
|
||||
|
||||
class LockScreenActivity : AbsMusicServiceActivity() {
|
||||
private var fragment: LockScreenPlayerControlsFragment? = null
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
setDrawUnderStatusBar()
|
||||
super.onCreate(savedInstanceState)
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O_MR1) {
|
||||
setShowWhenLocked(true)
|
||||
setTurnScreenOn(true)
|
||||
} else {
|
||||
this.window.addFlags(
|
||||
WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD or
|
||||
WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED or
|
||||
WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON
|
||||
)
|
||||
}
|
||||
setContentView(R.layout.activity_lock_screen)
|
||||
hideStatusBar()
|
||||
setStatusbarColorAuto()
|
||||
setNavigationbarColorAuto()
|
||||
setTaskDescriptionColorAuto()
|
||||
setLightNavigationBar(true)
|
||||
|
||||
val config = SlidrConfig.Builder().listener(object : SlidrListener {
|
||||
override fun onSlideStateChanged(state: Int) {
|
||||
}
|
||||
|
||||
override fun onSlideChange(percent: Float) {
|
||||
}
|
||||
|
||||
override fun onSlideOpened() {
|
||||
}
|
||||
|
||||
override fun onSlideClosed(): 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()
|
||||
|
||||
Slidr.attach(this, config)
|
||||
|
||||
fragment =
|
||||
supportFragmentManager.findFragmentById(R.id.playback_controls_fragment) as LockScreenPlayerControlsFragment?
|
||||
|
||||
findViewById<View>(R.id.slide).apply {
|
||||
translationY = 100f
|
||||
alpha = 0f
|
||||
ViewCompat.animate(this).translationY(0f).alpha(1f).setDuration(1500).start()
|
||||
}
|
||||
}
|
||||
|
||||
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(colors: MediaNotificationProcessor) {
|
||||
fragment?.setColor(colors)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
|
@ -1,414 +0,0 @@
|
|||
package code.name.monkey.retromusic.activities
|
||||
|
||||
import android.R.attr
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.res.ColorStateList
|
||||
import android.os.AsyncTask
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.text.TextUtils
|
||||
import android.view.*
|
||||
import androidx.annotation.StringRes
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.fragment.app.FragmentManager
|
||||
import androidx.fragment.app.FragmentStatePagerAdapter
|
||||
import androidx.viewpager.widget.ViewPager
|
||||
import code.name.monkey.appthemehelper.ThemeStore
|
||||
import code.name.monkey.appthemehelper.util.ATHUtil.resolveColor
|
||||
import code.name.monkey.appthemehelper.util.ColorUtil
|
||||
import code.name.monkey.appthemehelper.util.MaterialValueHelper
|
||||
import code.name.monkey.appthemehelper.util.TintHelper
|
||||
import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper
|
||||
import code.name.monkey.retromusic.App
|
||||
import code.name.monkey.retromusic.R
|
||||
import code.name.monkey.retromusic.activities.base.AbsMusicServiceActivity
|
||||
import code.name.monkey.retromusic.extensions.surfaceColor
|
||||
import code.name.monkey.retromusic.extensions.textColorSecondary
|
||||
import code.name.monkey.retromusic.fragments.base.AbsMusicServiceFragment
|
||||
import code.name.monkey.retromusic.helper.MusicPlayerRemote
|
||||
import code.name.monkey.retromusic.helper.MusicProgressViewUpdateHelper
|
||||
import code.name.monkey.retromusic.lyrics.LrcHelper
|
||||
import code.name.monkey.retromusic.model.Song
|
||||
import code.name.monkey.retromusic.model.lyrics.Lyrics
|
||||
import code.name.monkey.retromusic.util.LyricUtil
|
||||
import code.name.monkey.retromusic.util.MusicUtil
|
||||
import code.name.monkey.retromusic.util.PreferenceUtil
|
||||
import kotlinx.android.synthetic.main.activity_lyrics.*
|
||||
import kotlinx.android.synthetic.main.fragment_lyrics.*
|
||||
import kotlinx.android.synthetic.main.fragment_synced.*
|
||||
import java.io.File
|
||||
|
||||
class LyricsActivity : AbsMusicServiceActivity(), View.OnClickListener,
|
||||
ViewPager.OnPageChangeListener {
|
||||
override fun onPageScrollStateChanged(state: Int) {
|
||||
when (state) {
|
||||
ViewPager.SCROLL_STATE_IDLE -> fab.show()
|
||||
ViewPager.SCROLL_STATE_DRAGGING,
|
||||
ViewPager.SCROLL_STATE_SETTLING -> fab.hide()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int) {
|
||||
}
|
||||
|
||||
override fun onPageSelected(position: Int) {
|
||||
PreferenceUtil.lyricsOption = position
|
||||
if (position == 0) fab.text = getString(R.string.synced_lyrics)
|
||||
else if (position == 1) fab.text = getString(R.string.lyrics)
|
||||
}
|
||||
|
||||
override fun onClick(v: View?) {
|
||||
when (viewPager.currentItem) {
|
||||
0 -> showSyncedLyrics()
|
||||
1 -> showLyricsSaveDialog()
|
||||
}
|
||||
}
|
||||
|
||||
private lateinit var song: Song
|
||||
private var lyricsString: String? = null
|
||||
|
||||
private val googleSearchLrcUrl: String
|
||||
get() {
|
||||
var baseUrl = "http://www.google.com/search?"
|
||||
var query = song.title + "+" + song.artistName
|
||||
query = "q=" + query.replace(" ", "+") + " .lrc"
|
||||
baseUrl += query
|
||||
return baseUrl
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(R.layout.activity_lyrics)
|
||||
setStatusbarColorAuto()
|
||||
setTaskDescriptionColorAuto()
|
||||
setNavigationbarColorAuto()
|
||||
|
||||
fab.backgroundTintList = ColorStateList.valueOf(ThemeStore.accentColor(this))
|
||||
ColorStateList.valueOf(
|
||||
MaterialValueHelper.getPrimaryTextColor(
|
||||
this,
|
||||
ColorUtil.isColorLight(ThemeStore.accentColor(this))
|
||||
)
|
||||
)
|
||||
.apply {
|
||||
fab.setTextColor(this)
|
||||
fab.iconTint = this
|
||||
}
|
||||
setupWakelock()
|
||||
|
||||
viewPager.apply {
|
||||
adapter = PagerAdapter(supportFragmentManager)
|
||||
currentItem = PreferenceUtil.lyricsOption
|
||||
addOnPageChangeListener(this@LyricsActivity)
|
||||
}
|
||||
|
||||
|
||||
toolbar.setBackgroundColor(surfaceColor())
|
||||
tabs.setBackgroundColor(surfaceColor())
|
||||
ToolbarContentTintHelper.colorBackButton(toolbar)
|
||||
setSupportActionBar(toolbar)
|
||||
tabs.setupWithViewPager(viewPager)
|
||||
tabs.setSelectedTabIndicator(
|
||||
TintHelper.createTintedDrawable(
|
||||
ContextCompat.getDrawable(
|
||||
this,
|
||||
R.drawable.tab_indicator
|
||||
), ThemeStore.accentColor(this)
|
||||
)
|
||||
)
|
||||
tabs.setTabTextColors(
|
||||
textColorSecondary(),
|
||||
ThemeStore.accentColor(this)
|
||||
)
|
||||
tabs.setSelectedTabIndicatorColor(ThemeStore.accentColor(this))
|
||||
|
||||
fab.setOnClickListener(this)
|
||||
}
|
||||
|
||||
override fun onPlayingMetaChanged() {
|
||||
super.onPlayingMetaChanged()
|
||||
updateTitleSong()
|
||||
}
|
||||
|
||||
override fun onServiceConnected() {
|
||||
super.onServiceConnected()
|
||||
updateTitleSong()
|
||||
}
|
||||
|
||||
private fun updateTitleSong() {
|
||||
song = MusicPlayerRemote.currentSong
|
||||
toolbar.title = song.title
|
||||
toolbar.subtitle = song.artistName
|
||||
}
|
||||
|
||||
private fun setupWakelock() {
|
||||
window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
|
||||
}
|
||||
|
||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||
if (item.itemId == android.R.id.home) {
|
||||
finish()
|
||||
return true
|
||||
}
|
||||
return super.onOptionsItemSelected(item)
|
||||
}
|
||||
|
||||
private fun showSyncedLyrics() {
|
||||
var content = ""
|
||||
try {
|
||||
content = LyricUtil.getStringFromFile(song.title, song.artistName)
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
/*val materialDialog = MaterialDialog(this)
|
||||
.show {
|
||||
title(R.string.add_time_framed_lryics)
|
||||
negativeButton(R.string.action_search) {
|
||||
RetroUtil.openUrl(this@LyricsActivity, googleSearchLrcUrl)
|
||||
}
|
||||
input(
|
||||
hint = getString(R.string.paste_lyrics_here),
|
||||
prefill = content,
|
||||
inputType = InputType.TYPE_CLASS_TEXT or InputType.TYPE_TEXT_FLAG_MULTI_LINE
|
||||
) { _, input ->
|
||||
LyricUtil.writeLrcToLoc(song.title, song.artistName, input.toString())
|
||||
}
|
||||
positiveButton(android.R.string.ok) {
|
||||
updateSong()
|
||||
}
|
||||
}
|
||||
|
||||
MaterialUtil.setTint(materialDialog.getInputLayout(), false)*/
|
||||
}
|
||||
|
||||
private fun updateSong() {
|
||||
val page =
|
||||
supportFragmentManager.findFragmentByTag("android:switcher:" + R.id.viewPager + ":" + viewPager.currentItem)
|
||||
if (viewPager.currentItem == 0 && page != null) {
|
||||
(page as BaseLyricsFragment).upDateSong()
|
||||
}
|
||||
}
|
||||
|
||||
private fun showLyricsSaveDialog() {
|
||||
val content: String = if (lyricsString == null) {
|
||||
""
|
||||
} else {
|
||||
lyricsString!!
|
||||
}
|
||||
|
||||
/*val materialDialog = MaterialDialog(
|
||||
this
|
||||
).show {
|
||||
|
||||
title(R.string.add_lyrics)
|
||||
negativeButton(R.string.action_search) {
|
||||
RetroUtil.openUrl(this@LyricsActivity, getGoogleSearchUrl())
|
||||
}
|
||||
input(
|
||||
hint = getString(R.string.paste_lyrics_here),
|
||||
prefill = content,
|
||||
inputType = InputType.TYPE_CLASS_TEXT or InputType.TYPE_TEXT_FLAG_MULTI_LINE
|
||||
) { _, input ->
|
||||
val fieldKeyValueMap = EnumMap<FieldKey, String>(FieldKey::class.java)
|
||||
fieldKeyValueMap[FieldKey.LYRICS] = input.toString()
|
||||
WriteTagsAsyncTask(this@LyricsActivity).execute(
|
||||
WriteTagsAsyncTask.LoadingInfo(
|
||||
getSongPaths(song), fieldKeyValueMap, null
|
||||
)
|
||||
)
|
||||
}
|
||||
positiveButton(android.R.string.ok) {
|
||||
updateSong()
|
||||
}
|
||||
}
|
||||
MaterialUtil.setTint(materialDialog.getInputLayout(), false)*/
|
||||
}
|
||||
|
||||
private fun getSongPaths(song: Song): ArrayList<String> {
|
||||
val paths = ArrayList<String>(1)
|
||||
paths.add(song.data)
|
||||
return paths
|
||||
}
|
||||
|
||||
private fun getGoogleSearchUrl(): String {
|
||||
var baseUrl = "http://www.google.com/search?"
|
||||
var query = song.title + "+" + song.artistName
|
||||
query = "q=" + query.replace(" ", "+") + " lyrics"
|
||||
baseUrl += query
|
||||
return baseUrl
|
||||
}
|
||||
|
||||
class PagerAdapter(fm: FragmentManager) : FragmentStatePagerAdapter(
|
||||
fm, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT
|
||||
) {
|
||||
|
||||
class Tabs(
|
||||
@StringRes val title: Int, val fragment: Fragment
|
||||
)
|
||||
|
||||
private var tabs = ArrayList<Tabs>()
|
||||
|
||||
init {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP_MR1) {
|
||||
tabs.add(Tabs(R.string.synced_lyrics, SyncedLyricsFragment()))
|
||||
}
|
||||
tabs.add(Tabs(R.string.normal_lyrics, OfflineLyricsFragment()))
|
||||
}
|
||||
|
||||
override fun getItem(position: Int): Fragment {
|
||||
return tabs[position].fragment
|
||||
}
|
||||
|
||||
override fun getPageTitle(position: Int): CharSequence? {
|
||||
return App.getContext().getString(tabs[position].title)
|
||||
}
|
||||
|
||||
override fun getCount(): Int {
|
||||
return tabs.size
|
||||
}
|
||||
}
|
||||
|
||||
abstract class BaseLyricsFragment : AbsMusicServiceFragment() {
|
||||
abstract fun upDateSong()
|
||||
|
||||
override fun onPlayingMetaChanged() {
|
||||
super.onPlayingMetaChanged()
|
||||
upDateSong()
|
||||
}
|
||||
|
||||
override fun onServiceConnected() {
|
||||
super.onServiceConnected()
|
||||
upDateSong()
|
||||
}
|
||||
}
|
||||
|
||||
class OfflineLyricsFragment : BaseLyricsFragment() {
|
||||
override fun upDateSong() {
|
||||
loadSongLyrics()
|
||||
}
|
||||
|
||||
private var updateLyricsAsyncTask: AsyncTask<*, *, *>? = null
|
||||
private var lyrics: Lyrics? = null
|
||||
|
||||
@SuppressLint("StaticFieldLeak")
|
||||
private fun loadSongLyrics() {
|
||||
if (updateLyricsAsyncTask != null) {
|
||||
updateLyricsAsyncTask!!.cancel(false)
|
||||
}
|
||||
val song = MusicPlayerRemote.currentSong
|
||||
updateLyricsAsyncTask = object : AsyncTask<Void?, Void?, Lyrics?>() {
|
||||
override fun doInBackground(vararg params: Void?): Lyrics? {
|
||||
val data = MusicUtil.getLyrics(song)
|
||||
return if (TextUtils.isEmpty(data)) {
|
||||
null
|
||||
} else Lyrics.parse(song, data!!)
|
||||
}
|
||||
|
||||
override fun onPreExecute() {
|
||||
super.onPreExecute()
|
||||
lyrics = null
|
||||
}
|
||||
|
||||
override fun onPostExecute(l: Lyrics?) {
|
||||
lyrics = l
|
||||
offlineLyrics?.visibility = View.VISIBLE
|
||||
if (l == null) {
|
||||
offlineLyrics?.setText(R.string.no_lyrics_found)
|
||||
return
|
||||
}
|
||||
(activity as LyricsActivity).lyricsString = l.text
|
||||
offlineLyrics?.text = l.text
|
||||
}
|
||||
|
||||
override fun onCancelled(s: Lyrics?) {
|
||||
onPostExecute(null)
|
||||
}
|
||||
}.execute()
|
||||
}
|
||||
|
||||
override fun onActivityCreated(savedInstanceState: Bundle?) {
|
||||
super.onActivityCreated(savedInstanceState)
|
||||
loadSongLyrics()
|
||||
}
|
||||
|
||||
override fun onDestroyView() {
|
||||
super.onDestroyView()
|
||||
if (updateLyricsAsyncTask != null && !updateLyricsAsyncTask!!.isCancelled) {
|
||||
updateLyricsAsyncTask?.cancel(true)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?
|
||||
): View? {
|
||||
return inflater.inflate(R.layout.fragment_lyrics, container, false)
|
||||
}
|
||||
}
|
||||
|
||||
class SyncedLyricsFragment : BaseLyricsFragment(), MusicProgressViewUpdateHelper.Callback {
|
||||
override fun upDateSong() {
|
||||
loadLRCLyrics()
|
||||
}
|
||||
|
||||
private lateinit var updateHelper: MusicProgressViewUpdateHelper
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?
|
||||
): View? {
|
||||
return inflater.inflate(R.layout.fragment_synced, container, false)
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
updateHelper = MusicProgressViewUpdateHelper(this, 500, 1000)
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
setupLyricsView()
|
||||
}
|
||||
|
||||
private fun setupLyricsView() {
|
||||
lyricsView.apply {
|
||||
setCurrentPlayLineColor(ThemeStore.accentColor(requireContext()))
|
||||
setIndicatorTextColor(ThemeStore.accentColor(requireContext()))
|
||||
setCurrentIndicateLineTextColor(
|
||||
resolveColor(
|
||||
requireContext(),
|
||||
attr.textColorPrimary
|
||||
)
|
||||
)
|
||||
setNoLrcTextColor(resolveColor(requireContext(), attr.textColorPrimary))
|
||||
setOnPlayIndicatorLineListener { time, _ -> MusicPlayerRemote.seekTo(time.toInt()) }
|
||||
}
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
updateHelper.start()
|
||||
}
|
||||
|
||||
override fun onPause() {
|
||||
super.onPause()
|
||||
updateHelper.stop()
|
||||
}
|
||||
|
||||
override fun onUpdateProgressViews(progress: Int, total: Int) {
|
||||
lyricsView.updateTime(progress.toLong())
|
||||
}
|
||||
|
||||
private fun loadLRCLyrics() {
|
||||
lyricsView.resetView("Empty")
|
||||
val song = MusicPlayerRemote.currentSong
|
||||
if (LyricUtil.isLrcFileExist(song.title, song.artistName)) {
|
||||
showLyricsLocal(LyricUtil.getLocalLyricFile(song.title, song.artistName))
|
||||
}
|
||||
}
|
||||
|
||||
private fun showLyricsLocal(file: File?) {
|
||||
if (file != null) {
|
||||
lyricsView.setLrcData(LrcHelper.parseLrcFromFile(file))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,786 +0,0 @@
|
|||
package code.name.monkey.retromusic.activities
|
||||
|
||||
import android.app.ActivityOptions
|
||||
import android.content.*
|
||||
import android.content.IntentSender.SendIntentException
|
||||
import android.os.Bundle
|
||||
import android.provider.MediaStore
|
||||
import android.util.Log
|
||||
import android.view.Menu
|
||||
import android.view.MenuItem
|
||||
import android.view.SubMenu
|
||||
import android.view.View
|
||||
import androidx.core.app.ActivityCompat
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.fragment.app.commit
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import code.name.monkey.appthemehelper.ThemeStore.Companion.accentColor
|
||||
import code.name.monkey.appthemehelper.util.ATHUtil
|
||||
import code.name.monkey.appthemehelper.util.ATHUtil.resolveColor
|
||||
import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper
|
||||
import code.name.monkey.retromusic.*
|
||||
import code.name.monkey.retromusic.activities.base.AbsSlidingMusicPanelActivity
|
||||
import code.name.monkey.retromusic.dialogs.CreatePlaylistDialog.Companion.create
|
||||
import code.name.monkey.retromusic.fragments.LibraryViewModel
|
||||
import code.name.monkey.retromusic.fragments.albums.AlbumsFragment
|
||||
import code.name.monkey.retromusic.fragments.artists.ArtistsFragment
|
||||
import code.name.monkey.retromusic.fragments.base.AbsLibraryPagerRecyclerViewCustomGridSizeFragment
|
||||
import code.name.monkey.retromusic.fragments.genres.GenresFragment
|
||||
import code.name.monkey.retromusic.fragments.home.BannerHomeFragment
|
||||
import code.name.monkey.retromusic.fragments.mainactivity.FoldersFragment
|
||||
import code.name.monkey.retromusic.fragments.playlists.PlaylistsFragment
|
||||
import code.name.monkey.retromusic.fragments.queue.PlayingQueueFragment
|
||||
import code.name.monkey.retromusic.fragments.songs.SongsFragment
|
||||
import code.name.monkey.retromusic.helper.MusicPlayerRemote.isPlaying
|
||||
import code.name.monkey.retromusic.helper.MusicPlayerRemote.openAndShuffleQueue
|
||||
import code.name.monkey.retromusic.helper.MusicPlayerRemote.openQueue
|
||||
import code.name.monkey.retromusic.helper.MusicPlayerRemote.playFromUri
|
||||
import code.name.monkey.retromusic.helper.MusicPlayerRemote.shuffleMode
|
||||
import code.name.monkey.retromusic.helper.SearchQueryHelper.getSongs
|
||||
import code.name.monkey.retromusic.helper.SortOrder.*
|
||||
import code.name.monkey.retromusic.interfaces.CabHolder
|
||||
import code.name.monkey.retromusic.interfaces.MainActivityFragmentCallbacks
|
||||
import code.name.monkey.retromusic.loaders.AlbumLoader.getAlbum
|
||||
import code.name.monkey.retromusic.loaders.ArtistLoader.getArtist
|
||||
import code.name.monkey.retromusic.loaders.PlaylistSongsLoader.getPlaylistSongList
|
||||
import code.name.monkey.retromusic.model.Song
|
||||
import code.name.monkey.retromusic.service.MusicService
|
||||
import code.name.monkey.retromusic.util.AppRater.appLaunched
|
||||
import code.name.monkey.retromusic.util.NavigationUtil
|
||||
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.google.android.material.appbar.AppBarLayout
|
||||
import com.google.android.material.snackbar.Snackbar
|
||||
import com.google.android.play.core.appupdate.AppUpdateInfo
|
||||
import com.google.android.play.core.appupdate.AppUpdateManager
|
||||
import com.google.android.play.core.appupdate.AppUpdateManagerFactory
|
||||
import com.google.android.play.core.install.InstallState
|
||||
import com.google.android.play.core.install.InstallStateUpdatedListener
|
||||
import com.google.android.play.core.install.model.AppUpdateType
|
||||
import com.google.android.play.core.install.model.InstallStatus.DOWNLOADED
|
||||
import com.google.android.play.core.install.model.InstallStatus.INSTALLED
|
||||
import com.google.android.play.core.install.model.UpdateAvailability
|
||||
import com.google.android.play.core.tasks.Task
|
||||
import kotlinx.android.synthetic.main.activity_main_content.*
|
||||
import java.util.*
|
||||
|
||||
class MainActivity : AbsSlidingMusicPanelActivity(),
|
||||
SharedPreferences.OnSharedPreferenceChangeListener, CabHolder {
|
||||
companion object {
|
||||
const val TAG = "MainActivity"
|
||||
const val EXPAND_PANEL = "expand_panel"
|
||||
const val APP_UPDATE_REQUEST_CODE = 9002
|
||||
}
|
||||
|
||||
lateinit var libraryViewModel: LibraryViewModel
|
||||
private var cab: MaterialCab? = null
|
||||
private val intentFilter = IntentFilter(Intent.ACTION_SCREEN_OFF)
|
||||
private lateinit var currentFragment: MainActivityFragmentCallbacks
|
||||
private var appUpdateManager: AppUpdateManager? = null
|
||||
private var blockRequestPermissions = false
|
||||
private val listener = object : InstallStateUpdatedListener {
|
||||
override fun onStateUpdate(state: InstallState) {
|
||||
when {
|
||||
state.installStatus() == DOWNLOADED -> {
|
||||
popupSnackBarForCompleteUpdate()
|
||||
}
|
||||
state.installStatus() == INSTALLED -> {
|
||||
appUpdateManager?.unregisterListener(this)
|
||||
}
|
||||
else -> {
|
||||
Log.i(TAG, "InstallStateUpdatedListener: state: " + state.installStatus())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
private val broadcastReceiver: BroadcastReceiver = object : BroadcastReceiver() {
|
||||
override fun onReceive(context: Context, intent: Intent) {
|
||||
val action = intent.action
|
||||
if (action != null && action == Intent.ACTION_SCREEN_OFF) {
|
||||
if (PreferenceUtil.isLockScreen && isPlaying) {
|
||||
val activity = Intent(context, LockScreenActivity::class.java)
|
||||
activity.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||
activity.addFlags(Intent.FLAG_ACTIVITY_NO_HISTORY)
|
||||
ActivityCompat.startActivity(context, activity, null)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun createContentView(): View {
|
||||
return wrapSlidingMusicPanel(R.layout.activity_main_content)
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
setDrawUnderStatusBar()
|
||||
super.onCreate(savedInstanceState)
|
||||
setStatusbarColorAuto()
|
||||
setNavigationbarColorAuto()
|
||||
setLightNavigationBar(true)
|
||||
setTaskDescriptionColorAuto()
|
||||
hideStatusBar()
|
||||
setBottomBarVisibility(View.VISIBLE)
|
||||
|
||||
libraryViewModel = ViewModelProvider(this).get(LibraryViewModel::class.java)
|
||||
addMusicServiceEventListener(libraryViewModel)
|
||||
|
||||
if (savedInstanceState == null) {
|
||||
selectedFragment(PreferenceUtil.lastPage)
|
||||
} else {
|
||||
restoreCurrentFragment()
|
||||
}
|
||||
|
||||
appLaunched(this)
|
||||
setupToolbar()
|
||||
checkUpdate()
|
||||
updateTabs()
|
||||
getBottomNavigationView().selectedItemId = PreferenceUtil.lastPage
|
||||
getBottomNavigationView().setOnNavigationItemSelectedListener {
|
||||
PreferenceUtil.lastPage = it.itemId
|
||||
selectedFragment(it.itemId)
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
registerReceiver(broadcastReceiver, intentFilter)
|
||||
PreferenceUtil.registerOnSharedPreferenceChangedListener(this)
|
||||
if (intent.hasExtra(EXPAND_PANEL) &&
|
||||
intent.getBooleanExtra(EXPAND_PANEL, false) &&
|
||||
PreferenceUtil.isExpandPanel
|
||||
) {
|
||||
expandPanel()
|
||||
intent.removeExtra(EXPAND_PANEL)
|
||||
}
|
||||
|
||||
appUpdateManager?.appUpdateInfo
|
||||
?.addOnSuccessListener { appUpdateInfo: AppUpdateInfo ->
|
||||
if (appUpdateInfo.installStatus() == DOWNLOADED) {
|
||||
popupSnackBarForCompleteUpdate()
|
||||
}
|
||||
try {
|
||||
if (appUpdateInfo.updateAvailability() == UpdateAvailability.DEVELOPER_TRIGGERED_UPDATE_IN_PROGRESS) {
|
||||
appUpdateManager!!.startUpdateFlowForResult(
|
||||
appUpdateInfo,
|
||||
AppUpdateType.IMMEDIATE,
|
||||
this,
|
||||
APP_UPDATE_REQUEST_CODE
|
||||
)
|
||||
}
|
||||
} catch (e: SendIntentException) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
super.onDestroy()
|
||||
unregisterReceiver(broadcastReceiver)
|
||||
PreferenceUtil.unregisterOnSharedPreferenceChangedListener(this)
|
||||
}
|
||||
|
||||
override fun onPrepareOptionsMenu(menu: Menu?): Boolean {
|
||||
ToolbarContentTintHelper.handleOnPrepareOptionsMenu(this, toolbar)
|
||||
return super.onPrepareOptionsMenu(menu)
|
||||
}
|
||||
|
||||
override fun onCreateOptionsMenu(menu: Menu?): Boolean {
|
||||
menuInflater.inflate(R.menu.menu_main, menu)
|
||||
menu ?: return super.onCreateOptionsMenu(menu)
|
||||
if (isPlaylistPage()) {
|
||||
menu.add(0, R.id.action_new_playlist, 1, R.string.new_playlist_title)
|
||||
.setIcon(R.drawable.ic_playlist_add_white_24dp)
|
||||
.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM)
|
||||
}
|
||||
if (isHomePage()) {
|
||||
menu.add(0, R.id.action_mic, 1, getString(R.string.action_search))
|
||||
.setIcon(R.drawable.ic_mic_white_24dp)
|
||||
.setShowAsActionFlags(MenuItem.SHOW_AS_ACTION_IF_ROOM)
|
||||
}
|
||||
if (isFolderPage()) {
|
||||
menu.add(0, R.id.action_scan, 0, R.string.scan_media)
|
||||
.setIcon(R.drawable.ic_scanner_white_24dp)
|
||||
.setShowAsActionFlags(MenuItem.SHOW_AS_ACTION_IF_ROOM)
|
||||
menu.add(0, R.id.action_go_to_start_directory, 1, R.string.action_go_to_start_directory)
|
||||
.setIcon(R.drawable.ic_bookmark_music_white_24dp)
|
||||
.setShowAsActionFlags(MenuItem.SHOW_AS_ACTION_IF_ROOM)
|
||||
}
|
||||
val fragment: Fragment? = getCurrentFragment()
|
||||
if (fragment != null && fragment is AbsLibraryPagerRecyclerViewCustomGridSizeFragment<*, *>) {
|
||||
val gridSizeItem: MenuItem = menu.findItem(R.id.action_grid_size)
|
||||
if (RetroUtil.isLandscape()) {
|
||||
gridSizeItem.setTitle(R.string.action_grid_size_land)
|
||||
}
|
||||
setUpGridSizeMenu(fragment, gridSizeItem.subMenu)
|
||||
setupLayoutMenu(fragment, menu.findItem(R.id.action_layout_type).subMenu)
|
||||
setUpSortOrderMenu(fragment, menu.findItem(R.id.action_sort_order).subMenu)
|
||||
} else {
|
||||
menu.removeItem(R.id.action_layout_type)
|
||||
menu.removeItem(R.id.action_grid_size)
|
||||
menu.removeItem(R.id.action_sort_order)
|
||||
}
|
||||
menu.add(0, R.id.action_settings, 6, getString(R.string.action_settings))
|
||||
.setIcon(R.drawable.ic_settings_white_24dp)
|
||||
.setShowAsActionFlags(MenuItem.SHOW_AS_ACTION_IF_ROOM)
|
||||
menu.add(0, R.id.action_search, 0, getString(R.string.action_search))
|
||||
.setIcon(R.drawable.ic_search_white_24dp)
|
||||
.setShowAsActionFlags(MenuItem.SHOW_AS_ACTION_ALWAYS)
|
||||
ToolbarContentTintHelper.handleOnCreateOptionsMenu(
|
||||
this,
|
||||
toolbar,
|
||||
menu,
|
||||
getToolbarBackgroundColor(toolbar)
|
||||
)
|
||||
return super.onCreateOptionsMenu(menu)
|
||||
}
|
||||
|
||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||
val fragment = getCurrentFragment()
|
||||
if (fragment is AbsLibraryPagerRecyclerViewCustomGridSizeFragment<*, *>) {
|
||||
if (handleGridSizeMenuItem(fragment, item)) {
|
||||
return true
|
||||
}
|
||||
if (handleLayoutResType(fragment, item)) {
|
||||
return true
|
||||
}
|
||||
if (handleSortOrderMenuItem(fragment, item)) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
when (item.itemId) {
|
||||
R.id.action_search -> NavigationUtil.goToSearch(this)
|
||||
R.id.action_new_playlist -> {
|
||||
create().show(supportFragmentManager, "CREATE_PLAYLIST")
|
||||
return true
|
||||
}
|
||||
R.id.action_mic -> {
|
||||
val options = ActivityOptions.makeSceneTransitionAnimation(
|
||||
this, toolbar,
|
||||
getString(R.string.transition_toolbar)
|
||||
)
|
||||
NavigationUtil.goToSearch(this, true, options)
|
||||
return true
|
||||
}
|
||||
R.id.action_settings -> {
|
||||
NavigationUtil.goToSettings(this)
|
||||
return true
|
||||
}
|
||||
}
|
||||
return super.onOptionsItemSelected(item)
|
||||
}
|
||||
|
||||
private fun handleSortOrderMenuItem(
|
||||
fragment: AbsLibraryPagerRecyclerViewCustomGridSizeFragment<*, *>,
|
||||
item: MenuItem
|
||||
): Boolean {
|
||||
var sortOrder: String? = null
|
||||
when (fragment) {
|
||||
is AlbumsFragment -> {
|
||||
when (item.itemId) {
|
||||
R.id.action_album_sort_order_asc -> sortOrder = AlbumSortOrder.ALBUM_A_Z
|
||||
R.id.action_album_sort_order_desc -> sortOrder = AlbumSortOrder.ALBUM_Z_A
|
||||
R.id.action_album_sort_order_artist -> sortOrder = AlbumSortOrder.ALBUM_ARTIST
|
||||
R.id.action_album_sort_order_year -> sortOrder = AlbumSortOrder.ALBUM_YEAR
|
||||
}
|
||||
}
|
||||
is ArtistsFragment -> {
|
||||
when (item.itemId) {
|
||||
R.id.action_artist_sort_order_asc -> sortOrder = ArtistSortOrder.ARTIST_A_Z
|
||||
R.id.action_artist_sort_order_desc -> sortOrder = ArtistSortOrder.ARTIST_Z_A
|
||||
}
|
||||
}
|
||||
is SongsFragment -> {
|
||||
when (item.itemId) {
|
||||
R.id.action_song_sort_order_asc -> sortOrder = SongSortOrder.SONG_A_Z
|
||||
R.id.action_song_sort_order_desc -> sortOrder = SongSortOrder.SONG_Z_A
|
||||
R.id.action_song_sort_order_artist -> sortOrder = SongSortOrder.SONG_ARTIST
|
||||
R.id.action_song_sort_order_album -> sortOrder = SongSortOrder.SONG_ALBUM
|
||||
R.id.action_song_sort_order_year -> sortOrder = SongSortOrder.SONG_YEAR
|
||||
R.id.action_song_sort_order_date -> sortOrder = SongSortOrder.SONG_DATE
|
||||
R.id.action_song_sort_order_composer -> sortOrder = SongSortOrder.COMPOSER
|
||||
R.id.action_song_sort_order_date_modified ->
|
||||
sortOrder = SongSortOrder.SONG_DATE_MODIFIED
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (sortOrder != null) {
|
||||
item.isChecked = true
|
||||
fragment.setAndSaveSortOrder(sortOrder)
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
private fun handleLayoutResType(
|
||||
fragment: AbsLibraryPagerRecyclerViewCustomGridSizeFragment<*, *>,
|
||||
item: MenuItem
|
||||
): Boolean {
|
||||
var layoutRes = -1
|
||||
when (item.itemId) {
|
||||
R.id.action_layout_normal -> layoutRes = R.layout.item_grid
|
||||
R.id.action_layout_card -> layoutRes = R.layout.item_card
|
||||
R.id.action_layout_colored_card -> layoutRes = R.layout.item_card_color
|
||||
R.id.action_layout_circular -> layoutRes = R.layout.item_grid_circle
|
||||
R.id.action_layout_image -> layoutRes = R.layout.image
|
||||
R.id.action_layout_gradient_image -> layoutRes = R.layout.item_image_gradient
|
||||
}
|
||||
if (layoutRes != -1) {
|
||||
item.isChecked = true
|
||||
fragment.setAndSaveLayoutRes(layoutRes)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
private fun handleGridSizeMenuItem(
|
||||
fragment: AbsLibraryPagerRecyclerViewCustomGridSizeFragment<*, *>,
|
||||
item: MenuItem
|
||||
): Boolean {
|
||||
var gridSize = 0
|
||||
when (item.itemId) {
|
||||
R.id.action_grid_size_1 -> gridSize = 1
|
||||
R.id.action_grid_size_2 -> gridSize = 2
|
||||
R.id.action_grid_size_3 -> gridSize = 3
|
||||
R.id.action_grid_size_4 -> gridSize = 4
|
||||
R.id.action_grid_size_5 -> gridSize = 5
|
||||
R.id.action_grid_size_6 -> gridSize = 6
|
||||
R.id.action_grid_size_7 -> gridSize = 7
|
||||
R.id.action_grid_size_8 -> gridSize = 8
|
||||
}
|
||||
if (gridSize > 0) {
|
||||
item.isChecked = true
|
||||
fragment.setAndSaveGridSize(gridSize)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
private fun setUpGridSizeMenu(
|
||||
fragment: AbsLibraryPagerRecyclerViewCustomGridSizeFragment<*, *>,
|
||||
gridSizeMenu: SubMenu
|
||||
) {
|
||||
when (fragment.getGridSize()) {
|
||||
1 -> gridSizeMenu.findItem(R.id.action_grid_size_1).isChecked = true
|
||||
2 -> gridSizeMenu.findItem(R.id.action_grid_size_2).isChecked = true
|
||||
3 -> gridSizeMenu.findItem(R.id.action_grid_size_3).isChecked = true
|
||||
4 -> gridSizeMenu.findItem(R.id.action_grid_size_4).isChecked = true
|
||||
5 -> gridSizeMenu.findItem(R.id.action_grid_size_5).isChecked = true
|
||||
6 -> gridSizeMenu.findItem(R.id.action_grid_size_6).isChecked = true
|
||||
7 -> gridSizeMenu.findItem(R.id.action_grid_size_7).isChecked = true
|
||||
8 -> gridSizeMenu.findItem(R.id.action_grid_size_8).isChecked = true
|
||||
}
|
||||
val maxGridSize = fragment.maxGridSize
|
||||
if (maxGridSize < 8) {
|
||||
gridSizeMenu.findItem(R.id.action_grid_size_8).isVisible = false
|
||||
}
|
||||
if (maxGridSize < 7) {
|
||||
gridSizeMenu.findItem(R.id.action_grid_size_7).isVisible = false
|
||||
}
|
||||
if (maxGridSize < 6) {
|
||||
gridSizeMenu.findItem(R.id.action_grid_size_6).isVisible = false
|
||||
}
|
||||
if (maxGridSize < 5) {
|
||||
gridSizeMenu.findItem(R.id.action_grid_size_5).isVisible = false
|
||||
}
|
||||
if (maxGridSize < 4) {
|
||||
gridSizeMenu.findItem(R.id.action_grid_size_4).isVisible = false
|
||||
}
|
||||
if (maxGridSize < 3) {
|
||||
gridSizeMenu.findItem(R.id.action_grid_size_3).isVisible = false
|
||||
}
|
||||
}
|
||||
|
||||
private fun setupLayoutMenu(
|
||||
fragment: AbsLibraryPagerRecyclerViewCustomGridSizeFragment<*, *>,
|
||||
subMenu: SubMenu
|
||||
) {
|
||||
when (fragment.itemLayoutRes()) {
|
||||
R.layout.item_card ->
|
||||
subMenu.findItem(R.id.action_layout_card).isChecked = true
|
||||
R.layout.item_card_color ->
|
||||
subMenu.findItem(R.id.action_layout_colored_card).isChecked = true
|
||||
R.layout.item_grid_circle ->
|
||||
subMenu.findItem(R.id.action_layout_circular).isChecked = true
|
||||
R.layout.image ->
|
||||
subMenu.findItem(R.id.action_layout_image).isChecked = true
|
||||
R.layout.item_image_gradient ->
|
||||
subMenu.findItem(R.id.action_layout_gradient_image).isChecked = true
|
||||
R.layout.item_grid ->
|
||||
subMenu.findItem(R.id.action_layout_normal).isChecked = true
|
||||
else ->
|
||||
subMenu.findItem(R.id.action_layout_normal).isChecked = true
|
||||
}
|
||||
}
|
||||
|
||||
private fun setUpSortOrderMenu(
|
||||
fragment: AbsLibraryPagerRecyclerViewCustomGridSizeFragment<*, *>,
|
||||
sortOrderMenu: SubMenu
|
||||
) {
|
||||
val currentSortOrder = fragment.getSortOrder()
|
||||
sortOrderMenu.clear()
|
||||
when (fragment) {
|
||||
is AlbumsFragment -> {
|
||||
sortOrderMenu.add(
|
||||
0,
|
||||
R.id.action_album_sort_order_asc,
|
||||
0,
|
||||
R.string.sort_order_a_z
|
||||
).isChecked = currentSortOrder == AlbumSortOrder.ALBUM_A_Z
|
||||
sortOrderMenu.add(
|
||||
0,
|
||||
R.id.action_album_sort_order_desc,
|
||||
1,
|
||||
R.string.sort_order_z_a
|
||||
).isChecked =
|
||||
currentSortOrder == AlbumSortOrder.ALBUM_Z_A
|
||||
sortOrderMenu.add(
|
||||
0,
|
||||
R.id.action_album_sort_order_artist,
|
||||
2,
|
||||
R.string.sort_order_artist
|
||||
).isChecked =
|
||||
currentSortOrder == AlbumSortOrder.ALBUM_ARTIST
|
||||
sortOrderMenu.add(
|
||||
0,
|
||||
R.id.action_album_sort_order_year,
|
||||
3,
|
||||
R.string.sort_order_year
|
||||
).isChecked =
|
||||
currentSortOrder == AlbumSortOrder.ALBUM_YEAR
|
||||
}
|
||||
is ArtistsFragment -> {
|
||||
sortOrderMenu.add(
|
||||
0,
|
||||
R.id.action_artist_sort_order_asc,
|
||||
0,
|
||||
R.string.sort_order_a_z
|
||||
).isChecked =
|
||||
currentSortOrder == ArtistSortOrder.ARTIST_A_Z
|
||||
sortOrderMenu.add(
|
||||
0,
|
||||
R.id.action_artist_sort_order_desc,
|
||||
1,
|
||||
R.string.sort_order_z_a
|
||||
).isChecked =
|
||||
currentSortOrder == ArtistSortOrder.ARTIST_Z_A
|
||||
}
|
||||
is SongsFragment -> {
|
||||
sortOrderMenu.add(
|
||||
0,
|
||||
R.id.action_song_sort_order_asc,
|
||||
0,
|
||||
R.string.sort_order_a_z
|
||||
).isChecked =
|
||||
currentSortOrder == SongSortOrder.SONG_A_Z
|
||||
sortOrderMenu.add(
|
||||
0,
|
||||
R.id.action_song_sort_order_desc,
|
||||
1,
|
||||
R.string.sort_order_z_a
|
||||
).isChecked =
|
||||
currentSortOrder == SongSortOrder.SONG_Z_A
|
||||
sortOrderMenu.add(
|
||||
0,
|
||||
R.id.action_song_sort_order_artist,
|
||||
2,
|
||||
R.string.sort_order_artist
|
||||
).isChecked =
|
||||
currentSortOrder == SongSortOrder.SONG_ARTIST
|
||||
sortOrderMenu.add(
|
||||
0,
|
||||
R.id.action_song_sort_order_album,
|
||||
3,
|
||||
R.string.sort_order_album
|
||||
).isChecked =
|
||||
currentSortOrder == SongSortOrder.SONG_ALBUM
|
||||
sortOrderMenu.add(
|
||||
0,
|
||||
R.id.action_song_sort_order_year,
|
||||
4,
|
||||
R.string.sort_order_year
|
||||
).isChecked =
|
||||
currentSortOrder == SongSortOrder.SONG_YEAR
|
||||
sortOrderMenu.add(
|
||||
0,
|
||||
R.id.action_song_sort_order_date,
|
||||
5,
|
||||
R.string.sort_order_date
|
||||
).isChecked =
|
||||
currentSortOrder == SongSortOrder.SONG_DATE
|
||||
sortOrderMenu.add(
|
||||
0,
|
||||
R.id.action_song_sort_order_date_modified,
|
||||
6,
|
||||
R.string.sort_order_date_modified
|
||||
).isChecked = currentSortOrder == SongSortOrder.SONG_DATE_MODIFIED
|
||||
sortOrderMenu.add(
|
||||
0,
|
||||
R.id.action_song_sort_order_composer,
|
||||
7,
|
||||
R.string.sort_order_composer
|
||||
).isChecked = currentSortOrder == SongSortOrder.COMPOSER
|
||||
}
|
||||
}
|
||||
sortOrderMenu.setGroupCheckable(0, true, true)
|
||||
}
|
||||
|
||||
private fun getCurrentFragment(): Fragment? {
|
||||
return supportFragmentManager.findFragmentById(R.id.fragment_container)
|
||||
}
|
||||
|
||||
private fun isFolderPage(): Boolean {
|
||||
return supportFragmentManager.findFragmentByTag(FoldersFragment.TAG) is FoldersFragment
|
||||
}
|
||||
|
||||
private fun isHomePage(): Boolean {
|
||||
return supportFragmentManager.findFragmentByTag(BannerHomeFragment.TAG) is BannerHomeFragment
|
||||
}
|
||||
|
||||
private fun isPlaylistPage(): Boolean {
|
||||
return supportFragmentManager.findFragmentByTag(PlaylistsFragment.TAG) is PlaylistsFragment
|
||||
}
|
||||
|
||||
fun addOnAppBarOffsetChangedListener(
|
||||
changedListener: AppBarLayout.OnOffsetChangedListener
|
||||
) {
|
||||
appBarLayout.addOnOffsetChangedListener(changedListener)
|
||||
}
|
||||
|
||||
fun removeOnAppBarOffsetChangedListener(
|
||||
changedListener: AppBarLayout.OnOffsetChangedListener
|
||||
) {
|
||||
appBarLayout.removeOnOffsetChangedListener(changedListener)
|
||||
}
|
||||
|
||||
fun getTotalAppBarScrollingRange(): Int {
|
||||
return appBarLayout.totalScrollRange
|
||||
}
|
||||
|
||||
override fun requestPermissions() {
|
||||
if (!blockRequestPermissions) {
|
||||
super.requestPermissions()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
||||
super.onActivityResult(requestCode, resultCode, data)
|
||||
if (!hasPermissions()) {
|
||||
requestPermissions()
|
||||
}
|
||||
}
|
||||
|
||||
private fun setupToolbar() {
|
||||
toolbar.setBackgroundColor(resolveColor(this, R.attr.colorSurface))
|
||||
appBarLayout.setBackgroundColor(resolveColor(this, R.attr.colorSurface))
|
||||
setSupportActionBar(toolbar)
|
||||
}
|
||||
|
||||
private fun checkUpdate() {
|
||||
appUpdateManager = AppUpdateManagerFactory.create(this)
|
||||
appUpdateManager?.registerListener(listener)
|
||||
|
||||
val appUpdateInfoTask: Task<AppUpdateInfo>? = appUpdateManager?.appUpdateInfo
|
||||
appUpdateInfoTask?.addOnSuccessListener { appUpdateInfo ->
|
||||
if (appUpdateInfo.updateAvailability() == UpdateAvailability.UPDATE_AVAILABLE
|
||||
&& appUpdateInfo.isUpdateTypeAllowed(AppUpdateType.IMMEDIATE)
|
||||
) {
|
||||
try {
|
||||
appUpdateManager?.startUpdateFlowForResult(
|
||||
appUpdateInfo,
|
||||
AppUpdateType.IMMEDIATE,
|
||||
this,
|
||||
APP_UPDATE_REQUEST_CODE
|
||||
)
|
||||
} catch (e: SendIntentException) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun popupSnackBarForCompleteUpdate() {
|
||||
val snackBar =
|
||||
Snackbar.make(
|
||||
findViewById(R.id.mainContent),
|
||||
"New app is ready!",
|
||||
Snackbar.LENGTH_INDEFINITE
|
||||
)
|
||||
snackBar.setAction(
|
||||
"Install"
|
||||
) {
|
||||
appUpdateManager?.completeUpdate()
|
||||
}
|
||||
snackBar.setActionTextColor(accentColor(this))
|
||||
snackBar.show()
|
||||
}
|
||||
|
||||
private fun setCurrentFragment(
|
||||
fragment: Fragment,
|
||||
tag: String
|
||||
) {
|
||||
supportFragmentManager.commit {
|
||||
replace(R.id.fragment_container, fragment, tag)
|
||||
}
|
||||
currentFragment = fragment as MainActivityFragmentCallbacks
|
||||
}
|
||||
|
||||
private fun selectedFragment(itemId: Int) {
|
||||
when (itemId) {
|
||||
R.id.action_album -> setCurrentFragment(
|
||||
AlbumsFragment.newInstance(),
|
||||
AlbumsFragment.TAG
|
||||
)
|
||||
R.id.action_artist -> setCurrentFragment(
|
||||
ArtistsFragment.newInstance(),
|
||||
ArtistsFragment.TAG
|
||||
)
|
||||
R.id.action_playlist -> setCurrentFragment(
|
||||
PlaylistsFragment.newInstance(),
|
||||
PlaylistsFragment.TAG
|
||||
)
|
||||
R.id.action_genre -> setCurrentFragment(
|
||||
GenresFragment.newInstance(),
|
||||
GenresFragment.TAG
|
||||
)
|
||||
R.id.action_playing_queue -> setCurrentFragment(
|
||||
PlayingQueueFragment.newInstance(),
|
||||
PlayingQueueFragment.TAG
|
||||
)
|
||||
R.id.action_song -> setCurrentFragment(
|
||||
SongsFragment.newInstance(),
|
||||
SongsFragment.TAG
|
||||
)
|
||||
R.id.action_folder -> setCurrentFragment(
|
||||
FoldersFragment.newInstance(this),
|
||||
FoldersFragment.TAG
|
||||
)
|
||||
R.id.action_home -> setCurrentFragment(
|
||||
BannerHomeFragment.newInstance(),
|
||||
BannerHomeFragment.TAG
|
||||
)
|
||||
else -> setCurrentFragment(
|
||||
BannerHomeFragment.newInstance(),
|
||||
BannerHomeFragment.TAG
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun restoreCurrentFragment() {
|
||||
val fragment = supportFragmentManager.findFragmentById(R.id.fragment_container)
|
||||
if (fragment != null) {
|
||||
currentFragment = fragment as MainActivityFragmentCallbacks
|
||||
}
|
||||
}
|
||||
|
||||
override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences?, key: String?) {
|
||||
if (key == GENERAL_THEME || key == BLACK_THEME || key == ADAPTIVE_COLOR_APP || key == USER_NAME || key == TOGGLE_FULL_SCREEN || key == TOGGLE_VOLUME || key == ROUND_CORNERS || key == CAROUSEL_EFFECT || key == NOW_PLAYING_SCREEN_ID || key == TOGGLE_GENRE || key == BANNER_IMAGE_PATH || key == PROFILE_IMAGE_PATH || key == CIRCULAR_ALBUM_ART || key == KEEP_SCREEN_ON || key == TOGGLE_SEPARATE_LINE || key == TOGGLE_HOME_BANNER || key == TOGGLE_ADD_CONTROLS || key == ALBUM_COVER_STYLE || key == HOME_ARTIST_GRID_STYLE || key == ALBUM_COVER_TRANSFORM || key == DESATURATED_COLOR || key == EXTRA_SONG_INFO || key == TAB_TEXT_MODE || key == LANGUAGE_NAME || key == LIBRARY_CATEGORIES
|
||||
) {
|
||||
postRecreate()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onServiceConnected() {
|
||||
super.onServiceConnected()
|
||||
handlePlaybackIntent(intent)
|
||||
}
|
||||
|
||||
private fun handlePlaybackIntent(intent: Intent?) {
|
||||
if (intent == null) {
|
||||
return
|
||||
}
|
||||
val uri = intent.data
|
||||
val mimeType = intent.type
|
||||
var handled = false
|
||||
if (intent.action != null && (intent.action == MediaStore.INTENT_ACTION_MEDIA_PLAY_FROM_SEARCH)
|
||||
) {
|
||||
val songs: List<Song> =
|
||||
getSongs(this, intent.extras!!)
|
||||
if (shuffleMode == MusicService.SHUFFLE_MODE_SHUFFLE) {
|
||||
openAndShuffleQueue(songs, true)
|
||||
} else {
|
||||
openQueue(songs, 0, true)
|
||||
}
|
||||
handled = true
|
||||
}
|
||||
if (uri != null && uri.toString().isNotEmpty()) {
|
||||
playFromUri(uri)
|
||||
handled = true
|
||||
} else if (MediaStore.Audio.Playlists.CONTENT_TYPE == mimeType) {
|
||||
val id = parseIdFromIntent(intent, "playlistId", "playlist").toInt()
|
||||
if (id >= 0) {
|
||||
val position = intent.getIntExtra("position", 0)
|
||||
val songs: List<Song> =
|
||||
ArrayList(getPlaylistSongList(this, id))
|
||||
openQueue(songs, position, true)
|
||||
handled = true
|
||||
}
|
||||
} else if (MediaStore.Audio.Albums.CONTENT_TYPE == mimeType) {
|
||||
val id = parseIdFromIntent(intent, "albumId", "album").toInt()
|
||||
if (id >= 0) {
|
||||
val position = intent.getIntExtra("position", 0)
|
||||
openQueue(getAlbum(this, id).songs!!, position, true)
|
||||
handled = true
|
||||
}
|
||||
} else if (MediaStore.Audio.Artists.CONTENT_TYPE == mimeType) {
|
||||
val id = parseIdFromIntent(intent, "artistId", "artist").toInt()
|
||||
if (id >= 0) {
|
||||
val position = intent.getIntExtra("position", 0)
|
||||
openQueue(getArtist(this, id).songs, position, true)
|
||||
handled = true
|
||||
}
|
||||
}
|
||||
if (handled) {
|
||||
setIntent(Intent())
|
||||
}
|
||||
}
|
||||
|
||||
private fun parseIdFromIntent(
|
||||
intent: Intent, longKey: String,
|
||||
stringKey: String
|
||||
): Long {
|
||||
var id = intent.getLongExtra(longKey, -1)
|
||||
if (id < 0) {
|
||||
val idString = intent.getStringExtra(stringKey)
|
||||
if (idString != null) {
|
||||
try {
|
||||
id = idString.toLong()
|
||||
} catch (e: NumberFormatException) {
|
||||
Log.e(TAG, e.message)
|
||||
}
|
||||
}
|
||||
}
|
||||
return id
|
||||
}
|
||||
|
||||
override fun handleBackPress(): Boolean {
|
||||
if (cab != null && cab!!.isActive) {
|
||||
cab?.finish()
|
||||
return true
|
||||
}
|
||||
return super.handleBackPress() || currentFragment.handleBackPress()
|
||||
}
|
||||
|
||||
override fun openCab(menuRes: Int, callback: MaterialCab.Callback): MaterialCab {
|
||||
cab?.let {
|
||||
if (it.isActive) it.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)
|
||||
return cab as MaterialCab
|
||||
}
|
||||
}
|
|
@ -1,187 +0,0 @@
|
|||
package code.name.monkey.retromusic.activities
|
||||
|
||||
import android.content.res.ColorStateList
|
||||
import android.os.Bundle
|
||||
import android.view.MenuItem
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import code.name.monkey.appthemehelper.util.ColorUtil
|
||||
import code.name.monkey.appthemehelper.util.MaterialValueHelper
|
||||
import code.name.monkey.retromusic.R
|
||||
import code.name.monkey.retromusic.activities.base.AbsMusicServiceActivity
|
||||
import code.name.monkey.retromusic.adapter.song.PlayingQueueAdapter
|
||||
import code.name.monkey.retromusic.extensions.accentColor
|
||||
import code.name.monkey.retromusic.extensions.surfaceColor
|
||||
import code.name.monkey.retromusic.helper.MusicPlayerRemote
|
||||
import code.name.monkey.retromusic.util.MusicUtil
|
||||
import com.h6ah4i.android.widget.advrecyclerview.animator.DraggableItemAnimator
|
||||
import com.h6ah4i.android.widget.advrecyclerview.draggable.RecyclerViewDragDropManager
|
||||
import com.h6ah4i.android.widget.advrecyclerview.swipeable.RecyclerViewSwipeManager
|
||||
import com.h6ah4i.android.widget.advrecyclerview.touchguard.RecyclerViewTouchActionGuardManager
|
||||
import com.h6ah4i.android.widget.advrecyclerview.utils.WrapperAdapterUtils
|
||||
import kotlinx.android.synthetic.main.activity_playing_queue.*
|
||||
|
||||
open class PlayingQueueActivity : AbsMusicServiceActivity() {
|
||||
|
||||
private var wrappedAdapter: RecyclerView.Adapter<*>? = null
|
||||
private var recyclerViewDragDropManager: RecyclerViewDragDropManager? = null
|
||||
private var recyclerViewSwipeManager: RecyclerViewSwipeManager? = null
|
||||
private var recyclerViewTouchActionGuardManager: RecyclerViewTouchActionGuardManager? = null
|
||||
private var playingQueueAdapter: PlayingQueueAdapter? = null
|
||||
private lateinit var linearLayoutManager: LinearLayoutManager
|
||||
|
||||
private fun getUpNextAndQueueTime(): String {
|
||||
val duration = MusicPlayerRemote.getQueueDurationMillis(MusicPlayerRemote.position)
|
||||
return MusicUtil.buildInfoString(
|
||||
resources.getString(R.string.up_next),
|
||||
MusicUtil.getReadableDurationString(duration)
|
||||
)
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
setDrawUnderStatusBar()
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(R.layout.activity_playing_queue)
|
||||
setStatusbarColorAuto()
|
||||
setNavigationbarColorAuto()
|
||||
setTaskDescriptionColorAuto()
|
||||
setLightNavigationBar(true)
|
||||
|
||||
setupToolbar()
|
||||
setUpRecyclerView()
|
||||
|
||||
clearQueue.setOnClickListener {
|
||||
MusicPlayerRemote.clearQueue()
|
||||
}
|
||||
checkForPadding()
|
||||
}
|
||||
|
||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||
return when (item.itemId) {
|
||||
android.R.id.home -> {
|
||||
onBackPressed()
|
||||
true
|
||||
}
|
||||
else -> super.onOptionsItemSelected(item)
|
||||
}
|
||||
}
|
||||
|
||||
private fun setUpRecyclerView() {
|
||||
recyclerViewTouchActionGuardManager = RecyclerViewTouchActionGuardManager()
|
||||
recyclerViewDragDropManager = RecyclerViewDragDropManager()
|
||||
recyclerViewSwipeManager = RecyclerViewSwipeManager()
|
||||
|
||||
val animator = DraggableItemAnimator()
|
||||
animator.supportsChangeAnimations = false
|
||||
|
||||
playingQueueAdapter = PlayingQueueAdapter(
|
||||
this,
|
||||
MusicPlayerRemote.playingQueue.toMutableList(),
|
||||
MusicPlayerRemote.position,
|
||||
R.layout.item_queue
|
||||
)
|
||||
wrappedAdapter = recyclerViewDragDropManager?.createWrappedAdapter(playingQueueAdapter!!)
|
||||
wrappedAdapter = wrappedAdapter?.let { recyclerViewSwipeManager?.createWrappedAdapter(it) }
|
||||
|
||||
linearLayoutManager = LinearLayoutManager(this)
|
||||
|
||||
recyclerView.layoutManager = linearLayoutManager
|
||||
recyclerView.adapter = wrappedAdapter
|
||||
recyclerView.itemAnimator = animator
|
||||
recyclerViewTouchActionGuardManager?.attachRecyclerView(recyclerView)
|
||||
recyclerViewDragDropManager?.attachRecyclerView(recyclerView)
|
||||
recyclerViewSwipeManager?.attachRecyclerView(recyclerView)
|
||||
linearLayoutManager.scrollToPositionWithOffset(MusicPlayerRemote.position + 1, 0)
|
||||
|
||||
recyclerView.addOnScrollListener(object : RecyclerView.OnScrollListener() {
|
||||
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
|
||||
super.onScrolled(recyclerView, dx, dy)
|
||||
if (dy > 0) {
|
||||
clearQueue.shrink()
|
||||
} else if (dy < 0) {
|
||||
clearQueue.extend()
|
||||
}
|
||||
}
|
||||
})
|
||||
//ViewUtil.setUpFastScrollRecyclerViewColor(this, recyclerView)
|
||||
}
|
||||
|
||||
private fun checkForPadding() {
|
||||
}
|
||||
|
||||
override fun onQueueChanged() {
|
||||
if (MusicPlayerRemote.playingQueue.isEmpty()) {
|
||||
finish()
|
||||
return
|
||||
}
|
||||
checkForPadding()
|
||||
updateQueue()
|
||||
updateCurrentSong()
|
||||
}
|
||||
|
||||
override fun onMediaStoreChanged() {
|
||||
updateQueue()
|
||||
updateCurrentSong()
|
||||
}
|
||||
|
||||
private fun updateCurrentSong() {
|
||||
toolbar.subtitle = getUpNextAndQueueTime()
|
||||
}
|
||||
|
||||
override fun onPlayingMetaChanged() {
|
||||
updateQueuePosition()
|
||||
}
|
||||
|
||||
private fun updateQueuePosition() {
|
||||
playingQueueAdapter?.setCurrent(MusicPlayerRemote.position)
|
||||
resetToCurrentPosition()
|
||||
toolbar.subtitle = getUpNextAndQueueTime()
|
||||
}
|
||||
|
||||
private fun updateQueue() {
|
||||
playingQueueAdapter?.swapDataSet(MusicPlayerRemote.playingQueue, MusicPlayerRemote.position)
|
||||
resetToCurrentPosition()
|
||||
}
|
||||
|
||||
private fun resetToCurrentPosition() {
|
||||
recyclerView.stopScroll()
|
||||
linearLayoutManager.scrollToPositionWithOffset(MusicPlayerRemote.position + 1, 0)
|
||||
}
|
||||
|
||||
override fun onPause() {
|
||||
if (recyclerViewDragDropManager != null) {
|
||||
recyclerViewDragDropManager!!.cancelDrag()
|
||||
}
|
||||
super.onPause()
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
if (recyclerViewDragDropManager != null) {
|
||||
recyclerViewDragDropManager!!.release()
|
||||
recyclerViewDragDropManager = null
|
||||
}
|
||||
if (recyclerViewSwipeManager != null) {
|
||||
recyclerViewSwipeManager?.release()
|
||||
recyclerViewSwipeManager = null
|
||||
}
|
||||
if (wrappedAdapter != null) {
|
||||
WrapperAdapterUtils.releaseAll(wrappedAdapter)
|
||||
wrappedAdapter = null
|
||||
}
|
||||
playingQueueAdapter = null
|
||||
super.onDestroy()
|
||||
}
|
||||
|
||||
private fun setupToolbar() {
|
||||
toolbar.subtitle = getUpNextAndQueueTime()
|
||||
toolbar.setBackgroundColor(surfaceColor())
|
||||
setSupportActionBar(toolbar)
|
||||
clearQueue.backgroundTintList = ColorStateList.valueOf(accentColor())
|
||||
ColorStateList.valueOf(
|
||||
MaterialValueHelper.getPrimaryTextColor(this, ColorUtil.isColorLight(accentColor()))
|
||||
).apply {
|
||||
clearQueue.setTextColor(this)
|
||||
clearQueue.iconTint = this
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,245 +0,0 @@
|
|||
package code.name.monkey.retromusic.activities
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.Menu
|
||||
import android.view.MenuItem
|
||||
import android.view.View
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import code.name.monkey.appthemehelper.util.ATHUtil
|
||||
import code.name.monkey.retromusic.App
|
||||
import code.name.monkey.retromusic.R
|
||||
import code.name.monkey.retromusic.activities.base.AbsSlidingMusicPanelActivity
|
||||
import code.name.monkey.retromusic.adapter.song.OrderablePlaylistSongAdapter
|
||||
import code.name.monkey.retromusic.adapter.song.PlaylistSongAdapter
|
||||
import code.name.monkey.retromusic.adapter.song.SongAdapter
|
||||
import code.name.monkey.retromusic.extensions.applyToolbar
|
||||
import code.name.monkey.retromusic.helper.menu.PlaylistMenuHelper
|
||||
import code.name.monkey.retromusic.interfaces.CabHolder
|
||||
import code.name.monkey.retromusic.loaders.PlaylistLoader
|
||||
import code.name.monkey.retromusic.model.AbsCustomPlaylist
|
||||
import code.name.monkey.retromusic.model.Playlist
|
||||
import code.name.monkey.retromusic.model.Song
|
||||
import code.name.monkey.retromusic.mvp.presenter.PlaylistSongsPresenter
|
||||
import code.name.monkey.retromusic.mvp.presenter.PlaylistSongsView
|
||||
import code.name.monkey.retromusic.util.DensityUtil
|
||||
import code.name.monkey.retromusic.util.PlaylistsUtil
|
||||
import code.name.monkey.retromusic.util.RetroColorUtil
|
||||
import com.afollestad.materialcab.MaterialCab
|
||||
import com.h6ah4i.android.widget.advrecyclerview.animator.RefactoredDefaultItemAnimator
|
||||
import com.h6ah4i.android.widget.advrecyclerview.draggable.RecyclerViewDragDropManager
|
||||
import com.h6ah4i.android.widget.advrecyclerview.utils.WrapperAdapterUtils
|
||||
import kotlinx.android.synthetic.main.activity_playlist_detail.*
|
||||
import javax.inject.Inject
|
||||
|
||||
class PlaylistDetailActivity : AbsSlidingMusicPanelActivity(), CabHolder, PlaylistSongsView {
|
||||
|
||||
@Inject
|
||||
lateinit var playlistSongsPresenter: PlaylistSongsPresenter
|
||||
|
||||
private lateinit var playlist: Playlist
|
||||
private var cab: MaterialCab? = null
|
||||
private lateinit var adapter: SongAdapter
|
||||
private var wrappedAdapter: RecyclerView.Adapter<*>? = null
|
||||
private var recyclerViewDragDropManager: RecyclerViewDragDropManager? = null
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
setDrawUnderStatusBar()
|
||||
super.onCreate(savedInstanceState)
|
||||
setStatusbarColorAuto()
|
||||
setNavigationbarColorAuto()
|
||||
setTaskDescriptionColorAuto()
|
||||
setLightNavigationBar(true)
|
||||
setBottomBarVisibility(View.GONE)
|
||||
|
||||
App.musicComponent.inject(this)
|
||||
playlistSongsPresenter.attachView(this)
|
||||
|
||||
if (intent.extras != null) {
|
||||
playlist = intent.extras!!.getParcelable(EXTRA_PLAYLIST)!!
|
||||
} else {
|
||||
finish()
|
||||
}
|
||||
|
||||
setUpToolBar()
|
||||
setUpRecyclerView()
|
||||
}
|
||||
|
||||
override fun createContentView(): View {
|
||||
return wrapSlidingMusicPanel(R.layout.activity_playlist_detail)
|
||||
}
|
||||
|
||||
private fun setUpRecyclerView() {
|
||||
|
||||
recyclerView.layoutManager = LinearLayoutManager(this)
|
||||
if (playlist is AbsCustomPlaylist) {
|
||||
adapter = PlaylistSongAdapter(this, ArrayList(), R.layout.item_list, this)
|
||||
recyclerView.adapter = adapter
|
||||
} else {
|
||||
recyclerViewDragDropManager = RecyclerViewDragDropManager()
|
||||
val animator = RefactoredDefaultItemAnimator()
|
||||
adapter = OrderablePlaylistSongAdapter(this,
|
||||
ArrayList(),
|
||||
R.layout.item_list,
|
||||
this,
|
||||
object : OrderablePlaylistSongAdapter.OnMoveItemListener {
|
||||
override fun onMoveItem(fromPosition: Int, toPosition: Int) {
|
||||
if (PlaylistsUtil.moveItem(
|
||||
this@PlaylistDetailActivity,
|
||||
playlist.id,
|
||||
fromPosition,
|
||||
toPosition
|
||||
)
|
||||
) {
|
||||
val song = adapter.dataSet.removeAt(fromPosition)
|
||||
adapter.dataSet.add(toPosition, song)
|
||||
adapter.notifyItemMoved(fromPosition, toPosition)
|
||||
}
|
||||
}
|
||||
})
|
||||
wrappedAdapter = recyclerViewDragDropManager!!.createWrappedAdapter(adapter)
|
||||
|
||||
recyclerView.adapter = wrappedAdapter
|
||||
recyclerView.itemAnimator = animator
|
||||
|
||||
recyclerViewDragDropManager?.attachRecyclerView(recyclerView)
|
||||
}
|
||||
adapter.registerAdapterDataObserver(object : RecyclerView.AdapterDataObserver() {
|
||||
override fun onChanged() {
|
||||
super.onChanged()
|
||||
checkIsEmpty()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
playlistSongsPresenter.loadPlaylistSongs(playlist)
|
||||
}
|
||||
|
||||
private fun setUpToolBar() {
|
||||
applyToolbar(toolbar)
|
||||
title = playlist.name
|
||||
}
|
||||
|
||||
override fun onCreateOptionsMenu(menu: Menu): Boolean {
|
||||
menuInflater.inflate(
|
||||
if (playlist is AbsCustomPlaylist) R.menu.menu_smart_playlist_detail
|
||||
else R.menu.menu_playlist_detail, menu
|
||||
)
|
||||
return super.onCreateOptionsMenu(menu)
|
||||
}
|
||||
|
||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||
when (item.itemId) {
|
||||
android.R.id.home -> {
|
||||
onBackPressed()
|
||||
return true
|
||||
}
|
||||
}
|
||||
return PlaylistMenuHelper.handleMenuClick(this, playlist, item)
|
||||
}
|
||||
|
||||
override fun openCab(menuRes: Int, callback: MaterialCab.Callback): MaterialCab {
|
||||
if (cab != null && cab!!.isActive) {
|
||||
cab!!.finish()
|
||||
}
|
||||
cab = MaterialCab(this, R.id.cab_stub).setMenu(menuRes)
|
||||
.setCloseDrawableRes(R.drawable.ic_close_white_24dp)
|
||||
.setBackgroundColor(
|
||||
RetroColorUtil.shiftBackgroundColorForLightText(
|
||||
ATHUtil.resolveColor(
|
||||
this,
|
||||
R.attr.colorSurface
|
||||
)
|
||||
)
|
||||
).start(callback)
|
||||
return cab!!
|
||||
}
|
||||
|
||||
override fun onBackPressed() {
|
||||
if (cab != null && cab!!.isActive) {
|
||||
cab!!.finish()
|
||||
} else {
|
||||
recyclerView!!.stopScroll()
|
||||
super.onBackPressed()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onMediaStoreChanged() {
|
||||
super.onMediaStoreChanged()
|
||||
if (playlist !is AbsCustomPlaylist) {
|
||||
// Playlist deleted
|
||||
if (!PlaylistsUtil.doesPlaylistExist(this, playlist.id)) {
|
||||
finish()
|
||||
return
|
||||
}
|
||||
// Playlist renamed
|
||||
val playlistName = PlaylistsUtil.getNameForPlaylist(this, playlist.id.toLong())
|
||||
if (playlistName != playlist.name) {
|
||||
playlist = PlaylistLoader.getPlaylist(this, playlist.id)
|
||||
setToolbarTitle(playlist.name)
|
||||
}
|
||||
}
|
||||
playlistSongsPresenter.loadPlaylistSongs(playlist)
|
||||
}
|
||||
|
||||
private fun setToolbarTitle(title: String) {
|
||||
supportActionBar?.title = title
|
||||
}
|
||||
|
||||
private fun checkForPadding() {
|
||||
val height = DensityUtil.dip2px(this, 52f)
|
||||
recyclerView.setPadding(0, 0, 0, (height))
|
||||
}
|
||||
|
||||
private fun checkIsEmpty() {
|
||||
checkForPadding()
|
||||
emptyEmoji.text = getEmojiByUnicode(0x1F631)
|
||||
empty.visibility = if (adapter.itemCount == 0) View.VISIBLE else View.GONE
|
||||
emptyText.visibility = if (adapter.itemCount == 0) View.VISIBLE else View.GONE
|
||||
}
|
||||
|
||||
private fun getEmojiByUnicode(unicode: Int): String {
|
||||
return String(Character.toChars(unicode))
|
||||
}
|
||||
|
||||
public override fun onPause() {
|
||||
if (recyclerViewDragDropManager != null) {
|
||||
recyclerViewDragDropManager!!.cancelDrag()
|
||||
}
|
||||
super.onPause()
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
if (recyclerViewDragDropManager != null) {
|
||||
recyclerViewDragDropManager!!.release()
|
||||
recyclerViewDragDropManager = null
|
||||
}
|
||||
|
||||
if (recyclerView != null) {
|
||||
recyclerView!!.itemAnimator = null
|
||||
recyclerView!!.adapter = null
|
||||
}
|
||||
|
||||
if (wrappedAdapter != null) {
|
||||
WrapperAdapterUtils.releaseAll(wrappedAdapter)
|
||||
wrappedAdapter = null
|
||||
}
|
||||
super.onDestroy()
|
||||
playlistSongsPresenter.detachView()
|
||||
}
|
||||
|
||||
override fun showEmptyView() {
|
||||
empty.visibility = View.VISIBLE
|
||||
emptyText.visibility = View.VISIBLE
|
||||
}
|
||||
|
||||
override fun songs(songs: List<Song>) {
|
||||
adapter.swapDataSet(songs)
|
||||
}
|
||||
|
||||
companion object {
|
||||
var EXTRA_PLAYLIST = "extra_playlist"
|
||||
}
|
||||
}
|
|
@ -1,159 +0,0 @@
|
|||
package code.name.monkey.retromusic.activities
|
||||
|
||||
import android.content.Intent
|
||||
import android.content.res.ColorStateList
|
||||
import android.graphics.Color
|
||||
import android.os.AsyncTask
|
||||
import android.os.Bundle
|
||||
import android.util.Log
|
||||
import android.view.MenuItem
|
||||
import android.widget.Toast
|
||||
import code.name.monkey.appthemehelper.ThemeStore
|
||||
import code.name.monkey.appthemehelper.util.MaterialUtil
|
||||
import code.name.monkey.retromusic.App
|
||||
import code.name.monkey.retromusic.BuildConfig
|
||||
import code.name.monkey.retromusic.R
|
||||
import code.name.monkey.retromusic.activities.base.AbsBaseActivity
|
||||
import com.anjlab.android.iab.v3.BillingProcessor
|
||||
import com.anjlab.android.iab.v3.TransactionDetails
|
||||
import kotlinx.android.synthetic.main.activity_pro_version.*
|
||||
import java.lang.ref.WeakReference
|
||||
|
||||
class PurchaseActivity : AbsBaseActivity(), BillingProcessor.IBillingHandler {
|
||||
|
||||
private lateinit var billingProcessor: BillingProcessor
|
||||
private var restorePurchaseAsyncTask: AsyncTask<*, *, *>? = null
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
setDrawUnderStatusBar()
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(R.layout.activity_pro_version)
|
||||
setStatusbarColor(Color.TRANSPARENT)
|
||||
setLightStatusbar(false)
|
||||
setNavigationbarColor(Color.BLACK)
|
||||
setLightNavigationBar(false)
|
||||
toolbar.navigationIcon?.setTint(Color.WHITE)
|
||||
toolbar.setNavigationOnClickListener { onBackPressed() }
|
||||
|
||||
restoreButton.isEnabled = false
|
||||
purchaseButton.isEnabled = false
|
||||
|
||||
billingProcessor = BillingProcessor(this, BuildConfig.GOOGLE_PLAY_LICENSING_KEY, this)
|
||||
|
||||
MaterialUtil.setTint(purchaseButton, true)
|
||||
|
||||
restoreButton.setOnClickListener {
|
||||
if (restorePurchaseAsyncTask == null || restorePurchaseAsyncTask!!.status != AsyncTask.Status.RUNNING) {
|
||||
restorePurchase()
|
||||
}
|
||||
|
||||
}
|
||||
purchaseButton.setOnClickListener {
|
||||
billingProcessor.purchase(this@PurchaseActivity, App.PRO_VERSION_PRODUCT_ID)
|
||||
}
|
||||
bannerContainer.backgroundTintList =
|
||||
ColorStateList.valueOf(ThemeStore.accentColor(this))
|
||||
}
|
||||
|
||||
private fun restorePurchase() {
|
||||
if (restorePurchaseAsyncTask != null) {
|
||||
restorePurchaseAsyncTask!!.cancel(false)
|
||||
}
|
||||
restorePurchaseAsyncTask = RestorePurchaseAsyncTask(this).execute()
|
||||
}
|
||||
|
||||
override fun onProductPurchased(productId: String, details: TransactionDetails?) {
|
||||
Toast.makeText(this, R.string.thank_you, Toast.LENGTH_SHORT).show()
|
||||
setResult(RESULT_OK)
|
||||
}
|
||||
|
||||
override fun onPurchaseHistoryRestored() {
|
||||
if (App.isProVersion()) {
|
||||
Toast.makeText(
|
||||
this,
|
||||
R.string.restored_previous_purchase_please_restart,
|
||||
Toast.LENGTH_LONG
|
||||
).show()
|
||||
setResult(RESULT_OK)
|
||||
} else {
|
||||
Toast.makeText(this, R.string.no_purchase_found, Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onBillingError(errorCode: Int, error: Throwable?) {
|
||||
Log.e(TAG, "Billing error: code = $errorCode", error)
|
||||
}
|
||||
|
||||
override fun onBillingInitialized() {
|
||||
restoreButton.isEnabled = true
|
||||
purchaseButton.isEnabled = true
|
||||
}
|
||||
|
||||
public override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
||||
if (!billingProcessor.handleActivityResult(requestCode, resultCode, data)) {
|
||||
super.onActivityResult(requestCode, resultCode, data)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||
when (item.itemId) {
|
||||
android.R.id.home -> finish()
|
||||
}
|
||||
return super.onOptionsItemSelected(item)
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
billingProcessor.release()
|
||||
super.onDestroy()
|
||||
}
|
||||
|
||||
private class RestorePurchaseAsyncTask internal constructor(purchaseActivity: PurchaseActivity) :
|
||||
AsyncTask<Void, Void, Boolean>() {
|
||||
|
||||
private val buyActivityWeakReference: WeakReference<PurchaseActivity> = WeakReference(
|
||||
purchaseActivity
|
||||
)
|
||||
|
||||
override fun onPreExecute() {
|
||||
super.onPreExecute()
|
||||
val purchaseActivity = buyActivityWeakReference.get()
|
||||
if (purchaseActivity != null) {
|
||||
Toast.makeText(purchaseActivity, R.string.restoring_purchase, Toast.LENGTH_SHORT)
|
||||
.show()
|
||||
} else {
|
||||
cancel(false)
|
||||
}
|
||||
}
|
||||
|
||||
override fun doInBackground(vararg params: Void): Boolean? {
|
||||
val purchaseActivity = buyActivityWeakReference.get()
|
||||
if (purchaseActivity != null) {
|
||||
return purchaseActivity.billingProcessor.loadOwnedPurchasesFromGoogle()
|
||||
}
|
||||
cancel(false)
|
||||
return null
|
||||
}
|
||||
|
||||
override fun onPostExecute(b: Boolean?) {
|
||||
super.onPostExecute(b)
|
||||
val purchaseActivity = buyActivityWeakReference.get()
|
||||
if (purchaseActivity == null || b == null) {
|
||||
return
|
||||
}
|
||||
|
||||
if (b) {
|
||||
purchaseActivity.onPurchaseHistoryRestored()
|
||||
} else {
|
||||
Toast.makeText(
|
||||
purchaseActivity,
|
||||
R.string.could_not_restore_purchase,
|
||||
Toast.LENGTH_SHORT
|
||||
).show()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val TAG: String = "PurchaseActivity"
|
||||
}
|
||||
}
|
|
@ -1,224 +0,0 @@
|
|||
package code.name.monkey.retromusic.activities
|
||||
|
||||
import android.app.Activity
|
||||
import android.app.Service
|
||||
import android.content.ActivityNotFoundException
|
||||
import android.content.Intent
|
||||
import android.content.res.ColorStateList
|
||||
import android.os.Bundle
|
||||
import android.speech.RecognizerIntent
|
||||
import android.text.Editable
|
||||
import android.text.TextWatcher
|
||||
import android.view.View
|
||||
import android.view.inputmethod.InputMethodManager
|
||||
import android.widget.TextView.BufferType
|
||||
import android.widget.Toast
|
||||
import androidx.appcompat.widget.SearchView.OnQueryTextListener
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import androidx.transition.TransitionManager
|
||||
import code.name.monkey.appthemehelper.ThemeStore
|
||||
import code.name.monkey.appthemehelper.util.ATHUtil
|
||||
import code.name.monkey.appthemehelper.util.ColorUtil
|
||||
import code.name.monkey.appthemehelper.util.MaterialValueHelper
|
||||
import code.name.monkey.retromusic.App
|
||||
import code.name.monkey.retromusic.R
|
||||
import code.name.monkey.retromusic.activities.base.AbsMusicServiceActivity
|
||||
import code.name.monkey.retromusic.adapter.SearchAdapter
|
||||
import code.name.monkey.retromusic.mvp.presenter.SearchPresenter
|
||||
import code.name.monkey.retromusic.mvp.presenter.SearchView
|
||||
import code.name.monkey.retromusic.util.RetroUtil
|
||||
import com.google.android.material.textfield.TextInputEditText
|
||||
import kotlinx.android.synthetic.main.activity_search.*
|
||||
import java.util.*
|
||||
import javax.inject.Inject
|
||||
import kotlin.collections.ArrayList
|
||||
|
||||
class SearchActivity : AbsMusicServiceActivity(), OnQueryTextListener, TextWatcher, SearchView {
|
||||
@Inject
|
||||
lateinit var searchPresenter: SearchPresenter
|
||||
|
||||
private var searchAdapter: SearchAdapter? = null
|
||||
private var query: String? = null
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
setDrawUnderStatusBar()
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(R.layout.activity_search)
|
||||
setStatusbarColorAuto()
|
||||
setNavigationbarColorAuto()
|
||||
setTaskDescriptionColorAuto()
|
||||
setLightNavigationBar(true)
|
||||
|
||||
App.musicComponent.inject(this)
|
||||
searchPresenter.attachView(this)
|
||||
|
||||
setupRecyclerView()
|
||||
setUpToolBar()
|
||||
setupSearchView()
|
||||
|
||||
if (intent.getBooleanExtra(EXTRA_SHOW_MIC, false)) {
|
||||
startMicSearch()
|
||||
}
|
||||
|
||||
back.setOnClickListener { onBackPressed() }
|
||||
voiceSearch.setOnClickListener { startMicSearch() }
|
||||
clearText.setOnClickListener { searchView.clearText() }
|
||||
searchContainer.backgroundTintList =
|
||||
ColorStateList.valueOf(ATHUtil.resolveColor(this, R.attr.colorSurface))
|
||||
|
||||
keyboardPopup.setOnClickListener {
|
||||
val inputManager = getSystemService(Service.INPUT_METHOD_SERVICE) as InputMethodManager
|
||||
inputManager.showSoftInput(searchView, InputMethodManager.SHOW_IMPLICIT)
|
||||
}
|
||||
|
||||
keyboardPopup.backgroundTintList = ColorStateList.valueOf(ThemeStore.accentColor(this))
|
||||
ColorStateList.valueOf(
|
||||
MaterialValueHelper.getPrimaryTextColor(
|
||||
this,
|
||||
ColorUtil.isColorLight(ThemeStore.accentColor(this))
|
||||
)
|
||||
).apply {
|
||||
keyboardPopup.setTextColor(this)
|
||||
keyboardPopup.iconTint = this
|
||||
}
|
||||
if (savedInstanceState != null) {
|
||||
query = savedInstanceState.getString(QUERY)
|
||||
}
|
||||
}
|
||||
|
||||
private fun setupRecyclerView() {
|
||||
searchAdapter = SearchAdapter(this, emptyList())
|
||||
searchAdapter?.registerAdapterDataObserver(object : RecyclerView.AdapterDataObserver() {
|
||||
override fun onChanged() {
|
||||
super.onChanged()
|
||||
empty.visibility = if (searchAdapter!!.itemCount < 1) View.VISIBLE else View.GONE
|
||||
}
|
||||
})
|
||||
recyclerView.apply {
|
||||
layoutManager = LinearLayoutManager(this@SearchActivity)
|
||||
adapter = searchAdapter
|
||||
}
|
||||
recyclerView.addOnScrollListener(object : RecyclerView.OnScrollListener() {
|
||||
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
|
||||
super.onScrolled(recyclerView, dx, dy)
|
||||
if (dy > 0) {
|
||||
keyboardPopup.shrink()
|
||||
} else if (dy < 0) {
|
||||
keyboardPopup.extend()
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
private fun setupSearchView() {
|
||||
searchView.addTextChangedListener(this)
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
super.onDestroy()
|
||||
searchPresenter.detachView()
|
||||
}
|
||||
|
||||
override fun onSaveInstanceState(outState: Bundle) {
|
||||
super.onSaveInstanceState(outState)
|
||||
outState.putString(QUERY, query)
|
||||
}
|
||||
|
||||
private fun setUpToolBar() {
|
||||
title = null
|
||||
}
|
||||
|
||||
private fun search(query: String) {
|
||||
this.query = query
|
||||
TransitionManager.beginDelayedTransition(appBarLayout)
|
||||
voiceSearch.visibility = if (query.isNotEmpty()) View.GONE else View.VISIBLE
|
||||
clearText.visibility = if (query.isNotEmpty()) View.VISIBLE else View.GONE
|
||||
searchPresenter.search(query)
|
||||
}
|
||||
|
||||
override fun onMediaStoreChanged() {
|
||||
super.onMediaStoreChanged()
|
||||
query?.let { search(it) }
|
||||
}
|
||||
|
||||
override fun onQueryTextSubmit(query: String): Boolean {
|
||||
hideSoftKeyboard()
|
||||
return false
|
||||
}
|
||||
|
||||
override fun onQueryTextChange(newText: String): Boolean {
|
||||
search(newText)
|
||||
return false
|
||||
}
|
||||
|
||||
private fun hideSoftKeyboard() {
|
||||
RetroUtil.hideSoftKeyboard(this@SearchActivity)
|
||||
if (searchView != null) {
|
||||
searchView.clearFocus()
|
||||
}
|
||||
}
|
||||
|
||||
override fun showEmptyView() {
|
||||
searchAdapter?.swapDataSet(ArrayList())
|
||||
}
|
||||
|
||||
override fun showData(data: MutableList<Any>) {
|
||||
searchAdapter?.swapDataSet(data)
|
||||
}
|
||||
|
||||
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
||||
super.onActivityResult(requestCode, resultCode, data)
|
||||
when (requestCode) {
|
||||
REQ_CODE_SPEECH_INPUT -> {
|
||||
if (resultCode == Activity.RESULT_OK && null != data) {
|
||||
val result: ArrayList<String>? =
|
||||
data.getStringArrayListExtra(RecognizerIntent.EXTRA_RESULTS)
|
||||
query = result?.get(0)
|
||||
searchView.setText(query, BufferType.EDITABLE)
|
||||
searchPresenter.search(query!!)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun startMicSearch() {
|
||||
val intent = Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH)
|
||||
intent.putExtra(
|
||||
RecognizerIntent.EXTRA_LANGUAGE_MODEL,
|
||||
RecognizerIntent.LANGUAGE_MODEL_FREE_FORM
|
||||
)
|
||||
intent.putExtra(RecognizerIntent.EXTRA_LANGUAGE, Locale.getDefault())
|
||||
intent.putExtra(RecognizerIntent.EXTRA_PROMPT, getString(R.string.speech_prompt))
|
||||
try {
|
||||
startActivityForResult(intent, REQ_CODE_SPEECH_INPUT)
|
||||
} catch (e: ActivityNotFoundException) {
|
||||
e.printStackTrace()
|
||||
Toast.makeText(this, getString(R.string.speech_not_supported), Toast.LENGTH_SHORT)
|
||||
.show()
|
||||
}
|
||||
}
|
||||
|
||||
override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {
|
||||
}
|
||||
|
||||
override fun onTextChanged(newText: CharSequence, start: Int, before: Int, count: Int) {
|
||||
search(newText.toString())
|
||||
}
|
||||
|
||||
override fun afterTextChanged(s: Editable) {
|
||||
}
|
||||
|
||||
companion object {
|
||||
val TAG: String = SearchActivity::class.java.simpleName
|
||||
|
||||
const val EXTRA_SHOW_MIC = "extra_show_mic"
|
||||
const val QUERY: String = "query"
|
||||
|
||||
private const val REQ_CODE_SPEECH_INPUT = 9002
|
||||
}
|
||||
}
|
||||
|
||||
fun TextInputEditText.clearText() {
|
||||
text = null
|
||||
}
|
|
@ -1,91 +0,0 @@
|
|||
package code.name.monkey.retromusic.activities
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.MenuItem
|
||||
import androidx.annotation.StringRes
|
||||
import androidx.fragment.app.Fragment
|
||||
import code.name.monkey.appthemehelper.ThemeStore
|
||||
import code.name.monkey.appthemehelper.util.VersionUtils
|
||||
import code.name.monkey.retromusic.R
|
||||
import code.name.monkey.retromusic.activities.base.AbsBaseActivity
|
||||
import code.name.monkey.retromusic.appshortcuts.DynamicShortcutManager
|
||||
import code.name.monkey.retromusic.extensions.applyToolbar
|
||||
import code.name.monkey.retromusic.fragments.settings.MainSettingsFragment
|
||||
import com.afollestad.materialdialogs.color.ColorChooserDialog
|
||||
import kotlinx.android.synthetic.main.activity_settings.*
|
||||
|
||||
class SettingsActivity : AbsBaseActivity(), ColorChooserDialog.ColorCallback {
|
||||
|
||||
private val fragmentManager = supportFragmentManager
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
setDrawUnderStatusBar()
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(R.layout.activity_settings)
|
||||
setStatusbarColorAuto()
|
||||
setNavigationbarColorAuto()
|
||||
setLightNavigationBar(true)
|
||||
setupToolbar()
|
||||
|
||||
if (savedInstanceState == null) {
|
||||
fragmentManager.beginTransaction().replace(R.id.contentFrame, MainSettingsFragment())
|
||||
.commit()
|
||||
}
|
||||
}
|
||||
|
||||
private fun setupToolbar() {
|
||||
setTitle(R.string.action_settings)
|
||||
applyToolbar(toolbar)
|
||||
}
|
||||
|
||||
fun setupFragment(fragment: Fragment, @StringRes titleName: Int) {
|
||||
val fragmentTransaction = fragmentManager
|
||||
.beginTransaction()
|
||||
.setCustomAnimations(
|
||||
R.anim.sliding_in_left,
|
||||
R.anim.sliding_out_right,
|
||||
android.R.anim.slide_in_left,
|
||||
android.R.anim.slide_out_right
|
||||
)
|
||||
fragmentTransaction.replace(R.id.contentFrame, fragment, fragment.tag)
|
||||
fragmentTransaction.addToBackStack(null)
|
||||
fragmentTransaction.commit()
|
||||
setTitle(titleName)
|
||||
}
|
||||
|
||||
override fun onBackPressed() {
|
||||
if (fragmentManager.backStackEntryCount == 0) {
|
||||
super.onBackPressed()
|
||||
} else {
|
||||
setTitle(R.string.action_settings)
|
||||
fragmentManager.popBackStack()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||
if (item.itemId == android.R.id.home) {
|
||||
onBackPressed()
|
||||
return true
|
||||
}
|
||||
return super.onOptionsItemSelected(item)
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val TAG: String = "SettingsActivity"
|
||||
}
|
||||
|
||||
override fun onColorSelection(dialog: ColorChooserDialog, selectedColor: Int) {
|
||||
when (dialog.title) {
|
||||
R.string.accent_color -> {
|
||||
ThemeStore.editTheme(this).accentColor(selectedColor).commit()
|
||||
if (VersionUtils.hasNougatMR())
|
||||
DynamicShortcutManager(this).updateDynamicShortcuts()
|
||||
}
|
||||
}
|
||||
recreate()
|
||||
}
|
||||
|
||||
override fun onColorChooserDismissed(dialog: ColorChooserDialog) {
|
||||
|
||||
}
|
||||
}
|
|
@ -1,126 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2020 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.activities
|
||||
|
||||
import android.content.res.ColorStateList
|
||||
import android.graphics.Bitmap
|
||||
import android.graphics.Color
|
||||
import android.graphics.drawable.GradientDrawable
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
import android.provider.MediaStore.Images.Media
|
||||
import android.view.MenuItem
|
||||
import androidx.core.view.drawToBitmap
|
||||
import code.name.monkey.appthemehelper.ThemeStore
|
||||
import code.name.monkey.appthemehelper.util.ColorUtil
|
||||
import code.name.monkey.appthemehelper.util.MaterialValueHelper
|
||||
import code.name.monkey.retromusic.R
|
||||
import code.name.monkey.retromusic.activities.base.AbsBaseActivity
|
||||
import code.name.monkey.retromusic.glide.RetroMusicColoredTarget
|
||||
import code.name.monkey.retromusic.glide.SongGlideRequest
|
||||
import code.name.monkey.retromusic.model.Song
|
||||
import code.name.monkey.retromusic.util.Share
|
||||
import code.name.monkey.retromusic.util.color.MediaNotificationProcessor
|
||||
import com.bumptech.glide.Glide
|
||||
import kotlinx.android.synthetic.main.activity_share_instagram.*
|
||||
|
||||
/**
|
||||
* Created by hemanths on 2020-02-02.
|
||||
*/
|
||||
|
||||
class ShareInstagramStory : AbsBaseActivity() {
|
||||
|
||||
companion object {
|
||||
const val EXTRA_SONG = "extra_song"
|
||||
}
|
||||
|
||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||
if (item.itemId == android.R.id.home) {
|
||||
onBackPressed()
|
||||
return true
|
||||
}
|
||||
return super.onOptionsItemSelected(item)
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
setDrawUnderStatusBar()
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(R.layout.activity_share_instagram)
|
||||
setStatusbarColor(Color.TRANSPARENT)
|
||||
setNavigationbarColor(Color.BLACK)
|
||||
|
||||
toolbar.setBackgroundColor(Color.TRANSPARENT)
|
||||
setSupportActionBar(toolbar)
|
||||
|
||||
val song = intent.extras?.getParcelable<Song>(EXTRA_SONG)
|
||||
song?.let { songFinal ->
|
||||
SongGlideRequest.Builder.from(Glide.with(this), songFinal)
|
||||
.checkIgnoreMediaStore(this@ShareInstagramStory)
|
||||
.generatePalette(this@ShareInstagramStory)
|
||||
.build()
|
||||
.into(object : RetroMusicColoredTarget(image) {
|
||||
override fun onColorReady(colors: MediaNotificationProcessor) {
|
||||
val isColorLight = ColorUtil.isColorLight(colors.backgroundColor)
|
||||
setColors(isColorLight, colors.backgroundColor)
|
||||
}
|
||||
})
|
||||
|
||||
shareTitle.text = songFinal.title
|
||||
shareText.text = songFinal.artistName
|
||||
shareButton.setOnClickListener {
|
||||
val path: String = Media.insertImage(
|
||||
contentResolver,
|
||||
mainContent.drawToBitmap(Bitmap.Config.ARGB_8888),
|
||||
"Design", null
|
||||
)
|
||||
val uri = Uri.parse(path)
|
||||
Share.shareStoryToSocial(
|
||||
this@ShareInstagramStory,
|
||||
uri
|
||||
)
|
||||
}
|
||||
}
|
||||
shareButton.setTextColor(
|
||||
MaterialValueHelper.getPrimaryTextColor(
|
||||
this,
|
||||
ColorUtil.isColorLight(ThemeStore.accentColor(this))
|
||||
)
|
||||
)
|
||||
shareButton.backgroundTintList = ColorStateList.valueOf(ThemeStore.accentColor(this))
|
||||
}
|
||||
|
||||
private fun setColors(colorLight: Boolean, color: Int) {
|
||||
setLightStatusbar(colorLight)
|
||||
toolbar.setTitleTextColor(
|
||||
MaterialValueHelper.getPrimaryTextColor(
|
||||
this@ShareInstagramStory,
|
||||
colorLight
|
||||
)
|
||||
)
|
||||
toolbar.navigationIcon?.setTintList(
|
||||
ColorStateList.valueOf(
|
||||
MaterialValueHelper.getPrimaryTextColor(
|
||||
this@ShareInstagramStory,
|
||||
colorLight
|
||||
)
|
||||
)
|
||||
)
|
||||
mainContent.background =
|
||||
GradientDrawable(
|
||||
GradientDrawable.Orientation.TOP_BOTTOM,
|
||||
intArrayOf(color, Color.BLACK)
|
||||
)
|
||||
}
|
||||
}
|
|
@ -1,252 +0,0 @@
|
|||
package code.name.monkey.retromusic.activities
|
||||
|
||||
import android.content.Intent
|
||||
import android.graphics.Paint
|
||||
import android.os.AsyncTask
|
||||
import android.os.Bundle
|
||||
import android.util.Log
|
||||
import android.view.LayoutInflater
|
||||
import android.view.MenuItem
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.TextView
|
||||
import android.widget.Toast
|
||||
import androidx.annotation.LayoutRes
|
||||
import androidx.appcompat.widget.AppCompatImageView
|
||||
import androidx.recyclerview.widget.DefaultItemAnimator
|
||||
import androidx.recyclerview.widget.GridLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import code.name.monkey.appthemehelper.ThemeStore
|
||||
import code.name.monkey.appthemehelper.util.ATHUtil
|
||||
import code.name.monkey.appthemehelper.util.TintHelper
|
||||
import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper
|
||||
import code.name.monkey.retromusic.BuildConfig
|
||||
import code.name.monkey.retromusic.R
|
||||
import code.name.monkey.retromusic.activities.base.AbsBaseActivity
|
||||
import code.name.monkey.retromusic.extensions.textColorPrimary
|
||||
import code.name.monkey.retromusic.extensions.textColorSecondary
|
||||
import com.anjlab.android.iab.v3.BillingProcessor
|
||||
import com.anjlab.android.iab.v3.SkuDetails
|
||||
import com.anjlab.android.iab.v3.TransactionDetails
|
||||
import kotlinx.android.synthetic.main.activity_about.toolbar
|
||||
import kotlinx.android.synthetic.main.activity_donation.*
|
||||
import java.lang.ref.WeakReference
|
||||
import java.util.*
|
||||
|
||||
class SupportDevelopmentActivity : AbsBaseActivity(), BillingProcessor.IBillingHandler {
|
||||
|
||||
companion object {
|
||||
val TAG: String = SupportDevelopmentActivity::class.java.simpleName
|
||||
const val DONATION_PRODUCT_IDS = R.array.donation_ids
|
||||
private const val TEZ_REQUEST_CODE = 123
|
||||
}
|
||||
|
||||
var billingProcessor: BillingProcessor? = null
|
||||
private var skuDetailsLoadAsyncTask: AsyncTask<*, *, *>? = null
|
||||
|
||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||
if (item.itemId == android.R.id.home) {
|
||||
onBackPressed()
|
||||
return true
|
||||
}
|
||||
return super.onOptionsItemSelected(item)
|
||||
}
|
||||
|
||||
fun donate(i: Int) {
|
||||
val ids = resources.getStringArray(DONATION_PRODUCT_IDS)
|
||||
billingProcessor?.purchase(this, ids[i])
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(R.layout.activity_donation)
|
||||
|
||||
setStatusbarColorAuto()
|
||||
setNavigationbarColorAuto()
|
||||
setTaskDescriptionColorAuto()
|
||||
setLightNavigationBar(true)
|
||||
|
||||
setupToolbar()
|
||||
|
||||
billingProcessor = BillingProcessor(this, BuildConfig.GOOGLE_PLAY_LICENSING_KEY, this)
|
||||
TintHelper.setTint(progress, ThemeStore.accentColor(this))
|
||||
donation.setTextColor(ThemeStore.accentColor(this))
|
||||
}
|
||||
|
||||
private fun setupToolbar() {
|
||||
val toolbarColor = ATHUtil.resolveColor(this, R.attr.colorSurface)
|
||||
toolbar.setBackgroundColor(toolbarColor)
|
||||
ToolbarContentTintHelper.colorBackButton(toolbar)
|
||||
setSupportActionBar(toolbar)
|
||||
}
|
||||
|
||||
override fun onBillingInitialized() {
|
||||
loadSkuDetails()
|
||||
}
|
||||
|
||||
private fun loadSkuDetails() {
|
||||
if (skuDetailsLoadAsyncTask != null) {
|
||||
skuDetailsLoadAsyncTask!!.cancel(false)
|
||||
}
|
||||
skuDetailsLoadAsyncTask = SkuDetailsLoadAsyncTask(this).execute()
|
||||
}
|
||||
|
||||
override fun onProductPurchased(productId: String, details: TransactionDetails?) {
|
||||
//loadSkuDetails();
|
||||
Toast.makeText(this, R.string.thank_you, Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
|
||||
override fun onBillingError(errorCode: Int, error: Throwable?) {
|
||||
Log.e(TAG, "Billing error: code = $errorCode", error)
|
||||
}
|
||||
|
||||
override fun onPurchaseHistoryRestored() {
|
||||
//loadSkuDetails();
|
||||
Toast.makeText(this, R.string.restored_previous_purchases, Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
|
||||
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
||||
if (!billingProcessor!!.handleActivityResult(requestCode, resultCode, data)) {
|
||||
super.onActivityResult(requestCode, resultCode, data)
|
||||
}
|
||||
if (requestCode == TEZ_REQUEST_CODE) {
|
||||
// Process based on the data in response.
|
||||
Log.d("result", data!!.getStringExtra("Status"))
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
billingProcessor?.release()
|
||||
skuDetailsLoadAsyncTask?.cancel(true)
|
||||
super.onDestroy()
|
||||
}
|
||||
}
|
||||
|
||||
private class SkuDetailsLoadAsyncTask internal constructor(supportDevelopmentActivity: SupportDevelopmentActivity) :
|
||||
AsyncTask<Void, Void, List<SkuDetails>>() {
|
||||
|
||||
private val weakReference: WeakReference<SupportDevelopmentActivity> = WeakReference(
|
||||
supportDevelopmentActivity
|
||||
)
|
||||
|
||||
override fun onPreExecute() {
|
||||
super.onPreExecute()
|
||||
val supportDevelopmentActivity = weakReference.get() ?: return
|
||||
|
||||
supportDevelopmentActivity.progressContainer.visibility = View.VISIBLE
|
||||
supportDevelopmentActivity.recyclerView.visibility = View.GONE
|
||||
}
|
||||
|
||||
override fun doInBackground(vararg params: Void): List<SkuDetails>? {
|
||||
val dialog = weakReference.get()
|
||||
if (dialog != null) {
|
||||
val ids =
|
||||
dialog.resources.getStringArray(SupportDevelopmentActivity.DONATION_PRODUCT_IDS)
|
||||
return dialog.billingProcessor!!.getPurchaseListingDetails(ArrayList(Arrays.asList(*ids)))
|
||||
}
|
||||
cancel(false)
|
||||
return null
|
||||
}
|
||||
|
||||
override fun onPostExecute(skuDetails: List<SkuDetails>?) {
|
||||
super.onPostExecute(skuDetails)
|
||||
val dialog = weakReference.get() ?: return
|
||||
|
||||
if (skuDetails == null || skuDetails.isEmpty()) {
|
||||
dialog.progressContainer.visibility = View.GONE
|
||||
return
|
||||
}
|
||||
|
||||
dialog.progressContainer.visibility = View.GONE
|
||||
dialog.recyclerView.itemAnimator = DefaultItemAnimator()
|
||||
dialog.recyclerView.layoutManager = GridLayoutManager(dialog, 2)
|
||||
dialog.recyclerView.adapter = SkuDetailsAdapter(dialog, skuDetails)
|
||||
dialog.recyclerView.visibility = View.VISIBLE
|
||||
}
|
||||
}
|
||||
|
||||
class SkuDetailsAdapter(
|
||||
private var donationsDialog: SupportDevelopmentActivity, objects: List<SkuDetails>
|
||||
) : RecyclerView.Adapter<SkuDetailsAdapter.ViewHolder>() {
|
||||
|
||||
private var skuDetailsList: List<SkuDetails> = ArrayList()
|
||||
|
||||
init {
|
||||
skuDetailsList = objects
|
||||
}
|
||||
|
||||
private fun getIcon(position: Int): Int {
|
||||
return when (position) {
|
||||
0 -> R.drawable.ic_cookie_white_24dp
|
||||
1 -> R.drawable.ic_take_away_white_24dp
|
||||
2 -> R.drawable.ic_take_away_coffe_white_24dp
|
||||
3 -> R.drawable.ic_beer_white_24dp
|
||||
4 -> R.drawable.ic_fast_food_meal_white_24dp
|
||||
5 -> R.drawable.ic_popcorn_white_24dp
|
||||
6 -> R.drawable.ic_card_giftcard_white_24dp
|
||||
7 -> R.drawable.ic_food_croissant_white_24dp
|
||||
else -> R.drawable.ic_card_giftcard_white_24dp
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreateViewHolder(viewGroup: ViewGroup, i: Int): ViewHolder {
|
||||
return ViewHolder(
|
||||
LayoutInflater.from(donationsDialog).inflate(
|
||||
LAYOUT_RES_ID,
|
||||
viewGroup,
|
||||
false
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(viewHolder: ViewHolder, i: Int) {
|
||||
val skuDetails = skuDetailsList[i]
|
||||
viewHolder.title.text = skuDetails.title.replace("(Retro Music Player \uD83C\uDFB5)", "")
|
||||
.trim { it <= ' ' }
|
||||
viewHolder.text.text = skuDetails.description
|
||||
viewHolder.text.visibility = View.GONE
|
||||
viewHolder.price.text = skuDetails.priceText
|
||||
viewHolder.image.setImageResource(getIcon(i))
|
||||
|
||||
val purchased = donationsDialog.billingProcessor!!.isPurchased(skuDetails.productId)
|
||||
val titleTextColor = if (purchased) ATHUtil.resolveColor(
|
||||
donationsDialog,
|
||||
android.R.attr.textColorHint
|
||||
) else donationsDialog.textColorPrimary()
|
||||
val contentTextColor =
|
||||
if (purchased) titleTextColor else donationsDialog.textColorSecondary()
|
||||
|
||||
viewHolder.title.setTextColor(titleTextColor)
|
||||
viewHolder.text.setTextColor(contentTextColor)
|
||||
viewHolder.price.setTextColor(titleTextColor)
|
||||
|
||||
strikeThrough(viewHolder.title, purchased)
|
||||
strikeThrough(viewHolder.text, purchased)
|
||||
strikeThrough(viewHolder.price, purchased)
|
||||
|
||||
viewHolder.itemView.setOnTouchListener { _, _ -> purchased }
|
||||
viewHolder.itemView.setOnClickListener { donationsDialog.donate(i) }
|
||||
}
|
||||
|
||||
override fun getItemCount(): Int {
|
||||
return skuDetailsList.size
|
||||
}
|
||||
|
||||
class ViewHolder(view: View) : RecyclerView.ViewHolder(view) {
|
||||
var title: TextView = view.findViewById(R.id.itemTitle)
|
||||
var text: TextView = view.findViewById(R.id.itemText)
|
||||
var price: TextView = view.findViewById(R.id.itemPrice)
|
||||
var image: AppCompatImageView = view.findViewById(R.id.itemImage)
|
||||
}
|
||||
|
||||
companion object {
|
||||
@LayoutRes
|
||||
private val LAYOUT_RES_ID = R.layout.item_donation_option
|
||||
|
||||
private fun strikeThrough(textView: TextView, strikeThrough: Boolean) {
|
||||
textView.paintFlags =
|
||||
if (strikeThrough) textView.paintFlags or Paint.STRIKE_THRU_TEXT_FLAG
|
||||
else textView.paintFlags and Paint.STRIKE_THRU_TEXT_FLAG.inv()
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,218 +0,0 @@
|
|||
package code.name.monkey.retromusic.activities
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.Intent
|
||||
import android.content.res.ColorStateList
|
||||
import android.graphics.Bitmap
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
import android.text.TextUtils
|
||||
import android.view.MenuItem
|
||||
import android.widget.Toast
|
||||
import code.name.monkey.appthemehelper.util.ColorUtil
|
||||
import code.name.monkey.appthemehelper.util.MaterialUtil
|
||||
import code.name.monkey.appthemehelper.util.MaterialValueHelper
|
||||
import code.name.monkey.retromusic.Constants.USER_BANNER
|
||||
import code.name.monkey.retromusic.Constants.USER_PROFILE
|
||||
import code.name.monkey.retromusic.R
|
||||
import code.name.monkey.retromusic.activities.base.AbsBaseActivity
|
||||
import code.name.monkey.retromusic.extensions.accentColor
|
||||
import code.name.monkey.retromusic.extensions.applyToolbar
|
||||
import code.name.monkey.retromusic.glide.ProfileBannerGlideRequest
|
||||
import code.name.monkey.retromusic.glide.UserProfileGlideRequest
|
||||
import code.name.monkey.retromusic.util.ImageUtil
|
||||
import code.name.monkey.retromusic.util.PreferenceUtil
|
||||
import com.bumptech.glide.Glide
|
||||
import com.bumptech.glide.load.engine.DiskCacheStrategy
|
||||
import com.bumptech.glide.request.RequestListener
|
||||
import com.bumptech.glide.request.target.Target
|
||||
import com.github.dhaval2404.imagepicker.ImagePicker
|
||||
import com.github.dhaval2404.imagepicker.constant.ImageProvider
|
||||
import kotlinx.android.synthetic.main.activity_user_info.*
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import java.io.BufferedOutputStream
|
||||
import java.io.File
|
||||
import java.io.FileOutputStream
|
||||
import java.io.IOException
|
||||
|
||||
class UserInfoActivity : AbsBaseActivity() {
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(R.layout.activity_user_info)
|
||||
setStatusbarColorAuto()
|
||||
setNavigationbarColorAuto()
|
||||
setTaskDescriptionColorAuto()
|
||||
setLightNavigationBar(true)
|
||||
applyToolbar(toolbar)
|
||||
|
||||
MaterialUtil.setTint(nameContainer, false)
|
||||
name.setText(PreferenceUtil.userName)
|
||||
|
||||
userImage.setOnClickListener {
|
||||
pickNewPhoto()
|
||||
}
|
||||
|
||||
bannerSelect.setOnClickListener {
|
||||
selectBannerImage()
|
||||
}
|
||||
|
||||
next.setOnClickListener {
|
||||
val nameString = name.text.toString().trim { it <= ' ' }
|
||||
if (TextUtils.isEmpty(nameString)) {
|
||||
Toast.makeText(this, "Umm name is empty", Toast.LENGTH_SHORT).show()
|
||||
return@setOnClickListener
|
||||
}
|
||||
PreferenceUtil.userName = nameString
|
||||
setResult(Activity.RESULT_OK)
|
||||
finish()
|
||||
}
|
||||
|
||||
val textColor =
|
||||
MaterialValueHelper.getPrimaryTextColor(this, ColorUtil.isColorLight(accentColor()))
|
||||
next.backgroundTintList = ColorStateList.valueOf(accentColor())
|
||||
next.iconTint = ColorStateList.valueOf(textColor)
|
||||
next.setTextColor(textColor)
|
||||
loadProfile()
|
||||
}
|
||||
|
||||
private fun loadProfile() {
|
||||
bannerImage?.let {
|
||||
ProfileBannerGlideRequest.Builder.from(
|
||||
Glide.with(this),
|
||||
ProfileBannerGlideRequest.getBannerModel()
|
||||
).build().into(it)
|
||||
}
|
||||
UserProfileGlideRequest.Builder.from(
|
||||
Glide.with(this),
|
||||
UserProfileGlideRequest.getUserModel()
|
||||
).build().into(userImage)
|
||||
}
|
||||
|
||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||
if (item.itemId == android.R.id.home) {
|
||||
onBackPressed()
|
||||
}
|
||||
return super.onOptionsItemSelected(item)
|
||||
}
|
||||
|
||||
private fun selectBannerImage() {
|
||||
ImagePicker.with(this)
|
||||
.compress(1440)
|
||||
.provider(ImageProvider.GALLERY)
|
||||
.crop(16f, 9f)
|
||||
.start(PICK_BANNER_REQUEST)
|
||||
}
|
||||
|
||||
private fun pickNewPhoto() {
|
||||
ImagePicker.with(this)
|
||||
.provider(ImageProvider.GALLERY)
|
||||
.cropSquare()
|
||||
.compress(1440)
|
||||
.start(PICK_IMAGE_REQUEST)
|
||||
}
|
||||
|
||||
|
||||
public override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
||||
super.onActivityResult(requestCode, resultCode, data)
|
||||
if (resultCode == Activity.RESULT_OK && requestCode == PICK_IMAGE_REQUEST) {
|
||||
val fileUri = data?.data
|
||||
fileUri?.let { setAndSaveUserImage(it) }
|
||||
} else if (resultCode == Activity.RESULT_OK && requestCode == PICK_BANNER_REQUEST) {
|
||||
val fileUri = data?.data
|
||||
fileUri?.let { setAndSaveBannerImage(it) }
|
||||
} else if (resultCode == ImagePicker.RESULT_ERROR) {
|
||||
Toast.makeText(this, ImagePicker.getError(data), Toast.LENGTH_SHORT).show()
|
||||
} else {
|
||||
Toast.makeText(this, "Task Cancelled", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
|
||||
private fun setAndSaveBannerImage(fileUri: Uri) {
|
||||
Glide.with(this)
|
||||
.load(fileUri)
|
||||
.asBitmap()
|
||||
.diskCacheStrategy(DiskCacheStrategy.NONE)
|
||||
.listener(object : RequestListener<Any, Bitmap> {
|
||||
override fun onException(
|
||||
e: java.lang.Exception?,
|
||||
model: Any?,
|
||||
target: Target<Bitmap>?,
|
||||
isFirstResource: Boolean
|
||||
): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun onResourceReady(
|
||||
resource: Bitmap?,
|
||||
model: Any?,
|
||||
target: Target<Bitmap>?,
|
||||
isFromMemoryCache: Boolean,
|
||||
isFirstResource: Boolean
|
||||
): Boolean {
|
||||
resource?.let { saveImage(it, USER_BANNER) }
|
||||
return false
|
||||
}
|
||||
})
|
||||
.into(bannerImage)
|
||||
}
|
||||
|
||||
private fun saveImage(bitmap: Bitmap, fileName: String) {
|
||||
CoroutineScope(Dispatchers.IO).launch() {
|
||||
val appDir = applicationContext.filesDir
|
||||
val file = File(appDir, fileName)
|
||||
var successful = false
|
||||
try {
|
||||
val os = BufferedOutputStream(FileOutputStream(file))
|
||||
successful = ImageUtil.resizeBitmap(bitmap, 2048)
|
||||
.compress(Bitmap.CompressFormat.WEBP, 100, os)
|
||||
withContext(Dispatchers.IO) { os.close() }
|
||||
} catch (e: IOException) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
if (successful) {
|
||||
withContext(Dispatchers.Main) {
|
||||
Toast.makeText(this@UserInfoActivity, "Updated", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun setAndSaveUserImage(fileUri: Uri) {
|
||||
Glide.with(this)
|
||||
.load(fileUri)
|
||||
.asBitmap()
|
||||
.diskCacheStrategy(DiskCacheStrategy.NONE)
|
||||
.listener(object : RequestListener<Any, Bitmap> {
|
||||
override fun onException(
|
||||
e: java.lang.Exception?,
|
||||
model: Any?,
|
||||
target: Target<Bitmap>?,
|
||||
isFirstResource: Boolean
|
||||
): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun onResourceReady(
|
||||
resource: Bitmap?,
|
||||
model: Any?,
|
||||
target: Target<Bitmap>?,
|
||||
isFromMemoryCache: Boolean,
|
||||
isFirstResource: Boolean
|
||||
): Boolean {
|
||||
resource?.let { saveImage(it, USER_PROFILE) }
|
||||
return false
|
||||
}
|
||||
})
|
||||
.into(userImage)
|
||||
}
|
||||
|
||||
|
||||
companion object {
|
||||
private const val PICK_IMAGE_REQUEST = 9002
|
||||
private const val PICK_BANNER_REQUEST = 9004
|
||||
}
|
||||
}
|
|
@ -1,90 +0,0 @@
|
|||
package code.name.monkey.retromusic.activities;
|
||||
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.pm.PackageInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.graphics.Color;
|
||||
import android.os.Bundle;
|
||||
import android.webkit.WebView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.appcompat.widget.Toolbar;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Locale;
|
||||
|
||||
import code.name.monkey.appthemehelper.ThemeStore;
|
||||
import code.name.monkey.appthemehelper.util.ATHUtil;
|
||||
import code.name.monkey.appthemehelper.util.ColorUtil;
|
||||
import code.name.monkey.appthemehelper.util.MaterialValueHelper;
|
||||
import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper;
|
||||
import code.name.monkey.retromusic.R;
|
||||
import code.name.monkey.retromusic.activities.base.AbsBaseActivity;
|
||||
import code.name.monkey.retromusic.util.PreferenceUtil;
|
||||
|
||||
public class WhatsNewActivity extends AbsBaseActivity {
|
||||
|
||||
private static String colorToCSS(int color) {
|
||||
return String.format(Locale.getDefault(), "rgba(%d, %d, %d, %d)", Color.red(color), Color.green(color),
|
||||
Color.blue(color), Color.alpha(color)); // on API 29, WebView doesn't load with hex colors
|
||||
}
|
||||
|
||||
private static void setChangelogRead(@NonNull Context context) {
|
||||
try {
|
||||
PackageInfo pInfo = context.getPackageManager().getPackageInfo(context.getPackageName(), 0);
|
||||
int currentVersion = pInfo.versionCode;
|
||||
PreferenceUtil.INSTANCE.setLastVersion(currentVersion);
|
||||
} catch (PackageManager.NameNotFoundException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCreate(@Nullable Bundle savedInstanceState) {
|
||||
setDrawUnderStatusBar();
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_whats_new);
|
||||
setStatusbarColorAuto();
|
||||
setNavigationbarColorAuto();
|
||||
setTaskDescriptionColorAuto();
|
||||
|
||||
WebView webView = findViewById(R.id.webView);
|
||||
Toolbar toolbar = findViewById(R.id.toolbar);
|
||||
toolbar.setBackgroundColor(ATHUtil.INSTANCE.resolveColor(this, R.attr.colorSurface));
|
||||
toolbar.setNavigationOnClickListener(v -> onBackPressed());
|
||||
ToolbarContentTintHelper.colorBackButton(toolbar);
|
||||
|
||||
try {
|
||||
StringBuilder buf = new StringBuilder();
|
||||
InputStream json = getAssets().open("retro-changelog.html");
|
||||
BufferedReader in = new BufferedReader(new InputStreamReader(json, StandardCharsets.UTF_8));
|
||||
String str;
|
||||
while ((str = in.readLine()) != null) {
|
||||
buf.append(str);
|
||||
}
|
||||
in.close();
|
||||
|
||||
// Inject color values for WebView body background and links
|
||||
final boolean isDark = ATHUtil.INSTANCE.isWindowBackgroundDark(this);
|
||||
final int accentColor = ThemeStore.Companion.accentColor(this);
|
||||
final String backgroundColor = colorToCSS(ATHUtil.INSTANCE.resolveColor(this, R.attr.colorSurface, Color.parseColor(isDark ? "#424242" : "#ffffff")));
|
||||
final String contentColor = colorToCSS(Color.parseColor(isDark ? "#ffffff" : "#000000"));
|
||||
final String textColor = colorToCSS(Color.parseColor(isDark ? "#60FFFFFF" : "#80000000"));
|
||||
final String accentColorString = colorToCSS(ThemeStore.Companion.accentColor(this));
|
||||
final String accentTextColor = colorToCSS(MaterialValueHelper.getPrimaryTextColor(this, ColorUtil.INSTANCE.isColorLight(accentColor)));
|
||||
final String changeLog = buf.toString()
|
||||
.replace("{style-placeholder}", String.format("body { background-color: %s; color: %s; } li {color: %s;} .colorHeader {background-color: %s; color: %s;} .tag {color: %s;}", backgroundColor, contentColor, textColor, accentColorString, accentTextColor, accentColorString))
|
||||
.replace("{link-color}", colorToCSS(ThemeStore.Companion.accentColor(this)))
|
||||
.replace("{link-color-active}", colorToCSS(ColorUtil.INSTANCE.lightenColor(ThemeStore.Companion.accentColor(this))));
|
||||
webView.loadData(changeLog, "text/html", "UTF-8");
|
||||
} catch (Throwable e) {
|
||||
webView.loadData("<h1>Unable to load</h1><p>" + e.getLocalizedMessage() + "</p>", "text/html", "UTF-8");
|
||||
}
|
||||
setChangelogRead(this);
|
||||
}
|
||||
}
|
|
@ -1,401 +0,0 @@
|
|||
package code.name.monkey.retromusic.activities.albums
|
||||
|
||||
import android.app.ActivityOptions
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import android.transition.Slide
|
||||
import android.view.Menu
|
||||
import android.view.MenuItem
|
||||
import android.view.SubMenu
|
||||
import android.view.View
|
||||
import android.widget.ImageView
|
||||
import androidx.core.app.ActivityCompat
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import androidx.recyclerview.widget.DefaultItemAnimator
|
||||
import androidx.recyclerview.widget.GridLayoutManager
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import code.name.monkey.appthemehelper.util.ATHUtil
|
||||
import code.name.monkey.appthemehelper.util.MaterialUtil
|
||||
import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper
|
||||
import code.name.monkey.retromusic.R
|
||||
import code.name.monkey.retromusic.activities.base.AbsSlidingMusicPanelActivity
|
||||
import code.name.monkey.retromusic.activities.tageditor.AbsTagEditorActivity
|
||||
import code.name.monkey.retromusic.activities.tageditor.AlbumTagEditorActivity
|
||||
import code.name.monkey.retromusic.adapter.album.HorizontalAlbumAdapter
|
||||
import code.name.monkey.retromusic.adapter.song.SimpleSongAdapter
|
||||
import code.name.monkey.retromusic.dialogs.AddToPlaylistDialog
|
||||
import code.name.monkey.retromusic.dialogs.DeleteSongsDialog
|
||||
import code.name.monkey.retromusic.extensions.extraNotNull
|
||||
import code.name.monkey.retromusic.extensions.ripAlpha
|
||||
import code.name.monkey.retromusic.extensions.show
|
||||
import code.name.monkey.retromusic.extensions.surfaceColor
|
||||
import code.name.monkey.retromusic.glide.AlbumGlideRequest
|
||||
import code.name.monkey.retromusic.glide.ArtistGlideRequest
|
||||
import code.name.monkey.retromusic.glide.RetroMusicColoredTarget
|
||||
import code.name.monkey.retromusic.helper.MusicPlayerRemote
|
||||
import code.name.monkey.retromusic.helper.SortOrder.AlbumSongSortOrder
|
||||
import code.name.monkey.retromusic.interfaces.CabHolder
|
||||
import code.name.monkey.retromusic.model.Album
|
||||
import code.name.monkey.retromusic.model.Artist
|
||||
import code.name.monkey.retromusic.mvp.presenter.AlbumDetailsView
|
||||
import code.name.monkey.retromusic.rest.model.LastFmAlbum
|
||||
import code.name.monkey.retromusic.util.*
|
||||
import code.name.monkey.retromusic.util.color.MediaNotificationProcessor
|
||||
import com.afollestad.materialcab.MaterialCab
|
||||
import com.bumptech.glide.Glide
|
||||
import kotlinx.android.synthetic.main.activity_album.*
|
||||
import kotlinx.android.synthetic.main.activity_album_content.*
|
||||
import java.util.*
|
||||
import android.util.Pair as UtilPair
|
||||
|
||||
class AlbumDetailsActivity : AbsSlidingMusicPanelActivity(), AlbumDetailsView, CabHolder {
|
||||
override fun openCab(menuRes: Int, callback: MaterialCab.Callback): MaterialCab {
|
||||
cab?.let {
|
||||
if (it.isActive) it.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)
|
||||
return cab as MaterialCab
|
||||
}
|
||||
|
||||
private lateinit var viewModel: AlbumDetailsViewModel
|
||||
private lateinit var simpleSongAdapter: SimpleSongAdapter
|
||||
private lateinit var album: Album
|
||||
private lateinit var artistImage: ImageView
|
||||
private var cab: MaterialCab? = null
|
||||
private val savedSortOrder: String
|
||||
get() = PreferenceUtil.albumDetailSongSortOrder
|
||||
|
||||
override fun createContentView(): View {
|
||||
return wrapSlidingMusicPanel(R.layout.activity_album)
|
||||
}
|
||||
|
||||
private fun windowEnterTransition() {
|
||||
val slide = Slide()
|
||||
slide.excludeTarget(R.id.appBarLayout, true)
|
||||
slide.excludeTarget(R.id.status_bar, true)
|
||||
slide.excludeTarget(android.R.id.statusBarBackground, true)
|
||||
slide.excludeTarget(android.R.id.navigationBarBackground, true)
|
||||
window.enterTransition = slide
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
setDrawUnderStatusBar()
|
||||
super.onCreate(savedInstanceState)
|
||||
setStatusbarColorAuto()
|
||||
setNavigationbarColorAuto()
|
||||
setTaskDescriptionColorAuto()
|
||||
setLightNavigationBar(true)
|
||||
setBottomBarVisibility(View.GONE)
|
||||
window.sharedElementsUseOverlay = true
|
||||
windowEnterTransition()
|
||||
|
||||
val albumId = extraNotNull<Int>(EXTRA_ALBUM_ID).value
|
||||
ActivityCompat.postponeEnterTransition(this)
|
||||
val viewModelFactory = AlbumDetailsViewModelFactory(application, albumId)
|
||||
viewModel = ViewModelProvider(this, viewModelFactory).get(AlbumDetailsViewModel::class.java)
|
||||
addMusicServiceEventListener(viewModel)
|
||||
viewModel.getAlbum().observe(this, androidx.lifecycle.Observer {
|
||||
ActivityCompat.startPostponedEnterTransition(this@AlbumDetailsActivity)
|
||||
album(it)
|
||||
})
|
||||
viewModel.getArtist().observe(this, androidx.lifecycle.Observer {
|
||||
loadArtistImage(it)
|
||||
})
|
||||
viewModel.getAlbumInfo().observe(this, androidx.lifecycle.Observer {
|
||||
aboutAlbum(it)
|
||||
})
|
||||
setupRecyclerView()
|
||||
artistImage = findViewById(R.id.artistImage)
|
||||
artistImage.setOnClickListener {
|
||||
val artistPairs = ActivityOptions.makeSceneTransitionAnimation(
|
||||
this,
|
||||
UtilPair.create(
|
||||
artistImage,
|
||||
getString(R.string.transition_artist_image)
|
||||
)
|
||||
)
|
||||
NavigationUtil.goToArtistOptions(this, album.artistId, artistPairs)
|
||||
}
|
||||
playAction.apply {
|
||||
setOnClickListener { MusicPlayerRemote.openQueue(album.songs!!, 0, true) }
|
||||
}
|
||||
shuffleAction.apply {
|
||||
setOnClickListener { MusicPlayerRemote.openAndShuffleQueue(album.songs!!, true) }
|
||||
}
|
||||
|
||||
aboutAlbumText.setOnClickListener {
|
||||
if (aboutAlbumText.maxLines == 4) {
|
||||
aboutAlbumText.maxLines = Integer.MAX_VALUE
|
||||
} else {
|
||||
aboutAlbumText.maxLines = 4
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun setupRecyclerView() {
|
||||
simpleSongAdapter = SimpleSongAdapter(this, ArrayList(), R.layout.item_song, this)
|
||||
recyclerView.apply {
|
||||
layoutManager = LinearLayoutManager(this@AlbumDetailsActivity)
|
||||
itemAnimator = DefaultItemAnimator()
|
||||
isNestedScrollingEnabled = false
|
||||
adapter = simpleSongAdapter
|
||||
}
|
||||
}
|
||||
|
||||
override fun complete() {
|
||||
ActivityCompat.startPostponedEnterTransition(this)
|
||||
}
|
||||
|
||||
override fun album(album: Album) {
|
||||
complete()
|
||||
if (album.songs!!.isEmpty()) {
|
||||
finish()
|
||||
return
|
||||
}
|
||||
this.album = album
|
||||
|
||||
albumTitle.text = album.title
|
||||
val songText =
|
||||
resources.getQuantityString(
|
||||
R.plurals.albumSongs,
|
||||
album.songCount,
|
||||
album.songCount
|
||||
)
|
||||
songTitle.text = songText
|
||||
|
||||
if (MusicUtil.getYearString(album.year) == "-") {
|
||||
albumText.text = String.format(
|
||||
"%s • %s",
|
||||
album.artistName,
|
||||
MusicUtil.getReadableDurationString(MusicUtil.getTotalDuration(album.songs))
|
||||
)
|
||||
} else {
|
||||
albumText.text = String.format(
|
||||
"%s • %s • %s",
|
||||
album.artistName,
|
||||
MusicUtil.getYearString(album.year),
|
||||
MusicUtil.getReadableDurationString(MusicUtil.getTotalDuration(album.songs))
|
||||
)
|
||||
}
|
||||
loadAlbumCover()
|
||||
simpleSongAdapter.swapDataSet(album.songs)
|
||||
viewModel.loadArtist(album.artistId)
|
||||
viewModel.loadAlbumInfo(album)
|
||||
}
|
||||
|
||||
override fun moreAlbums(albums: List<Album>) {
|
||||
moreTitle.show()
|
||||
moreRecyclerView.show()
|
||||
moreTitle.text = String.format(getString(R.string.label_more_from), album.artistName)
|
||||
|
||||
val albumAdapter = HorizontalAlbumAdapter(this, albums, null)
|
||||
moreRecyclerView.layoutManager = GridLayoutManager(
|
||||
this,
|
||||
1,
|
||||
GridLayoutManager.HORIZONTAL,
|
||||
false
|
||||
)
|
||||
moreRecyclerView.adapter = albumAdapter
|
||||
}
|
||||
|
||||
override fun aboutAlbum(lastFmAlbum: LastFmAlbum) {
|
||||
if (lastFmAlbum.album != null) {
|
||||
if (lastFmAlbum.album.wiki != null) {
|
||||
aboutAlbumText.show()
|
||||
aboutAlbumTitle.show()
|
||||
aboutAlbumTitle.text =
|
||||
String.format(getString(R.string.about_album_label), lastFmAlbum.album.name)
|
||||
aboutAlbumText.text = lastFmAlbum.album.wiki.content
|
||||
}
|
||||
if (lastFmAlbum.album.listeners.isNotEmpty()) {
|
||||
listeners.show()
|
||||
listenersLabel.show()
|
||||
scrobbles.show()
|
||||
scrobblesLabel.show()
|
||||
|
||||
listeners.text = RetroUtil.formatValue(lastFmAlbum.album.listeners.toFloat())
|
||||
scrobbles.text = RetroUtil.formatValue(lastFmAlbum.album.playcount.toFloat())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun loadArtistImage(artist: Artist) {
|
||||
ArtistGlideRequest.Builder.from(Glide.with(this), artist)
|
||||
.generatePalette(this)
|
||||
.build()
|
||||
.dontAnimate()
|
||||
.dontTransform()
|
||||
.into(object : RetroMusicColoredTarget(artistImage) {
|
||||
override fun onColorReady(colors: MediaNotificationProcessor) {
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
private fun loadAlbumCover() {
|
||||
AlbumGlideRequest.Builder.from(Glide.with(this), album.safeGetFirstSong())
|
||||
.checkIgnoreMediaStore(this)
|
||||
.ignoreMediaStore(PreferenceUtil.isIgnoreMediaStoreArtwork)
|
||||
.generatePalette(this)
|
||||
.build()
|
||||
.dontAnimate()
|
||||
.dontTransform()
|
||||
.into(object : RetroMusicColoredTarget(image) {
|
||||
override fun onColorReady(colors: MediaNotificationProcessor) {
|
||||
setColors(colors)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
private fun setColors(color: MediaNotificationProcessor) {
|
||||
val buttonColor = if (PreferenceUtil.isAdaptiveColor)
|
||||
color.backgroundColor.ripAlpha()
|
||||
else
|
||||
ATHUtil.resolveColor(this, R.attr.colorSurface)
|
||||
|
||||
MaterialUtil.setTint(button = shuffleAction, color = buttonColor)
|
||||
MaterialUtil.setTint(button = playAction, color = buttonColor)
|
||||
|
||||
toolbar.setBackgroundColor(surfaceColor())
|
||||
setSupportActionBar(toolbar)
|
||||
supportActionBar?.title = null
|
||||
}
|
||||
|
||||
override fun onCreateOptionsMenu(menu: Menu): Boolean {
|
||||
menuInflater.inflate(R.menu.menu_album_detail, menu)
|
||||
val sortOrder = menu.findItem(R.id.action_sort_order)
|
||||
setUpSortOrderMenu(sortOrder.subMenu)
|
||||
ToolbarContentTintHelper.handleOnCreateOptionsMenu(
|
||||
this,
|
||||
toolbar,
|
||||
menu,
|
||||
getToolbarBackgroundColor(toolbar)
|
||||
)
|
||||
return super.onCreateOptionsMenu(menu)
|
||||
}
|
||||
|
||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||
return handleSortOrderMenuItem(item)
|
||||
}
|
||||
|
||||
private fun handleSortOrderMenuItem(item: MenuItem): Boolean {
|
||||
var sortOrder: String? = null
|
||||
val songs = simpleSongAdapter.dataSet
|
||||
when (item.itemId) {
|
||||
R.id.action_play_next -> {
|
||||
MusicPlayerRemote.playNext(songs)
|
||||
return true
|
||||
}
|
||||
R.id.action_add_to_current_playing -> {
|
||||
MusicPlayerRemote.enqueue(songs)
|
||||
return true
|
||||
}
|
||||
R.id.action_add_to_playlist -> {
|
||||
AddToPlaylistDialog.create(songs).show(supportFragmentManager, "ADD_PLAYLIST")
|
||||
return true
|
||||
}
|
||||
R.id.action_delete_from_device -> {
|
||||
DeleteSongsDialog.create(songs).show(supportFragmentManager, "DELETE_SONGS")
|
||||
return true
|
||||
}
|
||||
android.R.id.home -> {
|
||||
super.onBackPressed()
|
||||
return true
|
||||
}
|
||||
R.id.action_tag_editor -> {
|
||||
val intent = Intent(this, AlbumTagEditorActivity::class.java)
|
||||
intent.putExtra(AbsTagEditorActivity.EXTRA_ID, album.id)
|
||||
val options = ActivityOptions.makeSceneTransitionAnimation(
|
||||
this,
|
||||
albumCoverContainer,
|
||||
"${getString(R.string.transition_album_art)}_${album.id}"
|
||||
)
|
||||
startActivityForResult(
|
||||
intent,
|
||||
TAG_EDITOR_REQUEST, options.toBundle()
|
||||
)
|
||||
return true
|
||||
}
|
||||
/*Sort*/
|
||||
R.id.action_sort_order_title -> sortOrder = AlbumSongSortOrder.SONG_A_Z
|
||||
R.id.action_sort_order_title_desc -> sortOrder = AlbumSongSortOrder.SONG_Z_A
|
||||
R.id.action_sort_order_track_list -> sortOrder = AlbumSongSortOrder.SONG_TRACK_LIST
|
||||
R.id.action_sort_order_artist_song_duration ->
|
||||
sortOrder = AlbumSongSortOrder.SONG_DURATION
|
||||
}
|
||||
if (sortOrder != null) {
|
||||
item.isChecked = true
|
||||
setSaveSortOrder(sortOrder)
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
private fun setUpSortOrderMenu(sortOrder: SubMenu) {
|
||||
when (savedSortOrder) {
|
||||
AlbumSongSortOrder.SONG_A_Z -> sortOrder.findItem(R.id.action_sort_order_title)
|
||||
.isChecked = true
|
||||
AlbumSongSortOrder.SONG_Z_A -> sortOrder.findItem(R.id.action_sort_order_title_desc)
|
||||
.isChecked = true
|
||||
AlbumSongSortOrder.SONG_TRACK_LIST -> sortOrder.findItem(R.id.action_sort_order_track_list)
|
||||
.isChecked = true
|
||||
AlbumSongSortOrder.SONG_DURATION -> sortOrder.findItem(R.id.action_sort_order_artist_song_duration)
|
||||
.isChecked = true
|
||||
}
|
||||
}
|
||||
|
||||
private fun setSaveSortOrder(sortOrder: String) {
|
||||
PreferenceUtil.albumDetailSongSortOrder = sortOrder
|
||||
when (sortOrder) {
|
||||
AlbumSongSortOrder.SONG_TRACK_LIST -> album.songs?.sortWith(Comparator { o1, o2 ->
|
||||
o1.trackNumber.compareTo(
|
||||
o2.trackNumber
|
||||
)
|
||||
})
|
||||
AlbumSongSortOrder.SONG_A_Z -> album.songs?.sortWith(Comparator { o1, o2 ->
|
||||
o1.title.compareTo(
|
||||
o2.title
|
||||
)
|
||||
})
|
||||
AlbumSongSortOrder.SONG_Z_A -> album.songs?.sortWith(Comparator { o1, o2 ->
|
||||
o2.title.compareTo(
|
||||
o1.title
|
||||
)
|
||||
})
|
||||
AlbumSongSortOrder.SONG_DURATION -> album.songs?.sortWith(Comparator { o1, o2 ->
|
||||
o1.duration.compareTo(
|
||||
o2.duration
|
||||
)
|
||||
})
|
||||
}
|
||||
album.songs?.let { simpleSongAdapter.swapDataSet(it) }
|
||||
}
|
||||
|
||||
|
||||
override fun onBackPressed() {
|
||||
if (cab != null && cab!!.isActive) {
|
||||
cab?.finish()
|
||||
} else {
|
||||
super.onBackPressed()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
super.onDestroy()
|
||||
removeMusicServiceEventListener(viewModel)
|
||||
}
|
||||
companion object {
|
||||
|
||||
const val EXTRA_ALBUM_ID = "extra_album_id"
|
||||
private const val TAG_EDITOR_REQUEST = 2001
|
||||
}
|
||||
}
|
|
@ -1,66 +0,0 @@
|
|||
package code.name.monkey.retromusic.activities.albums
|
||||
|
||||
import android.app.Application
|
||||
import androidx.lifecycle.AndroidViewModel
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import code.name.monkey.retromusic.interfaces.MusicServiceEventListener
|
||||
import code.name.monkey.retromusic.model.Album
|
||||
import code.name.monkey.retromusic.model.Artist
|
||||
import code.name.monkey.retromusic.providers.RepositoryImpl
|
||||
import code.name.monkey.retromusic.rest.model.LastFmAlbum
|
||||
import kotlinx.coroutines.Deferred
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.async
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
class AlbumDetailsViewModel(
|
||||
application: Application,
|
||||
private val albumId: Int
|
||||
) : AndroidViewModel(application), MusicServiceEventListener {
|
||||
private val _repository = RepositoryImpl(application.applicationContext)
|
||||
private val _album = MutableLiveData<Album>()
|
||||
private val _artist = MutableLiveData<Artist>()
|
||||
private val _lastFmAlbum = MutableLiveData<LastFmAlbum>()
|
||||
|
||||
fun getAlbum(): LiveData<Album> = _album
|
||||
fun getArtist(): LiveData<Artist> = _artist
|
||||
fun getAlbumInfo(): LiveData<LastFmAlbum> = _lastFmAlbum
|
||||
|
||||
init {
|
||||
loadAlbumDetails()
|
||||
}
|
||||
|
||||
private fun loadAlbumDetails() = viewModelScope.launch {
|
||||
val album = loadAlbumAsync.await() ?: throw NullPointerException("Album couldn't found")
|
||||
_album.postValue(album)
|
||||
}
|
||||
|
||||
fun loadAlbumInfo(album: Album) = viewModelScope.launch(Dispatchers.IO) {
|
||||
val lastFmAlbum = _repository.albumInfo(album.artistName ?: "-", album.title ?: "-")
|
||||
_lastFmAlbum.postValue(lastFmAlbum)
|
||||
}
|
||||
|
||||
fun loadArtist(artistId: Int) = viewModelScope.launch(Dispatchers.IO) {
|
||||
val artist = _repository.artistById(artistId)
|
||||
_artist.postValue(artist)
|
||||
}
|
||||
|
||||
private val loadAlbumAsync: Deferred<Album?>
|
||||
get() = viewModelScope.async(Dispatchers.IO) {
|
||||
_repository.albumById(albumId)
|
||||
}
|
||||
|
||||
override fun onMediaStoreChanged() {
|
||||
loadAlbumDetails()
|
||||
}
|
||||
|
||||
override fun onServiceConnected() {}
|
||||
override fun onServiceDisconnected() {}
|
||||
override fun onQueueChanged() {}
|
||||
override fun onPlayingMetaChanged() {}
|
||||
override fun onPlayStateChanged() {}
|
||||
override fun onRepeatModeChanged() {}
|
||||
override fun onShuffleModeChanged() {}
|
||||
}
|
|
@ -1,19 +0,0 @@
|
|||
package code.name.monkey.retromusic.activities.albums
|
||||
|
||||
import android.app.Application
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
|
||||
class AlbumDetailsViewModelFactory(
|
||||
private val application: Application,
|
||||
private val albumId: Int
|
||||
) :
|
||||
ViewModelProvider.AndroidViewModelFactory(application) {
|
||||
override fun <T : ViewModel?> create(modelClass: Class<T>): T {
|
||||
return if (modelClass.isAssignableFrom(AlbumDetailsViewModel::class.java)) {
|
||||
AlbumDetailsViewModel(application, albumId) as T
|
||||
} else {
|
||||
throw IllegalArgumentException("ViewModel Not Found")
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,333 +0,0 @@
|
|||
package code.name.monkey.retromusic.activities.artists
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import android.text.Spanned
|
||||
import android.transition.Slide
|
||||
import android.view.Menu
|
||||
import android.view.MenuItem
|
||||
import android.view.View
|
||||
import android.widget.Toast
|
||||
import androidx.core.app.ActivityCompat
|
||||
import androidx.core.text.HtmlCompat
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import androidx.recyclerview.widget.DefaultItemAnimator
|
||||
import androidx.recyclerview.widget.GridLayoutManager
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import code.name.monkey.appthemehelper.util.ATHUtil
|
||||
import code.name.monkey.appthemehelper.util.MaterialUtil
|
||||
import code.name.monkey.retromusic.R
|
||||
import code.name.monkey.retromusic.activities.base.AbsSlidingMusicPanelActivity
|
||||
import code.name.monkey.retromusic.adapter.album.HorizontalAlbumAdapter
|
||||
import code.name.monkey.retromusic.adapter.song.SimpleSongAdapter
|
||||
import code.name.monkey.retromusic.dialogs.AddToPlaylistDialog
|
||||
import code.name.monkey.retromusic.extensions.extraNotNull
|
||||
import code.name.monkey.retromusic.extensions.ripAlpha
|
||||
import code.name.monkey.retromusic.extensions.show
|
||||
import code.name.monkey.retromusic.extensions.surfaceColor
|
||||
import code.name.monkey.retromusic.glide.ArtistGlideRequest
|
||||
import code.name.monkey.retromusic.glide.RetroMusicColoredTarget
|
||||
import code.name.monkey.retromusic.helper.MusicPlayerRemote
|
||||
import code.name.monkey.retromusic.interfaces.CabHolder
|
||||
import code.name.monkey.retromusic.model.Artist
|
||||
import code.name.monkey.retromusic.mvp.presenter.ArtistDetailsView
|
||||
import code.name.monkey.retromusic.rest.model.LastFmArtist
|
||||
import code.name.monkey.retromusic.util.*
|
||||
import code.name.monkey.retromusic.util.color.MediaNotificationProcessor
|
||||
import com.afollestad.materialcab.MaterialCab
|
||||
import com.bumptech.glide.Glide
|
||||
import kotlinx.android.synthetic.main.activity_artist_content.*
|
||||
import kotlinx.android.synthetic.main.activity_artist_details.*
|
||||
import java.util.*
|
||||
import kotlin.collections.ArrayList
|
||||
|
||||
class ArtistDetailActivity : AbsSlidingMusicPanelActivity(), ArtistDetailsView, CabHolder {
|
||||
override fun openCab(menuRes: Int, callback: MaterialCab.Callback): MaterialCab {
|
||||
cab?.let {
|
||||
if (it.isActive) it.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)
|
||||
return cab as MaterialCab
|
||||
}
|
||||
|
||||
private var cab: MaterialCab? = null
|
||||
private var biography: Spanned? = null
|
||||
private lateinit var artist: Artist
|
||||
private lateinit var songAdapter: SimpleSongAdapter
|
||||
private lateinit var albumAdapter: HorizontalAlbumAdapter
|
||||
private var forceDownload: Boolean = false
|
||||
private lateinit var viewModel: ArtistDetailsViewModel
|
||||
|
||||
override fun createContentView(): View {
|
||||
return wrapSlidingMusicPanel(R.layout.activity_artist_details)
|
||||
}
|
||||
|
||||
private fun windowEnterTransition() {
|
||||
val slide = Slide()
|
||||
slide.excludeTarget(R.id.appBarLayout, true)
|
||||
slide.excludeTarget(R.id.status_bar, true)
|
||||
slide.excludeTarget(android.R.id.statusBarBackground, true)
|
||||
slide.excludeTarget(android.R.id.navigationBarBackground, true)
|
||||
window.enterTransition = slide
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
setDrawUnderStatusBar()
|
||||
super.onCreate(savedInstanceState)
|
||||
setStatusbarColorAuto()
|
||||
setNavigationbarColorAuto()
|
||||
setTaskDescriptionColorAuto()
|
||||
setLightNavigationBar(true)
|
||||
setBottomBarVisibility(View.GONE)
|
||||
window.sharedElementsUseOverlay = true
|
||||
windowEnterTransition()
|
||||
|
||||
val artistId = extraNotNull<Int>(EXTRA_ARTIST_ID).value
|
||||
val viewModelFactory = ArtistDetailsViewModelFactory(application, artistId)
|
||||
viewModel =
|
||||
ViewModelProvider(this, viewModelFactory).get(ArtistDetailsViewModel::class.java)
|
||||
addMusicServiceEventListener(viewModel)
|
||||
viewModel.getArtist().observe(this, androidx.lifecycle.Observer {
|
||||
ActivityCompat.startPostponedEnterTransition(this@ArtistDetailActivity)
|
||||
artist(it)
|
||||
})
|
||||
viewModel.getArtistInfo().observe(this, androidx.lifecycle.Observer {
|
||||
artistInfo(it)
|
||||
})
|
||||
ActivityCompat.postponeEnterTransition(this)
|
||||
|
||||
setupRecyclerView()
|
||||
|
||||
playAction.apply {
|
||||
setOnClickListener { MusicPlayerRemote.openQueue(artist.songs, 0, true) }
|
||||
}
|
||||
shuffleAction.apply {
|
||||
setOnClickListener { MusicPlayerRemote.openAndShuffleQueue(artist.songs, true) }
|
||||
}
|
||||
|
||||
biographyText.setOnClickListener {
|
||||
if (biographyText.maxLines == 4) {
|
||||
biographyText.maxLines = Integer.MAX_VALUE
|
||||
} else {
|
||||
biographyText.maxLines = 4
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun setupRecyclerView() {
|
||||
albumAdapter = HorizontalAlbumAdapter(this, ArrayList(), null)
|
||||
albumRecyclerView.apply {
|
||||
itemAnimator = DefaultItemAnimator()
|
||||
layoutManager = GridLayoutManager(this.context, 1, GridLayoutManager.HORIZONTAL, false)
|
||||
adapter = albumAdapter
|
||||
}
|
||||
songAdapter = SimpleSongAdapter(this, ArrayList(), R.layout.item_song, this)
|
||||
recyclerView.apply {
|
||||
itemAnimator = DefaultItemAnimator()
|
||||
layoutManager = LinearLayoutManager(this.context)
|
||||
adapter = songAdapter
|
||||
}
|
||||
}
|
||||
|
||||
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
||||
super.onActivityResult(requestCode, resultCode, data)
|
||||
when (requestCode) {
|
||||
REQUEST_CODE_SELECT_IMAGE -> if (resultCode == Activity.RESULT_OK) {
|
||||
data?.data?.let {
|
||||
CustomArtistImageUtil.getInstance(this).setCustomArtistImage(artist, it)
|
||||
}
|
||||
}
|
||||
else -> if (resultCode == Activity.RESULT_OK) {
|
||||
//reload()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun showEmptyView() {
|
||||
}
|
||||
|
||||
override fun complete() {
|
||||
ActivityCompat.startPostponedEnterTransition(this)
|
||||
}
|
||||
|
||||
override fun artist(artist: Artist) {
|
||||
complete()
|
||||
if (artist.songCount <= 0) {
|
||||
finish()
|
||||
}
|
||||
this.artist = artist
|
||||
loadArtistImage()
|
||||
|
||||
if (RetroUtil.isAllowedToDownloadMetadata(this)) {
|
||||
loadBiography(artist.name)
|
||||
}
|
||||
artistTitle.text = artist.name
|
||||
text.text = String.format(
|
||||
"%s • %s",
|
||||
MusicUtil.getArtistInfoString(this, artist),
|
||||
MusicUtil.getReadableDurationString(MusicUtil.getTotalDuration(artist.songs))
|
||||
)
|
||||
val songText =
|
||||
resources.getQuantityString(
|
||||
R.plurals.albumSongs,
|
||||
artist.songCount,
|
||||
artist.songCount
|
||||
)
|
||||
val albumText =
|
||||
resources.getQuantityString(
|
||||
R.plurals.albums,
|
||||
artist.songCount,
|
||||
artist.songCount
|
||||
)
|
||||
songTitle.text = songText
|
||||
albumTitle.text = albumText
|
||||
songAdapter.swapDataSet(artist.songs)
|
||||
albumAdapter.swapDataSet(artist.albums!!)
|
||||
}
|
||||
|
||||
private fun loadBiography(
|
||||
name: String,
|
||||
lang: String? = Locale.getDefault().language
|
||||
) {
|
||||
biography = null
|
||||
this.lang = lang
|
||||
viewModel.loadBiography(name, lang, null)
|
||||
}
|
||||
|
||||
override fun artistInfo(lastFmArtist: LastFmArtist?) {
|
||||
if (lastFmArtist != null && lastFmArtist.artist != null) {
|
||||
val bioContent = lastFmArtist.artist.bio.content
|
||||
if (bioContent != null && bioContent.trim { it <= ' ' }.isNotEmpty()) {
|
||||
biographyText.visibility = View.VISIBLE
|
||||
biographyTitle.visibility = View.VISIBLE
|
||||
biography = HtmlCompat.fromHtml(bioContent, HtmlCompat.FROM_HTML_MODE_LEGACY)
|
||||
biographyText.text = biography
|
||||
if (lastFmArtist.artist.stats.listeners.isNotEmpty()) {
|
||||
listeners.show()
|
||||
listenersLabel.show()
|
||||
scrobbles.show()
|
||||
scrobblesLabel.show()
|
||||
|
||||
listeners.text =
|
||||
RetroUtil.formatValue(lastFmArtist.artist.stats.listeners.toFloat())
|
||||
scrobbles.text =
|
||||
RetroUtil.formatValue(lastFmArtist.artist.stats.playcount.toFloat())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If the "lang" parameter is set and no biography is given, retry with default language
|
||||
if (biography == null && lang != null) {
|
||||
loadBiography(artist.name, null)
|
||||
}
|
||||
}
|
||||
|
||||
private var lang: String? = null
|
||||
|
||||
private fun loadArtistImage() {
|
||||
ArtistGlideRequest.Builder.from(Glide.with(this), artist).generatePalette(this).build()
|
||||
.dontAnimate().into(object : RetroMusicColoredTarget(image) {
|
||||
override fun onColorReady(colors: MediaNotificationProcessor) {
|
||||
setColors(colors)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
private fun setColors(color: MediaNotificationProcessor) {
|
||||
val buttonColor = if (PreferenceUtil.isAdaptiveColor)
|
||||
color.backgroundColor.ripAlpha()
|
||||
else
|
||||
ATHUtil.resolveColor(this, R.attr.colorSurface)
|
||||
|
||||
MaterialUtil.setTint(button = shuffleAction, color = buttonColor)
|
||||
MaterialUtil.setTint(button = playAction, color = buttonColor)
|
||||
|
||||
|
||||
toolbar.setBackgroundColor(surfaceColor())
|
||||
setSupportActionBar(toolbar)
|
||||
supportActionBar?.title = null
|
||||
}
|
||||
|
||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||
return handleSortOrderMenuItem(item)
|
||||
}
|
||||
|
||||
private fun handleSortOrderMenuItem(item: MenuItem): Boolean {
|
||||
val songs = artist.songs
|
||||
when (item.itemId) {
|
||||
android.R.id.home -> {
|
||||
super.onBackPressed()
|
||||
return true
|
||||
}
|
||||
R.id.action_play_next -> {
|
||||
MusicPlayerRemote.playNext(songs)
|
||||
return true
|
||||
}
|
||||
R.id.action_add_to_current_playing -> {
|
||||
MusicPlayerRemote.enqueue(songs)
|
||||
return true
|
||||
}
|
||||
R.id.action_add_to_playlist -> {
|
||||
AddToPlaylistDialog.create(songs).show(supportFragmentManager, "ADD_PLAYLIST")
|
||||
return true
|
||||
}
|
||||
R.id.action_set_artist_image -> {
|
||||
val intent = Intent(Intent.ACTION_GET_CONTENT)
|
||||
intent.type = "image/*"
|
||||
startActivityForResult(
|
||||
Intent.createChooser(intent, getString(R.string.pick_from_local_storage)),
|
||||
REQUEST_CODE_SELECT_IMAGE
|
||||
)
|
||||
return true
|
||||
}
|
||||
R.id.action_reset_artist_image -> {
|
||||
Toast.makeText(
|
||||
this@ArtistDetailActivity,
|
||||
resources.getString(R.string.updating),
|
||||
Toast.LENGTH_SHORT
|
||||
)
|
||||
.show()
|
||||
CustomArtistImageUtil.getInstance(this@ArtistDetailActivity)
|
||||
.resetCustomArtistImage(artist)
|
||||
forceDownload = true
|
||||
return true
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
override fun onCreateOptionsMenu(menu: Menu): Boolean {
|
||||
menuInflater.inflate(R.menu.menu_artist_detail, menu)
|
||||
return super.onCreateOptionsMenu(menu)
|
||||
}
|
||||
|
||||
override fun onBackPressed() {
|
||||
if (cab != null && cab!!.isActive) {
|
||||
cab?.finish()
|
||||
} else {
|
||||
super.onBackPressed()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
super.onDestroy()
|
||||
removeMusicServiceEventListener(viewModel)
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val EXTRA_ARTIST_ID = "extra_artist_id"
|
||||
const val REQUEST_CODE_SELECT_IMAGE = 9003
|
||||
}
|
||||
}
|
|
@ -1,59 +0,0 @@
|
|||
package code.name.monkey.retromusic.activities.artists
|
||||
|
||||
import android.app.Application
|
||||
import androidx.lifecycle.AndroidViewModel
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import code.name.monkey.retromusic.interfaces.MusicServiceEventListener
|
||||
import code.name.monkey.retromusic.model.Artist
|
||||
import code.name.monkey.retromusic.providers.RepositoryImpl
|
||||
import code.name.monkey.retromusic.rest.model.LastFmArtist
|
||||
import kotlinx.coroutines.Deferred
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.async
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
class ArtistDetailsViewModel(
|
||||
application: Application,
|
||||
private val artistId: Int
|
||||
) : AndroidViewModel(application), MusicServiceEventListener {
|
||||
|
||||
private val loadArtistDetailsAsync: Deferred<Artist?>
|
||||
get() = viewModelScope.async(Dispatchers.IO) {
|
||||
_repository.artistById(artistId)
|
||||
}
|
||||
private val _repository = RepositoryImpl(application.applicationContext)
|
||||
private val _artist = MutableLiveData<Artist>()
|
||||
private val _lastFmArtist = MutableLiveData<LastFmArtist>()
|
||||
|
||||
fun getArtist(): LiveData<Artist> = _artist
|
||||
fun getArtistInfo(): LiveData<LastFmArtist> = _lastFmArtist
|
||||
|
||||
init {
|
||||
loadArtistDetails()
|
||||
}
|
||||
|
||||
private fun loadArtistDetails() = viewModelScope.launch {
|
||||
val artist =
|
||||
loadArtistDetailsAsync.await() ?: throw NullPointerException("Album couldn't found")
|
||||
_artist.postValue(artist)
|
||||
}
|
||||
|
||||
fun loadBiography(name: String, lang: String?, cache: String?) = viewModelScope.launch {
|
||||
val info = _repository.artistInfo(name, lang, cache)
|
||||
_lastFmArtist.postValue(info)
|
||||
}
|
||||
|
||||
override fun onMediaStoreChanged() {
|
||||
loadArtistDetails()
|
||||
}
|
||||
|
||||
override fun onServiceConnected() {}
|
||||
override fun onServiceDisconnected() {}
|
||||
override fun onQueueChanged() {}
|
||||
override fun onPlayingMetaChanged() {}
|
||||
override fun onPlayStateChanged() {}
|
||||
override fun onRepeatModeChanged() {}
|
||||
override fun onShuffleModeChanged() {}
|
||||
}
|
|
@ -1,19 +0,0 @@
|
|||
package code.name.monkey.retromusic.activities.artists
|
||||
|
||||
import android.app.Application
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
|
||||
class ArtistDetailsViewModelFactory(
|
||||
private val application: Application,
|
||||
private val artistId: Int
|
||||
) :
|
||||
ViewModelProvider.AndroidViewModelFactory(application) {
|
||||
override fun <T : ViewModel?> create(modelClass: Class<T>): T {
|
||||
return if (modelClass.isAssignableFrom(ArtistDetailsViewModel::class.java)) {
|
||||
ArtistDetailsViewModel(application, artistId) as T
|
||||
} else {
|
||||
throw IllegalArgumentException("ViewModel Not Found")
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,147 +0,0 @@
|
|||
package code.name.monkey.retromusic.activities.base
|
||||
|
||||
import android.Manifest
|
||||
import android.content.Intent
|
||||
import android.content.pm.PackageManager
|
||||
import android.media.AudioManager
|
||||
import android.net.Uri
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.provider.Settings
|
||||
import android.view.KeyEvent
|
||||
import android.view.View
|
||||
import androidx.core.app.ActivityCompat
|
||||
import code.name.monkey.appthemehelper.ThemeStore
|
||||
import code.name.monkey.retromusic.R
|
||||
import com.google.android.material.snackbar.Snackbar
|
||||
|
||||
abstract class AbsBaseActivity : AbsThemeActivity() {
|
||||
private var hadPermissions: Boolean = false
|
||||
private lateinit var permissions: Array<String>
|
||||
private var permissionDeniedMessage: String? = null
|
||||
|
||||
open fun getPermissionsToRequest(): Array<String> {
|
||||
return arrayOf()
|
||||
}
|
||||
|
||||
protected fun setPermissionDeniedMessage(message: String) {
|
||||
permissionDeniedMessage = message
|
||||
}
|
||||
|
||||
fun getPermissionDeniedMessage(): String {
|
||||
return if (permissionDeniedMessage == null) getString(R.string.permissions_denied) else permissionDeniedMessage!!
|
||||
}
|
||||
|
||||
private val snackBarContainer: View
|
||||
get() = window.decorView
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
volumeControlStream = AudioManager.STREAM_MUSIC
|
||||
permissions = getPermissionsToRequest()
|
||||
hadPermissions = hasPermissions()
|
||||
permissionDeniedMessage = null
|
||||
}
|
||||
|
||||
override fun onPostCreate(savedInstanceState: Bundle?) {
|
||||
super.onPostCreate(savedInstanceState)
|
||||
if (!hasPermissions()) {
|
||||
requestPermissions()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
val hasPermissions = hasPermissions()
|
||||
if (hasPermissions != hadPermissions) {
|
||||
hadPermissions = hasPermissions
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
onHasPermissionsChanged(hasPermissions)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected open fun onHasPermissionsChanged(hasPermissions: Boolean) {
|
||||
// implemented by sub classes
|
||||
println(hasPermissions)
|
||||
}
|
||||
|
||||
override fun dispatchKeyEvent(event: KeyEvent): Boolean {
|
||||
if (event.keyCode == KeyEvent.KEYCODE_MENU && event.action == KeyEvent.ACTION_UP) {
|
||||
showOverflowMenu()
|
||||
return true
|
||||
}
|
||||
return super.dispatchKeyEvent(event)
|
||||
}
|
||||
|
||||
private fun showOverflowMenu() {
|
||||
}
|
||||
|
||||
protected open fun requestPermissions() {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
requestPermissions(permissions, PERMISSION_REQUEST)
|
||||
}
|
||||
}
|
||||
|
||||
protected fun hasPermissions(): Boolean {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
for (permission in permissions) {
|
||||
if (checkSelfPermission(permission) != PackageManager.PERMISSION_GRANTED) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
override fun onRequestPermissionsResult(
|
||||
requestCode: Int,
|
||||
permissions: Array<String>,
|
||||
grantResults: IntArray
|
||||
) {
|
||||
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
|
||||
if (requestCode == PERMISSION_REQUEST) {
|
||||
for (grantResult in grantResults) {
|
||||
if (grantResult != PackageManager.PERMISSION_GRANTED) {
|
||||
if (ActivityCompat.shouldShowRequestPermissionRationale(
|
||||
this@AbsBaseActivity, Manifest.permission.WRITE_EXTERNAL_STORAGE
|
||||
)
|
||||
) {
|
||||
//User has deny from permission dialog
|
||||
Snackbar.make(
|
||||
snackBarContainer,
|
||||
permissionDeniedMessage!!,
|
||||
Snackbar.LENGTH_INDEFINITE
|
||||
)
|
||||
.setAction(code.name.monkey.retromusic.R.string.action_grant) { requestPermissions() }
|
||||
.setActionTextColor(ThemeStore.accentColor(this)).show()
|
||||
} else {
|
||||
// User has deny permission and checked never show permission dialog so you can redirect to Application settings page
|
||||
Snackbar.make(
|
||||
snackBarContainer,
|
||||
permissionDeniedMessage!!,
|
||||
Snackbar.LENGTH_INDEFINITE
|
||||
).setAction(code.name.monkey.retromusic.R.string.action_settings) {
|
||||
val intent = Intent()
|
||||
intent.action = Settings.ACTION_APPLICATION_DETAILS_SETTINGS
|
||||
val uri = Uri.fromParts(
|
||||
"package",
|
||||
this@AbsBaseActivity.packageName,
|
||||
null
|
||||
)
|
||||
intent.data = uri
|
||||
startActivity(intent)
|
||||
}.setActionTextColor(ThemeStore.accentColor(this)).show()
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
hadPermissions = true
|
||||
onHasPermissionsChanged(true)
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val PERMISSION_REQUEST = 100
|
||||
}
|
||||
}
|
|
@ -1,170 +0,0 @@
|
|||
package code.name.monkey.retromusic.activities.base
|
||||
|
||||
import android.Manifest
|
||||
import android.content.*
|
||||
import android.os.Bundle
|
||||
import android.os.IBinder
|
||||
import code.name.monkey.retromusic.R
|
||||
import code.name.monkey.retromusic.helper.MusicPlayerRemote
|
||||
import code.name.monkey.retromusic.interfaces.MusicServiceEventListener
|
||||
import code.name.monkey.retromusic.service.MusicService.*
|
||||
import java.lang.ref.WeakReference
|
||||
import java.util.*
|
||||
|
||||
abstract class AbsMusicServiceActivity : AbsBaseActivity(), MusicServiceEventListener {
|
||||
|
||||
private val mMusicServiceEventListeners = ArrayList<MusicServiceEventListener>()
|
||||
|
||||
private var serviceToken: MusicPlayerRemote.ServiceToken? = null
|
||||
private var musicStateReceiver: MusicStateReceiver? = null
|
||||
private var receiverRegistered: Boolean = false
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
serviceToken = MusicPlayerRemote.bindToService(this, object : ServiceConnection {
|
||||
override fun onServiceConnected(name: ComponentName, service: IBinder) {
|
||||
this@AbsMusicServiceActivity.onServiceConnected()
|
||||
}
|
||||
|
||||
override fun onServiceDisconnected(name: ComponentName) {
|
||||
this@AbsMusicServiceActivity.onServiceDisconnected()
|
||||
}
|
||||
})
|
||||
|
||||
setPermissionDeniedMessage(getString(R.string.permission_external_storage_denied))
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
super.onDestroy()
|
||||
MusicPlayerRemote.unbindFromService(serviceToken)
|
||||
if (receiverRegistered) {
|
||||
unregisterReceiver(musicStateReceiver)
|
||||
receiverRegistered = false
|
||||
}
|
||||
}
|
||||
|
||||
fun addMusicServiceEventListener(listener: MusicServiceEventListener?) {
|
||||
if (listener != null) {
|
||||
mMusicServiceEventListeners.add(listener)
|
||||
}
|
||||
}
|
||||
|
||||
fun removeMusicServiceEventListener(listener: MusicServiceEventListener?) {
|
||||
if (listener != null) {
|
||||
mMusicServiceEventListeners.remove(listener)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onServiceConnected() {
|
||||
if (!receiverRegistered) {
|
||||
musicStateReceiver = MusicStateReceiver(this)
|
||||
|
||||
val filter = IntentFilter()
|
||||
filter.addAction(PLAY_STATE_CHANGED)
|
||||
filter.addAction(SHUFFLE_MODE_CHANGED)
|
||||
filter.addAction(REPEAT_MODE_CHANGED)
|
||||
filter.addAction(META_CHANGED)
|
||||
filter.addAction(QUEUE_CHANGED)
|
||||
filter.addAction(MEDIA_STORE_CHANGED)
|
||||
filter.addAction(FAVORITE_STATE_CHANGED)
|
||||
|
||||
registerReceiver(musicStateReceiver, filter)
|
||||
|
||||
receiverRegistered = true
|
||||
}
|
||||
|
||||
for (listener in mMusicServiceEventListeners) {
|
||||
listener.onServiceConnected()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onServiceDisconnected() {
|
||||
if (receiverRegistered) {
|
||||
unregisterReceiver(musicStateReceiver)
|
||||
receiverRegistered = false
|
||||
}
|
||||
|
||||
for (listener in mMusicServiceEventListeners) {
|
||||
listener.onServiceDisconnected()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onPlayingMetaChanged() {
|
||||
for (listener in mMusicServiceEventListeners) {
|
||||
listener.onPlayingMetaChanged()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onQueueChanged() {
|
||||
for (listener in mMusicServiceEventListeners) {
|
||||
listener.onQueueChanged()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onPlayStateChanged() {
|
||||
for (listener in mMusicServiceEventListeners) {
|
||||
listener.onPlayStateChanged()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onMediaStoreChanged() {
|
||||
for (listener in mMusicServiceEventListeners) {
|
||||
listener.onMediaStoreChanged()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onRepeatModeChanged() {
|
||||
for (listener in mMusicServiceEventListeners) {
|
||||
listener.onRepeatModeChanged()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onShuffleModeChanged() {
|
||||
for (listener in mMusicServiceEventListeners) {
|
||||
listener.onShuffleModeChanged()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onHasPermissionsChanged(hasPermissions: Boolean) {
|
||||
super.onHasPermissionsChanged(hasPermissions)
|
||||
val intent = Intent(MEDIA_STORE_CHANGED)
|
||||
intent.putExtra(
|
||||
"from_permissions_changed",
|
||||
true
|
||||
) // just in case we need to know this at some point
|
||||
sendBroadcast(intent)
|
||||
println("sendBroadcast $hasPermissions")
|
||||
}
|
||||
|
||||
override fun getPermissionsToRequest(): Array<String> {
|
||||
return arrayOf(
|
||||
Manifest.permission.READ_EXTERNAL_STORAGE,
|
||||
Manifest.permission.WRITE_EXTERNAL_STORAGE,
|
||||
Manifest.permission.BLUETOOTH
|
||||
)
|
||||
}
|
||||
|
||||
private class MusicStateReceiver(activity: AbsMusicServiceActivity) : BroadcastReceiver() {
|
||||
|
||||
private val reference: WeakReference<AbsMusicServiceActivity> = WeakReference(activity)
|
||||
|
||||
override fun onReceive(context: Context, intent: Intent) {
|
||||
val action = intent.action
|
||||
val activity = reference.get()
|
||||
if (activity != null && action != null) {
|
||||
when (action) {
|
||||
FAVORITE_STATE_CHANGED, META_CHANGED -> activity.onPlayingMetaChanged()
|
||||
QUEUE_CHANGED -> activity.onQueueChanged()
|
||||
PLAY_STATE_CHANGED -> activity.onPlayStateChanged()
|
||||
REPEAT_MODE_CHANGED -> activity.onRepeatModeChanged()
|
||||
SHUFFLE_MODE_CHANGED -> activity.onShuffleModeChanged()
|
||||
MEDIA_STORE_CHANGED -> activity.onMediaStoreChanged()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
val TAG: String = AbsMusicServiceActivity::class.java.simpleName
|
||||
}
|
||||
}
|
|
@ -1,388 +0,0 @@
|
|||
package code.name.monkey.retromusic.activities.base
|
||||
|
||||
import android.animation.ValueAnimator
|
||||
import android.graphics.Color
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.view.ViewTreeObserver
|
||||
import android.widget.FrameLayout
|
||||
import androidx.annotation.LayoutRes
|
||||
import androidx.fragment.app.Fragment
|
||||
import code.name.monkey.appthemehelper.util.ATHUtil
|
||||
import code.name.monkey.appthemehelper.util.ColorUtil
|
||||
import code.name.monkey.retromusic.R
|
||||
import code.name.monkey.retromusic.RetroBottomSheetBehavior
|
||||
import code.name.monkey.retromusic.extensions.hide
|
||||
import code.name.monkey.retromusic.extensions.show
|
||||
import code.name.monkey.retromusic.fragments.MiniPlayerFragment
|
||||
import code.name.monkey.retromusic.fragments.NowPlayingScreen
|
||||
import code.name.monkey.retromusic.fragments.NowPlayingScreen.*
|
||||
import code.name.monkey.retromusic.fragments.base.AbsPlayerFragment
|
||||
import code.name.monkey.retromusic.fragments.player.adaptive.AdaptiveFragment
|
||||
import code.name.monkey.retromusic.fragments.player.blur.BlurPlayerFragment
|
||||
import code.name.monkey.retromusic.fragments.player.card.CardFragment
|
||||
import code.name.monkey.retromusic.fragments.player.cardblur.CardBlurFragment
|
||||
import code.name.monkey.retromusic.fragments.player.circle.CirclePlayerFragment
|
||||
import code.name.monkey.retromusic.fragments.player.classic.ClassicPlayerFragment
|
||||
import code.name.monkey.retromusic.fragments.player.color.ColorFragment
|
||||
import code.name.monkey.retromusic.fragments.player.fit.FitFragment
|
||||
import code.name.monkey.retromusic.fragments.player.flat.FlatPlayerFragment
|
||||
import code.name.monkey.retromusic.fragments.player.full.FullPlayerFragment
|
||||
import code.name.monkey.retromusic.fragments.player.gradient.GradientPlayerFragment
|
||||
import code.name.monkey.retromusic.fragments.player.material.MaterialFragment
|
||||
import code.name.monkey.retromusic.fragments.player.normal.PlayerFragment
|
||||
import code.name.monkey.retromusic.fragments.player.peak.PeakPlayerFragment
|
||||
import code.name.monkey.retromusic.fragments.player.plain.PlainPlayerFragment
|
||||
import code.name.monkey.retromusic.fragments.player.simple.SimplePlayerFragment
|
||||
import code.name.monkey.retromusic.fragments.player.tiny.TinyPlayerFragment
|
||||
import code.name.monkey.retromusic.helper.MusicPlayerRemote
|
||||
import code.name.monkey.retromusic.model.CategoryInfo
|
||||
import code.name.monkey.retromusic.util.DensityUtil
|
||||
import code.name.monkey.retromusic.util.PreferenceUtil
|
||||
import code.name.monkey.retromusic.views.BottomNavigationBarTinted
|
||||
import com.google.android.material.bottomsheet.BottomSheetBehavior
|
||||
import com.google.android.material.shape.MaterialShapeDrawable
|
||||
import com.google.android.material.shape.ShapeAppearanceModel
|
||||
import kotlinx.android.synthetic.main.sliding_music_panel_layout.*
|
||||
|
||||
abstract class AbsSlidingMusicPanelActivity : AbsMusicServiceActivity(),
|
||||
AbsPlayerFragment.Callbacks {
|
||||
companion object {
|
||||
val TAG: String = AbsSlidingMusicPanelActivity::class.java.simpleName
|
||||
}
|
||||
|
||||
private lateinit var behavior: RetroBottomSheetBehavior<FrameLayout>
|
||||
private var miniPlayerFragment: MiniPlayerFragment? = null
|
||||
private var playerFragment: AbsPlayerFragment? = null
|
||||
private var cps: NowPlayingScreen? = null
|
||||
private var navigationBarColor: Int = 0
|
||||
private var taskColor: Int = 0
|
||||
private var lightStatusBar: Boolean = false
|
||||
private var lightNavigationBar: Boolean = false
|
||||
private var navigationBarColorAnimator: ValueAnimator? = null
|
||||
protected abstract fun createContentView(): View
|
||||
private lateinit var shapeDrawable: MaterialShapeDrawable
|
||||
private val panelState: Int
|
||||
get() = behavior.state
|
||||
|
||||
private val bottomSheetCallbackList = object : BottomSheetBehavior.BottomSheetCallback() {
|
||||
|
||||
override fun onSlide(bottomSheet: View, slideOffset: Float) {
|
||||
setMiniPlayerAlphaProgress(slideOffset)
|
||||
dimBackground.show()
|
||||
dimBackground.alpha = slideOffset
|
||||
shapeDrawable.interpolation = 1 - slideOffset
|
||||
}
|
||||
|
||||
override fun onStateChanged(bottomSheet: View, newState: Int) {
|
||||
when (newState) {
|
||||
BottomSheetBehavior.STATE_EXPANDED -> {
|
||||
onPanelExpanded()
|
||||
}
|
||||
BottomSheetBehavior.STATE_COLLAPSED -> {
|
||||
onPanelCollapsed()
|
||||
dimBackground.hide()
|
||||
}
|
||||
else -> {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun getBottomSheetBehavior() = behavior
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(createContentView())
|
||||
chooseFragmentForTheme()
|
||||
setupSlidingUpPanel()
|
||||
|
||||
|
||||
|
||||
behavior = BottomSheetBehavior.from(slidingPanel) as RetroBottomSheetBehavior
|
||||
|
||||
val themeColor = ATHUtil.resolveColor(this, android.R.attr.windowBackground, Color.GRAY)
|
||||
dimBackground.setBackgroundColor(ColorUtil.withAlpha(themeColor, 0.5f))
|
||||
shapeDrawable = MaterialShapeDrawable(
|
||||
ShapeAppearanceModel.builder(
|
||||
this,
|
||||
R.style.ClassicThemeOverLay,
|
||||
0
|
||||
).build()
|
||||
)
|
||||
slidingPanel.background = shapeDrawable
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
if (cps != PreferenceUtil.nowPlayingScreen) {
|
||||
postRecreate()
|
||||
}
|
||||
behavior.addBottomSheetCallback(bottomSheetCallbackList)
|
||||
|
||||
if (behavior.state == BottomSheetBehavior.STATE_EXPANDED) {
|
||||
setMiniPlayerAlphaProgress(1f)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
super.onDestroy()
|
||||
behavior.removeBottomSheetCallback(bottomSheetCallbackList)
|
||||
if (navigationBarColorAnimator != null) navigationBarColorAnimator?.cancel() // just in case
|
||||
}
|
||||
|
||||
protected fun wrapSlidingMusicPanel(@LayoutRes resId: Int): View {
|
||||
val slidingMusicPanelLayout =
|
||||
layoutInflater.inflate(R.layout.sliding_music_panel_layout, null)
|
||||
val contentContainer =
|
||||
slidingMusicPanelLayout.findViewById<ViewGroup>(R.id.mainContentFrame)
|
||||
layoutInflater.inflate(resId, contentContainer)
|
||||
return slidingMusicPanelLayout
|
||||
}
|
||||
|
||||
private fun collapsePanel() {
|
||||
behavior.state = BottomSheetBehavior.STATE_COLLAPSED
|
||||
}
|
||||
|
||||
fun expandPanel() {
|
||||
behavior.state = BottomSheetBehavior.STATE_EXPANDED
|
||||
setMiniPlayerAlphaProgress(1f)
|
||||
}
|
||||
|
||||
private fun setMiniPlayerAlphaProgress(progress: Float) {
|
||||
if (miniPlayerFragment?.view == null) return
|
||||
val alpha = 1 - progress
|
||||
miniPlayerFragment?.view?.alpha = alpha
|
||||
// necessary to make the views below clickable
|
||||
miniPlayerFragment?.view?.visibility = if (alpha == 0f) View.GONE else View.VISIBLE
|
||||
|
||||
bottomNavigationView.translationY = progress * 500
|
||||
//bottomNavigationView.alpha = alpha
|
||||
}
|
||||
|
||||
open fun onPanelCollapsed() {
|
||||
// restore values
|
||||
super.setLightStatusbar(lightStatusBar)
|
||||
super.setTaskDescriptionColor(taskColor)
|
||||
super.setNavigationbarColor(navigationBarColor)
|
||||
super.setLightNavigationBar(lightNavigationBar)
|
||||
|
||||
|
||||
playerFragment?.setMenuVisibility(false)
|
||||
playerFragment?.userVisibleHint = false
|
||||
playerFragment?.onHide()
|
||||
}
|
||||
|
||||
open fun onPanelExpanded() {
|
||||
val playerFragmentColor = playerFragment!!.paletteColor
|
||||
super.setTaskDescriptionColor(playerFragmentColor)
|
||||
|
||||
playerFragment?.setMenuVisibility(true)
|
||||
playerFragment?.userVisibleHint = true
|
||||
playerFragment?.onShow()
|
||||
onPaletteColorChanged()
|
||||
}
|
||||
|
||||
private fun setupSlidingUpPanel() {
|
||||
slidingPanel.viewTreeObserver.addOnGlobalLayoutListener(object :
|
||||
ViewTreeObserver.OnGlobalLayoutListener {
|
||||
override fun onGlobalLayout() {
|
||||
slidingPanel.viewTreeObserver.removeOnGlobalLayoutListener(this)
|
||||
if (cps != Peak) {
|
||||
val params = slidingPanel.layoutParams as ViewGroup.LayoutParams
|
||||
params.height = ViewGroup.LayoutParams.MATCH_PARENT
|
||||
slidingPanel.layoutParams = params
|
||||
}
|
||||
when (panelState) {
|
||||
BottomSheetBehavior.STATE_EXPANDED -> onPanelExpanded()
|
||||
BottomSheetBehavior.STATE_COLLAPSED -> onPanelCollapsed()
|
||||
else -> playerFragment!!.onHide()
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fun getBottomNavigationView(): BottomNavigationBarTinted {
|
||||
return bottomNavigationView
|
||||
}
|
||||
|
||||
fun setBottomBarVisibility(visible: Int) {
|
||||
bottomNavigationView.visibility = visible
|
||||
hideBottomBar(MusicPlayerRemote.playingQueue.isEmpty())
|
||||
}
|
||||
|
||||
private fun hideBottomBar(hide: Boolean) {
|
||||
val heightOfBar = resources.getDimensionPixelSize(R.dimen.mini_player_height)
|
||||
val heightOfBarWithTabs =
|
||||
resources.getDimensionPixelSize(R.dimen.mini_player_height_expanded)
|
||||
|
||||
if (hide) {
|
||||
behavior.isHideable = true
|
||||
behavior.peekHeight = 0
|
||||
bottomNavigationView.elevation = DensityUtil.dip2px(this, 10f).toFloat()
|
||||
collapsePanel()
|
||||
} else {
|
||||
if (MusicPlayerRemote.playingQueue.isNotEmpty()) {
|
||||
slidingPanel.elevation = DensityUtil.dip2px(this, 10f).toFloat()
|
||||
bottomNavigationView.elevation = DensityUtil.dip2px(this, 10f).toFloat()
|
||||
behavior.isHideable = false
|
||||
behavior.peekHeight =
|
||||
if (bottomNavigationView.visibility == View.VISIBLE) {
|
||||
heightOfBarWithTabs
|
||||
} else {
|
||||
heightOfBar
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun chooseFragmentForTheme() {
|
||||
cps = PreferenceUtil.nowPlayingScreen
|
||||
val fragment: Fragment = when (cps) {
|
||||
Blur -> BlurPlayerFragment()
|
||||
Adaptive -> AdaptiveFragment()
|
||||
Normal -> PlayerFragment()
|
||||
Card -> CardFragment()
|
||||
BlurCard -> CardBlurFragment()
|
||||
Fit -> FitFragment()
|
||||
Flat -> FlatPlayerFragment()
|
||||
Full -> FullPlayerFragment()
|
||||
Plain -> PlainPlayerFragment()
|
||||
Simple -> SimplePlayerFragment()
|
||||
Material -> MaterialFragment()
|
||||
Color -> ColorFragment()
|
||||
Tiny -> TinyPlayerFragment()
|
||||
Peak -> PeakPlayerFragment()
|
||||
Circle -> CirclePlayerFragment()
|
||||
Classic -> ClassicPlayerFragment()
|
||||
Gradient -> GradientPlayerFragment()
|
||||
else -> PlayerFragment()
|
||||
} // must implement AbsPlayerFragment
|
||||
supportFragmentManager.beginTransaction()
|
||||
.replace(R.id.playerFragmentContainer, fragment)
|
||||
.commit()
|
||||
supportFragmentManager.executePendingTransactions()
|
||||
|
||||
playerFragment =
|
||||
supportFragmentManager.findFragmentById(R.id.playerFragmentContainer) as AbsPlayerFragment
|
||||
miniPlayerFragment =
|
||||
supportFragmentManager.findFragmentById(R.id.miniPlayerFragment) as MiniPlayerFragment
|
||||
miniPlayerFragment?.view?.setOnClickListener { expandPanel() }
|
||||
}
|
||||
|
||||
override fun onServiceConnected() {
|
||||
super.onServiceConnected()
|
||||
if (MusicPlayerRemote.playingQueue.isNotEmpty()) {
|
||||
slidingPanel.viewTreeObserver.addOnGlobalLayoutListener(object :
|
||||
ViewTreeObserver.OnGlobalLayoutListener {
|
||||
override fun onGlobalLayout() {
|
||||
slidingPanel.viewTreeObserver.removeOnGlobalLayoutListener(this)
|
||||
hideBottomBar(false)
|
||||
}
|
||||
})
|
||||
} // don't call hideBottomBar(true) here as it causes a bug with the SlidingUpPanelLayout
|
||||
}
|
||||
|
||||
override fun onQueueChanged() {
|
||||
super.onQueueChanged()
|
||||
hideBottomBar(MusicPlayerRemote.playingQueue.isEmpty())
|
||||
}
|
||||
|
||||
override fun onBackPressed() {
|
||||
if (!handleBackPress()) super.onBackPressed()
|
||||
}
|
||||
|
||||
open fun handleBackPress(): Boolean {
|
||||
if (behavior.peekHeight != 0 && playerFragment!!.onBackPressed()) return true
|
||||
if (panelState == BottomSheetBehavior.STATE_EXPANDED) {
|
||||
collapsePanel()
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
override fun onPaletteColorChanged() {
|
||||
if (panelState == BottomSheetBehavior.STATE_EXPANDED) {
|
||||
val paletteColor = playerFragment!!.paletteColor
|
||||
super.setTaskDescriptionColor(paletteColor)
|
||||
|
||||
val isColorLight = ColorUtil.isColorLight(paletteColor)
|
||||
|
||||
if (PreferenceUtil.isAdaptiveColor && (cps == Normal || cps == Flat)) {
|
||||
super.setLightNavigationBar(true)
|
||||
super.setLightStatusbar(isColorLight)
|
||||
} else if (cps == Card || cps == Blur || cps == BlurCard) {
|
||||
super.setLightStatusbar(false)
|
||||
super.setLightNavigationBar(true)
|
||||
super.setNavigationbarColor(Color.BLACK)
|
||||
} else if (cps == Color || cps == Tiny || cps == Gradient) {
|
||||
super.setNavigationbarColor(paletteColor)
|
||||
super.setLightNavigationBar(isColorLight)
|
||||
super.setLightStatusbar(isColorLight)
|
||||
} else if (cps == Full) {
|
||||
super.setNavigationbarColor(paletteColor)
|
||||
super.setLightNavigationBar(isColorLight)
|
||||
super.setLightStatusbar(false)
|
||||
} else if (cps == Classic) {
|
||||
super.setLightStatusbar(false)
|
||||
} else if (cps == Fit) {
|
||||
super.setLightStatusbar(false)
|
||||
} else {
|
||||
super.setLightStatusbar(
|
||||
ColorUtil.isColorLight(
|
||||
ATHUtil.resolveColor(
|
||||
this,
|
||||
android.R.attr.windowBackground
|
||||
)
|
||||
)
|
||||
)
|
||||
super.setLightNavigationBar(true)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun setLightStatusbar(enabled: Boolean) {
|
||||
lightStatusBar = enabled
|
||||
if (panelState == BottomSheetBehavior.STATE_COLLAPSED) {
|
||||
super.setLightStatusbar(enabled)
|
||||
}
|
||||
}
|
||||
|
||||
override fun setLightNavigationBar(enabled: Boolean) {
|
||||
lightNavigationBar = enabled
|
||||
if (panelState == BottomSheetBehavior.STATE_COLLAPSED) {
|
||||
super.setLightNavigationBar(enabled)
|
||||
}
|
||||
}
|
||||
|
||||
override fun setNavigationbarColor(color: Int) {
|
||||
navigationBarColor = color
|
||||
if (panelState == BottomSheetBehavior.STATE_COLLAPSED) {
|
||||
if (navigationBarColorAnimator != null) navigationBarColorAnimator!!.cancel()
|
||||
super.setNavigationbarColor(color)
|
||||
}
|
||||
}
|
||||
|
||||
override fun setTaskDescriptionColor(color: Int) {
|
||||
taskColor = color
|
||||
if (panelState == BottomSheetBehavior.STATE_COLLAPSED) {
|
||||
super.setTaskDescriptionColor(color)
|
||||
}
|
||||
}
|
||||
|
||||
fun updateTabs() {
|
||||
bottomNavigationView.menu.clear()
|
||||
val currentTabs: List<CategoryInfo> = PreferenceUtil.libraryCategory
|
||||
for (tab in currentTabs) {
|
||||
if (tab.visible) {
|
||||
val menu = tab.category
|
||||
bottomNavigationView.menu.add(0, menu.id, 0, menu.stringRes).setIcon(menu.icon)
|
||||
}
|
||||
}
|
||||
if (bottomNavigationView.menu.size() == 1) {
|
||||
bottomNavigationView.hide()
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,207 +0,0 @@
|
|||
package code.name.monkey.retromusic.activities.base
|
||||
|
||||
import android.content.Context
|
||||
import android.graphics.Color
|
||||
import android.os.Bundle
|
||||
import android.os.Handler
|
||||
import android.view.KeyEvent
|
||||
import android.view.View
|
||||
import android.view.WindowManager
|
||||
import androidx.annotation.ColorInt
|
||||
import androidx.appcompat.app.AppCompatDelegate.setDefaultNightMode
|
||||
import code.name.monkey.appthemehelper.ATH
|
||||
import code.name.monkey.appthemehelper.ThemeStore
|
||||
import code.name.monkey.appthemehelper.common.ATHToolbarActivity
|
||||
import code.name.monkey.appthemehelper.util.ATHUtil
|
||||
import code.name.monkey.appthemehelper.util.ColorUtil
|
||||
import code.name.monkey.appthemehelper.util.MaterialDialogsUtil
|
||||
import code.name.monkey.appthemehelper.util.VersionUtils
|
||||
import code.name.monkey.retromusic.LanguageContextWrapper
|
||||
import code.name.monkey.retromusic.R
|
||||
import code.name.monkey.retromusic.util.PreferenceUtil
|
||||
import code.name.monkey.retromusic.util.RetroUtil
|
||||
import code.name.monkey.retromusic.util.theme.ThemeManager
|
||||
import java.util.*
|
||||
|
||||
abstract class AbsThemeActivity : ATHToolbarActivity(), Runnable {
|
||||
|
||||
private val handler = Handler()
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
updateTheme()
|
||||
hideStatusBar()
|
||||
super.onCreate(savedInstanceState)
|
||||
setImmersiveFullscreen()
|
||||
registerSystemUiVisibility()
|
||||
toggleScreenOn()
|
||||
MaterialDialogsUtil.updateMaterialDialogsThemeSingleton(this)
|
||||
}
|
||||
|
||||
|
||||
private fun updateTheme() {
|
||||
setTheme(ThemeManager.getThemeResValue(this))
|
||||
setDefaultNightMode(ThemeManager.getNightMode(this))
|
||||
}
|
||||
|
||||
private fun toggleScreenOn() {
|
||||
if (PreferenceUtil.isScreenOnEnabled) {
|
||||
window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
|
||||
} else {
|
||||
window.clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onWindowFocusChanged(hasFocus: Boolean) {
|
||||
super.onWindowFocusChanged(hasFocus)
|
||||
if (hasFocus) {
|
||||
hideStatusBar()
|
||||
handler.removeCallbacks(this)
|
||||
handler.postDelayed(this, 300)
|
||||
} else {
|
||||
handler.removeCallbacks(this)
|
||||
}
|
||||
}
|
||||
|
||||
fun hideStatusBar() {
|
||||
hideStatusBar(PreferenceUtil.isFullScreenMode)
|
||||
}
|
||||
|
||||
private fun hideStatusBar(fullscreen: Boolean) {
|
||||
val statusBar = window.decorView.rootView.findViewById<View>(R.id.status_bar)
|
||||
if (statusBar != null) {
|
||||
statusBar.visibility = if (fullscreen) View.GONE else View.VISIBLE
|
||||
}
|
||||
}
|
||||
|
||||
fun setDrawUnderStatusBar() {
|
||||
RetroUtil.setAllowDrawUnderStatusBar(window)
|
||||
}
|
||||
|
||||
fun setDrawUnderNavigationBar() {
|
||||
RetroUtil.setAllowDrawUnderNavigationBar(window)
|
||||
}
|
||||
|
||||
/**
|
||||
* This will set the color of the view with the id "status_bar" on KitKat and Lollipop. On
|
||||
* Lollipop if no such view is found it will set the statusbar color using the native method.
|
||||
*
|
||||
* @param color the new statusbar color (will be shifted down on Lollipop and above)
|
||||
*/
|
||||
fun setStatusbarColor(color: Int) {
|
||||
val statusBar = window.decorView.rootView.findViewById<View>(R.id.status_bar)
|
||||
if (statusBar != null) {
|
||||
when {
|
||||
VersionUtils.hasMarshmallow() -> statusBar.setBackgroundColor(color)
|
||||
VersionUtils.hasLollipop() -> statusBar.setBackgroundColor(
|
||||
ColorUtil.darkenColor(
|
||||
color
|
||||
)
|
||||
)
|
||||
else -> statusBar.setBackgroundColor(color)
|
||||
}
|
||||
} else {
|
||||
when {
|
||||
VersionUtils.hasMarshmallow() -> window.statusBarColor = color
|
||||
else -> window.statusBarColor = ColorUtil.darkenColor(color)
|
||||
}
|
||||
}
|
||||
setLightStatusbarAuto(ATHUtil.resolveColor(this, R.attr.colorSurface))
|
||||
}
|
||||
|
||||
fun setStatusbarColorAuto() {
|
||||
// we don't want to use statusbar color because we are doing the color darkening on our own to support KitKat
|
||||
setStatusbarColor(ATHUtil.resolveColor(this, R.attr.colorSurface))
|
||||
setLightStatusbarAuto(ATHUtil.resolveColor(this, R.attr.colorSurface))
|
||||
}
|
||||
|
||||
open fun setTaskDescriptionColor(@ColorInt color: Int) {
|
||||
ATH.setTaskDescriptionColor(this, color)
|
||||
}
|
||||
|
||||
fun setTaskDescriptionColorAuto() {
|
||||
setTaskDescriptionColor(ATHUtil.resolveColor(this, R.attr.colorSurface))
|
||||
}
|
||||
|
||||
open fun setNavigationbarColor(color: Int) {
|
||||
if (ThemeStore.coloredNavigationBar(this)) {
|
||||
ATH.setNavigationbarColor(this, color)
|
||||
} else {
|
||||
ATH.setNavigationbarColor(this, Color.BLACK)
|
||||
}
|
||||
}
|
||||
|
||||
fun setNavigationbarColorAuto() {
|
||||
setNavigationbarColor(ATHUtil.resolveColor(this, R.attr.colorSurface))
|
||||
}
|
||||
|
||||
open fun setLightStatusbar(enabled: Boolean) {
|
||||
ATH.setLightStatusbar(this, enabled)
|
||||
}
|
||||
|
||||
fun setLightStatusbarAuto(bgColor: Int) {
|
||||
setLightStatusbar(ColorUtil.isColorLight(bgColor))
|
||||
}
|
||||
|
||||
open fun setLightNavigationBar(enabled: Boolean) {
|
||||
if (!ATHUtil.isWindowBackgroundDark(this) and ThemeStore.coloredNavigationBar(this)) {
|
||||
ATH.setLightNavigationbar(this, enabled)
|
||||
}
|
||||
}
|
||||
|
||||
private fun registerSystemUiVisibility() {
|
||||
val decorView = window.decorView
|
||||
decorView.setOnSystemUiVisibilityChangeListener { visibility ->
|
||||
if (visibility and View.SYSTEM_UI_FLAG_FULLSCREEN == 0) {
|
||||
setImmersiveFullscreen()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun unregisterSystemUiVisibility() {
|
||||
val decorView = window.decorView
|
||||
decorView.setOnSystemUiVisibilityChangeListener(null)
|
||||
}
|
||||
|
||||
private fun setImmersiveFullscreen() {
|
||||
val flags =
|
||||
(View.SYSTEM_UI_FLAG_LAYOUT_STABLE or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN or View.SYSTEM_UI_FLAG_HIDE_NAVIGATION or View.SYSTEM_UI_FLAG_FULLSCREEN or View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY)
|
||||
|
||||
if (PreferenceUtil.isFullScreenMode) {
|
||||
window.decorView.systemUiVisibility = flags
|
||||
}
|
||||
}
|
||||
|
||||
private fun exitFullscreen() {
|
||||
window.decorView.systemUiVisibility = View.SYSTEM_UI_FLAG_VISIBLE
|
||||
}
|
||||
|
||||
override fun run() {
|
||||
setImmersiveFullscreen()
|
||||
}
|
||||
|
||||
override fun onStop() {
|
||||
handler.removeCallbacks(this)
|
||||
super.onStop()
|
||||
}
|
||||
|
||||
public override fun onDestroy() {
|
||||
super.onDestroy()
|
||||
unregisterSystemUiVisibility()
|
||||
exitFullscreen()
|
||||
}
|
||||
|
||||
override fun onKeyDown(keyCode: Int, event: KeyEvent): Boolean {
|
||||
if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN || keyCode == KeyEvent.KEYCODE_VOLUME_UP) {
|
||||
handler.removeCallbacks(this)
|
||||
handler.postDelayed(this, 500)
|
||||
}
|
||||
return super.onKeyDown(keyCode, event)
|
||||
}
|
||||
|
||||
override fun attachBaseContext(newBase: Context?) {
|
||||
val code = PreferenceUtil.languageCode
|
||||
if (code != "auto") {
|
||||
super.attachBaseContext(LanguageContextWrapper.wrap(newBase, Locale(code)))
|
||||
} else super.attachBaseContext(newBase)
|
||||
}
|
||||
}
|
|
@ -1,329 +0,0 @@
|
|||
package code.name.monkey.retromusic.activities.bugreport
|
||||
|
||||
import android.app.Activity
|
||||
import android.app.Dialog
|
||||
import android.content.ClipData
|
||||
import android.content.ClipboardManager
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
import android.text.TextUtils
|
||||
import android.view.MenuItem
|
||||
import android.view.inputmethod.EditorInfo
|
||||
import android.widget.Toast
|
||||
import androidx.annotation.StringDef
|
||||
import androidx.annotation.StringRes
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import code.name.monkey.appthemehelper.ThemeStore
|
||||
import code.name.monkey.appthemehelper.util.ATHUtil
|
||||
import code.name.monkey.appthemehelper.util.MaterialUtil
|
||||
import code.name.monkey.appthemehelper.util.TintHelper
|
||||
import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper
|
||||
import code.name.monkey.retromusic.R
|
||||
import code.name.monkey.retromusic.activities.base.AbsThemeActivity
|
||||
import code.name.monkey.retromusic.activities.bugreport.model.DeviceInfo
|
||||
import code.name.monkey.retromusic.activities.bugreport.model.Report
|
||||
import code.name.monkey.retromusic.activities.bugreport.model.github.ExtraInfo
|
||||
import code.name.monkey.retromusic.activities.bugreport.model.github.GithubLogin
|
||||
import code.name.monkey.retromusic.activities.bugreport.model.github.GithubTarget
|
||||
import code.name.monkey.retromusic.misc.DialogAsyncTask
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import com.google.android.material.floatingactionbutton.FloatingActionButton
|
||||
import com.google.android.material.textfield.TextInputLayout
|
||||
import kotlinx.android.synthetic.main.activity_bug_report.*
|
||||
import kotlinx.android.synthetic.main.bug_report_card_device_info.*
|
||||
import kotlinx.android.synthetic.main.bug_report_card_report.*
|
||||
import org.eclipse.egit.github.core.Issue
|
||||
import org.eclipse.egit.github.core.client.GitHubClient
|
||||
import org.eclipse.egit.github.core.client.RequestException
|
||||
import org.eclipse.egit.github.core.service.IssueService
|
||||
import java.io.IOException
|
||||
|
||||
private const val RESULT_SUCCESS = "RESULT_OK"
|
||||
private const val RESULT_BAD_CREDENTIALS = "RESULT_BAD_CREDENTIALS"
|
||||
private const val RESULT_INVALID_TOKEN = "RESULT_INVALID_TOKEN"
|
||||
private const val RESULT_ISSUES_NOT_ENABLED = "RESULT_ISSUES_NOT_ENABLED"
|
||||
private const val RESULT_UNKNOWN = "RESULT_UNKNOWN"
|
||||
|
||||
@StringDef(
|
||||
RESULT_SUCCESS,
|
||||
RESULT_BAD_CREDENTIALS,
|
||||
RESULT_INVALID_TOKEN,
|
||||
RESULT_ISSUES_NOT_ENABLED,
|
||||
RESULT_UNKNOWN
|
||||
)
|
||||
@Retention(AnnotationRetention.SOURCE)
|
||||
private annotation class Result
|
||||
|
||||
open class BugReportActivity : AbsThemeActivity() {
|
||||
|
||||
private var deviceInfo: DeviceInfo? = null
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
setDrawUnderStatusBar()
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(R.layout.activity_bug_report)
|
||||
setStatusbarColorAuto()
|
||||
setNavigationbarColorAuto()
|
||||
setTaskDescriptionColorAuto()
|
||||
|
||||
initViews()
|
||||
|
||||
if (TextUtils.isEmpty(title)) setTitle(R.string.report_an_issue)
|
||||
|
||||
deviceInfo = DeviceInfo(this)
|
||||
airTextDeviceInfo.text = deviceInfo.toString()
|
||||
}
|
||||
|
||||
private fun initViews() {
|
||||
val accentColor = ThemeStore.accentColor(this)
|
||||
val primaryColor = ATHUtil.resolveColor(this, R.attr.colorSurface)
|
||||
toolbar.setBackgroundColor(primaryColor)
|
||||
setSupportActionBar(toolbar)
|
||||
ToolbarContentTintHelper.colorBackButton(toolbar)
|
||||
supportActionBar?.setDisplayHomeAsUpEnabled(true)
|
||||
TintHelper.setTintAuto(optionUseAccount, accentColor, false)
|
||||
optionUseAccount?.setOnClickListener {
|
||||
inputTitle.isEnabled = true
|
||||
inputDescription.isEnabled = true
|
||||
inputUsername.isEnabled = true
|
||||
inputPassword.isEnabled = true
|
||||
|
||||
optionAnonymous.isChecked = false
|
||||
sendFab.hide(object : FloatingActionButton.OnVisibilityChangedListener() {
|
||||
override fun onHidden(fab: FloatingActionButton?) {
|
||||
super.onHidden(fab)
|
||||
sendFab.setImageResource(R.drawable.ic_send_white_24dp)
|
||||
sendFab.show()
|
||||
}
|
||||
})
|
||||
}
|
||||
TintHelper.setTintAuto(optionAnonymous, accentColor, false)
|
||||
optionAnonymous.setOnClickListener {
|
||||
inputTitle.isEnabled = false
|
||||
inputDescription.isEnabled = false
|
||||
inputUsername.isEnabled = false
|
||||
inputPassword.isEnabled = false
|
||||
|
||||
optionUseAccount.isChecked = false
|
||||
sendFab.hide(object : FloatingActionButton.OnVisibilityChangedListener() {
|
||||
override fun onHidden(fab: FloatingActionButton?) {
|
||||
super.onHidden(fab)
|
||||
sendFab.setImageResource(R.drawable.ic_open_in_browser_white_24dp)
|
||||
sendFab.show()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
inputPassword.setOnEditorActionListener { _, actionId, _ ->
|
||||
if (actionId == EditorInfo.IME_ACTION_SEND) {
|
||||
reportIssue()
|
||||
return@setOnEditorActionListener true
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
airTextDeviceInfo.setOnClickListener { copyDeviceInfoToClipBoard() }
|
||||
|
||||
TintHelper.setTintAuto(sendFab, accentColor, true)
|
||||
sendFab.setOnClickListener { reportIssue() }
|
||||
|
||||
MaterialUtil.setTint(inputLayoutTitle, false)
|
||||
MaterialUtil.setTint(inputLayoutDescription, false)
|
||||
MaterialUtil.setTint(inputLayoutUsername, false)
|
||||
MaterialUtil.setTint(inputLayoutPassword, false)
|
||||
}
|
||||
|
||||
private fun reportIssue() {
|
||||
if (optionUseAccount.isChecked) {
|
||||
if (!validateInput()) return
|
||||
val username = inputUsername.text.toString()
|
||||
val password = inputPassword.text.toString()
|
||||
sendBugReport(GithubLogin(username, password))
|
||||
} else {
|
||||
copyDeviceInfoToClipBoard()
|
||||
|
||||
val i = Intent(Intent.ACTION_VIEW)
|
||||
i.data = Uri.parse(ISSUE_TRACKER_LINK)
|
||||
i.flags = Intent.FLAG_ACTIVITY_NEW_TASK
|
||||
startActivity(i)
|
||||
}
|
||||
}
|
||||
|
||||
private fun copyDeviceInfoToClipBoard() {
|
||||
val clipboard = getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
|
||||
val clip = ClipData.newPlainText(getString(R.string.device_info), deviceInfo?.toMarkdown())
|
||||
clipboard.setPrimaryClip(clip)
|
||||
Toast.makeText(
|
||||
this@BugReportActivity,
|
||||
R.string.copied_device_info_to_clipboard,
|
||||
Toast.LENGTH_LONG
|
||||
).show()
|
||||
}
|
||||
|
||||
private fun validateInput(): Boolean {
|
||||
var hasErrors = false
|
||||
|
||||
if (optionUseAccount.isChecked) {
|
||||
if (TextUtils.isEmpty(inputUsername.text)) {
|
||||
setError(inputLayoutUsername, R.string.bug_report_no_username)
|
||||
hasErrors = true
|
||||
} else {
|
||||
removeError(inputLayoutUsername)
|
||||
}
|
||||
|
||||
if (TextUtils.isEmpty(inputPassword.text)) {
|
||||
setError(inputLayoutPassword, R.string.bug_report_no_password)
|
||||
hasErrors = true
|
||||
} else {
|
||||
removeError(inputLayoutPassword)
|
||||
}
|
||||
}
|
||||
|
||||
if (TextUtils.isEmpty(inputTitle.text)) {
|
||||
setError(inputLayoutTitle, R.string.bug_report_no_title)
|
||||
hasErrors = true
|
||||
} else {
|
||||
removeError(inputLayoutTitle)
|
||||
}
|
||||
|
||||
if (TextUtils.isEmpty(inputDescription.text)) {
|
||||
setError(inputLayoutDescription, R.string.bug_report_no_description)
|
||||
hasErrors = true
|
||||
} else {
|
||||
removeError(inputLayoutDescription)
|
||||
}
|
||||
|
||||
return !hasErrors
|
||||
}
|
||||
|
||||
private fun setError(editTextLayout: TextInputLayout, @StringRes errorRes: Int) {
|
||||
editTextLayout.error = getString(errorRes)
|
||||
}
|
||||
|
||||
private fun removeError(editTextLayout: TextInputLayout) {
|
||||
editTextLayout.error = null
|
||||
}
|
||||
|
||||
private fun sendBugReport(login: GithubLogin) {
|
||||
if (!validateInput()) return
|
||||
|
||||
val bugTitle = inputTitle.text.toString()
|
||||
val bugDescription = inputDescription.text.toString()
|
||||
|
||||
val extraInfo = ExtraInfo()
|
||||
onSaveExtraInfo()
|
||||
|
||||
val report = Report(bugTitle, bugDescription, deviceInfo, extraInfo)
|
||||
val target = GithubTarget("h4h13", "RetroMusicPlayer")
|
||||
|
||||
ReportIssueAsyncTask.report(this, report, target, login)
|
||||
}
|
||||
|
||||
private fun onSaveExtraInfo() {}
|
||||
|
||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||
if (item.itemId == android.R.id.home) {
|
||||
onBackPressed()
|
||||
}
|
||||
return super.onOptionsItemSelected(item)
|
||||
}
|
||||
|
||||
private class ReportIssueAsyncTask private constructor(
|
||||
activity: Activity,
|
||||
private val report: Report,
|
||||
private val target: GithubTarget,
|
||||
private val login: GithubLogin
|
||||
) : DialogAsyncTask<Void, Void, String>(activity) {
|
||||
|
||||
override fun createDialog(context: Context): Dialog {
|
||||
return AlertDialog.Builder(context).show()
|
||||
}
|
||||
|
||||
@Result
|
||||
override fun doInBackground(vararg params: Void): String {
|
||||
val client: GitHubClient = if (login.shouldUseApiToken()) {
|
||||
GitHubClient().setOAuth2Token(login.apiToken)
|
||||
} else {
|
||||
GitHubClient().setCredentials(login.username, login.password)
|
||||
}
|
||||
|
||||
val issue = Issue().setTitle(report.title).setBody(report.description)
|
||||
try {
|
||||
IssueService(client).createIssue(target.username, target.repository, issue)
|
||||
return RESULT_SUCCESS
|
||||
} catch (e: RequestException) {
|
||||
return when (e.status) {
|
||||
STATUS_BAD_CREDENTIALS -> {
|
||||
if (login.shouldUseApiToken()) RESULT_INVALID_TOKEN else RESULT_BAD_CREDENTIALS
|
||||
}
|
||||
STATUS_ISSUES_NOT_ENABLED -> RESULT_ISSUES_NOT_ENABLED
|
||||
else -> {
|
||||
e.printStackTrace()
|
||||
RESULT_UNKNOWN
|
||||
}
|
||||
}
|
||||
} catch (e: IOException) {
|
||||
e.printStackTrace()
|
||||
return RESULT_UNKNOWN
|
||||
}
|
||||
}
|
||||
|
||||
override fun onPostExecute(@Result result: String) {
|
||||
super.onPostExecute(result)
|
||||
|
||||
val context = context ?: return
|
||||
|
||||
when (result) {
|
||||
RESULT_SUCCESS -> tryToFinishActivity()
|
||||
RESULT_BAD_CREDENTIALS -> MaterialAlertDialogBuilder(context)
|
||||
.setTitle(R.string.bug_report_failed)
|
||||
.setMessage(R.string.bug_report_failed_wrong_credentials)
|
||||
.setPositiveButton(android.R.string.ok, null)
|
||||
.show()
|
||||
RESULT_INVALID_TOKEN -> MaterialAlertDialogBuilder(context)
|
||||
.setTitle(R.string.bug_report_failed)
|
||||
.setMessage(R.string.bug_report_failed_invalid_token)
|
||||
.setPositiveButton(android.R.string.ok, null).show()
|
||||
RESULT_ISSUES_NOT_ENABLED -> MaterialAlertDialogBuilder(context)
|
||||
.setTitle(R.string.bug_report_failed)
|
||||
.setMessage(R.string.bug_report_failed_issues_not_available)
|
||||
.setPositiveButton(android.R.string.ok, null)
|
||||
|
||||
else -> MaterialAlertDialogBuilder(context)
|
||||
.setTitle(R.string.bug_report_failed)
|
||||
.setMessage(R.string.bug_report_failed_unknown)
|
||||
.setPositiveButton(android.R.string.ok) { _, _ -> tryToFinishActivity() }
|
||||
.setNegativeButton(android.R.string.cancel) { _, _ -> { tryToFinishActivity() } }
|
||||
}
|
||||
}
|
||||
|
||||
private fun tryToFinishActivity() {
|
||||
val context = context
|
||||
if (context is Activity && !context.isFinishing) {
|
||||
context.finish()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
companion object {
|
||||
|
||||
fun report(
|
||||
activity: Activity,
|
||||
report: Report,
|
||||
target: GithubTarget,
|
||||
login: GithubLogin
|
||||
) {
|
||||
ReportIssueAsyncTask(activity, report, target, login).execute()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
private const val STATUS_BAD_CREDENTIALS = 401
|
||||
private const val STATUS_ISSUES_NOT_ENABLED = 410
|
||||
private const val ISSUE_TRACKER_LINK = "https://github.com/h4h13/RetroMusicPlayer"
|
||||
}
|
||||
}
|
|
@ -1,130 +0,0 @@
|
|||
package code.name.monkey.retromusic.activities.bugreport.model;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.Context;
|
||||
import android.content.pm.PackageInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.os.Build;
|
||||
|
||||
import androidx.annotation.IntRange;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Locale;
|
||||
|
||||
import code.name.monkey.retromusic.util.PreferenceUtil;
|
||||
|
||||
public class DeviceInfo {
|
||||
|
||||
@SuppressLint("NewApi")
|
||||
@SuppressWarnings("deprecation")
|
||||
private final String[] abis = Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP ?
|
||||
Build.SUPPORTED_ABIS : new String[]{Build.CPU_ABI, Build.CPU_ABI2};
|
||||
|
||||
@SuppressLint("NewApi")
|
||||
private final String[] abis32Bits = Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP ?
|
||||
Build.SUPPORTED_32_BIT_ABIS : null;
|
||||
|
||||
@SuppressLint("NewApi")
|
||||
private final String[] abis64Bits = Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP ?
|
||||
Build.SUPPORTED_64_BIT_ABIS : null;
|
||||
|
||||
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;
|
||||
private final String selectedLang;
|
||||
|
||||
public DeviceInfo(Context context) {
|
||||
PackageInfo packageInfo;
|
||||
try {
|
||||
packageInfo = context.getPackageManager()
|
||||
.getPackageInfo(context.getPackageName(), 0);
|
||||
} catch (PackageManager.NameNotFoundException e) {
|
||||
packageInfo = null;
|
||||
}
|
||||
if (packageInfo != null) {
|
||||
versionCode = packageInfo.versionCode;
|
||||
versionName = packageInfo.versionName;
|
||||
} else {
|
||||
versionCode = -1;
|
||||
versionName = null;
|
||||
}
|
||||
baseTheme = PreferenceUtil.INSTANCE.getBaseTheme();
|
||||
nowPlayingTheme = context.getString(PreferenceUtil.INSTANCE.getNowPlayingScreen().getTitleRes());
|
||||
isAdaptive = PreferenceUtil.INSTANCE.isAdaptiveColor();
|
||||
selectedLang = PreferenceUtil.INSTANCE.getLanguageCode();
|
||||
}
|
||||
|
||||
public String toMarkdown() {
|
||||
return "Device info:\n"
|
||||
+ "---\n"
|
||||
+ "<table>\n"
|
||||
+ "<tr><td>App version</td><td>" + versionName + "</td></tr>\n"
|
||||
+ "<tr><td>App version code</td><td>" + versionCode + "</td></tr>\n"
|
||||
+ "<tr><td>Android build version</td><td>" + buildVersion + "</td></tr>\n"
|
||||
+ "<tr><td>Android release version</td><td>" + releaseVersion + "</td></tr>\n"
|
||||
+ "<tr><td>Android SDK version</td><td>" + sdkVersion + "</td></tr>\n"
|
||||
+ "<tr><td>Android build ID</td><td>" + buildID + "</td></tr>\n"
|
||||
+ "<tr><td>Device brand</td><td>" + brand + "</td></tr>\n"
|
||||
+ "<tr><td>Device manufacturer</td><td>" + manufacturer + "</td></tr>\n"
|
||||
+ "<tr><td>Device name</td><td>" + device + "</td></tr>\n"
|
||||
+ "<tr><td>Device model</td><td>" + model + "</td></tr>\n"
|
||||
+ "<tr><td>Device product name</td><td>" + product + "</td></tr>\n"
|
||||
+ "<tr><td>Device hardware name</td><td>" + hardware + "</td></tr>\n"
|
||||
+ "<tr><td>ABIs</td><td>" + Arrays.toString(abis) + "</td></tr>\n"
|
||||
+ "<tr><td>ABIs (32bit)</td><td>" + Arrays.toString(abis32Bits) + "</td></tr>\n"
|
||||
+ "<tr><td>ABIs (64bit)</td><td>" + Arrays.toString(abis64Bits) + "</td></tr>\n"
|
||||
+ "<tr><td>Language</td><td>" + selectedLang + "</td></tr>\n"
|
||||
+ "</table>\n";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "App version: " + versionName + "\n"
|
||||
+ "App version code: " + versionCode + "\n"
|
||||
+ "Android build version: " + buildVersion + "\n"
|
||||
+ "Android release version: " + releaseVersion + "\n"
|
||||
+ "Android SDK version: " + sdkVersion + "\n"
|
||||
+ "Android build ID: " + buildID + "\n"
|
||||
+ "Device brand: " + brand + "\n"
|
||||
+ "Device manufacturer: " + manufacturer + "\n"
|
||||
+ "Device name: " + device + "\n"
|
||||
+ "Device model: " + model + "\n"
|
||||
+ "Device product name: " + product + "\n"
|
||||
+ "Device hardware name: " + hardware + "\n"
|
||||
+ "ABIs: " + Arrays.toString(abis) + "\n"
|
||||
+ "ABIs (32bit): " + Arrays.toString(abis32Bits) + "\n"
|
||||
+ "ABIs (64bit): " + Arrays.toString(abis64Bits) + "\n"
|
||||
+ "Base theme: " + baseTheme + "\n"
|
||||
+ "Now playing theme: " + nowPlayingTheme + "\n"
|
||||
+ "Adaptive: " + isAdaptive + "\n"
|
||||
+ "System language: " + Locale.getDefault().toLanguageTag() + "\n"
|
||||
+ "In-App Language: " + selectedLang;
|
||||
}
|
||||
}
|
|
@ -1,33 +0,0 @@
|
|||
package code.name.monkey.retromusic.activities.bugreport.model;
|
||||
|
||||
|
||||
import code.name.monkey.retromusic.activities.bugreport.model.github.ExtraInfo;
|
||||
|
||||
public class Report {
|
||||
|
||||
private final String description;
|
||||
|
||||
private final DeviceInfo deviceInfo;
|
||||
|
||||
private final ExtraInfo extraInfo;
|
||||
|
||||
private final String title;
|
||||
|
||||
public Report(String title, String description, DeviceInfo deviceInfo, ExtraInfo extraInfo) {
|
||||
this.title = title;
|
||||
this.description = description;
|
||||
this.deviceInfo = deviceInfo;
|
||||
this.extraInfo = extraInfo;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return description + "\n\n"
|
||||
+ "-\n\n"
|
||||
+ deviceInfo.toMarkdown() + "\n\n"
|
||||
+ extraInfo.toMarkdown();
|
||||
}
|
||||
|
||||
public String getTitle() {
|
||||
return title;
|
||||
}
|
||||
}
|
|
@ -1,62 +0,0 @@
|
|||
package code.name.monkey.retromusic.activities.bugreport.model.github;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class ExtraInfo {
|
||||
|
||||
private final Map<String, String> extraInfo = new LinkedHashMap<>();
|
||||
|
||||
public void put(String key, String value) {
|
||||
extraInfo.put(key, value);
|
||||
}
|
||||
|
||||
public void put(String key, boolean value) {
|
||||
extraInfo.put(key, Boolean.toString(value));
|
||||
}
|
||||
|
||||
public void put(String key, double value) {
|
||||
extraInfo.put(key, Double.toString(value));
|
||||
}
|
||||
|
||||
public void put(String key, float value) {
|
||||
extraInfo.put(key, Float.toString(value));
|
||||
}
|
||||
|
||||
public void put(String key, long value) {
|
||||
extraInfo.put(key, Long.toString(value));
|
||||
}
|
||||
|
||||
public void put(String key, int value) {
|
||||
extraInfo.put(key, Integer.toString(value));
|
||||
}
|
||||
|
||||
public void put(String key, Object value) {
|
||||
extraInfo.put(key, String.valueOf(value));
|
||||
}
|
||||
|
||||
public void remove(String key) {
|
||||
extraInfo.remove(key);
|
||||
}
|
||||
|
||||
public String toMarkdown() {
|
||||
if (extraInfo.isEmpty()) {
|
||||
return "";
|
||||
}
|
||||
|
||||
StringBuilder output = new StringBuilder();
|
||||
output.append("Extra info:\n"
|
||||
+ "---\n"
|
||||
+ "<table>\n");
|
||||
for (String key : extraInfo.keySet()) {
|
||||
output.append("<tr><td>")
|
||||
.append(key)
|
||||
.append("</td><td>")
|
||||
.append(extraInfo.get(key))
|
||||
.append("</td></tr>\n");
|
||||
}
|
||||
output.append("</table>\n");
|
||||
|
||||
return output.toString();
|
||||
}
|
||||
}
|
|
@ -1,41 +0,0 @@
|
|||
package code.name.monkey.retromusic.activities.bugreport.model.github;
|
||||
|
||||
import android.text.TextUtils;
|
||||
|
||||
public class GithubLogin {
|
||||
|
||||
private final String apiToken;
|
||||
|
||||
private final String password;
|
||||
|
||||
private final String username;
|
||||
|
||||
public GithubLogin(String username, String password) {
|
||||
this.username = username;
|
||||
this.password = password;
|
||||
this.apiToken = null;
|
||||
}
|
||||
|
||||
public GithubLogin(String apiToken) {
|
||||
this.username = null;
|
||||
this.password = null;
|
||||
this.apiToken = apiToken;
|
||||
}
|
||||
|
||||
public String getApiToken() {
|
||||
return apiToken;
|
||||
}
|
||||
|
||||
public String getPassword() {
|
||||
return password;
|
||||
}
|
||||
|
||||
public String getUsername() {
|
||||
return username;
|
||||
}
|
||||
|
||||
public boolean shouldUseApiToken() {
|
||||
return TextUtils.isEmpty(username) || TextUtils.isEmpty(password);
|
||||
}
|
||||
|
||||
}
|
|
@ -1,21 +0,0 @@
|
|||
package code.name.monkey.retromusic.activities.bugreport.model.github;
|
||||
|
||||
public class GithubTarget {
|
||||
|
||||
private final String repository;
|
||||
|
||||
private final String username;
|
||||
|
||||
public GithubTarget(String username, String repository) {
|
||||
this.username = username;
|
||||
this.repository = repository;
|
||||
}
|
||||
|
||||
public String getRepository() {
|
||||
return repository;
|
||||
}
|
||||
|
||||
public String getUsername() {
|
||||
return username;
|
||||
}
|
||||
}
|
|
@ -1,72 +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.activities.saf;
|
||||
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.heinrichreimersoftware.materialintro.app.IntroActivity;
|
||||
import com.heinrichreimersoftware.materialintro.slide.SimpleSlide;
|
||||
|
||||
import code.name.monkey.retromusic.R;
|
||||
|
||||
/**
|
||||
* Created by hemanths on 2019-07-31.
|
||||
*/
|
||||
public class SAFGuideActivity extends IntroActivity {
|
||||
|
||||
public static final int REQUEST_CODE_SAF_GUIDE = 98;
|
||||
|
||||
@Override
|
||||
protected void onCreate(@Nullable Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
setButtonCtaVisible(false);
|
||||
setButtonNextVisible(false);
|
||||
setButtonBackVisible(false);
|
||||
|
||||
setButtonCtaTintMode(BUTTON_CTA_TINT_MODE_TEXT);
|
||||
|
||||
String title = String.format(getString(R.string.saf_guide_slide1_title), getString(R.string.app_name));
|
||||
|
||||
addSlide(new SimpleSlide.Builder()
|
||||
.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)
|
||||
.image(R.drawable.saf_guide_1)
|
||||
.background(R.color.md_deep_purple_300)
|
||||
.backgroundDark(R.color.md_deep_purple_400)
|
||||
.layout(R.layout.fragment_simple_slide_large_image)
|
||||
.build());
|
||||
addSlide(new SimpleSlide.Builder()
|
||||
.title(R.string.saf_guide_slide2_title)
|
||||
.description(R.string.saf_guide_slide2_description)
|
||||
.image(R.drawable.saf_guide_2)
|
||||
.background(R.color.md_deep_purple_500)
|
||||
.backgroundDark(R.color.md_deep_purple_600)
|
||||
.layout(R.layout.fragment_simple_slide_large_image)
|
||||
.build());
|
||||
addSlide(new SimpleSlide.Builder()
|
||||
.title(R.string.saf_guide_slide3_title)
|
||||
.description(R.string.saf_guide_slide3_description)
|
||||
.image(R.drawable.saf_guide_3)
|
||||
.background(R.color.md_deep_purple_700)
|
||||
.backgroundDark(R.color.md_deep_purple_800)
|
||||
.layout(R.layout.fragment_simple_slide_large_image)
|
||||
.build());
|
||||
}
|
||||
}
|
|
@ -1,415 +0,0 @@
|
|||
package code.name.monkey.retromusic.activities.tageditor
|
||||
|
||||
import android.app.Activity
|
||||
import android.app.SearchManager
|
||||
import android.content.Intent
|
||||
import android.content.res.ColorStateList
|
||||
import android.graphics.Bitmap
|
||||
import android.graphics.BitmapFactory
|
||||
import android.net.Uri
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.util.Log
|
||||
import android.view.MenuItem
|
||||
import android.view.View
|
||||
import android.view.animation.OvershootInterpolator
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import code.name.monkey.appthemehelper.ThemeStore
|
||||
import code.name.monkey.appthemehelper.util.ATHUtil
|
||||
import code.name.monkey.appthemehelper.util.ColorUtil
|
||||
import code.name.monkey.appthemehelper.util.MaterialValueHelper
|
||||
import code.name.monkey.appthemehelper.util.TintHelper
|
||||
import code.name.monkey.retromusic.R
|
||||
import code.name.monkey.retromusic.R.drawable
|
||||
import code.name.monkey.retromusic.activities.base.AbsBaseActivity
|
||||
import code.name.monkey.retromusic.activities.saf.SAFGuideActivity
|
||||
import code.name.monkey.retromusic.util.RetroUtil
|
||||
import code.name.monkey.retromusic.util.SAFUtil
|
||||
import com.google.android.material.button.MaterialButton
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import kotlinx.android.synthetic.main.activity_album_tag_editor.*
|
||||
import org.jaudiotagger.audio.AudioFile
|
||||
import org.jaudiotagger.audio.AudioFileIO
|
||||
import org.jaudiotagger.tag.FieldKey
|
||||
import java.io.File
|
||||
import java.util.*
|
||||
|
||||
abstract class AbsTagEditorActivity : AbsBaseActivity() {
|
||||
|
||||
protected var id: Int = 0
|
||||
private set
|
||||
private var paletteColorPrimary: Int = 0
|
||||
private var isInNoImageMode: Boolean = false
|
||||
private var songPaths: List<String>? = null
|
||||
lateinit var saveFab: MaterialButton
|
||||
|
||||
private var savedSongPaths: List<String>? = null
|
||||
private val currentSongPath: String? = null
|
||||
private var savedTags: Map<FieldKey, String>? = null
|
||||
private var savedArtworkInfo: ArtworkInfo? = null
|
||||
|
||||
protected val show: AlertDialog
|
||||
get() =
|
||||
MaterialAlertDialogBuilder(this)
|
||||
.setTitle(R.string.update_image)
|
||||
.setItems(items.toTypedArray()) { _, position ->
|
||||
when (position) {
|
||||
0 -> startImagePicker()
|
||||
1 -> searchImageOnWeb()
|
||||
2 -> deleteImage()
|
||||
}
|
||||
}
|
||||
.show()
|
||||
protected abstract val contentViewLayout: Int
|
||||
|
||||
internal val albumArtist: String?
|
||||
get() {
|
||||
return try {
|
||||
getAudioFile(songPaths!![0]).tagOrCreateAndSetDefault.getFirst(FieldKey.ALBUM_ARTIST)
|
||||
} catch (ignored: Exception) {
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
protected val songTitle: String?
|
||||
get() {
|
||||
return try {
|
||||
getAudioFile(songPaths!![0]).tagOrCreateAndSetDefault.getFirst(FieldKey.TITLE)
|
||||
} catch (ignored: Exception) {
|
||||
null
|
||||
}
|
||||
}
|
||||
protected val composer: String?
|
||||
get() {
|
||||
return try {
|
||||
getAudioFile(songPaths!![0]).tagOrCreateAndSetDefault.getFirst(FieldKey.COMPOSER)
|
||||
} catch (ignored: Exception) {
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
protected val albumTitle: String?
|
||||
get() {
|
||||
return try {
|
||||
getAudioFile(songPaths!![0]).tagOrCreateAndSetDefault.getFirst(FieldKey.ALBUM)
|
||||
} catch (ignored: Exception) {
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
protected val artistName: String?
|
||||
get() {
|
||||
return try {
|
||||
getAudioFile(songPaths!![0]).tagOrCreateAndSetDefault.getFirst(FieldKey.ARTIST)
|
||||
} catch (ignored: Exception) {
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
protected val albumArtistName: String?
|
||||
get() {
|
||||
return try {
|
||||
getAudioFile(songPaths!![0]).tagOrCreateAndSetDefault.getFirst(FieldKey.ALBUM_ARTIST)
|
||||
} catch (ignored: Exception) {
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
protected val genreName: String?
|
||||
get() {
|
||||
return try {
|
||||
getAudioFile(songPaths!![0]).tagOrCreateAndSetDefault.getFirst(FieldKey.GENRE)
|
||||
} catch (ignored: Exception) {
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
protected val songYear: String?
|
||||
get() {
|
||||
return try {
|
||||
getAudioFile(songPaths!![0]).tagOrCreateAndSetDefault.getFirst(FieldKey.YEAR)
|
||||
} catch (ignored: Exception) {
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
protected val trackNumber: String?
|
||||
get() {
|
||||
return try {
|
||||
getAudioFile(songPaths!![0]).tagOrCreateAndSetDefault.getFirst(FieldKey.TRACK)
|
||||
} catch (ignored: Exception) {
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
protected val lyrics: String?
|
||||
get() {
|
||||
return try {
|
||||
getAudioFile(songPaths!![0]).tagOrCreateAndSetDefault.getFirst(FieldKey.LYRICS)
|
||||
} catch (ignored: Exception) {
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
protected val albumArt: Bitmap?
|
||||
get() {
|
||||
try {
|
||||
val artworkTag = getAudioFile(songPaths!![0]).tagOrCreateAndSetDefault.firstArtwork
|
||||
if (artworkTag != null) {
|
||||
val artworkBinaryData = artworkTag.binaryData
|
||||
return BitmapFactory.decodeByteArray(
|
||||
artworkBinaryData,
|
||||
0,
|
||||
artworkBinaryData.size
|
||||
)
|
||||
}
|
||||
return null
|
||||
} catch (ignored: Exception) {
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(contentViewLayout)
|
||||
|
||||
saveFab = findViewById(R.id.saveTags)
|
||||
getIntentExtras()
|
||||
|
||||
songPaths = getSongPaths()
|
||||
if (songPaths!!.isEmpty()) {
|
||||
finish()
|
||||
return
|
||||
}
|
||||
|
||||
setUpViews()
|
||||
|
||||
setStatusbarColorAuto()
|
||||
setNavigationbarColorAuto()
|
||||
setTaskDescriptionColorAuto()
|
||||
}
|
||||
|
||||
private fun setUpViews() {
|
||||
setUpScrollView()
|
||||
setUpFab()
|
||||
setUpImageView()
|
||||
}
|
||||
|
||||
private fun setUpScrollView() {
|
||||
//observableScrollView.setScrollViewCallbacks(observableScrollViewCallbacks);
|
||||
}
|
||||
|
||||
private lateinit var items: List<String>
|
||||
|
||||
private fun setUpImageView() {
|
||||
loadCurrentImage()
|
||||
items = listOf(
|
||||
getString(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.remove_cover)
|
||||
)
|
||||
editorImage?.setOnClickListener { show }
|
||||
}
|
||||
|
||||
private fun startImagePicker() {
|
||||
val intent = Intent(Intent.ACTION_GET_CONTENT)
|
||||
intent.type = "image/*"
|
||||
startActivityForResult(
|
||||
Intent.createChooser(
|
||||
intent,
|
||||
getString(code.name.monkey.retromusic.R.string.pick_from_local_storage)
|
||||
), REQUEST_CODE_SELECT_IMAGE
|
||||
)
|
||||
}
|
||||
|
||||
protected abstract fun loadCurrentImage()
|
||||
|
||||
protected abstract fun searchImageOnWeb()
|
||||
|
||||
protected abstract fun deleteImage()
|
||||
|
||||
private fun setUpFab() {
|
||||
saveFab.backgroundTintList = ColorStateList.valueOf(ThemeStore.accentColor(this))
|
||||
ColorStateList.valueOf(
|
||||
MaterialValueHelper.getPrimaryTextColor(
|
||||
this,
|
||||
ColorUtil.isColorLight(
|
||||
ThemeStore.accentColor(
|
||||
this
|
||||
)
|
||||
)
|
||||
)
|
||||
).apply {
|
||||
saveFab.setTextColor(this)
|
||||
saveFab.iconTint = this
|
||||
}
|
||||
saveFab.apply {
|
||||
scaleX = 0f
|
||||
scaleY = 0f
|
||||
isEnabled = false
|
||||
setOnClickListener { save() }
|
||||
TintHelper.setTintAuto(this, ThemeStore.accentColor(this@AbsTagEditorActivity), true)
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract fun save()
|
||||
|
||||
private fun getIntentExtras() {
|
||||
val intentExtras = intent.extras
|
||||
if (intentExtras != null) {
|
||||
id = intentExtras.getInt(EXTRA_ID)
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract fun getSongPaths(): List<String>
|
||||
|
||||
protected fun searchWebFor(vararg keys: String) {
|
||||
val stringBuilder = StringBuilder()
|
||||
for (key in keys) {
|
||||
stringBuilder.append(key)
|
||||
stringBuilder.append(" ")
|
||||
}
|
||||
val intent = Intent(Intent.ACTION_WEB_SEARCH)
|
||||
intent.putExtra(SearchManager.QUERY, stringBuilder.toString())
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||
|
||||
startActivity(intent)
|
||||
}
|
||||
|
||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||
when (item.itemId) {
|
||||
android.R.id.home -> {
|
||||
super.onBackPressed()
|
||||
return true
|
||||
}
|
||||
}
|
||||
return super.onOptionsItemSelected(item)
|
||||
}
|
||||
|
||||
protected fun setNoImageMode() {
|
||||
isInNoImageMode = true
|
||||
imageContainer?.visibility = View.GONE
|
||||
editorImage?.visibility = View.GONE
|
||||
editorImage?.isEnabled = false
|
||||
|
||||
setColors(
|
||||
intent.getIntExtra(
|
||||
EXTRA_PALETTE,
|
||||
ATHUtil.resolveColor(this, R.attr.colorPrimary)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
protected fun dataChanged() {
|
||||
showFab()
|
||||
}
|
||||
|
||||
private fun showFab() {
|
||||
saveFab.animate().setDuration(500).setInterpolator(OvershootInterpolator()).scaleX(1f)
|
||||
.scaleY(1f).start()
|
||||
saveFab.isEnabled = true
|
||||
}
|
||||
|
||||
private fun hideFab() {
|
||||
saveFab.animate().setDuration(500).setInterpolator(OvershootInterpolator()).scaleX(0.0f)
|
||||
.scaleY(0.0f).start()
|
||||
saveFab.isEnabled = false
|
||||
}
|
||||
|
||||
protected fun setImageBitmap(bitmap: Bitmap?, bgColor: Int) {
|
||||
if (bitmap == null) {
|
||||
editorImage.setImageResource(drawable.default_audio_art)
|
||||
} else {
|
||||
editorImage.setImageBitmap(bitmap)
|
||||
}
|
||||
setColors(bgColor)
|
||||
}
|
||||
|
||||
protected open fun setColors(color: Int) {
|
||||
paletteColorPrimary = color
|
||||
}
|
||||
|
||||
protected fun writeValuesToFiles(
|
||||
fieldKeyValueMap: Map<FieldKey, String>, artworkInfo: ArtworkInfo?
|
||||
) {
|
||||
RetroUtil.hideSoftKeyboard(this)
|
||||
|
||||
hideFab()
|
||||
|
||||
savedSongPaths = getSongPaths()
|
||||
savedTags = fieldKeyValueMap
|
||||
savedArtworkInfo = artworkInfo
|
||||
|
||||
if (!SAFUtil.isSAFRequired(savedSongPaths)) {
|
||||
writeTags(savedSongPaths)
|
||||
} else {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||
if (SAFUtil.isSDCardAccessGranted(this)) {
|
||||
writeTags(savedSongPaths)
|
||||
} else {
|
||||
startActivityForResult(
|
||||
Intent(this, SAFGuideActivity::class.java),
|
||||
SAFGuideActivity.REQUEST_CODE_SAF_GUIDE
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun writeTags(paths: List<String>?) {
|
||||
WriteTagsAsyncTask(this).execute(
|
||||
WriteTagsAsyncTask.LoadingInfo(
|
||||
paths,
|
||||
savedTags,
|
||||
savedArtworkInfo
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
override fun onActivityResult(requestCode: Int, resultCode: Int, intent: Intent?) {
|
||||
super.onActivityResult(requestCode, resultCode, intent)
|
||||
when (requestCode) {
|
||||
REQUEST_CODE_SELECT_IMAGE -> if (resultCode == Activity.RESULT_OK) {
|
||||
intent?.data?.let {
|
||||
loadImageFromFile(it)
|
||||
}
|
||||
}
|
||||
SAFGuideActivity.REQUEST_CODE_SAF_GUIDE -> {
|
||||
SAFUtil.openTreePicker(this)
|
||||
}
|
||||
SAFUtil.REQUEST_SAF_PICK_TREE -> {
|
||||
if (resultCode == Activity.RESULT_OK) {
|
||||
SAFUtil.saveTreeUri(this, intent)
|
||||
writeTags(savedSongPaths)
|
||||
}
|
||||
}
|
||||
SAFUtil.REQUEST_SAF_PICK_FILE -> {
|
||||
if (resultCode == Activity.RESULT_OK) {
|
||||
writeTags(Collections.singletonList(currentSongPath + SAFUtil.SEPARATOR + intent!!.dataString))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract fun loadImageFromFile(selectedFile: Uri?)
|
||||
|
||||
private fun getAudioFile(path: String): AudioFile {
|
||||
return try {
|
||||
AudioFileIO.read(File(path))
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "Could not read audio file $path", e)
|
||||
AudioFile()
|
||||
}
|
||||
}
|
||||
|
||||
class ArtworkInfo constructor(val albumId: Int, val artwork: Bitmap?)
|
||||
|
||||
companion object {
|
||||
|
||||
const val EXTRA_ID = "extra_id"
|
||||
const val EXTRA_PALETTE = "extra_palette"
|
||||
private val TAG = AbsTagEditorActivity::class.java.simpleName
|
||||
private const val REQUEST_CODE_SELECT_IMAGE = 1000
|
||||
}
|
||||
|
||||
}
|
|
@ -1,198 +0,0 @@
|
|||
package code.name.monkey.retromusic.activities.tageditor
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.res.ColorStateList
|
||||
import android.graphics.Bitmap
|
||||
import android.graphics.BitmapFactory
|
||||
import android.graphics.Color
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
import android.text.Editable
|
||||
import android.text.TextWatcher
|
||||
import android.transition.Slide
|
||||
import android.widget.Toast
|
||||
import code.name.monkey.appthemehelper.util.ATHUtil
|
||||
import code.name.monkey.appthemehelper.util.MaterialUtil
|
||||
import code.name.monkey.retromusic.R
|
||||
import code.name.monkey.retromusic.extensions.appHandleColor
|
||||
import code.name.monkey.retromusic.glide.palette.BitmapPaletteTranscoder
|
||||
import code.name.monkey.retromusic.glide.palette.BitmapPaletteWrapper
|
||||
import code.name.monkey.retromusic.loaders.AlbumLoader
|
||||
import code.name.monkey.retromusic.util.ImageUtil
|
||||
import code.name.monkey.retromusic.util.RetroColorUtil.generatePalette
|
||||
import code.name.monkey.retromusic.util.RetroColorUtil.getColor
|
||||
import com.bumptech.glide.Glide
|
||||
import com.bumptech.glide.load.engine.DiskCacheStrategy
|
||||
import com.bumptech.glide.request.animation.GlideAnimation
|
||||
import com.bumptech.glide.request.target.SimpleTarget
|
||||
import kotlinx.android.synthetic.main.activity_album_tag_editor.*
|
||||
import org.jaudiotagger.tag.FieldKey
|
||||
import java.util.*
|
||||
|
||||
class AlbumTagEditorActivity : AbsTagEditorActivity(), TextWatcher {
|
||||
override val contentViewLayout: Int
|
||||
get() = R.layout.activity_album_tag_editor
|
||||
|
||||
private fun windowEnterTransition() {
|
||||
val slide = Slide()
|
||||
slide.excludeTarget(R.id.appBarLayout, true)
|
||||
slide.excludeTarget(R.id.status_bar, true)
|
||||
slide.excludeTarget(android.R.id.statusBarBackground, true)
|
||||
slide.excludeTarget(android.R.id.navigationBarBackground, true)
|
||||
|
||||
window.enterTransition = slide
|
||||
}
|
||||
|
||||
override fun loadImageFromFile(selectedFileUri: Uri?) {
|
||||
|
||||
Glide.with(this@AlbumTagEditorActivity).load(selectedFileUri).asBitmap()
|
||||
.transcode(BitmapPaletteTranscoder(this), BitmapPaletteWrapper::class.java)
|
||||
.diskCacheStrategy(DiskCacheStrategy.NONE).skipMemoryCache(true)
|
||||
.into(object : SimpleTarget<BitmapPaletteWrapper>() {
|
||||
override fun onResourceReady(
|
||||
resource: BitmapPaletteWrapper?,
|
||||
glideAnimation: GlideAnimation<in BitmapPaletteWrapper>?
|
||||
) {
|
||||
getColor(resource?.palette, Color.TRANSPARENT)
|
||||
albumArtBitmap = resource?.bitmap?.let { ImageUtil.resizeBitmap(it, 2048) }
|
||||
setImageBitmap(
|
||||
albumArtBitmap,
|
||||
getColor(
|
||||
resource?.palette,
|
||||
ATHUtil.resolveColor(
|
||||
this@AlbumTagEditorActivity,
|
||||
R.attr.defaultFooterColor
|
||||
)
|
||||
)
|
||||
)
|
||||
deleteAlbumArt = false
|
||||
dataChanged()
|
||||
setResult(Activity.RESULT_OK)
|
||||
}
|
||||
|
||||
override fun onLoadFailed(e: Exception?, errorDrawable: Drawable?) {
|
||||
super.onLoadFailed(e, errorDrawable)
|
||||
Toast.makeText(this@AlbumTagEditorActivity, e.toString(), Toast.LENGTH_LONG)
|
||||
.show()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
private var albumArtBitmap: Bitmap? = null
|
||||
private var deleteAlbumArt: Boolean = false
|
||||
|
||||
private fun setupToolbar() {
|
||||
toolbar.setBackgroundColor(ATHUtil.resolveColor(this, R.attr.colorSurface))
|
||||
setSupportActionBar(toolbar)
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
setDrawUnderStatusBar()
|
||||
super.onCreate(savedInstanceState)
|
||||
window.sharedElementsUseOverlay = true
|
||||
imageContainer?.transitionName = getString(R.string.transition_album_art)
|
||||
windowEnterTransition()
|
||||
setUpViews()
|
||||
setupToolbar()
|
||||
}
|
||||
|
||||
private fun setUpViews() {
|
||||
fillViewsWithFileTags()
|
||||
|
||||
MaterialUtil.setTint(yearContainer, false)
|
||||
MaterialUtil.setTint(genreContainer, false)
|
||||
MaterialUtil.setTint(albumTitleContainer, false)
|
||||
MaterialUtil.setTint(albumArtistContainer, false)
|
||||
|
||||
albumText.appHandleColor().addTextChangedListener(this)
|
||||
albumArtistText.appHandleColor().addTextChangedListener(this)
|
||||
genreTitle.appHandleColor().addTextChangedListener(this)
|
||||
yearTitle.appHandleColor().addTextChangedListener(this)
|
||||
}
|
||||
|
||||
private fun fillViewsWithFileTags() {
|
||||
albumText.setText(albumTitle)
|
||||
albumArtistText.setText(albumArtistName)
|
||||
genreTitle.setText(genreName)
|
||||
yearTitle.setText(songYear)
|
||||
}
|
||||
|
||||
override fun loadCurrentImage() {
|
||||
val bitmap = albumArt
|
||||
setImageBitmap(
|
||||
bitmap,
|
||||
getColor(
|
||||
generatePalette(bitmap),
|
||||
ATHUtil.resolveColor(this, R.attr.defaultFooterColor)
|
||||
)
|
||||
)
|
||||
deleteAlbumArt = false
|
||||
}
|
||||
|
||||
private fun toastLoadingFailed() {
|
||||
Toast.makeText(
|
||||
this@AlbumTagEditorActivity,
|
||||
R.string.could_not_download_album_cover,
|
||||
Toast.LENGTH_SHORT
|
||||
).show()
|
||||
}
|
||||
|
||||
override fun searchImageOnWeb() {
|
||||
searchWebFor(albumText.text.toString(), albumArtistText.text.toString())
|
||||
}
|
||||
|
||||
override fun deleteImage() {
|
||||
setImageBitmap(
|
||||
BitmapFactory.decodeResource(resources, R.drawable.default_audio_art),
|
||||
ATHUtil.resolveColor(this, R.attr.defaultFooterColor)
|
||||
)
|
||||
deleteAlbumArt = true
|
||||
dataChanged()
|
||||
}
|
||||
|
||||
override fun save() {
|
||||
val fieldKeyValueMap = EnumMap<FieldKey, String>(FieldKey::class.java)
|
||||
fieldKeyValueMap[FieldKey.ALBUM] = albumText.text.toString()
|
||||
//android seems not to recognize album_artist field so we additionally write the normal artist field
|
||||
fieldKeyValueMap[FieldKey.ARTIST] = albumArtistText.text.toString()
|
||||
fieldKeyValueMap[FieldKey.ALBUM_ARTIST] = albumArtistText.text.toString()
|
||||
fieldKeyValueMap[FieldKey.GENRE] = genreTitle.text.toString()
|
||||
fieldKeyValueMap[FieldKey.YEAR] = yearTitle.text.toString()
|
||||
|
||||
writeValuesToFiles(
|
||||
fieldKeyValueMap,
|
||||
if (deleteAlbumArt) AbsTagEditorActivity.ArtworkInfo(id, null)
|
||||
else if (albumArtBitmap == null) null else ArtworkInfo(id, albumArtBitmap!!)
|
||||
)
|
||||
}
|
||||
|
||||
override fun getSongPaths(): List<String> {
|
||||
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 setColors(color: Int) {
|
||||
super.setColors(color)
|
||||
saveFab.backgroundTintList = ColorStateList.valueOf(color)
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
val TAG: String = AlbumTagEditorActivity::class.java.simpleName
|
||||
}
|
||||
}
|
|
@ -1,112 +0,0 @@
|
|||
package code.name.monkey.retromusic.activities.tageditor
|
||||
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
import android.text.Editable
|
||||
import android.text.TextWatcher
|
||||
import code.name.monkey.appthemehelper.util.ATHUtil
|
||||
import code.name.monkey.appthemehelper.util.MaterialUtil
|
||||
import code.name.monkey.retromusic.R
|
||||
import code.name.monkey.retromusic.extensions.appHandleColor
|
||||
import code.name.monkey.retromusic.loaders.SongLoader
|
||||
import kotlinx.android.synthetic.main.activity_song_tag_editor.*
|
||||
import org.jaudiotagger.tag.FieldKey
|
||||
import java.util.*
|
||||
|
||||
class SongTagEditorActivity : AbsTagEditorActivity(), TextWatcher {
|
||||
|
||||
override val contentViewLayout: Int
|
||||
get() = R.layout.activity_song_tag_editor
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
setNoImageMode()
|
||||
setUpViews()
|
||||
toolbar.setBackgroundColor(ATHUtil.resolveColor(this, R.attr.colorSurface))
|
||||
setSupportActionBar(toolbar)
|
||||
}
|
||||
|
||||
private fun setUpViews() {
|
||||
fillViewsWithFileTags()
|
||||
MaterialUtil.setTint(songTextContainer, false)
|
||||
MaterialUtil.setTint(composerContainer, false)
|
||||
MaterialUtil.setTint(albumTextContainer, false)
|
||||
MaterialUtil.setTint(artistContainer, false)
|
||||
MaterialUtil.setTint(albumArtistContainer, false)
|
||||
MaterialUtil.setTint(yearContainer, false)
|
||||
MaterialUtil.setTint(genreContainer, false)
|
||||
MaterialUtil.setTint(trackNumberContainer, false)
|
||||
MaterialUtil.setTint(lyricsContainer, false)
|
||||
|
||||
songText.appHandleColor().addTextChangedListener(this)
|
||||
albumText.appHandleColor().addTextChangedListener(this)
|
||||
albumArtistText.appHandleColor().addTextChangedListener(this)
|
||||
artistText.appHandleColor().addTextChangedListener(this)
|
||||
genreText.appHandleColor().addTextChangedListener(this)
|
||||
yearText.appHandleColor().addTextChangedListener(this)
|
||||
trackNumberText.appHandleColor().addTextChangedListener(this)
|
||||
lyricsText.appHandleColor().addTextChangedListener(this)
|
||||
songComposerText.appHandleColor().addTextChangedListener(this)
|
||||
}
|
||||
|
||||
private fun fillViewsWithFileTags() {
|
||||
songText.setText(songTitle)
|
||||
albumArtistText.setText(albumArtist)
|
||||
albumText.setText(albumTitle)
|
||||
artistText.setText(artistName)
|
||||
genreText.setText(genreName)
|
||||
yearText.setText(songYear)
|
||||
trackNumberText.setText(trackNumber)
|
||||
lyricsText.setText(lyrics)
|
||||
songComposerText.setText(composer)
|
||||
}
|
||||
|
||||
override fun loadCurrentImage() {
|
||||
}
|
||||
|
||||
override fun searchImageOnWeb() {
|
||||
}
|
||||
|
||||
override fun deleteImage() {
|
||||
}
|
||||
|
||||
override fun save() {
|
||||
val fieldKeyValueMap = EnumMap<FieldKey, String>(FieldKey::class.java)
|
||||
fieldKeyValueMap[FieldKey.TITLE] = songText.text.toString()
|
||||
fieldKeyValueMap[FieldKey.ALBUM] = albumText.text.toString()
|
||||
fieldKeyValueMap[FieldKey.ARTIST] = artistText.text.toString()
|
||||
fieldKeyValueMap[FieldKey.GENRE] = genreText.text.toString()
|
||||
fieldKeyValueMap[FieldKey.YEAR] = yearText.text.toString()
|
||||
fieldKeyValueMap[FieldKey.TRACK] = trackNumberText.text.toString()
|
||||
fieldKeyValueMap[FieldKey.LYRICS] = lyricsText.text.toString()
|
||||
fieldKeyValueMap[FieldKey.ALBUM_ARTIST] = albumArtistText.text.toString()
|
||||
fieldKeyValueMap[FieldKey.COMPOSER] = songComposerText.text.toString()
|
||||
writeValuesToFiles(fieldKeyValueMap, null)
|
||||
}
|
||||
|
||||
override fun getSongPaths(): List<String> {
|
||||
val paths = ArrayList<String>(1)
|
||||
paths.add(SongLoader.getSong(this, id).data)
|
||||
return paths
|
||||
}
|
||||
|
||||
override fun loadImageFromFile(selectedFile: Uri?) {
|
||||
}
|
||||
|
||||
override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {
|
||||
}
|
||||
|
||||
override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {
|
||||
}
|
||||
|
||||
override fun afterTextChanged(s: Editable) {
|
||||
dataChanged()
|
||||
}
|
||||
|
||||
companion object {
|
||||
val TAG: String = SongTagEditorActivity::class.java.simpleName
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1,193 +0,0 @@
|
|||
package code.name.monkey.retromusic.activities.tageditor;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.Dialog;
|
||||
import android.content.Context;
|
||||
import android.graphics.Bitmap;
|
||||
import android.media.MediaScannerConnection;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
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.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
|
||||
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;
|
||||
|
||||
public class WriteTagsAsyncTask extends
|
||||
DialogAsyncTask<WriteTagsAsyncTask.LoadingInfo, Integer, String[]> {
|
||||
|
||||
private WeakReference<Activity> activity;
|
||||
|
||||
public WriteTagsAsyncTask(@NonNull Activity activity) {
|
||||
super(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
|
||||
protected String[] doInBackground(LoadingInfo... params) {
|
||||
try {
|
||||
LoadingInfo info = params[0];
|
||||
|
||||
Artwork artwork = null;
|
||||
File albumArtFile = null;
|
||||
if (info.artworkInfo != null && info.artworkInfo.getArtwork() != null) {
|
||||
try {
|
||||
albumArtFile = MusicUtil.createAlbumArtFile().getCanonicalFile();
|
||||
info.artworkInfo.getArtwork()
|
||||
.compress(Bitmap.CompressFormat.PNG, 0, new FileOutputStream(albumArtFile));
|
||||
artwork = ArtworkFactory.createArtworkFromFile(albumArtFile);
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
int counter = 0;
|
||||
boolean wroteArtwork = false;
|
||||
boolean deletedArtwork = false;
|
||||
for (String filePath : info.filePaths) {
|
||||
publishProgress(++counter, info.filePaths.size());
|
||||
try {
|
||||
Uri safUri = null;
|
||||
if (filePath.contains(SAFUtil.SEPARATOR)) {
|
||||
String[] fragments = filePath.split(SAFUtil.SEPARATOR);
|
||||
filePath = fragments[0];
|
||||
safUri = Uri.parse(fragments[1]);
|
||||
}
|
||||
|
||||
AudioFile audioFile = AudioFileIO.read(new File(filePath));
|
||||
Tag tag = audioFile.getTagOrCreateAndSetDefault();
|
||||
|
||||
if (info.fieldKeyValueMap != null) {
|
||||
for (Map.Entry<FieldKey, String> entry : info.fieldKeyValueMap.entrySet()) {
|
||||
try {
|
||||
tag.setField(entry.getKey(), entry.getValue());
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (info.artworkInfo != null) {
|
||||
if (info.artworkInfo.getArtwork() == null) {
|
||||
tag.deleteArtworkField();
|
||||
deletedArtwork = true;
|
||||
} else if (artwork != null) {
|
||||
tag.deleteArtworkField();
|
||||
tag.setField(artwork);
|
||||
wroteArtwork = true;
|
||||
}
|
||||
}
|
||||
|
||||
Activity activity = this.activity.get();
|
||||
SAFUtil.write(activity, audioFile, safUri);
|
||||
|
||||
} catch (@NonNull Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
Context context = getContext();
|
||||
if (context != null) {
|
||||
if (wroteArtwork) {
|
||||
MusicUtil.insertAlbumArt(context, info.artworkInfo.getAlbumId(), albumArtFile.getPath());
|
||||
} else if (deletedArtwork) {
|
||||
MusicUtil.deleteAlbumArt(context, info.artworkInfo.getAlbumId());
|
||||
}
|
||||
}
|
||||
|
||||
Collection<String> paths = info.filePaths;
|
||||
if (Build.VERSION.SDK_INT == Build.VERSION_CODES.KITKAT) {
|
||||
paths = new ArrayList<>(info.filePaths.size());
|
||||
for (String path : info.filePaths) {
|
||||
if (path.contains(SAFUtil.SEPARATOR)) {
|
||||
path = path.split(SAFUtil.SEPARATOR)[0];
|
||||
}
|
||||
paths.add(path);
|
||||
}
|
||||
}
|
||||
|
||||
return paths.toArray(new String[paths.size()]);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCancelled(String[] toBeScanned) {
|
||||
super.onCancelled(toBeScanned);
|
||||
scan(toBeScanned);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(String[] toBeScanned) {
|
||||
super.onPostExecute(toBeScanned);
|
||||
scan(toBeScanned);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onProgressUpdate(@NonNull Dialog dialog, Integer... values) {
|
||||
super.onProgressUpdate(dialog, values);
|
||||
//((MaterialDialog) dialog).setMaxProgress(values[1]);
|
||||
//((MaterialDialog) dialog).setProgress(values[0]);
|
||||
}
|
||||
|
||||
private void scan(String[] toBeScanned) {
|
||||
Activity activity = this.activity.get();
|
||||
if (activity != null) {
|
||||
MediaScannerConnection.scanFile(activity, toBeScanned, null,
|
||||
new UpdateToastMediaScannerCompletionListener(activity, toBeScanned));
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,137 +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.adapter;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.res.ColorStateList;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.recyclerview.widget.ItemTouchHelper;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import com.google.android.material.checkbox.MaterialCheckBox;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import code.name.monkey.appthemehelper.ThemeStore;
|
||||
import code.name.monkey.retromusic.R;
|
||||
import code.name.monkey.retromusic.model.CategoryInfo;
|
||||
import code.name.monkey.retromusic.util.SwipeAndDragHelper;
|
||||
|
||||
public class CategoryInfoAdapter extends RecyclerView.Adapter<CategoryInfoAdapter.ViewHolder>
|
||||
implements SwipeAndDragHelper.ActionCompletionContract {
|
||||
|
||||
private List<CategoryInfo> categoryInfos;
|
||||
private ItemTouchHelper touchHelper;
|
||||
|
||||
public CategoryInfoAdapter() {
|
||||
SwipeAndDragHelper swipeAndDragHelper = new SwipeAndDragHelper(this);
|
||||
touchHelper = new ItemTouchHelper(swipeAndDragHelper);
|
||||
}
|
||||
|
||||
public void attachToRecyclerView(RecyclerView recyclerView) {
|
||||
touchHelper.attachToRecyclerView(recyclerView);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public List<CategoryInfo> getCategoryInfos() {
|
||||
return categoryInfos;
|
||||
}
|
||||
|
||||
public void setCategoryInfos(@NonNull List<CategoryInfo> categoryInfos) {
|
||||
this.categoryInfos = categoryInfos;
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
return categoryInfos.size();
|
||||
}
|
||||
|
||||
@SuppressLint("ClickableViewAccessibility")
|
||||
@Override
|
||||
public void onBindViewHolder(@NonNull CategoryInfoAdapter.ViewHolder holder, int position) {
|
||||
CategoryInfo categoryInfo = categoryInfos.get(position);
|
||||
|
||||
holder.checkBox.setChecked(categoryInfo.visible);
|
||||
holder.title.setText(holder.title.getResources().getString(categoryInfo.category.stringRes));
|
||||
|
||||
holder.itemView.setOnClickListener(v -> {
|
||||
if (!(categoryInfo.visible && isLastCheckedCategory(categoryInfo))) {
|
||||
categoryInfo.visible = !categoryInfo.visible;
|
||||
holder.checkBox.setChecked(categoryInfo.visible);
|
||||
} else {
|
||||
Toast.makeText(holder.itemView.getContext(), R.string.you_have_to_select_at_least_one_category,
|
||||
Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
});
|
||||
|
||||
holder.dragView.setOnTouchListener((view, event) -> {
|
||||
if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {
|
||||
touchHelper.startDrag(holder);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
@NonNull
|
||||
public CategoryInfoAdapter.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||
View view = LayoutInflater.from(parent.getContext())
|
||||
.inflate(R.layout.preference_dialog_library_categories_listitem, parent, false);
|
||||
return new ViewHolder(view);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onViewMoved(int oldPosition, int newPosition) {
|
||||
CategoryInfo categoryInfo = categoryInfos.get(oldPosition);
|
||||
categoryInfos.remove(oldPosition);
|
||||
categoryInfos.add(newPosition, categoryInfo);
|
||||
notifyItemMoved(oldPosition, newPosition);
|
||||
}
|
||||
|
||||
private boolean isLastCheckedCategory(CategoryInfo categoryInfo) {
|
||||
if (categoryInfo.visible) {
|
||||
for (CategoryInfo c : categoryInfos) {
|
||||
if (c != categoryInfo && c.visible) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static class ViewHolder extends RecyclerView.ViewHolder {
|
||||
private MaterialCheckBox checkBox;
|
||||
private View dragView;
|
||||
private TextView title;
|
||||
|
||||
ViewHolder(View view) {
|
||||
super(view);
|
||||
checkBox = view.findViewById(R.id.checkbox);
|
||||
checkBox.setButtonTintList(
|
||||
ColorStateList.valueOf(ThemeStore.Companion.accentColor(checkBox.getContext())));
|
||||
title = view.findViewById(R.id.title);
|
||||
dragView = view.findViewById(R.id.drag_view);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,78 +0,0 @@
|
|||
package code.name.monkey.retromusic.adapter
|
||||
|
||||
import android.app.Activity
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.TextView
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import code.name.monkey.retromusic.R
|
||||
import code.name.monkey.retromusic.model.Contributor
|
||||
import code.name.monkey.retromusic.util.RetroUtil.openUrl
|
||||
import code.name.monkey.retromusic.views.RetroShapeableImageView
|
||||
import com.bumptech.glide.Glide
|
||||
|
||||
class ContributorAdapter(
|
||||
private var contributors: List<Contributor>
|
||||
) : RecyclerView.Adapter<ContributorAdapter.ViewHolder>() {
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
|
||||
return if (viewType == HEADER) {
|
||||
ViewHolder(
|
||||
LayoutInflater.from(parent.context).inflate(
|
||||
R.layout.item_contributor_header,
|
||||
parent,
|
||||
false
|
||||
)
|
||||
)
|
||||
} else ViewHolder(
|
||||
LayoutInflater.from(parent.context).inflate(
|
||||
R.layout.item_contributor,
|
||||
parent,
|
||||
false
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val HEADER: Int = 0
|
||||
const val ITEM: Int = 1
|
||||
}
|
||||
|
||||
override fun getItemViewType(position: Int): Int {
|
||||
return if (position == 0) {
|
||||
HEADER
|
||||
} else {
|
||||
ITEM
|
||||
}
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
|
||||
val contributor = contributors[position]
|
||||
holder.bindData(contributor)
|
||||
holder.itemView.setOnClickListener {
|
||||
openUrl(it?.context as Activity, contributors[position].link)
|
||||
}
|
||||
}
|
||||
|
||||
override fun getItemCount(): Int {
|
||||
return contributors.size
|
||||
}
|
||||
|
||||
inner class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
|
||||
val title: TextView = itemView.findViewById(R.id.title)
|
||||
val text: TextView = itemView.findViewById(R.id.text)
|
||||
val image: RetroShapeableImageView = itemView.findViewById(R.id.icon)
|
||||
|
||||
internal fun bindData(contributor: Contributor) {
|
||||
title.text = contributor.name
|
||||
text.text = contributor.summary
|
||||
Glide.with(image.context)
|
||||
.load(contributor.profileImage)
|
||||
.error(R.drawable.ic_account_white_24dp)
|
||||
.placeholder(R.drawable.ic_account_white_24dp)
|
||||
.dontAnimate()
|
||||
.into(image)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,56 +0,0 @@
|
|||
package code.name.monkey.retromusic.adapter
|
||||
|
||||
import android.app.Activity
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import code.name.monkey.retromusic.R
|
||||
import code.name.monkey.retromusic.adapter.base.MediaEntryViewHolder
|
||||
import code.name.monkey.retromusic.model.Genre
|
||||
import code.name.monkey.retromusic.util.NavigationUtil
|
||||
import java.util.*
|
||||
|
||||
/**
|
||||
* @author Hemanth S (h4h13).
|
||||
*/
|
||||
|
||||
class GenreAdapter(
|
||||
private val activity: Activity,
|
||||
var dataSet: List<Genre>,
|
||||
private val mItemLayoutRes: Int
|
||||
) : RecyclerView.Adapter<GenreAdapter.ViewHolder>() {
|
||||
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
|
||||
return ViewHolder(LayoutInflater.from(activity).inflate(mItemLayoutRes, parent, false))
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
|
||||
val genre = dataSet[position]
|
||||
holder.title?.text = genre.name
|
||||
holder.text?.text = String.format(
|
||||
Locale.getDefault(),
|
||||
"%d %s",
|
||||
genre.songCount,
|
||||
if (genre.songCount > 1) activity.getString(R.string.songs) else activity.getString(R.string.song)
|
||||
)
|
||||
}
|
||||
|
||||
override fun getItemCount(): Int {
|
||||
return dataSet.size
|
||||
}
|
||||
|
||||
fun swapDataSet(list: List<Genre>) {
|
||||
dataSet = list
|
||||
notifyDataSetChanged()
|
||||
}
|
||||
|
||||
inner class ViewHolder(itemView: View) : MediaEntryViewHolder(itemView) {
|
||||
override fun onClick(v: View?) {
|
||||
super.onClick(v)
|
||||
val genre = dataSet[layoutPosition]
|
||||
NavigationUtil.goToGenre(activity, genre)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,182 +0,0 @@
|
|||
package code.name.monkey.retromusic.adapter
|
||||
|
||||
import android.util.DisplayMetrics
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.annotation.IntDef
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.appcompat.widget.AppCompatTextView
|
||||
import androidx.recyclerview.widget.GridLayoutManager
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import code.name.monkey.retromusic.R
|
||||
import code.name.monkey.retromusic.adapter.album.AlbumFullWidthAdapter
|
||||
import code.name.monkey.retromusic.adapter.artist.ArtistAdapter
|
||||
import code.name.monkey.retromusic.adapter.song.SongAdapter
|
||||
import code.name.monkey.retromusic.extensions.show
|
||||
import code.name.monkey.retromusic.loaders.PlaylistSongsLoader
|
||||
import code.name.monkey.retromusic.model.Album
|
||||
import code.name.monkey.retromusic.model.Artist
|
||||
import code.name.monkey.retromusic.model.Home
|
||||
import code.name.monkey.retromusic.model.Playlist
|
||||
|
||||
import code.name.monkey.retromusic.util.PreferenceUtil
|
||||
|
||||
class HomeAdapter(
|
||||
private val activity: AppCompatActivity,
|
||||
private val displayMetrics: DisplayMetrics
|
||||
) : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
|
||||
|
||||
private var list = listOf<Home>()
|
||||
|
||||
override fun getItemViewType(position: Int): Int {
|
||||
return list[position].homeSection
|
||||
}
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
|
||||
val layout = LayoutInflater.from(activity)
|
||||
.inflate(R.layout.section_recycler_view, parent, false)
|
||||
return when (viewType) {
|
||||
RECENT_ARTISTS, TOP_ARTISTS -> ArtistViewHolder(layout)
|
||||
PLAYLISTS -> PlaylistViewHolder(layout)
|
||||
else -> {
|
||||
AlbumViewHolder(
|
||||
LayoutInflater.from(activity).inflate(
|
||||
R.layout.metal_section_recycler_view,
|
||||
parent,
|
||||
false
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
|
||||
when (getItemViewType(position)) {
|
||||
RECENT_ALBUMS -> {
|
||||
val viewHolder = holder as AlbumViewHolder
|
||||
viewHolder.bindView(list[position].arrayList.toAlbums(), R.string.recent_albums)
|
||||
}
|
||||
TOP_ALBUMS -> {
|
||||
val viewHolder = holder as AlbumViewHolder
|
||||
viewHolder.bindView(list[position].arrayList.toAlbums(), R.string.top_albums)
|
||||
}
|
||||
RECENT_ARTISTS -> {
|
||||
val viewHolder = holder as ArtistViewHolder
|
||||
viewHolder.bindView(list[position].arrayList.toArtists(), R.string.recent_artists)
|
||||
}
|
||||
TOP_ARTISTS -> {
|
||||
val viewHolder = holder as ArtistViewHolder
|
||||
viewHolder.bindView(list[position].arrayList.toArtists(), R.string.top_artists)
|
||||
}
|
||||
PLAYLISTS -> {
|
||||
val viewHolder = holder as PlaylistViewHolder
|
||||
viewHolder.bindView(list[position].arrayList.toPlaylist(), R.string.favorites)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun getItemCount(): Int {
|
||||
return list.size
|
||||
}
|
||||
|
||||
fun swapData(sections: List<Home>) {
|
||||
list = sections
|
||||
notifyDataSetChanged()
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
@IntDef(RECENT_ALBUMS, TOP_ALBUMS, RECENT_ARTISTS, TOP_ARTISTS, PLAYLISTS)
|
||||
@Retention(AnnotationRetention.SOURCE)
|
||||
annotation class HomeSection
|
||||
|
||||
const val RECENT_ALBUMS = 3
|
||||
const val TOP_ALBUMS = 1
|
||||
const val RECENT_ARTISTS = 2
|
||||
const val TOP_ARTISTS = 0
|
||||
const val PLAYLISTS = 4
|
||||
}
|
||||
|
||||
private inner class AlbumViewHolder(view: View) : AbsHomeViewItem(view) {
|
||||
fun bindView(list: ArrayList<Album>, titleRes: Int) {
|
||||
if (list.isNotEmpty()) {
|
||||
recyclerView.apply {
|
||||
show()
|
||||
adapter = AlbumFullWidthAdapter(activity, list, displayMetrics)
|
||||
}
|
||||
title.text = activity.getString(titleRes)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
inner class ArtistViewHolder(view: View) : AbsHomeViewItem(view) {
|
||||
fun bindView(list: ArrayList<Artist>, titleRes: Int) {
|
||||
if (list.isNotEmpty()) {
|
||||
val manager = LinearLayoutManager(activity, LinearLayoutManager.HORIZONTAL, false)
|
||||
val artistAdapter = ArtistAdapter(
|
||||
activity,
|
||||
list,
|
||||
PreferenceUtil.homeGridStyle,
|
||||
null
|
||||
)
|
||||
recyclerView.apply {
|
||||
show()
|
||||
layoutManager = manager
|
||||
adapter = artistAdapter
|
||||
}
|
||||
title.text = activity.getString(titleRes)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private inner class PlaylistViewHolder(view: View) : AbsHomeViewItem(view) {
|
||||
fun bindView(arrayList: ArrayList<Playlist>, titleRes: Int) {
|
||||
if (arrayList.isNotEmpty()) {
|
||||
val songs = PlaylistSongsLoader.getPlaylistSongList(activity, arrayList[0])
|
||||
if (songs.isNotEmpty()) {
|
||||
recyclerView.apply {
|
||||
show()
|
||||
val songAdapter =
|
||||
SongAdapter(activity, songs, R.layout.item_album_card, null)
|
||||
layoutManager =
|
||||
GridLayoutManager(activity, 1, GridLayoutManager.HORIZONTAL, false)
|
||||
adapter = songAdapter
|
||||
}
|
||||
title.text = activity.getString(titleRes)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
open inner class AbsHomeViewItem(itemView: View) : RecyclerView.ViewHolder(itemView) {
|
||||
val recyclerView: RecyclerView = itemView.findViewById(R.id.recyclerView)
|
||||
val title: AppCompatTextView = itemView.findViewById(R.id.title)
|
||||
}
|
||||
}
|
||||
|
||||
private fun <E> ArrayList<E>.toAlbums(): ArrayList<Album> {
|
||||
val arrayList = ArrayList<Album>()
|
||||
for (x in this) {
|
||||
arrayList.add(x as Album)
|
||||
}
|
||||
return arrayList
|
||||
}
|
||||
|
||||
private fun <E> ArrayList<E>.toArtists(): ArrayList<Artist> {
|
||||
val arrayList = ArrayList<Artist>()
|
||||
for (x in this) {
|
||||
arrayList.add(x as Artist)
|
||||
}
|
||||
return arrayList
|
||||
}
|
||||
|
||||
private fun <E> ArrayList<E>.toPlaylist(): ArrayList<Playlist> {
|
||||
val arrayList = ArrayList<Playlist>()
|
||||
for (x in this) {
|
||||
arrayList.add(x as Playlist)
|
||||
}
|
||||
return arrayList
|
||||
}
|
||||
|
|
@ -1,172 +0,0 @@
|
|||
package code.name.monkey.retromusic.adapter
|
||||
|
||||
import android.app.ActivityOptions
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import code.name.monkey.appthemehelper.ThemeStore
|
||||
import code.name.monkey.retromusic.R
|
||||
import code.name.monkey.retromusic.adapter.base.MediaEntryViewHolder
|
||||
import code.name.monkey.retromusic.glide.AlbumGlideRequest
|
||||
import code.name.monkey.retromusic.glide.ArtistGlideRequest
|
||||
import code.name.monkey.retromusic.helper.MusicPlayerRemote
|
||||
import code.name.monkey.retromusic.helper.menu.SongMenuHelper
|
||||
import code.name.monkey.retromusic.loaders.PlaylistSongsLoader
|
||||
import code.name.monkey.retromusic.model.*
|
||||
import code.name.monkey.retromusic.model.smartplaylist.AbsSmartPlaylist
|
||||
import code.name.monkey.retromusic.util.MusicUtil
|
||||
import code.name.monkey.retromusic.util.NavigationUtil
|
||||
import com.bumptech.glide.Glide
|
||||
import android.util.Pair as UtilPair
|
||||
|
||||
class SearchAdapter(
|
||||
private val activity: AppCompatActivity,
|
||||
private var dataSet: List<Any>?
|
||||
) : RecyclerView.Adapter<SearchAdapter.ViewHolder>() {
|
||||
|
||||
fun swapDataSet(dataSet: MutableList<Any>) {
|
||||
this.dataSet = dataSet
|
||||
notifyDataSetChanged()
|
||||
}
|
||||
|
||||
override fun getItemViewType(position: Int): Int {
|
||||
if (dataSet!![position] is Album) return ALBUM
|
||||
if (dataSet!![position] is Artist) return ARTIST
|
||||
if (dataSet!![position] is Genre) return GENRE
|
||||
if (dataSet!![position] is Playlist) return PLAYLIST
|
||||
return if (dataSet!![position] is Song) SONG else HEADER
|
||||
}
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
|
||||
return if (viewType == HEADER) ViewHolder(
|
||||
LayoutInflater.from(activity).inflate(
|
||||
R.layout.sub_header,
|
||||
parent,
|
||||
false
|
||||
), viewType
|
||||
)
|
||||
else
|
||||
ViewHolder(
|
||||
LayoutInflater.from(activity).inflate(R.layout.item_list, parent, false),
|
||||
viewType
|
||||
)
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
|
||||
when (getItemViewType(position)) {
|
||||
ALBUM -> {
|
||||
val album = dataSet?.get(position) as Album
|
||||
holder.title?.text = album.title
|
||||
holder.text?.text = album.artistName
|
||||
AlbumGlideRequest.Builder.from(Glide.with(activity), album.safeGetFirstSong())
|
||||
.checkIgnoreMediaStore(activity).build().into(holder.image)
|
||||
}
|
||||
ARTIST -> {
|
||||
val artist = dataSet?.get(position) as Artist
|
||||
holder.title?.text = artist.name
|
||||
holder.text?.text = MusicUtil.getArtistInfoString(activity, artist)
|
||||
ArtistGlideRequest.Builder.from(Glide.with(activity), artist).build()
|
||||
.into(holder.image)
|
||||
}
|
||||
SONG -> {
|
||||
val song = dataSet?.get(position) as Song
|
||||
holder.title?.text = song.title
|
||||
holder.text?.text = song.albumName
|
||||
}
|
||||
GENRE -> {
|
||||
val genre = dataSet?.get(position) as Genre
|
||||
holder.title?.text = genre.name
|
||||
}
|
||||
PLAYLIST -> {
|
||||
val playlist = dataSet?.get(position) as Playlist
|
||||
holder.title?.text = playlist.name
|
||||
holder.text?.text = MusicUtil.getPlaylistInfoString(activity, getSongs(playlist))
|
||||
}
|
||||
else -> {
|
||||
holder.title?.text = dataSet?.get(position).toString()
|
||||
holder.title?.setTextColor(ThemeStore.accentColor(activity))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun getSongs(playlist: Playlist): java.util.ArrayList<Song> {
|
||||
val songs = java.util.ArrayList<Song>()
|
||||
if (playlist is AbsSmartPlaylist) {
|
||||
songs.addAll(playlist.getSongs(activity))
|
||||
} else {
|
||||
songs.addAll(PlaylistSongsLoader.getPlaylistSongList(activity, playlist.id))
|
||||
}
|
||||
return songs
|
||||
}
|
||||
|
||||
override fun getItemCount(): Int {
|
||||
return dataSet!!.size
|
||||
}
|
||||
|
||||
inner class ViewHolder(itemView: View, itemViewType: Int) : MediaEntryViewHolder(itemView) {
|
||||
init {
|
||||
itemView.setOnLongClickListener(null)
|
||||
|
||||
if (itemViewType == SONG) {
|
||||
menu?.visibility = View.VISIBLE
|
||||
menu?.setOnClickListener(object : SongMenuHelper.OnClickSongMenu(activity) {
|
||||
override val song: Song
|
||||
get() = dataSet!![layoutPosition] as Song
|
||||
})
|
||||
} else {
|
||||
menu?.visibility = View.GONE
|
||||
}
|
||||
|
||||
when (itemViewType) {
|
||||
ALBUM -> setImageTransitionName(activity.getString(R.string.transition_album_art))
|
||||
ARTIST -> setImageTransitionName(activity.getString(R.string.transition_artist_image))
|
||||
else -> {
|
||||
val container = itemView.findViewById<View>(R.id.imageContainer)
|
||||
container?.visibility = View.GONE
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onClick(v: View?) {
|
||||
val item = dataSet!![layoutPosition]
|
||||
when (itemViewType) {
|
||||
ALBUM -> {
|
||||
val options = ActivityOptions.makeSceneTransitionAnimation(
|
||||
activity,
|
||||
UtilPair.create(image, activity.getString(R.string.transition_album_art))
|
||||
)
|
||||
NavigationUtil.goToAlbumOptions(activity, (item as Album).id, options)
|
||||
}
|
||||
ARTIST -> {
|
||||
val options = ActivityOptions.makeSceneTransitionAnimation(
|
||||
activity,
|
||||
UtilPair.create(image, activity.getString(R.string.transition_artist_image))
|
||||
)
|
||||
NavigationUtil.goToArtistOptions(activity, (item as Artist).id, options)
|
||||
}
|
||||
GENRE -> {
|
||||
NavigationUtil.goToGenre(activity, item as Genre)
|
||||
}
|
||||
PLAYLIST -> {
|
||||
NavigationUtil.goToPlaylistNew(activity, item as Playlist)
|
||||
}
|
||||
SONG -> {
|
||||
val playList = ArrayList<Song>()
|
||||
playList.add(item as Song)
|
||||
MusicPlayerRemote.openQueue(playList, 0, true)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val HEADER = 0
|
||||
private const val ALBUM = 1
|
||||
private const val ARTIST = 2
|
||||
private const val SONG = 3
|
||||
private const val GENRE = 4
|
||||
private const val PLAYLIST = 5
|
||||
}
|
||||
}
|
|
@ -1,208 +0,0 @@
|
|||
/*
|
||||
* Copyright 2019 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package code.name.monkey.retromusic.adapter
|
||||
|
||||
import android.graphics.PorterDuff
|
||||
import android.view.LayoutInflater
|
||||
import android.view.MenuItem
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import code.name.monkey.appthemehelper.util.ATHUtil
|
||||
import code.name.monkey.retromusic.R
|
||||
import code.name.monkey.retromusic.adapter.base.AbsMultiSelectAdapter
|
||||
import code.name.monkey.retromusic.adapter.base.MediaEntryViewHolder
|
||||
import code.name.monkey.retromusic.glide.audiocover.AudioFileCover
|
||||
import code.name.monkey.retromusic.interfaces.CabHolder
|
||||
import code.name.monkey.retromusic.util.MusicUtil
|
||||
import code.name.monkey.retromusic.util.RetroUtil
|
||||
import com.bumptech.glide.Glide
|
||||
import com.bumptech.glide.load.engine.DiskCacheStrategy
|
||||
import com.bumptech.glide.signature.MediaStoreSignature
|
||||
import me.zhanghai.android.fastscroll.PopupTextProvider
|
||||
import java.io.File
|
||||
import java.text.DecimalFormat
|
||||
import java.util.*
|
||||
import kotlin.math.log10
|
||||
import kotlin.math.pow
|
||||
|
||||
class SongFileAdapter(
|
||||
private val activity: AppCompatActivity,
|
||||
private var dataSet: List<File>,
|
||||
private val itemLayoutRes: Int,
|
||||
private val callbacks: Callbacks?,
|
||||
cabHolder: CabHolder?
|
||||
) : AbsMultiSelectAdapter<SongFileAdapter.ViewHolder, File>(
|
||||
activity, cabHolder, R.menu.menu_media_selection
|
||||
), PopupTextProvider {
|
||||
|
||||
init {
|
||||
this.setHasStableIds(true)
|
||||
}
|
||||
|
||||
override fun getItemViewType(position: Int): Int {
|
||||
return if (dataSet[position].isDirectory) FOLDER else FILE
|
||||
}
|
||||
|
||||
override fun getItemId(position: Int): Long {
|
||||
return dataSet[position].hashCode().toLong()
|
||||
}
|
||||
|
||||
fun swapDataSet(songFiles: List<File>) {
|
||||
this.dataSet = songFiles
|
||||
notifyDataSetChanged()
|
||||
}
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
|
||||
return ViewHolder(LayoutInflater.from(activity).inflate(itemLayoutRes, parent, false))
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: ViewHolder, index: Int) {
|
||||
val file = dataSet[index]
|
||||
holder.itemView.isActivated = isChecked(file)
|
||||
holder.title?.text = getFileTitle(file)
|
||||
if (holder.text != null) {
|
||||
if (holder.itemViewType == FILE) {
|
||||
holder.text?.text = getFileText(file)
|
||||
} else {
|
||||
holder.text?.visibility = View.GONE
|
||||
}
|
||||
}
|
||||
|
||||
if (holder.image != null) {
|
||||
loadFileImage(file, holder)
|
||||
}
|
||||
}
|
||||
|
||||
private fun getFileTitle(file: File): String {
|
||||
return file.name
|
||||
}
|
||||
|
||||
private fun getFileText(file: File): String? {
|
||||
return if (file.isDirectory) null else readableFileSize(file.length())
|
||||
}
|
||||
|
||||
private fun loadFileImage(file: File, holder: ViewHolder) {
|
||||
val iconColor = ATHUtil.resolveColor(activity, R.attr.colorControlNormal)
|
||||
if (file.isDirectory) {
|
||||
holder.image?.let {
|
||||
it.setColorFilter(iconColor, PorterDuff.Mode.SRC_IN)
|
||||
it.setImageResource(R.drawable.ic_folder_white_24dp)
|
||||
}
|
||||
holder.imageTextContainer?.setCardBackgroundColor(
|
||||
ATHUtil.resolveColor(
|
||||
activity,
|
||||
R.attr.colorSurface
|
||||
)
|
||||
)
|
||||
} else {
|
||||
val error = RetroUtil.getTintedVectorDrawable(
|
||||
activity, R.drawable.ic_file_music_white_24dp, iconColor
|
||||
)
|
||||
Glide.with(activity)
|
||||
.load(AudioFileCover(file.path))
|
||||
.diskCacheStrategy(DiskCacheStrategy.NONE)
|
||||
.error(error)
|
||||
.placeholder(error)
|
||||
.animate(android.R.anim.fade_in)
|
||||
.signature(MediaStoreSignature("", file.lastModified(), 0))
|
||||
.into(holder.image)
|
||||
}
|
||||
}
|
||||
|
||||
override fun getItemCount(): Int {
|
||||
return dataSet.size
|
||||
}
|
||||
|
||||
override fun getIdentifier(position: Int): File? {
|
||||
return dataSet[position]
|
||||
}
|
||||
|
||||
override fun getName(`object`: File): String {
|
||||
return getFileTitle(`object`)
|
||||
}
|
||||
|
||||
override fun onMultipleItemAction(menuItem: MenuItem, selection: ArrayList<File>) {
|
||||
if (callbacks == null) return
|
||||
callbacks.onMultipleItemAction(menuItem, selection)
|
||||
}
|
||||
|
||||
override fun getPopupText(position: Int): String {
|
||||
return getSectionName(position)
|
||||
}
|
||||
|
||||
private fun getSectionName(position: Int): String {
|
||||
return MusicUtil.getSectionName(dataSet[position].name)
|
||||
}
|
||||
|
||||
interface Callbacks {
|
||||
fun onFileSelected(file: File)
|
||||
|
||||
fun onFileMenuClicked(file: File, view: View)
|
||||
|
||||
fun onMultipleItemAction(item: MenuItem, files: ArrayList<File>)
|
||||
}
|
||||
|
||||
inner class ViewHolder(itemView: View) : MediaEntryViewHolder(itemView) {
|
||||
|
||||
init {
|
||||
if (menu != null && callbacks != null) {
|
||||
menu?.setOnClickListener { v ->
|
||||
val position = layoutPosition
|
||||
if (isPositionInRange(position)) {
|
||||
callbacks.onFileMenuClicked(dataSet[position], v)
|
||||
}
|
||||
}
|
||||
}
|
||||
if (imageTextContainer != null) {
|
||||
imageTextContainer?.cardElevation = 0f
|
||||
}
|
||||
}
|
||||
|
||||
override fun onClick(v: View?) {
|
||||
val position = layoutPosition
|
||||
if (isPositionInRange(position)) {
|
||||
if (isInQuickSelectMode) {
|
||||
toggleChecked(position)
|
||||
} else {
|
||||
callbacks?.onFileSelected(dataSet[position])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onLongClick(v: View?): Boolean {
|
||||
val position = layoutPosition
|
||||
return isPositionInRange(position) && toggleChecked(position)
|
||||
}
|
||||
|
||||
private fun isPositionInRange(position: Int): Boolean {
|
||||
return position >= 0 && position < dataSet.size
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
private const val FILE = 0
|
||||
private const val FOLDER = 1
|
||||
|
||||
fun readableFileSize(size: Long): String {
|
||||
if (size <= 0) return "$size B"
|
||||
val units = arrayOf("B", "KB", "MB", "GB", "TB")
|
||||
val digitGroups = (log10(size.toDouble()) / log10(1024.0)).toInt()
|
||||
return DecimalFormat("#,##0.##").format(size / 1024.0.pow(digitGroups.toDouble())) + " " + units[digitGroups]
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,52 +0,0 @@
|
|||
package code.name.monkey.retromusic.adapter
|
||||
|
||||
import android.app.Activity
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.TextView
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import code.name.monkey.retromusic.R
|
||||
import code.name.monkey.retromusic.extensions.hide
|
||||
import code.name.monkey.retromusic.model.Contributor
|
||||
import code.name.monkey.retromusic.util.RetroUtil
|
||||
import code.name.monkey.retromusic.views.RetroShapeableImageView
|
||||
|
||||
class TranslatorsAdapter(
|
||||
private var contributors: List<Contributor>
|
||||
) : RecyclerView.Adapter<TranslatorsAdapter.ViewHolder>() {
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
|
||||
return ViewHolder(
|
||||
LayoutInflater.from(parent.context).inflate(
|
||||
R.layout.item_contributor,
|
||||
parent,
|
||||
false
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
|
||||
val contributor = contributors[position]
|
||||
holder.bindData(contributor)
|
||||
holder.itemView.setOnClickListener {
|
||||
RetroUtil.openUrl(it?.context as Activity, contributors[position].link)
|
||||
}
|
||||
}
|
||||
|
||||
override fun getItemCount(): Int {
|
||||
return contributors.size
|
||||
}
|
||||
|
||||
inner class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
|
||||
val title: TextView = itemView.findViewById(R.id.title)
|
||||
val text: TextView = itemView.findViewById(R.id.text)
|
||||
val image: RetroShapeableImageView = itemView.findViewById(R.id.icon)
|
||||
|
||||
internal fun bindData(contributor: Contributor) {
|
||||
title.text = contributor.name
|
||||
text.text = contributor.summary
|
||||
image.hide()
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,202 +0,0 @@
|
|||
package code.name.monkey.retromusic.adapter.album
|
||||
|
||||
import android.app.ActivityOptions
|
||||
import android.content.res.ColorStateList
|
||||
import android.content.res.Resources
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.view.LayoutInflater
|
||||
import android.view.MenuItem
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import code.name.monkey.retromusic.R
|
||||
import code.name.monkey.retromusic.adapter.base.AbsMultiSelectAdapter
|
||||
import code.name.monkey.retromusic.adapter.base.MediaEntryViewHolder
|
||||
import code.name.monkey.retromusic.glide.AlbumGlideRequest
|
||||
import code.name.monkey.retromusic.glide.RetroMusicColoredTarget
|
||||
import code.name.monkey.retromusic.helper.MusicPlayerRemote
|
||||
import code.name.monkey.retromusic.helper.SortOrder
|
||||
import code.name.monkey.retromusic.helper.menu.SongsMenuHelper
|
||||
import code.name.monkey.retromusic.interfaces.CabHolder
|
||||
import code.name.monkey.retromusic.model.Album
|
||||
import code.name.monkey.retromusic.model.Song
|
||||
import code.name.monkey.retromusic.util.MusicUtil
|
||||
import code.name.monkey.retromusic.util.NavigationUtil
|
||||
import code.name.monkey.retromusic.util.PreferenceUtil
|
||||
import code.name.monkey.retromusic.util.color.MediaNotificationProcessor
|
||||
import com.bumptech.glide.Glide
|
||||
import me.zhanghai.android.fastscroll.PopupTextProvider
|
||||
|
||||
open class AlbumAdapter(
|
||||
protected val activity: AppCompatActivity,
|
||||
var dataSet: List<Album>,
|
||||
protected var itemLayoutRes: Int,
|
||||
cabHolder: CabHolder?
|
||||
) : AbsMultiSelectAdapter<AlbumAdapter.ViewHolder, Album>(
|
||||
activity,
|
||||
cabHolder,
|
||||
R.menu.menu_media_selection
|
||||
), PopupTextProvider {
|
||||
|
||||
init {
|
||||
this.setHasStableIds(true)
|
||||
}
|
||||
|
||||
fun swapDataSet(dataSet: List<Album>) {
|
||||
this.dataSet = dataSet
|
||||
notifyDataSetChanged()
|
||||
}
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
|
||||
val view =
|
||||
try {
|
||||
LayoutInflater.from(activity).inflate(itemLayoutRes, parent, false)
|
||||
} catch (e: Resources.NotFoundException) {
|
||||
LayoutInflater.from(activity).inflate(R.layout.item_grid, parent, false)
|
||||
}
|
||||
return createViewHolder(view, viewType)
|
||||
}
|
||||
|
||||
protected open fun createViewHolder(view: View, viewType: Int): ViewHolder {
|
||||
return ViewHolder(view)
|
||||
}
|
||||
|
||||
private fun getAlbumTitle(album: Album): String? {
|
||||
return album.title
|
||||
}
|
||||
|
||||
protected open fun getAlbumText(album: Album): String? {
|
||||
return album.artistName
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
|
||||
val album = dataSet[position]
|
||||
val isChecked = isChecked(album)
|
||||
holder.itemView.isActivated = isChecked
|
||||
holder.title?.text = getAlbumTitle(album)
|
||||
holder.text?.text = getAlbumText(album)
|
||||
holder.playSongs?.setOnClickListener {
|
||||
album.songs?.let { songs ->
|
||||
MusicPlayerRemote.openQueue(
|
||||
songs,
|
||||
0,
|
||||
true
|
||||
)
|
||||
}
|
||||
}
|
||||
loadAlbumCover(album, holder)
|
||||
}
|
||||
|
||||
protected open fun setColors(color: MediaNotificationProcessor, holder: ViewHolder) {
|
||||
if (holder.paletteColorContainer != null) {
|
||||
holder.title?.setTextColor(color.primaryTextColor)
|
||||
holder.text?.setTextColor(color.secondaryTextColor)
|
||||
holder.paletteColorContainer?.setBackgroundColor(color.backgroundColor)
|
||||
}
|
||||
holder.mask?.backgroundTintList = ColorStateList.valueOf(color.primaryTextColor)
|
||||
}
|
||||
|
||||
protected open fun loadAlbumCover(album: Album, holder: ViewHolder) {
|
||||
if (holder.image == null) {
|
||||
return
|
||||
}
|
||||
|
||||
AlbumGlideRequest.Builder.from(Glide.with(activity), album.safeGetFirstSong())
|
||||
.checkIgnoreMediaStore(activity)
|
||||
.generatePalette(activity)
|
||||
.build()
|
||||
.into(object : RetroMusicColoredTarget(holder.image!!) {
|
||||
override fun onLoadCleared(placeholder: Drawable?) {
|
||||
super.onLoadCleared(placeholder)
|
||||
//setColors(defaultFooterColor, holder)
|
||||
}
|
||||
|
||||
override fun onColorReady(colors: MediaNotificationProcessor) {
|
||||
setColors(colors, holder)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
override fun getItemCount(): Int {
|
||||
return dataSet.size
|
||||
}
|
||||
|
||||
override fun getItemId(position: Int): Long {
|
||||
return dataSet[position].id.toLong()
|
||||
}
|
||||
|
||||
override fun getIdentifier(position: Int): Album? {
|
||||
return dataSet[position]
|
||||
}
|
||||
|
||||
override fun getName(album: Album): String {
|
||||
return album.title!!
|
||||
}
|
||||
|
||||
override fun onMultipleItemAction(
|
||||
menuItem: MenuItem, selection: ArrayList<Album>
|
||||
) {
|
||||
SongsMenuHelper.handleMenuClick(activity, getSongList(selection), menuItem.itemId)
|
||||
}
|
||||
|
||||
private fun getSongList(albums: List<Album>): ArrayList<Song> {
|
||||
val songs = ArrayList<Song>()
|
||||
for (album in albums) {
|
||||
songs.addAll(album.songs!!)
|
||||
}
|
||||
return songs
|
||||
}
|
||||
|
||||
override fun getPopupText(position: Int): String {
|
||||
return getSectionName(position)
|
||||
}
|
||||
|
||||
private fun getSectionName(position: Int): String {
|
||||
var sectionName: String? = null
|
||||
when (PreferenceUtil.albumSortOrder) {
|
||||
SortOrder.AlbumSortOrder.ALBUM_A_Z, SortOrder.AlbumSortOrder.ALBUM_Z_A -> sectionName =
|
||||
dataSet[position].title
|
||||
SortOrder.AlbumSortOrder.ALBUM_ARTIST -> sectionName = dataSet[position].artistName
|
||||
SortOrder.AlbumSortOrder.ALBUM_YEAR -> return MusicUtil.getYearString(
|
||||
dataSet[position].year
|
||||
)
|
||||
}
|
||||
|
||||
return MusicUtil.getSectionName(sectionName)
|
||||
}
|
||||
|
||||
inner class ViewHolder(itemView: View) : MediaEntryViewHolder(itemView) {
|
||||
|
||||
init {
|
||||
setImageTransitionName(activity.getString(R.string.transition_album_art))
|
||||
menu?.visibility = View.GONE
|
||||
}
|
||||
|
||||
override fun onClick(v: View?) {
|
||||
super.onClick(v)
|
||||
if (isInQuickSelectMode) {
|
||||
toggleChecked(layoutPosition)
|
||||
} else {
|
||||
val activityOptions = ActivityOptions.makeSceneTransitionAnimation(
|
||||
activity,
|
||||
imageContainerCard ?: image,
|
||||
activity.getString(R.string.transition_album_art)
|
||||
)
|
||||
NavigationUtil.goToAlbumOptions(
|
||||
activity,
|
||||
dataSet[layoutPosition].id,
|
||||
activityOptions
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onLongClick(v: View?): Boolean {
|
||||
toggleChecked(layoutPosition)
|
||||
return super.onLongClick(v)
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
val TAG: String = AlbumAdapter::class.java.simpleName
|
||||
}
|
||||
}
|
|
@ -1,181 +0,0 @@
|
|||
package code.name.monkey.retromusic.adapter.album
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.ImageView
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.fragment.app.FragmentManager
|
||||
import code.name.monkey.retromusic.R
|
||||
import code.name.monkey.retromusic.fragments.AlbumCoverStyle
|
||||
import code.name.monkey.retromusic.fragments.NowPlayingScreen.*
|
||||
import code.name.monkey.retromusic.glide.RetroMusicColoredTarget
|
||||
import code.name.monkey.retromusic.glide.SongGlideRequest
|
||||
import code.name.monkey.retromusic.misc.CustomFragmentStatePagerAdapter
|
||||
import code.name.monkey.retromusic.model.Song
|
||||
import code.name.monkey.retromusic.util.NavigationUtil
|
||||
|
||||
import code.name.monkey.retromusic.util.PreferenceUtil
|
||||
import code.name.monkey.retromusic.util.color.MediaNotificationProcessor
|
||||
import com.bumptech.glide.Glide
|
||||
|
||||
class AlbumCoverPagerAdapter(
|
||||
fragmentManager: FragmentManager,
|
||||
private val dataSet: List<Song>
|
||||
) : CustomFragmentStatePagerAdapter(fragmentManager) {
|
||||
|
||||
private var currentColorReceiver: AlbumCoverFragment.ColorReceiver? = null
|
||||
private var currentColorReceiverPosition = -1
|
||||
|
||||
override fun getItem(position: Int): Fragment {
|
||||
return AlbumCoverFragment.newInstance(dataSet[position])
|
||||
}
|
||||
|
||||
override fun getCount(): Int {
|
||||
return dataSet.size
|
||||
}
|
||||
|
||||
override fun instantiateItem(container: ViewGroup, position: Int): Any {
|
||||
val o = super.instantiateItem(container, position)
|
||||
if (currentColorReceiver != null && currentColorReceiverPosition == position) {
|
||||
receiveColor(currentColorReceiver!!, currentColorReceiverPosition)
|
||||
}
|
||||
return o
|
||||
}
|
||||
|
||||
/**
|
||||
* Only the latest passed [AlbumCoverFragment.ColorReceiver] is guaranteed to receive a
|
||||
* response
|
||||
*/
|
||||
fun receiveColor(colorReceiver: AlbumCoverFragment.ColorReceiver, position: Int) {
|
||||
|
||||
if (getFragment(position) is AlbumCoverFragment) {
|
||||
val fragment = getFragment(position) as AlbumCoverFragment
|
||||
currentColorReceiver = null
|
||||
currentColorReceiverPosition = -1
|
||||
fragment.receiveColor(colorReceiver, position)
|
||||
} else {
|
||||
currentColorReceiver = colorReceiver
|
||||
currentColorReceiverPosition = position
|
||||
}
|
||||
}
|
||||
|
||||
class AlbumCoverFragment : Fragment() {
|
||||
|
||||
lateinit var albumCover: ImageView
|
||||
private var isColorReady: Boolean = false
|
||||
private lateinit var color: MediaNotificationProcessor
|
||||
private lateinit var song: Song
|
||||
private var colorReceiver: ColorReceiver? = null
|
||||
private var request: Int = 0
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
if (arguments != null) {
|
||||
song = requireArguments().getParcelable(SONG_ARG)!!
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View? {
|
||||
val view = inflater.inflate(getLayoutWithPlayerTheme(), container, false)
|
||||
albumCover = view.findViewById(R.id.player_image)
|
||||
albumCover.setOnClickListener {
|
||||
NavigationUtil.goToLyrics(requireActivity())
|
||||
}
|
||||
return view
|
||||
}
|
||||
|
||||
private fun getLayoutWithPlayerTheme(): Int {
|
||||
return when (PreferenceUtil.nowPlayingScreen) {
|
||||
Card,
|
||||
Fit,
|
||||
Tiny,
|
||||
Classic,
|
||||
Peak,
|
||||
Gradient,
|
||||
Full -> R.layout.fragment_album_full_cover
|
||||
else -> {
|
||||
if (PreferenceUtil.isCarouselEffect) {
|
||||
R.layout.fragment_album_carousel_cover
|
||||
} else {
|
||||
when (PreferenceUtil.albumCoverStyle) {
|
||||
AlbumCoverStyle.Normal -> R.layout.fragment_album_cover
|
||||
AlbumCoverStyle.Flat -> R.layout.fragment_album_flat_cover
|
||||
AlbumCoverStyle.Circle -> R.layout.fragment_album_circle_cover
|
||||
AlbumCoverStyle.Card -> R.layout.fragment_album_card_cover
|
||||
AlbumCoverStyle.Material -> R.layout.fragment_album_material_cover
|
||||
AlbumCoverStyle.Full -> R.layout.fragment_album_full_cover
|
||||
AlbumCoverStyle.FullCard -> R.layout.fragment_album_full_card_cover
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
loadAlbumCover()
|
||||
}
|
||||
|
||||
override fun onDestroyView() {
|
||||
super.onDestroyView()
|
||||
colorReceiver = null
|
||||
}
|
||||
|
||||
private fun loadAlbumCover() {
|
||||
SongGlideRequest.Builder.from(Glide.with(requireContext()), song)
|
||||
.checkIgnoreMediaStore(requireContext())
|
||||
.generatePalette(requireContext()).build()
|
||||
.into(object : RetroMusicColoredTarget(albumCover) {
|
||||
override fun onColorReady(colors: MediaNotificationProcessor) {
|
||||
setColor(colors)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
private fun setColor(color: MediaNotificationProcessor) {
|
||||
this.color = color
|
||||
isColorReady = true
|
||||
if (colorReceiver != null) {
|
||||
colorReceiver!!.onColorReady(color, request)
|
||||
colorReceiver = null
|
||||
}
|
||||
}
|
||||
|
||||
internal fun receiveColor(colorReceiver: ColorReceiver, request: Int) {
|
||||
if (isColorReady) {
|
||||
colorReceiver.onColorReady(color, request)
|
||||
} else {
|
||||
this.colorReceiver = colorReceiver
|
||||
this.request = request
|
||||
}
|
||||
}
|
||||
|
||||
interface ColorReceiver {
|
||||
fun onColorReady(color: MediaNotificationProcessor, request: Int)
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
private const val SONG_ARG = "song"
|
||||
|
||||
fun newInstance(song: Song): AlbumCoverFragment {
|
||||
val frag = AlbumCoverFragment()
|
||||
val args = Bundle()
|
||||
args.putParcelable(SONG_ARG, song)
|
||||
frag.arguments = args
|
||||
return frag
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
val TAG: String = AlbumCoverPagerAdapter::class.java.simpleName
|
||||
}
|
||||
}
|
||||
|
|
@ -1,108 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2017. Alexander Bilchuk <a.bilchuk@sandrlab.com>
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package code.name.monkey.retromusic.adapter.album
|
||||
|
||||
import android.app.Activity
|
||||
import android.app.ActivityOptions
|
||||
import android.util.DisplayMetrics
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import code.name.monkey.retromusic.R
|
||||
import code.name.monkey.retromusic.glide.AlbumGlideRequest
|
||||
import code.name.monkey.retromusic.glide.RetroMusicColoredTarget
|
||||
import code.name.monkey.retromusic.helper.MusicPlayerRemote
|
||||
import code.name.monkey.retromusic.model.Album
|
||||
import code.name.monkey.retromusic.util.NavigationUtil
|
||||
import code.name.monkey.retromusic.util.color.MediaNotificationProcessor
|
||||
import code.name.monkey.retromusic.views.MetalRecyclerViewPager
|
||||
import com.bumptech.glide.Glide
|
||||
|
||||
class AlbumFullWidthAdapter(
|
||||
private val activity: Activity,
|
||||
private val dataSet: List<Album>,
|
||||
metrics: DisplayMetrics
|
||||
) : MetalRecyclerViewPager.MetalAdapter<AlbumFullWidthAdapter.FullMetalViewHolder>(metrics) {
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): FullMetalViewHolder {
|
||||
return FullMetalViewHolder(
|
||||
LayoutInflater.from(parent.context).inflate(
|
||||
R.layout.pager_item,
|
||||
parent,
|
||||
false
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: FullMetalViewHolder, position: Int) {
|
||||
// don't forget about calling supper.onBindViewHolder!
|
||||
super.onBindViewHolder(holder, position)
|
||||
val album = dataSet[position]
|
||||
holder.title?.text = getAlbumTitle(album)
|
||||
holder.text?.text = getAlbumText(album)
|
||||
holder.playSongs?.setOnClickListener {
|
||||
album.songs?.let { songs ->
|
||||
MusicPlayerRemote.openQueue(
|
||||
songs,
|
||||
0,
|
||||
true
|
||||
)
|
||||
}
|
||||
}
|
||||
loadAlbumCover(album, holder)
|
||||
}
|
||||
|
||||
private fun getAlbumTitle(album: Album): String? {
|
||||
return album.title
|
||||
}
|
||||
|
||||
private fun getAlbumText(album: Album): String? {
|
||||
return album.artistName
|
||||
}
|
||||
|
||||
private fun loadAlbumCover(album: Album, holder: FullMetalViewHolder) {
|
||||
if (holder.image == null) {
|
||||
return
|
||||
}
|
||||
|
||||
AlbumGlideRequest.Builder.from(Glide.with(activity), album.safeGetFirstSong())
|
||||
.checkIgnoreMediaStore(activity)
|
||||
.generatePalette(activity)
|
||||
.build()
|
||||
.into(object : RetroMusicColoredTarget(holder.image!!) {
|
||||
override fun onColorReady(colors: MediaNotificationProcessor) {
|
||||
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
override fun getItemCount(): Int {
|
||||
return dataSet.size
|
||||
}
|
||||
|
||||
inner class FullMetalViewHolder(itemView: View) :
|
||||
MetalRecyclerViewPager.MetalViewHolder(itemView) {
|
||||
|
||||
override fun onClick(v: View?) {
|
||||
val activityOptions = ActivityOptions.makeSceneTransitionAnimation(
|
||||
activity,
|
||||
imageContainerCard ?: image,
|
||||
activity.getString(R.string.transition_album_art)
|
||||
)
|
||||
NavigationUtil.goToAlbumOptions(activity, dataSet[layoutPosition].id, activityOptions)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,69 +0,0 @@
|
|||
package code.name.monkey.retromusic.adapter.album
|
||||
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import code.name.monkey.appthemehelper.util.ATHUtil
|
||||
import code.name.monkey.retromusic.glide.AlbumGlideRequest
|
||||
import code.name.monkey.retromusic.glide.RetroMusicColoredTarget
|
||||
import code.name.monkey.retromusic.helper.HorizontalAdapterHelper
|
||||
import code.name.monkey.retromusic.interfaces.CabHolder
|
||||
import code.name.monkey.retromusic.model.Album
|
||||
import code.name.monkey.retromusic.util.MusicUtil
|
||||
import code.name.monkey.retromusic.util.color.MediaNotificationProcessor
|
||||
import com.bumptech.glide.Glide
|
||||
|
||||
class HorizontalAlbumAdapter(
|
||||
activity: AppCompatActivity,
|
||||
dataSet: List<Album>,
|
||||
cabHolder: CabHolder?
|
||||
) : AlbumAdapter(
|
||||
activity, dataSet, HorizontalAdapterHelper.LAYOUT_RES, cabHolder
|
||||
) {
|
||||
|
||||
override fun createViewHolder(view: View, viewType: Int): ViewHolder {
|
||||
val params = view.layoutParams as ViewGroup.MarginLayoutParams
|
||||
HorizontalAdapterHelper.applyMarginToLayoutParams(activity, params, viewType)
|
||||
return ViewHolder(view)
|
||||
}
|
||||
|
||||
override fun setColors(color: MediaNotificationProcessor, holder: ViewHolder) {
|
||||
holder.title?.setTextColor(ATHUtil.resolveColor(activity, android.R.attr.textColorPrimary))
|
||||
holder.text?.setTextColor(ATHUtil.resolveColor(activity, android.R.attr.textColorSecondary))
|
||||
}
|
||||
|
||||
override fun loadAlbumCover(album: Album, holder: ViewHolder) {
|
||||
if (holder.image == null) return
|
||||
AlbumGlideRequest.Builder.from(Glide.with(activity), album.safeGetFirstSong())
|
||||
.checkIgnoreMediaStore(activity)
|
||||
.generatePalette(activity)
|
||||
.build()
|
||||
.into(object : RetroMusicColoredTarget(holder.image!!) {
|
||||
override fun onLoadCleared(placeholder: Drawable?) {
|
||||
super.onLoadCleared(placeholder)
|
||||
//setColors(albumArtistFooterColor, holder)
|
||||
}
|
||||
|
||||
override fun onColorReady(colors: MediaNotificationProcessor) {
|
||||
setColors(colors, holder)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
override fun getAlbumText(album: Album): String? {
|
||||
return MusicUtil.getYearString(album.year)
|
||||
}
|
||||
|
||||
override fun getItemViewType(position: Int): Int {
|
||||
return HorizontalAdapterHelper.getItemViewtype(position, itemCount)
|
||||
}
|
||||
|
||||
override fun getItemCount(): Int {
|
||||
return dataSet.size
|
||||
}
|
||||
|
||||
companion object {
|
||||
val TAG: String = AlbumAdapter::class.java.simpleName
|
||||
}
|
||||
}
|
|
@ -1,158 +0,0 @@
|
|||
package code.name.monkey.retromusic.adapter.artist
|
||||
|
||||
import android.app.ActivityOptions
|
||||
import android.content.res.ColorStateList
|
||||
import android.content.res.Resources
|
||||
import android.view.LayoutInflater
|
||||
import android.view.MenuItem
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import code.name.monkey.retromusic.R
|
||||
import code.name.monkey.retromusic.adapter.base.AbsMultiSelectAdapter
|
||||
import code.name.monkey.retromusic.adapter.base.MediaEntryViewHolder
|
||||
import code.name.monkey.retromusic.extensions.hide
|
||||
import code.name.monkey.retromusic.glide.ArtistGlideRequest
|
||||
import code.name.monkey.retromusic.glide.RetroMusicColoredTarget
|
||||
import code.name.monkey.retromusic.helper.menu.SongsMenuHelper
|
||||
import code.name.monkey.retromusic.interfaces.CabHolder
|
||||
import code.name.monkey.retromusic.model.Artist
|
||||
import code.name.monkey.retromusic.model.Song
|
||||
import code.name.monkey.retromusic.util.MusicUtil
|
||||
import code.name.monkey.retromusic.util.NavigationUtil
|
||||
import code.name.monkey.retromusic.util.color.MediaNotificationProcessor
|
||||
import com.bumptech.glide.Glide
|
||||
import me.zhanghai.android.fastscroll.PopupTextProvider
|
||||
import java.util.*
|
||||
|
||||
class ArtistAdapter(
|
||||
val activity: AppCompatActivity,
|
||||
var dataSet: List<Artist>,
|
||||
var itemLayoutRes: Int,
|
||||
cabHolder: CabHolder?
|
||||
) : AbsMultiSelectAdapter<ArtistAdapter.ViewHolder, Artist>(
|
||||
activity, cabHolder, R.menu.menu_media_selection
|
||||
), PopupTextProvider {
|
||||
|
||||
init {
|
||||
this.setHasStableIds(true)
|
||||
}
|
||||
|
||||
fun swapDataSet(dataSet: List<Artist>) {
|
||||
this.dataSet = dataSet
|
||||
notifyDataSetChanged()
|
||||
}
|
||||
|
||||
override fun getItemId(position: Int): Long {
|
||||
return dataSet[position].id.toLong()
|
||||
}
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
|
||||
val view =
|
||||
try {
|
||||
LayoutInflater.from(activity).inflate(itemLayoutRes, parent, false)
|
||||
} catch (e: Resources.NotFoundException) {
|
||||
LayoutInflater.from(activity).inflate(R.layout.item_grid_circle, parent, false)
|
||||
}
|
||||
return createViewHolder(view)
|
||||
}
|
||||
|
||||
private fun createViewHolder(view: View): ViewHolder {
|
||||
return ViewHolder(view)
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
|
||||
val artist = dataSet[position]
|
||||
val isChecked = isChecked(artist)
|
||||
holder.itemView.isActivated = isChecked
|
||||
holder.title?.text = artist.name
|
||||
holder.text?.hide()
|
||||
loadArtistImage(artist, holder)
|
||||
}
|
||||
|
||||
fun setColors(processor: MediaNotificationProcessor, holder: ViewHolder) {
|
||||
holder.mask?.backgroundTintList = ColorStateList.valueOf(processor.primaryTextColor)
|
||||
if (holder.paletteColorContainer != null) {
|
||||
holder.paletteColorContainer?.setBackgroundColor(processor.backgroundColor)
|
||||
holder.title?.setTextColor(processor.primaryTextColor)
|
||||
}
|
||||
holder.imageContainerCard?.setCardBackgroundColor(processor.backgroundColor)
|
||||
}
|
||||
|
||||
private fun loadArtistImage(artist: Artist, holder: ViewHolder) {
|
||||
if (holder.image == null) {
|
||||
return
|
||||
}
|
||||
ArtistGlideRequest.Builder.from(Glide.with(activity), artist)
|
||||
.generatePalette(activity)
|
||||
.build()
|
||||
.into(object : RetroMusicColoredTarget(holder.image!!) {
|
||||
override fun onColorReady(colors: MediaNotificationProcessor) {
|
||||
setColors(colors, holder)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
override fun getItemCount(): Int {
|
||||
return dataSet.size
|
||||
}
|
||||
|
||||
override fun getIdentifier(position: Int): Artist? {
|
||||
return dataSet[position]
|
||||
}
|
||||
|
||||
override fun getName(artist: Artist): String {
|
||||
return artist.name
|
||||
}
|
||||
|
||||
override fun onMultipleItemAction(
|
||||
menuItem: MenuItem, selection: ArrayList<Artist>
|
||||
) {
|
||||
SongsMenuHelper.handleMenuClick(activity, getSongList(selection), menuItem.itemId)
|
||||
}
|
||||
|
||||
private fun getSongList(artists: List<Artist>): ArrayList<Song> {
|
||||
val songs = ArrayList<Song>()
|
||||
for (artist in artists) {
|
||||
songs.addAll(artist.songs) // maybe async in future?
|
||||
}
|
||||
return songs
|
||||
}
|
||||
|
||||
override fun getPopupText(position: Int): String {
|
||||
return getSectionName(position)
|
||||
}
|
||||
|
||||
private fun getSectionName(position: Int): String {
|
||||
return MusicUtil.getSectionName(dataSet[position].name)
|
||||
}
|
||||
|
||||
inner class ViewHolder(itemView: View) : MediaEntryViewHolder(itemView) {
|
||||
|
||||
init {
|
||||
setImageTransitionName(activity.getString(R.string.transition_artist_image))
|
||||
menu?.visibility = View.GONE
|
||||
}
|
||||
|
||||
override fun onClick(v: View?) {
|
||||
super.onClick(v)
|
||||
if (isInQuickSelectMode) {
|
||||
toggleChecked(layoutPosition)
|
||||
} else {
|
||||
val activityOptions = ActivityOptions.makeSceneTransitionAnimation(
|
||||
activity,
|
||||
imageContainerCard ?: image,
|
||||
activity.getString(R.string.transition_artist_image)
|
||||
)
|
||||
NavigationUtil.goToArtistOptions(
|
||||
activity, dataSet[layoutPosition].id, activityOptions
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onLongClick(v: View?): Boolean {
|
||||
toggleChecked(layoutPosition)
|
||||
return super.onLongClick(v)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,133 +0,0 @@
|
|||
package code.name.monkey.retromusic.adapter.base;
|
||||
|
||||
import android.content.Context;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuItem;
|
||||
|
||||
import androidx.annotation.MenuRes;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import com.afollestad.materialcab.MaterialCab;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
import code.name.monkey.retromusic.R;
|
||||
import code.name.monkey.retromusic.interfaces.CabHolder;
|
||||
|
||||
|
||||
public abstract class AbsMultiSelectAdapter<V extends RecyclerView.ViewHolder, I> extends RecyclerView.Adapter<V>
|
||||
implements MaterialCab.Callback {
|
||||
|
||||
@Nullable
|
||||
private final CabHolder cabHolder;
|
||||
private final Context context;
|
||||
private MaterialCab cab;
|
||||
private ArrayList<I> checked;
|
||||
private int menuRes;
|
||||
|
||||
public AbsMultiSelectAdapter(@NonNull Context context, @Nullable CabHolder cabHolder, @MenuRes int menuRes) {
|
||||
this.cabHolder = cabHolder;
|
||||
checked = new ArrayList<>();
|
||||
this.menuRes = menuRes;
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCabCreated(MaterialCab materialCab, Menu menu) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCabFinished(MaterialCab materialCab) {
|
||||
clearChecked();
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCabItemClicked(MenuItem menuItem) {
|
||||
if (menuItem.getItemId() == R.id.action_multi_select_adapter_check_all) {
|
||||
checkAll();
|
||||
} else {
|
||||
onMultipleItemAction(menuItem, new ArrayList<>(checked));
|
||||
cab.finish();
|
||||
clearChecked();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
protected void checkAll() {
|
||||
if (cabHolder != null) {
|
||||
checked.clear();
|
||||
for (int i = 0; i < getItemCount(); i++) {
|
||||
I identifier = getIdentifier(i);
|
||||
if (identifier != null) {
|
||||
checked.add(identifier);
|
||||
}
|
||||
}
|
||||
notifyDataSetChanged();
|
||||
updateCab();
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
protected abstract I getIdentifier(int position);
|
||||
|
||||
protected String getName(I object) {
|
||||
return object.toString();
|
||||
}
|
||||
|
||||
protected boolean isChecked(I identifier) {
|
||||
return checked.contains(identifier);
|
||||
}
|
||||
|
||||
protected boolean isInQuickSelectMode() {
|
||||
return cab != null && cab.isActive();
|
||||
}
|
||||
|
||||
protected abstract void onMultipleItemAction(MenuItem menuItem, ArrayList<I> selection);
|
||||
|
||||
protected void setMultiSelectMenuRes(@MenuRes int menuRes) {
|
||||
this.menuRes = menuRes;
|
||||
}
|
||||
|
||||
protected boolean toggleChecked(final int position) {
|
||||
if (cabHolder != null) {
|
||||
I identifier = getIdentifier(position);
|
||||
if (identifier == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!checked.remove(identifier)) {
|
||||
checked.add(identifier);
|
||||
}
|
||||
|
||||
notifyItemChanged(position);
|
||||
updateCab();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private void clearChecked() {
|
||||
checked.clear();
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
|
||||
private void updateCab() {
|
||||
if (cabHolder != null) {
|
||||
if (cab == null || !cab.isActive()) {
|
||||
cab = cabHolder.openCab(menuRes, this);
|
||||
}
|
||||
final int size = checked.size();
|
||||
if (size <= 0) {
|
||||
cab.finish();
|
||||
} else if (size == 1) {
|
||||
cab.setTitle(getName(checked.get(0)));
|
||||
} else {
|
||||
cab.setTitle(context.getString(R.string.x_selected, size));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,139 +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.adapter.base;
|
||||
|
||||
import android.graphics.Color;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.ImageButton;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import com.google.android.material.card.MaterialCardView;
|
||||
import com.h6ah4i.android.widget.advrecyclerview.utils.AbstractDraggableSwipeableItemViewHolder;
|
||||
|
||||
import code.name.monkey.retromusic.R;
|
||||
|
||||
public class MediaEntryViewHolder extends AbstractDraggableSwipeableItemViewHolder
|
||||
implements View.OnLongClickListener, View.OnClickListener {
|
||||
|
||||
@Nullable
|
||||
public View dragView;
|
||||
|
||||
@Nullable
|
||||
public View dummyContainer;
|
||||
|
||||
@Nullable
|
||||
public ImageView image;
|
||||
@Nullable
|
||||
public ImageView artistImage;
|
||||
|
||||
@Nullable
|
||||
public ImageView playerImage;
|
||||
|
||||
@Nullable
|
||||
public ViewGroup imageContainer;
|
||||
|
||||
@Nullable
|
||||
public MaterialCardView imageContainerCard;
|
||||
|
||||
@Nullable
|
||||
public TextView imageText;
|
||||
|
||||
@Nullable
|
||||
public MaterialCardView imageTextContainer;
|
||||
|
||||
@Nullable
|
||||
public View mask;
|
||||
|
||||
@Nullable
|
||||
public View menu;
|
||||
|
||||
@Nullable
|
||||
public View paletteColorContainer;
|
||||
|
||||
@Nullable
|
||||
public ImageButton playSongs;
|
||||
|
||||
@Nullable
|
||||
public RecyclerView recyclerView;
|
||||
|
||||
@Nullable
|
||||
public TextView text;
|
||||
|
||||
@Nullable
|
||||
public TextView time;
|
||||
|
||||
@Nullable
|
||||
public TextView title;
|
||||
|
||||
public MediaEntryViewHolder(@NonNull View itemView) {
|
||||
super(itemView);
|
||||
title = itemView.findViewById(R.id.title);
|
||||
text = itemView.findViewById(R.id.text);
|
||||
|
||||
image = itemView.findViewById(R.id.image);
|
||||
artistImage = itemView.findViewById(R.id.artistImage);
|
||||
playerImage = itemView.findViewById(R.id.player_image);
|
||||
time = itemView.findViewById(R.id.time);
|
||||
|
||||
imageText = itemView.findViewById(R.id.imageText);
|
||||
imageContainer = itemView.findViewById(R.id.imageContainer);
|
||||
imageTextContainer = itemView.findViewById(R.id.imageTextContainer);
|
||||
imageContainerCard = itemView.findViewById(R.id.imageContainerCard);
|
||||
|
||||
menu = itemView.findViewById(R.id.menu);
|
||||
dragView = itemView.findViewById(R.id.drag_view);
|
||||
paletteColorContainer = itemView.findViewById(R.id.paletteColorContainer);
|
||||
recyclerView = itemView.findViewById(R.id.recycler_view);
|
||||
mask = itemView.findViewById(R.id.mask);
|
||||
playSongs = itemView.findViewById(R.id.playSongs);
|
||||
dummyContainer = itemView.findViewById(R.id.dummy_view);
|
||||
|
||||
if (imageContainerCard != null) {
|
||||
imageContainerCard.setCardBackgroundColor(Color.TRANSPARENT);
|
||||
}
|
||||
itemView.setOnClickListener(this);
|
||||
itemView.setOnLongClickListener(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public View getSwipeableContainerView() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onLongClick(View v) {
|
||||
return false;
|
||||
}
|
||||
|
||||
public void setImageTransitionName(@NonNull String transitionName) {
|
||||
if (imageContainerCard != null) {
|
||||
imageContainerCard.setTransitionName(transitionName);
|
||||
}
|
||||
if (image != null) {
|
||||
image.setTransitionName(transitionName);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,225 +0,0 @@
|
|||
package code.name.monkey.retromusic.adapter.playlist
|
||||
|
||||
import android.graphics.Bitmap
|
||||
import android.graphics.Color
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.os.AsyncTask
|
||||
import android.text.TextUtils
|
||||
import android.view.LayoutInflater
|
||||
import android.view.MenuItem
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.appcompat.widget.PopupMenu
|
||||
import code.name.monkey.appthemehelper.ThemeStore
|
||||
import code.name.monkey.appthemehelper.util.ATHUtil
|
||||
import code.name.monkey.appthemehelper.util.TintHelper
|
||||
import code.name.monkey.retromusic.R
|
||||
import code.name.monkey.retromusic.adapter.base.AbsMultiSelectAdapter
|
||||
import code.name.monkey.retromusic.adapter.base.MediaEntryViewHolder
|
||||
import code.name.monkey.retromusic.extensions.hide
|
||||
import code.name.monkey.retromusic.extensions.show
|
||||
import code.name.monkey.retromusic.helper.menu.PlaylistMenuHelper
|
||||
import code.name.monkey.retromusic.helper.menu.SongsMenuHelper
|
||||
import code.name.monkey.retromusic.interfaces.CabHolder
|
||||
import code.name.monkey.retromusic.loaders.PlaylistSongsLoader
|
||||
import code.name.monkey.retromusic.model.AbsCustomPlaylist
|
||||
import code.name.monkey.retromusic.model.Playlist
|
||||
import code.name.monkey.retromusic.model.Song
|
||||
import code.name.monkey.retromusic.model.smartplaylist.AbsSmartPlaylist
|
||||
import code.name.monkey.retromusic.util.AutoGeneratedPlaylistBitmap
|
||||
import code.name.monkey.retromusic.util.MusicUtil
|
||||
import code.name.monkey.retromusic.util.NavigationUtil
|
||||
import code.name.monkey.retromusic.util.RetroColorUtil
|
||||
import java.util.*
|
||||
|
||||
class PlaylistAdapter(
|
||||
private val activity: AppCompatActivity,
|
||||
var dataSet: List<Playlist>,
|
||||
private var itemLayoutRes: Int,
|
||||
cabHolder: CabHolder?
|
||||
) : AbsMultiSelectAdapter<PlaylistAdapter.ViewHolder, Playlist>(
|
||||
activity,
|
||||
cabHolder,
|
||||
R.menu.menu_playlists_selection
|
||||
) {
|
||||
|
||||
|
||||
init {
|
||||
setHasStableIds(true)
|
||||
}
|
||||
|
||||
fun swapDataSet(dataSet: List<Playlist>) {
|
||||
this.dataSet = dataSet
|
||||
notifyDataSetChanged()
|
||||
}
|
||||
|
||||
override fun getItemId(position: Int): Long {
|
||||
return dataSet[position].id.toLong()
|
||||
}
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
|
||||
val view = LayoutInflater.from(activity).inflate(itemLayoutRes, parent, false)
|
||||
return createViewHolder(view)
|
||||
}
|
||||
|
||||
fun createViewHolder(view: View): ViewHolder {
|
||||
return ViewHolder(view)
|
||||
}
|
||||
|
||||
private fun getPlaylistTitle(playlist: Playlist): String {
|
||||
return if (TextUtils.isEmpty(playlist.name)) "-" else playlist.name
|
||||
}
|
||||
|
||||
private fun getPlaylistText(playlist: Playlist): String {
|
||||
return MusicUtil.getPlaylistInfoString(activity, getSongs(playlist))
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
|
||||
val playlist = dataSet[position]
|
||||
holder.itemView.isActivated = isChecked(playlist)
|
||||
holder.title?.text = getPlaylistTitle(playlist)
|
||||
holder.text?.text = getPlaylistText(playlist)
|
||||
holder.image?.setImageDrawable(getIconRes(playlist))
|
||||
val isChecked = isChecked(playlist)
|
||||
if (isChecked) {
|
||||
holder.menu?.hide()
|
||||
} else {
|
||||
holder.menu?.show()
|
||||
}
|
||||
//PlaylistBitmapLoader(this, holder, playlist).execute()
|
||||
}
|
||||
|
||||
private fun getIconRes(playlist: Playlist): Drawable {
|
||||
return if (MusicUtil.isFavoritePlaylist(activity, playlist))
|
||||
TintHelper.createTintedDrawable(
|
||||
activity,
|
||||
R.drawable.ic_favorite_white_24dp,
|
||||
ThemeStore.accentColor(activity)
|
||||
)
|
||||
else TintHelper.createTintedDrawable(
|
||||
activity,
|
||||
R.drawable.ic_playlist_play_white_24dp,
|
||||
ATHUtil.resolveColor(activity, R.attr.colorControlNormal)
|
||||
)
|
||||
}
|
||||
|
||||
override fun getItemViewType(position: Int): Int {
|
||||
return if (dataSet[position] is AbsSmartPlaylist) SMART_PLAYLIST else DEFAULT_PLAYLIST
|
||||
}
|
||||
|
||||
override fun getItemCount(): Int {
|
||||
return dataSet.size
|
||||
}
|
||||
|
||||
override fun getIdentifier(position: Int): Playlist? {
|
||||
return dataSet[position]
|
||||
}
|
||||
|
||||
override fun getName(playlist: Playlist): String {
|
||||
return playlist.name
|
||||
}
|
||||
|
||||
override fun onMultipleItemAction(menuItem: MenuItem, selection: ArrayList<Playlist>) {
|
||||
when (menuItem.itemId) {
|
||||
else -> SongsMenuHelper.handleMenuClick(
|
||||
activity,
|
||||
getSongList(selection),
|
||||
menuItem.itemId
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun getSongList(playlists: List<Playlist>): ArrayList<Song> {
|
||||
val songs = ArrayList<Song>()
|
||||
for (playlist in playlists) {
|
||||
if (playlist is AbsCustomPlaylist) {
|
||||
songs.addAll(playlist.getSongs(activity))
|
||||
} else {
|
||||
songs.addAll(PlaylistSongsLoader.getPlaylistSongList(activity, playlist.id))
|
||||
}
|
||||
}
|
||||
return songs
|
||||
}
|
||||
|
||||
private fun getSongs(playlist: Playlist): ArrayList<Song> {
|
||||
val songs = ArrayList<Song>()
|
||||
if (playlist is AbsSmartPlaylist) {
|
||||
songs.addAll(playlist.getSongs(activity))
|
||||
} else {
|
||||
songs.addAll(PlaylistSongsLoader.getPlaylistSongList(activity, playlist.id))
|
||||
}
|
||||
return songs
|
||||
}
|
||||
|
||||
inner class ViewHolder(itemView: View) : MediaEntryViewHolder(itemView) {
|
||||
init {
|
||||
|
||||
image?.apply {
|
||||
val iconPadding =
|
||||
activity.resources.getDimensionPixelSize(R.dimen.list_item_image_icon_padding)
|
||||
setPadding(iconPadding, iconPadding, iconPadding, iconPadding)
|
||||
}
|
||||
|
||||
menu?.setOnClickListener { view ->
|
||||
val popupMenu = PopupMenu(activity, view)
|
||||
popupMenu.inflate(R.menu.menu_item_playlist)
|
||||
popupMenu.setOnMenuItemClickListener { item ->
|
||||
PlaylistMenuHelper.handleMenuClick(
|
||||
activity, dataSet[layoutPosition], item
|
||||
)
|
||||
}
|
||||
popupMenu.show()
|
||||
}
|
||||
|
||||
imageTextContainer?.apply {
|
||||
cardElevation = 0f
|
||||
setCardBackgroundColor(Color.TRANSPARENT)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onClick(v: View?) {
|
||||
if (isInQuickSelectMode) {
|
||||
toggleChecked(layoutPosition)
|
||||
} else {
|
||||
val playlist = dataSet[layoutPosition]
|
||||
NavigationUtil.goToPlaylistNew(activity, playlist)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onLongClick(v: View?): Boolean {
|
||||
toggleChecked(layoutPosition)
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
class PlaylistBitmapLoader(
|
||||
private var adapter: PlaylistAdapter,
|
||||
private var viewHolder: ViewHolder,
|
||||
private var playlist: Playlist
|
||||
) : AsyncTask<Void, Void, Bitmap>() {
|
||||
|
||||
override fun doInBackground(vararg params: Void?): Bitmap {
|
||||
val songs = PlaylistSongsLoader.getPlaylistSongList(adapter.activity, playlist)
|
||||
return AutoGeneratedPlaylistBitmap.getBitmap(adapter.activity, songs, false, false)
|
||||
}
|
||||
|
||||
override fun onPostExecute(result: Bitmap?) {
|
||||
super.onPostExecute(result)
|
||||
viewHolder.image?.setImageBitmap(result)
|
||||
val color = RetroColorUtil.getColor(
|
||||
RetroColorUtil.generatePalette(
|
||||
result
|
||||
),
|
||||
ATHUtil.resolveColor(adapter.activity, R.attr.colorSurface)
|
||||
)
|
||||
viewHolder.paletteColorContainer?.setBackgroundColor(color)
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
val TAG: String = PlaylistAdapter::class.java.simpleName
|
||||
private const val SMART_PLAYLIST = 0
|
||||
private const val DEFAULT_PLAYLIST = 1
|
||||
}
|
||||
}
|
|
@ -1,79 +0,0 @@
|
|||
package code.name.monkey.retromusic.adapter.song
|
||||
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.annotation.LayoutRes
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import code.name.monkey.retromusic.R
|
||||
import code.name.monkey.retromusic.helper.MusicPlayerRemote
|
||||
import code.name.monkey.retromusic.interfaces.CabHolder
|
||||
import code.name.monkey.retromusic.model.Song
|
||||
|
||||
abstract class AbsOffsetSongAdapter(
|
||||
activity: AppCompatActivity,
|
||||
dataSet: MutableList<Song>,
|
||||
@LayoutRes itemLayoutRes: Int,
|
||||
cabHolder: CabHolder?
|
||||
) : SongAdapter(activity, dataSet, itemLayoutRes, cabHolder) {
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): SongAdapter.ViewHolder {
|
||||
if (viewType == OFFSET_ITEM) {
|
||||
val view = LayoutInflater.from(activity)
|
||||
.inflate(R.layout.item_list_quick_actions, parent, false)
|
||||
return createViewHolder(view)
|
||||
}
|
||||
return super.onCreateViewHolder(parent, viewType)
|
||||
}
|
||||
|
||||
override fun createViewHolder(view: View): SongAdapter.ViewHolder {
|
||||
return ViewHolder(view)
|
||||
}
|
||||
|
||||
override fun getItemId(position: Int): Long {
|
||||
var positionFinal = position
|
||||
positionFinal--
|
||||
return if (positionFinal < 0) -2 else super.getItemId(positionFinal)
|
||||
}
|
||||
|
||||
override fun getIdentifier(position: Int): Song? {
|
||||
var positionFinal = position
|
||||
positionFinal--
|
||||
return if (positionFinal < 0) null else super.getIdentifier(positionFinal)
|
||||
}
|
||||
|
||||
override fun getItemCount(): Int {
|
||||
val superItemCount = super.getItemCount()
|
||||
return if (superItemCount == 0) 0 else superItemCount + 1
|
||||
}
|
||||
|
||||
override fun getItemViewType(position: Int): Int {
|
||||
return if (position == 0) OFFSET_ITEM else SONG
|
||||
}
|
||||
|
||||
open inner class ViewHolder(itemView: View) : SongAdapter.ViewHolder(itemView) {
|
||||
|
||||
override // could also return null, just to be safe return empty song
|
||||
val song: Song
|
||||
get() = if (itemViewType == OFFSET_ITEM) Song.emptySong else dataSet[layoutPosition - 1]
|
||||
|
||||
override fun onClick(v: View?) {
|
||||
if (isInQuickSelectMode && itemViewType != OFFSET_ITEM) {
|
||||
toggleChecked(layoutPosition)
|
||||
} else {
|
||||
MusicPlayerRemote.openQueue(dataSet, layoutPosition - 1, true)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onLongClick(v: View?): Boolean {
|
||||
if (itemViewType == OFFSET_ITEM) return false
|
||||
toggleChecked(layoutPosition)
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val OFFSET_ITEM = 0
|
||||
const val SONG = 1
|
||||
}
|
||||
}
|
|
@ -1,139 +0,0 @@
|
|||
package code.name.monkey.retromusic.adapter.song
|
||||
|
||||
import android.view.MenuItem
|
||||
import android.view.View
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import code.name.monkey.retromusic.R
|
||||
import code.name.monkey.retromusic.R.menu
|
||||
import code.name.monkey.retromusic.dialogs.RemoveFromPlaylistDialog
|
||||
import code.name.monkey.retromusic.interfaces.CabHolder
|
||||
import code.name.monkey.retromusic.model.PlaylistSong
|
||||
import code.name.monkey.retromusic.model.Song
|
||||
import code.name.monkey.retromusic.util.ViewUtil
|
||||
import com.h6ah4i.android.widget.advrecyclerview.draggable.DraggableItemAdapter
|
||||
import com.h6ah4i.android.widget.advrecyclerview.draggable.DraggableItemViewHolder
|
||||
import com.h6ah4i.android.widget.advrecyclerview.draggable.ItemDraggableRange
|
||||
import com.h6ah4i.android.widget.advrecyclerview.draggable.annotation.DraggableItemStateFlags
|
||||
|
||||
class OrderablePlaylistSongAdapter(
|
||||
activity: AppCompatActivity,
|
||||
dataSet: ArrayList<Song>,
|
||||
itemLayoutRes: Int,
|
||||
cabHolder: CabHolder?,
|
||||
private val onMoveItemListener: OnMoveItemListener?
|
||||
) : PlaylistSongAdapter(
|
||||
activity, dataSet, itemLayoutRes, cabHolder
|
||||
), DraggableItemAdapter<OrderablePlaylistSongAdapter.ViewHolder> {
|
||||
|
||||
init {
|
||||
setMultiSelectMenuRes(menu.menu_playlists_songs_selection)
|
||||
}
|
||||
|
||||
override fun createViewHolder(view: View): SongAdapter.ViewHolder {
|
||||
return ViewHolder(view)
|
||||
}
|
||||
|
||||
override fun getItemId(position: Int): Long {
|
||||
var positionFinal = position
|
||||
positionFinal--
|
||||
|
||||
var long: Long = 0
|
||||
if (positionFinal < 0) {
|
||||
long = -2
|
||||
} else {
|
||||
if (dataSet[positionFinal] is PlaylistSong) {
|
||||
long = (dataSet[positionFinal] as PlaylistSong).idInPlayList.toLong()
|
||||
}
|
||||
}
|
||||
return long
|
||||
}
|
||||
|
||||
override fun onMultipleItemAction(menuItem: MenuItem, selection: ArrayList<Song>) {
|
||||
when (menuItem.itemId) {
|
||||
R.id.action_remove_from_playlist -> {
|
||||
RemoveFromPlaylistDialog.create(selection as ArrayList<PlaylistSong>)
|
||||
.show(activity.supportFragmentManager, "ADD_PLAYLIST")
|
||||
return
|
||||
}
|
||||
}
|
||||
super.onMultipleItemAction(menuItem, selection)
|
||||
}
|
||||
|
||||
override fun onCheckCanStartDrag(holder: ViewHolder, position: Int, x: Int, y: Int): Boolean {
|
||||
return onMoveItemListener != null && position > 0 && (ViewUtil.hitTest(
|
||||
holder.dragView!!, x, y
|
||||
) || ViewUtil.hitTest(holder.image!!, x, y))
|
||||
}
|
||||
|
||||
override fun onGetItemDraggableRange(holder: ViewHolder, position: Int): ItemDraggableRange {
|
||||
return ItemDraggableRange(1, dataSet.size)
|
||||
}
|
||||
|
||||
override fun onMoveItem(fromPosition: Int, toPosition: Int) {
|
||||
if (onMoveItemListener != null && fromPosition != toPosition) {
|
||||
onMoveItemListener.onMoveItem(fromPosition - 1, toPosition - 1)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCheckCanDrop(draggingPosition: Int, dropPosition: Int): Boolean {
|
||||
return dropPosition > 0
|
||||
}
|
||||
|
||||
override fun onItemDragStarted(position: Int) {
|
||||
notifyDataSetChanged()
|
||||
}
|
||||
|
||||
override fun onItemDragFinished(fromPosition: Int, toPosition: Int, result: Boolean) {
|
||||
notifyDataSetChanged()
|
||||
}
|
||||
|
||||
interface OnMoveItemListener {
|
||||
fun onMoveItem(fromPosition: Int, toPosition: Int)
|
||||
}
|
||||
|
||||
inner class ViewHolder(itemView: View) : PlaylistSongAdapter.ViewHolder(itemView),
|
||||
DraggableItemViewHolder {
|
||||
@DraggableItemStateFlags
|
||||
private var mDragStateFlags: Int = 0
|
||||
|
||||
override var songMenuRes: Int
|
||||
get() = R.menu.menu_item_playlist_song
|
||||
set(value) {
|
||||
super.songMenuRes = value
|
||||
}
|
||||
|
||||
init {
|
||||
if (dragView != null) {
|
||||
if (onMoveItemListener != null) {
|
||||
dragView?.visibility = View.VISIBLE
|
||||
} else {
|
||||
dragView?.visibility = View.GONE
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onSongMenuItemClick(item: MenuItem): Boolean {
|
||||
when (item.itemId) {
|
||||
R.id.action_remove_from_playlist -> {
|
||||
RemoveFromPlaylistDialog.create(song as PlaylistSong)
|
||||
.show(activity.supportFragmentManager, "REMOVE_FROM_PLAYLIST")
|
||||
return true
|
||||
}
|
||||
}
|
||||
return super.onSongMenuItemClick(item)
|
||||
}
|
||||
|
||||
@DraggableItemStateFlags
|
||||
override fun getDragStateFlags(): Int {
|
||||
return mDragStateFlags
|
||||
}
|
||||
|
||||
override fun setDragStateFlags(@DraggableItemStateFlags flags: Int) {
|
||||
mDragStateFlags = flags
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
val TAG: String = OrderablePlaylistSongAdapter::class.java.simpleName
|
||||
}
|
||||
}
|
|
@ -1,217 +0,0 @@
|
|||
package code.name.monkey.retromusic.adapter.song
|
||||
|
||||
import android.view.MenuItem
|
||||
import android.view.View
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import code.name.monkey.retromusic.R
|
||||
import code.name.monkey.retromusic.helper.MusicPlayerRemote
|
||||
import code.name.monkey.retromusic.helper.MusicPlayerRemote.isPlaying
|
||||
import code.name.monkey.retromusic.helper.MusicPlayerRemote.playNextSong
|
||||
import code.name.monkey.retromusic.helper.MusicPlayerRemote.removeFromQueue
|
||||
import code.name.monkey.retromusic.model.Song
|
||||
import code.name.monkey.retromusic.util.MusicUtil
|
||||
import code.name.monkey.retromusic.util.ViewUtil
|
||||
import com.h6ah4i.android.widget.advrecyclerview.draggable.DraggableItemAdapter
|
||||
import com.h6ah4i.android.widget.advrecyclerview.draggable.ItemDraggableRange
|
||||
import com.h6ah4i.android.widget.advrecyclerview.draggable.annotation.DraggableItemStateFlags
|
||||
import com.h6ah4i.android.widget.advrecyclerview.swipeable.SwipeableItemAdapter
|
||||
import com.h6ah4i.android.widget.advrecyclerview.swipeable.SwipeableItemConstants
|
||||
import com.h6ah4i.android.widget.advrecyclerview.swipeable.action.SwipeResultAction
|
||||
import com.h6ah4i.android.widget.advrecyclerview.swipeable.action.SwipeResultActionDefault
|
||||
import com.h6ah4i.android.widget.advrecyclerview.swipeable.action.SwipeResultActionRemoveItem
|
||||
import com.h6ah4i.android.widget.advrecyclerview.swipeable.annotation.SwipeableItemResults
|
||||
import me.zhanghai.android.fastscroll.PopupTextProvider
|
||||
|
||||
class PlayingQueueAdapter(
|
||||
activity: AppCompatActivity,
|
||||
dataSet: MutableList<Song>,
|
||||
private var current: Int,
|
||||
itemLayoutRes: Int
|
||||
) : SongAdapter(
|
||||
activity, dataSet, itemLayoutRes, null
|
||||
), DraggableItemAdapter<PlayingQueueAdapter.ViewHolder>,
|
||||
SwipeableItemAdapter<PlayingQueueAdapter.ViewHolder>,
|
||||
PopupTextProvider {
|
||||
|
||||
private var songToRemove: Song? = null
|
||||
|
||||
override fun createViewHolder(view: View): SongAdapter.ViewHolder {
|
||||
return ViewHolder(view)
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: SongAdapter.ViewHolder, position: Int) {
|
||||
super.onBindViewHolder(holder, position)
|
||||
holder.imageText?.text = (position - current).toString()
|
||||
holder.time?.text = MusicUtil.getReadableDurationString(dataSet[position].duration)
|
||||
if (holder.itemViewType == HISTORY || holder.itemViewType == CURRENT) {
|
||||
setAlpha(holder, 0.5f)
|
||||
}
|
||||
}
|
||||
|
||||
override fun getItemViewType(position: Int): Int {
|
||||
if (position < current) {
|
||||
return HISTORY
|
||||
} else if (position > current) {
|
||||
return UP_NEXT
|
||||
}
|
||||
return CURRENT
|
||||
}
|
||||
|
||||
override fun loadAlbumCover(song: Song, holder: SongAdapter.ViewHolder) {
|
||||
// We don't want to load it in this adapter
|
||||
}
|
||||
|
||||
fun swapDataSet(dataSet: List<Song>, position: Int) {
|
||||
this.dataSet = dataSet.toMutableList()
|
||||
current = position
|
||||
notifyDataSetChanged()
|
||||
}
|
||||
|
||||
fun setCurrent(current: Int) {
|
||||
this.current = current
|
||||
notifyDataSetChanged()
|
||||
}
|
||||
|
||||
private fun setAlpha(holder: SongAdapter.ViewHolder, alpha: Float) {
|
||||
holder.image?.alpha = alpha
|
||||
holder.title?.alpha = alpha
|
||||
holder.text?.alpha = alpha
|
||||
holder.imageText?.alpha = alpha
|
||||
holder.paletteColorContainer?.alpha = alpha
|
||||
holder.dragView?.alpha = alpha
|
||||
holder.menu?.alpha = alpha
|
||||
}
|
||||
|
||||
override fun getPopupText(position: Int): String {
|
||||
return MusicUtil.getSectionName(dataSet[position].title)
|
||||
}
|
||||
|
||||
override fun onCheckCanStartDrag(holder: ViewHolder, position: Int, x: Int, y: Int): Boolean {
|
||||
return ViewUtil.hitTest(holder.imageText!!, x, y) || ViewUtil.hitTest(
|
||||
holder.dragView!!,
|
||||
x,
|
||||
y
|
||||
)
|
||||
}
|
||||
|
||||
override fun onGetItemDraggableRange(holder: ViewHolder, position: Int): ItemDraggableRange? {
|
||||
return null
|
||||
}
|
||||
|
||||
override fun onMoveItem(fromPosition: Int, toPosition: Int) {
|
||||
MusicPlayerRemote.moveSong(fromPosition, toPosition)
|
||||
}
|
||||
|
||||
override fun onCheckCanDrop(draggingPosition: Int, dropPosition: Int): Boolean {
|
||||
return true
|
||||
}
|
||||
|
||||
override fun onItemDragStarted(position: Int) {
|
||||
notifyDataSetChanged()
|
||||
}
|
||||
|
||||
override fun onItemDragFinished(fromPosition: Int, toPosition: Int, result: Boolean) {
|
||||
notifyDataSetChanged()
|
||||
}
|
||||
|
||||
fun setSongToRemove(song: Song) {
|
||||
songToRemove = song
|
||||
}
|
||||
|
||||
inner class ViewHolder(itemView: View) : SongAdapter.ViewHolder(itemView) {
|
||||
@DraggableItemStateFlags
|
||||
private var mDragStateFlags: Int = 0
|
||||
|
||||
override var songMenuRes: Int
|
||||
get() = R.menu.menu_item_playing_queue_song
|
||||
set(value) {
|
||||
super.songMenuRes = value
|
||||
}
|
||||
|
||||
init {
|
||||
imageText?.visibility = View.VISIBLE
|
||||
dragView?.visibility = View.VISIBLE
|
||||
}
|
||||
|
||||
override fun onSongMenuItemClick(item: MenuItem): Boolean {
|
||||
when (item.itemId) {
|
||||
R.id.action_remove_from_playing_queue -> {
|
||||
removeFromQueue(layoutPosition)
|
||||
return true
|
||||
}
|
||||
}
|
||||
return super.onSongMenuItemClick(item)
|
||||
}
|
||||
|
||||
@DraggableItemStateFlags
|
||||
override fun getDragStateFlags(): Int {
|
||||
return mDragStateFlags
|
||||
}
|
||||
|
||||
override fun setDragStateFlags(@DraggableItemStateFlags flags: Int) {
|
||||
mDragStateFlags = flags
|
||||
}
|
||||
|
||||
override fun getSwipeableContainerView(): View? {
|
||||
return dummyContainer
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
private const val HISTORY = 0
|
||||
private const val CURRENT = 1
|
||||
private const val UP_NEXT = 2
|
||||
}
|
||||
|
||||
override fun onSwipeItem(
|
||||
holder: ViewHolder?,
|
||||
position: Int, @SwipeableItemResults result: Int
|
||||
): SwipeResultAction {
|
||||
return if (result === SwipeableItemConstants.RESULT_CANCELED) {
|
||||
SwipeResultActionDefault()
|
||||
} else {
|
||||
SwipedResultActionRemoveItem(this, position, activity)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onGetSwipeReactionType(holder: ViewHolder?, position: Int, x: Int, y: Int): Int {
|
||||
return if (onCheckCanStartDrag(holder!!, position, x, y)) {
|
||||
SwipeableItemConstants.REACTION_CAN_NOT_SWIPE_BOTH_H
|
||||
} else {
|
||||
SwipeableItemConstants.REACTION_CAN_SWIPE_BOTH_H
|
||||
}
|
||||
}
|
||||
|
||||
override fun onSwipeItemStarted(p0: ViewHolder?, p1: Int) {
|
||||
}
|
||||
|
||||
override fun onSetSwipeBackground(holder: ViewHolder?, position: Int, result: Int) {
|
||||
}
|
||||
|
||||
internal class SwipedResultActionRemoveItem(
|
||||
private val adapter: PlayingQueueAdapter,
|
||||
private val position: Int,
|
||||
private val activity: AppCompatActivity
|
||||
) : SwipeResultActionRemoveItem() {
|
||||
|
||||
private var songToRemove: Song? = null
|
||||
private val isPlaying: Boolean = MusicPlayerRemote.isPlaying
|
||||
private val songProgressMillis = 0
|
||||
override fun onPerformAction() {
|
||||
//currentlyShownSnackbar = null
|
||||
}
|
||||
|
||||
override fun onSlideAnimationEnd() {
|
||||
//initializeSnackBar(adapter, position, activity, isPlaying)
|
||||
songToRemove = adapter.dataSet[position]
|
||||
//If song removed was the playing song, then play the next song
|
||||
if (isPlaying(songToRemove!!)) {
|
||||
playNextSong()
|
||||
}
|
||||
//Swipe animation is much smoother when we do the heavy lifting after it's completed
|
||||
adapter.setSongToRemove(songToRemove!!)
|
||||
removeFromQueue(songToRemove!!)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,75 +0,0 @@
|
|||
package code.name.monkey.retromusic.adapter.song
|
||||
|
||||
import android.app.ActivityOptions
|
||||
import android.view.MenuItem
|
||||
import android.view.View
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import code.name.monkey.retromusic.R
|
||||
import code.name.monkey.retromusic.helper.MusicPlayerRemote
|
||||
import code.name.monkey.retromusic.interfaces.CabHolder
|
||||
import code.name.monkey.retromusic.model.Song
|
||||
import code.name.monkey.retromusic.util.NavigationUtil
|
||||
import com.google.android.material.button.MaterialButton
|
||||
|
||||
open class PlaylistSongAdapter(
|
||||
activity: AppCompatActivity,
|
||||
dataSet: MutableList<Song>,
|
||||
itemLayoutRes: Int,
|
||||
cabHolder: CabHolder?
|
||||
) : AbsOffsetSongAdapter(activity, dataSet, itemLayoutRes, cabHolder) {
|
||||
|
||||
init {
|
||||
this.setMultiSelectMenuRes(R.menu.menu_cannot_delete_single_songs_playlist_songs_selection)
|
||||
}
|
||||
|
||||
override fun createViewHolder(view: View): SongAdapter.ViewHolder {
|
||||
return ViewHolder(view)
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: SongAdapter.ViewHolder, position: Int) {
|
||||
if (holder.itemViewType == OFFSET_ITEM) {
|
||||
val viewHolder = holder as ViewHolder
|
||||
viewHolder.playAction?.let {
|
||||
it.setOnClickListener {
|
||||
MusicPlayerRemote.openQueue(dataSet, 0, true)
|
||||
}
|
||||
}
|
||||
viewHolder.shuffleAction?.let {
|
||||
it.setOnClickListener {
|
||||
MusicPlayerRemote.openAndShuffleQueue(dataSet, true)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
super.onBindViewHolder(holder, position - 1)
|
||||
}
|
||||
}
|
||||
|
||||
open inner class ViewHolder(itemView: View) : AbsOffsetSongAdapter.ViewHolder(itemView) {
|
||||
|
||||
val playAction: MaterialButton? = itemView.findViewById(R.id.playAction)
|
||||
val shuffleAction: MaterialButton? = itemView.findViewById(R.id.shuffleAction)
|
||||
|
||||
override var songMenuRes: Int
|
||||
get() = R.menu.menu_item_cannot_delete_single_songs_playlist_song
|
||||
set(value) {
|
||||
super.songMenuRes = value
|
||||
}
|
||||
|
||||
override fun onSongMenuItemClick(item: MenuItem): Boolean {
|
||||
if (item.itemId == R.id.action_go_to_album) {
|
||||
val activityOptions = ActivityOptions.makeSceneTransitionAnimation(
|
||||
activity,
|
||||
imageContainerCard ?: image,
|
||||
activity.getString(R.string.transition_album_art)
|
||||
)
|
||||
NavigationUtil.goToAlbumOptions(activity, song.albumId, activityOptions)
|
||||
return true
|
||||
}
|
||||
return super.onSongMenuItemClick(item)
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
val TAG: String = PlaylistSongAdapter::class.java.simpleName
|
||||
}
|
||||
}
|
|
@ -1,52 +0,0 @@
|
|||
package code.name.monkey.retromusic.adapter.song
|
||||
|
||||
import android.view.View
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import code.name.monkey.retromusic.R
|
||||
import code.name.monkey.retromusic.helper.MusicPlayerRemote
|
||||
import code.name.monkey.retromusic.interfaces.CabHolder
|
||||
import code.name.monkey.retromusic.model.Song
|
||||
import com.google.android.material.button.MaterialButton
|
||||
|
||||
class ShuffleButtonSongAdapter(
|
||||
activity: AppCompatActivity,
|
||||
dataSet: MutableList<Song>,
|
||||
itemLayoutRes: Int,
|
||||
cabHolder: CabHolder?
|
||||
) : AbsOffsetSongAdapter(activity, dataSet, itemLayoutRes, cabHolder) {
|
||||
|
||||
override fun createViewHolder(view: View): SongAdapter.ViewHolder {
|
||||
return ViewHolder(view)
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: SongAdapter.ViewHolder, position: Int) {
|
||||
if (holder.itemViewType == OFFSET_ITEM) {
|
||||
val viewHolder = holder as ViewHolder
|
||||
viewHolder.playAction?.let {
|
||||
it.setOnClickListener {
|
||||
MusicPlayerRemote.openQueue(dataSet, 0, true)
|
||||
}
|
||||
}
|
||||
viewHolder.shuffleAction?.let {
|
||||
it.setOnClickListener {
|
||||
MusicPlayerRemote.openAndShuffleQueue(dataSet, true)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
super.onBindViewHolder(holder, position - 1)
|
||||
}
|
||||
}
|
||||
|
||||
inner class ViewHolder(itemView: View) : AbsOffsetSongAdapter.ViewHolder(itemView) {
|
||||
val playAction: MaterialButton? = itemView.findViewById(R.id.playAction)
|
||||
val shuffleAction: MaterialButton? = itemView.findViewById(R.id.shuffleAction)
|
||||
|
||||
override fun onClick(v: View?) {
|
||||
if (itemViewType == OFFSET_ITEM) {
|
||||
MusicPlayerRemote.openAndShuffleQueue(dataSet, true)
|
||||
return
|
||||
}
|
||||
super.onClick(v)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,38 +0,0 @@
|
|||
package code.name.monkey.retromusic.adapter.song
|
||||
|
||||
import android.view.LayoutInflater
|
||||
import android.view.ViewGroup
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import code.name.monkey.retromusic.interfaces.CabHolder
|
||||
import code.name.monkey.retromusic.model.Song
|
||||
import code.name.monkey.retromusic.util.MusicUtil
|
||||
import java.util.*
|
||||
|
||||
class SimpleSongAdapter(
|
||||
context: AppCompatActivity,
|
||||
songs: ArrayList<Song>,
|
||||
layoutRes: Int,
|
||||
cabHolder: CabHolder?
|
||||
) : SongAdapter(context, songs, layoutRes, cabHolder) {
|
||||
|
||||
override fun swapDataSet(dataSet: List<Song>) {
|
||||
this.dataSet = dataSet.toMutableList()
|
||||
notifyDataSetChanged()
|
||||
}
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
|
||||
return ViewHolder(LayoutInflater.from(activity).inflate(itemLayoutRes, parent, false))
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
|
||||
super.onBindViewHolder(holder, position)
|
||||
val fixedTrackNumber = MusicUtil.getFixedTrackNumber(dataSet[position].trackNumber)
|
||||
|
||||
holder.imageText?.text = if (fixedTrackNumber > 0) fixedTrackNumber.toString() else "-"
|
||||
holder.time?.text = MusicUtil.getReadableDurationString(dataSet[position].duration)
|
||||
}
|
||||
|
||||
override fun getItemCount(): Int {
|
||||
return dataSet.size
|
||||
}
|
||||
}
|
|
@ -1,216 +0,0 @@
|
|||
package code.name.monkey.retromusic.adapter.song
|
||||
|
||||
import android.app.ActivityOptions
|
||||
import android.content.res.ColorStateList
|
||||
import android.content.res.Resources
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.view.LayoutInflater
|
||||
import android.view.MenuItem
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import code.name.monkey.retromusic.R
|
||||
import code.name.monkey.retromusic.adapter.base.AbsMultiSelectAdapter
|
||||
import code.name.monkey.retromusic.adapter.base.MediaEntryViewHolder
|
||||
import code.name.monkey.retromusic.extensions.hide
|
||||
import code.name.monkey.retromusic.extensions.show
|
||||
import code.name.monkey.retromusic.glide.RetroMusicColoredTarget
|
||||
import code.name.monkey.retromusic.glide.SongGlideRequest
|
||||
import code.name.monkey.retromusic.helper.MusicPlayerRemote
|
||||
import code.name.monkey.retromusic.helper.SortOrder
|
||||
import code.name.monkey.retromusic.helper.menu.SongMenuHelper
|
||||
import code.name.monkey.retromusic.helper.menu.SongsMenuHelper
|
||||
import code.name.monkey.retromusic.interfaces.CabHolder
|
||||
import code.name.monkey.retromusic.model.Song
|
||||
import code.name.monkey.retromusic.util.MusicUtil
|
||||
import code.name.monkey.retromusic.util.NavigationUtil
|
||||
import code.name.monkey.retromusic.util.PreferenceUtil
|
||||
|
||||
import code.name.monkey.retromusic.util.color.MediaNotificationProcessor
|
||||
import com.afollestad.materialcab.MaterialCab
|
||||
import com.bumptech.glide.Glide
|
||||
import me.zhanghai.android.fastscroll.PopupTextProvider
|
||||
import java.util.*
|
||||
|
||||
/**
|
||||
* Created by hemanths on 13/08/17.
|
||||
*/
|
||||
|
||||
open class SongAdapter(
|
||||
protected val activity: AppCompatActivity,
|
||||
var dataSet: MutableList<Song>,
|
||||
protected var itemLayoutRes: Int,
|
||||
cabHolder: CabHolder?,
|
||||
showSectionName: Boolean = true
|
||||
) : AbsMultiSelectAdapter<SongAdapter.ViewHolder, Song>(
|
||||
activity,
|
||||
cabHolder,
|
||||
R.menu.menu_media_selection
|
||||
), MaterialCab.Callback, PopupTextProvider {
|
||||
|
||||
private var showSectionName = true
|
||||
|
||||
init {
|
||||
this.showSectionName = showSectionName
|
||||
this.setHasStableIds(true)
|
||||
}
|
||||
|
||||
open fun swapDataSet(dataSet: List<Song>) {
|
||||
this.dataSet = dataSet.toMutableList()
|
||||
notifyDataSetChanged()
|
||||
}
|
||||
|
||||
override fun getItemId(position: Int): Long {
|
||||
return dataSet[position].id.toLong()
|
||||
}
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
|
||||
val view =
|
||||
try {
|
||||
LayoutInflater.from(activity).inflate(itemLayoutRes, parent, false)
|
||||
} catch (e: Resources.NotFoundException) {
|
||||
LayoutInflater.from(activity).inflate(R.layout.item_list, parent, false)
|
||||
}
|
||||
return createViewHolder(view)
|
||||
}
|
||||
|
||||
protected open fun createViewHolder(view: View): ViewHolder {
|
||||
return ViewHolder(view)
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
|
||||
val song = dataSet[position]
|
||||
val isChecked = isChecked(song)
|
||||
holder.itemView.isActivated = isChecked
|
||||
if (isChecked) {
|
||||
holder.menu?.hide()
|
||||
} else {
|
||||
holder.menu?.show()
|
||||
}
|
||||
holder.title?.text = getSongTitle(song)
|
||||
holder.text?.text = getSongText(song)
|
||||
loadAlbumCover(song, holder)
|
||||
}
|
||||
|
||||
private fun setColors(color: MediaNotificationProcessor, holder: ViewHolder) {
|
||||
if (holder.paletteColorContainer != null) {
|
||||
holder.title?.setTextColor(color.primaryTextColor)
|
||||
holder.text?.setTextColor(color.secondaryTextColor)
|
||||
holder.paletteColorContainer?.setBackgroundColor(color.backgroundColor)
|
||||
}
|
||||
holder.mask?.backgroundTintList = ColorStateList.valueOf(color.primaryTextColor)
|
||||
}
|
||||
|
||||
protected open fun loadAlbumCover(song: Song, holder: ViewHolder) {
|
||||
if (holder.image == null) {
|
||||
return
|
||||
}
|
||||
SongGlideRequest.Builder.from(Glide.with(activity), song)
|
||||
.checkIgnoreMediaStore(activity)
|
||||
.generatePalette(activity).build()
|
||||
.into(object : RetroMusicColoredTarget(holder.image!!) {
|
||||
override fun onLoadCleared(placeholder: Drawable?) {
|
||||
super.onLoadCleared(placeholder)
|
||||
|
||||
}
|
||||
|
||||
override fun onColorReady(colors: MediaNotificationProcessor) {
|
||||
setColors(colors, holder)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
private fun getSongTitle(song: Song): String? {
|
||||
return song.title
|
||||
}
|
||||
|
||||
private fun getSongText(song: Song): String? {
|
||||
return song.artistName
|
||||
}
|
||||
|
||||
override fun getItemCount(): Int {
|
||||
return dataSet.size
|
||||
}
|
||||
|
||||
override fun getIdentifier(position: Int): Song? {
|
||||
return dataSet[position]
|
||||
}
|
||||
|
||||
override fun getName(song: Song): String {
|
||||
return song.title
|
||||
}
|
||||
|
||||
override fun onMultipleItemAction(menuItem: MenuItem, selection: ArrayList<Song>) {
|
||||
SongsMenuHelper.handleMenuClick(activity, selection, menuItem.itemId)
|
||||
}
|
||||
|
||||
override fun getPopupText(position: Int): String {
|
||||
val sectionName: String? = when (PreferenceUtil.songSortOrder) {
|
||||
SortOrder.SongSortOrder.SONG_A_Z, SortOrder.SongSortOrder.SONG_Z_A -> dataSet[position].title
|
||||
SortOrder.SongSortOrder.SONG_ALBUM -> dataSet[position].albumName
|
||||
SortOrder.SongSortOrder.SONG_ARTIST -> dataSet[position].artistName
|
||||
SortOrder.SongSortOrder.SONG_YEAR -> return MusicUtil.getYearString(dataSet[position].year)
|
||||
SortOrder.SongSortOrder.COMPOSER -> dataSet[position].composer
|
||||
else -> {
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
return MusicUtil.getSectionName(sectionName)
|
||||
}
|
||||
|
||||
open inner class ViewHolder(itemView: View) : MediaEntryViewHolder(itemView) {
|
||||
protected open var songMenuRes = SongMenuHelper.MENU_RES
|
||||
protected open val song: Song
|
||||
get() = dataSet[layoutPosition]
|
||||
|
||||
init {
|
||||
setImageTransitionName(activity.getString(R.string.transition_album_art))
|
||||
menu?.setOnClickListener(object : SongMenuHelper.OnClickSongMenu(activity) {
|
||||
override val song: Song
|
||||
get() = this@ViewHolder.song
|
||||
|
||||
override val menuRes: Int
|
||||
get() = songMenuRes
|
||||
|
||||
override fun onMenuItemClick(item: MenuItem): Boolean {
|
||||
return onSongMenuItemClick(item) || super.onMenuItemClick(item)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
protected open fun onSongMenuItemClick(item: MenuItem): Boolean {
|
||||
if (image != null && image!!.visibility == View.VISIBLE) {
|
||||
when (item.itemId) {
|
||||
R.id.action_go_to_album -> {
|
||||
val activityOptions = ActivityOptions.makeSceneTransitionAnimation(
|
||||
activity,
|
||||
imageContainerCard ?: image,
|
||||
activity.getString(R.string.transition_album_art)
|
||||
)
|
||||
NavigationUtil.goToAlbumOptions(activity, song.albumId, activityOptions)
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
override fun onClick(v: View?) {
|
||||
if (isInQuickSelectMode) {
|
||||
toggleChecked(layoutPosition)
|
||||
} else {
|
||||
MusicPlayerRemote.openQueue(dataSet, layoutPosition, true)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onLongClick(v: View?): Boolean {
|
||||
return toggleChecked(layoutPosition)
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
val TAG: String = SongAdapter::class.java.simpleName
|
||||
}
|
||||
}
|
|
@ -1,87 +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.appshortcuts
|
||||
|
||||
import android.content.Context
|
||||
import android.graphics.Bitmap
|
||||
import android.graphics.Canvas
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.graphics.drawable.Icon
|
||||
import android.graphics.drawable.LayerDrawable
|
||||
import android.os.Build
|
||||
import android.util.TypedValue
|
||||
import androidx.annotation.RequiresApi
|
||||
import code.name.monkey.appthemehelper.ThemeStore
|
||||
import code.name.monkey.retromusic.R
|
||||
import code.name.monkey.retromusic.util.PreferenceUtil
|
||||
import code.name.monkey.retromusic.util.RetroUtil
|
||||
|
||||
@RequiresApi(Build.VERSION_CODES.N_MR1)
|
||||
object AppShortcutIconGenerator {
|
||||
fun generateThemedIcon(context: Context, iconId: Int): Icon {
|
||||
return if (PreferenceUtil.isColoredAppShortcuts) {
|
||||
generateUserThemedIcon(context, iconId)
|
||||
} else {
|
||||
generateDefaultThemedIcon(context, iconId)
|
||||
}
|
||||
}
|
||||
|
||||
private fun generateDefaultThemedIcon(context: Context, iconId: Int): Icon {
|
||||
// Return an Icon of iconId with default colors
|
||||
return generateThemedIcon(
|
||||
context,
|
||||
iconId,
|
||||
context.getColor(R.color.app_shortcut_default_foreground),
|
||||
context.getColor(R.color.app_shortcut_default_background)
|
||||
)
|
||||
}
|
||||
|
||||
private fun generateUserThemedIcon(context: Context, iconId: Int): Icon {
|
||||
// Get background color from context's theme
|
||||
val typedColorBackground = TypedValue()
|
||||
context.theme.resolveAttribute(android.R.attr.colorBackground, typedColorBackground, true)
|
||||
|
||||
// Return an Icon of iconId with those colors
|
||||
return generateThemedIcon(
|
||||
context, iconId, ThemeStore.accentColor(context), typedColorBackground.data
|
||||
)
|
||||
}
|
||||
|
||||
private fun generateThemedIcon(
|
||||
context: Context, iconId: Int, foregroundColor: Int, backgroundColor: Int
|
||||
): Icon {
|
||||
// Get and tint foreground and background drawables
|
||||
val vectorDrawable = RetroUtil.getTintedVectorDrawable(context, iconId, foregroundColor)
|
||||
val backgroundDrawable = RetroUtil.getTintedVectorDrawable(
|
||||
context, R.drawable.ic_app_shortcut_background, backgroundColor
|
||||
)
|
||||
|
||||
// Squash the two drawables together
|
||||
val layerDrawable = LayerDrawable(arrayOf(backgroundDrawable, vectorDrawable))
|
||||
|
||||
// Return as an Icon
|
||||
return Icon.createWithBitmap(drawableToBitmap(layerDrawable))
|
||||
}
|
||||
|
||||
private fun drawableToBitmap(drawable: Drawable): Bitmap {
|
||||
val bitmap = Bitmap.createBitmap(
|
||||
drawable.intrinsicWidth, drawable.intrinsicHeight, Bitmap.Config.ARGB_8888
|
||||
)
|
||||
val canvas = Canvas(bitmap)
|
||||
drawable.setBounds(0, 0, canvas.width, canvas.height)
|
||||
drawable.draw(canvas)
|
||||
return bitmap
|
||||
}
|
||||
}
|
|
@ -1,93 +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.appshortcuts
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import code.name.monkey.retromusic.activities.SearchActivity
|
||||
import code.name.monkey.retromusic.appshortcuts.shortcuttype.LastAddedShortcutType
|
||||
import code.name.monkey.retromusic.appshortcuts.shortcuttype.SearchShortCutType
|
||||
import code.name.monkey.retromusic.appshortcuts.shortcuttype.ShuffleAllShortcutType
|
||||
import code.name.monkey.retromusic.appshortcuts.shortcuttype.TopTracksShortcutType
|
||||
import code.name.monkey.retromusic.model.Playlist
|
||||
import code.name.monkey.retromusic.model.smartplaylist.LastAddedPlaylist
|
||||
import code.name.monkey.retromusic.model.smartplaylist.MyTopTracksPlaylist
|
||||
import code.name.monkey.retromusic.model.smartplaylist.ShuffleAllPlaylist
|
||||
import code.name.monkey.retromusic.service.MusicService
|
||||
import code.name.monkey.retromusic.service.MusicService.*
|
||||
|
||||
class AppShortcutLauncherActivity : Activity() {
|
||||
|
||||
public override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
var shortcutType = SHORTCUT_TYPE_NONE
|
||||
|
||||
// Set shortcutType from the intent extras
|
||||
val extras = intent.extras
|
||||
if (extras != null) {
|
||||
shortcutType = extras.getInt(KEY_SHORTCUT_TYPE, SHORTCUT_TYPE_NONE)
|
||||
}
|
||||
|
||||
when (shortcutType) {
|
||||
SHORTCUT_TYPE_SHUFFLE_ALL -> {
|
||||
startServiceWithPlaylist(
|
||||
MusicService.SHUFFLE_MODE_SHUFFLE, ShuffleAllPlaylist(applicationContext)
|
||||
)
|
||||
DynamicShortcutManager.reportShortcutUsed(this, ShuffleAllShortcutType.id)
|
||||
}
|
||||
SHORTCUT_TYPE_TOP_TRACKS -> {
|
||||
startServiceWithPlaylist(
|
||||
MusicService.SHUFFLE_MODE_NONE, MyTopTracksPlaylist(applicationContext)
|
||||
)
|
||||
DynamicShortcutManager.reportShortcutUsed(this, TopTracksShortcutType.id)
|
||||
}
|
||||
SHORTCUT_TYPE_LAST_ADDED -> {
|
||||
startServiceWithPlaylist(
|
||||
MusicService.SHUFFLE_MODE_NONE, LastAddedPlaylist(applicationContext)
|
||||
)
|
||||
DynamicShortcutManager.reportShortcutUsed(this, LastAddedShortcutType.id)
|
||||
}
|
||||
SHORTCUT_TYPE_SEARCH -> {
|
||||
startActivity(Intent(this, SearchActivity::class.java))
|
||||
DynamicShortcutManager.reportShortcutUsed(this, SearchShortCutType.id)
|
||||
}
|
||||
}
|
||||
finish()
|
||||
}
|
||||
|
||||
private fun startServiceWithPlaylist(shuffleMode: Int, playlist: Playlist) {
|
||||
val intent = Intent(this, MusicService::class.java)
|
||||
intent.action = ACTION_PLAY_PLAYLIST
|
||||
|
||||
val bundle = Bundle()
|
||||
bundle.putParcelable(INTENT_EXTRA_PLAYLIST, playlist)
|
||||
bundle.putInt(INTENT_EXTRA_SHUFFLE_MODE, shuffleMode)
|
||||
|
||||
intent.putExtras(bundle)
|
||||
|
||||
startService(intent)
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val KEY_SHORTCUT_TYPE = "code.name.monkey.retromusic.appshortcuts.ShortcutType"
|
||||
const val SHORTCUT_TYPE_SHUFFLE_ALL = 0
|
||||
const val SHORTCUT_TYPE_TOP_TRACKS = 1
|
||||
const val SHORTCUT_TYPE_LAST_ADDED = 2
|
||||
const val SHORTCUT_TYPE_SEARCH = 3
|
||||
const val SHORTCUT_TYPE_NONE = 4
|
||||
}
|
||||
}
|
|
@ -1,72 +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.appshortcuts
|
||||
|
||||
import android.annotation.TargetApi
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.pm.ShortcutInfo
|
||||
import android.content.pm.ShortcutManager
|
||||
import android.graphics.drawable.Icon
|
||||
import android.os.Build
|
||||
import code.name.monkey.retromusic.appshortcuts.shortcuttype.LastAddedShortcutType
|
||||
import code.name.monkey.retromusic.appshortcuts.shortcuttype.SearchShortCutType
|
||||
import code.name.monkey.retromusic.appshortcuts.shortcuttype.ShuffleAllShortcutType
|
||||
import code.name.monkey.retromusic.appshortcuts.shortcuttype.TopTracksShortcutType
|
||||
import java.util.*
|
||||
|
||||
@TargetApi(Build.VERSION_CODES.N_MR1)
|
||||
class DynamicShortcutManager(private val context: Context) {
|
||||
private val shortcutManager: ShortcutManager =
|
||||
this.context.getSystemService(ShortcutManager::class.java)
|
||||
|
||||
private val defaultShortcuts: List<ShortcutInfo>
|
||||
get() = Arrays.asList(
|
||||
SearchShortCutType(context).shortcutInfo,
|
||||
ShuffleAllShortcutType(context).shortcutInfo,
|
||||
TopTracksShortcutType(context).shortcutInfo,
|
||||
LastAddedShortcutType(context).shortcutInfo
|
||||
|
||||
)
|
||||
|
||||
fun initDynamicShortcuts() {
|
||||
//if (shortcutManager.dynamicShortcuts.size == 0) {
|
||||
shortcutManager.dynamicShortcuts = defaultShortcuts
|
||||
//}
|
||||
}
|
||||
|
||||
fun updateDynamicShortcuts() {
|
||||
shortcutManager.updateShortcuts(defaultShortcuts)
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
fun createShortcut(
|
||||
context: Context,
|
||||
id: String,
|
||||
shortLabel: String,
|
||||
longLabel: String,
|
||||
icon: Icon,
|
||||
intent: Intent
|
||||
): ShortcutInfo {
|
||||
return ShortcutInfo.Builder(context, id).setShortLabel(shortLabel)
|
||||
.setLongLabel(longLabel).setIcon(icon).setIntent(intent).build()
|
||||
}
|
||||
|
||||
fun reportShortcutUsed(context: Context, shortcutId: String) {
|
||||
context.getSystemService(ShortcutManager::class.java).reportShortcutUsed(shortcutId)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,50 +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.appshortcuts.shortcuttype
|
||||
|
||||
import android.annotation.TargetApi
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.pm.ShortcutInfo
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import code.name.monkey.retromusic.appshortcuts.AppShortcutLauncherActivity
|
||||
|
||||
@TargetApi(Build.VERSION_CODES.N_MR1)
|
||||
abstract class BaseShortcutType(internal var context: Context) {
|
||||
|
||||
internal abstract val shortcutInfo: ShortcutInfo
|
||||
|
||||
/**
|
||||
* Creates an Intent that will launch MainActivtiy and immediately play {@param songs} in either shuffle or normal mode
|
||||
*
|
||||
* @param shortcutType Describes the type of shortcut to create (ShuffleAll, TopTracks, custom playlist, etc.)
|
||||
* @return
|
||||
*/
|
||||
internal fun getPlaySongsIntent(shortcutType: Int): Intent {
|
||||
val intent = Intent(context, AppShortcutLauncherActivity::class.java)
|
||||
intent.action = Intent.ACTION_VIEW
|
||||
val b = Bundle()
|
||||
b.putInt(AppShortcutLauncherActivity.KEY_SHORTCUT_TYPE, shortcutType)
|
||||
intent.putExtras(b)
|
||||
return intent
|
||||
}
|
||||
|
||||
companion object {
|
||||
internal const val ID_PREFIX = "code.name.monkey.retromusic.appshortcuts.id."
|
||||
val id: String
|
||||
get() = ID_PREFIX + "invalid"
|
||||
}
|
||||
}
|
|
@ -1,47 +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.appshortcuts.shortcuttype
|
||||
|
||||
import android.annotation.TargetApi
|
||||
import android.content.Context
|
||||
import android.content.pm.ShortcutInfo
|
||||
import android.os.Build
|
||||
import code.name.monkey.retromusic.R
|
||||
import code.name.monkey.retromusic.appshortcuts.AppShortcutIconGenerator
|
||||
import code.name.monkey.retromusic.appshortcuts.AppShortcutLauncherActivity
|
||||
|
||||
@TargetApi(Build.VERSION_CODES.N_MR1)
|
||||
class LastAddedShortcutType(context: Context) : BaseShortcutType(context) {
|
||||
|
||||
override val shortcutInfo: ShortcutInfo
|
||||
get() = ShortcutInfo.Builder(
|
||||
context,
|
||||
id
|
||||
).setShortLabel(context.getString(R.string.app_shortcut_last_added_short)).setLongLabel(
|
||||
context.getString(R.string.app_shortcut_last_added_long)
|
||||
).setIcon(
|
||||
AppShortcutIconGenerator.generateThemedIcon(
|
||||
context,
|
||||
R.drawable.ic_app_shortcut_last_added
|
||||
)
|
||||
).setIntent(getPlaySongsIntent(AppShortcutLauncherActivity.SHORTCUT_TYPE_LAST_ADDED))
|
||||
.build()
|
||||
|
||||
companion object {
|
||||
|
||||
val id: String
|
||||
get() = BaseShortcutType.ID_PREFIX + "last_added"
|
||||
}
|
||||
}
|
|
@ -1,44 +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.appshortcuts.shortcuttype
|
||||
|
||||
import android.annotation.TargetApi
|
||||
import android.content.Context
|
||||
import android.content.pm.ShortcutInfo
|
||||
import android.os.Build
|
||||
import code.name.monkey.retromusic.R
|
||||
import code.name.monkey.retromusic.appshortcuts.AppShortcutIconGenerator
|
||||
import code.name.monkey.retromusic.appshortcuts.AppShortcutLauncherActivity
|
||||
|
||||
@TargetApi(Build.VERSION_CODES.N_MR1)
|
||||
class SearchShortCutType(context: Context) : BaseShortcutType(context) {
|
||||
companion object {
|
||||
|
||||
val id: String
|
||||
get() = BaseShortcutType.ID_PREFIX + "search"
|
||||
}
|
||||
|
||||
override val shortcutInfo: ShortcutInfo
|
||||
get() = ShortcutInfo.Builder(
|
||||
context,
|
||||
id
|
||||
).setShortLabel(context.getString(R.string.action_search))
|
||||
.setLongLabel(context.getString(R.string.search_hint)).setIcon(
|
||||
AppShortcutIconGenerator.generateThemedIcon(
|
||||
context,
|
||||
R.drawable.ic_app_shortcut_search
|
||||
)
|
||||
).setIntent(getPlaySongsIntent(AppShortcutLauncherActivity.SHORTCUT_TYPE_SEARCH)).build()
|
||||
}
|
|
@ -1,45 +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.appshortcuts.shortcuttype
|
||||
|
||||
import android.annotation.TargetApi
|
||||
import android.content.Context
|
||||
import android.content.pm.ShortcutInfo
|
||||
import android.os.Build
|
||||
import code.name.monkey.retromusic.R
|
||||
import code.name.monkey.retromusic.appshortcuts.AppShortcutIconGenerator
|
||||
import code.name.monkey.retromusic.appshortcuts.AppShortcutLauncherActivity
|
||||
|
||||
@TargetApi(Build.VERSION_CODES.N_MR1)
|
||||
class ShuffleAllShortcutType(context: Context) : BaseShortcutType(context) {
|
||||
|
||||
override val shortcutInfo: ShortcutInfo
|
||||
get() = ShortcutInfo.Builder(
|
||||
context, id
|
||||
).setShortLabel(context.getString(R.string.app_shortcut_shuffle_all_short)).setLongLabel(
|
||||
context.getString(R.string.app_shortcut_shuffle_all_long)
|
||||
).setIcon(
|
||||
AppShortcutIconGenerator.generateThemedIcon(
|
||||
context, R.drawable.ic_app_shortcut_shuffle_all
|
||||
)
|
||||
).setIntent(getPlaySongsIntent(AppShortcutLauncherActivity.SHORTCUT_TYPE_SHUFFLE_ALL))
|
||||
.build()
|
||||
|
||||
companion object {
|
||||
|
||||
val id: String
|
||||
get() = BaseShortcutType.ID_PREFIX + "shuffle_all"
|
||||
}
|
||||
}
|
|
@ -1,45 +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.appshortcuts.shortcuttype
|
||||
|
||||
import android.annotation.TargetApi
|
||||
import android.content.Context
|
||||
import android.content.pm.ShortcutInfo
|
||||
import android.os.Build
|
||||
import code.name.monkey.retromusic.R
|
||||
import code.name.monkey.retromusic.appshortcuts.AppShortcutIconGenerator
|
||||
import code.name.monkey.retromusic.appshortcuts.AppShortcutLauncherActivity
|
||||
|
||||
@TargetApi(Build.VERSION_CODES.N_MR1)
|
||||
class TopTracksShortcutType(context: Context) : BaseShortcutType(context) {
|
||||
|
||||
override val shortcutInfo: ShortcutInfo
|
||||
get() = ShortcutInfo.Builder(
|
||||
context, id
|
||||
).setShortLabel(context.getString(R.string.app_shortcut_top_tracks_short)).setLongLabel(
|
||||
context.getString(R.string.app_shortcut_top_tracks_long)
|
||||
).setIcon(
|
||||
AppShortcutIconGenerator.generateThemedIcon(
|
||||
context, R.drawable.ic_app_shortcut_top_tracks
|
||||
)
|
||||
).setIntent(getPlaySongsIntent(AppShortcutLauncherActivity.SHORTCUT_TYPE_TOP_TRACKS))
|
||||
.build()
|
||||
|
||||
companion object {
|
||||
|
||||
val id: String
|
||||
get() = BaseShortcutType.ID_PREFIX + "top_tracks"
|
||||
}
|
||||
}
|
|
@ -1,234 +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.appwidgets
|
||||
|
||||
import android.app.PendingIntent
|
||||
import android.content.ComponentName
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.graphics.Bitmap
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.text.TextUtils
|
||||
import android.view.View
|
||||
import android.widget.RemoteViews
|
||||
import code.name.monkey.appthemehelper.util.MaterialValueHelper
|
||||
import code.name.monkey.retromusic.R
|
||||
import code.name.monkey.retromusic.activities.MainActivity
|
||||
import code.name.monkey.retromusic.appwidgets.base.BaseAppWidget
|
||||
import code.name.monkey.retromusic.glide.SongGlideRequest
|
||||
import code.name.monkey.retromusic.service.MusicService
|
||||
import code.name.monkey.retromusic.service.MusicService.*
|
||||
import code.name.monkey.retromusic.util.RetroUtil
|
||||
import com.bumptech.glide.Glide
|
||||
import com.bumptech.glide.request.animation.GlideAnimation
|
||||
import com.bumptech.glide.request.target.SimpleTarget
|
||||
import com.bumptech.glide.request.target.Target
|
||||
|
||||
class AppWidgetBig : BaseAppWidget() {
|
||||
private var target: Target<Bitmap>? = null // for cancellation
|
||||
|
||||
/**
|
||||
* Initialize given widgets to default state, where we launch Music on default click and hide
|
||||
* actions if service not running.
|
||||
*/
|
||||
override fun defaultAppWidget(context: Context, appWidgetIds: IntArray) {
|
||||
val appWidgetView = RemoteViews(
|
||||
context.packageName, R.layout.app_widget_big
|
||||
)
|
||||
|
||||
appWidgetView.setViewVisibility(
|
||||
R.id.media_titles,
|
||||
View.INVISIBLE
|
||||
)
|
||||
appWidgetView.setImageViewResource(R.id.image, R.drawable.default_audio_art)
|
||||
appWidgetView.setImageViewBitmap(
|
||||
R.id.button_next, createBitmap(
|
||||
RetroUtil.getTintedVectorDrawable(
|
||||
context,
|
||||
R.drawable.ic_skip_next_white_24dp,
|
||||
MaterialValueHelper.getPrimaryTextColor(context, false)
|
||||
)!!, 1f
|
||||
)
|
||||
)
|
||||
appWidgetView.setImageViewBitmap(
|
||||
R.id.button_prev, createBitmap(
|
||||
RetroUtil.getTintedVectorDrawable(
|
||||
context,
|
||||
R.drawable.ic_skip_previous_white_24dp,
|
||||
MaterialValueHelper.getPrimaryTextColor(context, false)
|
||||
)!!, 1f
|
||||
)
|
||||
)
|
||||
appWidgetView.setImageViewBitmap(
|
||||
R.id.button_toggle_play_pause, BaseAppWidget.Companion.createBitmap(
|
||||
RetroUtil.getTintedVectorDrawable(
|
||||
context,
|
||||
R.drawable.ic_play_arrow_white_32dp,
|
||||
MaterialValueHelper.getPrimaryTextColor(context, false)
|
||||
)!!, 1f
|
||||
)
|
||||
)
|
||||
|
||||
linkButtons(context, appWidgetView)
|
||||
pushUpdate(context, appWidgetIds, appWidgetView)
|
||||
}
|
||||
|
||||
/**
|
||||
* Update all active widget instances by pushing changes
|
||||
*/
|
||||
override fun performUpdate(service: MusicService, appWidgetIds: IntArray?) {
|
||||
val appWidgetView = RemoteViews(
|
||||
service.packageName, R.layout.app_widget_big
|
||||
)
|
||||
|
||||
val isPlaying = service.isPlaying
|
||||
val song = service.currentSong
|
||||
|
||||
// Set the titles and artwork
|
||||
if (TextUtils.isEmpty(song.title) && TextUtils.isEmpty(song.artistName)) {
|
||||
appWidgetView.setViewVisibility(
|
||||
R.id.media_titles,
|
||||
View.INVISIBLE
|
||||
)
|
||||
} else {
|
||||
appWidgetView.setViewVisibility(
|
||||
R.id.media_titles,
|
||||
View.VISIBLE
|
||||
)
|
||||
appWidgetView.setTextViewText(R.id.title, song.title)
|
||||
appWidgetView.setTextViewText(
|
||||
R.id.text,
|
||||
getSongArtistAndAlbum(song)
|
||||
)
|
||||
}
|
||||
|
||||
val primaryColor = MaterialValueHelper.getPrimaryTextColor(service, false)
|
||||
// Set correct drawable for pause state
|
||||
val playPauseRes =
|
||||
if (isPlaying) R.drawable.ic_pause_white_24dp else R.drawable.ic_play_arrow_white_32dp
|
||||
appWidgetView.setImageViewBitmap(
|
||||
R.id.button_toggle_play_pause, createBitmap(
|
||||
RetroUtil.getTintedVectorDrawable(
|
||||
service,
|
||||
playPauseRes,
|
||||
primaryColor
|
||||
)!!, 1f
|
||||
)
|
||||
)
|
||||
|
||||
// Set prev/next button drawables
|
||||
appWidgetView.setImageViewBitmap(
|
||||
R.id.button_next, createBitmap(
|
||||
RetroUtil.getTintedVectorDrawable(
|
||||
service,
|
||||
R.drawable.ic_skip_next_white_24dp,
|
||||
primaryColor
|
||||
)!!, 1f
|
||||
)
|
||||
)
|
||||
appWidgetView.setImageViewBitmap(
|
||||
R.id.button_prev, createBitmap(
|
||||
RetroUtil.getTintedVectorDrawable(
|
||||
service,
|
||||
R.drawable.ic_skip_previous_white_24dp,
|
||||
primaryColor
|
||||
)!!, 1f
|
||||
)
|
||||
)
|
||||
|
||||
// Link actions buttons to intents
|
||||
linkButtons(service, appWidgetView)
|
||||
|
||||
// Load the album cover async and push the update on completion
|
||||
val p = RetroUtil.getScreenSize(service)
|
||||
val widgetImageSize = Math.min(p.x, p.y)
|
||||
val appContext = service.applicationContext
|
||||
service.runOnUiThread {
|
||||
if (target != null) {
|
||||
Glide.clear(target)
|
||||
}
|
||||
target = SongGlideRequest.Builder.from(Glide.with(appContext), song)
|
||||
.checkIgnoreMediaStore(appContext).asBitmap().build()
|
||||
.into(object : SimpleTarget<Bitmap>(widgetImageSize, widgetImageSize) {
|
||||
override fun onResourceReady(
|
||||
resource: Bitmap,
|
||||
glideAnimation: GlideAnimation<in Bitmap>
|
||||
) {
|
||||
update(resource)
|
||||
}
|
||||
|
||||
override fun onLoadFailed(e: Exception?, errorDrawable: Drawable?) {
|
||||
super.onLoadFailed(e, errorDrawable)
|
||||
update(null)
|
||||
}
|
||||
|
||||
private fun update(bitmap: Bitmap?) {
|
||||
if (bitmap == null) {
|
||||
appWidgetView.setImageViewResource(
|
||||
R.id.image,
|
||||
R.drawable.default_audio_art
|
||||
)
|
||||
} else {
|
||||
appWidgetView.setImageViewBitmap(R.id.image, bitmap)
|
||||
}
|
||||
pushUpdate(appContext, appWidgetIds, appWidgetView)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Link up various button actions using [PendingIntent].
|
||||
*/
|
||||
private fun linkButtons(context: Context, views: RemoteViews) {
|
||||
val action =
|
||||
Intent(context, MainActivity::class.java).putExtra(MainActivity.EXPAND_PANEL, true)
|
||||
var pendingIntent: PendingIntent
|
||||
|
||||
val serviceName = ComponentName(context, MusicService::class.java)
|
||||
|
||||
// Home
|
||||
action.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TOP
|
||||
pendingIntent = PendingIntent.getActivity(context, 0, action, 0)
|
||||
views.setOnClickPendingIntent(R.id.clickable_area, pendingIntent)
|
||||
|
||||
// Previous track
|
||||
pendingIntent = buildPendingIntent(context, ACTION_REWIND, serviceName)
|
||||
views.setOnClickPendingIntent(R.id.button_prev, pendingIntent)
|
||||
|
||||
// Play and pause
|
||||
pendingIntent = buildPendingIntent(context, ACTION_TOGGLE_PAUSE, serviceName)
|
||||
views.setOnClickPendingIntent(R.id.button_toggle_play_pause, pendingIntent)
|
||||
|
||||
// Next track
|
||||
pendingIntent = buildPendingIntent(context, ACTION_SKIP, serviceName)
|
||||
views.setOnClickPendingIntent(R.id.button_next, pendingIntent)
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
const val NAME: String = "app_widget_big"
|
||||
private var mInstance: AppWidgetBig? = null
|
||||
|
||||
val instance: AppWidgetBig
|
||||
@Synchronized get() {
|
||||
if (mInstance == null) {
|
||||
mInstance = AppWidgetBig()
|
||||
}
|
||||
return mInstance!!
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -1,260 +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.appwidgets
|
||||
|
||||
import android.app.PendingIntent
|
||||
import android.content.ComponentName
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.graphics.Bitmap
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.text.TextUtils
|
||||
import android.view.View
|
||||
import android.widget.RemoteViews
|
||||
import code.name.monkey.appthemehelper.util.MaterialValueHelper
|
||||
import code.name.monkey.retromusic.R
|
||||
import code.name.monkey.retromusic.activities.MainActivity
|
||||
import code.name.monkey.retromusic.appwidgets.base.BaseAppWidget
|
||||
import code.name.monkey.retromusic.glide.SongGlideRequest
|
||||
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.util.ImageUtil
|
||||
import code.name.monkey.retromusic.util.RetroUtil
|
||||
import com.bumptech.glide.Glide
|
||||
import com.bumptech.glide.request.animation.GlideAnimation
|
||||
import com.bumptech.glide.request.target.SimpleTarget
|
||||
import com.bumptech.glide.request.target.Target
|
||||
|
||||
class AppWidgetCard : BaseAppWidget() {
|
||||
private var target: Target<BitmapPaletteWrapper>? = null // for cancellation
|
||||
|
||||
/**
|
||||
* Initialize given widgets to default state, where we launch Music on default click and hide
|
||||
* actions if service not running.
|
||||
*/
|
||||
override fun defaultAppWidget(context: Context, appWidgetIds: IntArray) {
|
||||
val appWidgetView = RemoteViews(context.packageName, R.layout.app_widget_card)
|
||||
|
||||
appWidgetView.setViewVisibility(R.id.media_titles, View.INVISIBLE)
|
||||
appWidgetView.setImageViewResource(R.id.image, R.drawable.default_audio_art)
|
||||
val secondaryColor = MaterialValueHelper.getSecondaryTextColor(context, true)
|
||||
appWidgetView.setImageViewBitmap(
|
||||
R.id.button_next, createBitmap(
|
||||
RetroUtil.getTintedVectorDrawable(
|
||||
context,
|
||||
R.drawable.ic_skip_next_white_24dp,
|
||||
secondaryColor
|
||||
)!!, 1f
|
||||
)
|
||||
)
|
||||
appWidgetView.setImageViewBitmap(
|
||||
R.id.button_prev, createBitmap(
|
||||
RetroUtil.getTintedVectorDrawable(
|
||||
context,
|
||||
R.drawable.ic_skip_previous_white_24dp,
|
||||
secondaryColor
|
||||
)!!, 1f
|
||||
)
|
||||
)
|
||||
appWidgetView.setImageViewBitmap(
|
||||
R.id.button_toggle_play_pause, createBitmap(
|
||||
RetroUtil.getTintedVectorDrawable(
|
||||
context,
|
||||
R.drawable.ic_play_arrow_white_32dp,
|
||||
secondaryColor
|
||||
)!!, 1f
|
||||
)
|
||||
)
|
||||
|
||||
linkButtons(context, appWidgetView)
|
||||
pushUpdate(context, appWidgetIds, appWidgetView)
|
||||
}
|
||||
|
||||
/**
|
||||
* Update all active widget instances by pushing changes
|
||||
*/
|
||||
override fun performUpdate(service: MusicService, appWidgetIds: IntArray?) {
|
||||
val appWidgetView = RemoteViews(service.packageName, R.layout.app_widget_card)
|
||||
|
||||
val isPlaying = service.isPlaying
|
||||
val song = service.currentSong
|
||||
|
||||
// Set the titles and artwork
|
||||
if (TextUtils.isEmpty(song.title) && TextUtils.isEmpty(song.artistName)) {
|
||||
appWidgetView.setViewVisibility(R.id.media_titles, View.INVISIBLE)
|
||||
} else {
|
||||
appWidgetView.setViewVisibility(R.id.media_titles, View.VISIBLE)
|
||||
appWidgetView.setTextViewText(R.id.title, song.title)
|
||||
appWidgetView.setTextViewText(R.id.text, getSongArtistAndAlbum(song))
|
||||
}
|
||||
|
||||
// Set correct drawable for pause state
|
||||
val playPauseRes =
|
||||
if (isPlaying) R.drawable.ic_pause_white_24dp else R.drawable.ic_play_arrow_white_32dp
|
||||
appWidgetView.setImageViewBitmap(
|
||||
R.id.button_toggle_play_pause, createBitmap(
|
||||
RetroUtil.getTintedVectorDrawable(
|
||||
service,
|
||||
playPauseRes,
|
||||
MaterialValueHelper.getSecondaryTextColor(service, true)
|
||||
)!!, 1f
|
||||
)
|
||||
)
|
||||
|
||||
// Set prev/next button drawables
|
||||
appWidgetView.setImageViewBitmap(
|
||||
R.id.button_next, createBitmap(
|
||||
RetroUtil.getTintedVectorDrawable(
|
||||
service,
|
||||
R.drawable.ic_skip_next_white_24dp,
|
||||
MaterialValueHelper.getSecondaryTextColor(service, true)
|
||||
)!!, 1f
|
||||
)
|
||||
)
|
||||
appWidgetView.setImageViewBitmap(
|
||||
R.id.button_prev, createBitmap(
|
||||
RetroUtil.getTintedVectorDrawable(
|
||||
service,
|
||||
R.drawable.ic_skip_previous_white_24dp,
|
||||
MaterialValueHelper.getSecondaryTextColor(service, true)
|
||||
)!!, 1f
|
||||
)
|
||||
)
|
||||
|
||||
// Link actions buttons to intents
|
||||
linkButtons(service, appWidgetView)
|
||||
|
||||
if (imageSize == 0) {
|
||||
imageSize =
|
||||
service.resources.getDimensionPixelSize(code.name.monkey.retromusic.R.dimen.app_widget_card_image_size)
|
||||
}
|
||||
if (cardRadius == 0f) {
|
||||
cardRadius =
|
||||
service.resources.getDimension(code.name.monkey.retromusic.R.dimen.app_widget_card_radius)
|
||||
}
|
||||
|
||||
// Load the album cover async and push the update on completion
|
||||
service.runOnUiThread {
|
||||
if (target != null) {
|
||||
Glide.clear(target)
|
||||
}
|
||||
target = SongGlideRequest.Builder.from(Glide.with(service), song)
|
||||
.checkIgnoreMediaStore(service).generatePalette(service).build().centerCrop()
|
||||
.into(object : SimpleTarget<BitmapPaletteWrapper>(imageSize, imageSize) {
|
||||
override fun onResourceReady(
|
||||
resource: BitmapPaletteWrapper,
|
||||
glideAnimation: GlideAnimation<in BitmapPaletteWrapper>
|
||||
) {
|
||||
val palette = resource.palette
|
||||
update(
|
||||
resource.bitmap, palette.getVibrantColor(
|
||||
palette.getMutedColor(
|
||||
MaterialValueHelper.getSecondaryTextColor(
|
||||
service, true
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
override fun onLoadFailed(e: Exception?, errorDrawable: Drawable?) {
|
||||
super.onLoadFailed(e, errorDrawable)
|
||||
update(null, MaterialValueHelper.getSecondaryTextColor(service, true))
|
||||
}
|
||||
|
||||
private fun update(bitmap: Bitmap?, color: Int) {
|
||||
// Set correct drawable for pause state
|
||||
appWidgetView.setImageViewBitmap(
|
||||
R.id.button_toggle_play_pause, ImageUtil.createBitmap(
|
||||
ImageUtil.getTintedVectorDrawable(
|
||||
service, playPauseRes, color
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
// Set prev/next button drawables
|
||||
appWidgetView.setImageViewBitmap(
|
||||
R.id.button_next, ImageUtil.createBitmap(
|
||||
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
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
val image = getAlbumArtDrawable(service.resources, bitmap)
|
||||
val roundedBitmap = createRoundedBitmap(
|
||||
image, imageSize, imageSize, cardRadius, 0F, cardRadius, 0F
|
||||
)
|
||||
appWidgetView.setImageViewBitmap(R.id.image, roundedBitmap)
|
||||
|
||||
pushUpdate(service, appWidgetIds, appWidgetView)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Link up various button actions using [PendingIntent].
|
||||
*/
|
||||
private fun linkButtons(context: Context, views: RemoteViews) {
|
||||
val action: Intent = Intent(context, MainActivity::class.java).putExtra("expand", true)
|
||||
var pendingIntent: PendingIntent
|
||||
|
||||
val serviceName = ComponentName(context, MusicService::class.java)
|
||||
|
||||
// Home
|
||||
action.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TOP
|
||||
pendingIntent = PendingIntent.getActivity(context, 0, action, 0)
|
||||
views.setOnClickPendingIntent(R.id.image, pendingIntent)
|
||||
views.setOnClickPendingIntent(R.id.media_titles, pendingIntent)
|
||||
|
||||
// Previous track
|
||||
pendingIntent = buildPendingIntent(context, ACTION_REWIND, serviceName)
|
||||
views.setOnClickPendingIntent(R.id.button_prev, pendingIntent)
|
||||
|
||||
// Play and pause
|
||||
pendingIntent = buildPendingIntent(context, ACTION_TOGGLE_PAUSE, serviceName)
|
||||
views.setOnClickPendingIntent(R.id.button_toggle_play_pause, pendingIntent)
|
||||
|
||||
// Next track
|
||||
pendingIntent = buildPendingIntent(context, ACTION_SKIP, serviceName)
|
||||
views.setOnClickPendingIntent(R.id.button_next, pendingIntent)
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
const val NAME = "app_widget_card"
|
||||
|
||||
private var mInstance: AppWidgetCard? = null
|
||||
private var imageSize = 0
|
||||
private var cardRadius = 0f
|
||||
|
||||
val instance: AppWidgetCard
|
||||
@Synchronized get() {
|
||||
if (mInstance == null) {
|
||||
mInstance = AppWidgetCard()
|
||||
}
|
||||
return mInstance!!
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,251 +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.appwidgets
|
||||
|
||||
import android.app.PendingIntent
|
||||
import android.content.ComponentName
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.graphics.Bitmap
|
||||
import android.graphics.Color
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.text.TextUtils
|
||||
import android.view.View
|
||||
import android.widget.RemoteViews
|
||||
import code.name.monkey.appthemehelper.util.MaterialValueHelper
|
||||
import code.name.monkey.retromusic.R
|
||||
import code.name.monkey.retromusic.activities.MainActivity
|
||||
import code.name.monkey.retromusic.appwidgets.base.BaseAppWidget
|
||||
import code.name.monkey.retromusic.glide.SongGlideRequest
|
||||
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.util.ImageUtil
|
||||
import code.name.monkey.retromusic.util.RetroUtil
|
||||
import com.bumptech.glide.Glide
|
||||
import com.bumptech.glide.request.animation.GlideAnimation
|
||||
import com.bumptech.glide.request.target.SimpleTarget
|
||||
import com.bumptech.glide.request.target.Target
|
||||
|
||||
class AppWidgetClassic : BaseAppWidget() {
|
||||
private var target: Target<BitmapPaletteWrapper>? = null // for cancellation
|
||||
|
||||
/**
|
||||
* Initialize given widgets to default state, where we launch Music on default click and hide
|
||||
* actions if service not running.
|
||||
*/
|
||||
override fun defaultAppWidget(context: Context, appWidgetIds: IntArray) {
|
||||
val appWidgetView = RemoteViews(context.packageName, R.layout.app_widget_classic)
|
||||
|
||||
|
||||
appWidgetView.setViewVisibility(R.id.media_titles, View.INVISIBLE)
|
||||
appWidgetView.setImageViewResource(R.id.image, R.drawable.default_audio_art)
|
||||
appWidgetView.setImageViewBitmap(
|
||||
R.id.button_next,
|
||||
createBitmap(
|
||||
RetroUtil.getTintedVectorDrawable(
|
||||
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)
|
||||
pushUpdate(context, appWidgetIds, appWidgetView)
|
||||
}
|
||||
|
||||
/**
|
||||
* Update all active widget instances by pushing changes
|
||||
*/
|
||||
override fun performUpdate(service: MusicService, appWidgetIds: IntArray?) {
|
||||
val appWidgetView = RemoteViews(service.packageName, R.layout.app_widget_classic)
|
||||
|
||||
val isPlaying = service.isPlaying
|
||||
val song = service.currentSong
|
||||
|
||||
// Set the titles and artwork
|
||||
if (TextUtils.isEmpty(song.title) && TextUtils.isEmpty(song.artistName)) {
|
||||
appWidgetView.setViewVisibility(R.id.media_titles, View.INVISIBLE)
|
||||
} else {
|
||||
appWidgetView.setViewVisibility(R.id.media_titles, View.VISIBLE)
|
||||
appWidgetView.setTextViewText(R.id.title, song.title)
|
||||
appWidgetView.setTextViewText(R.id.text, getSongArtistAndAlbum(song))
|
||||
}
|
||||
|
||||
// Link actions buttons to intents
|
||||
linkButtons(service, appWidgetView)
|
||||
|
||||
if (imageSize == 0) {
|
||||
imageSize =
|
||||
service.resources.getDimensionPixelSize(R.dimen.app_widget_classic_image_size)
|
||||
}
|
||||
if (cardRadius == 0f) {
|
||||
cardRadius = service.resources.getDimension(R.dimen.app_widget_card_radius)
|
||||
}
|
||||
|
||||
// Load the album cover async and push the update on completion
|
||||
val appContext = service.applicationContext
|
||||
service.runOnUiThread {
|
||||
if (target != null) {
|
||||
Glide.clear(target)
|
||||
}
|
||||
target = SongGlideRequest.Builder.from(Glide.with(service), song)
|
||||
.checkIgnoreMediaStore(service).generatePalette(service).build().centerCrop()
|
||||
.into(object : SimpleTarget<BitmapPaletteWrapper>(imageSize, imageSize) {
|
||||
override fun onResourceReady(
|
||||
resource: BitmapPaletteWrapper,
|
||||
glideAnimation: GlideAnimation<in BitmapPaletteWrapper>
|
||||
) {
|
||||
val palette = resource.palette
|
||||
update(
|
||||
resource.bitmap,
|
||||
palette.getVibrantColor(
|
||||
palette.getMutedColor(
|
||||
MaterialValueHelper.getSecondaryTextColor(
|
||||
service,
|
||||
true
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
override fun onLoadFailed(e: Exception?, errorDrawable: Drawable?) {
|
||||
super.onLoadFailed(e, errorDrawable)
|
||||
update(null, Color.WHITE)
|
||||
}
|
||||
|
||||
private fun update(bitmap: Bitmap?, color: Int) {
|
||||
// Set correct drawable for pause state
|
||||
val playPauseRes =
|
||||
if (isPlaying) R.drawable.ic_pause_white_24dp else R.drawable.ic_play_arrow_white_24dp
|
||||
appWidgetView.setImageViewBitmap(
|
||||
R.id.button_toggle_play_pause,
|
||||
ImageUtil.createBitmap(
|
||||
ImageUtil.getTintedVectorDrawable(
|
||||
service,
|
||||
playPauseRes,
|
||||
color
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
// Set prev/next button drawables
|
||||
appWidgetView.setImageViewBitmap(
|
||||
R.id.button_next,
|
||||
ImageUtil.createBitmap(
|
||||
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
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
val image = getAlbumArtDrawable(service.resources, bitmap)
|
||||
val roundedBitmap =
|
||||
createRoundedBitmap(
|
||||
image,
|
||||
imageSize,
|
||||
imageSize,
|
||||
cardRadius,
|
||||
0F,
|
||||
cardRadius,
|
||||
0F
|
||||
)
|
||||
appWidgetView.setImageViewBitmap(R.id.image, roundedBitmap)
|
||||
|
||||
pushUpdate(appContext, appWidgetIds, appWidgetView)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Link up various button actions using [PendingIntent].
|
||||
*/
|
||||
private fun linkButtons(context: Context, views: RemoteViews) {
|
||||
val action = Intent(context, MainActivity::class.java).putExtra("expand", true)
|
||||
var pendingIntent: PendingIntent
|
||||
|
||||
val serviceName = ComponentName(context, MusicService::class.java)
|
||||
|
||||
// Home
|
||||
action.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TOP
|
||||
pendingIntent = PendingIntent.getActivity(context, 0, action, 0)
|
||||
views.setOnClickPendingIntent(R.id.image, pendingIntent)
|
||||
views.setOnClickPendingIntent(R.id.media_titles, pendingIntent)
|
||||
|
||||
// Previous track
|
||||
pendingIntent = buildPendingIntent(context, ACTION_REWIND, serviceName)
|
||||
views.setOnClickPendingIntent(R.id.button_prev, pendingIntent)
|
||||
|
||||
// Play and pause
|
||||
pendingIntent = buildPendingIntent(context, ACTION_TOGGLE_PAUSE, serviceName)
|
||||
views.setOnClickPendingIntent(R.id.button_toggle_play_pause, pendingIntent)
|
||||
|
||||
// Next track
|
||||
pendingIntent = buildPendingIntent(context, ACTION_SKIP, serviceName)
|
||||
views.setOnClickPendingIntent(R.id.button_next, pendingIntent)
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
const val NAME = "app_widget_classic"
|
||||
|
||||
private var mInstance: AppWidgetClassic? = null
|
||||
private var imageSize = 0
|
||||
private var cardRadius = 0f
|
||||
|
||||
val instance: AppWidgetClassic
|
||||
@Synchronized get() {
|
||||
if (mInstance == null) {
|
||||
mInstance = AppWidgetClassic()
|
||||
}
|
||||
return mInstance!!
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,235 +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.appwidgets
|
||||
|
||||
import android.app.PendingIntent
|
||||
import android.content.ComponentName
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.graphics.Bitmap
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.text.TextUtils
|
||||
import android.view.View
|
||||
import android.widget.RemoteViews
|
||||
import code.name.monkey.appthemehelper.util.MaterialValueHelper
|
||||
import code.name.monkey.retromusic.R
|
||||
import code.name.monkey.retromusic.activities.MainActivity
|
||||
import code.name.monkey.retromusic.appwidgets.base.BaseAppWidget
|
||||
import code.name.monkey.retromusic.glide.SongGlideRequest
|
||||
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.util.RetroUtil
|
||||
import com.bumptech.glide.Glide
|
||||
import com.bumptech.glide.request.animation.GlideAnimation
|
||||
import com.bumptech.glide.request.target.SimpleTarget
|
||||
import com.bumptech.glide.request.target.Target
|
||||
|
||||
class AppWidgetSmall : BaseAppWidget() {
|
||||
private var target: Target<BitmapPaletteWrapper>? = null // for cancellation
|
||||
|
||||
/**
|
||||
* Initialize given widgets to default state, where we launch Music on default click and hide
|
||||
* actions if service not running.
|
||||
*/
|
||||
override fun defaultAppWidget(context: Context, appWidgetIds: IntArray) {
|
||||
val appWidgetView = RemoteViews(context.packageName, R.layout.app_widget_small)
|
||||
|
||||
appWidgetView.setViewVisibility(R.id.media_titles, View.INVISIBLE)
|
||||
appWidgetView.setImageViewResource(R.id.image, R.drawable.default_audio_art)
|
||||
appWidgetView.setImageViewBitmap(
|
||||
R.id.button_next,
|
||||
createBitmap(
|
||||
RetroUtil.getTintedVectorDrawable(
|
||||
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)
|
||||
pushUpdate(context, appWidgetIds, appWidgetView)
|
||||
}
|
||||
|
||||
/**
|
||||
* Update all active widget instances by pushing changes
|
||||
*/
|
||||
override fun performUpdate(service: MusicService, appWidgetIds: IntArray?) {
|
||||
val appWidgetView = RemoteViews(service.packageName, R.layout.app_widget_small)
|
||||
|
||||
val isPlaying = service.isPlaying
|
||||
val song = service.currentSong
|
||||
|
||||
// Set the titles and artwork
|
||||
if (TextUtils.isEmpty(song.title) && TextUtils.isEmpty(song.artistName)) {
|
||||
appWidgetView.setViewVisibility(R.id.media_titles, View.INVISIBLE)
|
||||
} else {
|
||||
if (TextUtils.isEmpty(song.title) || TextUtils.isEmpty(song.artistName)) {
|
||||
appWidgetView.setTextViewText(R.id.text_separator, "")
|
||||
} else {
|
||||
appWidgetView.setTextViewText(R.id.text_separator, "•")
|
||||
}
|
||||
|
||||
appWidgetView.setViewVisibility(R.id.media_titles, View.VISIBLE)
|
||||
appWidgetView.setTextViewText(R.id.title, song.title)
|
||||
appWidgetView.setTextViewText(R.id.text, song.artistName)
|
||||
}
|
||||
|
||||
// Link actions buttons to intents
|
||||
linkButtons(service, appWidgetView)
|
||||
|
||||
if (imageSize == 0) {
|
||||
imageSize = service.resources.getDimensionPixelSize(R.dimen.app_widget_small_image_size)
|
||||
}
|
||||
if (cardRadius == 0f) {
|
||||
cardRadius = service.resources.getDimension(R.dimen.app_widget_card_radius)
|
||||
}
|
||||
|
||||
// Load the album cover async and push the update on completion
|
||||
val appContext = service.applicationContext
|
||||
service.runOnUiThread {
|
||||
if (target != null) {
|
||||
Glide.clear(target)
|
||||
}
|
||||
target = SongGlideRequest.Builder.from(Glide.with(service), song)
|
||||
.checkIgnoreMediaStore(service).generatePalette(service).build().centerCrop()
|
||||
.into(object : SimpleTarget<BitmapPaletteWrapper>(imageSize, imageSize) {
|
||||
override fun onResourceReady(
|
||||
resource: BitmapPaletteWrapper,
|
||||
glideAnimation: GlideAnimation<in BitmapPaletteWrapper>
|
||||
) {
|
||||
val palette = resource.palette
|
||||
update(
|
||||
resource.bitmap, palette.getVibrantColor(
|
||||
palette.getMutedColor(
|
||||
MaterialValueHelper.getSecondaryTextColor(
|
||||
service, true
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
override fun onLoadFailed(e: Exception?, errorDrawable: Drawable?) {
|
||||
super.onLoadFailed(e, errorDrawable)
|
||||
update(null, MaterialValueHelper.getSecondaryTextColor(service, true))
|
||||
}
|
||||
|
||||
private fun update(bitmap: Bitmap?, color: Int) {
|
||||
// Set correct drawable for pause state
|
||||
val playPauseRes = if (isPlaying) R.drawable.ic_pause_white_24dp
|
||||
else R.drawable.ic_play_arrow_white_32dp
|
||||
appWidgetView.setImageViewBitmap(
|
||||
R.id.button_toggle_play_pause, createBitmap(
|
||||
RetroUtil.getTintedVectorDrawable(
|
||||
service, playPauseRes, color
|
||||
)!!, 1f
|
||||
)
|
||||
)
|
||||
|
||||
// Set prev/next button drawables
|
||||
appWidgetView.setImageViewBitmap(
|
||||
R.id.button_next, createBitmap(
|
||||
RetroUtil.getTintedVectorDrawable(
|
||||
service, R.drawable.ic_skip_next_white_24dp, color
|
||||
)!!, 1f
|
||||
)
|
||||
)
|
||||
appWidgetView.setImageViewBitmap(
|
||||
R.id.button_prev, createBitmap(
|
||||
RetroUtil.getTintedVectorDrawable(
|
||||
service, R.drawable.ic_skip_previous_white_24dp, color
|
||||
)!!, 1f
|
||||
)
|
||||
)
|
||||
|
||||
val image = getAlbumArtDrawable(service.resources, bitmap)
|
||||
val roundedBitmap = createRoundedBitmap(
|
||||
image, imageSize, imageSize, cardRadius, 0f, 0f, 0f
|
||||
)
|
||||
appWidgetView.setImageViewBitmap(R.id.image, roundedBitmap)
|
||||
|
||||
pushUpdate(appContext, appWidgetIds, appWidgetView)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Link up various button actions using [PendingIntent].
|
||||
*/
|
||||
private fun linkButtons(context: Context, views: RemoteViews) {
|
||||
val action = Intent(context, MainActivity::class.java).putExtra("expand", true)
|
||||
var pendingIntent: PendingIntent
|
||||
|
||||
val serviceName = ComponentName(context, MusicService::class.java)
|
||||
|
||||
// Home
|
||||
action.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TOP
|
||||
pendingIntent = PendingIntent.getActivity(context, 0, action, 0)
|
||||
views.setOnClickPendingIntent(R.id.image, pendingIntent)
|
||||
views.setOnClickPendingIntent(R.id.media_titles, pendingIntent)
|
||||
|
||||
// Previous track
|
||||
pendingIntent = buildPendingIntent(context, ACTION_REWIND, serviceName)
|
||||
views.setOnClickPendingIntent(R.id.button_prev, pendingIntent)
|
||||
|
||||
// Play and pause
|
||||
pendingIntent = buildPendingIntent(context, ACTION_TOGGLE_PAUSE, serviceName)
|
||||
views.setOnClickPendingIntent(R.id.button_toggle_play_pause, pendingIntent)
|
||||
|
||||
// Next track
|
||||
pendingIntent = buildPendingIntent(context, ACTION_SKIP, serviceName)
|
||||
views.setOnClickPendingIntent(R.id.button_next, pendingIntent)
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
const val NAME: String = "app_widget_small"
|
||||
|
||||
private var mInstance: AppWidgetSmall? = null
|
||||
private var imageSize = 0
|
||||
private var cardRadius = 0f
|
||||
|
||||
val instance: AppWidgetSmall
|
||||
@Synchronized get() {
|
||||
if (mInstance == null) {
|
||||
mInstance = AppWidgetSmall()
|
||||
}
|
||||
return mInstance!!
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,176 +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.appwidgets
|
||||
|
||||
import android.app.PendingIntent
|
||||
import android.content.ComponentName
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.text.TextUtils
|
||||
import android.view.View
|
||||
import android.widget.RemoteViews
|
||||
import androidx.core.content.ContextCompat
|
||||
import code.name.monkey.retromusic.App
|
||||
import code.name.monkey.retromusic.R
|
||||
import code.name.monkey.retromusic.activities.MainActivity
|
||||
import code.name.monkey.retromusic.appwidgets.base.BaseAppWidget
|
||||
import code.name.monkey.retromusic.service.MusicService
|
||||
import code.name.monkey.retromusic.service.MusicService.*
|
||||
import code.name.monkey.retromusic.util.RetroUtil
|
||||
|
||||
class AppWidgetText : BaseAppWidget() {
|
||||
override fun defaultAppWidget(context: Context, appWidgetIds: IntArray) {
|
||||
val appWidgetView = RemoteViews(context.packageName, R.layout.app_widget_text)
|
||||
|
||||
appWidgetView.setImageViewBitmap(
|
||||
R.id.button_next, createBitmap(
|
||||
RetroUtil.getTintedVectorDrawable(
|
||||
context, R.drawable.ic_skip_next_white_24dp, ContextCompat.getColor(
|
||||
context, R.color.md_white_1000
|
||||
)
|
||||
)!!, 1f
|
||||
)
|
||||
)
|
||||
appWidgetView.setImageViewBitmap(
|
||||
R.id.button_prev, createBitmap(
|
||||
RetroUtil.getTintedVectorDrawable(
|
||||
context, R.drawable.ic_skip_previous_white_24dp, ContextCompat.getColor(
|
||||
context, R.color.md_white_1000
|
||||
)
|
||||
)!!, 1f
|
||||
)
|
||||
)
|
||||
appWidgetView.setImageViewBitmap(
|
||||
R.id.button_toggle_play_pause, createBitmap(
|
||||
RetroUtil.getTintedVectorDrawable(
|
||||
context, R.drawable.ic_play_arrow_white_32dp, ContextCompat.getColor(
|
||||
context, R.color.md_white_1000
|
||||
)
|
||||
)!!, 1f
|
||||
)
|
||||
)
|
||||
|
||||
appWidgetView.setTextColor(
|
||||
R.id.title, ContextCompat.getColor(context, R.color.md_white_1000)
|
||||
)
|
||||
appWidgetView.setTextColor(
|
||||
R.id.text, ContextCompat.getColor(context, R.color.md_white_1000)
|
||||
)
|
||||
|
||||
linkButtons(context, appWidgetView)
|
||||
pushUpdate(context, appWidgetIds, appWidgetView)
|
||||
}
|
||||
|
||||
/**
|
||||
* Link up various button actions using [PendingIntent].
|
||||
*/
|
||||
private fun linkButtons(context: Context, views: RemoteViews) {
|
||||
val action = Intent(context, MainActivity::class.java).putExtra("expand", true)
|
||||
var pendingIntent: PendingIntent
|
||||
|
||||
val serviceName = ComponentName(context, MusicService::class.java)
|
||||
|
||||
// Home
|
||||
action.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TOP
|
||||
pendingIntent = PendingIntent.getActivity(context, 0, action, 0)
|
||||
views.setOnClickPendingIntent(R.id.image, pendingIntent)
|
||||
views.setOnClickPendingIntent(R.id.media_titles, pendingIntent)
|
||||
|
||||
// Previous track
|
||||
pendingIntent = buildPendingIntent(context, ACTION_REWIND, serviceName)
|
||||
views.setOnClickPendingIntent(R.id.button_prev, pendingIntent)
|
||||
|
||||
// Play and pause
|
||||
pendingIntent = buildPendingIntent(context, ACTION_TOGGLE_PAUSE, serviceName)
|
||||
views.setOnClickPendingIntent(R.id.button_toggle_play_pause, pendingIntent)
|
||||
|
||||
// Next track
|
||||
pendingIntent = buildPendingIntent(context, ACTION_SKIP, serviceName)
|
||||
views.setOnClickPendingIntent(R.id.button_next, pendingIntent)
|
||||
}
|
||||
|
||||
override fun performUpdate(service: MusicService, appWidgetIds: IntArray?) {
|
||||
val appWidgetView = RemoteViews(service.packageName, R.layout.app_widget_text)
|
||||
|
||||
val isPlaying = service.isPlaying
|
||||
val song = service.currentSong
|
||||
|
||||
// Set the titles and artwork
|
||||
if (TextUtils.isEmpty(song.title) && TextUtils.isEmpty(song.artistName)) {
|
||||
appWidgetView.setViewVisibility(R.id.media_titles, View.INVISIBLE)
|
||||
} else {
|
||||
appWidgetView.setViewVisibility(R.id.media_titles, View.VISIBLE)
|
||||
appWidgetView.setTextViewText(R.id.title, song.title)
|
||||
appWidgetView.setTextViewText(R.id.text, song.artistName)
|
||||
}
|
||||
// Link actions buttons to intents
|
||||
linkButtons(service, appWidgetView)
|
||||
|
||||
// Set correct drawable for pause state
|
||||
val playPauseRes = if (isPlaying) R.drawable.ic_pause_white_24dp
|
||||
else R.drawable.ic_play_arrow_white_32dp
|
||||
appWidgetView.setImageViewBitmap(
|
||||
R.id.button_toggle_play_pause, createBitmap(
|
||||
RetroUtil.getTintedVectorDrawable(
|
||||
App.getContext(), playPauseRes, ContextCompat.getColor(
|
||||
App.getContext(), R.color.md_white_1000
|
||||
)
|
||||
)!!, 1f
|
||||
)
|
||||
)
|
||||
appWidgetView.setImageViewBitmap(
|
||||
R.id.button_next, createBitmap(
|
||||
RetroUtil.getTintedVectorDrawable(
|
||||
App.getContext(),
|
||||
R.drawable.ic_skip_next_white_24dp,
|
||||
ContextCompat.getColor(
|
||||
App.getContext(), R.color.md_white_1000
|
||||
)
|
||||
)!!, 1f
|
||||
)
|
||||
)
|
||||
appWidgetView.setImageViewBitmap(
|
||||
R.id.button_prev, createBitmap(
|
||||
RetroUtil.getTintedVectorDrawable(
|
||||
App.getContext(),
|
||||
R.drawable.ic_skip_previous_white_24dp,
|
||||
ContextCompat.getColor(
|
||||
App.getContext(), R.color.md_white_1000
|
||||
)
|
||||
)!!, 1f
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
|
||||
pushUpdate(service.applicationContext, appWidgetIds, appWidgetView)
|
||||
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
const val NAME: String = "app_widget_text"
|
||||
|
||||
private var mInstance: AppWidgetText? = null
|
||||
|
||||
val instance: AppWidgetText
|
||||
@Synchronized get() {
|
||||
if (mInstance == null) {
|
||||
mInstance = AppWidgetText()
|
||||
}
|
||||
return mInstance!!
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,54 +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.appwidgets
|
||||
|
||||
import android.appwidget.AppWidgetManager
|
||||
import android.content.BroadcastReceiver
|
||||
import android.content.ComponentName
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.os.Build
|
||||
import code.name.monkey.retromusic.service.MusicService
|
||||
|
||||
class BootReceiver : BroadcastReceiver() {
|
||||
override fun onReceive(context: Context, intent: Intent) {
|
||||
val widgetManager = AppWidgetManager.getInstance(context)
|
||||
|
||||
// Start music service if there are any existing widgets
|
||||
if (widgetManager.getAppWidgetIds(
|
||||
ComponentName(
|
||||
context, AppWidgetBig::class.java
|
||||
)
|
||||
).isNotEmpty() || widgetManager.getAppWidgetIds(
|
||||
ComponentName(
|
||||
context, AppWidgetClassic::class.java
|
||||
)
|
||||
).isNotEmpty() || widgetManager.getAppWidgetIds(
|
||||
ComponentName(
|
||||
context, AppWidgetSmall::class.java
|
||||
)
|
||||
).isNotEmpty() || widgetManager.getAppWidgetIds(
|
||||
ComponentName(
|
||||
context, AppWidgetCard::class.java
|
||||
)
|
||||
).isNotEmpty()
|
||||
) {
|
||||
val serviceIntent = Intent(context, MusicService::class.java)
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) { // not allowed on Oreo
|
||||
context.startService(serviceIntent)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,189 +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.appwidgets.base
|
||||
|
||||
import android.app.PendingIntent
|
||||
import android.appwidget.AppWidgetManager
|
||||
import android.appwidget.AppWidgetProvider
|
||||
import android.content.ComponentName
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.res.Resources
|
||||
import android.graphics.*
|
||||
import android.graphics.drawable.BitmapDrawable
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.os.Build
|
||||
import android.text.TextUtils
|
||||
import android.widget.RemoteViews
|
||||
import androidx.core.content.ContextCompat
|
||||
import code.name.monkey.retromusic.App
|
||||
import code.name.monkey.retromusic.R
|
||||
import code.name.monkey.retromusic.model.Song
|
||||
import code.name.monkey.retromusic.service.MusicService
|
||||
import code.name.monkey.retromusic.service.MusicService.*
|
||||
|
||||
abstract class BaseAppWidget : AppWidgetProvider() {
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
override fun onUpdate(
|
||||
context: Context, appWidgetManager: AppWidgetManager, appWidgetIds: IntArray
|
||||
) {
|
||||
defaultAppWidget(context, appWidgetIds)
|
||||
val updateIntent = Intent(APP_WIDGET_UPDATE)
|
||||
updateIntent.putExtra(EXTRA_APP_WIDGET_NAME, NAME)
|
||||
updateIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, appWidgetIds)
|
||||
updateIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY)
|
||||
context.sendBroadcast(updateIntent)
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle a change notification coming over from [MusicService]
|
||||
*/
|
||||
fun notifyChange(service: MusicService, what: String) {
|
||||
if (hasInstances(service)) {
|
||||
if (META_CHANGED == what || PLAY_STATE_CHANGED == what) {
|
||||
performUpdate(service, null)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected fun pushUpdate(
|
||||
context: Context, appWidgetIds: IntArray?, views: RemoteViews
|
||||
) {
|
||||
val appWidgetManager = AppWidgetManager.getInstance(context)
|
||||
if (appWidgetIds != null) {
|
||||
appWidgetManager.updateAppWidget(appWidgetIds, views)
|
||||
} else {
|
||||
appWidgetManager.updateAppWidget(ComponentName(context, javaClass), views)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check against [AppWidgetManager] if there are any instances of this widget.
|
||||
*/
|
||||
private fun hasInstances(context: Context): Boolean {
|
||||
val appWidgetManager = AppWidgetManager.getInstance(context)
|
||||
val mAppWidgetIds = appWidgetManager.getAppWidgetIds(
|
||||
ComponentName(
|
||||
context, javaClass
|
||||
)
|
||||
)
|
||||
return mAppWidgetIds.isNotEmpty()
|
||||
}
|
||||
|
||||
protected fun buildPendingIntent(
|
||||
context: Context, action: String, serviceName: ComponentName
|
||||
): PendingIntent {
|
||||
val intent = Intent(action)
|
||||
intent.component = serviceName
|
||||
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
PendingIntent.getForegroundService(context, 0, intent, 0)
|
||||
} else {
|
||||
PendingIntent.getService(context, 0, intent, 0)
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract fun defaultAppWidget(context: Context, appWidgetIds: IntArray)
|
||||
|
||||
abstract fun performUpdate(service: MusicService, appWidgetIds: IntArray?)
|
||||
|
||||
protected fun getAlbumArtDrawable(resources: Resources, bitmap: Bitmap?): Drawable {
|
||||
return if (bitmap == null) {
|
||||
ContextCompat.getDrawable(App.getContext(), R.drawable.default_audio_art)!!
|
||||
} else {
|
||||
BitmapDrawable(resources, bitmap)
|
||||
}
|
||||
}
|
||||
|
||||
protected fun getSongArtistAndAlbum(song: Song): String {
|
||||
val builder = StringBuilder()
|
||||
builder.append(song.artistName)
|
||||
if (!TextUtils.isEmpty(song.artistName) && !TextUtils.isEmpty(song.albumName)) {
|
||||
builder.append(" • ")
|
||||
}
|
||||
builder.append(song.albumName)
|
||||
return builder.toString()
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
const val NAME: String = "app_widget"
|
||||
|
||||
fun createRoundedBitmap(
|
||||
drawable: Drawable?,
|
||||
width: Int,
|
||||
height: Int,
|
||||
tl: Float,
|
||||
tr: Float,
|
||||
bl: Float,
|
||||
br: Float
|
||||
): Bitmap? {
|
||||
if (drawable == null) {
|
||||
return null
|
||||
}
|
||||
|
||||
val bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888)
|
||||
val c = Canvas(bitmap)
|
||||
drawable.setBounds(0, 0, width, height)
|
||||
drawable.draw(c)
|
||||
|
||||
val rounded = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888)
|
||||
|
||||
val canvas = Canvas(rounded)
|
||||
val paint = Paint()
|
||||
paint.shader = BitmapShader(bitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP)
|
||||
paint.isAntiAlias = true
|
||||
canvas.drawPath(
|
||||
composeRoundedRectPath(
|
||||
RectF(0f, 0f, width.toFloat(), height.toFloat()), tl, tr, bl, br
|
||||
), paint
|
||||
)
|
||||
|
||||
return rounded
|
||||
}
|
||||
|
||||
fun createBitmap(drawable: Drawable, sizeMultiplier: Float): Bitmap {
|
||||
val bitmap = Bitmap.createBitmap(
|
||||
(drawable.intrinsicWidth * sizeMultiplier).toInt(),
|
||||
(drawable.intrinsicHeight * sizeMultiplier).toInt(),
|
||||
Bitmap.Config.ARGB_8888
|
||||
)
|
||||
val c = Canvas(bitmap)
|
||||
drawable.setBounds(0, 0, c.width, c.height)
|
||||
drawable.draw(c)
|
||||
return bitmap
|
||||
}
|
||||
|
||||
protected fun composeRoundedRectPath(
|
||||
rect: RectF, tl: Float, tr: Float, bl: Float, br: Float
|
||||
): Path {
|
||||
val path = Path()
|
||||
path.moveTo(rect.left + tl, rect.top)
|
||||
path.lineTo(rect.right - tr, rect.top)
|
||||
path.quadTo(rect.right, rect.top, rect.right, rect.top + tr)
|
||||
path.lineTo(rect.right, rect.bottom - br)
|
||||
path.quadTo(rect.right, rect.bottom, rect.right - br, rect.bottom)
|
||||
path.lineTo(rect.left + bl, rect.bottom)
|
||||
path.quadTo(rect.left, rect.bottom, rect.left, rect.bottom - bl)
|
||||
path.lineTo(rect.left, rect.top + tl)
|
||||
path.quadTo(rect.left, rect.top, rect.left + tl, rect.top)
|
||||
path.close()
|
||||
|
||||
return path
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,63 +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
|
||||
|
||||
import code.name.monkey.retromusic.activities.artists.ArtistDetailActivity
|
||||
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.albums.AlbumsFragment
|
||||
import code.name.monkey.retromusic.fragments.artists.ArtistsFragment
|
||||
import code.name.monkey.retromusic.fragments.genres.GenresFragment
|
||||
import code.name.monkey.retromusic.fragments.home.BannerHomeFragment
|
||||
import code.name.monkey.retromusic.fragments.playlists.PlaylistsFragment
|
||||
import code.name.monkey.retromusic.fragments.songs.SongsFragment
|
||||
import dagger.Component
|
||||
import javax.inject.Singleton
|
||||
|
||||
/**
|
||||
* Created by hemanths on 2019-09-04.
|
||||
*/
|
||||
@Singleton
|
||||
@Component(
|
||||
modules = [
|
||||
AppModule::class,
|
||||
PresenterModule::class
|
||||
]
|
||||
)
|
||||
interface MusicComponent {
|
||||
|
||||
fun inject(songsFragment: SongsFragment)
|
||||
|
||||
fun inject(albumsFragment: AlbumsFragment)
|
||||
|
||||
fun inject(artistsFragment: ArtistsFragment)
|
||||
|
||||
fun inject(genresFragment: GenresFragment)
|
||||
|
||||
fun inject(playlistsFragment: PlaylistsFragment)
|
||||
|
||||
fun inject(artistDetailActivity: ArtistDetailActivity)
|
||||
|
||||
fun inject(playlistDetailActivity: PlaylistDetailActivity)
|
||||
|
||||
fun inject(genreDetailsActivity: GenreDetailsActivity)
|
||||
|
||||
fun inject(searchActivity: SearchActivity)
|
||||
|
||||
fun inject(bannerHomeFragment: BannerHomeFragment)
|
||||
}
|
|
@ -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.app.Application
|
||||
import android.content.Context
|
||||
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
import javax.inject.Singleton
|
||||
|
||||
/**
|
||||
* Created by hemanths on 2019-09-04.
|
||||
*/
|
||||
@Module
|
||||
class AppModule(private val application: Application) {
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideContext(): Context = application
|
||||
}
|
|
@ -1,55 +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.mvp.presenter.GenreDetailsPresenter
|
||||
import code.name.monkey.retromusic.mvp.presenter.GenreDetailsPresenter.GenreDetailsPresenterImpl
|
||||
import code.name.monkey.retromusic.mvp.presenter.PlaylistSongsPresenter
|
||||
import code.name.monkey.retromusic.mvp.presenter.PlaylistSongsPresenter.PlaylistSongsPresenterImpl
|
||||
import code.name.monkey.retromusic.mvp.presenter.SearchPresenter
|
||||
import code.name.monkey.retromusic.mvp.presenter.SearchPresenter.SearchPresenterImpl
|
||||
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 providesGenreDetailsPresenter(presenter: GenreDetailsPresenterImpl): GenreDetailsPresenter {
|
||||
return presenter
|
||||
}
|
||||
|
||||
@Provides
|
||||
fun providesPlaylistSongPresenter(presenter: PlaylistSongsPresenterImpl): PlaylistSongsPresenter {
|
||||
return presenter
|
||||
}
|
||||
|
||||
@Provides
|
||||
fun providesSearchPresenter(presenter: SearchPresenterImpl): SearchPresenter {
|
||||
return presenter
|
||||
}
|
||||
}
|
|
@ -1,68 +0,0 @@
|
|||
package code.name.monkey.retromusic.deezer
|
||||
|
||||
import android.content.Context
|
||||
import okhttp3.Cache
|
||||
import okhttp3.Interceptor
|
||||
import okhttp3.OkHttpClient
|
||||
import retrofit2.Call
|
||||
import retrofit2.Retrofit
|
||||
import retrofit2.converter.gson.GsonConverterFactory
|
||||
import retrofit2.create
|
||||
import retrofit2.http.GET
|
||||
import retrofit2.http.Query
|
||||
import java.io.File
|
||||
import java.util.*
|
||||
|
||||
private const val BASE_QUERY_ARTIST = "search/artist"
|
||||
private const val BASE_URL = "https://api.deezer.com/"
|
||||
|
||||
interface DeezerApiService {
|
||||
|
||||
@GET("$BASE_QUERY_ARTIST&limit=1")
|
||||
fun getArtistImage(
|
||||
@Query("q") artistName: String
|
||||
): Call<DeezerResponse>
|
||||
|
||||
companion object {
|
||||
operator fun invoke(
|
||||
client: okhttp3.Call.Factory
|
||||
): DeezerApiService {
|
||||
return Retrofit.Builder()
|
||||
.baseUrl(BASE_URL)
|
||||
.callFactory(client)
|
||||
.addConverterFactory(GsonConverterFactory.create())
|
||||
.build()
|
||||
.create()
|
||||
}
|
||||
|
||||
fun createDefaultOkHttpClient(
|
||||
context: Context
|
||||
): OkHttpClient.Builder = OkHttpClient.Builder()
|
||||
.cache(createDefaultCache(context))
|
||||
.addInterceptor(createCacheControlInterceptor())
|
||||
|
||||
private fun createDefaultCache(
|
||||
context: Context
|
||||
): Cache? {
|
||||
val cacheDir = File(context.applicationContext.cacheDir.absolutePath, "/okhttp-deezer/")
|
||||
if (cacheDir.mkdir() or cacheDir.isDirectory) {
|
||||
return Cache(cacheDir, 1024 * 1024 * 10)
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
private fun createCacheControlInterceptor(): Interceptor {
|
||||
return Interceptor { chain ->
|
||||
val modifiedRequest = chain.request().newBuilder()
|
||||
.addHeader(
|
||||
"Cache-Control",
|
||||
String.format(
|
||||
Locale.getDefault(),
|
||||
"max-age=31536000, max-stale=31536000"
|
||||
)
|
||||
).build()
|
||||
chain.proceed(modifiedRequest)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,31 +0,0 @@
|
|||
package code.name.monkey.retromusic.deezer
|
||||
|
||||
import com.google.gson.annotations.SerializedName
|
||||
|
||||
data class Data(
|
||||
val id: String,
|
||||
val link: String,
|
||||
val name: String,
|
||||
@SerializedName("nb_album")
|
||||
val nbAlbum: Int,
|
||||
@SerializedName("nb_fan")
|
||||
val nbFan: Int,
|
||||
val picture: String,
|
||||
@SerializedName("picture_big")
|
||||
val pictureBig: String,
|
||||
@SerializedName("picture_medium")
|
||||
val pictureMedium: String,
|
||||
@SerializedName("picture_small")
|
||||
val pictureSmall: String,
|
||||
@SerializedName("picture_xl")
|
||||
val pictureXl: String,
|
||||
val radio: Boolean,
|
||||
val tracklist: String,
|
||||
val type: String
|
||||
)
|
||||
|
||||
data class DeezerResponse(
|
||||
val data: List<Data>,
|
||||
val next: String,
|
||||
val total: Int
|
||||
)
|
|
@ -1,79 +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.dialogs
|
||||
|
||||
import android.app.Dialog
|
||||
import android.os.Bundle
|
||||
import androidx.fragment.app.DialogFragment
|
||||
import code.name.monkey.retromusic.EXTRA_SONG
|
||||
import code.name.monkey.retromusic.R
|
||||
import code.name.monkey.retromusic.extensions.extraNotNull
|
||||
import code.name.monkey.retromusic.loaders.PlaylistLoader
|
||||
import code.name.monkey.retromusic.model.Song
|
||||
import code.name.monkey.retromusic.util.PlaylistsUtil
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
|
||||
class AddToPlaylistDialog : DialogFragment() {
|
||||
|
||||
override fun onCreateDialog(
|
||||
savedInstanceState: Bundle?
|
||||
): Dialog {
|
||||
val playlists = PlaylistLoader.getAllPlaylists(requireContext())
|
||||
val playlistNames = mutableListOf<CharSequence>()
|
||||
playlistNames.add(requireContext().resources.getString(R.string.action_new_playlist))
|
||||
for (p in playlists) {
|
||||
playlistNames.add(p.name)
|
||||
}
|
||||
|
||||
return MaterialAlertDialogBuilder(
|
||||
requireContext(),
|
||||
R.style.ThemeOverlay_MaterialComponents_Dialog_Alert
|
||||
)
|
||||
.setTitle(R.string.add_playlist_title)
|
||||
.setItems(playlistNames.toTypedArray()) { _, which ->
|
||||
val songs = extraNotNull<ArrayList<Song>>(EXTRA_SONG).value
|
||||
if (which == 0) {
|
||||
CreatePlaylistDialog.create(songs)
|
||||
.show(requireActivity().supportFragmentManager, "ADD_TO_PLAYLIST")
|
||||
} else {
|
||||
PlaylistsUtil.addToPlaylist(
|
||||
requireContext(),
|
||||
songs,
|
||||
playlists[which - 1].id,
|
||||
true
|
||||
)
|
||||
}
|
||||
dismiss()
|
||||
}
|
||||
.create()
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
fun create(song: Song): AddToPlaylistDialog {
|
||||
val list = ArrayList<Song>()
|
||||
list.add(song)
|
||||
return create(list)
|
||||
}
|
||||
|
||||
fun create(songs: List<Song>): AddToPlaylistDialog {
|
||||
val dialog = AddToPlaylistDialog()
|
||||
val args = Bundle()
|
||||
args.putParcelableArrayList(EXTRA_SONG, ArrayList(songs))
|
||||
dialog.arguments = args
|
||||
return dialog
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,157 +0,0 @@
|
|||
package code.name.monkey.retromusic.dialogs;
|
||||
|
||||
import android.Manifest;
|
||||
import android.app.Dialog;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.os.Environment;
|
||||
import android.view.View;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.core.app.ActivityCompat;
|
||||
import androidx.fragment.app.DialogFragment;
|
||||
|
||||
import com.afollestad.materialdialogs.MaterialDialog;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
|
||||
import code.name.monkey.retromusic.R;
|
||||
|
||||
public class BlacklistFolderChooserDialog extends DialogFragment implements MaterialDialog.ListCallback {
|
||||
|
||||
String initialPath = Environment.getExternalStorageDirectory().getAbsolutePath();
|
||||
private File parentFolder;
|
||||
private File[] parentContents;
|
||||
private boolean canGoUp = false;
|
||||
private FolderCallback callback;
|
||||
|
||||
public static BlacklistFolderChooserDialog create() {
|
||||
return new BlacklistFolderChooserDialog();
|
||||
}
|
||||
|
||||
private String[] getContentsArray() {
|
||||
if (parentContents == null) {
|
||||
if (canGoUp) {
|
||||
return new String[]{".."};
|
||||
}
|
||||
return new String[]{};
|
||||
}
|
||||
String[] results = new String[parentContents.length + (canGoUp ? 1 : 0)];
|
||||
if (canGoUp) {
|
||||
results[0] = "..";
|
||||
}
|
||||
for (int i = 0; i < parentContents.length; i++) {
|
||||
results[canGoUp ? i + 1 : i] = parentContents[i].getName();
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
private File[] listFiles() {
|
||||
File[] contents = parentFolder.listFiles();
|
||||
List<File> results = new ArrayList<>();
|
||||
if (contents != null) {
|
||||
for (File fi : contents) {
|
||||
if (fi.isDirectory()) {
|
||||
results.add(fi);
|
||||
}
|
||||
}
|
||||
Collections.sort(results, new FolderSorter());
|
||||
return results.toArray(new File[results.size()]);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M
|
||||
&& ActivityCompat.checkSelfPermission(
|
||||
getActivity(), Manifest.permission.READ_EXTERNAL_STORAGE)
|
||||
!= PackageManager.PERMISSION_GRANTED) {
|
||||
return new MaterialDialog.Builder(getActivity())
|
||||
.title(R.string.md_error_label)
|
||||
.content(R.string.md_storage_perm_error)
|
||||
.positiveText(android.R.string.ok)
|
||||
.build();
|
||||
}
|
||||
if (savedInstanceState == null) {
|
||||
savedInstanceState = new Bundle();
|
||||
}
|
||||
if (!savedInstanceState.containsKey("current_path")) {
|
||||
savedInstanceState.putString("current_path", initialPath);
|
||||
}
|
||||
parentFolder = new File(savedInstanceState.getString("current_path", File.pathSeparator));
|
||||
checkIfCanGoUp();
|
||||
parentContents = listFiles();
|
||||
MaterialDialog.Builder builder =
|
||||
new MaterialDialog.Builder(getActivity())
|
||||
.title(parentFolder.getAbsolutePath())
|
||||
.items((CharSequence[]) getContentsArray())
|
||||
.itemsCallback(this)
|
||||
.autoDismiss(false)
|
||||
.onPositive((dialog, which) -> {
|
||||
dismiss();
|
||||
callback.onFolderSelection(BlacklistFolderChooserDialog.this, parentFolder);
|
||||
})
|
||||
.onNegative((materialDialog, dialogAction) -> dismiss())
|
||||
.positiveText(R.string.add_action)
|
||||
.negativeText(android.R.string.cancel);
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSelection(MaterialDialog materialDialog, View view, int i, CharSequence s) {
|
||||
if (canGoUp && i == 0) {
|
||||
parentFolder = parentFolder.getParentFile();
|
||||
if (parentFolder.getAbsolutePath().equals("/storage/emulated")) {
|
||||
parentFolder = parentFolder.getParentFile();
|
||||
}
|
||||
checkIfCanGoUp();
|
||||
} else {
|
||||
parentFolder = parentContents[canGoUp ? i - 1 : i];
|
||||
canGoUp = true;
|
||||
if (parentFolder.getAbsolutePath().equals("/storage/emulated")) {
|
||||
parentFolder = Environment.getExternalStorageDirectory();
|
||||
}
|
||||
}
|
||||
reload();
|
||||
}
|
||||
|
||||
private void checkIfCanGoUp() {
|
||||
canGoUp = parentFolder.getParent() != null;
|
||||
}
|
||||
|
||||
private void reload() {
|
||||
parentContents = listFiles();
|
||||
MaterialDialog dialog = (MaterialDialog) getDialog();
|
||||
dialog.setTitle(parentFolder.getAbsolutePath());
|
||||
dialog.setItems((CharSequence[]) getContentsArray());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSaveInstanceState(Bundle outState) {
|
||||
super.onSaveInstanceState(outState);
|
||||
outState.putString("current_path", parentFolder.getAbsolutePath());
|
||||
}
|
||||
|
||||
public void setCallback(FolderCallback callback) {
|
||||
this.callback = callback;
|
||||
}
|
||||
|
||||
public interface FolderCallback {
|
||||
void onFolderSelection(@NonNull BlacklistFolderChooserDialog dialog, @NonNull File folder);
|
||||
}
|
||||
|
||||
private static class FolderSorter implements Comparator<File> {
|
||||
|
||||
@Override
|
||||
public int compare(File lhs, File rhs) {
|
||||
return lhs.getName().compareTo(rhs.getName());
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,90 +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.dialogs
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.app.Dialog
|
||||
import android.os.Bundle
|
||||
import android.text.TextUtils
|
||||
import android.view.LayoutInflater
|
||||
import androidx.fragment.app.DialogFragment
|
||||
import code.name.monkey.appthemehelper.util.MaterialUtil
|
||||
import code.name.monkey.retromusic.EXTRA_SONG
|
||||
import code.name.monkey.retromusic.R
|
||||
import code.name.monkey.retromusic.extensions.extraNotNull
|
||||
import code.name.monkey.retromusic.model.Song
|
||||
import code.name.monkey.retromusic.util.PlaylistsUtil
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import com.google.android.material.textfield.TextInputEditText
|
||||
import com.google.android.material.textfield.TextInputLayout
|
||||
import kotlinx.android.synthetic.main.dialog_playlist.view.*
|
||||
|
||||
class CreatePlaylistDialog : DialogFragment() {
|
||||
|
||||
@SuppressLint("InflateParams")
|
||||
override fun onCreateDialog(
|
||||
savedInstanceState: Bundle?
|
||||
): Dialog {
|
||||
val view = LayoutInflater.from(requireActivity()).inflate(R.layout.dialog_playlist, null)
|
||||
val playlistView: TextInputEditText = view.actionNewPlaylist
|
||||
val playlistContainer: TextInputLayout = view.actionNewPlaylistContainer
|
||||
MaterialUtil.setTint(playlistContainer, false)
|
||||
|
||||
return MaterialAlertDialogBuilder(
|
||||
requireActivity(),
|
||||
R.style.ThemeOverlay_MaterialComponents_Dialog_Alert
|
||||
)
|
||||
.setTitle(R.string.new_playlist_title)
|
||||
.setView(view)
|
||||
.setNegativeButton(android.R.string.cancel, null)
|
||||
.setPositiveButton(
|
||||
R.string.create_action
|
||||
) { _, _ ->
|
||||
val extra = extraNotNull<ArrayList<Song>>(EXTRA_SONG)
|
||||
val playlistName = playlistView.text.toString()
|
||||
if (!TextUtils.isEmpty(playlistName)) {
|
||||
val playlistId = PlaylistsUtil.createPlaylist(
|
||||
requireContext(),
|
||||
playlistView.text.toString()
|
||||
)
|
||||
if (playlistId != -1) {
|
||||
PlaylistsUtil.addToPlaylist(requireContext(), extra.value, playlistId, true)
|
||||
}
|
||||
}
|
||||
}
|
||||
.create()
|
||||
}
|
||||
|
||||
companion object {
|
||||
@JvmOverloads
|
||||
@JvmStatic
|
||||
fun create(song: Song? = null): CreatePlaylistDialog {
|
||||
val list = ArrayList<Song>()
|
||||
if (song != null) {
|
||||
list.add(song)
|
||||
}
|
||||
return create(list)
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun create(songs: ArrayList<Song>): CreatePlaylistDialog {
|
||||
val dialog = CreatePlaylistDialog()
|
||||
val args = Bundle()
|
||||
args.putParcelableArrayList(EXTRA_SONG, songs)
|
||||
dialog.arguments = args
|
||||
return dialog
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,78 +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.dialogs
|
||||
|
||||
import android.app.Dialog
|
||||
import android.os.Bundle
|
||||
import androidx.core.text.HtmlCompat
|
||||
import androidx.fragment.app.DialogFragment
|
||||
import code.name.monkey.retromusic.EXTRA_PLAYLIST
|
||||
import code.name.monkey.retromusic.R
|
||||
import code.name.monkey.retromusic.extensions.extraNotNull
|
||||
import code.name.monkey.retromusic.model.Playlist
|
||||
import code.name.monkey.retromusic.util.PlaylistsUtil
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
|
||||
class DeletePlaylistDialog : DialogFragment() {
|
||||
|
||||
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
||||
val playlists = extraNotNull<List<Playlist>>(EXTRA_PLAYLIST).value
|
||||
val title: Int
|
||||
val message: CharSequence
|
||||
//noinspection ConstantConditions
|
||||
if (playlists.size > 1) {
|
||||
title = R.string.delete_playlists_title
|
||||
message = HtmlCompat.fromHtml(
|
||||
String.format(getString(R.string.delete_x_playlists), playlists.size),
|
||||
HtmlCompat.FROM_HTML_MODE_LEGACY
|
||||
)
|
||||
} else {
|
||||
title = R.string.delete_playlist_title
|
||||
message = HtmlCompat.fromHtml(
|
||||
String.format(getString(R.string.delete_playlist_x), playlists[0].name),
|
||||
HtmlCompat.FROM_HTML_MODE_LEGACY
|
||||
)
|
||||
}
|
||||
|
||||
return MaterialAlertDialogBuilder(
|
||||
requireContext(),
|
||||
R.style.ThemeOverlay_MaterialComponents_Dialog_Alert
|
||||
)
|
||||
.setTitle(title)
|
||||
.setMessage(message)
|
||||
.setNegativeButton(android.R.string.cancel, null)
|
||||
.setPositiveButton(R.string.action_delete) { _, _ ->
|
||||
PlaylistsUtil.deletePlaylists(requireContext(), playlists)
|
||||
}
|
||||
.create()
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
fun create(playlist: Playlist): DeletePlaylistDialog {
|
||||
val list = ArrayList<Playlist>()
|
||||
list.add(playlist)
|
||||
return create(list)
|
||||
}
|
||||
|
||||
fun create(playlist: ArrayList<Playlist>): DeletePlaylistDialog {
|
||||
val dialog = DeletePlaylistDialog()
|
||||
val args = Bundle()
|
||||
args.putParcelableArrayList(EXTRA_PLAYLIST, playlist)
|
||||
dialog.arguments = args
|
||||
return dialog
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,145 +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.dialogs;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.Dialog;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.fragment.app.FragmentActivity;
|
||||
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import code.name.monkey.retromusic.R;
|
||||
import code.name.monkey.retromusic.activities.saf.SAFGuideActivity;
|
||||
import code.name.monkey.retromusic.misc.DialogAsyncTask;
|
||||
import code.name.monkey.retromusic.model.Song;
|
||||
import code.name.monkey.retromusic.util.SAFUtil;
|
||||
|
||||
/**
|
||||
* Created by hemanths on 2019-07-31.
|
||||
*/
|
||||
public class DeleteSongsAsyncTask extends DialogAsyncTask<DeleteSongsAsyncTask.LoadingInfo, Integer, Void> {
|
||||
|
||||
private WeakReference<FragmentActivity> activityWeakReference;
|
||||
private WeakReference<DeleteSongsDialog> dialogReference;
|
||||
|
||||
public DeleteSongsAsyncTask(@NonNull DeleteSongsDialog dialog) {
|
||||
super(dialog.getActivity());
|
||||
this.dialogReference = new WeakReference<>(dialog);
|
||||
this.activityWeakReference = new WeakReference<>(dialog.getActivity());
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
protected Dialog createDialog(@NonNull Context context) {
|
||||
return new MaterialAlertDialogBuilder(context,
|
||||
R.style.ThemeOverlay_MaterialComponents_Dialog_Alert)
|
||||
.setTitle(R.string.deleting_songs)
|
||||
.setView(R.layout.loading)
|
||||
.setCancelable(false)
|
||||
.create();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
protected Void doInBackground(@NonNull LoadingInfo... loadingInfos) {
|
||||
try {
|
||||
LoadingInfo info = loadingInfos[0];
|
||||
DeleteSongsDialog dialog = this.dialogReference.get();
|
||||
FragmentActivity fragmentActivity = this.activityWeakReference.get();
|
||||
|
||||
if (dialog == null || fragmentActivity == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!info.isIntent) {
|
||||
if (!SAFUtil.isSAFRequiredForSongs(info.songs)) {
|
||||
dialog.deleteSongs(info.songs, null);
|
||||
} else {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||
if (SAFUtil.isSDCardAccessGranted(fragmentActivity)) {
|
||||
dialog.deleteSongs(info.songs, null);
|
||||
} else {
|
||||
dialog.startActivityForResult(new Intent(fragmentActivity, SAFGuideActivity.class),
|
||||
SAFGuideActivity.REQUEST_CODE_SAF_GUIDE);
|
||||
}
|
||||
} else {
|
||||
Log.i("Hmm", "doInBackground: kitkat delete songs");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
switch (info.requestCode) {
|
||||
case SAFUtil.REQUEST_SAF_PICK_TREE:
|
||||
if (info.resultCode == Activity.RESULT_OK) {
|
||||
SAFUtil.saveTreeUri(fragmentActivity, info.intent);
|
||||
if (dialog.songsToRemove != null) {
|
||||
dialog.deleteSongs(dialog.songsToRemove, null);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case SAFUtil.REQUEST_SAF_PICK_FILE:
|
||||
if (info.resultCode == Activity.RESULT_OK) {
|
||||
dialog.deleteSongs(Collections.singletonList(dialog.currentSong),
|
||||
Collections.singletonList(info.intent.getData()));
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public static class LoadingInfo {
|
||||
|
||||
public Intent intent;
|
||||
|
||||
public boolean isIntent;
|
||||
|
||||
public int requestCode;
|
||||
|
||||
public int resultCode;
|
||||
|
||||
public List<Uri> safUris;
|
||||
|
||||
public List<Song> songs;
|
||||
|
||||
public LoadingInfo(List<Song> songs, List<Uri> safUris) {
|
||||
this.isIntent = false;
|
||||
this.songs = songs;
|
||||
this.safUris = safUris;
|
||||
}
|
||||
|
||||
public LoadingInfo(int requestCode, int resultCode, Intent intent) {
|
||||
this.isIntent = true;
|
||||
this.requestCode = requestCode;
|
||||
this.resultCode = resultCode;
|
||||
this.intent = intent;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,123 +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.dialogs
|
||||
|
||||
import android.app.Dialog
|
||||
import android.content.Intent
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
import androidx.core.text.HtmlCompat
|
||||
import androidx.fragment.app.DialogFragment
|
||||
import code.name.monkey.retromusic.EXTRA_SONG
|
||||
import code.name.monkey.retromusic.R
|
||||
import code.name.monkey.retromusic.activities.saf.SAFGuideActivity
|
||||
import code.name.monkey.retromusic.extensions.extraNotNull
|
||||
import code.name.monkey.retromusic.helper.MusicPlayerRemote
|
||||
import code.name.monkey.retromusic.model.Song
|
||||
import code.name.monkey.retromusic.util.MusicUtil
|
||||
import code.name.monkey.retromusic.util.SAFUtil
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
|
||||
class DeleteSongsDialog : DialogFragment() {
|
||||
@JvmField
|
||||
var currentSong: Song? = null
|
||||
|
||||
@JvmField
|
||||
var songsToRemove: List<Song>? = null
|
||||
|
||||
private var deleteSongsAsyncTask: DeleteSongsAsyncTask? = null
|
||||
|
||||
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
||||
val songs = extraNotNull<List<Song>>(EXTRA_SONG).value
|
||||
var title = 0
|
||||
var message: CharSequence = ""
|
||||
if (songs.size > 1) {
|
||||
title = R.string.delete_songs_title
|
||||
message = HtmlCompat.fromHtml(
|
||||
String.format(getString(R.string.delete_x_songs), songs.size),
|
||||
HtmlCompat.FROM_HTML_MODE_LEGACY
|
||||
)
|
||||
} else {
|
||||
title = R.string.delete_song_title
|
||||
message = HtmlCompat.fromHtml(
|
||||
String.format(getString(R.string.delete_song_x), songs[0].title),
|
||||
HtmlCompat.FROM_HTML_MODE_LEGACY
|
||||
)
|
||||
}
|
||||
|
||||
return MaterialAlertDialogBuilder(
|
||||
requireContext(),
|
||||
R.style.ThemeOverlay_MaterialComponents_Dialog_Alert
|
||||
)
|
||||
.setTitle(title)
|
||||
.setMessage(message)
|
||||
.setCancelable(false)
|
||||
.setNegativeButton(android.R.string.cancel, null)
|
||||
.setPositiveButton(R.string.action_delete) { _, _ ->
|
||||
if ((songs.size == 1) && MusicPlayerRemote.isPlaying(songs[0])) {
|
||||
MusicPlayerRemote.playNextSong()
|
||||
}
|
||||
songsToRemove = songs
|
||||
deleteSongsAsyncTask = DeleteSongsAsyncTask(this@DeleteSongsDialog)
|
||||
deleteSongsAsyncTask?.execute(DeleteSongsAsyncTask.LoadingInfo(songs, null))
|
||||
}
|
||||
.create()
|
||||
}
|
||||
|
||||
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
||||
super.onActivityResult(requestCode, resultCode, data)
|
||||
when (requestCode) {
|
||||
SAFGuideActivity.REQUEST_CODE_SAF_GUIDE -> {
|
||||
SAFUtil.openTreePicker(this)
|
||||
}
|
||||
SAFUtil.REQUEST_SAF_PICK_TREE,
|
||||
SAFUtil.REQUEST_SAF_PICK_FILE -> {
|
||||
if (deleteSongsAsyncTask != null) {
|
||||
deleteSongsAsyncTask?.cancel(true)
|
||||
}
|
||||
deleteSongsAsyncTask = DeleteSongsAsyncTask(this)
|
||||
deleteSongsAsyncTask?.execute(
|
||||
DeleteSongsAsyncTask.LoadingInfo(
|
||||
requestCode,
|
||||
resultCode,
|
||||
data
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun deleteSongs(songs: List<Song>, safUris: List<Uri>?) {
|
||||
MusicUtil.deleteTracks(requireActivity(), songs, safUris) { this.dismiss() }
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
fun create(song: Song): DeleteSongsDialog {
|
||||
val list = ArrayList<Song>()
|
||||
list.add(song)
|
||||
return create(list)
|
||||
}
|
||||
|
||||
fun create(songs: List<Song>): DeleteSongsDialog {
|
||||
val dialog = DeleteSongsDialog()
|
||||
val args = Bundle()
|
||||
args.putParcelableArrayList(EXTRA_SONG, ArrayList(songs))
|
||||
dialog.arguments = args
|
||||
return dialog
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,86 +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.dialogs
|
||||
|
||||
import android.app.Dialog
|
||||
import android.os.Bundle
|
||||
import androidx.core.text.HtmlCompat
|
||||
import androidx.fragment.app.DialogFragment
|
||||
import code.name.monkey.retromusic.EXTRA_SONG
|
||||
import code.name.monkey.retromusic.R
|
||||
import code.name.monkey.retromusic.R.string
|
||||
import code.name.monkey.retromusic.model.PlaylistSong
|
||||
import code.name.monkey.retromusic.util.PlaylistsUtil
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
|
||||
class RemoveFromPlaylistDialog : DialogFragment() {
|
||||
|
||||
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
||||
val songs = requireArguments().getParcelableArrayList<PlaylistSong>(EXTRA_SONG)
|
||||
|
||||
var title = 0
|
||||
var message: CharSequence = ""
|
||||
if (songs != null) {
|
||||
if (songs.size > 1) {
|
||||
title = R.string.remove_songs_from_playlist_title
|
||||
message = HtmlCompat.fromHtml(
|
||||
String.format(getString(string.remove_x_songs_from_playlist), songs.size),
|
||||
HtmlCompat.FROM_HTML_MODE_LEGACY
|
||||
)
|
||||
} else {
|
||||
title = R.string.remove_song_from_playlist_title
|
||||
message = HtmlCompat.fromHtml(
|
||||
String.format(
|
||||
getString(string.remove_song_x_from_playlist),
|
||||
songs[0].title
|
||||
),
|
||||
HtmlCompat.FROM_HTML_MODE_LEGACY
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
return MaterialAlertDialogBuilder(
|
||||
requireContext(),
|
||||
R.style.ThemeOverlay_MaterialComponents_Dialog_Alert
|
||||
)
|
||||
.setTitle(title)
|
||||
.setMessage(message)
|
||||
.setPositiveButton(R.string.remove_action) { _, _ ->
|
||||
PlaylistsUtil.removeFromPlaylist(
|
||||
requireContext(),
|
||||
songs as MutableList<PlaylistSong>
|
||||
)
|
||||
}
|
||||
.setNegativeButton(android.R.string.cancel, null)
|
||||
.create()
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
fun create(song: PlaylistSong): RemoveFromPlaylistDialog {
|
||||
val list = ArrayList<PlaylistSong>()
|
||||
list.add(song)
|
||||
return create(list)
|
||||
}
|
||||
|
||||
fun create(songs: ArrayList<PlaylistSong>): RemoveFromPlaylistDialog {
|
||||
val dialog = RemoveFromPlaylistDialog()
|
||||
val args = Bundle()
|
||||
args.putParcelableArrayList(EXTRA_SONG, songs)
|
||||
dialog.arguments = args
|
||||
return dialog
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,75 +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.dialogs
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.app.Dialog
|
||||
import android.os.Bundle
|
||||
import android.provider.MediaStore.Audio.Playlists.Members.PLAYLIST_ID
|
||||
import android.view.LayoutInflater
|
||||
import androidx.fragment.app.DialogFragment
|
||||
import code.name.monkey.appthemehelper.util.MaterialUtil
|
||||
import code.name.monkey.retromusic.R
|
||||
import code.name.monkey.retromusic.extensions.extraNotNull
|
||||
import code.name.monkey.retromusic.util.PlaylistsUtil
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import com.google.android.material.textfield.TextInputEditText
|
||||
import com.google.android.material.textfield.TextInputLayout
|
||||
|
||||
class RenamePlaylistDialog : DialogFragment() {
|
||||
|
||||
|
||||
@SuppressLint("InflateParams")
|
||||
override fun onCreateDialog(
|
||||
savedInstanceState: Bundle?
|
||||
): Dialog {
|
||||
val layout = LayoutInflater.from(requireContext())
|
||||
.inflate(R.layout.dialog_playlist, null)
|
||||
val inputEditText: TextInputEditText = layout.findViewById(R.id.actionNewPlaylist)
|
||||
val nameContainer: TextInputLayout =
|
||||
layout.findViewById(R.id.actionNewPlaylistContainer)
|
||||
MaterialUtil.setTint(nameContainer, false)
|
||||
|
||||
return MaterialAlertDialogBuilder(
|
||||
requireContext(),
|
||||
R.style.ThemeOverlay_MaterialComponents_Dialog_Alert
|
||||
)
|
||||
.setTitle(R.string.rename_playlist_title)
|
||||
.setView(layout)
|
||||
.setNegativeButton(android.R.string.cancel, null)
|
||||
.setPositiveButton(R.string.action_rename) { _, _ ->
|
||||
val name = inputEditText.text.toString()
|
||||
if (name.isNotEmpty()) {
|
||||
PlaylistsUtil.renamePlaylist(
|
||||
requireContext(),
|
||||
extraNotNull<Long>(PLAYLIST_ID).value,
|
||||
name
|
||||
)
|
||||
}
|
||||
}
|
||||
.create()
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
fun create(playlistId: Long): RenamePlaylistDialog {
|
||||
val dialog = RenamePlaylistDialog()
|
||||
val args = Bundle()
|
||||
args.putLong(PLAYLIST_ID, playlistId)
|
||||
dialog.arguments = args
|
||||
return dialog
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,177 +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.dialogs
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.app.AlarmManager
|
||||
import android.app.Dialog
|
||||
import android.app.PendingIntent
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import android.os.CountDownTimer
|
||||
import android.os.SystemClock
|
||||
import android.view.LayoutInflater
|
||||
import android.widget.CheckBox
|
||||
import android.widget.SeekBar
|
||||
import android.widget.TextView
|
||||
import android.widget.Toast
|
||||
import androidx.fragment.app.DialogFragment
|
||||
import code.name.monkey.retromusic.R
|
||||
import code.name.monkey.retromusic.extensions.addAccentColor
|
||||
import code.name.monkey.retromusic.helper.MusicPlayerRemote
|
||||
import code.name.monkey.retromusic.service.MusicService
|
||||
import code.name.monkey.retromusic.service.MusicService.ACTION_PENDING_QUIT
|
||||
import code.name.monkey.retromusic.service.MusicService.ACTION_QUIT
|
||||
import code.name.monkey.retromusic.util.PreferenceUtil
|
||||
import com.afollestad.materialdialogs.DialogAction
|
||||
import com.afollestad.materialdialogs.MaterialDialog
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
|
||||
class SleepTimerDialog : DialogFragment() {
|
||||
|
||||
private var seekArcProgress: Int = 0
|
||||
private lateinit var timerUpdater: TimerUpdater
|
||||
private lateinit var materialDialog: MaterialDialog
|
||||
private lateinit var shouldFinishLastSong: CheckBox
|
||||
private lateinit var seekBar: SeekBar
|
||||
private lateinit var timerDisplay: TextView
|
||||
|
||||
@SuppressLint("InflateParams")
|
||||
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
||||
timerUpdater = TimerUpdater()
|
||||
val layout = LayoutInflater.from(requireContext())
|
||||
.inflate(R.layout.dialog_sleep_timer, null)
|
||||
shouldFinishLastSong = layout.findViewById(R.id.shouldFinishLastSong)
|
||||
seekBar = layout.findViewById(R.id.seekBar)
|
||||
timerDisplay = layout.findViewById(R.id.timerDisplay)
|
||||
|
||||
val finishMusic = PreferenceUtil.isSleepTimerFinishMusic
|
||||
shouldFinishLastSong.apply {
|
||||
addAccentColor()
|
||||
isChecked = finishMusic
|
||||
}
|
||||
seekBar.apply {
|
||||
addAccentColor()
|
||||
seekArcProgress = PreferenceUtil.lastSleepTimerValue
|
||||
updateTimeDisplayTime()
|
||||
seekBar.progress = seekArcProgress
|
||||
}
|
||||
|
||||
seekBar.setOnSeekBarChangeListener(object : SeekBar.OnSeekBarChangeListener {
|
||||
override fun onProgressChanged(seekBar: SeekBar, i: Int, b: Boolean) {
|
||||
if (i < 1) {
|
||||
seekBar.progress = 1
|
||||
return
|
||||
}
|
||||
seekArcProgress = i
|
||||
updateTimeDisplayTime()
|
||||
}
|
||||
|
||||
override fun onStartTrackingTouch(seekBar: SeekBar) {
|
||||
}
|
||||
|
||||
override fun onStopTrackingTouch(seekBar: SeekBar) {
|
||||
PreferenceUtil.lastSleepTimerValue = seekArcProgress
|
||||
}
|
||||
})
|
||||
return MaterialAlertDialogBuilder(
|
||||
requireContext(),
|
||||
R.style.ThemeOverlay_MaterialComponents_Dialog_Alert
|
||||
)
|
||||
.setTitle(R.string.action_sleep_timer)
|
||||
.setView(layout)
|
||||
.setPositiveButton(R.string.action_set) { _, _ ->
|
||||
PreferenceUtil.isSleepTimerFinishMusic = shouldFinishLastSong.isChecked
|
||||
val minutes = seekArcProgress
|
||||
val pi = makeTimerPendingIntent(PendingIntent.FLAG_CANCEL_CURRENT)
|
||||
val nextSleepTimerElapsedTime = SystemClock.elapsedRealtime() + minutes * 60 * 1000
|
||||
PreferenceUtil.nextSleepTimerElapsedRealTime = nextSleepTimerElapsedTime.toInt()
|
||||
val am = requireContext().getSystemService(Context.ALARM_SERVICE) as AlarmManager
|
||||
am.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, nextSleepTimerElapsedTime, pi)
|
||||
|
||||
Toast.makeText(
|
||||
requireContext(),
|
||||
requireContext().resources.getString(R.string.sleep_timer_set, minutes),
|
||||
Toast.LENGTH_SHORT
|
||||
).show()
|
||||
}
|
||||
.setNegativeButton(android.R.string.cancel) { _, _ ->
|
||||
val previous = makeTimerPendingIntent(PendingIntent.FLAG_NO_CREATE)
|
||||
if (previous != null) {
|
||||
val am =
|
||||
requireContext().getSystemService(Context.ALARM_SERVICE) as AlarmManager
|
||||
am.cancel(previous)
|
||||
previous.cancel()
|
||||
Toast.makeText(
|
||||
requireContext(),
|
||||
requireContext().resources.getString(R.string.sleep_timer_canceled),
|
||||
Toast.LENGTH_SHORT
|
||||
).show()
|
||||
val musicService = MusicPlayerRemote.musicService
|
||||
if (musicService != null && musicService.pendingQuit) {
|
||||
musicService.pendingQuit = false
|
||||
Toast.makeText(
|
||||
requireContext(),
|
||||
requireContext().resources.getString(R.string.sleep_timer_canceled),
|
||||
Toast.LENGTH_SHORT
|
||||
).show()
|
||||
}
|
||||
}
|
||||
}
|
||||
.create()
|
||||
}
|
||||
|
||||
private fun updateTimeDisplayTime() {
|
||||
timerDisplay.text = "$seekArcProgress min"
|
||||
}
|
||||
|
||||
private fun makeTimerPendingIntent(flag: Int): PendingIntent? {
|
||||
return PendingIntent.getService(requireActivity(), 0, makeTimerIntent(), flag)
|
||||
}
|
||||
|
||||
private fun makeTimerIntent(): Intent {
|
||||
val intent = Intent(requireActivity(), MusicService::class.java)
|
||||
return if (shouldFinishLastSong.isChecked) {
|
||||
intent.setAction(ACTION_PENDING_QUIT)
|
||||
} else intent.setAction(ACTION_QUIT)
|
||||
}
|
||||
|
||||
private fun updateCancelButton() {
|
||||
val musicService = MusicPlayerRemote.musicService
|
||||
if (musicService != null && musicService.pendingQuit) {
|
||||
materialDialog.getActionButton(DialogAction.NEUTRAL).text =
|
||||
materialDialog.context.getString(R.string.cancel_current_timer)
|
||||
} else {
|
||||
materialDialog.getActionButton(DialogAction.NEUTRAL).text = null
|
||||
}
|
||||
}
|
||||
|
||||
private inner class TimerUpdater internal constructor() :
|
||||
CountDownTimer(
|
||||
PreferenceUtil.nextSleepTimerElapsedRealTime - SystemClock.elapsedRealtime(),
|
||||
1000
|
||||
) {
|
||||
|
||||
override fun onTick(millisUntilFinished: Long) {
|
||||
println("onTick: $millisUntilFinished")
|
||||
seekBar.progress = millisUntilFinished.toInt()
|
||||
}
|
||||
|
||||
override fun onFinish() {
|
||||
updateCancelButton()
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,187 +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.dialogs
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.app.Dialog
|
||||
import android.content.Context
|
||||
import android.os.Bundle
|
||||
import android.text.Spanned
|
||||
import android.util.Log
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.TextView
|
||||
import androidx.annotation.NonNull
|
||||
import androidx.core.text.HtmlCompat
|
||||
import androidx.fragment.app.DialogFragment
|
||||
import code.name.monkey.retromusic.EXTRA_SONG
|
||||
import code.name.monkey.retromusic.R
|
||||
import code.name.monkey.retromusic.model.Song
|
||||
import code.name.monkey.retromusic.util.MusicUtil
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import org.jaudiotagger.audio.AudioFileIO
|
||||
import org.jaudiotagger.audio.exceptions.CannotReadException
|
||||
import org.jaudiotagger.audio.exceptions.InvalidAudioFrameException
|
||||
import org.jaudiotagger.audio.exceptions.ReadOnlyFileException
|
||||
import org.jaudiotagger.tag.TagException
|
||||
import java.io.File
|
||||
import java.io.IOException
|
||||
|
||||
inline fun ViewGroup.forEach(action: (View) -> Unit) {
|
||||
for (i in 0 until childCount) {
|
||||
action(getChildAt(i))
|
||||
}
|
||||
}
|
||||
|
||||
class SongDetailDialog : DialogFragment() {
|
||||
|
||||
@SuppressLint("InflateParams")
|
||||
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
||||
val context: Context = requireContext()
|
||||
val dialogView = LayoutInflater.from(context).inflate(R.layout.dialog_file_details, null)
|
||||
|
||||
val song = requireArguments().getParcelable<Song>(EXTRA_SONG)
|
||||
val fileName: TextView = dialogView.findViewById(R.id.fileName)
|
||||
val filePath: TextView = dialogView.findViewById(R.id.filePath)
|
||||
val fileSize: TextView = dialogView.findViewById(R.id.fileSize)
|
||||
val fileFormat: TextView = dialogView.findViewById(R.id.fileFormat)
|
||||
val trackLength: TextView = dialogView.findViewById(R.id.trackLength)
|
||||
val bitRate: TextView = dialogView.findViewById(R.id.bitrate)
|
||||
val samplingRate: TextView = dialogView.findViewById(R.id.samplingRate)
|
||||
|
||||
fileName.text = makeTextWithTitle(context, R.string.label_file_name, "-")
|
||||
filePath.text = makeTextWithTitle(context, R.string.label_file_path, "-")
|
||||
fileSize.text = makeTextWithTitle(context, R.string.label_file_size, "-")
|
||||
fileFormat.text = makeTextWithTitle(context, R.string.label_file_format, "-")
|
||||
trackLength.text = makeTextWithTitle(context, R.string.label_track_length, "-")
|
||||
bitRate.text = makeTextWithTitle(context, R.string.label_bit_rate, "-")
|
||||
samplingRate.text = makeTextWithTitle(context, R.string.label_sampling_rate, "-")
|
||||
if (song != null) {
|
||||
val songFile = File(song.data)
|
||||
if (songFile.exists()) {
|
||||
fileName.text = makeTextWithTitle(context, R.string.label_file_name, songFile.name)
|
||||
filePath.text =
|
||||
makeTextWithTitle(context, R.string.label_file_path, songFile.absolutePath)
|
||||
fileSize.text =
|
||||
makeTextWithTitle(
|
||||
context,
|
||||
R.string.label_file_size,
|
||||
getFileSizeString(songFile.length())
|
||||
)
|
||||
try {
|
||||
val audioFile = AudioFileIO.read(songFile)
|
||||
val audioHeader = audioFile.audioHeader
|
||||
|
||||
fileFormat.text =
|
||||
makeTextWithTitle(context, R.string.label_file_format, audioHeader.format)
|
||||
trackLength.text = makeTextWithTitle(
|
||||
context,
|
||||
R.string.label_track_length,
|
||||
MusicUtil.getReadableDurationString((audioHeader.trackLength * 1000).toLong())
|
||||
)
|
||||
bitRate.text = makeTextWithTitle(
|
||||
context,
|
||||
R.string.label_bit_rate,
|
||||
audioHeader.bitRate + " kb/s"
|
||||
)
|
||||
samplingRate.text =
|
||||
makeTextWithTitle(
|
||||
context,
|
||||
R.string.label_sampling_rate,
|
||||
audioHeader.sampleRate + " Hz"
|
||||
)
|
||||
} catch (@NonNull e: CannotReadException) {
|
||||
Log.e(TAG, "error while reading the song file", e)
|
||||
// fallback
|
||||
trackLength.text = makeTextWithTitle(
|
||||
context,
|
||||
R.string.label_track_length,
|
||||
MusicUtil.getReadableDurationString(song.duration)
|
||||
)
|
||||
} catch (@NonNull e: IOException) {
|
||||
Log.e(TAG, "error while reading the song file", e)
|
||||
trackLength.text = makeTextWithTitle(
|
||||
context,
|
||||
R.string.label_track_length,
|
||||
MusicUtil.getReadableDurationString(song.duration)
|
||||
)
|
||||
} catch (@NonNull e: TagException) {
|
||||
Log.e(TAG, "error while reading the song file", e)
|
||||
trackLength.text = makeTextWithTitle(
|
||||
context,
|
||||
R.string.label_track_length,
|
||||
MusicUtil.getReadableDurationString(song.duration)
|
||||
)
|
||||
} catch (@NonNull e: ReadOnlyFileException) {
|
||||
Log.e(TAG, "error while reading the song file", e)
|
||||
trackLength.text = makeTextWithTitle(
|
||||
context,
|
||||
R.string.label_track_length,
|
||||
MusicUtil.getReadableDurationString(song.duration)
|
||||
)
|
||||
} catch (@NonNull e: InvalidAudioFrameException) {
|
||||
Log.e(TAG, "error while reading the song file", e)
|
||||
trackLength.text = makeTextWithTitle(
|
||||
context,
|
||||
R.string.label_track_length,
|
||||
MusicUtil.getReadableDurationString(song.duration)
|
||||
)
|
||||
}
|
||||
} else {
|
||||
// fallback
|
||||
fileName.text = makeTextWithTitle(context, R.string.label_file_name, song.title)
|
||||
trackLength.text = makeTextWithTitle(
|
||||
context,
|
||||
R.string.label_track_length,
|
||||
MusicUtil.getReadableDurationString(song.duration)
|
||||
)
|
||||
}
|
||||
}
|
||||
return MaterialAlertDialogBuilder(
|
||||
requireContext(),
|
||||
R.style.ThemeOverlay_MaterialComponents_Dialog_Alert
|
||||
).setTitle(R.string.action_details)
|
||||
.setPositiveButton(android.R.string.ok, null)
|
||||
.setView(dialogView)
|
||||
.create()
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
val TAG: String = SongDetailDialog::class.java.simpleName
|
||||
|
||||
fun create(song: Song): SongDetailDialog {
|
||||
val dialog = SongDetailDialog()
|
||||
val args = Bundle()
|
||||
args.putParcelable(EXTRA_SONG, song)
|
||||
dialog.arguments = args
|
||||
return dialog
|
||||
}
|
||||
|
||||
private fun makeTextWithTitle(context: Context, titleResId: Int, text: String?): Spanned {
|
||||
return HtmlCompat.fromHtml(
|
||||
"<b>" + context.resources.getString(titleResId) + ": " + "</b>" + text,
|
||||
HtmlCompat.FROM_HTML_MODE_LEGACY
|
||||
)
|
||||
}
|
||||
|
||||
private fun getFileSizeString(sizeInBytes: Long): String {
|
||||
val fileSizeInKB = sizeInBytes / 1024
|
||||
val fileSizeInMB = fileSizeInKB / 1024
|
||||
return "$fileSizeInMB MB"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,104 +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.dialogs
|
||||
|
||||
import android.app.Dialog
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import androidx.fragment.app.DialogFragment
|
||||
import code.name.monkey.retromusic.EXTRA_SONG
|
||||
import code.name.monkey.retromusic.R
|
||||
import code.name.monkey.retromusic.activities.ShareInstagramStory
|
||||
import code.name.monkey.retromusic.model.Song
|
||||
import code.name.monkey.retromusic.util.MusicUtil
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
|
||||
class SongShareDialog : DialogFragment() {
|
||||
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
||||
val song: Song? = requireArguments().getParcelable(EXTRA_SONG)
|
||||
val listening: String =
|
||||
String.format(
|
||||
getString(R.string.currently_listening_to_x_by_x),
|
||||
song?.title,
|
||||
song?.artistName
|
||||
)
|
||||
return MaterialAlertDialogBuilder(
|
||||
requireContext(),
|
||||
R.style.ThemeOverlay_MaterialComponents_Dialog_Alert
|
||||
).setTitle(R.string.what_do_you_want_to_share)
|
||||
.setItems(
|
||||
arrayOf(
|
||||
getString(R.string.the_audio_file),
|
||||
"\u201C" + listening + "\u201D",
|
||||
getString(R.string.social_stories)
|
||||
)
|
||||
) { _, which ->
|
||||
withAction(which, song, listening)
|
||||
}
|
||||
.create()
|
||||
}
|
||||
|
||||
private fun withAction(
|
||||
which: Int,
|
||||
song: Song?,
|
||||
currentlyListening: String
|
||||
) {
|
||||
when (which) {
|
||||
0 -> {
|
||||
startActivity(Intent.createChooser(song?.let {
|
||||
MusicUtil.createShareSongFileIntent(
|
||||
it,
|
||||
requireContext()
|
||||
)
|
||||
}, null))
|
||||
}
|
||||
1 -> {
|
||||
startActivity(
|
||||
Intent.createChooser(
|
||||
Intent()
|
||||
.setAction(Intent.ACTION_SEND)
|
||||
.putExtra(Intent.EXTRA_TEXT, currentlyListening)
|
||||
.setType("text/plain"),
|
||||
null
|
||||
)
|
||||
)
|
||||
}
|
||||
2 -> {
|
||||
if (song != null) {
|
||||
startActivity(
|
||||
Intent(
|
||||
requireContext(),
|
||||
ShareInstagramStory::class.java
|
||||
).putExtra(
|
||||
ShareInstagramStory.EXTRA_SONG,
|
||||
song
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
fun create(song: Song): SongShareDialog {
|
||||
val dialog = SongShareDialog()
|
||||
val args = Bundle()
|
||||
args.putParcelable(EXTRA_SONG, song)
|
||||
dialog.arguments = args
|
||||
return dialog
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,76 +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.extensions
|
||||
|
||||
import android.app.Activity
|
||||
import androidx.annotation.IdRes
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.fragment.app.FragmentActivity
|
||||
import androidx.fragment.app.FragmentTransaction
|
||||
import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper
|
||||
import code.name.monkey.retromusic.R
|
||||
import com.google.android.material.appbar.MaterialToolbar
|
||||
|
||||
fun AppCompatActivity.applyToolbar(toolbar: MaterialToolbar) {
|
||||
toolbar.setBackgroundColor(surfaceColor())
|
||||
ToolbarContentTintHelper.colorBackButton(toolbar)
|
||||
setSupportActionBar(toolbar)
|
||||
}
|
||||
|
||||
fun FragmentActivity?.addFragment(
|
||||
@IdRes idRes: Int = R.id.container,
|
||||
fragment: Fragment,
|
||||
tag: String? = null,
|
||||
addToBackStack: Boolean = false
|
||||
) {
|
||||
val compatActivity = this as? AppCompatActivity ?: return
|
||||
compatActivity.supportFragmentManager.beginTransaction()
|
||||
.apply {
|
||||
add(fragment, tag)
|
||||
setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN)
|
||||
if (addToBackStack) {
|
||||
addToBackStack(null)
|
||||
}
|
||||
commitNow()
|
||||
}
|
||||
}
|
||||
|
||||
fun AppCompatActivity.replaceFragment(
|
||||
@IdRes id: Int = R.id.container,
|
||||
fragment: Fragment,
|
||||
tag: String? = null,
|
||||
addToBackStack: Boolean = false
|
||||
) {
|
||||
val compatActivity = this ?: return
|
||||
compatActivity.supportFragmentManager.beginTransaction()
|
||||
.apply {
|
||||
replace(id, fragment, tag)
|
||||
if (addToBackStack) {
|
||||
addToBackStack(null)
|
||||
}
|
||||
commit()
|
||||
}
|
||||
}
|
||||
|
||||
inline fun <reified T : Any> Activity.extra(key: String, default: T? = null) = lazy {
|
||||
val value = intent?.extras?.get(key)
|
||||
if (value is T) value else default
|
||||
}
|
||||
|
||||
inline fun <reified T : Any> Activity.extraNotNull(key: String, default: T? = null) = lazy {
|
||||
val value = intent?.extras?.get(key)
|
||||
requireNotNull(if (value is T) value else default) { key }
|
||||
}
|
|
@ -1,80 +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.extensions
|
||||
|
||||
import android.app.Dialog
|
||||
import android.content.Context
|
||||
import android.content.res.ColorStateList
|
||||
import android.graphics.Color
|
||||
import android.widget.CheckBox
|
||||
import android.widget.SeekBar
|
||||
import androidx.annotation.AttrRes
|
||||
import androidx.appcompat.widget.Toolbar
|
||||
import androidx.fragment.app.Fragment
|
||||
import code.name.monkey.appthemehelper.ThemeStore
|
||||
import code.name.monkey.appthemehelper.util.ATHUtil
|
||||
import code.name.monkey.appthemehelper.util.ColorUtil
|
||||
import code.name.monkey.retromusic.R
|
||||
|
||||
fun Int.ripAlpha(): Int {
|
||||
return ColorUtil.stripAlpha(this)
|
||||
}
|
||||
|
||||
fun Dialog.colorControlNormal() = resolveColor(android.R.attr.colorControlNormal)
|
||||
fun Toolbar.backgroundTintList() {
|
||||
val surfaceColor = ATHUtil.resolveColor(context, R.attr.colorSurface, Color.BLACK)
|
||||
val colorStateList = ColorStateList.valueOf(surfaceColor)
|
||||
backgroundTintList = colorStateList
|
||||
}
|
||||
|
||||
fun Context.accentColor() = ThemeStore.accentColor(this)
|
||||
|
||||
fun Fragment.accentColor() = ThemeStore.accentColor(requireContext())
|
||||
|
||||
fun Context.surfaceColor() = resolveColor(R.attr.colorSurface, Color.WHITE)
|
||||
|
||||
fun Fragment.surfaceColor() = resolveColor(R.attr.colorSurface, Color.WHITE)
|
||||
|
||||
fun Context.textColorSecondary() = resolveColor(android.R.attr.textColorSecondary)
|
||||
|
||||
fun Fragment.textColorSecondary() = resolveColor(android.R.attr.textColorSecondary)
|
||||
|
||||
fun Context.colorControlNormal() = resolveColor(android.R.attr.colorControlNormal)
|
||||
|
||||
fun Fragment.colorControlNormal() = resolveColor(android.R.attr.colorControlNormal)
|
||||
|
||||
fun Context.textColorPrimary() = resolveColor(android.R.attr.textColorPrimary)
|
||||
|
||||
fun Fragment.textColorPrimary() = resolveColor(android.R.attr.textColorPrimary)
|
||||
|
||||
fun Context.resolveColor(@AttrRes attr: Int, fallBackColor: Int = 0) =
|
||||
ATHUtil.resolveColor(this, attr, fallBackColor)
|
||||
|
||||
fun Fragment.resolveColor(@AttrRes attr: Int, fallBackColor: Int = 0) =
|
||||
ATHUtil.resolveColor(requireContext(), attr, fallBackColor)
|
||||
|
||||
fun Dialog.resolveColor(@AttrRes attr: Int, fallBackColor: Int = 0) =
|
||||
ATHUtil.resolveColor(context, attr, fallBackColor)
|
||||
|
||||
|
||||
fun CheckBox.addAccentColor() {
|
||||
buttonTintList = ColorStateList.valueOf(ThemeStore.accentColor(context))
|
||||
}
|
||||
|
||||
fun SeekBar.addAccentColor() {
|
||||
val colorState = ColorStateList.valueOf(ThemeStore.accentColor(context))
|
||||
progressTintList = colorState
|
||||
thumbTintList = colorState
|
||||
}
|
|
@ -1,62 +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.extensions
|
||||
|
||||
import android.content.Context
|
||||
import android.graphics.Bitmap
|
||||
import android.graphics.BitmapFactory
|
||||
import android.graphics.Canvas
|
||||
import android.graphics.drawable.AdaptiveIconDrawable
|
||||
import android.graphics.drawable.BitmapDrawable
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.os.Build
|
||||
import androidx.annotation.DimenRes
|
||||
import androidx.annotation.DrawableRes
|
||||
import androidx.core.content.ContextCompat
|
||||
import code.name.monkey.retromusic.R
|
||||
|
||||
fun Context.scaledDrawableResources(
|
||||
@DrawableRes id: Int,
|
||||
@DimenRes width: Int,
|
||||
@DimenRes height: Int
|
||||
): Drawable {
|
||||
val w = resources.getDimension(width).toInt()
|
||||
val h = resources.getDimension(height).toInt()
|
||||
return scaledDrawable(id, w, h)
|
||||
}
|
||||
|
||||
fun Context.scaledDrawable(@DrawableRes id: Int, width: Int, height: Int): Drawable {
|
||||
val bmp = BitmapFactory.decodeResource(resources, id)
|
||||
val bmpScaled = Bitmap.createScaledBitmap(bmp, width, height, false)
|
||||
return BitmapDrawable(resources, bmpScaled)
|
||||
}
|
||||
|
||||
fun Drawable.getBitmapDrawable(): Bitmap {
|
||||
val bmp = Bitmap.createBitmap(bounds.width(), bounds.height(), Bitmap.Config.ARGB_8888)
|
||||
val canvas = Canvas(bmp)
|
||||
draw(canvas)
|
||||
return bmp
|
||||
}
|
||||
|
||||
fun getAdaptiveIconDrawable(context: Context): Drawable {
|
||||
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
||||
AdaptiveIconDrawable(
|
||||
ContextCompat.getDrawable(context, R.drawable.ic_launcher_background),
|
||||
ContextCompat.getDrawable(context, R.drawable.ic_launcher_background)
|
||||
)
|
||||
} else {
|
||||
ContextCompat.getDrawable(context, R.drawable.color_circle_gradient)!!
|
||||
}
|
||||
}
|
|
@ -1,39 +0,0 @@
|
|||
package code.name.monkey.retromusic.extensions
|
||||
|
||||
import android.content.Context
|
||||
import android.content.res.Configuration
|
||||
import android.os.PowerManager
|
||||
import androidx.annotation.IntegerRes
|
||||
import androidx.fragment.app.Fragment
|
||||
import code.name.monkey.retromusic.util.PreferenceUtil
|
||||
|
||||
fun Fragment.getIntRes(@IntegerRes int: Int): Int {
|
||||
return resources.getInteger(int)
|
||||
}
|
||||
|
||||
fun Context.getIntRes(@IntegerRes int: Int): Int {
|
||||
return resources.getInteger(int)
|
||||
}
|
||||
|
||||
val Context.generalThemeValue
|
||||
get() = PreferenceUtil.getGeneralThemeValue(isSystemDarkModeEnabled())
|
||||
|
||||
|
||||
fun Context.isSystemDarkModeEnabled(): Boolean {
|
||||
val isBatterySaverEnabled =
|
||||
(getSystemService(Context.POWER_SERVICE) as PowerManager?)?.isPowerSaveMode ?: false
|
||||
val isDarkModeEnabled =
|
||||
(resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK) == Configuration.UI_MODE_NIGHT_YES
|
||||
return isBatterySaverEnabled or isDarkModeEnabled
|
||||
}
|
||||
|
||||
|
||||
inline fun <reified T : Any> Fragment.extra(key: String, default: T? = null) = lazy {
|
||||
val value = arguments?.get(key)
|
||||
if (value is T) value else default
|
||||
}
|
||||
|
||||
inline fun <reified T : Any> Fragment.extraNotNull(key: String, default: T? = null) = lazy {
|
||||
val value = arguments?.get(key)
|
||||
requireNotNull(if (value is T) value else default) { key }
|
||||
}
|
|
@ -1,73 +0,0 @@
|
|||
package code.name.monkey.retromusic.extensions
|
||||
|
||||
import androidx.annotation.ColorInt
|
||||
import androidx.core.graphics.ColorUtils
|
||||
import androidx.palette.graphics.Palette
|
||||
|
||||
|
||||
fun getSuitableColorFor(palette: Palette, i: Int, i2: Int): Int {
|
||||
val dominantSwatch = palette.dominantSwatch
|
||||
if (dominantSwatch != null) {
|
||||
if (hasEnoughContrast(i, dominantSwatch.rgb)) {
|
||||
return dominantSwatch.rgb
|
||||
}
|
||||
}
|
||||
val vibrantSwatch = palette.vibrantSwatch
|
||||
if (vibrantSwatch != null) {
|
||||
if (hasEnoughContrast(i, vibrantSwatch.rgb)) {
|
||||
return vibrantSwatch.rgb
|
||||
}
|
||||
}
|
||||
val darkVibrantSwatch = palette.darkVibrantSwatch
|
||||
if (darkVibrantSwatch != null) {
|
||||
if (hasEnoughContrast(i, darkVibrantSwatch.rgb)) {
|
||||
return darkVibrantSwatch.rgb
|
||||
}
|
||||
}
|
||||
val lightVibrantSwatch = palette.lightVibrantSwatch
|
||||
if (lightVibrantSwatch != null) {
|
||||
if (hasEnoughContrast(i, lightVibrantSwatch.rgb)) {
|
||||
return lightVibrantSwatch.rgb
|
||||
}
|
||||
}
|
||||
val darkMutedSwatch = palette.darkMutedSwatch
|
||||
if (darkMutedSwatch != null) {
|
||||
if (hasEnoughContrast(i, darkMutedSwatch.rgb)) {
|
||||
return darkMutedSwatch.rgb
|
||||
}
|
||||
}
|
||||
val lightMutedSwatch = palette.lightMutedSwatch
|
||||
if (lightMutedSwatch != null) {
|
||||
if (hasEnoughContrast(i, lightMutedSwatch.rgb)) {
|
||||
return lightMutedSwatch.rgb
|
||||
}
|
||||
}
|
||||
val mutedSwatch = palette.mutedSwatch
|
||||
if (mutedSwatch != null) {
|
||||
if (hasEnoughContrast(i, mutedSwatch.rgb)) {
|
||||
return mutedSwatch.rgb
|
||||
}
|
||||
}
|
||||
return i2
|
||||
}
|
||||
|
||||
fun hasEnoughContrast(i: Int, i2: Int): Boolean {
|
||||
return ColorUtils.calculateContrast(i2, i) >= 2.toDouble()
|
||||
}
|
||||
|
||||
fun hasEnoughLuminance(i: Int): Boolean {
|
||||
return ColorUtils.calculateLuminance(i) >= 0.4f.toDouble()
|
||||
}
|
||||
|
||||
fun isBlack(fArr: FloatArray): Boolean {
|
||||
return fArr[2] <= 0.035f
|
||||
}
|
||||
|
||||
fun isNearRedLine(fArr: FloatArray): Boolean {
|
||||
val f = fArr[0]
|
||||
return f >= 10.0f && f <= 37.0f && fArr[1] <= 0.82f
|
||||
}
|
||||
|
||||
fun isDark(@ColorInt i: Int): Boolean {
|
||||
return ColorUtils.calculateLuminance(i) < 0.5
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue