├── common ├── .gitignore ├── consumer-rules.pro ├── src │ ├── main │ │ ├── res │ │ │ ├── mipmap │ │ │ │ └── ic_launcher.png │ │ │ ├── drawable │ │ │ │ ├── gradiant_background.png │ │ │ │ ├── background_gradient_top_bottom.xml │ │ │ │ ├── ic_play_arrow_white_24dp.xml │ │ │ │ ├── ic_play_arrow_black_24dp.xml │ │ │ │ ├── ic_pause_white_24dp.xml │ │ │ │ ├── ic_skip_next_white_24dp.xml │ │ │ │ ├── ic_skip_previous_black_24dp.xml │ │ │ │ ├── ic_skip_previous_white_24dp.xml │ │ │ │ ├── ic_pause_black_24dp.xml │ │ │ │ ├── ic_skip_next_black_24dp.xml │ │ │ │ ├── background_drawable_round_corner.xml │ │ │ │ ├── ic_keyboard_arrow_left_white.xml │ │ │ │ ├── ic_keyboard_arrow_left_black_24dp.xml │ │ │ │ ├── ic_clock.xml │ │ │ │ ├── play_circle_green.xml │ │ │ │ ├── ic_folder_black_24dp.xml │ │ │ │ ├── pause_circle.xml │ │ │ │ ├── pause_circle_green.xml │ │ │ │ ├── ic_info_outline_black_24dp.xml │ │ │ │ ├── arrow_collapse_down.xml │ │ │ │ ├── ic_book_play.xml │ │ │ │ ├── play_circle_outline.xml │ │ │ │ ├── share_outline.xml │ │ │ │ ├── ic_clock_outline.xml │ │ │ │ ├── book_search_outline.xml │ │ │ │ ├── loading_animation.xml │ │ │ │ └── ic_broken_image.xml │ │ │ ├── values │ │ │ │ ├── dimens.xml │ │ │ │ ├── strings.xml │ │ │ │ ├── colors.xml │ │ │ │ └── styles.xml │ │ │ ├── anim │ │ │ │ ├── stationary.xml │ │ │ │ ├── slide_down.xml │ │ │ │ └── slide_up.xml │ │ │ └── layout │ │ │ │ └── layout_no_connection.xml │ │ ├── java │ │ │ └── com │ │ │ │ └── allsoftdroid │ │ │ │ └── common │ │ │ │ ├── base │ │ │ │ ├── viewModel │ │ │ │ │ ├── BaseAction.kt │ │ │ │ │ └── BaseViewState.kt │ │ │ │ ├── extension │ │ │ │ │ ├── AudioPlayListItem.kt │ │ │ │ │ ├── MutableLiveDataExtension.kt │ │ │ │ │ ├── AudioPlayerEventState.kt │ │ │ │ │ ├── ViewExtension.kt │ │ │ │ │ ├── Variable.kt │ │ │ │ │ ├── CreateImageOverlay.kt │ │ │ │ │ ├── Event.kt │ │ │ │ │ └── ContextExtension.kt │ │ │ │ ├── utils │ │ │ │ │ ├── PlayerStatusListener.kt │ │ │ │ │ ├── SettingsPreferenceUtils.kt │ │ │ │ │ ├── ShareUtils.kt │ │ │ │ │ ├── SingletonHolder.kt │ │ │ │ │ └── ImageUtils.kt │ │ │ │ ├── network │ │ │ │ │ ├── NetworkResult.kt │ │ │ │ │ └── StoreUtils.kt │ │ │ │ ├── store │ │ │ │ │ ├── downloader │ │ │ │ │ │ ├── DownloaderEventBus.kt │ │ │ │ │ │ └── DownloadEventStore.kt │ │ │ │ │ ├── userAction │ │ │ │ │ │ ├── UserActionEventBus.kt │ │ │ │ │ │ ├── UserActionEventStore.kt │ │ │ │ │ │ └── UserActionEvent.kt │ │ │ │ │ ├── audioPlayer │ │ │ │ │ │ ├── AudioPlayerEventStore.kt │ │ │ │ │ │ ├── AudioPlayerEventBus.kt │ │ │ │ │ │ └── AudioPlayerEvent.kt │ │ │ │ │ └── Store.kt │ │ │ │ ├── usecase │ │ │ │ │ ├── BaseUseCaseScheduler.kt │ │ │ │ │ ├── UiCallbackWrapper.kt │ │ │ │ │ └── BaseUseCase.kt │ │ │ │ ├── fragment │ │ │ │ │ ├── BaseContainerFragment.kt │ │ │ │ │ └── BaseUIFragment.kt │ │ │ │ └── activity │ │ │ │ │ └── BaseActivity.kt │ │ │ │ └── test │ │ │ │ ├── EspressoIdlingResource.kt │ │ │ │ ├── MainCoroutineRule.kt │ │ │ │ └── LiveDataTestUtils.kt │ │ └── AndroidManifest.xml │ └── test │ │ └── java │ │ └── com │ │ └── allsoftdroid │ │ └── common │ │ └── ExampleUnitTest.kt └── proguard-rules.pro ├── database ├── consumer-rules.pro ├── .gitignore ├── src │ ├── main │ │ ├── res │ │ │ └── values │ │ │ │ └── strings.xml │ │ ├── AndroidManifest.xml │ │ └── java │ │ │ └── com │ │ │ └── allsoftdroid │ │ │ └── database │ │ │ ├── common │ │ │ └── SaveInDatabase.kt │ │ │ ├── networkCacheDB │ │ │ ├── DatabaseNetworkResponseEntity.kt │ │ │ └── NetworkCacheDao.kt │ │ │ ├── listenLaterDB │ │ │ ├── entity │ │ │ │ └── DatabaseListenLaterEntity.kt │ │ │ └── ListenLaterDao.kt │ │ │ ├── bookListDB │ │ │ └── DatabaseEntities.kt │ │ │ └── metadataCacheDB │ │ │ └── entity │ │ │ ├── DatabaseAlbumEntity.kt │ │ │ ├── DatabaseMetadataEntity.kt │ │ │ └── DatabaseTrackEntity.kt │ └── test │ │ └── java │ │ └── com │ │ └── allsoftdroid │ │ └── database │ │ └── ExampleUnitTest.kt └── proguard-rules.pro ├── services ├── consumer-rules.pro ├── .gitignore ├── src │ ├── test │ │ └── resources │ │ │ └── mockito-extensions │ │ │ └── org.mockito.plugins.MockMaker │ └── main │ │ ├── res │ │ └── values │ │ │ ├── dimens.xml │ │ │ └── strings.xml │ │ ├── java │ │ └── com │ │ │ └── allsoftdroid │ │ │ └── audiobook │ │ │ └── services │ │ │ └── audio │ │ │ └── utils │ │ │ ├── PlayerState.kt │ │ │ └── TextFormatter.kt │ │ └── AndroidManifest.xml └── proguard-rules.pro ├── feature_book ├── .gitignore └── src │ ├── main │ ├── res │ │ ├── drawable │ │ │ ├── notfound.png │ │ │ ├── animal_face.jpg │ │ │ ├── ic_toolbar_menu_hamburger.xml │ │ │ ├── ic_arrow_chevron_right.xml │ │ │ ├── ic_star.xml │ │ │ ├── close.xml │ │ │ ├── ic_refresh.xml │ │ │ ├── ic_history.xml │ │ │ ├── ic_bug.xml │ │ │ ├── ic_gauge.xml │ │ │ └── ic_jellyfish.xml │ │ ├── values │ │ │ ├── colors.xml │ │ │ ├── styles.xml │ │ │ └── dimens.xml │ │ └── layout │ │ │ ├── layout_no_results_found.xml │ │ │ └── nav_header_main.xml │ ├── java │ │ └── com │ │ │ └── allsoftdroid │ │ │ └── feature_book │ │ │ ├── utils │ │ │ └── NetworkState.kt │ │ │ ├── data │ │ │ ├── model │ │ │ │ ├── AudioBookListDataModel.kt │ │ │ │ ├── AudioBookSearchResultDataModel.kt │ │ │ │ └── AudioBookDataModel.kt │ │ │ ├── network │ │ │ │ ├── response │ │ │ │ │ └── GetAudioBooksResponse.kt │ │ │ │ └── Utils.kt │ │ │ └── databaseExtension │ │ │ │ └── DatabaseEntities.kt │ │ │ ├── domain │ │ │ ├── model │ │ │ │ └── AudioBookDomainModel.kt │ │ │ └── repository │ │ │ │ ├── NetworkResponseListener.kt │ │ │ │ └── AudioBookRepository.kt │ │ │ └── presentation │ │ │ └── recyclerView │ │ │ └── adapter │ │ │ └── PaginationListener.kt │ └── AndroidManifest.xml │ └── test │ └── java │ └── com │ └── allsoftdroid │ └── feature_book │ └── common │ └── MockitoUtils.kt ├── feature_mini_player ├── consumer-rules.pro ├── .gitignore ├── src │ └── main │ │ ├── AndroidManifest.xml │ │ ├── res │ │ └── values │ │ │ ├── strings.xml │ │ │ └── dimens.xml │ │ └── java │ │ └── com │ │ └── allsoftdroid │ │ └── audiobook │ │ └── feature_mini_player │ │ └── di │ │ └── FeatureMiniPlayerModule.kt └── proguard-rules.pro ├── feature_mybooks ├── .gitignore ├── consumer-rules.pro ├── src │ └── main │ │ ├── AndroidManifest.xml │ │ ├── java │ │ └── com │ │ │ └── allsoftdroid │ │ │ └── audiobook │ │ │ └── feature_mybooks │ │ │ ├── data │ │ │ └── model │ │ │ │ ├── LocalBookFiles.kt │ │ │ │ ├── BookMetadata.kt │ │ │ │ └── LocalBookDomainModel.kt │ │ │ ├── domain │ │ │ ├── IBookMetadataRepository.kt │ │ │ └── ILocalBooksRepository.kt │ │ │ └── utils │ │ │ └── RequestStatus.kt │ │ └── res │ │ ├── drawable │ │ └── background_round_corner_dark_border.xml │ │ ├── menu │ │ └── local_books_option_menu.xml │ │ ├── values │ │ ├── dimens.xml │ │ └── strings.xml │ │ └── layout │ │ └── layout_no_books_found_local_storage.xml └── proguard-rules.pro ├── feature_settings ├── .gitignore ├── consumer-rules.pro ├── src │ └── main │ │ ├── AndroidManifest.xml │ │ ├── java │ │ └── com │ │ │ └── allsoftdroid │ │ │ └── audiobook │ │ │ └── feature_settings │ │ │ └── model │ │ │ └── Feedback.kt │ │ └── res │ │ └── values │ │ └── dimens.xml └── proguard-rules.pro ├── app ├── .gitignore ├── src │ ├── main │ │ ├── res │ │ │ ├── drawable │ │ │ │ ├── launcher.png │ │ │ │ └── splash_drawable.xml │ │ │ ├── mipmap-hdpi │ │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-mdpi │ │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-xhdpi │ │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-xxhdpi │ │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-xxxhdpi │ │ │ │ └── ic_launcher.png │ │ │ ├── values │ │ │ │ ├── dimens.xml │ │ │ │ ├── colors.xml │ │ │ │ ├── styles.xml │ │ │ │ └── strings.xml │ │ │ ├── xml │ │ │ │ └── provider_paths.xml │ │ │ └── layout │ │ │ │ └── activity_main.xml │ │ └── java │ │ │ └── com │ │ │ └── allsoftdroid │ │ │ └── audiobook │ │ │ ├── domain │ │ │ └── model │ │ │ │ └── LastPlayedTrack.kt │ │ │ ├── ApplicationController.kt │ │ │ └── utility │ │ │ ├── MovableFrameLayout.kt │ │ │ └── MoveUpBehavior.kt │ └── androidTest │ │ └── java │ │ └── com │ │ └── allsoftdroid │ │ └── audiobook │ │ └── presentation │ │ └── utils │ │ ├── ScreenCaptureProcessor.kt │ │ └── TakeScreenshotUtils.kt ├── proguard-rules.pro └── google-services.json ├── feature_book_details ├── .gitignore └── src │ ├── main │ ├── java │ │ └── com │ │ │ └── allsoftdroid │ │ │ └── feature │ │ │ └── book_details │ │ │ ├── data │ │ │ ├── model │ │ │ │ ├── TrackFormat.kt │ │ │ │ ├── AudioBookMetadataDataModel.kt │ │ │ │ └── ListenLaterDomainModel.kt │ │ │ └── network │ │ │ │ ├── Utils.kt │ │ │ │ ├── response │ │ │ │ └── GetAudioBookMetadataResponse.kt │ │ │ │ └── service │ │ │ │ └── ArchiveAudioBookMetadataServiceApi.kt │ │ │ ├── utils │ │ │ ├── NetworkState.kt │ │ │ ├── DownloadStatusEvent.kt │ │ │ └── ViewLoadingAnimation.kt │ │ │ ├── presentation │ │ │ └── viewModel │ │ │ │ └── StateKey.kt │ │ │ └── domain │ │ │ ├── repository │ │ │ ├── IBaseRepository.kt │ │ │ ├── IListenLaterRepository.kt │ │ │ ├── ITrackListRepository.kt │ │ │ ├── IMetadataRepository.kt │ │ │ └── BookDetailsSharedPreferenceRepository.kt │ │ │ ├── model │ │ │ ├── AudioBookMetadataDomainModel.kt │ │ │ └── AudioBookTrackDomainModel.kt │ │ │ └── usecase │ │ │ ├── ListenLaterUsecase.kt │ │ │ └── GetDownloadUsecase.kt │ ├── res │ │ ├── drawable │ │ │ ├── circle_shape.xml │ │ │ ├── ic_details.xml │ │ │ ├── backdrop_fragment_background.xml │ │ │ ├── ic_bookmark_minus.xml │ │ │ ├── play_circle.xml │ │ │ ├── pause_circle.xml │ │ │ ├── download_check.xml │ │ │ ├── ic_headphones.xml │ │ │ ├── ic_bookmark_plus_outline.xml │ │ │ ├── semi_circle_shape_left.xml │ │ │ ├── semi_circle_shape_right.xml │ │ │ ├── download_outline.xml │ │ │ ├── ic_book_open_outline.xml │ │ │ ├── clock_outline.xml │ │ │ ├── play_circle_outline.xml │ │ │ ├── ic_account_outline.xml │ │ │ ├── close_circle_outline.xml │ │ │ ├── ic_format_list_bulleted.xml │ │ │ ├── circular_progressbar.xml │ │ │ ├── ic_tag_multiple_outline.xml │ │ │ └── ic_information_variant.xml │ │ ├── values │ │ │ └── colors.xml │ │ └── layout │ │ │ └── layout_server_error.xml │ └── AndroidManifest.xml │ ├── androidTest │ └── java │ │ └── com │ │ └── allsoftdroid │ │ └── feature │ │ └── book_details │ │ └── presentation │ │ └── utils │ │ ├── FakeListenLaterRepository.kt │ │ ├── FakeNetworkCacheDao.kt │ │ ├── FakeSaveInDatabase.kt │ │ └── FakeRemoteMetadataSource.kt │ └── test │ └── java │ └── com │ └── allsoftdroid │ └── feature │ └── book_details │ └── utils │ ├── FakeListenLaterRepository.kt │ ├── FakeNetworkCacheDao.kt │ ├── FakeSaveInDatabase.kt │ ├── FakeTrackListRepository.kt │ └── FakeRemoteMetadataSource.kt ├── feature_downloader ├── .gitignore └── src │ └── main │ ├── res │ ├── values │ │ ├── colors.xml │ │ ├── dimens.xml │ │ └── strings.xml │ ├── drawable │ │ ├── ic_download_multiple.xml │ │ ├── ic_download_multiple_black.xml │ │ ├── ic_delete.xml │ │ ├── ic_delete_sweep.xml │ │ ├── ic_file_music.xml │ │ ├── ic_close_circle.xml │ │ ├── ic_emoticon_excited.xml │ │ └── ic_restart.xml │ ├── menu │ │ └── menu_downloads.xml │ └── layout │ │ ├── layout_checkbox.xml │ │ └── layout_empty_content.xml │ ├── java │ └── com │ │ └── allsoftdroid │ │ └── audiobook │ │ └── feature_downloader │ │ ├── data │ │ ├── config │ │ │ └── ProviderConfig.java │ │ └── model │ │ │ └── LocalFileDetails.java │ │ ├── domain │ │ ├── IDownloaderRefresh.java │ │ ├── IDownloaderCore.java │ │ └── IDownloader.java │ │ └── utils │ │ └── DownloadStatus.java │ └── AndroidManifest.xml ├── feature_listen_later_ui ├── consumer-rules.pro ├── .gitignore ├── src │ └── main │ │ ├── AndroidManifest.xml │ │ ├── java │ │ └── com │ │ │ └── allsoftdroid │ │ │ └── audiobook │ │ │ └── feature_listen_later_ui │ │ │ ├── utils │ │ │ ├── SortType.kt │ │ │ └── StringUtility.kt │ │ │ ├── domain │ │ │ ├── RequestStatus.kt │ │ │ ├── contracts │ │ │ │ ├── ImportFileContract.kt │ │ │ │ └── ExportFileContract.kt │ │ │ └── repository │ │ │ │ ├── IExportUserDataRepository.kt │ │ │ │ ├── IImportUserDataRepository.kt │ │ │ │ └── IListenLaterRepository.kt │ │ │ └── data │ │ │ └── model │ │ │ ├── ListenLaterItemDomainModel.kt │ │ │ └── BookMarkDataItem.kt │ │ └── res │ │ ├── menu │ │ ├── backup_restore_menu.xml │ │ ├── listen_later_options_menu.xml │ │ └── sort_options_menu.xml │ │ ├── values │ │ └── dimens.xml │ │ ├── drawable │ │ ├── ic_sort_black_24dp.xml │ │ └── backup_restore.xml │ │ └── layout │ │ └── layout_no_books_found.xml └── proguard-rules.pro ├── feature_playerfullscreen ├── consumer-rules.pro ├── .gitignore ├── src │ ├── test │ │ └── resources │ │ │ └── mockito-extensions │ │ │ └── org.mockito.plugins.MockMaker │ └── main │ │ ├── AndroidManifest.xml │ │ ├── java │ │ └── com │ │ │ └── allsoftdroid │ │ │ └── audiobook │ │ │ └── feature │ │ │ └── feature_playerfullscreen │ │ │ └── data │ │ │ ├── PlayerControlState.kt │ │ │ └── PlayingTrackDetails.kt │ │ └── res │ │ ├── drawable │ │ ├── skip_next_outline.xml │ │ ├── skip_previous_outline.xml │ │ ├── play_circle.xml │ │ ├── ic_rewind_30.xml │ │ └── ic_fast_forward_30.xml │ │ └── values │ │ ├── dimens.xml │ │ └── strings.xml └── proguard-rules.pro ├── feature_audiobook_enhance_details ├── consumer-rules.pro ├── .gitignore ├── src │ ├── test │ │ ├── resources │ │ │ └── noResult.html │ │ └── java │ │ │ └── com │ │ │ └── allsoftdroid │ │ │ └── audiobook │ │ │ └── feature │ │ │ └── feature_audiobook_enhance_details │ │ │ └── domain │ │ │ └── usecase │ │ │ └── FakeBookDetailsRepository.kt │ └── main │ │ ├── res │ │ └── values │ │ │ └── strings.xml │ │ ├── AndroidManifest.xml │ │ └── java │ │ └── com │ │ └── allsoftdroid │ │ └── audiobook │ │ └── feature │ │ └── feature_audiobook_enhance_details │ │ ├── data │ │ ├── model │ │ │ ├── Chapter.kt │ │ │ ├── WebDocument.kt │ │ │ └── BookDetails.kt │ │ └── network │ │ │ ├── request │ │ │ ├── ILibriVoxDetailsApiService.kt │ │ │ └── LibrivoxDetailsApiService.kt │ │ │ ├── response │ │ │ └── NetworkResponse.kt │ │ │ └── Utils.kt │ │ └── domain │ │ ├── repository │ │ ├── IFetchAdditionBookDetailsRepository.kt │ │ ├── IStoreRepository.kt │ │ ├── INetworkBaseRepository.kt │ │ └── ISearchBookDetailsRepository.kt │ │ └── network │ │ └── NetworkResponseListener.kt └── proguard-rules.pro ├── photos ├── mybooks.png ├── bookdetails.png ├── mainscreen.png ├── listen_later.png └── dependencies_graph.png ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── .idea ├── codeStyles │ └── codeStyleConfig.xml ├── vcs.xml ├── kotlinc.xml ├── dictionaries │ └── Pravin_Tripathi.xml ├── runConfigurations.xml └── misc.xml ├── lint.xml ├── .github ├── workflows │ ├── PR-Labeler-Bot.yml │ ├── issue-pr-bot-reply.yml │ ├── release-notes.yml │ ├── New-Issue-Comments.yml │ ├── Create-Release-CI.yml │ ├── Build-InstrumentationTest.yml │ ├── lock-thread-ci.yml │ ├── Closes-Issue-Checker-Bot.yml │ └── Build-UnitTest.yml └── labeler.yml ├── .gitignore ├── settings.gradle └── LICENSE /common/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /common/consumer-rules.pro: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /database/consumer-rules.pro: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /services/consumer-rules.pro: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /database/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /services/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /feature_book/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /feature_mini_player/consumer-rules.pro: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /feature_mybooks/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /feature_mybooks/consumer-rules.pro: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /feature_settings/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /feature_settings/consumer-rules.pro: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | /release/ 3 | -------------------------------------------------------------------------------- /feature_book_details/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /feature_downloader/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /feature_listen_later_ui/consumer-rules.pro: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /feature_mini_player/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /feature_playerfullscreen/consumer-rules.pro: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /feature_listen_later_ui/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /feature_playerfullscreen/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /feature_audiobook_enhance_details/consumer-rules.pro: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /feature_audiobook_enhance_details/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /feature_audiobook_enhance_details/src/test/resources/noResult.html: -------------------------------------------------------------------------------- 1 | No results found -------------------------------------------------------------------------------- /photos/mybooks.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pravinyo/AudioBook/HEAD/photos/mybooks.png -------------------------------------------------------------------------------- /photos/bookdetails.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pravinyo/AudioBook/HEAD/photos/bookdetails.png -------------------------------------------------------------------------------- /photos/mainscreen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pravinyo/AudioBook/HEAD/photos/mainscreen.png -------------------------------------------------------------------------------- /services/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker: -------------------------------------------------------------------------------- 1 | mock-maker-inline -------------------------------------------------------------------------------- /photos/listen_later.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pravinyo/AudioBook/HEAD/photos/listen_later.png -------------------------------------------------------------------------------- /feature_playerfullscreen/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker: -------------------------------------------------------------------------------- 1 | mock-maker-inline -------------------------------------------------------------------------------- /photos/dependencies_graph.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pravinyo/AudioBook/HEAD/photos/dependencies_graph.png -------------------------------------------------------------------------------- /database/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | database 3 | 4 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pravinyo/AudioBook/HEAD/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /app/src/main/res/drawable/launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pravinyo/AudioBook/HEAD/app/src/main/res/drawable/launcher.png -------------------------------------------------------------------------------- /common/src/main/res/mipmap/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pravinyo/AudioBook/HEAD/common/src/main/res/mipmap/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pravinyo/AudioBook/HEAD/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pravinyo/AudioBook/HEAD/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pravinyo/AudioBook/HEAD/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pravinyo/AudioBook/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pravinyo/AudioBook/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /feature_book/src/main/res/drawable/notfound.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pravinyo/AudioBook/HEAD/feature_book/src/main/res/drawable/notfound.png -------------------------------------------------------------------------------- /common/src/main/java/com/allsoftdroid/common/base/viewModel/BaseAction.kt: -------------------------------------------------------------------------------- 1 | package com.allsoftdroid.common.base.viewModel 2 | 3 | interface BaseAction -------------------------------------------------------------------------------- /common/src/main/java/com/allsoftdroid/common/base/viewModel/BaseViewState.kt: -------------------------------------------------------------------------------- 1 | package com.allsoftdroid.common.base.viewModel 2 | 3 | interface BaseViewState -------------------------------------------------------------------------------- /common/src/main/res/drawable/gradiant_background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pravinyo/AudioBook/HEAD/common/src/main/res/drawable/gradiant_background.png -------------------------------------------------------------------------------- /database/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | -------------------------------------------------------------------------------- /feature_book/src/main/res/drawable/animal_face.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pravinyo/AudioBook/HEAD/feature_book/src/main/res/drawable/animal_face.jpg -------------------------------------------------------------------------------- /app/src/main/res/values/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 80dp 4 | -------------------------------------------------------------------------------- /common/src/main/res/values/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4dp 4 | -------------------------------------------------------------------------------- /feature_audiobook_enhance_details/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | Audiobook Enhance Details 3 | 4 | -------------------------------------------------------------------------------- /feature_downloader/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #ffffff 4 | -------------------------------------------------------------------------------- /feature_mybooks/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | -------------------------------------------------------------------------------- /feature_settings/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | -------------------------------------------------------------------------------- /common/src/main/res/anim/stationary.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /feature_mini_player/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | -------------------------------------------------------------------------------- /.idea/codeStyles/codeStyleConfig.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | -------------------------------------------------------------------------------- /feature_listen_later_ui/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | -------------------------------------------------------------------------------- /lint.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /feature_playerfullscreen/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /feature_book/src/main/java/com/allsoftdroid/feature_book/utils/NetworkState.kt: -------------------------------------------------------------------------------- 1 | package com.allsoftdroid.feature_book.utils 2 | 3 | enum class NetworkState { 4 | ERROR, 5 | LOADING, 6 | COMPLETED 7 | } -------------------------------------------------------------------------------- /.idea/kotlinc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/xml/provider_paths.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /feature_audiobook_enhance_details/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | -------------------------------------------------------------------------------- /common/src/main/java/com/allsoftdroid/common/base/extension/AudioPlayListItem.kt: -------------------------------------------------------------------------------- 1 | package com.allsoftdroid.common.base.extension 2 | 3 | interface AudioPlayListItem { 4 | val title : String? 5 | val filename : String 6 | } -------------------------------------------------------------------------------- /feature_settings/src/main/java/com/allsoftdroid/audiobook/feature_settings/model/Feedback.kt: -------------------------------------------------------------------------------- 1 | package com.allsoftdroid.audiobook.feature_settings.model 2 | 3 | data class Feedback( 4 | val title: String, 5 | val body: String) -------------------------------------------------------------------------------- /.idea/dictionaries/Pravin_Tripathi.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | coroutine 5 | usecase 6 | 7 | 8 | -------------------------------------------------------------------------------- /common/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /feature_book/src/main/java/com/allsoftdroid/feature_book/data/model/AudioBookListDataModel.kt: -------------------------------------------------------------------------------- 1 | package com.allsoftdroid.feature_book.data.model 2 | 3 | internal data class AudioBookListDataModel( 4 | val audioBooks : List 5 | ) 6 | -------------------------------------------------------------------------------- /feature_book/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #000000 4 | #ffffff 5 | #1a1a1a 6 | -------------------------------------------------------------------------------- /feature_book/src/main/java/com/allsoftdroid/feature_book/data/model/AudioBookSearchResultDataModel.kt: -------------------------------------------------------------------------------- 1 | package com.allsoftdroid.feature_book.data.model 2 | 3 | 4 | internal data class AudioBookSearchResultDataModel( 5 | val docs: List 6 | ) 7 | -------------------------------------------------------------------------------- /feature_mybooks/src/main/java/com/allsoftdroid/audiobook/feature_mybooks/data/model/LocalBookFiles.kt: -------------------------------------------------------------------------------- 1 | package com.allsoftdroid.audiobook.feature_mybooks.data.model 2 | 3 | data class LocalBookFiles( 4 | val identifier:String, 5 | val filePath:List 6 | ) -------------------------------------------------------------------------------- /database/src/main/java/com/allsoftdroid/database/common/SaveInDatabase.kt: -------------------------------------------------------------------------------- 1 | package com.allsoftdroid.database.common 2 | 3 | interface SaveInDatabase { 4 | 5 | var mDao : T 6 | 7 | fun addData(data:Any): R 8 | 9 | suspend fun execute(): Any 10 | } -------------------------------------------------------------------------------- /feature_mybooks/src/main/java/com/allsoftdroid/audiobook/feature_mybooks/data/model/BookMetadata.kt: -------------------------------------------------------------------------------- 1 | package com.allsoftdroid.audiobook.feature_mybooks.data.model 2 | 3 | data class BookMetadata( 4 | val title:String, 5 | val author:String, 6 | val totalTracks:Int 7 | ) -------------------------------------------------------------------------------- /.github/workflows/PR-Labeler-Bot.yml: -------------------------------------------------------------------------------- 1 | name: PR Labeler Bot 2 | 3 | on: 4 | - pull_request 5 | 6 | jobs: 7 | triage: 8 | runs-on: ubuntu-latest 9 | steps: 10 | - uses: actions/labeler@v2 11 | with: 12 | repo-token: "${{ secrets.GITHUB_TOKEN }}" -------------------------------------------------------------------------------- /common/src/main/java/com/allsoftdroid/common/base/utils/PlayerStatusListener.kt: -------------------------------------------------------------------------------- 1 | package com.allsoftdroid.common.base.utils 2 | 3 | import com.allsoftdroid.common.base.extension.Event 4 | 5 | interface PlayerStatusListener { 6 | fun onPlayerStatusChange(shouldShow : Event) 7 | } -------------------------------------------------------------------------------- /common/src/main/res/anim/slide_down.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 8 | -------------------------------------------------------------------------------- /common/src/main/res/anim/slide_up.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 8 | -------------------------------------------------------------------------------- /feature_mini_player/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | feature_mini_player 3 | audio book album art 4 | loading track to play 5 | 6 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Sat Sep 14 22:40:02 IST 2019 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.2-all.zip 7 | -------------------------------------------------------------------------------- /common/src/main/java/com/allsoftdroid/common/base/extension/MutableLiveDataExtension.kt: -------------------------------------------------------------------------------- 1 | package com.allsoftdroid.common.base.extension 2 | 3 | import androidx.lifecycle.LiveData 4 | import androidx.lifecycle.MutableLiveData 5 | 6 | 7 | 8 | fun MutableLiveData.toLiveData() = this as LiveData -------------------------------------------------------------------------------- /feature_downloader/src/main/java/com/allsoftdroid/audiobook/feature_downloader/data/config/ProviderConfig.java: -------------------------------------------------------------------------------- 1 | package com.allsoftdroid.audiobook.feature_downloader.data.config; 2 | 3 | public interface ProviderConfig { 4 | String PROVIDER_AUTHORITY = "com.allsoftdroid.audiobook.provider"; 5 | } 6 | -------------------------------------------------------------------------------- /services/src/main/res/values/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 32dp 4 | 4dp 5 | 8dp 6 | 4dp 7 | -------------------------------------------------------------------------------- /common/src/main/java/com/allsoftdroid/common/base/extension/AudioPlayerEventState.kt: -------------------------------------------------------------------------------- 1 | package com.allsoftdroid.common.base.extension 2 | 3 | 4 | sealed class AudioPlayerEventState 5 | 6 | data class PlayingState( 7 | val playingItemIndex:Int, 8 | val action_need : Boolean 9 | ) : AudioPlayerEventState() -------------------------------------------------------------------------------- /app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #000 4 | #000 5 | #35D807 6 | #3b7d43 7 | 8 | -------------------------------------------------------------------------------- /feature_downloader/src/main/java/com/allsoftdroid/audiobook/feature_downloader/domain/IDownloaderRefresh.java: -------------------------------------------------------------------------------- 1 | package com.allsoftdroid.audiobook.feature_downloader.domain; 2 | 3 | public interface IDownloaderRefresh { 4 | void ReloadAdapter(); 5 | void notifyItemChangedAtPosition(int position); 6 | } 7 | -------------------------------------------------------------------------------- /feature_listen_later_ui/src/main/java/com/allsoftdroid/audiobook/feature_listen_later_ui/utils/SortType.kt: -------------------------------------------------------------------------------- 1 | package com.allsoftdroid.audiobook.feature_listen_later_ui.utils 2 | 3 | enum class SortType(val value:String) { 4 | LatestFirst("latest"), 5 | OldestFirst("Oldest"), 6 | ShortestFirst("Shortest") 7 | } -------------------------------------------------------------------------------- /common/src/main/java/com/allsoftdroid/common/base/network/NetworkResult.kt: -------------------------------------------------------------------------------- 1 | package com.allsoftdroid.common.base.network 2 | 3 | sealed class NetworkResult 4 | 5 | object Loading : NetworkResult() 6 | data class Success(val result: Any?) : NetworkResult() 7 | data class Failure(val error: Error) : NetworkResult() 8 | 9 | -------------------------------------------------------------------------------- /feature_book_details/src/main/java/com/allsoftdroid/feature/book_details/data/model/TrackFormat.kt: -------------------------------------------------------------------------------- 1 | package com.allsoftdroid.feature.book_details.data.model 2 | 3 | sealed class TrackFormat{ 4 | object FormatBP64 : TrackFormat() 5 | object FormatBP128 : TrackFormat() 6 | object FormatVBR : TrackFormat() 7 | } 8 | -------------------------------------------------------------------------------- /feature_book/src/main/java/com/allsoftdroid/feature_book/domain/model/AudioBookDomainModel.kt: -------------------------------------------------------------------------------- 1 | package com.allsoftdroid.feature_book.domain.model 2 | 3 | data class AudioBookDomainModel( 4 | val mId: String, 5 | val title: String, 6 | val creator: String?, 7 | val date: String?, 8 | val addeddate:String? 9 | ) -------------------------------------------------------------------------------- /feature_book/src/main/java/com/allsoftdroid/feature_book/domain/repository/NetworkResponseListener.kt: -------------------------------------------------------------------------------- 1 | package com.allsoftdroid.feature_book.domain.repository 2 | 3 | import com.allsoftdroid.common.base.network.NetworkResult 4 | 5 | interface NetworkResponseListener{ 6 | suspend fun onResponse(result : NetworkResult) 7 | } -------------------------------------------------------------------------------- /common/src/main/java/com/allsoftdroid/common/base/network/StoreUtils.kt: -------------------------------------------------------------------------------- 1 | package com.allsoftdroid.common.base.network 2 | 3 | import android.content.Context 4 | 5 | object StoreUtils { 6 | fun getStoreUrl(context:Context) : String{ 7 | return "https://play.google.com/store/apps/details?id=${context.packageName}" 8 | } 9 | } -------------------------------------------------------------------------------- /feature_book_details/src/main/java/com/allsoftdroid/feature/book_details/utils/NetworkState.kt: -------------------------------------------------------------------------------- 1 | package com.allsoftdroid.feature.book_details.utils 2 | 3 | enum class NetworkState(val value:String) { 4 | CONNECTION_ERROR("Network Error"), 5 | SERVER_ERROR("Server Error"), 6 | LOADING("Loading"), 7 | COMPLETED("Done") 8 | } -------------------------------------------------------------------------------- /app/src/main/java/com/allsoftdroid/audiobook/domain/model/LastPlayedTrack.kt: -------------------------------------------------------------------------------- 1 | package com.allsoftdroid.audiobook.domain.model 2 | 3 | /** 4 | * Data Structure for last Played track 5 | */ 6 | data class LastPlayedTrack( 7 | val title : String, 8 | val position:Int, 9 | val bookId:String, 10 | val bookName:String 11 | ) -------------------------------------------------------------------------------- /common/src/main/java/com/allsoftdroid/common/base/extension/ViewExtension.kt: -------------------------------------------------------------------------------- 1 | package com.allsoftdroid.common.base.extension 2 | 3 | import android.view.View 4 | import com.google.android.material.snackbar.Snackbar 5 | 6 | fun View.showSnackbar(snackbarText: String, timeLength: Int) { 7 | Snackbar.make(this, snackbarText, timeLength).show() 8 | } -------------------------------------------------------------------------------- /common/src/main/res/drawable/background_gradient_top_bottom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | -------------------------------------------------------------------------------- /feature_book_details/src/main/java/com/allsoftdroid/feature/book_details/presentation/viewModel/StateKey.kt: -------------------------------------------------------------------------------- 1 | package com.allsoftdroid.feature.book_details.presentation.viewModel 2 | 3 | enum class StateKey(var key:String) { 4 | CurrentPlayingTrack("CurrentPlayingTrack"), 5 | CurrentTrackFormat("CurrentTrackFormat"), 6 | TrackId("TrackId") 7 | } -------------------------------------------------------------------------------- /services/src/main/java/com/allsoftdroid/audiobook/services/audio/utils/PlayerState.kt: -------------------------------------------------------------------------------- 1 | package com.allsoftdroid.audiobook.services.audio.utils 2 | 3 | enum class PlayerState{ 4 | SourceError, 5 | SystemError, 6 | 7 | PlayerFinished, 8 | PlayerReady, 9 | PlayerBusy, 10 | PlayerIdle, 11 | PlayingNext 12 | } 13 | 14 | -------------------------------------------------------------------------------- /feature_audiobook_enhance_details/src/main/java/com/allsoftdroid/audiobook/feature/feature_audiobook_enhance_details/data/model/Chapter.kt: -------------------------------------------------------------------------------- 1 | package com.allsoftdroid.audiobook.feature.feature_audiobook_enhance_details.data.model 2 | 3 | data class Chapter( 4 | val number:Int, 5 | val name:String, 6 | val author:String, 7 | val reader:String 8 | ) -------------------------------------------------------------------------------- /feature_playerfullscreen/src/main/java/com/allsoftdroid/audiobook/feature/feature_playerfullscreen/data/PlayerControlState.kt: -------------------------------------------------------------------------------- 1 | package com.allsoftdroid.audiobook.feature.feature_playerfullscreen.data 2 | 3 | data class PlayerControlState( 4 | var playNext:Boolean = false, 5 | var playPrevious:Boolean= false, 6 | var shouldItPlay:Boolean= false 7 | ) -------------------------------------------------------------------------------- /feature_book/src/main/java/com/allsoftdroid/feature_book/data/network/response/GetAudioBooksResponse.kt: -------------------------------------------------------------------------------- 1 | package com.allsoftdroid.feature_book.data.network.response 2 | 3 | import com.allsoftdroid.feature_book.data.model.AudioBookSearchResultDataModel 4 | 5 | 6 | internal data class GetAudioBooksResponse( 7 | val response : AudioBookSearchResultDataModel 8 | ) -------------------------------------------------------------------------------- /feature_mybooks/src/main/java/com/allsoftdroid/audiobook/feature_mybooks/domain/IBookMetadataRepository.kt: -------------------------------------------------------------------------------- 1 | package com.allsoftdroid.audiobook.feature_mybooks.domain 2 | 3 | import com.allsoftdroid.audiobook.feature_mybooks.data.model.BookMetadata 4 | 5 | interface IBookMetadataRepository { 6 | suspend fun getBookMetadata(identifier:String):BookMetadata 7 | } -------------------------------------------------------------------------------- /feature_audiobook_enhance_details/src/main/java/com/allsoftdroid/audiobook/feature/feature_audiobook_enhance_details/data/model/WebDocument.kt: -------------------------------------------------------------------------------- 1 | package com.allsoftdroid.audiobook.feature.feature_audiobook_enhance_details.data.model 2 | 3 | data class WebDocument( 4 | val title:String, 5 | val author:String, 6 | val url:String, 7 | val list: List 8 | ) -------------------------------------------------------------------------------- /feature_audiobook_enhance_details/src/main/java/com/allsoftdroid/audiobook/feature/feature_audiobook_enhance_details/data/network/request/ILibriVoxDetailsApiService.kt: -------------------------------------------------------------------------------- 1 | package com.allsoftdroid.audiobook.feature.feature_audiobook_enhance_details.data.network.request 2 | 3 | interface ILibriVoxDetailsApiService { 4 | fun getBookDetailsPageAsync(url:String):String? 5 | } -------------------------------------------------------------------------------- /feature_book_details/src/main/java/com/allsoftdroid/feature/book_details/data/network/Utils.kt: -------------------------------------------------------------------------------- 1 | package com.allsoftdroid.feature.book_details.data.network 2 | 3 | class Utils { 4 | object MetaData{ 5 | private const val BASE_URL = "https://archive.org/" 6 | fun getBaseURL() = BASE_URL 7 | 8 | const val PATH = "/metadata/" 9 | } 10 | } -------------------------------------------------------------------------------- /feature_listen_later_ui/src/main/res/menu/backup_restore_menu.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 7 | -------------------------------------------------------------------------------- /.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 | /feature_main_player/build/ 16 | /.idea/jarRepositories.xml 17 | /buildSource/build/ 18 | -------------------------------------------------------------------------------- /feature_book_details/src/main/res/drawable/circle_shape.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /common/src/main/res/drawable/ic_play_arrow_white_24dp.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /common/src/main/res/drawable/ic_play_arrow_black_24dp.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /feature_book_details/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #000 4 | #fff 5 | #0000ffff 6 | #973F3E3E 7 | #808080 8 | #0db9f2 9 | -------------------------------------------------------------------------------- /feature_downloader/src/main/java/com/allsoftdroid/audiobook/feature_downloader/utils/DownloadStatus.java: -------------------------------------------------------------------------------- 1 | package com.allsoftdroid.audiobook.feature_downloader.utils; 2 | 3 | public interface DownloadStatus { 4 | long DOWNLOADER_PROTOCOL_NOT_SUPPORTED=-444; 5 | long DOWNLOADER_NOT_DOWNLOADING=-145; 6 | long DOWNLOADER_PENDING_ID = 0; 7 | long DOWNLOADER_RE_DOWNLOAD = -99; 8 | } 9 | -------------------------------------------------------------------------------- /feature_downloader/src/main/res/drawable/ic_download_multiple.xml: -------------------------------------------------------------------------------- 1 | 6 | 7 | -------------------------------------------------------------------------------- /feature_downloader/src/main/java/com/allsoftdroid/audiobook/feature_downloader/domain/IDownloaderCore.java: -------------------------------------------------------------------------------- 1 | package com.allsoftdroid.audiobook.feature_downloader.domain; 2 | 3 | import com.allsoftdroid.common.base.store.downloader.DownloadEvent; 4 | 5 | public interface IDownloaderCore { 6 | 7 | void handleDownloadEvent(DownloadEvent downloadEvent); 8 | 9 | void Destroy(); 10 | } 11 | -------------------------------------------------------------------------------- /feature_downloader/src/main/res/drawable/ic_download_multiple_black.xml: -------------------------------------------------------------------------------- 1 | 6 | 7 | -------------------------------------------------------------------------------- /common/src/main/res/drawable/ic_pause_white_24dp.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /common/src/main/res/drawable/ic_skip_next_white_24dp.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /common/src/main/res/drawable/ic_skip_previous_black_24dp.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /common/src/main/res/drawable/ic_skip_previous_white_24dp.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /feature_book_details/src/main/res/drawable/ic_details.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | -------------------------------------------------------------------------------- /common/src/main/res/drawable/ic_pause_black_24dp.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /common/src/main/res/drawable/ic_skip_next_black_24dp.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /common/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | common 3 | No Internet Connection! 4 | dowloads_root_directory_for_downloads 5 | local_downloads_path 6 | Share using 7 | 8 | -------------------------------------------------------------------------------- /feature_audiobook_enhance_details/src/main/java/com/allsoftdroid/audiobook/feature/feature_audiobook_enhance_details/domain/repository/IFetchAdditionBookDetailsRepository.kt: -------------------------------------------------------------------------------- 1 | package com.allsoftdroid.audiobook.feature.feature_audiobook_enhance_details.domain.repository 2 | 3 | interface IFetchAdditionBookDetailsRepository: 4 | INetworkBaseRepository { 5 | suspend fun fetchBookDetails(bookUrl:String) 6 | } -------------------------------------------------------------------------------- /feature_book/src/main/res/drawable/ic_toolbar_menu_hamburger.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | -------------------------------------------------------------------------------- /feature_audiobook_enhance_details/src/main/java/com/allsoftdroid/audiobook/feature/feature_audiobook_enhance_details/domain/network/NetworkResponseListener.kt: -------------------------------------------------------------------------------- 1 | package com.allsoftdroid.audiobook.feature.feature_audiobook_enhance_details.domain.network 2 | 3 | import com.allsoftdroid.common.base.network.NetworkResult 4 | 5 | interface NetworkResponseListener{ 6 | suspend fun onResponse(result : NetworkResult) 7 | } -------------------------------------------------------------------------------- /common/src/main/java/com/allsoftdroid/common/base/store/downloader/DownloaderEventBus.kt: -------------------------------------------------------------------------------- 1 | package com.allsoftdroid.common.base.store.downloader 2 | 3 | import com.allsoftdroid.common.base.extension.Event 4 | 5 | class DownloaderEventBus { 6 | 7 | companion object{ 8 | fun getEventBusInstance() = DownloadEventStore.getInstance( 9 | Event(DownloadNothing()) 10 | ) 11 | } 12 | } -------------------------------------------------------------------------------- /common/src/main/res/drawable/background_drawable_round_corner.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 10 | -------------------------------------------------------------------------------- /common/src/main/res/drawable/ic_keyboard_arrow_left_white.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /common/src/main/res/drawable/ic_keyboard_arrow_left_black_24dp.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /feature_book/src/main/res/drawable/ic_arrow_chevron_right.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | -------------------------------------------------------------------------------- /feature_book_details/src/main/res/drawable/backdrop_fragment_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /feature_mybooks/src/main/java/com/allsoftdroid/audiobook/feature_mybooks/data/model/LocalBookDomainModel.kt: -------------------------------------------------------------------------------- 1 | package com.allsoftdroid.audiobook.feature_mybooks.data.model 2 | 3 | data class LocalBookDomainModel ( 4 | val bookTitle:String, 5 | val bookIdentifier:String, 6 | val bookAuthor:String, 7 | val bookChaptersDownloaded:Int, 8 | val totalChapters:Int, 9 | val fileNames:List 10 | ) -------------------------------------------------------------------------------- /feature_book_details/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /feature_listen_later_ui/src/main/res/menu/listen_later_options_menu.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | 8 | 11 | 12 | -------------------------------------------------------------------------------- /feature_listen_later_ui/src/main/res/values/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 24dp 4 | 16dp 5 | 90dp 6 | 7 | 2dp 8 | 32dp 9 | 16dp 10 | -------------------------------------------------------------------------------- /feature_mybooks/src/main/res/drawable/background_round_corner_dark_border.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 10 | -------------------------------------------------------------------------------- /common/src/main/java/com/allsoftdroid/common/base/store/userAction/UserActionEventBus.kt: -------------------------------------------------------------------------------- 1 | package com.allsoftdroid.common.base.store.userAction 2 | 3 | import com.allsoftdroid.common.base.extension.Event 4 | 5 | class UserActionEventBus { 6 | companion object{ 7 | fun getEventBusInstance() = UserActionEventStore.getInstance( 8 | Event(Nothing(this::class.java.simpleName)) 9 | ) 10 | } 11 | } -------------------------------------------------------------------------------- /feature_listen_later_ui/src/main/res/drawable/ic_sort_black_24dp.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /feature_playerfullscreen/src/main/java/com/allsoftdroid/audiobook/feature/feature_playerfullscreen/data/PlayingTrackDetails.kt: -------------------------------------------------------------------------------- 1 | package com.allsoftdroid.audiobook.feature.feature_playerfullscreen.data 2 | 3 | data class PlayingTrackDetails( 4 | val bookIdentifier : String, 5 | val bookTitle:String, 6 | val trackName: String, 7 | var chapterIndex:Int, 8 | val totalChapter:Int, 9 | var isPlaying:Boolean 10 | ) -------------------------------------------------------------------------------- /feature_playerfullscreen/src/main/res/drawable/skip_next_outline.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | -------------------------------------------------------------------------------- /feature_book_details/src/main/java/com/allsoftdroid/feature/book_details/utils/DownloadStatusEvent.kt: -------------------------------------------------------------------------------- 1 | package com.allsoftdroid.feature.book_details.utils 2 | 3 | sealed class DownloadStatusEvent 4 | 5 | object DOWNLOADING : DownloadStatusEvent() 6 | object DOWNLOADED: DownloadStatusEvent() 7 | object CANCELLED: DownloadStatusEvent() 8 | object NOTHING: DownloadStatusEvent() 9 | data class PROGRESS(val percent:Float) : DownloadStatusEvent() -------------------------------------------------------------------------------- /feature_downloader/src/main/res/drawable/ic_delete.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | -------------------------------------------------------------------------------- /feature_settings/src/main/res/values/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 32dp 4 | 800dp 5 | 400dp 6 | 16dp 7 | 32dp 8 | 16dp 9 | -------------------------------------------------------------------------------- /common/src/main/java/com/allsoftdroid/common/base/extension/Variable.kt: -------------------------------------------------------------------------------- 1 | package com.allsoftdroid.common.base.extension 2 | 3 | import io.reactivex.subjects.BehaviorSubject 4 | 5 | class Variable(defaultValue: T) { 6 | var value: T = defaultValue 7 | set(value) { 8 | field = value 9 | observable.onNext(value) 10 | } 11 | 12 | val observable = BehaviorSubject.createDefault(value) 13 | } -------------------------------------------------------------------------------- /feature_book/src/main/res/drawable/ic_star.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | -------------------------------------------------------------------------------- /feature_book/src/test/java/com/allsoftdroid/feature_book/common/MockitoUtils.kt: -------------------------------------------------------------------------------- 1 | package com.allsoftdroid.feature_book.common 2 | 3 | import org.mockito.Mockito 4 | import org.mockito.stubbing.OngoingStubbing 5 | 6 | /** 7 | * Created by Antoni Castejón 8 | * 26/01/2018. 9 | */ 10 | inline fun mock() = Mockito.mock(T::class.java) 11 | inline fun whenever(methodCall: T) : OngoingStubbing = Mockito.`when`(methodCall) -------------------------------------------------------------------------------- /feature_book_details/src/main/res/drawable/ic_bookmark_minus.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | -------------------------------------------------------------------------------- /feature_mybooks/src/main/java/com/allsoftdroid/audiobook/feature_mybooks/utils/RequestStatus.kt: -------------------------------------------------------------------------------- 1 | package com.allsoftdroid.audiobook.feature_mybooks.utils 2 | 3 | import com.allsoftdroid.audiobook.feature_mybooks.data.model.LocalBookDomainModel 4 | 5 | sealed class RequestStatus 6 | 7 | data class Success(val list : List) : RequestStatus() 8 | object Empty : RequestStatus() 9 | object Started : RequestStatus() 10 | -------------------------------------------------------------------------------- /feature_mybooks/src/main/res/menu/local_books_option_menu.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | 8 | 11 | -------------------------------------------------------------------------------- /feature_book/src/main/res/drawable/close.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | -------------------------------------------------------------------------------- /feature_playerfullscreen/src/main/res/drawable/skip_previous_outline.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | -------------------------------------------------------------------------------- /feature_book_details/src/main/java/com/allsoftdroid/feature/book_details/domain/repository/IBaseRepository.kt: -------------------------------------------------------------------------------- 1 | package com.allsoftdroid.feature.book_details.domain.repository 2 | 3 | import com.allsoftdroid.common.base.extension.Event 4 | import com.allsoftdroid.common.base.extension.Variable 5 | import com.allsoftdroid.feature.book_details.utils.NetworkState 6 | 7 | interface IBaseRepository { 8 | fun networkResponse(): Variable> 9 | } -------------------------------------------------------------------------------- /feature_book_details/src/main/res/drawable/play_circle.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | -------------------------------------------------------------------------------- /common/src/main/res/drawable/ic_clock.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | -------------------------------------------------------------------------------- /common/src/main/res/drawable/play_circle_green.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | -------------------------------------------------------------------------------- /common/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #000 4 | #fff 5 | #0000ffff 6 | #973F3E3E 7 | 8 | 9 | #000 10 | #000 11 | #35D807 12 | -------------------------------------------------------------------------------- /feature_listen_later_ui/src/main/java/com/allsoftdroid/audiobook/feature_listen_later_ui/utils/StringUtility.kt: -------------------------------------------------------------------------------- 1 | package com.allsoftdroid.audiobook.feature_listen_later_ui.utils 2 | 3 | object StringUtility { 4 | const val DATA_IMPORT_SUCCESS = "Data Imported Successfully" 5 | const val DATA_IMPORT_FAILED = "Failed to Import" 6 | 7 | const val DATA_EXPORT_SUCCESS = "Data Exported Successfully" 8 | const val DATA_EXPORT_FAILED = "Failed to Export" 9 | } -------------------------------------------------------------------------------- /common/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /feature_book_details/src/main/res/drawable/pause_circle.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | -------------------------------------------------------------------------------- /.github/workflows/issue-pr-bot-reply.yml: -------------------------------------------------------------------------------- 1 | name: Bot Reply 2 | 3 | on: [pull_request, issues] 4 | 5 | jobs: 6 | greeting: 7 | runs-on: ubuntu-latest 8 | steps: 9 | - uses: actions/first-interaction@v1 10 | with: 11 | repo-token: ${{ secrets.GITHUB_TOKEN }} 12 | issue-message: 'Thanks for reporting!, We will soon review it and assign labels based on severity' 13 | pr-message: 'Thanks for your input. Soon it will be reviewed.' -------------------------------------------------------------------------------- /common/src/main/res/drawable/ic_folder_black_24dp.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /feature_listen_later_ui/src/main/java/com/allsoftdroid/audiobook/feature_listen_later_ui/domain/RequestStatus.kt: -------------------------------------------------------------------------------- 1 | package com.allsoftdroid.audiobook.feature_listen_later_ui.domain 2 | 3 | import com.allsoftdroid.audiobook.feature_listen_later_ui.data.model.ListenLaterItemDomainModel 4 | 5 | sealed class RequestStatus 6 | 7 | data class Success(val list : List) : RequestStatus() 8 | object Empty : RequestStatus() 9 | object Started : RequestStatus() -------------------------------------------------------------------------------- /feature_playerfullscreen/src/main/res/drawable/play_circle.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | -------------------------------------------------------------------------------- /.github/labeler.yml: -------------------------------------------------------------------------------- 1 | test: 2 | - test/**/* 3 | 4 | feature: 5 | - feature_book/**/* 6 | - feature_book_details/**/* 7 | - feature_audiobook_enhance_details/**/* 8 | - feature_mini_player/**/* 9 | - feature_playerfullscreen/**/* 10 | - feature_downloader/**/* 11 | 12 | 13 | library: 14 | - common/**/* 15 | - buildSrc/**/* 16 | - services/**/* 17 | - database/**/* 18 | 19 | app: 20 | - app/**/* 21 | - ./* 22 | 23 | documentation: 24 | - ./*.md -------------------------------------------------------------------------------- /feature_downloader/src/main/res/menu/menu_downloads.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 10 | -------------------------------------------------------------------------------- /feature_mybooks/src/main/java/com/allsoftdroid/audiobook/feature_mybooks/domain/ILocalBooksRepository.kt: -------------------------------------------------------------------------------- 1 | package com.allsoftdroid.audiobook.feature_mybooks.domain 2 | 3 | import com.allsoftdroid.audiobook.feature_mybooks.data.model.LocalBookFiles 4 | 5 | interface ILocalBooksRepository { 6 | suspend fun getLocalBookFiles():List 7 | 8 | suspend fun removeBook(identifier:String) 9 | 10 | suspend fun deleteAllChapters(identifier: String) 11 | } -------------------------------------------------------------------------------- /feature_book/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /feature_book_details/src/main/res/drawable/download_check.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | -------------------------------------------------------------------------------- /feature_book_details/src/main/res/drawable/ic_headphones.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | -------------------------------------------------------------------------------- /feature_downloader/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /feature_downloader/src/main/res/drawable/ic_delete_sweep.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app' 2 | include ':buildSrc' 3 | include ':database' 4 | include ':common' 5 | include ':services' 6 | include ':feature_book' 7 | include ':feature_book_details' 8 | include ':feature_mini_player' 9 | include ':feature_downloader' 10 | include ':feature_audiobook_enhance_details' 11 | include ':feature_playerfullscreen' 12 | include ':feature_settings' 13 | include ':feature_listen_later_ui' 14 | include ':feature_mybooks' 15 | 16 | rootProject.name='AudioBook' 17 | -------------------------------------------------------------------------------- /common/src/main/res/drawable/pause_circle.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 10 | -------------------------------------------------------------------------------- /feature_audiobook_enhance_details/src/main/java/com/allsoftdroid/audiobook/feature/feature_audiobook_enhance_details/domain/repository/IStoreRepository.kt: -------------------------------------------------------------------------------- 1 | package com.allsoftdroid.audiobook.feature.feature_audiobook_enhance_details.domain.repository 2 | 3 | import com.dropbox.android.external.store4.Store 4 | 5 | interface IStoreRepository { 6 | 7 | fun provideEnhanceBookSearchStore(): Store, String> 8 | 9 | fun provideEnhanceBookDetailsStore(): Store 10 | } -------------------------------------------------------------------------------- /feature_book_details/src/main/java/com/allsoftdroid/feature/book_details/domain/model/AudioBookMetadataDomainModel.kt: -------------------------------------------------------------------------------- 1 | package com.allsoftdroid.feature.book_details.domain.model 2 | 3 | data class AudioBookMetadataDomainModel( 4 | val identifier : String, 5 | val creator : String, 6 | val date : String, 7 | val description : String, 8 | val licenseUrl : String, 9 | val tag : String, 10 | val title : String, 11 | val release_year : String, 12 | val runtime: String 13 | ) -------------------------------------------------------------------------------- /feature_book_details/src/main/res/drawable/ic_bookmark_plus_outline.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | -------------------------------------------------------------------------------- /feature_downloader/src/main/res/values/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 100dp 4 | 16dp 5 | 36dp 6 | 16dp 7 | 32dp 8 | 30dp 9 | 4dp 10 | -------------------------------------------------------------------------------- /common/src/main/res/drawable/pause_circle_green.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 10 | -------------------------------------------------------------------------------- /feature_book/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 10 | -------------------------------------------------------------------------------- /feature_book_details/src/main/java/com/allsoftdroid/feature/book_details/domain/repository/IListenLaterRepository.kt: -------------------------------------------------------------------------------- 1 | package com.allsoftdroid.feature.book_details.domain.repository 2 | 3 | import com.allsoftdroid.feature.book_details.data.model.ListenLaterDomainModel 4 | 5 | internal interface IListenLaterRepository { 6 | suspend fun isAddedToListenLater(bookId: String):Boolean 7 | suspend fun addToListenLater(listenLater : ListenLaterDomainModel) 8 | suspend fun removeListenLater(bookId:String) 9 | } -------------------------------------------------------------------------------- /feature_book_details/src/main/res/drawable/semi_circle_shape_left.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 8 | 11 | 14 | -------------------------------------------------------------------------------- /services/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /feature_book_details/src/main/java/com/allsoftdroid/feature/book_details/data/model/AudioBookMetadataDataModel.kt: -------------------------------------------------------------------------------- 1 | package com.allsoftdroid.feature.book_details.data.model 2 | 3 | 4 | internal data class AudioBookMetadataDataModel( 5 | val identifier : String, 6 | val creator : String, 7 | val date : String, 8 | val description : String, 9 | val licenseurl : String, 10 | val subject : String, 11 | val title : String, 12 | val publicdate : String, 13 | val runtime: String? 14 | ) -------------------------------------------------------------------------------- /feature_book_details/src/main/java/com/allsoftdroid/feature/book_details/domain/repository/ITrackListRepository.kt: -------------------------------------------------------------------------------- 1 | package com.allsoftdroid.feature.book_details.domain.repository 2 | 3 | import com.allsoftdroid.feature.book_details.data.model.TrackFormat 4 | import com.allsoftdroid.feature.book_details.domain.model.AudioBookTrackDomainModel 5 | import kotlinx.coroutines.flow.Flow 6 | 7 | interface ITrackListRepository{ 8 | suspend fun loadTrackListData(format: TrackFormat) : Flow> 9 | } -------------------------------------------------------------------------------- /feature_book_details/src/main/res/drawable/semi_circle_shape_right.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 8 | 11 | 14 | -------------------------------------------------------------------------------- /common/src/test/java/com/allsoftdroid/common/ExampleUnitTest.kt: -------------------------------------------------------------------------------- 1 | //package com.allsoftdroid.common 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 | //} 18 | -------------------------------------------------------------------------------- /database/src/test/java/com/allsoftdroid/database/ExampleUnitTest.kt: -------------------------------------------------------------------------------- 1 | //package com.allsoftdroid.database 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 | //} 18 | -------------------------------------------------------------------------------- /feature_audiobook_enhance_details/src/main/java/com/allsoftdroid/audiobook/feature/feature_audiobook_enhance_details/domain/repository/INetworkBaseRepository.kt: -------------------------------------------------------------------------------- 1 | package com.allsoftdroid.audiobook.feature.feature_audiobook_enhance_details.domain.repository 2 | 3 | import com.allsoftdroid.audiobook.feature.feature_audiobook_enhance_details.domain.network.NetworkResponseListener 4 | 5 | interface INetworkBaseRepository { 6 | fun registerNetworkResponse(listener: NetworkResponseListener) 7 | fun unRegisterNetworkResponse() 8 | } -------------------------------------------------------------------------------- /common/src/main/res/drawable/ic_info_outline_black_24dp.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /database/src/main/java/com/allsoftdroid/database/networkCacheDB/DatabaseNetworkResponseEntity.kt: -------------------------------------------------------------------------------- 1 | package com.allsoftdroid.database.networkCacheDB 2 | 3 | import androidx.room.ColumnInfo 4 | import androidx.room.Entity 5 | import androidx.room.PrimaryKey 6 | 7 | @Entity(tableName = "Network_Cache_Table") 8 | data class DatabaseNetworkResponseEntity( 9 | @PrimaryKey 10 | @ColumnInfo(name = "networkRequestId") 11 | var identifier : String, 12 | 13 | @ColumnInfo(name = "response") 14 | var networkResponse:String 15 | ) -------------------------------------------------------------------------------- /common/src/main/java/com/allsoftdroid/common/base/usecase/BaseUseCaseScheduler.kt: -------------------------------------------------------------------------------- 1 | package com.allsoftdroid.common.base.usecase 2 | 3 | interface BaseUseCaseScheduler { 4 | 5 | fun execute(runnable: Runnable) 6 | 7 | fun < V : BaseUseCase.ResponseValues> notifyResponse( 8 | response : V, 9 | useCaseCallback: BaseUseCase.UseCaseCallback 10 | ) 11 | 12 | fun onError( 13 | useCaseCallback: BaseUseCase.UseCaseCallback, 14 | t : Throwable 15 | ) 16 | } -------------------------------------------------------------------------------- /common/src/main/java/com/allsoftdroid/common/base/utils/SettingsPreferenceUtils.kt: -------------------------------------------------------------------------------- 1 | package com.allsoftdroid.common.base.utils 2 | 3 | object SettingsPreferenceUtils { 4 | const val DOWNLOADS_KEY = "downloads_preference_key" 5 | const val DOWNLOADS_HELP_KEY = "downloads_help_preference_key" 6 | const val VERSION_KEY = "version_preference_key" 7 | const val LICENSES_KEY = "licenses_preference_key" 8 | const val FEEDBACK_KEY= "feedback_preference_key" 9 | const val PRIVACY_POLICY="privacy_policy_preference_key" 10 | } -------------------------------------------------------------------------------- /feature_book_details/src/main/res/drawable/download_outline.xml: -------------------------------------------------------------------------------- 1 | 6 | 7 | -------------------------------------------------------------------------------- /feature_book_details/src/main/res/drawable/ic_book_open_outline.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | -------------------------------------------------------------------------------- /feature_downloader/src/main/res/drawable/ic_file_music.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | -------------------------------------------------------------------------------- /feature_mini_player/src/main/res/values/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 32dp 4 | 16dp 5 | 70dp 6 | 5dp 7 | 80dp 8 | 32dp 9 | 8dp 10 | 64dp 11 | -------------------------------------------------------------------------------- /feature_audiobook_enhance_details/src/main/java/com/allsoftdroid/audiobook/feature/feature_audiobook_enhance_details/data/model/BookDetails.kt: -------------------------------------------------------------------------------- 1 | package com.allsoftdroid.audiobook.feature.feature_audiobook_enhance_details.data.model 2 | 3 | data class BookDetails( 4 | var webDocument: WebDocument? = null, 5 | val runtime:String="", 6 | val archiveUrl : String="", 7 | val gutenbergUrl:String="", 8 | val description:String="", 9 | val genres:String="", 10 | val language:String="", 11 | val chapters : List 12 | ) -------------------------------------------------------------------------------- /feature_book_details/src/main/java/com/allsoftdroid/feature/book_details/domain/repository/IMetadataRepository.kt: -------------------------------------------------------------------------------- 1 | package com.allsoftdroid.feature.book_details.domain.repository 2 | 3 | import com.allsoftdroid.feature.book_details.domain.model.AudioBookMetadataDomainModel 4 | import kotlinx.coroutines.flow.Flow 5 | 6 | interface IMetadataRepository : IBaseRepository{ 7 | suspend fun loadMetadata() 8 | fun getMetadata() : Flow 9 | fun getBookId() : String 10 | 11 | fun cancelRequestInFlight() 12 | } -------------------------------------------------------------------------------- /feature_book/src/main/res/drawable/ic_refresh.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/splash_drawable.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /feature_book_details/src/main/res/drawable/clock_outline.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | -------------------------------------------------------------------------------- /.github/workflows/release-notes.yml: -------------------------------------------------------------------------------- 1 | # Trigger the workflow on milestone events 2 | name: Release notes generator 3 | 4 | on: 5 | milestone: 6 | types: [closed] 7 | 8 | jobs: 9 | create-release-notes: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - uses: actions/checkout@master 13 | - name: Create Release Notes 14 | uses: docker://decathlon/release-notes-generator-action:2.0.1 15 | env: 16 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 17 | OUTPUT_FOLDER: temp_release_notes 18 | USE_MILESTONE_TITLE: "true" -------------------------------------------------------------------------------- /feature_book_details/src/main/java/com/allsoftdroid/feature/book_details/data/network/response/GetAudioBookMetadataResponse.kt: -------------------------------------------------------------------------------- 1 | package com.allsoftdroid.feature.book_details.data.network.response 2 | 3 | import com.allsoftdroid.feature.book_details.data.model.AudioBookMetadataDataModel 4 | import com.allsoftdroid.feature.book_details.data.model.AudioBookTrackDataModel 5 | 6 | internal data class GetAudioBookMetadataResponse( 7 | val files : List, 8 | val item_size : String, 9 | val metadata : AudioBookMetadataDataModel 10 | ) -------------------------------------------------------------------------------- /feature_downloader/src/main/res/drawable/ic_close_circle.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | -------------------------------------------------------------------------------- /feature_book_details/src/main/res/drawable/play_circle_outline.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | -------------------------------------------------------------------------------- /feature_book/src/main/res/drawable/ic_history.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | -------------------------------------------------------------------------------- /feature_book/src/main/res/values/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8dp 4 | 4dp 5 | 90dp 6 | 16dp 7 | 30dp 8 | 4dp 9 | 160dp 10 | 16dp 11 | 64dp 12 | -------------------------------------------------------------------------------- /common/src/main/java/com/allsoftdroid/common/base/store/downloader/DownloadEventStore.kt: -------------------------------------------------------------------------------- 1 | package com.allsoftdroid.common.base.store.downloader 2 | 3 | import com.allsoftdroid.common.base.extension.Event 4 | import com.allsoftdroid.common.base.store.Store 5 | import com.allsoftdroid.common.base.utils.SingletonHolder 6 | 7 | class DownloadEventStore private constructor(defaultValue: Event) 8 | : Store>(defaultValue){ 9 | companion object : SingletonHolder>(creator = ::DownloadEventStore) 10 | } -------------------------------------------------------------------------------- /common/src/main/res/drawable/arrow_collapse_down.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 12 | -------------------------------------------------------------------------------- /common/src/main/res/drawable/ic_book_play.xml: -------------------------------------------------------------------------------- 1 | 6 | 7 | -------------------------------------------------------------------------------- /common/src/main/java/com/allsoftdroid/common/base/store/userAction/UserActionEventStore.kt: -------------------------------------------------------------------------------- 1 | package com.allsoftdroid.common.base.store.userAction 2 | 3 | import com.allsoftdroid.common.base.extension.Event 4 | import com.allsoftdroid.common.base.store.Store 5 | import com.allsoftdroid.common.base.utils.SingletonHolder 6 | 7 | class UserActionEventStore private constructor(defaultValue: Event) 8 | : Store>(defaultValue){ 9 | companion object : SingletonHolder>(creator = ::UserActionEventStore) 10 | } -------------------------------------------------------------------------------- /common/src/main/java/com/allsoftdroid/common/base/store/audioPlayer/AudioPlayerEventStore.kt: -------------------------------------------------------------------------------- 1 | package com.allsoftdroid.common.base.store.audioPlayer 2 | 3 | import com.allsoftdroid.common.base.extension.Event 4 | import com.allsoftdroid.common.base.store.Store 5 | import com.allsoftdroid.common.base.utils.SingletonHolder 6 | 7 | class AudioPlayerEventStore private constructor(defaultValue: Event) 8 | : Store>(defaultValue){ 9 | companion object : SingletonHolder>(creator = ::AudioPlayerEventStore) 10 | } -------------------------------------------------------------------------------- /common/src/main/res/drawable/play_circle_outline.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 10 | -------------------------------------------------------------------------------- /feature_listen_later_ui/src/main/res/drawable/backup_restore.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | -------------------------------------------------------------------------------- /feature_listen_later_ui/src/main/res/menu/sort_options_menu.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 11 | 15 | -------------------------------------------------------------------------------- /services/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | services 3 | prev 4 | pp 5 | nxt 6 | Play Next Track 7 | Play or Pause Track 8 | Play Previous Track 9 | AudioBook thumbnail 10 | audio_player_service 11 | 12 | -------------------------------------------------------------------------------- /common/src/main/java/com/allsoftdroid/common/base/usecase/UiCallbackWrapper.kt: -------------------------------------------------------------------------------- 1 | package com.allsoftdroid.common.base.usecase 2 | 3 | class UiCallbackWrapper< V : BaseUseCase.ResponseValues>( 4 | private val callback: BaseUseCase.UseCaseCallback, 5 | private val mUseCaseHandler: UseCaseHandler) : BaseUseCase.UseCaseCallback { 6 | 7 | 8 | 9 | override suspend fun onSuccess(response: V) { 10 | mUseCaseHandler.notifyResponse(response,callback) 11 | } 12 | 13 | override suspend fun onError(t: Throwable) { 14 | mUseCaseHandler.notifyError(callback,t) 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /.idea/runConfigurations.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 12 | -------------------------------------------------------------------------------- /common/src/main/java/com/allsoftdroid/common/base/store/Store.kt: -------------------------------------------------------------------------------- 1 | package com.allsoftdroid.common.base.store 2 | 3 | 4 | import com.jakewharton.rxrelay2.BehaviorRelay 5 | import com.jakewharton.rxrelay2.Relay 6 | import io.reactivex.Observable 7 | 8 | 9 | open class Store(defaultValue: T) { 10 | 11 | private val storeSubject: Relay = BehaviorRelay.createDefault(defaultValue).toSerialized() 12 | 13 | fun observe(): Observable { 14 | return storeSubject.hide() 15 | .distinctUntilChanged() 16 | } 17 | 18 | fun publish(value: T) { 19 | storeSubject.accept(value) 20 | } 21 | } -------------------------------------------------------------------------------- /feature_book_details/src/main/res/drawable/ic_account_outline.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | -------------------------------------------------------------------------------- /feature_audiobook_enhance_details/src/main/java/com/allsoftdroid/audiobook/feature/feature_audiobook_enhance_details/data/network/request/LibrivoxDetailsApiService.kt: -------------------------------------------------------------------------------- 1 | package com.allsoftdroid.audiobook.feature.feature_audiobook_enhance_details.data.network.request 2 | 3 | import org.jsoup.Jsoup 4 | 5 | class LibrivoxDetailsApiService : ILibriVoxDetailsApiService { 6 | 7 | override fun getBookDetailsPageAsync(url: String): String? { 8 | var htmlContent:String? 9 | 10 | Jsoup.connect(url).get().run { 11 | htmlContent = this.html() 12 | } 13 | 14 | return htmlContent 15 | } 16 | 17 | } -------------------------------------------------------------------------------- /common/src/main/res/drawable/share_outline.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 12 | -------------------------------------------------------------------------------- /common/src/main/java/com/allsoftdroid/common/base/store/audioPlayer/AudioPlayerEventBus.kt: -------------------------------------------------------------------------------- 1 | package com.allsoftdroid.common.base.store.audioPlayer 2 | 3 | import com.allsoftdroid.common.base.extension.Event 4 | import com.allsoftdroid.common.base.extension.PlayingState 5 | 6 | class AudioPlayerEventBus { 7 | 8 | companion object{ 9 | 10 | fun getEventBusInstance() = AudioPlayerEventStore.getInstance(Event( 11 | EmptyEvent( 12 | PlayingState( 13 | playingItemIndex = 0, 14 | action_need = false 15 | ) 16 | ) 17 | )) 18 | } 19 | } -------------------------------------------------------------------------------- /feature_book_details/src/main/res/drawable/close_circle_outline.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | -------------------------------------------------------------------------------- /services/src/main/java/com/allsoftdroid/audiobook/services/audio/utils/TextFormatter.kt: -------------------------------------------------------------------------------- 1 | package com.allsoftdroid.audiobook.services.audio.utils 2 | 3 | import android.os.Build 4 | import android.text.Html 5 | 6 | object TextFormatter { 7 | fun getPartialString(text:String):String{ 8 | val filterText = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { 9 | Html.fromHtml(text,Html.FROM_HTML_MODE_COMPACT).toString() 10 | } else { 11 | Html.fromHtml(text).toString() 12 | } 13 | return if(filterText.length>23){ 14 | filterText.substring(0,20)+"..." 15 | }else filterText 16 | } 17 | } -------------------------------------------------------------------------------- /common/src/main/res/drawable/ic_clock_outline.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 12 | -------------------------------------------------------------------------------- /feature_mybooks/src/main/res/values/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4dp 4 | 16dp 5 | 24dp 6 | 7 | 2dp 8 | 8dp 9 | 10 | 16dp 11 | 16dp 12 | 8dp 13 | 14 | 90dp 15 | 16 | 16dp 17 | 32dp 18 | -------------------------------------------------------------------------------- /.github/workflows/New-Issue-Comments.yml: -------------------------------------------------------------------------------- 1 | name: New Issue Comment Bot 2 | 3 | on: [issues] 4 | 5 | jobs: 6 | comment: 7 | runs-on: ubuntu-latest 8 | steps: 9 | - uses: actions/github-script@0.9.0 10 | with: 11 | github-token: ${{secrets.GITHUB_TOKEN}} 12 | script: | 13 | github.issues.createComment({ 14 | issue_number: context.issue.number, 15 | owner: context.repo.owner, 16 | repo: context.repo.repo, 17 | body: 'Thanks for reporting!, We will soon review it and assign labels based on severity' 18 | labels: ['Verification','Pending'] 19 | }) -------------------------------------------------------------------------------- /common/src/main/java/com/allsoftdroid/common/base/fragment/BaseContainerFragment.kt: -------------------------------------------------------------------------------- 1 | package com.allsoftdroid.common.base.fragment 2 | 3 | import android.view.LayoutInflater 4 | import android.view.ViewGroup 5 | import androidx.databinding.DataBindingUtil 6 | import androidx.databinding.ViewDataBinding 7 | import androidx.fragment.app.Fragment 8 | 9 | abstract class BaseContainerFragment : Fragment(){ 10 | 11 | fun inflateLayout(inflater:LayoutInflater,layout:Int,parentView:ViewGroup?,attachToParent:Boolean = false):T = DataBindingUtil.inflate( 12 | inflater, 13 | layout, 14 | parentView, 15 | attachToParent 16 | ) 17 | } -------------------------------------------------------------------------------- /feature_downloader/src/main/res/layout/layout_checkbox.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 15 | -------------------------------------------------------------------------------- /feature_book_details/src/main/res/drawable/ic_format_list_bulleted.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | -------------------------------------------------------------------------------- /common/src/main/java/com/allsoftdroid/common/base/activity/BaseActivity.kt: -------------------------------------------------------------------------------- 1 | package com.allsoftdroid.common.base.activity 2 | 3 | import android.os.Bundle 4 | import androidx.annotation.LayoutRes 5 | import androidx.appcompat.app.AppCompatActivity 6 | import timber.log.Timber 7 | 8 | abstract class BaseActivity : AppCompatActivity() { 9 | 10 | @get:LayoutRes 11 | protected abstract val layoutResId:Int 12 | 13 | override fun onCreate(savedInstanceState: Bundle?) { 14 | super.onCreate(savedInstanceState) 15 | 16 | setContentView(layoutResId) 17 | 18 | supportActionBar?.hide() 19 | 20 | Timber.v("onCreate ${javaClass.simpleName}") 21 | } 22 | } -------------------------------------------------------------------------------- /common/src/main/java/com/allsoftdroid/common/base/store/userAction/UserActionEvent.kt: -------------------------------------------------------------------------------- 1 | package com.allsoftdroid.common.base.store.userAction 2 | 3 | sealed class UserActionEvent { 4 | abstract val source:String 5 | } 6 | 7 | data class OpenDownloadUI( 8 | override val source: String 9 | ):UserActionEvent() 10 | 11 | data class OpenLicensesUI( 12 | override val source: String 13 | ):UserActionEvent() 14 | 15 | data class Nothing( 16 | override val source: String 17 | ):UserActionEvent() 18 | 19 | data class OpenMainPlayerUI( 20 | override val source: String 21 | ):UserActionEvent() 22 | 23 | data class OpenMiniPlayerUI( 24 | override val source: String 25 | ):UserActionEvent() -------------------------------------------------------------------------------- /app/src/main/java/com/allsoftdroid/audiobook/ApplicationController.kt: -------------------------------------------------------------------------------- 1 | package com.allsoftdroid.audiobook 2 | 3 | import android.app.Application 4 | import com.allsoftdroid.common.di.BaseModule 5 | import org.koin.android.ext.koin.androidContext 6 | import org.koin.core.context.startKoin 7 | import timber.log.Timber 8 | 9 | class ApplicationController : Application(){ 10 | 11 | override fun onCreate() { 12 | super.onCreate() 13 | 14 | startKoin { 15 | androidContext(this@ApplicationController) 16 | } 17 | 18 | BaseModule.injectFeature() 19 | 20 | if(BuildConfig.DEBUG){ 21 | Timber.plant(Timber.DebugTree()) 22 | } 23 | } 24 | } -------------------------------------------------------------------------------- /database/src/main/java/com/allsoftdroid/database/listenLaterDB/entity/DatabaseListenLaterEntity.kt: -------------------------------------------------------------------------------- 1 | package com.allsoftdroid.database.listenLaterDB.entity 2 | 3 | import androidx.room.ColumnInfo 4 | import androidx.room.Entity 5 | import androidx.room.PrimaryKey 6 | 7 | @Entity(tableName = "ListenLater_Table") 8 | data class DatabaseListenLaterEntity ( 9 | 10 | @PrimaryKey 11 | @ColumnInfo(name = "book_id") 12 | var identifier : String, 13 | 14 | var title : String, 15 | 16 | @ColumnInfo(name = "authors") 17 | var author : String, 18 | 19 | @ColumnInfo(name = "play_time") 20 | var duration : String, 21 | 22 | @ColumnInfo(name = "timestamp") 23 | var timeStamp : String 24 | ) -------------------------------------------------------------------------------- /feature_book/src/main/java/com/allsoftdroid/feature_book/domain/repository/AudioBookRepository.kt: -------------------------------------------------------------------------------- 1 | package com.allsoftdroid.feature_book.domain.repository 2 | 3 | import com.allsoftdroid.feature_book.domain.model.AudioBookDomainModel 4 | import kotlinx.coroutines.flow.Flow 5 | 6 | interface AudioBookRepository { 7 | 8 | suspend fun fetchBookList(page : Int) 9 | fun getAudioBooks() : Flow> 10 | 11 | suspend fun searchBookList(query:String,page: Int) 12 | fun getSearchBooks() : Flow> 13 | 14 | fun registerNetworkResponse(listener: NetworkResponseListener) 15 | fun unRegisterNetworkResponse() 16 | 17 | fun cancelRequestInFlight() 18 | } -------------------------------------------------------------------------------- /feature_downloader/src/main/res/drawable/ic_emoticon_excited.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | -------------------------------------------------------------------------------- /feature_listen_later_ui/src/main/java/com/allsoftdroid/audiobook/feature_listen_later_ui/data/model/ListenLaterItemDomainModel.kt: -------------------------------------------------------------------------------- 1 | package com.allsoftdroid.audiobook.feature_listen_later_ui.data.model 2 | 3 | import com.allsoftdroid.database.listenLaterDB.entity.DatabaseListenLaterEntity 4 | 5 | data class ListenLaterItemDomainModel( 6 | val identifier:String, 7 | val title:String, 8 | val author:String, 9 | val duration:String, 10 | val progress:Int=0 11 | ) 12 | 13 | fun DatabaseListenLaterEntity.toDomainModel() = ListenLaterItemDomainModel( 14 | identifier = this.identifier, 15 | title = this.title, 16 | author = this.author, 17 | duration = this.duration, 18 | progress = 0 19 | ) -------------------------------------------------------------------------------- /common/src/main/java/com/allsoftdroid/common/base/utils/ShareUtils.kt: -------------------------------------------------------------------------------- 1 | package com.allsoftdroid.common.base.utils 2 | 3 | import android.app.Activity 4 | import android.content.Intent 5 | import com.allsoftdroid.common.R 6 | 7 | object ShareUtils { 8 | 9 | fun share(context: Activity, subject:String, txt:String){ 10 | val shareIntent = Intent(Intent.ACTION_SEND) 11 | shareIntent.putExtra(Intent.EXTRA_SUBJECT,subject) 12 | shareIntent.putExtra(Intent.EXTRA_TITLE,subject) 13 | shareIntent.putExtra(Intent.EXTRA_TEXT,txt) 14 | shareIntent.type = "text/plain" 15 | 16 | context.startActivity(Intent.createChooser(shareIntent,context.getString(R.string.share_using))) 17 | } 18 | } -------------------------------------------------------------------------------- /feature_book_details/src/main/res/drawable/circular_progressbar.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 11 | 12 | 18 | 19 | -------------------------------------------------------------------------------- /feature_downloader/src/main/res/drawable/ic_restart.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | -------------------------------------------------------------------------------- /feature_audiobook_enhance_details/src/main/java/com/allsoftdroid/audiobook/feature/feature_audiobook_enhance_details/data/network/response/NetworkResponse.kt: -------------------------------------------------------------------------------- 1 | package com.allsoftdroid.audiobook.feature.feature_audiobook_enhance_details.data.network.response 2 | 3 | import com.squareup.moshi.Json 4 | import com.squareup.moshi.JsonClass 5 | 6 | /** 7 | * Network response received from LibriVox API 8 | */ 9 | 10 | @JsonClass(generateAdapter = true) 11 | data class NetworkResponse( 12 | @field:Json(name = "status") val status:String, 13 | @field:Json(name = "results") val results : String, 14 | @field:Json(name = "pagination") val pagination:String, 15 | @field:Json(name = "search_page") val search_page:String 16 | ) -------------------------------------------------------------------------------- /feature_playerfullscreen/src/main/res/drawable/ic_rewind_30.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | -------------------------------------------------------------------------------- /common/src/main/java/com/allsoftdroid/common/base/extension/CreateImageOverlay.kt: -------------------------------------------------------------------------------- 1 | package com.allsoftdroid.common.base.extension 2 | 3 | import android.graphics.drawable.LayerDrawable 4 | import android.content.Context 5 | import android.graphics.drawable.Drawable 6 | 7 | 8 | 9 | class CreateImageOverlay(private val context: Context) { 10 | 11 | companion object{ 12 | fun with(context:Context)= 13 | CreateImageOverlay(context) 14 | } 15 | 16 | fun buildOverlay(front:Int,back:Int): Drawable { 17 | 18 | val layers = arrayOfNulls(2) 19 | layers[0] = context.getDrawable(back) 20 | layers[1] = context.getDrawable(front) 21 | 22 | return LayerDrawable(layers) 23 | } 24 | } -------------------------------------------------------------------------------- /feature_audiobook_enhance_details/src/main/java/com/allsoftdroid/audiobook/feature/feature_audiobook_enhance_details/domain/repository/ISearchBookDetailsRepository.kt: -------------------------------------------------------------------------------- 1 | package com.allsoftdroid.audiobook.feature.feature_audiobook_enhance_details.domain.repository 2 | 3 | import com.allsoftdroid.audiobook.feature.feature_audiobook_enhance_details.data.model.WebDocument 4 | import kotlinx.coroutines.flow.Flow 5 | 6 | interface ISearchBookDetailsRepository : 7 | INetworkBaseRepository { 8 | suspend fun searchBookDetailsInRemoteRepository(searchTitle:String,author:String,page:Int) 9 | fun getSearchBooksList(): Flow> 10 | fun getBookListWithRanks(bookTitle:String,bookAuthor:String): Flow>> 11 | } -------------------------------------------------------------------------------- /feature_book_details/src/androidTest/java/com/allsoftdroid/feature/book_details/presentation/utils/FakeListenLaterRepository.kt: -------------------------------------------------------------------------------- 1 | package com.allsoftdroid.feature.book_details.presentation.utils 2 | 3 | import com.allsoftdroid.feature.book_details.data.model.ListenLaterDomainModel 4 | import com.allsoftdroid.feature.book_details.domain.repository.IListenLaterRepository 5 | 6 | internal class FakeListenLaterRepository : IListenLaterRepository { 7 | override suspend fun isAddedToListenLater(bookId: String): Boolean { 8 | return true 9 | } 10 | 11 | override suspend fun addToListenLater(listenLater: ListenLaterDomainModel) { 12 | 13 | } 14 | 15 | override suspend fun removeListenLater(bookId: String) { 16 | 17 | } 18 | } -------------------------------------------------------------------------------- /feature_playerfullscreen/src/main/res/drawable/ic_fast_forward_30.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | -------------------------------------------------------------------------------- /feature_book_details/src/main/res/drawable/ic_tag_multiple_outline.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | -------------------------------------------------------------------------------- /common/src/main/res/drawable/book_search_outline.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | -------------------------------------------------------------------------------- /feature_book/src/main/res/drawable/ic_bug.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | -------------------------------------------------------------------------------- /feature_book_details/src/main/java/com/allsoftdroid/feature/book_details/data/model/ListenLaterDomainModel.kt: -------------------------------------------------------------------------------- 1 | package com.allsoftdroid.feature.book_details.data.model 2 | 3 | import com.allsoftdroid.database.listenLaterDB.entity.DatabaseListenLaterEntity 4 | 5 | internal data class ListenLaterDomainModel( 6 | val bookId:String, 7 | val bookName:String, 8 | val bookAuthor:String, 9 | val bookDuration:String 10 | ) 11 | 12 | internal fun ListenLaterDomainModel.toDatabaseModel():DatabaseListenLaterEntity = 13 | DatabaseListenLaterEntity( 14 | identifier = bookId, 15 | title = bookName, 16 | author = bookAuthor, 17 | duration = bookDuration, 18 | timeStamp = System.currentTimeMillis().toString() 19 | ) 20 | -------------------------------------------------------------------------------- /app/src/main/java/com/allsoftdroid/audiobook/utility/MovableFrameLayout.kt: -------------------------------------------------------------------------------- 1 | package com.allsoftdroid.audiobook.utility 2 | 3 | import android.content.Context 4 | import android.util.AttributeSet 5 | import android.widget.FrameLayout 6 | import androidx.coordinatorlayout.widget.CoordinatorLayout.DefaultBehavior 7 | 8 | 9 | @DefaultBehavior(MoveUpBehavior::class) 10 | class MovableFrameLayout : FrameLayout { 11 | 12 | constructor(context: Context) : super(context) 13 | 14 | constructor(context: Context, attrs: AttributeSet?) : super( 15 | context, 16 | attrs 17 | ) 18 | 19 | constructor( 20 | context: Context, 21 | attrs: AttributeSet?, 22 | defStyleAttr: Int 23 | ) : super(context, attrs, defStyleAttr) 24 | } -------------------------------------------------------------------------------- /feature_playerfullscreen/src/main/res/values/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 32dp 4 | 5 | 16dp 6 | 7 | 20dp 8 | 180dp 9 | 16dp 10 | 8dp 11 | 12 | 8dp 13 | 24dp 14 | 16dp 15 | 16 | 32dp 17 | 40dp 18 | 80dp 19 | -------------------------------------------------------------------------------- /common/src/main/java/com/allsoftdroid/common/base/utils/SingletonHolder.kt: -------------------------------------------------------------------------------- 1 | package com.allsoftdroid.common.base.utils 2 | 3 | open class SingletonHolder(creator: (A) -> T) { 4 | private var creator: ((A) -> T)? = creator 5 | @Volatile private var instance: T? = null 6 | 7 | fun getInstance(arg: A): T { 8 | val i = instance 9 | if (i != null) { 10 | return i 11 | } 12 | 13 | return synchronized(this) { 14 | val i2 = instance 15 | if (i2 != null) { 16 | i2 17 | } else { 18 | val created = creator!!(arg) 19 | instance = created 20 | creator = null 21 | created 22 | } 23 | } 24 | } 25 | } -------------------------------------------------------------------------------- /feature_downloader/src/main/java/com/allsoftdroid/audiobook/feature_downloader/data/model/LocalFileDetails.java: -------------------------------------------------------------------------------- 1 | package com.allsoftdroid.audiobook.feature_downloader.data.model; 2 | 3 | import android.net.Uri; 4 | 5 | import androidx.annotation.NonNull; 6 | 7 | import org.jetbrains.annotations.NotNull; 8 | 9 | public class LocalFileDetails { 10 | 11 | @NonNull 12 | private Uri localUri; 13 | 14 | private String mimeType; 15 | 16 | public LocalFileDetails(@NotNull String uri, String mime){ 17 | this.localUri = Uri.parse(uri); 18 | this.mimeType = mime; 19 | } 20 | 21 | @NonNull 22 | public Uri getLocalUri() { 23 | return localUri; 24 | } 25 | 26 | public String getMimeType() { 27 | return mimeType; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 14 | 15 | 20 | 21 | -------------------------------------------------------------------------------- /feature_book_details/src/main/java/com/allsoftdroid/feature/book_details/domain/model/AudioBookTrackDomainModel.kt: -------------------------------------------------------------------------------- 1 | package com.allsoftdroid.feature.book_details.domain.model 2 | 3 | import com.allsoftdroid.common.base.extension.AudioPlayListItem 4 | import com.allsoftdroid.feature.book_details.utils.DownloadStatusEvent 5 | import com.allsoftdroid.feature.book_details.utils.NOTHING 6 | 7 | data class AudioBookTrackDomainModel( 8 | var downloadStatus:DownloadStatusEvent = NOTHING, 9 | var isPlaying:Boolean = false, 10 | override val filename : String, 11 | override val title : String?, 12 | val trackNumber : Int?, 13 | val trackAlbum : String?, 14 | val length: String?, 15 | val format: String?, 16 | val size : String?, 17 | val trackId:String 18 | ) : AudioPlayListItem -------------------------------------------------------------------------------- /database/src/main/java/com/allsoftdroid/database/bookListDB/DatabaseEntities.kt: -------------------------------------------------------------------------------- 1 | package com.allsoftdroid.database.bookListDB 2 | 3 | import androidx.room.ColumnInfo 4 | import androidx.room.Entity 5 | import androidx.room.PrimaryKey 6 | 7 | /** 8 | * Data Structure for Book instance in Database 9 | */ 10 | 11 | @Entity(tableName = "AudioBook_Table") 12 | data class DatabaseAudioBook( 13 | 14 | @PrimaryKey 15 | @ColumnInfo(name = "id") 16 | var identifier: String, 17 | 18 | @ColumnInfo(name = "book_title") 19 | var title : String, 20 | 21 | var creator : String?, 22 | 23 | @ColumnInfo(name = "published_date") 24 | var date: String?, 25 | 26 | @ColumnInfo(name = "added_date") 27 | var addeddate: String? 28 | ){ 29 | constructor() : this("","","","","") 30 | } -------------------------------------------------------------------------------- /database/src/main/java/com/allsoftdroid/database/metadataCacheDB/entity/DatabaseAlbumEntity.kt: -------------------------------------------------------------------------------- 1 | package com.allsoftdroid.database.metadataCacheDB.entity 2 | 3 | import androidx.room.ColumnInfo 4 | import androidx.room.Entity 5 | import androidx.room.ForeignKey 6 | import androidx.room.PrimaryKey 7 | 8 | @Entity(tableName = "Album_Table") 9 | data class DatabaseAlbumEntity( 10 | 11 | @ForeignKey( 12 | entity = DatabaseMetadataEntity::class, 13 | parentColumns = ["metadata_id"], 14 | childColumns = ["album_metadata_id"], 15 | onDelete = ForeignKey.RESTRICT) 16 | @ColumnInfo(name = "album_metadata_id") 17 | var identifier : String, 18 | 19 | @PrimaryKey 20 | @ColumnInfo(name = "album_name") 21 | var albumName:String, 22 | 23 | var creator : String 24 | ) -------------------------------------------------------------------------------- /feature_mybooks/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | My Books 4 | navigate to previous screen 5 | No Books found in Storage 6 | book thumbnail image 7 | 8 | Remove All Chapters 9 | Delete Book 10 | Number of chapters available locally 11 | %1d Books in Storage 12 | %d/%d Chapters 13 | -------------------------------------------------------------------------------- /feature_book_details/src/main/java/com/allsoftdroid/feature/book_details/utils/ViewLoadingAnimation.kt: -------------------------------------------------------------------------------- 1 | package com.allsoftdroid.feature.book_details.utils 2 | 3 | import android.animation.ObjectAnimator 4 | import android.graphics.Color 5 | import android.view.View 6 | 7 | class ViewLoadingAnimation(private val view: View) { 8 | 9 | private lateinit var animator:ObjectAnimator 10 | 11 | fun colorize(){ 12 | animator = ObjectAnimator.ofArgb(view,"backgroundColor",Color.GRAY,Color.LTGRAY) 13 | animator.duration = 500 14 | animator.repeatCount = ObjectAnimator.INFINITE 15 | animator.repeatMode = ObjectAnimator.REVERSE 16 | animator.start() 17 | } 18 | 19 | fun stop(){ 20 | animator.cancel() 21 | view.setBackgroundColor(Color.TRANSPARENT) 22 | } 23 | } -------------------------------------------------------------------------------- /common/src/main/java/com/allsoftdroid/common/base/extension/Event.kt: -------------------------------------------------------------------------------- 1 | package com.allsoftdroid.common.base.extension 2 | 3 | /** 4 | * Used as a wrapper for data that is exposed via a LiveData that represents an event. 5 | */ 6 | open class Event(private val content: T) { 7 | 8 | var hasBeenHandled = false 9 | private set // Allow external read but not write 10 | 11 | /** 12 | * Returns the content and prevents its use again. 13 | */ 14 | fun getContentIfNotHandled(): T? { 15 | return if (hasBeenHandled) { 16 | null 17 | } else { 18 | hasBeenHandled = true 19 | content 20 | } 21 | } 22 | 23 | /** 24 | * Returns the content, even if it's already been handled. 25 | */ 26 | fun peekContent(): T = content 27 | } -------------------------------------------------------------------------------- /feature_playerfullscreen/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | Player Full Screen 3 | share book 4 | close player 5 | book cover photo 6 | Play Next 7 | Play previous 8 | play pause control 9 | Book Thumbnail Image 10 | rewind by 30 11 | fast forward by 30 12 | Section %d of %d 13 | Buffering ... 14 | 15 | -------------------------------------------------------------------------------- /app/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 22 | -------------------------------------------------------------------------------- /app/src/androidTest/java/com/allsoftdroid/audiobook/presentation/utils/ScreenCaptureProcessor.kt: -------------------------------------------------------------------------------- 1 | package com.allsoftdroid.audiobook.presentation.utils 2 | 3 | import android.os.Environment.DIRECTORY_PICTURES 4 | import android.os.Environment.getExternalStoragePublicDirectory 5 | import androidx.test.runner.screenshot.BasicScreenCaptureProcessor 6 | import java.io.File 7 | 8 | class ScreenCaptureProcessor(parentFolderPath: String) : BasicScreenCaptureProcessor() { 9 | 10 | init { 11 | this.mDefaultScreenshotPath = File( 12 | File( 13 | getExternalStoragePublicDirectory(DIRECTORY_PICTURES), 14 | "audioBook_test_folder" 15 | ).absolutePath, 16 | "screenshots/$parentFolderPath" 17 | ) 18 | } 19 | 20 | override fun getFilename(prefix: String): String = prefix 21 | } -------------------------------------------------------------------------------- /common/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 22 | -------------------------------------------------------------------------------- /common/src/main/java/com/allsoftdroid/common/base/usecase/BaseUseCase.kt: -------------------------------------------------------------------------------- 1 | package com.allsoftdroid.common.base.usecase 2 | 3 | abstract class BaseUseCase { 4 | 5 | var requestValues : Q? = null 6 | var useCaseCallback : UseCaseCallback

