Merge pull request #1320 from prathameshmm02/dev

Bug fixes
This commit is contained in:
Daksh P. Jain 2022-04-07 20:07:01 +05:30 committed by GitHub
commit 4d873e59f0
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
60 changed files with 735 additions and 1064 deletions

View file

@ -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'
} }

View file

@ -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

View file

@ -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>

View file

@ -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,

View file

@ -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)

View file

@ -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()

View file

@ -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

View file

@ -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) {
override fun createDialog(context: Context): Dialog {
return AlertDialog.Builder(context).show()
}
@Result
override fun doInBackground(vararg params: Void): String {
val client: GitHubClient = if (login.shouldUseApiToken()) { val client: GitHubClient = if (login.shouldUseApiToken()) {
GitHubClient().setOAuth2Token(login.apiToken) GitHubClient().setOAuth2Token(login.apiToken)
} else { } else {
GitHubClient().setCredentials(login.username, login.password) GitHubClient().setCredentials(login.username, login.password)
} }
val issue = Issue().setTitle(report.title).setBody(report.description) val issue = Issue().setTitle(report.title).setBody(report.getDescription())
try {
lifecycleScope.launch(Dispatchers.IO) {
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
}
} }
override fun onPostExecute(@Result result: String) { withContext(Dispatchers.Main) {
super.onPostExecute(result) val activity = this@BugReportActivity
val context = context ?: return
when (result) { when (result) {
RESULT_SUCCESS -> tryToFinishActivity() RESULT_SUCCESS -> MaterialAlertDialogBuilder(activity)
RESULT_BAD_CREDENTIALS -> MaterialAlertDialogBuilder(context) .setTitle(R.string.bug_report_success)
.setPositiveButton(android.R.string.ok) { _, _ -> tryToFinishActivity() }
.show()
RESULT_BAD_CREDENTIALS -> MaterialAlertDialogBuilder(activity)
.setTitle(R.string.bug_report_failed) .setTitle(R.string.bug_report_failed)
.setMessage(R.string.bug_report_failed_wrong_credentials) .setMessage(R.string.bug_report_failed_wrong_credentials)
.setPositiveButton(android.R.string.ok, null) .setPositiveButton(android.R.string.ok, null)
.show() .show()
RESULT_INVALID_TOKEN -> MaterialAlertDialogBuilder(context) RESULT_INVALID_TOKEN -> MaterialAlertDialogBuilder(activity)
.setTitle(R.string.bug_report_failed) .setTitle(R.string.bug_report_failed)
.setMessage(R.string.bug_report_failed_invalid_token) .setMessage(R.string.bug_report_failed_invalid_token)
.setPositiveButton(android.R.string.ok, null).show() .setPositiveButton(android.R.string.ok, null)
RESULT_ISSUES_NOT_ENABLED -> MaterialAlertDialogBuilder(context) .show()
RESULT_ISSUES_NOT_ENABLED -> MaterialAlertDialogBuilder(activity)
.setTitle(R.string.bug_report_failed) .setTitle(R.string.bug_report_failed)
.setMessage(R.string.bug_report_failed_issues_not_available) .setMessage(R.string.bug_report_failed_issues_not_available)
.setPositiveButton(android.R.string.ok, null) .setPositiveButton(android.R.string.ok, null)
.show()
else -> MaterialAlertDialogBuilder(context) else -> MaterialAlertDialogBuilder(activity)
.setTitle(R.string.bug_report_failed) .setTitle(R.string.bug_report_failed)
.setMessage(R.string.bug_report_failed_unknown) .setMessage(R.string.bug_report_failed_unknown)
.setPositiveButton(android.R.string.ok) { _, _ -> tryToFinishActivity() } .setPositiveButton(android.R.string.ok) { _, _ -> tryToFinishActivity() }
.setNegativeButton(android.R.string.cancel) { _, _ -> tryToFinishActivity() } .setNegativeButton(android.R.string.cancel) { _, _ -> tryToFinishActivity() }
.show()
}
}
} }
} }
private fun tryToFinishActivity() { private fun tryToFinishActivity() {
val context = context if (!isFinishing) {
if (context is Activity && !context.isFinishing) { finish()
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"
} }
} }

View file

@ -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;
}
}

View file

@ -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
}
}

View file

@ -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;
}
}

View file

@ -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()
}
}

View file

@ -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();
}
}

View file

@ -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()
}
}

View file

@ -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);
}
}

View file

@ -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)
}
}

View file

@ -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;
}
}

View file

@ -0,0 +1,3 @@
package code.name.monkey.retromusic.activities.bugreport.model.github
class GithubTarget(val username: String, val repository: String)

View file

