Compare commits
21 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| e09169b111 | |||
| 4ee1822057 | |||
|
|
a179db6323 | ||
|
|
1beeab28a6 | ||
|
|
ad6a569961 | ||
|
|
0f5a8f6b97 | ||
|
|
3cd1bdf229 | ||
|
|
d7389db265 | ||
|
|
b3c93b3885 | ||
|
|
25864accc9 | ||
|
|
f734ced2cb | ||
|
|
4a3d2305c0 | ||
|
|
8db6797eaa | ||
|
|
cb4c19757d | ||
|
|
b6e75afe12 | ||
|
|
b621be06df | ||
|
|
7a17e91690 | ||
|
|
1036829186 | ||
|
|
becfc1d589 | ||
|
|
44bf346332 | ||
|
|
896e5fb3bd |
31
CHANGELOG.md
@@ -1,5 +1,36 @@
|
||||
# Changelog
|
||||
|
||||
## What's Changed
|
||||
## [4.13.0](https://github.com/eddyizm/tempo/releases/tag/v4.13.0) (2026-03-25)
|
||||
* chore(i18n): Improve Russian translation by @NikkoFox in https://github.com/eddyizm/tempus/pull/503
|
||||
* feat: tile size manager by @MaFo-28 in https://github.com/eddyizm/tempus/pull/440
|
||||
* chore(i18n): Translated to zh_TW by @olivertzeng in https://github.com/eddyizm/tempus/pull/494
|
||||
* fix: Show full album name when displaying details by @jaime-grj in https://github.com/eddyizm/tempus/pull/508
|
||||
* chore(i18n): Update Spanish translation by @jaime-grj in https://github.com/eddyizm/tempus/pull/509
|
||||
* fix: Relocate "Offline mode" text by @jaime-grj in https://github.com/eddyizm/tempus/pull/510
|
||||
* chore(i18n): Update Polish translation by @skajmer in https://github.com/eddyizm/tempus/pull/516
|
||||
* refactor: navigation and bottom sheet by @tvillega in https://github.com/eddyizm/tempus/pull/491
|
||||
* feat: Logo refresh by @eddyizm in https://github.com/eddyizm/tempus/pull/498
|
||||
* feat: Add 'genres' page/function to Android Auto by @Jorilx in https://github.com/eddyizm/tempus/pull/505
|
||||
* feat: Added all-songs feature by @unknown0816 in https://github.com/eddyizm/tempus/pull/517
|
||||
|
||||
## New Contributors
|
||||
* @NikkoFox made their first contribution in https://github.com/eddyizm/tempus/pull/503
|
||||
* @olivertzeng made their first contribution in https://github.com/eddyizm/tempus/pull/494
|
||||
* @Jorilx made their first contribution in https://github.com/eddyizm/tempus/pull/505
|
||||
* @unknown0816 made their first contribution in https://github.com/eddyizm/tempus/pull/517
|
||||
|
||||
**Full Changelog**: https://github.com/eddyizm/tempus/compare/v4.12.6...v4.13.0
|
||||
|
||||
## What's Changed
|
||||
## [4.12.6](https://github.com/eddyizm/tempo/releases/tag/v4.12.6) (2026-03-06)
|
||||
* doc: update USAGE with android auto configuration by @MaFo-28 in https://github.com/eddyizm/tempus/pull/481
|
||||
* chore(i18n): Update Polish translation by @skajmer in https://github.com/eddyizm/tempus/pull/483
|
||||
* fix: remove material you dynamic theming by @tvillega in https://github.com/eddyizm/tempus/pull/484
|
||||
* fix: collapse sheet on navitation change by @tvillega in https://github.com/eddyizm/tempus/pull/482
|
||||
|
||||
**Full Changelog**: https://github.com/eddyizm/tempus/compare/v4.12.4...v4.12.5
|
||||
|
||||
## What's Changed
|
||||
## [4.12.4](https://github.com/eddyizm/tempo/releases/tag/v4.12.4) (2026-03-01)
|
||||
* feat: advertise existing long press to refresh per section on library page by @tvillega in https://github.com/eddyizm/tempus/pull/467
|
||||
|
||||
36
README.md
@@ -1,5 +1,5 @@
|
||||
<p align="center">
|
||||
<img alt="Tempus" title="Tempus" src="mockup/svg/tempus_horizontal_logo.png" width="250">
|
||||
<img alt="Tempor" title="Tempor" src="mockup/svg/tempus-horizontal-banner.png" width="250">
|
||||
</p>
|
||||
|
||||
---
|
||||
@@ -31,9 +31,9 @@
|
||||
-->
|
||||
|
||||
|
||||
**Tempus** is an open-source and lightweight music client for Subsonic, designed and built natively for Android. It provides a seamless and intuitive music streaming experience, allowing you to access and play your Subsonic music library directly from your Android device.
|
||||
**Tempor** is an open-source and lightweight music client for Subsonic, designed and built natively for Android. It provides a seamless and intuitive music streaming experience, allowing you to access and play your Subsonic music library directly from your Android device.
|
||||
|
||||
Tempus does not rely on magic algorithms to decide what you should listen to. Instead, the interface is built around your listening history, randomness, and optionally integrates with services like Listenbrainz.org and Last.fm to personalize your music experience (These must be supported by your backend).
|
||||
Tempor does not rely on magic algorithms to decide what you should listen to. Instead, the interface is built around your listening history, randomness, and optionally integrates with services like Listenbrainz.org and Last.fm to personalize your music experience (These must be supported by your backend).
|
||||
|
||||
The project is a fork of [Tempo](#credits).
|
||||
|
||||
@@ -41,7 +41,7 @@ The project is a fork of [Tempo](#credits).
|
||||
[Wiki](USAGE.md)
|
||||
[Donate](https://github.com/eddyizm/tempus#donate)
|
||||
|
||||
**If you find Tempus useful, please consider starring the project on GitHub. It would mean a lot to me and help promote the app to a wider audience.**
|
||||
**If you find Tempor useful, please consider starring the project on GitHub. It would mean a lot to me and help promote the app to a wider audience.**
|
||||
|
||||
**Use the Github version of the app for full Android Auto and Chromecast support.**
|
||||
|
||||
@@ -58,19 +58,19 @@ Please note the two variants in the release assets include release/debug and 32/
|
||||
|
||||
|
||||
## Features
|
||||
- **Subsonic Integration**: Tempus seamlessly integrates with your Subsonic server, providing you with easy access to your entire music collection on the go.
|
||||
- **Subsonic Integration**: Tempor seamlessly integrates with your Subsonic server, providing you with easy access to your entire music collection on the go.
|
||||
- **Sleek and Intuitive UI**: Enjoy a clean and user-friendly interface designed to enhance your music listening experience, tailored to your preferences and listening history.
|
||||
- **Browse and Search**: Easily navigate through your music library using various browsing and searching options, including artists, albums, genres, playlists, decades and more.
|
||||
- **Streaming and Offline Mode**: Stream music directly from your Subsonic server. Offline mode is currently under active development and may have limitations when using multiple servers.
|
||||
- **Playlist Management**: Create, edit, and manage playlists to curate your perfect music collection.
|
||||
- **Gapless Playback**: Experience uninterrupted playback with gapless listening mode.
|
||||
- **Chromecast Support**: Stream your music to Chromecast devices. The support is currently in a rudimentary state.*
|
||||
- **Scrobbling Integration**: Optionally integrate Tempus with Last.fm or Listenbrainz.org to scrobble your played tracks, gather music insights, and further personalize your music recommendations, if supported by your Subsonic server.
|
||||
- **Podcasts and Radio**: If your Subsonic server supports it, listen to podcasts and radio shows directly within Tempus, expanding your audio entertainment options.
|
||||
- **Scrobbling Integration**: Optionally integrate Tempor with Last.fm or Listenbrainz.org to scrobble your played tracks, gather music insights, and further personalize your music recommendations, if supported by your Subsonic server.
|
||||
- **Podcasts and Radio**: If your Subsonic server supports it, listen to podcasts and radio shows directly within Tempor, expanding your audio entertainment options.
|
||||
- **Instant Mix**: Full refactor of instant mix function which leverages subsonics similarSongs2 by artist/album and similarSongs endpoints to server a larger play queue more reliably.
|
||||
- **Transcoding Support**: Activate transcoding of tracks on your Subsonic server, allowing you to set a transcoding profile for optimized streaming directly from the app. This feature requires support from your Subsonic server.
|
||||
- **Android Auto Support**: Enjoy your favorite music on the go with full Android Auto integration, allowing you to seamlessly control and listen to your tracks directly from your mobile device while driving.*
|
||||
- **Multiple Libraries**: Tempus handles multi-library setups gracefully. They are displayed as Library folders.
|
||||
- **Multiple Libraries**: Tempor handles multi-library setups gracefully. They are displayed as Library folders.
|
||||
- **Equalizer**: Option to use in app equalizer.
|
||||
- **Widget**: New widget to keeping the basic controls on your screen at all times.
|
||||
- **Available in 11 languages**: Currently in Chinese, French, German, Italian, Korean, Polish, Portuguese, Russion, Spanish and Turkish
|
||||
@@ -84,13 +84,13 @@ Please note the two variants in the release assets include release/debug and 32/
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
<img src="fastlane/metadata/android/en-US/images/phoneScreenshots/1_light.png" width=200>
|
||||
<img src="fastlane/metadata/android/en-US/images/phoneScreenshots/2_light.png" width=200>
|
||||
<img src="fastlane/metadata/android/en-US/images/phoneScreenshots/3_light.png" width=200>
|
||||
<img src="mockup/1_light_tempus.png" width=200>
|
||||
<img src="mockup/2_light_tempus.png" width=200>
|
||||
<img src="mockup/3_light_tempus.png" width=200>
|
||||
<img src="fastlane/metadata/android/en-US/images/phoneScreenshots/4_light.png" width=200>
|
||||
<img src="fastlane/metadata/android/en-US/images/phoneScreenshots/5_light.png" width=200>
|
||||
<img src="fastlane/metadata/android/en-US/images/phoneScreenshots/6_light.png" width=200>
|
||||
<img src="fastlane/metadata/android/en-US/images/phoneScreenshots/8_light.png" width=200>
|
||||
<!-- <img src="fastlane/metadata/android/en-US/images/phoneScreenshots/8_light.png" width=200> -->
|
||||
</p>
|
||||
|
||||
<br>
|
||||
@@ -100,13 +100,13 @@ Please note the two variants in the release assets include release/debug and 32/
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
<img src="fastlane/metadata/android/en-US/images/phoneScreenshots/1_dark.png" width=200>
|
||||
<img src="fastlane/metadata/android/en-US/images/phoneScreenshots/2_dark.png" width=200>
|
||||
<img src="fastlane/metadata/android/en-US/images/phoneScreenshots/3_dark.png" width=200>
|
||||
<img src="mockup/1_dark_tempus.png" width=200>
|
||||
<img src="mockup/2_dark_tempus.png" width=200>
|
||||
<img src="mockup/3_dark_tempus.png" width=200>
|
||||
<img src="fastlane/metadata/android/en-US/images/phoneScreenshots/4_dark.png" width=200>
|
||||
<img src="fastlane/metadata/android/en-US/images/phoneScreenshots/5_dark.png" width=200>
|
||||
<img src="fastlane/metadata/android/en-US/images/phoneScreenshots/6_dark.png" width=200>
|
||||
<img src="fastlane/metadata/android/en-US/images/phoneScreenshots/8_dark.png" width=200>
|
||||
<!-- <img src="fastlane/metadata/android/en-US/images/phoneScreenshots/8_dark.png" width=200> -->
|
||||
|
||||
</p>
|
||||
|
||||
@@ -130,10 +130,10 @@ bitcoin: `3QVHSSCJvn6yXEcJ3A3cxYLMmbvFsrnUs5`
|
||||
|
||||
## License
|
||||
|
||||
Tempus is released under the [GNU General Public License v3.0](LICENSE). Feel free to modify, distribute, and use the app in accordance with the terms of the license. Contributions to the project are also welcome.
|
||||
Tempor is released under the [GNU General Public License v3.0](LICENSE). Feel free to modify, distribute, and use the app in accordance with the terms of the license. Contributions to the project are also welcome.
|
||||
|
||||
|
||||
## Credits
|
||||
Thanks to the original repo/creator [CappielloAntonio](https://github.com/CappielloAntonio) (forked from v3.9.0)
|
||||
|
||||
[Opensvg.org](https://opensvg.org) for the new turntable logo.
|
||||
[SeattleGuy](https://github.com/SeattleGuy) for the new logo design.
|
||||
|
||||
70
USAGE.md
@@ -1,4 +1,4 @@
|
||||
# Tempus Usage Guide
|
||||
# Tempor Usage Guide
|
||||
[<- back home](README.md)
|
||||
|
||||
## Table of Contents
|
||||
@@ -62,15 +62,15 @@ This app works with any service that implements the Subsonic API, including:
|
||||
|
||||
**Multi-library**
|
||||
|
||||
Tempus handles multi-library setups gracefully. They are displayed as Library folders.
|
||||
Tempor handles multi-library setups gracefully. They are displayed as Library folders.
|
||||
|
||||
However, if you want to limit or change libraries you could use a workaround, if your server supports it.
|
||||
|
||||
You can create multiple users , one for each library, and save each of them in Tempus app.
|
||||
You can create multiple users , one for each library, and save each of them in Tempor.
|
||||
|
||||
### Folder or index playback
|
||||
|
||||
If your Subsonic-compatible server exposes the folder tree **or** provides an artist index (for example Gonic, Navidrome, or any backend with folder browsing enabled), Tempus lets you play an entire folder from anywhere in the library hierarchy:
|
||||
If your Subsonic-compatible server exposes the folder tree **or** provides an artist index (for example Gonic, Navidrome, or any backend with folder browsing enabled), Tempor lets you play an entire folder from anywhere in the library hierarchy:
|
||||
|
||||
<p align="left">
|
||||
<img src="mockup/usage/music_folders_root.png" width=317 style="margin-right:16px;">
|
||||
@@ -81,7 +81,7 @@ If your Subsonic-compatible server exposes the folder tree **or** provides an ar
|
||||
- When viewing **inner folders** **or artist index entries**, tap the new play button to immediately enqueue every audio track inside that folder/index and all nested subfolders.
|
||||
- Video files are excluded automatically, so only playable audio ends up in the queue.
|
||||
|
||||
No extra config is needed—Tempus adjusts based on the connected backend.
|
||||
No extra config is needed—Tempor adjusts based on the connected backend.
|
||||
|
||||
### Now Playing Screen
|
||||
|
||||
@@ -158,8 +158,9 @@ If your server supports it - add a internet radio station feed
|
||||
|
||||
## Android Auto
|
||||
|
||||
### Enabling on your head unit
|
||||
To allow the Tempus app on your car's head unit, "Unknown sources" needs to be enabled in the Android Auto "Developer settings". This is because Tempus isn't installed through Play Store. Note that the Android Auto developer settings are different from the global Android "Developer options".
|
||||
**Enabling on your head unit**
|
||||
|
||||
To allow the Tempor app on your car's head unit, "Unknown sources" needs to be enabled in the Android Auto "Developer settings". This is because Tempor isn't installed through Play Store. Note that the Android Auto developer settings are different from the global Android "Developer options".
|
||||
1. Switch to developer mode in the Android Auto settings by tapping ten times on the "Version" item at the bottom, followed by giving your permission.
|
||||
<p align="left">
|
||||
<img width="270" height="600" alt="1a" src="https://github.com/user-attachments/assets/f09f6999-9761-4b05-8ec7-bf221a15dda3" />
|
||||
@@ -177,6 +178,61 @@ To allow the Tempus app on your car's head unit, "Unknown sources" needs to be e
|
||||
<img width="270" height="600" alt="3" src="https://github.com/user-attachments/assets/37db88e9-1b76-417f-9c47-da9f3a750fff" />
|
||||
</p>
|
||||
|
||||
**Interface Configuration**
|
||||
|
||||
The Android Auto interface can be configured by user to best suit their preferences.
|
||||
|
||||
<p align="left">
|
||||
<img src="mockup/usage/aa_preferences.png" width=317 style="margin-right:16px;">
|
||||
<img src="mockup/usage/aa_functions.png" width=317>
|
||||
</p>
|
||||
|
||||
4 tabs can be configured with the following functions:
|
||||
- Do not display : This tab is not used
|
||||
- Home : Displays all functions not used in other tabs
|
||||
- Recent : The 15 most recently listened-to albums
|
||||
- Albums : Albums sorted by name
|
||||
- Artists : Albums sorted by artist
|
||||
- Playlists
|
||||
- Podcast : The 100 podcasts recently added
|
||||
- Radio
|
||||
- Folder : Navigation through music directories
|
||||
- Albums most played : The 15 most played albums
|
||||
- Albums added : The 15 recently added albums
|
||||
- Star tracks
|
||||
- Star albums
|
||||
- Star artists
|
||||
- Random : 100 random songs
|
||||
- Genres : 500 songs of the chosen genre OR 100 random songs if "shuffle genre songs" is selected
|
||||
|
||||
If all tabs are set to "Do not display", then "Home" tab will be created with all functions inside.
|
||||
|
||||
If "Home" is selected after another tab, it becomes "More"
|
||||
|
||||
In addition, you can choose to display the following functions as thumbnails or lists:
|
||||
- Home
|
||||
- Albums (Last played, Most played, Recently added, Artists, Star tracks, Star albums, Star artists, Random)
|
||||
- Playlists
|
||||
- Radio
|
||||
- Podcast
|
||||
|
||||
<p align="left">
|
||||
<img src="mockup/usage/aa_thumbnails.jpg" width=317 style="margin-right:16px;">
|
||||
<img src="mockup/usage/aa_list.jpg" width=317>
|
||||
</p>
|
||||
|
||||
The A-Z button allows you to jump to items starting with the chosen letter.
|
||||
|
||||
Search button returns albums or artists, even if they are not displayed by the selected function.
|
||||
|
||||
Results of the A-Z jump or search will always be displayed as a list.
|
||||
|
||||
<p align="left">
|
||||
<img src="mockup/usage/aa_AZ.jpg" width=317 style="margin-right:16px;">
|
||||
<img src="mockup/usage/aa_search.jpg" width=317>
|
||||
</p>
|
||||
|
||||
Display of albums and artists is limited to 500. For large libraries, it's preferable to use star albums or star artists.
|
||||
|
||||
### Server Settings
|
||||
**IN PROGRESS**
|
||||
|
||||
1
_config.yml
Normal file
@@ -0,0 +1 @@
|
||||
markdown: GFM
|
||||
@@ -10,8 +10,8 @@ android {
|
||||
minSdkVersion 24
|
||||
targetSdk 35
|
||||
|
||||
versionCode 22
|
||||
versionName '4.12.4'
|
||||
versionCode 24
|
||||
versionName '4.13.0'
|
||||
testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner'
|
||||
|
||||
javaCompileOptions {
|
||||
@@ -47,12 +47,12 @@ android {
|
||||
productFlavors {
|
||||
tempus {
|
||||
dimension = "default"
|
||||
applicationId 'com.eddyizm.tempus'
|
||||
applicationId 'ru.benya.tempor'
|
||||
}
|
||||
|
||||
degoogled {
|
||||
dimension = "default"
|
||||
applicationId "com.eddyizm.degoogled.tempus"
|
||||
applicationId "ru.benya.tempor.degoogled"
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
Before Width: | Height: | Size: 20 KiB |
10
app/src/degoogled/res/drawable/ic_launcher_background.xml
Normal file
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="108dp"
|
||||
android:height="108dp"
|
||||
android:viewportWidth="108"
|
||||
android:viewportHeight="108">
|
||||
<path
|
||||
android:fillColor="#FF36C12C"
|
||||
android:pathData="M0,0h108v108h-108z" />
|
||||
</vector>
|
||||
@@ -1,54 +1,78 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:aapt="http://schemas.android.com/aapt"
|
||||
android:width="108dp"
|
||||
android:height="108dp"
|
||||
android:viewportWidth="512"
|
||||
android:viewportHeight="512">
|
||||
<group android:scaleX="0.49"
|
||||
android:scaleY="0.49"
|
||||
android:translateX="130.56"
|
||||
android:translateY="130.56">
|
||||
android:viewportWidth="108"
|
||||
android:viewportHeight="108">
|
||||
|
||||
<path
|
||||
android:pathData="M512,437.33c0,11.78 -9.56,21.34 -21.34,21.34H21.33C9.55,458.67 0,449.11 0,437.33V96c0,-11.78 9.55,-21.33 21.33,-21.33h469.33c11.78,0 21.34,9.55 21.34,21.33L512,437.33L512,437.33z"
|
||||
android:fillColor="#8CC152"/> <path
|
||||
android:pathData="M512,416.01c0,11.78 -9.56,21.31 -21.34,21.31H21.33C9.55,437.33 0,427.8 0,416.01V74.67c0,-11.78 9.55,-21.34 21.33,-21.34h469.33c11.78,0 21.34,9.56 21.34,21.34L512,416.01L512,416.01z"
|
||||
android:fillColor="#62A43B"/> <path
|
||||
android:pathData="M63.99,160c-5.89,0 -10.66,4.78 -10.66,10.67v149.34c0,5.88 4.77,10.66 10.66,10.66c5.89,0 10.67,-4.78 10.67,-10.66V170.67C74.66,164.78 69.88,160 63.99,160z"
|
||||
android:fillColor="#8CC152"/> <path
|
||||
android:pathData="M74.66,106.67c0,5.89 -4.78,10.66 -10.67,10.66c-5.89,0 -10.66,-4.77 -10.66,-10.66S58.1,96 63.99,96C69.88,96 74.66,100.78 74.66,106.67z"
|
||||
android:fillColor="#E6E9ED"/>
|
||||
<path
|
||||
android:pathData="M74.66,384.01c0,5.88 -4.78,10.66 -10.67,10.66c-5.89,0 -10.66,-4.78 -10.66,-10.66c0,-5.91 4.77,-10.69 10.66,-10.69C69.88,373.33 74.66,378.11 74.66,384.01z"
|
||||
android:fillColor="#E6E9ED"/>
|
||||
<path
|
||||
android:pathData="M448,123.73h-21.34v203.19l-40.31,50.41v0.02c-1.47,1.83 -2.34,4.14 -2.34,6.67c0,5.88 4.78,10.66 10.66,10.66c3.38,0 6.38,-1.56 8.33,-4h0.02l42.66,-53.34l0,0c1.47,-1.81 2.34,-4.13 2.34,-6.66V123.73z"
|
||||
android:fillColor="#E6E9ED"/>
|
||||
<path
|
||||
android:pathData="M437.33,149.33c-11.77,0 -21.33,-9.56 -21.33,-21.33s9.56,-21.33 21.33,-21.33s21.33,9.56 21.33,21.33S449.09,149.33 437.33,149.33z"
|
||||
android:fillColor="#E6E9ED"/>
|
||||
<path
|
||||
android:pathData="M437.33,96c-17.67,0 -32,14.33 -32,32s14.33,32 32,32s32,-14.33 32,-32S455,96 437.33,96zM437.33,138.67c-5.89,0 -10.67,-4.8 -10.67,-10.67c0,-5.88 4.78,-10.67 10.67,-10.67s10.67,4.8 10.67,10.67C448,133.88 443.22,138.67 437.33,138.67z"
|
||||
android:fillColor="#CCD1D9"/>
|
||||
<path
|
||||
android:pathData="M405.33,245.33c0,82.48 -66.86,149.34 -149.33,149.34c-82.47,0 -149.33,-66.86 -149.33,-149.34C106.66,162.86 173.52,96 255.99,96C338.47,96 405.33,162.86 405.33,245.33z"
|
||||
android:fillColor="#434A54"/>
|
||||
<path
|
||||
android:pathData="M266.66,149.33c0,-5.89 -4.77,-10.66 -10.67,-10.66c-58.91,0 -106.66,47.75 -106.66,106.65l0,0c0,5.89 4.77,10.67 10.67,10.67s10.67,-4.78 10.67,-10.67l0,0c0,-22.78 8.88,-44.22 24.99,-60.33c16.12,-16.13 37.55,-25 60.34,-25C261.89,160 266.66,155.22 266.66,149.33z"
|
||||
android:fillColor="#656D78"/>
|
||||
<path
|
||||
android:pathData="M352,234.67c-5.9,0 -10.67,4.77 -10.67,10.66l0,0c0,22.8 -8.88,44.23 -24.98,60.34c-16.13,16.13 -37.56,25 -60.35,25c-5.89,0 -10.66,4.78 -10.66,10.66c0,5.91 4.77,10.69 10.66,10.69c58.91,0 106.66,-47.77 106.66,-106.69C362.65,239.44 357.89,234.67 352,234.67z"
|
||||
android:fillColor="#656D78"/>
|
||||
<path
|
||||
android:pathData="M255.99,288.01c-23.52,0 -42.66,-19.16 -42.66,-42.69c0,-23.52 19.14,-42.66 42.66,-42.66c23.54,0 42.66,19.14 42.66,42.66C298.65,268.86 279.53,288.01 255.99,288.01z"
|
||||
android:fillColor="#FFCE54"/>
|
||||
<path
|
||||
android:pathData="M255.99,192c-29.45,0 -53.33,23.88 -53.33,53.33s23.88,53.34 53.33,53.34c29.46,0 53.34,-23.89 53.34,-53.34S285.45,192 255.99,192zM255.99,277.34c-17.64,0 -32,-14.36 -32,-32.02c0,-17.64 14.36,-32 32,-32c17.65,0 32.01,14.36 32.01,32C288,262.98 273.64,277.34 255.99,277.34z"
|
||||
android:fillColor="#F6BB42"/>
|
||||
<path
|
||||
android:pathData="M266.66,245.33c0,5.89 -4.77,10.67 -10.67,10.67c-5.89,0 -10.66,-4.78 -10.66,-10.67s4.77,-10.66 10.66,-10.66C261.89,234.67 266.66,239.44 266.66,245.33z"
|
||||
android:fillColor="#434A54"/>
|
||||
<path
|
||||
android:pathData="M74.66,234.67H53.33c-5.89,0 -10.66,4.77 -10.66,10.66s4.77,10.67 10.66,10.67h21.34c5.89,0 10.66,-4.78 10.66,-10.67S80.56,234.67 74.66,234.67z"
|
||||
android:fillColor="#434A54"/>
|
||||
</group>
|
||||
<group
|
||||
android:scaleX="0.13"
|
||||
android:scaleY="0.13"
|
||||
android:translateX="21.5"
|
||||
android:translateY="21.5">
|
||||
|
||||
<path
|
||||
android:pathData="M250,0c138.07,0 250,111.93 250,250S388.07,500 250,500 0,388.07 0,250 111.93,0 250,0ZM250,235c-8.28,0 -15,6.72 -15,15c0,8.28 6.72,15 15,15c8.28,0 15,-6.72 15,-15c0,-8.28 -6.72,-15 -15,-15Z">
|
||||
<aapt:attr name="android:fillColor">
|
||||
<gradient
|
||||
android:startX="122.34"
|
||||
android:startY="23.55"
|
||||
android:endX="377.69"
|
||||
android:endY="465.83"
|
||||
android:type="linear">
|
||||
<item android:offset="0.0" android:color="#FF36C12C" />
|
||||
<item android:offset="1.0" android:color="#FF36C12C" />
|
||||
</gradient>
|
||||
</aapt:attr>
|
||||
</path>
|
||||
|
||||
<path
|
||||
android:pathData="M250.41,20.5c126.89,0 229.75,102.86 229.75,229.75c0,126.89 -102.86,229.75 -229.75,229.75c-126.89,0 -229.75,-102.86 -229.75,-229.75C20.66,123.36 123.53,20.5 250.41,20.5ZM250.85,161.82c-49.09,0 -88.88,39.79 -88.88,88.88c0,49.09 39.79,88.88 88.88,88.88c49.09,0 88.88,-39.79 88.88,-88.88c0,-49.09 -39.79,-88.88 -88.88,-88.88Z">
|
||||
<aapt:attr name="android:fillColor">
|
||||
<gradient
|
||||
android:startX="116.21"
|
||||
android:startY="67.61"
|
||||
android:endX="403.29"
|
||||
android:endY="429.34"
|
||||
android:type="linear">
|
||||
<item android:offset="0.0" android:color="#66060606" />
|
||||
<item android:offset="1.0" android:color="#CC060606" />
|
||||
</gradient>
|
||||
</aapt:attr>
|
||||
</path>
|
||||
|
||||
<path
|
||||
android:pathData="M453.23,307.8c-18.5,72.24 -73.8,129.26 -144.2,148.92l-36.39,-138.74c21.97,-7.21 39.22,-24.84 45.88,-47.06l134.71,36.88Z">
|
||||
<aapt:attr name="android:fillColor">
|
||||
<gradient
|
||||
android:startX="420.63"
|
||||
android:startY="403.74"
|
||||
android:endX="78.4"
|
||||
android:endY="117.92"
|
||||
android:type="linear">
|
||||
<item android:offset="0.0" android:color="#33FFFFFF" />
|
||||
<item android:offset="1.0" android:color="#4DFFFFFF" />
|
||||
</gradient>
|
||||
</aapt:attr>
|
||||
</path>
|
||||
|
||||
<path
|
||||
android:pathData="M228.3,183.04c-21.73,7.15 -38.82,24.5 -45.62,46.39L47.5,192.42c18.5,-72.24 73.8,-129.26 144.2,-148.92l36.6,139.54Z">
|
||||
<aapt:attr name="android:fillColor">
|
||||
<gradient
|
||||
android:startX="420.63"
|
||||
android:startY="403.74"
|
||||
android:endX="78.4"
|
||||
android:endY="117.92"
|
||||
android:type="linear">
|
||||
<item android:offset="0.0" android:color="#33FFFFFF" />
|
||||
<item android:offset="1.0" android:color="#4DFFFFFF" />
|
||||
</gradient>
|
||||
</aapt:attr>
|
||||
</path>
|
||||
|
||||
<path
|
||||
android:fillColor="#66FFFFFF"
|
||||
android:pathData="M250.5,179.5c39.21,0 71,31.79 71,71s-31.79,71 -71,71s-71,-31.79 -71,-71s31.79,-71 71,-71ZM250,235c-8.28,0 -15,6.72 -15,15c0,8.28 6.72,15 15,15c8.28,0 15,-6.72 15,-15c0,-8.28 -6.72,-15 -15,-15Z" />
|
||||
</group>
|
||||
</vector>
|
||||
@@ -1,53 +1,77 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:aapt="http://schemas.android.com/aapt"
|
||||
android:width="108dp"
|
||||
android:height="108dp"
|
||||
android:viewportWidth="512"
|
||||
android:viewportHeight="512">
|
||||
<group android:scaleX="0.55"
|
||||
android:scaleY="0.55"
|
||||
android:translateX="150.56"
|
||||
android:translateY="150.56">
|
||||
android:viewportWidth="108"
|
||||
android:viewportHeight="108">
|
||||
|
||||
<group
|
||||
android:scaleX="0.13"
|
||||
android:scaleY="0.13"
|
||||
android:translateX="21.5"
|
||||
android:translateY="21.5">
|
||||
<path
|
||||
android:pathData="M512,437.33c0,11.78 -9.56,21.34 -21.34,21.34H21.33C9.55,458.67 0,449.11 0,437.33V96c0,-11.78 9.55,-21.33 21.33,-21.33h469.33c11.78,0 21.34,9.55 21.34,21.33L512,437.33L512,437.33z"
|
||||
android:fillColor="#8CC152"/> <path
|
||||
android:pathData="M512,416.01c0,11.78 -9.56,21.31 -21.34,21.31H21.33C9.55,437.33 0,427.8 0,416.01V74.67c0,-11.78 9.55,-21.34 21.33,-21.34h469.33c11.78,0 21.34,9.56 21.34,21.34L512,416.01L512,416.01z"
|
||||
android:fillColor="#62A43B"/> <path
|
||||
android:pathData="M63.99,160c-5.89,0 -10.66,4.78 -10.66,10.67v149.34c0,5.88 4.77,10.66 10.66,10.66c5.89,0 10.67,-4.78 10.67,-10.66V170.67C74.66,164.78 69.88,160 63.99,160z"
|
||||
android:fillColor="#8CC152"/> <path
|
||||
android:pathData="M74.66,106.67c0,5.89 -4.78,10.66 -10.67,10.66c-5.89,0 -10.66,-4.77 -10.66,-10.66S58.1,96 63.99,96C69.88,96 74.66,100.78 74.66,106.67z"
|
||||
android:fillColor="#E6E9ED"/>
|
||||
android:pathData="M250,0c138.07,0 250,111.93 250,250S388.07,500 250,500 0,388.07 0,250 111.93,0 250,0ZM250,235c-8.28,0 -15,6.72 -15,15c0,8.28 6.72,15 15,15c8.28,0 15,-6.72 15,-15c0,-8.28 -6.72,-15 -15,-15Z">
|
||||
<aapt:attr name="android:fillColor">
|
||||
<gradient
|
||||
android:startX="122.34"
|
||||
android:startY="23.55"
|
||||
android:endX="377.69"
|
||||
android:endY="465.83"
|
||||
android:type="linear">
|
||||
<item android:offset="0.0" android:color="#FF36C12C" />
|
||||
<item android:offset="1.0" android:color="#FF36C12C" />
|
||||
</gradient>
|
||||
</aapt:attr>
|
||||
</path>
|
||||
|
||||
<path
|
||||
android:pathData="M74.66,384.01c0,5.88 -4.78,10.66 -10.67,10.66c-5.89,0 -10.66,-4.78 -10.66,-10.66c0,-5.91 4.77,-10.69 10.66,-10.69C69.88,373.33 74.66,378.11 74.66,384.01z"
|
||||
android:fillColor="#E6E9ED"/>
|
||||
android:pathData="M250.41,20.5c126.89,0 229.75,102.86 229.75,229.75c0,126.89 -102.86,229.75 -229.75,229.75c-126.89,0 -229.75,-102.86 -229.75,-229.75C20.66,123.36 123.53,20.5 250.41,20.5ZM250.85,161.82c-49.09,0 -88.88,39.79 -88.88,88.88c0,49.09 39.79,88.88 88.88,88.88c49.09,0 88.88,-39.79 88.88,-88.88c0,-49.09 -39.79,-88.88 -88.88,-88.88Z">
|
||||
<aapt:attr name="android:fillColor">
|
||||
<gradient
|
||||
android:startX="116.21"
|
||||
android:startY="67.61"
|
||||
android:endX="403.29"
|
||||
android:endY="429.34"
|
||||
android:type="linear">
|
||||
<item android:offset="0.0" android:color="#66060606" />
|
||||
<item android:offset="1.0" android:color="#CC060606" />
|
||||
</gradient>
|
||||
</aapt:attr>
|
||||
</path>
|
||||
|
||||
<path
|
||||
android:pathData="M448,123.73h-21.34v203.19l-40.31,50.41v0.02c-1.47,1.83 -2.34,4.14 -2.34,6.67c0,5.88 4.78,10.66 10.66,10.66c3.38,0 6.38,-1.56 8.33,-4h0.02l42.66,-53.34l0,0c1.47,-1.81 2.34,-4.13 2.34,-6.66V123.73z"
|
||||
android:fillColor="#E6E9ED"/>
|
||||
android:pathData="M453.23,307.8c-18.5,72.24 -73.8,129.26 -144.2,148.92l-36.39,-138.74c21.97,-7.21 39.22,-24.84 45.88,-47.06l134.71,36.88Z">
|
||||
<aapt:attr name="android:fillColor">
|
||||
<gradient
|
||||
android:startX="420.63"
|
||||
android:startY="403.74"
|
||||
android:endX="78.4"
|
||||
android:endY="117.92"
|
||||
android:type="linear">
|
||||
<item android:offset="0.0" android:color="#33FFFFFF" />
|
||||
<item android:offset="1.0" android:color="#4DFFFFFF" />
|
||||
</gradient>
|
||||
</aapt:attr>
|
||||
</path>
|
||||
|
||||
<path
|
||||
android:pathData="M437.33,149.33c-11.77,0 -21.33,-9.56 -21.33,-21.33s9.56,-21.33 21.33,-21.33s21.33,9.56 21.33,21.33S449.09,149.33 437.33,149.33z"
|
||||
android:fillColor="#E6E9ED"/>
|
||||
android:pathData="M228.3,183.04c-21.73,7.15 -38.82,24.5 -45.62,46.39L47.5,192.42c18.5,-72.24 73.8,-129.26 144.2,-148.92l36.6,139.54Z">
|
||||
<aapt:attr name="android:fillColor">
|
||||
<gradient
|
||||
android:startX="420.63"
|
||||
android:startY="403.74"
|
||||
android:endX="78.4"
|
||||
android:endY="117.92"
|
||||
android:type="linear">
|
||||
<item android:offset="0.0" android:color="#33FFFFFF" />
|
||||
<item android:offset="1.0" android:color="#4DFFFFFF" />
|
||||
</gradient>
|
||||
</aapt:attr>
|
||||
</path>
|
||||
|
||||
<path
|
||||
android:pathData="M437.33,96c-17.67,0 -32,14.33 -32,32s14.33,32 32,32s32,-14.33 32,-32S455,96 437.33,96zM437.33,138.67c-5.89,0 -10.67,-4.8 -10.67,-10.67c0,-5.88 4.78,-10.67 10.67,-10.67s10.67,4.8 10.67,10.67C448,133.88 443.22,138.67 437.33,138.67z"
|
||||
android:fillColor="#CCD1D9"/>
|
||||
<path
|
||||
android:pathData="M405.33,245.33c0,82.48 -66.86,149.34 -149.33,149.34c-82.47,0 -149.33,-66.86 -149.33,-149.34C106.66,162.86 173.52,96 255.99,96C338.47,96 405.33,162.86 405.33,245.33z"
|
||||
android:fillColor="#434A54"/>
|
||||
<path
|
||||
android:pathData="M266.66,149.33c0,-5.89 -4.77,-10.66 -10.67,-10.66c-58.91,0 -106.66,47.75 -106.66,106.65l0,0c0,5.89 4.77,10.67 10.67,10.67s10.67,-4.78 10.67,-10.67l0,0c0,-22.78 8.88,-44.22 24.99,-60.33c16.12,-16.13 37.55,-25 60.34,-25C261.89,160 266.66,155.22 266.66,149.33z"
|
||||
android:fillColor="#656D78"/>
|
||||
<path
|
||||
android:pathData="M352,234.67c-5.9,0 -10.67,4.77 -10.67,10.66l0,0c0,22.8 -8.88,44.23 -24.98,60.34c-16.13,16.13 -37.56,25 -60.35,25c-5.89,0 -10.66,4.78 -10.66,10.66c0,5.91 4.77,10.69 10.66,10.69c58.91,0 106.66,-47.77 106.66,-106.69C362.65,239.44 357.89,234.67 352,234.67z"
|
||||
android:fillColor="#656D78"/>
|
||||
<path
|
||||
android:pathData="M255.99,288.01c-23.52,0 -42.66,-19.16 -42.66,-42.69c0,-23.52 19.14,-42.66 42.66,-42.66c23.54,0 42.66,19.14 42.66,42.66C298.65,268.86 279.53,288.01 255.99,288.01z"
|
||||
android:fillColor="#FFCE54"/>
|
||||
<path
|
||||
android:pathData="M255.99,192c-29.45,0 -53.33,23.88 -53.33,53.33s23.88,53.34 53.33,53.34c29.46,0 53.34,-23.89 53.34,-53.34S285.45,192 255.99,192zM255.99,277.34c-17.64,0 -32,-14.36 -32,-32.02c0,-17.64 14.36,-32 32,-32c17.65,0 32.01,14.36 32.01,32C288,262.98 273.64,277.34 255.99,277.34z"
|
||||
android:fillColor="#F6BB42"/>
|
||||
<path
|
||||
android:pathData="M266.66,245.33c0,5.89 -4.77,10.67 -10.67,10.67c-5.89,0 -10.66,-4.78 -10.66,-10.67s4.77,-10.66 10.66,-10.66C261.89,234.67 266.66,239.44 266.66,245.33z"
|
||||
android:fillColor="#434A54"/>
|
||||
<path
|
||||
android:pathData="M74.66,234.67H53.33c-5.89,0 -10.66,4.77 -10.66,10.66s4.77,10.67 10.66,10.67h21.34c5.89,0 10.66,-4.78 10.66,-10.67S80.56,234.67 74.66,234.67z"
|
||||
android:fillColor="#434A54"/>
|
||||
</group>
|
||||
android:fillColor="#66FFFFFF"
|
||||
android:pathData="M250.5,179.5c39.21,0 71,31.79 71,71s-31.79,71 -71,71s-71,-31.79 -71,-71s31.79,-71 71,-71ZM250,235c-8.28,0 -15,6.72 -15,15c0,8.28 6.72,15 15,15c8.28,0 15,-6.72 15,-15c0,-8.28 -6.72,-15 -15,-15Z" />
|
||||
</group>
|
||||
</vector>
|
||||
77
app/src/degoogled/res/drawable/ic_toolbar_tempo.xml
Normal file
@@ -0,0 +1,77 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:aapt="http://schemas.android.com/aapt"
|
||||
android:width="108dp"
|
||||
android:height="108dp"
|
||||
android:viewportWidth="512"
|
||||
android:viewportHeight="522">
|
||||
<group
|
||||
android:scaleX="1.0"
|
||||
android:scaleY="1.0"
|
||||
android:translateX="14.0"
|
||||
android:translateY="14.0">
|
||||
|
||||
<path
|
||||
android:pathData="M250,0c138.07,0 250,111.93 250,250S388.07,500 250,500 0,388.07 0,250 111.93,0 250,0ZM250,235c-8.28,0 -15,6.72 -15,15c0,8.28 6.72,15 15,15c8.28,0 15,-6.72 15,-15c0,-8.28 -6.72,-15 -15,-15Z">
|
||||
<aapt:attr name="android:fillColor">
|
||||
<gradient
|
||||
android:startX="122.34"
|
||||
android:startY="23.55"
|
||||
android:endX="377.69"
|
||||
android:endY="465.83"
|
||||
android:type="linear">
|
||||
<item android:offset="0.0" android:color="#FF36C12C" />
|
||||
<item android:offset="1.0" android:color="#FF36C12C" />
|
||||
</gradient>
|
||||
</aapt:attr>
|
||||
</path>
|
||||
|
||||
<path
|
||||
android:pathData="M250.41,20.5c126.89,0 229.75,102.86 229.75,229.75c0,126.89 -102.86,229.75 -229.75,229.75c-126.89,0 -229.75,-102.86 -229.75,-229.75C20.66,123.36 123.53,20.5 250.41,20.5ZM250.85,161.82c-49.09,0 -88.88,39.79 -88.88,88.88c0,49.09 39.79,88.88 88.88,88.88c49.09,0 88.88,-39.79 88.88,-88.88c0,-49.09 -39.79,-88.88 -88.88,-88.88Z">
|
||||
<aapt:attr name="android:fillColor">
|
||||
<gradient
|
||||
android:startX="116.21"
|
||||
android:startY="67.61"
|
||||
android:endX="403.29"
|
||||
android:endY="429.34"
|
||||
android:type="linear">
|
||||
<item android:offset="0.0" android:color="#66060606" />
|
||||
<item android:offset="1.0" android:color="#CC060606" />
|
||||
</gradient>
|
||||
</aapt:attr>
|
||||
</path>
|
||||
|
||||
<path
|
||||
android:pathData="M453.23,307.8c-18.5,72.24 -73.8,129.26 -144.2,148.92l-36.39,-138.74c21.97,-7.21 39.22,-24.84 45.88,-47.06l134.71,36.88Z">
|
||||
<aapt:attr name="android:fillColor">
|
||||
<gradient
|
||||
android:startX="420.63"
|
||||
android:startY="403.74"
|
||||
android:endX="78.4"
|
||||
android:endY="117.92"
|
||||
android:type="linear">
|
||||
<item android:offset="0.0" android:color="#33FFFFFF" />
|
||||
<item android:offset="1.0" android:color="#4DFFFFFF" />
|
||||
</gradient>
|
||||
</aapt:attr>
|
||||
</path>
|
||||
|
||||
<path
|
||||
android:pathData="M228.3,183.04c-21.73,7.15 -38.82,24.5 -45.62,46.39L47.5,192.42c18.5,-72.24 73.8,-129.26 144.2,-148.92l36.6,139.54Z">
|
||||
<aapt:attr name="android:fillColor">
|
||||
<gradient
|
||||
android:startX="420.63"
|
||||
android:startY="403.74"
|
||||
android:endX="78.4"
|
||||
android:endY="117.92"
|
||||
android:type="linear">
|
||||
<item android:offset="0.0" android:color="#33FFFFFF" />
|
||||
<item android:offset="1.0" android:color="#4DFFFFFF" />
|
||||
</gradient>
|
||||
</aapt:attr>
|
||||
</path>
|
||||
|
||||
<path
|
||||
android:fillColor="#66FFFFFF"
|
||||
android:pathData="M250.5,179.5c39.21,0 71,31.79 71,71s-31.79,71 -71,71s-71,-31.79 -71,-71s31.79,-71 71,-71ZM250,235c-8.28,0 -15,6.72 -15,15c0,8.28 6.72,15 15,15c8.28,0 15,-6.72 15,-15c0,-8.28 -6.72,-15 -15,-15Z" />
|
||||
</group>
|
||||
</vector>
|
||||
78
app/src/degoogled/res/drawable/logo.xml
Normal file
@@ -0,0 +1,78 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:aapt="http://schemas.android.com/aapt"
|
||||
android:width="108dp"
|
||||
android:height="108dp"
|
||||
android:viewportWidth="108"
|
||||
android:viewportHeight="108">
|
||||
|
||||
<group
|
||||
android:scaleX="0.16"
|
||||
android:scaleY="0.16"
|
||||
android:translateX="14.0"
|
||||
android:translateY="14.0">
|
||||
|
||||
<path
|
||||
android:pathData="M250,0c138.07,0 250,111.93 250,250S388.07,500 250,500 0,388.07 0,250 111.93,0 250,0ZM250,235c-8.28,0 -15,6.72 -15,15c0,8.28 6.72,15 15,15c8.28,0 15,-6.72 15,-15c0,-8.28 -6.72,-15 -15,-15Z">
|
||||
<aapt:attr name="android:fillColor">
|
||||
<gradient
|
||||
android:startX="122.34"
|
||||
android:startY="23.55"
|
||||
android:endX="377.69"
|
||||
android:endY="465.83"
|
||||
android:type="linear">
|
||||
<item android:offset="0.0" android:color="#FF36C12C" />
|
||||
<item android:offset="1.0" android:color="#FF36C12C" />
|
||||
</gradient>
|
||||
</aapt:attr>
|
||||
</path>
|
||||
|
||||
<path
|
||||
android:pathData="M250.41,20.5c126.89,0 229.75,102.86 229.75,229.75c0,126.89 -102.86,229.75 -229.75,229.75c-126.89,0 -229.75,-102.86 -229.75,-229.75C20.66,123.36 123.53,20.5 250.41,20.5ZM250.85,161.82c-49.09,0 -88.88,39.79 -88.88,88.88c0,49.09 39.79,88.88 88.88,88.88c49.09,0 88.88,-39.79 88.88,-88.88c0,-49.09 -39.79,-88.88 -88.88,-88.88Z">
|
||||
<aapt:attr name="android:fillColor">
|
||||
<gradient
|
||||
android:startX="116.21"
|
||||
android:startY="67.61"
|
||||
android:endX="403.29"
|
||||
android:endY="429.34"
|
||||
android:type="linear">
|
||||
<item android:offset="0.0" android:color="#66060606" />
|
||||
<item android:offset="1.0" android:color="#CC060606" />
|
||||
</gradient>
|
||||
</aapt:attr>
|
||||
</path>
|
||||
|
||||
<path
|
||||
android:pathData="M453.23,307.8c-18.5,72.24 -73.8,129.26 -144.2,148.92l-36.39,-138.74c21.97,-7.21 39.22,-24.84 45.88,-47.06l134.71,36.88Z">
|
||||
<aapt:attr name="android:fillColor">
|
||||
<gradient
|
||||
android:startX="420.63"
|
||||
android:startY="403.74"
|
||||
android:endX="78.4"
|
||||
android:endY="117.92"
|
||||
android:type="linear">
|
||||
<item android:offset="0.0" android:color="#33FFFFFF" />
|
||||
<item android:offset="1.0" android:color="#4DFFFFFF" />
|
||||
</gradient>
|
||||
</aapt:attr>
|
||||
</path>
|
||||
|
||||
<path
|
||||
android:pathData="M228.3,183.04c-21.73,7.15 -38.82,24.5 -45.62,46.39L47.5,192.42c18.5,-72.24 73.8,-129.26 144.2,-148.92l36.6,139.54Z">
|
||||
<aapt:attr name="android:fillColor">
|
||||
<gradient
|
||||
android:startX="420.63"
|
||||
android:startY="403.74"
|
||||
android:endX="78.4"
|
||||
android:endY="117.92"
|
||||
android:type="linear">
|
||||
<item android:offset="0.0" android:color="#33FFFFFF" />
|
||||
<item android:offset="1.0" android:color="#4DFFFFFF" />
|
||||
</gradient>
|
||||
</aapt:attr>
|
||||
</path>
|
||||
|
||||
<path
|
||||
android:fillColor="#66FFFFFF"
|
||||
android:pathData="M250.5,179.5c39.21,0 71,31.79 71,71s-31.79,71 -71,71s-71,-31.79 -71,-71s31.79,-71 71,-71ZM250,235c-8.28,0 -15,6.72 -15,15c0,8.28 6.72,15 15,15c8.28,0 15,-6.72 15,-15c0,-8.28 -6.72,-15 -15,-15Z" />
|
||||
</group>
|
||||
</vector>
|
||||
@@ -1,5 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<background android:drawable="@color/ic_launcher_background"/>
|
||||
<foreground android:drawable="@drawable/ic_launcher_foreground"/>
|
||||
</adaptive-icon>
|
||||
<background android:drawable="@drawable/ic_launcher_background_tempor_b"/>
|
||||
<foreground android:drawable="@drawable/ic_launcher_foreground_tempor_b"/>
|
||||
<monochrome android:drawable="@drawable/ic_launcher_monochrome_tempor_b"/>
|
||||
</adaptive-icon>
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<background android:drawable="@color/ic_launcher_background"/>
|
||||
<foreground android:drawable="@drawable/ic_launcher_foreground"/>
|
||||
</adaptive-icon>
|
||||
|
Before Width: | Height: | Size: 1.6 KiB |
|
Before Width: | Height: | Size: 3.2 KiB |
|
Before Width: | Height: | Size: 1.1 KiB |
|
Before Width: | Height: | Size: 2.0 KiB |
|
Before Width: | Height: | Size: 2.2 KiB |
|
Before Width: | Height: | Size: 4.4 KiB |
|
Before Width: | Height: | Size: 3.2 KiB |
|
Before Width: | Height: | Size: 6.7 KiB |
|
Before Width: | Height: | Size: 4.3 KiB |
|
Before Width: | Height: | Size: 9.2 KiB |
@@ -1,4 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<color name="ic_launcher_background">#626A75</color>
|
||||
</resources>
|
||||
|
Before Width: | Height: | Size: 20 KiB |
@@ -0,0 +1,44 @@
|
||||
package com.cappielloantonio.tempo.navigation;
|
||||
|
||||
import android.view.View;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.navigation.NavController;
|
||||
|
||||
import com.google.android.material.bottomsheet.BottomSheetBehavior;
|
||||
|
||||
public class NavigationController {
|
||||
|
||||
NavigationHelper helper;
|
||||
|
||||
public NavigationController(@NonNull NavigationHelper helper) {
|
||||
this.helper = helper;
|
||||
}
|
||||
|
||||
public void syncWithBottomSheetBehavior(BottomSheetBehavior<View> bottomSheetBehavior,
|
||||
NavController navController) {
|
||||
helper.syncWithBottomSheetBehavior(bottomSheetBehavior, navController);
|
||||
|
||||
}
|
||||
|
||||
public void setNavbarVisibility(boolean visibility) {
|
||||
helper.setBottomNavigationBarVisibility(visibility);
|
||||
}
|
||||
|
||||
public void setDrawerLock(boolean visibility) {
|
||||
helper.setNavigationDrawerLock(visibility);
|
||||
}
|
||||
|
||||
public boolean isNavigationDrawerLocked() {
|
||||
return helper.isNavigationDrawerLocked();
|
||||
}
|
||||
|
||||
public void toggleDrawerLockOnOrientation(AppCompatActivity activity) {
|
||||
helper.toggleNavigationDrawerLockOnOrientationChange(activity);
|
||||
}
|
||||
|
||||
public void setSystemBarsVisibility(AppCompatActivity activity, boolean visibility) {
|
||||
helper.setSystemBarsVisibility(activity, visibility);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,167 @@
|
||||
package com.cappielloantonio.tempo.navigation;
|
||||
|
||||
import android.content.res.Configuration;
|
||||
import android.view.View;
|
||||
import android.view.Window;
|
||||
import android.widget.FrameLayout;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.OptIn;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.core.view.WindowCompat;
|
||||
import androidx.core.view.WindowInsetsCompat;
|
||||
import androidx.core.view.WindowInsetsControllerCompat;
|
||||
import androidx.drawerlayout.widget.DrawerLayout;
|
||||
import androidx.media3.common.util.UnstableApi;
|
||||
import androidx.navigation.NavController;
|
||||
import androidx.navigation.NavDestination;
|
||||
import androidx.navigation.fragment.NavHostFragment;
|
||||
import androidx.navigation.ui.NavigationUI;
|
||||
|
||||
import com.cappielloantonio.tempo.R;
|
||||
import com.cappielloantonio.tempo.util.Preferences;
|
||||
import com.google.android.material.bottomnavigation.BottomNavigationView;
|
||||
import com.google.android.material.bottomsheet.BottomSheetBehavior;
|
||||
import com.google.android.material.navigation.NavigationView;
|
||||
|
||||
import org.jetbrains.annotations.Contract;
|
||||
|
||||
public class NavigationHelper {
|
||||
/* UI components */
|
||||
private BottomNavigationView bottomNavigationView;
|
||||
private FrameLayout bottomNavigationViewFrame;
|
||||
private DrawerLayout drawerLayout;
|
||||
|
||||
/* Navigation components */
|
||||
private NavigationView navigationView;
|
||||
private NavHostFragment navHostFragment;
|
||||
|
||||
/* States that need to be remembered */
|
||||
// -- //
|
||||
|
||||
/* Private constructor */
|
||||
public NavigationHelper(@NonNull BottomNavigationView bottomNavigationView,
|
||||
@NonNull FrameLayout bottomNavigationViewFrame,
|
||||
@NonNull DrawerLayout drawerLayout,
|
||||
@NonNull NavigationView navigationView,
|
||||
@NonNull NavHostFragment navHostFragment) {
|
||||
this.bottomNavigationView = bottomNavigationView;
|
||||
this.bottomNavigationViewFrame = bottomNavigationViewFrame;
|
||||
this.drawerLayout = drawerLayout;
|
||||
this.navigationView = navigationView;
|
||||
this.navHostFragment = navHostFragment;
|
||||
}
|
||||
|
||||
public void syncWithBottomSheetBehavior(@NonNull BottomSheetBehavior<View> bottomSheetBehavior,
|
||||
@NonNull NavController navController) {
|
||||
navController.addOnDestinationChangedListener(
|
||||
(controller, destination, arguments) -> {
|
||||
// React to the user clicking one of these on bottom-navbar/drawer
|
||||
boolean isTarget = isTargetDestination(destination);
|
||||
int currentState = bottomSheetBehavior.getState();
|
||||
|
||||
if (isTarget && currentState == BottomSheetBehavior.STATE_EXPANDED) {
|
||||
bottomSheetBehavior.setState(BottomSheetBehavior.STATE_COLLAPSED);
|
||||
}
|
||||
});
|
||||
|
||||
NavigationUI.setupWithNavController(bottomNavigationView, navController);
|
||||
NavigationUI.setupWithNavController(navigationView, navController);
|
||||
}
|
||||
|
||||
@Contract(pure = true)
|
||||
private static boolean isTargetDestination(NavDestination destination) {
|
||||
int destId = destination.getId();
|
||||
return destId == R.id.homeFragment ||
|
||||
destId == R.id.libraryFragment ||
|
||||
destId == R.id.downloadFragment ||
|
||||
destId == R.id.albumCatalogueFragment ||
|
||||
destId == R.id.artistCatalogueFragment ||
|
||||
destId == R.id.genreCatalogueFragment ||
|
||||
destId == R.id.playlistCatalogueFragment;
|
||||
}
|
||||
|
||||
/*
|
||||
Clean public methods
|
||||
Removes the need to invoke the activity on the fragment
|
||||
*/
|
||||
|
||||
public void setBottomNavigationBarVisibility(boolean visible) {
|
||||
int visibility = visible
|
||||
? View.VISIBLE
|
||||
: View.GONE;
|
||||
bottomNavigationView.setVisibility(visibility);
|
||||
bottomNavigationViewFrame.setVisibility(visibility);
|
||||
}
|
||||
|
||||
public void setNavigationDrawerLock(boolean locked) {
|
||||
int mode = locked
|
||||
? DrawerLayout.LOCK_MODE_LOCKED_CLOSED
|
||||
: DrawerLayout.LOCK_MODE_UNLOCKED;
|
||||
drawerLayout.setDrawerLockMode(mode);
|
||||
}
|
||||
|
||||
public boolean isNavigationDrawerLocked() {
|
||||
return drawerLayout.getDrawerLockMode(navigationView) != DrawerLayout.LOCK_MODE_UNLOCKED;
|
||||
}
|
||||
|
||||
@OptIn(markerClass = UnstableApi.class)
|
||||
public void toggleNavigationDrawerLockOnOrientationChange(
|
||||
AppCompatActivity activity) {
|
||||
|
||||
int orientation = activity.getResources().getConfiguration().orientation;
|
||||
boolean isLandscape = orientation == Configuration.ORIENTATION_LANDSCAPE;
|
||||
|
||||
if (Preferences.getEnableDrawerOnPortrait()) {
|
||||
setNavigationDrawerLock(false);
|
||||
return;
|
||||
}
|
||||
setNavigationDrawerLock(!isLandscape);
|
||||
}
|
||||
|
||||
/*
|
||||
All of these are the "backward compatible" changes that don't break the assumption
|
||||
that everything was defined on the activity and is gobally available
|
||||
*/
|
||||
|
||||
@NonNull
|
||||
public BottomNavigationView getBottomNavigationView() {
|
||||
return bottomNavigationView;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public FrameLayout getBottomNavigationViewFrame() {
|
||||
return bottomNavigationViewFrame;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public DrawerLayout getDrawerLayout() {
|
||||
return drawerLayout;
|
||||
}
|
||||
|
||||
/*
|
||||
Auxiliar functions, could be moved somewhere else
|
||||
*/
|
||||
|
||||
@OptIn(markerClass = UnstableApi.class)
|
||||
public void setSystemBarsVisibility(AppCompatActivity activity, boolean visibility) {
|
||||
WindowInsetsControllerCompat insetsController;
|
||||
Window window = activity.getWindow();
|
||||
View decorView = window.getDecorView();
|
||||
insetsController = new WindowInsetsControllerCompat(window, decorView);
|
||||
|
||||
if (visibility) {
|
||||
WindowCompat.setDecorFitsSystemWindows(window, true);
|
||||
insetsController.show(WindowInsetsCompat.Type.navigationBars());
|
||||
insetsController.show(WindowInsetsCompat.Type.statusBars());
|
||||
insetsController.setSystemBarsBehavior(
|
||||
WindowInsetsControllerCompat.BEHAVIOR_DEFAULT);
|
||||
} else {
|
||||
WindowCompat.setDecorFitsSystemWindows(window, false);
|
||||
insetsController.hide(WindowInsetsCompat.Type.navigationBars());
|
||||
insetsController.hide(WindowInsetsCompat.Type.statusBars());
|
||||
insetsController.setSystemBarsBehavior(
|
||||
WindowInsetsControllerCompat.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -35,6 +35,7 @@ import com.cappielloantonio.tempo.subsonic.models.InternetRadioStation;
|
||||
import com.cappielloantonio.tempo.subsonic.models.MusicFolder;
|
||||
import com.cappielloantonio.tempo.subsonic.models.Playlist;
|
||||
import com.cappielloantonio.tempo.subsonic.models.PodcastEpisode;
|
||||
import com.cappielloantonio.tempo.subsonic.models.Genre;
|
||||
import com.cappielloantonio.tempo.util.DownloadUtil;
|
||||
import com.cappielloantonio.tempo.util.MappingUtil;
|
||||
import com.cappielloantonio.tempo.util.MusicUtil;
|
||||
@@ -789,7 +790,7 @@ public class AutomotiveRepository {
|
||||
|
||||
App.getSubsonicClientInstance(false)
|
||||
.getSearchingClient()
|
||||
.search3(query, 20, 20, 20)
|
||||
.search3(query, 20, 0, 20, 0, 20, 0)
|
||||
.enqueue(new Callback<ApiResponse>() {
|
||||
@Override
|
||||
public void onResponse(@NonNull Call<ApiResponse> call, @NonNull Response<ApiResponse> response) {
|
||||
@@ -952,6 +953,116 @@ public class AutomotiveRepository {
|
||||
thread.start();
|
||||
}
|
||||
|
||||
public ListenableFuture<LibraryResult<ImmutableList<MediaItem>>> getGenres(String prefix) {
|
||||
final SettableFuture<LibraryResult<ImmutableList<MediaItem>>> listenableFuture = SettableFuture.create();
|
||||
|
||||
App.getSubsonicClientInstance(false)
|
||||
.getBrowsingClient()
|
||||
.getGenres()
|
||||
.enqueue(new Callback<ApiResponse>() {
|
||||
@Override
|
||||
public void onResponse(@NonNull Call<ApiResponse> call, @NonNull Response<ApiResponse> response) {
|
||||
if (response.isSuccessful() && response.body() != null && response.body().getSubsonicResponse().getGenres() != null && response.body().getSubsonicResponse().getGenres().getGenres() != null) {
|
||||
List<Genre> genres = response.body().getSubsonicResponse().getGenres().getGenres();
|
||||
|
||||
// Sort genres alphabetically by name
|
||||
genres.sort((g1, g2) -> {
|
||||
String name1 = g1.getGenre() != null ? g1.getGenre() : "";
|
||||
String name2 = g2.getGenre() != null ? g2.getGenre() : "";
|
||||
return name1.compareToIgnoreCase(name2);
|
||||
});
|
||||
|
||||
List<MediaItem> mediaItems = new ArrayList<>();
|
||||
|
||||
for (Genre genre : genres) {
|
||||
MediaMetadata mediaMetadata = new MediaMetadata.Builder()
|
||||
.setTitle(genre.getGenre())
|
||||
.setIsBrowsable(true)
|
||||
.setIsPlayable(false)
|
||||
.setMediaType(MediaMetadata.MEDIA_TYPE_PLAYLIST)
|
||||
.build();
|
||||
|
||||
MediaItem mediaItem = new MediaItem.Builder()
|
||||
.setMediaId(prefix + genre.getGenre())
|
||||
.setMediaMetadata(mediaMetadata)
|
||||
.setUri("")
|
||||
.build();
|
||||
|
||||
mediaItems.add(mediaItem);
|
||||
}
|
||||
|
||||
LibraryResult<ImmutableList<MediaItem>> libraryResult = LibraryResult.ofItemList(ImmutableList.copyOf(mediaItems), null);
|
||||
|
||||
listenableFuture.set(libraryResult);
|
||||
} else {
|
||||
listenableFuture.set(LibraryResult.ofError(LibraryResult.RESULT_ERROR_BAD_VALUE));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(@NonNull Call<ApiResponse> call, @NonNull Throwable t) {
|
||||
listenableFuture.setException(t);
|
||||
}
|
||||
});
|
||||
|
||||
return listenableFuture;
|
||||
}
|
||||
|
||||
public ListenableFuture<LibraryResult<ImmutableList<MediaItem>>> getSongsByGenre(String genre, int count, boolean shuffle) {
|
||||
final SettableFuture<LibraryResult<ImmutableList<MediaItem>>> listenableFuture = SettableFuture.create();
|
||||
|
||||
Call<ApiResponse> call;
|
||||
if (shuffle) {
|
||||
call = App.getSubsonicClientInstance(false)
|
||||
.getAlbumSongListClient()
|
||||
.getRandomSongs(count, null, null, genre);
|
||||
} else {
|
||||
call = App.getSubsonicClientInstance(false)
|
||||
.getAlbumSongListClient()
|
||||
.getSongsByGenre(genre, count, 0);
|
||||
}
|
||||
|
||||
call.enqueue(new Callback<ApiResponse>() {
|
||||
@Override
|
||||
public void onResponse(@NonNull Call<ApiResponse> call, @NonNull Response<ApiResponse> response) {
|
||||
if (response.isSuccessful() && response.body() != null) {
|
||||
List<com.cappielloantonio.tempo.subsonic.models.Child> songs;
|
||||
if (shuffle) {
|
||||
songs = response.body().getSubsonicResponse().getRandomSongs() != null
|
||||
? response.body().getSubsonicResponse().getRandomSongs().getSongs()
|
||||
: null;
|
||||
} else {
|
||||
songs = response.body().getSubsonicResponse().getSongsByGenre() != null
|
||||
? response.body().getSubsonicResponse().getSongsByGenre().getSongs()
|
||||
: null;
|
||||
}
|
||||
|
||||
if (songs != null) {
|
||||
setChildrenMetadata(songs);
|
||||
List<MediaItem> mediaItems = MappingUtil.mapMediaItems(songs);
|
||||
LibraryResult<ImmutableList<MediaItem>> libraryResult = LibraryResult.ofItemList(ImmutableList.copyOf(mediaItems), null);
|
||||
listenableFuture.set(libraryResult);
|
||||
} else {
|
||||
listenableFuture.set(LibraryResult.ofError(LibraryResult.RESULT_ERROR_BAD_VALUE));
|
||||
}
|
||||
} else {
|
||||
listenableFuture.set(LibraryResult.ofError(LibraryResult.RESULT_ERROR_BAD_VALUE));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(@NonNull Call<ApiResponse> call, @NonNull Throwable t) {
|
||||
listenableFuture.setException(t);
|
||||
}
|
||||
});
|
||||
|
||||
return listenableFuture;
|
||||
}
|
||||
|
||||
public ListenableFuture<LibraryResult<ImmutableList<MediaItem>>> getSongsByGenre(String genre, int count) {
|
||||
return getSongsByGenre(genre, count, false);
|
||||
}
|
||||
|
||||
private static class GetMediaItemThreadSafe implements Runnable {
|
||||
private final SessionMediaItemDao sessionMediaItemDao;
|
||||
private final String id;
|
||||
|
||||
@@ -1,9 +1,14 @@
|
||||
package com.cappielloantonio.tempo.repository;
|
||||
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.lifecycle.MutableLiveData;
|
||||
import androidx.media3.common.util.UnstableApi;
|
||||
|
||||
import com.cappielloantonio.tempo.App;
|
||||
import com.cappielloantonio.tempo.R;
|
||||
import com.cappielloantonio.tempo.database.AppDatabase;
|
||||
import com.cappielloantonio.tempo.database.dao.RecentSearchDao;
|
||||
import com.cappielloantonio.tempo.model.RecentSearch;
|
||||
@@ -11,13 +16,18 @@ import com.cappielloantonio.tempo.subsonic.base.ApiResponse;
|
||||
import com.cappielloantonio.tempo.subsonic.models.AlbumID3;
|
||||
import com.cappielloantonio.tempo.subsonic.models.ArtistID3;
|
||||
import com.cappielloantonio.tempo.subsonic.models.Child;
|
||||
import com.cappielloantonio.tempo.subsonic.models.Playlist;
|
||||
import com.cappielloantonio.tempo.subsonic.models.PlaylistWithSongs;
|
||||
import com.cappielloantonio.tempo.subsonic.models.SearchResult2;
|
||||
import com.cappielloantonio.tempo.subsonic.models.SearchResult3;
|
||||
import com.cappielloantonio.tempo.util.Preferences;
|
||||
import com.cappielloantonio.tempo.ui.fragment.SearchFragment;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
import retrofit2.Call;
|
||||
import retrofit2.Callback;
|
||||
@@ -31,7 +41,7 @@ public class SearchingRepository {
|
||||
|
||||
App.getSubsonicClientInstance(false)
|
||||
.getSearchingClient()
|
||||
.search3(query, 20, 20, 20)
|
||||
.search3(query, 20, 0, 20, 0, 20, 0)
|
||||
.enqueue(new Callback<ApiResponse>() {
|
||||
@Override
|
||||
public void onResponse(@NonNull Call<ApiResponse> call, @NonNull Response<ApiResponse> response) {
|
||||
@@ -49,12 +59,63 @@ public class SearchingRepository {
|
||||
return result;
|
||||
}
|
||||
|
||||
public MutableLiveData<SearchResult3> search3(String query) {
|
||||
@UnstableApi
|
||||
public MutableLiveData<SearchResult3> search3(SearchFragment sf, String query) {
|
||||
MutableLiveData<SearchResult3> result = new MutableLiveData<>();
|
||||
|
||||
Executors.newSingleThreadExecutor().execute(() -> {
|
||||
List<Child> allSongs = new ArrayList<>();
|
||||
int offset = 0;
|
||||
int limit = 1000;
|
||||
boolean hasMore = true;
|
||||
|
||||
while (hasMore) {
|
||||
try {
|
||||
Response<ApiResponse> response = App.getSubsonicClientInstance(false)
|
||||
.getSearchingClient()
|
||||
.search3(query, limit, offset, 0, 0, 0, 0)
|
||||
.execute();
|
||||
|
||||
if (response.isSuccessful() && response.body() != null) {
|
||||
SearchResult3 tmp = response.body().getSubsonicResponse().getSearchResult3();
|
||||
if (tmp != null && tmp.getSongs() != null && !tmp.getSongs().isEmpty()) {
|
||||
List<Child> fetchedSongs = tmp.getSongs();
|
||||
allSongs.addAll(fetchedSongs);
|
||||
|
||||
offset += fetchedSongs.size();
|
||||
hasMore = fetchedSongs.size() == limit;
|
||||
} else {
|
||||
hasMore = false;
|
||||
}
|
||||
} else {
|
||||
hasMore = false;
|
||||
}
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
hasMore = false;
|
||||
}
|
||||
}
|
||||
PlaylistWithSongs pws = new PlaylistWithSongs("allsongs", allSongs);
|
||||
pws.setName(sf.getView().getContext().getString(R.string.search_all_songs, String.valueOf(allSongs.size())));
|
||||
pws.setSongCount(allSongs.size());
|
||||
List<Playlist> lpws = new ArrayList<>();
|
||||
lpws.add(pws);
|
||||
long duration = 0;
|
||||
for (Child song: allSongs) {
|
||||
if (song != null && song.getDuration() != null) {
|
||||
duration += song.getDuration();
|
||||
}
|
||||
}
|
||||
pws.setDuration(duration);
|
||||
|
||||
new Handler(Looper.getMainLooper()).post(() -> {
|
||||
sf.updateUI(lpws);
|
||||
});
|
||||
});
|
||||
|
||||
App.getSubsonicClientInstance(false)
|
||||
.getSearchingClient()
|
||||
.search3(query, 20, 20, 20)
|
||||
.search3(query, 20, 0, 20, 0, 20, 0)
|
||||
.enqueue(new Callback<ApiResponse>() {
|
||||
@Override
|
||||
public void onResponse(@NonNull Call<ApiResponse> call, @NonNull Response<ApiResponse> response) {
|
||||
@@ -77,7 +138,7 @@ public class SearchingRepository {
|
||||
|
||||
App.getSubsonicClientInstance(false)
|
||||
.getSearchingClient()
|
||||
.search3(query, 5, 5, 5)
|
||||
.search3(query, 5, 0, 5, 0, 5, 0)
|
||||
.enqueue(new Callback<ApiResponse>() {
|
||||
@Override
|
||||
public void onResponse(@NonNull Call<ApiResponse> call, @NonNull Response<ApiResponse> response) {
|
||||
|
||||
@@ -8,15 +8,23 @@ import androidx.lifecycle.MutableLiveData;
|
||||
import com.cappielloantonio.tempo.App;
|
||||
import com.cappielloantonio.tempo.subsonic.base.ApiResponse;
|
||||
import com.cappielloantonio.tempo.subsonic.models.Child;
|
||||
import com.cappielloantonio.tempo.subsonic.models.Directory;
|
||||
import com.cappielloantonio.tempo.subsonic.models.Index;
|
||||
import com.cappielloantonio.tempo.subsonic.models.Indexes;
|
||||
import com.cappielloantonio.tempo.subsonic.models.MusicFolder;
|
||||
import com.cappielloantonio.tempo.subsonic.models.SearchResult3;
|
||||
import com.cappielloantonio.tempo.subsonic.models.SubsonicResponse;
|
||||
import com.cappielloantonio.tempo.util.Constants.SeedType;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
import retrofit2.Call;
|
||||
import retrofit2.Callback;
|
||||
@@ -60,6 +68,20 @@ public class SongRepository {
|
||||
return starredSongs;
|
||||
}
|
||||
|
||||
public MutableLiveData<List<Child>> getAllSongs() {
|
||||
MutableLiveData<List<Child>> allSongs = new MutableLiveData<>(new ArrayList<>());
|
||||
|
||||
Executors.newSingleThreadExecutor().execute(() -> {
|
||||
List<Child> songs = fetchAllSongsViaSearch();
|
||||
if (songs.isEmpty()) {
|
||||
songs = fetchAllSongsViaBrowsing();
|
||||
}
|
||||
allSongs.postValue(songs);
|
||||
});
|
||||
|
||||
return allSongs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Used by ViewModels. Updates the LiveData list incrementally as songs are found.
|
||||
*/
|
||||
@@ -386,4 +408,148 @@ public class SongRepository {
|
||||
});
|
||||
return lyrics;
|
||||
}
|
||||
}
|
||||
|
||||
private List<Child> fetchAllSongsViaSearch() {
|
||||
LinkedHashMap<String, Child> songsById = new LinkedHashMap<>();
|
||||
int offset = 0;
|
||||
int limit = 500;
|
||||
|
||||
while (true) {
|
||||
try {
|
||||
Response<ApiResponse> response = App.getSubsonicClientInstance(false)
|
||||
.getSearchingClient()
|
||||
.search3("", limit, offset, 0, 0, 0, 0)
|
||||
.execute();
|
||||
|
||||
if (!response.isSuccessful() || response.body() == null) {
|
||||
break;
|
||||
}
|
||||
|
||||
SearchResult3 searchResult3 = response.body().getSubsonicResponse().getSearchResult3();
|
||||
List<Child> batch = searchResult3 != null ? searchResult3.getSongs() : null;
|
||||
if (batch == null || batch.isEmpty()) {
|
||||
break;
|
||||
}
|
||||
|
||||
for (Child child : batch) {
|
||||
addPlayableChild(songsById, child);
|
||||
}
|
||||
|
||||
offset += batch.size();
|
||||
} catch (IOException e) {
|
||||
Log.e(TAG, "fetchAllSongsViaSearch()", e);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return new ArrayList<>(songsById.values());
|
||||
}
|
||||
|
||||
private List<Child> fetchAllSongsViaBrowsing() {
|
||||
LinkedHashMap<String, Child> songsById = new LinkedHashMap<>();
|
||||
Set<String> visitedDirectories = new HashSet<>();
|
||||
|
||||
try {
|
||||
Response<ApiResponse> musicFoldersResponse = App.getSubsonicClientInstance(false)
|
||||
.getBrowsingClient()
|
||||
.getMusicFolders()
|
||||
.execute();
|
||||
|
||||
if (musicFoldersResponse.isSuccessful()
|
||||
&& musicFoldersResponse.body() != null
|
||||
&& musicFoldersResponse.body().getSubsonicResponse().getMusicFolders() != null
|
||||
&& musicFoldersResponse.body().getSubsonicResponse().getMusicFolders().getMusicFolders() != null
|
||||
&& !musicFoldersResponse.body().getSubsonicResponse().getMusicFolders().getMusicFolders().isEmpty()) {
|
||||
for (MusicFolder musicFolder : musicFoldersResponse.body().getSubsonicResponse().getMusicFolders().getMusicFolders()) {
|
||||
collectSongsFromIndexes(musicFolder.getId(), songsById, visitedDirectories);
|
||||
}
|
||||
} else {
|
||||
collectSongsFromIndexes(null, songsById, visitedDirectories);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
Log.e(TAG, "fetchAllSongsViaBrowsing()", e);
|
||||
}
|
||||
|
||||
return new ArrayList<>(songsById.values());
|
||||
}
|
||||
|
||||
private void collectSongsFromIndexes(String musicFolderId, LinkedHashMap<String, Child> songsById, Set<String> visitedDirectories) throws IOException {
|
||||
Response<ApiResponse> indexesResponse = App.getSubsonicClientInstance(false)
|
||||
.getBrowsingClient()
|
||||
.getIndexes(musicFolderId, null)
|
||||
.execute();
|
||||
|
||||
if (!indexesResponse.isSuccessful() || indexesResponse.body() == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
Indexes indexes = indexesResponse.body().getSubsonicResponse().getIndexes();
|
||||
if (indexes == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (indexes.getChildren() != null) {
|
||||
for (Child child : indexes.getChildren()) {
|
||||
if (child == null) {
|
||||
continue;
|
||||
}
|
||||
if (child.isDir()) {
|
||||
collectSongsFromDirectory(child.getId(), songsById, visitedDirectories);
|
||||
} else {
|
||||
addPlayableChild(songsById, child);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (indexes.getIndices() != null) {
|
||||
for (Index index : indexes.getIndices()) {
|
||||
if (index == null || index.getArtists() == null) {
|
||||
continue;
|
||||
}
|
||||
for (com.cappielloantonio.tempo.subsonic.models.Artist artist : index.getArtists()) {
|
||||
if (artist != null && artist.getId() != null && !artist.getId().isEmpty()) {
|
||||
collectSongsFromDirectory(artist.getId(), songsById, visitedDirectories);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void collectSongsFromDirectory(String directoryId, LinkedHashMap<String, Child> songsById, Set<String> visitedDirectories) throws IOException {
|
||||
if (directoryId == null || directoryId.isEmpty() || !visitedDirectories.add(directoryId)) {
|
||||
return;
|
||||
}
|
||||
|
||||
Response<ApiResponse> directoryResponse = App.getSubsonicClientInstance(false)
|
||||
.getBrowsingClient()
|
||||
.getMusicDirectory(directoryId)
|
||||
.execute();
|
||||
|
||||
if (!directoryResponse.isSuccessful() || directoryResponse.body() == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
Directory directory = directoryResponse.body().getSubsonicResponse().getDirectory();
|
||||
if (directory == null || directory.getChildren() == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (Child child : directory.getChildren()) {
|
||||
if (child == null) {
|
||||
continue;
|
||||
}
|
||||
if (child.isDir()) {
|
||||
collectSongsFromDirectory(child.getId(), songsById, visitedDirectories);
|
||||
} else {
|
||||
addPlayableChild(songsById, child);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void addPlayableChild(LinkedHashMap<String, Child> songsById, Child child) {
|
||||
if (child == null || child.getId() == null || child.isDir() || child.isVideo()) {
|
||||
return;
|
||||
}
|
||||
songsById.putIfAbsent(child.getId(), child);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,8 +24,8 @@ public class SearchingClient {
|
||||
return searchingService.search2(subsonic.getParams(), query, songCount, albumCount, artistCount);
|
||||
}
|
||||
|
||||
public Call<ApiResponse> search3(String query, int songCount, int albumCount, int artistCount) {
|
||||
public Call<ApiResponse> search3(String query, int songCount, int songOffset, int albumCount, int albumOffset, int artistCount, int artistOffset) {
|
||||
Log.d(TAG, "search3()");
|
||||
return searchingService.search3(subsonic.getParams(), query, songCount, albumCount, artistCount);
|
||||
return searchingService.search3(subsonic.getParams(), query, songCount, songOffset, albumCount, albumOffset, artistCount, artistOffset);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,5 +14,5 @@ public interface SearchingService {
|
||||
Call<ApiResponse> search2(@QueryMap Map<String, String> params, @Query("query") String query, @Query("songCount") int songCount, @Query("albumCount") int albumCount, @Query("artistCount") int artistCount);
|
||||
|
||||
@GET("search3")
|
||||
Call<ApiResponse> search3(@QueryMap Map<String, String> params, @Query("query") String query, @Query("songCount") int songCount, @Query("albumCount") int albumCount, @Query("artistCount") int artistCount);
|
||||
Call<ApiResponse> search3(@QueryMap Map<String, String> params, @Query("query") String query, @Query("songCount") int songCount, @Query("songOffset") int songOffset, @Query("albumCount") int albumCount, @Query("albumOffset") int albumOffset, @Query("artistCount") int artistCount, @Query("artistOffset") int artistOffset);
|
||||
}
|
||||
|
||||
@@ -4,23 +4,16 @@ import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.content.res.Configuration;
|
||||
import android.graphics.Rect;
|
||||
import android.content.IntentFilter;
|
||||
import android.net.ConnectivityManager;
|
||||
import android.net.NetworkInfo;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.text.TextUtils;
|
||||
import android.view.Gravity;
|
||||
import android.view.View;
|
||||
import android.widget.FrameLayout;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.core.splashscreen.SplashScreen;
|
||||
import androidx.core.view.WindowCompat;
|
||||
import androidx.core.view.WindowInsetsCompat;
|
||||
import androidx.core.view.WindowInsetsControllerCompat;
|
||||
import androidx.drawerlayout.widget.DrawerLayout;
|
||||
import androidx.fragment.app.FragmentManager;
|
||||
import androidx.lifecycle.ViewModelProvider;
|
||||
@@ -31,7 +24,6 @@ import androidx.media3.common.Player;
|
||||
import androidx.media3.common.util.UnstableApi;
|
||||
import androidx.navigation.NavController;
|
||||
import androidx.navigation.fragment.NavHostFragment;
|
||||
import androidx.navigation.ui.NavigationUI;
|
||||
|
||||
import com.cappielloantonio.tempo.App;
|
||||
import com.cappielloantonio.tempo.BuildConfig;
|
||||
@@ -39,8 +31,12 @@ import com.cappielloantonio.tempo.R;
|
||||
import com.cappielloantonio.tempo.broadcast.receiver.ConnectivityStatusBroadcastReceiver;
|
||||
import com.cappielloantonio.tempo.databinding.ActivityMainBinding;
|
||||
import com.cappielloantonio.tempo.github.utils.UpdateUtil;
|
||||
import com.cappielloantonio.tempo.navigation.NavigationController;
|
||||
import com.cappielloantonio.tempo.navigation.NavigationHelper;
|
||||
import com.cappielloantonio.tempo.service.MediaManager;
|
||||
import com.cappielloantonio.tempo.ui.activity.base.BaseActivity;
|
||||
import com.cappielloantonio.tempo.ui.controller.BottomSheetController;
|
||||
import com.cappielloantonio.tempo.ui.controller.BottomSheetHelper;
|
||||
import com.cappielloantonio.tempo.ui.dialog.ConnectionAlertDialog;
|
||||
import com.cappielloantonio.tempo.ui.dialog.GithubTempoUpdateDialog;
|
||||
import com.cappielloantonio.tempo.ui.dialog.ServerUnreachableDialog;
|
||||
@@ -70,10 +66,12 @@ public class MainActivity extends BaseActivity {
|
||||
private NavHostFragment navHostFragment;
|
||||
private BottomNavigationView bottomNavigationView;
|
||||
private FrameLayout bottomNavigationViewFrame;
|
||||
public NavController navController;
|
||||
private DrawerLayout drawerLayout;
|
||||
private NavigationView navigationView;
|
||||
private BottomSheetBehavior bottomSheetBehavior;
|
||||
public NavController navController;
|
||||
private NavigationController navigationController;
|
||||
private BottomSheetController bottomSheetController;
|
||||
public BottomSheetBehavior bottomSheetBehavior;
|
||||
public boolean isLandscape = false;
|
||||
private AssetLinkNavigator assetLinkNavigator;
|
||||
private AssetLinkUtil.AssetLink pendingAssetLink;
|
||||
@@ -81,6 +79,10 @@ public class MainActivity extends BaseActivity {
|
||||
ConnectivityStatusBroadcastReceiver connectivityStatusBroadcastReceiver;
|
||||
private Intent pendingDownloadPlaybackIntent;
|
||||
|
||||
public ActivityMainBinding getBinding() {
|
||||
return bind;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
SplashScreen.installSplashScreen(this);
|
||||
@@ -147,7 +149,6 @@ public class MainActivity extends BaseActivity {
|
||||
}
|
||||
|
||||
public void init() {
|
||||
fragmentManager = getSupportFragmentManager();
|
||||
|
||||
initBottomSheet();
|
||||
initNavigation();
|
||||
@@ -162,49 +163,74 @@ public class MainActivity extends BaseActivity {
|
||||
|
||||
}
|
||||
|
||||
// BOTTOM SHEET/NAVIGATION
|
||||
private void initBottomSheet() {
|
||||
bottomSheetBehavior = BottomSheetBehavior.from(findViewById(R.id.player_bottom_sheet));
|
||||
bottomSheetBehavior.addBottomSheetCallback(bottomSheetCallback);
|
||||
fragmentManager.beginTransaction().replace(R.id.player_bottom_sheet, new PlayerBottomSheetFragment(), "PlayerBottomSheet").commit();
|
||||
private void initNavigation() {
|
||||
// We link the nav_graph.xml with our navigationController
|
||||
NavHostFragment navHostFragment = (NavHostFragment) this
|
||||
.getSupportFragmentManager()
|
||||
.findFragmentById(R.id.nav_host_fragment);
|
||||
navController = Objects.requireNonNull(navHostFragment).getNavController();
|
||||
/*
|
||||
navController is currently global since some legacy code still invokes it directly
|
||||
the MainActivity methods that use it must be converted to NavigationHelper methods
|
||||
*/
|
||||
|
||||
checkBottomSheetAfterStateChanged();
|
||||
// Helper
|
||||
NavigationHelper navigationHelper =
|
||||
new NavigationHelper(
|
||||
findViewById(R.id.bottom_navigation),
|
||||
findViewById(R.id.bottom_navigation_frame),
|
||||
findViewById(R.id.drawer_layout),
|
||||
findViewById(R.id.nav_view),
|
||||
navHostFragment
|
||||
);
|
||||
|
||||
// Controller
|
||||
navigationController = new NavigationController(navigationHelper);
|
||||
navigationController.syncWithBottomSheetBehavior(bottomSheetBehavior, navController);
|
||||
}
|
||||
|
||||
private void initBottomSheet() {
|
||||
FragmentManager fragmentManager = getSupportFragmentManager();
|
||||
View bottomSheetView = findViewById(R.id.player_bottom_sheet);
|
||||
bottomSheetBehavior = BottomSheetBehavior.from(bottomSheetView);
|
||||
/*
|
||||
bottomSheetBehavior is currently global since some legacy code still invokes it directly
|
||||
the MainActivity methods that use it must be converted to BottomSheetHelper methods
|
||||
*/
|
||||
|
||||
// Helper
|
||||
BottomSheetHelper bottomSheetHelper =
|
||||
new BottomSheetHelper(
|
||||
bottomSheetBehavior,
|
||||
bottomSheetView,
|
||||
fragmentManager
|
||||
);
|
||||
|
||||
// Controller
|
||||
bottomSheetController = new BottomSheetController(bottomSheetHelper);
|
||||
bottomSheetController.addCallback(bottomSheetCallback);
|
||||
bottomSheetController.replaceFragment(R.id.player_bottom_sheet);
|
||||
bottomSheetController.checkAfterStateChanged(mainViewModel);
|
||||
}
|
||||
|
||||
public void setBottomSheetInPeek(Boolean isVisible) {
|
||||
if (isVisible) {
|
||||
bottomSheetBehavior.setState(BottomSheetBehavior.STATE_COLLAPSED);
|
||||
} else {
|
||||
bottomSheetBehavior.setState(BottomSheetBehavior.STATE_HIDDEN);
|
||||
}
|
||||
bottomSheetController.setStateInPeek(isVisible);
|
||||
}
|
||||
|
||||
public void setBottomSheetVisibility(boolean visibility) {
|
||||
if (visibility) {
|
||||
findViewById(R.id.player_bottom_sheet).setVisibility(View.VISIBLE);
|
||||
} else {
|
||||
findViewById(R.id.player_bottom_sheet).setVisibility(View.GONE);
|
||||
}
|
||||
}
|
||||
|
||||
private void checkBottomSheetAfterStateChanged() {
|
||||
final Handler handler = new Handler();
|
||||
final Runnable runnable = () -> setBottomSheetInPeek(mainViewModel.isQueueLoaded());
|
||||
handler.postDelayed(runnable, 100);
|
||||
bottomSheetController.setVisibility(visibility);
|
||||
}
|
||||
|
||||
public void collapseBottomSheetDelayed() {
|
||||
final Handler handler = new Handler();
|
||||
final Runnable runnable = () -> bottomSheetBehavior.setState(BottomSheetBehavior.STATE_COLLAPSED);
|
||||
handler.postDelayed(runnable, 100);
|
||||
bottomSheetController.collapseDelayed();
|
||||
}
|
||||
|
||||
public void expandBottomSheet() {
|
||||
bottomSheetBehavior.setState(BottomSheetBehavior.STATE_EXPANDED);
|
||||
bottomSheetController.expand();
|
||||
}
|
||||
|
||||
public void setBottomSheetDraggableState(Boolean isDraggable) {
|
||||
bottomSheetBehavior.setDraggable(isDraggable);
|
||||
bottomSheetController.setDraggable(isDraggable);
|
||||
}
|
||||
|
||||
private final BottomSheetBehavior.BottomSheetCallback bottomSheetCallback =
|
||||
@@ -217,7 +243,7 @@ public class MainActivity extends BaseActivity {
|
||||
|
||||
switch (state) {
|
||||
case BottomSheetBehavior.STATE_HIDDEN:
|
||||
resetMusicSession();
|
||||
resetMusicSession(); // I can't put the callback inside BottomSheetHelper because of this line
|
||||
break;
|
||||
case BottomSheetBehavior.STATE_COLLAPSED:
|
||||
if (playerBottomSheetFragment != null)
|
||||
@@ -241,12 +267,7 @@ public class MainActivity extends BaseActivity {
|
||||
};
|
||||
|
||||
private void animateBottomSheet(float slideOffset) {
|
||||
PlayerBottomSheetFragment playerBottomSheetFragment = (PlayerBottomSheetFragment) getSupportFragmentManager().findFragmentByTag("PlayerBottomSheet");
|
||||
if (playerBottomSheetFragment != null) {
|
||||
float condensedSlideOffset = Math.max(0.0f, Math.min(0.2f, slideOffset - 0.2f)) / 0.2f;
|
||||
playerBottomSheetFragment.getPlayerHeader().setAlpha(1 - condensedSlideOffset);
|
||||
playerBottomSheetFragment.getPlayerHeader().setVisibility(condensedSlideOffset > 0.99 ? View.GONE : View.VISIBLE);
|
||||
}
|
||||
bottomSheetController.animate(slideOffset);
|
||||
}
|
||||
|
||||
private void animateBottomNavigation(float slideOffset, int navigationHeight) {
|
||||
@@ -261,113 +282,56 @@ public class MainActivity extends BaseActivity {
|
||||
bind.bottomNavigation.setTranslationY(slideY);
|
||||
}
|
||||
|
||||
private void initNavigation() {
|
||||
bottomNavigationView = findViewById(R.id.bottom_navigation);
|
||||
bottomNavigationViewFrame = findViewById(R.id.bottom_navigation_frame);
|
||||
navHostFragment = (NavHostFragment) fragmentManager.findFragmentById(R.id.nav_host_fragment);
|
||||
navController = Objects.requireNonNull(navHostFragment).getNavController();
|
||||
// This is the lateral slide-in drawer
|
||||
drawerLayout = findViewById(R.id.drawer_layout);
|
||||
navigationView = findViewById(R.id.nav_view);
|
||||
|
||||
/*
|
||||
* In questo modo intercetto il cambio schermata tramite navbar e se il bottom sheet è aperto,
|
||||
* lo chiudo
|
||||
*/
|
||||
navController.addOnDestinationChangedListener((controller, destination, arguments) -> {
|
||||
if (bottomSheetBehavior.getState() == BottomSheetBehavior.STATE_EXPANDED && (
|
||||
destination.getId() == R.id.homeFragment ||
|
||||
destination.getId() == R.id.libraryFragment ||
|
||||
destination.getId() == R.id.downloadFragment)
|
||||
) {
|
||||
bottomSheetBehavior.setState(BottomSheetBehavior.STATE_COLLAPSED);
|
||||
}
|
||||
});
|
||||
|
||||
NavigationUI.setupWithNavController(bottomNavigationView, navController);
|
||||
NavigationUI.setupWithNavController(navigationView, navController);
|
||||
}
|
||||
|
||||
public void setBottomNavigationBarVisibility(boolean visibility) {
|
||||
if (visibility) {
|
||||
bottomNavigationView.setVisibility(View.VISIBLE);
|
||||
bottomNavigationViewFrame.setVisibility(View.VISIBLE);
|
||||
} else {
|
||||
bottomNavigationView.setVisibility(View.GONE);
|
||||
bottomNavigationViewFrame.setVisibility(View.GONE);
|
||||
}
|
||||
navigationController.setNavbarVisibility(visibility);
|
||||
}
|
||||
|
||||
public void toggleBottomNavigationBarVisibilityOnOrientationChange() {
|
||||
float displayDensity = getResources().getDisplayMetrics().density;
|
||||
// Ignore orientation change, bottom navbar always hidden
|
||||
if (Preferences.getHideBottomNavbarOnPortrait()) {
|
||||
setBottomNavigationBarVisibility(false);
|
||||
setPortraitPlayerBottomSheetPeekHeight(56);
|
||||
setSystemBarsVisibility(!isLandscape);
|
||||
navigationController.setNavbarVisibility(false);
|
||||
bottomSheetController.setPeekHeight(56, displayDensity);
|
||||
navigationController.setSystemBarsVisibility(this, !isLandscape);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!isLandscape) {
|
||||
// Show app navbar + show system bars
|
||||
setPortraitPlayerBottomSheetPeekHeight(136);
|
||||
setBottomNavigationBarVisibility(true);
|
||||
setSystemBarsVisibility(true);
|
||||
bottomSheetController.setPeekHeight(136, displayDensity);
|
||||
navigationController.setNavbarVisibility(true);
|
||||
navigationController.setSystemBarsVisibility(this, true);
|
||||
} else {
|
||||
// Hide app navbar + hide system bars
|
||||
setPortraitPlayerBottomSheetPeekHeight(56);
|
||||
setBottomNavigationBarVisibility(false);
|
||||
setSystemBarsVisibility(false);
|
||||
bottomSheetController.setPeekHeight(56, displayDensity);
|
||||
navigationController.setNavbarVisibility(false);
|
||||
navigationController.setSystemBarsVisibility(this, false);
|
||||
}
|
||||
}
|
||||
|
||||
public void setNavigationDrawerLock(boolean locked) {
|
||||
int mode = locked
|
||||
? DrawerLayout.LOCK_MODE_LOCKED_CLOSED
|
||||
: DrawerLayout.LOCK_MODE_UNLOCKED;
|
||||
drawerLayout.setDrawerLockMode(mode);
|
||||
navigationController.setDrawerLock(locked);
|
||||
}
|
||||
|
||||
public boolean isNavigationDrawerLocked() {
|
||||
return navigationController.isNavigationDrawerLocked();
|
||||
}
|
||||
|
||||
public void toggleNavigationDrawerLockOnOrientationChange() {
|
||||
// Ignore orientation check, drawer always unlocked
|
||||
if (Preferences.getEnableDrawerOnPortrait()) {
|
||||
setNavigationDrawerLock(false);
|
||||
return;
|
||||
}
|
||||
if (!isLandscape) {
|
||||
setNavigationDrawerLock(true);
|
||||
} else {
|
||||
setNavigationDrawerLock(false);
|
||||
}
|
||||
navigationController.toggleDrawerLockOnOrientation(this);
|
||||
}
|
||||
|
||||
public void setSystemBarsVisibility(boolean visibility) {
|
||||
WindowInsetsControllerCompat insetsController;
|
||||
View decorView = getWindow().getDecorView();
|
||||
insetsController = new WindowInsetsControllerCompat(getWindow(), decorView);
|
||||
|
||||
if (visibility) {
|
||||
WindowCompat.setDecorFitsSystemWindows(getWindow(), true);
|
||||
insetsController.show(WindowInsetsCompat.Type.navigationBars());
|
||||
insetsController.show(WindowInsetsCompat.Type.statusBars());
|
||||
insetsController.setSystemBarsBehavior(
|
||||
WindowInsetsControllerCompat.BEHAVIOR_DEFAULT);
|
||||
} else {
|
||||
WindowCompat.setDecorFitsSystemWindows(getWindow(), false);
|
||||
insetsController.hide(WindowInsetsCompat.Type.navigationBars());
|
||||
insetsController.hide(WindowInsetsCompat.Type.statusBars());
|
||||
insetsController.setSystemBarsBehavior(
|
||||
WindowInsetsControllerCompat.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE);
|
||||
}
|
||||
navigationController.setSystemBarsVisibility(this, visibility);
|
||||
}
|
||||
|
||||
private void setPortraitPlayerBottomSheetPeekHeight(int peekHeight) {
|
||||
FrameLayout bottomSheet = findViewById(R.id.player_bottom_sheet);
|
||||
BottomSheetBehavior<FrameLayout> behavior =
|
||||
BottomSheetBehavior.from(bottomSheet);
|
||||
|
||||
int newPeekPx = (int) (peekHeight * getResources().getDisplayMetrics().density);
|
||||
behavior.setPeekHeight(newPeekPx);
|
||||
}
|
||||
/*
|
||||
There are only 4 init functions that must exist up to here
|
||||
1. init()
|
||||
2. initNavigation()
|
||||
3. initBottomSheet()
|
||||
4. bottomSheetCallback = new BottomSheetBehavior.BottomSheetCallback() { ... }
|
||||
*/
|
||||
|
||||
private void initService() {
|
||||
MediaManager.check(getMediaBrowserListenableFuture());
|
||||
@@ -403,7 +367,7 @@ public class MainActivity extends BaseActivity {
|
||||
}
|
||||
|
||||
private void goToHome() {
|
||||
bottomNavigationView.setVisibility(View.VISIBLE);
|
||||
setBottomNavigationBarVisibility(true);
|
||||
|
||||
if (Objects.requireNonNull(navController.getCurrentDestination()).getId() == R.id.landingFragment) {
|
||||
navController.navigate(R.id.action_landingFragment_to_homeFragment);
|
||||
@@ -653,4 +617,4 @@ public class MainActivity extends BaseActivity {
|
||||
|
||||
MediaManager.playDownloadedMediaItem(getMediaBrowserListenableFuture(), mediaItem);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@ import com.cappielloantonio.tempo.interfaces.ClickCallback;
|
||||
import com.cappielloantonio.tempo.subsonic.models.AlbumID3;
|
||||
import com.cappielloantonio.tempo.util.Constants;
|
||||
import com.cappielloantonio.tempo.util.MusicUtil;
|
||||
import com.cappielloantonio.tempo.util.TileSizeManager;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
@@ -22,6 +23,8 @@ public class AlbumAdapter extends RecyclerView.Adapter<AlbumAdapter.ViewHolder>
|
||||
|
||||
private List<AlbumID3> albums;
|
||||
|
||||
private int sizePx = 400;
|
||||
|
||||
public AlbumAdapter(ClickCallback click) {
|
||||
this.click = click;
|
||||
this.albums = Collections.emptyList();
|
||||
@@ -31,11 +34,20 @@ public class AlbumAdapter extends RecyclerView.Adapter<AlbumAdapter.ViewHolder>
|
||||
@Override
|
||||
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||
ItemLibraryAlbumBinding view = ItemLibraryAlbumBinding.inflate(LayoutInflater.from(parent.getContext()), parent, false);
|
||||
|
||||
TileSizeManager.getInstance().calculateTileSize(parent.getContext());
|
||||
sizePx = TileSizeManager.getInstance().getTileSizePx(parent.getContext());
|
||||
|
||||
return new ViewHolder(view);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(ViewHolder holder, int position) {
|
||||
ViewGroup.LayoutParams lp = holder.item.albumCoverImageView.getLayoutParams();
|
||||
lp.width = sizePx;
|
||||
lp.height = sizePx;
|
||||
holder.item.albumCoverImageView.setLayoutParams(lp);
|
||||
|
||||
AlbumID3 album = albums.get(position);
|
||||
|
||||
holder.item.albumNameLabel.setText(album.getName());
|
||||
|
||||
@@ -14,6 +14,7 @@ import com.cappielloantonio.tempo.interfaces.ClickCallback;
|
||||
import com.cappielloantonio.tempo.subsonic.models.ArtistID3;
|
||||
import com.cappielloantonio.tempo.util.Constants;
|
||||
import com.cappielloantonio.tempo.util.MusicUtil;
|
||||
import com.cappielloantonio.tempo.util.TileSizeManager;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
@@ -24,6 +25,7 @@ public class ArtistAdapter extends RecyclerView.Adapter<ArtistAdapter.ViewHolder
|
||||
private final boolean mix;
|
||||
private final boolean bestOf;
|
||||
|
||||
private int sizePx = 400;
|
||||
private List<ArtistID3> artists;
|
||||
|
||||
public ArtistAdapter(ClickCallback click, Boolean mix, Boolean bestOf) {
|
||||
@@ -37,11 +39,20 @@ public class ArtistAdapter extends RecyclerView.Adapter<ArtistAdapter.ViewHolder
|
||||
@Override
|
||||
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||
ItemLibraryArtistBinding view = ItemLibraryArtistBinding.inflate(LayoutInflater.from(parent.getContext()), parent, false);
|
||||
|
||||
TileSizeManager.getInstance().calculateTileSize(parent.getContext());
|
||||
sizePx = TileSizeManager.getInstance().getTileSizePx(parent.getContext());
|
||||
|
||||
return new ViewHolder(view);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(ViewHolder holder, int position) {
|
||||
ViewGroup.LayoutParams lp = holder.item.artistCoverImageView.getLayoutParams();
|
||||
lp.width = sizePx;
|
||||
lp.height = sizePx;
|
||||
holder.item.artistCoverImageView.setLayoutParams(lp);
|
||||
|
||||
ArtistID3 artist = artists.get(position);
|
||||
|
||||
holder.item.artistNameLabel.setText(artist.getName());
|
||||
|
||||
@@ -13,6 +13,7 @@ import com.cappielloantonio.tempo.interfaces.ClickCallback;
|
||||
import com.cappielloantonio.tempo.subsonic.models.SimilarArtistID3;
|
||||
import com.cappielloantonio.tempo.util.Constants;
|
||||
import com.cappielloantonio.tempo.util.MusicUtil;
|
||||
import com.cappielloantonio.tempo.util.TileSizeManager;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
@@ -22,6 +23,8 @@ public class ArtistSimilarAdapter extends RecyclerView.Adapter<ArtistSimilarAdap
|
||||
|
||||
private List<SimilarArtistID3> artists;
|
||||
|
||||
private int sizePx = 400;
|
||||
|
||||
public ArtistSimilarAdapter(ClickCallback click) {
|
||||
this.click = click;
|
||||
this.artists = Collections.emptyList();
|
||||
@@ -31,11 +34,20 @@ public class ArtistSimilarAdapter extends RecyclerView.Adapter<ArtistSimilarAdap
|
||||
@Override
|
||||
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||
ItemLibrarySimilarArtistBinding view = ItemLibrarySimilarArtistBinding.inflate(LayoutInflater.from(parent.getContext()), parent, false);
|
||||
|
||||
TileSizeManager.getInstance().calculateTileSize(parent.getContext());
|
||||
sizePx = TileSizeManager.getInstance().getTileSizePx(parent.getContext());
|
||||
|
||||
return new ViewHolder(view);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(ViewHolder holder, int position) {
|
||||
ViewGroup.LayoutParams lp = holder.item.similarArtistCoverImageView.getLayoutParams();
|
||||
lp.width = sizePx;
|
||||
lp.height = sizePx;
|
||||
holder.item.similarArtistCoverImageView.setLayoutParams(lp);
|
||||
|
||||
SimilarArtistID3 artist = artists.get(position);
|
||||
|
||||
holder.item.artistNameLabel.setText(artist.getName());
|
||||
|
||||
@@ -2,6 +2,7 @@ package com.cappielloantonio.tempo.ui.adapter;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.animation.AccelerateDecelerateInterpolator;
|
||||
|
||||
@@ -14,6 +15,7 @@ import com.cappielloantonio.tempo.interfaces.ClickCallback;
|
||||
import com.cappielloantonio.tempo.subsonic.models.Child;
|
||||
import com.cappielloantonio.tempo.util.Constants;
|
||||
import com.cappielloantonio.tempo.util.MusicUtil;
|
||||
import com.cappielloantonio.tempo.util.TileSizeManager;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
@@ -23,6 +25,9 @@ public class DiscoverSongAdapter extends RecyclerView.Adapter<DiscoverSongAdapte
|
||||
|
||||
private List<Child> songs;
|
||||
|
||||
private int widthPx = 800;
|
||||
private int heightPx = 400;
|
||||
|
||||
public DiscoverSongAdapter(ClickCallback click) {
|
||||
this.click = click;
|
||||
this.songs = Collections.emptyList();
|
||||
@@ -32,11 +37,21 @@ public class DiscoverSongAdapter extends RecyclerView.Adapter<DiscoverSongAdapte
|
||||
@Override
|
||||
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||
ItemHomeDiscoverSongBinding view = ItemHomeDiscoverSongBinding.inflate(LayoutInflater.from(parent.getContext()), parent, false);
|
||||
|
||||
TileSizeManager.getInstance().calculateDiscoverSize(parent.getContext());
|
||||
widthPx = TileSizeManager.getInstance().getDiscoverWidthPx(parent.getContext());;
|
||||
heightPx = TileSizeManager.getInstance().getDiscoverHeightPx(parent.getContext());;
|
||||
|
||||
return new ViewHolder(view);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(ViewHolder holder, int position) {
|
||||
ViewGroup.LayoutParams lp = holder.item.discoverSongCoverImageView.getLayoutParams();
|
||||
lp.width = widthPx;
|
||||
lp.height = heightPx;
|
||||
holder.item.discoverSongCoverImageView.setLayoutParams(lp);
|
||||
|
||||
Child song = songs.get(position);
|
||||
|
||||
holder.item.titleDiscoverSongLabel.setText(song.getTitle());
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
package com.cappielloantonio.tempo.ui.controller;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import com.cappielloantonio.tempo.viewmodel.MainViewModel;
|
||||
import com.google.android.material.bottomsheet.BottomSheetBehavior;
|
||||
|
||||
public class BottomSheetController {
|
||||
|
||||
BottomSheetHelper helper;
|
||||
|
||||
public BottomSheetController(@NonNull BottomSheetHelper bottomSheetPlayerHelper) {
|
||||
this.helper = bottomSheetPlayerHelper;
|
||||
}
|
||||
|
||||
public void expand() {
|
||||
helper.setState(BottomSheetBehavior.STATE_EXPANDED);
|
||||
}
|
||||
|
||||
public void hide() {
|
||||
helper.setState(BottomSheetBehavior.STATE_HIDDEN);
|
||||
}
|
||||
|
||||
public void setStateInPeek(boolean isVisible) {
|
||||
helper.setStateInPeek(isVisible);
|
||||
}
|
||||
|
||||
public void setVisibility(boolean visibility) {
|
||||
helper.setVisibility(visibility);
|
||||
}
|
||||
|
||||
public void addCallback(BottomSheetBehavior.BottomSheetCallback callback) {
|
||||
helper.addCallback(callback);
|
||||
}
|
||||
|
||||
public void replaceFragment(int playerBottomSheet) {
|
||||
helper.replaceFragment(playerBottomSheet);
|
||||
}
|
||||
|
||||
public void checkAfterStateChanged(MainViewModel mainViewModel) {
|
||||
helper.checkAfterStateChanged(mainViewModel);
|
||||
}
|
||||
|
||||
public void collapseDelayed() {
|
||||
helper.collapseDelayed();
|
||||
}
|
||||
|
||||
public void setDraggable(Boolean isDraggable) {
|
||||
helper.setDraggable(isDraggable);
|
||||
}
|
||||
|
||||
public int getState() {
|
||||
return helper.getState();
|
||||
}
|
||||
|
||||
public void animate(float slideOffset) {
|
||||
helper.animate(slideOffset);
|
||||
}
|
||||
|
||||
public void setPeekHeight(int peekHeight, float displayDensity) {
|
||||
helper.setPeekHeight(peekHeight, displayDensity);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
package com.cappielloantonio.tempo.ui.controller;
|
||||
|
||||
import android.os.Handler;
|
||||
import android.view.View;
|
||||
import android.widget.FrameLayout;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.fragment.app.FragmentManager;
|
||||
|
||||
import com.cappielloantonio.tempo.R;
|
||||
import com.cappielloantonio.tempo.ui.fragment.PlayerBottomSheetFragment;
|
||||
import com.cappielloantonio.tempo.viewmodel.MainViewModel;
|
||||
import com.google.android.material.bottomsheet.BottomSheetBehavior;
|
||||
|
||||
public class BottomSheetHelper {
|
||||
|
||||
BottomSheetBehavior<View> bottomSheetBehavior;
|
||||
View bottomSheetView;
|
||||
FragmentManager fragmentManager; // Of the entire activity
|
||||
PlayerBottomSheetFragment playerBottomSheetFragment;
|
||||
|
||||
public void setState(int state) {
|
||||
bottomSheetBehavior.setState(state);
|
||||
}
|
||||
|
||||
public BottomSheetHelper(@NonNull BottomSheetBehavior<View> bottomSheetBehavior,
|
||||
@NonNull View bottomSheetView,
|
||||
@NonNull FragmentManager fragmentManager) {
|
||||
this.bottomSheetBehavior = bottomSheetBehavior;
|
||||
this.bottomSheetView = bottomSheetView;
|
||||
this.fragmentManager = fragmentManager;
|
||||
this.playerBottomSheetFragment = new PlayerBottomSheetFragment();
|
||||
}
|
||||
|
||||
public void addCallback(BottomSheetBehavior.BottomSheetCallback callback) {
|
||||
bottomSheetBehavior.addBottomSheetCallback(callback);
|
||||
}
|
||||
|
||||
public void setStateInPeek(boolean isVisible) {
|
||||
if (isVisible) {
|
||||
bottomSheetBehavior.setState(BottomSheetBehavior.STATE_COLLAPSED);
|
||||
} else {
|
||||
bottomSheetBehavior.setState(BottomSheetBehavior.STATE_HIDDEN);
|
||||
}
|
||||
}
|
||||
|
||||
public void setVisibility(boolean visibility) {
|
||||
if (visibility) {
|
||||
bottomSheetView.setVisibility(View.VISIBLE);
|
||||
} else {
|
||||
bottomSheetView.setVisibility(View.GONE);
|
||||
}
|
||||
}
|
||||
|
||||
public void replaceFragment(int playerBottomSheet) {
|
||||
fragmentManager
|
||||
.beginTransaction()
|
||||
.replace(
|
||||
playerBottomSheet,
|
||||
playerBottomSheetFragment,
|
||||
"PlayerBottomSheet")
|
||||
.commit();
|
||||
}
|
||||
|
||||
public void checkAfterStateChanged(MainViewModel mainViewModel) {
|
||||
final Handler handler = new Handler();
|
||||
final Runnable runnable = () -> setStateInPeek(mainViewModel.isQueueLoaded());
|
||||
handler.postDelayed(runnable, 100);
|
||||
}
|
||||
|
||||
public void collapseDelayed() {
|
||||
final Handler handler = new Handler();
|
||||
final Runnable runnable = () -> bottomSheetBehavior.setState(BottomSheetBehavior.STATE_COLLAPSED);
|
||||
handler.postDelayed(runnable, 100);
|
||||
}
|
||||
|
||||
public void setDraggable(Boolean isDraggable) {
|
||||
bottomSheetBehavior.setDraggable((isDraggable));
|
||||
}
|
||||
|
||||
public int getState() {
|
||||
return bottomSheetBehavior.getState();
|
||||
}
|
||||
|
||||
public void animate(float slideOffset) {
|
||||
if (playerBottomSheetFragment != null) {
|
||||
float condensedSlideOffset = Math.max(0.0f, Math.min(0.2f, slideOffset - 0.2f)) / 0.2f;
|
||||
playerBottomSheetFragment.getPlayerHeader().setAlpha(1 - condensedSlideOffset);
|
||||
playerBottomSheetFragment.getPlayerHeader().setVisibility(condensedSlideOffset > 0.99 ? View.GONE : View.VISIBLE);
|
||||
}
|
||||
}
|
||||
|
||||
public void setPeekHeight(int peekHeight, float displayDensity) {
|
||||
int newPeekPx = (int) (peekHeight * displayDensity);
|
||||
bottomSheetBehavior.setPeekHeight(newPeekPx);
|
||||
}
|
||||
}
|
||||
@@ -35,6 +35,7 @@ import com.cappielloantonio.tempo.ui.activity.MainActivity;
|
||||
import com.cappielloantonio.tempo.ui.adapter.AlbumCatalogueAdapter;
|
||||
import com.cappielloantonio.tempo.util.Constants;
|
||||
import com.cappielloantonio.tempo.util.Preferences;
|
||||
import com.cappielloantonio.tempo.util.TileSizeManager;
|
||||
import com.cappielloantonio.tempo.viewmodel.AlbumCatalogueViewModel;
|
||||
|
||||
import java.util.ArrayList;
|
||||
@@ -48,9 +49,9 @@ public class AlbumCatalogueFragment extends Fragment implements ClickCallback {
|
||||
private FragmentAlbumCatalogueBinding bind;
|
||||
private MainActivity activity;
|
||||
private AlbumCatalogueViewModel albumCatalogueViewModel;
|
||||
|
||||
private AlbumCatalogueAdapter albumAdapter;
|
||||
private int spanCount = 2;
|
||||
private int tileSpacing = 20;
|
||||
private AlbumCatalogueAdapter albumAdapter;
|
||||
private String currentSortOrder;
|
||||
private List<com.cappielloantonio.tempo.subsonic.models.AlbumID3> originalAlbums;
|
||||
|
||||
@@ -92,9 +93,9 @@ public class AlbumCatalogueFragment extends Fragment implements ClickCallback {
|
||||
bind = FragmentAlbumCatalogueBinding.inflate(inflater, container, false);
|
||||
View view = bind.getRoot();
|
||||
|
||||
if (getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE) {
|
||||
spanCount = Preferences.getLandscapeItemsPerRow();
|
||||
}
|
||||
TileSizeManager.getInstance().calculateTileSize( requireContext() );
|
||||
spanCount = TileSizeManager.getInstance().getTileSpanCount( requireContext() );
|
||||
tileSpacing = TileSizeManager.getInstance().getTileSpacing( requireContext() );
|
||||
|
||||
initAppBar();
|
||||
initAlbumCatalogueView();
|
||||
@@ -140,7 +141,7 @@ public class AlbumCatalogueFragment extends Fragment implements ClickCallback {
|
||||
@SuppressLint("ClickableViewAccessibility")
|
||||
private void initAlbumCatalogueView() {
|
||||
bind.albumCatalogueRecyclerView.setLayoutManager(new GridLayoutManager(requireContext(), spanCount));
|
||||
bind.albumCatalogueRecyclerView.addItemDecoration(new GridItemDecoration(spanCount, 20, false));
|
||||
bind.albumCatalogueRecyclerView.addItemDecoration(new GridItemDecoration(spanCount, tileSpacing, false));
|
||||
bind.albumCatalogueRecyclerView.setHasFixedSize(true);
|
||||
|
||||
albumAdapter = new AlbumCatalogueAdapter(this, true);
|
||||
|
||||
@@ -261,8 +261,10 @@ public class AlbumPageFragment extends Fragment implements ClickCallback {
|
||||
bind.albumOtherInfoButton.setOnClickListener(v -> {
|
||||
if (bind.albumDetailView.getVisibility() == View.GONE) {
|
||||
bind.albumDetailView.setVisibility(View.VISIBLE);
|
||||
bind.albumNameLabel.setMaxLines(Integer.MAX_VALUE);
|
||||
} else if (bind.albumDetailView.getVisibility() == View.VISIBLE) {
|
||||
bind.albumDetailView.setVisibility(View.GONE);
|
||||
bind.albumNameLabel.setMaxLines(2);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -36,6 +36,7 @@ import com.cappielloantonio.tempo.ui.activity.MainActivity;
|
||||
import com.cappielloantonio.tempo.ui.adapter.ArtistCatalogueAdapter;
|
||||
import com.cappielloantonio.tempo.util.Constants;
|
||||
import com.cappielloantonio.tempo.util.Preferences;
|
||||
import com.cappielloantonio.tempo.util.TileSizeManager;
|
||||
import com.cappielloantonio.tempo.viewmodel.ArtistCatalogueViewModel;
|
||||
import com.cappielloantonio.tempo.subsonic.models.ArtistID3;
|
||||
|
||||
@@ -51,7 +52,9 @@ public class ArtistCatalogueFragment extends Fragment implements ClickCallback {
|
||||
private ArtistCatalogueViewModel artistCatalogueViewModel;
|
||||
|
||||
private ArtistCatalogueAdapter artistAdapter;
|
||||
|
||||
private int spanCount = 2;
|
||||
private int tileSpacing = 20;
|
||||
|
||||
@Override
|
||||
public void onCreate(@Nullable Bundle savedInstanceState) {
|
||||
@@ -68,9 +71,9 @@ public class ArtistCatalogueFragment extends Fragment implements ClickCallback {
|
||||
bind = FragmentArtistCatalogueBinding.inflate(inflater, container, false);
|
||||
View view = bind.getRoot();
|
||||
|
||||
if (getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE) {
|
||||
spanCount = Preferences.getLandscapeItemsPerRow();
|
||||
}
|
||||
TileSizeManager.getInstance().calculateTileSize( requireContext() );
|
||||
spanCount = TileSizeManager.getInstance().getTileSpanCount( requireContext() );
|
||||
tileSpacing = TileSizeManager.getInstance().getTileSpacing( requireContext() );
|
||||
|
||||
initAppBar();
|
||||
initArtistCatalogueView();
|
||||
@@ -115,7 +118,7 @@ public class ArtistCatalogueFragment extends Fragment implements ClickCallback {
|
||||
@SuppressLint("ClickableViewAccessibility")
|
||||
private void initArtistCatalogueView() {
|
||||
bind.artistCatalogueRecyclerView.setLayoutManager(new GridLayoutManager(requireContext(), spanCount));
|
||||
bind.artistCatalogueRecyclerView.addItemDecoration(new GridItemDecoration(spanCount, 20, false));
|
||||
bind.artistCatalogueRecyclerView.addItemDecoration(new GridItemDecoration(spanCount, tileSpacing, false));
|
||||
bind.artistCatalogueRecyclerView.setHasFixedSize(true);
|
||||
|
||||
artistAdapter = new ArtistCatalogueAdapter(this);
|
||||
|
||||
@@ -43,6 +43,7 @@ import com.cappielloantonio.tempo.ui.adapter.SongHorizontalAdapter;
|
||||
import com.cappielloantonio.tempo.util.Constants;
|
||||
import com.cappielloantonio.tempo.util.MusicUtil;
|
||||
import com.cappielloantonio.tempo.util.Preferences;
|
||||
import com.cappielloantonio.tempo.util.TileSizeManager;
|
||||
import com.cappielloantonio.tempo.viewmodel.ArtistPageViewModel;
|
||||
import com.cappielloantonio.tempo.viewmodel.PlaybackViewModel;
|
||||
import com.google.common.util.concurrent.ListenableFuture;
|
||||
@@ -65,6 +66,7 @@ public class ArtistPageFragment extends Fragment implements ClickCallback {
|
||||
private ListenableFuture<MediaBrowser> mediaBrowserListenableFuture;
|
||||
|
||||
private int spanCount = 2;
|
||||
private int tileSpacing = 20;
|
||||
|
||||
@Override
|
||||
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||
@@ -75,9 +77,9 @@ public class ArtistPageFragment extends Fragment implements ClickCallback {
|
||||
artistPageViewModel = new ViewModelProvider(requireActivity()).get(ArtistPageViewModel.class);
|
||||
playbackViewModel = new ViewModelProvider(requireActivity()).get(PlaybackViewModel.class);
|
||||
|
||||
if (getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE) {
|
||||
spanCount = Preferences.getLandscapeItemsPerRow();
|
||||
}
|
||||
TileSizeManager.getInstance().calculateTileSize( requireContext() );
|
||||
spanCount = TileSizeManager.getInstance().getTileSpanCount( requireContext() );
|
||||
tileSpacing = TileSizeManager.getInstance().getTileSpacing( requireContext() );
|
||||
|
||||
init(view);
|
||||
initAppBar();
|
||||
@@ -285,7 +287,7 @@ public class ArtistPageFragment extends Fragment implements ClickCallback {
|
||||
|
||||
private void initAlbumsView() {
|
||||
bind.albumsRecyclerView.setLayoutManager(new GridLayoutManager(requireContext(), spanCount));
|
||||
bind.albumsRecyclerView.addItemDecoration(new GridItemDecoration(spanCount, 20, false));
|
||||
bind.albumsRecyclerView.addItemDecoration(new GridItemDecoration(spanCount, tileSpacing, false));
|
||||
bind.albumsRecyclerView.setHasFixedSize(true);
|
||||
|
||||
albumCatalogueAdapter = new AlbumCatalogueAdapter(this, false);
|
||||
@@ -304,7 +306,7 @@ public class ArtistPageFragment extends Fragment implements ClickCallback {
|
||||
|
||||
private void initSimilarArtistsView() {
|
||||
bind.similarArtistsRecyclerView.setLayoutManager(new GridLayoutManager(requireContext(), spanCount));
|
||||
bind.similarArtistsRecyclerView.addItemDecoration(new GridItemDecoration(spanCount, 20, false));
|
||||
bind.similarArtistsRecyclerView.addItemDecoration(new GridItemDecoration(spanCount, tileSpacing, false));
|
||||
bind.similarArtistsRecyclerView.setHasFixedSize(true);
|
||||
|
||||
artistCatalogueAdapter = new ArtistCatalogueAdapter(this);
|
||||
|
||||
@@ -34,6 +34,7 @@ import com.cappielloantonio.tempo.ui.activity.MainActivity;
|
||||
import com.cappielloantonio.tempo.ui.adapter.GenreCatalogueAdapter;
|
||||
import com.cappielloantonio.tempo.util.Constants;
|
||||
import com.cappielloantonio.tempo.util.Preferences;
|
||||
import com.cappielloantonio.tempo.util.TileSizeManager;
|
||||
import com.cappielloantonio.tempo.viewmodel.GenreCatalogueViewModel;
|
||||
|
||||
@OptIn(markerClass = UnstableApi.class)
|
||||
@@ -43,7 +44,9 @@ public class GenreCatalogueFragment extends Fragment implements ClickCallback {
|
||||
private GenreCatalogueViewModel genreCatalogueViewModel;
|
||||
|
||||
private GenreCatalogueAdapter genreCatalogueAdapter;
|
||||
|
||||
private int spanCount = 2;
|
||||
private int tileSpacing = 20;
|
||||
|
||||
@Override
|
||||
public void onCreate(@Nullable Bundle savedInstanceState) {
|
||||
@@ -59,9 +62,9 @@ public class GenreCatalogueFragment extends Fragment implements ClickCallback {
|
||||
View view = bind.getRoot();
|
||||
genreCatalogueViewModel = new ViewModelProvider(requireActivity()).get(GenreCatalogueViewModel.class);
|
||||
|
||||
if (getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE) {
|
||||
spanCount = Preferences.getLandscapeItemsPerRow();
|
||||
}
|
||||
TileSizeManager.getInstance().calculateGenreSize( requireContext() );
|
||||
spanCount = TileSizeManager.getInstance().getGenreSpanCount( requireContext() );
|
||||
tileSpacing = TileSizeManager.getInstance().getGenreSpacing( requireContext() );
|
||||
|
||||
init();
|
||||
initAppBar();
|
||||
@@ -105,7 +108,7 @@ public class GenreCatalogueFragment extends Fragment implements ClickCallback {
|
||||
@SuppressLint("ClickableViewAccessibility")
|
||||
private void initGenreCatalogueView() {
|
||||
bind.genreCatalogueRecyclerView.setLayoutManager(new GridLayoutManager(requireContext(), spanCount));
|
||||
bind.genreCatalogueRecyclerView.addItemDecoration(new GridItemDecoration(spanCount, 16, false));
|
||||
bind.genreCatalogueRecyclerView.addItemDecoration(new GridItemDecoration(spanCount, tileSpacing, false));
|
||||
bind.genreCatalogueRecyclerView.setHasFixedSize(true);
|
||||
|
||||
genreCatalogueAdapter = new GenreCatalogueAdapter(this);
|
||||
|
||||
@@ -64,6 +64,7 @@ import com.cappielloantonio.tempo.util.ExternalAudioWriter;
|
||||
import com.cappielloantonio.tempo.util.MappingUtil;
|
||||
import com.cappielloantonio.tempo.util.MusicUtil;
|
||||
import com.cappielloantonio.tempo.util.Preferences;
|
||||
import com.cappielloantonio.tempo.util.TileSizeManager;
|
||||
import com.cappielloantonio.tempo.util.UIUtil;
|
||||
import com.cappielloantonio.tempo.viewmodel.HomeViewModel;
|
||||
import com.cappielloantonio.tempo.viewmodel.PlaybackViewModel;
|
||||
@@ -682,11 +683,12 @@ public class HomeTabMusicFragment extends Fragment implements ClickCallback {
|
||||
private void initDiscoverSongSlideView() {
|
||||
if (homeViewModel.checkHomeSectorVisibility(Constants.HOME_SECTOR_DISCOVERY)) return;
|
||||
|
||||
bind.discoverSongViewPager.setOrientation(ViewPager2.ORIENTATION_HORIZONTAL);
|
||||
bind.discoverSongRecyclerView.setLayoutManager(new LinearLayoutManager(requireContext(), LinearLayoutManager.HORIZONTAL, false));
|
||||
bind.discoverSongRecyclerView.setHasFixedSize(true);
|
||||
|
||||
discoverSongAdapter = new DiscoverSongAdapter(this);
|
||||
bind.discoverSongViewPager.setAdapter(discoverSongAdapter);
|
||||
bind.discoverSongViewPager.setOffscreenPageLimit(1);
|
||||
bind.discoverSongRecyclerView.setAdapter(discoverSongAdapter);
|
||||
|
||||
homeViewModel.getDiscoverSongSample(getViewLifecycleOwner()).observe(getViewLifecycleOwner(), songs -> {
|
||||
MusicUtil.ratingFilter(songs);
|
||||
|
||||
@@ -699,8 +701,6 @@ public class HomeTabMusicFragment extends Fragment implements ClickCallback {
|
||||
discoverSongAdapter.setItems(songs);
|
||||
}
|
||||
});
|
||||
|
||||
setSlideViewOffset(bind.discoverSongViewPager, 20, 16);
|
||||
}
|
||||
|
||||
private void initSimilarSongView() {
|
||||
|
||||
@@ -110,6 +110,11 @@ public class LibraryFragment extends Fragment implements ClickCallback {
|
||||
}
|
||||
|
||||
private void init() {
|
||||
bind.songCatalogueTextViewClickable.setOnClickListener(v -> {
|
||||
Bundle bundle = new Bundle();
|
||||
bundle.putString(Constants.MEDIA_ALL, Constants.MEDIA_ALL);
|
||||
activity.navController.navigate(R.id.action_libraryFragment_to_songListPageFragment, bundle);
|
||||
});
|
||||
bind.albumCatalogueTextViewClickable.setOnClickListener(v -> activity.navController.navigate(R.id.action_libraryFragment_to_albumCatalogueFragment));
|
||||
bind.artistCatalogueTextViewClickable.setOnClickListener(v -> activity.navController.navigate(R.id.action_libraryFragment_to_artistCatalogueFragment));
|
||||
bind.genreCatalogueTextViewClickable.setOnClickListener(v -> activity.navController.navigate(R.id.action_libraryFragment_to_genreCatalogueFragment));
|
||||
|
||||
@@ -26,16 +26,20 @@ import com.cappielloantonio.tempo.helper.recyclerview.CustomLinearSnapHelper;
|
||||
import com.cappielloantonio.tempo.interfaces.ClickCallback;
|
||||
import com.cappielloantonio.tempo.service.MediaManager;
|
||||
import com.cappielloantonio.tempo.service.MediaService;
|
||||
import com.cappielloantonio.tempo.subsonic.models.Playlist;
|
||||
import com.cappielloantonio.tempo.ui.activity.MainActivity;
|
||||
import com.cappielloantonio.tempo.ui.adapter.AlbumAdapter;
|
||||
import com.cappielloantonio.tempo.ui.adapter.ArtistAdapter;
|
||||
import com.cappielloantonio.tempo.ui.adapter.SongHorizontalAdapter;
|
||||
import com.cappielloantonio.tempo.ui.adapter.PlaylistHorizontalAdapter;
|
||||
import com.cappielloantonio.tempo.util.Constants;
|
||||
import com.cappielloantonio.tempo.viewmodel.PlaybackViewModel;
|
||||
import com.cappielloantonio.tempo.viewmodel.SearchViewModel;
|
||||
import com.cappielloantonio.tempo.subsonic.models.PlaylistWithSongs;
|
||||
import com.google.common.util.concurrent.ListenableFuture;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
@UnstableApi
|
||||
public class SearchFragment extends Fragment implements ClickCallback {
|
||||
@@ -49,6 +53,7 @@ public class SearchFragment extends Fragment implements ClickCallback {
|
||||
private ArtistAdapter artistAdapter;
|
||||
private AlbumAdapter albumAdapter;
|
||||
private SongHorizontalAdapter songHorizontalAdapter;
|
||||
private PlaylistHorizontalAdapter playlistHorizontalAdapter;
|
||||
|
||||
private ListenableFuture<MediaBrowser> mediaBrowserListenableFuture;
|
||||
|
||||
@@ -126,6 +131,12 @@ public class SearchFragment extends Fragment implements ClickCallback {
|
||||
reapplyPlayback();
|
||||
|
||||
bind.searchResultTracksRecyclerView.setAdapter(songHorizontalAdapter);
|
||||
|
||||
bind.allsongsview.setLayoutManager(new LinearLayoutManager(requireContext()));
|
||||
bind.allsongsview.setHasFixedSize(true);
|
||||
|
||||
playlistHorizontalAdapter = new PlaylistHorizontalAdapter(this);
|
||||
bind.allsongsview.setAdapter(playlistHorizontalAdapter);
|
||||
}
|
||||
|
||||
private void initSearchView() {
|
||||
@@ -216,13 +227,23 @@ public class SearchFragment extends Fragment implements ClickCallback {
|
||||
|
||||
public void search(String query) {
|
||||
searchViewModel.setQuery(query);
|
||||
bind.allSongs.setText(this.getView().getContext().getString(R.string.search_all_songs_loading));
|
||||
playlistHorizontalAdapter.setItems(Collections.emptyList());
|
||||
bind.searchBar.setText(query);
|
||||
bind.searchView.hide();
|
||||
performSearch(query);
|
||||
}
|
||||
|
||||
public void updateUI(List<Playlist> allSongs) {
|
||||
if (!allSongs.isEmpty()) {
|
||||
playlistHorizontalAdapter.setItems(allSongs);
|
||||
} else {
|
||||
playlistHorizontalAdapter.setItems(Collections.emptyList());
|
||||
}
|
||||
bind.allSongs.setText(this.getView().getContext().getString(R.string.search_all_songs_play,String.valueOf(allSongs.getFirst().getName())));
|
||||
}
|
||||
private void performSearch(String query) {
|
||||
searchViewModel.search3(query).observe(getViewLifecycleOwner(), result -> {
|
||||
searchViewModel.search3(this, query).observe(getViewLifecycleOwner(), result -> {
|
||||
if (bind != null) {
|
||||
if (result.getArtists() != null) {
|
||||
bind.searchArtistSector.setVisibility(!result.getArtists().isEmpty() ? View.VISIBLE : View.GONE);
|
||||
@@ -281,6 +302,19 @@ public class SearchFragment extends Fragment implements ClickCallback {
|
||||
Navigation.findNavController(requireView()).navigate(R.id.songBottomSheetDialog, bundle);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPlaylistClick(Bundle bundle) {
|
||||
PlaylistWithSongs playlistWithSongs = bundle.getParcelable(Constants.PLAYLIST_OBJECT);
|
||||
if (playlistWithSongs != null) {
|
||||
MediaManager.startQueue(mediaBrowserListenableFuture, playlistWithSongs.getEntries(), 0);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPlaylistLongClick(Bundle bundle) {
|
||||
Navigation.findNavController(requireView()).navigate(R.id.playlistBottomSheetDialog, bundle);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAlbumClick(Bundle bundle) {
|
||||
Navigation.findNavController(requireView()).navigate(R.id.albumPageFragment, bundle);
|
||||
|
||||
@@ -0,0 +1,603 @@
|
||||
package com.cappielloantonio.tempo.ui.fragment;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.ServiceConnection;
|
||||
import android.media.audiofx.AudioEffect;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.os.IBinder;
|
||||
import android.text.InputFilter;
|
||||
import android.text.InputType;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.WindowManager;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.activity.result.ActivityResultLauncher;
|
||||
import androidx.activity.result.contract.ActivityResultContracts;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.OptIn;
|
||||
import androidx.appcompat.app.AppCompatDelegate;
|
||||
import androidx.core.os.LocaleListCompat;
|
||||
import androidx.lifecycle.ViewModelProvider;
|
||||
import androidx.media3.common.util.UnstableApi;
|
||||
import androidx.navigation.NavController;
|
||||
import androidx.navigation.NavOptions;
|
||||
import androidx.navigation.fragment.NavHostFragment;
|
||||
import androidx.preference.EditTextPreference;
|
||||
import androidx.preference.ListPreference;
|
||||
import androidx.preference.Preference;
|
||||
import androidx.preference.PreferenceCategory;
|
||||
import androidx.preference.PreferenceFragmentCompat;
|
||||
import androidx.preference.SwitchPreference;
|
||||
|
||||
import com.cappielloantonio.tempo.BuildConfig;
|
||||
import com.cappielloantonio.tempo.R;
|
||||
import com.cappielloantonio.tempo.helper.ThemeHelper;
|
||||
import com.cappielloantonio.tempo.interfaces.DialogClickCallback;
|
||||
import com.cappielloantonio.tempo.interfaces.ScanCallback;
|
||||
import com.cappielloantonio.tempo.service.EqualizerManager;
|
||||
import com.cappielloantonio.tempo.service.MediaService;
|
||||
import com.cappielloantonio.tempo.ui.activity.MainActivity;
|
||||
import com.cappielloantonio.tempo.ui.dialog.DeleteDownloadStorageDialog;
|
||||
import com.cappielloantonio.tempo.ui.dialog.DownloadStorageDialog;
|
||||
import com.cappielloantonio.tempo.ui.dialog.StarredAlbumSyncDialog;
|
||||
import com.cappielloantonio.tempo.ui.dialog.StarredArtistSyncDialog;
|
||||
import com.cappielloantonio.tempo.ui.dialog.StarredSyncDialog;
|
||||
import com.cappielloantonio.tempo.ui.dialog.StreamingCacheStorageDialog;
|
||||
import com.cappielloantonio.tempo.util.DownloadUtil;
|
||||
import com.cappielloantonio.tempo.util.ExternalAudioReader;
|
||||
import com.cappielloantonio.tempo.util.Preferences;
|
||||
import com.cappielloantonio.tempo.util.UIUtil;
|
||||
import com.cappielloantonio.tempo.viewmodel.SettingViewModel;
|
||||
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
|
||||
@OptIn(markerClass = UnstableApi.class)
|
||||
public class SettingsContainerFragment extends PreferenceFragmentCompat {
|
||||
|
||||
private static final String TAG = "SettingsFragment";
|
||||
private MainActivity activity;
|
||||
|
||||
private SettingViewModel settingViewModel;
|
||||
|
||||
private ActivityResultLauncher<Intent> directoryPickerLauncher;
|
||||
|
||||
private MediaService.LocalBinder mediaServiceBinder;
|
||||
private boolean isServiceBound = false;
|
||||
private ActivityResultLauncher<Intent> equalizerResultLauncher;
|
||||
|
||||
@Override
|
||||
public void onCreate(@Nullable Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
equalizerResultLauncher = registerForActivityResult(
|
||||
new ActivityResultContracts.StartActivityForResult(),
|
||||
result -> {}
|
||||
);
|
||||
|
||||
if (!BuildConfig.FLAVOR.equals("tempus")) {
|
||||
PreferenceCategory githubUpdateCategory = findPreference("settings_github_update_category_key");
|
||||
if (githubUpdateCategory != null) {
|
||||
getPreferenceScreen().removePreference(githubUpdateCategory);
|
||||
}
|
||||
}
|
||||
|
||||
directoryPickerLauncher = registerForActivityResult(
|
||||
new ActivityResultContracts.StartActivityForResult(),
|
||||
result -> {
|
||||
if (result.getResultCode() == Activity.RESULT_OK) {
|
||||
Intent data = result.getData();
|
||||
if (data != null) {
|
||||
Uri uri = data.getData();
|
||||
if (uri != null) {
|
||||
requireContext().getContentResolver().takePersistableUriPermission(
|
||||
uri,
|
||||
Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION
|
||||
);
|
||||
|
||||
Preferences.setDownloadDirectoryUri(uri.toString());
|
||||
ExternalAudioReader.refreshCache();
|
||||
Toast.makeText(requireContext(), R.string.settings_download_folder_set, Toast.LENGTH_SHORT).show();
|
||||
checkDownloadDirectory();
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||
activity = (MainActivity) getActivity();
|
||||
|
||||
View view = super.onCreateView(inflater, container, savedInstanceState);
|
||||
settingViewModel = new ViewModelProvider(requireActivity()).get(SettingViewModel.class);
|
||||
|
||||
if (view != null) {
|
||||
getListView().setPadding(0, 0, 0, (int) getResources().getDimension(R.dimen.global_padding_bottom));
|
||||
}
|
||||
|
||||
return view;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStart() {
|
||||
super.onStart();
|
||||
activity.setBottomNavigationBarVisibility(false);
|
||||
activity.setBottomSheetVisibility(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
|
||||
checkSystemEqualizer();
|
||||
checkCacheStorage();
|
||||
checkStorage();
|
||||
checkDownloadDirectory();
|
||||
|
||||
setStreamingCacheSize();
|
||||
setAppLanguage();
|
||||
setVersion();
|
||||
setNetorkPingTimeoutBase();
|
||||
|
||||
actionLogout();
|
||||
actionScan();
|
||||
actionSyncStarredAlbums();
|
||||
actionSyncStarredTracks();
|
||||
actionSyncStarredArtists();
|
||||
actionChangeStreamingCacheStorage();
|
||||
actionChangeDownloadStorage();
|
||||
actionSetDownloadDirectory();
|
||||
actionDeleteDownloadStorage();
|
||||
actionKeepScreenOn();
|
||||
actionAutoDownloadLyrics();
|
||||
actionMiniPlayerHeart();
|
||||
|
||||
bindMediaService();
|
||||
actionAppEqualizer();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
|
||||
setPreferencesFromResource(R.xml.global_preferences, rootKey);
|
||||
ListPreference themePreference = findPreference(Preferences.THEME);
|
||||
if (themePreference != null) {
|
||||
themePreference.setOnPreferenceChangeListener(
|
||||
(preference, newValue) -> {
|
||||
String themeOption = (String) newValue;
|
||||
ThemeHelper.applyTheme(themeOption);
|
||||
return true;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private void checkSystemEqualizer() {
|
||||
Preference equalizer = findPreference("system_equalizer");
|
||||
|
||||
if (equalizer == null) return;
|
||||
|
||||
Intent intent = new Intent(AudioEffect.ACTION_DISPLAY_AUDIO_EFFECT_CONTROL_PANEL);
|
||||
|
||||
if ((intent.resolveActivity(requireActivity().getPackageManager()) != null)) {
|
||||
equalizer.setOnPreferenceClickListener(preference -> {
|
||||
equalizerResultLauncher.launch(intent);
|
||||
return true;
|
||||
});
|
||||
} else {
|
||||
equalizer.setVisible(false);
|
||||
}
|
||||
}
|
||||
|
||||
private void checkCacheStorage() {
|
||||
Preference storage = findPreference("streaming_cache_storage");
|
||||
|
||||
if (storage == null) return;
|
||||
|
||||
try {
|
||||
if (requireContext().getExternalFilesDirs(null)[1] == null) {
|
||||
storage.setVisible(false);
|
||||
} else {
|
||||
storage.setSummary(Preferences.getStreamingCacheStoragePreference() == 0 ? R.string.download_storage_internal_dialog_negative_button : R.string.download_storage_external_dialog_positive_button);
|
||||
}
|
||||
} catch (Exception exception) {
|
||||
storage.setVisible(false);
|
||||
}
|
||||
}
|
||||
|
||||
private void checkStorage() {
|
||||
Preference storage = findPreference("download_storage");
|
||||
|
||||
if (storage == null) return;
|
||||
|
||||
try {
|
||||
if (requireContext().getExternalFilesDirs(null)[1] == null) {
|
||||
storage.setVisible(false);
|
||||
} else {
|
||||
int pref = Preferences.getDownloadStoragePreference();
|
||||
if (pref == 0) {
|
||||
storage.setSummary(R.string.download_storage_internal_dialog_negative_button);
|
||||
} else if (pref == 1) {
|
||||
storage.setSummary(R.string.download_storage_external_dialog_positive_button);
|
||||
} else {
|
||||
storage.setSummary(R.string.download_storage_directory_dialog_neutral_button);
|
||||
}
|
||||
}
|
||||
} catch (Exception exception) {
|
||||
storage.setVisible(false);
|
||||
}
|
||||
}
|
||||
|
||||
private void checkDownloadDirectory() {
|
||||
Preference storage = findPreference("download_storage");
|
||||
Preference directory = findPreference("set_download_directory");
|
||||
|
||||
if (directory == null) return;
|
||||
|
||||
String current = Preferences.getDownloadDirectoryUri();
|
||||
if (current != null) {
|
||||
if (storage != null) storage.setVisible(false);
|
||||
directory.setVisible(true);
|
||||
directory.setIcon(R.drawable.ic_close);
|
||||
directory.setTitle(R.string.settings_clear_download_folder);
|
||||
directory.setSummary(current);
|
||||
} else {
|
||||
if (storage != null) storage.setVisible(true);
|
||||
if (Preferences.getDownloadStoragePreference() == 2) {
|
||||
directory.setVisible(true);
|
||||
directory.setIcon(R.drawable.ic_folder);
|
||||
directory.setTitle(R.string.settings_set_download_folder);
|
||||
directory.setSummary(R.string.settings_choose_download_folder);
|
||||
} else {
|
||||
directory.setVisible(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void setNetorkPingTimeoutBase() {
|
||||
EditTextPreference networkPingTimeoutBase = findPreference("network_ping_timeout_base");
|
||||
|
||||
if (networkPingTimeoutBase != null) {
|
||||
networkPingTimeoutBase.setSummaryProvider(EditTextPreference.SimpleSummaryProvider.getInstance());
|
||||
networkPingTimeoutBase.setOnBindEditTextListener(editText -> {
|
||||
editText.setInputType(InputType.TYPE_CLASS_NUMBER);
|
||||
editText.setFilters(new InputFilter[]{ (source, start, end, dest, dstart, dend) -> {
|
||||
for (int i = start; i < end; i++) {
|
||||
if (!Character.isDigit(source.charAt(i))) {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}});
|
||||
});
|
||||
|
||||
networkPingTimeoutBase.setOnPreferenceChangeListener((preference, newValue) -> {
|
||||
String input = (String) newValue;
|
||||
return input != null && !input.isEmpty();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private void setStreamingCacheSize() {
|
||||
ListPreference streamingCachePreference = findPreference("streaming_cache_size");
|
||||
|
||||
if (streamingCachePreference != null) {
|
||||
streamingCachePreference.setSummaryProvider(new Preference.SummaryProvider<ListPreference>() {
|
||||
@Nullable
|
||||
@Override
|
||||
public CharSequence provideSummary(@NonNull ListPreference preference) {
|
||||
CharSequence entry = preference.getEntry();
|
||||
|
||||
if (entry == null) return null;
|
||||
|
||||
long currentSizeMb = DownloadUtil.getStreamingCacheSize(requireActivity()) / (1024 * 1024);
|
||||
|
||||
return getString(R.string.settings_summary_streaming_cache_size, entry, String.valueOf(currentSizeMb));
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private void setAppLanguage() {
|
||||
ListPreference localePref = (ListPreference) findPreference("language");
|
||||
|
||||
Map<String, String> locales = UIUtil.getLangPreferenceDropdownEntries(requireContext());
|
||||
|
||||
CharSequence[] entries = locales.keySet().toArray(new CharSequence[locales.size()]);
|
||||
CharSequence[] entryValues = locales.values().toArray(new CharSequence[locales.size()]);
|
||||
|
||||
localePref.setEntries(entries);
|
||||
localePref.setEntryValues(entryValues);
|
||||
|
||||
String value = localePref.getValue();
|
||||
if ("default".equals(value)) {
|
||||
localePref.setSummary(requireContext().getString(R.string.settings_system_language));
|
||||
} else {
|
||||
localePref.setSummary(Locale.forLanguageTag(value).getDisplayName());
|
||||
}
|
||||
|
||||
localePref.setOnPreferenceChangeListener((preference, newValue) -> {
|
||||
if ("default".equals(newValue)) {
|
||||
AppCompatDelegate.setApplicationLocales(LocaleListCompat.getEmptyLocaleList());
|
||||
preference.setSummary(requireContext().getString(R.string.settings_system_language));
|
||||
} else {
|
||||
LocaleListCompat appLocale = LocaleListCompat.forLanguageTags((String) newValue);
|
||||
AppCompatDelegate.setApplicationLocales(appLocale);
|
||||
preference.setSummary(Locale.forLanguageTag((String) newValue).getDisplayName());
|
||||
}
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
private void setVersion() {
|
||||
findPreference("version").setSummary(BuildConfig.VERSION_NAME);
|
||||
}
|
||||
|
||||
private void actionLogout() {
|
||||
findPreference("logout").setOnPreferenceClickListener(preference -> {
|
||||
activity.quit();
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
private void actionScan() {
|
||||
findPreference("scan_library").setOnPreferenceClickListener(preference -> {
|
||||
settingViewModel.launchScan(new ScanCallback() {
|
||||
@Override
|
||||
public void onError(Exception exception) {
|
||||
findPreference("scan_library").setSummary(exception.getMessage());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSuccess(boolean isScanning, long count) {
|
||||
findPreference("scan_library").setSummary(getString(R.string.settings_scan_result, count));
|
||||
if (isScanning) getScanStatus();
|
||||
}
|
||||
});
|
||||
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
private void actionSyncStarredTracks() {
|
||||
findPreference("sync_starred_tracks_for_offline_use").setOnPreferenceChangeListener((preference, newValue) -> {
|
||||
if (newValue instanceof Boolean) {
|
||||
if ((Boolean) newValue) {
|
||||
StarredSyncDialog dialog = new StarredSyncDialog(() -> {
|
||||
((SwitchPreference)preference).setChecked(false);
|
||||
});
|
||||
dialog.show(activity.getSupportFragmentManager(), null);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
private void actionSyncStarredAlbums() {
|
||||
findPreference("sync_starred_albums_for_offline_use").setOnPreferenceChangeListener((preference, newValue) -> {
|
||||
if (newValue instanceof Boolean) {
|
||||
if ((Boolean) newValue) {
|
||||
StarredAlbumSyncDialog dialog = new StarredAlbumSyncDialog(() -> {
|
||||
((SwitchPreference)preference).setChecked(false);
|
||||
});
|
||||
dialog.show(activity.getSupportFragmentManager(), null);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
private void actionSyncStarredArtists() {
|
||||
findPreference("sync_starred_artists_for_offline_use").setOnPreferenceChangeListener((preference, newValue) -> {
|
||||
if (newValue instanceof Boolean) {
|
||||
if ((Boolean) newValue) {
|
||||
StarredArtistSyncDialog dialog = new StarredArtistSyncDialog(() -> {
|
||||
((SwitchPreference)preference).setChecked(false);
|
||||
});
|
||||
dialog.show(activity.getSupportFragmentManager(), null);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
private void actionChangeStreamingCacheStorage() {
|
||||
findPreference("streaming_cache_storage").setOnPreferenceClickListener(preference -> {
|
||||
StreamingCacheStorageDialog dialog = new StreamingCacheStorageDialog(new DialogClickCallback() {
|
||||
@Override
|
||||
public void onPositiveClick() {
|
||||
findPreference("streaming_cache_storage").setSummary(R.string.streaming_cache_storage_external_dialog_positive_button);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNegativeClick() {
|
||||
findPreference("streaming_cache_storage").setSummary(R.string.streaming_cache_storage_internal_dialog_negative_button);
|
||||
}
|
||||
});
|
||||
dialog.show(activity.getSupportFragmentManager(), null);
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
private void actionChangeDownloadStorage() {
|
||||
findPreference("download_storage").setOnPreferenceClickListener(preference -> {
|
||||
DownloadStorageDialog dialog = new DownloadStorageDialog(new DialogClickCallback() {
|
||||
@Override
|
||||
public void onPositiveClick() {
|
||||
findPreference("download_storage").setSummary(R.string.download_storage_external_dialog_positive_button);
|
||||
checkDownloadDirectory();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNegativeClick() {
|
||||
findPreference("download_storage").setSummary(R.string.download_storage_internal_dialog_negative_button);
|
||||
checkDownloadDirectory();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNeutralClick() {
|
||||
findPreference("download_storage").setSummary(R.string.download_storage_directory_dialog_neutral_button);
|
||||
checkDownloadDirectory();
|
||||
}
|
||||
});
|
||||
dialog.show(activity.getSupportFragmentManager(), null);
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
private void actionSetDownloadDirectory() {
|
||||
Preference pref = findPreference("set_download_directory");
|
||||
if (pref != null) {
|
||||
pref.setOnPreferenceClickListener(preference -> {
|
||||
String current = Preferences.getDownloadDirectoryUri();
|
||||
|
||||
if (current != null) {
|
||||
Preferences.setDownloadDirectoryUri(null);
|
||||
Preferences.setDownloadStoragePreference(0);
|
||||
ExternalAudioReader.refreshCache();
|
||||
Toast.makeText(requireContext(), R.string.settings_download_folder_cleared, Toast.LENGTH_SHORT).show();
|
||||
checkStorage();
|
||||
checkDownloadDirectory();
|
||||
} else {
|
||||
Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE);
|
||||
intent.addFlags(Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION
|
||||
| Intent.FLAG_GRANT_READ_URI_PERMISSION
|
||||
| Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
|
||||
directoryPickerLauncher.launch(intent);
|
||||
}
|
||||
return true;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private void actionDeleteDownloadStorage() {
|
||||
findPreference("delete_download_storage").setOnPreferenceClickListener(preference -> {
|
||||
DeleteDownloadStorageDialog dialog = new DeleteDownloadStorageDialog();
|
||||
dialog.show(activity.getSupportFragmentManager(), null);
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
private void actionMiniPlayerHeart() {
|
||||
SwitchPreference preference = findPreference("mini_shuffle_button_visibility");
|
||||
if (preference == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
preference.setChecked(Preferences.showShuffleInsteadOfHeart());
|
||||
preference.setOnPreferenceChangeListener((pref, newValue) -> {
|
||||
if (newValue instanceof Boolean) {
|
||||
Preferences.setShuffleInsteadOfHeart((Boolean) newValue);
|
||||
}
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
private void actionAutoDownloadLyrics() {
|
||||
SwitchPreference preference = findPreference("auto_download_lyrics");
|
||||
if (preference == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
preference.setChecked(Preferences.isAutoDownloadLyricsEnabled());
|
||||
preference.setOnPreferenceChangeListener((pref, newValue) -> {
|
||||
if (newValue instanceof Boolean) {
|
||||
Preferences.setAutoDownloadLyricsEnabled((Boolean) newValue);
|
||||
}
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
private void getScanStatus() {
|
||||
settingViewModel.getScanStatus(new ScanCallback() {
|
||||
@Override
|
||||
public void onError(Exception exception) {
|
||||
findPreference("scan_library").setSummary(exception.getMessage());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSuccess(boolean isScanning, long count) {
|
||||
findPreference("scan_library").setSummary(getString(R.string.settings_scan_result, count));
|
||||
if (isScanning) getScanStatus();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void actionKeepScreenOn() {
|
||||
findPreference("always_on_display").setOnPreferenceChangeListener((preference, newValue) -> {
|
||||
if (newValue instanceof Boolean) {
|
||||
if ((Boolean) newValue) {
|
||||
activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
|
||||
} else {
|
||||
activity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
private final ServiceConnection serviceConnection = new ServiceConnection() {
|
||||
@Override
|
||||
public void onServiceConnected(ComponentName name, IBinder service) {
|
||||
mediaServiceBinder = (MediaService.LocalBinder) service;
|
||||
isServiceBound = true;
|
||||
checkEqualizerBands();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onServiceDisconnected(ComponentName name) {
|
||||
mediaServiceBinder = null;
|
||||
isServiceBound = false;
|
||||
}
|
||||
};
|
||||
|
||||
private void bindMediaService() {
|
||||
Intent intent = new Intent(requireActivity(), MediaService.class);
|
||||
intent.setAction(MediaService.ACTION_BIND_EQUALIZER);
|
||||
requireActivity().bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE);
|
||||
isServiceBound = true;
|
||||
}
|
||||
|
||||
private void checkEqualizerBands() {
|
||||
if (mediaServiceBinder != null) {
|
||||
EqualizerManager eqManager = mediaServiceBinder.getEqualizerManager();
|
||||
short numBands = eqManager.getNumberOfBands();
|
||||
Preference appEqualizer = findPreference("app_equalizer");
|
||||
if (appEqualizer != null) {
|
||||
appEqualizer.setVisible(numBands > 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void actionAppEqualizer() {
|
||||
Preference appEqualizer = findPreference("app_equalizer");
|
||||
if (appEqualizer != null) {
|
||||
appEqualizer.setOnPreferenceClickListener(preference -> {
|
||||
NavController navController = NavHostFragment.findNavController(this);
|
||||
NavOptions navOptions = new NavOptions.Builder()
|
||||
.setLaunchSingleTop(true)
|
||||
.setPopUpTo(R.id.equalizerFragment, true)
|
||||
.build();
|
||||
activity.setBottomNavigationBarVisibility(true);
|
||||
activity.setBottomSheetVisibility(true);
|
||||
navController.navigate(R.id.equalizerFragment, null, navOptions);
|
||||
return true;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPause() {
|
||||
super.onPause();
|
||||
if (isServiceBound) {
|
||||
requireActivity().unbindService(serviceConnection);
|
||||
isServiceBound = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,128 +1,61 @@
|
||||
package com.cappielloantonio.tempo.ui.fragment;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Intent;
|
||||
import android.content.ServiceConnection;
|
||||
import android.media.audiofx.AudioEffect;
|
||||
import android.net.Uri;
|
||||
import static com.google.android.material.internal.ViewUtils.hideKeyboard;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.os.IBinder;
|
||||
import android.text.InputFilter;
|
||||
import android.text.InputType;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.WindowManager;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.activity.result.ActivityResultLauncher;
|
||||
import androidx.activity.result.contract.ActivityResultContracts;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.OptIn;
|
||||
import androidx.appcompat.app.AppCompatDelegate;
|
||||
import androidx.core.os.LocaleListCompat;
|
||||
import androidx.lifecycle.ViewModelProvider;
|
||||
import androidx.media3.common.util.UnstableApi;
|
||||
import androidx.navigation.NavController;
|
||||
import androidx.navigation.NavOptions;
|
||||
import androidx.navigation.fragment.NavHostFragment;
|
||||
import androidx.preference.EditTextPreference;
|
||||
import androidx.preference.ListPreference;
|
||||
import androidx.preference.Preference;
|
||||
import androidx.preference.PreferenceCategory;
|
||||
import androidx.preference.PreferenceFragmentCompat;
|
||||
import androidx.preference.SwitchPreference;
|
||||
import androidx.fragment.app.Fragment;
|
||||
|
||||
import com.cappielloantonio.tempo.BuildConfig;
|
||||
import com.cappielloantonio.tempo.R;
|
||||
import com.cappielloantonio.tempo.helper.ThemeHelper;
|
||||
import com.cappielloantonio.tempo.interfaces.DialogClickCallback;
|
||||
import com.cappielloantonio.tempo.interfaces.ScanCallback;
|
||||
import com.cappielloantonio.tempo.service.EqualizerManager;
|
||||
import com.cappielloantonio.tempo.service.MediaService;
|
||||
import com.cappielloantonio.tempo.databinding.FragmentSettingsBinding;
|
||||
import com.cappielloantonio.tempo.ui.activity.MainActivity;
|
||||
import com.cappielloantonio.tempo.ui.dialog.DeleteDownloadStorageDialog;
|
||||
import com.cappielloantonio.tempo.ui.dialog.DownloadStorageDialog;
|
||||
import com.cappielloantonio.tempo.ui.dialog.StarredSyncDialog;
|
||||
import com.cappielloantonio.tempo.ui.dialog.StarredAlbumSyncDialog;
|
||||
import com.cappielloantonio.tempo.ui.dialog.StarredArtistSyncDialog;
|
||||
import com.cappielloantonio.tempo.ui.dialog.StreamingCacheStorageDialog;
|
||||
import com.cappielloantonio.tempo.util.DownloadUtil;
|
||||
import com.cappielloantonio.tempo.util.Preferences;
|
||||
import com.cappielloantonio.tempo.util.UIUtil;
|
||||
import com.cappielloantonio.tempo.util.ExternalAudioReader;
|
||||
import com.cappielloantonio.tempo.viewmodel.SettingViewModel;
|
||||
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
|
||||
@OptIn(markerClass = UnstableApi.class)
|
||||
public class SettingsFragment extends PreferenceFragmentCompat {
|
||||
private static final String TAG = "SettingsFragment";
|
||||
public class SettingsFragment extends Fragment {
|
||||
|
||||
private MainActivity activity;
|
||||
private SettingViewModel settingViewModel;
|
||||
|
||||
private ActivityResultLauncher<Intent> equalizerResultLauncher;
|
||||
private ActivityResultLauncher<Intent> directoryPickerLauncher;
|
||||
|
||||
private MediaService.LocalBinder mediaServiceBinder;
|
||||
private boolean isServiceBound = false;
|
||||
private FragmentSettingsBinding bind;
|
||||
|
||||
@Override
|
||||
public void onCreate(@Nullable Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
equalizerResultLauncher = registerForActivityResult(
|
||||
new ActivityResultContracts.StartActivityForResult(),
|
||||
result -> {}
|
||||
);
|
||||
activity = (MainActivity) getActivity();
|
||||
|
||||
if (!BuildConfig.FLAVOR.equals("tempus")) {
|
||||
PreferenceCategory githubUpdateCategory = findPreference("settings_github_update_category_key");
|
||||
if (githubUpdateCategory != null) {
|
||||
getPreferenceScreen().removePreference(githubUpdateCategory);
|
||||
}
|
||||
}
|
||||
|
||||
directoryPickerLauncher = registerForActivityResult(
|
||||
new ActivityResultContracts.StartActivityForResult(),
|
||||
result -> {
|
||||
if (result.getResultCode() == Activity.RESULT_OK) {
|
||||
Intent data = result.getData();
|
||||
if (data != null) {
|
||||
Uri uri = data.getData();
|
||||
if (uri != null) {
|
||||
requireContext().getContentResolver().takePersistableUriPermission(
|
||||
uri,
|
||||
Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION
|
||||
);
|
||||
|
||||
Preferences.setDownloadDirectoryUri(uri.toString());
|
||||
ExternalAudioReader.refreshCache();
|
||||
Toast.makeText(requireContext(), R.string.settings_download_folder_set, Toast.LENGTH_SHORT).show();
|
||||
checkDownloadDirectory();
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||
activity = (MainActivity) getActivity();
|
||||
bind = FragmentSettingsBinding.inflate(inflater,container,false);
|
||||
View view = bind.getRoot();
|
||||
|
||||
View view = super.onCreateView(inflater, container, savedInstanceState);
|
||||
settingViewModel = new ViewModelProvider(requireActivity()).get(SettingViewModel.class);
|
||||
|
||||
if (view != null) {
|
||||
getListView().setPadding(0, 0, 0, (int) getResources().getDimension(R.dimen.global_padding_bottom));
|
||||
}
|
||||
initAppBar();
|
||||
|
||||
return view;
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onViewCreated(@NonNull View view,
|
||||
@Nullable Bundle savedInstanceState) {
|
||||
super.onViewCreated(view, savedInstanceState);
|
||||
|
||||
// Add the PreferenceFragment only the first time
|
||||
if (savedInstanceState == null) {
|
||||
SettingsContainerFragment prefFragment = new SettingsContainerFragment();
|
||||
|
||||
// Use the child fragment manager so the PreferenceFragment is scoped to this fragment
|
||||
getChildFragmentManager()
|
||||
.beginTransaction()
|
||||
.replace(R.id.settings_container, prefFragment)
|
||||
.setReorderingAllowed(true) // optional but recommended
|
||||
.commit();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -134,479 +67,21 @@ public class SettingsFragment extends PreferenceFragmentCompat {
|
||||
activity.setSystemBarsVisibility(!activity.isLandscape);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
|
||||
checkSystemEqualizer();
|
||||
checkCacheStorage();
|
||||
checkStorage();
|
||||
checkDownloadDirectory();
|
||||
|
||||
setStreamingCacheSize();
|
||||
setAppLanguage();
|
||||
setVersion();
|
||||
setNetorkPingTimeoutBase();
|
||||
|
||||
actionLogout();
|
||||
actionScan();
|
||||
actionSyncStarredAlbums();
|
||||
actionSyncStarredTracks();
|
||||
actionSyncStarredArtists();
|
||||
actionChangeStreamingCacheStorage();
|
||||
actionChangeDownloadStorage();
|
||||
actionSetDownloadDirectory();
|
||||
actionDeleteDownloadStorage();
|
||||
actionKeepScreenOn();
|
||||
actionAutoDownloadLyrics();
|
||||
actionMiniPlayerHeart();
|
||||
|
||||
bindMediaService();
|
||||
actionAppEqualizer();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStop() {
|
||||
super.onStop();
|
||||
activity.setBottomSheetVisibility(true);
|
||||
activity.toggleNavigationDrawerLockOnOrientationChange();
|
||||
activity.setSystemBarsVisibility(!activity.isLandscape);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
|
||||
setPreferencesFromResource(R.xml.global_preferences, rootKey);
|
||||
ListPreference themePreference = findPreference(Preferences.THEME);
|
||||
if (themePreference != null) {
|
||||
themePreference.setOnPreferenceChangeListener(
|
||||
(preference, newValue) -> {
|
||||
String themeOption = (String) newValue;
|
||||
ThemeHelper.applyTheme(themeOption);
|
||||
return true;
|
||||
});
|
||||
if (activity.isLandscape) {
|
||||
activity.setNavigationDrawerLock(false);
|
||||
} else if (Preferences.getEnableDrawerOnPortrait()) {
|
||||
activity.setNavigationDrawerLock(false);
|
||||
}
|
||||
}
|
||||
|
||||
private void checkSystemEqualizer() {
|
||||
Preference equalizer = findPreference("system_equalizer");
|
||||
|
||||
if (equalizer == null) return;
|
||||
|
||||
Intent intent = new Intent(AudioEffect.ACTION_DISPLAY_AUDIO_EFFECT_CONTROL_PANEL);
|
||||
|
||||
if ((intent.resolveActivity(requireActivity().getPackageManager()) != null)) {
|
||||
equalizer.setOnPreferenceClickListener(preference -> {
|
||||
equalizerResultLauncher.launch(intent);
|
||||
return true;
|
||||
});
|
||||
} else {
|
||||
equalizer.setVisible(false);
|
||||
}
|
||||
}
|
||||
|
||||
private void checkCacheStorage() {
|
||||
Preference storage = findPreference("streaming_cache_storage");
|
||||
|
||||
if (storage == null) return;
|
||||
|
||||
try {
|
||||
if (requireContext().getExternalFilesDirs(null)[1] == null) {
|
||||
storage.setVisible(false);
|
||||
} else {
|
||||
storage.setSummary(Preferences.getStreamingCacheStoragePreference() == 0 ? R.string.download_storage_internal_dialog_negative_button : R.string.download_storage_external_dialog_positive_button);
|
||||
}
|
||||
} catch (Exception exception) {
|
||||
storage.setVisible(false);
|
||||
}
|
||||
}
|
||||
|
||||
private void checkStorage() {
|
||||
Preference storage = findPreference("download_storage");
|
||||
|
||||
if (storage == null) return;
|
||||
|
||||
try {
|
||||
if (requireContext().getExternalFilesDirs(null)[1] == null) {
|
||||
storage.setVisible(false);
|
||||
} else {
|
||||
int pref = Preferences.getDownloadStoragePreference();
|
||||
if (pref == 0) {
|
||||
storage.setSummary(R.string.download_storage_internal_dialog_negative_button);
|
||||
} else if (pref == 1) {
|
||||
storage.setSummary(R.string.download_storage_external_dialog_positive_button);
|
||||
} else {
|
||||
storage.setSummary(R.string.download_storage_directory_dialog_neutral_button);
|
||||
}
|
||||
}
|
||||
} catch (Exception exception) {
|
||||
storage.setVisible(false);
|
||||
}
|
||||
}
|
||||
|
||||
private void checkDownloadDirectory() {
|
||||
Preference storage = findPreference("download_storage");
|
||||
Preference directory = findPreference("set_download_directory");
|
||||
|
||||
if (directory == null) return;
|
||||
|
||||
String current = Preferences.getDownloadDirectoryUri();
|
||||
if (current != null) {
|
||||
if (storage != null) storage.setVisible(false);
|
||||
directory.setVisible(true);
|
||||
directory.setIcon(R.drawable.ic_close);
|
||||
directory.setTitle(R.string.settings_clear_download_folder);
|
||||
directory.setSummary(current);
|
||||
} else {
|
||||
if (storage != null) storage.setVisible(true);
|
||||
if (Preferences.getDownloadStoragePreference() == 2) {
|
||||
directory.setVisible(true);
|
||||
directory.setIcon(R.drawable.ic_folder);
|
||||
directory.setTitle(R.string.settings_set_download_folder);
|
||||
directory.setSummary(R.string.settings_choose_download_folder);
|
||||
} else {
|
||||
directory.setVisible(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void setNetorkPingTimeoutBase() {
|
||||
EditTextPreference networkPingTimeoutBase = findPreference("network_ping_timeout_base");
|
||||
|
||||
if (networkPingTimeoutBase != null) {
|
||||
networkPingTimeoutBase.setSummaryProvider(EditTextPreference.SimpleSummaryProvider.getInstance());
|
||||
networkPingTimeoutBase.setOnBindEditTextListener(editText -> {
|
||||
editText.setInputType(InputType.TYPE_CLASS_NUMBER);
|
||||
editText.setFilters(new InputFilter[]{ (source, start, end, dest, dstart, dend) -> {
|
||||
for (int i = start; i < end; i++) {
|
||||
if (!Character.isDigit(source.charAt(i))) {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}});
|
||||
private void initAppBar() {
|
||||
bind.settingsToolbar.setNavigationOnClickListener(v -> {
|
||||
activity.navController.navigateUp();
|
||||
});
|
||||
|
||||
networkPingTimeoutBase.setOnPreferenceChangeListener((preference, newValue) -> {
|
||||
String input = (String) newValue;
|
||||
return input != null && !input.isEmpty();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private void setStreamingCacheSize() {
|
||||
ListPreference streamingCachePreference = findPreference("streaming_cache_size");
|
||||
|
||||
if (streamingCachePreference != null) {
|
||||
streamingCachePreference.setSummaryProvider(new Preference.SummaryProvider<ListPreference>() {
|
||||
@Nullable
|
||||
@Override
|
||||
public CharSequence provideSummary(@NonNull ListPreference preference) {
|
||||
CharSequence entry = preference.getEntry();
|
||||
|
||||
if (entry == null) return null;
|
||||
|
||||
long currentSizeMb = DownloadUtil.getStreamingCacheSize(requireActivity()) / (1024 * 1024);
|
||||
|
||||
return getString(R.string.settings_summary_streaming_cache_size, entry, String.valueOf(currentSizeMb));
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private void setAppLanguage() {
|
||||
ListPreference localePref = (ListPreference) findPreference("language");
|
||||
|
||||
Map<String, String> locales = UIUtil.getLangPreferenceDropdownEntries(requireContext());
|
||||
|
||||
CharSequence[] entries = locales.keySet().toArray(new CharSequence[locales.size()]);
|
||||
CharSequence[] entryValues = locales.values().toArray(new CharSequence[locales.size()]);
|
||||
|
||||
localePref.setEntries(entries);
|
||||
localePref.setEntryValues(entryValues);
|
||||
|
||||
String value = localePref.getValue();
|
||||
if ("default".equals(value)) {
|
||||
localePref.setSummary(requireContext().getString(R.string.settings_system_language));
|
||||
} else {
|
||||
localePref.setSummary(Locale.forLanguageTag(value).getDisplayName());
|
||||
}
|
||||
|
||||
localePref.setOnPreferenceChangeListener((preference, newValue) -> {
|
||||
if ("default".equals(newValue)) {
|
||||
AppCompatDelegate.setApplicationLocales(LocaleListCompat.getEmptyLocaleList());
|
||||
preference.setSummary(requireContext().getString(R.string.settings_system_language));
|
||||
} else {
|
||||
LocaleListCompat appLocale = LocaleListCompat.forLanguageTags((String) newValue);
|
||||
AppCompatDelegate.setApplicationLocales(appLocale);
|
||||
preference.setSummary(Locale.forLanguageTag((String) newValue).getDisplayName());
|
||||
}
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
private void setVersion() {
|
||||
findPreference("version").setSummary(BuildConfig.VERSION_NAME);
|
||||
}
|
||||
|
||||
private void actionLogout() {
|
||||
findPreference("logout").setOnPreferenceClickListener(preference -> {
|
||||
activity.quit();
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
private void actionScan() {
|
||||
findPreference("scan_library").setOnPreferenceClickListener(preference -> {
|
||||
settingViewModel.launchScan(new ScanCallback() {
|
||||
@Override
|
||||
public void onError(Exception exception) {
|
||||
findPreference("scan_library").setSummary(exception.getMessage());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSuccess(boolean isScanning, long count) {
|
||||
findPreference("scan_library").setSummary(getString(R.string.settings_scan_result, count));
|
||||
if (isScanning) getScanStatus();
|
||||
}
|
||||
});
|
||||
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
private void actionSyncStarredTracks() {
|
||||
findPreference("sync_starred_tracks_for_offline_use").setOnPreferenceChangeListener((preference, newValue) -> {
|
||||
if (newValue instanceof Boolean) {
|
||||
if ((Boolean) newValue) {
|
||||
StarredSyncDialog dialog = new StarredSyncDialog(() -> {
|
||||
((SwitchPreference)preference).setChecked(false);
|
||||
});
|
||||
dialog.show(activity.getSupportFragmentManager(), null);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
private void actionSyncStarredAlbums() {
|
||||
findPreference("sync_starred_albums_for_offline_use").setOnPreferenceChangeListener((preference, newValue) -> {
|
||||
if (newValue instanceof Boolean) {
|
||||
if ((Boolean) newValue) {
|
||||
StarredAlbumSyncDialog dialog = new StarredAlbumSyncDialog(() -> {
|
||||
((SwitchPreference)preference).setChecked(false);
|
||||
});
|
||||
dialog.show(activity.getSupportFragmentManager(), null);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
private void actionSyncStarredArtists() {
|
||||
findPreference("sync_starred_artists_for_offline_use").setOnPreferenceChangeListener((preference, newValue) -> {
|
||||
if (newValue instanceof Boolean) {
|
||||
if ((Boolean) newValue) {
|
||||
StarredArtistSyncDialog dialog = new StarredArtistSyncDialog(() -> {
|
||||
((SwitchPreference)preference).setChecked(false);
|
||||
});
|
||||
dialog.show(activity.getSupportFragmentManager(), null);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
private void actionChangeStreamingCacheStorage() {
|
||||
findPreference("streaming_cache_storage").setOnPreferenceClickListener(preference -> {
|
||||
StreamingCacheStorageDialog dialog = new StreamingCacheStorageDialog(new DialogClickCallback() {
|
||||
@Override
|
||||
public void onPositiveClick() {
|
||||
findPreference("streaming_cache_storage").setSummary(R.string.streaming_cache_storage_external_dialog_positive_button);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNegativeClick() {
|
||||
findPreference("streaming_cache_storage").setSummary(R.string.streaming_cache_storage_internal_dialog_negative_button);
|
||||
}
|
||||
});
|
||||
dialog.show(activity.getSupportFragmentManager(), null);
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
private void actionChangeDownloadStorage() {
|
||||
findPreference("download_storage").setOnPreferenceClickListener(preference -> {
|
||||
DownloadStorageDialog dialog = new DownloadStorageDialog(new DialogClickCallback() {
|
||||
@Override
|
||||
public void onPositiveClick() {
|
||||
findPreference("download_storage").setSummary(R.string.download_storage_external_dialog_positive_button);
|
||||
checkDownloadDirectory();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNegativeClick() {
|
||||
findPreference("download_storage").setSummary(R.string.download_storage_internal_dialog_negative_button);
|
||||
checkDownloadDirectory();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNeutralClick() {
|
||||
findPreference("download_storage").setSummary(R.string.download_storage_directory_dialog_neutral_button);
|
||||
checkDownloadDirectory();
|
||||
}
|
||||
});
|
||||
dialog.show(activity.getSupportFragmentManager(), null);
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
private void actionSetDownloadDirectory() {
|
||||
Preference pref = findPreference("set_download_directory");
|
||||
if (pref != null) {
|
||||
pref.setOnPreferenceClickListener(preference -> {
|
||||
String current = Preferences.getDownloadDirectoryUri();
|
||||
|
||||
if (current != null) {
|
||||
Preferences.setDownloadDirectoryUri(null);
|
||||
Preferences.setDownloadStoragePreference(0);
|
||||
ExternalAudioReader.refreshCache();
|
||||
Toast.makeText(requireContext(), R.string.settings_download_folder_cleared, Toast.LENGTH_SHORT).show();
|
||||
checkStorage();
|
||||
checkDownloadDirectory();
|
||||
} else {
|
||||
Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE);
|
||||
intent.addFlags(Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION
|
||||
| Intent.FLAG_GRANT_READ_URI_PERMISSION
|
||||
| Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
|
||||
directoryPickerLauncher.launch(intent);
|
||||
}
|
||||
return true;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private void actionDeleteDownloadStorage() {
|
||||
findPreference("delete_download_storage").setOnPreferenceClickListener(preference -> {
|
||||
DeleteDownloadStorageDialog dialog = new DeleteDownloadStorageDialog();
|
||||
dialog.show(activity.getSupportFragmentManager(), null);
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
private void actionMiniPlayerHeart() {
|
||||
SwitchPreference preference = findPreference("mini_shuffle_button_visibility");
|
||||
if (preference == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
preference.setChecked(Preferences.showShuffleInsteadOfHeart());
|
||||
preference.setOnPreferenceChangeListener((pref, newValue) -> {
|
||||
if (newValue instanceof Boolean) {
|
||||
Preferences.setShuffleInsteadOfHeart((Boolean) newValue);
|
||||
}
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
private void actionAutoDownloadLyrics() {
|
||||
SwitchPreference preference = findPreference("auto_download_lyrics");
|
||||
if (preference == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
preference.setChecked(Preferences.isAutoDownloadLyricsEnabled());
|
||||
preference.setOnPreferenceChangeListener((pref, newValue) -> {
|
||||
if (newValue instanceof Boolean) {
|
||||
Preferences.setAutoDownloadLyricsEnabled((Boolean) newValue);
|
||||
}
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
private void getScanStatus() {
|
||||
settingViewModel.getScanStatus(new ScanCallback() {
|
||||
@Override
|
||||
public void onError(Exception exception) {
|
||||
findPreference("scan_library").setSummary(exception.getMessage());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSuccess(boolean isScanning, long count) {
|
||||
findPreference("scan_library").setSummary(getString(R.string.settings_scan_result, count));
|
||||
if (isScanning) getScanStatus();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void actionKeepScreenOn() {
|
||||
findPreference("always_on_display").setOnPreferenceChangeListener((preference, newValue) -> {
|
||||
if (newValue instanceof Boolean) {
|
||||
if ((Boolean) newValue) {
|
||||
activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
|
||||
} else {
|
||||
activity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
private final ServiceConnection serviceConnection = new ServiceConnection() {
|
||||
@Override
|
||||
public void onServiceConnected(ComponentName name, IBinder service) {
|
||||
mediaServiceBinder = (MediaService.LocalBinder) service;
|
||||
isServiceBound = true;
|
||||
checkEqualizerBands();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onServiceDisconnected(ComponentName name) {
|
||||
mediaServiceBinder = null;
|
||||
isServiceBound = false;
|
||||
}
|
||||
};
|
||||
|
||||
private void bindMediaService() {
|
||||
Intent intent = new Intent(requireActivity(), MediaService.class);
|
||||
intent.setAction(MediaService.ACTION_BIND_EQUALIZER);
|
||||
requireActivity().bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE);
|
||||
isServiceBound = true;
|
||||
}
|
||||
|
||||
private void checkEqualizerBands() {
|
||||
if (mediaServiceBinder != null) {
|
||||
EqualizerManager eqManager = mediaServiceBinder.getEqualizerManager();
|
||||
short numBands = eqManager.getNumberOfBands();
|
||||
Preference appEqualizer = findPreference("app_equalizer");
|
||||
if (appEqualizer != null) {
|
||||
appEqualizer.setVisible(numBands > 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void actionAppEqualizer() {
|
||||
Preference appEqualizer = findPreference("app_equalizer");
|
||||
if (appEqualizer != null) {
|
||||
appEqualizer.setOnPreferenceClickListener(preference -> {
|
||||
NavController navController = NavHostFragment.findNavController(this);
|
||||
NavOptions navOptions = new NavOptions.Builder()
|
||||
.setLaunchSingleTop(true)
|
||||
.setPopUpTo(R.id.equalizerFragment, true)
|
||||
.build();
|
||||
activity.setBottomNavigationBarVisibility(true);
|
||||
activity.setBottomSheetVisibility(true);
|
||||
navController.navigate(R.id.equalizerFragment, null, navOptions);
|
||||
return true;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPause() {
|
||||
super.onPause();
|
||||
if (isServiceBound) {
|
||||
requireActivity().unbindService(serviceConnection);
|
||||
isServiceBound = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -109,6 +109,8 @@ public class SongListPageFragment extends Fragment implements ClickCallback {
|
||||
}
|
||||
|
||||
private void init() {
|
||||
songListPageViewModel.invalidateSongList();
|
||||
|
||||
if (requireArguments().getString(Constants.MEDIA_RECENTLY_PLAYED) != null) {
|
||||
songListPageViewModel.title = Constants.MEDIA_RECENTLY_PLAYED;
|
||||
songListPageViewModel.toolbarTitle = getString(R.string.song_list_page_recently_played);
|
||||
@@ -146,6 +148,10 @@ public class SongListPageFragment extends Fragment implements ClickCallback {
|
||||
songListPageViewModel.title = Constants.MEDIA_STARRED;
|
||||
songListPageViewModel.toolbarTitle = getString(R.string.song_list_page_starred);
|
||||
bind.pageTitleLabel.setText(R.string.song_list_page_starred);
|
||||
} else if (requireArguments().getString(Constants.MEDIA_ALL) != null) {
|
||||
songListPageViewModel.title = Constants.MEDIA_ALL;
|
||||
songListPageViewModel.toolbarTitle = getString(R.string.song_list_page_all);
|
||||
bind.pageTitleLabel.setText(R.string.song_list_page_all);
|
||||
} else if (requireArguments().getString(Constants.MEDIA_DOWNLOADED) != null) {
|
||||
songListPageViewModel.title = Constants.MEDIA_DOWNLOADED;
|
||||
songListPageViewModel.toolbarTitle = getString(R.string.song_list_page_downloaded);
|
||||
@@ -302,6 +308,7 @@ public class SongListPageFragment extends Fragment implements ClickCallback {
|
||||
case Constants.MEDIA_BY_ARTIST:
|
||||
case Constants.MEDIA_BY_GENRES:
|
||||
case Constants.MEDIA_STARRED:
|
||||
case Constants.MEDIA_ALL:
|
||||
bind.pageSubtitleLabel.setText(getString(R.string.generic_list_page_count, children.size()));
|
||||
break;
|
||||
}
|
||||
@@ -316,6 +323,7 @@ public class SongListPageFragment extends Fragment implements ClickCallback {
|
||||
case Constants.MEDIA_BY_ARTIST:
|
||||
case Constants.MEDIA_BY_GENRES:
|
||||
case Constants.MEDIA_STARRED:
|
||||
case Constants.MEDIA_ALL:
|
||||
bind.songListSortImageView.setVisibility(View.VISIBLE);
|
||||
break;
|
||||
}
|
||||
@@ -367,4 +375,4 @@ public class SongListPageFragment extends Fragment implements ClickCallback {
|
||||
private void setMediaBrowserListenableFuture() {
|
||||
songHorizontalAdapter.setMediaBrowserListenableFuture(mediaBrowserListenableFuture);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -299,4 +299,4 @@ public class AlbumBottomSheetDialog extends BottomSheetDialogFragment implements
|
||||
homeViewModel.refreshShares(requireActivity());
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,112 @@
|
||||
package com.cappielloantonio.tempo.ui.fragment.bottomsheetdialog;
|
||||
|
||||
import android.content.ComponentName;
|
||||
import android.os.Bundle;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.media3.common.util.UnstableApi;
|
||||
import androidx.media3.session.MediaBrowser;
|
||||
import androidx.media3.session.SessionToken;
|
||||
|
||||
import com.cappielloantonio.tempo.R;
|
||||
import com.cappielloantonio.tempo.glide.CustomGlideRequest;
|
||||
import com.cappielloantonio.tempo.service.MediaManager;
|
||||
import com.cappielloantonio.tempo.service.MediaService;
|
||||
import com.cappielloantonio.tempo.subsonic.models.PlaylistWithSongs;
|
||||
import com.cappielloantonio.tempo.ui.activity.MainActivity;
|
||||
import com.cappielloantonio.tempo.util.Constants;
|
||||
import com.cappielloantonio.tempo.util.MusicUtil;
|
||||
import com.google.android.material.bottomsheet.BottomSheetDialogFragment;
|
||||
import com.google.common.util.concurrent.ListenableFuture;
|
||||
|
||||
@UnstableApi
|
||||
public class PlaylistBottomSheetDialog extends BottomSheetDialogFragment implements View.OnClickListener {
|
||||
private PlaylistWithSongs playlist;
|
||||
private ListenableFuture<MediaBrowser> mediaBrowserListenableFuture;
|
||||
private static final String TAG = "PlaylistBottomSheetDialog";
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
|
||||
View view = inflater.inflate(R.layout.bottom_sheet_playlist_dialog, container, false);
|
||||
|
||||
playlist = requireArguments().getParcelable(Constants.PLAYLIST_OBJECT);
|
||||
|
||||
init(view);
|
||||
|
||||
return view;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
|
||||
super.onViewCreated(view, savedInstanceState);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStart() {
|
||||
super.onStart();
|
||||
|
||||
initializeMediaBrowser();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStop() {
|
||||
releaseMediaBrowser();
|
||||
super.onStop();
|
||||
}
|
||||
|
||||
private void init(View view) {
|
||||
ImageView coverPlaylist = view.findViewById(R.id.playlist_cover_image_view);
|
||||
|
||||
CustomGlideRequest.Builder
|
||||
.from(view.getContext(), playlist.getCoverArtId(), CustomGlideRequest.ResourceType.Playlist)
|
||||
.build()
|
||||
.into(coverPlaylist);
|
||||
|
||||
TextView titlePlaylist = view.findViewById(R.id.playlist_title_text_view);
|
||||
titlePlaylist.setText(playlist.getName());
|
||||
|
||||
titlePlaylist.setSelected(true);
|
||||
|
||||
TextView countPlaylist = view.findViewById(R.id.playlist_count_text_view);
|
||||
countPlaylist.setText(view.getContext().getString(R.string.playlist_counted_tracks, playlist.getSongCount(), MusicUtil.getReadableDurationString(playlist.getDuration(), false)));
|
||||
|
||||
TextView playNext = view.findViewById(R.id.play_next_text_view);
|
||||
playNext.setOnClickListener(v -> {
|
||||
MediaManager.enqueue(mediaBrowserListenableFuture, playlist.getEntries(), true);
|
||||
((MainActivity) requireActivity()).setBottomSheetInPeek(true);
|
||||
dismissBottomSheet();
|
||||
});
|
||||
|
||||
TextView addToQueue = view.findViewById(R.id.add_to_queue_text_view);
|
||||
addToQueue.setOnClickListener(v -> {
|
||||
MediaManager.enqueue(mediaBrowserListenableFuture, playlist.getEntries(), false);
|
||||
((MainActivity) requireActivity()).setBottomSheetInPeek(true);
|
||||
dismissBottomSheet();
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
dismissBottomSheet();
|
||||
}
|
||||
|
||||
private void dismissBottomSheet() {
|
||||
dismiss();
|
||||
}
|
||||
|
||||
private void initializeMediaBrowser() {
|
||||
mediaBrowserListenableFuture = new MediaBrowser.Builder(requireContext(), new SessionToken(requireContext(), new ComponentName(requireContext(), MediaService.class))).buildAsync();
|
||||
}
|
||||
|
||||
private void releaseMediaBrowser() {
|
||||
MediaBrowser.releaseFuture(mediaBrowserListenableFuture);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -70,6 +70,7 @@ object Constants {
|
||||
const val MEDIA_BY_ARTIST = "MEDIA_BY_ARTIST"
|
||||
const val MEDIA_BY_YEAR = "MEDIA_BY_YEAR"
|
||||
const val MEDIA_STARRED = "MEDIA_STARRED"
|
||||
const val MEDIA_ALL = "MEDIA_ALL"
|
||||
const val MEDIA_DOWNLOADED = "MEDIA_DOWNLOADED"
|
||||
const val MEDIA_FROM_ALBUM = "MEDIA_FROM_ALBUM"
|
||||
const val MEDIA_MIX = "MEDIA_MIX"
|
||||
@@ -130,4 +131,4 @@ object Constants {
|
||||
enum class SeedType {
|
||||
ARTIST, ALBUM, TRACK
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -92,6 +92,7 @@ object Preferences {
|
||||
private const val ARTIST_DISPLAY_BIOGRAPHY= "artist_display_biography"
|
||||
private const val NETWORK_PING_TIMEOUT = "network_ping_timeout_base"
|
||||
|
||||
private const val TILE_SIZE = "tile_size"
|
||||
private const val AA_ALBUM_VIEW = "androidauto_album_view"
|
||||
private const val AA_HOME_VIEW = "androidauto_home_view"
|
||||
private const val AA_PLAYLIST_VIEW = "androidauto_playlist_view"
|
||||
@@ -101,6 +102,7 @@ object Preferences {
|
||||
private const val AA_SECOND_TAB = "androidauto_second_tab"
|
||||
private const val AA_THIRD_TAB = "androidauto_third_tab"
|
||||
private const val AA_FOURTH_TAB = "androidauto_fourth_tab"
|
||||
private const val AA_SHUFFLE_GENRE_SONGS = "androidauto_shuffle_genre_songs"
|
||||
|
||||
@JvmStatic
|
||||
fun getServer(): String? {
|
||||
@@ -769,6 +771,10 @@ object Preferences {
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun getTileSize(): Int {
|
||||
val parsed = App.getInstance().preferences.getString(TILE_SIZE, "2")?.toIntOrNull()
|
||||
return parsed?.takeIf { it in 2..6 } ?: 2
|
||||
}
|
||||
fun isAndroidAutoAlbumViewEnabled(): Boolean {
|
||||
return App.getInstance().preferences.getBoolean(AA_ALBUM_VIEW, true)
|
||||
}
|
||||
@@ -813,4 +819,14 @@ object Preferences {
|
||||
return App.getInstance().preferences.getString(AA_FOURTH_TAB, "3")!!.toInt()
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun isAndroidAutoShuffleGenreSongsEnabled(): Boolean {
|
||||
return App.getInstance().preferences.getBoolean(AA_SHUFFLE_GENRE_SONGS, false)
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun setAndroidAutoShuffleGenreSongsEnabled(enabled: Boolean) {
|
||||
App.getInstance().preferences.edit().putBoolean(AA_SHUFFLE_GENRE_SONGS, enabled).apply()
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,174 @@
|
||||
package com.cappielloantonio.tempo.util;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.DisplayMetrics;
|
||||
|
||||
public class TileSizeManager {
|
||||
|
||||
private static TileSizeManager instance;
|
||||
|
||||
private int tileSizePx;
|
||||
private int tileSpanCount;
|
||||
private int tileSpacing;
|
||||
private int genreSizePx;
|
||||
private int genreSpanCount;
|
||||
private int genreSpacing;
|
||||
private int GenreSpacing;
|
||||
private int discoverWidthPx;
|
||||
private int discoverHeightPx;
|
||||
private boolean tileIsInitialized;
|
||||
private boolean genreIsInitialized;
|
||||
private boolean discoverIsInitialized;
|
||||
|
||||
private TileSizeManager() {
|
||||
}
|
||||
|
||||
public static TileSizeManager getInstance() {
|
||||
if (instance == null) {
|
||||
instance = new TileSizeManager();
|
||||
}
|
||||
return instance;
|
||||
}
|
||||
|
||||
public int getTileSizePx(Context context) {
|
||||
if( !tileIsInitialized )
|
||||
calculateTileSize(context);
|
||||
return tileSizePx;
|
||||
}
|
||||
public int getTileSpanCount(Context context) {
|
||||
if( !tileIsInitialized )
|
||||
calculateTileSize(context);
|
||||
return tileSpanCount;
|
||||
}
|
||||
public int getTileSpacing(Context context) {
|
||||
if( !tileIsInitialized )
|
||||
calculateTileSize(context);
|
||||
return tileSpacing;
|
||||
}
|
||||
public int getGenreSizePx(Context context) {
|
||||
if( !genreIsInitialized )
|
||||
calculateGenreSize(context);
|
||||
return genreSizePx;
|
||||
}
|
||||
public int getGenreSpanCount(Context context) {
|
||||
if( !genreIsInitialized )
|
||||
calculateGenreSize(context);
|
||||
return genreSpanCount;
|
||||
}
|
||||
public int getGenreSpacing(Context context) {
|
||||
if( !genreIsInitialized )
|
||||
calculateGenreSize(context);
|
||||
return genreSpacing;
|
||||
}
|
||||
public int getDiscoverWidthPx(Context context) {
|
||||
if( !discoverIsInitialized )
|
||||
calculateTileSize(context);
|
||||
return discoverWidthPx;
|
||||
}
|
||||
public int getDiscoverHeightPx(Context context) {
|
||||
if( !discoverIsInitialized )
|
||||
calculateTileSize(context);
|
||||
return discoverHeightPx;
|
||||
}
|
||||
|
||||
public void calculateTileSize(Context context) {
|
||||
DisplayMetrics metrics = context.getResources().getDisplayMetrics();
|
||||
float screenWidth = metrics.widthPixels;
|
||||
float screenHeight = metrics.heightPixels;
|
||||
|
||||
// retrieve the divisor in the preferences
|
||||
int userTileSize = Math.max(2, Math.min(6, Preferences.getTileSize()));
|
||||
float divisor = (float)userTileSize;
|
||||
|
||||
// little pading = 10
|
||||
tileSizePx = Math.round(Math.min(screenWidth, screenHeight) / divisor) - 10;
|
||||
tileSpanCount = Math.max(2, Math.round(screenWidth / (float)tileSizePx) );
|
||||
|
||||
switch (userTileSize) {
|
||||
default:
|
||||
case 2: // XL
|
||||
tileSpacing = 20;
|
||||
break;
|
||||
case 3: // L
|
||||
tileSpacing = 15;
|
||||
break;
|
||||
case 4: // M
|
||||
tileSpacing = 10;
|
||||
break;
|
||||
case 5: // S
|
||||
tileSpacing = 6;
|
||||
break;
|
||||
case 6: // SX
|
||||
tileSpacing = 2;
|
||||
break;
|
||||
}
|
||||
tileIsInitialized = true;
|
||||
}
|
||||
|
||||
public void calculateGenreSize(Context context) {
|
||||
DisplayMetrics metrics = context.getResources().getDisplayMetrics();
|
||||
float screenWidth = metrics.widthPixels;
|
||||
float screenHeight = metrics.heightPixels;
|
||||
|
||||
// retrieve the divisor in the preferences
|
||||
int userTileSize = Math.max(2, Math.min(3, Preferences.getTileSize()));
|
||||
float divisor = (float)userTileSize;
|
||||
|
||||
// little pading = 10
|
||||
genreSizePx = Math.round(Math.min(screenWidth, screenHeight) / divisor) - 10;
|
||||
genreSpanCount = Math.max(2, Math.round(screenWidth / (float)genreSizePx) );
|
||||
|
||||
switch (userTileSize) {
|
||||
default:
|
||||
case 2: // XL
|
||||
genreSpacing = 20;
|
||||
break;
|
||||
case 3: // L
|
||||
genreSpacing = 15;
|
||||
break;
|
||||
case 4: // M
|
||||
genreSpacing = 10;
|
||||
break;
|
||||
case 5: // S
|
||||
genreSpacing = 6;
|
||||
break;
|
||||
case 6: // XS
|
||||
genreSpacing = 2;
|
||||
break;
|
||||
}
|
||||
genreIsInitialized = true;
|
||||
}
|
||||
|
||||
public void calculateDiscoverSize(Context context) {
|
||||
DisplayMetrics metrics = context.getResources().getDisplayMetrics();
|
||||
float screenWidth = metrics.widthPixels;
|
||||
float screenHeight = metrics.heightPixels;
|
||||
float discoverDivisor;
|
||||
|
||||
// retrieve the divisor in the preferences
|
||||
int userTileSize = Math.max(2, Math.min(6, Preferences.getTileSize()));
|
||||
|
||||
switch (userTileSize) {
|
||||
default:
|
||||
case 2: // XL
|
||||
discoverDivisor = 1.0f;
|
||||
break;
|
||||
case 3: // L
|
||||
discoverDivisor = 1.25f;
|
||||
break;
|
||||
case 4: // M
|
||||
discoverDivisor = 1.5f;
|
||||
break;
|
||||
case 5: // S
|
||||
discoverDivisor = 1.75f;
|
||||
break;
|
||||
case 6: // XS
|
||||
discoverDivisor = 2.0f;
|
||||
break;
|
||||
}
|
||||
|
||||
discoverWidthPx = Math.round(Math.min(screenWidth, screenHeight) / discoverDivisor) - 50;
|
||||
discoverHeightPx = Math.round((float)discoverWidthPx * 0.6f);
|
||||
discoverIsInitialized = true;
|
||||
}
|
||||
}
|
||||
@@ -5,11 +5,13 @@ import android.app.Application;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.lifecycle.AndroidViewModel;
|
||||
import androidx.lifecycle.LiveData;
|
||||
import androidx.media3.common.util.UnstableApi;
|
||||
|
||||
import com.cappielloantonio.tempo.model.RecentSearch;
|
||||
import com.cappielloantonio.tempo.repository.SearchingRepository;
|
||||
import com.cappielloantonio.tempo.subsonic.models.SearchResult2;
|
||||
import com.cappielloantonio.tempo.subsonic.models.SearchResult3;
|
||||
import com.cappielloantonio.tempo.ui.fragment.SearchFragment;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
@@ -43,8 +45,9 @@ public class SearchViewModel extends AndroidViewModel {
|
||||
return searchingRepository.search2(title);
|
||||
}
|
||||
|
||||
public LiveData<SearchResult3> search3(String title) {
|
||||
return searchingRepository.search3(title);
|
||||
@UnstableApi
|
||||
public LiveData<SearchResult3> search3(SearchFragment sf, String title) {
|
||||
return searchingRepository.search3(sf, title);
|
||||
}
|
||||
|
||||
public void insertNewSearch(String search) {
|
||||
|
||||
@@ -47,6 +47,10 @@ public class SongListPageViewModel extends AndroidViewModel {
|
||||
}
|
||||
|
||||
public LiveData<List<Child>> getSongList() {
|
||||
if (songList != null) {
|
||||
return songList;
|
||||
}
|
||||
|
||||
songList = new MutableLiveData<>(new ArrayList<>());
|
||||
|
||||
switch (title) {
|
||||
@@ -65,6 +69,9 @@ public class SongListPageViewModel extends AndroidViewModel {
|
||||
case Constants.MEDIA_STARRED:
|
||||
songList = songRepository.getStarredSongs(false, -1);
|
||||
break;
|
||||
case Constants.MEDIA_ALL:
|
||||
songList = songRepository.getAllSongs();
|
||||
break;
|
||||
}
|
||||
|
||||
return songList;
|
||||
@@ -90,10 +97,15 @@ public class SongListPageViewModel extends AndroidViewModel {
|
||||
case Constants.MEDIA_BY_GENRES:
|
||||
case Constants.MEDIA_BY_YEAR:
|
||||
case Constants.MEDIA_STARRED:
|
||||
case Constants.MEDIA_ALL:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public void invalidateSongList() {
|
||||
songList = null;
|
||||
}
|
||||
|
||||
public String getFiltersTitle() {
|
||||
return TextUtils.join(", ", filterNames);
|
||||
}
|
||||
|
||||
11
app/src/main/res/drawable/ic_aa_genres.xml
Normal file
@@ -0,0 +1,11 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:height="24dp"
|
||||
android:viewportHeight="960"
|
||||
android:viewportWidth="960"
|
||||
android:width="24dp">
|
||||
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M480,660Q555,660 607.5,607.5Q660,555 660,480Q660,405 607.5,352.5Q555,300 480,300Q405,300 352.5,352.5Q300,405 300,480Q300,555 352.5,607.5Q405,660 480,660ZM480,880Q397,880 324,848.5Q251,817 197,763Q143,709 111.5,636Q80,563 80,480Q80,397 111.5,324Q143,251 197,197Q251,143 324,111.5Q397,80 480,80Q563,80 636,111.5Q709,143 763,197Q817,251 848.5,324Q880,397 880,480Q880,563 848.5,636Q817,709 763,763Q709,817 636,848.5Q563,880 480,880Z"/>
|
||||
|
||||
</vector>
|
||||
10
app/src/main/res/drawable/ic_launcher_background.xml
Normal file
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="108dp"
|
||||
android:height="108dp"
|
||||
android:viewportWidth="108"
|
||||
android:viewportHeight="108">
|
||||
<path
|
||||
android:fillColor="#FFEC4A4A"
|
||||
android:pathData="M0,0h108v108h-108z" />
|
||||
</vector>
|
||||
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="108dp"
|
||||
android:height="108dp"
|
||||
android:viewportWidth="108"
|
||||
android:viewportHeight="108">
|
||||
<path
|
||||
android:fillColor="#FF111827"
|
||||
android:pathData="M0,0h108v108h-108z" />
|
||||
</vector>
|
||||
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="108dp"
|
||||
android:height="108dp"
|
||||
android:viewportWidth="108"
|
||||
android:viewportHeight="108">
|
||||
<path
|
||||
android:fillColor="#FF0F172A"
|
||||
android:pathData="M0,0h108v108h-108z" />
|
||||
</vector>
|
||||
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="108dp"
|
||||
android:height="108dp"
|
||||
android:viewportWidth="108"
|
||||
android:viewportHeight="108">
|
||||
<path
|
||||
android:fillColor="#FF1F2937"
|
||||
android:pathData="M0,0h108v108h-108z" />
|
||||
</vector>
|
||||
@@ -1,56 +1,77 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:aapt="http://schemas.android.com/aapt"
|
||||
android:width="108dp"
|
||||
android:height="108dp"
|
||||
android:viewportWidth="512"
|
||||
android:viewportHeight="512">
|
||||
<group android:scaleX="0.49"
|
||||
android:scaleY="0.49"
|
||||
android:translateX="130.56"
|
||||
android:translateY="130.56">
|
||||
<path
|
||||
android:pathData="M512,437.33c0,11.78 -9.56,21.34 -21.34,21.34H21.33C9.55,458.67 0,449.11 0,437.33V96c0,-11.78 9.55,-21.33 21.33,-21.33h469.33c11.78,0 21.34,9.55 21.34,21.33L512,437.33L512,437.33z"
|
||||
android:fillColor="#DA4453"/>
|
||||
<path
|
||||
android:pathData="M512,416.01c0,11.78 -9.56,21.31 -21.34,21.31H21.33C9.55,437.33 0,427.8 0,416.01V74.67c0,-11.78 9.55,-21.34 21.33,-21.34h469.33c11.78,0 21.34,9.56 21.34,21.34L512,416.01L512,416.01z"
|
||||
android:fillColor="#ED5564"/>
|
||||
<path
|
||||
android:pathData="M63.99,160c-5.89,0 -10.66,4.78 -10.66,10.67v149.34c0,5.88 4.77,10.66 10.66,10.66c5.89,0 10.67,-4.78 10.67,-10.66V170.67C74.66,164.78 69.88,160 63.99,160z"
|
||||
android:fillColor="#DA4453"/>
|
||||
<path
|
||||
android:pathData="M74.66,106.67c0,5.89 -4.78,10.66 -10.67,10.66c-5.89,0 -10.66,-4.77 -10.66,-10.66S58.1,96 63.99,96C69.88,96 74.66,100.78 74.66,106.67z"
|
||||
android:fillColor="#E6E9ED"/>
|
||||
<path
|
||||
android:pathData="M74.66,384.01c0,5.88 -4.78,10.66 -10.67,10.66c-5.89,0 -10.66,-4.78 -10.66,-10.66c0,-5.91 4.77,-10.69 10.66,-10.69C69.88,373.33 74.66,378.11 74.66,384.01z"
|
||||
android:fillColor="#E6E9ED"/>
|
||||
<path
|
||||
android:pathData="M448,123.73h-21.34v203.19l-40.31,50.41v0.02c-1.47,1.83 -2.34,4.14 -2.34,6.67c0,5.88 4.78,10.66 10.66,10.66c3.38,0 6.38,-1.56 8.33,-4h0.02l42.66,-53.34l0,0c1.47,-1.81 2.34,-4.13 2.34,-6.66V123.73z"
|
||||
android:fillColor="#E6E9ED"/>
|
||||
<path
|
||||
android:pathData="M437.33,149.33c-11.77,0 -21.33,-9.56 -21.33,-21.33s9.56,-21.33 21.33,-21.33s21.33,9.56 21.33,21.33S449.09,149.33 437.33,149.33z"
|
||||
android:fillColor="#E6E9ED"/>
|
||||
<path
|
||||
android:pathData="M437.33,96c-17.67,0 -32,14.33 -32,32s14.33,32 32,32s32,-14.33 32,-32S455,96 437.33,96zM437.33,138.67c-5.89,0 -10.67,-4.8 -10.67,-10.67c0,-5.88 4.78,-10.67 10.67,-10.67s10.67,4.8 10.67,10.67C448,133.88 443.22,138.67 437.33,138.67z"
|
||||
android:fillColor="#CCD1D9"/>
|
||||
<path
|
||||
android:pathData="M405.33,245.33c0,82.48 -66.86,149.34 -149.33,149.34c-82.47,0 -149.33,-66.86 -149.33,-149.34C106.66,162.86 173.52,96 255.99,96C338.47,96 405.33,162.86 405.33,245.33z"
|
||||
android:fillColor="#434A54"/>
|
||||
<path
|
||||
android:pathData="M266.66,149.33c0,-5.89 -4.77,-10.66 -10.67,-10.66c-58.91,0 -106.66,47.75 -106.66,106.65l0,0c0,5.89 4.77,10.67 10.66,10.67s10.67,-4.78 10.67,-10.67l0,0c0,-22.78 8.88,-44.22 24.99,-60.33c16.12,-16.13 37.55,-25 60.34,-25C261.89,160 266.66,155.22 266.66,149.33z"
|
||||
android:fillColor="#656D78"/>
|
||||
<path
|
||||
android:pathData="M352,234.67c-5.9,0 -10.67,4.77 -10.67,10.66l0,0c0,22.8 -8.88,44.23 -24.98,60.34c-16.13,16.13 -37.56,25 -60.35,25c-5.89,0 -10.66,4.78 -10.66,10.66c0,5.91 4.77,10.69 10.66,10.69c58.91,0 106.66,-47.77 106.66,-106.69C362.65,239.44 357.89,234.67 352,234.67z"
|
||||
android:fillColor="#656D78"/>
|
||||
<path
|
||||
android:pathData="M255.99,288.01c-23.52,0 -42.66,-19.16 -42.66,-42.69c0,-23.52 19.14,-42.66 42.66,-42.66c23.54,0 42.66,19.14 42.66,42.66C298.65,268.86 279.53,288.01 255.99,288.01z"
|
||||
android:fillColor="#FFCE54"/>
|
||||
<path
|
||||
android:pathData="M255.99,192c-29.45,0 -53.33,23.88 -53.33,53.33s23.88,53.34 53.33,53.34c29.46,0 53.34,-23.89 53.34,-53.34S285.45,192 255.99,192zM255.99,277.34c-17.64,0 -32,-14.36 -32,-32.02c0,-17.64 14.36,-32 32,-32c17.65,0 32.01,14.36 32.01,32C288,262.98 273.64,277.34 255.99,277.34z"
|
||||
android:fillColor="#F6BB42"/>
|
||||
<path
|
||||
android:pathData="M266.66,245.33c0,5.89 -4.77,10.67 -10.67,10.67c-5.89,0 -10.66,-4.78 -10.66,-10.67s4.77,-10.66 10.66,-10.66C261.89,234.67 266.66,239.44 266.66,245.33z"
|
||||
android:fillColor="#434A54"/>
|
||||
<path
|
||||
android:pathData="M74.66,234.67H53.33c-5.89,0 -10.66,4.77 -10.66,10.66s4.77,10.67 10.66,10.67h21.34c5.89,0 10.66,-4.78 10.66,-10.67S80.56,234.67 74.66,234.67z"
|
||||
android:fillColor="#434A54"/>
|
||||
</group>
|
||||
</vector>
|
||||
android:viewportWidth="108"
|
||||
android:viewportHeight="108">
|
||||
|
||||
<group
|
||||
android:scaleX="0.13"
|
||||
android:scaleY="0.13"
|
||||
android:translateX="21.5"
|
||||
android:translateY="21.5">
|
||||
<path
|
||||
android:pathData="M250,0c138.07,0 250,111.93 250,250S388.07,500 250,500 0,388.07 0,250 111.93,0 250,0ZM250,235c-8.28,0 -15,6.72 -15,15c0,8.28 6.72,15 15,15c8.28,0 15,-6.72 15,-15c0,-8.28 -6.72,-15 -15,-15Z">
|
||||
<aapt:attr name="android:fillColor">
|
||||
<gradient
|
||||
android:startX="122.34"
|
||||
android:startY="23.55"
|
||||
android:endX="377.69"
|
||||
android:endY="465.83"
|
||||
android:type="linear">
|
||||
<item android:offset="0.0" android:color="#FFEC4A4A" />
|
||||
<item android:offset="1.0" android:color="#FFEC4A4A" />
|
||||
</gradient>
|
||||
</aapt:attr>
|
||||
</path>
|
||||
|
||||
<path
|
||||
android:pathData="M250.41,20.5c126.89,0 229.75,102.86 229.75,229.75c0,126.89 -102.86,229.75 -229.75,229.75c-126.89,0 -229.75,-102.86 -229.75,-229.75C20.66,123.36 123.53,20.5 250.41,20.5ZM250.85,161.82c-49.09,0 -88.88,39.79 -88.88,88.88c0,49.09 39.79,88.88 88.88,88.88c49.09,0 88.88,-39.79 88.88,-88.88c0,-49.09 -39.79,-88.88 -88.88,-88.88Z">
|
||||
<aapt:attr name="android:fillColor">
|
||||
<gradient
|
||||
android:startX="116.21"
|
||||
android:startY="67.61"
|
||||
android:endX="403.29"
|
||||
android:endY="429.34"
|
||||
android:type="linear">
|
||||
<item android:offset="0.0" android:color="#66060606" />
|
||||
<item android:offset="1.0" android:color="#CC060606" />
|
||||
</gradient>
|
||||
</aapt:attr>
|
||||
</path>
|
||||
|
||||
<path
|
||||
android:pathData="M453.23,307.8c-18.5,72.24 -73.8,129.26 -144.2,148.92l-36.39,-138.74c21.97,-7.21 39.22,-24.84 45.88,-47.06l134.71,36.88Z">
|
||||
<aapt:attr name="android:fillColor">
|
||||
<gradient
|
||||
android:startX="420.63"
|
||||
android:startY="403.74"
|
||||
android:endX="78.4"
|
||||
android:endY="117.92"
|
||||
android:type="linear">
|
||||
<item android:offset="0.0" android:color="#33FFFFFF" />
|
||||
<item android:offset="1.0" android:color="#4DFFFFFF" />
|
||||
</gradient>
|
||||
</aapt:attr>
|
||||
</path>
|
||||
|
||||
<path
|
||||
android:pathData="M228.3,183.04c-21.73,7.15 -38.82,24.5 -45.62,46.39L47.5,192.42c18.5,-72.24 73.8,-129.26 144.2,-148.92l36.6,139.54Z">
|
||||
<aapt:attr name="android:fillColor">
|
||||
<gradient
|
||||
android:startX="420.63"
|
||||
android:startY="403.74"
|
||||
android:endX="78.4"
|
||||
android:endY="117.92"
|
||||
android:type="linear">
|
||||
<item android:offset="0.0" android:color="#33FFFFFF" />
|
||||
<item android:offset="1.0" android:color="#4DFFFFFF" />
|
||||
</gradient>
|
||||
</aapt:attr>
|
||||
</path>
|
||||
|
||||
<path
|
||||
android:fillColor="#66FFFFFF"
|
||||
android:pathData="M250.5,179.5c39.21,0 71,31.79 71,71s-31.79,71 -71,71s-71,-31.79 -71,-71s31.79,-71 71,-71ZM250,235c-8.28,0 -15,6.72 -15,15c0,8.28 6.72,15 15,15c8.28,0 15,-6.72 15,-15c0,-8.28 -6.72,-15 -15,-15Z" />
|
||||
</group>
|
||||
</vector>
|
||||
@@ -0,0 +1,22 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="108dp"
|
||||
android:height="108dp"
|
||||
android:viewportWidth="108"
|
||||
android:viewportHeight="108">
|
||||
|
||||
<path
|
||||
android:fillColor="#00FFFFFF"
|
||||
android:pathData="M54,18A36,36 0 1,1 53.99,18"
|
||||
android:strokeColor="#FFF97316"
|
||||
android:strokeLineCap="round"
|
||||
android:strokeWidth="8" />
|
||||
|
||||
<path
|
||||
android:fillColor="#FFF9FAFB"
|
||||
android:pathData="M34,28h40v10h-14v42h-12v-42h-14z" />
|
||||
|
||||
<path
|
||||
android:fillColor="#FFF97316"
|
||||
android:pathData="M70,64h12v12h-12z" />
|
||||
</vector>
|
||||
@@ -0,0 +1,28 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="108dp"
|
||||
android:height="108dp"
|
||||
android:viewportWidth="108"
|
||||
android:viewportHeight="108">
|
||||
<group
|
||||
android:scaleX="0.82"
|
||||
android:scaleY="0.82"
|
||||
android:translateX="9.72"
|
||||
android:translateY="9.72">
|
||||
<path
|
||||
android:fillColor="#FF38BDF8"
|
||||
android:pathData="M24,68h12v-18h-12z" />
|
||||
<path
|
||||
android:fillColor="#FFF8FAFC"
|
||||
android:pathData="M42,78h12v-48h-12z" />
|
||||
<path
|
||||
android:fillColor="#FF38BDF8"
|
||||
android:pathData="M60,60h12v-22h-12z" />
|
||||
<path
|
||||
android:fillColor="#FFF8FAFC"
|
||||
android:pathData="M78,48h6v24h-6z" />
|
||||
<path
|
||||
android:fillColor="#FF38BDF8"
|
||||
android:pathData="M24,30h60v8h-60z" />
|
||||
</group>
|
||||
</vector>
|
||||
@@ -0,0 +1,22 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="108dp"
|
||||
android:height="108dp"
|
||||
android:viewportWidth="108"
|
||||
android:viewportHeight="108">
|
||||
|
||||
<path
|
||||
android:fillColor="#00FFFFFF"
|
||||
android:pathData="M54,20A34,34 0 1,1 53.99,20"
|
||||
android:strokeColor="#FFF59E0B"
|
||||
android:strokeLineCap="round"
|
||||
android:strokeWidth="8" />
|
||||
|
||||
<path
|
||||
android:fillColor="#FFF9FAFB"
|
||||
android:pathData="M48,36h8v18l14,8l-4,7l-18,-11z" />
|
||||
|
||||
<path
|
||||
android:fillColor="#FFF59E0B"
|
||||
android:pathData="M30,28l20,12l-20,12z" />
|
||||
</vector>
|
||||
27
app/src/main/res/drawable/ic_launcher_monochrome.xml
Normal file
@@ -0,0 +1,27 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="108dp"
|
||||
android:height="108dp"
|
||||
android:viewportWidth="108"
|
||||
android:viewportHeight="108">
|
||||
|
||||
<path
|
||||
android:fillColor="#424940"
|
||||
android:pathData="M54,50.6018C55.8768,50.6018 57.3982,52.1232 57.3982,54C57.3982,55.8768 55.8768,57.3982 54,57.3982C52.1232,57.3982 50.6018,55.8768 50.6018,54C50.6018,52.1232 52.1232,50.6018 54,50.6018Z"/>
|
||||
|
||||
<path
|
||||
android:fillColor="#424940"
|
||||
android:pathData="M54.1049,42.2712C60.6295,42.2712 65.9187,47.5605 65.9188,54.0851C65.9188,60.6097 60.6295,65.8989 54.1049,65.8989C47.5802,65.8989 42.2907,60.6097 42.2907,54.0851C42.2908,47.5604 47.5802,42.2712 54.1049,42.2712ZM54.0611,46.3408C49.7973,46.3408 46.3408,49.7973 46.3408,54.0611C46.3408,58.3249 49.7972,61.7815 54.0611,61.7816C58.3249,61.7816 61.7816,58.3249 61.7816,54.0611C61.7815,49.7972 58.3249,46.3408 54.0611,46.3408Z"/>
|
||||
|
||||
<path
|
||||
android:fillColor="#424940"
|
||||
android:pathData="M54,22.5C71.397,22.5 85.5,36.603 85.5,54C85.5,71.397 71.397,85.5 54,85.5C36.603,85.5 22.5,71.397 22.5,54C22.5,36.603 36.603,22.5 54,22.5ZM54.0506,26.9138C39.0743,26.9138 26.9334,39.0545 26.9334,54.0308C26.9334,69.007 39.0743,81.1477 54.0506,81.1477C69.0268,81.1476 81.1675,69.007 81.1675,54.0308C81.1675,39.0545 69.0268,26.9139 54.0506,26.9138Z"/>
|
||||
|
||||
<path
|
||||
android:fillColor="#424940"
|
||||
android:pathData="M43.4405,31.2951C43.9287,31.1317 44.4432,31.4282 44.5738,31.9262L46.9248,40.8911C47.055,41.3873 46.8662,41.9097 46.4665,42.2312C44.8479,43.5335 43.4426,45.0999 42.3131,46.8671C42.0058,47.3479 41.4269,47.5914 40.8765,47.4408L31.9176,44.9882C31.4322,44.8554 31.141,44.3566 31.2884,43.8754C33.1082,37.9357 37.6601,33.2302 43.4405,31.2951Z"/>
|
||||
|
||||
<path
|
||||
android:fillColor="#424940"
|
||||
android:pathData="M65.1611,75.9356C64.6879,76.0939 64.1895,75.8064 64.0629,75.3238L61.7908,66.6599C61.6607,66.1637 61.8495,65.6414 62.2488,65.3193C63.8012,64.0666 65.1506,62.5627 66.2378,60.8676C66.5459,60.3873 67.1247,60.1437 67.675,60.2943L76.3298,62.6636C76.8002,62.7924 77.0825,63.2759 76.9397,63.7422C75.1759,69.4992 70.7637,74.0601 65.1611,75.9356Z"/>
|
||||
|
||||
</vector>
|
||||
@@ -0,0 +1,22 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="108dp"
|
||||
android:height="108dp"
|
||||
android:viewportWidth="108"
|
||||
android:viewportHeight="108">
|
||||
|
||||
<path
|
||||
android:fillColor="#00FFFFFF"
|
||||
android:pathData="M54,18A36,36 0 1,1 53.99,18"
|
||||
android:strokeColor="#FF111827"
|
||||
android:strokeLineCap="round"
|
||||
android:strokeWidth="8" />
|
||||
|
||||
<path
|
||||
android:fillColor="#FF111827"
|
||||
android:pathData="M34,28h40v10h-14v42h-12v-42h-14z" />
|
||||
|
||||
<path
|
||||
android:fillColor="#FF111827"
|
||||
android:pathData="M70,64h12v12h-12z" />
|
||||
</vector>
|
||||
@@ -0,0 +1,23 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="108dp"
|
||||
android:height="108dp"
|
||||
android:viewportWidth="108"
|
||||
android:viewportHeight="108">
|
||||
|
||||
<path
|
||||
android:fillColor="#FF111827"
|
||||
android:pathData="M24,68h12v-18h-12z" />
|
||||
<path
|
||||
android:fillColor="#FF111827"
|
||||
android:pathData="M42,78h12v-48h-12z" />
|
||||
<path
|
||||
android:fillColor="#FF111827"
|
||||
android:pathData="M60,60h12v-22h-12z" />
|
||||
<path
|
||||
android:fillColor="#FF111827"
|
||||
android:pathData="M78,48h6v24h-6z" />
|
||||
<path
|
||||
android:fillColor="#FF111827"
|
||||
android:pathData="M24,30h60v8h-60z" />
|
||||
</vector>
|
||||
@@ -0,0 +1,22 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="108dp"
|
||||
android:height="108dp"
|
||||
android:viewportWidth="108"
|
||||
android:viewportHeight="108">
|
||||
|
||||
<path
|
||||
android:fillColor="#00FFFFFF"
|
||||
android:pathData="M54,20A34,34 0 1,1 53.99,20"
|
||||
android:strokeColor="#FF111827"
|
||||
android:strokeLineCap="round"
|
||||
android:strokeWidth="8" />
|
||||
|
||||
<path
|
||||
android:fillColor="#FF111827"
|
||||
android:pathData="M48,36h8v18l14,8l-4,7l-18,-11z" />
|
||||
|
||||
<path
|
||||
android:fillColor="#FF111827"
|
||||
android:pathData="M30,28l20,12l-20,12z" />
|
||||
</vector>
|
||||
@@ -1,56 +1,77 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:aapt="http://schemas.android.com/aapt"
|
||||
android:width="108dp"
|
||||
android:height="108dp"
|
||||
android:viewportWidth="512"
|
||||
android:viewportHeight="512">
|
||||
<group android:scaleX="0.55"
|
||||
android:scaleY="0.55"
|
||||
android:translateX="150.56"
|
||||
android:translateY="150.56">
|
||||
<path
|
||||
android:pathData="M512,437.33c0,11.78 -9.56,21.34 -21.34,21.34H21.33C9.55,458.67 0,449.11 0,437.33V96c0,-11.78 9.55,-21.33 21.33,-21.33h469.33c11.78,0 21.34,9.55 21.34,21.33L512,437.33L512,437.33z"
|
||||
android:fillColor="#DA4453"/>
|
||||
<path
|
||||
android:pathData="M512,416.01c0,11.78 -9.56,21.31 -21.34,21.31H21.33C9.55,437.33 0,427.8 0,416.01V74.67c0,-11.78 9.55,-21.34 21.33,-21.34h469.33c11.78,0 21.34,9.56 21.34,21.34L512,416.01L512,416.01z"
|
||||
android:fillColor="#ED5564"/>
|
||||
<path
|
||||
android:pathData="M63.99,160c-5.89,0 -10.66,4.78 -10.66,10.67v149.34c0,5.88 4.77,10.66 10.66,10.66c5.89,0 10.67,-4.78 10.67,-10.66V170.67C74.66,164.78 69.88,160 63.99,160z"
|
||||
android:fillColor="#DA4453"/>
|
||||
<path
|
||||
android:pathData="M74.66,106.67c0,5.89 -4.78,10.66 -10.67,10.66c-5.89,0 -10.66,-4.77 -10.66,-10.66S58.1,96 63.99,96C69.88,96 74.66,100.78 74.66,106.67z"
|
||||
android:fillColor="#E6E9ED"/>
|
||||
<path
|
||||
android:pathData="M74.66,384.01c0,5.88 -4.78,10.66 -10.67,10.66c-5.89,0 -10.66,-4.78 -10.66,-10.66c0,-5.91 4.77,-10.69 10.66,-10.69C69.88,373.33 74.66,378.11 74.66,384.01z"
|
||||
android:fillColor="#E6E9ED"/>
|
||||
<path
|
||||
android:pathData="M448,123.73h-21.34v203.19l-40.31,50.41v0.02c-1.47,1.83 -2.34,4.14 -2.34,6.67c0,5.88 4.78,10.66 10.66,10.66c3.38,0 6.38,-1.56 8.33,-4h0.02l42.66,-53.34l0,0c1.47,-1.81 2.34,-4.13 2.34,-6.66V123.73z"
|
||||
android:fillColor="#E6E9ED"/>
|
||||
<path
|
||||
android:pathData="M437.33,149.33c-11.77,0 -21.33,-9.56 -21.33,-21.33s9.56,-21.33 21.33,-21.33s21.33,9.56 21.33,21.33S449.09,149.33 437.33,149.33z"
|
||||
android:fillColor="#E6E9ED"/>
|
||||
<path
|
||||
android:pathData="M437.33,96c-17.67,0 -32,14.33 -32,32s14.33,32 32,32s32,-14.33 32,-32S455,96 437.33,96zM437.33,138.67c-5.89,0 -10.67,-4.8 -10.67,-10.67c0,-5.88 4.78,-10.67 10.67,-10.67s10.67,4.8 10.67,10.67C448,133.88 443.22,138.67 437.33,138.67z"
|
||||
android:fillColor="#CCD1D9"/>
|
||||
<path
|
||||
android:pathData="M405.33,245.33c0,82.48 -66.86,149.34 -149.33,149.34c-82.47,0 -149.33,-66.86 -149.33,-149.34C106.66,162.86 173.52,96 255.99,96C338.47,96 405.33,162.86 405.33,245.33z"
|
||||
android:fillColor="#434A54"/>
|
||||
<path
|
||||
android:pathData="M266.66,149.33c0,-5.89 -4.77,-10.66 -10.67,-10.66c-58.91,0 -106.66,47.75 -106.66,106.65l0,0c0,5.89 4.77,10.67 10.66,10.67s10.67,-4.78 10.67,-10.67l0,0c0,-22.78 8.88,-44.22 24.99,-60.33c16.12,-16.13 37.55,-25 60.34,-25C261.89,160 266.66,155.22 266.66,149.33z"
|
||||
android:fillColor="#656D78"/>
|
||||
<path
|
||||
android:pathData="M352,234.67c-5.9,0 -10.67,4.77 -10.67,10.66l0,0c0,22.8 -8.88,44.23 -24.98,60.34c-16.13,16.13 -37.56,25 -60.35,25c-5.89,0 -10.66,4.78 -10.66,10.66c0,5.91 4.77,10.69 10.66,10.69c58.91,0 106.66,-47.77 106.66,-106.69C362.65,239.44 357.89,234.67 352,234.67z"
|
||||
android:fillColor="#656D78"/>
|
||||
<path
|
||||
android:pathData="M255.99,288.01c-23.52,0 -42.66,-19.16 -42.66,-42.69c0,-23.52 19.14,-42.66 42.66,-42.66c23.54,0 42.66,19.14 42.66,42.66C298.65,268.86 279.53,288.01 255.99,288.01z"
|
||||
android:fillColor="#FFCE54"/>
|
||||
<path
|
||||
android:pathData="M255.99,192c-29.45,0 -53.33,23.88 -53.33,53.33s23.88,53.34 53.33,53.34c29.46,0 53.34,-23.89 53.34,-53.34S285.45,192 255.99,192zM255.99,277.34c-17.64,0 -32,-14.36 -32,-32.02c0,-17.64 14.36,-32 32,-32c17.65,0 32.01,14.36 32.01,32C288,262.98 273.64,277.34 255.99,277.34z"
|
||||
android:fillColor="#F6BB42"/>
|
||||
<path
|
||||
android:pathData="M266.66,245.33c0,5.89 -4.77,10.67 -10.67,10.67c-5.89,0 -10.66,-4.78 -10.66,-10.67s4.77,-10.66 10.66,-10.66C261.89,234.67 266.66,239.44 266.66,245.33z"
|
||||
android:fillColor="#434A54"/>
|
||||
<path
|
||||
android:pathData="M74.66,234.67H53.33c-5.89,0 -10.66,4.77 -10.66,10.66s4.77,10.67 10.66,10.67h21.34c5.89,0 10.66,-4.78 10.66,-10.67S80.56,234.67 74.66,234.67z"
|
||||
android:fillColor="#434A54"/>
|
||||
</group>
|
||||
</vector>
|
||||
android:viewportWidth="108"
|
||||
android:viewportHeight="108">
|
||||
|
||||
<group
|
||||
android:scaleX="0.13"
|
||||
android:scaleY="0.13"
|
||||
android:translateX="21.5"
|
||||
android:translateY="21.5">
|
||||
<path
|
||||
android:pathData="M250,0c138.07,0 250,111.93 250,250S388.07,500 250,500 0,388.07 0,250 111.93,0 250,0ZM250,235c-8.28,0 -15,6.72 -15,15c0,8.28 6.72,15 15,15c8.28,0 15,-6.72 15,-15c0,-8.28 -6.72,-15 -15,-15Z">
|
||||
<aapt:attr name="android:fillColor">
|
||||
<gradient
|
||||
android:startX="122.34"
|
||||
android:startY="23.55"
|
||||
android:endX="377.69"
|
||||
android:endY="465.83"
|
||||
android:type="linear">
|
||||
<item android:offset="0.0" android:color="#FFEC4A4A" />
|
||||
<item android:offset="1.0" android:color="#FFEC4A4A" />
|
||||
</gradient>
|
||||
</aapt:attr>
|
||||
</path>
|
||||
|
||||
<path
|
||||
android:pathData="M250.41,20.5c126.89,0 229.75,102.86 229.75,229.75c0,126.89 -102.86,229.75 -229.75,229.75c-126.89,0 -229.75,-102.86 -229.75,-229.75C20.66,123.36 123.53,20.5 250.41,20.5ZM250.85,161.82c-49.09,0 -88.88,39.79 -88.88,88.88c0,49.09 39.79,88.88 88.88,88.88c49.09,0 88.88,-39.79 88.88,-88.88c0,-49.09 -39.79,-88.88 -88.88,-88.88Z">
|
||||
<aapt:attr name="android:fillColor">
|
||||
<gradient
|
||||
android:startX="116.21"
|
||||
android:startY="67.61"
|
||||
android:endX="403.29"
|
||||
android:endY="429.34"
|
||||
android:type="linear">
|
||||
<item android:offset="0.0" android:color="#66060606" />
|
||||
<item android:offset="1.0" android:color="#CC060606" />
|
||||
</gradient>
|
||||
</aapt:attr>
|
||||
</path>
|
||||
|
||||
<path
|
||||
android:pathData="M453.23,307.8c-18.5,72.24 -73.8,129.26 -144.2,148.92l-36.39,-138.74c21.97,-7.21 39.22,-24.84 45.88,-47.06l134.71,36.88Z">
|
||||
<aapt:attr name="android:fillColor">
|
||||
<gradient
|
||||
android:startX="420.63"
|
||||
android:startY="403.74"
|
||||
android:endX="78.4"
|
||||
android:endY="117.92"
|
||||
android:type="linear">
|
||||
<item android:offset="0.0" android:color="#33FFFFFF" />
|
||||
<item android:offset="1.0" android:color="#4DFFFFFF" />
|
||||
</gradient>
|
||||
</aapt:attr>
|
||||
</path>
|
||||
|
||||
<path
|
||||
android:pathData="M228.3,183.04c-21.73,7.15 -38.82,24.5 -45.62,46.39L47.5,192.42c18.5,-72.24 73.8,-129.26 144.2,-148.92l36.6,139.54Z">
|
||||
<aapt:attr name="android:fillColor">
|
||||
<gradient
|
||||
android:startX="420.63"
|
||||
android:startY="403.74"
|
||||
android:endX="78.4"
|
||||
android:endY="117.92"
|
||||
android:type="linear">
|
||||
<item android:offset="0.0" android:color="#33FFFFFF" />
|
||||
<item android:offset="1.0" android:color="#4DFFFFFF" />
|
||||
</gradient>
|
||||
</aapt:attr>
|
||||
</path>
|
||||
|
||||
<path
|
||||
android:fillColor="#66FFFFFF"
|
||||
android:pathData="M250.5,179.5c39.21,0 71,31.79 71,71s-31.79,71 -71,71s-71,-31.79 -71,-71s31.79,-71 71,-71ZM250,235c-8.28,0 -15,6.72 -15,15c0,8.28 6.72,15 15,15c8.28,0 15,-6.72 15,-15c0,-8.28 -6.72,-15 -15,-15Z" />
|
||||
</group>
|
||||
</vector>
|
||||
@@ -1,52 +1,77 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
xmlns:aapt="http://schemas.android.com/aapt"
|
||||
android:width="108dp"
|
||||
android:height="108dp"
|
||||
android:viewportWidth="512"
|
||||
android:viewportHeight="512">
|
||||
android:viewportHeight="522">
|
||||
<group
|
||||
android:scaleX="1.0"
|
||||
android:scaleY="1.0"
|
||||
android:translateX="14.0"
|
||||
android:translateY="14.0">
|
||||
|
||||
<path
|
||||
android:pathData="M512,437.33c0,11.78 -9.56,21.34 -21.34,21.34H21.33C9.55,458.67 0,449.11 0,437.33V96c0,-11.78 9.55,-21.33 21.33,-21.33h469.33c11.78,0 21.34,9.55 21.34,21.33L512,437.33L512,437.33z"
|
||||
android:fillColor="#DA4453"/>
|
||||
<path
|
||||
android:pathData="M512,416.01c0,11.78 -9.56,21.31 -21.34,21.31H21.33C9.55,437.33 0,427.8 0,416.01V74.67c0,-11.78 9.55,-21.34 21.33,-21.34h469.33c11.78,0 21.34,9.56 21.34,21.34L512,416.01L512,416.01z"
|
||||
android:fillColor="#ED5564"/>
|
||||
<path
|
||||
android:pathData="M250,0c138.07,0 250,111.93 250,250S388.07,500 250,500 0,388.07 0,250 111.93,0 250,0ZM250,235c-8.28,0 -15,6.72 -15,15c0,8.28 6.72,15 15,15c8.28,0 15,-6.72 15,-15c0,-8.28 -6.72,-15 -15,-15Z">
|
||||
<aapt:attr name="android:fillColor">
|
||||
<gradient
|
||||
android:startX="122.34"
|
||||
android:startY="23.55"
|
||||
android:endX="377.69"
|
||||
android:endY="465.83"
|
||||
android:type="linear">
|
||||
<item android:offset="0.0" android:color="#FFEC4A4A" />
|
||||
<item android:offset="1.0" android:color="#FFEC4A4A" />
|
||||
</gradient>
|
||||
</aapt:attr>
|
||||
</path>
|
||||
|
||||
<path android:pathData="M63.99,160c-5.89,0 -10.66,4.78 -10.66,10.67v149.34c0,5.88 4.77,10.66 10.66,10.66c5.89,0 10.67,-4.78 10.67,-10.66V170.67C74.66,164.78 69.88,160 63.99,160z"
|
||||
android:fillColor="#DA4453"/>
|
||||
<path
|
||||
android:pathData="M74.66,106.67c0,5.89 -4.78,10.66 -10.67,10.66c-5.89,0 -10.66,-4.77 -10.66,-10.66S58.1,96 63.99,96C69.88,96 74.66,100.78 74.66,106.67z"
|
||||
android:fillColor="#E6E9ED"/>
|
||||
<path
|
||||
android:pathData="M74.66,384.01c0,5.88 -4.78,10.66 -10.67,10.66c-5.89,0 -10.66,-4.78 -10.66,-10.66c0,-5.91 4.77,-10.69 10.66,-10.69C69.88,373.33 74.66,378.11 74.66,384.01z"
|
||||
android:fillColor="#E6E9ED"/>
|
||||
<path
|
||||
android:pathData="M448,123.73h-21.34v203.19l-40.31,50.41v0.02c-1.47,1.83 -2.34,4.14 -2.34,6.67c0,5.88 4.78,10.66 10.66,10.66c3.38,0 6.38,-1.56 8.33,-4h0.02l42.66,-53.34l0,0c1.47,-1.81 2.34,-4.13 2.34,-6.66V123.73z"
|
||||
android:fillColor="#E6E9ED"/>
|
||||
<path
|
||||
android:pathData="M437.33,149.33c-11.77,0 -21.33,-9.56 -21.33,-21.33s9.56,-21.33 21.33,-21.33s21.33,9.56 21.33,21.33S449.09,149.33 437.33,149.33z"
|
||||
android:fillColor="#E6E9ED"/>
|
||||
<path
|
||||
android:pathData="M437.33,96c-17.67,0 -32,14.33 -32,32s14.33,32 32,32s32,-14.33 32,-32S455,96 437.33,96zM437.33,138.67c-5.89,0 -10.67,-4.8 -10.67,-10.67c0,-5.88 4.78,-10.67 10.67,-10.67s10.67,4.8 10.67,10.67C448,133.88 443.22,138.67 437.33,138.67z"
|
||||
android:fillColor="#CCD1D9"/>
|
||||
<path
|
||||
android:pathData="M405.33,245.33c0,82.48 -66.86,149.34 -149.33,149.34c-82.47,0 -149.33,-66.86 -149.33,-149.34C106.66,162.86 173.52,96 255.99,96C338.47,96 405.33,162.86 405.33,245.33z"
|
||||
android:fillColor="#434A54"/>
|
||||
<path
|
||||
android:pathData="M266.66,149.33c0,-5.89 -4.77,-10.66 -10.67,-10.66c-58.91,0 -106.66,47.75 -106.66,106.65l0,0c0,5.89 4.77,10.67 10.67,10.67s10.67,-4.78 10.67,-10.67l0,0c0,-22.78 8.88,-44.22 24.99,-60.33c16.12,-16.13 37.55,-25 60.34,-25C261.89,160 266.66,155.22 266.66,149.33z"
|
||||
android:fillColor="#656D78"/>
|
||||
<path
|
||||
android:pathData="M352,234.67c-5.9,0 -10.67,4.77 -10.67,10.66l0,0c0,22.8 -8.88,44.23 -24.98,60.34c-16.13,16.13 -37.56,25 -60.35,25c-5.89,0 -10.66,4.78 -10.66,10.66c0,5.91 4.77,10.69 10.66,10.69c58.91,0 106.66,-47.77 106.66,-106.69C362.65,239.44 357.89,234.67 352,234.67z"
|
||||
android:fillColor="#656D78"/>
|
||||
<path
|
||||
android:pathData="M255.99,288.01c-23.52,0 -42.66,-19.16 -42.66,-42.69c0,-23.52 19.14,-42.66 42.66,-42.66c23.54,0 42.66,19.14 42.66,42.66C298.65,268.86 279.53,288.01 255.99,288.01z"
|
||||
android:fillColor="#FFCE54"/>
|
||||
<path
|
||||
android:pathData="M255.99,192c-29.45,0 -53.33,23.88 -53.33,53.33s23.88,53.34 53.33,53.34c29.46,0 53.34,-23.89 53.34,-53.34S285.45,192 255.99,192zM255.99,277.34c-17.64,0 -32,-14.36 -32,-32.02c0,-17.64 14.36,-32 32,-32c17.65,0 32.01,14.36 32.01,32C288,262.98 273.64,277.34 255.99,277.34z"
|
||||
android:fillColor="#F6BB42"/>
|
||||
<path
|
||||
android:pathData="M266.66,245.33c0,5.89 -4.77,10.67 -10.67,10.67c-5.89,0 -10.66,-4.78 -10.66,-10.67s4.77,-10.66 10.66,-10.66C261.89,234.67 266.66,239.44 266.66,245.33z"
|
||||
android:fillColor="#434A54"/>
|
||||
<path
|
||||
android:pathData="M74.66,234.67H53.33c-5.89,0 -10.66,4.77 -10.66,10.66s4.77,10.67 10.66,10.67h21.34c5.89,0 10.66,-4.78 10.66,-10.67S80.56,234.67 74.66,234.67z"
|
||||
android:fillColor="#434A54"/>
|
||||
<path
|
||||
android:pathData="M250.41,20.5c126.89,0 229.75,102.86 229.75,229.75c0,126.89 -102.86,229.75 -229.75,229.75c-126.89,0 -229.75,-102.86 -229.75,-229.75C20.66,123.36 123.53,20.5 250.41,20.5ZM250.85,161.82c-49.09,0 -88.88,39.79 -88.88,88.88c0,49.09 39.79,88.88 88.88,88.88c49.09,0 88.88,-39.79 88.88,-88.88c0,-49.09 -39.79,-88.88 -88.88,-88.88Z">
|
||||
<aapt:attr name="android:fillColor">
|
||||
<gradient
|
||||
android:startX="116.21"
|
||||
android:startY="67.61"
|
||||
android:endX="403.29"
|
||||
android:endY="429.34"
|
||||
android:type="linear">
|
||||
<item android:offset="0.0" android:color="#66060606" />
|
||||
<item android:offset="1.0" android:color="#CC060606" />
|
||||
</gradient>
|
||||
</aapt:attr>
|
||||
</path>
|
||||
|
||||
<path
|
||||
android:pathData="M453.23,307.8c-18.5,72.24 -73.8,129.26 -144.2,148.92l-36.39,-138.74c21.97,-7.21 39.22,-24.84 45.88,-47.06l134.71,36.88Z">
|
||||
<aapt:attr name="android:fillColor">
|
||||
<gradient
|
||||
android:startX="420.63"
|
||||
android:startY="403.74"
|
||||
android:endX="78.4"
|
||||
android:endY="117.92"
|
||||
android:type="linear">
|
||||
<item android:offset="0.0" android:color="#33FFFFFF" />
|
||||
<item android:offset="1.0" android:color="#4DFFFFFF" />
|
||||
</gradient>
|
||||
</aapt:attr>
|
||||
</path>
|
||||
|
||||
<path
|
||||
android:pathData="M228.3,183.04c-21.73,7.15 -38.82,24.5 -45.62,46.39L47.5,192.42c18.5,-72.24 73.8,-129.26 144.2,-148.92l36.6,139.54Z">
|
||||
<aapt:attr name="android:fillColor">
|
||||
<gradient
|
||||
android:startX="420.63"
|
||||
android:startY="403.74"
|
||||
android:endX="78.4"
|
||||
android:endY="117.92"
|
||||
android:type="linear">
|
||||
<item android:offset="0.0" android:color="#33FFFFFF" />
|
||||
<item android:offset="1.0" android:color="#4DFFFFFF" />
|
||||
</gradient>
|
||||
</aapt:attr>
|
||||
</path>
|
||||
|
||||
<path
|
||||
android:fillColor="#66FFFFFF"
|
||||
android:pathData="M250.5,179.5c39.21,0 71,31.79 71,71s-31.79,71 -71,71s-71,-31.79 -71,-71s31.79,-71 71,-71ZM250,235c-8.28,0 -15,6.72 -15,15c0,8.28 6.72,15 15,15c8.28,0 15,-6.72 15,-15c0,-8.28 -6.72,-15 -15,-15Z" />
|
||||
</group>
|
||||
</vector>
|
||||
@@ -1,51 +1,78 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="800dp"
|
||||
android:height="800dp"
|
||||
android:viewportWidth="512"
|
||||
android:viewportHeight="512">
|
||||
<path
|
||||
android:pathData="M512,437.33c0,11.78 -9.56,21.34 -21.34,21.34H21.33C9.55,458.67 0,449.11 0,437.33V96c0,-11.78 9.55,-21.33 21.33,-21.33h469.33c11.78,0 21.34,9.55 21.34,21.33L512,437.33L512,437.33z"
|
||||
android:fillColor="#DA4453"/>
|
||||
<path
|
||||
android:pathData="M512,416.01c0,11.78 -9.56,21.31 -21.34,21.31H21.33C9.55,437.33 0,427.8 0,416.01V74.67c0,-11.78 9.55,-21.34 21.33,-21.34h469.33c11.78,0 21.34,9.56 21.34,21.34L512,416.01L512,416.01z"
|
||||
android:fillColor="#ED5564"/>
|
||||
<path
|
||||
android:pathData="M63.99,160c-5.89,0 -10.66,4.78 -10.66,10.67v149.34c0,5.88 4.77,10.66 10.66,10.66c5.89,0 10.67,-4.78 10.67,-10.66V170.67C74.66,164.78 69.88,160 63.99,160z"
|
||||
android:fillColor="#DA4453"/>
|
||||
<path
|
||||
android:pathData="M74.66,106.67c0,5.89 -4.78,10.66 -10.67,10.66c-5.89,0 -10.66,-4.77 -10.66,-10.66S58.1,96 63.99,96C69.88,96 74.66,100.78 74.66,106.67z"
|
||||
android:fillColor="#E6E9ED"/>
|
||||
<path
|
||||
android:pathData="M74.66,384.01c0,5.88 -4.78,10.66 -10.67,10.66c-5.89,0 -10.66,-4.78 -10.66,-10.66c0,-5.91 4.77,-10.69 10.66,-10.69C69.88,373.33 74.66,378.11 74.66,384.01z"
|
||||
android:fillColor="#E6E9ED"/>
|
||||
<path
|
||||
android:pathData="M448,123.73h-21.34v203.19l-40.31,50.41v0.02c-1.47,1.83 -2.34,4.14 -2.34,6.67c0,5.88 4.78,10.66 10.66,10.66c3.38,0 6.38,-1.56 8.33,-4h0.02l42.66,-53.34l0,0c1.47,-1.81 2.34,-4.13 2.34,-6.66V123.73z"
|
||||
android:fillColor="#E6E9ED"/>
|
||||
<path
|
||||
android:pathData="M437.33,149.33c-11.77,0 -21.33,-9.56 -21.33,-21.33s9.56,-21.33 21.33,-21.33s21.33,9.56 21.33,21.33S449.09,149.33 437.33,149.33z"
|
||||
android:fillColor="#E6E9ED"/>
|
||||
<path
|
||||
android:pathData="M437.33,96c-17.67,0 -32,14.33 -32,32s14.33,32 32,32s32,-14.33 32,-32S455,96 437.33,96zM437.33,138.67c-5.89,0 -10.67,-4.8 -10.67,-10.67c0,-5.88 4.78,-10.67 10.67,-10.67s10.67,4.8 10.67,10.67C448,133.88 443.22,138.67 437.33,138.67z"
|
||||
android:fillColor="#CCD1D9"/>
|
||||
<path
|
||||
android:pathData="M405.33,245.33c0,82.48 -66.86,149.34 -149.33,149.34c-82.47,0 -149.33,-66.86 -149.33,-149.34C106.66,162.86 173.52,96 255.99,96C338.47,96 405.33,162.86 405.33,245.33z"
|
||||
android:fillColor="#434A54"/>
|
||||
<path
|
||||
android:pathData="M266.66,149.33c0,-5.89 -4.77,-10.66 -10.67,-10.66c-58.91,0 -106.66,47.75 -106.66,106.65l0,0c0,5.89 4.77,10.67 10.66,10.67s10.67,-4.78 10.67,-10.67l0,0c0,-22.78 8.88,-44.22 24.99,-60.33c16.12,-16.13 37.55,-25 60.34,-25C261.89,160 266.66,155.22 266.66,149.33z"
|
||||
android:fillColor="#656D78"/>
|
||||
<path
|
||||
android:pathData="M352,234.67c-5.9,0 -10.67,4.77 -10.67,10.66l0,0c0,22.8 -8.88,44.23 -24.98,60.34c-16.13,16.13 -37.56,25 -60.35,25c-5.89,0 -10.66,4.78 -10.66,10.66c0,5.91 4.77,10.69 10.66,10.69c58.91,0 106.66,-47.77 106.66,-106.69C362.65,239.44 357.89,234.67 352,234.67z"
|
||||
android:fillColor="#656D78"/>
|
||||
<path
|
||||
android:pathData="M255.99,288.01c-23.52,0 -42.66,-19.16 -42.66,-42.69c0,-23.52 19.14,-42.66 42.66,-42.66c23.54,0 42.66,19.14 42.66,42.66C298.65,268.86 279.53,288.01 255.99,288.01z"
|
||||
android:fillColor="#FFCE54"/>
|
||||
<path
|
||||
android:pathData="M255.99,192c-29.45,0 -53.33,23.88 -53.33,53.33s23.88,53.34 53.33,53.34c29.46,0 53.34,-23.89 53.34,-53.34S285.45,192 255.99,192zM255.99,277.34c-17.64,0 -32,-14.36 -32,-32.02c0,-17.64 14.36,-32 32,-32c17.65,0 32.01,14.36 32.01,32C288,262.98 273.64,277.34 255.99,277.34z"
|
||||
android:fillColor="#F6BB42"/>
|
||||
<path
|
||||
android:pathData="M266.66,245.33c0,5.89 -4.77,10.67 -10.67,10.67c-5.89,0 -10.66,-4.78 -10.66,-10.67s4.77,-10.66 10.66,-10.66C261.89,234.67 266.66,239.44 266.66,245.33z"
|
||||
android:fillColor="#434A54"/>
|
||||
<path
|
||||
android:pathData="M74.66,234.67H53.33c-5.89,0 -10.66,4.77 -10.66,10.66s4.77,10.67 10.66,10.67h21.34c5.89,0 10.66,-4.78 10.66,-10.67S80.56,234.67 74.66,234.67z"
|
||||
android:fillColor="#434A54"/>
|
||||
</vector>
|
||||
xmlns:aapt="http://schemas.android.com/aapt"
|
||||
android:width="108dp"
|
||||
android:height="108dp"
|
||||
android:viewportWidth="108"
|
||||
android:viewportHeight="108">
|
||||
|
||||
<group
|
||||
android:scaleX="0.16"
|
||||
android:scaleY="0.16"
|
||||
android:translateX="14.0"
|
||||
android:translateY="14.0">
|
||||
|
||||
<path
|
||||
android:pathData="M250,0c138.07,0 250,111.93 250,250S388.07,500 250,500 0,388.07 0,250 111.93,0 250,0ZM250,235c-8.28,0 -15,6.72 -15,15c0,8.28 6.72,15 15,15c8.28,0 15,-6.72 15,-15c0,-8.28 -6.72,-15 -15,-15Z">
|
||||
<aapt:attr name="android:fillColor">
|
||||
<gradient
|
||||
android:startX="122.34"
|
||||
android:startY="23.55"
|
||||
android:endX="377.69"
|
||||
android:endY="465.83"
|
||||
android:type="linear">
|
||||
<item android:offset="0.0" android:color="#FFEC4A4A" />
|
||||
<item android:offset="1.0" android:color="#FFEC4A4A" />
|
||||
</gradient>
|
||||
</aapt:attr>
|
||||
</path>
|
||||
|
||||
<path
|
||||
android:pathData="M250.41,20.5c126.89,0 229.75,102.86 229.75,229.75c0,126.89 -102.86,229.75 -229.75,229.75c-126.89,0 -229.75,-102.86 -229.75,-229.75C20.66,123.36 123.53,20.5 250.41,20.5ZM250.85,161.82c-49.09,0 -88.88,39.79 -88.88,88.88c0,49.09 39.79,88.88 88.88,88.88c49.09,0 88.88,-39.79 88.88,-88.88c0,-49.09 -39.79,-88.88 -88.88,-88.88Z">
|
||||
<aapt:attr name="android:fillColor">
|
||||
<gradient
|
||||
android:startX="116.21"
|
||||
android:startY="67.61"
|
||||
android:endX="403.29"
|
||||
android:endY="429.34"
|
||||
android:type="linear">
|
||||
<item android:offset="0.0" android:color="#66060606" />
|
||||
<item android:offset="1.0" android:color="#CC060606" />
|
||||
</gradient>
|
||||
</aapt:attr>
|
||||
</path>
|
||||
|
||||
<path
|
||||
android:pathData="M453.23,307.8c-18.5,72.24 -73.8,129.26 -144.2,148.92l-36.39,-138.74c21.97,-7.21 39.22,-24.84 45.88,-47.06l134.71,36.88Z">
|
||||
<aapt:attr name="android:fillColor">
|
||||
<gradient
|
||||
android:startX="420.63"
|
||||
android:startY="403.74"
|
||||
android:endX="78.4"
|
||||
android:endY="117.92"
|
||||
android:type="linear">
|
||||
<item android:offset="0.0" android:color="#33FFFFFF" />
|
||||
<item android:offset="1.0" android:color="#4DFFFFFF" />
|
||||
</gradient>
|
||||
</aapt:attr>
|
||||
</path>
|
||||
|
||||
<path
|
||||
android:pathData="M228.3,183.04c-21.73,7.15 -38.82,24.5 -45.62,46.39L47.5,192.42c18.5,-72.24 73.8,-129.26 144.2,-148.92l36.6,139.54Z">
|
||||
<aapt:attr name="android:fillColor">
|
||||
<gradient
|
||||
android:startX="420.63"
|
||||
android:startY="403.74"
|
||||
android:endX="78.4"
|
||||
android:endY="117.92"
|
||||
android:type="linear">
|
||||
<item android:offset="0.0" android:color="#33FFFFFF" />
|
||||
<item android:offset="1.0" android:color="#4DFFFFFF" />
|
||||
</gradient>
|
||||
</aapt:attr>
|
||||
</path>
|
||||
|
||||
<path
|
||||
android:fillColor="#66FFFFFF"
|
||||
android:pathData="M250.5,179.5c39.21,0 71,31.79 71,71s-31.79,71 -71,71s-71,-31.79 -71,-71s31.79,-71 71,-71ZM250,235c-8.28,0 -15,6.72 -15,15c0,8.28 6.72,15 15,15c8.28,0 15,-6.72 15,-15c0,-8.28 -6.72,-15 -15,-15Z" />
|
||||
</group>
|
||||
</vector>
|
||||
@@ -47,6 +47,19 @@
|
||||
app:behavior_hideable="true"
|
||||
app:behavior_peekHeight="@dimen/bottom_sheet_peek_height"
|
||||
app:layout_behavior="@string/bottom_sheet_behavior" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/offline_mode_text_view"
|
||||
style="@style/NoConnectionTextView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center"
|
||||
android:layout_gravity="top"
|
||||
android:padding="2dp"
|
||||
android:text="@string/activity_info_offline_mode"
|
||||
android:textSize="11sp"
|
||||
android:visibility="gone" />
|
||||
|
||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||
|
||||
<com.google.android.material.navigation.NavigationView
|
||||
@@ -57,13 +70,4 @@
|
||||
app:menu="@menu/nav_drawer"
|
||||
app:headerLayout="@layout/nav_drawer_header" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/offline_mode_text_view"
|
||||
style="@style/NoConnectionTextView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center"
|
||||
android:text="@string/activity_info_offline_mode"
|
||||
android:textSize="6sp"
|
||||
android:visibility="gone" />
|
||||
</androidx.drawerlayout.widget.DrawerLayout>
|
||||
|
||||
@@ -52,7 +52,7 @@
|
||||
android:focusable="true"
|
||||
android:text="Unknown"
|
||||
app:chipStrokeWidth="0dp"
|
||||
app:chipBackgroundColor="@color/material_dynamic_secondary40"
|
||||
app:chipBackgroundColor="?attr/colorSecondaryContainer"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.drawerlayout.widget.DrawerLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
<androidx.drawerlayout.widget.DrawerLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:id="@+id/drawer_layout"
|
||||
android:layout_width="match_parent"
|
||||
@@ -37,6 +36,17 @@
|
||||
android:paddingEnd="24dp"
|
||||
app:menu="@menu/bottom_nav_menu" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/offline_mode_text_view"
|
||||
style="@style/NoConnectionTextView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center"
|
||||
android:layout_gravity="top"
|
||||
android:padding="2dp"
|
||||
android:text="@string/activity_info_offline_mode"
|
||||
android:textSize="11sp"
|
||||
android:visibility="gone" />
|
||||
|
||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||
|
||||
@@ -60,13 +70,4 @@
|
||||
app:menu="@menu/nav_drawer"
|
||||
app:headerLayout="@layout/nav_drawer_header" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/offline_mode_text_view"
|
||||
style="@style/NoConnectionTextView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center"
|
||||
android:text="@string/activity_info_offline_mode"
|
||||
android:textSize="6sp"
|
||||
android:visibility="gone" />
|
||||
</androidx.drawerlayout.widget.DrawerLayout>
|
||||
|
||||
@@ -200,4 +200,4 @@
|
||||
android:text="@string/album_bottom_sheet_share"
|
||||
android:visibility="gone"/>
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
|
||||
@@ -92,4 +92,4 @@
|
||||
android:paddingBottom="12dp"
|
||||
android:text="@string/artist_bottom_sheet_shuffle" />
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
|
||||
130
app/src/main/res/layout/bottom_sheet_playlist_dialog.xml
Normal file
@@ -0,0 +1,130 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="20dp"
|
||||
android:layout_marginTop="12dp"
|
||||
android:layout_marginEnd="12dp"
|
||||
android:clipChildren="false">
|
||||
|
||||
<!-- Header -->
|
||||
<ImageView
|
||||
android:id="@+id/playlist_cover_image_view"
|
||||
android:layout_width="54dp"
|
||||
android:layout_height="54dp"
|
||||
android:layout_margin="2dp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<ToggleButton
|
||||
android:id="@+id/button_favorite"
|
||||
android:layout_width="24dp"
|
||||
android:layout_height="24dp"
|
||||
android:layout_marginTop="8dp"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:background="@drawable/button_favorite_selector"
|
||||
android:checked="false"
|
||||
android:foreground="?android:attr/selectableItemBackgroundBorderless"
|
||||
android:gravity="center_vertical"
|
||||
android:text=""
|
||||
android:textOff=""
|
||||
android:textOn=""
|
||||
android:visibility="gone"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/playlist_title_text_view"
|
||||
style="@style/LabelMedium"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
android:ellipsize="marquee"
|
||||
android:paddingStart="12dp"
|
||||
android:paddingEnd="12dp"
|
||||
android:singleLine="true"
|
||||
android:text="@string/label_placeholder"
|
||||
app:layout_constraintEnd_toStartOf="@id/button_favorite"
|
||||
app:layout_constraintStart_toEndOf="@+id/playlist_cover_image_view"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/playlist_count_text_view"
|
||||
style="@style/LabelSmall"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingStart="12dp"
|
||||
android:paddingEnd="12dp"
|
||||
android:text="@string/label_placeholder"
|
||||
app:layout_constraintEnd_toStartOf="@id/button_favorite"
|
||||
app:layout_constraintStart_toEndOf="@+id/playlist_cover_image_view"
|
||||
app:layout_constraintTop_toBottomOf="@+id/playlist_title_text_view" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
<include
|
||||
android:id="@+id/song_asset_link_row"
|
||||
layout="@layout/view_asset_link_row"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingStart="20dp"
|
||||
android:paddingEnd="12dp" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/option_linear_layout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:paddingTop="8dp"
|
||||
android:paddingBottom="12dp"
|
||||
app:layout_constraintLeft_toLeftOf="parent"
|
||||
app:layout_constraintRight_toRightOf="parent">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/play_next_text_view"
|
||||
style="@style/LabelMedium"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="?attr/selectableItemBackground"
|
||||
android:clickable="true"
|
||||
android:paddingStart="20dp"
|
||||
android:paddingTop="12dp"
|
||||
android:paddingEnd="20dp"
|
||||
android:paddingBottom="12dp"
|
||||
android:text="@string/song_bottom_sheet_play_next" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/add_to_queue_text_view"
|
||||
style="@style/LabelMedium"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="?attr/selectableItemBackground"
|
||||
android:clickable="true"
|
||||
android:paddingStart="20dp"
|
||||
android:paddingTop="12dp"
|
||||
android:paddingEnd="20dp"
|
||||
android:paddingBottom="12dp"
|
||||
android:text="@string/song_bottom_sheet_add_to_queue" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/share_text_view"
|
||||
style="@style/LabelMedium"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="?attr/selectableItemBackground"
|
||||
android:clickable="true"
|
||||
android:paddingStart="20dp"
|
||||
android:paddingTop="12dp"
|
||||
android:paddingEnd="20dp"
|
||||
android:paddingBottom="12dp"
|
||||
android:text="@string/song_bottom_sheet_share"
|
||||
android:visibility="gone"/>
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
@@ -330,13 +330,13 @@
|
||||
</LinearLayout>
|
||||
|
||||
<!-- slideview -->
|
||||
<androidx.viewpager2.widget.ViewPager2
|
||||
android:id="@+id/discover_song_view_pager"
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/discover_song_recycler_view"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="212dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:clipToPadding="false"
|
||||
android:paddingTop="8dp"
|
||||
android:paddingBottom="16dp" />
|
||||
android:paddingBottom="8dp" />
|
||||
</LinearLayout>
|
||||
|
||||
<!-- Similar tracks -->
|
||||
|
||||
@@ -30,13 +30,54 @@
|
||||
android:orientation="vertical"
|
||||
android:paddingBottom="@dimen/global_padding_bottom">
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/library_song_sector"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
tools:visibility="visible">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:paddingStart="8dp"
|
||||
android:paddingTop="16dp"
|
||||
android:paddingEnd="8dp"
|
||||
android:paddingBottom="8dp">
|
||||
|
||||
<TextView
|
||||
style="@style/TitleLarge"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingStart="8dp"
|
||||
android:paddingEnd="8dp"
|
||||
android:text="@string/library_title_song" />
|
||||
|
||||
<View
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight="1" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/song_catalogue_text_view_clickable"
|
||||
style="@style/TitleMedium"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="end|center_vertical"
|
||||
android:paddingStart="8dp"
|
||||
android:paddingEnd="8dp"
|
||||
android:text="@string/library_title_song_see_all_button" />
|
||||
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
|
||||
<!-- Music Folder -->
|
||||
<LinearLayout
|
||||
android:id="@+id/library_music_folder_sector"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:paddingBottom="8dp"
|
||||
android:visibility="gone"
|
||||
tools:visibility="visible">
|
||||
|
||||
@@ -356,4 +397,4 @@
|
||||
</LinearLayout>
|
||||
</androidx.core.widget.NestedScrollView>
|
||||
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
|
||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||
|
||||
@@ -108,6 +108,22 @@
|
||||
android:clipToPadding="false"
|
||||
android:paddingTop="8dp"
|
||||
android:paddingBottom="8dp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/allSongs"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingStart="16dp"
|
||||
android:text="@string/search_all_songs_loading"
|
||||
android:textAppearance="@style/TextAppearance.AppCompat.Small"
|
||||
android:textSize="16sp"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/allsongsview"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent" />
|
||||
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
</androidx.core.widget.NestedScrollView>
|
||||
@@ -146,4 +162,4 @@
|
||||
android:orientation="vertical"/>
|
||||
</ScrollView>
|
||||
</com.google.android.material.search.SearchView>
|
||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||
|
||||
@@ -1,6 +1,22 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
<LinearLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:orientation="vertical"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:paddingTop="20dp"
|
||||
android:paddingBottom="@dimen/global_padding_bottom" />
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<com.google.android.material.appbar.MaterialToolbar
|
||||
android:id="@+id/settings_toolbar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="?attr/actionBarSize"
|
||||
android:background="?attr/colorSurface"
|
||||
app:layout_collapseMode="pin"
|
||||
app:navigationIcon="@drawable/ic_arrow_back" />
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/settings_container"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight="1"/>
|
||||
</LinearLayout>
|
||||
|
||||
@@ -61,7 +61,7 @@
|
||||
android:focusable="true"
|
||||
android:text="Unknown"
|
||||
app:chipStrokeWidth="0dp"
|
||||
app:chipBackgroundColor="@color/material_dynamic_secondary40"
|
||||
app:chipBackgroundColor="?attr/colorSecondaryContainer"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:paddingHorizontal="16dp">
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingStart="8dp">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/discover_song_cover_image_view"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_width="344dp"
|
||||
android:layout_height="172dp"
|
||||
android:scaleType="centerCrop"
|
||||
android:pivotX="50%"
|
||||
android:pivotY="50%"
|
||||
@@ -16,30 +17,34 @@
|
||||
|
||||
<TextView
|
||||
android:id="@+id/title_discover_song_label"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="16dp"
|
||||
android:layout_marginTop="18dp"
|
||||
android:layout_marginRight="16dp"
|
||||
android:layout_marginTop="4dp"
|
||||
android:layout_marginLeft="4dp"
|
||||
android:layout_marginRight="4dp"
|
||||
android:ellipsize="end"
|
||||
android:fontFamily="@font/inter"
|
||||
android:maxLines="2"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="@id/discover_song_cover_image_view"
|
||||
android:textColor="@color/gradientTitleColor"
|
||||
android:textFontWeight="400"
|
||||
android:textSize="20sp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/album_discover_song_label"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_below="@id/title_discover_song_label"
|
||||
android:layout_marginLeft="16dp"
|
||||
android:layout_marginRight="16dp"
|
||||
android:drawablePadding="10dp"
|
||||
android:layout_marginLeft="4dp"
|
||||
android:layout_marginRight="4dp"
|
||||
android:ellipsize="end"
|
||||
android:fontFamily="@font/inter"
|
||||
android:maxLines="1"
|
||||
android:maxLines="2"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/title_discover_song_label"
|
||||
android:textColor="@color/gradientSubtitleColor"
|
||||
android:textFontWeight="400"
|
||||
android:textSize="14sp" />
|
||||
</RelativeLayout>
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
@@ -4,13 +4,13 @@
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:paddingEnd="12dp"
|
||||
android:paddingBottom="8dp">
|
||||
android:paddingEnd="8dp"
|
||||
android:paddingBottom="4dp">
|
||||
|
||||
<androidx.cardview.widget.CardView
|
||||
android:id="@+id/card_view"
|
||||
android:layout_width="172dp"
|
||||
android:layout_height="72dp"
|
||||
android:layout_width="120dp"
|
||||
android:layout_height="54dp"
|
||||
android:layout_gravity="center"
|
||||
card_view:cardCornerRadius="4dp"
|
||||
card_view:cardElevation="2dp"
|
||||
|
||||
@@ -225,4 +225,4 @@
|
||||
android:background="@drawable/ic_more_vert"
|
||||
android:foreground="?android:attr/selectableItemBackgroundBorderless" />
|
||||
</FrameLayout>
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
|
||||
<androidx.cardview.widget.CardView xmlns:card_view="http://schemas.android.com/apk/res-auto"
|
||||
android:id="@+id/card_view"
|
||||
android:layout_width="196dp"
|
||||
android:layout_width="172dp"
|
||||
android:layout_height="54dp"
|
||||
android:layout_gravity="center"
|
||||
card_view:cardCornerRadius="4dp"
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<background android:drawable="@color/ic_launcher_background"/>
|
||||
<foreground android:drawable="@drawable/ic_launcher_foreground"/>
|
||||
</adaptive-icon>
|
||||
<background android:drawable="@drawable/ic_launcher_background_tempor_b"/>
|
||||
<foreground android:drawable="@drawable/ic_launcher_foreground_tempor_b"/>
|
||||
<monochrome android:drawable="@drawable/ic_launcher_monochrome_tempor_b"/>
|
||||
</adaptive-icon>
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<background android:drawable="@color/ic_launcher_background"/>
|
||||
<foreground android:drawable="@drawable/ic_launcher_foreground"/>
|
||||
</adaptive-icon>
|
||||
|
Before Width: | Height: | Size: 1.5 KiB |
|
Before Width: | Height: | Size: 3.1 KiB |
|
Before Width: | Height: | Size: 1.1 KiB |
|
Before Width: | Height: | Size: 1.9 KiB |
|
Before Width: | Height: | Size: 2.1 KiB |
|
Before Width: | Height: | Size: 3.1 KiB |
|
Before Width: | Height: | Size: 6.6 KiB |
|
Before Width: | Height: | Size: 4.1 KiB |
|
Before Width: | Height: | Size: 9.0 KiB |
@@ -329,6 +329,11 @@
|
||||
android:name="com.cappielloantonio.tempo.ui.fragment.bottomsheetdialog.SongBottomSheetDialog"
|
||||
android:label="SongBottomSheetDialog"
|
||||
tools:layout="@layout/bottom_sheet_song_dialog" />
|
||||
<dialog
|
||||
android:id="@+id/playlistBottomSheetDialog"
|
||||
android:name="com.cappielloantonio.tempo.ui.fragment.bottomsheetdialog.PlaylistBottomSheetDialog"
|
||||
android:label="PlaylistBottomSheetDialog"
|
||||
tools:layout="@layout/bottom_sheet_playlist_dialog" />
|
||||
<dialog
|
||||
android:id="@+id/artistBottomSheetDialog"
|
||||
android:name="com.cappielloantonio.tempo.ui.fragment.bottomsheetdialog.ArtistBottomSheetDialog"
|
||||
|
||||