? = null 7 | 8 | 9 | internal suspend fun run(){ 10 | executeUseCase(requestValues) 11 | } 12 | 13 | protected abstract suspend fun executeUseCase(requestValues : Q?) 14 | 15 | /** 16 | * Data passed to a request 17 | */ 18 | interface RequestValues 19 | 20 | /** 21 | * Data received from a request 22 | */ 23 | interface ResponseValues 24 | 25 | 26 | interface UseCaseCallback{ 27 | suspend fun onSuccess(response : R) 28 | suspend fun onError(t : Throwable) 29 | } 30 | } -------------------------------------------------------------------------------- /database/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 22 | -------------------------------------------------------------------------------- /services/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 22 | -------------------------------------------------------------------------------- /database/src/main/java/com/allsoftdroid/database/metadataCacheDB/entity/DatabaseMetadataEntity.kt: -------------------------------------------------------------------------------- 1 | package com.allsoftdroid.database.metadataCacheDB.entity 2 | 3 | import androidx.room.ColumnInfo 4 | import androidx.room.Entity 5 | import androidx.room.PrimaryKey 6 | 7 | 8 | @Entity(tableName = "Metadata_Table") 9 | data class DatabaseMetadataEntity( 10 | 11 | @PrimaryKey 12 | @ColumnInfo(name = "metadata_id") 13 | var identifier : String, 14 | 15 | @ColumnInfo(name = "uploader") 16 | var creator : String, 17 | 18 | @ColumnInfo(name = "upload_date") 19 | var date : String, 20 | 21 | var description : String, 22 | 23 | var licenseUrl : String, 24 | 25 | @ColumnInfo(name = "category") 26 | var tag : String, 27 | 28 | var title : String, 29 | 30 | var release_year : String, 31 | 32 | var runtime: String 33 | ) -------------------------------------------------------------------------------- /feature_mybooks/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 22 | -------------------------------------------------------------------------------- /feature_settings/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 22 | -------------------------------------------------------------------------------- /common/src/main/java/com/allsoftdroid/common/base/fragment/BaseUIFragment.kt: -------------------------------------------------------------------------------- 1 | package com.allsoftdroid.common.base.fragment 2 | 3 | import android.os.Bundle 4 | import androidx.activity.OnBackPressedCallback 5 | import androidx.activity.addCallback 6 | 7 | abstract class BaseUIFragment : BaseContainerFragment() { 8 | 9 | private lateinit var callback: OnBackPressedCallback 10 | 11 | override fun onCreate(savedInstanceState: Bundle?) { 12 | super.onCreate(savedInstanceState) 13 | 14 | callback = requireActivity().onBackPressedDispatcher.addCallback(this){ 15 | handleBackPressEvent(callback) 16 | } 17 | 18 | callback.isEnabled = true 19 | } 20 | 21 | abstract fun handleBackPressEvent(callback: OnBackPressedCallback) 22 | 23 | fun onBackPressed(){ 24 | handleBackPressEvent(callback) 25 | } 26 | } -------------------------------------------------------------------------------- /feature_mini_player/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 22 | -------------------------------------------------------------------------------- /feature_listen_later_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 22 | -------------------------------------------------------------------------------- /feature_playerfullscreen/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 22 | -------------------------------------------------------------------------------- /feature_audiobook_enhance_details/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 22 | -------------------------------------------------------------------------------- /common/src/main/java/com/allsoftdroid/common/base/extension/ContextExtension.kt: -------------------------------------------------------------------------------- 1 | package com.allsoftdroid.common.base.extension 2 | 3 | import android.content.Context 4 | import android.content.Intent 5 | import android.widget.Toast 6 | import androidx.annotation.StringRes 7 | import androidx.appcompat.app.AppCompatActivity 8 | import androidx.core.os.bundleOf 9 | import kotlin.reflect.KProperty1 10 | 11 | fun Context.toast(@StringRes resId: Int, length: Int = Toast.LENGTH_SHORT) { 12 | Toast.makeText(this, getString(resId), length).show() 13 | } 14 | 15 | inline fun Context.startActivity( 16 | vararg params: Pair, Any?> 17 | ) { 18 | val extras = params.map { it.first.name to it.second }.toTypedArray() 19 | val intent = Intent(this, T::class.java) 20 | intent.putExtras(bundleOf(*extras)) 21 | startActivity(intent) 22 | } -------------------------------------------------------------------------------- /common/src/main/java/com/allsoftdroid/common/test/EspressoIdlingResource.kt: -------------------------------------------------------------------------------- 1 | package com.allsoftdroid.common.test 2 | 3 | import androidx.test.espresso.idling.CountingIdlingResource 4 | 5 | object EspressoIdlingResource { 6 | 7 | private const val RESOURCE = "GLOBAL" 8 | 9 | @JvmField 10 | val countingIdlingResource = CountingIdlingResource(RESOURCE) 11 | 12 | fun increment() { 13 | countingIdlingResource.increment() 14 | } 15 | 16 | fun decrement() { 17 | if (!countingIdlingResource.isIdleNow) { 18 | countingIdlingResource.decrement() 19 | } 20 | } 21 | } 22 | 23 | inline fun wrapEspressoIdlingResource(function: () -> T): T { 24 | EspressoIdlingResource.increment() // Set app as busy. 25 | return try { 26 | function() 27 | } finally { 28 | EspressoIdlingResource.decrement() // Set app as idle. 29 | } 30 | } -------------------------------------------------------------------------------- /app/src/androidTest/java/com/allsoftdroid/audiobook/presentation/utils/TakeScreenshotUtils.kt: -------------------------------------------------------------------------------- 1 | package com.allsoftdroid.audiobook.presentation.utils 2 | 3 | import androidx.test.runner.screenshot.Screenshot 4 | import timber.log.Timber 5 | import java.io.IOException 6 | 7 | object TakeScreenshotUtils { 8 | fun takeScreenshot(parentFolderPath: String = "", screenShotName: String) { 9 | Timber.d("Taking screenshot of '$screenShotName'") 10 | val screenCapture = Screenshot.capture() 11 | val processors = setOf(ScreenCaptureProcessor(parentFolderPath)) 12 | try { 13 | screenCapture.apply { 14 | name = screenShotName 15 | process(processors) 16 | } 17 | Timber.d("Screenshot taken") 18 | } catch (ex: IOException) { 19 | Timber.d("Could not take the screenshot: $ex") 20 | } 21 | } 22 | } -------------------------------------------------------------------------------- /app/src/main/java/com/allsoftdroid/audiobook/utility/MoveUpBehavior.kt: -------------------------------------------------------------------------------- 1 | package com.allsoftdroid.audiobook.utility 2 | 3 | import android.view.View 4 | import androidx.coordinatorlayout.widget.CoordinatorLayout 5 | import com.google.android.material.snackbar.Snackbar.SnackbarLayout 6 | 7 | 8 | class MoveUpBehavior:CoordinatorLayout.Behavior() { 9 | 10 | override fun layoutDependsOn( 11 | parent: CoordinatorLayout, 12 | child: View, 13 | dependency: View 14 | ): Boolean { 15 | return dependency is SnackbarLayout 16 | } 17 | 18 | override fun onDependentViewChanged( 19 | parent: CoordinatorLayout, 20 | child: View, 21 | dependency: View 22 | ): Boolean { 23 | val translationY = 24 | Math.min(0f, dependency.translationY - dependency.height) 25 | child.translationY = translationY+1 26 | return true 27 | } 28 | } -------------------------------------------------------------------------------- /.github/workflows/Create-Release-CI.yml: -------------------------------------------------------------------------------- 1 | name: "Release Deploy CI" 2 | 3 | on: 4 | push: 5 | branches: # array of glob patterns matching against refs/heads. Optional; defaults to all 6 | - release # triggers on pushes that contain changes in release 7 | 8 | jobs: 9 | build: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - uses: actions/checkout@v1 13 | - name: Create Release Folder 14 | run: rsync -arv --exclude='.git/' --exclude='.github/' --exclude='.idea/' --exclude='.gitignore' . ./release 15 | - name: Switch to Release folder 16 | run: | 17 | cd release 18 | ls -la 19 | - name: Bump Version and Push tag 20 | uses: anothrNick/github-tag-action@master 21 | env: 22 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 23 | REPO_OWNER: pravinyo 24 | WITH_V: true 25 | RELEASE_BRANCHES: release -------------------------------------------------------------------------------- /database/src/main/java/com/allsoftdroid/database/networkCacheDB/NetworkCacheDao.kt: -------------------------------------------------------------------------------- 1 | package com.allsoftdroid.database.networkCacheDB 2 | 3 | import androidx.room.Dao 4 | import androidx.room.Insert 5 | import androidx.room.OnConflictStrategy 6 | import androidx.room.Query 7 | import kotlinx.coroutines.flow.Flow 8 | 9 | 10 | @Dao 11 | interface NetworkCacheDao { 12 | @Query("SELECT response FROM Network_Cache_Table where networkRequestId=:networkRequestId") 13 | fun getNetworkResponse(networkRequestId: String): Flow 14 | 15 | @Insert(onConflict = OnConflictStrategy.REPLACE) 16 | suspend fun insertResponse(networkResponseEntity: DatabaseNetworkResponseEntity) 17 | 18 | @Query("DELETE FROM Network_Cache_Table WHERE networkRequestId=:networkRequestId") 19 | suspend fun removeResponse(networkRequestId: String) 20 | 21 | @Query("DELETE FROM Network_Cache_Table") 22 | suspend fun removeAll() 23 | } -------------------------------------------------------------------------------- /feature_book_details/src/test/java/com/allsoftdroid/feature/book_details/utils/FakeListenLaterRepository.kt: -------------------------------------------------------------------------------- 1 | package com.allsoftdroid.feature.book_details.utils 2 | 3 | import com.allsoftdroid.feature.book_details.data.model.ListenLaterDomainModel 4 | import com.allsoftdroid.feature.book_details.domain.repository.IListenLaterRepository 5 | 6 | internal class FakeListenLaterRepository : IListenLaterRepository { 7 | 8 | var listenLater:ListenLaterDomainModel?=null 9 | 10 | override suspend fun isAddedToListenLater(bookId: String): Boolean { 11 | if(listenLater!=null){ 12 | return listenLater!!.bookId == bookId 13 | } 14 | return false 15 | } 16 | 17 | override suspend fun addToListenLater(listenLater: ListenLaterDomainModel) { 18 | this.listenLater = listenLater 19 | } 20 | 21 | override suspend fun removeListenLater(bookId: String) { 22 | this.listenLater = null 23 | } 24 | } -------------------------------------------------------------------------------- /feature_book_details/src/main/java/com/allsoftdroid/feature/book_details/domain/repository/BookDetailsSharedPreferenceRepository.kt: -------------------------------------------------------------------------------- 1 | package com.allsoftdroid.feature.book_details.domain.repository 2 | 3 | /** 4 | * Interface for sharePreference for saving current state of the track Playing 5 | */ 6 | 7 | interface BookDetailsSharedPreferenceRepository { 8 | 9 | fun saveTrackPosition(pos : Int) 10 | fun trackPosition():Int 11 | 12 | fun saveTrackTitle(title : String) 13 | fun trackTitle():String 14 | 15 | fun saveBookId(bookId:String) 16 | fun bookId():String 17 | 18 | fun saveBookName(name:String) 19 | fun bookName():String 20 | 21 | fun saveTrackFormatIndex(formatIndex:Int) 22 | fun trackFormatIndex():Int 23 | 24 | fun clear() 25 | 26 | fun saveIsPlaying(isPlaying: Boolean) 27 | fun isPlaying(): Boolean 28 | 29 | fun isToolTipShown():Boolean 30 | fun setToolTipShown(shouldSkip:Boolean) 31 | } -------------------------------------------------------------------------------- /feature_downloader/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | multiple download 4 | happy icons 5 | No Downloads… 6 | clear all downloaded 7 | file icon 8 | waiting… 9 | cancel download 10 | delete 11 | DELETE 12 | CANCEL 13 | Downloading… 14 | Don\'t Show this 15 | Download Status 16 | -------------------------------------------------------------------------------- /feature_listen_later_ui/src/main/java/com/allsoftdroid/audiobook/feature_listen_later_ui/data/model/BookMarkDataItem.kt: -------------------------------------------------------------------------------- 1 | package com.allsoftdroid.audiobook.feature_listen_later_ui.data.model 2 | 3 | import com.allsoftdroid.database.listenLaterDB.entity.DatabaseListenLaterEntity 4 | 5 | data class BookMarkDataItem( 6 | val bookId:String, 7 | val bookName:String, 8 | val bookAuthor:String, 9 | val duration:String?, 10 | val timeStamp:String 11 | ) 12 | 13 | fun DatabaseListenLaterEntity.toExternalModel() = BookMarkDataItem( 14 | bookId = this.identifier, 15 | bookName = this.title, 16 | bookAuthor = this.author, 17 | duration = this.duration, 18 | timeStamp = this.timeStamp 19 | ) 20 | 21 | fun BookMarkDataItem.toDatabaseModel() = DatabaseListenLaterEntity( 22 | identifier = this.bookId, 23 | title = this.bookName, 24 | author = this.bookAuthor, 25 | duration = this.duration?:"", 26 | timeStamp = this.timeStamp 27 | ) -------------------------------------------------------------------------------- /feature_book_details/src/main/res/drawable/ic_information_variant.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 12 | -------------------------------------------------------------------------------- /feature_listen_later_ui/src/main/java/com/allsoftdroid/audiobook/feature_listen_later_ui/domain/contracts/ImportFileContract.kt: -------------------------------------------------------------------------------- 1 | package com.allsoftdroid.audiobook.feature_listen_later_ui.domain.contracts 2 | 3 | import android.app.Activity 4 | import android.content.Context 5 | import android.content.Intent 6 | import android.net.Uri 7 | import androidx.activity.result.contract.ActivityResultContract 8 | 9 | class ImportFileContract : ActivityResultContract() { 10 | override fun createIntent(context: Context, input: Int?): Intent { 11 | val intent = Intent(Intent.ACTION_OPEN_DOCUMENT) 12 | intent.addCategory(Intent.CATEGORY_OPENABLE) 13 | intent.type = "text/plain" 14 | return intent 15 | } 16 | 17 | override fun parseResult(resultCode: Int, intent: Intent?): Uri? = when{ 18 | resultCode != Activity.RESULT_OK -> null // Return null, if action is cancelled 19 | else -> intent?.data // Return the data 20 | } 21 | } -------------------------------------------------------------------------------- /feature_book/src/main/res/drawable/ic_gauge.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | -------------------------------------------------------------------------------- /feature_listen_later_ui/src/main/java/com/allsoftdroid/audiobook/feature_listen_later_ui/domain/repository/IExportUserDataRepository.kt: -------------------------------------------------------------------------------- 1 | package com.allsoftdroid.audiobook.feature_listen_later_ui.domain.repository 2 | 3 | import android.os.ParcelFileDescriptor 4 | import com.allsoftdroid.audiobook.feature_listen_later_ui.data.model.BookMarkDataItem 5 | 6 | /** 7 | * Interface for saving user data to specified path 8 | */ 9 | interface IExportUserDataRepository { 10 | /** 11 | * This method allow data to be saved at path. on separate thread 12 | * @param path to where this content be saved 13 | * @param data which need to be saved 14 | */ 15 | suspend fun toFile(path:String,data: List) 16 | 17 | /** 18 | * This method allow data to be saved at path. on separate thread 19 | * @param pfd to where this content be saved 20 | * @param data which need to be saved 21 | */ 22 | suspend fun toFile(pfd: ParcelFileDescriptor, data: List) 23 | } -------------------------------------------------------------------------------- /feature_listen_later_ui/src/main/java/com/allsoftdroid/audiobook/feature_listen_later_ui/domain/repository/IImportUserDataRepository.kt: -------------------------------------------------------------------------------- 1 | package com.allsoftdroid.audiobook.feature_listen_later_ui.domain.repository 2 | 3 | import android.os.ParcelFileDescriptor 4 | import com.allsoftdroid.audiobook.feature_listen_later_ui.data.model.BookMarkDataItem 5 | 6 | /** 7 | * Interface for importing user content to App Listen later DB 8 | */ 9 | interface IImportUserDataRepository { 10 | /** 11 | * It load the data from the file at Path and returns list of data. 12 | * @param path of the file for data import 13 | * @return list of @[BookMarkDataItem] 14 | */ 15 | suspend fun fromFile(path:String):List 16 | 17 | /** 18 | * It load the data from the file at Path and returns list of data. 19 | * @param pfd of the file for data import 20 | * @return list of @[BookMarkDataItem] 21 | */ 22 | suspend fun fromFile(pfd: ParcelFileDescriptor):List 23 | } -------------------------------------------------------------------------------- /common/src/main/res/drawable/loading_animation.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 19 | 20 | 24 | -------------------------------------------------------------------------------- /feature_book/src/main/java/com/allsoftdroid/feature_book/data/databaseExtension/DatabaseEntities.kt: -------------------------------------------------------------------------------- 1 | package com.allsoftdroid.feature_book.data.databaseExtension 2 | 3 | import com.allsoftdroid.database.bookListDB.DatabaseAudioBook 4 | import com.allsoftdroid.feature_book.domain.model.AudioBookDomainModel 5 | 6 | /** 7 | * Convert database instance into domain model instance 8 | */ 9 | 10 | fun List.asBookDomainModel():List{ 11 | return map { 12 | AudioBookDomainModel( 13 | mId = it.identifier, 14 | title = it.title, 15 | creator = it.creator, 16 | date = it.date, 17 | addeddate = it.addeddate?:"" 18 | ) 19 | } 20 | } 21 | 22 | fun DatabaseAudioBook.toBookDomainModel():AudioBookDomainModel{ 23 | return AudioBookDomainModel( 24 | mId = identifier, 25 | title = title, 26 | creator = creator, 27 | date = date, 28 | addeddate = addeddate?:"" 29 | ) 30 | } 31 | -------------------------------------------------------------------------------- /feature_downloader/src/main/java/com/allsoftdroid/audiobook/feature_downloader/domain/IDownloader.java: -------------------------------------------------------------------------------- 1 | package com.allsoftdroid.audiobook.feature_downloader.domain; 2 | 3 | import android.content.Context; 4 | 5 | import com.allsoftdroid.common.base.store.downloader.Download; 6 | 7 | import java.util.List; 8 | 9 | public interface IDownloader extends IDownloaderCore { 10 | 11 | void updateDownloaded(String mUrl,String mBookId,int mChapterIndex); 12 | void updateProgress(String mUrl,String mBookId,int mChapterIndex,long progress); 13 | 14 | long[] getProgress(long downloadId); 15 | void bulkDownload(List downloads); 16 | 17 | void removeFromDownloadDatabase(long downloadId); 18 | 19 | void LogAllLocalData(); 20 | void clearAllDownloadedEntry(); 21 | 22 | String findURLbyDownloadId(long downloadId); 23 | long getDownloadIdByURL(String url); 24 | 25 | byte getStatusByDownloadId(long downloadId); 26 | 27 | void openDownloadedFile(Context context, long downloadId); 28 | } 29 | -------------------------------------------------------------------------------- /app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 10 | 11 | 16 | 17 | 18 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /feature_book_details/src/main/java/com/allsoftdroid/feature/book_details/domain/usecase/ListenLaterUsecase.kt: -------------------------------------------------------------------------------- 1 | package com.allsoftdroid.feature.book_details.domain.usecase 2 | 3 | import com.allsoftdroid.feature.book_details.data.model.ListenLaterDomainModel 4 | import com.allsoftdroid.feature.book_details.domain.repository.IListenLaterRepository 5 | 6 | internal class ListenLaterUsecase( 7 | private val listenLaterRepository: IListenLaterRepository 8 | ) { 9 | 10 | suspend fun addToListenLater(bookId:String,title:String,author:String,duration:String){ 11 | val listenLaterDomainModel = ListenLaterDomainModel(bookId = bookId, 12 | bookName = title, 13 | bookAuthor = author, 14 | bookDuration = duration) 15 | 16 | listenLaterRepository.addToListenLater(listenLaterDomainModel) 17 | } 18 | 19 | suspend fun isAdded(bookId: String) = listenLaterRepository.isAddedToListenLater(bookId) 20 | 21 | suspend fun remove(bookId: String) = listenLaterRepository.removeListenLater(bookId) 22 | } -------------------------------------------------------------------------------- /.github/workflows/Build-InstrumentationTest.yml: -------------------------------------------------------------------------------- 1 | name: AudioBook InstrumentationTest 2 | 3 | on: 4 | push: 5 | branches: 6 | - 'ci_instrumentation_test' 7 | 8 | jobs: 9 | instrumentationJob: 10 | name: Run Instrumentation Tests 11 | runs-on: ubuntu-18.04 12 | 13 | steps: 14 | - uses: actions/checkout@v1 15 | - name: set up JDK 1.8 16 | uses: actions/setup-java@v1 17 | with: 18 | java-version: 1.8 19 | 20 | - uses: malinskiy/action-android/install-sdk@release/0.0.6 21 | - run: adb devices 22 | - run: echo $ANDROID_HOME 23 | 24 | - uses: malinskiy/action-android/emulator-run-cmd@release/0.0.6 25 | with: 26 | cmd: ./gradlew connectedDebugAndroidTest 27 | api: 25 28 | tag: default 29 | abi: x86 30 | - name: Save logcat output 31 | uses: actions/upload-artifact@master 32 | if: failure() 33 | with: 34 | name: logcat 35 | path: artifacts/logcat.log 36 | -------------------------------------------------------------------------------- /.github/workflows/lock-thread-ci.yml: -------------------------------------------------------------------------------- 1 | name: 'Lock threads Bot' 2 | 3 | on: 4 | schedule: 5 | - cron: '0 0 * * *' 6 | 7 | jobs: 8 | lock: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - uses: dessant/lock-threads@v2 12 | with: 13 | github-token: ${{ github.token }} 14 | issue-lock-inactive-days: '10' 15 | issue-lock-reason: 'resolved' 16 | issue-lock-labels: 'outdated' 17 | issue-lock-comment: > 18 | This issue has been automatically locked since there 19 | has not been any recent activity after it was closed. 20 | Please open a new issue for related bugs. 21 | pr-lock-inactive-days: '10' 22 | issue-exclude-labels: 'bug, help-wanted' 23 | pr-lock-reason: 'resolved' 24 | pr-lock-comment: > 25 | This pull request has been automatically locked since there 26 | has not been any recent activity after it was closed. 27 | Please open a new issue for related bugs. 28 | -------------------------------------------------------------------------------- /database/src/main/java/com/allsoftdroid/database/metadataCacheDB/entity/DatabaseTrackEntity.kt: -------------------------------------------------------------------------------- 1 | package com.allsoftdroid.database.metadataCacheDB.entity 2 | 3 | import androidx.room.ColumnInfo 4 | import androidx.room.Entity 5 | import androidx.room.ForeignKey 6 | import androidx.room.PrimaryKey 7 | 8 | @Entity(tableName = "MediaTrack_Table") 9 | data class DatabaseTrackEntity( 10 | 11 | @PrimaryKey 12 | var track_id : String, 13 | 14 | @ForeignKey( 15 | entity = DatabaseAlbumEntity::class, 16 | parentColumns = ["album_metadata_id"], 17 | childColumns = ["track_album_id"], 18 | onDelete = ForeignKey.RESTRICT) 19 | @ColumnInfo(name = "track_album_id") 20 | var trackAlbum_id : String, 21 | 22 | @ColumnInfo(name = "remote_filename") 23 | val filename : String, 24 | 25 | @ColumnInfo(name = "title") 26 | var trackTitle : String?, 27 | 28 | var trackNumber : Int?, 29 | 30 | var length: String?, 31 | 32 | var format: String?, 33 | 34 | var size : String? 35 | ) -------------------------------------------------------------------------------- /feature_mini_player/src/main/java/com/allsoftdroid/audiobook/feature_mini_player/di/FeatureMiniPlayerModule.kt: -------------------------------------------------------------------------------- 1 | package com.allsoftdroid.audiobook.feature_mini_player.di 2 | 3 | import com.allsoftdroid.audiobook.feature_mini_player.presentation.viewModel.MiniPlayerViewModel 4 | import org.koin.androidx.viewmodel.dsl.viewModel 5 | import org.koin.core.context.loadKoinModules 6 | import org.koin.core.context.unloadKoinModules 7 | import org.koin.core.module.Module 8 | import org.koin.dsl.module 9 | 10 | object FeatureMiniPlayerModule { 11 | fun injectFeature() = loadFeature 12 | 13 | fun unloadModule() = unloadKoinModules(listOf( 14 | miniPlayerViewModelModule 15 | )) 16 | 17 | private val loadFeature by lazy { 18 | loadKoinModules(listOf( 19 | miniPlayerViewModelModule 20 | )) 21 | } 22 | 23 | private val miniPlayerViewModelModule: Module = module{ 24 | viewModel { 25 | MiniPlayerViewModel(eventStore = get(),userActionEventStore = get()) 26 | } 27 | } 28 | } -------------------------------------------------------------------------------- /.github/workflows/Closes-Issue-Checker-Bot.yml: -------------------------------------------------------------------------------- 1 | name: 'Lock threads Bot' 2 | 3 | on: 4 | schedule: 5 | - cron: '0 0 * * *' 6 | 7 | jobs: 8 | lock: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - uses: dessant/lock-threads@v2 12 | with: 13 | github-token: ${{ github.token }} 14 | issue-lock-inactive-days: '10' 15 | issue-lock-reason: 'resolved' 16 | issue-lock-labels: 'outdated' 17 | issue-lock-comment: > 18 | This issue has been automatically locked since there 19 | has not been any recent activity after it was closed. 20 | Please open a new issue for related bugs. 21 | pr-lock-inactive-days: '10' 22 | issue-exclude-labels: 'bug, help-wanted' 23 | pr-lock-reason: 'resolved' 24 | pr-lock-comment: > 25 | This pull request has been automatically locked since there 26 | has not been any recent activity after it was closed. 27 | Please open a new issue for related bugs. 28 | -------------------------------------------------------------------------------- /common/src/main/java/com/allsoftdroid/common/test/MainCoroutineRule.kt: -------------------------------------------------------------------------------- 1 | package com.allsoftdroid.common.test 2 | 3 | import kotlinx.coroutines.Dispatchers 4 | import kotlinx.coroutines.ExperimentalCoroutinesApi 5 | import kotlinx.coroutines.test.TestCoroutineDispatcher 6 | import kotlinx.coroutines.test.TestCoroutineScope 7 | import kotlinx.coroutines.test.resetMain 8 | import kotlinx.coroutines.test.setMain 9 | import org.junit.rules.TestWatcher 10 | import org.junit.runner.Description 11 | 12 | @ExperimentalCoroutinesApi 13 | class MainCoroutineRule(val dispatcher: TestCoroutineDispatcher = TestCoroutineDispatcher()): 14 | TestWatcher(), 15 | TestCoroutineScope by TestCoroutineScope(dispatcher) { 16 | override fun starting(description: Description?) { 17 | super.starting(description) 18 | Dispatchers.setMain(dispatcher) 19 | } 20 | 21 | override fun finished(description: Description?) { 22 | super.finished(description) 23 | cleanupTestCoroutines() 24 | Dispatchers.resetMain() 25 | } 26 | } -------------------------------------------------------------------------------- /feature_listen_later_ui/src/main/java/com/allsoftdroid/audiobook/feature_listen_later_ui/domain/contracts/ExportFileContract.kt: -------------------------------------------------------------------------------- 1 | package com.allsoftdroid.audiobook.feature_listen_later_ui.domain.contracts 2 | 3 | import android.app.Activity 4 | import android.content.Context 5 | import android.content.Intent 6 | import android.net.Uri 7 | import androidx.activity.result.contract.ActivityResultContract 8 | 9 | class ExportFileContract: ActivityResultContract() { 10 | override fun createIntent(context: Context, documentName: String): Intent { 11 | val intent = Intent(Intent.ACTION_CREATE_DOCUMENT) 12 | intent.addCategory(Intent.CATEGORY_OPENABLE) 13 | intent.type = "text/plain" 14 | intent.putExtra(Intent.EXTRA_TITLE, documentName) 15 | return intent 16 | } 17 | 18 | override fun parseResult(resultCode: Int, intent: Intent?): Uri? = when{ 19 | resultCode != Activity.RESULT_OK -> null // Return null, if action is cancelled 20 | else -> intent?.data // Return the data 21 | } 22 | } -------------------------------------------------------------------------------- /common/src/main/java/com/allsoftdroid/common/base/utils/ImageUtils.kt: -------------------------------------------------------------------------------- 1 | package com.allsoftdroid.common.base.utils 2 | 3 | import android.graphics.* 4 | 5 | 6 | class ImageUtils { 7 | 8 | companion object{ 9 | fun getCircleBitmap(bitmap: Bitmap): Bitmap { 10 | val output = Bitmap.createBitmap( 11 | bitmap.width, 12 | bitmap.height, Bitmap.Config.ARGB_8888 13 | ) 14 | val canvas = Canvas(output) 15 | 16 | val color = Color.RED 17 | val paint = Paint() 18 | val rect = Rect(0, 0, bitmap.width, bitmap.height) 19 | val rectF = RectF(rect) 20 | 21 | paint.isAntiAlias = true 22 | canvas.drawARGB(0, 0, 0, 0) 23 | paint.color = color 24 | canvas.drawOval(rectF, paint) 25 | 26 | paint.xfermode = PorterDuffXfermode(PorterDuff.Mode.SRC_IN) 27 | canvas.drawBitmap(bitmap, rect, rect, paint) 28 | 29 | bitmap.recycle() 30 | 31 | return output 32 | } 33 | } 34 | } -------------------------------------------------------------------------------- /common/src/main/res/layout/layout_no_connection.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 15 | 16 | 24 | -------------------------------------------------------------------------------- /feature_book_details/src/main/res/layout/layout_server_error.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 15 | 16 | 24 | -------------------------------------------------------------------------------- /feature_book_details/src/test/java/com/allsoftdroid/feature/book_details/utils/FakeNetworkCacheDao.kt: -------------------------------------------------------------------------------- 1 | package com.allsoftdroid.feature.book_details.utils 2 | 3 | import com.allsoftdroid.database.networkCacheDB.DatabaseNetworkResponseEntity 4 | import com.allsoftdroid.database.networkCacheDB.NetworkCacheDao 5 | import kotlinx.coroutines.flow.Flow 6 | import kotlinx.coroutines.flow.flowOf 7 | 8 | class FakeNetworkCacheDao : NetworkCacheDao { 9 | 10 | private var hashMap = HashMap() 11 | 12 | override fun getNetworkResponse(networkRequestId: String): Flow { 13 | return flowOf(hashMap[networkRequestId]?:"") 14 | } 15 | 16 | override suspend fun insertResponse(networkResponseEntity: DatabaseNetworkResponseEntity) { 17 | hashMap[networkResponseEntity.identifier] = networkResponseEntity.networkResponse 18 | } 19 | 20 | override suspend fun removeResponse(networkRequestId: String) { 21 | hashMap.remove(networkRequestId) 22 | } 23 | 24 | override suspend fun removeAll() { 25 | hashMap.clear() 26 | } 27 | } -------------------------------------------------------------------------------- /.github/workflows/Build-UnitTest.yml: -------------------------------------------------------------------------------- 1 | name: AudioBook Feature UnitTest 2 | 3 | on: 4 | push: 5 | branches: 6 | - '*' 7 | - '!master' 8 | - '!release*' 9 | 10 | jobs: 11 | testJob: 12 | name: Run Unit Tests 13 | runs-on: ubuntu-18.04 14 | 15 | steps: 16 | - uses: actions/checkout@v1 17 | - name: set up JDK 1.8 18 | uses: actions/setup-java@v1 19 | with: 20 | java-version: 1.8 21 | - name: Unit tests 22 | run: bash ./gradlew testDebugUnitTest --stacktrace 23 | 24 | apk: 25 | name: Generate APK 26 | needs: testJob 27 | runs-on: ubuntu-18.04 28 | 29 | steps: 30 | - uses: actions/checkout@v1 31 | - name: set up JDK 1.8 32 | uses: actions/setup-java@v1 33 | with: 34 | java-version: 1.8 35 | - name: Build debug APK 36 | run: bash ./gradlew assembleDebug --stacktrace 37 | - name: Upload APK 38 | uses: actions/upload-artifact@v1 39 | with: 40 | name: app 41 | path: app/build/outputs/apk/debug/app-debug.apk -------------------------------------------------------------------------------- /feature_book_details/src/androidTest/java/com/allsoftdroid/feature/book_details/presentation/utils/FakeNetworkCacheDao.kt: -------------------------------------------------------------------------------- 1 | package com.allsoftdroid.feature.book_details.presentation.utils 2 | 3 | import com.allsoftdroid.database.networkCacheDB.DatabaseNetworkResponseEntity 4 | import com.allsoftdroid.database.networkCacheDB.NetworkCacheDao 5 | import kotlinx.coroutines.flow.Flow 6 | import kotlinx.coroutines.flow.flowOf 7 | 8 | class FakeNetworkCacheDao : NetworkCacheDao { 9 | 10 | private var hashMap = HashMap() 11 | 12 | override fun getNetworkResponse(networkRequestId: String): Flow { 13 | return flowOf(hashMap[networkRequestId]?:"") 14 | } 15 | 16 | override suspend fun insertResponse(networkResponseEntity: DatabaseNetworkResponseEntity) { 17 | hashMap[networkResponseEntity.identifier] = networkResponseEntity.networkResponse 18 | } 19 | 20 | override suspend fun removeResponse(networkRequestId: String) { 21 | hashMap.remove(networkRequestId) 22 | } 23 | 24 | override suspend fun removeAll() { 25 | hashMap.clear() 26 | } 27 | } -------------------------------------------------------------------------------- /app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | AudioBook 3 | Feature Book 4 | Download Feature 5 | Swipe up to open player 6 | Third-party Licenses 7 | Thanks for granting permission 8 | This Feature need Storage Permission 9 | Dismiss 10 | Please connect to internet 11 | Playing 12 | continue %s from where you left, 13 | Chapter : %s 14 | Listen 15 | You are offline 16 | Can\'t navigate to Player 17 | 18 | -------------------------------------------------------------------------------- /feature_book/src/main/java/com/allsoftdroid/feature_book/data/model/AudioBookDataModel.kt: -------------------------------------------------------------------------------- 1 | package com.allsoftdroid.feature_book.data.model 2 | 3 | import com.allsoftdroid.database.bookListDB.DatabaseAudioBook 4 | import com.allsoftdroid.feature_book.domain.model.AudioBookDomainModel 5 | 6 | internal data class AudioBookDataModel( 7 | val identifier: String, 8 | val title: String, 9 | val creator: Any?, 10 | val date: String, 11 | val addeddate:String? 12 | ) 13 | 14 | internal fun AudioBookDataModel.toDomainModel(): AudioBookDomainModel { 15 | 16 | return AudioBookDomainModel( 17 | mId = this.identifier, 18 | title = this.title, 19 | creator = this.creator?.toString()?:"N/A", 20 | date = this.date, 21 | addeddate = this.addeddate 22 | ) 23 | } 24 | 25 | 26 | internal fun AudioBookDataModel.toDatabaseModel(): DatabaseAudioBook { 27 | 28 | return DatabaseAudioBook( 29 | identifier = this.identifier, 30 | title = this.title, 31 | creator = this.creator?.toString()?:"N/A", 32 | date = this.date, 33 | addeddate = this.addeddate 34 | ) 35 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Pravin Tripathi 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /feature_listen_later_ui/src/main/java/com/allsoftdroid/audiobook/feature_listen_later_ui/domain/repository/IListenLaterRepository.kt: -------------------------------------------------------------------------------- 1 | package com.allsoftdroid.audiobook.feature_listen_later_ui.domain.repository 2 | 3 | import com.allsoftdroid.audiobook.feature_listen_later_ui.data.model.ListenLaterItemDomainModel 4 | 5 | interface IListenLaterRepository { 6 | /** 7 | * Remove [ListenLaterItemDomainModel] from listen later DB using identifier 8 | * @param identifier 9 | * Unique id assigned to each book 10 | */ 11 | suspend fun removeBookById(identifier:String) 12 | 13 | /** 14 | * Return list of [ListenLaterItemDomainModel] in last in first out fashion from DB 15 | */ 16 | suspend fun getBooksInLIFO(): List 17 | 18 | /** 19 | * Return list of [ListenLaterItemDomainModel] in fist in first out fashion from DB 20 | */ 21 | suspend fun getBooksInFIFO():List 22 | 23 | /** 24 | * Return list of [ListenLaterItemDomainModel] in short playtime first 25 | */ 26 | suspend fun getBooksInOrderOfLength():List 27 | } -------------------------------------------------------------------------------- /feature_book_details/src/test/java/com/allsoftdroid/feature/book_details/utils/FakeSaveInDatabase.kt: -------------------------------------------------------------------------------- 1 | package com.allsoftdroid.feature.book_details.utils 2 | 3 | import com.allsoftdroid.database.common.SaveInDatabase 4 | import com.allsoftdroid.database.metadataCacheDB.MetadataDao 5 | import com.allsoftdroid.feature.book_details.data.model.AudioBookMetadataDataModel 6 | import timber.log.Timber 7 | 8 | class FakeSaveInDatabase(private val dao : MetadataDao) : SaveInDatabase { 9 | override var mDao: MetadataDao = dao 10 | 11 | private lateinit var mBookList: List 12 | 13 | override fun addData(data: Any): FakeSaveInDatabase { 14 | mBookList = (data as List<*>).filterIsInstance() 15 | 16 | return this 17 | } 18 | 19 | override suspend fun execute() { 20 | 21 | //scan the list and build the new to be inserted in the database 22 | for(element in mBookList){ 23 | val book: AudioBookMetadataDataModel = element 24 | 25 | Timber.i(book.identifier) 26 | Timber.i(book.title) 27 | } 28 | } 29 | } -------------------------------------------------------------------------------- /feature_book_details/src/androidTest/java/com/allsoftdroid/feature/book_details/presentation/utils/FakeSaveInDatabase.kt: -------------------------------------------------------------------------------- 1 | package com.allsoftdroid.feature.book_details.presentation.utils 2 | 3 | import com.allsoftdroid.database.common.SaveInDatabase 4 | import com.allsoftdroid.database.metadataCacheDB.MetadataDao 5 | import com.allsoftdroid.feature.book_details.data.model.AudioBookMetadataDataModel 6 | import timber.log.Timber 7 | 8 | class FakeSaveInDatabase(private val dao : MetadataDao) : SaveInDatabase { 9 | override var mDao: MetadataDao = dao 10 | 11 | private lateinit var mBookList: List 12 | 13 | override fun addData(data: Any): FakeSaveInDatabase { 14 | mBookList = (data as List<*>).filterIsInstance() 15 | 16 | return this 17 | } 18 | 19 | override suspend fun execute() { 20 | 21 | //scan the list and build the new to be inserted in the database 22 | for(element in mBookList){ 23 | val book: AudioBookMetadataDataModel = element 24 | 25 | Timber.i(book.identifier) 26 | Timber.i(book.title) 27 | } 28 | } 29 | } -------------------------------------------------------------------------------- /feature_book_details/src/main/java/com/allsoftdroid/feature/book_details/data/network/service/ArchiveAudioBookMetadataServiceApi.kt: -------------------------------------------------------------------------------- 1 | package com.allsoftdroid.feature.book_details.data.network.service 2 | 3 | import com.allsoftdroid.feature.book_details.data.network.Utils 4 | import retrofit2.Call 5 | import retrofit2.Retrofit 6 | import retrofit2.converter.scalars.ScalarsConverterFactory 7 | import retrofit2.http.GET 8 | import retrofit2.http.Path 9 | 10 | /** 11 | * create retrofit instance 12 | */ 13 | private val retrofit = Retrofit.Builder() 14 | .addConverterFactory(ScalarsConverterFactory.create()) 15 | .baseUrl(Utils.MetaData.getBaseURL()) 16 | .build() 17 | 18 | 19 | /** 20 | * API to allow access of the following content type as a json string 21 | */ 22 | interface ArchiveMetadataService{ 23 | 24 | @GET("${Utils.MetaData.PATH}{book_id}") 25 | fun getMetadata(@Path("book_id") bookId:String): Call 26 | } 27 | 28 | 29 | 30 | /** 31 | * API is accessible via this object 32 | */ 33 | internal object ArchiveMetadataApi{ 34 | val RETROFIT_SERVICE: ArchiveMetadataService by lazy { 35 | retrofit.create(ArchiveMetadataService::class.java) 36 | } 37 | } -------------------------------------------------------------------------------- /feature_downloader/src/main/res/layout/layout_empty_content.xml: -------------------------------------------------------------------------------- 1 | 2 | 10 | 11 | 18 | 19 | 26 | -------------------------------------------------------------------------------- /feature_book_details/src/test/java/com/allsoftdroid/feature/book_details/utils/FakeTrackListRepository.kt: -------------------------------------------------------------------------------- 1 | package com.allsoftdroid.feature.book_details.utils 2 | 3 | import com.allsoftdroid.feature.book_details.data.model.TrackFormat 4 | import com.allsoftdroid.feature.book_details.domain.model.AudioBookTrackDomainModel 5 | import com.allsoftdroid.feature.book_details.domain.repository.ITrackListRepository 6 | import kotlinx.coroutines.flow.Flow 7 | import kotlinx.coroutines.flow.flow 8 | 9 | class FakeTrackListRepository : ITrackListRepository { 10 | 11 | override suspend fun loadTrackListData(format: TrackFormat): Flow> { 12 | val tracks = mutableListOf() 13 | 14 | tracks.add(AudioBookTrackDomainModel(filename = "sample.mp3",title = "sample track",trackNumber = 0,trackAlbum = "Intro", 15 | length = "1",format = "64KB",size = "2MB",trackId = "1")) 16 | 17 | tracks.add(AudioBookTrackDomainModel(filename = "sample2.mp3",title = "sample2 track",trackNumber = 1,trackAlbum = "Intro", 18 | length = "1",format = "64KB",size = "2MB",trackId = "2")) 19 | 20 | return flow { emit(tracks) } 21 | } 22 | } -------------------------------------------------------------------------------- /database/src/main/java/com/allsoftdroid/database/listenLaterDB/ListenLaterDao.kt: -------------------------------------------------------------------------------- 1 | package com.allsoftdroid.database.listenLaterDB 2 | 3 | import androidx.room.Dao 4 | import androidx.room.Insert 5 | import androidx.room.OnConflictStrategy 6 | import androidx.room.Query 7 | import com.allsoftdroid.database.listenLaterDB.entity.DatabaseListenLaterEntity 8 | 9 | @Dao 10 | interface ListenLaterDao { 11 | 12 | @Insert(onConflict = OnConflictStrategy.REPLACE) 13 | fun insertForLater(listenLater : DatabaseListenLaterEntity) 14 | 15 | @Query("DELETE FROM ListenLater_Table where book_id = :identifier") 16 | fun removeById(identifier:String):Int 17 | 18 | @Query("SELECT * FROM ListenLater_Table order by timestamp desc") 19 | fun getBooksInLIFO():List 20 | 21 | @Query("SELECT * FROM ListenLater_Table order by timestamp asc") 22 | fun getBooksInFIFO():List 23 | 24 | @Query("SELECT * FROM ListenLater_Table order by play_time") 25 | fun getBooksInOrderOfLength():List 26 | 27 | @Query("SELECT count(*) FROM ListenLater_Table where book_id = :bookId") 28 | fun getListenLaterStatusFor(bookId:String):Int 29 | } -------------------------------------------------------------------------------- /feature_book_details/src/main/java/com/allsoftdroid/feature/book_details/domain/usecase/GetDownloadUsecase.kt: -------------------------------------------------------------------------------- 1 | package com.allsoftdroid.feature.book_details.domain.usecase 2 | 3 | import com.allsoftdroid.common.base.extension.Event 4 | import com.allsoftdroid.common.base.store.downloader.DownloadEvent 5 | import com.allsoftdroid.common.base.store.downloader.DownloadEventStore 6 | import com.allsoftdroid.common.base.usecase.BaseUseCase 7 | 8 | 9 | class GetDownloadUsecase(private val downloadEventStore: DownloadEventStore) : 10 | BaseUseCase() { 11 | 12 | public override suspend fun executeUseCase(requestValues: RequestValues?) { 13 | //check network connection 14 | requestValues?.let { 15 | downloadEventStore.publish( 16 | Event(it.downloaderAction) 17 | ) 18 | useCaseCallback?.onSuccess(ResponseValues(Event("Sent"))) 19 | }?:useCaseCallback?.onError(Error("Request is null")) 20 | } 21 | 22 | class RequestValues(val downloaderAction: DownloadEvent) : BaseUseCase.RequestValues 23 | class ResponseValues(val event: Event) : BaseUseCase.ResponseValues 24 | } -------------------------------------------------------------------------------- /feature_book/src/main/java/com/allsoftdroid/feature_book/presentation/recyclerView/adapter/PaginationListener.kt: -------------------------------------------------------------------------------- 1 | package com.allsoftdroid.feature_book.presentation.recyclerView.adapter 2 | 3 | import androidx.recyclerview.widget.LinearLayoutManager 4 | import androidx.recyclerview.widget.RecyclerView 5 | 6 | abstract class PaginationListener(private val layoutManager: LinearLayoutManager, 7 | private val pageSize:Int) : RecyclerView.OnScrollListener() { 8 | 9 | 10 | override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) { 11 | super.onScrolled(recyclerView, dx, dy) 12 | 13 | val visibleItemCount = layoutManager.childCount 14 | val totalItemCount = layoutManager.itemCount 15 | val firstVisibleItemPos = layoutManager.findFirstVisibleItemPosition() 16 | 17 | if(!isLoading()){ 18 | if ((visibleItemCount + firstVisibleItemPos) >= totalItemCount 19 | && firstVisibleItemPos >= 0 20 | && totalItemCount >= pageSize) { 21 | loadNext() 22 | } 23 | } 24 | } 25 | 26 | protected abstract fun loadNext() 27 | 28 | abstract fun isLoading(): Boolean 29 | } -------------------------------------------------------------------------------- /feature_book/src/main/res/layout/layout_no_results_found.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 18 | 19 | 27 | -------------------------------------------------------------------------------- /app/google-services.json: -------------------------------------------------------------------------------- 1 | { 2 | "project_info": { 3 | "project_number": "263305683750", 4 | "firebase_url": "https://audiobook-a39db.firebaseio.com", 5 | "project_id": "audiobook-a39db", 6 | "storage_bucket": "audiobook-a39db.appspot.com" 7 | }, 8 | "client": [ 9 | { 10 | "client_info": { 11 | "mobilesdk_app_id": "1:263305683750:android:6118053ea4f7e6be0017e7", 12 | "android_client_info": { 13 | "package_name": "com.allsoftdroid.audiobook" 14 | } 15 | }, 16 | "oauth_client": [ 17 | { 18 | "client_id": "263305683750-mmc79b4dpohl2t7sajpr4nllostmogut.apps.googleusercontent.com", 19 | "client_type": 3 20 | } 21 | ], 22 | "api_key": [ 23 | { 24 | "current_key": "AIzaSyAMxyRsA7D-lu6lmm0b95xLF_XaYGa4I_c" 25 | } 26 | ], 27 | "services": { 28 | "appinvite_service": { 29 | "other_platform_oauth_client": [ 30 | { 31 | "client_id": "263305683750-mmc79b4dpohl2t7sajpr4nllostmogut.apps.googleusercontent.com", 32 | "client_type": 3 33 | } 34 | ] 35 | } 36 | } 37 | } 38 | ], 39 | "configuration_version": "1" 40 | } -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 10 | 11 | 18 | 19 | 26 | -------------------------------------------------------------------------------- /feature_book/src/main/res/layout/nav_header_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 13 | 14 | 20 | 21 | 28 | -------------------------------------------------------------------------------- /feature_listen_later_ui/src/main/res/layout/layout_no_books_found.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 18 | 19 | 28 | -------------------------------------------------------------------------------- /feature_book/src/main/res/drawable/ic_jellyfish.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | -------------------------------------------------------------------------------- /feature_mybooks/src/main/res/layout/layout_no_books_found_local_storage.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 18 | 19 | 28 | -------------------------------------------------------------------------------- /common/src/main/res/drawable/ic_broken_image.xml: -------------------------------------------------------------------------------- 1 | 17 | 18 | 24 | 27 | 28 | -------------------------------------------------------------------------------- /feature_audiobook_enhance_details/src/test/java/com/allsoftdroid/audiobook/feature/feature_audiobook_enhance_details/domain/usecase/FakeBookDetailsRepository.kt: -------------------------------------------------------------------------------- 1 | package com.allsoftdroid.audiobook.feature.feature_audiobook_enhance_details.domain.usecase 2 | 3 | import androidx.lifecycle.LiveData 4 | import androidx.lifecycle.MutableLiveData 5 | import com.allsoftdroid.audiobook.feature.feature_audiobook_enhance_details.data.model.BookDetails 6 | import com.allsoftdroid.audiobook.feature.feature_audiobook_enhance_details.domain.network.NetworkResponseListener 7 | import com.allsoftdroid.audiobook.feature.feature_audiobook_enhance_details.domain.repository.IFetchAdditionBookDetailsRepository 8 | 9 | class FakeBookDetailsRepository: IFetchAdditionBookDetailsRepository { 10 | 11 | private val details = MutableLiveData() 12 | 13 | override suspend fun fetchBookDetails(bookUrl: String) { 14 | details.value = BookDetails( 15 | null,"1",bookUrl,"url","","","", emptyList() 16 | ) 17 | } 18 | 19 | fun getBookDetails(): LiveData { 20 | return details 21 | } 22 | 23 | override fun registerNetworkResponse(listener: NetworkResponseListener) { 24 | 25 | } 26 | 27 | override fun unRegisterNetworkResponse() { 28 | 29 | } 30 | } -------------------------------------------------------------------------------- /feature_book_details/src/test/java/com/allsoftdroid/feature/book_details/utils/FakeRemoteMetadataSource.kt: -------------------------------------------------------------------------------- 1 | package com.allsoftdroid.feature.book_details.utils 2 | 3 | import com.allsoftdroid.feature.book_details.data.network.service.ArchiveMetadataService 4 | import okhttp3.Request 5 | import retrofit2.Call 6 | import retrofit2.Callback 7 | import retrofit2.Response 8 | 9 | class FakeRemoteMetadataSource: ArchiveMetadataService { 10 | override fun getMetadata(bookId: String): Call { 11 | return object : Call{ 12 | override fun enqueue(callback: Callback) { 13 | 14 | } 15 | 16 | override fun isExecuted(): Boolean { 17 | return true 18 | } 19 | 20 | override fun clone(): Call { 21 | return this 22 | } 23 | 24 | override fun isCanceled(): Boolean { 25 | return false 26 | } 27 | 28 | override fun cancel() { 29 | 30 | } 31 | 32 | override fun execute(): Response { 33 | return Response.success("") 34 | } 35 | 36 | override fun request(): Request { 37 | return Request.Builder().build() 38 | } 39 | } 40 | } 41 | } -------------------------------------------------------------------------------- /common/src/main/java/com/allsoftdroid/common/base/store/audioPlayer/AudioPlayerEvent.kt: -------------------------------------------------------------------------------- 1 | package com.allsoftdroid.common.base.store.audioPlayer 2 | 3 | import com.allsoftdroid.common.base.extension.AudioPlayListItem 4 | import com.allsoftdroid.common.base.extension.AudioPlayerEventState 5 | 6 | sealed class AudioPlayerEvent 7 | 8 | //Action Event for the Audio Player 9 | data class Next(val result:AudioPlayerEventState) : AudioPlayerEvent() 10 | data class Previous(val result: AudioPlayerEventState) : AudioPlayerEvent() 11 | data class Play(val result: AudioPlayerEventState) : AudioPlayerEvent() 12 | data class Pause(val result: AudioPlayerEventState) : AudioPlayerEvent() 13 | data class EmptyEvent(val default: AudioPlayerEventState):AudioPlayerEvent() 14 | object Rewind : AudioPlayerEvent() 15 | object Forward : AudioPlayerEvent() 16 | object Buffering : AudioPlayerEvent() 17 | object Finished : AudioPlayerEvent() 18 | 19 | //Details or information event for the player and UI 20 | data class PlaySelectedTrack(val trackList : List,val bookId:String,val bookName:String, val position:Int) : AudioPlayerEvent() 21 | data class TrackDetails(val trackTitle:String,val bookId: String, val position: Int):AudioPlayerEvent() 22 | 23 | //Player State event 24 | data class AudioPlayerPlayingState(val isReady:Boolean) : AudioPlayerEvent() -------------------------------------------------------------------------------- /feature_audiobook_enhance_details/src/main/java/com/allsoftdroid/audiobook/feature/feature_audiobook_enhance_details/data/network/Utils.kt: -------------------------------------------------------------------------------- 1 | package com.allsoftdroid.audiobook.feature.feature_audiobook_enhance_details.data.network 2 | 3 | class Utils{ 4 | object Books{ 5 | private const val BASE_URL = "https://librivox.org/" 6 | fun getBaseURL() = BASE_URL 7 | 8 | const val ADVANCED_SEARCH="/advanced_search" 9 | 10 | //Param 11 | const val QUERY_TITLE = "title" 12 | const val QUERY_AUTHOR = "author" 13 | const val QUERY_READER = "reader" 14 | const val QUERY_KEYWORDS = "keywords" 15 | const val QUERY_GENRE_ID = "genre_id" 16 | const val QUERY_STATUS = "status" 17 | const val QUERY_PROJECT_TYPE = "project_type" 18 | const val QUERY_RECORDED_LANGUAGE = "recorded_language" 19 | const val QUERY_SORT_ORDER = "sort_order" 20 | const val QUERY_SEARCH_PAGE = "search_page" 21 | const val QUERY_SEARCH_FORM= "search_form" 22 | const val QUERY_Q="q" 23 | 24 | const val DEFAULT_SORT_ORDER = "catalog_date" 25 | const val DEFAULT_PROJECT_TYPE = "either" 26 | const val DEFAULT_STATUS = "all" 27 | const val DEFAULT_SEARCH_FORM = "advanced" 28 | const val DEFAULT_GENRE_ID = 0 29 | } 30 | } -------------------------------------------------------------------------------- /feature_book/src/main/java/com/allsoftdroid/feature_book/data/network/Utils.kt: -------------------------------------------------------------------------------- 1 | package com.allsoftdroid.feature_book.data.network 2 | 3 | class Utils { 4 | object Books{ 5 | private const val BASE_URL = "https://archive.org/" 6 | private const val FILTER_MOST_RECENT = "+AND+mediatype%3A(audio)&fl[]=creator,date,identifier,title,addeddate&sort[]=-date&output=json" 7 | private const val QUERY="librivox" 8 | private const val COLLECTIONS="librivoxaudio" 9 | private const val OUTPUT_FIELDS = "fl[]=creator,date,identifier,title&sort[]=-date&output=json" 10 | 11 | const val QUERY_PAGE = "page" 12 | const val QUERY_ROW = "rows" 13 | const val QUERY_SEARCH="q" 14 | 15 | const val DEFAULT_ROW_COUNT = 50 16 | fun getBaseURL() = BASE_URL 17 | 18 | const val BOOKS_COLLECTION_PATH = "/advancedsearch.php?q=($QUERY)+AND+collection%3A($COLLECTIONS)$FILTER_MOST_RECENT" 19 | 20 | const val BOOKS_SEARCH_PATH = "/advancedsearch.php?collection%3A($COLLECTIONS)$FILTER_MOST_RECENT" 21 | 22 | const val LIBREVOX_REPOSITORY_SEARCH = "advancedsearch.php?fl[]=creator,date,identifier,title&sort[]=-date&output=json" 23 | 24 | fun buildQuery(search:String):String = 25 | "($search) AND collection:($COLLECTIONS) AND mediatype:(audio)" 26 | 27 | } 28 | } -------------------------------------------------------------------------------- /feature_book_details/src/androidTest/java/com/allsoftdroid/feature/book_details/presentation/utils/FakeRemoteMetadataSource.kt: -------------------------------------------------------------------------------- 1 | package com.allsoftdroid.feature.book_details.presentation.utils 2 | 3 | import com.allsoftdroid.feature.book_details.data.network.service.ArchiveMetadataService 4 | import okhttp3.Request 5 | import retrofit2.Call 6 | import retrofit2.Callback 7 | import retrofit2.Response 8 | 9 | class FakeRemoteMetadataSource: ArchiveMetadataService { 10 | override fun getMetadata(bookId: String): Call { 11 | return object : Call{ 12 | override fun enqueue(callback: Callback) { 13 | 14 | } 15 | 16 | override fun isExecuted(): Boolean { 17 | return true 18 | } 19 | 20 | override fun clone(): Call { 21 | return this 22 | } 23 | 24 | override fun isCanceled(): Boolean { 25 | return false 26 | } 27 | 28 | override fun cancel() { 29 | 30 | } 31 | 32 | override fun execute(): Response { 33 | return Response.success("") 34 | } 35 | 36 | override fun request(): Request { 37 | return Request.Builder().build() 38 | } 39 | } 40 | } 41 | } -------------------------------------------------------------------------------- /common/src/main/java/com/allsoftdroid/common/test/LiveDataTestUtils.kt: -------------------------------------------------------------------------------- 1 | package com.allsoftdroid.common.test 2 | 3 | import androidx.annotation.VisibleForTesting 4 | import androidx.lifecycle.LiveData 5 | import androidx.lifecycle.Observer 6 | import java.util.concurrent.CountDownLatch 7 | import java.util.concurrent.TimeUnit 8 | import java.util.concurrent.TimeoutException 9 | 10 | 11 | @VisibleForTesting(otherwise = VisibleForTesting.NONE) 12 | fun LiveData.getOrAwaitValue( 13 | time: Long = 2, 14 | timeUnit: TimeUnit = TimeUnit.SECONDS, 15 | afterObserve: () -> Unit = {} 16 | ): T { 17 | var data: T? = null 18 | val latch = CountDownLatch(1) 19 | val observer = object : Observer { 20 | override fun onChanged(o: T?) { 21 | data = o 22 | latch.countDown() 23 | this@getOrAwaitValue.removeObserver(this) 24 | } 25 | } 26 | this.observeForever(observer) 27 | 28 | try { 29 | afterObserve.invoke() 30 | 31 | // Don't wait indefinitely if the LiveData is not set. 32 | if (!latch.await(time, timeUnit)) { 33 | throw TimeoutException("LiveData value was never set.") 34 | } 35 | 36 | } finally { 37 | this.removeObserver(observer) 38 | } 39 | 40 | @Suppress("UNCHECKED_CAST") 41 | return data as T 42 | } --------------------------------------------------------------------------------