├── .github
├── ISSUE_TEMPLATE
│ ├── bug_report.md
│ └── feature_request.md
└── workflows
│ └── release_workflow.yml
├── .gitignore
├── .idea
├── CrowdinSettingsPlugin.xml
├── appInsightsSettings.xml
├── codeStyles
│ ├── Project.xml
│ └── codeStyleConfig.xml
├── compiler.xml
├── deploymentTargetSelector.xml
├── gradle.xml
├── inspectionProfiles
│ └── Project_Default.xml
├── kotlinc.xml
├── migrations.xml
├── misc.xml
├── runConfigurations.xml
├── studiobot.xml
└── vcs.xml
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── LICENSE
├── README.md
├── SECURITY.md
├── ads
├── .gitignore
├── build.gradle.kts
├── consumer-rules.pro
├── proguard-rules.pro
└── src
│ ├── androidTest
│ └── java
│ │ └── com
│ │ └── zs
│ │ └── ads
│ │ └── ExampleInstrumentedTest.kt
│ ├── main
│ ├── AndroidManifest.xml
│ └── java
│ │ └── com
│ │ └── zs
│ │ └── ads
│ │ ├── AdData.kt
│ │ ├── AdEventListener.kt
│ │ ├── AdManager.kt
│ │ ├── AdManagerImpl.kt
│ │ ├── AdSize.kt
│ │ └── Reward.kt
│ └── test
│ └── java
│ └── com
│ └── zs
│ └── ads
│ └── ExampleUnitTest.kt
├── app
├── .gitignore
├── build.gradle.kts
├── google-services.json
├── proguard-rules.pro
└── src
│ ├── androidTest
│ └── java
│ │ └── com
│ │ └── prime
│ │ └── media
│ │ └── ExampleInstrumentedTest.kt
│ ├── main
│ ├── AndroidManifest.xml
│ ├── ic_launcher-playstore.png
│ ├── java
│ │ └── com
│ │ │ └── prime
│ │ │ └── media
│ │ │ ├── Home.kt
│ │ │ ├── MainActivity.kt
│ │ │ ├── about
│ │ │ ├── AboutUs.kt
│ │ │ └── MyApps.kt
│ │ │ ├── common
│ │ │ ├── Backdrop.kt
│ │ │ ├── Banner.kt
│ │ │ ├── BannerTopBar.kt
│ │ │ ├── Chronometer.kt
│ │ │ ├── Delegates.kt
│ │ │ ├── FloatingActionMenu.kt
│ │ │ ├── MetaData.kt
│ │ │ ├── Route.kt
│ │ │ ├── SelectionTracker.kt
│ │ │ ├── SystemFacade.kt
│ │ │ ├── Util.kt
│ │ │ ├── lazy_ktx.kt
│ │ │ └── menu
│ │ │ │ └── Action.kt
│ │ │ ├── impl
│ │ │ ├── AbstractLocalDirViewModel.kt
│ │ │ ├── Initializers.kt
│ │ │ ├── KoinViewModel.kt
│ │ │ ├── PersonalizeViewModel.kt
│ │ │ ├── PlaylistViewModel.kt
│ │ │ ├── PlaylistsViewModel.kt
│ │ │ ├── SettingsViewModel.kt
│ │ │ └── VideosViewModel.kt
│ │ │ ├── local
│ │ │ ├── Albums.kt
│ │ │ ├── Artists.kt
│ │ │ ├── Directory.kt
│ │ │ ├── DirectoryViewState.kt
│ │ │ ├── Folders.kt
│ │ │ ├── Genres.kt
│ │ │ └── videos
│ │ │ │ ├── Video.kt
│ │ │ │ ├── Videos.kt
│ │ │ │ └── VideosViewState.kt
│ │ │ ├── old
│ │ │ ├── common
│ │ │ │ ├── Delegates.kt
│ │ │ │ ├── Util.kt
│ │ │ │ ├── View.kt
│ │ │ │ └── util
│ │ │ │ │ ├── DateUtils.kt
│ │ │ │ │ ├── PathUtils.kt
│ │ │ │ │ └── Util.kt
│ │ │ ├── console
│ │ │ │ ├── ConsoleView.kt
│ │ │ │ ├── Constraints.kt
│ │ │ │ ├── Dialogs.kt
│ │ │ │ ├── PlayingQueue.kt
│ │ │ │ └── ViewState.kt
│ │ │ ├── core
│ │ │ │ ├── db
│ │ │ │ │ └── ContentResolver.kt
│ │ │ │ └── playback
│ │ │ │ │ ├── Delegates.kt
│ │ │ │ │ └── Remote.kt
│ │ │ ├── dialog
│ │ │ │ └── Properties.kt
│ │ │ ├── directory
│ │ │ │ ├── Action.kt
│ │ │ │ ├── Directory.kt
│ │ │ │ ├── DirectoryViewModel.kt
│ │ │ │ ├── Metadata.kt
│ │ │ │ ├── dialogs
│ │ │ │ │ └── Playlists.kt
│ │ │ │ ├── playlists
│ │ │ │ │ └── Members.kt
│ │ │ │ └── store
│ │ │ │ │ └── Audios.kt
│ │ │ ├── editor
│ │ │ │ ├── Editor.kt
│ │ │ │ └── ViewState.kt
│ │ │ ├── effects
│ │ │ │ ├── AudioFx.kt
│ │ │ │ └── ViewState.kt
│ │ │ ├── feedback
│ │ │ │ ├── Feedback.kt
│ │ │ │ └── FeedbackViewState.kt
│ │ │ ├── impl
│ │ │ │ ├── AudioFxViewModel.kt
│ │ │ │ ├── ConsoleViewModel.kt
│ │ │ │ ├── FeedbackViewModel.kt
│ │ │ │ ├── LibraryViewModel.kt
│ │ │ │ ├── Remote.kt
│ │ │ │ ├── Repository.kt
│ │ │ │ ├── SystemDelegate.kt
│ │ │ │ └── TagEditorViewModel.kt
│ │ │ └── library
│ │ │ │ ├── Library.kt
│ │ │ │ ├── LibraryStateFulList.kt
│ │ │ │ ├── NewlyAddedList.kt
│ │ │ │ ├── Promotions.kt
│ │ │ │ ├── RecentList.kt
│ │ │ │ ├── Shortcuts.kt
│ │ │ │ ├── TopAppBar.kt
│ │ │ │ └── ViewState.kt
│ │ │ ├── personalize
│ │ │ ├── FineTune.kt
│ │ │ ├── Personalize.kt
│ │ │ ├── PersonalizeViewState.kt
│ │ │ ├── Tweaks.kt
│ │ │ ├── Upgrades.kt
│ │ │ └── WIdgets.kt
│ │ │ ├── playlists
│ │ │ ├── NewPlaylist.kt
│ │ │ ├── Playlist.kt
│ │ │ ├── PlaylistItem.kt
│ │ │ ├── Playlists.kt
│ │ │ └── PlaylistsViewState.kt
│ │ │ ├── settings
│ │ │ ├── Settings.kt
│ │ │ └── SettingsViewState.kt
│ │ │ └── widget
│ │ │ ├── DiskDynamo.kt
│ │ │ ├── ElongatedBeat.kt
│ │ │ ├── Glance.kt
│ │ │ ├── GoldenDust.kt
│ │ │ ├── GradientGroves.kt
│ │ │ ├── Iphone.kt
│ │ │ ├── MiniLayout.kt
│ │ │ ├── MistyDream.kt
│ │ │ ├── RedVioletCake.kt
│ │ │ ├── RotatingGradient.kt
│ │ │ ├── SkewedDynamic.kt
│ │ │ ├── SnowCone.kt
│ │ │ ├── Tiramisu.kt
│ │ │ └── WavyGradeintDots.kt
│ └── res
│ │ ├── drawable-v24
│ │ ├── ic_launcher_foreground.xml
│ │ └── ic_splash.xml
│ │ ├── drawable
│ │ ├── avd_repeat_more_one_all.xml
│ │ ├── default_art.png
│ │ ├── ic_artist.xml
│ │ ├── ic_branded_image.png
│ │ ├── ic_remove_ads.xml
│ │ ├── ic_splash.png
│ │ ├── media3_notification_pause.xml
│ │ └── media3_notification_play.xml
│ │ ├── mipmap-anydpi-v26
│ │ ├── ic_launcher.xml
│ │ └── ic_launcher_round.xml
│ │ ├── mipmap-hdpi
│ │ ├── ic_launcher.webp
│ │ └── ic_launcher_round.webp
│ │ ├── mipmap-mdpi
│ │ ├── ic_launcher.webp
│ │ └── ic_launcher_round.webp
│ │ ├── mipmap-xhdpi
│ │ ├── ic_launcher.webp
│ │ └── ic_launcher_round.webp
│ │ ├── mipmap-xxhdpi
│ │ ├── ic_launcher.webp
│ │ └── ic_launcher_round.webp
│ │ ├── mipmap-xxxhdpi
│ │ ├── ic_launcher.webp
│ │ └── ic_launcher_round.webp
│ │ ├── raw
│ │ ├── bg_blur_color_blob.lottie
│ │ ├── bg_dark_wavy_lines.lottie
│ │ ├── bg_fading_streight_lines.lottie
│ │ ├── bg_gradeint_dots.lottie
│ │ ├── bg_rotating_color_gradient.lottie
│ │ ├── loading_hand.lottie
│ │ ├── lt_audio_waves.json
│ │ ├── lt_bg_baloon_in_air.lottie
│ │ ├── lt_bg_blur.lottie
│ │ ├── lt_bg_fluid_color.lottie
│ │ ├── lt_bg_snowflakes.lottie
│ │ ├── lt_color_loader.json
│ │ ├── lt_empty_box.json
│ │ ├── lt_golden_ticket.lottie
│ │ ├── lt_green_list_loading.lottie
│ │ ├── lt_list_loading_grey.lottie
│ │ ├── lt_list_loading_shimmer_2.lottie
│ │ ├── lt_loading_bubbles.json
│ │ ├── lt_loading_dots_blue.json
│ │ ├── lt_permission.json
│ │ ├── lt_play_pause.json
│ │ ├── lt_play_pause2.json
│ │ ├── lt_play_pause3.json
│ │ ├── lt_play_pause4.json
│ │ ├── lt_play_pause5.json
│ │ ├── lt_play_pause6.json
│ │ ├── lt_play_pause7.json
│ │ ├── lt_play_pause8.json
│ │ ├── lt_play_pause9.json
│ │ ├── lt_play_pause_circle_bordered.json
│ │ ├── lt_rounded_next_btn.json
│ │ ├── lt_settings_roll.json
│ │ ├── lt_shuffle_on_off.json
│ │ ├── lt_skip_to_next.json
│ │ ├── lt_skip_to_next_circular_bordered.json
│ │ ├── lt_twitter_heart_filled_unfilled.json
│ │ ├── play_pause_circle_bordered.lottie
│ │ ├── play_pause_filled.lottie
│ │ └── playback_indicator.json
│ │ ├── values-af-rZA
│ │ └── strings.xml
│ │ ├── values-ar-rSA
│ │ └── strings.xml
│ │ ├── values-ca-rES
│ │ └── strings.xml
│ │ ├── values-cs-rCZ
│ │ └── strings.xml
│ │ ├── values-da-rDK
│ │ └── strings.xml
│ │ ├── values-de-rDE
│ │ └── strings.xml
│ │ ├── values-el-rGR
│ │ └── strings.xml
│ │ ├── values-es-rES
│ │ └── strings.xml
│ │ ├── values-fi-rFI
│ │ └── strings.xml
│ │ ├── values-fr-rFR
│ │ └── strings.xml
│ │ ├── values-hi-rIN
│ │ └── strings.xml
│ │ ├── values-hu-rHU
│ │ └── strings.xml
│ │ ├── values-it-rIT
│ │ └── strings.xml
│ │ ├── values-iw-rIL
│ │ └── strings.xml
│ │ ├── values-ja-rJP
│ │ └── strings.xml
│ │ ├── values-ko-rKR
│ │ └── strings.xml
│ │ ├── values-nl-rNL
│ │ └── strings.xml
│ │ ├── values-no-rNO
│ │ └── strings.xml
│ │ ├── values-pl-rPL
│ │ └── strings.xml
│ │ ├── values-pt-rBR
│ │ └── strings.xml
│ │ ├── values-pt-rPT
│ │ └── strings.xml
│ │ ├── values-ro-rRO
│ │ └── strings.xml
│ │ ├── values-ru-rRU
│ │ └── strings.xml
│ │ ├── values-sr-rSP
│ │ └── strings.xml
│ │ ├── values-sv-rSE
│ │ └── strings.xml
│ │ ├── values-tr-rTR
│ │ └── strings.xml
│ │ ├── values-uk-rUA
│ │ └── strings.xml
│ │ ├── values-ur-rPK
│ │ └── strings.xml
│ │ ├── values-vi-rVN
│ │ └── strings.xml
│ │ ├── values-zh-rCN
│ │ └── strings.xml
│ │ ├── values-zh-rTW
│ │ └── strings.xml
│ │ └── values
│ │ ├── font_certs.xml
│ │ ├── ic_launcher_background.xml
│ │ ├── strings.xml
│ │ ├── theme.xml
│ │ └── what_s_new.xml
│ └── test
│ └── java
│ └── com
│ └── prime
│ └── media
│ └── ExampleUnitTest.kt
├── build.gradle.kts
├── codex
├── .gitignore
├── build.gradle.kts
└── src
│ ├── androidTest
│ └── java
│ │ └── com
│ │ └── prime
│ │ └── codex
│ │ └── ExampleInstrumentedTest.kt
│ ├── main
│ ├── AndroidManifest.xml
│ └── java
│ │ └── com
│ │ └── prime
│ │ └── codex
│ │ └── Codex.kt
│ └── test
│ └── java
│ └── com
│ └── prime
│ └── codex
│ └── ExampleUnitTest.kt
├── core-ui
├── .gitignore
├── build.gradle.kts
├── consumer-rules.pro
├── proguard-rules.pro
└── src
│ ├── androidTest
│ └── java
│ │ └── com
│ │ └── zs
│ │ └── core_ui
│ │ └── ExampleInstrumentedTest.kt
│ ├── main
│ ├── AndroidManifest.xml
│ └── java
│ │ └── com
│ │ └── zs
│ │ └── core_ui
│ │ ├── AppTheme.kt
│ │ ├── BackgroundPainter.kt
│ │ ├── CollapsableNeumorphicLargeAppBar.kt
│ │ ├── ColorPicker.kt
│ │ ├── Delegates.kt
│ │ ├── LazyList.kt
│ │ ├── NightMode.kt
│ │ ├── PagerIndicator.kt
│ │ ├── ScaleIndication.kt
│ │ ├── ToggleButton.kt
│ │ ├── Utils.kt
│ │ ├── WallpaperAccentColor.kt
│ │ ├── WindowSize.kt
│ │ ├── WindowStyle.kt
│ │ ├── adaptive
│ │ ├── NavigationItem.kt
│ │ ├── NavigationSuiteScaffold.kt
│ │ ├── TwoPane.kt
│ │ └── TwoPaneStrategy.kt
│ │ ├── coil
│ │ ├── MediaMetaDataArtFetcher.kt
│ │ ├── ReBlurTransformation.kt
│ │ ├── RsBlurTransformation.kt
│ │ └── VideoThumbnailFetcher.kt
│ │ ├── shape
│ │ ├── CloudShape.kt
│ │ ├── CompactDisk.kt
│ │ ├── EndConcaveShape.kt
│ │ ├── Folder.kt
│ │ ├── HeartShape.kt
│ │ ├── NotichedCornorShape.kt
│ │ ├── RoundedPolygonShape.kt
│ │ ├── RoundedStarShape.kt
│ │ ├── SkewedRoundedRectangleShape.kt
│ │ ├── TagShape.kt
│ │ └── TopConcaveShape.kt
│ │ ├── shimmer
│ │ ├── Highlight.kt
│ │ └── Shimmer.kt
│ │ └── toast
│ │ ├── Helpers.kt
│ │ ├── Toast.kt
│ │ └── ToastHostState.kt
│ └── test
│ └── java
│ └── com
│ └── zs
│ └── core_ui
│ └── ExampleUnitTest.kt
├── core
├── .gitignore
├── build.gradle.kts
├── consumer-rules.pro
├── proguard-rules.pro
└── src
│ ├── androidTest
│ └── java
│ │ └── com
│ │ └── zs
│ │ └── core
│ │ └── ExampleInstrumentedTest.kt
│ ├── main
│ ├── AndroidManifest.xml
│ └── java
│ │ └── com
│ │ └── zs
│ │ └── core
│ │ ├── Temp.kt
│ │ ├── Util.kt
│ │ ├── db
│ │ ├── Playlists.kt
│ │ ├── Playlists2.kt
│ │ └── Realm.kt
│ │ ├── paymaster
│ │ ├── Paymaster.kt
│ │ ├── PaymasterImpl.kt
│ │ ├── Purchase.kt
│ │ └── Security.java
│ │ ├── playback
│ │ ├── MediaItem.kt
│ │ ├── NowPlaying.kt
│ │ ├── Playback.kt
│ │ ├── PlaybackController.kt
│ │ ├── PlaybackControllerImpl.kt
│ │ └── Util.kt
│ │ ├── store
│ │ ├── MediaProvider.kt
│ │ ├── MediaProviderImpl.kt
│ │ ├── Model.kt
│ │ └── resolver-ktx.kt
│ │ └── util
│ │ ├── PathUtil.kt
│ │ └── Util.kt
│ └── test
│ └── java
│ └── com
│ └── zs
│ └── core
│ └── ExampleUnitTest.kt
├── crowdin.yml
├── gradle.properties
├── gradle
├── libs.versions.toml
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── settings.gradle.kts
├── stability_config.conf
└── widget
├── .gitignore
├── build.gradle.kts
├── consumer-rules.pro
├── proguard-rules.pro
└── src
├── androidTest
└── java
│ └── com
│ └── zs
│ └── widget
│ └── ExampleInstrumentedTest.kt
├── main
├── AndroidManifest.xml
├── java
│ └── com
│ │ └── zs
│ │ └── widget
│ │ ├── AppWidget.kt
│ │ ├── Chronometer.kt
│ │ ├── Universal.kt
│ │ ├── Util.kt
│ │ └── ViewType.kt
└── res
│ ├── drawable
│ ├── app_widget_preview.webp
│ ├── ic_config.xml
│ ├── ic_pause_rounded_filled.xml
│ ├── ic_round_play_filled.xml
│ ├── ic_skip_to_next.xml
│ ├── ic_skip_to_prev.xml
│ ├── media3_notification_pause.xml
│ ├── media3_notification_play.xml
│ └── rect_rounded_cornors_12dp.xml
│ ├── layout
│ ├── chronometer.xml
│ └── chronometer_bold.xml
│ └── xml
│ └── app_widget_info.xml
└── test
└── java
└── com
└── zs
└── widget
└── ExampleUnitTest.kt
/.github/ISSUE_TEMPLATE/bug_report.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Bug report
3 | about: Create a report to help us improve
4 | title: ''
5 | labels: ''
6 | assignees: ''
7 |
8 | ---
9 |
10 | **Describe the bug**
11 | A clear and concise description of what the bug is.
12 |
13 | **To Reproduce**
14 | Steps to reproduce the behavior:
15 | 1. Go to '...'
16 | 2. Click on '....'
17 | 3. Scroll down to '....'
18 | 4. See error
19 |
20 | **Expected behavior**
21 | A clear and concise description of what you expected to happen.
22 |
23 | **Screenshots**
24 | If applicable, add screenshots to help explain your problem.
25 |
26 | **Desktop (please complete the following information):**
27 | - OS: [e.g. iOS]
28 | - Browser [e.g. chrome, safari]
29 | - Version [e.g. 22]
30 |
31 | **Smartphone (please complete the following information):**
32 | - Device: [e.g. iPhone6]
33 | - OS: [e.g. iOS8.1]
34 | - Browser [e.g. stock browser, safari]
35 | - Version [e.g. 22]
36 |
37 | **Additional context**
38 | Add any other context about the problem here.
39 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/feature_request.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Feature request
3 | about: Suggest an idea for this project
4 | title: ''
5 | labels: ''
6 | assignees: ''
7 |
8 | ---
9 |
10 | **Is your feature request related to a problem? Please describe.**
11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
12 |
13 | **Describe the solution you'd like**
14 | A clear and concise description of what you want to happen.
15 |
16 | **Describe alternatives you've considered**
17 | A clear and concise description of any alternative solutions or features you've considered.
18 |
19 | **Additional context**
20 | Add any other context or screenshots about the feature request here.
21 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | .gradle
3 | /local.properties
4 | /.idea/caches
5 | /.idea/libraries
6 | /.idea/modules.xml
7 | /.idea/workspace.xml
8 | /.idea/navEditor.xml
9 | /.idea/assetWizardSettings.xml
10 | .DS_Store
11 | /build
12 | /captures
13 | .externalNativeBuild
14 | .cxx
15 | local.properties
16 | app/src/main/java/com/prime/media/core/billing/Private.kt
17 | .kotlin
18 |
--------------------------------------------------------------------------------
/.idea/CrowdinSettingsPlugin.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/.idea/appInsightsSettings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
45 |
46 |
--------------------------------------------------------------------------------
/.idea/codeStyles/codeStyleConfig.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/.idea/compiler.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.idea/deploymentTargetSelector.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/.idea/gradle.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
24 |
25 |
--------------------------------------------------------------------------------
/.idea/kotlinc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/.idea/migrations.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/.idea/runConfigurations.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/.idea/studiobot.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | Sure! Here's an example of a simplified version of contributing guidelines for an app in the alpha stage:
2 |
3 | # Contributing Guidelines (Alpha Stage)
4 |
5 | Thank you for your interest in our app! We are currently in the alpha stage, which means that many things are still being developed and changed. At this time, we are not accepting external contributions. However, we appreciate your support and encourage you to stay tuned for future updates.
6 |
7 | ## Providing Feedback
8 |
9 | As we continue to refine and improve our app, we value your feedback. If you encounter any issues, have suggestions, or want to share your thoughts, please feel free to reach out to us through [contact information or link to feedback channels]. Your input is essential in helping us shape the future of the app.
10 |
11 | ## Stay Updated
12 |
13 | To stay informed about the latest updates, releases, and opportunities to contribute in the future, we recommend:
14 |
15 | 1. Following our official channels, such as our website, blog, and social media accounts.
16 | 2. Joining our mailing list, if available, to receive important announcements.
17 | 3. Keeping an eye on our GitHub repository for any future changes to the contributing guidelines.
18 |
19 | Thank you for your understanding and patience as we work towards improving the app. We look forward to your continued support!
20 |
21 | Please note that these guidelines are specific to the alpha stage of your app, and you can modify them based on your project's needs and the stage of development.
22 |
--------------------------------------------------------------------------------
/SECURITY.md:
--------------------------------------------------------------------------------
1 | # Security Policy
2 |
3 | ## Supported Versions
4 |
5 | The following table provides information about the versions of our project currently being supported with security updates:
6 |
7 | | Version | Supported |
8 | | ------- | ------------------ |
9 | | 1.0.x | :white_check_mark: |
10 | | < 1.0 | :x: |
11 |
12 | Please note that only the version 1.0.x is actively supported with security updates. It is crucial to keep your project up to date with the latest version to ensure you receive necessary security patches and fixes.
13 |
14 | ## Reporting a Vulnerability
15 |
16 | If you discover a vulnerability within our project, we encourage you to report it promptly. To report a vulnerability, please follow these steps:
17 |
18 | 1. Visit our GitHub repository at [https://github.com/prime-zs/Audiofy2](https://github.com/prime-zs/Audiofy2).
19 |
20 | 2. Create a new issue in the repository by clicking on the "Issues" tab and then the "New Issue" button.
21 |
22 | 3. Provide a descriptive title for the issue, summarizing the vulnerability in a concise manner.
23 |
24 | 4. In the issue description, include the following information:
25 | - A detailed explanation of the vulnerability, including steps to reproduce it and any relevant technical details.
26 | - Information about the affected version(s) of the project.
27 | - If applicable, suggestions or recommendations for mitigating the vulnerability.
28 |
29 | 5. Submit the issue and our security team will review it promptly.
30 |
31 | We will keep you informed about the progress of the vulnerability assessment and subsequent actions taken through GitHub issue comments. You can expect regular updates on the status of your report.
32 |
33 | We appreciate your contribution to our project's security and value your efforts in helping us maintain a robust and secure system. Thank you for your cooperation!
34 |
35 |
36 |
--------------------------------------------------------------------------------
/ads/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/ads/build.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | alias(libs.plugins.android.library)
3 | alias(libs.plugins.jetbrains.kotlin.android)
4 | }
5 |
6 | android {
7 | namespace = "com.zs.ads"
8 | compileSdk = 34
9 |
10 | defaultConfig {
11 | minSdk = 21
12 | testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
13 | consumerProguardFiles("consumer-rules.pro")
14 | }
15 |
16 | buildTypes {
17 | release {
18 | isMinifyEnabled = false
19 | proguardFiles(
20 | getDefaultProguardFile("proguard-android-optimize.txt"),
21 | "proguard-rules.pro"
22 | )
23 | }
24 | }
25 | compileOptions {
26 | sourceCompatibility = JavaVersion.VERSION_1_8
27 | targetCompatibility = JavaVersion.VERSION_1_8
28 | }
29 | kotlinOptions { jvmTarget = "1.8" }
30 | }
31 |
32 | dependencies { implementation(libs.bundles.ad.network) }
--------------------------------------------------------------------------------
/ads/consumer-rules.pro:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/iZakirSheikh/Audiofy/7cc20318196ab355d99863b0192abd1a5af6ba14/ads/consumer-rules.pro
--------------------------------------------------------------------------------
/ads/src/androidTest/java/com/zs/ads/ExampleInstrumentedTest.kt:
--------------------------------------------------------------------------------
1 | package com.zs.ads
2 |
3 | import androidx.test.platform.app.InstrumentationRegistry
4 | import androidx.test.ext.junit.runners.AndroidJUnit4
5 |
6 | import org.junit.Test
7 | import org.junit.runner.RunWith
8 |
9 | import org.junit.Assert.*
10 |
11 | /**
12 | * Instrumented test, which will execute on an Android device.
13 | *
14 | * See [testing documentation](http://d.android.com/tools/testing).
15 | */
16 | @RunWith(AndroidJUnit4::class)
17 | class ExampleInstrumentedTest {
18 | @Test
19 | fun useAppContext() {
20 | // Context of the app under test.
21 | val appContext = InstrumentationRegistry.getInstrumentation().targetContext
22 | assertEquals("com.zs.ads.test", appContext.packageName)
23 | }
24 | }
--------------------------------------------------------------------------------
/ads/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/ads/src/main/java/com/zs/ads/AdEventListener.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2024 Zakir Sheikh
3 | *
4 | * Created by Zakir Sheikh on 29-07-2024.
5 | *
6 | * Licensed under the Apache License, Version 2.0 (the "License");
7 | * you may not use this file except in compliance with the License.
8 | * You may obtain a copy of the License at
9 | *
10 | * https://www.apache.org/licenses/LICENSE-2.0
11 | *
12 | * Unless required by applicable law or agreed to in writing, software
13 | * distributed under the License is distributed on an "AS IS" BASIS,
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | * See the License for the specific language governing permissions and
16 | * limitations under the License.
17 | */
18 |
19 | package com.zs.ads
20 |
21 |
22 | /**
23 | * An interface for listening to ad events.
24 | *
25 | * Implement this interface to receive notifications about various ad events,
26 | * such asad loading, impressions, clicks, and errors.
27 | */
28 | interface AdEventListener {
29 |
30 | /**
31 | * Called when an ad event occurs.
32 | *
33 | * @param event The name of the ad event that occurred.
34 | * @param data An optional [AdData] object containing additional information about the event,
35 | * such as ad metadata or error details.
36 | */
37 | fun onAdEvent(event: String, data: AdData?)
38 |
39 | /**
40 | * Called when an ad impression event occurs. This can be triggered when:
41 | * - A banner ad is successfully loaded.
42 | * - or the impression listener records impression
43 | *
44 | * @param value An [AdData.AdImpression] object containing details about the ad impression,
45 | * or null if no ad impression data is available.
46 | */
47 | fun onAdImpression(value: AdData.AdImpression?)
48 |
49 | /**
50 | * Called when a rewarded ad is successfully completed and the user earns a reward.
51 | *
52 | * @param reward A [Reward] object representing the reward earned, or null if no reward is provided.
53 | * @param info An [AdData.AdInfo] object containing additional information about the ad event.
54 | */
55 | fun onAdRewarded(reward: Reward?, info: AdData.AdInfo?)
56 | }
--------------------------------------------------------------------------------
/ads/src/main/java/com/zs/ads/AdSize.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2024 Zakir Sheikh
3 | *
4 | * Created by Zakir Sheikh on 02-07-2024.
5 | *
6 | * Licensed under the Apache License, Version 2.0 (the "License");
7 | * you may not use this file except in compliance with the License.
8 | * You may obtain a copy of the License at
9 | *
10 | * https://www.apache.org/licenses/LICENSE-2.0
11 | *
12 | * Unless required by applicable law or agreed to in writing, software
13 | * distributed under the License is distributed on an "AS IS" BASIS,
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | * See the License for the specific language governing permissions and
16 | * limitations under the License.
17 | */
18 |
19 | package com.zs.ads
20 |
21 | import com.ironsource.mediationsdk.ISBannerSize
22 |
23 | /**
24 | * Represents the size of an ad banner.
25 | ** @property size The underlying [ISBannerSize] object.
26 | */
27 | @JvmInline
28 | value class AdSize private constructor(internal val value: ISBannerSize) {
29 |
30 | /**
31 | * Creates a custom ad size.
32 | *
33 | * @param type The type of the custom ad size (default is "CUSTOM").
34 | * @param width The width of the ad in pixels.
35 | * @param height The height of the ad in pixels.
36 | */
37 | constructor(width: Int, height: Int, type: String = "CUSTOM") : this(
38 | ISBannerSize(
39 | type,
40 | width,
41 | height
42 | )
43 | )
44 |
45 | companion object {
46 | /**
47 | * Standard banner size (320x50).
48 | */
49 | val BANNER = AdSize(ISBannerSize.BANNER)
50 |
51 | /**
52 | * Large banner size (320x100).*/
53 | val LARGE_BANNER = AdSize(ISBannerSize.LARGE)
54 |
55 | /**
56 | * Medium rectangle size (300x250).
57 | */
58 | val MEDIUM_RECTANGLE = AdSize(ISBannerSize.RECTANGLE)
59 |
60 | /**
61 | * Smart banner size that adjusts to the device's width.
62 | */
63 | val SMART = AdSize(ISBannerSize.SMART)
64 |
65 | /**
66 | * Leaderboard size (728x90).
67 | */
68 | val LEADERBOARD = AdSize(728, 90, "LEADERBOARD",)
69 |
70 | /**
71 | * Full banner size (468x60).
72 | */
73 | val FULL_BANNER = AdSize(468, 60, "FULL_BANNER",)
74 | }
75 | }
--------------------------------------------------------------------------------
/ads/src/main/java/com/zs/ads/Reward.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2024 Zakir Sheikh
3 | *
4 | * Created by Zakir Sheikh on 31-07-2024.
5 | *
6 | * Licensed under the Apache License, Version 2.0 (the "License");
7 | * you may not use this file except in compliance with the License.
8 | * You may obtain a copy of the License at
9 | *
10 | * https://www.apache.org/licenses/LICENSE-2.0
11 | *
12 | * Unless required by applicable law or agreed to in writing, software
13 | * distributed under the License is distributed on an "AS IS" BASIS,
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | * See the License for the specific language governing permissions and
16 | * limitations under the License.
17 | */
18 |
19 | package com.zs.ads
20 |
21 | import com.ironsource.mediationsdk.model.Placement
22 |
23 |
24 | /**
25 | * A value class representing a reward earned from an ad placement.
26 | *
27 | * @property value The underlying [Placement] object that holds the reward details.
28 | * @property amount The amount of the reward.
29 | * @property name The name or type of the reward.
30 | */
31 | @JvmInline
32 | value class Reward(internal val value: Placement){
33 | val amount get() = value.rewardAmount
34 | val name get() = value.rewardName
35 | }
--------------------------------------------------------------------------------
/ads/src/test/java/com/zs/ads/ExampleUnitTest.kt:
--------------------------------------------------------------------------------
1 | package com.zs.ads
2 |
3 | import org.junit.Test
4 |
5 | import org.junit.Assert.*
6 |
7 | /**
8 | * Example local unit test, which will execute on the development machine (host).
9 | *
10 | * See [testing documentation](http://d.android.com/tools/testing).
11 | */
12 | class ExampleUnitTest {
13 | @Test
14 | fun addition_isCorrect() {
15 | assertEquals(4, 2 + 2)
16 | }
17 | }
--------------------------------------------------------------------------------
/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/app/google-services.json:
--------------------------------------------------------------------------------
1 | {
2 | "project_info": {
3 | "project_number": "395273193376",
4 | "project_id": "audiofy-4ce6c",
5 | "storage_bucket": "audiofy-4ce6c.appspot.com"
6 | },
7 | "client": [
8 | {
9 | "client_info": {
10 | "mobilesdk_app_id": "1:395273193376:android:1bffdeb26f98ce4a0e46c5",
11 | "android_client_info": {
12 | "package_name": "com.prime.mediaplayer"
13 | }
14 | },
15 | "oauth_client": [
16 | {
17 | "client_id": "395273193376-25rhk3332e3gntapskvshc4lv3g0jm03.apps.googleusercontent.com",
18 | "client_type": 3
19 | }
20 | ],
21 | "api_key": [
22 | {
23 | "current_key": "AIzaSyBKeIYrSFPr7tbeSiAj07kHWOUG0w3fFRg"
24 | }
25 | ],
26 | "services": {
27 | "appinvite_service": {
28 | "other_platform_oauth_client": [
29 | {
30 | "client_id": "395273193376-25rhk3332e3gntapskvshc4lv3g0jm03.apps.googleusercontent.com",
31 | "client_type": 3
32 | }
33 | ]
34 | }
35 | }
36 | },
37 | {
38 | "client_info": {
39 | "mobilesdk_app_id": "1:395273193376:android:d177d81aff07ac610e46c5",
40 | "android_client_info": {
41 | "package_name": "com.prime.player"
42 | }
43 | },
44 | "oauth_client": [
45 | {
46 | "client_id": "395273193376-25rhk3332e3gntapskvshc4lv3g0jm03.apps.googleusercontent.com",
47 | "client_type": 3
48 | }
49 | ],
50 | "api_key": [
51 | {
52 | "current_key": "AIzaSyBKeIYrSFPr7tbeSiAj07kHWOUG0w3fFRg"
53 | }
54 | ],
55 | "services": {
56 | "appinvite_service": {
57 | "other_platform_oauth_client": [
58 | {
59 | "client_id": "395273193376-25rhk3332e3gntapskvshc4lv3g0jm03.apps.googleusercontent.com",
60 | "client_type": 3
61 | }
62 | ]
63 | }
64 | }
65 | },
66 | {
67 | "client_info": {
68 | "mobilesdk_app_id": "1:395273193376:android:d177d81aff07ac610e46c5",
69 | "android_client_info": {
70 | "package_name": "com.prime.player.debug"
71 | }
72 | },
73 | "oauth_client": [
74 | {
75 | "client_id": "395273193376-25rhk3332e3gntapskvshc4lv3g0jm03.apps.googleusercontent.com",
76 | "client_type": 3
77 | }
78 | ],
79 | "api_key": [
80 | {
81 | "current_key": "AIzaSyBKeIYrSFPr7tbeSiAj07kHWOUG0w3fFRg"
82 | }
83 | ],
84 | "services": {
85 | "appinvite_service": {
86 | "other_platform_oauth_client": [
87 | {
88 | "client_id": "395273193376-25rhk3332e3gntapskvshc4lv3g0jm03.apps.googleusercontent.com",
89 | "client_type": 3
90 | }
91 | ]
92 | }
93 | }
94 | }
95 | ],
96 | "configuration_version": "1"
97 | }
--------------------------------------------------------------------------------
/app/src/androidTest/java/com/prime/media/ExampleInstrumentedTest.kt:
--------------------------------------------------------------------------------
1 | package com.prime.media
2 |
3 | import androidx.test.platform.app.InstrumentationRegistry
4 | import androidx.test.ext.junit.runners.AndroidJUnit4
5 |
6 | import org.junit.Test
7 | import org.junit.runner.RunWith
8 |
9 | import org.junit.Assert.*
10 |
11 | /**
12 | * Instrumented test, which will execute on an Android device.
13 | *
14 | * See [testing documentation](http://d.android.com/tools/testing).
15 | */
16 | @RunWith(AndroidJUnit4::class)
17 | class ExampleInstrumentedTest {
18 | @Test
19 | fun useAppContext() {
20 | // Context of the app under test.
21 | val appContext = InstrumentationRegistry.getInstrumentation().targetContext
22 | assertEquals("com.prime.player", appContext.packageName)
23 | }
24 | }
--------------------------------------------------------------------------------
/app/src/main/ic_launcher-playstore.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/iZakirSheikh/Audiofy/7cc20318196ab355d99863b0192abd1a5af6ba14/app/src/main/ic_launcher-playstore.png
--------------------------------------------------------------------------------
/app/src/main/java/com/prime/media/common/Chronometer.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2025 Zakir Sheikh
3 | *
4 | * Created by Zakir Sheikh on 05-01-2025.
5 | *
6 | * Licensed under the Apache License, Version 2.0 (the "License");
7 | * you may not use this file except in compliance with the License.
8 | * You may obtain a copy of the License at
9 | *
10 | * https://www.apache.org/licenses/LICENSE-2.0
11 | *
12 | * Unless required by applicable law or agreed to in writing, software
13 | * distributed under the License is distributed on an "AS IS" BASIS,
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | * See the License for the specific language governing permissions and
16 | * limitations under the License.
17 | */
18 |
19 | package com.prime.media.common
20 |
21 | import androidx.compose.runtime.MutableLongState
22 | import androidx.compose.runtime.mutableLongStateOf
23 |
24 | /**
25 | * A composable chronometer that tracks elapsed time.
26 | *
27 | * This class provides a way to manage and display a time duration, similar to a stopwatch.
28 | * It uses a [MutableLongState] internally to hold the current time value, allowing for
29 | * manual setting and retrieval of the elapsed time.
30 | *
31 | * The chronometer's value represents the elapsed time in milliseconds.
32 | *
33 | * @property value The current elapsed time in milliseconds. This property can be used to
34 | * get or set the chronometer's current value.
35 | */
36 | @JvmInline
37 | value class Chronometer private constructor(private val state: MutableLongState){
38 |
39 | /**
40 | * Constructs a [Chronometer] with an initial time value.
41 | *
42 | * @param initial The initial time value in milliseconds.
43 | */
44 | constructor(initial: Long): this(mutableLongStateOf(initial))
45 |
46 | /**
47 | * Gets or sets the current elapsed time in milliseconds.
48 | *
49 | * When setting the value, the chronometer's internal state is updated to reflect the new time.
50 | * When getting the value, the current elapsed time is returned.
51 | */
52 | var value get() = state.longValue
53 | set(value){
54 | state.longValue = value
55 | }
56 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/prime/media/common/FloatingActionMenu.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2024 Zakir Sheikh
3 | *
4 | * Created by 2024 on 20-10-2024.
5 | *
6 | * Licensed under the Apache License, Version 2.0 (the "License");
7 | * you may not use this file except in compliance with the License.
8 | * You may obtain a copy of the License at
9 | *
10 | * https://www.apache.org/licenses/LICENSE-2.0
11 | *
12 | * Unless required by applicable law or agreed to in writing, software
13 | * distributed under the License is distributed on an "AS IS" BASIS,
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | * See the License for the specific language governing permissions and
16 | * limitations under the License.
17 | */
18 |
19 | package com.prime.media.common
20 |
21 | import androidx.compose.animation.animateContentSize
22 | import androidx.compose.foundation.BorderStroke
23 | import androidx.compose.foundation.layout.Arrangement
24 | import androidx.compose.foundation.layout.Row
25 | import androidx.compose.foundation.layout.RowScope
26 | import androidx.compose.foundation.shape.CircleShape
27 | import androidx.compose.material.Surface
28 | import androidx.compose.runtime.Composable
29 | import androidx.compose.runtime.NonRestartableComposable
30 | import androidx.compose.ui.Alignment
31 | import androidx.compose.ui.Modifier
32 | import androidx.compose.ui.draw.scale
33 | import androidx.compose.ui.unit.dp
34 | import com.zs.core_ui.AppTheme
35 |
36 | private val DefaultItemSpace = Arrangement.spacedBy(AppTheme.padding.small)
37 |
38 | /**
39 | * A composable function that creates a floating action menu.
40 | *
41 | * @param modifier The modifier to be applied to the layout.
42 | * @param content The composable content to be displayed within the menu.
43 | */
44 | @Composable
45 | @NonRestartableComposable
46 | fun FloatingActionMenu(
47 | modifier: Modifier = Modifier,
48 | content: @Composable RowScope.() -> Unit
49 | ) {
50 | Surface(
51 | modifier = modifier.scale(0.85f),
52 | color = AppTheme.colors.background(elevation = 2.dp),
53 | contentColor = AppTheme.colors.onBackground,
54 | shape = CircleShape,
55 | border = BorderStroke(1.dp, AppTheme.colors.background(elevation = 4.dp)),
56 | elevation = 12.dp,
57 | content = {
58 | Row(
59 | horizontalArrangement = DefaultItemSpace,
60 | verticalAlignment = Alignment.CenterVertically,
61 | modifier = Modifier.animateContentSize(),
62 | content = content
63 | )
64 | }
65 | )
66 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/prime/media/common/SelectionTracker.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2024 Zakir Sheikh
3 | *
4 | * Created by 2024 on 24-10-2024.
5 | *
6 | * Licensed under the Apache License, Version 2.0 (the "License");
7 | * you may not use this file except in compliance with the License.
8 | * You may obtain a copy of the License at
9 | *
10 | * https://www.apache.org/licenses/LICENSE-2.0
11 | *
12 | * Unless required by applicable law or agreed to in writing, software
13 | * distributed under the License is distributed on an "AS IS" BASIS,
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | * See the License for the specific language governing permissions and
16 | * limitations under the License.
17 | */
18 |
19 | package com.prime.media.common
20 |
21 | import androidx.compose.runtime.State
22 |
23 | /**
24 | * Provides a way to track selection state.
25 | * Each property is observable.
26 | *
27 | * @property focused The key of the currently focused item. Null if none is focused.
28 | * @property selected The list of keys representing the currently selected items.
29 | * @property isInSelectionMode Indicates whether selection mode is active.
30 | * @property allSelected True if all items are selected; otherwise, false.
31 | */
32 | interface SelectionTracker {
33 |
34 | /**
35 | * Represents the selection level of a group.
36 | */
37 | enum class Level { NONE, PARTIAL, FULL }
38 |
39 | var focused: String?
40 | val selected: List
41 | val isInSelectionMode: Boolean
42 | val allSelected: Boolean
43 |
44 | /**
45 | * Returns the selection level of the group represented by the key.
46 | *
47 | * @param key The key representing the group.
48 | * @return A `State` object representing the selection level of the group.
49 | * @throws UnsupportedOperationException If this function is not supported by the implementing class.
50 | */
51 | fun isGroupSelected(key: String): State {
52 | throw UnsupportedOperationException("This function is not supported by ${this::class.java.simpleName}")
53 | }
54 |
55 | /**
56 | * Toggles the selection of the group represented by the key.
57 | *
58 | * @param key The key representing the group.
59 | * @throws UnsupportedOperationException If this function is not supported by the implementing class.
60 | */
61 | fun check(key: String) {
62 | throw UnsupportedOperationException("This function is not supported by ${this::class.java.simpleName}")
63 | }
64 |
65 | /**
66 | * Toggles the selection of the item having the specified key.
67 | *
68 | * @param key The key representing the item.
69 | */
70 | fun select(key: String)
71 |
72 | /**
73 | * Clears the selection.
74 | */
75 | fun clear()
76 |
77 | /**
78 | * Selects all items.
79 | */
80 | fun selectAll()
81 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/prime/media/common/menu/Action.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2024 Zakir Sheikh
3 | *
4 | * Created by 2024 on 24-10-2024.
5 | *
6 | * Licensed under the Apache License, Version 2.0 (the "License");
7 | * you may not use this file except in compliance with the License.
8 | * You may obtain a copy of the License at
9 | *
10 | * https://www.apache.org/licenses/LICENSE-2.0
11 | *
12 | * Unless required by applicable law or agreed to in writing, software
13 | * distributed under the License is distributed on an "AS IS" BASIS,
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | * See the License for the specific language governing permissions and
16 | * limitations under the License.
17 | */
18 |
19 | package com.prime.media.common.menu
20 |
21 | import androidx.annotation.StringRes
22 | import androidx.compose.ui.graphics.vector.ImageVector
23 |
24 | /**
25 | * Represents a single item within a menu. The [label] represents the id of this item as well.
26 | *
27 | * @property label The text label displayed for the menu item.
28 | * Can include a subtitle on a new line, up to two lines maximum.
29 | * @property icon Optional icon associated with the menu item.
30 | * @property enabled Indicates whether the menu item is currently enabled and interactive.
31 | */
32 | interface Action {
33 | @get:StringRes val label: Int
34 | val id: String
35 | val icon: ImageVector?
36 | val enabled: Boolean
37 |
38 | /**
39 | * Creates a copy of this [MenuItem] with the specified modifications.
40 | *
41 | * @param title The text label for the new menu item.
42 | * @param icon The icon for the new menu item.
43 | * @param enabled Whether the new menu item is enabled.
44 | * @return A new [MenuItem] instance with the applied modifications.
45 | */
46 | fun copy(
47 | enabled: Boolean = this.enabled,
48 | ): Action = ActionImpl(label, id, icon, enabled)
49 |
50 | companion object {
51 |
52 | /**
53 | * @see MenuItem
54 | */
55 | operator fun invoke(
56 | @StringRes label: Int,
57 | icon: ImageVector? = null,
58 | enabled: Boolean = true,
59 | id: String = label.toString()
60 | ): Action = ActionImpl(label, id, icon, enabled)
61 | }
62 | }
63 |
64 | /**
65 | * Default implementation of [MenuItem].
66 | */
67 | private class ActionImpl(
68 | override val label: Int ,
69 | override val id: String,
70 | override val icon: ImageVector?,
71 | override val enabled: Boolean
72 | ) : Action {
73 | override fun equals(other: Any?): Boolean {
74 | if (this === other) return true
75 | if (javaClass != other?.javaClass) return false
76 |
77 | other as ActionImpl
78 |
79 | return id == other.id
80 | }
81 |
82 | override fun hashCode(): Int {
83 | return id.hashCode()
84 | }
85 |
86 | override fun toString(): String {
87 | return "Action(Label = $label, id = $id, icon = $icon, enabled = $enabled)"
88 | }
89 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/prime/media/impl/PersonalizeViewModel.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2024 Zakir Sheikh
3 | *
4 | * Created by 2024 on 17-10-2024.
5 | *
6 | * Licensed under the Apache License, Version 2.0 (the "License");
7 | * you may not use this file except in compliance with the License.
8 | * You may obtain a copy of the License at
9 | *
10 | * https://www.apache.org/licenses/LICENSE-2.0
11 | *
12 | * Unless required by applicable law or agreed to in writing, software
13 | * distributed under the License is distributed on an "AS IS" BASIS,
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | * See the License for the specific language governing permissions and
16 | * limitations under the License.
17 | */
18 |
19 | package com.prime.media.impl
20 |
21 | import androidx.compose.ui.graphics.Shape
22 | import com.google.firebase.analytics.FirebaseAnalytics
23 | import com.google.firebase.analytics.ktx.analytics
24 | import com.google.firebase.analytics.logEvent
25 | import com.google.firebase.ktx.Firebase
26 | import com.prime.media.BuildConfig
27 | import com.prime.media.personalize.PersonalizeViewState
28 | import com.prime.media.settings.Settings
29 | import com.primex.preferences.Key
30 |
31 | class PersonalizeViewModel : KoinViewModel(), PersonalizeViewState {
32 | override fun setInAppWidget(id: String) {
33 | preferences[Settings.GLANCE] = id
34 | Firebase.analytics.logEvent(FirebaseAnalytics.Event.SELECT_ITEM) {
35 | param(FirebaseAnalytics.Param.ITEM_ID, id)
36 | }
37 | }
38 |
39 | override fun setArtworkShapeKey(key: String) {
40 | preferences[Settings.ARTWORK_SHAPE_KEY] = key
41 | Firebase.analytics.logEvent(FirebaseAnalytics.Event.SELECT_ITEM) {
42 | param(FirebaseAnalytics.Param.ITEM_ID, key)
43 | }
44 | }
45 |
46 | override fun set(key: Key, value: O) {
47 | preferences[key] = value
48 | }
49 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/prime/media/impl/SettingsViewModel.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2024 Zakir Sheikh
3 | *
4 | * Created by 2024 on 14-10-2024.
5 | *
6 | * Licensed under the Apache License, Version 2.0 (the "License");
7 | * you may not use this file except in compliance with the License.
8 | * You may obtain a copy of the License at
9 | *
10 | * https://www.apache.org/licenses/LICENSE-2.0
11 | *
12 | * Unless required by applicable law or agreed to in writing, software
13 | * distributed under the License is distributed on an "AS IS" BASIS,
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | * See the License for the specific language governing permissions and
16 | * limitations under the License.
17 | */
18 |
19 | package com.prime.media.impl
20 |
21 | import com.prime.media.settings.SettingsViewState
22 | import com.primex.preferences.Key
23 |
24 | class SettingsViewModel() : KoinViewModel(), SettingsViewState {
25 | override fun set(key: Key, value: O) {
26 | preferences[key] = value
27 | }
28 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/prime/media/local/DirectoryViewState.kt:
--------------------------------------------------------------------------------
1 | package com.prime.media.local
2 |
3 | import androidx.compose.foundation.text.input.TextFieldState
4 | import com.prime.media.common.Filter
5 | import com.prime.media.common.Mapped
6 | import com.prime.media.common.menu.Action
7 | import kotlinx.coroutines.flow.StateFlow
8 |
9 | /**
10 | * Represents the common interface among directories
11 | * @property data
12 | */
13 | interface DirectoryViewState {
14 |
15 |
16 |
17 | companion object
18 |
19 | val title: CharSequence
20 |
21 | val orders: List
22 | val data: StateFlow?>
23 |
24 | // filter.
25 | val query: TextFieldState
26 | val order: Filter
27 |
28 | // actions
29 | fun filter(ascending: Boolean = this.order.first, order: Action = this.order.second)
30 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/prime/media/local/videos/VideosViewState.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2024 Zakir Sheikh
3 | *
4 | * Created by Zakir Sheikh on 16-11-2024.
5 | *
6 | * Licensed under the Apache License, Version 2.0 (the "License");
7 | * you may not use this file except in compliance with the License.
8 | * You may obtain a copy of the License at
9 | *
10 | * https://www.apache.org/licenses/LICENSE-2.0
11 | *
12 | * Unless required by applicable law or agreed to in writing, software
13 | * distributed under the License is distributed on an "AS IS" BASIS,
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | * See the License for the specific language governing permissions and
16 | * limitations under the License.
17 | */
18 |
19 | package com.prime.media.local.videos
20 |
21 | import android.app.Activity
22 | import androidx.compose.foundation.text.input.TextFieldState
23 | import androidx.compose.material.icons.Icons
24 | import androidx.compose.material.icons.outlined.Delete
25 | import androidx.compose.material.icons.outlined.Edit
26 | import androidx.compose.material.icons.outlined.Share
27 | import com.prime.media.R
28 | import com.prime.media.common.Filter
29 | import com.prime.media.common.Mapped
30 | import com.prime.media.common.Route
31 | import com.prime.media.common.menu.Action
32 | import com.zs.core.store.Video
33 | import kotlinx.coroutines.flow.StateFlow
34 |
35 | object RouteVideos : Route
36 |
37 | /**
38 | * Represents the view state for the Videos screen.
39 | *
40 | * @property data The state flow containing mapped video data.
41 | * @property query The current text field state for filtering videos.
42 | * @property order The current filter applied to the video list.
43 | * @property actions The list of available actions that can be performed.
44 | * @property orders The list of possible orderings for the video list.
45 | */
46 | interface VideosViewState {
47 |
48 | companion object {
49 | val ACTION_RENAME = Action(R.string.rename, Icons.Outlined.Edit)
50 | val ACTION_DELETE = Action(R.string.delete,Icons.Outlined.Delete)
51 | val ACTION_SHARE = Action(R.string.share, Icons.Outlined.Share)
52 | }
53 |
54 | val data: StateFlow?>
55 | val query: TextFieldState
56 | val order: Filter
57 | val actions: List
58 | val orders: List
59 |
60 | /**
61 | * Filters the video list based on the given ascending flag and order action.
62 | *
63 | * @param ascending Whether the videos should be ordered in ascending order.
64 | * @param order The action representing the order to apply.
65 | */
66 | fun filter(ascending: Boolean = this.order.first, order: Action = this.order.second)
67 |
68 | /**
69 | *
70 | */
71 | fun delete(activity: Activity, video: Video)
72 |
73 | /**
74 | *
75 | */
76 | fun share(activity: Activity, file: Video)
77 |
78 | /**
79 | * Plays the specified video.
80 | *
81 | * @param video The video to be played.
82 | */
83 | fun play(video: Video)
84 | }
85 |
--------------------------------------------------------------------------------
/app/src/main/java/com/prime/media/old/common/View.kt:
--------------------------------------------------------------------------------
1 | package com.prime.media.old.common
2 |
3 |
4 | import android.annotation.SuppressLint
5 | import android.view.ViewGroup
6 | import android.widget.FrameLayout
7 | import androidx.compose.runtime.Composable
8 | import androidx.compose.ui.Modifier
9 | import androidx.compose.ui.graphics.Color
10 | import androidx.compose.ui.graphics.toArgb
11 | import androidx.compose.ui.viewinterop.AndroidView
12 | import androidx.media3.common.Player
13 | import androidx.media3.ui.AspectRatioFrameLayout
14 | import androidx.media3.ui.PlayerView
15 |
16 | private const val TAG = "View"
17 |
18 | /**
19 | * A wrapper around Media3 [PlayerView]
20 | */
21 | @SuppressLint("UnsafeOptInUsageError")
22 | @Composable
23 | fun PlayerView(
24 | player: Player?,
25 | modifier: Modifier = Modifier,
26 | resizeMode: Int = AspectRatioFrameLayout.RESIZE_MODE_FIT
27 | ) {
28 | AndroidView(
29 | modifier = modifier,
30 | factory = {
31 | PlayerView(it).apply {
32 | hideController()
33 | useController = false
34 | this.player = player
35 | this.resizeMode = resizeMode
36 | layoutParams = FrameLayout.LayoutParams(
37 | ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT
38 | )
39 | clipToOutline = true
40 | // Set the Background Color of the player as Solid Black Color.
41 | setBackgroundColor(Color.Black.toArgb())
42 | keepScreenOn = true
43 | }
44 | },
45 | update = { it.resizeMode = resizeMode; it.player = player; it.keepScreenOn = true },
46 | onRelease = {it.player = null; it.keepScreenOn = false}
47 | )
48 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/prime/media/old/common/util/PathUtils.kt:
--------------------------------------------------------------------------------
1 | package com.prime.media.old.common.util
2 |
3 | import android.content.Context
4 | import org.jetbrains.annotations.Contract
5 |
6 | /**
7 | * A scope for [PathUtils] functions.
8 | */
9 | object PathUtils {
10 | /**
11 | * The Unix separator character.
12 | */
13 | const val PATH_SEPARATOR = '/'
14 |
15 | /**
16 | * The extension separator character.
17 | * @since 1.4
18 | */
19 | const val EXTENSION_SEPARATOR = '.'
20 |
21 | const val HIDDEN_PATTERN = "/."
22 |
23 | /**
24 | * Gets the name minus the path from a full fileName.
25 | *
26 | * @param path the fileName to query
27 | * @return the name of the file without the path
28 | */
29 | fun name(path: String): String =
30 | path.substring(path.lastIndexOf(PATH_SEPARATOR) + 1)
31 |
32 | /**
33 | * @return parent of path.
34 | */
35 | fun parent(path: String): String =
36 | path.replace("$PATH_SEPARATOR${name(path = path)}", "")
37 |
38 | /**
39 | * Returns the file extension or null string if there is no extension. This method is a
40 | * convenience method for obtaining the extension of a url and has undefined
41 | * results for other Strings.
42 | * It is Assumed that Url is file
43 | *
44 | * @param url Url of the file
45 | *
46 | * @return extension
47 | */
48 | fun extension(url: String): String? =
49 | if (url.contains(EXTENSION_SEPARATOR))
50 | url.substring(url.lastIndexOf(EXTENSION_SEPARATOR) + 1).lowercase()
51 | else
52 | null
53 |
54 | /**
55 | * Checks if the file or its ancestors are hidden in System.
56 | */
57 | @Contract(pure = true)
58 | fun areAncestorsHidden(path: String): Boolean =
59 | path.contains(HIDDEN_PATTERN)
60 |
61 | /**
62 | * Returns [bytes] as formatted data unit.
63 | */
64 | @Deprecated(message = "find new solution.")
65 | fun toFormattedDataUnit(
66 | context: Context,
67 | bytes: Long,
68 | short: Boolean = true,
69 | ): String = when (short) {
70 | true -> android.text.format.Formatter.formatShortFileSize(context, bytes)
71 | else -> android.text.format.Formatter.formatFileSize(context, bytes)
72 | }
73 | }
74 |
75 |
--------------------------------------------------------------------------------
/app/src/main/java/com/prime/media/old/editor/ViewState.kt:
--------------------------------------------------------------------------------
1 | package com.prime.media.old.editor
2 |
3 | import android.content.Context
4 | import android.net.Uri
5 | import androidx.compose.ui.graphics.ImageBitmap
6 | import androidx.compose.ui.text.input.TextFieldValue
7 |
8 | private const val TAG = "TagEditor"
9 |
10 | interface TagEditor {
11 |
12 | companion object {
13 | const val KEY_PRAM_DATA = "_data"
14 | const val route = "tag_editor/{${KEY_PRAM_DATA}}"
15 | fun direction(path: String) =
16 | "tag_editor/${Uri.encode(path)}"
17 | }
18 |
19 | val extraInfo: CharSequence?
20 | val artwork: ImageBitmap?
21 |
22 |
23 | var title: TextFieldValue
24 | var artist: TextFieldValue
25 | var album: TextFieldValue
26 | var composer: TextFieldValue
27 | var albumArtist: TextFieldValue
28 | var genre: TextFieldValue
29 | var year: TextFieldValue
30 | var trackNumber: TextFieldValue
31 | var diskNumber: TextFieldValue
32 | var totalDisks: TextFieldValue
33 | var lyrics: TextFieldValue
34 | var comment: TextFieldValue
35 | var copyright: TextFieldValue
36 | var url: TextFieldValue
37 | var originalArtist: TextFieldValue
38 | var publisher: TextFieldValue
39 |
40 | /**
41 | * Sets the artwork of the media item to the given URI, or removes the artwork if the URI is null.
42 | * This function modifies the APIC (Attached picture) frame in the ID3v2 tag, or deletes it if the URI is null.
43 | * @param new the URI of the new artwork image, or null to remove the artwork
44 | */
45 | fun setArtwork(new: Uri?)
46 |
47 | /**
48 | * Saves the media item to the persistent memory, either replacing the original file or creating a new one depending on the user's strategy.
49 | * This function requires the context to be of ComponentActivity type, otherwise it will throw an exception.
50 | * @param ctx the context of the ComponentActivity that calls this function
51 | * @throws IllegalArgumentException if the context is not of ComponentActivity type
52 | */
53 | fun save(ctx: Context)
54 |
55 | /**
56 | * Resets the tags of the media item to their original value, discarding any changes made by the user.
57 | * This function restores the ID3v2 tag to its initial state when the media item was created or loaded.
58 | */
59 | fun reset()
60 | }
61 |
62 |
--------------------------------------------------------------------------------
/app/src/main/java/com/prime/media/old/feedback/FeedbackViewState.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2024 Zakir Sheikh
3 | *
4 | * Created by Zakir Sheikh on 13-08-2024.
5 | *
6 | * Licensed under the Apache License, Version 2.0 (the "License");
7 | * you may not use this file except in compliance with the License.
8 | * You may obtain a copy of the License at
9 | *
10 | * https://www.apache.org/licenses/LICENSE-2.0
11 | *
12 | * Unless required by applicable law or agreed to in writing, software
13 | * distributed under the License is distributed on an "AS IS" BASIS,
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | * See the License for the specific language governing permissions and
16 | * limitations under the License.
17 | */
18 |
19 | package com.prime.media.old.feedback
20 |
21 | import androidx.annotation.IntRange
22 | import androidx.compose.ui.text.input.TextFieldValue
23 | import androidx.navigation.NavController
24 | import com.prime.media.common.Route
25 |
26 | /**
27 | * A route for submitting feedback.
28 | */
29 | object RouteFeedback : Route
30 |
31 | /**
32 | * Typealias for [FeedbackViewState.Companion] for easy access to its constants.
33 | */
34 | typealias Feedback = FeedbackViewState.Companion
35 |
36 | /**
37 | * Represents the view state for the feedback screen.
38 | */
39 | interface FeedbackViewState {
40 | companion object {
41 | /**
42 | * The maximum number of characters allowed in the feedback text.*/
43 | const val TEXT_LIMIT_CHARS = 500
44 |
45 | /**
46 | * The default value for [rate] indicating that no rating has been set.
47 | */
48 | const val RATING_NOT_SET = -1
49 |
50 | /**
51 | * The valid range for the rating value.
52 | */
53 | val RATING_RANGE = 1..5
54 |
55 | /**
56 | * The maximum number of characters allowed in the feedback text.
57 | */
58 | const val FEEDBACK_MAX_CHAR_COUNT = 500
59 |
60 | val FEEDBACK_TAG_OTHER = "tag_other"
61 | val FEEDBACK_TAG_BUG = "tag_bug_report"
62 | val FEEDBACK_TAG_SUGGESTION = "tag_suggestion"
63 | val FEEDBACK_TAG_FEEDBACK = "tag_feedback"
64 | val FEEDBACK_TAG_FEATURE_REQUEST = "tag_feature_request"
65 | }
66 |
67 | /**
68 | * The type or category of feedback this submission represents.
69 | * For example, this could be "Suggestion", "Bug Report","Question", etc.
70 | * Defaults to [FEEDBACK_TAG_FEEDBACK] if not explicitly set.
71 | */
72 | var tag: String
73 |
74 | /**
75 | * A value between 1 and 5 that represents the rating given by the user.
76 | * A value of [RATING_NOT_SET] indicates that no rating has been set.
77 | */
78 | @setparam:IntRange(from = 1, to = 5)
79 | var rate: Int
80 |
81 | /**
82 | * The feedback text entered by the user.
83 | */
84 | var feedback: TextFieldValue
85 |
86 | /**
87 | * Sends the feedback to the server.
88 | */
89 | fun submit(navController: NavController)
90 | }
91 |
--------------------------------------------------------------------------------
/app/src/main/java/com/prime/media/old/library/ViewState.kt:
--------------------------------------------------------------------------------
1 | package com.prime.media.old.library
2 |
3 | import android.content.Context
4 | import android.net.Uri
5 | import androidx.compose.material.icons.Icons
6 | import androidx.compose.material.icons.outlined.LibraryMusic
7 | import androidx.compose.ui.graphics.vector.ImageVector
8 | import com.prime.media.old.core.db.Audio
9 | import com.primex.core.Text
10 | import com.zs.core.db.Playlist
11 | import kotlinx.coroutines.flow.Flow
12 | import kotlinx.coroutines.flow.StateFlow
13 |
14 | interface Library {
15 |
16 | companion object {
17 | val route: String get() = "route_library"
18 | fun direction() = route
19 | }
20 |
21 | /**
22 | * The recently played tracks.
23 | */
24 | val recent: StateFlow?>
25 | val carousel: StateFlow
26 | val newlyAdded: StateFlow?>
27 |
28 | /**
29 | * Callback method invoked upon clicking a history item.
30 | *
31 | * This method manages diverse scenarios contingent on the selected history item:
32 | *
33 | * - Scenario 1: In the event the newly added file is already present in the playback queue,
34 | * the queue will be directed to the chosen item's position, initiating playback from there.
35 | *
36 | * - Scenario 2: If the recently added item is absent from the queue, the ensuing sub-scenarios arise:
37 | * - Sub-Scenario 2.1: A Snackbar is exhibited to the user, offering options to either append
38 | * the item to the current queue at next or substitute the queue with this recently added playlist.
39 | * - Sub-Scenario 2.2: Further actions are contingent upon the user's decision.
40 | *
41 | * @param uri The URI of the history item clicked by the user.
42 | */
43 | fun onClickRecentFile(uri: String)
44 |
45 | /**
46 | * Callback method triggered when a recently added item is clicked.
47 | * This function handles the action where the recently added file is either included in the
48 | * playback queue or prompts the user to replace the existing queue with recently added items.
49 | *
50 | * @param id The unique identifier of the recently added history item.
51 | */
52 | fun onClickRecentAddedFile(id: Long)
53 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/prime/media/personalize/PersonalizeViewState.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2024 Zakir Sheikh
3 | *
4 | * Created by 2024 on 17-10-2024.
5 | *
6 | * Licensed under the Apache License, Version 2.0 (the "License");
7 | * you may not use this file except in compliance with the License.
8 | * You may obtain a copy of the License at
9 | *
10 | * https://www.apache.org/licenses/LICENSE-2.0
11 | *
12 | * Unless required by applicable law or agreed to in writing, software
13 | * distributed under the License is distributed on an "AS IS" BASIS,
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | * See the License for the specific language governing permissions and
16 | * limitations under the License.
17 | */
18 |
19 | package com.prime.media.personalize
20 |
21 | import androidx.compose.ui.graphics.Shape
22 | import com.prime.media.common.Route
23 | import com.primex.preferences.Key
24 |
25 | object RoutePersonalize: Route
26 |
27 |
28 | interface PersonalizeViewState {
29 | /**
30 | * Sets the current widget to [id]
31 | */
32 | fun setInAppWidget(id: String)
33 |
34 | fun setArtworkShapeKey(key: String)
35 |
36 | /**
37 | * Sets the [key] to [value]
38 | */
39 | operator fun set(key: Key, value: O)
40 | }
--------------------------------------------------------------------------------
/app/src/main/res/drawable-v24/ic_launcher_foreground.xml:
--------------------------------------------------------------------------------
1 |
7 |
11 |
15 |
19 |
20 |
26 |
27 |
28 |
29 |
30 |
31 |
35 |
36 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/default_art.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/iZakirSheikh/Audiofy/7cc20318196ab355d99863b0192abd1a5af6ba14/app/src/main/res/drawable/default_art.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_artist.xml:
--------------------------------------------------------------------------------
1 |
7 |
12 |
17 |
18 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_branded_image.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/iZakirSheikh/Audiofy/7cc20318196ab355d99863b0192abd1a5af6ba14/app/src/main/res/drawable/ic_branded_image.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_remove_ads.xml:
--------------------------------------------------------------------------------
1 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_splash.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/iZakirSheikh/Audiofy/7cc20318196ab355d99863b0192abd1a5af6ba14/app/src/main/res/drawable/ic_splash.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable/media3_notification_pause.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
12 |
13 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/media3_notification_play.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/iZakirSheikh/Audiofy/7cc20318196ab355d99863b0192abd1a5af6ba14/app/src/main/res/mipmap-hdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/iZakirSheikh/Audiofy/7cc20318196ab355d99863b0192abd1a5af6ba14/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/iZakirSheikh/Audiofy/7cc20318196ab355d99863b0192abd1a5af6ba14/app/src/main/res/mipmap-mdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/iZakirSheikh/Audiofy/7cc20318196ab355d99863b0192abd1a5af6ba14/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/iZakirSheikh/Audiofy/7cc20318196ab355d99863b0192abd1a5af6ba14/app/src/main/res/mipmap-xhdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/iZakirSheikh/Audiofy/7cc20318196ab355d99863b0192abd1a5af6ba14/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/iZakirSheikh/Audiofy/7cc20318196ab355d99863b0192abd1a5af6ba14/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/iZakirSheikh/Audiofy/7cc20318196ab355d99863b0192abd1a5af6ba14/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/iZakirSheikh/Audiofy/7cc20318196ab355d99863b0192abd1a5af6ba14/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/iZakirSheikh/Audiofy/7cc20318196ab355d99863b0192abd1a5af6ba14/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/app/src/main/res/raw/bg_blur_color_blob.lottie:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/iZakirSheikh/Audiofy/7cc20318196ab355d99863b0192abd1a5af6ba14/app/src/main/res/raw/bg_blur_color_blob.lottie
--------------------------------------------------------------------------------
/app/src/main/res/raw/bg_dark_wavy_lines.lottie:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/iZakirSheikh/Audiofy/7cc20318196ab355d99863b0192abd1a5af6ba14/app/src/main/res/raw/bg_dark_wavy_lines.lottie
--------------------------------------------------------------------------------
/app/src/main/res/raw/bg_fading_streight_lines.lottie:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/iZakirSheikh/Audiofy/7cc20318196ab355d99863b0192abd1a5af6ba14/app/src/main/res/raw/bg_fading_streight_lines.lottie
--------------------------------------------------------------------------------
/app/src/main/res/raw/bg_gradeint_dots.lottie:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/iZakirSheikh/Audiofy/7cc20318196ab355d99863b0192abd1a5af6ba14/app/src/main/res/raw/bg_gradeint_dots.lottie
--------------------------------------------------------------------------------
/app/src/main/res/raw/bg_rotating_color_gradient.lottie:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/iZakirSheikh/Audiofy/7cc20318196ab355d99863b0192abd1a5af6ba14/app/src/main/res/raw/bg_rotating_color_gradient.lottie
--------------------------------------------------------------------------------
/app/src/main/res/raw/loading_hand.lottie:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/iZakirSheikh/Audiofy/7cc20318196ab355d99863b0192abd1a5af6ba14/app/src/main/res/raw/loading_hand.lottie
--------------------------------------------------------------------------------
/app/src/main/res/raw/lt_bg_baloon_in_air.lottie:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/iZakirSheikh/Audiofy/7cc20318196ab355d99863b0192abd1a5af6ba14/app/src/main/res/raw/lt_bg_baloon_in_air.lottie
--------------------------------------------------------------------------------
/app/src/main/res/raw/lt_bg_blur.lottie:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/iZakirSheikh/Audiofy/7cc20318196ab355d99863b0192abd1a5af6ba14/app/src/main/res/raw/lt_bg_blur.lottie
--------------------------------------------------------------------------------
/app/src/main/res/raw/lt_bg_fluid_color.lottie:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/iZakirSheikh/Audiofy/7cc20318196ab355d99863b0192abd1a5af6ba14/app/src/main/res/raw/lt_bg_fluid_color.lottie
--------------------------------------------------------------------------------
/app/src/main/res/raw/lt_bg_snowflakes.lottie:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/iZakirSheikh/Audiofy/7cc20318196ab355d99863b0192abd1a5af6ba14/app/src/main/res/raw/lt_bg_snowflakes.lottie
--------------------------------------------------------------------------------
/app/src/main/res/raw/lt_golden_ticket.lottie:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/iZakirSheikh/Audiofy/7cc20318196ab355d99863b0192abd1a5af6ba14/app/src/main/res/raw/lt_golden_ticket.lottie
--------------------------------------------------------------------------------
/app/src/main/res/raw/lt_green_list_loading.lottie:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/iZakirSheikh/Audiofy/7cc20318196ab355d99863b0192abd1a5af6ba14/app/src/main/res/raw/lt_green_list_loading.lottie
--------------------------------------------------------------------------------
/app/src/main/res/raw/lt_list_loading_grey.lottie:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/iZakirSheikh/Audiofy/7cc20318196ab355d99863b0192abd1a5af6ba14/app/src/main/res/raw/lt_list_loading_grey.lottie
--------------------------------------------------------------------------------
/app/src/main/res/raw/lt_list_loading_shimmer_2.lottie:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/iZakirSheikh/Audiofy/7cc20318196ab355d99863b0192abd1a5af6ba14/app/src/main/res/raw/lt_list_loading_shimmer_2.lottie
--------------------------------------------------------------------------------
/app/src/main/res/raw/play_pause_circle_bordered.lottie:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/iZakirSheikh/Audiofy/7cc20318196ab355d99863b0192abd1a5af6ba14/app/src/main/res/raw/play_pause_circle_bordered.lottie
--------------------------------------------------------------------------------
/app/src/main/res/raw/play_pause_filled.lottie:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/iZakirSheikh/Audiofy/7cc20318196ab355d99863b0192abd1a5af6ba14/app/src/main/res/raw/play_pause_filled.lottie
--------------------------------------------------------------------------------
/app/src/main/res/values-ar-rSA/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
18 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
--------------------------------------------------------------------------------
/app/src/main/res/values-ca-rES/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
18 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
--------------------------------------------------------------------------------
/app/src/main/res/values-cs-rCZ/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
18 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
--------------------------------------------------------------------------------
/app/src/main/res/values-da-rDK/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
18 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
--------------------------------------------------------------------------------
/app/src/main/res/values-de-rDE/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
18 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
--------------------------------------------------------------------------------
/app/src/main/res/values-el-rGR/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
18 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
--------------------------------------------------------------------------------
/app/src/main/res/values-fi-rFI/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
18 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
--------------------------------------------------------------------------------
/app/src/main/res/values-fr-rFR/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
18 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
--------------------------------------------------------------------------------
/app/src/main/res/values-hi-rIN/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
18 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
--------------------------------------------------------------------------------
/app/src/main/res/values-hu-rHU/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
18 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
--------------------------------------------------------------------------------
/app/src/main/res/values-it-rIT/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
18 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
--------------------------------------------------------------------------------
/app/src/main/res/values-iw-rIL/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
18 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
--------------------------------------------------------------------------------
/app/src/main/res/values-ja-rJP/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
18 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
--------------------------------------------------------------------------------
/app/src/main/res/values-ko-rKR/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
18 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
--------------------------------------------------------------------------------
/app/src/main/res/values-nl-rNL/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
18 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
--------------------------------------------------------------------------------
/app/src/main/res/values-no-rNO/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
18 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
--------------------------------------------------------------------------------
/app/src/main/res/values-pt-rBR/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
18 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
--------------------------------------------------------------------------------
/app/src/main/res/values-pt-rPT/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
18 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
--------------------------------------------------------------------------------
/app/src/main/res/values/ic_launcher_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #000000
4 |
--------------------------------------------------------------------------------
/app/src/main/res/values/theme.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
13 |
14 |
23 |
24 |
--------------------------------------------------------------------------------
/app/src/main/res/values/what_s_new.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | What\'s New (v3.8.0)
5 |
6 | \n - Made subtle yet meaningful UI improvements.
7 | \n - Introduced a user-friendly swipe gesture to seamlessly adjust volume & brightness, offering an intuitive and efficient control experience.
8 | \n - Positioned the controller lock feature at a more accessible and convenient location
9 | \n - Introducing a variety of stunning Album Art styles! You can now customize your experience by selecting your favorite style from the Personalize Screen.
10 | \n - Say goodbye to the conventional white/dark themes of the Console! Audiofy now features the all-new Ambient Mode, showcasing an exquisite blend of intricate blurs and vibrant color combinations for a truly captivating visual experience.
11 | \n - Introduced multiple options for selecting the Accent Color, including manual selection and wallpaper-based choices.
12 |
13 |
14 |
15 | @string/what_s_new_toast
16 |
17 |
18 |
19 |
20 | - @string/what_s_new_latest
21 | - v3.3.0
22 |
23 | \n We\'ve enhanced the functionality of the app.
24 | \n We\'re utilizing the new, advanced NowPlaying API for real-time updates regarding playback metadata state.
25 | \n We\'ve added a new Dynamic Widget that automatically adjusts to size.
26 | \n More App Widget styles are coming soon.
27 |
28 |
29 |
30 |
31 |
--------------------------------------------------------------------------------
/app/src/test/java/com/prime/media/ExampleUnitTest.kt:
--------------------------------------------------------------------------------
1 | package com.prime.media
2 |
3 | import org.junit.Test
4 |
5 | import org.junit.Assert.*
6 |
7 | /**
8 | * Example local unit test, which will execute on the development machine (host).
9 | *
10 | * See [testing documentation](http://d.android.com/tools/testing).
11 | */
12 | class ExampleUnitTest {
13 | @Test
14 | fun addition_isCorrect() {
15 | assertEquals(4, 2 + 2)
16 | }
17 | }
--------------------------------------------------------------------------------
/build.gradle.kts:
--------------------------------------------------------------------------------
1 | // Top-level build file where you can add configuration options common to all sub-projects/modules.
2 | plugins {
3 | alias(libs.plugins.android.application) apply false
4 | alias(libs.plugins.jetbrains.kotlin.android) apply false
5 | alias(libs.plugins.compose.compiler) apply false
6 | alias(libs.plugins.android.library) apply false
7 | alias(libs.plugins.google.service) apply false
8 | alias(libs.plugins.crashanlytics) apply false
9 | alias(libs.plugins.android.dynamic.feature) apply false
10 | alias(libs.plugins.ksp) apply false
11 | }
--------------------------------------------------------------------------------
/codex/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/codex/build.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | alias(libs.plugins.android.dynamic.feature)
3 | alias(libs.plugins.jetbrains.kotlin.android)
4 | }
5 | android {
6 | namespace = "com.prime.codex"
7 | compileSdk = 34
8 | defaultConfig {
9 | minSdk = 21
10 | testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
11 | }
12 | buildTypes {
13 | release {
14 | isMinifyEnabled = false
15 | proguardFiles("proguard-rules.pro")
16 | }
17 | }
18 | compileOptions {
19 | sourceCompatibility = JavaVersion.VERSION_1_8
20 | targetCompatibility = JavaVersion.VERSION_1_8
21 | }
22 | kotlinOptions {
23 | jvmTarget = "1.8"
24 | freeCompilerArgs = listOf(
25 | "-Xopt-in=kotlin.RequiresOptIn",
26 | "-Xcontext-receivers"
27 | )
28 | }
29 | }
30 |
31 | dependencies {
32 | implementation(project(":app"))
33 | implementation(libs.nextlib) // To add media3 software decoders and extensions
34 | }
--------------------------------------------------------------------------------
/codex/src/androidTest/java/com/prime/codex/ExampleInstrumentedTest.kt:
--------------------------------------------------------------------------------
1 | package com.prime.codex
2 |
3 | import androidx.test.platform.app.InstrumentationRegistry
4 | import androidx.test.ext.junit.runners.AndroidJUnit4
5 |
6 | import org.junit.Test
7 | import org.junit.runner.RunWith
8 |
9 | import org.junit.Assert.*
10 |
11 | /**
12 | * Instrumented test, which will execute on an Android device.
13 | *
14 | * See [testing documentation](http://d.android.com/tools/testing).
15 | */
16 | @RunWith(AndroidJUnit4::class)
17 | class ExampleInstrumentedTest {
18 | @Test
19 | fun useAppContext() {
20 | // Context of the app under test.
21 | val appContext = InstrumentationRegistry.getInstrumentation().targetContext
22 | assertEquals("com.prime.codex", appContext.packageName)
23 | }
24 | }
--------------------------------------------------------------------------------
/codex/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/codex/src/main/java/com/prime/codex/Codex.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2024 Zakir Sheikh
3 | *
4 | * Created by Zakir Sheikh on 23-07-2024.
5 | *
6 | * Licensed under the Apache License, Version 2.0 (the "License");
7 | * you may not use this file except in compliance with the License.
8 | * You may obtain a copy of the License at
9 | *
10 | * https://www.apache.org/licenses/LICENSE-2.0
11 | *
12 | * Unless required by applicable law or agreed to in writing, software
13 | * distributed under the License is distributed on an "AS IS" BASIS,
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | * See the License for the specific language governing permissions and
16 | * limitations under the License.
17 | */
18 |
19 | package com.prime.codex
20 |
21 | import android.annotation.SuppressLint
22 | import android.content.Context
23 | import io.github.anilbeesetti.nextlib.media3ext.ffdecoder.NextRenderersFactory
24 |
25 | // com.prime.codex.CodexKt.Codex
26 | @SuppressLint("UnsafeOptInUsageError")
27 | fun Codex(context: Context) = NextRenderersFactory(context)
--------------------------------------------------------------------------------
/codex/src/test/java/com/prime/codex/ExampleUnitTest.kt:
--------------------------------------------------------------------------------
1 | package com.prime.codex
2 |
3 | import org.junit.Test
4 |
5 | import org.junit.Assert.*
6 |
7 | /**
8 | * Example local unit test, which will execute on the development machine (host).
9 | *
10 | * See [testing documentation](http://d.android.com/tools/testing).
11 | */
12 | class ExampleUnitTest {
13 | @Test
14 | fun addition_isCorrect() {
15 | assertEquals(4, 2 + 2)
16 | }
17 | }
--------------------------------------------------------------------------------
/core-ui/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/core-ui/build.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | alias(libs.plugins.android.library)
3 | alias(libs.plugins.jetbrains.kotlin.android)
4 | alias(libs.plugins.compose.compiler)
5 | }
6 |
7 | android {
8 | namespace = "com.zs.core_ui"
9 | compileSdk = 34
10 | defaultConfig {
11 | minSdk = 21
12 | testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
13 | consumerProguardFiles("consumer-rules.pro")
14 | }
15 | buildTypes {
16 | release {
17 | isMinifyEnabled = false
18 | proguardFiles(
19 | getDefaultProguardFile("proguard-android-optimize.txt"),
20 | "proguard-rules.pro"
21 | )
22 | }
23 | }
24 | compileOptions {
25 | sourceCompatibility = JavaVersion.VERSION_1_8
26 | targetCompatibility = JavaVersion.VERSION_1_8
27 | }
28 | kotlinOptions {
29 | jvmTarget = "1.8"
30 | freeCompilerArgs = listOf(
31 | "-Xopt-in=kotlin.RequiresOptIn",
32 | "-Xopt-in=com.primex.core.ExperimentalToolkitApi"
33 | )
34 | }
35 | buildFeatures { compose = true }
36 | }
37 |
38 | dependencies {
39 | api(libs.bundles.compose)
40 | api(libs.bundles.material.icons)
41 | api(libs.coil.compose)
42 | debugApi(libs.bundles.compose.preview)
43 |
44 | implementation(libs.androidx.core.ktx)
45 | implementation(libs.androidx.window)
46 | implementation(libs.androidx.palette.ktx)
47 | implementation(libs.androidx.graphics.shapes)
48 | // TODO - Revert these to impl once old code is removed from the project.
49 | api(libs.wavy.slider)
50 | api(libs.lottie.compose)
51 |
52 | }
53 |
54 | // TODO: It appears that Material3 components may be leaking into this project, which is intended to support Material2.
55 | // Please investigate if this issue is related to the Wavy Slider and resolve it. Once the main issue is fixed,
56 | // consider removing this block of code.
57 | configurations {
58 | all { exclude(group = "androidx.compose.material3", module = "material3") }
59 | }
--------------------------------------------------------------------------------
/core-ui/consumer-rules.pro:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/iZakirSheikh/Audiofy/7cc20318196ab355d99863b0192abd1a5af6ba14/core-ui/consumer-rules.pro
--------------------------------------------------------------------------------
/core-ui/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
--------------------------------------------------------------------------------
/core-ui/src/androidTest/java/com/zs/core_ui/ExampleInstrumentedTest.kt:
--------------------------------------------------------------------------------
1 | package com.zs.core_ui
2 |
3 | import androidx.test.platform.app.InstrumentationRegistry
4 | import androidx.test.ext.junit.runners.AndroidJUnit4
5 |
6 | import org.junit.Test
7 | import org.junit.runner.RunWith
8 |
9 | import org.junit.Assert.*
10 |
11 | /**
12 | * Instrumented test, which will execute on an Android device.
13 | *
14 | * See [testing documentation](http://d.android.com/tools/testing).
15 | */
16 | @RunWith(AndroidJUnit4::class)
17 | class ExampleInstrumentedTest {
18 | @Test
19 | fun useAppContext() {
20 | // Context of the app under test.
21 | val appContext = InstrumentationRegistry.getInstrumentation().targetContext
22 | assertEquals("com.zs.core_ui.test", appContext.packageName)
23 | }
24 | }
--------------------------------------------------------------------------------
/core-ui/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/core-ui/src/main/java/com/zs/core_ui/LazyList.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2024 Zakir Sheikh
3 | *
4 | * Created by Zakir Sheikh on 18-11-2024.
5 | *
6 | * Licensed under the Apache License, Version 2.0 (the "License");
7 | * you may not use this file except in compliance with the License.
8 | * You may obtain a copy of the License at
9 | *
10 | * https://www.apache.org/licenses/LICENSE-2.0
11 | *
12 | * Unless required by applicable law or agreed to in writing, software
13 | * distributed under the License is distributed on an "AS IS" BASIS,
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | * See the License for the specific language governing permissions and
16 | * limitations under the License.
17 | */
18 |
19 | package com.zs.core_ui
20 |
21 | import androidx.compose.foundation.ExperimentalFoundationApi
22 | import androidx.compose.foundation.lazy.grid.LazyGridItemScope
23 | import androidx.compose.foundation.lazy.grid.LazyGridScope
24 | import androidx.compose.foundation.lazy.grid.LazyGridState
25 | import androidx.compose.runtime.Composable
26 | import androidx.compose.ui.layout.Layout
27 | import androidx.compose.ui.layout.positionInParent
28 | import androidx.compose.ui.unit.constrainHeight
29 | import androidx.compose.ui.unit.constrainWidth
30 |
31 | private const val TAG = "LazyList"
32 |
33 | /**
34 | * A sticky header implementation that respects the top padding of the content.
35 | * This should be removed when an official solution is provided.
36 | * Currently, the only issue is that the sticky layout and the next item overlap before moving,
37 | * while the sticky header should start moving when the next item is about to become sticky.
38 | *
39 | * @param state The state of the LazyGrid.
40 | * @param key The key for the sticky header item.
41 | * @param contentType The type of content for the sticky header.
42 | * @param content The composable content for the sticky header.
43 | */
44 | @OptIn(ExperimentalFoundationApi::class)
45 | fun LazyGridScope.stickyHeader(
46 | state: LazyGridState,
47 | key: Any? = null,
48 | contentType: Any? = null,
49 | content: @Composable LazyGridItemScope.() -> Unit
50 | ) {
51 | stickyHeader(
52 | key = key,
53 | contentType = contentType,
54 | content = {
55 | Layout(content = { content() }) { measurables, constraints ->
56 | val placeable = measurables[0].measure(constraints)
57 | val width = constraints.constrainWidth(placeable.width)
58 | val height = constraints.constrainHeight(placeable.height)
59 | layout(width, height) {
60 | val posY = coordinates?.positionInParent()?.y?.toInt() ?: 0
61 | val paddingTop = state.layoutInfo.beforeContentPadding
62 | var top = (paddingTop - posY).coerceIn(0, paddingTop)
63 | placeable.placeRelativeWithLayer(0, top)
64 | }
65 | }
66 | }
67 | )
68 | }
69 |
--------------------------------------------------------------------------------
/core-ui/src/main/java/com/zs/core_ui/NightMode.kt:
--------------------------------------------------------------------------------
1 | package com.zs.core_ui
2 |
3 | enum class NightMode {
4 | /**
5 | * Night mode which uses always uses a light mode, enabling {@code notnight} qualified
6 | * resources regardless of the time.
7 | *
8 | * @see #setLocalNightMode(int)
9 | */
10 | YES,
11 |
12 | /**
13 | * Night mode which uses always uses a dark mode, enabling {@code night} qualified
14 | * resources regardless of the time.
15 | *
16 | * @see #setLocalNightMode(int)
17 | */
18 | NO,
19 |
20 | /**
21 | * Mode which uses the system's night mode setting to determine if it is night or not.
22 | *
23 | * @see #setLocalNightMode(int)
24 | */
25 | FOLLOW_SYSTEM
26 | }
--------------------------------------------------------------------------------
/core-ui/src/main/java/com/zs/core_ui/ScaleIndication.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2024 Zakir Sheikh
3 | *
4 | * Created by Zakir Sheikh on 10-08-2024.
5 | *
6 | * Licensed under the Apache License, Version 2.0 (the "License");
7 | * you may not use this file except in compliance with the License.
8 | * You may obtain a copy of the License at
9 | *
10 | * https://www.apache.org/licenses/LICENSE-2.0
11 | *
12 | * Unless required by applicable law or agreed to in writing, software
13 | * distributed under the License is distributed on an "AS IS" BASIS,
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | * See the License for the specific language governing permissions and
16 | * limitations under the License.
17 | */
18 |
19 | package com.zs.core_ui
20 |
21 | import androidx.compose.animation.core.Animatable
22 | import androidx.compose.animation.core.spring
23 | import androidx.compose.foundation.IndicationNodeFactory
24 | import androidx.compose.foundation.interaction.InteractionSource
25 | import androidx.compose.foundation.interaction.PressInteraction
26 | import androidx.compose.runtime.Stable
27 | import androidx.compose.ui.Modifier
28 | import androidx.compose.ui.geometry.Offset
29 | import androidx.compose.ui.graphics.drawscope.ContentDrawScope
30 | import androidx.compose.ui.graphics.drawscope.scale
31 | import androidx.compose.ui.node.DelegatableNode
32 | import androidx.compose.ui.node.DrawModifierNode
33 | import kotlinx.coroutines.flow.collectLatest
34 | import kotlinx.coroutines.launch
35 |
36 | private const val TAG = "ScaleIndication"
37 |
38 | private class ScaleIndicationNode(
39 | private val interactionSource: InteractionSource
40 | ) : Modifier.Node(), DrawModifierNode {
41 | var currentPressPosition: Offset = Offset.Zero
42 | val animatedScalePercent = Animatable(1f)
43 |
44 | private suspend fun animateToPressed(pressPosition: Offset) {
45 | currentPressPosition = pressPosition
46 | animatedScalePercent.animateTo(0.9f, spring())
47 | }
48 |
49 | private suspend fun animateToResting() {
50 | animatedScalePercent.animateTo(1f, spring())
51 | }
52 |
53 | override fun onAttach() {
54 | coroutineScope.launch {
55 | interactionSource.interactions.collectLatest { interaction ->
56 | when (interaction) {
57 | is PressInteraction.Press -> animateToPressed(interaction.pressPosition)
58 | is PressInteraction.Release -> animateToResting()
59 | is PressInteraction.Cancel -> animateToResting()
60 | }
61 | }
62 | }
63 | }
64 |
65 | override fun ContentDrawScope.draw() {
66 | scale(
67 | scale = animatedScalePercent.value,
68 | pivot = currentPressPosition
69 | ) {
70 | this@draw.drawContent()
71 | }
72 | }
73 | }
74 |
75 |
76 | @Stable
77 | fun scale(
78 | ): IndicationNodeFactory {
79 | return ScaleIndicationNodeFactory
80 | }
81 |
82 | private object ScaleIndicationNodeFactory : IndicationNodeFactory {
83 | override fun create(interactionSource: InteractionSource): DelegatableNode {
84 | return ScaleIndicationNode(interactionSource)
85 | }
86 |
87 | override fun hashCode(): Int = -1
88 |
89 | override fun equals(other: Any?) = other === this
90 | }
--------------------------------------------------------------------------------
/core-ui/src/main/java/com/zs/core_ui/WallpaperAccentColor.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2024 Zakir Sheikh
3 | *
4 | * Created by Zakir Sheikh on 12-08-2024.
5 | *
6 | * Licensed under the Apache License, Version 2.0 (the "License");
7 | * you may not use this file except in compliance with the License.
8 | * You may obtain a copy of the License at
9 | *
10 | * https://www.apache.org/licenses/LICENSE-2.0
11 | *
12 | * Unless required by applicable law or agreed to in writing, software
13 | * distributed under the License is distributed on an "AS IS" BASIS,
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | * See the License for the specific language governing permissions and
16 | * limitations under the License.
17 | */
18 |
19 | package com.zs.core_ui
20 |
21 | import android.app.WallpaperColors
22 | import android.graphics.Bitmap
23 | import android.os.Build
24 | import androidx.annotation.ColorInt
25 | import androidx.compose.runtime.Stable
26 | import androidx.compose.ui.graphics.Color
27 | import androidx.compose.ui.graphics.toArgb
28 | import androidx.palette.graphics.Palette
29 |
30 | /**
31 | * Extracts a color from a bitmap to be used as a wallpaper accent color.
32 | *
33 | * On Android O MR1 and above, it uses [WallpaperColors] to extract the primary color.
34 | * On older versions, it uses [Palette] to extract a vibrant color based on the provided theme.
35 | * If no suitable color is found in the palette, the provided default color is returned.
36 | *
37 | * @param bitmap The bitmap to extract the color from. Must not be null or empty.
38 | * @param isDark Whether the current theme is dark.
39 | * @param default The default color to use if no suitable color is found.
40 | *
41 | * @return The extracted color as an ARGB integer.
42 | */
43 | @Stable
44 | @ColorInt
45 | fun WallpaperAccentColor(
46 | bitmap: Bitmap?,
47 | isDark: Boolean,
48 | default: Color
49 | ): Int {
50 | if (bitmap == null || bitmap.isRecycled || bitmap.width == 0 || bitmap.height == 0) {
51 | return default.toArgb()
52 | }
53 | return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O_MR1) {
54 | WallpaperColors.fromBitmap(bitmap).primaryColor.toArgb()
55 | } else {
56 | // Generate a color palette from the bitmap
57 | val palette = Palette.from(bitmap).generate()
58 | val argb = default.toArgb() // Obtain the ARGB value of the default color for potential use
59 | // Pick a vibrant color based on the current theme, or use the default if none is found
60 | if (isDark) palette.getLightVibrantColor(argb) else palette.getDarkVibrantColor(argb)
61 | }
62 | }
63 |
64 |
65 |
--------------------------------------------------------------------------------
/core-ui/src/main/java/com/zs/core_ui/coil/RsBlurTransformation.kt:
--------------------------------------------------------------------------------
1 | @file:Suppress("unused")
2 |
3 | package com.zs.core_ui.coil
4 |
5 | import android.content.Context
6 | import android.graphics.Bitmap
7 | import android.graphics.Paint
8 | import android.renderscript.Allocation
9 | import android.renderscript.Element
10 | import android.renderscript.RenderScript
11 | import android.renderscript.ScriptIntrinsicBlur
12 | import androidx.annotation.RequiresApi
13 | import androidx.core.graphics.applyCanvas
14 | import androidx.core.graphics.createBitmap
15 | import coil.size.Size
16 | import coil.transform.Transformation
17 |
18 | private const val TAG = "RsBlurTransformation"
19 |
20 | private const val DEFAULT_RADIUS = 10f
21 | private const val DEFAULT_SAMPLING = 1f
22 |
23 | private val Bitmap.safeConfig: Bitmap.Config
24 | get() = config ?: Bitmap.Config.ARGB_8888
25 |
26 | /**
27 | * A [Transformation] that applies a Gaussian blur to an image.
28 | *
29 | * @param context The [Context] used to create a [RenderScript] instance.
30 | * @param radius The radius of the blur.
31 | * @param sampling The sampling multiplier used to scale the image. Values > 1
32 | * will downscale the image. Values between 0 and 1 will upscale the image.
33 | */
34 | @RequiresApi(18)
35 | class RsBlurTransformation @JvmOverloads constructor(
36 | private val context: Context,
37 | private val radius: Float = DEFAULT_RADIUS,
38 | private val sampling: Float = DEFAULT_SAMPLING
39 | ) : Transformation {
40 |
41 | init {
42 | require(radius in 0.0..25.0) { "radius must be in [0, 25]." }
43 | require(sampling > 0) { "sampling must be > 0." }
44 | }
45 |
46 | @Suppress("NullableToStringCall")
47 | override val cacheKey = "${RsBlurTransformation::class.java.name}-$radius-$sampling"
48 |
49 | override suspend fun transform(input: Bitmap, size: Size): Bitmap {
50 | val paint = Paint(Paint.ANTI_ALIAS_FLAG or Paint.FILTER_BITMAP_FLAG)
51 |
52 | val scaledWidth = (input.width / sampling).toInt()
53 | val scaledHeight = (input.height / sampling).toInt()
54 | val output = createBitmap(scaledWidth, scaledHeight, input.safeConfig)
55 | output.applyCanvas {
56 | scale(1 / sampling, 1 / sampling)
57 | drawBitmap(input, 0f, 0f, paint)
58 | }
59 |
60 | var script: RenderScript? = null
61 | var tmpInt: Allocation? = null
62 | var tmpOut: Allocation? = null
63 | var blur: ScriptIntrinsicBlur? = null
64 | try {
65 | script = RenderScript.create(context)
66 | tmpInt = Allocation.createFromBitmap(
67 | script,
68 | output,
69 | Allocation.MipmapControl.MIPMAP_NONE,
70 | Allocation.USAGE_SCRIPT
71 | )
72 | tmpOut = Allocation.createTyped(script, tmpInt.type)
73 | blur = ScriptIntrinsicBlur.create(script, Element.U8_4(script))
74 | blur.setRadius(radius)
75 | blur.setInput(tmpInt)
76 | blur.forEach(tmpOut)
77 | tmpOut.copyTo(output)
78 | } finally {
79 | script?.destroy()
80 | tmpInt?.destroy()
81 | tmpOut?.destroy()
82 | blur?.destroy()
83 | }
84 |
85 | return output
86 | }
87 | }
--------------------------------------------------------------------------------
/core-ui/src/main/java/com/zs/core_ui/shape/CompactDisk.kt:
--------------------------------------------------------------------------------
1 | package com.zs.core_ui.shape
2 |
3 | import androidx.compose.ui.geometry.Offset
4 | import androidx.compose.ui.geometry.Rect
5 | import androidx.compose.ui.geometry.Size
6 | import androidx.compose.ui.graphics.Outline
7 | import androidx.compose.ui.graphics.Path
8 | import androidx.compose.ui.graphics.PathOperation
9 | import androidx.compose.ui.graphics.Shape
10 | import androidx.compose.ui.unit.Density
11 | import androidx.compose.ui.unit.LayoutDirection
12 |
13 | /**
14 | * The shape of a compact disk.
15 | */
16 | val CompactDisk =
17 | object : Shape {
18 | override fun createOutline(
19 | size: Size,
20 | layoutDirection: LayoutDirection,
21 | density: Density
22 | ): Outline {
23 | val innerRadiusFraction = 0.259f
24 | val radius = kotlin.math.min(size.width, size.height) / 2f // the outer radius
25 | val innerRadius = radius * innerRadiusFraction // the inner radius
26 | val center = Offset(size.width / 2f, size.height / 2f) // the center of the shape
27 |
28 | // create a path for the outer circle
29 | val outerPath = Path().apply {
30 | addArc(
31 | Rect(
32 | left = center.x - radius,
33 | top = center.y - radius,
34 | right = center.x + radius,
35 | bottom = center.y + radius,
36 | ),
37 | startAngleDegrees = 0f,
38 | sweepAngleDegrees = 360f
39 | )
40 | }
41 |
42 | // create a path for the inner circle
43 | val innerPath = Path().apply {
44 | addArc(
45 | Rect(
46 | left = center.x - innerRadius,
47 | top = center.y - innerRadius,
48 | right = center.x + innerRadius,
49 | bottom = center.y + innerRadius,
50 | ),
51 | startAngleDegrees = 0f,
52 | sweepAngleDegrees = 360f
53 | )
54 | }
55 |
56 | // subtract the inner path from the outer path using PathOperation.Difference
57 | val resultPath = Path().apply {
58 | op(outerPath, innerPath, PathOperation.Difference)
59 | }
60 |
61 | // return an outline based on the result path
62 | return Outline.Generic(resultPath)
63 | }
64 | }
--------------------------------------------------------------------------------
/core-ui/src/main/java/com/zs/core_ui/shape/EndConcaveShape.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2024 Zakir Sheikh
3 | *
4 | * Created by 2024 on 11-10-2024.
5 | *
6 | * Licensed under the Apache License, Version 2.0 (the "License");
7 | * you may not use this file except in compliance with the License.
8 | * You may obtain a copy of the License at
9 | *
10 | * https://www.apache.org/licenses/LICENSE-2.0
11 | *
12 | * Unless required by applicable law or agreed to in writing, software
13 | * distributed under the License is distributed on an "AS IS" BASIS,
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | * See the License for the specific language governing permissions and
16 | * limitations under the License.
17 | */
18 |
19 | package com.zs.core_ui.shape
20 |
21 | import androidx.compose.ui.geometry.Size
22 | import androidx.compose.ui.graphics.Outline
23 | import androidx.compose.ui.graphics.Path
24 | import androidx.compose.ui.graphics.Shape
25 | import androidx.compose.ui.unit.Density
26 | import androidx.compose.ui.unit.Dp
27 | import androidx.compose.ui.unit.LayoutDirection
28 |
29 | private const val TAG = "EndConcaveShape"
30 |
31 | /**
32 | * A shape that represents a rectangle with a concave top.
33 | *
34 | * @param radius the radius of the concave curve at the top of the shape.
35 | */
36 | class EndConcaveShape(private val radius: Dp) : Shape {
37 | override fun createOutline(size: Size, layoutDirection: LayoutDirection, density: Density): Outline {
38 | val (w, h) = size
39 | val radiusPx = with(density) { radius.toPx() }
40 |
41 | val path = Path().apply {
42 | moveTo(w, 0f)
43 | quadraticTo(w -radiusPx, 0f, w - radiusPx, radiusPx)
44 | lineTo(w-radiusPx, h- radiusPx)
45 | quadraticTo(w - radiusPx, h, w, h)
46 | lineTo(0f, h)
47 | lineTo(0f, 0f)
48 | lineTo(w, 0f)
49 | }
50 | return Outline.Generic(path)
51 | }
52 | }
--------------------------------------------------------------------------------
/core-ui/src/main/java/com/zs/core_ui/shape/Folder.kt:
--------------------------------------------------------------------------------
1 | package com.zs.core_ui.shape
2 |
3 | import androidx.compose.foundation.shape.GenericShape
4 |
5 | private const val TAG = "Folder"
6 |
7 | /**
8 | * Defines a [GenericShape] simpler to [Icons.Rounded.Folder]
9 | */
10 | val FolderShape = GenericShape { (x, y), _ ->
11 | val radius = 0.18f * x
12 | val stepAt = 0.40f * x
13 | moveTo(radius, 0f)
14 | lineTo(stepAt, 0f)
15 | lineTo(stepAt + radius, radius)
16 | lineTo(x - radius, radius)
17 | quadraticTo(x, radius, x, 2 * radius)
18 | lineTo(x, y - radius)
19 | quadraticTo(x, y, x - radius, y)
20 | lineTo(radius, y)
21 | quadraticTo(0f, y, 0f, y - radius)
22 | lineTo(0f, radius)
23 | quadraticTo(0f, 0f, radius, 0f)
24 | close()
25 | }
26 |
--------------------------------------------------------------------------------
/core-ui/src/main/java/com/zs/core_ui/shape/HeartShape.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2025 sheik
3 | *
4 | * Created by sheik on 03-03-2025.
5 | *
6 | * Licensed under the Apache License, Version 2.0 (the "License");
7 | * you may not use this file except in compliance with the License.
8 | * You may obtain a copy of the License at
9 | *
10 | * https://www.apache.org/licenses/LICENSE-2.0
11 | *
12 | * Unless required by applicable law or agreed to in writing, software
13 | * distributed under the License is distributed on an "AS IS" BASIS,
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | * See the License for the specific language governing permissions and
16 | * limitations under the License.
17 | */
18 |
19 | package com.zs.core_ui.shape
20 |
21 | import androidx.compose.foundation.shape.GenericShape
22 |
23 | /**
24 | * Represents the shape of the [Heart](https://user-images.githubusercontent.com/6584143/125109896-8e16ae00-e101-11eb-99ee-4d4a7696c667.png)
25 | */
26 | val HeartShape = GenericShape { size, _ ->
27 | val h = size.height
28 | val w = size.width
29 | lineTo(0.5f*w, 0.25f*h)
30 | cubicTo(0.5f*w, 0.225f*h, 0.458333333333333f*w, 0.125f*h, 0.291666666666667f*w, 0.125f*h)
31 | cubicTo(0.0416666666666667f*w, 0.125f*h, 0.0416666666666667f*w, 0.4f*h, 0.0416666666666667f*w, 0.4f*h)
32 | cubicTo(0.0416666666666667f*w, 0.583333333333333f*h, 0.208333333333333f*w, 0.766666666666667f*h, 0.5f*w, 0.916666666666667f*h)
33 | cubicTo(0.791666666666667f*w, 0.766666666666667f*h, 0.958333333333333f*w, 0.583333333333333f*h, 0.958333333333333f*w, 0.4f*h)
34 | cubicTo(0.958333333333333f*w, 0.4f*h, 0.958333333333333f*w, 0.125f*h, 0.708333333333333f*w, 0.125f*h)
35 | cubicTo(0.583333333333333f*w, 0.125f*h, 0.5f*w, 0.225f*h, 0.5f*w, 0.25f*h)
36 | close()
37 | }
--------------------------------------------------------------------------------
/core-ui/src/main/java/com/zs/core_ui/shape/RoundedPolygonShape.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2024 Zakir Sheikh
3 | *
4 | * Created by 2024 on 20-09-2024.
5 | *
6 | * Licensed under the Apache License, Version 2.0 (the "License");
7 | * you may not use this file except in compliance with the License.
8 | * You may obtain a copy of the License at
9 | *
10 | * https://www.apache.org/licenses/LICENSE-2.0
11 | *
12 | * Unless required by applicable law or agreed to in writing, software
13 | * distributed under the License is distributed on an "AS IS" BASIS,
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | * See the License for the specific language governing permissions and
16 | * limitations under the License.
17 | */
18 |
19 | package com.zs.core_ui.shape
20 |
21 | import androidx.annotation.FloatRange
22 | import androidx.annotation.IntRange
23 | import androidx.compose.ui.geometry.Rect
24 | import androidx.compose.ui.geometry.Size
25 | import androidx.compose.ui.graphics.Matrix
26 | import androidx.compose.ui.graphics.Outline
27 | import androidx.compose.ui.graphics.Path
28 | import androidx.compose.ui.graphics.Shape
29 | import androidx.compose.ui.graphics.asComposePath
30 | import androidx.compose.ui.unit.Density
31 | import androidx.compose.ui.unit.LayoutDirection
32 | import androidx.graphics.shapes.CornerRounding
33 | import androidx.graphics.shapes.RoundedPolygon
34 | import androidx.graphics.shapes.toPath
35 | import kotlin.math.max
36 |
37 | private fun RoundedPolygon.getBounds() = calculateBounds().let { Rect(it[0], it[1], it[2], it[3]) }
38 |
39 | /**
40 | * A shape representing a rounded polygon.
41 | *
42 | * This shape creates a polygon with the specified number of sides and applies rounding to its corners.
43 | * The rounding is controlled by the `rounding` parameter, which represents the fraction of the side length used for rounding.
44 | *
45 | * @param sides The number of sides of the polygon. Must be 3 or greater.
46 | * @param rounding The rounding factor, as a fraction of the side length. Should be a value between 0.0 (no rounding) and 1.0 (maximum rounding).
47 | */
48 | class RoundedPolygonShape(
49 | @IntRange(3) private val sides: Int,
50 | @FloatRange(0.0, 1.0) private val rounding: Float = 0.0f
51 | ): Shape {
52 |
53 | private val polygon = RoundedPolygon(sides, rounding = CornerRounding(rounding, 1f))
54 | private var path = Path()
55 | private var matrix = Matrix()
56 |
57 | override fun createOutline(size: Size, layoutDirection: LayoutDirection, density: Density): Outline {
58 | path.rewind()
59 | path = polygon.toPath().asComposePath()
60 | matrix.reset()
61 | val bounds = polygon.getBounds()
62 | val maxDimension = max(bounds.width, bounds.height)
63 | matrix.scale(size.width / maxDimension, size.height / maxDimension)
64 | matrix.translate(-bounds.left, -bounds.top)
65 |
66 | path.transform(matrix)
67 | return Outline.Generic(path)
68 | }
69 | }
--------------------------------------------------------------------------------
/core-ui/src/main/java/com/zs/core_ui/shape/RoundedStarShape.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2025 Zakir Sheikh
3 | *
4 | * Created by Zakir Sheikh on 26-02-2025.
5 | *
6 | * Licensed under the Apache License, Version 2.0 (the "License");
7 | * you may not use this file except in compliance with the License.
8 | * You may obtain a copy of the License at
9 | *
10 | * https://www.apache.org/licenses/LICENSE-2.0
11 | *
12 | * Unless required by applicable law or agreed to in writing, software
13 | * distributed under the License is distributed on an "AS IS" BASIS,
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | * See the License for the specific language governing permissions and
16 | * limitations under the License.
17 | */
18 |
19 | package com.zs.core_ui.shape
20 |
21 | import androidx.compose.ui.geometry.Size
22 | import androidx.compose.ui.graphics.Outline
23 | import androidx.compose.ui.graphics.Path
24 | import androidx.compose.ui.graphics.Shape
25 | import androidx.compose.ui.unit.Density
26 | import androidx.compose.ui.unit.LayoutDirection
27 | import kotlin.math.PI
28 | import kotlin.math.cos
29 | import kotlin.math.min
30 | import kotlin.math.sin
31 |
32 | /**
33 | * Shape describing star with rounded corners
34 | *
35 | * Note: The shape draws within the minimum of provided width and height so can't be used to create stretched shape.
36 | *
37 | * @param sides number of sides.
38 | * @param curve a double value between 0.0 - 1.0 for modifying star curve.
39 | * @param rotation value between 0 - 360
40 | * @param iterations a value between 0 - 360 that determines the quality of star shape.
41 | */
42 | class RoundedStarShape(
43 | private val sides: Int,
44 | private val curve: Double = 0.09,
45 | private val rotation: Float = 0f,
46 | iterations: Int = 360,
47 | ) : Shape {
48 |
49 | private companion object {
50 | const val TWO_PI = 2 * PI
51 | }
52 |
53 | private val steps = (TWO_PI) / min(iterations, 360)
54 | private val rotationDegree = (PI / 180) * rotation
55 |
56 | override fun createOutline(
57 | size: Size,
58 | layoutDirection: LayoutDirection,
59 | density: Density
60 | ): Outline = Outline.Generic(Path().apply {
61 |
62 |
63 | val r = min(size.height, size.width) * 0.4 * mapRange(1.0, 0.0, 0.5, 1.0, curve)
64 |
65 | val xCenter = size.width * .5f
66 | val yCenter = size.height * .5f
67 |
68 | moveTo(xCenter, yCenter)
69 |
70 | var t = 0.0
71 |
72 | while (t <= TWO_PI) {
73 | val x = r * (cos(t - rotationDegree) * (1 + curve * cos(sides * t)))
74 | val y = r * (sin(t - rotationDegree) * (1 + curve * cos(sides * t)))
75 | lineTo((x + xCenter).toFloat(), (y + yCenter).toFloat())
76 |
77 | t += steps
78 | }
79 |
80 | val x = r * (cos(t - rotationDegree) * (1 + curve * cos(sides * t)))
81 | val y = r * (sin(t - rotationDegree) * (1 + curve * cos(sides * t)))
82 | lineTo((x + xCenter).toFloat(), (y + yCenter).toFloat())
83 |
84 | })
85 |
86 |
87 | private fun mapRange(a: Double, b: Double, c: Double, d: Double, x: Double): Double {
88 | return (x - a) / (b - a) * (d - c) + c
89 | }
90 | }
--------------------------------------------------------------------------------
/core-ui/src/main/java/com/zs/core_ui/shape/TagShape.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2025 Zakir Sheikh
3 | *
4 | * Created by Zakir Sheikh on 03-03-2025.
5 | *
6 | * Licensed under the Apache License, Version 2.0 (the "License");
7 | * you may not use this file except in compliance with the License.
8 | * You may obtain a copy of the License at
9 | *
10 | * https://www.apache.org/licenses/LICENSE-2.0
11 | *
12 | * Unless required by applicable law or agreed to in writing, software
13 | * distributed under the License is distributed on an "AS IS" BASIS,
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | * See the License for the specific language governing permissions and
16 | * limitations under the License.
17 | */
18 |
19 | package com.zs.core_ui.shape
20 |
21 | import androidx.compose.foundation.shape.GenericShape
22 |
23 | // TODO: TagShape from a val to a class accepting cornerRadius and maybe use layoutDirection for RTL support.
24 |
25 | /**
26 | * Represents the shape of the [Tag](https://user-images.githubusercontent.com/6584143/125108986-63782580-e100-11eb-9438-19e5594f4fea.png)
27 | *
28 | */
29 | val TagShape = GenericShape { size, _ ->
30 | val w = size.width / 27f
31 | val h = size.height / 11f
32 |
33 | moveTo(5f * w, 0f * h)
34 | lineTo(25f * w, 0f * h)
35 | cubicTo(26f * w, 0f, 27f * w, 1f * h, 27f * w, 2f * h)
36 | lineTo(27f * w, 9f * h)
37 | cubicTo(27f * w, 10f * h, 26f * w, 11f * h, 25f * w, 11f * h)
38 | lineTo(5f * w, 11f * h)
39 | lineTo(1f * w, 7f * h)
40 | cubicTo(0f * w, 6f * h, 0f * w, 5f * h, 1f * w, 4f * h)
41 | }
--------------------------------------------------------------------------------
/core-ui/src/main/java/com/zs/core_ui/shape/TopConcaveShape.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2024 Zakir Sheikh
3 | *
4 | * Created by 2024 on 11-10-2024.
5 | *
6 | * Licensed under the Apache License, Version 2.0 (the "License");
7 | * you may not use this file except in compliance with the License.
8 | * You may obtain a copy of the License at
9 | *
10 | * https://www.apache.org/licenses/LICENSE-2.0
11 | *
12 | * Unless required by applicable law or agreed to in writing, software
13 | * distributed under the License is distributed on an "AS IS" BASIS,
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | * See the License for the specific language governing permissions and
16 | * limitations under the License.
17 | */
18 |
19 | package com.zs.core_ui.shape
20 |
21 | import androidx.compose.ui.geometry.Size
22 | import androidx.compose.ui.graphics.Outline
23 | import androidx.compose.ui.graphics.Path
24 | import androidx.compose.ui.graphics.Shape
25 | import androidx.compose.ui.unit.Density
26 | import androidx.compose.ui.unit.Dp
27 | import androidx.compose.ui.unit.LayoutDirection as ld
28 |
29 | /**
30 | * A shape that represents a rectangle with a concave top.
31 | *
32 | * @param radius the radius of the concave curve at the top of the shape.
33 | */
34 | class TopConcaveShape(private val radius: Dp) : Shape {
35 | override fun createOutline(size: Size, layoutDirection: ld, density: Density): Outline {
36 | val (w, h) = size
37 | val radiusPx = with(density) { radius.toPx() }
38 |
39 | val path = Path().apply {
40 | // Start from top left corner with a radius
41 | moveTo(radiusPx, radiusPx)
42 | quadraticTo(0f, radiusPx, 0f, 0f)
43 |
44 | // Draw left side
45 | lineTo(0f, h)
46 | lineTo(w, h)
47 | lineTo(w, 0f)
48 |
49 | // Draw top right corner with a radius
50 | quadraticTo(w, radiusPx, w - radiusPx, radiusPx)
51 |
52 | // Close the path
53 | lineTo(radiusPx, radiusPx)
54 | }
55 | return Outline.Generic(path)
56 | }
57 | }
--------------------------------------------------------------------------------
/core-ui/src/test/java/com/zs/core_ui/ExampleUnitTest.kt:
--------------------------------------------------------------------------------
1 | package com.zs.core_ui
2 |
3 | import org.junit.Test
4 |
5 | import org.junit.Assert.*
6 |
7 | /**
8 | * Example local unit test, which will execute on the development machine (host).
9 | *
10 | * See [testing documentation](http://d.android.com/tools/testing).
11 | */
12 | class ExampleUnitTest {
13 | @Test
14 | fun addition_isCorrect() {
15 | assertEquals(4, 2 + 2)
16 | }
17 | }
--------------------------------------------------------------------------------
/core/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/core/build.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | alias(libs.plugins.android.library)
3 | alias(libs.plugins.jetbrains.kotlin.android)
4 | alias(libs.plugins.ksp)
5 | }
6 |
7 | android {
8 | namespace = "com.zs.core"
9 | compileSdk = 34
10 | defaultConfig {
11 | minSdk = 21
12 | testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
13 | consumerProguardFiles("consumer-rules.pro")
14 | }
15 | buildTypes {
16 | release {
17 | isMinifyEnabled = false
18 | proguardFiles(
19 | getDefaultProguardFile("proguard-android-optimize.txt"),
20 | "proguard-rules.pro"
21 | )
22 | }
23 | }
24 | compileOptions {
25 | sourceCompatibility = JavaVersion.VERSION_1_8
26 | targetCompatibility = JavaVersion.VERSION_1_8
27 | }
28 | kotlinOptions { jvmTarget = "1.8" }
29 | }
30 |
31 | dependencies {
32 | implementation(libs.androidx.core.ktx)
33 | implementation(libs.bundles.room)
34 | implementation(libs.google.billing.ktx)
35 |
36 | ksp(libs.room.compiler)
37 | api(libs.bundles.media3)
38 | api(libs.mp3agic)
39 | }
--------------------------------------------------------------------------------
/core/consumer-rules.pro:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/iZakirSheikh/Audiofy/7cc20318196ab355d99863b0192abd1a5af6ba14/core/consumer-rules.pro
--------------------------------------------------------------------------------
/core/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
--------------------------------------------------------------------------------
/core/src/androidTest/java/com/zs/core/ExampleInstrumentedTest.kt:
--------------------------------------------------------------------------------
1 | package com.zs.core
2 |
3 | import androidx.test.platform.app.InstrumentationRegistry
4 | import androidx.test.ext.junit.runners.AndroidJUnit4
5 |
6 | import org.junit.Test
7 | import org.junit.runner.RunWith
8 |
9 | import org.junit.Assert.*
10 |
11 | /**
12 | * Instrumented test, which will execute on an Android device.
13 | *
14 | * See [testing documentation](http://d.android.com/tools/testing).
15 | */
16 | @RunWith(AndroidJUnit4::class)
17 | class ExampleInstrumentedTest {
18 | @Test
19 | fun useAppContext() {
20 | // Context of the app under test.
21 | val appContext = InstrumentationRegistry.getInstrumentation().targetContext
22 | assertEquals("com.zs.core.test", appContext.packageName)
23 | }
24 | }
--------------------------------------------------------------------------------
/core/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/core/src/main/java/com/zs/core/Temp.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2024 Zakir Sheikh
3 | *
4 | * Created by 2024 on 26-09-2024.
5 | *
6 | * Licensed under the Apache License, Version 2.0 (the "License");
7 | * you may not use this file except in compliance with the License.
8 | * You may obtain a copy of the License at
9 | *
10 | * https://www.apache.org/licenses/LICENSE-2.0
11 | *
12 | * Unless required by applicable law or agreed to in writing, software
13 | * distributed under the License is distributed on an "AS IS" BASIS,
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | * See the License for the specific language governing permissions and
16 | * limitations under the License.
17 | */
18 |
19 | package com.zs.core
20 |
21 |
--------------------------------------------------------------------------------
/core/src/main/java/com/zs/core/paymaster/Purchase.kt:
--------------------------------------------------------------------------------
1 |
2 | /*
3 | * Copyright 2024 Zakir Sheikh
4 | *
5 | * Created by 2024 on 02-10-2024.
6 | *
7 | * Licensed under the Apache License, Version 2.0 (the "License");
8 | * you may not use this file except in compliance with the License.
9 | * You may obtain a copy of the License at
10 | *
11 | * https://www.apache.org/licenses/LICENSE-2.0
12 | *
13 | * Unless required by applicable law or agreed to in writing, software
14 | * distributed under the License is distributed on an "AS IS" BASIS,
15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 | * See the License for the specific language governing permissions and
17 | * limitations under the License.
18 | */
19 |
20 | package com.zs.core.paymaster
21 |
22 |
23 | import com.android.billingclient.api.ProductDetails
24 | import com.android.billingclient.api.Purchase as GP_Purchase
25 |
26 | /**
27 | * Representing a purchase.
28 | *
29 | * This class wraps a [GP_Purchase] purchase object and provides convenient access to its properties.
30 | *
31 | * @property value The underlying [GP_Purchase] purchase object.
32 | * @property isAcknowledged Whether the purchase has been acknowledged.
33 | * @property state The state of the purchase. [com.android.billingclient.api.Purchase.PurchaseState]
34 | * @property quantity The quantity of the purchased items.
35 | * @property id The ID of the first product in the purchase, as assigned in Play Console Billing.
36 | * @property time The time of the purchase.
37 | */
38 | @JvmInline
39 | value class Purchase internal constructor(internal val value: GP_Purchase) {
40 | val isAcknowledged get() = value.isAcknowledged
41 | val state get() = value.purchaseState
42 | val quantity get() = value.quantity
43 | val id get() = value.products.first()
44 | val time get() = value.purchaseTime
45 |
46 | constructor(JSONInfo: String, signature: String): this(GP_Purchase(JSONInfo, signature))
47 | }
48 |
49 | /**
50 | * @return `true` if this [Purchase] object is `non-null`, has been `acknowledged`, and is in the
51 | * [GP_Purchase.PurchaseState.PURCHASED] state. Returns `false` otherwise.
52 | */
53 | val Purchase?.purchased get() = if (this == null) false else isAcknowledged && state == GP_Purchase.PurchaseState.PURCHASED
54 |
55 | /**
56 | * Represents a product.
57 | *
58 | * @property value The underlying [ProductDetails] object.
59 | * @property title The title of the product.
60 | * @property description The description of the product.
61 | * @property formattedPrice The formatted price of the product, or null if not available.
62 | * @property id The ID of the product.
63 | */
64 | @JvmInline
65 | value class ProductInfo internal constructor(internal val value: ProductDetails) {
66 | val title get() = value.title.trim()
67 | val description get() = value.description.trim()
68 | val formattedPrice get() = value.oneTimePurchaseOfferDetails?.formattedPrice?.trim()
69 | val id get() = value.productId
70 | }
71 |
72 |
--------------------------------------------------------------------------------
/core/src/main/java/com/zs/core/playback/PlaybackControllerImpl.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2024 Zakir Sheikh
3 | *
4 | * Created by Zakir Sheikh on 17-11-2024.
5 | *
6 | * Licensed under the Apache License, Version 2.0 (the "License");
7 | * you may not use this file except in compliance with the License.
8 | * You may obtain a copy of the License at
9 | *
10 | * https://www.apache.org/licenses/LICENSE-2.0
11 | *
12 | * Unless required by applicable law or agreed to in writing, software
13 | * distributed under the License is distributed on an "AS IS" BASIS,
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | * See the License for the specific language governing permissions and
16 | * limitations under the License.
17 | */
18 |
19 | package com.zs.core.playback
20 |
21 | import android.content.ComponentName
22 | import android.content.Context
23 | import androidx.media3.session.MediaBrowser
24 | import androidx.media3.session.SessionToken
25 | import com.zs.core.await
26 |
27 | private const val TAG = "PlaybackControllerImpl"
28 |
29 | // TODO: currently a quickfix requirement. find better alternative.
30 | private fun Context.browser(listener: MediaBrowser.Listener) =
31 | MediaBrowser
32 | .Builder(this, SessionToken(this, ComponentName(this, Playback::class.java)))
33 | .setListener(listener)
34 | .buildAsync()
35 |
36 | internal class PlaybackControllerImpl(
37 | val context: Context
38 | ) : PlaybackController, MediaBrowser.Listener {
39 |
40 | // TODO: A quickfix, find better alternative of doing this.
41 |
42 | // The fBrowser variable is lazily initialized with context.browser(this).
43 | // Whenever fBrowser is accessed, the getter checks if the current value is cancelled.
44 | // If it is cancelled, it re-initializes fBrowser with a new context.browser(this).
45 | // Otherwise, it retains the current value.
46 | // The goal is to ensure that fBrowser always holds a valid browser context,
47 | // reinitializing it if the current one has been cancelled.
48 | private var fBrowser = context.browser(this)
49 | get() {
50 | field = if (field.isCancelled) context.browser(this) else field
51 | return field
52 | }
53 |
54 |
55 | override suspend fun setMediaFiles(values: List): Int {
56 | val browser = fBrowser.await()
57 | // make sure the items are distinct.
58 | val list = values.distinctBy { it.mediaUri }
59 | // set the media items; this will automatically clear the old ones.
60 | browser.setMediaItems(list.map { it.value })
61 | // return how many have been added to the list.
62 | return list.size
63 | }
64 |
65 | override suspend fun play(playWhenReady: Boolean) {
66 | val browser = fBrowser.await()
67 | browser.playWhenReady = playWhenReady
68 | browser.play()
69 | }
70 |
71 | override suspend fun clear() {
72 | val browser = fBrowser.await()
73 | browser.clearMediaItems()
74 | }
75 |
76 | override suspend fun connect(){
77 | val browser = fBrowser.await()
78 | /*TODO: Do nothing*/
79 | }
80 | }
--------------------------------------------------------------------------------
/core/src/main/java/com/zs/core/util/PathUtil.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2024 Zakir Sheikh
3 | *
4 | * Created by Zakir Sheikh on 11-07-2024.
5 | *
6 | * Licensed under the Apache License, Version 2.0 (the "License");
7 | * you may not use this file except in compliance with the License.
8 | * You may obtain a copy of the License at
9 | *
10 | * https://www.apache.org/licenses/LICENSE-2.0
11 | *
12 | * Unless required by applicable law or agreed to in writing, software
13 | * distributed under the License is distributed on an "AS IS" BASIS,
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | * See the License for the specific language governing permissions and
16 | * limitations under the License.
17 | */
18 |
19 | package com.zs.core.util
20 |
21 | import org.jetbrains.annotations.Contract
22 |
23 | /**
24 | * A scope for [PathUtils] functions.
25 | */
26 | object PathUtils {
27 | /**
28 | * The Unix separator character.
29 | */
30 | const val PATH_SEPARATOR = '/'
31 |
32 | /**
33 | * The extension separator character.
34 | * @since 1.4
35 | */
36 | const val EXTENSION_SEPARATOR = '.'
37 |
38 | const val HIDDEN_PATTERN = "/."
39 |
40 | /**
41 | * Gets the name minus the path from a full fileName.
42 | *
43 | * @param path the fileName to query
44 | * @return the name of the file without the path
45 | */
46 | fun name(path: String): String =
47 | path.substring(path.lastIndexOf(PATH_SEPARATOR) + 1)
48 |
49 | /**
50 | * @return parent of path.
51 | */
52 | fun parent(path: String): String =
53 | path.replace("$PATH_SEPARATOR${name(path = path)}", "")
54 |
55 | /**
56 | * Returns the file extension or null string if there is no extension. This method is a
57 | * convenience method for obtaining the extension of a url and has undefined
58 | * results for other Strings.
59 | * It is Assumed that Url is file
60 | *
61 | * @param url Url of the file
62 | *
63 | * @return extension
64 | */
65 | fun extension(url: String): String? =
66 | if (url.contains(EXTENSION_SEPARATOR))
67 | url.substring(url.lastIndexOf(EXTENSION_SEPARATOR) + 1).lowercase()
68 | else
69 | null
70 |
71 | /**
72 | * Checks if the file or its ancestors are hidden in System.
73 | */
74 | @Contract(pure = true)
75 | fun areAncestorsHidden(path: String): Boolean =
76 | path.contains(HIDDEN_PATTERN)
77 | }
--------------------------------------------------------------------------------
/core/src/main/java/com/zs/core/util/Util.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2024 Zakir Sheikh
3 | *
4 | * Created by Zakir Sheikh on 11-07-2024.
5 | *
6 | * Licensed under the Apache License, Version 2.0 (the "License");
7 | * you may not use this file except in compliance with the License.
8 | * You may obtain a copy of the License at
9 | *
10 | * https://www.apache.org/licenses/LICENSE-2.0
11 | *
12 | * Unless required by applicable law or agreed to in writing, software
13 | * distributed under the License is distributed on an "AS IS" BASIS,
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | * See the License for the specific language governing permissions and
16 | * limitations under the License.
17 | */
18 |
19 | package com.zs.core.util
20 |
21 | import android.media.MediaMetadataRetriever
22 | import android.util.Log
23 | import java.util.regex.Matcher
24 | import java.util.regex.Pattern
25 |
26 | private const val TAG = "Util"
27 |
28 | //language=RegExp
29 | private val ISO6709LocationPattern = Pattern.compile("([+\\-][0-9.]+)([+\\-][0-9.]+)")
30 |
31 |
32 | internal inline fun T.runCatching(tag: String, block: T.() -> R): R? {
33 | return try {
34 | block()
35 | } catch (e: Throwable) {
36 | Log.d(tag, "runCatching: $e")
37 | null
38 | }
39 | }
40 |
41 | /**
42 | * This method parses the given string representing a geographic point location by coordinates in ISO 6709 format
43 | * and returns the latitude and the longitude in float. If `location` is not in ISO 6709 format,
44 | * this method returns `null`
45 | *
46 | * @param location a String representing a geographic point location by coordinates in ISO 6709 format
47 | * @return `null` if the given string is not as expected, an array of floats with size 2,
48 | * where the first element represents latitude and the second represents longitude, otherwise.
49 | */
50 | val MediaMetadataRetriever.latLong: DoubleArray?
51 | get() = runCatching(TAG) {
52 | val location =
53 | extractMetadata(MediaMetadataRetriever.METADATA_KEY_LOCATION) ?: return@runCatching null
54 | val m: Matcher = ISO6709LocationPattern.matcher(location)
55 | if (m.find() && m.groupCount() == 2) {
56 | val latstr: String = m.group(1) ?: return@runCatching null
57 | val lonstr: String = m.group(2) ?: return@runCatching null
58 | val lat = latstr.toDouble()
59 | val lon = lonstr.toDouble()
60 | doubleArrayOf(lat, lon)
61 | } else null
62 | }
--------------------------------------------------------------------------------
/core/src/test/java/com/zs/core/ExampleUnitTest.kt:
--------------------------------------------------------------------------------
1 | package com.zs.core
2 |
3 | import org.junit.Test
4 |
5 | import org.junit.Assert.*
6 |
7 | /**
8 | * Example local unit test, which will execute on the development machine (host).
9 | *
10 | * See [testing documentation](http://d.android.com/tools/testing).
11 | */
12 | class ExampleUnitTest {
13 | @Test
14 | fun addition_isCorrect() {
15 | assertEquals(4, 2 + 2)
16 | }
17 | }
--------------------------------------------------------------------------------
/crowdin.yml:
--------------------------------------------------------------------------------
1 | preserve_hierarchy: true
2 | # project_id: 741779
3 | files:
4 | - source: "**/values/strings.xml"
5 | translation: "/values-%android_code%/%original_file_name%" #CORRECT
6 | update_strings: true
7 | cleanup_mode: true
8 | import_eq_suggestions: true
9 | auto_approve_imported: true
10 | translate_hidden: true
11 | # This will be converted to 'app/src/main/res/values-%two_letter_code%/%original_file_name%' export pattern for each file
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | ## For more details on how to configure your build environment visit
2 | # http://www.gradle.org/docs/current/userguide/build_environment.html
3 | #
4 | # Specifies the JVM arguments used for the daemon process.
5 | # The setting is particularly useful for tweaking memory settings.
6 | # Default value: -Xmx1024m -XX:MaxPermSize=256m
7 | org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
8 | # When configured, Gradle will run in incubating parallel mode.
9 | # This option should only be used with decoupled projects. More details, visit
10 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
11 | android.useAndroidX=true
12 | # Kotlin code style for this project: "official" or "obsolete":
13 | kotlin.code.style=official
14 | # org.gradle.parallel=true
15 | #Fri Dec 15 20:21:04 IST 2023
16 | # Enable build config feature to specify app's build configuration
17 | # android.defaults.buildfeatures.buildconfig=true
18 | # Disable Jetifier feature to avoid compatibility issues with newer Android versions
19 | android.enableJetifier=false
20 | # Prevent caching non-final resource IDs to avoid stale resource IDs
21 | android.nonFinalResIds=false
22 | # Enable non-transitive R class feature to avoid conflicts between R classes in dependencies
23 | android.nonTransitiveRClass=true
24 | #Enable k2 to hopefully to improve autocomplete performance
25 | # kotlin.experimental.tryK2=true
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/iZakirSheikh/Audiofy/7cc20318196ab355d99863b0192abd1a5af6ba14/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Wed Jul 19 10:22:34 IST 2023
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.11.1-bin.zip
5 | zipStoreBase=GRADLE_USER_HOME
6 | zipStorePath=wrapper/dists
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @rem
2 | @rem Copyright 2015 the original author or authors.
3 | @rem
4 | @rem Licensed under the Apache License, Version 2.0 (the "License");
5 | @rem you may not use this file except in compliance with the License.
6 | @rem You may obtain a copy of the License at
7 | @rem
8 | @rem https://www.apache.org/licenses/LICENSE-2.0
9 | @rem
10 | @rem Unless required by applicable law or agreed to in writing, software
11 | @rem distributed under the License is distributed on an "AS IS" BASIS,
12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | @rem See the License for the specific language governing permissions and
14 | @rem limitations under the License.
15 | @rem
16 |
17 | @if "%DEBUG%" == "" @echo off
18 | @rem ##########################################################################
19 | @rem
20 | @rem Gradle startup script for Windows
21 | @rem
22 | @rem ##########################################################################
23 |
24 | @rem Set local scope for the variables with windows NT shell
25 | if "%OS%"=="Windows_NT" setlocal
26 |
27 | set DIRNAME=%~dp0
28 | if "%DIRNAME%" == "" set DIRNAME=.
29 | set APP_BASE_NAME=%~n0
30 | set APP_HOME=%DIRNAME%
31 |
32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter.
33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
34 |
35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
37 |
38 | @rem Find java.exe
39 | if defined JAVA_HOME goto findJavaFromJavaHome
40 |
41 | set JAVA_EXE=java.exe
42 | %JAVA_EXE% -version >NUL 2>&1
43 | if "%ERRORLEVEL%" == "0" goto execute
44 |
45 | echo.
46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
47 | echo.
48 | echo Please set the JAVA_HOME variable in your environment to match the
49 | echo location of your Java installation.
50 |
51 | goto fail
52 |
53 | :findJavaFromJavaHome
54 | set JAVA_HOME=%JAVA_HOME:"=%
55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
56 |
57 | if exist "%JAVA_EXE%" goto execute
58 |
59 | echo.
60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
61 | echo.
62 | echo Please set the JAVA_HOME variable in your environment to match the
63 | echo location of your Java installation.
64 |
65 | goto fail
66 |
67 | :execute
68 | @rem Setup the command line
69 |
70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
71 |
72 |
73 | @rem Execute Gradle
74 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
75 |
76 | :end
77 | @rem End local scope for the variables with windows NT shell
78 | if "%ERRORLEVEL%"=="0" goto mainEnd
79 |
80 | :fail
81 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
82 | rem the _cmd.exe /c_ return code!
83 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
84 | exit /b 1
85 |
86 | :mainEnd
87 | if "%OS%"=="Windows_NT" endlocal
88 |
89 | :omega
90 |
--------------------------------------------------------------------------------
/settings.gradle.kts:
--------------------------------------------------------------------------------
1 | pluginManagement {
2 | repositories {
3 | google()
4 | mavenCentral()
5 | gradlePluginPortal()
6 | }
7 | }
8 | dependencyResolutionManagement {
9 | repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
10 | repositories {
11 | google()
12 | mavenCentral()
13 | maven(url ="https://android-sdk.is.com/")
14 | maven(url = "https://jitpack.io")
15 | }
16 | }
17 | rootProject.name = "Audiofy"
18 | include(":app")
19 |
20 |
21 |
22 | include(":codex")
23 | include(":ads")
24 | include(":core-ui")
25 | include(":core")
26 | include(":widget")
27 |
--------------------------------------------------------------------------------
/stability_config.conf:
--------------------------------------------------------------------------------
1 | // Consider LocalDateTime stable
2 | //java.time.LocalDateTime
3 | // Consider my datalayer stable
4 | //com.datalayer.*
5 | // Consider my datalayer and all submodules stable
6 | //com.datalayer.**
7 | // Consider my generic type stable based off it is first type parameter only
8 | //com.example.GenericClass<*,_>
9 | // Consider NowPlaying stable;
10 | // NowPlaying is inline class and hence need the internal to be declared as stable
11 | com.zs.core.playback.NowPlaying
12 | android.content.Intent
13 | com.android.billingclient.api.Purchase
14 | com.android.billingclient.api.ProductDetails
--------------------------------------------------------------------------------
/widget/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/widget/build.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | alias(libs.plugins.android.library)
3 | alias(libs.plugins.jetbrains.kotlin.android)
4 | alias(libs.plugins.compose.compiler)
5 | }
6 |
7 | android {
8 | namespace = "com.zs.widget"
9 | compileSdk = 35
10 |
11 | defaultConfig {
12 | minSdk = 21
13 |
14 | testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
15 | consumerProguardFiles("consumer-rules.pro")
16 | }
17 |
18 | buildTypes {
19 | release {
20 | isMinifyEnabled = false
21 | proguardFiles(
22 | getDefaultProguardFile("proguard-android-optimize.txt"),
23 | "proguard-rules.pro"
24 | )
25 | }
26 | }
27 | compileOptions {
28 | sourceCompatibility = JavaVersion.VERSION_11
29 | targetCompatibility = JavaVersion.VERSION_11
30 | }
31 | kotlinOptions { jvmTarget = "11" }
32 | buildFeatures { compose = true }
33 | }
34 |
35 | dependencies {
36 | implementation(libs.glance.appwidget)
37 | implementation(libs.glance.material3)
38 | implementation(project(":core"))
39 | }
--------------------------------------------------------------------------------
/widget/consumer-rules.pro:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/iZakirSheikh/Audiofy/7cc20318196ab355d99863b0192abd1a5af6ba14/widget/consumer-rules.pro
--------------------------------------------------------------------------------
/widget/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
--------------------------------------------------------------------------------
/widget/src/androidTest/java/com/zs/widget/ExampleInstrumentedTest.kt:
--------------------------------------------------------------------------------
1 | package com.zs.widget
2 |
3 | import androidx.test.platform.app.InstrumentationRegistry
4 | import androidx.test.ext.junit.runners.AndroidJUnit4
5 |
6 | import org.junit.Test
7 | import org.junit.runner.RunWith
8 |
9 | import org.junit.Assert.*
10 |
11 | /**
12 | * Instrumented test, which will execute on an Android device.
13 | *
14 | * See [testing documentation](http://d.android.com/tools/testing).
15 | */
16 | @RunWith(AndroidJUnit4::class)
17 | class ExampleInstrumentedTest {
18 | @Test
19 | fun useAppContext() {
20 | // Context of the app under test.
21 | val appContext = InstrumentationRegistry.getInstrumentation().targetContext
22 | assertEquals("com.zs.widget.test", appContext.packageName)
23 | }
24 | }
--------------------------------------------------------------------------------
/widget/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
9 |
10 |
11 |
12 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/widget/src/main/java/com/zs/widget/AppWidget.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2025 Zakir Sheikh
3 | *
4 | * Created by Zakir Sheikh on 15-01-2025.
5 | *
6 | * Licensed under the Apache License, Version 2.0 (the "License");
7 | * you may not use this file except in compliance with the License.
8 | * You may obtain a copy of the License at
9 | *
10 | * https://www.apache.org/licenses/LICENSE-2.0
11 | *
12 | * Unless required by applicable law or agreed to in writing, software
13 | * distributed under the License is distributed on an "AS IS" BASIS,
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | * See the License for the specific language governing permissions and
16 | * limitations under the License.
17 | */
18 |
19 | package com.zs.widget
20 |
21 | import android.content.Context
22 | import android.util.Log
23 | import androidx.compose.runtime.collectAsState
24 | import androidx.compose.runtime.getValue
25 | import androidx.compose.runtime.remember
26 | import androidx.compose.ui.unit.dp
27 | import androidx.glance.GlanceTheme
28 | import androidx.glance.LocalSize
29 | import androidx.glance.appwidget.GlanceAppWidget
30 | import androidx.glance.appwidget.GlanceAppWidgetReceiver
31 | import androidx.glance.appwidget.SizeMode
32 | import androidx.glance.appwidget.provideContent
33 | import com.zs.core.playback.NowPlaying
34 | import com.zs.core.playback.PlaybackController
35 | import androidx.glance.GlanceId as ID
36 |
37 | private const val TAG = "AppWidget"
38 |
39 | class AppWidget : GlanceAppWidgetReceiver() {
40 |
41 | private class RealWidget() : GlanceAppWidget() {
42 |
43 | override val sizeMode: SizeMode = SizeMode.Exact
44 |
45 | override suspend fun provideGlance(context: Context, id: ID) {
46 | provideContent {
47 | GlanceTheme {
48 | val type = LocalSize.current.let { (width, height) ->
49 | when {
50 | width > 300.dp -> ViewType.NORMAL
51 | height > 100.dp -> ViewType.SQUARE
52 | else -> ViewType.COMPACT
53 | }
54 | }
55 | // Observe the playback state
56 | val state by remember { PlaybackController.observe(context) }
57 | .collectAsState(NowPlaying.EMPTY)
58 | Log.d(TAG, "$type")
59 | Log.d(TAG, "State: $state")
60 | Universal(state, type)
61 | }
62 | }
63 | }
64 | }
65 |
66 | override val glanceAppWidget: GlanceAppWidget = RealWidget()
67 | }
68 |
--------------------------------------------------------------------------------
/widget/src/main/java/com/zs/widget/Util.kt:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2025 Zakir Sheikh
3 | *
4 | * Created by Zakir Sheikh on 20-01-2025.
5 | *
6 | * Licensed under the Apache License, Version 2.0 (the "License");
7 | * you may not use this file except in compliance with the License.
8 | * You may obtain a copy of the License at
9 | *
10 | * https://www.apache.org/licenses/LICENSE-2.0
11 | *
12 | * Unless required by applicable law or agreed to in writing, software
13 | * distributed under the License is distributed on an "AS IS" BASIS,
14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | * See the License for the specific language governing permissions and
16 | * limitations under the License.
17 | */
18 |
19 | package com.zs.widget
20 |
21 | import android.annotation.SuppressLint
22 | import android.content.Context
23 | import android.util.Log
24 | import androidx.compose.runtime.Composable
25 | import androidx.glance.LocalContext
26 | import androidx.glance.action.clickable
27 | import androidx.glance.GlanceModifier as Modifier
28 | import androidx.glance.unit.ColorProvider
29 | import java.nio.file.WatchEvent
30 |
31 | private const val TAG = "Util"
32 |
33 | /**
34 | * Creates a new `ColorProvider` with a modified alpha value.
35 | *
36 | * @param context The context needed to resolve the color.
37 | * @param alpha The new alpha value (0f-1f); -1f means use the original alpha.
38 | * @return A new `ColorProvider` or the original if alpha is -1f.
39 | */
40 | @SuppressLint("RestrictedApi")
41 | fun ColorProvider.copy(context: Context, alpha: Float = -1f) = if (alpha == -1f) this else
42 | ColorProvider(getColor(context).copy(alpha))
43 |
44 | @Composable
45 | internal fun Modifier.launchApp(): Modifier {
46 | val context = LocalContext.current
47 | return clickable {
48 | Log.d(TAG, "Universal: ${context.packageName}")
49 | context.startActivity(context.packageManager.getLaunchIntentForPackage(context.packageName))
50 | }
51 | }
--------------------------------------------------------------------------------
/widget/src/main/java/com/zs/widget/ViewType.kt:
--------------------------------------------------------------------------------
1 | package com.zs.widget
2 |
3 | /**
4 | * Represents the different sizes for a widget.
5 | *
6 | * This enum is used to categorize the size of a widget based on its width and height.
7 | * It provides three distinct categories:
8 | * - COMPACT: A small widget with width less than 300 and height less than 100.
9 | * - SQUARE: A widget with approximately equal width and height, or a size that doesn't fall into COMPACT or NORMAL.
10 | * - NORMAL: A larger widget with width greater than 300 and height greater than 100.
11 | */
12 | internal enum class ViewType {
13 | COMPACT,
14 | SQUARE,
15 | NORMAL
16 | }
--------------------------------------------------------------------------------
/widget/src/main/res/drawable/app_widget_preview.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/iZakirSheikh/Audiofy/7cc20318196ab355d99863b0192abd1a5af6ba14/widget/src/main/res/drawable/app_widget_preview.webp
--------------------------------------------------------------------------------
/widget/src/main/res/drawable/ic_config.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/widget/src/main/res/drawable/ic_pause_rounded_filled.xml:
--------------------------------------------------------------------------------
1 |
7 |
8 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/widget/src/main/res/drawable/ic_round_play_filled.xml:
--------------------------------------------------------------------------------
1 |
7 |
8 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/widget/src/main/res/drawable/ic_skip_to_next.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/widget/src/main/res/drawable/ic_skip_to_prev.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/widget/src/main/res/drawable/media3_notification_pause.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
12 |
13 |
--------------------------------------------------------------------------------
/widget/src/main/res/drawable/media3_notification_play.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/widget/src/main/res/drawable/rect_rounded_cornors_12dp.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
8 |
9 |
12 |
13 |
18 |
19 |
--------------------------------------------------------------------------------
/widget/src/main/res/layout/chronometer.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/widget/src/main/res/layout/chronometer_bold.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/widget/src/main/res/xml/app_widget_info.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/widget/src/test/java/com/zs/widget/ExampleUnitTest.kt:
--------------------------------------------------------------------------------
1 | package com.zs.widget
2 |
3 | import org.junit.Test
4 |
5 | import org.junit.Assert.*
6 |
7 | /**
8 | * Example local unit test, which will execute on the development machine (host).
9 | *
10 | * See [testing documentation](http://d.android.com/tools/testing).
11 | */
12 | class ExampleUnitTest {
13 | @Test
14 | fun addition_isCorrect() {
15 | assertEquals(4, 2 + 2)
16 | }
17 | }
--------------------------------------------------------------------------------