This commit is contained in:
Muntashir Al-Islam 2020-08-19 13:40:58 +06:00
commit 3c3cbf08a9
226 changed files with 4395 additions and 5422 deletions

42
.github/ISSUE_TEMPLATE/bug_report.md vendored Normal file
View file

@ -0,0 +1,42 @@
---
name: Bug report
about: Create a report to help us improve. Please write in English only.
title: ''
labels: bug
assignees: ''
---
**Have you read the [FAQ](https://www.github.com/h4h13/RetroMusicPlayer/tree/dev/FAQ.md)?**
[Yes/No]
**Describe the bug**
A clear and concise description of what the bug is.
**How To Reproduce**
Steps to reproduce the behavior:
1.
2.
3.
4.
**Expected behavior**
A clear and concise description of what you expected to happen.
**Screenshots**
If applicable, add screenshots to help explain your problem.
**Crash log**
If the app is crashing, add a crash log
<details>
<summary>Click to view logs</summary>
PASTE YOUR LOGS HERE.
</details>
**Device info:**
- Device: [e.g. OnePlus 7]
- Android version: [e.g. Android 9]
- App version [e.g. 3.5.300_0517]
**Additional context**
Add any other context about the problem here.

View file

@ -0,0 +1,20 @@
---
name: Feature request
about: Suggest an idea for this project. Please write in English only.
title: ''
labels: enhancement
assignees: ''
---
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.
**Additional context**
Add any other context or screenshots about the feature request here.

76
CODE_OF_CONDUCT.md Normal file
View file

@ -0,0 +1,76 @@
# Contributor Covenant Code of Conduct
## Our Pledge
In the interest of fostering an open and welcoming environment, we as
contributors and maintainers pledge to making participation in our project and
our community a harassment-free experience for everyone, regardless of age, body
size, disability, ethnicity, sex characteristics, gender identity and expression,
level of experience, education, socio-economic status, nationality, personal
appearance, race, religion, or sexual identity and orientation.
## Our Standards
Examples of behavior that contributes to creating a positive environment
include:
* Using welcoming and inclusive language
* Being respectful of differing viewpoints and experiences
* Gracefully accepting constructive criticism
* Focusing on what is best for the community
* Showing empathy towards other community members
Examples of unacceptable behavior by participants include:
* The use of sexualized language or imagery and unwelcome sexual attention or
advances
* Trolling, insulting/derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or electronic
address, without explicit permission
* Other conduct which could reasonably be considered inappropriate in a
professional setting
## Our Responsibilities
Project maintainers are responsible for clarifying the standards of acceptable
behavior and are expected to take appropriate and fair corrective action in
response to any instances of unacceptable behavior.
Project maintainers have the right and responsibility to remove, edit, or
reject comments, commits, code, wiki edits, issues, and other contributions
that are not aligned to this Code of Conduct, or to ban temporarily or
permanently any contributor for other behaviors that they deem inappropriate,
threatening, offensive, or harmful.
## Scope
This Code of Conduct applies both within project spaces and in public spaces
when an individual is representing the project or its community. Examples of
representing a project or community include using an official project e-mail
address, posting via an official social media account, or acting as an appointed
representative at an online or offline event. Representation of a project may be
further defined and clarified by project maintainers.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported by contacting the project team at retromusicapp@gmail.com. All
complaints will be reviewed and investigated and will result in a response that
is deemed necessary and appropriate to the circumstances. The project team is
obligated to maintain confidentiality with regard to the reporter of an incident.
Further details of specific enforcement policies may be posted separately.
Project maintainers who do not follow or enforce the Code of Conduct in good
faith may face temporary or permanent repercussions as determined by other
members of the project's leadership.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
[homepage]: https://www.contributor-covenant.org
For answers to common questions about this code of conduct, see
https://www.contributor-covenant.org/faq

35
CONTRIBUTING.md Normal file
View file

@ -0,0 +1,35 @@
# Contributing
## Using the issue tracker
The [issue tracker](https://github.com/h4h13/RetroMusicPlayer/issues) is the preferred channel for bug reports, feature requests and submitting pull requests, but please follow these rules:
* Please **do not** derail or troll issues. Keep the discussion on topic and respect the opinions of others.
* Please **do not** post comments consisting solely of "+1" or "👍". Use [GitHub's "reactions" feature](https://github.com/blog/2119-add-reactions-to-pull-requests-issues-and-comments) instead.
* Please **do not** write [enhancement]/[bug]/[feature request] or similar stuff in the title of issues, as there are labels for that purpose that will be added by devs or collaborators.
## Bug reports
A bug is a _demonstrable problem_. Good bug reports are extremely helpful, so thanks!
Guidelines for bug reports:
* Use the GitHub issue search, check if the issue has already been reported both in the open issues and in the closed ones, if there are some missing details, add them in issue comments, without creating a new one
* Check if the issue has been fixed — try to reproduce it using the latest available build
* Isolate the problem — ideally create a reproducible scenario and a live example
* Do not report multiple bugs in a single ticket, otherwise it will be confusing.
A good bug report shouldn't leave others needing to chase you up for more information. Please try to be as detailed as possible in your report.
## Feature requests
Feature requests are welcome, please make sure to be as detailed as possible and use screenshots, videos, GIFs, to demonstrate it better, if possible.
## Pull requests
**Please ask first** before embarking on any significant pull request (e.g. implementing features, refactoring code), otherwise you risk spending a lot of time working on something that developers might not want to merge into the project. To avoid that, you can join the official [Telegram group](https://t.me/retromusicapp) or open an issue.
## License
By contributing your code, you agree to license your contribution under the [GNU General Public License](https://github.com/h4h13/RetroMusicPlayer/blob/dev/LICENSE.md).
Please note we have a code of conduct, please follow it in all your interactions with the project.

5
FAQ.md
View file

@ -31,8 +31,9 @@ A ".lrc" is like a text file which contains the time stamped lyrics for example,
##### STEP 3: ##### STEP 3:
Now you have to rename the file you created in this way: <song_name> - <artist_name>.lrc or for better matching copy the <song_name> and the <artist_name> from the tag editor and then rename the file. Now you have to rename the file you created in this way: <song_name> - <artist_name>.lrc or for better matching copy the <song_name> and the <artist_name> from the tag editor and then rename the file.
##### STEP 4: ##### STEP 4:
You have to copy or move this file into a location on the SD Card: whatever_sdcard/RetroMusic/lyrics/ and paste it there. Now paste the LRC files to the following path : /sdcard/Retromusic/lyrics/
Finally you will be able to see the lyrics working. Here sdcard is your internal storage.
> If you want to skip to particular time stamp, simply scroll to the time stamp from where you want to start from and a 'Play' icon will appear left to the particular stamp. Tap on play button to play from there. > If you want to skip to particular time stamp, simply scroll to the time stamp from where you want to start from and a 'Play' icon will appear left to the particular stamp. Tap on play button to play from there.

View file

@ -29,50 +29,50 @@ Material Design music player for Android music lovers
| Classic | Adaptive | Blur | Tiny | Peak | | Classic | Adaptive | Blur | Tiny | Peak |
### 🧭 Navigation never made easier ### 🧭 Navigation never made easier
Self-explanatory interface without overloaded menus Self-explanatory interface without overloaded menus.
### 🎨 Colorful ### 🎨 Colorful
You can choose between three different main themes: Clearly white, Kind You can choose between three different main themes: Clearly White, Kinda
dark and Just black for AMOLED displays. Select your favorite accent Dark and Just Black for AMOLED displays. Select your favorite accent
color from a color palette. color from a color palette.
### 🏠 Home ### 🏠 Home
Where you can have your recently/ top played Artists, Albums and Where you can have your recently/top played artists, albums and
Favorite Songs. No other music player has this feature favorite songs. No other music player has this feature.
### 📦 Included Features ### 📦 Included Features
- Base 3 themes (Clearly white, Kinda dark and Just Black) - Base 3 themes (Clearly White, Kinda Dark and Just Black)
- Choose from 10+ now playing themes - Choose from 10+ now playing themes
- Drive Mode - Driving Mode
- Headset/Bluetooth support - Headset/Bluetooth support
- Music Duration Filter - Music duration filter
- Folder support - Play song by folder - Folder support - Play song by folder
- Gapless playback - Gapless playback
- Volume controls - Volume controls
- More than 10 Now playing themes
- Carousel effect for an album cover - Carousel effect for an album cover
- Home screen Widgets - Home screen widgets
- Lock Screen Playback Controls - Lock screen playback controls
- Lyrics Screen(download and sync with music) - Lyrics screen (download and sync with music)
- Sleep Timer - Sleep Timer
- Home screen Widgets - Easy drag to sort playlist & play queue
- Easy Drag to Sort Playlist & Play Queue
- Tag editor - Tag editor
- Create, Edit, Import playlists - Create, edit and import playlists
- Playing queue with reorder - Playing queue with reorder
- User profile - User profile
- 30 Languages support - 30 Languages support
- Browse and play your music by Songs, Albums, Artists, Playlists, - Browse and play your music by songs, albums, artists, playlists and
Genre genre
- Smart Auto Playlists - Recently played/Top Played/History Fully - Smart Auto Playlists - Recently played, most played and history
playlist support & Build your own playlist on the go - Build your own playlist on the go
We are trying our best to bring you the best user experience. Until now We are trying our best to bring you the best user experience. The app is regulary being updated for bug fixes and new features.
it is a beta version - bug fixes (if any) and more features are on the
way. for FAQ's https://goo.gl/DR2mE2
### 🗂️ License ### 🗂️ License
Metro is released under the GNU General Public License v3.0 Metro is released under the GNU General Public License v3.0
(GPLv3), which can be found here: [License](LICENSE.md) (GPLv3), which can be found here: [License](LICENSE.md)
>Please note: Retro Music player is an offline music player app. It
>doesn't support music downloading or online music streaming.

View file

@ -80,17 +80,18 @@ dependencies {
implementation "androidx.gridlayout:gridlayout:1.0.0" implementation "androidx.gridlayout:gridlayout:1.0.0"
implementation "androidx.cardview:cardview:1.0.0" implementation "androidx.cardview:cardview:1.0.0"
implementation "androidx.viewpager2:viewpager2:1.1.0-alpha01" implementation "androidx.viewpager2:viewpager2:1.1.0-alpha01"
implementation 'androidx.appcompat:appcompat:1.1.0' implementation 'androidx.appcompat:appcompat:1.2.0'
implementation 'androidx.annotation:annotation:1.1.0' implementation 'androidx.annotation:annotation:1.1.0'
implementation 'androidx.preference:preference-ktx:1.1.1' implementation 'androidx.preference:preference-ktx:1.1.1'
implementation 'androidx.core:core-ktx:1.3.1' implementation 'androidx.core:core-ktx:1.3.1'
implementation 'androidx.fragment:fragment-ktx:1.2.5' implementation 'androidx.fragment:fragment-ktx:1.2.5'
implementation 'androidx.palette:palette-ktx:1.0.0' implementation 'androidx.palette:palette-ktx:1.0.0'
implementation 'androidx.constraintlayout:constraintlayout:2.0.0-beta8' implementation 'androidx.constraintlayout:constraintlayout:2.0.0-rc1'
implementation 'androidx.recyclerview:recyclerview:1.1.0' implementation 'androidx.recyclerview:recyclerview:1.1.0'
implementation 'com.google.android.material:material:1.3.0-alpha01' implementation 'com.google.android.material:material:1.3.0-alpha02'
implementation 'androidx.legacy:legacy-support-v4:1.0.0'
def retrofit_version = '2.9.0' def retrofit_version = '2.9.0'
implementation "com.squareup.retrofit2:retrofit:$retrofit_version" implementation "com.squareup.retrofit2:retrofit:$retrofit_version"
@ -144,5 +145,4 @@ dependencies {
def nav_version = "2.3.0" def nav_version = "2.3.0"
implementation "androidx.navigation:navigation-fragment-ktx:$nav_version" implementation "androidx.navigation:navigation-fragment-ktx:$nav_version"
implementation "androidx.navigation:navigation-ui-ktx:$nav_version" implementation "androidx.navigation:navigation-ui-ktx:$nav_version"
} }

View file

@ -1,20 +0,0 @@
{
"version": 1,
"artifactType": {
"type": "APK",
"kind": "Directory"
},
"applicationId": "code.name.monkey.retromusic",
"variantName": "release",
"elements": [
{
"type": "SINGLE",
"filters": [],
"properties": [],
"versionCode": 10438,
"versionName": "10438",
"enabled": true,
"outputFile": "app-release.apk"
}
]
}

View file

@ -20,12 +20,12 @@
<application <application
android:name=".App" android:name=".App"
android:allowBackup="true" android:allowBackup="true"
android:configChanges="locale|layoutDirection"
android:icon="@mipmap/ic_launcher" android:icon="@mipmap/ic_launcher"
android:label="@string/app_name" android:label="@string/app_name"
android:requestLegacyExternalStorage="true" android:requestLegacyExternalStorage="true"
android:roundIcon="@mipmap/ic_launcher_round" android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true" android:supportsRtl="true"
android:configChanges="locale|layoutDirection"
android:theme="@style/Theme.RetroMusic.FollowSystem" android:theme="@style/Theme.RetroMusic.FollowSystem"
android:usesCleartextTraffic="false" android:usesCleartextTraffic="false"
tools:ignore="AllowBackup,GoogleAppIndexingWarning" tools:ignore="AllowBackup,GoogleAppIndexingWarning"
@ -104,25 +104,17 @@
<data android:mimeType="vnd.android.cursor.dir/audio" /> <data android:mimeType="vnd.android.cursor.dir/audio" />
</intent-filter> </intent-filter>
</activity> </activity>
<activity android:name=".activities.albums.AlbumDetailsActivity" />
<activity android:name=".activities.artists.ArtistDetailActivity" />
<activity android:name=".activities.playlist.PlaylistDetailActivity" />
<activity android:name=".activities.PlayingQueueActivity" /> <activity android:name=".activities.PlayingQueueActivity" />
<activity android:name=".activities.AboutActivity" /> <activity android:name=".activities.SettingsActivity" />
<activity android:name=".activities.tageditor.AlbumTagEditorActivity" /> <activity android:name=".activities.tageditor.AlbumTagEditorActivity" />
<activity android:name=".activities.tageditor.SongTagEditorActivity" /> <activity android:name=".activities.tageditor.SongTagEditorActivity" />
<activity android:name=".activities.SettingsActivity" />
<activity android:name=".activities.LyricsActivity" /> <activity android:name=".activities.LyricsActivity" />
<activity android:name=".activities.UserInfoActivity" /> <activity android:name=".activities.UserInfoActivity" />
<activity android:name=".activities.GenreDetailsActivity" />
<activity android:name=".activities.LicenseActivity" /> <activity android:name=".activities.LicenseActivity" />
<activity android:name=".activities.WhatsNewActivity" /> <activity android:name=".activities.WhatsNewActivity" />
<activity android:name=".activities.bugreport.BugReportActivity" /> <activity android:name=".activities.bugreport.BugReportActivity" />
<activity android:name=".activities.ShareInstagramStory" /> <activity android:name=".activities.ShareInstagramStory" />
<activity android:name=".activities.DriveModeActivity" /> <activity android:name=".activities.DriveModeActivity" />
<activity
android:name=".activities.search.SearchActivity"
android:windowSoftInputMode="stateVisible" />
<activity <activity
android:name=".activities.LockScreenActivity" android:name=".activities.LockScreenActivity"
@ -263,5 +255,8 @@
<meta-data <meta-data
android:name="com.android.vending.splits.required" android:name="com.android.vending.splits.required"
android:value="true" /> android:value="true" />
<meta-data
android:name="preloaded_fonts"
android:resource="@array/preloaded_fonts" />
</application> </application>
</manifest> </manifest>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 43 KiB

Before After
Before After

View file

@ -47,13 +47,17 @@ object Constants {
MediaStore.Audio.AudioColumns.ALBUM, // 8 MediaStore.Audio.AudioColumns.ALBUM, // 8
MediaStore.Audio.AudioColumns.ARTIST_ID, // 9 MediaStore.Audio.AudioColumns.ARTIST_ID, // 9
MediaStore.Audio.AudioColumns.ARTIST,// 10 MediaStore.Audio.AudioColumns.ARTIST,// 10
MediaStore.Audio.AudioColumns.COMPOSER// 11 MediaStore.Audio.AudioColumns.COMPOSER,// 11
"album_artist"//12
) )
const val NUMBER_OF_TOP_TRACKS = 99 const val NUMBER_OF_TOP_TRACKS = 99
} }
const val EXTRA_GENRE = "extra_genre"
const val EXTRA_PLAYLIST = "extra_playlist"
const val EXTRA_ALBUM_ID = "extra_album_id"
const val EXTRA_ARTIST_ID = "extra_artist_id"
const val EXTRA_SONG = "extra_songs" const val EXTRA_SONG = "extra_songs"
const val EXTRA_PLAYLIST = "extra_list"
const val LIBRARY_CATEGORIES = "library_categories" const val LIBRARY_CATEGORIES = "library_categories"
const val EXTRA_SONG_INFO = "extra_song_info" const val EXTRA_SONG_INFO = "extra_song_info"
const val DESATURATED_COLOR = "desaturated_color" const val DESATURATED_COLOR = "desaturated_color"

View file

@ -0,0 +1,25 @@
package io.github.muntashirakon.music
import androidx.annotation.IntDef
@IntDef(
RECENT_ALBUMS,
TOP_ALBUMS,
RECENT_ARTISTS,
TOP_ARTISTS,
SUGGESTIONS,
FAVOURITES,
GENRES,
PLAYLISTS
)
@Retention(AnnotationRetention.SOURCE)
annotation class HomeSection
const val RECENT_ALBUMS = 3
const val TOP_ALBUMS = 1
const val RECENT_ARTISTS = 2
const val TOP_ARTISTS = 0
const val SUGGESTIONS = 5
const val FAVOURITES = 4
const val GENRES = 6
const val PLAYLISTS = 7

View file

@ -1,24 +1,70 @@
package io.github.muntashirakon.music package io.github.muntashirakon.music
import io.github.muntashirakon.music.activities.albums.AlbumDetailsViewModel
import io.github.muntashirakon.music.activities.artists.ArtistDetailsViewModel
import io.github.muntashirakon.music.activities.genre.GenreDetailsViewModel
import io.github.muntashirakon.music.activities.playlist.PlaylistDetailsViewModel
import io.github.muntashirakon.music.activities.search.SearchViewModel
import io.github.muntashirakon.music.fragments.LibraryViewModel import io.github.muntashirakon.music.fragments.LibraryViewModel
import io.github.muntashirakon.music.fragments.albums.AlbumDetailsViewModel
import io.github.muntashirakon.music.fragments.artists.ArtistDetailsViewModel
import io.github.muntashirakon.music.fragments.genres.GenreDetailsViewModel
import io.github.muntashirakon.music.fragments.playlists.PlaylistDetailsViewModel
import io.github.muntashirakon.music.fragments.search.SearchViewModel
import io.github.muntashirakon.music.model.Genre import io.github.muntashirakon.music.model.Genre
import io.github.muntashirakon.music.model.Playlist import io.github.muntashirakon.music.model.Playlist
import io.github.muntashirakon.music.network.networkModule import io.github.muntashirakon.music.network.networkModule
import io.github.muntashirakon.music.providers.RepositoryImpl import io.github.muntashirakon.music.repository.*
import org.eclipse.egit.github.core.Repository import org.koin.android.ext.koin.androidContext
import org.koin.androidx.viewmodel.dsl.viewModel import org.koin.androidx.viewmodel.dsl.viewModel
import org.koin.dsl.bind import org.koin.dsl.bind
import org.koin.dsl.module import org.koin.dsl.module
private val dataModule = module { private val dataModule = module {
single { single {
RepositoryImpl(get(), get()) RealRepository(get(), get(), get(), get(), get(), get(), get(), get(), get(), get())
} bind Repository::class } bind Repository::class
single {
RealSongRepository(get())
} bind SongRepository::class
single {
RealGenreRepository(get(), get())
} bind GenreRepository::class
single {
RealAlbumRepository(get())
} bind AlbumRepository::class
single {
RealArtistRepository(get(), get())
} bind ArtistRepository::class
single {
RealPlaylistRepository(get())
} bind PlaylistRepository::class
single {
RealTopPlayedRepository(get(), get(), get(), get())
} bind TopPlayedRepository::class
single {
RealLastAddedRepository(
get(),
get(),
get()
)
} bind LastAddedRepository::class
single {
RealSearchRepository(
get(),
get(),
get(),
get(),
get()
)
}
single {
androidContext().contentResolver
}
} }
private val viewModules = module { private val viewModules = module {
@ -28,19 +74,31 @@ private val viewModules = module {
} }
viewModel { (albumId: Int) -> viewModel { (albumId: Int) ->
AlbumDetailsViewModel(get(), albumId) AlbumDetailsViewModel(
get(),
albumId
)
} }
viewModel { (artistId: Int) -> viewModel { (artistId: Int) ->
ArtistDetailsViewModel(get(), artistId) ArtistDetailsViewModel(
get(),
artistId
)
} }
viewModel { (playlist: Playlist) -> viewModel { (playlist: Playlist) ->
PlaylistDetailsViewModel(get(), playlist) PlaylistDetailsViewModel(
get(),
playlist
)
} }
viewModel { (genre: Genre) -> viewModel { (genre: Genre) ->
GenreDetailsViewModel(get(), genre) GenreDetailsViewModel(
get(),
genre
)
} }
viewModel { viewModel {

View file

@ -0,0 +1,25 @@
/*
* Copyright (C) 2020 Fatih Giris. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.github.muntashirakon.music
/**
* Generic class that holds the network state
*/
sealed class Result<out R> {
data class Success<out T>(val data: T) : Result<T>()
object Loading : Result<Nothing>()
object Error : Result<Nothing>()
}

View file

@ -20,7 +20,6 @@ import kotlinx.android.synthetic.main.activity_lyrics.*
class LyricsActivity : AbsMusicServiceActivity(), MusicProgressViewUpdateHelper.Callback { class LyricsActivity : AbsMusicServiceActivity(), MusicProgressViewUpdateHelper.Callback {
private lateinit var updateHelper: MusicProgressViewUpdateHelper private lateinit var updateHelper: MusicProgressViewUpdateHelper
private lateinit var song: Song private lateinit var song: Song
private val googleSearchLrcUrl: String private val googleSearchLrcUrl: String

View file

@ -1,83 +1,44 @@
package io.github.muntashirakon.music.activities package io.github.muntashirakon.music.activities
import android.app.ActivityOptions import android.content.Intent
import android.content.* import android.content.SharedPreferences
import android.content.SharedPreferences.OnSharedPreferenceChangeListener
import android.os.Bundle import android.os.Bundle
import android.provider.MediaStore import android.provider.MediaStore
import android.util.Log import android.util.Log
import android.view.Menu
import android.view.MenuItem
import android.view.SubMenu
import android.view.View import android.view.View
import androidx.core.app.ActivityCompat import androidx.lifecycle.lifecycleScope
import androidx.fragment.app.Fragment import io.github.muntashirakon.music.*
import androidx.fragment.app.commit
import code.name.monkey.appthemehelper.util.ATHUtil.resolveColor
import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper
import io.github.muntashirakon.music.R
import io.github.muntashirakon.music.activities.base.AbsSlidingMusicPanelActivity import io.github.muntashirakon.music.activities.base.AbsSlidingMusicPanelActivity
import io.github.muntashirakon.music.dialogs.CreatePlaylistDialog.Companion.create import io.github.muntashirakon.music.extensions.findNavController
import io.github.muntashirakon.music.fragments.LibraryViewModel import io.github.muntashirakon.music.fragments.LibraryViewModel
import io.github.muntashirakon.music.fragments.albums.AlbumsFragment
import io.github.muntashirakon.music.fragments.artists.ArtistsFragment
import io.github.muntashirakon.music.fragments.base.AbsRecyclerViewCustomGridSizeFragment
import io.github.muntashirakon.music.fragments.folder.FoldersFragment
import io.github.muntashirakon.music.fragments.genres.GenresFragment
import io.github.muntashirakon.music.fragments.home.BannerHomeFragment
import io.github.muntashirakon.music.fragments.playlists.PlaylistsFragment
import io.github.muntashirakon.music.fragments.queue.PlayingQueueFragment
import io.github.muntashirakon.music.fragments.songs.SongsFragment
import io.github.muntashirakon.music.helper.MusicPlayerRemote.isPlaying
import io.github.muntashirakon.music.helper.MusicPlayerRemote.openAndShuffleQueue import io.github.muntashirakon.music.helper.MusicPlayerRemote.openAndShuffleQueue
import io.github.muntashirakon.music.helper.MusicPlayerRemote.openQueue import io.github.muntashirakon.music.helper.MusicPlayerRemote.openQueue
import io.github.muntashirakon.music.helper.MusicPlayerRemote.playFromUri import io.github.muntashirakon.music.helper.MusicPlayerRemote.playFromUri
import io.github.muntashirakon.music.helper.MusicPlayerRemote.shuffleMode import io.github.muntashirakon.music.helper.MusicPlayerRemote.shuffleMode
import io.github.muntashirakon.music.helper.SearchQueryHelper.getSongs import io.github.muntashirakon.music.helper.SearchQueryHelper.getSongs
import io.github.muntashirakon.music.helper.SortOrder.*
import io.github.muntashirakon.music.interfaces.CabHolder
import io.github.muntashirakon.music.interfaces.MainActivityFragmentCallbacks
import io.github.muntashirakon.music.loaders.AlbumLoader.getAlbum
import io.github.muntashirakon.music.loaders.ArtistLoader.getArtist
import io.github.muntashirakon.music.loaders.PlaylistSongsLoader.getPlaylistSongList
import io.github.muntashirakon.music.model.Song import io.github.muntashirakon.music.model.Song
import io.github.muntashirakon.music.repository.PlaylistSongsLoader.getPlaylistSongList
import io.github.muntashirakon.music.repository.Repository
import io.github.muntashirakon.music.service.MusicService import io.github.muntashirakon.music.service.MusicService
import io.github.muntashirakon.music.util.AppRater.appLaunched import io.github.muntashirakon.music.util.AppRater.appLaunched
import io.github.muntashirakon.music.util.NavigationUtil
import io.github.muntashirakon.music.util.PreferenceUtil import io.github.muntashirakon.music.util.PreferenceUtil
import io.github.muntashirakon.music.util.RetroColorUtil import kotlinx.coroutines.Dispatchers
import io.github.muntashirakon.music.util.RetroUtil import kotlinx.coroutines.launch
import com.afollestad.materialcab.MaterialCab
import com.google.android.material.appbar.AppBarLayout
import io.github.muntashirakon.music.*
import kotlinx.android.synthetic.main.activity_main_content.*
import org.koin.android.ext.android.inject import org.koin.android.ext.android.inject
import java.util.* import java.util.*
class MainActivity : AbsSlidingMusicPanelActivity(), class MainActivity : AbsSlidingMusicPanelActivity(), OnSharedPreferenceChangeListener {
SharedPreferences.OnSharedPreferenceChangeListener, CabHolder {
companion object { companion object {
const val TAG = "MainActivity" const val TAG = "MainActivity"
const val EXPAND_PANEL = "expand_panel" const val EXPAND_PANEL = "expand_panel"
const val APP_UPDATE_REQUEST_CODE = 9002
} }
private val libraryViewModel: LibraryViewModel by inject() private val repository by inject<Repository>()
private var cab: MaterialCab? = null private val libraryViewModel by inject<LibraryViewModel>()
private val intentFilter = IntentFilter(Intent.ACTION_SCREEN_OFF)
private lateinit var currentFragment: MainActivityFragmentCallbacks
private var blockRequestPermissions = false private var blockRequestPermissions = false
private val broadcastReceiver: BroadcastReceiver = object : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
val action = intent.action
if (action != null && action == Intent.ACTION_SCREEN_OFF) {
if (PreferenceUtil.isLockScreen && isPlaying) {
val activity = Intent(context, LockScreenActivity::class.java)
activity.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
activity.addFlags(Intent.FLAG_ACTIVITY_NO_HISTORY)
ActivityCompat.startActivity(context, activity, null)
}
}
}
}
override fun createContentView(): View { override fun createContentView(): View {
return wrapSlidingMusicPanel(R.layout.activity_main_content) return wrapSlidingMusicPanel(R.layout.activity_main_content)
@ -91,29 +52,16 @@ class MainActivity : AbsSlidingMusicPanelActivity(),
setLightNavigationBar(true) setLightNavigationBar(true)
setTaskDescriptionColorAuto() setTaskDescriptionColorAuto()
hideStatusBar() hideStatusBar()
setBottomBarVisibility(View.VISIBLE)
addMusicServiceEventListener(libraryViewModel)
if (savedInstanceState == null) {
selectedFragment(PreferenceUtil.lastPage)
} else {
restoreCurrentFragment()
}
appLaunched(this) appLaunched(this)
setupToolbar() addMusicServiceEventListener(libraryViewModel)
updateTabs() updateTabs()
getBottomNavigationView().selectedItemId = PreferenceUtil.lastPage
getBottomNavigationView().setOnNavigationItemSelectedListener {
PreferenceUtil.lastPage = it.itemId
selectedFragment(it.itemId)
true
}
} }
override fun onSupportNavigateUp(): Boolean =
findNavController(R.id.fragment_container).navigateUp()
override fun onResume() { override fun onResume() {
super.onResume() super.onResume()
registerReceiver(broadcastReceiver, intentFilter)
PreferenceUtil.registerOnSharedPreferenceChangedListener(this) PreferenceUtil.registerOnSharedPreferenceChangedListener(this)
if (intent.hasExtra(EXPAND_PANEL) && if (intent.hasExtra(EXPAND_PANEL) &&
intent.getBooleanExtra(EXPAND_PANEL, false) && intent.getBooleanExtra(EXPAND_PANEL, false) &&
@ -126,389 +74,9 @@ class MainActivity : AbsSlidingMusicPanelActivity(),
override fun onDestroy() { override fun onDestroy() {
super.onDestroy() super.onDestroy()
unregisterReceiver(broadcastReceiver)
PreferenceUtil.unregisterOnSharedPreferenceChangedListener(this) PreferenceUtil.unregisterOnSharedPreferenceChangedListener(this)
} }
override fun onPrepareOptionsMenu(menu: Menu?): Boolean {
ToolbarContentTintHelper.handleOnPrepareOptionsMenu(this, toolbar)
return super.onPrepareOptionsMenu(menu)
}
override fun onCreateOptionsMenu(menu: Menu?): Boolean {
menuInflater.inflate(R.menu.menu_main, menu)
menu ?: return super.onCreateOptionsMenu(menu)
if (isPlaylistPage()) {
menu.add(0, R.id.action_new_playlist, 1, R.string.new_playlist_title)
.setIcon(R.drawable.ic_playlist_add)
.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM)
}
if (isHomePage()) {
menu.add(0, R.id.action_mic, 1, getString(R.string.action_search))
.setIcon(R.drawable.ic_mic)
.setShowAsActionFlags(MenuItem.SHOW_AS_ACTION_IF_ROOM)
}
if (isFolderPage()) {
menu.add(0, R.id.action_scan, 0, R.string.scan_media)
.setIcon(R.drawable.ic_scanner)
.setShowAsActionFlags(MenuItem.SHOW_AS_ACTION_IF_ROOM)
menu.add(0, R.id.action_go_to_start_directory, 1, R.string.action_go_to_start_directory)
.setIcon(R.drawable.ic_bookmark_music)
.setShowAsActionFlags(MenuItem.SHOW_AS_ACTION_IF_ROOM)
}
val fragment: Fragment? = getCurrentFragment()
if (fragment != null && fragment is AbsRecyclerViewCustomGridSizeFragment<*, *>) {
val gridSizeItem: MenuItem = menu.findItem(R.id.action_grid_size)
if (RetroUtil.isLandscape()) {
gridSizeItem.setTitle(R.string.action_grid_size_land)
}
setUpGridSizeMenu(fragment, gridSizeItem.subMenu)
setupLayoutMenu(fragment, menu.findItem(R.id.action_layout_type).subMenu)
setUpSortOrderMenu(fragment, menu.findItem(R.id.action_sort_order).subMenu)
} else {
menu.removeItem(R.id.action_layout_type)
menu.removeItem(R.id.action_grid_size)
menu.removeItem(R.id.action_sort_order)
}
menu.add(0, R.id.action_settings, 6, getString(R.string.action_settings))
.setIcon(R.drawable.ic_settings)
.setShowAsActionFlags(MenuItem.SHOW_AS_ACTION_IF_ROOM)
menu.add(0, R.id.action_search, 0, getString(R.string.action_search))
.setIcon(R.drawable.ic_search)
.setShowAsActionFlags(MenuItem.SHOW_AS_ACTION_ALWAYS)
ToolbarContentTintHelper.handleOnCreateOptionsMenu(
this,
toolbar,
menu,
getToolbarBackgroundColor(toolbar)
)
return super.onCreateOptionsMenu(menu)
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
val fragment = getCurrentFragment()
if (fragment is AbsRecyclerViewCustomGridSizeFragment<*, *>) {
if (handleGridSizeMenuItem(fragment, item)) {
return true
}
if (handleLayoutResType(fragment, item)) {
return true
}
if (handleSortOrderMenuItem(fragment, item)) {
return true
}
}
when (item.itemId) {
R.id.action_search -> NavigationUtil.goToSearch(this)
R.id.action_new_playlist -> {
create().show(supportFragmentManager, "CREATE_PLAYLIST")
return true
}
R.id.action_mic -> {
val options = ActivityOptions.makeSceneTransitionAnimation(
this, toolbar,
getString(R.string.transition_toolbar)
)
NavigationUtil.goToSearch(this, true, options)
return true
}
R.id.action_settings -> {
NavigationUtil.goToSettings(this)
return true
}
}
return super.onOptionsItemSelected(item)
}
private fun handleSortOrderMenuItem(
fragment: AbsRecyclerViewCustomGridSizeFragment<*, *>,
item: MenuItem
): Boolean {
var sortOrder: String? = null
when (fragment) {
is AlbumsFragment -> {
when (item.itemId) {
R.id.action_album_sort_order_asc -> sortOrder = AlbumSortOrder.ALBUM_A_Z
R.id.action_album_sort_order_desc -> sortOrder = AlbumSortOrder.ALBUM_Z_A
R.id.action_album_sort_order_artist -> sortOrder = AlbumSortOrder.ALBUM_ARTIST
R.id.action_album_sort_order_year -> sortOrder = AlbumSortOrder.ALBUM_YEAR
}
}
is ArtistsFragment -> {
when (item.itemId) {
R.id.action_artist_sort_order_asc -> sortOrder = ArtistSortOrder.ARTIST_A_Z
R.id.action_artist_sort_order_desc -> sortOrder = ArtistSortOrder.ARTIST_Z_A
}
}
is SongsFragment -> {
when (item.itemId) {
R.id.action_song_sort_order_asc -> sortOrder = SongSortOrder.SONG_A_Z
R.id.action_song_sort_order_desc -> sortOrder = SongSortOrder.SONG_Z_A
R.id.action_song_sort_order_artist -> sortOrder = SongSortOrder.SONG_ARTIST
R.id.action_song_sort_order_album -> sortOrder = SongSortOrder.SONG_ALBUM
R.id.action_song_sort_order_year -> sortOrder = SongSortOrder.SONG_YEAR
R.id.action_song_sort_order_date -> sortOrder = SongSortOrder.SONG_DATE
R.id.action_song_sort_order_composer -> sortOrder = SongSortOrder.COMPOSER
R.id.action_song_sort_order_date_modified ->
sortOrder = SongSortOrder.SONG_DATE_MODIFIED
}
}
}
if (sortOrder != null) {
item.isChecked = true
fragment.setAndSaveSortOrder(sortOrder)
return true
}
return false
}
private fun handleLayoutResType(
fragment: AbsRecyclerViewCustomGridSizeFragment<*, *>,
item: MenuItem
): Boolean {
var layoutRes = -1
when (item.itemId) {
R.id.action_layout_normal -> layoutRes = R.layout.item_grid
R.id.action_layout_card -> layoutRes = R.layout.item_card
R.id.action_layout_colored_card -> layoutRes = R.layout.item_card_color
R.id.action_layout_circular -> layoutRes = R.layout.item_grid_circle
R.id.action_layout_image -> layoutRes = R.layout.image
R.id.action_layout_gradient_image -> layoutRes = R.layout.item_image_gradient
}
if (layoutRes != -1) {
item.isChecked = true
fragment.setAndSaveLayoutRes(layoutRes)
return true
}
return false
}
private fun handleGridSizeMenuItem(
fragment: AbsRecyclerViewCustomGridSizeFragment<*, *>,
item: MenuItem
): Boolean {
var gridSize = 0
when (item.itemId) {
R.id.action_grid_size_1 -> gridSize = 1
R.id.action_grid_size_2 -> gridSize = 2
R.id.action_grid_size_3 -> gridSize = 3
R.id.action_grid_size_4 -> gridSize = 4
R.id.action_grid_size_5 -> gridSize = 5
R.id.action_grid_size_6 -> gridSize = 6
R.id.action_grid_size_7 -> gridSize = 7
R.id.action_grid_size_8 -> gridSize = 8
}
if (gridSize > 0) {
item.isChecked = true
fragment.setAndSaveGridSize(gridSize)
return true
}
return false
}
private fun setUpGridSizeMenu(
fragment: AbsRecyclerViewCustomGridSizeFragment<*, *>,
gridSizeMenu: SubMenu
) {
when (fragment.getGridSize()) {
1 -> gridSizeMenu.findItem(R.id.action_grid_size_1).isChecked = true
2 -> gridSizeMenu.findItem(R.id.action_grid_size_2).isChecked = true
3 -> gridSizeMenu.findItem(R.id.action_grid_size_3).isChecked = true
4 -> gridSizeMenu.findItem(R.id.action_grid_size_4).isChecked = true
5 -> gridSizeMenu.findItem(R.id.action_grid_size_5).isChecked = true
6 -> gridSizeMenu.findItem(R.id.action_grid_size_6).isChecked = true
7 -> gridSizeMenu.findItem(R.id.action_grid_size_7).isChecked = true
8 -> gridSizeMenu.findItem(R.id.action_grid_size_8).isChecked = true
}
val maxGridSize = fragment.maxGridSize
if (maxGridSize < 8) {
gridSizeMenu.findItem(R.id.action_grid_size_8).isVisible = false
}
if (maxGridSize < 7) {
gridSizeMenu.findItem(R.id.action_grid_size_7).isVisible = false
}
if (maxGridSize < 6) {
gridSizeMenu.findItem(R.id.action_grid_size_6).isVisible = false
}
if (maxGridSize < 5) {
gridSizeMenu.findItem(R.id.action_grid_size_5).isVisible = false
}
if (maxGridSize < 4) {
gridSizeMenu.findItem(R.id.action_grid_size_4).isVisible = false
}
if (maxGridSize < 3) {
gridSizeMenu.findItem(R.id.action_grid_size_3).isVisible = false
}
}
private fun setupLayoutMenu(
fragment: AbsRecyclerViewCustomGridSizeFragment<*, *>,
subMenu: SubMenu
) {
when (fragment.itemLayoutRes()) {
R.layout.item_card ->
subMenu.findItem(R.id.action_layout_card).isChecked = true
R.layout.item_card_color ->
subMenu.findItem(R.id.action_layout_colored_card).isChecked = true
R.layout.item_grid_circle ->
subMenu.findItem(R.id.action_layout_circular).isChecked = true
R.layout.image ->
subMenu.findItem(R.id.action_layout_image).isChecked = true
R.layout.item_image_gradient ->
subMenu.findItem(R.id.action_layout_gradient_image).isChecked = true
R.layout.item_grid ->
subMenu.findItem(R.id.action_layout_normal).isChecked = true
else ->
subMenu.findItem(R.id.action_layout_normal).isChecked = true
}
}
private fun setUpSortOrderMenu(
fragment: AbsRecyclerViewCustomGridSizeFragment<*, *>,
sortOrderMenu: SubMenu
) {
val currentSortOrder = fragment.getSortOrder()
sortOrderMenu.clear()
when (fragment) {
is AlbumsFragment -> {
sortOrderMenu.add(
0,
R.id.action_album_sort_order_asc,
0,
R.string.sort_order_a_z
).isChecked = currentSortOrder == AlbumSortOrder.ALBUM_A_Z
sortOrderMenu.add(
0,
R.id.action_album_sort_order_desc,
1,
R.string.sort_order_z_a
).isChecked =
currentSortOrder == AlbumSortOrder.ALBUM_Z_A
sortOrderMenu.add(
0,
R.id.action_album_sort_order_artist,
2,
R.string.sort_order_artist
).isChecked =
currentSortOrder == AlbumSortOrder.ALBUM_ARTIST
sortOrderMenu.add(
0,
R.id.action_album_sort_order_year,
3,
R.string.sort_order_year
).isChecked =
currentSortOrder == AlbumSortOrder.ALBUM_YEAR
}
is ArtistsFragment -> {
sortOrderMenu.add(
0,
R.id.action_artist_sort_order_asc,
0,
R.string.sort_order_a_z
).isChecked =
currentSortOrder == ArtistSortOrder.ARTIST_A_Z
sortOrderMenu.add(
0,
R.id.action_artist_sort_order_desc,
1,
R.string.sort_order_z_a
).isChecked =
currentSortOrder == ArtistSortOrder.ARTIST_Z_A
}
is SongsFragment -> {
sortOrderMenu.add(
0,
R.id.action_song_sort_order_asc,
0,
R.string.sort_order_a_z
).isChecked =
currentSortOrder == SongSortOrder.SONG_A_Z
sortOrderMenu.add(
0,
R.id.action_song_sort_order_desc,
1,
R.string.sort_order_z_a
).isChecked =
currentSortOrder == SongSortOrder.SONG_Z_A
sortOrderMenu.add(
0,
R.id.action_song_sort_order_artist,
2,
R.string.sort_order_artist
).isChecked =
currentSortOrder == SongSortOrder.SONG_ARTIST
sortOrderMenu.add(
0,
R.id.action_song_sort_order_album,
3,
R.string.sort_order_album
).isChecked =
currentSortOrder == SongSortOrder.SONG_ALBUM
sortOrderMenu.add(
0,
R.id.action_song_sort_order_year,
4,
R.string.sort_order_year
).isChecked =
currentSortOrder == SongSortOrder.SONG_YEAR
sortOrderMenu.add(
0,
R.id.action_song_sort_order_date,
5,
R.string.sort_order_date
).isChecked =
currentSortOrder == SongSortOrder.SONG_DATE
sortOrderMenu.add(
0,
R.id.action_song_sort_order_date_modified,
6,
R.string.sort_order_date_modified
).isChecked = currentSortOrder == SongSortOrder.SONG_DATE_MODIFIED
sortOrderMenu.add(
0,
R.id.action_song_sort_order_composer,
7,
R.string.sort_order_composer
).isChecked = currentSortOrder == SongSortOrder.COMPOSER
}
}
sortOrderMenu.setGroupCheckable(0, true, true)
}
private fun getCurrentFragment(): Fragment? {
return supportFragmentManager.findFragmentById(R.id.fragment_container)
}
private fun isFolderPage(): Boolean {
return supportFragmentManager.findFragmentByTag(FoldersFragment.TAG) is FoldersFragment
}
private fun isHomePage(): Boolean {
return supportFragmentManager.findFragmentByTag(BannerHomeFragment.TAG) is BannerHomeFragment
}
private fun isPlaylistPage(): Boolean {
return supportFragmentManager.findFragmentByTag(PlaylistsFragment.TAG) is PlaylistsFragment
}
fun addOnAppBarOffsetChangedListener(
changedListener: AppBarLayout.OnOffsetChangedListener
) {
appBarLayout.addOnOffsetChangedListener(changedListener)
}
fun removeOnAppBarOffsetChangedListener(
changedListener: AppBarLayout.OnOffsetChangedListener
) {
appBarLayout.removeOnOffsetChangedListener(changedListener)
}
fun getTotalAppBarScrollingRange(): Int {
return appBarLayout.totalScrollRange
}
override fun requestPermissions() { override fun requestPermissions() {
if (!blockRequestPermissions) { if (!blockRequestPermissions) {
super.requestPermissions() super.requestPermissions()
@ -522,76 +90,6 @@ class MainActivity : AbsSlidingMusicPanelActivity(),
} }
} }
private fun setupToolbar() {
toolbar.setBackgroundColor(resolveColor(this, R.attr.colorSurface))
appBarLayout.setBackgroundColor(resolveColor(this, R.attr.colorSurface))
setSupportActionBar(toolbar)
}
private fun setCurrentFragment(
fragment: Fragment,
tag: String
) {
supportFragmentManager.commit {
setCustomAnimations(
R.anim.retro_fragment_open_enter,
R.anim.retro_fragment_open_exit,
R.anim.retro_fragment_fade_enter,
R.anim.retro_fragment_fade_exit
)
replace(R.id.fragment_container, fragment, tag)
}
currentFragment = fragment as MainActivityFragmentCallbacks
}
private fun selectedFragment(itemId: Int) {
when (itemId) {
R.id.action_album -> setCurrentFragment(
AlbumsFragment.newInstance(),
AlbumsFragment.TAG
)
R.id.action_artist -> setCurrentFragment(
ArtistsFragment.newInstance(),
ArtistsFragment.TAG
)
R.id.action_playlist -> setCurrentFragment(
PlaylistsFragment.newInstance(),
PlaylistsFragment.TAG
)
R.id.action_genre -> setCurrentFragment(
GenresFragment.newInstance(),
GenresFragment.TAG
)
R.id.action_playing_queue -> setCurrentFragment(
PlayingQueueFragment.newInstance(),
PlayingQueueFragment.TAG
)
R.id.action_song -> setCurrentFragment(
SongsFragment.newInstance(),
SongsFragment.TAG
)
R.id.action_folder -> setCurrentFragment(
FoldersFragment.newInstance(this),
FoldersFragment.TAG
)
R.id.action_home -> setCurrentFragment(
BannerHomeFragment.newInstance(),
BannerHomeFragment.TAG
)
else -> setCurrentFragment(
BannerHomeFragment.newInstance(),
BannerHomeFragment.TAG
)
}
}
private fun restoreCurrentFragment() {
val fragment = supportFragmentManager.findFragmentById(R.id.fragment_container)
if (fragment != null) {
currentFragment = fragment as MainActivityFragmentCallbacks
}
}
override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences?, key: String?) { override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences?, key: String?) {
if (key == GENERAL_THEME || key == BLACK_THEME || key == ADAPTIVE_COLOR_APP || key == USER_NAME || key == TOGGLE_FULL_SCREEN || key == TOGGLE_VOLUME || key == ROUND_CORNERS || key == CAROUSEL_EFFECT || key == NOW_PLAYING_SCREEN_ID || key == TOGGLE_GENRE || key == BANNER_IMAGE_PATH || key == PROFILE_IMAGE_PATH || key == CIRCULAR_ALBUM_ART || key == KEEP_SCREEN_ON || key == TOGGLE_SEPARATE_LINE || key == TOGGLE_HOME_BANNER || key == TOGGLE_ADD_CONTROLS || key == ALBUM_COVER_STYLE || key == HOME_ARTIST_GRID_STYLE || key == ALBUM_COVER_TRANSFORM || key == DESATURATED_COLOR || key == EXTRA_SONG_INFO || key == TAB_TEXT_MODE || key == LANGUAGE_NAME || key == LIBRARY_CATEGORIES if (key == GENERAL_THEME || key == 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
) { ) {
@ -637,16 +135,20 @@ class MainActivity : AbsSlidingMusicPanelActivity(),
} else if (MediaStore.Audio.Albums.CONTENT_TYPE == mimeType) { } else if (MediaStore.Audio.Albums.CONTENT_TYPE == mimeType) {
val id = parseIdFromIntent(intent, "albumId", "album").toInt() val id = parseIdFromIntent(intent, "albumId", "album").toInt()
if (id >= 0) { if (id >= 0) {
val position = intent.getIntExtra("position", 0) lifecycleScope.launch(Dispatchers.Main) {
openQueue(getAlbum(this, id).songs!!, position, true) val position = intent.getIntExtra("position", 0)
handled = true openQueue(repository.albumById(id).songs!!, position, true)
handled = true
}
} }
} else if (MediaStore.Audio.Artists.CONTENT_TYPE == mimeType) { } else if (MediaStore.Audio.Artists.CONTENT_TYPE == mimeType) {
val id = parseIdFromIntent(intent, "artistId", "artist").toInt() val id = parseIdFromIntent(intent, "artistId", "artist").toInt()
if (id >= 0) { if (id >= 0) {
val position = intent.getIntExtra("position", 0) lifecycleScope.launch {
openQueue(getArtist(this, id).songs, position, true) val position = intent.getIntExtra("position", 0)
handled = true openQueue(repository.artistById(id).songs, position, true)
handled = true
}
} }
} }
if (handled) { if (handled) {
@ -671,31 +173,4 @@ class MainActivity : AbsSlidingMusicPanelActivity(),
} }
return id return id
} }
override fun handleBackPress(): Boolean {
if (cab != null && cab!!.isActive) {
cab?.finish()
return true
}
return super.handleBackPress() || currentFragment.handleBackPress()
}
override fun openCab(menuRes: Int, callback: MaterialCab.Callback): MaterialCab {
cab?.let {
if (it.isActive) it.finish()
}
cab = MaterialCab(this, R.id.cab_stub)
.setMenu(menuRes)
.setCloseDrawableRes(R.drawable.ic_close)
.setBackgroundColor(
RetroColorUtil.shiftBackgroundColorForLightText(
resolveColor(
this,
R.attr.colorSurface
)
)
)
.start(callback)
return cab as MaterialCab
}
} }

View file

@ -7,6 +7,11 @@ import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import code.name.monkey.appthemehelper.util.ColorUtil import code.name.monkey.appthemehelper.util.ColorUtil
import code.name.monkey.appthemehelper.util.MaterialValueHelper import code.name.monkey.appthemehelper.util.MaterialValueHelper
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
import io.github.muntashirakon.music.R import io.github.muntashirakon.music.R
import io.github.muntashirakon.music.activities.base.AbsMusicServiceActivity import io.github.muntashirakon.music.activities.base.AbsMusicServiceActivity
import io.github.muntashirakon.music.adapter.song.PlayingQueueAdapter import io.github.muntashirakon.music.adapter.song.PlayingQueueAdapter
@ -14,11 +19,7 @@ import io.github.muntashirakon.music.extensions.accentColor
import io.github.muntashirakon.music.extensions.surfaceColor import io.github.muntashirakon.music.extensions.surfaceColor
import io.github.muntashirakon.music.helper.MusicPlayerRemote import io.github.muntashirakon.music.helper.MusicPlayerRemote
import io.github.muntashirakon.music.util.MusicUtil import io.github.muntashirakon.music.util.MusicUtil
import com.h6ah4i.android.widget.advrecyclerview.animator.DraggableItemAnimator import io.github.muntashirakon.music.util.ThemedFastScroller
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
import kotlinx.android.synthetic.main.activity_playing_queue.* import kotlinx.android.synthetic.main.activity_playing_queue.*
open class PlayingQueueActivity : AbsMusicServiceActivity() { open class PlayingQueueActivity : AbsMusicServiceActivity() {
@ -103,7 +104,7 @@ open class PlayingQueueActivity : AbsMusicServiceActivity() {
} }
} }
}) })
//ViewUtil.setUpFastScrollRecyclerViewColor(this, recyclerView) val fastScroller = ThemedFastScroller.create(recyclerView)
} }
private fun checkForPadding() { private fun checkForPadding() {

View file

@ -2,26 +2,18 @@ package io.github.muntashirakon.music.activities
import android.os.Bundle import android.os.Bundle
import android.view.MenuItem import android.view.MenuItem
import androidx.annotation.StringRes
import androidx.fragment.app.Fragment
import androidx.navigation.NavController import androidx.navigation.NavController
import androidx.navigation.fragment.NavHostFragment
import androidx.navigation.ui.AppBarConfiguration
import code.name.monkey.appthemehelper.ThemeStore import code.name.monkey.appthemehelper.ThemeStore
import code.name.monkey.appthemehelper.util.VersionUtils import code.name.monkey.appthemehelper.util.VersionUtils
import com.afollestad.materialdialogs.color.ColorChooserDialog
import io.github.muntashirakon.music.R import io.github.muntashirakon.music.R
import io.github.muntashirakon.music.activities.base.AbsBaseActivity import io.github.muntashirakon.music.activities.base.AbsBaseActivity
import io.github.muntashirakon.music.appshortcuts.DynamicShortcutManager import io.github.muntashirakon.music.appshortcuts.DynamicShortcutManager
import io.github.muntashirakon.music.extensions.applyToolbar import io.github.muntashirakon.music.extensions.applyToolbar
import com.afollestad.materialdialogs.color.ColorChooserDialog import io.github.muntashirakon.music.extensions.findNavController
import kotlinx.android.synthetic.main.activity_settings.* import kotlinx.android.synthetic.main.activity_settings.*
class SettingsActivity : AbsBaseActivity(), ColorChooserDialog.ColorCallback { class SettingsActivity : AbsBaseActivity(), ColorChooserDialog.ColorCallback {
private val fragmentManager = supportFragmentManager
private lateinit var appBarConfiguration: AppBarConfiguration
private lateinit var navController: NavController
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
setDrawUnderStatusBar() setDrawUnderStatusBar()
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
@ -35,56 +27,14 @@ class SettingsActivity : AbsBaseActivity(), ColorChooserDialog.ColorCallback {
private fun setupToolbar() { private fun setupToolbar() {
setTitle(R.string.action_settings) setTitle(R.string.action_settings)
applyToolbar(toolbar) applyToolbar(toolbar)
val navHostFragment = val navController: NavController = findNavController(R.id.contentFrame)
supportFragmentManager.findFragmentById(R.id.contentFrame) as NavHostFragment
val navController: NavController = navHostFragment.navController
navController.addOnDestinationChangedListener { _, _, _ -> navController.addOnDestinationChangedListener { _, _, _ ->
toolbar.title = navController.currentDestination?.label toolbar.title = navController.currentDestination?.label
} }
//It removes the back button
//appBarConfiguration = AppBarConfiguration(navController.graph)
//setupActionBarWithNavController(navController, appBarConfiguration)
} }
override fun onSupportNavigateUp(): Boolean { override fun onSupportNavigateUp(): Boolean {
return navController.navigateUp() || super.onSupportNavigateUp() return findNavController(R.id.contentFrame).navigateUp() || super.onSupportNavigateUp()
}
fun setupFragment(fragment: Fragment, @StringRes titleName: Int) {
val fragmentTransaction = fragmentManager
.beginTransaction()
.setCustomAnimations(
R.anim.sliding_in_left,
R.anim.sliding_out_right,
android.R.anim.slide_in_left,
android.R.anim.slide_out_right
)
fragmentTransaction.replace(R.id.contentFrame, fragment, fragment.tag)
fragmentTransaction.addToBackStack(null)
fragmentTransaction.commit()
setTitle(titleName)
}
override fun onBackPressed() {
if (fragmentManager.backStackEntryCount == 0) {
super.onBackPressed()
} else {
setTitle(R.string.action_settings)
fragmentManager.popBackStack()
}
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
if (item.itemId == android.R.id.home) {
onBackPressed()
return true
}
return super.onOptionsItemSelected(item)
}
companion object {
const val TAG: String = "SettingsActivity"
} }
override fun onColorSelection(dialog: ColorChooserDialog, selectedColor: Int) { override fun onColorSelection(dialog: ColorChooserDialog, selectedColor: Int) {
@ -101,4 +51,11 @@ class SettingsActivity : AbsBaseActivity(), ColorChooserDialog.ColorCallback {
override fun onColorChooserDismissed(dialog: ColorChooserDialog) { override fun onColorChooserDismissed(dialog: ColorChooserDialog) {
} }
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
if (item.itemId == android.R.id.home) {
onBackPressed()
}
return super.onOptionsItemSelected(item)
}
}

View file

@ -10,10 +10,16 @@ import android.text.TextUtils
import android.view.MenuItem import android.view.MenuItem
import android.widget.Toast import android.widget.Toast
import code.name.monkey.appthemehelper.util.ColorUtil import code.name.monkey.appthemehelper.util.ColorUtil
import code.name.monkey.appthemehelper.util.MaterialUtil
import code.name.monkey.appthemehelper.util.MaterialValueHelper import code.name.monkey.appthemehelper.util.MaterialValueHelper
import com.bumptech.glide.Glide
import com.bumptech.glide.load.engine.DiskCacheStrategy
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 io.github.muntashirakon.music.Constants.USER_BANNER import io.github.muntashirakon.music.Constants.USER_BANNER
import io.github.muntashirakon.music.Constants.USER_PROFILE import io.github.muntashirakon.music.Constants.USER_PROFILE
import io.github.muntashirakon.music.R
import io.github.muntashirakon.music.activities.base.AbsBaseActivity import io.github.muntashirakon.music.activities.base.AbsBaseActivity
import io.github.muntashirakon.music.extensions.accentColor import io.github.muntashirakon.music.extensions.accentColor
import io.github.muntashirakon.music.extensions.applyToolbar import io.github.muntashirakon.music.extensions.applyToolbar
@ -21,13 +27,6 @@ import io.github.muntashirakon.music.glide.ProfileBannerGlideRequest
import io.github.muntashirakon.music.glide.UserProfileGlideRequest import io.github.muntashirakon.music.glide.UserProfileGlideRequest
import io.github.muntashirakon.music.util.ImageUtil import io.github.muntashirakon.music.util.ImageUtil
import io.github.muntashirakon.music.util.PreferenceUtil import io.github.muntashirakon.music.util.PreferenceUtil
import com.bumptech.glide.Glide
import com.bumptech.glide.load.engine.DiskCacheStrategy
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 io.github.muntashirakon.music.R
import kotlinx.android.synthetic.main.activity_user_info.* import kotlinx.android.synthetic.main.activity_user_info.*
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
@ -49,7 +48,7 @@ class UserInfoActivity : AbsBaseActivity() {
setLightNavigationBar(true) setLightNavigationBar(true)
applyToolbar(toolbar) applyToolbar(toolbar)
MaterialUtil.setTint(nameContainer, false) nameContainer.accentColor()
name.setText(PreferenceUtil.userName) name.setText(PreferenceUtil.userName)
userImage.setOnClickListener { userImage.setOnClickListener {
@ -161,7 +160,7 @@ class UserInfoActivity : AbsBaseActivity() {
} }
private fun saveImage(bitmap: Bitmap, fileName: String) { private fun saveImage(bitmap: Bitmap, fileName: String) {
CoroutineScope(Dispatchers.IO).launch() { CoroutineScope(Dispatchers.IO).launch {
val appDir = applicationContext.filesDir val appDir = applicationContext.filesDir
val file = File(appDir, fileName) val file = File(appDir, fileName)
var successful = false var successful = false

View file

@ -1,250 +0,0 @@
package io.github.muntashirakon.music.activities.albums
import android.app.ActivityOptions
import android.os.Bundle
import android.transition.TransitionInflater
import android.util.Pair
import android.view.MenuItem
import android.view.View
import androidx.appcompat.app.AppCompatActivity
import androidx.navigation.fragment.findNavController
import androidx.recyclerview.widget.DefaultItemAnimator
import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.LinearLayoutManager
import code.name.monkey.appthemehelper.util.MaterialUtil
import io.github.muntashirakon.music.R
import io.github.muntashirakon.music.adapter.album.HorizontalAlbumAdapter
import io.github.muntashirakon.music.adapter.song.SimpleSongAdapter
import io.github.muntashirakon.music.extensions.extraNotNull
import io.github.muntashirakon.music.extensions.show
import io.github.muntashirakon.music.fragments.base.AbsMusicServiceFragment
import io.github.muntashirakon.music.glide.AlbumGlideRequest
import io.github.muntashirakon.music.glide.ArtistGlideRequest
import io.github.muntashirakon.music.glide.RetroMusicColoredTarget
import io.github.muntashirakon.music.helper.MusicPlayerRemote
import io.github.muntashirakon.music.model.Album
import io.github.muntashirakon.music.model.Artist
import io.github.muntashirakon.music.network.model.LastFmAlbum
import io.github.muntashirakon.music.util.MusicUtil
import io.github.muntashirakon.music.util.NavigationUtil
import io.github.muntashirakon.music.util.PreferenceUtil
import io.github.muntashirakon.music.util.RetroUtil
import io.github.muntashirakon.music.util.color.MediaNotificationProcessor
import com.bumptech.glide.Glide
import kotlinx.android.synthetic.main.activity_album.*
import kotlinx.android.synthetic.main.activity_album_content.*
import org.koin.androidx.viewmodel.ext.android.viewModel
import org.koin.core.parameter.parametersOf
import java.util.*
class AlbumDetailsFragment : AbsMusicServiceFragment(R.layout.fragment_album_details) {
private lateinit var simpleSongAdapter: SimpleSongAdapter
private lateinit var album: Album
private val savedSortOrder: String
get() = PreferenceUtil.albumDetailSongSortOrder
private val detailsViewModel by viewModel<AlbumDetailsViewModel> {
parametersOf(extraNotNull<Int>(AlbumDetailsActivity.EXTRA_ALBUM_ID).value)
}
private fun setSharedElementTransitionOnEnter() {
sharedElementEnterTransition = TransitionInflater.from(context)
.inflateTransition(R.transition.change_bounds)
}
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
setSharedElementTransitionOnEnter()
postponeEnterTransition()
playerActivity?.addMusicServiceEventListener(detailsViewModel)
detailsViewModel.getAlbum().observe(viewLifecycleOwner, androidx.lifecycle.Observer {
startPostponedEnterTransition()
showAlbum(it)
})
detailsViewModel.getArtist().observe(viewLifecycleOwner, androidx.lifecycle.Observer {
loadArtistImage(it)
})
detailsViewModel.getMoreAlbums().observe(viewLifecycleOwner, androidx.lifecycle.Observer {
moreAlbums(it)
})
detailsViewModel.getAlbumInfo().observe(viewLifecycleOwner, androidx.lifecycle.Observer {
aboutAlbum(it)
})
setupRecyclerView()
artistImage.setOnClickListener {
val artistPairs = ActivityOptions.makeSceneTransitionAnimation(
requireActivity(),
Pair.create(
artistImage,
getString(R.string.transition_artist_image)
)
)
NavigationUtil.goToArtistOptions(requireActivity(), album.artistId, artistPairs)
}
playAction.setOnClickListener { MusicPlayerRemote.openQueue(album.songs!!, 0, true) }
shuffleAction.setOnClickListener {
MusicPlayerRemote.openAndShuffleQueue(
album.songs!!,
true
)
}
aboutAlbumText.setOnClickListener {
if (aboutAlbumText.maxLines == 4) {
aboutAlbumText.maxLines = Integer.MAX_VALUE
} else {
aboutAlbumText.maxLines = 4
}
}
image.apply {
transitionName = getString(R.string.transition_album_art)
}
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val activity = activity as AppCompatActivity
activity.supportActionBar?.setDisplayHomeAsUpEnabled(true)
}
override fun onDestroy() {
super.onDestroy()
playerActivity?.removeMusicServiceEventListener(detailsViewModel)
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
when (item.itemId) {
android.R.id.home -> findNavController().navigateUp()
}
return super.onOptionsItemSelected(item)
}
private fun setupRecyclerView() {
simpleSongAdapter = SimpleSongAdapter(
requireActivity() as AppCompatActivity,
ArrayList(),
R.layout.item_song,
null
)
recyclerView.apply {
layoutManager = LinearLayoutManager(requireContext())
itemAnimator = DefaultItemAnimator()
isNestedScrollingEnabled = false
adapter = simpleSongAdapter
}
}
private fun showAlbum(album: Album) {
if (album.songs!!.isEmpty()) {
return
}
this.album = album
albumTitle.text = album.title
val songText =
resources.getQuantityString(
R.plurals.albumSongs,
album.songCount,
album.songCount
)
songTitle.text = songText
if (MusicUtil.getYearString(album.year) == "-") {
albumText.text = String.format(
"%s • %s",
album.artistName,
MusicUtil.getReadableDurationString(MusicUtil.getTotalDuration(album.songs))
)
} else {
albumText.text = String.format(
"%s • %s • %s",
album.artistName,
MusicUtil.getYearString(album.year),
MusicUtil.getReadableDurationString(MusicUtil.getTotalDuration(album.songs))
)
}
loadAlbumCover()
simpleSongAdapter.swapDataSet(album.songs)
detailsViewModel.loadArtist(album.artistId)
detailsViewModel.loadAlbumInfo(album)
}
private fun moreAlbums(albums: List<Album>) {
moreTitle.show()
moreRecyclerView.show()
moreTitle.text = String.format(getString(R.string.label_more_from), album.artistName)
val albumAdapter =
HorizontalAlbumAdapter(requireActivity() as AppCompatActivity, albums, null)
moreRecyclerView.layoutManager = GridLayoutManager(
requireContext(),
1,
GridLayoutManager.HORIZONTAL,
false
)
moreRecyclerView.adapter = albumAdapter
}
private fun aboutAlbum(lastFmAlbum: LastFmAlbum) {
if (lastFmAlbum.album != null) {
if (lastFmAlbum.album.wiki != null) {
aboutAlbumText.show()
aboutAlbumTitle.show()
aboutAlbumTitle.text =
String.format(getString(R.string.about_album_label), lastFmAlbum.album.name)
aboutAlbumText.text = lastFmAlbum.album.wiki.content
}
if (lastFmAlbum.album.listeners.isNotEmpty()) {
listeners.show()
listenersLabel.show()
scrobbles.show()
scrobblesLabel.show()
listeners.text = RetroUtil.formatValue(lastFmAlbum.album.listeners.toFloat())
scrobbles.text = RetroUtil.formatValue(lastFmAlbum.album.playcount.toFloat())
}
}
}
private fun loadArtistImage(artist: Artist) {
ArtistGlideRequest.Builder.from(Glide.with(requireContext()), artist)
.generatePalette(requireContext())
.build()
.dontAnimate()
.dontTransform()
.into(object : RetroMusicColoredTarget(artistImage) {
override fun onColorReady(colors: MediaNotificationProcessor) {
}
})
}
private fun loadAlbumCover() {
AlbumGlideRequest.Builder.from(Glide.with(requireContext()), album.safeGetFirstSong())
.checkIgnoreMediaStore(requireContext())
.ignoreMediaStore(PreferenceUtil.isIgnoreMediaStoreArtwork)
.generatePalette(requireContext())
.build()
.dontAnimate()
.dontTransform()
.into(object : RetroMusicColoredTarget(image) {
override fun onColorReady(colors: MediaNotificationProcessor) {
setColors(colors)
}
})
}
private fun setColors(color: MediaNotificationProcessor) {
MaterialUtil.tintColor(
button = shuffleAction,
textColor = color.primaryTextColor,
backgroundColor = color.backgroundColor
)
MaterialUtil.tintColor(
button = playAction,
textColor = color.primaryTextColor,
backgroundColor = color.backgroundColor
)
}
}

View file

@ -7,27 +7,29 @@ import android.view.ViewGroup
import android.view.ViewTreeObserver import android.view.ViewTreeObserver
import android.widget.FrameLayout import android.widget.FrameLayout
import androidx.annotation.LayoutRes import androidx.annotation.LayoutRes
import androidx.core.view.ViewCompat
import androidx.core.view.isVisible
import androidx.lifecycle.Observer import androidx.lifecycle.Observer
import code.name.monkey.appthemehelper.util.ATHUtil import code.name.monkey.appthemehelper.util.ATHUtil
import code.name.monkey.appthemehelper.util.ColorUtil import code.name.monkey.appthemehelper.util.ColorUtil
import com.google.android.material.bottomsheet.BottomSheetBehavior
import io.github.muntashirakon.music.R import io.github.muntashirakon.music.R
import io.github.muntashirakon.music.RetroBottomSheetBehavior import io.github.muntashirakon.music.RetroBottomSheetBehavior
import io.github.muntashirakon.music.extensions.hide import io.github.muntashirakon.music.extensions.hide
import io.github.muntashirakon.music.extensions.show import io.github.muntashirakon.music.extensions.show
import io.github.muntashirakon.music.extensions.whichFragment
import io.github.muntashirakon.music.fragments.LibraryViewModel import io.github.muntashirakon.music.fragments.LibraryViewModel
import io.github.muntashirakon.music.fragments.MiniPlayerFragment import io.github.muntashirakon.music.fragments.MiniPlayerFragment
import io.github.muntashirakon.music.fragments.NowPlayingScreen import io.github.muntashirakon.music.fragments.NowPlayingScreen
import io.github.muntashirakon.music.fragments.NowPlayingScreen.* import io.github.muntashirakon.music.fragments.NowPlayingScreen.*
import io.github.muntashirakon.music.helper.MusicPlayerRemote import io.github.muntashirakon.music.helper.MusicPlayerRemote
import io.github.muntashirakon.music.model.CategoryInfo import io.github.muntashirakon.music.model.CategoryInfo
import io.github.muntashirakon.music.util.DensityUtil
import io.github.muntashirakon.music.util.PreferenceUtil import io.github.muntashirakon.music.util.PreferenceUtil
import io.github.muntashirakon.music.views.BottomNavigationBarTinted import io.github.muntashirakon.music.views.BottomNavigationBarTinted
import com.google.android.material.bottomsheet.BottomSheetBehavior
import kotlinx.android.synthetic.main.sliding_music_panel_layout.* import kotlinx.android.synthetic.main.sliding_music_panel_layout.*
import org.koin.androidx.viewmodel.ext.android.viewModel import org.koin.androidx.viewmodel.ext.android.viewModel
abstract class AbsSlidingMusicPanelActivity() : AbsMusicServiceActivity() { abstract class AbsSlidingMusicPanelActivity : AbsMusicServiceActivity() {
companion object { companion object {
val TAG: String = AbsSlidingMusicPanelActivity::class.java.simpleName val TAG: String = AbsSlidingMusicPanelActivity::class.java.simpleName
} }
@ -120,8 +122,9 @@ abstract class AbsSlidingMusicPanelActivity() : AbsMusicServiceActivity() {
return slidingMusicPanelLayout return slidingMusicPanelLayout
} }
private fun collapsePanel() { fun collapsePanel() {
behavior.state = BottomSheetBehavior.STATE_COLLAPSED behavior.state = BottomSheetBehavior.STATE_COLLAPSED
setMiniPlayerAlphaProgress(0f)
} }
fun expandPanel() { fun expandPanel() {
@ -133,9 +136,7 @@ abstract class AbsSlidingMusicPanelActivity() : AbsMusicServiceActivity() {
if (miniPlayerFragment?.view == null) return if (miniPlayerFragment?.view == null) return
val alpha = 1 - progress val alpha = 1 - progress
miniPlayerFragment?.view?.alpha = alpha miniPlayerFragment?.view?.alpha = alpha
// necessary to make the views below clickable
miniPlayerFragment?.view?.visibility = if (alpha == 0f) View.GONE else View.VISIBLE miniPlayerFragment?.view?.visibility = if (alpha == 0f) View.GONE else View.VISIBLE
bottomNavigationView.translationY = progress * 500 bottomNavigationView.translationY = progress * 500
bottomNavigationView.alpha = alpha bottomNavigationView.alpha = alpha
} }
@ -172,40 +173,31 @@ abstract class AbsSlidingMusicPanelActivity() : AbsMusicServiceActivity() {
return bottomNavigationView return bottomNavigationView
} }
fun setBottomBarVisibility(visible: Int) { fun hideBottomBarVisibility(visible: Boolean) {
bottomNavigationView.visibility = visible bottomNavigationView.isVisible = visible
hideBottomBar(MusicPlayerRemote.playingQueue.isEmpty()) hideBottomBar(MusicPlayerRemote.playingQueue.isEmpty())
} }
private fun hideBottomBar(hide: Boolean) { private fun hideBottomBar(hide: Boolean) {
val heightOfBar = resources.getDimensionPixelSize(R.dimen.mini_player_height) val heightOfBar = bottomNavigationView.height
val heightOfBarWithTabs = val isBottomBarVisible = bottomNavigationView.isVisible
resources.getDimensionPixelSize(R.dimen.mini_player_height_expanded)
if (hide) { if (hide) {
behavior.isHideable = true behavior.isHideable = true
behavior.peekHeight = 0 behavior.peekHeight = 0
bottomNavigationView.elevation = DensityUtil.dip2px(this, 10f).toFloat()
collapsePanel() collapsePanel()
ViewCompat.setElevation(bottomNavigationView, 10f)
} else { } else {
if (MusicPlayerRemote.playingQueue.isNotEmpty()) { ViewCompat.setElevation(bottomNavigationView, 10f)
slidingPanel.elevation = DensityUtil.dip2px(this, 10f).toFloat() ViewCompat.setElevation(slidingPanel, 10f)
bottomNavigationView.elevation = DensityUtil.dip2px(this, 10f).toFloat() behavior.isHideable = false
behavior.isHideable = false behavior.peekHeight = (if (isBottomBarVisible) heightOfBar * 2 else heightOfBar) - 24
behavior.peekHeight =
if (bottomNavigationView.visibility == View.VISIBLE) {
heightOfBarWithTabs
} else {
heightOfBar
}
}
} }
} }
private fun chooseFragmentForTheme() { private fun chooseFragmentForTheme() {
cps = PreferenceUtil.nowPlayingScreen cps = PreferenceUtil.nowPlayingScreen
miniPlayerFragment = miniPlayerFragment = whichFragment<MiniPlayerFragment>(R.id.miniPlayerFragment)
supportFragmentManager.findFragmentById(R.id.miniPlayerFragment) as MiniPlayerFragment
miniPlayerFragment?.view?.setOnClickListener { expandPanel() } miniPlayerFragment?.view?.setOnClickListener { expandPanel() }
} }
@ -232,7 +224,6 @@ abstract class AbsSlidingMusicPanelActivity() : AbsMusicServiceActivity() {
} }
open fun handleBackPress(): Boolean { open fun handleBackPress(): Boolean {
if (panelState == BottomSheetBehavior.STATE_EXPANDED) { if (panelState == BottomSheetBehavior.STATE_EXPANDED) {
collapsePanel() collapsePanel()
return true return true
@ -305,6 +296,12 @@ abstract class AbsSlidingMusicPanelActivity() : AbsMusicServiceActivity() {
} }
} }
fun hideBottomNavigation() {
behavior.isHideable = true
behavior.peekHeight = 0
hideBottomBarVisibility(false)
}
fun updateTabs() { fun updateTabs() {
bottomNavigationView.menu.clear() bottomNavigationView.menu.clear()
val currentTabs: List<CategoryInfo> = PreferenceUtil.libraryCategory val currentTabs: List<CategoryInfo> = PreferenceUtil.libraryCategory

View file

@ -1,143 +0,0 @@
package io.github.muntashirakon.music.activities.genre
import android.os.Bundle
import android.view.Menu
import android.view.MenuItem
import android.view.View
import androidx.recyclerview.widget.DefaultItemAnimator
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import code.name.monkey.appthemehelper.util.ATHUtil
import io.github.muntashirakon.music.R
import io.github.muntashirakon.music.activities.base.AbsSlidingMusicPanelActivity
import io.github.muntashirakon.music.adapter.song.ShuffleButtonSongAdapter
import io.github.muntashirakon.music.extensions.applyToolbar
import io.github.muntashirakon.music.extensions.extraNotNull
import io.github.muntashirakon.music.helper.menu.GenreMenuHelper
import io.github.muntashirakon.music.interfaces.CabHolder
import io.github.muntashirakon.music.model.Genre
import io.github.muntashirakon.music.model.Song
import io.github.muntashirakon.music.util.DensityUtil
import io.github.muntashirakon.music.util.RetroColorUtil
import com.afollestad.materialcab.MaterialCab
import kotlinx.android.synthetic.main.activity_playlist_detail.*
import org.koin.androidx.viewmodel.ext.android.viewModel
import org.koin.core.parameter.parametersOf
import java.util.*
/**
* @author Hemanth S (h4h13).
*/
class GenreDetailsActivity : AbsSlidingMusicPanelActivity(), CabHolder {
private val detailsViewModel: GenreDetailsViewModel by viewModel {
parametersOf(extraNotNull<Genre>(EXTRA_GENRE_ID).value)
}
private lateinit var genre: Genre
private lateinit var songAdapter: ShuffleButtonSongAdapter
private var cab: MaterialCab? = null
private fun getEmojiByUnicode(unicode: Int): String {
return String(Character.toChars(unicode))
}
private fun checkIsEmpty() {
checkForPadding()
emptyEmoji.text = getEmojiByUnicode(0x1F631)
empty?.visibility = if (songAdapter.itemCount == 0) View.VISIBLE else View.GONE
}
private fun checkForPadding() {
val height = DensityUtil.dip2px(this, 52f)
recyclerView.setPadding(0, 0, 0, (height))
}
override fun onCreate(savedInstanceState: Bundle?) {
setDrawUnderStatusBar()
super.onCreate(savedInstanceState)
setStatusbarColorAuto()
setNavigationbarColorAuto()
setTaskDescriptionColorAuto()
setLightNavigationBar(true)
setBottomBarVisibility(View.GONE)
applyToolbar(toolbar)
setupRecyclerView()
detailsViewModel.getSongs().observe(this, androidx.lifecycle.Observer {
songs(it)
})
detailsViewModel.getGenre().observe(this, androidx.lifecycle.Observer {
genre = it
supportActionBar?.title = it.name
})
addMusicServiceEventListener(detailsViewModel)
}
override fun createContentView(): View {
return wrapSlidingMusicPanel(R.layout.activity_playlist_detail)
}
override fun onCreateOptionsMenu(menu: Menu): Boolean {
menuInflater.inflate(R.menu.menu_genre_detail, menu)
return super.onCreateOptionsMenu(menu)
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
if (item.itemId == android.R.id.home) {
onBackPressed()
}
return GenreMenuHelper.handleMenuClick(this, genre, item)
}
private fun setupRecyclerView() {
songAdapter = ShuffleButtonSongAdapter(this, ArrayList(), R.layout.item_list, this)
recyclerView.apply {
itemAnimator = DefaultItemAnimator()
layoutManager = LinearLayoutManager(this@GenreDetailsActivity)
adapter = songAdapter
}
songAdapter.registerAdapterDataObserver(object : RecyclerView.AdapterDataObserver() {
override fun onChanged() {
super.onChanged()
checkIsEmpty()
}
})
}
fun songs(songs: List<Song>) {
songAdapter.swapDataSet(songs)
}
override fun openCab(menuRes: Int, callback: MaterialCab.Callback): MaterialCab {
if (cab != null && cab!!.isActive) cab?.finish()
cab = MaterialCab(this, R.id.cab_stub).setMenu(menuRes)
.setCloseDrawableRes(R.drawable.ic_close)
.setBackgroundColor(
RetroColorUtil.shiftBackgroundColorForLightText(
ATHUtil.resolveColor(
this,
R.attr.colorSurface
)
)
).start(callback)
return cab!!
}
override fun onBackPressed() {
if (cab != null && cab!!.isActive) cab!!.finish()
else {
recyclerView!!.stopScroll()
super.onBackPressed()
}
}
companion object {
const val EXTRA_GENRE_ID = "extra_genre_id"
}
}

View file

@ -1,224 +0,0 @@
package io.github.muntashirakon.music.activities.search
import android.app.Activity
import android.app.Service
import android.content.ActivityNotFoundException
import android.content.Intent
import android.content.res.ColorStateList
import android.os.Bundle
import android.speech.RecognizerIntent
import android.text.Editable
import android.text.TextWatcher
import android.view.View
import android.view.inputmethod.InputMethodManager
import android.widget.TextView.BufferType
import android.widget.Toast
import androidx.appcompat.widget.SearchView.OnQueryTextListener
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import androidx.transition.TransitionManager
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 io.github.muntashirakon.music.R
import io.github.muntashirakon.music.activities.base.AbsMusicServiceActivity
import io.github.muntashirakon.music.adapter.SearchAdapter
import io.github.muntashirakon.music.extensions.extra
import io.github.muntashirakon.music.util.RetroUtil
import com.google.android.material.textfield.TextInputEditText
import kotlinx.android.synthetic.main.activity_search.*
import org.koin.android.ext.android.inject
import java.util.*
import kotlin.collections.ArrayList
class SearchActivity : AbsMusicServiceActivity(), OnQueryTextListener, TextWatcher {
private val viewModel: SearchViewModel by inject()
private var searchAdapter: SearchAdapter? = null
private var query: String? = null
override fun onCreate(savedInstanceState: Bundle?) {
setDrawUnderStatusBar()
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_search)
setStatusbarColorAuto()
setNavigationbarColorAuto()
setTaskDescriptionColorAuto()
setLightNavigationBar(true)
setupRecyclerView()
setUpToolBar()
setupSearchView()
if (extra<Boolean>(EXTRA_SHOW_MIC).value == true) {
startMicSearch()
}
back.setOnClickListener { onBackPressed() }
voiceSearch.setOnClickListener { startMicSearch() }
clearText.setOnClickListener { searchView.clearText() }
searchContainer.backgroundTintList =
ColorStateList.valueOf(ATHUtil.resolveColor(this, R.attr.colorSurface))
keyboardPopup.setOnClickListener {
val inputManager = getSystemService(Service.INPUT_METHOD_SERVICE) as InputMethodManager
inputManager.showSoftInput(searchView, InputMethodManager.SHOW_IMPLICIT)
}
keyboardPopup.backgroundTintList = ColorStateList.valueOf(ThemeStore.accentColor(this))
ColorStateList.valueOf(
MaterialValueHelper.getPrimaryTextColor(
this,
ColorUtil.isColorLight(ThemeStore.accentColor(this))
)
).apply {
keyboardPopup.setTextColor(this)
keyboardPopup.iconTint = this
}
if (savedInstanceState != null) {
query = savedInstanceState.getString(QUERY)
}
viewModel.getSearchResult().observe(this, androidx.lifecycle.Observer {
showData(it)
})
}
private fun setupRecyclerView() {
searchAdapter = SearchAdapter(this, emptyList())
searchAdapter?.registerAdapterDataObserver(object : RecyclerView.AdapterDataObserver() {
override fun onChanged() {
super.onChanged()
empty.visibility = if (searchAdapter!!.itemCount < 1) View.VISIBLE else View.GONE
}
})
recyclerView.apply {
layoutManager = LinearLayoutManager(this@SearchActivity)
adapter = searchAdapter
}
recyclerView.addOnScrollListener(object : RecyclerView.OnScrollListener() {
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
super.onScrolled(recyclerView, dx, dy)
if (dy > 0) {
keyboardPopup.shrink()
} else if (dy < 0) {
keyboardPopup.extend()
}
}
})
}
private fun setupSearchView() {
searchView.addTextChangedListener(this)
}
override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
outState.putString(QUERY, query)
}
private fun setUpToolBar() {
title = null
}
private fun search(query: String) {
this.query = query
TransitionManager.beginDelayedTransition(appBarLayout)
voiceSearch.visibility = if (query.isNotEmpty()) View.GONE else View.VISIBLE
clearText.visibility = if (query.isNotEmpty()) View.VISIBLE else View.GONE
viewModel.search(query)
}
override fun onMediaStoreChanged() {
super.onMediaStoreChanged()
query?.let { search(it) }
}
override fun onQueryTextSubmit(query: String): Boolean {
hideSoftKeyboard()
return false
}
override fun onQueryTextChange(newText: String): Boolean {
search(newText)
return false
}
private fun hideSoftKeyboard() {
RetroUtil.hideSoftKeyboard(this@SearchActivity)
if (searchView != null) {
searchView.clearFocus()
}
}
private fun showEmptyView() {
searchAdapter?.swapDataSet(ArrayList())
}
private fun showData(data: MutableList<Any>) {
if (data.isNotEmpty()) {
searchAdapter?.swapDataSet(data)
} else {
showEmptyView()
}
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
when (requestCode) {
REQ_CODE_SPEECH_INPUT -> {
if (resultCode == Activity.RESULT_OK && null != data) {
val result: ArrayList<String>? =
data.getStringArrayListExtra(RecognizerIntent.EXTRA_RESULTS)
query = result?.get(0)
searchView.setText(query, BufferType.EDITABLE)
viewModel.search(query!!)
}
}
}
}
private fun startMicSearch() {
val intent = Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH)
intent.putExtra(
RecognizerIntent.EXTRA_LANGUAGE_MODEL,
RecognizerIntent.LANGUAGE_MODEL_FREE_FORM
)
intent.putExtra(RecognizerIntent.EXTRA_LANGUAGE, Locale.getDefault())
intent.putExtra(RecognizerIntent.EXTRA_PROMPT, getString(R.string.speech_prompt))
try {
startActivityForResult(
intent,
REQ_CODE_SPEECH_INPUT
)
} catch (e: ActivityNotFoundException) {
e.printStackTrace()
Toast.makeText(this, getString(R.string.speech_not_supported), Toast.LENGTH_SHORT)
.show()
}
}
override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {
}
override fun onTextChanged(newText: CharSequence, start: Int, before: Int, count: Int) {
search(newText.toString())
}
override fun afterTextChanged(s: Editable) {
}
companion object {
val TAG: String = SearchActivity::class.java.simpleName
const val EXTRA_SHOW_MIC = "extra_show_mic"
const val QUERY: String = "query"
private const val REQ_CODE_SPEECH_INPUT = 9002
}
}
fun TextInputEditText.clearText() {
text = null
}

View file

@ -14,35 +14,38 @@ import android.view.MenuItem
import android.view.View import android.view.View
import android.view.animation.OvershootInterpolator import android.view.animation.OvershootInterpolator
import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AlertDialog
import androidx.lifecycle.lifecycleScope
import code.name.monkey.appthemehelper.ThemeStore import code.name.monkey.appthemehelper.ThemeStore
import code.name.monkey.appthemehelper.util.ATHUtil import code.name.monkey.appthemehelper.util.ATHUtil
import code.name.monkey.appthemehelper.util.ColorUtil import code.name.monkey.appthemehelper.util.ColorUtil
import code.name.monkey.appthemehelper.util.MaterialValueHelper import code.name.monkey.appthemehelper.util.MaterialValueHelper
import code.name.monkey.appthemehelper.util.TintHelper import code.name.monkey.appthemehelper.util.TintHelper
import com.google.android.material.button.MaterialButton
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import io.github.muntashirakon.music.R import io.github.muntashirakon.music.R
import io.github.muntashirakon.music.R.drawable import io.github.muntashirakon.music.R.drawable
import io.github.muntashirakon.music.activities.base.AbsBaseActivity import io.github.muntashirakon.music.activities.base.AbsBaseActivity
import io.github.muntashirakon.music.activities.saf.SAFGuideActivity import io.github.muntashirakon.music.activities.saf.SAFGuideActivity
import io.github.muntashirakon.music.repository.Repository
import io.github.muntashirakon.music.util.RetroUtil import io.github.muntashirakon.music.util.RetroUtil
import io.github.muntashirakon.music.util.SAFUtil import io.github.muntashirakon.music.util.SAFUtil
import com.google.android.material.button.MaterialButton
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import kotlinx.android.synthetic.main.activity_album_tag_editor.* import kotlinx.android.synthetic.main.activity_album_tag_editor.*
import org.jaudiotagger.audio.AudioFile import org.jaudiotagger.audio.AudioFile
import org.jaudiotagger.audio.AudioFileIO import org.jaudiotagger.audio.AudioFileIO
import org.jaudiotagger.tag.FieldKey import org.jaudiotagger.tag.FieldKey
import org.koin.android.ext.android.inject
import java.io.File import java.io.File
import java.util.* import java.util.*
abstract class AbsTagEditorActivity : AbsBaseActivity() { abstract class AbsTagEditorActivity : AbsBaseActivity() {
val repository by inject<Repository>()
lateinit var saveFab: MaterialButton
protected var id: Int = 0 protected var id: Int = 0
private set private set
private var paletteColorPrimary: Int = 0 private var paletteColorPrimary: Int = 0
private var isInNoImageMode: Boolean = false private var isInNoImageMode: Boolean = false
private var songPaths: List<String>? = null private var songPaths: List<String>? = null
lateinit var saveFab: MaterialButton
private var savedSongPaths: List<String>? = null private var savedSongPaths: List<String>? = null
private val currentSongPath: String? = null private val currentSongPath: String? = null
private var savedTags: Map<FieldKey, String>? = null private var savedTags: Map<FieldKey, String>? = null
@ -172,41 +175,35 @@ abstract class AbsTagEditorActivity : AbsBaseActivity() {
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
setContentView(contentViewLayout) setContentView(contentViewLayout)
setStatusbarColorAuto()
setNavigationbarColorAuto()
setTaskDescriptionColorAuto()
saveFab = findViewById(R.id.saveTags) saveFab = findViewById(R.id.saveTags)
getIntentExtras() getIntentExtras()
songPaths = getSongPaths() lifecycleScope.launchWhenCreated {
if (songPaths!!.isEmpty()) { songPaths = getSongPaths()
finish() if (songPaths!!.isEmpty()) {
return finish()
}
} }
setUpViews() setUpViews()
setStatusbarColorAuto()
setNavigationbarColorAuto()
setTaskDescriptionColorAuto()
} }
private fun setUpViews() { private fun setUpViews() {
setUpScrollView()
setUpFab() setUpFab()
setUpImageView() setUpImageView()
} }
private fun setUpScrollView() {
//observableScrollView.setScrollViewCallbacks(observableScrollViewCallbacks);
}
private lateinit var items: List<String> private lateinit var items: List<String>
private fun setUpImageView() { private fun setUpImageView() {
loadCurrentImage() loadCurrentImage()
items = listOf( items = listOf(
getString(R.string.pick_from_local_storage), getString(io.github.muntashirakon.music.R.string.pick_from_local_storage),
getString(R.string.web_search), getString(io.github.muntashirakon.music.R.string.web_search),
getString(R.string.remove_cover) getString(io.github.muntashirakon.music.R.string.remove_cover)
) )
editorImage?.setOnClickListener { show } editorImage?.setOnClickListener { show }
} }
@ -217,7 +214,7 @@ abstract class AbsTagEditorActivity : AbsBaseActivity() {
startActivityForResult( startActivityForResult(
Intent.createChooser( Intent.createChooser(
intent, intent,
getString(R.string.pick_from_local_storage) getString(io.github.muntashirakon.music.R.string.pick_from_local_storage)
), REQUEST_CODE_SELECT_IMAGE ), REQUEST_CODE_SELECT_IMAGE
) )
} }
@ -261,7 +258,7 @@ abstract class AbsTagEditorActivity : AbsBaseActivity() {
} }
} }
protected abstract fun getSongPaths(): List<String> protected abstract suspend fun getSongPaths(): List<String>
protected fun searchWebFor(vararg keys: String) { protected fun searchWebFor(vararg keys: String) {
val stringBuilder = StringBuilder() val stringBuilder = StringBuilder()
@ -336,7 +333,7 @@ abstract class AbsTagEditorActivity : AbsBaseActivity() {
hideFab() hideFab()
savedSongPaths = getSongPaths() savedSongPaths = songPaths
savedTags = fieldKeyValueMap savedTags = fieldKeyValueMap
savedArtworkInfo = artworkInfo savedArtworkInfo = artworkInfo

View file

@ -14,23 +14,23 @@ import android.transition.Slide
import android.widget.Toast import android.widget.Toast
import code.name.monkey.appthemehelper.util.ATHUtil import code.name.monkey.appthemehelper.util.ATHUtil
import code.name.monkey.appthemehelper.util.MaterialUtil import code.name.monkey.appthemehelper.util.MaterialUtil
import io.github.muntashirakon.music.R
import io.github.muntashirakon.music.extensions.appHandleColor
import io.github.muntashirakon.music.glide.palette.BitmapPaletteTranscoder
import io.github.muntashirakon.music.glide.palette.BitmapPaletteWrapper
import io.github.muntashirakon.music.loaders.AlbumLoader
import io.github.muntashirakon.music.util.ImageUtil
import io.github.muntashirakon.music.util.RetroColorUtil.generatePalette
import io.github.muntashirakon.music.util.RetroColorUtil.getColor
import com.bumptech.glide.Glide import com.bumptech.glide.Glide
import com.bumptech.glide.load.engine.DiskCacheStrategy import com.bumptech.glide.load.engine.DiskCacheStrategy
import com.bumptech.glide.request.animation.GlideAnimation import com.bumptech.glide.request.animation.GlideAnimation
import com.bumptech.glide.request.target.SimpleTarget import com.bumptech.glide.request.target.SimpleTarget
import io.github.muntashirakon.music.R
import io.github.muntashirakon.music.extensions.appHandleColor
import io.github.muntashirakon.music.glide.palette.BitmapPaletteTranscoder
import io.github.muntashirakon.music.glide.palette.BitmapPaletteWrapper
import io.github.muntashirakon.music.util.ImageUtil
import io.github.muntashirakon.music.util.RetroColorUtil.generatePalette
import io.github.muntashirakon.music.util.RetroColorUtil.getColor
import kotlinx.android.synthetic.main.activity_album_tag_editor.* import kotlinx.android.synthetic.main.activity_album_tag_editor.*
import org.jaudiotagger.tag.FieldKey import org.jaudiotagger.tag.FieldKey
import java.util.* import java.util.*
class AlbumTagEditorActivity : AbsTagEditorActivity(), TextWatcher { class AlbumTagEditorActivity : AbsTagEditorActivity(), TextWatcher {
override val contentViewLayout: Int override val contentViewLayout: Int
get() = R.layout.activity_album_tag_editor get() = R.layout.activity_album_tag_editor
@ -162,13 +162,13 @@ class AlbumTagEditorActivity : AbsTagEditorActivity(), TextWatcher {
writeValuesToFiles( writeValuesToFiles(
fieldKeyValueMap, fieldKeyValueMap,
if (deleteAlbumArt) AbsTagEditorActivity.ArtworkInfo(id, null) if (deleteAlbumArt) ArtworkInfo(id, null)
else if (albumArtBitmap == null) null else ArtworkInfo(id, albumArtBitmap!!) else if (albumArtBitmap == null) null else ArtworkInfo(id, albumArtBitmap!!)
) )
} }
override fun getSongPaths(): List<String> { override suspend fun getSongPaths(): List<String> {
val songs = AlbumLoader.getAlbum(this, id).songs val songs = repository.albumById(id).songs
val paths = ArrayList<String>(songs!!.size) val paths = ArrayList<String>(songs!!.size)
for (song in songs) { for (song in songs) {
paths.add(song.data) paths.add(song.data)

View file

@ -8,9 +8,10 @@ import code.name.monkey.appthemehelper.util.ATHUtil
import code.name.monkey.appthemehelper.util.MaterialUtil import code.name.monkey.appthemehelper.util.MaterialUtil
import io.github.muntashirakon.music.R import io.github.muntashirakon.music.R
import io.github.muntashirakon.music.extensions.appHandleColor import io.github.muntashirakon.music.extensions.appHandleColor
import io.github.muntashirakon.music.loaders.SongLoader import io.github.muntashirakon.music.repository.SongRepository
import kotlinx.android.synthetic.main.activity_song_tag_editor.* import kotlinx.android.synthetic.main.activity_song_tag_editor.*
import org.jaudiotagger.tag.FieldKey import org.jaudiotagger.tag.FieldKey
import org.koin.android.ext.android.inject
import java.util.* import java.util.*
class SongTagEditorActivity : AbsTagEditorActivity(), TextWatcher { class SongTagEditorActivity : AbsTagEditorActivity(), TextWatcher {
@ -18,6 +19,8 @@ class SongTagEditorActivity : AbsTagEditorActivity(), TextWatcher {
override val contentViewLayout: Int override val contentViewLayout: Int
get() = R.layout.activity_song_tag_editor get() = R.layout.activity_song_tag_editor
private val songRepository by inject<SongRepository>()
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
@ -85,9 +88,9 @@ class SongTagEditorActivity : AbsTagEditorActivity(), TextWatcher {
writeValuesToFiles(fieldKeyValueMap, null) writeValuesToFiles(fieldKeyValueMap, null)
} }
override fun getSongPaths(): List<String> { override suspend fun getSongPaths(): List<String> {
val paths = ArrayList<String>(1) val paths = ArrayList<String>(1)
paths.add(SongLoader.getSong(this, id).data) paths.add(songRepository.song(id).data)
return paths return paths
} }

View file

@ -63,7 +63,7 @@ public class WriteTagsAsyncTask extends DialogAsyncTask<WriteTagsAsyncTask.Loadi
File albumArtFile = null; File albumArtFile = null;
if (info.artworkInfo != null && info.artworkInfo.getArtwork() != null) { if (info.artworkInfo != null && info.artworkInfo.getArtwork() != null) {
try { try {
albumArtFile = MusicUtil.createAlbumArtFile().getCanonicalFile(); albumArtFile = MusicUtil.INSTANCE.createAlbumArtFile().getCanonicalFile();
info.artworkInfo.getArtwork() info.artworkInfo.getArtwork()
.compress(Bitmap.CompressFormat.PNG, 0, new FileOutputStream(albumArtFile)); .compress(Bitmap.CompressFormat.PNG, 0, new FileOutputStream(albumArtFile));
artwork = ArtworkFactory.createArtworkFromFile(albumArtFile); artwork = ArtworkFactory.createArtworkFromFile(albumArtFile);
@ -120,9 +120,9 @@ public class WriteTagsAsyncTask extends DialogAsyncTask<WriteTagsAsyncTask.Loadi
Context context = getContext(); Context context = getContext();
if (context != null) { if (context != null) {
if (wroteArtwork) { if (wroteArtwork) {
MusicUtil.insertAlbumArt(context, info.artworkInfo.getAlbumId(), albumArtFile.getPath()); MusicUtil.INSTANCE.insertAlbumArt(context, info.artworkInfo.getAlbumId(), albumArtFile.getPath());
} else if (deletedArtwork) { } else if (deletedArtwork) {
MusicUtil.deleteAlbumArt(context, info.artworkInfo.getAlbumId()); MusicUtil.INSTANCE.deleteAlbumArt(context, info.artworkInfo.getAlbumId());
} }
} }

View file

@ -1,14 +1,16 @@
package io.github.muntashirakon.music.adapter package io.github.muntashirakon.music.adapter
import android.app.Activity
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import androidx.core.os.bundleOf
import androidx.fragment.app.FragmentActivity
import androidx.navigation.findNavController
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import io.github.muntashirakon.music.EXTRA_GENRE
import io.github.muntashirakon.music.R import io.github.muntashirakon.music.R
import io.github.muntashirakon.music.adapter.base.MediaEntryViewHolder import io.github.muntashirakon.music.adapter.base.MediaEntryViewHolder
import io.github.muntashirakon.music.model.Genre import io.github.muntashirakon.music.model.Genre
import io.github.muntashirakon.music.util.NavigationUtil
import java.util.* import java.util.*
/** /**
@ -16,7 +18,7 @@ import java.util.*
*/ */
class GenreAdapter( class GenreAdapter(
private val activity: Activity, private val activity: FragmentActivity,
var dataSet: List<Genre>, var dataSet: List<Genre>,
private val mItemLayoutRes: Int private val mItemLayoutRes: Int
) : RecyclerView.Adapter<GenreAdapter.ViewHolder>() { ) : RecyclerView.Adapter<GenreAdapter.ViewHolder>() {
@ -48,9 +50,10 @@ class GenreAdapter(
inner class ViewHolder(itemView: View) : MediaEntryViewHolder(itemView) { inner class ViewHolder(itemView: View) : MediaEntryViewHolder(itemView) {
override fun onClick(v: View?) { override fun onClick(v: View?) {
super.onClick(v) activity.findNavController(R.id.fragment_container).navigate(
val genre = dataSet[layoutPosition] R.id.genreDetailsFragment,
NavigationUtil.goToGenre(activity, genre) bundleOf(EXTRA_GENRE to dataSet[layoutPosition])
)
} }
} }
} }

View file

@ -1,38 +1,37 @@
package io.github.muntashirakon.music.adapter package io.github.muntashirakon.music.adapter
import android.util.DisplayMetrics
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.widget.ImageView
import android.widget.TextView import android.widget.TextView
import androidx.annotation.IntDef
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.widget.AppCompatTextView import androidx.appcompat.widget.AppCompatTextView
import androidx.core.os.bundleOf
import androidx.navigation.findNavController
import androidx.navigation.fragment.FragmentNavigatorExtras
import androidx.recyclerview.widget.GridLayoutManager import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import androidx.recyclerview.widget.RecyclerView.HORIZONTAL
import code.name.monkey.appthemehelper.ThemeStore import code.name.monkey.appthemehelper.ThemeStore
import code.name.monkey.appthemehelper.util.ColorUtil import code.name.monkey.appthemehelper.util.ColorUtil
import io.github.muntashirakon.music.PeekingLinearLayoutManager import com.bumptech.glide.Glide
import io.github.muntashirakon.music.R import com.google.android.material.card.MaterialCardView
import io.github.muntashirakon.music.*
import io.github.muntashirakon.music.adapter.album.AlbumAdapter import io.github.muntashirakon.music.adapter.album.AlbumAdapter
import io.github.muntashirakon.music.adapter.artist.ArtistAdapter import io.github.muntashirakon.music.adapter.artist.ArtistAdapter
import io.github.muntashirakon.music.adapter.song.SongAdapter import io.github.muntashirakon.music.adapter.song.SongAdapter
import io.github.muntashirakon.music.extensions.show import io.github.muntashirakon.music.extensions.hide
import io.github.muntashirakon.music.fragments.albums.AlbumClickListener
import io.github.muntashirakon.music.fragments.artists.ArtistClickListener
import io.github.muntashirakon.music.glide.SongGlideRequest import io.github.muntashirakon.music.glide.SongGlideRequest
import io.github.muntashirakon.music.helper.MusicPlayerRemote import io.github.muntashirakon.music.helper.MusicPlayerRemote
import io.github.muntashirakon.music.loaders.PlaylistSongsLoader
import io.github.muntashirakon.music.model.* import io.github.muntashirakon.music.model.*
import io.github.muntashirakon.music.model.Playlist
import io.github.muntashirakon.music.util.PreferenceUtil import io.github.muntashirakon.music.util.PreferenceUtil
import com.bumptech.glide.Glide
import com.google.android.material.card.MaterialCardView
class HomeAdapter( class HomeAdapter(
private val activity: AppCompatActivity, private val activity: AppCompatActivity
private val displayMetrics: DisplayMetrics ) : RecyclerView.Adapter<RecyclerView.ViewHolder>(), ArtistClickListener, AlbumClickListener {
) : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
private var list = listOf<Home>() private var list = listOf<Home>()
@ -45,14 +44,9 @@ class HomeAdapter(
.inflate(R.layout.section_recycler_view, parent, false) .inflate(R.layout.section_recycler_view, parent, false)
return when (viewType) { return when (viewType) {
RECENT_ARTISTS, TOP_ARTISTS -> ArtistViewHolder(layout) RECENT_ARTISTS, TOP_ARTISTS -> ArtistViewHolder(layout)
TOP_ALBUMS, RECENT_ALBUMS -> {
AlbumViewHolder(
LayoutInflater.from(activity)
.inflate(R.layout.metal_section_recycler_view, parent, false)
)
}
GENRES -> GenreViewHolder(layout) GENRES -> GenreViewHolder(layout)
FAVOURITES -> PlaylistViewHolder(layout) FAVOURITES -> PlaylistViewHolder(layout)
TOP_ALBUMS, RECENT_ALBUMS -> AlbumViewHolder(layout)
else -> { else -> {
SuggestionsViewHolder( SuggestionsViewHolder(
LayoutInflater.from(activity).inflate( LayoutInflater.from(activity).inflate(
@ -66,48 +60,62 @@ class HomeAdapter(
} }
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
val home = list[position]
when (getItemViewType(position)) { when (getItemViewType(position)) {
RECENT_ALBUMS -> { RECENT_ALBUMS -> {
val viewHolder = holder as AlbumViewHolder val viewHolder = holder as AlbumViewHolder
viewHolder.bindView( viewHolder.bindView(home.arrayList as List<Album>, R.string.recent_albums)
list[position].arrayList as List<Album>, viewHolder.clickableArea.setOnClickListener {
R.string.recent_albums activity.findNavController(R.id.fragment_container).navigate(
) R.id.detailListFragment,
bundleOf("type" to RECENT_ALBUMS)
)
}
} }
TOP_ALBUMS -> { TOP_ALBUMS -> {
val viewHolder = holder as AlbumViewHolder val viewHolder = holder as AlbumViewHolder
viewHolder.bindView( viewHolder.bindView(home.arrayList as List<Album>, R.string.top_albums)
list[position].arrayList as List<Album>, viewHolder.clickableArea.setOnClickListener {
R.string.top_albums activity.findNavController(R.id.fragment_container).navigate(
) R.id.detailListFragment,
bundleOf("type" to TOP_ALBUMS)
)
}
} }
RECENT_ARTISTS -> { RECENT_ARTISTS -> {
val viewHolder = holder as ArtistViewHolder val viewHolder = holder as ArtistViewHolder
viewHolder.bindView( viewHolder.bindView(home.arrayList, R.string.recent_artists)
list[position].arrayList as List<Artist>, viewHolder.clickableArea.setOnClickListener {
R.string.recent_artists activity.findNavController(R.id.fragment_container).navigate(
) R.id.detailListFragment,
bundleOf("type" to RECENT_ARTISTS)
)
}
} }
TOP_ARTISTS -> { TOP_ARTISTS -> {
val viewHolder = holder as ArtistViewHolder val viewHolder = holder as ArtistViewHolder
viewHolder.bindView(list[position].arrayList as List<Artist>, R.string.top_artists) viewHolder.bindView(home.arrayList, R.string.top_artists)
viewHolder.clickableArea.setOnClickListener {
activity.findNavController(R.id.fragment_container).navigate(
R.id.detailListFragment,
bundleOf("type" to TOP_ARTISTS)
)
}
} }
SUGGESTIONS -> { SUGGESTIONS -> {
val viewHolder = holder as SuggestionsViewHolder val viewHolder = holder as SuggestionsViewHolder
viewHolder.bindView( viewHolder.bindView(home.arrayList)
list[position].arrayList as List<Song>
)
} }
FAVOURITES -> { FAVOURITES -> {
val viewHolder = holder as PlaylistViewHolder val viewHolder = holder as PlaylistViewHolder
viewHolder.bindView( viewHolder.bindView(home.arrayList, R.string.favorites)
list[position].arrayList as List<Playlist>,
R.string.favorites
)
} }
GENRES -> { GENRES -> {
val viewHolder = holder as GenreViewHolder val viewHolder = holder as GenreViewHolder
viewHolder.bind(list[position].arrayList as List<Genre>, R.string.genres) viewHolder.bind(home.arrayList, R.string.genres)
}
PLAYLISTS -> {
} }
} }
} }
@ -121,60 +129,23 @@ class HomeAdapter(
notifyDataSetChanged() notifyDataSetChanged()
} }
companion object {
@IntDef(
RECENT_ALBUMS,
TOP_ALBUMS,
RECENT_ARTISTS,
TOP_ARTISTS,
SUGGESTIONS,
FAVOURITES,
GENRES
)
@Retention(AnnotationRetention.SOURCE)
annotation class HomeSection
const val RECENT_ALBUMS = 3
const val TOP_ALBUMS = 1
const val RECENT_ARTISTS = 2
const val TOP_ARTISTS = 0
const val SUGGESTIONS = 5
const val FAVOURITES = 4
const val GENRES = 6
}
private inner class AlbumViewHolder(view: View) : AbsHomeViewItem(view) { private inner class AlbumViewHolder(view: View) : AbsHomeViewItem(view) {
fun bindView(list: List<Album>, titleRes: Int) { fun bindView(albums: List<Album>, titleRes: Int) {
if (list.isNotEmpty()) { title.text = activity.getString(titleRes)
recyclerView.apply { recyclerView.apply {
show() adapter = albumAdapter(albums)
adapter = AlbumAdapter(activity, list, R.layout.pager_item, null) layoutManager = gridLayoutManager()
layoutManager =
PeekingLinearLayoutManager(activity, HORIZONTAL, false)
}
title.text = activity.getString(titleRes)
} }
} }
} }
inner class ArtistViewHolder(view: View) : AbsHomeViewItem(view) { private inner class ArtistViewHolder(view: View) : AbsHomeViewItem(view) {
fun bindView(list: List<Artist>, titleRes: Int) { fun bindView(artists: List<Any>, titleRes: Int) {
if (list.isNotEmpty()) { recyclerView.apply {
val manager = LinearLayoutManager(activity, LinearLayoutManager.HORIZONTAL, false) layoutManager = linearLayoutManager()
val artistAdapter = ArtistAdapter( adapter = artistsAdapter(artists as List<Artist>)
activity,
list,
PreferenceUtil.homeGridStyle,
null
)
recyclerView.apply {
show()
layoutManager = manager
adapter = artistAdapter
}
title.text = activity.getString(titleRes)
} }
title.text = activity.getString(titleRes)
} }
} }
@ -190,52 +161,50 @@ class HomeAdapter(
R.id.image8 R.id.image8
) )
fun bindView(arrayList: List<Song>) { fun bindView(songs: List<Any>) {
songs as List<Song>
val color = ThemeStore.accentColor(activity) val color = ThemeStore.accentColor(activity)
itemView.findViewById<TextView>(R.id.text).setTextColor(color) itemView.findViewById<TextView>(R.id.message).setTextColor(color)
itemView.findViewById<MaterialCardView>(R.id.card6).apply { itemView.findViewById<MaterialCardView>(R.id.card6).apply {
setCardBackgroundColor(ColorUtil.withAlpha(color, 0.2f)) setCardBackgroundColor(ColorUtil.withAlpha(color, 0.12f))
} }
if (arrayList.size > 9) images.forEachIndexed { index, id ->
images.forEachIndexed { index, i -> itemView.findViewById<View>(id).setOnClickListener {
itemView.findViewById<View>(i).setOnClickListener { MusicPlayerRemote.playNext(songs[index])
MusicPlayerRemote.playNext(arrayList[index])
}
SongGlideRequest.Builder.from(Glide.with(activity), arrayList[index])
.asBitmap()
.build()
.into(itemView.findViewById(i))
} }
SongGlideRequest.Builder.from(Glide.with(activity), songs[index])
.asBitmap()
.build()
.into(itemView.findViewById(id))
}
} }
} }
private inner class PlaylistViewHolder(view: View) : AbsHomeViewItem(view) { private inner class PlaylistViewHolder(view: View) : AbsHomeViewItem(view) {
fun bindView(arrayList: List<Playlist>, titleRes: Int) { fun bindView(songs: List<Any>, titleRes: Int) {
if (arrayList.isNotEmpty()) { arrow.hide()
val songs = PlaylistSongsLoader.getPlaylistSongList(activity, arrayList[0]) recyclerView.apply {
if (songs.isNotEmpty()) { val songAdapter = SongAdapter(
recyclerView.apply { activity,
show() songs as MutableList<Song>,
val songAdapter = R.layout.item_album_card, null
SongAdapter(activity, songs, R.layout.item_album_card, null) )
layoutManager = layoutManager = linearLayoutManager()
GridLayoutManager(activity, 1, GridLayoutManager.HORIZONTAL, false) adapter = songAdapter
adapter = songAdapter
}
title.text = activity.getString(titleRes)
}
} }
title.text = activity.getString(titleRes)
} }
} }
private inner class GenreViewHolder(itemView: View) : AbsHomeViewItem(itemView) { private inner class GenreViewHolder(itemView: View) : AbsHomeViewItem(itemView) {
fun bind(genres: List<Genre>, titleRes: Int) { fun bind(genres: List<Any>, titleRes: Int) {
arrow.hide()
title.text = activity.getString(titleRes) title.text = activity.getString(titleRes)
recyclerView.apply { recyclerView.apply {
show() layoutManager = GridLayoutManager(activity, 3, GridLayoutManager.HORIZONTAL, false)
layoutManager = GridLayoutManager(activity, 2, GridLayoutManager.HORIZONTAL, false) val genreAdapter =
val genreAdapter = GenreAdapter(activity, genres, R.layout.item_grid_genre) GenreAdapter(activity, genres as List<Genre>, R.layout.item_grid_genre)
adapter = genreAdapter adapter = genreAdapter
} }
} }
@ -244,5 +213,38 @@ class HomeAdapter(
open inner class AbsHomeViewItem(itemView: View) : RecyclerView.ViewHolder(itemView) { open inner class AbsHomeViewItem(itemView: View) : RecyclerView.ViewHolder(itemView) {
val recyclerView: RecyclerView = itemView.findViewById(R.id.recyclerView) val recyclerView: RecyclerView = itemView.findViewById(R.id.recyclerView)
val title: AppCompatTextView = itemView.findViewById(R.id.title) val title: AppCompatTextView = itemView.findViewById(R.id.title)
val arrow: ImageView = itemView.findViewById(R.id.arrow)
val clickableArea: ViewGroup = itemView.findViewById(R.id.clickable_area)
} }
}
fun artistsAdapter(artists: List<Artist>) =
ArtistAdapter(activity, artists, PreferenceUtil.homeGridStyle, null, this)
fun albumAdapter(albums: List<Album>) =
AlbumAdapter(activity, albums, R.layout.item_image, null, this)
fun gridLayoutManager() = GridLayoutManager(activity, 1, GridLayoutManager.HORIZONTAL, false)
fun linearLayoutManager() = LinearLayoutManager(activity, LinearLayoutManager.HORIZONTAL, false)
override fun onArtist(artistId: Int, imageView: ImageView) {
activity.findNavController(R.id.fragment_container).navigate(
R.id.artistDetailsFragment,
bundleOf(EXTRA_ARTIST_ID to artistId),
null,
FragmentNavigatorExtras(
imageView to activity.getString(R.string.transition_album_art)
)
)
}
override fun onAlbumClick(albumId: Int, view: View) {
activity.findNavController(R.id.fragment_container).navigate(
R.id.albumDetailsFragment,
bundleOf(EXTRA_ALBUM_ID to albumId),
null,
FragmentNavigatorExtras(
view to activity.getString(R.string.transition_album_art)
)
)
}
}

View file

@ -1,29 +1,28 @@
package io.github.muntashirakon.music.adapter package io.github.muntashirakon.music.adapter
import android.app.ActivityOptions
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import androidx.appcompat.app.AppCompatActivity import androidx.core.os.bundleOf
import androidx.fragment.app.FragmentActivity
import androidx.navigation.findNavController
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import code.name.monkey.appthemehelper.ThemeStore import code.name.monkey.appthemehelper.ThemeStore
import io.github.muntashirakon.music.R import com.bumptech.glide.Glide
import io.github.muntashirakon.music.*
import io.github.muntashirakon.music.adapter.base.MediaEntryViewHolder import io.github.muntashirakon.music.adapter.base.MediaEntryViewHolder
import io.github.muntashirakon.music.glide.AlbumGlideRequest import io.github.muntashirakon.music.glide.AlbumGlideRequest
import io.github.muntashirakon.music.glide.ArtistGlideRequest import io.github.muntashirakon.music.glide.ArtistGlideRequest
import io.github.muntashirakon.music.helper.MusicPlayerRemote import io.github.muntashirakon.music.helper.MusicPlayerRemote
import io.github.muntashirakon.music.helper.menu.SongMenuHelper import io.github.muntashirakon.music.helper.menu.SongMenuHelper
import io.github.muntashirakon.music.loaders.PlaylistSongsLoader
import io.github.muntashirakon.music.model.* import io.github.muntashirakon.music.model.*
import io.github.muntashirakon.music.model.smartplaylist.AbsSmartPlaylist import io.github.muntashirakon.music.model.smartplaylist.AbsSmartPlaylist
import io.github.muntashirakon.music.repository.PlaylistSongsLoader
import io.github.muntashirakon.music.util.MusicUtil import io.github.muntashirakon.music.util.MusicUtil
import io.github.muntashirakon.music.util.NavigationUtil
import com.bumptech.glide.Glide
import android.util.Pair as UtilPair
class SearchAdapter( class SearchAdapter(
private val activity: AppCompatActivity, private val activity: FragmentActivity,
private var dataSet: List<Any>? private var dataSet: List<Any>
) : RecyclerView.Adapter<SearchAdapter.ViewHolder>() { ) : RecyclerView.Adapter<SearchAdapter.ViewHolder>() {
fun swapDataSet(dataSet: MutableList<Any>) { fun swapDataSet(dataSet: MutableList<Any>) {
@ -32,11 +31,11 @@ class SearchAdapter(
} }
override fun getItemViewType(position: Int): Int { override fun getItemViewType(position: Int): Int {
if (dataSet!![position] is Album) return ALBUM if (dataSet[position] is Album) return ALBUM
if (dataSet!![position] is Artist) return ARTIST if (dataSet[position] is Artist) return ARTIST
if (dataSet!![position] is Genre) return GENRE if (dataSet[position] is Genre) return GENRE
if (dataSet!![position] is Playlist) return PLAYLIST if (dataSet[position] is Playlist) return PLAYLIST
return if (dataSet!![position] is Song) SONG else HEADER return if (dataSet[position] is Song) SONG else HEADER
} }
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
@ -57,35 +56,35 @@ class SearchAdapter(
override fun onBindViewHolder(holder: ViewHolder, position: Int) { override fun onBindViewHolder(holder: ViewHolder, position: Int) {
when (getItemViewType(position)) { when (getItemViewType(position)) {
ALBUM -> { ALBUM -> {
val album = dataSet?.get(position) as Album val album = dataSet.get(position) as Album
holder.title?.text = album.title holder.title?.text = album.title
holder.text?.text = album.artistName holder.text?.text = album.artistName
AlbumGlideRequest.Builder.from(Glide.with(activity), album.safeGetFirstSong()) AlbumGlideRequest.Builder.from(Glide.with(activity), album.safeGetFirstSong())
.checkIgnoreMediaStore(activity).build().into(holder.image) .checkIgnoreMediaStore(activity).build().into(holder.image)
} }
ARTIST -> { ARTIST -> {
val artist = dataSet?.get(position) as Artist val artist = dataSet.get(position) as Artist
holder.title?.text = artist.name holder.title?.text = artist.name
holder.text?.text = MusicUtil.getArtistInfoString(activity, artist) holder.text?.text = MusicUtil.getArtistInfoString(activity, artist)
ArtistGlideRequest.Builder.from(Glide.with(activity), artist).build() ArtistGlideRequest.Builder.from(Glide.with(activity), artist).build()
.into(holder.image) .into(holder.image)
} }
SONG -> { SONG -> {
val song = dataSet?.get(position) as Song val song = dataSet.get(position) as Song
holder.title?.text = song.title holder.title?.text = song.title
holder.text?.text = song.albumName holder.text?.text = song.albumName
} }
GENRE -> { GENRE -> {
val genre = dataSet?.get(position) as Genre val genre = dataSet.get(position) as Genre
holder.title?.text = genre.name holder.title?.text = genre.name
} }
PLAYLIST -> { PLAYLIST -> {
val playlist = dataSet?.get(position) as Playlist val playlist = dataSet.get(position) as Playlist
holder.title?.text = playlist.name holder.title?.text = playlist.name
holder.text?.text = MusicUtil.getPlaylistInfoString(activity, getSongs(playlist)) holder.text?.text = MusicUtil.getPlaylistInfoString(activity, getSongs(playlist))
} }
else -> { else -> {
holder.title?.text = dataSet?.get(position).toString() holder.title?.text = dataSet.get(position).toString()
holder.title?.setTextColor(ThemeStore.accentColor(activity)) holder.title?.setTextColor(ThemeStore.accentColor(activity))
} }
} }
@ -94,7 +93,7 @@ class SearchAdapter(
private fun getSongs(playlist: Playlist): java.util.ArrayList<Song> { private fun getSongs(playlist: Playlist): java.util.ArrayList<Song> {
val songs = java.util.ArrayList<Song>() val songs = java.util.ArrayList<Song>()
if (playlist is AbsSmartPlaylist) { if (playlist is AbsSmartPlaylist) {
songs.addAll(playlist.getSongs(activity)) songs.addAll(playlist.getSongs())
} else { } else {
songs.addAll(PlaylistSongsLoader.getPlaylistSongList(activity, playlist.id)) songs.addAll(PlaylistSongsLoader.getPlaylistSongList(activity, playlist.id))
} }
@ -102,7 +101,7 @@ class SearchAdapter(
} }
override fun getItemCount(): Int { override fun getItemCount(): Int {
return dataSet!!.size return dataSet.size
} }
inner class ViewHolder(itemView: View, itemViewType: Int) : MediaEntryViewHolder(itemView) { inner class ViewHolder(itemView: View, itemViewType: Int) : MediaEntryViewHolder(itemView) {
@ -113,7 +112,7 @@ class SearchAdapter(
menu?.visibility = View.VISIBLE menu?.visibility = View.VISIBLE
menu?.setOnClickListener(object : SongMenuHelper.OnClickSongMenu(activity) { menu?.setOnClickListener(object : SongMenuHelper.OnClickSongMenu(activity) {
override val song: Song override val song: Song
get() = dataSet!![layoutPosition] as Song get() = dataSet[layoutPosition] as Song
}) })
} else { } else {
menu?.visibility = View.GONE menu?.visibility = View.GONE
@ -130,27 +129,31 @@ class SearchAdapter(
} }
override fun onClick(v: View?) { override fun onClick(v: View?) {
val item = dataSet!![layoutPosition] val item = dataSet[layoutPosition]
when (itemViewType) { when (itemViewType) {
ALBUM -> { ALBUM -> {
val options = ActivityOptions.makeSceneTransitionAnimation( activity.findNavController(R.id.fragment_container).navigate(
activity, R.id.albumDetailsFragment,
UtilPair.create(image, activity.getString(R.string.transition_album_art)) bundleOf(EXTRA_ALBUM_ID to (item as Album).id)
) )
NavigationUtil.goToAlbumOptions(activity, (item as Album).id, options)
} }
ARTIST -> { ARTIST -> {
val options = ActivityOptions.makeSceneTransitionAnimation( activity.findNavController(R.id.fragment_container).navigate(
activity, R.id.artistDetailsFragment,
UtilPair.create(image, activity.getString(R.string.transition_artist_image)) bundleOf(EXTRA_ARTIST_ID to (item as Artist).id)
) )
NavigationUtil.goToArtistOptions(activity, (item as Artist).id, options)
} }
GENRE -> { GENRE -> {
NavigationUtil.goToGenre(activity, item as Genre) activity.findNavController(R.id.fragment_container).navigate(
R.id.genreDetailsFragment,
bundleOf(EXTRA_GENRE to (item as Genre))
)
} }
PLAYLIST -> { PLAYLIST -> {
NavigationUtil.goToPlaylistNew(activity, item as Playlist) activity.findNavController(R.id.fragment_container).navigate(
R.id.artistDetailsFragment,
bundleOf(EXTRA_PLAYLIST to (item as Playlist))
)
} }
SONG -> { SONG -> {
val playList = ArrayList<Song>() val playList = ArrayList<Song>()

View file

@ -22,20 +22,20 @@ import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import code.name.monkey.appthemehelper.util.ATHUtil import code.name.monkey.appthemehelper.util.ATHUtil
import com.bumptech.glide.Glide
import com.bumptech.glide.load.engine.DiskCacheStrategy
import com.bumptech.glide.signature.MediaStoreSignature
import io.github.muntashirakon.music.R import io.github.muntashirakon.music.R
import io.github.muntashirakon.music.adapter.base.AbsMultiSelectAdapter import io.github.muntashirakon.music.adapter.base.AbsMultiSelectAdapter
import io.github.muntashirakon.music.adapter.base.MediaEntryViewHolder import io.github.muntashirakon.music.adapter.base.MediaEntryViewHolder
import io.github.muntashirakon.music.glide.audiocover.AudioFileCover import io.github.muntashirakon.music.glide.audiocover.AudioFileCover
import io.github.muntashirakon.music.interfaces.CabHolder import io.github.muntashirakon.music.interfaces.CabHolder
import io.github.muntashirakon.music.interfaces.Callbacks
import io.github.muntashirakon.music.util.MusicUtil import io.github.muntashirakon.music.util.MusicUtil
import io.github.muntashirakon.music.util.RetroUtil import io.github.muntashirakon.music.util.RetroUtil
import com.bumptech.glide.Glide
import com.bumptech.glide.load.engine.DiskCacheStrategy
import com.bumptech.glide.signature.MediaStoreSignature
import me.zhanghai.android.fastscroll.PopupTextProvider import me.zhanghai.android.fastscroll.PopupTextProvider
import java.io.File import java.io.File
import java.text.DecimalFormat import java.text.DecimalFormat
import java.util.*
import kotlin.math.log10 import kotlin.math.log10
import kotlin.math.pow import kotlin.math.pow
@ -135,9 +135,9 @@ class SongFileAdapter(
return getFileTitle(`object`) return getFileTitle(`object`)
} }
override fun onMultipleItemAction(menuItem: MenuItem, selection: ArrayList<File>) { override fun onMultipleItemAction(menuItem: MenuItem, selection: List<File>) {
if (callbacks == null) return if (callbacks == null) return
callbacks.onMultipleItemAction(menuItem, selection) callbacks.onMultipleItemAction(menuItem, selection as ArrayList<File>)
} }
override fun getPopupText(position: Int): String { override fun getPopupText(position: Int): String {
@ -148,13 +148,6 @@ class SongFileAdapter(
return MusicUtil.getSectionName(dataSet[position].name) return MusicUtil.getSectionName(dataSet[position].name)
} }
interface Callbacks {
fun onFileSelected(file: File)
fun onFileMenuClicked(file: File, view: View)
fun onMultipleItemAction(item: MenuItem, files: ArrayList<File>)
}
inner class ViewHolder(itemView: View) : MediaEntryViewHolder(itemView) { inner class ViewHolder(itemView: View) : MediaEntryViewHolder(itemView) {

View file

@ -1,16 +1,17 @@
package io.github.muntashirakon.music.adapter.album package io.github.muntashirakon.music.adapter.album
import android.app.ActivityOptions
import android.content.res.ColorStateList import android.content.res.ColorStateList
import android.content.res.Resources import android.content.res.Resources
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.MenuItem import android.view.MenuItem
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import androidx.appcompat.app.AppCompatActivity import androidx.fragment.app.FragmentActivity
import com.bumptech.glide.Glide
import io.github.muntashirakon.music.R import io.github.muntashirakon.music.R
import io.github.muntashirakon.music.adapter.base.AbsMultiSelectAdapter import io.github.muntashirakon.music.adapter.base.AbsMultiSelectAdapter
import io.github.muntashirakon.music.adapter.base.MediaEntryViewHolder import io.github.muntashirakon.music.adapter.base.MediaEntryViewHolder
import io.github.muntashirakon.music.fragments.albums.AlbumClickListener
import io.github.muntashirakon.music.glide.AlbumGlideRequest import io.github.muntashirakon.music.glide.AlbumGlideRequest
import io.github.muntashirakon.music.glide.RetroMusicColoredTarget import io.github.muntashirakon.music.glide.RetroMusicColoredTarget
import io.github.muntashirakon.music.helper.MusicPlayerRemote import io.github.muntashirakon.music.helper.MusicPlayerRemote
@ -20,17 +21,16 @@ import io.github.muntashirakon.music.interfaces.CabHolder
import io.github.muntashirakon.music.model.Album import io.github.muntashirakon.music.model.Album
import io.github.muntashirakon.music.model.Song import io.github.muntashirakon.music.model.Song
import io.github.muntashirakon.music.util.MusicUtil import io.github.muntashirakon.music.util.MusicUtil
import io.github.muntashirakon.music.util.NavigationUtil
import io.github.muntashirakon.music.util.PreferenceUtil import io.github.muntashirakon.music.util.PreferenceUtil
import io.github.muntashirakon.music.util.color.MediaNotificationProcessor import io.github.muntashirakon.music.util.color.MediaNotificationProcessor
import com.bumptech.glide.Glide
import me.zhanghai.android.fastscroll.PopupTextProvider import me.zhanghai.android.fastscroll.PopupTextProvider
open class AlbumAdapter( open class AlbumAdapter(
protected val activity: AppCompatActivity, protected val activity: FragmentActivity,
var dataSet: List<Album>, var dataSet: List<Album>,
protected var itemLayoutRes: Int, protected var itemLayoutRes: Int,
cabHolder: CabHolder? cabHolder: CabHolder?,
private val albumClickListener: AlbumClickListener?
) : AbsMultiSelectAdapter<AlbumAdapter.ViewHolder, Album>( ) : AbsMultiSelectAdapter<AlbumAdapter.ViewHolder, Album>(
activity, activity,
cabHolder, cabHolder,
@ -129,12 +129,12 @@ open class AlbumAdapter(
} }
override fun onMultipleItemAction( override fun onMultipleItemAction(
menuItem: MenuItem, selection: ArrayList<Album> menuItem: MenuItem, selection: List<Album>
) { ) {
SongsMenuHelper.handleMenuClick(activity, getSongList(selection), menuItem.itemId) SongsMenuHelper.handleMenuClick(activity, getSongList(selection), menuItem.itemId)
} }
private fun getSongList(albums: List<Album>): ArrayList<Song> { private fun getSongList(albums: List<Album>): List<Song> {
val songs = ArrayList<Song>() val songs = ArrayList<Song>()
for (album in albums) { for (album in albums) {
songs.addAll(album.songs!!) songs.addAll(album.songs!!)
@ -156,7 +156,6 @@ open class AlbumAdapter(
dataSet[position].year dataSet[position].year
) )
} }
return MusicUtil.getSectionName(sectionName) return MusicUtil.getSectionName(sectionName)
} }
@ -172,16 +171,7 @@ open class AlbumAdapter(
if (isInQuickSelectMode) { if (isInQuickSelectMode) {
toggleChecked(layoutPosition) toggleChecked(layoutPosition)
} else { } else {
val activityOptions = ActivityOptions.makeSceneTransitionAnimation( image?.let { albumClickListener?.onAlbumClick(dataSet[layoutPosition].id, it) }
activity,
imageContainerCard ?: image,
activity.getString(R.string.transition_album_art)
)
NavigationUtil.goToAlbumOptions(
activity,
dataSet[layoutPosition].id,
activityOptions
)
} }
} }

View file

@ -1,10 +1,11 @@
package io.github.muntashirakon.music.adapter.album package io.github.muntashirakon.music.adapter.album
import android.graphics.drawable.Drawable
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import androidx.appcompat.app.AppCompatActivity import androidx.fragment.app.FragmentActivity
import code.name.monkey.appthemehelper.util.ATHUtil import code.name.monkey.appthemehelper.util.ATHUtil
import com.bumptech.glide.Glide
import io.github.muntashirakon.music.fragments.albums.AlbumClickListener
import io.github.muntashirakon.music.glide.AlbumGlideRequest import io.github.muntashirakon.music.glide.AlbumGlideRequest
import io.github.muntashirakon.music.glide.RetroMusicColoredTarget import io.github.muntashirakon.music.glide.RetroMusicColoredTarget
import io.github.muntashirakon.music.helper.HorizontalAdapterHelper import io.github.muntashirakon.music.helper.HorizontalAdapterHelper
@ -12,14 +13,14 @@ import io.github.muntashirakon.music.interfaces.CabHolder
import io.github.muntashirakon.music.model.Album import io.github.muntashirakon.music.model.Album
import io.github.muntashirakon.music.util.MusicUtil import io.github.muntashirakon.music.util.MusicUtil
import io.github.muntashirakon.music.util.color.MediaNotificationProcessor import io.github.muntashirakon.music.util.color.MediaNotificationProcessor
import com.bumptech.glide.Glide
class HorizontalAlbumAdapter( class HorizontalAlbumAdapter(
activity: AppCompatActivity, activity: FragmentActivity,
dataSet: List<Album>, dataSet: List<Album>,
cabHolder: CabHolder? cabHolder: CabHolder?,
albumClickListener: AlbumClickListener
) : AlbumAdapter( ) : AlbumAdapter(
activity, dataSet, HorizontalAdapterHelper.LAYOUT_RES, cabHolder activity, dataSet, HorizontalAdapterHelper.LAYOUT_RES, cabHolder, albumClickListener
) { ) {
override fun createViewHolder(view: View, viewType: Int): ViewHolder { override fun createViewHolder(view: View, viewType: Int): ViewHolder {
@ -40,11 +41,6 @@ class HorizontalAlbumAdapter(
.generatePalette(activity) .generatePalette(activity)
.build() .build()
.into(object : RetroMusicColoredTarget(holder.image!!) { .into(object : RetroMusicColoredTarget(holder.image!!) {
override fun onLoadCleared(placeholder: Drawable?) {
super.onLoadCleared(placeholder)
//setColors(albumArtistFooterColor, holder)
}
override fun onColorReady(colors: MediaNotificationProcessor) { override fun onColorReady(colors: MediaNotificationProcessor) {
setColors(colors, holder) setColors(colors, holder)
} }

View file

@ -1,17 +1,19 @@
package io.github.muntashirakon.music.adapter.artist package io.github.muntashirakon.music.adapter.artist
import android.app.ActivityOptions
import android.content.res.ColorStateList import android.content.res.ColorStateList
import android.content.res.Resources import android.content.res.Resources
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.MenuItem import android.view.MenuItem
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import androidx.appcompat.app.AppCompatActivity import androidx.core.view.ViewCompat
import androidx.fragment.app.FragmentActivity
import com.bumptech.glide.Glide
import io.github.muntashirakon.music.R import io.github.muntashirakon.music.R
import io.github.muntashirakon.music.adapter.base.AbsMultiSelectAdapter import io.github.muntashirakon.music.adapter.base.AbsMultiSelectAdapter
import io.github.muntashirakon.music.adapter.base.MediaEntryViewHolder import io.github.muntashirakon.music.adapter.base.MediaEntryViewHolder
import io.github.muntashirakon.music.extensions.hide import io.github.muntashirakon.music.extensions.hide
import io.github.muntashirakon.music.fragments.artists.ArtistClickListener
import io.github.muntashirakon.music.glide.ArtistGlideRequest import io.github.muntashirakon.music.glide.ArtistGlideRequest
import io.github.muntashirakon.music.glide.RetroMusicColoredTarget import io.github.muntashirakon.music.glide.RetroMusicColoredTarget
import io.github.muntashirakon.music.helper.menu.SongsMenuHelper import io.github.muntashirakon.music.helper.menu.SongsMenuHelper
@ -19,17 +21,16 @@ import io.github.muntashirakon.music.interfaces.CabHolder
import io.github.muntashirakon.music.model.Artist import io.github.muntashirakon.music.model.Artist
import io.github.muntashirakon.music.model.Song import io.github.muntashirakon.music.model.Song
import io.github.muntashirakon.music.util.MusicUtil import io.github.muntashirakon.music.util.MusicUtil
import io.github.muntashirakon.music.util.NavigationUtil
import io.github.muntashirakon.music.util.color.MediaNotificationProcessor import io.github.muntashirakon.music.util.color.MediaNotificationProcessor
import com.bumptech.glide.Glide
import me.zhanghai.android.fastscroll.PopupTextProvider import me.zhanghai.android.fastscroll.PopupTextProvider
import java.util.* import java.util.*
class ArtistAdapter( class ArtistAdapter(
val activity: AppCompatActivity, val activity: FragmentActivity,
var dataSet: List<Artist>, var dataSet: List<Artist>,
var itemLayoutRes: Int, var itemLayoutRes: Int,
cabHolder: CabHolder? cabHolder: CabHolder?,
private val artistClickListener: ArtistClickListener
) : AbsMultiSelectAdapter<ArtistAdapter.ViewHolder, Artist>( ) : AbsMultiSelectAdapter<ArtistAdapter.ViewHolder, Artist>(
activity, cabHolder, R.menu.menu_media_selection activity, cabHolder, R.menu.menu_media_selection
), PopupTextProvider { ), PopupTextProvider {
@ -106,12 +107,12 @@ class ArtistAdapter(
} }
override fun onMultipleItemAction( override fun onMultipleItemAction(
menuItem: MenuItem, selection: ArrayList<Artist> menuItem: MenuItem, selection: List<Artist>
) { ) {
SongsMenuHelper.handleMenuClick(activity, getSongList(selection), menuItem.itemId) SongsMenuHelper.handleMenuClick(activity, getSongList(selection), menuItem.itemId)
} }
private fun getSongList(artists: List<Artist>): ArrayList<Song> { private fun getSongList(artists: List<Artist>): List<Song> {
val songs = ArrayList<Song>() val songs = ArrayList<Song>()
for (artist in artists) { for (artist in artists) {
songs.addAll(artist.songs) // maybe async in future? songs.addAll(artist.songs) // maybe async in future?
@ -130,7 +131,6 @@ class ArtistAdapter(
inner class ViewHolder(itemView: View) : MediaEntryViewHolder(itemView) { inner class ViewHolder(itemView: View) : MediaEntryViewHolder(itemView) {
init { init {
setImageTransitionName(activity.getString(R.string.transition_artist_image))
menu?.visibility = View.GONE menu?.visibility = View.GONE
} }
@ -139,14 +139,13 @@ class ArtistAdapter(
if (isInQuickSelectMode) { if (isInQuickSelectMode) {
toggleChecked(layoutPosition) toggleChecked(layoutPosition)
} else { } else {
val activityOptions = ActivityOptions.makeSceneTransitionAnimation( image?.let {
activity, ViewCompat.setTransitionName(
imageContainerCard ?: image, it,
activity.getString(R.string.transition_artist_image) activity.getString(R.string.transition_artist_image)
) )
NavigationUtil.goToArtistOptions( artistClickListener.onArtist(dataSet[layoutPosition].id, it)
activity, dataSet[layoutPosition].id, activityOptions }
)
} }
} }

View file

@ -12,6 +12,7 @@ import androidx.recyclerview.widget.RecyclerView;
import com.afollestad.materialcab.MaterialCab; import com.afollestad.materialcab.MaterialCab;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List;
import io.github.muntashirakon.music.R; import io.github.muntashirakon.music.R;
import io.github.muntashirakon.music.interfaces.CabHolder; import io.github.muntashirakon.music.interfaces.CabHolder;
@ -24,7 +25,7 @@ public abstract class AbsMultiSelectAdapter<V extends RecyclerView.ViewHolder, I
private final CabHolder cabHolder; private final CabHolder cabHolder;
private final Context context; private final Context context;
private MaterialCab cab; private MaterialCab cab;
private ArrayList<I> checked; private List<I> checked;
private int menuRes; private int menuRes;
public AbsMultiSelectAdapter(@NonNull Context context, @Nullable CabHolder cabHolder, @MenuRes int menuRes) { public AbsMultiSelectAdapter(@NonNull Context context, @Nullable CabHolder cabHolder, @MenuRes int menuRes) {
@ -86,7 +87,7 @@ public abstract class AbsMultiSelectAdapter<V extends RecyclerView.ViewHolder, I
return cab != null && cab.isActive(); return cab != null && cab.isActive();
} }
protected abstract void onMultipleItemAction(MenuItem menuItem, ArrayList<I> selection); protected abstract void onMultipleItemAction(MenuItem menuItem, List<I> selection);
protected void setMultiSelectMenuRes(@MenuRes int menuRes) { protected void setMultiSelectMenuRes(@MenuRes int menuRes) {
this.menuRes = menuRes; this.menuRes = menuRes;

View file

@ -9,11 +9,14 @@ import android.view.LayoutInflater
import android.view.MenuItem import android.view.MenuItem
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.widget.PopupMenu import androidx.appcompat.widget.PopupMenu
import androidx.core.os.bundleOf
import androidx.fragment.app.FragmentActivity
import androidx.navigation.findNavController
import code.name.monkey.appthemehelper.ThemeStore import code.name.monkey.appthemehelper.ThemeStore
import code.name.monkey.appthemehelper.util.ATHUtil import code.name.monkey.appthemehelper.util.ATHUtil
import code.name.monkey.appthemehelper.util.TintHelper import code.name.monkey.appthemehelper.util.TintHelper
import io.github.muntashirakon.music.EXTRA_PLAYLIST
import io.github.muntashirakon.music.R import io.github.muntashirakon.music.R
import io.github.muntashirakon.music.adapter.base.AbsMultiSelectAdapter import io.github.muntashirakon.music.adapter.base.AbsMultiSelectAdapter
import io.github.muntashirakon.music.adapter.base.MediaEntryViewHolder import io.github.muntashirakon.music.adapter.base.MediaEntryViewHolder
@ -22,19 +25,18 @@ import io.github.muntashirakon.music.extensions.show
import io.github.muntashirakon.music.helper.menu.PlaylistMenuHelper import io.github.muntashirakon.music.helper.menu.PlaylistMenuHelper
import io.github.muntashirakon.music.helper.menu.SongsMenuHelper import io.github.muntashirakon.music.helper.menu.SongsMenuHelper
import io.github.muntashirakon.music.interfaces.CabHolder import io.github.muntashirakon.music.interfaces.CabHolder
import io.github.muntashirakon.music.loaders.PlaylistSongsLoader
import io.github.muntashirakon.music.model.AbsCustomPlaylist import io.github.muntashirakon.music.model.AbsCustomPlaylist
import io.github.muntashirakon.music.model.Playlist import io.github.muntashirakon.music.model.Playlist
import io.github.muntashirakon.music.model.Song import io.github.muntashirakon.music.model.Song
import io.github.muntashirakon.music.model.smartplaylist.AbsSmartPlaylist import io.github.muntashirakon.music.model.smartplaylist.AbsSmartPlaylist
import io.github.muntashirakon.music.repository.PlaylistSongsLoader
import io.github.muntashirakon.music.util.AutoGeneratedPlaylistBitmap import io.github.muntashirakon.music.util.AutoGeneratedPlaylistBitmap
import io.github.muntashirakon.music.util.MusicUtil import io.github.muntashirakon.music.util.MusicUtil
import io.github.muntashirakon.music.util.NavigationUtil
import io.github.muntashirakon.music.util.RetroColorUtil import io.github.muntashirakon.music.util.RetroColorUtil
import java.util.* import java.util.*
class PlaylistAdapter( class PlaylistAdapter(
private val activity: AppCompatActivity, private val activity: FragmentActivity,
var dataSet: List<Playlist>, var dataSet: List<Playlist>,
private var itemLayoutRes: Int, private var itemLayoutRes: Int,
cabHolder: CabHolder? cabHolder: CabHolder?
@ -120,7 +122,7 @@ class PlaylistAdapter(
return playlist.name return playlist.name
} }
override fun onMultipleItemAction(menuItem: MenuItem, selection: ArrayList<Playlist>) { override fun onMultipleItemAction(menuItem: MenuItem, selection: List<Playlist>) {
when (menuItem.itemId) { when (menuItem.itemId) {
else -> SongsMenuHelper.handleMenuClick( else -> SongsMenuHelper.handleMenuClick(
activity, activity,
@ -130,11 +132,11 @@ class PlaylistAdapter(
} }
} }
private fun getSongList(playlists: List<Playlist>): ArrayList<Song> { private fun getSongList(playlists: List<Playlist>): List<Song> {
val songs = ArrayList<Song>() val songs = ArrayList<Song>()
for (playlist in playlists) { for (playlist in playlists) {
if (playlist is AbsCustomPlaylist) { if (playlist is AbsCustomPlaylist) {
songs.addAll(playlist.getSongs(activity)) songs.addAll(playlist.songs())
} else { } else {
songs.addAll(PlaylistSongsLoader.getPlaylistSongList(activity, playlist.id)) songs.addAll(PlaylistSongsLoader.getPlaylistSongList(activity, playlist.id))
} }
@ -142,12 +144,12 @@ class PlaylistAdapter(
return songs return songs
} }
private fun getSongs(playlist: Playlist): ArrayList<Song> { private fun getSongs(playlist: Playlist): List<Song> {
val songs = ArrayList<Song>() val songs = ArrayList<Song>()
if (playlist is AbsSmartPlaylist) { if (playlist is AbsSmartPlaylist) {
songs.addAll(playlist.getSongs(activity)) songs.addAll(playlist.songs())
} else { } else {
songs.addAll(PlaylistSongsLoader.getPlaylistSongList(activity, playlist.id)) songs.addAll(playlist.getSongs())
} }
return songs return songs
} }
@ -165,9 +167,7 @@ class PlaylistAdapter(
val popupMenu = PopupMenu(activity, view) val popupMenu = PopupMenu(activity, view)
popupMenu.inflate(R.menu.menu_item_playlist) popupMenu.inflate(R.menu.menu_item_playlist)
popupMenu.setOnMenuItemClickListener { item -> popupMenu.setOnMenuItemClickListener { item ->
PlaylistMenuHelper.handleMenuClick( PlaylistMenuHelper.handleMenuClick(activity, dataSet[layoutPosition], item)
activity, dataSet[layoutPosition], item
)
} }
popupMenu.show() popupMenu.show()
} }
@ -182,8 +182,10 @@ class PlaylistAdapter(
if (isInQuickSelectMode) { if (isInQuickSelectMode) {
toggleChecked(layoutPosition) toggleChecked(layoutPosition)
} else { } else {
val playlist = dataSet[layoutPosition] activity.findNavController(R.id.fragment_container).navigate(
NavigationUtil.goToPlaylistNew(activity, playlist) R.id.playlistDetailsFragment,
bundleOf(EXTRA_PLAYLIST to dataSet[layoutPosition])
)
} }
} }

View file

@ -2,7 +2,11 @@ package io.github.muntashirakon.music.adapter.song
import android.view.MenuItem import android.view.MenuItem
import android.view.View import android.view.View
import androidx.appcompat.app.AppCompatActivity import androidx.fragment.app.FragmentActivity
import com.h6ah4i.android.widget.advrecyclerview.draggable.DraggableItemAdapter
import com.h6ah4i.android.widget.advrecyclerview.draggable.DraggableItemViewHolder
import com.h6ah4i.android.widget.advrecyclerview.draggable.ItemDraggableRange
import com.h6ah4i.android.widget.advrecyclerview.draggable.annotation.DraggableItemStateFlags
import io.github.muntashirakon.music.R import io.github.muntashirakon.music.R
import io.github.muntashirakon.music.R.menu import io.github.muntashirakon.music.R.menu
import io.github.muntashirakon.music.dialogs.RemoveFromPlaylistDialog import io.github.muntashirakon.music.dialogs.RemoveFromPlaylistDialog
@ -10,19 +14,18 @@ import io.github.muntashirakon.music.interfaces.CabHolder
import io.github.muntashirakon.music.model.PlaylistSong import io.github.muntashirakon.music.model.PlaylistSong
import io.github.muntashirakon.music.model.Song import io.github.muntashirakon.music.model.Song
import io.github.muntashirakon.music.util.ViewUtil import io.github.muntashirakon.music.util.ViewUtil
import com.h6ah4i.android.widget.advrecyclerview.draggable.DraggableItemAdapter
import com.h6ah4i.android.widget.advrecyclerview.draggable.DraggableItemViewHolder
import com.h6ah4i.android.widget.advrecyclerview.draggable.ItemDraggableRange
import com.h6ah4i.android.widget.advrecyclerview.draggable.annotation.DraggableItemStateFlags
class OrderablePlaylistSongAdapter( class OrderablePlaylistSongAdapter(
activity: AppCompatActivity, activity: FragmentActivity,
dataSet: ArrayList<Song>, dataSet: ArrayList<Song>,
itemLayoutRes: Int, itemLayoutRes: Int,
cabHolder: CabHolder?, cabHolder: CabHolder?,
private val onMoveItemListener: OnMoveItemListener? private val onMoveItemListener: OnMoveItemListener?
) : PlaylistSongAdapter( ) : SongAdapter(
activity, dataSet, itemLayoutRes, cabHolder activity,
dataSet,
itemLayoutRes,
cabHolder
), DraggableItemAdapter<OrderablePlaylistSongAdapter.ViewHolder> { ), DraggableItemAdapter<OrderablePlaylistSongAdapter.ViewHolder> {
init { init {
@ -48,7 +51,7 @@ class OrderablePlaylistSongAdapter(
return long return long
} }
override fun onMultipleItemAction(menuItem: MenuItem, selection: ArrayList<Song>) { override fun onMultipleItemAction(menuItem: MenuItem, selection: List<Song>) {
when (menuItem.itemId) { when (menuItem.itemId) {
R.id.action_remove_from_playlist -> { R.id.action_remove_from_playlist -> {
RemoveFromPlaylistDialog.create(selection as ArrayList<PlaylistSong>) RemoveFromPlaylistDialog.create(selection as ArrayList<PlaylistSong>)
@ -91,7 +94,7 @@ class OrderablePlaylistSongAdapter(
fun onMoveItem(fromPosition: Int, toPosition: Int) fun onMoveItem(fromPosition: Int, toPosition: Int)
} }
inner class ViewHolder(itemView: View) : PlaylistSongAdapter.ViewHolder(itemView), inner class ViewHolder(itemView: View) : SongAdapter.ViewHolder(itemView),
DraggableItemViewHolder { DraggableItemViewHolder {
@DraggableItemStateFlags @DraggableItemStateFlags
private var mDragStateFlags: Int = 0 private var mDragStateFlags: Int = 0
@ -132,8 +135,4 @@ class OrderablePlaylistSongAdapter(
mDragStateFlags = flags mDragStateFlags = flags
} }
} }
companion object {
val TAG: String = OrderablePlaylistSongAdapter::class.java.simpleName
}
} }

View file

@ -3,14 +3,7 @@ package io.github.muntashirakon.music.adapter.song
import android.view.MenuItem import android.view.MenuItem
import android.view.View import android.view.View
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import io.github.muntashirakon.music.R import androidx.fragment.app.FragmentActivity
import io.github.muntashirakon.music.helper.MusicPlayerRemote
import io.github.muntashirakon.music.helper.MusicPlayerRemote.isPlaying
import io.github.muntashirakon.music.helper.MusicPlayerRemote.playNextSong
import io.github.muntashirakon.music.helper.MusicPlayerRemote.removeFromQueue
import io.github.muntashirakon.music.model.Song
import io.github.muntashirakon.music.util.MusicUtil
import io.github.muntashirakon.music.util.ViewUtil
import com.h6ah4i.android.widget.advrecyclerview.draggable.DraggableItemAdapter import com.h6ah4i.android.widget.advrecyclerview.draggable.DraggableItemAdapter
import com.h6ah4i.android.widget.advrecyclerview.draggable.ItemDraggableRange import com.h6ah4i.android.widget.advrecyclerview.draggable.ItemDraggableRange
import com.h6ah4i.android.widget.advrecyclerview.draggable.annotation.DraggableItemStateFlags import com.h6ah4i.android.widget.advrecyclerview.draggable.annotation.DraggableItemStateFlags
@ -20,6 +13,14 @@ import com.h6ah4i.android.widget.advrecyclerview.swipeable.action.SwipeResultAct
import com.h6ah4i.android.widget.advrecyclerview.swipeable.action.SwipeResultActionDefault import com.h6ah4i.android.widget.advrecyclerview.swipeable.action.SwipeResultActionDefault
import com.h6ah4i.android.widget.advrecyclerview.swipeable.action.SwipeResultActionRemoveItem import com.h6ah4i.android.widget.advrecyclerview.swipeable.action.SwipeResultActionRemoveItem
import com.h6ah4i.android.widget.advrecyclerview.swipeable.annotation.SwipeableItemResults import com.h6ah4i.android.widget.advrecyclerview.swipeable.annotation.SwipeableItemResults
import io.github.muntashirakon.music.R
import io.github.muntashirakon.music.helper.MusicPlayerRemote
import io.github.muntashirakon.music.helper.MusicPlayerRemote.isPlaying
import io.github.muntashirakon.music.helper.MusicPlayerRemote.playNextSong
import io.github.muntashirakon.music.helper.MusicPlayerRemote.removeFromQueue
import io.github.muntashirakon.music.model.Song
import io.github.muntashirakon.music.util.MusicUtil
import io.github.muntashirakon.music.util.ViewUtil
import me.zhanghai.android.fastscroll.PopupTextProvider import me.zhanghai.android.fastscroll.PopupTextProvider
class PlayingQueueAdapter( class PlayingQueueAdapter(
@ -192,7 +193,7 @@ class PlayingQueueAdapter(
internal class SwipedResultActionRemoveItem( internal class SwipedResultActionRemoveItem(
private val adapter: PlayingQueueAdapter, private val adapter: PlayingQueueAdapter,
private val position: Int, private val position: Int,
private val activity: AppCompatActivity private val activity: FragmentActivity
) : SwipeResultActionRemoveItem() { ) : SwipeResultActionRemoveItem() {
private var songToRemove: Song? = null private var songToRemove: Song? = null

View file

@ -1,15 +1,16 @@
package io.github.muntashirakon.music.adapter.song package io.github.muntashirakon.music.adapter.song
import android.app.ActivityOptions
import android.view.MenuItem import android.view.MenuItem
import android.view.View import android.view.View
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.core.os.bundleOf
import androidx.navigation.findNavController
import com.google.android.material.button.MaterialButton
import io.github.muntashirakon.music.EXTRA_ALBUM_ID
import io.github.muntashirakon.music.R import io.github.muntashirakon.music.R
import io.github.muntashirakon.music.helper.MusicPlayerRemote import io.github.muntashirakon.music.helper.MusicPlayerRemote
import io.github.muntashirakon.music.interfaces.CabHolder import io.github.muntashirakon.music.interfaces.CabHolder
import io.github.muntashirakon.music.model.Song import io.github.muntashirakon.music.model.Song
import io.github.muntashirakon.music.util.NavigationUtil
import com.google.android.material.button.MaterialButton
open class PlaylistSongAdapter( open class PlaylistSongAdapter(
activity: AppCompatActivity, activity: AppCompatActivity,
@ -57,19 +58,14 @@ open class PlaylistSongAdapter(
override fun onSongMenuItemClick(item: MenuItem): Boolean { override fun onSongMenuItemClick(item: MenuItem): Boolean {
if (item.itemId == R.id.action_go_to_album) { if (item.itemId == R.id.action_go_to_album) {
val activityOptions = ActivityOptions.makeSceneTransitionAnimation( activity.findNavController(R.id.fragment_container)
activity, .navigate(
imageContainerCard ?: image, R.id.albumDetailsFragment,
activity.getString(R.string.transition_album_art) bundleOf(EXTRA_ALBUM_ID to song.albumId)
) )
NavigationUtil.goToAlbumOptions(activity, song.albumId, activityOptions)
return true return true
} }
return super.onSongMenuItemClick(item) return super.onSongMenuItemClick(item)
} }
} }
companion object {
val TAG: String = PlaylistSongAdapter::class.java.simpleName
}
} }

View file

@ -2,14 +2,14 @@ package io.github.muntashirakon.music.adapter.song
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.ViewGroup import android.view.ViewGroup
import androidx.appcompat.app.AppCompatActivity import androidx.fragment.app.FragmentActivity
import io.github.muntashirakon.music.interfaces.CabHolder import io.github.muntashirakon.music.interfaces.CabHolder
import io.github.muntashirakon.music.model.Song import io.github.muntashirakon.music.model.Song
import io.github.muntashirakon.music.util.MusicUtil import io.github.muntashirakon.music.util.MusicUtil
import java.util.* import java.util.*
class SimpleSongAdapter( class SimpleSongAdapter(
context: AppCompatActivity, context: FragmentActivity,
songs: ArrayList<Song>, songs: ArrayList<Song>,
layoutRes: Int, layoutRes: Int,
cabHolder: CabHolder? cabHolder: CabHolder?

View file

@ -1,13 +1,17 @@
package io.github.muntashirakon.music.adapter.song package io.github.muntashirakon.music.adapter.song
import android.app.ActivityOptions
import android.content.res.ColorStateList import android.content.res.ColorStateList
import android.content.res.Resources import android.content.res.Resources
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.MenuItem import android.view.MenuItem
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import androidx.appcompat.app.AppCompatActivity import androidx.core.os.bundleOf
import androidx.fragment.app.FragmentActivity
import androidx.navigation.findNavController
import com.afollestad.materialcab.MaterialCab
import com.bumptech.glide.Glide
import io.github.muntashirakon.music.EXTRA_ALBUM_ID
import io.github.muntashirakon.music.R import io.github.muntashirakon.music.R
import io.github.muntashirakon.music.adapter.base.AbsMultiSelectAdapter import io.github.muntashirakon.music.adapter.base.AbsMultiSelectAdapter
import io.github.muntashirakon.music.adapter.base.MediaEntryViewHolder import io.github.muntashirakon.music.adapter.base.MediaEntryViewHolder
@ -22,11 +26,8 @@ import io.github.muntashirakon.music.helper.menu.SongsMenuHelper
import io.github.muntashirakon.music.interfaces.CabHolder import io.github.muntashirakon.music.interfaces.CabHolder
import io.github.muntashirakon.music.model.Song import io.github.muntashirakon.music.model.Song
import io.github.muntashirakon.music.util.MusicUtil import io.github.muntashirakon.music.util.MusicUtil
import io.github.muntashirakon.music.util.NavigationUtil
import io.github.muntashirakon.music.util.PreferenceUtil import io.github.muntashirakon.music.util.PreferenceUtil
import io.github.muntashirakon.music.util.color.MediaNotificationProcessor import io.github.muntashirakon.music.util.color.MediaNotificationProcessor
import com.afollestad.materialcab.MaterialCab
import com.bumptech.glide.Glide
import me.zhanghai.android.fastscroll.PopupTextProvider import me.zhanghai.android.fastscroll.PopupTextProvider
/** /**
@ -34,7 +35,7 @@ import me.zhanghai.android.fastscroll.PopupTextProvider
*/ */
open class SongAdapter( open class SongAdapter(
protected val activity: AppCompatActivity, protected val activity: FragmentActivity,
var dataSet: MutableList<Song>, var dataSet: MutableList<Song>,
protected var itemLayoutRes: Int, protected var itemLayoutRes: Int,
cabHolder: CabHolder?, cabHolder: CabHolder?,
@ -132,7 +133,7 @@ open class SongAdapter(
return song.title return song.title
} }
override fun onMultipleItemAction(menuItem: MenuItem, selection: ArrayList<Song>) { override fun onMultipleItemAction(menuItem: MenuItem, selection: List<Song>) {
SongsMenuHelper.handleMenuClick(activity, selection, menuItem.itemId) SongsMenuHelper.handleMenuClick(activity, selection, menuItem.itemId)
} }
@ -147,7 +148,6 @@ open class SongAdapter(
return "" return ""
} }
} }
return MusicUtil.getSectionName(sectionName) return MusicUtil.getSectionName(sectionName)
} }
@ -175,12 +175,11 @@ open class SongAdapter(
if (image != null && image!!.visibility == View.VISIBLE) { if (image != null && image!!.visibility == View.VISIBLE) {
when (item.itemId) { when (item.itemId) {
R.id.action_go_to_album -> { R.id.action_go_to_album -> {
val activityOptions = ActivityOptions.makeSceneTransitionAnimation( activity.findNavController(R.id.fragment_container)
activity, .navigate(
imageContainerCard ?: image, R.id.albumDetailsFragment,
activity.getString(R.string.transition_album_art) bundleOf(EXTRA_ALBUM_ID to song.albumId)
) )
NavigationUtil.goToAlbumOptions(activity, song.albumId, activityOptions)
return true return true
} }
} }

View file

@ -17,15 +17,13 @@ package io.github.muntashirakon.music.appshortcuts
import android.app.Activity import android.app.Activity
import android.content.Intent import android.content.Intent
import android.os.Bundle import android.os.Bundle
import io.github.muntashirakon.music.activities.search.SearchActivity
import io.github.muntashirakon.music.appshortcuts.shortcuttype.LastAddedShortcutType import io.github.muntashirakon.music.appshortcuts.shortcuttype.LastAddedShortcutType
import io.github.muntashirakon.music.appshortcuts.shortcuttype.SearchShortCutType
import io.github.muntashirakon.music.appshortcuts.shortcuttype.ShuffleAllShortcutType import io.github.muntashirakon.music.appshortcuts.shortcuttype.ShuffleAllShortcutType
import io.github.muntashirakon.music.appshortcuts.shortcuttype.TopTracksShortcutType import io.github.muntashirakon.music.appshortcuts.shortcuttype.TopTracksShortcutType
import io.github.muntashirakon.music.model.Playlist import io.github.muntashirakon.music.model.Playlist
import io.github.muntashirakon.music.model.smartplaylist.LastAddedPlaylist import io.github.muntashirakon.music.model.smartplaylist.LastAddedPlaylist
import io.github.muntashirakon.music.model.smartplaylist.MyTopTracksPlaylist
import io.github.muntashirakon.music.model.smartplaylist.ShuffleAllPlaylist import io.github.muntashirakon.music.model.smartplaylist.ShuffleAllPlaylist
import io.github.muntashirakon.music.model.smartplaylist.TopTracksPlaylist
import io.github.muntashirakon.music.service.MusicService import io.github.muntashirakon.music.service.MusicService
import io.github.muntashirakon.music.service.MusicService.* import io.github.muntashirakon.music.service.MusicService.*
@ -45,26 +43,22 @@ class AppShortcutLauncherActivity : Activity() {
when (shortcutType) { when (shortcutType) {
SHORTCUT_TYPE_SHUFFLE_ALL -> { SHORTCUT_TYPE_SHUFFLE_ALL -> {
startServiceWithPlaylist( startServiceWithPlaylist(
MusicService.SHUFFLE_MODE_SHUFFLE, ShuffleAllPlaylist(applicationContext) SHUFFLE_MODE_SHUFFLE, ShuffleAllPlaylist()
) )
DynamicShortcutManager.reportShortcutUsed(this, ShuffleAllShortcutType.id) DynamicShortcutManager.reportShortcutUsed(this, ShuffleAllShortcutType.id)
} }
SHORTCUT_TYPE_TOP_TRACKS -> { SHORTCUT_TYPE_TOP_TRACKS -> {
startServiceWithPlaylist( startServiceWithPlaylist(
MusicService.SHUFFLE_MODE_NONE, MyTopTracksPlaylist(applicationContext) SHUFFLE_MODE_NONE, TopTracksPlaylist()
) )
DynamicShortcutManager.reportShortcutUsed(this, TopTracksShortcutType.id) DynamicShortcutManager.reportShortcutUsed(this, TopTracksShortcutType.id)
} }
SHORTCUT_TYPE_LAST_ADDED -> { SHORTCUT_TYPE_LAST_ADDED -> {
startServiceWithPlaylist( startServiceWithPlaylist(
MusicService.SHUFFLE_MODE_NONE, LastAddedPlaylist(applicationContext) SHUFFLE_MODE_NONE, LastAddedPlaylist()
) )
DynamicShortcutManager.reportShortcutUsed(this, LastAddedShortcutType.id) DynamicShortcutManager.reportShortcutUsed(this, LastAddedShortcutType.id)
} }
SHORTCUT_TYPE_SEARCH -> {
startActivity(Intent(this, SearchActivity::class.java))
DynamicShortcutManager.reportShortcutUsed(this, SearchShortCutType.id)
}
} }
finish() finish()
} }
@ -87,7 +81,6 @@ class AppShortcutLauncherActivity : Activity() {
const val SHORTCUT_TYPE_SHUFFLE_ALL = 0 const val SHORTCUT_TYPE_SHUFFLE_ALL = 0
const val SHORTCUT_TYPE_TOP_TRACKS = 1 const val SHORTCUT_TYPE_TOP_TRACKS = 1
const val SHORTCUT_TYPE_LAST_ADDED = 2 const val SHORTCUT_TYPE_LAST_ADDED = 2
const val SHORTCUT_TYPE_SEARCH = 3
const val SHORTCUT_TYPE_NONE = 4 const val SHORTCUT_TYPE_NONE = 4
} }
} }

View file

@ -22,7 +22,6 @@ import android.content.pm.ShortcutManager
import android.graphics.drawable.Icon import android.graphics.drawable.Icon
import android.os.Build import android.os.Build
import io.github.muntashirakon.music.appshortcuts.shortcuttype.LastAddedShortcutType import io.github.muntashirakon.music.appshortcuts.shortcuttype.LastAddedShortcutType
import io.github.muntashirakon.music.appshortcuts.shortcuttype.SearchShortCutType
import io.github.muntashirakon.music.appshortcuts.shortcuttype.ShuffleAllShortcutType import io.github.muntashirakon.music.appshortcuts.shortcuttype.ShuffleAllShortcutType
import io.github.muntashirakon.music.appshortcuts.shortcuttype.TopTracksShortcutType import io.github.muntashirakon.music.appshortcuts.shortcuttype.TopTracksShortcutType
import java.util.* import java.util.*
@ -34,11 +33,9 @@ class DynamicShortcutManager(private val context: Context) {
private val defaultShortcuts: List<ShortcutInfo> private val defaultShortcuts: List<ShortcutInfo>
get() = Arrays.asList( get() = Arrays.asList(
SearchShortCutType(context).shortcutInfo,
ShuffleAllShortcutType(context).shortcutInfo, ShuffleAllShortcutType(context).shortcutInfo,
TopTracksShortcutType(context).shortcutInfo, TopTracksShortcutType(context).shortcutInfo,
LastAddedShortcutType(context).shortcutInfo LastAddedShortcutType(context).shortcutInfo
) )
fun initDynamicShortcuts() { fun initDynamicShortcuts() {

View file

@ -1,45 +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 io.github.muntashirakon.music.appshortcuts.shortcuttype
import android.annotation.TargetApi
import android.content.Context
import android.content.pm.ShortcutInfo
import android.os.Build
import io.github.muntashirakon.music.R
import io.github.muntashirakon.music.appshortcuts.AppShortcutIconGenerator
import io.github.muntashirakon.music.appshortcuts.AppShortcutLauncherActivity
@TargetApi(Build.VERSION_CODES.N_MR1)
class SearchShortCutType(context: Context) : BaseShortcutType(context) {
companion object {
val id: String
get() = BaseShortcutType.ID_PREFIX + "search"
}
override val shortcutInfo: ShortcutInfo
get() = ShortcutInfo.Builder(
context,
id
).setShortLabel(context.getString(R.string.action_search))
.setLongLabel(context.getString(R.string.search_hint)).setIcon(
AppShortcutIconGenerator.generateThemedIcon(
context,
R.drawable.ic_app_shortcut_search
)
).setIntent(getPlaySongsIntent(AppShortcutLauncherActivity.SHORTCUT_TYPE_SEARCH))
.build()
}

View file

@ -22,22 +22,22 @@ import io.github.muntashirakon.music.R
import io.github.muntashirakon.music.extensions.colorButtons import io.github.muntashirakon.music.extensions.colorButtons
import io.github.muntashirakon.music.extensions.extraNotNull import io.github.muntashirakon.music.extensions.extraNotNull
import io.github.muntashirakon.music.extensions.materialDialog import io.github.muntashirakon.music.extensions.materialDialog
import io.github.muntashirakon.music.loaders.PlaylistLoader
import io.github.muntashirakon.music.model.Song import io.github.muntashirakon.music.model.Song
import io.github.muntashirakon.music.repository.PlaylistRepository
import io.github.muntashirakon.music.util.PlaylistsUtil import io.github.muntashirakon.music.util.PlaylistsUtil
import org.koin.android.ext.android.inject
class AddToPlaylistDialog : DialogFragment() { class AddToPlaylistDialog : DialogFragment() {
private val playlistRepository by inject<PlaylistRepository>()
override fun onCreateDialog( override fun onCreateDialog(
savedInstanceState: Bundle? savedInstanceState: Bundle?
): Dialog { ): Dialog {
val playlists = PlaylistLoader.getAllPlaylists(requireContext()) val playlists = playlistRepository.playlists()
val playlistNames = mutableListOf<String>() val playlistNames = mutableListOf<String>()
playlistNames.add(requireContext().resources.getString(R.string.action_new_playlist)) playlistNames.add(requireContext().resources.getString(R.string.action_new_playlist))
for (p in playlists) { for (p in playlists) {
playlistNames.add(p.name) playlistNames.add(p.name)
} }
return materialDialog(R.string.add_playlist_title) return materialDialog(R.string.add_playlist_title)
.setItems(playlistNames.toTypedArray()) { _, which -> .setItems(playlistNames.toTypedArray()) { _, which ->
val songs = extraNotNull<ArrayList<Song>>(EXTRA_SONG).value val songs = extraNotNull<ArrayList<Song>>(EXTRA_SONG).value

View file

@ -98,7 +98,9 @@ class DeleteSongsDialog : DialogFragment() {
} }
fun deleteSongs(songs: List<Song>, safUris: List<Uri>?) { fun deleteSongs(songs: List<Song>, safUris: List<Uri>?) {
MusicUtil.deleteTracks(requireActivity(), songs, safUris) { this.dismiss() } MusicUtil.deleteTracks(requireActivity(), songs, safUris, Runnable {
dismiss()
})
} }
companion object { companion object {

View file

@ -22,11 +22,17 @@ import android.widget.Button
import android.widget.CheckBox import android.widget.CheckBox
import android.widget.SeekBar import android.widget.SeekBar
import androidx.annotation.AttrRes import androidx.annotation.AttrRes
import androidx.annotation.ColorInt
import androidx.appcompat.widget.Toolbar import androidx.appcompat.widget.Toolbar
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import code.name.monkey.appthemehelper.ThemeStore import code.name.monkey.appthemehelper.ThemeStore
import code.name.monkey.appthemehelper.util.ATHUtil import code.name.monkey.appthemehelper.util.ATHUtil
import code.name.monkey.appthemehelper.util.ColorUtil import code.name.monkey.appthemehelper.util.ColorUtil
import code.name.monkey.appthemehelper.util.MaterialValueHelper
import com.google.android.material.button.MaterialButton
import com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton
import com.google.android.material.textfield.TextInputEditText
import com.google.android.material.textfield.TextInputLayout
import io.github.muntashirakon.music.App import io.github.muntashirakon.music.App
import io.github.muntashirakon.music.R import io.github.muntashirakon.music.R
@ -83,4 +89,45 @@ fun SeekBar.addAccentColor() {
fun Button.accentTextColor() { fun Button.accentTextColor() {
setTextColor(ThemeStore.accentColor(App.getContext())) setTextColor(ThemeStore.accentColor(App.getContext()))
}
fun SeekBar.applyColor(@ColorInt color: Int) {
thumbTintList = ColorStateList.valueOf(color)
progressTintList = ColorStateList.valueOf(color)
progressBackgroundTintList = ColorStateList.valueOf(color)
}
fun ExtendedFloatingActionButton.accentColor() {
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
setTextColor(textColorStateList)
iconTint = textColorStateList
}
fun MaterialButton.applyColor(color: Int) {
val backgroundColorStateList = ColorStateList.valueOf(color)
val textColorColorStateList = ColorStateList.valueOf(
MaterialValueHelper.getPrimaryTextColor(
context,
ColorUtil.isColorLight(color)
)
)
backgroundTintList = backgroundColorStateList
setTextColor(textColorColorStateList)
iconTint = textColorColorStateList
}
fun TextInputLayout.accentColor() {
val accentColor = ThemeStore.accentColor(context)
val colorState = ColorStateList.valueOf(accentColor)
boxStrokeColor = accentColor
defaultHintTextColor = colorState
isHintAnimationEnabled = true
}
fun TextInputEditText.accentColor() {
} }

View file

@ -0,0 +1,20 @@
package io.github.muntashirakon.music.extensions
import android.app.Activity
import androidx.annotation.DimenRes
import androidx.appcompat.app.AppCompatActivity
import androidx.fragment.app.Fragment
fun AppCompatActivity.dimToPixel(@DimenRes dimenRes: Int): Int {
return resources.getDimensionPixelSize(dimenRes)
}
fun Activity.dipToPix(dpInFloat: Float): Float {
val scale = resources.displayMetrics.density
return dpInFloat * scale + 0.5f
}
fun Fragment.dipToPix(dpInFloat: Float): Float {
val scale = resources.displayMetrics.density
return dpInFloat * scale + 0.5f
}

View file

@ -3,7 +3,10 @@ package io.github.muntashirakon.music.extensions
import android.content.Context import android.content.Context
import android.content.res.Configuration import android.content.res.Configuration
import android.os.PowerManager import android.os.PowerManager
import android.widget.Toast
import androidx.annotation.IdRes
import androidx.annotation.IntegerRes import androidx.annotation.IntegerRes
import androidx.annotation.StringRes
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentManager import androidx.fragment.app.FragmentManager
@ -51,5 +54,23 @@ fun AppCompatActivity.currentFragment(navHostId: Int): Fragment? {
val navHostFragment: NavHostFragment = val navHostFragment: NavHostFragment =
supportFragmentManager.findFragmentById(navHostId) as NavHostFragment supportFragmentManager.findFragmentById(navHostId) as NavHostFragment
navHostFragment.targetFragment navHostFragment.targetFragment
return navHostFragment?.childFragmentManager?.fragments?.first() return navHostFragment.childFragmentManager.fragments.first()
} }
@Suppress("UNCHECKED_CAST")
fun <T> AppCompatActivity.whichFragment(@IdRes id: Int): T {
return supportFragmentManager.findFragmentById(id) as T
}
@Suppress("UNCHECKED_CAST")
fun <T> Fragment.whichFragment(@IdRes id: Int): T {
return childFragmentManager.findFragmentById(id) as T
}
fun Fragment.showToast(@StringRes stringRes: Int) {
showToast(getString(stringRes))
}
fun Fragment.showToast(message: String) {
Toast.makeText(requireContext(), message, Toast.LENGTH_SHORT).show()
}

View file

@ -4,17 +4,22 @@ import androidx.annotation.IdRes
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.navigation.NavController import androidx.navigation.NavController
import androidx.navigation.findNavController
import androidx.navigation.fragment.NavHostFragment import androidx.navigation.fragment.NavHostFragment
import androidx.navigation.fragment.findNavController import androidx.navigation.fragment.findNavController
fun Fragment.navigate(@IdRes id: Int) = findNavController().navigate(id) fun Fragment.navigate(@IdRes id: Int) = findNavController().navigate(id)
fun Fragment.navController(@IdRes id: Int): NavController { fun Fragment.findNavController(@IdRes id: Int): NavController {
val fragment = childFragmentManager.findFragmentById(id) as NavHostFragment val fragment = childFragmentManager.findFragmentById(id) as NavHostFragment
return fragment.navController return fragment.navController
} }
fun AppCompatActivity.navController(@IdRes id: Int): NavController { fun Fragment.findActivityNavController(@IdRes id: Int): NavController {
return requireActivity().findNavController(id)
}
fun AppCompatActivity.findNavController(@IdRes id: Int): NavController {
val fragment = supportFragmentManager.findFragmentById(id) as NavHostFragment val fragment = supportFragmentManager.findFragmentById(id) as NavHostFragment
return fragment.navController return fragment.navController
} }

View file

@ -14,13 +14,10 @@
package io.github.muntashirakon.music.extensions package io.github.muntashirakon.music.extensions
import android.content.res.ColorStateList
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.widget.EditText import android.widget.EditText
import android.widget.SeekBar
import androidx.annotation.ColorInt
import androidx.annotation.LayoutRes import androidx.annotation.LayoutRes
import code.name.monkey.appthemehelper.ThemeStore import code.name.monkey.appthemehelper.ThemeStore
import code.name.monkey.appthemehelper.util.TintHelper import code.name.monkey.appthemehelper.util.TintHelper
@ -48,9 +45,3 @@ fun EditText.appHandleColor(): EditText {
TintHelper.colorHandles(this, ThemeStore.accentColor(context)) TintHelper.colorHandles(this, ThemeStore.accentColor(context))
return this return this
} }
fun SeekBar.applyColor(@ColorInt color: Int) {
thumbTintList = ColorStateList.valueOf(color)
progressTintList = ColorStateList.valueOf(color)
progressBackgroundTintList = ColorStateList.valueOf(color)
}

View file

@ -0,0 +1,130 @@
package io.github.muntashirakon.music.fragments
import android.os.Bundle
import android.view.View
import android.widget.ImageView
import androidx.navigation.fragment.navArgs
import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.LinearLayoutManager
import io.github.muntashirakon.music.*
import io.github.muntashirakon.music.adapter.album.AlbumAdapter
import io.github.muntashirakon.music.adapter.artist.ArtistAdapter
import io.github.muntashirakon.music.adapter.song.SongAdapter
import io.github.muntashirakon.music.fragments.albums.AlbumClickListener
import io.github.muntashirakon.music.fragments.artists.ArtistClickListener
import io.github.muntashirakon.music.fragments.base.AbsMainActivityFragment
import io.github.muntashirakon.music.model.Album
import io.github.muntashirakon.music.model.Artist
import io.github.muntashirakon.music.model.Song
import io.github.muntashirakon.music.repository.RealRepository
import kotlinx.android.synthetic.main.fragment_playlist_detail.*
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers.IO
import kotlinx.coroutines.Dispatchers.Main
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import org.koin.android.ext.android.inject
class DetailListFragment : AbsMainActivityFragment(R.layout.fragment_playlist_detail),
ArtistClickListener, AlbumClickListener {
private val args by navArgs<DetailListFragmentArgs>()
private val repository by inject<RealRepository>()
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
mainActivity.setSupportActionBar(toolbar)
mainActivity.hideBottomBarVisibility(false)
when (args.type) {
TOP_ARTISTS -> {
loadArtists(R.string.top_artists, TOP_ARTISTS)
}
RECENT_ARTISTS -> {
loadArtists(R.string.recent_artists, RECENT_ARTISTS)
}
TOP_ALBUMS -> {
loadAlbums(R.string.top_albums, TOP_ALBUMS)
}
RECENT_ALBUMS -> {
loadAlbums(R.string.recent_albums, RECENT_ALBUMS)
}
FAVOURITES -> {
loadFavorite()
}
}
}
private fun loadFavorite() {
toolbar.setTitle(R.string.favorites)
CoroutineScope(IO).launch {
val songs = repository.favoritePlaylistHome()
withContext(Main) {
recyclerView.apply {
adapter = SongAdapter(
requireActivity(),
songs.arrayList as MutableList<Song>,
R.layout.item_list, null
)
layoutManager = linearLayoutManager()
}
}
}
}
private fun loadArtists(title: Int, type: Int) {
toolbar.setTitle(title)
CoroutineScope(IO).launch {
val artists =
if (type == TOP_ARTISTS) repository.topArtists() else repository.recentArtists()
withContext(Main) {
recyclerView.apply {
adapter = artistAdapter(artists)
layoutManager = gridLayoutManager()
}
}
}
}
private fun loadAlbums(title: Int, type: Int) {
toolbar.setTitle(title)
CoroutineScope(IO).launch {
val albums =
if (type == TOP_ALBUMS) repository.topAlbums() else repository.recentAlbums()
withContext(Main) {
recyclerView.apply {
adapter = albumAdapter(albums)
layoutManager = gridLayoutManager()
}
}
}
}
private fun artistAdapter(artists: List<Artist>): ArtistAdapter = ArtistAdapter(
requireActivity(),
artists,
R.layout.item_grid_circle,
null, this@DetailListFragment
)
private fun albumAdapter(albums: List<Album>): AlbumAdapter = AlbumAdapter(
requireActivity(),
albums,
R.layout.item_grid,
null, this@DetailListFragment
)
private fun linearLayoutManager(): LinearLayoutManager =
LinearLayoutManager(requireContext(), LinearLayoutManager.VERTICAL, false)
private fun gridLayoutManager(): GridLayoutManager =
GridLayoutManager(requireContext(), 2, GridLayoutManager.VERTICAL, false)
override fun onArtist(artistId: Int, imageView: ImageView) {
}
override fun onAlbumClick(albumId: Int, view: View) {
}
}

View file

@ -4,27 +4,26 @@ import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import io.github.muntashirakon.music.adapter.HomeAdapter
import io.github.muntashirakon.music.fragments.ReloadType.* import io.github.muntashirakon.music.fragments.ReloadType.*
import io.github.muntashirakon.music.interfaces.MusicServiceEventListener import io.github.muntashirakon.music.interfaces.MusicServiceEventListener
import io.github.muntashirakon.music.model.* import io.github.muntashirakon.music.model.*
import io.github.muntashirakon.music.providers.RepositoryImpl import io.github.muntashirakon.music.repository.RealRepository
import kotlinx.coroutines.Deferred import kotlinx.coroutines.Deferred
import kotlinx.coroutines.Dispatchers.IO import kotlinx.coroutines.Dispatchers.IO
import kotlinx.coroutines.async import kotlinx.coroutines.async
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
class LibraryViewModel( class LibraryViewModel(
private val repository: RepositoryImpl private val realRepository: RealRepository
) : ViewModel(), MusicServiceEventListener { ) : ViewModel(), MusicServiceEventListener {
private val paletteColor = MutableLiveData<Int>()
private val albums = MutableLiveData<List<Album>>() private val albums = MutableLiveData<List<Album>>()
private val songs = MutableLiveData<List<Song>>() private val songs = MutableLiveData<List<Song>>()
private val artists = MutableLiveData<List<Artist>>() private val artists = MutableLiveData<List<Artist>>()
private val playlists = MutableLiveData<List<Playlist>>() private val playlists = MutableLiveData<List<Playlist>>()
private val genres = MutableLiveData<List<Genre>>() private val genres = MutableLiveData<List<Genre>>()
private val home = MutableLiveData<List<Home>>() private val home = MutableLiveData<List<Home>>()
private val paletteColor = MutableLiveData<Int>()
val paletteColorLiveData: LiveData<Int> = paletteColor val paletteColorLiveData: LiveData<Int> = paletteColor
val homeLiveData: LiveData<List<Home>> = home val homeLiveData: LiveData<List<Home>> = home
@ -46,59 +45,36 @@ class LibraryViewModel(
artists.value = loadArtists.await() artists.value = loadArtists.await()
playlists.value = loadPlaylists.await() playlists.value = loadPlaylists.await()
genres.value = loadGenres.await() genres.value = loadGenres.await()
loadHomeSections() home.value = loadHome.await()
} }
private fun loadHomeSections() = viewModelScope.launch { private val loadHome: Deferred<List<Home>>
val list = mutableListOf<Home>() get() = viewModelScope.async { realRepository.homeSections() }
val result = listOf(
repository.topArtists(),
repository.topAlbums(),
repository.recentArtists(),
repository.recentAlbums(),
repository.suggestions(),
repository.favoritePlaylist(),
repository.homeGenres()
)
result.forEach {
if (it != null && it.arrayList.isNotEmpty()) {
if (it.homeSection == HomeAdapter.SUGGESTIONS) {
if (it.arrayList.size > 9) {
list.add(it)
}
} else {
list.add(it)
}
}
}
home.value = list
}
private val loadSongs: Deferred<List<Song>> private val loadSongs: Deferred<List<Song>>
get() = viewModelScope.async(IO) { get() = viewModelScope.async(IO) { realRepository.allSongs() }
repository.allSongs()
}
private val loadAlbums: Deferred<List<Album>> private val loadAlbums: Deferred<List<Album>>
get() = viewModelScope.async(IO) { get() = viewModelScope.async(IO) {
repository.allAlbums() realRepository.allAlbums()
} }
private val loadArtists: Deferred<List<Artist>> private val loadArtists: Deferred<List<Artist>>
get() = viewModelScope.async(IO) { get() = viewModelScope.async(IO) {
repository.allArtists() realRepository.albumArtists()
} }
private val loadPlaylists: Deferred<List<Playlist>> private val loadPlaylists: Deferred<List<Playlist>>
get() = viewModelScope.async(IO) { get() = viewModelScope.async(IO) {
repository.allPlaylists() realRepository.allPlaylists()
} }
private val loadGenres: Deferred<List<Genre>> private val loadGenres: Deferred<List<Genre>>
get() = viewModelScope.async(IO) { get() = viewModelScope.async(IO) {
repository.allGenres() realRepository.allGenres()
} }
fun forceReload(reloadType: ReloadType) = viewModelScope.launch { fun forceReload(reloadType: ReloadType) = viewModelScope.launch {
when (reloadType) { when (reloadType) {
Songs -> songs.value = loadSongs.await() Songs -> songs.value = loadSongs.await()
@ -114,15 +90,37 @@ class LibraryViewModel(
override fun onMediaStoreChanged() { override fun onMediaStoreChanged() {
loadLibraryContent() loadLibraryContent()
println("onMediaStoreChanged")
} }
override fun onServiceConnected() {}
override fun onServiceDisconnected() {} override fun onServiceConnected() {
override fun onQueueChanged() {} println("onServiceConnected")
override fun onPlayingMetaChanged() {} }
override fun onPlayStateChanged() {}
override fun onRepeatModeChanged() {} override fun onServiceDisconnected() {
override fun onShuffleModeChanged() {} println("onServiceDisconnected")
}
override fun onQueueChanged() {
println("onQueueChanged")
}
override fun onPlayingMetaChanged() {
println("onPlayingMetaChanged")
}
override fun onPlayStateChanged() {
println("onPlayStateChanged")
}
override fun onRepeatModeChanged() {
println("onRepeatModeChanged")
}
override fun onShuffleModeChanged() {
println("onShuffleModeChanged")
}
} }

View file

@ -1,32 +1,21 @@
package io.github.muntashirakon.music.activities package io.github.muntashirakon.music.fragments.about
import android.content.Intent import android.content.Intent
import android.content.pm.PackageManager import android.content.pm.PackageManager
import android.net.Uri import android.net.Uri
import android.os.Bundle import android.os.Bundle
import android.view.MenuItem
import android.view.View import android.view.View
import androidx.core.app.ShareCompat import androidx.core.app.ShareCompat
import androidx.fragment.app.Fragment
import androidx.recyclerview.widget.DefaultItemAnimator import androidx.recyclerview.widget.DefaultItemAnimator
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import io.github.muntashirakon.music.Constants.APP_INSTAGRAM_LINK
import io.github.muntashirakon.music.Constants.APP_TELEGRAM_LINK
import io.github.muntashirakon.music.Constants.APP_TWITTER_LINK
import io.github.muntashirakon.music.Constants.FAQ_LINK
import io.github.muntashirakon.music.Constants.GITHUB_PROJECT
import io.github.muntashirakon.music.Constants.PINTEREST
import io.github.muntashirakon.music.Constants.RATE_ON_GOOGLE_PLAY
import io.github.muntashirakon.music.Constants.TELEGRAM_CHANGE_LOG
import io.github.muntashirakon.music.Constants.TRANSLATE
import io.github.muntashirakon.music.R
import io.github.muntashirakon.music.activities.base.AbsBaseActivity
import io.github.muntashirakon.music.adapter.ContributorAdapter
import io.github.muntashirakon.music.extensions.applyToolbar
import io.github.muntashirakon.music.model.Contributor
import io.github.muntashirakon.music.util.NavigationUtil
import com.google.gson.Gson import com.google.gson.Gson
import com.google.gson.reflect.TypeToken import com.google.gson.reflect.TypeToken
import kotlinx.android.synthetic.main.activity_about.* import io.github.muntashirakon.music.Constants
import io.github.muntashirakon.music.R
import io.github.muntashirakon.music.adapter.ContributorAdapter
import io.github.muntashirakon.music.model.Contributor
import io.github.muntashirakon.music.util.NavigationUtil
import kotlinx.android.synthetic.main.card_credit.* import kotlinx.android.synthetic.main.card_credit.*
import kotlinx.android.synthetic.main.card_other.* import kotlinx.android.synthetic.main.card_other.*
import kotlinx.android.synthetic.main.card_retro_info.* import kotlinx.android.synthetic.main.card_retro_info.*
@ -34,13 +23,19 @@ import kotlinx.android.synthetic.main.card_social.*
import java.io.IOException import java.io.IOException
import java.nio.charset.StandardCharsets import java.nio.charset.StandardCharsets
class AboutActivity : AbsBaseActivity(), View.OnClickListener { class AboutFragment : Fragment(R.layout.fragment_about), View.OnClickListener {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
version.setSummary(getAppVersion())
setUpView()
loadContributors()
}
private val contributorsJson: String? private val contributorsJson: String?
get() { get() {
val json: String val json: String
try { try {
val inputStream = assets.open("contributors.json") val inputStream = requireActivity().assets.open("contributors.json")
val size = inputStream.available() val size = inputStream.available()
val buffer = ByteArray(size) val buffer = ByteArray(size)
inputStream.read(buffer) inputStream.read(buffer)
@ -53,27 +48,6 @@ class AboutActivity : AbsBaseActivity(), View.OnClickListener {
return json return json
} }
override fun onCreate(savedInstanceState: Bundle?) {
setDrawUnderStatusBar()
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_about)
setStatusbarColorAuto()
setNavigationbarColorAuto()
setLightNavigationBar(true)
applyToolbar(toolbar)
version.setSummary(getAppVersion())
setUpView()
loadContributors()
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
if (item.itemId == android.R.id.home) {
onBackPressed()
return true
}
return super.onOptionsItemSelected(item)
}
private fun openUrl(url: String) { private fun openUrl(url: String) {
val i = Intent(Intent.ACTION_VIEW) val i = Intent(Intent.ACTION_VIEW)
@ -89,6 +63,7 @@ class AboutActivity : AbsBaseActivity(), View.OnClickListener {
appRate.setOnClickListener(this) appRate.setOnClickListener(this)
appTranslation.setOnClickListener(this) appTranslation.setOnClickListener(this)
appShare.setOnClickListener(this) appShare.setOnClickListener(this)
donateLink.setOnClickListener(this)
instagramLink.setOnClickListener(this) instagramLink.setOnClickListener(this)
twitterLink.setOnClickListener(this) twitterLink.setOnClickListener(this)
changelog.setOnClickListener(this) changelog.setOnClickListener(this)
@ -99,25 +74,26 @@ class AboutActivity : AbsBaseActivity(), View.OnClickListener {
override fun onClick(view: View) { override fun onClick(view: View) {
when (view.id) { when (view.id) {
R.id.pinterestLink -> openUrl(PINTEREST) R.id.pinterestLink -> openUrl(Constants.PINTEREST)
R.id.faqLink -> openUrl(FAQ_LINK) R.id.faqLink -> openUrl(Constants.FAQ_LINK)
R.id.telegramLink -> openUrl(APP_TELEGRAM_LINK) R.id.telegramLink -> openUrl(Constants.APP_TELEGRAM_LINK)
R.id.appGithub -> openUrl(GITHUB_PROJECT) R.id.appGithub -> openUrl(Constants.GITHUB_PROJECT)
R.id.appTranslation -> openUrl(TRANSLATE) R.id.appTranslation -> openUrl(Constants.TRANSLATE)
R.id.appRate -> openUrl(RATE_ON_GOOGLE_PLAY) R.id.appRate -> openUrl(Constants.RATE_ON_GOOGLE_PLAY)
R.id.appShare -> shareApp() R.id.appShare -> shareApp()
R.id.instagramLink -> openUrl(APP_INSTAGRAM_LINK) R.id.instagramLink -> openUrl(Constants.APP_INSTAGRAM_LINK)
R.id.twitterLink -> openUrl(APP_TWITTER_LINK) R.id.twitterLink -> openUrl(Constants.APP_TWITTER_LINK)
R.id.changelog -> openUrl(TELEGRAM_CHANGE_LOG) R.id.changelog -> openUrl(Constants.TELEGRAM_CHANGE_LOG)
R.id.openSource -> NavigationUtil.goToOpenSource(this) R.id.openSource -> NavigationUtil.goToOpenSource(requireActivity())
R.id.bugReportLink -> NavigationUtil.bugReport(this) R.id.bugReportLink -> NavigationUtil.bugReport(requireActivity())
} }
} }
private fun getAppVersion(): String { private fun getAppVersion(): String {
return try { return try {
val isPro = "Pro" val isPro = "Pro"
val packageInfo = packageManager.getPackageInfo(packageName, 0) val packageInfo =
requireActivity().packageManager.getPackageInfo(requireActivity().packageName, 0)
"${packageInfo.versionName} $isPro" "${packageInfo.versionName} $isPro"
} catch (e: PackageManager.NameNotFoundException) { } catch (e: PackageManager.NameNotFoundException) {
e.printStackTrace() e.printStackTrace()
@ -126,9 +102,10 @@ class AboutActivity : AbsBaseActivity(), View.OnClickListener {
} }
private fun shareApp() { private fun shareApp() {
ShareCompat.IntentBuilder.from(this).setType("text/plain") ShareCompat.IntentBuilder.from(requireActivity()).setType("text/plain")
.setChooserTitle(R.string.share_app) .setChooserTitle(R.string.share_app)
.setText(String.format(getString(R.string.app_share), packageName)).startChooser() .setText(String.format(getString(R.string.app_share), requireActivity().packageName))
.startChooser()
} }
private fun loadContributors() { private fun loadContributors() {
@ -138,8 +115,10 @@ class AboutActivity : AbsBaseActivity(), View.OnClickListener {
val contributors = Gson().fromJson<List<Contributor>>(contributorsJson, type) val contributors = Gson().fromJson<List<Contributor>>(contributorsJson, type)
val contributorAdapter = ContributorAdapter(contributors) val contributorAdapter = ContributorAdapter(contributors)
recyclerView.layoutManager = LinearLayoutManager(this) recyclerView.apply {
recyclerView.itemAnimator = DefaultItemAnimator() layoutManager = LinearLayoutManager(requireContext())
recyclerView.adapter = contributorAdapter itemAnimator = DefaultItemAnimator()
adapter = contributorAdapter
}
} }
} }

View file

@ -1,137 +1,103 @@
package io.github.muntashirakon.music.activities.albums package io.github.muntashirakon.music.fragments.albums
import android.app.ActivityOptions import android.app.ActivityOptions
import android.content.Intent import android.content.Intent
import android.os.Bundle import android.os.Bundle
import android.transition.Slide import android.view.*
import android.view.Menu import androidx.appcompat.app.AppCompatActivity
import android.view.MenuItem import androidx.core.os.bundleOf
import android.view.SubMenu import androidx.lifecycle.Observer
import android.view.View import androidx.navigation.findNavController
import androidx.core.app.ActivityCompat import androidx.navigation.fragment.findNavController
import androidx.navigation.fragment.navArgs
import androidx.recyclerview.widget.DefaultItemAnimator import androidx.recyclerview.widget.DefaultItemAnimator
import androidx.recyclerview.widget.GridLayoutManager import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import code.name.monkey.appthemehelper.util.ATHUtil import code.name.monkey.appthemehelper.common.ATHToolbarActivity.getToolbarBackgroundColor
import code.name.monkey.appthemehelper.util.MaterialUtil
import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper
import com.bumptech.glide.Glide
import io.github.muntashirakon.music.EXTRA_ALBUM_ID
import io.github.muntashirakon.music.EXTRA_ARTIST_ID
import io.github.muntashirakon.music.R import io.github.muntashirakon.music.R
import io.github.muntashirakon.music.activities.base.AbsSlidingMusicPanelActivity
import io.github.muntashirakon.music.activities.tageditor.AbsTagEditorActivity import io.github.muntashirakon.music.activities.tageditor.AbsTagEditorActivity
import io.github.muntashirakon.music.activities.tageditor.AlbumTagEditorActivity import io.github.muntashirakon.music.activities.tageditor.AlbumTagEditorActivity
import io.github.muntashirakon.music.adapter.album.HorizontalAlbumAdapter import io.github.muntashirakon.music.adapter.album.HorizontalAlbumAdapter
import io.github.muntashirakon.music.adapter.song.SimpleSongAdapter import io.github.muntashirakon.music.adapter.song.SimpleSongAdapter
import io.github.muntashirakon.music.dialogs.AddToPlaylistDialog import io.github.muntashirakon.music.dialogs.AddToPlaylistDialog
import io.github.muntashirakon.music.dialogs.DeleteSongsDialog import io.github.muntashirakon.music.dialogs.DeleteSongsDialog
import io.github.muntashirakon.music.extensions.extraNotNull import io.github.muntashirakon.music.extensions.applyColor
import io.github.muntashirakon.music.extensions.show import io.github.muntashirakon.music.extensions.show
import io.github.muntashirakon.music.extensions.surfaceColor import io.github.muntashirakon.music.fragments.base.AbsMainActivityFragment
import io.github.muntashirakon.music.glide.AlbumGlideRequest import io.github.muntashirakon.music.glide.AlbumGlideRequest
import io.github.muntashirakon.music.glide.ArtistGlideRequest import io.github.muntashirakon.music.glide.ArtistGlideRequest
import io.github.muntashirakon.music.glide.RetroMusicColoredTarget import io.github.muntashirakon.music.glide.RetroMusicColoredTarget
import io.github.muntashirakon.music.helper.MusicPlayerRemote import io.github.muntashirakon.music.helper.MusicPlayerRemote
import io.github.muntashirakon.music.helper.SortOrder.AlbumSongSortOrder import io.github.muntashirakon.music.helper.SortOrder
import io.github.muntashirakon.music.interfaces.CabHolder
import io.github.muntashirakon.music.model.Album import io.github.muntashirakon.music.model.Album
import io.github.muntashirakon.music.model.Artist import io.github.muntashirakon.music.model.Artist
import io.github.muntashirakon.music.network.model.LastFmAlbum import io.github.muntashirakon.music.network.model.LastFmAlbum
import io.github.muntashirakon.music.util.* import io.github.muntashirakon.music.util.MusicUtil
import io.github.muntashirakon.music.util.PreferenceUtil
import io.github.muntashirakon.music.util.RetroUtil
import io.github.muntashirakon.music.util.color.MediaNotificationProcessor import io.github.muntashirakon.music.util.color.MediaNotificationProcessor
import com.afollestad.materialcab.MaterialCab import kotlinx.android.synthetic.main.fragment_album_content.*
import com.bumptech.glide.Glide import kotlinx.android.synthetic.main.fragment_album_details.*
import kotlinx.android.synthetic.main.activity_album.*
import kotlinx.android.synthetic.main.activity_album_content.*
import org.koin.androidx.viewmodel.ext.android.viewModel import org.koin.androidx.viewmodel.ext.android.viewModel
import org.koin.core.parameter.parametersOf import org.koin.core.parameter.parametersOf
import java.util.* import java.util.*
import android.util.Pair as UtilPair
class AlbumDetailsActivity : AbsSlidingMusicPanelActivity(), CabHolder { class AlbumDetailsFragment : AbsMainActivityFragment(R.layout.fragment_album_details),
override fun openCab(menuRes: Int, callback: MaterialCab.Callback): MaterialCab { AlbumClickListener {
cab?.let {
if (it.isActive) it.finish()
}
cab = MaterialCab(this, R.id.cab_stub)
.setMenu(menuRes)
.setCloseDrawableRes(R.drawable.ic_close)
.setBackgroundColor(
RetroColorUtil.shiftBackgroundColorForLightText(
ATHUtil.resolveColor(
this,
R.attr.colorSurface
)
)
)
.start(callback)
return cab as MaterialCab
}
private val arguments by navArgs<AlbumDetailsFragmentArgs>()
private val detailsViewModel by viewModel<AlbumDetailsViewModel> { private val detailsViewModel by viewModel<AlbumDetailsViewModel> {
parametersOf(extraNotNull<Int>(EXTRA_ALBUM_ID).value) parametersOf(arguments.extraAlbumId)
} }
private lateinit var simpleSongAdapter: SimpleSongAdapter private lateinit var simpleSongAdapter: SimpleSongAdapter
private lateinit var album: Album private lateinit var album: Album
private var cab: MaterialCab? = null
private val savedSortOrder: String private val savedSortOrder: String
get() = PreferenceUtil.albumDetailSongSortOrder get() = PreferenceUtil.albumDetailSongSortOrder
override fun createContentView(): View { override fun onActivityCreated(savedInstanceState: Bundle?) {
return wrapSlidingMusicPanel(R.layout.activity_album) super.onActivityCreated(savedInstanceState)
} setHasOptionsMenu(true)
mainActivity.hideBottomBarVisibility(false)
mainActivity.addMusicServiceEventListener(detailsViewModel)
mainActivity.setSupportActionBar(toolbar)
private fun windowEnterTransition() { toolbar.title = null
val slide = Slide()
slide.excludeTarget(R.id.appBarLayout, true)
slide.excludeTarget(R.id.status_bar, true)
slide.excludeTarget(android.R.id.statusBarBackground, true)
slide.excludeTarget(android.R.id.navigationBarBackground, true)
window.enterTransition = slide
}
override fun onCreate(savedInstanceState: Bundle?) { postponeEnterTransition()
setDrawUnderStatusBar() detailsViewModel.getAlbum().observe(viewLifecycleOwner, Observer {
super.onCreate(savedInstanceState)
setStatusbarColorAuto()
setNavigationbarColorAuto()
setTaskDescriptionColorAuto()
setLightNavigationBar(true)
setBottomBarVisibility(View.GONE)
window.sharedElementsUseOverlay = true
windowEnterTransition()
toolbar.setBackgroundColor(surfaceColor())
addMusicServiceEventListener(detailsViewModel)
ActivityCompat.postponeEnterTransition(this)
detailsViewModel.getAlbum().observe(this, androidx.lifecycle.Observer {
ActivityCompat.startPostponedEnterTransition(this@AlbumDetailsActivity)
showAlbum(it) showAlbum(it)
startPostponedEnterTransition()
}) })
detailsViewModel.getArtist().observe(this, androidx.lifecycle.Observer { detailsViewModel.getArtist().observe(viewLifecycleOwner, Observer {
loadArtistImage(it) loadArtistImage(it)
}) })
detailsViewModel.getMoreAlbums().observe(this, androidx.lifecycle.Observer { detailsViewModel.getMoreAlbums().observe(viewLifecycleOwner, Observer {
moreAlbums(it) moreAlbums(it)
}) })
detailsViewModel.getAlbumInfo().observe(this, androidx.lifecycle.Observer { detailsViewModel.getAlbumInfo().observe(viewLifecycleOwner, Observer {
aboutAlbum(it) aboutAlbum(it)
}) })
setupRecyclerView() setupRecyclerView()
artistImage.setOnClickListener { artistImage.setOnClickListener {
val artistPairs = ActivityOptions.makeSceneTransitionAnimation( requireActivity().findNavController(R.id.fragment_container)
this, .navigate(
UtilPair.create( R.id.artistDetailsFragment,
artistImage, bundleOf(EXTRA_ARTIST_ID to album.artistId)
getString(R.string.transition_artist_image)
) )
}
playAction.setOnClickListener { MusicPlayerRemote.openQueue(album.songs!!, 0, true) }
shuffleAction.setOnClickListener {
MusicPlayerRemote.openAndShuffleQueue(
album.songs!!,
true
) )
NavigationUtil.goToArtistOptions(this, album.artistId, artistPairs)
}
playAction.apply {
setOnClickListener { MusicPlayerRemote.openQueue(album.songs!!, 0, true) }
}
shuffleAction.apply {
setOnClickListener { MusicPlayerRemote.openAndShuffleQueue(album.songs!!, true) }
} }
aboutAlbumText.setOnClickListener { aboutAlbumText.setOnClickListener {
@ -141,12 +107,26 @@ class AlbumDetailsActivity : AbsSlidingMusicPanelActivity(), CabHolder {
aboutAlbumText.maxLines = 4 aboutAlbumText.maxLines = 4
} }
} }
image.apply {
transitionName = getString(R.string.transition_album_art)
}
} }
override fun onDestroy() {
super.onDestroy()
playerActivity?.removeMusicServiceEventListener(detailsViewModel)
}
private fun setupRecyclerView() { private fun setupRecyclerView() {
simpleSongAdapter = SimpleSongAdapter(this, ArrayList(), R.layout.item_song, this) simpleSongAdapter = SimpleSongAdapter(
requireActivity() as AppCompatActivity,
ArrayList(),
R.layout.item_song,
null
)
recyclerView.apply { recyclerView.apply {
layoutManager = LinearLayoutManager(this@AlbumDetailsActivity) layoutManager = LinearLayoutManager(requireContext())
itemAnimator = DefaultItemAnimator() itemAnimator = DefaultItemAnimator()
isNestedScrollingEnabled = false isNestedScrollingEnabled = false
adapter = simpleSongAdapter adapter = simpleSongAdapter
@ -155,7 +135,6 @@ class AlbumDetailsActivity : AbsSlidingMusicPanelActivity(), CabHolder {
private fun showAlbum(album: Album) { private fun showAlbum(album: Album) {
if (album.songs!!.isEmpty()) { if (album.songs!!.isEmpty()) {
finish()
return return
} }
this.album = album this.album = album
@ -194,9 +173,10 @@ class AlbumDetailsActivity : AbsSlidingMusicPanelActivity(), CabHolder {
moreRecyclerView.show() moreRecyclerView.show()
moreTitle.text = String.format(getString(R.string.label_more_from), album.artistName) moreTitle.text = String.format(getString(R.string.label_more_from), album.artistName)
val albumAdapter = HorizontalAlbumAdapter(this, albums, null) val albumAdapter =
HorizontalAlbumAdapter(requireActivity() as AppCompatActivity, albums, null, this)
moreRecyclerView.layoutManager = GridLayoutManager( moreRecyclerView.layoutManager = GridLayoutManager(
this, requireContext(),
1, 1,
GridLayoutManager.HORIZONTAL, GridLayoutManager.HORIZONTAL,
false false
@ -226,8 +206,8 @@ class AlbumDetailsActivity : AbsSlidingMusicPanelActivity(), CabHolder {
} }
private fun loadArtistImage(artist: Artist) { private fun loadArtistImage(artist: Artist) {
ArtistGlideRequest.Builder.from(Glide.with(this), artist) ArtistGlideRequest.Builder.from(Glide.with(requireContext()), artist)
.generatePalette(this) .generatePalette(requireContext())
.build() .build()
.dontAnimate() .dontAnimate()
.dontTransform() .dontTransform()
@ -238,10 +218,10 @@ class AlbumDetailsActivity : AbsSlidingMusicPanelActivity(), CabHolder {
} }
private fun loadAlbumCover() { private fun loadAlbumCover() {
AlbumGlideRequest.Builder.from(Glide.with(this), album.safeGetFirstSong()) AlbumGlideRequest.Builder.from(Glide.with(requireContext()), album.safeGetFirstSong())
.checkIgnoreMediaStore(this) .checkIgnoreMediaStore(requireContext())
.ignoreMediaStore(PreferenceUtil.isIgnoreMediaStoreArtwork) .ignoreMediaStore(PreferenceUtil.isIgnoreMediaStoreArtwork)
.generatePalette(this) .generatePalette(requireContext())
.build() .build()
.dontAnimate() .dontAnimate()
.dontTransform() .dontTransform()
@ -253,32 +233,28 @@ class AlbumDetailsActivity : AbsSlidingMusicPanelActivity(), CabHolder {
} }
private fun setColors(color: MediaNotificationProcessor) { private fun setColors(color: MediaNotificationProcessor) {
MaterialUtil.tintColor( shuffleAction.applyColor(color.backgroundColor)
button = shuffleAction, playAction.applyColor(color.backgroundColor)
textColor = color.primaryTextColor,
backgroundColor = color.backgroundColor
)
MaterialUtil.tintColor(
button = playAction,
textColor = color.primaryTextColor,
backgroundColor = color.backgroundColor
)
setSupportActionBar(toolbar)
supportActionBar?.title = null
} }
override fun onCreateOptionsMenu(menu: Menu): Boolean { override fun onAlbumClick(albumId: Int, view: View) {
menuInflater.inflate(R.menu.menu_album_detail, menu) findNavController().navigate(
R.id.albumDetailsFragment,
bundleOf(EXTRA_ALBUM_ID to albumId)
)
}
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
super.onCreateOptionsMenu(menu, inflater)
inflater.inflate(R.menu.menu_album_detail, menu)
val sortOrder = menu.findItem(R.id.action_sort_order) val sortOrder = menu.findItem(R.id.action_sort_order)
setUpSortOrderMenu(sortOrder.subMenu) setUpSortOrderMenu(sortOrder.subMenu)
ToolbarContentTintHelper.handleOnCreateOptionsMenu( ToolbarContentTintHelper.handleOnCreateOptionsMenu(
this, requireContext(),
toolbar, toolbar,
menu, menu,
getToolbarBackgroundColor(toolbar) getToolbarBackgroundColor(toolbar)
) )
return super.onCreateOptionsMenu(menu)
} }
override fun onOptionsItemSelected(item: MenuItem): Boolean { override fun onOptionsItemSelected(item: MenuItem): Boolean {
@ -289,6 +265,7 @@ class AlbumDetailsActivity : AbsSlidingMusicPanelActivity(), CabHolder {
var sortOrder: String? = null var sortOrder: String? = null
val songs = simpleSongAdapter.dataSet val songs = simpleSongAdapter.dataSet
when (item.itemId) { when (item.itemId) {
android.R.id.home -> findNavController().navigateUp()
R.id.action_play_next -> { R.id.action_play_next -> {
MusicPlayerRemote.playNext(songs) MusicPlayerRemote.playNext(songs)
return true return true
@ -298,22 +275,18 @@ class AlbumDetailsActivity : AbsSlidingMusicPanelActivity(), CabHolder {
return true return true
} }
R.id.action_add_to_playlist -> { R.id.action_add_to_playlist -> {
AddToPlaylistDialog.create(songs).show(supportFragmentManager, "ADD_PLAYLIST") AddToPlaylistDialog.create(songs).show(childFragmentManager, "ADD_PLAYLIST")
return true return true
} }
R.id.action_delete_from_device -> { R.id.action_delete_from_device -> {
DeleteSongsDialog.create(songs).show(supportFragmentManager, "DELETE_SONGS") DeleteSongsDialog.create(songs).show(childFragmentManager, "DELETE_SONGS")
return true
}
android.R.id.home -> {
super.onBackPressed()
return true return true
} }
R.id.action_tag_editor -> { R.id.action_tag_editor -> {
val intent = Intent(this, AlbumTagEditorActivity::class.java) val intent = Intent(requireContext(), AlbumTagEditorActivity::class.java)
intent.putExtra(AbsTagEditorActivity.EXTRA_ID, album.id) intent.putExtra(AbsTagEditorActivity.EXTRA_ID, album.id)
val options = ActivityOptions.makeSceneTransitionAnimation( val options = ActivityOptions.makeSceneTransitionAnimation(
this, requireActivity(),
albumCoverContainer, albumCoverContainer,
"${getString(R.string.transition_album_art)}_${album.id}" "${getString(R.string.transition_album_art)}_${album.id}"
) )
@ -324,11 +297,12 @@ class AlbumDetailsActivity : AbsSlidingMusicPanelActivity(), CabHolder {
return true return true
} }
/*Sort*/ /*Sort*/
R.id.action_sort_order_title -> sortOrder = AlbumSongSortOrder.SONG_A_Z R.id.action_sort_order_title -> sortOrder = SortOrder.AlbumSongSortOrder.SONG_A_Z
R.id.action_sort_order_title_desc -> sortOrder = AlbumSongSortOrder.SONG_Z_A R.id.action_sort_order_title_desc -> sortOrder = SortOrder.AlbumSongSortOrder.SONG_Z_A
R.id.action_sort_order_track_list -> sortOrder = AlbumSongSortOrder.SONG_TRACK_LIST R.id.action_sort_order_track_list -> sortOrder =
SortOrder.AlbumSongSortOrder.SONG_TRACK_LIST
R.id.action_sort_order_artist_song_duration -> R.id.action_sort_order_artist_song_duration ->
sortOrder = AlbumSongSortOrder.SONG_DURATION sortOrder = SortOrder.AlbumSongSortOrder.SONG_DURATION
} }
if (sortOrder != null) { if (sortOrder != null) {
item.isChecked = true item.isChecked = true
@ -339,13 +313,13 @@ class AlbumDetailsActivity : AbsSlidingMusicPanelActivity(), CabHolder {
private fun setUpSortOrderMenu(sortOrder: SubMenu) { private fun setUpSortOrderMenu(sortOrder: SubMenu) {
when (savedSortOrder) { when (savedSortOrder) {
AlbumSongSortOrder.SONG_A_Z -> sortOrder.findItem(R.id.action_sort_order_title) SortOrder.AlbumSongSortOrder.SONG_A_Z -> sortOrder.findItem(R.id.action_sort_order_title)
.isChecked = true .isChecked = true
AlbumSongSortOrder.SONG_Z_A -> sortOrder.findItem(R.id.action_sort_order_title_desc) SortOrder.AlbumSongSortOrder.SONG_Z_A -> sortOrder.findItem(R.id.action_sort_order_title_desc)
.isChecked = true .isChecked = true
AlbumSongSortOrder.SONG_TRACK_LIST -> sortOrder.findItem(R.id.action_sort_order_track_list) SortOrder.AlbumSongSortOrder.SONG_TRACK_LIST -> sortOrder.findItem(R.id.action_sort_order_track_list)
.isChecked = true .isChecked = true
AlbumSongSortOrder.SONG_DURATION -> sortOrder.findItem(R.id.action_sort_order_artist_song_duration) SortOrder.AlbumSongSortOrder.SONG_DURATION -> sortOrder.findItem(R.id.action_sort_order_artist_song_duration)
.isChecked = true .isChecked = true
} }
} }
@ -353,22 +327,22 @@ class AlbumDetailsActivity : AbsSlidingMusicPanelActivity(), CabHolder {
private fun setSaveSortOrder(sortOrder: String) { private fun setSaveSortOrder(sortOrder: String) {
PreferenceUtil.albumDetailSongSortOrder = sortOrder PreferenceUtil.albumDetailSongSortOrder = sortOrder
when (sortOrder) { when (sortOrder) {
AlbumSongSortOrder.SONG_TRACK_LIST -> album.songs?.sortWith(Comparator { o1, o2 -> SortOrder.AlbumSongSortOrder.SONG_TRACK_LIST -> album.songs?.sortWith(Comparator { o1, o2 ->
o1.trackNumber.compareTo( o1.trackNumber.compareTo(
o2.trackNumber o2.trackNumber
) )
}) })
AlbumSongSortOrder.SONG_A_Z -> album.songs?.sortWith(Comparator { o1, o2 -> SortOrder.AlbumSongSortOrder.SONG_A_Z -> album.songs?.sortWith(Comparator { o1, o2 ->
o1.title.compareTo( o1.title.compareTo(
o2.title o2.title
) )
}) })
AlbumSongSortOrder.SONG_Z_A -> album.songs?.sortWith(Comparator { o1, o2 -> SortOrder.AlbumSongSortOrder.SONG_Z_A -> album.songs?.sortWith(Comparator { o1, o2 ->
o2.title.compareTo( o2.title.compareTo(
o1.title o1.title
) )
}) })
AlbumSongSortOrder.SONG_DURATION -> album.songs?.sortWith(Comparator { o1, o2 -> SortOrder.AlbumSongSortOrder.SONG_DURATION -> album.songs?.sortWith(Comparator { o1, o2 ->
o1.duration.compareTo( o1.duration.compareTo(
o2.duration o2.duration
) )
@ -377,23 +351,7 @@ class AlbumDetailsActivity : AbsSlidingMusicPanelActivity(), CabHolder {
album.songs?.let { simpleSongAdapter.swapDataSet(it) } album.songs?.let { simpleSongAdapter.swapDataSet(it) }
} }
override fun onBackPressed() {
if (cab != null && cab!!.isActive) {
cab?.finish()
} else {
super.onBackPressed()
}
}
override fun onDestroy() {
super.onDestroy()
removeMusicServiceEventListener(detailsViewModel)
}
companion object { companion object {
const val TAG_EDITOR_REQUEST = 9002
const val EXTRA_ALBUM_ID = "extra_album_id"
private const val TAG_EDITOR_REQUEST = 2001
} }
} }

View file

@ -1,4 +1,4 @@
package io.github.muntashirakon.music.activities.albums package io.github.muntashirakon.music.fragments.albums
import androidx.lifecycle.LiveData import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData import androidx.lifecycle.MutableLiveData
@ -8,15 +8,14 @@ import io.github.muntashirakon.music.interfaces.MusicServiceEventListener
import io.github.muntashirakon.music.model.Album import io.github.muntashirakon.music.model.Album
import io.github.muntashirakon.music.model.Artist import io.github.muntashirakon.music.model.Artist
import io.github.muntashirakon.music.network.model.LastFmAlbum import io.github.muntashirakon.music.network.model.LastFmAlbum
import io.github.muntashirakon.music.providers.RepositoryImpl import io.github.muntashirakon.music.repository.RealRepository
import kotlinx.coroutines.Deferred import kotlinx.coroutines.Deferred
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.async import kotlinx.coroutines.async
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import java.lang.Exception
class AlbumDetailsViewModel( class AlbumDetailsViewModel(
private val repository: RepositoryImpl, private val realRepository: RealRepository,
private val albumId: Int private val albumId: Int
) : ViewModel(), MusicServiceEventListener { ) : ViewModel(), MusicServiceEventListener {
@ -41,7 +40,7 @@ class AlbumDetailsViewModel(
fun loadAlbumInfo(album: Album) = viewModelScope.launch(Dispatchers.IO) { fun loadAlbumInfo(album: Album) = viewModelScope.launch(Dispatchers.IO) {
try { try {
val lastFmAlbum = repository.albumInfo( val lastFmAlbum = realRepository.albumInfo(
album.artistName ?: "-", album.title ?: "-" album.artistName ?: "-", album.title ?: "-"
) )
_lastFmAlbum.postValue(lastFmAlbum) _lastFmAlbum.postValue(lastFmAlbum)
@ -49,7 +48,7 @@ class AlbumDetailsViewModel(
} }
fun loadArtist(artistId: Int) = viewModelScope.launch(Dispatchers.IO) { fun loadArtist(artistId: Int) = viewModelScope.launch(Dispatchers.IO) {
val artist = repository.artistById(artistId) val artist = realRepository.artistById(artistId)
_artist.postValue(artist) _artist.postValue(artist)
artist.albums?.filter { item -> item.id != albumId }?.let { albums -> artist.albums?.filter { item -> item.id != albumId }?.let { albums ->
@ -59,7 +58,7 @@ class AlbumDetailsViewModel(
private val loadAlbumAsync: Deferred<Album?> private val loadAlbumAsync: Deferred<Album?>
get() = viewModelScope.async(Dispatchers.IO) { get() = viewModelScope.async(Dispatchers.IO) {
repository.albumById(albumId) realRepository.albumById(albumId)
} }
override fun onMediaStoreChanged() { override fun onMediaStoreChanged() {

View file

@ -2,32 +2,30 @@ package io.github.muntashirakon.music.fragments.albums
import android.os.Bundle import android.os.Bundle
import android.view.View import android.view.View
import androidx.core.os.bundleOf
import androidx.lifecycle.Observer import androidx.lifecycle.Observer
import androidx.navigation.findNavController
import androidx.navigation.fragment.FragmentNavigatorExtras
import androidx.recyclerview.widget.GridLayoutManager import androidx.recyclerview.widget.GridLayoutManager
import io.github.muntashirakon.music.EXTRA_ALBUM_ID
import io.github.muntashirakon.music.R import io.github.muntashirakon.music.R
import io.github.muntashirakon.music.adapter.album.AlbumAdapter import io.github.muntashirakon.music.adapter.album.AlbumAdapter
import io.github.muntashirakon.music.fragments.ReloadType import io.github.muntashirakon.music.fragments.ReloadType
import io.github.muntashirakon.music.fragments.base.AbsRecyclerViewCustomGridSizeFragment import io.github.muntashirakon.music.fragments.base.AbsRecyclerViewCustomGridSizeFragment
import io.github.muntashirakon.music.interfaces.MainActivityFragmentCallbacks
import io.github.muntashirakon.music.util.PreferenceUtil import io.github.muntashirakon.music.util.PreferenceUtil
class AlbumsFragment : class AlbumsFragment :
AbsRecyclerViewCustomGridSizeFragment<AlbumAdapter, GridLayoutManager>(), AbsRecyclerViewCustomGridSizeFragment<AlbumAdapter, GridLayoutManager>(),
MainActivityFragmentCallbacks { AlbumClickListener {
override fun handleBackPress(): Boolean {
return false
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
libraryViewModel.albumsLiveData libraryViewModel.albumsLiveData.observe(viewLifecycleOwner, Observer {
.observe(viewLifecycleOwner, Observer { albums -> if (it.isNotEmpty())
if (albums.isNotEmpty()) adapter?.swapDataSet(it)
adapter?.swapDataSet(albums) else
else adapter?.swapDataSet(listOf())
adapter?.swapDataSet(listOf()) })
})
} }
override val emptyMessage: Int override val emptyMessage: Int
@ -40,10 +38,11 @@ class AlbumsFragment :
override fun createAdapter(): AlbumAdapter { override fun createAdapter(): AlbumAdapter {
val dataSet = if (adapter == null) ArrayList() else adapter!!.dataSet val dataSet = if (adapter == null) ArrayList() else adapter!!.dataSet
return AlbumAdapter( return AlbumAdapter(
mainActivity, requireActivity(),
dataSet, dataSet,
itemLayoutRes(), R.layout.item_grid,
mainActivity null,
this
) )
} }
@ -90,12 +89,24 @@ class AlbumsFragment :
companion object { companion object {
@JvmField
var TAG: String = AlbumsFragment::class.java.simpleName
@JvmStatic
fun newInstance(): AlbumsFragment { fun newInstance(): AlbumsFragment {
return AlbumsFragment() return AlbumsFragment()
} }
} }
override fun onAlbumClick(albumId: Int, view: View) {
val controller = requireActivity().findNavController(R.id.fragment_container)
controller.navigate(
R.id.albumDetailsFragment,
bundleOf(EXTRA_ALBUM_ID to albumId),
null,
FragmentNavigatorExtras(
view to getString(R.string.transition_album_art)
)
)
}
} }
interface AlbumClickListener {
fun onAlbumClick(albumId: Int, view: View)
}

View file

@ -1,115 +1,79 @@
package io.github.muntashirakon.music.activities.artists package io.github.muntashirakon.music.fragments.artists
import android.app.Activity
import android.content.Intent import android.content.Intent
import android.os.Bundle import android.os.Bundle
import android.text.Spanned import android.text.Spanned
import android.transition.Slide
import android.view.Menu import android.view.Menu
import android.view.MenuInflater
import android.view.MenuItem import android.view.MenuItem
import android.view.View import android.view.View
import android.widget.Toast import androidx.core.os.bundleOf
import androidx.core.app.ActivityCompat
import androidx.core.text.HtmlCompat import androidx.core.text.HtmlCompat
import androidx.lifecycle.Observer
import androidx.navigation.fragment.FragmentNavigatorExtras
import androidx.navigation.fragment.findNavController
import androidx.navigation.fragment.navArgs
import androidx.recyclerview.widget.DefaultItemAnimator import androidx.recyclerview.widget.DefaultItemAnimator
import androidx.recyclerview.widget.GridLayoutManager import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import code.name.monkey.appthemehelper.util.ATHUtil import com.bumptech.glide.Glide
import code.name.monkey.appthemehelper.util.MaterialUtil
import io.github.muntashirakon.music.R import io.github.muntashirakon.music.R
import io.github.muntashirakon.music.activities.base.AbsSlidingMusicPanelActivity
import io.github.muntashirakon.music.adapter.album.HorizontalAlbumAdapter import io.github.muntashirakon.music.adapter.album.HorizontalAlbumAdapter
import io.github.muntashirakon.music.adapter.song.SimpleSongAdapter import io.github.muntashirakon.music.adapter.song.SimpleSongAdapter
import io.github.muntashirakon.music.dialogs.AddToPlaylistDialog import io.github.muntashirakon.music.dialogs.AddToPlaylistDialog
import io.github.muntashirakon.music.extensions.extraNotNull import io.github.muntashirakon.music.extensions.applyColor
import io.github.muntashirakon.music.extensions.show import io.github.muntashirakon.music.extensions.show
import io.github.muntashirakon.music.extensions.surfaceColor import io.github.muntashirakon.music.extensions.showToast
import io.github.muntashirakon.music.fragments.albums.AlbumClickListener
import io.github.muntashirakon.music.fragments.base.AbsMainActivityFragment
import io.github.muntashirakon.music.glide.ArtistGlideRequest import io.github.muntashirakon.music.glide.ArtistGlideRequest
import io.github.muntashirakon.music.glide.RetroMusicColoredTarget import io.github.muntashirakon.music.glide.RetroMusicColoredTarget
import io.github.muntashirakon.music.helper.MusicPlayerRemote import io.github.muntashirakon.music.helper.MusicPlayerRemote
import io.github.muntashirakon.music.interfaces.CabHolder
import io.github.muntashirakon.music.model.Artist import io.github.muntashirakon.music.model.Artist
import io.github.muntashirakon.music.network.model.LastFmArtist import io.github.muntashirakon.music.network.model.LastFmArtist
import io.github.muntashirakon.music.util.CustomArtistImageUtil import io.github.muntashirakon.music.util.CustomArtistImageUtil
import io.github.muntashirakon.music.util.MusicUtil import io.github.muntashirakon.music.util.MusicUtil
import io.github.muntashirakon.music.util.RetroColorUtil
import io.github.muntashirakon.music.util.RetroUtil import io.github.muntashirakon.music.util.RetroUtil
import io.github.muntashirakon.music.util.color.MediaNotificationProcessor import io.github.muntashirakon.music.util.color.MediaNotificationProcessor
import com.afollestad.materialcab.MaterialCab import kotlinx.android.synthetic.main.fragment_artist_content.*
import com.bumptech.glide.Glide import kotlinx.android.synthetic.main.fragment_artist_details.*
import kotlinx.android.synthetic.main.activity_artist_content.*
import kotlinx.android.synthetic.main.activity_artist_details.*
import org.koin.androidx.viewmodel.ext.android.viewModel import org.koin.androidx.viewmodel.ext.android.viewModel
import org.koin.core.parameter.parametersOf import org.koin.core.parameter.parametersOf
import java.util.* import java.util.*
import kotlin.collections.ArrayList import kotlin.collections.ArrayList
class ArtistDetailActivity : AbsSlidingMusicPanelActivity(), CabHolder { class ArtistDetailsFragment : AbsMainActivityFragment(R.layout.fragment_artist_details),
override fun openCab(menuRes: Int, callback: MaterialCab.Callback): MaterialCab { AlbumClickListener {
cab?.let { private val arguments by navArgs<ArtistDetailsFragmentArgs>()
if (it.isActive) it.finish() private val detailsViewModel: ArtistDetailsViewModel by viewModel {
} parametersOf(arguments.extraArtistId)
cab = MaterialCab(this, R.id.cab_stub)
.setMenu(menuRes)
.setCloseDrawableRes(R.drawable.ic_close)
.setBackgroundColor(
RetroColorUtil.shiftBackgroundColorForLightText(
ATHUtil.resolveColor(
this,
R.attr.colorSurface
)
)
)
.start(callback)
return cab as MaterialCab
} }
private var cab: MaterialCab? = null
private var biography: Spanned? = null
private lateinit var artist: Artist private lateinit var artist: Artist
private lateinit var songAdapter: SimpleSongAdapter private lateinit var songAdapter: SimpleSongAdapter
private lateinit var albumAdapter: HorizontalAlbumAdapter private lateinit var albumAdapter: HorizontalAlbumAdapter
private var forceDownload: Boolean = false private var forceDownload: Boolean = false
private val detailsViewModel: ArtistDetailsViewModel by viewModel { private var lang: String? = null
parametersOf(extraNotNull<Int>(EXTRA_ARTIST_ID).value) private var biography: Spanned? = null
}
override fun createContentView(): View {
return wrapSlidingMusicPanel(R.layout.activity_artist_details)
}
private fun windowEnterTransition() { override fun onActivityCreated(savedInstanceState: Bundle?) {
val slide = Slide() super.onActivityCreated(savedInstanceState)
slide.excludeTarget(R.id.appBarLayout, true) setHasOptionsMenu(true)
slide.excludeTarget(R.id.status_bar, true) mainActivity.setSupportActionBar(toolbar)
slide.excludeTarget(android.R.id.statusBarBackground, true) mainActivity.hideBottomBarVisibility(false)
slide.excludeTarget(android.R.id.navigationBarBackground, true) toolbar.title = null
window.enterTransition = slide setupRecyclerView()
} postponeEnterTransition()
detailsViewModel.getArtist().observe(viewLifecycleOwner, Observer {
override fun onCreate(savedInstanceState: Bundle?) { showArtist(it)
setDrawUnderStatusBar() startPostponedEnterTransition()
super.onCreate(savedInstanceState)
setStatusbarColorAuto()
setNavigationbarColorAuto()
setTaskDescriptionColorAuto()
setLightNavigationBar(true)
setBottomBarVisibility(View.GONE)
window.sharedElementsUseOverlay = true
windowEnterTransition()
toolbar.setBackgroundColor(surfaceColor())
addMusicServiceEventListener(detailsViewModel)
ActivityCompat.postponeEnterTransition(this)
detailsViewModel.getArtist().observe(this, androidx.lifecycle.Observer {
ActivityCompat.startPostponedEnterTransition(this@ArtistDetailActivity)
artist(it)
}) })
detailsViewModel.getArtistInfo().observe(this, androidx.lifecycle.Observer { detailsViewModel.getArtistInfo().observe(viewLifecycleOwner, Observer {
artistInfo(it) artistInfo(it)
}) })
setupRecyclerView()
playAction.apply { playAction.apply {
setOnClickListener { MusicPlayerRemote.openQueue(artist.songs, 0, true) } setOnClickListener { MusicPlayerRemote.openQueue(artist.songs, 0, true) }
} }
@ -127,13 +91,13 @@ class ArtistDetailActivity : AbsSlidingMusicPanelActivity(), CabHolder {
} }
private fun setupRecyclerView() { private fun setupRecyclerView() {
albumAdapter = HorizontalAlbumAdapter(this, ArrayList(), null) albumAdapter = HorizontalAlbumAdapter(requireActivity(), ArrayList(), null, this)
albumRecyclerView.apply { albumRecyclerView.apply {
itemAnimator = DefaultItemAnimator() itemAnimator = DefaultItemAnimator()
layoutManager = GridLayoutManager(this.context, 1, GridLayoutManager.HORIZONTAL, false) layoutManager = GridLayoutManager(this.context, 1, GridLayoutManager.HORIZONTAL, false)
adapter = albumAdapter adapter = albumAdapter
} }
songAdapter = SimpleSongAdapter(this, ArrayList(), R.layout.item_song, this) songAdapter = SimpleSongAdapter(requireActivity(), ArrayList(), R.layout.item_song, null)
recyclerView.apply { recyclerView.apply {
itemAnimator = DefaultItemAnimator() itemAnimator = DefaultItemAnimator()
layoutManager = LinearLayoutManager(this.context) layoutManager = LinearLayoutManager(this.context)
@ -141,33 +105,16 @@ class ArtistDetailActivity : AbsSlidingMusicPanelActivity(), CabHolder {
} }
} }
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { fun showArtist(artist: Artist) {
super.onActivityResult(requestCode, resultCode, data)
when (requestCode) {
REQUEST_CODE_SELECT_IMAGE -> if (resultCode == Activity.RESULT_OK) {
data?.data?.let {
CustomArtistImageUtil.getInstance(this).setCustomArtistImage(artist, it)
}
}
else -> if (resultCode == Activity.RESULT_OK) {
//reload()
}
}
}
fun artist(artist: Artist) {
if (artist.songs.isEmpty()) {
finish()
}
this.artist = artist this.artist = artist
loadArtistImage(artist) loadArtistImage(artist)
if (RetroUtil.isAllowedToDownloadMetadata(this)) { if (RetroUtil.isAllowedToDownloadMetadata(requireContext())) {
loadBiography(artist.name) loadBiography(artist.name)
} }
artistTitle.text = artist.name artistTitle.text = artist.name
text.text = String.format( text.text = String.format(
"%s • %s", "%s • %s",
MusicUtil.getArtistInfoString(this, artist), MusicUtil.getArtistInfoString(requireContext(), artist),
MusicUtil.getReadableDurationString(MusicUtil.getTotalDuration(artist.songs)) MusicUtil.getReadableDurationString(MusicUtil.getTotalDuration(artist.songs))
) )
val songText = val songText =
@ -184,7 +131,7 @@ class ArtistDetailActivity : AbsSlidingMusicPanelActivity(), CabHolder {
) )
songTitle.text = songText songTitle.text = songText
albumTitle.text = albumText albumTitle.text = albumText
songAdapter.swapDataSet(artist.songs) songAdapter.swapDataSet(artist.songs.sortedBy { it.trackNumber })
artist.albums?.let { albumAdapter.swapDataSet(it) } artist.albums?.let { albumAdapter.swapDataSet(it) }
} }
@ -224,31 +171,32 @@ class ArtistDetailActivity : AbsSlidingMusicPanelActivity(), CabHolder {
} }
} }
private var lang: String? = null
private fun loadArtistImage(artist: Artist) { private fun loadArtistImage(artist: Artist) {
ArtistGlideRequest.Builder.from(Glide.with(this), artist) ArtistGlideRequest.Builder.from(Glide.with(requireContext()), artist)
.generatePalette(this).build() .generatePalette(requireContext()).build()
.dontAnimate().into(object : RetroMusicColoredTarget(image) { .dontAnimate().into(object : RetroMusicColoredTarget(image) {
override fun onColorReady(colors: MediaNotificationProcessor) { override fun onColorReady(colors: MediaNotificationProcessor) {
startPostponedEnterTransition()
setColors(colors) setColors(colors)
} }
}) })
} }
private fun setColors(color: MediaNotificationProcessor) { private fun setColors(color: MediaNotificationProcessor) {
MaterialUtil.tintColor( shuffleAction.applyColor(color.backgroundColor)
button = shuffleAction, playAction.applyColor(color.backgroundColor)
textColor = color.primaryTextColor, }
backgroundColor = color.backgroundColor
override fun onAlbumClick(albumId: Int, view: View) {
findNavController().navigate(
R.id.albumDetailsFragment,
bundleOf("extra_album_id" to albumId),
null,
FragmentNavigatorExtras(
view to getString(R.string.transition_album_art)
)
) )
MaterialUtil.tintColor(
button = playAction,
textColor = color.primaryTextColor,
backgroundColor = color.backgroundColor
)
setSupportActionBar(toolbar)
supportActionBar?.title = null
} }
override fun onOptionsItemSelected(item: MenuItem): Boolean { override fun onOptionsItemSelected(item: MenuItem): Boolean {
@ -258,10 +206,7 @@ class ArtistDetailActivity : AbsSlidingMusicPanelActivity(), CabHolder {
private fun handleSortOrderMenuItem(item: MenuItem): Boolean { private fun handleSortOrderMenuItem(item: MenuItem): Boolean {
val songs = artist.songs val songs = artist.songs
when (item.itemId) { when (item.itemId) {
android.R.id.home -> { android.R.id.home -> findNavController().navigateUp()
super.onBackPressed()
return true
}
R.id.action_play_next -> { R.id.action_play_next -> {
MusicPlayerRemote.playNext(songs) MusicPlayerRemote.playNext(songs)
return true return true
@ -271,7 +216,7 @@ class ArtistDetailActivity : AbsSlidingMusicPanelActivity(), CabHolder {
return true return true
} }
R.id.action_add_to_playlist -> { R.id.action_add_to_playlist -> {
AddToPlaylistDialog.create(songs).show(supportFragmentManager, "ADD_PLAYLIST") AddToPlaylistDialog.create(songs).show(childFragmentManager, "ADD_PLAYLIST")
return true return true
} }
R.id.action_set_artist_image -> { R.id.action_set_artist_image -> {
@ -284,14 +229,8 @@ class ArtistDetailActivity : AbsSlidingMusicPanelActivity(), CabHolder {
return true return true
} }
R.id.action_reset_artist_image -> { R.id.action_reset_artist_image -> {
Toast.makeText( showToast(resources.getString(R.string.updating))
this@ArtistDetailActivity, CustomArtistImageUtil.getInstance(requireContext()).resetCustomArtistImage(artist)
resources.getString(R.string.updating),
Toast.LENGTH_SHORT
)
.show()
CustomArtistImageUtil.getInstance(this@ArtistDetailActivity)
.resetCustomArtistImage(artist)
forceDownload = true forceDownload = true
return true return true
} }
@ -299,26 +238,12 @@ class ArtistDetailActivity : AbsSlidingMusicPanelActivity(), CabHolder {
return true return true
} }
override fun onCreateOptionsMenu(menu: Menu): Boolean { override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
menuInflater.inflate(R.menu.menu_artist_detail, menu) super.onCreateOptionsMenu(menu, inflater)
return super.onCreateOptionsMenu(menu) inflater.inflate(R.menu.menu_artist_detail, menu)
}
override fun onBackPressed() {
if (cab != null && cab!!.isActive) {
cab?.finish()
} else {
super.onBackPressed()
}
}
override fun onDestroy() {
super.onDestroy()
removeMusicServiceEventListener(detailsViewModel)
} }
companion object { companion object {
const val EXTRA_ARTIST_ID = "extra_artist_id" const val REQUEST_CODE_SELECT_IMAGE = 9002
const val REQUEST_CODE_SELECT_IMAGE = 9003
} }
} }

View file

@ -1,4 +1,4 @@
package io.github.muntashirakon.music.activities.artists package io.github.muntashirakon.music.fragments.artists
import androidx.lifecycle.LiveData import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData import androidx.lifecycle.MutableLiveData
@ -6,21 +6,21 @@ import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import io.github.muntashirakon.music.interfaces.MusicServiceEventListener import io.github.muntashirakon.music.interfaces.MusicServiceEventListener
import io.github.muntashirakon.music.model.Artist import io.github.muntashirakon.music.model.Artist
import io.github.muntashirakon.music.providers.RepositoryImpl
import io.github.muntashirakon.music.network.model.LastFmArtist import io.github.muntashirakon.music.network.model.LastFmArtist
import io.github.muntashirakon.music.repository.RealRepository
import kotlinx.coroutines.Deferred import kotlinx.coroutines.Deferred
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.async import kotlinx.coroutines.async
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
class ArtistDetailsViewModel( class ArtistDetailsViewModel(
private val repository: RepositoryImpl, private val realRepository: RealRepository,
private val artistId: Int private val artistId: Int
) : ViewModel(), MusicServiceEventListener { ) : ViewModel(), MusicServiceEventListener {
private val loadArtistDetailsAsync: Deferred<Artist?> private val loadArtistDetailsAsync: Deferred<Artist?>
get() = viewModelScope.async(Dispatchers.IO) { get() = viewModelScope.async(Dispatchers.IO) {
repository.artistById(artistId) realRepository.artistById(artistId)
} }
private val _artist = MutableLiveData<Artist>() private val _artist = MutableLiveData<Artist>()
@ -40,7 +40,7 @@ class ArtistDetailsViewModel(
} }
fun loadBiography(name: String, lang: String?, cache: String?) = viewModelScope.launch { fun loadBiography(name: String, lang: String?, cache: String?) = viewModelScope.launch {
val info = repository.artistInfo(name, lang, cache) val info = realRepository.artistInfo(name, lang, cache)
_lastFmArtist.postValue(info) _lastFmArtist.postValue(info)
} }

View file

@ -2,10 +2,14 @@ package io.github.muntashirakon.music.fragments.artists
import android.os.Bundle import android.os.Bundle
import android.view.View import android.view.View
import android.widget.ImageView
import androidx.core.os.bundleOf
import androidx.lifecycle.Observer import androidx.lifecycle.Observer
import androidx.recyclerview.widget.GridLayoutManager import androidx.recyclerview.widget.GridLayoutManager
import io.github.muntashirakon.music.EXTRA_ARTIST_ID
import io.github.muntashirakon.music.R import io.github.muntashirakon.music.R
import io.github.muntashirakon.music.adapter.artist.ArtistAdapter import io.github.muntashirakon.music.adapter.artist.ArtistAdapter
import io.github.muntashirakon.music.extensions.findActivityNavController
import io.github.muntashirakon.music.fragments.ReloadType import io.github.muntashirakon.music.fragments.ReloadType
import io.github.muntashirakon.music.fragments.base.AbsRecyclerViewCustomGridSizeFragment import io.github.muntashirakon.music.fragments.base.AbsRecyclerViewCustomGridSizeFragment
import io.github.muntashirakon.music.interfaces.MainActivityFragmentCallbacks import io.github.muntashirakon.music.interfaces.MainActivityFragmentCallbacks
@ -13,7 +17,7 @@ import io.github.muntashirakon.music.util.PreferenceUtil
class ArtistsFragment : class ArtistsFragment :
AbsRecyclerViewCustomGridSizeFragment<ArtistAdapter, GridLayoutManager>(), AbsRecyclerViewCustomGridSizeFragment<ArtistAdapter, GridLayoutManager>(),
MainActivityFragmentCallbacks { MainActivityFragmentCallbacks, ArtistClickListener {
override fun handleBackPress(): Boolean { override fun handleBackPress(): Boolean {
return false return false
@ -22,14 +26,12 @@ class ArtistsFragment :
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
libraryViewModel.artistsLiveData libraryViewModel.artistsLiveData.observe(viewLifecycleOwner, Observer {
.observe(viewLifecycleOwner, Observer { artists -> if (it.isNotEmpty())
if (artists.isNotEmpty()) { adapter?.swapDataSet(it)
adapter?.swapDataSet(artists) else
} else { adapter?.swapDataSet(listOf())
adapter?.swapDataSet(listOf()) })
}
})
} }
override val emptyMessage: Int override val emptyMessage: Int
@ -46,10 +48,11 @@ class ArtistsFragment :
override fun createAdapter(): ArtistAdapter { override fun createAdapter(): ArtistAdapter {
val dataSet = if (adapter == null) ArrayList() else adapter!!.dataSet val dataSet = if (adapter == null) ArrayList() else adapter!!.dataSet
return ArtistAdapter( return ArtistAdapter(
mainActivity, requireActivity(),
dataSet, dataSet,
itemLayoutRes(), R.layout.item_grid_circle,
mainActivity null,
this
) )
} }
@ -91,12 +94,18 @@ class ArtistsFragment :
} }
companion object { companion object {
@JvmField
val TAG: String = ArtistsFragment::class.java.simpleName
@JvmStatic
fun newInstance(): ArtistsFragment { fun newInstance(): ArtistsFragment {
return ArtistsFragment() return ArtistsFragment()
} }
} }
override fun onArtist(artistId: Int, imageView: ImageView) {
val controller = findActivityNavController(R.id.fragment_container)
controller.navigate(R.id.artistDetailsFragment, bundleOf(EXTRA_ARTIST_ID to artistId))
}
}
interface ArtistClickListener {
fun onArtist(artistId: Int, imageView: ImageView)
} }

View file

@ -7,6 +7,8 @@ import android.view.View
import android.webkit.MimeTypeMap import android.webkit.MimeTypeMap
import androidx.annotation.LayoutRes import androidx.annotation.LayoutRes
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.navigation.navOptions
import io.github.muntashirakon.music.R
import io.github.muntashirakon.music.activities.base.AbsMusicServiceActivity import io.github.muntashirakon.music.activities.base.AbsMusicServiceActivity
import io.github.muntashirakon.music.interfaces.MusicServiceEventListener import io.github.muntashirakon.music.interfaces.MusicServiceEventListener
import io.github.muntashirakon.music.model.Song import io.github.muntashirakon.music.model.Song
@ -23,6 +25,21 @@ import java.util.*
open class AbsMusicServiceFragment(@LayoutRes layout: Int) : Fragment(layout), open class AbsMusicServiceFragment(@LayoutRes layout: Int) : Fragment(layout),
MusicServiceEventListener { MusicServiceEventListener {
val navOptions by lazy {
navOptions {
popUpTo(R.id.action_home) {
inclusive = false
}
launchSingleTop = false
anim {
enter = R.anim.retro_fragment_open_enter
exit = R.anim.retro_fragment_open_exit
popEnter = R.anim.retro_fragment_close_enter
popExit = R.anim.retro_fragment_close_exit
}
}
}
var playerActivity: AbsMusicServiceActivity? = null var playerActivity: AbsMusicServiceActivity? = null
private set private set

View file

@ -14,6 +14,10 @@ import android.view.View
import android.widget.Toast import android.widget.Toast
import androidx.annotation.LayoutRes import androidx.annotation.LayoutRes
import androidx.appcompat.widget.Toolbar import androidx.appcompat.widget.Toolbar
import androidx.core.os.bundleOf
import androidx.navigation.findNavController
import io.github.muntashirakon.music.EXTRA_ALBUM_ID
import io.github.muntashirakon.music.EXTRA_ARTIST_ID
import io.github.muntashirakon.music.R import io.github.muntashirakon.music.R
import io.github.muntashirakon.music.activities.tageditor.AbsTagEditorActivity import io.github.muntashirakon.music.activities.tageditor.AbsTagEditorActivity
import io.github.muntashirakon.music.activities.tageditor.SongTagEditorActivity import io.github.muntashirakon.music.activities.tageditor.SongTagEditorActivity
@ -30,11 +34,8 @@ import kotlinx.android.synthetic.main.shadow_statusbar_toolbar.*
import org.koin.androidx.viewmodel.ext.android.sharedViewModel import org.koin.androidx.viewmodel.ext.android.sharedViewModel
import java.io.FileNotFoundException import java.io.FileNotFoundException
abstract class AbsPlayerFragment(@LayoutRes layout: Int) : AbsMusicServiceFragment(layout), abstract class AbsPlayerFragment(@LayoutRes layout: Int) : AbsMainActivityFragment(layout),
Toolbar.OnMenuItemClickListener, Toolbar.OnMenuItemClickListener, PaletteColorHolder, PlayerAlbumCoverFragment.Callbacks {
PaletteColorHolder,
PlayerAlbumCoverFragment.Callbacks {
private var updateIsFavoriteTask: AsyncTask<*, *, *>? = null private var updateIsFavoriteTask: AsyncTask<*, *, *>? = null
private var updateLyricsAsyncTask: AsyncTask<*, *, *>? = null private var updateLyricsAsyncTask: AsyncTask<*, *, *>? = null
@ -86,11 +87,19 @@ abstract class AbsPlayerFragment(@LayoutRes layout: Int) : AbsMusicServiceFragme
return true return true
} }
R.id.action_go_to_album -> { R.id.action_go_to_album -> {
NavigationUtil.goToAlbum(requireActivity(), song.albumId) mainActivity.collapsePanel()
requireActivity().findNavController(R.id.fragment_container).navigate(
R.id.albumDetailsFragment,
bundleOf(EXTRA_ALBUM_ID to song.albumId)
)
return true return true
} }
R.id.action_go_to_artist -> { R.id.action_go_to_artist -> {
NavigationUtil.goToArtist(requireActivity(), song.artistId) mainActivity.collapsePanel()
requireActivity().findNavController(R.id.fragment_container).navigate(
R.id.artistDetailsFragment,
bundleOf(EXTRA_ARTIST_ID to song.artistId)
)
return true return true
} }
R.id.now_playing -> { R.id.now_playing -> {
@ -254,11 +263,6 @@ abstract class AbsPlayerFragment(@LayoutRes layout: Int) : AbsMusicServiceFragme
statusBarShadow?.hide() statusBarShadow?.hide()
} }
interface Callbacks {
fun onPaletteColorChanged()
}
companion object { companion object {
val TAG: String = AbsPlayerFragment::class.java.simpleName val TAG: String = AbsPlayerFragment::class.java.simpleName
const val VISIBILITY_ANIM_DURATION: Long = 300 const val VISIBILITY_ANIM_DURATION: Long = 300

View file

@ -6,26 +6,24 @@ import android.view.ViewGroup
import androidx.annotation.NonNull import androidx.annotation.NonNull
import androidx.annotation.StringRes import androidx.annotation.StringRes
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import com.google.android.material.appbar.AppBarLayout
import io.github.muntashirakon.music.R import io.github.muntashirakon.music.R
import io.github.muntashirakon.music.activities.MainActivity
import io.github.muntashirakon.music.fragments.LibraryViewModel import io.github.muntashirakon.music.fragments.LibraryViewModel
import io.github.muntashirakon.music.helper.MusicPlayerRemote import io.github.muntashirakon.music.helper.MusicPlayerRemote
import io.github.muntashirakon.music.util.DensityUtil import io.github.muntashirakon.music.util.DensityUtil
import io.github.muntashirakon.music.util.ThemedFastScroller.create import io.github.muntashirakon.music.util.ThemedFastScroller.create
import io.github.muntashirakon.music.views.ScrollingViewOnApplyWindowInsetsListener import io.github.muntashirakon.music.views.ScrollingViewOnApplyWindowInsetsListener
import com.google.android.material.appbar.AppBarLayout
import kotlinx.android.synthetic.main.fragment_main_activity_recycler_view.* import kotlinx.android.synthetic.main.fragment_main_activity_recycler_view.*
import me.zhanghai.android.fastscroll.FastScroller import me.zhanghai.android.fastscroll.FastScroller
import me.zhanghai.android.fastscroll.FastScrollerBuilder import me.zhanghai.android.fastscroll.FastScrollerBuilder
import org.koin.androidx.viewmodel.ext.android.sharedViewModel import org.koin.androidx.viewmodel.ext.android.sharedViewModel
abstract class AbsRecyclerViewFragment<A : RecyclerView.Adapter<*>, LM : RecyclerView.LayoutManager> : abstract class AbsRecyclerViewFragment<A : RecyclerView.Adapter<*>, LM : RecyclerView.LayoutManager> :
AbsMusicServiceFragment(R.layout.fragment_main_activity_recycler_view), AbsMusicServiceFragment(R.layout.fragment_main_activity_recycler_view),
AppBarLayout.OnOffsetChangedListener { AppBarLayout.OnOffsetChangedListener {
val libraryViewModel: LibraryViewModel by sharedViewModel() val libraryViewModel: LibraryViewModel by sharedViewModel()
val mainActivity: MainActivity
get() = requireActivity() as MainActivity
override fun onActivityCreated(savedInstanceState: Bundle?) { override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState) super.onActivityCreated(savedInstanceState)
@ -38,22 +36,23 @@ abstract class AbsRecyclerViewFragment<A : RecyclerView.Adapter<*>, LM : Recycle
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
mainActivity.addOnAppBarOffsetChangedListener(this)
initLayoutManager() initLayoutManager()
initAdapter() initAdapter()
setUpRecyclerView() setUpRecyclerView()
} }
private fun setUpRecyclerView() { private fun setUpRecyclerView() {
recyclerView.layoutManager = layoutManager recyclerView.apply {
recyclerView.adapter = adapter layoutManager = this@AbsRecyclerViewFragment.layoutManager
val fastScroller = create(recyclerView) adapter = this@AbsRecyclerViewFragment.adapter
recyclerView.setOnApplyWindowInsetsListener( val fastScroller = create(this)
ScrollingViewOnApplyWindowInsetsListener( setOnApplyWindowInsetsListener(
recyclerView, ScrollingViewOnApplyWindowInsetsListener(
fastScroller recyclerView,
fastScroller
)
) )
) }
checkForPadding() checkForPadding()
} }
@ -112,7 +111,7 @@ abstract class AbsRecyclerViewFragment<A : RecyclerView.Adapter<*>, LM : Recycle
container.paddingLeft, container.paddingLeft,
container.paddingTop, container.paddingTop,
container.paddingRight, container.paddingRight,
mainActivity.getTotalAppBarScrollingRange() + i i
) )
} }
@ -137,11 +136,6 @@ abstract class AbsRecyclerViewFragment<A : RecyclerView.Adapter<*>, LM : Recycle
recyclerView.adapter = adapter recyclerView.adapter = adapter
} }
override fun onDestroyView() {
super.onDestroyView()
mainActivity.removeOnAppBarOffsetChangedListener(this)
}
fun recyclerView(): RecyclerView { fun recyclerView(): RecyclerView {
return recyclerView return recyclerView
} }

View file

@ -60,6 +60,7 @@ import io.github.muntashirakon.music.helper.MusicPlayerRemote;
import io.github.muntashirakon.music.helper.menu.SongMenuHelper; import io.github.muntashirakon.music.helper.menu.SongMenuHelper;
import io.github.muntashirakon.music.helper.menu.SongsMenuHelper; import io.github.muntashirakon.music.helper.menu.SongsMenuHelper;
import io.github.muntashirakon.music.interfaces.CabHolder; import io.github.muntashirakon.music.interfaces.CabHolder;
import io.github.muntashirakon.music.interfaces.Callbacks;
import io.github.muntashirakon.music.interfaces.MainActivityFragmentCallbacks; import io.github.muntashirakon.music.interfaces.MainActivityFragmentCallbacks;
import io.github.muntashirakon.music.misc.DialogAsyncTask; import io.github.muntashirakon.music.misc.DialogAsyncTask;
import io.github.muntashirakon.music.misc.UpdateToastMediaScannerCompletionListener; import io.github.muntashirakon.music.misc.UpdateToastMediaScannerCompletionListener;
@ -76,7 +77,9 @@ import me.zhanghai.android.fastscroll.FastScroller;
public class FoldersFragment extends AbsMainActivityFragment implements public class FoldersFragment extends AbsMainActivityFragment implements
MainActivityFragmentCallbacks, MainActivityFragmentCallbacks,
CabHolder, BreadCrumbLayout.SelectionCallback, SongFileAdapter.Callbacks, CabHolder,
BreadCrumbLayout.SelectionCallback,
Callbacks,
LoaderManager.LoaderCallbacks<List<File>> { LoaderManager.LoaderCallbacks<List<File>> {
public static final String TAG = FoldersFragment.class.getSimpleName(); public static final String TAG = FoldersFragment.class.getSimpleName();
@ -84,7 +87,7 @@ public class FoldersFragment extends AbsMainActivityFragment implements
FileUtil.fileIsMimeType(file, "audio/*", MimeTypeMap.getSingleton()) || FileUtil.fileIsMimeType(file, "audio/*", MimeTypeMap.getSingleton()) ||
FileUtil.fileIsMimeType(file, "application/opus", MimeTypeMap.getSingleton()) || FileUtil.fileIsMimeType(file, "application/opus", MimeTypeMap.getSingleton()) ||
FileUtil.fileIsMimeType(file, "application/ogg", MimeTypeMap.getSingleton())); FileUtil.fileIsMimeType(file, "application/ogg", MimeTypeMap.getSingleton()));
public static final String PATH = "path";
private static final String CRUMBS = "crumbs"; private static final String CRUMBS = "crumbs";
private static final int LOADER_ID = 5; private static final int LOADER_ID = 5;
private SongFileAdapter adapter; private SongFileAdapter adapter;
@ -125,18 +128,6 @@ public class FoldersFragment extends AbsMainActivityFragment implements
return startFolder; return startFolder;
} }
public static FoldersFragment newInstance(File directory) {
FoldersFragment frag = new FoldersFragment();
Bundle b = new Bundle();
b.putSerializable(PATH, directory);
frag.setArguments(b);
return frag;
}
public static FoldersFragment newInstance(Context context) {
return newInstance(PreferenceUtil.INSTANCE.getStartDirectory());
}
private static File tryGetCanonicalFile(File file) { private static File tryGetCanonicalFile(File file) {
try { try {
return file.getCanonicalFile(); return file.getCanonicalFile();
@ -171,7 +162,7 @@ public class FoldersFragment extends AbsMainActivityFragment implements
if (savedInstanceState == null) { if (savedInstanceState == null) {
//noinspection ConstantConditions //noinspection ConstantConditions
setCrumb(new BreadCrumbLayout.Crumb(FileUtil.safeGetCanonicalFile((File) requireArguments().getSerializable(PATH))), true); setCrumb(new BreadCrumbLayout.Crumb(FileUtil.safeGetCanonicalFile(PreferenceUtil.INSTANCE.getStartDirectory())), true);
} else { } else {
breadCrumbs.restoreFromStateWrapper(savedInstanceState.getParcelable(CRUMBS)); breadCrumbs.restoreFromStateWrapper(savedInstanceState.getParcelable(CRUMBS));
getLoaderManager().initLoader(LOADER_ID, null, this); getLoaderManager().initLoader(LOADER_ID, null, this);
@ -299,7 +290,7 @@ public class FoldersFragment extends AbsMainActivityFragment implements
} }
} }
if (startIndex > -1) { if (startIndex > -1) {
MusicPlayerRemote.INSTANCE.openQueue(songs, startIndex, true); MusicPlayerRemote.openQueue(songs, startIndex, true);
} else { } else {
final File finalFile = file1; final File finalFile = file1;
Snackbar.make(coordinatorLayout, Html.fromHtml( Snackbar.make(coordinatorLayout, Html.fromHtml(
@ -619,7 +610,7 @@ public class FoldersFragment extends AbsMainActivityFragment implements
} }
private static class ListSongsAsyncTask private static class ListSongsAsyncTask
extends ListingFilesDialogAsyncTask<ListSongsAsyncTask.LoadingInfo, Void, ArrayList<Song>> { extends ListingFilesDialogAsyncTask<ListSongsAsyncTask.LoadingInfo, Void, List<Song>> {
private final Object extra; private final Object extra;
private WeakReference<OnSongsListedCallback> callbackWeakReference; private WeakReference<OnSongsListedCallback> callbackWeakReference;
@ -633,7 +624,7 @@ public class FoldersFragment extends AbsMainActivityFragment implements
} }
@Override @Override
protected ArrayList<Song> doInBackground(LoadingInfo... params) { protected List<Song> doInBackground(LoadingInfo... params) {
try { try {
LoadingInfo info = params[0]; LoadingInfo info = params[0];
List<File> files = FileUtil.listFilesDeep(info.files, info.fileFilter); List<File> files = FileUtil.listFilesDeep(info.files, info.fileFilter);
@ -659,7 +650,7 @@ public class FoldersFragment extends AbsMainActivityFragment implements
} }
@Override @Override
protected void onPostExecute(ArrayList<Song> songs) { protected void onPostExecute(List<Song> songs) {
super.onPostExecute(songs); super.onPostExecute(songs);
OnSongsListedCallback callback = checkCallbackReference(); OnSongsListedCallback callback = checkCallbackReference();
if (songs != null && callback != null) { if (songs != null && callback != null) {
@ -692,7 +683,7 @@ public class FoldersFragment extends AbsMainActivityFragment implements
public interface OnSongsListedCallback { public interface OnSongsListedCallback {
void onSongsListed(@NonNull ArrayList<Song> songs, Object extra); void onSongsListed(@NonNull List<Song> songs, Object extra);
} }
static class LoadingInfo { static class LoadingInfo {

View file

@ -0,0 +1,92 @@
package io.github.muntashirakon.music.fragments.genres
import android.os.Bundle
import android.view.Menu
import android.view.MenuInflater
import android.view.MenuItem
import android.view.View
import androidx.navigation.fragment.navArgs
import androidx.recyclerview.widget.DefaultItemAnimator
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import io.github.muntashirakon.music.R
import io.github.muntashirakon.music.adapter.song.SongAdapter
import io.github.muntashirakon.music.extensions.dipToPix
import io.github.muntashirakon.music.fragments.base.AbsMainActivityFragment
import io.github.muntashirakon.music.helper.menu.GenreMenuHelper
import io.github.muntashirakon.music.model.Genre
import io.github.muntashirakon.music.model.Song
import kotlinx.android.synthetic.main.fragment_playlist_detail.*
import org.koin.androidx.viewmodel.ext.android.viewModel
import org.koin.core.parameter.parametersOf
import java.util.*
class GenreDetailsFragment : AbsMainActivityFragment(R.layout.fragment_playlist_detail) {
private val arguments by navArgs<GenreDetailsFragmentArgs>()
private val detailsViewModel: GenreDetailsViewModel by viewModel {
parametersOf(arguments.extraGenre)
}
private lateinit var genre: Genre
private lateinit var songAdapter: SongAdapter
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
setHasOptionsMenu(true)
mainActivity.addMusicServiceEventListener(detailsViewModel)
mainActivity.setSupportActionBar(toolbar)
mainActivity.hideBottomBarVisibility(false)
setupRecyclerView()
detailsViewModel.getSongs().observe(viewLifecycleOwner, androidx.lifecycle.Observer {
songs(it)
})
detailsViewModel.getGenre().observe(viewLifecycleOwner, androidx.lifecycle.Observer {
genre = it
toolbar?.title = it.name
})
}
private fun setupRecyclerView() {
songAdapter = SongAdapter(requireActivity(), ArrayList(), R.layout.item_list, null)
recyclerView.apply {
itemAnimator = DefaultItemAnimator()
layoutManager = LinearLayoutManager(requireContext())
adapter = songAdapter
}
songAdapter.registerAdapterDataObserver(object : RecyclerView.AdapterDataObserver() {
override fun onChanged() {
super.onChanged()
checkIsEmpty()
}
})
}
fun songs(songs: List<Song>) {
songAdapter.swapDataSet(songs)
}
private fun getEmojiByUnicode(unicode: Int): String {
return String(Character.toChars(unicode))
}
private fun checkIsEmpty() {
checkForPadding()
emptyEmoji.text = getEmojiByUnicode(0x1F631)
empty?.visibility = if (songAdapter.itemCount == 0) View.VISIBLE else View.GONE
}
private fun checkForPadding() {
val height = dipToPix(52f).toInt()
recyclerView.setPadding(0, 0, 0, height)
}
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
super.onCreateOptionsMenu(menu, inflater)
inflater.inflate(R.menu.menu_genre_detail, menu)
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
return GenreMenuHelper.handleMenuClick(requireActivity(), genre, item)
}
}

View file

@ -1,4 +1,4 @@
package io.github.muntashirakon.music.activities.genre package io.github.muntashirakon.music.fragments.genres
import androidx.lifecycle.LiveData import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData import androidx.lifecycle.MutableLiveData
@ -7,13 +7,13 @@ import androidx.lifecycle.viewModelScope
import io.github.muntashirakon.music.interfaces.MusicServiceEventListener import io.github.muntashirakon.music.interfaces.MusicServiceEventListener
import io.github.muntashirakon.music.model.Genre import io.github.muntashirakon.music.model.Genre
import io.github.muntashirakon.music.model.Song import io.github.muntashirakon.music.model.Song
import io.github.muntashirakon.music.providers.RepositoryImpl import io.github.muntashirakon.music.repository.RealRepository
import kotlinx.coroutines.Dispatchers.Main import kotlinx.coroutines.Dispatchers.Main
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
class GenreDetailsViewModel( class GenreDetailsViewModel(
private val repository: RepositoryImpl, private val realRepository: RealRepository,
private val genre: Genre private val genre: Genre
) : ViewModel(), MusicServiceEventListener { ) : ViewModel(), MusicServiceEventListener {
@ -31,7 +31,7 @@ class GenreDetailsViewModel(
} }
private fun loadGenreSongs(genre: Genre) = viewModelScope.launch { private fun loadGenreSongs(genre: Genre) = viewModelScope.launch {
val songs = repository.getGenre(genre.id) val songs = realRepository.getGenre(genre.id)
withContext(Main) { _playListSongs.postValue(songs) } withContext(Main) { _playListSongs.postValue(songs) }
} }

View file

@ -32,14 +32,12 @@ class GenresFragment : AbsRecyclerViewFragment<GenreAdapter, LinearLayoutManager
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
libraryViewModel.genresLiveData libraryViewModel.genresLiveData.observe(viewLifecycleOwner, Observer {
.observe(viewLifecycleOwner, Observer { genres -> if (it.isNotEmpty())
if (genres.isNotEmpty()) { adapter?.swapDataSet(it)
adapter?.swapDataSet(genres) else
} else { adapter?.swapDataSet(listOf())
adapter?.swapDataSet(listOf()) })
}
})
} }
@ -49,7 +47,7 @@ class GenresFragment : AbsRecyclerViewFragment<GenreAdapter, LinearLayoutManager
override fun createAdapter(): GenreAdapter { override fun createAdapter(): GenreAdapter {
val dataSet = if (adapter == null) ArrayList() else adapter!!.dataSet val dataSet = if (adapter == null) ArrayList() else adapter!!.dataSet
return GenreAdapter(mainActivity, dataSet, R.layout.item_list_no_image) return GenreAdapter(requireActivity(), dataSet, R.layout.item_list_no_image)
} }
override val emptyMessage: Int override val emptyMessage: Int

View file

@ -18,36 +18,38 @@ import android.app.ActivityOptions
import android.os.Bundle import android.os.Bundle
import android.util.DisplayMetrics import android.util.DisplayMetrics
import android.view.View import android.view.View
import androidx.core.os.bundleOf
import androidx.lifecycle.Observer import androidx.lifecycle.Observer
import androidx.lifecycle.lifecycleScope
import androidx.navigation.findNavController
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import com.bumptech.glide.Glide
import io.github.muntashirakon.music.EXTRA_PLAYLIST
import io.github.muntashirakon.music.R import io.github.muntashirakon.music.R
import io.github.muntashirakon.music.adapter.HomeAdapter import io.github.muntashirakon.music.adapter.HomeAdapter
import io.github.muntashirakon.music.extensions.findActivityNavController
import io.github.muntashirakon.music.fragments.LibraryViewModel import io.github.muntashirakon.music.fragments.LibraryViewModel
import io.github.muntashirakon.music.fragments.base.AbsMainActivityFragment import io.github.muntashirakon.music.fragments.base.AbsMainActivityFragment
import io.github.muntashirakon.music.glide.ProfileBannerGlideRequest import io.github.muntashirakon.music.glide.ProfileBannerGlideRequest
import io.github.muntashirakon.music.glide.UserProfileGlideRequest import io.github.muntashirakon.music.glide.UserProfileGlideRequest
import io.github.muntashirakon.music.helper.MusicPlayerRemote import io.github.muntashirakon.music.helper.MusicPlayerRemote
import io.github.muntashirakon.music.interfaces.MainActivityFragmentCallbacks
import io.github.muntashirakon.music.loaders.SongLoader
import io.github.muntashirakon.music.model.smartplaylist.HistoryPlaylist import io.github.muntashirakon.music.model.smartplaylist.HistoryPlaylist
import io.github.muntashirakon.music.model.smartplaylist.LastAddedPlaylist import io.github.muntashirakon.music.model.smartplaylist.LastAddedPlaylist
import io.github.muntashirakon.music.model.smartplaylist.MyTopTracksPlaylist import io.github.muntashirakon.music.model.smartplaylist.TopTracksPlaylist
import io.github.muntashirakon.music.repository.Repository
import io.github.muntashirakon.music.util.NavigationUtil import io.github.muntashirakon.music.util.NavigationUtil
import io.github.muntashirakon.music.util.PreferenceUtil import io.github.muntashirakon.music.util.PreferenceUtil
import com.bumptech.glide.Glide
import kotlinx.android.synthetic.main.abs_playlists.* import kotlinx.android.synthetic.main.abs_playlists.*
import kotlinx.android.synthetic.main.fragment_banner_home.* import kotlinx.android.synthetic.main.fragment_banner_home.*
import kotlinx.android.synthetic.main.home_content.* import kotlinx.android.synthetic.main.home_content.*
import kotlinx.coroutines.launch
import org.koin.android.ext.android.inject
import org.koin.androidx.viewmodel.ext.android.sharedViewModel import org.koin.androidx.viewmodel.ext.android.sharedViewModel
class BannerHomeFragment : class HomeFragment :
AbsMainActivityFragment(if (PreferenceUtil.isHomeBanner) R.layout.fragment_banner_home else R.layout.fragment_home), AbsMainActivityFragment(if (PreferenceUtil.isHomeBanner) R.layout.fragment_banner_home else R.layout.fragment_home) {
MainActivityFragmentCallbacks {
override fun handleBackPress(): Boolean {
return false
}
private val repository by inject<Repository>()
private val libraryViewModel: LibraryViewModel by sharedViewModel() private val libraryViewModel: LibraryViewModel by sharedViewModel()
private val displayMetrics: DisplayMetrics private val displayMetrics: DisplayMetrics
@ -60,9 +62,7 @@ class BannerHomeFragment :
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
setStatusBarColorAuto(view) setStatusBarColorAuto(view)
bannerImage?.setOnClickListener { bannerImage?.setOnClickListener {
val options = ActivityOptions.makeSceneTransitionAnimation( val options = ActivityOptions.makeSceneTransitionAnimation(
mainActivity, mainActivity,
@ -73,22 +73,33 @@ class BannerHomeFragment :
} }
lastAdded.setOnClickListener { lastAdded.setOnClickListener {
NavigationUtil.goToPlaylistNew(requireActivity(), LastAddedPlaylist(requireActivity())) findActivityNavController(R.id.fragment_container).navigate(
R.id.playlistDetailsFragment,
bundleOf(EXTRA_PLAYLIST to LastAddedPlaylist())
)
} }
topPlayed.setOnClickListener { topPlayed.setOnClickListener {
NavigationUtil.goToPlaylistNew( findActivityNavController(R.id.fragment_container).navigate(
requireActivity(), R.id.playlistDetailsFragment,
MyTopTracksPlaylist(requireActivity()) bundleOf(EXTRA_PLAYLIST to TopTracksPlaylist())
) )
} }
actionShuffle.setOnClickListener { actionShuffle.setOnClickListener {
MusicPlayerRemote.openAndShuffleQueue(SongLoader.getAllSongs(requireActivity()), true) lifecycleScope.launch {
MusicPlayerRemote.openAndShuffleQueue(
repository.allSongs(),
true
)
}
} }
history.setOnClickListener { history.setOnClickListener {
NavigationUtil.goToPlaylistNew(requireActivity(), HistoryPlaylist(requireActivity())) requireActivity().findNavController(R.id.fragment_container).navigate(
R.id.playlistDetailsFragment,
bundleOf(EXTRA_PLAYLIST to HistoryPlaylist())
)
} }
userImage.setOnClickListener { userImage.setOnClickListener {
@ -101,16 +112,15 @@ class BannerHomeFragment :
} }
titleWelcome?.text = String.format("%s", PreferenceUtil.userName) titleWelcome?.text = String.format("%s", PreferenceUtil.userName)
val homeAdapter = HomeAdapter(mainActivity, displayMetrics) val homeAdapter = HomeAdapter(mainActivity)
recyclerView.apply { recyclerView.apply {
layoutManager = LinearLayoutManager(mainActivity) layoutManager = LinearLayoutManager(mainActivity)
adapter = homeAdapter adapter = homeAdapter
} }
libraryViewModel.homeLiveData libraryViewModel.homeLiveData.observe(viewLifecycleOwner, Observer {
.observe(viewLifecycleOwner, Observer { sections -> homeAdapter.swapData(it)
homeAdapter.swapData(sections) })
})
loadProfile() loadProfile()
} }
@ -133,8 +143,8 @@ class BannerHomeFragment :
const val TAG: String = "BannerHomeFragment" const val TAG: String = "BannerHomeFragment"
@JvmStatic @JvmStatic
fun newInstance(): BannerHomeFragment { fun newInstance(): HomeFragment {
return BannerHomeFragment() return HomeFragment()
} }
} }
} }

View file

@ -0,0 +1,78 @@
package io.github.muntashirakon.music.fragments.library
import android.os.Bundle
import android.view.Menu
import android.view.MenuInflater
import android.view.MenuItem
import androidx.navigation.fragment.findNavController
import androidx.navigation.ui.NavigationUI
import code.name.monkey.appthemehelper.common.ATHToolbarActivity.getToolbarBackgroundColor
import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper
import com.google.android.material.appbar.AppBarLayout
import io.github.muntashirakon.music.R
import io.github.muntashirakon.music.extensions.findNavController
import io.github.muntashirakon.music.fragments.base.AbsMainActivityFragment
import kotlinx.android.synthetic.main.fragment_library.*
class LibraryFragment : AbsMainActivityFragment(R.layout.fragment_library) {
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
setHasOptionsMenu(true)
mainActivity.hideBottomBarVisibility(true)
mainActivity.setSupportActionBar(toolbar)
mainActivity.supportActionBar?.title = null
toolbar.setNavigationOnClickListener {
findNavController().navigate(
R.id.searchFragment,
null,
navOptions
)
}
setupNavigationController()
}
private fun setupNavigationController() {
val navController = findNavController(R.id.fragment_container)
NavigationUI.setupWithNavController(mainActivity.getBottomNavigationView(), navController)
}
override fun onPrepareOptionsMenu(menu: Menu) {
super.onPrepareOptionsMenu(menu)
ToolbarContentTintHelper.handleOnPrepareOptionsMenu(requireActivity(), toolbar)
}
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
super.onCreateOptionsMenu(menu, inflater)
inflater.inflate(R.menu.menu_main, menu)
ToolbarContentTintHelper.handleOnCreateOptionsMenu(
requireContext(),
toolbar,
menu,
getToolbarBackgroundColor(toolbar)
)
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
when (item.itemId) {
R.id.action_settings -> findNavController().navigate(
R.id.settingsActivity,
null,
navOptions
)
}
return super.onOptionsItemSelected(item)
}
fun addOnAppBarOffsetChangedListener(changedListener: AppBarLayout.OnOffsetChangedListener) {
appBarLayout.addOnOffsetChangedListener(changedListener)
}
fun removeOnAppBarOffsetChangedListener(changedListener: AppBarLayout.OnOffsetChangedListener) {
appBarLayout.removeOnOffsetChangedListener(changedListener)
}
fun getTotalAppBarScrollingRange(): Int {
return 0
}
}

View file

@ -4,7 +4,7 @@ import android.os.Bundle
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.navigation.NavController import androidx.navigation.NavController
import io.github.muntashirakon.music.R import io.github.muntashirakon.music.R
import io.github.muntashirakon.music.extensions.navController import io.github.muntashirakon.music.extensions.findNavController
import io.github.muntashirakon.music.fragments.NowPlayingScreen.* import io.github.muntashirakon.music.fragments.NowPlayingScreen.*
import io.github.muntashirakon.music.util.PreferenceUtil import io.github.muntashirakon.music.util.PreferenceUtil
@ -15,7 +15,7 @@ class NowPlayingPlayerFragment : Fragment(R.layout.fragment_now_playing_player)
override fun onActivityCreated(savedInstanceState: Bundle?) { override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState) super.onActivityCreated(savedInstanceState)
val navController = navController(R.id.playerFragmentContainer) val navController = findNavController(R.id.playerFragmentContainer)
updateNowPlaying(navController) updateNowPlaying(navController)
} }

View file

@ -18,6 +18,7 @@ import kotlinx.android.synthetic.main.fragment_player_album_cover.*
class PlayerAlbumCoverFragment : AbsMusicServiceFragment(R.layout.fragment_player_album_cover), class PlayerAlbumCoverFragment : AbsMusicServiceFragment(R.layout.fragment_player_album_cover),
ViewPager.OnPageChangeListener { ViewPager.OnPageChangeListener {
private var callbacks: Callbacks? = null private var callbacks: Callbacks? = null
private var currentPosition: Int = 0 private var currentPosition: Int = 0
private val colorReceiver = object : AlbumCoverFragment.ColorReceiver { private val colorReceiver = object : AlbumCoverFragment.ColorReceiver {

View file

@ -1,6 +1,5 @@
package io.github.muntashirakon.music.fragments.player.full package io.github.muntashirakon.music.fragments.player.full
import android.app.ActivityOptions
import android.content.res.ColorStateList import android.content.res.ColorStateList
import android.graphics.Color import android.graphics.Color
import android.os.Bundle import android.os.Bundle
@ -8,31 +7,36 @@ import android.view.View
import android.widget.FrameLayout import android.widget.FrameLayout
import android.widget.TextView import android.widget.TextView
import androidx.appcompat.widget.Toolbar import androidx.appcompat.widget.Toolbar
import androidx.core.os.bundleOf
import androidx.lifecycle.lifecycleScope
import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper import code.name.monkey.appthemehelper.util.ToolbarContentTintHelper
import com.bumptech.glide.Glide
import io.github.muntashirakon.music.EXTRA_ARTIST_ID
import io.github.muntashirakon.music.R import io.github.muntashirakon.music.R
import io.github.muntashirakon.music.extensions.findActivityNavController
import io.github.muntashirakon.music.extensions.hide import io.github.muntashirakon.music.extensions.hide
import io.github.muntashirakon.music.extensions.show import io.github.muntashirakon.music.extensions.show
import io.github.muntashirakon.music.extensions.whichFragment
import io.github.muntashirakon.music.fragments.base.AbsPlayerFragment import io.github.muntashirakon.music.fragments.base.AbsPlayerFragment
import io.github.muntashirakon.music.fragments.player.PlayerAlbumCoverFragment import io.github.muntashirakon.music.fragments.player.PlayerAlbumCoverFragment
import io.github.muntashirakon.music.glide.ArtistGlideRequest import io.github.muntashirakon.music.glide.ArtistGlideRequest
import io.github.muntashirakon.music.glide.RetroMusicColoredTarget import io.github.muntashirakon.music.glide.RetroMusicColoredTarget
import io.github.muntashirakon.music.helper.MusicPlayerRemote import io.github.muntashirakon.music.helper.MusicPlayerRemote
import io.github.muntashirakon.music.helper.MusicProgressViewUpdateHelper import io.github.muntashirakon.music.helper.MusicProgressViewUpdateHelper
import io.github.muntashirakon.music.loaders.ArtistLoader
import io.github.muntashirakon.music.model.Song import io.github.muntashirakon.music.model.Song
import io.github.muntashirakon.music.model.lyrics.AbsSynchronizedLyrics import io.github.muntashirakon.music.model.lyrics.AbsSynchronizedLyrics
import io.github.muntashirakon.music.model.lyrics.Lyrics import io.github.muntashirakon.music.model.lyrics.Lyrics
import io.github.muntashirakon.music.util.NavigationUtil import io.github.muntashirakon.music.repository.ArtistRepository
import io.github.muntashirakon.music.util.color.MediaNotificationProcessor import io.github.muntashirakon.music.util.color.MediaNotificationProcessor
import com.bumptech.glide.Glide
import kotlinx.android.synthetic.main.fragment_full.* import kotlinx.android.synthetic.main.fragment_full.*
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import org.koin.android.ext.android.inject
class FullPlayerFragment : AbsPlayerFragment(R.layout.fragment_full), class FullPlayerFragment : AbsPlayerFragment(R.layout.fragment_full),
MusicProgressViewUpdateHelper.Callback { MusicProgressViewUpdateHelper.Callback {
private val artistRepository by inject<ArtistRepository>()
private lateinit var lyricsLayout: FrameLayout private lateinit var lyricsLayout: FrameLayout
private lateinit var lyricsLine1: TextView private lateinit var lyricsLine1: TextView
private lateinit var lyricsLine2: TextView private lateinit var lyricsLine2: TextView
@ -150,30 +154,20 @@ class FullPlayerFragment : AbsPlayerFragment(R.layout.fragment_full),
private fun setupArtist() { private fun setupArtist() {
artistImage.setOnClickListener { artistImage.setOnClickListener {
val transitionName = mainActivity.collapsePanel()
"${getString(R.string.transition_artist_image)}_${MusicPlayerRemote.currentSong.artistId}" findActivityNavController(R.id.fragment_container)
val activityOptions = .navigate(
ActivityOptions.makeSceneTransitionAnimation( R.id.artistDetailsFragment,
requireActivity(), bundleOf(EXTRA_ARTIST_ID to MusicPlayerRemote.currentSong.artistId)
artistImage,
transitionName
) )
NavigationUtil.goToArtistOptions(
requireActivity(),
MusicPlayerRemote.currentSong.artistId,
activityOptions
)
} }
} }
private fun setUpSubFragments() { private fun setUpSubFragments() {
controlsFragment = controlsFragment = whichFragment(R.id.playbackControlsFragment)
childFragmentManager.findFragmentById(R.id.playbackControlsFragment) as FullPlaybackControlsFragment val coverFragment: PlayerAlbumCoverFragment = whichFragment(R.id.playerAlbumCoverFragment)
coverFragment.setCallbacks(this)
val playerAlbumCoverFragment = coverFragment.removeSlideEffect()
childFragmentManager.findFragmentById(R.id.playerAlbumCoverFragment) as PlayerAlbumCoverFragment
playerAlbumCoverFragment.setCallbacks(this)
playerAlbumCoverFragment.removeSlideEffect()
} }
override fun onShow() { override fun onShow() {
@ -228,9 +222,8 @@ class FullPlayerFragment : AbsPlayerFragment(R.layout.fragment_full),
} }
private fun updateArtistImage() { private fun updateArtistImage() {
CoroutineScope(Dispatchers.IO).launch { lifecycleScope.launch {
val artist = val artist = artistRepository.artist(MusicPlayerRemote.currentSong.artistId)
ArtistLoader.getArtist(requireContext(), MusicPlayerRemote.currentSong.artistId)
withContext(Dispatchers.Main) { withContext(Dispatchers.Main) {
ArtistGlideRequest.Builder.from(Glide.with(requireContext()), artist) ArtistGlideRequest.Builder.from(Glide.with(requireContext()), artist)
.generatePalette(requireContext()) .generatePalette(requireContext())

View file

@ -52,7 +52,7 @@ class PlayerPlaybackControlsFragment :
showBonceAnimation(playPauseButton) showBonceAnimation(playPauseButton)
} }
title.isSelected = true title.isSelected = true
text.isSelected = true
} }
override fun setColor(color: MediaNotificationProcessor) { override fun setColor(color: MediaNotificationProcessor) {

View file

@ -1,95 +1,81 @@
package io.github.muntashirakon.music.activities.playlist package io.github.muntashirakon.music.fragments.playlists
import android.os.Bundle import android.os.Bundle
import android.view.Menu import android.view.Menu
import android.view.MenuInflater
import android.view.MenuItem import android.view.MenuItem
import android.view.View import android.view.View
import androidx.lifecycle.Observer import androidx.lifecycle.Observer
import androidx.navigation.fragment.navArgs
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import code.name.monkey.appthemehelper.util.ATHUtil
import io.github.muntashirakon.music.R
import io.github.muntashirakon.music.activities.base.AbsSlidingMusicPanelActivity
import io.github.muntashirakon.music.adapter.song.OrderablePlaylistSongAdapter
import io.github.muntashirakon.music.adapter.song.PlaylistSongAdapter
import io.github.muntashirakon.music.adapter.song.SongAdapter
import io.github.muntashirakon.music.extensions.applyToolbar
import io.github.muntashirakon.music.extensions.extraNotNull
import io.github.muntashirakon.music.helper.menu.PlaylistMenuHelper
import io.github.muntashirakon.music.interfaces.CabHolder
import io.github.muntashirakon.music.model.AbsCustomPlaylist
import io.github.muntashirakon.music.model.Playlist
import io.github.muntashirakon.music.model.Song
import io.github.muntashirakon.music.util.DensityUtil
import io.github.muntashirakon.music.util.PlaylistsUtil
import io.github.muntashirakon.music.util.RetroColorUtil
import com.afollestad.materialcab.MaterialCab
import com.h6ah4i.android.widget.advrecyclerview.animator.RefactoredDefaultItemAnimator import com.h6ah4i.android.widget.advrecyclerview.animator.RefactoredDefaultItemAnimator
import com.h6ah4i.android.widget.advrecyclerview.draggable.RecyclerViewDragDropManager import com.h6ah4i.android.widget.advrecyclerview.draggable.RecyclerViewDragDropManager
import com.h6ah4i.android.widget.advrecyclerview.utils.WrapperAdapterUtils import com.h6ah4i.android.widget.advrecyclerview.utils.WrapperAdapterUtils
import kotlinx.android.synthetic.main.activity_playlist_detail.* import io.github.muntashirakon.music.R
import io.github.muntashirakon.music.adapter.song.OrderablePlaylistSongAdapter
import io.github.muntashirakon.music.adapter.song.SongAdapter
import io.github.muntashirakon.music.extensions.dipToPix
import io.github.muntashirakon.music.fragments.base.AbsMainActivityFragment
import io.github.muntashirakon.music.helper.menu.PlaylistMenuHelper
import io.github.muntashirakon.music.model.AbsCustomPlaylist
import io.github.muntashirakon.music.model.Playlist
import io.github.muntashirakon.music.model.Song
import io.github.muntashirakon.music.util.PlaylistsUtil
import kotlinx.android.synthetic.main.fragment_playlist_detail.*
import org.koin.androidx.viewmodel.ext.android.viewModel import org.koin.androidx.viewmodel.ext.android.viewModel
import org.koin.core.parameter.parametersOf import org.koin.core.parameter.parametersOf
class PlaylistDetailsFragment : AbsMainActivityFragment(R.layout.fragment_playlist_detail) {
class PlaylistDetailActivity : AbsSlidingMusicPanelActivity(), CabHolder { private val arguments by navArgs<PlaylistDetailsFragmentArgs>()
private val viewModel: PlaylistDetailsViewModel by viewModel { private val viewModel: PlaylistDetailsViewModel by viewModel {
parametersOf(extraNotNull<Playlist>(EXTRA_PLAYLIST).value) parametersOf(arguments.extraPlaylist)
} }
private lateinit var playlist: Playlist private lateinit var playlist: Playlist
private var cab: MaterialCab? = null
private lateinit var adapter: SongAdapter private lateinit var adapter: SongAdapter
private var wrappedAdapter: RecyclerView.Adapter<*>? = null private var wrappedAdapter: RecyclerView.Adapter<*>? = null
private var recyclerViewDragDropManager: RecyclerViewDragDropManager? = null private var recyclerViewDragDropManager: RecyclerViewDragDropManager? = null
override fun onCreate(savedInstanceState: Bundle?) { override fun onActivityCreated(savedInstanceState: Bundle?) {
setDrawUnderStatusBar() super.onActivityCreated(savedInstanceState)
super.onCreate(savedInstanceState) setHasOptionsMenu(true)
setStatusbarColorAuto() mainActivity.addMusicServiceEventListener(viewModel)
setNavigationbarColorAuto() mainActivity.setSupportActionBar(toolbar)
setTaskDescriptionColorAuto() mainActivity.hideBottomBarVisibility(false)
setLightNavigationBar(true)
setBottomBarVisibility(View.GONE)
playlist = extraNotNull<Playlist>(EXTRA_PLAYLIST).value playlist = arguments.extraPlaylist
setUpToolBar()
setUpRecyclerView() setUpRecyclerView()
viewModel.getSongs().observe(this, Observer { viewModel.getSongs().observe(viewLifecycleOwner, Observer {
songs(it) songs(it)
}) })
viewModel.getPlaylist().observe(this, Observer { viewModel.getPlaylist().observe(viewLifecycleOwner, Observer {
playlist = it playlist = it
supportActionBar?.title = it.name toolbar.title = it.name
}) })
addMusicServiceEventListener(viewModel)
}
override fun createContentView(): View {
return wrapSlidingMusicPanel(R.layout.activity_playlist_detail)
} }
private fun setUpRecyclerView() { private fun setUpRecyclerView() {
recyclerView.layoutManager = LinearLayoutManager(this) recyclerView.layoutManager = LinearLayoutManager(requireContext())
if (playlist is AbsCustomPlaylist) { if (playlist is AbsCustomPlaylist) {
adapter = PlaylistSongAdapter(this, ArrayList(), R.layout.item_list, this) adapter = SongAdapter(requireActivity(), ArrayList(), R.layout.item_list, null)
recyclerView.adapter = adapter recyclerView.adapter = adapter
} else { } else {
recyclerViewDragDropManager = RecyclerViewDragDropManager() recyclerViewDragDropManager = RecyclerViewDragDropManager()
val animator = RefactoredDefaultItemAnimator() val animator = RefactoredDefaultItemAnimator()
adapter = OrderablePlaylistSongAdapter(this, adapter = OrderablePlaylistSongAdapter(
requireActivity(),
ArrayList(), ArrayList(),
R.layout.item_list, R.layout.item_list,
this, null,
object : OrderablePlaylistSongAdapter.OnMoveItemListener { object : OrderablePlaylistSongAdapter.OnMoveItemListener {
override fun onMoveItem(fromPosition: Int, toPosition: Int) { override fun onMoveItem(fromPosition: Int, toPosition: Int) {
if (PlaylistsUtil.moveItem( if (PlaylistsUtil.moveItem(
this@PlaylistDetailActivity, requireContext(),
playlist.id, playlist.id,
fromPosition, fromPosition,
toPosition toPosition
@ -116,58 +102,21 @@ class PlaylistDetailActivity : AbsSlidingMusicPanelActivity(), CabHolder {
}) })
} }
private fun setUpToolBar() { override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
applyToolbar(toolbar) super.onCreateOptionsMenu(menu, inflater)
title = playlist.name val menuRes = if (playlist is AbsCustomPlaylist)
} R.menu.menu_smart_playlist_detail
else R.menu.menu_playlist_detail
override fun onCreateOptionsMenu(menu: Menu): Boolean { inflater.inflate(menuRes, menu)
menuInflater.inflate(
if (playlist is AbsCustomPlaylist) R.menu.menu_smart_playlist_detail
else R.menu.menu_playlist_detail, menu
)
return super.onCreateOptionsMenu(menu)
} }
override fun onOptionsItemSelected(item: MenuItem): Boolean { override fun onOptionsItemSelected(item: MenuItem): Boolean {
when (item.itemId) { return PlaylistMenuHelper.handleMenuClick(requireActivity(), playlist, item)
android.R.id.home -> {
onBackPressed()
return true
}
}
return PlaylistMenuHelper.handleMenuClick(this, playlist, item)
}
override fun openCab(menuRes: Int, callback: MaterialCab.Callback): MaterialCab {
if (cab != null && cab!!.isActive) {
cab!!.finish()
}
cab = MaterialCab(this, R.id.cab_stub).setMenu(menuRes)
.setCloseDrawableRes(R.drawable.ic_close)
.setBackgroundColor(
RetroColorUtil.shiftBackgroundColorForLightText(
ATHUtil.resolveColor(
this,
R.attr.colorSurface
)
)
).start(callback)
return cab!!
}
override fun onBackPressed() {
if (cab != null && cab!!.isActive) {
cab!!.finish()
} else {
recyclerView!!.stopScroll()
super.onBackPressed()
}
} }
private fun checkForPadding() { private fun checkForPadding() {
val height = DensityUtil.dip2px(this, 52f) val height = dipToPix(52f)
recyclerView.setPadding(0, 0, 0, (height)) recyclerView.setPadding(0, 0, 0, height.toInt())
} }
private fun checkIsEmpty() { private fun checkIsEmpty() {
@ -181,7 +130,7 @@ class PlaylistDetailActivity : AbsSlidingMusicPanelActivity(), CabHolder {
return String(Character.toChars(unicode)) return String(Character.toChars(unicode))
} }
public override fun onPause() { override fun onPause() {
if (recyclerViewDragDropManager != null) { if (recyclerViewDragDropManager != null) {
recyclerViewDragDropManager!!.cancelDrag() recyclerViewDragDropManager!!.cancelDrag()
} }
@ -206,7 +155,7 @@ class PlaylistDetailActivity : AbsSlidingMusicPanelActivity(), CabHolder {
super.onDestroy() super.onDestroy()
} }
fun showEmptyView() { private fun showEmptyView() {
empty.visibility = View.VISIBLE empty.visibility = View.VISIBLE
emptyText.visibility = View.VISIBLE emptyText.visibility = View.VISIBLE
} }
@ -219,7 +168,4 @@ class PlaylistDetailActivity : AbsSlidingMusicPanelActivity(), CabHolder {
} }
} }
companion object {
var EXTRA_PLAYLIST = "extra_playlist"
}
} }

View file

@ -1,4 +1,4 @@
package io.github.muntashirakon.music.activities.playlist package io.github.muntashirakon.music.fragments.playlists
import androidx.lifecycle.LiveData import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData import androidx.lifecycle.MutableLiveData
@ -6,22 +6,20 @@ import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import io.github.muntashirakon.music.App import io.github.muntashirakon.music.App
import io.github.muntashirakon.music.interfaces.MusicServiceEventListener import io.github.muntashirakon.music.interfaces.MusicServiceEventListener
import io.github.muntashirakon.music.loaders.PlaylistLoader
import io.github.muntashirakon.music.model.AbsCustomPlaylist import io.github.muntashirakon.music.model.AbsCustomPlaylist
import io.github.muntashirakon.music.model.Playlist import io.github.muntashirakon.music.model.Playlist
import io.github.muntashirakon.music.model.Song import io.github.muntashirakon.music.model.Song
import io.github.muntashirakon.music.providers.RepositoryImpl import io.github.muntashirakon.music.repository.RealRepository
import io.github.muntashirakon.music.util.PlaylistsUtil import io.github.muntashirakon.music.util.PlaylistsUtil
import kotlinx.coroutines.Dispatchers.Main import kotlinx.coroutines.Dispatchers.Main
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
class PlaylistDetailsViewModel( class PlaylistDetailsViewModel(
private val repository: RepositoryImpl, private val realRepository: RealRepository,
private var playlist: Playlist private var playlist: Playlist
) : ViewModel(), MusicServiceEventListener { ) : ViewModel(), MusicServiceEventListener {
private val _playListSongs = MutableLiveData<List<Song>>() private val _playListSongs = MutableLiveData<List<Song>>()
private val _playlist = MutableLiveData<Playlist>().apply { private val _playlist = MutableLiveData<Playlist>().apply {
postValue(playlist) postValue(playlist)
} }
@ -35,7 +33,7 @@ class PlaylistDetailsViewModel(
} }
private fun loadPlaylistSongs(playlist: Playlist) = viewModelScope.launch { private fun loadPlaylistSongs(playlist: Playlist) = viewModelScope.launch {
val songs = repository.getPlaylistSongs(playlist) val songs = realRepository.getPlaylistSongs(playlist)
withContext(Main) { _playListSongs.postValue(songs) } withContext(Main) { _playListSongs.postValue(songs) }
} }
@ -50,8 +48,10 @@ class PlaylistDetailsViewModel(
val playlistName = val playlistName =
PlaylistsUtil.getNameForPlaylist(App.getContext(), playlist.id.toLong()) PlaylistsUtil.getNameForPlaylist(App.getContext(), playlist.id.toLong())
if (playlistName != playlist.name) { if (playlistName != playlist.name) {
playlist = PlaylistLoader.getPlaylist(App.getContext(), playlist.id) viewModelScope.launch {
_playlist.postValue(playlist) playlist = realRepository.playlist(playlist.id)
_playlist.postValue(playlist)
}
} }
} }
loadPlaylistSongs(playlist) loadPlaylistSongs(playlist)

View file

@ -1,8 +1,6 @@
package io.github.muntashirakon.music.fragments.playlists package io.github.muntashirakon.music.fragments.playlists
import android.os.Bundle import android.os.Bundle
import android.view.Menu
import android.view.MenuInflater
import android.view.View import android.view.View
import androidx.lifecycle.Observer import androidx.lifecycle.Observer
import androidx.recyclerview.widget.GridLayoutManager import androidx.recyclerview.widget.GridLayoutManager
@ -12,7 +10,7 @@ import io.github.muntashirakon.music.fragments.base.AbsRecyclerViewFragment
import io.github.muntashirakon.music.interfaces.MainActivityFragmentCallbacks import io.github.muntashirakon.music.interfaces.MainActivityFragmentCallbacks
class PlaylistsFragment : class PlaylistsFragment :
AbsRecyclerViewFragment<PlaylistAdapter, GridLayoutManager>() , AbsRecyclerViewFragment<PlaylistAdapter, GridLayoutManager>(),
MainActivityFragmentCallbacks { MainActivityFragmentCallbacks {
override fun handleBackPress(): Boolean { override fun handleBackPress(): Boolean {
@ -21,12 +19,11 @@ class PlaylistsFragment :
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
libraryViewModel.playlisitsLiveData.observe(viewLifecycleOwner, Observer { playlists -> libraryViewModel.playlisitsLiveData.observe(viewLifecycleOwner, Observer {
if (playlists.isNotEmpty()) { if (it.isNotEmpty())
adapter?.swapDataSet(playlists) adapter?.swapDataSet(it)
} else { else
adapter?.swapDataSet(listOf()) adapter?.swapDataSet(listOf())
}
}) })
} }
@ -39,26 +36,14 @@ class PlaylistsFragment :
override fun createAdapter(): PlaylistAdapter { override fun createAdapter(): PlaylistAdapter {
return PlaylistAdapter( return PlaylistAdapter(
mainActivity, requireActivity(),
ArrayList(), ArrayList(),
R.layout.item_list, R.layout.item_list,
mainActivity null
) )
} }
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
super.onCreateOptionsMenu(menu, inflater)
menu.apply {
removeItem(R.id.action_sort_order)
removeItem(R.id.action_grid_size)
}
}
companion object { companion object {
@JvmField
val TAG: String = PlaylistsFragment::class.java.simpleName
@JvmStatic
fun newInstance(): PlaylistsFragment { fun newInstance(): PlaylistsFragment {
return PlaylistsFragment() return PlaylistsFragment()
} }

View file

@ -0,0 +1,143 @@
package io.github.muntashirakon.music.fragments.search
import android.content.ActivityNotFoundException
import android.content.Intent
import android.os.Bundle
import android.speech.RecognizerIntent
import android.text.Editable
import android.text.TextWatcher
import android.view.View
import android.view.inputmethod.InputMethodManager
import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.ContextCompat.getSystemService
import androidx.core.view.isGone
import androidx.core.view.isVisible
import androidx.lifecycle.Observer
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import androidx.transition.TransitionManager
import com.google.android.material.textfield.TextInputEditText
import io.github.muntashirakon.music.R
import io.github.muntashirakon.music.adapter.SearchAdapter
import io.github.muntashirakon.music.extensions.accentColor
import io.github.muntashirakon.music.extensions.showToast
import io.github.muntashirakon.music.fragments.base.AbsMainActivityFragment
import kotlinx.android.synthetic.main.fragment_search.*
import org.koin.android.ext.android.inject
import java.util.*
import kotlin.collections.ArrayList
class SearchFragment : AbsMainActivityFragment(R.layout.fragment_search), TextWatcher {
companion object {
const val QUERY = "query"
const val REQ_CODE_SPEECH_INPUT = 9001
}
private val viewModel: SearchViewModel by inject()
private lateinit var searchAdapter: SearchAdapter
private var query: String? = null
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
mainActivity.setSupportActionBar(toolbar)
mainActivity.hideBottomBarVisibility(false)
setupRecyclerView()
keyboardPopup.accentColor()
searchView.addTextChangedListener(this)
voiceSearch.setOnClickListener { startMicSearch() }
clearText.setOnClickListener { searchView.clearText() }
keyboardPopup.setOnClickListener {
val inputManager =
getSystemService<InputMethodManager>(
requireContext(),
InputMethodManager::class.java
)
inputManager?.showSoftInput(searchView, InputMethodManager.SHOW_IMPLICIT)
}
if (savedInstanceState != null) {
query = savedInstanceState.getString(QUERY)
}
viewModel.getSearchResult().observe(viewLifecycleOwner, Observer {
showData(it)
})
}
private fun showData(data: MutableList<Any>) {
if (data.isNotEmpty()) {
searchAdapter.swapDataSet(data)
} else {
searchAdapter.swapDataSet(ArrayList())
}
}
private fun setupRecyclerView() {
searchAdapter = SearchAdapter(requireActivity() as AppCompatActivity, emptyList())
searchAdapter.registerAdapterDataObserver(object : RecyclerView.AdapterDataObserver() {
override fun onChanged() {
super.onChanged()
empty.isVisible = searchAdapter.itemCount < 1
}
})
recyclerView.apply {
layoutManager = LinearLayoutManager(requireContext())
adapter = searchAdapter
addOnScrollListener(object : RecyclerView.OnScrollListener() {
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
super.onScrolled(recyclerView, dx, dy)
if (dy > 0) {
keyboardPopup.shrink()
} else if (dy < 0) {
keyboardPopup.extend()
}
}
})
}
}
override fun afterTextChanged(newText: Editable?) {
search(newText.toString())
}
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {
}
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
}
private fun search(query: String) {
this.query = query
TransitionManager.beginDelayedTransition(appBarLayout)
voiceSearch.isGone = query.isNotEmpty()
clearText.isVisible = query.isNotEmpty()
viewModel.search(query)
}
private fun startMicSearch() {
val intent = Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH)
intent.putExtra(
RecognizerIntent.EXTRA_LANGUAGE_MODEL,
RecognizerIntent.LANGUAGE_MODEL_FREE_FORM
)
intent.putExtra(RecognizerIntent.EXTRA_LANGUAGE, Locale.getDefault())
intent.putExtra(RecognizerIntent.EXTRA_PROMPT, getString(R.string.speech_prompt))
try {
startActivityForResult(
intent,
REQ_CODE_SPEECH_INPUT
)
} catch (e: ActivityNotFoundException) {
e.printStackTrace()
showToast(getString(R.string.speech_not_supported))
}
}
}
fun TextInputEditText.clearText() {
text = null
}

View file

@ -1,22 +1,22 @@
package io.github.muntashirakon.music.activities.search package io.github.muntashirakon.music.fragments.search
import androidx.lifecycle.LiveData import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import io.github.muntashirakon.music.providers.RepositoryImpl import io.github.muntashirakon.music.repository.RealRepository
import kotlinx.coroutines.Dispatchers.IO import kotlinx.coroutines.Dispatchers.IO
import kotlinx.coroutines.Dispatchers.Main import kotlinx.coroutines.Dispatchers.Main
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
class SearchViewModel(private val repository: RepositoryImpl) : ViewModel() { class SearchViewModel(private val realRepository: RealRepository) : ViewModel() {
private val results = MutableLiveData<MutableList<Any>>() private val results = MutableLiveData<MutableList<Any>>()
fun getSearchResult(): LiveData<MutableList<Any>> = results fun getSearchResult(): LiveData<MutableList<Any>> = results
fun search(query: String?) = viewModelScope.launch(IO) { fun search(query: String?) = viewModelScope.launch(IO) {
val result = repository.search(query) val result = realRepository.search(query)
withContext(Main) { results.postValue(result) } withContext(Main) { results.postValue(result) }
} }
} }

View file

@ -14,17 +14,13 @@
package io.github.muntashirakon.music.fragments.settings package io.github.muntashirakon.music.fragments.settings
import android.content.res.ColorStateList
import android.os.Bundle import android.os.Bundle
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import androidx.annotation.StringRes
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.navigation.fragment.findNavController import androidx.navigation.fragment.findNavController
import io.github.muntashirakon.music.R import io.github.muntashirakon.music.R
import io.github.muntashirakon.music.activities.SettingsActivity
import io.github.muntashirakon.music.util.NavigationUtil
import kotlinx.android.synthetic.main.fragment_main_settings.* import kotlinx.android.synthetic.main.fragment_main_settings.*
class MainSettingsFragment : Fragment(), View.OnClickListener { class MainSettingsFragment : Fragment(), View.OnClickListener {
@ -60,12 +56,4 @@ class MainSettingsFragment : Fragment(), View.OnClickListener {
otherSettings.setOnClickListener(this) otherSettings.setOnClickListener(this)
aboutSettings.setOnClickListener(this) aboutSettings.setOnClickListener(this)
} }
companion object {
}
private fun inflateFragment(fragment: Fragment, @StringRes title: Int) {
(requireActivity() as SettingsActivity).setupFragment(fragment, title)
}
} }

View file

@ -1,12 +1,11 @@
package io.github.muntashirakon.music.fragments.songs package io.github.muntashirakon.music.fragments.songs
import android.os.Bundle import android.os.Bundle
import android.view.* import android.view.View
import androidx.annotation.LayoutRes import androidx.annotation.LayoutRes
import androidx.lifecycle.Observer import androidx.lifecycle.Observer
import androidx.recyclerview.widget.GridLayoutManager import androidx.recyclerview.widget.GridLayoutManager
import io.github.muntashirakon.music.R import io.github.muntashirakon.music.R
import io.github.muntashirakon.music.adapter.song.ShuffleButtonSongAdapter
import io.github.muntashirakon.music.adapter.song.SongAdapter import io.github.muntashirakon.music.adapter.song.SongAdapter
import io.github.muntashirakon.music.fragments.ReloadType import io.github.muntashirakon.music.fragments.ReloadType
import io.github.muntashirakon.music.fragments.base.AbsRecyclerViewCustomGridSizeFragment import io.github.muntashirakon.music.fragments.base.AbsRecyclerViewCustomGridSizeFragment
@ -24,11 +23,10 @@ class SongsFragment :
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
libraryViewModel.songsLiveData.observe(viewLifecycleOwner, Observer { libraryViewModel.songsLiveData.observe(viewLifecycleOwner, Observer {
if (it.isNotEmpty()) { if (it.isNotEmpty())
adapter?.swapDataSet(it) adapter?.swapDataSet(it)
} else { else
adapter?.swapDataSet(listOf()) adapter?.swapDataSet(listOf())
}
}) })
} }
@ -36,26 +34,16 @@ class SongsFragment :
get() = R.string.no_songs get() = R.string.no_songs
override fun createLayoutManager(): GridLayoutManager { override fun createLayoutManager(): GridLayoutManager {
return GridLayoutManager(requireActivity(), getGridSize()).apply { return GridLayoutManager(requireActivity(), getGridSize())
spanSizeLookup = object : GridLayoutManager.SpanSizeLookup() {
override fun getSpanSize(position: Int): Int {
return if (position == 0) {
getGridSize()
} else {
1
}
}
}
}
} }
override fun createAdapter(): SongAdapter { override fun createAdapter(): SongAdapter {
val dataSet = if (adapter == null) mutableListOf() else adapter!!.dataSet val dataSet = if (adapter == null) mutableListOf() else adapter!!.dataSet
return ShuffleButtonSongAdapter( return SongAdapter(
mainActivity, requireActivity(),
dataSet, dataSet,
itemLayoutRes(), R.layout.item_list,
mainActivity null
) )
} }
@ -109,72 +97,4 @@ class SongsFragment :
return SongsFragment() return SongsFragment()
} }
} }
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
super.onCreateOptionsMenu(menu, inflater)
setUpGridSizeMenu(menu.findItem(R.id.action_grid_size).subMenu)
}
private fun setUpGridSizeMenu(
gridSizeMenu: SubMenu
) {
println(getGridSize())
when (getGridSize()) {
1 -> gridSizeMenu.findItem(R.id.action_grid_size_1).isChecked = true
2 -> gridSizeMenu.findItem(R.id.action_grid_size_2).isChecked = true
3 -> gridSizeMenu.findItem(R.id.action_grid_size_3).isChecked = true
4 -> gridSizeMenu.findItem(R.id.action_grid_size_4).isChecked = true
5 -> gridSizeMenu.findItem(R.id.action_grid_size_5).isChecked = true
6 -> gridSizeMenu.findItem(R.id.action_grid_size_6).isChecked = true
7 -> gridSizeMenu.findItem(R.id.action_grid_size_7).isChecked = true
8 -> gridSizeMenu.findItem(R.id.action_grid_size_8).isChecked = true
}
val maxGridSize = maxGridSize
if (maxGridSize < 8) {
gridSizeMenu.findItem(R.id.action_grid_size_8).isVisible = false
}
if (maxGridSize < 7) {
gridSizeMenu.findItem(R.id.action_grid_size_7).isVisible = false
}
if (maxGridSize < 6) {
gridSizeMenu.findItem(R.id.action_grid_size_6).isVisible = false
}
if (maxGridSize < 5) {
gridSizeMenu.findItem(R.id.action_grid_size_5).isVisible = false
}
if (maxGridSize < 4) {
gridSizeMenu.findItem(R.id.action_grid_size_4).isVisible = false
}
if (maxGridSize < 3) {
gridSizeMenu.findItem(R.id.action_grid_size_3).isVisible = false
}
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
if (handleGridSizeMenuItem(item)) return true
return super.onOptionsItemSelected(item)
}
fun handleGridSizeMenuItem(
item: MenuItem
): Boolean {
var gridSize = 0
when (item.itemId) {
R.id.action_grid_size_1 -> gridSize = 1
R.id.action_grid_size_2 -> gridSize = 2
R.id.action_grid_size_3 -> gridSize = 3
R.id.action_grid_size_4 -> gridSize = 4
R.id.action_grid_size_5 -> gridSize = 5
R.id.action_grid_size_6 -> gridSize = 6
R.id.action_grid_size_7 -> gridSize = 7
R.id.action_grid_size_8 -> gridSize = 8
}
if (gridSize > 0) {
item.isChecked = true
setAndSaveGridSize(gridSize)
return true
}
return false
}
} }

View file

@ -34,7 +34,7 @@ public class AlbumGlideRequest {
if (ignoreMediaStore) { if (ignoreMediaStore) {
return requestManager.load(new AudioFileCover(song.getData())); return requestManager.load(new AudioFileCover(song.getData()));
} else { } else {
return requestManager.loadFromMediaStore(MusicUtil.getMediaStoreAlbumCoverUri(song.getAlbumId())); return requestManager.loadFromMediaStore(MusicUtil.INSTANCE.getMediaStoreAlbumCoverUri(song.getAlbumId()));
} }
} }

View file

@ -52,7 +52,7 @@ public class SongGlideRequest {
if (ignoreMediaStore) { if (ignoreMediaStore) {
return requestManager.load(new AudioFileCover(song.getData())); return requestManager.load(new AudioFileCover(song.getData()));
} else { } else {
return requestManager.loadFromMediaStore(MusicUtil.getMediaStoreAlbumCoverUri(song.getAlbumId())); return requestManager.loadFromMediaStore(MusicUtil.INSTANCE.getMediaStoreAlbumCoverUri(song.getAlbumId()));
} }
} }

View file

@ -30,7 +30,7 @@ object M3UWriter : M3UConstants {
): File? { ): File? {
if (!dir.exists()) dir.mkdirs() if (!dir.exists()) dir.mkdirs()
val file = File(dir, playlist.name + "." + M3UConstants.EXTENSION) val file = File(dir, playlist.name + "." + M3UConstants.EXTENSION)
val songs = playlist.getSongs(context) val songs = playlist.getSongs()
if (songs.size > 0) { if (songs.size > 0) {
val bw = BufferedWriter(FileWriter(file)) val bw = BufferedWriter(FileWriter(file))
bw.write(M3UConstants.HEADER) bw.write(M3UConstants.HEADER)

View file

@ -23,23 +23,26 @@ import android.os.Build
import android.os.Environment import android.os.Environment
import android.os.IBinder import android.os.IBinder
import android.provider.DocumentsContract import android.provider.DocumentsContract
import android.provider.MediaStore
import android.widget.Toast import android.widget.Toast
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import io.github.muntashirakon.music.R import io.github.muntashirakon.music.R
import io.github.muntashirakon.music.loaders.SongLoader
import io.github.muntashirakon.music.model.Song import io.github.muntashirakon.music.model.Song
import io.github.muntashirakon.music.repository.SongRepository
import io.github.muntashirakon.music.service.MusicService import io.github.muntashirakon.music.service.MusicService
import io.github.muntashirakon.music.util.PreferenceUtil import io.github.muntashirakon.music.util.PreferenceUtil
import org.koin.core.KoinComponent
import org.koin.core.inject
import java.io.File import java.io.File
import java.util.* import java.util.*
object MusicPlayerRemote { object MusicPlayerRemote : KoinComponent {
val TAG: String = MusicPlayerRemote::class.java.simpleName val TAG: String = MusicPlayerRemote::class.java.simpleName
private val mConnectionMap = WeakHashMap<Context, ServiceBinder>() private val mConnectionMap = WeakHashMap<Context, ServiceBinder>()
var musicService: MusicService? = null var musicService: MusicService? = null
private val songRepository by inject<SongRepository>()
@JvmStatic @JvmStatic
val isPlaying: Boolean val isPlaying: Boolean
get() = musicService != null && musicService!!.isPlaying get() = musicService != null && musicService!!.isPlaying
@ -413,24 +416,17 @@ object MusicPlayerRemote {
songId = uri.lastPathSegment songId = uri.lastPathSegment
} }
if (songId != null) { if (songId != null) {
songs = SongLoader.getSongs( songs = songRepository.songs(songId)
SongLoader.makeSongCursor(
musicService!!,
MediaStore.Audio.AudioColumns._ID + "=?",
arrayOf(songId)
)
)
} }
} }
} }
if (songs == null) { if (songs == null) {
var songFile: File? = null var songFile: File? = null
if (uri.authority != null && uri.authority == "com.android.externalstorage.documents") { if (uri.authority != null && uri.authority == "com.android.externalstorage.documents") {
songFile = songFile = File(
File( Environment.getExternalStorageDirectory(),
Environment.getExternalStorageDirectory(), uri.path?.split(":".toRegex(), 2)?.get(1)
uri.path?.split(":".toRegex(), 2)?.get(1) )
)
} }
if (songFile == null) { if (songFile == null) {
val path = getFilePathFromUri(musicService!!, uri) val path = getFilePathFromUri(musicService!!, uri)
@ -441,13 +437,7 @@ object MusicPlayerRemote {
songFile = File(uri.path) songFile = File(uri.path)
} }
if (songFile != null) { if (songFile != null) {
songs = SongLoader.getSongs( songs = songRepository.songsByFilePath(songFile.absolutePath)
SongLoader.makeSongCursor(
musicService!!,
MediaStore.Audio.AudioColumns.DATA + "=?",
arrayOf(songFile.absolutePath)
)
)
} }
} }
if (songs != null && songs.isNotEmpty()) { if (songs != null && songs.isNotEmpty()) {

View file

@ -18,29 +18,31 @@ import android.app.SearchManager
import android.content.Context import android.content.Context
import android.os.Bundle import android.os.Bundle
import android.provider.MediaStore import android.provider.MediaStore
import io.github.muntashirakon.music.loaders.SongLoader
import io.github.muntashirakon.music.model.Song import io.github.muntashirakon.music.model.Song
import io.github.muntashirakon.music.repository.RealSongRepository
import org.koin.core.KoinComponent
import org.koin.core.inject
import java.util.* import java.util.*
object SearchQueryHelper { object SearchQueryHelper : KoinComponent {
private const val TITLE_SELECTION = "lower(" + MediaStore.Audio.AudioColumns.TITLE + ") = ?" private const val TITLE_SELECTION = "lower(" + MediaStore.Audio.AudioColumns.TITLE + ") = ?"
private const val ALBUM_SELECTION = "lower(" + MediaStore.Audio.AudioColumns.ALBUM + ") = ?" private const val ALBUM_SELECTION = "lower(" + MediaStore.Audio.AudioColumns.ALBUM + ") = ?"
private const val ARTIST_SELECTION = "lower(" + MediaStore.Audio.AudioColumns.ARTIST + ") = ?" private const val ARTIST_SELECTION = "lower(" + MediaStore.Audio.AudioColumns.ARTIST + ") = ?"
private const val AND = " AND " private const val AND = " AND "
private val songRepository by inject<RealSongRepository>()
var songs = ArrayList<Song>() var songs = ArrayList<Song>()
@JvmStatic @JvmStatic
fun getSongs(context: Context, extras: Bundle): ArrayList<Song> { fun getSongs(context: Context, extras: Bundle): List<Song> {
val query = extras.getString(SearchManager.QUERY, null) val query = extras.getString(SearchManager.QUERY, null)
val artistName = extras.getString(MediaStore.EXTRA_MEDIA_ARTIST, null) val artistName = extras.getString(MediaStore.EXTRA_MEDIA_ARTIST, null)
val albumName = extras.getString(MediaStore.EXTRA_MEDIA_ALBUM, null) val albumName = extras.getString(MediaStore.EXTRA_MEDIA_ALBUM, null)
val titleName = extras.getString(MediaStore.EXTRA_MEDIA_TITLE, null) val titleName = extras.getString(MediaStore.EXTRA_MEDIA_TITLE, null)
var songs = ArrayList<Song>() var songs = listOf<Song>()
if (artistName != null && albumName != null && titleName != null) { if (artistName != null && albumName != null && titleName != null) {
songs = SongLoader.getSongs( songs = songRepository.songs(
SongLoader.makeSongCursor( songRepository.makeSongCursor(
context,
ARTIST_SELECTION + AND + ALBUM_SELECTION + AND + TITLE_SELECTION, ARTIST_SELECTION + AND + ALBUM_SELECTION + AND + TITLE_SELECTION,
arrayOf( arrayOf(
artistName.toLowerCase(), artistName.toLowerCase(),
@ -54,9 +56,8 @@ object SearchQueryHelper {
return songs return songs
} }
if (artistName != null && titleName != null) { if (artistName != null && titleName != null) {
songs = SongLoader.getSongs( songs = songRepository.songs(
SongLoader.makeSongCursor( songRepository.makeSongCursor(
context,
ARTIST_SELECTION + AND + TITLE_SELECTION, ARTIST_SELECTION + AND + TITLE_SELECTION,
arrayOf(artistName.toLowerCase(), titleName.toLowerCase()) arrayOf(artistName.toLowerCase(), titleName.toLowerCase())
) )
@ -66,9 +67,8 @@ object SearchQueryHelper {
return songs return songs
} }
if (albumName != null && titleName != null) { if (albumName != null && titleName != null) {
songs = SongLoader.getSongs( songs = songRepository.songs(
SongLoader.makeSongCursor( songRepository.makeSongCursor(
context,
ALBUM_SELECTION + AND + TITLE_SELECTION, ALBUM_SELECTION + AND + TITLE_SELECTION,
arrayOf(albumName.toLowerCase(), titleName.toLowerCase()) arrayOf(albumName.toLowerCase(), titleName.toLowerCase())
) )
@ -78,9 +78,8 @@ object SearchQueryHelper {
return songs return songs
} }
if (artistName != null) { if (artistName != null) {
songs = SongLoader.getSongs( songs = songRepository.songs(
SongLoader.makeSongCursor( songRepository.makeSongCursor(
context,
ARTIST_SELECTION, ARTIST_SELECTION,
arrayOf(artistName.toLowerCase()) arrayOf(artistName.toLowerCase())
) )
@ -90,9 +89,8 @@ object SearchQueryHelper {
return songs return songs
} }
if (albumName != null) { if (albumName != null) {
songs = SongLoader.getSongs( songs = songRepository.songs(
SongLoader.makeSongCursor( songRepository.makeSongCursor(
context,
ALBUM_SELECTION, ALBUM_SELECTION,
arrayOf(albumName.toLowerCase()) arrayOf(albumName.toLowerCase())
) )
@ -102,9 +100,8 @@ object SearchQueryHelper {
return songs return songs
} }
if (titleName != null) { if (titleName != null) {
songs = SongLoader.getSongs( songs = songRepository.songs(
SongLoader.makeSongCursor( songRepository.makeSongCursor(
context,
TITLE_SELECTION, TITLE_SELECTION,
arrayOf(titleName.toLowerCase()) arrayOf(titleName.toLowerCase())
) )
@ -113,21 +110,18 @@ object SearchQueryHelper {
if (songs.isNotEmpty()) { if (songs.isNotEmpty()) {
return songs return songs
} }
songs = songs = songRepository.songs(
SongLoader.getSongs( songRepository.makeSongCursor(
SongLoader.makeSongCursor( ARTIST_SELECTION,
context, arrayOf(query.toLowerCase())
ARTIST_SELECTION,
arrayOf(query.toLowerCase())
)
) )
)
if (songs.isNotEmpty()) { if (songs.isNotEmpty()) {
return songs return songs
} }
songs = SongLoader.getSongs( songs = songRepository.songs(
SongLoader.makeSongCursor( songRepository.makeSongCursor(
context,
ALBUM_SELECTION, ALBUM_SELECTION,
arrayOf(query.toLowerCase()) arrayOf(query.toLowerCase())
) )
@ -135,9 +129,8 @@ object SearchQueryHelper {
if (songs.isNotEmpty()) { if (songs.isNotEmpty()) {
return songs return songs
} }
songs = SongLoader.getSongs( songs = songRepository.songs(
SongLoader.makeSongCursor( songRepository.makeSongCursor(
context,
TITLE_SELECTION, TITLE_SELECTION,
arrayOf(query.toLowerCase()) arrayOf(query.toLowerCase())
) )

View file

@ -14,43 +14,43 @@
package io.github.muntashirakon.music.helper.menu package io.github.muntashirakon.music.helper.menu
import android.app.Activity
import android.view.MenuItem import android.view.MenuItem
import androidx.appcompat.app.AppCompatActivity import androidx.fragment.app.FragmentActivity
import io.github.muntashirakon.music.R import io.github.muntashirakon.music.R
import io.github.muntashirakon.music.dialogs.AddToPlaylistDialog import io.github.muntashirakon.music.dialogs.AddToPlaylistDialog
import io.github.muntashirakon.music.helper.MusicPlayerRemote import io.github.muntashirakon.music.helper.MusicPlayerRemote
import io.github.muntashirakon.music.loaders.GenreLoader
import io.github.muntashirakon.music.model.Genre import io.github.muntashirakon.music.model.Genre
import io.github.muntashirakon.music.model.Song import io.github.muntashirakon.music.model.Song
import java.util.* import io.github.muntashirakon.music.repository.GenreRepository
import org.koin.core.KoinComponent
import org.koin.core.inject
object GenreMenuHelper { object GenreMenuHelper : KoinComponent {
fun handleMenuClick(activity: AppCompatActivity, genre: Genre, item: MenuItem): Boolean { private val genreRepository by inject<GenreRepository>()
fun handleMenuClick(activity: FragmentActivity, genre: Genre, item: MenuItem): Boolean {
when (item.itemId) { when (item.itemId) {
R.id.action_play -> { R.id.action_play -> {
MusicPlayerRemote.openQueue(getGenreSongs(activity, genre), 0, true) MusicPlayerRemote.openQueue(getGenreSongs(genre), 0, true)
return true return true
} }
R.id.action_play_next -> { R.id.action_play_next -> {
MusicPlayerRemote.playNext(getGenreSongs(activity, genre)) MusicPlayerRemote.playNext(getGenreSongs(genre))
return true return true
} }
R.id.action_add_to_playlist -> { R.id.action_add_to_playlist -> {
AddToPlaylistDialog.create(getGenreSongs(activity, genre)) AddToPlaylistDialog.create(getGenreSongs(genre))
.show(activity.supportFragmentManager, "ADD_PLAYLIST") .show(activity.supportFragmentManager, "ADD_PLAYLIST")
return true return true
} }
R.id.action_add_to_current_playing -> { R.id.action_add_to_current_playing -> {
MusicPlayerRemote.enqueue(getGenreSongs(activity, genre)) MusicPlayerRemote.enqueue(getGenreSongs(genre))
return true return true
} }
} }
return false return false
} }
private fun getGenreSongs(activity: Activity, genre: Genre): ArrayList<Song> { private fun getGenreSongs(genre: Genre): List<Song> {
return GenreLoader.getSongs(activity, genre.id) return genreRepository.songs(genre.id)
} }
} }

View file

@ -19,26 +19,24 @@ import android.app.Activity
import android.content.Context import android.content.Context
import android.view.MenuItem import android.view.MenuItem
import android.widget.Toast import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity import androidx.fragment.app.FragmentActivity
import io.github.muntashirakon.music.App import io.github.muntashirakon.music.App
import io.github.muntashirakon.music.R import io.github.muntashirakon.music.R
import io.github.muntashirakon.music.dialogs.AddToPlaylistDialog import io.github.muntashirakon.music.dialogs.AddToPlaylistDialog
import io.github.muntashirakon.music.dialogs.DeletePlaylistDialog import io.github.muntashirakon.music.dialogs.DeletePlaylistDialog
import io.github.muntashirakon.music.dialogs.RenamePlaylistDialog import io.github.muntashirakon.music.dialogs.RenamePlaylistDialog
import io.github.muntashirakon.music.helper.MusicPlayerRemote import io.github.muntashirakon.music.helper.MusicPlayerRemote
import io.github.muntashirakon.music.loaders.PlaylistSongsLoader
import io.github.muntashirakon.music.misc.WeakContextAsyncTask import io.github.muntashirakon.music.misc.WeakContextAsyncTask
import io.github.muntashirakon.music.model.AbsCustomPlaylist import io.github.muntashirakon.music.model.AbsCustomPlaylist
import io.github.muntashirakon.music.model.Playlist import io.github.muntashirakon.music.model.Playlist
import io.github.muntashirakon.music.model.Song import io.github.muntashirakon.music.model.Song
import io.github.muntashirakon.music.util.PlaylistsUtil import io.github.muntashirakon.music.util.PlaylistsUtil
import java.util.*
object PlaylistMenuHelper { object PlaylistMenuHelper {
fun handleMenuClick( fun handleMenuClick(
activity: AppCompatActivity, activity: FragmentActivity,
playlist: Playlist, item: MenuItem playlist: Playlist, item: MenuItem
): Boolean { ): Boolean {
when (item.itemId) { when (item.itemId) {
@ -80,11 +78,11 @@ object PlaylistMenuHelper {
private fun getPlaylistSongs( private fun getPlaylistSongs(
activity: Activity, activity: Activity,
playlist: Playlist playlist: Playlist
): ArrayList<Song> { ): List<Song> {
return if (playlist is AbsCustomPlaylist) { return if (playlist is AbsCustomPlaylist) {
playlist.getSongs(activity) playlist.songs()
} else { } else {
PlaylistSongsLoader.getPlaylistSongList(activity, playlist) playlist.getSongs()
} }
} }

View file

@ -18,8 +18,11 @@ import android.content.Intent
import android.view.MenuItem import android.view.MenuItem
import android.view.View import android.view.View
import android.widget.PopupMenu import android.widget.PopupMenu
import androidx.appcompat.app.AppCompatActivity import androidx.core.os.bundleOf
import androidx.fragment.app.FragmentActivity import androidx.fragment.app.FragmentActivity
import androidx.navigation.findNavController
import io.github.muntashirakon.music.EXTRA_ALBUM_ID
import io.github.muntashirakon.music.EXTRA_ARTIST_ID
import io.github.muntashirakon.music.R import io.github.muntashirakon.music.R
import io.github.muntashirakon.music.activities.tageditor.AbsTagEditorActivity import io.github.muntashirakon.music.activities.tageditor.AbsTagEditorActivity
import io.github.muntashirakon.music.activities.tageditor.SongTagEditorActivity import io.github.muntashirakon.music.activities.tageditor.SongTagEditorActivity
@ -30,7 +33,6 @@ import io.github.muntashirakon.music.helper.MusicPlayerRemote
import io.github.muntashirakon.music.interfaces.PaletteColorHolder import io.github.muntashirakon.music.interfaces.PaletteColorHolder
import io.github.muntashirakon.music.model.Song import io.github.muntashirakon.music.model.Song
import io.github.muntashirakon.music.util.MusicUtil import io.github.muntashirakon.music.util.MusicUtil
import io.github.muntashirakon.music.util.NavigationUtil
import io.github.muntashirakon.music.util.RingtoneManager import io.github.muntashirakon.music.util.RingtoneManager
object SongMenuHelper { object SongMenuHelper {
@ -89,18 +91,24 @@ object SongMenuHelper {
return true return true
} }
R.id.action_go_to_album -> { R.id.action_go_to_album -> {
NavigationUtil.goToAlbum(activity, song.albumId) activity.findNavController(R.id.fragment_container).navigate(
R.id.albumDetailsFragment,
bundleOf(EXTRA_ALBUM_ID to song.albumId)
)
return true return true
} }
R.id.action_go_to_artist -> { R.id.action_go_to_artist -> {
NavigationUtil.goToArtist(activity, song.artistId) activity.findNavController(R.id.fragment_container).navigate(
R.id.artistDetailsFragment,
bundleOf(EXTRA_ARTIST_ID to song.artistId)
)
return true return true
} }
} }
return false return false
} }
abstract class OnClickSongMenu protected constructor(private val activity: AppCompatActivity) : abstract class OnClickSongMenu(private val activity: FragmentActivity) :
View.OnClickListener, PopupMenu.OnMenuItemClickListener { View.OnClickListener, PopupMenu.OnMenuItemClickListener {
open val menuRes: Int open val menuRes: Int

View file

@ -15,19 +15,17 @@
package io.github.muntashirakon.music.helper.menu package io.github.muntashirakon.music.helper.menu
import androidx.fragment.app.FragmentActivity import androidx.fragment.app.FragmentActivity
import io.github.muntashirakon.music.R import io.github.muntashirakon.music.R
import io.github.muntashirakon.music.dialogs.AddToPlaylistDialog import io.github.muntashirakon.music.dialogs.AddToPlaylistDialog
import io.github.muntashirakon.music.dialogs.DeleteSongsDialog import io.github.muntashirakon.music.dialogs.DeleteSongsDialog
import io.github.muntashirakon.music.helper.MusicPlayerRemote import io.github.muntashirakon.music.helper.MusicPlayerRemote
import io.github.muntashirakon.music.model.Song import io.github.muntashirakon.music.model.Song
import java.util.*
object SongsMenuHelper { object SongsMenuHelper {
fun handleMenuClick( fun handleMenuClick(
activity: FragmentActivity, activity: FragmentActivity,
songs: ArrayList<Song>, songs: List<Song>,
menuItemId: Int menuItemId: Int
): Boolean { ): Boolean {
when (menuItemId) { when (menuItemId) {

View file

@ -0,0 +1,13 @@
package io.github.muntashirakon.music.interfaces
import android.view.MenuItem
import android.view.View
import java.io.File
interface Callbacks {
fun onFileSelected(file: File)
fun onFileMenuClicked(file: File, view: View)
fun onMultipleItemAction(item: MenuItem, files: ArrayList<File>)
}

View file

@ -1,86 +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 io.github.muntashirakon.music.loaders
import android.content.Context
import android.provider.MediaStore.Audio.AudioColumns
import io.github.muntashirakon.music.model.Album
import io.github.muntashirakon.music.model.Artist
import io.github.muntashirakon.music.util.PreferenceUtil
object ArtistLoader {
private fun getSongLoaderSortOrder(): String {
return PreferenceUtil.artistSortOrder + ", " +
PreferenceUtil.artistAlbumSortOrder + ", " +
PreferenceUtil.artistSongSortOrder
}
fun getAllArtists(context: Context): ArrayList<Artist> {
val songs = SongLoader.getSongs(
SongLoader.makeSongCursor(
context,
null, null,
getSongLoaderSortOrder()
)
)
return splitIntoArtists(AlbumLoader.splitIntoAlbums(songs))
}
fun getArtists(context: Context, query: String): ArrayList<Artist> {
val songs = SongLoader.getSongs(
SongLoader.makeSongCursor(
context,
AudioColumns.ARTIST + " LIKE ?",
arrayOf("%$query%"),
getSongLoaderSortOrder()
)
)
return splitIntoArtists(AlbumLoader.splitIntoAlbums(songs))
}
fun splitIntoArtists(albums: ArrayList<Album>?): ArrayList<Artist> {
val artists = ArrayList<Artist>()
if (albums != null) {
for (album in albums) {
getOrCreateArtist(artists, album.artistId).albums!!.add(album)
}
}
return artists
}
private fun getOrCreateArtist(artists: ArrayList<Artist>, artistId: Int): Artist {
for (artist in artists) {
if (artist.albums!!.isNotEmpty() && artist.albums[0].songs!!.isNotEmpty() && artist.albums[0].songs!![0].artistId == artistId) {
return artist
}
}
val album = Artist()
artists.add(album)
return album
}
@JvmStatic
fun getArtist(context: Context, artistId: Int): Artist {
val songs = SongLoader.getSongs(
SongLoader.makeSongCursor(
context,
AudioColumns.ARTIST_ID + "=?",
arrayOf(artistId.toString()),
getSongLoaderSortOrder()
)
)
return Artist(AlbumLoader.splitIntoAlbums(songs))
}
}

View file

@ -1,141 +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 io.github.muntashirakon.music.loaders
import android.content.Context
import android.database.Cursor
import android.provider.BaseColumns
import android.provider.MediaStore
import android.provider.MediaStore.Audio.PlaylistsColumns
import io.github.muntashirakon.music.R
import io.github.muntashirakon.music.model.Playlist
import java.util.*
/**
* Created by hemanths on 16/08/17.
*/
object PlaylistLoader {
private fun getPlaylist(
cursor: Cursor?
): Playlist {
var playlist = Playlist()
if (cursor != null && cursor.moveToFirst()) {
playlist = getPlaylistFromCursorImpl(cursor)
}
cursor?.close()
return playlist
}
fun searchPlaylist(context: Context, searchString: String): List<Playlist> {
return getAllPlaylists(
makePlaylistCursor(
context, PlaylistsColumns.NAME + "=?", arrayOf(searchString)
)
)
}
fun getPlaylist(
context: Context,
playlistName: String
): Playlist {
return getPlaylist(
makePlaylistCursor(
context,
PlaylistsColumns.NAME + "=?",
arrayOf(playlistName)
)
)
}
fun getAllPlaylists(context: Context): ArrayList<Playlist> {
return getAllPlaylists(makePlaylistCursor(context, null, null))
}
fun getFavoritePlaylist(context: Context): ArrayList<Playlist> {
return getAllPlaylists(
makePlaylistCursor(
context,
PlaylistsColumns.NAME + "=?",
arrayOf(context.getString(R.string.favorites))
)
)
}
fun getAllPlaylists(cursor: Cursor?): ArrayList<Playlist> {
val playlists = ArrayList<Playlist>()
if (cursor != null && cursor.moveToFirst()) {
do {
playlists.add(getPlaylistFromCursorImpl(cursor))
} while (cursor.moveToNext())
}
cursor?.close()
return playlists
}
fun deletePlaylists(context: Context, playlistId: Long) {
val localUri = MediaStore.Audio.Playlists.EXTERNAL_CONTENT_URI
val localStringBuilder = StringBuilder()
localStringBuilder.append("_id IN (")
localStringBuilder.append(playlistId)
localStringBuilder.append(")")
context.contentResolver.delete(localUri, localStringBuilder.toString(), null)
}
private fun makePlaylistCursor(
context: Context,
selection: String?,
values: Array<String>?
): Cursor? {
try {
return context.contentResolver.query(
MediaStore.Audio.Playlists.EXTERNAL_CONTENT_URI,
arrayOf(
BaseColumns._ID, /* 0 */
PlaylistsColumns.NAME /* 1 */
),
selection,
values,
MediaStore.Audio.Playlists.DEFAULT_SORT_ORDER
)
} catch (e: SecurityException) {
return null
}
}
fun getPlaylist(
context: Context,
playlistId: Int
): Playlist {
return getPlaylist(
makePlaylistCursor(
context,
BaseColumns._ID + "=?",
arrayOf(playlistId.toString())
)
)
}
private fun getPlaylistFromCursorImpl(
cursor: Cursor
): Playlist {
val id = cursor.getInt(0)
val name = cursor.getString(1)
return Playlist(id, name)
}
}

View file

@ -89,11 +89,6 @@ public class LrcView extends View {
} }
} }
}; };
public LrcView(Context context) {
this(context, null);
}
/** /**
* 手势监听器 * 手势监听器
*/ */
@ -151,6 +146,10 @@ public class LrcView extends View {
} }
}; };
public LrcView(Context context) {
this(context, null);
}
public LrcView(Context context, AttributeSet attrs) { public LrcView(Context context, AttributeSet attrs) {
this(context, attrs, 0); this(context, attrs, 0);
} }

View file

@ -1,45 +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 io.github.muntashirakon.music.model;
import android.content.Context;
import android.os.Parcel;
import androidx.annotation.NonNull;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
/**
* @author Karim Abou Zeid (kabouzeid)
*/
public abstract class AbsCustomPlaylist extends Playlist {
public AbsCustomPlaylist(int id, String name) {
super(id, name);
}
public AbsCustomPlaylist() {
}
public AbsCustomPlaylist(Parcel in) {
super(in);
}
@NonNull
public abstract ArrayList<Song> getSongs(@NotNull Context context);
}

View file

@ -0,0 +1,21 @@
package io.github.muntashirakon.music.model
import io.github.muntashirakon.music.repository.LastAddedRepository
import io.github.muntashirakon.music.repository.SongRepository
import io.github.muntashirakon.music.repository.TopPlayedRepository
import org.koin.core.KoinComponent
import org.koin.core.inject
abstract class AbsCustomPlaylist(
id: Int = -1,
name: String = ""
) : Playlist(id, name), KoinComponent {
abstract fun songs(): List<Song>
protected val songRepository by inject<SongRepository>()
protected val topPlayedRepository by inject<TopPlayedRepository>()
protected val lastAddedRepository by inject<LastAddedRepository>()
}

View file

@ -41,6 +41,9 @@ class Album {
val songCount: Int val songCount: Int
get() = songs!!.size get() = songs!!.size
val albumArtist: String?
get() = safeGetFirstSong().albumArtist
constructor(songs: ArrayList<Song>) { constructor(songs: ArrayList<Song>) {
this.songs = songs this.songs = songs
} }

View file

@ -25,10 +25,10 @@ class Artist {
val name: String val name: String
get() { get() {
val name = safeGetFirstAlbum().artistName val name = safeGetFirstAlbum().safeGetFirstSong().albumArtist
return if (MusicUtil.isArtistNameUnknown(name)) { return if (MusicUtil.isArtistNameUnknown(name)) {
UNKNOWN_ARTIST_DISPLAY_NAME UNKNOWN_ARTIST_DISPLAY_NAME
} else name!! } else safeGetFirstAlbum().safeGetFirstSong().artistName
} }
val songCount: Int val songCount: Int

View file

@ -59,9 +59,9 @@ public class CategoryInfo implements Parcelable {
} }
public enum Category { public enum Category {
Home(R.id.action_home, R.string.home, R.drawable.asld_home), Home(R.id.action_home, R.string.for_you, R.drawable.ic_face),
Songs(R.id.action_song, R.string.songs, R.drawable.asld_music_note), Songs(R.id.action_song, R.string.songs, R.drawable.ic_audiotrack),
Albums(R.id.action_album, R.string.albums, R.drawable.asld_album), Albums(R.id.action_album, R.string.albums, R.drawable.ic_album),
Artists(R.id.action_artist, R.string.artists, R.drawable.ic_artist), Artists(R.id.action_artist, R.string.artists, R.drawable.ic_artist),
Playlists(R.id.action_playlist, R.string.playlists, R.drawable.ic_playlist_play), Playlists(R.id.action_playlist, R.string.playlists, R.drawable.ic_playlist_play),
Genres(R.id.action_genre, R.string.genres, R.drawable.ic_guitar), Genres(R.id.action_genre, R.string.genres, R.drawable.ic_guitar),

View file

@ -14,13 +14,10 @@
package io.github.muntashirakon.music.model package io.github.muntashirakon.music.model
import androidx.annotation.DrawableRes import io.github.muntashirakon.music.HomeSection
import io.github.muntashirakon.music.adapter.HomeAdapter.Companion.HomeSection
class Home( class Home(
val arrayList: List<*>, val arrayList: List<Any>,
@HomeSection @HomeSection
val homeSection: Int, val homeSection: Int
@DrawableRes
val icon: Int
) )

View file

@ -1,120 +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 io.github.muntashirakon.music.model;
import android.content.Context;
import android.os.Parcel;
import android.os.Parcelable;
import androidx.annotation.NonNull;
import java.util.ArrayList;
import io.github.muntashirakon.music.loaders.PlaylistSongsLoader;
import io.github.muntashirakon.music.util.MusicUtil;
public class Playlist implements Parcelable {
public static final Creator<Playlist> CREATOR = new Creator<Playlist>() {
public Playlist createFromParcel(Parcel source) {
return new Playlist(source);
}
public Playlist[] newArray(int size) {
return new Playlist[size];
}
};
public final int id;
public final String name;
public Playlist(final int id, final String name) {
this.id = id;
this.name = name;
}
public Playlist() {
this.id = -1;
this.name = "";
}
protected Playlist(Parcel in) {
this.id = in.readInt();
this.name = in.readString();
}
@Override
public int describeContents() {
return 0;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
Playlist playlist = (Playlist) o;
if (id != playlist.id) {
return false;
}
return name != null ? name.equals(playlist.name) : playlist.name == null;
}
@NonNull
public ArrayList<Song> getSongs(@NonNull Context context) {
// this default implementation covers static playlists
return PlaylistSongsLoader.INSTANCE.getPlaylistSongList(context, id);
}
@Override
public int hashCode() {
int result = id;
result = 31 * result + (name != null ? name.hashCode() : 0);
return result;
}
@Override
public String toString() {
return "Playlist{" +
"id=" + id +
", name='" + name + '\'' +
'}';
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(this.id);
dest.writeString(this.name);
}
@NonNull
public String getInfoString(@NonNull Context context) {
int songCount = getSongs(context).size();
String songCountString = MusicUtil.getSongCountString(context, songCount);
return MusicUtil.buildInfoString(
songCountString,
""
);
}
}

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