@ -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) {

View file

@ -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>
} }

View file

@ -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))
}
}
} }

View file

@ -220,10 +220,10 @@ 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 = volumeFragment =
childFragmentManager.findFragmentById(R.id.volumeFragmentContainer) as? VolumeFragment childFragmentManager.findFragmentById(R.id.volumeFragmentContainer) as? VolumeFragment
} }
}
override fun onResume() { override fun onResume() {
super.onResume() super.onResume()

View file

@ -137,7 +137,6 @@ class VolumeFragment : Fragment(), SeekBar.OnSeekBarChangeListener, OnAudioVolum
} }
companion object { companion object {
fun newInstance(): VolumeFragment { fun newInstance(): VolumeFragment {
return VolumeFragment() return VolumeFragment()
} }

View file

@ -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))

View file

@ -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)

View file

@ -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)

View file

@ -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
} }

View file

@ -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) {

View file

@ -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() {}
} }

View file

@ -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()

View file

@ -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 {

View file

@ -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

View file

@ -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()

View file

@ -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)

View file

@ -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) }

View file

@ -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)

View file

@ -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
}
}
}
}

View file

@ -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 -> {

View file

@ -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;
} }

View file

@ -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,8 +822,8 @@ 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) {
@ -833,7 +833,6 @@ class MusicService : MediaBrowserServiceCompat(),
notHandledMetaChangedForCurrentTrack = false notHandledMetaChangedForCurrentTrack = false
return prepared return prepared
} }
}
fun pause() { fun pause() {
Log.i(TAG, "Paused") Log.i(TAG, "Paused")
@ -856,8 +855,8 @@ 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) {
@ -899,7 +898,6 @@ class MusicService : MediaBrowserServiceCompat(),
.show() .show()
} }
} }
}
fun playNextSong(force: Boolean) { fun playNextSong(force: Boolean) {
playSongAt(getNextPosition(force)) playSongAt(getNextPosition(force))
@ -947,8 +945,8 @@ 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)))
@ -956,7 +954,6 @@ class MusicService : MediaBrowserServiceCompat(),
} catch (ignored: Exception) { } catch (ignored: Exception) {
} }
} }
}
fun quit() { fun quit() {
pause() pause()
@ -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,8 +1070,8 @@ 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) {
@ -1086,7 +1083,6 @@ class MusicService : MediaBrowserServiceCompat(),
-1 -1
} }
} }
}
// to let other apps know whats playing. i.E. last.fm (scrobbling) or musixmatch // to let other apps know whats playing. i.E. last.fm (scrobbling) or musixmatch
fun sendPublicIntent(what: String) { fun sendPublicIntent(what: String) {
@ -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,9 +1284,9 @@ 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(
@ -1299,14 +1295,12 @@ class MusicService : MediaBrowserServiceCompat(),
) )
) )
) )
} } else false
} catch (e: Exception) { } catch (e: Exception) {
e.printStackTrace() e.printStackTrace()
return false false
} }
} }
return false
}
private fun playFromPlaylist(intent: Intent) { private fun playFromPlaylist(intent: Intent) {
val playlist: AbsSmartPlaylist? = intent.getParcelableExtra(INTENT_EXTRA_PLAYLIST) val playlist: AbsSmartPlaylist? = intent.getParcelableExtra(INTENT_EXTRA_PLAYLIST)
@ -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() {

View file

@ -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 {

View file

@ -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 {

View file

@ -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()
} }
} }

View file

@ -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
}
}
} }
} }
} }

View file

@ -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) {}
}

View file

@ -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) {
page.apply {
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.
page.alpha = 0f alpha = 0f
} }
position <= 0 -> { // [-1,0] position <= 0 -> { // [-1,0]
page.alpha = 1f // Use the default slide transition when moving to the left page
page.translationX = 0f alpha = 1f
page.scaleX = 1f translationX = 0f
page.scaleY = 1f scaleX = 1f
scaleY = 1f
} }
position <= 1 -> { // (0,1] position <= 1 -> { // (0,1]
page.translationX = -position * page.width // Fade the page out.
page.alpha = 1 - abs(position) alpha = 1 - position
page.scaleX = 1 - abs(position)
page.scaleY = 1 - abs(position) // 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] else -> { // (1,+Infinity]
// This page is way off-screen to the right. // This page is way off-screen to the right.
page.alpha = 0f alpha = 0f
}
}
}
}
} companion object {
} private const val MIN_SCALE = 0.5f
} }
} }

View file

