commit
c7d158f860
|
@ -6,7 +6,6 @@ apply plugin: 'kotlin-parcelize'
|
|||
|
||||
android {
|
||||
compileSdkVersion 31
|
||||
buildToolsVersion = '29.0.3'
|
||||
|
||||
defaultConfig {
|
||||
minSdkVersion 21
|
||||
|
@ -16,7 +15,7 @@ android {
|
|||
vectorDrawables.useSupportLibrary = true
|
||||
|
||||
applicationId "code.name.monkey.retromusic"
|
||||
versionCode 10520
|
||||
versionCode 10519
|
||||
versionName '5.0.0' + "_" + getDate()
|
||||
|
||||
buildConfigField("String", "GOOGLE_PLAY_LICENSING_KEY", "\"${getProperty(getProperties('../public.properties'), 'GOOGLE_PLAY_LICENSE_KEY')}\"")
|
||||
|
@ -92,21 +91,21 @@ dependencies {
|
|||
implementation project(':appthemehelper')
|
||||
implementation "androidx.gridlayout:gridlayout:1.0.0"
|
||||
implementation "androidx.cardview:cardview:1.0.0"
|
||||
implementation 'androidx.appcompat:appcompat:1.3.1'
|
||||
implementation 'androidx.annotation:annotation:1.2.0'
|
||||
implementation 'androidx.constraintlayout:constraintlayout:2.1.0'
|
||||
implementation 'androidx.appcompat:appcompat:1.4.0'
|
||||
implementation 'androidx.annotation:annotation:1.3.0'
|
||||
implementation 'androidx.constraintlayout:constraintlayout:2.1.2'
|
||||
implementation 'androidx.recyclerview:recyclerview:1.2.1'
|
||||
implementation 'androidx.preference:preference-ktx:1.1.1'
|
||||
implementation 'androidx.core:core-ktx:1.6.0'
|
||||
implementation 'androidx.core:core-ktx:1.7.0'
|
||||
implementation 'androidx.palette:palette-ktx:1.0.0'
|
||||
|
||||
//Cast Dependencies
|
||||
implementation 'androidx.mediarouter:mediarouter:1.2.5'
|
||||
implementation 'com.google.android.gms:play-services-cast-framework:20.0.0'
|
||||
implementation 'com.google.android.gms:play-services-cast-framework:20.1.0'
|
||||
//WebServer by NanoHttpd
|
||||
implementation "org.nanohttpd:nanohttpd:2.3.1"
|
||||
|
||||
def nav_version = "2.4.0-alpha07"
|
||||
def nav_version = '2.4.0-beta02'
|
||||
implementation "androidx.navigation:navigation-runtime-ktx:$nav_version"
|
||||
implementation "androidx.navigation:navigation-fragment-ktx:$nav_version"
|
||||
implementation "androidx.navigation:navigation-ui-ktx:$nav_version"
|
||||
|
@ -116,13 +115,13 @@ dependencies {
|
|||
implementation "androidx.room:room-ktx:$room_version"
|
||||
kapt "androidx.room:room-compiler:$room_version"
|
||||
|
||||
def lifecycle_version = "2.3.1"
|
||||
def lifecycle_version = "2.4.0"
|
||||
implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycle_version"
|
||||
implementation "androidx.lifecycle:lifecycle-livedata-ktx:$lifecycle_version"
|
||||
implementation "androidx.lifecycle:lifecycle-common-java8:$lifecycle_version"
|
||||
|
||||
implementation 'com.google.android.play:core-ktx:1.8.1'
|
||||
implementation 'com.google.android.material:material:1.5.0-alpha03'
|
||||
implementation 'com.google.android.material:material:1.5.0-beta01'
|
||||
|
||||
def retrofit_version = '2.9.0'
|
||||
implementation "com.squareup.retrofit2:retrofit:$retrofit_version"
|
||||
|
@ -134,18 +133,18 @@ dependencies {
|
|||
implementation "com.afollestad.material-dialogs:input:$material_dialog_version"
|
||||
implementation "com.afollestad.material-dialogs:color:$material_dialog_version"
|
||||
implementation "com.afollestad.material-dialogs:bottomsheets:$material_dialog_version"
|
||||
//noinspection GradleDependency
|
||||
implementation 'com.afollestad:material-cab:0.1.12'
|
||||
|
||||
implementation 'com.afollestad:material-cab:2.0.1'
|
||||
|
||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
|
||||
|
||||
def kotlin_coroutines_version = "1.5.2-native-mt"
|
||||
def kotlin_coroutines_version = '1.5.2'
|
||||
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$kotlin_coroutines_version"
|
||||
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$kotlin_coroutines_version"
|
||||
|
||||
def koin_version = "2.1.5"
|
||||
implementation "org.koin:koin-core:$koin_version"
|
||||
implementation "org.koin:koin-androidx-viewmodel:$koin_version"
|
||||
def koin_version = '3.1.3'
|
||||
implementation "io.insert-koin:koin-core:$koin_version"
|
||||
implementation "io.insert-koin:koin-android:$koin_version"
|
||||
|
||||
implementation 'com.github.bumptech.glide:glide:4.12.0'
|
||||
kapt 'com.github.bumptech.glide:compiler:4.12.0'
|
||||
|
@ -153,14 +152,15 @@ dependencies {
|
|||
|
||||
implementation 'com.h6ah4i.android.widget.advrecyclerview:advrecyclerview:1.0.0'
|
||||
|
||||
implementation 'org.eclipse.mylyn.github:org.eclipse.egit.github.core:3.4.0.201406110918-r'
|
||||
implementation 'org.bitbucket.ijabz:jaudiotagger:2.2.5'
|
||||
implementation 'com.anjlab.android.iab.v3:library:1.1.0'
|
||||
implementation 'org.eclipse.mylyn.github:org.eclipse.egit.github.core:2.1.5'
|
||||
implementation 'com.github.AdrienPoupa:jaudiotagger:2.2.3'
|
||||
implementation 'com.anjlab.android.iab.v3:library:2.0.1'
|
||||
implementation 'com.r0adkll:slidableactivity:2.1.0'
|
||||
implementation 'com.heinrichreimersoftware:material-intro:2.0.0'
|
||||
implementation 'com.github.dhaval2404:imagepicker:1.7.1'
|
||||
implementation 'me.zhanghai.android.fastscroll:library:1.1.7'
|
||||
debugImplementation 'com.amitshekhar.android:debug-db:1.0.6'
|
||||
implementation 'cat.ereza:customactivityoncrash:2.3.0'
|
||||
debugImplementation 'com.github.amitshekhariitbhu:Android-Debug-Database:1.0.6'
|
||||
}
|
||||
|
||||
apply from: '../spotless.gradle'
|
||||
|
|
|
@ -16,16 +16,13 @@
|
|||
# public *;
|
||||
#}
|
||||
|
||||
# Uncomment this to preserve the line number information for
|
||||
# Preserve the line number information for
|
||||
# debugging stack traces.
|
||||
#-keepattributes SourceFile,LineNumberTable
|
||||
-keepattributes SourceFile,LineNumberTable
|
||||
|
||||
# If you keep the line number information, uncomment this to
|
||||
# hide the original source file name.
|
||||
#-renamesourcefileattribute SourceFile
|
||||
-keepnames class **
|
||||
-keepnames class ** { *; }
|
||||
-keepattributes SourceFile,LineNumberTable
|
||||
|
||||
-dontwarn java.lang.invoke.*
|
||||
-dontwarn **$$Lambda$*
|
||||
|
@ -58,11 +55,9 @@
|
|||
-keepclassmembers enum * { *; }
|
||||
-keepattributes *Annotation*, Signature, Exception
|
||||
-keepnames class androidx.navigation.fragment.NavHostFragment
|
||||
-keepnames class code.name.monkey.retromusic.model.Home
|
||||
-keep class * extends androidx.fragment.app.Fragment{}
|
||||
-keepnames class * extends android.os.Parcelable
|
||||
-keepnames class * extends java.io.Serializable
|
||||
-keep class code.name.monkey.retromusic.network.model.** { *; }
|
||||
-keep class code.name.monkey.retromusic.model.CategoryInfo { *; }
|
||||
-keep class com.google.android.material.bottomsheet.** { *; }
|
||||
-keep class code.name.monkey.retromusic.Constants { *; }
|
||||
-keep class com.google.android.material.bottomsheet.** { *; }
|
|
@ -0,0 +1,4 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<bool name="md3_available">true</bool>
|
||||
</resources>
|
|
@ -96,7 +96,7 @@
|
|||
|
||||
<style name="circleImageView" parent="">
|
||||
<item name="cornerFamily">rounded</item>
|
||||
<item name="cornerSize">16dp</item>
|
||||
<item name="cornerSize">40dp</item>
|
||||
</style>
|
||||
|
||||
<style name="BottomSheetItemTextAppearance" parent="Widget.MaterialComponents.BottomNavigationView.Colored">
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
<uses-permission android:name="android.permission.VIBRATE" />
|
||||
<uses-permission android:name="android.permission.WAKE_LOCK" />
|
||||
<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.ACCESS_NETWORK_STATE" />
|
||||
<uses-permission android:name="android.permission.BROADCAST_STICKY" />
|
||||
|
@ -107,12 +107,9 @@
|
|||
<data android:mimeType="vnd.android.cursor.dir/audio" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<activity android:name=".activities.PlayingQueueActivity" />
|
||||
<activity android:name=".activities.SettingsActivity" />
|
||||
<activity android:name=".activities.tageditor.AlbumTagEditorActivity" />
|
||||
<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.LicenseActivity" />
|
||||
<activity android:name=".activities.PurchaseActivity" />
|
||||
|
@ -122,6 +119,19 @@
|
|||
<activity android:name=".activities.DriveModeActivity" />
|
||||
<activity android:name=".activities.PermissionActivity" />
|
||||
<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
|
||||
android:name=".appshortcuts.AppShortcutLauncherActivity"
|
||||
|
@ -136,14 +146,14 @@
|
|||
android:name=".cast.ExpandedControlsActivity"
|
||||
android:label="@string/app_name"
|
||||
android:launchMode="singleTask"
|
||||
android:theme="@style/Theme.AppCompat.NoActionBar"
|
||||
android:screenOrientation="portrait">
|
||||
android:screenOrientation="portrait"
|
||||
android:theme="@style/Theme.AppCompat.NoActionBar">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN"/>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
</intent-filter>
|
||||
<meta-data
|
||||
android:name="android.support.PARENT_ACTIVITY"
|
||||
android:value=".activities.MainActivity"/>
|
||||
android:value=".activities.MainActivity" />
|
||||
</activity>
|
||||
|
||||
<provider
|
||||
|
@ -156,7 +166,6 @@
|
|||
android:resource="@xml/provider_paths" />
|
||||
</provider>
|
||||
|
||||
|
||||
<provider
|
||||
android:name="androidx.core.content.FileProvider"
|
||||
android:authorities="${applicationId}"
|
||||
|
@ -266,16 +275,16 @@
|
|||
android:value="true" />
|
||||
|
||||
<!-- Android Auto -->
|
||||
<!-- <meta-data
|
||||
<meta-data
|
||||
android:name="com.google.android.gms.car.application"
|
||||
android:resource="@xml/automotive_app_desc"/>
|
||||
android:resource="@xml/automotive_app_desc" />
|
||||
<meta-data
|
||||
android:name="com.google.android.gms.car.application.theme"
|
||||
android:resource="@style/CarTheme" />
|
||||
<meta-data
|
||||
android:name="com.google.android.gms.car.notification.SmallIcon"
|
||||
android:resource="@drawable/ic_notification"/>
|
||||
-->
|
||||
android:resource="@drawable/ic_notification" />
|
||||
|
||||
<!-- ChromeCast -->
|
||||
<meta-data
|
||||
android:name="com.google.android.gms.cast.framework.OPTIONS_PROVIDER_CLASS_NAME"
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
word-wrap: break-word;
|
||||
}
|
||||
|
||||
div{
|
||||
div {
|
||||
margin: 20px 10px;
|
||||
padding: 10px;
|
||||
border-radius: 10px;
|
||||
|
@ -16,6 +16,8 @@
|
|||
h2 {
|
||||
margin-block-end: 0rem;
|
||||
margin-block-start: 0rem;
|
||||
display: inline-block;
|
||||
vertical-align: center;
|
||||
}
|
||||
|
||||
li {
|
||||
|
@ -35,7 +37,6 @@
|
|||
|
||||
span {
|
||||
font-size: 0.7rem;
|
||||
line-height: 0.7rem;
|
||||
}
|
||||
|
||||
h5 {
|
||||
|
@ -47,58 +48,124 @@
|
|||
margin: 10px 0px;
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.tag {
|
||||
border-radius: 5px;
|
||||
margin-left: 10px;
|
||||
padding: 5px;
|
||||
display: inline-block;
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
{style-placeholder}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<h2>
|
||||
<b>Clear the app if it crashes after updating</b>
|
||||
</h2>
|
||||
<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>
|
||||
<h2>v5.0.0</h2>
|
||||
<h3>What's New</h3>
|
||||
<ul>
|
||||
<li>Added Chromecast support</li>
|
||||
<li>Added animated icons</li>
|
||||
<li>Added cross-fade (experimental)</li>
|
||||
<li>Added ability to remember the last tab</li>
|
||||
<li>Added whitelisting songs</li>
|
||||
<li>Added support for embedded synced lyrics</li>
|
||||
<li>Added lyrics editor for normal and synced lyrics</li>
|
||||
<li>Added playlist ordering</li>
|
||||
<li>Added search filters</li>
|
||||
<li>Added audio fade</li>
|
||||
<li>Added Multi-select in album and artist details</li>
|
||||
<li>Added SD card from folders tab</li>
|
||||
<li>Added Synced lyrics in all themes</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>
|
||||
<li>Added ability to Remember last tab</li>
|
||||
<li>Added Whitelisting songs</li>
|
||||
<li>You can now browse SDCard from Folders Tab</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div>
|
||||
<h5>August 22, 2021</h5>
|
||||
<h2>v4.4.0<span class="tag"><i>Beta</i></span></h2>
|
||||
<h3>What's New</h3>
|
||||
<ul>
|
||||
<li>Added Crossfade</li>
|
||||
<li>Multi-select in Album and Artist Details</li>
|
||||
<li>Albums now show Album Artists instead of artist of first song</li>
|
||||
</ul>
|
||||
<h3>Fixed</h3>
|
||||
<ul>
|
||||
<li> Fixed playlist preview images</li>
|
||||
<li> Fixed language switching</li>
|
||||
<li>Fixed Playlist Preview Images</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>
|
||||
<h3>Improved</h3>
|
||||
<ul>
|
||||
<li>Improved playlists tab</li>
|
||||
<li> Improved genres tab</li>
|
||||
<li>Fixed Navigate by Album Artist</li>
|
||||
<li>Changed New Music Mix Actions</li>
|
||||
<li>Improved Animations</li>
|
||||
<li>And some minor bug fixes and improvements</li>
|
||||
</ul>
|
||||
</div>
|
||||
<!--<h3><span class="colorHeader">Bug fixes</span></h3>
|
||||
<ul>
|
||||
<li></li>
|
||||
</ul>-->
|
||||
</body>
|
||||
<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>
|
||||
</div>
|
||||
</body>
|
|
@ -21,7 +21,7 @@ import code.name.monkey.appthemehelper.util.VersionUtils
|
|||
import code.name.monkey.retromusic.Constants.PRO_VERSION_PRODUCT_ID
|
||||
import code.name.monkey.retromusic.appshortcuts.DynamicShortcutManager
|
||||
import com.anjlab.android.iab.v3.BillingProcessor
|
||||
import com.anjlab.android.iab.v3.TransactionDetails
|
||||
import com.anjlab.android.iab.v3.PurchaseInfo
|
||||
import org.koin.android.ext.koin.androidContext
|
||||
import org.koin.core.context.startKoin
|
||||
|
||||
|
@ -49,9 +49,10 @@ class App : Application() {
|
|||
DynamicShortcutManager(this).initDynamicShortcuts()
|
||||
|
||||
// automatically restores purchases
|
||||
billingProcessor = BillingProcessor(this, BuildConfig.GOOGLE_PLAY_LICENSING_KEY,
|
||||
billingProcessor = BillingProcessor(
|
||||
this, BuildConfig.GOOGLE_PLAY_LICENSING_KEY,
|
||||
object : BillingProcessor.IBillingHandler {
|
||||
override fun onProductPurchased(productId: String, details: TransactionDetails?) {}
|
||||
override fun onProductPurchased(productId: String, details: PurchaseInfo?) {}
|
||||
|
||||
override fun onPurchaseHistoryRestored() {
|
||||
Toast.makeText(
|
||||
|
|
|
@ -148,3 +148,4 @@ const val SHOW_LYRICS = "show_lyrics"
|
|||
const val REMEMBER_LAST_TAB = "remember_last_tab"
|
||||
const val LAST_USED_TAB = "last_used_tab"
|
||||
const val WHITELIST_MUSIC = "whitelist_music"
|
||||
const val MATERIAL_YOU = "material_you"
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -48,9 +48,7 @@ class LockScreenActivity : AbsMusicServiceActivity() {
|
|||
setContentView(binding.root)
|
||||
hideStatusBar()
|
||||
setStatusbarColorAuto()
|
||||
setNavigationbarColorAuto()
|
||||
setTaskDescriptionColorAuto()
|
||||
setLightNavigationBar(true)
|
||||
|
||||
val config = SlidrConfig.Builder().listener(object : SlidrListener {
|
||||
override fun onSlideStateChanged(state: Int) {
|
||||
|
|
|
@ -21,10 +21,12 @@ import android.net.Uri
|
|||
import android.os.Bundle
|
||||
import android.provider.MediaStore
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.navigation.contains
|
||||
import androidx.navigation.ui.setupWithNavController
|
||||
import code.name.monkey.retromusic.*
|
||||
import code.name.monkey.retromusic.activities.base.AbsCastActivity
|
||||
import code.name.monkey.retromusic.databinding.SlidingMusicPanelLayoutBinding
|
||||
import code.name.monkey.retromusic.extensions.currentFragment
|
||||
import code.name.monkey.retromusic.extensions.extra
|
||||
import code.name.monkey.retromusic.extensions.findNavController
|
||||
import code.name.monkey.retromusic.fragments.base.AbsRecyclerViewFragment
|
||||
|
@ -54,9 +56,6 @@ class MainActivity : AbsCastActivity(), OnSharedPreferenceChangeListener {
|
|||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
setDrawUnderStatusBar()
|
||||
super.onCreate(savedInstanceState)
|
||||
setStatusbarColorAuto()
|
||||
setNavigationbarColorAuto()
|
||||
setLightNavigationBar(true)
|
||||
setTaskDescriptionColorAuto()
|
||||
hideStatusBar()
|
||||
updateTabs()
|
||||
|
@ -75,6 +74,8 @@ class MainActivity : AbsCastActivity(), OnSharedPreferenceChangeListener {
|
|||
|
||||
val categoryInfo: CategoryInfo = PreferenceUtil.libraryCategory.first { it.visible }
|
||||
if (categoryInfo.visible) {
|
||||
if (!navGraph.contains(PreferenceUtil.lastTab)) PreferenceUtil.lastTab =
|
||||
categoryInfo.category.id
|
||||
navGraph.setStartDestination(
|
||||
if (PreferenceUtil.rememberLastTab) {
|
||||
PreferenceUtil.lastTab.let {
|
||||
|
@ -88,10 +89,10 @@ class MainActivity : AbsCastActivity(), OnSharedPreferenceChangeListener {
|
|||
)
|
||||
}
|
||||
navController.graph = navGraph
|
||||
getBottomNavigationView().setupWithNavController(navController)
|
||||
bottomNavigationView.setupWithNavController(navController)
|
||||
// Scroll Fragment to top
|
||||
getBottomNavigationView().setOnItemReselectedListener {
|
||||
supportFragmentManager.findFragmentById(R.id.fragment_container)?.childFragmentManager?.fragments?.get(0)
|
||||
bottomNavigationView.setOnItemReselectedListener {
|
||||
currentFragment(R.id.fragment_container)
|
||||
.also {
|
||||
if (it is AbsRecyclerViewFragment<*, *>) {
|
||||
it.scrollToTop()
|
||||
|
@ -101,19 +102,25 @@ class MainActivity : AbsCastActivity(), OnSharedPreferenceChangeListener {
|
|||
}
|
||||
}
|
||||
}
|
||||
// This is more like a work-around as for start destination of navGraph
|
||||
// enterTransition won't work as expected
|
||||
navGraph.setStartDestination(R.id.libraryFragment)
|
||||
navController.addOnDestinationChangedListener { _, destination, _ ->
|
||||
when (destination.id) {
|
||||
when (destination.id) {
|
||||
R.id.action_home, R.id.action_song, R.id.action_album, R.id.action_artist, R.id.action_folder, R.id.action_playlist, R.id.action_genre -> {
|
||||
// Save the last tab
|
||||
if (PreferenceUtil.rememberLastTab) {
|
||||
saveTab(destination.id)
|
||||
}
|
||||
// Show Bottom Navigation Bar
|
||||
setBottomBarVisibility(true)
|
||||
setBottomNavVisibility(visible = true, animate = true)
|
||||
}
|
||||
else -> setBottomBarVisibility(false) // Hide Bottom Navigation Bar
|
||||
R.id.playing_queue_fragment -> {
|
||||
setBottomNavVisibility(visible = false)
|
||||
hideBottomSheet(true)
|
||||
}
|
||||
else -> setBottomNavVisibility(visible = false, animate = true) // Hide Bottom Navigation Bar
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -129,7 +136,7 @@ class MainActivity : AbsCastActivity(), OnSharedPreferenceChangeListener {
|
|||
PreferenceUtil.registerOnSharedPreferenceChangedListener(this)
|
||||
val expand = extra<Boolean>(EXPAND_PANEL).value ?: false
|
||||
if (expand && PreferenceUtil.isExpandPanel) {
|
||||
setBottomBarVisibility(false)
|
||||
setBottomNavVisibility(false)
|
||||
fromNotification = true
|
||||
expandPanel()
|
||||
intent.removeExtra(EXPAND_PANEL)
|
||||
|
@ -142,7 +149,7 @@ class MainActivity : AbsCastActivity(), OnSharedPreferenceChangeListener {
|
|||
}
|
||||
|
||||
override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences?, key: String?) {
|
||||
if (key == GENERAL_THEME || key == BLACK_THEME || key == ADAPTIVE_COLOR_APP || key == USER_NAME || key == TOGGLE_FULL_SCREEN || key == TOGGLE_VOLUME || key == ROUND_CORNERS || key == CAROUSEL_EFFECT || key == NOW_PLAYING_SCREEN_ID || key == TOGGLE_GENRE || key == BANNER_IMAGE_PATH || key == PROFILE_IMAGE_PATH || key == CIRCULAR_ALBUM_ART || key == KEEP_SCREEN_ON || key == TOGGLE_SEPARATE_LINE || key == TOGGLE_HOME_BANNER || key == TOGGLE_ADD_CONTROLS || key == ALBUM_COVER_STYLE || key == HOME_ARTIST_GRID_STYLE || key == ALBUM_COVER_TRANSFORM || key == DESATURATED_COLOR || key == EXTRA_SONG_INFO || key == TAB_TEXT_MODE || key == LANGUAGE_NAME || key == LIBRARY_CATEGORIES) {
|
||||
if (key == GENERAL_THEME || key == MATERIAL_YOU || key == BLACK_THEME || key == ADAPTIVE_COLOR_APP || key == USER_NAME || key == TOGGLE_FULL_SCREEN || key == TOGGLE_VOLUME || key == ROUND_CORNERS || key == CAROUSEL_EFFECT || key == NOW_PLAYING_SCREEN_ID || key == TOGGLE_GENRE || key == BANNER_IMAGE_PATH || key == PROFILE_IMAGE_PATH || key == CIRCULAR_ALBUM_ART || key == KEEP_SCREEN_ON || key == TOGGLE_SEPARATE_LINE || key == TOGGLE_HOME_BANNER || key == TOGGLE_ADD_CONTROLS || key == ALBUM_COVER_STYLE || key == HOME_ARTIST_GRID_STYLE || key == ALBUM_COVER_TRANSFORM || key == DESATURATED_COLOR || key == EXTRA_SONG_INFO || key == TAB_TEXT_MODE || key == LANGUAGE_NAME || key == LIBRARY_CATEGORIES) {
|
||||
postRecreate()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -41,8 +41,6 @@ class PermissionActivity : AbsMusicServiceActivity() {
|
|||
binding = ActivityPermissionBinding.inflate(layoutInflater)
|
||||
setContentView(binding.root)
|
||||
setStatusbarColorAuto()
|
||||
setNavigationbarColorAuto()
|
||||
setLightNavigationBar(true)
|
||||
setTaskDescriptionColorAuto()
|
||||
setupTitle()
|
||||
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
|
@ -14,10 +14,8 @@
|
|||
*/
|
||||
package code.name.monkey.retromusic.activities
|
||||
|
||||
import android.content.Intent
|
||||
import android.content.res.ColorStateList
|
||||
import android.graphics.Color
|
||||
import android.os.AsyncTask
|
||||
import android.os.Bundle
|
||||
import android.util.Log
|
||||
import android.view.MenuItem
|
||||
|
@ -31,14 +29,12 @@ import code.name.monkey.retromusic.R
|
|||
import code.name.monkey.retromusic.activities.base.AbsBaseActivity
|
||||
import code.name.monkey.retromusic.databinding.ActivityProVersionBinding
|
||||
import com.anjlab.android.iab.v3.BillingProcessor
|
||||
import com.anjlab.android.iab.v3.TransactionDetails
|
||||
import java.lang.ref.WeakReference
|
||||
import com.anjlab.android.iab.v3.PurchaseInfo
|
||||
|
||||
class PurchaseActivity : AbsBaseActivity(), BillingProcessor.IBillingHandler {
|
||||
|
||||
private lateinit var binding: ActivityProVersionBinding
|
||||
private lateinit var billingProcessor: BillingProcessor
|
||||
private var restorePurchaseAsyncTask: AsyncTask<*, *, *>? = null
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
setDrawUnderStatusBar()
|
||||
|
@ -47,8 +43,6 @@ class PurchaseActivity : AbsBaseActivity(), BillingProcessor.IBillingHandler {
|
|||
setContentView(binding.root)
|
||||
setStatusbarColor(Color.TRANSPARENT)
|
||||
setLightStatusbar(false)
|
||||
setNavigationbarColor(Color.BLACK)
|
||||
setLightNavigationBar(false)
|
||||
binding.toolbar.navigationIcon?.setTint(Color.WHITE)
|
||||
binding.toolbar.setNavigationOnClickListener { onBackPressed() }
|
||||
|
||||
|
@ -60,9 +54,7 @@ class PurchaseActivity : AbsBaseActivity(), BillingProcessor.IBillingHandler {
|
|||
MaterialUtil.setTint(binding.purchaseButton, true)
|
||||
|
||||
binding.restoreButton.setOnClickListener {
|
||||
if (restorePurchaseAsyncTask == null || restorePurchaseAsyncTask!!.status != AsyncTask.Status.RUNNING) {
|
||||
restorePurchase()
|
||||
}
|
||||
restorePurchase()
|
||||
}
|
||||
binding.purchaseButton.setOnClickListener {
|
||||
billingProcessor.purchase(this@PurchaseActivity, PRO_VERSION_PRODUCT_ID)
|
||||
|
@ -72,13 +64,25 @@ class PurchaseActivity : AbsBaseActivity(), BillingProcessor.IBillingHandler {
|
|||
}
|
||||
|
||||
private fun restorePurchase() {
|
||||
if (restorePurchaseAsyncTask != null) {
|
||||
restorePurchaseAsyncTask!!.cancel(false)
|
||||
}
|
||||
restorePurchaseAsyncTask = RestorePurchaseAsyncTask(this).execute()
|
||||
Toast.makeText(this, R.string.restoring_purchase, Toast.LENGTH_SHORT)
|
||||
.show()
|
||||
billingProcessor.loadOwnedPurchasesFromGoogleAsync(object :
|
||||
BillingProcessor.IPurchasesResponseListener {
|
||||
override fun onPurchasesSuccess() {
|
||||
onPurchaseHistoryRestored()
|
||||
}
|
||||
|
||||
override fun onPurchasesError() {
|
||||
Toast.makeText(
|
||||
this@PurchaseActivity,
|
||||
R.string.could_not_restore_purchase,
|
||||
Toast.LENGTH_SHORT
|
||||
).show()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
override fun onProductPurchased(productId: String, details: TransactionDetails?) {
|
||||
override fun onProductPurchased(productId: String, details: PurchaseInfo?) {
|
||||
Toast.makeText(this, R.string.thank_you, Toast.LENGTH_SHORT).show()
|
||||
setResult(RESULT_OK)
|
||||
}
|
||||
|
@ -105,12 +109,6 @@ class PurchaseActivity : AbsBaseActivity(), BillingProcessor.IBillingHandler {
|
|||
binding.purchaseButton.isEnabled = true
|
||||
}
|
||||
|
||||
public override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
||||
if (!billingProcessor.handleActivityResult(requestCode, resultCode, data)) {
|
||||
super.onActivityResult(requestCode, resultCode, data)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||
when (item.itemId) {
|
||||
android.R.id.home -> finish()
|
||||
|
@ -123,52 +121,6 @@ class PurchaseActivity : AbsBaseActivity(), BillingProcessor.IBillingHandler {
|
|||
super.onDestroy()
|
||||
}
|
||||
|
||||
private class RestorePurchaseAsyncTask(purchaseActivity: PurchaseActivity) :
|
||||
AsyncTask<Void, Void, Boolean>() {
|
||||
|
||||
private val buyActivityWeakReference: WeakReference<PurchaseActivity> = WeakReference(
|
||||
purchaseActivity
|
||||
)
|
||||
|
||||
override fun onPreExecute() {
|
||||
super.onPreExecute()
|
||||
val purchaseActivity = buyActivityWeakReference.get()
|
||||
if (purchaseActivity != null) {
|
||||
Toast.makeText(purchaseActivity, R.string.restoring_purchase, Toast.LENGTH_SHORT)
|
||||
.show()
|
||||
} else {
|
||||
cancel(false)
|
||||
}
|
||||
}
|
||||
|
||||
override fun doInBackground(vararg params: Void): Boolean? {
|
||||
val purchaseActivity = buyActivityWeakReference.get()
|
||||
if (purchaseActivity != null) {
|
||||
return purchaseActivity.billingProcessor.loadOwnedPurchasesFromGoogle()
|
||||
}
|
||||
cancel(false)
|
||||
return null
|
||||
}
|
||||
|
||||
override fun onPostExecute(b: Boolean?) {
|
||||
super.onPostExecute(b)
|
||||
val purchaseActivity = buyActivityWeakReference.get()
|
||||
if (purchaseActivity == null || b == null) {
|
||||
return
|
||||
}
|
||||
|
||||
if (b) {
|
||||
purchaseActivity.onPurchaseHistoryRestored()
|
||||
} else {
|
||||
Toast.makeText(
|
||||
purchaseActivity,
|
||||
R.string.could_not_restore_purchase,
|
||||
Toast.LENGTH_SHORT
|
||||
).show()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val TAG: String = "PurchaseActivity"
|
||||
}
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
*/
|
||||
package code.name.monkey.retromusic.activities
|
||||
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import android.view.MenuItem
|
||||
import androidx.navigation.NavController
|
||||
|
@ -21,33 +22,33 @@ import androidx.navigation.NavDestination
|
|||
import code.name.monkey.appthemehelper.ThemeStore
|
||||
import code.name.monkey.appthemehelper.util.VersionUtils
|
||||
import code.name.monkey.retromusic.R
|
||||
import code.name.monkey.retromusic.activities.base.AbsBaseActivity
|
||||
import code.name.monkey.retromusic.activities.base.AbsThemeActivity
|
||||
import code.name.monkey.retromusic.appshortcuts.DynamicShortcutManager
|
||||
import code.name.monkey.retromusic.databinding.ActivitySettingsBinding
|
||||
import code.name.monkey.retromusic.extensions.applyToolbar
|
||||
import code.name.monkey.retromusic.extensions.extra
|
||||
import code.name.monkey.retromusic.extensions.findNavController
|
||||
import code.name.monkey.retromusic.extensions.surfaceColor
|
||||
import com.afollestad.materialdialogs.MaterialDialog
|
||||
import com.afollestad.materialdialogs.color.ColorCallback
|
||||
|
||||
class SettingsActivity : AbsBaseActivity(), ColorCallback {
|
||||
class SettingsActivity : AbsThemeActivity(), ColorCallback, OnThemeChangedListener {
|
||||
private lateinit var binding: ActivitySettingsBinding
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
setDrawUnderStatusBar()
|
||||
super.onCreate(savedInstanceState)
|
||||
val mSavedInstanceState = extra<Bundle>(TAG).value ?: savedInstanceState
|
||||
super.onCreate(mSavedInstanceState)
|
||||
setLightStatusbarAuto(surfaceColor())
|
||||
binding = ActivitySettingsBinding.inflate(layoutInflater)
|
||||
setContentView(binding.root)
|
||||
setStatusbarColorAuto()
|
||||
setNavigationbarColorAuto()
|
||||
setLightNavigationBar(true)
|
||||
setupToolbar()
|
||||
}
|
||||
|
||||
private fun setupToolbar() {
|
||||
setTitle(R.string.action_settings)
|
||||
applyToolbar(binding.toolbar)
|
||||
val navController: NavController = findNavController(R.id.contentFrame)
|
||||
navController.addOnDestinationChangedListener { _, _, _ ->
|
||||
binding.toolbar.title =
|
||||
binding.collapsingToolbarLayout.title =
|
||||
navController.currentDestination?.let { getStringFromDestination(it) }
|
||||
}
|
||||
}
|
||||
|
@ -63,6 +64,7 @@ class SettingsActivity : AbsBaseActivity(), ColorCallback {
|
|||
R.id.personalizeSettingsFragment -> R.string.personalize
|
||||
R.id.themeSettingsFragment -> R.string.general_settings_title
|
||||
R.id.aboutActivity -> R.string.action_about
|
||||
R.id.backup_fragment -> R.string.backup_restore_title
|
||||
else -> R.id.action_settings
|
||||
}
|
||||
return getString(idRes)
|
||||
|
@ -84,6 +86,28 @@ class SettingsActivity : AbsBaseActivity(), ColorCallback {
|
|||
if (VersionUtils.hasNougatMR())
|
||||
DynamicShortcutManager(this).updateDynamicShortcuts()
|
||||
|
||||
recreate()
|
||||
restart()
|
||||
}
|
||||
|
||||
override fun onThemeValuesChanged() {
|
||||
restart()
|
||||
}
|
||||
|
||||
private fun restart() {
|
||||
val savedInstanceState = Bundle().apply {
|
||||
onSaveInstanceState(this)
|
||||
}
|
||||
finish()
|
||||
val intent = Intent(this, this::class.java).putExtra(TAG, savedInstanceState)
|
||||
startActivity(intent)
|
||||
overridePendingTransition(android.R.anim.fade_in, android.R.anim.fade_out)
|
||||
}
|
||||
|
||||
companion object {
|
||||
val TAG: String = SettingsActivity::class.java.simpleName
|
||||
}
|
||||
}
|
||||
|
||||
interface OnThemeChangedListener {
|
||||
fun onThemeValuesChanged()
|
||||
}
|
||||
|
|
|
@ -61,7 +61,6 @@ class ShareInstagramStory : AbsBaseActivity() {
|
|||
binding = ActivityShareInstagramBinding.inflate(layoutInflater)
|
||||
setContentView(binding.root)
|
||||
setStatusbarColor(Color.TRANSPARENT)
|
||||
setNavigationbarColor(Color.BLACK)
|
||||
|
||||
binding.toolbar.setBackgroundColor(Color.TRANSPARENT)
|
||||
setSupportActionBar(binding.toolbar)
|
||||
|
|
|
@ -14,9 +14,7 @@
|
|||
*/
|
||||
package code.name.monkey.retromusic.activities
|
||||
|
||||
import android.content.Intent
|
||||
import android.graphics.Paint
|
||||
import android.os.AsyncTask
|
||||
import android.os.Bundle
|
||||
import android.util.Log
|
||||
import android.view.LayoutInflater
|
||||
|
@ -27,6 +25,7 @@ import android.widget.TextView
|
|||
import android.widget.Toast
|
||||
import androidx.annotation.LayoutRes
|
||||
import androidx.appcompat.widget.AppCompatImageView
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.recyclerview.widget.DefaultItemAnimator
|
||||
import androidx.recyclerview.widget.GridLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
|
@ -41,9 +40,8 @@ import code.name.monkey.retromusic.databinding.ActivityDonationBinding
|
|||
import code.name.monkey.retromusic.extensions.textColorPrimary
|
||||
import code.name.monkey.retromusic.extensions.textColorSecondary
|
||||
import com.anjlab.android.iab.v3.BillingProcessor
|
||||
import com.anjlab.android.iab.v3.PurchaseInfo
|
||||
import com.anjlab.android.iab.v3.SkuDetails
|
||||
import com.anjlab.android.iab.v3.TransactionDetails
|
||||
import java.lang.ref.WeakReference
|
||||
import java.util.*
|
||||
|
||||
class SupportDevelopmentActivity : AbsBaseActivity(), BillingProcessor.IBillingHandler {
|
||||
|
@ -53,11 +51,9 @@ class SupportDevelopmentActivity : AbsBaseActivity(), BillingProcessor.IBillingH
|
|||
companion object {
|
||||
val TAG: String = SupportDevelopmentActivity::class.java.simpleName
|
||||
const val DONATION_PRODUCT_IDS = R.array.donation_ids
|
||||
private const val TEZ_REQUEST_CODE = 123
|
||||
}
|
||||
|
||||
var billingProcessor: BillingProcessor? = null
|
||||
private var skuDetailsLoadAsyncTask: AsyncTask<*, *, *>? = null
|
||||
|
||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||
if (item.itemId == android.R.id.home) {
|
||||
|
@ -75,12 +71,10 @@ class SupportDevelopmentActivity : AbsBaseActivity(), BillingProcessor.IBillingH
|
|||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
binding = ActivityDonationBinding.inflate(layoutInflater)
|
||||
setContentView(R.layout.activity_donation)
|
||||
setContentView(binding.root)
|
||||
|
||||
setStatusbarColorAuto()
|
||||
setNavigationbarColorAuto()
|
||||
setTaskDescriptionColorAuto()
|
||||
setLightNavigationBar(true)
|
||||
|
||||
setupToolbar()
|
||||
|
||||
|
@ -101,13 +95,35 @@ class SupportDevelopmentActivity : AbsBaseActivity(), BillingProcessor.IBillingH
|
|||
}
|
||||
|
||||
private fun loadSkuDetails() {
|
||||
if (skuDetailsLoadAsyncTask != null) {
|
||||
skuDetailsLoadAsyncTask!!.cancel(false)
|
||||
}
|
||||
skuDetailsLoadAsyncTask = SkuDetailsLoadAsyncTask(this).execute()
|
||||
binding.progressContainer.isVisible = true
|
||||
binding.recyclerView.isVisible = false
|
||||
val ids =
|
||||
resources.getStringArray(DONATION_PRODUCT_IDS)
|
||||
billingProcessor!!.getPurchaseListingDetailsAsync(
|
||||
ArrayList(listOf(*ids)),
|
||||
object : BillingProcessor.ISkuDetailsResponseListener {
|
||||
override fun onSkuDetailsResponse(skuDetails: MutableList<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();
|
||||
Toast.makeText(this, R.string.thank_you, Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
|
@ -121,68 +137,12 @@ class SupportDevelopmentActivity : AbsBaseActivity(), BillingProcessor.IBillingH
|
|||
Toast.makeText(this, R.string.restored_previous_purchases, Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
|
||||
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
||||
if (!billingProcessor!!.handleActivityResult(requestCode, resultCode, data)) {
|
||||
super.onActivityResult(requestCode, resultCode, data)
|
||||
}
|
||||
if (requestCode == TEZ_REQUEST_CODE) {
|
||||
// Process based on the data in response.
|
||||
// Log.d("result", data!!.getStringExtra("Status"))
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
billingProcessor?.release()
|
||||
skuDetailsLoadAsyncTask?.cancel(true)
|
||||
super.onDestroy()
|
||||
}
|
||||
}
|
||||
|
||||
private class SkuDetailsLoadAsyncTask(supportDevelopmentActivity: SupportDevelopmentActivity) :
|
||||
AsyncTask<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(
|
||||
private var donationsDialog: SupportDevelopmentActivity,
|
||||
objects: List<SkuDetails>
|
||||
|
@ -223,7 +183,7 @@ class SkuDetailsAdapter(
|
|||
viewHolder.title.text = skuDetails.title.replace("Music Player - MP3 Player - Retro", "")
|
||||
.trim { it <= ' ' }
|
||||
viewHolder.text.text = skuDetails.description
|
||||
viewHolder.text.visibility = View.GONE
|
||||
viewHolder.text.isVisible = false
|
||||
viewHolder.price.text = skuDetails.priceText
|
||||
viewHolder.image.setImageResource(getIcon(i))
|
||||
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -14,13 +14,17 @@
|
|||
*/
|
||||
package code.name.monkey.retromusic.activities.base
|
||||
|
||||
import android.animation.Animator
|
||||
import android.content.res.ColorStateList
|
||||
import android.graphics.Color
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.view.ViewTreeObserver
|
||||
import android.widget.FrameLayout
|
||||
import androidx.core.animation.doOnEnd
|
||||
import androidx.core.view.ViewCompat
|
||||
import androidx.core.view.WindowInsetsCompat
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.fragment.app.commit
|
||||
|
@ -28,14 +32,13 @@ import code.name.monkey.appthemehelper.util.ATHUtil
|
|||
import code.name.monkey.appthemehelper.util.ColorUtil
|
||||
import code.name.monkey.retromusic.R
|
||||
import code.name.monkey.retromusic.RetroBottomSheetBehavior
|
||||
import code.name.monkey.retromusic.databinding.ActivityMainContentBinding
|
||||
import code.name.monkey.retromusic.databinding.SlidingMusicPanelLayoutBinding
|
||||
import code.name.monkey.retromusic.extensions.*
|
||||
import code.name.monkey.retromusic.fragments.LibraryViewModel
|
||||
import code.name.monkey.retromusic.fragments.MiniPlayerFragment
|
||||
import code.name.monkey.retromusic.fragments.NowPlayingScreen
|
||||
import code.name.monkey.retromusic.fragments.NowPlayingScreen.*
|
||||
import code.name.monkey.retromusic.fragments.base.AbsPlayerFragment
|
||||
import code.name.monkey.retromusic.fragments.other.MiniPlayerFragment
|
||||
import code.name.monkey.retromusic.fragments.player.adaptive.AdaptiveFragment
|
||||
import code.name.monkey.retromusic.fragments.player.blur.BlurPlayerFragment
|
||||
import code.name.monkey.retromusic.fragments.player.card.CardFragment
|
||||
|
@ -53,10 +56,11 @@ import code.name.monkey.retromusic.fragments.player.peak.PeakPlayerFragment
|
|||
import code.name.monkey.retromusic.fragments.player.plain.PlainPlayerFragment
|
||||
import code.name.monkey.retromusic.fragments.player.simple.SimplePlayerFragment
|
||||
import code.name.monkey.retromusic.fragments.player.tiny.TinyPlayerFragment
|
||||
import code.name.monkey.retromusic.fragments.queue.PlayingQueueFragment
|
||||
import code.name.monkey.retromusic.helper.MusicPlayerRemote
|
||||
import code.name.monkey.retromusic.model.CategoryInfo
|
||||
import code.name.monkey.retromusic.util.PreferenceUtil
|
||||
import code.name.monkey.retromusic.views.BottomNavigationBarTinted
|
||||
import code.name.monkey.retromusic.util.RetroUtil
|
||||
import com.google.android.material.bottomsheet.BottomSheetBehavior.*
|
||||
import org.koin.androidx.viewmodel.ext.android.viewModel
|
||||
|
||||
|
@ -66,15 +70,14 @@ abstract class AbsSlidingMusicPanelActivity : AbsMusicServiceActivity() {
|
|||
var fromNotification: Boolean = false
|
||||
}
|
||||
|
||||
private var windowInsets: WindowInsetsCompat? = null
|
||||
private var bottomNavAnimator: Animator? = null
|
||||
protected val libraryViewModel by viewModel<LibraryViewModel>()
|
||||
private lateinit var bottomSheetBehavior: RetroBottomSheetBehavior<FrameLayout>
|
||||
private var playerFragment: AbsPlayerFragment? = null
|
||||
private var miniPlayerFragment: MiniPlayerFragment? = null
|
||||
private var nowPlayingScreen: NowPlayingScreen? = null
|
||||
private var navigationBarColor: Int = 0
|
||||
private var taskColor: Int = 0
|
||||
private var lightStatusBar: Boolean = false
|
||||
private var lightNavigationBar: Boolean = false
|
||||
private var paletteColor: Int = Color.WHITE
|
||||
protected abstract fun createContentView(): SlidingMusicPanelLayoutBinding
|
||||
private val panelState: Int
|
||||
|
@ -82,11 +85,8 @@ abstract class AbsSlidingMusicPanelActivity : AbsMusicServiceActivity() {
|
|||
private lateinit var binding: SlidingMusicPanelLayoutBinding
|
||||
private val bottomSheetCallbackList = object : BottomSheetCallback() {
|
||||
|
||||
|
||||
override fun onSlide(bottomSheet: View, slideOffset: Float) {
|
||||
setMiniPlayerAlphaProgress(slideOffset)
|
||||
binding.dimBackground.show()
|
||||
binding.dimBackground.alpha = slideOffset
|
||||
}
|
||||
|
||||
override fun onStateChanged(bottomSheet: View, newState: Int) {
|
||||
|
@ -96,15 +96,14 @@ abstract class AbsSlidingMusicPanelActivity : AbsMusicServiceActivity() {
|
|||
}
|
||||
STATE_COLLAPSED -> {
|
||||
onPanelCollapsed()
|
||||
binding.dimBackground.hide()
|
||||
if (fromNotification) {
|
||||
hideBottomBar(MusicPlayerRemote.playingQueue.isEmpty())
|
||||
hideBottomSheet(MusicPlayerRemote.playingQueue.isEmpty())
|
||||
fromNotification = false
|
||||
}
|
||||
}
|
||||
STATE_SETTLING, STATE_DRAGGING -> {
|
||||
if (fromNotification) {
|
||||
getBottomNavigationView().isVisible = true
|
||||
bottomNavigationView.isVisible = true
|
||||
}
|
||||
}
|
||||
else -> {
|
||||
|
@ -120,23 +119,30 @@ abstract class AbsSlidingMusicPanelActivity : AbsMusicServiceActivity() {
|
|||
super.onCreate(savedInstanceState)
|
||||
binding = createContentView()
|
||||
setContentView(binding.root)
|
||||
ViewCompat.setOnApplyWindowInsetsListener(
|
||||
binding.root
|
||||
) { _, insets ->
|
||||
windowInsets = insets
|
||||
insets
|
||||
}
|
||||
bottomNavigationView.drawAboveSystemBarsWithPadding()
|
||||
if (RetroUtil.isLandscape()) {
|
||||
binding.slidingPanel.drawAboveSystemBarsWithPadding(true)
|
||||
}
|
||||
binding.fragmentContainer.addBottomInsets()
|
||||
chooseFragmentForTheme()
|
||||
setupSlidingUpPanel()
|
||||
setupBottomSheet()
|
||||
updateColor()
|
||||
|
||||
val themeColor = resolveColor(android.R.attr.windowBackground, Color.GRAY)
|
||||
binding.dimBackground.setBackgroundColor(ColorUtil.withAlpha(themeColor, 0.5f))
|
||||
binding.dimBackground.setOnClickListener {
|
||||
println("dimBackground")
|
||||
collapsePanel()
|
||||
}
|
||||
binding.slidingPanel.backgroundTintList = ColorStateList.valueOf(darkAccentColor())
|
||||
bottomNavigationView.backgroundTintList = ColorStateList.valueOf(darkAccentColor())
|
||||
}
|
||||
|
||||
private fun setupBottomSheet() {
|
||||
bottomSheetBehavior = from(binding.slidingPanel) as RetroBottomSheetBehavior
|
||||
bottomSheetBehavior.addBottomSheetCallback(bottomSheetCallbackList)
|
||||
bottomSheetBehavior.maxWidth = resources.displayMetrics.widthPixels
|
||||
bottomSheetBehavior.isHideable = false
|
||||
setMiniPlayerAlphaProgress(0F)
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
|
@ -155,41 +161,37 @@ abstract class AbsSlidingMusicPanelActivity : AbsMusicServiceActivity() {
|
|||
}
|
||||
|
||||
protected fun wrapSlidingMusicPanel(): SlidingMusicPanelLayoutBinding {
|
||||
val slidingMusicPanelLayoutBinding =
|
||||
SlidingMusicPanelLayoutBinding.inflate(layoutInflater)
|
||||
val contentContainer: ViewGroup =
|
||||
slidingMusicPanelLayoutBinding.mainContentFrame
|
||||
ActivityMainContentBinding.inflate(layoutInflater, contentContainer, true)
|
||||
return slidingMusicPanelLayoutBinding
|
||||
return SlidingMusicPanelLayoutBinding.inflate(layoutInflater)
|
||||
}
|
||||
|
||||
fun collapsePanel() {
|
||||
bottomSheetBehavior.state = STATE_COLLAPSED
|
||||
setMiniPlayerAlphaProgress(0f)
|
||||
}
|
||||
|
||||
fun expandPanel() {
|
||||
bottomSheetBehavior.state = STATE_EXPANDED
|
||||
setMiniPlayerAlphaProgress(1f)
|
||||
}
|
||||
|
||||
private fun setMiniPlayerAlphaProgress(progress: Float) {
|
||||
if (progress < 0) return
|
||||
val alpha = 1 - progress
|
||||
miniPlayerFragment?.view?.alpha = alpha
|
||||
miniPlayerFragment?.view?.alpha = 1 - (progress / 0.2F)
|
||||
miniPlayerFragment?.view?.visibility = if (alpha == 0f) View.GONE else View.VISIBLE
|
||||
binding.bottomNavigationView.translationY = progress * 500
|
||||
binding.bottomNavigationView.alpha = alpha
|
||||
binding.playerFragmentContainer.alpha = (progress - 0.2F) / 0.2F
|
||||
}
|
||||
|
||||
open fun onPanelCollapsed() {
|
||||
setMiniPlayerAlphaProgress(0F)
|
||||
// restore values
|
||||
super.setLightStatusbar(lightStatusBar)
|
||||
super.setLightStatusbarAuto(surfaceColor())
|
||||
super.setLightNavigationAuto()
|
||||
super.setTaskDescriptionColor(taskColor)
|
||||
super.setNavigationbarColor(navigationBarColor)
|
||||
super.setLightNavigationBar(lightNavigationBar)
|
||||
}
|
||||
|
||||
open fun onPanelExpanded() {
|
||||
setMiniPlayerAlphaProgress(1F)
|
||||
onPaletteColorChanged()
|
||||
}
|
||||
|
||||
|
@ -214,9 +216,7 @@ abstract class AbsSlidingMusicPanelActivity : AbsMusicServiceActivity() {
|
|||
})
|
||||
}
|
||||
|
||||
fun getBottomNavigationView(): BottomNavigationBarTinted {
|
||||
return binding.bottomNavigationView
|
||||
}
|
||||
val bottomNavigationView get() = binding.bottomNavigationView
|
||||
|
||||
override fun onServiceConnected() {
|
||||
super.onServiceConnected()
|
||||
|
@ -225,15 +225,19 @@ abstract class AbsSlidingMusicPanelActivity : AbsMusicServiceActivity() {
|
|||
ViewTreeObserver.OnGlobalLayoutListener {
|
||||
override fun onGlobalLayout() {
|
||||
binding.slidingPanel.viewTreeObserver.removeOnGlobalLayoutListener(this)
|
||||
hideBottomBar(false)
|
||||
hideBottomSheet(false)
|
||||
}
|
||||
})
|
||||
} // don't call hideBottomBar(true) here as it causes a bug with the SlidingUpPanelLayout
|
||||
} // don't call hideBottomSheet(true) here as it causes a bug with the SlidingUpPanelLayout
|
||||
}
|
||||
|
||||
override fun onQueueChanged() {
|
||||
super.onQueueChanged()
|
||||
hideBottomBar(MusicPlayerRemote.playingQueue.isEmpty())
|
||||
// Mini player should be hidden in Playing Queue
|
||||
// it may pop up if hideBottomSheet is called
|
||||
if (currentFragment(R.id.fragment_container) !is PlayingQueueFragment) {
|
||||
hideBottomSheet(MusicPlayerRemote.playingQueue.isEmpty())
|
||||
}
|
||||
}
|
||||
|
||||
override fun onBackPressed() {
|
||||
|
@ -260,13 +264,10 @@ abstract class AbsSlidingMusicPanelActivity : AbsMusicServiceActivity() {
|
|||
} else if (nowPlayingScreen == Card || nowPlayingScreen == Blur || nowPlayingScreen == BlurCard) {
|
||||
super.setLightStatusbar(false)
|
||||
super.setLightNavigationBar(true)
|
||||
super.setNavigationbarColor(Color.BLACK)
|
||||
} else if (nowPlayingScreen == Color || nowPlayingScreen == Tiny || nowPlayingScreen == Gradient) {
|
||||
super.setNavigationbarColor(paletteColor)
|
||||
super.setLightNavigationBar(isColorLight)
|
||||
super.setLightStatusbar(isColorLight)
|
||||
} else if (nowPlayingScreen == Full) {
|
||||
super.setNavigationbarColor(paletteColor)
|
||||
super.setLightNavigationBar(isColorLight)
|
||||
super.setLightStatusbar(false)
|
||||
} else if (nowPlayingScreen == Classic) {
|
||||
|
@ -287,27 +288,6 @@ abstract class AbsSlidingMusicPanelActivity : AbsMusicServiceActivity() {
|
|||
}
|
||||
}
|
||||
|
||||
override fun setLightStatusbar(enabled: Boolean) {
|
||||
lightStatusBar = enabled
|
||||
if (panelState == STATE_COLLAPSED) {
|
||||
super.setLightStatusbar(enabled)
|
||||
}
|
||||
}
|
||||
|
||||
override fun setLightNavigationBar(enabled: Boolean) {
|
||||
lightNavigationBar = enabled
|
||||
if (panelState == STATE_COLLAPSED) {
|
||||
super.setLightNavigationBar(enabled)
|
||||
}
|
||||
}
|
||||
|
||||
override fun setNavigationbarColor(color: Int) {
|
||||
navigationBarColor = color
|
||||
if (panelState == STATE_COLLAPSED) {
|
||||
super.setNavigationbarColor(color)
|
||||
}
|
||||
}
|
||||
|
||||
override fun setTaskDescriptionColor(color: Int) {
|
||||
taskColor = color
|
||||
if (panelState == STATE_COLLAPSED) {
|
||||
|
@ -337,38 +317,57 @@ abstract class AbsSlidingMusicPanelActivity : AbsMusicServiceActivity() {
|
|||
})
|
||||
}
|
||||
|
||||
fun setBottomBarVisibility(visible: Boolean) {
|
||||
fun setBottomNavVisibility(visible: Boolean, animate: Boolean = false) {
|
||||
binding.bottomNavigationView.isVisible = visible
|
||||
hideBottomBar(MusicPlayerRemote.playingQueue.isEmpty())
|
||||
hideBottomSheet(MusicPlayerRemote.playingQueue.isEmpty(), animate)
|
||||
}
|
||||
|
||||
private fun hideBottomBar(hide: Boolean) {
|
||||
fun hideBottomSheet(hide: Boolean, animate: Boolean = false) {
|
||||
val heightOfBar =
|
||||
if (MusicPlayerRemote.isCasting) dip(R.dimen.cast_mini_player_height) else dip(R.dimen.mini_player_height)
|
||||
val heightOfBarWithTabs =
|
||||
if (MusicPlayerRemote.isCasting) dip(R.dimen.mini_cast_player_height_expanded) else dip(
|
||||
R.dimen.mini_player_height_expanded
|
||||
)
|
||||
windowInsets.safeGetBottomInsets() +
|
||||
if (MusicPlayerRemote.isCasting) dip(R.dimen.cast_mini_player_height) else dip(R.dimen.mini_player_height)
|
||||
val heightOfBarWithTabs = heightOfBar + dip(R.dimen.bottom_nav_height)
|
||||
val isVisible = binding.bottomNavigationView.isVisible
|
||||
if (hide) {
|
||||
bottomSheetBehavior.isHideable = true
|
||||
bottomSheetBehavior.peekHeight = 0
|
||||
bottomSheetBehavior.peekHeight = -windowInsets.safeGetBottomInsets()
|
||||
bottomSheetBehavior.state = STATE_COLLAPSED
|
||||
libraryViewModel.setFabMargin(if (isVisible) dip(R.dimen.bottom_nav_height) else 0)
|
||||
ViewCompat.setElevation(binding.slidingPanel, 0f)
|
||||
ViewCompat.setElevation(binding.bottomNavigationView, 10f)
|
||||
collapsePanel()
|
||||
} else {
|
||||
if (MusicPlayerRemote.playingQueue.isNotEmpty()) {
|
||||
bottomSheetBehavior.isHideable = false
|
||||
|
||||
ViewCompat.setElevation(binding.slidingPanel, 10f)
|
||||
ViewCompat.setElevation(binding.bottomNavigationView, 10f)
|
||||
if (isVisible) {
|
||||
println("List")
|
||||
if (bottomSheetBehavior.state != STATE_EXPANDED)
|
||||
getBottomNavigationView().translateYAnimate(0F)
|
||||
bottomSheetBehavior.peekHeightAnimate(heightOfBarWithTabs)
|
||||
if (animate) {
|
||||
bottomNavAnimator?.end()
|
||||
bottomSheetBehavior.peekHeightAnimate(heightOfBarWithTabs)
|
||||
bottomNavAnimator = binding.bottomNavigationView.translateYAnimate(0F)
|
||||
} else {
|
||||
bottomSheetBehavior.peekHeight = heightOfBarWithTabs
|
||||
binding.bottomNavigationView.translationY = 0F
|
||||
}
|
||||
binding.bottomNavigationView.bringToFront()
|
||||
libraryViewModel.setFabMargin(heightOfBarWithTabs - 2 * windowInsets.safeGetBottomInsets())
|
||||
} else {
|
||||
println("Details")
|
||||
bottomSheetBehavior.peekHeight = heightOfBar
|
||||
if (animate) {
|
||||
bottomSheetBehavior.peekHeightAnimate(heightOfBar)
|
||||
bottomNavAnimator?.end()
|
||||
bottomNavAnimator =
|
||||
bottomNavigationView.translateYAnimate(dip(R.dimen.bottom_nav_height).toFloat())
|
||||
bottomNavAnimator?.doOnEnd {
|
||||
binding.slidingPanel.bringToFront()
|
||||
}
|
||||
} else {
|
||||
bottomSheetBehavior.peekHeight = heightOfBar
|
||||
binding.bottomNavigationView.translationY =
|
||||
dip(R.dimen.bottom_nav_height).toFloat()
|
||||
binding.slidingPanel.bringToFront()
|
||||
}
|
||||
libraryViewModel.setFabMargin(heightOfBar - 2 * windowInsets.safeGetBottomInsets())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -376,7 +375,7 @@ abstract class AbsSlidingMusicPanelActivity : AbsMusicServiceActivity() {
|
|||
|
||||
fun setAllowDragging(allowDragging: Boolean) {
|
||||
bottomSheetBehavior.setAllowDragging(allowDragging)
|
||||
hideBottomBar(false)
|
||||
hideBottomSheet(false)
|
||||
}
|
||||
|
||||
private fun chooseFragmentForTheme() {
|
||||
|
|
|
@ -33,9 +33,11 @@ import code.name.monkey.appthemehelper.util.ColorUtil
|
|||
import code.name.monkey.appthemehelper.util.VersionUtils
|
||||
import code.name.monkey.retromusic.LanguageContextWrapper
|
||||
import code.name.monkey.retromusic.R
|
||||
import code.name.monkey.retromusic.extensions.surfaceColor
|
||||
import code.name.monkey.retromusic.util.PreferenceUtil
|
||||
import code.name.monkey.retromusic.util.RetroUtil
|
||||
import code.name.monkey.retromusic.util.theme.ThemeManager
|
||||
import com.google.android.material.color.DynamicColors
|
||||
import java.util.*
|
||||
|
||||
abstract class AbsThemeActivity : ATHToolbarActivity(), Runnable {
|
||||
|
@ -49,12 +51,22 @@ abstract class AbsThemeActivity : ATHToolbarActivity(), Runnable {
|
|||
setImmersiveFullscreen()
|
||||
registerSystemUiVisibility()
|
||||
toggleScreenOn()
|
||||
//MaterialDialogsUtil.updateMaterialDialogsThemeSingleton(this)
|
||||
setDrawUnderNavigationBar()
|
||||
setLightNavigationAuto()
|
||||
setLightStatusbarAuto(surfaceColor())
|
||||
}
|
||||
|
||||
private fun updateTheme() {
|
||||
setTheme(ThemeManager.getThemeResValue(this))
|
||||
setDefaultNightMode(ThemeManager.getNightMode(this))
|
||||
|
||||
// Apply dynamic colors to activity if enabled
|
||||
if (PreferenceUtil.materialYou) {
|
||||
DynamicColors.applyIfAvailable(
|
||||
this,
|
||||
com.google.android.material.R.style.ThemeOverlay_Material3_DynamicColors_DayNight
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun toggleScreenOn() {
|
||||
|
@ -91,7 +103,7 @@ abstract class AbsThemeActivity : ATHToolbarActivity(), Runnable {
|
|||
RetroUtil.setAllowDrawUnderStatusBar(window)
|
||||
}
|
||||
|
||||
fun setDrawUnderNavigationBar() {
|
||||
private fun setDrawUnderNavigationBar() {
|
||||
RetroUtil.setAllowDrawUnderNavigationBar(window)
|
||||
}
|
||||
|
||||
|
@ -148,6 +160,10 @@ abstract class AbsThemeActivity : ATHToolbarActivity(), Runnable {
|
|||
setNavigationbarColor(ATHUtil.resolveColor(this, R.attr.colorSurface))
|
||||
}
|
||||
|
||||
fun setLightNavigationAuto() {
|
||||
ATH.setLightNavigationbarAuto(this, surfaceColor())
|
||||
}
|
||||
|
||||
open fun setLightStatusbar(enabled: Boolean) {
|
||||
ATH.setLightStatusbar(this, enabled)
|
||||
}
|
||||
|
|
|
@ -30,7 +30,6 @@ import androidx.annotation.StringDef
|
|||
import androidx.annotation.StringRes
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import code.name.monkey.appthemehelper.ThemeStore
|
||||
import code.name.monkey.appthemehelper.util.ATHUtil
|
||||
import code.name.monkey.appthemehelper.util.MaterialUtil
|
||||
import code.name.monkey.appthemehelper.util.TintHelper
|
||||
import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper
|
||||
|
@ -74,12 +73,9 @@ open class BugReportActivity : AbsThemeActivity() {
|
|||
private var deviceInfo: DeviceInfo? = null
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
setDrawUnderStatusBar()
|
||||
super.onCreate(savedInstanceState)
|
||||
binding = ActivityBugReportBinding.inflate(layoutInflater)
|
||||
setContentView(binding.root)
|
||||
setStatusbarColorAuto()
|
||||
setNavigationbarColorAuto()
|
||||
setTaskDescriptionColorAuto()
|
||||
|
||||
initViews()
|
||||
|
@ -92,8 +88,6 @@ open class BugReportActivity : AbsThemeActivity() {
|
|||
|
||||
private fun initViews() {
|
||||
val accentColor = ThemeStore.accentColor(this)
|
||||
val primaryColor = ATHUtil.resolveColor(this, R.attr.colorSurface)
|
||||
binding.toolbar.setBackgroundColor(primaryColor)
|
||||
setSupportActionBar(binding.toolbar)
|
||||
ToolbarContentTintHelper.colorBackButton(binding.toolbar)
|
||||
supportActionBar?.setDisplayHomeAsUpEnabled(true)
|
||||
|
|
|
@ -196,7 +196,6 @@ abstract class AbsTagEditorActivity<VB : ViewBinding> : AbsBaseActivity() {
|
|||
_binding = bindingInflater.invoke(layoutInflater)
|
||||
setContentView(binding.root)
|
||||
setStatusbarColorAuto()
|
||||
setNavigationbarColorAuto()
|
||||
setTaskDescriptionColorAuto()
|
||||
|
||||
saveFab = findViewById(R.id.saveTags)
|
||||
|
|
|
@ -29,10 +29,10 @@ import android.view.LayoutInflater
|
|||
import android.widget.ImageView
|
||||
import android.widget.Toast
|
||||
import code.name.monkey.appthemehelper.util.ATHUtil
|
||||
import code.name.monkey.appthemehelper.util.MaterialUtil
|
||||
import code.name.monkey.retromusic.R
|
||||
import code.name.monkey.retromusic.databinding.ActivityAlbumTagEditorBinding
|
||||
import code.name.monkey.retromusic.extensions.appHandleColor
|
||||
import code.name.monkey.retromusic.extensions.setTint
|
||||
import code.name.monkey.retromusic.glide.GlideApp
|
||||
import code.name.monkey.retromusic.glide.palette.BitmapPaletteWrapper
|
||||
import code.name.monkey.retromusic.model.ArtworkInfo
|
||||
|
@ -62,8 +62,6 @@ class AlbumTagEditorActivity : AbsTagEditorActivity<ActivityAlbumTagEditorBindin
|
|||
}
|
||||
|
||||
override fun loadImageFromFile(selectedFile: Uri?) {
|
||||
|
||||
|
||||
GlideApp.with(this@AlbumTagEditorActivity).asBitmapPalette().load(selectedFile)
|
||||
.diskCacheStrategy(DiskCacheStrategy.NONE).skipMemoryCache(true)
|
||||
.into(object : ImageViewTarget<BitmapPaletteWrapper>(binding.editorImage) {
|
||||
|
@ -119,10 +117,10 @@ class AlbumTagEditorActivity : AbsTagEditorActivity<ActivityAlbumTagEditorBindin
|
|||
private fun setUpViews() {
|
||||
fillViewsWithFileTags()
|
||||
|
||||
MaterialUtil.setTint(binding.yearContainer, false)
|
||||
MaterialUtil.setTint(binding.genreContainer, false)
|
||||
MaterialUtil.setTint(binding.albumTitleContainer, false)
|
||||
MaterialUtil.setTint(binding.albumArtistContainer, false)
|
||||
binding.yearContainer.setTint(false)
|
||||
binding.genreContainer.setTint(false)
|
||||
binding.albumTitleContainer.setTint(false)
|
||||
binding.albumArtistContainer.setTint(false)
|
||||
|
||||
binding.albumText.appHandleColor().addTextChangedListener(this)
|
||||
binding.albumArtistText.appHandleColor().addTextChangedListener(this)
|
||||
|
|
|
@ -22,10 +22,10 @@ import android.text.TextWatcher
|
|||
import android.view.LayoutInflater
|
||||
import android.widget.ImageView
|
||||
import code.name.monkey.appthemehelper.util.ATHUtil
|
||||
import code.name.monkey.appthemehelper.util.MaterialUtil
|
||||
import code.name.monkey.retromusic.R
|
||||
import code.name.monkey.retromusic.databinding.ActivitySongTagEditorBinding
|
||||
import code.name.monkey.retromusic.extensions.appHandleColor
|
||||
import code.name.monkey.retromusic.extensions.setTint
|
||||
import code.name.monkey.retromusic.repository.SongRepository
|
||||
import org.jaudiotagger.tag.FieldKey
|
||||
import org.koin.android.ext.android.inject
|
||||
|
@ -50,15 +50,15 @@ class SongTagEditorActivity : AbsTagEditorActivity<ActivitySongTagEditorBinding>
|
|||
@SuppressLint("ClickableViewAccessibility")
|
||||
private fun setUpViews() {
|
||||
fillViewsWithFileTags()
|
||||
MaterialUtil.setTint(binding.songTextContainer, false)
|
||||
MaterialUtil.setTint(binding.composerContainer, false)
|
||||
MaterialUtil.setTint(binding.albumTextContainer, false)
|
||||
MaterialUtil.setTint(binding.artistContainer, false)
|
||||
MaterialUtil.setTint(binding.albumArtistContainer, false)
|
||||
MaterialUtil.setTint(binding.yearContainer, false)
|
||||
MaterialUtil.setTint(binding.genreContainer, false)
|
||||
MaterialUtil.setTint(binding.trackNumberContainer, false)
|
||||
MaterialUtil.setTint(binding.lyricsContainer, false)
|
||||
binding.songTextContainer.setTint(false)
|
||||
binding.composerContainer.setTint(false)
|
||||
binding.albumTextContainer.setTint(false)
|
||||
binding.artistContainer.setTint(false)
|
||||
binding.albumArtistContainer.setTint(false)
|
||||
binding.yearContainer.setTint(false)
|
||||
binding.genreContainer.setTint(false)
|
||||
binding.trackNumberContainer.setTint(false)
|
||||
binding.lyricsContainer.setTint(false)
|
||||
|
||||
binding.songText.appHandleColor().addTextChangedListener(this)
|
||||
binding.albumText.appHandleColor().addTextChangedListener(this)
|
||||
|
|
|
@ -40,7 +40,7 @@ public class CategoryInfoAdapter extends RecyclerView.Adapter<CategoryInfoAdapte
|
|||
implements SwipeAndDragHelper.ActionCompletionContract {
|
||||
|
||||
private List<CategoryInfo> categoryInfos;
|
||||
private ItemTouchHelper touchHelper;
|
||||
private final ItemTouchHelper touchHelper;
|
||||
|
||||
public CategoryInfoAdapter() {
|
||||
SwipeAndDragHelper swipeAndDragHelper = new SwipeAndDragHelper(this);
|
||||
|
@ -128,15 +128,15 @@ public class CategoryInfoAdapter extends RecyclerView.Adapter<CategoryInfoAdapte
|
|||
}
|
||||
|
||||
static class ViewHolder extends RecyclerView.ViewHolder {
|
||||
private MaterialCheckBox checkBox;
|
||||
private View dragView;
|
||||
private TextView title;
|
||||
private final MaterialCheckBox checkBox;
|
||||
private final View dragView;
|
||||
private final TextView title;
|
||||
|
||||
ViewHolder(View view) {
|
||||
super(view);
|
||||
checkBox = view.findViewById(R.id.checkbox);
|
||||
checkBox.setButtonTintList(
|
||||
ColorStateList.valueOf(ThemeStore.Companion.accentColor(checkBox.getContext())));
|
||||
ColorStateList.valueOf(ThemeStore.Companion.accentColor(checkBox.getContext())));
|
||||
title = view.findViewById(R.id.title);
|
||||
dragView = view.findViewById(R.id.drag_view);
|
||||
}
|
||||
|
|
|
@ -127,8 +127,8 @@ class SearchAdapter(
|
|||
val artist = dataSet[position] as Artist
|
||||
holder.title?.text = artist.name
|
||||
holder.text?.text = MusicUtil.getArtistInfoString(activity, artist)
|
||||
GlideApp.with(activity).asDrawable().artistImageOptions(artist).load(artist)
|
||||
.into(holder.image!!)
|
||||
GlideApp.with(activity).asDrawable().artistImageOptions(artist).load(
|
||||
RetroGlideExtension.getArtistModel(artist)).into(holder.image!!)
|
||||
}
|
||||
else -> {
|
||||
holder.title?.text = dataSet[position].toString()
|
||||
|
|
|
@ -40,7 +40,7 @@ import kotlin.math.log10
|
|||
import kotlin.math.pow
|
||||
|
||||
class SongFileAdapter(
|
||||
private val activity: AppCompatActivity,
|
||||
override val activity: AppCompatActivity,
|
||||
private var dataSet: List<File>,
|
||||
private val itemLayoutRes: Int,
|
||||
private val iCallbacks: ICallbacks?,
|
||||
|
|
|
@ -39,7 +39,7 @@ import code.name.monkey.retromusic.util.color.MediaNotificationProcessor
|
|||
import me.zhanghai.android.fastscroll.PopupTextProvider
|
||||
|
||||
open class AlbumAdapter(
|
||||
val activity: FragmentActivity,
|
||||
override val activity: FragmentActivity,
|
||||
var dataSet: List<Album>,
|
||||
var itemLayoutRes: Int,
|
||||
iCabHolder: ICabHolder?,
|
||||
|
@ -68,7 +68,7 @@ open class AlbumAdapter(
|
|||
return ViewHolder(view)
|
||||
}
|
||||
|
||||
private fun getAlbumTitle(album: Album): String? {
|
||||
private fun getAlbumTitle(album: Album): String {
|
||||
return album.title
|
||||
}
|
||||
|
||||
|
@ -88,7 +88,13 @@ open class AlbumAdapter(
|
|||
holder.itemView.isActivated = isChecked
|
||||
holder.title?.text = getAlbumTitle(album)
|
||||
holder.text?.text = getAlbumText(album)
|
||||
ViewCompat.setTransitionName(holder.image!!, album.id.toString())
|
||||
// Check if imageContainer exists so we can have a smooth transition without
|
||||
// CardView clipping, if it doesn't exist in current layout set transition name to image instead.
|
||||
if (holder.imageContainer != null) {
|
||||
ViewCompat.setTransitionName(holder.imageContainer!!, album.id.toString())
|
||||
} else {
|
||||
ViewCompat.setTransitionName(holder.image!!, album.id.toString())
|
||||
}
|
||||
loadAlbumCover(album, holder)
|
||||
}
|
||||
|
||||
|
@ -130,7 +136,7 @@ open class AlbumAdapter(
|
|||
}
|
||||
|
||||
override fun getName(album: Album): String {
|
||||
return album.title!!
|
||||
return album.title
|
||||
}
|
||||
|
||||
override fun onMultipleItemAction(
|
||||
|
@ -177,7 +183,7 @@ open class AlbumAdapter(
|
|||
toggleChecked(layoutPosition)
|
||||
} else {
|
||||
image?.let {
|
||||
listener?.onAlbumClick(dataSet[layoutPosition].id, it)
|
||||
listener?.onAlbumClick(dataSet[layoutPosition].id, imageContainer ?: it)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,6 +24,7 @@ import androidx.fragment.app.Fragment
|
|||
import androidx.fragment.app.FragmentManager
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import code.name.monkey.retromusic.R
|
||||
import code.name.monkey.retromusic.activities.MainActivity
|
||||
import code.name.monkey.retromusic.fragments.AlbumCoverStyle
|
||||
import code.name.monkey.retromusic.fragments.NowPlayingScreen.*
|
||||
import code.name.monkey.retromusic.glide.GlideApp
|
||||
|
@ -35,6 +36,7 @@ import code.name.monkey.retromusic.util.MusicUtil
|
|||
import code.name.monkey.retromusic.util.NavigationUtil
|
||||
import code.name.monkey.retromusic.util.PreferenceUtil
|
||||
import code.name.monkey.retromusic.util.color.MediaNotificationProcessor
|
||||
import com.google.android.material.bottomsheet.BottomSheetBehavior.STATE_EXPANDED
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
|
@ -89,6 +91,7 @@ class AlbumCoverPagerAdapter(
|
|||
private lateinit var song: Song
|
||||
private var colorReceiver: ColorReceiver? = null
|
||||
private var request: Int = 0
|
||||
private val mainActivity get() = activity as MainActivity
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
@ -106,7 +109,9 @@ class AlbumCoverPagerAdapter(
|
|||
ViewCompat.setTransitionName(view, "lyrics")
|
||||
albumCover = view.findViewById(R.id.player_image)
|
||||
view.setOnClickListener {
|
||||
showLyricsDialog()
|
||||
if (mainActivity.getBottomSheetBehavior().state == STATE_EXPANDED) {
|
||||
showLyricsDialog()
|
||||
}
|
||||
}
|
||||
return view
|
||||
}
|
||||
|
|
|
@ -42,7 +42,7 @@ import me.zhanghai.android.fastscroll.PopupTextProvider
|
|||
import java.util.*
|
||||
|
||||
class ArtistAdapter(
|
||||
val activity: FragmentActivity,
|
||||
override val activity: FragmentActivity,
|
||||
var dataSet: List<Artist>,
|
||||
var itemLayoutRes: Int,
|
||||
val ICabHolder: ICabHolder?,
|
||||
|
@ -85,12 +85,12 @@ class ArtistAdapter(
|
|||
holder.itemView.isActivated = isChecked
|
||||
holder.title?.text = artist.name
|
||||
holder.text?.hide()
|
||||
holder.image?.let {
|
||||
if (PreferenceUtil.albumArtistsOnly) {
|
||||
ViewCompat.setTransitionName(it, artist.name)
|
||||
} else {
|
||||
ViewCompat.setTransitionName(it, artist.id.toString())
|
||||
}
|
||||
val transitionName =
|
||||
if (PreferenceUtil.albumArtistsOnly) artist.name else artist.id.toString()
|
||||
if (holder.imageContainer != null) {
|
||||
ViewCompat.setTransitionName(holder.imageContainer!!, transitionName)
|
||||
} else {
|
||||
ViewCompat.setTransitionName(holder.image!!, transitionName)
|
||||
}
|
||||
loadArtistImage(artist, holder)
|
||||
}
|
||||
|
@ -169,9 +169,9 @@ class ArtistAdapter(
|
|||
val artist = dataSet[layoutPosition]
|
||||
image?.let {
|
||||
if (PreferenceUtil.albumArtistsOnly && IAlbumArtistClickListener != null) {
|
||||
IAlbumArtistClickListener.onAlbumArtist(artist.name, it)
|
||||
IAlbumArtistClickListener.onAlbumArtist(artist.name, imageContainer ?: it)
|
||||
} else {
|
||||
IArtistClickListener.onArtist(artist.id, it)
|
||||
IArtistClickListener.onArtist(artist.id, imageContainer ?: it)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -16,13 +16,13 @@ package code.name.monkey.retromusic.adapter.base;
|
|||
|
||||
import android.graphics.Color;
|
||||
import android.view.View;
|
||||
import android.widget.FrameLayout;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.appcompat.widget.AppCompatImageView;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import com.google.android.material.card.MaterialCardView;
|
||||
import com.h6ah4i.android.widget.advrecyclerview.utils.AbstractDraggableSwipeableItemViewHolder;
|
||||
|
@ -41,15 +41,12 @@ public class MediaEntryViewHolder extends AbstractDraggableSwipeableItemViewHold
|
|||
@Nullable
|
||||
public ImageView image;
|
||||
|
||||
@Nullable
|
||||
public ImageView artistImage;
|
||||
|
||||
@Nullable
|
||||
public ImageView playerImage;
|
||||
|
||||
@Nullable
|
||||
public MaterialCardView imageContainerCard;
|
||||
|
||||
@Nullable
|
||||
public FrameLayout imageContainer;
|
||||
|
||||
@Nullable
|
||||
public TextView imageText;
|
||||
|
||||
|
@ -65,10 +62,6 @@ public class MediaEntryViewHolder extends AbstractDraggableSwipeableItemViewHold
|
|||
@Nullable
|
||||
public View paletteColorContainer;
|
||||
|
||||
|
||||
@Nullable
|
||||
public RecyclerView recyclerView;
|
||||
|
||||
@Nullable
|
||||
public TextView text;
|
||||
|
||||
|
@ -88,18 +81,16 @@ public class MediaEntryViewHolder extends AbstractDraggableSwipeableItemViewHold
|
|||
text2 = itemView.findViewById(R.id.text2);
|
||||
|
||||
image = itemView.findViewById(R.id.image);
|
||||
artistImage = itemView.findViewById(R.id.artistImage);
|
||||
playerImage = itemView.findViewById(R.id.player_image);
|
||||
time = itemView.findViewById(R.id.time);
|
||||
|
||||
imageText = itemView.findViewById(R.id.imageText);
|
||||
imageTextContainer = itemView.findViewById(R.id.imageTextContainer);
|
||||
imageContainerCard = itemView.findViewById(R.id.imageContainerCard);
|
||||
imageContainer = itemView.findViewById(R.id.imageContainer);
|
||||
|
||||
menu = itemView.findViewById(R.id.menu);
|
||||
dragView = itemView.findViewById(R.id.drag_view);
|
||||
paletteColorContainer = itemView.findViewById(R.id.paletteColorContainer);
|
||||
recyclerView = itemView.findViewById(R.id.recycler_view);
|
||||
mask = itemView.findViewById(R.id.mask);
|
||||
dummyContainer = itemView.findViewById(R.id.dummy_view);
|
||||
|
||||
|
|
|
@ -41,7 +41,7 @@ import code.name.monkey.retromusic.model.Song
|
|||
import code.name.monkey.retromusic.util.MusicUtil
|
||||
|
||||
class PlaylistAdapter(
|
||||
private val activity: FragmentActivity,
|
||||
override val activity: FragmentActivity,
|
||||
private var dataSet: List<PlaylistWithSongs>,
|
||||
private var itemLayoutRes: Int,
|
||||
ICabHolder: ICabHolder?,
|
||||
|
|
|
@ -16,7 +16,6 @@ package code.name.monkey.retromusic.adapter.song
|
|||
|
||||
import android.view.MenuItem
|
||||
import android.view.View
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.fragment.app.FragmentActivity
|
||||
import code.name.monkey.retromusic.R
|
||||
import code.name.monkey.retromusic.glide.GlideApp
|
||||
|
@ -41,7 +40,7 @@ import com.h6ah4i.android.widget.advrecyclerview.swipeable.action.SwipeResultAct
|
|||
import me.zhanghai.android.fastscroll.PopupTextProvider
|
||||
|
||||
class PlayingQueueAdapter(
|
||||
activity: AppCompatActivity,
|
||||
activity: FragmentActivity,
|
||||
dataSet: MutableList<Song>,
|
||||
private var current: Int,
|
||||
itemLayoutRes: Int
|
||||
|
|
|
@ -16,10 +16,9 @@ package code.name.monkey.retromusic.adapter.song
|
|||
|
||||
import android.view.View
|
||||
import androidx.fragment.app.FragmentActivity
|
||||
import code.name.monkey.appthemehelper.ThemeStore
|
||||
import code.name.monkey.retromusic.R
|
||||
import code.name.monkey.retromusic.extensions.applyColor
|
||||
import code.name.monkey.retromusic.extensions.applyOutlineColor
|
||||
import code.name.monkey.retromusic.extensions.accentColor
|
||||
import code.name.monkey.retromusic.extensions.accentOutlineColor
|
||||
import code.name.monkey.retromusic.helper.MusicPlayerRemote
|
||||
import code.name.monkey.retromusic.interfaces.ICabHolder
|
||||
import code.name.monkey.retromusic.model.Song
|
||||
|
@ -45,19 +44,18 @@ class ShuffleButtonSongAdapter(
|
|||
|
||||
override fun onBindViewHolder(holder: SongAdapter.ViewHolder, position: Int) {
|
||||
if (holder.itemViewType == OFFSET_ITEM) {
|
||||
val color = ThemeStore.accentColor(activity)
|
||||
val viewHolder = holder as ViewHolder
|
||||
viewHolder.playAction?.let {
|
||||
it.setOnClickListener {
|
||||
MusicPlayerRemote.openQueue(dataSet, 0, true)
|
||||
}
|
||||
it.applyOutlineColor(color)
|
||||
it.accentOutlineColor()
|
||||
}
|
||||
viewHolder.shuffleAction?.let {
|
||||
it.setOnClickListener {
|
||||
MusicPlayerRemote.openAndShuffleQueue(dataSet, true)
|
||||
}
|
||||
it.applyColor(color)
|
||||
it.accentColor()
|
||||
}
|
||||
} else {
|
||||
super.onBindViewHolder(holder, position - 1)
|
||||
|
|
|
@ -37,12 +37,13 @@ import code.name.monkey.retromusic.helper.MusicPlayerRemote
|
|||
import code.name.monkey.retromusic.helper.SortOrder
|
||||
import code.name.monkey.retromusic.helper.menu.SongMenuHelper
|
||||
import code.name.monkey.retromusic.helper.menu.SongsMenuHelper
|
||||
import code.name.monkey.retromusic.interfaces.ICabCallback
|
||||
import code.name.monkey.retromusic.interfaces.ICabHolder
|
||||
import code.name.monkey.retromusic.model.Song
|
||||
import code.name.monkey.retromusic.util.MusicUtil
|
||||
import code.name.monkey.retromusic.util.PreferenceUtil
|
||||
import code.name.monkey.retromusic.util.RetroUtil
|
||||
import code.name.monkey.retromusic.util.color.MediaNotificationProcessor
|
||||
import com.afollestad.materialcab.MaterialCab
|
||||
import me.zhanghai.android.fastscroll.PopupTextProvider
|
||||
|
||||
/**
|
||||
|
@ -50,7 +51,7 @@ import me.zhanghai.android.fastscroll.PopupTextProvider
|
|||
*/
|
||||
|
||||
open class SongAdapter(
|
||||
protected val activity: FragmentActivity,
|
||||
override val activity: FragmentActivity,
|
||||
var dataSet: MutableList<Song>,
|
||||
protected var itemLayoutRes: Int,
|
||||
ICabHolder: ICabHolder?,
|
||||
|
@ -59,7 +60,7 @@ open class SongAdapter(
|
|||
activity,
|
||||
ICabHolder,
|
||||
R.menu.menu_media_selection
|
||||
), MaterialCab.Callback, PopupTextProvider {
|
||||
), ICabCallback, PopupTextProvider {
|
||||
|
||||
private var showSectionName = true
|
||||
|
||||
|
@ -104,6 +105,10 @@ open class SongAdapter(
|
|||
holder.text?.text = getSongText(song)
|
||||
holder.text2?.text = getSongText(song)
|
||||
loadAlbumCover(song, holder)
|
||||
val landscape = RetroUtil.isLandscape()
|
||||
if ((PreferenceUtil.songGridSize > 2 && !landscape) || (PreferenceUtil.songGridSizeLand > 5 && landscape)) {
|
||||
holder.menu?.isVisible = false
|
||||
}
|
||||
}
|
||||
|
||||
private fun setColors(color: MediaNotificationProcessor, holder: ViewHolder) {
|
||||
|
|
|
@ -35,6 +35,7 @@ public class AutoMediaIDHelper {
|
|||
public static final String MEDIA_ID_MUSICS_BY_GENRE = "__BY_GENRE__";
|
||||
public static final String MEDIA_ID_MUSICS_BY_SHUFFLE = "__BY_SHUFFLE__";
|
||||
public static final String MEDIA_ID_MUSICS_BY_QUEUE = "__BY_QUEUE__";
|
||||
public static final String RECENT_ROOT = "__RECENT__";
|
||||
|
||||
private static final String CATEGORY_SEPARATOR = "__/__";
|
||||
private static final String LEAF_SEPARATOR = "__|__";
|
||||
|
|
|
@ -15,7 +15,6 @@ package code.name.monkey.retromusic.auto
|
|||
|
||||
import android.content.Context
|
||||
import android.content.res.Resources
|
||||
import android.net.Uri
|
||||
import android.support.v4.media.MediaBrowserCompat
|
||||
import code.name.monkey.retromusic.R
|
||||
import code.name.monkey.retromusic.helper.MusicPlayerRemote
|
||||
|
|
|
@ -14,7 +14,6 @@
|
|||
*/
|
||||
package code.name.monkey.retromusic.dialogs
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.app.Dialog
|
||||
import android.content.Context
|
||||
import android.os.Bundle
|
||||
|
@ -42,7 +41,6 @@ import java.io.IOException
|
|||
|
||||
class SongDetailDialog : DialogFragment() {
|
||||
|
||||
@SuppressLint("InflateParams")
|
||||
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
||||
val context: Context = requireContext()
|
||||
val dialogView = LayoutInflater.from(context).inflate(R.layout.dialog_file_details, null)
|
||||
|
|
|
@ -21,7 +21,7 @@ import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper
|
|||
import com.google.android.material.appbar.MaterialToolbar
|
||||
|
||||
fun AppCompatActivity.applyToolbar(toolbar: MaterialToolbar) {
|
||||
toolbar.setBackgroundColor(surfaceColor())
|
||||
//toolbar.setBackgroundColor(surfaceColor())
|
||||
ToolbarContentTintHelper.colorBackButton(toolbar)
|
||||
setSupportActionBar(toolbar)
|
||||
}
|
|
@ -29,6 +29,7 @@ import androidx.annotation.ColorRes
|
|||
import androidx.appcompat.widget.AppCompatImageView
|
||||
import androidx.appcompat.widget.Toolbar
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.graphics.ColorUtils
|
||||
import androidx.core.graphics.drawable.DrawableCompat
|
||||
import androidx.fragment.app.Fragment
|
||||
import code.name.monkey.appthemehelper.ThemeStore
|
||||
|
@ -37,6 +38,7 @@ import code.name.monkey.appthemehelper.util.ColorUtil
|
|||
import code.name.monkey.appthemehelper.util.MaterialValueHelper
|
||||
import code.name.monkey.retromusic.App
|
||||
import code.name.monkey.retromusic.R
|
||||
import code.name.monkey.retromusic.util.PreferenceUtil.materialYou
|
||||
import com.google.android.material.button.MaterialButton
|
||||
import com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton
|
||||
import com.google.android.material.floatingactionbutton.FloatingActionButton
|
||||
|
@ -84,23 +86,32 @@ fun Fragment.resolveColor(@AttrRes attr: Int, fallBackColor: Int = 0) =
|
|||
fun Dialog.resolveColor(@AttrRes attr: Int, fallBackColor: Int = 0) =
|
||||
ATHUtil.resolveColor(context, attr, fallBackColor)
|
||||
|
||||
// Don't apply accent colors if Material You is enabled
|
||||
// Material Components will take care of applying material you colors
|
||||
fun CheckBox.addAccentColor() {
|
||||
if (materialYou) return
|
||||
buttonTintList = ColorStateList.valueOf(ThemeStore.accentColor(context))
|
||||
}
|
||||
|
||||
fun SeekBar.addAccentColor() {
|
||||
if (materialYou) return
|
||||
val colorState = ColorStateList.valueOf(ThemeStore.accentColor(context))
|
||||
progressTintList = colorState
|
||||
thumbTintList = colorState
|
||||
}
|
||||
|
||||
fun Button.accentTextColor() = setTextColor(ThemeStore.accentColor(App.getContext()))
|
||||
fun Button.accentTextColor() {
|
||||
if (materialYou) return
|
||||
setTextColor(ThemeStore.accentColor(App.getContext()))
|
||||
}
|
||||
|
||||
fun MaterialButton.accentBackgroundColor() {
|
||||
if (materialYou) return
|
||||
backgroundTintList = ColorStateList.valueOf(ThemeStore.accentColor(App.getContext()))
|
||||
}
|
||||
|
||||
fun MaterialButton.accentOutlineColor() {
|
||||
if (materialYou) return
|
||||
val color = ThemeStore.accentColor(context)
|
||||
val colorStateList = ColorStateList.valueOf(color)
|
||||
iconTint = colorStateList
|
||||
|
@ -116,6 +127,7 @@ fun SeekBar.applyColor(@ColorInt color: Int) {
|
|||
}
|
||||
|
||||
fun ExtendedFloatingActionButton.accentColor() {
|
||||
if (materialYou) return
|
||||
val color = ThemeStore.accentColor(context)
|
||||
val textColor = MaterialValueHelper.getPrimaryTextColor(context, ColorUtil.isColorLight(color))
|
||||
val colorStateList = ColorStateList.valueOf(color)
|
||||
|
@ -126,12 +138,11 @@ fun ExtendedFloatingActionButton.accentColor() {
|
|||
}
|
||||
|
||||
fun FloatingActionButton.accentColor() {
|
||||
if (materialYou) return
|
||||
val color = ThemeStore.accentColor(context)
|
||||
val textColor = MaterialValueHelper.getPrimaryTextColor(context, ColorUtil.isColorLight(color))
|
||||
val colorStateList = ColorStateList.valueOf(color)
|
||||
val textColorStateList = ColorStateList.valueOf(textColor)
|
||||
backgroundTintList = colorStateList
|
||||
imageTintList = textColorStateList
|
||||
backgroundTintList = ColorStateList.valueOf(color)
|
||||
imageTintList = ColorStateList.valueOf(textColor)
|
||||
}
|
||||
|
||||
fun MaterialButton.applyColor(color: Int) {
|
||||
|
@ -147,15 +158,21 @@ fun MaterialButton.applyColor(color: Int) {
|
|||
iconTint = textColorColorStateList
|
||||
}
|
||||
|
||||
fun MaterialButton.accentColor() = applyColor(ThemeStore.accentColor(context))
|
||||
fun MaterialButton.accentColor() {
|
||||
if (materialYou) return
|
||||
applyColor(ThemeStore.accentColor(context))
|
||||
}
|
||||
|
||||
fun MaterialButton.applyOutlineColor(color: Int) {
|
||||
val textColorColorStateList = ColorStateList.valueOf(color)
|
||||
setTextColor(textColorColorStateList)
|
||||
iconTint = textColorColorStateList
|
||||
val colorStateList = ColorStateList.valueOf(color)
|
||||
iconTint = colorStateList
|
||||
strokeColor = colorStateList
|
||||
setTextColor(colorStateList)
|
||||
rippleColor = colorStateList
|
||||
}
|
||||
|
||||
fun TextInputLayout.accentColor() {
|
||||
if (materialYou) return
|
||||
val accentColor = ThemeStore.accentColor(context)
|
||||
val colorState = ColorStateList.valueOf(accentColor)
|
||||
boxStrokeColor = accentColor
|
||||
|
@ -164,6 +181,7 @@ fun TextInputLayout.accentColor() {
|
|||
}
|
||||
|
||||
fun CircularProgressIndicator.accentColor() {
|
||||
if (materialYou) return
|
||||
val color = ThemeStore.accentColor(context)
|
||||
setIndicatorColor(color)
|
||||
trackColor = ColorUtil.withAlpha(color, 0.2f)
|
||||
|
@ -176,6 +194,21 @@ fun CircularProgressIndicator.applyColor(color: Int) {
|
|||
|
||||
fun AppCompatImageView.accentColor(): Int = ThemeStore.accentColor(context)
|
||||
|
||||
fun TextInputLayout.setTint(background: Boolean = true) {
|
||||
if (materialYou) return
|
||||
val accentColor = ThemeStore.accentColor(context)
|
||||
val colorState = ColorStateList.valueOf(accentColor)
|
||||
|
||||
if (background) {
|
||||
backgroundTintList = colorState
|
||||
defaultHintTextColor = colorState
|
||||
} else {
|
||||
boxStrokeColor = accentColor
|
||||
defaultHintTextColor = colorState
|
||||
isHintAnimationEnabled = true
|
||||
}
|
||||
}
|
||||
|
||||
@CheckResult
|
||||
fun Drawable.tint(@ColorInt color: Int): Drawable {
|
||||
val tintedDrawable = DrawableCompat.wrap(this).mutate()
|
||||
|
@ -191,3 +224,15 @@ fun Drawable.tint(context: Context, @ColorRes color: Int): Drawable =
|
|||
fun Context.getColorCompat(@ColorRes colorRes: Int): Int {
|
||||
return ContextCompat.getColor(this, colorRes)
|
||||
}
|
||||
|
||||
@ColorInt
|
||||
fun Context.darkAccentColor(): Int {
|
||||
return ColorUtils.blendARGB(
|
||||
accentColor(),
|
||||
surfaceColor(),
|
||||
if (surfaceColor().isColorLight) 0.96f else 0.975f
|
||||
)
|
||||
}
|
||||
|
||||
inline val @receiver:ColorInt Int.isColorLight
|
||||
get() = ColorUtil.isColorLight(this)
|
|
@ -19,16 +19,14 @@ import android.content.res.Configuration
|
|||
import android.graphics.drawable.Drawable
|
||||
import android.os.PowerManager
|
||||
import android.widget.Toast
|
||||
import androidx.annotation.DrawableRes
|
||||
import androidx.annotation.IdRes
|
||||
import androidx.annotation.IntegerRes
|
||||
import androidx.annotation.StringRes
|
||||
import androidx.annotation.*
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.appcompat.content.res.AppCompatResources
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.fragment.app.FragmentManager
|
||||
import androidx.navigation.fragment.NavHostFragment
|
||||
import code.name.monkey.retromusic.util.PreferenceUtil
|
||||
import com.google.android.material.appbar.MaterialToolbar
|
||||
|
||||
fun Fragment.getIntRes(@IntegerRes int: Int): Int {
|
||||
return resources.getInteger(int)
|
||||
|
@ -97,3 +95,11 @@ fun Context.getDrawableCompat(@DrawableRes drawableRes: Int): Drawable {
|
|||
fun Fragment.getDrawableCompat(@DrawableRes drawableRes: Int): Drawable {
|
||||
return AppCompatResources.getDrawable(requireContext(), drawableRes)!!
|
||||
}
|
||||
|
||||
fun Fragment.applyToolbar(toolbar: MaterialToolbar) {
|
||||
(requireActivity() as AppCompatActivity).applyToolbar(toolbar)
|
||||
}
|
||||
|
||||
fun Fragment.dip(@DimenRes id: Int): Int {
|
||||
return resources.getDimensionPixelSize(id)
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -14,19 +14,25 @@
|
|||
*/
|
||||
package code.name.monkey.retromusic.extensions
|
||||
|
||||
import android.animation.Animator
|
||||
import android.animation.ObjectAnimator
|
||||
import android.content.Context
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.view.ViewGroup.MarginLayoutParams
|
||||
import android.view.ViewTreeObserver
|
||||
import android.view.inputmethod.InputMethodManager
|
||||
import android.widget.EditText
|
||||
import androidx.annotation.LayoutRes
|
||||
import androidx.core.animation.doOnEnd
|
||||
import androidx.core.animation.doOnStart
|
||||
import androidx.core.view.*
|
||||
import code.name.monkey.appthemehelper.ThemeStore
|
||||
import code.name.monkey.appthemehelper.util.TintHelper
|
||||
import code.name.monkey.retromusic.util.PreferenceUtil
|
||||
import code.name.monkey.retromusic.util.RetroUtil
|
||||
import com.afollestad.materialdialogs.utils.MDUtil.updatePadding
|
||||
import com.google.android.material.bottomsheet.BottomSheetBehavior
|
||||
import com.google.android.material.imageview.ShapeableImageView
|
||||
import com.google.android.material.shape.ShapeAppearanceModel
|
||||
|
@ -51,12 +57,13 @@ fun View.hidden() {
|
|||
fun View.showOrHide(show: Boolean) = if (show) show() else hide()
|
||||
|
||||
fun EditText.appHandleColor(): EditText {
|
||||
if (PreferenceUtil.materialYou) return this
|
||||
TintHelper.colorHandles(this, ThemeStore.accentColor(context))
|
||||
return this
|
||||
}
|
||||
|
||||
fun View.translateYAnimate(value: Float) {
|
||||
ObjectAnimator.ofFloat(this, "translationY", value)
|
||||
fun View.translateYAnimate(value: Float): Animator {
|
||||
return ObjectAnimator.ofFloat(this, "translationY", value)
|
||||
.apply {
|
||||
duration = 300
|
||||
doOnStart {
|
||||
|
@ -122,4 +129,114 @@ fun ShapeableImageView.setCircleShape(boolean: Boolean) {
|
|||
val radius = width / 2f
|
||||
shapeAppearanceModel = ShapeAppearanceModel().withCornerSize(radius)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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
|
||||
)
|
||||
|
|
|
@ -19,10 +19,12 @@ import androidx.lifecycle.*
|
|||
import code.name.monkey.retromusic.*
|
||||
import code.name.monkey.retromusic.db.*
|
||||
import code.name.monkey.retromusic.fragments.ReloadType.*
|
||||
import code.name.monkey.retromusic.fragments.search.Filter
|
||||
import code.name.monkey.retromusic.helper.MusicPlayerRemote
|
||||
import code.name.monkey.retromusic.interfaces.IMusicServiceEventListener
|
||||
import code.name.monkey.retromusic.model.*
|
||||
import code.name.monkey.retromusic.repository.RealRepository
|
||||
import code.name.monkey.retromusic.util.DensityUtil
|
||||
import code.name.monkey.retromusic.util.PreferenceUtil
|
||||
import kotlinx.coroutines.Dispatchers.IO
|
||||
import kotlinx.coroutines.launch
|
||||
|
@ -40,6 +42,7 @@ class LibraryViewModel(
|
|||
private val legacyPlaylists = MutableLiveData<List<Playlist>>()
|
||||
private val genres = MutableLiveData<List<Genre>>()
|
||||
private val searchResults = MutableLiveData<List<Any>>()
|
||||
private val fabMargin = MutableLiveData(0)
|
||||
val paletteColor: LiveData<Int> = _paletteColor
|
||||
|
||||
init {
|
||||
|
@ -85,6 +88,10 @@ class LibraryViewModel(
|
|||
return home
|
||||
}
|
||||
|
||||
fun getFabMargin(): LiveData<Int> {
|
||||
return fabMargin
|
||||
}
|
||||
|
||||
private fun fetchSongs() {
|
||||
viewModelScope.launch(IO) {
|
||||
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) {
|
||||
val result = repository.search(query, filters)
|
||||
val result = repository.search(query, filter)
|
||||
searchResults.postValue(result)
|
||||
}
|
||||
}
|
||||
|
@ -328,6 +335,14 @@ class LibraryViewModel(
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun setFabMargin(bottomMargin: Int) {
|
||||
fabMargin.postValue(
|
||||
// Normal Margin
|
||||
DensityUtil.dip2px(App.getContext(), 16F) +
|
||||
bottomMargin
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
enum class ReloadType {
|
||||
|
|
|
@ -20,6 +20,7 @@ import android.net.Uri
|
|||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import androidx.core.app.ShareCompat
|
||||
import androidx.core.view.updatePadding
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.recyclerview.widget.DefaultItemAnimator
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
|
@ -30,6 +31,7 @@ import code.name.monkey.retromusic.adapter.ContributorAdapter
|
|||
import code.name.monkey.retromusic.databinding.FragmentAboutBinding
|
||||
import code.name.monkey.retromusic.fragments.LibraryViewModel
|
||||
import code.name.monkey.retromusic.util.NavigationUtil
|
||||
import code.name.monkey.retromusic.util.RetroUtil
|
||||
import org.koin.androidx.viewmodel.ext.android.sharedViewModel
|
||||
|
||||
class AboutFragment : Fragment(R.layout.fragment_about), View.OnClickListener {
|
||||
|
@ -43,6 +45,12 @@ class AboutFragment : Fragment(R.layout.fragment_about), View.OnClickListener {
|
|||
binding.aboutContent.cardOther.version.setSummary(getAppVersion())
|
||||
setUpView()
|
||||
loadContributors()
|
||||
// This is a workaround as CollapsingToolbarLayout consumes insets and
|
||||
// insets are not passed to child views
|
||||
// https://github.com/material-components/material-components-android/issues/1310
|
||||
if (!RetroUtil.isLandscape()) {
|
||||
binding.root.updatePadding(bottom = RetroUtil.getNavigationBarHeight())
|
||||
}
|
||||
}
|
||||
|
||||
private fun openUrl(url: String) {
|
||||
|
|
|
@ -57,6 +57,7 @@ import code.name.monkey.retromusic.helper.SortOrder.AlbumSongSortOrder.Companion
|
|||
import code.name.monkey.retromusic.helper.SortOrder.AlbumSongSortOrder.Companion.SONG_TRACK_LIST
|
||||
import code.name.monkey.retromusic.helper.SortOrder.AlbumSongSortOrder.Companion.SONG_Z_A
|
||||
import code.name.monkey.retromusic.interfaces.IAlbumClickListener
|
||||
import code.name.monkey.retromusic.interfaces.ICabCallback
|
||||
import code.name.monkey.retromusic.interfaces.ICabHolder
|
||||
import code.name.monkey.retromusic.model.Album
|
||||
import code.name.monkey.retromusic.model.Artist
|
||||
|
@ -68,7 +69,11 @@ import code.name.monkey.retromusic.util.PreferenceUtil
|
|||
import code.name.monkey.retromusic.util.RetroColorUtil
|
||||
import code.name.monkey.retromusic.util.RetroUtil
|
||||
import code.name.monkey.retromusic.util.color.MediaNotificationProcessor
|
||||
import com.afollestad.materialcab.MaterialCab
|
||||
import com.afollestad.materialcab.attached.AttachedCab
|
||||
import com.afollestad.materialcab.attached.destroy
|
||||
import com.afollestad.materialcab.attached.isActive
|
||||
import com.afollestad.materialcab.createCab
|
||||
import com.google.android.material.shape.MaterialShapeDrawable
|
||||
import com.google.android.material.transition.MaterialArcMotion
|
||||
import com.google.android.material.transition.MaterialContainerTransform
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
|
@ -101,7 +106,6 @@ class AlbumDetailsFragment : AbsMainActivityFragment(R.layout.fragment_album_det
|
|||
super.onCreate(savedInstanceState)
|
||||
sharedElementEnterTransition = MaterialContainerTransform().apply {
|
||||
drawingViewId = R.id.fragment_container
|
||||
duration = 300L
|
||||
scrimColor = Color.TRANSPARENT
|
||||
setAllContainerColors(requireContext().resolveColor(R.attr.colorSurface))
|
||||
setPathMotion(MaterialArcMotion())
|
||||
|
@ -176,6 +180,8 @@ class AlbumDetailsFragment : AbsMainActivityFragment(R.layout.fragment_album_det
|
|||
requireActivity().onBackPressed()
|
||||
}
|
||||
}
|
||||
binding.appBarLayout?.statusBarForeground =
|
||||
MaterialShapeDrawable.createWithElevationOverlay(requireContext())
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
|
@ -228,9 +234,10 @@ class AlbumDetailsFragment : AbsMainActivityFragment(R.layout.fragment_album_det
|
|||
loadAlbumCover(album)
|
||||
simpleSongAdapter.swapDataSet(album.songs)
|
||||
if (albumArtistExists) {
|
||||
detailsViewModel.getAlbumArtist(album.albumArtist.toString()).observe(viewLifecycleOwner, {
|
||||
loadArtistImage(it)
|
||||
})
|
||||
detailsViewModel.getAlbumArtist(album.albumArtist.toString())
|
||||
.observe(viewLifecycleOwner, {
|
||||
loadArtistImage(it)
|
||||
})
|
||||
} else {
|
||||
detailsViewModel.getArtist(album.artistId).observe(viewLifecycleOwner, {
|
||||
loadArtistImage(it)
|
||||
|
@ -302,7 +309,12 @@ class AlbumDetailsFragment : AbsMainActivityFragment(R.layout.fragment_album_det
|
|||
})
|
||||
GlideApp.with(requireContext()).asBitmapPalette().artistImageOptions(artist)
|
||||
//.forceDownload(PreferenceUtil.isAllowedToDownloadMetadata())
|
||||
.load(RetroGlideExtension.getArtistModel(artist, PreferenceUtil.isAllowedToDownloadMetadata()))
|
||||
.load(
|
||||
RetroGlideExtension.getArtistModel(
|
||||
artist,
|
||||
PreferenceUtil.isAllowedToDownloadMetadata()
|
||||
)
|
||||
)
|
||||
.dontAnimate()
|
||||
.dontTransform()
|
||||
.into(object : RetroMusicColoredTarget(binding.artistImage) {
|
||||
|
@ -312,7 +324,8 @@ class AlbumDetailsFragment : AbsMainActivityFragment(R.layout.fragment_album_det
|
|||
}
|
||||
|
||||
private fun loadAlbumCover(album: Album) {
|
||||
GlideApp.with(requireContext()).asBitmapPalette().albumCoverOptions(album.safeGetFirstSong())
|
||||
GlideApp.with(requireContext()).asBitmapPalette()
|
||||
.albumCoverOptions(album.safeGetFirstSong())
|
||||
//.checkIgnoreMediaStore()
|
||||
.load(RetroGlideExtension.getSongModel(album.safeGetFirstSong()))
|
||||
.into(object : SingleColorTarget(binding.image) {
|
||||
|
@ -323,8 +336,10 @@ class AlbumDetailsFragment : AbsMainActivityFragment(R.layout.fragment_album_det
|
|||
}
|
||||
|
||||
private fun setColors(color: Int) {
|
||||
binding.fragmentAlbumContent.shuffleAction.applyColor(color)
|
||||
binding.fragmentAlbumContent.playAction.applyOutlineColor(color)
|
||||
_binding?.fragmentAlbumContent?.apply {
|
||||
shuffleAction.applyColor(color)
|
||||
playAction.applyOutlineColor(color)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onAlbumClick(albumId: Long, view: View) {
|
||||
|
@ -450,28 +465,34 @@ class AlbumDetailsFragment : AbsMainActivityFragment(R.layout.fragment_album_det
|
|||
|
||||
private fun handleBackPress(): Boolean {
|
||||
cab?.let {
|
||||
if (it.isActive) {
|
||||
it.finish()
|
||||
if (it.isActive()) {
|
||||
it.destroy()
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
private var cab: MaterialCab? = null
|
||||
private var cab: AttachedCab? = null
|
||||
|
||||
override fun openCab(menuRes: Int, callback: MaterialCab.Callback): MaterialCab {
|
||||
override fun openCab(menuRes: Int, callback: ICabCallback): AttachedCab {
|
||||
cab?.let {
|
||||
if (it.isActive) {
|
||||
it.finish()
|
||||
if (it.isActive()) {
|
||||
it.destroy()
|
||||
}
|
||||
}
|
||||
cab = MaterialCab(mainActivity, R.id.cab_stub)
|
||||
.setMenu(menuRes)
|
||||
.setCloseDrawableRes(R.drawable.ic_close)
|
||||
.setBackgroundColor(RetroColorUtil.shiftBackgroundColorForLightText(surfaceColor()))
|
||||
.start(callback)
|
||||
return cab as MaterialCab
|
||||
cab = createCab(R.id.toolbar_container) {
|
||||
menu(menuRes)
|
||||
closeDrawable(R.drawable.ic_close)
|
||||
backgroundColor(literal = RetroColorUtil.shiftBackgroundColor(surfaceColor()))
|
||||
slideDown()
|
||||
onCreate { cab, menu -> callback.onCabCreated(cab, menu) }
|
||||
onSelection {
|
||||
callback.onCabItemClicked(it)
|
||||
}
|
||||
onDestroy { callback.onCabFinished(it) }
|
||||
}
|
||||
return cab as AttachedCab
|
||||
}
|
||||
|
||||
override fun onDestroyView() {
|
||||
|
|
|
@ -54,7 +54,7 @@ class AlbumDetailsViewModel(
|
|||
|
||||
fun getAlbumInfo(album: Album): LiveData<Result<LastFmAlbum>> = liveData {
|
||||
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) {
|
||||
|
|
|
@ -24,16 +24,23 @@ import androidx.recyclerview.widget.GridLayoutManager
|
|||
import code.name.monkey.retromusic.EXTRA_ALBUM_ID
|
||||
import code.name.monkey.retromusic.R
|
||||
import code.name.monkey.retromusic.adapter.album.AlbumAdapter
|
||||
import code.name.monkey.retromusic.extensions.navigate
|
||||
import code.name.monkey.retromusic.extensions.surfaceColor
|
||||
import code.name.monkey.retromusic.fragments.ReloadType
|
||||
import code.name.monkey.retromusic.fragments.base.AbsRecyclerViewCustomGridSizeFragment
|
||||
import code.name.monkey.retromusic.helper.MusicPlayerRemote
|
||||
import code.name.monkey.retromusic.helper.SortOrder.AlbumSortOrder
|
||||
import code.name.monkey.retromusic.interfaces.IAlbumClickListener
|
||||
import code.name.monkey.retromusic.interfaces.ICabCallback
|
||||
import code.name.monkey.retromusic.interfaces.ICabHolder
|
||||
import code.name.monkey.retromusic.service.MusicService
|
||||
import code.name.monkey.retromusic.util.PreferenceUtil
|
||||
import code.name.monkey.retromusic.util.RetroColorUtil
|
||||
import code.name.monkey.retromusic.util.RetroUtil
|
||||
import com.afollestad.materialcab.MaterialCab
|
||||
import com.afollestad.materialcab.attached.AttachedCab
|
||||
import com.afollestad.materialcab.attached.destroy
|
||||
import com.afollestad.materialcab.attached.isActive
|
||||
import com.afollestad.materialcab.createCab
|
||||
import com.google.android.gms.cast.framework.CastButtonFactory
|
||||
|
||||
class AlbumsFragment : AbsRecyclerViewCustomGridSizeFragment<AlbumAdapter, GridLayoutManager>(),
|
||||
|
@ -50,7 +57,7 @@ class AlbumsFragment : AbsRecyclerViewCustomGridSizeFragment<AlbumAdapter, GridL
|
|||
requireActivity().onBackPressedDispatcher.addCallback(viewLifecycleOwner) {
|
||||
if (!handleBackPress()) {
|
||||
remove()
|
||||
requireActivity().onBackPressed()
|
||||
mainActivity.finish()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -61,6 +68,20 @@ class AlbumsFragment : AbsRecyclerViewCustomGridSizeFragment<AlbumAdapter, GridL
|
|||
override val emptyMessage: Int
|
||||
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 {
|
||||
return GridLayoutManager(requireActivity(), getGridSize())
|
||||
}
|
||||
|
@ -325,28 +346,34 @@ class AlbumsFragment : AbsRecyclerViewCustomGridSizeFragment<AlbumAdapter, GridL
|
|||
|
||||
private fun handleBackPress(): Boolean {
|
||||
cab?.let {
|
||||
if (it.isActive) {
|
||||
it.finish()
|
||||
if (it.isActive()) {
|
||||
it.destroy()
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
private var cab: MaterialCab? = null
|
||||
private var cab: AttachedCab? = null
|
||||
|
||||
override fun openCab(menuRes: Int, callback: MaterialCab.Callback): MaterialCab {
|
||||
override fun openCab(menuRes: Int, callback: ICabCallback): AttachedCab {
|
||||
cab?.let {
|
||||
println("Cab")
|
||||
if (it.isActive) {
|
||||
it.finish()
|
||||
if (it.isActive()) {
|
||||
it.destroy()
|
||||
}
|
||||
}
|
||||
cab = MaterialCab(mainActivity, R.id.cab_stub)
|
||||
.setMenu(menuRes)
|
||||
.setCloseDrawableRes(R.drawable.ic_close)
|
||||
.setBackgroundColor(RetroColorUtil.shiftBackgroundColorForLightText(surfaceColor()))
|
||||
.start(callback)
|
||||
return cab as MaterialCab
|
||||
cab = createCab(R.id.toolbar_container) {
|
||||
menu(menuRes)
|
||||
closeDrawable(R.drawable.ic_close)
|
||||
backgroundColor(literal = RetroColorUtil.shiftBackgroundColor(surfaceColor()))
|
||||
slideDown()
|
||||
onCreate { cab, menu -> callback.onCabCreated(cab, menu) }
|
||||
onSelection {
|
||||
callback.onCabItemClicked(it)
|
||||
}
|
||||
onDestroy { callback.onCabFinished(it) }
|
||||
}
|
||||
return cab as AttachedCab
|
||||
}
|
||||
}
|
||||
|
|
|
@ -33,6 +33,7 @@ import code.name.monkey.retromusic.glide.RetroGlideExtension
|
|||
import code.name.monkey.retromusic.glide.SingleColorTarget
|
||||
import code.name.monkey.retromusic.helper.MusicPlayerRemote
|
||||
import code.name.monkey.retromusic.interfaces.IAlbumClickListener
|
||||
import code.name.monkey.retromusic.interfaces.ICabCallback
|
||||
import code.name.monkey.retromusic.interfaces.ICabHolder
|
||||
import code.name.monkey.retromusic.model.Artist
|
||||
import code.name.monkey.retromusic.network.Result
|
||||
|
@ -42,7 +43,11 @@ import code.name.monkey.retromusic.util.CustomArtistImageUtil
|
|||
import code.name.monkey.retromusic.util.MusicUtil
|
||||
import code.name.monkey.retromusic.util.RetroColorUtil
|
||||
import code.name.monkey.retromusic.util.RetroUtil
|
||||
import com.afollestad.materialcab.MaterialCab
|
||||
import com.afollestad.materialcab.attached.AttachedCab
|
||||
import com.afollestad.materialcab.attached.destroy
|
||||
import com.afollestad.materialcab.attached.isActive
|
||||
import com.afollestad.materialcab.createCab
|
||||
import com.google.android.material.shape.MaterialShapeDrawable
|
||||
import com.google.android.material.transition.MaterialContainerTransform
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
|
@ -70,7 +75,6 @@ abstract class AbsArtistDetailsFragment : AbsMainActivityFragment(R.layout.fragm
|
|||
super.onCreate(savedInstanceState)
|
||||
sharedElementEnterTransition = MaterialContainerTransform().apply {
|
||||
drawingViewId = R.id.fragment_container
|
||||
duration = 300L
|
||||
scrimColor = Color.TRANSPARENT
|
||||
setAllContainerColors(requireContext().resolveColor(R.attr.colorSurface))
|
||||
}
|
||||
|
@ -121,6 +125,8 @@ abstract class AbsArtistDetailsFragment : AbsMainActivityFragment(R.layout.fragm
|
|||
requireActivity().onBackPressed()
|
||||
}
|
||||
}
|
||||
binding.appBarLayout?.statusBarForeground =
|
||||
MaterialShapeDrawable.createWithElevationOverlay(requireContext())
|
||||
}
|
||||
|
||||
private fun setupRecyclerView() {
|
||||
|
@ -306,28 +312,34 @@ abstract class AbsArtistDetailsFragment : AbsMainActivityFragment(R.layout.fragm
|
|||
|
||||
private fun handleBackPress(): Boolean {
|
||||
cab?.let {
|
||||
if (it.isActive) {
|
||||
it.finish()
|
||||
if (it.isActive()) {
|
||||
it.destroy()
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
private var cab: MaterialCab? = null
|
||||
private var cab: AttachedCab? = null
|
||||
|
||||
override fun openCab(menuRes: Int, callback: MaterialCab.Callback): MaterialCab {
|
||||
override fun openCab(menuRes: Int, callback: ICabCallback): AttachedCab {
|
||||
cab?.let {
|
||||
if (it.isActive) {
|
||||
it.finish()
|
||||
if (it.isActive()) {
|
||||
it.destroy()
|
||||
}
|
||||
}
|
||||
cab = MaterialCab(mainActivity, R.id.cab_stub)
|
||||
.setMenu(menuRes)
|
||||
.setCloseDrawableRes(R.drawable.ic_close)
|
||||
.setBackgroundColor(RetroColorUtil.shiftBackgroundColorForLightText(surfaceColor()))
|
||||
.start(callback)
|
||||
return cab as MaterialCab
|
||||
cab = createCab(R.id.toolbar_container) {
|
||||
menu(menuRes)
|
||||
closeDrawable(R.drawable.ic_close)
|
||||
backgroundColor(literal = RetroColorUtil.shiftBackgroundColor(surfaceColor()))
|
||||
slideDown()
|
||||
onCreate { cab, menu -> callback.onCabCreated(cab, menu) }
|
||||
onSelection {
|
||||
callback.onCabItemClicked(it)
|
||||
}
|
||||
onDestroy { callback.onCabFinished(it) }
|
||||
}
|
||||
return cab as AttachedCab
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -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() {}
|
||||
}
|
|
@ -25,17 +25,24 @@ import code.name.monkey.retromusic.EXTRA_ARTIST_ID
|
|||
import code.name.monkey.retromusic.EXTRA_ARTIST_NAME
|
||||
import code.name.monkey.retromusic.R
|
||||
import code.name.monkey.retromusic.adapter.artist.ArtistAdapter
|
||||
import code.name.monkey.retromusic.extensions.navigate
|
||||
import code.name.monkey.retromusic.extensions.surfaceColor
|
||||
import code.name.monkey.retromusic.fragments.ReloadType
|
||||
import code.name.monkey.retromusic.fragments.base.AbsRecyclerViewCustomGridSizeFragment
|
||||
import code.name.monkey.retromusic.helper.MusicPlayerRemote
|
||||
import code.name.monkey.retromusic.helper.SortOrder.ArtistSortOrder
|
||||
import code.name.monkey.retromusic.interfaces.IAlbumArtistClickListener
|
||||
import code.name.monkey.retromusic.interfaces.IArtistClickListener
|
||||
import code.name.monkey.retromusic.interfaces.ICabCallback
|
||||
import code.name.monkey.retromusic.interfaces.ICabHolder
|
||||
import code.name.monkey.retromusic.service.MusicService
|
||||
import code.name.monkey.retromusic.util.PreferenceUtil
|
||||
import code.name.monkey.retromusic.util.RetroColorUtil
|
||||
import code.name.monkey.retromusic.util.RetroUtil
|
||||
import com.afollestad.materialcab.MaterialCab
|
||||
import com.afollestad.materialcab.attached.AttachedCab
|
||||
import com.afollestad.materialcab.attached.destroy
|
||||
import com.afollestad.materialcab.attached.isActive
|
||||
import com.afollestad.materialcab.createCab
|
||||
import com.google.android.gms.cast.framework.CastButtonFactory
|
||||
|
||||
class ArtistsFragment : AbsRecyclerViewCustomGridSizeFragment<ArtistAdapter, GridLayoutManager>(),
|
||||
|
@ -51,16 +58,31 @@ class ArtistsFragment : AbsRecyclerViewCustomGridSizeFragment<ArtistAdapter, Gri
|
|||
requireActivity().onBackPressedDispatcher.addCallback(viewLifecycleOwner) {
|
||||
if (!handleBackPress()) {
|
||||
remove()
|
||||
requireActivity().onBackPressed()
|
||||
mainActivity.finish()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override val titleRes: Int
|
||||
get() = R.string.artists
|
||||
|
||||
override val emptyMessage: Int
|
||||
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) {
|
||||
libraryViewModel.forceReload(ReloadType.Artists)
|
||||
}
|
||||
|
@ -324,28 +346,34 @@ class ArtistsFragment : AbsRecyclerViewCustomGridSizeFragment<ArtistAdapter, Gri
|
|||
|
||||
private fun handleBackPress(): Boolean {
|
||||
cab?.let {
|
||||
if (it.isActive) {
|
||||
it.finish()
|
||||
if (it.isActive()) {
|
||||
it.destroy()
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
private var cab: MaterialCab? = null
|
||||
private var cab: AttachedCab? = null
|
||||
|
||||
override fun openCab(menuRes: Int, callback: MaterialCab.Callback): MaterialCab {
|
||||
override fun openCab(menuRes: Int, callback: ICabCallback): AttachedCab {
|
||||
cab?.let {
|
||||
if (it.isActive) {
|
||||
it.finish()
|
||||
if (it.isActive()) {
|
||||
it.destroy()
|
||||
}
|
||||
}
|
||||
cab = MaterialCab(mainActivity, R.id.cab_stub)
|
||||
.setMenu(menuRes)
|
||||
.setCloseDrawableRes(R.drawable.ic_close)
|
||||
.setBackgroundColor(RetroColorUtil.shiftBackgroundColorForLightText(surfaceColor()))
|
||||
.start(callback)
|
||||
return cab as MaterialCab
|
||||
cab = createCab(R.id.toolbar_container) {
|
||||
menu(menuRes)
|
||||
closeDrawable(R.drawable.ic_close)
|
||||
backgroundColor(literal = RetroColorUtil.shiftBackgroundColor(surfaceColor()))
|
||||
slideDown()
|
||||
onCreate { cab, menu -> callback.onCabCreated(cab, menu) }
|
||||
onSelection {
|
||||
callback.onCabItemClicked(it)
|
||||
}
|
||||
onDestroy { callback.onCabFinished(it) }
|
||||
}
|
||||
return cab as AttachedCab
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
}
|
|
@ -34,10 +34,7 @@ abstract class AbsMainActivityFragment(@LayoutRes layout: Int) : AbsMusicService
|
|||
override fun onActivityCreated(savedInstanceState: Bundle?) {
|
||||
super.onActivityCreated(savedInstanceState)
|
||||
setHasOptionsMenu(true)
|
||||
mainActivity.setNavigationbarColorAuto()
|
||||
mainActivity.setLightNavigationBar(true)
|
||||
mainActivity.setTaskDescriptionColorAuto()
|
||||
mainActivity.hideStatusBar()
|
||||
}
|
||||
|
||||
private fun setStatusBarColor(view: View, color: Int) {
|
||||
|
|
|
@ -20,7 +20,7 @@ import android.view.animation.AccelerateInterpolator
|
|||
import android.view.animation.DecelerateInterpolator
|
||||
import androidx.annotation.LayoutRes
|
||||
import code.name.monkey.retromusic.R
|
||||
import code.name.monkey.retromusic.fragments.VolumeFragment
|
||||
import code.name.monkey.retromusic.fragments.other.VolumeFragment
|
||||
import code.name.monkey.retromusic.helper.MusicProgressViewUpdateHelper
|
||||
import code.name.monkey.retromusic.util.PreferenceUtil
|
||||
import code.name.monkey.retromusic.util.color.MediaNotificationProcessor
|
||||
|
|
|
@ -131,7 +131,7 @@ abstract class AbsPlayerFragment(@LayoutRes layout: Int) : AbsMainActivityFragme
|
|||
}
|
||||
R.id.action_go_to_album -> {
|
||||
//Hide Bottom Bar First, else Bottom Sheet doesn't collapse fully
|
||||
mainActivity.setBottomBarVisibility(false)
|
||||
mainActivity.setBottomNavVisibility(false)
|
||||
mainActivity.collapsePanel()
|
||||
requireActivity().findNavController(R.id.fragment_container).navigate(
|
||||
R.id.albumDetailsFragment,
|
||||
|
@ -144,7 +144,10 @@ abstract class AbsPlayerFragment(@LayoutRes layout: Int) : AbsMainActivityFragme
|
|||
return true
|
||||
}
|
||||
R.id.now_playing -> {
|
||||
NavigationUtil.goToPlayingQueue(requireActivity())
|
||||
requireActivity().findNavController(R.id.fragment_container).navigate(
|
||||
R.id.playing_queue_fragment,
|
||||
null
|
||||
)
|
||||
return true
|
||||
}
|
||||
R.id.action_show_lyrics -> {
|
||||
|
@ -325,6 +328,7 @@ abstract class AbsPlayerFragment(@LayoutRes layout: Int) : AbsMainActivityFragme
|
|||
requireView()
|
||||
)
|
||||
)
|
||||
playerToolbar()?.menu?.findItem(R.id.action_toggle_lyrics)?.let { showLyricsIcon(it) }
|
||||
}
|
||||
|
||||
class SwipeDetector(val context: Context, val viewPager: ViewPager?, val view: View) :
|
||||
|
@ -383,7 +387,7 @@ fun goToArtist(activity: Activity) {
|
|||
currentFragment(R.id.fragment_container)?.exitTransition = null
|
||||
|
||||
//Hide Bottom Bar First, else Bottom Sheet doesn't collapse fully
|
||||
setBottomBarVisibility(false)
|
||||
setBottomNavVisibility(false)
|
||||
if (getBottomSheetBehavior().state == BottomSheetBehavior.STATE_EXPANDED) {
|
||||
collapsePanel()
|
||||
}
|
||||
|
@ -402,7 +406,7 @@ fun goToAlbum(activity: Activity) {
|
|||
currentFragment(R.id.fragment_container)?.exitTransition = null
|
||||
|
||||
//Hide Bottom Bar First, else Bottom Sheet doesn't collapse fully
|
||||
setBottomBarVisibility(false)
|
||||
setBottomNavVisibility(false)
|
||||
if (getBottomSheetBehavior().state == BottomSheetBehavior.STATE_EXPANDED) {
|
||||
collapsePanel()
|
||||
}
|
||||
|
|
|
@ -86,7 +86,7 @@ abstract class AbsRecyclerViewCustomGridSizeFragment<A : RecyclerView.Adapter<*>
|
|||
} else {
|
||||
saveGridSize(gridSize)
|
||||
}
|
||||
recyclerView().isVisible = false
|
||||
recyclerView.isVisible = false
|
||||
invalidateLayoutManager()
|
||||
// only recreate the adapter and layout manager if the layout currentLayoutRes has changed
|
||||
if (oldLayoutRes != itemLayoutRes()) {
|
||||
|
@ -95,10 +95,10 @@ abstract class AbsRecyclerViewCustomGridSizeFragment<A : RecyclerView.Adapter<*>
|
|||
setGridSize(gridSize)
|
||||
}
|
||||
val transition = MaterialFade().apply {
|
||||
addTarget(recyclerView())
|
||||
addTarget(recyclerView)
|
||||
}
|
||||
TransitionManager.beginDelayedTransition(getContainer(), transition)
|
||||
recyclerView().isVisible = true
|
||||
TransitionManager.beginDelayedTransition(container, transition)
|
||||
recyclerView.isVisible = true
|
||||
}
|
||||
|
||||
protected abstract fun setGridSize(gridSize: Int)
|
||||
|
|
|
@ -18,8 +18,10 @@ import android.os.Bundle
|
|||
import android.view.*
|
||||
import androidx.annotation.NonNull
|
||||
import androidx.annotation.StringRes
|
||||
import androidx.coordinatorlayout.widget.CoordinatorLayout
|
||||
import androidx.appcompat.widget.Toolbar
|
||||
import androidx.core.view.doOnPreDraw
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.core.view.updateLayoutParams
|
||||
import androidx.core.view.updatePadding
|
||||
import androidx.navigation.fragment.findNavController
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
|
@ -29,9 +31,12 @@ import code.name.monkey.retromusic.R
|
|||
import code.name.monkey.retromusic.databinding.FragmentMainRecyclerBinding
|
||||
import code.name.monkey.retromusic.dialogs.CreatePlaylistDialog
|
||||
import code.name.monkey.retromusic.dialogs.ImportPlaylistDialog
|
||||
import code.name.monkey.retromusic.extensions.accentColor
|
||||
import code.name.monkey.retromusic.extensions.dip
|
||||
import code.name.monkey.retromusic.extensions.drawNextToNavbar
|
||||
import code.name.monkey.retromusic.helper.MusicPlayerRemote
|
||||
import code.name.monkey.retromusic.util.DensityUtil
|
||||
import code.name.monkey.retromusic.util.ThemedFastScroller.create
|
||||
import com.google.android.material.shape.MaterialShapeDrawable
|
||||
import com.google.android.material.transition.MaterialFadeThrough
|
||||
import com.google.android.material.transition.MaterialSharedAxis
|
||||
import me.zhanghai.android.fastscroll.FastScroller
|
||||
|
@ -44,21 +49,57 @@ abstract class AbsRecyclerViewFragment<A : RecyclerView.Adapter<*>, LM : Recycle
|
|||
private val binding get() = _binding!!
|
||||
protected var adapter: A? = null
|
||||
protected var layoutManager: LM? = null
|
||||
val shuffleButton get() = binding.shuffleButton
|
||||
abstract val isShuffleVisible: Boolean
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
_binding = FragmentMainRecyclerBinding.bind(view)
|
||||
enterTransition = MaterialFadeThrough()
|
||||
exitTransition = MaterialFadeThrough()
|
||||
postponeEnterTransition()
|
||||
view.doOnPreDraw { startPostponedEnterTransition() }
|
||||
|
||||
enterTransition = MaterialFadeThrough().apply {
|
||||
addTarget(binding.recyclerView)
|
||||
}
|
||||
mainActivity.setSupportActionBar(binding.toolbar)
|
||||
mainActivity.supportActionBar?.title = null
|
||||
initLayoutManager()
|
||||
initAdapter()
|
||||
setUpRecyclerView()
|
||||
setupToolbar()
|
||||
// Add listeners when shuffle is visible
|
||||
if (isShuffleVisible) {
|
||||
binding.recyclerView.addOnScrollListener(object : RecyclerView.OnScrollListener() {
|
||||
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
|
||||
super.onScrolled(recyclerView, dx, dy)
|
||||
if (dy > 0) {
|
||||
binding.shuffleButton.hide()
|
||||
} else if (dy < 0) {
|
||||
binding.shuffleButton.show()
|
||||
}
|
||||
|
||||
}
|
||||
})
|
||||
binding.shuffleButton.apply {
|
||||
setOnClickListener {
|
||||
onShuffleClicked()
|
||||
}
|
||||
accentColor()
|
||||
}
|
||||
} else {
|
||||
binding.shuffleButton.isVisible = false
|
||||
}
|
||||
libraryViewModel.getFabMargin().observe(viewLifecycleOwner, {
|
||||
binding.shuffleButton.updateLayoutParams<ViewGroup.MarginLayoutParams> {
|
||||
bottomMargin = it
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
open fun onShuffleClicked() {
|
||||
}
|
||||
|
||||
fun toolbar(): Toolbar {
|
||||
return binding.toolbar
|
||||
}
|
||||
|
||||
private fun setupToolbar() {
|
||||
|
@ -73,6 +114,9 @@ abstract class AbsRecyclerViewFragment<A : RecyclerView.Adapter<*>, LM : Recycle
|
|||
}
|
||||
val appName = resources.getString(titleRes)
|
||||
binding.appNameText.text = appName
|
||||
binding.toolbarContainer.drawNextToNavbar()
|
||||
binding.appBarLayout.statusBarForeground =
|
||||
MaterialShapeDrawable.createWithElevationOverlay(requireContext())
|
||||
}
|
||||
|
||||
abstract val titleRes: Int
|
||||
|
@ -117,10 +161,10 @@ abstract class AbsRecyclerViewFragment<A : RecyclerView.Adapter<*>, LM : Recycle
|
|||
val itemCount: Int = adapter?.itemCount ?: 0
|
||||
|
||||
if (itemCount > 0 && MusicPlayerRemote.playingQueue.isNotEmpty()) {
|
||||
val height = DensityUtil.dip2px(requireContext(), 112f)
|
||||
val height = dip(R.dimen.mini_player_height_expanded)
|
||||
binding.recyclerView.updatePadding(0, 0, 0, height)
|
||||
} else {
|
||||
val height = DensityUtil.dip2px(requireContext(), 56f)
|
||||
val height = dip(R.dimen.mini_player_height)
|
||||
binding.recyclerView.updatePadding(0, 0, 0, height)
|
||||
}
|
||||
}
|
||||
|
@ -155,16 +199,12 @@ abstract class AbsRecyclerViewFragment<A : RecyclerView.Adapter<*>, LM : Recycle
|
|||
binding.recyclerView.adapter = adapter
|
||||
}
|
||||
|
||||
fun recyclerView(): RecyclerView {
|
||||
return binding.recyclerView
|
||||
}
|
||||
val recyclerView get() = binding.recyclerView
|
||||
|
||||
fun getContainer(): CoordinatorLayout {
|
||||
return binding.root
|
||||
}
|
||||
val container get() = binding.root
|
||||
|
||||
fun scrollToTop() {
|
||||
recyclerView().scrollToPosition(0)
|
||||
recyclerView.scrollToPosition(0)
|
||||
binding.appBarLayout.setExpanded(true, true)
|
||||
}
|
||||
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -33,6 +33,7 @@ import code.name.monkey.retromusic.fragments.base.AbsMainActivityFragment
|
|||
import code.name.monkey.retromusic.helper.menu.GenreMenuHelper
|
||||
import code.name.monkey.retromusic.model.Genre
|
||||
import code.name.monkey.retromusic.model.Song
|
||||
import com.google.android.material.shape.MaterialShapeDrawable
|
||||
import com.google.android.material.transition.MaterialSharedAxis
|
||||
import org.koin.androidx.viewmodel.ext.android.viewModel
|
||||
import org.koin.core.parameter.parametersOf
|
||||
|
@ -67,6 +68,8 @@ class GenreDetailsFragment : AbsMainActivityFragment(R.layout.fragment_playlist_
|
|||
view.doOnPreDraw {
|
||||
startPostponedEnterTransition()
|
||||
}
|
||||
binding.appBarLayout.statusBarForeground =
|
||||
MaterialShapeDrawable.createWithElevationOverlay(requireContext())
|
||||
}
|
||||
|
||||
private fun setupRecyclerView() {
|
||||
|
|
|
@ -19,6 +19,7 @@ import android.view.Menu
|
|||
import android.view.MenuInflater
|
||||
import android.view.MenuItem
|
||||
import android.view.View
|
||||
import androidx.activity.addCallback
|
||||
import androidx.core.os.bundleOf
|
||||
import androidx.navigation.fragment.findNavController
|
||||
import androidx.recyclerview.widget.GridLayoutManager
|
||||
|
@ -26,10 +27,12 @@ import androidx.recyclerview.widget.LinearLayoutManager
|
|||
import code.name.monkey.retromusic.EXTRA_GENRE
|
||||
import code.name.monkey.retromusic.R
|
||||
import code.name.monkey.retromusic.adapter.GenreAdapter
|
||||
import code.name.monkey.retromusic.extensions.navigate
|
||||
import code.name.monkey.retromusic.fragments.ReloadType
|
||||
import code.name.monkey.retromusic.fragments.base.AbsRecyclerViewFragment
|
||||
import code.name.monkey.retromusic.interfaces.IGenreClickListener
|
||||
import code.name.monkey.retromusic.model.Genre
|
||||
import code.name.monkey.retromusic.util.PreferenceUtil
|
||||
import code.name.monkey.retromusic.util.RetroUtil
|
||||
import com.google.android.gms.cast.framework.CastButtonFactory
|
||||
import com.google.android.material.transition.MaterialSharedAxis
|
||||
|
@ -45,6 +48,10 @@ GenresFragment : AbsRecyclerViewFragment<GenreAdapter, LinearLayoutManager>(),
|
|||
else
|
||||
adapter?.swapDataSet(listOf())
|
||||
})
|
||||
requireActivity().onBackPressedDispatcher.addCallback(viewLifecycleOwner) {
|
||||
remove()
|
||||
mainActivity.finish()
|
||||
}
|
||||
}
|
||||
|
||||
override fun createLayoutManager(): LinearLayoutManager {
|
||||
|
@ -82,6 +89,9 @@ GenresFragment : AbsRecyclerViewFragment<GenreAdapter, LinearLayoutManager>(),
|
|||
override val emptyMessage: Int
|
||||
get() = R.string.no_genres
|
||||
|
||||
override val isShuffleVisible: Boolean
|
||||
get() = false
|
||||
|
||||
companion object {
|
||||
@JvmField
|
||||
val TAG: String = GenresFragment::class.java.simpleName
|
||||
|
|
|
@ -14,19 +14,19 @@
|
|||
*/
|
||||
package code.name.monkey.retromusic.fragments.home
|
||||
|
||||
import android.app.ActivityOptions
|
||||
import android.os.Bundle
|
||||
import android.view.Menu
|
||||
import android.view.MenuInflater
|
||||
import android.view.MenuItem
|
||||
import android.view.MenuItem.SHOW_AS_ACTION_IF_ROOM
|
||||
import android.view.View
|
||||
import androidx.activity.addCallback
|
||||
import androidx.core.os.bundleOf
|
||||
import androidx.core.text.HtmlCompat
|
||||
import androidx.core.view.doOnPreDraw
|
||||
import androidx.navigation.fragment.FragmentNavigatorExtras
|
||||
import androidx.navigation.fragment.findNavController
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import code.name.monkey.appthemehelper.ThemeStore
|
||||
import code.name.monkey.appthemehelper.common.ATHToolbarActivity
|
||||
import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper
|
||||
import code.name.monkey.retromusic.*
|
||||
|
@ -35,12 +35,14 @@ import code.name.monkey.retromusic.databinding.FragmentBannerHomeBinding
|
|||
import code.name.monkey.retromusic.databinding.FragmentHomeBinding
|
||||
import code.name.monkey.retromusic.dialogs.CreatePlaylistDialog
|
||||
import code.name.monkey.retromusic.dialogs.ImportPlaylistDialog
|
||||
import code.name.monkey.retromusic.extensions.accentColor
|
||||
import code.name.monkey.retromusic.extensions.drawNextToNavbar
|
||||
import code.name.monkey.retromusic.fragments.base.AbsMainActivityFragment
|
||||
import code.name.monkey.retromusic.glide.GlideApp
|
||||
import code.name.monkey.retromusic.glide.RetroGlideExtension
|
||||
import code.name.monkey.retromusic.util.NavigationUtil
|
||||
import code.name.monkey.retromusic.util.PreferenceUtil
|
||||
import com.google.android.gms.cast.framework.CastButtonFactory
|
||||
import com.google.android.material.shape.MaterialShapeDrawable
|
||||
import com.google.android.material.transition.MaterialFadeThrough
|
||||
import com.google.android.material.transition.MaterialSharedAxis
|
||||
|
||||
|
@ -53,18 +55,45 @@ class HomeFragment :
|
|||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
_binding = getBinding(PreferenceUtil.isHomeBanner, view)
|
||||
enterTransition = MaterialFadeThrough()
|
||||
exitTransition = MaterialFadeThrough()
|
||||
mainActivity.setSupportActionBar(binding.toolbar)
|
||||
mainActivity.supportActionBar?.title = null
|
||||
setStatusBarColorAuto(view)
|
||||
setupListeners()
|
||||
binding.titleWelcome.text = String.format("%s", PreferenceUtil.userName)
|
||||
|
||||
enterTransition = MaterialFadeThrough().apply {
|
||||
addTarget(binding.contentContainer)
|
||||
}
|
||||
|
||||
val homeAdapter = HomeAdapter(mainActivity)
|
||||
binding.recyclerView.apply {
|
||||
layoutManager = LinearLayoutManager(mainActivity)
|
||||
adapter = homeAdapter
|
||||
}
|
||||
libraryViewModel.getHome().observe(viewLifecycleOwner, {
|
||||
homeAdapter.swapData(it)
|
||||
})
|
||||
|
||||
loadProfile()
|
||||
setupTitle()
|
||||
postponeEnterTransition()
|
||||
view.doOnPreDraw { startPostponedEnterTransition() }
|
||||
binding.appBarLayout.statusBarForeground =
|
||||
MaterialShapeDrawable.createWithElevationOverlay(requireContext())
|
||||
binding.toolbar.drawNextToNavbar()
|
||||
requireActivity().onBackPressedDispatcher.addCallback(viewLifecycleOwner) {
|
||||
remove()
|
||||
mainActivity.finish()
|
||||
}
|
||||
}
|
||||
|
||||
private fun setupListeners() {
|
||||
binding.bannerImage?.setOnClickListener {
|
||||
val options = ActivityOptions.makeSceneTransitionAnimation(
|
||||
mainActivity,
|
||||
binding.userImage,
|
||||
getString(R.string.transition_user_image)
|
||||
findNavController().navigate(
|
||||
R.id.user_info_fragment, null, null, FragmentNavigatorExtras(
|
||||
binding.userImage to "user_image"
|
||||
)
|
||||
)
|
||||
NavigationUtil.goToUserInfo(requireActivity(), options)
|
||||
reenterTransition = null
|
||||
}
|
||||
|
||||
binding.lastAdded.setOnClickListener {
|
||||
|
@ -96,28 +125,12 @@ class HomeFragment :
|
|||
}
|
||||
|
||||
binding.userImage.setOnClickListener {
|
||||
val options = ActivityOptions.makeSceneTransitionAnimation(
|
||||
mainActivity,
|
||||
binding.userImage,
|
||||
getString(R.string.transition_user_image)
|
||||
findNavController().navigate(
|
||||
R.id.user_info_fragment, null, null, FragmentNavigatorExtras(
|
||||
binding.userImage to "user_image"
|
||||
)
|
||||
)
|
||||
NavigationUtil.goToUserInfo(requireActivity(), options)
|
||||
}
|
||||
binding.titleWelcome.text = String.format("%s", PreferenceUtil.userName)
|
||||
|
||||
val homeAdapter = HomeAdapter(mainActivity)
|
||||
binding.recyclerView.apply {
|
||||
layoutManager = LinearLayoutManager(mainActivity)
|
||||
adapter = homeAdapter
|
||||
}
|
||||
libraryViewModel.getHome().observe(viewLifecycleOwner, {
|
||||
homeAdapter.swapData(it)
|
||||
})
|
||||
|
||||
loadProfile()
|
||||
setupTitle()
|
||||
postponeEnterTransition()
|
||||
view.doOnPreDraw { startPostponedEnterTransition() }
|
||||
}
|
||||
|
||||
private fun getBinding(homeBanner: Boolean, view: View): HomeBindingAdapter {
|
||||
|
@ -137,8 +150,7 @@ class HomeFragment :
|
|||
MaterialSharedAxis(MaterialSharedAxis.Z, false)
|
||||
findNavController().navigate(R.id.searchFragment, null, navOptions)
|
||||
}
|
||||
val color = ThemeStore.accentColor(requireContext())
|
||||
val hexColor = String.format("#%06X", 0xFFFFFF and color)
|
||||
val hexColor = String.format("#%06X", 0xFFFFFF and accentColor())
|
||||
val appName = HtmlCompat.fromHtml(
|
||||
"Retro <span style='color:$hexColor';>Music</span>",
|
||||
HtmlCompat.FROM_HTML_MODE_COMPACT
|
||||
|
|
|
@ -49,7 +49,7 @@ class LibraryFragment : AbsMainActivityFragment(R.layout.fragment_library) {
|
|||
override fun onActivityCreated(savedInstanceState: Bundle?) {
|
||||
super.onActivityCreated(savedInstanceState)
|
||||
setHasOptionsMenu(true)
|
||||
mainActivity.setBottomBarVisibility(true)
|
||||
mainActivity.setBottomNavVisibility(true)
|
||||
mainActivity.setSupportActionBar(binding.toolbar)
|
||||
mainActivity.supportActionBar?.title = null
|
||||
binding.toolbar.setNavigationOnClickListener {
|
||||
|
@ -84,7 +84,7 @@ class LibraryFragment : AbsMainActivityFragment(R.layout.fragment_library) {
|
|||
navGraph.setStartDestination(categoryInfo.category.id)
|
||||
}
|
||||
navController.graph = navGraph
|
||||
NavigationUI.setupWithNavController(mainActivity.getBottomNavigationView(), navController)
|
||||
NavigationUI.setupWithNavController(mainActivity.bottomNavigationView, navController)
|
||||
navController.addOnDestinationChangedListener { _, _, _ ->
|
||||
binding.appBarLayout.setExpanded(true, true)
|
||||
}
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
* See the GNU General Public License for more details.
|
||||
*
|
||||
*/
|
||||
package code.name.monkey.retromusic.fragments
|
||||
package code.name.monkey.retromusic.fragments.other
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
|
@ -38,6 +38,7 @@ import code.name.monkey.retromusic.interfaces.IArtistClickListener
|
|||
import code.name.monkey.retromusic.model.Album
|
||||
import code.name.monkey.retromusic.model.Artist
|
||||
import code.name.monkey.retromusic.util.RetroUtil
|
||||
import com.google.android.material.shape.MaterialShapeDrawable
|
||||
import com.google.android.material.transition.MaterialSharedAxis
|
||||
|
||||
class DetailListFragment : AbsMainActivityFragment(R.layout.fragment_playlist_detail),
|
||||
|
@ -63,6 +64,8 @@ class DetailListFragment : AbsMainActivityFragment(R.layout.fragment_playlist_de
|
|||
returnTransition = MaterialSharedAxis(MaterialSharedAxis.Y, false)
|
||||
}
|
||||
}
|
||||
binding.appBarLayout.statusBarForeground =
|
||||
MaterialShapeDrawable.createWithElevationOverlay(requireContext())
|
||||
postponeEnterTransition()
|
||||
view.doOnPreDraw { startPostponedEnterTransition() }
|
||||
}
|
|
@ -12,7 +12,7 @@
|
|||
* See the GNU General Public License for more details.
|
||||
*
|
||||
*/
|
||||
package code.name.monkey.retromusic.activities
|
||||
package code.name.monkey.retromusic.fragments.other
|
||||
|
||||
import android.os.Bundle
|
||||
import android.text.InputType
|
||||
|
@ -20,13 +20,14 @@ import android.view.*
|
|||
import androidx.core.view.ViewCompat
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.fragment.app.FragmentActivity
|
||||
import androidx.navigation.fragment.findNavController
|
||||
import androidx.transition.Fade
|
||||
import androidx.viewpager2.adapter.FragmentStateAdapter
|
||||
import code.name.monkey.appthemehelper.ThemeStore
|
||||
import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper
|
||||
import code.name.monkey.retromusic.R
|
||||
import code.name.monkey.retromusic.activities.base.AbsMusicServiceActivity
|
||||
import code.name.monkey.retromusic.activities.MainActivity
|
||||
import code.name.monkey.retromusic.activities.tageditor.WriteTagsAsyncTask
|
||||
import code.name.monkey.retromusic.databinding.ActivityLyricsBinding
|
||||
import code.name.monkey.retromusic.databinding.FragmentLyricsBinding
|
||||
import code.name.monkey.retromusic.databinding.FragmentNormalLyricsBinding
|
||||
import code.name.monkey.retromusic.databinding.FragmentSyncedLyricsBinding
|
||||
import code.name.monkey.retromusic.extensions.accentColor
|
||||
|
@ -53,12 +54,16 @@ import org.jaudiotagger.tag.FieldKey
|
|||
import java.io.File
|
||||
import java.util.*
|
||||
|
||||
class LyricsActivity : AbsMusicServiceActivity() {
|
||||
class LyricsFragment : AbsMusicServiceFragment(R.layout.fragment_lyrics) {
|
||||
|
||||
private lateinit var binding: ActivityLyricsBinding
|
||||
private var _binding: FragmentLyricsBinding? = null
|
||||
private val binding get() = _binding!!
|
||||
private lateinit var song: Song
|
||||
|
||||
private val lyricsSectionsAdapter = LyricsSectionsAdapter(this)
|
||||
val mainActivity: MainActivity
|
||||
get() = activity as MainActivity
|
||||
|
||||
private lateinit var lyricsSectionsAdapter: LyricsSectionsAdapter
|
||||
|
||||
private val googleSearchLrcUrl: String
|
||||
get() {
|
||||
|
@ -80,34 +85,30 @@ class LyricsActivity : AbsMusicServiceActivity() {
|
|||
private fun buildContainerTransform(): MaterialContainerTransform {
|
||||
val transform = MaterialContainerTransform()
|
||||
transform.setAllContainerColors(
|
||||
MaterialColors.getColor(findViewById(R.id.container), R.attr.colorSurface)
|
||||
MaterialColors.getColor(requireView().findViewById(R.id.container), R.attr.colorSurface)
|
||||
)
|
||||
transform.addTarget(R.id.container)
|
||||
transform.duration = 300
|
||||
return transform
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
binding = ActivityLyricsBinding.inflate(layoutInflater)
|
||||
setContentView(binding.root)
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
enterTransition = Fade()
|
||||
exitTransition = Fade()
|
||||
lyricsSectionsAdapter = LyricsSectionsAdapter(requireActivity())
|
||||
_binding = FragmentLyricsBinding.bind(view)
|
||||
ViewCompat.setTransitionName(binding.container, "lyrics")
|
||||
setStatusbarColorAuto()
|
||||
setTaskDescriptionColorAuto()
|
||||
setNavigationbarColorAuto()
|
||||
|
||||
setupWakelock()
|
||||
|
||||
binding.toolbar.setBackgroundColor(surfaceColor())
|
||||
binding.tabLyrics.setBackgroundColor(surfaceColor())
|
||||
binding.container.setBackgroundColor(surfaceColor())
|
||||
ToolbarContentTintHelper.colorBackButton(binding.toolbar)
|
||||
setSupportActionBar(binding.toolbar)
|
||||
setupViews()
|
||||
|
||||
setupToolbar()
|
||||
updateTitleSong()
|
||||
}
|
||||
|
||||
|
||||
private fun setupViews() {
|
||||
binding.lyricsPager.adapter = lyricsSectionsAdapter
|
||||
TabLayoutMediator(binding.tabLyrics, binding.lyricsPager) { tab, position ->
|
||||
|
@ -119,10 +120,29 @@ class LyricsActivity : AbsMusicServiceActivity() {
|
|||
}.attach()
|
||||
// lyricsPager.isUserInputEnabled = false
|
||||
|
||||
binding.tabLyrics.setSelectedTabIndicatorColor(ThemeStore.accentColor(this))
|
||||
binding.tabLyrics.setSelectedTabIndicatorColor(accentColor())
|
||||
binding.tabLyrics.setTabTextColors(textColorSecondary(), accentColor())
|
||||
binding.editButton.accentColor()
|
||||
binding.editButton.setOnClickListener {
|
||||
when (binding.lyricsPager.currentItem) {
|
||||
0 -> {
|
||||
editSyncedLyrics()
|
||||
}
|
||||
1 -> {
|
||||
editNormalLyrics()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun setupToolbar() {
|
||||
mainActivity.setSupportActionBar(binding.toolbar)
|
||||
binding.toolbar.setBackgroundColor(surfaceColor())
|
||||
ToolbarContentTintHelper.colorBackButton(binding.toolbar)
|
||||
binding.toolbar.setNavigationOnClickListener {
|
||||
findNavController().navigateUp()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onPlayingMetaChanged() {
|
||||
super.onPlayingMetaChanged()
|
||||
|
@ -141,36 +161,27 @@ class LyricsActivity : AbsMusicServiceActivity() {
|
|||
}
|
||||
|
||||
private fun setupWakelock() {
|
||||
window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
|
||||
requireActivity().window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
|
||||
}
|
||||
|
||||
override fun onCreateOptionsMenu(menu: Menu?): Boolean {
|
||||
menuInflater.inflate(R.menu.menu_search, menu)
|
||||
return super.onCreateOptionsMenu(menu)
|
||||
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
|
||||
inflater.inflate(R.menu.menu_search, menu)
|
||||
return super.onCreateOptionsMenu(menu, inflater)
|
||||
}
|
||||
|
||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||
if (item.itemId == android.R.id.home) {
|
||||
finish()
|
||||
findNavController().navigateUp()
|
||||
return true
|
||||
}
|
||||
if (item.itemId == R.id.action_search) {
|
||||
RetroUtil.openUrl(
|
||||
this, when (binding.lyricsPager.currentItem) {
|
||||
requireActivity(), when (binding.lyricsPager.currentItem) {
|
||||
0 -> syairSearchLrcUrl
|
||||
1 -> googleSearchLrcUrl
|
||||
else -> googleSearchLrcUrl
|
||||
}
|
||||
)
|
||||
} else if (item.itemId == R.id.action_edit) {
|
||||
when (binding.lyricsPager.currentItem) {
|
||||
0 -> {
|
||||
editSyncedLyrics()
|
||||
}
|
||||
1 -> {
|
||||
editNormalLyrics()
|
||||
}
|
||||
}
|
||||
}
|
||||
return super.onOptionsItemSelected(item)
|
||||
}
|
||||
|
@ -185,7 +196,7 @@ class LyricsActivity : AbsMusicServiceActivity() {
|
|||
e.printStackTrace()
|
||||
}
|
||||
|
||||
MaterialDialog(this, BottomSheet(LayoutMode.WRAP_CONTENT)).show {
|
||||
MaterialDialog(requireContext(), BottomSheet(LayoutMode.WRAP_CONTENT)).show {
|
||||
title(res = R.string.edit_normal_lyrics)
|
||||
input(
|
||||
hintRes = R.string.paste_lyrics_here,
|
||||
|
@ -194,7 +205,7 @@ class LyricsActivity : AbsMusicServiceActivity() {
|
|||
) { _, input ->
|
||||
val fieldKeyValueMap = EnumMap<FieldKey, String>(FieldKey::class.java)
|
||||
fieldKeyValueMap[FieldKey.LYRICS] = input.toString()
|
||||
WriteTagsAsyncTask(this@LyricsActivity).execute(
|
||||
WriteTagsAsyncTask(requireActivity()).execute(
|
||||
LoadingInfo(
|
||||
listOf(song.data), fieldKeyValueMap, null
|
||||
)
|
||||
|
@ -217,7 +228,7 @@ class LyricsActivity : AbsMusicServiceActivity() {
|
|||
}
|
||||
val content: String = LyricUtil.getStringFromLrc(lrcFile)
|
||||
|
||||
MaterialDialog(this, BottomSheet(LayoutMode.WRAP_CONTENT)).show {
|
||||
MaterialDialog(requireContext(), BottomSheet(LayoutMode.WRAP_CONTENT)).show {
|
||||
title(res = R.string.edit_synced_lyrics)
|
||||
input(
|
||||
hintRes = R.string.paste_timeframe_lyrics_here,
|
||||
|
@ -320,10 +331,10 @@ class LyricsActivity : AbsMusicServiceActivity() {
|
|||
|
||||
private fun setupLyricsView() {
|
||||
binding.lyricsView.apply {
|
||||
setCurrentColor(ThemeStore.accentColor(context))
|
||||
setTimeTextColor(ThemeStore.accentColor(context))
|
||||
setTimelineColor(ThemeStore.accentColor(context))
|
||||
setTimelineTextColor(ThemeStore.accentColor(context))
|
||||
setCurrentColor(accentColor())
|
||||
setTimeTextColor(accentColor())
|
||||
setTimelineColor(accentColor())
|
||||
setTimelineTextColor(accentColor())
|
||||
setDraggable(true, LrcView.OnPlayClickListener {
|
||||
MusicPlayerRemote.seekTo(it.toInt())
|
||||
return@OnPlayClickListener true
|
||||
|
@ -356,5 +367,8 @@ class LyricsActivity : AbsMusicServiceActivity() {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
override fun onDestroyView() {
|
||||
super.onDestroyView()
|
||||
(requireActivity() as MainActivity).expandPanel()
|
||||
}
|
||||
}
|
|
@ -12,7 +12,7 @@
|
|||
* See the GNU General Public License for more details.
|
||||
*
|
||||
*/
|
||||
package code.name.monkey.retromusic.fragments
|
||||
package code.name.monkey.retromusic.fragments.other
|
||||
|
||||
import android.animation.ObjectAnimator
|
||||
import android.annotation.SuppressLint
|
||||
|
@ -32,8 +32,6 @@ import code.name.monkey.retromusic.extensions.show
|
|||
import code.name.monkey.retromusic.extensions.textColorPrimary
|
||||
import code.name.monkey.retromusic.extensions.textColorSecondary
|
||||
import code.name.monkey.retromusic.fragments.base.AbsMusicServiceFragment
|
||||
import code.name.monkey.retromusic.glide.GlideApp
|
||||
import code.name.monkey.retromusic.glide.RetroGlideExtension
|
||||
import code.name.monkey.retromusic.helper.MusicPlayerRemote
|
||||
import code.name.monkey.retromusic.helper.MusicProgressViewUpdateHelper
|
||||
import code.name.monkey.retromusic.helper.PlayPauseButtonOnClickHandler
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
}
|
|
@ -12,25 +12,34 @@
|
|||
* See the GNU General Public License for more details.
|
||||
*
|
||||
*/
|
||||
package code.name.monkey.retromusic.activities
|
||||
package code.name.monkey.retromusic.fragments.other
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.Intent
|
||||
import android.content.res.ColorStateList
|
||||
import android.graphics.Bitmap
|
||||
import android.graphics.Color
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
import android.text.TextUtils
|
||||
import android.view.LayoutInflater
|
||||
import android.view.MenuItem
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.Toast
|
||||
import androidx.core.view.doOnPreDraw
|
||||
import androidx.core.view.updateLayoutParams
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.navigation.fragment.findNavController
|
||||
import code.name.monkey.appthemehelper.util.ColorUtil
|
||||
import code.name.monkey.appthemehelper.util.MaterialValueHelper
|
||||
import code.name.monkey.retromusic.Constants.USER_BANNER
|
||||
import code.name.monkey.retromusic.Constants.USER_PROFILE
|
||||
import code.name.monkey.retromusic.activities.base.AbsBaseActivity
|
||||
import code.name.monkey.retromusic.databinding.ActivityUserInfoBinding
|
||||
import code.name.monkey.retromusic.R
|
||||
import code.name.monkey.retromusic.databinding.FragmentUserInfoBinding
|
||||
import code.name.monkey.retromusic.extensions.accentColor
|
||||
import code.name.monkey.retromusic.extensions.applyToolbar
|
||||
import code.name.monkey.retromusic.fragments.LibraryViewModel
|
||||
import code.name.monkey.retromusic.glide.GlideApp
|
||||
import code.name.monkey.retromusic.glide.RetroGlideExtension
|
||||
import code.name.monkey.retromusic.util.ImageUtil
|
||||
|
@ -43,27 +52,39 @@ import com.bumptech.glide.request.RequestListener
|
|||
import com.bumptech.glide.request.target.Target
|
||||
import com.github.dhaval2404.imagepicker.ImagePicker
|
||||
import com.github.dhaval2404.imagepicker.constant.ImageProvider
|
||||
import com.google.android.material.transition.MaterialContainerTransform
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import org.koin.androidx.viewmodel.ext.android.sharedViewModel
|
||||
import java.io.BufferedOutputStream
|
||||
import java.io.File
|
||||
import java.io.FileOutputStream
|
||||
import java.io.IOException
|
||||
|
||||
class UserInfoActivity : AbsBaseActivity() {
|
||||
class UserInfoFragment : Fragment() {
|
||||
|
||||
private lateinit var binding: ActivityUserInfoBinding
|
||||
private var _binding: FragmentUserInfoBinding? = null
|
||||
private val binding get() = _binding!!
|
||||
private val libraryViewModel: LibraryViewModel by sharedViewModel()
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
binding = ActivityUserInfoBinding.inflate(layoutInflater)
|
||||
setContentView(binding.root)
|
||||
setStatusbarColorAuto()
|
||||
setNavigationbarColorAuto()
|
||||
setTaskDescriptionColorAuto()
|
||||
setLightNavigationBar(true)
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View {
|
||||
sharedElementEnterTransition = MaterialContainerTransform().apply {
|
||||
drawingViewId = R.id.fragment_container
|
||||
duration = 300L
|
||||
scrimColor = Color.TRANSPARENT
|
||||
}
|
||||
_binding = FragmentUserInfoBinding.inflate(layoutInflater)
|
||||
return binding.root
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
applyToolbar(binding.toolbar)
|
||||
|
||||
binding.nameContainer.accentColor()
|
||||
|
@ -80,20 +101,35 @@ class UserInfoActivity : AbsBaseActivity() {
|
|||
binding.next.setOnClickListener {
|
||||
val nameString = binding.name.text.toString().trim { it <= ' ' }
|
||||
if (TextUtils.isEmpty(nameString)) {
|
||||
Toast.makeText(this, "Umm you're name can't be empty!", Toast.LENGTH_SHORT).show()
|
||||
Toast.makeText(
|
||||
requireContext(),
|
||||
"Umm you're name can't be empty!",
|
||||
Toast.LENGTH_SHORT
|
||||
).show()
|
||||
return@setOnClickListener
|
||||
}
|
||||
PreferenceUtil.userName = nameString
|
||||
setResult(Activity.RESULT_OK)
|
||||
finish()
|
||||
findNavController().navigateUp()
|
||||
}
|
||||
|
||||
val textColor =
|
||||
MaterialValueHelper.getPrimaryTextColor(this, ColorUtil.isColorLight(accentColor()))
|
||||
MaterialValueHelper.getPrimaryTextColor(
|
||||
requireContext(),
|
||||
ColorUtil.isColorLight(accentColor())
|
||||
)
|
||||
binding.next.backgroundTintList = ColorStateList.valueOf(accentColor())
|
||||
binding.next.iconTint = ColorStateList.valueOf(textColor)
|
||||
binding.next.setTextColor(textColor)
|
||||
loadProfile()
|
||||
postponeEnterTransition()
|
||||
view.doOnPreDraw {
|
||||
startPostponedEnterTransition()
|
||||
}
|
||||
libraryViewModel.getFabMargin().observe(viewLifecycleOwner, {
|
||||
binding.next.updateLayoutParams<ViewGroup.MarginLayoutParams> {
|
||||
bottomMargin = it
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
private fun loadProfile() {
|
||||
|
@ -112,7 +148,7 @@ class UserInfoActivity : AbsBaseActivity() {
|
|||
|
||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||
if (item.itemId == android.R.id.home) {
|
||||
onBackPressed()
|
||||
findNavController().navigateUp()
|
||||
}
|
||||
return super.onOptionsItemSelected(item)
|
||||
}
|
||||
|
@ -133,7 +169,7 @@ class UserInfoActivity : AbsBaseActivity() {
|
|||
.start(PICK_IMAGE_REQUEST)
|
||||
}
|
||||
|
||||
public override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
||||
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
||||
super.onActivityResult(requestCode, resultCode, data)
|
||||
if (resultCode == Activity.RESULT_OK && requestCode == PICK_IMAGE_REQUEST) {
|
||||
val fileUri = data?.data
|
||||
|
@ -142,9 +178,9 @@ class UserInfoActivity : AbsBaseActivity() {
|
|||
val fileUri = data?.data
|
||||
fileUri?.let { setAndSaveBannerImage(it) }
|
||||
} else if (resultCode == ImagePicker.RESULT_ERROR) {
|
||||
Toast.makeText(this, ImagePicker.getError(data), Toast.LENGTH_SHORT).show()
|
||||
Toast.makeText(requireContext(), ImagePicker.getError(data), Toast.LENGTH_SHORT).show()
|
||||
} else {
|
||||
Toast.makeText(this, "Task Cancelled", Toast.LENGTH_SHORT).show()
|
||||
Toast.makeText(requireContext(), "Task Cancelled", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -179,7 +215,7 @@ class UserInfoActivity : AbsBaseActivity() {
|
|||
|
||||
private fun saveImage(bitmap: Bitmap, fileName: String) {
|
||||
CoroutineScope(Dispatchers.IO).launch {
|
||||
val appDir = applicationContext.filesDir
|
||||
val appDir = requireContext().filesDir
|
||||
val file = File(appDir, fileName)
|
||||
var successful = false
|
||||
try {
|
||||
|
@ -192,7 +228,7 @@ class UserInfoActivity : AbsBaseActivity() {
|
|||
}
|
||||
if (successful) {
|
||||
withContext(Dispatchers.Main) {
|
||||
Toast.makeText(this@UserInfoActivity, "Updated", Toast.LENGTH_SHORT).show()
|
||||
Toast.makeText(requireContext(), "Updated", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -227,6 +263,11 @@ class UserInfoActivity : AbsBaseActivity() {
|
|||
.into(binding.userImage)
|
||||
}
|
||||
|
||||
override fun onDestroyView() {
|
||||
super.onDestroyView()
|
||||
_binding = null
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val PICK_IMAGE_REQUEST = 9002
|
||||
private const val PICK_BANNER_REQUEST = 9004
|
|
@ -12,7 +12,7 @@
|
|||
* See the GNU General Public License for more details.
|
||||
*
|
||||
*/
|
||||
package code.name.monkey.retromusic.fragments
|
||||
package code.name.monkey.retromusic.fragments.other
|
||||
|
||||
import android.content.Context
|
||||
import android.graphics.Color
|
|
@ -281,7 +281,7 @@ class PlayerAlbumCoverFragment : AbsMusicServiceFragment(R.layout.fragment_playe
|
|||
private fun updatePlayingQueue() {
|
||||
binding.viewPager.apply {
|
||||
adapter = AlbumCoverPagerAdapter(childFragmentManager, MusicPlayerRemote.playingQueue)
|
||||
adapter!!.notifyDataSetChanged()
|
||||
adapter?.notifyDataSetChanged()
|
||||
currentItem = MusicPlayerRemote.position
|
||||
onPageSelected(MusicPlayerRemote.position)
|
||||
}
|
||||
|
|
|
@ -21,6 +21,7 @@ import code.name.monkey.appthemehelper.util.ATHUtil
|
|||
import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper
|
||||
import code.name.monkey.retromusic.R
|
||||
import code.name.monkey.retromusic.databinding.FragmentAdaptivePlayerBinding
|
||||
import code.name.monkey.retromusic.extensions.drawAboveSystemBars
|
||||
import code.name.monkey.retromusic.extensions.surfaceColor
|
||||
import code.name.monkey.retromusic.extensions.textColorPrimary
|
||||
import code.name.monkey.retromusic.extensions.textColorSecondary
|
||||
|
@ -46,6 +47,7 @@ class AdaptiveFragment : AbsPlayerFragment(R.layout.fragment_adaptive_player) {
|
|||
_binding = FragmentAdaptivePlayerBinding.bind(view)
|
||||
setUpSubFragments()
|
||||
setUpPlayerToolbar()
|
||||
binding.root.drawAboveSystemBars()
|
||||
}
|
||||
|
||||
private fun setUpSubFragments() {
|
||||
|
|
|
@ -24,6 +24,7 @@ import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper
|
|||
import code.name.monkey.retromusic.NEW_BLUR_AMOUNT
|
||||
import code.name.monkey.retromusic.R
|
||||
import code.name.monkey.retromusic.databinding.FragmentBlurBinding
|
||||
import code.name.monkey.retromusic.extensions.drawAboveSystemBars
|
||||
import code.name.monkey.retromusic.fragments.base.AbsPlayerFragment
|
||||
import code.name.monkey.retromusic.fragments.player.PlayerAlbumCoverFragment
|
||||
import code.name.monkey.retromusic.glide.BlurTransformation
|
||||
|
@ -32,6 +33,7 @@ import code.name.monkey.retromusic.glide.RetroGlideExtension
|
|||
import code.name.monkey.retromusic.glide.RetroMusicColoredTarget
|
||||
import code.name.monkey.retromusic.helper.MusicPlayerRemote
|
||||
import code.name.monkey.retromusic.model.Song
|
||||
import code.name.monkey.retromusic.util.PreferenceUtil.blurAmount
|
||||
import code.name.monkey.retromusic.util.color.MediaNotificationProcessor
|
||||
|
||||
class BlurPlayerFragment : AbsPlayerFragment(R.layout.fragment_blur),
|
||||
|
@ -54,6 +56,7 @@ class BlurPlayerFragment : AbsPlayerFragment(R.layout.fragment_blur),
|
|||
_binding = FragmentBlurBinding.bind(view)
|
||||
setUpSubFragments()
|
||||
setUpPlayerToolbar()
|
||||
binding.playerToolbar.drawAboveSystemBars()
|
||||
}
|
||||
|
||||
private fun setUpSubFragments() {
|
||||
|
@ -108,8 +111,6 @@ class BlurPlayerFragment : AbsPlayerFragment(R.layout.fragment_blur),
|
|||
get() = lastColor
|
||||
|
||||
private fun updateBlur() {
|
||||
val blurAmount = PreferenceManager.getDefaultSharedPreferences(requireContext())
|
||||
.getInt(NEW_BLUR_AMOUNT, 25)
|
||||
binding.colorBackground.clearColorFilter()
|
||||
GlideApp.with(requireActivity()).asBitmapPalette()
|
||||
.songCoverOptions(MusicPlayerRemote.currentSong)
|
||||
|
|
|
@ -21,6 +21,7 @@ import androidx.appcompat.widget.Toolbar
|
|||
import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper
|
||||
import code.name.monkey.retromusic.R
|
||||
import code.name.monkey.retromusic.databinding.FragmentCardPlayerBinding
|
||||
import code.name.monkey.retromusic.extensions.drawAboveSystemBars
|
||||
import code.name.monkey.retromusic.fragments.base.AbsPlayerFragment
|
||||
import code.name.monkey.retromusic.fragments.player.PlayerAlbumCoverFragment
|
||||
import code.name.monkey.retromusic.fragments.player.normal.PlayerFragment
|
||||
|
@ -82,6 +83,7 @@ class CardFragment : AbsPlayerFragment(R.layout.fragment_card_player) {
|
|||
_binding = FragmentCardPlayerBinding.bind(view)
|
||||
setUpSubFragments()
|
||||
setUpPlayerToolbar()
|
||||
(binding.playbackControlsFragment.parent as View).drawAboveSystemBars()
|
||||
}
|
||||
|
||||
private fun setUpSubFragments() {
|
||||
|
|
|
@ -24,6 +24,7 @@ import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper
|
|||
import code.name.monkey.retromusic.NEW_BLUR_AMOUNT
|
||||
import code.name.monkey.retromusic.R
|
||||
import code.name.monkey.retromusic.databinding.FragmentCardBlurPlayerBinding
|
||||
import code.name.monkey.retromusic.extensions.drawAboveSystemBars
|
||||
import code.name.monkey.retromusic.fragments.base.AbsPlayerFragment
|
||||
import code.name.monkey.retromusic.fragments.player.PlayerAlbumCoverFragment
|
||||
import code.name.monkey.retromusic.fragments.player.normal.PlayerFragment
|
||||
|
@ -33,6 +34,7 @@ import code.name.monkey.retromusic.glide.RetroGlideExtension
|
|||
import code.name.monkey.retromusic.glide.RetroMusicColoredTarget
|
||||
import code.name.monkey.retromusic.helper.MusicPlayerRemote
|
||||
import code.name.monkey.retromusic.model.Song
|
||||
import code.name.monkey.retromusic.util.PreferenceUtil.blurAmount
|
||||
import code.name.monkey.retromusic.util.color.MediaNotificationProcessor
|
||||
|
||||
class CardBlurFragment : AbsPlayerFragment(R.layout.fragment_card_blur_player),
|
||||
|
@ -92,6 +94,7 @@ class CardBlurFragment : AbsPlayerFragment(R.layout.fragment_card_blur_player),
|
|||
_binding = FragmentCardBlurPlayerBinding.bind(view)
|
||||
setUpSubFragments()
|
||||
setUpPlayerToolbar()
|
||||
binding.cardContainer?.drawAboveSystemBars()
|
||||
}
|
||||
|
||||
private fun setUpSubFragments() {
|
||||
|
@ -133,8 +136,6 @@ class CardBlurFragment : AbsPlayerFragment(R.layout.fragment_card_blur_player),
|
|||
}
|
||||
|
||||
private fun updateBlur() {
|
||||
val blurAmount = PreferenceManager.getDefaultSharedPreferences(requireContext())
|
||||
.getInt(NEW_BLUR_AMOUNT, 25)
|
||||
binding.colorBackground.clearColorFilter()
|
||||
GlideApp.with(requireActivity()).asBitmapPalette()
|
||||
.songCoverOptions(MusicPlayerRemote.currentSong)
|
||||
|
|
|
@ -33,10 +33,7 @@ import code.name.monkey.appthemehelper.util.TintHelper
|
|||
import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper
|
||||
import code.name.monkey.retromusic.R
|
||||
import code.name.monkey.retromusic.databinding.FragmentCirclePlayerBinding
|
||||
import code.name.monkey.retromusic.extensions.accentColor
|
||||
import code.name.monkey.retromusic.extensions.applyColor
|
||||
import code.name.monkey.retromusic.extensions.hide
|
||||
import code.name.monkey.retromusic.extensions.show
|
||||
import code.name.monkey.retromusic.extensions.*
|
||||
import code.name.monkey.retromusic.fragments.base.AbsPlayerControlsFragment
|
||||
import code.name.monkey.retromusic.fragments.base.AbsPlayerFragment
|
||||
import code.name.monkey.retromusic.fragments.base.goToAlbum
|
||||
|
@ -96,6 +93,7 @@ class CirclePlayerFragment : AbsPlayerFragment(R.layout.fragment_circle_player),
|
|||
binding.text.setOnClickListener {
|
||||
goToArtist(requireActivity())
|
||||
}
|
||||
binding.songInfo.drawAboveSystemBars()
|
||||
}
|
||||
|
||||
private fun setUpPlayerToolbar() {
|
||||
|
|
|
@ -38,11 +38,11 @@ import code.name.monkey.retromusic.adapter.song.PlayingQueueAdapter
|
|||
import code.name.monkey.retromusic.databinding.FragmentClassicPlayerBinding
|
||||
import code.name.monkey.retromusic.extensions.hide
|
||||
import code.name.monkey.retromusic.extensions.show
|
||||
import code.name.monkey.retromusic.fragments.VolumeFragment
|
||||
import code.name.monkey.retromusic.fragments.base.AbsPlayerControlsFragment
|
||||
import code.name.monkey.retromusic.fragments.base.AbsPlayerFragment
|
||||
import code.name.monkey.retromusic.fragments.base.goToAlbum
|
||||
import code.name.monkey.retromusic.fragments.base.goToArtist
|
||||
import code.name.monkey.retromusic.fragments.other.VolumeFragment
|
||||
import code.name.monkey.retromusic.fragments.player.PlayerAlbumCoverFragment
|
||||
import code.name.monkey.retromusic.helper.MusicPlayerRemote
|
||||
import code.name.monkey.retromusic.helper.MusicProgressViewUpdateHelper
|
||||
|
|
|
@ -16,15 +16,14 @@ package code.name.monkey.retromusic.fragments.player.color
|
|||
|
||||
import android.animation.ValueAnimator
|
||||
import android.os.Bundle
|
||||
import android.os.Handler
|
||||
import android.view.View
|
||||
import androidx.appcompat.widget.Toolbar
|
||||
import androidx.core.animation.doOnEnd
|
||||
import code.name.monkey.appthemehelper.util.ATHUtil
|
||||
import code.name.monkey.appthemehelper.util.ColorUtil
|
||||
import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper
|
||||
import code.name.monkey.retromusic.R
|
||||
import code.name.monkey.retromusic.databinding.FragmentColorPlayerBinding
|
||||
import code.name.monkey.retromusic.extensions.drawAboveSystemBars
|
||||
import code.name.monkey.retromusic.fragments.base.AbsPlayerFragment
|
||||
import code.name.monkey.retromusic.fragments.player.PlayerAlbumCoverFragment
|
||||
import code.name.monkey.retromusic.helper.MusicPlayerRemote
|
||||
|
@ -61,14 +60,11 @@ class ColorFragment : AbsPlayerFragment(R.layout.fragment_color_player) {
|
|||
_binding?.root?.setBackgroundColor(color.backgroundColor)
|
||||
}
|
||||
animator.start()
|
||||
serviceActivity?.setLightNavigationBar(ColorUtil.isColorLight(color.backgroundColor))
|
||||
Handler().post {
|
||||
ToolbarContentTintHelper.colorizeToolbar(
|
||||
binding.playerToolbar,
|
||||
color.secondaryTextColor,
|
||||
requireActivity()
|
||||
)
|
||||
}
|
||||
ToolbarContentTintHelper.colorizeToolbar(
|
||||
binding.playerToolbar,
|
||||
color.secondaryTextColor,
|
||||
requireActivity()
|
||||
)
|
||||
}
|
||||
|
||||
override fun onFavoriteToggled() {
|
||||
|
@ -116,6 +112,7 @@ class ColorFragment : AbsPlayerFragment(R.layout.fragment_color_player) {
|
|||
val playerAlbumCoverFragment =
|
||||
childFragmentManager.findFragmentById(R.id.playerAlbumCoverFragment) as PlayerAlbumCoverFragment
|
||||
playerAlbumCoverFragment.setCallbacks(this)
|
||||
playerToolbar().drawAboveSystemBars()
|
||||
}
|
||||
|
||||
private fun setUpSubFragments() {
|
||||
|
|
|
@ -21,6 +21,7 @@ import code.name.monkey.appthemehelper.util.ATHUtil
|
|||
import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper
|
||||
import code.name.monkey.retromusic.R
|
||||
import code.name.monkey.retromusic.databinding.FragmentFitBinding
|
||||
import code.name.monkey.retromusic.extensions.drawAboveSystemBars
|
||||
import code.name.monkey.retromusic.fragments.base.AbsPlayerFragment
|
||||
import code.name.monkey.retromusic.fragments.player.PlayerAlbumCoverFragment
|
||||
import code.name.monkey.retromusic.helper.MusicPlayerRemote
|
||||
|
@ -85,6 +86,7 @@ class FitFragment : AbsPlayerFragment(R.layout.fragment_fit) {
|
|||
_binding = FragmentFitBinding.bind(view)
|
||||
setUpSubFragments()
|
||||
setUpPlayerToolbar()
|
||||
playerToolbar().drawAboveSystemBars()
|
||||
}
|
||||
|
||||
private fun setUpSubFragments() {
|
||||
|
|
|
@ -26,6 +26,7 @@ import code.name.monkey.appthemehelper.util.MaterialValueHelper
|
|||
import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper
|
||||
import code.name.monkey.retromusic.R
|
||||
import code.name.monkey.retromusic.databinding.FragmentFlatPlayerBinding
|
||||
import code.name.monkey.retromusic.extensions.drawAboveSystemBars
|
||||
import code.name.monkey.retromusic.fragments.base.AbsPlayerFragment
|
||||
import code.name.monkey.retromusic.fragments.player.PlayerAlbumCoverFragment
|
||||
import code.name.monkey.retromusic.helper.MusicPlayerRemote
|
||||
|
@ -90,6 +91,7 @@ class FlatPlayerFragment : AbsPlayerFragment(R.layout.fragment_flat_player) {
|
|||
_binding = FragmentFlatPlayerBinding.bind(view)
|
||||
setUpPlayerToolbar()
|
||||
setUpSubFragments()
|
||||
binding.playbackControlsFragment.drawAboveSystemBars()
|
||||
}
|
||||
|
||||
override fun onShow() {
|
||||
|
|
|
@ -22,6 +22,7 @@ import androidx.appcompat.widget.Toolbar
|
|||
import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper
|
||||
import code.name.monkey.retromusic.R
|
||||
import code.name.monkey.retromusic.databinding.FragmentFullBinding
|
||||
import code.name.monkey.retromusic.extensions.drawAboveSystemBars
|
||||
import code.name.monkey.retromusic.extensions.hide
|
||||
import code.name.monkey.retromusic.extensions.show
|
||||
import code.name.monkey.retromusic.extensions.whichFragment
|
||||
|
@ -62,6 +63,7 @@ class FullPlayerFragment : AbsPlayerFragment(R.layout.fragment_full) {
|
|||
setUpPlayerToolbar()
|
||||
setupArtist()
|
||||
binding.nextSong.isSelected = true
|
||||
binding.playbackControlsFragment.drawAboveSystemBars()
|
||||
}
|
||||
|
||||
private fun setupArtist() {
|
||||
|
@ -131,12 +133,15 @@ class FullPlayerFragment : AbsPlayerFragment(R.layout.fragment_full) {
|
|||
private fun updateArtistImage() {
|
||||
libraryViewModel.artist(MusicPlayerRemote.currentSong.artistId)
|
||||
.observe(viewLifecycleOwner, { artist ->
|
||||
GlideApp.with(requireActivity()).asBitmapPalette().artistImageOptions(artist)
|
||||
.load(RetroGlideExtension.getArtistModel(artist))
|
||||
.into(object : RetroMusicColoredTarget(binding.artistImage) {
|
||||
override fun onColorReady(colors: MediaNotificationProcessor) {
|
||||
}
|
||||
})
|
||||
if (artist.id != -1L) {
|
||||
GlideApp.with(requireActivity()).asBitmapPalette().artistImageOptions(artist)
|
||||
.load(RetroGlideExtension.getArtistModel(artist))
|
||||
.into(object : RetroMusicColoredTarget(binding.artistImage) {
|
||||
override fun onColorReady(colors: MediaNotificationProcessor) {
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
@ -29,6 +29,8 @@ import androidx.appcompat.app.AppCompatActivity
|
|||
import androidx.appcompat.widget.Toolbar
|
||||
import androidx.constraintlayout.widget.ConstraintLayout
|
||||
import androidx.core.view.ViewCompat
|
||||
import androidx.core.view.WindowInsetsCompat
|
||||
import androidx.core.view.updatePadding
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
|
@ -40,14 +42,12 @@ import code.name.monkey.retromusic.databinding.FragmentGradientPlayerBinding
|
|||
import code.name.monkey.retromusic.db.PlaylistEntity
|
||||
import code.name.monkey.retromusic.db.SongEntity
|
||||
import code.name.monkey.retromusic.db.toSongEntity
|
||||
import code.name.monkey.retromusic.extensions.hide
|
||||
import code.name.monkey.retromusic.extensions.ripAlpha
|
||||
import code.name.monkey.retromusic.extensions.show
|
||||
import code.name.monkey.retromusic.fragments.VolumeFragment
|
||||
import code.name.monkey.retromusic.extensions.*
|
||||
import code.name.monkey.retromusic.fragments.base.AbsPlayerControlsFragment
|
||||
import code.name.monkey.retromusic.fragments.base.AbsPlayerFragment
|
||||
import code.name.monkey.retromusic.fragments.base.goToAlbum
|
||||
import code.name.monkey.retromusic.fragments.base.goToArtist
|
||||
import code.name.monkey.retromusic.fragments.other.VolumeFragment
|
||||
import code.name.monkey.retromusic.helper.MusicPlayerRemote
|
||||
import code.name.monkey.retromusic.helper.MusicProgressViewUpdateHelper
|
||||
import code.name.monkey.retromusic.helper.PlayPauseButtonOnClickHandler
|
||||
|
@ -81,6 +81,7 @@ class GradientPlayerFragment : AbsPlayerFragment(R.layout.fragment_gradient_play
|
|||
private var recyclerViewTouchActionGuardManager: RecyclerViewTouchActionGuardManager? = null
|
||||
private var playingQueueAdapter: PlayingQueueAdapter? = null
|
||||
private lateinit var linearLayoutManager: LinearLayoutManager
|
||||
private var bottomInsets = 0
|
||||
|
||||
private var _binding: FragmentGradientPlayerBinding? = null
|
||||
private val binding get() = _binding!!
|
||||
|
@ -88,11 +89,11 @@ class GradientPlayerFragment : AbsPlayerFragment(R.layout.fragment_gradient_play
|
|||
private val bottomSheetCallbackList = object : BottomSheetCallback() {
|
||||
override fun onSlide(bottomSheet: View, slideOffset: Float) {
|
||||
mainActivity.getBottomSheetBehavior().setAllowDragging(false)
|
||||
binding.playerQueueSheet.setPadding(
|
||||
binding.playerQueueSheet.paddingLeft,
|
||||
(slideOffset * binding.statusBarLayout.statusBar.height).toInt(),
|
||||
binding.playerQueueSheet.paddingRight,
|
||||
binding.playerQueueSheet.paddingBottom
|
||||
binding.playerQueueSheet.updatePadding(
|
||||
top = (slideOffset * binding.statusBarLayout.statusBar.height).toInt()
|
||||
)
|
||||
binding.container.updatePadding(
|
||||
bottom = ((1 - slideOffset) * bottomInsets).toInt()
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -157,6 +158,14 @@ class GradientPlayerFragment : AbsPlayerFragment(R.layout.fragment_gradient_play
|
|||
binding.playbackControlsFragment.text.setOnClickListener {
|
||||
goToArtist(requireActivity())
|
||||
}
|
||||
ViewCompat.setOnApplyWindowInsetsListener(
|
||||
(binding.container)
|
||||
) { v: View, insets: WindowInsetsCompat ->
|
||||
bottomInsets = insets.safeGetBottomInsets()
|
||||
v.updatePadding(bottom = bottomInsets)
|
||||
insets
|
||||
}
|
||||
binding.playbackControlsFragment.root.drawAboveSystemBars()
|
||||
}
|
||||
|
||||
@SuppressLint("ClickableViewAccessibility")
|
||||
|
|
|
@ -21,6 +21,7 @@ import code.name.monkey.appthemehelper.util.ATHUtil
|
|||
import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper
|
||||
import code.name.monkey.retromusic.R
|
||||
import code.name.monkey.retromusic.databinding.FragmentMaterialBinding
|
||||
import code.name.monkey.retromusic.extensions.drawAboveSystemBars
|
||||
import code.name.monkey.retromusic.fragments.base.AbsPlayerFragment
|
||||
import code.name.monkey.retromusic.fragments.player.PlayerAlbumCoverFragment
|
||||
import code.name.monkey.retromusic.fragments.player.normal.PlayerFragment
|
||||
|
@ -93,6 +94,7 @@ class MaterialFragment : AbsPlayerFragment(R.layout.fragment_material) {
|
|||
_binding = FragmentMaterialBinding.bind(view)
|
||||
setUpSubFragments()
|
||||
setUpPlayerToolbar()
|
||||
playerToolbar().drawAboveSystemBars()
|
||||
}
|
||||
|
||||
private fun setUpSubFragments() {
|
||||
|
|
|
@ -24,6 +24,7 @@ import code.name.monkey.appthemehelper.util.ATHUtil
|
|||
import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper
|
||||
import code.name.monkey.retromusic.R
|
||||
import code.name.monkey.retromusic.databinding.FragmentPlayerBinding
|
||||
import code.name.monkey.retromusic.extensions.drawAboveSystemBars
|
||||
import code.name.monkey.retromusic.fragments.base.AbsPlayerFragment
|
||||
import code.name.monkey.retromusic.fragments.player.PlayerAlbumCoverFragment
|
||||
import code.name.monkey.retromusic.helper.MusicPlayerRemote
|
||||
|
@ -120,6 +121,7 @@ class PlayerFragment : AbsPlayerFragment(R.layout.fragment_player) {
|
|||
_binding = FragmentPlayerBinding.bind(view)
|
||||
setUpSubFragments()
|
||||
setUpPlayerToolbar()
|
||||
playerToolbar().drawAboveSystemBars()
|
||||
}
|
||||
|
||||
override fun onDestroyView() {
|
||||
|
|
|
@ -21,6 +21,7 @@ import code.name.monkey.appthemehelper.util.ATHUtil
|
|||
import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper
|
||||
import code.name.monkey.retromusic.R
|
||||
import code.name.monkey.retromusic.databinding.FragmentPeakPlayerBinding
|
||||
import code.name.monkey.retromusic.extensions.drawAboveSystemBars
|
||||
import code.name.monkey.retromusic.extensions.hide
|
||||
import code.name.monkey.retromusic.extensions.show
|
||||
import code.name.monkey.retromusic.fragments.base.AbsPlayerFragment
|
||||
|
@ -55,6 +56,7 @@ class PeakPlayerFragment : AbsPlayerFragment(R.layout.fragment_peak_player) {
|
|||
binding.text.setOnClickListener {
|
||||
goToArtist(requireActivity())
|
||||
}
|
||||
binding.root.drawAboveSystemBars()
|
||||
}
|
||||
|
||||
private fun setUpSubFragments() {
|
||||
|
|
|
@ -21,6 +21,7 @@ import code.name.monkey.appthemehelper.util.ATHUtil
|
|||
import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper
|
||||
import code.name.monkey.retromusic.R
|
||||
import code.name.monkey.retromusic.databinding.FragmentPlainPlayerBinding
|
||||
import code.name.monkey.retromusic.extensions.drawAboveSystemBars
|
||||
import code.name.monkey.retromusic.fragments.base.AbsPlayerFragment
|
||||
import code.name.monkey.retromusic.fragments.base.goToAlbum
|
||||
import code.name.monkey.retromusic.fragments.base.goToArtist
|
||||
|
@ -84,6 +85,7 @@ class PlainPlayerFragment : AbsPlayerFragment(R.layout.fragment_plain_player) {
|
|||
binding.text.setOnClickListener {
|
||||
goToArtist(requireActivity())
|
||||
}
|
||||
playerToolbar().drawAboveSystemBars()
|
||||
}
|
||||
|
||||
private fun setUpSubFragments() {
|
||||
|
|
|
@ -21,6 +21,7 @@ import code.name.monkey.appthemehelper.util.ATHUtil
|
|||
import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper
|
||||
import code.name.monkey.retromusic.R
|
||||
import code.name.monkey.retromusic.databinding.FragmentSimplePlayerBinding
|
||||
import code.name.monkey.retromusic.extensions.drawAboveSystemBars
|
||||
import code.name.monkey.retromusic.fragments.base.AbsPlayerFragment
|
||||
import code.name.monkey.retromusic.fragments.player.PlayerAlbumCoverFragment
|
||||
import code.name.monkey.retromusic.helper.MusicPlayerRemote
|
||||
|
@ -51,6 +52,7 @@ class SimplePlayerFragment : AbsPlayerFragment(R.layout.fragment_simple_player)
|
|||
_binding = FragmentSimplePlayerBinding.bind(view)
|
||||
setUpSubFragments()
|
||||
setUpPlayerToolbar()
|
||||
playerToolbar().drawAboveSystemBars()
|
||||
}
|
||||
|
||||
private fun setUpSubFragments() {
|
||||
|
|
|
@ -27,6 +27,7 @@ import androidx.appcompat.widget.Toolbar
|
|||
import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper
|
||||
import code.name.monkey.retromusic.R
|
||||
import code.name.monkey.retromusic.databinding.FragmentTinyPlayerBinding
|
||||
import code.name.monkey.retromusic.extensions.drawAboveSystemBars
|
||||
import code.name.monkey.retromusic.extensions.hide
|
||||
import code.name.monkey.retromusic.extensions.show
|
||||
import code.name.monkey.retromusic.fragments.base.AbsPlayerFragment
|
||||
|
@ -147,6 +148,7 @@ class TinyPlayerFragment : AbsPlayerFragment(R.layout.fragment_tiny_player),
|
|||
binding.text.setOnClickListener {
|
||||
goToArtist(requireActivity())
|
||||
}
|
||||
playerToolbar().drawAboveSystemBars()
|
||||
}
|
||||
|
||||
private fun setUpSubFragments() {
|
||||
|
|
|
@ -22,10 +22,15 @@ import code.name.monkey.retromusic.extensions.dipToPix
|
|||
import code.name.monkey.retromusic.extensions.surfaceColor
|
||||
import code.name.monkey.retromusic.fragments.base.AbsMainActivityFragment
|
||||
import code.name.monkey.retromusic.helper.menu.PlaylistMenuHelper
|
||||
import code.name.monkey.retromusic.interfaces.ICabCallback
|
||||
import code.name.monkey.retromusic.interfaces.ICabHolder
|
||||
import code.name.monkey.retromusic.model.Song
|
||||
import code.name.monkey.retromusic.util.RetroColorUtil
|
||||
import com.afollestad.materialcab.MaterialCab
|
||||
import com.afollestad.materialcab.attached.AttachedCab
|
||||
import com.afollestad.materialcab.attached.destroy
|
||||
import com.afollestad.materialcab.attached.isActive
|
||||
import com.afollestad.materialcab.createCab
|
||||
import com.google.android.material.shape.MaterialShapeDrawable
|
||||
import com.google.android.material.transition.MaterialSharedAxis
|
||||
import com.h6ah4i.android.widget.advrecyclerview.animator.DraggableItemAnimator
|
||||
import com.h6ah4i.android.widget.advrecyclerview.animator.GeneralItemAnimator
|
||||
|
@ -71,6 +76,8 @@ class PlaylistDetailsFragment : AbsMainActivityFragment(R.layout.fragment_playli
|
|||
requireActivity().onBackPressed()
|
||||
}
|
||||
}
|
||||
binding.appBarLayout.statusBarForeground =
|
||||
MaterialShapeDrawable.createWithElevationOverlay(requireContext())
|
||||
}
|
||||
|
||||
private fun setUpRecyclerView() {
|
||||
|
@ -78,7 +85,7 @@ class PlaylistDetailsFragment : AbsMainActivityFragment(R.layout.fragment_playli
|
|||
playlist.playlistEntity,
|
||||
requireActivity(),
|
||||
ArrayList(),
|
||||
R.layout.item_list,
|
||||
R.layout.item_queue,
|
||||
this
|
||||
)
|
||||
|
||||
|
@ -158,29 +165,35 @@ class PlaylistDetailsFragment : AbsMainActivityFragment(R.layout.fragment_playli
|
|||
|
||||
private fun handleBackPress(): Boolean {
|
||||
cab?.let {
|
||||
if (it.isActive) {
|
||||
it.finish()
|
||||
if (it.isActive()) {
|
||||
it.destroy()
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
private var cab: MaterialCab? = null
|
||||
private var cab: AttachedCab? = null
|
||||
|
||||
override fun openCab(menuRes: Int, callback: MaterialCab.Callback): MaterialCab {
|
||||
override fun openCab(menuRes: Int, callback: ICabCallback): AttachedCab {
|
||||
cab?.let {
|
||||
println("Cab")
|
||||
if (it.isActive) {
|
||||
it.finish()
|
||||
if (it.isActive()) {
|
||||
it.destroy()
|
||||
}
|
||||
}
|
||||
cab = MaterialCab(mainActivity, R.id.cab_stub)
|
||||
.setMenu(menuRes)
|
||||
.setCloseDrawableRes(R.drawable.ic_close)
|
||||
.setBackgroundColor(RetroColorUtil.shiftBackgroundColorForLightText(surfaceColor()))
|
||||
.start(callback)
|
||||
return cab as MaterialCab
|
||||
cab = createCab(R.id.toolbar_container) {
|
||||
menu(menuRes)
|
||||
closeDrawable(R.drawable.ic_close)
|
||||
backgroundColor(literal = RetroColorUtil.shiftBackgroundColor(surfaceColor()))
|
||||
slideDown()
|
||||
onCreate { cab, menu -> callback.onCabCreated(cab, menu) }
|
||||
onSelection {
|
||||
callback.onCabItemClicked(it)
|
||||
}
|
||||
onDestroy { callback.onCabFinished(it) }
|
||||
}
|
||||
return cab as AttachedCab
|
||||
}
|
||||
|
||||
}
|
|
@ -25,21 +25,18 @@ import code.name.monkey.retromusic.EXTRA_PLAYLIST
|
|||
import code.name.monkey.retromusic.R
|
||||
import code.name.monkey.retromusic.adapter.playlist.PlaylistAdapter
|
||||
import code.name.monkey.retromusic.db.PlaylistWithSongs
|
||||
import code.name.monkey.retromusic.extensions.surfaceColor
|
||||
import code.name.monkey.retromusic.extensions.navigate
|
||||
import code.name.monkey.retromusic.fragments.ReloadType
|
||||
import code.name.monkey.retromusic.fragments.base.AbsRecyclerViewCustomGridSizeFragment
|
||||
import code.name.monkey.retromusic.helper.SortOrder.PlaylistSortOrder
|
||||
import code.name.monkey.retromusic.interfaces.ICabHolder
|
||||
import code.name.monkey.retromusic.interfaces.IPlaylistClickListener
|
||||
import code.name.monkey.retromusic.util.PreferenceUtil
|
||||
import code.name.monkey.retromusic.util.RetroColorUtil
|
||||
import com.afollestad.materialcab.MaterialCab
|
||||
import com.google.android.gms.cast.framework.CastButtonFactory
|
||||
import com.google.android.material.transition.MaterialSharedAxis
|
||||
|
||||
class PlaylistsFragment :
|
||||
AbsRecyclerViewCustomGridSizeFragment<PlaylistAdapter, GridLayoutManager>(),
|
||||
IPlaylistClickListener, ICabHolder {
|
||||
IPlaylistClickListener {
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
@ -50,18 +47,20 @@ class PlaylistsFragment :
|
|||
adapter?.swapDataSet(listOf())
|
||||
})
|
||||
requireActivity().onBackPressedDispatcher.addCallback(viewLifecycleOwner) {
|
||||
if (!handleBackPress()) {
|
||||
remove()
|
||||
requireActivity().onBackPressed()
|
||||
}
|
||||
remove()
|
||||
mainActivity.finish()
|
||||
}
|
||||
}
|
||||
|
||||
override val titleRes: Int
|
||||
get() = R.string.playlists
|
||||
|
||||
override val emptyMessage: Int
|
||||
get() = R.string.no_playlists
|
||||
|
||||
override val isShuffleVisible: Boolean
|
||||
get() = false
|
||||
|
||||
override fun createLayoutManager(): GridLayoutManager {
|
||||
return GridLayoutManager(requireContext(), getGridSize())
|
||||
}
|
||||
|
@ -71,7 +70,7 @@ class PlaylistsFragment :
|
|||
requireActivity(),
|
||||
ArrayList(),
|
||||
itemLayoutRes(),
|
||||
this,
|
||||
null,
|
||||
this
|
||||
)
|
||||
}
|
||||
|
@ -181,7 +180,7 @@ class PlaylistsFragment :
|
|||
}
|
||||
|
||||
override fun loadLayoutRes(): Int {
|
||||
return R.layout.item_grid
|
||||
return R.layout.item_card
|
||||
}
|
||||
|
||||
override fun saveLayoutRes(layoutRes: Int) {
|
||||
|
@ -198,31 +197,4 @@ class PlaylistsFragment :
|
|||
null
|
||||
)
|
||||
}
|
||||
|
||||
private fun handleBackPress(): Boolean {
|
||||
cab?.let {
|
||||
if (it.isActive) {
|
||||
it.finish()
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
private var cab: MaterialCab? = null
|
||||
|
||||
override fun openCab(menuRes: Int, callback: MaterialCab.Callback): MaterialCab {
|
||||
cab?.let {
|
||||
println("Cab")
|
||||
if (it.isActive) {
|
||||
it.finish()
|
||||
}
|
||||
}
|
||||
cab = MaterialCab(mainActivity, R.id.cab_stub)
|
||||
.setMenu(menuRes)
|
||||
.setCloseDrawableRes(R.drawable.ic_close)
|
||||
.setBackgroundColor(RetroColorUtil.shiftBackgroundColorForLightText(surfaceColor()))
|
||||
.start(callback)
|
||||
return cab as MaterialCab
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,80 +14,121 @@
|
|||
*/
|
||||
package code.name.monkey.retromusic.fragments.queue
|
||||
|
||||
import android.content.res.ColorStateList
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.navigation.findNavController
|
||||
import androidx.navigation.fragment.findNavController
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import code.name.monkey.appthemehelper.util.ColorUtil
|
||||
import code.name.monkey.appthemehelper.util.MaterialValueHelper
|
||||
import code.name.monkey.retromusic.R
|
||||
import code.name.monkey.retromusic.adapter.song.PlayingQueueAdapter
|
||||
import code.name.monkey.retromusic.fragments.base.AbsRecyclerViewFragment
|
||||
import code.name.monkey.retromusic.databinding.FragmentPlayingQueueBinding
|
||||
import code.name.monkey.retromusic.extensions.accentColor
|
||||
import code.name.monkey.retromusic.fragments.base.AbsMusicServiceFragment
|
||||
import code.name.monkey.retromusic.helper.MusicPlayerRemote
|
||||
import code.name.monkey.retromusic.util.MusicUtil
|
||||
import code.name.monkey.retromusic.util.ThemedFastScroller
|
||||
import com.h6ah4i.android.widget.advrecyclerview.animator.DraggableItemAnimator
|
||||
import com.h6ah4i.android.widget.advrecyclerview.draggable.RecyclerViewDragDropManager
|
||||
import com.h6ah4i.android.widget.advrecyclerview.swipeable.RecyclerViewSwipeManager
|
||||
import com.h6ah4i.android.widget.advrecyclerview.touchguard.RecyclerViewTouchActionGuardManager
|
||||
import com.h6ah4i.android.widget.advrecyclerview.utils.WrapperAdapterUtils
|
||||
|
||||
/**
|
||||
* Created by hemanths on 2019-12-08.
|
||||
*/
|
||||
class PlayingQueueFragment : AbsRecyclerViewFragment<PlayingQueueAdapter, LinearLayoutManager>() {
|
||||
class PlayingQueueFragment : AbsMusicServiceFragment(R.layout.fragment_playing_queue) {
|
||||
|
||||
private lateinit var wrappedAdapter: RecyclerView.Adapter<*>
|
||||
private var _binding: FragmentPlayingQueueBinding? = null
|
||||
private val binding get() = _binding!!
|
||||
private var wrappedAdapter: RecyclerView.Adapter<*>? = null
|
||||
private var recyclerViewDragDropManager: RecyclerViewDragDropManager? = null
|
||||
private var recyclerViewSwipeManager: RecyclerViewSwipeManager? = null
|
||||
private var recyclerViewTouchActionGuardManager: RecyclerViewTouchActionGuardManager? = null
|
||||
override val titleRes: Int
|
||||
get() = R.string.now_playing_queue
|
||||
private var playingQueueAdapter: PlayingQueueAdapter? = null
|
||||
private lateinit var linearLayoutManager: LinearLayoutManager
|
||||
|
||||
private fun getUpNextAndQueueTime(): String {
|
||||
val duration = MusicPlayerRemote.getQueueDurationMillis(MusicPlayerRemote.position)
|
||||
return MusicUtil.buildInfoString(
|
||||
resources.getString(R.string.up_next),
|
||||
MusicUtil.getReadableDurationString(duration)
|
||||
)
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
setupRecyclerView()
|
||||
_binding = FragmentPlayingQueueBinding.bind(view)
|
||||
|
||||
setupToolbar()
|
||||
setUpRecyclerView()
|
||||
|
||||
binding.clearQueue.setOnClickListener {
|
||||
MusicPlayerRemote.clearQueue()
|
||||
}
|
||||
checkForPadding()
|
||||
}
|
||||
|
||||
private fun setupRecyclerView() {
|
||||
private fun setUpRecyclerView() {
|
||||
recyclerViewTouchActionGuardManager = RecyclerViewTouchActionGuardManager()
|
||||
recyclerViewDragDropManager = RecyclerViewDragDropManager()
|
||||
recyclerViewSwipeManager = RecyclerViewSwipeManager()
|
||||
|
||||
val animator = DraggableItemAnimator()
|
||||
animator.supportsChangeAnimations = false
|
||||
wrappedAdapter =
|
||||
recyclerViewDragDropManager?.createWrappedAdapter(adapter!!) as RecyclerView.Adapter<*>
|
||||
wrappedAdapter =
|
||||
recyclerViewSwipeManager?.createWrappedAdapter(wrappedAdapter) as RecyclerView.Adapter<*>
|
||||
recyclerView().layoutManager = layoutManager
|
||||
recyclerView().adapter = wrappedAdapter
|
||||
recyclerView().itemAnimator = animator
|
||||
recyclerViewTouchActionGuardManager?.attachRecyclerView(recyclerView())
|
||||
recyclerViewDragDropManager?.attachRecyclerView(recyclerView())
|
||||
recyclerViewSwipeManager?.attachRecyclerView(recyclerView())
|
||||
|
||||
layoutManager?.scrollToPositionWithOffset(MusicPlayerRemote.position + 1, 0)
|
||||
}
|
||||
|
||||
override fun createLayoutManager(): LinearLayoutManager {
|
||||
return LinearLayoutManager(requireContext())
|
||||
}
|
||||
|
||||
override fun createAdapter(): PlayingQueueAdapter {
|
||||
return PlayingQueueAdapter(
|
||||
requireActivity() as AppCompatActivity,
|
||||
playingQueueAdapter = PlayingQueueAdapter(
|
||||
requireActivity(),
|
||||
MusicPlayerRemote.playingQueue.toMutableList(),
|
||||
MusicPlayerRemote.position,
|
||||
R.layout.item_queue
|
||||
)
|
||||
wrappedAdapter = recyclerViewDragDropManager?.createWrappedAdapter(playingQueueAdapter!!)
|
||||
wrappedAdapter = wrappedAdapter?.let { recyclerViewSwipeManager?.createWrappedAdapter(it) }
|
||||
|
||||
linearLayoutManager = LinearLayoutManager(requireContext())
|
||||
|
||||
binding.recyclerView.layoutManager = linearLayoutManager
|
||||
binding.recyclerView.adapter = wrappedAdapter
|
||||
binding.recyclerView.itemAnimator = animator
|
||||
recyclerViewTouchActionGuardManager?.attachRecyclerView(binding.recyclerView)
|
||||
recyclerViewDragDropManager?.attachRecyclerView(binding.recyclerView)
|
||||
recyclerViewSwipeManager?.attachRecyclerView(binding.recyclerView)
|
||||
linearLayoutManager.scrollToPositionWithOffset(MusicPlayerRemote.position + 1, 0)
|
||||
|
||||
binding.recyclerView.addOnScrollListener(object : RecyclerView.OnScrollListener() {
|
||||
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
|
||||
super.onScrolled(recyclerView, dx, dy)
|
||||
if (dy > 0) {
|
||||
binding.clearQueue.shrink()
|
||||
} else if (dy < 0) {
|
||||
binding.clearQueue.extend()
|
||||
}
|
||||
}
|
||||
})
|
||||
ThemedFastScroller.create(binding.recyclerView)
|
||||
}
|
||||
|
||||
override fun onServiceConnected() {
|
||||
super.onServiceConnected()
|
||||
updateQueue()
|
||||
private fun checkForPadding() {
|
||||
}
|
||||
|
||||
override fun onQueueChanged() {
|
||||
super.onQueueChanged()
|
||||
if (MusicPlayerRemote.playingQueue.isEmpty()) {
|
||||
findNavController().navigateUp()
|
||||
return
|
||||
}
|
||||
checkForPadding()
|
||||
updateQueue()
|
||||
updateCurrentSong()
|
||||
}
|
||||
|
||||
override fun onMediaStoreChanged() {
|
||||
updateQueue()
|
||||
updateCurrentSong()
|
||||
}
|
||||
|
||||
private fun updateCurrentSong() {
|
||||
binding.toolbar.subtitle = getUpNextAndQueueTime()
|
||||
}
|
||||
|
||||
override fun onPlayingMetaChanged() {
|
||||
|
@ -95,50 +136,62 @@ class PlayingQueueFragment : AbsRecyclerViewFragment<PlayingQueueAdapter, Linear
|
|||
}
|
||||
|
||||
private fun updateQueuePosition() {
|
||||
adapter?.setCurrent(MusicPlayerRemote.position)
|
||||
playingQueueAdapter?.setCurrent(MusicPlayerRemote.position)
|
||||
resetToCurrentPosition()
|
||||
binding.toolbar.subtitle = getUpNextAndQueueTime()
|
||||
}
|
||||
|
||||
private fun updateQueue() {
|
||||
adapter?.swapDataSet(MusicPlayerRemote.playingQueue, MusicPlayerRemote.position)
|
||||
resetToCurrentPosition()
|
||||
playingQueueAdapter?.swapDataSet(MusicPlayerRemote.playingQueue, MusicPlayerRemote.position)
|
||||
}
|
||||
|
||||
private fun resetToCurrentPosition() {
|
||||
recyclerView().stopScroll()
|
||||
layoutManager?.scrollToPositionWithOffset(MusicPlayerRemote.position + 1, 0)
|
||||
binding.recyclerView.stopScroll()
|
||||
linearLayoutManager.scrollToPositionWithOffset(MusicPlayerRemote.position + 1, 0)
|
||||
}
|
||||
|
||||
override fun onPause() {
|
||||
recyclerViewDragDropManager?.cancelDrag()
|
||||
if (recyclerViewDragDropManager != null) {
|
||||
recyclerViewDragDropManager!!.cancelDrag()
|
||||
}
|
||||
super.onPause()
|
||||
}
|
||||
|
||||
override val emptyMessage: Int
|
||||
get() = R.string.no_playing_queue
|
||||
|
||||
override fun onDestroyView() {
|
||||
super.onDestroyView()
|
||||
override fun onDestroy() {
|
||||
if (recyclerViewDragDropManager != null) {
|
||||
recyclerViewDragDropManager?.release()
|
||||
recyclerViewDragDropManager!!.release()
|
||||
recyclerViewDragDropManager = null
|
||||
}
|
||||
|
||||
if (recyclerViewSwipeManager != null) {
|
||||
recyclerViewSwipeManager?.release()
|
||||
recyclerViewSwipeManager = null
|
||||
}
|
||||
|
||||
WrapperAdapterUtils.releaseAll(wrappedAdapter)
|
||||
if (wrappedAdapter != null) {
|
||||
WrapperAdapterUtils.releaseAll(wrappedAdapter)
|
||||
wrappedAdapter = null
|
||||
}
|
||||
playingQueueAdapter = null
|
||||
super.onDestroy()
|
||||
}
|
||||
|
||||
companion object {
|
||||
@JvmField
|
||||
val TAG: String = PlayingQueueFragment::class.java.simpleName
|
||||
|
||||
@JvmStatic
|
||||
fun newInstance(): PlayingQueueFragment {
|
||||
return PlayingQueueFragment()
|
||||
private fun setupToolbar() {
|
||||
binding.toolbar.subtitle = getUpNextAndQueueTime()
|
||||
binding.clearQueue.backgroundTintList = ColorStateList.valueOf(accentColor())
|
||||
ColorStateList.valueOf(
|
||||
MaterialValueHelper.getPrimaryTextColor(
|
||||
requireContext(),
|
||||
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
Loading…
Reference in New Issue