Merge pull request #1168 from prathameshmm02/dev

New Update
This commit is contained in:
Prathamesh More 2021-11-21 21:34:36 +05:30 committed by GitHub
commit c7d158f860
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
319 changed files with 5265 additions and 3980 deletions

View file

@ -6,7 +6,6 @@ apply plugin: 'kotlin-parcelize'
android { android {
compileSdkVersion 31 compileSdkVersion 31
buildToolsVersion = '29.0.3'
defaultConfig { defaultConfig {
minSdkVersion 21 minSdkVersion 21
@ -16,7 +15,7 @@ android {
vectorDrawables.useSupportLibrary = true vectorDrawables.useSupportLibrary = true
applicationId "code.name.monkey.retromusic" applicationId "code.name.monkey.retromusic"
versionCode 10520 versionCode 10519
versionName '5.0.0' + "_" + getDate() versionName '5.0.0' + "_" + getDate()
buildConfigField("String", "GOOGLE_PLAY_LICENSING_KEY", "\"${getProperty(getProperties('../public.properties'), 'GOOGLE_PLAY_LICENSE_KEY')}\"") buildConfigField("String", "GOOGLE_PLAY_LICENSING_KEY", "\"${getProperty(getProperties('../public.properties'), 'GOOGLE_PLAY_LICENSE_KEY')}\"")
@ -92,21 +91,21 @@ dependencies {
implementation project(':appthemehelper') implementation project(':appthemehelper')
implementation "androidx.gridlayout:gridlayout:1.0.0" implementation "androidx.gridlayout:gridlayout:1.0.0"
implementation "androidx.cardview:cardview:1.0.0" implementation "androidx.cardview:cardview:1.0.0"
implementation 'androidx.appcompat:appcompat:1.3.1' implementation 'androidx.appcompat:appcompat:1.4.0'
implementation 'androidx.annotation:annotation:1.2.0' implementation 'androidx.annotation:annotation:1.3.0'
implementation 'androidx.constraintlayout:constraintlayout:2.1.0' implementation 'androidx.constraintlayout:constraintlayout:2.1.2'
implementation 'androidx.recyclerview:recyclerview:1.2.1' implementation 'androidx.recyclerview:recyclerview:1.2.1'
implementation 'androidx.preference:preference-ktx:1.1.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' implementation 'androidx.palette:palette-ktx:1.0.0'
//Cast Dependencies //Cast Dependencies
implementation 'androidx.mediarouter:mediarouter:1.2.5' 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 //WebServer by NanoHttpd
implementation "org.nanohttpd:nanohttpd:2.3.1" 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-runtime-ktx:$nav_version"
implementation "androidx.navigation:navigation-fragment-ktx:$nav_version" implementation "androidx.navigation:navigation-fragment-ktx:$nav_version"
implementation "androidx.navigation:navigation-ui-ktx:$nav_version" implementation "androidx.navigation:navigation-ui-ktx:$nav_version"
@ -116,13 +115,13 @@ dependencies {
implementation "androidx.room:room-ktx:$room_version" implementation "androidx.room:room-ktx:$room_version"
kapt "androidx.room:room-compiler:$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-viewmodel-ktx:$lifecycle_version"
implementation "androidx.lifecycle:lifecycle-livedata-ktx:$lifecycle_version" implementation "androidx.lifecycle:lifecycle-livedata-ktx:$lifecycle_version"
implementation "androidx.lifecycle:lifecycle-common-java8:$lifecycle_version" implementation "androidx.lifecycle:lifecycle-common-java8:$lifecycle_version"
implementation 'com.google.android.play:core-ktx:1.8.1' 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' def retrofit_version = '2.9.0'
implementation "com.squareup.retrofit2:retrofit:$retrofit_version" 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:input:$material_dialog_version"
implementation "com.afollestad.material-dialogs:color:$material_dialog_version" implementation "com.afollestad.material-dialogs:color:$material_dialog_version"
implementation "com.afollestad.material-dialogs:bottomsheets:$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" 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-core:$kotlin_coroutines_version"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$kotlin_coroutines_version" implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$kotlin_coroutines_version"
def koin_version = "2.1.5" def koin_version = '3.1.3'
implementation "org.koin:koin-core:$koin_version" implementation "io.insert-koin:koin-core:$koin_version"
implementation "org.koin:koin-androidx-viewmodel:$koin_version" implementation "io.insert-koin:koin-android:$koin_version"
implementation 'com.github.bumptech.glide:glide:4.12.0' implementation 'com.github.bumptech.glide:glide:4.12.0'
kapt 'com.github.bumptech.glide:compiler: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 '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.eclipse.mylyn.github:org.eclipse.egit.github.core:2.1.5'
implementation 'org.bitbucket.ijabz:jaudiotagger:2.2.5' implementation 'com.github.AdrienPoupa:jaudiotagger:2.2.3'
implementation 'com.anjlab.android.iab.v3:library:1.1.0' implementation 'com.anjlab.android.iab.v3:library:2.0.1'
implementation 'com.r0adkll:slidableactivity:2.1.0' implementation 'com.r0adkll:slidableactivity:2.1.0'
implementation 'com.heinrichreimersoftware:material-intro:2.0.0' implementation 'com.heinrichreimersoftware:material-intro:2.0.0'
implementation 'com.github.dhaval2404:imagepicker:1.7.1' implementation 'com.github.dhaval2404:imagepicker:1.7.1'
implementation 'me.zhanghai.android.fastscroll:library:1.1.7' 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' apply from: '../spotless.gradle'

View file

@ -16,16 +16,13 @@
# public *; # public *;
#} #}
# Uncomment this to preserve the line number information for # Preserve the line number information for
# debugging stack traces. # debugging stack traces.
#-keepattributes SourceFile,LineNumberTable -keepattributes SourceFile,LineNumberTable
# If you keep the line number information, uncomment this to # If you keep the line number information, uncomment this to
# hide the original source file name. # hide the original source file name.
#-renamesourcefileattribute SourceFile #-renamesourcefileattribute SourceFile
-keepnames class **
-keepnames class ** { *; }
-keepattributes SourceFile,LineNumberTable
-dontwarn java.lang.invoke.* -dontwarn java.lang.invoke.*
-dontwarn **$$Lambda$* -dontwarn **$$Lambda$*
@ -58,11 +55,9 @@
-keepclassmembers enum * { *; } -keepclassmembers enum * { *; }
-keepattributes *Annotation*, Signature, Exception -keepattributes *Annotation*, Signature, Exception
-keepnames class androidx.navigation.fragment.NavHostFragment -keepnames class androidx.navigation.fragment.NavHostFragment
-keepnames class code.name.monkey.retromusic.model.Home
-keep class * extends androidx.fragment.app.Fragment{} -keep class * extends androidx.fragment.app.Fragment{}
-keepnames class * extends android.os.Parcelable -keepnames class * extends android.os.Parcelable
-keepnames class * extends java.io.Serializable -keepnames class * extends java.io.Serializable
-keep class code.name.monkey.retromusic.network.model.** { *; } -keep class code.name.monkey.retromusic.network.model.** { *; }
-keep class code.name.monkey.retromusic.model.CategoryInfo { *; } -keep class code.name.monkey.retromusic.model.CategoryInfo { *; }
-keep class com.google.android.material.bottomsheet.** { *; } -keep class com.google.android.material.bottomsheet.** { *; }
-keep class code.name.monkey.retromusic.Constants { *; }

View file

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<bool name="md3_available">true</bool>
</resources>

View file

@ -96,7 +96,7 @@
<style name="circleImageView" parent=""> <style name="circleImageView" parent="">
<item name="cornerFamily">rounded</item> <item name="cornerFamily">rounded</item>
<item name="cornerSize">16dp</item> <item name="cornerSize">40dp</item>
</style> </style>
<style name="BottomSheetItemTextAppearance" parent="Widget.MaterialComponents.BottomNavigationView.Colored"> <style name="BottomSheetItemTextAppearance" parent="Widget.MaterialComponents.BottomNavigationView.Colored">

View file

@ -8,7 +8,7 @@
<uses-permission android:name="android.permission.VIBRATE" /> <uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="android.permission.WAKE_LOCK" /> <uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.BROADCAST_STICKY" /> <uses-permission android:name="android.permission.BROADCAST_STICKY" />
@ -107,12 +107,9 @@
<data android:mimeType="vnd.android.cursor.dir/audio" /> <data android:mimeType="vnd.android.cursor.dir/audio" />
</intent-filter> </intent-filter>
</activity> </activity>
<activity android:name=".activities.PlayingQueueActivity" />
<activity android:name=".activities.SettingsActivity" /> <activity android:name=".activities.SettingsActivity" />
<activity android:name=".activities.tageditor.AlbumTagEditorActivity" /> <activity android:name=".activities.tageditor.AlbumTagEditorActivity" />
<activity android:name=".activities.tageditor.SongTagEditorActivity" /> <activity android:name=".activities.tageditor.SongTagEditorActivity" />
<activity android:name=".activities.LyricsActivity" />
<activity android:name=".activities.UserInfoActivity" />
<activity android:name=".activities.SupportDevelopmentActivity" /> <activity android:name=".activities.SupportDevelopmentActivity" />
<activity android:name=".activities.LicenseActivity" /> <activity android:name=".activities.LicenseActivity" />
<activity android:name=".activities.PurchaseActivity" /> <activity android:name=".activities.PurchaseActivity" />
@ -122,6 +119,19 @@
<activity android:name=".activities.DriveModeActivity" /> <activity android:name=".activities.DriveModeActivity" />
<activity android:name=".activities.PermissionActivity" /> <activity android:name=".activities.PermissionActivity" />
<activity android:name=".activities.LockScreenActivity" /> <activity android:name=".activities.LockScreenActivity" />
<activity
android:name="code.name.monkey.retromusic.fragments.backup.RestoreActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<data android:scheme="file" />
<data android:mimeType="*/*" />
<data android:pathPattern=".*\\.rmbak" />
</intent-filter>
</activity>
<activity <activity
android:name=".appshortcuts.AppShortcutLauncherActivity" android:name=".appshortcuts.AppShortcutLauncherActivity"
@ -136,14 +146,14 @@
android:name=".cast.ExpandedControlsActivity" android:name=".cast.ExpandedControlsActivity"
android:label="@string/app_name" android:label="@string/app_name"
android:launchMode="singleTask" android:launchMode="singleTask"
android:theme="@style/Theme.AppCompat.NoActionBar" android:screenOrientation="portrait"
android:screenOrientation="portrait"> android:theme="@style/Theme.AppCompat.NoActionBar">
<intent-filter> <intent-filter>
<action android:name="android.intent.action.MAIN"/> <action android:name="android.intent.action.MAIN" />
</intent-filter> </intent-filter>
<meta-data <meta-data
android:name="android.support.PARENT_ACTIVITY" android:name="android.support.PARENT_ACTIVITY"
android:value=".activities.MainActivity"/> android:value=".activities.MainActivity" />
</activity> </activity>
<provider <provider
@ -156,7 +166,6 @@
android:resource="@xml/provider_paths" /> android:resource="@xml/provider_paths" />
</provider> </provider>
<provider <provider
android:name="androidx.core.content.FileProvider" android:name="androidx.core.content.FileProvider"
android:authorities="${applicationId}" android:authorities="${applicationId}"
@ -266,16 +275,16 @@
android:value="true" /> android:value="true" />
<!-- Android Auto --> <!-- Android Auto -->
<!-- <meta-data <meta-data
android:name="com.google.android.gms.car.application" android:name="com.google.android.gms.car.application"
android:resource="@xml/automotive_app_desc"/> android:resource="@xml/automotive_app_desc" />
<meta-data <meta-data
android:name="com.google.android.gms.car.application.theme" android:name="com.google.android.gms.car.application.theme"
android:resource="@style/CarTheme" /> android:resource="@style/CarTheme" />
<meta-data <meta-data
android:name="com.google.android.gms.car.notification.SmallIcon" android:name="com.google.android.gms.car.notification.SmallIcon"
android:resource="@drawable/ic_notification"/> android:resource="@drawable/ic_notification" />
-->
<!-- ChromeCast --> <!-- ChromeCast -->
<meta-data <meta-data
android:name="com.google.android.gms.cast.framework.OPTIONS_PROVIDER_CLASS_NAME" android:name="com.google.android.gms.cast.framework.OPTIONS_PROVIDER_CLASS_NAME"

View file

@ -6,7 +6,7 @@
word-wrap: break-word; word-wrap: break-word;
} }
div{ div {
margin: 20px 10px; margin: 20px 10px;
padding: 10px; padding: 10px;
border-radius: 10px; border-radius: 10px;
@ -16,6 +16,8 @@
h2 { h2 {
margin-block-end: 0rem; margin-block-end: 0rem;
margin-block-start: 0rem; margin-block-start: 0rem;
display: inline-block;
vertical-align: center;
} }
li { li {
@ -35,7 +37,6 @@
span { span {
font-size: 0.7rem; font-size: 0.7rem;
line-height: 0.7rem;
} }
h5 { h5 {
@ -47,58 +48,124 @@
margin: 10px 0px; margin: 10px 0px;
font-size: 1rem; font-size: 1rem;
} }
.tag {
border-radius: 5px;
margin-left: 10px;
padding: 5px;
display: inline-block;
vertical-align: top;
}
{style-placeholder} {style-placeholder}
</style> </style>
</head> </head>
<body> <body>
<h2>
<b>Clear the app if it crashes after updating</b>
</h2>
<div> <div>
<h5>Date</h5>
<h2>v5.1.0<span class="tag"><i>Beta</i></span></h2>
<h3>What's New</h3>
<ul>
<li><b>Material You</b></li>
<li>Going Edge-to-Edge</li>
<li>Added Backup & restore</li>
</ul>
<h3>Improved</h3>
<ul>
<li>Improved Crossfade</li>
</ul>
</div>
<div>
<h5>September 06, 2021</h5> <h5>September 06, 2021</h5>
<h2>v5.0.0</h2> <h2>v5.0.0</h2>
<h3>What's New</h3> <h3>What's New</h3>
<ul> <ul>
<li>Added Chromecast support</li> <li>Added ability to Remember last tab</li>
<li>Added animated icons</li> <li>Added Whitelisting songs</li>
<li>Added cross-fade (experimental)</li> <li>You can now browse SDCard from Folders Tab</li>
<li>Added ability to remember the last tab</li> </ul>
<li>Added whitelisting songs</li> </div>
<li>Added support for embedded synced lyrics</li> <div>
<li>Added lyrics editor for normal and synced lyrics</li> <h5>August 22, 2021</h5>
<li>Added playlist ordering</li> <h2>v4.4.0<span class="tag"><i>Beta</i></span></h2>
<li>Added search filters</li> <h3>What's New</h3>
<li>Added audio fade</li> <ul>
<li>Added Multi-select in album and artist details</li> <li>Added Crossfade</li>
<li>Added SD card from folders tab</li> <li>Multi-select in Album and Artist Details</li>
<li>Added Synced lyrics in all themes</li> <li>Albums now show Album Artists instead of artist of first song</li>
<li>Added Swipe anywhere to change the song</li>
<li>Added album artist</li>
<li> Albums now show album artists instead of artists of the first song</li>
</ul> </ul>
<h3>Fixed</h3> <h3>Fixed</h3>
<ul> <ul>
<li> Fixed playlist preview images</li> <li>Fixed Playlist Preview Images</li>
<li> Fixed language switching</li> </ul>
</div>
<div>
<h5>August 11, 2021</h5>
<h2>v4.2.30<span class="tag"><i>Beta</i></span></h2>
<h3>What's New</h3>
<ul>
<li>Revamped Playlist Tab</li>
<li>Revamped Genres Tab</li>
<li>Added support for embedded Synced Lyrics</li>
<li>Added animated icons</li>
</ul>
<h3>Fixed</h3>
<ul>
<li>Fixed Language Switching</li>
<li>Fixed some reported bugs</li>
</ul>
</div>
<div>
<h5>July 18, 2021</h5>
<h2>v4.2.020<span class="tag"><i>Beta</i></span></h2>
<h3>What's New</h3>
<ul>
<li>Added ChromeCast Support</li>
<li>Added Lyrics Editor for Normal and Synced Lyrics</li>
<li>Added Ripple Animation for Color Theme</li>
<li>Added Drag to seek in Tiny Theme</li>
<li>Added Playlist Order</li>
<li>Added Search Filters</li>
<li>Added Audio Fade</li>
<li>Synced Lyrics in all Themes</li>
<li>Swipe anywhere to change song</li>
</ul> </ul>
<h3>Improved</h3> <h3>Improved</h3>
<ul> <ul>
<li>Improved playlists tab</li> <li>Fixed Navigate by Album Artist</li>
<li> Improved genres tab</li> <li>Changed New Music Mix Actions</li>
<li>Improved Animations</li>
<li>And some minor bug fixes and improvements</li>
</ul>
</div>
<div>
<h5>October 12, 2020</h5>
<h2>v4.0.10</h2>
<h3>What's New</h3>
<ul>
<li>Re-built from scratch using MVVM Architecture and JetPack Components</li>
<li>New Material Design icon</li>
<li>Implemented a custom database for playlists</li>
<li>Added new Material Design motions</li>
<li>Bug fixes & performance improvements</li>
<li>Revamped Home tab UI</li>
<li>Android 11 support</li>
<li>And more!</li>
</ul>
</div>
<div>
<h5>April 30, 2020</h5>
<h2>v3.5.110<span class="tag"><i>Beta</i></span></h2>
<h3>What's New</h3>
<ul>
<li>Changed profile form image to icon</li>
<li>New what's new screen</li>
<li>Added In-App language changer, where you can select language</li>
</ul>
<h3>Improved</h3>
<ul>
<li>Improved loading of Songs, Albums, Artists, Genres, Playlists</li>
</ul> </ul>
</div> </div>
<!--<h3><span class="colorHeader">Bug fixes</span></h3>
<ul>
<li></li>
</ul>-->
</body> </body>

View file

@ -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.Constants.PRO_VERSION_PRODUCT_ID
import code.name.monkey.retromusic.appshortcuts.DynamicShortcutManager import code.name.monkey.retromusic.appshortcuts.DynamicShortcutManager
import com.anjlab.android.iab.v3.BillingProcessor import com.anjlab.android.iab.v3.BillingProcessor
import com.anjlab.android.iab.v3.TransactionDetails import com.anjlab.android.iab.v3.PurchaseInfo
import org.koin.android.ext.koin.androidContext import org.koin.android.ext.koin.androidContext
import org.koin.core.context.startKoin import org.koin.core.context.startKoin
@ -49,9 +49,10 @@ class App : Application() {
DynamicShortcutManager(this).initDynamicShortcuts() DynamicShortcutManager(this).initDynamicShortcuts()
// automatically restores purchases // automatically restores purchases
billingProcessor = BillingProcessor(this, BuildConfig.GOOGLE_PLAY_LICENSING_KEY, billingProcessor = BillingProcessor(
this, BuildConfig.GOOGLE_PLAY_LICENSING_KEY,
object : BillingProcessor.IBillingHandler { object : BillingProcessor.IBillingHandler {
override fun onProductPurchased(productId: String, details: TransactionDetails?) {} override fun onProductPurchased(productId: String, details: PurchaseInfo?) {}
override fun onPurchaseHistoryRestored() { override fun onPurchaseHistoryRestored() {
Toast.makeText( Toast.makeText(

View file

@ -148,3 +148,4 @@ const val SHOW_LYRICS = "show_lyrics"
const val REMEMBER_LAST_TAB = "remember_last_tab" const val REMEMBER_LAST_TAB = "remember_last_tab"
const val LAST_USED_TAB = "last_used_tab" const val LAST_USED_TAB = "last_used_tab"
const val WHITELIST_MUSIC = "whitelist_music" const val WHITELIST_MUSIC = "whitelist_music"
const val MATERIAL_YOU = "material_you"

View file

@ -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(
"<h1>Unable to load</h1><p>" + e.getLocalizedMessage() + "</p>", "text/html", "UTF-8");
}
}
@Override
public boolean onOptionsItemSelected(@NonNull MenuItem item) {
if (item.getItemId() == android.R.id.home) {
onBackPressed();
return true;
}
return super.onOptionsItemSelected(item);
}
private String colorToCSS(int color) {
return String.format(
"rgb(%d, %d, %d)",
Color.red(color),
Color.green(color),
Color.blue(color)); // on API 29, WebView doesn't load with hex colors
}
}

View file

@ -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<Toolbar>(R.id.toolbar)
setSupportActionBar(toolbar)
ToolbarContentTintHelper.colorBackButton(toolbar)
toolbar.setBackgroundColor(resolveColor(this, R.attr.colorSurface))
val webView = findViewById<WebView>(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(
"<h1>Unable to load</h1><p>" + e.localizedMessage + "</p>", "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
}
}

View file

@ -48,9 +48,7 @@ class LockScreenActivity : AbsMusicServiceActivity() {
setContentView(binding.root) setContentView(binding.root)
hideStatusBar() hideStatusBar()
setStatusbarColorAuto() setStatusbarColorAuto()
setNavigationbarColorAuto()
setTaskDescriptionColorAuto() setTaskDescriptionColorAuto()
setLightNavigationBar(true)
val config = SlidrConfig.Builder().listener(object : SlidrListener { val config = SlidrConfig.Builder().listener(object : SlidrListener {
override fun onSlideStateChanged(state: Int) { override fun onSlideStateChanged(state: Int) {

View file

@ -21,10 +21,12 @@ import android.net.Uri
import android.os.Bundle import android.os.Bundle
import android.provider.MediaStore import android.provider.MediaStore
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import androidx.navigation.contains
import androidx.navigation.ui.setupWithNavController import androidx.navigation.ui.setupWithNavController
import code.name.monkey.retromusic.* import code.name.monkey.retromusic.*
import code.name.monkey.retromusic.activities.base.AbsCastActivity import code.name.monkey.retromusic.activities.base.AbsCastActivity
import code.name.monkey.retromusic.databinding.SlidingMusicPanelLayoutBinding 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.extra
import code.name.monkey.retromusic.extensions.findNavController import code.name.monkey.retromusic.extensions.findNavController
import code.name.monkey.retromusic.fragments.base.AbsRecyclerViewFragment import code.name.monkey.retromusic.fragments.base.AbsRecyclerViewFragment
@ -54,9 +56,6 @@ class MainActivity : AbsCastActivity(), OnSharedPreferenceChangeListener {
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
setDrawUnderStatusBar() setDrawUnderStatusBar()
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
setStatusbarColorAuto()
setNavigationbarColorAuto()
setLightNavigationBar(true)
setTaskDescriptionColorAuto() setTaskDescriptionColorAuto()
hideStatusBar() hideStatusBar()
updateTabs() updateTabs()
@ -75,6 +74,8 @@ class MainActivity : AbsCastActivity(), OnSharedPreferenceChangeListener {
val categoryInfo: CategoryInfo = PreferenceUtil.libraryCategory.first { it.visible } val categoryInfo: CategoryInfo = PreferenceUtil.libraryCategory.first { it.visible }
if (categoryInfo.visible) { if (categoryInfo.visible) {
if (!navGraph.contains(PreferenceUtil.lastTab)) PreferenceUtil.lastTab =
categoryInfo.category.id
navGraph.setStartDestination( navGraph.setStartDestination(
if (PreferenceUtil.rememberLastTab) { if (PreferenceUtil.rememberLastTab) {
PreferenceUtil.lastTab.let { PreferenceUtil.lastTab.let {
@ -88,10 +89,10 @@ class MainActivity : AbsCastActivity(), OnSharedPreferenceChangeListener {
) )
} }
navController.graph = navGraph navController.graph = navGraph
getBottomNavigationView().setupWithNavController(navController) bottomNavigationView.setupWithNavController(navController)
// Scroll Fragment to top // Scroll Fragment to top
getBottomNavigationView().setOnItemReselectedListener { bottomNavigationView.setOnItemReselectedListener {
supportFragmentManager.findFragmentById(R.id.fragment_container)?.childFragmentManager?.fragments?.get(0) currentFragment(R.id.fragment_container)
.also { .also {
if (it is AbsRecyclerViewFragment<*, *>) { if (it is AbsRecyclerViewFragment<*, *>) {
it.scrollToTop() 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, _ -> 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 -> { 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 // Save the last tab
if (PreferenceUtil.rememberLastTab) { if (PreferenceUtil.rememberLastTab) {
saveTab(destination.id) saveTab(destination.id)
} }
// Show Bottom Navigation Bar // 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) PreferenceUtil.registerOnSharedPreferenceChangedListener(this)
val expand = extra<Boolean>(EXPAND_PANEL).value ?: false val expand = extra<Boolean>(EXPAND_PANEL).value ?: false
if (expand && PreferenceUtil.isExpandPanel) { if (expand && PreferenceUtil.isExpandPanel) {
setBottomBarVisibility(false) setBottomNavVisibility(false)
fromNotification = true fromNotification = true
expandPanel() expandPanel()
intent.removeExtra(EXPAND_PANEL) intent.removeExtra(EXPAND_PANEL)
@ -142,7 +149,7 @@ class MainActivity : AbsCastActivity(), OnSharedPreferenceChangeListener {
} }
override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences?, key: String?) { 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() postRecreate()
} }
} }

View file

@ -41,8 +41,6 @@ class PermissionActivity : AbsMusicServiceActivity() {
binding = ActivityPermissionBinding.inflate(layoutInflater) binding = ActivityPermissionBinding.inflate(layoutInflater)
setContentView(binding.root) setContentView(binding.root)
setStatusbarColorAuto() setStatusbarColorAuto()
setNavigationbarColorAuto()
setLightNavigationBar(true)
setTaskDescriptionColorAuto() setTaskDescriptionColorAuto()
setupTitle() setupTitle()

View file

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

View file

@ -14,10 +14,8 @@
*/ */
package code.name.monkey.retromusic.activities package code.name.monkey.retromusic.activities
import android.content.Intent
import android.content.res.ColorStateList import android.content.res.ColorStateList
import android.graphics.Color import android.graphics.Color
import android.os.AsyncTask
import android.os.Bundle import android.os.Bundle
import android.util.Log import android.util.Log
import android.view.MenuItem 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.activities.base.AbsBaseActivity
import code.name.monkey.retromusic.databinding.ActivityProVersionBinding import code.name.monkey.retromusic.databinding.ActivityProVersionBinding
import com.anjlab.android.iab.v3.BillingProcessor import com.anjlab.android.iab.v3.BillingProcessor
import com.anjlab.android.iab.v3.TransactionDetails import com.anjlab.android.iab.v3.PurchaseInfo
import java.lang.ref.WeakReference
class PurchaseActivity : AbsBaseActivity(), BillingProcessor.IBillingHandler { class PurchaseActivity : AbsBaseActivity(), BillingProcessor.IBillingHandler {
private lateinit var binding: ActivityProVersionBinding private lateinit var binding: ActivityProVersionBinding
private lateinit var billingProcessor: BillingProcessor private lateinit var billingProcessor: BillingProcessor
private var restorePurchaseAsyncTask: AsyncTask<*, *, *>? = null
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
setDrawUnderStatusBar() setDrawUnderStatusBar()
@ -47,8 +43,6 @@ class PurchaseActivity : AbsBaseActivity(), BillingProcessor.IBillingHandler {
setContentView(binding.root) setContentView(binding.root)
setStatusbarColor(Color.TRANSPARENT) setStatusbarColor(Color.TRANSPARENT)
setLightStatusbar(false) setLightStatusbar(false)
setNavigationbarColor(Color.BLACK)
setLightNavigationBar(false)
binding.toolbar.navigationIcon?.setTint(Color.WHITE) binding.toolbar.navigationIcon?.setTint(Color.WHITE)
binding.toolbar.setNavigationOnClickListener { onBackPressed() } binding.toolbar.setNavigationOnClickListener { onBackPressed() }
@ -60,9 +54,7 @@ class PurchaseActivity : AbsBaseActivity(), BillingProcessor.IBillingHandler {
MaterialUtil.setTint(binding.purchaseButton, true) MaterialUtil.setTint(binding.purchaseButton, true)
binding.restoreButton.setOnClickListener { binding.restoreButton.setOnClickListener {
if (restorePurchaseAsyncTask == null || restorePurchaseAsyncTask!!.status != AsyncTask.Status.RUNNING) { restorePurchase()
restorePurchase()
}
} }
binding.purchaseButton.setOnClickListener { binding.purchaseButton.setOnClickListener {
billingProcessor.purchase(this@PurchaseActivity, PRO_VERSION_PRODUCT_ID) billingProcessor.purchase(this@PurchaseActivity, PRO_VERSION_PRODUCT_ID)
@ -72,13 +64,25 @@ class PurchaseActivity : AbsBaseActivity(), BillingProcessor.IBillingHandler {
} }
private fun restorePurchase() { private fun restorePurchase() {
if (restorePurchaseAsyncTask != null) { Toast.makeText(this, R.string.restoring_purchase, Toast.LENGTH_SHORT)
restorePurchaseAsyncTask!!.cancel(false) .show()
} billingProcessor.loadOwnedPurchasesFromGoogleAsync(object :
restorePurchaseAsyncTask = RestorePurchaseAsyncTask(this).execute() 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() Toast.makeText(this, R.string.thank_you, Toast.LENGTH_SHORT).show()
setResult(RESULT_OK) setResult(RESULT_OK)
} }
@ -105,12 +109,6 @@ class PurchaseActivity : AbsBaseActivity(), BillingProcessor.IBillingHandler {
binding.purchaseButton.isEnabled = true 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 { override fun onOptionsItemSelected(item: MenuItem): Boolean {
when (item.itemId) { when (item.itemId) {
android.R.id.home -> finish() android.R.id.home -> finish()
@ -123,52 +121,6 @@ class PurchaseActivity : AbsBaseActivity(), BillingProcessor.IBillingHandler {
super.onDestroy() super.onDestroy()
} }
private class RestorePurchaseAsyncTask(purchaseActivity: PurchaseActivity) :
AsyncTask<Void, Void, Boolean>() {
private val buyActivityWeakReference: WeakReference<PurchaseActivity> = WeakReference(
purchaseActivity
)
override fun onPreExecute() {
super.onPreExecute()
val purchaseActivity = buyActivityWeakReference.get()
if (purchaseActivity != null) {
Toast.makeText(purchaseActivity, R.string.restoring_purchase, Toast.LENGTH_SHORT)
.show()
} else {
cancel(false)
}
}
override fun doInBackground(vararg params: Void): Boolean? {
val purchaseActivity = buyActivityWeakReference.get()
if (purchaseActivity != null) {
return purchaseActivity.billingProcessor.loadOwnedPurchasesFromGoogle()
}
cancel(false)
return null
}
override fun onPostExecute(b: Boolean?) {
super.onPostExecute(b)
val purchaseActivity = buyActivityWeakReference.get()
if (purchaseActivity == null || b == null) {
return
}
if (b) {
purchaseActivity.onPurchaseHistoryRestored()
} else {
Toast.makeText(
purchaseActivity,
R.string.could_not_restore_purchase,
Toast.LENGTH_SHORT
).show()
}
}
}
companion object { companion object {
private const val TAG: String = "PurchaseActivity" private const val TAG: String = "PurchaseActivity"
} }

View file

@ -14,6 +14,7 @@
*/ */
package code.name.monkey.retromusic.activities package code.name.monkey.retromusic.activities
import android.content.Intent
import android.os.Bundle import android.os.Bundle
import android.view.MenuItem import android.view.MenuItem
import androidx.navigation.NavController import androidx.navigation.NavController
@ -21,33 +22,33 @@ import androidx.navigation.NavDestination
import code.name.monkey.appthemehelper.ThemeStore import code.name.monkey.appthemehelper.ThemeStore
import code.name.monkey.appthemehelper.util.VersionUtils import code.name.monkey.appthemehelper.util.VersionUtils
import code.name.monkey.retromusic.R import code.name.monkey.retromusic.R
import code.name.monkey.retromusic.activities.base.AbsBaseActivity import code.name.monkey.retromusic.activities.base.AbsThemeActivity
import code.name.monkey.retromusic.appshortcuts.DynamicShortcutManager import code.name.monkey.retromusic.appshortcuts.DynamicShortcutManager
import code.name.monkey.retromusic.databinding.ActivitySettingsBinding import code.name.monkey.retromusic.databinding.ActivitySettingsBinding
import code.name.monkey.retromusic.extensions.applyToolbar 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.findNavController
import code.name.monkey.retromusic.extensions.surfaceColor
import com.afollestad.materialdialogs.MaterialDialog import com.afollestad.materialdialogs.MaterialDialog
import com.afollestad.materialdialogs.color.ColorCallback import com.afollestad.materialdialogs.color.ColorCallback
class SettingsActivity : AbsBaseActivity(), ColorCallback { class SettingsActivity : AbsThemeActivity(), ColorCallback, OnThemeChangedListener {
private lateinit var binding: ActivitySettingsBinding private lateinit var binding: ActivitySettingsBinding
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
setDrawUnderStatusBar() setDrawUnderStatusBar()
super.onCreate(savedInstanceState) val mSavedInstanceState = extra<Bundle>(TAG).value ?: savedInstanceState
super.onCreate(mSavedInstanceState)
setLightStatusbarAuto(surfaceColor())
binding = ActivitySettingsBinding.inflate(layoutInflater) binding = ActivitySettingsBinding.inflate(layoutInflater)
setContentView(binding.root) setContentView(binding.root)
setStatusbarColorAuto()
setNavigationbarColorAuto()
setLightNavigationBar(true)
setupToolbar() setupToolbar()
} }
private fun setupToolbar() { private fun setupToolbar() {
setTitle(R.string.action_settings)
applyToolbar(binding.toolbar) applyToolbar(binding.toolbar)
val navController: NavController = findNavController(R.id.contentFrame) val navController: NavController = findNavController(R.id.contentFrame)
navController.addOnDestinationChangedListener { _, _, _ -> navController.addOnDestinationChangedListener { _, _, _ ->
binding.toolbar.title = binding.collapsingToolbarLayout.title =
navController.currentDestination?.let { getStringFromDestination(it) } navController.currentDestination?.let { getStringFromDestination(it) }
} }
} }
@ -63,6 +64,7 @@ class SettingsActivity : AbsBaseActivity(), ColorCallback {
R.id.personalizeSettingsFragment -> R.string.personalize R.id.personalizeSettingsFragment -> R.string.personalize
R.id.themeSettingsFragment -> R.string.general_settings_title R.id.themeSettingsFragment -> R.string.general_settings_title
R.id.aboutActivity -> R.string.action_about R.id.aboutActivity -> R.string.action_about
R.id.backup_fragment -> R.string.backup_restore_title
else -> R.id.action_settings else -> R.id.action_settings
} }
return getString(idRes) return getString(idRes)
@ -84,6 +86,28 @@ class SettingsActivity : AbsBaseActivity(), ColorCallback {
if (VersionUtils.hasNougatMR()) if (VersionUtils.hasNougatMR())
DynamicShortcutManager(this).updateDynamicShortcuts() 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()
}

View file

@ -61,7 +61,6 @@ class ShareInstagramStory : AbsBaseActivity() {
binding = ActivityShareInstagramBinding.inflate(layoutInflater) binding = ActivityShareInstagramBinding.inflate(layoutInflater)
setContentView(binding.root) setContentView(binding.root)
setStatusbarColor(Color.TRANSPARENT) setStatusbarColor(Color.TRANSPARENT)
setNavigationbarColor(Color.BLACK)
binding.toolbar.setBackgroundColor(Color.TRANSPARENT) binding.toolbar.setBackgroundColor(Color.TRANSPARENT)
setSupportActionBar(binding.toolbar) setSupportActionBar(binding.toolbar)

View file

@ -14,9 +14,7 @@
*/ */
package code.name.monkey.retromusic.activities package code.name.monkey.retromusic.activities
import android.content.Intent
import android.graphics.Paint import android.graphics.Paint
import android.os.AsyncTask
import android.os.Bundle import android.os.Bundle
import android.util.Log import android.util.Log
import android.view.LayoutInflater import android.view.LayoutInflater
@ -27,6 +25,7 @@ import android.widget.TextView
import android.widget.Toast import android.widget.Toast
import androidx.annotation.LayoutRes import androidx.annotation.LayoutRes
import androidx.appcompat.widget.AppCompatImageView import androidx.appcompat.widget.AppCompatImageView
import androidx.core.view.isVisible
import androidx.recyclerview.widget.DefaultItemAnimator import androidx.recyclerview.widget.DefaultItemAnimator
import androidx.recyclerview.widget.GridLayoutManager import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.RecyclerView 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.textColorPrimary
import code.name.monkey.retromusic.extensions.textColorSecondary import code.name.monkey.retromusic.extensions.textColorSecondary
import com.anjlab.android.iab.v3.BillingProcessor 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.SkuDetails
import com.anjlab.android.iab.v3.TransactionDetails
import java.lang.ref.WeakReference
import java.util.* import java.util.*
class SupportDevelopmentActivity : AbsBaseActivity(), BillingProcessor.IBillingHandler { class SupportDevelopmentActivity : AbsBaseActivity(), BillingProcessor.IBillingHandler {
@ -53,11 +51,9 @@ class SupportDevelopmentActivity : AbsBaseActivity(), BillingProcessor.IBillingH
companion object { companion object {
val TAG: String = SupportDevelopmentActivity::class.java.simpleName val TAG: String = SupportDevelopmentActivity::class.java.simpleName
const val DONATION_PRODUCT_IDS = R.array.donation_ids const val DONATION_PRODUCT_IDS = R.array.donation_ids
private const val TEZ_REQUEST_CODE = 123
} }
var billingProcessor: BillingProcessor? = null var billingProcessor: BillingProcessor? = null
private var skuDetailsLoadAsyncTask: AsyncTask<*, *, *>? = null
override fun onOptionsItemSelected(item: MenuItem): Boolean { override fun onOptionsItemSelected(item: MenuItem): Boolean {
if (item.itemId == android.R.id.home) { if (item.itemId == android.R.id.home) {
@ -75,12 +71,10 @@ class SupportDevelopmentActivity : AbsBaseActivity(), BillingProcessor.IBillingH
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
binding = ActivityDonationBinding.inflate(layoutInflater) binding = ActivityDonationBinding.inflate(layoutInflater)
setContentView(R.layout.activity_donation) setContentView(binding.root)
setStatusbarColorAuto() setStatusbarColorAuto()
setNavigationbarColorAuto()
setTaskDescriptionColorAuto() setTaskDescriptionColorAuto()
setLightNavigationBar(true)
setupToolbar() setupToolbar()
@ -101,13 +95,35 @@ class SupportDevelopmentActivity : AbsBaseActivity(), BillingProcessor.IBillingH
} }
private fun loadSkuDetails() { private fun loadSkuDetails() {
if (skuDetailsLoadAsyncTask != null) { binding.progressContainer.isVisible = true
skuDetailsLoadAsyncTask!!.cancel(false) binding.recyclerView.isVisible = false
} val ids =
skuDetailsLoadAsyncTask = SkuDetailsLoadAsyncTask(this).execute() resources.getStringArray(DONATION_PRODUCT_IDS)
billingProcessor!!.getPurchaseListingDetailsAsync(
ArrayList(listOf(*ids)),
object : BillingProcessor.ISkuDetailsResponseListener {
override fun onSkuDetailsResponse(skuDetails: MutableList<SkuDetails>?) {
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(); // loadSkuDetails();
Toast.makeText(this, R.string.thank_you, Toast.LENGTH_SHORT).show() 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() 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() { override fun onDestroy() {
billingProcessor?.release() billingProcessor?.release()
skuDetailsLoadAsyncTask?.cancel(true)
super.onDestroy() super.onDestroy()
} }
} }
private class SkuDetailsLoadAsyncTask(supportDevelopmentActivity: SupportDevelopmentActivity) :
AsyncTask<Void, Void, List<SkuDetails>>() {
private val weakReference: WeakReference<SupportDevelopmentActivity> = WeakReference(
supportDevelopmentActivity
)
override fun onPreExecute() {
super.onPreExecute()
val supportDevelopmentActivity = weakReference.get() ?: return
supportDevelopmentActivity.binding.progressContainer.visibility = View.VISIBLE
supportDevelopmentActivity.binding.recyclerView.visibility = View.GONE
}
override fun doInBackground(vararg params: Void): List<SkuDetails>? {
val dialog = weakReference.get()
if (dialog != null) {
val ids =
dialog.resources.getStringArray(SupportDevelopmentActivity.DONATION_PRODUCT_IDS)
return dialog.billingProcessor!!.getPurchaseListingDetails(ArrayList(Arrays.asList(*ids)))
}
cancel(false)
return null
}
override fun onPostExecute(skuDetails: List<SkuDetails>?) {
super.onPostExecute(skuDetails)
val dialog = weakReference.get() ?: return
if (skuDetails == null || skuDetails.isEmpty()) {
dialog.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( class SkuDetailsAdapter(
private var donationsDialog: SupportDevelopmentActivity, private var donationsDialog: SupportDevelopmentActivity,
objects: List<SkuDetails> objects: List<SkuDetails>
@ -223,7 +183,7 @@ class SkuDetailsAdapter(
viewHolder.title.text = skuDetails.title.replace("Music Player - MP3 Player - Retro", "") viewHolder.title.text = skuDetails.title.replace("Music Player - MP3 Player - Retro", "")
.trim { it <= ' ' } .trim { it <= ' ' }
viewHolder.text.text = skuDetails.description viewHolder.text.text = skuDetails.description
viewHolder.text.visibility = View.GONE viewHolder.text.isVisible = false
viewHolder.price.text = skuDetails.priceText viewHolder.price.text = skuDetails.priceText
viewHolder.image.setImageResource(getIcon(i)) viewHolder.image.setImageResource(getIcon(i))

View file

@ -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(
"<h1>Unable to load</h1><p>" + e.getLocalizedMessage() + "</p>", "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();
}
});
}
}

View file

@ -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(
"<h1>Unable to load</h1><p>" + e.localizedMessage + "</p>", "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()
}
}
}
}

View file

@ -14,13 +14,17 @@
*/ */
package code.name.monkey.retromusic.activities.base package code.name.monkey.retromusic.activities.base
import android.animation.Animator
import android.content.res.ColorStateList
import android.graphics.Color import android.graphics.Color
import android.os.Bundle import android.os.Bundle
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.view.ViewTreeObserver import android.view.ViewTreeObserver
import android.widget.FrameLayout import android.widget.FrameLayout
import androidx.core.animation.doOnEnd
import androidx.core.view.ViewCompat import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
import androidx.core.view.isVisible import androidx.core.view.isVisible
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.fragment.app.commit 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.appthemehelper.util.ColorUtil
import code.name.monkey.retromusic.R import code.name.monkey.retromusic.R
import code.name.monkey.retromusic.RetroBottomSheetBehavior 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.databinding.SlidingMusicPanelLayoutBinding
import code.name.monkey.retromusic.extensions.* import code.name.monkey.retromusic.extensions.*
import code.name.monkey.retromusic.fragments.LibraryViewModel 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.NowPlayingScreen.* import code.name.monkey.retromusic.fragments.NowPlayingScreen.*
import code.name.monkey.retromusic.fragments.base.AbsPlayerFragment 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.adaptive.AdaptiveFragment
import code.name.monkey.retromusic.fragments.player.blur.BlurPlayerFragment import code.name.monkey.retromusic.fragments.player.blur.BlurPlayerFragment
import code.name.monkey.retromusic.fragments.player.card.CardFragment import code.name.monkey.retromusic.fragments.player.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.plain.PlainPlayerFragment
import code.name.monkey.retromusic.fragments.player.simple.SimplePlayerFragment import code.name.monkey.retromusic.fragments.player.simple.SimplePlayerFragment
import code.name.monkey.retromusic.fragments.player.tiny.TinyPlayerFragment 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.helper.MusicPlayerRemote
import code.name.monkey.retromusic.model.CategoryInfo import code.name.monkey.retromusic.model.CategoryInfo
import code.name.monkey.retromusic.util.PreferenceUtil import code.name.monkey.retromusic.util.PreferenceUtil
import code.name.monkey.retromusic.views.BottomNavigationBarTinted import code.name.monkey.retromusic.util.RetroUtil
import com.google.android.material.bottomsheet.BottomSheetBehavior.* import com.google.android.material.bottomsheet.BottomSheetBehavior.*
import org.koin.androidx.viewmodel.ext.android.viewModel import org.koin.androidx.viewmodel.ext.android.viewModel
@ -66,15 +70,14 @@ abstract class AbsSlidingMusicPanelActivity : AbsMusicServiceActivity() {
var fromNotification: Boolean = false var fromNotification: Boolean = false
} }
private var windowInsets: WindowInsetsCompat? = null
private var bottomNavAnimator: Animator? = null
protected val libraryViewModel by viewModel<LibraryViewModel>() protected val libraryViewModel by viewModel<LibraryViewModel>()
private lateinit var bottomSheetBehavior: RetroBottomSheetBehavior<FrameLayout> private lateinit var bottomSheetBehavior: RetroBottomSheetBehavior<FrameLayout>
private var playerFragment: AbsPlayerFragment? = null private var playerFragment: AbsPlayerFragment? = null
private var miniPlayerFragment: MiniPlayerFragment? = null private var miniPlayerFragment: MiniPlayerFragment? = null
private var nowPlayingScreen: NowPlayingScreen? = null private var nowPlayingScreen: NowPlayingScreen? = null
private var navigationBarColor: Int = 0
private var taskColor: Int = 0 private var taskColor: Int = 0
private var lightStatusBar: Boolean = false
private var lightNavigationBar: Boolean = false
private var paletteColor: Int = Color.WHITE private var paletteColor: Int = Color.WHITE
protected abstract fun createContentView(): SlidingMusicPanelLayoutBinding protected abstract fun createContentView(): SlidingMusicPanelLayoutBinding
private val panelState: Int private val panelState: Int
@ -82,11 +85,8 @@ abstract class AbsSlidingMusicPanelActivity : AbsMusicServiceActivity() {
private lateinit var binding: SlidingMusicPanelLayoutBinding private lateinit var binding: SlidingMusicPanelLayoutBinding
private val bottomSheetCallbackList = object : BottomSheetCallback() { private val bottomSheetCallbackList = object : BottomSheetCallback() {
override fun onSlide(bottomSheet: View, slideOffset: Float) { override fun onSlide(bottomSheet: View, slideOffset: Float) {
setMiniPlayerAlphaProgress(slideOffset) setMiniPlayerAlphaProgress(slideOffset)
binding.dimBackground.show()
binding.dimBackground.alpha = slideOffset
} }
override fun onStateChanged(bottomSheet: View, newState: Int) { override fun onStateChanged(bottomSheet: View, newState: Int) {
@ -96,15 +96,14 @@ abstract class AbsSlidingMusicPanelActivity : AbsMusicServiceActivity() {
} }
STATE_COLLAPSED -> { STATE_COLLAPSED -> {
onPanelCollapsed() onPanelCollapsed()
binding.dimBackground.hide()
if (fromNotification) { if (fromNotification) {
hideBottomBar(MusicPlayerRemote.playingQueue.isEmpty()) hideBottomSheet(MusicPlayerRemote.playingQueue.isEmpty())
fromNotification = false fromNotification = false
} }
} }
STATE_SETTLING, STATE_DRAGGING -> { STATE_SETTLING, STATE_DRAGGING -> {
if (fromNotification) { if (fromNotification) {
getBottomNavigationView().isVisible = true bottomNavigationView.isVisible = true
} }
} }
else -> { else -> {
@ -120,23 +119,30 @@ abstract class AbsSlidingMusicPanelActivity : AbsMusicServiceActivity() {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
binding = createContentView() binding = createContentView()
setContentView(binding.root) setContentView(binding.root)
ViewCompat.setOnApplyWindowInsetsListener(
binding.root
) { _, insets ->
windowInsets = insets
insets
}
bottomNavigationView.drawAboveSystemBarsWithPadding()
if (RetroUtil.isLandscape()) {
binding.slidingPanel.drawAboveSystemBarsWithPadding(true)
}
binding.fragmentContainer.addBottomInsets()
chooseFragmentForTheme() chooseFragmentForTheme()
setupSlidingUpPanel() setupSlidingUpPanel()
setupBottomSheet() setupBottomSheet()
updateColor() updateColor()
binding.slidingPanel.backgroundTintList = ColorStateList.valueOf(darkAccentColor())
val themeColor = resolveColor(android.R.attr.windowBackground, Color.GRAY) bottomNavigationView.backgroundTintList = ColorStateList.valueOf(darkAccentColor())
binding.dimBackground.setBackgroundColor(ColorUtil.withAlpha(themeColor, 0.5f))
binding.dimBackground.setOnClickListener {
println("dimBackground")
collapsePanel()
}
} }
private fun setupBottomSheet() { private fun setupBottomSheet() {
bottomSheetBehavior = from(binding.slidingPanel) as RetroBottomSheetBehavior bottomSheetBehavior = from(binding.slidingPanel) as RetroBottomSheetBehavior
bottomSheetBehavior.addBottomSheetCallback(bottomSheetCallbackList) bottomSheetBehavior.addBottomSheetCallback(bottomSheetCallbackList)
bottomSheetBehavior.maxWidth = resources.displayMetrics.widthPixels bottomSheetBehavior.isHideable = false
setMiniPlayerAlphaProgress(0F)
} }
override fun onResume() { override fun onResume() {
@ -155,41 +161,37 @@ abstract class AbsSlidingMusicPanelActivity : AbsMusicServiceActivity() {
} }
protected fun wrapSlidingMusicPanel(): SlidingMusicPanelLayoutBinding { protected fun wrapSlidingMusicPanel(): SlidingMusicPanelLayoutBinding {
val slidingMusicPanelLayoutBinding = return SlidingMusicPanelLayoutBinding.inflate(layoutInflater)
SlidingMusicPanelLayoutBinding.inflate(layoutInflater)
val contentContainer: ViewGroup =
slidingMusicPanelLayoutBinding.mainContentFrame
ActivityMainContentBinding.inflate(layoutInflater, contentContainer, true)
return slidingMusicPanelLayoutBinding
} }
fun collapsePanel() { fun collapsePanel() {
bottomSheetBehavior.state = STATE_COLLAPSED bottomSheetBehavior.state = STATE_COLLAPSED
setMiniPlayerAlphaProgress(0f)
} }
fun expandPanel() { fun expandPanel() {
bottomSheetBehavior.state = STATE_EXPANDED bottomSheetBehavior.state = STATE_EXPANDED
setMiniPlayerAlphaProgress(1f)
} }
private fun setMiniPlayerAlphaProgress(progress: Float) { private fun setMiniPlayerAlphaProgress(progress: Float) {
if (progress < 0) return
val alpha = 1 - progress 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 miniPlayerFragment?.view?.visibility = if (alpha == 0f) View.GONE else View.VISIBLE
binding.bottomNavigationView.translationY = progress * 500 binding.bottomNavigationView.translationY = progress * 500
binding.bottomNavigationView.alpha = alpha binding.bottomNavigationView.alpha = alpha
binding.playerFragmentContainer.alpha = (progress - 0.2F) / 0.2F
} }
open fun onPanelCollapsed() { open fun onPanelCollapsed() {
setMiniPlayerAlphaProgress(0F)
// restore values // restore values
super.setLightStatusbar(lightStatusBar) super.setLightStatusbarAuto(surfaceColor())
super.setLightNavigationAuto()
super.setTaskDescriptionColor(taskColor) super.setTaskDescriptionColor(taskColor)
super.setNavigationbarColor(navigationBarColor)
super.setLightNavigationBar(lightNavigationBar)
} }
open fun onPanelExpanded() { open fun onPanelExpanded() {
setMiniPlayerAlphaProgress(1F)
onPaletteColorChanged() onPaletteColorChanged()
} }
@ -214,9 +216,7 @@ abstract class AbsSlidingMusicPanelActivity : AbsMusicServiceActivity() {
}) })
} }
fun getBottomNavigationView(): BottomNavigationBarTinted { val bottomNavigationView get() = binding.bottomNavigationView
return binding.bottomNavigationView
}
override fun onServiceConnected() { override fun onServiceConnected() {
super.onServiceConnected() super.onServiceConnected()
@ -225,15 +225,19 @@ abstract class AbsSlidingMusicPanelActivity : AbsMusicServiceActivity() {
ViewTreeObserver.OnGlobalLayoutListener { ViewTreeObserver.OnGlobalLayoutListener {
override fun onGlobalLayout() { override fun onGlobalLayout() {
binding.slidingPanel.viewTreeObserver.removeOnGlobalLayoutListener(this) 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() { override fun onQueueChanged() {
super.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() { override fun onBackPressed() {
@ -260,13 +264,10 @@ abstract class AbsSlidingMusicPanelActivity : AbsMusicServiceActivity() {
} else if (nowPlayingScreen == Card || nowPlayingScreen == Blur || nowPlayingScreen == BlurCard) { } else if (nowPlayingScreen == Card || nowPlayingScreen == Blur || nowPlayingScreen == BlurCard) {
super.setLightStatusbar(false) super.setLightStatusbar(false)
super.setLightNavigationBar(true) super.setLightNavigationBar(true)
super.setNavigationbarColor(Color.BLACK)
} else if (nowPlayingScreen == Color || nowPlayingScreen == Tiny || nowPlayingScreen == Gradient) { } else if (nowPlayingScreen == Color || nowPlayingScreen == Tiny || nowPlayingScreen == Gradient) {
super.setNavigationbarColor(paletteColor)
super.setLightNavigationBar(isColorLight) super.setLightNavigationBar(isColorLight)
super.setLightStatusbar(isColorLight) super.setLightStatusbar(isColorLight)
} else if (nowPlayingScreen == Full) { } else if (nowPlayingScreen == Full) {
super.setNavigationbarColor(paletteColor)
super.setLightNavigationBar(isColorLight) super.setLightNavigationBar(isColorLight)
super.setLightStatusbar(false) super.setLightStatusbar(false)
} else if (nowPlayingScreen == Classic) { } 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) { override fun setTaskDescriptionColor(color: Int) {
taskColor = color taskColor = color
if (panelState == STATE_COLLAPSED) { 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 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 = val heightOfBar =
if (MusicPlayerRemote.isCasting) dip(R.dimen.cast_mini_player_height) else dip(R.dimen.mini_player_height) windowInsets.safeGetBottomInsets() +
val heightOfBarWithTabs = if (MusicPlayerRemote.isCasting) dip(R.dimen.cast_mini_player_height) else dip(R.dimen.mini_player_height)
if (MusicPlayerRemote.isCasting) dip(R.dimen.mini_cast_player_height_expanded) else dip( val heightOfBarWithTabs = heightOfBar + dip(R.dimen.bottom_nav_height)
R.dimen.mini_player_height_expanded
)
val isVisible = binding.bottomNavigationView.isVisible val isVisible = binding.bottomNavigationView.isVisible
if (hide) { if (hide) {
bottomSheetBehavior.isHideable = true bottomSheetBehavior.peekHeight = -windowInsets.safeGetBottomInsets()
bottomSheetBehavior.peekHeight = 0 bottomSheetBehavior.state = STATE_COLLAPSED
libraryViewModel.setFabMargin(if (isVisible) dip(R.dimen.bottom_nav_height) else 0)
ViewCompat.setElevation(binding.slidingPanel, 0f) ViewCompat.setElevation(binding.slidingPanel, 0f)
ViewCompat.setElevation(binding.bottomNavigationView, 10f) ViewCompat.setElevation(binding.bottomNavigationView, 10f)
collapsePanel()
} else { } else {
if (MusicPlayerRemote.playingQueue.isNotEmpty()) { if (MusicPlayerRemote.playingQueue.isNotEmpty()) {
bottomSheetBehavior.isHideable = false
ViewCompat.setElevation(binding.slidingPanel, 10f) ViewCompat.setElevation(binding.slidingPanel, 10f)
ViewCompat.setElevation(binding.bottomNavigationView, 10f) ViewCompat.setElevation(binding.bottomNavigationView, 10f)
if (isVisible) { if (isVisible) {
println("List") println("List")
if (bottomSheetBehavior.state != STATE_EXPANDED) if (animate) {
getBottomNavigationView().translateYAnimate(0F) bottomNavAnimator?.end()
bottomSheetBehavior.peekHeightAnimate(heightOfBarWithTabs) 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 { } else {
println("Details") 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) { fun setAllowDragging(allowDragging: Boolean) {
bottomSheetBehavior.setAllowDragging(allowDragging) bottomSheetBehavior.setAllowDragging(allowDragging)
hideBottomBar(false) hideBottomSheet(false)
} }
private fun chooseFragmentForTheme() { private fun chooseFragmentForTheme() {

View file

@ -33,9 +33,11 @@ import code.name.monkey.appthemehelper.util.ColorUtil
import code.name.monkey.appthemehelper.util.VersionUtils import code.name.monkey.appthemehelper.util.VersionUtils
import code.name.monkey.retromusic.LanguageContextWrapper import code.name.monkey.retromusic.LanguageContextWrapper
import code.name.monkey.retromusic.R 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.PreferenceUtil
import code.name.monkey.retromusic.util.RetroUtil import code.name.monkey.retromusic.util.RetroUtil
import code.name.monkey.retromusic.util.theme.ThemeManager import code.name.monkey.retromusic.util.theme.ThemeManager
import com.google.android.material.color.DynamicColors
import java.util.* import java.util.*
abstract class AbsThemeActivity : ATHToolbarActivity(), Runnable { abstract class AbsThemeActivity : ATHToolbarActivity(), Runnable {
@ -49,12 +51,22 @@ abstract class AbsThemeActivity : ATHToolbarActivity(), Runnable {
setImmersiveFullscreen() setImmersiveFullscreen()
registerSystemUiVisibility() registerSystemUiVisibility()
toggleScreenOn() toggleScreenOn()
//MaterialDialogsUtil.updateMaterialDialogsThemeSingleton(this) setDrawUnderNavigationBar()
setLightNavigationAuto()
setLightStatusbarAuto(surfaceColor())
} }
private fun updateTheme() { private fun updateTheme() {
setTheme(ThemeManager.getThemeResValue(this)) setTheme(ThemeManager.getThemeResValue(this))
setDefaultNightMode(ThemeManager.getNightMode(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() { private fun toggleScreenOn() {
@ -91,7 +103,7 @@ abstract class AbsThemeActivity : ATHToolbarActivity(), Runnable {
RetroUtil.setAllowDrawUnderStatusBar(window) RetroUtil.setAllowDrawUnderStatusBar(window)
} }
fun setDrawUnderNavigationBar() { private fun setDrawUnderNavigationBar() {
RetroUtil.setAllowDrawUnderNavigationBar(window) RetroUtil.setAllowDrawUnderNavigationBar(window)
} }
@ -148,6 +160,10 @@ abstract class AbsThemeActivity : ATHToolbarActivity(), Runnable {
setNavigationbarColor(ATHUtil.resolveColor(this, R.attr.colorSurface)) setNavigationbarColor(ATHUtil.resolveColor(this, R.attr.colorSurface))
} }
fun setLightNavigationAuto() {
ATH.setLightNavigationbarAuto(this, surfaceColor())
}
open fun setLightStatusbar(enabled: Boolean) { open fun setLightStatusbar(enabled: Boolean) {
ATH.setLightStatusbar(this, enabled) ATH.setLightStatusbar(this, enabled)
} }

View file

@ -30,7 +30,6 @@ import androidx.annotation.StringDef
import androidx.annotation.StringRes import androidx.annotation.StringRes
import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AlertDialog
import code.name.monkey.appthemehelper.ThemeStore import code.name.monkey.appthemehelper.ThemeStore
import code.name.monkey.appthemehelper.util.ATHUtil
import code.name.monkey.appthemehelper.util.MaterialUtil import code.name.monkey.appthemehelper.util.MaterialUtil
import code.name.monkey.appthemehelper.util.TintHelper import code.name.monkey.appthemehelper.util.TintHelper
import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper
@ -74,12 +73,9 @@ open class BugReportActivity : AbsThemeActivity() {
private var deviceInfo: DeviceInfo? = null private var deviceInfo: DeviceInfo? = null
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
setDrawUnderStatusBar()
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
binding = ActivityBugReportBinding.inflate(layoutInflater) binding = ActivityBugReportBinding.inflate(layoutInflater)
setContentView(binding.root) setContentView(binding.root)
setStatusbarColorAuto()
setNavigationbarColorAuto()
setTaskDescriptionColorAuto() setTaskDescriptionColorAuto()
initViews() initViews()
@ -92,8 +88,6 @@ open class BugReportActivity : AbsThemeActivity() {
private fun initViews() { private fun initViews() {
val accentColor = ThemeStore.accentColor(this) val accentColor = ThemeStore.accentColor(this)
val primaryColor = ATHUtil.resolveColor(this, R.attr.colorSurface)
binding.toolbar.setBackgroundColor(primaryColor)
setSupportActionBar(binding.toolbar) setSupportActionBar(binding.toolbar)
ToolbarContentTintHelper.colorBackButton(binding.toolbar) ToolbarContentTintHelper.colorBackButton(binding.toolbar)
supportActionBar?.setDisplayHomeAsUpEnabled(true) supportActionBar?.setDisplayHomeAsUpEnabled(true)

View file

@ -196,7 +196,6 @@ abstract class AbsTagEditorActivity<VB : ViewBinding> : AbsBaseActivity() {
_binding = bindingInflater.invoke(layoutInflater) _binding = bindingInflater.invoke(layoutInflater)
setContentView(binding.root) setContentView(binding.root)
setStatusbarColorAuto() setStatusbarColorAuto()
setNavigationbarColorAuto()
setTaskDescriptionColorAuto() setTaskDescriptionColorAuto()
saveFab = findViewById(R.id.saveTags) saveFab = findViewById(R.id.saveTags)

View file

@ -29,10 +29,10 @@ import android.view.LayoutInflater
import android.widget.ImageView import android.widget.ImageView
import android.widget.Toast import android.widget.Toast
import code.name.monkey.appthemehelper.util.ATHUtil 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.R
import code.name.monkey.retromusic.databinding.ActivityAlbumTagEditorBinding import code.name.monkey.retromusic.databinding.ActivityAlbumTagEditorBinding
import code.name.monkey.retromusic.extensions.appHandleColor 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.GlideApp
import code.name.monkey.retromusic.glide.palette.BitmapPaletteWrapper import code.name.monkey.retromusic.glide.palette.BitmapPaletteWrapper
import code.name.monkey.retromusic.model.ArtworkInfo import code.name.monkey.retromusic.model.ArtworkInfo
@ -62,8 +62,6 @@ class AlbumTagEditorActivity : AbsTagEditorActivity<ActivityAlbumTagEditorBindin
} }
override fun loadImageFromFile(selectedFile: Uri?) { override fun loadImageFromFile(selectedFile: Uri?) {
GlideApp.with(this@AlbumTagEditorActivity).asBitmapPalette().load(selectedFile) GlideApp.with(this@AlbumTagEditorActivity).asBitmapPalette().load(selectedFile)
.diskCacheStrategy(DiskCacheStrategy.NONE).skipMemoryCache(true) .diskCacheStrategy(DiskCacheStrategy.NONE).skipMemoryCache(true)
.into(object : ImageViewTarget<BitmapPaletteWrapper>(binding.editorImage) { .into(object : ImageViewTarget<BitmapPaletteWrapper>(binding.editorImage) {
@ -119,10 +117,10 @@ class AlbumTagEditorActivity : AbsTagEditorActivity<ActivityAlbumTagEditorBindin
private fun setUpViews() { private fun setUpViews() {
fillViewsWithFileTags() fillViewsWithFileTags()
MaterialUtil.setTint(binding.yearContainer, false) binding.yearContainer.setTint(false)
MaterialUtil.setTint(binding.genreContainer, false) binding.genreContainer.setTint(false)
MaterialUtil.setTint(binding.albumTitleContainer, false) binding.albumTitleContainer.setTint(false)
MaterialUtil.setTint(binding.albumArtistContainer, false) binding.albumArtistContainer.setTint(false)
binding.albumText.appHandleColor().addTextChangedListener(this) binding.albumText.appHandleColor().addTextChangedListener(this)
binding.albumArtistText.appHandleColor().addTextChangedListener(this) binding.albumArtistText.appHandleColor().addTextChangedListener(this)

View file

@ -22,10 +22,10 @@ import android.text.TextWatcher
import android.view.LayoutInflater import android.view.LayoutInflater
import android.widget.ImageView import android.widget.ImageView
import code.name.monkey.appthemehelper.util.ATHUtil 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.R
import code.name.monkey.retromusic.databinding.ActivitySongTagEditorBinding import code.name.monkey.retromusic.databinding.ActivitySongTagEditorBinding
import code.name.monkey.retromusic.extensions.appHandleColor import code.name.monkey.retromusic.extensions.appHandleColor
import code.name.monkey.retromusic.extensions.setTint
import code.name.monkey.retromusic.repository.SongRepository import code.name.monkey.retromusic.repository.SongRepository
import org.jaudiotagger.tag.FieldKey import org.jaudiotagger.tag.FieldKey
import org.koin.android.ext.android.inject import org.koin.android.ext.android.inject
@ -50,15 +50,15 @@ class SongTagEditorActivity : AbsTagEditorActivity<ActivitySongTagEditorBinding>
@SuppressLint("ClickableViewAccessibility") @SuppressLint("ClickableViewAccessibility")
private fun setUpViews() { private fun setUpViews() {
fillViewsWithFileTags() fillViewsWithFileTags()
MaterialUtil.setTint(binding.songTextContainer, false) binding.songTextContainer.setTint(false)
MaterialUtil.setTint(binding.composerContainer, false) binding.composerContainer.setTint(false)
MaterialUtil.setTint(binding.albumTextContainer, false) binding.albumTextContainer.setTint(false)
MaterialUtil.setTint(binding.artistContainer, false) binding.artistContainer.setTint(false)
MaterialUtil.setTint(binding.albumArtistContainer, false) binding.albumArtistContainer.setTint(false)
MaterialUtil.setTint(binding.yearContainer, false) binding.yearContainer.setTint(false)
MaterialUtil.setTint(binding.genreContainer, false) binding.genreContainer.setTint(false)
MaterialUtil.setTint(binding.trackNumberContainer, false) binding.trackNumberContainer.setTint(false)
MaterialUtil.setTint(binding.lyricsContainer, false) binding.lyricsContainer.setTint(false)
binding.songText.appHandleColor().addTextChangedListener(this) binding.songText.appHandleColor().addTextChangedListener(this)
binding.albumText.appHandleColor().addTextChangedListener(this) binding.albumText.appHandleColor().addTextChangedListener(this)

View file

@ -40,7 +40,7 @@ public class CategoryInfoAdapter extends RecyclerView.Adapter<CategoryInfoAdapte
implements SwipeAndDragHelper.ActionCompletionContract { implements SwipeAndDragHelper.ActionCompletionContract {
private List<CategoryInfo> categoryInfos; private List<CategoryInfo> categoryInfos;
private ItemTouchHelper touchHelper; private final ItemTouchHelper touchHelper;
public CategoryInfoAdapter() { public CategoryInfoAdapter() {
SwipeAndDragHelper swipeAndDragHelper = new SwipeAndDragHelper(this); SwipeAndDragHelper swipeAndDragHelper = new SwipeAndDragHelper(this);
@ -128,15 +128,15 @@ public class CategoryInfoAdapter extends RecyclerView.Adapter<CategoryInfoAdapte
} }
static class ViewHolder extends RecyclerView.ViewHolder { static class ViewHolder extends RecyclerView.ViewHolder {
private MaterialCheckBox checkBox; private final MaterialCheckBox checkBox;
private View dragView; private final View dragView;
private TextView title; private final TextView title;
ViewHolder(View view) { ViewHolder(View view) {
super(view); super(view);
checkBox = view.findViewById(R.id.checkbox); checkBox = view.findViewById(R.id.checkbox);
checkBox.setButtonTintList( checkBox.setButtonTintList(
ColorStateList.valueOf(ThemeStore.Companion.accentColor(checkBox.getContext()))); ColorStateList.valueOf(ThemeStore.Companion.accentColor(checkBox.getContext())));
title = view.findViewById(R.id.title); title = view.findViewById(R.id.title);
dragView = view.findViewById(R.id.drag_view); dragView = view.findViewById(R.id.drag_view);
} }

View file

@ -127,8 +127,8 @@ class SearchAdapter(
val artist = dataSet[position] as Artist val artist = dataSet[position] as Artist
holder.title?.text = artist.name holder.title?.text = artist.name
holder.text?.text = MusicUtil.getArtistInfoString(activity, artist) holder.text?.text = MusicUtil.getArtistInfoString(activity, artist)
GlideApp.with(activity).asDrawable().artistImageOptions(artist).load(artist) GlideApp.with(activity).asDrawable().artistImageOptions(artist).load(
.into(holder.image!!) RetroGlideExtension.getArtistModel(artist)).into(holder.image!!)
} }
else -> { else -> {
holder.title?.text = dataSet[position].toString() holder.title?.text = dataSet[position].toString()

View file

@ -40,7 +40,7 @@ import kotlin.math.log10
import kotlin.math.pow import kotlin.math.pow
class SongFileAdapter( class SongFileAdapter(
private val activity: AppCompatActivity, override val activity: AppCompatActivity,
private var dataSet: List<File>, private var dataSet: List<File>,
private val itemLayoutRes: Int, private val itemLayoutRes: Int,
private val iCallbacks: ICallbacks?, private val iCallbacks: ICallbacks?,

View file

@ -39,7 +39,7 @@ import code.name.monkey.retromusic.util.color.MediaNotificationProcessor
import me.zhanghai.android.fastscroll.PopupTextProvider import me.zhanghai.android.fastscroll.PopupTextProvider
open class AlbumAdapter( open class AlbumAdapter(
val activity: FragmentActivity, override val activity: FragmentActivity,
var dataSet: List<Album>, var dataSet: List<Album>,
var itemLayoutRes: Int, var itemLayoutRes: Int,
iCabHolder: ICabHolder?, iCabHolder: ICabHolder?,
@ -68,7 +68,7 @@ open class AlbumAdapter(
return ViewHolder(view) return ViewHolder(view)
} }
private fun getAlbumTitle(album: Album): String? { private fun getAlbumTitle(album: Album): String {
return album.title return album.title
} }
@ -88,7 +88,13 @@ open class AlbumAdapter(
holder.itemView.isActivated = isChecked holder.itemView.isActivated = isChecked
holder.title?.text = getAlbumTitle(album) holder.title?.text = getAlbumTitle(album)
holder.text?.text = getAlbumText(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) loadAlbumCover(album, holder)
} }
@ -130,7 +136,7 @@ open class AlbumAdapter(
} }
override fun getName(album: Album): String { override fun getName(album: Album): String {
return album.title!! return album.title
} }
override fun onMultipleItemAction( override fun onMultipleItemAction(
@ -177,7 +183,7 @@ open class AlbumAdapter(
toggleChecked(layoutPosition) toggleChecked(layoutPosition)
} else { } else {
image?.let { image?.let {
listener?.onAlbumClick(dataSet[layoutPosition].id, it) listener?.onAlbumClick(dataSet[layoutPosition].id, imageContainer ?: it)
} }
} }
} }

View file

@ -24,6 +24,7 @@ import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentManager import androidx.fragment.app.FragmentManager
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import code.name.monkey.retromusic.R 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.AlbumCoverStyle
import code.name.monkey.retromusic.fragments.NowPlayingScreen.* import code.name.monkey.retromusic.fragments.NowPlayingScreen.*
import code.name.monkey.retromusic.glide.GlideApp 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.NavigationUtil
import code.name.monkey.retromusic.util.PreferenceUtil import code.name.monkey.retromusic.util.PreferenceUtil
import code.name.monkey.retromusic.util.color.MediaNotificationProcessor 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 com.google.android.material.dialog.MaterialAlertDialogBuilder
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@ -89,6 +91,7 @@ class AlbumCoverPagerAdapter(
private lateinit var song: Song private lateinit var song: Song
private var colorReceiver: ColorReceiver? = null private var colorReceiver: ColorReceiver? = null
private var request: Int = 0 private var request: Int = 0
private val mainActivity get() = activity as MainActivity
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
@ -106,7 +109,9 @@ class AlbumCoverPagerAdapter(
ViewCompat.setTransitionName(view, "lyrics") ViewCompat.setTransitionName(view, "lyrics")
albumCover = view.findViewById(R.id.player_image) albumCover = view.findViewById(R.id.player_image)
view.setOnClickListener { view.setOnClickListener {
showLyricsDialog() if (mainActivity.getBottomSheetBehavior().state == STATE_EXPANDED) {
showLyricsDialog()
}
} }
return view return view
} }

View file

@ -42,7 +42,7 @@ import me.zhanghai.android.fastscroll.PopupTextProvider
import java.util.* import java.util.*
class ArtistAdapter( class ArtistAdapter(
val activity: FragmentActivity, override val activity: FragmentActivity,
var dataSet: List<Artist>, var dataSet: List<Artist>,
var itemLayoutRes: Int, var itemLayoutRes: Int,
val ICabHolder: ICabHolder?, val ICabHolder: ICabHolder?,
@ -85,12 +85,12 @@ class ArtistAdapter(
holder.itemView.isActivated = isChecked holder.itemView.isActivated = isChecked
holder.title?.text = artist.name holder.title?.text = artist.name
holder.text?.hide() holder.text?.hide()
holder.image?.let { val transitionName =
if (PreferenceUtil.albumArtistsOnly) { if (PreferenceUtil.albumArtistsOnly) artist.name else artist.id.toString()
ViewCompat.setTransitionName(it, artist.name) if (holder.imageContainer != null) {
} else { ViewCompat.setTransitionName(holder.imageContainer!!, transitionName)
ViewCompat.setTransitionName(it, artist.id.toString()) } else {
} ViewCompat.setTransitionName(holder.image!!, transitionName)
} }
loadArtistImage(artist, holder) loadArtistImage(artist, holder)
} }
@ -169,9 +169,9 @@ class ArtistAdapter(
val artist = dataSet[layoutPosition] val artist = dataSet[layoutPosition]
image?.let { image?.let {
if (PreferenceUtil.albumArtistsOnly && IAlbumArtistClickListener != null) { if (PreferenceUtil.albumArtistsOnly && IAlbumArtistClickListener != null) {
IAlbumArtistClickListener.onAlbumArtist(artist.name, it) IAlbumArtistClickListener.onAlbumArtist(artist.name, imageContainer ?: it)
} else { } else {
IArtistClickListener.onArtist(artist.id, it) IArtistClickListener.onArtist(artist.id, imageContainer ?: it)
} }
} }
} }

View file

@ -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<File>,
val backupClickedListener: BackupClickedListener
) : RecyclerView.Adapter<BackupAdapter.ViewHolder>() {
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<File>) {
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
}
}

View file

@ -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<V extends RecyclerView.ViewHolder, I>
extends RecyclerView.Adapter<V> implements MaterialCab.Callback {
@Nullable
private final ICabHolder ICabHolder;
private final Context context;
private MaterialCab cab;
private final List<I> 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<I> 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);
}
}

View file

@ -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<V : RecyclerView.ViewHolder?, I>(
open val activity: FragmentActivity, private val ICabHolder: ICabHolder?, @MenuRes menuRes: Int
) : RecyclerView.Adapter<V>(), ICabCallback {
private var cab: AttachedCab? = null
private val checked: MutableList<I>
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<I>)
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
}
}

View file

@ -16,13 +16,13 @@ package code.name.monkey.retromusic.adapter.base;
import android.graphics.Color; import android.graphics.Color;
import android.view.View; import android.view.View;
import android.widget.FrameLayout;
import android.widget.ImageView; import android.widget.ImageView;
import android.widget.TextView; import android.widget.TextView;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.appcompat.widget.AppCompatImageView; import androidx.appcompat.widget.AppCompatImageView;
import androidx.recyclerview.widget.RecyclerView;
import com.google.android.material.card.MaterialCardView; import com.google.android.material.card.MaterialCardView;
import com.h6ah4i.android.widget.advrecyclerview.utils.AbstractDraggableSwipeableItemViewHolder; import com.h6ah4i.android.widget.advrecyclerview.utils.AbstractDraggableSwipeableItemViewHolder;
@ -41,15 +41,12 @@ public class MediaEntryViewHolder extends AbstractDraggableSwipeableItemViewHold
@Nullable @Nullable
public ImageView image; public ImageView image;
@Nullable
public ImageView artistImage;
@Nullable
public ImageView playerImage;
@Nullable @Nullable
public MaterialCardView imageContainerCard; public MaterialCardView imageContainerCard;
@Nullable
public FrameLayout imageContainer;
@Nullable @Nullable
public TextView imageText; public TextView imageText;
@ -65,10 +62,6 @@ public class MediaEntryViewHolder extends AbstractDraggableSwipeableItemViewHold
@Nullable @Nullable
public View paletteColorContainer; public View paletteColorContainer;
@Nullable
public RecyclerView recyclerView;
@Nullable @Nullable
public TextView text; public TextView text;
@ -88,18 +81,16 @@ public class MediaEntryViewHolder extends AbstractDraggableSwipeableItemViewHold
text2 = itemView.findViewById(R.id.text2); text2 = itemView.findViewById(R.id.text2);
image = itemView.findViewById(R.id.image); 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); time = itemView.findViewById(R.id.time);
imageText = itemView.findViewById(R.id.imageText); imageText = itemView.findViewById(R.id.imageText);
imageTextContainer = itemView.findViewById(R.id.imageTextContainer); imageTextContainer = itemView.findViewById(R.id.imageTextContainer);
imageContainerCard = itemView.findViewById(R.id.imageContainerCard); imageContainerCard = itemView.findViewById(R.id.imageContainerCard);
imageContainer = itemView.findViewById(R.id.imageContainer);
menu = itemView.findViewById(R.id.menu); menu = itemView.findViewById(R.id.menu);
dragView = itemView.findViewById(R.id.drag_view); dragView = itemView.findViewById(R.id.drag_view);
paletteColorContainer = itemView.findViewById(R.id.paletteColorContainer); paletteColorContainer = itemView.findViewById(R.id.paletteColorContainer);
recyclerView = itemView.findViewById(R.id.recycler_view);
mask = itemView.findViewById(R.id.mask); mask = itemView.findViewById(R.id.mask);
dummyContainer = itemView.findViewById(R.id.dummy_view); dummyContainer = itemView.findViewById(R.id.dummy_view);

View file

@ -41,7 +41,7 @@ import code.name.monkey.retromusic.model.Song
import code.name.monkey.retromusic.util.MusicUtil import code.name.monkey.retromusic.util.MusicUtil
class PlaylistAdapter( class PlaylistAdapter(
private val activity: FragmentActivity, override val activity: FragmentActivity,
private var dataSet: List<PlaylistWithSongs>, private var dataSet: List<PlaylistWithSongs>,
private var itemLayoutRes: Int, private var itemLayoutRes: Int,
ICabHolder: ICabHolder?, ICabHolder: ICabHolder?,

View file

@ -16,7 +16,6 @@ package code.name.monkey.retromusic.adapter.song
import android.view.MenuItem import android.view.MenuItem
import android.view.View import android.view.View
import androidx.appcompat.app.AppCompatActivity
import androidx.fragment.app.FragmentActivity import androidx.fragment.app.FragmentActivity
import code.name.monkey.retromusic.R import code.name.monkey.retromusic.R
import code.name.monkey.retromusic.glide.GlideApp 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 import me.zhanghai.android.fastscroll.PopupTextProvider
class PlayingQueueAdapter( class PlayingQueueAdapter(
activity: AppCompatActivity, activity: FragmentActivity,
dataSet: MutableList<Song>, dataSet: MutableList<Song>,
private var current: Int, private var current: Int,
itemLayoutRes: Int itemLayoutRes: Int

View file

@ -16,10 +16,9 @@ package code.name.monkey.retromusic.adapter.song
import android.view.View import android.view.View
import androidx.fragment.app.FragmentActivity import androidx.fragment.app.FragmentActivity
import code.name.monkey.appthemehelper.ThemeStore
import code.name.monkey.retromusic.R import code.name.monkey.retromusic.R
import code.name.monkey.retromusic.extensions.applyColor import code.name.monkey.retromusic.extensions.accentColor
import code.name.monkey.retromusic.extensions.applyOutlineColor import code.name.monkey.retromusic.extensions.accentOutlineColor
import code.name.monkey.retromusic.helper.MusicPlayerRemote import code.name.monkey.retromusic.helper.MusicPlayerRemote
import code.name.monkey.retromusic.interfaces.ICabHolder import code.name.monkey.retromusic.interfaces.ICabHolder
import code.name.monkey.retromusic.model.Song import code.name.monkey.retromusic.model.Song
@ -45,19 +44,18 @@ class ShuffleButtonSongAdapter(
override fun onBindViewHolder(holder: SongAdapter.ViewHolder, position: Int) { override fun onBindViewHolder(holder: SongAdapter.ViewHolder, position: Int) {
if (holder.itemViewType == OFFSET_ITEM) { if (holder.itemViewType == OFFSET_ITEM) {
val color = ThemeStore.accentColor(activity)
val viewHolder = holder as ViewHolder val viewHolder = holder as ViewHolder
viewHolder.playAction?.let { viewHolder.playAction?.let {
it.setOnClickListener { it.setOnClickListener {
MusicPlayerRemote.openQueue(dataSet, 0, true) MusicPlayerRemote.openQueue(dataSet, 0, true)
} }
it.applyOutlineColor(color) it.accentOutlineColor()
} }
viewHolder.shuffleAction?.let { viewHolder.shuffleAction?.let {
it.setOnClickListener { it.setOnClickListener {
MusicPlayerRemote.openAndShuffleQueue(dataSet, true) MusicPlayerRemote.openAndShuffleQueue(dataSet, true)
} }
it.applyColor(color) it.accentColor()
} }
} else { } else {
super.onBindViewHolder(holder, position - 1) super.onBindViewHolder(holder, position - 1)

View file

@ -37,12 +37,13 @@ import code.name.monkey.retromusic.helper.MusicPlayerRemote
import code.name.monkey.retromusic.helper.SortOrder import code.name.monkey.retromusic.helper.SortOrder
import code.name.monkey.retromusic.helper.menu.SongMenuHelper import code.name.monkey.retromusic.helper.menu.SongMenuHelper
import code.name.monkey.retromusic.helper.menu.SongsMenuHelper 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.ICabHolder
import code.name.monkey.retromusic.model.Song import code.name.monkey.retromusic.model.Song
import code.name.monkey.retromusic.util.MusicUtil import code.name.monkey.retromusic.util.MusicUtil
import code.name.monkey.retromusic.util.PreferenceUtil import code.name.monkey.retromusic.util.PreferenceUtil
import code.name.monkey.retromusic.util.RetroUtil
import code.name.monkey.retromusic.util.color.MediaNotificationProcessor import code.name.monkey.retromusic.util.color.MediaNotificationProcessor
import com.afollestad.materialcab.MaterialCab
import me.zhanghai.android.fastscroll.PopupTextProvider import me.zhanghai.android.fastscroll.PopupTextProvider
/** /**
@ -50,7 +51,7 @@ import me.zhanghai.android.fastscroll.PopupTextProvider
*/ */
open class SongAdapter( open class SongAdapter(
protected val activity: FragmentActivity, override val activity: FragmentActivity,
var dataSet: MutableList<Song>, var dataSet: MutableList<Song>,
protected var itemLayoutRes: Int, protected var itemLayoutRes: Int,
ICabHolder: ICabHolder?, ICabHolder: ICabHolder?,
@ -59,7 +60,7 @@ open class SongAdapter(
activity, activity,
ICabHolder, ICabHolder,
R.menu.menu_media_selection R.menu.menu_media_selection
), MaterialCab.Callback, PopupTextProvider { ), ICabCallback, PopupTextProvider {
private var showSectionName = true private var showSectionName = true
@ -104,6 +105,10 @@ open class SongAdapter(
holder.text?.text = getSongText(song) holder.text?.text = getSongText(song)
holder.text2?.text = getSongText(song) holder.text2?.text = getSongText(song)
loadAlbumCover(song, holder) 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) { private fun setColors(color: MediaNotificationProcessor, holder: ViewHolder) {

View file

@ -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_GENRE = "__BY_GENRE__";
public static final String MEDIA_ID_MUSICS_BY_SHUFFLE = "__BY_SHUFFLE__"; 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 MEDIA_ID_MUSICS_BY_QUEUE = "__BY_QUEUE__";
public static final String RECENT_ROOT = "__RECENT__";
private static final String CATEGORY_SEPARATOR = "__/__"; private static final String CATEGORY_SEPARATOR = "__/__";
private static final String LEAF_SEPARATOR = "__|__"; private static final String LEAF_SEPARATOR = "__|__";

View file

@ -15,7 +15,6 @@ package code.name.monkey.retromusic.auto
import android.content.Context import android.content.Context
import android.content.res.Resources import android.content.res.Resources
import android.net.Uri
import android.support.v4.media.MediaBrowserCompat import android.support.v4.media.MediaBrowserCompat
import code.name.monkey.retromusic.R import code.name.monkey.retromusic.R
import code.name.monkey.retromusic.helper.MusicPlayerRemote import code.name.monkey.retromusic.helper.MusicPlayerRemote

View file

@ -14,7 +14,6 @@
*/ */
package code.name.monkey.retromusic.dialogs package code.name.monkey.retromusic.dialogs
import android.annotation.SuppressLint
import android.app.Dialog import android.app.Dialog
import android.content.Context import android.content.Context
import android.os.Bundle import android.os.Bundle
@ -42,7 +41,6 @@ import java.io.IOException
class SongDetailDialog : DialogFragment() { class SongDetailDialog : DialogFragment() {
@SuppressLint("InflateParams")
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
val context: Context = requireContext() val context: Context = requireContext()
val dialogView = LayoutInflater.from(context).inflate(R.layout.dialog_file_details, null) val dialogView = LayoutInflater.from(context).inflate(R.layout.dialog_file_details, null)

View file

@ -21,7 +21,7 @@ import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper
import com.google.android.material.appbar.MaterialToolbar import com.google.android.material.appbar.MaterialToolbar
fun AppCompatActivity.applyToolbar(toolbar: MaterialToolbar) { fun AppCompatActivity.applyToolbar(toolbar: MaterialToolbar) {
toolbar.setBackgroundColor(surfaceColor()) //toolbar.setBackgroundColor(surfaceColor())
ToolbarContentTintHelper.colorBackButton(toolbar) ToolbarContentTintHelper.colorBackButton(toolbar)
setSupportActionBar(toolbar) setSupportActionBar(toolbar)
} }

View file

@ -29,6 +29,7 @@ import androidx.annotation.ColorRes
import androidx.appcompat.widget.AppCompatImageView import androidx.appcompat.widget.AppCompatImageView
import androidx.appcompat.widget.Toolbar import androidx.appcompat.widget.Toolbar
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import androidx.core.graphics.ColorUtils
import androidx.core.graphics.drawable.DrawableCompat import androidx.core.graphics.drawable.DrawableCompat
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import code.name.monkey.appthemehelper.ThemeStore 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.appthemehelper.util.MaterialValueHelper
import code.name.monkey.retromusic.App import code.name.monkey.retromusic.App
import code.name.monkey.retromusic.R import code.name.monkey.retromusic.R
import code.name.monkey.retromusic.util.PreferenceUtil.materialYou
import com.google.android.material.button.MaterialButton import com.google.android.material.button.MaterialButton
import com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton import com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton
import com.google.android.material.floatingactionbutton.FloatingActionButton 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) = fun Dialog.resolveColor(@AttrRes attr: Int, fallBackColor: Int = 0) =
ATHUtil.resolveColor(context, attr, fallBackColor) 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() { fun CheckBox.addAccentColor() {
if (materialYou) return
buttonTintList = ColorStateList.valueOf(ThemeStore.accentColor(context)) buttonTintList = ColorStateList.valueOf(ThemeStore.accentColor(context))
} }
fun SeekBar.addAccentColor() { fun SeekBar.addAccentColor() {
if (materialYou) return
val colorState = ColorStateList.valueOf(ThemeStore.accentColor(context)) val colorState = ColorStateList.valueOf(ThemeStore.accentColor(context))
progressTintList = colorState progressTintList = colorState
thumbTintList = 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() { fun MaterialButton.accentBackgroundColor() {
if (materialYou) return
backgroundTintList = ColorStateList.valueOf(ThemeStore.accentColor(App.getContext())) backgroundTintList = ColorStateList.valueOf(ThemeStore.accentColor(App.getContext()))
} }
fun MaterialButton.accentOutlineColor() { fun MaterialButton.accentOutlineColor() {
if (materialYou) return
val color = ThemeStore.accentColor(context) val color = ThemeStore.accentColor(context)
val colorStateList = ColorStateList.valueOf(color) val colorStateList = ColorStateList.valueOf(color)
iconTint = colorStateList iconTint = colorStateList
@ -116,6 +127,7 @@ fun SeekBar.applyColor(@ColorInt color: Int) {
} }
fun ExtendedFloatingActionButton.accentColor() { fun ExtendedFloatingActionButton.accentColor() {
if (materialYou) return
val color = ThemeStore.accentColor(context) val color = ThemeStore.accentColor(context)
val textColor = MaterialValueHelper.getPrimaryTextColor(context, ColorUtil.isColorLight(color)) val textColor = MaterialValueHelper.getPrimaryTextColor(context, ColorUtil.isColorLight(color))
val colorStateList = ColorStateList.valueOf(color) val colorStateList = ColorStateList.valueOf(color)
@ -126,12 +138,11 @@ fun ExtendedFloatingActionButton.accentColor() {
} }
fun FloatingActionButton.accentColor() { fun FloatingActionButton.accentColor() {
if (materialYou) return
val color = ThemeStore.accentColor(context) val color = ThemeStore.accentColor(context)
val textColor = MaterialValueHelper.getPrimaryTextColor(context, ColorUtil.isColorLight(color)) val textColor = MaterialValueHelper.getPrimaryTextColor(context, ColorUtil.isColorLight(color))
val colorStateList = ColorStateList.valueOf(color) backgroundTintList = ColorStateList.valueOf(color)
val textColorStateList = ColorStateList.valueOf(textColor) imageTintList = ColorStateList.valueOf(textColor)
backgroundTintList = colorStateList
imageTintList = textColorStateList
} }
fun MaterialButton.applyColor(color: Int) { fun MaterialButton.applyColor(color: Int) {
@ -147,15 +158,21 @@ fun MaterialButton.applyColor(color: Int) {
iconTint = textColorColorStateList iconTint = textColorColorStateList
} }
fun MaterialButton.accentColor() = applyColor(ThemeStore.accentColor(context)) fun MaterialButton.accentColor() {
if (materialYou) return
applyColor(ThemeStore.accentColor(context))
}
fun MaterialButton.applyOutlineColor(color: Int) { fun MaterialButton.applyOutlineColor(color: Int) {
val textColorColorStateList = ColorStateList.valueOf(color) val colorStateList = ColorStateList.valueOf(color)
setTextColor(textColorColorStateList) iconTint = colorStateList
iconTint = textColorColorStateList strokeColor = colorStateList
setTextColor(colorStateList)
rippleColor = colorStateList
} }
fun TextInputLayout.accentColor() { fun TextInputLayout.accentColor() {
if (materialYou) return
val accentColor = ThemeStore.accentColor(context) val accentColor = ThemeStore.accentColor(context)
val colorState = ColorStateList.valueOf(accentColor) val colorState = ColorStateList.valueOf(accentColor)
boxStrokeColor = accentColor boxStrokeColor = accentColor
@ -164,6 +181,7 @@ fun TextInputLayout.accentColor() {
} }
fun CircularProgressIndicator.accentColor() { fun CircularProgressIndicator.accentColor() {
if (materialYou) return
val color = ThemeStore.accentColor(context) val color = ThemeStore.accentColor(context)
setIndicatorColor(color) setIndicatorColor(color)
trackColor = ColorUtil.withAlpha(color, 0.2f) trackColor = ColorUtil.withAlpha(color, 0.2f)
@ -176,6 +194,21 @@ fun CircularProgressIndicator.applyColor(color: Int) {
fun AppCompatImageView.accentColor(): Int = ThemeStore.accentColor(context) 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 @CheckResult
fun Drawable.tint(@ColorInt color: Int): Drawable { fun Drawable.tint(@ColorInt color: Int): Drawable {
val tintedDrawable = DrawableCompat.wrap(this).mutate() 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 { fun Context.getColorCompat(@ColorRes colorRes: Int): Int {
return ContextCompat.getColor(this, colorRes) 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)

View file

@ -19,16 +19,14 @@ import android.content.res.Configuration
import android.graphics.drawable.Drawable import android.graphics.drawable.Drawable
import android.os.PowerManager import android.os.PowerManager
import android.widget.Toast import android.widget.Toast
import androidx.annotation.DrawableRes import androidx.annotation.*
import androidx.annotation.IdRes
import androidx.annotation.IntegerRes
import androidx.annotation.StringRes
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.content.res.AppCompatResources import androidx.appcompat.content.res.AppCompatResources
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentManager import androidx.fragment.app.FragmentManager
import androidx.navigation.fragment.NavHostFragment import androidx.navigation.fragment.NavHostFragment
import code.name.monkey.retromusic.util.PreferenceUtil import code.name.monkey.retromusic.util.PreferenceUtil
import com.google.android.material.appbar.MaterialToolbar
fun Fragment.getIntRes(@IntegerRes int: Int): Int { fun Fragment.getIntRes(@IntegerRes int: Int): Int {
return resources.getInteger(int) return resources.getInteger(int)
@ -97,3 +95,11 @@ fun Context.getDrawableCompat(@DrawableRes drawableRes: Int): Drawable {
fun Fragment.getDrawableCompat(@DrawableRes drawableRes: Int): Drawable { fun Fragment.getDrawableCompat(@DrawableRes drawableRes: Int): Drawable {
return AppCompatResources.getDrawable(requireContext(), drawableRes)!! 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)
}

View file

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

View file

@ -14,19 +14,25 @@
*/ */
package code.name.monkey.retromusic.extensions package code.name.monkey.retromusic.extensions
import android.animation.Animator
import android.animation.ObjectAnimator import android.animation.ObjectAnimator
import android.content.Context import android.content.Context
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.view.ViewGroup.MarginLayoutParams
import android.view.ViewTreeObserver import android.view.ViewTreeObserver
import android.view.inputmethod.InputMethodManager import android.view.inputmethod.InputMethodManager
import android.widget.EditText import android.widget.EditText
import androidx.annotation.LayoutRes import androidx.annotation.LayoutRes
import androidx.core.animation.doOnEnd import androidx.core.animation.doOnEnd
import androidx.core.animation.doOnStart import androidx.core.animation.doOnStart
import androidx.core.view.*
import code.name.monkey.appthemehelper.ThemeStore import code.name.monkey.appthemehelper.ThemeStore
import code.name.monkey.appthemehelper.util.TintHelper 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.bottomsheet.BottomSheetBehavior
import com.google.android.material.imageview.ShapeableImageView import com.google.android.material.imageview.ShapeableImageView
import com.google.android.material.shape.ShapeAppearanceModel 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 View.showOrHide(show: Boolean) = if (show) show() else hide()
fun EditText.appHandleColor(): EditText { fun EditText.appHandleColor(): EditText {
if (PreferenceUtil.materialYou) return this
TintHelper.colorHandles(this, ThemeStore.accentColor(context)) TintHelper.colorHandles(this, ThemeStore.accentColor(context))
return this return this
} }
fun View.translateYAnimate(value: Float) { fun View.translateYAnimate(value: Float): Animator {
ObjectAnimator.ofFloat(this, "translationY", value) return ObjectAnimator.ofFloat(this, "translationY", value)
.apply { .apply {
duration = 300 duration = 300
doOnStart { doOnStart {
@ -123,3 +130,113 @@ fun ShapeableImageView.setCircleShape(boolean: Boolean) {
shapeAppearanceModel = ShapeAppearanceModel().withCornerSize(radius) shapeAppearanceModel = ShapeAppearanceModel().withCornerSize(radius)
} }
} }
/**
* 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<MarginLayoutParams> {
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<MarginLayoutParams> {
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
)

View file

@ -19,10 +19,12 @@ import androidx.lifecycle.*
import code.name.monkey.retromusic.* import code.name.monkey.retromusic.*
import code.name.monkey.retromusic.db.* import code.name.monkey.retromusic.db.*
import code.name.monkey.retromusic.fragments.ReloadType.* 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.helper.MusicPlayerRemote
import code.name.monkey.retromusic.interfaces.IMusicServiceEventListener import code.name.monkey.retromusic.interfaces.IMusicServiceEventListener
import code.name.monkey.retromusic.model.* import code.name.monkey.retromusic.model.*
import code.name.monkey.retromusic.repository.RealRepository import code.name.monkey.retromusic.repository.RealRepository
import code.name.monkey.retromusic.util.DensityUtil
import code.name.monkey.retromusic.util.PreferenceUtil import code.name.monkey.retromusic.util.PreferenceUtil
import kotlinx.coroutines.Dispatchers.IO import kotlinx.coroutines.Dispatchers.IO
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@ -40,6 +42,7 @@ class LibraryViewModel(
private val legacyPlaylists = MutableLiveData<List<Playlist>>() private val legacyPlaylists = MutableLiveData<List<Playlist>>()
private val genres = MutableLiveData<List<Genre>>() private val genres = MutableLiveData<List<Genre>>()
private val searchResults = MutableLiveData<List<Any>>() private val searchResults = MutableLiveData<List<Any>>()
private val fabMargin = MutableLiveData(0)
val paletteColor: LiveData<Int> = _paletteColor val paletteColor: LiveData<Int> = _paletteColor
init { init {
@ -85,6 +88,10 @@ class LibraryViewModel(
return home return home
} }
fun getFabMargin(): LiveData<Int> {
return fabMargin
}
private fun fetchSongs() { private fun fetchSongs() {
viewModelScope.launch(IO) { viewModelScope.launch(IO) {
songs.postValue(repository.allSongs()) songs.postValue(repository.allSongs())
@ -133,9 +140,9 @@ class LibraryViewModel(
} }
} }
fun search(query: String?, filters: List<Boolean>) { fun search(query: String?, filter: Filter) {
viewModelScope.launch(IO) { viewModelScope.launch(IO) {
val result = repository.search(query, filters) val result = repository.search(query, filter)
searchResults.postValue(result) 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 { enum class ReloadType {

View file

@ -20,6 +20,7 @@ import android.net.Uri
import android.os.Bundle import android.os.Bundle
import android.view.View import android.view.View
import androidx.core.app.ShareCompat import androidx.core.app.ShareCompat
import androidx.core.view.updatePadding
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.recyclerview.widget.DefaultItemAnimator import androidx.recyclerview.widget.DefaultItemAnimator
import androidx.recyclerview.widget.LinearLayoutManager 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.databinding.FragmentAboutBinding
import code.name.monkey.retromusic.fragments.LibraryViewModel import code.name.monkey.retromusic.fragments.LibraryViewModel
import code.name.monkey.retromusic.util.NavigationUtil import code.name.monkey.retromusic.util.NavigationUtil
import code.name.monkey.retromusic.util.RetroUtil
import org.koin.androidx.viewmodel.ext.android.sharedViewModel import org.koin.androidx.viewmodel.ext.android.sharedViewModel
class AboutFragment : Fragment(R.layout.fragment_about), View.OnClickListener { 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()) binding.aboutContent.cardOther.version.setSummary(getAppVersion())
setUpView() setUpView()
loadContributors() 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) { private fun openUrl(url: String) {

View file

@ -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_TRACK_LIST
import code.name.monkey.retromusic.helper.SortOrder.AlbumSongSortOrder.Companion.SONG_Z_A 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.IAlbumClickListener
import code.name.monkey.retromusic.interfaces.ICabCallback
import code.name.monkey.retromusic.interfaces.ICabHolder import code.name.monkey.retromusic.interfaces.ICabHolder
import code.name.monkey.retromusic.model.Album import code.name.monkey.retromusic.model.Album
import code.name.monkey.retromusic.model.Artist 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.RetroColorUtil
import code.name.monkey.retromusic.util.RetroUtil import code.name.monkey.retromusic.util.RetroUtil
import code.name.monkey.retromusic.util.color.MediaNotificationProcessor 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.MaterialArcMotion
import com.google.android.material.transition.MaterialContainerTransform import com.google.android.material.transition.MaterialContainerTransform
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
@ -101,7 +106,6 @@ class AlbumDetailsFragment : AbsMainActivityFragment(R.layout.fragment_album_det
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
sharedElementEnterTransition = MaterialContainerTransform().apply { sharedElementEnterTransition = MaterialContainerTransform().apply {
drawingViewId = R.id.fragment_container drawingViewId = R.id.fragment_container
duration = 300L
scrimColor = Color.TRANSPARENT scrimColor = Color.TRANSPARENT
setAllContainerColors(requireContext().resolveColor(R.attr.colorSurface)) setAllContainerColors(requireContext().resolveColor(R.attr.colorSurface))
setPathMotion(MaterialArcMotion()) setPathMotion(MaterialArcMotion())
@ -176,6 +180,8 @@ class AlbumDetailsFragment : AbsMainActivityFragment(R.layout.fragment_album_det
requireActivity().onBackPressed() requireActivity().onBackPressed()
} }
} }
binding.appBarLayout?.statusBarForeground =
MaterialShapeDrawable.createWithElevationOverlay(requireContext())
} }
override fun onDestroy() { override fun onDestroy() {
@ -228,9 +234,10 @@ class AlbumDetailsFragment : AbsMainActivityFragment(R.layout.fragment_album_det
loadAlbumCover(album) loadAlbumCover(album)
simpleSongAdapter.swapDataSet(album.songs) simpleSongAdapter.swapDataSet(album.songs)
if (albumArtistExists) { if (albumArtistExists) {
detailsViewModel.getAlbumArtist(album.albumArtist.toString()).observe(viewLifecycleOwner, { detailsViewModel.getAlbumArtist(album.albumArtist.toString())
loadArtistImage(it) .observe(viewLifecycleOwner, {
}) loadArtistImage(it)
})
} else { } else {
detailsViewModel.getArtist(album.artistId).observe(viewLifecycleOwner, { detailsViewModel.getArtist(album.artistId).observe(viewLifecycleOwner, {
loadArtistImage(it) loadArtistImage(it)
@ -302,7 +309,12 @@ class AlbumDetailsFragment : AbsMainActivityFragment(R.layout.fragment_album_det
}) })
GlideApp.with(requireContext()).asBitmapPalette().artistImageOptions(artist) GlideApp.with(requireContext()).asBitmapPalette().artistImageOptions(artist)
//.forceDownload(PreferenceUtil.isAllowedToDownloadMetadata()) //.forceDownload(PreferenceUtil.isAllowedToDownloadMetadata())
.load(RetroGlideExtension.getArtistModel(artist, PreferenceUtil.isAllowedToDownloadMetadata())) .load(
RetroGlideExtension.getArtistModel(
artist,
PreferenceUtil.isAllowedToDownloadMetadata()
)
)
.dontAnimate() .dontAnimate()
.dontTransform() .dontTransform()
.into(object : RetroMusicColoredTarget(binding.artistImage) { .into(object : RetroMusicColoredTarget(binding.artistImage) {
@ -312,7 +324,8 @@ class AlbumDetailsFragment : AbsMainActivityFragment(R.layout.fragment_album_det
} }
private fun loadAlbumCover(album: Album) { private fun loadAlbumCover(album: Album) {
GlideApp.with(requireContext()).asBitmapPalette().albumCoverOptions(album.safeGetFirstSong()) GlideApp.with(requireContext()).asBitmapPalette()
.albumCoverOptions(album.safeGetFirstSong())
//.checkIgnoreMediaStore() //.checkIgnoreMediaStore()
.load(RetroGlideExtension.getSongModel(album.safeGetFirstSong())) .load(RetroGlideExtension.getSongModel(album.safeGetFirstSong()))
.into(object : SingleColorTarget(binding.image) { .into(object : SingleColorTarget(binding.image) {
@ -323,8 +336,10 @@ class AlbumDetailsFragment : AbsMainActivityFragment(R.layout.fragment_album_det
} }
private fun setColors(color: Int) { private fun setColors(color: Int) {
binding.fragmentAlbumContent.shuffleAction.applyColor(color) _binding?.fragmentAlbumContent?.apply {
binding.fragmentAlbumContent.playAction.applyOutlineColor(color) shuffleAction.applyColor(color)
playAction.applyOutlineColor(color)
}
} }
override fun onAlbumClick(albumId: Long, view: View) { override fun onAlbumClick(albumId: Long, view: View) {
@ -450,28 +465,34 @@ class AlbumDetailsFragment : AbsMainActivityFragment(R.layout.fragment_album_det
private fun handleBackPress(): Boolean { private fun handleBackPress(): Boolean {
cab?.let { cab?.let {
if (it.isActive) { if (it.isActive()) {
it.finish() it.destroy()
return true return true
} }
} }
return false 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 { cab?.let {
if (it.isActive) { if (it.isActive()) {
it.finish() it.destroy()
} }
} }
cab = MaterialCab(mainActivity, R.id.cab_stub) cab = createCab(R.id.toolbar_container) {
.setMenu(menuRes) menu(menuRes)
.setCloseDrawableRes(R.drawable.ic_close) closeDrawable(R.drawable.ic_close)
.setBackgroundColor(RetroColorUtil.shiftBackgroundColorForLightText(surfaceColor())) backgroundColor(literal = RetroColorUtil.shiftBackgroundColor(surfaceColor()))
.start(callback) slideDown()
return cab as MaterialCab onCreate { cab, menu -> callback.onCabCreated(cab, menu) }
onSelection {
callback.onCabItemClicked(it)
}
onDestroy { callback.onCabFinished(it) }
}
return cab as AttachedCab
} }
override fun onDestroyView() { override fun onDestroyView() {

View file

@ -54,7 +54,7 @@ class AlbumDetailsViewModel(
fun getAlbumInfo(album: Album): LiveData<Result<LastFmAlbum>> = liveData { fun getAlbumInfo(album: Album): LiveData<Result<LastFmAlbum>> = liveData {
emit(Result.Loading) emit(Result.Loading)
emit(repository.albumInfo(album.artistName ?: "-", album.title ?: "-")) emit(repository.albumInfo(album.artistName, album.title))
} }
fun getMoreAlbums(artist: Artist): LiveData<List<Album>> = liveData(IO) { fun getMoreAlbums(artist: Artist): LiveData<List<Album>> = liveData(IO) {

View file

@ -24,16 +24,23 @@ import androidx.recyclerview.widget.GridLayoutManager
import code.name.monkey.retromusic.EXTRA_ALBUM_ID import code.name.monkey.retromusic.EXTRA_ALBUM_ID
import code.name.monkey.retromusic.R import code.name.monkey.retromusic.R
import code.name.monkey.retromusic.adapter.album.AlbumAdapter 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.extensions.surfaceColor
import code.name.monkey.retromusic.fragments.ReloadType import code.name.monkey.retromusic.fragments.ReloadType
import code.name.monkey.retromusic.fragments.base.AbsRecyclerViewCustomGridSizeFragment import code.name.monkey.retromusic.fragments.base.AbsRecyclerViewCustomGridSizeFragment
import code.name.monkey.retromusic.helper.MusicPlayerRemote
import code.name.monkey.retromusic.helper.SortOrder.AlbumSortOrder import code.name.monkey.retromusic.helper.SortOrder.AlbumSortOrder
import code.name.monkey.retromusic.interfaces.IAlbumClickListener 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.interfaces.ICabHolder
import code.name.monkey.retromusic.service.MusicService
import code.name.monkey.retromusic.util.PreferenceUtil import code.name.monkey.retromusic.util.PreferenceUtil
import code.name.monkey.retromusic.util.RetroColorUtil import code.name.monkey.retromusic.util.RetroColorUtil
import code.name.monkey.retromusic.util.RetroUtil 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 import com.google.android.gms.cast.framework.CastButtonFactory
class AlbumsFragment : AbsRecyclerViewCustomGridSizeFragment<AlbumAdapter, GridLayoutManager>(), class AlbumsFragment : AbsRecyclerViewCustomGridSizeFragment<AlbumAdapter, GridLayoutManager>(),
@ -50,7 +57,7 @@ class AlbumsFragment : AbsRecyclerViewCustomGridSizeFragment<AlbumAdapter, GridL
requireActivity().onBackPressedDispatcher.addCallback(viewLifecycleOwner) { requireActivity().onBackPressedDispatcher.addCallback(viewLifecycleOwner) {
if (!handleBackPress()) { if (!handleBackPress()) {
remove() remove()
requireActivity().onBackPressed() mainActivity.finish()
} }
} }
} }
@ -61,6 +68,20 @@ class AlbumsFragment : AbsRecyclerViewCustomGridSizeFragment<AlbumAdapter, GridL
override val emptyMessage: Int override val emptyMessage: Int
get() = R.string.no_albums get() = R.string.no_albums
override val isShuffleVisible: Boolean
get() = true
override fun onShuffleClicked() {
libraryViewModel.getAlbums().value?.let {
MusicPlayerRemote.setShuffleMode(MusicService.SHUFFLE_MODE_NONE)
MusicPlayerRemote.openQueue(
queue = it.shuffled().flatMap { album -> album.songs },
startPosition = 0,
startPlaying = true
)
}
}
override fun createLayoutManager(): GridLayoutManager { override fun createLayoutManager(): GridLayoutManager {
return GridLayoutManager(requireActivity(), getGridSize()) return GridLayoutManager(requireActivity(), getGridSize())
} }
@ -325,28 +346,34 @@ class AlbumsFragment : AbsRecyclerViewCustomGridSizeFragment<AlbumAdapter, GridL
private fun handleBackPress(): Boolean { private fun handleBackPress(): Boolean {
cab?.let { cab?.let {
if (it.isActive) { if (it.isActive()) {
it.finish() it.destroy()
return true return true
} }
} }
return false 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 { cab?.let {
println("Cab") println("Cab")
if (it.isActive) { if (it.isActive()) {
it.finish() it.destroy()
} }
} }
cab = MaterialCab(mainActivity, R.id.cab_stub) cab = createCab(R.id.toolbar_container) {
.setMenu(menuRes) menu(menuRes)
.setCloseDrawableRes(R.drawable.ic_close) closeDrawable(R.drawable.ic_close)
.setBackgroundColor(RetroColorUtil.shiftBackgroundColorForLightText(surfaceColor())) backgroundColor(literal = RetroColorUtil.shiftBackgroundColor(surfaceColor()))
.start(callback) slideDown()
return cab as MaterialCab onCreate { cab, menu -> callback.onCabCreated(cab, menu) }
onSelection {
callback.onCabItemClicked(it)
}
onDestroy { callback.onCabFinished(it) }
}
return cab as AttachedCab
} }
} }

View file

@ -33,6 +33,7 @@ import code.name.monkey.retromusic.glide.RetroGlideExtension
import code.name.monkey.retromusic.glide.SingleColorTarget import code.name.monkey.retromusic.glide.SingleColorTarget
import code.name.monkey.retromusic.helper.MusicPlayerRemote import code.name.monkey.retromusic.helper.MusicPlayerRemote
import code.name.monkey.retromusic.interfaces.IAlbumClickListener 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.interfaces.ICabHolder
import code.name.monkey.retromusic.model.Artist import code.name.monkey.retromusic.model.Artist
import code.name.monkey.retromusic.network.Result 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.MusicUtil
import code.name.monkey.retromusic.util.RetroColorUtil import code.name.monkey.retromusic.util.RetroColorUtil
import code.name.monkey.retromusic.util.RetroUtil 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 com.google.android.material.transition.MaterialContainerTransform
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@ -70,7 +75,6 @@ abstract class AbsArtistDetailsFragment : AbsMainActivityFragment(R.layout.fragm
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
sharedElementEnterTransition = MaterialContainerTransform().apply { sharedElementEnterTransition = MaterialContainerTransform().apply {
drawingViewId = R.id.fragment_container drawingViewId = R.id.fragment_container
duration = 300L
scrimColor = Color.TRANSPARENT scrimColor = Color.TRANSPARENT
setAllContainerColors(requireContext().resolveColor(R.attr.colorSurface)) setAllContainerColors(requireContext().resolveColor(R.attr.colorSurface))
} }
@ -121,6 +125,8 @@ abstract class AbsArtistDetailsFragment : AbsMainActivityFragment(R.layout.fragm
requireActivity().onBackPressed() requireActivity().onBackPressed()
} }
} }
binding.appBarLayout?.statusBarForeground =
MaterialShapeDrawable.createWithElevationOverlay(requireContext())
} }
private fun setupRecyclerView() { private fun setupRecyclerView() {
@ -306,28 +312,34 @@ abstract class AbsArtistDetailsFragment : AbsMainActivityFragment(R.layout.fragm
private fun handleBackPress(): Boolean { private fun handleBackPress(): Boolean {
cab?.let { cab?.let {
if (it.isActive) { if (it.isActive()) {
it.finish() it.destroy()
return true return true
} }
} }
return false 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 { cab?.let {
if (it.isActive) { if (it.isActive()) {
it.finish() it.destroy()
} }
} }
cab = MaterialCab(mainActivity, R.id.cab_stub) cab = createCab(R.id.toolbar_container) {
.setMenu(menuRes) menu(menuRes)
.setCloseDrawableRes(R.drawable.ic_close) closeDrawable(R.drawable.ic_close)
.setBackgroundColor(RetroColorUtil.shiftBackgroundColorForLightText(surfaceColor())) backgroundColor(literal = RetroColorUtil.shiftBackgroundColor(surfaceColor()))
.start(callback) slideDown()
return cab as MaterialCab onCreate { cab, menu -> callback.onCabCreated(cab, menu) }
onSelection {
callback.onCabItemClicked(it)
}
onDestroy { callback.onCabFinished(it) }
}
return cab as AttachedCab
} }

View file

@ -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<Artist>()
init {
fetchAlbumArtist()
}
private fun fetchAlbumArtist() {
viewModelScope.launch(IO) {
artistDetails.postValue(realRepository.albumArtistByName(artistName))
}
}
fun getArtist(): LiveData<Artist> = artistDetails
fun getArtistInfo(
name: String,
lang: String?,
cache: String?
): LiveData<Result<LastFmArtist>> = 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() {}
}

View file

@ -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.EXTRA_ARTIST_NAME
import code.name.monkey.retromusic.R import code.name.monkey.retromusic.R
import code.name.monkey.retromusic.adapter.artist.ArtistAdapter 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.extensions.surfaceColor
import code.name.monkey.retromusic.fragments.ReloadType import code.name.monkey.retromusic.fragments.ReloadType
import code.name.monkey.retromusic.fragments.base.AbsRecyclerViewCustomGridSizeFragment import code.name.monkey.retromusic.fragments.base.AbsRecyclerViewCustomGridSizeFragment
import code.name.monkey.retromusic.helper.MusicPlayerRemote
import code.name.monkey.retromusic.helper.SortOrder.ArtistSortOrder import code.name.monkey.retromusic.helper.SortOrder.ArtistSortOrder
import code.name.monkey.retromusic.interfaces.IAlbumArtistClickListener import code.name.monkey.retromusic.interfaces.IAlbumArtistClickListener
import code.name.monkey.retromusic.interfaces.IArtistClickListener 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.interfaces.ICabHolder
import code.name.monkey.retromusic.service.MusicService
import code.name.monkey.retromusic.util.PreferenceUtil import code.name.monkey.retromusic.util.PreferenceUtil
import code.name.monkey.retromusic.util.RetroColorUtil import code.name.monkey.retromusic.util.RetroColorUtil
import code.name.monkey.retromusic.util.RetroUtil 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 import com.google.android.gms.cast.framework.CastButtonFactory
class ArtistsFragment : AbsRecyclerViewCustomGridSizeFragment<ArtistAdapter, GridLayoutManager>(), class ArtistsFragment : AbsRecyclerViewCustomGridSizeFragment<ArtistAdapter, GridLayoutManager>(),
@ -51,16 +58,31 @@ class ArtistsFragment : AbsRecyclerViewCustomGridSizeFragment<ArtistAdapter, Gri
requireActivity().onBackPressedDispatcher.addCallback(viewLifecycleOwner) { requireActivity().onBackPressedDispatcher.addCallback(viewLifecycleOwner) {
if (!handleBackPress()) { if (!handleBackPress()) {
remove() remove()
requireActivity().onBackPressed() mainActivity.finish()
} }
} }
} }
override val titleRes: Int override val titleRes: Int
get() = R.string.artists get() = R.string.artists
override val emptyMessage: Int override val emptyMessage: Int
get() = R.string.no_artists get() = R.string.no_artists
override val isShuffleVisible: Boolean
get() = true
override fun onShuffleClicked() {
libraryViewModel.getArtists().value?.let {
MusicPlayerRemote.setShuffleMode(MusicService.SHUFFLE_MODE_NONE)
MusicPlayerRemote.openQueue(
queue = it.shuffled().flatMap { artist -> artist.songs },
startPosition = 0,
startPlaying = true
)
}
}
override fun setSortOrder(sortOrder: String) { override fun setSortOrder(sortOrder: String) {
libraryViewModel.forceReload(ReloadType.Artists) libraryViewModel.forceReload(ReloadType.Artists)
} }
@ -324,28 +346,34 @@ class ArtistsFragment : AbsRecyclerViewCustomGridSizeFragment<ArtistAdapter, Gri
private fun handleBackPress(): Boolean { private fun handleBackPress(): Boolean {
cab?.let { cab?.let {
if (it.isActive) { if (it.isActive()) {
it.finish() it.destroy()
return true return true
} }
} }
return false 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 { cab?.let {
if (it.isActive) { if (it.isActive()) {
it.finish() it.destroy()
} }
} }
cab = MaterialCab(mainActivity, R.id.cab_stub) cab = createCab(R.id.toolbar_container) {
.setMenu(menuRes) menu(menuRes)
.setCloseDrawableRes(R.drawable.ic_close) closeDrawable(R.drawable.ic_close)
.setBackgroundColor(RetroColorUtil.shiftBackgroundColorForLightText(surfaceColor())) backgroundColor(literal = RetroColorUtil.shiftBackgroundColor(surfaceColor()))
.start(callback) slideDown()
return cab as MaterialCab onCreate { cab, menu -> callback.onCabCreated(cab, menu) }
onSelection {
callback.onCabItemClicked(it)
}
onDestroy { callback.onCabFinished(it) }
}
return cab as AttachedCab
} }
override fun onResume() { override fun onResume() {

View file

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

View file

@ -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<List<File>>()
val backupsLiveData: LiveData<List<File>> = 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)
}
}
}

View file

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

View file

@ -34,10 +34,7 @@ abstract class AbsMainActivityFragment(@LayoutRes layout: Int) : AbsMusicService
override fun onActivityCreated(savedInstanceState: Bundle?) { override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState) super.onActivityCreated(savedInstanceState)
setHasOptionsMenu(true) setHasOptionsMenu(true)
mainActivity.setNavigationbarColorAuto()
mainActivity.setLightNavigationBar(true)
mainActivity.setTaskDescriptionColorAuto() mainActivity.setTaskDescriptionColorAuto()
mainActivity.hideStatusBar()
} }
private fun setStatusBarColor(view: View, color: Int) { private fun setStatusBarColor(view: View, color: Int) {

View file

@ -20,7 +20,7 @@ import android.view.animation.AccelerateInterpolator
import android.view.animation.DecelerateInterpolator import android.view.animation.DecelerateInterpolator
import androidx.annotation.LayoutRes import androidx.annotation.LayoutRes
import code.name.monkey.retromusic.R 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.helper.MusicProgressViewUpdateHelper
import code.name.monkey.retromusic.util.PreferenceUtil import code.name.monkey.retromusic.util.PreferenceUtil
import code.name.monkey.retromusic.util.color.MediaNotificationProcessor import code.name.monkey.retromusic.util.color.MediaNotificationProcessor

View file

@ -131,7 +131,7 @@ abstract class AbsPlayerFragment(@LayoutRes layout: Int) : AbsMainActivityFragme
} }
R.id.action_go_to_album -> { R.id.action_go_to_album -> {
//Hide Bottom Bar First, else Bottom Sheet doesn't collapse fully //Hide Bottom Bar First, else Bottom Sheet doesn't collapse fully
mainActivity.setBottomBarVisibility(false) mainActivity.setBottomNavVisibility(false)
mainActivity.collapsePanel() mainActivity.collapsePanel()
requireActivity().findNavController(R.id.fragment_container).navigate( requireActivity().findNavController(R.id.fragment_container).navigate(
R.id.albumDetailsFragment, R.id.albumDetailsFragment,
@ -144,7 +144,10 @@ abstract class AbsPlayerFragment(@LayoutRes layout: Int) : AbsMainActivityFragme
return true return true
} }
R.id.now_playing -> { R.id.now_playing -> {
NavigationUtil.goToPlayingQueue(requireActivity()) requireActivity().findNavController(R.id.fragment_container).navigate(
R.id.playing_queue_fragment,
null
)
return true return true
} }
R.id.action_show_lyrics -> { R.id.action_show_lyrics -> {
@ -325,6 +328,7 @@ abstract class AbsPlayerFragment(@LayoutRes layout: Int) : AbsMainActivityFragme
requireView() requireView()
) )
) )
playerToolbar()?.menu?.findItem(R.id.action_toggle_lyrics)?.let { showLyricsIcon(it) }
} }
class SwipeDetector(val context: Context, val viewPager: ViewPager?, val view: View) : 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 currentFragment(R.id.fragment_container)?.exitTransition = null
//Hide Bottom Bar First, else Bottom Sheet doesn't collapse fully //Hide Bottom Bar First, else Bottom Sheet doesn't collapse fully
setBottomBarVisibility(false) setBottomNavVisibility(false)
if (getBottomSheetBehavior().state == BottomSheetBehavior.STATE_EXPANDED) { if (getBottomSheetBehavior().state == BottomSheetBehavior.STATE_EXPANDED) {
collapsePanel() collapsePanel()
} }
@ -402,7 +406,7 @@ fun goToAlbum(activity: Activity) {
currentFragment(R.id.fragment_container)?.exitTransition = null currentFragment(R.id.fragment_container)?.exitTransition = null
//Hide Bottom Bar First, else Bottom Sheet doesn't collapse fully //Hide Bottom Bar First, else Bottom Sheet doesn't collapse fully
setBottomBarVisibility(false) setBottomNavVisibility(false)
if (getBottomSheetBehavior().state == BottomSheetBehavior.STATE_EXPANDED) { if (getBottomSheetBehavior().state == BottomSheetBehavior.STATE_EXPANDED) {
collapsePanel() collapsePanel()
} }

View file

@ -86,7 +86,7 @@ abstract class AbsRecyclerViewCustomGridSizeFragment<A : RecyclerView.Adapter<*>
} else { } else {
saveGridSize(gridSize) saveGridSize(gridSize)
} }
recyclerView().isVisible = false recyclerView.isVisible = false
invalidateLayoutManager() invalidateLayoutManager()
// only recreate the adapter and layout manager if the layout currentLayoutRes has changed // only recreate the adapter and layout manager if the layout currentLayoutRes has changed
if (oldLayoutRes != itemLayoutRes()) { if (oldLayoutRes != itemLayoutRes()) {
@ -95,10 +95,10 @@ abstract class AbsRecyclerViewCustomGridSizeFragment<A : RecyclerView.Adapter<*>
setGridSize(gridSize) setGridSize(gridSize)
} }
val transition = MaterialFade().apply { val transition = MaterialFade().apply {
addTarget(recyclerView()) addTarget(recyclerView)
} }
TransitionManager.beginDelayedTransition(getContainer(), transition) TransitionManager.beginDelayedTransition(container, transition)
recyclerView().isVisible = true recyclerView.isVisible = true
} }
protected abstract fun setGridSize(gridSize: Int) protected abstract fun setGridSize(gridSize: Int)

View file

@ -18,8 +18,10 @@ import android.os.Bundle
import android.view.* import android.view.*
import androidx.annotation.NonNull import androidx.annotation.NonNull
import androidx.annotation.StringRes import androidx.annotation.StringRes
import androidx.coordinatorlayout.widget.CoordinatorLayout import androidx.appcompat.widget.Toolbar
import androidx.core.view.doOnPreDraw import androidx.core.view.doOnPreDraw
import androidx.core.view.isVisible
import androidx.core.view.updateLayoutParams
import androidx.core.view.updatePadding import androidx.core.view.updatePadding
import androidx.navigation.fragment.findNavController import androidx.navigation.fragment.findNavController
import androidx.recyclerview.widget.RecyclerView 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.databinding.FragmentMainRecyclerBinding
import code.name.monkey.retromusic.dialogs.CreatePlaylistDialog import code.name.monkey.retromusic.dialogs.CreatePlaylistDialog
import code.name.monkey.retromusic.dialogs.ImportPlaylistDialog 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.helper.MusicPlayerRemote
import code.name.monkey.retromusic.util.DensityUtil
import code.name.monkey.retromusic.util.ThemedFastScroller.create 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.MaterialFadeThrough
import com.google.android.material.transition.MaterialSharedAxis import com.google.android.material.transition.MaterialSharedAxis
import me.zhanghai.android.fastscroll.FastScroller import me.zhanghai.android.fastscroll.FastScroller
@ -44,21 +49,57 @@ abstract class AbsRecyclerViewFragment<A : RecyclerView.Adapter<*>, LM : Recycle
private val binding get() = _binding!! private val binding get() = _binding!!
protected var adapter: A? = null protected var adapter: A? = null
protected var layoutManager: LM? = null protected var layoutManager: LM? = null
val shuffleButton get() = binding.shuffleButton
abstract val isShuffleVisible: Boolean
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
_binding = FragmentMainRecyclerBinding.bind(view) _binding = FragmentMainRecyclerBinding.bind(view)
enterTransition = MaterialFadeThrough()
exitTransition = MaterialFadeThrough()
postponeEnterTransition() postponeEnterTransition()
view.doOnPreDraw { startPostponedEnterTransition() } view.doOnPreDraw { startPostponedEnterTransition() }
enterTransition = MaterialFadeThrough().apply {
addTarget(binding.recyclerView)
}
mainActivity.setSupportActionBar(binding.toolbar) mainActivity.setSupportActionBar(binding.toolbar)
mainActivity.supportActionBar?.title = null mainActivity.supportActionBar?.title = null
initLayoutManager() initLayoutManager()
initAdapter() initAdapter()
setUpRecyclerView() setUpRecyclerView()
setupToolbar() 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<ViewGroup.MarginLayoutParams> {
bottomMargin = it
}
})
}
open fun onShuffleClicked() {
}
fun toolbar(): Toolbar {
return binding.toolbar
} }
private fun setupToolbar() { private fun setupToolbar() {
@ -73,6 +114,9 @@ abstract class AbsRecyclerViewFragment<A : RecyclerView.Adapter<*>, LM : Recycle
} }
val appName = resources.getString(titleRes) val appName = resources.getString(titleRes)
binding.appNameText.text = appName binding.appNameText.text = appName
binding.toolbarContainer.drawNextToNavbar()
binding.appBarLayout.statusBarForeground =
MaterialShapeDrawable.createWithElevationOverlay(requireContext())
} }
abstract val titleRes: Int abstract val titleRes: Int
@ -117,10 +161,10 @@ abstract class AbsRecyclerViewFragment<A : RecyclerView.Adapter<*>, LM : Recycle
val itemCount: Int = adapter?.itemCount ?: 0 val itemCount: Int = adapter?.itemCount ?: 0
if (itemCount > 0 && MusicPlayerRemote.playingQueue.isNotEmpty()) { 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) binding.recyclerView.updatePadding(0, 0, 0, height)
} else { } else {
val height = DensityUtil.dip2px(requireContext(), 56f) val height = dip(R.dimen.mini_player_height)
binding.recyclerView.updatePadding(0, 0, 0, height) binding.recyclerView.updatePadding(0, 0, 0, height)
} }
} }
@ -155,16 +199,12 @@ abstract class AbsRecyclerViewFragment<A : RecyclerView.Adapter<*>, LM : Recycle
binding.recyclerView.adapter = adapter binding.recyclerView.adapter = adapter
} }
fun recyclerView(): RecyclerView { val recyclerView get() = binding.recyclerView
return binding.recyclerView
}
fun getContainer(): CoordinatorLayout { val container get() = binding.root
return binding.root
}
fun scrollToTop() { fun scrollToTop() {
recyclerView().scrollToPosition(0) recyclerView.scrollToPosition(0)
binding.appBarLayout.setExpanded(true, true) binding.appBarLayout.setExpanded(true, true)
} }

View file

@ -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<List<File>>, 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<File> 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<Storage> 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<List<File>> 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<List<File>> loader, List<File> data) {
updateAdapter(data);
}
@Override
public void onLoaderReset(@NonNull Loader<List<File>> loader) {
updateAdapter(new LinkedList<>());
}
@Override
public void onMultipleItemAction(MenuItem item, @NotNull ArrayList<File> 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<File> 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<File> toList(File file) {
ArrayList<File> files = new ArrayList<>(1);
files.add(file);
return files;
}
private void updateAdapter(@NonNull List<File> 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<ListPathsAsyncTask.LoadingInfo, String, String[]> {
private final WeakReference<OnPathsListedCallback> 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<File> 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<List<File>> {
private final WeakReference<FoldersFragment> fragmentWeakReference;
AsyncFileLoader(FoldersFragment foldersFragment) {
super(foldersFragment.requireActivity());
fragmentWeakReference = new WeakReference<>(foldersFragment);
}
@Override
public List<File> 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<File> files = FileUtil.listFiles(directory, AUDIO_FILE_FILTER);
Collections.sort(files, foldersFragment.getFileComparator());
return files;
} else {
return new LinkedList<>();
}
}
}
private static class ListSongsAsyncTask
extends ListingFilesDialogAsyncTask<ListSongsAsyncTask.LoadingInfo, Void, List<Song>> {
private final Object extra;
private final WeakReference<OnSongsListedCallback> callbackWeakReference;
private final WeakReference<Context> contextWeakReference;
ListSongsAsyncTask(Context context, Object extra, OnSongsListedCallback callback) {
super(context);
this.extra = extra;
contextWeakReference = new WeakReference<>(context);
callbackWeakReference = new WeakReference<>(callback);
}
@Override
protected List<Song> doInBackground(LoadingInfo... params) {
try {
LoadingInfo info = params[0];
List<File> 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<Song> 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<Song> songs, Object extra);
}
static class LoadingInfo {
final Comparator<File> fileComparator;
final FileFilter fileFilter;
final List<File> files;
LoadingInfo(
@NonNull List<File> files,
@NonNull FileFilter fileFilter,
@NonNull Comparator<File> fileComparator) {
this.fileComparator = fileComparator;
this.fileFilter = fileFilter;
this.files = files;
}
}
}
private abstract static class ListingFilesDialogAsyncTask<Params, Progress, Result>
extends DialogAsyncTask<Params, Progress, Result> {
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<String> 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();
}
}

View file

@ -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<List<File>>, 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<Storage>()
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<List<File>> {
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<Song>, 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<String?>) {
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<Song>, 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<String?>) {
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<Song>, 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<String?>) {
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<List<File>>, data: List<File>) {
updateAdapter(data)
}
override fun onLoaderReset(loader: Loader<List<File>>) {
updateAdapter(LinkedList())
}
override fun onMultipleItemAction(item: MenuItem, files: ArrayList<File>) {
val itemId = item.itemId
ListSongsAsyncTask(
activity,
null,
object : OnSongsListedCallback {
override fun onSongsListed(songs: List<Song>, 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<String?>) {
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<String?>) {
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<File> {
val files = ArrayList<File>(1)
files.add(file)
return files
}
private fun updateAdapter(files: List<File>) {
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<ListPathsAsyncTask.LoadingInfo, String?, Array<String?>>(
context
) {
private val onPathsListedCallbackWeakReference: WeakReference<OnPathsListedCallback> =
WeakReference(callback)
override fun doInBackground(vararg params: LoadingInfo): Array<String?> {
return try {
if (isCancelled || checkCallbackReference() == null) {
return arrayOf()
}
val info = params[0]
val paths: Array<String?>
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<String?>) {
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<String?>)
}
class LoadingInfo(val file: File, val fileFilter: FileFilter)
}
private class AsyncFileLoader(foldersFragment: FoldersFragment) :
WrappedAsyncTaskLoader<List<File>>(foldersFragment.requireActivity()) {
private val fragmentWeakReference: WeakReference<FoldersFragment> =
WeakReference(foldersFragment)
override fun loadInBackground(): List<File> {
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<ListSongsAsyncTask.LoadingInfo, Void, List<Song>>(context) {
private val callbackWeakReference = WeakReference(callback)
private val contextWeakReference = WeakReference(context)
override fun doInBackground(vararg params: LoadingInfo): List<Song> {
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<Song>) {
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<Song>, extra: Any?)
}
class LoadingInfo(
val files: List<File>,
val fileFilter: FileFilter,
val fileComparator: Comparator<File>
)
}
abstract class ListingFilesDialogAsyncTask<Params, Progress, Result> :
DialogAsyncTask<Params, Progress, Result> {
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
}
}
}
}

View file

@ -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.helper.menu.GenreMenuHelper
import code.name.monkey.retromusic.model.Genre import code.name.monkey.retromusic.model.Genre
import code.name.monkey.retromusic.model.Song import code.name.monkey.retromusic.model.Song
import com.google.android.material.shape.MaterialShapeDrawable
import com.google.android.material.transition.MaterialSharedAxis import com.google.android.material.transition.MaterialSharedAxis
import org.koin.androidx.viewmodel.ext.android.viewModel import org.koin.androidx.viewmodel.ext.android.viewModel
import org.koin.core.parameter.parametersOf import org.koin.core.parameter.parametersOf
@ -67,6 +68,8 @@ class GenreDetailsFragment : AbsMainActivityFragment(R.layout.fragment_playlist_
view.doOnPreDraw { view.doOnPreDraw {
startPostponedEnterTransition() startPostponedEnterTransition()
} }
binding.appBarLayout.statusBarForeground =
MaterialShapeDrawable.createWithElevationOverlay(requireContext())
} }
private fun setupRecyclerView() { private fun setupRecyclerView() {

View file

@ -19,6 +19,7 @@ import android.view.Menu
import android.view.MenuInflater import android.view.MenuInflater
import android.view.MenuItem import android.view.MenuItem
import android.view.View import android.view.View
import androidx.activity.addCallback
import androidx.core.os.bundleOf import androidx.core.os.bundleOf
import androidx.navigation.fragment.findNavController import androidx.navigation.fragment.findNavController
import androidx.recyclerview.widget.GridLayoutManager 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.EXTRA_GENRE
import code.name.monkey.retromusic.R import code.name.monkey.retromusic.R
import code.name.monkey.retromusic.adapter.GenreAdapter 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.ReloadType
import code.name.monkey.retromusic.fragments.base.AbsRecyclerViewFragment import code.name.monkey.retromusic.fragments.base.AbsRecyclerViewFragment
import code.name.monkey.retromusic.interfaces.IGenreClickListener import code.name.monkey.retromusic.interfaces.IGenreClickListener
import code.name.monkey.retromusic.model.Genre import code.name.monkey.retromusic.model.Genre
import code.name.monkey.retromusic.util.PreferenceUtil
import code.name.monkey.retromusic.util.RetroUtil import code.name.monkey.retromusic.util.RetroUtil
import com.google.android.gms.cast.framework.CastButtonFactory import com.google.android.gms.cast.framework.CastButtonFactory
import com.google.android.material.transition.MaterialSharedAxis import com.google.android.material.transition.MaterialSharedAxis
@ -45,6 +48,10 @@ GenresFragment : AbsRecyclerViewFragment<GenreAdapter, LinearLayoutManager>(),
else else
adapter?.swapDataSet(listOf()) adapter?.swapDataSet(listOf())
}) })
requireActivity().onBackPressedDispatcher.addCallback(viewLifecycleOwner) {
remove()
mainActivity.finish()
}
} }
override fun createLayoutManager(): LinearLayoutManager { override fun createLayoutManager(): LinearLayoutManager {
@ -82,6 +89,9 @@ GenresFragment : AbsRecyclerViewFragment<GenreAdapter, LinearLayoutManager>(),
override val emptyMessage: Int override val emptyMessage: Int
get() = R.string.no_genres get() = R.string.no_genres
override val isShuffleVisible: Boolean
get() = false
companion object { companion object {
@JvmField @JvmField
val TAG: String = GenresFragment::class.java.simpleName val TAG: String = GenresFragment::class.java.simpleName

View file

@ -14,19 +14,19 @@
*/ */
package code.name.monkey.retromusic.fragments.home package code.name.monkey.retromusic.fragments.home
import android.app.ActivityOptions
import android.os.Bundle import android.os.Bundle
import android.view.Menu import android.view.Menu
import android.view.MenuInflater import android.view.MenuInflater
import android.view.MenuItem import android.view.MenuItem
import android.view.MenuItem.SHOW_AS_ACTION_IF_ROOM import android.view.MenuItem.SHOW_AS_ACTION_IF_ROOM
import android.view.View import android.view.View
import androidx.activity.addCallback
import androidx.core.os.bundleOf import androidx.core.os.bundleOf
import androidx.core.text.HtmlCompat import androidx.core.text.HtmlCompat
import androidx.core.view.doOnPreDraw import androidx.core.view.doOnPreDraw
import androidx.navigation.fragment.FragmentNavigatorExtras
import androidx.navigation.fragment.findNavController import androidx.navigation.fragment.findNavController
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import code.name.monkey.appthemehelper.ThemeStore
import code.name.monkey.appthemehelper.common.ATHToolbarActivity import code.name.monkey.appthemehelper.common.ATHToolbarActivity
import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper
import code.name.monkey.retromusic.* 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.databinding.FragmentHomeBinding
import code.name.monkey.retromusic.dialogs.CreatePlaylistDialog import code.name.monkey.retromusic.dialogs.CreatePlaylistDialog
import code.name.monkey.retromusic.dialogs.ImportPlaylistDialog 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.fragments.base.AbsMainActivityFragment
import code.name.monkey.retromusic.glide.GlideApp import code.name.monkey.retromusic.glide.GlideApp
import code.name.monkey.retromusic.glide.RetroGlideExtension import code.name.monkey.retromusic.glide.RetroGlideExtension
import code.name.monkey.retromusic.util.NavigationUtil
import code.name.monkey.retromusic.util.PreferenceUtil import code.name.monkey.retromusic.util.PreferenceUtil
import com.google.android.gms.cast.framework.CastButtonFactory 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.MaterialFadeThrough
import com.google.android.material.transition.MaterialSharedAxis import com.google.android.material.transition.MaterialSharedAxis
@ -53,18 +55,45 @@ class HomeFragment :
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
_binding = getBinding(PreferenceUtil.isHomeBanner, view) _binding = getBinding(PreferenceUtil.isHomeBanner, view)
enterTransition = MaterialFadeThrough()
exitTransition = MaterialFadeThrough()
mainActivity.setSupportActionBar(binding.toolbar) mainActivity.setSupportActionBar(binding.toolbar)
mainActivity.supportActionBar?.title = null 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 { binding.bannerImage?.setOnClickListener {
val options = ActivityOptions.makeSceneTransitionAnimation( findNavController().navigate(
mainActivity, R.id.user_info_fragment, null, null, FragmentNavigatorExtras(
binding.userImage, binding.userImage to "user_image"
getString(R.string.transition_user_image) )
) )
NavigationUtil.goToUserInfo(requireActivity(), options) reenterTransition = null
} }
binding.lastAdded.setOnClickListener { binding.lastAdded.setOnClickListener {
@ -96,28 +125,12 @@ class HomeFragment :
} }
binding.userImage.setOnClickListener { binding.userImage.setOnClickListener {
val options = ActivityOptions.makeSceneTransitionAnimation( findNavController().navigate(
mainActivity, R.id.user_info_fragment, null, null, FragmentNavigatorExtras(
binding.userImage, binding.userImage to "user_image"
getString(R.string.transition_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 { private fun getBinding(homeBanner: Boolean, view: View): HomeBindingAdapter {
@ -137,8 +150,7 @@ class HomeFragment :
MaterialSharedAxis(MaterialSharedAxis.Z, false) MaterialSharedAxis(MaterialSharedAxis.Z, false)
findNavController().navigate(R.id.searchFragment, null, navOptions) findNavController().navigate(R.id.searchFragment, null, navOptions)
} }
val color = ThemeStore.accentColor(requireContext()) val hexColor = String.format("#%06X", 0xFFFFFF and accentColor())
val hexColor = String.format("#%06X", 0xFFFFFF and color)
val appName = HtmlCompat.fromHtml( val appName = HtmlCompat.fromHtml(
"Retro <span style='color:$hexColor';>Music</span>", "Retro <span style='color:$hexColor';>Music</span>",
HtmlCompat.FROM_HTML_MODE_COMPACT HtmlCompat.FROM_HTML_MODE_COMPACT

View file

@ -49,7 +49,7 @@ class LibraryFragment : AbsMainActivityFragment(R.layout.fragment_library) {
override fun onActivityCreated(savedInstanceState: Bundle?) { override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState) super.onActivityCreated(savedInstanceState)
setHasOptionsMenu(true) setHasOptionsMenu(true)
mainActivity.setBottomBarVisibility(true) mainActivity.setBottomNavVisibility(true)
mainActivity.setSupportActionBar(binding.toolbar) mainActivity.setSupportActionBar(binding.toolbar)
mainActivity.supportActionBar?.title = null mainActivity.supportActionBar?.title = null
binding.toolbar.setNavigationOnClickListener { binding.toolbar.setNavigationOnClickListener {
@ -84,7 +84,7 @@ class LibraryFragment : AbsMainActivityFragment(R.layout.fragment_library) {
navGraph.setStartDestination(categoryInfo.category.id) navGraph.setStartDestination(categoryInfo.category.id)
} }
navController.graph = navGraph navController.graph = navGraph
NavigationUI.setupWithNavController(mainActivity.getBottomNavigationView(), navController) NavigationUI.setupWithNavController(mainActivity.bottomNavigationView, navController)
navController.addOnDestinationChangedListener { _, _, _ -> navController.addOnDestinationChangedListener { _, _, _ ->
binding.appBarLayout.setExpanded(true, true) binding.appBarLayout.setExpanded(true, true)
} }

View file

@ -12,7 +12,7 @@
* See the GNU General Public License for more details. * 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.os.Bundle
import android.view.View 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.Album
import code.name.monkey.retromusic.model.Artist import code.name.monkey.retromusic.model.Artist
import code.name.monkey.retromusic.util.RetroUtil import code.name.monkey.retromusic.util.RetroUtil
import com.google.android.material.shape.MaterialShapeDrawable
import com.google.android.material.transition.MaterialSharedAxis import com.google.android.material.transition.MaterialSharedAxis
class DetailListFragment : AbsMainActivityFragment(R.layout.fragment_playlist_detail), 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) returnTransition = MaterialSharedAxis(MaterialSharedAxis.Y, false)
} }
} }
binding.appBarLayout.statusBarForeground =
MaterialShapeDrawable.createWithElevationOverlay(requireContext())
postponeEnterTransition() postponeEnterTransition()
view.doOnPreDraw { startPostponedEnterTransition() } view.doOnPreDraw { startPostponedEnterTransition() }
} }

View file

@ -12,7 +12,7 @@
* See the GNU General Public License for more details. * 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.os.Bundle
import android.text.InputType import android.text.InputType
@ -20,13 +20,14 @@ import android.view.*
import androidx.core.view.ViewCompat import androidx.core.view.ViewCompat
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentActivity import androidx.fragment.app.FragmentActivity
import androidx.navigation.fragment.findNavController
import androidx.transition.Fade
import androidx.viewpager2.adapter.FragmentStateAdapter import androidx.viewpager2.adapter.FragmentStateAdapter
import code.name.monkey.appthemehelper.ThemeStore
import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper
import code.name.monkey.retromusic.R import code.name.monkey.retromusic.R
import code.name.monkey.retromusic.activities.base.AbsMusicServiceActivity import code.name.monkey.retromusic.activities.MainActivity
import code.name.monkey.retromusic.activities.tageditor.WriteTagsAsyncTask 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.FragmentNormalLyricsBinding
import code.name.monkey.retromusic.databinding.FragmentSyncedLyricsBinding import code.name.monkey.retromusic.databinding.FragmentSyncedLyricsBinding
import code.name.monkey.retromusic.extensions.accentColor import code.name.monkey.retromusic.extensions.accentColor
@ -53,12 +54,16 @@ import org.jaudiotagger.tag.FieldKey
import java.io.File import java.io.File
import java.util.* 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 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 private val googleSearchLrcUrl: String
get() { get() {
@ -80,34 +85,30 @@ class LyricsActivity : AbsMusicServiceActivity() {
private fun buildContainerTransform(): MaterialContainerTransform { private fun buildContainerTransform(): MaterialContainerTransform {
val transform = MaterialContainerTransform() val transform = MaterialContainerTransform()
transform.setAllContainerColors( 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.addTarget(R.id.container)
transform.duration = 300 transform.duration = 300
return transform return transform
} }
override fun onCreate(savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onViewCreated(view, savedInstanceState)
binding = ActivityLyricsBinding.inflate(layoutInflater) enterTransition = Fade()
setContentView(binding.root) exitTransition = Fade()
lyricsSectionsAdapter = LyricsSectionsAdapter(requireActivity())
_binding = FragmentLyricsBinding.bind(view)
ViewCompat.setTransitionName(binding.container, "lyrics") ViewCompat.setTransitionName(binding.container, "lyrics")
setStatusbarColorAuto()
setTaskDescriptionColorAuto()
setNavigationbarColorAuto()
setupWakelock() setupWakelock()
binding.toolbar.setBackgroundColor(surfaceColor())
binding.tabLyrics.setBackgroundColor(surfaceColor()) binding.tabLyrics.setBackgroundColor(surfaceColor())
binding.container.setBackgroundColor(surfaceColor()) binding.container.setBackgroundColor(surfaceColor())
ToolbarContentTintHelper.colorBackButton(binding.toolbar)
setSupportActionBar(binding.toolbar)
setupViews() setupViews()
setupToolbar()
updateTitleSong()
} }
private fun setupViews() { private fun setupViews() {
binding.lyricsPager.adapter = lyricsSectionsAdapter binding.lyricsPager.adapter = lyricsSectionsAdapter
TabLayoutMediator(binding.tabLyrics, binding.lyricsPager) { tab, position -> TabLayoutMediator(binding.tabLyrics, binding.lyricsPager) { tab, position ->
@ -119,10 +120,29 @@ class LyricsActivity : AbsMusicServiceActivity() {
}.attach() }.attach()
// lyricsPager.isUserInputEnabled = false // lyricsPager.isUserInputEnabled = false
binding.tabLyrics.setSelectedTabIndicatorColor(ThemeStore.accentColor(this)) binding.tabLyrics.setSelectedTabIndicatorColor(accentColor())
binding.tabLyrics.setTabTextColors(textColorSecondary(), 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() { override fun onPlayingMetaChanged() {
super.onPlayingMetaChanged() super.onPlayingMetaChanged()
@ -141,36 +161,27 @@ class LyricsActivity : AbsMusicServiceActivity() {
} }
private fun setupWakelock() { 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 { override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
menuInflater.inflate(R.menu.menu_search, menu) inflater.inflate(R.menu.menu_search, menu)
return super.onCreateOptionsMenu(menu) return super.onCreateOptionsMenu(menu, inflater)
} }
override fun onOptionsItemSelected(item: MenuItem): Boolean { override fun onOptionsItemSelected(item: MenuItem): Boolean {
if (item.itemId == android.R.id.home) { if (item.itemId == android.R.id.home) {
finish() findNavController().navigateUp()
return true return true
} }
if (item.itemId == R.id.action_search) { if (item.itemId == R.id.action_search) {
RetroUtil.openUrl( RetroUtil.openUrl(
this, when (binding.lyricsPager.currentItem) { requireActivity(), when (binding.lyricsPager.currentItem) {
0 -> syairSearchLrcUrl 0 -> syairSearchLrcUrl
1 -> googleSearchLrcUrl 1 -> googleSearchLrcUrl
else -> googleSearchLrcUrl else -> googleSearchLrcUrl
} }
) )
} else if (item.itemId == R.id.action_edit) {
when (binding.lyricsPager.currentItem) {
0 -> {
editSyncedLyrics()
}
1 -> {
editNormalLyrics()
}
}
} }
return super.onOptionsItemSelected(item) return super.onOptionsItemSelected(item)
} }
@ -185,7 +196,7 @@ class LyricsActivity : AbsMusicServiceActivity() {
e.printStackTrace() e.printStackTrace()
} }
MaterialDialog(this, BottomSheet(LayoutMode.WRAP_CONTENT)).show { MaterialDialog(requireContext(), BottomSheet(LayoutMode.WRAP_CONTENT)).show {
title(res = R.string.edit_normal_lyrics) title(res = R.string.edit_normal_lyrics)
input( input(
hintRes = R.string.paste_lyrics_here, hintRes = R.string.paste_lyrics_here,
@ -194,7 +205,7 @@ class LyricsActivity : AbsMusicServiceActivity() {
) { _, input -> ) { _, input ->
val fieldKeyValueMap = EnumMap<FieldKey, String>(FieldKey::class.java) val fieldKeyValueMap = EnumMap<FieldKey, String>(FieldKey::class.java)
fieldKeyValueMap[FieldKey.LYRICS] = input.toString() fieldKeyValueMap[FieldKey.LYRICS] = input.toString()
WriteTagsAsyncTask(this@LyricsActivity).execute( WriteTagsAsyncTask(requireActivity()).execute(
LoadingInfo( LoadingInfo(
listOf(song.data), fieldKeyValueMap, null listOf(song.data), fieldKeyValueMap, null
) )
@ -217,7 +228,7 @@ class LyricsActivity : AbsMusicServiceActivity() {
} }
val content: String = LyricUtil.getStringFromLrc(lrcFile) 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) title(res = R.string.edit_synced_lyrics)
input( input(
hintRes = R.string.paste_timeframe_lyrics_here, hintRes = R.string.paste_timeframe_lyrics_here,
@ -320,10 +331,10 @@ class LyricsActivity : AbsMusicServiceActivity() {
private fun setupLyricsView() { private fun setupLyricsView() {
binding.lyricsView.apply { binding.lyricsView.apply {
setCurrentColor(ThemeStore.accentColor(context)) setCurrentColor(accentColor())
setTimeTextColor(ThemeStore.accentColor(context)) setTimeTextColor(accentColor())
setTimelineColor(ThemeStore.accentColor(context)) setTimelineColor(accentColor())
setTimelineTextColor(ThemeStore.accentColor(context)) setTimelineTextColor(accentColor())
setDraggable(true, LrcView.OnPlayClickListener { setDraggable(true, LrcView.OnPlayClickListener {
MusicPlayerRemote.seekTo(it.toInt()) MusicPlayerRemote.seekTo(it.toInt())
return@OnPlayClickListener true return@OnPlayClickListener true
@ -356,5 +367,8 @@ class LyricsActivity : AbsMusicServiceActivity() {
} }
} }
override fun onDestroyView() {
super.onDestroyView()
(requireActivity() as MainActivity).expandPanel()
}
} }

View file

@ -12,7 +12,7 @@
* See the GNU General Public License for more details. * 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.animation.ObjectAnimator
import android.annotation.SuppressLint 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.textColorPrimary
import code.name.monkey.retromusic.extensions.textColorSecondary import code.name.monkey.retromusic.extensions.textColorSecondary
import code.name.monkey.retromusic.fragments.base.AbsMusicServiceFragment 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.MusicPlayerRemote
import code.name.monkey.retromusic.helper.MusicProgressViewUpdateHelper import code.name.monkey.retromusic.helper.MusicProgressViewUpdateHelper
import code.name.monkey.retromusic.helper.PlayPauseButtonOnClickHandler import code.name.monkey.retromusic.helper.PlayPauseButtonOnClickHandler

View file

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

View file

@ -12,25 +12,34 @@
* See the GNU General Public License for more details. * 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.app.Activity
import android.content.Intent import android.content.Intent
import android.content.res.ColorStateList import android.content.res.ColorStateList
import android.graphics.Bitmap import android.graphics.Bitmap
import android.graphics.Color
import android.net.Uri import android.net.Uri
import android.os.Bundle import android.os.Bundle
import android.text.TextUtils import android.text.TextUtils
import android.view.LayoutInflater
import android.view.MenuItem import android.view.MenuItem
import android.view.View
import android.view.ViewGroup
import android.widget.Toast 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.ColorUtil
import code.name.monkey.appthemehelper.util.MaterialValueHelper import code.name.monkey.appthemehelper.util.MaterialValueHelper
import code.name.monkey.retromusic.Constants.USER_BANNER import code.name.monkey.retromusic.Constants.USER_BANNER
import code.name.monkey.retromusic.Constants.USER_PROFILE import code.name.monkey.retromusic.Constants.USER_PROFILE
import code.name.monkey.retromusic.activities.base.AbsBaseActivity import code.name.monkey.retromusic.R
import code.name.monkey.retromusic.databinding.ActivityUserInfoBinding import code.name.monkey.retromusic.databinding.FragmentUserInfoBinding
import code.name.monkey.retromusic.extensions.accentColor import code.name.monkey.retromusic.extensions.accentColor
import code.name.monkey.retromusic.extensions.applyToolbar 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.GlideApp
import code.name.monkey.retromusic.glide.RetroGlideExtension import code.name.monkey.retromusic.glide.RetroGlideExtension
import code.name.monkey.retromusic.util.ImageUtil 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.bumptech.glide.request.target.Target
import com.github.dhaval2404.imagepicker.ImagePicker import com.github.dhaval2404.imagepicker.ImagePicker
import com.github.dhaval2404.imagepicker.constant.ImageProvider import com.github.dhaval2404.imagepicker.constant.ImageProvider
import com.google.android.material.transition.MaterialContainerTransform
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import org.koin.androidx.viewmodel.ext.android.sharedViewModel
import java.io.BufferedOutputStream import java.io.BufferedOutputStream
import java.io.File import java.io.File
import java.io.FileOutputStream import java.io.FileOutputStream
import java.io.IOException 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?) { override fun onCreateView(
super.onCreate(savedInstanceState) inflater: LayoutInflater,
binding = ActivityUserInfoBinding.inflate(layoutInflater) container: ViewGroup?,
setContentView(binding.root) savedInstanceState: Bundle?
setStatusbarColorAuto() ): View {
setNavigationbarColorAuto() sharedElementEnterTransition = MaterialContainerTransform().apply {
setTaskDescriptionColorAuto() drawingViewId = R.id.fragment_container
setLightNavigationBar(true) 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) applyToolbar(binding.toolbar)
binding.nameContainer.accentColor() binding.nameContainer.accentColor()
@ -80,20 +101,35 @@ class UserInfoActivity : AbsBaseActivity() {
binding.next.setOnClickListener { binding.next.setOnClickListener {
val nameString = binding.name.text.toString().trim { it <= ' ' } val nameString = binding.name.text.toString().trim { it <= ' ' }
if (TextUtils.isEmpty(nameString)) { 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 return@setOnClickListener
} }
PreferenceUtil.userName = nameString PreferenceUtil.userName = nameString
setResult(Activity.RESULT_OK) findNavController().navigateUp()
finish()
} }
val textColor = val textColor =
MaterialValueHelper.getPrimaryTextColor(this, ColorUtil.isColorLight(accentColor())) MaterialValueHelper.getPrimaryTextColor(
requireContext(),
ColorUtil.isColorLight(accentColor())
)
binding.next.backgroundTintList = ColorStateList.valueOf(accentColor()) binding.next.backgroundTintList = ColorStateList.valueOf(accentColor())
binding.next.iconTint = ColorStateList.valueOf(textColor) binding.next.iconTint = ColorStateList.valueOf(textColor)
binding.next.setTextColor(textColor) binding.next.setTextColor(textColor)
loadProfile() loadProfile()
postponeEnterTransition()
view.doOnPreDraw {
startPostponedEnterTransition()
}
libraryViewModel.getFabMargin().observe(viewLifecycleOwner, {
binding.next.updateLayoutParams<ViewGroup.MarginLayoutParams> {
bottomMargin = it
}
})
} }
private fun loadProfile() { private fun loadProfile() {
@ -112,7 +148,7 @@ class UserInfoActivity : AbsBaseActivity() {
override fun onOptionsItemSelected(item: MenuItem): Boolean { override fun onOptionsItemSelected(item: MenuItem): Boolean {
if (item.itemId == android.R.id.home) { if (item.itemId == android.R.id.home) {
onBackPressed() findNavController().navigateUp()
} }
return super.onOptionsItemSelected(item) return super.onOptionsItemSelected(item)
} }
@ -133,7 +169,7 @@ class UserInfoActivity : AbsBaseActivity() {
.start(PICK_IMAGE_REQUEST) .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) super.onActivityResult(requestCode, resultCode, data)
if (resultCode == Activity.RESULT_OK && requestCode == PICK_IMAGE_REQUEST) { if (resultCode == Activity.RESULT_OK && requestCode == PICK_IMAGE_REQUEST) {
val fileUri = data?.data val fileUri = data?.data
@ -142,9 +178,9 @@ class UserInfoActivity : AbsBaseActivity() {
val fileUri = data?.data val fileUri = data?.data
fileUri?.let { setAndSaveBannerImage(it) } fileUri?.let { setAndSaveBannerImage(it) }
} else if (resultCode == ImagePicker.RESULT_ERROR) { } 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 { } 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) { private fun saveImage(bitmap: Bitmap, fileName: String) {
CoroutineScope(Dispatchers.IO).launch { CoroutineScope(Dispatchers.IO).launch {
val appDir = applicationContext.filesDir val appDir = requireContext().filesDir
val file = File(appDir, fileName) val file = File(appDir, fileName)
var successful = false var successful = false
try { try {
@ -192,7 +228,7 @@ class UserInfoActivity : AbsBaseActivity() {
} }
if (successful) { if (successful) {
withContext(Dispatchers.Main) { 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) .into(binding.userImage)
} }
override fun onDestroyView() {
super.onDestroyView()
_binding = null
}
companion object { companion object {
private const val PICK_IMAGE_REQUEST = 9002 private const val PICK_IMAGE_REQUEST = 9002
private const val PICK_BANNER_REQUEST = 9004 private const val PICK_BANNER_REQUEST = 9004

View file

@ -12,7 +12,7 @@
* See the GNU General Public License for more details. * 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.content.Context
import android.graphics.Color import android.graphics.Color

View file

@ -281,7 +281,7 @@ class PlayerAlbumCoverFragment : AbsMusicServiceFragment(R.layout.fragment_playe
private fun updatePlayingQueue() { private fun updatePlayingQueue() {
binding.viewPager.apply { binding.viewPager.apply {
adapter = AlbumCoverPagerAdapter(childFragmentManager, MusicPlayerRemote.playingQueue) adapter = AlbumCoverPagerAdapter(childFragmentManager, MusicPlayerRemote.playingQueue)
adapter!!.notifyDataSetChanged() adapter?.notifyDataSetChanged()
currentItem = MusicPlayerRemote.position currentItem = MusicPlayerRemote.position
onPageSelected(MusicPlayerRemote.position) onPageSelected(MusicPlayerRemote.position)
} }

View file

@ -21,6 +21,7 @@ import code.name.monkey.appthemehelper.util.ATHUtil
import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper
import code.name.monkey.retromusic.R import code.name.monkey.retromusic.R
import code.name.monkey.retromusic.databinding.FragmentAdaptivePlayerBinding 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.surfaceColor
import code.name.monkey.retromusic.extensions.textColorPrimary import code.name.monkey.retromusic.extensions.textColorPrimary
import code.name.monkey.retromusic.extensions.textColorSecondary import code.name.monkey.retromusic.extensions.textColorSecondary
@ -46,6 +47,7 @@ class AdaptiveFragment : AbsPlayerFragment(R.layout.fragment_adaptive_player) {
_binding = FragmentAdaptivePlayerBinding.bind(view) _binding = FragmentAdaptivePlayerBinding.bind(view)
setUpSubFragments() setUpSubFragments()
setUpPlayerToolbar() setUpPlayerToolbar()
binding.root.drawAboveSystemBars()
} }
private fun setUpSubFragments() { private fun setUpSubFragments() {

View file

@ -24,6 +24,7 @@ import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper
import code.name.monkey.retromusic.NEW_BLUR_AMOUNT import code.name.monkey.retromusic.NEW_BLUR_AMOUNT
import code.name.monkey.retromusic.R import code.name.monkey.retromusic.R
import code.name.monkey.retromusic.databinding.FragmentBlurBinding 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.base.AbsPlayerFragment
import code.name.monkey.retromusic.fragments.player.PlayerAlbumCoverFragment import code.name.monkey.retromusic.fragments.player.PlayerAlbumCoverFragment
import code.name.monkey.retromusic.glide.BlurTransformation 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.glide.RetroMusicColoredTarget
import code.name.monkey.retromusic.helper.MusicPlayerRemote import code.name.monkey.retromusic.helper.MusicPlayerRemote
import code.name.monkey.retromusic.model.Song import code.name.monkey.retromusic.model.Song
import code.name.monkey.retromusic.util.PreferenceUtil.blurAmount
import code.name.monkey.retromusic.util.color.MediaNotificationProcessor import code.name.monkey.retromusic.util.color.MediaNotificationProcessor
class BlurPlayerFragment : AbsPlayerFragment(R.layout.fragment_blur), class BlurPlayerFragment : AbsPlayerFragment(R.layout.fragment_blur),
@ -54,6 +56,7 @@ class BlurPlayerFragment : AbsPlayerFragment(R.layout.fragment_blur),
_binding = FragmentBlurBinding.bind(view) _binding = FragmentBlurBinding.bind(view)
setUpSubFragments() setUpSubFragments()
setUpPlayerToolbar() setUpPlayerToolbar()
binding.playerToolbar.drawAboveSystemBars()
} }
private fun setUpSubFragments() { private fun setUpSubFragments() {
@ -108,8 +111,6 @@ class BlurPlayerFragment : AbsPlayerFragment(R.layout.fragment_blur),
get() = lastColor get() = lastColor
private fun updateBlur() { private fun updateBlur() {
val blurAmount = PreferenceManager.getDefaultSharedPreferences(requireContext())
.getInt(NEW_BLUR_AMOUNT, 25)
binding.colorBackground.clearColorFilter() binding.colorBackground.clearColorFilter()
GlideApp.with(requireActivity()).asBitmapPalette() GlideApp.with(requireActivity()).asBitmapPalette()
.songCoverOptions(MusicPlayerRemote.currentSong) .songCoverOptions(MusicPlayerRemote.currentSong)

View file

@ -21,6 +21,7 @@ import androidx.appcompat.widget.Toolbar
import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper
import code.name.monkey.retromusic.R import code.name.monkey.retromusic.R
import code.name.monkey.retromusic.databinding.FragmentCardPlayerBinding 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.base.AbsPlayerFragment
import code.name.monkey.retromusic.fragments.player.PlayerAlbumCoverFragment import code.name.monkey.retromusic.fragments.player.PlayerAlbumCoverFragment
import code.name.monkey.retromusic.fragments.player.normal.PlayerFragment 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) _binding = FragmentCardPlayerBinding.bind(view)
setUpSubFragments() setUpSubFragments()
setUpPlayerToolbar() setUpPlayerToolbar()
(binding.playbackControlsFragment.parent as View).drawAboveSystemBars()
} }
private fun setUpSubFragments() { private fun setUpSubFragments() {

View file

@ -24,6 +24,7 @@ import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper
import code.name.monkey.retromusic.NEW_BLUR_AMOUNT import code.name.monkey.retromusic.NEW_BLUR_AMOUNT
import code.name.monkey.retromusic.R import code.name.monkey.retromusic.R
import code.name.monkey.retromusic.databinding.FragmentCardBlurPlayerBinding 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.base.AbsPlayerFragment
import code.name.monkey.retromusic.fragments.player.PlayerAlbumCoverFragment import code.name.monkey.retromusic.fragments.player.PlayerAlbumCoverFragment
import code.name.monkey.retromusic.fragments.player.normal.PlayerFragment 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.glide.RetroMusicColoredTarget
import code.name.monkey.retromusic.helper.MusicPlayerRemote import code.name.monkey.retromusic.helper.MusicPlayerRemote
import code.name.monkey.retromusic.model.Song import code.name.monkey.retromusic.model.Song
import code.name.monkey.retromusic.util.PreferenceUtil.blurAmount
import code.name.monkey.retromusic.util.color.MediaNotificationProcessor import code.name.monkey.retromusic.util.color.MediaNotificationProcessor
class CardBlurFragment : AbsPlayerFragment(R.layout.fragment_card_blur_player), 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) _binding = FragmentCardBlurPlayerBinding.bind(view)
setUpSubFragments() setUpSubFragments()
setUpPlayerToolbar() setUpPlayerToolbar()
binding.cardContainer?.drawAboveSystemBars()
} }
private fun setUpSubFragments() { private fun setUpSubFragments() {
@ -133,8 +136,6 @@ class CardBlurFragment : AbsPlayerFragment(R.layout.fragment_card_blur_player),
} }
private fun updateBlur() { private fun updateBlur() {
val blurAmount = PreferenceManager.getDefaultSharedPreferences(requireContext())
.getInt(NEW_BLUR_AMOUNT, 25)
binding.colorBackground.clearColorFilter() binding.colorBackground.clearColorFilter()
GlideApp.with(requireActivity()).asBitmapPalette() GlideApp.with(requireActivity()).asBitmapPalette()
.songCoverOptions(MusicPlayerRemote.currentSong) .songCoverOptions(MusicPlayerRemote.currentSong)

View file

@ -33,10 +33,7 @@ import code.name.monkey.appthemehelper.util.TintHelper
import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper
import code.name.monkey.retromusic.R import code.name.monkey.retromusic.R
import code.name.monkey.retromusic.databinding.FragmentCirclePlayerBinding import code.name.monkey.retromusic.databinding.FragmentCirclePlayerBinding
import code.name.monkey.retromusic.extensions.accentColor import code.name.monkey.retromusic.extensions.*
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.fragments.base.AbsPlayerControlsFragment import code.name.monkey.retromusic.fragments.base.AbsPlayerControlsFragment
import code.name.monkey.retromusic.fragments.base.AbsPlayerFragment import code.name.monkey.retromusic.fragments.base.AbsPlayerFragment
import code.name.monkey.retromusic.fragments.base.goToAlbum import code.name.monkey.retromusic.fragments.base.goToAlbum
@ -96,6 +93,7 @@ class CirclePlayerFragment : AbsPlayerFragment(R.layout.fragment_circle_player),
binding.text.setOnClickListener { binding.text.setOnClickListener {
goToArtist(requireActivity()) goToArtist(requireActivity())
} }
binding.songInfo.drawAboveSystemBars()
} }
private fun setUpPlayerToolbar() { private fun setUpPlayerToolbar() {

View file

@ -38,11 +38,11 @@ import code.name.monkey.retromusic.adapter.song.PlayingQueueAdapter
import code.name.monkey.retromusic.databinding.FragmentClassicPlayerBinding import code.name.monkey.retromusic.databinding.FragmentClassicPlayerBinding
import code.name.monkey.retromusic.extensions.hide import code.name.monkey.retromusic.extensions.hide
import code.name.monkey.retromusic.extensions.show import code.name.monkey.retromusic.extensions.show
import code.name.monkey.retromusic.fragments.VolumeFragment
import code.name.monkey.retromusic.fragments.base.AbsPlayerControlsFragment import code.name.monkey.retromusic.fragments.base.AbsPlayerControlsFragment
import code.name.monkey.retromusic.fragments.base.AbsPlayerFragment import code.name.monkey.retromusic.fragments.base.AbsPlayerFragment
import code.name.monkey.retromusic.fragments.base.goToAlbum import code.name.monkey.retromusic.fragments.base.goToAlbum
import code.name.monkey.retromusic.fragments.base.goToArtist 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.fragments.player.PlayerAlbumCoverFragment
import code.name.monkey.retromusic.helper.MusicPlayerRemote import code.name.monkey.retromusic.helper.MusicPlayerRemote
import code.name.monkey.retromusic.helper.MusicProgressViewUpdateHelper import code.name.monkey.retromusic.helper.MusicProgressViewUpdateHelper

View file

@ -16,15 +16,14 @@ package code.name.monkey.retromusic.fragments.player.color
import android.animation.ValueAnimator import android.animation.ValueAnimator
import android.os.Bundle import android.os.Bundle
import android.os.Handler
import android.view.View import android.view.View
import androidx.appcompat.widget.Toolbar import androidx.appcompat.widget.Toolbar
import androidx.core.animation.doOnEnd import androidx.core.animation.doOnEnd
import code.name.monkey.appthemehelper.util.ATHUtil import code.name.monkey.appthemehelper.util.ATHUtil
import code.name.monkey.appthemehelper.util.ColorUtil
import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper
import code.name.monkey.retromusic.R import code.name.monkey.retromusic.R
import code.name.monkey.retromusic.databinding.FragmentColorPlayerBinding 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.base.AbsPlayerFragment
import code.name.monkey.retromusic.fragments.player.PlayerAlbumCoverFragment import code.name.monkey.retromusic.fragments.player.PlayerAlbumCoverFragment
import code.name.monkey.retromusic.helper.MusicPlayerRemote import code.name.monkey.retromusic.helper.MusicPlayerRemote
@ -61,14 +60,11 @@ class ColorFragment : AbsPlayerFragment(R.layout.fragment_color_player) {
_binding?.root?.setBackgroundColor(color.backgroundColor) _binding?.root?.setBackgroundColor(color.backgroundColor)
} }
animator.start() animator.start()
serviceActivity?.setLightNavigationBar(ColorUtil.isColorLight(color.backgroundColor)) ToolbarContentTintHelper.colorizeToolbar(
Handler().post { binding.playerToolbar,
ToolbarContentTintHelper.colorizeToolbar( color.secondaryTextColor,
binding.playerToolbar, requireActivity()
color.secondaryTextColor, )
requireActivity()
)
}
} }
override fun onFavoriteToggled() { override fun onFavoriteToggled() {
@ -116,6 +112,7 @@ class ColorFragment : AbsPlayerFragment(R.layout.fragment_color_player) {
val playerAlbumCoverFragment = val playerAlbumCoverFragment =
childFragmentManager.findFragmentById(R.id.playerAlbumCoverFragment) as PlayerAlbumCoverFragment childFragmentManager.findFragmentById(R.id.playerAlbumCoverFragment) as PlayerAlbumCoverFragment
playerAlbumCoverFragment.setCallbacks(this) playerAlbumCoverFragment.setCallbacks(this)
playerToolbar().drawAboveSystemBars()
} }
private fun setUpSubFragments() { private fun setUpSubFragments() {

View file

@ -21,6 +21,7 @@ import code.name.monkey.appthemehelper.util.ATHUtil
import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper
import code.name.monkey.retromusic.R import code.name.monkey.retromusic.R
import code.name.monkey.retromusic.databinding.FragmentFitBinding 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.base.AbsPlayerFragment
import code.name.monkey.retromusic.fragments.player.PlayerAlbumCoverFragment import code.name.monkey.retromusic.fragments.player.PlayerAlbumCoverFragment
import code.name.monkey.retromusic.helper.MusicPlayerRemote import code.name.monkey.retromusic.helper.MusicPlayerRemote
@ -85,6 +86,7 @@ class FitFragment : AbsPlayerFragment(R.layout.fragment_fit) {
_binding = FragmentFitBinding.bind(view) _binding = FragmentFitBinding.bind(view)
setUpSubFragments() setUpSubFragments()
setUpPlayerToolbar() setUpPlayerToolbar()
playerToolbar().drawAboveSystemBars()
} }
private fun setUpSubFragments() { private fun setUpSubFragments() {

View file

@ -26,6 +26,7 @@ import code.name.monkey.appthemehelper.util.MaterialValueHelper
import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper
import code.name.monkey.retromusic.R import code.name.monkey.retromusic.R
import code.name.monkey.retromusic.databinding.FragmentFlatPlayerBinding 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.base.AbsPlayerFragment
import code.name.monkey.retromusic.fragments.player.PlayerAlbumCoverFragment import code.name.monkey.retromusic.fragments.player.PlayerAlbumCoverFragment
import code.name.monkey.retromusic.helper.MusicPlayerRemote import code.name.monkey.retromusic.helper.MusicPlayerRemote
@ -90,6 +91,7 @@ class FlatPlayerFragment : AbsPlayerFragment(R.layout.fragment_flat_player) {
_binding = FragmentFlatPlayerBinding.bind(view) _binding = FragmentFlatPlayerBinding.bind(view)
setUpPlayerToolbar() setUpPlayerToolbar()
setUpSubFragments() setUpSubFragments()
binding.playbackControlsFragment.drawAboveSystemBars()
} }
override fun onShow() { override fun onShow() {

View file

@ -22,6 +22,7 @@ import androidx.appcompat.widget.Toolbar
import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper
import code.name.monkey.retromusic.R import code.name.monkey.retromusic.R
import code.name.monkey.retromusic.databinding.FragmentFullBinding 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.hide
import code.name.monkey.retromusic.extensions.show import code.name.monkey.retromusic.extensions.show
import code.name.monkey.retromusic.extensions.whichFragment import code.name.monkey.retromusic.extensions.whichFragment
@ -62,6 +63,7 @@ class FullPlayerFragment : AbsPlayerFragment(R.layout.fragment_full) {
setUpPlayerToolbar() setUpPlayerToolbar()
setupArtist() setupArtist()
binding.nextSong.isSelected = true binding.nextSong.isSelected = true
binding.playbackControlsFragment.drawAboveSystemBars()
} }
private fun setupArtist() { private fun setupArtist() {
@ -131,12 +133,15 @@ class FullPlayerFragment : AbsPlayerFragment(R.layout.fragment_full) {
private fun updateArtistImage() { private fun updateArtistImage() {
libraryViewModel.artist(MusicPlayerRemote.currentSong.artistId) libraryViewModel.artist(MusicPlayerRemote.currentSong.artistId)
.observe(viewLifecycleOwner, { artist -> .observe(viewLifecycleOwner, { artist ->
GlideApp.with(requireActivity()).asBitmapPalette().artistImageOptions(artist) if (artist.id != -1L) {
.load(RetroGlideExtension.getArtistModel(artist)) GlideApp.with(requireActivity()).asBitmapPalette().artistImageOptions(artist)
.into(object : RetroMusicColoredTarget(binding.artistImage) { .load(RetroGlideExtension.getArtistModel(artist))
override fun onColorReady(colors: MediaNotificationProcessor) { .into(object : RetroMusicColoredTarget(binding.artistImage) {
} override fun onColorReady(colors: MediaNotificationProcessor) {
}) }
})
}
}) })
} }

View file

@ -29,6 +29,8 @@ import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.widget.Toolbar import androidx.appcompat.widget.Toolbar
import androidx.constraintlayout.widget.ConstraintLayout import androidx.constraintlayout.widget.ConstraintLayout
import androidx.core.view.ViewCompat import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
import androidx.core.view.updatePadding
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView 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.PlaylistEntity
import code.name.monkey.retromusic.db.SongEntity import code.name.monkey.retromusic.db.SongEntity
import code.name.monkey.retromusic.db.toSongEntity import code.name.monkey.retromusic.db.toSongEntity
import code.name.monkey.retromusic.extensions.hide import code.name.monkey.retromusic.extensions.*
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.fragments.base.AbsPlayerControlsFragment import code.name.monkey.retromusic.fragments.base.AbsPlayerControlsFragment
import code.name.monkey.retromusic.fragments.base.AbsPlayerFragment import code.name.monkey.retromusic.fragments.base.AbsPlayerFragment
import code.name.monkey.retromusic.fragments.base.goToAlbum import code.name.monkey.retromusic.fragments.base.goToAlbum
import code.name.monkey.retromusic.fragments.base.goToArtist 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.MusicPlayerRemote
import code.name.monkey.retromusic.helper.MusicProgressViewUpdateHelper import code.name.monkey.retromusic.helper.MusicProgressViewUpdateHelper
import code.name.monkey.retromusic.helper.PlayPauseButtonOnClickHandler 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 recyclerViewTouchActionGuardManager: RecyclerViewTouchActionGuardManager? = null
private var playingQueueAdapter: PlayingQueueAdapter? = null private var playingQueueAdapter: PlayingQueueAdapter? = null
private lateinit var linearLayoutManager: LinearLayoutManager private lateinit var linearLayoutManager: LinearLayoutManager
private var bottomInsets = 0
private var _binding: FragmentGradientPlayerBinding? = null private var _binding: FragmentGradientPlayerBinding? = null
private val binding get() = _binding!! private val binding get() = _binding!!
@ -88,11 +89,11 @@ class GradientPlayerFragment : AbsPlayerFragment(R.layout.fragment_gradient_play
private val bottomSheetCallbackList = object : BottomSheetCallback() { private val bottomSheetCallbackList = object : BottomSheetCallback() {
override fun onSlide(bottomSheet: View, slideOffset: Float) { override fun onSlide(bottomSheet: View, slideOffset: Float) {
mainActivity.getBottomSheetBehavior().setAllowDragging(false) mainActivity.getBottomSheetBehavior().setAllowDragging(false)
binding.playerQueueSheet.setPadding( binding.playerQueueSheet.updatePadding(
binding.playerQueueSheet.paddingLeft, top = (slideOffset * binding.statusBarLayout.statusBar.height).toInt()
(slideOffset * binding.statusBarLayout.statusBar.height).toInt(), )
binding.playerQueueSheet.paddingRight, binding.container.updatePadding(
binding.playerQueueSheet.paddingBottom bottom = ((1 - slideOffset) * bottomInsets).toInt()
) )
} }
@ -157,6 +158,14 @@ class GradientPlayerFragment : AbsPlayerFragment(R.layout.fragment_gradient_play
binding.playbackControlsFragment.text.setOnClickListener { binding.playbackControlsFragment.text.setOnClickListener {
goToArtist(requireActivity()) goToArtist(requireActivity())
} }
ViewCompat.setOnApplyWindowInsetsListener(
(binding.container)
) { v: View, insets: WindowInsetsCompat ->
bottomInsets = insets.safeGetBottomInsets()
v.updatePadding(bottom = bottomInsets)
insets
}
binding.playbackControlsFragment.root.drawAboveSystemBars()
} }
@SuppressLint("ClickableViewAccessibility") @SuppressLint("ClickableViewAccessibility")

View file

@ -21,6 +21,7 @@ import code.name.monkey.appthemehelper.util.ATHUtil
import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper
import code.name.monkey.retromusic.R import code.name.monkey.retromusic.R
import code.name.monkey.retromusic.databinding.FragmentMaterialBinding 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.base.AbsPlayerFragment
import code.name.monkey.retromusic.fragments.player.PlayerAlbumCoverFragment import code.name.monkey.retromusic.fragments.player.PlayerAlbumCoverFragment
import code.name.monkey.retromusic.fragments.player.normal.PlayerFragment import code.name.monkey.retromusic.fragments.player.normal.PlayerFragment
@ -93,6 +94,7 @@ class MaterialFragment : AbsPlayerFragment(R.layout.fragment_material) {
_binding = FragmentMaterialBinding.bind(view) _binding = FragmentMaterialBinding.bind(view)
setUpSubFragments() setUpSubFragments()
setUpPlayerToolbar() setUpPlayerToolbar()
playerToolbar().drawAboveSystemBars()
} }
private fun setUpSubFragments() { private fun setUpSubFragments() {

View file

@ -24,6 +24,7 @@ import code.name.monkey.appthemehelper.util.ATHUtil
import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper
import code.name.monkey.retromusic.R import code.name.monkey.retromusic.R
import code.name.monkey.retromusic.databinding.FragmentPlayerBinding 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.base.AbsPlayerFragment
import code.name.monkey.retromusic.fragments.player.PlayerAlbumCoverFragment import code.name.monkey.retromusic.fragments.player.PlayerAlbumCoverFragment
import code.name.monkey.retromusic.helper.MusicPlayerRemote import code.name.monkey.retromusic.helper.MusicPlayerRemote
@ -120,6 +121,7 @@ class PlayerFragment : AbsPlayerFragment(R.layout.fragment_player) {
_binding = FragmentPlayerBinding.bind(view) _binding = FragmentPlayerBinding.bind(view)
setUpSubFragments() setUpSubFragments()
setUpPlayerToolbar() setUpPlayerToolbar()
playerToolbar().drawAboveSystemBars()
} }
override fun onDestroyView() { override fun onDestroyView() {

View file

@ -21,6 +21,7 @@ import code.name.monkey.appthemehelper.util.ATHUtil
import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper
import code.name.monkey.retromusic.R import code.name.monkey.retromusic.R
import code.name.monkey.retromusic.databinding.FragmentPeakPlayerBinding 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.hide
import code.name.monkey.retromusic.extensions.show import code.name.monkey.retromusic.extensions.show
import code.name.monkey.retromusic.fragments.base.AbsPlayerFragment import code.name.monkey.retromusic.fragments.base.AbsPlayerFragment
@ -55,6 +56,7 @@ class PeakPlayerFragment : AbsPlayerFragment(R.layout.fragment_peak_player) {
binding.text.setOnClickListener { binding.text.setOnClickListener {
goToArtist(requireActivity()) goToArtist(requireActivity())
} }
binding.root.drawAboveSystemBars()
} }
private fun setUpSubFragments() { private fun setUpSubFragments() {

View file

@ -21,6 +21,7 @@ import code.name.monkey.appthemehelper.util.ATHUtil
import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper
import code.name.monkey.retromusic.R import code.name.monkey.retromusic.R
import code.name.monkey.retromusic.databinding.FragmentPlainPlayerBinding 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.AbsPlayerFragment
import code.name.monkey.retromusic.fragments.base.goToAlbum import code.name.monkey.retromusic.fragments.base.goToAlbum
import code.name.monkey.retromusic.fragments.base.goToArtist import code.name.monkey.retromusic.fragments.base.goToArtist
@ -84,6 +85,7 @@ class PlainPlayerFragment : AbsPlayerFragment(R.layout.fragment_plain_player) {
binding.text.setOnClickListener { binding.text.setOnClickListener {
goToArtist(requireActivity()) goToArtist(requireActivity())
} }
playerToolbar().drawAboveSystemBars()
} }
private fun setUpSubFragments() { private fun setUpSubFragments() {

View file

@ -21,6 +21,7 @@ import code.name.monkey.appthemehelper.util.ATHUtil
import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper
import code.name.monkey.retromusic.R import code.name.monkey.retromusic.R
import code.name.monkey.retromusic.databinding.FragmentSimplePlayerBinding 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.base.AbsPlayerFragment
import code.name.monkey.retromusic.fragments.player.PlayerAlbumCoverFragment import code.name.monkey.retromusic.fragments.player.PlayerAlbumCoverFragment
import code.name.monkey.retromusic.helper.MusicPlayerRemote import code.name.monkey.retromusic.helper.MusicPlayerRemote
@ -51,6 +52,7 @@ class SimplePlayerFragment : AbsPlayerFragment(R.layout.fragment_simple_player)
_binding = FragmentSimplePlayerBinding.bind(view) _binding = FragmentSimplePlayerBinding.bind(view)
setUpSubFragments() setUpSubFragments()
setUpPlayerToolbar() setUpPlayerToolbar()
playerToolbar().drawAboveSystemBars()
} }
private fun setUpSubFragments() { private fun setUpSubFragments() {

View file

@ -27,6 +27,7 @@ import androidx.appcompat.widget.Toolbar
import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper
import code.name.monkey.retromusic.R import code.name.monkey.retromusic.R
import code.name.monkey.retromusic.databinding.FragmentTinyPlayerBinding 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.hide
import code.name.monkey.retromusic.extensions.show import code.name.monkey.retromusic.extensions.show
import code.name.monkey.retromusic.fragments.base.AbsPlayerFragment import code.name.monkey.retromusic.fragments.base.AbsPlayerFragment
@ -147,6 +148,7 @@ class TinyPlayerFragment : AbsPlayerFragment(R.layout.fragment_tiny_player),
binding.text.setOnClickListener { binding.text.setOnClickListener {
goToArtist(requireActivity()) goToArtist(requireActivity())
} }
playerToolbar().drawAboveSystemBars()
} }
private fun setUpSubFragments() { private fun setUpSubFragments() {

View file

@ -22,10 +22,15 @@ import code.name.monkey.retromusic.extensions.dipToPix
import code.name.monkey.retromusic.extensions.surfaceColor import code.name.monkey.retromusic.extensions.surfaceColor
import code.name.monkey.retromusic.fragments.base.AbsMainActivityFragment import code.name.monkey.retromusic.fragments.base.AbsMainActivityFragment
import code.name.monkey.retromusic.helper.menu.PlaylistMenuHelper 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.interfaces.ICabHolder
import code.name.monkey.retromusic.model.Song import code.name.monkey.retromusic.model.Song
import code.name.monkey.retromusic.util.RetroColorUtil 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.google.android.material.transition.MaterialSharedAxis
import com.h6ah4i.android.widget.advrecyclerview.animator.DraggableItemAnimator import com.h6ah4i.android.widget.advrecyclerview.animator.DraggableItemAnimator
import com.h6ah4i.android.widget.advrecyclerview.animator.GeneralItemAnimator import com.h6ah4i.android.widget.advrecyclerview.animator.GeneralItemAnimator
@ -71,6 +76,8 @@ class PlaylistDetailsFragment : AbsMainActivityFragment(R.layout.fragment_playli
requireActivity().onBackPressed() requireActivity().onBackPressed()
} }
} }
binding.appBarLayout.statusBarForeground =
MaterialShapeDrawable.createWithElevationOverlay(requireContext())
} }
private fun setUpRecyclerView() { private fun setUpRecyclerView() {
@ -78,7 +85,7 @@ class PlaylistDetailsFragment : AbsMainActivityFragment(R.layout.fragment_playli
playlist.playlistEntity, playlist.playlistEntity,
requireActivity(), requireActivity(),
ArrayList(), ArrayList(),
R.layout.item_list, R.layout.item_queue,
this this
) )
@ -158,29 +165,35 @@ class PlaylistDetailsFragment : AbsMainActivityFragment(R.layout.fragment_playli
private fun handleBackPress(): Boolean { private fun handleBackPress(): Boolean {
cab?.let { cab?.let {
if (it.isActive) { if (it.isActive()) {
it.finish() it.destroy()
return true return true
} }
} }
return false 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 { cab?.let {
println("Cab") println("Cab")
if (it.isActive) { if (it.isActive()) {
it.finish() it.destroy()
} }
} }
cab = MaterialCab(mainActivity, R.id.cab_stub) cab = createCab(R.id.toolbar_container) {
.setMenu(menuRes) menu(menuRes)
.setCloseDrawableRes(R.drawable.ic_close) closeDrawable(R.drawable.ic_close)
.setBackgroundColor(RetroColorUtil.shiftBackgroundColorForLightText(surfaceColor())) backgroundColor(literal = RetroColorUtil.shiftBackgroundColor(surfaceColor()))
.start(callback) slideDown()
return cab as MaterialCab onCreate { cab, menu -> callback.onCabCreated(cab, menu) }
onSelection {
callback.onCabItemClicked(it)
}
onDestroy { callback.onCabFinished(it) }
}
return cab as AttachedCab
} }
} }

View file

@ -25,21 +25,18 @@ import code.name.monkey.retromusic.EXTRA_PLAYLIST
import code.name.monkey.retromusic.R import code.name.monkey.retromusic.R
import code.name.monkey.retromusic.adapter.playlist.PlaylistAdapter import code.name.monkey.retromusic.adapter.playlist.PlaylistAdapter
import code.name.monkey.retromusic.db.PlaylistWithSongs 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.ReloadType
import code.name.monkey.retromusic.fragments.base.AbsRecyclerViewCustomGridSizeFragment import code.name.monkey.retromusic.fragments.base.AbsRecyclerViewCustomGridSizeFragment
import code.name.monkey.retromusic.helper.SortOrder.PlaylistSortOrder 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.interfaces.IPlaylistClickListener
import code.name.monkey.retromusic.util.PreferenceUtil 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.gms.cast.framework.CastButtonFactory
import com.google.android.material.transition.MaterialSharedAxis import com.google.android.material.transition.MaterialSharedAxis
class PlaylistsFragment : class PlaylistsFragment :
AbsRecyclerViewCustomGridSizeFragment<PlaylistAdapter, GridLayoutManager>(), AbsRecyclerViewCustomGridSizeFragment<PlaylistAdapter, GridLayoutManager>(),
IPlaylistClickListener, ICabHolder { IPlaylistClickListener {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
@ -50,18 +47,20 @@ class PlaylistsFragment :
adapter?.swapDataSet(listOf()) adapter?.swapDataSet(listOf())
}) })
requireActivity().onBackPressedDispatcher.addCallback(viewLifecycleOwner) { requireActivity().onBackPressedDispatcher.addCallback(viewLifecycleOwner) {
if (!handleBackPress()) { remove()
remove() mainActivity.finish()
requireActivity().onBackPressed()
}
} }
} }
override val titleRes: Int override val titleRes: Int
get() = R.string.playlists get() = R.string.playlists
override val emptyMessage: Int override val emptyMessage: Int
get() = R.string.no_playlists get() = R.string.no_playlists
override val isShuffleVisible: Boolean
get() = false
override fun createLayoutManager(): GridLayoutManager { override fun createLayoutManager(): GridLayoutManager {
return GridLayoutManager(requireContext(), getGridSize()) return GridLayoutManager(requireContext(), getGridSize())
} }
@ -71,7 +70,7 @@ class PlaylistsFragment :
requireActivity(), requireActivity(),
ArrayList(), ArrayList(),
itemLayoutRes(), itemLayoutRes(),
this, null,
this this
) )
} }
@ -181,7 +180,7 @@ class PlaylistsFragment :
} }
override fun loadLayoutRes(): Int { override fun loadLayoutRes(): Int {
return R.layout.item_grid return R.layout.item_card
} }
override fun saveLayoutRes(layoutRes: Int) { override fun saveLayoutRes(layoutRes: Int) {
@ -198,31 +197,4 @@ class PlaylistsFragment :
null 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
}
} }

View file

@ -14,80 +14,121 @@
*/ */
package code.name.monkey.retromusic.fragments.queue package code.name.monkey.retromusic.fragments.queue
import android.content.res.ColorStateList
import android.os.Bundle import android.os.Bundle
import android.view.View 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.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView 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.R
import code.name.monkey.retromusic.adapter.song.PlayingQueueAdapter 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.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.animator.DraggableItemAnimator
import com.h6ah4i.android.widget.advrecyclerview.draggable.RecyclerViewDragDropManager import com.h6ah4i.android.widget.advrecyclerview.draggable.RecyclerViewDragDropManager
import com.h6ah4i.android.widget.advrecyclerview.swipeable.RecyclerViewSwipeManager import com.h6ah4i.android.widget.advrecyclerview.swipeable.RecyclerViewSwipeManager
import com.h6ah4i.android.widget.advrecyclerview.touchguard.RecyclerViewTouchActionGuardManager import com.h6ah4i.android.widget.advrecyclerview.touchguard.RecyclerViewTouchActionGuardManager
import com.h6ah4i.android.widget.advrecyclerview.utils.WrapperAdapterUtils import com.h6ah4i.android.widget.advrecyclerview.utils.WrapperAdapterUtils
/** class PlayingQueueFragment : AbsMusicServiceFragment(R.layout.fragment_playing_queue) {
* Created by hemanths on 2019-12-08.
*/
class PlayingQueueFragment : AbsRecyclerViewFragment<PlayingQueueAdapter, LinearLayoutManager>() {
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 recyclerViewDragDropManager: RecyclerViewDragDropManager? = null
private var recyclerViewSwipeManager: RecyclerViewSwipeManager? = null private var recyclerViewSwipeManager: RecyclerViewSwipeManager? = null
private var recyclerViewTouchActionGuardManager: RecyclerViewTouchActionGuardManager? = null private var recyclerViewTouchActionGuardManager: RecyclerViewTouchActionGuardManager? = null
override val titleRes: Int private var playingQueueAdapter: PlayingQueueAdapter? = null
get() = R.string.now_playing_queue 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?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) 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() recyclerViewTouchActionGuardManager = RecyclerViewTouchActionGuardManager()
recyclerViewDragDropManager = RecyclerViewDragDropManager() recyclerViewDragDropManager = RecyclerViewDragDropManager()
recyclerViewSwipeManager = RecyclerViewSwipeManager() recyclerViewSwipeManager = RecyclerViewSwipeManager()
val animator = DraggableItemAnimator() val animator = DraggableItemAnimator()
animator.supportsChangeAnimations = false 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) playingQueueAdapter = PlayingQueueAdapter(
} requireActivity(),
override fun createLayoutManager(): LinearLayoutManager {
return LinearLayoutManager(requireContext())
}
override fun createAdapter(): PlayingQueueAdapter {
return PlayingQueueAdapter(
requireActivity() as AppCompatActivity,
MusicPlayerRemote.playingQueue.toMutableList(), MusicPlayerRemote.playingQueue.toMutableList(),
MusicPlayerRemote.position, MusicPlayerRemote.position,
R.layout.item_queue 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() { private fun checkForPadding() {
super.onServiceConnected()
updateQueue()
} }
override fun onQueueChanged() { override fun onQueueChanged() {
super.onQueueChanged() if (MusicPlayerRemote.playingQueue.isEmpty()) {
findNavController().navigateUp()
return
}
checkForPadding()
updateQueue() updateQueue()
updateCurrentSong()
}
override fun onMediaStoreChanged() {
updateQueue()
updateCurrentSong()
}
private fun updateCurrentSong() {
binding.toolbar.subtitle = getUpNextAndQueueTime()
} }
override fun onPlayingMetaChanged() { override fun onPlayingMetaChanged() {
@ -95,50 +136,62 @@ class PlayingQueueFragment : AbsRecyclerViewFragment<PlayingQueueAdapter, Linear
} }
private fun updateQueuePosition() { private fun updateQueuePosition() {
adapter?.setCurrent(MusicPlayerRemote.position) playingQueueAdapter?.setCurrent(MusicPlayerRemote.position)
resetToCurrentPosition() resetToCurrentPosition()
binding.toolbar.subtitle = getUpNextAndQueueTime()
} }
private fun updateQueue() { private fun updateQueue() {
adapter?.swapDataSet(MusicPlayerRemote.playingQueue, MusicPlayerRemote.position) playingQueueAdapter?.swapDataSet(MusicPlayerRemote.playingQueue, MusicPlayerRemote.position)
resetToCurrentPosition()
} }
private fun resetToCurrentPosition() { private fun resetToCurrentPosition() {
recyclerView().stopScroll() binding.recyclerView.stopScroll()
layoutManager?.scrollToPositionWithOffset(MusicPlayerRemote.position + 1, 0) linearLayoutManager.scrollToPositionWithOffset(MusicPlayerRemote.position + 1, 0)
} }
override fun onPause() { override fun onPause() {
recyclerViewDragDropManager?.cancelDrag() if (recyclerViewDragDropManager != null) {
recyclerViewDragDropManager!!.cancelDrag()
}
super.onPause() super.onPause()
} }
override val emptyMessage: Int override fun onDestroy() {
get() = R.string.no_playing_queue
override fun onDestroyView() {
super.onDestroyView()
if (recyclerViewDragDropManager != null) { if (recyclerViewDragDropManager != null) {
recyclerViewDragDropManager?.release() recyclerViewDragDropManager!!.release()
recyclerViewDragDropManager = null recyclerViewDragDropManager = null
} }
if (recyclerViewSwipeManager != null) { if (recyclerViewSwipeManager != null) {
recyclerViewSwipeManager?.release() recyclerViewSwipeManager?.release()
recyclerViewSwipeManager = null recyclerViewSwipeManager = null
} }
if (wrappedAdapter != null) {
WrapperAdapterUtils.releaseAll(wrappedAdapter) WrapperAdapterUtils.releaseAll(wrappedAdapter)
wrappedAdapter = null
}
playingQueueAdapter = null
super.onDestroy()
} }
companion object { private fun setupToolbar() {
@JvmField binding.toolbar.subtitle = getUpNextAndQueueTime()
val TAG: String = PlayingQueueFragment::class.java.simpleName binding.clearQueue.backgroundTintList = ColorStateList.valueOf(accentColor())
ColorStateList.valueOf(
@JvmStatic MaterialValueHelper.getPrimaryTextColor(
fun newInstance(): PlayingQueueFragment { requireContext(),
return PlayingQueueFragment() ColorUtil.isColorLight(accentColor())
)
).apply {
binding.clearQueue.setTextColor(this)
binding.clearQueue.iconTint = this
}
binding.toolbar.apply {
setNavigationOnClickListener {
findNavController().navigateUp()
}
setNavigationIcon(R.drawable.ic_keyboard_backspace_black)
} }
} }
} }

Some files were not shown because too many files have changed in this diff Show more