@ -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 {
page.translationX = -position * page.width translationX = -position * width
page.pivotX = 0f pivotX = 0f
page.pivotY = 0f pivotY = 0f
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.
page.alpha = 0f alpha = 0f
// The Page is off-screen but it may still interfere with // The Page is off-screen but it may still interfere with
// click events of current page if // click events of current page if
// it's visibility is not set to Gone // it's visibility is not set to Gone
page.isVisible = false isVisible = false
} }
position <= 0 -> { // [-1,0] position <= 0 -> { // [-1,0]
page.rotation = 90 * abs(position) rotation = 90 * abs(position)
page.alpha = 1 - abs(position) alpha = 1 - abs(position)
page.isVisible = true isVisible = true
} }
position <= 1 -> { // (0,1] position <= 1 -> { // (0,1]
page.rotation = 0f rotation = 0f
page.alpha = 1f alpha = 1f
page.isVisible = true isVisible = true
} }
else -> { // (1,+Infinity] else -> { // (1,+Infinity]
// This page is way off-screen to the right. // This page is way off-screen to the right.
page.alpha = 0f alpha = 0f
page.isVisible = false isVisible = false
}
} }
} }
} }

View file

@ -22,7 +22,7 @@ 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.translationX = -position * page.width
page.cameraDistance = 20000f page.cameraDistance = 20000f
@ -32,7 +32,6 @@ class HorizontalFlipTransformation : ViewPager.PageTransformer {
page.isInvisible = true page.isInvisible = true
} }
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.
@ -52,4 +51,5 @@ class HorizontalFlipTransformation : ViewPager.PageTransformer {
} }
} }
} }
}
} }

View file

@ -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] position <= 1 -> { // [-1,1]
// Modify the default slide transition to shrink the page as well // Modify the default slide transition to shrink the page as well
val scaleFactor = max(MIN_SCALE, 1 - abs(position)) val scaleFactor = max(MIN_SCALE, 1 - abs(position))
val vertMargin = pageHeight * (1 - scaleFactor) / 2 val vertMargin = pageHeight * (1 - scaleFactor) / 2
val horzMargin = pageWidth * (1 - scaleFactor) / 2 val horzMargin = pageWidth * (1 - scaleFactor) / 2
if (position < 0) { translationX = if (position < 0) {
view.translationX = horzMargin - vertMargin / 2 horzMargin - vertMargin / 2
} else { } else {
view.translationX = -horzMargin + vertMargin / 2 -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
}
} }
} }
} }

View file

@ -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 (parallaxView != null) {
if (position > -1 && position < 1) { if (position > -1 && position < 1) {
val width = parallaxView.width.toFloat() val width = parallaxView.width.toFloat()
parallaxView.translationX = -(position * width * speed) parallaxView.translationX = -(position * width * speed)
val sc = (view.width.toFloat() - border) / view.width val sc = (width - border) / width
if (position == 0f) { if (position == 0f) {
view.scaleX = 1f scaleX = 1f
view.scaleY = 1f scaleY = 1f
} else { } else {
view.scaleX = sc scaleX = sc
view.scaleY = sc scaleY = sc
}
} }
} }
} }

View file

@ -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 {
page.translationX = -position * page.width translationX = -position * width
page.cameraDistance = 100000f cameraDistance = 100000f
if (position < 0.5 && position > -0.5) { if (position < 0.5 && position > -0.5) {
page.isVisible = true isVisible = true
} else { } else {
page.isInvisible = true isInvisible = true
} }
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.
page.alpha = 0f alpha = 0f
} }
position <= 0 -> { // [-1,0] position <= 0 -> { // [-1,0]
page.alpha = 1f alpha = 1f
page.rotationY = 180 * (1 - abs(position) + 1) rotationY = 180 * (1 - abs(position) + 1)
} }
position <= 1 -> { // (0,1] position <= 1 -> { // (0,1]
page.alpha = 1f alpha = 1f
page.rotationY = -180 * (1 - abs(position) + 1) rotationY = -180 * (1 - abs(position) + 1)
} }
else -> { // (1,+Infinity] else -> { // (1,+Infinity]
// This page is way off-screen to the right. // This page is way off-screen to the right.
page.alpha = 0f alpha = 0f
}
} }
} }
} }
} }

View file

@ -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) {
page.apply {
if (position >= 0) { if (position >= 0) {
page.scaleX = (0.9f - 0.05f * position) scaleX = (0.9f - 0.05f * position)
page.scaleY = 0.9f scaleY = 0.9f
page.translationX = -page.width * position translationX = -width * position
page.translationY = -30 * position translationY = -30 * position
} }
} }
}
} }

View file

@ -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();
}
}

View file

@ -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()
}
}

View file

@ -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">

View file

@ -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>

View file

@ -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>

View file

@ -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>

View file

@ -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"

View file

@ -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'