commit
4d873e59f0
60 changed files with 735 additions and 1064 deletions
|
@ -14,7 +14,7 @@ android {
|
||||||
vectorDrawables.useSupportLibrary = true
|
vectorDrawables.useSupportLibrary = true
|
||||||
|
|
||||||
applicationId "code.name.monkey.retromusic"
|
applicationId "code.name.monkey.retromusic"
|
||||||
versionCode 10570
|
versionCode 10573
|
||||||
versionName '5.8.0'
|
versionName '5.8.0'
|
||||||
|
|
||||||
buildConfigField("String", "GOOGLE_PLAY_LICENSING_KEY", "\"${getProperty(getProperties('../public.properties'), 'GOOGLE_PLAY_LICENSE_KEY')}\"")
|
buildConfigField("String", "GOOGLE_PLAY_LICENSING_KEY", "\"${getProperty(getProperties('../public.properties'), 'GOOGLE_PLAY_LICENSE_KEY')}\"")
|
||||||
|
@ -172,4 +172,5 @@ dependencies {
|
||||||
implementation 'cat.ereza:customactivityoncrash:2.3.0'
|
implementation 'cat.ereza:customactivityoncrash:2.3.0'
|
||||||
implementation 'me.tankery.lib:circularSeekBar:1.3.2'
|
implementation 'me.tankery.lib:circularSeekBar:1.3.2'
|
||||||
debugImplementation 'com.github.amitshekhariitbhu:Android-Debug-Database:1.0.6'
|
debugImplementation 'com.github.amitshekhariitbhu:Android-Debug-Database:1.0.6'
|
||||||
|
debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.8.1'
|
||||||
}
|
}
|
|
@ -38,8 +38,8 @@
|
||||||
<activity
|
<activity
|
||||||
android:name=".activities.MainActivity"
|
android:name=".activities.MainActivity"
|
||||||
android:exported="true"
|
android:exported="true"
|
||||||
android:theme="@style/SplashTheme"
|
android:launchMode="singleTop"
|
||||||
android:launchMode="singleTop">
|
android:theme="@style/SplashTheme">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.MAIN" />
|
<action android:name="android.intent.action.MAIN" />
|
||||||
<action android:name="android.intent.action.MUSIC_PLAYER" />
|
<action android:name="android.intent.action.MUSIC_PLAYER" />
|
||||||
|
@ -212,7 +212,7 @@
|
||||||
</provider>
|
</provider>
|
||||||
|
|
||||||
<receiver
|
<receiver
|
||||||
android:name=".service.MediaButtonIntentReceiver"
|
android:name="androidx.media.session.MediaButtonReceiver"
|
||||||
android:exported="true">
|
android:exported="true">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.MEDIA_BUTTON" />
|
<action android:name="android.intent.action.MEDIA_BUTTON" />
|
||||||
|
@ -323,6 +323,9 @@
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.media.browse.MediaBrowserService" />
|
<action android:name="android.media.browse.MediaBrowserService" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.MEDIA_BUTTON" />
|
||||||
|
</intent-filter>
|
||||||
</service>
|
</service>
|
||||||
|
|
||||||
<meta-data
|
<meta-data
|
||||||
|
|
|
@ -63,16 +63,21 @@
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
<div>
|
<div>
|
||||||
<h5>March 13, 2022</h5>
|
<h5>April 8, 2022</h5>
|
||||||
<h2>v5.8.0</h2>
|
<h2>v5.8.0</h2>
|
||||||
<h3>What's New</h3>
|
<h3>What's New</h3>
|
||||||
<ul>
|
<ul>
|
||||||
<li>Updated translations</li>
|
<li>Updated translations</li>
|
||||||
<li>Minor UI improvements</li>
|
|
||||||
</ul>
|
</ul>
|
||||||
<h3>Fixed</h3>
|
<h3>Fixed</h3>
|
||||||
<ul>
|
<ul>
|
||||||
<li>Fixed Classic Notification crash</li>
|
<li>Fixed Classic Notification crash</li>
|
||||||
|
<li>Fixed crash when clicking on Playlist in the Search Tab</li>
|
||||||
|
<li>Fixed settings change not reflecting immediately</li>
|
||||||
|
<li>Fixed shuffle</li>
|
||||||
|
<li>Fixed Song duration not visible in Card & Blur card themes</li>
|
||||||
|
<li>Fixed some Album skip styles</li>
|
||||||
|
<li>Minor bug fixes & UI improvements</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
|
|
|
@ -53,7 +53,7 @@ class ErrorActivity : AppCompatActivity() {
|
||||||
)
|
)
|
||||||
.setNeutralButton(
|
.setNeutralButton(
|
||||||
R.string.customactivityoncrash_error_activity_error_details_share
|
R.string.customactivityoncrash_error_activity_error_details_share
|
||||||
) { dialog, which ->
|
) { _, _ ->
|
||||||
|
|
||||||
val bugReport = createFile(
|
val bugReport = createFile(
|
||||||
context = this,
|
context = this,
|
||||||
|
|
|
@ -131,7 +131,6 @@ class MainActivity : AbsCastActivity(), OnSharedPreferenceChangeListener {
|
||||||
|
|
||||||
override fun onNewIntent(intent: Intent?) {
|
override fun onNewIntent(intent: Intent?) {
|
||||||
super.onNewIntent(intent)
|
super.onNewIntent(intent)
|
||||||
PreferenceUtil.registerOnSharedPreferenceChangedListener(this)
|
|
||||||
val expand = intent?.extra<Boolean>(EXPAND_PANEL)?.value ?: false
|
val expand = intent?.extra<Boolean>(EXPAND_PANEL)?.value ?: false
|
||||||
if (expand && PreferenceUtil.isExpandPanel) {
|
if (expand && PreferenceUtil.isExpandPanel) {
|
||||||
fromNotification = true
|
fromNotification = true
|
||||||
|
@ -141,6 +140,11 @@ class MainActivity : AbsCastActivity(), OnSharedPreferenceChangeListener {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onResume() {
|
||||||
|
super.onResume()
|
||||||
|
PreferenceUtil.registerOnSharedPreferenceChangedListener(this)
|
||||||
|
}
|
||||||
|
|
||||||
override fun onDestroy() {
|
override fun onDestroy() {
|
||||||
super.onDestroy()
|
super.onDestroy()
|
||||||
PreferenceUtil.unregisterOnSharedPreferenceChangedListener(this)
|
PreferenceUtil.unregisterOnSharedPreferenceChangedListener(this)
|
||||||
|
|
|
@ -9,6 +9,7 @@ import code.name.monkey.retromusic.cast.RetroWebServer
|
||||||
import code.name.monkey.retromusic.helper.MusicPlayerRemote
|
import code.name.monkey.retromusic.helper.MusicPlayerRemote
|
||||||
import com.google.android.gms.cast.framework.CastContext
|
import com.google.android.gms.cast.framework.CastContext
|
||||||
import com.google.android.gms.cast.framework.CastSession
|
import com.google.android.gms.cast.framework.CastSession
|
||||||
|
import com.google.android.gms.cast.framework.SessionManager
|
||||||
import com.google.android.gms.common.ConnectionResult
|
import com.google.android.gms.common.ConnectionResult
|
||||||
import com.google.android.gms.common.GoogleApiAvailability
|
import com.google.android.gms.common.GoogleApiAvailability
|
||||||
|
|
||||||
|
@ -16,7 +17,7 @@ import com.google.android.gms.common.GoogleApiAvailability
|
||||||
abstract class AbsCastActivity : AbsSlidingMusicPanelActivity() {
|
abstract class AbsCastActivity : AbsSlidingMusicPanelActivity() {
|
||||||
|
|
||||||
private var mCastSession: CastSession? = null
|
private var mCastSession: CastSession? = null
|
||||||
private lateinit var castContext: CastContext
|
private lateinit var sessionManager: SessionManager
|
||||||
private var webServer: RetroWebServer? = null
|
private var webServer: RetroWebServer? = null
|
||||||
private var playServicesAvailable: Boolean = false
|
private var playServicesAvailable: Boolean = false
|
||||||
|
|
||||||
|
@ -87,17 +88,17 @@ abstract class AbsCastActivity : AbsSlidingMusicPanelActivity() {
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun setupCast() {
|
private fun setupCast() {
|
||||||
castContext = CastContext.getSharedInstance(this)
|
sessionManager = CastContext.getSharedInstance(applicationContext).sessionManager
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onResume() {
|
override fun onResume() {
|
||||||
if (playServicesAvailable) {
|
if (playServicesAvailable) {
|
||||||
castContext.sessionManager.addSessionManagerListener(
|
sessionManager.addSessionManagerListener(
|
||||||
sessionManagerListener,
|
sessionManagerListener,
|
||||||
CastSession::class.java
|
CastSession::class.java
|
||||||
)
|
)
|
||||||
if (mCastSession == null) {
|
if (mCastSession == null) {
|
||||||
mCastSession = castContext.sessionManager.currentCastSession
|
mCastSession = sessionManager.currentCastSession
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
super.onResume()
|
super.onResume()
|
||||||
|
|
|
@ -64,7 +64,6 @@ import code.name.monkey.retromusic.model.CategoryInfo
|
||||||
import code.name.monkey.retromusic.util.PreferenceUtil
|
import code.name.monkey.retromusic.util.PreferenceUtil
|
||||||
import code.name.monkey.retromusic.util.ViewUtil
|
import code.name.monkey.retromusic.util.ViewUtil
|
||||||
import com.google.android.material.bottomsheet.BottomSheetBehavior.*
|
import com.google.android.material.bottomsheet.BottomSheetBehavior.*
|
||||||
import dev.chrisbanes.insetter.applyInsetter
|
|
||||||
import org.koin.androidx.viewmodel.ext.android.viewModel
|
import org.koin.androidx.viewmodel.ext.android.viewModel
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -14,11 +14,8 @@
|
||||||
*/
|
*/
|
||||||
package code.name.monkey.retromusic.activities.bugreport
|
package code.name.monkey.retromusic.activities.bugreport
|
||||||
|
|
||||||
import android.app.Activity
|
|
||||||
import android.app.Dialog
|
|
||||||
import android.content.ClipData
|
import android.content.ClipData
|
||||||
import android.content.ClipboardManager
|
import android.content.ClipboardManager
|
||||||
import android.content.Context
|
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
|
@ -28,8 +25,8 @@ import android.view.inputmethod.EditorInfo
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
import androidx.annotation.StringDef
|
import androidx.annotation.StringDef
|
||||||
import androidx.annotation.StringRes
|
import androidx.annotation.StringRes
|
||||||
import androidx.appcompat.app.AlertDialog
|
|
||||||
import androidx.core.content.getSystemService
|
import androidx.core.content.getSystemService
|
||||||
|
import androidx.lifecycle.lifecycleScope
|
||||||
import code.name.monkey.appthemehelper.ThemeStore
|
import code.name.monkey.appthemehelper.ThemeStore
|
||||||
import code.name.monkey.appthemehelper.util.MaterialUtil
|
import code.name.monkey.appthemehelper.util.MaterialUtil
|
||||||
import code.name.monkey.appthemehelper.util.TintHelper
|
import code.name.monkey.appthemehelper.util.TintHelper
|
||||||
|
@ -43,10 +40,12 @@ 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.activities.bugreport.model.github.GithubTarget
|
||||||
import code.name.monkey.retromusic.databinding.ActivityBugReportBinding
|
import code.name.monkey.retromusic.databinding.ActivityBugReportBinding
|
||||||
import code.name.monkey.retromusic.extensions.setTaskDescriptionColorAuto
|
import code.name.monkey.retromusic.extensions.setTaskDescriptionColorAuto
|
||||||
import code.name.monkey.retromusic.misc.DialogAsyncTask
|
|
||||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||||
import com.google.android.material.floatingactionbutton.FloatingActionButton
|
import com.google.android.material.floatingactionbutton.FloatingActionButton
|
||||||
import com.google.android.material.textfield.TextInputLayout
|
import com.google.android.material.textfield.TextInputLayout
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import kotlinx.coroutines.withContext
|
||||||
import org.eclipse.egit.github.core.Issue
|
import org.eclipse.egit.github.core.Issue
|
||||||
import org.eclipse.egit.github.core.client.GitHubClient
|
import org.eclipse.egit.github.core.client.GitHubClient
|
||||||
import org.eclipse.egit.github.core.client.RequestException
|
import org.eclipse.egit.github.core.client.RequestException
|
||||||
|
@ -226,9 +225,9 @@ open class BugReportActivity : AbsThemeActivity() {
|
||||||
onSaveExtraInfo()
|
onSaveExtraInfo()
|
||||||
|
|
||||||
val report = Report(bugTitle, bugDescription, deviceInfo, extraInfo)
|
val report = Report(bugTitle, bugDescription, deviceInfo, extraInfo)
|
||||||
val target = GithubTarget("RetroMusicPlayer", "RetroMusicPlayer")
|
val target = GithubTarget("prathameshmm02", "RetroMusicPlayer")
|
||||||
|
|
||||||
ReportIssueAsyncTask.report(this, report, target, login)
|
reportIssue(report, target, login)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun onSaveExtraInfo() {}
|
private fun onSaveExtraInfo() {}
|
||||||
|
@ -240,92 +239,75 @@ open class BugReportActivity : AbsThemeActivity() {
|
||||||
return super.onOptionsItemSelected(item)
|
return super.onOptionsItemSelected(item)
|
||||||
}
|
}
|
||||||
|
|
||||||
private class ReportIssueAsyncTask private constructor(
|
private fun reportIssue(
|
||||||
activity: Activity,
|
report: Report,
|
||||||
private val report: Report,
|
target: GithubTarget,
|
||||||
private val target: GithubTarget,
|
login: GithubLogin
|
||||||
private val login: GithubLogin
|
) {
|
||||||
) : DialogAsyncTask<Void, Void, String>(activity) {
|
val client: GitHubClient = if (login.shouldUseApiToken()) {
|
||||||
|
GitHubClient().setOAuth2Token(login.apiToken)
|
||||||
override fun createDialog(context: Context): Dialog {
|
} else {
|
||||||
return AlertDialog.Builder(context).show()
|
GitHubClient().setCredentials(login.username, login.password)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Result
|
val issue = Issue().setTitle(report.title).setBody(report.getDescription())
|
||||||
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)
|
lifecycleScope.launch(Dispatchers.IO) {
|
||||||
try {
|
val result = try {
|
||||||
IssueService(client).createIssue(target.username, target.repository, issue)
|
IssueService(client).createIssue(target.username, target.repository, issue)
|
||||||
return RESULT_SUCCESS
|
RESULT_SUCCESS
|
||||||
} catch (e: RequestException) {
|
} catch (e: RequestException) {
|
||||||
return when (e.status) {
|
when (e.status) {
|
||||||
STATUS_BAD_CREDENTIALS -> {
|
STATUS_BAD_CREDENTIALS -> {
|
||||||
if (login.shouldUseApiToken()) RESULT_INVALID_TOKEN else RESULT_BAD_CREDENTIALS
|
if (login.shouldUseApiToken()) RESULT_INVALID_TOKEN else RESULT_BAD_CREDENTIALS
|
||||||
}
|
}
|
||||||
STATUS_ISSUES_NOT_ENABLED -> RESULT_ISSUES_NOT_ENABLED
|
STATUS_ISSUES_NOT_ENABLED -> RESULT_ISSUES_NOT_ENABLED
|
||||||
else -> {
|
else -> {
|
||||||
e.printStackTrace()
|
|
||||||
RESULT_UNKNOWN
|
RESULT_UNKNOWN
|
||||||
|
throw e
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (e: IOException) {
|
} catch (e: IOException) {
|
||||||
e.printStackTrace()
|
e.printStackTrace()
|
||||||
return RESULT_UNKNOWN
|
RESULT_UNKNOWN
|
||||||
|
}
|
||||||
|
|
||||||
|
withContext(Dispatchers.Main) {
|
||||||
|
val activity = this@BugReportActivity
|
||||||
|
when (result) {
|
||||||
|
RESULT_SUCCESS -> MaterialAlertDialogBuilder(activity)
|
||||||
|
.setTitle(R.string.bug_report_success)
|
||||||
|
.setPositiveButton(android.R.string.ok) { _, _ -> tryToFinishActivity() }
|
||||||
|
.show()
|
||||||
|
RESULT_BAD_CREDENTIALS -> MaterialAlertDialogBuilder(activity)
|
||||||
|
.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(activity)
|
||||||
|
.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(activity)
|
||||||
|
.setTitle(R.string.bug_report_failed)
|
||||||
|
.setMessage(R.string.bug_report_failed_issues_not_available)
|
||||||
|
.setPositiveButton(android.R.string.ok, null)
|
||||||
|
.show()
|
||||||
|
else -> MaterialAlertDialogBuilder(activity)
|
||||||
|
.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() }
|
||||||
|
.show()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override fun onPostExecute(@Result result: String) {
|
private fun tryToFinishActivity() {
|
||||||
super.onPostExecute(result)
|
if (!isFinishing) {
|
||||||
|
finish()
|
||||||
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()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -333,6 +315,6 @@ open class BugReportActivity : AbsThemeActivity() {
|
||||||
|
|
||||||
private const val STATUS_BAD_CREDENTIALS = 401
|
private const val STATUS_BAD_CREDENTIALS = 401
|
||||||
private const val STATUS_ISSUES_NOT_ENABLED = 410
|
private const val STATUS_ISSUES_NOT_ENABLED = 410
|
||||||
private const val ISSUE_TRACKER_LINK = "https://github.com/RetroMusicPlayer/RetroMusicPlayer"
|
private const val ISSUE_TRACKER_LINK = "https://github.com/prathameshmm02/RetroMusicPlayer"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,202 +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 androidx.annotation.NonNull;
|
|
||||||
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.Locale;
|
|
||||||
|
|
||||||
import code.name.monkey.retromusic.util.PreferenceUtil;
|
|
||||||
|
|
||||||
public class DeviceInfo {
|
|
||||||
|
|
||||||
@SuppressLint("NewApi")
|
|
||||||
private final String[] abis =
|
|
||||||
Build.SUPPORTED_ABIS;
|
|
||||||
|
|
||||||
@SuppressLint("NewApi")
|
|
||||||
private final String[] abis32Bits =
|
|
||||||
Build.SUPPORTED_32_BIT_ABIS;
|
|
||||||
|
|
||||||
@SuppressLint("NewApi")
|
|
||||||
private final String[] abis64Bits =
|
|
||||||
Build.SUPPORTED_64_BIT_ABIS;
|
|
||||||
|
|
||||||
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><b>App version</b></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";
|
|
||||||
}
|
|
||||||
|
|
||||||
@NonNull
|
|
||||||
@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;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,110 @@
|
||||||
|
package code.name.monkey.retromusic.activities.bugreport.model
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.pm.PackageManager
|
||||||
|
import android.os.Build
|
||||||
|
import androidx.annotation.IntRange
|
||||||
|
import code.name.monkey.retromusic.util.PreferenceUtil
|
||||||
|
import code.name.monkey.retromusic.util.PreferenceUtil.isAdaptiveColor
|
||||||
|
import code.name.monkey.retromusic.util.PreferenceUtil.languageCode
|
||||||
|
import code.name.monkey.retromusic.util.PreferenceUtil.nowPlayingScreen
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
class DeviceInfo(context: Context) {
|
||||||
|
@SuppressLint("NewApi")
|
||||||
|
private val abis = Build.SUPPORTED_ABIS
|
||||||
|
|
||||||
|
@SuppressLint("NewApi")
|
||||||
|
private val abis32Bits = Build.SUPPORTED_32_BIT_ABIS
|
||||||
|
|
||||||
|
@SuppressLint("NewApi")
|
||||||
|
private val abis64Bits = Build.SUPPORTED_64_BIT_ABIS
|
||||||
|
private val baseTheme: String
|
||||||
|
private val brand = Build.BRAND
|
||||||
|
private val buildID = Build.DISPLAY
|
||||||
|
private val buildVersion = Build.VERSION.INCREMENTAL
|
||||||
|
private val device = Build.DEVICE
|
||||||
|
private val hardware = Build.HARDWARE
|
||||||
|
private val isAdaptive: Boolean
|
||||||
|
private val manufacturer = Build.MANUFACTURER
|
||||||
|
private val model = Build.MODEL
|
||||||
|
private val nowPlayingTheme: String
|
||||||
|
private val product = Build.PRODUCT
|
||||||
|
private val releaseVersion = Build.VERSION.RELEASE
|
||||||
|
|
||||||
|
@IntRange(from = 0)
|
||||||
|
private val sdkVersion = Build.VERSION.SDK_INT
|
||||||
|
private var versionCode = 0
|
||||||
|
private var versionName: String? = null
|
||||||
|
private val selectedLang: String
|
||||||
|
fun toMarkdown(): String {
|
||||||
|
return """
|
||||||
|
Device info:
|
||||||
|
---
|
||||||
|
<table>
|
||||||
|
<tr><td><b>App version</b></td><td>$versionName</td></tr>
|
||||||
|
<tr><td>App version code</td><td>$versionCode</td></tr>
|
||||||
|
<tr><td>Android build version</td><td>$buildVersion</td></tr>
|
||||||
|
<tr><td>Android release version</td><td>$releaseVersion</td></tr>
|
||||||
|
<tr><td>Android SDK version</td><td>$sdkVersion</td></tr>
|
||||||
|
<tr><td>Android build ID</td><td>$buildID</td></tr>
|
||||||
|
<tr><td>Device brand</td><td>$brand</td></tr>
|
||||||
|
<tr><td>Device manufacturer</td><td>$manufacturer</td></tr>
|
||||||
|
<tr><td>Device name</td><td>$device</td></tr>
|
||||||
|
<tr><td>Device model</td><td>$model</td></tr>
|
||||||
|
<tr><td>Device product name</td><td>$product</td></tr>
|
||||||
|
<tr><td>Device hardware name</td><td>$hardware</td></tr>
|
||||||
|
<tr><td>ABIs</td><td>${Arrays.toString(abis)}</td></tr>
|
||||||
|
<tr><td>ABIs (32bit)</td><td>${Arrays.toString(abis32Bits)}</td></tr>
|
||||||
|
<tr><td>ABIs (64bit)</td><td>${Arrays.toString(abis64Bits)}</td></tr>
|
||||||
|
<tr><td>Language</td><td>$selectedLang</td></tr>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
""".trimIndent()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun toString(): String {
|
||||||
|
return """
|
||||||
|
App version: $versionName
|
||||||
|
App version code: $versionCode
|
||||||
|
Android build version: $buildVersion
|
||||||
|
Android release version: $releaseVersion
|
||||||
|
Android SDK version: $sdkVersion
|
||||||
|
Android build ID: $buildID
|
||||||
|
Device brand: $brand
|
||||||
|
Device manufacturer: $manufacturer
|
||||||
|
Device name: $device
|
||||||
|
Device model: $model
|
||||||
|
Device product name: $product
|
||||||
|
Device hardware name: $hardware
|
||||||
|
ABIs: ${Arrays.toString(abis)}
|
||||||
|
ABIs (32bit): ${Arrays.toString(abis32Bits)}
|
||||||
|
ABIs (64bit): ${Arrays.toString(abis64Bits)}
|
||||||
|
Base theme: $baseTheme
|
||||||
|
Now playing theme: $nowPlayingTheme
|
||||||
|
Adaptive: $isAdaptive
|
||||||
|
System language: ${Locale.getDefault().toLanguageTag()}
|
||||||
|
In-App Language: $selectedLang
|
||||||
|
""".trimIndent()
|
||||||
|
}
|
||||||
|
|
||||||
|
init {
|
||||||
|
val packageInfo = try {
|
||||||
|
context.packageManager.getPackageInfo(context.packageName, 0)
|
||||||
|
} catch (e: PackageManager.NameNotFoundException) {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
if (packageInfo != null) {
|
||||||
|
versionCode = packageInfo.versionCode
|
||||||
|
versionName = packageInfo.versionName
|
||||||
|
} else {
|
||||||
|
versionCode = -1
|
||||||
|
versionName = null
|
||||||
|
}
|
||||||
|
baseTheme = PreferenceUtil.baseTheme
|
||||||
|
nowPlayingTheme = context.getString(nowPlayingScreen.titleRes)
|
||||||
|
isAdaptive = isAdaptiveColor
|
||||||
|
selectedLang = languageCode
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,34 +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;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,22 @@
|
||||||
|
package code.name.monkey.retromusic.activities.bugreport.model
|
||||||
|
|
||||||
|
import code.name.monkey.retromusic.activities.bugreport.model.github.ExtraInfo
|
||||||
|
|
||||||
|
class Report(
|
||||||
|
val title: String,
|
||||||
|
private val description: String,
|
||||||
|
private val deviceInfo: DeviceInfo?,
|
||||||
|
private val extraInfo: ExtraInfo
|
||||||
|
) {
|
||||||
|
fun getDescription(): String {
|
||||||
|
return """
|
||||||
|
$description
|
||||||
|
|
||||||
|
-
|
||||||
|
|
||||||
|
${deviceInfo?.toMarkdown()}
|
||||||
|
|
||||||
|
${extraInfo.toMarkdown()}
|
||||||
|
""".trimIndent()
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,61 +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();
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,61 @@
|
||||||
|
package code.name.monkey.retromusic.activities.bugreport.model.github
|
||||||
|
|
||||||
|
class ExtraInfo {
|
||||||
|
private val extraInfo: MutableMap<String, String> = LinkedHashMap()
|
||||||
|
fun put(key: String, value: String) {
|
||||||
|
extraInfo[key] = value
|
||||||
|
}
|
||||||
|
|
||||||
|
fun put(key: String, value: Boolean) {
|
||||||
|
extraInfo[key] = value.toString()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun put(key: String, value: Double) {
|
||||||
|
extraInfo[key] = value.toString()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun put(key: String, value: Float) {
|
||||||
|
extraInfo[key] = value.toString()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun put(key: String, value: Long) {
|
||||||
|
extraInfo[key] = value.toString()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun put(key: String, value: Int) {
|
||||||
|
extraInfo[key] = value.toString()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun put(key: String, value: Any) {
|
||||||
|
extraInfo[key] = value.toString()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun remove(key: String) {
|
||||||
|
extraInfo.remove(key)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun toMarkdown(): String {
|
||||||
|
if (extraInfo.isEmpty()) {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
val output = StringBuilder()
|
||||||
|
output.append(
|
||||||
|
"""
|
||||||
|
Extra info:
|
||||||
|
---
|
||||||
|
<table>
|
||||||
|
|
||||||
|
""".trimIndent()
|
||||||
|
)
|
||||||
|
for (key in extraInfo.keys) {
|
||||||
|
output
|
||||||
|
.append("<tr><td>")
|
||||||
|
.append(key)
|
||||||
|
.append("</td><td>")
|
||||||
|
.append(extraInfo[key])
|
||||||
|
.append("</td></tr>\n")
|
||||||
|
}
|
||||||
|
output.append("</table>\n")
|
||||||
|
return output.toString()
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,40 +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);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,25 @@
|
||||||
|
package code.name.monkey.retromusic.activities.bugreport.model.github
|
||||||
|
|
||||||
|
import android.text.TextUtils
|
||||||
|
|
||||||
|
class GithubLogin {
|
||||||
|
val apiToken: String?
|
||||||
|
val password: String?
|
||||||
|
val username: String?
|
||||||
|
|
||||||
|
constructor(username: String?, password: String?) {
|
||||||
|
this.username = username
|
||||||
|
this.password = password
|
||||||
|
apiToken = null
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(apiToken: String?) {
|
||||||
|
username = null
|
||||||
|
password = null
|
||||||
|
this.apiToken = apiToken
|
||||||
|
}
|
||||||
|
|
||||||
|
fun shouldUseApiToken(): Boolean {
|
||||||
|
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;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
package code.name.monkey.retromusic.activities.bugreport.model.github
|
||||||
|
|
||||||
|
class GithubTarget(val username: String, val repository: String)
|
|
@ -51,7 +51,7 @@ class TagWriter {
|
||||||
|
|
||||||
suspend fun writeTagsToFiles(context: Context, info: AudioTagInfo) {
|
suspend fun writeTagsToFiles(context: Context, info: AudioTagInfo) {
|
||||||
withContext(Dispatchers.IO) {
|
withContext(Dispatchers.IO) {
|
||||||
kotlin.runCatching {
|
runCatching {
|
||||||
var artwork: Artwork? = null
|
var artwork: Artwork? = null
|
||||||
var albumArtFile: File? = null
|
var albumArtFile: File? = null
|
||||||
if (info.artworkInfo?.artwork != null) {
|
if (info.artworkInfo?.artwork != null) {
|
||||||
|
@ -124,7 +124,7 @@ class TagWriter {
|
||||||
suspend fun writeTagsToFilesR(context: Context, info: AudioTagInfo): List<File> =
|
suspend fun writeTagsToFilesR(context: Context, info: AudioTagInfo): List<File> =
|
||||||
withContext(Dispatchers.IO) {
|
withContext(Dispatchers.IO) {
|
||||||
val cacheFiles = mutableListOf<File>()
|
val cacheFiles = mutableListOf<File>()
|
||||||
kotlin.runCatching {
|
runCatching {
|
||||||
var artwork: Artwork? = null
|
var artwork: Artwork? = null
|
||||||
var albumArtFile: File? = null
|
var albumArtFile: File? = null
|
||||||
if (info.artworkInfo?.artwork != null) {
|
if (info.artworkInfo?.artwork != null) {
|
||||||
|
|
|
@ -64,4 +64,7 @@ interface PlaylistDao {
|
||||||
|
|
||||||
@Query("SELECT * FROM SongEntity WHERE playlist_creator_id= :playlistId")
|
@Query("SELECT * FROM SongEntity WHERE playlist_creator_id= :playlistId")
|
||||||
fun favoritesSongs(playlistId: Long): List<SongEntity>
|
fun favoritesSongs(playlistId: Long): List<SongEntity>
|
||||||
|
|
||||||
|
@Query("SELECT EXISTS(SELECT * FROM PlaylistEntity WHERE playlist_id = :playlistId)")
|
||||||
|
fun checkPlaylistExists(playlistId: Long): LiveData<Boolean>
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,15 +15,9 @@
|
||||||
package code.name.monkey.retromusic.fragments.base
|
package code.name.monkey.retromusic.fragments.base
|
||||||
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.view.View
|
|
||||||
import androidx.annotation.LayoutRes
|
import androidx.annotation.LayoutRes
|
||||||
import code.name.monkey.appthemehelper.util.ColorUtil
|
|
||||||
import code.name.monkey.appthemehelper.util.VersionUtils
|
|
||||||
import code.name.monkey.retromusic.R
|
|
||||||
import code.name.monkey.retromusic.activities.MainActivity
|
import code.name.monkey.retromusic.activities.MainActivity
|
||||||
import code.name.monkey.retromusic.extensions.setLightStatusBarAuto
|
|
||||||
import code.name.monkey.retromusic.extensions.setTaskDescriptionColorAuto
|
import code.name.monkey.retromusic.extensions.setTaskDescriptionColorAuto
|
||||||
import code.name.monkey.retromusic.extensions.surfaceColor
|
|
||||||
import code.name.monkey.retromusic.fragments.LibraryViewModel
|
import code.name.monkey.retromusic.fragments.LibraryViewModel
|
||||||
import org.koin.androidx.viewmodel.ext.android.sharedViewModel
|
import org.koin.androidx.viewmodel.ext.android.sharedViewModel
|
||||||
|
|
||||||
|
@ -38,26 +32,4 @@ abstract class AbsMainActivityFragment(@LayoutRes layout: Int) : AbsMusicService
|
||||||
setHasOptionsMenu(true)
|
setHasOptionsMenu(true)
|
||||||
mainActivity.setTaskDescriptionColorAuto()
|
mainActivity.setTaskDescriptionColorAuto()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun setStatusBarColor(view: View, color: Int) {
|
|
||||||
val statusBar = view.findViewById<View>(R.id.status_bar)
|
|
||||||
if (statusBar != null) {
|
|
||||||
if (VersionUtils.hasMarshmallow()) {
|
|
||||||
statusBar.setBackgroundColor(color)
|
|
||||||
mainActivity.setLightStatusBarAuto(color)
|
|
||||||
} else {
|
|
||||||
statusBar.setBackgroundColor(color)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun setStatusBarColorAuto(view: View) {
|
|
||||||
val colorPrimary = surfaceColor()
|
|
||||||
// we don't want to use statusbar color because we are doing the color darkening on our own to support KitKat
|
|
||||||
if (VersionUtils.hasMarshmallow()) {
|
|
||||||
setStatusBarColor(view, colorPrimary)
|
|
||||||
} else {
|
|
||||||
setStatusBarColor(view, ColorUtil.darkenColor(colorPrimary))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -220,9 +220,9 @@ abstract class AbsPlayerControlsFragment(@LayoutRes layout: Int) : AbsMusicServi
|
||||||
childFragmentManager.beginTransaction()
|
childFragmentManager.beginTransaction()
|
||||||
.replace(R.id.volumeFragmentContainer, VolumeFragment()).commit()
|
.replace(R.id.volumeFragmentContainer, VolumeFragment()).commit()
|
||||||
childFragmentManager.executePendingTransactions()
|
childFragmentManager.executePendingTransactions()
|
||||||
volumeFragment =
|
|
||||||
childFragmentManager.findFragmentById(R.id.volumeFragmentContainer) as? VolumeFragment
|
|
||||||
}
|
}
|
||||||
|
volumeFragment =
|
||||||
|
childFragmentManager.findFragmentById(R.id.volumeFragmentContainer) as? VolumeFragment
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onResume() {
|
override fun onResume() {
|
||||||
|
|
|
@ -137,7 +137,6 @@ class VolumeFragment : Fragment(), SeekBar.OnSeekBarChangeListener, OnAudioVolum
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
|
||||||
fun newInstance(): VolumeFragment {
|
fun newInstance(): VolumeFragment {
|
||||||
return VolumeFragment()
|
return VolumeFragment()
|
||||||
}
|
}
|
||||||
|
|
|
@ -77,7 +77,10 @@ class PlayerAlbumCoverFragment : AbsMusicServiceFragment(R.layout.fragment_playe
|
||||||
fun removeSlideEffect() {
|
fun removeSlideEffect() {
|
||||||
val transformer = ParallaxPagerTransformer(R.id.player_image)
|
val transformer = ParallaxPagerTransformer(R.id.player_image)
|
||||||
transformer.setSpeed(0.3f)
|
transformer.setSpeed(0.3f)
|
||||||
}
|
lifecycleScope.launchWhenStarted {
|
||||||
|
viewPager.setPageTransformer(false, transformer)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun updateLyrics() {
|
private fun updateLyrics() {
|
||||||
binding.lyricsView.setLabel(context?.getString(R.string.no_lyrics_found))
|
binding.lyricsView.setLabel(context?.getString(R.string.no_lyrics_found))
|
||||||
|
|
|
@ -19,6 +19,7 @@ import android.os.Bundle
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.widget.ImageButton
|
import android.widget.ImageButton
|
||||||
import android.widget.SeekBar
|
import android.widget.SeekBar
|
||||||
|
import android.widget.TextView
|
||||||
import code.name.monkey.appthemehelper.ThemeStore
|
import code.name.monkey.appthemehelper.ThemeStore
|
||||||
import code.name.monkey.appthemehelper.util.ATHUtil
|
import code.name.monkey.appthemehelper.util.ATHUtil
|
||||||
import code.name.monkey.appthemehelper.util.ColorUtil
|
import code.name.monkey.appthemehelper.util.ColorUtil
|
||||||
|
@ -59,6 +60,12 @@ class CardPlaybackControlsFragment :
|
||||||
override val previousButton: ImageButton
|
override val previousButton: ImageButton
|
||||||
get() = binding.mediaButton.previousButton
|
get() = binding.mediaButton.previousButton
|
||||||
|
|
||||||
|
override val songTotalTime: TextView
|
||||||
|
get() = binding.songTotalTime
|
||||||
|
|
||||||
|
override val songCurrentProgress: TextView
|
||||||
|
get() = binding.songCurrentProgress
|
||||||
|
|
||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
super.onViewCreated(view, savedInstanceState)
|
super.onViewCreated(view, savedInstanceState)
|
||||||
_binding = FragmentCardPlayerPlaybackControlsBinding.bind(view)
|
_binding = FragmentCardPlayerPlaybackControlsBinding.bind(view)
|
||||||
|
|
|
@ -20,6 +20,7 @@ import android.view.View
|
||||||
import android.view.animation.DecelerateInterpolator
|
import android.view.animation.DecelerateInterpolator
|
||||||
import android.widget.ImageButton
|
import android.widget.ImageButton
|
||||||
import android.widget.SeekBar
|
import android.widget.SeekBar
|
||||||
|
import android.widget.TextView
|
||||||
import code.name.monkey.appthemehelper.util.ColorUtil
|
import code.name.monkey.appthemehelper.util.ColorUtil
|
||||||
import code.name.monkey.appthemehelper.util.TintHelper
|
import code.name.monkey.appthemehelper.util.TintHelper
|
||||||
import code.name.monkey.retromusic.R
|
import code.name.monkey.retromusic.R
|
||||||
|
@ -55,6 +56,12 @@ class CardBlurPlaybackControlsFragment :
|
||||||
override val previousButton: ImageButton
|
override val previousButton: ImageButton
|
||||||
get() = binding.mediaButton.previousButton
|
get() = binding.mediaButton.previousButton
|
||||||
|
|
||||||
|
override val songTotalTime: TextView
|
||||||
|
get() = binding.songTotalTime
|
||||||
|
|
||||||
|
override val songCurrentProgress: TextView
|
||||||
|
get() = binding.songCurrentProgress
|
||||||
|
|
||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
super.onViewCreated(view, savedInstanceState)
|
super.onViewCreated(view, savedInstanceState)
|
||||||
_binding = FragmentCardBlurPlayerPlaybackControlsBinding.bind(view)
|
_binding = FragmentCardBlurPlayerPlaybackControlsBinding.bind(view)
|
||||||
|
|
|
@ -39,7 +39,6 @@ class ColorFragment : AbsPlayerFragment(R.layout.fragment_color_player) {
|
||||||
private var _binding: FragmentColorPlayerBinding? = null
|
private var _binding: FragmentColorPlayerBinding? = null
|
||||||
private val binding get() = _binding!!
|
private val binding get() = _binding!!
|
||||||
|
|
||||||
|
|
||||||
override fun playerToolbar(): Toolbar {
|
override fun playerToolbar(): Toolbar {
|
||||||
return binding.playerToolbar
|
return binding.playerToolbar
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,6 +9,7 @@ import androidx.activity.addCallback
|
||||||
import androidx.core.view.ViewCompat
|
import androidx.core.view.ViewCompat
|
||||||
import androidx.core.view.doOnPreDraw
|
import androidx.core.view.doOnPreDraw
|
||||||
import androidx.core.view.isVisible
|
import androidx.core.view.isVisible
|
||||||
|
import androidx.navigation.fragment.findNavController
|
||||||
import androidx.navigation.fragment.navArgs
|
import androidx.navigation.fragment.navArgs
|
||||||
import androidx.recyclerview.widget.LinearLayoutManager
|
import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
|
@ -58,7 +59,6 @@ class PlaylistDetailsFragment : AbsMainActivityFragment(R.layout.fragment_playli
|
||||||
enterTransition = MaterialSharedAxis(MaterialSharedAxis.Z, true).addTarget(view)
|
enterTransition = MaterialSharedAxis(MaterialSharedAxis.Z, true).addTarget(view)
|
||||||
returnTransition = MaterialSharedAxis(MaterialSharedAxis.Z, false)
|
returnTransition = MaterialSharedAxis(MaterialSharedAxis.Z, false)
|
||||||
setHasOptionsMenu(true)
|
setHasOptionsMenu(true)
|
||||||
mainActivity.addMusicServiceEventListener(viewModel)
|
|
||||||
mainActivity.setSupportActionBar(binding.toolbar)
|
mainActivity.setSupportActionBar(binding.toolbar)
|
||||||
ViewCompat.setTransitionName(binding.container, "playlist")
|
ViewCompat.setTransitionName(binding.container, "playlist")
|
||||||
playlist = arguments.extraPlaylist
|
playlist = arguments.extraPlaylist
|
||||||
|
@ -67,6 +67,11 @@ class PlaylistDetailsFragment : AbsMainActivityFragment(R.layout.fragment_playli
|
||||||
viewModel.getSongs().observe(viewLifecycleOwner) {
|
viewModel.getSongs().observe(viewLifecycleOwner) {
|
||||||
songs(it.toSongs())
|
songs(it.toSongs())
|
||||||
}
|
}
|
||||||
|
viewModel.playlistExists().observe(viewLifecycleOwner) {
|
||||||
|
if (!it) {
|
||||||
|
findNavController().navigateUp()
|
||||||
|
}
|
||||||
|
}
|
||||||
postponeEnterTransition()
|
postponeEnterTransition()
|
||||||
requireView().doOnPreDraw { startPostponedEnterTransition() }
|
requireView().doOnPreDraw { startPostponedEnterTransition() }
|
||||||
requireActivity().onBackPressedDispatcher.addCallback(viewLifecycleOwner) {
|
requireActivity().onBackPressedDispatcher.addCallback(viewLifecycleOwner) {
|
||||||
|
|
|
@ -22,24 +22,15 @@ import code.name.monkey.retromusic.db.SongEntity
|
||||||
import code.name.monkey.retromusic.interfaces.IMusicServiceEventListener
|
import code.name.monkey.retromusic.interfaces.IMusicServiceEventListener
|
||||||
import code.name.monkey.retromusic.model.Song
|
import code.name.monkey.retromusic.model.Song
|
||||||
import code.name.monkey.retromusic.repository.RealRepository
|
import code.name.monkey.retromusic.repository.RealRepository
|
||||||
|
import code.name.monkey.retromusic.repository.RealRoomRepository
|
||||||
|
|
||||||
class PlaylistDetailsViewModel(
|
class PlaylistDetailsViewModel(
|
||||||
private val realRepository: RealRepository,
|
private val realRepository: RealRepository,
|
||||||
private var playlist: PlaylistWithSongs
|
private var playlist: PlaylistWithSongs
|
||||||
) : ViewModel(), IMusicServiceEventListener {
|
) : ViewModel() {
|
||||||
|
|
||||||
private val playListSongs = MutableLiveData<List<Song>>()
|
|
||||||
|
|
||||||
fun getSongs(): LiveData<List<SongEntity>> =
|
fun getSongs(): LiveData<List<SongEntity>> =
|
||||||
realRepository.playlistSongs(playlist.playlistEntity.playListId)
|
realRepository.playlistSongs(playlist.playlistEntity.playListId)
|
||||||
|
|
||||||
override fun onMediaStoreChanged() {}
|
fun playlistExists(): LiveData<Boolean> =
|
||||||
override fun onServiceConnected() {}
|
realRepository.checkPlaylistExists(playlist.playlistEntity.playListId)
|
||||||
override fun onServiceDisconnected() {}
|
|
||||||
override fun onQueueChanged() {}
|
|
||||||
override fun onPlayingMetaChanged() {}
|
|
||||||
override fun onPlayStateChanged() {}
|
|
||||||
override fun onRepeatModeChanged() {}
|
|
||||||
override fun onShuffleModeChanged() {}
|
|
||||||
override fun onFavoriteStateChanged() {}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,6 +14,7 @@
|
||||||
*/
|
*/
|
||||||
package code.name.monkey.retromusic.fragments.search
|
package code.name.monkey.retromusic.fragments.search
|
||||||
|
|
||||||
|
import android.app.Activity.RESULT_OK
|
||||||
import android.content.ActivityNotFoundException
|
import android.content.ActivityNotFoundException
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.content.res.ColorStateList
|
import android.content.res.ColorStateList
|
||||||
|
@ -221,6 +222,16 @@ class SearchFragment : AbsMainActivityFragment(R.layout.fragment_search), TextWa
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
||||||
|
super.onActivityResult(requestCode, resultCode, data)
|
||||||
|
if (resultCode == RESULT_OK) {
|
||||||
|
val spokenText: String? =
|
||||||
|
data?.getStringArrayListExtra(RecognizerIntent.EXTRA_RESULTS)
|
||||||
|
.let { text -> text?.get(0) }
|
||||||
|
binding.searchView.setText(spokenText)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override fun onDestroyView() {
|
override fun onDestroyView() {
|
||||||
hideKeyboard(view)
|
hideKeyboard(view)
|
||||||
super.onDestroyView()
|
super.onDestroyView()
|
||||||
|
|
|
@ -25,7 +25,6 @@ import code.name.monkey.retromusic.extensions.surfaceColor
|
||||||
import code.name.monkey.retromusic.fragments.GridStyle
|
import code.name.monkey.retromusic.fragments.GridStyle
|
||||||
import code.name.monkey.retromusic.fragments.ReloadType
|
import code.name.monkey.retromusic.fragments.ReloadType
|
||||||
import code.name.monkey.retromusic.fragments.base.AbsRecyclerViewCustomGridSizeFragment
|
import code.name.monkey.retromusic.fragments.base.AbsRecyclerViewCustomGridSizeFragment
|
||||||
import code.name.monkey.retromusic.helper.MusicPlayerRemote
|
|
||||||
import code.name.monkey.retromusic.helper.SortOrder.SongSortOrder
|
import code.name.monkey.retromusic.helper.SortOrder.SongSortOrder
|
||||||
import code.name.monkey.retromusic.interfaces.ICabCallback
|
import code.name.monkey.retromusic.interfaces.ICabCallback
|
||||||
import code.name.monkey.retromusic.interfaces.ICabHolder
|
import code.name.monkey.retromusic.interfaces.ICabHolder
|
||||||
|
@ -66,7 +65,7 @@ class SongsFragment : AbsRecyclerViewCustomGridSizeFragment<SongAdapter, GridLay
|
||||||
get() = true
|
get() = true
|
||||||
|
|
||||||
override fun onShuffleClicked() {
|
override fun onShuffleClicked() {
|
||||||
libraryViewModel.getSongs().value?.let { MusicPlayerRemote.openAndShuffleQueue(it, true) }
|
libraryViewModel.shuffleSongs()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun createLayoutManager(): GridLayoutManager {
|
override fun createLayoutManager(): GridLayoutManager {
|
||||||
|
|
|
@ -302,7 +302,7 @@ object MusicPlayerRemote : KoinComponent {
|
||||||
|
|
||||||
fun setShuffleMode(shuffleMode: Int): Boolean {
|
fun setShuffleMode(shuffleMode: Int): Boolean {
|
||||||
if (musicService != null) {
|
if (musicService != null) {
|
||||||
musicService!!.shuffleMode = shuffleMode
|
musicService!!.setShuffleMode(shuffleMode)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
|
|
|
@ -111,6 +111,7 @@ interface Repository {
|
||||||
suspend fun searchAlbums(query: String): List<Album>
|
suspend fun searchAlbums(query: String): List<Album>
|
||||||
suspend fun isSongFavorite(songId: Long): Boolean
|
suspend fun isSongFavorite(songId: Long): Boolean
|
||||||
fun getSongByGenre(genreId: Long): Song
|
fun getSongByGenre(genreId: Long): Song
|
||||||
|
fun checkPlaylistExists(playListId: Long): LiveData<Boolean>
|
||||||
}
|
}
|
||||||
|
|
||||||
class RealRepository(
|
class RealRepository(
|
||||||
|
@ -277,6 +278,9 @@ class RealRepository(
|
||||||
override suspend fun checkPlaylistExists(playlistName: String): List<PlaylistEntity> =
|
override suspend fun checkPlaylistExists(playlistName: String): List<PlaylistEntity> =
|
||||||
roomRepository.checkPlaylistExists(playlistName)
|
roomRepository.checkPlaylistExists(playlistName)
|
||||||
|
|
||||||
|
override fun checkPlaylistExists(playListId: Long): LiveData<Boolean> =
|
||||||
|
roomRepository.checkPlaylistExists(playListId)
|
||||||
|
|
||||||
override suspend fun createPlaylist(playlistEntity: PlaylistEntity): Long =
|
override suspend fun createPlaylist(playlistEntity: PlaylistEntity): Long =
|
||||||
roomRepository.createPlaylist(playlistEntity)
|
roomRepository.createPlaylist(playlistEntity)
|
||||||
|
|
||||||
|
@ -377,7 +381,7 @@ class RealRepository(
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun suggestions(): List<Song> {
|
override suspend fun suggestions(): List<Song> {
|
||||||
if (!PreferenceUtil.homeSuggestions) return listOf<Song>()
|
if (!PreferenceUtil.homeSuggestions) return listOf()
|
||||||
return NotPlayedPlaylist().songs().shuffled().takeIf {
|
return NotPlayedPlaylist().songs().shuffled().takeIf {
|
||||||
it.size > 9
|
it.size > 9
|
||||||
} ?: emptyList()
|
} ?: emptyList()
|
||||||
|
|
|
@ -49,6 +49,7 @@ interface RoomRepository {
|
||||||
suspend fun blackListPaths(): List<BlackListStoreEntity>
|
suspend fun blackListPaths(): List<BlackListStoreEntity>
|
||||||
suspend fun deleteSongs(songs: List<Song>)
|
suspend fun deleteSongs(songs: List<Song>)
|
||||||
suspend fun isSongFavorite(context: Context, songId: Long): Boolean
|
suspend fun isSongFavorite(context: Context, songId: Long): Boolean
|
||||||
|
fun checkPlaylistExists(playListId: Long): LiveData<Boolean>
|
||||||
}
|
}
|
||||||
|
|
||||||
class RealRoomRepository(
|
class RealRoomRepository(
|
||||||
|
@ -97,6 +98,9 @@ class RealRoomRepository(
|
||||||
override fun getSongs(playListId: Long): LiveData<List<SongEntity>> =
|
override fun getSongs(playListId: Long): LiveData<List<SongEntity>> =
|
||||||
playlistDao.songsFromPlaylist(playListId)
|
playlistDao.songsFromPlaylist(playListId)
|
||||||
|
|
||||||
|
override fun checkPlaylistExists(playListId: Long): LiveData<Boolean> =
|
||||||
|
playlistDao.checkPlaylistExists(playListId)
|
||||||
|
|
||||||
override suspend fun deletePlaylistEntities(playlistEntities: List<PlaylistEntity>) =
|
override suspend fun deletePlaylistEntities(playlistEntities: List<PlaylistEntity>) =
|
||||||
playlistDao.deletePlaylists(playlistEntities)
|
playlistDao.deletePlaylists(playlistEntities)
|
||||||
|
|
||||||
|
|
|
@ -43,6 +43,8 @@ interface SongRepository {
|
||||||
|
|
||||||
fun songs(cursor: Cursor?): List<Song>
|
fun songs(cursor: Cursor?): List<Song>
|
||||||
|
|
||||||
|
fun sortedSongs(cursor: Cursor?): List<Song>
|
||||||
|
|
||||||
fun songs(query: String): List<Song>
|
fun songs(query: String): List<Song>
|
||||||
|
|
||||||
fun songsByFilePath(filePath: String, ignoreBlacklist: Boolean = false): List<Song>
|
fun songsByFilePath(filePath: String, ignoreBlacklist: Boolean = false): List<Song>
|
||||||
|
@ -57,7 +59,7 @@ interface SongRepository {
|
||||||
class RealSongRepository(private val context: Context) : SongRepository {
|
class RealSongRepository(private val context: Context) : SongRepository {
|
||||||
|
|
||||||
override fun songs(): List<Song> {
|
override fun songs(): List<Song> {
|
||||||
return songs(makeSongCursor(null, null))
|
return sortedSongs(makeSongCursor(null, null))
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun songs(cursor: Cursor?): List<Song> {
|
override fun songs(cursor: Cursor?): List<Song> {
|
||||||
|
@ -68,7 +70,12 @@ class RealSongRepository(private val context: Context) : SongRepository {
|
||||||
} while (cursor.moveToNext())
|
} while (cursor.moveToNext())
|
||||||
}
|
}
|
||||||
cursor?.close()
|
cursor?.close()
|
||||||
|
return songs
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun sortedSongs(cursor: Cursor?): List<Song> {
|
||||||
val collator = Collator.getInstance()
|
val collator = Collator.getInstance()
|
||||||
|
val songs = songs(cursor)
|
||||||
return when (PreferenceUtil.songSortOrder) {
|
return when (PreferenceUtil.songSortOrder) {
|
||||||
SortOrder.SongSortOrder.SONG_A_Z -> {
|
SortOrder.SongSortOrder.SONG_A_Z -> {
|
||||||
songs.sortedWith{ s1, s2 -> collator.compare(s1.title, s2.title) }
|
songs.sortedWith{ s1, s2 -> collator.compare(s1.title, s2.title) }
|
||||||
|
|
|
@ -9,6 +9,7 @@ import android.media.MediaPlayer
|
||||||
import android.media.audiofx.AudioEffect
|
import android.media.audiofx.AudioEffect
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import android.os.PowerManager
|
import android.os.PowerManager
|
||||||
|
import android.util.Log
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
import code.name.monkey.retromusic.R
|
import code.name.monkey.retromusic.R
|
||||||
import code.name.monkey.retromusic.helper.MusicPlayerRemote
|
import code.name.monkey.retromusic.helper.MusicPlayerRemote
|
||||||
|
@ -296,6 +297,7 @@ class CrossFadePlayer(val context: Context) : Playback, MediaPlayer.OnCompletion
|
||||||
Toast.LENGTH_SHORT
|
Toast.LENGTH_SHORT
|
||||||
)
|
)
|
||||||
.show()
|
.show()
|
||||||
|
Log.e(TAG, what.toString() + extra)
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -354,6 +356,10 @@ class CrossFadePlayer(val context: Context) : Playback, MediaPlayer.OnCompletion
|
||||||
override fun setCrossFadeDuration(duration: Int) {
|
override fun setCrossFadeDuration(duration: Int) {
|
||||||
crossFadeDuration = duration
|
crossFadeDuration = duration
|
||||||
}
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
val TAG: String = CrossFadePlayer::class.java.simpleName
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun crossFadeScope(): CoroutineScope = CoroutineScope(Job() + Dispatchers.Main)
|
internal fun crossFadeScope(): CoroutineScope = CoroutineScope(Job() + Dispatchers.Main)
|
|
@ -1,200 +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.service
|
|
||||||
|
|
||||||
import android.annotation.SuppressLint
|
|
||||||
import android.content.BroadcastReceiver
|
|
||||||
import android.content.Context
|
|
||||||
import android.content.Intent
|
|
||||||
import android.os.Handler
|
|
||||||
import android.os.Message
|
|
||||||
import android.os.PowerManager
|
|
||||||
import android.os.PowerManager.WakeLock
|
|
||||||
import android.util.Log
|
|
||||||
import android.view.KeyEvent
|
|
||||||
import androidx.core.content.ContextCompat
|
|
||||||
import androidx.core.content.getSystemService
|
|
||||||
import code.name.monkey.retromusic.BuildConfig
|
|
||||||
import code.name.monkey.retromusic.service.MusicService.Companion.ACTION_PAUSE
|
|
||||||
import code.name.monkey.retromusic.service.MusicService.Companion.ACTION_PLAY
|
|
||||||
import code.name.monkey.retromusic.service.MusicService.Companion.ACTION_REWIND
|
|
||||||
import code.name.monkey.retromusic.service.MusicService.Companion.ACTION_SKIP
|
|
||||||
import code.name.monkey.retromusic.service.MusicService.Companion.ACTION_STOP
|
|
||||||
import code.name.monkey.retromusic.service.MusicService.Companion.ACTION_TOGGLE_PAUSE
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Used to control headset playback.
|
|
||||||
* Single press: pause/resume
|
|
||||||
* Double press: actionNext track
|
|
||||||
* Triple press: previous track
|
|
||||||
*/
|
|
||||||
class MediaButtonIntentReceiver : BroadcastReceiver() {
|
|
||||||
|
|
||||||
override fun onReceive(context: Context, intent: Intent) {
|
|
||||||
if (DEBUG) Log.v(TAG, "Received intent: $intent")
|
|
||||||
if (handleIntent(context, intent) && isOrderedBroadcast) {
|
|
||||||
abortBroadcast()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
val TAG: String = MediaButtonIntentReceiver::class.java.simpleName
|
|
||||||
private val DEBUG = BuildConfig.DEBUG
|
|
||||||
private const val MSG_HEADSET_DOUBLE_CLICK_TIMEOUT = 2
|
|
||||||
|
|
||||||
private const val DOUBLE_CLICK = 400
|
|
||||||
|
|
||||||
private var wakeLock: WakeLock? = null
|
|
||||||
private var mClickCounter = 0
|
|
||||||
private var mLastClickTime: Long = 0
|
|
||||||
|
|
||||||
@SuppressLint("HandlerLeak") // false alarm, handler is already static
|
|
||||||
private val mHandler = object : Handler() {
|
|
||||||
|
|
||||||
override fun handleMessage(msg: Message) {
|
|
||||||
when (msg.what) {
|
|
||||||
MSG_HEADSET_DOUBLE_CLICK_TIMEOUT -> {
|
|
||||||
val clickCount = msg.arg1
|
|
||||||
|
|
||||||
if (DEBUG) Log.v(TAG, "Handling headset click, count = $clickCount")
|
|
||||||
val command = when (clickCount) {
|
|
||||||
1 -> ACTION_TOGGLE_PAUSE
|
|
||||||
2 -> ACTION_SKIP
|
|
||||||
3 -> ACTION_REWIND
|
|
||||||
else -> null
|
|
||||||
}
|
|
||||||
|
|
||||||
if (command != null) {
|
|
||||||
val context = msg.obj as Context
|
|
||||||
startService(context, command)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
releaseWakeLockIfHandlerIdle()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun handleIntent(context: Context, intent: Intent): Boolean {
|
|
||||||
val intentAction = intent.action
|
|
||||||
if (Intent.ACTION_MEDIA_BUTTON == intentAction) {
|
|
||||||
val event = intent.getParcelableExtra<KeyEvent>(Intent.EXTRA_KEY_EVENT)
|
|
||||||
?: return false
|
|
||||||
|
|
||||||
val keycode = event.keyCode
|
|
||||||
val action = event.action
|
|
||||||
val eventTime = if (event.eventTime != 0L)
|
|
||||||
event.eventTime
|
|
||||||
else
|
|
||||||
System.currentTimeMillis()
|
|
||||||
|
|
||||||
var command: String? = null
|
|
||||||
when (keycode) {
|
|
||||||
KeyEvent.KEYCODE_MEDIA_STOP -> command = ACTION_STOP
|
|
||||||
KeyEvent.KEYCODE_HEADSETHOOK, KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE -> command =
|
|
||||||
ACTION_TOGGLE_PAUSE
|
|
||||||
KeyEvent.KEYCODE_MEDIA_NEXT -> command = ACTION_SKIP
|
|
||||||
KeyEvent.KEYCODE_MEDIA_PREVIOUS -> command = ACTION_REWIND
|
|
||||||
KeyEvent.KEYCODE_MEDIA_PAUSE -> command = ACTION_PAUSE
|
|
||||||
KeyEvent.KEYCODE_MEDIA_PLAY -> command = ACTION_PLAY
|
|
||||||
}
|
|
||||||
if (command != null) {
|
|
||||||
if (action == KeyEvent.ACTION_DOWN) {
|
|
||||||
if (event.repeatCount == 0) {
|
|
||||||
// Only consider the first event in a sequence, not the repeat events,
|
|
||||||
// so that we don't trigger in cases where the first event went to
|
|
||||||
// a different app (e.g. when the user ends a phone call by
|
|
||||||
// long pressing the headset button)
|
|
||||||
|
|
||||||
// The service may or may not be running, but we need to send it
|
|
||||||
// a command.
|
|
||||||
if (keycode == KeyEvent.KEYCODE_HEADSETHOOK || keycode == KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE) {
|
|
||||||
if (eventTime - mLastClickTime >= DOUBLE_CLICK) {
|
|
||||||
mClickCounter = 0
|
|
||||||
}
|
|
||||||
|
|
||||||
mClickCounter++
|
|
||||||
if (DEBUG) Log.v(TAG, "Got headset click, count = $mClickCounter")
|
|
||||||
mHandler.removeMessages(MSG_HEADSET_DOUBLE_CLICK_TIMEOUT)
|
|
||||||
|
|
||||||
val msg = mHandler.obtainMessage(
|
|
||||||
MSG_HEADSET_DOUBLE_CLICK_TIMEOUT, mClickCounter, 0, context
|
|
||||||
)
|
|
||||||
|
|
||||||
val delay = (if (mClickCounter < 3) DOUBLE_CLICK else 0).toLong()
|
|
||||||
if (mClickCounter >= 3) {
|
|
||||||
mClickCounter = 0
|
|
||||||
}
|
|
||||||
mLastClickTime = eventTime
|
|
||||||
acquireWakeLockAndSendMessage(context, msg, delay)
|
|
||||||
} else {
|
|
||||||
startService(context, command)
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun startService(context: Context, command: String?) {
|
|
||||||
val intent = Intent(context, MusicService::class.java)
|
|
||||||
intent.action = command
|
|
||||||
try {
|
|
||||||
// IMPORTANT NOTE: (kind of a hack)
|
|
||||||
// on Android O and above the following crashes when the app is not running
|
|
||||||
// there is no good way to check whether the app is running so we catch the exception
|
|
||||||
// we do not always want to use startForegroundService() because then one gets an ANR
|
|
||||||
// if no notification is displayed via startForeground()
|
|
||||||
// according to Play analytics this happens a lot, I suppose for example if command = PAUSE
|
|
||||||
context.startService(intent)
|
|
||||||
} catch (ignored: IllegalStateException) {
|
|
||||||
ContextCompat.startForegroundService(context, intent)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun acquireWakeLockAndSendMessage(context: Context, msg: Message, delay: Long) {
|
|
||||||
if (wakeLock == null) {
|
|
||||||
val appContext = context.applicationContext
|
|
||||||
val pm = appContext.getSystemService<PowerManager>()
|
|
||||||
wakeLock = pm?.newWakeLock(
|
|
||||||
PowerManager.PARTIAL_WAKE_LOCK,
|
|
||||||
"RetroMusicApp:Wakelock headset button"
|
|
||||||
)
|
|
||||||
wakeLock!!.setReferenceCounted(false)
|
|
||||||
}
|
|
||||||
if (DEBUG) Log.v(TAG, "Acquiring wake lock and sending " + msg.what)
|
|
||||||
// Make sure we don't indefinitely hold the wake lock under any circumstances
|
|
||||||
wakeLock!!.acquire(10000)
|
|
||||||
|
|
||||||
mHandler.sendMessageDelayed(msg, delay)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun releaseWakeLockIfHandlerIdle() {
|
|
||||||
if (mHandler.hasMessages(MSG_HEADSET_DOUBLE_CLICK_TIMEOUT)) {
|
|
||||||
if (DEBUG) Log.v(TAG, "Handler still has messages pending, not releasing wake lock")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if (wakeLock != null) {
|
|
||||||
if (DEBUG) Log.v(TAG, "Releasing wake lock")
|
|
||||||
wakeLock!!.release()
|
|
||||||
wakeLock = null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -15,7 +15,6 @@
|
||||||
package code.name.monkey.retromusic.service
|
package code.name.monkey.retromusic.service
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.provider.MediaStore
|
import android.provider.MediaStore
|
||||||
import android.support.v4.media.session.MediaSessionCompat
|
import android.support.v4.media.session.MediaSessionCompat
|
||||||
|
@ -179,10 +178,6 @@ class MediaSessionCallback(
|
||||||
musicService.seek(pos.toInt())
|
musicService.seek(pos.toInt())
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onMediaButtonEvent(mediaButtonIntent: Intent): Boolean {
|
|
||||||
return MediaButtonIntentReceiver.handleIntent(context, mediaButtonIntent)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onCustomAction(action: String, extras: Bundle?) {
|
override fun onCustomAction(action: String, extras: Bundle?) {
|
||||||
when (action) {
|
when (action) {
|
||||||
CYCLE_REPEAT -> {
|
CYCLE_REPEAT -> {
|
||||||
|
|
|
@ -330,6 +330,7 @@ public class MultiPlayer
|
||||||
context.getResources().getString(R.string.unplayable_file),
|
context.getResources().getString(R.string.unplayable_file),
|
||||||
Toast.LENGTH_SHORT)
|
Toast.LENGTH_SHORT)
|
||||||
.show();
|
.show();
|
||||||
|
Log.e(TAG, String.valueOf(what) + extra);
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,7 +14,6 @@
|
||||||
package code.name.monkey.retromusic.service
|
package code.name.monkey.retromusic.service
|
||||||
|
|
||||||
import android.app.NotificationManager
|
import android.app.NotificationManager
|
||||||
import android.app.PendingIntent
|
|
||||||
import android.appwidget.AppWidgetManager
|
import android.appwidget.AppWidgetManager
|
||||||
import android.bluetooth.BluetoothDevice
|
import android.bluetooth.BluetoothDevice
|
||||||
import android.content.*
|
import android.content.*
|
||||||
|
@ -47,9 +46,9 @@ import androidx.media.AudioAttributesCompat.CONTENT_TYPE_MUSIC
|
||||||
import androidx.media.AudioFocusRequestCompat
|
import androidx.media.AudioFocusRequestCompat
|
||||||
import androidx.media.AudioManagerCompat
|
import androidx.media.AudioManagerCompat
|
||||||
import androidx.media.MediaBrowserServiceCompat
|
import androidx.media.MediaBrowserServiceCompat
|
||||||
|
import androidx.media.session.MediaButtonReceiver.handleIntent
|
||||||
import androidx.preference.PreferenceManager
|
import androidx.preference.PreferenceManager
|
||||||
import code.name.monkey.appthemehelper.util.VersionUtils.hasMarshmallow
|
import code.name.monkey.appthemehelper.util.VersionUtils
|
||||||
import code.name.monkey.appthemehelper.util.VersionUtils.hasQ
|
|
||||||
import code.name.monkey.retromusic.*
|
import code.name.monkey.retromusic.*
|
||||||
import code.name.monkey.retromusic.activities.LockScreenActivity
|
import code.name.monkey.retromusic.activities.LockScreenActivity
|
||||||
import code.name.monkey.retromusic.appwidgets.*
|
import code.name.monkey.retromusic.appwidgets.*
|
||||||
|
@ -175,10 +174,10 @@ class MusicService : MediaBrowserServiceCompat(),
|
||||||
private lateinit var mediaStoreObserver: ContentObserver
|
private lateinit var mediaStoreObserver: ContentObserver
|
||||||
private var musicPlayerHandlerThread: HandlerThread? = null
|
private var musicPlayerHandlerThread: HandlerThread? = null
|
||||||
private var notHandledMetaChangedForCurrentTrack = false
|
private var notHandledMetaChangedForCurrentTrack = false
|
||||||
private var originalPlayingQueue = mutableListOf<Song>()
|
private var originalPlayingQueue = ArrayList<Song>()
|
||||||
|
|
||||||
@JvmField
|
@JvmField
|
||||||
var playingQueue = mutableListOf<Song>()
|
var playingQueue = ArrayList<Song>()
|
||||||
var isPausedByTransientLossOfFocus = false
|
var isPausedByTransientLossOfFocus = false
|
||||||
private val becomingNoisyReceiver: BroadcastReceiver = object : BroadcastReceiver() {
|
private val becomingNoisyReceiver: BroadcastReceiver = object : BroadcastReceiver() {
|
||||||
override fun onReceive(context: Context, intent: Intent) {
|
override fun onReceive(context: Context, intent: Intent) {
|
||||||
|
@ -540,7 +539,7 @@ class MusicService : MediaBrowserServiceCompat(),
|
||||||
return shuffleMode
|
return shuffleMode
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun setShuffleMode(shuffleMode: Int) {
|
fun setShuffleMode(shuffleMode: Int) {
|
||||||
PreferenceManager.getDefaultSharedPreferences(this)
|
PreferenceManager.getDefaultSharedPreferences(this)
|
||||||
.edit()
|
.edit()
|
||||||
.putInt(SAVED_SHUFFLE_MODE, shuffleMode)
|
.putInt(SAVED_SHUFFLE_MODE, shuffleMode)
|
||||||
|
@ -752,6 +751,7 @@ class MusicService : MediaBrowserServiceCompat(),
|
||||||
|
|
||||||
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
|
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
|
||||||
if (intent != null && intent.action != null) {
|
if (intent != null && intent.action != null) {
|
||||||
|
handleIntent(mediaSession, intent)
|
||||||
restoreQueuesAndPositionIfNecessary()
|
restoreQueuesAndPositionIfNecessary()
|
||||||
when (intent.action) {
|
when (intent.action) {
|
||||||
ACTION_TOGGLE_PAUSE -> if (isPlaying) {
|
ACTION_TOGGLE_PAUSE -> if (isPlaying) {
|
||||||
|
@ -822,17 +822,16 @@ class MusicService : MediaBrowserServiceCompat(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Synchronized
|
||||||
fun openTrackAndPrepareNextAt(position: Int): Boolean {
|
fun openTrackAndPrepareNextAt(position: Int): Boolean {
|
||||||
synchronized(this) {
|
this.position = position
|
||||||
this.position = position
|
val prepared = openCurrent()
|
||||||
val prepared = openCurrent()
|
if (prepared) {
|
||||||
if (prepared) {
|
prepareNextImpl()
|
||||||
prepareNextImpl()
|
|
||||||
}
|
|
||||||
notifyChange(META_CHANGED)
|
|
||||||
notHandledMetaChangedForCurrentTrack = false
|
|
||||||
return prepared
|
|
||||||
}
|
}
|
||||||
|
notifyChange(META_CHANGED)
|
||||||
|
notHandledMetaChangedForCurrentTrack = false
|
||||||
|
return prepared
|
||||||
}
|
}
|
||||||
|
|
||||||
fun pause() {
|
fun pause() {
|
||||||
|
@ -856,48 +855,47 @@ class MusicService : MediaBrowserServiceCompat(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Synchronized
|
||||||
fun play() {
|
fun play() {
|
||||||
synchronized(this) {
|
if (requestFocus()) {
|
||||||
if (requestFocus()) {
|
if (playback != null && !playback!!.isPlaying) {
|
||||||
if (playback != null && !playback!!.isPlaying) {
|
if (!playback!!.isInitialized) {
|
||||||
if (!playback!!.isInitialized) {
|
playSongAt(getPosition())
|
||||||
playSongAt(getPosition())
|
} else {
|
||||||
} else {
|
//Don't Start playing when it's casting
|
||||||
//Don't Start playing when it's casting
|
if (isCasting) {
|
||||||
if (isCasting) {
|
return
|
||||||
return
|
|
||||||
}
|
|
||||||
startFadeAnimator(playback!!, true) {
|
|
||||||
|
|
||||||
// Code when Animator Ends
|
|
||||||
if (!becomingNoisyReceiverRegistered) {
|
|
||||||
registerReceiver(
|
|
||||||
becomingNoisyReceiver,
|
|
||||||
becomingNoisyReceiverIntentFilter
|
|
||||||
)
|
|
||||||
becomingNoisyReceiverRegistered = true
|
|
||||||
}
|
|
||||||
if (notHandledMetaChangedForCurrentTrack) {
|
|
||||||
handleChangeInternal(META_CHANGED)
|
|
||||||
notHandledMetaChangedForCurrentTrack = false
|
|
||||||
}
|
|
||||||
|
|
||||||
// fixes a bug where the volume would stay ducked because the
|
|
||||||
// AudioManager.AUDIOFOCUS_GAIN event is not sent
|
|
||||||
playerHandler?.removeMessages(DUCK)
|
|
||||||
playerHandler?.sendEmptyMessage(UNDUCK)
|
|
||||||
}
|
|
||||||
//Start Playback with Animator
|
|
||||||
playback?.start()
|
|
||||||
notifyChange(PLAY_STATE_CHANGED)
|
|
||||||
}
|
}
|
||||||
|
startFadeAnimator(playback!!, true) {
|
||||||
|
|
||||||
|
// Code when Animator Ends
|
||||||
|
if (!becomingNoisyReceiverRegistered) {
|
||||||
|
registerReceiver(
|
||||||
|
becomingNoisyReceiver,
|
||||||
|
becomingNoisyReceiverIntentFilter
|
||||||
|
)
|
||||||
|
becomingNoisyReceiverRegistered = true
|
||||||
|
}
|
||||||
|
if (notHandledMetaChangedForCurrentTrack) {
|
||||||
|
handleChangeInternal(META_CHANGED)
|
||||||
|
notHandledMetaChangedForCurrentTrack = false
|
||||||
|
}
|
||||||
|
|
||||||
|
// fixes a bug where the volume would stay ducked because the
|
||||||
|
// AudioManager.AUDIOFOCUS_GAIN event is not sent
|
||||||
|
playerHandler?.removeMessages(DUCK)
|
||||||
|
playerHandler?.sendEmptyMessage(UNDUCK)
|
||||||
|
}
|
||||||
|
//Start Playback with Animator
|
||||||
|
playback?.start()
|
||||||
|
notifyChange(PLAY_STATE_CHANGED)
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
Toast.makeText(
|
|
||||||
this, resources.getString(R.string.audio_focus_denied), Toast.LENGTH_SHORT
|
|
||||||
)
|
|
||||||
.show()
|
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
Toast.makeText(
|
||||||
|
this, resources.getString(R.string.audio_focus_denied), Toast.LENGTH_SHORT
|
||||||
|
)
|
||||||
|
.show()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -947,14 +945,13 @@ class MusicService : MediaBrowserServiceCompat(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Synchronized
|
||||||
fun prepareNextImpl() {
|
fun prepareNextImpl() {
|
||||||
synchronized(this) {
|
try {
|
||||||
try {
|
val nextPosition = getNextPosition(false)
|
||||||
val nextPosition = getNextPosition(false)
|
playback?.setNextDataSource(getTrackUri(getSongAt(nextPosition)))
|
||||||
playback?.setNextDataSource(getTrackUri(getSongAt(nextPosition)))
|
this.nextPosition = nextPosition
|
||||||
this.nextPosition = nextPosition
|
} catch (ignored: Exception) {
|
||||||
} catch (ignored: Exception) {
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1037,8 +1034,8 @@ class MusicService : MediaBrowserServiceCompat(),
|
||||||
SAVED_POSITION_IN_TRACK, -1
|
SAVED_POSITION_IN_TRACK, -1
|
||||||
)
|
)
|
||||||
if (restoredQueue.size > 0 && restoredQueue.size == restoredOriginalQueue.size && restoredPosition != -1) {
|
if (restoredQueue.size > 0 && restoredQueue.size == restoredOriginalQueue.size && restoredPosition != -1) {
|
||||||
originalPlayingQueue = restoredOriginalQueue
|
originalPlayingQueue = ArrayList(restoredOriginalQueue)
|
||||||
playingQueue = restoredQueue
|
playingQueue = ArrayList(restoredQueue)
|
||||||
position = restoredPosition
|
position = restoredPosition
|
||||||
openCurrent()
|
openCurrent()
|
||||||
prepareNext()
|
prepareNext()
|
||||||
|
@ -1073,18 +1070,17 @@ class MusicService : MediaBrowserServiceCompat(),
|
||||||
savePositionInTrack()
|
savePositionInTrack()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Synchronized
|
||||||
fun seek(millis: Int): Int {
|
fun seek(millis: Int): Int {
|
||||||
synchronized(this) {
|
return try {
|
||||||
return try {
|
var newPosition = 0
|
||||||
var newPosition = 0
|
if (playback != null) {
|
||||||
if (playback != null) {
|
newPosition = playback!!.seek(millis)
|
||||||
newPosition = playback!!.seek(millis)
|
|
||||||
}
|
|
||||||
throttledSeekHandler?.notifySeek()
|
|
||||||
newPosition
|
|
||||||
} catch (e: Exception) {
|
|
||||||
-1
|
|
||||||
}
|
}
|
||||||
|
throttledSeekHandler?.notifySeek()
|
||||||
|
newPosition
|
||||||
|
} catch (e: Exception) {
|
||||||
|
-1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1223,6 +1219,7 @@ class MusicService : MediaBrowserServiceCompat(),
|
||||||
}
|
}
|
||||||
META_CHANGED -> {
|
META_CHANGED -> {
|
||||||
playingNotification?.updateMetadata(currentSong) { startForegroundOrNotify() }
|
playingNotification?.updateMetadata(currentSong) { startForegroundOrNotify() }
|
||||||
|
playingNotification?.updateFavorite(currentSong) { startForegroundOrNotify() }
|
||||||
updateMediaSessionMetaData()
|
updateMediaSessionMetaData()
|
||||||
updateMediaSessionPlaybackState()
|
updateMediaSessionPlaybackState()
|
||||||
savePosition()
|
savePosition()
|
||||||
|
@ -1248,19 +1245,19 @@ class MusicService : MediaBrowserServiceCompat(),
|
||||||
|
|
||||||
private fun startForegroundOrNotify() {
|
private fun startForegroundOrNotify() {
|
||||||
if (playingNotification != null && currentSong.id != -1L) {
|
if (playingNotification != null && currentSong.id != -1L) {
|
||||||
val isPlaying = isPlaying
|
if (!VersionUtils.hasS()) {
|
||||||
if (isForeground != isPlaying && !isPlaying) {
|
if (isForeground && !isPlaying) {
|
||||||
// This makes the notification dismissible
|
// This makes the notification dismissible
|
||||||
// We can't call stopForeground(false) on A12 though, which may result in crashes
|
// We can't call stopForeground(false) on A12 though, which may result in crashes
|
||||||
// when we call startForeground after that e.g. when Alarm goes off,
|
// when we call startForeground after that e.g. when Alarm goes off
|
||||||
if (VERSION.SDK_INT < VERSION_CODES.S) {
|
|
||||||
stopForeground(false)
|
stopForeground(false)
|
||||||
isForeground = false
|
isForeground = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!isForeground && isPlaying) {
|
if (!isForeground) {
|
||||||
// Specify that this is a media service, if supported.
|
// Specify that this is a media service, if supported.
|
||||||
if (hasQ()) {
|
if (VersionUtils.hasQ()) {
|
||||||
startForeground(
|
startForeground(
|
||||||
PlayingNotification.NOTIFICATION_ID, playingNotification!!.build(),
|
PlayingNotification.NOTIFICATION_ID, playingNotification!!.build(),
|
||||||
ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PLAYBACK
|
ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PLAYBACK
|
||||||
|
@ -1279,7 +1276,6 @@ class MusicService : MediaBrowserServiceCompat(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun stopForegroundAndNotification() {
|
private fun stopForegroundAndNotification() {
|
||||||
|
@ -1288,24 +1284,22 @@ class MusicService : MediaBrowserServiceCompat(),
|
||||||
isForeground = false
|
isForeground = false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Synchronized
|
||||||
private fun openCurrent(): Boolean {
|
private fun openCurrent(): Boolean {
|
||||||
synchronized(this) {
|
return try {
|
||||||
try {
|
if (playback != null) {
|
||||||
if (playback != null) {
|
return playback!!.setDataSource(
|
||||||
return playback!!.setDataSource(
|
getTrackUri(
|
||||||
getTrackUri(
|
Objects.requireNonNull(
|
||||||
Objects.requireNonNull(
|
currentSong
|
||||||
currentSong
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
)
|
||||||
} catch (e: Exception) {
|
} else false
|
||||||
e.printStackTrace()
|
} catch (e: Exception) {
|
||||||
return false
|
e.printStackTrace()
|
||||||
}
|
false
|
||||||
}
|
}
|
||||||
return false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun playFromPlaylist(intent: Intent) {
|
private fun playFromPlaylist(intent: Intent) {
|
||||||
|
@ -1443,24 +1437,13 @@ class MusicService : MediaBrowserServiceCompat(),
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun setupMediaSession() {
|
private fun setupMediaSession() {
|
||||||
val mediaButtonReceiverComponentName =
|
|
||||||
ComponentName(applicationContext, MediaButtonIntentReceiver::class.java)
|
|
||||||
val mediaButtonIntent = Intent(Intent.ACTION_MEDIA_BUTTON)
|
|
||||||
mediaButtonIntent.component = mediaButtonReceiverComponentName
|
|
||||||
val mediaButtonReceiverPendingIntent = PendingIntent.getBroadcast(
|
|
||||||
applicationContext, 0, mediaButtonIntent,
|
|
||||||
if (hasMarshmallow()) PendingIntent.FLAG_IMMUTABLE else 0
|
|
||||||
)
|
|
||||||
mediaSession = MediaSessionCompat(
|
mediaSession = MediaSessionCompat(
|
||||||
this,
|
this,
|
||||||
"RetroMusicPlayer",
|
"RetroMusicPlayer"
|
||||||
mediaButtonReceiverComponentName,
|
|
||||||
mediaButtonReceiverPendingIntent
|
|
||||||
)
|
)
|
||||||
val mediasessionCallback = MediaSessionCallback(applicationContext, this)
|
val mediasessionCallback = MediaSessionCallback(applicationContext, this)
|
||||||
mediaSession?.setCallback(mediasessionCallback)
|
mediaSession?.setCallback(mediasessionCallback)
|
||||||
mediaSession?.isActive = true
|
mediaSession?.isActive = true
|
||||||
mediaSession?.setMediaButtonReceiver(mediaButtonReceiverPendingIntent)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
inner class MusicBinder : Binder() {
|
inner class MusicBinder : Binder() {
|
||||||
|
|
|
@ -25,6 +25,7 @@ import android.graphics.Color
|
||||||
import android.graphics.drawable.Drawable
|
import android.graphics.drawable.Drawable
|
||||||
import android.widget.RemoteViews
|
import android.widget.RemoteViews
|
||||||
import androidx.core.app.NotificationCompat
|
import androidx.core.app.NotificationCompat
|
||||||
|
import androidx.media.app.NotificationCompat.DecoratedMediaCustomViewStyle
|
||||||
import code.name.monkey.appthemehelper.util.ATHUtil.resolveColor
|
import code.name.monkey.appthemehelper.util.ATHUtil.resolveColor
|
||||||
import code.name.monkey.appthemehelper.util.ColorUtil
|
import code.name.monkey.appthemehelper.util.ColorUtil
|
||||||
import code.name.monkey.appthemehelper.util.MaterialValueHelper
|
import code.name.monkey.appthemehelper.util.MaterialValueHelper
|
||||||
|
@ -57,7 +58,6 @@ class PlayingNotificationClassic(
|
||||||
val context: Context
|
val context: Context
|
||||||
) : PlayingNotification(context) {
|
) : PlayingNotification(context) {
|
||||||
private var primaryColor: Int = 0
|
private var primaryColor: Int = 0
|
||||||
private var isInitialized = false
|
|
||||||
|
|
||||||
private fun getCombinedRemoteViews(collapsed: Boolean, song: Song): RemoteViews {
|
private fun getCombinedRemoteViews(collapsed: Boolean, song: Song): RemoteViews {
|
||||||
val remoteViews = RemoteViews(
|
val remoteViews = RemoteViews(
|
||||||
|
@ -75,7 +75,6 @@ class PlayingNotificationClassic(
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun updateMetadata(song: Song, onUpdate: () -> Unit) {
|
override fun updateMetadata(song: Song, onUpdate: () -> Unit) {
|
||||||
isInitialized = true
|
|
||||||
val notificationLayout = getCombinedRemoteViews(true, song)
|
val notificationLayout = getCombinedRemoteViews(true, song)
|
||||||
val notificationLayoutBig = getCombinedRemoteViews(false, song)
|
val notificationLayoutBig = getCombinedRemoteViews(false, song)
|
||||||
|
|
||||||
|
@ -98,11 +97,11 @@ class PlayingNotificationClassic(
|
||||||
setContentIntent(clickIntent)
|
setContentIntent(clickIntent)
|
||||||
setDeleteIntent(deleteIntent)
|
setDeleteIntent(deleteIntent)
|
||||||
setCategory(NotificationCompat.CATEGORY_SERVICE)
|
setCategory(NotificationCompat.CATEGORY_SERVICE)
|
||||||
setColorized(PreferenceUtil.isColoredNotification)
|
|
||||||
priority = NotificationCompat.PRIORITY_MAX
|
priority = NotificationCompat.PRIORITY_MAX
|
||||||
setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
|
setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
|
||||||
setCustomContentView(notificationLayout)
|
setCustomContentView(notificationLayout)
|
||||||
setCustomBigContentView(notificationLayoutBig)
|
setCustomBigContentView(notificationLayoutBig)
|
||||||
|
setStyle(DecoratedMediaCustomViewStyle())
|
||||||
setOngoing(true)
|
setOngoing(true)
|
||||||
val bigNotificationImageSize = context.resources
|
val bigNotificationImageSize = context.resources
|
||||||
.getDimensionPixelSize(R.dimen.notification_big_image_size)
|
.getDimensionPixelSize(R.dimen.notification_big_image_size)
|
||||||
|
@ -164,6 +163,7 @@ class PlayingNotificationClassic(
|
||||||
setNotificationContent(ColorUtil.isColorLight(bgColorFinal))
|
setNotificationContent(ColorUtil.isColorLight(bgColorFinal))
|
||||||
} else {
|
} else {
|
||||||
if (PreferenceUtil.isColoredNotification) {
|
if (PreferenceUtil.isColoredNotification) {
|
||||||
|
setColorized(true)
|
||||||
color = bgColor
|
color = bgColor
|
||||||
setNotificationContent(color.isColorLight)
|
setNotificationContent(color.isColorLight)
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -46,7 +46,10 @@ import code.name.monkey.retromusic.util.PreferenceUtil
|
||||||
import code.name.monkey.retromusic.util.RetroColorUtil
|
import code.name.monkey.retromusic.util.RetroColorUtil
|
||||||
import com.bumptech.glide.request.target.CustomTarget
|
import com.bumptech.glide.request.target.CustomTarget
|
||||||
import com.bumptech.glide.request.transition.Transition
|
import com.bumptech.glide.request.transition.Transition
|
||||||
import kotlinx.coroutines.*
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.GlobalScope
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import kotlinx.coroutines.withContext
|
||||||
|
|
||||||
@SuppressLint("RestrictedApi")
|
@SuppressLint("RestrictedApi")
|
||||||
class PlayingNotificationImpl24(
|
class PlayingNotificationImpl24(
|
||||||
|
@ -114,11 +117,6 @@ class PlayingNotificationImpl24(
|
||||||
.setShowActionsInCompactView(1, 2, 3)
|
.setShowActionsInCompactView(1, 2, 3)
|
||||||
)
|
)
|
||||||
setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
|
setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
|
||||||
if (Build.VERSION.SDK_INT <=
|
|
||||||
Build.VERSION_CODES.O && PreferenceUtil.isColoredNotification
|
|
||||||
) {
|
|
||||||
this.color = color
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun updateMetadata(song: Song, onUpdate: () -> Unit) {
|
override fun updateMetadata(song: Song, onUpdate: () -> Unit) {
|
||||||
|
@ -181,7 +179,6 @@ class PlayingNotificationImpl24(
|
||||||
onUpdate()
|
onUpdate()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
updateFavorite(song, onUpdate)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun buildPlayAction(isPlaying: Boolean): NotificationCompat.Action {
|
private fun buildPlayAction(isPlaying: Boolean): NotificationCompat.Action {
|
||||||
|
|
|
@ -49,6 +49,4 @@ class CarousalPagerTransformer(context: Context) : ViewPager.PageTransformer {
|
||||||
val m = context.resources.displayMetrics.density
|
val m = context.resources.displayMetrics.density
|
||||||
return (dipValue * m + 0.5f).toInt()
|
return (dipValue * m + 0.5f).toInt()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
|
@ -22,21 +22,28 @@ class CascadingPageTransformer : ViewPager.PageTransformer {
|
||||||
private var mScaleOffset = 40
|
private var mScaleOffset = 40
|
||||||
|
|
||||||
override fun transformPage(page: View, position: Float) {
|
override fun transformPage(page: View, position: Float) {
|
||||||
if (position <= 0.0f) {//被滑动的那页 position 是-下标~ 0
|
page.apply {
|
||||||
page.translationX = 0f
|
when {
|
||||||
//旋转角度 45° * -0.1 = -4.5°
|
position < -1 -> { // [-Infinity,-1)
|
||||||
page.rotation = 45 * position
|
alpha = 0f
|
||||||
//X轴偏移 li: 300/3 * -0.1 = -10
|
}
|
||||||
page.translationX = page.width / 3 * position
|
position <= 0 -> {
|
||||||
} else {
|
alpha = 1f
|
||||||
//缩放比例
|
rotation = 45 * position
|
||||||
val scale = (page.width - mScaleOffset * position) / page.width.toFloat()
|
translationX = width / 3 * position
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
alpha = 1f
|
||||||
|
rotation = 0f
|
||||||
|
val scale = (width - mScaleOffset * position) / width.toFloat()
|
||||||
|
|
||||||
page.scaleX = scale
|
scaleX = scale
|
||||||
page.scaleY = scale
|
scaleY = scale
|
||||||
|
|
||||||
page.translationX = -page.width * position
|
translationX = -width * position
|
||||||
page.translationY = mScaleOffset * 0.8f * position
|
translationY = mScaleOffset * 0.8f * position
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -0,0 +1,8 @@
|
||||||
|
package code.name.monkey.retromusic.transform
|
||||||
|
|
||||||
|
import android.view.View
|
||||||
|
import androidx.viewpager.widget.ViewPager
|
||||||
|
|
||||||
|
class DefaultTransformer : ViewPager.PageTransformer {
|
||||||
|
override fun transformPage(page: View, position: Float) {}
|
||||||
|
}
|
|
@ -20,28 +20,40 @@ import kotlin.math.abs
|
||||||
|
|
||||||
class DepthTransformation : ViewPager.PageTransformer {
|
class DepthTransformation : ViewPager.PageTransformer {
|
||||||
override fun transformPage(page: View, position: Float) {
|
override fun transformPage(page: View, position: Float) {
|
||||||
when {
|
page.apply {
|
||||||
position < -1 -> { // [-Infinity,-1)
|
when {
|
||||||
// This page is way off-screen to the left.
|
position < -1 -> { // [-Infinity,-1)
|
||||||
page.alpha = 0f
|
// This page is way off-screen to the left.
|
||||||
}
|
alpha = 0f
|
||||||
position <= 0 -> { // [-1,0]
|
}
|
||||||
page.alpha = 1f
|
position <= 0 -> { // [-1,0]
|
||||||
page.translationX = 0f
|
// Use the default slide transition when moving to the left page
|
||||||
page.scaleX = 1f
|
alpha = 1f
|
||||||
page.scaleY = 1f
|
translationX = 0f
|
||||||
}
|
scaleX = 1f
|
||||||
position <= 1 -> { // (0,1]
|
scaleY = 1f
|
||||||
page.translationX = -position * page.width
|
}
|
||||||
page.alpha = 1 - abs(position)
|
position <= 1 -> { // (0,1]
|
||||||
page.scaleX = 1 - abs(position)
|
// Fade the page out.
|
||||||
page.scaleY = 1 - abs(position)
|
alpha = 1 - position
|
||||||
}
|
|
||||||
else -> { // (1,+Infinity]
|
|
||||||
// This page is way off-screen to the right.
|
|
||||||
page.alpha = 0f
|
|
||||||
|
|
||||||
|
// Counteract the default slide transition
|
||||||
|
translationX = width * -position
|
||||||
|
|
||||||
|
// Scale the page down (between MIN_SCALE and 1)
|
||||||
|
val scaleFactor = (MIN_SCALE + (1 - MIN_SCALE) * (1 - abs(position)))
|
||||||
|
scaleX = scaleFactor
|
||||||
|
scaleY = scaleFactor
|
||||||
|
}
|
||||||
|
else -> { // (1,+Infinity]
|
||||||
|
// This page is way off-screen to the right.
|
||||||
|
alpha = 0f
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private const val MIN_SCALE = 0.5f
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -21,35 +21,35 @@ import kotlin.math.abs
|
||||||
|
|
||||||
class HingeTransformation : ViewPager.PageTransformer {
|
class HingeTransformation : ViewPager.PageTransformer {
|
||||||
override fun transformPage(page: View, position: Float) {
|
override fun transformPage(page: View, position: Float) {
|
||||||
|
page.apply {
|
||||||
|
translationX = -position * width
|
||||||
|
pivotX = 0f
|
||||||
|
pivotY = 0f
|
||||||
|
|
||||||
page.translationX = -position * page.width
|
when {
|
||||||
page.pivotX = 0f
|
position < -1 -> { // [-Infinity,-1)
|
||||||
page.pivotY = 0f
|
// This page is way off-screen to the left.
|
||||||
|
alpha = 0f
|
||||||
|
// The Page is off-screen but it may still interfere with
|
||||||
when {
|
// click events of current page if
|
||||||
position < -1 -> { // [-Infinity,-1)
|
// it's visibility is not set to Gone
|
||||||
// This page is way off-screen to the left.
|
isVisible = false
|
||||||
page.alpha = 0f
|
}
|
||||||
// The Page is off-screen but it may still interfere with
|
position <= 0 -> { // [-1,0]
|
||||||
// click events of current page if
|
rotation = 90 * abs(position)
|
||||||
// it's visibility is not set to Gone
|
alpha = 1 - abs(position)
|
||||||
page.isVisible = false
|
isVisible = true
|
||||||
}
|
}
|
||||||
position <= 0 -> { // [-1,0]
|
position <= 1 -> { // (0,1]
|
||||||
page.rotation = 90 * abs(position)
|
rotation = 0f
|
||||||
page.alpha = 1 - abs(position)
|
alpha = 1f
|
||||||
page.isVisible = true
|
isVisible = true
|
||||||
}
|
}
|
||||||
position <= 1 -> { // (0,1]
|
else -> { // (1,+Infinity]
|
||||||
page.rotation = 0f
|
// This page is way off-screen to the right.
|
||||||
page.alpha = 1f
|
alpha = 0f
|
||||||
page.isVisible = true
|
isVisible = false
|
||||||
}
|
}
|
||||||
else -> { // (1,+Infinity]
|
|
||||||
// This page is way off-screen to the right.
|
|
||||||
page.alpha = 0f
|
|
||||||
page.isVisible = false
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,33 +22,33 @@ import kotlin.math.abs
|
||||||
|
|
||||||
class HorizontalFlipTransformation : ViewPager.PageTransformer {
|
class HorizontalFlipTransformation : ViewPager.PageTransformer {
|
||||||
override fun transformPage(page: View, position: Float) {
|
override fun transformPage(page: View, position: Float) {
|
||||||
|
page.apply {
|
||||||
|
page.translationX = -position * page.width
|
||||||
|
page.cameraDistance = 20000f
|
||||||
|
|
||||||
page.translationX = -position * page.width
|
if (position < 0.5 && position > -0.5) {
|
||||||
page.cameraDistance = 20000f
|
page.isVisible = true
|
||||||
|
} else {
|
||||||
if (position < 0.5 && position > -0.5) {
|
page.isInvisible = true
|
||||||
page.isVisible = true
|
|
||||||
} else {
|
|
||||||
page.isInvisible = true
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
when {
|
|
||||||
position < -1 -> { // [-Infinity,-1)
|
|
||||||
// This page is way off-screen to the left.
|
|
||||||
page.alpha = 0f
|
|
||||||
}
|
}
|
||||||
position <= 0 -> { // [-1,0]
|
|
||||||
page.alpha = 1f
|
when {
|
||||||
page.rotationX = 180 * (1 - abs(position) + 1)
|
position < -1 -> { // [-Infinity,-1)
|
||||||
}
|
// This page is way off-screen to the left.
|
||||||
position <= 1 -> { // (0,1]
|
page.alpha = 0f
|
||||||
page.alpha = 1f
|
}
|
||||||
page.rotationX = -180 * (1 - abs(position) + 1)
|
position <= 0 -> { // [-1,0]
|
||||||
}
|
page.alpha = 1f
|
||||||
else -> { // (1,+Infinity]
|
page.rotationX = 180 * (1 - abs(position) + 1)
|
||||||
// This page is way off-screen to the right.
|
}
|
||||||
page.alpha = 0f
|
position <= 1 -> { // (0,1]
|
||||||
|
page.alpha = 1f
|
||||||
|
page.rotationX = -180 * (1 - abs(position) + 1)
|
||||||
|
}
|
||||||
|
else -> { // (1,+Infinity]
|
||||||
|
// This page is way off-screen to the right.
|
||||||
|
page.alpha = 0f
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,39 +25,41 @@ import kotlin.math.max
|
||||||
|
|
||||||
class NormalPageTransformer : ViewPager.PageTransformer {
|
class NormalPageTransformer : ViewPager.PageTransformer {
|
||||||
|
|
||||||
override fun transformPage(view: View, position: Float) {
|
override fun transformPage(page: View, position: Float) {
|
||||||
val pageWidth = view.width
|
page.apply {
|
||||||
val pageHeight = view.height
|
val pageWidth = width
|
||||||
|
val pageHeight = height
|
||||||
|
|
||||||
when {
|
when {
|
||||||
position < -1 -> { // [-Infinity,-1)
|
position < -1 -> { // [-Infinity,-1)
|
||||||
// This page is way off-screen to the left.
|
// This page is way off-screen to the left.
|
||||||
view.alpha = 1f
|
alpha = 1f
|
||||||
view.scaleY = 0.7f
|
scaleY = 0.7f
|
||||||
}
|
|
||||||
position <= 1 -> { // [-1,1]
|
|
||||||
// Modify the default slide transition to shrink the page as well
|
|
||||||
val scaleFactor = max(MIN_SCALE, 1 - abs(position))
|
|
||||||
val vertMargin = pageHeight * (1 - scaleFactor) / 2
|
|
||||||
val horzMargin = pageWidth * (1 - scaleFactor) / 2
|
|
||||||
if (position < 0) {
|
|
||||||
view.translationX = horzMargin - vertMargin / 2
|
|
||||||
} else {
|
|
||||||
view.translationX = -horzMargin + vertMargin / 2
|
|
||||||
}
|
}
|
||||||
|
position <= 1 -> { // [-1,1]
|
||||||
|
// Modify the default slide transition to shrink the page as well
|
||||||
|
val scaleFactor = max(MIN_SCALE, 1 - abs(position))
|
||||||
|
val vertMargin = pageHeight * (1 - scaleFactor) / 2
|
||||||
|
val horzMargin = pageWidth * (1 - scaleFactor) / 2
|
||||||
|
translationX = if (position < 0) {
|
||||||
|
horzMargin - vertMargin / 2
|
||||||
|
} else {
|
||||||
|
-horzMargin + vertMargin / 2
|
||||||
|
}
|
||||||
|
|
||||||
// Scale the page down (between MIN_SCALE and 1)
|
// Scale the page down (between MIN_SCALE and 1)
|
||||||
view.scaleX = scaleFactor
|
scaleX = scaleFactor
|
||||||
view.scaleY = scaleFactor
|
scaleY = scaleFactor
|
||||||
|
|
||||||
// Fade the page relative to its size.
|
// Fade the page relative to its size.
|
||||||
//view.setAlpha(MIN_ALPHA + (scaleFactor - MIN_SCALE) / (1 - MIN_SCALE) * (1 - MIN_ALPHA));
|
//setAlpha(MIN_ALPHA + (scaleFactor - MIN_SCALE) / (1 - MIN_SCALE) * (1 - MIN_ALPHA));
|
||||||
|
|
||||||
}
|
}
|
||||||
else -> { // (1,+Infinity]
|
else -> { // (1,+Infinity]
|
||||||
// This page is way off-screen to the right.
|
// This page is way off-screen to the right.
|
||||||
view.alpha = 1f
|
alpha = 1f
|
||||||
view.scaleY = 0.7f
|
scaleY = 0.7f
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,8 +14,6 @@
|
||||||
|
|
||||||
package code.name.monkey.retromusic.transform
|
package code.name.monkey.retromusic.transform
|
||||||
|
|
||||||
import android.annotation.TargetApi
|
|
||||||
import android.os.Build
|
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import androidx.viewpager.widget.ViewPager
|
import androidx.viewpager.widget.ViewPager
|
||||||
|
|
||||||
|
@ -27,22 +25,21 @@ class ParallaxPagerTransformer(private val id: Int) : ViewPager.PageTransformer
|
||||||
private var border = 0
|
private var border = 0
|
||||||
private var speed = 0.2f
|
private var speed = 0.2f
|
||||||
|
|
||||||
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
|
override fun transformPage(page: View, position: Float) {
|
||||||
override fun transformPage(view: View, position: Float) {
|
val parallaxView = page.findViewById<View>(id)
|
||||||
|
page.apply {
|
||||||
val parallaxView = view.findViewById<View>(id)
|
if (parallaxView != null) {
|
||||||
|
if (position > -1 && position < 1) {
|
||||||
if (parallaxView != null) {
|
val width = parallaxView.width.toFloat()
|
||||||
if (position > -1 && position < 1) {
|
parallaxView.translationX = -(position * width * speed)
|
||||||
val width = parallaxView.width.toFloat()
|
val sc = (width - border) / width
|
||||||
parallaxView.translationX = -(position * width * speed)
|
if (position == 0f) {
|
||||||
val sc = (view.width.toFloat() - border) / view.width
|
scaleX = 1f
|
||||||
if (position == 0f) {
|
scaleY = 1f
|
||||||
view.scaleX = 1f
|
} else {
|
||||||
view.scaleY = 1f
|
scaleX = sc
|
||||||
} else {
|
scaleY = sc
|
||||||
view.scaleX = sc
|
}
|
||||||
view.scaleY = sc
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,37 +22,35 @@ import kotlin.math.abs
|
||||||
|
|
||||||
class VerticalFlipTransformation : ViewPager.PageTransformer {
|
class VerticalFlipTransformation : ViewPager.PageTransformer {
|
||||||
override fun transformPage(page: View, position: Float) {
|
override fun transformPage(page: View, position: Float) {
|
||||||
|
page.apply {
|
||||||
|
translationX = -position * width
|
||||||
|
cameraDistance = 100000f
|
||||||
|
|
||||||
page.translationX = -position * page.width
|
if (position < 0.5 && position > -0.5) {
|
||||||
page.cameraDistance = 100000f
|
isVisible = true
|
||||||
|
} else {
|
||||||
if (position < 0.5 && position > -0.5) {
|
isInvisible = true
|
||||||
page.isVisible = true
|
|
||||||
} else {
|
|
||||||
page.isInvisible = true
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
when {
|
|
||||||
position < -1 -> { // [-Infinity,-1)
|
|
||||||
// This page is way off-screen to the left.
|
|
||||||
page.alpha = 0f
|
|
||||||
}
|
}
|
||||||
position <= 0 -> { // [-1,0]
|
|
||||||
page.alpha = 1f
|
|
||||||
page.rotationY = 180 * (1 - abs(position) + 1)
|
|
||||||
}
|
|
||||||
position <= 1 -> { // (0,1]
|
|
||||||
page.alpha = 1f
|
|
||||||
page.rotationY = -180 * (1 - abs(position) + 1)
|
|
||||||
}
|
|
||||||
else -> { // (1,+Infinity]
|
|
||||||
// This page is way off-screen to the right.
|
|
||||||
page.alpha = 0f
|
|
||||||
|
|
||||||
|
when {
|
||||||
|
position < -1 -> { // [-Infinity,-1)
|
||||||
|
// This page is way off-screen to the left.
|
||||||
|
alpha = 0f
|
||||||
|
}
|
||||||
|
position <= 0 -> { // [-1,0]
|
||||||
|
alpha = 1f
|
||||||
|
rotationY = 180 * (1 - abs(position) + 1)
|
||||||
|
}
|
||||||
|
position <= 1 -> { // (0,1]
|
||||||
|
alpha = 1f
|
||||||
|
rotationY = -180 * (1 - abs(position) + 1)
|
||||||
|
}
|
||||||
|
else -> { // (1,+Infinity]
|
||||||
|
// This page is way off-screen to the right.
|
||||||
|
alpha = 0f
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -19,11 +19,14 @@ import androidx.viewpager.widget.ViewPager
|
||||||
|
|
||||||
class VerticalStackTransformer : ViewPager.PageTransformer {
|
class VerticalStackTransformer : ViewPager.PageTransformer {
|
||||||
override fun transformPage(page: View, position: Float) {
|
override fun transformPage(page: View, position: Float) {
|
||||||
if (position >= 0) {
|
page.apply {
|
||||||
page.scaleX = (0.9f - 0.05f * position)
|
if (position >= 0) {
|
||||||
page.scaleY = 0.9f
|
scaleX = (0.9f - 0.05f * position)
|
||||||
page.translationX = -page.width * position
|
scaleY = 0.9f
|
||||||
page.translationY = -30 * position
|
translationX = -width * position
|
||||||
|
translationY = -30 * position
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,64 +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.volume;
|
|
||||||
|
|
||||||
import android.database.ContentObserver;
|
|
||||||
import android.media.AudioManager;
|
|
||||||
import android.net.Uri;
|
|
||||||
import android.os.Handler;
|
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
|
||||||
|
|
||||||
public class AudioVolumeContentObserver extends ContentObserver {
|
|
||||||
|
|
||||||
private final OnAudioVolumeChangedListener mListener;
|
|
||||||
|
|
||||||
private final AudioManager mAudioManager;
|
|
||||||
|
|
||||||
private final int mAudioStreamType;
|
|
||||||
|
|
||||||
private float mLastVolume;
|
|
||||||
|
|
||||||
AudioVolumeContentObserver(
|
|
||||||
@NonNull Handler handler,
|
|
||||||
@NonNull AudioManager audioManager,
|
|
||||||
int audioStreamType,
|
|
||||||
@NonNull OnAudioVolumeChangedListener listener) {
|
|
||||||
|
|
||||||
super(handler);
|
|
||||||
mAudioManager = audioManager;
|
|
||||||
mAudioStreamType = audioStreamType;
|
|
||||||
mListener = listener;
|
|
||||||
mLastVolume = audioManager.getStreamVolume(mAudioStreamType);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Depending on the handler this method may be executed on the UI thread */
|
|
||||||
@Override
|
|
||||||
public void onChange(boolean selfChange, Uri uri) {
|
|
||||||
if (mAudioManager != null && mListener != null) {
|
|
||||||
int maxVolume = mAudioManager.getStreamMaxVolume(mAudioStreamType);
|
|
||||||
int currentVolume = mAudioManager.getStreamVolume(mAudioStreamType);
|
|
||||||
if (currentVolume != mLastVolume) {
|
|
||||||
mLastVolume = currentVolume;
|
|
||||||
mListener.onAudioVolumeChanged(currentVolume, maxVolume);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean deliverSelfNotifications() {
|
|
||||||
return super.deliverSelfNotifications();
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,50 @@
|
||||||
|
/*
|
||||||
|
* 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.volume
|
||||||
|
|
||||||
|
import android.database.ContentObserver
|
||||||
|
import android.media.AudioManager
|
||||||
|
import android.net.Uri
|
||||||
|
import android.os.Handler
|
||||||
|
|
||||||
|
class AudioVolumeContentObserver internal constructor(
|
||||||
|
handler: Handler,
|
||||||
|
audioManager: AudioManager,
|
||||||
|
audioStreamType: Int,
|
||||||
|
listener: OnAudioVolumeChangedListener
|
||||||
|
) : ContentObserver(handler) {
|
||||||
|
private val mListener: OnAudioVolumeChangedListener?
|
||||||
|
private val mAudioManager: AudioManager?
|
||||||
|
private val mAudioStreamType: Int
|
||||||
|
private var mLastVolume: Float
|
||||||
|
|
||||||
|
/** Depending on the handler this method may be executed on the UI thread */
|
||||||
|
override fun onChange(selfChange: Boolean, uri: Uri?) {
|
||||||
|
if (mAudioManager != null && mListener != null) {
|
||||||
|
val maxVolume = mAudioManager.getStreamMaxVolume(mAudioStreamType)
|
||||||
|
val currentVolume = mAudioManager.getStreamVolume(mAudioStreamType)
|
||||||
|
if (currentVolume.toFloat() != mLastVolume) {
|
||||||
|
mLastVolume = currentVolume.toFloat()
|
||||||
|
mListener.onAudioVolumeChanged(currentVolume, maxVolume)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
init {
|
||||||
|
mAudioManager = audioManager
|
||||||
|
mAudioStreamType = audioStreamType
|
||||||
|
mListener = listener
|
||||||
|
mLastVolume = audioManager.getStreamVolume(mAudioStreamType).toFloat()
|
||||||
|
}
|
||||||
|
}
|
|
@ -27,7 +27,6 @@
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginStart="56dp"
|
android:layout_marginStart="56dp"
|
||||||
android:layout_marginLeft="56dp"
|
|
||||||
android:orientation="vertical"
|
android:orientation="vertical"
|
||||||
android:paddingTop="8dp"
|
android:paddingTop="8dp"
|
||||||
android:paddingBottom="8dp">
|
android:paddingBottom="8dp">
|
||||||
|
@ -54,9 +53,7 @@
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:orientation="vertical"
|
android:orientation="vertical"
|
||||||
android:paddingStart="72dp"
|
android:paddingStart="72dp"
|
||||||
android:paddingLeft="72dp"
|
android:paddingEnd="16dp">
|
||||||
android:paddingEnd="16dp"
|
|
||||||
android:paddingRight="16dp">
|
|
||||||
|
|
||||||
<com.google.android.material.textview.MaterialTextView
|
<com.google.android.material.textview.MaterialTextView
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
|
@ -166,7 +163,6 @@
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginStart="56dp"
|
android:layout_marginStart="56dp"
|
||||||
android:layout_marginLeft="56dp"
|
|
||||||
android:orientation="vertical"
|
android:orientation="vertical"
|
||||||
android:paddingTop="8dp"
|
android:paddingTop="8dp"
|
||||||
android:paddingBottom="8dp">
|
android:paddingBottom="8dp">
|
||||||
|
|
|
@ -74,7 +74,6 @@
|
||||||
<string name="app_shortcut_top_tracks_short">Canciones más reproducidas</string>
|
<string name="app_shortcut_top_tracks_short">Canciones más reproducidas</string>
|
||||||
<string name="app_widget_big_name">Imagen completa</string>
|
<string name="app_widget_big_name">Imagen completa</string>
|
||||||
<string name="app_widget_card_name">Tarjeta</string>
|
<string name="app_widget_card_name">Tarjeta</string>
|
||||||
<string name="app_widget_circle_name">@cadena/círculo</string>
|
|
||||||
<string name="app_widget_classic_name">Clásico</string>
|
<string name="app_widget_classic_name">Clásico</string>
|
||||||
<string name="app_widget_md3_name">MD3</string>
|
<string name="app_widget_md3_name">MD3</string>
|
||||||
<string name="app_widget_small_name">Pequeño</string>
|
<string name="app_widget_small_name">Pequeño</string>
|
|
@ -8,6 +8,7 @@
|
||||||
<item>@string/horizontal_flip</item>
|
<item>@string/horizontal_flip</item>
|
||||||
<item>@string/hinge</item>
|
<item>@string/hinge</item>
|
||||||
<item>@string/stack</item>
|
<item>@string/stack</item>
|
||||||
|
<item>@string/classic</item>
|
||||||
</array>
|
</array>
|
||||||
|
|
||||||
<string-array name="pref_album_cover_transform_values">
|
<string-array name="pref_album_cover_transform_values">
|
||||||
|
@ -18,6 +19,7 @@
|
||||||
<item>4</item>
|
<item>4</item>
|
||||||
<item>5</item>
|
<item>5</item>
|
||||||
<item>6</item>
|
<item>6</item>
|
||||||
|
<item>7</item>
|
||||||
</string-array>
|
</string-array>
|
||||||
|
|
||||||
<string-array name="pref_lyrics_type_values">
|
<string-array name="pref_lyrics_type_values">
|
||||||
|
@ -125,6 +127,7 @@
|
||||||
<item>Arabic</item>
|
<item>Arabic</item>
|
||||||
<item>Basque</item>
|
<item>Basque</item>
|
||||||
<item>Bengali</item>
|
<item>Bengali</item>
|
||||||
|
<item>Burmese</item>
|
||||||
<item>Catalan</item>
|
<item>Catalan</item>
|
||||||
<item>Simplified Chinese</item>
|
<item>Simplified Chinese</item>
|
||||||
<item>Traditional Chinese(Hong Kong)</item>
|
<item>Traditional Chinese(Hong Kong)</item>
|
||||||
|
@ -135,6 +138,7 @@
|
||||||
<item>Dutch</item>
|
<item>Dutch</item>
|
||||||
<item>English</item>
|
<item>English</item>
|
||||||
<item>Finnish</item>
|
<item>Finnish</item>
|
||||||
|
<item>Filipino</item>
|
||||||
<item>French</item>
|
<item>French</item>
|
||||||
<item>German</item>
|
<item>German</item>
|
||||||
<item>Greek</item>
|
<item>Greek</item>
|
||||||
|
@ -152,15 +156,18 @@
|
||||||
<item>Oriya</item>
|
<item>Oriya</item>
|
||||||
<item>Persian</item>
|
<item>Persian</item>
|
||||||
<item>Polish</item>
|
<item>Polish</item>
|
||||||
<item>Portuguese</item>
|
<item>Portuguese(Brazil)</item>
|
||||||
|
<item>Portuguese(Portugal)</item>
|
||||||
<item>Romanian</item>
|
<item>Romanian</item>
|
||||||
<item>Russian</item>
|
<item>Russian</item>
|
||||||
<item>Serbian</item>
|
<item>Serbian</item>
|
||||||
<item>Slovak</item>
|
<item>Slovak</item>
|
||||||
<item>Spain</item>
|
<item>Spanish</item>
|
||||||
|
<item>Spanish(Latin America)</item>
|
||||||
<item>Swedish</item>
|
<item>Swedish</item>
|
||||||
<item>Tamil</item>
|
<item>Tamil</item>
|
||||||
<item>Telugu</item>
|
<item>Telugu</item>
|
||||||
|
<item>Thai</item>
|
||||||
<item>Turkish</item>
|
<item>Turkish</item>
|
||||||
<item>Ukrainian</item>
|
<item>Ukrainian</item>
|
||||||
<item>Urdu</item>
|
<item>Urdu</item>
|
||||||
|
@ -172,6 +179,7 @@
|
||||||
<item>ar-SA</item>
|
<item>ar-SA</item>
|
||||||
<item>eu-ES</item>
|
<item>eu-ES</item>
|
||||||
<item>bn-IN</item>
|
<item>bn-IN</item>
|
||||||
|
<item>my-MM</item>
|
||||||
<item>ca-ES</item>
|
<item>ca-ES</item>
|
||||||
<item>zh-CN</item>
|
<item>zh-CN</item>
|
||||||
<item>zh-HK</item>
|
<item>zh-HK</item>
|
||||||
|
@ -182,6 +190,7 @@
|
||||||
<item>nl-NL</item>
|
<item>nl-NL</item>
|
||||||
<item>en</item>
|
<item>en</item>
|
||||||
<item>fi-FI</item>
|
<item>fi-FI</item>
|
||||||
|
<item>fil-PH</item>
|
||||||
<item>fr</item>
|
<item>fr</item>
|
||||||
<item>de</item>
|
<item>de</item>
|
||||||
<item>el</item>
|
<item>el</item>
|
||||||
|
@ -199,16 +208,19 @@
|
||||||
<item>or</item>
|
<item>or</item>
|
||||||
<item>fa</item>
|
<item>fa</item>
|
||||||
<item>pl</item>
|
<item>pl</item>
|
||||||
<item>pt</item>
|
<item>pt-BR</item>
|
||||||
|
<item>pt-PT</item>
|
||||||
<item>ro</item>
|
<item>ro</item>
|
||||||
<item>ru</item>
|
<item>ru</item>
|
||||||
<item>sr</item>
|
<item>sr</item>
|
||||||
<item>sk</item>
|
<item>sk</item>
|
||||||
<item>es</item>
|
<item>es</item>
|
||||||
|
<item>es-419</item>
|
||||||
<item>sv</item>
|
<item>sv</item>
|
||||||
<item>ta</item>
|
<item>ta</item>
|
||||||
<item>te</item>
|
<item>te</item>
|
||||||
<item>tr</item>
|
<item>tr</item>
|
||||||
|
<item>th-TH</item>
|
||||||
<item>uk</item>
|
<item>uk</item>
|
||||||
<item>ur</item>
|
<item>ur</item>
|
||||||
<item>vi</item>
|
<item>vi</item>
|
||||||
|
|
|
@ -533,4 +533,5 @@
|
||||||
<string name="you_have_to_select_at_least_one_category">You have to select at least one category.</string>
|
<string name="you_have_to_select_at_least_one_category">You have to select at least one category.</string>
|
||||||
<string name="you_will_be_forwarded_to_the_issue_tracker_website">You will be forwarded to the issue tracker website.</string>
|
<string name="you_will_be_forwarded_to_the_issue_tracker_website">You will be forwarded to the issue tracker website.</string>
|
||||||
<string name="your_account_data_is_only_used_for_authentication">Your account data is only used for authentication.</string>
|
<string name="your_account_data_is_only_used_for_authentication">Your account data is only used for authentication.</string>
|
||||||
|
<string name="bug_report_success">Bug report successful</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
|
@ -48,7 +48,8 @@
|
||||||
android:key="wallpaper_accent"
|
android:key="wallpaper_accent"
|
||||||
android:layout="@layout/list_item_view_switch"
|
android:layout="@layout/list_item_view_switch"
|
||||||
android:summary="@string/pref_summary_wallpaper_accent"
|
android:summary="@string/pref_summary_wallpaper_accent"
|
||||||
android:title="@string/pref_title_wallpaper_accent" />
|
android:title="@string/pref_title_wallpaper_accent"
|
||||||
|
app:isPreferenceVisible="@bool/wallpaper_accent_visible" />
|
||||||
|
|
||||||
<code.name.monkey.appthemehelper.common.prefs.supportv7.ATEColorPreference
|
<code.name.monkey.appthemehelper.common.prefs.supportv7.ATEColorPreference
|
||||||
android:dependency="material_you"
|
android:dependency="material_you"
|
||||||
|
|
|
@ -2,8 +2,8 @@
|
||||||
|
|
||||||
buildscript {
|
buildscript {
|
||||||
ext {
|
ext {
|
||||||
kotlin_version = '1.6.10'
|
kotlin_version = '1.6.20'
|
||||||
navigation_version = '2.4.1'
|
navigation_version = '2.4.2'
|
||||||
mdc_version = '1.6.0-beta01'
|
mdc_version = '1.6.0-beta01'
|
||||||
preference_version = '1.2.0'
|
preference_version = '1.2.0'
|
||||||
appcompat_version = '1.4.1'
|
appcompat_version = '1.4.1'
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue