diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 000000000..58639a31a --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -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 +
+ Click to view logs +PASTE YOUR LOGS HERE. +
+ +**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. diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 000000000..e2d9f3239 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -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. diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 000000000..810f53156 --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -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 diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 000000000..9c190e380 --- /dev/null +++ b/CONTRIBUTING.md @@ -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. diff --git a/FAQ.md b/FAQ.md index 2ac44e716..efa40c7ac 100644 --- a/FAQ.md +++ b/FAQ.md @@ -31,8 +31,9 @@ A ".lrc" is like a text file which contains the time stamped lyrics for example, ##### STEP 3: Now you have to rename the file you created in this way: - .lrc or for better matching copy the and the from the tag editor and then rename the file. ##### 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. -Finally you will be able to see the lyrics working. +Now paste the LRC files to the following path : /sdcard/Retromusic/lyrics/ +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. diff --git a/README.md b/README.md index 9aff14f0f..5eb1de1a4 100644 --- a/README.md +++ b/README.md @@ -29,50 +29,50 @@ Material Design music player for Android music lovers | Classic | Adaptive | Blur | Tiny | Peak | ### 🧭 Navigation never made easier -Self-explanatory interface without overloaded menus +Self-explanatory interface without overloaded menus. ### 🎨 Colorful -You can choose between three different main themes: Clearly white, Kind -dark and Just black for AMOLED displays. Select your favorite accent +You can choose between three different main themes: Clearly White, Kinda +Dark and Just Black for AMOLED displays. Select your favorite accent color from a color palette. ### 🏠 Home -Where you can have your recently/ top played Artists, Albums and -Favorite Songs. No other music player has this feature +Where you can have your recently/top played artists, albums and +favorite songs. No other music player has this feature. ### 📦 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 -- Drive Mode +- Driving Mode - Headset/Bluetooth support -- Music Duration Filter +- Music duration filter - Folder support - Play song by folder - Gapless playback - Volume controls -- More than 10 Now playing themes - Carousel effect for an album cover -- Home screen Widgets -- Lock Screen Playback Controls -- Lyrics Screen(download and sync with music) +- Home screen widgets +- Lock screen playback controls +- Lyrics screen (download and sync with music) - Sleep Timer -- Home screen Widgets -- Easy Drag to Sort Playlist & Play Queue +- Easy drag to sort playlist & play queue - Tag editor -- Create, Edit, Import playlists +- Create, edit and import playlists - Playing queue with reorder - User profile - 30 Languages support -- Browse and play your music by Songs, Albums, Artists, Playlists, - Genre -- Smart Auto Playlists - Recently played/Top Played/History Fully - playlist support & Build your own playlist on the go +- Browse and play your music by songs, albums, artists, playlists and + genre +- Smart Auto Playlists - Recently played, most played and history +- Build your own playlist on the go -We are trying our best to bring you the best user experience. Until now -it is a beta version - bug fixes (if any) and more features are on the -way. for FAQ's https://goo.gl/DR2mE2 +We are trying our best to bring you the best user experience. The app is regulary being updated for bug fixes and new features. ### 🗂️ License Metro is released under the GNU General Public License v3.0 (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. diff --git a/app/build.gradle b/app/build.gradle index 851e95dfc..6b4cc101a 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -80,17 +80,18 @@ dependencies { implementation "androidx.gridlayout:gridlayout:1.0.0" implementation "androidx.cardview:cardview:1.0.0" 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.preference:preference-ktx:1.1.1' implementation 'androidx.core:core-ktx:1.3.1' implementation 'androidx.fragment:fragment-ktx:1.2.5' 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 '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' implementation "com.squareup.retrofit2:retrofit:$retrofit_version" @@ -144,5 +145,4 @@ dependencies { def nav_version = "2.3.0" implementation "androidx.navigation:navigation-fragment-ktx:$nav_version" implementation "androidx.navigation:navigation-ui-ktx:$nav_version" - } \ No newline at end of file diff --git a/app/release/output.json b/app/release/output.json deleted file mode 100644 index 758f3ea4a..000000000 --- a/app/release/output.json +++ /dev/null @@ -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" - } - ] -} \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 30cf2312c..d90e768d1 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -20,12 +20,12 @@ - - - - + - - - + diff --git a/app/src/main/ic_launcher-playstore.png b/app/src/main/ic_launcher-playstore.png index 991844b5e..f9c45db6b 100644 Binary files a/app/src/main/ic_launcher-playstore.png and b/app/src/main/ic_launcher-playstore.png differ diff --git a/app/src/main/java/io/github/muntashirakon/music/Constants.kt b/app/src/main/java/io/github/muntashirakon/music/Constants.kt index fd01d4335..85c8c327b 100644 --- a/app/src/main/java/io/github/muntashirakon/music/Constants.kt +++ b/app/src/main/java/io/github/muntashirakon/music/Constants.kt @@ -47,13 +47,17 @@ object Constants { MediaStore.Audio.AudioColumns.ALBUM, // 8 MediaStore.Audio.AudioColumns.ARTIST_ID, // 9 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 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_PLAYLIST = "extra_list" const val LIBRARY_CATEGORIES = "library_categories" const val EXTRA_SONG_INFO = "extra_song_info" const val DESATURATED_COLOR = "desaturated_color" diff --git a/app/src/main/java/io/github/muntashirakon/music/HomeSection.kt b/app/src/main/java/io/github/muntashirakon/music/HomeSection.kt new file mode 100644 index 000000000..237e5bca6 --- /dev/null +++ b/app/src/main/java/io/github/muntashirakon/music/HomeSection.kt @@ -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 \ No newline at end of file diff --git a/app/src/main/java/io/github/muntashirakon/music/MainModule.kt b/app/src/main/java/io/github/muntashirakon/music/MainModule.kt index 303776da1..af439dcb6 100644 --- a/app/src/main/java/io/github/muntashirakon/music/MainModule.kt +++ b/app/src/main/java/io/github/muntashirakon/music/MainModule.kt @@ -1,24 +1,70 @@ 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.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.Playlist import io.github.muntashirakon.music.network.networkModule -import io.github.muntashirakon.music.providers.RepositoryImpl -import org.eclipse.egit.github.core.Repository +import io.github.muntashirakon.music.repository.* +import org.koin.android.ext.koin.androidContext import org.koin.androidx.viewmodel.dsl.viewModel import org.koin.dsl.bind import org.koin.dsl.module private val dataModule = module { single { - RepositoryImpl(get(), get()) + RealRepository(get(), get(), get(), get(), get(), get(), get(), get(), get(), get()) } 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 { @@ -28,19 +74,31 @@ private val viewModules = module { } viewModel { (albumId: Int) -> - AlbumDetailsViewModel(get(), albumId) + AlbumDetailsViewModel( + get(), + albumId + ) } viewModel { (artistId: Int) -> - ArtistDetailsViewModel(get(), artistId) + ArtistDetailsViewModel( + get(), + artistId + ) } viewModel { (playlist: Playlist) -> - PlaylistDetailsViewModel(get(), playlist) + PlaylistDetailsViewModel( + get(), + playlist + ) } viewModel { (genre: Genre) -> - GenreDetailsViewModel(get(), genre) + GenreDetailsViewModel( + get(), + genre + ) } viewModel { diff --git a/app/src/main/java/io/github/muntashirakon/music/Result.kt b/app/src/main/java/io/github/muntashirakon/music/Result.kt new file mode 100644 index 000000000..e99868a98 --- /dev/null +++ b/app/src/main/java/io/github/muntashirakon/music/Result.kt @@ -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 { + data class Success(val data: T) : Result() + object Loading : Result() + object Error : Result() +} \ No newline at end of file diff --git a/app/src/main/java/io/github/muntashirakon/music/activities/LyricsActivity.kt b/app/src/main/java/io/github/muntashirakon/music/activities/LyricsActivity.kt index 70df03e08..4ca69a924 100644 --- a/app/src/main/java/io/github/muntashirakon/music/activities/LyricsActivity.kt +++ b/app/src/main/java/io/github/muntashirakon/music/activities/LyricsActivity.kt @@ -20,7 +20,6 @@ import kotlinx.android.synthetic.main.activity_lyrics.* class LyricsActivity : AbsMusicServiceActivity(), MusicProgressViewUpdateHelper.Callback { private lateinit var updateHelper: MusicProgressViewUpdateHelper - private lateinit var song: Song private val googleSearchLrcUrl: String diff --git a/app/src/main/java/io/github/muntashirakon/music/activities/MainActivity.kt b/app/src/main/java/io/github/muntashirakon/music/activities/MainActivity.kt index 4c15facbe..665823529 100644 --- a/app/src/main/java/io/github/muntashirakon/music/activities/MainActivity.kt +++ b/app/src/main/java/io/github/muntashirakon/music/activities/MainActivity.kt @@ -1,83 +1,44 @@ package io.github.muntashirakon.music.activities -import android.app.ActivityOptions -import android.content.* +import android.content.Intent +import android.content.SharedPreferences +import android.content.SharedPreferences.OnSharedPreferenceChangeListener import android.os.Bundle import android.provider.MediaStore import android.util.Log -import android.view.Menu -import android.view.MenuItem -import android.view.SubMenu import android.view.View -import androidx.core.app.ActivityCompat -import androidx.fragment.app.Fragment -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 androidx.lifecycle.lifecycleScope +import io.github.muntashirakon.music.* 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.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.openQueue import io.github.muntashirakon.music.helper.MusicPlayerRemote.playFromUri import io.github.muntashirakon.music.helper.MusicPlayerRemote.shuffleMode 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.repository.PlaylistSongsLoader.getPlaylistSongList +import io.github.muntashirakon.music.repository.Repository import io.github.muntashirakon.music.service.MusicService 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.RetroColorUtil -import io.github.muntashirakon.music.util.RetroUtil -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 kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch import org.koin.android.ext.android.inject import java.util.* -class MainActivity : AbsSlidingMusicPanelActivity(), - SharedPreferences.OnSharedPreferenceChangeListener, CabHolder { +class MainActivity : AbsSlidingMusicPanelActivity(), OnSharedPreferenceChangeListener { companion object { const val TAG = "MainActivity" const val EXPAND_PANEL = "expand_panel" + const val APP_UPDATE_REQUEST_CODE = 9002 } - private val libraryViewModel: LibraryViewModel by inject() - private var cab: MaterialCab? = null - private val intentFilter = IntentFilter(Intent.ACTION_SCREEN_OFF) - private lateinit var currentFragment: MainActivityFragmentCallbacks + private val repository by inject() + private val libraryViewModel by inject() + 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 { return wrapSlidingMusicPanel(R.layout.activity_main_content) @@ -91,29 +52,16 @@ class MainActivity : AbsSlidingMusicPanelActivity(), setLightNavigationBar(true) setTaskDescriptionColorAuto() hideStatusBar() - setBottomBarVisibility(View.VISIBLE) - - addMusicServiceEventListener(libraryViewModel) - if (savedInstanceState == null) { - selectedFragment(PreferenceUtil.lastPage) - } else { - restoreCurrentFragment() - } - appLaunched(this) - setupToolbar() + addMusicServiceEventListener(libraryViewModel) 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() { super.onResume() - registerReceiver(broadcastReceiver, intentFilter) PreferenceUtil.registerOnSharedPreferenceChangedListener(this) if (intent.hasExtra(EXPAND_PANEL) && intent.getBooleanExtra(EXPAND_PANEL, false) && @@ -126,389 +74,9 @@ class MainActivity : AbsSlidingMusicPanelActivity(), override fun onDestroy() { super.onDestroy() - unregisterReceiver(broadcastReceiver) 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() { if (!blockRequestPermissions) { 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?) { 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) { val id = parseIdFromIntent(intent, "albumId", "album").toInt() if (id >= 0) { - val position = intent.getIntExtra("position", 0) - openQueue(getAlbum(this, id).songs!!, position, true) - handled = true + lifecycleScope.launch(Dispatchers.Main) { + val position = intent.getIntExtra("position", 0) + openQueue(repository.albumById(id).songs!!, position, true) + handled = true + } } } else if (MediaStore.Audio.Artists.CONTENT_TYPE == mimeType) { val id = parseIdFromIntent(intent, "artistId", "artist").toInt() if (id >= 0) { - val position = intent.getIntExtra("position", 0) - openQueue(getArtist(this, id).songs, position, true) - handled = true + lifecycleScope.launch { + val position = intent.getIntExtra("position", 0) + openQueue(repository.artistById(id).songs, position, true) + handled = true + } } } if (handled) { @@ -671,31 +173,4 @@ class MainActivity : AbsSlidingMusicPanelActivity(), } 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 - } } \ No newline at end of file diff --git a/app/src/main/java/io/github/muntashirakon/music/activities/PlayingQueueActivity.kt b/app/src/main/java/io/github/muntashirakon/music/activities/PlayingQueueActivity.kt index 17f91853c..ad4661670 100644 --- a/app/src/main/java/io/github/muntashirakon/music/activities/PlayingQueueActivity.kt +++ b/app/src/main/java/io/github/muntashirakon/music/activities/PlayingQueueActivity.kt @@ -7,6 +7,11 @@ import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView import code.name.monkey.appthemehelper.util.ColorUtil import code.name.monkey.appthemehelper.util.MaterialValueHelper +import 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.activities.base.AbsMusicServiceActivity 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.helper.MusicPlayerRemote import io.github.muntashirakon.music.util.MusicUtil -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.util.ThemedFastScroller import kotlinx.android.synthetic.main.activity_playing_queue.* open class PlayingQueueActivity : AbsMusicServiceActivity() { @@ -103,7 +104,7 @@ open class PlayingQueueActivity : AbsMusicServiceActivity() { } } }) - //ViewUtil.setUpFastScrollRecyclerViewColor(this, recyclerView) + val fastScroller = ThemedFastScroller.create(recyclerView) } private fun checkForPadding() { diff --git a/app/src/main/java/io/github/muntashirakon/music/activities/SettingsActivity.kt b/app/src/main/java/io/github/muntashirakon/music/activities/SettingsActivity.kt old mode 100755 new mode 100644 index 8d038b549..a82d834a7 --- a/app/src/main/java/io/github/muntashirakon/music/activities/SettingsActivity.kt +++ b/app/src/main/java/io/github/muntashirakon/music/activities/SettingsActivity.kt @@ -2,26 +2,18 @@ package io.github.muntashirakon.music.activities import android.os.Bundle import android.view.MenuItem -import androidx.annotation.StringRes -import androidx.fragment.app.Fragment 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.util.VersionUtils +import com.afollestad.materialdialogs.color.ColorChooserDialog import io.github.muntashirakon.music.R import io.github.muntashirakon.music.activities.base.AbsBaseActivity import io.github.muntashirakon.music.appshortcuts.DynamicShortcutManager 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.* class SettingsActivity : AbsBaseActivity(), ColorChooserDialog.ColorCallback { - - private val fragmentManager = supportFragmentManager - private lateinit var appBarConfiguration: AppBarConfiguration - private lateinit var navController: NavController - override fun onCreate(savedInstanceState: Bundle?) { setDrawUnderStatusBar() super.onCreate(savedInstanceState) @@ -35,56 +27,14 @@ class SettingsActivity : AbsBaseActivity(), ColorChooserDialog.ColorCallback { private fun setupToolbar() { setTitle(R.string.action_settings) applyToolbar(toolbar) - val navHostFragment = - supportFragmentManager.findFragmentById(R.id.contentFrame) as NavHostFragment - val navController: NavController = navHostFragment.navController + val navController: NavController = findNavController(R.id.contentFrame) navController.addOnDestinationChangedListener { _, _, _ -> toolbar.title = navController.currentDestination?.label } - - //It removes the back button - //appBarConfiguration = AppBarConfiguration(navController.graph) - //setupActionBarWithNavController(navController, appBarConfiguration) } override fun onSupportNavigateUp(): Boolean { - return navController.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" + return findNavController(R.id.contentFrame).navigateUp() || super.onSupportNavigateUp() } override fun onColorSelection(dialog: ColorChooserDialog, selectedColor: Int) { @@ -101,4 +51,11 @@ class SettingsActivity : AbsBaseActivity(), ColorChooserDialog.ColorCallback { override fun onColorChooserDismissed(dialog: ColorChooserDialog) { } -} + + override fun onOptionsItemSelected(item: MenuItem): Boolean { + if (item.itemId == android.R.id.home) { + onBackPressed() + } + return super.onOptionsItemSelected(item) + } +} \ No newline at end of file diff --git a/app/src/main/java/io/github/muntashirakon/music/activities/UserInfoActivity.kt b/app/src/main/java/io/github/muntashirakon/music/activities/UserInfoActivity.kt index 7606b73e3..4f40dff43 100644 --- a/app/src/main/java/io/github/muntashirakon/music/activities/UserInfoActivity.kt +++ b/app/src/main/java/io/github/muntashirakon/music/activities/UserInfoActivity.kt @@ -10,10 +10,16 @@ import android.text.TextUtils import android.view.MenuItem import android.widget.Toast import code.name.monkey.appthemehelper.util.ColorUtil -import code.name.monkey.appthemehelper.util.MaterialUtil 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_PROFILE +import io.github.muntashirakon.music.R import io.github.muntashirakon.music.activities.base.AbsBaseActivity import io.github.muntashirakon.music.extensions.accentColor 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.util.ImageUtil 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.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers @@ -49,7 +48,7 @@ class UserInfoActivity : AbsBaseActivity() { setLightNavigationBar(true) applyToolbar(toolbar) - MaterialUtil.setTint(nameContainer, false) + nameContainer.accentColor() name.setText(PreferenceUtil.userName) userImage.setOnClickListener { @@ -161,7 +160,7 @@ class UserInfoActivity : AbsBaseActivity() { } private fun saveImage(bitmap: Bitmap, fileName: String) { - CoroutineScope(Dispatchers.IO).launch() { + CoroutineScope(Dispatchers.IO).launch { val appDir = applicationContext.filesDir val file = File(appDir, fileName) var successful = false diff --git a/app/src/main/java/io/github/muntashirakon/music/activities/albums/AlbumDetailsFragment.kt b/app/src/main/java/io/github/muntashirakon/music/activities/albums/AlbumDetailsFragment.kt deleted file mode 100644 index aafdfc31d..000000000 --- a/app/src/main/java/io/github/muntashirakon/music/activities/albums/AlbumDetailsFragment.kt +++ /dev/null @@ -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 { - parametersOf(extraNotNull(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) { - 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 - ) - } -} \ No newline at end of file diff --git a/app/src/main/java/io/github/muntashirakon/music/activities/base/AbsSlidingMusicPanelActivity.kt b/app/src/main/java/io/github/muntashirakon/music/activities/base/AbsSlidingMusicPanelActivity.kt index fdcd683e1..e05776f72 100644 --- a/app/src/main/java/io/github/muntashirakon/music/activities/base/AbsSlidingMusicPanelActivity.kt +++ b/app/src/main/java/io/github/muntashirakon/music/activities/base/AbsSlidingMusicPanelActivity.kt @@ -7,27 +7,29 @@ import android.view.ViewGroup import android.view.ViewTreeObserver import android.widget.FrameLayout import androidx.annotation.LayoutRes +import androidx.core.view.ViewCompat +import androidx.core.view.isVisible import androidx.lifecycle.Observer import code.name.monkey.appthemehelper.util.ATHUtil 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.RetroBottomSheetBehavior import io.github.muntashirakon.music.extensions.hide 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.MiniPlayerFragment 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.model.CategoryInfo -import io.github.muntashirakon.music.util.DensityUtil import io.github.muntashirakon.music.util.PreferenceUtil import io.github.muntashirakon.music.views.BottomNavigationBarTinted -import com.google.android.material.bottomsheet.BottomSheetBehavior import kotlinx.android.synthetic.main.sliding_music_panel_layout.* import org.koin.androidx.viewmodel.ext.android.viewModel -abstract class AbsSlidingMusicPanelActivity() : AbsMusicServiceActivity() { +abstract class AbsSlidingMusicPanelActivity : AbsMusicServiceActivity() { companion object { val TAG: String = AbsSlidingMusicPanelActivity::class.java.simpleName } @@ -120,8 +122,9 @@ abstract class AbsSlidingMusicPanelActivity() : AbsMusicServiceActivity() { return slidingMusicPanelLayout } - private fun collapsePanel() { + fun collapsePanel() { behavior.state = BottomSheetBehavior.STATE_COLLAPSED + setMiniPlayerAlphaProgress(0f) } fun expandPanel() { @@ -133,9 +136,7 @@ abstract class AbsSlidingMusicPanelActivity() : AbsMusicServiceActivity() { if (miniPlayerFragment?.view == null) return val alpha = 1 - progress miniPlayerFragment?.view?.alpha = alpha - // necessary to make the views below clickable miniPlayerFragment?.view?.visibility = if (alpha == 0f) View.GONE else View.VISIBLE - bottomNavigationView.translationY = progress * 500 bottomNavigationView.alpha = alpha } @@ -172,40 +173,31 @@ abstract class AbsSlidingMusicPanelActivity() : AbsMusicServiceActivity() { return bottomNavigationView } - fun setBottomBarVisibility(visible: Int) { - bottomNavigationView.visibility = visible + fun hideBottomBarVisibility(visible: Boolean) { + bottomNavigationView.isVisible = visible hideBottomBar(MusicPlayerRemote.playingQueue.isEmpty()) } private fun hideBottomBar(hide: Boolean) { - val heightOfBar = resources.getDimensionPixelSize(R.dimen.mini_player_height) - val heightOfBarWithTabs = - resources.getDimensionPixelSize(R.dimen.mini_player_height_expanded) + val heightOfBar = bottomNavigationView.height + val isBottomBarVisible = bottomNavigationView.isVisible if (hide) { behavior.isHideable = true behavior.peekHeight = 0 - bottomNavigationView.elevation = DensityUtil.dip2px(this, 10f).toFloat() collapsePanel() + ViewCompat.setElevation(bottomNavigationView, 10f) } else { - if (MusicPlayerRemote.playingQueue.isNotEmpty()) { - slidingPanel.elevation = DensityUtil.dip2px(this, 10f).toFloat() - bottomNavigationView.elevation = DensityUtil.dip2px(this, 10f).toFloat() - behavior.isHideable = false - behavior.peekHeight = - if (bottomNavigationView.visibility == View.VISIBLE) { - heightOfBarWithTabs - } else { - heightOfBar - } - } + ViewCompat.setElevation(bottomNavigationView, 10f) + ViewCompat.setElevation(slidingPanel, 10f) + behavior.isHideable = false + behavior.peekHeight = (if (isBottomBarVisible) heightOfBar * 2 else heightOfBar) - 24 } } private fun chooseFragmentForTheme() { cps = PreferenceUtil.nowPlayingScreen - miniPlayerFragment = - supportFragmentManager.findFragmentById(R.id.miniPlayerFragment) as MiniPlayerFragment + miniPlayerFragment = whichFragment(R.id.miniPlayerFragment) miniPlayerFragment?.view?.setOnClickListener { expandPanel() } } @@ -232,7 +224,6 @@ abstract class AbsSlidingMusicPanelActivity() : AbsMusicServiceActivity() { } open fun handleBackPress(): Boolean { - if (panelState == BottomSheetBehavior.STATE_EXPANDED) { collapsePanel() return true @@ -305,6 +296,12 @@ abstract class AbsSlidingMusicPanelActivity() : AbsMusicServiceActivity() { } } + fun hideBottomNavigation() { + behavior.isHideable = true + behavior.peekHeight = 0 + hideBottomBarVisibility(false) + } + fun updateTabs() { bottomNavigationView.menu.clear() val currentTabs: List = PreferenceUtil.libraryCategory diff --git a/app/src/main/java/io/github/muntashirakon/music/activities/genre/GenreDetailsActivity.kt b/app/src/main/java/io/github/muntashirakon/music/activities/genre/GenreDetailsActivity.kt deleted file mode 100644 index 1464d4b2a..000000000 --- a/app/src/main/java/io/github/muntashirakon/music/activities/genre/GenreDetailsActivity.kt +++ /dev/null @@ -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(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) { - 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" - } -} diff --git a/app/src/main/java/io/github/muntashirakon/music/activities/search/SearchActivity.kt b/app/src/main/java/io/github/muntashirakon/music/activities/search/SearchActivity.kt deleted file mode 100644 index 8eebd95eb..000000000 --- a/app/src/main/java/io/github/muntashirakon/music/activities/search/SearchActivity.kt +++ /dev/null @@ -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(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) { - 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? = - 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 -} \ No newline at end of file diff --git a/app/src/main/java/io/github/muntashirakon/music/activities/tageditor/AbsTagEditorActivity.kt b/app/src/main/java/io/github/muntashirakon/music/activities/tageditor/AbsTagEditorActivity.kt index 19990376c..67f300d90 100755 --- a/app/src/main/java/io/github/muntashirakon/music/activities/tageditor/AbsTagEditorActivity.kt +++ b/app/src/main/java/io/github/muntashirakon/music/activities/tageditor/AbsTagEditorActivity.kt @@ -14,35 +14,38 @@ import android.view.MenuItem import android.view.View import android.view.animation.OvershootInterpolator import androidx.appcompat.app.AlertDialog +import androidx.lifecycle.lifecycleScope import code.name.monkey.appthemehelper.ThemeStore import code.name.monkey.appthemehelper.util.ATHUtil import code.name.monkey.appthemehelper.util.ColorUtil import code.name.monkey.appthemehelper.util.MaterialValueHelper import code.name.monkey.appthemehelper.util.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.drawable import io.github.muntashirakon.music.activities.base.AbsBaseActivity 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.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 org.jaudiotagger.audio.AudioFile import org.jaudiotagger.audio.AudioFileIO import org.jaudiotagger.tag.FieldKey +import org.koin.android.ext.android.inject import java.io.File import java.util.* abstract class AbsTagEditorActivity : AbsBaseActivity() { + val repository by inject() + lateinit var saveFab: MaterialButton protected var id: Int = 0 private set private var paletteColorPrimary: Int = 0 private var isInNoImageMode: Boolean = false private var songPaths: List? = null - lateinit var saveFab: MaterialButton - private var savedSongPaths: List? = null private val currentSongPath: String? = null private var savedTags: Map? = null @@ -172,41 +175,35 @@ abstract class AbsTagEditorActivity : AbsBaseActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(contentViewLayout) + setStatusbarColorAuto() + setNavigationbarColorAuto() + setTaskDescriptionColorAuto() saveFab = findViewById(R.id.saveTags) getIntentExtras() - songPaths = getSongPaths() - if (songPaths!!.isEmpty()) { - finish() - return + lifecycleScope.launchWhenCreated { + songPaths = getSongPaths() + if (songPaths!!.isEmpty()) { + finish() + } } - setUpViews() - - setStatusbarColorAuto() - setNavigationbarColorAuto() - setTaskDescriptionColorAuto() } private fun setUpViews() { - setUpScrollView() setUpFab() setUpImageView() } - private fun setUpScrollView() { - //observableScrollView.setScrollViewCallbacks(observableScrollViewCallbacks); - } - private lateinit var items: List private fun setUpImageView() { loadCurrentImage() items = listOf( - getString(R.string.pick_from_local_storage), - getString(R.string.web_search), - getString(R.string.remove_cover) + getString(io.github.muntashirakon.music.R.string.pick_from_local_storage), + getString(io.github.muntashirakon.music.R.string.web_search), + getString(io.github.muntashirakon.music.R.string.remove_cover) ) editorImage?.setOnClickListener { show } } @@ -217,7 +214,7 @@ abstract class AbsTagEditorActivity : AbsBaseActivity() { startActivityForResult( Intent.createChooser( intent, - getString(R.string.pick_from_local_storage) + getString(io.github.muntashirakon.music.R.string.pick_from_local_storage) ), REQUEST_CODE_SELECT_IMAGE ) } @@ -261,7 +258,7 @@ abstract class AbsTagEditorActivity : AbsBaseActivity() { } } - protected abstract fun getSongPaths(): List + protected abstract suspend fun getSongPaths(): List protected fun searchWebFor(vararg keys: String) { val stringBuilder = StringBuilder() @@ -336,7 +333,7 @@ abstract class AbsTagEditorActivity : AbsBaseActivity() { hideFab() - savedSongPaths = getSongPaths() + savedSongPaths = songPaths savedTags = fieldKeyValueMap savedArtworkInfo = artworkInfo diff --git a/app/src/main/java/io/github/muntashirakon/music/activities/tageditor/AlbumTagEditorActivity.kt b/app/src/main/java/io/github/muntashirakon/music/activities/tageditor/AlbumTagEditorActivity.kt index af45b7f60..9f01b7063 100755 --- a/app/src/main/java/io/github/muntashirakon/music/activities/tageditor/AlbumTagEditorActivity.kt +++ b/app/src/main/java/io/github/muntashirakon/music/activities/tageditor/AlbumTagEditorActivity.kt @@ -14,23 +14,23 @@ import android.transition.Slide import android.widget.Toast import code.name.monkey.appthemehelper.util.ATHUtil 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.load.engine.DiskCacheStrategy import com.bumptech.glide.request.animation.GlideAnimation 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 org.jaudiotagger.tag.FieldKey import java.util.* class AlbumTagEditorActivity : AbsTagEditorActivity(), TextWatcher { + override val contentViewLayout: Int get() = R.layout.activity_album_tag_editor @@ -162,13 +162,13 @@ class AlbumTagEditorActivity : AbsTagEditorActivity(), TextWatcher { writeValuesToFiles( fieldKeyValueMap, - if (deleteAlbumArt) AbsTagEditorActivity.ArtworkInfo(id, null) + if (deleteAlbumArt) ArtworkInfo(id, null) else if (albumArtBitmap == null) null else ArtworkInfo(id, albumArtBitmap!!) ) } - override fun getSongPaths(): List { - val songs = AlbumLoader.getAlbum(this, id).songs + override suspend fun getSongPaths(): List { + val songs = repository.albumById(id).songs val paths = ArrayList(songs!!.size) for (song in songs) { paths.add(song.data) diff --git a/app/src/main/java/io/github/muntashirakon/music/activities/tageditor/SongTagEditorActivity.kt b/app/src/main/java/io/github/muntashirakon/music/activities/tageditor/SongTagEditorActivity.kt index 0dd9b2cf3..30ae4ce48 100755 --- a/app/src/main/java/io/github/muntashirakon/music/activities/tageditor/SongTagEditorActivity.kt +++ b/app/src/main/java/io/github/muntashirakon/music/activities/tageditor/SongTagEditorActivity.kt @@ -8,9 +8,10 @@ import code.name.monkey.appthemehelper.util.ATHUtil 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.loaders.SongLoader +import io.github.muntashirakon.music.repository.SongRepository import kotlinx.android.synthetic.main.activity_song_tag_editor.* import org.jaudiotagger.tag.FieldKey +import org.koin.android.ext.android.inject import java.util.* class SongTagEditorActivity : AbsTagEditorActivity(), TextWatcher { @@ -18,6 +19,8 @@ class SongTagEditorActivity : AbsTagEditorActivity(), TextWatcher { override val contentViewLayout: Int get() = R.layout.activity_song_tag_editor + private val songRepository by inject() + override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @@ -85,9 +88,9 @@ class SongTagEditorActivity : AbsTagEditorActivity(), TextWatcher { writeValuesToFiles(fieldKeyValueMap, null) } - override fun getSongPaths(): List { + override suspend fun getSongPaths(): List { val paths = ArrayList(1) - paths.add(SongLoader.getSong(this, id).data) + paths.add(songRepository.song(id).data) return paths } diff --git a/app/src/main/java/io/github/muntashirakon/music/activities/tageditor/WriteTagsAsyncTask.java b/app/src/main/java/io/github/muntashirakon/music/activities/tageditor/WriteTagsAsyncTask.java index 25cb35cf6..07aaf76b8 100644 --- a/app/src/main/java/io/github/muntashirakon/music/activities/tageditor/WriteTagsAsyncTask.java +++ b/app/src/main/java/io/github/muntashirakon/music/activities/tageditor/WriteTagsAsyncTask.java @@ -63,7 +63,7 @@ public class WriteTagsAsyncTask extends DialogAsyncTask, private val mItemLayoutRes: Int ) : RecyclerView.Adapter() { @@ -48,9 +50,10 @@ class GenreAdapter( inner class ViewHolder(itemView: View) : MediaEntryViewHolder(itemView) { override fun onClick(v: View?) { - super.onClick(v) - val genre = dataSet[layoutPosition] - NavigationUtil.goToGenre(activity, genre) + activity.findNavController(R.id.fragment_container).navigate( + R.id.genreDetailsFragment, + bundleOf(EXTRA_GENRE to dataSet[layoutPosition]) + ) } } } diff --git a/app/src/main/java/io/github/muntashirakon/music/adapter/HomeAdapter.kt b/app/src/main/java/io/github/muntashirakon/music/adapter/HomeAdapter.kt index f943e20c2..ac163e1a7 100644 --- a/app/src/main/java/io/github/muntashirakon/music/adapter/HomeAdapter.kt +++ b/app/src/main/java/io/github/muntashirakon/music/adapter/HomeAdapter.kt @@ -1,38 +1,37 @@ package io.github.muntashirakon.music.adapter -import android.util.DisplayMetrics import android.view.LayoutInflater import android.view.View import android.view.ViewGroup +import android.widget.ImageView import android.widget.TextView -import androidx.annotation.IntDef import androidx.appcompat.app.AppCompatActivity 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.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView -import androidx.recyclerview.widget.RecyclerView.HORIZONTAL import code.name.monkey.appthemehelper.ThemeStore import code.name.monkey.appthemehelper.util.ColorUtil -import io.github.muntashirakon.music.PeekingLinearLayoutManager -import io.github.muntashirakon.music.R +import com.bumptech.glide.Glide +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.artist.ArtistAdapter 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.helper.MusicPlayerRemote -import io.github.muntashirakon.music.loaders.PlaylistSongsLoader import io.github.muntashirakon.music.model.* -import io.github.muntashirakon.music.model.Playlist import io.github.muntashirakon.music.util.PreferenceUtil -import com.bumptech.glide.Glide -import com.google.android.material.card.MaterialCardView class HomeAdapter( - private val activity: AppCompatActivity, - private val displayMetrics: DisplayMetrics -) : RecyclerView.Adapter() { + private val activity: AppCompatActivity +) : RecyclerView.Adapter(), ArtistClickListener, AlbumClickListener { private var list = listOf() @@ -45,14 +44,9 @@ class HomeAdapter( .inflate(R.layout.section_recycler_view, parent, false) return when (viewType) { 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) FAVOURITES -> PlaylistViewHolder(layout) + TOP_ALBUMS, RECENT_ALBUMS -> AlbumViewHolder(layout) else -> { SuggestionsViewHolder( LayoutInflater.from(activity).inflate( @@ -66,48 +60,62 @@ class HomeAdapter( } override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { + val home = list[position] when (getItemViewType(position)) { RECENT_ALBUMS -> { val viewHolder = holder as AlbumViewHolder - viewHolder.bindView( - list[position].arrayList as List, - R.string.recent_albums - ) + viewHolder.bindView(home.arrayList as List, R.string.recent_albums) + viewHolder.clickableArea.setOnClickListener { + activity.findNavController(R.id.fragment_container).navigate( + R.id.detailListFragment, + bundleOf("type" to RECENT_ALBUMS) + ) + } } TOP_ALBUMS -> { val viewHolder = holder as AlbumViewHolder - viewHolder.bindView( - list[position].arrayList as List, - R.string.top_albums - ) + viewHolder.bindView(home.arrayList as List, R.string.top_albums) + viewHolder.clickableArea.setOnClickListener { + activity.findNavController(R.id.fragment_container).navigate( + R.id.detailListFragment, + bundleOf("type" to TOP_ALBUMS) + ) + } } RECENT_ARTISTS -> { val viewHolder = holder as ArtistViewHolder - viewHolder.bindView( - list[position].arrayList as List, - R.string.recent_artists - ) + viewHolder.bindView(home.arrayList, R.string.recent_artists) + viewHolder.clickableArea.setOnClickListener { + activity.findNavController(R.id.fragment_container).navigate( + R.id.detailListFragment, + bundleOf("type" to RECENT_ARTISTS) + ) + } } TOP_ARTISTS -> { val viewHolder = holder as ArtistViewHolder - viewHolder.bindView(list[position].arrayList as List, 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 -> { val viewHolder = holder as SuggestionsViewHolder - viewHolder.bindView( - list[position].arrayList as List - ) + viewHolder.bindView(home.arrayList) } FAVOURITES -> { val viewHolder = holder as PlaylistViewHolder - viewHolder.bindView( - list[position].arrayList as List, - R.string.favorites - ) + viewHolder.bindView(home.arrayList, R.string.favorites) } GENRES -> { val viewHolder = holder as GenreViewHolder - viewHolder.bind(list[position].arrayList as List, R.string.genres) + viewHolder.bind(home.arrayList, R.string.genres) + } + PLAYLISTS -> { + } } } @@ -121,60 +129,23 @@ class HomeAdapter( 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) { - fun bindView(list: List, titleRes: Int) { - if (list.isNotEmpty()) { - recyclerView.apply { - show() - adapter = AlbumAdapter(activity, list, R.layout.pager_item, null) - layoutManager = - PeekingLinearLayoutManager(activity, HORIZONTAL, false) - } - title.text = activity.getString(titleRes) + fun bindView(albums: List, titleRes: Int) { + title.text = activity.getString(titleRes) + recyclerView.apply { + adapter = albumAdapter(albums) + layoutManager = gridLayoutManager() } } } - inner class ArtistViewHolder(view: View) : AbsHomeViewItem(view) { - fun bindView(list: List, titleRes: Int) { - if (list.isNotEmpty()) { - val manager = LinearLayoutManager(activity, LinearLayoutManager.HORIZONTAL, false) - val artistAdapter = ArtistAdapter( - activity, - list, - PreferenceUtil.homeGridStyle, - null - ) - recyclerView.apply { - show() - layoutManager = manager - adapter = artistAdapter - } - title.text = activity.getString(titleRes) + private inner class ArtistViewHolder(view: View) : AbsHomeViewItem(view) { + fun bindView(artists: List, titleRes: Int) { + recyclerView.apply { + layoutManager = linearLayoutManager() + adapter = artistsAdapter(artists as List) } + title.text = activity.getString(titleRes) } } @@ -190,52 +161,50 @@ class HomeAdapter( R.id.image8 ) - fun bindView(arrayList: List) { + fun bindView(songs: List) { + songs as List val color = ThemeStore.accentColor(activity) - itemView.findViewById(R.id.text).setTextColor(color) + itemView.findViewById(R.id.message).setTextColor(color) itemView.findViewById(R.id.card6).apply { - setCardBackgroundColor(ColorUtil.withAlpha(color, 0.2f)) + setCardBackgroundColor(ColorUtil.withAlpha(color, 0.12f)) } - if (arrayList.size > 9) - images.forEachIndexed { index, i -> - itemView.findViewById(i).setOnClickListener { - MusicPlayerRemote.playNext(arrayList[index]) - } - SongGlideRequest.Builder.from(Glide.with(activity), arrayList[index]) - .asBitmap() - .build() - .into(itemView.findViewById(i)) - + images.forEachIndexed { index, id -> + itemView.findViewById(id).setOnClickListener { + MusicPlayerRemote.playNext(songs[index]) } + SongGlideRequest.Builder.from(Glide.with(activity), songs[index]) + .asBitmap() + .build() + .into(itemView.findViewById(id)) + + } } } private inner class PlaylistViewHolder(view: View) : AbsHomeViewItem(view) { - fun bindView(arrayList: List, titleRes: Int) { - if (arrayList.isNotEmpty()) { - val songs = PlaylistSongsLoader.getPlaylistSongList(activity, arrayList[0]) - if (songs.isNotEmpty()) { - recyclerView.apply { - show() - val songAdapter = - SongAdapter(activity, songs, R.layout.item_album_card, null) - layoutManager = - GridLayoutManager(activity, 1, GridLayoutManager.HORIZONTAL, false) - adapter = songAdapter - } - title.text = activity.getString(titleRes) - } + fun bindView(songs: List, titleRes: Int) { + arrow.hide() + recyclerView.apply { + val songAdapter = SongAdapter( + activity, + songs as MutableList, + R.layout.item_album_card, null + ) + layoutManager = linearLayoutManager() + adapter = songAdapter } + title.text = activity.getString(titleRes) } } private inner class GenreViewHolder(itemView: View) : AbsHomeViewItem(itemView) { - fun bind(genres: List, titleRes: Int) { + fun bind(genres: List, titleRes: Int) { + arrow.hide() title.text = activity.getString(titleRes) recyclerView.apply { - show() - layoutManager = GridLayoutManager(activity, 2, GridLayoutManager.HORIZONTAL, false) - val genreAdapter = GenreAdapter(activity, genres, R.layout.item_grid_genre) + layoutManager = GridLayoutManager(activity, 3, GridLayoutManager.HORIZONTAL, false) + val genreAdapter = + GenreAdapter(activity, genres as List, R.layout.item_grid_genre) adapter = genreAdapter } } @@ -244,5 +213,38 @@ class HomeAdapter( open inner class AbsHomeViewItem(itemView: View) : RecyclerView.ViewHolder(itemView) { val recyclerView: RecyclerView = itemView.findViewById(R.id.recyclerView) 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) = + ArtistAdapter(activity, artists, PreferenceUtil.homeGridStyle, null, this) + + fun albumAdapter(albums: List) = + 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) + ) + ) + } +} \ No newline at end of file diff --git a/app/src/main/java/io/github/muntashirakon/music/adapter/SearchAdapter.kt b/app/src/main/java/io/github/muntashirakon/music/adapter/SearchAdapter.kt index 722e94851..fcef5c254 100644 --- a/app/src/main/java/io/github/muntashirakon/music/adapter/SearchAdapter.kt +++ b/app/src/main/java/io/github/muntashirakon/music/adapter/SearchAdapter.kt @@ -1,29 +1,28 @@ package io.github.muntashirakon.music.adapter -import android.app.ActivityOptions import android.view.LayoutInflater import android.view.View 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 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.glide.AlbumGlideRequest import io.github.muntashirakon.music.glide.ArtistGlideRequest import io.github.muntashirakon.music.helper.MusicPlayerRemote 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.smartplaylist.AbsSmartPlaylist +import io.github.muntashirakon.music.repository.PlaylistSongsLoader 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( - private val activity: AppCompatActivity, - private var dataSet: List? + private val activity: FragmentActivity, + private var dataSet: List ) : RecyclerView.Adapter() { fun swapDataSet(dataSet: MutableList) { @@ -32,11 +31,11 @@ class SearchAdapter( } override fun getItemViewType(position: Int): Int { - if (dataSet!![position] is Album) return ALBUM - if (dataSet!![position] is Artist) return ARTIST - if (dataSet!![position] is Genre) return GENRE - if (dataSet!![position] is Playlist) return PLAYLIST - return if (dataSet!![position] is Song) SONG else HEADER + if (dataSet[position] is Album) return ALBUM + if (dataSet[position] is Artist) return ARTIST + if (dataSet[position] is Genre) return GENRE + if (dataSet[position] is Playlist) return PLAYLIST + return if (dataSet[position] is Song) SONG else HEADER } override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { @@ -57,35 +56,35 @@ class SearchAdapter( override fun onBindViewHolder(holder: ViewHolder, position: Int) { when (getItemViewType(position)) { ALBUM -> { - val album = dataSet?.get(position) as Album + val album = dataSet.get(position) as Album holder.title?.text = album.title holder.text?.text = album.artistName AlbumGlideRequest.Builder.from(Glide.with(activity), album.safeGetFirstSong()) .checkIgnoreMediaStore(activity).build().into(holder.image) } ARTIST -> { - val artist = dataSet?.get(position) as Artist + val artist = dataSet.get(position) as Artist holder.title?.text = artist.name holder.text?.text = MusicUtil.getArtistInfoString(activity, artist) ArtistGlideRequest.Builder.from(Glide.with(activity), artist).build() .into(holder.image) } SONG -> { - val song = dataSet?.get(position) as Song + val song = dataSet.get(position) as Song holder.title?.text = song.title holder.text?.text = song.albumName } GENRE -> { - val genre = dataSet?.get(position) as Genre + val genre = dataSet.get(position) as Genre holder.title?.text = genre.name } PLAYLIST -> { - val playlist = dataSet?.get(position) as Playlist + val playlist = dataSet.get(position) as Playlist holder.title?.text = playlist.name holder.text?.text = MusicUtil.getPlaylistInfoString(activity, getSongs(playlist)) } else -> { - holder.title?.text = dataSet?.get(position).toString() + holder.title?.text = dataSet.get(position).toString() holder.title?.setTextColor(ThemeStore.accentColor(activity)) } } @@ -94,7 +93,7 @@ class SearchAdapter( private fun getSongs(playlist: Playlist): java.util.ArrayList { val songs = java.util.ArrayList() if (playlist is AbsSmartPlaylist) { - songs.addAll(playlist.getSongs(activity)) + songs.addAll(playlist.getSongs()) } else { songs.addAll(PlaylistSongsLoader.getPlaylistSongList(activity, playlist.id)) } @@ -102,7 +101,7 @@ class SearchAdapter( } override fun getItemCount(): Int { - return dataSet!!.size + return dataSet.size } inner class ViewHolder(itemView: View, itemViewType: Int) : MediaEntryViewHolder(itemView) { @@ -113,7 +112,7 @@ class SearchAdapter( menu?.visibility = View.VISIBLE menu?.setOnClickListener(object : SongMenuHelper.OnClickSongMenu(activity) { override val song: Song - get() = dataSet!![layoutPosition] as Song + get() = dataSet[layoutPosition] as Song }) } else { menu?.visibility = View.GONE @@ -130,27 +129,31 @@ class SearchAdapter( } override fun onClick(v: View?) { - val item = dataSet!![layoutPosition] + val item = dataSet[layoutPosition] when (itemViewType) { ALBUM -> { - val options = ActivityOptions.makeSceneTransitionAnimation( - activity, - UtilPair.create(image, activity.getString(R.string.transition_album_art)) + activity.findNavController(R.id.fragment_container).navigate( + R.id.albumDetailsFragment, + bundleOf(EXTRA_ALBUM_ID to (item as Album).id) ) - NavigationUtil.goToAlbumOptions(activity, (item as Album).id, options) } ARTIST -> { - val options = ActivityOptions.makeSceneTransitionAnimation( - activity, - UtilPair.create(image, activity.getString(R.string.transition_artist_image)) + activity.findNavController(R.id.fragment_container).navigate( + R.id.artistDetailsFragment, + bundleOf(EXTRA_ARTIST_ID to (item as Artist).id) ) - NavigationUtil.goToArtistOptions(activity, (item as Artist).id, options) } 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 -> { - NavigationUtil.goToPlaylistNew(activity, item as Playlist) + activity.findNavController(R.id.fragment_container).navigate( + R.id.artistDetailsFragment, + bundleOf(EXTRA_PLAYLIST to (item as Playlist)) + ) } SONG -> { val playList = ArrayList() diff --git a/app/src/main/java/io/github/muntashirakon/music/adapter/SongFileAdapter.kt b/app/src/main/java/io/github/muntashirakon/music/adapter/SongFileAdapter.kt index a7fd9e87c..b6c8503c4 100644 --- a/app/src/main/java/io/github/muntashirakon/music/adapter/SongFileAdapter.kt +++ b/app/src/main/java/io/github/muntashirakon/music/adapter/SongFileAdapter.kt @@ -22,20 +22,20 @@ import android.view.View import android.view.ViewGroup import androidx.appcompat.app.AppCompatActivity 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.adapter.base.AbsMultiSelectAdapter import io.github.muntashirakon.music.adapter.base.MediaEntryViewHolder import io.github.muntashirakon.music.glide.audiocover.AudioFileCover 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.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 java.io.File import java.text.DecimalFormat -import java.util.* import kotlin.math.log10 import kotlin.math.pow @@ -135,9 +135,9 @@ class SongFileAdapter( return getFileTitle(`object`) } - override fun onMultipleItemAction(menuItem: MenuItem, selection: ArrayList) { + override fun onMultipleItemAction(menuItem: MenuItem, selection: List) { if (callbacks == null) return - callbacks.onMultipleItemAction(menuItem, selection) + callbacks.onMultipleItemAction(menuItem, selection as ArrayList) } override fun getPopupText(position: Int): String { @@ -148,13 +148,6 @@ class SongFileAdapter( return MusicUtil.getSectionName(dataSet[position].name) } - interface Callbacks { - fun onFileSelected(file: File) - - fun onFileMenuClicked(file: File, view: View) - - fun onMultipleItemAction(item: MenuItem, files: ArrayList) - } inner class ViewHolder(itemView: View) : MediaEntryViewHolder(itemView) { diff --git a/app/src/main/java/io/github/muntashirakon/music/adapter/album/AlbumAdapter.kt b/app/src/main/java/io/github/muntashirakon/music/adapter/album/AlbumAdapter.kt index f1147f71a..011356c60 100644 --- a/app/src/main/java/io/github/muntashirakon/music/adapter/album/AlbumAdapter.kt +++ b/app/src/main/java/io/github/muntashirakon/music/adapter/album/AlbumAdapter.kt @@ -1,16 +1,17 @@ package io.github.muntashirakon.music.adapter.album -import android.app.ActivityOptions import android.content.res.ColorStateList import android.content.res.Resources import android.view.LayoutInflater import android.view.MenuItem import android.view.View 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.adapter.base.AbsMultiSelectAdapter 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.RetroMusicColoredTarget 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.Song 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.color.MediaNotificationProcessor -import com.bumptech.glide.Glide import me.zhanghai.android.fastscroll.PopupTextProvider open class AlbumAdapter( - protected val activity: AppCompatActivity, + protected val activity: FragmentActivity, var dataSet: List, protected var itemLayoutRes: Int, - cabHolder: CabHolder? + cabHolder: CabHolder?, + private val albumClickListener: AlbumClickListener? ) : AbsMultiSelectAdapter( activity, cabHolder, @@ -129,12 +129,12 @@ open class AlbumAdapter( } override fun onMultipleItemAction( - menuItem: MenuItem, selection: ArrayList + menuItem: MenuItem, selection: List ) { SongsMenuHelper.handleMenuClick(activity, getSongList(selection), menuItem.itemId) } - private fun getSongList(albums: List): ArrayList { + private fun getSongList(albums: List): List { val songs = ArrayList() for (album in albums) { songs.addAll(album.songs!!) @@ -156,7 +156,6 @@ open class AlbumAdapter( dataSet[position].year ) } - return MusicUtil.getSectionName(sectionName) } @@ -172,16 +171,7 @@ open class AlbumAdapter( if (isInQuickSelectMode) { toggleChecked(layoutPosition) } else { - val activityOptions = ActivityOptions.makeSceneTransitionAnimation( - activity, - imageContainerCard ?: image, - activity.getString(R.string.transition_album_art) - ) - NavigationUtil.goToAlbumOptions( - activity, - dataSet[layoutPosition].id, - activityOptions - ) + image?.let { albumClickListener?.onAlbumClick(dataSet[layoutPosition].id, it) } } } diff --git a/app/src/main/java/io/github/muntashirakon/music/adapter/album/HorizontalAlbumAdapter.kt b/app/src/main/java/io/github/muntashirakon/music/adapter/album/HorizontalAlbumAdapter.kt index 99cf66910..cb6dd36e1 100644 --- a/app/src/main/java/io/github/muntashirakon/music/adapter/album/HorizontalAlbumAdapter.kt +++ b/app/src/main/java/io/github/muntashirakon/music/adapter/album/HorizontalAlbumAdapter.kt @@ -1,10 +1,11 @@ package io.github.muntashirakon.music.adapter.album -import android.graphics.drawable.Drawable import android.view.View import android.view.ViewGroup -import androidx.appcompat.app.AppCompatActivity +import androidx.fragment.app.FragmentActivity 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.RetroMusicColoredTarget 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.util.MusicUtil import io.github.muntashirakon.music.util.color.MediaNotificationProcessor -import com.bumptech.glide.Glide class HorizontalAlbumAdapter( - activity: AppCompatActivity, + activity: FragmentActivity, dataSet: List, - cabHolder: CabHolder? + cabHolder: CabHolder?, + albumClickListener: AlbumClickListener ) : AlbumAdapter( - activity, dataSet, HorizontalAdapterHelper.LAYOUT_RES, cabHolder + activity, dataSet, HorizontalAdapterHelper.LAYOUT_RES, cabHolder, albumClickListener ) { override fun createViewHolder(view: View, viewType: Int): ViewHolder { @@ -40,11 +41,6 @@ class HorizontalAlbumAdapter( .generatePalette(activity) .build() .into(object : RetroMusicColoredTarget(holder.image!!) { - override fun onLoadCleared(placeholder: Drawable?) { - super.onLoadCleared(placeholder) - //setColors(albumArtistFooterColor, holder) - } - override fun onColorReady(colors: MediaNotificationProcessor) { setColors(colors, holder) } diff --git a/app/src/main/java/io/github/muntashirakon/music/adapter/artist/ArtistAdapter.kt b/app/src/main/java/io/github/muntashirakon/music/adapter/artist/ArtistAdapter.kt index 60f62d042..54fd14e16 100644 --- a/app/src/main/java/io/github/muntashirakon/music/adapter/artist/ArtistAdapter.kt +++ b/app/src/main/java/io/github/muntashirakon/music/adapter/artist/ArtistAdapter.kt @@ -1,17 +1,19 @@ package io.github.muntashirakon.music.adapter.artist -import android.app.ActivityOptions import android.content.res.ColorStateList import android.content.res.Resources import android.view.LayoutInflater import android.view.MenuItem import android.view.View 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.adapter.base.AbsMultiSelectAdapter import io.github.muntashirakon.music.adapter.base.MediaEntryViewHolder 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.RetroMusicColoredTarget 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.Song import io.github.muntashirakon.music.util.MusicUtil -import io.github.muntashirakon.music.util.NavigationUtil import io.github.muntashirakon.music.util.color.MediaNotificationProcessor -import com.bumptech.glide.Glide import me.zhanghai.android.fastscroll.PopupTextProvider import java.util.* class ArtistAdapter( - val activity: AppCompatActivity, + val activity: FragmentActivity, var dataSet: List, var itemLayoutRes: Int, - cabHolder: CabHolder? + cabHolder: CabHolder?, + private val artistClickListener: ArtistClickListener ) : AbsMultiSelectAdapter( activity, cabHolder, R.menu.menu_media_selection ), PopupTextProvider { @@ -106,12 +107,12 @@ class ArtistAdapter( } override fun onMultipleItemAction( - menuItem: MenuItem, selection: ArrayList + menuItem: MenuItem, selection: List ) { SongsMenuHelper.handleMenuClick(activity, getSongList(selection), menuItem.itemId) } - private fun getSongList(artists: List): ArrayList { + private fun getSongList(artists: List): List { val songs = ArrayList() for (artist in artists) { songs.addAll(artist.songs) // maybe async in future? @@ -130,7 +131,6 @@ class ArtistAdapter( inner class ViewHolder(itemView: View) : MediaEntryViewHolder(itemView) { init { - setImageTransitionName(activity.getString(R.string.transition_artist_image)) menu?.visibility = View.GONE } @@ -139,14 +139,13 @@ class ArtistAdapter( if (isInQuickSelectMode) { toggleChecked(layoutPosition) } else { - val activityOptions = ActivityOptions.makeSceneTransitionAnimation( - activity, - imageContainerCard ?: image, - activity.getString(R.string.transition_artist_image) - ) - NavigationUtil.goToArtistOptions( - activity, dataSet[layoutPosition].id, activityOptions - ) + image?.let { + ViewCompat.setTransitionName( + it, + activity.getString(R.string.transition_artist_image) + ) + artistClickListener.onArtist(dataSet[layoutPosition].id, it) + } } } diff --git a/app/src/main/java/io/github/muntashirakon/music/adapter/base/AbsMultiSelectAdapter.java b/app/src/main/java/io/github/muntashirakon/music/adapter/base/AbsMultiSelectAdapter.java index 59e48087a..1cdb9a531 100644 --- a/app/src/main/java/io/github/muntashirakon/music/adapter/base/AbsMultiSelectAdapter.java +++ b/app/src/main/java/io/github/muntashirakon/music/adapter/base/AbsMultiSelectAdapter.java @@ -12,6 +12,7 @@ import androidx.recyclerview.widget.RecyclerView; import com.afollestad.materialcab.MaterialCab; import java.util.ArrayList; +import java.util.List; import io.github.muntashirakon.music.R; import io.github.muntashirakon.music.interfaces.CabHolder; @@ -24,7 +25,7 @@ public abstract class AbsMultiSelectAdapter checked; + private List checked; private int menuRes; public AbsMultiSelectAdapter(@NonNull Context context, @Nullable CabHolder cabHolder, @MenuRes int menuRes) { @@ -86,7 +87,7 @@ public abstract class AbsMultiSelectAdapter selection); + protected abstract void onMultipleItemAction(MenuItem menuItem, List selection); protected void setMultiSelectMenuRes(@MenuRes int menuRes) { this.menuRes = menuRes; diff --git a/app/src/main/java/io/github/muntashirakon/music/adapter/playlist/PlaylistAdapter.kt b/app/src/main/java/io/github/muntashirakon/music/adapter/playlist/PlaylistAdapter.kt index 165351398..223cff33d 100755 --- a/app/src/main/java/io/github/muntashirakon/music/adapter/playlist/PlaylistAdapter.kt +++ b/app/src/main/java/io/github/muntashirakon/music/adapter/playlist/PlaylistAdapter.kt @@ -9,11 +9,14 @@ import android.view.LayoutInflater import android.view.MenuItem import android.view.View import android.view.ViewGroup -import androidx.appcompat.app.AppCompatActivity 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.util.ATHUtil 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.adapter.base.AbsMultiSelectAdapter 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.SongsMenuHelper 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.Playlist import io.github.muntashirakon.music.model.Song 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.MusicUtil -import io.github.muntashirakon.music.util.NavigationUtil import io.github.muntashirakon.music.util.RetroColorUtil import java.util.* class PlaylistAdapter( - private val activity: AppCompatActivity, + private val activity: FragmentActivity, var dataSet: List, private var itemLayoutRes: Int, cabHolder: CabHolder? @@ -120,7 +122,7 @@ class PlaylistAdapter( return playlist.name } - override fun onMultipleItemAction(menuItem: MenuItem, selection: ArrayList) { + override fun onMultipleItemAction(menuItem: MenuItem, selection: List) { when (menuItem.itemId) { else -> SongsMenuHelper.handleMenuClick( activity, @@ -130,11 +132,11 @@ class PlaylistAdapter( } } - private fun getSongList(playlists: List): ArrayList { + private fun getSongList(playlists: List): List { val songs = ArrayList() for (playlist in playlists) { if (playlist is AbsCustomPlaylist) { - songs.addAll(playlist.getSongs(activity)) + songs.addAll(playlist.songs()) } else { songs.addAll(PlaylistSongsLoader.getPlaylistSongList(activity, playlist.id)) } @@ -142,12 +144,12 @@ class PlaylistAdapter( return songs } - private fun getSongs(playlist: Playlist): ArrayList { + private fun getSongs(playlist: Playlist): List { val songs = ArrayList() if (playlist is AbsSmartPlaylist) { - songs.addAll(playlist.getSongs(activity)) + songs.addAll(playlist.songs()) } else { - songs.addAll(PlaylistSongsLoader.getPlaylistSongList(activity, playlist.id)) + songs.addAll(playlist.getSongs()) } return songs } @@ -165,9 +167,7 @@ class PlaylistAdapter( val popupMenu = PopupMenu(activity, view) popupMenu.inflate(R.menu.menu_item_playlist) popupMenu.setOnMenuItemClickListener { item -> - PlaylistMenuHelper.handleMenuClick( - activity, dataSet[layoutPosition], item - ) + PlaylistMenuHelper.handleMenuClick(activity, dataSet[layoutPosition], item) } popupMenu.show() } @@ -182,8 +182,10 @@ class PlaylistAdapter( if (isInQuickSelectMode) { toggleChecked(layoutPosition) } else { - val playlist = dataSet[layoutPosition] - NavigationUtil.goToPlaylistNew(activity, playlist) + activity.findNavController(R.id.fragment_container).navigate( + R.id.playlistDetailsFragment, + bundleOf(EXTRA_PLAYLIST to dataSet[layoutPosition]) + ) } } diff --git a/app/src/main/java/io/github/muntashirakon/music/adapter/song/OrderablePlaylistSongAdapter.kt b/app/src/main/java/io/github/muntashirakon/music/adapter/song/OrderablePlaylistSongAdapter.kt index 030daf3b8..0bc4efe30 100644 --- a/app/src/main/java/io/github/muntashirakon/music/adapter/song/OrderablePlaylistSongAdapter.kt +++ b/app/src/main/java/io/github/muntashirakon/music/adapter/song/OrderablePlaylistSongAdapter.kt @@ -2,7 +2,11 @@ package io.github.muntashirakon.music.adapter.song import android.view.MenuItem 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.menu 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.Song 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( - activity: AppCompatActivity, + activity: FragmentActivity, dataSet: ArrayList, itemLayoutRes: Int, cabHolder: CabHolder?, private val onMoveItemListener: OnMoveItemListener? -) : PlaylistSongAdapter( - activity, dataSet, itemLayoutRes, cabHolder +) : SongAdapter( + activity, + dataSet, + itemLayoutRes, + cabHolder ), DraggableItemAdapter { init { @@ -48,7 +51,7 @@ class OrderablePlaylistSongAdapter( return long } - override fun onMultipleItemAction(menuItem: MenuItem, selection: ArrayList) { + override fun onMultipleItemAction(menuItem: MenuItem, selection: List) { when (menuItem.itemId) { R.id.action_remove_from_playlist -> { RemoveFromPlaylistDialog.create(selection as ArrayList) @@ -91,7 +94,7 @@ class OrderablePlaylistSongAdapter( fun onMoveItem(fromPosition: Int, toPosition: Int) } - inner class ViewHolder(itemView: View) : PlaylistSongAdapter.ViewHolder(itemView), + inner class ViewHolder(itemView: View) : SongAdapter.ViewHolder(itemView), DraggableItemViewHolder { @DraggableItemStateFlags private var mDragStateFlags: Int = 0 @@ -132,8 +135,4 @@ class OrderablePlaylistSongAdapter( mDragStateFlags = flags } } - - companion object { - val TAG: String = OrderablePlaylistSongAdapter::class.java.simpleName - } } diff --git a/app/src/main/java/io/github/muntashirakon/music/adapter/song/PlayingQueueAdapter.kt b/app/src/main/java/io/github/muntashirakon/music/adapter/song/PlayingQueueAdapter.kt index b58df715e..4f3c1891a 100644 --- a/app/src/main/java/io/github/muntashirakon/music/adapter/song/PlayingQueueAdapter.kt +++ b/app/src/main/java/io/github/muntashirakon/music/adapter/song/PlayingQueueAdapter.kt @@ -3,14 +3,7 @@ package io.github.muntashirakon.music.adapter.song import android.view.MenuItem import android.view.View import androidx.appcompat.app.AppCompatActivity -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 androidx.fragment.app.FragmentActivity import com.h6ah4i.android.widget.advrecyclerview.draggable.DraggableItemAdapter import com.h6ah4i.android.widget.advrecyclerview.draggable.ItemDraggableRange 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.SwipeResultActionRemoveItem 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 class PlayingQueueAdapter( @@ -192,7 +193,7 @@ class PlayingQueueAdapter( internal class SwipedResultActionRemoveItem( private val adapter: PlayingQueueAdapter, private val position: Int, - private val activity: AppCompatActivity + private val activity: FragmentActivity ) : SwipeResultActionRemoveItem() { private var songToRemove: Song? = null diff --git a/app/src/main/java/io/github/muntashirakon/music/adapter/song/PlaylistSongAdapter.kt b/app/src/main/java/io/github/muntashirakon/music/adapter/song/PlaylistSongAdapter.kt index e3aa67fec..4a73c7441 100644 --- a/app/src/main/java/io/github/muntashirakon/music/adapter/song/PlaylistSongAdapter.kt +++ b/app/src/main/java/io/github/muntashirakon/music/adapter/song/PlaylistSongAdapter.kt @@ -1,15 +1,16 @@ package io.github.muntashirakon.music.adapter.song -import android.app.ActivityOptions import android.view.MenuItem import android.view.View 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.helper.MusicPlayerRemote import io.github.muntashirakon.music.interfaces.CabHolder import io.github.muntashirakon.music.model.Song -import io.github.muntashirakon.music.util.NavigationUtil -import com.google.android.material.button.MaterialButton open class PlaylistSongAdapter( activity: AppCompatActivity, @@ -57,19 +58,14 @@ open class PlaylistSongAdapter( override fun onSongMenuItemClick(item: MenuItem): Boolean { if (item.itemId == R.id.action_go_to_album) { - val activityOptions = ActivityOptions.makeSceneTransitionAnimation( - activity, - imageContainerCard ?: image, - activity.getString(R.string.transition_album_art) - ) - NavigationUtil.goToAlbumOptions(activity, song.albumId, activityOptions) + activity.findNavController(R.id.fragment_container) + .navigate( + R.id.albumDetailsFragment, + bundleOf(EXTRA_ALBUM_ID to song.albumId) + ) return true } return super.onSongMenuItemClick(item) } } - - companion object { - val TAG: String = PlaylistSongAdapter::class.java.simpleName - } } \ No newline at end of file diff --git a/app/src/main/java/io/github/muntashirakon/music/adapter/song/SimpleSongAdapter.kt b/app/src/main/java/io/github/muntashirakon/music/adapter/song/SimpleSongAdapter.kt index 2ffa38428..7c7a8cbd5 100755 --- a/app/src/main/java/io/github/muntashirakon/music/adapter/song/SimpleSongAdapter.kt +++ b/app/src/main/java/io/github/muntashirakon/music/adapter/song/SimpleSongAdapter.kt @@ -2,14 +2,14 @@ package io.github.muntashirakon.music.adapter.song import android.view.LayoutInflater 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.model.Song import io.github.muntashirakon.music.util.MusicUtil import java.util.* class SimpleSongAdapter( - context: AppCompatActivity, + context: FragmentActivity, songs: ArrayList, layoutRes: Int, cabHolder: CabHolder? diff --git a/app/src/main/java/io/github/muntashirakon/music/adapter/song/SongAdapter.kt b/app/src/main/java/io/github/muntashirakon/music/adapter/song/SongAdapter.kt index 42d260b4b..4af8e3f85 100644 --- a/app/src/main/java/io/github/muntashirakon/music/adapter/song/SongAdapter.kt +++ b/app/src/main/java/io/github/muntashirakon/music/adapter/song/SongAdapter.kt @@ -1,13 +1,17 @@ package io.github.muntashirakon.music.adapter.song -import android.app.ActivityOptions import android.content.res.ColorStateList import android.content.res.Resources import android.view.LayoutInflater import android.view.MenuItem import android.view.View 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.adapter.base.AbsMultiSelectAdapter 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.model.Song 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.color.MediaNotificationProcessor -import com.afollestad.materialcab.MaterialCab -import com.bumptech.glide.Glide import me.zhanghai.android.fastscroll.PopupTextProvider /** @@ -34,7 +35,7 @@ import me.zhanghai.android.fastscroll.PopupTextProvider */ open class SongAdapter( - protected val activity: AppCompatActivity, + protected val activity: FragmentActivity, var dataSet: MutableList, protected var itemLayoutRes: Int, cabHolder: CabHolder?, @@ -132,7 +133,7 @@ open class SongAdapter( return song.title } - override fun onMultipleItemAction(menuItem: MenuItem, selection: ArrayList) { + override fun onMultipleItemAction(menuItem: MenuItem, selection: List) { SongsMenuHelper.handleMenuClick(activity, selection, menuItem.itemId) } @@ -147,7 +148,6 @@ open class SongAdapter( return "" } } - return MusicUtil.getSectionName(sectionName) } @@ -175,12 +175,11 @@ open class SongAdapter( if (image != null && image!!.visibility == View.VISIBLE) { when (item.itemId) { R.id.action_go_to_album -> { - val activityOptions = ActivityOptions.makeSceneTransitionAnimation( - activity, - imageContainerCard ?: image, - activity.getString(R.string.transition_album_art) - ) - NavigationUtil.goToAlbumOptions(activity, song.albumId, activityOptions) + activity.findNavController(R.id.fragment_container) + .navigate( + R.id.albumDetailsFragment, + bundleOf(EXTRA_ALBUM_ID to song.albumId) + ) return true } } diff --git a/app/src/main/java/io/github/muntashirakon/music/appshortcuts/AppShortcutLauncherActivity.kt b/app/src/main/java/io/github/muntashirakon/music/appshortcuts/AppShortcutLauncherActivity.kt index fc5417778..185db958b 100644 --- a/app/src/main/java/io/github/muntashirakon/music/appshortcuts/AppShortcutLauncherActivity.kt +++ b/app/src/main/java/io/github/muntashirakon/music/appshortcuts/AppShortcutLauncherActivity.kt @@ -17,15 +17,13 @@ package io.github.muntashirakon.music.appshortcuts import android.app.Activity import android.content.Intent 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.SearchShortCutType import io.github.muntashirakon.music.appshortcuts.shortcuttype.ShuffleAllShortcutType import io.github.muntashirakon.music.appshortcuts.shortcuttype.TopTracksShortcutType import io.github.muntashirakon.music.model.Playlist 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.TopTracksPlaylist import io.github.muntashirakon.music.service.MusicService import io.github.muntashirakon.music.service.MusicService.* @@ -45,26 +43,22 @@ class AppShortcutLauncherActivity : Activity() { when (shortcutType) { SHORTCUT_TYPE_SHUFFLE_ALL -> { startServiceWithPlaylist( - MusicService.SHUFFLE_MODE_SHUFFLE, ShuffleAllPlaylist(applicationContext) + SHUFFLE_MODE_SHUFFLE, ShuffleAllPlaylist() ) DynamicShortcutManager.reportShortcutUsed(this, ShuffleAllShortcutType.id) } SHORTCUT_TYPE_TOP_TRACKS -> { startServiceWithPlaylist( - MusicService.SHUFFLE_MODE_NONE, MyTopTracksPlaylist(applicationContext) + SHUFFLE_MODE_NONE, TopTracksPlaylist() ) DynamicShortcutManager.reportShortcutUsed(this, TopTracksShortcutType.id) } SHORTCUT_TYPE_LAST_ADDED -> { startServiceWithPlaylist( - MusicService.SHUFFLE_MODE_NONE, LastAddedPlaylist(applicationContext) + SHUFFLE_MODE_NONE, LastAddedPlaylist() ) DynamicShortcutManager.reportShortcutUsed(this, LastAddedShortcutType.id) } - SHORTCUT_TYPE_SEARCH -> { - startActivity(Intent(this, SearchActivity::class.java)) - DynamicShortcutManager.reportShortcutUsed(this, SearchShortCutType.id) - } } finish() } @@ -87,7 +81,6 @@ class AppShortcutLauncherActivity : Activity() { const val SHORTCUT_TYPE_SHUFFLE_ALL = 0 const val SHORTCUT_TYPE_TOP_TRACKS = 1 const val SHORTCUT_TYPE_LAST_ADDED = 2 - const val SHORTCUT_TYPE_SEARCH = 3 const val SHORTCUT_TYPE_NONE = 4 } } diff --git a/app/src/main/java/io/github/muntashirakon/music/appshortcuts/DynamicShortcutManager.kt b/app/src/main/java/io/github/muntashirakon/music/appshortcuts/DynamicShortcutManager.kt index 6cfcedba5..5c7b7b571 100644 --- a/app/src/main/java/io/github/muntashirakon/music/appshortcuts/DynamicShortcutManager.kt +++ b/app/src/main/java/io/github/muntashirakon/music/appshortcuts/DynamicShortcutManager.kt @@ -22,7 +22,6 @@ import android.content.pm.ShortcutManager import android.graphics.drawable.Icon import android.os.Build 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.TopTracksShortcutType import java.util.* @@ -34,11 +33,9 @@ class DynamicShortcutManager(private val context: Context) { private val defaultShortcuts: List get() = Arrays.asList( - SearchShortCutType(context).shortcutInfo, ShuffleAllShortcutType(context).shortcutInfo, TopTracksShortcutType(context).shortcutInfo, LastAddedShortcutType(context).shortcutInfo - ) fun initDynamicShortcuts() { diff --git a/app/src/main/java/io/github/muntashirakon/music/appshortcuts/shortcuttype/SearchShortCutType.kt b/app/src/main/java/io/github/muntashirakon/music/appshortcuts/shortcuttype/SearchShortCutType.kt deleted file mode 100644 index 4a37a4cce..000000000 --- a/app/src/main/java/io/github/muntashirakon/music/appshortcuts/shortcuttype/SearchShortCutType.kt +++ /dev/null @@ -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() -} \ No newline at end of file diff --git a/app/src/main/java/io/github/muntashirakon/music/dialogs/AddToPlaylistDialog.kt b/app/src/main/java/io/github/muntashirakon/music/dialogs/AddToPlaylistDialog.kt index 70779a683..79560c438 100644 --- a/app/src/main/java/io/github/muntashirakon/music/dialogs/AddToPlaylistDialog.kt +++ b/app/src/main/java/io/github/muntashirakon/music/dialogs/AddToPlaylistDialog.kt @@ -22,22 +22,22 @@ import io.github.muntashirakon.music.R import io.github.muntashirakon.music.extensions.colorButtons import io.github.muntashirakon.music.extensions.extraNotNull 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.repository.PlaylistRepository import io.github.muntashirakon.music.util.PlaylistsUtil +import org.koin.android.ext.android.inject class AddToPlaylistDialog : DialogFragment() { - + private val playlistRepository by inject() override fun onCreateDialog( savedInstanceState: Bundle? ): Dialog { - val playlists = PlaylistLoader.getAllPlaylists(requireContext()) + val playlists = playlistRepository.playlists() val playlistNames = mutableListOf() playlistNames.add(requireContext().resources.getString(R.string.action_new_playlist)) for (p in playlists) { playlistNames.add(p.name) } - return materialDialog(R.string.add_playlist_title) .setItems(playlistNames.toTypedArray()) { _, which -> val songs = extraNotNull>(EXTRA_SONG).value diff --git a/app/src/main/java/io/github/muntashirakon/music/dialogs/DeleteSongsDialog.kt b/app/src/main/java/io/github/muntashirakon/music/dialogs/DeleteSongsDialog.kt index 17ae9b1dc..43ba88dc5 100644 --- a/app/src/main/java/io/github/muntashirakon/music/dialogs/DeleteSongsDialog.kt +++ b/app/src/main/java/io/github/muntashirakon/music/dialogs/DeleteSongsDialog.kt @@ -98,7 +98,9 @@ class DeleteSongsDialog : DialogFragment() { } fun deleteSongs(songs: List, safUris: List?) { - MusicUtil.deleteTracks(requireActivity(), songs, safUris) { this.dismiss() } + MusicUtil.deleteTracks(requireActivity(), songs, safUris, Runnable { + dismiss() + }) } companion object { diff --git a/app/src/main/java/io/github/muntashirakon/music/extensions/ColorExt.kt b/app/src/main/java/io/github/muntashirakon/music/extensions/ColorExt.kt index 7e9c60bcb..3f93d4d3c 100644 --- a/app/src/main/java/io/github/muntashirakon/music/extensions/ColorExt.kt +++ b/app/src/main/java/io/github/muntashirakon/music/extensions/ColorExt.kt @@ -22,11 +22,17 @@ import android.widget.Button import android.widget.CheckBox import android.widget.SeekBar import androidx.annotation.AttrRes +import androidx.annotation.ColorInt import androidx.appcompat.widget.Toolbar import androidx.fragment.app.Fragment 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 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.R @@ -83,4 +89,45 @@ fun SeekBar.addAccentColor() { fun Button.accentTextColor() { 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() { + } \ No newline at end of file diff --git a/app/src/main/java/io/github/muntashirakon/music/extensions/DimenExtension.kt b/app/src/main/java/io/github/muntashirakon/music/extensions/DimenExtension.kt new file mode 100644 index 000000000..836f2e47a --- /dev/null +++ b/app/src/main/java/io/github/muntashirakon/music/extensions/DimenExtension.kt @@ -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 +} \ No newline at end of file diff --git a/app/src/main/java/io/github/muntashirakon/music/extensions/FragmentExt.kt b/app/src/main/java/io/github/muntashirakon/music/extensions/FragmentExt.kt index 7da29401a..7c300f54a 100644 --- a/app/src/main/java/io/github/muntashirakon/music/extensions/FragmentExt.kt +++ b/app/src/main/java/io/github/muntashirakon/music/extensions/FragmentExt.kt @@ -3,7 +3,10 @@ package io.github.muntashirakon.music.extensions import android.content.Context import android.content.res.Configuration import android.os.PowerManager +import android.widget.Toast +import androidx.annotation.IdRes import androidx.annotation.IntegerRes +import androidx.annotation.StringRes import androidx.appcompat.app.AppCompatActivity import androidx.fragment.app.Fragment import androidx.fragment.app.FragmentManager @@ -51,5 +54,23 @@ fun AppCompatActivity.currentFragment(navHostId: Int): Fragment? { val navHostFragment: NavHostFragment = supportFragmentManager.findFragmentById(navHostId) as NavHostFragment navHostFragment.targetFragment - return navHostFragment?.childFragmentManager?.fragments?.first() + return navHostFragment.childFragmentManager.fragments.first() } + +@Suppress("UNCHECKED_CAST") +fun AppCompatActivity.whichFragment(@IdRes id: Int): T { + return supportFragmentManager.findFragmentById(id) as T +} + +@Suppress("UNCHECKED_CAST") +fun 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() +} \ No newline at end of file diff --git a/app/src/main/java/io/github/muntashirakon/music/extensions/NavigationExtensions.kt b/app/src/main/java/io/github/muntashirakon/music/extensions/NavigationExtensions.kt index 17cfbe196..7b2d0473f 100644 --- a/app/src/main/java/io/github/muntashirakon/music/extensions/NavigationExtensions.kt +++ b/app/src/main/java/io/github/muntashirakon/music/extensions/NavigationExtensions.kt @@ -4,17 +4,22 @@ import androidx.annotation.IdRes import androidx.appcompat.app.AppCompatActivity import androidx.fragment.app.Fragment import androidx.navigation.NavController +import androidx.navigation.findNavController import androidx.navigation.fragment.NavHostFragment import androidx.navigation.fragment.findNavController 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 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 return fragment.navController } \ No newline at end of file diff --git a/app/src/main/java/io/github/muntashirakon/music/extensions/ViewExtensions.kt b/app/src/main/java/io/github/muntashirakon/music/extensions/ViewExtensions.kt index f34def9c0..bc75abf41 100644 --- a/app/src/main/java/io/github/muntashirakon/music/extensions/ViewExtensions.kt +++ b/app/src/main/java/io/github/muntashirakon/music/extensions/ViewExtensions.kt @@ -14,13 +14,10 @@ package io.github.muntashirakon.music.extensions -import android.content.res.ColorStateList import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import android.widget.EditText -import android.widget.SeekBar -import androidx.annotation.ColorInt import androidx.annotation.LayoutRes import code.name.monkey.appthemehelper.ThemeStore import code.name.monkey.appthemehelper.util.TintHelper @@ -48,9 +45,3 @@ fun EditText.appHandleColor(): EditText { TintHelper.colorHandles(this, ThemeStore.accentColor(context)) return this } - -fun SeekBar.applyColor(@ColorInt color: Int) { - thumbTintList = ColorStateList.valueOf(color) - progressTintList = ColorStateList.valueOf(color) - progressBackgroundTintList = ColorStateList.valueOf(color) -} \ No newline at end of file diff --git a/app/src/main/java/io/github/muntashirakon/music/fragments/DetailListFragment.kt b/app/src/main/java/io/github/muntashirakon/music/fragments/DetailListFragment.kt new file mode 100644 index 000000000..f90145eca --- /dev/null +++ b/app/src/main/java/io/github/muntashirakon/music/fragments/DetailListFragment.kt @@ -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() + private val repository by inject() + + 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, + 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): ArtistAdapter = ArtistAdapter( + requireActivity(), + artists, + R.layout.item_grid_circle, + null, this@DetailListFragment + ) + + private fun albumAdapter(albums: List): 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) { + + } +} \ No newline at end of file diff --git a/app/src/main/java/io/github/muntashirakon/music/fragments/LibraryViewModel.kt b/app/src/main/java/io/github/muntashirakon/music/fragments/LibraryViewModel.kt index 149e17f69..6406d0ffc 100644 --- a/app/src/main/java/io/github/muntashirakon/music/fragments/LibraryViewModel.kt +++ b/app/src/main/java/io/github/muntashirakon/music/fragments/LibraryViewModel.kt @@ -4,27 +4,26 @@ import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope -import io.github.muntashirakon.music.adapter.HomeAdapter import io.github.muntashirakon.music.fragments.ReloadType.* import io.github.muntashirakon.music.interfaces.MusicServiceEventListener 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.Dispatchers.IO import kotlinx.coroutines.async import kotlinx.coroutines.launch class LibraryViewModel( - private val repository: RepositoryImpl + private val realRepository: RealRepository ) : ViewModel(), MusicServiceEventListener { + private val paletteColor = MutableLiveData() private val albums = MutableLiveData>() private val songs = MutableLiveData>() private val artists = MutableLiveData>() private val playlists = MutableLiveData>() private val genres = MutableLiveData>() private val home = MutableLiveData>() - private val paletteColor = MutableLiveData() val paletteColorLiveData: LiveData = paletteColor val homeLiveData: LiveData> = home @@ -46,59 +45,36 @@ class LibraryViewModel( artists.value = loadArtists.await() playlists.value = loadPlaylists.await() genres.value = loadGenres.await() - loadHomeSections() + home.value = loadHome.await() } - private fun loadHomeSections() = viewModelScope.launch { - val list = mutableListOf() - 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 loadHome: Deferred> + get() = viewModelScope.async { realRepository.homeSections() } private val loadSongs: Deferred> - get() = viewModelScope.async(IO) { - repository.allSongs() - } + get() = viewModelScope.async(IO) { realRepository.allSongs() } private val loadAlbums: Deferred> get() = viewModelScope.async(IO) { - repository.allAlbums() + realRepository.allAlbums() } private val loadArtists: Deferred> get() = viewModelScope.async(IO) { - repository.allArtists() + realRepository.albumArtists() } private val loadPlaylists: Deferred> get() = viewModelScope.async(IO) { - repository.allPlaylists() + realRepository.allPlaylists() } private val loadGenres: Deferred> get() = viewModelScope.async(IO) { - repository.allGenres() + realRepository.allGenres() } + fun forceReload(reloadType: ReloadType) = viewModelScope.launch { when (reloadType) { Songs -> songs.value = loadSongs.await() @@ -114,15 +90,37 @@ class LibraryViewModel( override fun onMediaStoreChanged() { loadLibraryContent() + println("onMediaStoreChanged") } - override fun onServiceConnected() {} - override fun onServiceDisconnected() {} - override fun onQueueChanged() {} - override fun onPlayingMetaChanged() {} - override fun onPlayStateChanged() {} - override fun onRepeatModeChanged() {} - override fun onShuffleModeChanged() {} + + override fun onServiceConnected() { + println("onServiceConnected") + } + + override fun onServiceDisconnected() { + 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") + } } diff --git a/app/src/main/java/io/github/muntashirakon/music/activities/AboutActivity.kt b/app/src/main/java/io/github/muntashirakon/music/fragments/about/AboutFragment.kt similarity index 55% rename from app/src/main/java/io/github/muntashirakon/music/activities/AboutActivity.kt rename to app/src/main/java/io/github/muntashirakon/music/fragments/about/AboutFragment.kt index b097f9b1f..b76f8cb32 100644 --- a/app/src/main/java/io/github/muntashirakon/music/activities/AboutActivity.kt +++ b/app/src/main/java/io/github/muntashirakon/music/fragments/about/AboutFragment.kt @@ -1,32 +1,21 @@ -package io.github.muntashirakon.music.activities +package io.github.muntashirakon.music.fragments.about import android.content.Intent import android.content.pm.PackageManager import android.net.Uri import android.os.Bundle -import android.view.MenuItem import android.view.View import androidx.core.app.ShareCompat +import androidx.fragment.app.Fragment import androidx.recyclerview.widget.DefaultItemAnimator 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.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_other.* 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.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? get() { val json: String try { - val inputStream = assets.open("contributors.json") + val inputStream = requireActivity().assets.open("contributors.json") val size = inputStream.available() val buffer = ByteArray(size) inputStream.read(buffer) @@ -53,27 +48,6 @@ class AboutActivity : AbsBaseActivity(), View.OnClickListener { 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) { val i = Intent(Intent.ACTION_VIEW) @@ -89,6 +63,7 @@ class AboutActivity : AbsBaseActivity(), View.OnClickListener { appRate.setOnClickListener(this) appTranslation.setOnClickListener(this) appShare.setOnClickListener(this) + donateLink.setOnClickListener(this) instagramLink.setOnClickListener(this) twitterLink.setOnClickListener(this) changelog.setOnClickListener(this) @@ -99,25 +74,26 @@ class AboutActivity : AbsBaseActivity(), View.OnClickListener { override fun onClick(view: View) { when (view.id) { - R.id.pinterestLink -> openUrl(PINTEREST) - R.id.faqLink -> openUrl(FAQ_LINK) - R.id.telegramLink -> openUrl(APP_TELEGRAM_LINK) - R.id.appGithub -> openUrl(GITHUB_PROJECT) - R.id.appTranslation -> openUrl(TRANSLATE) - R.id.appRate -> openUrl(RATE_ON_GOOGLE_PLAY) + R.id.pinterestLink -> openUrl(Constants.PINTEREST) + R.id.faqLink -> openUrl(Constants.FAQ_LINK) + R.id.telegramLink -> openUrl(Constants.APP_TELEGRAM_LINK) + R.id.appGithub -> openUrl(Constants.GITHUB_PROJECT) + R.id.appTranslation -> openUrl(Constants.TRANSLATE) + R.id.appRate -> openUrl(Constants.RATE_ON_GOOGLE_PLAY) R.id.appShare -> shareApp() - R.id.instagramLink -> openUrl(APP_INSTAGRAM_LINK) - R.id.twitterLink -> openUrl(APP_TWITTER_LINK) - R.id.changelog -> openUrl(TELEGRAM_CHANGE_LOG) - R.id.openSource -> NavigationUtil.goToOpenSource(this) - R.id.bugReportLink -> NavigationUtil.bugReport(this) + R.id.instagramLink -> openUrl(Constants.APP_INSTAGRAM_LINK) + R.id.twitterLink -> openUrl(Constants.APP_TWITTER_LINK) + R.id.changelog -> openUrl(Constants.TELEGRAM_CHANGE_LOG) + R.id.openSource -> NavigationUtil.goToOpenSource(requireActivity()) + R.id.bugReportLink -> NavigationUtil.bugReport(requireActivity()) } } private fun getAppVersion(): String { return try { val isPro = "Pro" - val packageInfo = packageManager.getPackageInfo(packageName, 0) + val packageInfo = + requireActivity().packageManager.getPackageInfo(requireActivity().packageName, 0) "${packageInfo.versionName} $isPro" } catch (e: PackageManager.NameNotFoundException) { e.printStackTrace() @@ -126,9 +102,10 @@ class AboutActivity : AbsBaseActivity(), View.OnClickListener { } private fun shareApp() { - ShareCompat.IntentBuilder.from(this).setType("text/plain") + ShareCompat.IntentBuilder.from(requireActivity()).setType("text/plain") .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() { @@ -138,8 +115,10 @@ class AboutActivity : AbsBaseActivity(), View.OnClickListener { val contributors = Gson().fromJson>(contributorsJson, type) val contributorAdapter = ContributorAdapter(contributors) - recyclerView.layoutManager = LinearLayoutManager(this) - recyclerView.itemAnimator = DefaultItemAnimator() - recyclerView.adapter = contributorAdapter + recyclerView.apply { + layoutManager = LinearLayoutManager(requireContext()) + itemAnimator = DefaultItemAnimator() + adapter = contributorAdapter + } } } diff --git a/app/src/main/java/io/github/muntashirakon/music/activities/albums/AlbumDetailsActivity.kt b/app/src/main/java/io/github/muntashirakon/music/fragments/albums/AlbumDetailsFragment.kt similarity index 56% rename from app/src/main/java/io/github/muntashirakon/music/activities/albums/AlbumDetailsActivity.kt rename to app/src/main/java/io/github/muntashirakon/music/fragments/albums/AlbumDetailsFragment.kt index 94e081d57..15bf1b0e9 100644 --- a/app/src/main/java/io/github/muntashirakon/music/activities/albums/AlbumDetailsActivity.kt +++ b/app/src/main/java/io/github/muntashirakon/music/fragments/albums/AlbumDetailsFragment.kt @@ -1,137 +1,103 @@ -package io.github.muntashirakon.music.activities.albums +package io.github.muntashirakon.music.fragments.albums import android.app.ActivityOptions import android.content.Intent import android.os.Bundle -import android.transition.Slide -import android.view.Menu -import android.view.MenuItem -import android.view.SubMenu -import android.view.View -import androidx.core.app.ActivityCompat +import android.view.* +import androidx.appcompat.app.AppCompatActivity +import androidx.core.os.bundleOf +import androidx.lifecycle.Observer +import androidx.navigation.findNavController +import androidx.navigation.fragment.findNavController +import androidx.navigation.fragment.navArgs import androidx.recyclerview.widget.DefaultItemAnimator import androidx.recyclerview.widget.GridLayoutManager import androidx.recyclerview.widget.LinearLayoutManager -import code.name.monkey.appthemehelper.util.ATHUtil -import code.name.monkey.appthemehelper.util.MaterialUtil +import code.name.monkey.appthemehelper.common.ATHToolbarActivity.getToolbarBackgroundColor 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.activities.base.AbsSlidingMusicPanelActivity import io.github.muntashirakon.music.activities.tageditor.AbsTagEditorActivity import io.github.muntashirakon.music.activities.tageditor.AlbumTagEditorActivity import io.github.muntashirakon.music.adapter.album.HorizontalAlbumAdapter import io.github.muntashirakon.music.adapter.song.SimpleSongAdapter import io.github.muntashirakon.music.dialogs.AddToPlaylistDialog 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.surfaceColor +import io.github.muntashirakon.music.fragments.base.AbsMainActivityFragment 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.helper.SortOrder.AlbumSongSortOrder -import io.github.muntashirakon.music.interfaces.CabHolder +import io.github.muntashirakon.music.helper.SortOrder 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.* +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 com.afollestad.materialcab.MaterialCab -import com.bumptech.glide.Glide -import kotlinx.android.synthetic.main.activity_album.* -import kotlinx.android.synthetic.main.activity_album_content.* +import kotlinx.android.synthetic.main.fragment_album_content.* +import kotlinx.android.synthetic.main.fragment_album_details.* import org.koin.androidx.viewmodel.ext.android.viewModel import org.koin.core.parameter.parametersOf import java.util.* -import android.util.Pair as UtilPair -class AlbumDetailsActivity : AbsSlidingMusicPanelActivity(), CabHolder { - 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( - ATHUtil.resolveColor( - this, - R.attr.colorSurface - ) - ) - ) - .start(callback) - return cab as MaterialCab - } +class AlbumDetailsFragment : AbsMainActivityFragment(R.layout.fragment_album_details), + AlbumClickListener { + private val arguments by navArgs() private val detailsViewModel by viewModel { - parametersOf(extraNotNull(EXTRA_ALBUM_ID).value) + parametersOf(arguments.extraAlbumId) } + private lateinit var simpleSongAdapter: SimpleSongAdapter private lateinit var album: Album - private var cab: MaterialCab? = null + private val savedSortOrder: String get() = PreferenceUtil.albumDetailSongSortOrder - override fun createContentView(): View { - return wrapSlidingMusicPanel(R.layout.activity_album) - } + override fun onActivityCreated(savedInstanceState: Bundle?) { + super.onActivityCreated(savedInstanceState) + setHasOptionsMenu(true) + mainActivity.hideBottomBarVisibility(false) + mainActivity.addMusicServiceEventListener(detailsViewModel) + mainActivity.setSupportActionBar(toolbar) - private fun windowEnterTransition() { - 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 - } + toolbar.title = null - override fun onCreate(savedInstanceState: Bundle?) { - setDrawUnderStatusBar() - 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) + postponeEnterTransition() + detailsViewModel.getAlbum().observe(viewLifecycleOwner, Observer { showAlbum(it) + startPostponedEnterTransition() }) - detailsViewModel.getArtist().observe(this, androidx.lifecycle.Observer { + detailsViewModel.getArtist().observe(viewLifecycleOwner, Observer { loadArtistImage(it) }) - detailsViewModel.getMoreAlbums().observe(this, androidx.lifecycle.Observer { + detailsViewModel.getMoreAlbums().observe(viewLifecycleOwner, Observer { moreAlbums(it) }) - detailsViewModel.getAlbumInfo().observe(this, androidx.lifecycle.Observer { + detailsViewModel.getAlbumInfo().observe(viewLifecycleOwner, Observer { aboutAlbum(it) }) setupRecyclerView() artistImage.setOnClickListener { - val artistPairs = ActivityOptions.makeSceneTransitionAnimation( - this, - UtilPair.create( - artistImage, - getString(R.string.transition_artist_image) + requireActivity().findNavController(R.id.fragment_container) + .navigate( + R.id.artistDetailsFragment, + bundleOf(EXTRA_ARTIST_ID to album.artistId) ) + } + 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 { @@ -141,12 +107,26 @@ class AlbumDetailsActivity : AbsSlidingMusicPanelActivity(), CabHolder { aboutAlbumText.maxLines = 4 } } + image.apply { + transitionName = getString(R.string.transition_album_art) + } } + override fun onDestroy() { + super.onDestroy() + playerActivity?.removeMusicServiceEventListener(detailsViewModel) + } + + 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 { - layoutManager = LinearLayoutManager(this@AlbumDetailsActivity) + layoutManager = LinearLayoutManager(requireContext()) itemAnimator = DefaultItemAnimator() isNestedScrollingEnabled = false adapter = simpleSongAdapter @@ -155,7 +135,6 @@ class AlbumDetailsActivity : AbsSlidingMusicPanelActivity(), CabHolder { private fun showAlbum(album: Album) { if (album.songs!!.isEmpty()) { - finish() return } this.album = album @@ -194,9 +173,10 @@ class AlbumDetailsActivity : AbsSlidingMusicPanelActivity(), CabHolder { moreRecyclerView.show() 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( - this, + requireContext(), 1, GridLayoutManager.HORIZONTAL, false @@ -226,8 +206,8 @@ class AlbumDetailsActivity : AbsSlidingMusicPanelActivity(), CabHolder { } private fun loadArtistImage(artist: Artist) { - ArtistGlideRequest.Builder.from(Glide.with(this), artist) - .generatePalette(this) + ArtistGlideRequest.Builder.from(Glide.with(requireContext()), artist) + .generatePalette(requireContext()) .build() .dontAnimate() .dontTransform() @@ -238,10 +218,10 @@ class AlbumDetailsActivity : AbsSlidingMusicPanelActivity(), CabHolder { } private fun loadAlbumCover() { - AlbumGlideRequest.Builder.from(Glide.with(this), album.safeGetFirstSong()) - .checkIgnoreMediaStore(this) + AlbumGlideRequest.Builder.from(Glide.with(requireContext()), album.safeGetFirstSong()) + .checkIgnoreMediaStore(requireContext()) .ignoreMediaStore(PreferenceUtil.isIgnoreMediaStoreArtwork) - .generatePalette(this) + .generatePalette(requireContext()) .build() .dontAnimate() .dontTransform() @@ -253,32 +233,28 @@ class AlbumDetailsActivity : AbsSlidingMusicPanelActivity(), CabHolder { } 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 - ) - - setSupportActionBar(toolbar) - supportActionBar?.title = null + shuffleAction.applyColor(color.backgroundColor) + playAction.applyColor(color.backgroundColor) } - override fun onCreateOptionsMenu(menu: Menu): Boolean { - menuInflater.inflate(R.menu.menu_album_detail, menu) + override fun onAlbumClick(albumId: Int, view: View) { + 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) setUpSortOrderMenu(sortOrder.subMenu) ToolbarContentTintHelper.handleOnCreateOptionsMenu( - this, + requireContext(), toolbar, menu, getToolbarBackgroundColor(toolbar) ) - return super.onCreateOptionsMenu(menu) } override fun onOptionsItemSelected(item: MenuItem): Boolean { @@ -289,6 +265,7 @@ class AlbumDetailsActivity : AbsSlidingMusicPanelActivity(), CabHolder { var sortOrder: String? = null val songs = simpleSongAdapter.dataSet when (item.itemId) { + android.R.id.home -> findNavController().navigateUp() R.id.action_play_next -> { MusicPlayerRemote.playNext(songs) return true @@ -298,22 +275,18 @@ class AlbumDetailsActivity : AbsSlidingMusicPanelActivity(), CabHolder { return true } R.id.action_add_to_playlist -> { - AddToPlaylistDialog.create(songs).show(supportFragmentManager, "ADD_PLAYLIST") + AddToPlaylistDialog.create(songs).show(childFragmentManager, "ADD_PLAYLIST") return true } R.id.action_delete_from_device -> { - DeleteSongsDialog.create(songs).show(supportFragmentManager, "DELETE_SONGS") - return true - } - android.R.id.home -> { - super.onBackPressed() + DeleteSongsDialog.create(songs).show(childFragmentManager, "DELETE_SONGS") return true } 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) val options = ActivityOptions.makeSceneTransitionAnimation( - this, + requireActivity(), albumCoverContainer, "${getString(R.string.transition_album_art)}_${album.id}" ) @@ -324,11 +297,12 @@ class AlbumDetailsActivity : AbsSlidingMusicPanelActivity(), CabHolder { return true } /*Sort*/ - R.id.action_sort_order_title -> sortOrder = AlbumSongSortOrder.SONG_A_Z - R.id.action_sort_order_title_desc -> sortOrder = AlbumSongSortOrder.SONG_Z_A - R.id.action_sort_order_track_list -> sortOrder = AlbumSongSortOrder.SONG_TRACK_LIST + R.id.action_sort_order_title -> sortOrder = SortOrder.AlbumSongSortOrder.SONG_A_Z + R.id.action_sort_order_title_desc -> sortOrder = SortOrder.AlbumSongSortOrder.SONG_Z_A + R.id.action_sort_order_track_list -> sortOrder = + SortOrder.AlbumSongSortOrder.SONG_TRACK_LIST R.id.action_sort_order_artist_song_duration -> - sortOrder = AlbumSongSortOrder.SONG_DURATION + sortOrder = SortOrder.AlbumSongSortOrder.SONG_DURATION } if (sortOrder != null) { item.isChecked = true @@ -339,13 +313,13 @@ class AlbumDetailsActivity : AbsSlidingMusicPanelActivity(), CabHolder { private fun setUpSortOrderMenu(sortOrder: SubMenu) { 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 - 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 - 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 - 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 } } @@ -353,22 +327,22 @@ class AlbumDetailsActivity : AbsSlidingMusicPanelActivity(), CabHolder { private fun setSaveSortOrder(sortOrder: String) { PreferenceUtil.albumDetailSongSortOrder = 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( 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( 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( o1.title ) }) - AlbumSongSortOrder.SONG_DURATION -> album.songs?.sortWith(Comparator { o1, o2 -> + SortOrder.AlbumSongSortOrder.SONG_DURATION -> album.songs?.sortWith(Comparator { o1, o2 -> o1.duration.compareTo( o2.duration ) @@ -377,23 +351,7 @@ class AlbumDetailsActivity : AbsSlidingMusicPanelActivity(), CabHolder { 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 { - - const val EXTRA_ALBUM_ID = "extra_album_id" - private const val TAG_EDITOR_REQUEST = 2001 + const val TAG_EDITOR_REQUEST = 9002 } } \ No newline at end of file diff --git a/app/src/main/java/io/github/muntashirakon/music/activities/albums/AlbumDetailsViewModel.kt b/app/src/main/java/io/github/muntashirakon/music/fragments/albums/AlbumDetailsViewModel.kt similarity index 87% rename from app/src/main/java/io/github/muntashirakon/music/activities/albums/AlbumDetailsViewModel.kt rename to app/src/main/java/io/github/muntashirakon/music/fragments/albums/AlbumDetailsViewModel.kt index 2ffc2e636..65b7338b2 100644 --- a/app/src/main/java/io/github/muntashirakon/music/activities/albums/AlbumDetailsViewModel.kt +++ b/app/src/main/java/io/github/muntashirakon/music/fragments/albums/AlbumDetailsViewModel.kt @@ -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.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.Artist 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.Dispatchers import kotlinx.coroutines.async import kotlinx.coroutines.launch -import java.lang.Exception class AlbumDetailsViewModel( - private val repository: RepositoryImpl, + private val realRepository: RealRepository, private val albumId: Int ) : ViewModel(), MusicServiceEventListener { @@ -41,7 +40,7 @@ class AlbumDetailsViewModel( fun loadAlbumInfo(album: Album) = viewModelScope.launch(Dispatchers.IO) { try { - val lastFmAlbum = repository.albumInfo( + val lastFmAlbum = realRepository.albumInfo( album.artistName ?: "-", album.title ?: "-" ) _lastFmAlbum.postValue(lastFmAlbum) @@ -49,7 +48,7 @@ class AlbumDetailsViewModel( } fun loadArtist(artistId: Int) = viewModelScope.launch(Dispatchers.IO) { - val artist = repository.artistById(artistId) + val artist = realRepository.artistById(artistId) _artist.postValue(artist) artist.albums?.filter { item -> item.id != albumId }?.let { albums -> @@ -59,7 +58,7 @@ class AlbumDetailsViewModel( private val loadAlbumAsync: Deferred get() = viewModelScope.async(Dispatchers.IO) { - repository.albumById(albumId) + realRepository.albumById(albumId) } override fun onMediaStoreChanged() { diff --git a/app/src/main/java/io/github/muntashirakon/music/fragments/albums/AlbumsFragment.kt b/app/src/main/java/io/github/muntashirakon/music/fragments/albums/AlbumsFragment.kt index 1b5e0d5c0..9724b3247 100644 --- a/app/src/main/java/io/github/muntashirakon/music/fragments/albums/AlbumsFragment.kt +++ b/app/src/main/java/io/github/muntashirakon/music/fragments/albums/AlbumsFragment.kt @@ -2,32 +2,30 @@ package io.github.muntashirakon.music.fragments.albums import android.os.Bundle import android.view.View +import androidx.core.os.bundleOf import androidx.lifecycle.Observer +import androidx.navigation.findNavController +import androidx.navigation.fragment.FragmentNavigatorExtras import androidx.recyclerview.widget.GridLayoutManager +import io.github.muntashirakon.music.EXTRA_ALBUM_ID import io.github.muntashirakon.music.R import io.github.muntashirakon.music.adapter.album.AlbumAdapter import io.github.muntashirakon.music.fragments.ReloadType import io.github.muntashirakon.music.fragments.base.AbsRecyclerViewCustomGridSizeFragment -import io.github.muntashirakon.music.interfaces.MainActivityFragmentCallbacks import io.github.muntashirakon.music.util.PreferenceUtil class AlbumsFragment : AbsRecyclerViewCustomGridSizeFragment(), - MainActivityFragmentCallbacks { - - override fun handleBackPress(): Boolean { - return false - } + AlbumClickListener { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - libraryViewModel.albumsLiveData - .observe(viewLifecycleOwner, Observer { albums -> - if (albums.isNotEmpty()) - adapter?.swapDataSet(albums) - else - adapter?.swapDataSet(listOf()) - }) + libraryViewModel.albumsLiveData.observe(viewLifecycleOwner, Observer { + if (it.isNotEmpty()) + adapter?.swapDataSet(it) + else + adapter?.swapDataSet(listOf()) + }) } override val emptyMessage: Int @@ -40,10 +38,11 @@ class AlbumsFragment : override fun createAdapter(): AlbumAdapter { val dataSet = if (adapter == null) ArrayList() else adapter!!.dataSet return AlbumAdapter( - mainActivity, + requireActivity(), dataSet, - itemLayoutRes(), - mainActivity + R.layout.item_grid, + null, + this ) } @@ -90,12 +89,24 @@ class AlbumsFragment : companion object { - @JvmField - var TAG: String = AlbumsFragment::class.java.simpleName - - @JvmStatic fun newInstance(): 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) +} \ No newline at end of file diff --git a/app/src/main/java/io/github/muntashirakon/music/activities/artists/ArtistDetailActivity.kt b/app/src/main/java/io/github/muntashirakon/music/fragments/artists/ArtistDetailsFragment.kt old mode 100755 new mode 100644 similarity index 55% rename from app/src/main/java/io/github/muntashirakon/music/activities/artists/ArtistDetailActivity.kt rename to app/src/main/java/io/github/muntashirakon/music/fragments/artists/ArtistDetailsFragment.kt index 53f0d5215..679be368a --- a/app/src/main/java/io/github/muntashirakon/music/activities/artists/ArtistDetailActivity.kt +++ b/app/src/main/java/io/github/muntashirakon/music/fragments/artists/ArtistDetailsFragment.kt @@ -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.os.Bundle import android.text.Spanned -import android.transition.Slide import android.view.Menu +import android.view.MenuInflater import android.view.MenuItem import android.view.View -import android.widget.Toast -import androidx.core.app.ActivityCompat +import androidx.core.os.bundleOf 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.GridLayoutManager import androidx.recyclerview.widget.LinearLayoutManager -import code.name.monkey.appthemehelper.util.ATHUtil -import code.name.monkey.appthemehelper.util.MaterialUtil +import com.bumptech.glide.Glide 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.song.SimpleSongAdapter 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.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.RetroMusicColoredTarget 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.network.model.LastFmArtist import io.github.muntashirakon.music.util.CustomArtistImageUtil 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.color.MediaNotificationProcessor -import com.afollestad.materialcab.MaterialCab -import com.bumptech.glide.Glide -import kotlinx.android.synthetic.main.activity_artist_content.* -import kotlinx.android.synthetic.main.activity_artist_details.* +import kotlinx.android.synthetic.main.fragment_artist_content.* +import kotlinx.android.synthetic.main.fragment_artist_details.* import org.koin.androidx.viewmodel.ext.android.viewModel import org.koin.core.parameter.parametersOf import java.util.* import kotlin.collections.ArrayList -class ArtistDetailActivity : AbsSlidingMusicPanelActivity(), CabHolder { - 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( - ATHUtil.resolveColor( - this, - R.attr.colorSurface - ) - ) - ) - .start(callback) - return cab as MaterialCab +class ArtistDetailsFragment : AbsMainActivityFragment(R.layout.fragment_artist_details), + AlbumClickListener { + private val arguments by navArgs() + private val detailsViewModel: ArtistDetailsViewModel by viewModel { + parametersOf(arguments.extraArtistId) } - private var cab: MaterialCab? = null - private var biography: Spanned? = null private lateinit var artist: Artist private lateinit var songAdapter: SimpleSongAdapter private lateinit var albumAdapter: HorizontalAlbumAdapter private var forceDownload: Boolean = false - private val detailsViewModel: ArtistDetailsViewModel by viewModel { - parametersOf(extraNotNull(EXTRA_ARTIST_ID).value) - } + private var lang: String? = null + private var biography: Spanned? = null - override fun createContentView(): View { - return wrapSlidingMusicPanel(R.layout.activity_artist_details) - } - private fun windowEnterTransition() { - 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 onActivityCreated(savedInstanceState: Bundle?) { + super.onActivityCreated(savedInstanceState) + setHasOptionsMenu(true) + mainActivity.setSupportActionBar(toolbar) + mainActivity.hideBottomBarVisibility(false) + toolbar.title = null + setupRecyclerView() + postponeEnterTransition() + detailsViewModel.getArtist().observe(viewLifecycleOwner, Observer { - override fun onCreate(savedInstanceState: Bundle?) { - setDrawUnderStatusBar() - 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) + showArtist(it) + startPostponedEnterTransition() }) - detailsViewModel.getArtistInfo().observe(this, androidx.lifecycle.Observer { + detailsViewModel.getArtistInfo().observe(viewLifecycleOwner, Observer { artistInfo(it) }) - setupRecyclerView() + playAction.apply { setOnClickListener { MusicPlayerRemote.openQueue(artist.songs, 0, true) } } @@ -127,13 +91,13 @@ class ArtistDetailActivity : AbsSlidingMusicPanelActivity(), CabHolder { } private fun setupRecyclerView() { - albumAdapter = HorizontalAlbumAdapter(this, ArrayList(), null) + albumAdapter = HorizontalAlbumAdapter(requireActivity(), ArrayList(), null, this) albumRecyclerView.apply { itemAnimator = DefaultItemAnimator() layoutManager = GridLayoutManager(this.context, 1, GridLayoutManager.HORIZONTAL, false) adapter = albumAdapter } - songAdapter = SimpleSongAdapter(this, ArrayList(), R.layout.item_song, this) + songAdapter = SimpleSongAdapter(requireActivity(), ArrayList(), R.layout.item_song, null) recyclerView.apply { itemAnimator = DefaultItemAnimator() layoutManager = LinearLayoutManager(this.context) @@ -141,33 +105,16 @@ class ArtistDetailActivity : AbsSlidingMusicPanelActivity(), CabHolder { } } - override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { - 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() - } + fun showArtist(artist: Artist) { this.artist = artist loadArtistImage(artist) - if (RetroUtil.isAllowedToDownloadMetadata(this)) { + if (RetroUtil.isAllowedToDownloadMetadata(requireContext())) { loadBiography(artist.name) } artistTitle.text = artist.name text.text = String.format( "%s • %s", - MusicUtil.getArtistInfoString(this, artist), + MusicUtil.getArtistInfoString(requireContext(), artist), MusicUtil.getReadableDurationString(MusicUtil.getTotalDuration(artist.songs)) ) val songText = @@ -184,7 +131,7 @@ class ArtistDetailActivity : AbsSlidingMusicPanelActivity(), CabHolder { ) songTitle.text = songText albumTitle.text = albumText - songAdapter.swapDataSet(artist.songs) + songAdapter.swapDataSet(artist.songs.sortedBy { it.trackNumber }) artist.albums?.let { albumAdapter.swapDataSet(it) } } @@ -224,31 +171,32 @@ class ArtistDetailActivity : AbsSlidingMusicPanelActivity(), CabHolder { } } - private var lang: String? = null private fun loadArtistImage(artist: Artist) { - ArtistGlideRequest.Builder.from(Glide.with(this), artist) - .generatePalette(this).build() + ArtistGlideRequest.Builder.from(Glide.with(requireContext()), artist) + .generatePalette(requireContext()).build() .dontAnimate().into(object : RetroMusicColoredTarget(image) { override fun onColorReady(colors: MediaNotificationProcessor) { + startPostponedEnterTransition() setColors(colors) } }) } private fun setColors(color: MediaNotificationProcessor) { - MaterialUtil.tintColor( - button = shuffleAction, - textColor = color.primaryTextColor, - backgroundColor = color.backgroundColor + shuffleAction.applyColor(color.backgroundColor) + playAction.applyColor(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 { @@ -258,10 +206,7 @@ class ArtistDetailActivity : AbsSlidingMusicPanelActivity(), CabHolder { private fun handleSortOrderMenuItem(item: MenuItem): Boolean { val songs = artist.songs when (item.itemId) { - android.R.id.home -> { - super.onBackPressed() - return true - } + android.R.id.home -> findNavController().navigateUp() R.id.action_play_next -> { MusicPlayerRemote.playNext(songs) return true @@ -271,7 +216,7 @@ class ArtistDetailActivity : AbsSlidingMusicPanelActivity(), CabHolder { return true } R.id.action_add_to_playlist -> { - AddToPlaylistDialog.create(songs).show(supportFragmentManager, "ADD_PLAYLIST") + AddToPlaylistDialog.create(songs).show(childFragmentManager, "ADD_PLAYLIST") return true } R.id.action_set_artist_image -> { @@ -284,14 +229,8 @@ class ArtistDetailActivity : AbsSlidingMusicPanelActivity(), CabHolder { return true } R.id.action_reset_artist_image -> { - Toast.makeText( - this@ArtistDetailActivity, - resources.getString(R.string.updating), - Toast.LENGTH_SHORT - ) - .show() - CustomArtistImageUtil.getInstance(this@ArtistDetailActivity) - .resetCustomArtistImage(artist) + showToast(resources.getString(R.string.updating)) + CustomArtistImageUtil.getInstance(requireContext()).resetCustomArtistImage(artist) forceDownload = true return true } @@ -299,26 +238,12 @@ class ArtistDetailActivity : AbsSlidingMusicPanelActivity(), CabHolder { return true } - override fun onCreateOptionsMenu(menu: Menu): Boolean { - menuInflater.inflate(R.menu.menu_artist_detail, menu) - return super.onCreateOptionsMenu(menu) - } - - override fun onBackPressed() { - if (cab != null && cab!!.isActive) { - cab?.finish() - } else { - super.onBackPressed() - } - } - - override fun onDestroy() { - super.onDestroy() - removeMusicServiceEventListener(detailsViewModel) + override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) { + super.onCreateOptionsMenu(menu, inflater) + inflater.inflate(R.menu.menu_artist_detail, menu) } companion object { - const val EXTRA_ARTIST_ID = "extra_artist_id" - const val REQUEST_CODE_SELECT_IMAGE = 9003 + const val REQUEST_CODE_SELECT_IMAGE = 9002 } -} +} \ No newline at end of file diff --git a/app/src/main/java/io/github/muntashirakon/music/activities/artists/ArtistDetailsViewModel.kt b/app/src/main/java/io/github/muntashirakon/music/fragments/artists/ArtistDetailsViewModel.kt similarity index 86% rename from app/src/main/java/io/github/muntashirakon/music/activities/artists/ArtistDetailsViewModel.kt rename to app/src/main/java/io/github/muntashirakon/music/fragments/artists/ArtistDetailsViewModel.kt index e088921cf..06a6d15e2 100644 --- a/app/src/main/java/io/github/muntashirakon/music/activities/artists/ArtistDetailsViewModel.kt +++ b/app/src/main/java/io/github/muntashirakon/music/fragments/artists/ArtistDetailsViewModel.kt @@ -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.MutableLiveData @@ -6,21 +6,21 @@ import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import io.github.muntashirakon.music.interfaces.MusicServiceEventListener 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.repository.RealRepository import kotlinx.coroutines.Deferred import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.async import kotlinx.coroutines.launch class ArtistDetailsViewModel( - private val repository: RepositoryImpl, + private val realRepository: RealRepository, private val artistId: Int ) : ViewModel(), MusicServiceEventListener { private val loadArtistDetailsAsync: Deferred get() = viewModelScope.async(Dispatchers.IO) { - repository.artistById(artistId) + realRepository.artistById(artistId) } private val _artist = MutableLiveData() @@ -40,7 +40,7 @@ class ArtistDetailsViewModel( } 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) } diff --git a/app/src/main/java/io/github/muntashirakon/music/fragments/artists/ArtistsFragment.kt b/app/src/main/java/io/github/muntashirakon/music/fragments/artists/ArtistsFragment.kt index 802938b97..bb641ff98 100644 --- a/app/src/main/java/io/github/muntashirakon/music/fragments/artists/ArtistsFragment.kt +++ b/app/src/main/java/io/github/muntashirakon/music/fragments/artists/ArtistsFragment.kt @@ -2,10 +2,14 @@ package io.github.muntashirakon.music.fragments.artists import android.os.Bundle import android.view.View +import android.widget.ImageView +import androidx.core.os.bundleOf import androidx.lifecycle.Observer import androidx.recyclerview.widget.GridLayoutManager +import io.github.muntashirakon.music.EXTRA_ARTIST_ID import io.github.muntashirakon.music.R 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.base.AbsRecyclerViewCustomGridSizeFragment import io.github.muntashirakon.music.interfaces.MainActivityFragmentCallbacks @@ -13,7 +17,7 @@ import io.github.muntashirakon.music.util.PreferenceUtil class ArtistsFragment : AbsRecyclerViewCustomGridSizeFragment(), - MainActivityFragmentCallbacks { + MainActivityFragmentCallbacks, ArtistClickListener { override fun handleBackPress(): Boolean { return false @@ -22,14 +26,12 @@ class ArtistsFragment : override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - libraryViewModel.artistsLiveData - .observe(viewLifecycleOwner, Observer { artists -> - if (artists.isNotEmpty()) { - adapter?.swapDataSet(artists) - } else { - adapter?.swapDataSet(listOf()) - } - }) + libraryViewModel.artistsLiveData.observe(viewLifecycleOwner, Observer { + if (it.isNotEmpty()) + adapter?.swapDataSet(it) + else + adapter?.swapDataSet(listOf()) + }) } override val emptyMessage: Int @@ -46,10 +48,11 @@ class ArtistsFragment : override fun createAdapter(): ArtistAdapter { val dataSet = if (adapter == null) ArrayList() else adapter!!.dataSet return ArtistAdapter( - mainActivity, + requireActivity(), dataSet, - itemLayoutRes(), - mainActivity + R.layout.item_grid_circle, + null, + this ) } @@ -91,12 +94,18 @@ class ArtistsFragment : } companion object { - @JvmField - val TAG: String = ArtistsFragment::class.java.simpleName - @JvmStatic fun newInstance(): 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) } \ No newline at end of file diff --git a/app/src/main/java/io/github/muntashirakon/music/fragments/base/AbsMusicServiceFragment.kt b/app/src/main/java/io/github/muntashirakon/music/fragments/base/AbsMusicServiceFragment.kt index 983db8310..bc85842cd 100644 --- a/app/src/main/java/io/github/muntashirakon/music/fragments/base/AbsMusicServiceFragment.kt +++ b/app/src/main/java/io/github/muntashirakon/music/fragments/base/AbsMusicServiceFragment.kt @@ -7,6 +7,8 @@ import android.view.View import android.webkit.MimeTypeMap import androidx.annotation.LayoutRes 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.interfaces.MusicServiceEventListener import io.github.muntashirakon.music.model.Song @@ -23,6 +25,21 @@ import java.util.* open class AbsMusicServiceFragment(@LayoutRes layout: Int) : Fragment(layout), 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 private set diff --git a/app/src/main/java/io/github/muntashirakon/music/fragments/base/AbsPlayerFragment.kt b/app/src/main/java/io/github/muntashirakon/music/fragments/base/AbsPlayerFragment.kt index 0183592ce..5e3cc98d7 100644 --- a/app/src/main/java/io/github/muntashirakon/music/fragments/base/AbsPlayerFragment.kt +++ b/app/src/main/java/io/github/muntashirakon/music/fragments/base/AbsPlayerFragment.kt @@ -14,6 +14,10 @@ import android.view.View import android.widget.Toast import androidx.annotation.LayoutRes 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.activities.tageditor.AbsTagEditorActivity 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 java.io.FileNotFoundException -abstract class AbsPlayerFragment(@LayoutRes layout: Int) : AbsMusicServiceFragment(layout), - Toolbar.OnMenuItemClickListener, - PaletteColorHolder, - PlayerAlbumCoverFragment.Callbacks { - +abstract class AbsPlayerFragment(@LayoutRes layout: Int) : AbsMainActivityFragment(layout), + Toolbar.OnMenuItemClickListener, PaletteColorHolder, PlayerAlbumCoverFragment.Callbacks { private var updateIsFavoriteTask: AsyncTask<*, *, *>? = null private var updateLyricsAsyncTask: AsyncTask<*, *, *>? = null @@ -86,11 +87,19 @@ abstract class AbsPlayerFragment(@LayoutRes layout: Int) : AbsMusicServiceFragme return true } 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 } 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 } R.id.now_playing -> { @@ -254,11 +263,6 @@ abstract class AbsPlayerFragment(@LayoutRes layout: Int) : AbsMusicServiceFragme statusBarShadow?.hide() } - interface Callbacks { - - fun onPaletteColorChanged() - } - companion object { val TAG: String = AbsPlayerFragment::class.java.simpleName const val VISIBILITY_ANIM_DURATION: Long = 300 diff --git a/app/src/main/java/io/github/muntashirakon/music/fragments/base/AbsRecyclerViewFragment.kt b/app/src/main/java/io/github/muntashirakon/music/fragments/base/AbsRecyclerViewFragment.kt index 7c6b81e86..8cc11fdbe 100644 --- a/app/src/main/java/io/github/muntashirakon/music/fragments/base/AbsRecyclerViewFragment.kt +++ b/app/src/main/java/io/github/muntashirakon/music/fragments/base/AbsRecyclerViewFragment.kt @@ -6,26 +6,24 @@ import android.view.ViewGroup import androidx.annotation.NonNull import androidx.annotation.StringRes import androidx.recyclerview.widget.RecyclerView +import com.google.android.material.appbar.AppBarLayout 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.helper.MusicPlayerRemote import io.github.muntashirakon.music.util.DensityUtil import io.github.muntashirakon.music.util.ThemedFastScroller.create 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 me.zhanghai.android.fastscroll.FastScroller import me.zhanghai.android.fastscroll.FastScrollerBuilder import org.koin.androidx.viewmodel.ext.android.sharedViewModel + abstract class AbsRecyclerViewFragment, LM : RecyclerView.LayoutManager> : AbsMusicServiceFragment(R.layout.fragment_main_activity_recycler_view), AppBarLayout.OnOffsetChangedListener { val libraryViewModel: LibraryViewModel by sharedViewModel() - val mainActivity: MainActivity - get() = requireActivity() as MainActivity override fun onActivityCreated(savedInstanceState: Bundle?) { super.onActivityCreated(savedInstanceState) @@ -38,22 +36,23 @@ abstract class AbsRecyclerViewFragment, LM : Recycle override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - mainActivity.addOnAppBarOffsetChangedListener(this) initLayoutManager() initAdapter() setUpRecyclerView() } private fun setUpRecyclerView() { - recyclerView.layoutManager = layoutManager - recyclerView.adapter = adapter - val fastScroller = create(recyclerView) - recyclerView.setOnApplyWindowInsetsListener( - ScrollingViewOnApplyWindowInsetsListener( - recyclerView, - fastScroller + recyclerView.apply { + layoutManager = this@AbsRecyclerViewFragment.layoutManager + adapter = this@AbsRecyclerViewFragment.adapter + val fastScroller = create(this) + setOnApplyWindowInsetsListener( + ScrollingViewOnApplyWindowInsetsListener( + recyclerView, + fastScroller + ) ) - ) + } checkForPadding() } @@ -112,7 +111,7 @@ abstract class AbsRecyclerViewFragment, LM : Recycle container.paddingLeft, container.paddingTop, container.paddingRight, - mainActivity.getTotalAppBarScrollingRange() + i + i ) } @@ -137,11 +136,6 @@ abstract class AbsRecyclerViewFragment, LM : Recycle recyclerView.adapter = adapter } - override fun onDestroyView() { - super.onDestroyView() - mainActivity.removeOnAppBarOffsetChangedListener(this) - } - fun recyclerView(): RecyclerView { return recyclerView } diff --git a/app/src/main/java/io/github/muntashirakon/music/fragments/folder/FoldersFragment.java b/app/src/main/java/io/github/muntashirakon/music/fragments/folder/FoldersFragment.java index eb7b7e2d6..b2415a279 100644 --- a/app/src/main/java/io/github/muntashirakon/music/fragments/folder/FoldersFragment.java +++ b/app/src/main/java/io/github/muntashirakon/music/fragments/folder/FoldersFragment.java @@ -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.SongsMenuHelper; 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.misc.DialogAsyncTask; import io.github.muntashirakon.music.misc.UpdateToastMediaScannerCompletionListener; @@ -76,7 +77,9 @@ import me.zhanghai.android.fastscroll.FastScroller; public class FoldersFragment extends AbsMainActivityFragment implements MainActivityFragmentCallbacks, - CabHolder, BreadCrumbLayout.SelectionCallback, SongFileAdapter.Callbacks, + CabHolder, + BreadCrumbLayout.SelectionCallback, + Callbacks, LoaderManager.LoaderCallbacks> { 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, "application/opus", MimeTypeMap.getSingleton()) || FileUtil.fileIsMimeType(file, "application/ogg", MimeTypeMap.getSingleton())); - public static final String PATH = "path"; + private static final String CRUMBS = "crumbs"; private static final int LOADER_ID = 5; private SongFileAdapter adapter; @@ -125,18 +128,6 @@ public class FoldersFragment extends AbsMainActivityFragment implements 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) { try { return file.getCanonicalFile(); @@ -171,7 +162,7 @@ public class FoldersFragment extends AbsMainActivityFragment implements if (savedInstanceState == null) { //noinspection ConstantConditions - setCrumb(new BreadCrumbLayout.Crumb(FileUtil.safeGetCanonicalFile((File) requireArguments().getSerializable(PATH))), true); + setCrumb(new BreadCrumbLayout.Crumb(FileUtil.safeGetCanonicalFile(PreferenceUtil.INSTANCE.getStartDirectory())), true); } else { breadCrumbs.restoreFromStateWrapper(savedInstanceState.getParcelable(CRUMBS)); getLoaderManager().initLoader(LOADER_ID, null, this); @@ -299,7 +290,7 @@ public class FoldersFragment extends AbsMainActivityFragment implements } } if (startIndex > -1) { - MusicPlayerRemote.INSTANCE.openQueue(songs, startIndex, true); + MusicPlayerRemote.openQueue(songs, startIndex, true); } else { final File finalFile = file1; Snackbar.make(coordinatorLayout, Html.fromHtml( @@ -619,7 +610,7 @@ public class FoldersFragment extends AbsMainActivityFragment implements } private static class ListSongsAsyncTask - extends ListingFilesDialogAsyncTask> { + extends ListingFilesDialogAsyncTask> { private final Object extra; private WeakReference callbackWeakReference; @@ -633,7 +624,7 @@ public class FoldersFragment extends AbsMainActivityFragment implements } @Override - protected ArrayList doInBackground(LoadingInfo... params) { + protected List doInBackground(LoadingInfo... params) { try { LoadingInfo info = params[0]; List files = FileUtil.listFilesDeep(info.files, info.fileFilter); @@ -659,7 +650,7 @@ public class FoldersFragment extends AbsMainActivityFragment implements } @Override - protected void onPostExecute(ArrayList songs) { + protected void onPostExecute(List songs) { super.onPostExecute(songs); OnSongsListedCallback callback = checkCallbackReference(); if (songs != null && callback != null) { @@ -692,7 +683,7 @@ public class FoldersFragment extends AbsMainActivityFragment implements public interface OnSongsListedCallback { - void onSongsListed(@NonNull ArrayList songs, Object extra); + void onSongsListed(@NonNull List songs, Object extra); } static class LoadingInfo { diff --git a/app/src/main/java/io/github/muntashirakon/music/fragments/genres/GenreDetailsFragment.kt b/app/src/main/java/io/github/muntashirakon/music/fragments/genres/GenreDetailsFragment.kt new file mode 100644 index 000000000..5b676af48 --- /dev/null +++ b/app/src/main/java/io/github/muntashirakon/music/fragments/genres/GenreDetailsFragment.kt @@ -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() + 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) { + 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) + } +} \ No newline at end of file diff --git a/app/src/main/java/io/github/muntashirakon/music/activities/genre/GenreDetailsViewModel.kt b/app/src/main/java/io/github/muntashirakon/music/fragments/genres/GenreDetailsViewModel.kt similarity index 86% rename from app/src/main/java/io/github/muntashirakon/music/activities/genre/GenreDetailsViewModel.kt rename to app/src/main/java/io/github/muntashirakon/music/fragments/genres/GenreDetailsViewModel.kt index 4d56466cc..1b1331f62 100644 --- a/app/src/main/java/io/github/muntashirakon/music/activities/genre/GenreDetailsViewModel.kt +++ b/app/src/main/java/io/github/muntashirakon/music/fragments/genres/GenreDetailsViewModel.kt @@ -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.MutableLiveData @@ -7,13 +7,13 @@ import androidx.lifecycle.viewModelScope import io.github.muntashirakon.music.interfaces.MusicServiceEventListener import io.github.muntashirakon.music.model.Genre 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.launch import kotlinx.coroutines.withContext class GenreDetailsViewModel( - private val repository: RepositoryImpl, + private val realRepository: RealRepository, private val genre: Genre ) : ViewModel(), MusicServiceEventListener { @@ -31,7 +31,7 @@ class GenreDetailsViewModel( } private fun loadGenreSongs(genre: Genre) = viewModelScope.launch { - val songs = repository.getGenre(genre.id) + val songs = realRepository.getGenre(genre.id) withContext(Main) { _playListSongs.postValue(songs) } } diff --git a/app/src/main/java/io/github/muntashirakon/music/fragments/genres/GenresFragment.kt b/app/src/main/java/io/github/muntashirakon/music/fragments/genres/GenresFragment.kt index b97093312..a4f623639 100644 --- a/app/src/main/java/io/github/muntashirakon/music/fragments/genres/GenresFragment.kt +++ b/app/src/main/java/io/github/muntashirakon/music/fragments/genres/GenresFragment.kt @@ -32,14 +32,12 @@ class GenresFragment : AbsRecyclerViewFragment - if (genres.isNotEmpty()) { - adapter?.swapDataSet(genres) - } else { - adapter?.swapDataSet(listOf()) - } - }) + libraryViewModel.genresLiveData.observe(viewLifecycleOwner, Observer { + if (it.isNotEmpty()) + adapter?.swapDataSet(it) + else + adapter?.swapDataSet(listOf()) + }) } @@ -49,7 +47,7 @@ class GenresFragment : AbsRecyclerViewFragment() private val libraryViewModel: LibraryViewModel by sharedViewModel() private val displayMetrics: DisplayMetrics @@ -60,9 +62,7 @@ class BannerHomeFragment : override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - setStatusBarColorAuto(view) - bannerImage?.setOnClickListener { val options = ActivityOptions.makeSceneTransitionAnimation( mainActivity, @@ -73,22 +73,33 @@ class BannerHomeFragment : } lastAdded.setOnClickListener { - NavigationUtil.goToPlaylistNew(requireActivity(), LastAddedPlaylist(requireActivity())) + findActivityNavController(R.id.fragment_container).navigate( + R.id.playlistDetailsFragment, + bundleOf(EXTRA_PLAYLIST to LastAddedPlaylist()) + ) } topPlayed.setOnClickListener { - NavigationUtil.goToPlaylistNew( - requireActivity(), - MyTopTracksPlaylist(requireActivity()) + findActivityNavController(R.id.fragment_container).navigate( + R.id.playlistDetailsFragment, + bundleOf(EXTRA_PLAYLIST to TopTracksPlaylist()) ) } actionShuffle.setOnClickListener { - MusicPlayerRemote.openAndShuffleQueue(SongLoader.getAllSongs(requireActivity()), true) + lifecycleScope.launch { + MusicPlayerRemote.openAndShuffleQueue( + repository.allSongs(), + true + ) + } } history.setOnClickListener { - NavigationUtil.goToPlaylistNew(requireActivity(), HistoryPlaylist(requireActivity())) + requireActivity().findNavController(R.id.fragment_container).navigate( + R.id.playlistDetailsFragment, + bundleOf(EXTRA_PLAYLIST to HistoryPlaylist()) + ) } userImage.setOnClickListener { @@ -101,16 +112,15 @@ class BannerHomeFragment : } titleWelcome?.text = String.format("%s", PreferenceUtil.userName) - val homeAdapter = HomeAdapter(mainActivity, displayMetrics) + val homeAdapter = HomeAdapter(mainActivity) recyclerView.apply { layoutManager = LinearLayoutManager(mainActivity) adapter = homeAdapter } - libraryViewModel.homeLiveData - .observe(viewLifecycleOwner, Observer { sections -> - homeAdapter.swapData(sections) - }) + libraryViewModel.homeLiveData.observe(viewLifecycleOwner, Observer { + homeAdapter.swapData(it) + }) loadProfile() } @@ -133,8 +143,8 @@ class BannerHomeFragment : const val TAG: String = "BannerHomeFragment" @JvmStatic - fun newInstance(): BannerHomeFragment { - return BannerHomeFragment() + fun newInstance(): HomeFragment { + return HomeFragment() } } } \ No newline at end of file diff --git a/app/src/main/java/io/github/muntashirakon/music/fragments/library/LibraryFragment.kt b/app/src/main/java/io/github/muntashirakon/music/fragments/library/LibraryFragment.kt new file mode 100644 index 000000000..7243bb12f --- /dev/null +++ b/app/src/main/java/io/github/muntashirakon/music/fragments/library/LibraryFragment.kt @@ -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 + } +} \ No newline at end of file diff --git a/app/src/main/java/io/github/muntashirakon/music/fragments/player/NowPlayingPlayerFragment.kt b/app/src/main/java/io/github/muntashirakon/music/fragments/player/NowPlayingPlayerFragment.kt index a86757726..0f7a6c1a7 100644 --- a/app/src/main/java/io/github/muntashirakon/music/fragments/player/NowPlayingPlayerFragment.kt +++ b/app/src/main/java/io/github/muntashirakon/music/fragments/player/NowPlayingPlayerFragment.kt @@ -4,7 +4,7 @@ import android.os.Bundle import androidx.fragment.app.Fragment import androidx.navigation.NavController 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.util.PreferenceUtil @@ -15,7 +15,7 @@ class NowPlayingPlayerFragment : Fragment(R.layout.fragment_now_playing_player) override fun onActivityCreated(savedInstanceState: Bundle?) { super.onActivityCreated(savedInstanceState) - val navController = navController(R.id.playerFragmentContainer) + val navController = findNavController(R.id.playerFragmentContainer) updateNowPlaying(navController) } diff --git a/app/src/main/java/io/github/muntashirakon/music/fragments/player/PlayerAlbumCoverFragment.kt b/app/src/main/java/io/github/muntashirakon/music/fragments/player/PlayerAlbumCoverFragment.kt index de479a092..07869bdbb 100644 --- a/app/src/main/java/io/github/muntashirakon/music/fragments/player/PlayerAlbumCoverFragment.kt +++ b/app/src/main/java/io/github/muntashirakon/music/fragments/player/PlayerAlbumCoverFragment.kt @@ -18,6 +18,7 @@ import kotlinx.android.synthetic.main.fragment_player_album_cover.* class PlayerAlbumCoverFragment : AbsMusicServiceFragment(R.layout.fragment_player_album_cover), ViewPager.OnPageChangeListener { + private var callbacks: Callbacks? = null private var currentPosition: Int = 0 private val colorReceiver = object : AlbumCoverFragment.ColorReceiver { diff --git a/app/src/main/java/io/github/muntashirakon/music/fragments/player/full/FullPlayerFragment.kt b/app/src/main/java/io/github/muntashirakon/music/fragments/player/full/FullPlayerFragment.kt index cf14b3b54..35085a08a 100644 --- a/app/src/main/java/io/github/muntashirakon/music/fragments/player/full/FullPlayerFragment.kt +++ b/app/src/main/java/io/github/muntashirakon/music/fragments/player/full/FullPlayerFragment.kt @@ -1,6 +1,5 @@ package io.github.muntashirakon.music.fragments.player.full -import android.app.ActivityOptions import android.content.res.ColorStateList import android.graphics.Color import android.os.Bundle @@ -8,31 +7,36 @@ import android.view.View import android.widget.FrameLayout import android.widget.TextView import androidx.appcompat.widget.Toolbar +import androidx.core.os.bundleOf +import androidx.lifecycle.lifecycleScope 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.extensions.findActivityNavController import io.github.muntashirakon.music.extensions.hide 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.player.PlayerAlbumCoverFragment 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.helper.MusicProgressViewUpdateHelper -import io.github.muntashirakon.music.loaders.ArtistLoader import io.github.muntashirakon.music.model.Song import io.github.muntashirakon.music.model.lyrics.AbsSynchronizedLyrics 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 com.bumptech.glide.Glide import kotlinx.android.synthetic.main.fragment_full.* -import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import kotlinx.coroutines.withContext +import org.koin.android.ext.android.inject class FullPlayerFragment : AbsPlayerFragment(R.layout.fragment_full), MusicProgressViewUpdateHelper.Callback { + private val artistRepository by inject() private lateinit var lyricsLayout: FrameLayout private lateinit var lyricsLine1: TextView private lateinit var lyricsLine2: TextView @@ -150,30 +154,20 @@ class FullPlayerFragment : AbsPlayerFragment(R.layout.fragment_full), private fun setupArtist() { artistImage.setOnClickListener { - val transitionName = - "${getString(R.string.transition_artist_image)}_${MusicPlayerRemote.currentSong.artistId}" - val activityOptions = - ActivityOptions.makeSceneTransitionAnimation( - requireActivity(), - artistImage, - transitionName + mainActivity.collapsePanel() + findActivityNavController(R.id.fragment_container) + .navigate( + R.id.artistDetailsFragment, + bundleOf(EXTRA_ARTIST_ID to MusicPlayerRemote.currentSong.artistId) ) - NavigationUtil.goToArtistOptions( - requireActivity(), - MusicPlayerRemote.currentSong.artistId, - activityOptions - ) } } private fun setUpSubFragments() { - controlsFragment = - childFragmentManager.findFragmentById(R.id.playbackControlsFragment) as FullPlaybackControlsFragment - - val playerAlbumCoverFragment = - childFragmentManager.findFragmentById(R.id.playerAlbumCoverFragment) as PlayerAlbumCoverFragment - playerAlbumCoverFragment.setCallbacks(this) - playerAlbumCoverFragment.removeSlideEffect() + controlsFragment = whichFragment(R.id.playbackControlsFragment) + val coverFragment: PlayerAlbumCoverFragment = whichFragment(R.id.playerAlbumCoverFragment) + coverFragment.setCallbacks(this) + coverFragment.removeSlideEffect() } override fun onShow() { @@ -228,9 +222,8 @@ class FullPlayerFragment : AbsPlayerFragment(R.layout.fragment_full), } private fun updateArtistImage() { - CoroutineScope(Dispatchers.IO).launch { - val artist = - ArtistLoader.getArtist(requireContext(), MusicPlayerRemote.currentSong.artistId) + lifecycleScope.launch { + val artist = artistRepository.artist(MusicPlayerRemote.currentSong.artistId) withContext(Dispatchers.Main) { ArtistGlideRequest.Builder.from(Glide.with(requireContext()), artist) .generatePalette(requireContext()) diff --git a/app/src/main/java/io/github/muntashirakon/music/fragments/player/normal/PlayerPlaybackControlsFragment.kt b/app/src/main/java/io/github/muntashirakon/music/fragments/player/normal/PlayerPlaybackControlsFragment.kt index cee4d785b..d1f68f4fd 100644 --- a/app/src/main/java/io/github/muntashirakon/music/fragments/player/normal/PlayerPlaybackControlsFragment.kt +++ b/app/src/main/java/io/github/muntashirakon/music/fragments/player/normal/PlayerPlaybackControlsFragment.kt @@ -52,7 +52,7 @@ class PlayerPlaybackControlsFragment : showBonceAnimation(playPauseButton) } title.isSelected = true - + text.isSelected = true } override fun setColor(color: MediaNotificationProcessor) { diff --git a/app/src/main/java/io/github/muntashirakon/music/activities/playlist/PlaylistDetailActivity.kt b/app/src/main/java/io/github/muntashirakon/music/fragments/playlists/PlaylistDetailsFragment.kt similarity index 55% rename from app/src/main/java/io/github/muntashirakon/music/activities/playlist/PlaylistDetailActivity.kt rename to app/src/main/java/io/github/muntashirakon/music/fragments/playlists/PlaylistDetailsFragment.kt index 42039753d..26b27d5bf 100644 --- a/app/src/main/java/io/github/muntashirakon/music/activities/playlist/PlaylistDetailActivity.kt +++ b/app/src/main/java/io/github/muntashirakon/music/fragments/playlists/PlaylistDetailsFragment.kt @@ -1,95 +1,81 @@ -package io.github.muntashirakon.music.activities.playlist +package io.github.muntashirakon.music.fragments.playlists import android.os.Bundle import android.view.Menu +import android.view.MenuInflater import android.view.MenuItem import android.view.View import androidx.lifecycle.Observer +import androidx.navigation.fragment.navArgs 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.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.draggable.RecyclerViewDragDropManager 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.core.parameter.parametersOf - -class PlaylistDetailActivity : AbsSlidingMusicPanelActivity(), CabHolder { - - +class PlaylistDetailsFragment : AbsMainActivityFragment(R.layout.fragment_playlist_detail) { + private val arguments by navArgs() private val viewModel: PlaylistDetailsViewModel by viewModel { - parametersOf(extraNotNull(EXTRA_PLAYLIST).value) + parametersOf(arguments.extraPlaylist) } + private lateinit var playlist: Playlist - private var cab: MaterialCab? = null private lateinit var adapter: SongAdapter + private var wrappedAdapter: RecyclerView.Adapter<*>? = null private var recyclerViewDragDropManager: RecyclerViewDragDropManager? = null - override fun onCreate(savedInstanceState: Bundle?) { - setDrawUnderStatusBar() - super.onCreate(savedInstanceState) - setStatusbarColorAuto() - setNavigationbarColorAuto() - setTaskDescriptionColorAuto() - setLightNavigationBar(true) - setBottomBarVisibility(View.GONE) + override fun onActivityCreated(savedInstanceState: Bundle?) { + super.onActivityCreated(savedInstanceState) + setHasOptionsMenu(true) + mainActivity.addMusicServiceEventListener(viewModel) + mainActivity.setSupportActionBar(toolbar) + mainActivity.hideBottomBarVisibility(false) - playlist = extraNotNull(EXTRA_PLAYLIST).value + playlist = arguments.extraPlaylist - setUpToolBar() setUpRecyclerView() - viewModel.getSongs().observe(this, Observer { + viewModel.getSongs().observe(viewLifecycleOwner, Observer { songs(it) }) - viewModel.getPlaylist().observe(this, Observer { + viewModel.getPlaylist().observe(viewLifecycleOwner, Observer { 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() { - recyclerView.layoutManager = LinearLayoutManager(this) + recyclerView.layoutManager = LinearLayoutManager(requireContext()) 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 } else { recyclerViewDragDropManager = RecyclerViewDragDropManager() val animator = RefactoredDefaultItemAnimator() - adapter = OrderablePlaylistSongAdapter(this, + adapter = OrderablePlaylistSongAdapter( + requireActivity(), ArrayList(), R.layout.item_list, - this, + null, object : OrderablePlaylistSongAdapter.OnMoveItemListener { override fun onMoveItem(fromPosition: Int, toPosition: Int) { if (PlaylistsUtil.moveItem( - this@PlaylistDetailActivity, + requireContext(), playlist.id, fromPosition, toPosition @@ -116,58 +102,21 @@ class PlaylistDetailActivity : AbsSlidingMusicPanelActivity(), CabHolder { }) } - private fun setUpToolBar() { - applyToolbar(toolbar) - title = playlist.name - } - - override fun onCreateOptionsMenu(menu: Menu): Boolean { - 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 onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) { + super.onCreateOptionsMenu(menu, inflater) + val menuRes = if (playlist is AbsCustomPlaylist) + R.menu.menu_smart_playlist_detail + else R.menu.menu_playlist_detail + inflater.inflate(menuRes, menu) } override fun onOptionsItemSelected(item: MenuItem): Boolean { - when (item.itemId) { - 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() - } + return PlaylistMenuHelper.handleMenuClick(requireActivity(), playlist, item) } private fun checkForPadding() { - val height = DensityUtil.dip2px(this, 52f) - recyclerView.setPadding(0, 0, 0, (height)) + val height = dipToPix(52f) + recyclerView.setPadding(0, 0, 0, height.toInt()) } private fun checkIsEmpty() { @@ -181,7 +130,7 @@ class PlaylistDetailActivity : AbsSlidingMusicPanelActivity(), CabHolder { return String(Character.toChars(unicode)) } - public override fun onPause() { + override fun onPause() { if (recyclerViewDragDropManager != null) { recyclerViewDragDropManager!!.cancelDrag() } @@ -206,7 +155,7 @@ class PlaylistDetailActivity : AbsSlidingMusicPanelActivity(), CabHolder { super.onDestroy() } - fun showEmptyView() { + private fun showEmptyView() { empty.visibility = View.VISIBLE emptyText.visibility = View.VISIBLE } @@ -219,7 +168,4 @@ class PlaylistDetailActivity : AbsSlidingMusicPanelActivity(), CabHolder { } } - companion object { - var EXTRA_PLAYLIST = "extra_playlist" - } } \ No newline at end of file diff --git a/app/src/main/java/io/github/muntashirakon/music/activities/playlist/PlaylistDetailsViewModel.kt b/app/src/main/java/io/github/muntashirakon/music/fragments/playlists/PlaylistDetailsViewModel.kt similarity index 83% rename from app/src/main/java/io/github/muntashirakon/music/activities/playlist/PlaylistDetailsViewModel.kt rename to app/src/main/java/io/github/muntashirakon/music/fragments/playlists/PlaylistDetailsViewModel.kt index 830388fea..253208a78 100644 --- a/app/src/main/java/io/github/muntashirakon/music/activities/playlist/PlaylistDetailsViewModel.kt +++ b/app/src/main/java/io/github/muntashirakon/music/fragments/playlists/PlaylistDetailsViewModel.kt @@ -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.MutableLiveData @@ -6,22 +6,20 @@ import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import io.github.muntashirakon.music.App 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.Playlist 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 kotlinx.coroutines.Dispatchers.Main import kotlinx.coroutines.launch import kotlinx.coroutines.withContext class PlaylistDetailsViewModel( - private val repository: RepositoryImpl, + private val realRepository: RealRepository, private var playlist: Playlist ) : ViewModel(), MusicServiceEventListener { private val _playListSongs = MutableLiveData>() - private val _playlist = MutableLiveData().apply { postValue(playlist) } @@ -35,7 +33,7 @@ class PlaylistDetailsViewModel( } private fun loadPlaylistSongs(playlist: Playlist) = viewModelScope.launch { - val songs = repository.getPlaylistSongs(playlist) + val songs = realRepository.getPlaylistSongs(playlist) withContext(Main) { _playListSongs.postValue(songs) } } @@ -50,8 +48,10 @@ class PlaylistDetailsViewModel( val playlistName = PlaylistsUtil.getNameForPlaylist(App.getContext(), playlist.id.toLong()) if (playlistName != playlist.name) { - playlist = PlaylistLoader.getPlaylist(App.getContext(), playlist.id) - _playlist.postValue(playlist) + viewModelScope.launch { + playlist = realRepository.playlist(playlist.id) + _playlist.postValue(playlist) + } } } loadPlaylistSongs(playlist) diff --git a/app/src/main/java/io/github/muntashirakon/music/fragments/playlists/PlaylistsFragment.kt b/app/src/main/java/io/github/muntashirakon/music/fragments/playlists/PlaylistsFragment.kt index a88d95468..10bc589d6 100644 --- a/app/src/main/java/io/github/muntashirakon/music/fragments/playlists/PlaylistsFragment.kt +++ b/app/src/main/java/io/github/muntashirakon/music/fragments/playlists/PlaylistsFragment.kt @@ -1,8 +1,6 @@ package io.github.muntashirakon.music.fragments.playlists import android.os.Bundle -import android.view.Menu -import android.view.MenuInflater import android.view.View import androidx.lifecycle.Observer import androidx.recyclerview.widget.GridLayoutManager @@ -12,7 +10,7 @@ import io.github.muntashirakon.music.fragments.base.AbsRecyclerViewFragment import io.github.muntashirakon.music.interfaces.MainActivityFragmentCallbacks class PlaylistsFragment : - AbsRecyclerViewFragment() , + AbsRecyclerViewFragment(), MainActivityFragmentCallbacks { override fun handleBackPress(): Boolean { @@ -21,12 +19,11 @@ class PlaylistsFragment : override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - libraryViewModel.playlisitsLiveData.observe(viewLifecycleOwner, Observer { playlists -> - if (playlists.isNotEmpty()) { - adapter?.swapDataSet(playlists) - } else { + libraryViewModel.playlisitsLiveData.observe(viewLifecycleOwner, Observer { + if (it.isNotEmpty()) + adapter?.swapDataSet(it) + else adapter?.swapDataSet(listOf()) - } }) } @@ -39,26 +36,14 @@ class PlaylistsFragment : override fun createAdapter(): PlaylistAdapter { return PlaylistAdapter( - mainActivity, + requireActivity(), ArrayList(), 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 { - @JvmField - val TAG: String = PlaylistsFragment::class.java.simpleName - - @JvmStatic fun newInstance(): PlaylistsFragment { return PlaylistsFragment() } diff --git a/app/src/main/java/io/github/muntashirakon/music/fragments/search/SearchFragment.kt b/app/src/main/java/io/github/muntashirakon/music/fragments/search/SearchFragment.kt new file mode 100644 index 000000000..8db443002 --- /dev/null +++ b/app/src/main/java/io/github/muntashirakon/music/fragments/search/SearchFragment.kt @@ -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( + 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) { + 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 +} \ No newline at end of file diff --git a/app/src/main/java/io/github/muntashirakon/music/activities/search/SearchViewModel.kt b/app/src/main/java/io/github/muntashirakon/music/fragments/search/SearchViewModel.kt similarity index 69% rename from app/src/main/java/io/github/muntashirakon/music/activities/search/SearchViewModel.kt rename to app/src/main/java/io/github/muntashirakon/music/fragments/search/SearchViewModel.kt index 9431b638c..fdf8a287a 100644 --- a/app/src/main/java/io/github/muntashirakon/music/activities/search/SearchViewModel.kt +++ b/app/src/main/java/io/github/muntashirakon/music/fragments/search/SearchViewModel.kt @@ -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.MutableLiveData import androidx.lifecycle.ViewModel 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.Main import kotlinx.coroutines.launch import kotlinx.coroutines.withContext -class SearchViewModel(private val repository: RepositoryImpl) : ViewModel() { +class SearchViewModel(private val realRepository: RealRepository) : ViewModel() { private val results = MutableLiveData>() fun getSearchResult(): LiveData> = results fun search(query: String?) = viewModelScope.launch(IO) { - val result = repository.search(query) + val result = realRepository.search(query) withContext(Main) { results.postValue(result) } } } \ No newline at end of file diff --git a/app/src/main/java/io/github/muntashirakon/music/fragments/settings/MainSettingsFragment.kt b/app/src/main/java/io/github/muntashirakon/music/fragments/settings/MainSettingsFragment.kt index cd65d5e7b..603773f90 100644 --- a/app/src/main/java/io/github/muntashirakon/music/fragments/settings/MainSettingsFragment.kt +++ b/app/src/main/java/io/github/muntashirakon/music/fragments/settings/MainSettingsFragment.kt @@ -14,17 +14,13 @@ package io.github.muntashirakon.music.fragments.settings -import android.content.res.ColorStateList import android.os.Bundle import android.view.LayoutInflater import android.view.View import android.view.ViewGroup -import androidx.annotation.StringRes import androidx.fragment.app.Fragment import androidx.navigation.fragment.findNavController 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.* class MainSettingsFragment : Fragment(), View.OnClickListener { @@ -60,12 +56,4 @@ class MainSettingsFragment : Fragment(), View.OnClickListener { otherSettings.setOnClickListener(this) aboutSettings.setOnClickListener(this) } - - companion object { - - } - - private fun inflateFragment(fragment: Fragment, @StringRes title: Int) { - (requireActivity() as SettingsActivity).setupFragment(fragment, title) - } } \ No newline at end of file diff --git a/app/src/main/java/io/github/muntashirakon/music/fragments/songs/SongsFragment.kt b/app/src/main/java/io/github/muntashirakon/music/fragments/songs/SongsFragment.kt index 47fbff0a9..f33ca639f 100644 --- a/app/src/main/java/io/github/muntashirakon/music/fragments/songs/SongsFragment.kt +++ b/app/src/main/java/io/github/muntashirakon/music/fragments/songs/SongsFragment.kt @@ -1,12 +1,11 @@ package io.github.muntashirakon.music.fragments.songs import android.os.Bundle -import android.view.* +import android.view.View import androidx.annotation.LayoutRes import androidx.lifecycle.Observer import androidx.recyclerview.widget.GridLayoutManager 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.fragments.ReloadType import io.github.muntashirakon.music.fragments.base.AbsRecyclerViewCustomGridSizeFragment @@ -24,11 +23,10 @@ class SongsFragment : override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) libraryViewModel.songsLiveData.observe(viewLifecycleOwner, Observer { - if (it.isNotEmpty()) { + if (it.isNotEmpty()) adapter?.swapDataSet(it) - } else { + else adapter?.swapDataSet(listOf()) - } }) } @@ -36,26 +34,16 @@ class SongsFragment : get() = R.string.no_songs override fun createLayoutManager(): GridLayoutManager { - return GridLayoutManager(requireActivity(), getGridSize()).apply { - spanSizeLookup = object : GridLayoutManager.SpanSizeLookup() { - override fun getSpanSize(position: Int): Int { - return if (position == 0) { - getGridSize() - } else { - 1 - } - } - } - } + return GridLayoutManager(requireActivity(), getGridSize()) } override fun createAdapter(): SongAdapter { val dataSet = if (adapter == null) mutableListOf() else adapter!!.dataSet - return ShuffleButtonSongAdapter( - mainActivity, + return SongAdapter( + requireActivity(), dataSet, - itemLayoutRes(), - mainActivity + R.layout.item_list, + null ) } @@ -109,72 +97,4 @@ class 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 - } } diff --git a/app/src/main/java/io/github/muntashirakon/music/glide/AlbumGlideRequest.java b/app/src/main/java/io/github/muntashirakon/music/glide/AlbumGlideRequest.java index 1a154ea10..1c3c04336 100644 --- a/app/src/main/java/io/github/muntashirakon/music/glide/AlbumGlideRequest.java +++ b/app/src/main/java/io/github/muntashirakon/music/glide/AlbumGlideRequest.java @@ -34,7 +34,7 @@ public class AlbumGlideRequest { if (ignoreMediaStore) { return requestManager.load(new AudioFileCover(song.getData())); } else { - return requestManager.loadFromMediaStore(MusicUtil.getMediaStoreAlbumCoverUri(song.getAlbumId())); + return requestManager.loadFromMediaStore(MusicUtil.INSTANCE.getMediaStoreAlbumCoverUri(song.getAlbumId())); } } diff --git a/app/src/main/java/io/github/muntashirakon/music/glide/SongGlideRequest.java b/app/src/main/java/io/github/muntashirakon/music/glide/SongGlideRequest.java index 3f8e3855d..e8285284a 100644 --- a/app/src/main/java/io/github/muntashirakon/music/glide/SongGlideRequest.java +++ b/app/src/main/java/io/github/muntashirakon/music/glide/SongGlideRequest.java @@ -52,7 +52,7 @@ public class SongGlideRequest { if (ignoreMediaStore) { return requestManager.load(new AudioFileCover(song.getData())); } else { - return requestManager.loadFromMediaStore(MusicUtil.getMediaStoreAlbumCoverUri(song.getAlbumId())); + return requestManager.loadFromMediaStore(MusicUtil.INSTANCE.getMediaStoreAlbumCoverUri(song.getAlbumId())); } } diff --git a/app/src/main/java/io/github/muntashirakon/music/helper/M3UWriter.kt b/app/src/main/java/io/github/muntashirakon/music/helper/M3UWriter.kt index 611f3d5d0..3f79fc866 100644 --- a/app/src/main/java/io/github/muntashirakon/music/helper/M3UWriter.kt +++ b/app/src/main/java/io/github/muntashirakon/music/helper/M3UWriter.kt @@ -30,7 +30,7 @@ object M3UWriter : M3UConstants { ): File? { if (!dir.exists()) dir.mkdirs() val file = File(dir, playlist.name + "." + M3UConstants.EXTENSION) - val songs = playlist.getSongs(context) + val songs = playlist.getSongs() if (songs.size > 0) { val bw = BufferedWriter(FileWriter(file)) bw.write(M3UConstants.HEADER) diff --git a/app/src/main/java/io/github/muntashirakon/music/helper/MusicPlayerRemote.kt b/app/src/main/java/io/github/muntashirakon/music/helper/MusicPlayerRemote.kt index bbc63604d..7cc9e175f 100644 --- a/app/src/main/java/io/github/muntashirakon/music/helper/MusicPlayerRemote.kt +++ b/app/src/main/java/io/github/muntashirakon/music/helper/MusicPlayerRemote.kt @@ -23,23 +23,26 @@ import android.os.Build import android.os.Environment import android.os.IBinder import android.provider.DocumentsContract -import android.provider.MediaStore import android.widget.Toast import androidx.core.content.ContextCompat 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.repository.SongRepository import io.github.muntashirakon.music.service.MusicService - import io.github.muntashirakon.music.util.PreferenceUtil +import org.koin.core.KoinComponent +import org.koin.core.inject import java.io.File import java.util.* -object MusicPlayerRemote { +object MusicPlayerRemote : KoinComponent { val TAG: String = MusicPlayerRemote::class.java.simpleName private val mConnectionMap = WeakHashMap() var musicService: MusicService? = null + private val songRepository by inject() + + @JvmStatic val isPlaying: Boolean get() = musicService != null && musicService!!.isPlaying @@ -413,24 +416,17 @@ object MusicPlayerRemote { songId = uri.lastPathSegment } if (songId != null) { - songs = SongLoader.getSongs( - SongLoader.makeSongCursor( - musicService!!, - MediaStore.Audio.AudioColumns._ID + "=?", - arrayOf(songId) - ) - ) + songs = songRepository.songs(songId) } } } if (songs == null) { var songFile: File? = null if (uri.authority != null && uri.authority == "com.android.externalstorage.documents") { - songFile = - File( - Environment.getExternalStorageDirectory(), - uri.path?.split(":".toRegex(), 2)?.get(1) - ) + songFile = File( + Environment.getExternalStorageDirectory(), + uri.path?.split(":".toRegex(), 2)?.get(1) + ) } if (songFile == null) { val path = getFilePathFromUri(musicService!!, uri) @@ -441,13 +437,7 @@ object MusicPlayerRemote { songFile = File(uri.path) } if (songFile != null) { - songs = SongLoader.getSongs( - SongLoader.makeSongCursor( - musicService!!, - MediaStore.Audio.AudioColumns.DATA + "=?", - arrayOf(songFile.absolutePath) - ) - ) + songs = songRepository.songsByFilePath(songFile.absolutePath) } } if (songs != null && songs.isNotEmpty()) { diff --git a/app/src/main/java/io/github/muntashirakon/music/helper/SearchQueryHelper.kt b/app/src/main/java/io/github/muntashirakon/music/helper/SearchQueryHelper.kt index faa466aba..0ccef5cf7 100644 --- a/app/src/main/java/io/github/muntashirakon/music/helper/SearchQueryHelper.kt +++ b/app/src/main/java/io/github/muntashirakon/music/helper/SearchQueryHelper.kt @@ -18,29 +18,31 @@ import android.app.SearchManager import android.content.Context import android.os.Bundle import android.provider.MediaStore -import io.github.muntashirakon.music.loaders.SongLoader 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.* -object SearchQueryHelper { +object SearchQueryHelper : KoinComponent { private const val TITLE_SELECTION = "lower(" + MediaStore.Audio.AudioColumns.TITLE + ") = ?" private const val ALBUM_SELECTION = "lower(" + MediaStore.Audio.AudioColumns.ALBUM + ") = ?" private const val ARTIST_SELECTION = "lower(" + MediaStore.Audio.AudioColumns.ARTIST + ") = ?" private const val AND = " AND " + private val songRepository by inject() var songs = ArrayList() @JvmStatic - fun getSongs(context: Context, extras: Bundle): ArrayList { + fun getSongs(context: Context, extras: Bundle): List { val query = extras.getString(SearchManager.QUERY, null) val artistName = extras.getString(MediaStore.EXTRA_MEDIA_ARTIST, null) val albumName = extras.getString(MediaStore.EXTRA_MEDIA_ALBUM, null) val titleName = extras.getString(MediaStore.EXTRA_MEDIA_TITLE, null) - var songs = ArrayList() + var songs = listOf() if (artistName != null && albumName != null && titleName != null) { - songs = SongLoader.getSongs( - SongLoader.makeSongCursor( - context, + songs = songRepository.songs( + songRepository.makeSongCursor( ARTIST_SELECTION + AND + ALBUM_SELECTION + AND + TITLE_SELECTION, arrayOf( artistName.toLowerCase(), @@ -54,9 +56,8 @@ object SearchQueryHelper { return songs } if (artistName != null && titleName != null) { - songs = SongLoader.getSongs( - SongLoader.makeSongCursor( - context, + songs = songRepository.songs( + songRepository.makeSongCursor( ARTIST_SELECTION + AND + TITLE_SELECTION, arrayOf(artistName.toLowerCase(), titleName.toLowerCase()) ) @@ -66,9 +67,8 @@ object SearchQueryHelper { return songs } if (albumName != null && titleName != null) { - songs = SongLoader.getSongs( - SongLoader.makeSongCursor( - context, + songs = songRepository.songs( + songRepository.makeSongCursor( ALBUM_SELECTION + AND + TITLE_SELECTION, arrayOf(albumName.toLowerCase(), titleName.toLowerCase()) ) @@ -78,9 +78,8 @@ object SearchQueryHelper { return songs } if (artistName != null) { - songs = SongLoader.getSongs( - SongLoader.makeSongCursor( - context, + songs = songRepository.songs( + songRepository.makeSongCursor( ARTIST_SELECTION, arrayOf(artistName.toLowerCase()) ) @@ -90,9 +89,8 @@ object SearchQueryHelper { return songs } if (albumName != null) { - songs = SongLoader.getSongs( - SongLoader.makeSongCursor( - context, + songs = songRepository.songs( + songRepository.makeSongCursor( ALBUM_SELECTION, arrayOf(albumName.toLowerCase()) ) @@ -102,9 +100,8 @@ object SearchQueryHelper { return songs } if (titleName != null) { - songs = SongLoader.getSongs( - SongLoader.makeSongCursor( - context, + songs = songRepository.songs( + songRepository.makeSongCursor( TITLE_SELECTION, arrayOf(titleName.toLowerCase()) ) @@ -113,21 +110,18 @@ object SearchQueryHelper { if (songs.isNotEmpty()) { return songs } - songs = - SongLoader.getSongs( - SongLoader.makeSongCursor( - context, - ARTIST_SELECTION, - arrayOf(query.toLowerCase()) - ) + songs = songRepository.songs( + songRepository.makeSongCursor( + ARTIST_SELECTION, + arrayOf(query.toLowerCase()) ) + ) if (songs.isNotEmpty()) { return songs } - songs = SongLoader.getSongs( - SongLoader.makeSongCursor( - context, + songs = songRepository.songs( + songRepository.makeSongCursor( ALBUM_SELECTION, arrayOf(query.toLowerCase()) ) @@ -135,9 +129,8 @@ object SearchQueryHelper { if (songs.isNotEmpty()) { return songs } - songs = SongLoader.getSongs( - SongLoader.makeSongCursor( - context, + songs = songRepository.songs( + songRepository.makeSongCursor( TITLE_SELECTION, arrayOf(query.toLowerCase()) ) diff --git a/app/src/main/java/io/github/muntashirakon/music/helper/menu/GenreMenuHelper.kt b/app/src/main/java/io/github/muntashirakon/music/helper/menu/GenreMenuHelper.kt index 2a7144068..fc3b94708 100644 --- a/app/src/main/java/io/github/muntashirakon/music/helper/menu/GenreMenuHelper.kt +++ b/app/src/main/java/io/github/muntashirakon/music/helper/menu/GenreMenuHelper.kt @@ -14,43 +14,43 @@ package io.github.muntashirakon.music.helper.menu -import android.app.Activity 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.dialogs.AddToPlaylistDialog 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.Song -import java.util.* +import io.github.muntashirakon.music.repository.GenreRepository +import org.koin.core.KoinComponent +import org.koin.core.inject -object GenreMenuHelper { - fun handleMenuClick(activity: AppCompatActivity, genre: Genre, item: MenuItem): Boolean { +object GenreMenuHelper : KoinComponent { + private val genreRepository by inject() + fun handleMenuClick(activity: FragmentActivity, genre: Genre, item: MenuItem): Boolean { when (item.itemId) { R.id.action_play -> { - MusicPlayerRemote.openQueue(getGenreSongs(activity, genre), 0, true) + MusicPlayerRemote.openQueue(getGenreSongs(genre), 0, true) return true } R.id.action_play_next -> { - MusicPlayerRemote.playNext(getGenreSongs(activity, genre)) + MusicPlayerRemote.playNext(getGenreSongs(genre)) return true } R.id.action_add_to_playlist -> { - AddToPlaylistDialog.create(getGenreSongs(activity, genre)) + AddToPlaylistDialog.create(getGenreSongs(genre)) .show(activity.supportFragmentManager, "ADD_PLAYLIST") return true } R.id.action_add_to_current_playing -> { - MusicPlayerRemote.enqueue(getGenreSongs(activity, genre)) + MusicPlayerRemote.enqueue(getGenreSongs(genre)) return true } } return false } - private fun getGenreSongs(activity: Activity, genre: Genre): ArrayList { - return GenreLoader.getSongs(activity, genre.id) + private fun getGenreSongs(genre: Genre): List { + return genreRepository.songs(genre.id) } } diff --git a/app/src/main/java/io/github/muntashirakon/music/helper/menu/PlaylistMenuHelper.kt b/app/src/main/java/io/github/muntashirakon/music/helper/menu/PlaylistMenuHelper.kt index 980321547..5afc4541e 100644 --- a/app/src/main/java/io/github/muntashirakon/music/helper/menu/PlaylistMenuHelper.kt +++ b/app/src/main/java/io/github/muntashirakon/music/helper/menu/PlaylistMenuHelper.kt @@ -19,26 +19,24 @@ import android.app.Activity import android.content.Context import android.view.MenuItem 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.R import io.github.muntashirakon.music.dialogs.AddToPlaylistDialog import io.github.muntashirakon.music.dialogs.DeletePlaylistDialog import io.github.muntashirakon.music.dialogs.RenamePlaylistDialog 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.model.AbsCustomPlaylist import io.github.muntashirakon.music.model.Playlist import io.github.muntashirakon.music.model.Song import io.github.muntashirakon.music.util.PlaylistsUtil -import java.util.* object PlaylistMenuHelper { fun handleMenuClick( - activity: AppCompatActivity, + activity: FragmentActivity, playlist: Playlist, item: MenuItem ): Boolean { when (item.itemId) { @@ -80,11 +78,11 @@ object PlaylistMenuHelper { private fun getPlaylistSongs( activity: Activity, playlist: Playlist - ): ArrayList { + ): List { return if (playlist is AbsCustomPlaylist) { - playlist.getSongs(activity) + playlist.songs() } else { - PlaylistSongsLoader.getPlaylistSongList(activity, playlist) + playlist.getSongs() } } diff --git a/app/src/main/java/io/github/muntashirakon/music/helper/menu/SongMenuHelper.kt b/app/src/main/java/io/github/muntashirakon/music/helper/menu/SongMenuHelper.kt index 7d7427194..67834a943 100644 --- a/app/src/main/java/io/github/muntashirakon/music/helper/menu/SongMenuHelper.kt +++ b/app/src/main/java/io/github/muntashirakon/music/helper/menu/SongMenuHelper.kt @@ -18,8 +18,11 @@ import android.content.Intent import android.view.MenuItem import android.view.View import android.widget.PopupMenu -import androidx.appcompat.app.AppCompatActivity +import androidx.core.os.bundleOf 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.activities.tageditor.AbsTagEditorActivity 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.model.Song import io.github.muntashirakon.music.util.MusicUtil -import io.github.muntashirakon.music.util.NavigationUtil import io.github.muntashirakon.music.util.RingtoneManager object SongMenuHelper { @@ -89,18 +91,24 @@ object SongMenuHelper { return true } 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 } 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 false } - abstract class OnClickSongMenu protected constructor(private val activity: AppCompatActivity) : + abstract class OnClickSongMenu(private val activity: FragmentActivity) : View.OnClickListener, PopupMenu.OnMenuItemClickListener { open val menuRes: Int diff --git a/app/src/main/java/io/github/muntashirakon/music/helper/menu/SongsMenuHelper.kt b/app/src/main/java/io/github/muntashirakon/music/helper/menu/SongsMenuHelper.kt index 4e7f3a2d1..ad3821f74 100644 --- a/app/src/main/java/io/github/muntashirakon/music/helper/menu/SongsMenuHelper.kt +++ b/app/src/main/java/io/github/muntashirakon/music/helper/menu/SongsMenuHelper.kt @@ -15,19 +15,17 @@ package io.github.muntashirakon.music.helper.menu import androidx.fragment.app.FragmentActivity - import io.github.muntashirakon.music.R import io.github.muntashirakon.music.dialogs.AddToPlaylistDialog import io.github.muntashirakon.music.dialogs.DeleteSongsDialog import io.github.muntashirakon.music.helper.MusicPlayerRemote import io.github.muntashirakon.music.model.Song -import java.util.* object SongsMenuHelper { fun handleMenuClick( activity: FragmentActivity, - songs: ArrayList, + songs: List, menuItemId: Int ): Boolean { when (menuItemId) { diff --git a/app/src/main/java/io/github/muntashirakon/music/interfaces/Callbacks.kt b/app/src/main/java/io/github/muntashirakon/music/interfaces/Callbacks.kt new file mode 100644 index 000000000..17924914e --- /dev/null +++ b/app/src/main/java/io/github/muntashirakon/music/interfaces/Callbacks.kt @@ -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) +} \ No newline at end of file diff --git a/app/src/main/java/io/github/muntashirakon/music/loaders/ArtistLoader.kt b/app/src/main/java/io/github/muntashirakon/music/loaders/ArtistLoader.kt deleted file mode 100644 index 630a9011e..000000000 --- a/app/src/main/java/io/github/muntashirakon/music/loaders/ArtistLoader.kt +++ /dev/null @@ -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 { - val songs = SongLoader.getSongs( - SongLoader.makeSongCursor( - context, - null, null, - getSongLoaderSortOrder() - ) - ) - return splitIntoArtists(AlbumLoader.splitIntoAlbums(songs)) - } - - fun getArtists(context: Context, query: String): ArrayList { - val songs = SongLoader.getSongs( - SongLoader.makeSongCursor( - context, - AudioColumns.ARTIST + " LIKE ?", - arrayOf("%$query%"), - getSongLoaderSortOrder() - ) - ) - return splitIntoArtists(AlbumLoader.splitIntoAlbums(songs)) - } - - fun splitIntoArtists(albums: ArrayList?): ArrayList { - val artists = ArrayList() - if (albums != null) { - for (album in albums) { - getOrCreateArtist(artists, album.artistId).albums!!.add(album) - } - } - return artists - } - - private fun getOrCreateArtist(artists: ArrayList, 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)) - } -} diff --git a/app/src/main/java/io/github/muntashirakon/music/loaders/PlaylistLoader.kt b/app/src/main/java/io/github/muntashirakon/music/loaders/PlaylistLoader.kt deleted file mode 100644 index 69e44f7de..000000000 --- a/app/src/main/java/io/github/muntashirakon/music/loaders/PlaylistLoader.kt +++ /dev/null @@ -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 { - 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 { - return getAllPlaylists(makePlaylistCursor(context, null, null)) - } - - fun getFavoritePlaylist(context: Context): ArrayList { - return getAllPlaylists( - makePlaylistCursor( - context, - PlaylistsColumns.NAME + "=?", - arrayOf(context.getString(R.string.favorites)) - ) - ) - } - - fun getAllPlaylists(cursor: Cursor?): ArrayList { - val playlists = ArrayList() - - 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? - ): 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) - } -} diff --git a/app/src/main/java/io/github/muntashirakon/music/lyrics/LrcView.java b/app/src/main/java/io/github/muntashirakon/music/lyrics/LrcView.java index d4c0f7c34..e065d2f62 100644 --- a/app/src/main/java/io/github/muntashirakon/music/lyrics/LrcView.java +++ b/app/src/main/java/io/github/muntashirakon/music/lyrics/LrcView.java @@ -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) { this(context, attrs, 0); } diff --git a/app/src/main/java/io/github/muntashirakon/music/model/AbsCustomPlaylist.java b/app/src/main/java/io/github/muntashirakon/music/model/AbsCustomPlaylist.java deleted file mode 100644 index 83024a0a5..000000000 --- a/app/src/main/java/io/github/muntashirakon/music/model/AbsCustomPlaylist.java +++ /dev/null @@ -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 getSongs(@NotNull Context context); -} diff --git a/app/src/main/java/io/github/muntashirakon/music/model/AbsCustomPlaylist.kt b/app/src/main/java/io/github/muntashirakon/music/model/AbsCustomPlaylist.kt new file mode 100644 index 000000000..9db83c837 --- /dev/null +++ b/app/src/main/java/io/github/muntashirakon/music/model/AbsCustomPlaylist.kt @@ -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 + + protected val songRepository by inject() + + protected val topPlayedRepository by inject() + + protected val lastAddedRepository by inject() +} \ No newline at end of file diff --git a/app/src/main/java/io/github/muntashirakon/music/model/Album.kt b/app/src/main/java/io/github/muntashirakon/music/model/Album.kt index 3b3b18a1e..0f83eb21b 100644 --- a/app/src/main/java/io/github/muntashirakon/music/model/Album.kt +++ b/app/src/main/java/io/github/muntashirakon/music/model/Album.kt @@ -41,6 +41,9 @@ class Album { val songCount: Int get() = songs!!.size + val albumArtist: String? + get() = safeGetFirstSong().albumArtist + constructor(songs: ArrayList) { this.songs = songs } diff --git a/app/src/main/java/io/github/muntashirakon/music/model/Artist.kt b/app/src/main/java/io/github/muntashirakon/music/model/Artist.kt index 41959691f..f78f60deb 100644 --- a/app/src/main/java/io/github/muntashirakon/music/model/Artist.kt +++ b/app/src/main/java/io/github/muntashirakon/music/model/Artist.kt @@ -25,10 +25,10 @@ class Artist { val name: String get() { - val name = safeGetFirstAlbum().artistName + val name = safeGetFirstAlbum().safeGetFirstSong().albumArtist return if (MusicUtil.isArtistNameUnknown(name)) { UNKNOWN_ARTIST_DISPLAY_NAME - } else name!! + } else safeGetFirstAlbum().safeGetFirstSong().artistName } val songCount: Int diff --git a/app/src/main/java/io/github/muntashirakon/music/model/CategoryInfo.java b/app/src/main/java/io/github/muntashirakon/music/model/CategoryInfo.java index 75e730723..76e39dba0 100644 --- a/app/src/main/java/io/github/muntashirakon/music/model/CategoryInfo.java +++ b/app/src/main/java/io/github/muntashirakon/music/model/CategoryInfo.java @@ -59,9 +59,9 @@ public class CategoryInfo implements Parcelable { } public enum Category { - Home(R.id.action_home, R.string.home, R.drawable.asld_home), - Songs(R.id.action_song, R.string.songs, R.drawable.asld_music_note), - Albums(R.id.action_album, R.string.albums, R.drawable.asld_album), + Home(R.id.action_home, R.string.for_you, R.drawable.ic_face), + Songs(R.id.action_song, R.string.songs, R.drawable.ic_audiotrack), + Albums(R.id.action_album, R.string.albums, R.drawable.ic_album), 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), Genres(R.id.action_genre, R.string.genres, R.drawable.ic_guitar), diff --git a/app/src/main/java/io/github/muntashirakon/music/model/Home.kt b/app/src/main/java/io/github/muntashirakon/music/model/Home.kt index 7420a4a7c..86a9c3647 100644 --- a/app/src/main/java/io/github/muntashirakon/music/model/Home.kt +++ b/app/src/main/java/io/github/muntashirakon/music/model/Home.kt @@ -14,13 +14,10 @@ package io.github.muntashirakon.music.model -import androidx.annotation.DrawableRes -import io.github.muntashirakon.music.adapter.HomeAdapter.Companion.HomeSection +import io.github.muntashirakon.music.HomeSection class Home( - val arrayList: List<*>, + val arrayList: List, @HomeSection - val homeSection: Int, - @DrawableRes - val icon: Int + val homeSection: Int ) \ No newline at end of file diff --git a/app/src/main/java/io/github/muntashirakon/music/model/Playlist.java b/app/src/main/java/io/github/muntashirakon/music/model/Playlist.java deleted file mode 100644 index c7bb84a23..000000000 --- a/app/src/main/java/io/github/muntashirakon/music/model/Playlist.java +++ /dev/null @@ -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 CREATOR = new Creator() { - 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 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, - "" - ); - } -} diff --git a/app/src/main/java/io/github/muntashirakon/music/model/Playlist.kt b/app/src/main/java/io/github/muntashirakon/music/model/Playlist.kt new file mode 100644 index 000000000..26f2abb66 --- /dev/null +++ b/app/src/main/java/io/github/muntashirakon/music/model/Playlist.kt @@ -0,0 +1,30 @@ +package io.github.muntashirakon.music.model + +import android.content.Context +import android.os.Parcelable +import io.github.muntashirakon.music.repository.RealPlaylistRepository +import io.github.muntashirakon.music.util.MusicUtil +import kotlinx.android.parcel.Parcelize +import org.koin.core.KoinComponent +import org.koin.core.get + +@Parcelize +open class Playlist( + val id: Int = -1, + val name: String = "" +) : Parcelable, KoinComponent { + + // this default implementation covers static playlists + fun getSongs(): List { + return RealPlaylistRepository(get()).playlistSongs(id) + } + + open fun getInfoString(context: Context): String { + val songCount = getSongs().size + val songCountString = MusicUtil.getSongCountString(context, songCount) + return MusicUtil.buildInfoString( + songCountString, + "" + ) + } +} \ No newline at end of file diff --git a/app/src/main/java/io/github/muntashirakon/music/model/PlaylistSong.java b/app/src/main/java/io/github/muntashirakon/music/model/PlaylistSong.java index 645bda7f3..d2fc8f9c7 100644 --- a/app/src/main/java/io/github/muntashirakon/music/model/PlaylistSong.java +++ b/app/src/main/java/io/github/muntashirakon/music/model/PlaylistSong.java @@ -41,9 +41,9 @@ public class PlaylistSong extends Song { @NotNull String artistName, int playlistId, int idInPlayList, - @NotNull String composer) { - super(id, title, trackNumber, year, duration, data, dateModified, albumId, albumName, artistId, artistName, - composer); + @NotNull String composer, + String albumArtist) { + super(id, title, trackNumber, year, duration, data, dateModified, albumId, albumName, artistId, artistName, composer, albumArtist); this.playlistId = playlistId; this.idInPlayList = idInPlayList; } diff --git a/app/src/main/java/io/github/muntashirakon/music/model/Song.kt b/app/src/main/java/io/github/muntashirakon/music/model/Song.kt index 66b8e77ce..85697fd4a 100644 --- a/app/src/main/java/io/github/muntashirakon/music/model/Song.kt +++ b/app/src/main/java/io/github/muntashirakon/music/model/Song.kt @@ -29,7 +29,8 @@ open class Song( val albumName: String, val artistId: Int, val artistName: String, - val composer: String? + val composer: String?, + val albumArtist: String? ) : Parcelable { @@ -48,6 +49,7 @@ open class Song( "", -1, "", + "", "" ) } diff --git a/app/src/main/java/io/github/muntashirakon/music/model/smartplaylist/AbsSmartPlaylist.java b/app/src/main/java/io/github/muntashirakon/music/model/smartplaylist/AbsSmartPlaylist.java deleted file mode 100644 index 69baac8b9..000000000 --- a/app/src/main/java/io/github/muntashirakon/music/model/smartplaylist/AbsSmartPlaylist.java +++ /dev/null @@ -1,83 +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.smartplaylist; - -import android.content.Context; -import android.os.Parcel; - -import androidx.annotation.DrawableRes; -import androidx.annotation.Nullable; - -import io.github.muntashirakon.music.R; -import io.github.muntashirakon.music.model.AbsCustomPlaylist; - - -public abstract class AbsSmartPlaylist extends AbsCustomPlaylist { - - @DrawableRes - public final int iconRes; - - public AbsSmartPlaylist(final String name, final int iconRes) { - super(-Math.abs(31 * name.hashCode() + (iconRes * name.hashCode() * 31 * 31)), name); - this.iconRes = iconRes; - } - - public AbsSmartPlaylist() { - super(); - this.iconRes = R.drawable.ic_queue_music; - } - - protected AbsSmartPlaylist(Parcel in) { - super(in); - this.iconRes = in.readInt(); - } - - public abstract void clear(Context context); - - @Override - public int describeContents() { - return 0; - } - - @Override - public boolean equals(@Nullable final Object obj) { - if (super.equals(obj)) { - if (getClass() != obj.getClass()) { - return false; - } - final AbsSmartPlaylist other = (AbsSmartPlaylist) obj; - return iconRes == other.iconRes; - } - return false; - } - - @Override - public int hashCode() { - final int prime = 31; - int result = super.hashCode(); - result = prime * result + iconRes; - return result; - } - - public boolean isClearable() { - return true; - } - - @Override - public void writeToParcel(Parcel dest, int flags) { - super.writeToParcel(dest, flags); - dest.writeInt(this.iconRes); - } -} diff --git a/app/src/main/java/io/github/muntashirakon/music/model/smartplaylist/AbsSmartPlaylist.kt b/app/src/main/java/io/github/muntashirakon/music/model/smartplaylist/AbsSmartPlaylist.kt new file mode 100644 index 000000000..2202d95a1 --- /dev/null +++ b/app/src/main/java/io/github/muntashirakon/music/model/smartplaylist/AbsSmartPlaylist.kt @@ -0,0 +1,10 @@ +package io.github.muntashirakon.music.model.smartplaylist + +import androidx.annotation.DrawableRes +import io.github.muntashirakon.music.R +import io.github.muntashirakon.music.model.AbsCustomPlaylist + +abstract class AbsSmartPlaylist( + name: String = "", + @DrawableRes val iconRes: Int = R.drawable.ic_queue_music +) : AbsCustomPlaylist(-Math.abs(31 * name.hashCode() + iconRes * name.hashCode() * 31 * 31), name) \ No newline at end of file diff --git a/app/src/main/java/io/github/muntashirakon/music/model/smartplaylist/HistoryPlaylist.java b/app/src/main/java/io/github/muntashirakon/music/model/smartplaylist/HistoryPlaylist.java deleted file mode 100644 index c487b6ef8..000000000 --- a/app/src/main/java/io/github/muntashirakon/music/model/smartplaylist/HistoryPlaylist.java +++ /dev/null @@ -1,69 +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.smartplaylist; - -import android.content.Context; -import android.os.Parcel; - -import androidx.annotation.NonNull; - -import org.jetbrains.annotations.NotNull; - -import java.util.ArrayList; - -import io.github.muntashirakon.music.R; -import io.github.muntashirakon.music.loaders.TopAndRecentlyPlayedTracksLoader; -import io.github.muntashirakon.music.model.Song; -import io.github.muntashirakon.music.providers.HistoryStore; - -/** - * @author Karim Abou Zeid (kabouzeid) - */ -public class HistoryPlaylist extends AbsSmartPlaylist { - - public static final Creator CREATOR = new Creator() { - public HistoryPlaylist createFromParcel(Parcel source) { - return new HistoryPlaylist(source); - } - - public HistoryPlaylist[] newArray(int size) { - return new HistoryPlaylist[size]; - } - }; - - public HistoryPlaylist(@NonNull Context context) { - super(context.getString(R.string.history), R.drawable.ic_history); - } - - protected HistoryPlaylist(Parcel in) { - super(in); - } - - @Override - public void clear(@NonNull Context context) { - HistoryStore.getInstance(context).clear(); - } - - @Override - public int describeContents() { - return 0; - } - - @NonNull - @Override - public ArrayList getSongs(@NotNull @NonNull Context context) { - return TopAndRecentlyPlayedTracksLoader.INSTANCE.getRecentlyPlayedTracks(context); - } -} diff --git a/app/src/main/java/io/github/muntashirakon/music/model/smartplaylist/HistoryPlaylist.kt b/app/src/main/java/io/github/muntashirakon/music/model/smartplaylist/HistoryPlaylist.kt new file mode 100644 index 000000000..56e473dac --- /dev/null +++ b/app/src/main/java/io/github/muntashirakon/music/model/smartplaylist/HistoryPlaylist.kt @@ -0,0 +1,19 @@ +package io.github.muntashirakon.music.model.smartplaylist + +import io.github.muntashirakon.music.App +import io.github.muntashirakon.music.R +import io.github.muntashirakon.music.model.Song +import kotlinx.android.parcel.Parcelize +import org.koin.core.KoinComponent + +@Parcelize +class HistoryPlaylist : + AbsSmartPlaylist( + App.getContext().getString(R.string.history), + R.drawable.ic_history + ), + KoinComponent { + override fun songs(): List { + return topPlayedRepository.recentlyPlayedTracks() + } +} \ No newline at end of file diff --git a/app/src/main/java/io/github/muntashirakon/music/model/smartplaylist/LastAddedPlaylist.java b/app/src/main/java/io/github/muntashirakon/music/model/smartplaylist/LastAddedPlaylist.java deleted file mode 100644 index 31d3984f7..000000000 --- a/app/src/main/java/io/github/muntashirakon/music/model/smartplaylist/LastAddedPlaylist.java +++ /dev/null @@ -1,70 +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.smartplaylist; - -import android.content.Context; -import android.os.Parcel; - -import androidx.annotation.NonNull; - -import org.jetbrains.annotations.NotNull; - -import java.util.ArrayList; - -import io.github.muntashirakon.music.R; -import io.github.muntashirakon.music.loaders.LastAddedSongsLoader; -import io.github.muntashirakon.music.model.Song; - - -public class LastAddedPlaylist extends AbsSmartPlaylist { - - public static final Creator CREATOR = new Creator() { - public LastAddedPlaylist createFromParcel(Parcel source) { - return new LastAddedPlaylist(source); - } - - public LastAddedPlaylist[] newArray(int size) { - return new LastAddedPlaylist[size]; - } - }; - - public LastAddedPlaylist(@NonNull Context context) { - super(context.getString(R.string.last_added), R.drawable.ic_library_add); - } - - protected LastAddedPlaylist(Parcel in) { - super(in); - } - - @Override - public void clear(@NonNull Context context) { - } - - @Override - public int describeContents() { - return 0; - } - - @NonNull - @Override - public ArrayList getSongs(@NotNull @NonNull Context context) { - return LastAddedSongsLoader.INSTANCE.getLastAddedSongs(context); - } - - @Override - public boolean isClearable() { - return false; - } -} diff --git a/app/src/main/java/io/github/muntashirakon/music/model/smartplaylist/LastAddedPlaylist.kt b/app/src/main/java/io/github/muntashirakon/music/model/smartplaylist/LastAddedPlaylist.kt new file mode 100644 index 000000000..fabd66608 --- /dev/null +++ b/app/src/main/java/io/github/muntashirakon/music/model/smartplaylist/LastAddedPlaylist.kt @@ -0,0 +1,14 @@ +package io.github.muntashirakon.music.model.smartplaylist + +import io.github.muntashirakon.music.App +import io.github.muntashirakon.music.R +import io.github.muntashirakon.music.model.Song +import kotlinx.android.parcel.Parcelize + +@Parcelize +class LastAddedPlaylist : + AbsSmartPlaylist(App.getContext().getString(R.string.last_added), R.drawable.ic_library_add) { + override fun songs(): List { + return lastAddedRepository.recentSongs() + } +} \ No newline at end of file diff --git a/app/src/main/java/io/github/muntashirakon/music/model/smartplaylist/MyTopTracksPlaylist.java b/app/src/main/java/io/github/muntashirakon/music/model/smartplaylist/MyTopTracksPlaylist.java deleted file mode 100644 index fcac697f6..000000000 --- a/app/src/main/java/io/github/muntashirakon/music/model/smartplaylist/MyTopTracksPlaylist.java +++ /dev/null @@ -1,69 +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.smartplaylist; - -import android.content.Context; -import android.os.Parcel; - -import androidx.annotation.NonNull; - -import org.jetbrains.annotations.NotNull; - -import java.util.ArrayList; - -import io.github.muntashirakon.music.R; -import io.github.muntashirakon.music.loaders.TopAndRecentlyPlayedTracksLoader; -import io.github.muntashirakon.music.model.Song; -import io.github.muntashirakon.music.providers.SongPlayCountStore; - -/** - * @author Karim Abou Zeid (kabouzeid) - */ -public class MyTopTracksPlaylist extends AbsSmartPlaylist { - - public static final Creator CREATOR = new Creator() { - public MyTopTracksPlaylist createFromParcel(Parcel source) { - return new MyTopTracksPlaylist(source); - } - - public MyTopTracksPlaylist[] newArray(int size) { - return new MyTopTracksPlaylist[size]; - } - }; - - public MyTopTracksPlaylist(@NonNull Context context) { - super(context.getString(R.string.my_top_tracks), R.drawable.ic_trending_up); - } - - protected MyTopTracksPlaylist(Parcel in) { - super(in); - } - - @Override - public void clear(@NonNull Context context) { - SongPlayCountStore.getInstance(context).clear(); - } - - @Override - public int describeContents() { - return 0; - } - - @NonNull - @Override - public ArrayList getSongs(@NotNull @NonNull Context context) { - return TopAndRecentlyPlayedTracksLoader.INSTANCE.getTopTracks(context); - } -} diff --git a/app/src/main/java/io/github/muntashirakon/music/model/smartplaylist/NotPlayedPlaylist.kt b/app/src/main/java/io/github/muntashirakon/music/model/smartplaylist/NotPlayedPlaylist.kt new file mode 100644 index 000000000..5c00a920a --- /dev/null +++ b/app/src/main/java/io/github/muntashirakon/music/model/smartplaylist/NotPlayedPlaylist.kt @@ -0,0 +1,16 @@ +package io.github.muntashirakon.music.model.smartplaylist + +import io.github.muntashirakon.music.App +import io.github.muntashirakon.music.R +import io.github.muntashirakon.music.model.Song +import kotlinx.android.parcel.Parcelize + +@Parcelize +class NotPlayedPlaylist : AbsSmartPlaylist( + App.getContext().getString(R.string.not_recently_played), + R.drawable.ic_watch_later +) { + override fun songs(): List { + return topPlayedRepository.notRecentlyPlayedTracks() + } +} \ No newline at end of file diff --git a/app/src/main/java/io/github/muntashirakon/music/model/smartplaylist/NotRecentlyPlayedPlaylist.java b/app/src/main/java/io/github/muntashirakon/music/model/smartplaylist/NotRecentlyPlayedPlaylist.java deleted file mode 100644 index 4c37763d7..000000000 --- a/app/src/main/java/io/github/muntashirakon/music/model/smartplaylist/NotRecentlyPlayedPlaylist.java +++ /dev/null @@ -1,69 +0,0 @@ -package io.github.muntashirakon.music.model.smartplaylist; - -import android.content.Context; -import android.os.Parcel; - -import androidx.annotation.NonNull; - -import java.util.ArrayList; - -import io.github.muntashirakon.music.R; -import io.github.muntashirakon.music.loaders.TopAndRecentlyPlayedTracksLoader; -import io.github.muntashirakon.music.model.Song; -import io.github.muntashirakon.music.util.MusicUtil; -import io.github.muntashirakon.music.util.PreferenceUtil; - -/** - * @author SC (soncaokim) - */ -public class NotRecentlyPlayedPlaylist extends AbsSmartPlaylist { - - public static final Creator CREATOR = new Creator() { - public NotRecentlyPlayedPlaylist createFromParcel(Parcel source) { - return new NotRecentlyPlayedPlaylist(source); - } - - public NotRecentlyPlayedPlaylist[] newArray(int size) { - return new NotRecentlyPlayedPlaylist[size]; - } - }; - - public NotRecentlyPlayedPlaylist(@NonNull Context context) { - super(context.getString(R.string.not_recently_played), R.drawable.ic_watch_later); - } - - protected NotRecentlyPlayedPlaylist(Parcel in) { - super(in); - } - - @NonNull - @Override - public String getInfoString(@NonNull Context context) { - String cutoff = PreferenceUtil.INSTANCE.getRecentlyPlayedCutoffText(context); - - return MusicUtil.buildInfoString( - cutoff, - super.getInfoString(context) - ); - } - - @NonNull - @Override - public ArrayList getSongs(@NonNull Context context) { - return TopAndRecentlyPlayedTracksLoader.INSTANCE.getNotRecentlyPlayedTracks(context); - } - - @Override - public void clear(@NonNull Context context) { - } - - @Override - public boolean isClearable() { - return false; - } - - @Override - public int describeContents() { - return 0; - } -} \ No newline at end of file diff --git a/app/src/main/java/io/github/muntashirakon/music/model/smartplaylist/ShuffleAllPlaylist.java b/app/src/main/java/io/github/muntashirakon/music/model/smartplaylist/ShuffleAllPlaylist.java deleted file mode 100644 index 0e84629ca..000000000 --- a/app/src/main/java/io/github/muntashirakon/music/model/smartplaylist/ShuffleAllPlaylist.java +++ /dev/null @@ -1,65 +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.smartplaylist; - -import android.content.Context; -import android.os.Parcel; - -import androidx.annotation.NonNull; - -import org.jetbrains.annotations.NotNull; - -import java.util.ArrayList; - -import io.github.muntashirakon.music.R; -import io.github.muntashirakon.music.loaders.SongLoader; -import io.github.muntashirakon.music.model.Song; - -public class ShuffleAllPlaylist extends AbsSmartPlaylist { - - public static final Creator CREATOR = new Creator() { - public ShuffleAllPlaylist createFromParcel(Parcel source) { - return new ShuffleAllPlaylist(source); - } - - public ShuffleAllPlaylist[] newArray(int size) { - return new ShuffleAllPlaylist[size]; - } - }; - - public ShuffleAllPlaylist(@NonNull Context context) { - super(context.getString(R.string.action_shuffle_all), R.drawable.ic_shuffle); - } - - protected ShuffleAllPlaylist(Parcel in) { - super(in); - } - - @Override - public void clear(@NonNull Context context) { - // Shuffle all is not a real "Smart Playlist" - } - - @Override - public int describeContents() { - return 0; - } - - @NonNull - @Override - public ArrayList getSongs(@NotNull Context context) { - return SongLoader.INSTANCE.getAllSongs(context); - } -} diff --git a/app/src/main/java/io/github/muntashirakon/music/model/smartplaylist/ShuffleAllPlaylist.kt b/app/src/main/java/io/github/muntashirakon/music/model/smartplaylist/ShuffleAllPlaylist.kt new file mode 100644 index 000000000..a2de672fd --- /dev/null +++ b/app/src/main/java/io/github/muntashirakon/music/model/smartplaylist/ShuffleAllPlaylist.kt @@ -0,0 +1,16 @@ +package io.github.muntashirakon.music.model.smartplaylist + +import io.github.muntashirakon.music.App +import io.github.muntashirakon.music.R +import io.github.muntashirakon.music.model.Song +import kotlinx.android.parcel.Parcelize + +@Parcelize +class ShuffleAllPlaylist : AbsSmartPlaylist( + App.getContext().getString(R.string.action_shuffle_all), + R.drawable.ic_shuffle +) { + override fun songs(): List { + return songRepository.songs() + } +} \ No newline at end of file diff --git a/app/src/main/java/io/github/muntashirakon/music/model/smartplaylist/TopTracksPlaylist.kt b/app/src/main/java/io/github/muntashirakon/music/model/smartplaylist/TopTracksPlaylist.kt new file mode 100644 index 000000000..1f63ed53f --- /dev/null +++ b/app/src/main/java/io/github/muntashirakon/music/model/smartplaylist/TopTracksPlaylist.kt @@ -0,0 +1,16 @@ +package io.github.muntashirakon.music.model.smartplaylist + +import io.github.muntashirakon.music.App +import io.github.muntashirakon.music.R +import io.github.muntashirakon.music.model.Song +import kotlinx.android.parcel.Parcelize + +@Parcelize +class TopTracksPlaylist : AbsSmartPlaylist( + App.getContext().getString(R.string.my_top_tracks), + R.drawable.ic_trending_up +) { + override fun songs(): List { + return topPlayedRepository.topTracks() + } +} \ No newline at end of file diff --git a/app/src/main/java/io/github/muntashirakon/music/preferences/BlacklistPreferenceDialog.kt b/app/src/main/java/io/github/muntashirakon/music/preferences/BlacklistPreferenceDialog.kt index 2e8c3b365..7e7a7b785 100644 --- a/app/src/main/java/io/github/muntashirakon/music/preferences/BlacklistPreferenceDialog.kt +++ b/app/src/main/java/io/github/muntashirakon/music/preferences/BlacklistPreferenceDialog.kt @@ -24,6 +24,7 @@ import androidx.core.text.HtmlCompat import androidx.fragment.app.DialogFragment import androidx.fragment.app.FragmentActivity import code.name.monkey.appthemehelper.common.prefs.supportv7.ATEDialogPreference +import com.google.android.material.dialog.MaterialAlertDialogBuilder import io.github.muntashirakon.music.App import io.github.muntashirakon.music.R import io.github.muntashirakon.music.dialogs.BlacklistFolderChooserDialog @@ -31,7 +32,6 @@ import io.github.muntashirakon.music.extensions.colorButtons import io.github.muntashirakon.music.extensions.colorControlNormal import io.github.muntashirakon.music.extensions.materialDialog import io.github.muntashirakon.music.providers.BlacklistStore -import com.google.android.material.dialog.MaterialAlertDialogBuilder import java.io.File import java.util.* @@ -69,7 +69,7 @@ class BlacklistPreferenceDialog : DialogFragment(), BlacklistFolderChooserDialog chooserDialog?.setCallback(this) refreshBlacklistData() return materialDialog(R.string.blacklist) - .setPositiveButton(android.R.string.ok) { _, _ -> + .setPositiveButton(R.string.done) { _, _ -> dismiss() } .setNeutralButton(R.string.clear_action) { _, _ -> diff --git a/app/src/main/java/io/github/muntashirakon/music/providers/MusicPlaybackQueueStore.java b/app/src/main/java/io/github/muntashirakon/music/providers/MusicPlaybackQueueStore.java index 01f097383..fb8b119f4 100644 --- a/app/src/main/java/io/github/muntashirakon/music/providers/MusicPlaybackQueueStore.java +++ b/app/src/main/java/io/github/muntashirakon/music/providers/MusicPlaybackQueueStore.java @@ -24,11 +24,11 @@ import android.provider.MediaStore.Audio.AudioColumns; import androidx.annotation.NonNull; import androidx.annotation.Nullable; -import java.util.ArrayList; import java.util.List; -import io.github.muntashirakon.music.loaders.SongLoader; +import io.github.muntashirakon.music.App; import io.github.muntashirakon.music.model.Song; +import io.github.muntashirakon.music.repository.RealSongRepository; /** * @author Andrew Neal, modified for Phonograph by Karim Abou Zeid @@ -43,7 +43,7 @@ public class MusicPlaybackQueueStore extends SQLiteOpenHelper { public static final String ORIGINAL_PLAYING_QUEUE_TABLE_NAME = "original_playing_queue"; - private static final int VERSION = 10; + private static final int VERSION = 12; @Nullable private static MusicPlaybackQueueStore sInstance = null; @@ -76,12 +76,12 @@ public class MusicPlaybackQueueStore extends SQLiteOpenHelper { } @NonNull - public ArrayList getSavedOriginalPlayingQueue() { + public List getSavedOriginalPlayingQueue() { return getQueue(ORIGINAL_PLAYING_QUEUE_TABLE_NAME); } @NonNull - public ArrayList getSavedPlayingQueue() { + public List getSavedPlayingQueue() { return getQueue(PLAYING_QUEUE_TABLE_NAME); } @@ -148,16 +148,19 @@ public class MusicPlaybackQueueStore extends SQLiteOpenHelper { builder.append(" STRING NOT NULL,"); builder.append(AudioColumns.COMPOSER); + builder.append(" STRING,"); + + builder.append("album_artist"); builder.append(" STRING);"); db.execSQL(builder.toString()); } @NonNull - private ArrayList getQueue(@NonNull final String tableName) { + private List getQueue(@NonNull final String tableName) { Cursor cursor = getReadableDatabase().query(tableName, null, null, null, null, null, null); - return SongLoader.INSTANCE.getSongs(cursor); + return new RealSongRepository(App.Companion.getContext()).songs(cursor); } /** diff --git a/app/src/main/java/io/github/muntashirakon/music/providers/RepositoryImpl.kt b/app/src/main/java/io/github/muntashirakon/music/providers/RepositoryImpl.kt deleted file mode 100644 index cd1e14c8e..000000000 --- a/app/src/main/java/io/github/muntashirakon/music/providers/RepositoryImpl.kt +++ /dev/null @@ -1,151 +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.providers - -import android.content.Context -import io.github.muntashirakon.music.R -import io.github.muntashirakon.music.adapter.HomeAdapter -import io.github.muntashirakon.music.loaders.* -import io.github.muntashirakon.music.model.* -import io.github.muntashirakon.music.model.smartplaylist.NotRecentlyPlayedPlaylist -import io.github.muntashirakon.music.network.LastFMService -import io.github.muntashirakon.music.network.model.LastFmAlbum -import io.github.muntashirakon.music.network.model.LastFmArtist -import io.github.muntashirakon.music.providers.interfaces.Repository - -class RepositoryImpl( - private val context: Context, - private val lastFMService: LastFMService -) : Repository { - - override suspend fun allAlbums(): List = AlbumLoader.getAllAlbums(context) - - override suspend fun albumById(albumId: Int): Album = AlbumLoader.getAlbum(context, albumId) - - override suspend fun allArtists(): List = ArtistLoader.getAllArtists(context) - - override suspend fun artistById(artistId: Int): Artist = - ArtistLoader.getArtist(context, artistId) - - override suspend fun allPlaylists(): List = PlaylistLoader.getAllPlaylists(context) - - override suspend fun allGenres(): List = GenreLoader.getAllGenres(context) - - override suspend fun allSongs(): List = SongLoader.getAllSongs(context) - - override suspend fun suggestions(): Home? { - val songs = NotRecentlyPlayedPlaylist(context).getSongs(context).shuffled().apply { - if (size > 9) subList(0, 9) - } - if (songs.isNotEmpty()) { - return Home( - songs, - HomeAdapter.SUGGESTIONS, - R.drawable.ic_audiotrack - ) - } - return null - } - - override suspend fun homeGenres(): Home? { - val genres = - GenreLoader.getAllGenres(context) - .shuffled() - .filter { it.name.length in 5..10 } - - if (genres.isNotEmpty()) { - return Home( - genres, - HomeAdapter.GENRES, - R.drawable.ic_guitar - ) - } - return null - } - - override suspend fun search(query: String?): MutableList = - SearchLoader.searchAll(context, query) - - override suspend fun getPlaylistSongs(playlist: Playlist): ArrayList { - return if (playlist is AbsCustomPlaylist) { - playlist.getSongs(context) - } else { - PlaylistSongsLoader.getPlaylistSongList(context, playlist.id) - } - } - - override suspend fun getGenre(genreId: Int): ArrayList = - GenreLoader.getSongs(context, genreId) - - override suspend fun recentArtists(): Home? { - val artists = LastAddedSongsLoader.getLastAddedArtists(context) - return if (artists.isNotEmpty()) Home( - artists, - HomeAdapter.RECENT_ARTISTS, - R.drawable.ic_artist - ) else null - } - - override suspend fun recentAlbums(): Home? { - val albums = LastAddedSongsLoader.getLastAddedAlbums(context) - return if (albums.isNotEmpty()) Home( - albums, - HomeAdapter.RECENT_ALBUMS, - R.drawable.ic_album - ) else null - } - - override suspend fun topAlbums(): Home? { - val albums = TopAndRecentlyPlayedTracksLoader.getTopAlbums(context) - return if (albums.isNotEmpty()) Home( - albums, - HomeAdapter.TOP_ALBUMS, - R.drawable.ic_album - ) else null - } - - override suspend fun topArtists(): Home? { - - val artists = TopAndRecentlyPlayedTracksLoader.getTopArtists(context) - return if (artists.isNotEmpty()) Home( - artists, - HomeAdapter.TOP_ARTISTS, - R.drawable.ic_artist - ) else null - - } - - override suspend fun favoritePlaylist(): Home? { - val playlists = PlaylistLoader.getFavoritePlaylist(context) - return if (playlists.isNotEmpty()) Home( - playlists, - HomeAdapter.FAVOURITES, - R.drawable.ic_favorite - ) else null - } - - override suspend fun artistInfo( - name: String, - lang: String?, - cache: String? - ): LastFmArtist = lastFMService.artistInfo(name, lang, cache) - - - override suspend fun albumInfo( - artist: String, - album: String - ): LastFmAlbum = lastFMService.albumInfo(artist, album) - -} \ No newline at end of file diff --git a/app/src/main/java/io/github/muntashirakon/music/providers/interfaces/Repository.kt b/app/src/main/java/io/github/muntashirakon/music/providers/interfaces/Repository.kt deleted file mode 100644 index 53f69fe3e..000000000 --- a/app/src/main/java/io/github/muntashirakon/music/providers/interfaces/Repository.kt +++ /dev/null @@ -1,63 +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.providers.interfaces - -import io.github.muntashirakon.music.model.* -import io.github.muntashirakon.music.network.model.LastFmAlbum -import io.github.muntashirakon.music.network.model.LastFmArtist - -/** - * Created by hemanths on 11/08/17. - */ - -interface Repository { - - suspend fun allAlbums(): List - - suspend fun albumById(albumId: Int): Album - - suspend fun allSongs(): List - - suspend fun allArtists(): List - - suspend fun allPlaylists(): List - - suspend fun allGenres(): List - - suspend fun search(query: String?): MutableList - - suspend fun getPlaylistSongs(playlist: Playlist): ArrayList - - suspend fun getGenre(genreId: Int): ArrayList - - suspend fun artistInfo(name: String, lang: String?, cache: String?): LastFmArtist - - suspend fun albumInfo(artist: String, album: String): LastFmAlbum - - suspend fun artistById(artistId: Int): Artist - suspend fun recentArtists(): Home? - - suspend fun topArtists(): Home? - - suspend fun topAlbums(): Home? - - suspend fun recentAlbums(): Home? - - suspend fun favoritePlaylist(): Home? - - suspend fun suggestions(): Home? - - suspend fun homeGenres(): Home? -} \ No newline at end of file diff --git a/app/src/main/java/io/github/muntashirakon/music/loaders/AlbumLoader.kt b/app/src/main/java/io/github/muntashirakon/music/repository/AlbumRepository.kt similarity index 78% rename from app/src/main/java/io/github/muntashirakon/music/loaders/AlbumLoader.kt rename to app/src/main/java/io/github/muntashirakon/music/repository/AlbumRepository.kt index 88af498ad..601930858 100644 --- a/app/src/main/java/io/github/muntashirakon/music/loaders/AlbumLoader.kt +++ b/app/src/main/java/io/github/muntashirakon/music/repository/AlbumRepository.kt @@ -12,9 +12,8 @@ * See the GNU General Public License for more details. */ -package io.github.muntashirakon.music.loaders +package io.github.muntashirakon.music.repository -import android.content.Context import android.provider.MediaStore.Audio.AudioColumns import io.github.muntashirakon.music.helper.SortOrder import io.github.muntashirakon.music.model.Album @@ -27,16 +26,31 @@ import kotlin.collections.ArrayList /** * Created by hemanths on 11/08/17. */ +interface AlbumRepository { + fun albums(): List -object AlbumLoader { + fun albums(query: String): List - fun getAlbums( - context: Context, - query: String - ): ArrayList { - val songs = SongLoader.getSongs( - SongLoader.makeSongCursor( - context, + fun album(albumId: Int): Album +} + +class RealAlbumRepository(private val songRepository: RealSongRepository) : + AlbumRepository { + + override fun albums(): List { + val songs = songRepository.songs( + songRepository.makeSongCursor( + null, + null, + getSongLoaderSortOrder() + ) + ) + return splitIntoAlbums(songs) + } + + override fun albums(query: String): List { + val songs = songRepository.songs( + songRepository.makeSongCursor( AudioColumns.ALBUM + " LIKE ?", arrayOf("%$query%"), getSongLoaderSortOrder() @@ -45,41 +59,22 @@ object AlbumLoader { return splitIntoAlbums(songs) } - @JvmStatic - fun getAlbum( - context: Context, - albumId: Int - ): Album { - val songs = SongLoader.getSongs( - SongLoader.makeSongCursor( - context, + override fun album(albumId: Int): Album { + val songs = songRepository.songs( + songRepository.makeSongCursor( AudioColumns.ALBUM_ID + "=?", arrayOf(albumId.toString()), getSongLoaderSortOrder() ) ) - val album = Album(songs) - sortSongsByTrackNumber(album) + val album = Album(ArrayList(songs)) + sortAlbumSongs(album) return album } - fun getAllAlbums( - context: Context - ): ArrayList { - val songs = SongLoader.getSongs( - SongLoader.makeSongCursor( - context, - null, - null, - getSongLoaderSortOrder() - ) - ) - return splitIntoAlbums(songs) - } - fun splitIntoAlbums( - songs: ArrayList? - ): ArrayList { + songs: List? + ): List { val albums = ArrayList() if (songs != null) { for (song in songs) { @@ -87,7 +82,7 @@ object AlbumLoader { } } for (album in albums) { - sortSongsByTrackNumber(album) + sortAlbumSongs(album) } return albums } @@ -106,7 +101,7 @@ object AlbumLoader { return album } - private fun sortSongsByTrackNumber(album: Album) { + private fun sortAlbumSongs(album: Album) { when (PreferenceUtil.albumDetailSongSortOrder) { SortOrder.AlbumSongSortOrder.SONG_TRACK_LIST -> album.songs?.sortWith(Comparator { o1, o2 -> o1.trackNumber.compareTo( @@ -135,4 +130,6 @@ object AlbumLoader { return PreferenceUtil.albumSortOrder + ", " + PreferenceUtil.albumSongSortOrder } + + } diff --git a/app/src/main/java/io/github/muntashirakon/music/repository/ArtistRepository.kt b/app/src/main/java/io/github/muntashirakon/music/repository/ArtistRepository.kt new file mode 100644 index 000000000..49591757f --- /dev/null +++ b/app/src/main/java/io/github/muntashirakon/music/repository/ArtistRepository.kt @@ -0,0 +1,120 @@ +/* + * 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.repository + +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 + +interface ArtistRepository { + fun artists(): List + + fun albumArtists(): List + + fun artists(query: String): List + + fun artist(artistId: Int): Artist +} + +class RealArtistRepository( + private val songRepository: RealSongRepository, + private val albumRepository: RealAlbumRepository +) : ArtistRepository { + + private fun getSongLoaderSortOrder(): String { + return PreferenceUtil.artistSortOrder + ", " + + PreferenceUtil.artistAlbumSortOrder + ", " + + PreferenceUtil.artistSongSortOrder + } + + override fun artists(): List { + val songs = songRepository.songs( + songRepository.makeSongCursor( + null, null, + getSongLoaderSortOrder() + ) + ) + return splitIntoArtists(albumRepository.splitIntoAlbums(songs)) + } + + override fun artists(query: String): List { + val songs = songRepository.songs( + songRepository.makeSongCursor( + AudioColumns.ARTIST + " LIKE ?", + arrayOf("%$query%"), + getSongLoaderSortOrder() + ) + ) + return splitIntoArtists(albumRepository.splitIntoAlbums(songs)) + } + + override fun albumArtists(): List { + val songs = songRepository.songs( + songRepository.makeSongCursor( + null, + null, + getSongLoaderSortOrder() + ) + ) + return splitIntoAlbumArtists(albumRepository.splitIntoAlbums(songs)) + } + + private fun splitIntoAlbumArtists(albums: List): List { + // First group the songs in albums by filtering each artist name + val amap = hashMapOf() + albums.forEach { + val key = it.albumArtist + if (key != null) { + val artist: Artist = if (amap[key] != null) amap[key]!! else Artist() + artist.albums?.add(it) + amap[key] = artist + } + } + return ArrayList(amap.values) + } + + override fun artist(artistId: Int): Artist { + val songs = songRepository.songs( + songRepository.makeSongCursor( + AudioColumns.ARTIST_ID + "=?", + arrayOf(artistId.toString()), + getSongLoaderSortOrder() + ) + ) + return Artist(ArrayList(albumRepository.splitIntoAlbums(songs))) + } + + fun splitIntoArtists(albums: List?): List { + val artists = mutableListOf() + if (albums != null) { + for (album in albums) { + getOrCreateArtist(artists, album.artistId).albums!!.add(album) + } + } + return artists + } + + private fun getOrCreateArtist(artists: MutableList, 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 + } +} diff --git a/app/src/main/java/io/github/muntashirakon/music/loaders/GenreLoader.kt b/app/src/main/java/io/github/muntashirakon/music/repository/GenreRepository.kt similarity index 50% rename from app/src/main/java/io/github/muntashirakon/music/loaders/GenreLoader.kt rename to app/src/main/java/io/github/muntashirakon/music/repository/GenreRepository.kt index 19ab6b114..7c47b22bf 100644 --- a/app/src/main/java/io/github/muntashirakon/music/loaders/GenreLoader.kt +++ b/app/src/main/java/io/github/muntashirakon/music/repository/GenreRepository.kt @@ -12,12 +12,13 @@ * See the GNU General Public License for more details. */ -package io.github.muntashirakon.music.loaders +package io.github.muntashirakon.music.repository -import android.content.Context +import android.content.ContentResolver import android.database.Cursor import android.net.Uri import android.provider.BaseColumns +import android.provider.MediaStore import android.provider.MediaStore.Audio.Genres import io.github.muntashirakon.music.Constants.IS_MUSIC import io.github.muntashirakon.music.Constants.baseProjection @@ -25,49 +26,52 @@ import io.github.muntashirakon.music.model.Genre import io.github.muntashirakon.music.model.Song import io.github.muntashirakon.music.util.PreferenceUtil +interface GenreRepository { + fun genres(): List -object GenreLoader { + fun songs(genreId: Int): List +} - fun getAllGenres(context: Context): ArrayList { - return getGenresFromCursor(context, makeGenreCursor(context)) +class RealGenreRepository( + private val contentResolver: ContentResolver, + private val songRepository: RealSongRepository +) : GenreRepository { + + override fun genres(): List { + return getGenresFromCursor(makeGenreCursor()) } - fun searchGenres(context: Context): ArrayList { - return getGenresFromCursorForSearch(context, makeGenreCursor(context)) - } - - fun getSongs(context: Context, genreId: Int): ArrayList { + override fun songs(genreId: Int): List { // The genres table only stores songs that have a genre specified, // so we need to get songs without a genre a different way. return if (genreId == -1) { - getSongsWithNoGenre(context) - } else SongLoader.getSongs(makeGenreSongCursor(context, genreId)) - + getSongsWithNoGenre() + } else songRepository.songs(makeGenreSongCursor(genreId)) } - private fun getGenreFromCursor(context: Context, cursor: Cursor): Genre { + private fun getGenreFromCursor(cursor: Cursor): Genre { val id = cursor.getInt(0) val name = cursor.getString(1) - val songCount = getSongs(context, id).size + val songCount = songs(id).size return Genre(id, name, songCount) } - private fun getGenreFromCursorWithOutSongs(context: Context, cursor: Cursor): Genre { + private fun getGenreFromCursorWithOutSongs(cursor: Cursor): Genre { val id = cursor.getInt(0) val name = cursor.getString(1) return Genre(id, name, -1) } - private fun getSongsWithNoGenre(context: Context): ArrayList { - val selection = BaseColumns._ID + " NOT IN " + - "(SELECT " + Genres.Members.AUDIO_ID + " FROM audio_genres_map)" - return SongLoader.getSongs(SongLoader.makeSongCursor(context, selection, null)) + private fun getSongsWithNoGenre(): List { + val selection = + BaseColumns._ID + " NOT IN " + "(SELECT " + Genres.Members.AUDIO_ID + " FROM audio_genres_map)" + return songRepository.songs(songRepository.makeSongCursor(selection, null)) } - private fun hasSongsWithNoGenre(context: Context): Boolean { - val allSongsCursor = SongLoader.makeSongCursor(context, null, null) - val allSongsWithGenreCursor = makeAllSongsWithGenreCursor(context) + private fun hasSongsWithNoGenre(): Boolean { + val allSongsCursor = songRepository.makeSongCursor(null, null) + val allSongsWithGenreCursor = makeAllSongsWithGenreCursor() if (allSongsCursor == null || allSongsWithGenreCursor == null) { return false @@ -79,44 +83,36 @@ object GenreLoader { return hasSongsWithNoGenre } - private fun makeAllSongsWithGenreCursor(context: Context): Cursor? { - return try { - context.contentResolver.query( - Uri.parse("content://media/external/audio/genres/all/members"), - arrayOf(Genres.Members.AUDIO_ID), null, null, null - ) - } catch (e: SecurityException) { - null - } + private fun makeAllSongsWithGenreCursor(): Cursor? { + println(MediaStore.Audio.Genres.EXTERNAL_CONTENT_URI.toString()) + return contentResolver.query( + Uri.parse("content://media/external/audio/genres/all/members"), + arrayOf(Genres.Members.AUDIO_ID), null, null, null + ) } - private fun makeGenreSongCursor(context: Context, genreId: Int): Cursor? { - try { - return context.contentResolver.query( - Genres.Members.getContentUri("external", genreId.toLong()), - baseProjection, - IS_MUSIC, - null, - PreferenceUtil.songSortOrder - ) - } catch (e: SecurityException) { - return null - } - + private fun makeGenreSongCursor(genreId: Int): Cursor? { + return contentResolver.query( + Genres.Members.getContentUri("external", genreId.toLong()), + baseProjection, + IS_MUSIC, + null, + PreferenceUtil.songSortOrder + ) } - private fun getGenresFromCursor(context: Context, cursor: Cursor?): ArrayList { + private fun getGenresFromCursor(cursor: Cursor?): ArrayList { val genres = arrayListOf() if (cursor != null) { if (cursor.moveToFirst()) { do { - val genre = getGenreFromCursor(context, cursor) + val genre = getGenreFromCursor(cursor) if (genre.songCount > 0) { genres.add(genre) } else { // try to remove the empty genre from the media store try { - context.contentResolver.delete( + contentResolver.delete( Genres.EXTERNAL_CONTENT_URI, Genres._ID + " == " + genre.id, null @@ -133,11 +129,11 @@ object GenreLoader { return genres } - private fun getGenresFromCursorForSearch(context: Context, cursor: Cursor?): ArrayList { - val genres = arrayListOf() + private fun getGenresFromCursorForSearch(cursor: Cursor?): List { + val genres = mutableListOf() if (cursor != null && cursor.moveToFirst()) { do { - genres.add(getGenreFromCursorWithOutSongs(context, cursor)) + genres.add(getGenreFromCursorWithOutSongs(cursor)) } while (cursor.moveToNext()) } cursor?.close() @@ -145,18 +141,16 @@ object GenreLoader { } - private fun makeGenreCursor(context: Context): Cursor? { + private fun makeGenreCursor(): Cursor? { val projection = arrayOf(Genres._ID, Genres.NAME) - try { - return context.contentResolver.query( - Genres.EXTERNAL_CONTENT_URI, - projection, - null, - null, - PreferenceUtil.genreSortOrder - ) - } catch (e: SecurityException) { - return null - } + return contentResolver.query( + Genres.EXTERNAL_CONTENT_URI, + projection, + null, + null, + PreferenceUtil.genreSortOrder + ) } + + } diff --git a/app/src/main/java/io/github/muntashirakon/music/loaders/LastAddedSongsLoader.kt b/app/src/main/java/io/github/muntashirakon/music/repository/LastAddedSongsRepository.kt similarity index 58% rename from app/src/main/java/io/github/muntashirakon/music/loaders/LastAddedSongsLoader.kt rename to app/src/main/java/io/github/muntashirakon/music/repository/LastAddedSongsRepository.kt index 382113dfd..c5a73f5a8 100644 --- a/app/src/main/java/io/github/muntashirakon/music/loaders/LastAddedSongsLoader.kt +++ b/app/src/main/java/io/github/muntashirakon/music/repository/LastAddedSongsRepository.kt @@ -12,9 +12,8 @@ * See the GNU General Public License for more details. */ -package io.github.muntashirakon.music.loaders +package io.github.muntashirakon.music.repository -import android.content.Context import android.database.Cursor import android.provider.MediaStore import io.github.muntashirakon.music.model.Album @@ -25,28 +24,37 @@ import io.github.muntashirakon.music.util.PreferenceUtil /** * Created by hemanths on 16/08/17. */ +interface LastAddedRepository { + fun recentSongs(): List -object LastAddedSongsLoader { + fun recentAlbums(): List - fun getLastAddedSongs(context: Context): ArrayList { - return SongLoader.getSongs(makeLastAddedCursor(context)) + fun recentArtists(): List +} + +class RealLastAddedRepository( + private val songRepository: RealSongRepository, + private val albumRepository: RealAlbumRepository, + private val artistRepository: RealArtistRepository +) : LastAddedRepository { + override fun recentSongs(): List { + return songRepository.songs(makeLastAddedCursor()) } - private fun makeLastAddedCursor(context: Context): Cursor? { + override fun recentAlbums(): List { + return albumRepository.splitIntoAlbums(recentSongs()) + } + + override fun recentArtists(): List { + return artistRepository.splitIntoArtists(recentAlbums()) + } + + private fun makeLastAddedCursor(): Cursor? { val cutoff = PreferenceUtil.lastAddedCutoff - return SongLoader.makeSongCursor( - context, + return songRepository.makeSongCursor( MediaStore.Audio.Media.DATE_ADDED + ">?", arrayOf(cutoff.toString()), MediaStore.Audio.Media.DATE_ADDED + " DESC" ) } - - fun getLastAddedAlbums(context: Context): ArrayList { - return AlbumLoader.splitIntoAlbums(getLastAddedSongs(context)) - } - - fun getLastAddedArtists(context: Context): ArrayList { - return ArtistLoader.splitIntoArtists(getLastAddedAlbums(context)) - } } diff --git a/app/src/main/java/io/github/muntashirakon/music/repository/PlaylistRepository.kt b/app/src/main/java/io/github/muntashirakon/music/repository/PlaylistRepository.kt new file mode 100644 index 000000000..408710585 --- /dev/null +++ b/app/src/main/java/io/github/muntashirakon/music/repository/PlaylistRepository.kt @@ -0,0 +1,206 @@ +/* + * 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.repository + +import android.content.ContentResolver +import android.database.Cursor +import android.provider.BaseColumns +import android.provider.MediaStore +import android.provider.MediaStore.Audio.PlaylistsColumns +import io.github.muntashirakon.music.Constants +import io.github.muntashirakon.music.model.Playlist +import io.github.muntashirakon.music.model.PlaylistSong +import io.github.muntashirakon.music.model.Song + +/** + * Created by hemanths on 16/08/17. + */ +interface PlaylistRepository { + fun playlist(cursor: Cursor?): Playlist + + fun searchPlaylist(query: String): List + + fun playlist(playlistName: String): Playlist + + fun playlists(): List + + fun playlists(cursor: Cursor?): List + + fun favoritePlaylist(playlistName: String): List + + fun deletePlaylist(playlistId: Int) + + fun playlist(playlistId: Int): Playlist + + fun playlistSongs(playlistId: Int): List +} + +class RealPlaylistRepository( + private val contentResolver: ContentResolver +) : PlaylistRepository { + + override fun playlist(cursor: Cursor?): Playlist { + var playlist = Playlist() + if (cursor != null && cursor.moveToFirst()) { + playlist = getPlaylistFromCursorImpl(cursor) + } + cursor?.close() + return playlist + } + + override fun playlist(playlistName: String): Playlist { + return playlist(makePlaylistCursor(PlaylistsColumns.NAME + "=?", arrayOf(playlistName))) + } + + override fun playlist(playlistId: Int): Playlist { + return playlist( + makePlaylistCursor( + BaseColumns._ID + "=?", + arrayOf(playlistId.toString()) + ) + ) + } + + override fun searchPlaylist(query: String): List { + return playlists(makePlaylistCursor(PlaylistsColumns.NAME + "=?", arrayOf(query))) + } + + override fun playlists(): List { + return playlists(makePlaylistCursor(null, null)) + } + + override fun playlists(cursor: Cursor?): List { + val playlists = mutableListOf() + if (cursor != null && cursor.moveToFirst()) { + do { + playlists.add(getPlaylistFromCursorImpl(cursor)) + } while (cursor.moveToNext()) + } + cursor?.close() + return playlists + } + + override fun favoritePlaylist(playlistName: String): List { + return playlists( + makePlaylistCursor( + PlaylistsColumns.NAME + "=?", + arrayOf(playlistName) + ) + ) + } + + override fun deletePlaylist(playlistId: Int) { + val localUri = MediaStore.Audio.Playlists.EXTERNAL_CONTENT_URI + val localStringBuilder = StringBuilder() + localStringBuilder.append("_id IN (") + localStringBuilder.append(playlistId) + localStringBuilder.append(")") + contentResolver.delete(localUri, localStringBuilder.toString(), null) + } + + private fun getPlaylistFromCursorImpl( + cursor: Cursor + ): Playlist { + val id = cursor.getInt(0) + val name = cursor.getString(1) + return Playlist(id, name) + } + + override fun playlistSongs(playlistId: Int): List { + val songs = arrayListOf() + val cursor = makePlaylistSongCursor(playlistId) + + if (cursor != null && cursor.moveToFirst()) { + do { + songs.add(getPlaylistSongFromCursorImpl(cursor, playlistId)) + } while (cursor.moveToNext()) + } + cursor?.close() + return songs + } + + private fun getPlaylistSongFromCursorImpl(cursor: Cursor, playlistId: Int): PlaylistSong { + val id = cursor.getInt(0) + val title = cursor.getString(1) + val trackNumber = cursor.getInt(2) + val year = cursor.getInt(3) + val duration = cursor.getLong(4) + val data = cursor.getString(5) + val dateModified = cursor.getLong(6) + val albumId = cursor.getInt(7) + val albumName = cursor.getString(8) + val artistId = cursor.getInt(9) + val artistName = cursor.getString(10) + val idInPlaylist = cursor.getInt(11) + val composer = cursor.getString(12) + val albumArtist = cursor.getString(13) + return PlaylistSong( + id, + title, + trackNumber, + year, + duration, + data, + dateModified, + albumId, + albumName, + artistId, + artistName, + playlistId, + idInPlaylist, + composer, + albumArtist + ) + } + + private fun makePlaylistCursor( + selection: String?, + values: Array? + ): Cursor? { + return contentResolver.query( + MediaStore.Audio.Playlists.EXTERNAL_CONTENT_URI, + arrayOf( + BaseColumns._ID, /* 0 */ + PlaylistsColumns.NAME /* 1 */ + ), + selection, + values, + MediaStore.Audio.Playlists.DEFAULT_SORT_ORDER + ) + } + + + private fun makePlaylistSongCursor(playlistId: Int): Cursor? { + return contentResolver.query( + MediaStore.Audio.Playlists.Members.getContentUri("external", playlistId.toLong()), + arrayOf( + MediaStore.Audio.Playlists.Members.AUDIO_ID, // 0 + MediaStore.Audio.AudioColumns.TITLE, // 1 + MediaStore.Audio.AudioColumns.TRACK, // 2 + MediaStore.Audio.AudioColumns.YEAR, // 3 + MediaStore.Audio.AudioColumns.DURATION, // 4 + MediaStore.Audio.AudioColumns.DATA, // 5 + MediaStore.Audio.AudioColumns.DATE_MODIFIED, // 6 + MediaStore.Audio.AudioColumns.ALBUM_ID, // 7 + MediaStore.Audio.AudioColumns.ALBUM, // 8 + MediaStore.Audio.AudioColumns.ARTIST_ID, // 9 + MediaStore.Audio.AudioColumns.ARTIST, // 10 + MediaStore.Audio.Playlists.Members._ID,//11 + MediaStore.Audio.AudioColumns.COMPOSER,//12 + "album_artist"//13 + ), Constants.IS_MUSIC, null, MediaStore.Audio.Playlists.Members.DEFAULT_SORT_ORDER + ) + } +} diff --git a/app/src/main/java/io/github/muntashirakon/music/loaders/PlaylistSongsLoader.kt b/app/src/main/java/io/github/muntashirakon/music/repository/PlaylistSongsLoader.kt similarity index 80% rename from app/src/main/java/io/github/muntashirakon/music/loaders/PlaylistSongsLoader.kt rename to app/src/main/java/io/github/muntashirakon/music/repository/PlaylistSongsLoader.kt index 366207057..3fe4b814c 100644 --- a/app/src/main/java/io/github/muntashirakon/music/loaders/PlaylistSongsLoader.kt +++ b/app/src/main/java/io/github/muntashirakon/music/repository/PlaylistSongsLoader.kt @@ -12,7 +12,7 @@ * See the GNU General Public License for more details. */ -package io.github.muntashirakon.music.loaders +package io.github.muntashirakon.music.repository import android.content.Context import android.database.Cursor @@ -34,19 +34,31 @@ object PlaylistSongsLoader { fun getPlaylistSongList( context: Context, playlist: Playlist - ): ArrayList { - return (playlist as? AbsCustomPlaylist)?.getSongs(context) - ?: getPlaylistSongList(context, playlist.id) + ): List { + return if (playlist is AbsCustomPlaylist) { + return playlist.songs() + } else { + getPlaylistSongList(context, playlist.id) + } } @JvmStatic fun getPlaylistSongList(context: Context, playlistId: Int): ArrayList { val songs = arrayListOf() - val cursor = makePlaylistSongCursor(context, playlistId) + val cursor = + makePlaylistSongCursor( + context, + playlistId + ) if (cursor != null && cursor.moveToFirst()) { do { - songs.add(getPlaylistSongFromCursorImpl(cursor, playlistId)) + songs.add( + getPlaylistSongFromCursorImpl( + cursor, + playlistId + ) + ) } while (cursor.moveToNext()) } cursor?.close() @@ -67,7 +79,7 @@ object PlaylistSongsLoader { val artistName = cursor.getString(10) val idInPlaylist = cursor.getInt(11) val composer = cursor.getString(12) - + val albumArtist = cursor.getString(13) return PlaylistSong( id, title, @@ -82,7 +94,8 @@ object PlaylistSongsLoader { artistName, playlistId, idInPlaylist, - composer + composer, + albumArtist ) } @@ -103,10 +116,9 @@ object PlaylistSongsLoader { AudioColumns.ARTIST_ID, // 9 AudioColumns.ARTIST, // 10 MediaStore.Audio.Playlists.Members._ID,//11 - AudioColumns.COMPOSER - )// 12 - , IS_MUSIC, null, - MediaStore.Audio.Playlists.Members.DEFAULT_SORT_ORDER + AudioColumns.COMPOSER,//12 + "album_artist"//13 + ), IS_MUSIC, null, MediaStore.Audio.Playlists.Members.DEFAULT_SORT_ORDER ) } catch (e: SecurityException) { return null diff --git a/app/src/main/java/io/github/muntashirakon/music/repository/Repository.kt b/app/src/main/java/io/github/muntashirakon/music/repository/Repository.kt new file mode 100644 index 000000000..808736416 --- /dev/null +++ b/app/src/main/java/io/github/muntashirakon/music/repository/Repository.kt @@ -0,0 +1,311 @@ +/* + * 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.repository + +import android.content.Context +import io.github.muntashirakon.music.* +import io.github.muntashirakon.music.model.* +import io.github.muntashirakon.music.model.smartplaylist.NotPlayedPlaylist +import io.github.muntashirakon.music.network.LastFMService +import io.github.muntashirakon.music.network.model.LastFmAlbum +import io.github.muntashirakon.music.network.model.LastFmArtist +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.flow + +interface Repository { + + suspend fun allAlbums(): List + + suspend fun albumById(albumId: Int): Album + + suspend fun allSongs(): List + + suspend fun allArtists(): List + + suspend fun albumArtists(): List + + suspend fun allPlaylists(): List + + suspend fun allGenres(): List + + suspend fun search(query: String?): MutableList + + suspend fun getPlaylistSongs(playlist: Playlist): List + + suspend fun getGenre(genreId: Int): List + + suspend fun artistInfo(name: String, lang: String?, cache: String?): LastFmArtist + + suspend fun albumInfo(artist: String, album: String): LastFmAlbum + + suspend fun artistById(artistId: Int): Artist + + suspend fun recentArtists(): List + + suspend fun topArtists(): List + + suspend fun topAlbums(): List + + suspend fun recentAlbums(): List + + suspend fun recentArtistsHome(): Home + + suspend fun topArtistsHome(): Home + + suspend fun topAlbumsHome(): Home + + suspend fun recentAlbumsHome(): Home + + suspend fun favoritePlaylistHome(): Home + + suspend fun suggestionsHome(): Home + + suspend fun genresHome(): Home + + suspend fun playlists(): Home + + suspend fun homeSections(): List + + suspend fun homeSectionsFlow(): Flow>> + + fun songsFlow(): Flow>> + + fun albumsFlow(): Flow>> + + fun artistsFlow(): Flow>> + + fun playlistsFlow(): Flow>> + + fun genresFlow(): Flow>> + + suspend fun playlist(playlistId: Int): Playlist +} + +class RealRepository( + private val context: Context, + private val lastFMService: LastFMService, + private val songRepository: SongRepository, + private val albumRepository: AlbumRepository, + private val artistRepository: ArtistRepository, + private val genreRepository: GenreRepository, + private val lastAddedRepository: LastAddedRepository, + private val playlistRepository: PlaylistRepository, + private val searchRepository: RealSearchRepository, + private val playedTracksRepository: TopPlayedRepository +) : Repository { + + override suspend fun allAlbums(): List = albumRepository.albums() + + override suspend fun albumById(albumId: Int): Album = albumRepository.album(albumId) + + override suspend fun allArtists(): List = artistRepository.artists() + + override suspend fun albumArtists(): List = artistRepository.albumArtists() + + override suspend fun artistById(artistId: Int): Artist = artistRepository.artist(artistId) + + override suspend fun recentArtists(): List = lastAddedRepository.recentArtists() + + override suspend fun recentAlbums(): List = lastAddedRepository.recentAlbums() + + override suspend fun topArtists(): List = playedTracksRepository.topArtists() + + override suspend fun topAlbums(): List = playedTracksRepository.topAlbums() + + override suspend fun allPlaylists(): List = playlistRepository.playlists() + + override suspend fun allGenres(): List = genreRepository.genres() + + override suspend fun allSongs(): List = songRepository.songs() + + + override suspend fun search(query: String?): MutableList = + searchRepository.searchAll(context, query) + + override suspend fun getPlaylistSongs(playlist: Playlist): List { + return if (playlist is AbsCustomPlaylist) { + playlist.songs() + } else { + PlaylistSongsLoader.getPlaylistSongList(context, playlist.id) + } + } + + override suspend fun getGenre(genreId: Int): List = genreRepository.songs(genreId) + + + override suspend fun artistInfo( + name: String, + lang: String?, + cache: String? + ): LastFmArtist = lastFMService.artistInfo(name, lang, cache) + + + override suspend fun albumInfo( + artist: String, + album: String + ): LastFmAlbum = lastFMService.albumInfo(artist, album) + + @ExperimentalCoroutinesApi + override suspend fun homeSectionsFlow(): Flow>> { + val homes = MutableStateFlow>>(value = Result.Loading) + println("homeSections:Loading") + val homeSections = mutableListOf() + val sections = listOf( + topArtistsHome(), + topAlbumsHome(), + recentArtistsHome(), + recentAlbumsHome(), + suggestionsHome(), + favoritePlaylistHome(), + genresHome() + ) + for (section in sections) { + if (section.arrayList.isNotEmpty()) { + println("${section.homeSection} -> ${section.arrayList.size}") + homeSections.add(section) + } + } + if (homeSections.isEmpty()) { + homes.value = Result.Error + } else { + homes.value = Result.Success(homeSections) + } + return homes + } + + override suspend fun homeSections(): List { + val homeSections = mutableListOf() + val sections = listOf( + topArtistsHome(), + topAlbumsHome(), + recentArtistsHome(), + recentAlbumsHome(), + suggestionsHome(), + favoritePlaylistHome() + ) + for (section in sections) { + if (section.arrayList.isNotEmpty()) { + println("${section.homeSection} -> ${section.arrayList.size}") + homeSections.add(section) + } + } + return homeSections + } + + override suspend fun playlists(): Home { + val playlist = playlistRepository.playlists() + return Home(playlist, TOP_ALBUMS) + } + + override suspend fun playlist(playlistId: Int) = + playlistRepository.playlist(playlistId) + + override suspend fun suggestionsHome(): Home { + val songs = + NotPlayedPlaylist().songs().shuffled().takeIf { + it.size > 9 + } ?: emptyList() + println(songs.size) + return Home(songs, SUGGESTIONS) + } + + override suspend fun genresHome(): Home { + val genres = genreRepository.genres().shuffled() + return Home(genres, GENRES) + } + + + override suspend fun recentArtistsHome(): Home { + val artists = lastAddedRepository.recentArtists().take(5) + return Home(artists, RECENT_ARTISTS) + } + + override suspend fun recentAlbumsHome(): Home { + val albums = lastAddedRepository.recentAlbums().take(5) + return Home(albums, RECENT_ALBUMS) + } + + override suspend fun topAlbumsHome(): Home { + val albums = playedTracksRepository.topAlbums().take(5) + return Home(albums, TOP_ALBUMS) + } + + override suspend fun topArtistsHome(): Home { + val artists = playedTracksRepository.topArtists().take(5) + return Home(artists, TOP_ARTISTS) + } + + override suspend fun favoritePlaylistHome(): Home { + val playlists = + playlistRepository.favoritePlaylist(context.getString(R.string.favorites)).take(5) + val songs = if (playlists.isNotEmpty()) + PlaylistSongsLoader.getPlaylistSongList(context, playlists[0]) + else emptyList() + + return Home(songs, FAVOURITES) + } + + override fun songsFlow(): Flow>> = flow { + emit(Result.Loading) + val data = songRepository.songs() + if (data.isEmpty()) { + emit(Result.Error) + } else { + emit(Result.Success(data)) + } + } + + override fun albumsFlow(): Flow>> = flow { + emit(Result.Loading) + val data = albumRepository.albums() + if (data.isEmpty()) { + emit(Result.Error) + } else { + emit(Result.Success(data)) + } + } + + override fun artistsFlow(): Flow>> = flow { + emit(Result.Loading) + val data = artistRepository.artists() + if (data.isEmpty()) { + emit(Result.Error) + } else { + emit(Result.Success(data)) + } + } + + override fun playlistsFlow(): Flow>> = flow { + emit(Result.Loading) + val data = playlistRepository.playlists() + if (data.isEmpty()) { + emit(Result.Error) + } else { + emit(Result.Success(data)) + } + } + + override fun genresFlow(): Flow>> = flow { + emit(Result.Loading) + val data = genreRepository.genres() + if (data.isEmpty()) { + emit(Result.Error) + } else { + emit(Result.Success(data)) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/io/github/muntashirakon/music/loaders/SearchLoader.kt b/app/src/main/java/io/github/muntashirakon/music/repository/SearchRepository.kt similarity index 63% rename from app/src/main/java/io/github/muntashirakon/music/loaders/SearchLoader.kt rename to app/src/main/java/io/github/muntashirakon/music/repository/SearchRepository.kt index e217fc4f7..40b0eb23c 100644 --- a/app/src/main/java/io/github/muntashirakon/music/loaders/SearchLoader.kt +++ b/app/src/main/java/io/github/muntashirakon/music/repository/SearchRepository.kt @@ -12,48 +12,51 @@ * See the GNU General Public License for more details. */ -package io.github.muntashirakon.music.loaders +package io.github.muntashirakon.music.repository import android.content.Context import io.github.muntashirakon.music.R import io.github.muntashirakon.music.model.Genre import java.util.* -object SearchLoader { +class RealSearchRepository( + private val songRepository: SongRepository, + private val albumRepository: AlbumRepository, + private val artistRepository: RealArtistRepository, + private val genreRepository: GenreRepository, + private val playlistRepository: PlaylistRepository +) { fun searchAll(context: Context, query: String?): MutableList { val results = mutableListOf() query?.let { searchString -> - val songs = SongLoader.getSongs(context, searchString) + val songs = songRepository.songs(searchString) if (songs.isNotEmpty()) { results.add(context.resources.getString(R.string.songs)) results.addAll(songs) } - - val artists = ArtistLoader.getArtists(context, searchString) + val artists = artistRepository.artists(searchString) if (artists.isNotEmpty()) { results.add(context.resources.getString(R.string.artists)) results.addAll(artists) } - val albums = AlbumLoader.getAlbums(context, searchString) + val albums = albumRepository.albums(searchString) if (albums.isNotEmpty()) { results.add(context.resources.getString(R.string.albums)) results.addAll(albums) } - val genres: List = GenreLoader.searchGenres(context) - .filter { genre -> - genre.name.toLowerCase(Locale.getDefault()) - .contains(searchString.toLowerCase(Locale.getDefault())) - } + val genres: List = genreRepository.genres().filter { genre -> + genre.name.toLowerCase(Locale.getDefault()) + .contains(searchString.toLowerCase(Locale.getDefault())) + } if (genres.isNotEmpty()) { results.add(context.resources.getString(R.string.genres)) results.addAll(genres) } - val playlist = PlaylistLoader.getAllPlaylists(context) - .filter { playlist -> - playlist.name.toLowerCase(Locale.getDefault()) - .contains(searchString.toLowerCase(Locale.getDefault())) - } + val playlist = playlistRepository.playlists().filter { playlist -> + playlist.name.toLowerCase(Locale.getDefault()) + .contains(searchString.toLowerCase(Locale.getDefault())) + } if (playlist.isNotEmpty()) { results.add(context.getString(R.string.playlists)) results.addAll(playlist) diff --git a/app/src/main/java/io/github/muntashirakon/music/loaders/SongLoader.kt b/app/src/main/java/io/github/muntashirakon/music/repository/SongRepository.kt similarity index 66% rename from app/src/main/java/io/github/muntashirakon/music/loaders/SongLoader.kt rename to app/src/main/java/io/github/muntashirakon/music/repository/SongRepository.kt index 7794126c5..76dc41d70 100644 --- a/app/src/main/java/io/github/muntashirakon/music/loaders/SongLoader.kt +++ b/app/src/main/java/io/github/muntashirakon/music/repository/SongRepository.kt @@ -12,7 +12,7 @@ * See the GNU General Public License for more details. */ -package io.github.muntashirakon.music.loaders +package io.github.muntashirakon.music.repository import android.content.Context import android.database.Cursor @@ -29,55 +29,63 @@ import java.util.* /** * Created by hemanths on 10/08/17. */ +interface SongRepository { -object SongLoader { + fun songs(): List - fun getAllSongs( - context: Context - ): ArrayList { - val cursor = makeSongCursor(context, null, null) - return getSongs(cursor) + fun songs(cursor: Cursor?): List + + fun songs(query: String): List + + fun songsByFilePath(filePath: String): List + + fun song(cursor: Cursor?): Song + + fun song(songId: Int): Song +} + +class RealSongRepository(private val context: Context) : SongRepository { + + override fun songs(): List { + return songs(makeSongCursor(null, null)) } - fun getSongs( - cursor: Cursor? - ): ArrayList { + override fun songs(cursor: Cursor?): List { val songs = arrayListOf() if (cursor != null && cursor.moveToFirst()) { do { songs.add(getSongFromCursorImpl(cursor)) } while (cursor.moveToNext()) } - cursor?.close() return songs } - fun getSongs( - context: Context, - query: String - ): ArrayList { - val cursor = makeSongCursor(context, AudioColumns.TITLE + " LIKE ?", arrayOf("%$query%")) - return getSongs(cursor) - } - - fun getSong( - cursor: Cursor? - ): Song { - val song: Song - if (cursor != null && cursor.moveToFirst()) { - song = getSongFromCursorImpl(cursor) + override fun song(cursor: Cursor?): Song { + val song: Song = if (cursor != null && cursor.moveToFirst()) { + getSongFromCursorImpl(cursor) } else { - song = Song.emptySong + Song.emptySong } cursor?.close() return song } - @JvmStatic - fun getSong(context: Context, queryId: Int): Song { - val cursor = makeSongCursor(context, AudioColumns._ID + "=?", arrayOf(queryId.toString())) - return getSong(cursor) + override fun songs(query: String): List { + return songs(makeSongCursor(AudioColumns.TITLE + " LIKE ?", arrayOf("%$query%"))) + } + + override fun song(songId: Int): Song { + return song(makeSongCursor(AudioColumns._ID + "=?", arrayOf(songId.toString()))) + } + + override fun songsByFilePath(filePath: String): List { + return songs( + makeSongCursor( + MediaStore.Audio.AudioColumns.DATA + "=?", + arrayOf(filePath) + ) + ) } private fun getSongFromCursorImpl( @@ -95,16 +103,26 @@ object SongLoader { val artistId = cursor.getInt(9) val artistName = cursor.getString(10) val composer = cursor.getString(11) - + val albumArtist = cursor.getString(12) return Song( - id, title, trackNumber, year, duration, data, dateModified, albumId, - albumName ?: "", artistId, artistName, composer ?: "" + id, + title, + trackNumber, + year, + duration, + data, + dateModified, + albumId, + albumName ?: "", + artistId, + artistName ?: "", + composer ?: "", + albumArtist ?: "" ) } @JvmOverloads fun makeSongCursor( - context: Context, selection: String?, selectionValues: Array?, sortOrder: String = PreferenceUtil.songSortOrder @@ -120,16 +138,24 @@ object SongLoader { // Blacklist val paths = BlacklistStore.getInstance(context).paths if (paths.isNotEmpty()) { - selectionFinal = generateBlacklistSelection(selectionFinal, paths.size) - selectionValuesFinal = addBlacklistSelectionValues(selectionValuesFinal, paths) + selectionFinal = + generateBlacklistSelection( + selectionFinal, + paths.size + ) + selectionValuesFinal = + addBlacklistSelectionValues( + selectionValuesFinal, + paths + ) } - + selectionFinal = + selectionFinal + " AND " + MediaStore.Audio.Media.DURATION + ">= " + (PreferenceUtil.filterLength * 1000) try { return context.contentResolver.query( MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, baseProjection, - selectionFinal + " AND " + MediaStore.Audio.Media.DURATION + ">= " + - (PreferenceUtil.filterLength * 1000), + selectionFinal, selectionValuesFinal, sortOrder ) diff --git a/app/src/main/java/io/github/muntashirakon/music/loaders/SortedCursor.java b/app/src/main/java/io/github/muntashirakon/music/repository/SortedCursor.java similarity index 99% rename from app/src/main/java/io/github/muntashirakon/music/loaders/SortedCursor.java rename to app/src/main/java/io/github/muntashirakon/music/repository/SortedCursor.java index a8b5ccc57..c036edbea 100644 --- a/app/src/main/java/io/github/muntashirakon/music/loaders/SortedCursor.java +++ b/app/src/main/java/io/github/muntashirakon/music/repository/SortedCursor.java @@ -11,7 +11,7 @@ * 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; +package io.github.muntashirakon.music.repository; import android.database.AbstractCursor; import android.database.Cursor; diff --git a/app/src/main/java/io/github/muntashirakon/music/loaders/SortedLongCursor.java b/app/src/main/java/io/github/muntashirakon/music/repository/SortedLongCursor.java similarity index 99% rename from app/src/main/java/io/github/muntashirakon/music/loaders/SortedLongCursor.java rename to app/src/main/java/io/github/muntashirakon/music/repository/SortedLongCursor.java index 9123b1337..575f69df0 100644 --- a/app/src/main/java/io/github/muntashirakon/music/loaders/SortedLongCursor.java +++ b/app/src/main/java/io/github/muntashirakon/music/repository/SortedLongCursor.java @@ -11,7 +11,7 @@ * 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; +package io.github.muntashirakon.music.repository; import android.database.AbstractCursor; import android.database.Cursor; diff --git a/app/src/main/java/io/github/muntashirakon/music/loaders/TopAndRecentlyPlayedTracksLoader.kt b/app/src/main/java/io/github/muntashirakon/music/repository/TopPlayedRepository.kt similarity index 62% rename from app/src/main/java/io/github/muntashirakon/music/loaders/TopAndRecentlyPlayedTracksLoader.kt rename to app/src/main/java/io/github/muntashirakon/music/repository/TopPlayedRepository.kt index afb2f3109..effd5921c 100644 --- a/app/src/main/java/io/github/muntashirakon/music/loaders/TopAndRecentlyPlayedTracksLoader.kt +++ b/app/src/main/java/io/github/muntashirakon/music/repository/TopPlayedRepository.kt @@ -12,59 +12,85 @@ * See the GNU General Public License for more details. */ -package io.github.muntashirakon.music.loaders +package io.github.muntashirakon.music.repository import android.content.Context import android.database.Cursor import android.provider.BaseColumns import android.provider.MediaStore import io.github.muntashirakon.music.Constants.NUMBER_OF_TOP_TRACKS -import io.github.muntashirakon.music.loaders.SongLoader.makeSongCursor 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.providers.HistoryStore import io.github.muntashirakon.music.providers.SongPlayCountStore import io.github.muntashirakon.music.util.PreferenceUtil -import java.util.* /** * Created by hemanths on 16/08/17. */ -object TopAndRecentlyPlayedTracksLoader { +interface TopPlayedRepository { + fun recentlyPlayedTracks(): List - fun getRecentlyPlayedTracks(context: Context): ArrayList { - return SongLoader.getSongs(makeRecentTracksCursorAndClearUpDatabase(context)) + fun topTracks(): List + + fun notRecentlyPlayedTracks(): List + + fun topAlbums(): List + + fun topArtists(): List +} + +class RealTopPlayedRepository( + private val context: Context, + private val songRepository: RealSongRepository, + private val albumRepository: RealAlbumRepository, + private val artistRepository: RealArtistRepository +) : TopPlayedRepository { + + override fun recentlyPlayedTracks(): List { + return songRepository.songs(makeRecentTracksCursorAndClearUpDatabase()) } - fun getTopTracks(context: Context): ArrayList { - return SongLoader.getSongs(makeTopTracksCursorAndClearUpDatabase(context)) + override fun topTracks(): List { + return songRepository.songs(makeTopTracksCursorAndClearUpDatabase()) } - fun getNotRecentlyPlayedTracks(context: Context): ArrayList { - val allSongs = SongLoader.getSongs( - makeSongCursor( - context, - null, null, - MediaStore.Audio.Media.DATE_ADDED + " ASC" + override fun notRecentlyPlayedTracks(): List { + val allSongs = mutableListOf().apply { + addAll( + songRepository.songs( + songRepository.makeSongCursor( + null, null, + MediaStore.Audio.Media.DATE_ADDED + " ASC" + ) + ) ) + } + val playedSongs = songRepository.songs( + makePlayedTracksCursorAndClearUpDatabase() ) - val playedSongs = SongLoader.getSongs( - makePlayedTracksCursorAndClearUpDatabase(context) - ) - val notRecentlyPlayedSongs = SongLoader.getSongs( - makeNotRecentTracksCursorAndClearUpDatabase(context) + val notRecentlyPlayedSongs = songRepository.songs( + makeNotRecentTracksCursorAndClearUpDatabase() ) allSongs.removeAll(playedSongs) allSongs.addAll(notRecentlyPlayedSongs) return allSongs } - private fun makeTopTracksCursorAndClearUpDatabase(context: Context): Cursor? { - val retCursor = makeTopTracksCursorImpl(context) + override fun topAlbums(): List { + return albumRepository.splitIntoAlbums(topTracks()) + } + override fun topArtists(): List { + return artistRepository.splitIntoArtists(topAlbums()) + } + + + private fun makeTopTracksCursorAndClearUpDatabase(): Cursor? { + val retCursor = makeTopTracksCursorImpl() // clean up the databases with any ids not found if (retCursor != null) { val missingIds = retCursor.missingIds @@ -77,33 +103,31 @@ object TopAndRecentlyPlayedTracksLoader { return retCursor } - private fun makeRecentTracksCursorImpl(context: Context): SortedLongCursor? { + private fun makeRecentTracksCursorImpl(): SortedLongCursor? { // first get the top results ids from the internal database val songs = HistoryStore.getInstance(context).queryRecentIds() songs.use { return makeSortedCursor( - context, it, it.getColumnIndex(HistoryStore.RecentStoreColumns.ID) ) } } - private fun makeTopTracksCursorImpl(context: Context): SortedLongCursor? { + private fun makeTopTracksCursorImpl(): SortedLongCursor? { // first get the top results ids from the internal database val songs = SongPlayCountStore.getInstance(context).getTopPlayedResults(NUMBER_OF_TOP_TRACKS) songs.use { localSongs -> return makeSortedCursor( - context, localSongs, + localSongs, localSongs.getColumnIndex(SongPlayCountStore.SongPlayCountColumns.ID) ) } } private fun makeSortedCursor( - context: Context, cursor: Cursor?, idColumn: Int ): SortedLongCursor? { @@ -131,48 +155,46 @@ object TopAndRecentlyPlayedTracksLoader { selection.append(")") // get a list of songs with the data given the selection statement - val songCursor = SongLoader.makeSongCursor(context, selection.toString(), null) + val songCursor = songRepository.makeSongCursor(selection.toString(), null) if (songCursor != null) { // now return the wrapped TopTracksCursor to handle sorting given order - return SortedLongCursor(songCursor, order, BaseColumns._ID) + return SortedLongCursor( + songCursor, + order, + BaseColumns._ID + ) } } return null } - fun getTopAlbums( - context: Context - ): ArrayList { - arrayListOf() - return AlbumLoader.splitIntoAlbums(getTopTracks(context)) + private fun makeRecentTracksCursorAndClearUpDatabase(): Cursor? { + return makeRecentTracksCursorAndClearUpDatabaseImpl( + ignoreCutoffTime = false, + reverseOrder = false + ) } - fun getTopArtists(context: Context): ArrayList { - return ArtistLoader.splitIntoArtists(getTopAlbums(context)) + private fun makePlayedTracksCursorAndClearUpDatabase(): Cursor? { + return makeRecentTracksCursorAndClearUpDatabaseImpl( + ignoreCutoffTime = true, + reverseOrder = false + ) } - - fun makeRecentTracksCursorAndClearUpDatabase(context: Context): Cursor? { - return makeRecentTracksCursorAndClearUpDatabaseImpl(context, false, false) - } - - - fun makePlayedTracksCursorAndClearUpDatabase(context: Context): Cursor? { - return makeRecentTracksCursorAndClearUpDatabaseImpl(context, true, false) - } - - - fun makeNotRecentTracksCursorAndClearUpDatabase(context: Context): Cursor? { - return makeRecentTracksCursorAndClearUpDatabaseImpl(context, false, true) + private fun makeNotRecentTracksCursorAndClearUpDatabase(): Cursor? { + return makeRecentTracksCursorAndClearUpDatabaseImpl( + ignoreCutoffTime = false, + reverseOrder = true + ) } private fun makeRecentTracksCursorAndClearUpDatabaseImpl( - context: Context, ignoreCutoffTime: Boolean, reverseOrder: Boolean ): SortedLongCursor? { - val retCursor = makeRecentTracksCursorImpl(context, ignoreCutoffTime, reverseOrder) + val retCursor = makeRecentTracksCursorImpl(ignoreCutoffTime, reverseOrder) // clean up the databases with any ids not found // clean up the databases with any ids not found if (retCursor != null) { @@ -186,9 +208,7 @@ object TopAndRecentlyPlayedTracksLoader { return retCursor } - private fun makeRecentTracksCursorImpl( - context: Context, ignoreCutoffTime: Boolean, reverseOrder: Boolean ): SortedLongCursor? { @@ -198,7 +218,6 @@ object TopAndRecentlyPlayedTracksLoader { HistoryStore.getInstance(context).queryRecentIds(cutoff * if (reverseOrder) -1 else 1) return songs.use { makeSortedCursor( - context, it, it.getColumnIndex(HistoryStore.RecentStoreColumns.ID) ) diff --git a/app/src/main/java/io/github/muntashirakon/music/service/MusicService.java b/app/src/main/java/io/github/muntashirakon/music/service/MusicService.java index f1d57ccaf..71b1fc625 100644 --- a/app/src/main/java/io/github/muntashirakon/music/service/MusicService.java +++ b/app/src/main/java/io/github/muntashirakon/music/service/MusicService.java @@ -327,7 +327,7 @@ public class MusicService extends Service implements } private static String getTrackUri(@NonNull Song song) { - return MusicUtil.getSongFileUri(song.getId()).toString(); + return MusicUtil.INSTANCE.getSongFileUri(song.getId()).toString(); } @Override @@ -790,7 +790,7 @@ public class MusicService extends Service implements pendingQuit = true; break; case TOGGLE_FAVORITE: - MusicUtil.toggleFavorite(getApplicationContext(), getCurrentSong()); + MusicUtil.INSTANCE.toggleFavorite(getApplicationContext(), getCurrentSong()); break; } } @@ -1214,7 +1214,7 @@ public class MusicService extends Service implements Playlist playlist = intent.getParcelableExtra(INTENT_EXTRA_PLAYLIST); int shuffleMode = intent.getIntExtra(INTENT_EXTRA_SHUFFLE_MODE, getShuffleMode()); if (playlist != null) { - ArrayList playlistSongs = playlist.getSongs(getApplicationContext()); + List playlistSongs = playlist.getSongs(); if (!playlistSongs.isEmpty()) { if (shuffleMode == SHUFFLE_MODE_SHUFFLE) { int startPosition = new Random().nextInt(playlistSongs.size()); @@ -1327,7 +1327,7 @@ public class MusicService extends Service implements TOGGLE_SHUFFLE, getString(R.string.action_toggle_shuffle), shuffleIcon) .build()); - final int favoriteIcon = MusicUtil.isFavorite(getApplicationContext(), getCurrentSong()) + final int favoriteIcon = MusicUtil.INSTANCE.isFavorite(getApplicationContext(), getCurrentSong()) ? R.drawable.ic_favorite : R.drawable.ic_favorite_border; stateBuilder.addCustomAction(new PlaybackStateCompat.CustomAction.Builder( TOGGLE_FAVORITE, getString(R.string.action_toggle_favorite), favoriteIcon) diff --git a/app/src/main/java/io/github/muntashirakon/music/util/AppRater.kt b/app/src/main/java/io/github/muntashirakon/music/util/AppRater.kt index 880fc96e2..8ed214a5b 100644 --- a/app/src/main/java/io/github/muntashirakon/music/util/AppRater.kt +++ b/app/src/main/java/io/github/muntashirakon/music/util/AppRater.kt @@ -18,8 +18,8 @@ import android.content.Context import android.content.Intent import android.content.SharedPreferences import android.net.Uri -import io.github.muntashirakon.music.R import com.google.android.material.dialog.MaterialAlertDialogBuilder +import io.github.muntashirakon.music.R object AppRater { private const val DO_NOT_SHOW_AGAIN = "do_not_show_again"// Package Name @@ -50,13 +50,6 @@ object AppRater { editor.putLong(DATE_FIRST_LAUNCH, dateFirstLaunch) } - // Wait at least n days before opening - if (launchCount >= LAUNCHES_UNTIL_PROMPT) { - if (System.currentTimeMillis() >= dateFirstLaunch + DAYS_UNTIL_PROMPT * 24 * 60 * 60 * 1000) { - showRateDialog(context, editor) - } - } - editor.commit() } diff --git a/app/src/main/java/io/github/muntashirakon/music/util/AutoGeneratedPlaylistBitmap.java b/app/src/main/java/io/github/muntashirakon/music/util/AutoGeneratedPlaylistBitmap.java index 2e4a76678..28a826062 100644 --- a/app/src/main/java/io/github/muntashirakon/music/util/AutoGeneratedPlaylistBitmap.java +++ b/app/src/main/java/io/github/muntashirakon/music/util/AutoGeneratedPlaylistBitmap.java @@ -162,7 +162,6 @@ public class AutoGeneratedPlaylistBitmap { } - ; Log.d(TAG, "getBitmapCollection: smalltime = " + (System.currentTimeMillis() - start)); if (round) return BitmapEditor.getRoundedCornerBitmap(bitmap, bitmap.getWidth() / 40); @@ -172,7 +171,7 @@ public class AutoGeneratedPlaylistBitmap { private static Bitmap getBitmapWithAlbumId(@NonNull Context context, Integer id) { try { return Glide.with(context) - .load(MusicUtil.getMediaStoreAlbumCoverUri(id)) + .load(MusicUtil.INSTANCE.getMediaStoreAlbumCoverUri(id)) .asBitmap() .into(200, 200) .get(); diff --git a/app/src/main/java/io/github/muntashirakon/music/util/FileUtil.java b/app/src/main/java/io/github/muntashirakon/music/util/FileUtil.java index 7a1eb0abb..c12296f9b 100644 --- a/app/src/main/java/io/github/muntashirakon/music/util/FileUtil.java +++ b/app/src/main/java/io/github/muntashirakon/music/util/FileUtil.java @@ -31,15 +31,14 @@ import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; -import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.LinkedList; import java.util.List; -import io.github.muntashirakon.music.loaders.SongLoader; -import io.github.muntashirakon.music.loaders.SortedCursor; import io.github.muntashirakon.music.model.Song; +import io.github.muntashirakon.music.repository.RealSongRepository; +import io.github.muntashirakon.music.repository.SortedCursor; public final class FileUtil { @@ -59,9 +58,9 @@ public final class FileUtil { } @NonNull - public static ArrayList matchFilesWithMediaStore(@NonNull Context context, - @Nullable List files) { - return SongLoader.INSTANCE.getSongs(makeSongCursor(context, files)); + public static List matchFilesWithMediaStore(@NonNull Context context, + @Nullable List files) { + return new RealSongRepository(context).songs(makeSongCursor(context, files)); } public static String safeGetCanonicalPath(File file) { @@ -89,7 +88,7 @@ public final class FileUtil { } } - Cursor songCursor = SongLoader.INSTANCE.makeSongCursor(context, selection, selection == null ? null : paths); + Cursor songCursor = new RealSongRepository(context).makeSongCursor(selection, selection == null ? null : paths); return songCursor == null ? null : new SortedCursor(songCursor, paths, MediaStore.Audio.AudioColumns.DATA); @@ -246,13 +245,9 @@ public final class FileUtil { .equals(android.os.Environment.MEDIA_MOUNTED); Boolean isSDSupportedDevice = Environment.isExternalStorageRemovable(); - if (isSDSupportedDevice && isSDPresent) { - // yes SD-card is present - return true; - } else { - return false; - // Sorry - } + // yes SD-card is present + // Sorry + return isSDSupportedDevice && isSDPresent; } public static File safeGetCanonicalFile(File file) { diff --git a/app/src/main/java/io/github/muntashirakon/music/util/MusicUtil.java b/app/src/main/java/io/github/muntashirakon/music/util/MusicUtil.java deleted file mode 100644 index 9f39309f1..000000000 --- a/app/src/main/java/io/github/muntashirakon/music/util/MusicUtil.java +++ /dev/null @@ -1,428 +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.util; - -import android.app.Activity; -import android.content.ContentResolver; -import android.content.ContentUris; -import android.content.ContentValues; -import android.content.Context; -import android.content.Intent; -import android.database.Cursor; -import android.net.Uri; -import android.os.Environment; -import android.provider.BaseColumns; -import android.provider.MediaStore; -import android.text.TextUtils; -import android.widget.Toast; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.core.content.FileProvider; - -import org.jaudiotagger.audio.AudioFileIO; -import org.jaudiotagger.tag.FieldKey; - -import java.io.File; -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; -import java.util.Locale; -import java.util.regex.Pattern; - -import io.github.muntashirakon.music.R; -import io.github.muntashirakon.music.helper.MusicPlayerRemote; -import io.github.muntashirakon.music.loaders.PlaylistLoader; -import io.github.muntashirakon.music.loaders.SongLoader; -import io.github.muntashirakon.music.model.Artist; -import io.github.muntashirakon.music.model.Playlist; -import io.github.muntashirakon.music.model.Song; -import io.github.muntashirakon.music.model.lyrics.AbsSynchronizedLyrics; -import io.github.muntashirakon.music.service.MusicService; - - -public class MusicUtil { - - public static final String TAG = MusicUtil.class.getSimpleName(); - - private static Playlist playlist; - - /** - * Build a concatenated string from the provided arguments - * The intended purpose is to show extra annotations - * to a music library item. - * Ex: for a given album --> buildInfoString(album.artist, album.songCount) - */ - @NonNull - public static String buildInfoString(@Nullable final String string1, @Nullable final String string2) { - // Skip empty strings - if (TextUtils.isEmpty(string1)) { - //noinspection ConstantConditions - return TextUtils.isEmpty(string2) ? "" : string2; - } - if (TextUtils.isEmpty(string2)) { - //noinspection ConstantConditions - return TextUtils.isEmpty(string1) ? "" : string1; - } - - return string1 + " • " + string2; - } - - @NonNull - public static File createAlbumArtFile() { - return new File(createAlbumArtDir(), String.valueOf(System.currentTimeMillis())); - } - - @NonNull - public static Intent createShareSongFileIntent(@NonNull final Song song, @NonNull Context context) { - try { - return new Intent() - .setAction(Intent.ACTION_SEND) - .putExtra(Intent.EXTRA_STREAM, FileProvider - .getUriForFile(context, context.getApplicationContext().getPackageName(), - new File(song.getData()))) - .addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) - .setType("audio/*"); - } catch (IllegalArgumentException e) { - // TODO the path is most likely not like /storage/emulated/0/... but something like /storage/28C7-75B0/... - e.printStackTrace(); - Toast.makeText(context, "Could not share this file, I'm aware of the issue.", Toast.LENGTH_SHORT).show(); - return new Intent(); - } - } - - public static void deleteAlbumArt(@NonNull Context context, int albumId) { - ContentResolver contentResolver = context.getContentResolver(); - Uri localUri = Uri.parse("content://media/external/audio/albumart"); - contentResolver.delete(ContentUris.withAppendedId(localUri, albumId), null, null); - contentResolver.notifyChange(localUri, null); - } - - public static void deleteTracks( - @NonNull final Activity activity, - @NonNull final List songs, - @Nullable final List safUris, - @Nullable final Runnable callback) { - final String[] projection = new String[]{ - BaseColumns._ID, MediaStore.MediaColumns.DATA - }; - - // Split the query into multiple batches, and merge the resulting cursors - int batchStart = 0; - int batchEnd = 0; - final int batchSize = 1000000 - / 10; // 10^6 being the SQLite limite on the query lenth in bytes, 10 being the max number of digits in an int, used to store the track ID - final int songCount = songs.size(); - - while (batchEnd < songCount) { - batchStart = batchEnd; - - final StringBuilder selection = new StringBuilder(); - selection.append(BaseColumns._ID + " IN ("); - - for (int i = 0; (i < batchSize - 1) && (batchEnd < songCount - 1); i++, batchEnd++) { - selection.append(songs.get(batchEnd).getId()); - selection.append(","); - } - // The last element of a batch - selection.append(songs.get(batchEnd).getId()); - batchEnd++; - selection.append(")"); - - try { - final Cursor cursor = activity.getContentResolver().query( - MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, projection, selection.toString(), - null, null); - // TODO: At this point, there is no guarantee that the size of the cursor is the same as the size of the selection string. - // Despite that, the Step 3 assumes that the safUris elements are tracking closely the content of the cursor. - - if (cursor != null) { - // Step 1: Remove selected tracks from the current playlist, as well - // as from the album art cache - cursor.moveToFirst(); - while (!cursor.isAfterLast()) { - final int id = cursor.getInt(0); - final Song song = SongLoader.getSong(activity, id); - MusicPlayerRemote.removeFromQueue(song); - cursor.moveToNext(); - } - - // Step 2: Remove selected tracks from the database - activity.getContentResolver().delete(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, - selection.toString(), null); - - // Step 3: Remove files from card - cursor.moveToFirst(); - int i = batchStart; - while (!cursor.isAfterLast()) { - final String name = cursor.getString(1); - final Uri safUri = safUris == null || safUris.size() <= i ? null : safUris.get(i); - SAFUtil.delete(activity, name, safUri); - i++; - cursor.moveToNext(); - } - cursor.close(); - } - } catch (SecurityException ignored) { - } - } - - activity.getContentResolver().notifyChange(Uri.parse("content://media"), null); - - activity.runOnUiThread(() -> { - Toast.makeText(activity, activity.getString(R.string.deleted_x_songs, songCount), Toast.LENGTH_SHORT) - .show(); - if (callback != null) { - callback.run(); - } - }); - } - - @NonNull - public static String getArtistInfoString(@NonNull final Context context, - @NonNull final Artist artist) { - int albumCount = artist.getAlbumCount(); - int songCount = artist.getSongCount(); - String albumString = albumCount == 1 ? context.getResources().getString(R.string.album) - : context.getResources().getString(R.string.albums); - String songString = songCount == 1 ? context.getResources().getString(R.string.song) - : context.getResources().getString(R.string.songs); - return albumCount + " " + albumString + " • " + songCount + " " + songString; - } - - //iTunes uses for example 1002 for track 2 CD1 or 3011 for track 11 CD3. - //this method converts those values to normal tracknumbers - public static int getFixedTrackNumber(int trackNumberToFix) { - return trackNumberToFix % 1000; - } - - @Nullable - public static String getLyrics(@NonNull Song song) { - String lyrics = null; - - File file = new File(song.getData()); - - try { - lyrics = AudioFileIO.read(file).getTagOrCreateDefault().getFirst(FieldKey.LYRICS); - } catch (Exception e) { - e.printStackTrace(); - } - - if (lyrics == null || lyrics.trim().isEmpty() || !AbsSynchronizedLyrics - .isSynchronized(lyrics)) { - File dir = file.getAbsoluteFile().getParentFile(); - - if (dir != null && dir.exists() && dir.isDirectory()) { - String format = ".*%s.*\\.(lrc|txt)"; - String filename = Pattern.quote(FileUtil.stripExtension(file.getName())); - String songtitle = Pattern.quote(song.getTitle()); - - final ArrayList patterns = new ArrayList<>(); - patterns.add(Pattern.compile(String.format(format, filename), - Pattern.CASE_INSENSITIVE | Pattern.UNICODE_CASE)); - patterns.add(Pattern.compile(String.format(format, songtitle), - Pattern.CASE_INSENSITIVE | Pattern.UNICODE_CASE)); - - File[] files = dir.listFiles(f -> { - for (Pattern pattern : patterns) { - if (pattern.matcher(f.getName()).matches()) { - return true; - } - } - return false; - }); - - if (files != null && files.length > 0) { - for (File f : files) { - try { - String newLyrics = FileUtil.read(f); - if (newLyrics != null && !newLyrics.trim().isEmpty()) { - if (AbsSynchronizedLyrics.isSynchronized(newLyrics)) { - return newLyrics; - } - lyrics = newLyrics; - } - } catch (Exception e) { - e.printStackTrace(); - } - } - } - } - } - - return lyrics; - } - - @NonNull - public static Uri getMediaStoreAlbumCoverUri(int albumId) { - final Uri sArtworkUri = Uri.parse("content://media/external/audio/albumart"); - return ContentUris.withAppendedId(sArtworkUri, albumId); - } - - @NonNull - public static Playlist getPlaylist() { - return playlist; - } - - public static void setPlaylist(@NonNull Playlist playlist) { - MusicUtil.playlist = playlist; - } - - @NonNull - public static String getPlaylistInfoString(@NonNull final Context context, @NonNull List songs) { - final long duration = getTotalDuration(songs); - - return MusicUtil.buildInfoString( - MusicUtil.getSongCountString(context, songs.size()), - MusicUtil.getReadableDurationString(duration) - ); - } - - public static String getReadableDurationString(long songDurationMillis) { - long minutes = (songDurationMillis / 1000) / 60; - long seconds = (songDurationMillis / 1000) % 60; - if (minutes < 60) { - return String.format(Locale.getDefault(), "%02d:%02d", minutes, seconds); - } else { - long hours = minutes / 60; - minutes = minutes % 60; - return String.format(Locale.getDefault(), "%02d:%02d:%02d", hours, minutes, seconds); - } - } - - @NonNull - public static String getSectionName(@Nullable String musicMediaTitle) { - try { - if (TextUtils.isEmpty(musicMediaTitle)) { - return ""; - } - - musicMediaTitle = musicMediaTitle.trim().toLowerCase(); - if (musicMediaTitle.startsWith("the ")) { - musicMediaTitle = musicMediaTitle.substring(4); - } else if (musicMediaTitle.startsWith("a ")) { - musicMediaTitle = musicMediaTitle.substring(2); - } - if (musicMediaTitle.isEmpty()) { - return ""; - } - return musicMediaTitle.substring(0, 1).toUpperCase(); - } catch (Exception e) { - return ""; - } - } - - @NonNull - public static String getSongCountString(@NonNull final Context context, int songCount) { - final String songString = songCount == 1 ? context.getResources().getString(R.string.song) - : context.getResources().getString(R.string.songs); - return songCount + " " + songString; - } - - public static Uri getSongFileUri(int songId) { - return ContentUris.withAppendedId(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, songId); - } - - public static long getTotalDuration(@NonNull List songs) { - long duration = 0; - for (int i = 0; i < songs.size(); i++) { - duration += songs.get(i).getDuration(); - } - return duration; - } - - @NonNull - public static String getYearString(int year) { - return year > 0 ? String.valueOf(year) : "-"; - } - - public static int indexOfSongInList(@NonNull List songs, int songId) { - for (int i = 0; i < songs.size(); i++) { - if (songs.get(i).getId() == songId) { - return i; - } - } - return -1; - } - - public static void insertAlbumArt(@NonNull Context context, int albumId, String path) { - ContentResolver contentResolver = context.getContentResolver(); - - Uri artworkUri = Uri.parse("content://media/external/audio/albumart"); - contentResolver.delete(ContentUris.withAppendedId(artworkUri, albumId), null, null); - - ContentValues values = new ContentValues(); - values.put("album_id", albumId); - values.put("_data", path); - - contentResolver.insert(artworkUri, values); - contentResolver.notifyChange(artworkUri, null); - } - - public static boolean isArtistNameUnknown(@Nullable String artistName) { - if (TextUtils.isEmpty(artistName)) { - return false; - } - if (artistName.equals(Artist.UNKNOWN_ARTIST_DISPLAY_NAME)) { - return true; - } - String tempName = artistName.trim().toLowerCase(); - return tempName.equals("unknown") || tempName.equals(""); - } - - public static boolean isFavorite(@NonNull final Context context, @NonNull final Song song) { - return PlaylistsUtil - .doPlaylistContains(context, getFavoritesPlaylist(context).id, song.getId()); - } - - public static boolean isFavoritePlaylist(@NonNull final Context context, - @NonNull final Playlist playlist) { - return playlist.name != null && playlist.name.equals(context.getString(R.string.favorites)); - } - - public static void toggleFavorite(@NonNull final Context context, @NonNull final Song song) { - if (isFavorite(context, song)) { - PlaylistsUtil.removeFromPlaylist(context, song, getFavoritesPlaylist(context).id); - } else { - PlaylistsUtil.addToPlaylist(context, song, getOrCreateFavoritesPlaylist(context).id, - false); - } - context.sendBroadcast(new Intent(MusicService.FAVORITE_STATE_CHANGED)); - } - - @NonNull - @SuppressWarnings("ResultOfMethodCallIgnored") - private static File createAlbumArtDir() { - File albumArtDir = new File(Environment.getExternalStorageDirectory(), "/albumthumbs/"); - if (!albumArtDir.exists()) { - albumArtDir.mkdirs(); - try { - new File(albumArtDir, ".nomedia").createNewFile(); - } catch (IOException e) { - e.printStackTrace(); - } - } - return albumArtDir; - } - - private static Playlist getFavoritesPlaylist(@NonNull final Context context) { - return PlaylistLoader.INSTANCE.getPlaylist(context, context.getString(R.string.favorites)); - } - - private static Playlist getOrCreateFavoritesPlaylist(@NonNull final Context context) { - return PlaylistLoader.INSTANCE.getPlaylist(context, - PlaylistsUtil.createPlaylist(context, context.getString(R.string.favorites))); - } -} diff --git a/app/src/main/java/io/github/muntashirakon/music/util/MusicUtil.kt b/app/src/main/java/io/github/muntashirakon/music/util/MusicUtil.kt new file mode 100644 index 000000000..c01e5488c --- /dev/null +++ b/app/src/main/java/io/github/muntashirakon/music/util/MusicUtil.kt @@ -0,0 +1,418 @@ +package io.github.muntashirakon.music.util + +import android.content.ContentUris +import android.content.ContentValues +import android.content.Context +import android.content.Intent +import android.net.Uri +import android.os.Environment +import android.provider.BaseColumns +import android.provider.MediaStore +import android.text.TextUtils +import android.widget.Toast +import androidx.core.content.FileProvider +import androidx.fragment.app.FragmentActivity +import io.github.muntashirakon.music.R +import io.github.muntashirakon.music.helper.MusicPlayerRemote.removeFromQueue +import io.github.muntashirakon.music.model.Artist +import io.github.muntashirakon.music.model.Playlist +import io.github.muntashirakon.music.model.Song +import io.github.muntashirakon.music.model.lyrics.AbsSynchronizedLyrics +import io.github.muntashirakon.music.repository.RealPlaylistRepository +import io.github.muntashirakon.music.repository.SongRepository +import io.github.muntashirakon.music.service.MusicService +import org.jaudiotagger.audio.AudioFileIO +import org.jaudiotagger.tag.FieldKey +import org.koin.core.KoinComponent +import org.koin.core.get +import java.io.File +import java.io.IOException +import java.util.* +import java.util.regex.Pattern + + +object MusicUtil : KoinComponent { + fun createShareSongFileIntent(song: Song, context: Context): Intent? { + return try { + Intent().setAction(Intent.ACTION_SEND).putExtra( + Intent.EXTRA_STREAM, + FileProvider.getUriForFile( + context, + context.applicationContext.packageName, + File(song.data) + ) + ).addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION).setType("audio/*") + } catch (e: IllegalArgumentException) { + // TODO the path is most likely not like /storage/emulated/0/... but something like /storage/28C7-75B0/... + e.printStackTrace() + Toast.makeText( + context, + "Could not share this file, I'm aware of the issue.", + Toast.LENGTH_SHORT + ).show() + Intent() + } + } + + fun buildInfoString(string1: String?, string2: String?): String { + if (string1.isNullOrEmpty()) { + return if (string2.isNullOrEmpty()) "" else string2 + } + return if (string2.isNullOrEmpty()) if (string1.isNullOrEmpty()) "" else string1 else "$string1 • $string2" + } + + fun createAlbumArtFile(): File { + return File( + createAlbumArtDir(), + System.currentTimeMillis().toString() + ) + } + + private fun createAlbumArtDir(): File { + val albumArtDir = File(Environment.getExternalStorageDirectory(), "/albumthumbs/") + if (!albumArtDir.exists()) { + albumArtDir.mkdirs() + try { + File(albumArtDir, ".nomedia").createNewFile() + } catch (e: IOException) { + e.printStackTrace() + } + } + return albumArtDir + } + + fun deleteAlbumArt(context: Context, albumId: Int) { + val contentResolver = context.contentResolver + val localUri = Uri.parse("content://media/external/audio/albumart") + contentResolver.delete(ContentUris.withAppendedId(localUri, albumId.toLong()), null, null) + contentResolver.notifyChange(localUri, null) + } + + fun getArtistInfoString( + context: Context, + artist: Artist + ): String { + val albumCount = artist.albumCount + val songCount = artist.songCount + val albumString = + if (albumCount == 1) context.resources.getString(R.string.album) + else context.resources.getString(R.string.albums) + val songString = + if (songCount == 1) context.resources.getString(R.string.song) + else context.resources.getString(R.string.songs) + return "$albumCount $albumString • $songCount $songString" + } + + //iTunes uses for example 1002 for track 2 CD1 or 3011 for track 11 CD3. + //this method converts those values to normal tracknumbers + fun getFixedTrackNumber(trackNumberToFix: Int): Int { + return trackNumberToFix % 1000 + } + + fun getLyrics(song: Song): String? { + var lyrics: String? = null + val file = File(song.data) + try { + lyrics = AudioFileIO.read(file).tagOrCreateDefault.getFirst(FieldKey.LYRICS) + } catch (e: Exception) { + e.printStackTrace() + } + if (lyrics == null || lyrics.trim { it <= ' ' }.isEmpty() || !AbsSynchronizedLyrics + .isSynchronized(lyrics) + ) { + val dir = file.absoluteFile.parentFile + if (dir != null && dir.exists() && dir.isDirectory) { + val format = ".*%s.*\\.(lrc|txt)" + val filename = Pattern.quote( + FileUtil.stripExtension(file.name) + ) + val songtitle = Pattern.quote(song.title) + val patterns = + ArrayList() + patterns.add( + Pattern.compile( + String.format(format, filename), + Pattern.CASE_INSENSITIVE or Pattern.UNICODE_CASE + ) + ) + patterns.add( + Pattern.compile( + String.format(format, songtitle), + Pattern.CASE_INSENSITIVE or Pattern.UNICODE_CASE + ) + ) + val files = + dir.listFiles { f: File -> + for (pattern in patterns) { + if (pattern.matcher(f.name).matches()) { + return@listFiles true + } + } + false + } + if (files != null && files.size > 0) { + for (f in files) { + try { + val newLyrics = + FileUtil.read(f) + if (newLyrics != null && !newLyrics.trim { it <= ' ' }.isEmpty()) { + if (AbsSynchronizedLyrics.isSynchronized(newLyrics)) { + return newLyrics + } + lyrics = newLyrics + } + } catch (e: Exception) { + e.printStackTrace() + } + } + } + } + } + return lyrics + } + + fun getMediaStoreAlbumCoverUri(albumId: Int): Uri { + val sArtworkUri = + Uri.parse("content://media/external/audio/albumart") + return ContentUris.withAppendedId(sArtworkUri, albumId.toLong()) + } + + + fun getPlaylistInfoString( + context: Context, + songs: List + ): String { + val duration = getTotalDuration(songs) + return buildInfoString( + getSongCountString(context, songs.size), + getReadableDurationString(duration) + ) + } + + fun getReadableDurationString(songDurationMillis: Long): String? { + var minutes = songDurationMillis / 1000 / 60 + val seconds = songDurationMillis / 1000 % 60 + return if (minutes < 60) { + String.format( + Locale.getDefault(), + "%02d:%02d", + minutes, + seconds + ) + } else { + val hours = minutes / 60 + minutes = minutes % 60 + String.format( + Locale.getDefault(), + "%02d:%02d:%02d", + hours, + minutes, + seconds + ) + } + } + + fun getSectionName(musicMediaTitle: String?): String { + var musicMediaTitle = musicMediaTitle + return try { + if (TextUtils.isEmpty(musicMediaTitle)) { + return "" + } + musicMediaTitle = musicMediaTitle!!.trim { it <= ' ' }.toLowerCase() + if (musicMediaTitle.startsWith("the ")) { + musicMediaTitle = musicMediaTitle.substring(4) + } else if (musicMediaTitle.startsWith("a ")) { + musicMediaTitle = musicMediaTitle.substring(2) + } + if (musicMediaTitle.isEmpty()) { + "" + } else musicMediaTitle.substring(0, 1).toUpperCase() + } catch (e: Exception) { + "" + } + } + + fun getSongCountString(context: Context, songCount: Int): String { + val songString = if (songCount == 1) context.resources + .getString(R.string.song) else context.resources.getString(R.string.songs) + return "$songCount $songString" + } + + fun getSongFileUri(songId: Int): Uri { + return ContentUris.withAppendedId( + MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, + songId.toLong() + ) + } + + fun getTotalDuration(songs: List): Long { + var duration: Long = 0 + for (i in songs.indices) { + duration += songs[i].duration + } + return duration + } + + fun getYearString(year: Int): String { + return if (year > 0) year.toString() else "-" + } + + fun indexOfSongInList(songs: List, songId: Int): Int { + for (i in songs.indices) { + if (songs[i].id == songId) { + return i + } + } + return -1 + } + + fun insertAlbumArt( + context: Context, + albumId: Int, + path: String? + ) { + val contentResolver = context.contentResolver + val artworkUri = + Uri.parse("content://media/external/audio/albumart") + contentResolver.delete(ContentUris.withAppendedId(artworkUri, albumId.toLong()), null, null) + val values = ContentValues() + values.put("album_id", albumId) + values.put("_data", path) + contentResolver.insert(artworkUri, values) + contentResolver.notifyChange(artworkUri, null) + } + + fun isArtistNameUnknown(artistName: String?): Boolean { + if (TextUtils.isEmpty(artistName)) { + return false + } + if (artistName == Artist.UNKNOWN_ARTIST_DISPLAY_NAME) { + return true + } + val tempName = artistName!!.trim { it <= ' ' }.toLowerCase() + return tempName == "unknown" || tempName == "" + } + + fun isFavorite(context: Context, song: Song): Boolean { + return PlaylistsUtil + .doPlaylistContains(context, getFavoritesPlaylist(context).id.toLong(), song.id) + } + + fun isFavoritePlaylist( + context: Context, + playlist: Playlist + ): Boolean { + return playlist.name != null && playlist.name == context.getString(R.string.favorites) + } + + fun toggleFavorite(context: Context, song: Song) { + if (isFavorite(context, song)) { + PlaylistsUtil.removeFromPlaylist(context, song, getFavoritesPlaylist(context).id) + } else { + PlaylistsUtil.addToPlaylist( + context, song, getOrCreateFavoritesPlaylist(context).id, + false + ) + } + context.sendBroadcast(Intent(MusicService.FAVORITE_STATE_CHANGED)) + } + + private fun getFavoritesPlaylist(context: Context): Playlist { + return RealPlaylistRepository(context.contentResolver).playlist(context.getString(R.string.favorites)) + } + + private fun getOrCreateFavoritesPlaylist(context: Context): Playlist { + return RealPlaylistRepository(context.contentResolver).playlist( + PlaylistsUtil.createPlaylist( + context, + context.getString(R.string.favorites) + ) + ) + } + + fun deleteTracks( + activity: FragmentActivity, + songs: List, + safUris: List?, + callback: Runnable? + ) { + val songRepository: SongRepository = get() + val projection = arrayOf( + BaseColumns._ID, MediaStore.MediaColumns.DATA + ) + // Split the query into multiple batches, and merge the resulting cursors + var batchStart = 0 + var batchEnd = 0 + val batchSize = + 1000000 / 10 // 10^6 being the SQLite limite on the query lenth in bytes, 10 being the max number of digits in an int, used to store the track ID + val songCount = songs.size + + while (batchEnd < songCount) { + batchStart = batchEnd + + val selection = StringBuilder() + selection.append(BaseColumns._ID + " IN (") + + var i = 0 + while (i < batchSize - 1 && batchEnd < songCount - 1) { + selection.append(songs[batchEnd].id) + selection.append(",") + i++ + batchEnd++ + } + // The last element of a batch + // The last element of a batch + selection.append(songs[batchEnd].id) + batchEnd++ + selection.append(")") + + try { + val cursor = activity.contentResolver.query( + MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, projection, selection.toString(), + null, null + ) + if (cursor != null) { + // Step 1: Remove selected tracks from the current playlist, as well + // as from the album art cache + cursor.moveToFirst() + while (!cursor.isAfterLast) { + val id = cursor.getInt(0) + val song: Song = songRepository.song(id) + removeFromQueue(song) + cursor.moveToNext() + } + + // Step 2: Remove selected tracks from the database + activity.contentResolver.delete( + MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, + selection.toString(), null + ) + // Step 3: Remove files from card + cursor.moveToFirst() + var index = batchStart + while (!cursor.isAfterLast) { + val name = cursor.getString(1) + val safUri = + if (safUris == null || safUris.size <= index) null else safUris[index] + SAFUtil.delete(activity, name, safUri) + index++ + cursor.moveToNext() + } + cursor.close() + } + } catch (ignored: SecurityException) { + + } + activity.contentResolver.notifyChange(Uri.parse("content://media"), null) + activity.runOnUiThread { + Toast.makeText( + activity, + activity.getString(R.string.deleted_x_songs, songCount), + Toast.LENGTH_SHORT + ) + .show() + callback?.run() + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/io/github/muntashirakon/music/util/NavigationUtil.java b/app/src/main/java/io/github/muntashirakon/music/util/NavigationUtil.java index b34099327..59f645fcc 100755 --- a/app/src/main/java/io/github/muntashirakon/music/util/NavigationUtil.java +++ b/app/src/main/java/io/github/muntashirakon/music/util/NavigationUtil.java @@ -27,23 +27,15 @@ import androidx.core.app.ActivityCompat; import org.jetbrains.annotations.NotNull; import io.github.muntashirakon.music.R; -import io.github.muntashirakon.music.activities.AboutActivity; import io.github.muntashirakon.music.activities.DriveModeActivity; -import io.github.muntashirakon.music.activities.genre.GenreDetailsActivity; import io.github.muntashirakon.music.activities.LicenseActivity; import io.github.muntashirakon.music.activities.LyricsActivity; import io.github.muntashirakon.music.activities.PlayingQueueActivity; -import io.github.muntashirakon.music.activities.playlist.PlaylistDetailActivity; -import io.github.muntashirakon.music.activities.search.SearchActivity; -import io.github.muntashirakon.music.activities.SettingsActivity; import io.github.muntashirakon.music.activities.UserInfoActivity; import io.github.muntashirakon.music.activities.WhatsNewActivity; -import io.github.muntashirakon.music.activities.albums.AlbumDetailsActivity; -import io.github.muntashirakon.music.activities.artists.ArtistDetailActivity; import io.github.muntashirakon.music.activities.bugreport.BugReportActivity; import io.github.muntashirakon.music.helper.MusicPlayerRemote; -import io.github.muntashirakon.music.model.Genre; -import io.github.muntashirakon.music.model.Playlist; + public class NavigationUtil { @@ -51,45 +43,6 @@ public class NavigationUtil { ActivityCompat.startActivity(activity, new Intent(activity, BugReportActivity.class), null); } - public static void goToAbout(@NonNull Activity activity) { - ActivityCompat.startActivity(activity, new Intent(activity, AboutActivity.class), null); - } - - public static void goToAlbum(@NonNull Activity activity, int albumId) { - Intent intent = new Intent(activity, AlbumDetailsActivity.class); - intent.putExtra(AlbumDetailsActivity.EXTRA_ALBUM_ID, albumId); - ActivityCompat.startActivity(activity, intent, null); - } - - public static void goToAlbumOptions(@NonNull Activity activity, - int albumId, - @NonNull ActivityOptions options) { - Intent intent = new Intent(activity, AlbumDetailsActivity.class); - intent.putExtra(AlbumDetailsActivity.EXTRA_ALBUM_ID, albumId); - ActivityCompat.startActivity(activity, intent, options.toBundle()); - } - - public static void goToArtist(@NonNull Activity activity, int i) { - Intent intent = new Intent(activity, ArtistDetailActivity.class); - intent.putExtra(ArtistDetailActivity.EXTRA_ARTIST_ID, i); - ActivityCompat.startActivity(activity, intent, null); - } - - public static void goToArtistOptions(@NotNull Activity activity, - int artistId, - @NonNull ActivityOptions options) { - - Intent intent = new Intent(activity, ArtistDetailActivity.class); - intent.putExtra(ArtistDetailActivity.EXTRA_ARTIST_ID, artistId); - ActivityCompat.startActivity(activity, intent, options.toBundle()); - } - - public static void goToGenre(@NonNull Activity activity, @NonNull Genre genre) { - Intent intent = new Intent(activity, GenreDetailsActivity.class); - intent.putExtra(GenreDetailsActivity.EXTRA_GENRE_ID, genre); - ActivityCompat.startActivity(activity, intent, null); - } - public static void goToLyrics(@NonNull Activity activity) { Intent intent = new Intent(activity, LyricsActivity.class); ActivityCompat.startActivity(activity, intent, null); @@ -104,34 +57,6 @@ public class NavigationUtil { ActivityCompat.startActivity(activity, intent, null); } - public static void goToPlaylistNew(@NonNull Activity activity, @NonNull Playlist playlist) { - Intent intent = new Intent(activity, PlaylistDetailActivity.class); - intent.putExtra(PlaylistDetailActivity.Companion.getEXTRA_PLAYLIST(), playlist); - ActivityCompat.startActivity(activity, intent, null); - } - - public static void goToSearch(@NonNull Activity activity, - @NonNull ActivityOptions activityOptions) { - ActivityCompat.startActivity(activity, new Intent(activity, SearchActivity.class), - activityOptions.toBundle()); - } - - public static void goToSearch(@NonNull Activity activity) { - ActivityCompat.startActivity(activity, new Intent(activity, SearchActivity.class), - null); - } - - public static void goToSearch(@NonNull Activity activity, boolean isMicOpen, - @NonNull ActivityOptions activityOptions) { - ActivityCompat.startActivity(activity, new Intent(activity, SearchActivity.class) - .putExtra(SearchActivity.EXTRA_SHOW_MIC, isMicOpen), - activityOptions.toBundle()); - } - - public static void goToSettings(@NonNull Activity activity) { - ActivityCompat.startActivity(activity, new Intent(activity, SettingsActivity.class), null); - } - public static void goToUserInfo(@NonNull Activity activity, @NonNull ActivityOptions activityOptions) { ActivityCompat.startActivity(activity, new Intent(activity, UserInfoActivity.class), diff --git a/app/src/main/java/io/github/muntashirakon/music/util/PlaylistsUtil.java b/app/src/main/java/io/github/muntashirakon/music/util/PlaylistsUtil.java index e27f99797..76f14ec99 100644 --- a/app/src/main/java/io/github/muntashirakon/music/util/PlaylistsUtil.java +++ b/app/src/main/java/io/github/muntashirakon/music/util/PlaylistsUtil.java @@ -87,7 +87,7 @@ public class PlaylistsUtil { final StringBuilder selection = new StringBuilder(); selection.append(MediaStore.Audio.Playlists._ID + " IN ("); for (int i = 0; i < playlists.size(); i++) { - selection.append(playlists.get(i).id); + selection.append(playlists.get(i).getId()); if (i < playlists.size() - 1) { selection.append(","); } @@ -195,7 +195,7 @@ public class PlaylistsUtil { final int playlistId = songs.get(0).getPlaylistId(); Uri uri = MediaStore.Audio.Playlists.Members.getContentUri( "external", playlistId); - String selectionArgs[] = new String[songs.size()]; + String[] selectionArgs = new String[songs.size()]; for (int i = 0; i < selectionArgs.length; i++) { selectionArgs[i] = String.valueOf(songs.get(i).getIdInPlayList()); } diff --git a/app/src/main/res/drawable/ic_car.xml b/app/src/main/res/drawable/ic_car.xml new file mode 100644 index 000000000..c928b80b0 --- /dev/null +++ b/app/src/main/res/drawable/ic_car.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/drawable/ic_face.xml b/app/src/main/res/drawable/ic_face.xml new file mode 100644 index 000000000..602d3dc1d --- /dev/null +++ b/app/src/main/res/drawable/ic_face.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/drawable/ic_layout.xml b/app/src/main/res/drawable/ic_layout.xml new file mode 100644 index 000000000..731165200 --- /dev/null +++ b/app/src/main/res/drawable/ic_layout.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/drawable/popup_background.xml b/app/src/main/res/drawable/popup_background.xml new file mode 100644 index 000000000..4a4471df5 --- /dev/null +++ b/app/src/main/res/drawable/popup_background.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/font/pacifico.xml b/app/src/main/res/font/pacifico.xml new file mode 100644 index 000000000..40b5fa206 --- /dev/null +++ b/app/src/main/res/font/pacifico.xml @@ -0,0 +1,6 @@ + + diff --git a/app/src/main/res/layout-land/activity_album.xml b/app/src/main/res/layout-land/fragment_album_details.xml similarity index 98% rename from app/src/main/res/layout-land/activity_album.xml rename to app/src/main/res/layout-land/fragment_album_details.xml index 8eda0acad..aa2457c36 100644 --- a/app/src/main/res/layout-land/activity_album.xml +++ b/app/src/main/res/layout-land/fragment_album_details.xml @@ -133,9 +133,8 @@ - + - diff --git a/app/src/main/res/layout-land/activity_artist_details.xml b/app/src/main/res/layout-land/fragment_artist_details.xml similarity index 98% rename from app/src/main/res/layout-land/activity_artist_details.xml rename to app/src/main/res/layout-land/fragment_artist_details.xml index 1fa416eef..af65ed84e 100644 --- a/app/src/main/res/layout-land/activity_artist_details.xml +++ b/app/src/main/res/layout-land/fragment_artist_details.xml @@ -112,7 +112,7 @@ - + diff --git a/app/src/main/res/layout-xlarge/activity_about_content.xml b/app/src/main/res/layout-xlarge/fragment_about_content.xml similarity index 100% rename from app/src/main/res/layout-xlarge/activity_about_content.xml rename to app/src/main/res/layout-xlarge/fragment_about_content.xml diff --git a/app/src/main/res/layout/activity_album.xml b/app/src/main/res/layout/activity_album.xml deleted file mode 100755 index 0130e3b90..000000000 --- a/app/src/main/res/layout/activity_album.xml +++ /dev/null @@ -1,141 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/layout/activity_artist_details.xml b/app/src/main/res/layout/activity_artist_details.xml deleted file mode 100755 index 34e9ba33e..000000000 --- a/app/src/main/res/layout/activity_artist_details.xml +++ /dev/null @@ -1,126 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/layout/activity_drive_mode.xml b/app/src/main/res/layout/activity_drive_mode.xml index b6157dd27..f477cf9fc 100644 --- a/app/src/main/res/layout/activity_drive_mode.xml +++ b/app/src/main/res/layout/activity_drive_mode.xml @@ -50,7 +50,6 @@ app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" /> - + + @@ -161,10 +173,10 @@ android:id="@+id/songText" android:layout_width="match_parent" android:layout_height="wrap_content" - android:layout_marginTop="4dp" + android:layout_marginTop="8dp" android:gravity="center" android:maxLines="3" - android:textAppearance="@style/TextViewHeadline6" + android:textAppearance="@style/TextViewHeadline5" android:textColor="@color/md_grey_200" tools:text="@tools:sample/lorem/random" /> diff --git a/app/src/main/res/layout/activity_lyrics.xml b/app/src/main/res/layout/activity_lyrics.xml index b8fc48785..496a0a961 100644 --- a/app/src/main/res/layout/activity_lyrics.xml +++ b/app/src/main/res/layout/activity_lyrics.xml @@ -6,10 +6,30 @@ android:layout_height="match_parent" android:transitionName="@string/transition_lyrics"> - + + + android:layout_gravity="bottom" + android:padding="0dp" + app:contentInsetLeft="0dp" + app:contentInsetStart="0dp" + app:contentInsetStartWithNavigation="0dp" + app:hideOnScroll="true" + app:titleMargin="0dp" + app:titleMarginStart="0dp" + tools:backgroundTint="@color/md_red_400"> - - - - + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_main_content.xml b/app/src/main/res/layout/activity_main_content.xml index 604812182..aee8d3b0d 100644 --- a/app/src/main/res/layout/activity_main_content.xml +++ b/app/src/main/res/layout/activity_main_content.xml @@ -17,45 +17,12 @@ - - - - - - - - - - - - - - - \ No newline at end of file + android:layout_height="match_parent" + app:defaultNavHost="true" + app:layout_behavior="com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior" + app:navGraph="@navigation/main_graph" /> + diff --git a/app/src/main/res/layout/activity_playlist_detail.xml b/app/src/main/res/layout/activity_playlist_detail.xml deleted file mode 100644 index af405033a..000000000 --- a/app/src/main/res/layout/activity_playlist_detail.xml +++ /dev/null @@ -1,87 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/layout/activity_search.xml b/app/src/main/res/layout/activity_search.xml deleted file mode 100755 index 595228ec8..000000000 --- a/app/src/main/res/layout/activity_search.xml +++ /dev/null @@ -1,128 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/layout/activity_settings.xml b/app/src/main/res/layout/activity_settings.xml old mode 100755 new mode 100644 index 9ca6a9cb8..925ee2259 --- a/app/src/main/res/layout/activity_settings.xml +++ b/app/src/main/res/layout/activity_settings.xml @@ -48,5 +48,4 @@ - - + \ No newline at end of file diff --git a/app/src/main/res/layout/bread_crumb.xml b/app/src/main/res/layout/bread_crumb.xml index 345a4ed7f..e22bfae2e 100644 --- a/app/src/main/res/layout/bread_crumb.xml +++ b/app/src/main/res/layout/bread_crumb.xml @@ -15,7 +15,8 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:paddingEnd="8dp" - android:textAllCaps="true" + android:textAllCaps="false" + android:textAppearance="@style/TextViewNormal" android:textColor="#fff" tools:ignore="RtlSymmetry,UnusedAttribute" tools:text="Storage" diff --git a/app/src/main/res/layout/fragment_about.xml b/app/src/main/res/layout/fragment_about.xml new file mode 100644 index 000000000..d64fb15ad --- /dev/null +++ b/app/src/main/res/layout/fragment_about.xml @@ -0,0 +1,11 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_about_content.xml b/app/src/main/res/layout/fragment_about_content.xml similarity index 100% rename from app/src/main/res/layout/activity_about_content.xml rename to app/src/main/res/layout/fragment_about_content.xml diff --git a/app/src/main/res/layout/activity_album_content.xml b/app/src/main/res/layout/fragment_album_content.xml similarity index 100% rename from app/src/main/res/layout/activity_album_content.xml rename to app/src/main/res/layout/fragment_album_content.xml diff --git a/app/src/main/res/layout/fragment_album_details.xml b/app/src/main/res/layout/fragment_album_details.xml index 9d3fd3be8..2453547b9 100644 --- a/app/src/main/res/layout/fragment_album_details.xml +++ b/app/src/main/res/layout/fragment_album_details.xml @@ -5,6 +5,30 @@ android:layout_width="match_parent" android:layout_height="match_parent"> + + + + + + + + + + + - diff --git a/app/src/main/res/layout/activity_artist_content.xml b/app/src/main/res/layout/fragment_artist_content.xml similarity index 100% rename from app/src/main/res/layout/activity_artist_content.xml rename to app/src/main/res/layout/fragment_artist_content.xml diff --git a/app/src/main/res/layout/fragment_artist_details.xml b/app/src/main/res/layout/fragment_artist_details.xml new file mode 100644 index 000000000..b434bf47f --- /dev/null +++ b/app/src/main/res/layout/fragment_artist_details.xml @@ -0,0 +1,110 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_blur.xml b/app/src/main/res/layout/fragment_blur.xml index 44ced6e44..827cc3809 100644 --- a/app/src/main/res/layout/fragment_blur.xml +++ b/app/src/main/res/layout/fragment_blur.xml @@ -42,17 +42,17 @@ - + - - + - + - - + @@ -37,7 +37,7 @@ android:layout_height="wrap_content" android:layout_weight="0"> - - + - - + - - + - + - - + + android:layout_height="match_parent"> - - - - - - - - - - - - - - - - - - - - + app:liftOnScroll="true"> + android:layout_height="wrap_content" + app:layout_scrollFlags="scroll|enterAlways"> - - + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_main.xml b/app/src/main/res/layout/fragment_main.xml index 5c6b26f06..9e45c2c96 100644 --- a/app/src/main/res/layout/fragment_main.xml +++ b/app/src/main/res/layout/fragment_main.xml @@ -58,6 +58,6 @@ android:layout_height="match_parent" app:defaultNavHost="true" app:layout_behavior="com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior" - app:navGraph="@navigation/retro_graph" /> + app:navGraph="@navigation/main_graph" /> \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_material.xml b/app/src/main/res/layout/fragment_material.xml index a1eb49bf8..b57b39b81 100644 --- a/app/src/main/res/layout/fragment_material.xml +++ b/app/src/main/res/layout/fragment_material.xml @@ -22,7 +22,7 @@ - - - + - - + - - - - + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_search.xml b/app/src/main/res/layout/fragment_search.xml new file mode 100644 index 000000000..7d07f4318 --- /dev/null +++ b/app/src/main/res/layout/fragment_search.xml @@ -0,0 +1,105 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_about.xml b/app/src/main/res/layout/fragment_settings.xml similarity index 75% rename from app/src/main/res/layout/activity_about.xml rename to app/src/main/res/layout/fragment_settings.xml index 6ef4de68f..620e47fbc 100644 --- a/app/src/main/res/layout/activity_about.xml +++ b/app/src/main/res/layout/fragment_settings.xml @@ -10,7 +10,6 @@ android:id="@+id/status_bar" android:layout_width="match_parent" android:layout_height="0dp" - android:background="?attr/colorSurface" tools:ignore="UnusedAttribute" /> + + + + + + app:titleTextAppearance="@style/ToolbarTextAppearanceNormal" + tools:title="@string/action_settings" /> - - - - - - \ No newline at end of file + + diff --git a/app/src/main/res/layout/fragment_simple_player.xml b/app/src/main/res/layout/fragment_simple_player.xml index eb614e214..04d2f092a 100644 --- a/app/src/main/res/layout/fragment_simple_player.xml +++ b/app/src/main/res/layout/fragment_simple_player.xml @@ -25,17 +25,17 @@ - + - - + - @@ -24,21 +25,23 @@ android:scaleType="centerCrop" app:srcCompat="@drawable/default_album_art" tools:src="@tools:sample/avatars" /> + + - - - \ No newline at end of file diff --git a/app/src/main/res/layout/item_card.xml b/app/src/main/res/layout/item_card.xml index 8ce8c74e7..86037eefe 100644 --- a/app/src/main/res/layout/item_card.xml +++ b/app/src/main/res/layout/item_card.xml @@ -4,6 +4,7 @@ xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="wrap_content" + android:layout_margin="1dp" android:background="?attr/rectSelector" android:clickable="true" android:focusable="true"> @@ -11,7 +12,7 @@ diff --git a/app/src/main/res/layout/item_card_color.xml b/app/src/main/res/layout/item_card_color.xml index 14f009703..438bfde9e 100644 --- a/app/src/main/res/layout/item_card_color.xml +++ b/app/src/main/res/layout/item_card_color.xml @@ -4,15 +4,15 @@ xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="wrap_content" - android:layout_margin="4dp" + android:layout_margin="1dp" android:background="?attr/rectSelector" android:clickable="true" - android:focusable="true" - tools:ignore="MissingPrefix"> + android:focusable="true"> @@ -33,7 +33,6 @@ tools:ignore="ContentDescription" tools:src="@tools:sample/avatars" /> - \ No newline at end of file diff --git a/app/src/main/res/layout/item_image.xml b/app/src/main/res/layout/item_image.xml index 5ec37319b..5753a7b8f 100644 --- a/app/src/main/res/layout/item_image.xml +++ b/app/src/main/res/layout/item_image.xml @@ -35,7 +35,9 @@ android:ellipsize="end" android:maxLines="1" android:padding="8dp" - android:textAppearance="@style/TextViewNormal" + android:textAppearance="@style/TextViewBody1" + android:textColor="?android:attr/textColorPrimary" + android:textStyle="bold" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@id/imageContainer" diff --git a/app/src/main/res/layout/item_list_quick_actions.xml b/app/src/main/res/layout/item_list_quick_actions.xml index 3c601d3d9..c73c49f23 100644 --- a/app/src/main/res/layout/item_list_quick_actions.xml +++ b/app/src/main/res/layout/item_list_quick_actions.xml @@ -16,7 +16,7 @@ xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="wrap_content" - android:gravity="center_vertical" + android:gravity="center_vertical|end" android:orientation="horizontal"> @@ -38,7 +39,35 @@ android:layout_marginEnd="16dp" android:layout_weight="1" android:text="@string/shuffle" + android:visibility="gone" app:backgroundTint="?attr/colorSurface" app:icon="@drawable/ic_shuffle" /> + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/item_suggestions.xml b/app/src/main/res/layout/item_suggestions.xml index b3f759262..70f5a24c3 100644 --- a/app/src/main/res/layout/item_suggestions.xml +++ b/app/src/main/res/layout/item_suggestions.xml @@ -10,8 +10,7 @@ android:id="@+id/title" android:layout_width="0dp" android:layout_height="wrap_content" - android:paddingHorizontal="16dp" - android:paddingVertical="14dp" + android:padding="16dp" android:text="@string/suggestion_songs" android:textAppearance="@style/TextViewHeadline6" android:textStyle="bold" @@ -20,6 +19,7 @@ app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" /> + + android:textAppearance="@style/TextViewNormal" + android:textStyle="bold" + app:autoSizeMaxTextSize="32sp" + app:autoSizeMinTextSize="18sp" + app:autoSizeStepGranularity="1sp" /> + app:layout_constraintTop_toTopOf="@id/card6"> + app:layout_constraintTop_toTopOf="@id/card7"> + android:orientation="vertical" + android:paddingBottom="12dp"> - - + app:layout_constraintTop_toTopOf="parent"> + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/sliding_music_panel_layout.xml b/app/src/main/res/layout/sliding_music_panel_layout.xml index ca68ddd9b..32e6005b1 100644 --- a/app/src/main/res/layout/sliding_music_panel_layout.xml +++ b/app/src/main/res/layout/sliding_music_panel_layout.xml @@ -24,17 +24,17 @@ android:layout_width="match_parent" android:layout_height="match_parent" android:background="?attr/colorSurface" - app:behavior_hideable="false" + app:behavior_hideable="true" app:behavior_peekHeight="0dp" app:layout_behavior="io.github.muntashirakon.music.RetroBottomSheetBehavior"> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + android:id="@+id/action_settings" + android:icon="@drawable/ic_settings" + android:title="@string/action_settings" + app:showAsAction="ifRoom" /> \ No newline at end of file diff --git a/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml index eca70cfe5..c9ad5f98f 100644 --- a/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml +++ b/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml @@ -1,5 +1,5 @@ - - + + \ No newline at end of file diff --git a/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml index eca70cfe5..c9ad5f98f 100644 --- a/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml +++ b/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml @@ -1,5 +1,5 @@ - - + + \ No newline at end of file diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher.png b/app/src/main/res/mipmap-hdpi/ic_launcher.png index 2cbb88429..ef3cfc0aa 100644 Binary files a/app/src/main/res/mipmap-hdpi/ic_launcher.png and b/app/src/main/res/mipmap-hdpi/ic_launcher.png differ diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png b/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png index 912c27f24..ba6874195 100644 Binary files a/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png and b/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png differ diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher_round.png b/app/src/main/res/mipmap-hdpi/ic_launcher_round.png index 2cbb88429..6c46ac4cb 100644 Binary files a/app/src/main/res/mipmap-hdpi/ic_launcher_round.png and b/app/src/main/res/mipmap-hdpi/ic_launcher_round.png differ diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher.png b/app/src/main/res/mipmap-mdpi/ic_launcher.png index 7f7b6d052..4e45638f7 100644 Binary files a/app/src/main/res/mipmap-mdpi/ic_launcher.png and b/app/src/main/res/mipmap-mdpi/ic_launcher.png differ diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png b/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png index 129090fc2..5fe585e36 100644 Binary files a/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png and b/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png differ diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher_round.png b/app/src/main/res/mipmap-mdpi/ic_launcher_round.png index 7f7b6d052..00668b42b 100644 Binary files a/app/src/main/res/mipmap-mdpi/ic_launcher_round.png and b/app/src/main/res/mipmap-mdpi/ic_launcher_round.png differ diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/app/src/main/res/mipmap-xhdpi/ic_launcher.png index 6e0f844e5..23394228c 100644 Binary files a/app/src/main/res/mipmap-xhdpi/ic_launcher.png and b/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png b/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png index c18aa6c84..bcaf1a42e 100644 Binary files a/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png and b/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png differ diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png index 6e0f844e5..c998e2065 100644 Binary files a/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png and b/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png differ diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/app/src/main/res/mipmap-xxhdpi/ic_launcher.png index 5b423cb79..9142374eb 100644 Binary files a/app/src/main/res/mipmap-xxhdpi/ic_launcher.png and b/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png b/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png index 4678e7020..451e815e3 100644 Binary files a/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png and b/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png differ diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png index 5b423cb79..663d2450f 100644 Binary files a/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png and b/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png differ diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png index 544a56bfa..e78b0afd6 100644 Binary files a/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png and b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png index 02a7dc5e9..3f076e2d0 100644 Binary files a/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png and b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png differ diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png index 544a56bfa..8b5d5d8dd 100644 Binary files a/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png and b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png differ diff --git a/app/src/main/res/navigation/retro_graph.xml b/app/src/main/res/navigation/library_graph.xml similarity index 64% rename from app/src/main/res/navigation/retro_graph.xml rename to app/src/main/res/navigation/library_graph.xml index 21f6cbbc8..13aa65539 100644 --- a/app/src/main/res/navigation/retro_graph.xml +++ b/app/src/main/res/navigation/library_graph.xml @@ -1,51 +1,49 @@ + xmlns:tools="http://schemas.android.com/tools" + android:id="@+id/library_graph" + app:startDestination="@id/action_home"> + tools:layout="@layout/fragment_main_activity_recycler_view" /> + tools:layout="@layout/fragment_main_activity_recycler_view" /> + tools:layout="@layout/fragment_main_activity_recycler_view" /> + tools:layout="@layout/fragment_main_activity_recycler_view" /> + tools:layout="@layout/fragment_main_activity_recycler_view" /> + tools:layout="@layout/fragment_main_activity_recycler_view" /> + tools:layout="@layout/fragment_folder" /> + android:name="io.github.muntashirakon.music.fragments.home.HomeFragment" + android:label="" + tools:layout="@layout/fragment_banner_home" /> - \ No newline at end of file diff --git a/app/src/main/res/navigation/main_graph.xml b/app/src/main/res/navigation/main_graph.xml new file mode 100644 index 000000000..4fd9cdb97 --- /dev/null +++ b/app/src/main/res/navigation/main_graph.xml @@ -0,0 +1,86 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/navigation/settings_graph.xml b/app/src/main/res/navigation/settings_graph.xml index 68599daae..9589ac642 100644 --- a/app/src/main/res/navigation/settings_graph.xml +++ b/app/src/main/res/navigation/settings_graph.xml @@ -18,6 +18,7 @@ app:exitAnim="@anim/retro_fragment_open_exit" app:popEnterAnim="@anim/retro_fragment_close_enter" app:popExitAnim="@anim/retro_fragment_close_exit" /> + + + + + + app:popExitAnim="@anim/retro_fragment_close_exit" /> + + app:destination="@id/aboutActivity" + app:enterAnim="@anim/retro_fragment_open_enter" + app:exitAnim="@anim/retro_fragment_open_exit" + app:launchSingleTop="true" + app:popEnterAnim="@anim/retro_fragment_close_enter" + app:popExitAnim="@anim/retro_fragment_close_exit" /> - + android:name="io.github.muntashirakon.music.fragments.about.AboutFragment" + android:label="About" /> \ No newline at end of file diff --git a/app/src/main/res/transition/change_bounds.xml b/app/src/main/res/transition/change_bounds.xml index 8595f5a97..3d86880d7 100644 --- a/app/src/main/res/transition/change_bounds.xml +++ b/app/src/main/res/transition/change_bounds.xml @@ -1,3 +1,9 @@ - \ No newline at end of file + + + + + \ No newline at end of file diff --git a/app/src/main/res/transition/grid_exit_transition.xml b/app/src/main/res/transition/grid_exit_transition.xml new file mode 100644 index 000000000..6ff14dad3 --- /dev/null +++ b/app/src/main/res/transition/grid_exit_transition.xml @@ -0,0 +1,27 @@ + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/values-night-v27/styles.xml b/app/src/main/res/values-night-v27/styles.xml index 701328992..87c77add9 100644 --- a/app/src/main/res/values-night-v27/styles.xml +++ b/app/src/main/res/values-night-v27/styles.xml @@ -15,11 +15,9 @@ \ No newline at end of file diff --git a/app/src/main/res/values-night/styles.xml b/app/src/main/res/values-night/styles.xml index 5313087d4..99e61bb24 100644 --- a/app/src/main/res/values-night/styles.xml +++ b/app/src/main/res/values-night/styles.xml @@ -15,11 +15,9 @@ diff --git a/app/src/main/res/values-v23/styles.xml b/app/src/main/res/values-v23/styles.xml index dee5c9fdd..c70057acf 100644 --- a/app/src/main/res/values-v23/styles.xml +++ b/app/src/main/res/values-v23/styles.xml @@ -21,7 +21,6 @@ \ No newline at end of file diff --git a/app/src/main/res/values-v27/styles_parents.xml b/app/src/main/res/values-v27/styles_parents.xml index ca42d0971..d346f69c9 100644 --- a/app/src/main/res/values-v27/styles_parents.xml +++ b/app/src/main/res/values-v27/styles_parents.xml @@ -15,20 +15,18 @@ + + + + + + + + + + + diff --git a/app/src/main/res/values/styles_parents.xml b/app/src/main/res/values/styles_parents.xml index e036682d4..ef8e30992 100644 --- a/app/src/main/res/values/styles_parents.xml +++ b/app/src/main/res/values/styles_parents.xml @@ -1,5 +1,5 @@ - + @@ -87,19 +88,4 @@ wrap_content - - - - \ No newline at end of file