diff --git a/app/build.gradle b/app/build.gradle index f45ff7d9..76738816 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -6,7 +6,6 @@ apply plugin: 'kotlin-parcelize' android { compileSdkVersion 31 - buildToolsVersion = '29.0.3' defaultConfig { minSdkVersion 21 @@ -16,7 +15,7 @@ android { vectorDrawables.useSupportLibrary = true applicationId "code.name.monkey.retromusic" - versionCode 10520 + versionCode 10519 versionName '5.0.0' + "_" + getDate() buildConfigField("String", "GOOGLE_PLAY_LICENSING_KEY", "\"${getProperty(getProperties('../public.properties'), 'GOOGLE_PLAY_LICENSE_KEY')}\"") @@ -92,21 +91,21 @@ dependencies { implementation project(':appthemehelper') implementation "androidx.gridlayout:gridlayout:1.0.0" implementation "androidx.cardview:cardview:1.0.0" - implementation 'androidx.appcompat:appcompat:1.3.1' - implementation 'androidx.annotation:annotation:1.2.0' - implementation 'androidx.constraintlayout:constraintlayout:2.1.0' + implementation 'androidx.appcompat:appcompat:1.4.0' + implementation 'androidx.annotation:annotation:1.3.0' + implementation 'androidx.constraintlayout:constraintlayout:2.1.2' implementation 'androidx.recyclerview:recyclerview:1.2.1' implementation 'androidx.preference:preference-ktx:1.1.1' - implementation 'androidx.core:core-ktx:1.6.0' + implementation 'androidx.core:core-ktx:1.7.0' implementation 'androidx.palette:palette-ktx:1.0.0' //Cast Dependencies implementation 'androidx.mediarouter:mediarouter:1.2.5' - implementation 'com.google.android.gms:play-services-cast-framework:20.0.0' + implementation 'com.google.android.gms:play-services-cast-framework:20.1.0' //WebServer by NanoHttpd implementation "org.nanohttpd:nanohttpd:2.3.1" - def nav_version = "2.4.0-alpha07" + def nav_version = '2.4.0-beta02' implementation "androidx.navigation:navigation-runtime-ktx:$nav_version" implementation "androidx.navigation:navigation-fragment-ktx:$nav_version" implementation "androidx.navigation:navigation-ui-ktx:$nav_version" @@ -116,13 +115,13 @@ dependencies { implementation "androidx.room:room-ktx:$room_version" kapt "androidx.room:room-compiler:$room_version" - def lifecycle_version = "2.3.1" + def lifecycle_version = "2.4.0" implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycle_version" implementation "androidx.lifecycle:lifecycle-livedata-ktx:$lifecycle_version" implementation "androidx.lifecycle:lifecycle-common-java8:$lifecycle_version" implementation 'com.google.android.play:core-ktx:1.8.1' - implementation 'com.google.android.material:material:1.5.0-alpha03' + implementation 'com.google.android.material:material:1.5.0-beta01' def retrofit_version = '2.9.0' implementation "com.squareup.retrofit2:retrofit:$retrofit_version" @@ -134,18 +133,18 @@ dependencies { implementation "com.afollestad.material-dialogs:input:$material_dialog_version" implementation "com.afollestad.material-dialogs:color:$material_dialog_version" implementation "com.afollestad.material-dialogs:bottomsheets:$material_dialog_version" - //noinspection GradleDependency - implementation 'com.afollestad:material-cab:0.1.12' + + implementation 'com.afollestad:material-cab:2.0.1' implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version" - def kotlin_coroutines_version = "1.5.2-native-mt" + def kotlin_coroutines_version = '1.5.2' implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$kotlin_coroutines_version" implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$kotlin_coroutines_version" - def koin_version = "2.1.5" - implementation "org.koin:koin-core:$koin_version" - implementation "org.koin:koin-androidx-viewmodel:$koin_version" + def koin_version = '3.1.3' + implementation "io.insert-koin:koin-core:$koin_version" + implementation "io.insert-koin:koin-android:$koin_version" implementation 'com.github.bumptech.glide:glide:4.12.0' kapt 'com.github.bumptech.glide:compiler:4.12.0' @@ -153,14 +152,15 @@ dependencies { implementation 'com.h6ah4i.android.widget.advrecyclerview:advrecyclerview:1.0.0' - implementation 'org.eclipse.mylyn.github:org.eclipse.egit.github.core:3.4.0.201406110918-r' - implementation 'org.bitbucket.ijabz:jaudiotagger:2.2.5' - implementation 'com.anjlab.android.iab.v3:library:1.1.0' + implementation 'org.eclipse.mylyn.github:org.eclipse.egit.github.core:2.1.5' + implementation 'com.github.AdrienPoupa:jaudiotagger:2.2.3' + implementation 'com.anjlab.android.iab.v3:library:2.0.1' implementation 'com.r0adkll:slidableactivity:2.1.0' implementation 'com.heinrichreimersoftware:material-intro:2.0.0' implementation 'com.github.dhaval2404:imagepicker:1.7.1' implementation 'me.zhanghai.android.fastscroll:library:1.1.7' - debugImplementation 'com.amitshekhar.android:debug-db:1.0.6' + implementation 'cat.ereza:customactivityoncrash:2.3.0' + debugImplementation 'com.github.amitshekhariitbhu:Android-Debug-Database:1.0.6' } apply from: '../spotless.gradle' diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro index f5edd6ed..20ae1348 100644 --- a/app/proguard-rules.pro +++ b/app/proguard-rules.pro @@ -16,16 +16,13 @@ # public *; #} -# Uncomment this to preserve the line number information for +# Preserve the line number information for # debugging stack traces. -#-keepattributes SourceFile,LineNumberTable +-keepattributes SourceFile,LineNumberTable # If you keep the line number information, uncomment this to # hide the original source file name. #-renamesourcefileattribute SourceFile --keepnames class ** --keepnames class ** { *; } --keepattributes SourceFile,LineNumberTable -dontwarn java.lang.invoke.* -dontwarn **$$Lambda$* @@ -58,11 +55,9 @@ -keepclassmembers enum * { *; } -keepattributes *Annotation*, Signature, Exception -keepnames class androidx.navigation.fragment.NavHostFragment --keepnames class code.name.monkey.retromusic.model.Home -keep class * extends androidx.fragment.app.Fragment{} -keepnames class * extends android.os.Parcelable -keepnames class * extends java.io.Serializable -keep class code.name.monkey.retromusic.network.model.** { *; } -keep class code.name.monkey.retromusic.model.CategoryInfo { *; } --keep class com.google.android.material.bottomsheet.** { *; } --keep class code.name.monkey.retromusic.Constants { *; } \ No newline at end of file +-keep class com.google.android.material.bottomsheet.** { *; } \ No newline at end of file diff --git a/app/src/debug/res/values/donottranslate.xml b/app/src/debug/res/values/donottranslate.xml new file mode 100644 index 00000000..879226a3 --- /dev/null +++ b/app/src/debug/res/values/donottranslate.xml @@ -0,0 +1,4 @@ + + + true + \ No newline at end of file diff --git a/app/src/debug/res/values/styles.xml b/app/src/debug/res/values/styles.xml index e984e856..b9ca988e 100644 --- a/app/src/debug/res/values/styles.xml +++ b/app/src/debug/res/values/styles.xml @@ -96,7 +96,7 @@ -

- Clear the app if it crashes after updating -

- +
Date
+

v5.1.0Beta

+

What's New

+ +

Improved

+ +
+
September 06, 2021

v5.0.0

What's New

+
+
+
August 22, 2021
+

v4.4.0Beta

+

What's New

+

Fixed

+
+
+
August 11, 2021
+

v4.2.30Beta

+

What's New

+ +

Fixed

+ +
+
+
July 18, 2021
+

v4.2.020Beta

+

What's New

+

Improved

- - +
+
October 12, 2020
+

v4.0.10

+

What's New

+ +
+
+
April 30, 2020
+

v3.5.110Beta

+

What's New

+ +

Improved

+ +
+ \ No newline at end of file diff --git a/app/src/main/java/code/name/monkey/retromusic/App.kt b/app/src/main/java/code/name/monkey/retromusic/App.kt index 7e5202f5..1b46996c 100644 --- a/app/src/main/java/code/name/monkey/retromusic/App.kt +++ b/app/src/main/java/code/name/monkey/retromusic/App.kt @@ -21,7 +21,7 @@ import code.name.monkey.appthemehelper.util.VersionUtils import code.name.monkey.retromusic.Constants.PRO_VERSION_PRODUCT_ID import code.name.monkey.retromusic.appshortcuts.DynamicShortcutManager import com.anjlab.android.iab.v3.BillingProcessor -import com.anjlab.android.iab.v3.TransactionDetails +import com.anjlab.android.iab.v3.PurchaseInfo import org.koin.android.ext.koin.androidContext import org.koin.core.context.startKoin @@ -49,9 +49,10 @@ class App : Application() { DynamicShortcutManager(this).initDynamicShortcuts() // automatically restores purchases - billingProcessor = BillingProcessor(this, BuildConfig.GOOGLE_PLAY_LICENSING_KEY, + billingProcessor = BillingProcessor( + this, BuildConfig.GOOGLE_PLAY_LICENSING_KEY, object : BillingProcessor.IBillingHandler { - override fun onProductPurchased(productId: String, details: TransactionDetails?) {} + override fun onProductPurchased(productId: String, details: PurchaseInfo?) {} override fun onPurchaseHistoryRestored() { Toast.makeText( diff --git a/app/src/main/java/code/name/monkey/retromusic/Constants.kt b/app/src/main/java/code/name/monkey/retromusic/Constants.kt index 738e5aa8..8b97ef47 100644 --- a/app/src/main/java/code/name/monkey/retromusic/Constants.kt +++ b/app/src/main/java/code/name/monkey/retromusic/Constants.kt @@ -148,3 +148,4 @@ const val SHOW_LYRICS = "show_lyrics" const val REMEMBER_LAST_TAB = "remember_last_tab" const val LAST_USED_TAB = "last_used_tab" const val WHITELIST_MUSIC = "whitelist_music" +const val MATERIAL_YOU = "material_you" diff --git a/app/src/main/java/code/name/monkey/retromusic/activities/LicenseActivity.java b/app/src/main/java/code/name/monkey/retromusic/activities/LicenseActivity.java deleted file mode 100644 index f9ada218..00000000 --- a/app/src/main/java/code/name/monkey/retromusic/activities/LicenseActivity.java +++ /dev/null @@ -1,107 +0,0 @@ -/* - * Copyright (c) 2019 Hemanth Savarala. - * - * Licensed under the GNU General Public License v3 - * - * This is free software: you can redistribute it and/or modify it under - * the terms of the GNU General Public License as published by - * the Free Software Foundation either version 3 of the License, or (at your option) any later version. - * - * This software is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU General Public License for more details. - */ - -package code.name.monkey.retromusic.activities; - -import android.graphics.Color; -import android.os.Bundle; -import android.view.MenuItem; -import android.webkit.WebView; - -import androidx.annotation.NonNull; -import androidx.appcompat.widget.Toolbar; - -import org.jetbrains.annotations.Nullable; - -import java.io.BufferedReader; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.nio.charset.StandardCharsets; - -import code.name.monkey.appthemehelper.ThemeStore; -import code.name.monkey.appthemehelper.util.ATHUtil; -import code.name.monkey.appthemehelper.util.ColorUtil; -import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper; -import code.name.monkey.retromusic.R; -import code.name.monkey.retromusic.activities.base.AbsBaseActivity; - -/** Created by hemanths on 2019-09-27. */ -public class LicenseActivity extends AbsBaseActivity { - - @Override - protected void onCreate(@Nullable Bundle savedInstanceState) { - setDrawUnderStatusBar(); - super.onCreate(savedInstanceState); - setContentView(R.layout.activity_license); - setStatusbarColorAuto(); - setNavigationbarColorAuto(); - setLightNavigationBar(true); - Toolbar toolbar = findViewById(R.id.toolbar); - setSupportActionBar(toolbar); - ToolbarContentTintHelper.colorBackButton(toolbar); - toolbar.setBackgroundColor(ATHUtil.INSTANCE.resolveColor(this, R.attr.colorSurface)); - WebView webView = findViewById(R.id.license); - try { - StringBuilder buf = new StringBuilder(); - InputStream json = getAssets().open("oldindex.html"); - BufferedReader in = new BufferedReader(new InputStreamReader(json, StandardCharsets.UTF_8)); - String str; - while ((str = in.readLine()) != null) { - buf.append(str); - } - in.close(); - - // Inject color values for WebView body background and links - final boolean isDark = ATHUtil.INSTANCE.isWindowBackgroundDark(this); - final String backgroundColor = - colorToCSS( - ATHUtil.INSTANCE.resolveColor( - this, R.attr.colorSurface, Color.parseColor(isDark ? "#424242" : "#ffffff"))); - final String contentColor = colorToCSS(Color.parseColor(isDark ? "#ffffff" : "#000000")); - final String changeLog = - buf.toString() - .replace( - "{style-placeholder}", - String.format( - "body { background-color: %s; color: %s; }", backgroundColor, contentColor)) - .replace("{link-color}", colorToCSS(ThemeStore.Companion.accentColor(this))) - .replace( - "{link-color-active}", - colorToCSS( - ColorUtil.INSTANCE.lightenColor(ThemeStore.Companion.accentColor(this)))); - - webView.loadData(changeLog, "text/html", "UTF-8"); - } catch (Throwable e) { - webView.loadData( - "

Unable to load

" + e.getLocalizedMessage() + "

", "text/html", "UTF-8"); - } - } - - @Override - public boolean onOptionsItemSelected(@NonNull MenuItem item) { - if (item.getItemId() == android.R.id.home) { - onBackPressed(); - return true; - } - return super.onOptionsItemSelected(item); - } - - private String colorToCSS(int color) { - return String.format( - "rgb(%d, %d, %d)", - Color.red(color), - Color.green(color), - Color.blue(color)); // on API 29, WebView doesn't load with hex colors - } -} diff --git a/app/src/main/java/code/name/monkey/retromusic/activities/LicenseActivity.kt b/app/src/main/java/code/name/monkey/retromusic/activities/LicenseActivity.kt new file mode 100644 index 00000000..11cd201c --- /dev/null +++ b/app/src/main/java/code/name/monkey/retromusic/activities/LicenseActivity.kt @@ -0,0 +1,99 @@ +/* + * Copyright (c) 2019 Hemanth Savarala. + * + * Licensed under the GNU General Public License v3 + * + * This is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by + * the Free Software Foundation either version 3 of the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + */ +package code.name.monkey.retromusic.activities + +import android.graphics.Color +import android.os.Bundle +import android.view.MenuItem +import android.webkit.WebView +import androidx.appcompat.widget.Toolbar +import code.name.monkey.appthemehelper.ThemeStore.Companion.accentColor +import code.name.monkey.appthemehelper.util.ATHUtil.isWindowBackgroundDark +import code.name.monkey.appthemehelper.util.ATHUtil.resolveColor +import code.name.monkey.appthemehelper.util.ColorUtil.lightenColor +import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper +import code.name.monkey.retromusic.R +import code.name.monkey.retromusic.activities.base.AbsThemeActivity +import java.io.BufferedReader +import java.io.InputStreamReader +import java.nio.charset.StandardCharsets + +/** Created by hemanths on 2019-09-27. */ +class LicenseActivity : AbsThemeActivity() { + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(R.layout.activity_license) + val toolbar = findViewById(R.id.toolbar) + setSupportActionBar(toolbar) + ToolbarContentTintHelper.colorBackButton(toolbar) + toolbar.setBackgroundColor(resolveColor(this, R.attr.colorSurface)) + val webView = findViewById(R.id.license) + try { + val buf = StringBuilder() + val json = assets.open("oldindex.html") + val br = BufferedReader(InputStreamReader(json, StandardCharsets.UTF_8)) + var str: String? + while (br.readLine().also { str = it } != null) { + buf.append(str) + } + br.close() + + // Inject color values for WebView body background and links + val isDark = isWindowBackgroundDark(this) + val backgroundColor = colorToCSS( + resolveColor( + this, + R.attr.colorSurface, + Color.parseColor(if (isDark) "#424242" else "#ffffff") + ) + ) + val contentColor = colorToCSS(Color.parseColor(if (isDark) "#ffffff" else "#000000")) + val changeLog = buf.toString() + .replace( + "{style-placeholder}", String.format( + "body { background-color: %s; color: %s; }", backgroundColor, contentColor + ) + ) + .replace("{link-color}", colorToCSS(accentColor(this))) + .replace( + "{link-color-active}", + colorToCSS( + lightenColor(accentColor(this)) + ) + ) + webView.loadData(changeLog, "text/html", "UTF-8") + } catch (e: Throwable) { + webView.loadData( + "

Unable to load

" + e.localizedMessage + "

", "text/html", "UTF-8" + ) + } + } + + override fun onOptionsItemSelected(item: MenuItem): Boolean { + if (item.itemId == android.R.id.home) { + onBackPressed() + return true + } + return super.onOptionsItemSelected(item) + } + + private fun colorToCSS(color: Int): String { + return String.format( + "rgb(%d, %d, %d)", + Color.red(color), + Color.green(color), + Color.blue(color) + ) // on API 29, WebView doesn't load with hex colors + } +} \ No newline at end of file diff --git a/app/src/main/java/code/name/monkey/retromusic/activities/LockScreenActivity.kt b/app/src/main/java/code/name/monkey/retromusic/activities/LockScreenActivity.kt index 792c8365..621bc177 100644 --- a/app/src/main/java/code/name/monkey/retromusic/activities/LockScreenActivity.kt +++ b/app/src/main/java/code/name/monkey/retromusic/activities/LockScreenActivity.kt @@ -48,9 +48,7 @@ class LockScreenActivity : AbsMusicServiceActivity() { setContentView(binding.root) hideStatusBar() setStatusbarColorAuto() - setNavigationbarColorAuto() setTaskDescriptionColorAuto() - setLightNavigationBar(true) val config = SlidrConfig.Builder().listener(object : SlidrListener { override fun onSlideStateChanged(state: Int) { diff --git a/app/src/main/java/code/name/monkey/retromusic/activities/MainActivity.kt b/app/src/main/java/code/name/monkey/retromusic/activities/MainActivity.kt index 4f96a696..59b59bf1 100644 --- a/app/src/main/java/code/name/monkey/retromusic/activities/MainActivity.kt +++ b/app/src/main/java/code/name/monkey/retromusic/activities/MainActivity.kt @@ -21,10 +21,12 @@ import android.net.Uri import android.os.Bundle import android.provider.MediaStore import androidx.lifecycle.lifecycleScope +import androidx.navigation.contains import androidx.navigation.ui.setupWithNavController import code.name.monkey.retromusic.* import code.name.monkey.retromusic.activities.base.AbsCastActivity import code.name.monkey.retromusic.databinding.SlidingMusicPanelLayoutBinding +import code.name.monkey.retromusic.extensions.currentFragment import code.name.monkey.retromusic.extensions.extra import code.name.monkey.retromusic.extensions.findNavController import code.name.monkey.retromusic.fragments.base.AbsRecyclerViewFragment @@ -54,9 +56,6 @@ class MainActivity : AbsCastActivity(), OnSharedPreferenceChangeListener { override fun onCreate(savedInstanceState: Bundle?) { setDrawUnderStatusBar() super.onCreate(savedInstanceState) - setStatusbarColorAuto() - setNavigationbarColorAuto() - setLightNavigationBar(true) setTaskDescriptionColorAuto() hideStatusBar() updateTabs() @@ -75,6 +74,8 @@ class MainActivity : AbsCastActivity(), OnSharedPreferenceChangeListener { val categoryInfo: CategoryInfo = PreferenceUtil.libraryCategory.first { it.visible } if (categoryInfo.visible) { + if (!navGraph.contains(PreferenceUtil.lastTab)) PreferenceUtil.lastTab = + categoryInfo.category.id navGraph.setStartDestination( if (PreferenceUtil.rememberLastTab) { PreferenceUtil.lastTab.let { @@ -88,10 +89,10 @@ class MainActivity : AbsCastActivity(), OnSharedPreferenceChangeListener { ) } navController.graph = navGraph - getBottomNavigationView().setupWithNavController(navController) + bottomNavigationView.setupWithNavController(navController) // Scroll Fragment to top - getBottomNavigationView().setOnItemReselectedListener { - supportFragmentManager.findFragmentById(R.id.fragment_container)?.childFragmentManager?.fragments?.get(0) + bottomNavigationView.setOnItemReselectedListener { + currentFragment(R.id.fragment_container) .also { if (it is AbsRecyclerViewFragment<*, *>) { it.scrollToTop() @@ -101,19 +102,25 @@ class MainActivity : AbsCastActivity(), OnSharedPreferenceChangeListener { } } } + // This is more like a work-around as for start destination of navGraph + // enterTransition won't work as expected + navGraph.setStartDestination(R.id.libraryFragment) navController.addOnDestinationChangedListener { _, destination, _ -> - when (destination.id) { + when (destination.id) { R.id.action_home, R.id.action_song, R.id.action_album, R.id.action_artist, R.id.action_folder, R.id.action_playlist, R.id.action_genre -> { // Save the last tab if (PreferenceUtil.rememberLastTab) { saveTab(destination.id) } // Show Bottom Navigation Bar - setBottomBarVisibility(true) + setBottomNavVisibility(visible = true, animate = true) } - else -> setBottomBarVisibility(false) // Hide Bottom Navigation Bar + R.id.playing_queue_fragment -> { + setBottomNavVisibility(visible = false) + hideBottomSheet(true) + } + else -> setBottomNavVisibility(visible = false, animate = true) // Hide Bottom Navigation Bar } - } } @@ -129,7 +136,7 @@ class MainActivity : AbsCastActivity(), OnSharedPreferenceChangeListener { PreferenceUtil.registerOnSharedPreferenceChangedListener(this) val expand = extra(EXPAND_PANEL).value ?: false if (expand && PreferenceUtil.isExpandPanel) { - setBottomBarVisibility(false) + setBottomNavVisibility(false) fromNotification = true expandPanel() intent.removeExtra(EXPAND_PANEL) @@ -142,7 +149,7 @@ class MainActivity : AbsCastActivity(), OnSharedPreferenceChangeListener { } override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences?, key: String?) { - if (key == GENERAL_THEME || key == BLACK_THEME || key == ADAPTIVE_COLOR_APP || key == USER_NAME || key == TOGGLE_FULL_SCREEN || key == TOGGLE_VOLUME || key == ROUND_CORNERS || key == CAROUSEL_EFFECT || key == NOW_PLAYING_SCREEN_ID || key == TOGGLE_GENRE || key == BANNER_IMAGE_PATH || key == PROFILE_IMAGE_PATH || key == CIRCULAR_ALBUM_ART || key == KEEP_SCREEN_ON || key == TOGGLE_SEPARATE_LINE || key == TOGGLE_HOME_BANNER || key == TOGGLE_ADD_CONTROLS || key == ALBUM_COVER_STYLE || key == HOME_ARTIST_GRID_STYLE || key == ALBUM_COVER_TRANSFORM || key == DESATURATED_COLOR || key == EXTRA_SONG_INFO || key == TAB_TEXT_MODE || key == LANGUAGE_NAME || key == LIBRARY_CATEGORIES) { + if (key == GENERAL_THEME || key == MATERIAL_YOU || key == BLACK_THEME || key == ADAPTIVE_COLOR_APP || key == USER_NAME || key == TOGGLE_FULL_SCREEN || key == TOGGLE_VOLUME || key == ROUND_CORNERS || key == CAROUSEL_EFFECT || key == NOW_PLAYING_SCREEN_ID || key == TOGGLE_GENRE || key == BANNER_IMAGE_PATH || key == PROFILE_IMAGE_PATH || key == CIRCULAR_ALBUM_ART || key == KEEP_SCREEN_ON || key == TOGGLE_SEPARATE_LINE || key == TOGGLE_HOME_BANNER || key == TOGGLE_ADD_CONTROLS || key == ALBUM_COVER_STYLE || key == HOME_ARTIST_GRID_STYLE || key == ALBUM_COVER_TRANSFORM || key == DESATURATED_COLOR || key == EXTRA_SONG_INFO || key == TAB_TEXT_MODE || key == LANGUAGE_NAME || key == LIBRARY_CATEGORIES) { postRecreate() } } diff --git a/app/src/main/java/code/name/monkey/retromusic/activities/PermissionActivity.kt b/app/src/main/java/code/name/monkey/retromusic/activities/PermissionActivity.kt index f61eb518..34d4a50c 100644 --- a/app/src/main/java/code/name/monkey/retromusic/activities/PermissionActivity.kt +++ b/app/src/main/java/code/name/monkey/retromusic/activities/PermissionActivity.kt @@ -41,8 +41,6 @@ class PermissionActivity : AbsMusicServiceActivity() { binding = ActivityPermissionBinding.inflate(layoutInflater) setContentView(binding.root) setStatusbarColorAuto() - setNavigationbarColorAuto() - setLightNavigationBar(true) setTaskDescriptionColorAuto() setupTitle() diff --git a/app/src/main/java/code/name/monkey/retromusic/activities/PlayingQueueActivity.kt b/app/src/main/java/code/name/monkey/retromusic/activities/PlayingQueueActivity.kt deleted file mode 100644 index 70b45c24..00000000 --- a/app/src/main/java/code/name/monkey/retromusic/activities/PlayingQueueActivity.kt +++ /dev/null @@ -1,204 +0,0 @@ -/* - * Copyright (c) 2020 Hemanth Savarla. - * - * Licensed under the GNU General Public License v3 - * - * This is free software: you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at your option) any later version. - * - * This software is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU General Public License for more details. - * - */ -package code.name.monkey.retromusic.activities - -import android.content.res.ColorStateList -import android.os.Bundle -import android.view.MenuItem -import androidx.recyclerview.widget.LinearLayoutManager -import androidx.recyclerview.widget.RecyclerView -import code.name.monkey.appthemehelper.util.ColorUtil -import code.name.monkey.appthemehelper.util.MaterialValueHelper -import code.name.monkey.retromusic.R -import code.name.monkey.retromusic.activities.base.AbsMusicServiceActivity -import code.name.monkey.retromusic.adapter.song.PlayingQueueAdapter -import code.name.monkey.retromusic.databinding.ActivityPlayingQueueBinding -import code.name.monkey.retromusic.extensions.accentColor -import code.name.monkey.retromusic.extensions.surfaceColor -import code.name.monkey.retromusic.helper.MusicPlayerRemote -import code.name.monkey.retromusic.util.MusicUtil -import code.name.monkey.retromusic.util.ThemedFastScroller -import com.h6ah4i.android.widget.advrecyclerview.animator.DraggableItemAnimator -import com.h6ah4i.android.widget.advrecyclerview.draggable.RecyclerViewDragDropManager -import com.h6ah4i.android.widget.advrecyclerview.swipeable.RecyclerViewSwipeManager -import com.h6ah4i.android.widget.advrecyclerview.touchguard.RecyclerViewTouchActionGuardManager -import com.h6ah4i.android.widget.advrecyclerview.utils.WrapperAdapterUtils - -open class PlayingQueueActivity : AbsMusicServiceActivity() { - - private lateinit var binding: ActivityPlayingQueueBinding - private var wrappedAdapter: RecyclerView.Adapter<*>? = null - private var recyclerViewDragDropManager: RecyclerViewDragDropManager? = null - private var recyclerViewSwipeManager: RecyclerViewSwipeManager? = null - private var recyclerViewTouchActionGuardManager: RecyclerViewTouchActionGuardManager? = null - private var playingQueueAdapter: PlayingQueueAdapter? = null - private lateinit var linearLayoutManager: LinearLayoutManager - - private fun getUpNextAndQueueTime(): String { - val duration = MusicPlayerRemote.getQueueDurationMillis(MusicPlayerRemote.position) - return MusicUtil.buildInfoString( - resources.getString(R.string.up_next), - MusicUtil.getReadableDurationString(duration) - ) - } - - override fun onCreate(savedInstanceState: Bundle?) { - setDrawUnderStatusBar() - super.onCreate(savedInstanceState) - binding = ActivityPlayingQueueBinding.inflate(layoutInflater) - setContentView(binding.root) - setStatusbarColorAuto() - setNavigationbarColorAuto() - setTaskDescriptionColorAuto() - setLightNavigationBar(true) - - setupToolbar() - setUpRecyclerView() - - binding.clearQueue.setOnClickListener { - MusicPlayerRemote.clearQueue() - } - checkForPadding() - } - - override fun onOptionsItemSelected(item: MenuItem): Boolean { - return when (item.itemId) { - android.R.id.home -> { - onBackPressed() - true - } - else -> super.onOptionsItemSelected(item) - } - } - - private fun setUpRecyclerView() { - recyclerViewTouchActionGuardManager = RecyclerViewTouchActionGuardManager() - recyclerViewDragDropManager = RecyclerViewDragDropManager() - recyclerViewSwipeManager = RecyclerViewSwipeManager() - - val animator = DraggableItemAnimator() - animator.supportsChangeAnimations = false - - playingQueueAdapter = PlayingQueueAdapter( - this, - MusicPlayerRemote.playingQueue.toMutableList(), - MusicPlayerRemote.position, - R.layout.item_queue - ) - wrappedAdapter = recyclerViewDragDropManager?.createWrappedAdapter(playingQueueAdapter!!) - wrappedAdapter = wrappedAdapter?.let { recyclerViewSwipeManager?.createWrappedAdapter(it) } - - linearLayoutManager = LinearLayoutManager(this) - - binding.recyclerView.layoutManager = linearLayoutManager - binding.recyclerView.adapter = wrappedAdapter - binding.recyclerView.itemAnimator = animator - recyclerViewTouchActionGuardManager?.attachRecyclerView(binding.recyclerView) - recyclerViewDragDropManager?.attachRecyclerView(binding.recyclerView) - recyclerViewSwipeManager?.attachRecyclerView(binding.recyclerView) - linearLayoutManager.scrollToPositionWithOffset(MusicPlayerRemote.position + 1, 0) - - binding.recyclerView.addOnScrollListener(object : RecyclerView.OnScrollListener() { - override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) { - super.onScrolled(recyclerView, dx, dy) - if (dy > 0) { - binding.clearQueue.shrink() - } else if (dy < 0) { - binding.clearQueue.extend() - } - } - }) - ThemedFastScroller.create(binding.recyclerView) - } - - private fun checkForPadding() { - } - - override fun onQueueChanged() { - if (MusicPlayerRemote.playingQueue.isEmpty()) { - finish() - return - } - checkForPadding() - updateQueue() - updateCurrentSong() - } - - override fun onMediaStoreChanged() { - updateQueue() - updateCurrentSong() - } - - private fun updateCurrentSong() { - binding.toolbar.subtitle = getUpNextAndQueueTime() - } - - override fun onPlayingMetaChanged() { - updateQueuePosition() - } - - private fun updateQueuePosition() { - playingQueueAdapter?.setCurrent(MusicPlayerRemote.position) - resetToCurrentPosition() - binding.toolbar.subtitle = getUpNextAndQueueTime() - } - - private fun updateQueue() { - playingQueueAdapter?.swapDataSet(MusicPlayerRemote.playingQueue, MusicPlayerRemote.position) - resetToCurrentPosition() - } - - private fun resetToCurrentPosition() { - binding.recyclerView.stopScroll() - linearLayoutManager.scrollToPositionWithOffset(MusicPlayerRemote.position + 1, 0) - } - - override fun onPause() { - if (recyclerViewDragDropManager != null) { - recyclerViewDragDropManager!!.cancelDrag() - } - super.onPause() - } - - override fun onDestroy() { - if (recyclerViewDragDropManager != null) { - recyclerViewDragDropManager!!.release() - recyclerViewDragDropManager = null - } - if (recyclerViewSwipeManager != null) { - recyclerViewSwipeManager?.release() - recyclerViewSwipeManager = null - } - if (wrappedAdapter != null) { - WrapperAdapterUtils.releaseAll(wrappedAdapter) - wrappedAdapter = null - } - playingQueueAdapter = null - super.onDestroy() - } - - private fun setupToolbar() { - binding.toolbar.subtitle = getUpNextAndQueueTime() - binding.toolbar.setBackgroundColor(surfaceColor()) - setSupportActionBar(binding.toolbar) - binding.clearQueue.backgroundTintList = ColorStateList.valueOf(accentColor()) - ColorStateList.valueOf( - MaterialValueHelper.getPrimaryTextColor(this, ColorUtil.isColorLight(accentColor())) - ).apply { - binding.clearQueue.setTextColor(this) - binding.clearQueue.iconTint = this - } - } -} diff --git a/app/src/main/java/code/name/monkey/retromusic/activities/PurchaseActivity.kt b/app/src/main/java/code/name/monkey/retromusic/activities/PurchaseActivity.kt index c611d2f2..1550ee3c 100644 --- a/app/src/main/java/code/name/monkey/retromusic/activities/PurchaseActivity.kt +++ b/app/src/main/java/code/name/monkey/retromusic/activities/PurchaseActivity.kt @@ -14,10 +14,8 @@ */ package code.name.monkey.retromusic.activities -import android.content.Intent import android.content.res.ColorStateList import android.graphics.Color -import android.os.AsyncTask import android.os.Bundle import android.util.Log import android.view.MenuItem @@ -31,14 +29,12 @@ import code.name.monkey.retromusic.R import code.name.monkey.retromusic.activities.base.AbsBaseActivity import code.name.monkey.retromusic.databinding.ActivityProVersionBinding import com.anjlab.android.iab.v3.BillingProcessor -import com.anjlab.android.iab.v3.TransactionDetails -import java.lang.ref.WeakReference +import com.anjlab.android.iab.v3.PurchaseInfo class PurchaseActivity : AbsBaseActivity(), BillingProcessor.IBillingHandler { private lateinit var binding: ActivityProVersionBinding private lateinit var billingProcessor: BillingProcessor - private var restorePurchaseAsyncTask: AsyncTask<*, *, *>? = null override fun onCreate(savedInstanceState: Bundle?) { setDrawUnderStatusBar() @@ -47,8 +43,6 @@ class PurchaseActivity : AbsBaseActivity(), BillingProcessor.IBillingHandler { setContentView(binding.root) setStatusbarColor(Color.TRANSPARENT) setLightStatusbar(false) - setNavigationbarColor(Color.BLACK) - setLightNavigationBar(false) binding.toolbar.navigationIcon?.setTint(Color.WHITE) binding.toolbar.setNavigationOnClickListener { onBackPressed() } @@ -60,9 +54,7 @@ class PurchaseActivity : AbsBaseActivity(), BillingProcessor.IBillingHandler { MaterialUtil.setTint(binding.purchaseButton, true) binding.restoreButton.setOnClickListener { - if (restorePurchaseAsyncTask == null || restorePurchaseAsyncTask!!.status != AsyncTask.Status.RUNNING) { - restorePurchase() - } + restorePurchase() } binding.purchaseButton.setOnClickListener { billingProcessor.purchase(this@PurchaseActivity, PRO_VERSION_PRODUCT_ID) @@ -72,13 +64,25 @@ class PurchaseActivity : AbsBaseActivity(), BillingProcessor.IBillingHandler { } private fun restorePurchase() { - if (restorePurchaseAsyncTask != null) { - restorePurchaseAsyncTask!!.cancel(false) - } - restorePurchaseAsyncTask = RestorePurchaseAsyncTask(this).execute() + Toast.makeText(this, R.string.restoring_purchase, Toast.LENGTH_SHORT) + .show() + billingProcessor.loadOwnedPurchasesFromGoogleAsync(object : + BillingProcessor.IPurchasesResponseListener { + override fun onPurchasesSuccess() { + onPurchaseHistoryRestored() + } + + override fun onPurchasesError() { + Toast.makeText( + this@PurchaseActivity, + R.string.could_not_restore_purchase, + Toast.LENGTH_SHORT + ).show() + } + }) } - override fun onProductPurchased(productId: String, details: TransactionDetails?) { + override fun onProductPurchased(productId: String, details: PurchaseInfo?) { Toast.makeText(this, R.string.thank_you, Toast.LENGTH_SHORT).show() setResult(RESULT_OK) } @@ -105,12 +109,6 @@ class PurchaseActivity : AbsBaseActivity(), BillingProcessor.IBillingHandler { binding.purchaseButton.isEnabled = true } - public override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { - if (!billingProcessor.handleActivityResult(requestCode, resultCode, data)) { - super.onActivityResult(requestCode, resultCode, data) - } - } - override fun onOptionsItemSelected(item: MenuItem): Boolean { when (item.itemId) { android.R.id.home -> finish() @@ -123,52 +121,6 @@ class PurchaseActivity : AbsBaseActivity(), BillingProcessor.IBillingHandler { super.onDestroy() } - private class RestorePurchaseAsyncTask(purchaseActivity: PurchaseActivity) : - AsyncTask() { - - private val buyActivityWeakReference: WeakReference = WeakReference( - purchaseActivity - ) - - override fun onPreExecute() { - super.onPreExecute() - val purchaseActivity = buyActivityWeakReference.get() - if (purchaseActivity != null) { - Toast.makeText(purchaseActivity, R.string.restoring_purchase, Toast.LENGTH_SHORT) - .show() - } else { - cancel(false) - } - } - - override fun doInBackground(vararg params: Void): Boolean? { - val purchaseActivity = buyActivityWeakReference.get() - if (purchaseActivity != null) { - return purchaseActivity.billingProcessor.loadOwnedPurchasesFromGoogle() - } - cancel(false) - return null - } - - override fun onPostExecute(b: Boolean?) { - super.onPostExecute(b) - val purchaseActivity = buyActivityWeakReference.get() - if (purchaseActivity == null || b == null) { - return - } - - if (b) { - purchaseActivity.onPurchaseHistoryRestored() - } else { - Toast.makeText( - purchaseActivity, - R.string.could_not_restore_purchase, - Toast.LENGTH_SHORT - ).show() - } - } - } - companion object { private const val TAG: String = "PurchaseActivity" } diff --git a/app/src/main/java/code/name/monkey/retromusic/activities/SettingsActivity.kt b/app/src/main/java/code/name/monkey/retromusic/activities/SettingsActivity.kt index 54d31bb4..b9a233bb 100644 --- a/app/src/main/java/code/name/monkey/retromusic/activities/SettingsActivity.kt +++ b/app/src/main/java/code/name/monkey/retromusic/activities/SettingsActivity.kt @@ -14,6 +14,7 @@ */ package code.name.monkey.retromusic.activities +import android.content.Intent import android.os.Bundle import android.view.MenuItem import androidx.navigation.NavController @@ -21,33 +22,33 @@ import androidx.navigation.NavDestination import code.name.monkey.appthemehelper.ThemeStore import code.name.monkey.appthemehelper.util.VersionUtils import code.name.monkey.retromusic.R -import code.name.monkey.retromusic.activities.base.AbsBaseActivity +import code.name.monkey.retromusic.activities.base.AbsThemeActivity import code.name.monkey.retromusic.appshortcuts.DynamicShortcutManager import code.name.monkey.retromusic.databinding.ActivitySettingsBinding import code.name.monkey.retromusic.extensions.applyToolbar +import code.name.monkey.retromusic.extensions.extra import code.name.monkey.retromusic.extensions.findNavController +import code.name.monkey.retromusic.extensions.surfaceColor import com.afollestad.materialdialogs.MaterialDialog import com.afollestad.materialdialogs.color.ColorCallback -class SettingsActivity : AbsBaseActivity(), ColorCallback { +class SettingsActivity : AbsThemeActivity(), ColorCallback, OnThemeChangedListener { private lateinit var binding: ActivitySettingsBinding override fun onCreate(savedInstanceState: Bundle?) { setDrawUnderStatusBar() - super.onCreate(savedInstanceState) + val mSavedInstanceState = extra(TAG).value ?: savedInstanceState + super.onCreate(mSavedInstanceState) + setLightStatusbarAuto(surfaceColor()) binding = ActivitySettingsBinding.inflate(layoutInflater) setContentView(binding.root) - setStatusbarColorAuto() - setNavigationbarColorAuto() - setLightNavigationBar(true) setupToolbar() } private fun setupToolbar() { - setTitle(R.string.action_settings) applyToolbar(binding.toolbar) val navController: NavController = findNavController(R.id.contentFrame) navController.addOnDestinationChangedListener { _, _, _ -> - binding.toolbar.title = + binding.collapsingToolbarLayout.title = navController.currentDestination?.let { getStringFromDestination(it) } } } @@ -63,6 +64,7 @@ class SettingsActivity : AbsBaseActivity(), ColorCallback { R.id.personalizeSettingsFragment -> R.string.personalize R.id.themeSettingsFragment -> R.string.general_settings_title R.id.aboutActivity -> R.string.action_about + R.id.backup_fragment -> R.string.backup_restore_title else -> R.id.action_settings } return getString(idRes) @@ -84,6 +86,28 @@ class SettingsActivity : AbsBaseActivity(), ColorCallback { if (VersionUtils.hasNougatMR()) DynamicShortcutManager(this).updateDynamicShortcuts() - recreate() + restart() + } + + override fun onThemeValuesChanged() { + restart() + } + + private fun restart() { + val savedInstanceState = Bundle().apply { + onSaveInstanceState(this) + } + finish() + val intent = Intent(this, this::class.java).putExtra(TAG, savedInstanceState) + startActivity(intent) + overridePendingTransition(android.R.anim.fade_in, android.R.anim.fade_out) + } + + companion object { + val TAG: String = SettingsActivity::class.java.simpleName } } + +interface OnThemeChangedListener { + fun onThemeValuesChanged() +} diff --git a/app/src/main/java/code/name/monkey/retromusic/activities/ShareInstagramStory.kt b/app/src/main/java/code/name/monkey/retromusic/activities/ShareInstagramStory.kt index b3d31052..a0a71973 100644 --- a/app/src/main/java/code/name/monkey/retromusic/activities/ShareInstagramStory.kt +++ b/app/src/main/java/code/name/monkey/retromusic/activities/ShareInstagramStory.kt @@ -61,7 +61,6 @@ class ShareInstagramStory : AbsBaseActivity() { binding = ActivityShareInstagramBinding.inflate(layoutInflater) setContentView(binding.root) setStatusbarColor(Color.TRANSPARENT) - setNavigationbarColor(Color.BLACK) binding.toolbar.setBackgroundColor(Color.TRANSPARENT) setSupportActionBar(binding.toolbar) diff --git a/app/src/main/java/code/name/monkey/retromusic/activities/SupportDevelopmentActivity.kt b/app/src/main/java/code/name/monkey/retromusic/activities/SupportDevelopmentActivity.kt index 58cddd71..128746fb 100644 --- a/app/src/main/java/code/name/monkey/retromusic/activities/SupportDevelopmentActivity.kt +++ b/app/src/main/java/code/name/monkey/retromusic/activities/SupportDevelopmentActivity.kt @@ -14,9 +14,7 @@ */ package code.name.monkey.retromusic.activities -import android.content.Intent import android.graphics.Paint -import android.os.AsyncTask import android.os.Bundle import android.util.Log import android.view.LayoutInflater @@ -27,6 +25,7 @@ import android.widget.TextView import android.widget.Toast import androidx.annotation.LayoutRes import androidx.appcompat.widget.AppCompatImageView +import androidx.core.view.isVisible import androidx.recyclerview.widget.DefaultItemAnimator import androidx.recyclerview.widget.GridLayoutManager import androidx.recyclerview.widget.RecyclerView @@ -41,9 +40,8 @@ import code.name.monkey.retromusic.databinding.ActivityDonationBinding import code.name.monkey.retromusic.extensions.textColorPrimary import code.name.monkey.retromusic.extensions.textColorSecondary import com.anjlab.android.iab.v3.BillingProcessor +import com.anjlab.android.iab.v3.PurchaseInfo import com.anjlab.android.iab.v3.SkuDetails -import com.anjlab.android.iab.v3.TransactionDetails -import java.lang.ref.WeakReference import java.util.* class SupportDevelopmentActivity : AbsBaseActivity(), BillingProcessor.IBillingHandler { @@ -53,11 +51,9 @@ class SupportDevelopmentActivity : AbsBaseActivity(), BillingProcessor.IBillingH companion object { val TAG: String = SupportDevelopmentActivity::class.java.simpleName const val DONATION_PRODUCT_IDS = R.array.donation_ids - private const val TEZ_REQUEST_CODE = 123 } var billingProcessor: BillingProcessor? = null - private var skuDetailsLoadAsyncTask: AsyncTask<*, *, *>? = null override fun onOptionsItemSelected(item: MenuItem): Boolean { if (item.itemId == android.R.id.home) { @@ -75,12 +71,10 @@ class SupportDevelopmentActivity : AbsBaseActivity(), BillingProcessor.IBillingH override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) binding = ActivityDonationBinding.inflate(layoutInflater) - setContentView(R.layout.activity_donation) + setContentView(binding.root) setStatusbarColorAuto() - setNavigationbarColorAuto() setTaskDescriptionColorAuto() - setLightNavigationBar(true) setupToolbar() @@ -101,13 +95,35 @@ class SupportDevelopmentActivity : AbsBaseActivity(), BillingProcessor.IBillingH } private fun loadSkuDetails() { - if (skuDetailsLoadAsyncTask != null) { - skuDetailsLoadAsyncTask!!.cancel(false) - } - skuDetailsLoadAsyncTask = SkuDetailsLoadAsyncTask(this).execute() + binding.progressContainer.isVisible = true + binding.recyclerView.isVisible = false + val ids = + resources.getStringArray(DONATION_PRODUCT_IDS) + billingProcessor!!.getPurchaseListingDetailsAsync( + ArrayList(listOf(*ids)), + object : BillingProcessor.ISkuDetailsResponseListener { + override fun onSkuDetailsResponse(skuDetails: MutableList?) { + if (skuDetails == null || skuDetails.isEmpty()) { + binding.progressContainer.isVisible = false + return + } + + binding.progressContainer.isVisible = false + binding.recyclerView.apply { + itemAnimator = DefaultItemAnimator() + layoutManager = GridLayoutManager(this@SupportDevelopmentActivity, 2) + adapter = SkuDetailsAdapter(this@SupportDevelopmentActivity, skuDetails) + isVisible = true + } + } + + override fun onSkuDetailsError(error: String?) { + Log.e(TAG, error.toString()) + } + }) } - override fun onProductPurchased(productId: String, details: TransactionDetails?) { + override fun onProductPurchased(productId: String, details: PurchaseInfo?) { // loadSkuDetails(); Toast.makeText(this, R.string.thank_you, Toast.LENGTH_SHORT).show() } @@ -121,68 +137,12 @@ class SupportDevelopmentActivity : AbsBaseActivity(), BillingProcessor.IBillingH Toast.makeText(this, R.string.restored_previous_purchases, Toast.LENGTH_SHORT).show() } - override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { - if (!billingProcessor!!.handleActivityResult(requestCode, resultCode, data)) { - super.onActivityResult(requestCode, resultCode, data) - } - if (requestCode == TEZ_REQUEST_CODE) { - // Process based on the data in response. - // Log.d("result", data!!.getStringExtra("Status")) - } - } - override fun onDestroy() { billingProcessor?.release() - skuDetailsLoadAsyncTask?.cancel(true) super.onDestroy() } } -private class SkuDetailsLoadAsyncTask(supportDevelopmentActivity: SupportDevelopmentActivity) : - AsyncTask>() { - - private val weakReference: WeakReference = WeakReference( - supportDevelopmentActivity - ) - - override fun onPreExecute() { - super.onPreExecute() - val supportDevelopmentActivity = weakReference.get() ?: return - - supportDevelopmentActivity.binding.progressContainer.visibility = View.VISIBLE - supportDevelopmentActivity.binding.recyclerView.visibility = View.GONE - } - - override fun doInBackground(vararg params: Void): List? { - val dialog = weakReference.get() - if (dialog != null) { - val ids = - dialog.resources.getStringArray(SupportDevelopmentActivity.DONATION_PRODUCT_IDS) - return dialog.billingProcessor!!.getPurchaseListingDetails(ArrayList(Arrays.asList(*ids))) - } - cancel(false) - return null - } - - override fun onPostExecute(skuDetails: List?) { - super.onPostExecute(skuDetails) - val dialog = weakReference.get() ?: return - - if (skuDetails == null || skuDetails.isEmpty()) { - dialog.binding.progressContainer.visibility = View.GONE - return - } - - dialog.binding.progressContainer.visibility = View.GONE - dialog.binding.recyclerView.apply { - itemAnimator = DefaultItemAnimator() - layoutManager = GridLayoutManager(dialog, 2) - adapter = SkuDetailsAdapter(dialog, skuDetails) - visibility = View.VISIBLE - } - } -} - class SkuDetailsAdapter( private var donationsDialog: SupportDevelopmentActivity, objects: List @@ -223,7 +183,7 @@ class SkuDetailsAdapter( viewHolder.title.text = skuDetails.title.replace("Music Player - MP3 Player - Retro", "") .trim { it <= ' ' } viewHolder.text.text = skuDetails.description - viewHolder.text.visibility = View.GONE + viewHolder.text.isVisible = false viewHolder.price.text = skuDetails.priceText viewHolder.image.setImageResource(getIcon(i)) diff --git a/app/src/main/java/code/name/monkey/retromusic/activities/WhatsNewActivity.java b/app/src/main/java/code/name/monkey/retromusic/activities/WhatsNewActivity.java deleted file mode 100644 index 9e37a8ee..00000000 --- a/app/src/main/java/code/name/monkey/retromusic/activities/WhatsNewActivity.java +++ /dev/null @@ -1,128 +0,0 @@ -package code.name.monkey.retromusic.activities; - -import android.content.Context; -import android.content.pm.PackageInfo; -import android.content.pm.PackageManager; -import android.graphics.Color; -import android.os.Bundle; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.core.widget.NestedScrollView; - -import java.io.BufferedReader; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.nio.charset.StandardCharsets; -import java.util.Locale; - -import code.name.monkey.appthemehelper.ThemeStore; -import code.name.monkey.appthemehelper.util.ATHUtil; -import code.name.monkey.appthemehelper.util.ColorUtil; -import code.name.monkey.appthemehelper.util.MaterialValueHelper; -import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper; -import code.name.monkey.retromusic.Constants; -import code.name.monkey.retromusic.R; -import code.name.monkey.retromusic.activities.base.AbsBaseActivity; -import code.name.monkey.retromusic.databinding.ActivityWhatsNewBinding; -import code.name.monkey.retromusic.extensions.ColorExtKt; -import code.name.monkey.retromusic.util.PreferenceUtil; -import code.name.monkey.retromusic.util.RetroUtil; - -public class WhatsNewActivity extends AbsBaseActivity { - - private static String colorToCSS(int color) { - return String.format( - Locale.getDefault(), - "rgba(%d, %d, %d, %d)", - Color.red(color), - Color.green(color), - Color.blue(color), - Color.alpha(color)); // on API 29, WebView doesn't load with hex colors - } - - private static void setChangelogRead(@NonNull Context context) { - try { - PackageInfo pInfo = context.getPackageManager().getPackageInfo(context.getPackageName(), 0); - int currentVersion = pInfo.versionCode; - PreferenceUtil.INSTANCE.setLastVersion(currentVersion); - } catch (PackageManager.NameNotFoundException e) { - e.printStackTrace(); - } - } - - @Override - protected void onCreate(@Nullable Bundle savedInstanceState) { - setDrawUnderStatusBar(); - super.onCreate(savedInstanceState); - ActivityWhatsNewBinding binding = ActivityWhatsNewBinding.inflate(getLayoutInflater()); - setContentView(binding.getRoot()); - setStatusbarColorAuto(); - setNavigationbarColorAuto(); - setTaskDescriptionColorAuto(); - - binding.toolbar.setBackgroundColor(ATHUtil.INSTANCE.resolveColor(this, R.attr.colorSurface)); - binding.toolbar.setNavigationOnClickListener(v -> onBackPressed()); - ToolbarContentTintHelper.colorBackButton(binding.toolbar); - - try { - StringBuilder buf = new StringBuilder(); - InputStream json = getAssets().open("retro-changelog.html"); - BufferedReader in = new BufferedReader(new InputStreamReader(json, StandardCharsets.UTF_8)); - String str; - while ((str = in.readLine()) != null) { - buf.append(str); - } - in.close(); - - // Inject color values for WebView body background and links - final boolean isDark = ATHUtil.INSTANCE.isWindowBackgroundDark(this); - final int accentColor = ThemeStore.Companion.accentColor(this); - final String backgroundColor = - colorToCSS( - ATHUtil.INSTANCE.resolveColor( - this, R.attr.colorSurface, Color.parseColor(isDark ? "#424242" : "#ffffff"))); - final String contentColor = colorToCSS(Color.parseColor(isDark ? "#ffffff" : "#000000")); - final String textColor = colorToCSS(Color.parseColor(isDark ? "#60FFFFFF" : "#80000000")); - final String accentColorString = colorToCSS(ThemeStore.Companion.accentColor(this)); - final String cardBackgroundColor = colorToCSS(Color.parseColor(isDark ? "#353535" : "#ffffff")); - final String accentTextColor = - colorToCSS( - MaterialValueHelper.getPrimaryTextColor( - this, ColorUtil.INSTANCE.isColorLight(accentColor))); - final String changeLog = - buf.toString() - .replace( - "{style-placeholder}", - String.format( - "body { background-color: %s; color: %s; } li {color: %s;} h3 {color: %s;} .tag {color: %s;} div{background-color: %s;}", - backgroundColor, - contentColor, - textColor, - accentColorString, - accentColorString, - cardBackgroundColor)) - .replace("{link-color}", colorToCSS(ThemeStore.Companion.accentColor(this))) - .replace( - "{link-color-active}", - colorToCSS( - ColorUtil.INSTANCE.lightenColor(ThemeStore.Companion.accentColor(this)))); - binding.webView.loadData(changeLog, "text/html", "UTF-8"); - } catch (Throwable e) { - binding.webView.loadData( - "

Unable to load

" + e.getLocalizedMessage() + "

", "text/html", "UTF-8"); - } - setChangelogRead(this); - binding.tgFab.setOnClickListener(v -> RetroUtil.openUrl(this, Constants.TELEGRAM_CHANGE_LOG)); - ColorExtKt.accentColor(binding.tgFab); - binding.tgFab.shrink(); - binding.container.setOnScrollChangeListener((NestedScrollView.OnScrollChangeListener) (v, scrollX, scrollY, oldScrollX, oldScrollY) -> { - int dy = scrollY - oldScrollY; - if (dy > 0) { - binding.tgFab.shrink(); - } else if (dy < 0) { - binding.tgFab.extend(); - } - }); - } -} diff --git a/app/src/main/java/code/name/monkey/retromusic/activities/WhatsNewActivity.kt b/app/src/main/java/code/name/monkey/retromusic/activities/WhatsNewActivity.kt new file mode 100644 index 00000000..a8b05999 --- /dev/null +++ b/app/src/main/java/code/name/monkey/retromusic/activities/WhatsNewActivity.kt @@ -0,0 +1,125 @@ +package code.name.monkey.retromusic.activities + +import android.content.Context +import android.content.pm.PackageManager +import android.graphics.Color +import android.os.Bundle +import androidx.core.widget.NestedScrollView +import code.name.monkey.appthemehelper.ThemeStore.Companion.accentColor +import code.name.monkey.appthemehelper.util.ATHUtil.isWindowBackgroundDark +import code.name.monkey.appthemehelper.util.ATHUtil.resolveColor +import code.name.monkey.appthemehelper.util.ColorUtil.isColorLight +import code.name.monkey.appthemehelper.util.ColorUtil.lightenColor +import code.name.monkey.appthemehelper.util.MaterialValueHelper.getPrimaryTextColor +import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper +import code.name.monkey.retromusic.Constants +import code.name.monkey.retromusic.R +import code.name.monkey.retromusic.activities.base.AbsThemeActivity +import code.name.monkey.retromusic.databinding.ActivityWhatsNewBinding +import code.name.monkey.retromusic.extensions.accentColor +import code.name.monkey.retromusic.util.PreferenceUtil.lastVersion +import code.name.monkey.retromusic.util.RetroUtil +import java.io.BufferedReader +import java.io.InputStreamReader +import java.nio.charset.StandardCharsets +import java.util.* + +class WhatsNewActivity : AbsThemeActivity() { + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + val binding = ActivityWhatsNewBinding.inflate(layoutInflater) + setContentView(binding.root) + setLightStatusbarAuto(resolveColor(this, R.attr.colorSurface)) + setTaskDescriptionColorAuto() + binding.toolbar.setNavigationOnClickListener { onBackPressed() } + ToolbarContentTintHelper.colorBackButton(binding.toolbar) + try { + val buf = StringBuilder() + val json = assets.open("retro-changelog.html") + val br = BufferedReader(InputStreamReader(json, StandardCharsets.UTF_8)) + var str: String? + while (br.readLine().also { str = it } != null) { + buf.append(str) + } + br.close() + + // Inject color values for WebView body background and links + val isDark = isWindowBackgroundDark(this) + val accentColor = accentColor(this) + val backgroundColor = colorToCSS( + resolveColor( + this, + R.attr.colorSurface, + Color.parseColor(if (isDark) "#424242" else "#ffffff") + ) + ) + val contentColor = colorToCSS(Color.parseColor(if (isDark) "#ffffff" else "#000000")) + val textColor = colorToCSS(Color.parseColor(if (isDark) "#60FFFFFF" else "#80000000")) + val accentColorString = colorToCSS(accentColor(this)) + val cardBackgroundColor = + colorToCSS(Color.parseColor(if (isDark) "#353535" else "#ffffff")) + val accentTextColor = colorToCSS( + getPrimaryTextColor( + this, isColorLight(accentColor) + ) + ) + val changeLog = buf.toString() + .replace( + "{style-placeholder}", + "body { background-color: $backgroundColor; color: $contentColor; } li {color: $textColor;} h3 {color: $accentColorString;} .tag {background-color: $accentColorString; color: $accentTextColor; } div{background-color: $cardBackgroundColor;}" + ) + .replace("{link-color}", colorToCSS(accentColor(this))) + .replace( + "{link-color-active}", + colorToCSS( + lightenColor(accentColor(this)) + ) + ) + binding.webView.loadData(changeLog, "text/html", "UTF-8") + } catch (e: Throwable) { + binding.webView.loadData( + "

Unable to load

" + e.localizedMessage + "

", "text/html", "UTF-8" + ) + } + setChangelogRead(this) + binding.tgFab.setOnClickListener { + RetroUtil.openUrl( + this, + Constants.TELEGRAM_CHANGE_LOG + ) + } + binding.tgFab.accentColor() + binding.tgFab.shrink() + binding.container.setOnScrollChangeListener { _: NestedScrollView?, _: Int, scrollY: Int, _: Int, oldScrollY: Int -> + val dy = scrollY - oldScrollY + if (dy > 0) { + binding.tgFab.shrink() + } else if (dy < 0) { + binding.tgFab.extend() + } + } + } + + companion object { + private fun colorToCSS(color: Int): String { + return String.format( + Locale.getDefault(), + "rgba(%d, %d, %d, %d)", + Color.red(color), + Color.green(color), + Color.blue(color), + Color.alpha(color) + ) // on API 29, WebView doesn't load with hex colors + } + + private fun setChangelogRead(context: Context) { + try { + val pInfo = context.packageManager.getPackageInfo(context.packageName, 0) + val currentVersion = pInfo.versionCode + lastVersion = currentVersion + } catch (e: PackageManager.NameNotFoundException) { + e.printStackTrace() + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/code/name/monkey/retromusic/activities/base/AbsSlidingMusicPanelActivity.kt b/app/src/main/java/code/name/monkey/retromusic/activities/base/AbsSlidingMusicPanelActivity.kt index a6cfe26a..7b456a38 100644 --- a/app/src/main/java/code/name/monkey/retromusic/activities/base/AbsSlidingMusicPanelActivity.kt +++ b/app/src/main/java/code/name/monkey/retromusic/activities/base/AbsSlidingMusicPanelActivity.kt @@ -14,13 +14,17 @@ */ package code.name.monkey.retromusic.activities.base +import android.animation.Animator +import android.content.res.ColorStateList import android.graphics.Color import android.os.Bundle import android.view.View import android.view.ViewGroup import android.view.ViewTreeObserver import android.widget.FrameLayout +import androidx.core.animation.doOnEnd import androidx.core.view.ViewCompat +import androidx.core.view.WindowInsetsCompat import androidx.core.view.isVisible import androidx.fragment.app.Fragment import androidx.fragment.app.commit @@ -28,14 +32,13 @@ import code.name.monkey.appthemehelper.util.ATHUtil import code.name.monkey.appthemehelper.util.ColorUtil import code.name.monkey.retromusic.R import code.name.monkey.retromusic.RetroBottomSheetBehavior -import code.name.monkey.retromusic.databinding.ActivityMainContentBinding import code.name.monkey.retromusic.databinding.SlidingMusicPanelLayoutBinding import code.name.monkey.retromusic.extensions.* import code.name.monkey.retromusic.fragments.LibraryViewModel -import code.name.monkey.retromusic.fragments.MiniPlayerFragment import code.name.monkey.retromusic.fragments.NowPlayingScreen import code.name.monkey.retromusic.fragments.NowPlayingScreen.* import code.name.monkey.retromusic.fragments.base.AbsPlayerFragment +import code.name.monkey.retromusic.fragments.other.MiniPlayerFragment import code.name.monkey.retromusic.fragments.player.adaptive.AdaptiveFragment import code.name.monkey.retromusic.fragments.player.blur.BlurPlayerFragment import code.name.monkey.retromusic.fragments.player.card.CardFragment @@ -53,10 +56,11 @@ import code.name.monkey.retromusic.fragments.player.peak.PeakPlayerFragment import code.name.monkey.retromusic.fragments.player.plain.PlainPlayerFragment import code.name.monkey.retromusic.fragments.player.simple.SimplePlayerFragment import code.name.monkey.retromusic.fragments.player.tiny.TinyPlayerFragment +import code.name.monkey.retromusic.fragments.queue.PlayingQueueFragment import code.name.monkey.retromusic.helper.MusicPlayerRemote import code.name.monkey.retromusic.model.CategoryInfo import code.name.monkey.retromusic.util.PreferenceUtil -import code.name.monkey.retromusic.views.BottomNavigationBarTinted +import code.name.monkey.retromusic.util.RetroUtil import com.google.android.material.bottomsheet.BottomSheetBehavior.* import org.koin.androidx.viewmodel.ext.android.viewModel @@ -66,15 +70,14 @@ abstract class AbsSlidingMusicPanelActivity : AbsMusicServiceActivity() { var fromNotification: Boolean = false } + private var windowInsets: WindowInsetsCompat? = null + private var bottomNavAnimator: Animator? = null protected val libraryViewModel by viewModel() private lateinit var bottomSheetBehavior: RetroBottomSheetBehavior private var playerFragment: AbsPlayerFragment? = null private var miniPlayerFragment: MiniPlayerFragment? = null private var nowPlayingScreen: NowPlayingScreen? = null - private var navigationBarColor: Int = 0 private var taskColor: Int = 0 - private var lightStatusBar: Boolean = false - private var lightNavigationBar: Boolean = false private var paletteColor: Int = Color.WHITE protected abstract fun createContentView(): SlidingMusicPanelLayoutBinding private val panelState: Int @@ -82,11 +85,8 @@ abstract class AbsSlidingMusicPanelActivity : AbsMusicServiceActivity() { private lateinit var binding: SlidingMusicPanelLayoutBinding private val bottomSheetCallbackList = object : BottomSheetCallback() { - override fun onSlide(bottomSheet: View, slideOffset: Float) { setMiniPlayerAlphaProgress(slideOffset) - binding.dimBackground.show() - binding.dimBackground.alpha = slideOffset } override fun onStateChanged(bottomSheet: View, newState: Int) { @@ -96,15 +96,14 @@ abstract class AbsSlidingMusicPanelActivity : AbsMusicServiceActivity() { } STATE_COLLAPSED -> { onPanelCollapsed() - binding.dimBackground.hide() if (fromNotification) { - hideBottomBar(MusicPlayerRemote.playingQueue.isEmpty()) + hideBottomSheet(MusicPlayerRemote.playingQueue.isEmpty()) fromNotification = false } } STATE_SETTLING, STATE_DRAGGING -> { if (fromNotification) { - getBottomNavigationView().isVisible = true + bottomNavigationView.isVisible = true } } else -> { @@ -120,23 +119,30 @@ abstract class AbsSlidingMusicPanelActivity : AbsMusicServiceActivity() { super.onCreate(savedInstanceState) binding = createContentView() setContentView(binding.root) + ViewCompat.setOnApplyWindowInsetsListener( + binding.root + ) { _, insets -> + windowInsets = insets + insets + } + bottomNavigationView.drawAboveSystemBarsWithPadding() + if (RetroUtil.isLandscape()) { + binding.slidingPanel.drawAboveSystemBarsWithPadding(true) + } + binding.fragmentContainer.addBottomInsets() chooseFragmentForTheme() setupSlidingUpPanel() setupBottomSheet() updateColor() - - val themeColor = resolveColor(android.R.attr.windowBackground, Color.GRAY) - binding.dimBackground.setBackgroundColor(ColorUtil.withAlpha(themeColor, 0.5f)) - binding.dimBackground.setOnClickListener { - println("dimBackground") - collapsePanel() - } + binding.slidingPanel.backgroundTintList = ColorStateList.valueOf(darkAccentColor()) + bottomNavigationView.backgroundTintList = ColorStateList.valueOf(darkAccentColor()) } private fun setupBottomSheet() { bottomSheetBehavior = from(binding.slidingPanel) as RetroBottomSheetBehavior bottomSheetBehavior.addBottomSheetCallback(bottomSheetCallbackList) - bottomSheetBehavior.maxWidth = resources.displayMetrics.widthPixels + bottomSheetBehavior.isHideable = false + setMiniPlayerAlphaProgress(0F) } override fun onResume() { @@ -155,41 +161,37 @@ abstract class AbsSlidingMusicPanelActivity : AbsMusicServiceActivity() { } protected fun wrapSlidingMusicPanel(): SlidingMusicPanelLayoutBinding { - val slidingMusicPanelLayoutBinding = - SlidingMusicPanelLayoutBinding.inflate(layoutInflater) - val contentContainer: ViewGroup = - slidingMusicPanelLayoutBinding.mainContentFrame - ActivityMainContentBinding.inflate(layoutInflater, contentContainer, true) - return slidingMusicPanelLayoutBinding + return SlidingMusicPanelLayoutBinding.inflate(layoutInflater) } fun collapsePanel() { bottomSheetBehavior.state = STATE_COLLAPSED - setMiniPlayerAlphaProgress(0f) } fun expandPanel() { bottomSheetBehavior.state = STATE_EXPANDED - setMiniPlayerAlphaProgress(1f) } private fun setMiniPlayerAlphaProgress(progress: Float) { + if (progress < 0) return val alpha = 1 - progress - miniPlayerFragment?.view?.alpha = alpha + miniPlayerFragment?.view?.alpha = 1 - (progress / 0.2F) miniPlayerFragment?.view?.visibility = if (alpha == 0f) View.GONE else View.VISIBLE binding.bottomNavigationView.translationY = progress * 500 binding.bottomNavigationView.alpha = alpha + binding.playerFragmentContainer.alpha = (progress - 0.2F) / 0.2F } open fun onPanelCollapsed() { + setMiniPlayerAlphaProgress(0F) // restore values - super.setLightStatusbar(lightStatusBar) + super.setLightStatusbarAuto(surfaceColor()) + super.setLightNavigationAuto() super.setTaskDescriptionColor(taskColor) - super.setNavigationbarColor(navigationBarColor) - super.setLightNavigationBar(lightNavigationBar) } open fun onPanelExpanded() { + setMiniPlayerAlphaProgress(1F) onPaletteColorChanged() } @@ -214,9 +216,7 @@ abstract class AbsSlidingMusicPanelActivity : AbsMusicServiceActivity() { }) } - fun getBottomNavigationView(): BottomNavigationBarTinted { - return binding.bottomNavigationView - } + val bottomNavigationView get() = binding.bottomNavigationView override fun onServiceConnected() { super.onServiceConnected() @@ -225,15 +225,19 @@ abstract class AbsSlidingMusicPanelActivity : AbsMusicServiceActivity() { ViewTreeObserver.OnGlobalLayoutListener { override fun onGlobalLayout() { binding.slidingPanel.viewTreeObserver.removeOnGlobalLayoutListener(this) - hideBottomBar(false) + hideBottomSheet(false) } }) - } // don't call hideBottomBar(true) here as it causes a bug with the SlidingUpPanelLayout + } // don't call hideBottomSheet(true) here as it causes a bug with the SlidingUpPanelLayout } override fun onQueueChanged() { super.onQueueChanged() - hideBottomBar(MusicPlayerRemote.playingQueue.isEmpty()) + // Mini player should be hidden in Playing Queue + // it may pop up if hideBottomSheet is called + if (currentFragment(R.id.fragment_container) !is PlayingQueueFragment) { + hideBottomSheet(MusicPlayerRemote.playingQueue.isEmpty()) + } } override fun onBackPressed() { @@ -260,13 +264,10 @@ abstract class AbsSlidingMusicPanelActivity : AbsMusicServiceActivity() { } else if (nowPlayingScreen == Card || nowPlayingScreen == Blur || nowPlayingScreen == BlurCard) { super.setLightStatusbar(false) super.setLightNavigationBar(true) - super.setNavigationbarColor(Color.BLACK) } else if (nowPlayingScreen == Color || nowPlayingScreen == Tiny || nowPlayingScreen == Gradient) { - super.setNavigationbarColor(paletteColor) super.setLightNavigationBar(isColorLight) super.setLightStatusbar(isColorLight) } else if (nowPlayingScreen == Full) { - super.setNavigationbarColor(paletteColor) super.setLightNavigationBar(isColorLight) super.setLightStatusbar(false) } else if (nowPlayingScreen == Classic) { @@ -287,27 +288,6 @@ abstract class AbsSlidingMusicPanelActivity : AbsMusicServiceActivity() { } } - override fun setLightStatusbar(enabled: Boolean) { - lightStatusBar = enabled - if (panelState == STATE_COLLAPSED) { - super.setLightStatusbar(enabled) - } - } - - override fun setLightNavigationBar(enabled: Boolean) { - lightNavigationBar = enabled - if (panelState == STATE_COLLAPSED) { - super.setLightNavigationBar(enabled) - } - } - - override fun setNavigationbarColor(color: Int) { - navigationBarColor = color - if (panelState == STATE_COLLAPSED) { - super.setNavigationbarColor(color) - } - } - override fun setTaskDescriptionColor(color: Int) { taskColor = color if (panelState == STATE_COLLAPSED) { @@ -337,38 +317,57 @@ abstract class AbsSlidingMusicPanelActivity : AbsMusicServiceActivity() { }) } - fun setBottomBarVisibility(visible: Boolean) { + fun setBottomNavVisibility(visible: Boolean, animate: Boolean = false) { binding.bottomNavigationView.isVisible = visible - hideBottomBar(MusicPlayerRemote.playingQueue.isEmpty()) + hideBottomSheet(MusicPlayerRemote.playingQueue.isEmpty(), animate) } - private fun hideBottomBar(hide: Boolean) { + fun hideBottomSheet(hide: Boolean, animate: Boolean = false) { val heightOfBar = - if (MusicPlayerRemote.isCasting) dip(R.dimen.cast_mini_player_height) else dip(R.dimen.mini_player_height) - val heightOfBarWithTabs = - if (MusicPlayerRemote.isCasting) dip(R.dimen.mini_cast_player_height_expanded) else dip( - R.dimen.mini_player_height_expanded - ) + windowInsets.safeGetBottomInsets() + + if (MusicPlayerRemote.isCasting) dip(R.dimen.cast_mini_player_height) else dip(R.dimen.mini_player_height) + val heightOfBarWithTabs = heightOfBar + dip(R.dimen.bottom_nav_height) val isVisible = binding.bottomNavigationView.isVisible if (hide) { - bottomSheetBehavior.isHideable = true - bottomSheetBehavior.peekHeight = 0 + bottomSheetBehavior.peekHeight = -windowInsets.safeGetBottomInsets() + bottomSheetBehavior.state = STATE_COLLAPSED + libraryViewModel.setFabMargin(if (isVisible) dip(R.dimen.bottom_nav_height) else 0) ViewCompat.setElevation(binding.slidingPanel, 0f) ViewCompat.setElevation(binding.bottomNavigationView, 10f) - collapsePanel() } else { if (MusicPlayerRemote.playingQueue.isNotEmpty()) { - bottomSheetBehavior.isHideable = false + ViewCompat.setElevation(binding.slidingPanel, 10f) ViewCompat.setElevation(binding.bottomNavigationView, 10f) if (isVisible) { println("List") - if (bottomSheetBehavior.state != STATE_EXPANDED) - getBottomNavigationView().translateYAnimate(0F) - bottomSheetBehavior.peekHeightAnimate(heightOfBarWithTabs) + if (animate) { + bottomNavAnimator?.end() + bottomSheetBehavior.peekHeightAnimate(heightOfBarWithTabs) + bottomNavAnimator = binding.bottomNavigationView.translateYAnimate(0F) + } else { + bottomSheetBehavior.peekHeight = heightOfBarWithTabs + binding.bottomNavigationView.translationY = 0F + } + binding.bottomNavigationView.bringToFront() + libraryViewModel.setFabMargin(heightOfBarWithTabs - 2 * windowInsets.safeGetBottomInsets()) } else { println("Details") - bottomSheetBehavior.peekHeight = heightOfBar + if (animate) { + bottomSheetBehavior.peekHeightAnimate(heightOfBar) + bottomNavAnimator?.end() + bottomNavAnimator = + bottomNavigationView.translateYAnimate(dip(R.dimen.bottom_nav_height).toFloat()) + bottomNavAnimator?.doOnEnd { + binding.slidingPanel.bringToFront() + } + } else { + bottomSheetBehavior.peekHeight = heightOfBar + binding.bottomNavigationView.translationY = + dip(R.dimen.bottom_nav_height).toFloat() + binding.slidingPanel.bringToFront() + } + libraryViewModel.setFabMargin(heightOfBar - 2 * windowInsets.safeGetBottomInsets()) } } } @@ -376,7 +375,7 @@ abstract class AbsSlidingMusicPanelActivity : AbsMusicServiceActivity() { fun setAllowDragging(allowDragging: Boolean) { bottomSheetBehavior.setAllowDragging(allowDragging) - hideBottomBar(false) + hideBottomSheet(false) } private fun chooseFragmentForTheme() { diff --git a/app/src/main/java/code/name/monkey/retromusic/activities/base/AbsThemeActivity.kt b/app/src/main/java/code/name/monkey/retromusic/activities/base/AbsThemeActivity.kt index 8d437159..5f038e88 100644 --- a/app/src/main/java/code/name/monkey/retromusic/activities/base/AbsThemeActivity.kt +++ b/app/src/main/java/code/name/monkey/retromusic/activities/base/AbsThemeActivity.kt @@ -33,9 +33,11 @@ import code.name.monkey.appthemehelper.util.ColorUtil import code.name.monkey.appthemehelper.util.VersionUtils import code.name.monkey.retromusic.LanguageContextWrapper import code.name.monkey.retromusic.R +import code.name.monkey.retromusic.extensions.surfaceColor import code.name.monkey.retromusic.util.PreferenceUtil import code.name.monkey.retromusic.util.RetroUtil import code.name.monkey.retromusic.util.theme.ThemeManager +import com.google.android.material.color.DynamicColors import java.util.* abstract class AbsThemeActivity : ATHToolbarActivity(), Runnable { @@ -49,12 +51,22 @@ abstract class AbsThemeActivity : ATHToolbarActivity(), Runnable { setImmersiveFullscreen() registerSystemUiVisibility() toggleScreenOn() - //MaterialDialogsUtil.updateMaterialDialogsThemeSingleton(this) + setDrawUnderNavigationBar() + setLightNavigationAuto() + setLightStatusbarAuto(surfaceColor()) } private fun updateTheme() { setTheme(ThemeManager.getThemeResValue(this)) setDefaultNightMode(ThemeManager.getNightMode(this)) + + // Apply dynamic colors to activity if enabled + if (PreferenceUtil.materialYou) { + DynamicColors.applyIfAvailable( + this, + com.google.android.material.R.style.ThemeOverlay_Material3_DynamicColors_DayNight + ) + } } private fun toggleScreenOn() { @@ -91,7 +103,7 @@ abstract class AbsThemeActivity : ATHToolbarActivity(), Runnable { RetroUtil.setAllowDrawUnderStatusBar(window) } - fun setDrawUnderNavigationBar() { + private fun setDrawUnderNavigationBar() { RetroUtil.setAllowDrawUnderNavigationBar(window) } @@ -148,6 +160,10 @@ abstract class AbsThemeActivity : ATHToolbarActivity(), Runnable { setNavigationbarColor(ATHUtil.resolveColor(this, R.attr.colorSurface)) } + fun setLightNavigationAuto() { + ATH.setLightNavigationbarAuto(this, surfaceColor()) + } + open fun setLightStatusbar(enabled: Boolean) { ATH.setLightStatusbar(this, enabled) } diff --git a/app/src/main/java/code/name/monkey/retromusic/activities/bugreport/BugReportActivity.kt b/app/src/main/java/code/name/monkey/retromusic/activities/bugreport/BugReportActivity.kt index 3780834d..cc0ca55c 100644 --- a/app/src/main/java/code/name/monkey/retromusic/activities/bugreport/BugReportActivity.kt +++ b/app/src/main/java/code/name/monkey/retromusic/activities/bugreport/BugReportActivity.kt @@ -30,7 +30,6 @@ import androidx.annotation.StringDef import androidx.annotation.StringRes import androidx.appcompat.app.AlertDialog import code.name.monkey.appthemehelper.ThemeStore -import code.name.monkey.appthemehelper.util.ATHUtil import code.name.monkey.appthemehelper.util.MaterialUtil import code.name.monkey.appthemehelper.util.TintHelper import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper @@ -74,12 +73,9 @@ open class BugReportActivity : AbsThemeActivity() { private var deviceInfo: DeviceInfo? = null override fun onCreate(savedInstanceState: Bundle?) { - setDrawUnderStatusBar() super.onCreate(savedInstanceState) binding = ActivityBugReportBinding.inflate(layoutInflater) setContentView(binding.root) - setStatusbarColorAuto() - setNavigationbarColorAuto() setTaskDescriptionColorAuto() initViews() @@ -92,8 +88,6 @@ open class BugReportActivity : AbsThemeActivity() { private fun initViews() { val accentColor = ThemeStore.accentColor(this) - val primaryColor = ATHUtil.resolveColor(this, R.attr.colorSurface) - binding.toolbar.setBackgroundColor(primaryColor) setSupportActionBar(binding.toolbar) ToolbarContentTintHelper.colorBackButton(binding.toolbar) supportActionBar?.setDisplayHomeAsUpEnabled(true) diff --git a/app/src/main/java/code/name/monkey/retromusic/activities/tageditor/AbsTagEditorActivity.kt b/app/src/main/java/code/name/monkey/retromusic/activities/tageditor/AbsTagEditorActivity.kt index 536dea62..361555bb 100755 --- a/app/src/main/java/code/name/monkey/retromusic/activities/tageditor/AbsTagEditorActivity.kt +++ b/app/src/main/java/code/name/monkey/retromusic/activities/tageditor/AbsTagEditorActivity.kt @@ -196,7 +196,6 @@ abstract class AbsTagEditorActivity : AbsBaseActivity() { _binding = bindingInflater.invoke(layoutInflater) setContentView(binding.root) setStatusbarColorAuto() - setNavigationbarColorAuto() setTaskDescriptionColorAuto() saveFab = findViewById(R.id.saveTags) diff --git a/app/src/main/java/code/name/monkey/retromusic/activities/tageditor/AlbumTagEditorActivity.kt b/app/src/main/java/code/name/monkey/retromusic/activities/tageditor/AlbumTagEditorActivity.kt index 2a17c54c..6bbae4d9 100755 --- a/app/src/main/java/code/name/monkey/retromusic/activities/tageditor/AlbumTagEditorActivity.kt +++ b/app/src/main/java/code/name/monkey/retromusic/activities/tageditor/AlbumTagEditorActivity.kt @@ -29,10 +29,10 @@ import android.view.LayoutInflater import android.widget.ImageView import android.widget.Toast import code.name.monkey.appthemehelper.util.ATHUtil -import code.name.monkey.appthemehelper.util.MaterialUtil import code.name.monkey.retromusic.R import code.name.monkey.retromusic.databinding.ActivityAlbumTagEditorBinding import code.name.monkey.retromusic.extensions.appHandleColor +import code.name.monkey.retromusic.extensions.setTint import code.name.monkey.retromusic.glide.GlideApp import code.name.monkey.retromusic.glide.palette.BitmapPaletteWrapper import code.name.monkey.retromusic.model.ArtworkInfo @@ -62,8 +62,6 @@ class AlbumTagEditorActivity : AbsTagEditorActivity(binding.editorImage) { @@ -119,10 +117,10 @@ class AlbumTagEditorActivity : AbsTagEditorActivity @SuppressLint("ClickableViewAccessibility") private fun setUpViews() { fillViewsWithFileTags() - MaterialUtil.setTint(binding.songTextContainer, false) - MaterialUtil.setTint(binding.composerContainer, false) - MaterialUtil.setTint(binding.albumTextContainer, false) - MaterialUtil.setTint(binding.artistContainer, false) - MaterialUtil.setTint(binding.albumArtistContainer, false) - MaterialUtil.setTint(binding.yearContainer, false) - MaterialUtil.setTint(binding.genreContainer, false) - MaterialUtil.setTint(binding.trackNumberContainer, false) - MaterialUtil.setTint(binding.lyricsContainer, false) + binding.songTextContainer.setTint(false) + binding.composerContainer.setTint(false) + binding.albumTextContainer.setTint(false) + binding.artistContainer.setTint(false) + binding.albumArtistContainer.setTint(false) + binding.yearContainer.setTint(false) + binding.genreContainer.setTint(false) + binding.trackNumberContainer.setTint(false) + binding.lyricsContainer.setTint(false) binding.songText.appHandleColor().addTextChangedListener(this) binding.albumText.appHandleColor().addTextChangedListener(this) diff --git a/app/src/main/java/code/name/monkey/retromusic/adapter/CategoryInfoAdapter.java b/app/src/main/java/code/name/monkey/retromusic/adapter/CategoryInfoAdapter.java index d9b7b693..2354bb9a 100644 --- a/app/src/main/java/code/name/monkey/retromusic/adapter/CategoryInfoAdapter.java +++ b/app/src/main/java/code/name/monkey/retromusic/adapter/CategoryInfoAdapter.java @@ -40,7 +40,7 @@ public class CategoryInfoAdapter extends RecyclerView.Adapter categoryInfos; - private ItemTouchHelper touchHelper; + private final ItemTouchHelper touchHelper; public CategoryInfoAdapter() { SwipeAndDragHelper swipeAndDragHelper = new SwipeAndDragHelper(this); @@ -128,15 +128,15 @@ public class CategoryInfoAdapter extends RecyclerView.Adapter { holder.title?.text = dataSet[position].toString() diff --git a/app/src/main/java/code/name/monkey/retromusic/adapter/SongFileAdapter.kt b/app/src/main/java/code/name/monkey/retromusic/adapter/SongFileAdapter.kt index 323eae4e..7160568d 100644 --- a/app/src/main/java/code/name/monkey/retromusic/adapter/SongFileAdapter.kt +++ b/app/src/main/java/code/name/monkey/retromusic/adapter/SongFileAdapter.kt @@ -40,7 +40,7 @@ import kotlin.math.log10 import kotlin.math.pow class SongFileAdapter( - private val activity: AppCompatActivity, + override val activity: AppCompatActivity, private var dataSet: List, private val itemLayoutRes: Int, private val iCallbacks: ICallbacks?, diff --git a/app/src/main/java/code/name/monkey/retromusic/adapter/album/AlbumAdapter.kt b/app/src/main/java/code/name/monkey/retromusic/adapter/album/AlbumAdapter.kt index 8f9e00f0..25e24ff7 100644 --- a/app/src/main/java/code/name/monkey/retromusic/adapter/album/AlbumAdapter.kt +++ b/app/src/main/java/code/name/monkey/retromusic/adapter/album/AlbumAdapter.kt @@ -39,7 +39,7 @@ import code.name.monkey.retromusic.util.color.MediaNotificationProcessor import me.zhanghai.android.fastscroll.PopupTextProvider open class AlbumAdapter( - val activity: FragmentActivity, + override val activity: FragmentActivity, var dataSet: List, var itemLayoutRes: Int, iCabHolder: ICabHolder?, @@ -68,7 +68,7 @@ open class AlbumAdapter( return ViewHolder(view) } - private fun getAlbumTitle(album: Album): String? { + private fun getAlbumTitle(album: Album): String { return album.title } @@ -88,7 +88,13 @@ open class AlbumAdapter( holder.itemView.isActivated = isChecked holder.title?.text = getAlbumTitle(album) holder.text?.text = getAlbumText(album) - ViewCompat.setTransitionName(holder.image!!, album.id.toString()) + // Check if imageContainer exists so we can have a smooth transition without + // CardView clipping, if it doesn't exist in current layout set transition name to image instead. + if (holder.imageContainer != null) { + ViewCompat.setTransitionName(holder.imageContainer!!, album.id.toString()) + } else { + ViewCompat.setTransitionName(holder.image!!, album.id.toString()) + } loadAlbumCover(album, holder) } @@ -130,7 +136,7 @@ open class AlbumAdapter( } override fun getName(album: Album): String { - return album.title!! + return album.title } override fun onMultipleItemAction( @@ -177,7 +183,7 @@ open class AlbumAdapter( toggleChecked(layoutPosition) } else { image?.let { - listener?.onAlbumClick(dataSet[layoutPosition].id, it) + listener?.onAlbumClick(dataSet[layoutPosition].id, imageContainer ?: it) } } } diff --git a/app/src/main/java/code/name/monkey/retromusic/adapter/album/AlbumCoverPagerAdapter.kt b/app/src/main/java/code/name/monkey/retromusic/adapter/album/AlbumCoverPagerAdapter.kt index 50841883..e7849a0b 100644 --- a/app/src/main/java/code/name/monkey/retromusic/adapter/album/AlbumCoverPagerAdapter.kt +++ b/app/src/main/java/code/name/monkey/retromusic/adapter/album/AlbumCoverPagerAdapter.kt @@ -24,6 +24,7 @@ import androidx.fragment.app.Fragment import androidx.fragment.app.FragmentManager import androidx.lifecycle.lifecycleScope import code.name.monkey.retromusic.R +import code.name.monkey.retromusic.activities.MainActivity import code.name.monkey.retromusic.fragments.AlbumCoverStyle import code.name.monkey.retromusic.fragments.NowPlayingScreen.* import code.name.monkey.retromusic.glide.GlideApp @@ -35,6 +36,7 @@ import code.name.monkey.retromusic.util.MusicUtil import code.name.monkey.retromusic.util.NavigationUtil import code.name.monkey.retromusic.util.PreferenceUtil import code.name.monkey.retromusic.util.color.MediaNotificationProcessor +import com.google.android.material.bottomsheet.BottomSheetBehavior.STATE_EXPANDED import com.google.android.material.dialog.MaterialAlertDialogBuilder import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch @@ -89,6 +91,7 @@ class AlbumCoverPagerAdapter( private lateinit var song: Song private var colorReceiver: ColorReceiver? = null private var request: Int = 0 + private val mainActivity get() = activity as MainActivity override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @@ -106,7 +109,9 @@ class AlbumCoverPagerAdapter( ViewCompat.setTransitionName(view, "lyrics") albumCover = view.findViewById(R.id.player_image) view.setOnClickListener { - showLyricsDialog() + if (mainActivity.getBottomSheetBehavior().state == STATE_EXPANDED) { + showLyricsDialog() + } } return view } diff --git a/app/src/main/java/code/name/monkey/retromusic/adapter/artist/ArtistAdapter.kt b/app/src/main/java/code/name/monkey/retromusic/adapter/artist/ArtistAdapter.kt index 2de75bc1..7b3fe50d 100644 --- a/app/src/main/java/code/name/monkey/retromusic/adapter/artist/ArtistAdapter.kt +++ b/app/src/main/java/code/name/monkey/retromusic/adapter/artist/ArtistAdapter.kt @@ -42,7 +42,7 @@ import me.zhanghai.android.fastscroll.PopupTextProvider import java.util.* class ArtistAdapter( - val activity: FragmentActivity, + override val activity: FragmentActivity, var dataSet: List, var itemLayoutRes: Int, val ICabHolder: ICabHolder?, @@ -85,12 +85,12 @@ class ArtistAdapter( holder.itemView.isActivated = isChecked holder.title?.text = artist.name holder.text?.hide() - holder.image?.let { - if (PreferenceUtil.albumArtistsOnly) { - ViewCompat.setTransitionName(it, artist.name) - } else { - ViewCompat.setTransitionName(it, artist.id.toString()) - } + val transitionName = + if (PreferenceUtil.albumArtistsOnly) artist.name else artist.id.toString() + if (holder.imageContainer != null) { + ViewCompat.setTransitionName(holder.imageContainer!!, transitionName) + } else { + ViewCompat.setTransitionName(holder.image!!, transitionName) } loadArtistImage(artist, holder) } @@ -169,9 +169,9 @@ class ArtistAdapter( val artist = dataSet[layoutPosition] image?.let { if (PreferenceUtil.albumArtistsOnly && IAlbumArtistClickListener != null) { - IAlbumArtistClickListener.onAlbumArtist(artist.name, it) + IAlbumArtistClickListener.onAlbumArtist(artist.name, imageContainer ?: it) } else { - IArtistClickListener.onArtist(artist.id, it) + IArtistClickListener.onArtist(artist.id, imageContainer ?: it) } } } diff --git a/app/src/main/java/code/name/monkey/retromusic/adapter/backup/BackupAdapter.kt b/app/src/main/java/code/name/monkey/retromusic/adapter/backup/BackupAdapter.kt new file mode 100644 index 00000000..186fd3f5 --- /dev/null +++ b/app/src/main/java/code/name/monkey/retromusic/adapter/backup/BackupAdapter.kt @@ -0,0 +1,66 @@ +package code.name.monkey.retromusic.adapter.backup + +import android.view.LayoutInflater +import android.view.MenuItem +import android.view.View +import android.view.ViewGroup +import android.widget.PopupMenu +import android.widget.TextView +import androidx.appcompat.widget.AppCompatImageView +import androidx.fragment.app.FragmentActivity +import androidx.recyclerview.widget.RecyclerView +import code.name.monkey.retromusic.R +import java.io.File + + +class BackupAdapter( + val activity: FragmentActivity, + var dataSet: MutableList, + val backupClickedListener: BackupClickedListener +) : RecyclerView.Adapter() { + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { + return ViewHolder( + LayoutInflater.from(activity).inflate(R.layout.item_list_card, parent, false) + ) + } + + override fun onBindViewHolder(holder: ViewHolder, position: Int) { + holder.title.text = dataSet[position].nameWithoutExtension + } + + override fun getItemCount(): Int = dataSet.size + + fun swapDataset(dataSet: List) { + this.dataSet = ArrayList(dataSet) + notifyDataSetChanged() + } + + inner class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { + val title: TextView = itemView.findViewById(R.id.title) + val menu: AppCompatImageView = itemView.findViewById(R.id.menu) + + init { + menu.setOnClickListener { view -> + val popupMenu = PopupMenu(activity, view) + popupMenu.inflate(R.menu.menu_backup) + popupMenu.setOnMenuItemClickListener { menuItem -> + return@setOnMenuItemClickListener backupClickedListener.onBackupMenuClicked( + dataSet[bindingAdapterPosition], + menuItem + ) + } + popupMenu.show() + } + itemView.setOnClickListener { + backupClickedListener.onBackupClicked(dataSet[bindingAdapterPosition]) + } + } + } + + interface BackupClickedListener { + fun onBackupClicked(file: File) + + fun onBackupMenuClicked(file: File, menuItem: MenuItem): Boolean + } +} \ No newline at end of file diff --git a/app/src/main/java/code/name/monkey/retromusic/adapter/base/AbsMultiSelectAdapter.java b/app/src/main/java/code/name/monkey/retromusic/adapter/base/AbsMultiSelectAdapter.java deleted file mode 100644 index 581141bf..00000000 --- a/app/src/main/java/code/name/monkey/retromusic/adapter/base/AbsMultiSelectAdapter.java +++ /dev/null @@ -1,182 +0,0 @@ -package code.name.monkey.retromusic.adapter.base; - -import android.annotation.SuppressLint; -import android.content.Context; -import android.view.Menu; -import android.view.MenuItem; - -import androidx.annotation.MenuRes; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.appcompat.widget.AppCompatTextView; -import androidx.appcompat.widget.Toolbar; -import androidx.recyclerview.widget.RecyclerView; - -import com.afollestad.materialcab.MaterialCab; - -import java.util.ArrayList; -import java.util.List; - -import code.name.monkey.retromusic.R; -import code.name.monkey.retromusic.interfaces.ICabHolder; - -public abstract class AbsMultiSelectAdapter - extends RecyclerView.Adapter implements MaterialCab.Callback { - - @Nullable - private final ICabHolder ICabHolder; - private final Context context; - private MaterialCab cab; - private final List checked; - private int menuRes; - private AppCompatTextView dummyText; - private int oldSize = 0; - - public AbsMultiSelectAdapter( - @NonNull Context context, @Nullable ICabHolder ICabHolder, @MenuRes int menuRes) { - this.ICabHolder = ICabHolder; - checked = new ArrayList<>(); - this.menuRes = menuRes; - this.context = context; - } - - @Override - public boolean onCabCreated(MaterialCab materialCab, Menu menu) { - playCreateAnim(materialCab); - createDummyTextView(); - return true; - } - - @Override - public boolean onCabFinished(MaterialCab materialCab) { - clearChecked(); - cab.getToolbar().removeView(dummyText); - oldSize = 0; - return true; - } - - @Override - public boolean onCabItemClicked(MenuItem menuItem) { - if (menuItem.getItemId() == R.id.action_multi_select_adapter_check_all) { - checkAll(); - } else { - onMultipleItemAction(menuItem, new ArrayList<>(checked)); - cab.finish(); - clearChecked(); - } - return true; - } - - protected void checkAll() { - if (ICabHolder != null) { - checked.clear(); - for (int i = 0; i < getItemCount(); i++) { - I identifier = getIdentifier(i); - if (identifier != null) { - checked.add(identifier); - } - } - notifyDataSetChanged(); - updateCab(); - } - } - - @Nullable - protected abstract I getIdentifier(int position); - - protected String getName(I object) { - return object.toString(); - } - - protected boolean isChecked(I identifier) { - return checked.contains(identifier); - } - - protected boolean isInQuickSelectMode() { - return cab != null && cab.isActive(); - } - - protected abstract void onMultipleItemAction(MenuItem menuItem, List selection); - - protected void setMultiSelectMenuRes(@MenuRes int menuRes) { - this.menuRes = menuRes; - } - - protected boolean toggleChecked(final int position) { - if (ICabHolder != null) { - I identifier = getIdentifier(position); - if (identifier == null) { - return false; - } - - if (!checked.remove(identifier)) { - checked.add(identifier); - } - - notifyItemChanged(position); - updateCab(); - return true; - } - return false; - } - - private void clearChecked() { - checked.clear(); - notifyDataSetChanged(); - } - - @SuppressLint({"StringFormatInvalid", "StringFormatMatches"}) - private void updateCab() { - if (ICabHolder != null) { - if (cab == null || !cab.isActive()) { - cab = ICabHolder.openCab(menuRes, this); - } - final int size = checked.size(); - if (size <= 0) { - cab.finish(); - } else if (size == 1) { - cab.setTitle(context.getString(R.string.x_selected, size)); - if (oldSize == 0) { - cab.getToolbar().addView(dummyText); - } - } else { - AppCompatTextView title = (AppCompatTextView) cab.getToolbar().getChildAt(2); - dummyText.setText(title.getText()); - - title.setAlpha(0); - - cab.setTitle(context.getString(R.string.x_selected, size)); - dummyText.setTranslationX(title.getLeft() - dummyText.getLeft()); - - dummyText.setAlpha(1); - - dummyText.setTranslationY(0); - if (oldSize > size) { - title.setTranslationY(40); - dummyText.animate().translationY(-40).alpha(0.0F).setDuration(300).start(); - } else { - title.setTranslationY(-40); - dummyText.animate().translationY(40).alpha(0.0F).setDuration(300).start(); - } - title.animate().translationY(0).alpha(1.0F).setDuration(300).start(); - } - oldSize = size; - } - } - - private void playCreateAnim(MaterialCab materialCab) { - Toolbar cabToolbar = materialCab.getToolbar(); - int height = context.getResources().getDimensionPixelSize(R.dimen.toolbar_height); - cabToolbar.setTranslationY(-height); - cabToolbar.animate().translationYBy(height).setDuration(300).start(); - } - - private void createDummyTextView() { - if (dummyText != null) return; - dummyText = new AppCompatTextView(context); - dummyText.setSingleLine(); - dummyText.setTextAppearance(context, R.style.ToolbarTextAppearanceNormal); - Toolbar.LayoutParams l1 = new Toolbar.LayoutParams(Toolbar.LayoutParams.WRAP_CONTENT, Toolbar.LayoutParams.WRAP_CONTENT); - dummyText.setLayoutParams(l1); - } -} diff --git a/app/src/main/java/code/name/monkey/retromusic/adapter/base/AbsMultiSelectAdapter.kt b/app/src/main/java/code/name/monkey/retromusic/adapter/base/AbsMultiSelectAdapter.kt new file mode 100644 index 00000000..28975911 --- /dev/null +++ b/app/src/main/java/code/name/monkey/retromusic/adapter/base/AbsMultiSelectAdapter.kt @@ -0,0 +1,123 @@ +package code.name.monkey.retromusic.adapter.base + +import android.annotation.SuppressLint +import android.graphics.Color +import android.view.Menu +import android.view.MenuItem +import androidx.annotation.MenuRes +import androidx.fragment.app.FragmentActivity +import androidx.recyclerview.widget.RecyclerView +import code.name.monkey.appthemehelper.util.ATHUtil +import code.name.monkey.retromusic.R +import code.name.monkey.retromusic.interfaces.ICabCallback +import code.name.monkey.retromusic.interfaces.ICabHolder +import code.name.monkey.retromusic.util.RetroColorUtil +import com.afollestad.materialcab.attached.AttachedCab +import com.afollestad.materialcab.attached.destroy +import com.afollestad.materialcab.attached.isActive +import java.util.* + +abstract class AbsMultiSelectAdapter( + open val activity: FragmentActivity, private val ICabHolder: ICabHolder?, @MenuRes menuRes: Int +) : RecyclerView.Adapter(), ICabCallback { + private var cab: AttachedCab? = null + private val checked: MutableList + private var menuRes: Int + override fun onCabCreated(cab: AttachedCab, menu: Menu): Boolean { + activity.window.statusBarColor = + RetroColorUtil.shiftBackgroundColor(ATHUtil.resolveColor(activity, R.attr.colorSurface)) + return true + } + + override fun onCabFinished(cab: AttachedCab): Boolean { + clearChecked() + activity.window.statusBarColor = Color.TRANSPARENT + return true + } + + override fun onCabItemClicked(item: MenuItem): Boolean { + if (item.itemId == R.id.action_multi_select_adapter_check_all) { + checkAll() + } else { + onMultipleItemAction(item, ArrayList(checked)) + cab?.destroy() + clearChecked() + } + return true + } + + private fun checkAll() { + if (ICabHolder != null) { + checked.clear() + for (i in 0 until itemCount) { + val identifier = getIdentifier(i) + if (identifier != null) { + checked.add(identifier) + } + } + notifyDataSetChanged() + updateCab() + } + } + + protected abstract fun getIdentifier(position: Int): I? + protected open fun getName(i: I): String? { + return i.toString() + } + + protected fun isChecked(identifier: I): Boolean { + return checked.contains(identifier) + } + + protected val isInQuickSelectMode: Boolean + get() = cab != null && cab!!.isActive() + + protected abstract fun onMultipleItemAction(menuItem: MenuItem, selection: List) + protected fun setMultiSelectMenuRes(@MenuRes menuRes: Int) { + this.menuRes = menuRes + } + + protected fun toggleChecked(position: Int): Boolean { + if (ICabHolder != null) { + val identifier = getIdentifier(position) ?: return false + if (!checked.remove(identifier)) { + checked.add(identifier) + } + notifyItemChanged(position) + updateCab() + return true + } + return false + } + + private fun clearChecked() { + checked.clear() + notifyDataSetChanged() + } + + @SuppressLint("StringFormatInvalid", "StringFormatMatches") + private fun updateCab() { + if (ICabHolder != null) { + if (cab == null || !cab!!.isActive()) { + cab = ICabHolder.openCab(menuRes, this) + } + val size = checked.size + when { + size <= 0 -> { + cab?.destroy() + } + size == 1 -> { + cab?.title(literal = getName(checked[0])) + } + else -> { + cab?.title(literal = activity.getString(R.string.x_selected, size)) + } + } + } + } + + init { + checked = ArrayList() + this.menuRes = menuRes + } +} \ No newline at end of file diff --git a/app/src/main/java/code/name/monkey/retromusic/adapter/base/MediaEntryViewHolder.java b/app/src/main/java/code/name/monkey/retromusic/adapter/base/MediaEntryViewHolder.java index 16dbc1c5..d68186c0 100644 --- a/app/src/main/java/code/name/monkey/retromusic/adapter/base/MediaEntryViewHolder.java +++ b/app/src/main/java/code/name/monkey/retromusic/adapter/base/MediaEntryViewHolder.java @@ -16,13 +16,13 @@ package code.name.monkey.retromusic.adapter.base; import android.graphics.Color; import android.view.View; +import android.widget.FrameLayout; import android.widget.ImageView; import android.widget.TextView; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.appcompat.widget.AppCompatImageView; -import androidx.recyclerview.widget.RecyclerView; import com.google.android.material.card.MaterialCardView; import com.h6ah4i.android.widget.advrecyclerview.utils.AbstractDraggableSwipeableItemViewHolder; @@ -41,15 +41,12 @@ public class MediaEntryViewHolder extends AbstractDraggableSwipeableItemViewHold @Nullable public ImageView image; - @Nullable - public ImageView artistImage; - - @Nullable - public ImageView playerImage; - @Nullable public MaterialCardView imageContainerCard; + @Nullable + public FrameLayout imageContainer; + @Nullable public TextView imageText; @@ -65,10 +62,6 @@ public class MediaEntryViewHolder extends AbstractDraggableSwipeableItemViewHold @Nullable public View paletteColorContainer; - - @Nullable - public RecyclerView recyclerView; - @Nullable public TextView text; @@ -88,18 +81,16 @@ public class MediaEntryViewHolder extends AbstractDraggableSwipeableItemViewHold text2 = itemView.findViewById(R.id.text2); image = itemView.findViewById(R.id.image); - artistImage = itemView.findViewById(R.id.artistImage); - playerImage = itemView.findViewById(R.id.player_image); time = itemView.findViewById(R.id.time); imageText = itemView.findViewById(R.id.imageText); imageTextContainer = itemView.findViewById(R.id.imageTextContainer); imageContainerCard = itemView.findViewById(R.id.imageContainerCard); + imageContainer = itemView.findViewById(R.id.imageContainer); menu = itemView.findViewById(R.id.menu); dragView = itemView.findViewById(R.id.drag_view); paletteColorContainer = itemView.findViewById(R.id.paletteColorContainer); - recyclerView = itemView.findViewById(R.id.recycler_view); mask = itemView.findViewById(R.id.mask); dummyContainer = itemView.findViewById(R.id.dummy_view); diff --git a/app/src/main/java/code/name/monkey/retromusic/adapter/playlist/PlaylistAdapter.kt b/app/src/main/java/code/name/monkey/retromusic/adapter/playlist/PlaylistAdapter.kt index afcb4ce8..4fd2994b 100755 --- a/app/src/main/java/code/name/monkey/retromusic/adapter/playlist/PlaylistAdapter.kt +++ b/app/src/main/java/code/name/monkey/retromusic/adapter/playlist/PlaylistAdapter.kt @@ -41,7 +41,7 @@ import code.name.monkey.retromusic.model.Song import code.name.monkey.retromusic.util.MusicUtil class PlaylistAdapter( - private val activity: FragmentActivity, + override val activity: FragmentActivity, private var dataSet: List, private var itemLayoutRes: Int, ICabHolder: ICabHolder?, diff --git a/app/src/main/java/code/name/monkey/retromusic/adapter/song/PlayingQueueAdapter.kt b/app/src/main/java/code/name/monkey/retromusic/adapter/song/PlayingQueueAdapter.kt index 46b99c23..506eb1c1 100644 --- a/app/src/main/java/code/name/monkey/retromusic/adapter/song/PlayingQueueAdapter.kt +++ b/app/src/main/java/code/name/monkey/retromusic/adapter/song/PlayingQueueAdapter.kt @@ -16,7 +16,6 @@ package code.name.monkey.retromusic.adapter.song import android.view.MenuItem import android.view.View -import androidx.appcompat.app.AppCompatActivity import androidx.fragment.app.FragmentActivity import code.name.monkey.retromusic.R import code.name.monkey.retromusic.glide.GlideApp @@ -41,7 +40,7 @@ import com.h6ah4i.android.widget.advrecyclerview.swipeable.action.SwipeResultAct import me.zhanghai.android.fastscroll.PopupTextProvider class PlayingQueueAdapter( - activity: AppCompatActivity, + activity: FragmentActivity, dataSet: MutableList, private var current: Int, itemLayoutRes: Int diff --git a/app/src/main/java/code/name/monkey/retromusic/adapter/song/ShuffleButtonSongAdapter.kt b/app/src/main/java/code/name/monkey/retromusic/adapter/song/ShuffleButtonSongAdapter.kt index 989c4116..329c6874 100644 --- a/app/src/main/java/code/name/monkey/retromusic/adapter/song/ShuffleButtonSongAdapter.kt +++ b/app/src/main/java/code/name/monkey/retromusic/adapter/song/ShuffleButtonSongAdapter.kt @@ -16,10 +16,9 @@ package code.name.monkey.retromusic.adapter.song import android.view.View import androidx.fragment.app.FragmentActivity -import code.name.monkey.appthemehelper.ThemeStore import code.name.monkey.retromusic.R -import code.name.monkey.retromusic.extensions.applyColor -import code.name.monkey.retromusic.extensions.applyOutlineColor +import code.name.monkey.retromusic.extensions.accentColor +import code.name.monkey.retromusic.extensions.accentOutlineColor import code.name.monkey.retromusic.helper.MusicPlayerRemote import code.name.monkey.retromusic.interfaces.ICabHolder import code.name.monkey.retromusic.model.Song @@ -45,19 +44,18 @@ class ShuffleButtonSongAdapter( override fun onBindViewHolder(holder: SongAdapter.ViewHolder, position: Int) { if (holder.itemViewType == OFFSET_ITEM) { - val color = ThemeStore.accentColor(activity) val viewHolder = holder as ViewHolder viewHolder.playAction?.let { it.setOnClickListener { MusicPlayerRemote.openQueue(dataSet, 0, true) } - it.applyOutlineColor(color) + it.accentOutlineColor() } viewHolder.shuffleAction?.let { it.setOnClickListener { MusicPlayerRemote.openAndShuffleQueue(dataSet, true) } - it.applyColor(color) + it.accentColor() } } else { super.onBindViewHolder(holder, position - 1) diff --git a/app/src/main/java/code/name/monkey/retromusic/adapter/song/SongAdapter.kt b/app/src/main/java/code/name/monkey/retromusic/adapter/song/SongAdapter.kt index b828f664..2a247722 100644 --- a/app/src/main/java/code/name/monkey/retromusic/adapter/song/SongAdapter.kt +++ b/app/src/main/java/code/name/monkey/retromusic/adapter/song/SongAdapter.kt @@ -37,12 +37,13 @@ import code.name.monkey.retromusic.helper.MusicPlayerRemote import code.name.monkey.retromusic.helper.SortOrder import code.name.monkey.retromusic.helper.menu.SongMenuHelper import code.name.monkey.retromusic.helper.menu.SongsMenuHelper +import code.name.monkey.retromusic.interfaces.ICabCallback import code.name.monkey.retromusic.interfaces.ICabHolder import code.name.monkey.retromusic.model.Song import code.name.monkey.retromusic.util.MusicUtil import code.name.monkey.retromusic.util.PreferenceUtil +import code.name.monkey.retromusic.util.RetroUtil import code.name.monkey.retromusic.util.color.MediaNotificationProcessor -import com.afollestad.materialcab.MaterialCab import me.zhanghai.android.fastscroll.PopupTextProvider /** @@ -50,7 +51,7 @@ import me.zhanghai.android.fastscroll.PopupTextProvider */ open class SongAdapter( - protected val activity: FragmentActivity, + override val activity: FragmentActivity, var dataSet: MutableList, protected var itemLayoutRes: Int, ICabHolder: ICabHolder?, @@ -59,7 +60,7 @@ open class SongAdapter( activity, ICabHolder, R.menu.menu_media_selection -), MaterialCab.Callback, PopupTextProvider { +), ICabCallback, PopupTextProvider { private var showSectionName = true @@ -104,6 +105,10 @@ open class SongAdapter( holder.text?.text = getSongText(song) holder.text2?.text = getSongText(song) loadAlbumCover(song, holder) + val landscape = RetroUtil.isLandscape() + if ((PreferenceUtil.songGridSize > 2 && !landscape) || (PreferenceUtil.songGridSizeLand > 5 && landscape)) { + holder.menu?.isVisible = false + } } private fun setColors(color: MediaNotificationProcessor, holder: ViewHolder) { diff --git a/app/src/main/java/code/name/monkey/retromusic/auto/AutoMediaIDHelper.java b/app/src/main/java/code/name/monkey/retromusic/auto/AutoMediaIDHelper.java index e087e7e7..3d1f4815 100644 --- a/app/src/main/java/code/name/monkey/retromusic/auto/AutoMediaIDHelper.java +++ b/app/src/main/java/code/name/monkey/retromusic/auto/AutoMediaIDHelper.java @@ -35,6 +35,7 @@ public class AutoMediaIDHelper { public static final String MEDIA_ID_MUSICS_BY_GENRE = "__BY_GENRE__"; public static final String MEDIA_ID_MUSICS_BY_SHUFFLE = "__BY_SHUFFLE__"; public static final String MEDIA_ID_MUSICS_BY_QUEUE = "__BY_QUEUE__"; + public static final String RECENT_ROOT = "__RECENT__"; private static final String CATEGORY_SEPARATOR = "__/__"; private static final String LEAF_SEPARATOR = "__|__"; diff --git a/app/src/main/java/code/name/monkey/retromusic/auto/AutoMusicProvider.kt b/app/src/main/java/code/name/monkey/retromusic/auto/AutoMusicProvider.kt index 1f4f4d5d..1e0751aa 100644 --- a/app/src/main/java/code/name/monkey/retromusic/auto/AutoMusicProvider.kt +++ b/app/src/main/java/code/name/monkey/retromusic/auto/AutoMusicProvider.kt @@ -15,7 +15,6 @@ package code.name.monkey.retromusic.auto import android.content.Context import android.content.res.Resources -import android.net.Uri import android.support.v4.media.MediaBrowserCompat import code.name.monkey.retromusic.R import code.name.monkey.retromusic.helper.MusicPlayerRemote diff --git a/app/src/main/java/code/name/monkey/retromusic/dialogs/SongDetailDialog.kt b/app/src/main/java/code/name/monkey/retromusic/dialogs/SongDetailDialog.kt index c363f8b4..08ddbbe1 100644 --- a/app/src/main/java/code/name/monkey/retromusic/dialogs/SongDetailDialog.kt +++ b/app/src/main/java/code/name/monkey/retromusic/dialogs/SongDetailDialog.kt @@ -14,7 +14,6 @@ */ package code.name.monkey.retromusic.dialogs -import android.annotation.SuppressLint import android.app.Dialog import android.content.Context import android.os.Bundle @@ -42,7 +41,6 @@ import java.io.IOException class SongDetailDialog : DialogFragment() { - @SuppressLint("InflateParams") override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { val context: Context = requireContext() val dialogView = LayoutInflater.from(context).inflate(R.layout.dialog_file_details, null) diff --git a/app/src/main/java/code/name/monkey/retromusic/extensions/ActivityEx.kt b/app/src/main/java/code/name/monkey/retromusic/extensions/ActivityExtensions.kt similarity index 96% rename from app/src/main/java/code/name/monkey/retromusic/extensions/ActivityEx.kt rename to app/src/main/java/code/name/monkey/retromusic/extensions/ActivityExtensions.kt index 99176589..ff33f4ad 100644 --- a/app/src/main/java/code/name/monkey/retromusic/extensions/ActivityEx.kt +++ b/app/src/main/java/code/name/monkey/retromusic/extensions/ActivityExtensions.kt @@ -21,7 +21,7 @@ import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper import com.google.android.material.appbar.MaterialToolbar fun AppCompatActivity.applyToolbar(toolbar: MaterialToolbar) { - toolbar.setBackgroundColor(surfaceColor()) + //toolbar.setBackgroundColor(surfaceColor()) ToolbarContentTintHelper.colorBackButton(toolbar) setSupportActionBar(toolbar) } diff --git a/app/src/main/java/code/name/monkey/retromusic/extensions/ColorExt.kt b/app/src/main/java/code/name/monkey/retromusic/extensions/ColorExtensions.kt similarity index 80% rename from app/src/main/java/code/name/monkey/retromusic/extensions/ColorExt.kt rename to app/src/main/java/code/name/monkey/retromusic/extensions/ColorExtensions.kt index c15a821a..8c0bb5b3 100644 --- a/app/src/main/java/code/name/monkey/retromusic/extensions/ColorExt.kt +++ b/app/src/main/java/code/name/monkey/retromusic/extensions/ColorExtensions.kt @@ -29,6 +29,7 @@ import androidx.annotation.ColorRes import androidx.appcompat.widget.AppCompatImageView import androidx.appcompat.widget.Toolbar import androidx.core.content.ContextCompat +import androidx.core.graphics.ColorUtils import androidx.core.graphics.drawable.DrawableCompat import androidx.fragment.app.Fragment import code.name.monkey.appthemehelper.ThemeStore @@ -37,6 +38,7 @@ import code.name.monkey.appthemehelper.util.ColorUtil import code.name.monkey.appthemehelper.util.MaterialValueHelper import code.name.monkey.retromusic.App import code.name.monkey.retromusic.R +import code.name.monkey.retromusic.util.PreferenceUtil.materialYou import com.google.android.material.button.MaterialButton import com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton import com.google.android.material.floatingactionbutton.FloatingActionButton @@ -84,23 +86,32 @@ fun Fragment.resolveColor(@AttrRes attr: Int, fallBackColor: Int = 0) = fun Dialog.resolveColor(@AttrRes attr: Int, fallBackColor: Int = 0) = ATHUtil.resolveColor(context, attr, fallBackColor) +// Don't apply accent colors if Material You is enabled +// Material Components will take care of applying material you colors fun CheckBox.addAccentColor() { + if (materialYou) return buttonTintList = ColorStateList.valueOf(ThemeStore.accentColor(context)) } fun SeekBar.addAccentColor() { + if (materialYou) return val colorState = ColorStateList.valueOf(ThemeStore.accentColor(context)) progressTintList = colorState thumbTintList = colorState } -fun Button.accentTextColor() = setTextColor(ThemeStore.accentColor(App.getContext())) +fun Button.accentTextColor() { + if (materialYou) return + setTextColor(ThemeStore.accentColor(App.getContext())) +} fun MaterialButton.accentBackgroundColor() { + if (materialYou) return backgroundTintList = ColorStateList.valueOf(ThemeStore.accentColor(App.getContext())) } fun MaterialButton.accentOutlineColor() { + if (materialYou) return val color = ThemeStore.accentColor(context) val colorStateList = ColorStateList.valueOf(color) iconTint = colorStateList @@ -116,6 +127,7 @@ fun SeekBar.applyColor(@ColorInt color: Int) { } fun ExtendedFloatingActionButton.accentColor() { + if (materialYou) return val color = ThemeStore.accentColor(context) val textColor = MaterialValueHelper.getPrimaryTextColor(context, ColorUtil.isColorLight(color)) val colorStateList = ColorStateList.valueOf(color) @@ -126,12 +138,11 @@ fun ExtendedFloatingActionButton.accentColor() { } fun FloatingActionButton.accentColor() { + if (materialYou) return val color = ThemeStore.accentColor(context) val textColor = MaterialValueHelper.getPrimaryTextColor(context, ColorUtil.isColorLight(color)) - val colorStateList = ColorStateList.valueOf(color) - val textColorStateList = ColorStateList.valueOf(textColor) - backgroundTintList = colorStateList - imageTintList = textColorStateList + backgroundTintList = ColorStateList.valueOf(color) + imageTintList = ColorStateList.valueOf(textColor) } fun MaterialButton.applyColor(color: Int) { @@ -147,15 +158,21 @@ fun MaterialButton.applyColor(color: Int) { iconTint = textColorColorStateList } -fun MaterialButton.accentColor() = applyColor(ThemeStore.accentColor(context)) +fun MaterialButton.accentColor() { + if (materialYou) return + applyColor(ThemeStore.accentColor(context)) +} fun MaterialButton.applyOutlineColor(color: Int) { - val textColorColorStateList = ColorStateList.valueOf(color) - setTextColor(textColorColorStateList) - iconTint = textColorColorStateList + val colorStateList = ColorStateList.valueOf(color) + iconTint = colorStateList + strokeColor = colorStateList + setTextColor(colorStateList) + rippleColor = colorStateList } fun TextInputLayout.accentColor() { + if (materialYou) return val accentColor = ThemeStore.accentColor(context) val colorState = ColorStateList.valueOf(accentColor) boxStrokeColor = accentColor @@ -164,6 +181,7 @@ fun TextInputLayout.accentColor() { } fun CircularProgressIndicator.accentColor() { + if (materialYou) return val color = ThemeStore.accentColor(context) setIndicatorColor(color) trackColor = ColorUtil.withAlpha(color, 0.2f) @@ -176,6 +194,21 @@ fun CircularProgressIndicator.applyColor(color: Int) { fun AppCompatImageView.accentColor(): Int = ThemeStore.accentColor(context) +fun TextInputLayout.setTint(background: Boolean = true) { + if (materialYou) return + val accentColor = ThemeStore.accentColor(context) + val colorState = ColorStateList.valueOf(accentColor) + + if (background) { + backgroundTintList = colorState + defaultHintTextColor = colorState + } else { + boxStrokeColor = accentColor + defaultHintTextColor = colorState + isHintAnimationEnabled = true + } +} + @CheckResult fun Drawable.tint(@ColorInt color: Int): Drawable { val tintedDrawable = DrawableCompat.wrap(this).mutate() @@ -191,3 +224,15 @@ fun Drawable.tint(context: Context, @ColorRes color: Int): Drawable = fun Context.getColorCompat(@ColorRes colorRes: Int): Int { return ContextCompat.getColor(this, colorRes) } + +@ColorInt +fun Context.darkAccentColor(): Int { + return ColorUtils.blendARGB( + accentColor(), + surfaceColor(), + if (surfaceColor().isColorLight) 0.96f else 0.975f + ) +} + +inline val @receiver:ColorInt Int.isColorLight + get() = ColorUtil.isColorLight(this) diff --git a/app/src/main/java/code/name/monkey/retromusic/extensions/DrawableExt.kt b/app/src/main/java/code/name/monkey/retromusic/extensions/DrawableExtensions.kt similarity index 100% rename from app/src/main/java/code/name/monkey/retromusic/extensions/DrawableExt.kt rename to app/src/main/java/code/name/monkey/retromusic/extensions/DrawableExtensions.kt diff --git a/app/src/main/java/code/name/monkey/retromusic/extensions/FragmentExt.kt b/app/src/main/java/code/name/monkey/retromusic/extensions/FragmentExtensions.kt similarity index 91% rename from app/src/main/java/code/name/monkey/retromusic/extensions/FragmentExt.kt rename to app/src/main/java/code/name/monkey/retromusic/extensions/FragmentExtensions.kt index 6c74050a..24fc4dd3 100644 --- a/app/src/main/java/code/name/monkey/retromusic/extensions/FragmentExt.kt +++ b/app/src/main/java/code/name/monkey/retromusic/extensions/FragmentExtensions.kt @@ -19,16 +19,14 @@ import android.content.res.Configuration import android.graphics.drawable.Drawable import android.os.PowerManager import android.widget.Toast -import androidx.annotation.DrawableRes -import androidx.annotation.IdRes -import androidx.annotation.IntegerRes -import androidx.annotation.StringRes +import androidx.annotation.* import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.content.res.AppCompatResources import androidx.fragment.app.Fragment import androidx.fragment.app.FragmentManager import androidx.navigation.fragment.NavHostFragment import code.name.monkey.retromusic.util.PreferenceUtil +import com.google.android.material.appbar.MaterialToolbar fun Fragment.getIntRes(@IntegerRes int: Int): Int { return resources.getInteger(int) @@ -97,3 +95,11 @@ fun Context.getDrawableCompat(@DrawableRes drawableRes: Int): Drawable { fun Fragment.getDrawableCompat(@DrawableRes drawableRes: Int): Drawable { return AppCompatResources.getDrawable(requireContext(), drawableRes)!! } + +fun Fragment.applyToolbar(toolbar: MaterialToolbar) { + (requireActivity() as AppCompatActivity).applyToolbar(toolbar) +} + +fun Fragment.dip(@DimenRes id: Int): Int { + return resources.getDimensionPixelSize(id) +} \ No newline at end of file diff --git a/app/src/main/java/code/name/monkey/retromusic/extensions/InsetsExtensions.kt b/app/src/main/java/code/name/monkey/retromusic/extensions/InsetsExtensions.kt new file mode 100644 index 00000000..b24168a3 --- /dev/null +++ b/app/src/main/java/code/name/monkey/retromusic/extensions/InsetsExtensions.kt @@ -0,0 +1,7 @@ +package code.name.monkey.retromusic.extensions + +import androidx.core.view.WindowInsetsCompat + +fun WindowInsetsCompat?.safeGetBottomInsets(): Int { + return this?.getInsets(WindowInsetsCompat.Type.systemBars())?.bottom ?: 0 +} diff --git a/app/src/main/java/code/name/monkey/retromusic/extensions/PaletteEX.kt b/app/src/main/java/code/name/monkey/retromusic/extensions/PaletteExtensions.kt similarity index 100% rename from app/src/main/java/code/name/monkey/retromusic/extensions/PaletteEX.kt rename to app/src/main/java/code/name/monkey/retromusic/extensions/PaletteExtensions.kt diff --git a/app/src/main/java/code/name/monkey/retromusic/extensions/Preference.kt b/app/src/main/java/code/name/monkey/retromusic/extensions/PreferenceExtensions.kt similarity index 100% rename from app/src/main/java/code/name/monkey/retromusic/extensions/Preference.kt rename to app/src/main/java/code/name/monkey/retromusic/extensions/PreferenceExtensions.kt diff --git a/app/src/main/java/code/name/monkey/retromusic/extensions/ViewExtensions.kt b/app/src/main/java/code/name/monkey/retromusic/extensions/ViewExtensions.kt index 1e4d7745..8571fe8b 100644 --- a/app/src/main/java/code/name/monkey/retromusic/extensions/ViewExtensions.kt +++ b/app/src/main/java/code/name/monkey/retromusic/extensions/ViewExtensions.kt @@ -14,19 +14,25 @@ */ package code.name.monkey.retromusic.extensions +import android.animation.Animator import android.animation.ObjectAnimator import android.content.Context import android.view.LayoutInflater import android.view.View import android.view.ViewGroup +import android.view.ViewGroup.MarginLayoutParams import android.view.ViewTreeObserver import android.view.inputmethod.InputMethodManager import android.widget.EditText import androidx.annotation.LayoutRes import androidx.core.animation.doOnEnd import androidx.core.animation.doOnStart +import androidx.core.view.* import code.name.monkey.appthemehelper.ThemeStore import code.name.monkey.appthemehelper.util.TintHelper +import code.name.monkey.retromusic.util.PreferenceUtil +import code.name.monkey.retromusic.util.RetroUtil +import com.afollestad.materialdialogs.utils.MDUtil.updatePadding import com.google.android.material.bottomsheet.BottomSheetBehavior import com.google.android.material.imageview.ShapeableImageView import com.google.android.material.shape.ShapeAppearanceModel @@ -51,12 +57,13 @@ fun View.hidden() { fun View.showOrHide(show: Boolean) = if (show) show() else hide() fun EditText.appHandleColor(): EditText { + if (PreferenceUtil.materialYou) return this TintHelper.colorHandles(this, ThemeStore.accentColor(context)) return this } -fun View.translateYAnimate(value: Float) { - ObjectAnimator.ofFloat(this, "translationY", value) +fun View.translateYAnimate(value: Float): Animator { + return ObjectAnimator.ofFloat(this, "translationY", value) .apply { duration = 300 doOnStart { @@ -122,4 +129,114 @@ fun ShapeableImageView.setCircleShape(boolean: Boolean) { val radius = width / 2f shapeAppearanceModel = ShapeAppearanceModel().withCornerSize(radius) } -} \ No newline at end of file +} + + +/** + * This will draw our view above the navigation bar instead of behind it by adding margins. + */ +fun View.drawAboveSystemBars(onlyPortrait: Boolean = true) { + if (onlyPortrait && RetroUtil.isLandscape()) return + // Create a snapshot of the view's margin state + val initialMargin = recordInitialMarginForView(this) + ViewCompat.setOnApplyWindowInsetsListener( + (this) + ) { _: View, windowInsets: WindowInsetsCompat -> + val insets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars()) + // Apply the insets as a margin to the view. + updateLayoutParams { + leftMargin = initialMargin.left + insets.left + bottomMargin = initialMargin.bottom + insets.bottom + rightMargin = initialMargin.right + insets.right + } + windowInsets + } +} + +/** + * This will draw our view above the navigation bar instead of behind it by adding padding. + */ +fun View.drawAboveSystemBarsWithPadding(consume: Boolean = false) { + val initialPadding = recordInitialPaddingForView(this) + + ViewCompat.setOnApplyWindowInsetsListener( + (this) + ) { v: View, windowInsets: WindowInsetsCompat -> + val insets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars()) + v.updatePadding( + left = initialPadding.left + insets.left, + bottom = initialPadding.bottom + insets.bottom, + right = initialPadding.right + insets.right + ) + if (consume) WindowInsetsCompat.CONSUMED else windowInsets + } + requestApplyInsetsWhenAttached() +} + +fun View.requestApplyInsetsWhenAttached() { + if (isAttachedToWindow) { + // We're already attached, just request as normal + requestApplyInsets() + } else { + // We're not attached to the hierarchy, add a listener to + // request when we are + addOnAttachStateChangeListener(object : View.OnAttachStateChangeListener { + override fun onViewAttachedToWindow(v: View) { + v.removeOnAttachStateChangeListener(this) + v.requestApplyInsets() + } + + override fun onViewDetachedFromWindow(v: View) = Unit + }) + } +} + +fun View.drawNextToNavbar() { + val initialPadding = recordInitialPaddingForView(this) + + ViewCompat.setOnApplyWindowInsetsListener( + (this) + ) { v: View, windowInsets: WindowInsetsCompat -> + val insets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars()) + v.updatePadding( + left = initialPadding.left + insets.left, + right = initialPadding.right + insets.right + ) + windowInsets + } + requestApplyInsetsWhenAttached() +} + +fun View.addBottomInsets() { + // Create a snapshot of the view's margin state + val initialMargin = recordInitialMarginForView(this) + ViewCompat.setOnApplyWindowInsetsListener( + (this) + ) { _: View, windowInsets: WindowInsetsCompat -> + val insets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars()) + // Apply the insets as a margin to the view. + updateLayoutParams { + bottomMargin = initialMargin.bottom + insets.bottom + } + windowInsets + } +} + +data class InitialMargin( + val left: Int, val top: Int, + val right: Int, val bottom: Int +) + +fun recordInitialMarginForView(view: View) = InitialMargin( + view.marginLeft, view.marginTop, view.marginRight, view.marginBottom +) + + +data class InitialPadding( + val left: Int, val top: Int, + val right: Int, val bottom: Int +) + +private fun recordInitialPaddingForView(view: View) = InitialPadding( + view.paddingLeft, view.paddingTop, view.paddingRight, view.paddingBottom +) diff --git a/app/src/main/java/code/name/monkey/retromusic/fragments/LibraryViewModel.kt b/app/src/main/java/code/name/monkey/retromusic/fragments/LibraryViewModel.kt index c474823f..8a5aea2c 100644 --- a/app/src/main/java/code/name/monkey/retromusic/fragments/LibraryViewModel.kt +++ b/app/src/main/java/code/name/monkey/retromusic/fragments/LibraryViewModel.kt @@ -19,10 +19,12 @@ import androidx.lifecycle.* import code.name.monkey.retromusic.* import code.name.monkey.retromusic.db.* import code.name.monkey.retromusic.fragments.ReloadType.* +import code.name.monkey.retromusic.fragments.search.Filter import code.name.monkey.retromusic.helper.MusicPlayerRemote import code.name.monkey.retromusic.interfaces.IMusicServiceEventListener import code.name.monkey.retromusic.model.* import code.name.monkey.retromusic.repository.RealRepository +import code.name.monkey.retromusic.util.DensityUtil import code.name.monkey.retromusic.util.PreferenceUtil import kotlinx.coroutines.Dispatchers.IO import kotlinx.coroutines.launch @@ -40,6 +42,7 @@ class LibraryViewModel( private val legacyPlaylists = MutableLiveData>() private val genres = MutableLiveData>() private val searchResults = MutableLiveData>() + private val fabMargin = MutableLiveData(0) val paletteColor: LiveData = _paletteColor init { @@ -85,6 +88,10 @@ class LibraryViewModel( return home } + fun getFabMargin(): LiveData { + return fabMargin + } + private fun fetchSongs() { viewModelScope.launch(IO) { songs.postValue(repository.allSongs()) @@ -133,9 +140,9 @@ class LibraryViewModel( } } - fun search(query: String?, filters: List) { + fun search(query: String?, filter: Filter) { viewModelScope.launch(IO) { - val result = repository.search(query, filters) + val result = repository.search(query, filter) searchResults.postValue(result) } } @@ -328,6 +335,14 @@ class LibraryViewModel( } } } + + fun setFabMargin(bottomMargin: Int) { + fabMargin.postValue( + // Normal Margin + DensityUtil.dip2px(App.getContext(), 16F) + + bottomMargin + ) + } } enum class ReloadType { diff --git a/app/src/main/java/code/name/monkey/retromusic/fragments/about/AboutFragment.kt b/app/src/main/java/code/name/monkey/retromusic/fragments/about/AboutFragment.kt index 72cac439..3bbe5e39 100644 --- a/app/src/main/java/code/name/monkey/retromusic/fragments/about/AboutFragment.kt +++ b/app/src/main/java/code/name/monkey/retromusic/fragments/about/AboutFragment.kt @@ -20,6 +20,7 @@ import android.net.Uri import android.os.Bundle import android.view.View import androidx.core.app.ShareCompat +import androidx.core.view.updatePadding import androidx.fragment.app.Fragment import androidx.recyclerview.widget.DefaultItemAnimator import androidx.recyclerview.widget.LinearLayoutManager @@ -30,6 +31,7 @@ import code.name.monkey.retromusic.adapter.ContributorAdapter import code.name.monkey.retromusic.databinding.FragmentAboutBinding import code.name.monkey.retromusic.fragments.LibraryViewModel import code.name.monkey.retromusic.util.NavigationUtil +import code.name.monkey.retromusic.util.RetroUtil import org.koin.androidx.viewmodel.ext.android.sharedViewModel class AboutFragment : Fragment(R.layout.fragment_about), View.OnClickListener { @@ -43,6 +45,12 @@ class AboutFragment : Fragment(R.layout.fragment_about), View.OnClickListener { binding.aboutContent.cardOther.version.setSummary(getAppVersion()) setUpView() loadContributors() + // This is a workaround as CollapsingToolbarLayout consumes insets and + // insets are not passed to child views + // https://github.com/material-components/material-components-android/issues/1310 + if (!RetroUtil.isLandscape()) { + binding.root.updatePadding(bottom = RetroUtil.getNavigationBarHeight()) + } } private fun openUrl(url: String) { diff --git a/app/src/main/java/code/name/monkey/retromusic/fragments/albums/AlbumDetailsFragment.kt b/app/src/main/java/code/name/monkey/retromusic/fragments/albums/AlbumDetailsFragment.kt index c9291185..5075ca1a 100644 --- a/app/src/main/java/code/name/monkey/retromusic/fragments/albums/AlbumDetailsFragment.kt +++ b/app/src/main/java/code/name/monkey/retromusic/fragments/albums/AlbumDetailsFragment.kt @@ -57,6 +57,7 @@ import code.name.monkey.retromusic.helper.SortOrder.AlbumSongSortOrder.Companion import code.name.monkey.retromusic.helper.SortOrder.AlbumSongSortOrder.Companion.SONG_TRACK_LIST import code.name.monkey.retromusic.helper.SortOrder.AlbumSongSortOrder.Companion.SONG_Z_A import code.name.monkey.retromusic.interfaces.IAlbumClickListener +import code.name.monkey.retromusic.interfaces.ICabCallback import code.name.monkey.retromusic.interfaces.ICabHolder import code.name.monkey.retromusic.model.Album import code.name.monkey.retromusic.model.Artist @@ -68,7 +69,11 @@ import code.name.monkey.retromusic.util.PreferenceUtil import code.name.monkey.retromusic.util.RetroColorUtil import code.name.monkey.retromusic.util.RetroUtil import code.name.monkey.retromusic.util.color.MediaNotificationProcessor -import com.afollestad.materialcab.MaterialCab +import com.afollestad.materialcab.attached.AttachedCab +import com.afollestad.materialcab.attached.destroy +import com.afollestad.materialcab.attached.isActive +import com.afollestad.materialcab.createCab +import com.google.android.material.shape.MaterialShapeDrawable import com.google.android.material.transition.MaterialArcMotion import com.google.android.material.transition.MaterialContainerTransform import kotlinx.coroutines.Dispatchers @@ -101,7 +106,6 @@ class AlbumDetailsFragment : AbsMainActivityFragment(R.layout.fragment_album_det super.onCreate(savedInstanceState) sharedElementEnterTransition = MaterialContainerTransform().apply { drawingViewId = R.id.fragment_container - duration = 300L scrimColor = Color.TRANSPARENT setAllContainerColors(requireContext().resolveColor(R.attr.colorSurface)) setPathMotion(MaterialArcMotion()) @@ -176,6 +180,8 @@ class AlbumDetailsFragment : AbsMainActivityFragment(R.layout.fragment_album_det requireActivity().onBackPressed() } } + binding.appBarLayout?.statusBarForeground = + MaterialShapeDrawable.createWithElevationOverlay(requireContext()) } override fun onDestroy() { @@ -228,9 +234,10 @@ class AlbumDetailsFragment : AbsMainActivityFragment(R.layout.fragment_album_det loadAlbumCover(album) simpleSongAdapter.swapDataSet(album.songs) if (albumArtistExists) { - detailsViewModel.getAlbumArtist(album.albumArtist.toString()).observe(viewLifecycleOwner, { - loadArtistImage(it) - }) + detailsViewModel.getAlbumArtist(album.albumArtist.toString()) + .observe(viewLifecycleOwner, { + loadArtistImage(it) + }) } else { detailsViewModel.getArtist(album.artistId).observe(viewLifecycleOwner, { loadArtistImage(it) @@ -302,7 +309,12 @@ class AlbumDetailsFragment : AbsMainActivityFragment(R.layout.fragment_album_det }) GlideApp.with(requireContext()).asBitmapPalette().artistImageOptions(artist) //.forceDownload(PreferenceUtil.isAllowedToDownloadMetadata()) - .load(RetroGlideExtension.getArtistModel(artist, PreferenceUtil.isAllowedToDownloadMetadata())) + .load( + RetroGlideExtension.getArtistModel( + artist, + PreferenceUtil.isAllowedToDownloadMetadata() + ) + ) .dontAnimate() .dontTransform() .into(object : RetroMusicColoredTarget(binding.artistImage) { @@ -312,7 +324,8 @@ class AlbumDetailsFragment : AbsMainActivityFragment(R.layout.fragment_album_det } private fun loadAlbumCover(album: Album) { - GlideApp.with(requireContext()).asBitmapPalette().albumCoverOptions(album.safeGetFirstSong()) + GlideApp.with(requireContext()).asBitmapPalette() + .albumCoverOptions(album.safeGetFirstSong()) //.checkIgnoreMediaStore() .load(RetroGlideExtension.getSongModel(album.safeGetFirstSong())) .into(object : SingleColorTarget(binding.image) { @@ -323,8 +336,10 @@ class AlbumDetailsFragment : AbsMainActivityFragment(R.layout.fragment_album_det } private fun setColors(color: Int) { - binding.fragmentAlbumContent.shuffleAction.applyColor(color) - binding.fragmentAlbumContent.playAction.applyOutlineColor(color) + _binding?.fragmentAlbumContent?.apply { + shuffleAction.applyColor(color) + playAction.applyOutlineColor(color) + } } override fun onAlbumClick(albumId: Long, view: View) { @@ -450,28 +465,34 @@ class AlbumDetailsFragment : AbsMainActivityFragment(R.layout.fragment_album_det private fun handleBackPress(): Boolean { cab?.let { - if (it.isActive) { - it.finish() + if (it.isActive()) { + it.destroy() return true } } return false } - private var cab: MaterialCab? = null + private var cab: AttachedCab? = null - override fun openCab(menuRes: Int, callback: MaterialCab.Callback): MaterialCab { + override fun openCab(menuRes: Int, callback: ICabCallback): AttachedCab { cab?.let { - if (it.isActive) { - it.finish() + if (it.isActive()) { + it.destroy() } } - cab = MaterialCab(mainActivity, R.id.cab_stub) - .setMenu(menuRes) - .setCloseDrawableRes(R.drawable.ic_close) - .setBackgroundColor(RetroColorUtil.shiftBackgroundColorForLightText(surfaceColor())) - .start(callback) - return cab as MaterialCab + cab = createCab(R.id.toolbar_container) { + menu(menuRes) + closeDrawable(R.drawable.ic_close) + backgroundColor(literal = RetroColorUtil.shiftBackgroundColor(surfaceColor())) + slideDown() + onCreate { cab, menu -> callback.onCabCreated(cab, menu) } + onSelection { + callback.onCabItemClicked(it) + } + onDestroy { callback.onCabFinished(it) } + } + return cab as AttachedCab } override fun onDestroyView() { diff --git a/app/src/main/java/code/name/monkey/retromusic/fragments/albums/AlbumDetailsViewModel.kt b/app/src/main/java/code/name/monkey/retromusic/fragments/albums/AlbumDetailsViewModel.kt index 702217d2..4ac3fb66 100644 --- a/app/src/main/java/code/name/monkey/retromusic/fragments/albums/AlbumDetailsViewModel.kt +++ b/app/src/main/java/code/name/monkey/retromusic/fragments/albums/AlbumDetailsViewModel.kt @@ -54,7 +54,7 @@ class AlbumDetailsViewModel( fun getAlbumInfo(album: Album): LiveData> = liveData { emit(Result.Loading) - emit(repository.albumInfo(album.artistName ?: "-", album.title ?: "-")) + emit(repository.albumInfo(album.artistName, album.title)) } fun getMoreAlbums(artist: Artist): LiveData> = liveData(IO) { diff --git a/app/src/main/java/code/name/monkey/retromusic/fragments/albums/AlbumsFragment.kt b/app/src/main/java/code/name/monkey/retromusic/fragments/albums/AlbumsFragment.kt index 92a372fe..f63cc644 100644 --- a/app/src/main/java/code/name/monkey/retromusic/fragments/albums/AlbumsFragment.kt +++ b/app/src/main/java/code/name/monkey/retromusic/fragments/albums/AlbumsFragment.kt @@ -24,16 +24,23 @@ import androidx.recyclerview.widget.GridLayoutManager import code.name.monkey.retromusic.EXTRA_ALBUM_ID import code.name.monkey.retromusic.R import code.name.monkey.retromusic.adapter.album.AlbumAdapter +import code.name.monkey.retromusic.extensions.navigate import code.name.monkey.retromusic.extensions.surfaceColor import code.name.monkey.retromusic.fragments.ReloadType import code.name.monkey.retromusic.fragments.base.AbsRecyclerViewCustomGridSizeFragment +import code.name.monkey.retromusic.helper.MusicPlayerRemote import code.name.monkey.retromusic.helper.SortOrder.AlbumSortOrder import code.name.monkey.retromusic.interfaces.IAlbumClickListener +import code.name.monkey.retromusic.interfaces.ICabCallback import code.name.monkey.retromusic.interfaces.ICabHolder +import code.name.monkey.retromusic.service.MusicService import code.name.monkey.retromusic.util.PreferenceUtil import code.name.monkey.retromusic.util.RetroColorUtil import code.name.monkey.retromusic.util.RetroUtil -import com.afollestad.materialcab.MaterialCab +import com.afollestad.materialcab.attached.AttachedCab +import com.afollestad.materialcab.attached.destroy +import com.afollestad.materialcab.attached.isActive +import com.afollestad.materialcab.createCab import com.google.android.gms.cast.framework.CastButtonFactory class AlbumsFragment : AbsRecyclerViewCustomGridSizeFragment(), @@ -50,7 +57,7 @@ class AlbumsFragment : AbsRecyclerViewCustomGridSizeFragment album.songs }, + startPosition = 0, + startPlaying = true + ) + } + } + override fun createLayoutManager(): GridLayoutManager { return GridLayoutManager(requireActivity(), getGridSize()) } @@ -325,28 +346,34 @@ class AlbumsFragment : AbsRecyclerViewCustomGridSizeFragment callback.onCabCreated(cab, menu) } + onSelection { + callback.onCabItemClicked(it) + } + onDestroy { callback.onCabFinished(it) } + } + return cab as AttachedCab } } diff --git a/app/src/main/java/code/name/monkey/retromusic/fragments/artists/AbsArtistDetailsFragment.kt b/app/src/main/java/code/name/monkey/retromusic/fragments/artists/AbsArtistDetailsFragment.kt index e02cbb31..9a638e1f 100644 --- a/app/src/main/java/code/name/monkey/retromusic/fragments/artists/AbsArtistDetailsFragment.kt +++ b/app/src/main/java/code/name/monkey/retromusic/fragments/artists/AbsArtistDetailsFragment.kt @@ -33,6 +33,7 @@ import code.name.monkey.retromusic.glide.RetroGlideExtension import code.name.monkey.retromusic.glide.SingleColorTarget import code.name.monkey.retromusic.helper.MusicPlayerRemote import code.name.monkey.retromusic.interfaces.IAlbumClickListener +import code.name.monkey.retromusic.interfaces.ICabCallback import code.name.monkey.retromusic.interfaces.ICabHolder import code.name.monkey.retromusic.model.Artist import code.name.monkey.retromusic.network.Result @@ -42,7 +43,11 @@ import code.name.monkey.retromusic.util.CustomArtistImageUtil import code.name.monkey.retromusic.util.MusicUtil import code.name.monkey.retromusic.util.RetroColorUtil import code.name.monkey.retromusic.util.RetroUtil -import com.afollestad.materialcab.MaterialCab +import com.afollestad.materialcab.attached.AttachedCab +import com.afollestad.materialcab.attached.destroy +import com.afollestad.materialcab.attached.isActive +import com.afollestad.materialcab.createCab +import com.google.android.material.shape.MaterialShapeDrawable import com.google.android.material.transition.MaterialContainerTransform import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch @@ -70,7 +75,6 @@ abstract class AbsArtistDetailsFragment : AbsMainActivityFragment(R.layout.fragm super.onCreate(savedInstanceState) sharedElementEnterTransition = MaterialContainerTransform().apply { drawingViewId = R.id.fragment_container - duration = 300L scrimColor = Color.TRANSPARENT setAllContainerColors(requireContext().resolveColor(R.attr.colorSurface)) } @@ -121,6 +125,8 @@ abstract class AbsArtistDetailsFragment : AbsMainActivityFragment(R.layout.fragm requireActivity().onBackPressed() } } + binding.appBarLayout?.statusBarForeground = + MaterialShapeDrawable.createWithElevationOverlay(requireContext()) } private fun setupRecyclerView() { @@ -306,28 +312,34 @@ abstract class AbsArtistDetailsFragment : AbsMainActivityFragment(R.layout.fragm private fun handleBackPress(): Boolean { cab?.let { - if (it.isActive) { - it.finish() + if (it.isActive()) { + it.destroy() return true } } return false } - private var cab: MaterialCab? = null + private var cab: AttachedCab? = null - override fun openCab(menuRes: Int, callback: MaterialCab.Callback): MaterialCab { + override fun openCab(menuRes: Int, callback: ICabCallback): AttachedCab { cab?.let { - if (it.isActive) { - it.finish() + if (it.isActive()) { + it.destroy() } } - cab = MaterialCab(mainActivity, R.id.cab_stub) - .setMenu(menuRes) - .setCloseDrawableRes(R.drawable.ic_close) - .setBackgroundColor(RetroColorUtil.shiftBackgroundColorForLightText(surfaceColor())) - .start(callback) - return cab as MaterialCab + cab = createCab(R.id.toolbar_container) { + menu(menuRes) + closeDrawable(R.drawable.ic_close) + backgroundColor(literal = RetroColorUtil.shiftBackgroundColor(surfaceColor())) + slideDown() + onCreate { cab, menu -> callback.onCabCreated(cab, menu) } + onSelection { + callback.onCabItemClicked(it) + } + onDestroy { callback.onCabFinished(it) } + } + return cab as AttachedCab } diff --git a/app/src/main/java/code/name/monkey/retromusic/fragments/artists/AlbumArtistDetailsViewModel.kt b/app/src/main/java/code/name/monkey/retromusic/fragments/artists/AlbumArtistDetailsViewModel.kt deleted file mode 100644 index 3d202bb5..00000000 --- a/app/src/main/java/code/name/monkey/retromusic/fragments/artists/AlbumArtistDetailsViewModel.kt +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright (c) 2020 Hemanth Savarla. - * - * 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.fragments.artists - -import androidx.lifecycle.* -import code.name.monkey.retromusic.interfaces.IMusicServiceEventListener -import code.name.monkey.retromusic.model.Artist -import code.name.monkey.retromusic.network.Result -import code.name.monkey.retromusic.network.model.LastFmArtist -import code.name.monkey.retromusic.repository.RealRepository -import kotlinx.coroutines.Dispatchers.IO -import kotlinx.coroutines.launch - -class AlbumArtistDetailsViewModel( - private val realRepository: RealRepository, - private val artistName: String -) : ViewModel(), IMusicServiceEventListener { - private val artistDetails = MutableLiveData() - - init { - fetchAlbumArtist() - } - - private fun fetchAlbumArtist() { - viewModelScope.launch(IO) { - artistDetails.postValue(realRepository.albumArtistByName(artistName)) - } - } - - fun getArtist(): LiveData = artistDetails - - fun getArtistInfo( - name: String, - lang: String?, - cache: String? - ): LiveData> = liveData(IO) { - emit(Result.Loading) - val info = realRepository.artistInfo(name, lang, cache) - emit(info) - } - - override fun onMediaStoreChanged() { - fetchAlbumArtist() - } - - override fun onServiceConnected() {} - override fun onServiceDisconnected() {} - override fun onQueueChanged() {} - override fun onFavoriteStateChanged() {} - override fun onPlayingMetaChanged() {} - override fun onPlayStateChanged() {} - override fun onRepeatModeChanged() {} - override fun onShuffleModeChanged() {} -} diff --git a/app/src/main/java/code/name/monkey/retromusic/fragments/artists/ArtistsFragment.kt b/app/src/main/java/code/name/monkey/retromusic/fragments/artists/ArtistsFragment.kt index 8275c526..8739b766 100644 --- a/app/src/main/java/code/name/monkey/retromusic/fragments/artists/ArtistsFragment.kt +++ b/app/src/main/java/code/name/monkey/retromusic/fragments/artists/ArtistsFragment.kt @@ -25,17 +25,24 @@ import code.name.monkey.retromusic.EXTRA_ARTIST_ID import code.name.monkey.retromusic.EXTRA_ARTIST_NAME import code.name.monkey.retromusic.R import code.name.monkey.retromusic.adapter.artist.ArtistAdapter +import code.name.monkey.retromusic.extensions.navigate import code.name.monkey.retromusic.extensions.surfaceColor import code.name.monkey.retromusic.fragments.ReloadType import code.name.monkey.retromusic.fragments.base.AbsRecyclerViewCustomGridSizeFragment +import code.name.monkey.retromusic.helper.MusicPlayerRemote import code.name.monkey.retromusic.helper.SortOrder.ArtistSortOrder import code.name.monkey.retromusic.interfaces.IAlbumArtistClickListener import code.name.monkey.retromusic.interfaces.IArtistClickListener +import code.name.monkey.retromusic.interfaces.ICabCallback import code.name.monkey.retromusic.interfaces.ICabHolder +import code.name.monkey.retromusic.service.MusicService import code.name.monkey.retromusic.util.PreferenceUtil import code.name.monkey.retromusic.util.RetroColorUtil import code.name.monkey.retromusic.util.RetroUtil -import com.afollestad.materialcab.MaterialCab +import com.afollestad.materialcab.attached.AttachedCab +import com.afollestad.materialcab.attached.destroy +import com.afollestad.materialcab.attached.isActive +import com.afollestad.materialcab.createCab import com.google.android.gms.cast.framework.CastButtonFactory class ArtistsFragment : AbsRecyclerViewCustomGridSizeFragment(), @@ -51,16 +58,31 @@ class ArtistsFragment : AbsRecyclerViewCustomGridSizeFragment artist.songs }, + startPosition = 0, + startPlaying = true + ) + } + } + override fun setSortOrder(sortOrder: String) { libraryViewModel.forceReload(ReloadType.Artists) } @@ -324,28 +346,34 @@ class ArtistsFragment : AbsRecyclerViewCustomGridSizeFragment callback.onCabCreated(cab, menu) } + onSelection { + callback.onCabItemClicked(it) + } + onDestroy { callback.onCabFinished(it) } + } + return cab as AttachedCab } override fun onResume() { diff --git a/app/src/main/java/code/name/monkey/retromusic/fragments/backup/BackupFragment.kt b/app/src/main/java/code/name/monkey/retromusic/fragments/backup/BackupFragment.kt new file mode 100644 index 00000000..2415f812 --- /dev/null +++ b/app/src/main/java/code/name/monkey/retromusic/fragments/backup/BackupFragment.kt @@ -0,0 +1,152 @@ +package code.name.monkey.retromusic.fragments.backup + +import android.content.Intent +import android.os.Bundle +import android.view.MenuItem +import android.view.View +import android.widget.Toast +import androidx.appcompat.app.AlertDialog +import androidx.core.view.isVisible +import androidx.fragment.app.Fragment +import androidx.fragment.app.viewModels +import androidx.lifecycle.lifecycleScope +import androidx.recyclerview.widget.LinearLayoutManager +import androidx.recyclerview.widget.RecyclerView +import code.name.monkey.retromusic.R +import code.name.monkey.retromusic.adapter.backup.BackupAdapter +import code.name.monkey.retromusic.databinding.FragmentBackupBinding +import code.name.monkey.retromusic.helper.BackupHelper +import code.name.monkey.retromusic.util.BackupUtil +import com.afollestad.materialdialogs.MaterialDialog +import com.afollestad.materialdialogs.input.input +import kotlinx.coroutines.launch +import java.io.File + +class BackupFragment : Fragment(R.layout.fragment_backup), BackupAdapter.BackupClickedListener { + + private val backupViewModel by viewModels() + private var backupAdapter: BackupAdapter? = null + + private var _binding: FragmentBackupBinding? = null + private val binding get() = _binding!! + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + _binding = FragmentBackupBinding.bind(view) + initAdapter() + setupRecyclerview() + backupViewModel.backupsLiveData.observe(this) { + if (it.isNotEmpty()) + backupAdapter?.swapDataset(it) + else + backupAdapter?.swapDataset(listOf()) + } + backupViewModel.loadBackups() + setupButtons() + } + + private fun setupButtons() { + binding.createBackup.setOnClickListener { + MaterialDialog(requireContext()).show { + title(res = R.string.action_rename) + input(prefill = System.currentTimeMillis().toString()) { _, text -> + // Text submitted with the action button + lifecycleScope.launch { + BackupHelper.createBackup(requireContext(), text.toString()) + backupViewModel.loadBackups() + } + } + positiveButton(android.R.string.ok) + negativeButton(R.string.action_cancel) + setTitle(R.string.title_new_backup) + } + + } + } + + private fun initAdapter() { + backupAdapter = BackupAdapter(requireActivity(), ArrayList(), this) + backupAdapter?.registerAdapterDataObserver(object : RecyclerView.AdapterDataObserver() { + override fun onChanged() { + super.onChanged() + checkIsEmpty() + } + }) + } + + private fun checkIsEmpty() { + val isEmpty = backupAdapter!!.itemCount == 0 + binding.empty.isVisible = isEmpty + binding.backupTitle.isVisible = !isEmpty + binding.backupRecyclerview.isVisible = !isEmpty + } + + fun setupRecyclerview() { + binding.backupRecyclerview.apply { + layoutManager = LinearLayoutManager(context) + adapter = backupAdapter + } + } + + override fun onBackupClicked(file: File) { + AlertDialog.Builder(requireContext()) + .setTitle(R.string.restore) + .setMessage(R.string.restore_message) + .setPositiveButton(R.string.restore) { _, _ -> + lifecycleScope.launch { + backupViewModel.restoreBackup(requireActivity(), file) + } + } + .setNegativeButton(android.R.string.cancel, null) + .create() + .show() + } + + override fun onBackupMenuClicked(file: File, menuItem: MenuItem): Boolean { + when (menuItem.itemId) { + R.id.action_delete -> { + try { + file.delete() + } catch (exception: SecurityException) { + Toast.makeText( + activity, + "Could not delete backup", + Toast.LENGTH_SHORT + ).show() + } + backupViewModel.loadBackups() + return true + } + R.id.action_share -> { + activity?.startActivity( + Intent.createChooser(BackupUtil.createShareFileIntent(file, requireContext()), null)) + return true + } + R.id.action_rename -> { + MaterialDialog(requireContext()).show { + title(res = R.string.action_rename) + input(prefill = file.nameWithoutExtension) { _, text -> + // Text submitted with the action button + val renamedFile = + File(file.parent + File.separator + text + BackupHelper.APPEND_EXTENSION) + if (!renamedFile.exists()) { + file.renameTo(renamedFile) + backupViewModel.loadBackups() + } else { + Toast.makeText( + requireContext(), + "File already exists", + Toast.LENGTH_SHORT + ).show() + } + } + positiveButton(android.R.string.ok) + negativeButton(R.string.action_cancel) + setTitle(R.string.action_rename) + } + return true + } + } + return false + } +} \ No newline at end of file diff --git a/app/src/main/java/code/name/monkey/retromusic/fragments/backup/BackupViewModel.kt b/app/src/main/java/code/name/monkey/retromusic/fragments/backup/BackupViewModel.kt new file mode 100644 index 00000000..d2194749 --- /dev/null +++ b/app/src/main/java/code/name/monkey/retromusic/fragments/backup/BackupViewModel.kt @@ -0,0 +1,38 @@ +package code.name.monkey.retromusic.fragments.backup + +import android.app.Activity +import android.content.Intent +import androidx.lifecycle.LiveData +import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.ViewModel +import code.name.monkey.retromusic.helper.BackupHelper +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.withContext +import java.io.File +import kotlin.system.exitProcess + + +class BackupViewModel : ViewModel() { + private val backupsMutableLiveData = MutableLiveData>() + val backupsLiveData: LiveData> = backupsMutableLiveData + + fun loadBackups() { + File(BackupHelper.backupRootPath).listFiles { _, name -> + return@listFiles name.endsWith(BackupHelper.BACKUP_EXTENSION) + }?.toList()?.let { + backupsMutableLiveData.value = it + } + } + + suspend fun restoreBackup(activity: Activity, file: File) { + BackupHelper.restoreBackup(activity, file) + withContext(Dispatchers.Main) { + val intent = Intent( + activity, + activity::class.java + ) + activity.startActivity(intent) + exitProcess(0) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/code/name/monkey/retromusic/fragments/backup/RestoreActivity.kt b/app/src/main/java/code/name/monkey/retromusic/fragments/backup/RestoreActivity.kt new file mode 100644 index 00000000..f54e697c --- /dev/null +++ b/app/src/main/java/code/name/monkey/retromusic/fragments/backup/RestoreActivity.kt @@ -0,0 +1,12 @@ +package code.name.monkey.retromusic.fragments.backup + +import android.os.Bundle +import androidx.appcompat.app.AppCompatActivity +import code.name.monkey.retromusic.R + +class RestoreActivity : AppCompatActivity() { + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(R.layout.activity_restore) + } +} \ No newline at end of file diff --git a/app/src/main/java/code/name/monkey/retromusic/fragments/base/AbsMainActivityFragment.kt b/app/src/main/java/code/name/monkey/retromusic/fragments/base/AbsMainActivityFragment.kt index 2edf2ecc..0ced7780 100644 --- a/app/src/main/java/code/name/monkey/retromusic/fragments/base/AbsMainActivityFragment.kt +++ b/app/src/main/java/code/name/monkey/retromusic/fragments/base/AbsMainActivityFragment.kt @@ -34,10 +34,7 @@ abstract class AbsMainActivityFragment(@LayoutRes layout: Int) : AbsMusicService override fun onActivityCreated(savedInstanceState: Bundle?) { super.onActivityCreated(savedInstanceState) setHasOptionsMenu(true) - mainActivity.setNavigationbarColorAuto() - mainActivity.setLightNavigationBar(true) mainActivity.setTaskDescriptionColorAuto() - mainActivity.hideStatusBar() } private fun setStatusBarColor(view: View, color: Int) { diff --git a/app/src/main/java/code/name/monkey/retromusic/fragments/base/AbsPlayerControlsFragment.kt b/app/src/main/java/code/name/monkey/retromusic/fragments/base/AbsPlayerControlsFragment.kt index 93c2346a..3530d8f6 100644 --- a/app/src/main/java/code/name/monkey/retromusic/fragments/base/AbsPlayerControlsFragment.kt +++ b/app/src/main/java/code/name/monkey/retromusic/fragments/base/AbsPlayerControlsFragment.kt @@ -20,7 +20,7 @@ import android.view.animation.AccelerateInterpolator import android.view.animation.DecelerateInterpolator import androidx.annotation.LayoutRes import code.name.monkey.retromusic.R -import code.name.monkey.retromusic.fragments.VolumeFragment +import code.name.monkey.retromusic.fragments.other.VolumeFragment import code.name.monkey.retromusic.helper.MusicProgressViewUpdateHelper import code.name.monkey.retromusic.util.PreferenceUtil import code.name.monkey.retromusic.util.color.MediaNotificationProcessor diff --git a/app/src/main/java/code/name/monkey/retromusic/fragments/base/AbsPlayerFragment.kt b/app/src/main/java/code/name/monkey/retromusic/fragments/base/AbsPlayerFragment.kt index 8838b6e5..9b93f990 100644 --- a/app/src/main/java/code/name/monkey/retromusic/fragments/base/AbsPlayerFragment.kt +++ b/app/src/main/java/code/name/monkey/retromusic/fragments/base/AbsPlayerFragment.kt @@ -131,7 +131,7 @@ abstract class AbsPlayerFragment(@LayoutRes layout: Int) : AbsMainActivityFragme } R.id.action_go_to_album -> { //Hide Bottom Bar First, else Bottom Sheet doesn't collapse fully - mainActivity.setBottomBarVisibility(false) + mainActivity.setBottomNavVisibility(false) mainActivity.collapsePanel() requireActivity().findNavController(R.id.fragment_container).navigate( R.id.albumDetailsFragment, @@ -144,7 +144,10 @@ abstract class AbsPlayerFragment(@LayoutRes layout: Int) : AbsMainActivityFragme return true } R.id.now_playing -> { - NavigationUtil.goToPlayingQueue(requireActivity()) + requireActivity().findNavController(R.id.fragment_container).navigate( + R.id.playing_queue_fragment, + null + ) return true } R.id.action_show_lyrics -> { @@ -325,6 +328,7 @@ abstract class AbsPlayerFragment(@LayoutRes layout: Int) : AbsMainActivityFragme requireView() ) ) + playerToolbar()?.menu?.findItem(R.id.action_toggle_lyrics)?.let { showLyricsIcon(it) } } class SwipeDetector(val context: Context, val viewPager: ViewPager?, val view: View) : @@ -383,7 +387,7 @@ fun goToArtist(activity: Activity) { currentFragment(R.id.fragment_container)?.exitTransition = null //Hide Bottom Bar First, else Bottom Sheet doesn't collapse fully - setBottomBarVisibility(false) + setBottomNavVisibility(false) if (getBottomSheetBehavior().state == BottomSheetBehavior.STATE_EXPANDED) { collapsePanel() } @@ -402,7 +406,7 @@ fun goToAlbum(activity: Activity) { currentFragment(R.id.fragment_container)?.exitTransition = null //Hide Bottom Bar First, else Bottom Sheet doesn't collapse fully - setBottomBarVisibility(false) + setBottomNavVisibility(false) if (getBottomSheetBehavior().state == BottomSheetBehavior.STATE_EXPANDED) { collapsePanel() } diff --git a/app/src/main/java/code/name/monkey/retromusic/fragments/base/AbsRecyclerViewCustomGridSizeFragment.kt b/app/src/main/java/code/name/monkey/retromusic/fragments/base/AbsRecyclerViewCustomGridSizeFragment.kt index 5a4723d5..43dd9460 100644 --- a/app/src/main/java/code/name/monkey/retromusic/fragments/base/AbsRecyclerViewCustomGridSizeFragment.kt +++ b/app/src/main/java/code/name/monkey/retromusic/fragments/base/AbsRecyclerViewCustomGridSizeFragment.kt @@ -86,7 +86,7 @@ abstract class AbsRecyclerViewCustomGridSizeFragment } else { saveGridSize(gridSize) } - recyclerView().isVisible = false + recyclerView.isVisible = false invalidateLayoutManager() // only recreate the adapter and layout manager if the layout currentLayoutRes has changed if (oldLayoutRes != itemLayoutRes()) { @@ -95,10 +95,10 @@ abstract class AbsRecyclerViewCustomGridSizeFragment setGridSize(gridSize) } val transition = MaterialFade().apply { - addTarget(recyclerView()) + addTarget(recyclerView) } - TransitionManager.beginDelayedTransition(getContainer(), transition) - recyclerView().isVisible = true + TransitionManager.beginDelayedTransition(container, transition) + recyclerView.isVisible = true } protected abstract fun setGridSize(gridSize: Int) diff --git a/app/src/main/java/code/name/monkey/retromusic/fragments/base/AbsRecyclerViewFragment.kt b/app/src/main/java/code/name/monkey/retromusic/fragments/base/AbsRecyclerViewFragment.kt index 87227dc9..c667b2fb 100644 --- a/app/src/main/java/code/name/monkey/retromusic/fragments/base/AbsRecyclerViewFragment.kt +++ b/app/src/main/java/code/name/monkey/retromusic/fragments/base/AbsRecyclerViewFragment.kt @@ -18,8 +18,10 @@ import android.os.Bundle import android.view.* import androidx.annotation.NonNull import androidx.annotation.StringRes -import androidx.coordinatorlayout.widget.CoordinatorLayout +import androidx.appcompat.widget.Toolbar import androidx.core.view.doOnPreDraw +import androidx.core.view.isVisible +import androidx.core.view.updateLayoutParams import androidx.core.view.updatePadding import androidx.navigation.fragment.findNavController import androidx.recyclerview.widget.RecyclerView @@ -29,9 +31,12 @@ import code.name.monkey.retromusic.R import code.name.monkey.retromusic.databinding.FragmentMainRecyclerBinding import code.name.monkey.retromusic.dialogs.CreatePlaylistDialog import code.name.monkey.retromusic.dialogs.ImportPlaylistDialog +import code.name.monkey.retromusic.extensions.accentColor +import code.name.monkey.retromusic.extensions.dip +import code.name.monkey.retromusic.extensions.drawNextToNavbar import code.name.monkey.retromusic.helper.MusicPlayerRemote -import code.name.monkey.retromusic.util.DensityUtil import code.name.monkey.retromusic.util.ThemedFastScroller.create +import com.google.android.material.shape.MaterialShapeDrawable import com.google.android.material.transition.MaterialFadeThrough import com.google.android.material.transition.MaterialSharedAxis import me.zhanghai.android.fastscroll.FastScroller @@ -44,21 +49,57 @@ abstract class AbsRecyclerViewFragment, LM : Recycle private val binding get() = _binding!! protected var adapter: A? = null protected var layoutManager: LM? = null + val shuffleButton get() = binding.shuffleButton + abstract val isShuffleVisible: Boolean override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) _binding = FragmentMainRecyclerBinding.bind(view) - enterTransition = MaterialFadeThrough() - exitTransition = MaterialFadeThrough() postponeEnterTransition() view.doOnPreDraw { startPostponedEnterTransition() } - + enterTransition = MaterialFadeThrough().apply { + addTarget(binding.recyclerView) + } mainActivity.setSupportActionBar(binding.toolbar) mainActivity.supportActionBar?.title = null initLayoutManager() initAdapter() setUpRecyclerView() setupToolbar() + // Add listeners when shuffle is visible + if (isShuffleVisible) { + binding.recyclerView.addOnScrollListener(object : RecyclerView.OnScrollListener() { + override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) { + super.onScrolled(recyclerView, dx, dy) + if (dy > 0) { + binding.shuffleButton.hide() + } else if (dy < 0) { + binding.shuffleButton.show() + } + + } + }) + binding.shuffleButton.apply { + setOnClickListener { + onShuffleClicked() + } + accentColor() + } + } else { + binding.shuffleButton.isVisible = false + } + libraryViewModel.getFabMargin().observe(viewLifecycleOwner, { + binding.shuffleButton.updateLayoutParams { + bottomMargin = it + } + }) + } + + open fun onShuffleClicked() { + } + + fun toolbar(): Toolbar { + return binding.toolbar } private fun setupToolbar() { @@ -73,6 +114,9 @@ abstract class AbsRecyclerViewFragment, LM : Recycle } val appName = resources.getString(titleRes) binding.appNameText.text = appName + binding.toolbarContainer.drawNextToNavbar() + binding.appBarLayout.statusBarForeground = + MaterialShapeDrawable.createWithElevationOverlay(requireContext()) } abstract val titleRes: Int @@ -117,10 +161,10 @@ abstract class AbsRecyclerViewFragment, LM : Recycle val itemCount: Int = adapter?.itemCount ?: 0 if (itemCount > 0 && MusicPlayerRemote.playingQueue.isNotEmpty()) { - val height = DensityUtil.dip2px(requireContext(), 112f) + val height = dip(R.dimen.mini_player_height_expanded) binding.recyclerView.updatePadding(0, 0, 0, height) } else { - val height = DensityUtil.dip2px(requireContext(), 56f) + val height = dip(R.dimen.mini_player_height) binding.recyclerView.updatePadding(0, 0, 0, height) } } @@ -155,16 +199,12 @@ abstract class AbsRecyclerViewFragment, LM : Recycle binding.recyclerView.adapter = adapter } - fun recyclerView(): RecyclerView { - return binding.recyclerView - } + val recyclerView get() = binding.recyclerView - fun getContainer(): CoordinatorLayout { - return binding.root - } + val container get() = binding.root fun scrollToTop() { - recyclerView().scrollToPosition(0) + recyclerView.scrollToPosition(0) binding.appBarLayout.setExpanded(true, true) } diff --git a/app/src/main/java/code/name/monkey/retromusic/fragments/folder/FoldersFragment.java b/app/src/main/java/code/name/monkey/retromusic/fragments/folder/FoldersFragment.java deleted file mode 100644 index d9803ac7..00000000 --- a/app/src/main/java/code/name/monkey/retromusic/fragments/folder/FoldersFragment.java +++ /dev/null @@ -1,942 +0,0 @@ -/* - * Copyright (c) 2020 Hemanth Savarala. - * - * Licensed under the GNU General Public License v3 - * - * This is free software: you can redistribute it and/or modify it under - * the terms of the GNU General Public License as published by - * the Free Software Foundation either version 3 of the License, or (at your option) any later version. - * - * This software is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU General Public License for more details. - */ - -package code.name.monkey.retromusic.fragments.folder; - -import static code.name.monkey.appthemehelper.common.ATHToolbarActivity.getToolbarBackgroundColor; - -import android.app.Dialog; -import android.content.Context; -import android.media.MediaScannerConnection; -import android.os.Bundle; -import android.os.Environment; -import android.text.Html; -import android.view.LayoutInflater; -import android.view.Menu; -import android.view.MenuInflater; -import android.view.MenuItem; -import android.view.View; -import android.view.ViewGroup; -import android.view.ViewGroup.MarginLayoutParams; -import android.webkit.MimeTypeMap; -import android.widget.PopupMenu; -import android.widget.Toast; - -import androidx.activity.OnBackPressedCallback; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.loader.app.LoaderManager; -import androidx.loader.content.Loader; -import androidx.navigation.Navigation; -import androidx.recyclerview.widget.LinearLayoutManager; -import androidx.recyclerview.widget.RecyclerView; - -import com.afollestad.materialcab.MaterialCab; -import com.google.android.material.dialog.MaterialAlertDialogBuilder; -import com.google.android.material.snackbar.Snackbar; -import com.google.android.material.transition.MaterialFadeThrough; -import com.google.android.material.transition.MaterialSharedAxis; - -import org.jetbrains.annotations.NotNull; - -import java.io.BufferedReader; -import java.io.File; -import java.io.FileFilter; -import java.io.FileReader; -import java.io.IOException; -import java.lang.ref.WeakReference; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.Comparator; -import java.util.HashSet; -import java.util.LinkedList; -import java.util.List; -import java.util.StringTokenizer; - -import code.name.monkey.appthemehelper.ThemeStore; -import code.name.monkey.appthemehelper.util.ATHUtil; -import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper; -import code.name.monkey.retromusic.App; -import code.name.monkey.retromusic.R; -import code.name.monkey.retromusic.adapter.SongFileAdapter; -import code.name.monkey.retromusic.adapter.Storage; -import code.name.monkey.retromusic.adapter.StorageAdapter; -import code.name.monkey.retromusic.adapter.StorageClickListener; -import code.name.monkey.retromusic.databinding.FragmentFolderBinding; -import code.name.monkey.retromusic.fragments.base.AbsMainActivityFragment; -import code.name.monkey.retromusic.helper.MusicPlayerRemote; -import code.name.monkey.retromusic.helper.menu.SongMenuHelper; -import code.name.monkey.retromusic.helper.menu.SongsMenuHelper; -import code.name.monkey.retromusic.interfaces.ICabHolder; -import code.name.monkey.retromusic.interfaces.ICallbacks; -import code.name.monkey.retromusic.interfaces.IMainActivityFragmentCallbacks; -import code.name.monkey.retromusic.misc.DialogAsyncTask; -import code.name.monkey.retromusic.misc.UpdateToastMediaScannerCompletionListener; -import code.name.monkey.retromusic.misc.WrappedAsyncTaskLoader; -import code.name.monkey.retromusic.model.Song; -import code.name.monkey.retromusic.providers.BlacklistStore; -import code.name.monkey.retromusic.util.DensityUtil; -import code.name.monkey.retromusic.util.FileUtil; -import code.name.monkey.retromusic.util.PreferenceUtil; -import code.name.monkey.retromusic.util.RetroColorUtil; -import code.name.monkey.retromusic.util.ThemedFastScroller; -import code.name.monkey.retromusic.views.BreadCrumbLayout; -import code.name.monkey.retromusic.views.ScrollingViewOnApplyWindowInsetsListener; -import me.zhanghai.android.fastscroll.FastScroller; - -public class FoldersFragment extends AbsMainActivityFragment - implements IMainActivityFragmentCallbacks, - ICabHolder, - BreadCrumbLayout.SelectionCallback, - ICallbacks, - LoaderManager.LoaderCallbacks>, StorageClickListener { - - private FragmentFolderBinding binding; - public static final String TAG = FoldersFragment.class.getSimpleName(); - public static final FileFilter AUDIO_FILE_FILTER = - file -> - !file.isHidden() - && (file.isDirectory() - || FileUtil.fileIsMimeType(file, "audio/*", MimeTypeMap.getSingleton()) - || FileUtil.fileIsMimeType(file, "application/opus", MimeTypeMap.getSingleton()) - || FileUtil.fileIsMimeType(file, "application/ogg", MimeTypeMap.getSingleton())); - - private static final String CRUMBS = "crumbs"; - private static final int LOADER_ID = 5; - private SongFileAdapter adapter; - private StorageAdapter storageAdapter; - private MaterialCab cab; - private final Comparator fileComparator = - (lhs, rhs) -> { - if (lhs.isDirectory() && !rhs.isDirectory()) { - return -1; - } else if (!lhs.isDirectory() && rhs.isDirectory()) { - return 1; - } else { - return lhs.getName().compareToIgnoreCase(rhs.getName()); - } - }; - private final ArrayList storageItems = new ArrayList<>(); - - public FoldersFragment() { - super(R.layout.fragment_folder); - } - - public static File getDefaultStartDirectory() { - File musicDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_MUSIC); - File startFolder; - if (musicDir.exists() && musicDir.isDirectory()) { - startFolder = musicDir; - } else { - File externalStorage = Environment.getExternalStorageDirectory(); - if (externalStorage.exists() && externalStorage.isDirectory()) { - startFolder = externalStorage; - } else { - startFolder = new File("/"); // root - } - } - return startFolder; - } - - private static File tryGetCanonicalFile(File file) { - try { - return file.getCanonicalFile(); - } catch (IOException e) { - e.printStackTrace(); - return file; - } - } - - @NonNull - @Override - public View onCreateView( - @NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - binding = FragmentFolderBinding.inflate(inflater, container, false); - return binding.getRoot(); - } - - @Override - public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { - setEnterTransition(new MaterialFadeThrough()); - setExitTransition(new MaterialFadeThrough()); - getMainActivity().addMusicServiceEventListener(getLibraryViewModel()); - getMainActivity().setSupportActionBar(binding.toolbar); - getMainActivity().getSupportActionBar().setTitle(null); - setStatusBarColorAuto(view); - setUpAppbarColor(); - setUpBreadCrumbs(); - setUpRecyclerView(); - listRoots(); - setUpAdapter(); - setUpTitle(); - requireActivity().getOnBackPressedDispatcher().addCallback(getViewLifecycleOwner(), new OnBackPressedCallback(true) { - @Override - public void handleOnBackPressed() { - if (!handleBackPress()) { - remove(); - requireActivity().onBackPressed(); - } - } - }); - } - - private void setUpTitle() { - binding.toolbar.setNavigationOnClickListener( - v -> { - setExitTransition(new MaterialSharedAxis(MaterialSharedAxis.Z, true).setDuration(300)); - setReenterTransition(new MaterialSharedAxis(MaterialSharedAxis.Z, false).setDuration(300)); - Navigation.findNavController(v).navigate(R.id.searchFragment, null, getNavOptions()); - }); - binding.appNameText.setText(getResources().getString(R.string.folders)); - } - - @Override - public void onActivityCreated(Bundle savedInstanceState) { - super.onActivityCreated(savedInstanceState); - setHasOptionsMenu(true); - if (savedInstanceState == null) { - switchToFileAdapter(); - setCrumb( - new BreadCrumbLayout.Crumb( - FileUtil.safeGetCanonicalFile(PreferenceUtil.INSTANCE.getStartDirectory())), - true); - - } else { - binding.breadCrumbs.restoreFromStateWrapper(savedInstanceState.getParcelable(CRUMBS)); - LoaderManager.getInstance(this).initLoader(LOADER_ID, null, this); - } - } - - @Override - public void onPause() { - super.onPause(); - saveScrollPosition(); - } - - @Override - public void onSaveInstanceState(@NonNull Bundle outState) { - super.onSaveInstanceState(outState); - if (binding != null) { - outState.putParcelable(CRUMBS, binding.breadCrumbs.getStateWrapper()); - } - } - - @Override - public boolean handleBackPress() { - if (cab != null && cab.isActive()) { - cab.finish(); - return true; - } - if (binding.breadCrumbs.popHistory()) { - setCrumb(binding.breadCrumbs.lastHistory(), false); - return true; - } - return false; - } - - @NonNull - @Override - public Loader> onCreateLoader(int id, Bundle args) { - return new AsyncFileLoader(this); - } - - @Override - public void onCrumbSelection(BreadCrumbLayout.Crumb crumb, int index) { - setCrumb(crumb, true); - } - - @Override - public void onFileMenuClicked(final File file, @NotNull View view) { - PopupMenu popupMenu = new PopupMenu(getActivity(), view); - if (file.isDirectory()) { - popupMenu.inflate(R.menu.menu_item_directory); - popupMenu.setOnMenuItemClickListener( - item -> { - final int itemId = item.getItemId(); - switch (itemId) { - case R.id.action_play_next: - case R.id.action_add_to_current_playing: - case R.id.action_add_to_playlist: - case R.id.action_delete_from_device: - new ListSongsAsyncTask( - getActivity(), - null, - (songs, extra) -> { - if (!songs.isEmpty()) { - SongsMenuHelper.INSTANCE.handleMenuClick( - requireActivity(), songs, itemId); - } - }) - .execute( - new ListSongsAsyncTask.LoadingInfo( - toList(file), AUDIO_FILE_FILTER, getFileComparator())); - return true; - case R.id.action_add_to_blacklist: - BlacklistStore.getInstance(App.Companion.getContext()).addPath(file); - return true; - case R.id.action_set_as_start_directory: - PreferenceUtil.INSTANCE.setStartDirectory(file); - Toast.makeText( - getActivity(), - String.format(getString(R.string.new_start_directory), file.getPath()), - Toast.LENGTH_SHORT) - .show(); - return true; - case R.id.action_scan: - new ListPathsAsyncTask(getActivity(), this::scanPaths) - .execute(new ListPathsAsyncTask.LoadingInfo(file, AUDIO_FILE_FILTER)); - return true; - } - return false; - }); - } else { - popupMenu.inflate(R.menu.menu_item_file); - popupMenu.setOnMenuItemClickListener( - item -> { - final int itemId = item.getItemId(); - switch (itemId) { - case R.id.action_play_next: - case R.id.action_add_to_current_playing: - case R.id.action_add_to_playlist: - case R.id.action_go_to_album: - case R.id.action_go_to_artist: - case R.id.action_share: - case R.id.action_tag_editor: - case R.id.action_details: - case R.id.action_set_as_ringtone: - case R.id.action_delete_from_device: - new ListSongsAsyncTask( - getActivity(), - null, - (songs, extra) -> - SongMenuHelper.INSTANCE.handleMenuClick( - requireActivity(), songs.get(0), itemId)) - .execute( - new ListSongsAsyncTask.LoadingInfo( - toList(file), AUDIO_FILE_FILTER, getFileComparator())); - return true; - case R.id.action_scan: - new ListPathsAsyncTask(getActivity(), this::scanPaths) - .execute(new ListPathsAsyncTask.LoadingInfo(file, AUDIO_FILE_FILTER)); - return true; - } - return false; - }); - } - popupMenu.show(); - } - - @Override - public void onFileSelected(@NotNull File file) { - file = tryGetCanonicalFile(file); // important as we compare the path value later - if (file.isDirectory()) { - setCrumb(new BreadCrumbLayout.Crumb(file), true); - } else { - FileFilter fileFilter = - pathname -> !pathname.isDirectory() && AUDIO_FILE_FILTER.accept(pathname); - new ListSongsAsyncTask( - getActivity(), - file, - (songs, extra) -> { - File file1 = (File) extra; - int startIndex = -1; - for (int i = 0; i < songs.size(); i++) { - if (file1 - .getPath() - .equals(songs.get(i).getData())) { // path is already canonical here - startIndex = i; - break; - } - } - if (startIndex > -1) { - MusicPlayerRemote.openQueue(songs, startIndex, true); - } else { - final File finalFile = file1; - Snackbar.make( - binding.coordinatorLayout, - Html.fromHtml( - String.format( - getString(R.string.not_listed_in_media_store), file1.getName())), - Snackbar.LENGTH_LONG) - .setAction( - R.string.action_scan, - v -> - new ListPathsAsyncTask(requireActivity(), this::scanPaths) - .execute( - new ListPathsAsyncTask.LoadingInfo( - finalFile, AUDIO_FILE_FILTER))) - .setActionTextColor(ThemeStore.Companion.accentColor(requireActivity())) - .show(); - } - }) - .execute( - new ListSongsAsyncTask.LoadingInfo( - toList(file.getParentFile()), fileFilter, getFileComparator())); - } - } - - @Override - public void onLoadFinished(@NonNull Loader> loader, List data) { - updateAdapter(data); - } - - @Override - public void onLoaderReset(@NonNull Loader> loader) { - updateAdapter(new LinkedList<>()); - } - - @Override - public void onMultipleItemAction(MenuItem item, @NotNull ArrayList files) { - final int itemId = item.getItemId(); - new ListSongsAsyncTask( - getActivity(), - null, - (songs, extra) -> - SongsMenuHelper.INSTANCE.handleMenuClick(requireActivity(), songs, itemId)) - .execute(new ListSongsAsyncTask.LoadingInfo(files, AUDIO_FILE_FILTER, getFileComparator())); - } - - @Override - public void onPrepareOptionsMenu(@NonNull Menu menu) { - super.onPrepareOptionsMenu(menu); - ToolbarContentTintHelper.handleOnPrepareOptionsMenu(requireActivity(), binding.toolbar); - } - - @Override - public void onCreateOptionsMenu(@NonNull Menu menu, @NonNull MenuInflater inflater) { - super.onCreateOptionsMenu(menu, inflater); - menu.add(0, R.id.action_scan, 0, R.string.scan_media) - .setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER); - menu.add(0, R.id.action_go_to_start_directory, 1, R.string.action_go_to_start_directory) - .setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER); - menu.removeItem(R.id.action_grid_size); - menu.removeItem(R.id.action_layout_type); - menu.removeItem(R.id.action_sort_order); - ToolbarContentTintHelper.handleOnCreateOptionsMenu( - requireContext(), binding.toolbar, menu, getToolbarBackgroundColor(binding.toolbar)); - } - - @Override - public boolean onOptionsItemSelected(@NonNull MenuItem item) { - switch (item.getItemId()) { - case R.id.action_go_to_start_directory: - setCrumb( - new BreadCrumbLayout.Crumb( - tryGetCanonicalFile(PreferenceUtil.INSTANCE.getStartDirectory())), - true); - return true; - case R.id.action_scan: - BreadCrumbLayout.Crumb crumb = getActiveCrumb(); - if (crumb != null) { - //noinspection Convert2MethodRef - new ListPathsAsyncTask(getActivity(), paths -> scanPaths(paths)) - .execute(new ListPathsAsyncTask.LoadingInfo(crumb.getFile(), AUDIO_FILE_FILTER)); - } - return true; - } - return super.onOptionsItemSelected(item); - } - - @Override - public void onQueueChanged() { - super.onQueueChanged(); - checkForPadding(); - } - - @Override - public void onServiceConnected() { - super.onServiceConnected(); - checkForPadding(); - } - - @NonNull - @Override - public MaterialCab openCab(int menuRes, @NotNull MaterialCab.Callback callback) { - if (cab != null && cab.isActive()) { - cab.finish(); - } - cab = - new MaterialCab(getMainActivity(), R.id.cab_stub) - .setMenu(menuRes) - .setCloseDrawableRes(R.drawable.ic_close) - .setBackgroundColor( - RetroColorUtil.shiftBackgroundColorForLightText( - ATHUtil.INSTANCE.resolveColor(requireContext(), R.attr.colorSurface))) - .start(callback); - return cab; - } - - private void checkForPadding() { - final int count = adapter.getItemCount(); - if (binding != null) { - final MarginLayoutParams params = (MarginLayoutParams) binding.coordinatorLayout.getLayoutParams(); - params.bottomMargin = - count > 0 && !MusicPlayerRemote.getPlayingQueue().isEmpty() - ? DensityUtil.dip2px(requireContext(), 104f) - : DensityUtil.dip2px(requireContext(), 54f); - binding.coordinatorLayout.setLayoutParams(params); - } - } - - private void checkIsEmpty() { - if (binding != null) { - binding.emptyEmoji.setText(getEmojiByUnicode(0x1F631)); - binding.empty.setVisibility( - adapter == null || adapter.getItemCount() == 0 ? View.VISIBLE : View.GONE); - } - } - - @Nullable - private BreadCrumbLayout.Crumb getActiveCrumb() { - if (binding != null) { - return binding.breadCrumbs.size() > 0 - ? binding.breadCrumbs.getCrumb(binding.breadCrumbs.getActiveIndex()) - : null; - } - return null; - } - - private String getEmojiByUnicode(int unicode) { - return new String(Character.toChars(unicode)); - } - - private Comparator getFileComparator() { - return fileComparator; - } - - private void saveScrollPosition() { - BreadCrumbLayout.Crumb crumb = getActiveCrumb(); - if (crumb != null) { - crumb.setScrollPosition( - ((LinearLayoutManager) binding.recyclerView.getLayoutManager()).findFirstVisibleItemPosition()); - } - } - - private void scanPaths(@Nullable String[] toBeScanned) { - if (getActivity() == null) { - return; - } - if (toBeScanned == null || toBeScanned.length < 1) { - Toast.makeText(getActivity(), R.string.nothing_to_scan, Toast.LENGTH_SHORT).show(); - } else { - MediaScannerConnection.scanFile( - getActivity().getApplicationContext(), - toBeScanned, - null, - new UpdateToastMediaScannerCompletionListener(getActivity(), Arrays.asList(toBeScanned))); - } - } - - private void setCrumb(BreadCrumbLayout.Crumb crumb, boolean addToHistory) { - if (crumb == null) { - return; - } - String path = crumb.getFile().getPath(); - if (path.equals("/") || path.equals("/storage") || path.equals("/storage/emulated")) { - switchToStorageAdapter(); - } else { - saveScrollPosition(); - binding.breadCrumbs.setActiveOrAdd(crumb, false); - if (addToHistory) { - binding.breadCrumbs.addHistory(crumb); - } - LoaderManager.getInstance(this).restartLoader(LOADER_ID, null, this); - } - } - - private void setUpAdapter() { - switchToFileAdapter(); - } - - private void setUpAppbarColor() { - binding.breadCrumbs.setActivatedContentColor( - ATHUtil.INSTANCE.resolveColor(requireContext(), android.R.attr.textColorPrimary)); - binding.breadCrumbs.setDeactivatedContentColor( - ATHUtil.INSTANCE.resolveColor(requireContext(), android.R.attr.textColorSecondary)); - } - - private void setUpBreadCrumbs() { - binding.breadCrumbs.setCallback(this); - } - - private void setUpRecyclerView() { - binding.recyclerView.setLayoutManager(new LinearLayoutManager(getActivity())); - FastScroller fastScroller = ThemedFastScroller.INSTANCE.create(binding.recyclerView); - binding.recyclerView.setOnApplyWindowInsetsListener( - new ScrollingViewOnApplyWindowInsetsListener(binding.recyclerView, fastScroller)); - } - - private ArrayList toList(File file) { - ArrayList files = new ArrayList<>(1); - files.add(file); - return files; - } - - private void updateAdapter(@NonNull List files) { - adapter.swapDataSet(files); - BreadCrumbLayout.Crumb crumb = getActiveCrumb(); - if (crumb != null) { - ((LinearLayoutManager) binding.recyclerView.getLayoutManager()) - .scrollToPositionWithOffset(crumb.getScrollPosition(), 0); - } - } - - @Override - public void onDestroyView() { - super.onDestroyView(); - binding = null; - } - - public static class ListPathsAsyncTask - extends ListingFilesDialogAsyncTask { - - private final WeakReference onPathsListedCallbackWeakReference; - - public ListPathsAsyncTask(Context context, OnPathsListedCallback callback) { - super(context); - onPathsListedCallbackWeakReference = new WeakReference<>(callback); - } - - @Override - protected String[] doInBackground(LoadingInfo... params) { - try { - if (isCancelled() || checkCallbackReference() == null) { - return null; - } - - LoadingInfo info = params[0]; - - final String[] paths; - - if (info.file.isDirectory()) { - List files = FileUtil.listFilesDeep(info.file, info.fileFilter); - - if (isCancelled() || checkCallbackReference() == null) { - return null; - } - - paths = new String[files.size()]; - for (int i = 0; i < files.size(); i++) { - File f = files.get(i); - paths[i] = FileUtil.safeGetCanonicalPath(f); - - if (isCancelled() || checkCallbackReference() == null) { - return null; - } - } - } else { - paths = new String[1]; - paths[0] = info.file.getPath(); - } - - return paths; - } catch (Exception e) { - e.printStackTrace(); - cancel(false); - return null; - } - } - - @Override - protected void onPostExecute(String[] paths) { - super.onPostExecute(paths); - OnPathsListedCallback callback = checkCallbackReference(); - if (callback != null && paths != null) { - callback.onPathsListed(paths); - } - } - - @Override - protected void onPreExecute() { - super.onPreExecute(); - checkCallbackReference(); - } - - private OnPathsListedCallback checkCallbackReference() { - OnPathsListedCallback callback = onPathsListedCallbackWeakReference.get(); - if (callback == null) { - cancel(false); - } - return callback; - } - - public interface OnPathsListedCallback { - - void onPathsListed(@NonNull String[] paths); - } - - public static class LoadingInfo { - - public final File file; - - final FileFilter fileFilter; - - public LoadingInfo(File file, FileFilter fileFilter) { - this.file = file; - this.fileFilter = fileFilter; - } - } - } - - private static class AsyncFileLoader extends WrappedAsyncTaskLoader> { - - private final WeakReference fragmentWeakReference; - - AsyncFileLoader(FoldersFragment foldersFragment) { - super(foldersFragment.requireActivity()); - fragmentWeakReference = new WeakReference<>(foldersFragment); - } - - @Override - public List loadInBackground() { - FoldersFragment foldersFragment = fragmentWeakReference.get(); - File directory = null; - if (foldersFragment != null) { - BreadCrumbLayout.Crumb crumb = foldersFragment.getActiveCrumb(); - if (crumb != null) { - directory = crumb.getFile(); - } - } - if (directory != null) { - List files = FileUtil.listFiles(directory, AUDIO_FILE_FILTER); - Collections.sort(files, foldersFragment.getFileComparator()); - return files; - } else { - return new LinkedList<>(); - } - } - } - - private static class ListSongsAsyncTask - extends ListingFilesDialogAsyncTask> { - - private final Object extra; - private final WeakReference callbackWeakReference; - private final WeakReference contextWeakReference; - - ListSongsAsyncTask(Context context, Object extra, OnSongsListedCallback callback) { - super(context); - this.extra = extra; - contextWeakReference = new WeakReference<>(context); - callbackWeakReference = new WeakReference<>(callback); - } - - @Override - protected List doInBackground(LoadingInfo... params) { - try { - LoadingInfo info = params[0]; - List files = FileUtil.listFilesDeep(info.files, info.fileFilter); - - if (isCancelled() || checkContextReference() == null || checkCallbackReference() == null) { - return null; - } - - Collections.sort(files, info.fileComparator); - - Context context = checkContextReference(); - if (isCancelled() || context == null || checkCallbackReference() == null) { - return null; - } - - return FileUtil.matchFilesWithMediaStore(context, files); - } catch (Exception e) { - e.printStackTrace(); - cancel(false); - return null; - } - } - - @Override - protected void onPostExecute(List songs) { - super.onPostExecute(songs); - OnSongsListedCallback callback = checkCallbackReference(); - if (songs != null && callback != null) { - callback.onSongsListed(songs, extra); - } - } - - @Override - protected void onPreExecute() { - super.onPreExecute(); - checkCallbackReference(); - checkContextReference(); - } - - private OnSongsListedCallback checkCallbackReference() { - OnSongsListedCallback callback = callbackWeakReference.get(); - if (callback == null) { - cancel(false); - } - return callback; - } - - private Context checkContextReference() { - Context context = contextWeakReference.get(); - if (context == null) { - cancel(false); - } - return context; - } - - public interface OnSongsListedCallback { - - void onSongsListed(@NonNull List songs, Object extra); - } - - static class LoadingInfo { - - final Comparator fileComparator; - - final FileFilter fileFilter; - - final List files; - - LoadingInfo( - @NonNull List files, - @NonNull FileFilter fileFilter, - @NonNull Comparator fileComparator) { - this.fileComparator = fileComparator; - this.fileFilter = fileFilter; - this.files = files; - } - } - } - - private abstract static class ListingFilesDialogAsyncTask - extends DialogAsyncTask { - - ListingFilesDialogAsyncTask(Context context) { - super(context); - } - - public ListingFilesDialogAsyncTask(Context context, int showDelay) { - super(context, showDelay); - } - - @Override - protected Dialog createDialog(@NonNull Context context) { - return new MaterialAlertDialogBuilder(context) - .setTitle(R.string.listing_files) - .setCancelable(false) - .setView(R.layout.loading) - .setOnCancelListener(dialog -> cancel(false)) - .setOnDismissListener(dialog -> cancel(false)) - .create(); - } - } - - // https://github.com/DrKLO/Telegram/blob/ab221dafadbc17459d78d9ea3e643ae18e934b16/TMessagesProj/src/main/java/org/telegram/ui/Components/ChatAttachAlertDocumentLayout.java#L939 - private void listRoots() { - storageItems.clear(); - HashSet paths = new HashSet<>(); - String defaultPath = Environment.getExternalStorageDirectory().getPath(); - String defaultPathState = Environment.getExternalStorageState(); - if (defaultPathState.equals(Environment.MEDIA_MOUNTED) || defaultPathState.equals(Environment.MEDIA_MOUNTED_READ_ONLY)) { - Storage ext = new Storage(); - if (Environment.isExternalStorageRemovable()) { - ext.title = "SD Card"; - } else { - ext.title = "Internal Storage"; - } - ext.file = Environment.getExternalStorageDirectory(); - storageItems.add(ext); - paths.add(defaultPath); - } - - BufferedReader bufferedReader = null; - try { - bufferedReader = new BufferedReader(new FileReader("/proc/mounts")); - String line; - while ((line = bufferedReader.readLine()) != null) { - if (line.contains("vfat") || line.contains("/mnt")) { - StringTokenizer tokens = new StringTokenizer(line, " "); - tokens.nextToken(); - String path = tokens.nextToken(); - if (paths.contains(path)) { - continue; - } - if (line.contains("/dev/block/vold")) { - if (!line.contains("/mnt/secure") && !line.contains("/mnt/asec") && !line.contains("/mnt/obb") && !line.contains("/dev/mapper") && !line.contains("tmpfs")) { - if (!new File(path).isDirectory()) { - int index = path.lastIndexOf('/'); - if (index != -1) { - String newPath = "/storage/" + path.substring(index + 1); - if (new File(newPath).isDirectory()) { - path = newPath; - } - } - } - paths.add(path); - try { - Storage item = new Storage(); - if (path.toLowerCase().contains("sd")) { - item.title = "SD Card"; - } else { - item.title = "External Storage"; - } - item.file = new File(path); - storageItems.add(item); - } catch (Exception e) { - e.printStackTrace(); - } - } - } - } - } - } catch (Exception e) { - e.printStackTrace(); - } finally { - if (bufferedReader != null) { - try { - bufferedReader.close(); - } catch (Exception e) { - e.printStackTrace(); - } - } - } - } - - @Override - public void onStorageClicked(@NonNull Storage storage) { - switchToFileAdapter(); - setCrumb( - new BreadCrumbLayout.Crumb( - FileUtil.safeGetCanonicalFile(storage.file)), - true); - } - - public void switchToFileAdapter() { - adapter = - new SongFileAdapter(getMainActivity(), new LinkedList<>(), R.layout.item_list, this, this); - adapter.registerAdapterDataObserver( - new RecyclerView.AdapterDataObserver() { - @Override - public void onChanged() { - super.onChanged(); - checkIsEmpty(); - checkForPadding(); - } - }); - binding.recyclerView.setAdapter(adapter); - checkIsEmpty(); - } - - public void switchToStorageAdapter() { - listRoots(); - storageAdapter = new StorageAdapter(storageItems, this); - binding.recyclerView.setAdapter(storageAdapter); - binding.breadCrumbs.clearCrumbs(); - } -} diff --git a/app/src/main/java/code/name/monkey/retromusic/fragments/folder/FoldersFragment.kt b/app/src/main/java/code/name/monkey/retromusic/fragments/folder/FoldersFragment.kt new file mode 100644 index 00000000..ed8bab54 --- /dev/null +++ b/app/src/main/java/code/name/monkey/retromusic/fragments/folder/FoldersFragment.kt @@ -0,0 +1,817 @@ +/* + * Copyright (c) 2020 Hemanth Savarala. + * + * Licensed under the GNU General Public License v3 + * + * This is free software: you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by + * the Free Software Foundation either version 3 of the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + */ +package code.name.monkey.retromusic.fragments.folder + +import android.app.Dialog +import android.content.Context +import android.media.MediaScannerConnection +import android.os.Bundle +import android.os.Environment +import android.text.Html +import android.view.* +import android.webkit.MimeTypeMap +import android.widget.PopupMenu +import android.widget.Toast +import androidx.activity.OnBackPressedCallback +import androidx.loader.app.LoaderManager +import androidx.loader.content.Loader +import androidx.navigation.Navigation.findNavController +import androidx.recyclerview.widget.LinearLayoutManager +import androidx.recyclerview.widget.RecyclerView +import code.name.monkey.appthemehelper.ThemeStore.Companion.accentColor +import code.name.monkey.appthemehelper.common.ATHToolbarActivity +import code.name.monkey.appthemehelper.util.ATHUtil.resolveColor +import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper +import code.name.monkey.retromusic.App +import code.name.monkey.retromusic.R +import code.name.monkey.retromusic.adapter.SongFileAdapter +import code.name.monkey.retromusic.adapter.Storage +import code.name.monkey.retromusic.adapter.StorageAdapter +import code.name.monkey.retromusic.adapter.StorageClickListener +import code.name.monkey.retromusic.databinding.FragmentFolderBinding +import code.name.monkey.retromusic.extensions.drawNextToNavbar +import code.name.monkey.retromusic.extensions.navigate +import code.name.monkey.retromusic.extensions.surfaceColor +import code.name.monkey.retromusic.fragments.base.AbsMainActivityFragment +import code.name.monkey.retromusic.fragments.folder.FoldersFragment.ListPathsAsyncTask.OnPathsListedCallback +import code.name.monkey.retromusic.fragments.folder.FoldersFragment.ListSongsAsyncTask.OnSongsListedCallback +import code.name.monkey.retromusic.helper.MusicPlayerRemote.openQueue +import code.name.monkey.retromusic.helper.MusicPlayerRemote.playingQueue +import code.name.monkey.retromusic.helper.menu.SongMenuHelper.handleMenuClick +import code.name.monkey.retromusic.helper.menu.SongsMenuHelper +import code.name.monkey.retromusic.interfaces.ICabCallback +import code.name.monkey.retromusic.interfaces.ICabHolder +import code.name.monkey.retromusic.interfaces.ICallbacks +import code.name.monkey.retromusic.interfaces.IMainActivityFragmentCallbacks +import code.name.monkey.retromusic.misc.DialogAsyncTask +import code.name.monkey.retromusic.misc.UpdateToastMediaScannerCompletionListener +import code.name.monkey.retromusic.misc.WrappedAsyncTaskLoader +import code.name.monkey.retromusic.model.Song +import code.name.monkey.retromusic.providers.BlacklistStore +import code.name.monkey.retromusic.util.* +import code.name.monkey.retromusic.util.DensityUtil.dip2px +import code.name.monkey.retromusic.util.PreferenceUtil.startDirectory +import code.name.monkey.retromusic.util.ThemedFastScroller.create +import code.name.monkey.retromusic.views.BreadCrumbLayout.Crumb +import code.name.monkey.retromusic.views.BreadCrumbLayout.SelectionCallback +import code.name.monkey.retromusic.views.ScrollingViewOnApplyWindowInsetsListener +import com.afollestad.materialcab.attached.AttachedCab +import com.afollestad.materialcab.attached.destroy +import com.afollestad.materialcab.attached.isActive +import com.afollestad.materialcab.createCab +import com.google.android.material.dialog.MaterialAlertDialogBuilder +import com.google.android.material.shape.MaterialShapeDrawable +import com.google.android.material.snackbar.Snackbar +import com.google.android.material.transition.MaterialFadeThrough +import com.google.android.material.transition.MaterialSharedAxis +import java.io.* +import java.lang.ref.WeakReference +import java.util.* + +class FoldersFragment : AbsMainActivityFragment(R.layout.fragment_folder), + IMainActivityFragmentCallbacks, ICabHolder, SelectionCallback, ICallbacks, + LoaderManager.LoaderCallbacks>, StorageClickListener { + private var _binding: FragmentFolderBinding? = null + private val binding get() = _binding!! + private var adapter: SongFileAdapter? = null + private var storageAdapter: StorageAdapter? = null + private var cab: AttachedCab? = null + private val fileComparator = Comparator { lhs: File, rhs: File -> + if (lhs.isDirectory && !rhs.isDirectory) { + return@Comparator -1 + } else if (!lhs.isDirectory && rhs.isDirectory) { + return@Comparator 1 + } else { + return@Comparator lhs.name.compareTo(rhs.name, ignoreCase = true) + } + } + private var storageItems = ArrayList() + override fun onCreateView( + inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? + ): View { + _binding = FragmentFolderBinding.inflate(inflater, container, false) + return binding.root + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + mainActivity.addMusicServiceEventListener(libraryViewModel) + mainActivity.setSupportActionBar(binding.toolbar) + mainActivity.supportActionBar?.title = null + enterTransition = MaterialFadeThrough().apply { + addTarget(binding.recyclerView) + } + setUpBreadCrumbs() + setUpRecyclerView() + setUpAdapter() + setUpTitle() + requireActivity().onBackPressedDispatcher.addCallback( + viewLifecycleOwner, + object : OnBackPressedCallback(true) { + override fun handleOnBackPressed() { + if (!handleBackPress()) { + remove() + mainActivity.finish() + } + } + }) + binding.toolbarContainer.drawNextToNavbar() + binding.appBarLayout.statusBarForeground = + MaterialShapeDrawable.createWithElevationOverlay(requireContext()) + } + + private fun setUpTitle() { + binding.toolbar.setNavigationOnClickListener { v: View? -> + exitTransition = MaterialSharedAxis(MaterialSharedAxis.Z, true).setDuration(300) + reenterTransition = MaterialSharedAxis(MaterialSharedAxis.Z, false).setDuration(300) + findNavController(v!!).navigate(R.id.searchFragment, null, navOptions) + } + binding.appNameText.text = resources.getString(R.string.folders) + } + + override fun onActivityCreated(savedInstanceState: Bundle?) { + super.onActivityCreated(savedInstanceState) + setHasOptionsMenu(true) + if (savedInstanceState == null) { + switchToFileAdapter() + setCrumb( + Crumb( + FileUtil.safeGetCanonicalFile(startDirectory) + ), + true + ) + } else { + binding.breadCrumbs.restoreFromStateWrapper(savedInstanceState.getParcelable(CRUMBS)) + LoaderManager.getInstance(this).initLoader(LOADER_ID, null, this) + } + } + + override fun onPause() { + super.onPause() + saveScrollPosition() + } + + override fun onSaveInstanceState(outState: Bundle) { + super.onSaveInstanceState(outState) + if (_binding != null) { + outState.putParcelable(CRUMBS, binding.breadCrumbs.stateWrapper) + } + } + + override fun handleBackPress(): Boolean { + if (cab != null && cab!!.isActive()) { + cab?.destroy() + return true + } + if (binding.breadCrumbs.popHistory()) { + setCrumb(binding.breadCrumbs.lastHistory(), false) + return true + } + return false + } + + override fun onCreateLoader(id: Int, args: Bundle?): Loader> { + return AsyncFileLoader(this) + } + + override fun onCrumbSelection(crumb: Crumb, index: Int) { + setCrumb(crumb, true) + } + + override fun onFileMenuClicked(file: File, view: View) { + val popupMenu = PopupMenu(activity, view) + if (file.isDirectory) { + popupMenu.inflate(R.menu.menu_item_directory) + popupMenu.setOnMenuItemClickListener { item: MenuItem -> + when (val itemId = item.itemId) { + R.id.action_play_next, R.id.action_add_to_current_playing, R.id.action_add_to_playlist, R.id.action_delete_from_device -> { + ListSongsAsyncTask( + activity, + null, + object : OnSongsListedCallback { + override fun onSongsListed(songs: List, extra: Any?) { + if (songs.isNotEmpty()) { + SongsMenuHelper.handleMenuClick( + requireActivity(), songs, itemId + ) + } + } + }) + .execute( + ListSongsAsyncTask.LoadingInfo( + toList(file), AUDIO_FILE_FILTER, fileComparator + ) + ) + return@setOnMenuItemClickListener true + } + R.id.action_add_to_blacklist -> { + BlacklistStore.getInstance(App.getContext()).addPath(file) + return@setOnMenuItemClickListener true + } + R.id.action_set_as_start_directory -> { + startDirectory = file + Toast.makeText( + activity, + String.format(getString(R.string.new_start_directory), file.path), + Toast.LENGTH_SHORT + ) + .show() + return@setOnMenuItemClickListener true + } + R.id.action_scan -> { + ListPathsAsyncTask( + activity, + object : OnPathsListedCallback { + override fun onPathsListed(paths: Array) { + scanPaths(paths) + } + }) + .execute(ListPathsAsyncTask.LoadingInfo(file, AUDIO_FILE_FILTER)) + return@setOnMenuItemClickListener true + } + } + false + } + } else { + popupMenu.inflate(R.menu.menu_item_file) + popupMenu.setOnMenuItemClickListener { item: MenuItem -> + when (val itemId = item.itemId) { + R.id.action_play_next, R.id.action_add_to_current_playing, R.id.action_add_to_playlist, R.id.action_go_to_album, R.id.action_go_to_artist, R.id.action_share, R.id.action_tag_editor, R.id.action_details, R.id.action_set_as_ringtone, R.id.action_delete_from_device -> { + ListSongsAsyncTask( + activity, + null, + object : OnSongsListedCallback { + override fun onSongsListed(songs: List, extra: Any?) { + handleMenuClick( + requireActivity(), songs[0], itemId + ) + } + }) + .execute( + ListSongsAsyncTask.LoadingInfo( + toList(file), AUDIO_FILE_FILTER, fileComparator + ) + ) + return@setOnMenuItemClickListener true + } + R.id.action_scan -> { + ListPathsAsyncTask( + activity, + object : OnPathsListedCallback { + override fun onPathsListed(paths: Array) { + scanPaths(paths) + } + }) + .execute(ListPathsAsyncTask.LoadingInfo(file, AUDIO_FILE_FILTER)) + return@setOnMenuItemClickListener true + } + } + false + } + } + popupMenu.show() + } + + override fun onFileSelected(file: File) { + var mFile = file + mFile = tryGetCanonicalFile(mFile) // important as we compare the path value later + if (mFile.isDirectory) { + setCrumb(Crumb(mFile), true) + } else { + val fileFilter = FileFilter { pathname: File -> + !pathname.isDirectory && AUDIO_FILE_FILTER.accept(pathname) + } + ListSongsAsyncTask( + activity, + mFile, + object : OnSongsListedCallback { + override fun onSongsListed(songs: List, extra: Any?) { + val file1 = extra as File + var startIndex = -1 + for (i in songs.indices) { + if (file1 + .path + == songs[i].data + ) { // path is already canonical here + startIndex = i + break + } + } + if (startIndex > -1) { + openQueue(songs, startIndex, true) + } else { + Snackbar.make( + binding.root, + Html.fromHtml( + String.format( + getString(R.string.not_listed_in_media_store), file1.name + ) + ), + Snackbar.LENGTH_LONG + ) + .setAction( + R.string.action_scan + ) { + ListPathsAsyncTask( + requireActivity(), + object : OnPathsListedCallback { + override fun onPathsListed(paths: Array) { + scanPaths(paths) + } + }) + .execute( + ListPathsAsyncTask.LoadingInfo( + file1, AUDIO_FILE_FILTER + ) + ) + } + .setActionTextColor(accentColor(requireActivity())) + .show() + } + } + }) + .execute( + ListSongsAsyncTask.LoadingInfo( + toList(mFile.parentFile), fileFilter, fileComparator + ) + ) + } + } + + override fun onLoadFinished(loader: Loader>, data: List) { + updateAdapter(data) + } + + override fun onLoaderReset(loader: Loader>) { + updateAdapter(LinkedList()) + } + + override fun onMultipleItemAction(item: MenuItem, files: ArrayList) { + val itemId = item.itemId + ListSongsAsyncTask( + activity, + null, + object : OnSongsListedCallback { + override fun onSongsListed(songs: List, extra: Any?) { + SongsMenuHelper.handleMenuClick( + requireActivity(), + songs, + itemId + ) + } + }) + .execute(ListSongsAsyncTask.LoadingInfo(files, AUDIO_FILE_FILTER, fileComparator)) + } + + override fun onPrepareOptionsMenu(menu: Menu) { + super.onPrepareOptionsMenu(menu) + ToolbarContentTintHelper.handleOnPrepareOptionsMenu(requireActivity(), binding.toolbar) + } + + override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) { + super.onCreateOptionsMenu(menu, inflater) + menu.add(0, R.id.action_scan, 0, R.string.scan_media) + .setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER) + menu.add(0, R.id.action_go_to_start_directory, 1, R.string.action_go_to_start_directory) + .setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER) + menu.removeItem(R.id.action_grid_size) + menu.removeItem(R.id.action_layout_type) + menu.removeItem(R.id.action_sort_order) + ToolbarContentTintHelper.handleOnCreateOptionsMenu( + requireContext(), binding.toolbar, menu, ATHToolbarActivity.getToolbarBackgroundColor( + binding.toolbar + ) + ) + } + + override fun onOptionsItemSelected(item: MenuItem): Boolean { + when (item.itemId) { + R.id.action_go_to_start_directory -> { + setCrumb( + Crumb( + tryGetCanonicalFile(startDirectory) + ), + true + ) + return true + } + R.id.action_scan -> { + val crumb = activeCrumb + if (crumb != null) { + ListPathsAsyncTask( + activity, + object : OnPathsListedCallback { + override fun onPathsListed(paths: Array) { + scanPaths(paths) + } + }) + .execute(ListPathsAsyncTask.LoadingInfo(crumb.file, AUDIO_FILE_FILTER)) + } + return true + } + } + return super.onOptionsItemSelected(item) + } + + override fun onQueueChanged() { + super.onQueueChanged() + checkForPadding() + } + + override fun onServiceConnected() { + super.onServiceConnected() + checkForPadding() + } + + override fun openCab(menuRes: Int, callback: ICabCallback): AttachedCab { + if (cab != null && cab!!.isActive()) { + cab?.destroy() + } + cab = createCab(R.id.toolbar_container) { + menu(menuRes) + closeDrawable(R.drawable.ic_close) + backgroundColor(literal = RetroColorUtil.shiftBackgroundColor(surfaceColor())) + slideDown() + onCreate { cab, menu -> callback.onCabCreated(cab, menu) } + onSelection { + callback.onCabItemClicked(it) + } + onDestroy { callback.onCabFinished(it) } + } + return cab as AttachedCab + } + + private fun checkForPadding() { + val count = adapter?.itemCount ?: 0 + if (_binding != null) { + val params = binding.root.layoutParams as ViewGroup.MarginLayoutParams + params.bottomMargin = if (count > 0 && playingQueue.isNotEmpty()) dip2px( + requireContext(), + 104f + ) else dip2px(requireContext(), 54f) + binding.root.layoutParams = params + } + } + + private fun checkIsEmpty() { + if (_binding != null) { + binding.emptyEmoji.text = getEmojiByUnicode(0x1F631) + binding.empty.visibility = + if (adapter == null || adapter!!.itemCount == 0) View.VISIBLE else View.GONE + } + } + + private val activeCrumb: Crumb? + get() = if (_binding != null) { + if (binding.breadCrumbs.size() > 0) binding.breadCrumbs.getCrumb(binding.breadCrumbs.activeIndex) else null + } else null + + private fun getEmojiByUnicode(unicode: Int): String { + return String(Character.toChars(unicode)) + } + + private fun saveScrollPosition() { + val crumb = activeCrumb + if (crumb != null) { + crumb.scrollPosition = + (binding.recyclerView.layoutManager as LinearLayoutManager?)!!.findFirstVisibleItemPosition() + } + } + + private fun scanPaths(toBeScanned: Array) { + if (activity == null) { + return + } + if (toBeScanned.isEmpty()) { + Toast.makeText(activity, R.string.nothing_to_scan, Toast.LENGTH_SHORT).show() + } else { + MediaScannerConnection.scanFile( + requireContext(), + toBeScanned, + null, + UpdateToastMediaScannerCompletionListener(activity, listOf(*toBeScanned)) + ) + } + } + + private fun setCrumb(crumb: Crumb?, addToHistory: Boolean) { + if (crumb == null) { + return + } + val path = crumb.file.path + if (path == "/" || path == "/storage" || path == "/storage/emulated") { + switchToStorageAdapter() + } else { + saveScrollPosition() + binding.breadCrumbs.setActiveOrAdd(crumb, false) + if (addToHistory) { + binding.breadCrumbs.addHistory(crumb) + } + LoaderManager.getInstance(this).restartLoader(LOADER_ID, null, this) + } + } + + private fun setUpAdapter() { + switchToFileAdapter() + } + + private fun setUpBreadCrumbs() { + binding.breadCrumbs.setActivatedContentColor( + resolveColor(requireContext(), android.R.attr.textColorPrimary) + ) + binding.breadCrumbs.setDeactivatedContentColor( + resolveColor(requireContext(), android.R.attr.textColorSecondary) + ) + binding.breadCrumbs.setCallback(this) + } + + private fun setUpRecyclerView() { + binding.recyclerView.layoutManager = LinearLayoutManager( + activity + ) + val fastScroller = create( + binding.recyclerView + ) + binding.recyclerView.setOnApplyWindowInsetsListener( + ScrollingViewOnApplyWindowInsetsListener(binding.recyclerView, fastScroller) + ) + } + + private fun toList(file: File): ArrayList { + val files = ArrayList(1) + files.add(file) + return files + } + + private fun updateAdapter(files: List) { + adapter?.swapDataSet(files) + val crumb = activeCrumb + if (crumb != null) { + (binding.recyclerView.layoutManager as LinearLayoutManager?) + ?.scrollToPositionWithOffset(crumb.scrollPosition, 0) + } + } + + override fun onDestroyView() { + super.onDestroyView() + _binding = null + } + + class ListPathsAsyncTask(context: Context?, callback: OnPathsListedCallback) : + ListingFilesDialogAsyncTask>( + context + ) { + private val onPathsListedCallbackWeakReference: WeakReference = + WeakReference(callback) + + override fun doInBackground(vararg params: LoadingInfo): Array { + return try { + if (isCancelled || checkCallbackReference() == null) { + return arrayOf() + } + val info = params[0] + val paths: Array + if (info.file.isDirectory) { + val files = FileUtil.listFilesDeep(info.file, info.fileFilter) + if (isCancelled || checkCallbackReference() == null) { + return arrayOf() + } + paths = arrayOfNulls(files.size) + for (i in files.indices) { + val f = files[i] + paths[i] = FileUtil.safeGetCanonicalPath(f) + if (isCancelled || checkCallbackReference() == null) { + return arrayOf() + } + } + } else { + paths = arrayOfNulls(1) + paths[0] = info.file.path + } + paths + } catch (e: Exception) { + e.printStackTrace() + cancel(false) + arrayOf() + } + } + + override fun onPostExecute(paths: Array) { + super.onPostExecute(paths) + checkCallbackReference()?.onPathsListed(paths) + } + + override fun onPreExecute() { + super.onPreExecute() + checkCallbackReference() + } + + private fun checkCallbackReference(): OnPathsListedCallback? { + val callback = onPathsListedCallbackWeakReference.get() + if (callback == null) { + cancel(false) + } + return callback + } + + interface OnPathsListedCallback { + fun onPathsListed(paths: Array) + } + + class LoadingInfo(val file: File, val fileFilter: FileFilter) + + } + + private class AsyncFileLoader(foldersFragment: FoldersFragment) : + WrappedAsyncTaskLoader>(foldersFragment.requireActivity()) { + private val fragmentWeakReference: WeakReference = + WeakReference(foldersFragment) + + override fun loadInBackground(): List { + val foldersFragment = fragmentWeakReference.get() + var directory: File? = null + if (foldersFragment != null) { + val crumb = foldersFragment.activeCrumb + if (crumb != null) { + directory = crumb.file + } + } + return if (directory != null) { + val files = FileUtil.listFiles( + directory, + AUDIO_FILE_FILTER + ) + Collections.sort(files, foldersFragment!!.fileComparator) + files + } else { + LinkedList() + } + } + + } + + private open class ListSongsAsyncTask( + context: Context?, + private val extra: Any?, + callback: OnSongsListedCallback + ) : ListingFilesDialogAsyncTask>(context) { + private val callbackWeakReference = WeakReference(callback) + private val contextWeakReference = WeakReference(context) + override fun doInBackground(vararg params: LoadingInfo): List { + return try { + val info = params[0] + val files = FileUtil.listFilesDeep(info.files, info.fileFilter) + if (isCancelled || checkContextReference() == null || checkCallbackReference() == null) { + return emptyList() + } + Collections.sort(files, info.fileComparator) + val context = checkContextReference() + if (isCancelled || context == null || checkCallbackReference() == null) { + emptyList() + } else FileUtil.matchFilesWithMediaStore(context, files) + } catch (e: Exception) { + e.printStackTrace() + cancel(false) + emptyList() + } + } + + override fun onPostExecute(songs: List) { + super.onPostExecute(songs) + checkCallbackReference()?.onSongsListed(songs, extra) + } + + override fun onPreExecute() { + super.onPreExecute() + checkCallbackReference() + checkContextReference() + } + + private fun checkCallbackReference(): OnSongsListedCallback? { + val callback = callbackWeakReference.get() + if (callback == null) { + cancel(false) + } + return callback + } + + private fun checkContextReference(): Context? { + val context = contextWeakReference.get() + if (context == null) { + cancel(false) + } + return context + } + + interface OnSongsListedCallback { + fun onSongsListed(songs: List, extra: Any?) + } + + class LoadingInfo( + val files: List, + val fileFilter: FileFilter, + val fileComparator: Comparator + ) + + } + + abstract class ListingFilesDialogAsyncTask : + DialogAsyncTask { + internal constructor(context: Context?) : super(context) + + override fun createDialog(context: Context): Dialog { + return MaterialAlertDialogBuilder(context) + .setTitle(R.string.listing_files) + .setCancelable(false) + .setView(R.layout.loading) + .setOnCancelListener { cancel(false) } + .setOnDismissListener { cancel(false) } + .create() + } + } + + override fun onStorageClicked(storage: Storage) { + switchToFileAdapter() + setCrumb( + Crumb( + FileUtil.safeGetCanonicalFile(storage.file) + ), + true + ) + } + + private fun switchToFileAdapter() { + adapter = SongFileAdapter(mainActivity, LinkedList(), R.layout.item_list, this, this) + adapter!!.registerAdapterDataObserver( + object : RecyclerView.AdapterDataObserver() { + override fun onChanged() { + super.onChanged() + checkIsEmpty() + checkForPadding() + } + }) + binding.recyclerView.adapter = adapter + checkIsEmpty() + } + + private fun switchToStorageAdapter() { + storageItems = FileUtil.listRoots() + storageAdapter = StorageAdapter(storageItems, this) + binding.recyclerView.adapter = storageAdapter + binding.breadCrumbs.clearCrumbs() + } + + companion object { + val TAG: String = FoldersFragment::class.java.simpleName + val AUDIO_FILE_FILTER = FileFilter { file: File -> + (!file.isHidden + && (file.isDirectory + || FileUtil.fileIsMimeType(file, "audio/*", MimeTypeMap.getSingleton()) + || FileUtil.fileIsMimeType(file, "application/opus", MimeTypeMap.getSingleton()) + || FileUtil.fileIsMimeType( + file, + "application/ogg", + MimeTypeMap.getSingleton() + ))) + } + private const val CRUMBS = "crumbs" + private const val LOADER_ID = 5 + + // root + val defaultStartDirectory: File + get() { + val musicDir = + Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_MUSIC) + val startFolder = if (musicDir.exists() && musicDir.isDirectory) { + musicDir + } else { + val externalStorage = Environment.getExternalStorageDirectory() + if (externalStorage.exists() && externalStorage.isDirectory) { + externalStorage + } else { + File("/") // root + } + } + return startFolder + } + + private fun tryGetCanonicalFile(file: File): File { + return try { + file.canonicalFile + } catch (e: IOException) { + e.printStackTrace() + file + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/code/name/monkey/retromusic/fragments/genres/GenreDetailsFragment.kt b/app/src/main/java/code/name/monkey/retromusic/fragments/genres/GenreDetailsFragment.kt index 567df41f..a12d787b 100644 --- a/app/src/main/java/code/name/monkey/retromusic/fragments/genres/GenreDetailsFragment.kt +++ b/app/src/main/java/code/name/monkey/retromusic/fragments/genres/GenreDetailsFragment.kt @@ -33,6 +33,7 @@ import code.name.monkey.retromusic.fragments.base.AbsMainActivityFragment import code.name.monkey.retromusic.helper.menu.GenreMenuHelper import code.name.monkey.retromusic.model.Genre import code.name.monkey.retromusic.model.Song +import com.google.android.material.shape.MaterialShapeDrawable import com.google.android.material.transition.MaterialSharedAxis import org.koin.androidx.viewmodel.ext.android.viewModel import org.koin.core.parameter.parametersOf @@ -67,6 +68,8 @@ class GenreDetailsFragment : AbsMainActivityFragment(R.layout.fragment_playlist_ view.doOnPreDraw { startPostponedEnterTransition() } + binding.appBarLayout.statusBarForeground = + MaterialShapeDrawable.createWithElevationOverlay(requireContext()) } private fun setupRecyclerView() { diff --git a/app/src/main/java/code/name/monkey/retromusic/fragments/genres/GenresFragment.kt b/app/src/main/java/code/name/monkey/retromusic/fragments/genres/GenresFragment.kt index de1cb4d6..1d675fb8 100644 --- a/app/src/main/java/code/name/monkey/retromusic/fragments/genres/GenresFragment.kt +++ b/app/src/main/java/code/name/monkey/retromusic/fragments/genres/GenresFragment.kt @@ -19,6 +19,7 @@ import android.view.Menu import android.view.MenuInflater import android.view.MenuItem import android.view.View +import androidx.activity.addCallback import androidx.core.os.bundleOf import androidx.navigation.fragment.findNavController import androidx.recyclerview.widget.GridLayoutManager @@ -26,10 +27,12 @@ import androidx.recyclerview.widget.LinearLayoutManager import code.name.monkey.retromusic.EXTRA_GENRE import code.name.monkey.retromusic.R import code.name.monkey.retromusic.adapter.GenreAdapter +import code.name.monkey.retromusic.extensions.navigate import code.name.monkey.retromusic.fragments.ReloadType import code.name.monkey.retromusic.fragments.base.AbsRecyclerViewFragment import code.name.monkey.retromusic.interfaces.IGenreClickListener import code.name.monkey.retromusic.model.Genre +import code.name.monkey.retromusic.util.PreferenceUtil import code.name.monkey.retromusic.util.RetroUtil import com.google.android.gms.cast.framework.CastButtonFactory import com.google.android.material.transition.MaterialSharedAxis @@ -45,6 +48,10 @@ GenresFragment : AbsRecyclerViewFragment(), else adapter?.swapDataSet(listOf()) }) + requireActivity().onBackPressedDispatcher.addCallback(viewLifecycleOwner) { + remove() + mainActivity.finish() + } } override fun createLayoutManager(): LinearLayoutManager { @@ -82,6 +89,9 @@ GenresFragment : AbsRecyclerViewFragment(), override val emptyMessage: Int get() = R.string.no_genres + override val isShuffleVisible: Boolean + get() = false + companion object { @JvmField val TAG: String = GenresFragment::class.java.simpleName diff --git a/app/src/main/java/code/name/monkey/retromusic/fragments/home/HomeFragment.kt b/app/src/main/java/code/name/monkey/retromusic/fragments/home/HomeFragment.kt index b35c90a4..6f521637 100644 --- a/app/src/main/java/code/name/monkey/retromusic/fragments/home/HomeFragment.kt +++ b/app/src/main/java/code/name/monkey/retromusic/fragments/home/HomeFragment.kt @@ -14,19 +14,19 @@ */ package code.name.monkey.retromusic.fragments.home -import android.app.ActivityOptions import android.os.Bundle import android.view.Menu import android.view.MenuInflater import android.view.MenuItem import android.view.MenuItem.SHOW_AS_ACTION_IF_ROOM import android.view.View +import androidx.activity.addCallback import androidx.core.os.bundleOf import androidx.core.text.HtmlCompat import androidx.core.view.doOnPreDraw +import androidx.navigation.fragment.FragmentNavigatorExtras import androidx.navigation.fragment.findNavController import androidx.recyclerview.widget.LinearLayoutManager -import code.name.monkey.appthemehelper.ThemeStore import code.name.monkey.appthemehelper.common.ATHToolbarActivity import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper import code.name.monkey.retromusic.* @@ -35,12 +35,14 @@ import code.name.monkey.retromusic.databinding.FragmentBannerHomeBinding import code.name.monkey.retromusic.databinding.FragmentHomeBinding import code.name.monkey.retromusic.dialogs.CreatePlaylistDialog import code.name.monkey.retromusic.dialogs.ImportPlaylistDialog +import code.name.monkey.retromusic.extensions.accentColor +import code.name.monkey.retromusic.extensions.drawNextToNavbar import code.name.monkey.retromusic.fragments.base.AbsMainActivityFragment import code.name.monkey.retromusic.glide.GlideApp import code.name.monkey.retromusic.glide.RetroGlideExtension -import code.name.monkey.retromusic.util.NavigationUtil import code.name.monkey.retromusic.util.PreferenceUtil import com.google.android.gms.cast.framework.CastButtonFactory +import com.google.android.material.shape.MaterialShapeDrawable import com.google.android.material.transition.MaterialFadeThrough import com.google.android.material.transition.MaterialSharedAxis @@ -53,18 +55,45 @@ class HomeFragment : override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) _binding = getBinding(PreferenceUtil.isHomeBanner, view) - enterTransition = MaterialFadeThrough() - exitTransition = MaterialFadeThrough() mainActivity.setSupportActionBar(binding.toolbar) mainActivity.supportActionBar?.title = null - setStatusBarColorAuto(view) + setupListeners() + binding.titleWelcome.text = String.format("%s", PreferenceUtil.userName) + + enterTransition = MaterialFadeThrough().apply { + addTarget(binding.contentContainer) + } + + val homeAdapter = HomeAdapter(mainActivity) + binding.recyclerView.apply { + layoutManager = LinearLayoutManager(mainActivity) + adapter = homeAdapter + } + libraryViewModel.getHome().observe(viewLifecycleOwner, { + homeAdapter.swapData(it) + }) + + loadProfile() + setupTitle() + postponeEnterTransition() + view.doOnPreDraw { startPostponedEnterTransition() } + binding.appBarLayout.statusBarForeground = + MaterialShapeDrawable.createWithElevationOverlay(requireContext()) + binding.toolbar.drawNextToNavbar() + requireActivity().onBackPressedDispatcher.addCallback(viewLifecycleOwner) { + remove() + mainActivity.finish() + } + } + + private fun setupListeners() { binding.bannerImage?.setOnClickListener { - val options = ActivityOptions.makeSceneTransitionAnimation( - mainActivity, - binding.userImage, - getString(R.string.transition_user_image) + findNavController().navigate( + R.id.user_info_fragment, null, null, FragmentNavigatorExtras( + binding.userImage to "user_image" + ) ) - NavigationUtil.goToUserInfo(requireActivity(), options) + reenterTransition = null } binding.lastAdded.setOnClickListener { @@ -96,28 +125,12 @@ class HomeFragment : } binding.userImage.setOnClickListener { - val options = ActivityOptions.makeSceneTransitionAnimation( - mainActivity, - binding.userImage, - getString(R.string.transition_user_image) + findNavController().navigate( + R.id.user_info_fragment, null, null, FragmentNavigatorExtras( + binding.userImage to "user_image" + ) ) - NavigationUtil.goToUserInfo(requireActivity(), options) } - binding.titleWelcome.text = String.format("%s", PreferenceUtil.userName) - - val homeAdapter = HomeAdapter(mainActivity) - binding.recyclerView.apply { - layoutManager = LinearLayoutManager(mainActivity) - adapter = homeAdapter - } - libraryViewModel.getHome().observe(viewLifecycleOwner, { - homeAdapter.swapData(it) - }) - - loadProfile() - setupTitle() - postponeEnterTransition() - view.doOnPreDraw { startPostponedEnterTransition() } } private fun getBinding(homeBanner: Boolean, view: View): HomeBindingAdapter { @@ -137,8 +150,7 @@ class HomeFragment : MaterialSharedAxis(MaterialSharedAxis.Z, false) findNavController().navigate(R.id.searchFragment, null, navOptions) } - val color = ThemeStore.accentColor(requireContext()) - val hexColor = String.format("#%06X", 0xFFFFFF and color) + val hexColor = String.format("#%06X", 0xFFFFFF and accentColor()) val appName = HtmlCompat.fromHtml( "Retro Music", HtmlCompat.FROM_HTML_MODE_COMPACT diff --git a/app/src/main/java/code/name/monkey/retromusic/fragments/library/LibraryFragment.kt b/app/src/main/java/code/name/monkey/retromusic/fragments/library/LibraryFragment.kt index a9d4af02..0a468f35 100644 --- a/app/src/main/java/code/name/monkey/retromusic/fragments/library/LibraryFragment.kt +++ b/app/src/main/java/code/name/monkey/retromusic/fragments/library/LibraryFragment.kt @@ -49,7 +49,7 @@ class LibraryFragment : AbsMainActivityFragment(R.layout.fragment_library) { override fun onActivityCreated(savedInstanceState: Bundle?) { super.onActivityCreated(savedInstanceState) setHasOptionsMenu(true) - mainActivity.setBottomBarVisibility(true) + mainActivity.setBottomNavVisibility(true) mainActivity.setSupportActionBar(binding.toolbar) mainActivity.supportActionBar?.title = null binding.toolbar.setNavigationOnClickListener { @@ -84,7 +84,7 @@ class LibraryFragment : AbsMainActivityFragment(R.layout.fragment_library) { navGraph.setStartDestination(categoryInfo.category.id) } navController.graph = navGraph - NavigationUI.setupWithNavController(mainActivity.getBottomNavigationView(), navController) + NavigationUI.setupWithNavController(mainActivity.bottomNavigationView, navController) navController.addOnDestinationChangedListener { _, _, _ -> binding.appBarLayout.setExpanded(true, true) } diff --git a/app/src/main/java/code/name/monkey/retromusic/fragments/DetailListFragment.kt b/app/src/main/java/code/name/monkey/retromusic/fragments/other/DetailListFragment.kt similarity index 97% rename from app/src/main/java/code/name/monkey/retromusic/fragments/DetailListFragment.kt rename to app/src/main/java/code/name/monkey/retromusic/fragments/other/DetailListFragment.kt index b001c1e8..49f22678 100644 --- a/app/src/main/java/code/name/monkey/retromusic/fragments/DetailListFragment.kt +++ b/app/src/main/java/code/name/monkey/retromusic/fragments/other/DetailListFragment.kt @@ -12,7 +12,7 @@ * See the GNU General Public License for more details. * */ -package code.name.monkey.retromusic.fragments +package code.name.monkey.retromusic.fragments.other import android.os.Bundle import android.view.View @@ -38,6 +38,7 @@ import code.name.monkey.retromusic.interfaces.IArtistClickListener import code.name.monkey.retromusic.model.Album import code.name.monkey.retromusic.model.Artist import code.name.monkey.retromusic.util.RetroUtil +import com.google.android.material.shape.MaterialShapeDrawable import com.google.android.material.transition.MaterialSharedAxis class DetailListFragment : AbsMainActivityFragment(R.layout.fragment_playlist_detail), @@ -63,6 +64,8 @@ class DetailListFragment : AbsMainActivityFragment(R.layout.fragment_playlist_de returnTransition = MaterialSharedAxis(MaterialSharedAxis.Y, false) } } + binding.appBarLayout.statusBarForeground = + MaterialShapeDrawable.createWithElevationOverlay(requireContext()) postponeEnterTransition() view.doOnPreDraw { startPostponedEnterTransition() } } diff --git a/app/src/main/java/code/name/monkey/retromusic/activities/LyricsActivity.kt b/app/src/main/java/code/name/monkey/retromusic/fragments/other/LyricsFragment.kt similarity index 82% rename from app/src/main/java/code/name/monkey/retromusic/activities/LyricsActivity.kt rename to app/src/main/java/code/name/monkey/retromusic/fragments/other/LyricsFragment.kt index 5b72c24a..a197ddb6 100644 --- a/app/src/main/java/code/name/monkey/retromusic/activities/LyricsActivity.kt +++ b/app/src/main/java/code/name/monkey/retromusic/fragments/other/LyricsFragment.kt @@ -12,7 +12,7 @@ * See the GNU General Public License for more details. * */ -package code.name.monkey.retromusic.activities +package code.name.monkey.retromusic.fragments.other import android.os.Bundle import android.text.InputType @@ -20,13 +20,14 @@ import android.view.* import androidx.core.view.ViewCompat import androidx.fragment.app.Fragment import androidx.fragment.app.FragmentActivity +import androidx.navigation.fragment.findNavController +import androidx.transition.Fade import androidx.viewpager2.adapter.FragmentStateAdapter -import code.name.monkey.appthemehelper.ThemeStore import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper import code.name.monkey.retromusic.R -import code.name.monkey.retromusic.activities.base.AbsMusicServiceActivity +import code.name.monkey.retromusic.activities.MainActivity import code.name.monkey.retromusic.activities.tageditor.WriteTagsAsyncTask -import code.name.monkey.retromusic.databinding.ActivityLyricsBinding +import code.name.monkey.retromusic.databinding.FragmentLyricsBinding import code.name.monkey.retromusic.databinding.FragmentNormalLyricsBinding import code.name.monkey.retromusic.databinding.FragmentSyncedLyricsBinding import code.name.monkey.retromusic.extensions.accentColor @@ -53,12 +54,16 @@ import org.jaudiotagger.tag.FieldKey import java.io.File import java.util.* -class LyricsActivity : AbsMusicServiceActivity() { +class LyricsFragment : AbsMusicServiceFragment(R.layout.fragment_lyrics) { - private lateinit var binding: ActivityLyricsBinding + private var _binding: FragmentLyricsBinding? = null + private val binding get() = _binding!! private lateinit var song: Song - private val lyricsSectionsAdapter = LyricsSectionsAdapter(this) + val mainActivity: MainActivity + get() = activity as MainActivity + + private lateinit var lyricsSectionsAdapter: LyricsSectionsAdapter private val googleSearchLrcUrl: String get() { @@ -80,34 +85,30 @@ class LyricsActivity : AbsMusicServiceActivity() { private fun buildContainerTransform(): MaterialContainerTransform { val transform = MaterialContainerTransform() transform.setAllContainerColors( - MaterialColors.getColor(findViewById(R.id.container), R.attr.colorSurface) + MaterialColors.getColor(requireView().findViewById(R.id.container), R.attr.colorSurface) ) transform.addTarget(R.id.container) transform.duration = 300 return transform } - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - binding = ActivityLyricsBinding.inflate(layoutInflater) - setContentView(binding.root) + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + enterTransition = Fade() + exitTransition = Fade() + lyricsSectionsAdapter = LyricsSectionsAdapter(requireActivity()) + _binding = FragmentLyricsBinding.bind(view) ViewCompat.setTransitionName(binding.container, "lyrics") - setStatusbarColorAuto() - setTaskDescriptionColorAuto() - setNavigationbarColorAuto() setupWakelock() - binding.toolbar.setBackgroundColor(surfaceColor()) binding.tabLyrics.setBackgroundColor(surfaceColor()) binding.container.setBackgroundColor(surfaceColor()) - ToolbarContentTintHelper.colorBackButton(binding.toolbar) - setSupportActionBar(binding.toolbar) setupViews() - + setupToolbar() + updateTitleSong() } - private fun setupViews() { binding.lyricsPager.adapter = lyricsSectionsAdapter TabLayoutMediator(binding.tabLyrics, binding.lyricsPager) { tab, position -> @@ -119,10 +120,29 @@ class LyricsActivity : AbsMusicServiceActivity() { }.attach() // lyricsPager.isUserInputEnabled = false - binding.tabLyrics.setSelectedTabIndicatorColor(ThemeStore.accentColor(this)) + binding.tabLyrics.setSelectedTabIndicatorColor(accentColor()) binding.tabLyrics.setTabTextColors(textColorSecondary(), accentColor()) + binding.editButton.accentColor() + binding.editButton.setOnClickListener { + when (binding.lyricsPager.currentItem) { + 0 -> { + editSyncedLyrics() + } + 1 -> { + editNormalLyrics() + } + } + } } + private fun setupToolbar() { + mainActivity.setSupportActionBar(binding.toolbar) + binding.toolbar.setBackgroundColor(surfaceColor()) + ToolbarContentTintHelper.colorBackButton(binding.toolbar) + binding.toolbar.setNavigationOnClickListener { + findNavController().navigateUp() + } + } override fun onPlayingMetaChanged() { super.onPlayingMetaChanged() @@ -141,36 +161,27 @@ class LyricsActivity : AbsMusicServiceActivity() { } private fun setupWakelock() { - window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON) + requireActivity().window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON) } - override fun onCreateOptionsMenu(menu: Menu?): Boolean { - menuInflater.inflate(R.menu.menu_search, menu) - return super.onCreateOptionsMenu(menu) + override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) { + inflater.inflate(R.menu.menu_search, menu) + return super.onCreateOptionsMenu(menu, inflater) } override fun onOptionsItemSelected(item: MenuItem): Boolean { if (item.itemId == android.R.id.home) { - finish() + findNavController().navigateUp() return true } if (item.itemId == R.id.action_search) { RetroUtil.openUrl( - this, when (binding.lyricsPager.currentItem) { + requireActivity(), when (binding.lyricsPager.currentItem) { 0 -> syairSearchLrcUrl 1 -> googleSearchLrcUrl else -> googleSearchLrcUrl } ) - } else if (item.itemId == R.id.action_edit) { - when (binding.lyricsPager.currentItem) { - 0 -> { - editSyncedLyrics() - } - 1 -> { - editNormalLyrics() - } - } } return super.onOptionsItemSelected(item) } @@ -185,7 +196,7 @@ class LyricsActivity : AbsMusicServiceActivity() { e.printStackTrace() } - MaterialDialog(this, BottomSheet(LayoutMode.WRAP_CONTENT)).show { + MaterialDialog(requireContext(), BottomSheet(LayoutMode.WRAP_CONTENT)).show { title(res = R.string.edit_normal_lyrics) input( hintRes = R.string.paste_lyrics_here, @@ -194,7 +205,7 @@ class LyricsActivity : AbsMusicServiceActivity() { ) { _, input -> val fieldKeyValueMap = EnumMap(FieldKey::class.java) fieldKeyValueMap[FieldKey.LYRICS] = input.toString() - WriteTagsAsyncTask(this@LyricsActivity).execute( + WriteTagsAsyncTask(requireActivity()).execute( LoadingInfo( listOf(song.data), fieldKeyValueMap, null ) @@ -217,7 +228,7 @@ class LyricsActivity : AbsMusicServiceActivity() { } val content: String = LyricUtil.getStringFromLrc(lrcFile) - MaterialDialog(this, BottomSheet(LayoutMode.WRAP_CONTENT)).show { + MaterialDialog(requireContext(), BottomSheet(LayoutMode.WRAP_CONTENT)).show { title(res = R.string.edit_synced_lyrics) input( hintRes = R.string.paste_timeframe_lyrics_here, @@ -320,10 +331,10 @@ class LyricsActivity : AbsMusicServiceActivity() { private fun setupLyricsView() { binding.lyricsView.apply { - setCurrentColor(ThemeStore.accentColor(context)) - setTimeTextColor(ThemeStore.accentColor(context)) - setTimelineColor(ThemeStore.accentColor(context)) - setTimelineTextColor(ThemeStore.accentColor(context)) + setCurrentColor(accentColor()) + setTimeTextColor(accentColor()) + setTimelineColor(accentColor()) + setTimelineTextColor(accentColor()) setDraggable(true, LrcView.OnPlayClickListener { MusicPlayerRemote.seekTo(it.toInt()) return@OnPlayClickListener true @@ -356,5 +367,8 @@ class LyricsActivity : AbsMusicServiceActivity() { } } - + override fun onDestroyView() { + super.onDestroyView() + (requireActivity() as MainActivity).expandPanel() + } } diff --git a/app/src/main/java/code/name/monkey/retromusic/fragments/MiniPlayerFragment.kt b/app/src/main/java/code/name/monkey/retromusic/fragments/other/MiniPlayerFragment.kt similarity index 97% rename from app/src/main/java/code/name/monkey/retromusic/fragments/MiniPlayerFragment.kt rename to app/src/main/java/code/name/monkey/retromusic/fragments/other/MiniPlayerFragment.kt index c37bc346..8b5295e5 100644 --- a/app/src/main/java/code/name/monkey/retromusic/fragments/MiniPlayerFragment.kt +++ b/app/src/main/java/code/name/monkey/retromusic/fragments/other/MiniPlayerFragment.kt @@ -12,7 +12,7 @@ * See the GNU General Public License for more details. * */ -package code.name.monkey.retromusic.fragments +package code.name.monkey.retromusic.fragments.other import android.animation.ObjectAnimator import android.annotation.SuppressLint @@ -32,8 +32,6 @@ import code.name.monkey.retromusic.extensions.show import code.name.monkey.retromusic.extensions.textColorPrimary import code.name.monkey.retromusic.extensions.textColorSecondary import code.name.monkey.retromusic.fragments.base.AbsMusicServiceFragment -import code.name.monkey.retromusic.glide.GlideApp -import code.name.monkey.retromusic.glide.RetroGlideExtension import code.name.monkey.retromusic.helper.MusicPlayerRemote import code.name.monkey.retromusic.helper.MusicProgressViewUpdateHelper import code.name.monkey.retromusic.helper.PlayPauseButtonOnClickHandler diff --git a/app/src/main/java/code/name/monkey/retromusic/fragments/other/PlayingQueueRVFragment.kt b/app/src/main/java/code/name/monkey/retromusic/fragments/other/PlayingQueueRVFragment.kt new file mode 100644 index 00000000..dd17bdde --- /dev/null +++ b/app/src/main/java/code/name/monkey/retromusic/fragments/other/PlayingQueueRVFragment.kt @@ -0,0 +1,159 @@ +/* + * Copyright (c) 2020 Hemanth Savarla. + * + * 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.fragments.other + +import android.os.Bundle +import android.view.View +import androidx.appcompat.app.AppCompatActivity +import androidx.navigation.findNavController +import androidx.recyclerview.widget.LinearLayoutManager +import androidx.recyclerview.widget.RecyclerView +import code.name.monkey.retromusic.R +import code.name.monkey.retromusic.adapter.song.PlayingQueueAdapter +import code.name.monkey.retromusic.fragments.base.AbsRecyclerViewFragment +import code.name.monkey.retromusic.helper.MusicPlayerRemote +import com.h6ah4i.android.widget.advrecyclerview.animator.DraggableItemAnimator +import com.h6ah4i.android.widget.advrecyclerview.draggable.RecyclerViewDragDropManager +import com.h6ah4i.android.widget.advrecyclerview.swipeable.RecyclerViewSwipeManager +import com.h6ah4i.android.widget.advrecyclerview.touchguard.RecyclerViewTouchActionGuardManager +import com.h6ah4i.android.widget.advrecyclerview.utils.WrapperAdapterUtils + +/** + * Created by hemanths on 2019-12-08. + */ +class PlayingQueueRVFragment : AbsRecyclerViewFragment() { + + private lateinit var wrappedAdapter: RecyclerView.Adapter<*> + private var recyclerViewDragDropManager: RecyclerViewDragDropManager? = null + private var recyclerViewSwipeManager: RecyclerViewSwipeManager? = null + private var recyclerViewTouchActionGuardManager: RecyclerViewTouchActionGuardManager? = null + override val titleRes: Int + get() = R.string.now_playing_queue + override val isShuffleVisible: Boolean + get() = false + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + setupRecyclerView() + setupToolbar() + } + + private fun setupToolbar() { + toolbar().apply { + setNavigationOnClickListener { + findNavController().navigateUp() + } + setNavigationIcon(R.drawable.ic_keyboard_backspace_black) + } + } + + private fun setupRecyclerView() { + recyclerViewTouchActionGuardManager = RecyclerViewTouchActionGuardManager() + recyclerViewDragDropManager = RecyclerViewDragDropManager() + recyclerViewSwipeManager = RecyclerViewSwipeManager() + + val animator = DraggableItemAnimator() + animator.supportsChangeAnimations = false + wrappedAdapter = + recyclerViewDragDropManager?.createWrappedAdapter(adapter!!) as RecyclerView.Adapter<*> + wrappedAdapter = + recyclerViewSwipeManager?.createWrappedAdapter(wrappedAdapter) as RecyclerView.Adapter<*> + recyclerView.layoutManager = layoutManager + recyclerView.adapter = wrappedAdapter + recyclerView.itemAnimator = animator + recyclerViewTouchActionGuardManager?.attachRecyclerView(recyclerView) + recyclerViewDragDropManager?.attachRecyclerView(recyclerView) + recyclerViewSwipeManager?.attachRecyclerView(recyclerView) + + layoutManager?.scrollToPositionWithOffset(MusicPlayerRemote.position + 1, 0) + } + + override fun createLayoutManager(): LinearLayoutManager { + return LinearLayoutManager(requireContext()) + } + + override fun createAdapter(): PlayingQueueAdapter { + return PlayingQueueAdapter( + requireActivity() as AppCompatActivity, + MusicPlayerRemote.playingQueue.toMutableList(), + MusicPlayerRemote.position, + R.layout.item_queue + ) + } + + override fun onServiceConnected() { + super.onServiceConnected() + updateQueue() + } + + override fun onQueueChanged() { + super.onQueueChanged() + updateQueue() + mainActivity.hideBottomSheet(true) + } + + override fun onPlayingMetaChanged() { + updateQueuePosition() + mainActivity.hideBottomSheet(true) + } + + private fun updateQueuePosition() { + adapter?.setCurrent(MusicPlayerRemote.position) + resetToCurrentPosition() + } + + private fun updateQueue() { + adapter?.swapDataSet(MusicPlayerRemote.playingQueue, MusicPlayerRemote.position) + resetToCurrentPosition() + } + + private fun resetToCurrentPosition() { + recyclerView.stopScroll() + layoutManager?.scrollToPositionWithOffset(MusicPlayerRemote.position + 1, 0) + } + + override fun onPause() { + recyclerViewDragDropManager?.cancelDrag() + super.onPause() + } + + override val emptyMessage: Int + get() = R.string.no_playing_queue + + override fun onDestroyView() { + super.onDestroyView() + if (recyclerViewDragDropManager != null) { + recyclerViewDragDropManager?.release() + recyclerViewDragDropManager = null + } + + if (recyclerViewSwipeManager != null) { + recyclerViewSwipeManager?.release() + recyclerViewSwipeManager = null + } + + WrapperAdapterUtils.releaseAll(wrappedAdapter) + } + + companion object { + @JvmField + val TAG: String = PlayingQueueRVFragment::class.java.simpleName + + @JvmStatic + fun newInstance(): PlayingQueueRVFragment { + return PlayingQueueRVFragment() + } + } +} diff --git a/app/src/main/java/code/name/monkey/retromusic/activities/UserInfoActivity.kt b/app/src/main/java/code/name/monkey/retromusic/fragments/other/UserInfoFragment.kt similarity index 73% rename from app/src/main/java/code/name/monkey/retromusic/activities/UserInfoActivity.kt rename to app/src/main/java/code/name/monkey/retromusic/fragments/other/UserInfoFragment.kt index 1394cc93..e3ebe33f 100644 --- a/app/src/main/java/code/name/monkey/retromusic/activities/UserInfoActivity.kt +++ b/app/src/main/java/code/name/monkey/retromusic/fragments/other/UserInfoFragment.kt @@ -12,25 +12,34 @@ * See the GNU General Public License for more details. * */ -package code.name.monkey.retromusic.activities +package code.name.monkey.retromusic.fragments.other import android.app.Activity import android.content.Intent import android.content.res.ColorStateList import android.graphics.Bitmap +import android.graphics.Color import android.net.Uri import android.os.Bundle import android.text.TextUtils +import android.view.LayoutInflater import android.view.MenuItem +import android.view.View +import android.view.ViewGroup import android.widget.Toast +import androidx.core.view.doOnPreDraw +import androidx.core.view.updateLayoutParams +import androidx.fragment.app.Fragment +import androidx.navigation.fragment.findNavController import code.name.monkey.appthemehelper.util.ColorUtil import code.name.monkey.appthemehelper.util.MaterialValueHelper import code.name.monkey.retromusic.Constants.USER_BANNER import code.name.monkey.retromusic.Constants.USER_PROFILE -import code.name.monkey.retromusic.activities.base.AbsBaseActivity -import code.name.monkey.retromusic.databinding.ActivityUserInfoBinding +import code.name.monkey.retromusic.R +import code.name.monkey.retromusic.databinding.FragmentUserInfoBinding import code.name.monkey.retromusic.extensions.accentColor import code.name.monkey.retromusic.extensions.applyToolbar +import code.name.monkey.retromusic.fragments.LibraryViewModel import code.name.monkey.retromusic.glide.GlideApp import code.name.monkey.retromusic.glide.RetroGlideExtension import code.name.monkey.retromusic.util.ImageUtil @@ -43,27 +52,39 @@ import com.bumptech.glide.request.RequestListener import com.bumptech.glide.request.target.Target import com.github.dhaval2404.imagepicker.ImagePicker import com.github.dhaval2404.imagepicker.constant.ImageProvider +import com.google.android.material.transition.MaterialContainerTransform import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import kotlinx.coroutines.withContext +import org.koin.androidx.viewmodel.ext.android.sharedViewModel import java.io.BufferedOutputStream import java.io.File import java.io.FileOutputStream import java.io.IOException -class UserInfoActivity : AbsBaseActivity() { +class UserInfoFragment : Fragment() { - private lateinit var binding: ActivityUserInfoBinding + private var _binding: FragmentUserInfoBinding? = null + private val binding get() = _binding!! + private val libraryViewModel: LibraryViewModel by sharedViewModel() - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - binding = ActivityUserInfoBinding.inflate(layoutInflater) - setContentView(binding.root) - setStatusbarColorAuto() - setNavigationbarColorAuto() - setTaskDescriptionColorAuto() - setLightNavigationBar(true) + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ): View { + sharedElementEnterTransition = MaterialContainerTransform().apply { + drawingViewId = R.id.fragment_container + duration = 300L + scrimColor = Color.TRANSPARENT + } + _binding = FragmentUserInfoBinding.inflate(layoutInflater) + return binding.root + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) applyToolbar(binding.toolbar) binding.nameContainer.accentColor() @@ -80,20 +101,35 @@ class UserInfoActivity : AbsBaseActivity() { binding.next.setOnClickListener { val nameString = binding.name.text.toString().trim { it <= ' ' } if (TextUtils.isEmpty(nameString)) { - Toast.makeText(this, "Umm you're name can't be empty!", Toast.LENGTH_SHORT).show() + Toast.makeText( + requireContext(), + "Umm you're name can't be empty!", + Toast.LENGTH_SHORT + ).show() return@setOnClickListener } PreferenceUtil.userName = nameString - setResult(Activity.RESULT_OK) - finish() + findNavController().navigateUp() } val textColor = - MaterialValueHelper.getPrimaryTextColor(this, ColorUtil.isColorLight(accentColor())) + MaterialValueHelper.getPrimaryTextColor( + requireContext(), + ColorUtil.isColorLight(accentColor()) + ) binding.next.backgroundTintList = ColorStateList.valueOf(accentColor()) binding.next.iconTint = ColorStateList.valueOf(textColor) binding.next.setTextColor(textColor) loadProfile() + postponeEnterTransition() + view.doOnPreDraw { + startPostponedEnterTransition() + } + libraryViewModel.getFabMargin().observe(viewLifecycleOwner, { + binding.next.updateLayoutParams { + bottomMargin = it + } + }) } private fun loadProfile() { @@ -112,7 +148,7 @@ class UserInfoActivity : AbsBaseActivity() { override fun onOptionsItemSelected(item: MenuItem): Boolean { if (item.itemId == android.R.id.home) { - onBackPressed() + findNavController().navigateUp() } return super.onOptionsItemSelected(item) } @@ -133,7 +169,7 @@ class UserInfoActivity : AbsBaseActivity() { .start(PICK_IMAGE_REQUEST) } - public override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { + override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { super.onActivityResult(requestCode, resultCode, data) if (resultCode == Activity.RESULT_OK && requestCode == PICK_IMAGE_REQUEST) { val fileUri = data?.data @@ -142,9 +178,9 @@ class UserInfoActivity : AbsBaseActivity() { val fileUri = data?.data fileUri?.let { setAndSaveBannerImage(it) } } else if (resultCode == ImagePicker.RESULT_ERROR) { - Toast.makeText(this, ImagePicker.getError(data), Toast.LENGTH_SHORT).show() + Toast.makeText(requireContext(), ImagePicker.getError(data), Toast.LENGTH_SHORT).show() } else { - Toast.makeText(this, "Task Cancelled", Toast.LENGTH_SHORT).show() + Toast.makeText(requireContext(), "Task Cancelled", Toast.LENGTH_SHORT).show() } } @@ -179,7 +215,7 @@ class UserInfoActivity : AbsBaseActivity() { private fun saveImage(bitmap: Bitmap, fileName: String) { CoroutineScope(Dispatchers.IO).launch { - val appDir = applicationContext.filesDir + val appDir = requireContext().filesDir val file = File(appDir, fileName) var successful = false try { @@ -192,7 +228,7 @@ class UserInfoActivity : AbsBaseActivity() { } if (successful) { withContext(Dispatchers.Main) { - Toast.makeText(this@UserInfoActivity, "Updated", Toast.LENGTH_SHORT).show() + Toast.makeText(requireContext(), "Updated", Toast.LENGTH_SHORT).show() } } } @@ -227,6 +263,11 @@ class UserInfoActivity : AbsBaseActivity() { .into(binding.userImage) } + override fun onDestroyView() { + super.onDestroyView() + _binding = null + } + companion object { private const val PICK_IMAGE_REQUEST = 9002 private const val PICK_BANNER_REQUEST = 9004 diff --git a/app/src/main/java/code/name/monkey/retromusic/fragments/VolumeFragment.kt b/app/src/main/java/code/name/monkey/retromusic/fragments/other/VolumeFragment.kt old mode 100755 new mode 100644 similarity index 99% rename from app/src/main/java/code/name/monkey/retromusic/fragments/VolumeFragment.kt rename to app/src/main/java/code/name/monkey/retromusic/fragments/other/VolumeFragment.kt index 27c8b92a..89d3f530 --- a/app/src/main/java/code/name/monkey/retromusic/fragments/VolumeFragment.kt +++ b/app/src/main/java/code/name/monkey/retromusic/fragments/other/VolumeFragment.kt @@ -12,7 +12,7 @@ * See the GNU General Public License for more details. * */ -package code.name.monkey.retromusic.fragments +package code.name.monkey.retromusic.fragments.other import android.content.Context import android.graphics.Color diff --git a/app/src/main/java/code/name/monkey/retromusic/fragments/player/PlayerAlbumCoverFragment.kt b/app/src/main/java/code/name/monkey/retromusic/fragments/player/PlayerAlbumCoverFragment.kt index 9249fcb7..876f1688 100644 --- a/app/src/main/java/code/name/monkey/retromusic/fragments/player/PlayerAlbumCoverFragment.kt +++ b/app/src/main/java/code/name/monkey/retromusic/fragments/player/PlayerAlbumCoverFragment.kt @@ -281,7 +281,7 @@ class PlayerAlbumCoverFragment : AbsMusicServiceFragment(R.layout.fragment_playe private fun updatePlayingQueue() { binding.viewPager.apply { adapter = AlbumCoverPagerAdapter(childFragmentManager, MusicPlayerRemote.playingQueue) - adapter!!.notifyDataSetChanged() + adapter?.notifyDataSetChanged() currentItem = MusicPlayerRemote.position onPageSelected(MusicPlayerRemote.position) } diff --git a/app/src/main/java/code/name/monkey/retromusic/fragments/player/adaptive/AdaptiveFragment.kt b/app/src/main/java/code/name/monkey/retromusic/fragments/player/adaptive/AdaptiveFragment.kt index 34f8baf5..225d7b20 100644 --- a/app/src/main/java/code/name/monkey/retromusic/fragments/player/adaptive/AdaptiveFragment.kt +++ b/app/src/main/java/code/name/monkey/retromusic/fragments/player/adaptive/AdaptiveFragment.kt @@ -21,6 +21,7 @@ import code.name.monkey.appthemehelper.util.ATHUtil import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper import code.name.monkey.retromusic.R import code.name.monkey.retromusic.databinding.FragmentAdaptivePlayerBinding +import code.name.monkey.retromusic.extensions.drawAboveSystemBars import code.name.monkey.retromusic.extensions.surfaceColor import code.name.monkey.retromusic.extensions.textColorPrimary import code.name.monkey.retromusic.extensions.textColorSecondary @@ -46,6 +47,7 @@ class AdaptiveFragment : AbsPlayerFragment(R.layout.fragment_adaptive_player) { _binding = FragmentAdaptivePlayerBinding.bind(view) setUpSubFragments() setUpPlayerToolbar() + binding.root.drawAboveSystemBars() } private fun setUpSubFragments() { diff --git a/app/src/main/java/code/name/monkey/retromusic/fragments/player/blur/BlurPlayerFragment.kt b/app/src/main/java/code/name/monkey/retromusic/fragments/player/blur/BlurPlayerFragment.kt index a4f3df43..aec5f2e5 100644 --- a/app/src/main/java/code/name/monkey/retromusic/fragments/player/blur/BlurPlayerFragment.kt +++ b/app/src/main/java/code/name/monkey/retromusic/fragments/player/blur/BlurPlayerFragment.kt @@ -24,6 +24,7 @@ import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper import code.name.monkey.retromusic.NEW_BLUR_AMOUNT import code.name.monkey.retromusic.R import code.name.monkey.retromusic.databinding.FragmentBlurBinding +import code.name.monkey.retromusic.extensions.drawAboveSystemBars import code.name.monkey.retromusic.fragments.base.AbsPlayerFragment import code.name.monkey.retromusic.fragments.player.PlayerAlbumCoverFragment import code.name.monkey.retromusic.glide.BlurTransformation @@ -32,6 +33,7 @@ import code.name.monkey.retromusic.glide.RetroGlideExtension import code.name.monkey.retromusic.glide.RetroMusicColoredTarget import code.name.monkey.retromusic.helper.MusicPlayerRemote import code.name.monkey.retromusic.model.Song +import code.name.monkey.retromusic.util.PreferenceUtil.blurAmount import code.name.monkey.retromusic.util.color.MediaNotificationProcessor class BlurPlayerFragment : AbsPlayerFragment(R.layout.fragment_blur), @@ -54,6 +56,7 @@ class BlurPlayerFragment : AbsPlayerFragment(R.layout.fragment_blur), _binding = FragmentBlurBinding.bind(view) setUpSubFragments() setUpPlayerToolbar() + binding.playerToolbar.drawAboveSystemBars() } private fun setUpSubFragments() { @@ -108,8 +111,6 @@ class BlurPlayerFragment : AbsPlayerFragment(R.layout.fragment_blur), get() = lastColor private fun updateBlur() { - val blurAmount = PreferenceManager.getDefaultSharedPreferences(requireContext()) - .getInt(NEW_BLUR_AMOUNT, 25) binding.colorBackground.clearColorFilter() GlideApp.with(requireActivity()).asBitmapPalette() .songCoverOptions(MusicPlayerRemote.currentSong) diff --git a/app/src/main/java/code/name/monkey/retromusic/fragments/player/card/CardFragment.kt b/app/src/main/java/code/name/monkey/retromusic/fragments/player/card/CardFragment.kt index 698bf513..3afb9166 100644 --- a/app/src/main/java/code/name/monkey/retromusic/fragments/player/card/CardFragment.kt +++ b/app/src/main/java/code/name/monkey/retromusic/fragments/player/card/CardFragment.kt @@ -21,6 +21,7 @@ import androidx.appcompat.widget.Toolbar import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper import code.name.monkey.retromusic.R import code.name.monkey.retromusic.databinding.FragmentCardPlayerBinding +import code.name.monkey.retromusic.extensions.drawAboveSystemBars import code.name.monkey.retromusic.fragments.base.AbsPlayerFragment import code.name.monkey.retromusic.fragments.player.PlayerAlbumCoverFragment import code.name.monkey.retromusic.fragments.player.normal.PlayerFragment @@ -82,6 +83,7 @@ class CardFragment : AbsPlayerFragment(R.layout.fragment_card_player) { _binding = FragmentCardPlayerBinding.bind(view) setUpSubFragments() setUpPlayerToolbar() + (binding.playbackControlsFragment.parent as View).drawAboveSystemBars() } private fun setUpSubFragments() { diff --git a/app/src/main/java/code/name/monkey/retromusic/fragments/player/cardblur/CardBlurFragment.kt b/app/src/main/java/code/name/monkey/retromusic/fragments/player/cardblur/CardBlurFragment.kt index 5c5432a8..bec78c7e 100644 --- a/app/src/main/java/code/name/monkey/retromusic/fragments/player/cardblur/CardBlurFragment.kt +++ b/app/src/main/java/code/name/monkey/retromusic/fragments/player/cardblur/CardBlurFragment.kt @@ -24,6 +24,7 @@ import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper import code.name.monkey.retromusic.NEW_BLUR_AMOUNT import code.name.monkey.retromusic.R import code.name.monkey.retromusic.databinding.FragmentCardBlurPlayerBinding +import code.name.monkey.retromusic.extensions.drawAboveSystemBars import code.name.monkey.retromusic.fragments.base.AbsPlayerFragment import code.name.monkey.retromusic.fragments.player.PlayerAlbumCoverFragment import code.name.monkey.retromusic.fragments.player.normal.PlayerFragment @@ -33,6 +34,7 @@ import code.name.monkey.retromusic.glide.RetroGlideExtension import code.name.monkey.retromusic.glide.RetroMusicColoredTarget import code.name.monkey.retromusic.helper.MusicPlayerRemote import code.name.monkey.retromusic.model.Song +import code.name.monkey.retromusic.util.PreferenceUtil.blurAmount import code.name.monkey.retromusic.util.color.MediaNotificationProcessor class CardBlurFragment : AbsPlayerFragment(R.layout.fragment_card_blur_player), @@ -92,6 +94,7 @@ class CardBlurFragment : AbsPlayerFragment(R.layout.fragment_card_blur_player), _binding = FragmentCardBlurPlayerBinding.bind(view) setUpSubFragments() setUpPlayerToolbar() + binding.cardContainer?.drawAboveSystemBars() } private fun setUpSubFragments() { @@ -133,8 +136,6 @@ class CardBlurFragment : AbsPlayerFragment(R.layout.fragment_card_blur_player), } private fun updateBlur() { - val blurAmount = PreferenceManager.getDefaultSharedPreferences(requireContext()) - .getInt(NEW_BLUR_AMOUNT, 25) binding.colorBackground.clearColorFilter() GlideApp.with(requireActivity()).asBitmapPalette() .songCoverOptions(MusicPlayerRemote.currentSong) diff --git a/app/src/main/java/code/name/monkey/retromusic/fragments/player/circle/CirclePlayerFragment.kt b/app/src/main/java/code/name/monkey/retromusic/fragments/player/circle/CirclePlayerFragment.kt index 5701f528..4d9826a3 100644 --- a/app/src/main/java/code/name/monkey/retromusic/fragments/player/circle/CirclePlayerFragment.kt +++ b/app/src/main/java/code/name/monkey/retromusic/fragments/player/circle/CirclePlayerFragment.kt @@ -33,10 +33,7 @@ import code.name.monkey.appthemehelper.util.TintHelper import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper import code.name.monkey.retromusic.R import code.name.monkey.retromusic.databinding.FragmentCirclePlayerBinding -import code.name.monkey.retromusic.extensions.accentColor -import code.name.monkey.retromusic.extensions.applyColor -import code.name.monkey.retromusic.extensions.hide -import code.name.monkey.retromusic.extensions.show +import code.name.monkey.retromusic.extensions.* import code.name.monkey.retromusic.fragments.base.AbsPlayerControlsFragment import code.name.monkey.retromusic.fragments.base.AbsPlayerFragment import code.name.monkey.retromusic.fragments.base.goToAlbum @@ -96,6 +93,7 @@ class CirclePlayerFragment : AbsPlayerFragment(R.layout.fragment_circle_player), binding.text.setOnClickListener { goToArtist(requireActivity()) } + binding.songInfo.drawAboveSystemBars() } private fun setUpPlayerToolbar() { diff --git a/app/src/main/java/code/name/monkey/retromusic/fragments/player/classic/ClassicPlayerFragment.kt b/app/src/main/java/code/name/monkey/retromusic/fragments/player/classic/ClassicPlayerFragment.kt index 34f30bd2..78b84e5d 100644 --- a/app/src/main/java/code/name/monkey/retromusic/fragments/player/classic/ClassicPlayerFragment.kt +++ b/app/src/main/java/code/name/monkey/retromusic/fragments/player/classic/ClassicPlayerFragment.kt @@ -38,11 +38,11 @@ import code.name.monkey.retromusic.adapter.song.PlayingQueueAdapter import code.name.monkey.retromusic.databinding.FragmentClassicPlayerBinding import code.name.monkey.retromusic.extensions.hide import code.name.monkey.retromusic.extensions.show -import code.name.monkey.retromusic.fragments.VolumeFragment import code.name.monkey.retromusic.fragments.base.AbsPlayerControlsFragment import code.name.monkey.retromusic.fragments.base.AbsPlayerFragment import code.name.monkey.retromusic.fragments.base.goToAlbum import code.name.monkey.retromusic.fragments.base.goToArtist +import code.name.monkey.retromusic.fragments.other.VolumeFragment import code.name.monkey.retromusic.fragments.player.PlayerAlbumCoverFragment import code.name.monkey.retromusic.helper.MusicPlayerRemote import code.name.monkey.retromusic.helper.MusicProgressViewUpdateHelper diff --git a/app/src/main/java/code/name/monkey/retromusic/fragments/player/color/ColorFragment.kt b/app/src/main/java/code/name/monkey/retromusic/fragments/player/color/ColorFragment.kt index 461afbcf..a8d3b4b3 100644 --- a/app/src/main/java/code/name/monkey/retromusic/fragments/player/color/ColorFragment.kt +++ b/app/src/main/java/code/name/monkey/retromusic/fragments/player/color/ColorFragment.kt @@ -16,15 +16,14 @@ package code.name.monkey.retromusic.fragments.player.color import android.animation.ValueAnimator import android.os.Bundle -import android.os.Handler import android.view.View import androidx.appcompat.widget.Toolbar import androidx.core.animation.doOnEnd import code.name.monkey.appthemehelper.util.ATHUtil -import code.name.monkey.appthemehelper.util.ColorUtil import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper import code.name.monkey.retromusic.R import code.name.monkey.retromusic.databinding.FragmentColorPlayerBinding +import code.name.monkey.retromusic.extensions.drawAboveSystemBars import code.name.monkey.retromusic.fragments.base.AbsPlayerFragment import code.name.monkey.retromusic.fragments.player.PlayerAlbumCoverFragment import code.name.monkey.retromusic.helper.MusicPlayerRemote @@ -61,14 +60,11 @@ class ColorFragment : AbsPlayerFragment(R.layout.fragment_color_player) { _binding?.root?.setBackgroundColor(color.backgroundColor) } animator.start() - serviceActivity?.setLightNavigationBar(ColorUtil.isColorLight(color.backgroundColor)) - Handler().post { - ToolbarContentTintHelper.colorizeToolbar( - binding.playerToolbar, - color.secondaryTextColor, - requireActivity() - ) - } + ToolbarContentTintHelper.colorizeToolbar( + binding.playerToolbar, + color.secondaryTextColor, + requireActivity() + ) } override fun onFavoriteToggled() { @@ -116,6 +112,7 @@ class ColorFragment : AbsPlayerFragment(R.layout.fragment_color_player) { val playerAlbumCoverFragment = childFragmentManager.findFragmentById(R.id.playerAlbumCoverFragment) as PlayerAlbumCoverFragment playerAlbumCoverFragment.setCallbacks(this) + playerToolbar().drawAboveSystemBars() } private fun setUpSubFragments() { diff --git a/app/src/main/java/code/name/monkey/retromusic/fragments/player/fit/FitFragment.kt b/app/src/main/java/code/name/monkey/retromusic/fragments/player/fit/FitFragment.kt index 936e380e..3400f28f 100644 --- a/app/src/main/java/code/name/monkey/retromusic/fragments/player/fit/FitFragment.kt +++ b/app/src/main/java/code/name/monkey/retromusic/fragments/player/fit/FitFragment.kt @@ -21,6 +21,7 @@ import code.name.monkey.appthemehelper.util.ATHUtil import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper import code.name.monkey.retromusic.R import code.name.monkey.retromusic.databinding.FragmentFitBinding +import code.name.monkey.retromusic.extensions.drawAboveSystemBars import code.name.monkey.retromusic.fragments.base.AbsPlayerFragment import code.name.monkey.retromusic.fragments.player.PlayerAlbumCoverFragment import code.name.monkey.retromusic.helper.MusicPlayerRemote @@ -85,6 +86,7 @@ class FitFragment : AbsPlayerFragment(R.layout.fragment_fit) { _binding = FragmentFitBinding.bind(view) setUpSubFragments() setUpPlayerToolbar() + playerToolbar().drawAboveSystemBars() } private fun setUpSubFragments() { diff --git a/app/src/main/java/code/name/monkey/retromusic/fragments/player/flat/FlatPlayerFragment.kt b/app/src/main/java/code/name/monkey/retromusic/fragments/player/flat/FlatPlayerFragment.kt index 5cad96de..2d357fb8 100644 --- a/app/src/main/java/code/name/monkey/retromusic/fragments/player/flat/FlatPlayerFragment.kt +++ b/app/src/main/java/code/name/monkey/retromusic/fragments/player/flat/FlatPlayerFragment.kt @@ -26,6 +26,7 @@ import code.name.monkey.appthemehelper.util.MaterialValueHelper import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper import code.name.monkey.retromusic.R import code.name.monkey.retromusic.databinding.FragmentFlatPlayerBinding +import code.name.monkey.retromusic.extensions.drawAboveSystemBars import code.name.monkey.retromusic.fragments.base.AbsPlayerFragment import code.name.monkey.retromusic.fragments.player.PlayerAlbumCoverFragment import code.name.monkey.retromusic.helper.MusicPlayerRemote @@ -90,6 +91,7 @@ class FlatPlayerFragment : AbsPlayerFragment(R.layout.fragment_flat_player) { _binding = FragmentFlatPlayerBinding.bind(view) setUpPlayerToolbar() setUpSubFragments() + binding.playbackControlsFragment.drawAboveSystemBars() } override fun onShow() { diff --git a/app/src/main/java/code/name/monkey/retromusic/fragments/player/full/FullPlayerFragment.kt b/app/src/main/java/code/name/monkey/retromusic/fragments/player/full/FullPlayerFragment.kt index bb2eee36..23dd550d 100644 --- a/app/src/main/java/code/name/monkey/retromusic/fragments/player/full/FullPlayerFragment.kt +++ b/app/src/main/java/code/name/monkey/retromusic/fragments/player/full/FullPlayerFragment.kt @@ -22,6 +22,7 @@ import androidx.appcompat.widget.Toolbar import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper import code.name.monkey.retromusic.R import code.name.monkey.retromusic.databinding.FragmentFullBinding +import code.name.monkey.retromusic.extensions.drawAboveSystemBars import code.name.monkey.retromusic.extensions.hide import code.name.monkey.retromusic.extensions.show import code.name.monkey.retromusic.extensions.whichFragment @@ -62,6 +63,7 @@ class FullPlayerFragment : AbsPlayerFragment(R.layout.fragment_full) { setUpPlayerToolbar() setupArtist() binding.nextSong.isSelected = true + binding.playbackControlsFragment.drawAboveSystemBars() } private fun setupArtist() { @@ -131,12 +133,15 @@ class FullPlayerFragment : AbsPlayerFragment(R.layout.fragment_full) { private fun updateArtistImage() { libraryViewModel.artist(MusicPlayerRemote.currentSong.artistId) .observe(viewLifecycleOwner, { artist -> - GlideApp.with(requireActivity()).asBitmapPalette().artistImageOptions(artist) - .load(RetroGlideExtension.getArtistModel(artist)) - .into(object : RetroMusicColoredTarget(binding.artistImage) { - override fun onColorReady(colors: MediaNotificationProcessor) { - } - }) + if (artist.id != -1L) { + GlideApp.with(requireActivity()).asBitmapPalette().artistImageOptions(artist) + .load(RetroGlideExtension.getArtistModel(artist)) + .into(object : RetroMusicColoredTarget(binding.artistImage) { + override fun onColorReady(colors: MediaNotificationProcessor) { + } + }) + } + }) } diff --git a/app/src/main/java/code/name/monkey/retromusic/fragments/player/gradient/GradientPlayerFragment.kt b/app/src/main/java/code/name/monkey/retromusic/fragments/player/gradient/GradientPlayerFragment.kt index 68725bab..ed0c3dca 100644 --- a/app/src/main/java/code/name/monkey/retromusic/fragments/player/gradient/GradientPlayerFragment.kt +++ b/app/src/main/java/code/name/monkey/retromusic/fragments/player/gradient/GradientPlayerFragment.kt @@ -29,6 +29,8 @@ import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.widget.Toolbar import androidx.constraintlayout.widget.ConstraintLayout import androidx.core.view.ViewCompat +import androidx.core.view.WindowInsetsCompat +import androidx.core.view.updatePadding import androidx.lifecycle.lifecycleScope import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView @@ -40,14 +42,12 @@ import code.name.monkey.retromusic.databinding.FragmentGradientPlayerBinding import code.name.monkey.retromusic.db.PlaylistEntity import code.name.monkey.retromusic.db.SongEntity import code.name.monkey.retromusic.db.toSongEntity -import code.name.monkey.retromusic.extensions.hide -import code.name.monkey.retromusic.extensions.ripAlpha -import code.name.monkey.retromusic.extensions.show -import code.name.monkey.retromusic.fragments.VolumeFragment +import code.name.monkey.retromusic.extensions.* import code.name.monkey.retromusic.fragments.base.AbsPlayerControlsFragment import code.name.monkey.retromusic.fragments.base.AbsPlayerFragment import code.name.monkey.retromusic.fragments.base.goToAlbum import code.name.monkey.retromusic.fragments.base.goToArtist +import code.name.monkey.retromusic.fragments.other.VolumeFragment import code.name.monkey.retromusic.helper.MusicPlayerRemote import code.name.monkey.retromusic.helper.MusicProgressViewUpdateHelper import code.name.monkey.retromusic.helper.PlayPauseButtonOnClickHandler @@ -81,6 +81,7 @@ class GradientPlayerFragment : AbsPlayerFragment(R.layout.fragment_gradient_play private var recyclerViewTouchActionGuardManager: RecyclerViewTouchActionGuardManager? = null private var playingQueueAdapter: PlayingQueueAdapter? = null private lateinit var linearLayoutManager: LinearLayoutManager + private var bottomInsets = 0 private var _binding: FragmentGradientPlayerBinding? = null private val binding get() = _binding!! @@ -88,11 +89,11 @@ class GradientPlayerFragment : AbsPlayerFragment(R.layout.fragment_gradient_play private val bottomSheetCallbackList = object : BottomSheetCallback() { override fun onSlide(bottomSheet: View, slideOffset: Float) { mainActivity.getBottomSheetBehavior().setAllowDragging(false) - binding.playerQueueSheet.setPadding( - binding.playerQueueSheet.paddingLeft, - (slideOffset * binding.statusBarLayout.statusBar.height).toInt(), - binding.playerQueueSheet.paddingRight, - binding.playerQueueSheet.paddingBottom + binding.playerQueueSheet.updatePadding( + top = (slideOffset * binding.statusBarLayout.statusBar.height).toInt() + ) + binding.container.updatePadding( + bottom = ((1 - slideOffset) * bottomInsets).toInt() ) } @@ -157,6 +158,14 @@ class GradientPlayerFragment : AbsPlayerFragment(R.layout.fragment_gradient_play binding.playbackControlsFragment.text.setOnClickListener { goToArtist(requireActivity()) } + ViewCompat.setOnApplyWindowInsetsListener( + (binding.container) + ) { v: View, insets: WindowInsetsCompat -> + bottomInsets = insets.safeGetBottomInsets() + v.updatePadding(bottom = bottomInsets) + insets + } + binding.playbackControlsFragment.root.drawAboveSystemBars() } @SuppressLint("ClickableViewAccessibility") diff --git a/app/src/main/java/code/name/monkey/retromusic/fragments/player/material/MaterialFragment.kt b/app/src/main/java/code/name/monkey/retromusic/fragments/player/material/MaterialFragment.kt index 888f6ad9..b70a38b9 100644 --- a/app/src/main/java/code/name/monkey/retromusic/fragments/player/material/MaterialFragment.kt +++ b/app/src/main/java/code/name/monkey/retromusic/fragments/player/material/MaterialFragment.kt @@ -21,6 +21,7 @@ import code.name.monkey.appthemehelper.util.ATHUtil import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper import code.name.monkey.retromusic.R import code.name.monkey.retromusic.databinding.FragmentMaterialBinding +import code.name.monkey.retromusic.extensions.drawAboveSystemBars import code.name.monkey.retromusic.fragments.base.AbsPlayerFragment import code.name.monkey.retromusic.fragments.player.PlayerAlbumCoverFragment import code.name.monkey.retromusic.fragments.player.normal.PlayerFragment @@ -93,6 +94,7 @@ class MaterialFragment : AbsPlayerFragment(R.layout.fragment_material) { _binding = FragmentMaterialBinding.bind(view) setUpSubFragments() setUpPlayerToolbar() + playerToolbar().drawAboveSystemBars() } private fun setUpSubFragments() { diff --git a/app/src/main/java/code/name/monkey/retromusic/fragments/player/normal/PlayerFragment.kt b/app/src/main/java/code/name/monkey/retromusic/fragments/player/normal/PlayerFragment.kt index 50a3a466..3be4587c 100644 --- a/app/src/main/java/code/name/monkey/retromusic/fragments/player/normal/PlayerFragment.kt +++ b/app/src/main/java/code/name/monkey/retromusic/fragments/player/normal/PlayerFragment.kt @@ -24,6 +24,7 @@ import code.name.monkey.appthemehelper.util.ATHUtil import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper import code.name.monkey.retromusic.R import code.name.monkey.retromusic.databinding.FragmentPlayerBinding +import code.name.monkey.retromusic.extensions.drawAboveSystemBars import code.name.monkey.retromusic.fragments.base.AbsPlayerFragment import code.name.monkey.retromusic.fragments.player.PlayerAlbumCoverFragment import code.name.monkey.retromusic.helper.MusicPlayerRemote @@ -120,6 +121,7 @@ class PlayerFragment : AbsPlayerFragment(R.layout.fragment_player) { _binding = FragmentPlayerBinding.bind(view) setUpSubFragments() setUpPlayerToolbar() + playerToolbar().drawAboveSystemBars() } override fun onDestroyView() { diff --git a/app/src/main/java/code/name/monkey/retromusic/fragments/player/peak/PeakPlayerFragment.kt b/app/src/main/java/code/name/monkey/retromusic/fragments/player/peak/PeakPlayerFragment.kt index 3fe48ac4..9b2b0b11 100644 --- a/app/src/main/java/code/name/monkey/retromusic/fragments/player/peak/PeakPlayerFragment.kt +++ b/app/src/main/java/code/name/monkey/retromusic/fragments/player/peak/PeakPlayerFragment.kt @@ -21,6 +21,7 @@ import code.name.monkey.appthemehelper.util.ATHUtil import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper import code.name.monkey.retromusic.R import code.name.monkey.retromusic.databinding.FragmentPeakPlayerBinding +import code.name.monkey.retromusic.extensions.drawAboveSystemBars import code.name.monkey.retromusic.extensions.hide import code.name.monkey.retromusic.extensions.show import code.name.monkey.retromusic.fragments.base.AbsPlayerFragment @@ -55,6 +56,7 @@ class PeakPlayerFragment : AbsPlayerFragment(R.layout.fragment_peak_player) { binding.text.setOnClickListener { goToArtist(requireActivity()) } + binding.root.drawAboveSystemBars() } private fun setUpSubFragments() { diff --git a/app/src/main/java/code/name/monkey/retromusic/fragments/player/plain/PlainPlayerFragment.kt b/app/src/main/java/code/name/monkey/retromusic/fragments/player/plain/PlainPlayerFragment.kt index 220ef458..83d825b1 100644 --- a/app/src/main/java/code/name/monkey/retromusic/fragments/player/plain/PlainPlayerFragment.kt +++ b/app/src/main/java/code/name/monkey/retromusic/fragments/player/plain/PlainPlayerFragment.kt @@ -21,6 +21,7 @@ import code.name.monkey.appthemehelper.util.ATHUtil import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper import code.name.monkey.retromusic.R import code.name.monkey.retromusic.databinding.FragmentPlainPlayerBinding +import code.name.monkey.retromusic.extensions.drawAboveSystemBars import code.name.monkey.retromusic.fragments.base.AbsPlayerFragment import code.name.monkey.retromusic.fragments.base.goToAlbum import code.name.monkey.retromusic.fragments.base.goToArtist @@ -84,6 +85,7 @@ class PlainPlayerFragment : AbsPlayerFragment(R.layout.fragment_plain_player) { binding.text.setOnClickListener { goToArtist(requireActivity()) } + playerToolbar().drawAboveSystemBars() } private fun setUpSubFragments() { diff --git a/app/src/main/java/code/name/monkey/retromusic/fragments/player/simple/SimplePlayerFragment.kt b/app/src/main/java/code/name/monkey/retromusic/fragments/player/simple/SimplePlayerFragment.kt index 1a2b354d..f4f434ca 100644 --- a/app/src/main/java/code/name/monkey/retromusic/fragments/player/simple/SimplePlayerFragment.kt +++ b/app/src/main/java/code/name/monkey/retromusic/fragments/player/simple/SimplePlayerFragment.kt @@ -21,6 +21,7 @@ import code.name.monkey.appthemehelper.util.ATHUtil import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper import code.name.monkey.retromusic.R import code.name.monkey.retromusic.databinding.FragmentSimplePlayerBinding +import code.name.monkey.retromusic.extensions.drawAboveSystemBars import code.name.monkey.retromusic.fragments.base.AbsPlayerFragment import code.name.monkey.retromusic.fragments.player.PlayerAlbumCoverFragment import code.name.monkey.retromusic.helper.MusicPlayerRemote @@ -51,6 +52,7 @@ class SimplePlayerFragment : AbsPlayerFragment(R.layout.fragment_simple_player) _binding = FragmentSimplePlayerBinding.bind(view) setUpSubFragments() setUpPlayerToolbar() + playerToolbar().drawAboveSystemBars() } private fun setUpSubFragments() { diff --git a/app/src/main/java/code/name/monkey/retromusic/fragments/player/tiny/TinyPlayerFragment.kt b/app/src/main/java/code/name/monkey/retromusic/fragments/player/tiny/TinyPlayerFragment.kt index 630efbce..80ba1c56 100644 --- a/app/src/main/java/code/name/monkey/retromusic/fragments/player/tiny/TinyPlayerFragment.kt +++ b/app/src/main/java/code/name/monkey/retromusic/fragments/player/tiny/TinyPlayerFragment.kt @@ -27,6 +27,7 @@ import androidx.appcompat.widget.Toolbar import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper import code.name.monkey.retromusic.R import code.name.monkey.retromusic.databinding.FragmentTinyPlayerBinding +import code.name.monkey.retromusic.extensions.drawAboveSystemBars import code.name.monkey.retromusic.extensions.hide import code.name.monkey.retromusic.extensions.show import code.name.monkey.retromusic.fragments.base.AbsPlayerFragment @@ -147,6 +148,7 @@ class TinyPlayerFragment : AbsPlayerFragment(R.layout.fragment_tiny_player), binding.text.setOnClickListener { goToArtist(requireActivity()) } + playerToolbar().drawAboveSystemBars() } private fun setUpSubFragments() { diff --git a/app/src/main/java/code/name/monkey/retromusic/fragments/playlists/PlaylistDetailsFragment.kt b/app/src/main/java/code/name/monkey/retromusic/fragments/playlists/PlaylistDetailsFragment.kt index 320f9a88..bf85121a 100644 --- a/app/src/main/java/code/name/monkey/retromusic/fragments/playlists/PlaylistDetailsFragment.kt +++ b/app/src/main/java/code/name/monkey/retromusic/fragments/playlists/PlaylistDetailsFragment.kt @@ -22,10 +22,15 @@ import code.name.monkey.retromusic.extensions.dipToPix import code.name.monkey.retromusic.extensions.surfaceColor import code.name.monkey.retromusic.fragments.base.AbsMainActivityFragment import code.name.monkey.retromusic.helper.menu.PlaylistMenuHelper +import code.name.monkey.retromusic.interfaces.ICabCallback import code.name.monkey.retromusic.interfaces.ICabHolder import code.name.monkey.retromusic.model.Song import code.name.monkey.retromusic.util.RetroColorUtil -import com.afollestad.materialcab.MaterialCab +import com.afollestad.materialcab.attached.AttachedCab +import com.afollestad.materialcab.attached.destroy +import com.afollestad.materialcab.attached.isActive +import com.afollestad.materialcab.createCab +import com.google.android.material.shape.MaterialShapeDrawable import com.google.android.material.transition.MaterialSharedAxis import com.h6ah4i.android.widget.advrecyclerview.animator.DraggableItemAnimator import com.h6ah4i.android.widget.advrecyclerview.animator.GeneralItemAnimator @@ -71,6 +76,8 @@ class PlaylistDetailsFragment : AbsMainActivityFragment(R.layout.fragment_playli requireActivity().onBackPressed() } } + binding.appBarLayout.statusBarForeground = + MaterialShapeDrawable.createWithElevationOverlay(requireContext()) } private fun setUpRecyclerView() { @@ -78,7 +85,7 @@ class PlaylistDetailsFragment : AbsMainActivityFragment(R.layout.fragment_playli playlist.playlistEntity, requireActivity(), ArrayList(), - R.layout.item_list, + R.layout.item_queue, this ) @@ -158,29 +165,35 @@ class PlaylistDetailsFragment : AbsMainActivityFragment(R.layout.fragment_playli private fun handleBackPress(): Boolean { cab?.let { - if (it.isActive) { - it.finish() + if (it.isActive()) { + it.destroy() return true } } return false } - private var cab: MaterialCab? = null + private var cab: AttachedCab? = null - override fun openCab(menuRes: Int, callback: MaterialCab.Callback): MaterialCab { + override fun openCab(menuRes: Int, callback: ICabCallback): AttachedCab { cab?.let { println("Cab") - if (it.isActive) { - it.finish() + if (it.isActive()) { + it.destroy() } } - cab = MaterialCab(mainActivity, R.id.cab_stub) - .setMenu(menuRes) - .setCloseDrawableRes(R.drawable.ic_close) - .setBackgroundColor(RetroColorUtil.shiftBackgroundColorForLightText(surfaceColor())) - .start(callback) - return cab as MaterialCab + cab = createCab(R.id.toolbar_container) { + menu(menuRes) + closeDrawable(R.drawable.ic_close) + backgroundColor(literal = RetroColorUtil.shiftBackgroundColor(surfaceColor())) + slideDown() + onCreate { cab, menu -> callback.onCabCreated(cab, menu) } + onSelection { + callback.onCabItemClicked(it) + } + onDestroy { callback.onCabFinished(it) } + } + return cab as AttachedCab } } \ No newline at end of file diff --git a/app/src/main/java/code/name/monkey/retromusic/fragments/playlists/PlaylistsFragment.kt b/app/src/main/java/code/name/monkey/retromusic/fragments/playlists/PlaylistsFragment.kt index 31d38ac5..e86f983b 100644 --- a/app/src/main/java/code/name/monkey/retromusic/fragments/playlists/PlaylistsFragment.kt +++ b/app/src/main/java/code/name/monkey/retromusic/fragments/playlists/PlaylistsFragment.kt @@ -25,21 +25,18 @@ import code.name.monkey.retromusic.EXTRA_PLAYLIST import code.name.monkey.retromusic.R import code.name.monkey.retromusic.adapter.playlist.PlaylistAdapter import code.name.monkey.retromusic.db.PlaylistWithSongs -import code.name.monkey.retromusic.extensions.surfaceColor +import code.name.monkey.retromusic.extensions.navigate import code.name.monkey.retromusic.fragments.ReloadType import code.name.monkey.retromusic.fragments.base.AbsRecyclerViewCustomGridSizeFragment import code.name.monkey.retromusic.helper.SortOrder.PlaylistSortOrder -import code.name.monkey.retromusic.interfaces.ICabHolder import code.name.monkey.retromusic.interfaces.IPlaylistClickListener import code.name.monkey.retromusic.util.PreferenceUtil -import code.name.monkey.retromusic.util.RetroColorUtil -import com.afollestad.materialcab.MaterialCab import com.google.android.gms.cast.framework.CastButtonFactory import com.google.android.material.transition.MaterialSharedAxis class PlaylistsFragment : AbsRecyclerViewCustomGridSizeFragment(), - IPlaylistClickListener, ICabHolder { + IPlaylistClickListener { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) @@ -50,18 +47,20 @@ class PlaylistsFragment : adapter?.swapDataSet(listOf()) }) requireActivity().onBackPressedDispatcher.addCallback(viewLifecycleOwner) { - if (!handleBackPress()) { - remove() - requireActivity().onBackPressed() - } + remove() + mainActivity.finish() } } override val titleRes: Int get() = R.string.playlists + override val emptyMessage: Int get() = R.string.no_playlists + override val isShuffleVisible: Boolean + get() = false + override fun createLayoutManager(): GridLayoutManager { return GridLayoutManager(requireContext(), getGridSize()) } @@ -71,7 +70,7 @@ class PlaylistsFragment : requireActivity(), ArrayList(), itemLayoutRes(), - this, + null, this ) } @@ -181,7 +180,7 @@ class PlaylistsFragment : } override fun loadLayoutRes(): Int { - return R.layout.item_grid + return R.layout.item_card } override fun saveLayoutRes(layoutRes: Int) { @@ -198,31 +197,4 @@ class PlaylistsFragment : null ) } - - private fun handleBackPress(): Boolean { - cab?.let { - if (it.isActive) { - it.finish() - return true - } - } - return false - } - - private var cab: MaterialCab? = null - - override fun openCab(menuRes: Int, callback: MaterialCab.Callback): MaterialCab { - cab?.let { - println("Cab") - if (it.isActive) { - it.finish() - } - } - cab = MaterialCab(mainActivity, R.id.cab_stub) - .setMenu(menuRes) - .setCloseDrawableRes(R.drawable.ic_close) - .setBackgroundColor(RetroColorUtil.shiftBackgroundColorForLightText(surfaceColor())) - .start(callback) - return cab as MaterialCab - } -} \ No newline at end of file +} diff --git a/app/src/main/java/code/name/monkey/retromusic/fragments/queue/PlayingQueueFragment.kt b/app/src/main/java/code/name/monkey/retromusic/fragments/queue/PlayingQueueFragment.kt index c8a218c7..8ec96e2e 100644 --- a/app/src/main/java/code/name/monkey/retromusic/fragments/queue/PlayingQueueFragment.kt +++ b/app/src/main/java/code/name/monkey/retromusic/fragments/queue/PlayingQueueFragment.kt @@ -14,80 +14,121 @@ */ package code.name.monkey.retromusic.fragments.queue +import android.content.res.ColorStateList import android.os.Bundle import android.view.View -import androidx.appcompat.app.AppCompatActivity +import androidx.navigation.findNavController +import androidx.navigation.fragment.findNavController import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView +import code.name.monkey.appthemehelper.util.ColorUtil +import code.name.monkey.appthemehelper.util.MaterialValueHelper import code.name.monkey.retromusic.R import code.name.monkey.retromusic.adapter.song.PlayingQueueAdapter -import code.name.monkey.retromusic.fragments.base.AbsRecyclerViewFragment +import code.name.monkey.retromusic.databinding.FragmentPlayingQueueBinding +import code.name.monkey.retromusic.extensions.accentColor +import code.name.monkey.retromusic.fragments.base.AbsMusicServiceFragment import code.name.monkey.retromusic.helper.MusicPlayerRemote +import code.name.monkey.retromusic.util.MusicUtil +import code.name.monkey.retromusic.util.ThemedFastScroller import com.h6ah4i.android.widget.advrecyclerview.animator.DraggableItemAnimator import com.h6ah4i.android.widget.advrecyclerview.draggable.RecyclerViewDragDropManager import com.h6ah4i.android.widget.advrecyclerview.swipeable.RecyclerViewSwipeManager import com.h6ah4i.android.widget.advrecyclerview.touchguard.RecyclerViewTouchActionGuardManager import com.h6ah4i.android.widget.advrecyclerview.utils.WrapperAdapterUtils -/** - * Created by hemanths on 2019-12-08. - */ -class PlayingQueueFragment : AbsRecyclerViewFragment() { +class PlayingQueueFragment : AbsMusicServiceFragment(R.layout.fragment_playing_queue) { - private lateinit var wrappedAdapter: RecyclerView.Adapter<*> + private var _binding: FragmentPlayingQueueBinding? = null + private val binding get() = _binding!! + private var wrappedAdapter: RecyclerView.Adapter<*>? = null private var recyclerViewDragDropManager: RecyclerViewDragDropManager? = null private var recyclerViewSwipeManager: RecyclerViewSwipeManager? = null private var recyclerViewTouchActionGuardManager: RecyclerViewTouchActionGuardManager? = null - override val titleRes: Int - get() = R.string.now_playing_queue + private var playingQueueAdapter: PlayingQueueAdapter? = null + private lateinit var linearLayoutManager: LinearLayoutManager + + private fun getUpNextAndQueueTime(): String { + val duration = MusicPlayerRemote.getQueueDurationMillis(MusicPlayerRemote.position) + return MusicUtil.buildInfoString( + resources.getString(R.string.up_next), + MusicUtil.getReadableDurationString(duration) + ) + } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - setupRecyclerView() + _binding = FragmentPlayingQueueBinding.bind(view) + + setupToolbar() + setUpRecyclerView() + + binding.clearQueue.setOnClickListener { + MusicPlayerRemote.clearQueue() + } + checkForPadding() } - private fun setupRecyclerView() { + private fun setUpRecyclerView() { recyclerViewTouchActionGuardManager = RecyclerViewTouchActionGuardManager() recyclerViewDragDropManager = RecyclerViewDragDropManager() recyclerViewSwipeManager = RecyclerViewSwipeManager() val animator = DraggableItemAnimator() animator.supportsChangeAnimations = false - wrappedAdapter = - recyclerViewDragDropManager?.createWrappedAdapter(adapter!!) as RecyclerView.Adapter<*> - wrappedAdapter = - recyclerViewSwipeManager?.createWrappedAdapter(wrappedAdapter) as RecyclerView.Adapter<*> - recyclerView().layoutManager = layoutManager - recyclerView().adapter = wrappedAdapter - recyclerView().itemAnimator = animator - recyclerViewTouchActionGuardManager?.attachRecyclerView(recyclerView()) - recyclerViewDragDropManager?.attachRecyclerView(recyclerView()) - recyclerViewSwipeManager?.attachRecyclerView(recyclerView()) - layoutManager?.scrollToPositionWithOffset(MusicPlayerRemote.position + 1, 0) - } - - override fun createLayoutManager(): LinearLayoutManager { - return LinearLayoutManager(requireContext()) - } - - override fun createAdapter(): PlayingQueueAdapter { - return PlayingQueueAdapter( - requireActivity() as AppCompatActivity, + playingQueueAdapter = PlayingQueueAdapter( + requireActivity(), MusicPlayerRemote.playingQueue.toMutableList(), MusicPlayerRemote.position, R.layout.item_queue ) + wrappedAdapter = recyclerViewDragDropManager?.createWrappedAdapter(playingQueueAdapter!!) + wrappedAdapter = wrappedAdapter?.let { recyclerViewSwipeManager?.createWrappedAdapter(it) } + + linearLayoutManager = LinearLayoutManager(requireContext()) + + binding.recyclerView.layoutManager = linearLayoutManager + binding.recyclerView.adapter = wrappedAdapter + binding.recyclerView.itemAnimator = animator + recyclerViewTouchActionGuardManager?.attachRecyclerView(binding.recyclerView) + recyclerViewDragDropManager?.attachRecyclerView(binding.recyclerView) + recyclerViewSwipeManager?.attachRecyclerView(binding.recyclerView) + linearLayoutManager.scrollToPositionWithOffset(MusicPlayerRemote.position + 1, 0) + + binding.recyclerView.addOnScrollListener(object : RecyclerView.OnScrollListener() { + override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) { + super.onScrolled(recyclerView, dx, dy) + if (dy > 0) { + binding.clearQueue.shrink() + } else if (dy < 0) { + binding.clearQueue.extend() + } + } + }) + ThemedFastScroller.create(binding.recyclerView) } - override fun onServiceConnected() { - super.onServiceConnected() - updateQueue() + private fun checkForPadding() { } override fun onQueueChanged() { - super.onQueueChanged() + if (MusicPlayerRemote.playingQueue.isEmpty()) { + findNavController().navigateUp() + return + } + checkForPadding() updateQueue() + updateCurrentSong() + } + + override fun onMediaStoreChanged() { + updateQueue() + updateCurrentSong() + } + + private fun updateCurrentSong() { + binding.toolbar.subtitle = getUpNextAndQueueTime() } override fun onPlayingMetaChanged() { @@ -95,50 +136,62 @@ class PlayingQueueFragment : AbsRecyclerViewFragment { + bottomMargin = it + } + }) } private fun setupChips() { val chips = binding.searchFilterGroup.children.map { it as Chip } - val states = arrayOf( - intArrayOf(-android.R.attr.state_checked), - intArrayOf(android.R.attr.state_checked) - ) + if (!PreferenceUtil.materialYou) { + val states = arrayOf( + intArrayOf(-android.R.attr.state_checked), + intArrayOf(android.R.attr.state_checked) + ) - val colors = intArrayOf( - android.R.color.transparent, - ThemeStore.accentColor(requireContext()) - ) + val colors = intArrayOf( + android.R.color.transparent, + ThemeStore.accentColor(requireContext()).addAlpha(0.5F) + ) - chips.forEach { - it.chipBackgroundColor = ColorStateList(states, colors) - it.chipIconTint = ColorStateList.valueOf(ThemeStore.textColorPrimary(requireContext())) - it.chipStrokeColor = - ColorStateList.valueOf(ThemeStore.textColorSecondary(requireContext())) - .withAlpha(30) - it.closeIconTint = - ColorStateList.valueOf(ThemeStore.textColorPrimaryInverse(requireContext())) - it.chipStrokeWidth = 2F - it.setOnCheckedChangeListener(this) + chips.forEach { + it.chipBackgroundColor = ColorStateList(states, colors) + } } + binding.searchFilterGroup.setOnCheckedChangeListener(this) } private fun showData(data: List) { @@ -165,13 +167,18 @@ class SearchFragment : AbsMainActivityFragment(R.layout.fragment_search), TextWa TransitionManager.beginDelayedTransition(binding.appBarLayout) binding.voiceSearch.isGone = query.isNotEmpty() binding.clearText.isVisible = query.isNotEmpty() - val filters = getFilters() - libraryViewModel.search(query, filters) + val filter = getFilter() + libraryViewModel.search(query, filter) } - private fun getFilters(): List { - return binding.searchFilterGroup.children.toList().map { - (it as Chip).isChecked + private fun getFilter(): Filter { + return when (binding.searchFilterGroup.checkedChipId) { + R.id.chip_audio -> Filter.SONGS + R.id.chip_artists -> Filter.ARTISTS + R.id.chip_albums -> Filter.ALBUMS + R.id.chip_album_artists -> Filter.ALBUM_ARTISTS + R.id.chip_genres -> Filter.GENRES + else -> Filter.NO_FILTER } } @@ -207,7 +214,7 @@ class SearchFragment : AbsMainActivityFragment(R.layout.fragment_search), TextWa override fun onResume() { super.onResume() - mainActivity.setBottomBarVisibility(false) + mainActivity.setBottomNavVisibility(false) } private fun hideKeyboard(view: View?) { @@ -218,28 +225,20 @@ class SearchFragment : AbsMainActivityFragment(R.layout.fragment_search), TextWa } } - override fun onCheckedChanged(buttonView: CompoundButton?, isChecked: Boolean) { - val checkedChip = (buttonView as Chip) - checkedChip.isCloseIconVisible = isChecked - if (isChecked) { - val color = ThemeStore.textColorPrimaryInverse(requireContext()) - checkedChip.apply { - setTextColor(color) - chipIconTint = ColorStateList.valueOf(color) - chipStrokeWidth = 0F - } - } else { - val color = ThemeStore.textColorPrimary(requireContext()) - checkedChip.apply { - setTextColor(color) - chipIconTint = ColorStateList.valueOf(color) - chipStrokeWidth = 2F - } - } + override fun onCheckedChanged(group: ChipGroup?, @IdRes checkedId: Int) { search(binding.searchView.text.toString()) } } +enum class Filter { + SONGS, + ARTISTS, + ALBUMS, + ALBUM_ARTISTS, + GENRES, + NO_FILTER +} + fun TextInputEditText.clearText() { text = null } diff --git a/app/src/main/java/code/name/monkey/retromusic/fragments/settings/AbsSettingsFragment.kt b/app/src/main/java/code/name/monkey/retromusic/fragments/settings/AbsSettingsFragment.kt index 5ef8ce7e..94775a27 100644 --- a/app/src/main/java/code/name/monkey/retromusic/fragments/settings/AbsSettingsFragment.kt +++ b/app/src/main/java/code/name/monkey/retromusic/fragments/settings/AbsSettingsFragment.kt @@ -19,12 +19,15 @@ import android.graphics.drawable.ColorDrawable import android.os.Bundle import android.view.View import android.widget.Toast +import androidx.core.view.updatePadding import androidx.preference.ListPreference import androidx.preference.Preference import androidx.preference.PreferenceManager import code.name.monkey.appthemehelper.common.prefs.supportv7.ATEPreferenceFragmentCompat +import code.name.monkey.retromusic.activities.OnThemeChangedListener import code.name.monkey.retromusic.preferences.* import code.name.monkey.retromusic.util.NavigationUtil +import code.name.monkey.retromusic.util.RetroUtil /** * @author Hemanth S (h4h13). @@ -64,8 +67,12 @@ abstract class AbsSettingsFragment : ATEPreferenceFragmentCompat() { super.onViewCreated(view, savedInstanceState) setDivider(ColorDrawable(Color.TRANSPARENT)) listView.overScrollMode = View.OVER_SCROLL_NEVER - listView.setPadding(0, 0, 0, 0) - listView.setPaddingRelative(0, 0, 0, 0) + // This is a workaround as CollapsingToolbarLayout consumes insets and + // insets are not passed to child views + // https://github.com/material-components/material-components-android/issues/1310 + if (!RetroUtil.isLandscape()) { + listView.updatePadding(bottom = RetroUtil.getNavigationBarHeight()) + } invalidateSettings() } @@ -94,4 +101,12 @@ abstract class AbsSettingsFragment : ATEPreferenceFragmentCompat() { else -> super.onDisplayPreferenceDialog(preference) } } + + fun restartActivity() { + if (activity is OnThemeChangedListener) { + (activity as OnThemeChangedListener).onThemeValuesChanged() + } else { + activity?.recreate() + } + } } diff --git a/app/src/main/java/code/name/monkey/retromusic/fragments/settings/MainSettingsFragment.kt b/app/src/main/java/code/name/monkey/retromusic/fragments/settings/MainSettingsFragment.kt index 30b39fa5..c507a259 100644 --- a/app/src/main/java/code/name/monkey/retromusic/fragments/settings/MainSettingsFragment.kt +++ b/app/src/main/java/code/name/monkey/retromusic/fragments/settings/MainSettingsFragment.kt @@ -45,6 +45,7 @@ class MainSettingsFragment : Fragment(), View.OnClickListener { R.id.otherSettings -> findNavController().navigate(R.id.action_mainSettingsFragment_to_otherSettingsFragment) R.id.aboutSettings -> findNavController().navigate(R.id.action_mainSettingsFragment_to_aboutActivity) R.id.nowPlayingSettings -> findNavController().navigate(R.id.action_mainSettingsFragment_to_nowPlayingSettingsFragment) + R.id.backup_restore_settings -> findNavController().navigate(R.id.action_mainSettingsFragment_to_backupFragment) } } @@ -68,6 +69,7 @@ class MainSettingsFragment : Fragment(), View.OnClickListener { binding.notificationSettings.setOnClickListener(this) binding.otherSettings.setOnClickListener(this) binding.aboutSettings.setOnClickListener(this) + binding.backupRestoreSettings.setOnClickListener(this) binding.buyProContainer.apply { if (App.isProVersion()) hide() else show() diff --git a/app/src/main/java/code/name/monkey/retromusic/fragments/settings/OtherSettingsFragment.kt b/app/src/main/java/code/name/monkey/retromusic/fragments/settings/OtherSettingsFragment.kt index f7fb48fe..da9088db 100644 --- a/app/src/main/java/code/name/monkey/retromusic/fragments/settings/OtherSettingsFragment.kt +++ b/app/src/main/java/code/name/monkey/retromusic/fragments/settings/OtherSettingsFragment.kt @@ -39,7 +39,7 @@ class OtherSettingsFragment : AbsSettingsFragment() { val languagePreference: ATEListPreference? = findPreference(LANGUAGE_NAME) languagePreference?.setOnPreferenceChangeListener { _, _ -> println("Invalidated") - requireActivity().recreate() + restartActivity() return@setOnPreferenceChangeListener true } } @@ -69,7 +69,7 @@ class OtherSettingsFragment : AbsSettingsFragment() { manager.startInstall(request) // Recreate the activity on download complete .addOnCompleteListener { - activity?.recreate() + restartActivity() } } else { requireActivity().recreate() diff --git a/app/src/main/java/code/name/monkey/retromusic/fragments/settings/PersonalizeSettingsFragment.kt b/app/src/main/java/code/name/monkey/retromusic/fragments/settings/PersonalizeSettingsFragment.kt index 85c7b594..3ebd069c 100644 --- a/app/src/main/java/code/name/monkey/retromusic/fragments/settings/PersonalizeSettingsFragment.kt +++ b/app/src/main/java/code/name/monkey/retromusic/fragments/settings/PersonalizeSettingsFragment.kt @@ -25,7 +25,7 @@ class PersonalizeSettingsFragment : AbsSettingsFragment() { override fun invalidateSettings() { val toggleFullScreen: TwoStatePreference? = findPreference(TOGGLE_FULL_SCREEN) toggleFullScreen?.setOnPreferenceChangeListener { _, _ -> - requireActivity().recreate() + restartActivity() true } } diff --git a/app/src/main/java/code/name/monkey/retromusic/fragments/settings/ThemeSettingsFragment.kt b/app/src/main/java/code/name/monkey/retromusic/fragments/settings/ThemeSettingsFragment.kt index befbca95..c0c8207e 100644 --- a/app/src/main/java/code/name/monkey/retromusic/fragments/settings/ThemeSettingsFragment.kt +++ b/app/src/main/java/code/name/monkey/retromusic/fragments/settings/ThemeSettingsFragment.kt @@ -30,6 +30,7 @@ import code.name.monkey.retromusic.appshortcuts.DynamicShortcutManager import code.name.monkey.retromusic.util.PreferenceUtil import com.afollestad.materialdialogs.MaterialDialog import com.afollestad.materialdialogs.color.colorChooser +import com.google.android.material.color.DynamicColors /** * @author Hemanth S (h4h13). @@ -49,7 +50,7 @@ class ThemeSettingsFragment : AbsSettingsFragment() { requireActivity().setTheme(PreferenceUtil.themeResFromPrefValue(theme)) DynamicShortcutManager(requireContext()).updateDynamicShortcuts() } - requireActivity().recreate() + restartActivity() true } } @@ -68,7 +69,7 @@ class ThemeSettingsFragment : AbsSettingsFragment() { ThemeStore.editTheme(requireContext()).accentColor(color).commit() if (VersionUtils.hasNougatMR()) DynamicShortcutManager(requireContext()).updateDynamicShortcuts() - requireActivity().recreate() + restartActivity() } } return@setOnPreferenceClickListener true @@ -84,7 +85,7 @@ class ThemeSettingsFragment : AbsSettingsFragment() { requireActivity().setTheme(PreferenceUtil.themeResFromPrefValue("black")) DynamicShortcutManager(requireContext()).updateDynamicShortcuts() } - requireActivity().recreate() + restartActivity() true } @@ -96,7 +97,7 @@ class ThemeSettingsFragment : AbsSettingsFragment() { .putBoolean("desaturated_color", desaturated) .apply() PreferenceUtil.isDesaturatedColor = desaturated - requireActivity().recreate() + restartActivity() true } @@ -111,6 +112,15 @@ class ThemeSettingsFragment : AbsSettingsFragment() { true } } + + val materialYou: ATESwitchPreference? = findPreference(MATERIAL_YOU) + materialYou?.setOnPreferenceChangeListener { _, newValue -> + if (newValue as Boolean) { + DynamicColors.applyToActivitiesIfAvailable(App.getContext()) + } + restartActivity() + true + } } override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { diff --git a/app/src/main/java/code/name/monkey/retromusic/fragments/songs/SongsFragment.kt b/app/src/main/java/code/name/monkey/retromusic/fragments/songs/SongsFragment.kt index 022b4999..d0b3095e 100644 --- a/app/src/main/java/code/name/monkey/retromusic/fragments/songs/SongsFragment.kt +++ b/app/src/main/java/code/name/monkey/retromusic/fragments/songs/SongsFragment.kt @@ -20,17 +20,22 @@ import androidx.activity.addCallback import androidx.annotation.LayoutRes import androidx.recyclerview.widget.GridLayoutManager import code.name.monkey.retromusic.R -import code.name.monkey.retromusic.adapter.song.ShuffleButtonSongAdapter import code.name.monkey.retromusic.adapter.song.SongAdapter +import code.name.monkey.retromusic.extensions.navigate import code.name.monkey.retromusic.extensions.surfaceColor import code.name.monkey.retromusic.fragments.ReloadType 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.interfaces.ICabCallback import code.name.monkey.retromusic.interfaces.ICabHolder import code.name.monkey.retromusic.util.PreferenceUtil import code.name.monkey.retromusic.util.RetroColorUtil import code.name.monkey.retromusic.util.RetroUtil -import com.afollestad.materialcab.MaterialCab +import com.afollestad.materialcab.attached.AttachedCab +import com.afollestad.materialcab.attached.destroy +import com.afollestad.materialcab.attached.isActive +import com.afollestad.materialcab.createCab import com.google.android.gms.cast.framework.CastButtonFactory class SongsFragment : AbsRecyclerViewCustomGridSizeFragment(), @@ -46,7 +51,7 @@ class SongsFragment : AbsRecyclerViewCustomGridSizeFragment callback.onCabCreated(cab, menu) } + onSelection { + callback.onCabItemClicked(it) + } + onDestroy { callback.onCabFinished(it) } + } + return cab as AttachedCab } } diff --git a/app/src/main/java/code/name/monkey/retromusic/glide/RetroGlideExtension.kt b/app/src/main/java/code/name/monkey/retromusic/glide/RetroGlideExtension.kt index 3f5edc8c..5f54bb94 100644 --- a/app/src/main/java/code/name/monkey/retromusic/glide/RetroGlideExtension.kt +++ b/app/src/main/java/code/name/monkey/retromusic/glide/RetroGlideExtension.kt @@ -26,7 +26,6 @@ import com.bumptech.glide.annotation.GlideType import com.bumptech.glide.load.Key import com.bumptech.glide.load.engine.DiskCacheStrategy import com.bumptech.glide.request.BaseRequestOptions -import com.bumptech.glide.request.target.Target.SIZE_ORIGINAL import com.bumptech.glide.signature.MediaStoreSignature import java.io.File @@ -79,7 +78,11 @@ object RetroGlideExtension { ) } - private fun getArtistModel(artist: Artist, hasCustomImage: Boolean, forceDownload: Boolean): Any { + private fun getArtistModel( + artist: Artist, + hasCustomImage: Boolean, + forceDownload: Boolean + ): Any { return if (!hasCustomImage) { ArtistImage(artist) } else { @@ -97,7 +100,6 @@ object RetroGlideExtension { .diskCacheStrategy(DEFAULT_DISK_CACHE_STRATEGY_ARTIST) .priority(Priority.LOW) .error(DEFAULT_ERROR_ARTIST_IMAGE) - .override(SIZE_ORIGINAL, SIZE_ORIGINAL) .signature(createSignature(artist)) } diff --git a/app/src/main/java/code/name/monkey/retromusic/helper/BackupHelper.kt b/app/src/main/java/code/name/monkey/retromusic/helper/BackupHelper.kt new file mode 100644 index 00000000..02eff857 --- /dev/null +++ b/app/src/main/java/code/name/monkey/retromusic/helper/BackupHelper.kt @@ -0,0 +1,159 @@ +package code.name.monkey.retromusic.helper + +import android.content.Context +import android.os.Environment +import android.widget.Toast +import code.name.monkey.retromusic.App +import code.name.monkey.retromusic.BuildConfig +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.withContext +import java.io.* +import java.util.zip.ZipEntry +import java.util.zip.ZipInputStream +import java.util.zip.ZipOutputStream + +object BackupHelper { + suspend fun createBackup(context: Context, name: String) { + val backupFile = + File(backupRootPath + name + APPEND_EXTENSION) + if (backupFile.parentFile?.exists() != true) { + backupFile.parentFile?.mkdirs() + } + val zipItems = mutableListOf() + zipItems.addAll(getDatabaseZipItems(context)) + zipItems.addAll(getSettingsZipItems(context)) + getUserImageZipItems(context)?.let { zipItems.addAll(it) } + withContext(Dispatchers.IO) { + zipAll(zipItems, backupFile) + } + } + + private fun zipAll(zipItems: List, backupFile: File) { + try { + ZipOutputStream(BufferedOutputStream(FileOutputStream(backupFile))).use { out -> + for (zipItem in zipItems) { + FileInputStream(zipItem.filePath).use { fi -> + BufferedInputStream(fi).use { origin -> + val entry = ZipEntry(zipItem.zipPath) + out.putNextEntry(entry) + origin.copyTo(out, 1024) + } + } + } + } + } catch (exception: FileNotFoundException) { + Toast.makeText(App.getContext(), "Couldn't create backup", Toast.LENGTH_SHORT).show() + } + } + + private fun getDatabaseZipItems(context: Context): List { + return context.databaseList().filter { + it.endsWith(".db") + }.map { + ZipItem(context.getDatabasePath(it).absolutePath, "$DATABASES_PATH${File.separator}$it") + } + } + + private fun getSettingsZipItems(context: Context): List { + val sharedPrefPath = context.filesDir.parentFile?.absolutePath + "/shared_prefs/" + return listOf( + "${BuildConfig.APPLICATION_ID}_preferences.xml", // App settings pref path + "$THEME_PREFS_KEY_DEFAULT.xml" // appthemehelper pref path + ).map { + ZipItem(sharedPrefPath + it, "$SETTINGS_PATH${File.separator}$it") + } + } + + private fun getUserImageZipItems(context: Context): List? { + return context.filesDir.listFiles { _, name -> + name.endsWith(".jpg") + }?.map { + ZipItem(it.absolutePath, "$IMAGES_PATH${File.separator}${it.name}") + } + } + + suspend fun restoreBackup(context: Context, file: File) { + withContext(Dispatchers.IO) { + ZipInputStream(FileInputStream(file)).use { + var entry = it.nextEntry + while (entry != null) { + if (entry.isDatabaseEntry()) restoreDatabase(context, it, entry) + if (entry.isPreferenceEntry()) restorePreferences(context, it, entry) + if (entry.isImageEntry()) restoreImages(context, it, entry) + entry = it.nextEntry + } + } + withContext(Dispatchers.Main) { + Toast.makeText(context, "Restore Completed Successfully", Toast.LENGTH_SHORT).show() + } + } + } + + private fun restoreImages(context: Context, zipIn: ZipInputStream, zipEntry: ZipEntry) { + val filePath = + context.filesDir.path + File.separator + zipEntry.getFileName() + BufferedOutputStream(FileOutputStream(filePath)).use { bos -> + val bytesIn = ByteArray(DEFAULT_BUFFER_SIZE) + var read: Int + while (zipIn.read(bytesIn).also { read = it } != -1) { + bos.write(bytesIn, 0, read) + } + } + } + + private fun restorePreferences(context: Context, zipIn: ZipInputStream, zipEntry: ZipEntry) { + val file = File( + context.filesDir.parent!! + File.separator + "shared_prefs" + File.separator + zipEntry.getFileName() + ) + if (file.exists()) { + file.delete() + } + BufferedOutputStream(FileOutputStream(file)).use { bos -> + val bytesIn = ByteArray(DEFAULT_BUFFER_SIZE) + var read: Int + while (zipIn.read(bytesIn).also { read = it } != -1) { + bos.write(bytesIn, 0, read) + } + } + } + + private fun restoreDatabase(context: Context, zipIn: ZipInputStream, zipEntry: ZipEntry) { + val filePath = + context.filesDir.parent!! + File.separator + DATABASES_PATH + File.separator + zipEntry.getFileName() + BufferedOutputStream(FileOutputStream(filePath)).use { bos -> + val bytesIn = ByteArray(DEFAULT_BUFFER_SIZE) + var read: Int + while (zipIn.read(bytesIn).also { read = it } != -1) { + bos.write(bytesIn, 0, read) + } + } + } + + val backupRootPath = + Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOCUMENTS) + .toString() + "/RetroMusic/Backups/" + const val BACKUP_EXTENSION = "rmbak" + const val APPEND_EXTENSION = ".$BACKUP_EXTENSION" + private const val DATABASES_PATH = "databases" + private const val SETTINGS_PATH = "prefs" + private const val IMAGES_PATH = "userImages" + private const val THEME_PREFS_KEY_DEFAULT = "[[kabouzeid_app-theme-helper]]" + + private fun ZipEntry.isDatabaseEntry(): Boolean { + return name.startsWith(DATABASES_PATH) + } + + private fun ZipEntry.isPreferenceEntry(): Boolean { + return name.startsWith(SETTINGS_PATH) + } + + private fun ZipEntry.isImageEntry(): Boolean { + return name.startsWith(IMAGES_PATH) + } + + private fun ZipEntry.getFileName(): String { + return name.substring(name.lastIndexOf(File.separator)) + } +} + +data class ZipItem(val filePath: String, val zipPath: String) \ No newline at end of file diff --git a/app/src/main/java/code/name/monkey/retromusic/helper/M3UWriter.kt b/app/src/main/java/code/name/monkey/retromusic/helper/M3UWriter.kt index 249cd778..293d3d38 100644 --- a/app/src/main/java/code/name/monkey/retromusic/helper/M3UWriter.kt +++ b/app/src/main/java/code/name/monkey/retromusic/helper/M3UWriter.kt @@ -53,7 +53,9 @@ object M3UWriter : M3UConstants { if (!dir.exists()) dir.mkdirs() val fileName = "${playlistWithSongs.playlistEntity.playlistName}.${M3UConstants.EXTENSION}" val file = File(dir, fileName) - val songs: List = playlistWithSongs.songs.toSongs() + val songs: List = playlistWithSongs.songs.sortedBy { + it.songPrimaryKey + }.toSongs() if (songs.isNotEmpty()) { val bufferedWriter = BufferedWriter(FileWriter(file)) bufferedWriter.write(M3UConstants.HEADER) diff --git a/app/src/main/java/code/name/monkey/retromusic/helper/MusicPlayerRemote.kt b/app/src/main/java/code/name/monkey/retromusic/helper/MusicPlayerRemote.kt index 4f7b5c93..285aa2ad 100644 --- a/app/src/main/java/code/name/monkey/retromusic/helper/MusicPlayerRemote.kt +++ b/app/src/main/java/code/name/monkey/retromusic/helper/MusicPlayerRemote.kt @@ -25,12 +25,13 @@ import android.os.IBinder import android.provider.DocumentsContract import android.widget.Toast import androidx.core.content.ContextCompat +import code.name.monkey.retromusic.R import code.name.monkey.retromusic.model.Song import code.name.monkey.retromusic.repository.SongRepository import code.name.monkey.retromusic.service.MusicService import code.name.monkey.retromusic.util.PreferenceUtil -import org.koin.core.KoinComponent -import org.koin.core.inject +import org.koin.core.component.KoinComponent +import org.koin.core.component.inject import java.io.File import java.util.* @@ -319,7 +320,7 @@ object MusicPlayerRemote : KoinComponent { } Toast.makeText( musicService, - musicService!!.resources.getString(code.name.monkey.retromusic.R.string.added_title_to_playing_queue), + musicService!!.resources.getString(R.string.added_title_to_playing_queue), Toast.LENGTH_SHORT ).show() return true @@ -335,8 +336,8 @@ object MusicPlayerRemote : KoinComponent { openQueue(songs, 0, false) } val toast = - if (songs.size == 1) musicService!!.resources.getString(code.name.monkey.retromusic.R.string.added_title_to_playing_queue) else musicService!!.resources.getString( - code.name.monkey.retromusic.R.string.added_x_titles_to_playing_queue, + if (songs.size == 1) musicService!!.resources.getString(R.string.added_title_to_playing_queue) else musicService!!.resources.getString( + R.string.added_x_titles_to_playing_queue, songs.size ) Toast.makeText(musicService, toast, Toast.LENGTH_SHORT).show() @@ -356,7 +357,7 @@ object MusicPlayerRemote : KoinComponent { } Toast.makeText( musicService, - musicService!!.resources.getString(code.name.monkey.retromusic.R.string.added_title_to_playing_queue), + musicService!!.resources.getString(R.string.added_title_to_playing_queue), Toast.LENGTH_SHORT ).show() return true @@ -372,8 +373,8 @@ object MusicPlayerRemote : KoinComponent { openQueue(songs, 0, false) } val toast = - if (songs.size == 1) musicService!!.resources.getString(code.name.monkey.retromusic.R.string.added_title_to_playing_queue) else musicService!!.resources.getString( - code.name.monkey.retromusic.R.string.added_x_titles_to_playing_queue, + if (songs.size == 1) musicService!!.resources.getString(R.string.added_title_to_playing_queue) else musicService!!.resources.getString( + R.string.added_x_titles_to_playing_queue, songs.size ) Toast.makeText(musicService, toast, Toast.LENGTH_SHORT).show() @@ -391,6 +392,15 @@ object MusicPlayerRemote : KoinComponent { return false } + @JvmStatic + fun removeFromQueue(songs: List): Boolean { + if (musicService != null) { + musicService!!.removeSongs(songs) + return true + } + return false + } + fun removeFromQueue(position: Int): Boolean { if (musicService != null && position >= 0 && position < playingQueue.size) { musicService!!.removeSong(position) diff --git a/app/src/main/java/code/name/monkey/retromusic/helper/SearchQueryHelper.kt b/app/src/main/java/code/name/monkey/retromusic/helper/SearchQueryHelper.kt index 36e9728a..d25b32c0 100644 --- a/app/src/main/java/code/name/monkey/retromusic/helper/SearchQueryHelper.kt +++ b/app/src/main/java/code/name/monkey/retromusic/helper/SearchQueryHelper.kt @@ -19,8 +19,8 @@ import android.os.Bundle import android.provider.MediaStore import code.name.monkey.retromusic.model.Song import code.name.monkey.retromusic.repository.RealSongRepository -import org.koin.core.KoinComponent -import org.koin.core.inject +import org.koin.core.component.KoinComponent +import org.koin.core.component.inject import java.util.* object SearchQueryHelper : KoinComponent { diff --git a/app/src/main/java/code/name/monkey/retromusic/helper/menu/GenreMenuHelper.kt b/app/src/main/java/code/name/monkey/retromusic/helper/menu/GenreMenuHelper.kt index 7aba2b09..aa682ace 100644 --- a/app/src/main/java/code/name/monkey/retromusic/helper/menu/GenreMenuHelper.kt +++ b/app/src/main/java/code/name/monkey/retromusic/helper/menu/GenreMenuHelper.kt @@ -27,9 +27,9 @@ import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import kotlinx.coroutines.withContext -import org.koin.core.KoinComponent -import org.koin.core.get -import org.koin.core.inject +import org.koin.core.component.KoinComponent +import org.koin.core.component.get +import org.koin.core.component.inject object GenreMenuHelper : KoinComponent { private val genreRepository by inject() diff --git a/app/src/main/java/code/name/monkey/retromusic/helper/menu/PlaylistMenuHelper.kt b/app/src/main/java/code/name/monkey/retromusic/helper/menu/PlaylistMenuHelper.kt index 92f35bf2..fb9b3cda 100644 --- a/app/src/main/java/code/name/monkey/retromusic/helper/menu/PlaylistMenuHelper.kt +++ b/app/src/main/java/code/name/monkey/retromusic/helper/menu/PlaylistMenuHelper.kt @@ -29,8 +29,8 @@ import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import kotlinx.coroutines.withContext -import org.koin.core.KoinComponent -import org.koin.core.get +import org.koin.core.component.KoinComponent +import org.koin.core.component.get object PlaylistMenuHelper : KoinComponent { diff --git a/app/src/main/java/code/name/monkey/retromusic/helper/menu/SongMenuHelper.kt b/app/src/main/java/code/name/monkey/retromusic/helper/menu/SongMenuHelper.kt index 114a2ec2..6dc30923 100644 --- a/app/src/main/java/code/name/monkey/retromusic/helper/menu/SongMenuHelper.kt +++ b/app/src/main/java/code/name/monkey/retromusic/helper/menu/SongMenuHelper.kt @@ -44,8 +44,8 @@ import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import kotlinx.coroutines.withContext import org.koin.androidx.viewmodel.ext.android.getViewModel -import org.koin.core.KoinComponent -import org.koin.core.get +import org.koin.core.component.KoinComponent +import org.koin.core.component.get import java.io.File object SongMenuHelper : KoinComponent { diff --git a/app/src/main/java/code/name/monkey/retromusic/helper/menu/SongsMenuHelper.kt b/app/src/main/java/code/name/monkey/retromusic/helper/menu/SongsMenuHelper.kt index 63ef5883..1d738342 100644 --- a/app/src/main/java/code/name/monkey/retromusic/helper/menu/SongsMenuHelper.kt +++ b/app/src/main/java/code/name/monkey/retromusic/helper/menu/SongsMenuHelper.kt @@ -25,8 +25,8 @@ import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import kotlinx.coroutines.withContext -import org.koin.core.KoinComponent -import org.koin.core.get +import org.koin.core.component.KoinComponent +import org.koin.core.component.get object SongsMenuHelper : KoinComponent { fun handleMenuClick( diff --git a/app/src/main/java/code/name/monkey/retromusic/interfaces/ICabCallback.kt b/app/src/main/java/code/name/monkey/retromusic/interfaces/ICabCallback.kt new file mode 100644 index 00000000..76750ff0 --- /dev/null +++ b/app/src/main/java/code/name/monkey/retromusic/interfaces/ICabCallback.kt @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2020 Hemanth Savarla. + * + * 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.interfaces + +import android.view.Menu +import android.view.MenuItem +import com.afollestad.materialcab.attached.AttachedCab + +interface ICabCallback { + fun onCabCreated(cab: AttachedCab, menu: Menu): Boolean + + fun onCabItemClicked(item: MenuItem): Boolean + + fun onCabFinished(cab: AttachedCab): Boolean +} diff --git a/app/src/main/java/code/name/monkey/retromusic/interfaces/ICabHolder.kt b/app/src/main/java/code/name/monkey/retromusic/interfaces/ICabHolder.kt index 81052035..e04a4c32 100644 --- a/app/src/main/java/code/name/monkey/retromusic/interfaces/ICabHolder.kt +++ b/app/src/main/java/code/name/monkey/retromusic/interfaces/ICabHolder.kt @@ -14,9 +14,9 @@ */ package code.name.monkey.retromusic.interfaces -import com.afollestad.materialcab.MaterialCab +import com.afollestad.materialcab.attached.AttachedCab interface ICabHolder { - fun openCab(menuRes: Int, callback: MaterialCab.Callback): MaterialCab + fun openCab(menuRes: Int, callback: ICabCallback): AttachedCab } diff --git a/app/src/main/java/code/name/monkey/retromusic/lyrics/LrcEntry.java b/app/src/main/java/code/name/monkey/retromusic/lyrics/LrcEntry.java index 81682f8a..75fc63a4 100644 --- a/app/src/main/java/code/name/monkey/retromusic/lyrics/LrcEntry.java +++ b/app/src/main/java/code/name/monkey/retromusic/lyrics/LrcEntry.java @@ -19,13 +19,15 @@ import android.text.StaticLayout; import android.text.TextPaint; import android.text.TextUtils; -/** 一行歌词实体 */ +/** + * 一行歌词实体 + */ class LrcEntry implements Comparable { public static final int GRAVITY_CENTER = 0; public static final int GRAVITY_LEFT = 1; public static final int GRAVITY_RIGHT = 2; - private long time; - private String text; + private final long time; + private final String text; private String secondText; private StaticLayout staticLayout; /** 歌词距离视图顶部的距离 */ diff --git a/app/src/main/java/code/name/monkey/retromusic/lyrics/LrcView.java b/app/src/main/java/code/name/monkey/retromusic/lyrics/LrcView.java index a27f1a19..60f904bd 100644 --- a/app/src/main/java/code/name/monkey/retromusic/lyrics/LrcView.java +++ b/app/src/main/java/code/name/monkey/retromusic/lyrics/LrcView.java @@ -43,15 +43,17 @@ import java.util.List; import code.name.monkey.retromusic.BuildConfig; import code.name.monkey.retromusic.R; -/** 歌词 Created by wcy on 2015/11/9. */ +/** + * 歌词 Created by wcy on 2015/11/9. + */ @SuppressLint("StaticFieldLeak") public class LrcView extends View { private static final long ADJUST_DURATION = 100; private static final long TIMELINE_KEEP_TIME = 4 * DateUtils.SECOND_IN_MILLIS; - private List mLrcEntryList = new ArrayList<>(); - private TextPaint mLrcPaint = new TextPaint(); - private TextPaint mTimePaint = new TextPaint(); + private final List mLrcEntryList = new ArrayList<>(); + private final TextPaint mLrcPaint = new TextPaint(); + private final TextPaint mTimePaint = new TextPaint(); private Paint.FontMetrics mTimeFontMetrics; private Drawable mPlayDrawable; private float mDividerHeight; @@ -78,28 +80,30 @@ public class LrcView extends View { private boolean isTouching; private boolean isFling; private int mTextGravity; // 歌词显示位置,靠左/居中/靠右 - private Runnable hideTimelineRunnable = - new Runnable() { - @Override - public void run() { - if (hasLrc() && isShowTimeline) { - isShowTimeline = false; - smoothScrollTo(mCurrentLine); - } - } - }; - /** 手势监听器 */ - private GestureDetector.SimpleOnGestureListener mSimpleOnGestureListener = - new GestureDetector.SimpleOnGestureListener() { - @Override - public boolean onDown(MotionEvent e) { - if (hasLrc() && mOnPlayClickListener != null) { - mScroller.forceFinished(true); - removeCallbacks(hideTimelineRunnable); - isTouching = true; - isShowTimeline = true; - invalidate(); - return true; + private final Runnable hideTimelineRunnable = + new Runnable() { + @Override + public void run() { + if (hasLrc() && isShowTimeline) { + isShowTimeline = false; + smoothScrollTo(mCurrentLine); + } + } + }; + /** + * 手势监听器 + */ + private final GestureDetector.SimpleOnGestureListener mSimpleOnGestureListener = + new GestureDetector.SimpleOnGestureListener() { + @Override + public boolean onDown(MotionEvent e) { + if (hasLrc() && mOnPlayClickListener != null) { + mScroller.forceFinished(true); + removeCallbacks(hideTimelineRunnable); + isTouching = true; + isShowTimeline = true; + invalidate(); + return true; } return super.onDown(e); } diff --git a/app/src/main/java/code/name/monkey/retromusic/misc/CustomFragmentStatePagerAdapter.java b/app/src/main/java/code/name/monkey/retromusic/misc/CustomFragmentStatePagerAdapter.java index b8ff09bf..50ce6119 100644 --- a/app/src/main/java/code/name/monkey/retromusic/misc/CustomFragmentStatePagerAdapter.java +++ b/app/src/main/java/code/name/monkey/retromusic/misc/CustomFragmentStatePagerAdapter.java @@ -78,15 +78,17 @@ public abstract class CustomFragmentStatePagerAdapter extends PagerAdapter { private final FragmentManager mFragmentManager; private FragmentTransaction mCurTransaction = null; - private ArrayList mSavedState = new ArrayList(); - private ArrayList mFragments = new ArrayList(); + private final ArrayList mSavedState = new ArrayList(); + private final ArrayList mFragments = new ArrayList(); private Fragment mCurrentPrimaryItem = null; public CustomFragmentStatePagerAdapter(FragmentManager fm) { mFragmentManager = fm; } - /** Return the Fragment associated with a specified position. */ + /** + * Return the Fragment associated with a specified position. + */ public abstract Fragment getItem(int position); @Override diff --git a/app/src/main/java/code/name/monkey/retromusic/misc/UpdateToastMediaScannerCompletionListener.java b/app/src/main/java/code/name/monkey/retromusic/misc/UpdateToastMediaScannerCompletionListener.java index 1b6dd023..d749cf22 100644 --- a/app/src/main/java/code/name/monkey/retromusic/misc/UpdateToastMediaScannerCompletionListener.java +++ b/app/src/main/java/code/name/monkey/retromusic/misc/UpdateToastMediaScannerCompletionListener.java @@ -38,7 +38,7 @@ public class UpdateToastMediaScannerCompletionListener private final List toBeScanned; private int failed = 0; private int scanned = 0; - private Toast toast; + private final Toast toast; @SuppressLint("ShowToast") public UpdateToastMediaScannerCompletionListener(Activity activity, List toBeScanned) { diff --git a/app/src/main/java/code/name/monkey/retromusic/model/AbsCustomPlaylist.kt b/app/src/main/java/code/name/monkey/retromusic/model/AbsCustomPlaylist.kt index 3d62ca5c..ad5f3b1e 100644 --- a/app/src/main/java/code/name/monkey/retromusic/model/AbsCustomPlaylist.kt +++ b/app/src/main/java/code/name/monkey/retromusic/model/AbsCustomPlaylist.kt @@ -3,8 +3,8 @@ package code.name.monkey.retromusic.model import code.name.monkey.retromusic.repository.LastAddedRepository import code.name.monkey.retromusic.repository.SongRepository import code.name.monkey.retromusic.repository.TopPlayedRepository -import org.koin.core.KoinComponent -import org.koin.core.inject +import org.koin.core.component.KoinComponent +import org.koin.core.component.inject abstract class AbsCustomPlaylist( id: Long, diff --git a/app/src/main/java/code/name/monkey/retromusic/model/Artist.kt b/app/src/main/java/code/name/monkey/retromusic/model/Artist.kt index 12150c45..faf13398 100644 --- a/app/src/main/java/code/name/monkey/retromusic/model/Artist.kt +++ b/app/src/main/java/code/name/monkey/retromusic/model/Artist.kt @@ -30,9 +30,6 @@ data class Artist( } var name: String = "" - set(value) { - field = value - } get() { val name = if (isAlbumArtist) getAlbumArtistName() else getArtistName() diff --git a/app/src/main/java/code/name/monkey/retromusic/model/Playlist.kt b/app/src/main/java/code/name/monkey/retromusic/model/Playlist.kt index ca2e7d77..19ad179a 100644 --- a/app/src/main/java/code/name/monkey/retromusic/model/Playlist.kt +++ b/app/src/main/java/code/name/monkey/retromusic/model/Playlist.kt @@ -5,8 +5,8 @@ import android.os.Parcelable import code.name.monkey.retromusic.repository.RealPlaylistRepository import code.name.monkey.retromusic.util.MusicUtil import kotlinx.parcelize.Parcelize -import org.koin.core.KoinComponent -import org.koin.core.get +import org.koin.core.component.KoinComponent +import org.koin.core.component.get @Parcelize open class Playlist( diff --git a/app/src/main/java/code/name/monkey/retromusic/model/smartplaylist/HistoryPlaylist.kt b/app/src/main/java/code/name/monkey/retromusic/model/smartplaylist/HistoryPlaylist.kt index 9506314c..c3becdf6 100644 --- a/app/src/main/java/code/name/monkey/retromusic/model/smartplaylist/HistoryPlaylist.kt +++ b/app/src/main/java/code/name/monkey/retromusic/model/smartplaylist/HistoryPlaylist.kt @@ -4,7 +4,7 @@ import code.name.monkey.retromusic.App import code.name.monkey.retromusic.R import code.name.monkey.retromusic.model.Song import kotlinx.parcelize.Parcelize -import org.koin.core.KoinComponent +import org.koin.core.component.KoinComponent @Parcelize class HistoryPlaylist : AbsSmartPlaylist( diff --git a/app/src/main/java/code/name/monkey/retromusic/network/model/LastFmAlbum.java b/app/src/main/java/code/name/monkey/retromusic/network/model/LastFmAlbum.java index 481372ff..baef218f 100644 --- a/app/src/main/java/code/name/monkey/retromusic/network/model/LastFmAlbum.java +++ b/app/src/main/java/code/name/monkey/retromusic/network/model/LastFmAlbum.java @@ -112,7 +112,8 @@ public class LastFmAlbum { public class Tags { - @Expose private List tag = null; + @Expose + private final List tag = null; public List getTag() { return tag; diff --git a/app/src/main/java/code/name/monkey/retromusic/network/model/LastFmTrack.java b/app/src/main/java/code/name/monkey/retromusic/network/model/LastFmTrack.java index cdaed55e..8319b15d 100644 --- a/app/src/main/java/code/name/monkey/retromusic/network/model/LastFmTrack.java +++ b/app/src/main/java/code/name/monkey/retromusic/network/model/LastFmTrack.java @@ -84,7 +84,8 @@ public class LastFmTrack { } public static class Toptags { - @Expose private List tag = null; + @Expose + private final List tag = null; public List getTag() { return tag; diff --git a/app/src/main/java/code/name/monkey/retromusic/preferences/LibraryPreference.kt b/app/src/main/java/code/name/monkey/retromusic/preferences/LibraryPreference.kt index 1b5192b5..6f28e1ce 100644 --- a/app/src/main/java/code/name/monkey/retromusic/preferences/LibraryPreference.kt +++ b/app/src/main/java/code/name/monkey/retromusic/preferences/LibraryPreference.kt @@ -66,7 +66,7 @@ class LibraryPreferenceDialog : DialogFragment() { .setNeutralButton( R.string.reset_action ) { _, _ -> - categoryAdapter.categoryInfos = PreferenceUtil.defaultCategories + updateCategories(PreferenceUtil.defaultCategories) } .setNegativeButton(android.R.string.cancel, null) .setPositiveButton( R.string.done) { _, _ -> updateCategories(categoryAdapter.categoryInfos) } diff --git a/app/src/main/java/code/name/monkey/retromusic/providers/BlacklistStore.java b/app/src/main/java/code/name/monkey/retromusic/providers/BlacklistStore.java index bf274f66..90a89770 100644 --- a/app/src/main/java/code/name/monkey/retromusic/providers/BlacklistStore.java +++ b/app/src/main/java/code/name/monkey/retromusic/providers/BlacklistStore.java @@ -36,7 +36,7 @@ public class BlacklistStore extends SQLiteOpenHelper { public static final String DATABASE_NAME = "blacklist.db"; private static final int VERSION = 2; private static BlacklistStore sInstance = null; - private Context context; + private final Context context; public BlacklistStore(final Context context) { super(context, DATABASE_NAME, null, VERSION); diff --git a/app/src/main/java/code/name/monkey/retromusic/providers/SongPlayCountStore.java b/app/src/main/java/code/name/monkey/retromusic/providers/SongPlayCountStore.java index fd74c907..771f06e1 100644 --- a/app/src/main/java/code/name/monkey/retromusic/providers/SongPlayCountStore.java +++ b/app/src/main/java/code/name/monkey/retromusic/providers/SongPlayCountStore.java @@ -34,24 +34,27 @@ public class SongPlayCountStore extends SQLiteOpenHelper { private static final int VERSION = 3; // how many weeks worth of playback to track private static final int NUM_WEEKS = 52; - @Nullable private static SongPlayCountStore sInstance = null; + @Nullable + private static SongPlayCountStore sInstance = null; // interpolator curve applied for measuring the curve - @NonNull private static Interpolator sInterpolator = new AccelerateInterpolator(1.5f); + @NonNull + private static final Interpolator sInterpolator = new AccelerateInterpolator(1.5f); // how high to multiply the interpolation curve @SuppressWarnings("FieldCanBeLocal") - private static int INTERPOLATOR_HEIGHT = 50; + private static final int INTERPOLATOR_HEIGHT = 50; // how high the base value is. The ratio of the Height to Base is what really matters @SuppressWarnings("FieldCanBeLocal") - private static int INTERPOLATOR_BASE = 25; + private static final int INTERPOLATOR_BASE = 25; @SuppressWarnings("FieldCanBeLocal") - private static int ONE_WEEK_IN_MS = 1000 * 60 * 60 * 24 * 7; + private static final int ONE_WEEK_IN_MS = 1000 * 60 * 60 * 24 * 7; - @NonNull private static String WHERE_ID_EQUALS = SongPlayCountColumns.ID + "=?"; + @NonNull + private static final String WHERE_ID_EQUALS = SongPlayCountColumns.ID + "=?"; // number of weeks since epoch time - private int mNumberOfWeeksSinceEpoch; + private final int mNumberOfWeeksSinceEpoch; // used to track if we've walked through the db and updated all the rows private boolean mDatabaseUpdated; diff --git a/app/src/main/java/code/name/monkey/retromusic/repository/Repository.kt b/app/src/main/java/code/name/monkey/retromusic/repository/Repository.kt index 7eaab7dd..d8652bc3 100644 --- a/app/src/main/java/code/name/monkey/retromusic/repository/Repository.kt +++ b/app/src/main/java/code/name/monkey/retromusic/repository/Repository.kt @@ -19,6 +19,7 @@ import androidx.lifecycle.LiveData import androidx.lifecycle.Transformations import code.name.monkey.retromusic.* import code.name.monkey.retromusic.db.* +import code.name.monkey.retromusic.fragments.search.Filter import code.name.monkey.retromusic.model.* import code.name.monkey.retromusic.model.smartplaylist.NotPlayedPlaylist import code.name.monkey.retromusic.network.LastFMService @@ -51,7 +52,7 @@ interface Repository { suspend fun albumArtists(): List suspend fun fetchLegacyPlaylist(): List suspend fun fetchGenres(): List - suspend fun search(query: String?, filters: List): MutableList + suspend fun search(query: String?, filter: Filter): MutableList suspend fun getPlaylistSongs(playlist: Playlist): List suspend fun getGenre(genreId: Long): List suspend fun artistInfo(name: String, lang: String?, cache: String?): Result @@ -163,8 +164,8 @@ class RealRepository( override suspend fun allSongs(): List = songRepository.songs() - override suspend fun search(query: String?, filters: List): MutableList = - searchRepository.searchAll(context, query, filters) + override suspend fun search(query: String?, filter: Filter): MutableList = + searchRepository.searchAll(context, query, filter) override suspend fun getPlaylistSongs(playlist: Playlist): List = if (playlist is AbsCustomPlaylist) { diff --git a/app/src/main/java/code/name/monkey/retromusic/repository/SearchRepository.kt b/app/src/main/java/code/name/monkey/retromusic/repository/SearchRepository.kt index 68305890..4a1280d6 100644 --- a/app/src/main/java/code/name/monkey/retromusic/repository/SearchRepository.kt +++ b/app/src/main/java/code/name/monkey/retromusic/repository/SearchRepository.kt @@ -16,6 +16,7 @@ package code.name.monkey.retromusic.repository import android.content.Context import code.name.monkey.retromusic.R +import code.name.monkey.retromusic.fragments.search.Filter import code.name.monkey.retromusic.model.Album import code.name.monkey.retromusic.model.Artist import code.name.monkey.retromusic.model.Genre @@ -28,11 +29,11 @@ class RealSearchRepository( private val roomRepository: RoomRepository, private val genreRepository: GenreRepository, ) { - fun searchAll(context: Context, query: String?, filters: List): MutableList { + fun searchAll(context: Context, query: String?, filter: Filter): MutableList { val results = mutableListOf() - query?.let { searchString -> - val isAll = !filters.contains(true) - val songs: List = if (filters[0] || isAll) { + if (query.isNullOrEmpty()) return results + query.let { searchString -> + val songs: List = if (filter == Filter.SONGS || filter == Filter.NO_FILTER) { songRepository.songs(searchString) } else { emptyList() @@ -42,16 +43,17 @@ class RealSearchRepository( results.add(context.resources.getString(R.string.songs)) results.addAll(songs) } - val artists: List = if (filters[1] || isAll) { - artistRepository.artists(searchString) - } else { - emptyList() - } + val artists: List = + if (filter == Filter.ARTISTS || filter == Filter.NO_FILTER) { + artistRepository.artists(searchString) + } else { + emptyList() + } if (artists.isNotEmpty()) { results.add(context.resources.getString(R.string.artists)) results.addAll(artists) } - val albums: List = if (filters[2] || isAll) { + val albums: List = if (filter == Filter.ALBUMS || filter == Filter.NO_FILTER) { albumRepository.albums(searchString) } else { emptyList() @@ -60,16 +62,17 @@ class RealSearchRepository( results.add(context.resources.getString(R.string.albums)) results.addAll(albums) } - val albumArtists: List = if (filters[3] || isAll) { - artistRepository.albumArtists(searchString) - } else { - emptyList() - } + val albumArtists: List = + if (filter == Filter.ALBUM_ARTISTS || filter == Filter.NO_FILTER) { + artistRepository.albumArtists(searchString) + } else { + emptyList() + } if (albumArtists.isNotEmpty()) { results.add(context.resources.getString(R.string.album_artist)) results.addAll(albumArtists) } - val genres: List = if (filters[4] || isAll) { + val genres: List = if (filter == Filter.GENRES || filter == Filter.NO_FILTER) { genreRepository.genres().filter { genre -> genre.name.lowercase() .contains(searchString.lowercase()) @@ -82,13 +85,13 @@ class RealSearchRepository( results.addAll(genres) } /* val playlist = roomRepository.playlists().filter { playlist -> - playlist.playlistName.toLowerCase(Locale.getDefault()) - .contains(searchString.toLowerCase(Locale.getDefault())) - } - if (playlist.isNotEmpty()) { - results.add(context.getString(R.string.playlists)) - results.addAll(playlist) - }*/ + playlist.playlistName.toLowerCase(Locale.getDefault()) + .contains(searchString.toLowerCase(Locale.getDefault())) + } + if (playlist.isNotEmpty()) { + results.add(context.getString(R.string.playlists)) + results.addAll(playlist) + }*/ } return results } diff --git a/app/src/main/java/code/name/monkey/retromusic/repository/SortedCursor.java b/app/src/main/java/code/name/monkey/retromusic/repository/SortedCursor.java index 0d1cbb30..531af65d 100644 --- a/app/src/main/java/code/name/monkey/retromusic/repository/SortedCursor.java +++ b/app/src/main/java/code/name/monkey/retromusic/repository/SortedCursor.java @@ -34,7 +34,7 @@ public class SortedCursor extends AbstractCursor { // the map of external indices to internal indices private ArrayList mOrderedPositions; // this contains the ids that weren't found in the underlying cursor - private ArrayList mMissingValues; + private final ArrayList mMissingValues; // this contains the mapped cursor positions and afterwards the extra ids that weren't found private HashMap mMapCursorPositions; diff --git a/app/src/main/java/code/name/monkey/retromusic/repository/SortedLongCursor.java b/app/src/main/java/code/name/monkey/retromusic/repository/SortedLongCursor.java index 9884a32e..75996d4a 100644 --- a/app/src/main/java/code/name/monkey/retromusic/repository/SortedLongCursor.java +++ b/app/src/main/java/code/name/monkey/retromusic/repository/SortedLongCursor.java @@ -34,7 +34,7 @@ public class SortedLongCursor extends AbstractCursor { // the map of external indices to internal indices private ArrayList mOrderedPositions; // this contains the ids that weren't found in the underlying cursor - private ArrayList mMissingIds; + private final ArrayList mMissingIds; // this contains the mapped cursor positions and afterwards the extra ids that weren't found private HashMap mMapCursorPositions; diff --git a/app/src/main/java/code/name/monkey/retromusic/service/AudioFader.kt b/app/src/main/java/code/name/monkey/retromusic/service/AudioFader.kt index 3f8c0605..8979327c 100644 --- a/app/src/main/java/code/name/monkey/retromusic/service/AudioFader.kt +++ b/app/src/main/java/code/name/monkey/retromusic/service/AudioFader.kt @@ -1,50 +1,66 @@ package code.name.monkey.retromusic.service +import android.animation.Animator +import android.animation.ValueAnimator +import android.media.MediaPlayer +import androidx.core.animation.doOnEnd import code.name.monkey.retromusic.service.playback.Playback -import java.util.* +import code.name.monkey.retromusic.util.PreferenceUtil -class AudioFader( - private val player: Playback, - durationMillis: Long, - private val fadeIn: Boolean, - private val doOnEnd: Runnable -) { - val timer = Timer() - var volume = if (fadeIn) 0F else 1F - val maxVolume = if (fadeIn) 1F else 0F - private val volumeStep: Float = PERIOD / durationMillis.toFloat() +class AudioFader { + companion object { - fun start() { - timer.scheduleAtFixedRate( - object : TimerTask() { - override fun run() { - setVolume() - if (volume < 0 || volume > 1) { - player.setVolume(maxVolume) - stop() - doOnEnd.run() - } else { - player.setVolume(volume) - } + @JvmStatic + inline fun createFadeAnimator( + fadeIn: Boolean /* fadeIn -> true fadeOut -> false*/, + mediaPlayer: MediaPlayer, + crossinline endAction: (animator: Animator) -> Unit /* Code to run when Animator Ends*/ + ): Animator? { + val duration = PreferenceUtil.crossFadeDuration * 1000 + if (duration == 0) { + return null + } + val startValue = if (fadeIn) 0f else 1.0f + val endValue = if (fadeIn) 1.0f else 0f + return ValueAnimator.ofFloat(startValue, endValue).apply { + this.duration = duration.toLong() + addUpdateListener { animation: ValueAnimator -> + mediaPlayer.setVolume( + animation.animatedValue as Float, animation.animatedValue as Float + ) } - }, 0, PERIOD - ) - } + doOnEnd { + endAction(it) + // Set end values + mediaPlayer.setVolume(endValue, endValue) + } + } + } - fun stop() { - timer.purge() - timer.cancel() - } - - private fun setVolume() { - if (fadeIn) { - volume += volumeStep - } else { - volume -= volumeStep + @JvmStatic + fun startFadeAnimator( + playback: Playback, + fadeIn: Boolean /* fadeIn -> true fadeOut -> false*/, + callback: Runnable /* Code to run when Animator Ends*/ + ) { + val duration = PreferenceUtil.audioFadeDuration.toLong() + if (duration == 0L) { + callback.run() + return + } + val startValue = if (fadeIn) 0f else 1.0f + val endValue = if (fadeIn) 1.0f else 0f + val animator = ValueAnimator.ofFloat(startValue, endValue) + animator.duration = duration + animator.addUpdateListener { animation: ValueAnimator -> + playback.setVolume( + animation.animatedValue as Float + ) + } + animator.doOnEnd { + callback.run() + } + animator.start() } } - - companion object { - const val PERIOD = 100L - } } \ No newline at end of file diff --git a/app/src/main/java/code/name/monkey/retromusic/service/CrossFadePlayer.kt b/app/src/main/java/code/name/monkey/retromusic/service/CrossFadePlayer.kt index 6dda1e07..27eb8676 100644 --- a/app/src/main/java/code/name/monkey/retromusic/service/CrossFadePlayer.kt +++ b/app/src/main/java/code/name/monkey/retromusic/service/CrossFadePlayer.kt @@ -1,7 +1,6 @@ package code.name.monkey.retromusic.service import android.animation.Animator -import android.animation.ValueAnimator import android.content.Context import android.content.Intent import android.media.AudioAttributes @@ -9,17 +8,16 @@ import android.media.AudioManager import android.media.MediaPlayer import android.media.audiofx.AudioEffect import android.net.Uri -import android.os.Handler -import android.os.Message import android.os.PowerManager import android.widget.Toast -import androidx.core.animation.doOnEnd import code.name.monkey.retromusic.R import code.name.monkey.retromusic.helper.MusicPlayerRemote +import code.name.monkey.retromusic.service.AudioFader.Companion.createFadeAnimator import code.name.monkey.retromusic.service.playback.Playback import code.name.monkey.retromusic.service.playback.Playback.PlaybackCallbacks import code.name.monkey.retromusic.util.MusicUtil import code.name.monkey.retromusic.util.PreferenceUtil +import kotlinx.coroutines.* /** @author Prathamesh M */ @@ -37,12 +35,12 @@ class CrossFadePlayer(val context: Context) : Playback, MediaPlayer.OnCompletion private var player1 = MediaPlayer() private var player2 = MediaPlayer() private var durationListener = DurationListener() - private var trackEndHandledByCrossFade = false private var mIsInitialized = false private var hasDataSource: Boolean = false /* Whether first player has DataSource */ private var fadeInAnimator: Animator? = null private var fadeOutAnimator: Animator? = null private var callbacks: PlaybackCallbacks? = null + private var crossFadeDuration = PreferenceUtil.crossFadeDuration init { player1.setWakeMode(context, PowerManager.PARTIAL_WAKE_LOCK) @@ -123,7 +121,7 @@ class CrossFadePlayer(val context: Context) : Playback, MediaPlayer.OnCompletion // This has to run when queue is changed or song is changed manually by user fun sourceChangedByUser() { - this.hasDataSource = false + hasDataSource = false cancelFade() getCurrentPlayer()?.apply { if (isPlaying) stop() @@ -231,17 +229,7 @@ class CrossFadePlayer(val context: Context) : Playback, MediaPlayer.OnCompletion } override fun onCompletion(mp: MediaPlayer?) { - if (mp == getNextPlayer()) { - if (trackEndHandledByCrossFade) { - trackEndHandledByCrossFade = false - } else { - notifyTrackEnded() - } - } - } - - private fun notifyTrackEnded(){ - if (callbacks != null) { + if (mp == getCurrentPlayer()) { callbacks?.onTrackEnded() } } @@ -276,53 +264,23 @@ class CrossFadePlayer(val context: Context) : Playback, MediaPlayer.OnCompletion private fun fadeIn(mediaPlayer: MediaPlayer) { fadeInAnimator = createFadeAnimator(true, mediaPlayer) { - println("Fade In Completed") fadeInAnimator = null + durationListener.start() } fadeInAnimator?.start() } private fun fadeOut(mediaPlayer: MediaPlayer) { fadeOutAnimator = createFadeAnimator(false, mediaPlayer) { - println("Fade Out Completed") fadeOutAnimator = null + mediaPlayer.stop() } fadeOutAnimator?.start() } private fun cancelFade() { - fadeInAnimator?.cancel() - fadeOutAnimator?.cancel() fadeInAnimator = null fadeOutAnimator = null - getCurrentPlayer()?.setVolume(1f, 1f) - getNextPlayer()?.setVolume(0f, 0f) - } - - private fun createFadeAnimator( - fadeIn: Boolean /* fadeIn -> true fadeOut -> false*/, - mediaPlayer: MediaPlayer, - callback: Runnable /* Code to run when Animator Ends*/ - ): Animator? { - val duration = PreferenceUtil.crossFadeDuration * 1000 - if (duration == 0) { - return null - } - val startValue = if (fadeIn) 0f else 1.0f - val endValue = if (fadeIn) 1.0f else 0f - val animator = ValueAnimator.ofFloat(startValue, endValue) - animator.duration = duration.toLong() - animator.addUpdateListener { animation: ValueAnimator -> - mediaPlayer.setVolume( - animation.animatedValue as Float, animation.animatedValue as Float - ) - } - animator.doOnEnd { - callback.run() - // Set end values - mediaPlayer.setVolume(endValue, endValue) - } - return animator } override fun onError(mp: MediaPlayer?, what: Int, extra: Int): Boolean { @@ -347,34 +305,28 @@ class CrossFadePlayer(val context: Context) : Playback, MediaPlayer.OnCompletion NOT_SET } - inner class DurationListener : Handler() { + inner class DurationListener : CoroutineScope by crossFadeScope() { + + private var job: Job? = null fun start() { - nextRefresh() - } - - fun stop() { - removeMessages(DURATION_CHANGED) - } - - override fun handleMessage(msg: Message) { - super.handleMessage(msg) - if (msg.what == DURATION_CHANGED) { - nextRefresh() - onDurationUpdated(position(), duration()) + job?.cancel() + job = launch { + while (true) { + delay(250) + onDurationUpdated(position(), duration()) + } } } - private fun nextRefresh() { - val message = obtainMessage(DURATION_CHANGED) - removeMessages(DURATION_CHANGED) - sendMessageDelayed(message, 100) + fun stop() { + job?.cancel() } } fun onDurationUpdated(progress: Int, total: Int) { - if (total > 0 && (total - progress).div(1000) == PreferenceUtil.crossFadeDuration) { + if (total > 0 && (total - progress).div(1000) == crossFadeDuration) { getNextPlayer()?.let { player -> val nextSong = MusicPlayerRemote.nextSong if (nextSong != null) { @@ -396,11 +348,12 @@ class CrossFadePlayer(val context: Context) : Playback, MediaPlayer.OnCompletion } else { CurrentPlayer.PLAYER_ONE } - notifyTrackEnded() - trackEndHandledByCrossFade = true + callbacks?.onTrackEndedWithCrossfade() } - companion object { - private const val DURATION_CHANGED = 1 + override fun setCrossFadeDuration(duration: Int) { + crossFadeDuration = duration } } + +internal fun crossFadeScope(): CoroutineScope = CoroutineScope(Job() + Dispatchers.Main) \ No newline at end of file diff --git a/app/src/main/java/code/name/monkey/retromusic/service/MediaButtonIntentReceiver.kt b/app/src/main/java/code/name/monkey/retromusic/service/MediaButtonIntentReceiver.kt index d77a7d75..848b2110 100644 --- a/app/src/main/java/code/name/monkey/retromusic/service/MediaButtonIntentReceiver.kt +++ b/app/src/main/java/code/name/monkey/retromusic/service/MediaButtonIntentReceiver.kt @@ -63,14 +63,13 @@ class MediaButtonIntentReceiver : BroadcastReceiver() { when (msg.what) { MSG_HEADSET_DOUBLE_CLICK_TIMEOUT -> { val clickCount = msg.arg1 - val command: String? if (DEBUG) Log.v(TAG, "Handling headset click, count = $clickCount") - when (clickCount) { - 1 -> command = ACTION_TOGGLE_PAUSE - 2 -> command = ACTION_SKIP - 3 -> command = ACTION_REWIND - else -> command = null + val command = when (clickCount) { + 1 -> ACTION_TOGGLE_PAUSE + 2 -> ACTION_SKIP + 3 -> ACTION_REWIND + else -> null } if (command != null) { diff --git a/app/src/main/java/code/name/monkey/retromusic/service/MediaSessionCallback.kt b/app/src/main/java/code/name/monkey/retromusic/service/MediaSessionCallback.kt index c901a391..360c6dd6 100644 --- a/app/src/main/java/code/name/monkey/retromusic/service/MediaSessionCallback.kt +++ b/app/src/main/java/code/name/monkey/retromusic/service/MediaSessionCallback.kt @@ -17,6 +17,7 @@ package code.name.monkey.retromusic.service import android.content.Context import android.content.Intent import android.os.Bundle +import android.provider.MediaStore import android.support.v4.media.session.MediaSessionCompat import code.name.monkey.retromusic.auto.AutoMediaIDHelper import code.name.monkey.retromusic.helper.MusicPlayerRemote @@ -29,9 +30,8 @@ import code.name.monkey.retromusic.model.Song import code.name.monkey.retromusic.repository.* import code.name.monkey.retromusic.service.MusicService.* import code.name.monkey.retromusic.util.MusicUtil -import org.koin.core.KoinComponent -import org.koin.core.inject -import java.util.* +import org.koin.core.component.KoinComponent +import org.koin.core.component.inject /** @@ -56,8 +56,7 @@ class MediaSessionCallback( println(musicId) val itemId = musicId?.toLong() ?: -1 val songs: ArrayList = ArrayList() - val category = AutoMediaIDHelper.extractCategory(mediaId) - when (category) { + when (val category = AutoMediaIDHelper.extractCategory(mediaId)) { AutoMediaIDHelper.MEDIA_ID_MUSICS_BY_ALBUM -> { val album: Album = albumRepository.album(itemId) songs.addAll(album.songs) @@ -111,6 +110,43 @@ class MediaSessionCallback( musicService.play() } + override fun onPlayFromSearch(query: String?, extras: Bundle?) { + val songs = ArrayList() + if (query.isNullOrEmpty()) { + // The user provided generic string e.g. 'Play music' + // Build appropriate playlist queue + songs.addAll(songRepository.songs()) + } else { + // Build a queue based on songs that match "query" or "extras" param + val mediaFocus: String? = extras?.getString(MediaStore.EXTRA_MEDIA_FOCUS) + if (mediaFocus == MediaStore.Audio.Artists.ENTRY_CONTENT_TYPE) { + val artistQuery = extras.getString(MediaStore.EXTRA_MEDIA_ARTIST) + if (artistQuery != null) { + artistRepository.artists(artistQuery).forEach { + songs.addAll(it.songs) + } + } + } else if (mediaFocus == MediaStore.Audio.Albums.ENTRY_CONTENT_TYPE) { + val albumQuery = extras.getString(MediaStore.EXTRA_MEDIA_ALBUM) + if (albumQuery != null) { + albumRepository.albums(albumQuery).forEach { + songs.addAll(it.songs) + } + } + } + } + + if (songs.isEmpty()) { + // No focus found, search by query for song title + query?.also { + songs.addAll(songRepository.songs(it)) + } + } + + musicService.openQueue(songs, 0, true) + + musicService.play() + } override fun onPlay() { super.onPlay() diff --git a/app/src/main/java/code/name/monkey/retromusic/service/MultiPlayer.java b/app/src/main/java/code/name/monkey/retromusic/service/MultiPlayer.java index c0d7e023..f8e89f11 100644 --- a/app/src/main/java/code/name/monkey/retromusic/service/MultiPlayer.java +++ b/app/src/main/java/code/name/monkey/retromusic/service/MultiPlayer.java @@ -39,8 +39,9 @@ public class MultiPlayer private MediaPlayer mCurrentMediaPlayer = new MediaPlayer(); private MediaPlayer mNextMediaPlayer; - private Context context; - @Nullable private Playback.PlaybackCallbacks callbacks; + private final Context context; + @Nullable + private Playback.PlaybackCallbacks callbacks; private boolean mIsInitialized = false; @@ -321,4 +322,7 @@ public class MultiPlayer if (callbacks != null) callbacks.onTrackEnded(); } } + + @Override + public void setCrossFadeDuration(int duration) { } } \ No newline at end of file diff --git a/app/src/main/java/code/name/monkey/retromusic/service/MusicService.java b/app/src/main/java/code/name/monkey/retromusic/service/MusicService.java index 6ea0e2f4..95799879 100644 --- a/app/src/main/java/code/name/monkey/retromusic/service/MusicService.java +++ b/app/src/main/java/code/name/monkey/retromusic/service/MusicService.java @@ -14,6 +14,8 @@ package code.name.monkey.retromusic.service; +import static android.support.v4.media.MediaBrowserCompat.MediaItem.FLAG_PLAYABLE; +import static androidx.media.MediaBrowserServiceCompat.BrowserRoot.EXTRA_RECENT; import static org.koin.java.KoinJavaComponent.get; import static code.name.monkey.retromusic.ConstantsKt.ALBUM_ART_ON_LOCK_SCREEN; import static code.name.monkey.retromusic.ConstantsKt.BLURRED_ALBUM_ART; @@ -21,6 +23,7 @@ import static code.name.monkey.retromusic.ConstantsKt.CLASSIC_NOTIFICATION; import static code.name.monkey.retromusic.ConstantsKt.COLORED_NOTIFICATION; import static code.name.monkey.retromusic.ConstantsKt.CROSS_FADE_DURATION; import static code.name.monkey.retromusic.ConstantsKt.TOGGLE_HEADSET; +import static code.name.monkey.retromusic.service.AudioFader.startFadeAnimator; import android.app.PendingIntent; import android.appwidget.AppWidgetManager; @@ -49,6 +52,7 @@ import android.os.PowerManager; import android.os.Process; import android.provider.MediaStore; import android.support.v4.media.MediaBrowserCompat; +import android.support.v4.media.MediaDescriptionCompat; import android.support.v4.media.MediaMetadataCompat; import android.support.v4.media.session.MediaSessionCompat; import android.support.v4.media.session.PlaybackStateCompat; @@ -66,6 +70,7 @@ import com.bumptech.glide.RequestBuilder; import com.bumptech.glide.request.target.SimpleTarget; import java.util.ArrayList; +import java.util.Collections; import java.util.List; import java.util.Objects; import java.util.Random; @@ -97,12 +102,14 @@ import code.name.monkey.retromusic.util.MusicUtil; import code.name.monkey.retromusic.util.PackageValidator; import code.name.monkey.retromusic.util.PreferenceUtil; import code.name.monkey.retromusic.util.RetroUtil; +import code.name.monkey.retromusic.volume.AudioVolumeObserver; +import code.name.monkey.retromusic.volume.OnAudioVolumeChangedListener; /** * @author Karim Abou Zeid (kabouzeid), Andrew Neal */ public class MusicService extends MediaBrowserServiceCompat - implements SharedPreferences.OnSharedPreferenceChangeListener, Playback.PlaybackCallbacks { + implements SharedPreferences.OnSharedPreferenceChangeListener, Playback.PlaybackCallbacks, OnAudioVolumeChangedListener { public static final String TAG = MusicService.class.getSimpleName(); public static final String RETRO_MUSIC_PACKAGE_NAME = "code.name.monkey.retromusic"; @@ -180,15 +187,15 @@ public class MusicService extends MediaBrowserServiceCompat public int position = -1; - private AppWidgetBig appWidgetBig = AppWidgetBig.Companion.getInstance(); + private final AppWidgetBig appWidgetBig = AppWidgetBig.Companion.getInstance(); - private AppWidgetCard appWidgetCard = AppWidgetCard.Companion.getInstance(); + private final AppWidgetCard appWidgetCard = AppWidgetCard.Companion.getInstance(); - private AppWidgetClassic appWidgetClassic = AppWidgetClassic.Companion.getInstance(); + private final AppWidgetClassic appWidgetClassic = AppWidgetClassic.Companion.getInstance(); - private AppWidgetSmall appWidgetSmall = AppWidgetSmall.Companion.getInstance(); + private final AppWidgetSmall appWidgetSmall = AppWidgetSmall.Companion.getInstance(); - private AppWidgetText appWidgetText = AppWidgetText.Companion.getInstance(); + private final AppWidgetText appWidgetText = AppWidgetText.Companion.getInstance(); private final BroadcastReceiver widgetIntentReceiver = new BroadcastReceiver() { @@ -222,15 +229,15 @@ public class MusicService extends MediaBrowserServiceCompat } } }; - private AudioManager audioManager; - private IntentFilter becomingNoisyReceiverIntentFilter = - new IntentFilter(AudioManager.ACTION_AUDIO_BECOMING_NOISY); - private boolean becomingNoisyReceiverRegistered; - private IntentFilter bluetoothConnectedIntentFilter = - new IntentFilter(BluetoothDevice.ACTION_ACL_CONNECTED); - private boolean bluetoothConnectedRegistered = false; - private IntentFilter headsetReceiverIntentFilter = new IntentFilter(Intent.ACTION_HEADSET_PLUG); - private boolean headsetReceiverRegistered = false; + private AudioManager audioManager; + private final IntentFilter becomingNoisyReceiverIntentFilter = + new IntentFilter(AudioManager.ACTION_AUDIO_BECOMING_NOISY); + private boolean becomingNoisyReceiverRegistered; + private final IntentFilter bluetoothConnectedIntentFilter = + new IntentFilter(BluetoothDevice.ACTION_ACL_CONNECTED); + private boolean bluetoothConnectedRegistered = false; + private final IntentFilter headsetReceiverIntentFilter = new IntentFilter(Intent.ACTION_HEADSET_PLUG); + private boolean headsetReceiverRegistered = false; private MediaSessionCompat mediaSession; private ContentObserver mediaStoreObserver; private HandlerThread musicPlayerHandlerThread; @@ -238,6 +245,7 @@ public class MusicService extends MediaBrowserServiceCompat private List originalPlayingQueue = new ArrayList<>(); private List playingQueue = new ArrayList<>(); private boolean pausedByTransientLossOfFocus; + private AudioVolumeObserver audioVolumeObserver = null; private final BroadcastReceiver becomingNoisyReceiver = new BroadcastReceiver() { @@ -283,59 +291,59 @@ public class MusicService extends MediaBrowserServiceCompat private HandlerThread queueSaveHandlerThread; private boolean queuesRestored; private int repeatMode; - private int shuffleMode; - private SongPlayCountHelper songPlayCountHelper = new SongPlayCountHelper(); - private final BroadcastReceiver bluetoothReceiver = - new BroadcastReceiver() { - @Override - public void onReceive(final Context context, final Intent intent) { - String action = intent.getAction(); - if (action != null) { - if (BluetoothDevice.ACTION_ACL_CONNECTED.equals(action) - && PreferenceUtil.INSTANCE.isBluetoothSpeaker()) { - if (VERSION.SDK_INT >= VERSION_CODES.M) { - if (getAudioManager().getDevices(AudioManager.GET_DEVICES_OUTPUTS).length > 0) { + private int shuffleMode; + private final SongPlayCountHelper songPlayCountHelper = new SongPlayCountHelper(); + private final BroadcastReceiver bluetoothReceiver = + new BroadcastReceiver() { + @Override + public void onReceive(final Context context, final Intent intent) { + String action = intent.getAction(); + if (action != null) { + if (BluetoothDevice.ACTION_ACL_CONNECTED.equals(action) + && PreferenceUtil.INSTANCE.isBluetoothSpeaker()) { + if (VERSION.SDK_INT >= VERSION_CODES.M) { + if (getAudioManager().getDevices(AudioManager.GET_DEVICES_OUTPUTS).length > 0) { play(); - } - } else { - if (getAudioManager().isBluetoothA2dpOn()) { - play(); - } - } - } } - } - }; - private PhoneStateListener phoneStateListener = - new PhoneStateListener() { - @Override - public void onCallStateChanged(int state, String incomingNumber) { - switch (state) { - case TelephonyManager.CALL_STATE_IDLE: - // Not in call: Play music - play(); - break; - case TelephonyManager.CALL_STATE_RINGING: - case TelephonyManager.CALL_STATE_OFFHOOK: - // A call is dialing, active or on hold - pause(); - break; - default: + } else { + if (getAudioManager().isBluetoothA2dpOn()) { + play(); } - super.onCallStateChanged(state, incomingNumber); + } } - }; - private BroadcastReceiver headsetReceiver = - new BroadcastReceiver() { - @Override - public void onReceive(Context context, Intent intent) { - String action = intent.getAction(); - if (action != null) { - if (Intent.ACTION_HEADSET_PLUG.equals(action)) { - int state = intent.getIntExtra("state", -1); - switch (state) { - case 0: - pause(); + } + } + }; + private final PhoneStateListener phoneStateListener = + new PhoneStateListener() { + @Override + public void onCallStateChanged(int state, String incomingNumber) { + switch (state) { + case TelephonyManager.CALL_STATE_IDLE: + // Not in call: Play music + play(); + break; + case TelephonyManager.CALL_STATE_RINGING: + case TelephonyManager.CALL_STATE_OFFHOOK: + // A call is dialing, active or on hold + pause(); + break; + default: + } + super.onCallStateChanged(state, incomingNumber); + } + }; + private final BroadcastReceiver headsetReceiver = + new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + String action = intent.getAction(); + if (action != null) { + if (Intent.ACTION_HEADSET_PLUG.equals(action)) { + int state = intent.getIntExtra("state", -1); + switch (state) { + case 0: + pause(); break; case 1: play(); @@ -348,7 +356,6 @@ public class MusicService extends MediaBrowserServiceCompat private ThrottledSeekHandler throttledSeekHandler; private Handler uiThreadHandler; private PowerManager.WakeLock wakeLock; - private AudioFader fader; private static Bitmap copy(Bitmap bitmap) { Bitmap.Config config = bitmap.getConfig(); @@ -446,6 +453,9 @@ public class MusicService extends MediaBrowserServiceCompat .registerContentObserver( MediaStore.Audio.Playlists.INTERNAL_CONTENT_URI, true, mediaStoreObserver); + audioVolumeObserver = new AudioVolumeObserver(this); + audioVolumeObserver.register(AudioManager.STREAM_MUSIC, this); + PreferenceUtil.INSTANCE.registerOnSharedPreferenceChangedListener(this); restoreState(); @@ -491,6 +501,23 @@ public class MusicService extends MediaBrowserServiceCompat wakeLock.acquire(milli); } + boolean pausedByZeroVolume; + + @Override + public void onAudioVolumeChanged(int currentVolume, int maxVolume) { + if (PreferenceUtil.INSTANCE.isPauseOnZeroVolume()) { + if (isPlaying() && currentVolume < 1) { + pause(); + System.out.println("Paused"); + pausedByZeroVolume = true; + } else if (pausedByZeroVolume && currentVolume >= 1) { + System.out.println("Played"); + play(); + pausedByZeroVolume = false; + } + } + } + public void addSong(int position, Song song) { playingQueue.add(position, song); originalPlayingQueue.add(position, song); @@ -558,10 +585,10 @@ public class MusicService extends MediaBrowserServiceCompat } public Song getNextSong() { - if (!isLastTrack() || getRepeatMode() == REPEAT_MODE_THIS) { - return getSongAt(getNextPosition(false)); - } else { + if (isLastTrack() && getRepeatMode() == REPEAT_MODE_NONE) { return null; + } else { + return getSongAt(getNextPosition(false)); } } @@ -804,18 +831,46 @@ public class MusicService extends MediaBrowserServiceCompat @Override public BrowserRoot onGetRoot(@NonNull String clientPackageName, int clientUid, @Nullable Bundle rootHints) { + // Check origin to ensure we're not allowing any arbitrary app to browse app contents if (!mPackageValidator.isKnownCaller(clientPackageName, clientUid)) { // Request from an untrusted package: return an empty browser root return new BrowserRoot(AutoMediaIDHelper.MEDIA_ID_EMPTY_ROOT, null); + } else { + /** + * By default return the browsable root. Treat the EXTRA_RECENT flag as a special case + * and return the recent root instead. + */ + boolean isRecentRequest = false; + if (rootHints != null) { + isRecentRequest = rootHints.getBoolean(EXTRA_RECENT); + } + String browserRootPath; + if (isRecentRequest) { + browserRootPath = AutoMediaIDHelper.RECENT_ROOT; + } else { + browserRootPath = AutoMediaIDHelper.MEDIA_ID_ROOT; + } + return new BrowserRoot(browserRootPath, null); } - - return new BrowserRoot(AutoMediaIDHelper.MEDIA_ID_ROOT, null); } @Override public void onLoadChildren(@NonNull String parentId, @NonNull MediaBrowserServiceCompat.Result> result) { - result.sendResult(mMusicProvider.getChildren(parentId, getResources())); + if (parentId.equals(AutoMediaIDHelper.RECENT_ROOT)) { + Song song = getCurrentSong(); + MediaBrowserCompat.MediaItem mediaItem = new MediaBrowserCompat.MediaItem( + new MediaDescriptionCompat.Builder() + .setMediaId(String.valueOf(song.getId())) + .setTitle(song.getTitle()) + .setSubtitle(song.getArtistName()) + .setIconUri(MusicUtil.getMediaStoreAlbumCoverUri(song.getAlbumId())) + .build(), FLAG_PLAYABLE + ); + result.sendResult(Collections.singletonList(mediaItem)); + } else { + result.sendResult(mMusicProvider.getChildren(parentId, getResources())); + } } @Override @@ -827,6 +882,8 @@ public class MusicService extends MediaBrowserServiceCompat boolean wasPlaying = isPlaying(); /* Switch to MultiPlayer if Crossfade duration is 0 and Playback is not an instance of MultiPlayer */ + if (playback != null) + playback.setCrossFadeDuration(PreferenceUtil.INSTANCE.getCrossFadeDuration()); if (!(playback instanceof MultiPlayer) && PreferenceUtil.INSTANCE.getCrossFadeDuration() == 0) { if (playback != null) { playback.release(); @@ -926,6 +983,13 @@ public class MusicService extends MediaBrowserServiceCompat playerHandler.sendEmptyMessage(TRACK_ENDED); } + @Override + public void onTrackEndedWithCrossfade() { + trackEndedByCrossfade = true; + acquireWakeLock(30000); + playerHandler.sendEmptyMessage(TRACK_ENDED); + } + @Override public void onTrackWentToNext() { playerHandler.sendEmptyMessage(TRACK_WENT_TO_NEXT); @@ -981,16 +1045,13 @@ public class MusicService extends MediaBrowserServiceCompat public void pause() { pausedByTransientLossOfFocus = false; if (playback != null && playback.isPlaying()) { - if (fader != null) { - fader.stop(); - } - fader = new AudioFader(playback, PreferenceUtil.INSTANCE.getAudioFadeDuration(), false, () -> { - if (playback != null && playback.isPlaying()) { + startFadeAnimator(playback, false, () -> { + //Code to run when Animator Ends + if (playback != null) { playback.pause(); - notifyChange(PLAY_STATE_CHANGED); } + notifyChange(PLAY_STATE_CHANGED); }); - fader.start(); } } @@ -1013,10 +1074,8 @@ public class MusicService extends MediaBrowserServiceCompat if (MusicPlayerRemote.INSTANCE.isCasting()) { return; } - if (fader != null) { - fader.stop(); - } - fader = new AudioFader(playback, PreferenceUtil.INSTANCE.getAudioFadeDuration(), false, () -> { + startFadeAnimator(playback, true, () -> { + // Code when Animator Ends if (!becomingNoisyReceiverRegistered) { registerReceiver(becomingNoisyReceiver, becomingNoisyReceiverIntentFilter); becomingNoisyReceiverRegistered = true; @@ -1031,9 +1090,9 @@ public class MusicService extends MediaBrowserServiceCompat playerHandler.removeMessages(DUCK); playerHandler.sendEmptyMessage(UNDUCK); }); + //Start Playback with Animator playback.start(); notifyChange(PLAY_STATE_CHANGED); - fader.start(); } } } else { @@ -1131,7 +1190,7 @@ public class MusicService extends MediaBrowserServiceCompat notifyChange(QUEUE_CHANGED); } - public void removeSong(@NonNull Song song) { + public void removeSongImpl(@NonNull Song song) { for (int i = 0; i < playingQueue.size(); i++) { if (playingQueue.get(i).getId() == song.getId()) { playingQueue.remove(i); @@ -1143,6 +1202,17 @@ public class MusicService extends MediaBrowserServiceCompat originalPlayingQueue.remove(i); } } + } + + public void removeSong(@NonNull Song song) { + removeSongImpl(song); + notifyChange(QUEUE_CHANGED); + } + + public void removeSongs(@NonNull List songs) { + for (Song song : songs) { + removeSongImpl(song); + } notifyChange(QUEUE_CHANGED); } @@ -1550,4 +1620,4 @@ public class MusicService extends MediaBrowserServiceCompat return MusicService.this; } } -} +} \ No newline at end of file diff --git a/app/src/main/java/code/name/monkey/retromusic/service/PlaybackHandler.java b/app/src/main/java/code/name/monkey/retromusic/service/PlaybackHandler.java index 82e90cfb..44b4cdfc 100644 --- a/app/src/main/java/code/name/monkey/retromusic/service/PlaybackHandler.java +++ b/app/src/main/java/code/name/monkey/retromusic/service/PlaybackHandler.java @@ -80,7 +80,7 @@ class PlaybackHandler extends Handler { case TRACK_WENT_TO_NEXT: if (service.pendingQuit - || service.getRepeatMode() == REPEAT_MODE_NONE && service.isLastTrack()) { + || service.getRepeatMode() == REPEAT_MODE_NONE && service.isLastTrack()) { service.pause(); service.seek(0); if (service.pendingQuit) { @@ -96,10 +96,9 @@ class PlaybackHandler extends Handler { break; case TRACK_ENDED: - service.trackEndedByCrossfade = true; // if there is a timer finished, don't continue if (service.pendingQuit - || service.getRepeatMode() == REPEAT_MODE_NONE && service.isLastTrack()) { + || service.getRepeatMode() == REPEAT_MODE_NONE && service.isLastTrack()) { service.notifyChange(PLAY_STATE_CHANGED); service.seek(0); if (service.pendingQuit) { @@ -169,4 +168,4 @@ class PlaybackHandler extends Handler { break; } } -} +} \ No newline at end of file diff --git a/app/src/main/java/code/name/monkey/retromusic/service/notification/PlayingNotificationImpl.kt b/app/src/main/java/code/name/monkey/retromusic/service/notification/PlayingNotificationImpl.kt index bdc0b07d..b188f4b9 100644 --- a/app/src/main/java/code/name/monkey/retromusic/service/notification/PlayingNotificationImpl.kt +++ b/app/src/main/java/code/name/monkey/retromusic/service/notification/PlayingNotificationImpl.kt @@ -43,7 +43,7 @@ import com.bumptech.glide.request.target.Target import com.bumptech.glide.request.transition.Transition import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.launch -import org.koin.core.KoinComponent +import org.koin.core.component.KoinComponent class PlayingNotificationImpl : PlayingNotification(), KoinComponent { private var target: Target? = null diff --git a/app/src/main/java/code/name/monkey/retromusic/service/playback/Playback.kt b/app/src/main/java/code/name/monkey/retromusic/service/playback/Playback.kt index 7925bef9..08c81bb0 100644 --- a/app/src/main/java/code/name/monkey/retromusic/service/playback/Playback.kt +++ b/app/src/main/java/code/name/monkey/retromusic/service/playback/Playback.kt @@ -47,9 +47,13 @@ interface Playback { fun setAudioSessionId(sessionId: Int): Boolean + fun setCrossFadeDuration(duration: Int) + interface PlaybackCallbacks { fun onTrackWentToNext() fun onTrackEnded() + + fun onTrackEndedWithCrossfade() } } diff --git a/app/src/main/java/code/name/monkey/retromusic/util/AutoGeneratedPlaylistBitmap.java b/app/src/main/java/code/name/monkey/retromusic/util/AutoGeneratedPlaylistBitmap.java index cf2e38f3..aea4fa1d 100644 --- a/app/src/main/java/code/name/monkey/retromusic/util/AutoGeneratedPlaylistBitmap.java +++ b/app/src/main/java/code/name/monkey/retromusic/util/AutoGeneratedPlaylistBitmap.java @@ -146,7 +146,7 @@ public class AutoGeneratedPlaylistBitmap { try { return Glide.with(context) .asBitmap() - .load(MusicUtil.INSTANCE.getMediaStoreAlbumCoverUri(id)) + .load(MusicUtil.getMediaStoreAlbumCoverUri(id)) .submit(200, 200) .get(); } catch (Exception e) { diff --git a/app/src/main/java/code/name/monkey/retromusic/util/BackupUtil.kt b/app/src/main/java/code/name/monkey/retromusic/util/BackupUtil.kt new file mode 100644 index 00000000..c3f00af0 --- /dev/null +++ b/app/src/main/java/code/name/monkey/retromusic/util/BackupUtil.kt @@ -0,0 +1,30 @@ +package code.name.monkey.retromusic.util + +import android.content.Context +import android.content.Intent +import android.widget.Toast +import androidx.core.content.FileProvider +import java.io.File + +object BackupUtil { + fun createShareFileIntent(file: File, context: Context): Intent? { + return try { + Intent().setAction(Intent.ACTION_SEND).putExtra( + Intent.EXTRA_STREAM, + FileProvider.getUriForFile( + context, + context.applicationContext.packageName, + file + ) + ).addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION).setType("*/*") + } catch (e: IllegalArgumentException) { + e.printStackTrace() + Toast.makeText( + context, + "Could not share this file.", + Toast.LENGTH_SHORT + ).show() + Intent() + } + } +} \ No newline at end of file diff --git a/app/src/main/java/code/name/monkey/retromusic/util/CalendarUtil.java b/app/src/main/java/code/name/monkey/retromusic/util/CalendarUtil.java index b396df9a..28a75cc1 100644 --- a/app/src/main/java/code/name/monkey/retromusic/util/CalendarUtil.java +++ b/app/src/main/java/code/name/monkey/retromusic/util/CalendarUtil.java @@ -22,7 +22,7 @@ public class CalendarUtil { private static final long MS_PER_MINUTE = 60 * 1000; private static final long MS_PER_DAY = 24 * 60 * MS_PER_MINUTE; - private Calendar calendar; + private final Calendar calendar; public CalendarUtil() { this.calendar = Calendar.getInstance(); diff --git a/app/src/main/java/code/name/monkey/retromusic/util/ColorAnimUtil.kt b/app/src/main/java/code/name/monkey/retromusic/util/ColorAnimUtil.kt new file mode 100644 index 00000000..81cb66de --- /dev/null +++ b/app/src/main/java/code/name/monkey/retromusic/util/ColorAnimUtil.kt @@ -0,0 +1,19 @@ +package code.name.monkey.retromusic.util + +import android.animation.ArgbEvaluator +import android.animation.ValueAnimator + +class ColorAnimUtil { + companion object { + fun createColorAnimator( + fromColor: Int, + toColor: Int, + mDuration: Long = 300 + ): ValueAnimator { + return ValueAnimator.ofInt(fromColor, toColor).apply { + setEvaluator(ArgbEvaluator()) + duration = mDuration + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/code/name/monkey/retromusic/util/FileUtil.java b/app/src/main/java/code/name/monkey/retromusic/util/FileUtil.java index 0023ec5f..78b77898 100644 --- a/app/src/main/java/code/name/monkey/retromusic/util/FileUtil.java +++ b/app/src/main/java/code/name/monkey/retromusic/util/FileUtil.java @@ -28,14 +28,19 @@ import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileFilter; import java.io.FileInputStream; +import java.io.FileReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; +import java.util.ArrayList; import java.util.Collection; import java.util.Collections; +import java.util.HashSet; import java.util.LinkedList; import java.util.List; +import java.util.StringTokenizer; +import code.name.monkey.retromusic.adapter.Storage; import code.name.monkey.retromusic.model.Song; import code.name.monkey.retromusic.repository.RealSongRepository; import code.name.monkey.retromusic.repository.SortedCursor; @@ -258,4 +263,76 @@ public final class FileUtil { return file.getAbsoluteFile(); } } + + // https://github.com/DrKLO/Telegram/blob/ab221dafadbc17459d78d9ea3e643ae18e934b16/TMessagesProj/src/main/java/org/telegram/ui/Components/ChatAttachAlertDocumentLayout.java#L939 + public static ArrayList listRoots() { + ArrayList storageItems = new ArrayList<>(); + HashSet paths = new HashSet<>(); + String defaultPath = Environment.getExternalStorageDirectory().getPath(); + String defaultPathState = Environment.getExternalStorageState(); + if (defaultPathState.equals(Environment.MEDIA_MOUNTED) || defaultPathState.equals(Environment.MEDIA_MOUNTED_READ_ONLY)) { + Storage ext = new Storage(); + if (Environment.isExternalStorageRemovable()) { + ext.title = "SD Card"; + } else { + ext.title = "Internal Storage"; + } + ext.file = Environment.getExternalStorageDirectory(); + storageItems.add(ext); + paths.add(defaultPath); + } + + BufferedReader bufferedReader = null; + try { + bufferedReader = new BufferedReader(new FileReader("/proc/mounts")); + String line; + while ((line = bufferedReader.readLine()) != null) { + if (line.contains("vfat") || line.contains("/mnt")) { + StringTokenizer tokens = new StringTokenizer(line, " "); + tokens.nextToken(); + String path = tokens.nextToken(); + if (paths.contains(path)) { + continue; + } + if (line.contains("/dev/block/vold")) { + if (!line.contains("/mnt/secure") && !line.contains("/mnt/asec") && !line.contains("/mnt/obb") && !line.contains("/dev/mapper") && !line.contains("tmpfs")) { + if (!new File(path).isDirectory()) { + int index = path.lastIndexOf('/'); + if (index != -1) { + String newPath = "/storage/" + path.substring(index + 1); + if (new File(newPath).isDirectory()) { + path = newPath; + } + } + } + paths.add(path); + try { + Storage item = new Storage(); + if (path.toLowerCase().contains("sd")) { + item.title = "SD Card"; + } else { + item.title = "External Storage"; + } + item.file = new File(path); + storageItems.add(item); + } catch (Exception e) { + e.printStackTrace(); + } + } + } + } + } + } catch (Exception e) { + e.printStackTrace(); + } finally { + if (bufferedReader != null) { + try { + bufferedReader.close(); + } catch (Exception e) { + e.printStackTrace(); + } + } + } + return storageItems; + } } diff --git a/app/src/main/java/code/name/monkey/retromusic/util/MusicUtil.kt b/app/src/main/java/code/name/monkey/retromusic/util/MusicUtil.kt index ed89e34d..9971afb7 100644 --- a/app/src/main/java/code/name/monkey/retromusic/util/MusicUtil.kt +++ b/app/src/main/java/code/name/monkey/retromusic/util/MusicUtil.kt @@ -25,7 +25,6 @@ import code.name.monkey.retromusic.model.Playlist import code.name.monkey.retromusic.model.Song import code.name.monkey.retromusic.model.lyrics.AbsSynchronizedLyrics import code.name.monkey.retromusic.repository.RealPlaylistRepository -import code.name.monkey.retromusic.repository.RealSongRepository import code.name.monkey.retromusic.repository.Repository import code.name.monkey.retromusic.repository.SongRepository import code.name.monkey.retromusic.service.MusicService @@ -35,8 +34,8 @@ import kotlinx.coroutines.launch import kotlinx.coroutines.withContext import org.jaudiotagger.audio.AudioFileIO import org.jaudiotagger.tag.FieldKey -import org.koin.core.KoinComponent -import org.koin.core.get +import org.koin.core.component.KoinComponent +import org.koin.core.component.get import java.io.File import java.io.IOException import java.util.* @@ -183,6 +182,7 @@ object MusicUtil : KoinComponent { return lyrics } + @JvmStatic fun getMediaStoreAlbumCoverUri(albumId: Long): Uri { val sArtworkUri = Uri.parse("content://media/external/audio/albumart") return ContentUris.withAppendedId(sArtworkUri, albumId) @@ -473,16 +473,7 @@ object MusicUtil : KoinComponent { null, null ) if (cursor != null) { - // Step 1: Remove selected tracks from the current playlist, as well - // as from the album art cache - cursor.moveToFirst() - while (!cursor.isAfterLast) { - val id = cursor.getLong(BaseColumns._ID) - val song: Song = RealSongRepository(context).song(id) - removeFromQueue(song) - cursor.moveToNext() - } - + removeFromQueue(songs) // Step 2: Remove files from card cursor.moveToFirst() diff --git a/app/src/main/java/code/name/monkey/retromusic/util/NavigationUtil.java b/app/src/main/java/code/name/monkey/retromusic/util/NavigationUtil.java deleted file mode 100755 index f0d324c7..00000000 --- a/app/src/main/java/code/name/monkey/retromusic/util/NavigationUtil.java +++ /dev/null @@ -1,110 +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.util; - -import android.app.Activity; -import android.app.ActivityOptions; -import android.content.ActivityNotFoundException; -import android.content.Context; -import android.content.Intent; -import android.media.audiofx.AudioEffect; -import android.widget.Toast; - -import androidx.annotation.NonNull; -import androidx.core.app.ActivityCompat; - -import org.jetbrains.annotations.NotNull; - -import code.name.monkey.retromusic.R; -import code.name.monkey.retromusic.activities.DriveModeActivity; -import code.name.monkey.retromusic.activities.LicenseActivity; -import code.name.monkey.retromusic.activities.LyricsActivity; -import code.name.monkey.retromusic.activities.PlayingQueueActivity; -import code.name.monkey.retromusic.activities.PurchaseActivity; -import code.name.monkey.retromusic.activities.SupportDevelopmentActivity; -import code.name.monkey.retromusic.activities.UserInfoActivity; -import code.name.monkey.retromusic.activities.WhatsNewActivity; -import code.name.monkey.retromusic.activities.bugreport.BugReportActivity; -import code.name.monkey.retromusic.helper.MusicPlayerRemote; - -public class NavigationUtil { - - public static void bugReport(@NonNull Activity activity) { - ActivityCompat.startActivity(activity, new Intent(activity, BugReportActivity.class), null); - } - - public static void goToLyrics(@NonNull Activity activity) { - Intent intent = new Intent(activity, LyricsActivity.class); - ActivityCompat.startActivity(activity, intent, null); - } - - public static void goToOpenSource(@NonNull Activity activity) { - ActivityCompat.startActivity(activity, new Intent(activity, LicenseActivity.class), null); - } - - public static void goToPlayingQueue(@NonNull Activity activity) { - Intent intent = new Intent(activity, PlayingQueueActivity.class); - ActivityCompat.startActivity(activity, intent, null); - } - - public static void goToProVersion(@NonNull Context context) { - ActivityCompat.startActivity(context, new Intent(context, PurchaseActivity.class), null); - } - - public static void goToSupportDevelopment(@NonNull Activity activity) { - ActivityCompat.startActivity( - activity, new Intent(activity, SupportDevelopmentActivity.class), null); - } - - public static void goToUserInfo( - @NonNull Activity activity, @NonNull ActivityOptions activityOptions) { - ActivityCompat.startActivity( - activity, new Intent(activity, UserInfoActivity.class), activityOptions.toBundle()); - } - - public static void gotoDriveMode(@NotNull final Activity activity) { - ActivityCompat.startActivity(activity, new Intent(activity, DriveModeActivity.class), null); - } - - public static void gotoWhatNews(@NonNull Activity activity) { - ActivityCompat.startActivity(activity, new Intent(activity, WhatsNewActivity.class), null); - } - - public static void openEqualizer(@NonNull final Activity activity) { - stockEqalizer(activity); - } - - private static void stockEqalizer(@NonNull Activity activity) { - final int sessionId = MusicPlayerRemote.INSTANCE.getAudioSessionId(); - if (sessionId == AudioEffect.ERROR_BAD_VALUE) { - Toast.makeText( - activity, activity.getResources().getString(R.string.no_audio_ID), Toast.LENGTH_LONG) - .show(); - } else { - try { - final Intent effects = new Intent(AudioEffect.ACTION_DISPLAY_AUDIO_EFFECT_CONTROL_PANEL); - effects.putExtra(AudioEffect.EXTRA_AUDIO_SESSION, sessionId); - effects.putExtra(AudioEffect.EXTRA_CONTENT_TYPE, AudioEffect.CONTENT_TYPE_MUSIC); - activity.startActivityForResult(effects, 0); - } catch (@NonNull final ActivityNotFoundException notFound) { - Toast.makeText( - activity, - activity.getResources().getString(R.string.no_equalizer), - Toast.LENGTH_SHORT) - .show(); - } - } - } -} diff --git a/app/src/main/java/code/name/monkey/retromusic/util/NavigationUtil.kt b/app/src/main/java/code/name/monkey/retromusic/util/NavigationUtil.kt new file mode 100755 index 00000000..71af0911 --- /dev/null +++ b/app/src/main/java/code/name/monkey/retromusic/util/NavigationUtil.kt @@ -0,0 +1,107 @@ +/* + * 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.util + +import android.app.Activity +import android.content.ActivityNotFoundException +import android.content.Context +import android.content.Intent +import android.media.audiofx.AudioEffect +import android.widget.Toast +import androidx.core.app.ActivityCompat +import androidx.navigation.findNavController +import code.name.monkey.retromusic.R +import code.name.monkey.retromusic.activities.* +import code.name.monkey.retromusic.activities.bugreport.BugReportActivity +import code.name.monkey.retromusic.helper.MusicPlayerRemote.audioSessionId +import com.google.android.material.bottomsheet.BottomSheetBehavior + +object NavigationUtil { + fun bugReport(activity: Activity) { + ActivityCompat.startActivity( + activity, + Intent(activity, BugReportActivity::class.java), + null + ) + } + + fun goToOpenSource(activity: Activity) { + ActivityCompat.startActivity(activity, Intent(activity, LicenseActivity::class.java), null) + } + + fun goToLyrics(activity: Activity) { + if (activity !is MainActivity) return + activity.apply { + //Hide Bottom Bar First, else Bottom Sheet doesn't collapse fully + setBottomNavVisibility(false) + if (getBottomSheetBehavior().state == BottomSheetBehavior.STATE_EXPANDED) { + collapsePanel() + } + + findNavController(R.id.fragment_container).navigate( + R.id.lyrics_fragment + ) + } + } + + fun goToProVersion(context: Context) { + ActivityCompat.startActivity(context, Intent(context, PurchaseActivity::class.java), null) + } + + fun goToSupportDevelopment(activity: Activity) { + ActivityCompat.startActivity( + activity, Intent(activity, SupportDevelopmentActivity::class.java), null + ) + } + + fun gotoDriveMode(activity: Activity) { + ActivityCompat.startActivity( + activity, + Intent(activity, DriveModeActivity::class.java), + null + ) + } + + fun gotoWhatNews(activity: Activity) { + ActivityCompat.startActivity(activity, Intent(activity, WhatsNewActivity::class.java), null) + } + + fun openEqualizer(activity: Activity) { + stockEqualizer(activity) + } + + private fun stockEqualizer(activity: Activity) { + val sessionId = audioSessionId + if (sessionId == AudioEffect.ERROR_BAD_VALUE) { + Toast.makeText( + activity, activity.resources.getString(R.string.no_audio_ID), Toast.LENGTH_LONG + ) + .show() + } else { + try { + val effects = Intent(AudioEffect.ACTION_DISPLAY_AUDIO_EFFECT_CONTROL_PANEL) + effects.putExtra(AudioEffect.EXTRA_AUDIO_SESSION, sessionId) + effects.putExtra(AudioEffect.EXTRA_CONTENT_TYPE, AudioEffect.CONTENT_TYPE_MUSIC) + activity.startActivityForResult(effects, 0) + } catch (notFound: ActivityNotFoundException) { + Toast.makeText( + activity, + activity.resources.getString(R.string.no_equalizer), + Toast.LENGTH_SHORT + ) + .show() + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/code/name/monkey/retromusic/util/PackageValidator.kt b/app/src/main/java/code/name/monkey/retromusic/util/PackageValidator.kt index d75bd28b..352fb1e4 100644 --- a/app/src/main/java/code/name/monkey/retromusic/util/PackageValidator.kt +++ b/app/src/main/java/code/name/monkey/retromusic/util/PackageValidator.kt @@ -34,6 +34,8 @@ import org.xmlpull.v1.XmlPullParserException import java.io.IOException import java.security.MessageDigest import java.security.NoSuchAlgorithmException +import java.util.* +import kotlin.collections.LinkedHashMap /** * Validates that the calling package is authorized to browse a [MediaBrowserServiceCompat]. @@ -274,7 +276,8 @@ class PackageValidator( var eventType = parser.next() while (eventType != XmlResourceParser.END_TAG) { val isRelease = parser.getAttributeBooleanValue(null, "release", false) - val signature = parser.nextText().replace(WHITESPACE_REGEX, "").toLowerCase() + val signature = parser.nextText().replace(WHITESPACE_REGEX, "") + .lowercase(Locale.getDefault()) callerSignatures += KnownSignature(signature, isRelease) eventType = parser.next() @@ -319,14 +322,14 @@ class PackageValidator( } private data class KnownCallerInfo( - internal val name: String, - internal val packageName: String, - internal val signatures: MutableSet + val name: String, + val packageName: String, + val signatures: MutableSet ) private data class KnownSignature( - internal val signature: String, - internal val release: Boolean + val signature: String, + val release: Boolean ) /** @@ -334,11 +337,11 @@ class PackageValidator( * to see if it's a known caller. */ private data class CallerPackageInfo( - internal val name: String, - internal val packageName: String, - internal val uid: Int, - internal val signature: String?, - internal val permissions: Set + val name: String, + val packageName: String, + val uid: Int, + val signature: String?, + val permissions: Set ) } diff --git a/app/src/main/java/code/name/monkey/retromusic/util/PreferenceUtil.kt b/app/src/main/java/code/name/monkey/retromusic/util/PreferenceUtil.kt index 62a24375..8a8e3041 100644 --- a/app/src/main/java/code/name/monkey/retromusic/util/PreferenceUtil.kt +++ b/app/src/main/java/code/name/monkey/retromusic/util/PreferenceUtil.kt @@ -269,6 +269,8 @@ object PreferenceUtil { BLURRED_ALBUM_ART, false ) + val blurAmount get() = sharedPreferences.getInt(NEW_BLUR_AMOUNT, 25) + val isCarouselEffect get() = sharedPreferences.getBoolean( CAROUSEL_EFFECT, false @@ -542,7 +544,7 @@ object PreferenceUtil { var startDirectory: File get() { - val folderPath = FoldersFragment.getDefaultStartDirectory().path + val folderPath = FoldersFragment.defaultStartDirectory.path val filePath: String = sharedPreferences.getStringOrDefault(START_DIRECTORY, folderPath) return File(filePath) } @@ -608,8 +610,10 @@ object PreferenceUtil { val isWhiteList: Boolean get() = sharedPreferences.getBoolean(WHITELIST_MUSIC, false) - var crossFadeDuration + val crossFadeDuration get() = sharedPreferences .getInt(CROSS_FADE_DURATION, 0) - set(value) = sharedPreferences.edit { putInt(CROSS_FADE_DURATION, value) } + + val materialYou + get() = sharedPreferences.getBoolean(MATERIAL_YOU, false) } diff --git a/app/src/main/java/code/name/monkey/retromusic/util/RetroColorUtil.java b/app/src/main/java/code/name/monkey/retromusic/util/RetroColorUtil.java index 061eb17f..a07cc1fb 100644 --- a/app/src/main/java/code/name/monkey/retromusic/util/RetroColorUtil.java +++ b/app/src/main/java/code/name/monkey/retromusic/util/RetroColorUtil.java @@ -22,14 +22,20 @@ import android.graphics.Color; import androidx.annotation.ColorInt; import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import androidx.core.content.ContextCompat; import androidx.palette.graphics.Palette; +import org.jetbrains.annotations.NotNull; + import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.List; +import code.name.monkey.appthemehelper.ThemeStore; import code.name.monkey.appthemehelper.util.ColorUtil; +import code.name.monkey.appthemehelper.util.VersionUtils; +import code.name.monkey.retromusic.R; public class RetroColorUtil { public static int desaturateColor(int color, float ratio) { @@ -205,6 +211,25 @@ public class RetroColorUtil { return color; } + @ColorInt + public static int shiftBackgroundColor(@ColorInt int backgroundColor) { + int color = backgroundColor; + if (ColorUtil.INSTANCE.isColorLight(color)) { + color = ColorUtil.INSTANCE.shiftColor(color, 0.5F); + } else { + color = ColorUtil.INSTANCE.shiftColor(color, 1.5F); + } + return color; + } + + public static int getMD3AccentColor(@NotNull Context context) { + if (VersionUtils.hasS()) { + return ContextCompat.getColor(context, R.color.m3_accent_color); + } else { + return ThemeStore.Companion.accentColor(context); + } + } + private static class SwatchComparator implements Comparator { private static SwatchComparator sInstance; diff --git a/app/src/main/java/code/name/monkey/retromusic/util/RetroUtil.java b/app/src/main/java/code/name/monkey/retromusic/util/RetroUtil.java old mode 100755 new mode 100644 index f06aade5..81c03ae7 --- a/app/src/main/java/code/name/monkey/retromusic/util/RetroUtil.java +++ b/app/src/main/java/code/name/monkey/retromusic/util/RetroUtil.java @@ -66,10 +66,10 @@ public class RetroUtil { @NonNull public static Bitmap createBitmap(@NonNull Drawable drawable, float sizeMultiplier) { Bitmap bitmap = - Bitmap.createBitmap( - (int) (drawable.getIntrinsicWidth() * sizeMultiplier), - (int) (drawable.getIntrinsicHeight() * sizeMultiplier), - Bitmap.Config.ARGB_8888); + Bitmap.createBitmap( + (int) (drawable.getIntrinsicWidth() * sizeMultiplier), + (int) (drawable.getIntrinsicHeight() * sizeMultiplier), + Bitmap.Config.ARGB_8888); Canvas c = new Canvas(bitmap); drawable.setBounds(0, 0, c.getWidth(), c.getHeight()); drawable.draw(c); @@ -106,9 +106,21 @@ public class RetroUtil { public static int getStatusBarHeight() { int result = 0; int resourceId = - App.Companion.getContext() - .getResources() - .getIdentifier("status_bar_height", "dimen", "android"); + App.Companion.getContext() + .getResources() + .getIdentifier("status_bar_height", "dimen", "android"); + if (resourceId > 0) { + result = App.Companion.getContext().getResources().getDimensionPixelSize(resourceId); + } + return result; + } + + public static int getNavigationBarHeight() { + int result = 0; + int resourceId = + App.Companion.getContext() + .getResources() + .getIdentifier("navigation_bar_height", "dimen", "android"); if (resourceId > 0) { result = App.Companion.getContext().getResources().getDimensionPixelSize(resourceId); } @@ -117,23 +129,23 @@ public class RetroUtil { @Nullable public static Drawable getTintedVectorDrawable( - @NonNull Context context, @DrawableRes int id, @ColorInt int color) { + @NonNull Context context, @DrawableRes int id, @ColorInt int color) { return TintHelper.createTintedDrawable( - getVectorDrawable(context.getResources(), id, context.getTheme()), color); + getVectorDrawable(context.getResources(), id, context.getTheme()), color); } @Nullable public static Drawable getTintedVectorDrawable( - @NonNull Resources res, - @DrawableRes int resId, - @Nullable Resources.Theme theme, - @ColorInt int color) { + @NonNull Resources res, + @DrawableRes int resId, + @Nullable Resources.Theme theme, + @ColorInt int color) { return TintHelper.createTintedDrawable(getVectorDrawable(res, resId, theme), color); } @Nullable public static Drawable getVectorDrawable( - @NonNull Resources res, @DrawableRes int resId, @Nullable Resources.Theme theme) { + @NonNull Resources res, @DrawableRes int resId, @Nullable Resources.Theme theme) { if (Build.VERSION.SDK_INT >= 21) { return res.getDrawable(resId, theme); } @@ -145,7 +157,7 @@ public class RetroUtil { View currentFocus = activity.getCurrentFocus(); if (currentFocus != null) { InputMethodManager inputMethodManager = - (InputMethodManager) activity.getSystemService(Activity.INPUT_METHOD_SERVICE); + (InputMethodManager) activity.getSystemService(Activity.INPUT_METHOD_SERVICE); if (inputMethodManager != null) { inputMethodManager.hideSoftInputFromWindow(currentFocus.getWindowToken(), 0); } @@ -159,11 +171,11 @@ public class RetroUtil { return true; case "only_wifi": final ConnectivityManager connectivityManager = - (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); + (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); NetworkInfo netInfo = connectivityManager.getActiveNetworkInfo(); return netInfo != null - && netInfo.getType() == ConnectivityManager.TYPE_WIFI - && netInfo.isConnectedOrConnecting(); + && netInfo.getType() == ConnectivityManager.TYPE_WIFI + && netInfo.isConnectedOrConnecting(); case "never": default: return false; @@ -172,7 +184,7 @@ public class RetroUtil { public static boolean isLandscape() { return App.Companion.getContext().getResources().getConfiguration().orientation - == Configuration.ORIENTATION_LANDSCAPE; + == Configuration.ORIENTATION_LANDSCAPE; } @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1) @@ -183,7 +195,7 @@ public class RetroUtil { public static boolean isTablet() { return App.Companion.getContext().getResources().getConfiguration().smallestScreenWidthDp - >= 600; + >= 600; } public static void openUrl(@NonNull Activity context, @NonNull String str) { @@ -196,16 +208,16 @@ public class RetroUtil { public static void setAllowDrawUnderNavigationBar(Window window) { window.setNavigationBarColor(Color.TRANSPARENT); window - .getDecorView() - .setSystemUiVisibility( - Build.VERSION.SDK_INT >= Build.VERSION_CODES.O - ? View.SYSTEM_UI_FLAG_LAYOUT_STABLE - | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN - | View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR - | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION - : View.SYSTEM_UI_FLAG_LAYOUT_STABLE - | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN - | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION); + .getDecorView() + .setSystemUiVisibility( + Build.VERSION.SDK_INT >= Build.VERSION_CODES.O + ? View.SYSTEM_UI_FLAG_LAYOUT_STABLE + | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN + | View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR + | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION + : View.SYSTEM_UI_FLAG_LAYOUT_STABLE + | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN + | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION); } public static void setAllowDrawUnderStatusBar(@NonNull Window window) { @@ -213,6 +225,7 @@ public class RetroUtil { .getDecorView() .setSystemUiVisibility( View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN); + window.setStatusBarColor(Color.TRANSPARENT); } public static String getIpAddress(boolean useIPv4) { diff --git a/app/src/main/java/code/name/monkey/retromusic/util/SwipeAndDragHelper.java b/app/src/main/java/code/name/monkey/retromusic/util/SwipeAndDragHelper.java index 2fdf3827..589f8d4b 100644 --- a/app/src/main/java/code/name/monkey/retromusic/util/SwipeAndDragHelper.java +++ b/app/src/main/java/code/name/monkey/retromusic/util/SwipeAndDragHelper.java @@ -22,7 +22,7 @@ import androidx.recyclerview.widget.RecyclerView; public class SwipeAndDragHelper extends ItemTouchHelper.Callback { - private ActionCompletionContract contract; + private final ActionCompletionContract contract; public SwipeAndDragHelper(@NonNull ActionCompletionContract contract) { this.contract = contract; diff --git a/app/src/main/java/code/name/monkey/retromusic/util/ThemedFastScroller.kt b/app/src/main/java/code/name/monkey/retromusic/util/ThemedFastScroller.kt index f062c023..2019fa6a 100644 --- a/app/src/main/java/code/name/monkey/retromusic/util/ThemedFastScroller.kt +++ b/app/src/main/java/code/name/monkey/retromusic/util/ThemedFastScroller.kt @@ -33,7 +33,7 @@ object ThemedFastScroller { fastScrollerBuilder.useMd2Style() fastScrollerBuilder.setPopupStyle { popupText -> PopupStyles.MD2.accept(popupText) - popupText.background = PopupBackground(context) + popupText.background = PopupBackground(context, color) popupText.setTextColor(textColor) } diff --git a/app/src/main/java/code/name/monkey/retromusic/util/color/MediaNotificationProcessor.java b/app/src/main/java/code/name/monkey/retromusic/util/color/MediaNotificationProcessor.java index 6f17f982..80233a62 100644 --- a/app/src/main/java/code/name/monkey/retromusic/util/color/MediaNotificationProcessor.java +++ b/app/src/main/java/code/name/monkey/retromusic/util/color/MediaNotificationProcessor.java @@ -77,7 +77,7 @@ public class MediaNotificationProcessor { private static final String TAG = "ColorPicking"; private float[] mFilteredBackgroundHsl = null; - private Palette.Filter mBlackWhiteFilter = + private final Palette.Filter mBlackWhiteFilter = (rgb, hsl) -> !isWhiteOrBlack(hsl); private boolean mIsLowPriority; private int backgroundColor; @@ -85,7 +85,7 @@ public class MediaNotificationProcessor { private int primaryTextColor; private int actionBarColor; private Drawable drawable; - private Context context; + private final Context context; public MediaNotificationProcessor(Context context, Drawable drawable) { this.context = context; diff --git a/app/src/main/java/code/name/monkey/retromusic/util/theme/ThemeManager.kt b/app/src/main/java/code/name/monkey/retromusic/util/theme/ThemeManager.kt index 106ecb99..b95e5ddf 100644 --- a/app/src/main/java/code/name/monkey/retromusic/util/theme/ThemeManager.kt +++ b/app/src/main/java/code/name/monkey/retromusic/util/theme/ThemeManager.kt @@ -5,6 +5,7 @@ import androidx.annotation.StyleRes import androidx.appcompat.app.AppCompatDelegate import code.name.monkey.retromusic.R import code.name.monkey.retromusic.extensions.generalThemeValue +import code.name.monkey.retromusic.util.PreferenceUtil import code.name.monkey.retromusic.util.theme.ThemeMode.* object ThemeManager { @@ -12,11 +13,16 @@ object ThemeManager { @StyleRes fun getThemeResValue( context: Context - ): Int = when (context.generalThemeValue) { - LIGHT -> R.style.Theme_RetroMusic_Light - DARK -> R.style.Theme_RetroMusic_Base - BLACK -> R.style.Theme_RetroMusic_Black - AUTO -> R.style.Theme_RetroMusic_FollowSystem + ): Int = + if (PreferenceUtil.materialYou) { + R.style.Theme_RetroMusic_MD3 + } else { + when (context.generalThemeValue) { + LIGHT -> R.style.Theme_RetroMusic_Light + DARK -> R.style.Theme_RetroMusic_Base + BLACK -> R.style.Theme_RetroMusic_Black + AUTO -> R.style.Theme_RetroMusic_FollowSystem + } } fun getNightMode( diff --git a/app/src/main/java/code/name/monkey/retromusic/views/AccentIcon.kt b/app/src/main/java/code/name/monkey/retromusic/views/AccentIcon.kt index c48c3c41..74a08e83 100644 --- a/app/src/main/java/code/name/monkey/retromusic/views/AccentIcon.kt +++ b/app/src/main/java/code/name/monkey/retromusic/views/AccentIcon.kt @@ -4,7 +4,7 @@ import android.content.Context import android.content.res.ColorStateList import android.util.AttributeSet import androidx.appcompat.widget.AppCompatImageView -import code.name.monkey.retromusic.extensions.accentColor +import code.name.monkey.appthemehelper.ThemeStore class AccentIcon @JvmOverloads constructor( context: Context, @@ -12,6 +12,6 @@ class AccentIcon @JvmOverloads constructor( defStyleAttr: Int = -1 ) : AppCompatImageView(context, attrs, defStyleAttr) { init { - imageTintList = ColorStateList.valueOf(accentColor()) + imageTintList = ColorStateList.valueOf(ThemeStore.accentColor(context)) } } \ No newline at end of file diff --git a/app/src/main/java/code/name/monkey/retromusic/views/BottomNavigationBarTinted.kt b/app/src/main/java/code/name/monkey/retromusic/views/BottomNavigationBarTinted.kt index 91cb5d53..14b96c28 100644 --- a/app/src/main/java/code/name/monkey/retromusic/views/BottomNavigationBarTinted.kt +++ b/app/src/main/java/code/name/monkey/retromusic/views/BottomNavigationBarTinted.kt @@ -17,16 +17,13 @@ package code.name.monkey.retromusic.views import android.content.Context import android.content.res.ColorStateList import android.graphics.drawable.ColorDrawable -import android.graphics.drawable.RippleDrawable import android.util.AttributeSet -import androidx.core.content.ContextCompat import code.name.monkey.appthemehelper.ThemeStore import code.name.monkey.appthemehelper.util.ATHUtil import code.name.monkey.appthemehelper.util.ColorUtil import code.name.monkey.appthemehelper.util.NavigationViewUtil import code.name.monkey.retromusic.R import code.name.monkey.retromusic.util.PreferenceUtil -import code.name.monkey.retromusic.util.RippleUtils import com.google.android.material.bottomnavigation.BottomNavigationView class BottomNavigationBarTinted @JvmOverloads constructor( @@ -38,33 +35,26 @@ class BottomNavigationBarTinted @JvmOverloads constructor( init { labelVisibilityMode = PreferenceUtil.tabTitleMode - val iconColor = ATHUtil.resolveColor(context, android.R.attr.colorControlNormal) - val accentColor = ThemeStore.accentColor(context) - NavigationViewUtil.setItemIconColors( - this, - ColorUtil.withAlpha(iconColor, 0.5f), - accentColor - ) - NavigationViewUtil.setItemTextColors( - this, - ColorUtil.withAlpha(iconColor, 0.5f), - accentColor - ) - itemBackground = RippleDrawable( - RippleUtils.convertToRippleDrawableColor( - ColorStateList.valueOf( - ThemeStore.accentColor(context).addAlpha() - ) - ), - ContextCompat.getDrawable(context, R.drawable.bottom_navigation_item_background), - ContextCompat.getDrawable(context, R.drawable.bottom_navigation_item_background_mask) - ) - setOnApplyWindowInsetsListener(null) - //itemRippleColor = ColorStateList.valueOf(accentColor) - background = ColorDrawable(ATHUtil.resolveColor(context, R.attr.colorSurface)) + if (!PreferenceUtil.materialYou) { + val iconColor = ATHUtil.resolveColor(context, android.R.attr.colorControlNormal) + val accentColor = ThemeStore.accentColor(context) + NavigationViewUtil.setItemIconColors( + this, + ColorUtil.withAlpha(iconColor, 0.5f), + accentColor + ) + NavigationViewUtil.setItemTextColors( + this, + ColorUtil.withAlpha(iconColor, 0.5f), + accentColor + ) + itemRippleColor = ColorStateList.valueOf(accentColor.addAlpha(0.08F)) + background = ColorDrawable(ATHUtil.resolveColor(context, R.attr.bottomSheetTint)) + itemActiveIndicatorColor = ColorStateList.valueOf(accentColor.addAlpha(0.12F)) + } } } -fun Int.addAlpha(): Int { - return ColorUtil.withAlpha(this, 0.12f) +fun Int.addAlpha(alpha: Float): Int { + return ColorUtil.withAlpha(this, alpha) } diff --git a/app/src/main/java/code/name/monkey/retromusic/views/PopupBackground.java b/app/src/main/java/code/name/monkey/retromusic/views/PopupBackground.java index 1c4c814e..f1df03d1 100644 --- a/app/src/main/java/code/name/monkey/retromusic/views/PopupBackground.java +++ b/app/src/main/java/code/name/monkey/retromusic/views/PopupBackground.java @@ -32,7 +32,6 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.core.graphics.drawable.DrawableCompat; -import code.name.monkey.appthemehelper.ThemeStore; import code.name.monkey.retromusic.R; public class PopupBackground extends Drawable { @@ -47,10 +46,10 @@ public class PopupBackground extends Drawable { @NonNull private final Matrix mTempMatrix = new Matrix(); - public PopupBackground(@NonNull Context context) { + public PopupBackground(@NonNull Context context, int color) { mPaint = new Paint(); mPaint.setAntiAlias(true); - mPaint.setColor(ThemeStore.Companion.accentColor(context)); + mPaint.setColor(color); mPaint.setStyle(Paint.Style.FILL); Resources resources = context.getResources(); mPaddingStart = resources.getDimensionPixelOffset(R.dimen.afs_md2_popup_padding_start); diff --git a/app/src/main/java/code/name/monkey/retromusic/views/SeekArc.java b/app/src/main/java/code/name/monkey/retromusic/views/SeekArc.java index 029ef96e..ccf533c4 100644 --- a/app/src/main/java/code/name/monkey/retromusic/views/SeekArc.java +++ b/app/src/main/java/code/name/monkey/retromusic/views/SeekArc.java @@ -38,14 +38,16 @@ import code.name.monkey.retromusic.R; public class SeekArc extends View { private static final String TAG = SeekArc.class.getSimpleName(); - private static int INVALID_PROGRESS_VALUE = -1; + private static final int INVALID_PROGRESS_VALUE = -1; // The initial rotational offset -90 means we start at 12 o'clock private final int mAngleOffset = -90; private Paint mArcPaint; // Internal variables private int mArcRadius = 0; - private RectF mArcRect = new RectF(); - /** The Width of the background arc for the SeekArc */ + private final RectF mArcRect = new RectF(); + /** + * The Width of the background arc for the SeekArc + */ private int mArcWidth = 2; /** Will the progress increase clockwise or anti-clockwise */ private boolean mClockwise = true; diff --git a/app/src/main/res/drawable-v31/rect_selector.xml b/app/src/main/res/drawable-v31/rect_selector.xml new file mode 100644 index 00000000..31422ac9 --- /dev/null +++ b/app/src/main/res/drawable-v31/rect_selector.xml @@ -0,0 +1,34 @@ + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_backup.xml b/app/src/main/res/drawable/ic_backup.xml new file mode 100644 index 00000000..9faa168d --- /dev/null +++ b/app/src/main/res/drawable/ic_backup.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/drawable/ic_drag_handle.xml b/app/src/main/res/drawable/ic_drag_handle.xml new file mode 100644 index 00000000..88fe5b40 --- /dev/null +++ b/app/src/main/res/drawable/ic_drag_handle.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/drawable/ic_restore.xml b/app/src/main/res/drawable/ic_restore.xml new file mode 100644 index 00000000..d4fa3ee6 --- /dev/null +++ b/app/src/main/res/drawable/ic_restore.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/drawable/popup_background.xml b/app/src/main/res/drawable/popup_background.xml index 4a4471df..995f1a73 100644 --- a/app/src/main/res/drawable/popup_background.xml +++ b/app/src/main/res/drawable/popup_background.xml @@ -1,5 +1,5 @@ - + \ No newline at end of file diff --git a/app/src/main/res/drawable/rect_selector.xml b/app/src/main/res/drawable/rect_selector.xml index b4770919..35a2089f 100644 --- a/app/src/main/res/drawable/rect_selector.xml +++ b/app/src/main/res/drawable/rect_selector.xml @@ -17,7 +17,7 @@ - + @@ -25,7 +25,7 @@ - + diff --git a/app/src/main/res/drawable/shadow_blur_theme.xml b/app/src/main/res/drawable/shadow_blur_theme.xml index fad19a4d..2304e611 100644 --- a/app/src/main/res/drawable/shadow_blur_theme.xml +++ b/app/src/main/res/drawable/shadow_blur_theme.xml @@ -2,7 +2,7 @@ \ No newline at end of file diff --git a/app/src/main/res/drawable/switch_thumb.xml b/app/src/main/res/drawable/switch_thumb.xml new file mode 100644 index 00000000..c5f48c5c --- /dev/null +++ b/app/src/main/res/drawable/switch_thumb.xml @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/switch_track.xml b/app/src/main/res/drawable/switch_track.xml new file mode 100644 index 00000000..cb59bb7e --- /dev/null +++ b/app/src/main/res/drawable/switch_track.xml @@ -0,0 +1,12 @@ + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout-land/activity_album_tag_editor.xml b/app/src/main/res/layout-land/activity_album_tag_editor.xml index 8b13076b..43b99d6a 100644 --- a/app/src/main/res/layout-land/activity_album_tag_editor.xml +++ b/app/src/main/res/layout-land/activity_album_tag_editor.xml @@ -10,13 +10,6 @@ android:layout_height="match_parent" android:orientation="vertical"> - - - - - + tools:ignore="UnusedAttribute" + android:transitionGroup="true"> - - + app:layout_constraintTop_toBottomOf="@id/toolbar_container"> + app:layout_constraintTop_toBottomOf="@id/toolbar_container"> - - @@ -34,13 +30,13 @@ android:layout_height="0dp" android:layout_marginStart="16dp" android:layout_marginTop="8dp" - android:layout_marginEnd="16dp" + android:layout_marginBottom="@dimen/mini_player_height" android:transitionName="@string/transition_artist_image" - app:cardCornerRadius="24dp" + app:cardCornerRadius="@dimen/m3_card_large_radius" + app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintDimensionRatio="1:1" - app:layout_constraintEnd_toStartOf="@+id/content" app:layout_constraintStart_toStartOf="parent" - app:layout_constraintTop_toBottomOf="@id/toolbarContainer"> + app:layout_constraintTop_toBottomOf="@id/toolbar_container"> + app:layout_constraintTop_toBottomOf="@id/toolbar_container"> + android:fitsSystemWindows="true"> @@ -71,7 +70,7 @@ android:id="@+id/contentContainer" android:layout_width="match_parent" android:layout_height="wrap_content" - android:orientation="vertical"> + android:transitionGroup="true"> + android:fitsSystemWindows="true"> + android:layout_height="wrap_content" + android:transitionGroup="true"> diff --git a/app/src/main/res/layout-land/fragment_player.xml b/app/src/main/res/layout-land/fragment_player.xml index a982f11b..e447c619 100755 --- a/app/src/main/res/layout-land/fragment_player.xml +++ b/app/src/main/res/layout-land/fragment_player.xml @@ -50,6 +50,7 @@ diff --git a/app/src/main/res/layout-sw600dp/activity_playing_queue.xml b/app/src/main/res/layout-sw600dp/fragment_playing_queue.xml similarity index 98% rename from app/src/main/res/layout-sw600dp/activity_playing_queue.xml rename to app/src/main/res/layout-sw600dp/fragment_playing_queue.xml index 0eddd402..bdd18ae3 100644 --- a/app/src/main/res/layout-sw600dp/activity_playing_queue.xml +++ b/app/src/main/res/layout-sw600dp/fragment_playing_queue.xml @@ -42,7 +42,6 @@ android:layout_width="match_parent" android:layout_height="match_parent" android:clipToPadding="false" - android:overScrollMode="never" android:paddingBottom="96dp" android:scrollbars="none" app:layout_behavior="com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior" diff --git a/app/src/main/res/layout-sw600dp/item_list.xml b/app/src/main/res/layout-sw600dp/item_list.xml index cc09a2a4..7843e3c5 100644 --- a/app/src/main/res/layout-sw600dp/item_list.xml +++ b/app/src/main/res/layout-sw600dp/item_list.xml @@ -7,12 +7,12 @@ android:layout_marginStart="1dp" android:layout_marginEnd="1dp" android:layout_marginBottom="1dp" - android:paddingHorizontal="64dp" android:background="?rectSelector" android:clickable="true" android:focusable="true" android:minHeight="?attr/listPreferredItemHeight" android:orientation="horizontal" + android:paddingHorizontal="64dp" tools:ignore="MissingPrefix"> - - - - - - - \ No newline at end of file + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_bug_report.xml b/app/src/main/res/layout/activity_bug_report.xml index b1216d92..1b845f50 100644 --- a/app/src/main/res/layout/activity_bug_report.xml +++ b/app/src/main/res/layout/activity_bug_report.xml @@ -1,76 +1,63 @@ - + android:fitsSystemWindows="true"> - + android:layout_height="wrap_content" + android:fitsSystemWindows="true" + app:liftOnScroll="true"> - + + + + + android:layout_height="wrap_content" + app:layout_behavior="com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior"> - + android:clipToPadding="false" + android:orientation="vertical"> - - - - - - - + android:layout_marginBottom="8dp" /> - + - + - + - + - - - - - \ No newline at end of file + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_donation.xml b/app/src/main/res/layout/activity_donation.xml index 28e7c6aa..e276c795 100644 --- a/app/src/main/res/layout/activity_donation.xml +++ b/app/src/main/res/layout/activity_donation.xml @@ -4,12 +4,14 @@ xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" - android:background="?attr/colorSurface"> + android:background="?attr/colorSurface" + android:fitsSystemWindows="true"> + android:background="?attr/colorSurface" + android:fitsSystemWindows="true"> - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/layout/activity_restore.xml b/app/src/main/res/layout/activity_restore.xml new file mode 100644 index 00000000..79eda336 --- /dev/null +++ b/app/src/main/res/layout/activity_restore.xml @@ -0,0 +1,8 @@ + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_settings.xml b/app/src/main/res/layout/activity_settings.xml index c5cc80e5..8eaeb850 100644 --- a/app/src/main/res/layout/activity_settings.xml +++ b/app/src/main/res/layout/activity_settings.xml @@ -1,46 +1,49 @@ - + android:background="?attr/colorSurface" + android:fitsSystemWindows="true"> - + android:layout_height="wrap_content" + android:fitsSystemWindows="true" + app:liftOnScroll="true"> - - - + android:layout_height="?attr/collapsingToolbarLayoutLargeSize" + app:expandedTitleMarginBottom="24dp" + app:expandedTitleMarginEnd="24dp" + app:expandedTitleMarginStart="24dp" + app:expandedTitleTextAppearance="@style/TextAppearance.Material3.HeadlineLarge" + app:layout_scrollFlags="scroll|exitUntilCollapsed|snap|enterAlwaysCollapsed"> + - + - + - - \ No newline at end of file + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_share_instagram.xml b/app/src/main/res/layout/activity_share_instagram.xml index fe53bb31..4fa8c82c 100644 --- a/app/src/main/res/layout/activity_share_instagram.xml +++ b/app/src/main/res/layout/activity_share_instagram.xml @@ -19,6 +19,7 @@ android:layout_width="match_parent" android:layout_height="match_parent" android:background="?attr/colorSurface" + android:fitsSystemWindows="true" android:orientation="vertical"> - + android:layout_height="wrap_content" + android:elevation="0dp" + app:elevation="0dp"> - - - - - - - + app:navigationIcon="@drawable/ic_keyboard_backspace_black" + app:title="@string/social_stories" + app:titleTextAppearance="@style/ToolbarTextAppearanceNormal" /> + diff --git a/app/src/main/res/layout/activity_song_tag_editor.xml b/app/src/main/res/layout/activity_song_tag_editor.xml index e6fbdd7a..2f9402a7 100755 --- a/app/src/main/res/layout/activity_song_tag_editor.xml +++ b/app/src/main/res/layout/activity_song_tag_editor.xml @@ -4,6 +4,7 @@ android:layout_width="match_parent" android:layout_height="match_parent" android:background="?attr/colorSurface" + android:fitsSystemWindows="true" android:focusable="true" android:focusableInTouchMode="true"> @@ -11,6 +12,7 @@ android:id="@+id/appBarLayout" android:layout_width="match_parent" android:layout_height="wrap_content" + android:fitsSystemWindows="true" app:liftOnScroll="true"> + android:baselineAligned="false" + android:orientation="horizontal"> - + android:background="?attr/colorSurface" + android:fitsSystemWindows="true"> - + android:layout_height="wrap_content" + android:elevation="0dp" + app:liftOnScroll="true" + android:fitsSystemWindows="true"> - + + + + + app:layout_behavior="com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior"> - - - - + android:scrollbars="none" + app:layout_behavior="com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior" /> - + - - - - - - - - - + + diff --git a/app/src/main/res/layout/card_credit.xml b/app/src/main/res/layout/card_credit.xml index f7900700..13bd142f 100644 --- a/app/src/main/res/layout/card_credit.xml +++ b/app/src/main/res/layout/card_credit.xml @@ -21,7 +21,7 @@ android:paddingEnd="?attr/listPreferredItemPaddingEnd" android:paddingBottom="?attr/listPreferredItemPaddingEnd" android:text="@string/credit_title" - android:textAppearance="@style/TextViewOverline" + android:textAppearance="@style/TextViewNormal" android:textColor="?android:attr/textColorSecondary" app:layout_constrainedWidth="true" app:layout_constraintEnd_toEndOf="parent" diff --git a/app/src/main/res/layout/card_other.xml b/app/src/main/res/layout/card_other.xml index 2a991b6a..987e4830 100644 --- a/app/src/main/res/layout/card_other.xml +++ b/app/src/main/res/layout/card_other.xml @@ -20,7 +20,7 @@ android:paddingEnd="?attr/listPreferredItemPaddingEnd" android:paddingBottom="?attr/listPreferredItemPaddingEnd" android:text="@string/others" - android:textAppearance="@style/TextViewOverline" + android:textAppearance="@style/TextViewNormal" android:textColor="?colorAccent" app:layout_constrainedWidth="true" app:layout_constraintEnd_toEndOf="parent" diff --git a/app/src/main/res/layout/card_retro_info.xml b/app/src/main/res/layout/card_retro_info.xml index 1e901866..f5670206 100644 --- a/app/src/main/res/layout/card_retro_info.xml +++ b/app/src/main/res/layout/card_retro_info.xml @@ -20,7 +20,7 @@ android:paddingEnd="?attr/listPreferredItemPaddingEnd" android:paddingBottom="?attr/listPreferredItemPaddingEnd" android:text="@string/support_development" - android:textAppearance="@style/TextViewOverline" + android:textAppearance="@style/TextViewNormal" android:textColor="?android:attr/textColorSecondary" app:layout_constrainedWidth="true" app:layout_constraintEnd_toEndOf="parent" diff --git a/app/src/main/res/layout/card_social.xml b/app/src/main/res/layout/card_social.xml index d20f1856..0eade5e1 100644 --- a/app/src/main/res/layout/card_social.xml +++ b/app/src/main/res/layout/card_social.xml @@ -20,7 +20,7 @@ android:paddingEnd="?attr/listPreferredItemPaddingEnd" android:paddingBottom="?attr/listPreferredItemPaddingEnd" android:text="@string/social" - android:textAppearance="@style/TextViewOverline" + android:textAppearance="@style/TextViewNormal" android:textColor="?colorAccent" app:layout_constrainedWidth="true" app:layout_constraintEnd_toEndOf="parent" @@ -38,8 +38,8 @@ app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@id/sb3" app:listItemIcon="@drawable/ic_baseline" - app:listItemTitle="@string/website" - app:listItemSummary="@string/website_summary"/> + app:listItemSummary="@string/website_summary" + app:listItemTitle="@string/website" /> + android:orientation="vertical" + android:paddingTop="0dp"> + android:layout_height="1dp" + android:layout_centerVertical="true" + android:visibility="invisible" /> @@ -32,47 +32,47 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerVertical="true" - android:layout_toLeftOf="@+id/button_1" - android:layout_toStartOf="@+id/button_1" + android:layout_marginEnd="@dimen/cast_mini_controller_control_button_margin" android:layout_marginRight="@dimen/cast_mini_controller_control_button_margin" - android:layout_marginEnd="@dimen/cast_mini_controller_control_button_margin" /> + android:layout_toStartOf="@+id/button_1" + android:layout_toLeftOf="@+id/button_1" /> + android:layout_toStartOf="@+id/button_2" + android:layout_toLeftOf="@+id/button_2" /> + android:layout_marginEnd="8dp" + android:layout_marginRight="8dp" /> @@ -100,8 +100,8 @@ android:layout_width="fill_parent" android:layout_height="5dp" android:layout_alignParentTop="true" - android:progressDrawable="@drawable/cast_mini_controller_progress_drawable" - android:layoutDirection="ltr" /> + android:layoutDirection="ltr" + android:progressDrawable="@drawable/cast_mini_controller_progress_drawable" /> diff --git a/app/src/main/res/layout/dialog_file_details.xml b/app/src/main/res/layout/dialog_file_details.xml index c31152fe..1bc23701 100644 --- a/app/src/main/res/layout/dialog_file_details.xml +++ b/app/src/main/res/layout/dialog_file_details.xml @@ -11,8 +11,8 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:paddingStart="16dp" - android:paddingEnd="16dp" android:paddingTop="16dp" + android:paddingEnd="16dp" android:textAppearance="@style/TextViewNormal" android:textColor="?android:attr/textColorPrimary" android:textSize="16sp" /> diff --git a/app/src/main/res/layout/fragment_about.xml b/app/src/main/res/layout/fragment_about.xml index 6125e374..99d84795 100644 --- a/app/src/main/res/layout/fragment_about.xml +++ b/app/src/main/res/layout/fragment_about.xml @@ -4,7 +4,6 @@ android:id="@+id/container" android:layout_width="match_parent" android:layout_height="match_parent" - android:overScrollMode="never" app:layout_behavior="com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior"> - - - - - - - - - diff --git a/app/src/main/res/layout/fragment_album_card_cover.xml b/app/src/main/res/layout/fragment_album_card_cover.xml index 4d2b14ea..bc7bd97c 100644 --- a/app/src/main/res/layout/fragment_album_card_cover.xml +++ b/app/src/main/res/layout/fragment_album_card_cover.xml @@ -12,7 +12,7 @@ android:layout_marginTop="8dp" android:layout_marginEnd="16dp" android:layout_marginBottom="16dp" - app:cardCornerRadius="12dp" + app:cardCornerRadius="@dimen/m3_card_medium_radius" app:cardElevation="4dp" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" diff --git a/app/src/main/res/layout/fragment_album_circle_cover.xml b/app/src/main/res/layout/fragment_album_circle_cover.xml index ce28ec7c..ad1d28b4 100644 --- a/app/src/main/res/layout/fragment_album_circle_cover.xml +++ b/app/src/main/res/layout/fragment_album_circle_cover.xml @@ -3,7 +3,8 @@ xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" - android:layout_height="match_parent"> + android:layout_height="match_parent" + android:padding="@dimen/padding_album_cover"> + android:layout_height="match_parent" + android:fitsSystemWindows="true" + android:transitionGroup="true"> + app:liftOnScroll="true" + android:fitsSystemWindows="true"> - - @@ -35,7 +33,6 @@ android:layout_height="wrap_content" android:descendantFocusability="beforeDescendants" android:focusableInTouchMode="true" - android:overScrollMode="never" app:layout_behavior="com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior"> + android:layout_height="wrap_content" + android:padding="@dimen/padding_album_cover"> diff --git a/app/src/main/res/layout/fragment_artist_details.xml b/app/src/main/res/layout/fragment_artist_details.xml index a110d686..a50a39bb 100644 --- a/app/src/main/res/layout/fragment_artist_details.xml +++ b/app/src/main/res/layout/fragment_artist_details.xml @@ -3,28 +3,25 @@ xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" - android:layout_height="match_parent"> + android:layout_height="match_parent" + android:fitsSystemWindows="true"> + app:liftOnScroll="true" + android:fitsSystemWindows="true"> - - @@ -32,7 +29,6 @@ android:id="@+id/container" android:layout_width="match_parent" android:layout_height="wrap_content" - android:overScrollMode="never" android:descendantFocusability="beforeDescendants" android:focusableInTouchMode="true" app:layout_behavior="com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior"> @@ -49,7 +45,7 @@ android:layout_marginStart="16dp" android:layout_marginTop="8dp" android:layout_marginEnd="16dp" - app:cardCornerRadius="24dp" + app:cardCornerRadius="@dimen/m3_card_large_radius" app:layout_constraintDimensionRatio="1:1" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" diff --git a/app/src/main/res/layout/fragment_backup.xml b/app/src/main/res/layout/fragment_backup.xml new file mode 100644 index 00000000..c24a3399 --- /dev/null +++ b/app/src/main/res/layout/fragment_backup.xml @@ -0,0 +1,96 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_banner_home.xml b/app/src/main/res/layout/fragment_banner_home.xml index 983367f6..27066559 100644 --- a/app/src/main/res/layout/fragment_banner_home.xml +++ b/app/src/main/res/layout/fragment_banner_home.xml @@ -16,12 +16,13 @@ xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" - android:transitionGroup="true"> + android:fitsSystemWindows="true"> + android:transitionGroup="true"> - - @@ -62,41 +62,38 @@ tools:ignore="RtlHardcoded,RtlSymmetry" tools:text="@tools:sample/date/hhmmss" /> - + diff --git a/app/src/main/res/layout/fragment_card_blur_player.xml b/app/src/main/res/layout/fragment_card_blur_player.xml index d20ed957..13b74311 100644 --- a/app/src/main/res/layout/fragment_card_blur_player.xml +++ b/app/src/main/res/layout/fragment_card_blur_player.xml @@ -26,6 +26,7 @@ diff --git a/app/src/main/res/layout/fragment_card_blur_player_playback_controls.xml b/app/src/main/res/layout/fragment_card_blur_player_playback_controls.xml index b00dcdfd..c0b591a2 100644 --- a/app/src/main/res/layout/fragment_card_blur_player_playback_controls.xml +++ b/app/src/main/res/layout/fragment_card_blur_player_playback_controls.xml @@ -57,11 +57,11 @@ + app:cardCornerRadius="8dp"> @@ -95,9 +95,9 @@ android:id="@+id/title" android:layout_width="match_parent" android:layout_height="wrap_content" + android:clickable="true" android:ellipsize="marquee" android:focusable="true" - android:clickable="true" android:freezesText="true" android:marqueeRepeatLimit="marquee_forever" android:paddingHorizontal="16dp" @@ -113,10 +113,10 @@ android:id="@+id/text" android:layout_width="match_parent" android:layout_height="wrap_content" - android:ellipsize="end" - android:maxLines="1" android:clickable="true" + android:ellipsize="end" android:focusable="true" + android:maxLines="1" android:paddingHorizontal="16dp" android:textAppearance="@style/TextViewBody1" android:textColor="?android:attr/textColorSecondary" @@ -140,8 +140,7 @@ android:id="@+id/recyclerView" android:layout_width="match_parent" android:layout_height="match_parent" - android:overScrollMode="never" - tools:listitem="@layout/item_list" /> + tools:listitem="@layout/item_list" /> diff --git a/app/src/main/res/layout/fragment_color_player.xml b/app/src/main/res/layout/fragment_color_player.xml index 9c544044..8bb06719 100644 --- a/app/src/main/res/layout/fragment_color_player.xml +++ b/app/src/main/res/layout/fragment_color_player.xml @@ -31,7 +31,7 @@ - - - - - - + app:layout_constraintTop_toBottomOf="@+id/text"> + app:liftOnScroll="true" + android:fitsSystemWindows="true"> @@ -21,7 +24,6 @@ android:id="@+id/toolbar" android:layout_width="match_parent" android:layout_height="wrap_content" - android:background="?attr/colorSurface" app:navigationIcon="@drawable/ic_search" app:popupTheme="?attr/toolbarPopupTheme" app:title="@null" @@ -35,72 +37,51 @@ android:textAppearance="@style/TextViewHeadline6" android:textStyle="bold" /> - - + + - + - + + + android:text="@string/nothing_to_see" + android:textAppearance="@style/TextViewHeadline5" + android:textColor="?android:attr/textColorSecondary" + tools:visibility="visible" /> + - - - - - - - - - - - - - + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_full.xml b/app/src/main/res/layout/fragment_full.xml index bef864bf..f1382aa4 100644 --- a/app/src/main/res/layout/fragment_full.xml +++ b/app/src/main/res/layout/fragment_full.xml @@ -8,7 +8,7 @@ android:focusable="true"> - - @@ -96,10 +96,10 @@ diff --git a/app/src/main/res/layout/fragment_gradient_player.xml b/app/src/main/res/layout/fragment_gradient_player.xml index ea984676..b8ab7af9 100644 --- a/app/src/main/res/layout/fragment_gradient_player.xml +++ b/app/src/main/res/layout/fragment_gradient_player.xml @@ -62,10 +62,11 @@ + android:fitsSystemWindows="true"> + android:layout_height="wrap_content" + android:transitionGroup="true"> - + android:fitsSystemWindows="true" + android:paddingBottom="@dimen/mini_player_height"> - - + + android:layout_marginTop="?attr/actionBarSize"> + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_main_activity_recycler_view.xml b/app/src/main/res/layout/fragment_main_activity_recycler_view.xml index 8356b924..392d049b 100644 --- a/app/src/main/res/layout/fragment_main_activity_recycler_view.xml +++ b/app/src/main/res/layout/fragment_main_activity_recycler_view.xml @@ -11,9 +11,8 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:clipToPadding="false" - android:overScrollMode="never" - android:transitionGroup="true" android:scrollbars="none" + android:transitionGroup="true" app:layout_dodgeInsetEdges="bottom" tools:listitem="@layout/item_list" /> diff --git a/app/src/main/res/layout/fragment_main_recycler.xml b/app/src/main/res/layout/fragment_main_recycler.xml index fa5aa94d..cf7d9816 100644 --- a/app/src/main/res/layout/fragment_main_recycler.xml +++ b/app/src/main/res/layout/fragment_main_recycler.xml @@ -4,15 +4,16 @@ xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" - android:transitionGroup="true"> + android:fitsSystemWindows="true"> + android:fitsSystemWindows="true"> @@ -21,7 +22,6 @@ android:id="@+id/toolbar" android:layout_width="match_parent" android:layout_height="wrap_content" - android:background="?attr/colorSurface" app:navigationIcon="@drawable/ic_search" app:popupTheme="?attr/toolbarPopupTheme" app:title="@null" @@ -35,24 +35,18 @@ android:textAppearance="@style/TextViewHeadline6" android:textStyle="bold" /> - - - + tools:listitem="@layout/item_list" + android:transitionGroup="true"/> + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_main_settings.xml b/app/src/main/res/layout/fragment_main_settings.xml index 774ec074..d538c45f 100644 --- a/app/src/main/res/layout/fragment_main_settings.xml +++ b/app/src/main/res/layout/fragment_main_settings.xml @@ -5,7 +5,6 @@ android:id="@+id/scrollView" android:layout_width="match_parent" android:layout_height="wrap_content" - android:overScrollMode="never" android:transitionGroup="true"> + diff --git a/app/src/main/res/layout/fragment_normal_lyrics.xml b/app/src/main/res/layout/fragment_normal_lyrics.xml index 0bb15c7f..bf7086a1 100644 --- a/app/src/main/res/layout/fragment_normal_lyrics.xml +++ b/app/src/main/res/layout/fragment_normal_lyrics.xml @@ -14,6 +14,7 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:padding="@dimen/normal_lyrics_padding" + android:textIsSelectable="true" android:textSize="@dimen/lyrics_text_size" /> diff --git a/app/src/main/res/layout/fragment_peak_player.xml b/app/src/main/res/layout/fragment_peak_player.xml index b954f397..9fa46501 100644 --- a/app/src/main/res/layout/fragment_peak_player.xml +++ b/app/src/main/res/layout/fragment_peak_player.xml @@ -17,105 +17,97 @@ xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="wrap_content" + android:background="?attr/colorSurface" android:clickable="true" android:focusable="true" android:orientation="vertical"> - - - - - + + - + - + - + - + - + - \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_plain_player.xml b/app/src/main/res/layout/fragment_plain_player.xml index 80ad6b8e..7cc0a06e 100644 --- a/app/src/main/res/layout/fragment_plain_player.xml +++ b/app/src/main/res/layout/fragment_plain_player.xml @@ -25,18 +25,19 @@ android:id="@+id/titleContainer" android:layout_width="0dp" android:layout_height="wrap_content" + android:clickable="false" android:orientation="vertical" android:paddingStart="16dp" android:paddingEnd="16dp" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" - android:clickable="false" app:layout_constraintTop_toBottomOf="@id/statusBarContainer"> - - + app:layout_constraintTop_toBottomOf="@id/statusBarContainer" /> + android:layout_height="match_parent" /> - + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_playlist_detail.xml b/app/src/main/res/layout/fragment_playlist_detail.xml index 9b5fa43f..c3dca285 100644 --- a/app/src/main/res/layout/fragment_playlist_detail.xml +++ b/app/src/main/res/layout/fragment_playlist_detail.xml @@ -5,15 +5,18 @@ android:id="@+id/container" android:layout_width="match_parent" android:layout_height="match_parent" + android:fitsSystemWindows="true" android:transitionGroup="true"> @@ -22,15 +25,8 @@ android:id="@+id/toolbar" android:layout_width="match_parent" android:layout_height="wrap_content" - android:background="?attr/colorSurface" app:navigationIcon="@drawable/ic_keyboard_backspace_black" app:titleTextAppearance="@style/ToolbarTextAppearanceNormal" /> - - - @@ -39,7 +35,6 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:clipToPadding="false" - android:overScrollMode="never" android:scrollbars="none" app:layout_behavior="com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior" /> diff --git a/app/src/main/res/layout/fragment_search.xml b/app/src/main/res/layout/fragment_search.xml index c2975bae..68415d09 100644 --- a/app/src/main/res/layout/fragment_search.xml +++ b/app/src/main/res/layout/fragment_search.xml @@ -5,6 +5,7 @@ android:layout_width="match_parent" android:layout_height="match_parent" android:background="?attr/colorSurface" + android:fitsSystemWindows="true" android:orientation="vertical"> + app:singleLine="true" + app:singleSelection="true"> - + android:text="@string/songs" /> + android:text="@string/artists" /> + android:text="@string/albums" /> - + android:text="@string/album_artist" /> + android:text="@string/genres" /> @@ -180,8 +135,7 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:clipToPadding="false" - android:overScrollMode="never" - android:scrollbarStyle="outsideOverlay" + android:scrollbarStyle="outsideOverlay" android:scrollbars="vertical" app:layout_behavior="com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior" /> diff --git a/app/src/main/res/layout/fragment_simple_controls_fragment.xml b/app/src/main/res/layout/fragment_simple_controls_fragment.xml index cdcd2afa..a002f9ba 100644 --- a/app/src/main/res/layout/fragment_simple_controls_fragment.xml +++ b/app/src/main/res/layout/fragment_simple_controls_fragment.xml @@ -10,20 +10,20 @@ android:id="@+id/title" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:paddingHorizontal="16dp" - app:layout_constraintBottom_toTopOf="@+id/text" - app:layout_constraintEnd_toEndOf="parent" - app:layout_constraintStart_toStartOf="parent" android:layout_gravity="center" android:ellipsize="marquee" android:focusable="true" android:freezesText="true" android:gravity="center" android:marqueeRepeatLimit="marquee_forever" + android:paddingHorizontal="16dp" android:scrollHorizontally="true" android:singleLine="true" android:textAppearance="@style/TextViewHeadline6" android:textStyle="bold" + app:layout_constraintBottom_toTopOf="@+id/text" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" tools:text="@tools:sample/lorem/random" /> diff --git a/app/src/main/res/layout/fragment_simple_player.xml b/app/src/main/res/layout/fragment_simple_player.xml index a7a8a139..ff6892a2 100644 --- a/app/src/main/res/layout/fragment_simple_player.xml +++ b/app/src/main/res/layout/fragment_simple_player.xml @@ -25,7 +25,7 @@ - - - - @@ -51,7 +53,7 @@ android:layout_marginStart="16dp" android:layout_marginTop="16dp" android:scaleType="centerCrop" - android:transitionName="@string/transition_user_image" + android:transitionName="user_image" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@id/bannerImage" app:retroCornerSize="36dp" @@ -91,7 +93,9 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_gravity="bottom" - android:layout_margin="16dp" + android:layout_marginHorizontal="16dp" + android:layout_marginBottom="70dp" + android:fitsSystemWindows="false" android:gravity="center" android:text="@string/save" app:icon="@drawable/ic_save" diff --git a/app/src/main/res/layout/home_content.xml b/app/src/main/res/layout/home_content.xml index 77f3e86f..10f38720 100644 --- a/app/src/main/res/layout/home_content.xml +++ b/app/src/main/res/layout/home_content.xml @@ -66,15 +66,14 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:nestedScrollingEnabled="false" - android:overScrollMode="never" - tools:itemCount="10" + tools:itemCount="10" tools:layoutManager="androidx.recyclerview.widget.GridLayoutManager" tools:listitem="@layout/item_album_card" tools:spanCount="3" /> + android:layout_height="@dimen/mini_player_height_expanded" /> \ No newline at end of file diff --git a/app/src/main/res/layout/item_album_card.xml b/app/src/main/res/layout/item_album_card.xml index a1d69663..7b1b4cd8 100644 --- a/app/src/main/res/layout/item_album_card.xml +++ b/app/src/main/res/layout/item_album_card.xml @@ -9,7 +9,7 @@ android:background="?attr/rectSelector" android:orientation="vertical" android:padding="4dp" - app:cardCornerRadius="12dp" + app:cardCornerRadius="@dimen/m3_card_corner_radius" app:layout_constraintDimensionRatio="3:4" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" diff --git a/app/src/main/res/layout/item_artist.xml b/app/src/main/res/layout/item_artist.xml index e8503a7b..8ec97715 100644 --- a/app/src/main/res/layout/item_artist.xml +++ b/app/src/main/res/layout/item_artist.xml @@ -26,8 +26,8 @@ android:ellipsize="end" android:gravity="center" android:maxLines="2" - android:textAppearance="@style/TextViewBody1" android:paddingTop="12dp" + android:textAppearance="@style/TextViewBody1" android:textColor="?android:attr/textColorPrimary" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" diff --git a/app/src/main/res/layout/item_artist_card.xml b/app/src/main/res/layout/item_artist_card.xml index f0558b04..dc7bf38e 100644 --- a/app/src/main/res/layout/item_artist_card.xml +++ b/app/src/main/res/layout/item_artist_card.xml @@ -17,7 +17,7 @@ android:layout_width="128dp" android:layout_height="128dp" android:layout_margin="4dp" - app:cardCornerRadius="16dp"> + app:cardCornerRadius="@dimen/m3_card_corner_radius"> + app:cardCornerRadius="@dimen/m3_card_corner_radius"> + app:cardCornerRadius="@dimen/m3_card_corner_radius"> diff --git a/app/src/main/res/layout/item_grid.xml b/app/src/main/res/layout/item_grid.xml index 4440a526..e5a5f5e4 100644 --- a/app/src/main/res/layout/item_grid.xml +++ b/app/src/main/res/layout/item_grid.xml @@ -17,7 +17,7 @@ android:layout_height="0dp" android:scaleType="centerCrop" app:cardBackgroundColor="@color/transparent" - app:cardCornerRadius="8dp" + app:cardCornerRadius="@dimen/m3_card_corner_radius" app:layout_constraintDimensionRatio="1:1" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" diff --git a/app/src/main/res/layout/item_image.xml b/app/src/main/res/layout/item_image.xml index 76c2a466..e6cbd121 100644 --- a/app/src/main/res/layout/item_image.xml +++ b/app/src/main/res/layout/item_image.xml @@ -14,7 +14,7 @@ android:layout_margin="8dp" android:orientation="vertical" app:cardBackgroundColor="@color/transparent" - app:cardCornerRadius="8dp" + app:cardCornerRadius="@dimen/m3_card_corner_radius" app:cardPreventCornerOverlap="true" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" diff --git a/app/src/main/res/layout/item_image_gradient.xml b/app/src/main/res/layout/item_image_gradient.xml index 864215cc..309cea05 100644 --- a/app/src/main/res/layout/item_image_gradient.xml +++ b/app/src/main/res/layout/item_image_gradient.xml @@ -10,11 +10,12 @@ tools:ignore="MissingPrefix"> - + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/item_list_quick_actions.xml b/app/src/main/res/layout/item_list_quick_actions.xml index 85e70c0a..7f295fc3 100644 --- a/app/src/main/res/layout/item_list_quick_actions.xml +++ b/app/src/main/res/layout/item_list_quick_actions.xml @@ -12,52 +12,38 @@ ~ See the GNU General Public License for more details. --> - + android:orientation="horizontal" + android:paddingHorizontal="16dp"> + app:icon="@drawable/ic_play_arrow" /> + app:icon="@drawable/ic_shuffle" /> - \ No newline at end of file + \ No newline at end of file diff --git a/app/src/main/res/layout/item_permission.xml b/app/src/main/res/layout/item_permission.xml index 17d80684..b4be1a71 100644 --- a/app/src/main/res/layout/item_permission.xml +++ b/app/src/main/res/layout/item_permission.xml @@ -15,16 +15,16 @@ android:id="@+id/number" android:layout_width="24dp" android:layout_height="24dp" - android:layout_marginTop="4dp" android:layout_marginStart="16dp" + android:layout_marginTop="4dp" android:background="@drawable/color_circle_gradient" android:gravity="center" android:maxLines="3" - tools:text="1" android:textAppearance="@style/TextViewButton" app:layout_constraintBottom_toBottomOf="@id/title" app:layout_constraintStart_toStartOf="parent" - app:layout_constraintTop_toTopOf="@id/title" /> + app:layout_constraintTop_toTopOf="@id/title" + tools:text="1" /> - + android:focusable="true" + android:transitionGroup="true"> - @@ -27,15 +24,16 @@ @@ -44,7 +42,6 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center_vertical" - android:layout_marginStart="16dp" app:cardCornerRadius="6dp" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintStart_toEndOf="@id/drag_view" diff --git a/app/src/main/res/layout/item_storage.xml b/app/src/main/res/layout/item_storage.xml index 1f1d88c7..28cbebf4 100644 --- a/app/src/main/res/layout/item_storage.xml +++ b/app/src/main/res/layout/item_storage.xml @@ -29,8 +29,8 @@ android:id="@+id/icon" android:layout_width="42dp" android:layout_height="42dp" - android:scaleType="centerCrop" android:padding="10dp" + android:scaleType="centerCrop" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" diff --git a/app/src/main/res/layout/item_suggestions.xml b/app/src/main/res/layout/item_suggestions.xml index 0d346dc5..49e55485 100644 --- a/app/src/main/res/layout/item_suggestions.xml +++ b/app/src/main/res/layout/item_suggestions.xml @@ -24,7 +24,7 @@ android:id="@+id/card1" android:layout_width="0dp" android:layout_height="0dp" - app:cardCornerRadius="8dp" + app:cardCornerRadius="@dimen/m3_card_corner_radius" app:cardUseCompatPadding="true" app:layout_constraintDimensionRatio="1,1" app:layout_constraintEnd_toStartOf="@id/card2" @@ -44,7 +44,7 @@ android:id="@+id/card2" android:layout_width="0dp" android:layout_height="0dp" - app:cardCornerRadius="8dp" + app:cardCornerRadius="@dimen/m3_card_corner_radius" app:cardUseCompatPadding="true" app:layout_constraintDimensionRatio="1,1" app:layout_constraintEnd_toStartOf="@id/card3" @@ -64,7 +64,7 @@ android:id="@+id/card3" android:layout_width="0dp" android:layout_height="0dp" - app:cardCornerRadius="8dp" + app:cardCornerRadius="@dimen/m3_card_corner_radius" app:cardUseCompatPadding="true" app:layout_constraintDimensionRatio="1,1" app:layout_constraintEnd_toStartOf="@id/card4" @@ -84,7 +84,7 @@ android:id="@+id/card4" android:layout_width="0dp" android:layout_height="0dp" - app:cardCornerRadius="8dp" + app:cardCornerRadius="@dimen/m3_card_corner_radius" app:cardUseCompatPadding="true" app:layout_constraintDimensionRatio="1,1" app:layout_constraintEnd_toStartOf="@id/card5" @@ -104,7 +104,7 @@ android:id="@+id/card5" android:layout_width="0dp" android:layout_height="0dp" - app:cardCornerRadius="8dp" + app:cardCornerRadius="@dimen/m3_card_corner_radius" app:cardUseCompatPadding="true" app:layout_constraintDimensionRatio="1,1" app:layout_constraintEnd_toEndOf="parent" @@ -127,7 +127,7 @@ android:clickable="true" android:focusable="true" app:cardBackgroundColor="?attr/colorSurface" - app:cardCornerRadius="8dp" + app:cardCornerRadius="@dimen/m3_card_corner_radius" app:cardElevation="0dp" app:cardUseCompatPadding="true" app:layout_constraintDimensionRatio="1,1" @@ -144,8 +144,8 @@ android:padding="8dp" android:text="New music mix" android:textAppearance="@style/TextViewNormal" - android:textStyle="bold" android:textSize="32sp" + android:textStyle="bold" app:autoSizeMaxTextSize="32sp" app:autoSizeMinTextSize="24sp" app:autoSizeStepGranularity="1sp" /> @@ -155,7 +155,7 @@ android:id="@+id/card7" android:layout_width="0dp" android:layout_height="0dp" - app:cardCornerRadius="8dp" + app:cardCornerRadius="@dimen/m3_card_corner_radius" app:cardUseCompatPadding="true" app:layout_constraintDimensionRatio="1,1" app:layout_constraintEnd_toStartOf="@id/card8" @@ -175,7 +175,7 @@ android:id="@+id/card8" android:layout_width="0dp" android:layout_height="0dp" - app:cardCornerRadius="8dp" + app:cardCornerRadius="@dimen/m3_card_corner_radius" app:cardUseCompatPadding="true" app:layout_constraintDimensionRatio="1,1" app:layout_constraintEnd_toEndOf="parent" @@ -194,7 +194,7 @@ diff --git a/app/src/main/res/layout/list_item_view_switch.xml b/app/src/main/res/layout/list_item_view_switch.xml index bf08f605..a536b1fa 100644 --- a/app/src/main/res/layout/list_item_view_switch.xml +++ b/app/src/main/res/layout/list_item_view_switch.xml @@ -76,9 +76,11 @@ android:id="@android:id/checkbox" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:background="@null" + app:showText="false" android:clickable="false" android:focusable="false" + app:track="@drawable/switch_track" + android:thumb="@drawable/switch_thumb" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintTop_toTopOf="parent" /> diff --git a/app/src/main/res/layout/list_item_view_switch_no_title.xml b/app/src/main/res/layout/list_item_view_switch_no_title.xml index 2eeb6a18..4f65d0a6 100644 --- a/app/src/main/res/layout/list_item_view_switch_no_title.xml +++ b/app/src/main/res/layout/list_item_view_switch_no_title.xml @@ -20,9 +20,9 @@ android:background="?attr/rectSelector" android:minHeight="?attr/listPreferredItemHeight" android:paddingStart="?attr/listPreferredItemPaddingStart" - android:paddingTop="16dp" + android:paddingTop="8dp" android:paddingEnd="?attr/listPreferredItemPaddingEnd" - android:paddingBottom="16dp" + android:paddingBottom="8dp" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" tools:ignore="PrivateResource"> @@ -64,7 +64,9 @@ android:id="@android:id/checkbox" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:background="@null" + app:showText="false" + app:track="@drawable/switch_track" + android:thumb="@drawable/switch_thumb" android:clickable="false" android:focusable="false" app:layout_constraintBottom_toBottomOf="parent" diff --git a/app/src/main/res/layout/mcab_toolbar.xml b/app/src/main/res/layout/mcab_toolbar.xml new file mode 100644 index 00000000..9efe854e --- /dev/null +++ b/app/src/main/res/layout/mcab_toolbar.xml @@ -0,0 +1,8 @@ + + \ No newline at end of file diff --git a/app/src/main/res/layout/section_recycler_view.xml b/app/src/main/res/layout/section_recycler_view.xml index 1b9b26e7..f554b90d 100644 --- a/app/src/main/res/layout/section_recycler_view.xml +++ b/app/src/main/res/layout/section_recycler_view.xml @@ -4,15 +4,15 @@ xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="wrap_content" - android:orientation="vertical" > + android:orientation="vertical"> - - - + android:background="?colorSurface" + app:defaultNavHost="true" + app:layout_behavior="com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior" /> @@ -44,23 +43,21 @@ android:id="@+id/cast_stub" android:layout_width="match_parent" android:layout_height="wrap_content" - android:layout="@layout/cast_controller_layout"/> + android:layout="@layout/cast_controller_layout" /> + tools:layout_height="wrap_content" /> \ No newline at end of file diff --git a/app/src/main/res/layout/sub_header.xml b/app/src/main/res/layout/sub_header.xml index 5b066f07..18e3dc58 100644 --- a/app/src/main/res/layout/sub_header.xml +++ b/app/src/main/res/layout/sub_header.xml @@ -5,9 +5,9 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="start" - android:gravity="start" android:layout_marginStart="@dimen/toolbar_margin_horizontal" android:layout_marginEnd="@dimen/toolbar_margin_horizontal" + android:gravity="start" android:paddingVertical="12dp" android:textAppearance="@style/TextViewOverline" android:textStyle="bold" diff --git a/app/src/main/res/menu/menu_backup.xml b/app/src/main/res/menu/menu_backup.xml new file mode 100644 index 00000000..760eab79 --- /dev/null +++ b/app/src/main/res/menu/menu_backup.xml @@ -0,0 +1,19 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/menu/menu_search.xml b/app/src/main/res/menu/menu_search.xml index a792688a..683dbb51 100644 --- a/app/src/main/res/menu/menu_search.xml +++ b/app/src/main/res/menu/menu_search.xml @@ -1,15 +1,9 @@ - - \ No newline at end of file diff --git a/app/src/main/res/navigation/main_graph.xml b/app/src/main/res/navigation/main_graph.xml index 77147cf1..499f527f 100644 --- a/app/src/main/res/navigation/main_graph.xml +++ b/app/src/main/res/navigation/main_graph.xml @@ -62,7 +62,7 @@ - + + + + \ No newline at end of file diff --git a/app/src/main/res/navigation/settings_graph.xml b/app/src/main/res/navigation/settings_graph.xml index 725feda1..70dffca0 100644 --- a/app/src/main/res/navigation/settings_graph.xml +++ b/app/src/main/res/navigation/settings_graph.xml @@ -74,6 +74,15 @@ app:launchSingleTop="true" app:popEnterAnim="@anim/retro_fragment_close_enter" app:popExitAnim="@anim/retro_fragment_close_exit" /> + + + + \ No newline at end of file diff --git a/app/src/main/res/values-night-v27/styles.xml b/app/src/main/res/values-night-v27/styles.xml index 87c77add..4666e03d 100644 --- a/app/src/main/res/values-night-v27/styles.xml +++ b/app/src/main/res/values-night-v27/styles.xml @@ -12,14 +12,13 @@ ~ See the GNU General Public License for more details. --> - + - \ No newline at end of file diff --git a/app/src/main/res/values-night/colors.xml b/app/src/main/res/values-night/colors.xml index 9a919933..5718c56e 100644 --- a/app/src/main/res/values-night/colors.xml +++ b/app/src/main/res/values-night/colors.xml @@ -3,4 +3,5 @@ @android:color/black + #202020 \ No newline at end of file diff --git a/app/src/main/res/values-night/colors_md3.xml b/app/src/main/res/values-night/colors_md3.xml new file mode 100644 index 00000000..4450fd61 --- /dev/null +++ b/app/src/main/res/values-night/colors_md3.xml @@ -0,0 +1,4 @@ + + + @color/elevationOverlayDark + \ No newline at end of file diff --git a/app/src/main/res/values-night/styles.xml b/app/src/main/res/values-night/styles.xml index 99e61bb2..7ab94a34 100644 --- a/app/src/main/res/values-night/styles.xml +++ b/app/src/main/res/values-night/styles.xml @@ -14,12 +14,11 @@ - \ No newline at end of file diff --git a/app/src/main/res/values-v27/styles_parents.xml b/app/src/main/res/values-v27/styles_parents.xml index d346f69c..2aea8a24 100644 --- a/app/src/main/res/values-v27/styles_parents.xml +++ b/app/src/main/res/values-v27/styles_parents.xml @@ -12,30 +12,36 @@ ~ See the GNU General Public License for more details. --> - + - - - \ No newline at end of file diff --git a/app/src/main/res/values-v31/donottranslate.xml b/app/src/main/res/values-v31/donottranslate.xml new file mode 100644 index 00000000..e9ef74c3 --- /dev/null +++ b/app/src/main/res/values-v31/donottranslate.xml @@ -0,0 +1,5 @@ + + + true + true + \ No newline at end of file diff --git a/app/src/main/res/values-v31/styles.xml b/app/src/main/res/values-v31/styles.xml new file mode 100644 index 00000000..f8479c49 --- /dev/null +++ b/app/src/main/res/values-v31/styles.xml @@ -0,0 +1,17 @@ + + + + + + diff --git a/app/src/main/res/values-xlarge/dimens.xml b/app/src/main/res/values-xlarge/dimens.xml index b6e544b5..a7ffbf07 100644 --- a/app/src/main/res/values-xlarge/dimens.xml +++ b/app/src/main/res/values-xlarge/dimens.xml @@ -1,7 +1,7 @@ 64dp 64dp - 64dp + 72dp 16dp 80dp diff --git a/app/src/main/res/values/attrs.xml b/app/src/main/res/values/attrs.xml index d2db61cf..63e3c427 100644 --- a/app/src/main/res/values/attrs.xml +++ b/app/src/main/res/values/attrs.xml @@ -6,6 +6,7 @@ + diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml index 3973baac..9d64c907 100644 --- a/app/src/main/res/values/colors.xml +++ b/app/src/main/res/values/colors.xml @@ -15,6 +15,9 @@ #202124 #202124 + #353535 + #dddddd + #000000 #00000000 @@ -27,5 +30,9 @@ #FFD8D8D8 #ff33b5e5 + #FFFFFF + #202020 + #000000 + #FFFFFF diff --git a/app/src/main/res/values/colors_md3.xml b/app/src/main/res/values/colors_md3.xml new file mode 100644 index 00000000..bedef30c --- /dev/null +++ b/app/src/main/res/values/colors_md3.xml @@ -0,0 +1,7 @@ + + + @color/md_grey_700 + @color/md_grey_500 + @color/md_black_1000 + @color/elevationOverlayLight + \ No newline at end of file diff --git a/app/src/main/res/values/dimens.xml b/app/src/main/res/values/dimens.xml index 6fb2613d..59cbf640 100644 --- a/app/src/main/res/values/dimens.xml +++ b/app/src/main/res/values/dimens.xml @@ -13,6 +13,12 @@ 8dp 56dp + 64dp + + 120dp + 128dp + + 64dp 96dp 96dp @@ -34,11 +40,9 @@ 12dp 32dp - 112dp - 120dp 8dp 48dp - 52dp + 60dp 10dp 56dp 72dp @@ -52,5 +56,9 @@ 18sp 10dp 20dp - 64dp + + 16dp + 24dp + 40dp + 16dp diff --git a/app/src/main/res/values/donottranslate.xml b/app/src/main/res/values/donottranslate.xml index 4ffe8a06..3e5913bc 100755 --- a/app/src/main/res/values/donottranslate.xml +++ b/app/src/main/res/values/donottranslate.xml @@ -21,4 +21,9 @@ @string/my_top_tracks 😱 + + + Material You + false + false \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index be470180..b4aa0bba 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -514,4 +514,7 @@ You have to select at least one category. You will be forwarded to the issue tracker website. Your account data is only used for authentication. + Do you want to restore backup? + New Backup + Backup and restore your settings, playlists diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml index 3881a4d9..315cdd07 100644 --- a/app/src/main/res/values/styles.xml +++ b/app/src/main/res/values/styles.xml @@ -23,6 +23,14 @@ @style/WindowAnimationTransition + + - @@ -130,12 +137,11 @@ @android:color/transparent - - - - - - + + - - - -