From 9b883b1a09a8833b43018028720b53144e225627 Mon Sep 17 00:00:00 2001 From: Rani Sargees Date: Mon, 6 Jan 2020 03:26:31 -0500 Subject: [PATCH] androidx migration I DID THIS ONE MYSELF WITHOUT TAKING IT FROM THE OTHER FORKS YEEEEEEEEEEET --- app/build.gradle | 59 +- app/src/main/AndroidManifest.xml | 2 +- app/src/main/java/eu/kanade/tachiyomi/App.kt | 2 +- .../tachiyomi/data/cache/ChapterCache.kt | 11 +- .../tachiyomi/data/database/DatabaseHelper.kt | 2 +- .../tachiyomi/data/database/DbOpenCallback.kt | 4 +- .../resolvers/HistoryLastReadPutResolver.kt | 2 +- .../data/download/DownloadNotifier.kt | 2 +- .../data/download/DownloadService.kt | 2 +- .../tachiyomi/data/download/Downloader.kt | 4 +- .../data/library/LibraryUpdateNotifier.kt | 2 +- .../data/library/LibraryUpdateService.kt | 6 +- .../preference/EmptyPreferenceDataStore.kt | 2 +- .../preference/SharedPreferencesDataStore.kt | 2 +- .../tachiyomi/data/track/TrackService.kt | 140 +-- .../data/track/anilist/AnilistApi.kt | 572 ++++----- .../data/track/bangumi/BangumiApi.kt | 8 +- .../data/track/bangumi/BangumiInterceptor.kt | 10 +- .../data/track/kitsu/KitsuInterceptor.kt | 2 +- .../tachiyomi/data/track/kitsu/KitsuModels.kt | 2 +- .../data/track/myanimelist/MyAnimeList.kt | 327 +++-- .../myanimelist/MyAnimeListInterceptor.kt | 4 +- .../data/track/myanimelist/MyanimelistApi.kt | 20 +- .../data/track/shikimori/ShikimoriApi.kt | 18 +- .../track/shikimori/ShikimoriInterceptor.kt | 2 +- .../tachiyomi/data/updater/UpdaterJob.kt | 2 +- .../tachiyomi/data/updater/UpdaterNotifier.kt | 2 +- .../tachiyomi/data/updater/UpdaterService.kt | 2 +- .../extension/api/ExtensionGithubApi.kt | 2 +- .../tachiyomi/network/AndroidCookieJar.kt | 2 +- .../network/CloudflareInterceptor.kt | 308 ++--- .../kanade/tachiyomi/network/NetworkHelper.kt | 240 ++-- .../tachiyomi/network/OkHttpExtensions.kt | 160 +-- .../tachiyomi/network/ProgressResponseBody.kt | 78 +- .../tachiyomi/source/ConfigurableSource.kt | 2 +- .../tachiyomi/source/online/HttpSource.kt | 792 ++++++------ .../tachiyomi/source/online/all/EHentai.kt | 31 +- .../tachiyomi/source/online/all/Hitomi.kt | 9 +- .../tachiyomi/source/online/all/NHentai.kt | 11 +- .../source/online/english/EightMuses.kt | 12 +- .../source/online/english/HBrowse.kt | 7 +- .../source/online/english/HentaiCafe.kt | 4 +- .../source/online/english/Tsumino.kt | 10 +- .../ui/base/activity/BaseActivity.kt | 2 +- .../ui/base/controller/BaseController.kt | 2 +- .../ui/base/controller/ConductorExtensions.kt | 2 +- .../ui/base/controller/DialogController.java | 5 +- .../ui/base/controller/RxController.kt | 2 +- .../controller/SecondaryDrawerController.kt | 5 +- .../ui/base/controller/TabbedController.kt | 2 +- .../ui/base/holder/BaseViewHolder.kt | 3 +- .../presenter/NucleusConductorDelegate.java | 122 +- .../NucleusConductorLifecycleListener.java | 89 +- .../ui/catalogue/CatalogueController.kt | 5 +- .../kanade/tachiyomi/ui/catalogue/LangItem.kt | 5 +- .../catalogue/SourceDividerItemDecoration.kt | 11 +- .../tachiyomi/ui/catalogue/SourceItem.kt | 5 +- .../browse/BrowseCatalogueController.kt | 45 +- .../ui/catalogue/browse/CatalogueItem.kt | 5 +- .../browse/CatalogueNavigationView.kt | 11 +- .../ui/catalogue/browse/ProgressItem.kt | 5 +- .../ui/catalogue/filter/CheckboxItem.kt | 5 +- .../ui/catalogue/filter/GroupItem.kt | 5 +- .../ui/catalogue/filter/HeaderItem.kt | 7 +- .../ui/catalogue/filter/HelpDialogItem.kt | 5 +- .../ui/catalogue/filter/SelectItem.kt | 5 +- .../ui/catalogue/filter/SeparatorItem.kt | 7 +- .../ui/catalogue/filter/SortGroup.kt | 105 +- .../tachiyomi/ui/catalogue/filter/SortItem.kt | 9 +- .../tachiyomi/ui/catalogue/filter/TextItem.kt | 9 +- .../ui/catalogue/filter/TriStateItem.kt | 9 +- .../global_search/CatalogueSearchAdapter.kt | 9 +- .../global_search/CatalogueSearchCardItem.kt | 71 +- .../CatalogueSearchController.kt | 5 +- .../global_search/CatalogueSearchHolder.kt | 8 +- .../global_search/CatalogueSearchItem.kt | 5 +- .../latest/LatestUpdatesController.kt | 5 +- .../ui/category/CategoryController.kt | 13 +- .../tachiyomi/ui/category/CategoryItem.kt | 5 +- .../ui/download/DownloadController.kt | 519 ++++---- .../tachiyomi/ui/download/DownloadItem.kt | 7 +- .../ui/extension/ExtensionController.kt | 11 +- .../extension/ExtensionDetailsController.kt | 23 +- .../ExtensionDividerItemDecoration.kt | 11 +- .../ui/extension/ExtensionGroupItem.kt | 5 +- .../tachiyomi/ui/extension/ExtensionItem.kt | 5 +- .../ui/library/LibraryCategoryView.kt | 14 +- .../tachiyomi/ui/library/LibraryController.kt | 20 +- .../tachiyomi/ui/library/LibraryItem.kt | 217 ++-- .../kanade/tachiyomi/ui/main/MainActivity.kt | 28 +- .../kanade/tachiyomi/ui/main/TabsAnimator.kt | 2 +- .../tachiyomi/ui/manga/MangaController.kt | 444 +++---- .../tachiyomi/ui/manga/chapter/ChapterItem.kt | 107 +- .../ui/manga/chapter/ChaptersController.kt | 1076 ++++++++--------- .../ui/manga/info/MangaInfoController.kt | 14 +- .../tachiyomi/ui/manga/track/TrackAdapter.kt | 89 +- .../ui/manga/track/TrackController.kt | 281 +++-- .../tachiyomi/ui/migration/MangaItem.kt | 5 +- .../ui/migration/MigrationController.kt | 3 +- .../tachiyomi/ui/migration/SelectionHeader.kt | 7 +- .../tachiyomi/ui/migration/SourceItem.kt | 5 +- .../ui/reader/PageIndicatorTextView.kt | 2 +- .../ui/reader/ReaderColorFilterSheet.kt | 6 +- .../tachiyomi/ui/reader/ReaderPageSheet.kt | 2 +- .../tachiyomi/ui/reader/ReaderSeekBar.kt | 2 +- .../ui/reader/ReaderSettingsSheet.kt | 4 +- .../tachiyomi/ui/reader/SaveImageNotifier.kt | 2 +- .../tachiyomi/ui/reader/loader/PageLoader.kt | 2 +- .../tachiyomi/ui/reader/viewer/pager/Pager.kt | 2 +- .../ui/reader/viewer/pager/PagerButton.kt | 2 +- .../viewer/pager/PagerTransitionHolder.kt | 2 +- .../ui/reader/viewer/pager/PagerViewer.kt | 6 +- .../reader/viewer/pager/PagerViewerAdapter.kt | 3 +- .../reader/viewer/webtoon/WebtoonAdapter.kt | 11 +- .../viewer/webtoon/WebtoonLayoutManager.kt | 12 +- .../viewer/webtoon/WebtoonPageHolder.kt | 4 +- .../viewer/webtoon/WebtoonRecyclerView.kt | 7 +- .../viewer/webtoon/WebtoonTransitionHolder.kt | 4 +- .../ui/reader/viewer/webtoon/WebtoonViewer.kt | 8 +- .../tachiyomi/ui/recent_updates/DateItem.kt | 5 +- .../ui/recent_updates/RecentChapterItem.kt | 5 +- .../RecentChaptersController.kt | 664 +++++----- .../recently_read/RecentlyReadController.kt | 3 +- .../ui/recently_read/RecentlyReadItem.kt | 5 +- .../ui/setting/AnilistLoginActivity.kt | 2 +- .../ui/setting/BangumiLoginActivity.kt | 2 +- .../tachiyomi/ui/setting/PreferenceDSL.kt | 6 +- .../ui/setting/SettingsAboutController.kt | 5 +- .../ui/setting/SettingsAdvancedController.kt | 3 +- .../ui/setting/SettingsBackupController.kt | 2 +- .../ui/setting/SettingsController.kt | 172 +-- .../ui/setting/SettingsDownloadController.kt | 4 +- .../ui/setting/SettingsEhController.kt | 2 +- .../ui/setting/SettingsGeneralController.kt | 2 +- .../ui/setting/SettingsHlController.kt | 8 +- .../ui/setting/SettingsMainController.kt | 156 +-- .../ui/setting/SettingsNhController.kt | 2 +- .../ui/setting/SettingsReaderController.kt | 3 +- .../ui/setting/SettingsSourcesController.kt | 4 +- .../ui/setting/SettingsTrackingController.kt | 12 +- .../ui/setting/ShikomoriLoginActivity.kt | 2 +- .../tachiyomi/util/ContextExtensions.kt | 23 +- .../java/eu/kanade/tachiyomi/util/DiskUtil.kt | 4 +- .../kanade/tachiyomi/util/FileExtensions.kt | 2 +- .../tachiyomi/util/ImageViewExtensions.kt | 4 +- .../kanade/tachiyomi/util/JsoupExtensions.kt | 2 +- .../kanade/tachiyomi/util/OkioExtensions.kt | 5 +- .../kanade/tachiyomi/util/ViewExtensions.kt | 4 +- .../tachiyomi/util/ViewGroupExtensions.kt | 4 +- .../tachiyomi/widget/AutofitRecyclerView.kt | 6 +- .../tachiyomi/widget/CustomLayoutPicker.kt | 3 +- .../tachiyomi/widget/DialogCheckboxView.kt | 2 +- .../widget/DrawerSwipeCloseListener.kt | 10 +- .../tachiyomi/widget/ElevationAppBarLayout.kt | 4 +- .../widget/ExtendedNavigationView.kt | 537 ++++---- .../tachiyomi/widget/FABAnimationBase.kt | 9 +- .../tachiyomi/widget/FABAnimationUpDown.kt | 4 +- .../widget/PreCachingLayoutManager.kt | 6 +- .../tachiyomi/widget/SimpleNavigationView.kt | 20 +- .../tachiyomi/widget/StateImageViewTarget.kt | 2 +- .../tachiyomi/widget/ViewPagerAdapter.kt | 3 +- .../widget/preference/IntListPreference.kt | 2 +- .../preference/LoginCheckBoxPreference.kt | 4 +- .../widget/preference/LoginPreference.kt | 4 +- .../preference/SwitchPreferenceCategory.kt | 6 +- .../java/exh/debug/SettingsDebugController.kt | 2 +- .../main/java/exh/eh/EHentaiUpdateWorker.kt | 9 +- .../java/exh/eh/MemAutoFlushingLookupTable.kt | 7 +- app/src/main/java/exh/hitomi/HitomiNozomi.kt | 8 +- app/src/main/java/exh/log/EHNetworkLogging.kt | 8 +- app/src/main/java/exh/patch/MangaDexLogin.kt | 9 +- .../exh/patch/UniversalCaptchaDetection.kt | 2 +- .../main/java/exh/uconfig/EHConfigurator.kt | 6 +- .../java/exh/ui/base/BaseExhController.kt | 2 +- .../ui/captcha/AutoSolvingWebViewClient.kt | 2 +- .../exh/ui/captcha/BrowserActionActivity.kt | 42 +- .../captcha/HeadersInjectingWebViewClient.kt | 2 +- .../main/java/exh/ui/captcha/WebViewUtil.kt | 2 +- .../java/exh/ui/lock/FingerLockPreference.kt | 4 +- .../main/java/exh/ui/lock/LockPreference.kt | 2 +- .../manga/design/MigrationDesignController.kt | 3 +- .../manga/design/MigrationSourceItem.kt | 5 +- .../manga/process/DeactivatableViewPager.kt | 3 +- .../process/MigrationProcedureAdapter.kt | 7 +- .../java/exh/ui/webview/WebViewActivity.kt | 2 +- app/src/main/java/exh/util/OkHttpUtil.kt | 6 +- .../res/layout-land/manga_info_controller.xml | 18 +- .../layout-land/reader_color_filter_sheet.xml | 10 +- app/src/main/res/layout/activity_lock.xml | 9 +- app/src/main/res/layout/activity_webview.xml | 13 +- .../main/res/layout/catalogue_controller.xml | 4 +- .../catalogue_global_search_controller.xml | 2 +- ...atalogue_global_search_controller_card.xml | 10 +- ...gue_global_search_controller_card_item.xml | 4 +- .../main/res/layout/catalogue_grid_item.xml | 4 +- .../main/res/layout/catalogue_list_item.xml | 4 +- .../res/layout/catalogue_main_controller.xml | 2 +- .../catalogue_main_controller_card_item.xml | 4 +- .../main/res/layout/categories_controller.xml | 4 +- .../main/res/layout/chapters_controller.xml | 14 +- app/src/main/res/layout/chapters_item.xml | 4 +- app/src/main/res/layout/common_view_empty.xml | 2 +- .../main/res/layout/download_controller.xml | 2 +- .../res/layout/download_custom_amount.xml | 8 +- .../main/res/layout/eh_activity_captcha.xml | 10 +- .../main/res/layout/eh_activity_intercept.xml | 12 +- app/src/main/res/layout/eh_activity_login.xml | 14 +- .../main/res/layout/eh_fragment_batch_add.xml | 10 +- app/src/main/res/layout/eh_manga_card.xml | 14 +- .../main/res/layout/eh_migration_design.xml | 18 +- .../main/res/layout/eh_migration_process.xml | 8 +- .../res/layout/eh_migration_process_item.xml | 5 +- app/src/main/res/layout/eh_smart_search.xml | 12 +- .../main/res/layout/extension_card_header.xml | 8 +- .../main/res/layout/extension_card_item.xml | 4 +- .../main/res/layout/extension_controller.xml | 6 +- .../layout/extension_detail_controller.xml | 8 +- app/src/main/res/layout/library_category.xml | 4 +- .../main/res/layout/library_controller.xml | 2 +- .../main/res/layout/library_list_recycler.xml | 2 +- app/src/main/res/layout/main_activity.xml | 10 +- app/src/main/res/layout/manga_controller.xml | 2 +- .../main/res/layout/manga_info_controller.xml | 24 +- .../main/res/layout/migration_controller.xml | 2 +- .../main/res/layout/navigation_view_text.xml | 6 +- app/src/main/res/layout/reader_activity.xml | 6 +- .../main/res/layout/reader_color_filter.xml | 16 +- .../res/layout/reader_color_filter_sheet.xml | 6 +- .../main/res/layout/reader_settings_sheet.xml | 40 +- .../res/layout/recent_chapters_controller.xml | 6 +- .../main/res/layout/recent_chapters_item.xml | 4 +- .../res/layout/recently_read_controller.xml | 4 +- .../main/res/layout/recently_read_item.xml | 4 +- app/src/main/res/layout/track_controller.xml | 6 +- app/src/main/res/layout/track_item.xml | 382 +++--- app/src/main/res/layout/track_search_item.xml | 10 +- app/src/main/res/menu/catalogue_list.xml | 2 +- app/src/main/res/menu/catalogue_main.xml | 2 +- app/src/main/res/menu/catalogue_new_list.xml | 2 +- app/src/main/res/menu/extension_main.xml | 2 +- app/src/main/res/menu/library.xml | 2 +- app/src/main/res/values/themes.xml | 4 +- gradle.properties | 4 +- 243 files changed, 4537 insertions(+), 4604 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index cd22d2ce5..0a3ed5e33 100755 --- a/app/build.gradle +++ b/app/build.gradle @@ -40,7 +40,7 @@ android { applicationId "eu.kanade.tachiyomi.az" minSdkVersion 16 targetSdkVersion 28 - testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" versionCode 8405 versionName "v8.4.5-AZ" @@ -134,22 +134,21 @@ dependencies { implementation 'com.github.inorichi:junrar-android:634c1f5' // Android support library - final support_library_version = '28.0.0' - implementation "com.android.support:support-v4:$support_library_version" - implementation "com.android.support:appcompat-v7:$support_library_version" - implementation "com.android.support:cardview-v7:$support_library_version" - implementation "com.android.support:design:$support_library_version" - implementation "com.android.support:recyclerview-v7:$support_library_version" - implementation "com.android.support:preference-v7:$support_library_version" - implementation "com.android.support:support-annotations:$support_library_version" - implementation "com.android.support:customtabs:$support_library_version" + implementation 'androidx.legacy:legacy-support-v4:1.0.0' + implementation 'androidx.appcompat:appcompat:1.1.0' + implementation 'androidx.cardview:cardview:1.0.0' + implementation 'com.google.android.material:material:1.0.0' + implementation 'androidx.recyclerview:recyclerview:1.1.0' + implementation 'androidx.preference:preference:1.1.0' + implementation 'androidx.annotation:annotation:1.1.0' + implementation 'androidx.browser:browser:1.2.0' - implementation 'com.android.support.constraint:constraint-layout:1.1.3' + implementation 'androidx.constraintlayout:constraintlayout:1.1.3' - implementation 'com.android.support:multidex:1.0.3' + implementation 'androidx.multidex:multidex:2.0.1' // DO NOT UPGRADE TO 17.0, IT REQUIRES ANDROIDX - standardImplementation 'com.google.firebase:firebase-core:16.0.9' + standardImplementation 'com.google.firebase:firebase-core:17.2.1' // ReactiveX implementation 'io.reactivex:rxandroid:1.2.1' @@ -159,11 +158,11 @@ dependencies { implementation 'com.github.pwittchen:reactivenetwork:0.13.0' // Network client - implementation "com.squareup.okhttp3:okhttp:3.12.3" // DO NOT UPGRADE TO 3.13.X+, it requires minSdk 21 - implementation 'com.squareup.okio:okio:1.17.4' // TODO I think we can do 2.x, okhttp is ok with it but is there any other deps that need 1.x? + implementation "com.squareup.okhttp3:okhttp:4.2.1" // DO NOT UPGRADE TO 3.13.X+, it requires minSdk 21 + implementation 'com.squareup.okio:okio:2.4.0' // I think we can do 2.x, okhttp is ok with it but is there any other deps that need 1.x? // REST - final retrofit_version = '2.6.1' + final retrofit_version = '2.6.2' implementation "com.squareup.retrofit2:retrofit:$retrofit_version" implementation "com.squareup.retrofit2:converter-gson:$retrofit_version" implementation "com.squareup.retrofit2:adapter-rxjava:$retrofit_version" @@ -185,16 +184,16 @@ dependencies { // Job scheduling implementation 'com.evernote:android-job:1.2.5' // DO NOT UPGRADE TO 17.0, IT REQUIRES ANDROIDX - implementation 'com.google.android.gms:play-services-gcm:16.1.0' + implementation 'com.google.android.gms:play-services-gcm:17.0.0' // [EXH] Android 7 SSL Workaround - implementation 'com.google.android.gms:play-services-safetynet:16.0.0' + implementation 'com.google.android.gms:play-services-safetynet:17.0.0' // Changelog implementation 'com.github.gabrielemariotti.changeloglib:changelog:2.1.0' // Database - implementation 'android.arch.persistence:db:1.1.1' + implementation 'androidx.sqlite:sqlite:2.0.1' implementation 'com.github.inorichi.storio:storio-common:8be19de@aar' implementation 'com.github.inorichi.storio:storio-sqlite:8be19de@aar' implementation 'io.requery:sqlite-android:3.25.2' @@ -208,13 +207,13 @@ dependencies { implementation "com.github.inorichi.injekt:injekt-core:65b0440" // Image library - final glide_version = '4.9.0' + final glide_version = '4.10.0' implementation "com.github.bumptech.glide:glide:$glide_version" implementation "com.github.bumptech.glide:okhttp3-integration:$glide_version" kapt "com.github.bumptech.glide:compiler:$glide_version" // Transformations - implementation 'jp.wasabeef:glide-transformations:3.1.1' + implementation 'jp.wasabeef:glide-transformations:4.0.0' // Logging implementation 'com.jakewharton.timber:timber:4.7.1' @@ -225,24 +224,24 @@ dependencies { // UI implementation 'com.dmitrymalkovich.android:material-design-dimens:1.4' implementation 'com.github.dmytrodanylyk.android-process-button:library:1.0.4' - implementation 'eu.davidea:flexible-adapter:5.0.6' // Cannot upgrade to 5.1.0 as it uses AndroidX - implementation 'eu.davidea:flexible-adapter-ui:1.0.0-b5' + implementation 'eu.davidea:flexible-adapter:5.1.0' // Cannot upgrade to 5.1.0 as it uses AndroidX + implementation 'eu.davidea:flexible-adapter-ui:1.0.0' implementation 'com.nononsenseapps:filepicker:2.5.2' implementation 'com.github.amulyakhare:TextDrawable:558677e' implementation 'com.afollestad.material-dialogs:core:0.9.6.0' // Cannot upgrade to 2.x, AndroidX and API changes implementation 'me.zhanghai.android.systemuihelper:library:1.0.0' - implementation 'com.nightlynexus.viewstatepageradapter:viewstatepageradapter:1.0.4' + implementation 'com.nightlynexus.viewstatepageradapter:viewstatepageradapter:1.1.0' implementation 'com.github.mthli:Slice:v1.3' implementation 'me.gujun.android.taggroup:library:1.4@aar' - implementation 'com.github.chrisbanes:PhotoView:2.1.4' // Cannot upgrade to 2.2.x+ as it uses AndroidX - implementation 'com.github.inorichi:DirectionalViewPager:3acc51a' + implementation 'com.github.chrisbanes:PhotoView:2.3.0' // Cannot upgrade to 2.2.x+ as it uses AndroidX + implementation 'com.github.carlosesco:DirectionalViewPager:a844dbca0a' // Conductor implementation 'com.bluelinelabs:conductor:2.1.5' implementation("com.bluelinelabs:conductor-support:2.1.5") { exclude group: "com.android.support" } - implementation 'com.github.inorichi:conductor-support-preference:27.0.2' + implementation 'com.github.inorichi:conductor-support-preference:78e2344' // RxBindings final rxbindings_version = '1.0.1' @@ -287,7 +286,7 @@ dependencies { implementation 'com.lvla.android:rxjava2-interop-kt:0.2.1' // Debug network interceptor (EH) - implementation "com.squareup.okhttp3:logging-interceptor:3.12.1" + implementation "com.squareup.okhttp3:logging-interceptor:4.2.1" // Firebase (EH) implementation 'com.crashlytics.sdk.android:crashlytics:2.10.1' @@ -309,7 +308,7 @@ dependencies { // Humanize (EH) implementation 'com.github.mfornos:humanize-slim:1.2.2' - implementation 'com.android.support:gridlayout-v7:28.0.0' + implementation 'androidx.gridlayout:gridlayout:1.0.0' final def markwon_version = '4.1.0' @@ -319,6 +318,8 @@ dependencies { implementation "io.noties.markwon:html:$markwon_version" implementation "io.noties.markwon:image:$markwon_version" implementation "io.noties.markwon:linkify:$markwon_version" + + implementation 'com.google.guava:guava:27.0.1-android' } buildscript { diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 835279355..51afaa1f3 100755 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -111,7 +111,7 @@ android:theme="@android:style/Theme.Translucent.NoTitleBar" /> diff --git a/app/src/main/java/eu/kanade/tachiyomi/App.kt b/app/src/main/java/eu/kanade/tachiyomi/App.kt index bf2e42938..4a2607d5d 100755 --- a/app/src/main/java/eu/kanade/tachiyomi/App.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/App.kt @@ -6,7 +6,7 @@ import android.content.res.Configuration import android.graphics.Color import android.os.Build import android.os.Environment -import android.support.multidex.MultiDex +import androidx.multidex.MultiDex import com.elvishew.xlog.LogConfiguration import com.elvishew.xlog.LogLevel import com.elvishew.xlog.XLog diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/cache/ChapterCache.kt b/app/src/main/java/eu/kanade/tachiyomi/data/cache/ChapterCache.kt index caa81c958..47c25636b 100755 --- a/app/src/main/java/eu/kanade/tachiyomi/data/cache/ChapterCache.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/cache/ChapterCache.kt @@ -12,10 +12,9 @@ import eu.kanade.tachiyomi.source.model.Page import eu.kanade.tachiyomi.util.DiskUtil import eu.kanade.tachiyomi.util.saveTo import okhttp3.Response -import okio.Okio +import okio.buffer +import okio.sink import rx.Observable -import uy.kohesive.injekt.Injekt -import uy.kohesive.injekt.api.get import uy.kohesive.injekt.injectLazy import java.io.File import java.io.IOException @@ -147,7 +146,7 @@ class ChapterCache(private val context: Context) { editor = diskCache.edit(key) ?: return // Write chapter urls to cache. - Okio.buffer(Okio.sink(editor.newOutputStream(0))).use { + editor.newOutputStream(0).sink().buffer().use { it.write(cachedValue.toByteArray()) it.flush() } @@ -207,12 +206,12 @@ class ChapterCache(private val context: Context) { editor = diskCache.edit(key) ?: throw IOException("Unable to edit key") // Get OutputStream and write image with Okio. - response.body()!!.source().saveTo(editor.newOutputStream(0)) + response.body!!.source().saveTo(editor.newOutputStream(0)) diskCache.flush() editor.commit() } finally { - response.body()?.close() + response.body?.close() editor?.abortUnlessCommitted() } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/database/DatabaseHelper.kt b/app/src/main/java/eu/kanade/tachiyomi/data/database/DatabaseHelper.kt index fb3803cfa..812667411 100755 --- a/app/src/main/java/eu/kanade/tachiyomi/data/database/DatabaseHelper.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/database/DatabaseHelper.kt @@ -1,7 +1,7 @@ package eu.kanade.tachiyomi.data.database -import android.arch.persistence.db.SupportSQLiteOpenHelper import android.content.Context +import androidx.sqlite.db.SupportSQLiteOpenHelper import com.pushtorefresh.storio.sqlite.impl.DefaultStorIOSQLite import eu.kanade.tachiyomi.data.database.mappers.* import eu.kanade.tachiyomi.data.database.models.* diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/database/DbOpenCallback.kt b/app/src/main/java/eu/kanade/tachiyomi/data/database/DbOpenCallback.kt index a2c532bbf..3eb00f694 100755 --- a/app/src/main/java/eu/kanade/tachiyomi/data/database/DbOpenCallback.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/database/DbOpenCallback.kt @@ -1,7 +1,7 @@ package eu.kanade.tachiyomi.data.database -import android.arch.persistence.db.SupportSQLiteDatabase -import android.arch.persistence.db.SupportSQLiteOpenHelper +import androidx.sqlite.db.SupportSQLiteDatabase +import androidx.sqlite.db.SupportSQLiteOpenHelper import eu.kanade.tachiyomi.data.database.tables.* import exh.metadata.sql.tables.SearchMetadataTable import exh.metadata.sql.tables.SearchTagTable diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/database/resolvers/HistoryLastReadPutResolver.kt b/app/src/main/java/eu/kanade/tachiyomi/data/database/resolvers/HistoryLastReadPutResolver.kt index b9d583d4c..f1d68c22a 100755 --- a/app/src/main/java/eu/kanade/tachiyomi/data/database/resolvers/HistoryLastReadPutResolver.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/database/resolvers/HistoryLastReadPutResolver.kt @@ -1,7 +1,7 @@ package eu.kanade.tachiyomi.data.database.resolvers import android.content.ContentValues -import android.support.annotation.NonNull +import androidx.annotation.NonNull import com.pushtorefresh.storio.sqlite.StorIOSQLite import com.pushtorefresh.storio.sqlite.operations.put.PutResult import com.pushtorefresh.storio.sqlite.queries.Query diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/download/DownloadNotifier.kt b/app/src/main/java/eu/kanade/tachiyomi/data/download/DownloadNotifier.kt index af9d84c0a..016c93a85 100755 --- a/app/src/main/java/eu/kanade/tachiyomi/data/download/DownloadNotifier.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/download/DownloadNotifier.kt @@ -2,7 +2,7 @@ package eu.kanade.tachiyomi.data.download import android.content.Context import android.graphics.BitmapFactory -import android.support.v4.app.NotificationCompat +import androidx.core.app.NotificationCompat import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.data.download.model.Download import eu.kanade.tachiyomi.data.download.model.DownloadQueue diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/download/DownloadService.kt b/app/src/main/java/eu/kanade/tachiyomi/data/download/DownloadService.kt index 33ef28abb..75676fcb8 100755 --- a/app/src/main/java/eu/kanade/tachiyomi/data/download/DownloadService.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/download/DownloadService.kt @@ -9,7 +9,7 @@ import android.net.NetworkInfo.State.DISCONNECTED import android.os.Build import android.os.IBinder import android.os.PowerManager -import android.support.v4.app.NotificationCompat +import androidx.core.app.NotificationCompat import com.github.pwittchen.reactivenetwork.library.Connectivity import com.github.pwittchen.reactivenetwork.library.ReactiveNetwork import com.jakewharton.rxrelay.BehaviorRelay diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/download/Downloader.kt b/app/src/main/java/eu/kanade/tachiyomi/data/download/Downloader.kt index b5a43e168..e8aa906de 100755 --- a/app/src/main/java/eu/kanade/tachiyomi/data/download/Downloader.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/download/Downloader.kt @@ -362,7 +362,7 @@ class Downloader( .map { response -> val file = tmpDir.createFile("$filename.tmp") try { - response.body()!!.source().saveTo(file.openOutputStream()) + response.body!!.source().saveTo(file.openOutputStream()) val extension = getImageExtension(response, file) file.renameTo("$filename.$extension") } catch (e: Exception) { @@ -394,7 +394,7 @@ class Downloader( */ private fun getImageExtension(response: Response, file: UniFile): String { // Read content type if available. - val mime = response.body()?.contentType()?.let { ct -> "${ct.type()}/${ct.subtype()}" } + val mime = response.body?.contentType()?.let { ct -> "${ct.type}/${ct.subtype}" } // Else guess from the uri. ?: context.contentResolver.getType(file.uri) // Else read magic numbers. diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/library/LibraryUpdateNotifier.kt b/app/src/main/java/eu/kanade/tachiyomi/data/library/LibraryUpdateNotifier.kt index a944d595f..5a7b2ff73 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/library/LibraryUpdateNotifier.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/library/LibraryUpdateNotifier.kt @@ -6,7 +6,7 @@ import android.content.Context import android.content.Intent import android.graphics.BitmapFactory import android.os.Build -import android.support.v4.app.NotificationCompat +import androidx.core.app.NotificationCompat import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.data.database.models.Manga import eu.kanade.tachiyomi.data.notification.Notifications diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/library/LibraryUpdateService.kt b/app/src/main/java/eu/kanade/tachiyomi/data/library/LibraryUpdateService.kt index f835a45da..6b5b8fe66 100755 --- a/app/src/main/java/eu/kanade/tachiyomi/data/library/LibraryUpdateService.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/library/LibraryUpdateService.kt @@ -6,7 +6,7 @@ import android.content.Intent import android.os.Build import android.os.IBinder import android.os.PowerManager -import android.support.v4.app.NotificationCompat +import androidx.core.app.NotificationCompat import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.data.database.DatabaseHelper import eu.kanade.tachiyomi.data.database.models.Category @@ -25,7 +25,9 @@ import eu.kanade.tachiyomi.data.track.TrackManager import eu.kanade.tachiyomi.source.SourceManager import eu.kanade.tachiyomi.source.model.SManga import eu.kanade.tachiyomi.source.online.HttpSource -import eu.kanade.tachiyomi.util.* +import eu.kanade.tachiyomi.util.isServiceRunning +import eu.kanade.tachiyomi.util.notificationManager +import eu.kanade.tachiyomi.util.syncChaptersWithSource import exh.LIBRARY_UPDATE_EXCLUDED_SOURCES import rx.Observable import rx.Subscription diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/preference/EmptyPreferenceDataStore.kt b/app/src/main/java/eu/kanade/tachiyomi/data/preference/EmptyPreferenceDataStore.kt index 10e83b84e..d162cfd65 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/preference/EmptyPreferenceDataStore.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/preference/EmptyPreferenceDataStore.kt @@ -1,6 +1,6 @@ package eu.kanade.tachiyomi.data.preference -import android.support.v7.preference.PreferenceDataStore +import androidx.preference.PreferenceDataStore class EmptyPreferenceDataStore : PreferenceDataStore() { diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/preference/SharedPreferencesDataStore.kt b/app/src/main/java/eu/kanade/tachiyomi/data/preference/SharedPreferencesDataStore.kt index 8d02d1c77..7c3295f21 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/preference/SharedPreferencesDataStore.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/preference/SharedPreferencesDataStore.kt @@ -1,7 +1,7 @@ package eu.kanade.tachiyomi.data.preference import android.content.SharedPreferences -import android.support.v7.preference.PreferenceDataStore +import androidx.preference.PreferenceDataStore class SharedPreferencesDataStore(private val prefs: SharedPreferences) : PreferenceDataStore() { diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/track/TrackService.kt b/app/src/main/java/eu/kanade/tachiyomi/data/track/TrackService.kt index 9397f71e4..a3dc22d2d 100755 --- a/app/src/main/java/eu/kanade/tachiyomi/data/track/TrackService.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/track/TrackService.kt @@ -1,70 +1,70 @@ -package eu.kanade.tachiyomi.data.track - -import android.support.annotation.CallSuper -import android.support.annotation.DrawableRes -import eu.kanade.tachiyomi.data.database.models.Track -import eu.kanade.tachiyomi.data.track.model.TrackSearch -import eu.kanade.tachiyomi.data.preference.PreferencesHelper -import eu.kanade.tachiyomi.network.NetworkHelper -import okhttp3.OkHttpClient -import rx.Completable -import rx.Observable -import uy.kohesive.injekt.injectLazy - -abstract class TrackService(val id: Int) { - - val preferences: PreferencesHelper by injectLazy() - val networkService: NetworkHelper by injectLazy() - - open val client: OkHttpClient - get() = networkService.client - - // Name of the manga sync service to display - abstract val name: String - - @DrawableRes - abstract fun getLogo(): Int - - abstract fun getLogoColor(): Int - - abstract fun getStatusList(): List - - abstract fun getStatus(status: Int): String - - abstract fun getScoreList(): List - - open fun indexToScore(index: Int): Float { - return index.toFloat() - } - - abstract fun displayScore(track: Track): String - - abstract fun add(track: Track): Observable - - abstract fun update(track: Track): Observable - - abstract fun bind(track: Track): Observable - - abstract fun search(query: String): Observable> - - abstract fun refresh(track: Track): Observable - - abstract fun login(username: String, password: String): Completable - - @CallSuper - open fun logout() { - preferences.setTrackCredentials(this, "", "") - } - - open val isLogged: Boolean - get() = !getUsername().isEmpty() && - !getPassword().isEmpty() - - fun getUsername() = preferences.trackUsername(this)!! - - fun getPassword() = preferences.trackPassword(this)!! - - fun saveCredentials(username: String, password: String) { - preferences.setTrackCredentials(this, username, password) - } -} +package eu.kanade.tachiyomi.data.track + +import androidx.annotation.CallSuper +import androidx.annotation.DrawableRes +import eu.kanade.tachiyomi.data.database.models.Track +import eu.kanade.tachiyomi.data.preference.PreferencesHelper +import eu.kanade.tachiyomi.data.track.model.TrackSearch +import eu.kanade.tachiyomi.network.NetworkHelper +import okhttp3.OkHttpClient +import rx.Completable +import rx.Observable +import uy.kohesive.injekt.injectLazy + +abstract class TrackService(val id: Int) { + + val preferences: PreferencesHelper by injectLazy() + val networkService: NetworkHelper by injectLazy() + + open val client: OkHttpClient + get() = networkService.client + + // Name of the manga sync service to display + abstract val name: String + + @DrawableRes + abstract fun getLogo(): Int + + abstract fun getLogoColor(): Int + + abstract fun getStatusList(): List + + abstract fun getStatus(status: Int): String + + abstract fun getScoreList(): List + + open fun indexToScore(index: Int): Float { + return index.toFloat() + } + + abstract fun displayScore(track: Track): String + + abstract fun add(track: Track): Observable + + abstract fun update(track: Track): Observable + + abstract fun bind(track: Track): Observable + + abstract fun search(query: String): Observable> + + abstract fun refresh(track: Track): Observable + + abstract fun login(username: String, password: String): Completable + + @CallSuper + open fun logout() { + preferences.setTrackCredentials(this, "", "") + } + + open val isLogged: Boolean + get() = !getUsername().isEmpty() && + !getPassword().isEmpty() + + fun getUsername() = preferences.trackUsername(this)!! + + fun getPassword() = preferences.trackPassword(this)!! + + fun saveCredentials(username: String, password: String) { + preferences.setTrackCredentials(this, username, password) + } +} diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/track/anilist/AnilistApi.kt b/app/src/main/java/eu/kanade/tachiyomi/data/track/anilist/AnilistApi.kt index 9a09658c6..ae21e2d4c 100755 --- a/app/src/main/java/eu/kanade/tachiyomi/data/track/anilist/AnilistApi.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/track/anilist/AnilistApi.kt @@ -1,286 +1,286 @@ -package eu.kanade.tachiyomi.data.track.anilist - -import android.net.Uri -import com.github.salomonbrys.kotson.* -import com.google.gson.JsonObject -import com.google.gson.JsonParser -import eu.kanade.tachiyomi.data.database.models.Track -import eu.kanade.tachiyomi.data.track.model.TrackSearch -import eu.kanade.tachiyomi.network.asObservableSuccess -import okhttp3.MediaType -import okhttp3.OkHttpClient -import okhttp3.Request -import okhttp3.RequestBody -import rx.Observable -import java.util.Calendar - - -class AnilistApi(val client: OkHttpClient, interceptor: AnilistInterceptor) { - - private val parser = JsonParser() - private val jsonMime = MediaType.parse("application/json; charset=utf-8") - private val authClient = client.newBuilder().addInterceptor(interceptor).build() - - fun addLibManga(track: Track): Observable { - val query = """ - |mutation AddManga(${'$'}mangaId: Int, ${'$'}progress: Int, ${'$'}status: MediaListStatus) { - |SaveMediaListEntry (mediaId: ${'$'}mangaId, progress: ${'$'}progress, status: ${'$'}status) { - | id - | status - |} - |} - |""".trimMargin() - val variables = jsonObject( - "mangaId" to track.media_id, - "progress" to track.last_chapter_read, - "status" to track.toAnilistStatus() - ) - val payload = jsonObject( - "query" to query, - "variables" to variables - ) - val body = RequestBody.create(jsonMime, payload.toString()) - val request = Request.Builder() - .url(apiUrl) - .post(body) - .build() - return authClient.newCall(request) - .asObservableSuccess() - .map { netResponse -> - val responseBody = netResponse.body()?.string().orEmpty() - netResponse.close() - if (responseBody.isEmpty()) { - throw Exception("Null Response") - } - val response = parser.parse(responseBody).obj - track.library_id = response["data"]["SaveMediaListEntry"]["id"].asLong - track - } - } - - fun updateLibManga(track: Track): Observable { - val query = """ - |mutation UpdateManga(${'$'}listId: Int, ${'$'}progress: Int, ${'$'}status: MediaListStatus, ${'$'}score: Int) { - |SaveMediaListEntry (id: ${'$'}listId, progress: ${'$'}progress, status: ${'$'}status, scoreRaw: ${'$'}score) { - |id - |status - |progress - |} - |} - |""".trimMargin() - val variables = jsonObject( - "listId" to track.library_id, - "progress" to track.last_chapter_read, - "status" to track.toAnilistStatus(), - "score" to track.score.toInt() - ) - val payload = jsonObject( - "query" to query, - "variables" to variables - ) - val body = RequestBody.create(jsonMime, payload.toString()) - val request = Request.Builder() - .url(apiUrl) - .post(body) - .build() - return authClient.newCall(request) - .asObservableSuccess() - .map { - track - } - } - - fun search(search: String): Observable> { - val query = """ - |query Search(${'$'}query: String) { - |Page (perPage: 50) { - |media(search: ${'$'}query, type: MANGA, format_not_in: [NOVEL]) { - |id - |title { - |romaji - |} - |coverImage { - |large - |} - |type - |status - |chapters - |description - |startDate { - |year - |month - |day - |} - |} - |} - |} - |""".trimMargin() - val variables = jsonObject( - "query" to search - ) - val payload = jsonObject( - "query" to query, - "variables" to variables - ) - val body = RequestBody.create(jsonMime, payload.toString()) - val request = Request.Builder() - .url(apiUrl) - .post(body) - .build() - return authClient.newCall(request) - .asObservableSuccess() - .map { netResponse -> - val responseBody = netResponse.body()?.string().orEmpty() - if (responseBody.isEmpty()) { - throw Exception("Null Response") - } - val response = parser.parse(responseBody).obj - val data = response["data"]!!.obj - val page = data["Page"].obj - val media = page["media"].array - val entries = media.map { jsonToALManga(it.obj) } - entries.map { it.toTrack() } - } - } - - - fun findLibManga(track: Track, userid: Int): Observable { - val query = """ - |query (${'$'}id: Int!, ${'$'}manga_id: Int!) { - |Page { - |mediaList(userId: ${'$'}id, type: MANGA, mediaId: ${'$'}manga_id) { - |id - |status - |scoreRaw: score(format: POINT_100) - |progress - |media { - |id - |title { - |romaji - |} - |coverImage { - |large - |} - |type - |status - |chapters - |description - |startDate { - |year - |month - |day - |} - |} - |} - |} - |} - |""".trimMargin() - val variables = jsonObject( - "id" to userid, - "manga_id" to track.media_id - ) - val payload = jsonObject( - "query" to query, - "variables" to variables - ) - val body = RequestBody.create(jsonMime, payload.toString()) - val request = Request.Builder() - .url(apiUrl) - .post(body) - .build() - return authClient.newCall(request) - .asObservableSuccess() - .map { netResponse -> - val responseBody = netResponse.body()?.string().orEmpty() - if (responseBody.isEmpty()) { - throw Exception("Null Response") - } - val response = parser.parse(responseBody).obj - val data = response["data"]!!.obj - val page = data["Page"].obj - val media = page["mediaList"].array - val entries = media.map { jsonToALUserManga(it.obj) } - entries.firstOrNull()?.toTrack() - - } - } - - fun getLibManga(track: Track, userid: Int): Observable { - return findLibManga(track, userid) - .map { it ?: throw Exception("Could not find manga") } - } - - fun createOAuth(token: String): OAuth { - return OAuth(token, "Bearer", System.currentTimeMillis() + 31536000000, 31536000000) - } - - fun getCurrentUser(): Observable> { - val query = """ - |query User { - |Viewer { - |id - |mediaListOptions { - |scoreFormat - |} - |} - |} - |""".trimMargin() - val payload = jsonObject( - "query" to query - ) - val body = RequestBody.create(jsonMime, payload.toString()) - val request = Request.Builder() - .url(apiUrl) - .post(body) - .build() - return authClient.newCall(request) - .asObservableSuccess() - .map { netResponse -> - val responseBody = netResponse.body()?.string().orEmpty() - if (responseBody.isEmpty()) { - throw Exception("Null Response") - } - val response = parser.parse(responseBody).obj - val data = response["data"]!!.obj - val viewer = data["Viewer"].obj - Pair(viewer["id"].asInt, viewer["mediaListOptions"]["scoreFormat"].asString) - } - } - - private fun jsonToALManga(struct: JsonObject): ALManga { - val date = try { - val date = Calendar.getInstance() - date.set(struct["startDate"]["year"].nullInt ?: 0, (struct["startDate"]["month"].nullInt ?: 0) - 1, - struct["startDate"]["day"].nullInt ?: 0) - date.timeInMillis - } catch (_: Exception) { - 0L - } - - return ALManga(struct["id"].asInt, struct["title"]["romaji"].asString, struct["coverImage"]["large"].asString, - struct["description"].nullString.orEmpty(), struct["type"].asString, struct["status"].asString, - date, struct["chapters"].nullInt ?: 0) - } - - private fun jsonToALUserManga(struct: JsonObject): ALUserManga { - return ALUserManga(struct["id"].asLong, struct["status"].asString, struct["scoreRaw"].asInt, struct["progress"].asInt, jsonToALManga(struct["media"].obj)) - } - - companion object { - private const val clientId = "385" - private const val clientUrl = "tachiyomi://anilist-auth" - private const val apiUrl = "https://graphql.anilist.co/" - private const val baseUrl = "https://anilist.co/api/v2/" - private const val baseMangaUrl = "https://anilist.co/manga/" - - fun mangaUrl(mediaId: Int): String { - return baseMangaUrl + mediaId - } - - fun authUrl() = Uri.parse("${baseUrl}oauth/authorize").buildUpon() - .appendQueryParameter("client_id", clientId) - .appendQueryParameter("response_type", "token") - .build() - } - -} +package eu.kanade.tachiyomi.data.track.anilist + +import android.net.Uri +import com.github.salomonbrys.kotson.* +import com.google.gson.JsonObject +import com.google.gson.JsonParser +import eu.kanade.tachiyomi.data.database.models.Track +import eu.kanade.tachiyomi.data.track.model.TrackSearch +import eu.kanade.tachiyomi.network.asObservableSuccess +import okhttp3.MediaType.Companion.toMediaTypeOrNull +import okhttp3.OkHttpClient +import okhttp3.Request +import okhttp3.RequestBody +import rx.Observable +import java.util.* + + +class AnilistApi(val client: OkHttpClient, interceptor: AnilistInterceptor) { + + private val parser = JsonParser() + private val jsonMime = "application/json; charset=utf-8".toMediaTypeOrNull() + private val authClient = client.newBuilder().addInterceptor(interceptor).build() + + fun addLibManga(track: Track): Observable { + val query = """ + |mutation AddManga(${'$'}mangaId: Int, ${'$'}progress: Int, ${'$'}status: MediaListStatus) { + |SaveMediaListEntry (mediaId: ${'$'}mangaId, progress: ${'$'}progress, status: ${'$'}status) { + | id + | status + |} + |} + |""".trimMargin() + val variables = jsonObject( + "mangaId" to track.media_id, + "progress" to track.last_chapter_read, + "status" to track.toAnilistStatus() + ) + val payload = jsonObject( + "query" to query, + "variables" to variables + ) + val body = RequestBody.create(jsonMime, payload.toString()) + val request = Request.Builder() + .url(apiUrl) + .post(body) + .build() + return authClient.newCall(request) + .asObservableSuccess() + .map { netResponse -> + val responseBody = netResponse.body?.string().orEmpty() + netResponse.close() + if (responseBody.isEmpty()) { + throw Exception("Null Response") + } + val response = parser.parse(responseBody).obj + track.library_id = response["data"]["SaveMediaListEntry"]["id"].asLong + track + } + } + + fun updateLibManga(track: Track): Observable { + val query = """ + |mutation UpdateManga(${'$'}listId: Int, ${'$'}progress: Int, ${'$'}status: MediaListStatus, ${'$'}score: Int) { + |SaveMediaListEntry (id: ${'$'}listId, progress: ${'$'}progress, status: ${'$'}status, scoreRaw: ${'$'}score) { + |id + |status + |progress + |} + |} + |""".trimMargin() + val variables = jsonObject( + "listId" to track.library_id, + "progress" to track.last_chapter_read, + "status" to track.toAnilistStatus(), + "score" to track.score.toInt() + ) + val payload = jsonObject( + "query" to query, + "variables" to variables + ) + val body = RequestBody.create(jsonMime, payload.toString()) + val request = Request.Builder() + .url(apiUrl) + .post(body) + .build() + return authClient.newCall(request) + .asObservableSuccess() + .map { + track + } + } + + fun search(search: String): Observable> { + val query = """ + |query Search(${'$'}query: String) { + |Page (perPage: 50) { + |media(search: ${'$'}query, type: MANGA, format_not_in: [NOVEL]) { + |id + |title { + |romaji + |} + |coverImage { + |large + |} + |type + |status + |chapters + |description + |startDate { + |year + |month + |day + |} + |} + |} + |} + |""".trimMargin() + val variables = jsonObject( + "query" to search + ) + val payload = jsonObject( + "query" to query, + "variables" to variables + ) + val body = RequestBody.create(jsonMime, payload.toString()) + val request = Request.Builder() + .url(apiUrl) + .post(body) + .build() + return authClient.newCall(request) + .asObservableSuccess() + .map { netResponse -> + val responseBody = netResponse.body?.string().orEmpty() + if (responseBody.isEmpty()) { + throw Exception("Null Response") + } + val response = parser.parse(responseBody).obj + val data = response["data"]!!.obj + val page = data["Page"].obj + val media = page["media"].array + val entries = media.map { jsonToALManga(it.obj) } + entries.map { it.toTrack() } + } + } + + + fun findLibManga(track: Track, userid: Int): Observable { + val query = """ + |query (${'$'}id: Int!, ${'$'}manga_id: Int!) { + |Page { + |mediaList(userId: ${'$'}id, type: MANGA, mediaId: ${'$'}manga_id) { + |id + |status + |scoreRaw: score(format: POINT_100) + |progress + |media { + |id + |title { + |romaji + |} + |coverImage { + |large + |} + |type + |status + |chapters + |description + |startDate { + |year + |month + |day + |} + |} + |} + |} + |} + |""".trimMargin() + val variables = jsonObject( + "id" to userid, + "manga_id" to track.media_id + ) + val payload = jsonObject( + "query" to query, + "variables" to variables + ) + val body = RequestBody.create(jsonMime, payload.toString()) + val request = Request.Builder() + .url(apiUrl) + .post(body) + .build() + return authClient.newCall(request) + .asObservableSuccess() + .map { netResponse -> + val responseBody = netResponse.body?.string().orEmpty() + if (responseBody.isEmpty()) { + throw Exception("Null Response") + } + val response = parser.parse(responseBody).obj + val data = response["data"]!!.obj + val page = data["Page"].obj + val media = page["mediaList"].array + val entries = media.map { jsonToALUserManga(it.obj) } + entries.firstOrNull()?.toTrack() + + } + } + + fun getLibManga(track: Track, userid: Int): Observable { + return findLibManga(track, userid) + .map { it ?: throw Exception("Could not find manga") } + } + + fun createOAuth(token: String): OAuth { + return OAuth(token, "Bearer", System.currentTimeMillis() + 31536000000, 31536000000) + } + + fun getCurrentUser(): Observable> { + val query = """ + |query User { + |Viewer { + |id + |mediaListOptions { + |scoreFormat + |} + |} + |} + |""".trimMargin() + val payload = jsonObject( + "query" to query + ) + val body = RequestBody.create(jsonMime, payload.toString()) + val request = Request.Builder() + .url(apiUrl) + .post(body) + .build() + return authClient.newCall(request) + .asObservableSuccess() + .map { netResponse -> + val responseBody = netResponse.body?.string().orEmpty() + if (responseBody.isEmpty()) { + throw Exception("Null Response") + } + val response = parser.parse(responseBody).obj + val data = response["data"]!!.obj + val viewer = data["Viewer"].obj + Pair(viewer["id"].asInt, viewer["mediaListOptions"]["scoreFormat"].asString) + } + } + + private fun jsonToALManga(struct: JsonObject): ALManga { + val date = try { + val date = Calendar.getInstance() + date.set(struct["startDate"]["year"].nullInt ?: 0, (struct["startDate"]["month"].nullInt ?: 0) - 1, + struct["startDate"]["day"].nullInt ?: 0) + date.timeInMillis + } catch (_: Exception) { + 0L + } + + return ALManga(struct["id"].asInt, struct["title"]["romaji"].asString, struct["coverImage"]["large"].asString, + struct["description"].nullString.orEmpty(), struct["type"].asString, struct["status"].asString, + date, struct["chapters"].nullInt ?: 0) + } + + private fun jsonToALUserManga(struct: JsonObject): ALUserManga { + return ALUserManga(struct["id"].asLong, struct["status"].asString, struct["scoreRaw"].asInt, struct["progress"].asInt, jsonToALManga(struct["media"].obj)) + } + + companion object { + private const val clientId = "385" + private const val clientUrl = "tachiyomi://anilist-auth" + private const val apiUrl = "https://graphql.anilist.co/" + private const val baseUrl = "https://anilist.co/api/v2/" + private const val baseMangaUrl = "https://anilist.co/manga/" + + fun mangaUrl(mediaId: Int): String { + return baseMangaUrl + mediaId + } + + fun authUrl() = Uri.parse("${baseUrl}oauth/authorize").buildUpon() + .appendQueryParameter("client_id", clientId) + .appendQueryParameter("response_type", "token") + .build() + } + +} diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/track/bangumi/BangumiApi.kt b/app/src/main/java/eu/kanade/tachiyomi/data/track/bangumi/BangumiApi.kt index 661c26523..8e302e110 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/track/bangumi/BangumiApi.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/track/bangumi/BangumiApi.kt @@ -84,7 +84,7 @@ class BangumiApi(private val client: OkHttpClient, interceptor: BangumiIntercept return authClient.newCall(request) .asObservableSuccess() .map { netResponse -> - val responseBody = netResponse.body()?.string().orEmpty() + val responseBody = netResponse.body?.string().orEmpty() if (responseBody.isEmpty()) { throw Exception("Null Response") } @@ -127,7 +127,7 @@ class BangumiApi(private val client: OkHttpClient, interceptor: BangumiIntercept .asObservableSuccess() .map { netResponse -> // get comic info - val responseBody = netResponse.body()?.string().orEmpty() + val responseBody = netResponse.body?.string().orEmpty() jsonToTrack(parser.parse(responseBody).obj) } } @@ -144,7 +144,7 @@ class BangumiApi(private val client: OkHttpClient, interceptor: BangumiIntercept return authClient.newCall(requestUserRead) .asObservableSuccess() .map { netResponse -> - val resp = netResponse.body()?.string() + val resp = netResponse.body?.string() val coll = gson.fromJson(resp, Collection::class.java) track.status = coll.status?.id!! track.last_chapter_read = coll.ep_status!! @@ -154,7 +154,7 @@ class BangumiApi(private val client: OkHttpClient, interceptor: BangumiIntercept fun accessToken(code: String): Observable { return client.newCall(accessTokenRequest(code)).asObservableSuccess().map { netResponse -> - val responseBody = netResponse.body()?.string().orEmpty() + val responseBody = netResponse.body?.string().orEmpty() if (responseBody.isEmpty()) { throw Exception("Null Response") } diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/track/bangumi/BangumiInterceptor.kt b/app/src/main/java/eu/kanade/tachiyomi/data/track/bangumi/BangumiInterceptor.kt index 69565f447..a3a546c88 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/track/bangumi/BangumiInterceptor.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/track/bangumi/BangumiInterceptor.kt @@ -14,7 +14,7 @@ class BangumiInterceptor(val bangumi: Bangumi, val gson: Gson) : Interceptor { fun addTocken(tocken: String, oidFormBody: FormBody): FormBody { val newFormBody = FormBody.Builder() - for (i in 0 until oidFormBody.size()) { + for (i in 0 until oidFormBody.size) { newFormBody.add(oidFormBody.name(i), oidFormBody.value(i)) } newFormBody.add("access_token", tocken) @@ -29,18 +29,18 @@ class BangumiInterceptor(val bangumi: Bangumi, val gson: Gson) : Interceptor { if (currAuth.isExpired()) { val response = chain.proceed(BangumiApi.refreshTokenRequest(currAuth.refresh_token!!)) if (response.isSuccessful) { - newAuth(gson.fromJson(response.body()!!.string(), OAuth::class.java)) + newAuth(gson.fromJson(response.body!!.string(), OAuth::class.java)) } else { response.close() } } - var authRequest = if (originalRequest.method() == "GET") originalRequest.newBuilder() + var authRequest = if (originalRequest.method == "GET") originalRequest.newBuilder() .header("User-Agent", "Tachiyomi") - .url(originalRequest.url().newBuilder() + .url(originalRequest.url.newBuilder() .addQueryParameter("access_token", currAuth.access_token).build()) .build() else originalRequest.newBuilder() - .post(addTocken(currAuth.access_token, originalRequest.body() as FormBody)) + .post(addTocken(currAuth.access_token, originalRequest.body as FormBody)) .header("User-Agent", "Tachiyomi") .build() diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/track/kitsu/KitsuInterceptor.kt b/app/src/main/java/eu/kanade/tachiyomi/data/track/kitsu/KitsuInterceptor.kt index 8810dd274..1a74b8d9e 100755 --- a/app/src/main/java/eu/kanade/tachiyomi/data/track/kitsu/KitsuInterceptor.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/track/kitsu/KitsuInterceptor.kt @@ -22,7 +22,7 @@ class KitsuInterceptor(val kitsu: Kitsu, val gson: Gson) : Interceptor { if (currAuth.isExpired()) { val response = chain.proceed(KitsuApi.refreshTokenRequest(refreshToken)) if (response.isSuccessful) { - newAuth(gson.fromJson(response.body()!!.string(), OAuth::class.java)) + newAuth(gson.fromJson(response.body!!.string(), OAuth::class.java)) } else { response.close() } diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/track/kitsu/KitsuModels.kt b/app/src/main/java/eu/kanade/tachiyomi/data/track/kitsu/KitsuModels.kt index b5e55d9d9..5e709e810 100755 --- a/app/src/main/java/eu/kanade/tachiyomi/data/track/kitsu/KitsuModels.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/track/kitsu/KitsuModels.kt @@ -1,6 +1,6 @@ package eu.kanade.tachiyomi.data.track.kitsu -import android.support.annotation.CallSuper +import androidx.annotation.CallSuper import com.github.salomonbrys.kotson.* import com.google.gson.JsonObject import eu.kanade.tachiyomi.data.database.models.Track diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/track/myanimelist/MyAnimeList.kt b/app/src/main/java/eu/kanade/tachiyomi/data/track/myanimelist/MyAnimeList.kt index da40b5cfd..083060016 100755 --- a/app/src/main/java/eu/kanade/tachiyomi/data/track/myanimelist/MyAnimeList.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/track/myanimelist/MyAnimeList.kt @@ -1,164 +1,163 @@ -package eu.kanade.tachiyomi.data.track.myanimelist - -import android.content.Context -import android.graphics.Color -import eu.kanade.tachiyomi.R -import eu.kanade.tachiyomi.data.database.models.Track -import eu.kanade.tachiyomi.data.preference.getOrDefault -import eu.kanade.tachiyomi.data.track.TrackService -import eu.kanade.tachiyomi.data.track.model.TrackSearch -import okhttp3.HttpUrl -import rx.Completable -import rx.Observable -import java.lang.Exception - -class Myanimelist(private val context: Context, id: Int) : TrackService(id) { - - companion object { - const val READING = 1 - const val COMPLETED = 2 - const val ON_HOLD = 3 - const val DROPPED = 4 - const val PLAN_TO_READ = 6 - - const val DEFAULT_STATUS = READING - const val DEFAULT_SCORE = 0 - - const val BASE_URL = "https://myanimelist.net" - const val USER_SESSION_COOKIE = "MALSESSIONID" - const val LOGGED_IN_COOKIE = "is_logged_in" - } - - private val interceptor by lazy { MyAnimeListInterceptor(this) } - private val api by lazy { MyanimelistApi(client, interceptor) } - - override val name: String - get() = "MyAnimeList" - - override fun getLogo() = R.drawable.mal - - override fun getLogoColor() = Color.rgb(46, 81, 162) - - override fun getStatus(status: Int): String = with(context) { - when (status) { - READING -> getString(R.string.reading) - COMPLETED -> getString(R.string.completed) - ON_HOLD -> getString(R.string.on_hold) - DROPPED -> getString(R.string.dropped) - PLAN_TO_READ -> getString(R.string.plan_to_read) - else -> "" - } - } - - override fun getStatusList(): List { - return listOf(READING, COMPLETED, ON_HOLD, DROPPED, PLAN_TO_READ) - } - - override fun getScoreList(): List { - return IntRange(0, 10).map(Int::toString) - } - - override fun displayScore(track: Track): String { - return track.score.toInt().toString() - } - - override fun add(track: Track): Observable { - return api.addLibManga(track) - } - - override fun update(track: Track): Observable { - if (track.total_chapters != 0 && track.last_chapter_read == track.total_chapters) { - track.status = COMPLETED - } - - return api.updateLibManga(track) - } - - override fun bind(track: Track): Observable { - return api.findLibManga(track) - .flatMap { remoteTrack -> - if (remoteTrack != null) { - track.copyPersonalFrom(remoteTrack) - update(track) - } else { - // Set default fields if it's not found in the list - track.score = DEFAULT_SCORE.toFloat() - track.status = DEFAULT_STATUS - add(track) - } - } - } - - override fun search(query: String): Observable> { - return api.search(query) - } - - override fun refresh(track: Track): Observable { - return api.getLibManga(track) - .map { remoteTrack -> - track.copyPersonalFrom(remoteTrack) - track.total_chapters = remoteTrack.total_chapters - track - } - } - - override fun login(username: String, password: String): Completable { - logout() - - return Observable.fromCallable { api.login(username, password) } - .doOnNext { csrf -> saveCSRF(csrf) } - .doOnNext { saveCredentials(username, password) } - .doOnError { logout() } - .toCompletable() - } - - fun refreshLogin() { - val username = getUsername() - val password = getPassword() - logout() - - try { - val csrf = api.login(username, password) - saveCSRF(csrf) - saveCredentials(username, password) - } catch (e: Exception) { - logout() - throw e - } - } - - // Attempt to login again if cookies have been cleared but credentials are still filled - fun ensureLoggedIn() { - if (isAuthorized) return - if (!isLogged) throw Exception("MAL Login Credentials not found") - - refreshLogin() - } - - override fun logout() { - super.logout() - preferences.trackToken(this).delete() - networkService.cookieManager.remove(HttpUrl.parse(BASE_URL)!!) - } - - val isAuthorized: Boolean - get() = super.isLogged && - getCSRF().isNotEmpty() && - checkCookies() - - fun getCSRF(): String = preferences.trackToken(this).getOrDefault() - - private fun saveCSRF(csrf: String) = preferences.trackToken(this).set(csrf) - - private fun checkCookies(): Boolean { - var ckCount = 0 - val url = HttpUrl.parse(BASE_URL)!! - for (ck in networkService.cookieManager.get(url)) { - if (ck.name() == USER_SESSION_COOKIE || ck.name() == LOGGED_IN_COOKIE) - ckCount++ - } - - return ckCount == 2 - } - -} +package eu.kanade.tachiyomi.data.track.myanimelist + +import android.content.Context +import android.graphics.Color +import eu.kanade.tachiyomi.R +import eu.kanade.tachiyomi.data.database.models.Track +import eu.kanade.tachiyomi.data.preference.getOrDefault +import eu.kanade.tachiyomi.data.track.TrackService +import eu.kanade.tachiyomi.data.track.model.TrackSearch +import okhttp3.HttpUrl.Companion.toHttpUrlOrNull +import rx.Completable +import rx.Observable + +class Myanimelist(private val context: Context, id: Int) : TrackService(id) { + + companion object { + const val READING = 1 + const val COMPLETED = 2 + const val ON_HOLD = 3 + const val DROPPED = 4 + const val PLAN_TO_READ = 6 + + const val DEFAULT_STATUS = READING + const val DEFAULT_SCORE = 0 + + const val BASE_URL = "https://myanimelist.net" + const val USER_SESSION_COOKIE = "MALSESSIONID" + const val LOGGED_IN_COOKIE = "is_logged_in" + } + + private val interceptor by lazy { MyAnimeListInterceptor(this) } + private val api by lazy { MyanimelistApi(client, interceptor) } + + override val name: String + get() = "MyAnimeList" + + override fun getLogo() = R.drawable.mal + + override fun getLogoColor() = Color.rgb(46, 81, 162) + + override fun getStatus(status: Int): String = with(context) { + when (status) { + READING -> getString(R.string.reading) + COMPLETED -> getString(R.string.completed) + ON_HOLD -> getString(R.string.on_hold) + DROPPED -> getString(R.string.dropped) + PLAN_TO_READ -> getString(R.string.plan_to_read) + else -> "" + } + } + + override fun getStatusList(): List { + return listOf(READING, COMPLETED, ON_HOLD, DROPPED, PLAN_TO_READ) + } + + override fun getScoreList(): List { + return IntRange(0, 10).map(Int::toString) + } + + override fun displayScore(track: Track): String { + return track.score.toInt().toString() + } + + override fun add(track: Track): Observable { + return api.addLibManga(track) + } + + override fun update(track: Track): Observable { + if (track.total_chapters != 0 && track.last_chapter_read == track.total_chapters) { + track.status = COMPLETED + } + + return api.updateLibManga(track) + } + + override fun bind(track: Track): Observable { + return api.findLibManga(track) + .flatMap { remoteTrack -> + if (remoteTrack != null) { + track.copyPersonalFrom(remoteTrack) + update(track) + } else { + // Set default fields if it's not found in the list + track.score = DEFAULT_SCORE.toFloat() + track.status = DEFAULT_STATUS + add(track) + } + } + } + + override fun search(query: String): Observable> { + return api.search(query) + } + + override fun refresh(track: Track): Observable { + return api.getLibManga(track) + .map { remoteTrack -> + track.copyPersonalFrom(remoteTrack) + track.total_chapters = remoteTrack.total_chapters + track + } + } + + override fun login(username: String, password: String): Completable { + logout() + + return Observable.fromCallable { api.login(username, password) } + .doOnNext { csrf -> saveCSRF(csrf) } + .doOnNext { saveCredentials(username, password) } + .doOnError { logout() } + .toCompletable() + } + + fun refreshLogin() { + val username = getUsername() + val password = getPassword() + logout() + + try { + val csrf = api.login(username, password) + saveCSRF(csrf) + saveCredentials(username, password) + } catch (e: Exception) { + logout() + throw e + } + } + + // Attempt to login again if cookies have been cleared but credentials are still filled + fun ensureLoggedIn() { + if (isAuthorized) return + if (!isLogged) throw Exception("MAL Login Credentials not found") + + refreshLogin() + } + + override fun logout() { + super.logout() + preferences.trackToken(this).delete() + networkService.cookieManager.remove(BASE_URL.toHttpUrlOrNull()!!) + } + + val isAuthorized: Boolean + get() = super.isLogged && + getCSRF().isNotEmpty() && + checkCookies() + + fun getCSRF(): String = preferences.trackToken(this).getOrDefault() + + private fun saveCSRF(csrf: String) = preferences.trackToken(this).set(csrf) + + private fun checkCookies(): Boolean { + var ckCount = 0 + val url = BASE_URL.toHttpUrlOrNull()!! + for (ck in networkService.cookieManager.get(url)) { + if (ck.name == USER_SESSION_COOKIE || ck.name == LOGGED_IN_COOKIE) + ckCount++ + } + + return ckCount == 2 + } + +} diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/track/myanimelist/MyAnimeListInterceptor.kt b/app/src/main/java/eu/kanade/tachiyomi/data/track/myanimelist/MyAnimeListInterceptor.kt index 124182168..89770c6a0 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/track/myanimelist/MyAnimeListInterceptor.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/track/myanimelist/MyAnimeListInterceptor.kt @@ -15,7 +15,7 @@ class MyAnimeListInterceptor(private val myanimelist: Myanimelist): Interceptor val request = chain.request() var response = chain.proceed(updateRequest(request)) - if (response.code() == 400){ + if (response.code == 400) { myanimelist.refreshLogin() response = chain.proceed(updateRequest(request)) } @@ -24,7 +24,7 @@ class MyAnimeListInterceptor(private val myanimelist: Myanimelist): Interceptor } private fun updateRequest(request: Request): Request { - return request.body()?.let { + return request.body?.let { val contentType = it.contentType().toString() val updatedBody = when { contentType.contains("x-www-form-urlencoded") -> updateFormBody(it) diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/track/myanimelist/MyanimelistApi.kt b/app/src/main/java/eu/kanade/tachiyomi/data/track/myanimelist/MyanimelistApi.kt index efc3abefc..ae3c93ef8 100755 --- a/app/src/main/java/eu/kanade/tachiyomi/data/track/myanimelist/MyanimelistApi.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/track/myanimelist/MyanimelistApi.kt @@ -10,7 +10,11 @@ import eu.kanade.tachiyomi.network.asObservable import eu.kanade.tachiyomi.network.asObservableSuccess import eu.kanade.tachiyomi.util.selectInt import eu.kanade.tachiyomi.util.selectText -import okhttp3.* +import okhttp3.FormBody +import okhttp3.MediaType.Companion.toMediaTypeOrNull +import okhttp3.OkHttpClient +import okhttp3.RequestBody +import okhttp3.Response import org.json.JSONObject import org.jsoup.Jsoup import org.jsoup.nodes.Document @@ -85,7 +89,7 @@ class MyanimelistApi(private val client: OkHttpClient, interceptor: MyAnimeListI .map {response -> var libTrack: Track? = null response.use { - if (it.priorResponse()?.isRedirect != true) { + if (it.priorResponse?.isRedirect != true) { val trackForm = Jsoup.parse(it.consumeBody()) libTrack = Track.create(TrackManager.MYANIMELIST).apply { @@ -125,7 +129,7 @@ class MyanimelistApi(private val client: OkHttpClient, interceptor: MyAnimeListI val response = client.newCall(POST(url = loginUrl(), body = loginPostBody(username, password, csrf))).execute() response.use { - if (response.priorResponse()?.code() != 302) throw Exception("Authentication error") + if (response.priorResponse?.code != 302) throw Exception("Authentication error") } } @@ -172,15 +176,15 @@ class MyanimelistApi(private val client: OkHttpClient, interceptor: MyAnimeListI private fun Response.consumeBody(): String? { use { - if (it.code() != 200) throw Exception("HTTP error ${it.code()}") - return it.body()?.string() + if (it.code != 200) throw Exception("HTTP error ${it.code}") + return it.body?.string() } } private fun Response.consumeXmlBody(): String? { use { res -> - if (res.code() != 200) throw Exception("Export list error") - BufferedReader(InputStreamReader(GZIPInputStream(res.body()?.source()?.inputStream()))).use { reader -> + if (res.code != 200) throw Exception("Export list error") + BufferedReader(InputStreamReader(GZIPInputStream(res.body?.source()?.inputStream()))).use { reader -> val sb = StringBuilder() reader.forEachLine { line -> sb.append(line) @@ -262,7 +266,7 @@ class MyanimelistApi(private val client: OkHttpClient, interceptor: MyAnimeListI .put("score", track.score) .put("num_read_chapters", track.last_chapter_read) - return RequestBody.create(MediaType.parse("application/json; charset=utf-8"), body.toString()) + return RequestBody.create("application/json; charset=utf-8".toMediaTypeOrNull(), body.toString()) } private fun Element.searchTitle() = select("strong").text()!! diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/track/shikimori/ShikimoriApi.kt b/app/src/main/java/eu/kanade/tachiyomi/data/track/shikimori/ShikimoriApi.kt index 0180e015e..7441cf66d 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/track/shikimori/ShikimoriApi.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/track/shikimori/ShikimoriApi.kt @@ -14,7 +14,11 @@ import eu.kanade.tachiyomi.data.track.model.TrackSearch import eu.kanade.tachiyomi.network.GET import eu.kanade.tachiyomi.network.POST import eu.kanade.tachiyomi.network.asObservableSuccess -import okhttp3.* +import okhttp3.FormBody +import okhttp3.MediaType.Companion.toMediaTypeOrNull +import okhttp3.OkHttpClient +import okhttp3.Request +import okhttp3.RequestBody import rx.Observable import uy.kohesive.injekt.injectLazy @@ -22,7 +26,7 @@ class ShikimoriApi(private val client: OkHttpClient, interceptor: ShikimoriInter private val gson: Gson by injectLazy() private val parser = JsonParser() - private val jsonime = MediaType.parse("application/json; charset=utf-8") + private val jsonime = "application/json; charset=utf-8".toMediaTypeOrNull() private val authClient = client.newBuilder().addInterceptor(interceptor).build() fun addLibManga(track: Track, user_id: String): Observable { @@ -63,7 +67,7 @@ class ShikimoriApi(private val client: OkHttpClient, interceptor: ShikimoriInter return authClient.newCall(request) .asObservableSuccess() .map { netResponse -> - val responseBody = netResponse.body()?.string().orEmpty() + val responseBody = netResponse.body?.string().orEmpty() if (responseBody.isEmpty()) { throw Exception("Null Response") } @@ -120,13 +124,13 @@ class ShikimoriApi(private val client: OkHttpClient, interceptor: ShikimoriInter return authClient.newCall(requestMangas) .asObservableSuccess() .map { netResponse -> - val responseBody = netResponse.body()?.string().orEmpty() + val responseBody = netResponse.body?.string().orEmpty() parser.parse(responseBody).obj }.flatMap { mangas -> authClient.newCall(request) .asObservableSuccess() .map { netResponse -> - val responseBody = netResponse.body()?.string().orEmpty() + val responseBody = netResponse.body?.string().orEmpty() if (responseBody.isEmpty()) { throw Exception("Null Response") } @@ -143,13 +147,13 @@ class ShikimoriApi(private val client: OkHttpClient, interceptor: ShikimoriInter } fun getCurrentUser(): Int { - val user = authClient.newCall(GET("$apiUrl/users/whoami")).execute().body()?.string() + val user = authClient.newCall(GET("$apiUrl/users/whoami")).execute().body?.string() return parser.parse(user).obj["id"].asInt } fun accessToken(code: String): Observable { return client.newCall(accessTokenRequest(code)).asObservableSuccess().map { netResponse -> - val responseBody = netResponse.body()?.string().orEmpty() + val responseBody = netResponse.body?.string().orEmpty() if (responseBody.isEmpty()) { throw Exception("Null Response") } diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/track/shikimori/ShikimoriInterceptor.kt b/app/src/main/java/eu/kanade/tachiyomi/data/track/shikimori/ShikimoriInterceptor.kt index 154020727..6e10b4de3 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/track/shikimori/ShikimoriInterceptor.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/track/shikimori/ShikimoriInterceptor.kt @@ -22,7 +22,7 @@ class ShikimoriInterceptor(val shikimori: Shikimori, val gson: Gson) : Intercept if (currAuth.isExpired()) { val response = chain.proceed(ShikimoriApi.refreshTokenRequest(refreshToken)) if (response.isSuccessful) { - newAuth(gson.fromJson(response.body()!!.string(), OAuth::class.java)) + newAuth(gson.fromJson(response.body!!.string(), OAuth::class.java)) } else { response.close() } diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/updater/UpdaterJob.kt b/app/src/main/java/eu/kanade/tachiyomi/data/updater/UpdaterJob.kt index 594ecd31b..3bcfe9fcf 100755 --- a/app/src/main/java/eu/kanade/tachiyomi/data/updater/UpdaterJob.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/updater/UpdaterJob.kt @@ -2,7 +2,7 @@ package eu.kanade.tachiyomi.data.updater import android.app.PendingIntent import android.content.Intent -import android.support.v4.app.NotificationCompat +import androidx.core.app.NotificationCompat import com.evernote.android.job.Job import com.evernote.android.job.JobManager import com.evernote.android.job.JobRequest diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/updater/UpdaterNotifier.kt b/app/src/main/java/eu/kanade/tachiyomi/data/updater/UpdaterNotifier.kt index 509c65bb4..9879ec5ed 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/updater/UpdaterNotifier.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/updater/UpdaterNotifier.kt @@ -2,7 +2,7 @@ package eu.kanade.tachiyomi.data.updater import android.content.Context import android.net.Uri -import android.support.v4.app.NotificationCompat +import androidx.core.app.NotificationCompat import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.data.notification.NotificationHandler import eu.kanade.tachiyomi.data.notification.NotificationReceiver diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/updater/UpdaterService.kt b/app/src/main/java/eu/kanade/tachiyomi/data/updater/UpdaterService.kt index 4bcff3f1f..5dd8dee2e 100755 --- a/app/src/main/java/eu/kanade/tachiyomi/data/updater/UpdaterService.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/updater/UpdaterService.kt @@ -71,7 +71,7 @@ class UpdaterService : IntentService(UpdaterService::class.java.name) { val apkFile = File(externalCacheDir, "update.apk") if (response.isSuccessful) { - response.body()!!.source().saveTo(apkFile) + response.body!!.source().saveTo(apkFile) } else { response.close() throw Exception("Unsuccessful response") diff --git a/app/src/main/java/eu/kanade/tachiyomi/extension/api/ExtensionGithubApi.kt b/app/src/main/java/eu/kanade/tachiyomi/extension/api/ExtensionGithubApi.kt index f8ef81b89..d8eb98212 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/extension/api/ExtensionGithubApi.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/extension/api/ExtensionGithubApi.kt @@ -32,7 +32,7 @@ internal class ExtensionGithubApi { } private fun parseResponse(response: Response): List { - val text = response.body()?.use { it.string() } ?: return emptyList() + val text = response.body?.use { it.string() } ?: return emptyList() val json = gson.fromJson(text) diff --git a/app/src/main/java/eu/kanade/tachiyomi/network/AndroidCookieJar.kt b/app/src/main/java/eu/kanade/tachiyomi/network/AndroidCookieJar.kt index 6d425bfb9..ff231ed42 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/network/AndroidCookieJar.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/network/AndroidCookieJar.kt @@ -21,7 +21,7 @@ class AndroidCookieJar(context: Context) : CookieJar { } } - override fun saveFromResponse(url: HttpUrl, cookies: MutableList) { + override fun saveFromResponse(url: HttpUrl, cookies: List) { val urlString = url.toString() for (cookie in cookies) { diff --git a/app/src/main/java/eu/kanade/tachiyomi/network/CloudflareInterceptor.kt b/app/src/main/java/eu/kanade/tachiyomi/network/CloudflareInterceptor.kt index a3f4283a1..18051f680 100755 --- a/app/src/main/java/eu/kanade/tachiyomi/network/CloudflareInterceptor.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/network/CloudflareInterceptor.kt @@ -1,154 +1,154 @@ -package eu.kanade.tachiyomi.network - -import android.annotation.SuppressLint -import android.content.Context -import android.os.Build -import android.os.Handler -import android.os.Looper -import android.webkit.WebResourceResponse -import android.webkit.WebSettings -import android.webkit.WebView -import eu.kanade.tachiyomi.util.WebViewClientCompat -import okhttp3.Interceptor -import okhttp3.Request -import okhttp3.Response -import java.io.IOException -import java.util.concurrent.CountDownLatch -import java.util.concurrent.TimeUnit - -class CloudflareInterceptor(private val context: Context) : Interceptor { - - private val serverCheck = arrayOf("cloudflare-nginx", "cloudflare") - - private val handler = Handler(Looper.getMainLooper()) - - /** - * When this is called, it initializes the WebView if it wasn't already. We use this to avoid - * blocking the main thread too much. If used too often we could consider moving it to the - * Application class. - */ - private val initWebView by lazy { - if (Build.VERSION.SDK_INT >= 17) { - WebSettings.getDefaultUserAgent(context) - } else { - null - } - } - - @Synchronized - override fun intercept(chain: Interceptor.Chain): Response { - initWebView - - val response = chain.proceed(chain.request()) - - // Check if Cloudflare anti-bot is on - if (response.code() == 503 && response.header("Server") in serverCheck) { - try { - response.close() - val solutionRequest = resolveWithWebView(chain.request()) - return chain.proceed(solutionRequest) - } catch (e: Exception) { - // Because OkHttp's enqueue only handles IOExceptions, wrap the exception so that - // we don't crash the entire app - throw IOException(e) - } - } - - return response - } - - private fun isChallengeSolutionUrl(url: String): Boolean { - return "chk_jschl" in url - } - - @SuppressLint("SetJavaScriptEnabled") - private fun resolveWithWebView(request: Request): Request { - // We need to lock this thread until the WebView finds the challenge solution url, because - // OkHttp doesn't support asynchronous interceptors. - val latch = CountDownLatch(1) - - var webView: WebView? = null - var solutionUrl: String? = null - var challengeFound = false - - val origRequestUrl = request.url().toString() - val headers = request.headers().toMultimap().mapValues { it.value.getOrNull(0) ?: "" } - - handler.post { - val view = WebView(context) - webView = view - view.settings.javaScriptEnabled = true - view.settings.userAgentString = request.header("User-Agent") - view.webViewClient = object : WebViewClientCompat() { - - override fun shouldOverrideUrlCompat(view: WebView, url: String): Boolean { - if (isChallengeSolutionUrl(url)) { - solutionUrl = url - latch.countDown() - } - return solutionUrl != null - } - - override fun shouldInterceptRequestCompat( - view: WebView, - url: String - ): WebResourceResponse? { - if (solutionUrl != null) { - // Intercept any request when we have the solution. - return WebResourceResponse("text/plain", "UTF-8", null) - } - return null - } - - override fun onPageFinished(view: WebView, url: String) { - // Http error codes are only received since M - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && - url == origRequestUrl && !challengeFound - ) { - // The first request didn't return the challenge, abort. - latch.countDown() - } - } - - override fun onReceivedErrorCompat( - view: WebView, - errorCode: Int, - description: String?, - failingUrl: String, - isMainFrame: Boolean - ) { - if (isMainFrame) { - if (errorCode == 503) { - // Found the cloudflare challenge page. - challengeFound = true - } else { - // Unlock thread, the challenge wasn't found. - latch.countDown() - } - } - } - } - webView?.loadUrl(origRequestUrl, headers) - } - - // Wait a reasonable amount of time to retrieve the solution. The minimum should be - // around 4 seconds but it can take more due to slow networks or server issues. - latch.await(12, TimeUnit.SECONDS) - - handler.post { - webView?.stopLoading() - webView?.destroy() - } - - val solution = solutionUrl ?: throw Exception("Challenge not found") - - return Request.Builder().get() - .url(solution) - .headers(request.headers()) - .addHeader("Referer", origRequestUrl) - .addHeader("Accept", "text/html,application/xhtml+xml,application/xml") - .addHeader("Accept-Language", "en") - .build() - } - -} +package eu.kanade.tachiyomi.network + +import android.annotation.SuppressLint +import android.content.Context +import android.os.Build +import android.os.Handler +import android.os.Looper +import android.webkit.WebResourceResponse +import android.webkit.WebSettings +import android.webkit.WebView +import eu.kanade.tachiyomi.util.WebViewClientCompat +import okhttp3.Interceptor +import okhttp3.Request +import okhttp3.Response +import java.io.IOException +import java.util.concurrent.CountDownLatch +import java.util.concurrent.TimeUnit + +class CloudflareInterceptor(private val context: Context) : Interceptor { + + private val serverCheck = arrayOf("cloudflare-nginx", "cloudflare") + + private val handler = Handler(Looper.getMainLooper()) + + /** + * When this is called, it initializes the WebView if it wasn't already. We use this to avoid + * blocking the main thread too much. If used too often we could consider moving it to the + * Application class. + */ + private val initWebView by lazy { + if (Build.VERSION.SDK_INT >= 17) { + WebSettings.getDefaultUserAgent(context) + } else { + null + } + } + + @Synchronized + override fun intercept(chain: Interceptor.Chain): Response { + initWebView + + val response = chain.proceed(chain.request()) + + // Check if Cloudflare anti-bot is on + if (response.code == 503 && response.header("Server") in serverCheck) { + try { + response.close() + val solutionRequest = resolveWithWebView(chain.request()) + return chain.proceed(solutionRequest) + } catch (e: Exception) { + // Because OkHttp's enqueue only handles IOExceptions, wrap the exception so that + // we don't crash the entire app + throw IOException(e) + } + } + + return response + } + + private fun isChallengeSolutionUrl(url: String): Boolean { + return "chk_jschl" in url + } + + @SuppressLint("SetJavaScriptEnabled") + private fun resolveWithWebView(request: Request): Request { + // We need to lock this thread until the WebView finds the challenge solution url, because + // OkHttp doesn't support asynchronous interceptors. + val latch = CountDownLatch(1) + + var webView: WebView? = null + var solutionUrl: String? = null + var challengeFound = false + + val origRequestUrl = request.url.toString() + val headers = request.headers.toMultimap().mapValues { it.value.getOrNull(0) ?: "" } + + handler.post { + val view = WebView(context) + webView = view + view.settings.javaScriptEnabled = true + view.settings.userAgentString = request.header("User-Agent") + view.webViewClient = object : WebViewClientCompat() { + + override fun shouldOverrideUrlCompat(view: WebView, url: String): Boolean { + if (isChallengeSolutionUrl(url)) { + solutionUrl = url + latch.countDown() + } + return solutionUrl != null + } + + override fun shouldInterceptRequestCompat( + view: WebView, + url: String + ): WebResourceResponse? { + if (solutionUrl != null) { + // Intercept any request when we have the solution. + return WebResourceResponse("text/plain", "UTF-8", null) + } + return null + } + + override fun onPageFinished(view: WebView, url: String) { + // Http error codes are only received since M + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && + url == origRequestUrl && !challengeFound + ) { + // The first request didn't return the challenge, abort. + latch.countDown() + } + } + + override fun onReceivedErrorCompat( + view: WebView, + errorCode: Int, + description: String?, + failingUrl: String, + isMainFrame: Boolean + ) { + if (isMainFrame) { + if (errorCode == 503) { + // Found the cloudflare challenge page. + challengeFound = true + } else { + // Unlock thread, the challenge wasn't found. + latch.countDown() + } + } + } + } + webView?.loadUrl(origRequestUrl, headers) + } + + // Wait a reasonable amount of time to retrieve the solution. The minimum should be + // around 4 seconds but it can take more due to slow networks or server issues. + latch.await(12, TimeUnit.SECONDS) + + handler.post { + webView?.stopLoading() + webView?.destroy() + } + + val solution = solutionUrl ?: throw Exception("Challenge not found") + + return Request.Builder().get() + .url(solution) + .headers(request.headers) + .addHeader("Referer", origRequestUrl) + .addHeader("Accept", "text/html,application/xhtml+xml,application/xml") + .addHeader("Accept-Language", "en") + .build() + } + +} diff --git a/app/src/main/java/eu/kanade/tachiyomi/network/NetworkHelper.kt b/app/src/main/java/eu/kanade/tachiyomi/network/NetworkHelper.kt index f156e0ead..f67c7e840 100755 --- a/app/src/main/java/eu/kanade/tachiyomi/network/NetworkHelper.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/network/NetworkHelper.kt @@ -1,120 +1,120 @@ -package eu.kanade.tachiyomi.network - -import android.content.Context -import android.os.Build -import exh.log.maybeInjectEHLogger -import okhttp3.* -import java.io.File -import java.io.IOException -import java.net.InetAddress -import java.net.Socket -import java.net.UnknownHostException -import java.security.KeyManagementException -import java.security.KeyStore -import java.security.NoSuchAlgorithmException -import javax.net.ssl.* - -open class NetworkHelper(context: Context) { - - private val cacheDir = File(context.cacheDir, "network_cache") - - private val cacheSize = 5L * 1024 * 1024 // 5 MiB - - open val cookieManager = AndroidCookieJar(context) - - open val client = OkHttpClient.Builder() - .cookieJar(cookieManager) - .cache(Cache(cacheDir, cacheSize)) - .enableTLS12() - .maybeInjectEHLogger() - .build() - - open val cloudflareClient = client.newBuilder() - .addInterceptor(CloudflareInterceptor(context)) - .maybeInjectEHLogger() - .build() - - private fun OkHttpClient.Builder.enableTLS12(): OkHttpClient.Builder { - if (Build.VERSION.SDK_INT > Build.VERSION_CODES.KITKAT) { - return this - } - - val trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()) - trustManagerFactory.init(null as KeyStore?) - val trustManagers = trustManagerFactory.trustManagers - if (trustManagers.size == 1 && trustManagers[0] is X509TrustManager) { - class TLSSocketFactory @Throws(KeyManagementException::class, NoSuchAlgorithmException::class) - constructor() : SSLSocketFactory() { - - private val internalSSLSocketFactory: SSLSocketFactory - - init { - val context = SSLContext.getInstance("TLS") - context.init(null, null, null) - internalSSLSocketFactory = context.socketFactory - } - - override fun getDefaultCipherSuites(): Array { - return internalSSLSocketFactory.defaultCipherSuites - } - - override fun getSupportedCipherSuites(): Array { - return internalSSLSocketFactory.supportedCipherSuites - } - - @Throws(IOException::class) - override fun createSocket(): Socket? { - return enableTLSOnSocket(internalSSLSocketFactory.createSocket()) - } - - @Throws(IOException::class) - override fun createSocket(s: Socket, host: String, port: Int, autoClose: Boolean): Socket? { - return enableTLSOnSocket(internalSSLSocketFactory.createSocket(s, host, port, autoClose)) - } - - @Throws(IOException::class, UnknownHostException::class) - override fun createSocket(host: String, port: Int): Socket? { - return enableTLSOnSocket(internalSSLSocketFactory.createSocket(host, port)) - } - - @Throws(IOException::class, UnknownHostException::class) - override fun createSocket(host: String, port: Int, localHost: InetAddress, localPort: Int): Socket? { - return enableTLSOnSocket(internalSSLSocketFactory.createSocket(host, port, localHost, localPort)) - } - - @Throws(IOException::class) - override fun createSocket(host: InetAddress, port: Int): Socket? { - return enableTLSOnSocket(internalSSLSocketFactory.createSocket(host, port)) - } - - @Throws(IOException::class) - override fun createSocket(address: InetAddress, port: Int, localAddress: InetAddress, localPort: Int): Socket? { - return enableTLSOnSocket(internalSSLSocketFactory.createSocket(address, port, localAddress, localPort)) - } - - private fun enableTLSOnSocket(socket: Socket?): Socket? { - if (socket != null && socket is SSLSocket) { - socket.enabledProtocols = socket.supportedProtocols - } - return socket - } - } - - sslSocketFactory(TLSSocketFactory(), trustManagers[0] as X509TrustManager) - } - - val specCompat = ConnectionSpec.Builder(ConnectionSpec.MODERN_TLS) - .tlsVersions(TlsVersion.TLS_1_2, TlsVersion.TLS_1_1, TlsVersion.TLS_1_0) - .cipherSuites( - *ConnectionSpec.MODERN_TLS.cipherSuites().orEmpty().toTypedArray(), - CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, - CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA - ) - .build() - - val specs = listOf(specCompat, ConnectionSpec.CLEARTEXT) - connectionSpecs(specs) - - return this - } -} +package eu.kanade.tachiyomi.network + +import android.content.Context +import android.os.Build +import exh.log.maybeInjectEHLogger +import okhttp3.* +import java.io.File +import java.io.IOException +import java.net.InetAddress +import java.net.Socket +import java.net.UnknownHostException +import java.security.KeyManagementException +import java.security.KeyStore +import java.security.NoSuchAlgorithmException +import javax.net.ssl.* + +open class NetworkHelper(context: Context) { + + private val cacheDir = File(context.cacheDir, "network_cache") + + private val cacheSize = 5L * 1024 * 1024 // 5 MiB + + open val cookieManager = AndroidCookieJar(context) + + open val client = OkHttpClient.Builder() + .cookieJar(cookieManager) + .cache(Cache(cacheDir, cacheSize)) + .enableTLS12() + .maybeInjectEHLogger() + .build() + + open val cloudflareClient = client.newBuilder() + .addInterceptor(CloudflareInterceptor(context)) + .maybeInjectEHLogger() + .build() + + private fun OkHttpClient.Builder.enableTLS12(): OkHttpClient.Builder { + if (Build.VERSION.SDK_INT > Build.VERSION_CODES.KITKAT) { + return this + } + + val trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()) + trustManagerFactory.init(null as KeyStore?) + val trustManagers = trustManagerFactory.trustManagers + if (trustManagers.size == 1 && trustManagers[0] is X509TrustManager) { + class TLSSocketFactory @Throws(KeyManagementException::class, NoSuchAlgorithmException::class) + constructor() : SSLSocketFactory() { + + private val internalSSLSocketFactory: SSLSocketFactory + + init { + val context = SSLContext.getInstance("TLS") + context.init(null, null, null) + internalSSLSocketFactory = context.socketFactory + } + + override fun getDefaultCipherSuites(): Array { + return internalSSLSocketFactory.defaultCipherSuites + } + + override fun getSupportedCipherSuites(): Array { + return internalSSLSocketFactory.supportedCipherSuites + } + + @Throws(IOException::class) + override fun createSocket(): Socket? { + return enableTLSOnSocket(internalSSLSocketFactory.createSocket()) + } + + @Throws(IOException::class) + override fun createSocket(s: Socket, host: String, port: Int, autoClose: Boolean): Socket? { + return enableTLSOnSocket(internalSSLSocketFactory.createSocket(s, host, port, autoClose)) + } + + @Throws(IOException::class, UnknownHostException::class) + override fun createSocket(host: String, port: Int): Socket? { + return enableTLSOnSocket(internalSSLSocketFactory.createSocket(host, port)) + } + + @Throws(IOException::class, UnknownHostException::class) + override fun createSocket(host: String, port: Int, localHost: InetAddress, localPort: Int): Socket? { + return enableTLSOnSocket(internalSSLSocketFactory.createSocket(host, port, localHost, localPort)) + } + + @Throws(IOException::class) + override fun createSocket(host: InetAddress, port: Int): Socket? { + return enableTLSOnSocket(internalSSLSocketFactory.createSocket(host, port)) + } + + @Throws(IOException::class) + override fun createSocket(address: InetAddress, port: Int, localAddress: InetAddress, localPort: Int): Socket? { + return enableTLSOnSocket(internalSSLSocketFactory.createSocket(address, port, localAddress, localPort)) + } + + private fun enableTLSOnSocket(socket: Socket?): Socket? { + if (socket != null && socket is SSLSocket) { + socket.enabledProtocols = socket.supportedProtocols + } + return socket + } + } + + sslSocketFactory(TLSSocketFactory(), trustManagers[0] as X509TrustManager) + } + + val specCompat = ConnectionSpec.Builder(ConnectionSpec.MODERN_TLS) + .tlsVersions(TlsVersion.TLS_1_2, TlsVersion.TLS_1_1, TlsVersion.TLS_1_0) + .cipherSuites( + *ConnectionSpec.MODERN_TLS.cipherSuites.orEmpty().toTypedArray(), + CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, + CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA + ) + .build() + + val specs = listOf(specCompat, ConnectionSpec.CLEARTEXT) + connectionSpecs(specs) + + return this + } +} diff --git a/app/src/main/java/eu/kanade/tachiyomi/network/OkHttpExtensions.kt b/app/src/main/java/eu/kanade/tachiyomi/network/OkHttpExtensions.kt index 98355a653..2d103a8f8 100755 --- a/app/src/main/java/eu/kanade/tachiyomi/network/OkHttpExtensions.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/network/OkHttpExtensions.kt @@ -1,81 +1,81 @@ -package eu.kanade.tachiyomi.network - -import exh.util.withRootCause -import okhttp3.Call -import okhttp3.OkHttpClient -import okhttp3.Request -import okhttp3.Response -import rx.Observable -import rx.Producer -import rx.Subscription -import java.util.concurrent.atomic.AtomicBoolean - -fun Call.asObservableWithAsyncStacktrace(): Observable> { - // Record stacktrace at creation time for easier debugging - // asObservable is involved in a lot of crashes so this is worth the performance hit - val asyncStackTrace = Exception("Async stacktrace") - - return Observable.unsafeCreate { subscriber -> - // Since Call is a one-shot type, clone it for each new subscriber. - val call = clone() - - // Wrap the call in a helper which handles both unsubscription and backpressure. - val requestArbiter = object : AtomicBoolean(), Producer, Subscription { - val executed = AtomicBoolean(false) - - override fun request(n: Long) { - if (n == 0L || !compareAndSet(false, true)) return - - try { - val response = call.execute() - executed.set(true) - if (!subscriber.isUnsubscribed) { - subscriber.onNext(asyncStackTrace to response) - subscriber.onCompleted() - } - } catch (error: Throwable) { - if (!subscriber.isUnsubscribed) { - subscriber.onError(error.withRootCause(asyncStackTrace)) - } - } - } - - override fun unsubscribe() { - if(!executed.get()) - call.cancel() - } - - override fun isUnsubscribed(): Boolean { - return call.isCanceled - } - } - - subscriber.add(requestArbiter) - subscriber.setProducer(requestArbiter) - } -} - -fun Call.asObservable() = asObservableWithAsyncStacktrace().map { it.second } - -fun Call.asObservableSuccess(): Observable { - return asObservableWithAsyncStacktrace().map { (asyncStacktrace, response) -> - if (!response.isSuccessful) { - response.close() - throw Exception("HTTP error ${response.code()}", asyncStacktrace) - } else response - } -} - -fun OkHttpClient.newCallWithProgress(request: Request, listener: ProgressListener): Call { - val progressClient = newBuilder() - .cache(null) - .addNetworkInterceptor { chain -> - val originalResponse = chain.proceed(chain.request()) - originalResponse.newBuilder() - .body(ProgressResponseBody(originalResponse.body()!!, listener)) - .build() - } - .build() - - return progressClient.newCall(request) +package eu.kanade.tachiyomi.network + +import exh.util.withRootCause +import okhttp3.Call +import okhttp3.OkHttpClient +import okhttp3.Request +import okhttp3.Response +import rx.Observable +import rx.Producer +import rx.Subscription +import java.util.concurrent.atomic.AtomicBoolean + +fun Call.asObservableWithAsyncStacktrace(): Observable> { + // Record stacktrace at creation time for easier debugging + // asObservable is involved in a lot of crashes so this is worth the performance hit + val asyncStackTrace = Exception("Async stacktrace") + + return Observable.unsafeCreate { subscriber -> + // Since Call is a one-shot type, clone it for each new subscriber. + val call = clone() + + // Wrap the call in a helper which handles both unsubscription and backpressure. + val requestArbiter = object : AtomicBoolean(), Producer, Subscription { + val executed = AtomicBoolean(false) + + override fun request(n: Long) { + if (n == 0L || !compareAndSet(false, true)) return + + try { + val response = call.execute() + executed.set(true) + if (!subscriber.isUnsubscribed) { + subscriber.onNext(asyncStackTrace to response) + subscriber.onCompleted() + } + } catch (error: Throwable) { + if (!subscriber.isUnsubscribed) { + subscriber.onError(error.withRootCause(asyncStackTrace)) + } + } + } + + override fun unsubscribe() { + if(!executed.get()) + call.cancel() + } + + override fun isUnsubscribed(): Boolean { + return call.isCanceled() + } + } + + subscriber.add(requestArbiter) + subscriber.setProducer(requestArbiter) + } +} + +fun Call.asObservable() = asObservableWithAsyncStacktrace().map { it.second } + +fun Call.asObservableSuccess(): Observable { + return asObservableWithAsyncStacktrace().map { (asyncStacktrace, response) -> + if (!response.isSuccessful) { + response.close() + throw Exception("HTTP error ${response.code}", asyncStacktrace) + } else response + } +} + +fun OkHttpClient.newCallWithProgress(request: Request, listener: ProgressListener): Call { + val progressClient = newBuilder() + .cache(null) + .addNetworkInterceptor { chain -> + val originalResponse = chain.proceed(chain.request()) + originalResponse.newBuilder() + .body(ProgressResponseBody(originalResponse.body!!, listener)) + .build() + } + .build() + + return progressClient.newCall(request) } \ No newline at end of file diff --git a/app/src/main/java/eu/kanade/tachiyomi/network/ProgressResponseBody.kt b/app/src/main/java/eu/kanade/tachiyomi/network/ProgressResponseBody.kt index f8123c519..a566a5d90 100755 --- a/app/src/main/java/eu/kanade/tachiyomi/network/ProgressResponseBody.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/network/ProgressResponseBody.kt @@ -1,40 +1,40 @@ -package eu.kanade.tachiyomi.network - -import okhttp3.MediaType -import okhttp3.ResponseBody -import okio.* -import java.io.IOException - -class ProgressResponseBody(private val responseBody: ResponseBody, private val progressListener: ProgressListener) : ResponseBody() { - - private val bufferedSource: BufferedSource by lazy { - Okio.buffer(source(responseBody.source())) - } - - override fun contentType(): MediaType { - return responseBody.contentType()!! - } - - override fun contentLength(): Long { - return responseBody.contentLength() - } - - override fun source(): BufferedSource { - return bufferedSource - } - - private fun source(source: Source): Source { - return object : ForwardingSource(source) { - internal var totalBytesRead = 0L - - @Throws(IOException::class) - override fun read(sink: Buffer, byteCount: Long): Long { - val bytesRead = super.read(sink, byteCount) - // read() returns the number of bytes read, or -1 if this source is exhausted. - totalBytesRead += if (bytesRead != -1L) bytesRead else 0 - progressListener.update(totalBytesRead, responseBody.contentLength(), bytesRead == -1L) - return bytesRead - } - } - } +package eu.kanade.tachiyomi.network + +import okhttp3.MediaType +import okhttp3.ResponseBody +import okio.* +import java.io.IOException + +class ProgressResponseBody(private val responseBody: ResponseBody, private val progressListener: ProgressListener) : ResponseBody() { + + private val bufferedSource: BufferedSource by lazy { + source(responseBody.source()).buffer() + } + + override fun contentType(): MediaType { + return responseBody.contentType()!! + } + + override fun contentLength(): Long { + return responseBody.contentLength() + } + + override fun source(): BufferedSource { + return bufferedSource + } + + private fun source(source: Source): Source { + return object : ForwardingSource(source) { + internal var totalBytesRead = 0L + + @Throws(IOException::class) + override fun read(sink: Buffer, byteCount: Long): Long { + val bytesRead = super.read(sink, byteCount) + // read() returns the number of bytes read, or -1 if this source is exhausted. + totalBytesRead += if (bytesRead != -1L) bytesRead else 0 + progressListener.update(totalBytesRead, responseBody.contentLength(), bytesRead == -1L) + return bytesRead + } + } + } } \ No newline at end of file diff --git a/app/src/main/java/eu/kanade/tachiyomi/source/ConfigurableSource.kt b/app/src/main/java/eu/kanade/tachiyomi/source/ConfigurableSource.kt index 6b3f99ace..ed82ff6f8 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/source/ConfigurableSource.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/source/ConfigurableSource.kt @@ -1,6 +1,6 @@ package eu.kanade.tachiyomi.source -import android.support.v7.preference.PreferenceScreen +import androidx.preference.PreferenceScreen interface ConfigurableSource : Source { diff --git a/app/src/main/java/eu/kanade/tachiyomi/source/online/HttpSource.kt b/app/src/main/java/eu/kanade/tachiyomi/source/online/HttpSource.kt index c47a9d380..29d226788 100755 --- a/app/src/main/java/eu/kanade/tachiyomi/source/online/HttpSource.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/source/online/HttpSource.kt @@ -1,395 +1,397 @@ -package eu.kanade.tachiyomi.source.online - -import android.app.Application -import eu.kanade.tachiyomi.data.preference.PreferencesHelper -import eu.kanade.tachiyomi.data.preference.getOrDefault -import eu.kanade.tachiyomi.network.* -import eu.kanade.tachiyomi.source.CatalogueSource -import eu.kanade.tachiyomi.source.model.* -import exh.patch.injectPatches -import exh.source.DelegatedHttpSource -import okhttp3.* -import rx.Observable -import uy.kohesive.injekt.Injekt -import uy.kohesive.injekt.api.get -import java.lang.Exception -import java.net.URI -import java.net.URISyntaxException -import java.security.MessageDigest - -/** - * A simple implementation for sources from a website. - */ -abstract class HttpSource : CatalogueSource { - - /** - * Network service. - */ - protected val network: NetworkHelper by lazy { - val original = Injekt.get() - object : NetworkHelper(Injekt.get()) { - override val client: OkHttpClient? - get() = delegate?.networkHttpClient ?: original.client - .newBuilder() - .injectPatches { id } - .build() - - override val cloudflareClient: OkHttpClient? - get() = delegate?.networkCloudflareClient ?: original.cloudflareClient - .newBuilder() - .injectPatches { id } - .build() - - override val cookieManager: AndroidCookieJar - get() = original.cookieManager - } - } - -// /** -// * Preferences that a source may need. -// */ -// val preferences: SharedPreferences by lazy { -// Injekt.get().getSharedPreferences("source_$id", Context.MODE_PRIVATE) -// } - - /** - * Base url of the website without the trailing slash, like: http://mysite.com - */ - abstract val baseUrl: String - - /** - * Version id used to generate the source id. If the site completely changes and urls are - * incompatible, you may increase this value and it'll be considered as a new source. - */ - open val versionId = 1 - - /** - * Id of the source. By default it uses a generated id using the first 16 characters (64 bits) - * of the MD5 of the string: sourcename/language/versionId - * Note the generated id sets the sign bit to 0. - */ - override val id by lazy { - val key = "${name.toLowerCase()}/$lang/$versionId" - val bytes = MessageDigest.getInstance("MD5").digest(key.toByteArray()) - (0..7).map { bytes[it].toLong() and 0xff shl 8 * (7 - it) }.reduce(Long::or) and Long.MAX_VALUE - } - - /** - * Headers used for requests. - */ - val headers: Headers by lazy { headersBuilder().build() } - - /** - * Default network client for doing requests. - */ - open val client: OkHttpClient - get() = delegate?.baseHttpClient ?: network.client - - /** - * Headers builder for requests. Implementations can override this method for custom headers. - */ - open protected fun headersBuilder() = Headers.Builder().apply { - add("User-Agent", "Mozilla/5.0 (Windows NT 6.3; WOW64)") - } - - /** - * Visible name of the source. - */ - override fun toString() = "$name (${lang.toUpperCase()})" - - /** - * Returns an observable containing a page with a list of manga. Normally it's not needed to - * override this method. - * - * @param page the page number to retrieve. - */ - override fun fetchPopularManga(page: Int): Observable { - return client.newCall(popularMangaRequest(page)) - .asObservableSuccess() - .map { response -> - popularMangaParse(response) - } - } - - /** - * Returns the request for the popular manga given the page. - * - * @param page the page number to retrieve. - */ - abstract protected fun popularMangaRequest(page: Int): Request - - /** - * Parses the response from the site and returns a [MangasPage] object. - * - * @param response the response from the site. - */ - abstract protected fun popularMangaParse(response: Response): MangasPage - - /** - * Returns an observable containing a page with a list of manga. Normally it's not needed to - * override this method. - * - * @param page the page number to retrieve. - * @param query the search query. - * @param filters the list of filters to apply. - */ - override fun fetchSearchManga(page: Int, query: String, filters: FilterList): Observable { - return client.newCall(searchMangaRequest(page, query, filters)) - .asObservableSuccess() - .map { response -> - searchMangaParse(response) - } - } - - /** - * Returns the request for the search manga given the page. - * - * @param page the page number to retrieve. - * @param query the search query. - * @param filters the list of filters to apply. - */ - abstract protected fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request - - /** - * Parses the response from the site and returns a [MangasPage] object. - * - * @param response the response from the site. - */ - abstract protected fun searchMangaParse(response: Response): MangasPage - - /** - * Returns an observable containing a page with a list of latest manga updates. - * - * @param page the page number to retrieve. - */ - override fun fetchLatestUpdates(page: Int): Observable { - return client.newCall(latestUpdatesRequest(page)) - .asObservableSuccess() - .map { response -> - latestUpdatesParse(response) - } - } - - /** - * Returns the request for latest manga given the page. - * - * @param page the page number to retrieve. - */ - abstract protected fun latestUpdatesRequest(page: Int): Request - - /** - * Parses the response from the site and returns a [MangasPage] object. - * - * @param response the response from the site. - */ - abstract protected fun latestUpdatesParse(response: Response): MangasPage - - /** - * Returns an observable with the updated details for a manga. Normally it's not needed to - * override this method. - * - * @param manga the manga to be updated. - */ - override fun fetchMangaDetails(manga: SManga): Observable { - return client.newCall(mangaDetailsRequest(manga)) - .asObservableSuccess() - .map { response -> - mangaDetailsParse(response).apply { initialized = true } - } - } - - /** - * Returns the request for the details of a manga. Override only if it's needed to change the - * url, send different headers or request method like POST. - * - * @param manga the manga to be updated. - */ - open fun mangaDetailsRequest(manga: SManga): Request { - return GET(baseUrl + manga.url, headers) - } - - /** - * Parses the response from the site and returns the details of a manga. - * - * @param response the response from the site. - */ - abstract protected fun mangaDetailsParse(response: Response): SManga - - /** - * Returns an observable with the updated chapter list for a manga. Normally it's not needed to - * override this method. If a manga is licensed an empty chapter list observable is returned - * - * @param manga the manga to look for chapters. - */ - override fun fetchChapterList(manga: SManga): Observable> { - if (manga.status != SManga.LICENSED) { - return client.newCall(chapterListRequest(manga)) - .asObservableSuccess() - .map { response -> - chapterListParse(response) - } - } else { - return Observable.error(Exception("Licensed - No chapters to show")) - } - } - - /** - * Returns the request for updating the chapter list. Override only if it's needed to override - * the url, send different headers or request method like POST. - * - * @param manga the manga to look for chapters. - */ - open protected fun chapterListRequest(manga: SManga): Request { - return GET(baseUrl + manga.url, headers) - } - - /** - * Parses the response from the site and returns a list of chapters. - * - * @param response the response from the site. - */ - abstract protected fun chapterListParse(response: Response): List - - /** - * Returns an observable with the page list for a chapter. - * - * @param chapter the chapter whose page list has to be fetched. - */ - override fun fetchPageList(chapter: SChapter): Observable> { - return client.newCall(pageListRequest(chapter)) - .asObservableSuccess() - .map { response -> - pageListParse(response) - } - } - - /** - * Returns the request for getting the page list. Override only if it's needed to override the - * url, send different headers or request method like POST. - * - * @param chapter the chapter whose page list has to be fetched. - */ - open protected fun pageListRequest(chapter: SChapter): Request { - return GET(baseUrl + chapter.url, headers) - } - - /** - * Parses the response from the site and returns a list of pages. - * - * @param response the response from the site. - */ - abstract protected fun pageListParse(response: Response): List - - /** - * Returns an observable with the page containing the source url of the image. If there's any - * error, it will return null instead of throwing an exception. - * - * @param page the page whose source image has to be fetched. - */ - open fun fetchImageUrl(page: Page): Observable { - return client.newCall(imageUrlRequest(page)) - .asObservableSuccess() - .map { imageUrlParse(it) } - } - - /** - * Returns the request for getting the url to the source image. Override only if it's needed to - * override the url, send different headers or request method like POST. - * - * @param page the chapter whose page list has to be fetched - */ - open protected fun imageUrlRequest(page: Page): Request { - return GET(page.url, headers) - } - - /** - * Parses the response from the site and returns the absolute url to the source image. - * - * @param response the response from the site. - */ - abstract protected fun imageUrlParse(response: Response): String - - /** - * Returns an observable with the response of the source image. - * - * @param page the page whose source image has to be downloaded. - */ - open fun fetchImage(page: Page): Observable { - return client.newCallWithProgress(imageRequest(page), page) - .asObservableSuccess() - } - - /** - * Returns the request for getting the source image. Override only if it's needed to override - * the url, send different headers or request method like POST. - * - * @param page the chapter whose page list has to be fetched - */ - open protected fun imageRequest(page: Page): Request { - return GET(page.imageUrl!!, headers) - } - - /** - * Assigns the url of the chapter without the scheme and domain. It saves some redundancy from - * database and the urls could still work after a domain change. - * - * @param url the full url to the chapter. - */ - fun SChapter.setUrlWithoutDomain(url: String) { - this.url = getUrlWithoutDomain(url) - } - - /** - * Assigns the url of the manga without the scheme and domain. It saves some redundancy from - * database and the urls could still work after a domain change. - * - * @param url the full url to the manga. - */ - fun SManga.setUrlWithoutDomain(url: String) { - this.url = getUrlWithoutDomain(url) - } - - /** - * Returns the url of the given string without the scheme and domain. - * - * @param orig the full url. - */ - private fun getUrlWithoutDomain(orig: String): String { - try { - val uri = URI(orig) - var out = uri.path - if (uri.query != null) - out += "?" + uri.query - if (uri.fragment != null) - out += "#" + uri.fragment - return out - } catch (e: URISyntaxException) { - return orig - } - } - - /** - * Called before inserting a new chapter into database. Use it if you need to override chapter - * fields, like the title or the chapter number. Do not change anything to [manga]. - * - * @param chapter the chapter to be added. - * @param manga the manga of the chapter. - */ - open fun prepareNewChapter(chapter: SChapter, manga: SManga) { - } - - /** - * Returns the list of filters for the source. - */ - override fun getFilterList() = FilterList() - - // EXH --> - private var delegate: DelegatedHttpSource? = null - get() = if(Injekt.get().eh_delegateSources().getOrDefault()) - field - else null - fun bindDelegate(delegate: DelegatedHttpSource) { - this.delegate = delegate - } - // EXH <-- -} +package eu.kanade.tachiyomi.source.online + +import android.app.Application +import eu.kanade.tachiyomi.data.preference.PreferencesHelper +import eu.kanade.tachiyomi.data.preference.getOrDefault +import eu.kanade.tachiyomi.network.* +import eu.kanade.tachiyomi.source.CatalogueSource +import eu.kanade.tachiyomi.source.model.* +import exh.patch.injectPatches +import exh.source.DelegatedHttpSource +import okhttp3.Headers +import okhttp3.OkHttpClient +import okhttp3.Request +import okhttp3.Response +import rx.Observable +import uy.kohesive.injekt.Injekt +import uy.kohesive.injekt.api.get +import java.net.URI +import java.net.URISyntaxException +import java.security.MessageDigest + +/** + * A simple implementation for sources from a website. + */ +abstract class HttpSource : CatalogueSource { + + /** + * Network service. + */ + protected val network: NetworkHelper by lazy { + val original = Injekt.get() + object : NetworkHelper(Injekt.get()) { + override val client: OkHttpClient + get() = delegate?.networkHttpClient ?: original.client + .newBuilder() + .injectPatches { id } + .build() + + override val cloudflareClient: OkHttpClient + get() = delegate?.networkCloudflareClient ?: original.cloudflareClient + .newBuilder() + .injectPatches { id } + .build() + + override val cookieManager: AndroidCookieJar + get() = original.cookieManager + } + } + +// /** +// * Preferences that a source may need. +// */ +// val preferences: SharedPreferences by lazy { +// Injekt.get().getSharedPreferences("source_$id", Context.MODE_PRIVATE) +// } + + /** + * Base url of the website without the trailing slash, like: http://mysite.com + */ + abstract val baseUrl: String + + /** + * Version id used to generate the source id. If the site completely changes and urls are + * incompatible, you may increase this value and it'll be considered as a new source. + */ + open val versionId = 1 + + /** + * Id of the source. By default it uses a generated id using the first 16 characters (64 bits) + * of the MD5 of the string: sourcename/language/versionId + * Note the generated id sets the sign bit to 0. + */ + override val id by lazy { + val key = "${name.toLowerCase()}/$lang/$versionId" + val bytes = MessageDigest.getInstance("MD5").digest(key.toByteArray()) + (0..7).map { bytes[it].toLong() and 0xff shl 8 * (7 - it) }.reduce(Long::or) and Long.MAX_VALUE + } + + /** + * Headers used for requests. + */ + val headers: Headers by lazy { headersBuilder().build() } + + /** + * Default network client for doing requests. + */ + open val client: OkHttpClient + get() = delegate?.baseHttpClient ?: network.client + + /** + * Headers builder for requests. Implementations can override this method for custom headers. + */ + open protected fun headersBuilder() = Headers.Builder().apply { + add("User-Agent", "Mozilla/5.0 (Windows NT 6.3; WOW64)") + } + + /** + * Visible name of the source. + */ + override fun toString() = "$name (${lang.toUpperCase()})" + + /** + * Returns an observable containing a page with a list of manga. Normally it's not needed to + * override this method. + * + * @param page the page number to retrieve. + */ + override fun fetchPopularManga(page: Int): Observable { + return client.newCall(popularMangaRequest(page)) + .asObservableSuccess() + .map { response -> + popularMangaParse(response) + } + } + + /** + * Returns the request for the popular manga given the page. + * + * @param page the page number to retrieve. + */ + abstract protected fun popularMangaRequest(page: Int): Request + + /** + * Parses the response from the site and returns a [MangasPage] object. + * + * @param response the response from the site. + */ + abstract protected fun popularMangaParse(response: Response): MangasPage + + /** + * Returns an observable containing a page with a list of manga. Normally it's not needed to + * override this method. + * + * @param page the page number to retrieve. + * @param query the search query. + * @param filters the list of filters to apply. + */ + override fun fetchSearchManga(page: Int, query: String, filters: FilterList): Observable { + return client.newCall(searchMangaRequest(page, query, filters)) + .asObservableSuccess() + .map { response -> + searchMangaParse(response) + } + } + + /** + * Returns the request for the search manga given the page. + * + * @param page the page number to retrieve. + * @param query the search query. + * @param filters the list of filters to apply. + */ + abstract protected fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request + + /** + * Parses the response from the site and returns a [MangasPage] object. + * + * @param response the response from the site. + */ + abstract protected fun searchMangaParse(response: Response): MangasPage + + /** + * Returns an observable containing a page with a list of latest manga updates. + * + * @param page the page number to retrieve. + */ + override fun fetchLatestUpdates(page: Int): Observable { + return client.newCall(latestUpdatesRequest(page)) + .asObservableSuccess() + .map { response -> + latestUpdatesParse(response) + } + } + + /** + * Returns the request for latest manga given the page. + * + * @param page the page number to retrieve. + */ + abstract protected fun latestUpdatesRequest(page: Int): Request + + /** + * Parses the response from the site and returns a [MangasPage] object. + * + * @param response the response from the site. + */ + abstract protected fun latestUpdatesParse(response: Response): MangasPage + + /** + * Returns an observable with the updated details for a manga. Normally it's not needed to + * override this method. + * + * @param manga the manga to be updated. + */ + override fun fetchMangaDetails(manga: SManga): Observable { + return client.newCall(mangaDetailsRequest(manga)) + .asObservableSuccess() + .map { response -> + mangaDetailsParse(response).apply { initialized = true } + } + } + + /** + * Returns the request for the details of a manga. Override only if it's needed to change the + * url, send different headers or request method like POST. + * + * @param manga the manga to be updated. + */ + open fun mangaDetailsRequest(manga: SManga): Request { + return GET(baseUrl + manga.url, headers) + } + + /** + * Parses the response from the site and returns the details of a manga. + * + * @param response the response from the site. + */ + abstract protected fun mangaDetailsParse(response: Response): SManga + + /** + * Returns an observable with the updated chapter list for a manga. Normally it's not needed to + * override this method. If a manga is licensed an empty chapter list observable is returned + * + * @param manga the manga to look for chapters. + */ + override fun fetchChapterList(manga: SManga): Observable> { + if (manga.status != SManga.LICENSED) { + return client.newCall(chapterListRequest(manga)) + .asObservableSuccess() + .map { response -> + chapterListParse(response) + } + } else { + return Observable.error(Exception("Licensed - No chapters to show")) + } + } + + /** + * Returns the request for updating the chapter list. Override only if it's needed to override + * the url, send different headers or request method like POST. + * + * @param manga the manga to look for chapters. + */ + open protected fun chapterListRequest(manga: SManga): Request { + return GET(baseUrl + manga.url, headers) + } + + /** + * Parses the response from the site and returns a list of chapters. + * + * @param response the response from the site. + */ + abstract protected fun chapterListParse(response: Response): List + + /** + * Returns an observable with the page list for a chapter. + * + * @param chapter the chapter whose page list has to be fetched. + */ + override fun fetchPageList(chapter: SChapter): Observable> { + return client.newCall(pageListRequest(chapter)) + .asObservableSuccess() + .map { response -> + pageListParse(response) + } + } + + /** + * Returns the request for getting the page list. Override only if it's needed to override the + * url, send different headers or request method like POST. + * + * @param chapter the chapter whose page list has to be fetched. + */ + open protected fun pageListRequest(chapter: SChapter): Request { + return GET(baseUrl + chapter.url, headers) + } + + /** + * Parses the response from the site and returns a list of pages. + * + * @param response the response from the site. + */ + abstract protected fun pageListParse(response: Response): List + + /** + * Returns an observable with the page containing the source url of the image. If there's any + * error, it will return null instead of throwing an exception. + * + * @param page the page whose source image has to be fetched. + */ + open fun fetchImageUrl(page: Page): Observable { + return client.newCall(imageUrlRequest(page)) + .asObservableSuccess() + .map { imageUrlParse(it) } + } + + /** + * Returns the request for getting the url to the source image. Override only if it's needed to + * override the url, send different headers or request method like POST. + * + * @param page the chapter whose page list has to be fetched + */ + open protected fun imageUrlRequest(page: Page): Request { + return GET(page.url, headers) + } + + /** + * Parses the response from the site and returns the absolute url to the source image. + * + * @param response the response from the site. + */ + abstract protected fun imageUrlParse(response: Response): String + + /** + * Returns an observable with the response of the source image. + * + * @param page the page whose source image has to be downloaded. + */ + open fun fetchImage(page: Page): Observable { + return client.newCallWithProgress(imageRequest(page), page) + .asObservableSuccess() + } + + /** + * Returns the request for getting the source image. Override only if it's needed to override + * the url, send different headers or request method like POST. + * + * @param page the chapter whose page list has to be fetched + */ + open protected fun imageRequest(page: Page): Request { + return GET(page.imageUrl!!, headers) + } + + /** + * Assigns the url of the chapter without the scheme and domain. It saves some redundancy from + * database and the urls could still work after a domain change. + * + * @param url the full url to the chapter. + */ + fun SChapter.setUrlWithoutDomain(url: String) { + this.url = getUrlWithoutDomain(url) + } + + /** + * Assigns the url of the manga without the scheme and domain. It saves some redundancy from + * database and the urls could still work after a domain change. + * + * @param url the full url to the manga. + */ + fun SManga.setUrlWithoutDomain(url: String) { + this.url = getUrlWithoutDomain(url) + } + + /** + * Returns the url of the given string without the scheme and domain. + * + * @param orig the full url. + */ + private fun getUrlWithoutDomain(orig: String): String { + try { + val uri = URI(orig) + var out = uri.path + if (uri.query != null) + out += "?" + uri.query + if (uri.fragment != null) + out += "#" + uri.fragment + return out + } catch (e: URISyntaxException) { + return orig + } + } + + /** + * Called before inserting a new chapter into database. Use it if you need to override chapter + * fields, like the title or the chapter number. Do not change anything to [manga]. + * + * @param chapter the chapter to be added. + * @param manga the manga of the chapter. + */ + open fun prepareNewChapter(chapter: SChapter, manga: SManga) { + } + + /** + * Returns the list of filters for the source. + */ + override fun getFilterList() = FilterList() + + // EXH --> + private var delegate: DelegatedHttpSource? = null + get() = if(Injekt.get().eh_delegateSources().getOrDefault()) + field + else null + fun bindDelegate(delegate: DelegatedHttpSource) { + this.delegate = delegate + } + // EXH <-- +} diff --git a/app/src/main/java/eu/kanade/tachiyomi/source/online/all/EHentai.kt b/app/src/main/java/eu/kanade/tachiyomi/source/online/all/EHentai.kt index 6a9ae7d10..a320f2e4a 100755 --- a/app/src/main/java/eu/kanade/tachiyomi/source/online/all/EHentai.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/source/online/all/EHentai.kt @@ -18,35 +18,36 @@ import eu.kanade.tachiyomi.source.online.HttpSource import eu.kanade.tachiyomi.source.online.LewdSource import eu.kanade.tachiyomi.source.online.UrlImportableSource import eu.kanade.tachiyomi.util.asJsoup +import exh.debug.DebugToggles import exh.eh.EHentaiUpdateHelper +import exh.eh.EHentaiUpdateWorkerConstants +import exh.eh.GalleryEntry import exh.metadata.EX_DATE_FORMAT import exh.metadata.metadata.EHentaiSearchMetadata import exh.metadata.metadata.EHentaiSearchMetadata.Companion.EH_GENRE_NAMESPACE import exh.metadata.metadata.EHentaiSearchMetadata.Companion.TAG_TYPE_LIGHT import exh.metadata.metadata.EHentaiSearchMetadata.Companion.TAG_TYPE_NORMAL import exh.metadata.metadata.base.RaisedSearchMetadata.Companion.TAG_TYPE_VIRTUAL +import exh.metadata.metadata.base.RaisedTag import exh.metadata.nullIfBlank import exh.metadata.parseHumanReadableByteCount -import exh.debug.DebugToggles import exh.ui.login.LoginController import exh.util.UriFilter import exh.util.UriGroup import exh.util.ignore import exh.util.urlImportFetchSearchManga +import kotlinx.coroutines.runBlocking import okhttp3.* +import okhttp3.HttpUrl.Companion.toHttpUrlOrNull +import okhttp3.MediaType.Companion.toMediaTypeOrNull import org.jsoup.nodes.Document import org.jsoup.nodes.Element +import org.jsoup.nodes.TextNode import rx.Observable +import rx.Single import uy.kohesive.injekt.injectLazy import java.net.URLEncoder import java.util.* -import exh.metadata.metadata.base.RaisedTag -import exh.eh.EHentaiUpdateWorkerConstants -import exh.eh.GalleryEntry -import kotlinx.coroutines.runBlocking -import org.jsoup.nodes.TextNode -import rx.Single -import java.lang.RuntimeException // TODO Consider gallery updating when doing tabbed browsing class EHentai(override val id: Long, @@ -108,11 +109,11 @@ class EHentai(override val id: Long, }) } - val parsedLocation = HttpUrl.parse(doc.location()) + val parsedLocation = doc.location().toHttpUrlOrNull() //Add to page if required val hasNextPage = if(parsedLocation == null - || !parsedLocation.queryParameterNames().contains(REVERSE_PARAM)) { + || !parsedLocation.queryParameterNames.contains(REVERSE_PARAM)) { select("a[onclick=return false]").last()?.let { it.text() == ">" } ?: false @@ -151,7 +152,7 @@ class EHentai(override val id: Long, throttleFunc() val resp = client.newCall(exGet(baseUrl + url)).execute() - if (!resp.isSuccessful) error("HTTP error (${resp.code()})!") + if (!resp.isSuccessful) error("HTTP error (${resp.code})!") doc = resp.asJsoup() val parentLink = doc!!.select("#gdd .gdt1").find { el -> @@ -344,10 +345,10 @@ class EHentai(override val id: Long, } else { response.close() - if(response.code() == 404) { + if (response.code == 404) { throw GalleryNotFoundException(stacktrace) } else { - throw Exception("HTTP error ${response.code()}", stacktrace) + throw Exception("HTTP error ${response.code}", stacktrace) } } } @@ -707,7 +708,7 @@ class EHentai(override val id: Long, val outJson = JsonParser().parse(client.newCall(Request.Builder() .url(EH_API_BASE) .post(RequestBody.create(JSON, json.toString())) - .build()).execute().body()!!.string()).obj + .build()).execute().body!!.string()).obj val obj = outJson["tokenlist"].array.first() return "${uri.scheme}://${uri.host}/g/${obj["gid"].int}/${obj["token"].string}/" @@ -720,7 +721,7 @@ class EHentai(override val id: Long, private const val REVERSE_PARAM = "TEH_REVERSE" private const val EH_API_BASE = "https://api.e-hentai.org/api.php" - private val JSON = MediaType.parse("application/json; charset=utf-8")!! + private val JSON = "application/json; charset=utf-8".toMediaTypeOrNull()!! private val FAVORITES_BORDER_HEX_COLORS = listOf( "000", diff --git a/app/src/main/java/eu/kanade/tachiyomi/source/online/all/Hitomi.kt b/app/src/main/java/eu/kanade/tachiyomi/source/online/all/Hitomi.kt index 486561a15..027233cf7 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/source/online/all/Hitomi.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/source/online/all/Hitomi.kt @@ -15,7 +15,6 @@ import eu.kanade.tachiyomi.source.online.HttpSource import eu.kanade.tachiyomi.source.online.LewdSource import eu.kanade.tachiyomi.source.online.UrlImportableSource import eu.kanade.tachiyomi.util.asJsoup -import exh.GalleryAddEvent import exh.HITOMI_SOURCE_ID import exh.hitomi.HitomiNozomi import exh.metadata.metadata.HitomiSearchMetadata @@ -265,7 +264,7 @@ class Hitomi : HttpSource(), LewdSource, UrlImpo val range = response.header("Content-Range")!! val total = range.substringAfter('/').toLong() val end = range.substringBefore('/').substringAfter('-').toLong() - val body = response.body()!! + val body = response.body!! return parseNozomiPage(body.bytes()) .map { MangasPage(it, end < total - 1) @@ -360,8 +359,8 @@ class Hitomi : HttpSource(), LewdSource, UrlImpo * @param response the response from the site. */ override fun pageListParse(response: Response): List { - val hlId = response.request().url().pathSegments().last().removeSuffix(".js").toLong() - val str = response.body()!!.string() + val hlId = response.request.url.pathSegments.last().removeSuffix(".js").toLong() + val str = response.body!!.string() val json = jsonParser.parse(str.removePrefix("var galleryinfo =")) return json.array.mapIndexed { index, jsonElement -> Page( @@ -385,7 +384,7 @@ class Hitomi : HttpSource(), LewdSource, UrlImpo override fun imageRequest(page: Page): Request { val request = super.imageRequest(page) - val hlId = request.url().pathSegments().let { + val hlId = request.url.pathSegments.let { it[it.lastIndex - 1] } return request.newBuilder() diff --git a/app/src/main/java/eu/kanade/tachiyomi/source/online/all/NHentai.kt b/app/src/main/java/eu/kanade/tachiyomi/source/online/all/NHentai.kt index f0b7d9bc8..cf1813654 100755 --- a/app/src/main/java/eu/kanade/tachiyomi/source/online/all/NHentai.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/source/online/all/NHentai.kt @@ -3,8 +3,6 @@ package eu.kanade.tachiyomi.source.online.all import android.content.Context import android.net.Uri import com.github.salomonbrys.kotson.* -import com.google.gson.JsonElement -import com.google.gson.JsonNull import com.google.gson.JsonParser import eu.kanade.tachiyomi.BuildConfig import eu.kanade.tachiyomi.R @@ -15,12 +13,11 @@ import eu.kanade.tachiyomi.source.online.HttpSource import eu.kanade.tachiyomi.source.online.LewdSource import eu.kanade.tachiyomi.source.online.UrlImportableSource import eu.kanade.tachiyomi.util.asJsoup -import exh.GalleryAddEvent import exh.NHENTAI_SOURCE_ID import exh.metadata.metadata.NHentaiSearchMetadata import exh.metadata.metadata.NHentaiSearchMetadata.Companion.TAG_TYPE_DEFAULT import exh.metadata.metadata.base.RaisedTag -import exh.util.* +import exh.util.urlImportFetchSearchManga import okhttp3.Request import okhttp3.Response import rx.Observable @@ -153,17 +150,17 @@ class NHentai(context: Context) : HttpSource(), LewdSource, UrlIm */ override fun pageListParse(response: Response): List { val doc = response.asJsoup() - val basePath = listOf("data") + response.request().url().pathSegments() + val basePath = listOf("data") + response.request.url.pathSegments val scripts = doc.getElementsByTag("script").map { it.data() } for(script in scripts) { val totalPages = TOTAL_PAGES_REGEX.find(script)?.groupValues?.getOrNull(1)?.toIntOrNull() ?: continue diff --git a/app/src/main/java/eu/kanade/tachiyomi/source/online/english/HentaiCafe.kt b/app/src/main/java/eu/kanade/tachiyomi/source/online/english/HentaiCafe.kt index 051e743b4..63557917c 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/source/online/english/HentaiCafe.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/source/online/english/HentaiCafe.kt @@ -15,7 +15,7 @@ import exh.metadata.metadata.base.RaisedSearchMetadata.Companion.TAG_TYPE_VIRTUA import exh.metadata.metadata.base.RaisedTag import exh.source.DelegatedHttpSource import exh.util.urlImportFetchSearchManga -import okhttp3.HttpUrl +import okhttp3.HttpUrl.Companion.toHttpUrlOrNull import org.jsoup.nodes.Document import rx.Observable @@ -72,7 +72,7 @@ class HentaiCafe(delegate: HttpSource) : DelegatedHttpSource(delegate), RaisedTag("artist", it, TAG_TYPE_VIRTUAL) } - readerId = HttpUrl.parse(input.select("[title=Read]").attr("href"))!!.pathSegments()[2] + readerId = input.select("[title=Read]").attr("href").toHttpUrlOrNull()!!.pathSegments[2] } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/source/online/english/Tsumino.kt b/app/src/main/java/eu/kanade/tachiyomi/source/online/english/Tsumino.kt index 4b88422b8..1e22dc794 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/source/online/english/Tsumino.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/source/online/english/Tsumino.kt @@ -16,25 +16,23 @@ import eu.kanade.tachiyomi.source.online.ParsedHttpSource import eu.kanade.tachiyomi.source.online.UrlImportableSource import eu.kanade.tachiyomi.util.asJsoup import eu.kanade.tachiyomi.util.toast -import exh.GalleryAddEvent import exh.TSUMINO_SOURCE_ID -import exh.ui.captcha.ActionCompletionVerifier -import exh.ui.captcha.BrowserActionActivity import exh.metadata.metadata.TsuminoSearchMetadata import exh.metadata.metadata.TsuminoSearchMetadata.Companion.BASE_URL import exh.metadata.metadata.TsuminoSearchMetadata.Companion.TAG_TYPE_DEFAULT import exh.metadata.metadata.base.RaisedSearchMetadata.Companion.TAG_TYPE_VIRTUAL import exh.metadata.metadata.base.RaisedTag +import exh.ui.captcha.ActionCompletionVerifier +import exh.ui.captcha.BrowserActionActivity import exh.util.urlImportFetchSearchManga import okhttp3.* import org.jsoup.nodes.Document import org.jsoup.nodes.Element import rx.Observable -import rx.schedulers.Schedulers import uy.kohesive.injekt.injectLazy +import java.io.IOException import java.text.SimpleDateFormat import java.util.* -import java.io.IOException class Tsumino(private val context: Context): ParsedHttpSource(), LewdSource, @@ -127,7 +125,7 @@ class Tsumino(private val context: Context): ParsedHttpSource(), } fun genericMangaParse(response: Response): MangasPage { - val json = jsonParser.parse(response.body()!!.string()!!).asJsonObject + val json = jsonParser.parse(response.body!!.string()!!).asJsonObject val hasNextPage = json["pageNumber"].int < json["pageCount"].int val manga = json["data"].array.map { diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/base/activity/BaseActivity.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/base/activity/BaseActivity.kt index 171cc94cb..3740f3af0 100755 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/base/activity/BaseActivity.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/base/activity/BaseActivity.kt @@ -1,6 +1,6 @@ package eu.kanade.tachiyomi.ui.base.activity -import android.support.v7.app.AppCompatActivity +import androidx.appcompat.app.AppCompatActivity import eu.kanade.tachiyomi.util.LocaleHelper abstract class BaseActivity : AppCompatActivity() { diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/base/controller/BaseController.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/base/controller/BaseController.kt index acf2ff1e8..bbe4f36d1 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/base/controller/BaseController.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/base/controller/BaseController.kt @@ -1,11 +1,11 @@ package eu.kanade.tachiyomi.ui.base.controller import android.os.Bundle -import android.support.v7.app.AppCompatActivity import android.view.LayoutInflater import android.view.MenuItem import android.view.View import android.view.ViewGroup +import androidx.appcompat.app.AppCompatActivity import com.bluelinelabs.conductor.Controller import com.bluelinelabs.conductor.ControllerChangeHandler import com.bluelinelabs.conductor.ControllerChangeType diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/base/controller/ConductorExtensions.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/base/controller/ConductorExtensions.kt index 492c5a280..79a6ce86a 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/base/controller/ConductorExtensions.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/base/controller/ConductorExtensions.kt @@ -2,7 +2,7 @@ package eu.kanade.tachiyomi.ui.base.controller import android.content.pm.PackageManager.PERMISSION_GRANTED import android.os.Build -import android.support.v4.content.ContextCompat +import androidx.core.content.ContextCompat import com.bluelinelabs.conductor.Controller import com.bluelinelabs.conductor.Router import com.bluelinelabs.conductor.RouterTransaction diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/base/controller/DialogController.java b/app/src/main/java/eu/kanade/tachiyomi/ui/base/controller/DialogController.java index db8efbd83..95adc2038 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/base/controller/DialogController.java +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/base/controller/DialogController.java @@ -3,8 +3,6 @@ package eu.kanade.tachiyomi.ui.base.controller; import android.app.Dialog; import android.content.DialogInterface; import android.os.Bundle; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; @@ -14,6 +12,9 @@ import com.bluelinelabs.conductor.Router; import com.bluelinelabs.conductor.RouterTransaction; import com.bluelinelabs.conductor.changehandler.SimpleSwapChangeHandler; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + /** * A controller that displays a dialog window, floating on top of its activity's window. * This is a wrapper over {@link Dialog} object like {@link android.app.DialogFragment}. diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/base/controller/RxController.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/base/controller/RxController.kt index 2b2e1ecc6..109efb119 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/base/controller/RxController.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/base/controller/RxController.kt @@ -1,8 +1,8 @@ package eu.kanade.tachiyomi.ui.base.controller import android.os.Bundle -import android.support.annotation.CallSuper import android.view.View +import androidx.annotation.CallSuper import rx.Observable import rx.Subscription import rx.subscriptions.CompositeSubscription diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/base/controller/SecondaryDrawerController.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/base/controller/SecondaryDrawerController.kt index ba2ce016a..ad8180848 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/base/controller/SecondaryDrawerController.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/base/controller/SecondaryDrawerController.kt @@ -1,11 +1,10 @@ package eu.kanade.tachiyomi.ui.base.controller -import android.support.v4.widget.DrawerLayout import android.view.ViewGroup interface SecondaryDrawerController { - fun createSecondaryDrawer(drawer: DrawerLayout): ViewGroup? + fun createSecondaryDrawer(drawer: androidx.drawerlayout.widget.DrawerLayout): ViewGroup? - fun cleanupSecondaryDrawer(drawer: DrawerLayout) + fun cleanupSecondaryDrawer(drawer: androidx.drawerlayout.widget.DrawerLayout) } \ No newline at end of file diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/base/controller/TabbedController.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/base/controller/TabbedController.kt index 02fba36c3..f70d2e11f 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/base/controller/TabbedController.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/base/controller/TabbedController.kt @@ -1,6 +1,6 @@ package eu.kanade.tachiyomi.ui.base.controller -import android.support.design.widget.TabLayout +import com.google.android.material.tabs.TabLayout interface TabbedController { diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/base/holder/BaseViewHolder.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/base/holder/BaseViewHolder.kt index c809b7eeb..9a69e171b 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/base/holder/BaseViewHolder.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/base/holder/BaseViewHolder.kt @@ -1,10 +1,9 @@ package eu.kanade.tachiyomi.ui.base.holder -import android.support.v7.widget.RecyclerView import android.view.View import kotlinx.android.extensions.LayoutContainer -abstract class BaseViewHolder(view: View) : RecyclerView.ViewHolder(view), LayoutContainer { +abstract class BaseViewHolder(view: View) : androidx.recyclerview.widget.RecyclerView.ViewHolder(view), LayoutContainer { override val containerView: View? get() = itemView diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/base/presenter/NucleusConductorDelegate.java b/app/src/main/java/eu/kanade/tachiyomi/ui/base/presenter/NucleusConductorDelegate.java index 5210a3a2a..328a01e67 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/base/presenter/NucleusConductorDelegate.java +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/base/presenter/NucleusConductorDelegate.java @@ -1,61 +1,61 @@ -package eu.kanade.tachiyomi.ui.base.presenter; - -import android.os.Bundle; -import android.support.annotation.Nullable; - -import nucleus.factory.PresenterFactory; -import nucleus.presenter.Presenter; - -public class NucleusConductorDelegate

{ - - @Nullable private P presenter; - @Nullable private Bundle bundle; - - private PresenterFactory

factory; - - public NucleusConductorDelegate(PresenterFactory

creator) { - this.factory = creator; - } - - public P getPresenter() { - if (presenter == null) { - presenter = factory.createPresenter(); - presenter.create(bundle); - bundle = null; - } - return presenter; - } - - Bundle onSaveInstanceState() { - Bundle bundle = new Bundle(); -// getPresenter(); // Workaround a crash related to saving instance state with child routers - if (presenter != null) { - presenter.save(bundle); - } - return bundle; - } - - void onRestoreInstanceState(Bundle presenterState) { - bundle = presenterState; - } - - void onTakeView(Object view) { - getPresenter(); - if (presenter != null) { - //noinspection unchecked - presenter.takeView(view); - } - } - - void onDropView() { - if (presenter != null) { - presenter.dropView(); - } - } - - void onDestroy() { - if (presenter != null) { - presenter.destroy(); - } - } -} +package eu.kanade.tachiyomi.ui.base.presenter; + +import android.os.Bundle; + +import androidx.annotation.Nullable; +import nucleus.factory.PresenterFactory; +import nucleus.presenter.Presenter; + +public class NucleusConductorDelegate

{ + + @Nullable private P presenter; + @Nullable private Bundle bundle; + + private PresenterFactory

factory; + + public NucleusConductorDelegate(PresenterFactory

creator) { + this.factory = creator; + } + + public P getPresenter() { + if (presenter == null) { + presenter = factory.createPresenter(); + presenter.create(bundle); + bundle = null; + } + return presenter; + } + + Bundle onSaveInstanceState() { + Bundle bundle = new Bundle(); +// getPresenter(); // Workaround a crash related to saving instance state with child routers + if (presenter != null) { + presenter.save(bundle); + } + return bundle; + } + + void onRestoreInstanceState(Bundle presenterState) { + bundle = presenterState; + } + + void onTakeView(Object view) { + getPresenter(); + if (presenter != null) { + //noinspection unchecked + presenter.takeView(view); + } + } + + void onDropView() { + if (presenter != null) { + presenter.dropView(); + } + } + + void onDestroy() { + if (presenter != null) { + presenter.destroy(); + } + } +} diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/base/presenter/NucleusConductorLifecycleListener.java b/app/src/main/java/eu/kanade/tachiyomi/ui/base/presenter/NucleusConductorLifecycleListener.java index 33272a1b2..51b7cf571 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/base/presenter/NucleusConductorLifecycleListener.java +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/base/presenter/NucleusConductorLifecycleListener.java @@ -1,44 +1,45 @@ -package eu.kanade.tachiyomi.ui.base.presenter; - -import android.os.Bundle; -import android.support.annotation.NonNull; -import android.view.View; - -import com.bluelinelabs.conductor.Controller; - -public class NucleusConductorLifecycleListener extends Controller.LifecycleListener { - - private static final String PRESENTER_STATE_KEY = "presenter_state"; - - private NucleusConductorDelegate delegate; - - public NucleusConductorLifecycleListener(NucleusConductorDelegate delegate) { - this.delegate = delegate; - } - - @Override - public void postCreateView(@NonNull Controller controller, @NonNull View view) { - delegate.onTakeView(controller); - } - - @Override - public void preDestroyView(@NonNull Controller controller, @NonNull View view) { - delegate.onDropView(); - } - - @Override - public void preDestroy(@NonNull Controller controller) { - delegate.onDestroy(); - } - - @Override - public void onSaveInstanceState(@NonNull Controller controller, @NonNull Bundle outState) { - outState.putBundle(PRESENTER_STATE_KEY, delegate.onSaveInstanceState()); - } - - @Override - public void onRestoreInstanceState(@NonNull Controller controller, @NonNull Bundle savedInstanceState) { - delegate.onRestoreInstanceState(savedInstanceState.getBundle(PRESENTER_STATE_KEY)); - } - -} +package eu.kanade.tachiyomi.ui.base.presenter; + +import android.os.Bundle; +import android.view.View; + +import com.bluelinelabs.conductor.Controller; + +import androidx.annotation.NonNull; + +public class NucleusConductorLifecycleListener extends Controller.LifecycleListener { + + private static final String PRESENTER_STATE_KEY = "presenter_state"; + + private NucleusConductorDelegate delegate; + + public NucleusConductorLifecycleListener(NucleusConductorDelegate delegate) { + this.delegate = delegate; + } + + @Override + public void postCreateView(@NonNull Controller controller, @NonNull View view) { + delegate.onTakeView(controller); + } + + @Override + public void preDestroyView(@NonNull Controller controller, @NonNull View view) { + delegate.onDropView(); + } + + @Override + public void preDestroy(@NonNull Controller controller) { + delegate.onDestroy(); + } + + @Override + public void onSaveInstanceState(@NonNull Controller controller, @NonNull Bundle outState) { + outState.putBundle(PRESENTER_STATE_KEY, delegate.onSaveInstanceState()); + } + + @Override + public void onRestoreInstanceState(@NonNull Controller controller, @NonNull Bundle savedInstanceState) { + delegate.onRestoreInstanceState(savedInstanceState.getBundle(PRESENTER_STATE_KEY)); + } + +} diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/CatalogueController.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/CatalogueController.kt index ed99191a4..c194d7b03 100755 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/CatalogueController.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/CatalogueController.kt @@ -3,9 +3,8 @@ package eu.kanade.tachiyomi.ui.catalogue import android.Manifest.permission.WRITE_EXTERNAL_STORAGE import android.os.Bundle import android.os.Parcelable -import android.support.v7.widget.LinearLayoutManager -import android.support.v7.widget.SearchView import android.view.* +import androidx.appcompat.widget.SearchView import com.bluelinelabs.conductor.ControllerChangeHandler import com.bluelinelabs.conductor.ControllerChangeType import com.bluelinelabs.conductor.RouterTransaction @@ -111,7 +110,7 @@ class CatalogueController(bundle: Bundle? = null) : NucleusController() { /** * Creates a new view holder for this item. */ - override fun createViewHolder(view: View, adapter: FlexibleAdapter>): LangHolder { + override fun createViewHolder(view: View, adapter: FlexibleAdapter>): LangHolder { return LangHolder(view, adapter) } /** * Binds this item to the given view holder. */ - override fun bindViewHolder(adapter: FlexibleAdapter>, holder: LangHolder, + override fun bindViewHolder(adapter: FlexibleAdapter>, holder: LangHolder, position: Int, payloads: List?) { holder.bind(this) diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/SourceDividerItemDecoration.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/SourceDividerItemDecoration.kt index e21b58124..951d0238a 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/SourceDividerItemDecoration.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/SourceDividerItemDecoration.kt @@ -4,10 +4,9 @@ import android.content.Context import android.graphics.Canvas import android.graphics.Rect import android.graphics.drawable.Drawable -import android.support.v7.widget.RecyclerView import android.view.View -class SourceDividerItemDecoration(context: Context) : RecyclerView.ItemDecoration() { +class SourceDividerItemDecoration(context: Context) : androidx.recyclerview.widget.RecyclerView.ItemDecoration() { private val divider: Drawable @@ -17,14 +16,14 @@ class SourceDividerItemDecoration(context: Context) : RecyclerView.ItemDecoratio a.recycle() } - override fun onDraw(c: Canvas, parent: RecyclerView, state: RecyclerView.State) { + override fun onDraw(c: Canvas, parent: androidx.recyclerview.widget.RecyclerView, state: androidx.recyclerview.widget.RecyclerView.State) { val childCount = parent.childCount for (i in 0 until childCount - 1) { val child = parent.getChildAt(i) val holder = parent.getChildViewHolder(child) if (holder is SourceHolder && parent.getChildViewHolder(parent.getChildAt(i + 1)) is SourceHolder) { - val params = child.layoutParams as RecyclerView.LayoutParams + val params = child.layoutParams as androidx.recyclerview.widget.RecyclerView.LayoutParams val top = child.bottom + params.bottomMargin val bottom = top + divider.intrinsicHeight val left = parent.paddingLeft + holder.margin @@ -36,8 +35,8 @@ class SourceDividerItemDecoration(context: Context) : RecyclerView.ItemDecoratio } } - override fun getItemOffsets(outRect: Rect, view: View, parent: RecyclerView, - state: RecyclerView.State) { + override fun getItemOffsets(outRect: Rect, view: View, parent: androidx.recyclerview.widget.RecyclerView, + state: androidx.recyclerview.widget.RecyclerView.State) { outRect.set(0, 0, 0, divider.intrinsicHeight) } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/SourceItem.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/SourceItem.kt index fd1853652..365c06df4 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/SourceItem.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/SourceItem.kt @@ -1,6 +1,5 @@ package eu.kanade.tachiyomi.ui.catalogue -import android.support.v7.widget.RecyclerView import android.view.View import eu.davidea.flexibleadapter.FlexibleAdapter import eu.davidea.flexibleadapter.items.AbstractSectionableItem @@ -27,14 +26,14 @@ data class SourceItem(val source: CatalogueSource, val header: LangItem? = null, /** * Creates a new view holder for this item. */ - override fun createViewHolder(view: View, adapter: FlexibleAdapter>): SourceHolder { + override fun createViewHolder(view: View, adapter: FlexibleAdapter>): SourceHolder { return SourceHolder(view, adapter as CatalogueAdapter, showButtons) } /** * Binds this item to the given view holder. */ - override fun bindViewHolder(adapter: FlexibleAdapter>, holder: SourceHolder, + override fun bindViewHolder(adapter: FlexibleAdapter>, holder: SourceHolder, position: Int, payloads: List?) { holder.bind(this) diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/browse/BrowseCatalogueController.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/browse/BrowseCatalogueController.kt index 848815638..74936d3d2 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/browse/BrowseCatalogueController.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/browse/BrowseCatalogueController.kt @@ -2,13 +2,15 @@ package eu.kanade.tachiyomi.ui.catalogue.browse import android.content.res.Configuration import android.os.Bundle -import android.support.design.widget.Snackbar -import android.support.v4.widget.DrawerLayout -import android.support.v7.widget.* import android.view.* +import androidx.appcompat.widget.SearchView +import androidx.core.view.GravityCompat +import androidx.drawerlayout.widget.DrawerLayout +import androidx.recyclerview.widget.RecyclerView import com.afollestad.materialdialogs.MaterialDialog import com.elvishew.xlog.XLog import com.f2prateek.rx.preferences.Preference +import com.google.android.material.snackbar.Snackbar import com.jakewharton.rxbinding.support.v7.widget.queryTextChangeEvents import eu.davidea.flexibleadapter.FlexibleAdapter import eu.davidea.flexibleadapter.items.IFlexible @@ -35,7 +37,6 @@ import rx.Observable import rx.Subscription import rx.android.schedulers.AndroidSchedulers import rx.subscriptions.Subscriptions -import timber.log.Timber import uy.kohesive.injekt.injectLazy import java.util.concurrent.TimeUnit @@ -85,7 +86,7 @@ open class BrowseCatalogueController(bundle: Bundle) : /** * Recycler view with the list of results. */ - private var recycler: RecyclerView? = null + private var recycler: androidx.recyclerview.widget.RecyclerView? = null /** * Subscription for the search view. @@ -142,13 +143,13 @@ open class BrowseCatalogueController(bundle: Bundle) : super.onDestroyView(view) } - override fun createSecondaryDrawer(drawer: DrawerLayout): ViewGroup? { + override fun createSecondaryDrawer(drawer: androidx.drawerlayout.widget.DrawerLayout): ViewGroup? { // Inflate and prepare drawer - val navView = drawer.inflate(R.layout.catalogue_drawer) as CatalogueNavigationView + val navView = drawer.inflate(R.layout.catalogue_drawer) as CatalogueNavigationView //TODO whatever this is this.navView = navView navView.setFilters(presenter.filterItems) - drawer.setDrawerLockMode(DrawerLayout.LOCK_MODE_UNLOCKED, Gravity.END) + drawer.setDrawerLockMode(androidx.drawerlayout.widget.DrawerLayout.LOCK_MODE_UNLOCKED, GravityCompat.END) // EXH --> navView.setSavedSearches(presenter.loadSearches()) @@ -196,7 +197,7 @@ open class BrowseCatalogueController(bundle: Bundle) : showProgressBar() adapter?.clear() - drawer.closeDrawer(Gravity.END) + drawer.closeDrawer(GravityCompat.END) presenter.restartPager(search.query, if (allDefault) FilterList() else presenter.sourceFilters) activity?.invalidateOptionsMenu() } @@ -238,7 +239,7 @@ open class BrowseCatalogueController(bundle: Bundle) : val allDefault = presenter.sourceFilters == presenter.source.getFilterList() showProgressBar() adapter?.clear() - drawer.closeDrawer(Gravity.END) + drawer.closeDrawer(GravityCompat.END) presenter.setSourceFilter(if (allDefault) FilterList() else presenter.sourceFilters) } @@ -248,31 +249,31 @@ open class BrowseCatalogueController(bundle: Bundle) : presenter.sourceFilters = newFilters navView.setFilters(presenter.filterItems) } - return navView + return navView as ViewGroup //TODO fix this bullshit } - override fun cleanupSecondaryDrawer(drawer: DrawerLayout) { + override fun cleanupSecondaryDrawer(drawer: androidx.drawerlayout.widget.DrawerLayout) { navView = null } private fun setupRecycler(view: View) { numColumnsSubscription?.unsubscribe() - var oldPosition = RecyclerView.NO_POSITION + var oldPosition = androidx.recyclerview.widget.RecyclerView.NO_POSITION val oldRecycler = catalogue_view?.getChildAt(1) - if (oldRecycler is RecyclerView) { - oldPosition = (oldRecycler.layoutManager as LinearLayoutManager).findFirstVisibleItemPosition() + if (oldRecycler is androidx.recyclerview.widget.RecyclerView) { + oldPosition = (oldRecycler.layoutManager as androidx.recyclerview.widget.LinearLayoutManager).findFirstVisibleItemPosition() oldRecycler.adapter = null catalogue_view?.removeView(oldRecycler) } val recycler = if (presenter.isListMode) { - RecyclerView(view.context).apply { + androidx.recyclerview.widget.RecyclerView(view.context).apply { id = R.id.recycler - layoutManager = LinearLayoutManager(context) - layoutParams = RecyclerView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT) - addItemDecoration(DividerItemDecoration(context, DividerItemDecoration.VERTICAL)) + layoutManager = androidx.recyclerview.widget.LinearLayoutManager(context) + layoutParams = androidx.recyclerview.widget.RecyclerView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT) + addItemDecoration(androidx.recyclerview.widget.DividerItemDecoration(context, androidx.recyclerview.widget.DividerItemDecoration.VERTICAL)) } } else { (catalogue_view.inflate(R.layout.catalogue_recycler_autofit) as AutofitRecyclerView).apply { @@ -282,7 +283,7 @@ open class BrowseCatalogueController(bundle: Bundle) : // Set again the adapter to recalculate the covers height .subscribe { adapter = this@BrowseCatalogueController.adapter } - (layoutManager as GridLayoutManager).spanSizeLookup = object : GridLayoutManager.SpanSizeLookup() { + (layoutManager as androidx.recyclerview.widget.GridLayoutManager).spanSizeLookup = object : androidx.recyclerview.widget.GridLayoutManager.SpanSizeLookup() { override fun getSpanSize(position: Int): Int { return when (adapter?.getItemViewType(position)) { R.layout.catalogue_grid_item, null -> 1 @@ -297,7 +298,7 @@ open class BrowseCatalogueController(bundle: Bundle) : catalogue_view.addView(recycler, 1) - if (oldPosition != RecyclerView.NO_POSITION) { + if (oldPosition != androidx.recyclerview.widget.RecyclerView.NO_POSITION) { recycler.layoutManager?.scrollToPosition(oldPosition) } this.recycler = recycler @@ -369,7 +370,7 @@ open class BrowseCatalogueController(bundle: Bundle) : override fun onOptionsItemSelected(item: MenuItem): Boolean { when (item.itemId) { R.id.action_display_mode -> swapDisplayMode() - R.id.action_set_filter -> navView?.let { activity?.drawer?.openDrawer(Gravity.END) } + R.id.action_set_filter -> navView?.let { activity?.drawer?.openDrawer(GravityCompat.END) } R.id.action_open_in_browser -> openInBrowser() R.id.action_open_in_web_view -> openInWebView() else -> return super.onOptionsItemSelected(item) diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/browse/CatalogueItem.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/browse/CatalogueItem.kt index 20fc5f9ad..08e1dcb93 100755 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/browse/CatalogueItem.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/browse/CatalogueItem.kt @@ -1,6 +1,5 @@ package eu.kanade.tachiyomi.ui.catalogue.browse -import android.support.v7.widget.RecyclerView import android.view.Gravity import android.view.View import android.view.ViewGroup.LayoutParams.MATCH_PARENT @@ -25,7 +24,7 @@ class CatalogueItem(val manga: Manga, private val catalogueAsList: Preference>): CatalogueHolder { + override fun createViewHolder(view: View, adapter: FlexibleAdapter>): CatalogueHolder { val parent = adapter.recyclerView return if (parent is AutofitRecyclerView) { view.apply { @@ -40,7 +39,7 @@ class CatalogueItem(val manga: Manga, private val catalogueAsList: Preference>, + override fun bindViewHolder(adapter: FlexibleAdapter>, holder: CatalogueHolder, position: Int, payloads: List?) { diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/browse/CatalogueNavigationView.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/browse/CatalogueNavigationView.kt index 2b260a667..afff33e80 100755 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/browse/CatalogueNavigationView.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/browse/CatalogueNavigationView.kt @@ -2,7 +2,9 @@ package eu.kanade.tachiyomi.ui.catalogue.browse import android.content.Context import android.util.AttributeSet +import android.util.TypedValue import android.view.Gravity +import android.view.View import android.view.ViewGroup import android.view.ViewGroup.LayoutParams.MATCH_PARENT import android.view.ViewGroup.LayoutParams.WRAP_CONTENT @@ -10,13 +12,12 @@ import android.widget.LinearLayout import android.widget.TextView import eu.davidea.flexibleadapter.FlexibleAdapter import eu.davidea.flexibleadapter.items.IFlexible +import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.util.dpToPx import eu.kanade.tachiyomi.util.inflate import eu.kanade.tachiyomi.widget.SimpleNavigationView -import kotlinx.android.synthetic.main.catalogue_drawer_content.view.* -import android.util.TypedValue -import android.view.View import exh.EXHSavedSearch +import kotlinx.android.synthetic.main.catalogue_drawer_content.view.* class CatalogueNavigationView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null) : SimpleNavigationView(context, attrs) { @@ -44,10 +45,10 @@ class CatalogueNavigationView @JvmOverloads constructor(context: Context, attrs: init { recycler.adapter = adapter recycler.setHasFixedSize(true) - val view = inflate(eu.kanade.tachiyomi.R.layout.catalogue_drawer_content) + val view = inflate(R.layout.catalogue_drawer_content) ((view as ViewGroup).getChildAt(1) as ViewGroup).addView(recycler) addView(view) - title.text = context?.getString(eu.kanade.tachiyomi.R.string.source_search_options) + title.text = context.getString(R.string.source_search_options) save_search_btn.setOnClickListener { onSaveClicked() } search_btn.setOnClickListener { onSearchClicked() } reset_btn.setOnClickListener { onResetClicked() } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/browse/ProgressItem.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/browse/ProgressItem.kt index 12e3741c3..ed135a656 100755 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/browse/ProgressItem.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/browse/ProgressItem.kt @@ -1,6 +1,5 @@ package eu.kanade.tachiyomi.ui.catalogue.browse -import android.support.v7.widget.RecyclerView import android.view.View import android.widget.ProgressBar import android.widget.TextView @@ -19,11 +18,11 @@ class ProgressItem : AbstractFlexibleItem() { return R.layout.catalogue_progress_item } - override fun createViewHolder(view: View, adapter: FlexibleAdapter>): Holder { + override fun createViewHolder(view: View, adapter: FlexibleAdapter>): Holder { return Holder(view, adapter) } - override fun bindViewHolder(adapter: FlexibleAdapter>, holder: Holder, position: Int, payloads: List) { + override fun bindViewHolder(adapter: FlexibleAdapter>, holder: Holder, position: Int, payloads: List) { holder.progressBar.visibility = View.GONE holder.progressMessage.visibility = View.GONE diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/filter/CheckboxItem.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/filter/CheckboxItem.kt index 11058b74e..ba59105ef 100755 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/filter/CheckboxItem.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/filter/CheckboxItem.kt @@ -1,6 +1,5 @@ package eu.kanade.tachiyomi.ui.catalogue.filter -import android.support.v7.widget.RecyclerView import android.view.View import android.widget.CheckBox import eu.davidea.flexibleadapter.FlexibleAdapter @@ -16,11 +15,11 @@ open class CheckboxItem(val filter: Filter.CheckBox) : AbstractFlexibleItem>): Holder { + override fun createViewHolder(view: View, adapter: FlexibleAdapter>): Holder { return Holder(view, adapter) } - override fun bindViewHolder(adapter: FlexibleAdapter>, holder: Holder, position: Int, payloads: List?) { + override fun bindViewHolder(adapter: FlexibleAdapter>, holder: Holder, position: Int, payloads: List?) { val view = holder.check view.text = filter.name view.isChecked = filter.state diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/filter/GroupItem.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/filter/GroupItem.kt index 416876bbb..09047002e 100755 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/filter/GroupItem.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/filter/GroupItem.kt @@ -1,6 +1,5 @@ package eu.kanade.tachiyomi.ui.catalogue.filter -import android.support.v7.widget.RecyclerView import android.view.View import android.widget.ImageView import android.widget.TextView @@ -33,11 +32,11 @@ class GroupItem(val filter: Filter.Group<*>) : AbstractExpandableHeaderItem>): Holder { + override fun createViewHolder(view: View, adapter: FlexibleAdapter>): Holder { return Holder(view, adapter) } - override fun bindViewHolder(adapter: FlexibleAdapter>, holder: Holder, position: Int, payloads: List?) { + override fun bindViewHolder(adapter: FlexibleAdapter>, holder: Holder, position: Int, payloads: List?) { holder.title.text = filter.name holder.icon.setVectorCompat(if (isExpanded) diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/filter/HeaderItem.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/filter/HeaderItem.kt index 6517d8dfa..8a903c6d7 100755 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/filter/HeaderItem.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/filter/HeaderItem.kt @@ -1,10 +1,9 @@ package eu.kanade.tachiyomi.ui.catalogue.filter import android.annotation.SuppressLint -import android.support.design.R -import android.support.v7.widget.RecyclerView import android.view.View import android.widget.TextView +import com.google.android.material.R import eu.davidea.flexibleadapter.FlexibleAdapter import eu.davidea.flexibleadapter.items.AbstractHeaderItem import eu.davidea.flexibleadapter.items.IFlexible @@ -18,11 +17,11 @@ class HeaderItem(val filter: Filter.Header) : AbstractHeaderItem>): Holder { + override fun createViewHolder(view: View, adapter: FlexibleAdapter>): Holder { return Holder(view, adapter) } - override fun bindViewHolder(adapter: FlexibleAdapter>, holder: Holder, position: Int, payloads: List?) { + override fun bindViewHolder(adapter: FlexibleAdapter>, holder: Holder, position: Int, payloads: List?) { val view = holder.itemView as TextView view.text = filter.name } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/filter/HelpDialogItem.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/filter/HelpDialogItem.kt index 539c93d51..cf4c5626a 100755 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/filter/HelpDialogItem.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/filter/HelpDialogItem.kt @@ -1,7 +1,6 @@ package eu.kanade.tachiyomi.ui.catalogue.filter import android.annotation.SuppressLint -import android.support.v7.widget.RecyclerView import android.view.View import android.widget.Button import android.widget.TextView @@ -23,11 +22,11 @@ class HelpDialogItem(val filter: Filter.HelpDialog) : AbstractHeaderItem>): Holder { + override fun createViewHolder(view: View, adapter: FlexibleAdapter>): Holder { return Holder(view, adapter) } - override fun bindViewHolder(adapter: FlexibleAdapter>, holder: Holder, position: Int, payloads: List?) { + override fun bindViewHolder(adapter: FlexibleAdapter>, holder: Holder, position: Int, payloads: List?) { val view = holder.button as TextView view.text = filter.name view.setOnClickListener { diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/filter/SelectItem.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/filter/SelectItem.kt index ab95a8484..7da61fc1a 100755 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/filter/SelectItem.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/filter/SelectItem.kt @@ -1,6 +1,5 @@ package eu.kanade.tachiyomi.ui.catalogue.filter -import android.support.v7.widget.RecyclerView import android.view.View import android.widget.ArrayAdapter import android.widget.Spinner @@ -19,11 +18,11 @@ open class SelectItem(val filter: Filter.Select<*>) : AbstractFlexibleItem>): Holder { + override fun createViewHolder(view: View, adapter: FlexibleAdapter>): Holder { return Holder(view, adapter) } - override fun bindViewHolder(adapter: FlexibleAdapter>, holder: Holder, position: Int, payloads: List?) { + override fun bindViewHolder(adapter: FlexibleAdapter>, holder: Holder, position: Int, payloads: List?) { holder.text.text = filter.name + ": " val spinner = holder.spinner diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/filter/SeparatorItem.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/filter/SeparatorItem.kt index d5b47ab67..75c443cc8 100755 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/filter/SeparatorItem.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/filter/SeparatorItem.kt @@ -1,9 +1,8 @@ package eu.kanade.tachiyomi.ui.catalogue.filter import android.annotation.SuppressLint -import android.support.design.R -import android.support.v7.widget.RecyclerView import android.view.View +import com.google.android.material.R import eu.davidea.flexibleadapter.FlexibleAdapter import eu.davidea.flexibleadapter.items.AbstractHeaderItem import eu.davidea.flexibleadapter.items.IFlexible @@ -17,11 +16,11 @@ class SeparatorItem(val filter: Filter.Separator) : AbstractHeaderItem>): Holder { + override fun createViewHolder(view: View, adapter: FlexibleAdapter>): Holder { return Holder(view, adapter) } - override fun bindViewHolder(adapter: FlexibleAdapter>, holder: Holder, position: Int, payloads: List?) { + override fun bindViewHolder(adapter: FlexibleAdapter>, holder: Holder, position: Int, payloads: List?) { } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/filter/SortGroup.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/filter/SortGroup.kt index 07eb80b93..507616b0c 100755 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/filter/SortGroup.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/filter/SortGroup.kt @@ -1,54 +1,53 @@ -package eu.kanade.tachiyomi.ui.catalogue.filter - -import android.support.v7.widget.RecyclerView -import android.view.View -import eu.davidea.flexibleadapter.FlexibleAdapter -import eu.davidea.flexibleadapter.items.AbstractExpandableHeaderItem -import eu.davidea.flexibleadapter.items.IFlexible -import eu.davidea.flexibleadapter.items.ISectionable -import eu.kanade.tachiyomi.R -import eu.kanade.tachiyomi.source.model.Filter -import eu.kanade.tachiyomi.util.setVectorCompat - -class SortGroup(val filter: Filter.Sort) : AbstractExpandableHeaderItem>() { - - init { - isExpanded = false - } - - override fun getLayoutRes(): Int { - return R.layout.navigation_view_group - } - - override fun getItemViewType(): Int { - return 100 - } - - override fun createViewHolder(view: View, adapter: FlexibleAdapter>): Holder { - return Holder(view, adapter) - } - - override fun bindViewHolder(adapter: FlexibleAdapter>, holder: Holder, position: Int, payloads: List?) { - holder.title.text = filter.name - - holder.icon.setVectorCompat(if (isExpanded) - R.drawable.ic_expand_more_white_24dp - else - R.drawable.ic_chevron_right_white_24dp) - - holder.itemView.setOnClickListener(holder) - - } - - override fun equals(other: Any?): Boolean { - if (this === other) return true - if (javaClass != other?.javaClass) return false - return filter == (other as SortGroup).filter - } - - override fun hashCode(): Int { - return filter.hashCode() - } - - class Holder(view: View, adapter: FlexibleAdapter<*>) : GroupItem.Holder(view, adapter) +package eu.kanade.tachiyomi.ui.catalogue.filter + +import android.view.View +import eu.davidea.flexibleadapter.FlexibleAdapter +import eu.davidea.flexibleadapter.items.AbstractExpandableHeaderItem +import eu.davidea.flexibleadapter.items.IFlexible +import eu.davidea.flexibleadapter.items.ISectionable +import eu.kanade.tachiyomi.R +import eu.kanade.tachiyomi.source.model.Filter +import eu.kanade.tachiyomi.util.setVectorCompat + +class SortGroup(val filter: Filter.Sort) : AbstractExpandableHeaderItem>() { + + init { + isExpanded = false + } + + override fun getLayoutRes(): Int { + return R.layout.navigation_view_group + } + + override fun getItemViewType(): Int { + return 100 + } + + override fun createViewHolder(view: View, adapter: FlexibleAdapter>): Holder { + return Holder(view, adapter) + } + + override fun bindViewHolder(adapter: FlexibleAdapter>, holder: Holder, position: Int, payloads: List?) { + holder.title.text = filter.name + + holder.icon.setVectorCompat(if (isExpanded) + R.drawable.ic_expand_more_white_24dp + else + R.drawable.ic_chevron_right_white_24dp) + + holder.itemView.setOnClickListener(holder) + + } + + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (javaClass != other?.javaClass) return false + return filter == (other as SortGroup).filter + } + + override fun hashCode(): Int { + return filter.hashCode() + } + + class Holder(view: View, adapter: FlexibleAdapter<*>) : GroupItem.Holder(view, adapter) } \ No newline at end of file diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/filter/SortItem.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/filter/SortItem.kt index 9823909d4..30fecd62c 100755 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/filter/SortItem.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/filter/SortItem.kt @@ -1,10 +1,9 @@ package eu.kanade.tachiyomi.ui.catalogue.filter -import android.support.graphics.drawable.VectorDrawableCompat -import android.support.v4.content.ContextCompat -import android.support.v7.widget.RecyclerView import android.view.View import android.widget.CheckedTextView +import androidx.core.content.ContextCompat +import androidx.vectordrawable.graphics.drawable.VectorDrawableCompat import eu.davidea.flexibleadapter.FlexibleAdapter import eu.davidea.flexibleadapter.items.AbstractSectionableItem import eu.davidea.flexibleadapter.items.IFlexible @@ -23,11 +22,11 @@ class SortItem(val name: String, val group: SortGroup) : AbstractSectionableItem return 102 } - override fun createViewHolder(view: View, adapter: FlexibleAdapter>): Holder { + override fun createViewHolder(view: View, adapter: FlexibleAdapter>): Holder { return Holder(view, adapter) } - override fun bindViewHolder(adapter: FlexibleAdapter>, holder: Holder, position: Int, payloads: List?) { + override fun bindViewHolder(adapter: FlexibleAdapter>, holder: Holder, position: Int, payloads: List?) { val view = holder.text view.text = name val filter = group.filter diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/filter/TextItem.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/filter/TextItem.kt index b0dcf5818..324f48fb7 100755 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/filter/TextItem.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/filter/TextItem.kt @@ -1,9 +1,8 @@ package eu.kanade.tachiyomi.ui.catalogue.filter -import android.support.design.widget.TextInputLayout -import android.support.v7.widget.RecyclerView import android.view.View import android.widget.EditText +import com.google.android.material.textfield.TextInputLayout import eu.davidea.flexibleadapter.FlexibleAdapter import eu.davidea.flexibleadapter.items.AbstractFlexibleItem import eu.davidea.flexibleadapter.items.IFlexible @@ -23,17 +22,17 @@ open class TextItem(val filter: Filter.Text) : AbstractFlexibleItem>): Holder { + override fun createViewHolder(view: View, adapter: FlexibleAdapter>): Holder { return Holder(view, adapter) } - override fun bindViewHolder(adapter: FlexibleAdapter>, holder: Holder, position: Int, payloads: List?) { + override fun bindViewHolder(adapter: FlexibleAdapter>, holder: Holder, position: Int, payloads: List?) { holder.wrapper.hint = filter.name holder.edit.setText(filter.state) holder.edit.addTextChangedListener(textWatcher) } - override fun unbindViewHolder(adapter: FlexibleAdapter>, holder: Holder, position: Int) { + override fun unbindViewHolder(adapter: FlexibleAdapter>, holder: Holder, position: Int) { holder.edit.removeTextChangedListener(textWatcher) } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/filter/TriStateItem.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/filter/TriStateItem.kt index f90cd345a..13681d616 100755 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/filter/TriStateItem.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/filter/TriStateItem.kt @@ -1,10 +1,9 @@ package eu.kanade.tachiyomi.ui.catalogue.filter -import android.support.design.R -import android.support.graphics.drawable.VectorDrawableCompat -import android.support.v7.widget.RecyclerView import android.view.View import android.widget.CheckedTextView +import androidx.vectordrawable.graphics.drawable.VectorDrawableCompat +import com.google.android.material.R import eu.davidea.flexibleadapter.FlexibleAdapter import eu.davidea.flexibleadapter.items.AbstractFlexibleItem import eu.davidea.flexibleadapter.items.IFlexible @@ -24,11 +23,11 @@ open class TriStateItem(val filter: Filter.TriState) : AbstractFlexibleItem>): Holder { + override fun createViewHolder(view: View, adapter: FlexibleAdapter>): Holder { return Holder(view, adapter) } - override fun bindViewHolder(adapter: FlexibleAdapter>, holder: Holder, position: Int, payloads: List?) { + override fun bindViewHolder(adapter: FlexibleAdapter>, holder: Holder, position: Int, payloads: List?) { val view = holder.text view.text = filter.name diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/global_search/CatalogueSearchAdapter.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/global_search/CatalogueSearchAdapter.kt index d981a3ab4..e9974d7da 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/global_search/CatalogueSearchAdapter.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/global_search/CatalogueSearchAdapter.kt @@ -2,7 +2,6 @@ package eu.kanade.tachiyomi.ui.catalogue.global_search import android.os.Bundle import android.os.Parcelable -import android.support.v7.widget.RecyclerView import android.util.SparseArray import eu.davidea.flexibleadapter.FlexibleAdapter import eu.kanade.tachiyomi.source.CatalogueSource @@ -25,12 +24,12 @@ class CatalogueSearchAdapter(val controller: CatalogueSearchController) : */ private var bundle = Bundle() - override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int, payloads: List) { + override fun onBindViewHolder(holder: androidx.recyclerview.widget.RecyclerView.ViewHolder, position: Int, payloads: List) { super.onBindViewHolder(holder, position, payloads) restoreHolderState(holder) } - override fun onViewRecycled(holder: RecyclerView.ViewHolder) { + override fun onViewRecycled(holder: androidx.recyclerview.widget.RecyclerView.ViewHolder) { super.onViewRecycled(holder) saveHolderState(holder, bundle) } @@ -53,7 +52,7 @@ class CatalogueSearchAdapter(val controller: CatalogueSearchController) : * @param holder The holder to save. * @param outState The bundle where the state is saved. */ - private fun saveHolderState(holder: RecyclerView.ViewHolder, outState: Bundle) { + private fun saveHolderState(holder: androidx.recyclerview.widget.RecyclerView.ViewHolder, outState: Bundle) { val key = "holder_${holder.adapterPosition}" val holderState = SparseArray() holder.itemView.saveHierarchyState(holderState) @@ -65,7 +64,7 @@ class CatalogueSearchAdapter(val controller: CatalogueSearchController) : * * @param holder The holder to restore. */ - private fun restoreHolderState(holder: RecyclerView.ViewHolder) { + private fun restoreHolderState(holder: androidx.recyclerview.widget.RecyclerView.ViewHolder) { val key = "holder_${holder.adapterPosition}" val holderState = bundle.getSparseParcelableArray(key) if (holderState != null) { diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/global_search/CatalogueSearchCardItem.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/global_search/CatalogueSearchCardItem.kt index 15f0a63a3..1719bb884 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/global_search/CatalogueSearchCardItem.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/global_search/CatalogueSearchCardItem.kt @@ -1,37 +1,36 @@ -package eu.kanade.tachiyomi.ui.catalogue.global_search - -import android.support.v7.widget.RecyclerView -import android.view.View -import eu.davidea.flexibleadapter.FlexibleAdapter -import eu.davidea.flexibleadapter.items.AbstractFlexibleItem -import eu.davidea.flexibleadapter.items.IFlexible -import eu.kanade.tachiyomi.R -import eu.kanade.tachiyomi.data.database.models.Manga - -class CatalogueSearchCardItem(val manga: Manga) : AbstractFlexibleItem() { - - override fun getLayoutRes(): Int { - return R.layout.catalogue_global_search_controller_card_item - } - - override fun createViewHolder(view: View, adapter: FlexibleAdapter>): CatalogueSearchCardHolder { - return CatalogueSearchCardHolder(view, adapter as CatalogueSearchCardAdapter) - } - - override fun bindViewHolder(adapter: FlexibleAdapter>, holder: CatalogueSearchCardHolder, - position: Int, payloads: List?) { - holder.bind(manga) - } - - override fun equals(other: Any?): Boolean { - if (other is CatalogueSearchCardItem) { - return manga.id == other.manga.id - } - return false - } - - override fun hashCode(): Int { - return manga.id?.toInt() ?: 0 - } - +package eu.kanade.tachiyomi.ui.catalogue.global_search + +import android.view.View +import eu.davidea.flexibleadapter.FlexibleAdapter +import eu.davidea.flexibleadapter.items.AbstractFlexibleItem +import eu.davidea.flexibleadapter.items.IFlexible +import eu.kanade.tachiyomi.R +import eu.kanade.tachiyomi.data.database.models.Manga + +class CatalogueSearchCardItem(val manga: Manga) : AbstractFlexibleItem() { + + override fun getLayoutRes(): Int { + return R.layout.catalogue_global_search_controller_card_item + } + + override fun createViewHolder(view: View, adapter: FlexibleAdapter>): CatalogueSearchCardHolder { + return CatalogueSearchCardHolder(view, adapter as CatalogueSearchCardAdapter) + } + + override fun bindViewHolder(adapter: FlexibleAdapter>, holder: CatalogueSearchCardHolder, + position: Int, payloads: List?) { + holder.bind(manga) + } + + override fun equals(other: Any?): Boolean { + if (other is CatalogueSearchCardItem) { + return manga.id == other.manga.id + } + return false + } + + override fun hashCode(): Int { + return manga.id?.toInt() ?: 0 + } + } \ No newline at end of file diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/global_search/CatalogueSearchController.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/global_search/CatalogueSearchController.kt index 2e6de23d6..75f85ae94 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/global_search/CatalogueSearchController.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/global_search/CatalogueSearchController.kt @@ -1,9 +1,8 @@ package eu.kanade.tachiyomi.ui.catalogue.global_search import android.os.Bundle -import android.support.v7.widget.LinearLayoutManager -import android.support.v7.widget.SearchView import android.view.* +import androidx.appcompat.widget.SearchView import com.jakewharton.rxbinding.support.v7.widget.queryTextChangeEvents import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.data.database.models.Manga @@ -139,7 +138,7 @@ open class CatalogueSearchController( adapter = CatalogueSearchAdapter(this) // Create recycler and set adapter. - recycler.layoutManager = LinearLayoutManager(view.context) + recycler.layoutManager = androidx.recyclerview.widget.LinearLayoutManager(view.context) recycler.adapter = adapter } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/global_search/CatalogueSearchHolder.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/global_search/CatalogueSearchHolder.kt index aa7ef3cee..41bf0fb91 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/global_search/CatalogueSearchHolder.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/global_search/CatalogueSearchHolder.kt @@ -1,16 +1,12 @@ package eu.kanade.tachiyomi.ui.catalogue.global_search -import android.support.v7.widget.LinearLayoutManager import android.view.View -import eu.kanade.tachiyomi.R +import androidx.recyclerview.widget.LinearLayoutManager import eu.kanade.tachiyomi.data.database.models.Manga import eu.kanade.tachiyomi.ui.base.holder.BaseFlexibleViewHolder -import eu.kanade.tachiyomi.util.getResourceColor import eu.kanade.tachiyomi.util.gone -import eu.kanade.tachiyomi.util.setVectorCompat import eu.kanade.tachiyomi.util.visible import kotlinx.android.synthetic.main.catalogue_global_search_controller_card.* -import kotlinx.android.synthetic.main.catalogue_global_search_controller_card.view.* /** * Holder that binds the [CatalogueSearchItem] containing catalogue cards. @@ -30,7 +26,7 @@ class CatalogueSearchHolder(view: View, val adapter: CatalogueSearchAdapter) : init { // Set layout horizontal. - recycler.layoutManager = LinearLayoutManager(view.context, LinearLayoutManager.HORIZONTAL, false) + recycler.layoutManager = androidx.recyclerview.widget.LinearLayoutManager(view.context, androidx.recyclerview.widget.LinearLayoutManager.HORIZONTAL, false) recycler.adapter = mangaAdapter more.setOnClickListener { diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/global_search/CatalogueSearchItem.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/global_search/CatalogueSearchItem.kt index d8ea13d6c..5c06d15c5 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/global_search/CatalogueSearchItem.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/global_search/CatalogueSearchItem.kt @@ -1,6 +1,5 @@ package eu.kanade.tachiyomi.ui.catalogue.global_search -import android.support.v7.widget.RecyclerView import android.view.View import eu.davidea.flexibleadapter.FlexibleAdapter import eu.davidea.flexibleadapter.items.AbstractFlexibleItem @@ -32,14 +31,14 @@ class CatalogueSearchItem(val source: CatalogueSource, val results: List>): CatalogueSearchHolder { + override fun createViewHolder(view: View, adapter: FlexibleAdapter>): CatalogueSearchHolder { return CatalogueSearchHolder(view, adapter as CatalogueSearchAdapter) } /** * Bind item to view. */ - override fun bindViewHolder(adapter: FlexibleAdapter>, holder: CatalogueSearchHolder, + override fun bindViewHolder(adapter: FlexibleAdapter>, holder: CatalogueSearchHolder, position: Int, payloads: List?) { holder.bind(this) } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/latest/LatestUpdatesController.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/latest/LatestUpdatesController.kt index 221a2142e..2475151a6 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/latest/LatestUpdatesController.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/latest/LatestUpdatesController.kt @@ -1,7 +1,6 @@ package eu.kanade.tachiyomi.ui.catalogue.latest import android.os.Bundle -import android.support.v4.widget.DrawerLayout import android.view.Menu import android.view.ViewGroup import eu.kanade.tachiyomi.R @@ -28,11 +27,11 @@ class LatestUpdatesController(bundle: Bundle) : BrowseCatalogueController(bundle menu.findItem(R.id.action_set_filter).isVisible = false } - override fun createSecondaryDrawer(drawer: DrawerLayout): ViewGroup? { + override fun createSecondaryDrawer(drawer: androidx.drawerlayout.widget.DrawerLayout): ViewGroup? { return null } - override fun cleanupSecondaryDrawer(drawer: DrawerLayout) { + override fun cleanupSecondaryDrawer(drawer: androidx.drawerlayout.widget.DrawerLayout) { } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/category/CategoryController.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/category/CategoryController.kt index 61effcd0a..950d0460d 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/category/CategoryController.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/category/CategoryController.kt @@ -1,11 +1,10 @@ package eu.kanade.tachiyomi.ui.category -import android.support.design.widget.Snackbar -import android.support.v7.app.AppCompatActivity -import android.support.v7.view.ActionMode -import android.support.v7.widget.LinearLayoutManager -import android.support.v7.widget.RecyclerView import android.view.* +import androidx.appcompat.app.AppCompatActivity +import androidx.appcompat.view.ActionMode +import androidx.recyclerview.widget.RecyclerView +import com.google.android.material.snackbar.Snackbar import com.jakewharton.rxbinding.view.clicks import eu.davidea.flexibleadapter.FlexibleAdapter import eu.davidea.flexibleadapter.SelectableAdapter @@ -74,7 +73,7 @@ class CategoryController : NucleusController(), super.onViewCreated(view) adapter = CategoryAdapter(this@CategoryController) - recycler.layoutManager = LinearLayoutManager(view.context) + recycler.layoutManager = androidx.recyclerview.widget.LinearLayoutManager(view.context) recycler.setHasFixedSize(true) recycler.adapter = adapter adapter?.isHandleDragEnabled = true @@ -207,7 +206,7 @@ class CategoryController : NucleusController(), */ override fun onItemClick(view: View, position: Int): Boolean { // Check if action mode is initialized and selected item exist. - if (actionMode != null && position != RecyclerView.NO_POSITION) { + if (actionMode != null && position != androidx.recyclerview.widget.RecyclerView.NO_POSITION) { toggleSelection(position) return true } else { diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/category/CategoryItem.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/category/CategoryItem.kt index 74107d03a..d168a2afd 100755 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/category/CategoryItem.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/category/CategoryItem.kt @@ -1,6 +1,5 @@ package eu.kanade.tachiyomi.ui.category -import android.support.v7.widget.RecyclerView import android.view.View import eu.davidea.flexibleadapter.FlexibleAdapter import eu.davidea.flexibleadapter.items.AbstractFlexibleItem @@ -31,7 +30,7 @@ class CategoryItem(val category: Category) : AbstractFlexibleItem>): CategoryHolder { + override fun createViewHolder(view: View, adapter: FlexibleAdapter>): CategoryHolder { return CategoryHolder(view, adapter as CategoryAdapter) } @@ -43,7 +42,7 @@ class CategoryItem(val category: Category) : AbstractFlexibleItem>, + override fun bindViewHolder(adapter: FlexibleAdapter>, holder: CategoryHolder, position: Int, payloads: List?) { diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/download/DownloadController.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/download/DownloadController.kt index eeedd17cc..17c441014 100755 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/download/DownloadController.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/download/DownloadController.kt @@ -1,260 +1,259 @@ -package eu.kanade.tachiyomi.ui.download - -import android.support.v7.widget.LinearLayoutManager -import android.view.* -import eu.kanade.tachiyomi.R -import eu.kanade.tachiyomi.data.download.DownloadService -import eu.kanade.tachiyomi.data.download.model.Download -import eu.kanade.tachiyomi.source.model.Page -import eu.kanade.tachiyomi.ui.base.controller.NucleusController -import kotlinx.android.synthetic.main.download_controller.* -import rx.Observable -import rx.Subscription -import rx.android.schedulers.AndroidSchedulers -import java.util.* -import java.util.concurrent.TimeUnit - -/** - * Controller that shows the currently active downloads. - * Uses R.layout.fragment_download_queue. - */ -class DownloadController : NucleusController(), - DownloadAdapter.OnItemReleaseListener { - - /** - * Adapter containing the active downloads. - */ - private var adapter: DownloadAdapter? = null - - /** - * Map of subscriptions for active downloads. - */ - private val progressSubscriptions by lazy { HashMap() } - - /** - * Whether the download queue is running or not. - */ - private var isRunning: Boolean = false - - init { - setHasOptionsMenu(true) - } - - override fun inflateView(inflater: LayoutInflater, container: ViewGroup): View { - return inflater.inflate(R.layout.download_controller, container, false) - } - - override fun createPresenter(): DownloadPresenter { - return DownloadPresenter() - } - - override fun getTitle(): String? { - return resources?.getString(R.string.label_download_queue) - } - - override fun onViewCreated(view: View) { - super.onViewCreated(view) - - // Check if download queue is empty and update information accordingly. - setInformationView() - - // Initialize adapter. - adapter = DownloadAdapter(this@DownloadController) - recycler.adapter = adapter - adapter?.isHandleDragEnabled = true - - // Set the layout manager for the recycler and fixed size. - recycler.layoutManager = LinearLayoutManager(view.context) - recycler.setHasFixedSize(true) - - // Suscribe to changes - DownloadService.runningRelay - .observeOn(AndroidSchedulers.mainThread()) - .subscribeUntilDestroy { onQueueStatusChange(it) } - - presenter.getDownloadStatusObservable() - .observeOn(AndroidSchedulers.mainThread()) - .subscribeUntilDestroy { onStatusChange(it) } - - presenter.getDownloadProgressObservable() - .observeOn(AndroidSchedulers.mainThread()) - .subscribeUntilDestroy { onUpdateDownloadedPages(it) } - } - - override fun onDestroyView(view: View) { - for (subscription in progressSubscriptions.values) { - subscription.unsubscribe() - } - progressSubscriptions.clear() - adapter = null - super.onDestroyView(view) - } - - override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) { - inflater.inflate(R.menu.download_queue, menu) - } - - override fun onPrepareOptionsMenu(menu: Menu) { - // Set start button visibility. - menu.findItem(R.id.start_queue).isVisible = !isRunning && !presenter.downloadQueue.isEmpty() - - // Set pause button visibility. - menu.findItem(R.id.pause_queue).isVisible = isRunning - - // Set clear button visibility. - menu.findItem(R.id.clear_queue).isVisible = !presenter.downloadQueue.isEmpty() - } - - override fun onOptionsItemSelected(item: MenuItem): Boolean { - val context = applicationContext ?: return false - when (item.itemId) { - R.id.start_queue -> DownloadService.start(context) - R.id.pause_queue -> { - DownloadService.stop(context) - presenter.pauseDownloads() - } - R.id.clear_queue -> { - DownloadService.stop(context) - presenter.clearQueue() - } - else -> return super.onOptionsItemSelected(item) - } - return true - } - - /** - * Called when the status of a download changes. - * - * @param download the download whose status has changed. - */ - private fun onStatusChange(download: Download) { - when (download.status) { - Download.DOWNLOADING -> { - observeProgress(download) - // Initial update of the downloaded pages - onUpdateDownloadedPages(download) - } - Download.DOWNLOADED -> { - unsubscribeProgress(download) - onUpdateProgress(download) - onUpdateDownloadedPages(download) - } - Download.ERROR -> unsubscribeProgress(download) - } - } - - /** - * Observe the progress of a download and notify the view. - * - * @param download the download to observe its progress. - */ - private fun observeProgress(download: Download) { - val subscription = Observable.interval(50, TimeUnit.MILLISECONDS) - // Get the sum of percentages for all the pages. - .flatMap { - Observable.from(download.pages) - .map(Page::progress) - .reduce { x, y -> x + y } - } - // Keep only the latest emission to avoid backpressure. - .onBackpressureLatest() - .observeOn(AndroidSchedulers.mainThread()) - .subscribe { progress -> - // Update the view only if the progress has changed. - if (download.totalProgress != progress) { - download.totalProgress = progress - onUpdateProgress(download) - } - } - - // Avoid leaking subscriptions - progressSubscriptions.remove(download)?.unsubscribe() - - progressSubscriptions[download] = subscription - } - - /** - * Unsubscribes the given download from the progress subscriptions. - * - * @param download the download to unsubscribe. - */ - private fun unsubscribeProgress(download: Download) { - progressSubscriptions.remove(download)?.unsubscribe() - } - - /** - * Called when the queue's status has changed. Updates the visibility of the buttons. - * - * @param running whether the queue is now running or not. - */ - private fun onQueueStatusChange(running: Boolean) { - isRunning = running - activity?.invalidateOptionsMenu() - - // Check if download queue is empty and update information accordingly. - setInformationView() - } - - /** - * Called from the presenter to assign the downloads for the adapter. - * - * @param downloads the downloads from the queue. - */ - fun onNextDownloads(downloads: List) { - activity?.invalidateOptionsMenu() - setInformationView() - adapter?.updateDataSet(downloads) - } - - /** - * Called when the progress of a download changes. - * - * @param download the download whose progress has changed. - */ - fun onUpdateProgress(download: Download) { - getHolder(download)?.notifyProgress() - } - - /** - * Called when a page of a download is downloaded. - * - * @param download the download whose page has been downloaded. - */ - fun onUpdateDownloadedPages(download: Download) { - getHolder(download)?.notifyDownloadedPages() - } - - /** - * Returns the holder for the given download. - * - * @param download the download to find. - * @return the holder of the download or null if it's not bound. - */ - private fun getHolder(download: Download): DownloadHolder? { - return recycler?.findViewHolderForItemId(download.chapter.id!!) as? DownloadHolder - } - - /** - * Set information view when queue is empty - */ - private fun setInformationView() { - if (presenter.downloadQueue.isEmpty()) { - empty_view?.show(R.drawable.ic_file_download_black_128dp, - R.string.information_no_downloads) - } else { - empty_view?.hide() - } - } - - /** - * Called when an item is released from a drag. - * - * @param position The position of the released item. - */ - override fun onItemReleased(position: Int) { - val adapter = adapter ?: return - val downloads = (0 until adapter.itemCount).mapNotNull { adapter.getItem(it)?.download } - presenter.reorder(downloads) - } - -} +package eu.kanade.tachiyomi.ui.download + +import android.view.* +import eu.kanade.tachiyomi.R +import eu.kanade.tachiyomi.data.download.DownloadService +import eu.kanade.tachiyomi.data.download.model.Download +import eu.kanade.tachiyomi.source.model.Page +import eu.kanade.tachiyomi.ui.base.controller.NucleusController +import kotlinx.android.synthetic.main.download_controller.* +import rx.Observable +import rx.Subscription +import rx.android.schedulers.AndroidSchedulers +import java.util.* +import java.util.concurrent.TimeUnit + +/** + * Controller that shows the currently active downloads. + * Uses R.layout.fragment_download_queue. + */ +class DownloadController : NucleusController(), + DownloadAdapter.OnItemReleaseListener { + + /** + * Adapter containing the active downloads. + */ + private var adapter: DownloadAdapter? = null + + /** + * Map of subscriptions for active downloads. + */ + private val progressSubscriptions by lazy { HashMap() } + + /** + * Whether the download queue is running or not. + */ + private var isRunning: Boolean = false + + init { + setHasOptionsMenu(true) + } + + override fun inflateView(inflater: LayoutInflater, container: ViewGroup): View { + return inflater.inflate(R.layout.download_controller, container, false) + } + + override fun createPresenter(): DownloadPresenter { + return DownloadPresenter() + } + + override fun getTitle(): String? { + return resources?.getString(R.string.label_download_queue) + } + + override fun onViewCreated(view: View) { + super.onViewCreated(view) + + // Check if download queue is empty and update information accordingly. + setInformationView() + + // Initialize adapter. + adapter = DownloadAdapter(this@DownloadController) + recycler.adapter = adapter + adapter?.isHandleDragEnabled = true + + // Set the layout manager for the recycler and fixed size. + recycler.layoutManager = androidx.recyclerview.widget.LinearLayoutManager(view.context) + recycler.setHasFixedSize(true) + + // Suscribe to changes + DownloadService.runningRelay + .observeOn(AndroidSchedulers.mainThread()) + .subscribeUntilDestroy { onQueueStatusChange(it) } + + presenter.getDownloadStatusObservable() + .observeOn(AndroidSchedulers.mainThread()) + .subscribeUntilDestroy { onStatusChange(it) } + + presenter.getDownloadProgressObservable() + .observeOn(AndroidSchedulers.mainThread()) + .subscribeUntilDestroy { onUpdateDownloadedPages(it) } + } + + override fun onDestroyView(view: View) { + for (subscription in progressSubscriptions.values) { + subscription.unsubscribe() + } + progressSubscriptions.clear() + adapter = null + super.onDestroyView(view) + } + + override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) { + inflater.inflate(R.menu.download_queue, menu) + } + + override fun onPrepareOptionsMenu(menu: Menu) { + // Set start button visibility. + menu.findItem(R.id.start_queue).isVisible = !isRunning && !presenter.downloadQueue.isEmpty() + + // Set pause button visibility. + menu.findItem(R.id.pause_queue).isVisible = isRunning + + // Set clear button visibility. + menu.findItem(R.id.clear_queue).isVisible = !presenter.downloadQueue.isEmpty() + } + + override fun onOptionsItemSelected(item: MenuItem): Boolean { + val context = applicationContext ?: return false + when (item.itemId) { + R.id.start_queue -> DownloadService.start(context) + R.id.pause_queue -> { + DownloadService.stop(context) + presenter.pauseDownloads() + } + R.id.clear_queue -> { + DownloadService.stop(context) + presenter.clearQueue() + } + else -> return super.onOptionsItemSelected(item) + } + return true + } + + /** + * Called when the status of a download changes. + * + * @param download the download whose status has changed. + */ + private fun onStatusChange(download: Download) { + when (download.status) { + Download.DOWNLOADING -> { + observeProgress(download) + // Initial update of the downloaded pages + onUpdateDownloadedPages(download) + } + Download.DOWNLOADED -> { + unsubscribeProgress(download) + onUpdateProgress(download) + onUpdateDownloadedPages(download) + } + Download.ERROR -> unsubscribeProgress(download) + } + } + + /** + * Observe the progress of a download and notify the view. + * + * @param download the download to observe its progress. + */ + private fun observeProgress(download: Download) { + val subscription = Observable.interval(50, TimeUnit.MILLISECONDS) + // Get the sum of percentages for all the pages. + .flatMap { + Observable.from(download.pages) + .map(Page::progress) + .reduce { x, y -> x + y } + } + // Keep only the latest emission to avoid backpressure. + .onBackpressureLatest() + .observeOn(AndroidSchedulers.mainThread()) + .subscribe { progress -> + // Update the view only if the progress has changed. + if (download.totalProgress != progress) { + download.totalProgress = progress + onUpdateProgress(download) + } + } + + // Avoid leaking subscriptions + progressSubscriptions.remove(download)?.unsubscribe() + + progressSubscriptions[download] = subscription + } + + /** + * Unsubscribes the given download from the progress subscriptions. + * + * @param download the download to unsubscribe. + */ + private fun unsubscribeProgress(download: Download) { + progressSubscriptions.remove(download)?.unsubscribe() + } + + /** + * Called when the queue's status has changed. Updates the visibility of the buttons. + * + * @param running whether the queue is now running or not. + */ + private fun onQueueStatusChange(running: Boolean) { + isRunning = running + activity?.invalidateOptionsMenu() + + // Check if download queue is empty and update information accordingly. + setInformationView() + } + + /** + * Called from the presenter to assign the downloads for the adapter. + * + * @param downloads the downloads from the queue. + */ + fun onNextDownloads(downloads: List) { + activity?.invalidateOptionsMenu() + setInformationView() + adapter?.updateDataSet(downloads) + } + + /** + * Called when the progress of a download changes. + * + * @param download the download whose progress has changed. + */ + fun onUpdateProgress(download: Download) { + getHolder(download)?.notifyProgress() + } + + /** + * Called when a page of a download is downloaded. + * + * @param download the download whose page has been downloaded. + */ + fun onUpdateDownloadedPages(download: Download) { + getHolder(download)?.notifyDownloadedPages() + } + + /** + * Returns the holder for the given download. + * + * @param download the download to find. + * @return the holder of the download or null if it's not bound. + */ + private fun getHolder(download: Download): DownloadHolder? { + return recycler?.findViewHolderForItemId(download.chapter.id!!) as? DownloadHolder + } + + /** + * Set information view when queue is empty + */ + private fun setInformationView() { + if (presenter.downloadQueue.isEmpty()) { + empty_view?.show(R.drawable.ic_file_download_black_128dp, + R.string.information_no_downloads) + } else { + empty_view?.hide() + } + } + + /** + * Called when an item is released from a drag. + * + * @param position The position of the released item. + */ + override fun onItemReleased(position: Int) { + val adapter = adapter ?: return + val downloads = (0 until adapter.itemCount).mapNotNull { adapter.getItem(it)?.download } + presenter.reorder(downloads) + } + +} diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/download/DownloadItem.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/download/DownloadItem.kt index 48cc31b9c..b68da76b0 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/download/DownloadItem.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/download/DownloadItem.kt @@ -1,7 +1,6 @@ package eu.kanade.tachiyomi.ui.download import android.view.View -import android.support.v7.widget.RecyclerView import eu.davidea.flexibleadapter.FlexibleAdapter import eu.davidea.flexibleadapter.items.AbstractFlexibleItem import eu.davidea.flexibleadapter.items.IFlexible @@ -28,7 +27,7 @@ class DownloadItem(val download: Download) : AbstractFlexibleItem>): DownloadHolder { return DownloadHolder(view, adapter as DownloadAdapter) } @@ -41,8 +40,8 @@ class DownloadItem(val download: Download) : AbstractFlexibleItem>, - holder: DownloadHolder, position: Int, payloads: MutableList) { + override fun bindViewHolder(adapter: FlexibleAdapter>, + holder: DownloadHolder, position: Int, payloads: MutableList) { holder.bind(download) } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/extension/ExtensionController.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/extension/ExtensionController.kt index 5814d6a62..ab44c0608 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/extension/ExtensionController.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/extension/ExtensionController.kt @@ -1,12 +1,7 @@ package eu.kanade.tachiyomi.ui.extension -import android.support.v7.widget.LinearLayoutManager -import android.support.v7.widget.SearchView -import android.view.LayoutInflater -import android.view.Menu -import android.view.MenuInflater -import android.view.View -import android.view.ViewGroup +import android.view.* +import androidx.appcompat.widget.SearchView import com.jakewharton.rxbinding.support.v4.widget.refreshes import com.jakewharton.rxbinding.support.v7.widget.queryTextChanges import eu.davidea.flexibleadapter.FlexibleAdapter @@ -63,7 +58,7 @@ open class ExtensionController : NucleusController(), // Initialize adapter, scroll listener and recycler views adapter = ExtensionAdapter(this) // Create recycler and set adapter. - ext_recycler.layoutManager = LinearLayoutManager(view.context) + ext_recycler.layoutManager = androidx.recyclerview.widget.LinearLayoutManager(view.context) ext_recycler.adapter = adapter ext_recycler.addItemDecoration(ExtensionDividerItemDecoration(view.context)) } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/extension/ExtensionDetailsController.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/extension/ExtensionDetailsController.kt index 6a5691763..a65dd35e4 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/extension/ExtensionDetailsController.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/extension/ExtensionDetailsController.kt @@ -3,16 +3,15 @@ package eu.kanade.tachiyomi.ui.extension import android.annotation.SuppressLint import android.content.Context import android.os.Bundle -import android.support.v7.preference.* -import android.support.v7.preference.internal.AbstractMultiSelectListPreference -import android.support.v7.widget.DividerItemDecoration -import android.support.v7.widget.DividerItemDecoration.VERTICAL -import android.support.v7.widget.LinearLayoutManager import android.util.TypedValue import android.view.ContextThemeWrapper import android.view.LayoutInflater import android.view.View import android.view.ViewGroup +import androidx.preference.* +import androidx.recyclerview.widget.DividerItemDecoration +import androidx.recyclerview.widget.DividerItemDecoration.VERTICAL +import androidx.recyclerview.widget.LinearLayoutManager import com.elvishew.xlog.XLog import com.jakewharton.rxbinding.view.clicks import eu.kanade.tachiyomi.R @@ -80,7 +79,7 @@ class ExtensionDetailsController(bundle: Bundle? = null) : val manager = PreferenceManager(themedContext) manager.preferenceDataStore = EmptyPreferenceDataStore() manager.onDisplayPreferenceDialogListener = this - val screen = manager.createPreferenceScreen(themedContext) + val screen = manager.createPreferenceScreen(context) preferenceScreen = screen val multiSource = extension.sources.size > 1 @@ -151,10 +150,12 @@ class ExtensionDetailsController(bundle: Bundle? = null) : val newScreen = screen.preferenceManager.createPreferenceScreen(context) source.setupPreferenceScreen(newScreen) - for (i in 0 until newScreen.preferenceCount) { - val pref = newScreen.getPreference(i) + // Reparent the preferences + while (newScreen.preferenceCount != 0) { + val pref = newScreen.getPreference(0) pref.preferenceDataStore = dataStore pref.order = Int.MAX_VALUE // reset to default order + newScreen.removePreference(pref) screen.addPreference(pref) } } @@ -180,7 +181,7 @@ class ExtensionDetailsController(bundle: Bundle? = null) : .newInstance(preference.getKey()) is ListPreference -> ListPreferenceDialogController .newInstance(preference.getKey()) - is AbstractMultiSelectListPreference -> MultiSelectListPreferenceDialogController + is MultiSelectListPreference -> MultiSelectListPreferenceDialogController .newInstance(preference.getKey()) else -> throw IllegalArgumentException("Tried to display dialog for unknown " + "preference type. Did you forget to override onDisplayPreferenceDialog()?") @@ -189,8 +190,8 @@ class ExtensionDetailsController(bundle: Bundle? = null) : f.showDialog(router) } - override fun findPreference(key: CharSequence?): Preference { - return preferenceScreen!!.getPreference(lastOpenPreferencePosition!!) + override fun findPreference(key: CharSequence): T? { + return preferenceScreen!!.findPreference(key) } override fun loginDialogClosed(source: LoginSource) { diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/extension/ExtensionDividerItemDecoration.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/extension/ExtensionDividerItemDecoration.kt index 247206df7..84798947f 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/extension/ExtensionDividerItemDecoration.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/extension/ExtensionDividerItemDecoration.kt @@ -4,10 +4,9 @@ import android.content.Context import android.graphics.Canvas import android.graphics.Rect import android.graphics.drawable.Drawable -import android.support.v7.widget.RecyclerView import android.view.View -class ExtensionDividerItemDecoration(context: Context) : RecyclerView.ItemDecoration() { +class ExtensionDividerItemDecoration(context: Context) : androidx.recyclerview.widget.RecyclerView.ItemDecoration() { private val divider: Drawable @@ -17,14 +16,14 @@ class ExtensionDividerItemDecoration(context: Context) : RecyclerView.ItemDecora a.recycle() } - override fun onDraw(c: Canvas, parent: RecyclerView, state: RecyclerView.State) { + override fun onDraw(c: Canvas, parent: androidx.recyclerview.widget.RecyclerView, state: androidx.recyclerview.widget.RecyclerView.State) { val childCount = parent.childCount for (i in 0 until childCount - 1) { val child = parent.getChildAt(i) val holder = parent.getChildViewHolder(child) if (holder is ExtensionHolder && parent.getChildViewHolder(parent.getChildAt(i + 1)) is ExtensionHolder) { - val params = child.layoutParams as RecyclerView.LayoutParams + val params = child.layoutParams as androidx.recyclerview.widget.RecyclerView.LayoutParams val top = child.bottom + params.bottomMargin val bottom = top + divider.intrinsicHeight val left = parent.paddingLeft + holder.margin @@ -36,8 +35,8 @@ class ExtensionDividerItemDecoration(context: Context) : RecyclerView.ItemDecora } } - override fun getItemOffsets(outRect: Rect, view: View, parent: RecyclerView, - state: RecyclerView.State) { + override fun getItemOffsets(outRect: Rect, view: View, parent: androidx.recyclerview.widget.RecyclerView, + state: androidx.recyclerview.widget.RecyclerView.State) { outRect.set(0, 0, 0, divider.intrinsicHeight) } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/extension/ExtensionGroupItem.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/extension/ExtensionGroupItem.kt index b4460ea69..b0667775a 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/extension/ExtensionGroupItem.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/extension/ExtensionGroupItem.kt @@ -1,6 +1,5 @@ package eu.kanade.tachiyomi.ui.extension -import android.support.v7.widget.RecyclerView import android.view.View import eu.davidea.flexibleadapter.FlexibleAdapter import eu.davidea.flexibleadapter.items.AbstractHeaderItem @@ -25,14 +24,14 @@ data class ExtensionGroupItem(val name: String, val size: Int) : AbstractHeaderI /** * Creates a new view holder for this item. */ - override fun createViewHolder(view: View, adapter: FlexibleAdapter>): ExtensionGroupHolder { + override fun createViewHolder(view: View, adapter: FlexibleAdapter>): ExtensionGroupHolder { return ExtensionGroupHolder(view, adapter) } /** * Binds this item to the given view holder. */ - override fun bindViewHolder(adapter: FlexibleAdapter>, holder: ExtensionGroupHolder, + override fun bindViewHolder(adapter: FlexibleAdapter>, holder: ExtensionGroupHolder, position: Int, payloads: List?) { holder.bind(this) diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/extension/ExtensionItem.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/extension/ExtensionItem.kt index ba6c5a5a9..09d0dc023 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/extension/ExtensionItem.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/extension/ExtensionItem.kt @@ -1,6 +1,5 @@ package eu.kanade.tachiyomi.ui.extension -import android.support.v7.widget.RecyclerView import android.view.View import eu.davidea.flexibleadapter.FlexibleAdapter import eu.davidea.flexibleadapter.items.AbstractSectionableItem @@ -31,14 +30,14 @@ data class ExtensionItem(val extension: Extension, /** * Creates a new view holder for this item. */ - override fun createViewHolder(view: View, adapter: FlexibleAdapter>): ExtensionHolder { + override fun createViewHolder(view: View, adapter: FlexibleAdapter>): ExtensionHolder { return ExtensionHolder(view, adapter as ExtensionAdapter) } /** * Binds this item to the given view holder. */ - override fun bindViewHolder(adapter: FlexibleAdapter>, holder: ExtensionHolder, + override fun bindViewHolder(adapter: FlexibleAdapter>, holder: ExtensionHolder, position: Int, payloads: List?) { if (payloads == null || payloads.isEmpty()) { diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryCategoryView.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryCategoryView.kt index 726796dea..07475ab96 100755 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryCategoryView.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryCategoryView.kt @@ -1,8 +1,6 @@ package eu.kanade.tachiyomi.ui.library import android.content.Context -import android.support.v7.widget.LinearLayoutManager -import android.support.v7.widget.RecyclerView import android.util.AttributeSet import android.view.View import android.widget.FrameLayout @@ -53,7 +51,7 @@ class LibraryCategoryView @JvmOverloads constructor(context: Context, attrs: Att /** * Recycler view of the list of manga. */ - private lateinit var recycler: RecyclerView + private lateinit var recycler: androidx.recyclerview.widget.RecyclerView /** * Adapter to hold the manga in this category. @@ -80,8 +78,8 @@ class LibraryCategoryView @JvmOverloads constructor(context: Context, attrs: Att this.controller = controller recycler = if (preferences.libraryAsList().getOrDefault()) { - (swipe_refresh.inflate(R.layout.library_list_recycler) as RecyclerView).apply { - layoutManager = LinearLayoutManager(context) + (swipe_refresh.inflate(R.layout.library_list_recycler) as androidx.recyclerview.widget.RecyclerView).apply { + layoutManager = androidx.recyclerview.widget.LinearLayoutManager(context) } } else { (swipe_refresh.inflate(R.layout.library_grid_recycler) as AutofitRecyclerView).apply { @@ -95,10 +93,10 @@ class LibraryCategoryView @JvmOverloads constructor(context: Context, attrs: Att recycler.adapter = adapter swipe_refresh.addView(recycler) - recycler.addOnScrollListener(object : RecyclerView.OnScrollListener() { - override fun onScrollStateChanged(recycler: RecyclerView, newState: Int) { + recycler.addOnScrollListener(object : androidx.recyclerview.widget.RecyclerView.OnScrollListener() { + override fun onScrollStateChanged(recycler: androidx.recyclerview.widget.RecyclerView, newState: Int) { // Disable swipe refresh when view is not at the top - val firstPos = (recycler.layoutManager as LinearLayoutManager) + val firstPos = (recycler.layoutManager as androidx.recyclerview.widget.LinearLayoutManager) .findFirstCompletelyVisibleItemPosition() swipe_refresh.isEnabled = firstPos <= 0 } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryController.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryController.kt index 9bfed95d7..97767fd95 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryController.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryController.kt @@ -5,17 +5,17 @@ import android.content.Intent import android.content.res.Configuration import android.graphics.Color import android.os.Bundle -import android.support.design.widget.TabLayout -import android.support.v4.graphics.drawable.DrawableCompat -import android.support.v4.widget.DrawerLayout -import android.support.v7.app.AppCompatActivity -import android.support.v7.view.ActionMode -import android.support.v7.widget.SearchView import android.view.* +import androidx.appcompat.app.AppCompatActivity +import androidx.appcompat.view.ActionMode +import androidx.appcompat.widget.SearchView +import androidx.core.graphics.drawable.DrawableCompat +import androidx.drawerlayout.widget.DrawerLayout import com.afollestad.materialdialogs.MaterialDialog import com.bluelinelabs.conductor.ControllerChangeHandler import com.bluelinelabs.conductor.ControllerChangeType import com.f2prateek.rx.preferences.Preference +import com.google.android.material.tabs.TabLayout import com.jakewharton.rxbinding.support.v4.view.pageSelections import com.jakewharton.rxbinding.support.v7.widget.queryTextChanges import com.jakewharton.rxrelay.BehaviorRelay @@ -120,7 +120,7 @@ class LibraryController( /** * Drawer listener to allow swipe only for closing the drawer. */ - private var drawerListener: DrawerLayout.DrawerListener? = null + private var drawerListener: androidx.drawerlayout.widget.DrawerLayout.DrawerListener? = null private var tabsVisibilityRelay: BehaviorRelay = BehaviorRelay.create(false) @@ -202,10 +202,10 @@ class LibraryController( super.onDestroyView(view) } - override fun createSecondaryDrawer(drawer: DrawerLayout): ViewGroup { + override fun createSecondaryDrawer(drawer: androidx.drawerlayout.widget.DrawerLayout): ViewGroup { val view = drawer.inflate(R.layout.library_drawer) as LibraryNavigationView navView = view - drawer.setDrawerLockMode(DrawerLayout.LOCK_MODE_UNLOCKED, Gravity.END) + drawer.setDrawerLockMode(androidx.drawerlayout.widget.DrawerLayout.LOCK_MODE_UNLOCKED, Gravity.END) navView?.onGroupClicked = { group -> when (group) { @@ -219,7 +219,7 @@ class LibraryController( return view } - override fun cleanupSecondaryDrawer(drawer: DrawerLayout) { + override fun cleanupSecondaryDrawer(drawer: androidx.drawerlayout.widget.DrawerLayout) { navView = null } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryItem.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryItem.kt index da94cc547..6eb34e2f9 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryItem.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryItem.kt @@ -1,109 +1,108 @@ -package eu.kanade.tachiyomi.ui.library - -import android.support.v7.widget.RecyclerView -import android.view.Gravity -import android.view.View -import android.view.ViewGroup.LayoutParams.MATCH_PARENT -import android.widget.FrameLayout -import com.f2prateek.rx.preferences.Preference -import eu.davidea.flexibleadapter.FlexibleAdapter -import eu.davidea.flexibleadapter.items.AbstractFlexibleItem -import eu.davidea.flexibleadapter.items.IFilterable -import eu.davidea.flexibleadapter.items.IFlexible -import eu.kanade.tachiyomi.R -import eu.kanade.tachiyomi.data.database.models.LibraryManga -import eu.kanade.tachiyomi.data.preference.getOrDefault -import eu.kanade.tachiyomi.widget.AutofitRecyclerView -import kotlinx.android.synthetic.main.catalogue_grid_item.view.* - -class LibraryItem(val manga: LibraryManga, private val libraryAsList: Preference) : - AbstractFlexibleItem(), IFilterable { - var downloadCount = -1 - - override fun getLayoutRes(): Int { - return if (libraryAsList.getOrDefault()) - R.layout.catalogue_list_item - else - R.layout.catalogue_grid_item - } - - override fun createViewHolder(view: View, adapter: FlexibleAdapter>): LibraryHolder { - val parent = adapter.recyclerView - return if (parent is AutofitRecyclerView) { - view.apply { - val coverHeight = parent.itemWidth / 3 * 4 - card.layoutParams = FrameLayout.LayoutParams(MATCH_PARENT, coverHeight) - gradient.layoutParams = FrameLayout.LayoutParams( - MATCH_PARENT, coverHeight / 2, Gravity.BOTTOM) - } - LibraryGridHolder(view, adapter) - } else { - LibraryListHolder(view, adapter) - } - } - - override fun bindViewHolder(adapter: FlexibleAdapter>, - holder: LibraryHolder, - position: Int, - payloads: List?) { - - holder.onSetValues(this) - } - - /** - * Filters a manga depending on a query. - * - * @param constraint the query to apply. - * @return true if the manga should be included, false otherwise. - */ - override fun filter(constraint: String): Boolean { - return manga.title.contains(constraint, true) || - (manga.author?.contains(constraint, true) ?: false) || - if (constraint.contains(" ") || constraint.contains("\"")) { - val genres = manga.genre?.split(", ")?.map { - it.drop(it.indexOfFirst{it==':'}+1).toLowerCase().trim() //tachiEH tag namespaces - } - var clean_constraint = "" - var ignorespace = false - for (i in constraint.trim().toLowerCase()) { - if (i==' ') { - if (!ignorespace) { - clean_constraint = clean_constraint + "," - } else { - clean_constraint = clean_constraint + " " - } - } else if (i=='"') { - ignorespace = !ignorespace - } else { - clean_constraint = clean_constraint + Character.toString(i) - } - } - clean_constraint.split(",").all { containsGenre(it.trim(), genres) } - } - else containsGenre(constraint, manga.genre?.split(", ")?.map { - it.drop(it.indexOfFirst{it==':'}+1).toLowerCase().trim() //tachiEH tag namespaces - }) - } - - private fun containsGenre(tag: String, genres: List?): Boolean { - return if (tag.startsWith("-")) - genres?.find { - it.trim().toLowerCase() == tag.substringAfter("-").toLowerCase() - } == null - else - genres?.find { - it.trim().toLowerCase() == tag.toLowerCase() - } != null - } - - override fun equals(other: Any?): Boolean { - if (other is LibraryItem) { - return manga.id == other.manga.id - } - return false - } - - override fun hashCode(): Int { - return manga.id!!.hashCode() - } -} +package eu.kanade.tachiyomi.ui.library + +import android.view.Gravity +import android.view.View +import android.view.ViewGroup.LayoutParams.MATCH_PARENT +import android.widget.FrameLayout +import com.f2prateek.rx.preferences.Preference +import eu.davidea.flexibleadapter.FlexibleAdapter +import eu.davidea.flexibleadapter.items.AbstractFlexibleItem +import eu.davidea.flexibleadapter.items.IFilterable +import eu.davidea.flexibleadapter.items.IFlexible +import eu.kanade.tachiyomi.R +import eu.kanade.tachiyomi.data.database.models.LibraryManga +import eu.kanade.tachiyomi.data.preference.getOrDefault +import eu.kanade.tachiyomi.widget.AutofitRecyclerView +import kotlinx.android.synthetic.main.catalogue_grid_item.view.* + +class LibraryItem(val manga: LibraryManga, private val libraryAsList: Preference) : + AbstractFlexibleItem(), IFilterable { + var downloadCount = -1 + + override fun getLayoutRes(): Int { + return if (libraryAsList.getOrDefault()) + R.layout.catalogue_list_item + else + R.layout.catalogue_grid_item + } + + override fun createViewHolder(view: View, adapter: FlexibleAdapter>): LibraryHolder { + val parent = adapter.recyclerView + return if (parent is AutofitRecyclerView) { + view.apply { + val coverHeight = parent.itemWidth / 3 * 4 + card.layoutParams = FrameLayout.LayoutParams(MATCH_PARENT, coverHeight) + gradient.layoutParams = FrameLayout.LayoutParams( + MATCH_PARENT, coverHeight / 2, Gravity.BOTTOM) + } + LibraryGridHolder(view, adapter) + } else { + LibraryListHolder(view, adapter) + } + } + + override fun bindViewHolder(adapter: FlexibleAdapter>, + holder: LibraryHolder, + position: Int, + payloads: List?) { + + holder.onSetValues(this) + } + + /** + * Filters a manga depending on a query. + * + * @param constraint the query to apply. + * @return true if the manga should be included, false otherwise. + */ + override fun filter(constraint: String): Boolean { + return manga.title.contains(constraint, true) || + (manga.author?.contains(constraint, true) ?: false) || + if (constraint.contains(" ") || constraint.contains("\"")) { + val genres = manga.genre?.split(", ")?.map { + it.drop(it.indexOfFirst{it==':'}+1).toLowerCase().trim() //tachiEH tag namespaces + } + var clean_constraint = "" + var ignorespace = false + for (i in constraint.trim().toLowerCase()) { + if (i==' ') { + if (!ignorespace) { + clean_constraint = clean_constraint + "," + } else { + clean_constraint = clean_constraint + " " + } + } else if (i=='"') { + ignorespace = !ignorespace + } else { + clean_constraint = clean_constraint + Character.toString(i) + } + } + clean_constraint.split(",").all { containsGenre(it.trim(), genres) } + } + else containsGenre(constraint, manga.genre?.split(", ")?.map { + it.drop(it.indexOfFirst{it==':'}+1).toLowerCase().trim() //tachiEH tag namespaces + }) + } + + private fun containsGenre(tag: String, genres: List?): Boolean { + return if (tag.startsWith("-")) + genres?.find { + it.trim().toLowerCase() == tag.substringAfter("-").toLowerCase() + } == null + else + genres?.find { + it.trim().toLowerCase() == tag.toLowerCase() + } != null + } + + override fun equals(other: Any?): Boolean { + if (other is LibraryItem) { + return manga.id == other.manga.id + } + return false + } + + override fun hashCode(): Int { + return manga.id!!.hashCode() + } +} diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/main/MainActivity.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/main/MainActivity.kt index 4a34b368c..58eff4f47 100755 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/main/MainActivity.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/main/MainActivity.kt @@ -3,19 +3,21 @@ package eu.kanade.tachiyomi.ui.main import android.animation.ObjectAnimator import android.app.ActivityManager import android.app.SearchManager -import android.app.usage.UsageStatsManager import android.app.Service +import android.app.usage.UsageStatsManager import android.content.Context import android.content.Intent import android.graphics.Color import android.os.Build import android.os.Bundle import android.os.Looper -import android.support.v4.view.GravityCompat -import android.support.v4.widget.DrawerLayout -import android.support.v7.graphics.drawable.DrawerArrowDrawable -import android.support.v7.widget.Toolbar +import android.text.TextUtils +import android.view.View import android.view.ViewGroup +import androidx.appcompat.graphics.drawable.DrawerArrowDrawable +import androidx.appcompat.widget.Toolbar +import androidx.core.view.GravityCompat +import androidx.drawerlayout.widget.DrawerLayout import com.bluelinelabs.conductor.* import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.data.preference.PreferencesHelper @@ -32,21 +34,19 @@ import eu.kanade.tachiyomi.ui.recent_updates.RecentChaptersController import eu.kanade.tachiyomi.ui.recently_read.RecentlyReadController import eu.kanade.tachiyomi.ui.setting.SettingsMainController import eu.kanade.tachiyomi.util.openInBrowser +import eu.kanade.tachiyomi.util.vibrate +import exh.EXHMigrations +import exh.eh.EHentaiUpdateWorker import exh.uconfig.WarnConfigureDialogController import exh.ui.batchadd.BatchAddController import exh.ui.lock.LockChangeHandler import exh.ui.lock.LockController import exh.ui.lock.lockEnabled import exh.ui.lock.notifyLockSecurity -import kotlinx.android.synthetic.main.main_activity.* -import uy.kohesive.injekt.injectLazy -import android.text.TextUtils -import android.view.View -import eu.kanade.tachiyomi.util.vibrate -import exh.EXHMigrations -import exh.eh.EHentaiUpdateWorker import exh.ui.migration.MetadataFetchDialog +import kotlinx.android.synthetic.main.main_activity.* import timber.log.Timber +import uy.kohesive.injekt.injectLazy import java.util.* import kotlin.collections.ArrayList @@ -357,9 +357,9 @@ class MainActivity : BaseActivity() { val showHamburger = router.backstackSize == 1 if (showHamburger) { - drawer.setDrawerLockMode(DrawerLayout.LOCK_MODE_UNLOCKED) + drawer.setDrawerLockMode(androidx.drawerlayout.widget.DrawerLayout.LOCK_MODE_UNLOCKED) } else { - drawer.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED) + drawer.setDrawerLockMode(androidx.drawerlayout.widget.DrawerLayout.LOCK_MODE_LOCKED_CLOSED) } // --> EH diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/main/TabsAnimator.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/main/TabsAnimator.kt index 304d32f2d..f3d8d955b 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/main/TabsAnimator.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/main/TabsAnimator.kt @@ -1,9 +1,9 @@ package eu.kanade.tachiyomi.ui.main import android.animation.ObjectAnimator -import android.support.design.widget.TabLayout import android.view.ViewTreeObserver import android.view.animation.DecelerateInterpolator +import com.google.android.material.tabs.TabLayout class TabsAnimator(val tabs: TabLayout) { diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaController.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaController.kt index 3b9011abe..71818aeaf 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaController.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaController.kt @@ -1,222 +1,222 @@ -package eu.kanade.tachiyomi.ui.manga - -import android.Manifest.permission.WRITE_EXTERNAL_STORAGE -import android.os.Bundle -import android.support.design.widget.TabLayout -import android.support.graphics.drawable.VectorDrawableCompat -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import android.widget.LinearLayout -import android.widget.TextView -import com.bluelinelabs.conductor.ControllerChangeHandler -import com.bluelinelabs.conductor.ControllerChangeType -import com.bluelinelabs.conductor.Router -import com.bluelinelabs.conductor.RouterTransaction -import com.bluelinelabs.conductor.support.RouterPagerAdapter -import com.jakewharton.rxrelay.BehaviorRelay -import com.jakewharton.rxrelay.PublishRelay -import eu.kanade.tachiyomi.R -import eu.kanade.tachiyomi.data.database.DatabaseHelper -import eu.kanade.tachiyomi.data.database.models.Manga -import eu.kanade.tachiyomi.data.track.TrackManager -import eu.kanade.tachiyomi.source.Source -import eu.kanade.tachiyomi.source.SourceManager -import eu.kanade.tachiyomi.ui.base.controller.RxController -import eu.kanade.tachiyomi.ui.base.controller.TabbedController -import eu.kanade.tachiyomi.ui.base.controller.requestPermissionsSafe -import eu.kanade.tachiyomi.ui.catalogue.CatalogueController -import eu.kanade.tachiyomi.ui.manga.chapter.ChaptersController -import eu.kanade.tachiyomi.ui.manga.chapter.ChaptersPresenter -import eu.kanade.tachiyomi.ui.manga.info.MangaInfoController -import eu.kanade.tachiyomi.ui.manga.track.TrackController -import eu.kanade.tachiyomi.util.toast -import kotlinx.android.synthetic.main.main_activity.* -import kotlinx.android.synthetic.main.manga_controller.* -import rx.Subscription -import uy.kohesive.injekt.Injekt -import uy.kohesive.injekt.api.get -import java.util.Date - -class MangaController : RxController, TabbedController { - - constructor(manga: Manga?, - fromCatalogue: Boolean = false, - smartSearchConfig: CatalogueController.SmartSearchConfig? = null, - update: Boolean = false) : super(Bundle().apply { - putLong(MANGA_EXTRA, manga?.id ?: 0) - putBoolean(FROM_CATALOGUE_EXTRA, fromCatalogue) - putParcelable(SMART_SEARCH_CONFIG_EXTRA, smartSearchConfig) - putBoolean(UPDATE_EXTRA, update) - }) { - this.manga = manga - if (manga != null) { - source = Injekt.get().getOrStub(manga.source) - } - } - - // EXH --> - constructor(redirect: ChaptersPresenter.EXHRedirect) : super(Bundle().apply { - putLong(MANGA_EXTRA, redirect.manga.id!!) - putBoolean(UPDATE_EXTRA, redirect.update) - }) { - this.manga = redirect.manga - if (manga != null) { - source = Injekt.get().getOrStub(redirect.manga.source) - } - } - // EXH <-- - - constructor(mangaId: Long) : this( - Injekt.get().getManga(mangaId).executeAsBlocking()) - - @Suppress("unused") - constructor(bundle: Bundle) : this(bundle.getLong(MANGA_EXTRA)) - - var manga: Manga? = null - private set - - var source: Source? = null - private set - - private var adapter: MangaDetailAdapter? = null - - val fromCatalogue = args.getBoolean(FROM_CATALOGUE_EXTRA, false) - - var update = args.getBoolean(UPDATE_EXTRA, false) - - // EXH --> - val smartSearchConfig: CatalogueController.SmartSearchConfig? = args.getParcelable(SMART_SEARCH_CONFIG_EXTRA) - // EXH <-- - - val lastUpdateRelay: BehaviorRelay = BehaviorRelay.create() - - val chapterCountRelay: BehaviorRelay = BehaviorRelay.create() - - val mangaFavoriteRelay: PublishRelay = PublishRelay.create() - - private val trackingIconRelay: BehaviorRelay = BehaviorRelay.create() - - private var trackingIconSubscription: Subscription? = null - - override fun getTitle(): String? { - return manga?.title - } - - override fun inflateView(inflater: LayoutInflater, container: ViewGroup): View { - return inflater.inflate(R.layout.manga_controller, container, false) - } - - override fun onViewCreated(view: View) { - super.onViewCreated(view) - - if (manga == null || source == null) return - - requestPermissionsSafe(arrayOf(WRITE_EXTERNAL_STORAGE), 301) - - adapter = MangaDetailAdapter() - manga_pager.offscreenPageLimit = 3 - manga_pager.adapter = adapter - - if (!fromCatalogue) - manga_pager.currentItem = CHAPTERS_CONTROLLER - } - - override fun onDestroyView(view: View) { - super.onDestroyView(view) - adapter = null - } - - override fun onChangeStarted(handler: ControllerChangeHandler, type: ControllerChangeType) { - super.onChangeStarted(handler, type) - if (type.isEnter) { - activity?.tabs?.setupWithViewPager(manga_pager) - trackingIconSubscription = trackingIconRelay.subscribe { setTrackingIconInternal(it) } - } - } - - override fun onChangeEnded(handler: ControllerChangeHandler, type: ControllerChangeType) { - super.onChangeEnded(handler, type) - if (manga == null || source == null) { - activity?.toast(R.string.manga_not_in_db) - router.popController(this) - } - } - - override fun configureTabs(tabs: TabLayout) { - with(tabs) { - tabGravity = TabLayout.GRAVITY_FILL - tabMode = TabLayout.MODE_FIXED - } - } - - override fun cleanupTabs(tabs: TabLayout) { - trackingIconSubscription?.unsubscribe() - setTrackingIconInternal(false) - } - - fun setTrackingIcon(visible: Boolean) { - trackingIconRelay.call(visible) - } - - private fun setTrackingIconInternal(visible: Boolean) { - val tab = activity?.tabs?.getTabAt(TRACK_CONTROLLER) ?: return - val drawable = if (visible) - VectorDrawableCompat.create(resources!!, R.drawable.ic_done_white_18dp, null) - else null - - val view = tabField.get(tab) as LinearLayout - val textView = view.getChildAt(1) as TextView - textView.setCompoundDrawablesWithIntrinsicBounds(null, null, drawable, null) - textView.compoundDrawablePadding = if (visible) 4 else 0 - } - - private inner class MangaDetailAdapter : RouterPagerAdapter(this@MangaController) { - - private val tabCount = if (Injekt.get().hasLoggedServices()) 3 else 2 - - private val tabTitles = listOf( - R.string.manga_detail_tab, - R.string.manga_chapters_tab, - R.string.manga_tracking_tab) - .map { resources!!.getString(it) } - - override fun getCount(): Int { - return tabCount - } - - override fun configureRouter(router: Router, position: Int) { - if (!router.hasRootController()) { - val controller = when (position) { - INFO_CONTROLLER -> MangaInfoController() - CHAPTERS_CONTROLLER -> ChaptersController() - TRACK_CONTROLLER -> TrackController() - else -> error("Wrong position $position") - } - router.setRoot(RouterTransaction.with(controller)) - } - } - - override fun getPageTitle(position: Int): CharSequence { - return tabTitles[position] - } - - } - - companion object { - // EXH --> - const val UPDATE_EXTRA = "update" - const val SMART_SEARCH_CONFIG_EXTRA = "smartSearchConfig" - // EXH <-- - const val FROM_CATALOGUE_EXTRA = "from_catalogue" - const val MANGA_EXTRA = "manga" - - const val INFO_CONTROLLER = 0 - const val CHAPTERS_CONTROLLER = 1 - const val TRACK_CONTROLLER = 2 - - private val tabField = TabLayout.Tab::class.java.getDeclaredField("view") - .apply { isAccessible = true } - } - -} +package eu.kanade.tachiyomi.ui.manga + +import android.Manifest.permission.WRITE_EXTERNAL_STORAGE +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.LinearLayout +import android.widget.TextView +import androidx.vectordrawable.graphics.drawable.VectorDrawableCompat +import com.bluelinelabs.conductor.ControllerChangeHandler +import com.bluelinelabs.conductor.ControllerChangeType +import com.bluelinelabs.conductor.Router +import com.bluelinelabs.conductor.RouterTransaction +import com.bluelinelabs.conductor.support.RouterPagerAdapter +import com.google.android.material.tabs.TabLayout +import com.jakewharton.rxrelay.BehaviorRelay +import com.jakewharton.rxrelay.PublishRelay +import eu.kanade.tachiyomi.R +import eu.kanade.tachiyomi.data.database.DatabaseHelper +import eu.kanade.tachiyomi.data.database.models.Manga +import eu.kanade.tachiyomi.data.track.TrackManager +import eu.kanade.tachiyomi.source.Source +import eu.kanade.tachiyomi.source.SourceManager +import eu.kanade.tachiyomi.ui.base.controller.RxController +import eu.kanade.tachiyomi.ui.base.controller.TabbedController +import eu.kanade.tachiyomi.ui.base.controller.requestPermissionsSafe +import eu.kanade.tachiyomi.ui.catalogue.CatalogueController +import eu.kanade.tachiyomi.ui.manga.chapter.ChaptersController +import eu.kanade.tachiyomi.ui.manga.chapter.ChaptersPresenter +import eu.kanade.tachiyomi.ui.manga.info.MangaInfoController +import eu.kanade.tachiyomi.ui.manga.track.TrackController +import eu.kanade.tachiyomi.util.toast +import kotlinx.android.synthetic.main.main_activity.* +import kotlinx.android.synthetic.main.manga_controller.* +import rx.Subscription +import uy.kohesive.injekt.Injekt +import uy.kohesive.injekt.api.get +import java.util.* + +class MangaController : RxController, TabbedController { + + constructor(manga: Manga?, + fromCatalogue: Boolean = false, + smartSearchConfig: CatalogueController.SmartSearchConfig? = null, + update: Boolean = false) : super(Bundle().apply { + putLong(MANGA_EXTRA, manga?.id ?: 0) + putBoolean(FROM_CATALOGUE_EXTRA, fromCatalogue) + putParcelable(SMART_SEARCH_CONFIG_EXTRA, smartSearchConfig) + putBoolean(UPDATE_EXTRA, update) + }) { + this.manga = manga + if (manga != null) { + source = Injekt.get().getOrStub(manga.source) + } + } + + // EXH --> + constructor(redirect: ChaptersPresenter.EXHRedirect) : super(Bundle().apply { + putLong(MANGA_EXTRA, redirect.manga.id!!) + putBoolean(UPDATE_EXTRA, redirect.update) + }) { + this.manga = redirect.manga + if (manga != null) { + source = Injekt.get().getOrStub(redirect.manga.source) + } + } + // EXH <-- + + constructor(mangaId: Long) : this( + Injekt.get().getManga(mangaId).executeAsBlocking()) + + @Suppress("unused") + constructor(bundle: Bundle) : this(bundle.getLong(MANGA_EXTRA)) + + var manga: Manga? = null + private set + + var source: Source? = null + private set + + private var adapter: MangaDetailAdapter? = null + + val fromCatalogue = args.getBoolean(FROM_CATALOGUE_EXTRA, false) + + var update = args.getBoolean(UPDATE_EXTRA, false) + + // EXH --> + val smartSearchConfig: CatalogueController.SmartSearchConfig? = args.getParcelable(SMART_SEARCH_CONFIG_EXTRA) + // EXH <-- + + val lastUpdateRelay: BehaviorRelay = BehaviorRelay.create() + + val chapterCountRelay: BehaviorRelay = BehaviorRelay.create() + + val mangaFavoriteRelay: PublishRelay = PublishRelay.create() + + private val trackingIconRelay: BehaviorRelay = BehaviorRelay.create() + + private var trackingIconSubscription: Subscription? = null + + override fun getTitle(): String? { + return manga?.title + } + + override fun inflateView(inflater: LayoutInflater, container: ViewGroup): View { + return inflater.inflate(R.layout.manga_controller, container, false) + } + + override fun onViewCreated(view: View) { + super.onViewCreated(view) + + if (manga == null || source == null) return + + requestPermissionsSafe(arrayOf(WRITE_EXTERNAL_STORAGE), 301) + + adapter = MangaDetailAdapter() + manga_pager.offscreenPageLimit = 3 + manga_pager.adapter = adapter + + if (!fromCatalogue) + manga_pager.currentItem = CHAPTERS_CONTROLLER + } + + override fun onDestroyView(view: View) { + super.onDestroyView(view) + adapter = null + } + + override fun onChangeStarted(handler: ControllerChangeHandler, type: ControllerChangeType) { + super.onChangeStarted(handler, type) + if (type.isEnter) { + activity?.tabs?.setupWithViewPager(manga_pager) + trackingIconSubscription = trackingIconRelay.subscribe { setTrackingIconInternal(it) } + } + } + + override fun onChangeEnded(handler: ControllerChangeHandler, type: ControllerChangeType) { + super.onChangeEnded(handler, type) + if (manga == null || source == null) { + activity?.toast(R.string.manga_not_in_db) + router.popController(this) + } + } + + override fun configureTabs(tabs: TabLayout) { + with(tabs) { + tabGravity = TabLayout.GRAVITY_FILL + tabMode = TabLayout.MODE_FIXED + } + } + + override fun cleanupTabs(tabs: TabLayout) { + trackingIconSubscription?.unsubscribe() + setTrackingIconInternal(false) + } + + fun setTrackingIcon(visible: Boolean) { + trackingIconRelay.call(visible) + } + + private fun setTrackingIconInternal(visible: Boolean) { + val tab = activity?.tabs?.getTabAt(TRACK_CONTROLLER) ?: return + val drawable = if (visible) + VectorDrawableCompat.create(resources!!, R.drawable.ic_done_white_18dp, null) + else null + + val view = tabField.get(tab) as LinearLayout + val textView = view.getChildAt(1) as TextView + textView.setCompoundDrawablesWithIntrinsicBounds(null, null, drawable, null) + textView.compoundDrawablePadding = if (visible) 4 else 0 + } + + private inner class MangaDetailAdapter : RouterPagerAdapter(this@MangaController) { + + private val tabCount = if (Injekt.get().hasLoggedServices()) 3 else 2 + + private val tabTitles = listOf( + R.string.manga_detail_tab, + R.string.manga_chapters_tab, + R.string.manga_tracking_tab) + .map { resources!!.getString(it) } + + override fun getCount(): Int { + return tabCount + } + + override fun configureRouter(router: Router, position: Int) { + if (!router.hasRootController()) { + val controller = when (position) { + INFO_CONTROLLER -> MangaInfoController() + CHAPTERS_CONTROLLER -> ChaptersController() + TRACK_CONTROLLER -> TrackController() + else -> error("Wrong position $position") + } + router.setRoot(RouterTransaction.with(controller)) + } + } + + override fun getPageTitle(position: Int): CharSequence { + return tabTitles[position] + } + + } + + companion object { + // EXH --> + const val UPDATE_EXTRA = "update" + const val SMART_SEARCH_CONFIG_EXTRA = "smartSearchConfig" + // EXH <-- + const val FROM_CATALOGUE_EXTRA = "from_catalogue" + const val MANGA_EXTRA = "manga" + + const val INFO_CONTROLLER = 0 + const val CHAPTERS_CONTROLLER = 1 + const val TRACK_CONTROLLER = 2 + + private val tabField = TabLayout.Tab::class.java.getDeclaredField("view") + .apply { isAccessible = true } + } + +} diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/chapter/ChapterItem.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/chapter/ChapterItem.kt index 390876689..f68536e30 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/chapter/ChapterItem.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/chapter/ChapterItem.kt @@ -1,55 +1,54 @@ -package eu.kanade.tachiyomi.ui.manga.chapter - -import android.support.v7.widget.RecyclerView -import android.view.View -import eu.davidea.flexibleadapter.FlexibleAdapter -import eu.davidea.flexibleadapter.items.AbstractFlexibleItem -import eu.davidea.flexibleadapter.items.IFlexible -import eu.kanade.tachiyomi.R -import eu.kanade.tachiyomi.data.database.models.Chapter -import eu.kanade.tachiyomi.data.database.models.Manga -import eu.kanade.tachiyomi.data.download.model.Download - -class ChapterItem(val chapter: Chapter, val manga: Manga) : AbstractFlexibleItem(), - Chapter by chapter { - - private var _status: Int = 0 - - var status: Int - get() = download?.status ?: _status - set(value) { _status = value } - - @Transient var download: Download? = null - - val isDownloaded: Boolean - get() = status == Download.DOWNLOADED - - override fun getLayoutRes(): Int { - return R.layout.chapters_item - } - - override fun createViewHolder(view: View, adapter: FlexibleAdapter>): ChapterHolder { - return ChapterHolder(view, adapter as ChaptersAdapter) - } - - override fun bindViewHolder(adapter: FlexibleAdapter>, - holder: ChapterHolder, - position: Int, - payloads: List?) { - - holder.bind(this, manga) - } - - override fun equals(other: Any?): Boolean { - if (this === other) return true - if (other is ChapterItem) { - return chapter.id!! == other.chapter.id!! - } - return false - } - - override fun hashCode(): Int { - return chapter.id!!.hashCode() - } - +package eu.kanade.tachiyomi.ui.manga.chapter + +import android.view.View +import eu.davidea.flexibleadapter.FlexibleAdapter +import eu.davidea.flexibleadapter.items.AbstractFlexibleItem +import eu.davidea.flexibleadapter.items.IFlexible +import eu.kanade.tachiyomi.R +import eu.kanade.tachiyomi.data.database.models.Chapter +import eu.kanade.tachiyomi.data.database.models.Manga +import eu.kanade.tachiyomi.data.download.model.Download + +class ChapterItem(val chapter: Chapter, val manga: Manga) : AbstractFlexibleItem(), + Chapter by chapter { + + private var _status: Int = 0 + + var status: Int + get() = download?.status ?: _status + set(value) { _status = value } + + @Transient var download: Download? = null + + val isDownloaded: Boolean + get() = status == Download.DOWNLOADED + + override fun getLayoutRes(): Int { + return R.layout.chapters_item + } + + override fun createViewHolder(view: View, adapter: FlexibleAdapter>): ChapterHolder { + return ChapterHolder(view, adapter as ChaptersAdapter) + } + + override fun bindViewHolder(adapter: FlexibleAdapter>, + holder: ChapterHolder, + position: Int, + payloads: List?) { + + holder.bind(this, manga) + } + + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (other is ChapterItem) { + return chapter.id!! == other.chapter.id!! + } + return false + } + + override fun hashCode(): Int { + return chapter.id!!.hashCode() + } + } \ No newline at end of file diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/chapter/ChaptersController.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/chapter/ChaptersController.kt index e65eb9910..6a0ea67a5 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/chapter/ChaptersController.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/chapter/ChaptersController.kt @@ -1,539 +1,537 @@ -package eu.kanade.tachiyomi.ui.manga.chapter - -import android.animation.Animator -import android.animation.AnimatorListenerAdapter -import android.annotation.SuppressLint -import android.app.Activity -import android.content.Intent -import android.support.design.widget.Snackbar -import android.support.v7.app.AppCompatActivity -import android.support.v7.view.ActionMode -import android.support.v7.widget.DividerItemDecoration -import android.support.v7.widget.LinearLayoutManager -import android.view.* -import com.bluelinelabs.conductor.RouterTransaction -import com.elvishew.xlog.XLog -import com.jakewharton.rxbinding.support.v4.widget.refreshes -import com.jakewharton.rxbinding.view.clicks -import eu.davidea.flexibleadapter.FlexibleAdapter -import eu.davidea.flexibleadapter.SelectableAdapter -import eu.kanade.tachiyomi.R -import eu.kanade.tachiyomi.data.database.models.Chapter -import eu.kanade.tachiyomi.data.database.models.Manga -import eu.kanade.tachiyomi.data.download.model.Download -import eu.kanade.tachiyomi.ui.base.controller.NucleusController -import eu.kanade.tachiyomi.ui.base.controller.popControllerWithTag -import eu.kanade.tachiyomi.ui.manga.MangaController -import eu.kanade.tachiyomi.ui.reader.ReaderActivity -import eu.kanade.tachiyomi.util.getCoordinates -import eu.kanade.tachiyomi.util.snack -import eu.kanade.tachiyomi.util.toast -import exh.EH_SOURCE_ID -import exh.EXH_SOURCE_ID -import kotlinx.android.synthetic.main.chapters_controller.* -import rx.android.schedulers.AndroidSchedulers -import timber.log.Timber - -class ChaptersController : NucleusController(), - ActionMode.Callback, - FlexibleAdapter.OnItemClickListener, - FlexibleAdapter.OnItemLongClickListener, - ChaptersAdapter.OnMenuItemClickListener, - SetDisplayModeDialog.Listener, - SetSortingDialog.Listener, - DownloadChaptersDialog.Listener, - DownloadCustomChaptersDialog.Listener, - DeleteChaptersDialog.Listener { - - /** - * Adapter containing a list of chapters. - */ - private var adapter: ChaptersAdapter? = null - - /** - * Action mode for multiple selection. - */ - private var actionMode: ActionMode? = null - - /** - * Selected items. Used to restore selections after a rotation. - */ - private val selectedItems = mutableSetOf() - - private var lastClickPosition = -1 - - init { - setHasOptionsMenu(true) - setOptionsMenuHidden(true) - } - - override fun createPresenter(): ChaptersPresenter { - val ctrl = parentController as MangaController - return ChaptersPresenter(ctrl.manga!!, ctrl.source!!, - ctrl.chapterCountRelay, ctrl.lastUpdateRelay, ctrl.mangaFavoriteRelay) - } - - override fun inflateView(inflater: LayoutInflater, container: ViewGroup): View { - return inflater.inflate(R.layout.chapters_controller, container, false) - } - - override fun onViewCreated(view: View) { - super.onViewCreated(view) - - // Init RecyclerView and adapter - adapter = ChaptersAdapter(this, view.context) - - recycler.adapter = adapter - recycler.layoutManager = LinearLayoutManager(view.context) - recycler.addItemDecoration(DividerItemDecoration(view.context, DividerItemDecoration.VERTICAL)) - recycler.setHasFixedSize(true) - adapter?.fastScroller = fast_scroller - - swipe_refresh.refreshes().subscribeUntilDestroy { fetchChaptersFromSource() } - - fab.clicks().subscribeUntilDestroy { - val item = presenter.getNextUnreadChapter() - if (item != null) { - // Create animation listener - val revealAnimationListener: Animator.AnimatorListener = object : AnimatorListenerAdapter() { - override fun onAnimationStart(animation: Animator?) { - openChapter(item.chapter, true) - } - } - - // Get coordinates and start animation - val coordinates = fab.getCoordinates() - if (!reveal_view.showRevealEffect(coordinates.x, coordinates.y, revealAnimationListener)) { - openChapter(item.chapter) - } - } else { - view.context.toast(R.string.no_next_chapter) - } - } - - presenter.redirectUserRelay - .observeOn(AndroidSchedulers.mainThread()) - .subscribeUntilDestroy { redirect -> - XLog.d("Redirecting to updated manga (manga.id: %s, manga.title: %s, update: %s)!", redirect.manga.id, redirect.manga.title, redirect.update) - // Replace self - parentController?.router?.replaceTopController(RouterTransaction.with(MangaController(redirect))) - } - } - - override fun onDestroyView(view: View) { - adapter = null - actionMode = null - super.onDestroyView(view) - } - - override fun onActivityResumed(activity: Activity) { - if (view == null) return - - // Check if animation view is visible - if (reveal_view.visibility == View.VISIBLE) { - // Show the unReveal effect - val coordinates = fab.getCoordinates() - reveal_view.hideRevealEffect(coordinates.x, coordinates.y, 1920) - } - super.onActivityResumed(activity) - } - - override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) { - inflater.inflate(R.menu.chapters, menu) - } - - override fun onPrepareOptionsMenu(menu: Menu) { - // Initialize menu items. - val menuFilterRead = menu.findItem(R.id.action_filter_read) ?: return - val menuFilterUnread = menu.findItem(R.id.action_filter_unread) - val menuFilterDownloaded = menu.findItem(R.id.action_filter_downloaded) - val menuFilterBookmarked = menu.findItem(R.id.action_filter_bookmarked) - - // Set correct checkbox values. - menuFilterRead.isChecked = presenter.onlyRead() - menuFilterUnread.isChecked = presenter.onlyUnread() - menuFilterDownloaded.isChecked = presenter.onlyDownloaded() - menuFilterBookmarked.isChecked = presenter.onlyBookmarked() - - if (presenter.onlyRead()) - //Disable unread filter option if read filter is enabled. - menuFilterUnread.isEnabled = false - if (presenter.onlyUnread()) - //Disable read filter option if unread filter is enabled. - menuFilterRead.isEnabled = false - } - - override fun onOptionsItemSelected(item: MenuItem): Boolean { - when (item.itemId) { - R.id.action_display_mode -> showDisplayModeDialog() - R.id.manga_download -> showDownloadDialog() - R.id.action_sorting_mode -> showSortingDialog() - R.id.action_filter_unread -> { - item.isChecked = !item.isChecked - presenter.setUnreadFilter(item.isChecked) - activity?.invalidateOptionsMenu() - } - R.id.action_filter_read -> { - item.isChecked = !item.isChecked - presenter.setReadFilter(item.isChecked) - activity?.invalidateOptionsMenu() - } - R.id.action_filter_downloaded -> { - item.isChecked = !item.isChecked - presenter.setDownloadedFilter(item.isChecked) - } - R.id.action_filter_bookmarked -> { - item.isChecked = !item.isChecked - presenter.setBookmarkedFilter(item.isChecked) - } - R.id.action_filter_empty -> { - presenter.removeFilters() - activity?.invalidateOptionsMenu() - } - R.id.action_sort -> presenter.revertSortOrder() - else -> return super.onOptionsItemSelected(item) - } - return true - } - - fun onNextChapters(chapters: List) { - // If the list is empty, fetch chapters from source if the conditions are met - // We use presenter chapters instead because they are always unfiltered - if (presenter.chapters.isEmpty()) - initialFetchChapters() - - val mangaController = parentController as MangaController - if (mangaController.update - // Auto-update old format galleries - || ((presenter.manga.source == EH_SOURCE_ID || presenter.manga.source == EXH_SOURCE_ID) - && chapters.size == 1 && chapters.first().date_upload == 0L)) { - mangaController.update = false - fetchChaptersFromSource() - } - - val adapter = adapter ?: return - adapter.updateDataSet(chapters) - - if (selectedItems.isNotEmpty()) { - adapter.clearSelection() // we need to start from a clean state, index may have changed - createActionModeIfNeeded() - selectedItems.forEach { item -> - val position = adapter.indexOf(item) - if (position != -1 && !adapter.isSelected(position)) { - adapter.toggleSelection(position) - } - } - actionMode?.invalidate() - } - - } - - private fun initialFetchChapters() { - // Only fetch if this view is from the catalog and it hasn't requested previously - if ((parentController as MangaController).fromCatalogue && !presenter.hasRequested) { - fetchChaptersFromSource() - } - } - - private fun fetchChaptersFromSource() { - swipe_refresh?.isRefreshing = true - presenter.fetchChaptersFromSource() - } - - fun onFetchChaptersDone() { - swipe_refresh?.isRefreshing = false - } - - fun onFetchChaptersError(error: Throwable) { - swipe_refresh?.isRefreshing = false - activity?.toast(error.message) - // [EXH] - XLog.w("> Failed to fetch chapters!", error) - XLog.w("> (source.id: %s, source.name: %s, manga.id: %s, manga.url: %s)", - presenter.source.id, - presenter.source.name, - presenter.manga.id, - presenter.manga.url) - } - - fun onChapterStatusChange(download: Download) { - getHolder(download.chapter)?.notifyStatus(download.status) - } - - private fun getHolder(chapter: Chapter): ChapterHolder? { - return recycler?.findViewHolderForItemId(chapter.id!!) as? ChapterHolder - } - - fun openChapter(chapter: Chapter, hasAnimation: Boolean = false) { - val activity = activity ?: return - val intent = ReaderActivity.newIntent(activity, presenter.manga, chapter) - if (hasAnimation) { - intent.addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION) - } - startActivity(intent) - } - - override fun onItemClick(view: View, position: Int): Boolean { - val adapter = adapter ?: return false - val item = adapter.getItem(position) ?: return false - if (actionMode != null && adapter.mode == SelectableAdapter.Mode.MULTI) { - lastClickPosition = position - toggleSelection(position) - return true - } else { - openChapter(item.chapter) - return false - } - } - - override fun onItemLongClick(position: Int) { - createActionModeIfNeeded() - when { - lastClickPosition == -1 -> setSelection(position) - lastClickPosition > position -> for (i in position until lastClickPosition) - setSelection(i) - lastClickPosition < position -> for (i in lastClickPosition + 1..position) - setSelection(i) - else -> setSelection(position) - } - lastClickPosition = position - adapter?.notifyDataSetChanged() - } - - // SELECTIONS & ACTION MODE - - private fun toggleSelection(position: Int) { - val adapter = adapter ?: return - val item = adapter.getItem(position) ?: return - adapter.toggleSelection(position) - adapter.notifyDataSetChanged() - if (adapter.isSelected(position)) { - selectedItems.add(item) - } else { - selectedItems.remove(item) - } - actionMode?.invalidate() - } - - private fun setSelection(position: Int) { - val adapter = adapter ?: return - val item = adapter.getItem(position) ?: return - if (!adapter.isSelected(position)) { - adapter.toggleSelection(position) - selectedItems.add(item) - actionMode?.invalidate() - } - } - - private fun getSelectedChapters(): List { - val adapter = adapter ?: return emptyList() - return adapter.selectedPositions.mapNotNull { adapter.getItem(it) } - } - - private fun createActionModeIfNeeded() { - if (actionMode == null) { - actionMode = (activity as? AppCompatActivity)?.startSupportActionMode(this) - } - } - - private fun destroyActionModeIfNeeded() { - lastClickPosition = -1 - actionMode?.finish() - } - - override fun onCreateActionMode(mode: ActionMode, menu: Menu): Boolean { - mode.menuInflater.inflate(R.menu.chapter_selection, menu) - adapter?.mode = SelectableAdapter.Mode.MULTI - return true - } - - @SuppressLint("StringFormatInvalid") - override fun onPrepareActionMode(mode: ActionMode, menu: Menu): Boolean { - val count = adapter?.selectedItemCount ?: 0 - if (count == 0) { - // Destroy action mode if there are no items selected. - destroyActionModeIfNeeded() - } else { - mode.title = resources?.getString(R.string.label_selected, count) - } - return false - } - - override fun onActionItemClicked(mode: ActionMode, item: MenuItem): Boolean { - when (item.itemId) { - R.id.action_select_all -> selectAll() - R.id.action_mark_as_read -> markAsRead(getSelectedChapters()) - R.id.action_mark_as_unread -> markAsUnread(getSelectedChapters()) - R.id.action_download -> downloadChapters(getSelectedChapters()) - R.id.action_delete -> showDeleteChaptersConfirmationDialog() - else -> return false - } - return true - } - - override fun onDestroyActionMode(mode: ActionMode) { - adapter?.mode = SelectableAdapter.Mode.SINGLE - adapter?.clearSelection() - selectedItems.clear() - actionMode = null - } - - override fun onMenuItemClick(position: Int, item: MenuItem) { - val chapter = adapter?.getItem(position) ?: return - val chapters = listOf(chapter) - - when (item.itemId) { - R.id.action_download -> downloadChapters(chapters) - R.id.action_bookmark -> bookmarkChapters(chapters, true) - R.id.action_remove_bookmark -> bookmarkChapters(chapters, false) - R.id.action_delete -> deleteChapters(chapters) - R.id.action_mark_as_read -> markAsRead(chapters) - R.id.action_mark_as_unread -> markAsUnread(chapters) - R.id.action_mark_previous_as_read -> markPreviousAsRead(chapter) - } - } - - // SELECTION MODE ACTIONS - - private fun selectAll() { - val adapter = adapter ?: return - adapter.selectAll() - selectedItems.addAll(adapter.items) - actionMode?.invalidate() - } - - private fun markAsRead(chapters: List) { - presenter.markChaptersRead(chapters, true) - if (presenter.preferences.removeAfterMarkedAsRead()) { - deleteChapters(chapters) - } - } - - private fun markAsUnread(chapters: List) { - presenter.markChaptersRead(chapters, false) - } - - private fun downloadChapters(chapters: List) { - val view = view - destroyActionModeIfNeeded() - presenter.downloadChapters(chapters) - if (view != null && !presenter.manga.favorite) { - recycler?.snack(view.context.getString(R.string.snack_add_to_library), Snackbar.LENGTH_INDEFINITE) { - setAction(R.string.action_add) { - presenter.addToLibrary() - } - } - } - } - - - private fun showDeleteChaptersConfirmationDialog() { - DeleteChaptersDialog(this).showDialog(router) - } - - override fun deleteChapters() { - deleteChapters(getSelectedChapters()) - } - - private fun markPreviousAsRead(chapter: ChapterItem) { - val adapter = adapter ?: return - val chapters = if (presenter.sortDescending()) adapter.items.reversed() else adapter.items - val chapterPos = chapters.indexOf(chapter) - if (chapterPos != -1) { - markAsRead(chapters.take(chapterPos)) - } - } - - private fun bookmarkChapters(chapters: List, bookmarked: Boolean) { - destroyActionModeIfNeeded() - presenter.bookmarkChapters(chapters, bookmarked) - } - - fun deleteChapters(chapters: List) { - destroyActionModeIfNeeded() - if (chapters.isEmpty()) return - - DeletingChaptersDialog().showDialog(router) - presenter.deleteChapters(chapters) - } - - fun onChaptersDeleted() { - dismissDeletingDialog() - adapter?.notifyDataSetChanged() - } - - fun onChaptersDeletedError(error: Throwable) { - dismissDeletingDialog() - Timber.e(error) - } - - private fun dismissDeletingDialog() { - router.popControllerWithTag(DeletingChaptersDialog.TAG) - } - - // OVERFLOW MENU DIALOGS - - private fun showDisplayModeDialog() { - val preselected = if (presenter.manga.displayMode == Manga.DISPLAY_NAME) 0 else 1 - SetDisplayModeDialog(this, preselected).showDialog(router) - } - - override fun setDisplayMode(id: Int) { - presenter.setDisplayMode(id) - adapter?.notifyDataSetChanged() - } - - private fun showSortingDialog() { - val preselected = if (presenter.manga.sorting == Manga.SORTING_SOURCE) 0 else 1 - SetSortingDialog(this, preselected).showDialog(router) - } - - override fun setSorting(id: Int) { - presenter.setSorting(id) - } - - private fun showDownloadDialog() { - DownloadChaptersDialog(this).showDialog(router) - } - - private fun getUnreadChaptersSorted() = presenter.chapters - .filter { !it.read && it.status == Download.NOT_DOWNLOADED } - .distinctBy { it.name } - .sortedByDescending { it.source_order } - - override fun downloadCustomChapters(amount: Int) { - val chaptersToDownload = getUnreadChaptersSorted().take(amount) - if (chaptersToDownload.isNotEmpty()) { - downloadChapters(chaptersToDownload) - } - } - - private fun showCustomDownloadDialog() { - DownloadCustomChaptersDialog(this, presenter.chapters.size).showDialog(router) - } - - - override fun downloadChapters(choice: Int) { - // i = 0: Download 1 - // i = 1: Download 5 - // i = 2: Download 10 - // i = 3: Download x - // i = 4: Download unread - // i = 5: Download all - val chaptersToDownload = when (choice) { - 0 -> getUnreadChaptersSorted().take(1) - 1 -> getUnreadChaptersSorted().take(5) - 2 -> getUnreadChaptersSorted().take(10) - 3 -> { - showCustomDownloadDialog() - return - } - 4 -> presenter.chapters.filter { !it.read } - 5 -> presenter.chapters - else -> emptyList() - } - if (chaptersToDownload.isNotEmpty()) { - downloadChapters(chaptersToDownload) - } - } -} +package eu.kanade.tachiyomi.ui.manga.chapter + +import android.animation.Animator +import android.animation.AnimatorListenerAdapter +import android.annotation.SuppressLint +import android.app.Activity +import android.content.Intent +import android.view.* +import androidx.appcompat.app.AppCompatActivity +import androidx.appcompat.view.ActionMode +import com.bluelinelabs.conductor.RouterTransaction +import com.elvishew.xlog.XLog +import com.google.android.material.snackbar.Snackbar +import com.jakewharton.rxbinding.support.v4.widget.refreshes +import com.jakewharton.rxbinding.view.clicks +import eu.davidea.flexibleadapter.FlexibleAdapter +import eu.davidea.flexibleadapter.SelectableAdapter +import eu.kanade.tachiyomi.R +import eu.kanade.tachiyomi.data.database.models.Chapter +import eu.kanade.tachiyomi.data.database.models.Manga +import eu.kanade.tachiyomi.data.download.model.Download +import eu.kanade.tachiyomi.ui.base.controller.NucleusController +import eu.kanade.tachiyomi.ui.base.controller.popControllerWithTag +import eu.kanade.tachiyomi.ui.manga.MangaController +import eu.kanade.tachiyomi.ui.reader.ReaderActivity +import eu.kanade.tachiyomi.util.getCoordinates +import eu.kanade.tachiyomi.util.snack +import eu.kanade.tachiyomi.util.toast +import exh.EH_SOURCE_ID +import exh.EXH_SOURCE_ID +import kotlinx.android.synthetic.main.chapters_controller.* +import rx.android.schedulers.AndroidSchedulers +import timber.log.Timber + +class ChaptersController : NucleusController(), + ActionMode.Callback, + FlexibleAdapter.OnItemClickListener, + FlexibleAdapter.OnItemLongClickListener, + ChaptersAdapter.OnMenuItemClickListener, + SetDisplayModeDialog.Listener, + SetSortingDialog.Listener, + DownloadChaptersDialog.Listener, + DownloadCustomChaptersDialog.Listener, + DeleteChaptersDialog.Listener { + + /** + * Adapter containing a list of chapters. + */ + private var adapter: ChaptersAdapter? = null + + /** + * Action mode for multiple selection. + */ + private var actionMode: ActionMode? = null + + /** + * Selected items. Used to restore selections after a rotation. + */ + private val selectedItems = mutableSetOf() + + private var lastClickPosition = -1 + + init { + setHasOptionsMenu(true) + setOptionsMenuHidden(true) + } + + override fun createPresenter(): ChaptersPresenter { + val ctrl = parentController as MangaController + return ChaptersPresenter(ctrl.manga!!, ctrl.source!!, + ctrl.chapterCountRelay, ctrl.lastUpdateRelay, ctrl.mangaFavoriteRelay) + } + + override fun inflateView(inflater: LayoutInflater, container: ViewGroup): View { + return inflater.inflate(R.layout.chapters_controller, container, false) + } + + override fun onViewCreated(view: View) { + super.onViewCreated(view) + + // Init RecyclerView and adapter + adapter = ChaptersAdapter(this, view.context) + + recycler.adapter = adapter + recycler.layoutManager = androidx.recyclerview.widget.LinearLayoutManager(view.context) + recycler.addItemDecoration(androidx.recyclerview.widget.DividerItemDecoration(view.context, androidx.recyclerview.widget.DividerItemDecoration.VERTICAL)) + recycler.setHasFixedSize(true) + adapter?.fastScroller = fast_scroller + + swipe_refresh.refreshes().subscribeUntilDestroy { fetchChaptersFromSource() } + + fab.clicks().subscribeUntilDestroy { + val item = presenter.getNextUnreadChapter() + if (item != null) { + // Create animation listener + val revealAnimationListener: Animator.AnimatorListener = object : AnimatorListenerAdapter() { + override fun onAnimationStart(animation: Animator?) { + openChapter(item.chapter, true) + } + } + + // Get coordinates and start animation + val coordinates = fab.getCoordinates() + if (!reveal_view.showRevealEffect(coordinates.x, coordinates.y, revealAnimationListener)) { + openChapter(item.chapter) + } + } else { + view.context.toast(R.string.no_next_chapter) + } + } + + presenter.redirectUserRelay + .observeOn(AndroidSchedulers.mainThread()) + .subscribeUntilDestroy { redirect -> + XLog.d("Redirecting to updated manga (manga.id: %s, manga.title: %s, update: %s)!", redirect.manga.id, redirect.manga.title, redirect.update) + // Replace self + parentController?.router?.replaceTopController(RouterTransaction.with(MangaController(redirect))) + } + } + + override fun onDestroyView(view: View) { + adapter = null + actionMode = null + super.onDestroyView(view) + } + + override fun onActivityResumed(activity: Activity) { + if (view == null) return + + // Check if animation view is visible + if (reveal_view.visibility == View.VISIBLE) { + // Show the unReveal effect + val coordinates = fab.getCoordinates() + reveal_view.hideRevealEffect(coordinates.x, coordinates.y, 1920) + } + super.onActivityResumed(activity) + } + + override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) { + inflater.inflate(R.menu.chapters, menu) + } + + override fun onPrepareOptionsMenu(menu: Menu) { + // Initialize menu items. + val menuFilterRead = menu.findItem(R.id.action_filter_read) ?: return + val menuFilterUnread = menu.findItem(R.id.action_filter_unread) + val menuFilterDownloaded = menu.findItem(R.id.action_filter_downloaded) + val menuFilterBookmarked = menu.findItem(R.id.action_filter_bookmarked) + + // Set correct checkbox values. + menuFilterRead.isChecked = presenter.onlyRead() + menuFilterUnread.isChecked = presenter.onlyUnread() + menuFilterDownloaded.isChecked = presenter.onlyDownloaded() + menuFilterBookmarked.isChecked = presenter.onlyBookmarked() + + if (presenter.onlyRead()) + //Disable unread filter option if read filter is enabled. + menuFilterUnread.isEnabled = false + if (presenter.onlyUnread()) + //Disable read filter option if unread filter is enabled. + menuFilterRead.isEnabled = false + } + + override fun onOptionsItemSelected(item: MenuItem): Boolean { + when (item.itemId) { + R.id.action_display_mode -> showDisplayModeDialog() + R.id.manga_download -> showDownloadDialog() + R.id.action_sorting_mode -> showSortingDialog() + R.id.action_filter_unread -> { + item.isChecked = !item.isChecked + presenter.setUnreadFilter(item.isChecked) + activity?.invalidateOptionsMenu() + } + R.id.action_filter_read -> { + item.isChecked = !item.isChecked + presenter.setReadFilter(item.isChecked) + activity?.invalidateOptionsMenu() + } + R.id.action_filter_downloaded -> { + item.isChecked = !item.isChecked + presenter.setDownloadedFilter(item.isChecked) + } + R.id.action_filter_bookmarked -> { + item.isChecked = !item.isChecked + presenter.setBookmarkedFilter(item.isChecked) + } + R.id.action_filter_empty -> { + presenter.removeFilters() + activity?.invalidateOptionsMenu() + } + R.id.action_sort -> presenter.revertSortOrder() + else -> return super.onOptionsItemSelected(item) + } + return true + } + + fun onNextChapters(chapters: List) { + // If the list is empty, fetch chapters from source if the conditions are met + // We use presenter chapters instead because they are always unfiltered + if (presenter.chapters.isEmpty()) + initialFetchChapters() + + val mangaController = parentController as MangaController + if (mangaController.update + // Auto-update old format galleries + || ((presenter.manga.source == EH_SOURCE_ID || presenter.manga.source == EXH_SOURCE_ID) + && chapters.size == 1 && chapters.first().date_upload == 0L)) { + mangaController.update = false + fetchChaptersFromSource() + } + + val adapter = adapter ?: return + adapter.updateDataSet(chapters) + + if (selectedItems.isNotEmpty()) { + adapter.clearSelection() // we need to start from a clean state, index may have changed + createActionModeIfNeeded() + selectedItems.forEach { item -> + val position = adapter.indexOf(item) + if (position != -1 && !adapter.isSelected(position)) { + adapter.toggleSelection(position) + } + } + actionMode?.invalidate() + } + + } + + private fun initialFetchChapters() { + // Only fetch if this view is from the catalog and it hasn't requested previously + if ((parentController as MangaController).fromCatalogue && !presenter.hasRequested) { + fetchChaptersFromSource() + } + } + + private fun fetchChaptersFromSource() { + swipe_refresh?.isRefreshing = true + presenter.fetchChaptersFromSource() + } + + fun onFetchChaptersDone() { + swipe_refresh?.isRefreshing = false + } + + fun onFetchChaptersError(error: Throwable) { + swipe_refresh?.isRefreshing = false + activity?.toast(error.message) + // [EXH] + XLog.w("> Failed to fetch chapters!", error) + XLog.w("> (source.id: %s, source.name: %s, manga.id: %s, manga.url: %s)", + presenter.source.id, + presenter.source.name, + presenter.manga.id, + presenter.manga.url) + } + + fun onChapterStatusChange(download: Download) { + getHolder(download.chapter)?.notifyStatus(download.status) + } + + private fun getHolder(chapter: Chapter): ChapterHolder? { + return recycler?.findViewHolderForItemId(chapter.id!!) as? ChapterHolder + } + + fun openChapter(chapter: Chapter, hasAnimation: Boolean = false) { + val activity = activity ?: return + val intent = ReaderActivity.newIntent(activity, presenter.manga, chapter) + if (hasAnimation) { + intent.addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION) + } + startActivity(intent) + } + + override fun onItemClick(view: View, position: Int): Boolean { + val adapter = adapter ?: return false + val item = adapter.getItem(position) ?: return false + if (actionMode != null && adapter.mode == SelectableAdapter.Mode.MULTI) { + lastClickPosition = position + toggleSelection(position) + return true + } else { + openChapter(item.chapter) + return false + } + } + + override fun onItemLongClick(position: Int) { + createActionModeIfNeeded() + when { + lastClickPosition == -1 -> setSelection(position) + lastClickPosition > position -> for (i in position until lastClickPosition) + setSelection(i) + lastClickPosition < position -> for (i in lastClickPosition + 1..position) + setSelection(i) + else -> setSelection(position) + } + lastClickPosition = position + adapter?.notifyDataSetChanged() + } + + // SELECTIONS & ACTION MODE + + private fun toggleSelection(position: Int) { + val adapter = adapter ?: return + val item = adapter.getItem(position) ?: return + adapter.toggleSelection(position) + adapter.notifyDataSetChanged() + if (adapter.isSelected(position)) { + selectedItems.add(item) + } else { + selectedItems.remove(item) + } + actionMode?.invalidate() + } + + private fun setSelection(position: Int) { + val adapter = adapter ?: return + val item = adapter.getItem(position) ?: return + if (!adapter.isSelected(position)) { + adapter.toggleSelection(position) + selectedItems.add(item) + actionMode?.invalidate() + } + } + + private fun getSelectedChapters(): List { + val adapter = adapter ?: return emptyList() + return adapter.selectedPositions.mapNotNull { adapter.getItem(it) } + } + + private fun createActionModeIfNeeded() { + if (actionMode == null) { + actionMode = (activity as? AppCompatActivity)?.startSupportActionMode(this) + } + } + + private fun destroyActionModeIfNeeded() { + lastClickPosition = -1 + actionMode?.finish() + } + + override fun onCreateActionMode(mode: ActionMode, menu: Menu): Boolean { + mode.menuInflater.inflate(R.menu.chapter_selection, menu) + adapter?.mode = SelectableAdapter.Mode.MULTI + return true + } + + @SuppressLint("StringFormatInvalid") + override fun onPrepareActionMode(mode: ActionMode, menu: Menu): Boolean { + val count = adapter?.selectedItemCount ?: 0 + if (count == 0) { + // Destroy action mode if there are no items selected. + destroyActionModeIfNeeded() + } else { + mode.title = resources?.getString(R.string.label_selected, count) + } + return false + } + + override fun onActionItemClicked(mode: ActionMode, item: MenuItem): Boolean { + when (item.itemId) { + R.id.action_select_all -> selectAll() + R.id.action_mark_as_read -> markAsRead(getSelectedChapters()) + R.id.action_mark_as_unread -> markAsUnread(getSelectedChapters()) + R.id.action_download -> downloadChapters(getSelectedChapters()) + R.id.action_delete -> showDeleteChaptersConfirmationDialog() + else -> return false + } + return true + } + + override fun onDestroyActionMode(mode: ActionMode) { + adapter?.mode = SelectableAdapter.Mode.SINGLE + adapter?.clearSelection() + selectedItems.clear() + actionMode = null + } + + override fun onMenuItemClick(position: Int, item: MenuItem) { + val chapter = adapter?.getItem(position) ?: return + val chapters = listOf(chapter) + + when (item.itemId) { + R.id.action_download -> downloadChapters(chapters) + R.id.action_bookmark -> bookmarkChapters(chapters, true) + R.id.action_remove_bookmark -> bookmarkChapters(chapters, false) + R.id.action_delete -> deleteChapters(chapters) + R.id.action_mark_as_read -> markAsRead(chapters) + R.id.action_mark_as_unread -> markAsUnread(chapters) + R.id.action_mark_previous_as_read -> markPreviousAsRead(chapter) + } + } + + // SELECTION MODE ACTIONS + + private fun selectAll() { + val adapter = adapter ?: return + adapter.selectAll() + selectedItems.addAll(adapter.items) + actionMode?.invalidate() + } + + private fun markAsRead(chapters: List) { + presenter.markChaptersRead(chapters, true) + if (presenter.preferences.removeAfterMarkedAsRead()) { + deleteChapters(chapters) + } + } + + private fun markAsUnread(chapters: List) { + presenter.markChaptersRead(chapters, false) + } + + private fun downloadChapters(chapters: List) { + val view = view + destroyActionModeIfNeeded() + presenter.downloadChapters(chapters) + if (view != null && !presenter.manga.favorite) { + recycler?.snack(view.context.getString(R.string.snack_add_to_library), Snackbar.LENGTH_INDEFINITE) { + setAction(R.string.action_add) { + presenter.addToLibrary() + } + } + } + } + + + private fun showDeleteChaptersConfirmationDialog() { + DeleteChaptersDialog(this).showDialog(router) + } + + override fun deleteChapters() { + deleteChapters(getSelectedChapters()) + } + + private fun markPreviousAsRead(chapter: ChapterItem) { + val adapter = adapter ?: return + val chapters = if (presenter.sortDescending()) adapter.items.reversed() else adapter.items + val chapterPos = chapters.indexOf(chapter) + if (chapterPos != -1) { + markAsRead(chapters.take(chapterPos)) + } + } + + private fun bookmarkChapters(chapters: List, bookmarked: Boolean) { + destroyActionModeIfNeeded() + presenter.bookmarkChapters(chapters, bookmarked) + } + + fun deleteChapters(chapters: List) { + destroyActionModeIfNeeded() + if (chapters.isEmpty()) return + + DeletingChaptersDialog().showDialog(router) + presenter.deleteChapters(chapters) + } + + fun onChaptersDeleted() { + dismissDeletingDialog() + adapter?.notifyDataSetChanged() + } + + fun onChaptersDeletedError(error: Throwable) { + dismissDeletingDialog() + Timber.e(error) + } + + private fun dismissDeletingDialog() { + router.popControllerWithTag(DeletingChaptersDialog.TAG) + } + + // OVERFLOW MENU DIALOGS + + private fun showDisplayModeDialog() { + val preselected = if (presenter.manga.displayMode == Manga.DISPLAY_NAME) 0 else 1 + SetDisplayModeDialog(this, preselected).showDialog(router) + } + + override fun setDisplayMode(id: Int) { + presenter.setDisplayMode(id) + adapter?.notifyDataSetChanged() + } + + private fun showSortingDialog() { + val preselected = if (presenter.manga.sorting == Manga.SORTING_SOURCE) 0 else 1 + SetSortingDialog(this, preselected).showDialog(router) + } + + override fun setSorting(id: Int) { + presenter.setSorting(id) + } + + private fun showDownloadDialog() { + DownloadChaptersDialog(this).showDialog(router) + } + + private fun getUnreadChaptersSorted() = presenter.chapters + .filter { !it.read && it.status == Download.NOT_DOWNLOADED } + .distinctBy { it.name } + .sortedByDescending { it.source_order } + + override fun downloadCustomChapters(amount: Int) { + val chaptersToDownload = getUnreadChaptersSorted().take(amount) + if (chaptersToDownload.isNotEmpty()) { + downloadChapters(chaptersToDownload) + } + } + + private fun showCustomDownloadDialog() { + DownloadCustomChaptersDialog(this, presenter.chapters.size).showDialog(router) + } + + + override fun downloadChapters(choice: Int) { + // i = 0: Download 1 + // i = 1: Download 5 + // i = 2: Download 10 + // i = 3: Download x + // i = 4: Download unread + // i = 5: Download all + val chaptersToDownload = when (choice) { + 0 -> getUnreadChaptersSorted().take(1) + 1 -> getUnreadChaptersSorted().take(5) + 2 -> getUnreadChaptersSorted().take(10) + 3 -> { + showCustomDownloadDialog() + return + } + 4 -> presenter.chapters.filter { !it.read } + 5 -> presenter.chapters + else -> emptyList() + } + if (chaptersToDownload.isNotEmpty()) { + downloadChapters(chaptersToDownload) + } + } +} diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/info/MangaInfoController.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/info/MangaInfoController.kt index 7897ac1f1..7b2cecbf8 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/info/MangaInfoController.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/info/MangaInfoController.kt @@ -10,11 +10,11 @@ import android.graphics.Bitmap import android.graphics.drawable.Drawable import android.os.Build import android.os.Bundle -import android.support.v4.content.pm.ShortcutInfoCompat -import android.support.v4.content.pm.ShortcutManagerCompat -import android.support.v4.graphics.drawable.IconCompat import android.view.* import android.widget.Toast +import androidx.core.content.pm.ShortcutInfoCompat +import androidx.core.content.pm.ShortcutManagerCompat +import androidx.core.graphics.drawable.IconCompat import com.afollestad.materialdialogs.MaterialDialog import com.bumptech.glide.load.engine.DiskCacheStrategy import com.bumptech.glide.load.resource.bitmap.RoundedCorners @@ -403,13 +403,13 @@ class MangaInfoController : NucleusController(), try { // --> EH - val urlString = source.mangaDetailsRequest(presenter.manga).url().toString() + val urlString = source.mangaDetailsRequest(presenter.manga).url.toString() if(preferences.eh_incogWebview().getOrDefault()) { activity?.startActivity(Intent(activity, WebViewActivity::class.java).apply { putExtra(WebViewActivity.KEY_URL, urlString) }) } else { - context.openInBrowser(source.mangaDetailsRequest(presenter.manga).url().toString()) + context.openInBrowser(source.mangaDetailsRequest(presenter.manga).url.toString()) } // <-- EH } catch (e: Exception) { @@ -421,7 +421,7 @@ class MangaInfoController : NucleusController(), val source = presenter.source as? HttpSource ?: return val url = try { - source.mangaDetailsRequest(presenter.manga).url().toString() + source.mangaDetailsRequest(presenter.manga).url.toString() } catch (e: Exception) { return } @@ -438,7 +438,7 @@ class MangaInfoController : NucleusController(), val source = presenter.source as? HttpSource ?: return try { - val url = source.mangaDetailsRequest(presenter.manga).url().toString() + val url = source.mangaDetailsRequest(presenter.manga).url.toString() val intent = Intent(Intent.ACTION_SEND).apply { type = "text/plain" putExtra(Intent.EXTRA_TEXT, url) diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/track/TrackAdapter.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/track/TrackAdapter.kt index bb8871097..873a37a7e 100755 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/track/TrackAdapter.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/track/TrackAdapter.kt @@ -1,45 +1,44 @@ -package eu.kanade.tachiyomi.ui.manga.track - -import android.support.v7.widget.RecyclerView -import android.view.ViewGroup -import eu.kanade.tachiyomi.R -import eu.kanade.tachiyomi.util.inflate - -class TrackAdapter(controller: TrackController) : RecyclerView.Adapter() { - - var items = emptyList() - set(value) { - if (field !== value) { - field = value - notifyDataSetChanged() - } - } - - val rowClickListener: OnClickListener = controller - - fun getItem(index: Int): TrackItem? { - return items.getOrNull(index) - } - - override fun getItemCount(): Int { - return items.size - } - - override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): TrackHolder { - val view = parent.inflate(R.layout.track_item) - return TrackHolder(view, this) - } - - override fun onBindViewHolder(holder: TrackHolder, position: Int) { - holder.bind(items[position]) - } - - interface OnClickListener { - fun onLogoClick(position: Int) - fun onTitleClick(position: Int) - fun onStatusClick(position: Int) - fun onChaptersClick(position: Int) - fun onScoreClick(position: Int) - } - -} +package eu.kanade.tachiyomi.ui.manga.track + +import android.view.ViewGroup +import eu.kanade.tachiyomi.R +import eu.kanade.tachiyomi.util.inflate + +class TrackAdapter(controller: TrackController) : androidx.recyclerview.widget.RecyclerView.Adapter() { + + var items = emptyList() + set(value) { + if (field !== value) { + field = value + notifyDataSetChanged() + } + } + + val rowClickListener: OnClickListener = controller + + fun getItem(index: Int): TrackItem? { + return items.getOrNull(index) + } + + override fun getItemCount(): Int { + return items.size + } + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): TrackHolder { + val view = parent.inflate(R.layout.track_item) + return TrackHolder(view, this) + } + + override fun onBindViewHolder(holder: TrackHolder, position: Int) { + holder.bind(items[position]) + } + + interface OnClickListener { + fun onLogoClick(position: Int) + fun onTitleClick(position: Int) + fun onStatusClick(position: Int) + fun onChaptersClick(position: Int) + fun onScoreClick(position: Int) + } + +} diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/track/TrackController.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/track/TrackController.kt index 0acf3381d..19d8bc1d6 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/track/TrackController.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/track/TrackController.kt @@ -1,142 +1,141 @@ -package eu.kanade.tachiyomi.ui.manga.track - -import android.content.Intent -import android.net.Uri -import android.support.v7.widget.LinearLayoutManager -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import com.jakewharton.rxbinding.support.v4.widget.refreshes -import eu.kanade.tachiyomi.R -import eu.kanade.tachiyomi.data.track.model.TrackSearch -import eu.kanade.tachiyomi.ui.base.controller.NucleusController -import eu.kanade.tachiyomi.ui.manga.MangaController -import eu.kanade.tachiyomi.util.toast -import kotlinx.android.synthetic.main.track_controller.* -import timber.log.Timber - -class TrackController : NucleusController(), - TrackAdapter.OnClickListener, - SetTrackStatusDialog.Listener, - SetTrackChaptersDialog.Listener, - SetTrackScoreDialog.Listener { - - private var adapter: TrackAdapter? = null - - init { - // There's no menu, but this avoids a bug when coming from the catalogue, where the menu - // disappears if the searchview is expanded - setHasOptionsMenu(true) - } - - override fun createPresenter(): TrackPresenter { - return TrackPresenter((parentController as MangaController).manga!!) - } - - override fun inflateView(inflater: LayoutInflater, container: ViewGroup): View { - return inflater.inflate(R.layout.track_controller, container, false) - } - - override fun onViewCreated(view: View) { - super.onViewCreated(view) - - adapter = TrackAdapter(this) - with(view) { - track_recycler.layoutManager = LinearLayoutManager(context) - track_recycler.adapter = adapter - swipe_refresh.isEnabled = false - swipe_refresh.refreshes().subscribeUntilDestroy { presenter.refresh() } - } - } - - override fun onDestroyView(view: View) { - adapter = null - super.onDestroyView(view) - } - - fun onNextTrackings(trackings: List) { - val atLeastOneLink = trackings.any { it.track != null } - adapter?.items = trackings - swipe_refresh?.isEnabled = atLeastOneLink - (parentController as? MangaController)?.setTrackingIcon(atLeastOneLink) - } - - fun onSearchResults(results: List) { - getSearchDialog()?.onSearchResults(results) - } - - @Suppress("UNUSED_PARAMETER") - fun onSearchResultsError(error: Throwable) { - Timber.e(error) - getSearchDialog()?.onSearchResultsError() - } - - private fun getSearchDialog(): TrackSearchDialog? { - return router.getControllerWithTag(TAG_SEARCH_CONTROLLER) as? TrackSearchDialog - } - - fun onRefreshDone() { - swipe_refresh?.isRefreshing = false - } - - fun onRefreshError(error: Throwable) { - swipe_refresh?.isRefreshing = false - activity?.toast(error.message) - } - - override fun onLogoClick(position: Int) { - val track = adapter?.getItem(position)?.track ?: return - - if (track.tracking_url.isNullOrBlank()) { - activity?.toast(R.string.url_not_set) - } else { - activity?.startActivity(Intent(Intent.ACTION_VIEW, Uri.parse(track.tracking_url))) - } - } - - override fun onTitleClick(position: Int) { - val item = adapter?.getItem(position) ?: return - TrackSearchDialog(this, item.service).showDialog(router, TAG_SEARCH_CONTROLLER) - } - - override fun onStatusClick(position: Int) { - val item = adapter?.getItem(position) ?: return - if (item.track == null) return - - SetTrackStatusDialog(this, item).showDialog(router) - } - - override fun onChaptersClick(position: Int) { - val item = adapter?.getItem(position) ?: return - if (item.track == null) return - - SetTrackChaptersDialog(this, item).showDialog(router) - } - - override fun onScoreClick(position: Int) { - val item = adapter?.getItem(position) ?: return - if (item.track == null) return - - SetTrackScoreDialog(this, item).showDialog(router) - } - - override fun setStatus(item: TrackItem, selection: Int) { - presenter.setStatus(item, selection) - swipe_refresh?.isRefreshing = true - } - - override fun setScore(item: TrackItem, score: Int) { - presenter.setScore(item, score) - swipe_refresh?.isRefreshing = true - } - - override fun setChaptersRead(item: TrackItem, chaptersRead: Int) { - presenter.setLastChapterRead(item, chaptersRead) - swipe_refresh?.isRefreshing = true - } - - private companion object { - const val TAG_SEARCH_CONTROLLER = "track_search_controller" - } - +package eu.kanade.tachiyomi.ui.manga.track + +import android.content.Intent +import android.net.Uri +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import com.jakewharton.rxbinding.support.v4.widget.refreshes +import eu.kanade.tachiyomi.R +import eu.kanade.tachiyomi.data.track.model.TrackSearch +import eu.kanade.tachiyomi.ui.base.controller.NucleusController +import eu.kanade.tachiyomi.ui.manga.MangaController +import eu.kanade.tachiyomi.util.toast +import kotlinx.android.synthetic.main.track_controller.* +import timber.log.Timber + +class TrackController : NucleusController(), + TrackAdapter.OnClickListener, + SetTrackStatusDialog.Listener, + SetTrackChaptersDialog.Listener, + SetTrackScoreDialog.Listener { + + private var adapter: TrackAdapter? = null + + init { + // There's no menu, but this avoids a bug when coming from the catalogue, where the menu + // disappears if the searchview is expanded + setHasOptionsMenu(true) + } + + override fun createPresenter(): TrackPresenter { + return TrackPresenter((parentController as MangaController).manga!!) + } + + override fun inflateView(inflater: LayoutInflater, container: ViewGroup): View { + return inflater.inflate(R.layout.track_controller, container, false) + } + + override fun onViewCreated(view: View) { + super.onViewCreated(view) + + adapter = TrackAdapter(this) + with(view) { + track_recycler.layoutManager = androidx.recyclerview.widget.LinearLayoutManager(context) + track_recycler.adapter = adapter + swipe_refresh.isEnabled = false + swipe_refresh.refreshes().subscribeUntilDestroy { presenter.refresh() } + } + } + + override fun onDestroyView(view: View) { + adapter = null + super.onDestroyView(view) + } + + fun onNextTrackings(trackings: List) { + val atLeastOneLink = trackings.any { it.track != null } + adapter?.items = trackings + swipe_refresh?.isEnabled = atLeastOneLink + (parentController as? MangaController)?.setTrackingIcon(atLeastOneLink) + } + + fun onSearchResults(results: List) { + getSearchDialog()?.onSearchResults(results) + } + + @Suppress("UNUSED_PARAMETER") + fun onSearchResultsError(error: Throwable) { + Timber.e(error) + getSearchDialog()?.onSearchResultsError() + } + + private fun getSearchDialog(): TrackSearchDialog? { + return router.getControllerWithTag(TAG_SEARCH_CONTROLLER) as? TrackSearchDialog + } + + fun onRefreshDone() { + swipe_refresh?.isRefreshing = false + } + + fun onRefreshError(error: Throwable) { + swipe_refresh?.isRefreshing = false + activity?.toast(error.message) + } + + override fun onLogoClick(position: Int) { + val track = adapter?.getItem(position)?.track ?: return + + if (track.tracking_url.isNullOrBlank()) { + activity?.toast(R.string.url_not_set) + } else { + activity?.startActivity(Intent(Intent.ACTION_VIEW, Uri.parse(track.tracking_url))) + } + } + + override fun onTitleClick(position: Int) { + val item = adapter?.getItem(position) ?: return + TrackSearchDialog(this, item.service).showDialog(router, TAG_SEARCH_CONTROLLER) + } + + override fun onStatusClick(position: Int) { + val item = adapter?.getItem(position) ?: return + if (item.track == null) return + + SetTrackStatusDialog(this, item).showDialog(router) + } + + override fun onChaptersClick(position: Int) { + val item = adapter?.getItem(position) ?: return + if (item.track == null) return + + SetTrackChaptersDialog(this, item).showDialog(router) + } + + override fun onScoreClick(position: Int) { + val item = adapter?.getItem(position) ?: return + if (item.track == null) return + + SetTrackScoreDialog(this, item).showDialog(router) + } + + override fun setStatus(item: TrackItem, selection: Int) { + presenter.setStatus(item, selection) + swipe_refresh?.isRefreshing = true + } + + override fun setScore(item: TrackItem, score: Int) { + presenter.setScore(item, score) + swipe_refresh?.isRefreshing = true + } + + override fun setChaptersRead(item: TrackItem, chaptersRead: Int) { + presenter.setLastChapterRead(item, chaptersRead) + swipe_refresh?.isRefreshing = true + } + + private companion object { + const val TAG_SEARCH_CONTROLLER = "track_search_controller" + } + } \ No newline at end of file diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/migration/MangaItem.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/migration/MangaItem.kt index ded058cae..62f94a7bb 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/migration/MangaItem.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/migration/MangaItem.kt @@ -1,6 +1,5 @@ package eu.kanade.tachiyomi.ui.migration -import android.support.v7.widget.RecyclerView import android.view.View import eu.davidea.flexibleadapter.FlexibleAdapter import eu.davidea.flexibleadapter.items.AbstractFlexibleItem @@ -14,11 +13,11 @@ class MangaItem(val manga: Manga) : AbstractFlexibleItem() { return R.layout.catalogue_list_item } - override fun createViewHolder(view: View, adapter: FlexibleAdapter>): MangaHolder { + override fun createViewHolder(view: View, adapter: FlexibleAdapter>): MangaHolder { return MangaHolder(view, adapter) } - override fun bindViewHolder(adapter: FlexibleAdapter>, + override fun bindViewHolder(adapter: FlexibleAdapter>, holder: MangaHolder, position: Int, payloads: List?) { diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/migration/MigrationController.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/migration/MigrationController.kt index fd213ad85..64be5b670 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/migration/MigrationController.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/migration/MigrationController.kt @@ -2,7 +2,6 @@ package eu.kanade.tachiyomi.ui.migration import android.app.Dialog import android.os.Bundle -import android.support.v7.widget.LinearLayoutManager import android.view.LayoutInflater import android.view.View import android.view.ViewGroup @@ -52,7 +51,7 @@ class MigrationController : NucleusController(), super.onViewCreated(view) adapter = FlexibleAdapter(null, this) - migration_recycler.layoutManager = LinearLayoutManager(view.context) + migration_recycler.layoutManager = androidx.recyclerview.widget.LinearLayoutManager(view.context) migration_recycler.adapter = adapter } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/migration/SelectionHeader.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/migration/SelectionHeader.kt index 6b59ce262..0241b323b 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/migration/SelectionHeader.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/migration/SelectionHeader.kt @@ -1,13 +1,12 @@ package eu.kanade.tachiyomi.ui.migration -import android.support.v7.widget.RecyclerView import android.view.View import eu.davidea.flexibleadapter.FlexibleAdapter import eu.davidea.flexibleadapter.items.AbstractHeaderItem import eu.davidea.flexibleadapter.items.IFlexible import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.ui.base.holder.BaseFlexibleViewHolder -import kotlinx.android.synthetic.main.catalogue_main_controller_card.title +import kotlinx.android.synthetic.main.catalogue_main_controller_card.* /** * Item that contains the selection header. @@ -24,14 +23,14 @@ class SelectionHeader : AbstractHeaderItem() { /** * Creates a new view holder for this item. */ - override fun createViewHolder(view: View, adapter: FlexibleAdapter>): Holder { + override fun createViewHolder(view: View, adapter: FlexibleAdapter>): Holder { return SelectionHeader.Holder(view, adapter) } /** * Binds this item to the given view holder. */ - override fun bindViewHolder(adapter: FlexibleAdapter>, holder: Holder, + override fun bindViewHolder(adapter: FlexibleAdapter>, holder: Holder, position: Int, payloads: List?) { // Intentionally empty } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/migration/SourceItem.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/migration/SourceItem.kt index 3b7b4b5b7..c3067845e 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/migration/SourceItem.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/migration/SourceItem.kt @@ -1,6 +1,5 @@ package eu.kanade.tachiyomi.ui.migration -import android.support.v7.widget.RecyclerView import android.view.View import eu.davidea.flexibleadapter.FlexibleAdapter import eu.davidea.flexibleadapter.items.AbstractSectionableItem @@ -27,14 +26,14 @@ data class SourceItem(val source: Source, val header: SelectionHeader? = null) : /** * Creates a new view holder for this item. */ - override fun createViewHolder(view: View, adapter: FlexibleAdapter>): SourceHolder { + override fun createViewHolder(view: View, adapter: FlexibleAdapter>): SourceHolder { return SourceHolder(view, adapter as SourceAdapter) } /** * Binds this item to the given view holder. */ - override fun bindViewHolder(adapter: FlexibleAdapter>, holder: SourceHolder, + override fun bindViewHolder(adapter: FlexibleAdapter>, holder: SourceHolder, position: Int, payloads: List?) { holder.bind(this) diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/PageIndicatorTextView.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/PageIndicatorTextView.kt index 7fa36a2a3..9be01b494 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/PageIndicatorTextView.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/PageIndicatorTextView.kt @@ -5,12 +5,12 @@ import android.content.Context import android.graphics.Canvas import android.graphics.Color import android.graphics.Paint -import android.support.v7.widget.AppCompatTextView import android.text.Spannable import android.text.SpannableString import android.text.style.ScaleXSpan import android.util.AttributeSet import android.widget.TextView +import androidx.appcompat.widget.AppCompatTextView /** * Page indicator found at the bottom of the reader diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderColorFilterSheet.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderColorFilterSheet.kt index a0579cdcc..4182bdb76 100755 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderColorFilterSheet.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderColorFilterSheet.kt @@ -1,12 +1,12 @@ package eu.kanade.tachiyomi.ui.reader import android.graphics.Color -import android.support.annotation.ColorInt -import android.support.design.widget.BottomSheetBehavior -import android.support.design.widget.BottomSheetDialog import android.view.View import android.view.ViewGroup import android.widget.SeekBar +import androidx.annotation.ColorInt +import com.google.android.material.bottomsheet.BottomSheetBehavior +import com.google.android.material.bottomsheet.BottomSheetDialog import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.data.preference.PreferencesHelper import eu.kanade.tachiyomi.data.preference.getOrDefault diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderPageSheet.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderPageSheet.kt index be465e873..f21583aa9 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderPageSheet.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderPageSheet.kt @@ -1,9 +1,9 @@ package eu.kanade.tachiyomi.ui.reader import android.os.Bundle -import android.support.design.widget.BottomSheetDialog import android.view.ViewGroup import com.afollestad.materialdialogs.MaterialDialog +import com.google.android.material.bottomsheet.BottomSheetDialog import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.source.model.Page import eu.kanade.tachiyomi.ui.reader.model.ReaderPage diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderSeekBar.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderSeekBar.kt index c429804eb..473bf0f79 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderSeekBar.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderSeekBar.kt @@ -2,9 +2,9 @@ package eu.kanade.tachiyomi.ui.reader import android.content.Context import android.graphics.Canvas -import android.support.v7.widget.AppCompatSeekBar import android.util.AttributeSet import android.view.MotionEvent +import androidx.appcompat.widget.AppCompatSeekBar /** * Seekbar to show current chapter progress. diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderSettingsSheet.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderSettingsSheet.kt index b798f3b49..3f9e96cc9 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderSettingsSheet.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderSettingsSheet.kt @@ -1,11 +1,11 @@ package eu.kanade.tachiyomi.ui.reader import android.os.Bundle -import android.support.design.widget.BottomSheetDialog -import android.support.v4.widget.NestedScrollView import android.widget.CompoundButton import android.widget.Spinner +import androidx.core.widget.NestedScrollView import com.f2prateek.rx.preferences.Preference +import com.google.android.material.bottomsheet.BottomSheetDialog import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.data.preference.PreferencesHelper import eu.kanade.tachiyomi.data.preference.getOrDefault diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/SaveImageNotifier.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/SaveImageNotifier.kt index 87ed7ab16..d69ced1e2 100755 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/SaveImageNotifier.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/SaveImageNotifier.kt @@ -2,7 +2,7 @@ package eu.kanade.tachiyomi.ui.reader import android.content.Context import android.graphics.Bitmap -import android.support.v4.app.NotificationCompat +import androidx.core.app.NotificationCompat import com.bumptech.glide.load.engine.DiskCacheStrategy import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.data.glide.GlideApp diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/loader/PageLoader.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/loader/PageLoader.kt index 38cac3817..b0b872b6d 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/loader/PageLoader.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/loader/PageLoader.kt @@ -1,6 +1,6 @@ package eu.kanade.tachiyomi.ui.reader.loader -import android.support.annotation.CallSuper +import androidx.annotation.CallSuper import eu.kanade.tachiyomi.ui.reader.model.ReaderPage import rx.Observable diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/pager/Pager.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/pager/Pager.kt index 0a0114dcc..a256a5c6e 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/pager/Pager.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/pager/Pager.kt @@ -1,10 +1,10 @@ package eu.kanade.tachiyomi.ui.reader.viewer.pager import android.content.Context -import android.support.v4.view.DirectionalViewPager import android.view.HapticFeedbackConstants import android.view.KeyEvent import android.view.MotionEvent +import androidx.viewpager.widget.DirectionalViewPager import eu.kanade.tachiyomi.ui.reader.viewer.GestureDetectorWithLongTap /** diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/pager/PagerButton.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/pager/PagerButton.kt index 030ed80d0..f14f328c3 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/pager/PagerButton.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/pager/PagerButton.kt @@ -2,8 +2,8 @@ package eu.kanade.tachiyomi.ui.reader.viewer.pager import android.annotation.SuppressLint import android.content.Context -import android.support.v7.widget.AppCompatButton import android.view.MotionEvent +import androidx.appcompat.widget.AppCompatButton /** * A button class to be used by child views of the pager viewer. All tap gestures are handled by diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/pager/PagerTransitionHolder.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/pager/PagerTransitionHolder.kt index 4133c19c5..911c5230b 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/pager/PagerTransitionHolder.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/pager/PagerTransitionHolder.kt @@ -2,7 +2,6 @@ package eu.kanade.tachiyomi.ui.reader.viewer.pager import android.annotation.SuppressLint import android.graphics.Typeface -import android.support.v7.widget.AppCompatTextView import android.text.SpannableStringBuilder import android.text.Spanned import android.text.style.StyleSpan @@ -14,6 +13,7 @@ import android.view.ViewGroup.LayoutParams.WRAP_CONTENT import android.widget.LinearLayout import android.widget.ProgressBar import android.widget.TextView +import androidx.appcompat.widget.AppCompatTextView import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.ui.reader.model.ChapterTransition import eu.kanade.tachiyomi.ui.reader.model.ReaderChapter diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/pager/PagerViewer.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/pager/PagerViewer.kt index 1e2c6f7f0..133a201c9 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/pager/PagerViewer.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/pager/PagerViewer.kt @@ -1,11 +1,11 @@ package eu.kanade.tachiyomi.ui.reader.viewer.pager -import android.support.v4.view.ViewPager import android.view.InputDevice import android.view.KeyEvent import android.view.MotionEvent import android.view.View import android.view.ViewGroup.LayoutParams +import androidx.viewpager.widget.ViewPager import com.elvishew.xlog.XLog import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.ui.reader.ReaderActivity @@ -75,7 +75,7 @@ abstract class PagerViewer(val activity: ReaderActivity) : BaseViewer { pager.offscreenPageLimit = 1 pager.id = R.id.reader_pager pager.adapter = adapter - pager.addOnPageChangeListener(object : ViewPager.SimpleOnPageChangeListener() { + pager.addOnPageChangeListener(object : androidx.viewpager.widget.ViewPager.SimpleOnPageChangeListener() { override fun onPageSelected(position: Int) { val page = adapter.items.getOrNull(position) if (page != null && currentPage != page) { @@ -88,7 +88,7 @@ abstract class PagerViewer(val activity: ReaderActivity) : BaseViewer { } override fun onPageScrollStateChanged(state: Int) { - isIdle = state == ViewPager.SCROLL_STATE_IDLE + isIdle = state == androidx.viewpager.widget.ViewPager.SCROLL_STATE_IDLE } }) pager.tapListener = { event -> diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/pager/PagerViewerAdapter.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/pager/PagerViewerAdapter.kt index df46cec2d..1da0c61ee 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/pager/PagerViewerAdapter.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/pager/PagerViewerAdapter.kt @@ -1,6 +1,5 @@ package eu.kanade.tachiyomi.ui.reader.viewer.pager -import android.support.v4.view.PagerAdapter import android.view.View import android.view.ViewGroup import eu.kanade.tachiyomi.ui.reader.model.ChapterTransition @@ -107,7 +106,7 @@ class PagerViewerAdapter(private val viewer: PagerViewer) : ViewPagerAdapter() { Timber.d("Position for ${view.item} not found") } } - return PagerAdapter.POSITION_NONE + return androidx.viewpager.widget.PagerAdapter.POSITION_NONE } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/webtoon/WebtoonAdapter.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/webtoon/WebtoonAdapter.kt index e215646c1..f17c5ee82 100755 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/webtoon/WebtoonAdapter.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/webtoon/WebtoonAdapter.kt @@ -1,10 +1,9 @@ package eu.kanade.tachiyomi.ui.reader.viewer.webtoon -import android.support.v7.util.DiffUtil -import android.support.v7.widget.RecyclerView import android.view.ViewGroup import android.widget.FrameLayout import android.widget.LinearLayout +import androidx.recyclerview.widget.DiffUtil import eu.kanade.tachiyomi.ui.reader.model.ChapterTransition import eu.kanade.tachiyomi.ui.reader.model.ReaderPage import eu.kanade.tachiyomi.ui.reader.model.ViewerChapters @@ -12,7 +11,7 @@ import eu.kanade.tachiyomi.ui.reader.model.ViewerChapters /** * RecyclerView Adapter used by this [viewer] to where [ViewerChapters] updates are posted. */ -class WebtoonAdapter(val viewer: WebtoonViewer) : RecyclerView.Adapter() { +class WebtoonAdapter(val viewer: WebtoonViewer) : androidx.recyclerview.widget.RecyclerView.Adapter() { /** * List of currently set items. @@ -82,7 +81,7 @@ class WebtoonAdapter(val viewer: WebtoonViewer) : RecyclerView.Adapter { val view = FrameLayout(parent.context) @@ -99,7 +98,7 @@ class WebtoonAdapter(val viewer: WebtoonViewer) : RecyclerView.Adapter holder.bind(item as ReaderPage) @@ -110,7 +109,7 @@ class WebtoonAdapter(val viewer: WebtoonViewer) : RecyclerView.Adapter holder.recycle() is WebtoonTransitionHolder -> holder.recycle() diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/webtoon/WebtoonLayoutManager.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/webtoon/WebtoonLayoutManager.kt index c9cd0712c..377b9677e 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/webtoon/WebtoonLayoutManager.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/webtoon/WebtoonLayoutManager.kt @@ -1,8 +1,8 @@ @file:Suppress("PackageDirectoryMismatch") -package android.support.v7.widget +package androidx.recyclerview.widget -import android.support.v7.widget.RecyclerView.NO_POSITION +import androidx.recyclerview.widget.RecyclerView.NO_POSITION import eu.kanade.tachiyomi.ui.reader.ReaderActivity /** @@ -13,7 +13,7 @@ import eu.kanade.tachiyomi.ui.reader.ReaderActivity * This layout manager uses the same package name as the support library in order to use a package * protected method. */ -class WebtoonLayoutManager(activity: ReaderActivity) : LinearLayoutManager(activity) { +class WebtoonLayoutManager(activity: ReaderActivity) : androidx.recyclerview.widget.LinearLayoutManager(activity) { /** * Extra layout space is set to half the screen height. @@ -27,7 +27,7 @@ class WebtoonLayoutManager(activity: ReaderActivity) : LinearLayoutManager(activ /** * Returns the custom extra layout space. */ - override fun getExtraLayoutSpace(state: RecyclerView.State): Int { + override fun getExtraLayoutSpace(state: androidx.recyclerview.widget.RecyclerView.State): Int { return extraLayoutSpace } @@ -36,8 +36,8 @@ class WebtoonLayoutManager(activity: ReaderActivity) : LinearLayoutManager(activ */ fun findLastEndVisibleItemPosition(): Int { ensureLayoutState() - @ViewBoundsCheck.ViewBounds val preferredBoundsFlag = - (ViewBoundsCheck.FLAG_CVE_LT_PVE or ViewBoundsCheck.FLAG_CVE_EQ_PVE) + @androidx.recyclerview.widget.ViewBoundsCheck.ViewBounds val preferredBoundsFlag = + (androidx.recyclerview.widget.ViewBoundsCheck.FLAG_CVE_LT_PVE or androidx.recyclerview.widget.ViewBoundsCheck.FLAG_CVE_EQ_PVE) val fromIndex = childCount - 1 val toIndex = -1 diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/webtoon/WebtoonPageHolder.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/webtoon/WebtoonPageHolder.kt index 754dbb7e1..ae603a3ec 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/webtoon/WebtoonPageHolder.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/webtoon/WebtoonPageHolder.kt @@ -4,8 +4,6 @@ import android.annotation.SuppressLint import android.content.Intent import android.graphics.drawable.Drawable import android.net.Uri -import android.support.v7.widget.AppCompatButton -import android.support.v7.widget.AppCompatImageView import android.view.Gravity import android.view.ViewGroup import android.view.ViewGroup.LayoutParams.MATCH_PARENT @@ -14,6 +12,8 @@ import android.widget.FrameLayout import android.widget.ImageView import android.widget.LinearLayout import android.widget.TextView +import androidx.appcompat.widget.AppCompatButton +import androidx.appcompat.widget.AppCompatImageView import com.bumptech.glide.load.DataSource import com.bumptech.glide.load.engine.DiskCacheStrategy import com.bumptech.glide.load.engine.GlideException diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/webtoon/WebtoonRecyclerView.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/webtoon/WebtoonRecyclerView.kt index 5ae6b4dcb..01489d56d 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/webtoon/WebtoonRecyclerView.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/webtoon/WebtoonRecyclerView.kt @@ -6,13 +6,12 @@ import android.animation.ValueAnimator import android.annotation.TargetApi import android.content.Context import android.os.Build -import android.support.v7.widget.LinearLayoutManager -import android.support.v7.widget.RecyclerView import android.util.AttributeSet import android.view.HapticFeedbackConstants import android.view.MotionEvent import android.view.ViewConfiguration import android.view.animation.DecelerateInterpolator +import androidx.recyclerview.widget.RecyclerView import eu.kanade.tachiyomi.ui.reader.viewer.GestureDetectorWithLongTap /** @@ -22,7 +21,7 @@ open class WebtoonRecyclerView @JvmOverloads constructor( context: Context, attrs: AttributeSet? = null, defStyle: Int = 0 -) : RecyclerView(context, attrs, defStyle) { +) : androidx.recyclerview.widget.RecyclerView(context, attrs, defStyle) { private var isZooming = false private var atLastPosition = false @@ -54,7 +53,7 @@ open class WebtoonRecyclerView @JvmOverloads constructor( super.onScrolled(dx, dy) val layoutManager = layoutManager lastVisibleItemPosition = - (layoutManager as LinearLayoutManager).findLastVisibleItemPosition() + (layoutManager as androidx.recyclerview.widget.LinearLayoutManager).findLastVisibleItemPosition() firstVisibleItemPosition = layoutManager.findFirstVisibleItemPosition() } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/webtoon/WebtoonTransitionHolder.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/webtoon/WebtoonTransitionHolder.kt index a426b04db..3bbca5c17 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/webtoon/WebtoonTransitionHolder.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/webtoon/WebtoonTransitionHolder.kt @@ -1,8 +1,6 @@ package eu.kanade.tachiyomi.ui.reader.viewer.webtoon import android.graphics.Typeface -import android.support.v7.widget.AppCompatButton -import android.support.v7.widget.AppCompatTextView import android.text.SpannableStringBuilder import android.text.Spanned import android.text.style.StyleSpan @@ -12,6 +10,8 @@ import android.view.ViewGroup.LayoutParams.WRAP_CONTENT import android.widget.LinearLayout import android.widget.ProgressBar import android.widget.TextView +import androidx.appcompat.widget.AppCompatButton +import androidx.appcompat.widget.AppCompatTextView import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.ui.reader.model.ChapterTransition import eu.kanade.tachiyomi.ui.reader.model.ReaderChapter diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/webtoon/WebtoonViewer.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/webtoon/WebtoonViewer.kt index a93077d9b..fdd629883 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/webtoon/WebtoonViewer.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/webtoon/WebtoonViewer.kt @@ -1,12 +1,12 @@ package eu.kanade.tachiyomi.ui.reader.viewer.webtoon -import android.support.v7.widget.RecyclerView -import android.support.v7.widget.WebtoonLayoutManager import android.view.KeyEvent import android.view.MotionEvent import android.view.View import android.view.ViewGroup import android.view.ViewGroup.LayoutParams.MATCH_PARENT +import androidx.recyclerview.widget.RecyclerView +import androidx.recyclerview.widget.WebtoonLayoutManager import com.elvishew.xlog.XLog import eu.kanade.tachiyomi.ui.reader.ReaderActivity import eu.kanade.tachiyomi.ui.reader.model.ChapterTransition @@ -67,8 +67,8 @@ class WebtoonViewer(val activity: ReaderActivity) : BaseViewer { recycler.itemAnimator = null recycler.layoutManager = layoutManager recycler.adapter = adapter - recycler.addOnScrollListener(object : RecyclerView.OnScrollListener() { - override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) { + recycler.addOnScrollListener(object : androidx.recyclerview.widget.RecyclerView.OnScrollListener() { + override fun onScrolled(recyclerView: androidx.recyclerview.widget.RecyclerView, dx: Int, dy: Int) { val position = layoutManager.findLastEndVisibleItemPosition() val item = adapter.items.getOrNull(position) if (item != null) { diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/recent_updates/DateItem.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/recent_updates/DateItem.kt index 2a76ef0ec..56ad55dce 100755 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/recent_updates/DateItem.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/recent_updates/DateItem.kt @@ -1,6 +1,5 @@ package eu.kanade.tachiyomi.ui.recent_updates -import android.support.v7.widget.RecyclerView import android.text.format.DateUtils import android.view.View import android.widget.TextView @@ -17,11 +16,11 @@ class DateItem(val date: Date) : AbstractHeaderItem() { return R.layout.recent_chapters_section_item } - override fun createViewHolder(view: View, adapter: FlexibleAdapter>): Holder { + override fun createViewHolder(view: View, adapter: FlexibleAdapter>): Holder { return Holder(view, adapter) } - override fun bindViewHolder(adapter: FlexibleAdapter>, holder: Holder, position: Int, payloads: List?) { + override fun bindViewHolder(adapter: FlexibleAdapter>, holder: Holder, position: Int, payloads: List?) { holder.bind(this) } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/recent_updates/RecentChapterItem.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/recent_updates/RecentChapterItem.kt index 6b7470853..b2e835e8f 100755 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/recent_updates/RecentChapterItem.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/recent_updates/RecentChapterItem.kt @@ -1,6 +1,5 @@ package eu.kanade.tachiyomi.ui.recent_updates -import android.support.v7.widget.RecyclerView import android.view.View import eu.davidea.flexibleadapter.FlexibleAdapter import eu.davidea.flexibleadapter.items.AbstractSectionableItem @@ -28,11 +27,11 @@ class RecentChapterItem(val chapter: Chapter, val manga: Manga, header: DateItem return R.layout.recent_chapters_item } - override fun createViewHolder(view: View, adapter: FlexibleAdapter>): RecentChapterHolder { + override fun createViewHolder(view: View, adapter: FlexibleAdapter>): RecentChapterHolder { return RecentChapterHolder(view , adapter as RecentChaptersAdapter) } - override fun bindViewHolder(adapter: FlexibleAdapter>, + override fun bindViewHolder(adapter: FlexibleAdapter>, holder: RecentChapterHolder, position: Int, payloads: List?) { diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/recent_updates/RecentChaptersController.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/recent_updates/RecentChaptersController.kt index 0d8d7c6d5..02d456c1d 100755 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/recent_updates/RecentChaptersController.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/recent_updates/RecentChaptersController.kt @@ -1,333 +1,331 @@ -package eu.kanade.tachiyomi.ui.recent_updates - -import android.support.v7.app.AppCompatActivity -import android.support.v7.view.ActionMode -import android.support.v7.widget.DividerItemDecoration -import android.support.v7.widget.LinearLayoutManager -import android.view.* -import com.jakewharton.rxbinding.support.v4.widget.refreshes -import com.jakewharton.rxbinding.support.v7.widget.scrollStateChanges -import eu.davidea.flexibleadapter.FlexibleAdapter -import eu.davidea.flexibleadapter.SelectableAdapter -import eu.davidea.flexibleadapter.items.IFlexible -import eu.kanade.tachiyomi.R -import eu.kanade.tachiyomi.data.download.model.Download -import eu.kanade.tachiyomi.data.library.LibraryUpdateService -import eu.kanade.tachiyomi.ui.base.controller.NoToolbarElevationController -import eu.kanade.tachiyomi.ui.base.controller.NucleusController -import eu.kanade.tachiyomi.ui.base.controller.popControllerWithTag -import eu.kanade.tachiyomi.ui.base.controller.withFadeTransaction -import eu.kanade.tachiyomi.ui.manga.MangaController -import eu.kanade.tachiyomi.ui.reader.ReaderActivity -import eu.kanade.tachiyomi.util.toast -import kotlinx.android.synthetic.main.recent_chapters_controller.* -import timber.log.Timber - -/** - * Fragment that shows recent chapters. - * Uses [R.layout.recent_chapters_controller]. - * UI related actions should be called from here. - */ -class RecentChaptersController : NucleusController(), - NoToolbarElevationController, - ActionMode.Callback, - FlexibleAdapter.OnItemClickListener, - FlexibleAdapter.OnItemLongClickListener, - FlexibleAdapter.OnUpdateListener, - ConfirmDeleteChaptersDialog.Listener, - RecentChaptersAdapter.OnCoverClickListener { - - /** - * Action mode for multiple selection. - */ - private var actionMode: ActionMode? = null - - /** - * Adapter containing the recent chapters. - */ - var adapter: RecentChaptersAdapter? = null - private set - - override fun getTitle(): String? { - return resources?.getString(R.string.label_recent_updates) - } - - override fun createPresenter(): RecentChaptersPresenter { - return RecentChaptersPresenter() - } - - override fun inflateView(inflater: LayoutInflater, container: ViewGroup): View { - return inflater.inflate(R.layout.recent_chapters_controller, container, false) - } - - /** - * Called when view is created - * @param view created view - */ - override fun onViewCreated(view: View) { - super.onViewCreated(view) - - // Init RecyclerView and adapter - val layoutManager = LinearLayoutManager(view.context) - recycler.layoutManager = layoutManager - recycler.addItemDecoration(DividerItemDecoration(view.context, DividerItemDecoration.VERTICAL)) - recycler.setHasFixedSize(true) - adapter = RecentChaptersAdapter(this@RecentChaptersController) - recycler.adapter = adapter - - recycler.scrollStateChanges().subscribeUntilDestroy { - // Disable swipe refresh when view is not at the top - val firstPos = layoutManager.findFirstCompletelyVisibleItemPosition() - swipe_refresh.isEnabled = firstPos <= 0 - } - - swipe_refresh.setDistanceToTriggerSync((2 * 64 * view.resources.displayMetrics.density).toInt()) - swipe_refresh.refreshes().subscribeUntilDestroy { - if (!LibraryUpdateService.isRunning(view.context)) { - LibraryUpdateService.start(view.context) - view.context.toast(R.string.action_update_library) - } - // It can be a very long operation, so we disable swipe refresh and show a toast. - swipe_refresh.isRefreshing = false - } - } - - override fun onDestroyView(view: View) { - adapter = null - actionMode = null - super.onDestroyView(view) - } - - /** - * Returns selected chapters - * @return list of selected chapters - */ - fun getSelectedChapters(): List { - val adapter = adapter ?: return emptyList() - return adapter.selectedPositions.mapNotNull { adapter.getItem(it) as? RecentChapterItem } - } - - /** - * Called when item in list is clicked - * @param position position of clicked item - */ - override fun onItemClick(view: View, position: Int): Boolean { - val adapter = adapter ?: return false - - // Get item from position - val item = adapter.getItem(position) as? RecentChapterItem ?: return false - if (actionMode != null && adapter.mode == SelectableAdapter.Mode.MULTI) { - toggleSelection(position) - return true - } else { - openChapter(item) - return false - } - } - - /** - * Called when item in list is long clicked - * @param position position of clicked item - */ - override fun onItemLongClick(position: Int) { - if (actionMode == null) - actionMode = (activity as AppCompatActivity).startSupportActionMode(this) - - toggleSelection(position) - } - - /** - * Called to toggle selection - * @param position position of selected item - */ - private fun toggleSelection(position: Int) { - val adapter = adapter ?: return - adapter.toggleSelection(position) - actionMode?.invalidate() - } - - /** - * Open chapter in reader - * @param chapter selected chapter - */ - private fun openChapter(item: RecentChapterItem) { - val activity = activity ?: return - val intent = ReaderActivity.newIntent(activity, item.manga, item.chapter) - startActivity(intent) - } - - /** - * Download selected items - * @param chapters list of selected [RecentChapter]s - */ - fun downloadChapters(chapters: List) { - destroyActionModeIfNeeded() - presenter.downloadChapters(chapters) - } - - /** - * Populate adapter with chapters - * @param chapters list of [Any] - */ - fun onNextRecentChapters(chapters: List>) { - destroyActionModeIfNeeded() - adapter?.updateDataSet(chapters) - } - - override fun onUpdateEmptyView(size: Int) { - if (size > 0) { - empty_view?.hide() - } else { - empty_view?.show(R.drawable.ic_update_black_128dp, R.string.information_no_recent) - } - } - - /** - * Update download status of chapter - * @param download [Download] object containing download progress. - */ - fun onChapterStatusChange(download: Download) { - getHolder(download)?.notifyStatus(download.status) - } - - /** - * Returns holder belonging to chapter - * @param download [Download] object containing download progress. - */ - private fun getHolder(download: Download): RecentChapterHolder? { - return recycler?.findViewHolderForItemId(download.chapter.id!!) as? RecentChapterHolder - } - - /** - * Mark chapter as read - * @param chapters list of chapters - */ - fun markAsRead(chapters: List) { - presenter.markChapterRead(chapters, true) - if (presenter.preferences.removeAfterMarkedAsRead()) { - deleteChapters(chapters) - } - } - - override fun deleteChapters(chaptersToDelete: List) { - destroyActionModeIfNeeded() - DeletingChaptersDialog().showDialog(router) - presenter.deleteChapters(chaptersToDelete) - } - - /** - * Destory [ActionMode] if it's shown - */ - fun destroyActionModeIfNeeded() { - actionMode?.finish() - } - - /** - * Mark chapter as unread - * @param chapters list of selected [RecentChapter] - */ - fun markAsUnread(chapters: List) { - presenter.markChapterRead(chapters, false) - } - - /** - * Start downloading chapter - * @param chapter selected chapter with manga - */ - fun downloadChapter(chapter: RecentChapterItem) { - presenter.downloadChapters(listOf(chapter)) - } - - /** - * Start deleting chapter - * @param chapter selected chapter with manga - */ - fun deleteChapter(chapter: RecentChapterItem) { - DeletingChaptersDialog().showDialog(router) - presenter.deleteChapters(listOf(chapter)) - } - - override fun onCoverClick(position: Int) { - val chapterClicked = adapter?.getItem(position) as? RecentChapterItem ?: return - openManga(chapterClicked) - - } - - fun openManga(chapter: RecentChapterItem) { - router.pushController(MangaController(chapter.manga).withFadeTransaction()) - } - - /** - * Called when chapters are deleted - */ - fun onChaptersDeleted() { - dismissDeletingDialog() - adapter?.notifyDataSetChanged() - } - - /** - * Called when error while deleting - * @param error error message - */ - fun onChaptersDeletedError(error: Throwable) { - dismissDeletingDialog() - Timber.e(error) - } - - /** - * Called to dismiss deleting dialog - */ - fun dismissDeletingDialog() { - router.popControllerWithTag(DeletingChaptersDialog.TAG) - } - - /** - * Called when ActionMode created. - * @param mode the ActionMode object - * @param menu menu object of ActionMode - */ - override fun onCreateActionMode(mode: ActionMode, menu: Menu): Boolean { - mode.menuInflater.inflate(R.menu.chapter_recent_selection, menu) - adapter?.mode = SelectableAdapter.Mode.MULTI - return true - } - - override fun onPrepareActionMode(mode: ActionMode, menu: Menu): Boolean { - val count = adapter?.selectedItemCount ?: 0 - if (count == 0) { - // Destroy action mode if there are no items selected. - destroyActionModeIfNeeded() - } else { - mode.title = resources?.getString(R.string.label_selected, count) - } - return false - } - - /** - * Called when ActionMode item clicked - * @param mode the ActionMode object - * @param item item from ActionMode. - */ - override fun onActionItemClicked(mode: ActionMode, item: MenuItem): Boolean { - when (item.itemId) { - R.id.action_mark_as_read -> markAsRead(getSelectedChapters()) - R.id.action_mark_as_unread -> markAsUnread(getSelectedChapters()) - R.id.action_download -> downloadChapters(getSelectedChapters()) - R.id.action_delete -> ConfirmDeleteChaptersDialog(this, getSelectedChapters()) - .showDialog(router) - else -> return false - } - return true - } - - /** - * Called when ActionMode destroyed - * @param mode the ActionMode object - */ - override fun onDestroyActionMode(mode: ActionMode?) { - adapter?.mode = SelectableAdapter.Mode.IDLE - adapter?.clearSelection() - actionMode = null - } - -} +package eu.kanade.tachiyomi.ui.recent_updates + +import android.view.* +import androidx.appcompat.app.AppCompatActivity +import androidx.appcompat.view.ActionMode +import com.jakewharton.rxbinding.support.v4.widget.refreshes +import com.jakewharton.rxbinding.support.v7.widget.scrollStateChanges +import eu.davidea.flexibleadapter.FlexibleAdapter +import eu.davidea.flexibleadapter.SelectableAdapter +import eu.davidea.flexibleadapter.items.IFlexible +import eu.kanade.tachiyomi.R +import eu.kanade.tachiyomi.data.download.model.Download +import eu.kanade.tachiyomi.data.library.LibraryUpdateService +import eu.kanade.tachiyomi.ui.base.controller.NoToolbarElevationController +import eu.kanade.tachiyomi.ui.base.controller.NucleusController +import eu.kanade.tachiyomi.ui.base.controller.popControllerWithTag +import eu.kanade.tachiyomi.ui.base.controller.withFadeTransaction +import eu.kanade.tachiyomi.ui.manga.MangaController +import eu.kanade.tachiyomi.ui.reader.ReaderActivity +import eu.kanade.tachiyomi.util.toast +import kotlinx.android.synthetic.main.recent_chapters_controller.* +import timber.log.Timber + +/** + * Fragment that shows recent chapters. + * Uses [R.layout.recent_chapters_controller]. + * UI related actions should be called from here. + */ +class RecentChaptersController : NucleusController(), + NoToolbarElevationController, + ActionMode.Callback, + FlexibleAdapter.OnItemClickListener, + FlexibleAdapter.OnItemLongClickListener, + FlexibleAdapter.OnUpdateListener, + ConfirmDeleteChaptersDialog.Listener, + RecentChaptersAdapter.OnCoverClickListener { + + /** + * Action mode for multiple selection. + */ + private var actionMode: ActionMode? = null + + /** + * Adapter containing the recent chapters. + */ + var adapter: RecentChaptersAdapter? = null + private set + + override fun getTitle(): String? { + return resources?.getString(R.string.label_recent_updates) + } + + override fun createPresenter(): RecentChaptersPresenter { + return RecentChaptersPresenter() + } + + override fun inflateView(inflater: LayoutInflater, container: ViewGroup): View { + return inflater.inflate(R.layout.recent_chapters_controller, container, false) + } + + /** + * Called when view is created + * @param view created view + */ + override fun onViewCreated(view: View) { + super.onViewCreated(view) + + // Init RecyclerView and adapter + val layoutManager = androidx.recyclerview.widget.LinearLayoutManager(view.context) + recycler.layoutManager = layoutManager + recycler.addItemDecoration(androidx.recyclerview.widget.DividerItemDecoration(view.context, androidx.recyclerview.widget.DividerItemDecoration.VERTICAL)) + recycler.setHasFixedSize(true) + adapter = RecentChaptersAdapter(this@RecentChaptersController) + recycler.adapter = adapter + + recycler.scrollStateChanges().subscribeUntilDestroy { + // Disable swipe refresh when view is not at the top + val firstPos = layoutManager.findFirstCompletelyVisibleItemPosition() + swipe_refresh.isEnabled = firstPos <= 0 + } + + swipe_refresh.setDistanceToTriggerSync((2 * 64 * view.resources.displayMetrics.density).toInt()) + swipe_refresh.refreshes().subscribeUntilDestroy { + if (!LibraryUpdateService.isRunning(view.context)) { + LibraryUpdateService.start(view.context) + view.context.toast(R.string.action_update_library) + } + // It can be a very long operation, so we disable swipe refresh and show a toast. + swipe_refresh.isRefreshing = false + } + } + + override fun onDestroyView(view: View) { + adapter = null + actionMode = null + super.onDestroyView(view) + } + + /** + * Returns selected chapters + * @return list of selected chapters + */ + fun getSelectedChapters(): List { + val adapter = adapter ?: return emptyList() + return adapter.selectedPositions.mapNotNull { adapter.getItem(it) as? RecentChapterItem } + } + + /** + * Called when item in list is clicked + * @param position position of clicked item + */ + override fun onItemClick(view: View, position: Int): Boolean { + val adapter = adapter ?: return false + + // Get item from position + val item = adapter.getItem(position) as? RecentChapterItem ?: return false + if (actionMode != null && adapter.mode == SelectableAdapter.Mode.MULTI) { + toggleSelection(position) + return true + } else { + openChapter(item) + return false + } + } + + /** + * Called when item in list is long clicked + * @param position position of clicked item + */ + override fun onItemLongClick(position: Int) { + if (actionMode == null) + actionMode = (activity as AppCompatActivity).startSupportActionMode(this) + + toggleSelection(position) + } + + /** + * Called to toggle selection + * @param position position of selected item + */ + private fun toggleSelection(position: Int) { + val adapter = adapter ?: return + adapter.toggleSelection(position) + actionMode?.invalidate() + } + + /** + * Open chapter in reader + * @param chapter selected chapter + */ + private fun openChapter(item: RecentChapterItem) { + val activity = activity ?: return + val intent = ReaderActivity.newIntent(activity, item.manga, item.chapter) + startActivity(intent) + } + + /** + * Download selected items + * @param chapters list of selected [RecentChapter]s + */ + fun downloadChapters(chapters: List) { + destroyActionModeIfNeeded() + presenter.downloadChapters(chapters) + } + + /** + * Populate adapter with chapters + * @param chapters list of [Any] + */ + fun onNextRecentChapters(chapters: List>) { + destroyActionModeIfNeeded() + adapter?.updateDataSet(chapters) + } + + override fun onUpdateEmptyView(size: Int) { + if (size > 0) { + empty_view?.hide() + } else { + empty_view?.show(R.drawable.ic_update_black_128dp, R.string.information_no_recent) + } + } + + /** + * Update download status of chapter + * @param download [Download] object containing download progress. + */ + fun onChapterStatusChange(download: Download) { + getHolder(download)?.notifyStatus(download.status) + } + + /** + * Returns holder belonging to chapter + * @param download [Download] object containing download progress. + */ + private fun getHolder(download: Download): RecentChapterHolder? { + return recycler?.findViewHolderForItemId(download.chapter.id!!) as? RecentChapterHolder + } + + /** + * Mark chapter as read + * @param chapters list of chapters + */ + fun markAsRead(chapters: List) { + presenter.markChapterRead(chapters, true) + if (presenter.preferences.removeAfterMarkedAsRead()) { + deleteChapters(chapters) + } + } + + override fun deleteChapters(chaptersToDelete: List) { + destroyActionModeIfNeeded() + DeletingChaptersDialog().showDialog(router) + presenter.deleteChapters(chaptersToDelete) + } + + /** + * Destory [ActionMode] if it's shown + */ + fun destroyActionModeIfNeeded() { + actionMode?.finish() + } + + /** + * Mark chapter as unread + * @param chapters list of selected [RecentChapter] + */ + fun markAsUnread(chapters: List) { + presenter.markChapterRead(chapters, false) + } + + /** + * Start downloading chapter + * @param chapter selected chapter with manga + */ + fun downloadChapter(chapter: RecentChapterItem) { + presenter.downloadChapters(listOf(chapter)) + } + + /** + * Start deleting chapter + * @param chapter selected chapter with manga + */ + fun deleteChapter(chapter: RecentChapterItem) { + DeletingChaptersDialog().showDialog(router) + presenter.deleteChapters(listOf(chapter)) + } + + override fun onCoverClick(position: Int) { + val chapterClicked = adapter?.getItem(position) as? RecentChapterItem ?: return + openManga(chapterClicked) + + } + + fun openManga(chapter: RecentChapterItem) { + router.pushController(MangaController(chapter.manga).withFadeTransaction()) + } + + /** + * Called when chapters are deleted + */ + fun onChaptersDeleted() { + dismissDeletingDialog() + adapter?.notifyDataSetChanged() + } + + /** + * Called when error while deleting + * @param error error message + */ + fun onChaptersDeletedError(error: Throwable) { + dismissDeletingDialog() + Timber.e(error) + } + + /** + * Called to dismiss deleting dialog + */ + fun dismissDeletingDialog() { + router.popControllerWithTag(DeletingChaptersDialog.TAG) + } + + /** + * Called when ActionMode created. + * @param mode the ActionMode object + * @param menu menu object of ActionMode + */ + override fun onCreateActionMode(mode: ActionMode, menu: Menu): Boolean { + mode.menuInflater.inflate(R.menu.chapter_recent_selection, menu) + adapter?.mode = SelectableAdapter.Mode.MULTI + return true + } + + override fun onPrepareActionMode(mode: ActionMode, menu: Menu): Boolean { + val count = adapter?.selectedItemCount ?: 0 + if (count == 0) { + // Destroy action mode if there are no items selected. + destroyActionModeIfNeeded() + } else { + mode.title = resources?.getString(R.string.label_selected, count) + } + return false + } + + /** + * Called when ActionMode item clicked + * @param mode the ActionMode object + * @param item item from ActionMode. + */ + override fun onActionItemClicked(mode: ActionMode, item: MenuItem): Boolean { + when (item.itemId) { + R.id.action_mark_as_read -> markAsRead(getSelectedChapters()) + R.id.action_mark_as_unread -> markAsUnread(getSelectedChapters()) + R.id.action_download -> downloadChapters(getSelectedChapters()) + R.id.action_delete -> ConfirmDeleteChaptersDialog(this, getSelectedChapters()) + .showDialog(router) + else -> return false + } + return true + } + + /** + * Called when ActionMode destroyed + * @param mode the ActionMode object + */ + override fun onDestroyActionMode(mode: ActionMode?) { + adapter?.mode = SelectableAdapter.Mode.IDLE + adapter?.clearSelection() + actionMode = null + } + +} diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/recently_read/RecentlyReadController.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/recently_read/RecentlyReadController.kt index c094c39fc..260b0e313 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/recently_read/RecentlyReadController.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/recently_read/RecentlyReadController.kt @@ -1,6 +1,5 @@ package eu.kanade.tachiyomi.ui.recently_read -import android.support.v7.widget.LinearLayoutManager import android.view.LayoutInflater import android.view.View import android.view.ViewGroup @@ -54,7 +53,7 @@ class RecentlyReadController : NucleusController(), super.onViewCreated(view) // Initialize adapter - recycler.layoutManager = LinearLayoutManager(view.context) + recycler.layoutManager = androidx.recyclerview.widget.LinearLayoutManager(view.context) adapter = RecentlyReadAdapter(this@RecentlyReadController) recycler.setHasFixedSize(true) recycler.adapter = adapter diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/recently_read/RecentlyReadItem.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/recently_read/RecentlyReadItem.kt index 2226721dd..e9346bf6e 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/recently_read/RecentlyReadItem.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/recently_read/RecentlyReadItem.kt @@ -1,6 +1,5 @@ package eu.kanade.tachiyomi.ui.recently_read -import android.support.v7.widget.RecyclerView import android.view.View import eu.davidea.flexibleadapter.FlexibleAdapter import eu.davidea.flexibleadapter.items.AbstractFlexibleItem @@ -14,11 +13,11 @@ class RecentlyReadItem(val mch: MangaChapterHistory) : AbstractFlexibleItem>): RecentlyReadHolder { + override fun createViewHolder(view: View, adapter: FlexibleAdapter>): RecentlyReadHolder { return RecentlyReadHolder(view, adapter as RecentlyReadAdapter) } - override fun bindViewHolder(adapter: FlexibleAdapter>, + override fun bindViewHolder(adapter: FlexibleAdapter>, holder: RecentlyReadHolder, position: Int, payloads: List?) { diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/setting/AnilistLoginActivity.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/setting/AnilistLoginActivity.kt index 6b5da186e..3815b847f 100755 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/setting/AnilistLoginActivity.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/setting/AnilistLoginActivity.kt @@ -2,11 +2,11 @@ package eu.kanade.tachiyomi.ui.setting import android.content.Intent import android.os.Bundle -import android.support.v7.app.AppCompatActivity import android.view.Gravity.CENTER import android.view.ViewGroup.LayoutParams.WRAP_CONTENT import android.widget.FrameLayout import android.widget.ProgressBar +import androidx.appcompat.app.AppCompatActivity import eu.kanade.tachiyomi.data.track.TrackManager import eu.kanade.tachiyomi.ui.main.MainActivity import rx.android.schedulers.AndroidSchedulers diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/setting/BangumiLoginActivity.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/setting/BangumiLoginActivity.kt index 5654b4efa..0d4e180da 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/setting/BangumiLoginActivity.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/setting/BangumiLoginActivity.kt @@ -2,11 +2,11 @@ package eu.kanade.tachiyomi.ui.setting import android.content.Intent import android.os.Bundle -import android.support.v7.app.AppCompatActivity import android.view.Gravity.CENTER import android.view.ViewGroup.LayoutParams.WRAP_CONTENT import android.widget.FrameLayout import android.widget.ProgressBar +import androidx.appcompat.app.AppCompatActivity import eu.kanade.tachiyomi.data.track.TrackManager import eu.kanade.tachiyomi.ui.main.MainActivity import rx.android.schedulers.AndroidSchedulers diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/setting/PreferenceDSL.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/setting/PreferenceDSL.kt index 5d74a00f7..265fc4e53 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/setting/PreferenceDSL.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/setting/PreferenceDSL.kt @@ -1,8 +1,8 @@ package eu.kanade.tachiyomi.ui.setting -import android.support.graphics.drawable.VectorDrawableCompat -import android.support.v4.graphics.drawable.DrawableCompat -import android.support.v7.preference.* +import androidx.core.graphics.drawable.DrawableCompat +import androidx.preference.* +import androidx.vectordrawable.graphics.drawable.VectorDrawableCompat import eu.kanade.tachiyomi.widget.preference.IntListPreference @DslMarker diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsAboutController.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsAboutController.kt index 90e243af7..76d60f1be 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsAboutController.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsAboutController.kt @@ -4,8 +4,8 @@ import android.app.Dialog import android.content.Intent import android.net.Uri import android.os.Bundle -import android.support.v7.preference.PreferenceScreen import android.view.View +import androidx.preference.PreferenceScreen import com.afollestad.materialdialogs.MaterialDialog import eu.kanade.tachiyomi.BuildConfig import eu.kanade.tachiyomi.R @@ -22,8 +22,7 @@ import timber.log.Timber import java.text.DateFormat import java.text.ParseException import java.text.SimpleDateFormat -import java.util.Locale -import java.util.TimeZone +import java.util.* import eu.kanade.tachiyomi.data.preference.PreferenceKeys as Keys class SettingsAboutController : SettingsController() { diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsAdvancedController.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsAdvancedController.kt index dd8066ca1..8bb17a265 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsAdvancedController.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsAdvancedController.kt @@ -2,13 +2,12 @@ package eu.kanade.tachiyomi.ui.setting import android.app.Dialog import android.os.Bundle -import android.support.v7.preference.PreferenceScreen import android.text.Html import android.view.View +import androidx.preference.PreferenceScreen import com.afollestad.materialdialogs.MaterialDialog import com.bluelinelabs.conductor.RouterTransaction import com.bluelinelabs.conductor.changehandler.FadeChangeHandler -import eu.kanade.tachiyomi.BuildConfig import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.data.cache.ChapterCache import eu.kanade.tachiyomi.data.database.DatabaseHelper diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsBackupController.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsBackupController.kt index c3d7677b5..843e1526f 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsBackupController.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsBackupController.kt @@ -7,8 +7,8 @@ import android.content.* import android.net.Uri import android.os.Build import android.os.Bundle -import android.support.v7.preference.PreferenceScreen import android.view.View +import androidx.preference.PreferenceScreen import com.afollestad.materialdialogs.MaterialDialog import com.hippo.unifile.UniFile import eu.kanade.tachiyomi.R diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsController.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsController.kt index 21af01ca5..7586e8a68 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsController.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsController.kt @@ -1,87 +1,87 @@ -package eu.kanade.tachiyomi.ui.setting - -import android.content.Context -import android.os.Bundle -import android.support.v7.app.AppCompatActivity -import android.support.v7.preference.PreferenceController -import android.support.v7.preference.PreferenceScreen -import android.util.TypedValue -import android.view.ContextThemeWrapper -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import com.bluelinelabs.conductor.ControllerChangeHandler -import com.bluelinelabs.conductor.ControllerChangeType -import eu.kanade.tachiyomi.R -import eu.kanade.tachiyomi.data.preference.PreferencesHelper -import eu.kanade.tachiyomi.ui.base.controller.BaseController -import rx.Observable -import rx.Subscription -import rx.subscriptions.CompositeSubscription -import uy.kohesive.injekt.Injekt -import uy.kohesive.injekt.api.get - -abstract class SettingsController : PreferenceController() { - - val preferences: PreferencesHelper = Injekt.get() - - var untilDestroySubscriptions = CompositeSubscription() - private set - - override fun onCreateView(inflater: LayoutInflater, container: ViewGroup, savedInstanceState: Bundle?): View { - if (untilDestroySubscriptions.isUnsubscribed) { - untilDestroySubscriptions = CompositeSubscription() - } - return super.onCreateView(inflater, container, savedInstanceState) - } - - override fun onDestroyView(view: View) { - super.onDestroyView(view) - untilDestroySubscriptions.unsubscribe() - } - - override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { - val screen = preferenceManager.createPreferenceScreen(getThemedContext()) - preferenceScreen = screen - setupPreferenceScreen(screen) - } - - abstract fun setupPreferenceScreen(screen: PreferenceScreen): Any? - - private fun getThemedContext(): Context { - val tv = TypedValue() - activity!!.theme.resolveAttribute(R.attr.preferenceTheme, tv, true) - return ContextThemeWrapper(activity, tv.resourceId) - } - - open fun getTitle(): String? { - return preferenceScreen?.title?.toString() - } - - fun setTitle() { - var parentController = parentController - while (parentController != null) { - if (parentController is BaseController && parentController.getTitle() != null) { - return - } - parentController = parentController.parentController - } - - (activity as? AppCompatActivity)?.supportActionBar?.title = getTitle() - } - - override fun onChangeStarted(handler: ControllerChangeHandler, type: ControllerChangeType) { - if (type.isEnter) { - setTitle() - } - super.onChangeStarted(handler, type) - } - - fun Observable.subscribeUntilDestroy(): Subscription { - return subscribe().also { untilDestroySubscriptions.add(it) } - } - - fun Observable.subscribeUntilDestroy(onNext: (T) -> Unit): Subscription { - return subscribe(onNext).also { untilDestroySubscriptions.add(it) } - } +package eu.kanade.tachiyomi.ui.setting + +import android.content.Context +import android.os.Bundle +import android.util.TypedValue +import android.view.ContextThemeWrapper +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.appcompat.app.AppCompatActivity +import androidx.preference.PreferenceController +import androidx.preference.PreferenceScreen +import com.bluelinelabs.conductor.ControllerChangeHandler +import com.bluelinelabs.conductor.ControllerChangeType +import eu.kanade.tachiyomi.R +import eu.kanade.tachiyomi.data.preference.PreferencesHelper +import eu.kanade.tachiyomi.ui.base.controller.BaseController +import rx.Observable +import rx.Subscription +import rx.subscriptions.CompositeSubscription +import uy.kohesive.injekt.Injekt +import uy.kohesive.injekt.api.get + +abstract class SettingsController : PreferenceController() { + + val preferences: PreferencesHelper = Injekt.get() + + var untilDestroySubscriptions = CompositeSubscription() + private set + + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup, savedInstanceState: Bundle?): View { + if (untilDestroySubscriptions.isUnsubscribed) { + untilDestroySubscriptions = CompositeSubscription() + } + return super.onCreateView(inflater, container, savedInstanceState) + } + + override fun onDestroyView(view: View) { + super.onDestroyView(view) + untilDestroySubscriptions.unsubscribe() + } + + override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { + val screen = preferenceManager.createPreferenceScreen(getThemedContext()) + preferenceScreen = screen + setupPreferenceScreen(screen) + } + + abstract fun setupPreferenceScreen(screen: PreferenceScreen): Any? + + private fun getThemedContext(): Context { + val tv = TypedValue() + activity!!.theme.resolveAttribute(R.attr.preferenceTheme, tv, true) + return ContextThemeWrapper(activity, tv.resourceId) + } + + open fun getTitle(): String? { + return preferenceScreen?.title?.toString() + } + + fun setTitle() { + var parentController = parentController + while (parentController != null) { + if (parentController is BaseController && parentController.getTitle() != null) { + return + } + parentController = parentController.parentController + } + + (activity as? AppCompatActivity)?.supportActionBar?.title = getTitle() + } + + override fun onChangeStarted(handler: ControllerChangeHandler, type: ControllerChangeType) { + if (type.isEnter) { + setTitle() + } + super.onChangeStarted(handler, type) + } + + fun Observable.subscribeUntilDestroy(): Subscription { + return subscribe().also { untilDestroySubscriptions.add(it) } + } + + fun Observable.subscribeUntilDestroy(onNext: (T) -> Unit): Subscription { + return subscribe(onNext).also { untilDestroySubscriptions.add(it) } + } } \ No newline at end of file diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsDownloadController.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsDownloadController.kt index 6a06ec8ca..e4531de8d 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsDownloadController.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsDownloadController.kt @@ -8,8 +8,8 @@ import android.net.Uri import android.os.Build import android.os.Bundle import android.os.Environment -import android.support.v4.content.ContextCompat -import android.support.v7.preference.PreferenceScreen +import androidx.core.content.ContextCompat +import androidx.preference.PreferenceScreen import com.afollestad.materialdialogs.MaterialDialog import com.hippo.unifile.UniFile import eu.kanade.tachiyomi.R diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsEhController.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsEhController.kt index 4299d4b5e..334943ddc 100755 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsEhController.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsEhController.kt @@ -2,8 +2,8 @@ package eu.kanade.tachiyomi.ui.setting import android.os.Build import android.os.Handler -import android.support.v7.preference.PreferenceScreen import android.widget.Toast +import androidx.preference.PreferenceScreen import com.afollestad.materialdialogs.MaterialDialog import com.bluelinelabs.conductor.RouterTransaction import com.bluelinelabs.conductor.changehandler.FadeChangeHandler diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsGeneralController.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsGeneralController.kt index e0f15700f..ca0d52128 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsGeneralController.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsGeneralController.kt @@ -4,8 +4,8 @@ import android.app.Dialog import android.os.Build import android.os.Bundle import android.os.Handler -import android.support.v7.preference.PreferenceScreen import android.view.View +import androidx.preference.PreferenceScreen import com.afollestad.materialdialogs.MaterialDialog import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.data.database.DatabaseHelper diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsHlController.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsHlController.kt index dde2f50f6..b29be094e 100755 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsHlController.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsHlController.kt @@ -1,13 +1,7 @@ package eu.kanade.tachiyomi.ui.setting -import android.support.v7.preference.PreferenceScreen -import android.widget.Toast +import androidx.preference.PreferenceScreen import eu.kanade.tachiyomi.data.preference.PreferenceKeys -import eu.kanade.tachiyomi.source.SourceManager -import eu.kanade.tachiyomi.util.toast -import exh.HITOMI_SOURCE_ID -import uy.kohesive.injekt.Injekt -import uy.kohesive.injekt.api.get /** * hitomi.la Settings fragment diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsMainController.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsMainController.kt index b3ea86b8d..ee690552a 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsMainController.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsMainController.kt @@ -1,79 +1,79 @@ -package eu.kanade.tachiyomi.ui.setting - -import android.support.v7.preference.PreferenceScreen -import eu.kanade.tachiyomi.R -import eu.kanade.tachiyomi.ui.base.controller.withFadeTransaction -import eu.kanade.tachiyomi.util.getResourceColor - -class SettingsMainController : SettingsController() { - override fun setupPreferenceScreen(screen: PreferenceScreen) = with(screen) { - titleRes = R.string.label_settings - - val tintColor = context.getResourceColor(R.attr.colorAccent) - - preference { - iconRes = R.drawable.ic_tune_black_24dp - iconTint = tintColor - titleRes = R.string.pref_category_general - onClick { navigateTo(SettingsGeneralController()) } - } - preference { - iconRes = R.drawable.ic_chrome_reader_mode_black_24dp - iconTint = tintColor - titleRes = R.string.pref_category_reader - onClick { navigateTo(SettingsReaderController()) } - } - preference { - iconRes = R.drawable.ic_file_download_black_24dp - iconTint = tintColor - titleRes = R.string.pref_category_downloads - onClick { navigateTo(SettingsDownloadController()) } - } - preference { - iconRes = R.drawable.ic_sync_black_24dp - iconTint = tintColor - titleRes = R.string.pref_category_tracking - onClick { navigateTo(SettingsTrackingController()) } - } - preference { - iconRes = R.drawable.ic_backup_black_24dp - iconTint = tintColor - titleRes = R.string.backup - onClick { navigateTo(SettingsBackupController()) } - } - preference { - iconRes = R.drawable.eh_ic_ehlogo_red_24dp - iconTint = tintColor - titleRes = R.string.pref_category_eh - onClick { navigateTo(SettingsEhController()) } - } - preference { - iconRes = R.drawable.eh_ic_nhlogo_color - iconTint = tintColor - titleRes = R.string.pref_category_nh - onClick { navigateTo(SettingsNhController()) } - } - preference { - iconRes = R.drawable.eh_ic_hllogo - iconTint = tintColor - titleRes = R.string.pref_category_hl - onClick { navigateTo(SettingsHlController()) } - } - preference { - iconRes = R.drawable.ic_code_black_24dp - iconTint = tintColor - titleRes = R.string.pref_category_advanced - onClick { navigateTo(SettingsAdvancedController()) } - } - preference { - iconRes = R.drawable.ic_help_black_24dp - iconTint = tintColor - titleRes = R.string.pref_category_about - onClick { navigateTo(SettingsAboutController()) } - } - } - - private fun navigateTo(controller: SettingsController) { - router.pushController(controller.withFadeTransaction()) - } +package eu.kanade.tachiyomi.ui.setting + +import androidx.preference.PreferenceScreen +import eu.kanade.tachiyomi.R +import eu.kanade.tachiyomi.ui.base.controller.withFadeTransaction +import eu.kanade.tachiyomi.util.getResourceColor + +class SettingsMainController : SettingsController() { + override fun setupPreferenceScreen(screen: PreferenceScreen) = with(screen) { + titleRes = R.string.label_settings + + val tintColor = context.getResourceColor(R.attr.colorAccent) + + preference { + iconRes = R.drawable.ic_tune_black_24dp + iconTint = tintColor + titleRes = R.string.pref_category_general + onClick { navigateTo(SettingsGeneralController()) } + } + preference { + iconRes = R.drawable.ic_chrome_reader_mode_black_24dp + iconTint = tintColor + titleRes = R.string.pref_category_reader + onClick { navigateTo(SettingsReaderController()) } + } + preference { + iconRes = R.drawable.ic_file_download_black_24dp + iconTint = tintColor + titleRes = R.string.pref_category_downloads + onClick { navigateTo(SettingsDownloadController()) } + } + preference { + iconRes = R.drawable.ic_sync_black_24dp + iconTint = tintColor + titleRes = R.string.pref_category_tracking + onClick { navigateTo(SettingsTrackingController()) } + } + preference { + iconRes = R.drawable.ic_backup_black_24dp + iconTint = tintColor + titleRes = R.string.backup + onClick { navigateTo(SettingsBackupController()) } + } + preference { + iconRes = R.drawable.eh_ic_ehlogo_red_24dp + iconTint = tintColor + titleRes = R.string.pref_category_eh + onClick { navigateTo(SettingsEhController()) } + } + preference { + iconRes = R.drawable.eh_ic_nhlogo_color + iconTint = tintColor + titleRes = R.string.pref_category_nh + onClick { navigateTo(SettingsNhController()) } + } + preference { + iconRes = R.drawable.eh_ic_hllogo + iconTint = tintColor + titleRes = R.string.pref_category_hl + onClick { navigateTo(SettingsHlController()) } + } + preference { + iconRes = R.drawable.ic_code_black_24dp + iconTint = tintColor + titleRes = R.string.pref_category_advanced + onClick { navigateTo(SettingsAdvancedController()) } + } + preference { + iconRes = R.drawable.ic_help_black_24dp + iconTint = tintColor + titleRes = R.string.pref_category_about + onClick { navigateTo(SettingsAboutController()) } + } + } + + private fun navigateTo(controller: SettingsController) { + router.pushController(controller.withFadeTransaction()) + } } \ No newline at end of file diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsNhController.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsNhController.kt index 856dfede6..26f105b0e 100755 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsNhController.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsNhController.kt @@ -1,6 +1,6 @@ package eu.kanade.tachiyomi.ui.setting -import android.support.v7.preference.PreferenceScreen +import androidx.preference.PreferenceScreen import eu.kanade.tachiyomi.data.preference.PreferenceKeys /** diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsReaderController.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsReaderController.kt index 28c898dac..3fc324445 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsReaderController.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsReaderController.kt @@ -1,9 +1,8 @@ package eu.kanade.tachiyomi.ui.setting import android.os.Build -import android.support.v7.preference.PreferenceScreen +import androidx.preference.PreferenceScreen import eu.kanade.tachiyomi.R -import eu.kanade.tachiyomi.util.SharedData.map import eu.kanade.tachiyomi.data.preference.PreferenceKeys as Keys class SettingsReaderController : SettingsController() { diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsSourcesController.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsSourcesController.kt index 355d0ac8c..d7f629c59 100755 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsSourcesController.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsSourcesController.kt @@ -1,8 +1,8 @@ package eu.kanade.tachiyomi.ui.setting import android.graphics.drawable.Drawable -import android.support.v7.preference.PreferenceGroup -import android.support.v7.preference.PreferenceScreen +import androidx.preference.PreferenceGroup +import androidx.preference.PreferenceScreen import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.data.preference.getOrDefault import eu.kanade.tachiyomi.source.SourceManager diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsTrackingController.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsTrackingController.kt index 55bc47059..7bc08c326 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsTrackingController.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsTrackingController.kt @@ -2,14 +2,14 @@ package eu.kanade.tachiyomi.ui.setting import android.app.Activity import android.content.Intent -import android.support.customtabs.CustomTabsIntent -import android.support.v7.preference.PreferenceScreen +import androidx.browser.customtabs.CustomTabsIntent +import androidx.preference.PreferenceScreen import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.data.track.TrackManager import eu.kanade.tachiyomi.data.track.TrackService import eu.kanade.tachiyomi.data.track.anilist.AnilistApi -import eu.kanade.tachiyomi.data.track.shikimori.ShikimoriApi import eu.kanade.tachiyomi.data.track.bangumi.BangumiApi +import eu.kanade.tachiyomi.data.track.shikimori.ShikimoriApi import eu.kanade.tachiyomi.util.getResourceColor import eu.kanade.tachiyomi.widget.preference.LoginPreference import eu.kanade.tachiyomi.widget.preference.TrackLoginDialog @@ -45,7 +45,7 @@ class SettingsTrackingController : SettingsController(), .setToolbarColor(context.getResourceColor(R.attr.colorPrimary)) .build() tabsIntent.intent.addFlags(Intent.FLAG_ACTIVITY_NO_HISTORY) - tabsIntent.launchUrl(activity, AnilistApi.authUrl()) + tabsIntent.launchUrl(activity!!, AnilistApi.authUrl()) } } trackPreference(trackManager.kitsu) { @@ -61,7 +61,7 @@ class SettingsTrackingController : SettingsController(), .setToolbarColor(context.getResourceColor(R.attr.colorPrimary)) .build() tabsIntent.intent.addFlags(Intent.FLAG_ACTIVITY_NO_HISTORY) - tabsIntent.launchUrl(activity, ShikimoriApi.authUrl()) + tabsIntent.launchUrl(activity!!, ShikimoriApi.authUrl()) } } trackPreference(trackManager.bangumi) { @@ -70,7 +70,7 @@ class SettingsTrackingController : SettingsController(), .setToolbarColor(context.getResourceColor(R.attr.colorPrimary)) .build() tabsIntent.intent.addFlags(Intent.FLAG_ACTIVITY_NO_HISTORY) - tabsIntent.launchUrl(activity, BangumiApi.authUrl()) + tabsIntent.launchUrl(activity!!, BangumiApi.authUrl()) } } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/setting/ShikomoriLoginActivity.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/setting/ShikomoriLoginActivity.kt index d369896ed..208001a29 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/setting/ShikomoriLoginActivity.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/setting/ShikomoriLoginActivity.kt @@ -2,11 +2,11 @@ package eu.kanade.tachiyomi.ui.setting import android.content.Intent import android.os.Bundle -import android.support.v7.app.AppCompatActivity import android.view.Gravity.CENTER import android.view.ViewGroup.LayoutParams.WRAP_CONTENT import android.widget.FrameLayout import android.widget.ProgressBar +import androidx.appcompat.app.AppCompatActivity import eu.kanade.tachiyomi.data.track.TrackManager import eu.kanade.tachiyomi.ui.main.MainActivity import rx.android.schedulers.AndroidSchedulers diff --git a/app/src/main/java/eu/kanade/tachiyomi/util/ContextExtensions.kt b/app/src/main/java/eu/kanade/tachiyomi/util/ContextExtensions.kt index b58d4decc..426c33070 100755 --- a/app/src/main/java/eu/kanade/tachiyomi/util/ContextExtensions.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/util/ContextExtensions.kt @@ -9,20 +9,19 @@ import android.content.Context.VIBRATOR_SERVICE import android.content.pm.PackageManager import android.content.res.Resources import android.net.ConnectivityManager +import android.net.Uri import android.net.wifi.WifiManager import android.os.Build -import android.net.Uri import android.os.PowerManager import android.os.VibrationEffect import android.os.Vibrator -import android.support.annotation.AttrRes -import android.support.annotation.RequiresApi -import android.support.annotation.StringRes -import android.support.customtabs.CustomTabsIntent -import android.support.v4.app.NotificationCompat -import android.support.v4.content.ContextCompat -import android.support.v4.content.LocalBroadcastManager import android.widget.Toast +import androidx.annotation.AttrRes +import androidx.annotation.RequiresApi +import androidx.annotation.StringRes +import androidx.browser.customtabs.CustomTabsIntent +import androidx.core.app.NotificationCompat +import androidx.core.content.ContextCompat import com.nononsenseapps.filepicker.FilePickerActivity import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.widget.CustomLayoutPickerActivity @@ -153,7 +152,7 @@ val Context.jobScheduler: JobScheduler * @param intent intent that contains broadcast information */ fun Context.sendLocalBroadcast(intent: Intent) { - LocalBroadcastManager.getInstance(this).sendBroadcast(intent) + androidx.localbroadcastmanager.content.LocalBroadcastManager.getInstance(this).sendBroadcast(intent) } /** @@ -162,7 +161,7 @@ fun Context.sendLocalBroadcast(intent: Intent) { * @param intent intent that contains broadcast information */ fun Context.sendLocalBroadcastSync(intent: Intent) { - LocalBroadcastManager.getInstance(this).sendBroadcastSync(intent) + androidx.localbroadcastmanager.content.LocalBroadcastManager.getInstance(this).sendBroadcastSync(intent) } /** @@ -171,7 +170,7 @@ fun Context.sendLocalBroadcastSync(intent: Intent) { * @param receiver receiver that gets registered. */ fun Context.registerLocalReceiver(receiver: BroadcastReceiver, filter: IntentFilter) { - LocalBroadcastManager.getInstance(this).registerReceiver(receiver, filter) + androidx.localbroadcastmanager.content.LocalBroadcastManager.getInstance(this).registerReceiver(receiver, filter) } /** @@ -180,7 +179,7 @@ fun Context.registerLocalReceiver(receiver: BroadcastReceiver, filter: IntentFil * @param receiver receiver that gets unregistered. */ fun Context.unregisterLocalReceiver(receiver: BroadcastReceiver) { - LocalBroadcastManager.getInstance(this).unregisterReceiver(receiver) + androidx.localbroadcastmanager.content.LocalBroadcastManager.getInstance(this).unregisterReceiver(receiver) } /** diff --git a/app/src/main/java/eu/kanade/tachiyomi/util/DiskUtil.kt b/app/src/main/java/eu/kanade/tachiyomi/util/DiskUtil.kt index c735c0b8d..4f0375e42 100755 --- a/app/src/main/java/eu/kanade/tachiyomi/util/DiskUtil.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/util/DiskUtil.kt @@ -5,8 +5,8 @@ import android.content.Intent import android.net.Uri import android.os.Build import android.os.Environment -import android.support.v4.content.ContextCompat -import android.support.v4.os.EnvironmentCompat +import androidx.core.content.ContextCompat +import androidx.core.os.EnvironmentCompat import com.hippo.unifile.UniFile import java.io.File diff --git a/app/src/main/java/eu/kanade/tachiyomi/util/FileExtensions.kt b/app/src/main/java/eu/kanade/tachiyomi/util/FileExtensions.kt index 3ad0eeca0..98fe9bfbb 100755 --- a/app/src/main/java/eu/kanade/tachiyomi/util/FileExtensions.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/util/FileExtensions.kt @@ -3,7 +3,7 @@ package eu.kanade.tachiyomi.util import android.content.Context import android.net.Uri import android.os.Build -import android.support.v4.content.FileProvider +import androidx.core.content.FileProvider import eu.kanade.tachiyomi.BuildConfig import java.io.File diff --git a/app/src/main/java/eu/kanade/tachiyomi/util/ImageViewExtensions.kt b/app/src/main/java/eu/kanade/tachiyomi/util/ImageViewExtensions.kt index 0419476da..4c9d5124c 100755 --- a/app/src/main/java/eu/kanade/tachiyomi/util/ImageViewExtensions.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/util/ImageViewExtensions.kt @@ -1,8 +1,8 @@ package eu.kanade.tachiyomi.util -import android.support.annotation.DrawableRes -import android.support.graphics.drawable.VectorDrawableCompat import android.widget.ImageView +import androidx.annotation.DrawableRes +import androidx.vectordrawable.graphics.drawable.VectorDrawableCompat /** * Set a vector on a [ImageView]. diff --git a/app/src/main/java/eu/kanade/tachiyomi/util/JsoupExtensions.kt b/app/src/main/java/eu/kanade/tachiyomi/util/JsoupExtensions.kt index 30471b37c..556f84e82 100755 --- a/app/src/main/java/eu/kanade/tachiyomi/util/JsoupExtensions.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/util/JsoupExtensions.kt @@ -22,5 +22,5 @@ fun Element.attrOrText(css: String): String { * @param html the body of the response. Use only if the body was read before calling this method. */ fun Response.asJsoup(html: String? = null): Document { - return Jsoup.parse(html ?: body()!!.string(), request().url().toString()) + return Jsoup.parse(html ?: body!!.string(), request.url.toString()) } \ No newline at end of file diff --git a/app/src/main/java/eu/kanade/tachiyomi/util/OkioExtensions.kt b/app/src/main/java/eu/kanade/tachiyomi/util/OkioExtensions.kt index c4fbf2c00..7556f18df 100755 --- a/app/src/main/java/eu/kanade/tachiyomi/util/OkioExtensions.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/util/OkioExtensions.kt @@ -1,7 +1,8 @@ package eu.kanade.tachiyomi.util import okio.BufferedSource -import okio.Okio +import okio.buffer +import okio.sink import java.io.File import java.io.OutputStream @@ -31,7 +32,7 @@ fun BufferedSource.saveTo(file: File) { */ fun BufferedSource.saveTo(stream: OutputStream) { use { input -> - Okio.buffer(Okio.sink(stream)).use { + stream.sink().buffer().use { it.writeAll(input) it.flush() } diff --git a/app/src/main/java/eu/kanade/tachiyomi/util/ViewExtensions.kt b/app/src/main/java/eu/kanade/tachiyomi/util/ViewExtensions.kt index af9b041e2..4f03ab40c 100755 --- a/app/src/main/java/eu/kanade/tachiyomi/util/ViewExtensions.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/util/ViewExtensions.kt @@ -5,11 +5,11 @@ package eu.kanade.tachiyomi.util import android.graphics.Color import android.graphics.Point import android.graphics.Typeface -import android.support.design.widget.Snackbar import android.view.View import android.widget.TextView import com.amulyakhare.textdrawable.TextDrawable import com.amulyakhare.textdrawable.util.ColorGenerator +import com.google.android.material.snackbar.Snackbar /** * Returns coordinates of view. @@ -28,7 +28,7 @@ fun View.getCoordinates() = Point((left + right) / 2, (top + bottom) / 2) */ inline fun View.snack(message: String, length: Int = Snackbar.LENGTH_LONG, f: Snackbar.() -> Unit): Snackbar { val snack = Snackbar.make(this, message, length) - val textView: TextView = snack.view.findViewById(android.support.design.R.id.snackbar_text) + val textView: TextView = snack.view.findViewById(com.google.android.material.R.id.snackbar_text) textView.setTextColor(Color.WHITE) snack.f() snack.show() diff --git a/app/src/main/java/eu/kanade/tachiyomi/util/ViewGroupExtensions.kt b/app/src/main/java/eu/kanade/tachiyomi/util/ViewGroupExtensions.kt index 21ccf5ae3..553900943 100755 --- a/app/src/main/java/eu/kanade/tachiyomi/util/ViewGroupExtensions.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/util/ViewGroupExtensions.kt @@ -1,9 +1,9 @@ package eu.kanade.tachiyomi.util -import android.support.annotation.LayoutRes import android.view.LayoutInflater import android.view.View import android.view.ViewGroup +import androidx.annotation.LayoutRes /** * Extension method to inflate a view directly from its parent. @@ -12,4 +12,4 @@ import android.view.ViewGroup */ fun ViewGroup.inflate(@LayoutRes layout: Int, attachToRoot: Boolean = false): View { return LayoutInflater.from(context).inflate(layout, this, attachToRoot) -} +} \ No newline at end of file diff --git a/app/src/main/java/eu/kanade/tachiyomi/widget/AutofitRecyclerView.kt b/app/src/main/java/eu/kanade/tachiyomi/widget/AutofitRecyclerView.kt index 62cc4b04c..f619e13e5 100755 --- a/app/src/main/java/eu/kanade/tachiyomi/widget/AutofitRecyclerView.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/widget/AutofitRecyclerView.kt @@ -1,14 +1,12 @@ package eu.kanade.tachiyomi.widget import android.content.Context -import android.support.v7.widget.GridLayoutManager -import android.support.v7.widget.RecyclerView import android.util.AttributeSet class AutofitRecyclerView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null) : - RecyclerView(context, attrs) { + androidx.recyclerview.widget.RecyclerView(context, attrs) { - private val manager = GridLayoutManager(context, 1) + private val manager = androidx.recyclerview.widget.GridLayoutManager(context, 1) private var columnWidth = -1 diff --git a/app/src/main/java/eu/kanade/tachiyomi/widget/CustomLayoutPicker.kt b/app/src/main/java/eu/kanade/tachiyomi/widget/CustomLayoutPicker.kt index 823e1ce43..96befcf4d 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/widget/CustomLayoutPicker.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/widget/CustomLayoutPicker.kt @@ -1,6 +1,5 @@ package eu.kanade.tachiyomi.widget -import android.support.v7.widget.RecyclerView import android.view.ViewGroup import com.nononsenseapps.filepicker.AbstractFilePickerFragment import com.nononsenseapps.filepicker.FilePickerActivity @@ -21,7 +20,7 @@ class CustomLayoutPickerActivity : FilePickerActivity() { } class CustomLayoutFilePickerFragment : FilePickerFragment() { - override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): androidx.recyclerview.widget.RecyclerView.ViewHolder { when (viewType) { LogicHandler.VIEWTYPE_DIR -> { val view = parent.inflate(R.layout.common_listitem_dir) diff --git a/app/src/main/java/eu/kanade/tachiyomi/widget/DialogCheckboxView.kt b/app/src/main/java/eu/kanade/tachiyomi/widget/DialogCheckboxView.kt index 327bb20aa..454885007 100755 --- a/app/src/main/java/eu/kanade/tachiyomi/widget/DialogCheckboxView.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/widget/DialogCheckboxView.kt @@ -1,9 +1,9 @@ package eu.kanade.tachiyomi.widget import android.content.Context -import android.support.annotation.StringRes import android.util.AttributeSet import android.widget.LinearLayout +import androidx.annotation.StringRes import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.util.inflate import kotlinx.android.synthetic.main.common_dialog_with_checkbox.view.* diff --git a/app/src/main/java/eu/kanade/tachiyomi/widget/DrawerSwipeCloseListener.kt b/app/src/main/java/eu/kanade/tachiyomi/widget/DrawerSwipeCloseListener.kt index 078bceff4..1c5c6a934 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/widget/DrawerSwipeCloseListener.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/widget/DrawerSwipeCloseListener.kt @@ -1,23 +1,23 @@ package eu.kanade.tachiyomi.widget -import android.support.v4.widget.DrawerLayout import android.view.View import android.view.ViewGroup +import androidx.drawerlayout.widget.DrawerLayout class DrawerSwipeCloseListener( - private val drawer: DrawerLayout, + private val drawer: androidx.drawerlayout.widget.DrawerLayout, private val navigationView: ViewGroup -) : DrawerLayout.SimpleDrawerListener() { +) : androidx.drawerlayout.widget.DrawerLayout.SimpleDrawerListener() { override fun onDrawerOpened(drawerView: View) { if (drawerView == navigationView) { - drawer.setDrawerLockMode(DrawerLayout.LOCK_MODE_UNLOCKED, drawerView) + drawer.setDrawerLockMode(androidx.drawerlayout.widget.DrawerLayout.LOCK_MODE_UNLOCKED, drawerView) } } override fun onDrawerClosed(drawerView: View) { if (drawerView == navigationView) { - drawer.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED, drawerView) + drawer.setDrawerLockMode(androidx.drawerlayout.widget.DrawerLayout.LOCK_MODE_LOCKED_CLOSED, drawerView) } } } \ No newline at end of file diff --git a/app/src/main/java/eu/kanade/tachiyomi/widget/ElevationAppBarLayout.kt b/app/src/main/java/eu/kanade/tachiyomi/widget/ElevationAppBarLayout.kt index 20c33a4fc..871a8f7da 100755 --- a/app/src/main/java/eu/kanade/tachiyomi/widget/ElevationAppBarLayout.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/widget/ElevationAppBarLayout.kt @@ -4,9 +4,9 @@ import android.animation.ObjectAnimator import android.animation.StateListAnimator import android.content.Context import android.os.Build -import android.support.design.R -import android.support.design.widget.AppBarLayout import android.util.AttributeSet +import com.google.android.material.R +import com.google.android.material.appbar.AppBarLayout class ElevationAppBarLayout @JvmOverloads constructor( context: Context, diff --git a/app/src/main/java/eu/kanade/tachiyomi/widget/ExtendedNavigationView.kt b/app/src/main/java/eu/kanade/tachiyomi/widget/ExtendedNavigationView.kt index d39e402ce..2a5e095bc 100755 --- a/app/src/main/java/eu/kanade/tachiyomi/widget/ExtendedNavigationView.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/widget/ExtendedNavigationView.kt @@ -1,269 +1,268 @@ -package eu.kanade.tachiyomi.widget - -import android.content.Context -import android.graphics.drawable.Drawable -import android.support.annotation.CallSuper -import android.support.graphics.drawable.VectorDrawableCompat -import android.support.v4.content.ContextCompat -import android.support.v7.widget.RecyclerView -import android.util.AttributeSet -import android.view.View -import android.view.ViewGroup -import eu.kanade.tachiyomi.R -import eu.kanade.tachiyomi.util.getResourceColor - -/** - * An alternative implementation of [android.support.design.widget.NavigationView], without menu - * inflation and allowing customizable items (multiple selections, custom views, etc). - */ -open class ExtendedNavigationView @JvmOverloads constructor( - context: Context, - attrs: AttributeSet? = null, - defStyleAttr: Int = 0) - : SimpleNavigationView(context, attrs, defStyleAttr) { - - /** - * Every item of the nav view. Generic items must belong to this list, custom items could be - * implemented by an abstract class. If more customization is needed in the future, this can be - * changed to an interface instead of sealed class. - */ - sealed class Item { - /** - * A view separator. - */ - class Separator(val paddingTop: Int = 0, val paddingBottom: Int = 0) : Item() - - /** - * A header with a title. - */ - class Header(val resTitle: Int) : Item() - - /** - * A checkbox. - */ - open class Checkbox(val resTitle: Int, var checked: Boolean = false) : Item() - - /** - * A checkbox belonging to a group. The group must handle selections and restrictions. - */ - class CheckboxGroup(resTitle: Int, override val group: Group, checked: Boolean = false) - : Checkbox(resTitle, checked), GroupedItem - - /** - * A radio belonging to a group (a sole radio makes no sense). The group must handle - * selections and restrictions. - */ - class Radio(val resTitle: Int, override val group: Group, var checked: Boolean = false) - : Item(), GroupedItem - - /** - * An item with which needs more than two states (selected/deselected). - */ - abstract class MultiState(val resTitle: Int, var state: Int = 0) : Item() { - - /** - * Returns the drawable associated to every possible each state. - */ - abstract fun getStateDrawable(context: Context): Drawable? - - /** - * Creates a vector tinted with the accent color. - * - * @param context any context. - * @param resId the vector resource to load and tint - */ - fun tintVector(context: Context, resId: Int): Drawable { - return VectorDrawableCompat.create(context.resources, resId, context.theme)!!.apply { - setTint(context.getResourceColor(R.attr.colorAccent)) - } - } - /** - * Creates a vector tinted with the accent color. - * - * @param context any context. - * @param resId the vector resource to load and tint - */ - fun tintVector(context: Context, resId: Int, colorId: Int): Drawable { - return VectorDrawableCompat.create(context.resources, resId, context.theme)!!.apply { - setTint(context.getResourceColor(colorId)) - } - } - } - - /** - * An item with which needs more than two states (selected/deselected) belonging to a group. - * The group must handle selections and restrictions. - */ - abstract class MultiStateGroup(resTitle: Int, override val group: Group, state: Int = 0) - : MultiState(resTitle, state), GroupedItem - - /** - * A multistate item for sorting lists (unselected, ascending, descending). - */ - class MultiSort(resId: Int, group: Group) : MultiStateGroup(resId, group) { - - companion object { - const val SORT_NONE = 0 - const val SORT_ASC = 1 - const val SORT_DESC = 2 - } - - override fun getStateDrawable(context: Context): Drawable? { - return when (state) { - SORT_ASC -> tintVector(context, R.drawable.ic_arrow_up_white_32dp) - SORT_DESC -> tintVector(context, R.drawable.ic_arrow_down_white_32dp) - SORT_NONE -> ContextCompat.getDrawable(context, R.drawable.empty_drawable_32dp) - else -> null - } - } - - } - - class TriStateGroup(resId: Int, group: Group) : MultiStateGroup(resId, group) { - - companion object { - const val STATE_IGNORE = 0 - const val STATE_INCLUDE = 1 - const val STATE_EXCLUDE = 2 - } - - override fun getStateDrawable(context: Context): Drawable? { - return when(state) { - STATE_INCLUDE -> tintVector(context, R.drawable.ic_check_box_24dp) - STATE_EXCLUDE -> tintVector(context, R.drawable.ic_check_box_x_24dp, - android.R.attr.textColorSecondary) - else -> tintVector(context, R.drawable.ic_check_box_outline_blank_24dp, - android.R.attr.textColorSecondary) - } - } - } - } - - /** - * Interface for an item belonging to a group. - */ - interface GroupedItem { - val group: Group - } - - /** - * A group containing a list of items. - */ - interface Group { - - /** - * An optional header for the group, typically a [Item.Header]. - */ - val header: Item? - - /** - * An optional footer for the group, typically a [Item.Separator]. - */ - val footer: Item? - - /** - * The items of the group, excluding header and footer. - */ - val items: List - - /** - * Creates all the elements of this group. Implementations can override this method for more - * customization. - */ - fun createItems() = (mutableListOf() + header + items + footer).filterNotNull() - - /** - * Called after creating the list of items. Implementations should load the current values - * into the models. - */ - fun initModels() - - /** - * Called when an item of this group is clicked. The group is responsible for all the - * selections of its items. - */ - fun onItemClicked(item: Item) - - } - - /** - * Base adapter for the navigation view. It knows how to create and render every subclass of - * [Item]. - */ - abstract inner class Adapter(private val items: List) : RecyclerView.Adapter() { - - private val onClick = View.OnClickListener { - val pos = recycler.getChildAdapterPosition(it) - val item = items[pos] - onItemClicked(item) - } - - fun notifyItemChanged(item: Item) { - val pos = items.indexOf(item) - if (pos != -1) notifyItemChanged(pos) - } - - override fun getItemCount(): Int { - return items.size - } - - @CallSuper - override fun getItemViewType(position: Int): Int { - val item = items[position] - return when (item) { - is Item.Header -> VIEW_TYPE_HEADER - is Item.Separator -> VIEW_TYPE_SEPARATOR - is Item.Radio -> VIEW_TYPE_RADIO - is Item.Checkbox -> VIEW_TYPE_CHECKBOX - is Item.MultiState -> VIEW_TYPE_MULTISTATE - } - } - - @CallSuper - override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): Holder { - return when (viewType) { - VIEW_TYPE_HEADER -> HeaderHolder(parent) - VIEW_TYPE_SEPARATOR -> SeparatorHolder(parent) - VIEW_TYPE_RADIO -> RadioHolder(parent, onClick) - VIEW_TYPE_CHECKBOX -> CheckboxHolder(parent, onClick) - VIEW_TYPE_MULTISTATE -> MultiStateHolder(parent, onClick) - else -> throw Exception("Unknown view type") - } - } - - @CallSuper - override fun onBindViewHolder(holder: Holder, position: Int) { - when (holder) { - is HeaderHolder -> { - val item = items[position] as Item.Header - holder.title.setText(item.resTitle) - } - is SeparatorHolder -> { - val view = holder.itemView - val item = items[position] as Item.Separator - view.setPadding(0, item.paddingTop, 0, item.paddingBottom) - } - is RadioHolder -> { - val item = items[position] as Item.Radio - holder.radio.setText(item.resTitle) - holder.radio.isChecked = item.checked - } - is CheckboxHolder -> { - val item = items[position] as Item.CheckboxGroup - holder.check.setText(item.resTitle) - holder.check.isChecked = item.checked - } - is MultiStateHolder -> { - val item = items[position] as Item.MultiStateGroup - val drawable = item.getStateDrawable(context) - holder.text.setText(item.resTitle) - holder.text.setCompoundDrawablesWithIntrinsicBounds(drawable, null, null, null) - } - } - } - - abstract fun onItemClicked(item: Item) - - } - -} +package eu.kanade.tachiyomi.widget + +import android.content.Context +import android.graphics.drawable.Drawable +import android.util.AttributeSet +import android.view.View +import android.view.ViewGroup +import androidx.annotation.CallSuper +import androidx.core.content.ContextCompat +import androidx.vectordrawable.graphics.drawable.VectorDrawableCompat +import eu.kanade.tachiyomi.R +import eu.kanade.tachiyomi.util.getResourceColor + +/** + * An alternative implementation of [android.support.design.widget.NavigationView], without menu + * inflation and allowing customizable items (multiple selections, custom views, etc). + */ +open class ExtendedNavigationView @JvmOverloads constructor( + context: Context, + attrs: AttributeSet? = null, + defStyleAttr: Int = 0) + : SimpleNavigationView(context, attrs, defStyleAttr) { + + /** + * Every item of the nav view. Generic items must belong to this list, custom items could be + * implemented by an abstract class. If more customization is needed in the future, this can be + * changed to an interface instead of sealed class. + */ + sealed class Item { + /** + * A view separator. + */ + class Separator(val paddingTop: Int = 0, val paddingBottom: Int = 0) : Item() + + /** + * A header with a title. + */ + class Header(val resTitle: Int) : Item() + + /** + * A checkbox. + */ + open class Checkbox(val resTitle: Int, var checked: Boolean = false) : Item() + + /** + * A checkbox belonging to a group. The group must handle selections and restrictions. + */ + class CheckboxGroup(resTitle: Int, override val group: Group, checked: Boolean = false) + : Checkbox(resTitle, checked), GroupedItem + + /** + * A radio belonging to a group (a sole radio makes no sense). The group must handle + * selections and restrictions. + */ + class Radio(val resTitle: Int, override val group: Group, var checked: Boolean = false) + : Item(), GroupedItem + + /** + * An item with which needs more than two states (selected/deselected). + */ + abstract class MultiState(val resTitle: Int, var state: Int = 0) : Item() { + + /** + * Returns the drawable associated to every possible each state. + */ + abstract fun getStateDrawable(context: Context): Drawable? + + /** + * Creates a vector tinted with the accent color. + * + * @param context any context. + * @param resId the vector resource to load and tint + */ + fun tintVector(context: Context, resId: Int): Drawable { + return VectorDrawableCompat.create(context.resources, resId, context.theme)!!.apply { + setTint(context.getResourceColor(R.attr.colorAccent)) + } + } + /** + * Creates a vector tinted with the accent color. + * + * @param context any context. + * @param resId the vector resource to load and tint + */ + fun tintVector(context: Context, resId: Int, colorId: Int): Drawable { + return VectorDrawableCompat.create(context.resources, resId, context.theme)!!.apply { + setTint(context.getResourceColor(colorId)) + } + } + } + + /** + * An item with which needs more than two states (selected/deselected) belonging to a group. + * The group must handle selections and restrictions. + */ + abstract class MultiStateGroup(resTitle: Int, override val group: Group, state: Int = 0) + : MultiState(resTitle, state), GroupedItem + + /** + * A multistate item for sorting lists (unselected, ascending, descending). + */ + class MultiSort(resId: Int, group: Group) : MultiStateGroup(resId, group) { + + companion object { + const val SORT_NONE = 0 + const val SORT_ASC = 1 + const val SORT_DESC = 2 + } + + override fun getStateDrawable(context: Context): Drawable? { + return when (state) { + SORT_ASC -> tintVector(context, R.drawable.ic_arrow_up_white_32dp) + SORT_DESC -> tintVector(context, R.drawable.ic_arrow_down_white_32dp) + SORT_NONE -> ContextCompat.getDrawable(context, R.drawable.empty_drawable_32dp) + else -> null + } + } + + } + + class TriStateGroup(resId: Int, group: Group) : MultiStateGroup(resId, group) { + + companion object { + const val STATE_IGNORE = 0 + const val STATE_INCLUDE = 1 + const val STATE_EXCLUDE = 2 + } + + override fun getStateDrawable(context: Context): Drawable? { + return when(state) { + STATE_INCLUDE -> tintVector(context, R.drawable.ic_check_box_24dp) + STATE_EXCLUDE -> tintVector(context, R.drawable.ic_check_box_x_24dp, + android.R.attr.textColorSecondary) + else -> tintVector(context, R.drawable.ic_check_box_outline_blank_24dp, + android.R.attr.textColorSecondary) + } + } + } + } + + /** + * Interface for an item belonging to a group. + */ + interface GroupedItem { + val group: Group + } + + /** + * A group containing a list of items. + */ + interface Group { + + /** + * An optional header for the group, typically a [Item.Header]. + */ + val header: Item? + + /** + * An optional footer for the group, typically a [Item.Separator]. + */ + val footer: Item? + + /** + * The items of the group, excluding header and footer. + */ + val items: List + + /** + * Creates all the elements of this group. Implementations can override this method for more + * customization. + */ + fun createItems() = (mutableListOf() + header + items + footer).filterNotNull() + + /** + * Called after creating the list of items. Implementations should load the current values + * into the models. + */ + fun initModels() + + /** + * Called when an item of this group is clicked. The group is responsible for all the + * selections of its items. + */ + fun onItemClicked(item: Item) + + } + + /** + * Base adapter for the navigation view. It knows how to create and render every subclass of + * [Item]. + */ + abstract inner class Adapter(private val items: List) : androidx.recyclerview.widget.RecyclerView.Adapter() { + + private val onClick = View.OnClickListener { + val pos = recycler.getChildAdapterPosition(it) + val item = items[pos] + onItemClicked(item) + } + + fun notifyItemChanged(item: Item) { + val pos = items.indexOf(item) + if (pos != -1) notifyItemChanged(pos) + } + + override fun getItemCount(): Int { + return items.size + } + + @CallSuper + override fun getItemViewType(position: Int): Int { + val item = items[position] + return when (item) { + is Item.Header -> VIEW_TYPE_HEADER + is Item.Separator -> VIEW_TYPE_SEPARATOR + is Item.Radio -> VIEW_TYPE_RADIO + is Item.Checkbox -> VIEW_TYPE_CHECKBOX + is Item.MultiState -> VIEW_TYPE_MULTISTATE + } + } + + @CallSuper + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): Holder { + return when (viewType) { + VIEW_TYPE_HEADER -> HeaderHolder(parent) + VIEW_TYPE_SEPARATOR -> SeparatorHolder(parent) + VIEW_TYPE_RADIO -> RadioHolder(parent, onClick) + VIEW_TYPE_CHECKBOX -> CheckboxHolder(parent, onClick) + VIEW_TYPE_MULTISTATE -> MultiStateHolder(parent, onClick) + else -> throw Exception("Unknown view type") + } + } + + @CallSuper + override fun onBindViewHolder(holder: Holder, position: Int) { + when (holder) { + is HeaderHolder -> { + val item = items[position] as Item.Header + holder.title.setText(item.resTitle) + } + is SeparatorHolder -> { + val view = holder.itemView + val item = items[position] as Item.Separator + view.setPadding(0, item.paddingTop, 0, item.paddingBottom) + } + is RadioHolder -> { + val item = items[position] as Item.Radio + holder.radio.setText(item.resTitle) + holder.radio.isChecked = item.checked + } + is CheckboxHolder -> { + val item = items[position] as Item.CheckboxGroup + holder.check.setText(item.resTitle) + holder.check.isChecked = item.checked + } + is MultiStateHolder -> { + val item = items[position] as Item.MultiStateGroup + val drawable = item.getStateDrawable(context) + holder.text.setText(item.resTitle) + holder.text.setCompoundDrawablesWithIntrinsicBounds(drawable, null, null, null) + } + } + } + + abstract fun onItemClicked(item: Item) + + } + +} diff --git a/app/src/main/java/eu/kanade/tachiyomi/widget/FABAnimationBase.kt b/app/src/main/java/eu/kanade/tachiyomi/widget/FABAnimationBase.kt index eaa4201e1..a66d8a662 100755 --- a/app/src/main/java/eu/kanade/tachiyomi/widget/FABAnimationBase.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/widget/FABAnimationBase.kt @@ -1,22 +1,21 @@ package eu.kanade.tachiyomi.widget -import android.support.design.widget.CoordinatorLayout -import android.support.design.widget.FloatingActionButton -import android.support.v4.view.ViewCompat import android.view.View +import androidx.core.view.ViewCompat +import com.google.android.material.floatingactionbutton.FloatingActionButton abstract class FABAnimationBase : FloatingActionButton.Behavior() { var isAnimatingOut = false - override fun onStartNestedScroll(coordinatorLayout: CoordinatorLayout, child: FloatingActionButton, + override fun onStartNestedScroll(coordinatorLayout: androidx.coordinatorlayout.widget.CoordinatorLayout, child: FloatingActionButton, directTargetChild: View, target: View, axes: Int, type: Int): Boolean { // Ensure we react to vertical scrolling return axes == ViewCompat.SCROLL_AXIS_VERTICAL || super.onStartNestedScroll(coordinatorLayout, child, directTargetChild, target, axes, type) } - override fun onNestedScroll(coordinatorLayout: CoordinatorLayout, child: FloatingActionButton, + override fun onNestedScroll(coordinatorLayout: androidx.coordinatorlayout.widget.CoordinatorLayout, child: FloatingActionButton, target: View, dxConsumed: Int, dyConsumed: Int, dxUnconsumed: Int, dyUnconsumed: Int, type: Int) { super.onNestedScroll(coordinatorLayout, child, target, dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed, type) diff --git a/app/src/main/java/eu/kanade/tachiyomi/widget/FABAnimationUpDown.kt b/app/src/main/java/eu/kanade/tachiyomi/widget/FABAnimationUpDown.kt index e1c4245d2..c52d6ad28 100755 --- a/app/src/main/java/eu/kanade/tachiyomi/widget/FABAnimationUpDown.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/widget/FABAnimationUpDown.kt @@ -1,12 +1,12 @@ package eu.kanade.tachiyomi.widget import android.content.Context -import android.support.design.widget.FloatingActionButton -import android.support.v4.view.animation.FastOutSlowInInterpolator import android.util.AttributeSet import android.view.View import android.view.animation.Animation import android.view.animation.AnimationUtils +import androidx.interpolator.view.animation.FastOutSlowInInterpolator +import com.google.android.material.floatingactionbutton.FloatingActionButton import eu.kanade.tachiyomi.R @Suppress("unused", "UNUSED_PARAMETER") diff --git a/app/src/main/java/eu/kanade/tachiyomi/widget/PreCachingLayoutManager.kt b/app/src/main/java/eu/kanade/tachiyomi/widget/PreCachingLayoutManager.kt index 82c616098..bf0121491 100755 --- a/app/src/main/java/eu/kanade/tachiyomi/widget/PreCachingLayoutManager.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/widget/PreCachingLayoutManager.kt @@ -1,10 +1,8 @@ package eu.kanade.tachiyomi.widget import android.content.Context -import android.support.v7.widget.LinearLayoutManager -import android.support.v7.widget.RecyclerView -class PreCachingLayoutManager(context: Context) : LinearLayoutManager(context) { +class PreCachingLayoutManager(context: Context) : androidx.recyclerview.widget.LinearLayoutManager(context) { init { isItemPrefetchEnabled = false @@ -16,7 +14,7 @@ class PreCachingLayoutManager(context: Context) : LinearLayoutManager(context) { var extraLayoutSpace = 0 - override fun getExtraLayoutSpace(state: RecyclerView.State): Int { + override fun getExtraLayoutSpace(state: androidx.recyclerview.widget.RecyclerView.State): Int { if (extraLayoutSpace > 0) { return extraLayoutSpace } diff --git a/app/src/main/java/eu/kanade/tachiyomi/widget/SimpleNavigationView.kt b/app/src/main/java/eu/kanade/tachiyomi/widget/SimpleNavigationView.kt index 31fcec901..bec8d36ad 100755 --- a/app/src/main/java/eu/kanade/tachiyomi/widget/SimpleNavigationView.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/widget/SimpleNavigationView.kt @@ -2,22 +2,20 @@ package eu.kanade.tachiyomi.widget import android.annotation.SuppressLint import android.content.Context -import android.support.design.R -import android.support.design.internal.ScrimInsetsFrameLayout -import android.support.design.widget.TextInputLayout -import android.support.v4.view.ViewCompat -import android.support.v7.widget.LinearLayoutManager -import android.support.v7.widget.RecyclerView -import android.support.v7.widget.TintTypedArray import android.util.AttributeSet import android.view.View import android.view.ViewGroup import android.widget.* +import androidx.appcompat.widget.TintTypedArray +import androidx.core.view.ViewCompat +import com.google.android.material.R +import com.google.android.material.internal.ScrimInsetsFrameLayout +import com.google.android.material.textfield.TextInputLayout import eu.kanade.tachiyomi.util.inflate import eu.kanade.tachiyomi.R as TR @Suppress("LeakingThis") -@SuppressLint("PrivateResource") +@SuppressLint("PrivateResource", "RestrictedApi") open class SimpleNavigationView @JvmOverloads constructor( context: Context, attrs: AttributeSet? = null, @@ -32,7 +30,7 @@ open class SimpleNavigationView @JvmOverloads constructor( /** * Recycler view containing all the items. */ - protected val recycler = RecyclerView(context) + protected val recycler = androidx.recyclerview.widget.RecyclerView(context) init { // Custom attributes @@ -56,7 +54,7 @@ open class SimpleNavigationView @JvmOverloads constructor( a.recycle() - recycler.layoutManager = LinearLayoutManager(context) + recycler.layoutManager = androidx.recyclerview.widget.LinearLayoutManager(context) } /** @@ -77,7 +75,7 @@ open class SimpleNavigationView @JvmOverloads constructor( /** * Base view holder. */ - abstract class Holder(view: View) : RecyclerView.ViewHolder(view) + abstract class Holder(view: View) : androidx.recyclerview.widget.RecyclerView.ViewHolder(view) /** * Separator view holder. diff --git a/app/src/main/java/eu/kanade/tachiyomi/widget/StateImageViewTarget.kt b/app/src/main/java/eu/kanade/tachiyomi/widget/StateImageViewTarget.kt index 2917879b9..8570109c9 100755 --- a/app/src/main/java/eu/kanade/tachiyomi/widget/StateImageViewTarget.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/widget/StateImageViewTarget.kt @@ -1,10 +1,10 @@ package eu.kanade.tachiyomi.widget import android.graphics.drawable.Drawable -import android.support.graphics.drawable.VectorDrawableCompat import android.view.View import android.widget.ImageView import android.widget.ImageView.ScaleType +import androidx.vectordrawable.graphics.drawable.VectorDrawableCompat import com.bumptech.glide.request.target.ImageViewTarget import com.bumptech.glide.request.transition.Transition import eu.kanade.tachiyomi.R diff --git a/app/src/main/java/eu/kanade/tachiyomi/widget/ViewPagerAdapter.kt b/app/src/main/java/eu/kanade/tachiyomi/widget/ViewPagerAdapter.kt index 3effd64ec..7274f4032 100755 --- a/app/src/main/java/eu/kanade/tachiyomi/widget/ViewPagerAdapter.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/widget/ViewPagerAdapter.kt @@ -1,10 +1,9 @@ package eu.kanade.tachiyomi.widget -import android.support.v4.view.PagerAdapter import android.view.View import android.view.ViewGroup -abstract class ViewPagerAdapter : PagerAdapter() { +abstract class ViewPagerAdapter : androidx.viewpager.widget.PagerAdapter() { protected abstract fun createView(container: ViewGroup, position: Int): View diff --git a/app/src/main/java/eu/kanade/tachiyomi/widget/preference/IntListPreference.kt b/app/src/main/java/eu/kanade/tachiyomi/widget/preference/IntListPreference.kt index 085cfd004..4d5a9b8c6 100755 --- a/app/src/main/java/eu/kanade/tachiyomi/widget/preference/IntListPreference.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/widget/preference/IntListPreference.kt @@ -1,8 +1,8 @@ package eu.kanade.tachiyomi.widget.preference import android.content.Context -import android.support.v7.preference.ListPreference import android.util.AttributeSet +import androidx.preference.ListPreference class IntListPreference @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null) : ListPreference(context, attrs) { diff --git a/app/src/main/java/eu/kanade/tachiyomi/widget/preference/LoginCheckBoxPreference.kt b/app/src/main/java/eu/kanade/tachiyomi/widget/preference/LoginCheckBoxPreference.kt index ac45bd4af..fb94b211c 100755 --- a/app/src/main/java/eu/kanade/tachiyomi/widget/preference/LoginCheckBoxPreference.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/widget/preference/LoginCheckBoxPreference.kt @@ -2,10 +2,10 @@ package eu.kanade.tachiyomi.widget.preference import android.content.Context import android.graphics.Color -import android.support.v7.preference.CheckBoxPreference -import android.support.v7.preference.PreferenceViewHolder import android.util.AttributeSet import android.view.View +import androidx.preference.CheckBoxPreference +import androidx.preference.PreferenceViewHolder import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.source.online.HttpSource import eu.kanade.tachiyomi.source.online.LoginSource diff --git a/app/src/main/java/eu/kanade/tachiyomi/widget/preference/LoginPreference.kt b/app/src/main/java/eu/kanade/tachiyomi/widget/preference/LoginPreference.kt index adc8f386a..2b448c867 100755 --- a/app/src/main/java/eu/kanade/tachiyomi/widget/preference/LoginPreference.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/widget/preference/LoginPreference.kt @@ -1,9 +1,9 @@ package eu.kanade.tachiyomi.widget.preference import android.content.Context -import android.support.v7.preference.Preference -import android.support.v7.preference.PreferenceViewHolder import android.util.AttributeSet +import androidx.preference.Preference +import androidx.preference.PreferenceViewHolder import eu.kanade.tachiyomi.R import kotlinx.android.synthetic.main.pref_widget_imageview.view.* diff --git a/app/src/main/java/eu/kanade/tachiyomi/widget/preference/SwitchPreferenceCategory.kt b/app/src/main/java/eu/kanade/tachiyomi/widget/preference/SwitchPreferenceCategory.kt index cff0847c1..5bbcb701d 100755 --- a/app/src/main/java/eu/kanade/tachiyomi/widget/preference/SwitchPreferenceCategory.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/widget/preference/SwitchPreferenceCategory.kt @@ -4,14 +4,14 @@ import android.annotation.TargetApi import android.content.Context import android.content.res.TypedArray import android.os.Build.VERSION_CODES.ICE_CREAM_SANDWICH -import android.support.v7.preference.PreferenceCategory -import android.support.v7.preference.PreferenceViewHolder -import android.support.v7.widget.SwitchCompat import android.util.AttributeSet import android.view.View import android.widget.Checkable import android.widget.CompoundButton import android.widget.TextView +import androidx.appcompat.widget.SwitchCompat +import androidx.preference.PreferenceCategory +import androidx.preference.PreferenceViewHolder import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.util.getResourceColor diff --git a/app/src/main/java/exh/debug/SettingsDebugController.kt b/app/src/main/java/exh/debug/SettingsDebugController.kt index 86f02816a..27fe8da5c 100644 --- a/app/src/main/java/exh/debug/SettingsDebugController.kt +++ b/app/src/main/java/exh/debug/SettingsDebugController.kt @@ -2,11 +2,11 @@ package exh.debug import android.annotation.SuppressLint import android.app.Activity -import android.support.v7.preference.PreferenceScreen import android.text.Html import android.util.Log import android.widget.HorizontalScrollView import android.widget.TextView +import androidx.preference.PreferenceScreen import com.afollestad.materialdialogs.MaterialDialog import eu.kanade.tachiyomi.ui.setting.* import kotlin.reflect.KVisibility diff --git a/app/src/main/java/exh/eh/EHentaiUpdateWorker.kt b/app/src/main/java/exh/eh/EHentaiUpdateWorker.kt index e50bfa894..ad98505c8 100644 --- a/app/src/main/java/exh/eh/EHentaiUpdateWorker.kt +++ b/app/src/main/java/exh/eh/EHentaiUpdateWorker.kt @@ -7,7 +7,7 @@ import android.app.job.JobService import android.content.ComponentName import android.content.Context import android.os.Build -import android.support.annotation.RequiresApi +import androidx.annotation.RequiresApi import com.elvishew.xlog.XLog import com.google.gson.Gson import com.kizitonwose.time.days @@ -27,11 +27,14 @@ import exh.EXH_SOURCE_ID import exh.debug.DebugToggles import exh.eh.EHentaiUpdateWorkerConstants.UPDATES_PER_ITERATION import exh.metadata.metadata.EHentaiSearchMetadata -import exh.metadata.metadata.base.* +import exh.metadata.metadata.base.getFlatMetadataForManga +import exh.metadata.metadata.base.insertFlatMetadata import exh.util.await import exh.util.cancellable import kotlinx.coroutines.* -import kotlinx.coroutines.flow.* +import kotlinx.coroutines.flow.asFlow +import kotlinx.coroutines.flow.mapNotNull +import kotlinx.coroutines.flow.toList import rx.schedulers.Schedulers import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.api.get diff --git a/app/src/main/java/exh/eh/MemAutoFlushingLookupTable.kt b/app/src/main/java/exh/eh/MemAutoFlushingLookupTable.kt index dd053ce33..cc1f2904c 100644 --- a/app/src/main/java/exh/eh/MemAutoFlushingLookupTable.kt +++ b/app/src/main/java/exh/eh/MemAutoFlushingLookupTable.kt @@ -1,12 +1,15 @@ package exh.eh -import android.support.v4.util.AtomicFile import android.util.SparseArray +import androidx.core.util.AtomicFile import com.elvishew.xlog.XLog import kotlinx.coroutines.* import kotlinx.coroutines.sync.Mutex import kotlinx.coroutines.sync.withLock -import java.io.* +import java.io.Closeable +import java.io.File +import java.io.FileNotFoundException +import java.io.InputStream import java.nio.ByteBuffer import kotlin.concurrent.thread import kotlin.coroutines.CoroutineContext diff --git a/app/src/main/java/exh/hitomi/HitomiNozomi.kt b/app/src/main/java/exh/hitomi/HitomiNozomi.kt index 1dc068972..80fe9d6ad 100644 --- a/app/src/main/java/exh/hitomi/HitomiNozomi.kt +++ b/app/src/main/java/exh/hitomi/HitomiNozomi.kt @@ -78,7 +78,7 @@ class HitomiNozomi(private val client: OkHttpClient, return client.newCall(rangedGet(url, offset, offset + length - 1)) .asObservable() .map { - it.body()?.bytes() ?: ByteArray(0) + it.body?.bytes() ?: ByteArray(0) } .onErrorReturn { ByteArray(0) } .map { inbuf -> @@ -183,7 +183,7 @@ class HitomiNozomi(private val client: OkHttpClient, return client.newCall(rangedGet(url, address, address + MAX_NODE_SIZE - 1)) .asObservableSuccess() .map { - it.body()?.bytes() ?: ByteArray(0) + it.body?.bytes() ?: ByteArray(0) } .onErrorReturn { ByteArray(0) } .map { nodedata -> @@ -204,7 +204,7 @@ class HitomiNozomi(private val client: OkHttpClient, .build()) .asObservableSuccess() .map { resp -> - val body = resp.body()!!.bytes() + val body = resp.body!!.bytes() val cursor = ByteCursor(body) (1 .. body.size / 4).map { cursor.nextInt() @@ -238,7 +238,7 @@ class HitomiNozomi(private val client: OkHttpClient, fun getIndexVersion(httpClient: OkHttpClient, name: String): Observable { return httpClient.newCall(GET("$LTN_BASE_URL/$name/version?_=${System.currentTimeMillis()}")) .asObservableSuccess() - .map { it.body()!!.string().toLong() } + .map { it.body!!.string().toLong() } } } } \ No newline at end of file diff --git a/app/src/main/java/exh/log/EHNetworkLogging.kt b/app/src/main/java/exh/log/EHNetworkLogging.kt index ec998e911..d2dd4a362 100644 --- a/app/src/main/java/exh/log/EHNetworkLogging.kt +++ b/app/src/main/java/exh/log/EHNetworkLogging.kt @@ -1,11 +1,9 @@ package exh.log -import com.elvishew.xlog.XLog import okhttp3.OkHttpClient -import okhttp3.logging.HttpLoggingInterceptor -fun OkHttpClient.Builder.maybeInjectEHLogger(): OkHttpClient.Builder { - if(EHLogLevel.shouldLog(EHLogLevel.EXTREME)) { +fun OkHttpClient.Builder.maybeInjectEHLogger(): OkHttpClient.Builder { //TODO - un-break this +/* if(false &&EHLogLevel.shouldLog(EHLogLevel.EXTREME)) { val xLogger = XLog.tag("EHNetwork") .nst() val interceptor = HttpLoggingInterceptor { @@ -13,6 +11,6 @@ fun OkHttpClient.Builder.maybeInjectEHLogger(): OkHttpClient.Builder { } interceptor.level = HttpLoggingInterceptor.Level.BODY return addInterceptor(interceptor) - } + } */ return this } diff --git a/app/src/main/java/exh/patch/MangaDexLogin.kt b/app/src/main/java/exh/patch/MangaDexLogin.kt index 38f349887..f863bc798 100644 --- a/app/src/main/java/exh/patch/MangaDexLogin.kt +++ b/app/src/main/java/exh/patch/MangaDexLogin.kt @@ -5,8 +5,7 @@ import eu.kanade.tachiyomi.source.SourceManager import eu.kanade.tachiyomi.source.online.HttpSource import exh.ui.captcha.BrowserActionActivity import exh.util.interceptAsHtml -import okhttp3.HttpUrl -import okhttp3.OkHttpClient +import okhttp3.HttpUrl.Companion.toHttpUrlOrNull import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.api.get @@ -19,13 +18,13 @@ private val HIDE_SCRIPT = """ """.trimIndent() private fun verifyComplete(url: String): Boolean { - return HttpUrl.parse(url)?.let { parsed -> - parsed.host() == "mangadex.org" && parsed.pathSegments().none { it.isNotBlank() } + return url.toHttpUrlOrNull()?.let { parsed -> + parsed.host == "mangadex.org" && parsed.pathSegments.none { it.isNotBlank() } } ?: false } val MANGADEX_LOGIN_PATCH: EHInterceptor = { request, response, sourceId -> - if(request.url().host() == MANGADEX_DOMAIN) { + if (request.url.host == MANGADEX_DOMAIN) { response.interceptAsHtml { doc -> if (doc.title().trim().equals("Login - MangaDex", true)) { BrowserActionActivity.launchAction( diff --git a/app/src/main/java/exh/patch/UniversalCaptchaDetection.kt b/app/src/main/java/exh/patch/UniversalCaptchaDetection.kt index 9e512b377..4e74ef5f5 100644 --- a/app/src/main/java/exh/patch/UniversalCaptchaDetection.kt +++ b/app/src/main/java/exh/patch/UniversalCaptchaDetection.kt @@ -15,7 +15,7 @@ val CAPTCHA_DETECTION_PATCH: EHInterceptor = { request, response, sourceId -> BrowserActionActivity.launchUniversal( Injekt.get(), sourceId, - request.url().toString() + request.url.toString() ) } } diff --git a/app/src/main/java/exh/uconfig/EHConfigurator.kt b/app/src/main/java/exh/uconfig/EHConfigurator.kt index a48afaef1..cc721b824 100644 --- a/app/src/main/java/exh/uconfig/EHConfigurator.kt +++ b/app/src/main/java/exh/uconfig/EHConfigurator.kt @@ -117,13 +117,13 @@ class EHConfigurator { //Persist slot + sk source.spPref().set(slot) - val keyCookie = response.headers().toMultimap()["Set-Cookie"]?.find { + val keyCookie = response.headers.toMultimap()["Set-Cookie"]?.find { it.startsWith("sk=") }?.removePrefix("sk=")?.substringBefore(';') - val sessionCookie = response.headers().toMultimap()["Set-Cookie"]?.find { + val sessionCookie = response.headers.toMultimap()["Set-Cookie"]?.find { it.startsWith("s=") }?.removePrefix("s=")?.substringBefore(';') - val hathPerksCookie = response.headers().toMultimap()["Set-Cookie"]?.find { + val hathPerksCookie = response.headers.toMultimap()["Set-Cookie"]?.find { it.startsWith("hath_perks=") }?.removePrefix("hath_perks=")?.substringBefore(';') diff --git a/app/src/main/java/exh/ui/base/BaseExhController.kt b/app/src/main/java/exh/ui/base/BaseExhController.kt index 2a25f16c9..d913e52a9 100644 --- a/app/src/main/java/exh/ui/base/BaseExhController.kt +++ b/app/src/main/java/exh/ui/base/BaseExhController.kt @@ -1,10 +1,10 @@ package exh.ui.base import android.os.Bundle -import android.support.annotation.LayoutRes import android.view.LayoutInflater import android.view.View import android.view.ViewGroup +import androidx.annotation.LayoutRes import eu.kanade.tachiyomi.ui.base.controller.BaseController import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers diff --git a/app/src/main/java/exh/ui/captcha/AutoSolvingWebViewClient.kt b/app/src/main/java/exh/ui/captcha/AutoSolvingWebViewClient.kt index f0a6acea1..76526b781 100644 --- a/app/src/main/java/exh/ui/captcha/AutoSolvingWebViewClient.kt +++ b/app/src/main/java/exh/ui/captcha/AutoSolvingWebViewClient.kt @@ -1,10 +1,10 @@ package exh.ui.captcha import android.os.Build -import android.support.annotation.RequiresApi import android.webkit.WebResourceRequest import android.webkit.WebResourceResponse import android.webkit.WebView +import androidx.annotation.RequiresApi import eu.kanade.tachiyomi.util.asJsoup import exh.ui.captcha.BrowserActionActivity.Companion.CROSS_WINDOW_SCRIPT_INNER import org.jsoup.nodes.DataNode diff --git a/app/src/main/java/exh/ui/captcha/BrowserActionActivity.kt b/app/src/main/java/exh/ui/captcha/BrowserActionActivity.kt index 924e26615..2ecd7a33c 100644 --- a/app/src/main/java/exh/ui/captcha/BrowserActionActivity.kt +++ b/app/src/main/java/exh/ui/captcha/BrowserActionActivity.kt @@ -4,34 +4,38 @@ import android.content.Context import android.content.Intent import android.os.Build import android.os.Bundle -import android.support.annotation.RequiresApi -import android.support.v7.app.AppCompatActivity +import android.os.SystemClock +import android.view.MotionEvent import android.webkit.* +import androidx.annotation.RequiresApi +import androidx.appcompat.app.AppCompatActivity +import com.afollestad.materialdialogs.MaterialDialog import com.github.salomonbrys.kotson.get import com.github.salomonbrys.kotson.string import com.google.gson.JsonParser +import eu.kanade.tachiyomi.data.preference.PreferencesHelper +import eu.kanade.tachiyomi.data.preference.getOrDefault +import eu.kanade.tachiyomi.network.NetworkHelper import eu.kanade.tachiyomi.network.asObservableSuccess import eu.kanade.tachiyomi.source.Source import eu.kanade.tachiyomi.source.SourceManager +import eu.kanade.tachiyomi.source.online.HttpSource +import exh.source.DelegatedHttpSource +import exh.util.melt import kotlinx.android.synthetic.main.eh_activity_captcha.* -import okhttp3.* +import okhttp3.HttpUrl.Companion.toHttpUrlOrNull +import okhttp3.MediaType.Companion.toMediaTypeOrNull +import okhttp3.MultipartBody +import okhttp3.Request +import okhttp3.RequestBody +import rx.Observable import rx.Single import rx.schedulers.Schedulers import timber.log.Timber import uy.kohesive.injekt.injectLazy +import java.io.Serializable import java.net.URL import java.util.* -import android.view.MotionEvent -import android.os.SystemClock -import com.afollestad.materialdialogs.MaterialDialog -import eu.kanade.tachiyomi.data.preference.PreferencesHelper -import eu.kanade.tachiyomi.data.preference.getOrDefault -import eu.kanade.tachiyomi.network.NetworkHelper -import eu.kanade.tachiyomi.source.online.HttpSource -import exh.source.DelegatedHttpSource -import exh.util.melt -import rx.Observable -import java.io.Serializable import kotlin.collections.HashMap class BrowserActionActivity : AppCompatActivity() { @@ -146,7 +150,7 @@ class BrowserActionActivity : AppCompatActivity() { .asObservableSuccess() .subscribeOn(Schedulers.io()) .map { - val json = jsonParser.parse(it.body()!!.string()) + val json = jsonParser.parse(it.body!!.string()) it.close() json["token"].string }.melt() @@ -267,10 +271,10 @@ class BrowserActionActivity : AppCompatActivity() { token to it } }.flatMap { (token, response) -> - val audioFile = response.body()!!.bytes() + val audioFile = response.body!!.bytes() httpClient.newCall(Request.Builder() - .url(HttpUrl.parse("https://stream.watsonplatform.net/speech-to-text/api/v1/recognize")!! + .url("https://stream.watsonplatform.net/speech-to-text/api/v1/recognize".toHttpUrlOrNull()!! .newBuilder() .addQueryParameter("watson-token", token) .build()) @@ -279,11 +283,11 @@ class BrowserActionActivity : AppCompatActivity() { .addFormDataPart("jsonDescription", RECOGNIZE_JSON) .addFormDataPart("audio.mp3", "audio.mp3", - RequestBody.create(MediaType.parse("audio/mp3"), audioFile)) + RequestBody.create("audio/mp3".toMediaTypeOrNull(), audioFile)) .build()) .build()).asObservableSuccess() }.map { response -> - jsonParser.parse(response.body()!!.string())["results"][0]["alternatives"][0]["transcript"].string.trim() + jsonParser.parse(response.body!!.string())["results"][0]["alternatives"][0]["transcript"].string.trim() }.toSingle() } diff --git a/app/src/main/java/exh/ui/captcha/HeadersInjectingWebViewClient.kt b/app/src/main/java/exh/ui/captcha/HeadersInjectingWebViewClient.kt index 5938543d5..2405bbd2d 100644 --- a/app/src/main/java/exh/ui/captcha/HeadersInjectingWebViewClient.kt +++ b/app/src/main/java/exh/ui/captcha/HeadersInjectingWebViewClient.kt @@ -1,10 +1,10 @@ package exh.ui.captcha import android.os.Build -import android.support.annotation.RequiresApi import android.webkit.WebResourceRequest import android.webkit.WebResourceResponse import android.webkit.WebView +import androidx.annotation.RequiresApi @RequiresApi(Build.VERSION_CODES.LOLLIPOP) open class HeadersInjectingWebViewClient(activity: BrowserActionActivity, diff --git a/app/src/main/java/exh/ui/captcha/WebViewUtil.kt b/app/src/main/java/exh/ui/captcha/WebViewUtil.kt index 1d1447e06..6802bccd7 100644 --- a/app/src/main/java/exh/ui/captcha/WebViewUtil.kt +++ b/app/src/main/java/exh/ui/captcha/WebViewUtil.kt @@ -1,8 +1,8 @@ package exh.ui.captcha import android.os.Build -import android.support.annotation.RequiresApi import android.webkit.WebResourceRequest +import androidx.annotation.RequiresApi import okhttp3.Request @RequiresApi(Build.VERSION_CODES.LOLLIPOP) diff --git a/app/src/main/java/exh/ui/lock/FingerLockPreference.kt b/app/src/main/java/exh/ui/lock/FingerLockPreference.kt index 2fc478a96..1af80c5ff 100644 --- a/app/src/main/java/exh/ui/lock/FingerLockPreference.kt +++ b/app/src/main/java/exh/ui/lock/FingerLockPreference.kt @@ -4,12 +4,12 @@ import android.annotation.SuppressLint import android.annotation.TargetApi import android.content.Context import android.os.Build -import android.support.v7.preference.SwitchPreferenceCompat -import android.support.v7.widget.LinearLayoutCompat import android.util.AttributeSet import android.view.Gravity import android.view.ViewGroup import android.widget.TextView +import androidx.appcompat.widget.LinearLayoutCompat +import androidx.preference.SwitchPreferenceCompat import com.afollestad.materialdialogs.MaterialDialog import com.github.ajalt.reprint.core.AuthenticationResult import com.github.ajalt.reprint.core.Reprint diff --git a/app/src/main/java/exh/ui/lock/LockPreference.kt b/app/src/main/java/exh/ui/lock/LockPreference.kt index 43cb261c6..8d5c61406 100755 --- a/app/src/main/java/exh/ui/lock/LockPreference.kt +++ b/app/src/main/java/exh/ui/lock/LockPreference.kt @@ -1,9 +1,9 @@ package exh.ui.lock import android.content.Context -import android.support.v7.preference.SwitchPreferenceCompat import android.text.InputType import android.util.AttributeSet +import androidx.preference.SwitchPreferenceCompat import com.afollestad.materialdialogs.MaterialDialog import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.data.preference.PreferencesHelper diff --git a/app/src/main/java/exh/ui/migration/manga/design/MigrationDesignController.kt b/app/src/main/java/exh/ui/migration/manga/design/MigrationDesignController.kt index f1c0f443d..8dc984664 100644 --- a/app/src/main/java/exh/ui/migration/manga/design/MigrationDesignController.kt +++ b/app/src/main/java/exh/ui/migration/manga/design/MigrationDesignController.kt @@ -1,7 +1,6 @@ package exh.ui.migration.manga.design import android.os.Bundle -import android.support.v7.widget.LinearLayoutManager import android.view.View import eu.davidea.flexibleadapter.FlexibleAdapter import eu.kanade.tachiyomi.R @@ -42,7 +41,7 @@ class MigrationDesignController(bundle: Bundle? = null) : BaseExhController(bund this ) adapter = ourAdapter - recycler.layoutManager = LinearLayoutManager(view.context) + recycler.layoutManager = androidx.recyclerview.widget.LinearLayoutManager(view.context) recycler.setHasFixedSize(true) recycler.adapter = ourAdapter ourAdapter.itemTouchHelperCallback = null // Reset adapter touch adapter to fix drag after rotation diff --git a/app/src/main/java/exh/ui/migration/manga/design/MigrationSourceItem.kt b/app/src/main/java/exh/ui/migration/manga/design/MigrationSourceItem.kt index c5a353ee0..e62365b14 100644 --- a/app/src/main/java/exh/ui/migration/manga/design/MigrationSourceItem.kt +++ b/app/src/main/java/exh/ui/migration/manga/design/MigrationSourceItem.kt @@ -1,7 +1,6 @@ package exh.ui.migration.manga.design import android.os.Parcelable -import android.support.v7.widget.RecyclerView import android.view.View import eu.davidea.flexibleadapter.FlexibleAdapter import eu.davidea.flexibleadapter.items.AbstractFlexibleItem @@ -14,7 +13,7 @@ import kotlinx.android.parcel.Parcelize class MigrationSourceItem(val source: HttpSource, var sourceEnabled: Boolean): AbstractFlexibleItem() { override fun getLayoutRes() = R.layout.eh_source_item - override fun createViewHolder(view: View, adapter: FlexibleAdapter>): MigrationSourceHolder { + override fun createViewHolder(view: View, adapter: FlexibleAdapter>): MigrationSourceHolder { return MigrationSourceHolder(view, adapter as MigrationSourceAdapter) } @@ -26,7 +25,7 @@ class MigrationSourceItem(val source: HttpSource, var sourceEnabled: Boolean): A * @param position The position of this item in the adapter. * @param payloads List of partial changes. */ - override fun bindViewHolder(adapter: FlexibleAdapter>, + override fun bindViewHolder(adapter: FlexibleAdapter>, holder: MigrationSourceHolder, position: Int, payloads: List?) { diff --git a/app/src/main/java/exh/ui/migration/manga/process/DeactivatableViewPager.kt b/app/src/main/java/exh/ui/migration/manga/process/DeactivatableViewPager.kt index fb11df2b4..526c522f6 100644 --- a/app/src/main/java/exh/ui/migration/manga/process/DeactivatableViewPager.kt +++ b/app/src/main/java/exh/ui/migration/manga/process/DeactivatableViewPager.kt @@ -1,11 +1,10 @@ package exh.ui.migration.manga.process import android.content.Context -import android.support.v4.view.ViewPager import android.util.AttributeSet import android.view.MotionEvent -class DeactivatableViewPager: ViewPager { +class DeactivatableViewPager : androidx.viewpager.widget.ViewPager { constructor(context: Context): super(context) constructor(context: Context, attrs: AttributeSet): super(context, attrs) diff --git a/app/src/main/java/exh/ui/migration/manga/process/MigrationProcedureAdapter.kt b/app/src/main/java/exh/ui/migration/manga/process/MigrationProcedureAdapter.kt index 63de180c9..bdbe90f99 100644 --- a/app/src/main/java/exh/ui/migration/manga/process/MigrationProcedureAdapter.kt +++ b/app/src/main/java/exh/ui/migration/manga/process/MigrationProcedureAdapter.kt @@ -1,6 +1,5 @@ package exh.ui.migration.manga.process -import android.support.v4.view.PagerAdapter import android.view.View import android.view.ViewGroup import com.bumptech.glide.load.engine.DiskCacheStrategy @@ -18,7 +17,9 @@ import eu.kanade.tachiyomi.source.online.all.MergedSource import eu.kanade.tachiyomi.ui.base.controller.withFadeTransaction import eu.kanade.tachiyomi.ui.manga.MangaController import eu.kanade.tachiyomi.ui.migration.MigrationFlags -import eu.kanade.tachiyomi.util.* +import eu.kanade.tachiyomi.util.gone +import eu.kanade.tachiyomi.util.inflate +import eu.kanade.tachiyomi.util.visible import exh.MERGED_SOURCE_ID import exh.util.await import kotlinx.android.synthetic.main.eh_manga_card.view.* @@ -34,7 +35,7 @@ import kotlin.coroutines.CoroutineContext class MigrationProcedureAdapter(val controller: MigrationProcedureController, val migratingManga: List, - override val coroutineContext: CoroutineContext) : PagerAdapter(), CoroutineScope { + override val coroutineContext: CoroutineContext) : androidx.viewpager.widget.PagerAdapter(), CoroutineScope { private val db: DatabaseHelper by injectLazy() private val gson: Gson by injectLazy() private val sourceManager: SourceManager by injectLazy() diff --git a/app/src/main/java/exh/ui/webview/WebViewActivity.kt b/app/src/main/java/exh/ui/webview/WebViewActivity.kt index 22076c3a6..5947fd6ae 100644 --- a/app/src/main/java/exh/ui/webview/WebViewActivity.kt +++ b/app/src/main/java/exh/ui/webview/WebViewActivity.kt @@ -129,7 +129,7 @@ class WebViewActivity : BaseActivity() { webview.restoreState(savedInstanceState) } - override fun onSaveInstanceState(outState: Bundle?) { + override fun onSaveInstanceState(outState: Bundle) { super.onSaveInstanceState(outState) outState?.putString(STATE_KEY_MOBILE_USER_AGENT, mobileUserAgent) outState?.putBoolean(STATE_KEY_IS_DESKTOP, isDesktop) diff --git a/app/src/main/java/exh/util/OkHttpUtil.kt b/app/src/main/java/exh/util/OkHttpUtil.kt index 28ace9c3f..614dc80f3 100644 --- a/app/src/main/java/exh/util/OkHttpUtil.kt +++ b/app/src/main/java/exh/util/OkHttpUtil.kt @@ -7,9 +7,9 @@ import okhttp3.ResponseBody import org.jsoup.nodes.Document fun Response.interceptAsHtml(block: (Document) -> Unit): Response { - val body = body() - if (body?.contentType()?.type() == "text" - && body.contentType()?.subtype() == "html") { + val body = body + if (body?.contentType()?.type == "text" + && body.contentType()?.subtype == "html") { val bodyString = body.string() val rebuiltResponse = newBuilder() .body(ResponseBody.create(body.contentType(), bodyString)) diff --git a/app/src/main/res/layout-land/manga_info_controller.xml b/app/src/main/res/layout-land/manga_info_controller.xml index 3699d6dea..80eece804 100644 --- a/app/src/main/res/layout-land/manga_info_controller.xml +++ b/app/src/main/res/layout-land/manga_info_controller.xml @@ -1,5 +1,5 @@ - - - - - @@ -284,10 +284,10 @@ - + - + - + - + diff --git a/app/src/main/res/layout-land/reader_color_filter_sheet.xml b/app/src/main/res/layout-land/reader_color_filter_sheet.xml index 761c27992..9cf10846d 100644 --- a/app/src/main/res/layout-land/reader_color_filter_sheet.xml +++ b/app/src/main/res/layout-land/reader_color_filter_sheet.xml @@ -1,5 +1,5 @@ - - - - + - + diff --git a/app/src/main/res/layout/activity_lock.xml b/app/src/main/res/layout/activity_lock.xml index 601bdc1a5..3dd0e7218 100755 --- a/app/src/main/res/layout/activity_lock.xml +++ b/app/src/main/res/layout/activity_lock.xml @@ -1,8 +1,7 @@ - @@ -32,7 +31,7 @@ app:layout_constraintTop_toTopOf="parent" app:layout_constraintVertical_chainStyle="packed" /> - - - \ No newline at end of file + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_webview.xml b/app/src/main/res/layout/activity_webview.xml index f59d4a656..d49cf5491 100644 --- a/app/src/main/res/layout/activity_webview.xml +++ b/app/src/main/res/layout/activity_webview.xml @@ -1,12 +1,11 @@ - - - - - + + - + diff --git a/app/src/main/res/layout/catalogue_controller.xml b/app/src/main/res/layout/catalogue_controller.xml index 929ea2275..640978c28 100755 --- a/app/src/main/res/layout/catalogue_controller.xml +++ b/app/src/main/res/layout/catalogue_controller.xml @@ -1,5 +1,5 @@ - - \ No newline at end of file + \ No newline at end of file diff --git a/app/src/main/res/layout/catalogue_global_search_controller.xml b/app/src/main/res/layout/catalogue_global_search_controller.xml index eae833eac..57db3200c 100644 --- a/app/src/main/res/layout/catalogue_global_search_controller.xml +++ b/app/src/main/res/layout/catalogue_global_search_controller.xml @@ -4,7 +4,7 @@ xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="wrap_content"> - - - - - - \ No newline at end of file + + \ No newline at end of file diff --git a/app/src/main/res/layout/catalogue_global_search_controller_card_item.xml b/app/src/main/res/layout/catalogue_global_search_controller_card_item.xml index 396ff7779..d79a0975c 100644 --- a/app/src/main/res/layout/catalogue_global_search_controller_card_item.xml +++ b/app/src/main/res/layout/catalogue_global_search_controller_card_item.xml @@ -1,5 +1,5 @@ - - \ No newline at end of file + \ No newline at end of file diff --git a/app/src/main/res/layout/catalogue_grid_item.xml b/app/src/main/res/layout/catalogue_grid_item.xml index 4bde878c4..3442670aa 100755 --- a/app/src/main/res/layout/catalogue_grid_item.xml +++ b/app/src/main/res/layout/catalogue_grid_item.xml @@ -29,7 +29,7 @@ android:layout_gravity="bottom" android:background="@drawable/gradient_shape"/> - - + - - + diff --git a/app/src/main/res/layout/catalogue_main_controller.xml b/app/src/main/res/layout/catalogue_main_controller.xml index 84efdf471..23a5bdf0f 100644 --- a/app/src/main/res/layout/catalogue_main_controller.xml +++ b/app/src/main/res/layout/catalogue_main_controller.xml @@ -5,7 +5,7 @@ android:layout_width="match_parent" android:layout_height="wrap_content"> - - - + diff --git a/app/src/main/res/layout/categories_controller.xml b/app/src/main/res/layout/categories_controller.xml index 9bc79a6c3..29382002f 100644 --- a/app/src/main/res/layout/categories_controller.xml +++ b/app/src/main/res/layout/categories_controller.xml @@ -5,7 +5,7 @@ android:layout_width="match_parent" android:layout_height="match_parent"> - - - - - - + - + - - \ No newline at end of file + \ No newline at end of file diff --git a/app/src/main/res/layout/chapters_item.xml b/app/src/main/res/layout/chapters_item.xml index 95a5c4eaf..c164c0104 100644 --- a/app/src/main/res/layout/chapters_item.xml +++ b/app/src/main/res/layout/chapters_item.xml @@ -1,5 +1,5 @@ - - + diff --git a/app/src/main/res/layout/common_view_empty.xml b/app/src/main/res/layout/common_view_empty.xml index 03e329259..8973f4013 100755 --- a/app/src/main/res/layout/common_view_empty.xml +++ b/app/src/main/res/layout/common_view_empty.xml @@ -5,7 +5,7 @@ android:layout_gravity="center" xmlns:android="http://schemas.android.com/apk/res/android"> - - - - - - - - - - + - \ No newline at end of file + \ No newline at end of file diff --git a/app/src/main/res/layout/eh_activity_intercept.xml b/app/src/main/res/layout/eh_activity_intercept.xml index a69d05b20..acb315583 100755 --- a/app/src/main/res/layout/eh_activity_intercept.xml +++ b/app/src/main/res/layout/eh_activity_intercept.xml @@ -1,8 +1,6 @@ - @@ -12,20 +10,20 @@ android:layout_height="match_parent" android:orientation="vertical"> - - - + - + diff --git a/app/src/main/res/layout/eh_activity_login.xml b/app/src/main/res/layout/eh_activity_login.xml index 9be06c6a4..c2e289763 100755 --- a/app/src/main/res/layout/eh_activity_login.xml +++ b/app/src/main/res/layout/eh_activity_login.xml @@ -1,5 +1,5 @@ - - - @@ -34,7 +34,7 @@ app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent"> - - + - + - + diff --git a/app/src/main/res/layout/eh_fragment_batch_add.xml b/app/src/main/res/layout/eh_fragment_batch_add.xml index a4001371a..69ac0aafd 100755 --- a/app/src/main/res/layout/eh_fragment_batch_add.xml +++ b/app/src/main/res/layout/eh_fragment_batch_add.xml @@ -1,13 +1,11 @@ - - - + - + diff --git a/app/src/main/res/layout/eh_manga_card.xml b/app/src/main/res/layout/eh_manga_card.xml index e4164d575..230161382 100644 --- a/app/src/main/res/layout/eh_manga_card.xml +++ b/app/src/main/res/layout/eh_manga_card.xml @@ -1,5 +1,5 @@ - - @@ -27,7 +27,7 @@ app:layout_constraintWidth_min="100dp" tools:background="@color/material_grey_700" /> - - + - - - \ No newline at end of file + + \ No newline at end of file diff --git a/app/src/main/res/layout/eh_migration_design.xml b/app/src/main/res/layout/eh_migration_design.xml index a7c58090a..bf8fb954d 100644 --- a/app/src/main/res/layout/eh_migration_design.xml +++ b/app/src/main/res/layout/eh_migration_design.xml @@ -1,12 +1,12 @@ - - - + - - - - - - \ No newline at end of file + \ No newline at end of file diff --git a/app/src/main/res/layout/eh_migration_process.xml b/app/src/main/res/layout/eh_migration_process.xml index a6027ef9d..2f660e248 100644 --- a/app/src/main/res/layout/eh_migration_process.xml +++ b/app/src/main/res/layout/eh_migration_process.xml @@ -1,11 +1,9 @@ - + android:background="?attr/colorPrimary"> - \ No newline at end of file + \ No newline at end of file diff --git a/app/src/main/res/layout/eh_migration_process_item.xml b/app/src/main/res/layout/eh_migration_process_item.xml index aee740d8d..52141c9cb 100644 --- a/app/src/main/res/layout/eh_migration_process_item.xml +++ b/app/src/main/res/layout/eh_migration_process_item.xml @@ -1,12 +1,11 @@ - @@ -86,7 +85,7 @@ app:layout_constraintHorizontal_bias="0.5" app:layout_constraintStart_toEndOf="@+id/skip_migration" /> - + - @@ -12,20 +10,20 @@ android:layout_height="match_parent" android:orientation="vertical"> - - - + - + diff --git a/app/src/main/res/layout/extension_card_header.xml b/app/src/main/res/layout/extension_card_header.xml index 2c2e11fc9..a12ea4be8 100644 --- a/app/src/main/res/layout/extension_card_header.xml +++ b/app/src/main/res/layout/extension_card_header.xml @@ -1,12 +1,10 @@ - - @@ -20,5 +18,5 @@ android:paddingTop="8dp" tools:text="Title"/> - + \ No newline at end of file diff --git a/app/src/main/res/layout/extension_card_item.xml b/app/src/main/res/layout/extension_card_item.xml index 23ee4e5e7..15266ac40 100644 --- a/app/src/main/res/layout/extension_card_item.xml +++ b/app/src/main/res/layout/extension_card_item.xml @@ -6,7 +6,7 @@ android:layout_width="match_parent" android:layout_height="wrap_content"> - - + diff --git a/app/src/main/res/layout/extension_controller.xml b/app/src/main/res/layout/extension_controller.xml index 8f4bb3d52..62a3e8d31 100644 --- a/app/src/main/res/layout/extension_controller.xml +++ b/app/src/main/res/layout/extension_controller.xml @@ -1,15 +1,15 @@ - - - \ No newline at end of file + \ No newline at end of file diff --git a/app/src/main/res/layout/extension_detail_controller.xml b/app/src/main/res/layout/extension_detail_controller.xml index a22c52696..267578d53 100644 --- a/app/src/main/res/layout/extension_detail_controller.xml +++ b/app/src/main/res/layout/extension_detail_controller.xml @@ -1,5 +1,5 @@ - - - - \ No newline at end of file + \ No newline at end of file diff --git a/app/src/main/res/layout/library_category.xml b/app/src/main/res/layout/library_category.xml index df6b7ce5a..8c431ae09 100755 --- a/app/src/main/res/layout/library_category.xml +++ b/app/src/main/res/layout/library_category.xml @@ -4,11 +4,11 @@ android:layout_width="match_parent" android:layout_height="match_parent"> - - + \ No newline at end of file diff --git a/app/src/main/res/layout/library_controller.xml b/app/src/main/res/layout/library_controller.xml index 075f88a71..3214b46fa 100644 --- a/app/src/main/res/layout/library_controller.xml +++ b/app/src/main/res/layout/library_controller.xml @@ -4,7 +4,7 @@ android:layout_width="match_parent" android:layout_height="match_parent"> - diff --git a/app/src/main/res/layout/library_list_recycler.xml b/app/src/main/res/layout/library_list_recycler.xml index bb0806f08..1f9ac99e2 100755 --- a/app/src/main/res/layout/library_list_recycler.xml +++ b/app/src/main/res/layout/library_list_recycler.xml @@ -1,5 +1,5 @@ - - - - - - + diff --git a/app/src/main/res/layout/manga_controller.xml b/app/src/main/res/layout/manga_controller.xml index f0b5e594d..ff3e76c66 100755 --- a/app/src/main/res/layout/manga_controller.xml +++ b/app/src/main/res/layout/manga_controller.xml @@ -1,5 +1,5 @@ - - - @@ -19,7 +19,7 @@ android:layout_marginTop="16dp" app:layout_constraintTop_toBottomOf="@+id/manga_cover"/> - - - - @@ -232,11 +232,11 @@ app:layout_constraintRight_toRightOf="parent" app:layout_constraintTop_toBottomOf="@+id/manga_status_label" /> - + - + - - + - + - + diff --git a/app/src/main/res/layout/migration_controller.xml b/app/src/main/res/layout/migration_controller.xml index 643832cd0..9c75a0230 100644 --- a/app/src/main/res/layout/migration_controller.xml +++ b/app/src/main/res/layout/migration_controller.xml @@ -1,5 +1,5 @@ - - - - + diff --git a/app/src/main/res/layout/reader_activity.xml b/app/src/main/res/layout/reader_activity.xml index 72c834ba9..793de7369 100755 --- a/app/src/main/res/layout/reader_activity.xml +++ b/app/src/main/res/layout/reader_activity.xml @@ -44,7 +44,7 @@ android:visibility="invisible" tools:visibility="visible"> - - - + - - - - - - - - + diff --git a/app/src/main/res/layout/reader_color_filter_sheet.xml b/app/src/main/res/layout/reader_color_filter_sheet.xml index 3155bc15c..496563ebf 100644 --- a/app/src/main/res/layout/reader_color_filter_sheet.xml +++ b/app/src/main/res/layout/reader_color_filter_sheet.xml @@ -9,7 +9,7 @@ android:layout_width="match_parent" android:layout_height="200dp"> - - - + diff --git a/app/src/main/res/layout/reader_settings_sheet.xml b/app/src/main/res/layout/reader_settings_sheet.xml index d28155d70..f18cc12c9 100644 --- a/app/src/main/res/layout/reader_settings_sheet.xml +++ b/app/src/main/res/layout/reader_settings_sheet.xml @@ -1,5 +1,5 @@ - - - - - - - - - - - - - - - - - - - - + diff --git a/app/src/main/res/layout/recent_chapters_controller.xml b/app/src/main/res/layout/recent_chapters_controller.xml index 6dcca143e..c0cc771bc 100755 --- a/app/src/main/res/layout/recent_chapters_controller.xml +++ b/app/src/main/res/layout/recent_chapters_controller.xml @@ -1,5 +1,5 @@ - - - \ No newline at end of file + \ No newline at end of file diff --git a/app/src/main/res/layout/recent_chapters_item.xml b/app/src/main/res/layout/recent_chapters_item.xml index 2bf1b92d8..6d90a4f32 100644 --- a/app/src/main/res/layout/recent_chapters_item.xml +++ b/app/src/main/res/layout/recent_chapters_item.xml @@ -1,5 +1,5 @@ - - \ No newline at end of file + \ No newline at end of file diff --git a/app/src/main/res/layout/recently_read_controller.xml b/app/src/main/res/layout/recently_read_controller.xml index e0f4da99b..ca92b4855 100755 --- a/app/src/main/res/layout/recently_read_controller.xml +++ b/app/src/main/res/layout/recently_read_controller.xml @@ -5,7 +5,7 @@ android:layout_height="match_parent" android:orientation="vertical"> - - + - @@ -69,4 +69,4 @@ android:text="@string/action_resume"/> - \ No newline at end of file + \ No newline at end of file diff --git a/app/src/main/res/layout/track_controller.xml b/app/src/main/res/layout/track_controller.xml index 2d3dd35e9..2917ba988 100755 --- a/app/src/main/res/layout/track_controller.xml +++ b/app/src/main/res/layout/track_controller.xml @@ -5,7 +5,7 @@ android:layout_width="match_parent" android:layout_height="match_parent"> - - - + \ No newline at end of file diff --git a/app/src/main/res/layout/track_item.xml b/app/src/main/res/layout/track_item.xml index 07c05ebcc..7d0bee737 100755 --- a/app/src/main/res/layout/track_item.xml +++ b/app/src/main/res/layout/track_item.xml @@ -1,191 +1,191 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/track_search_item.xml b/app/src/main/res/layout/track_search_item.xml index acab56c21..a38bddb7d 100755 --- a/app/src/main/res/layout/track_search_item.xml +++ b/app/src/main/res/layout/track_search_item.xml @@ -1,12 +1,12 @@ - - - - - + + diff --git a/app/src/main/res/menu/catalogue_list.xml b/app/src/main/res/menu/catalogue_list.xml index 1b75d4ae7..2c0779299 100755 --- a/app/src/main/res/menu/catalogue_list.xml +++ b/app/src/main/res/menu/catalogue_list.xml @@ -7,7 +7,7 @@ android:title="@string/action_search" android:icon="@drawable/ic_search_white_24dp" app:showAsAction="collapseActionView|ifRoom" - app:actionViewClass="android.support.v7.widget.SearchView"/> + app:actionViewClass="androidx.appcompat.widget.SearchView" /> + app:actionViewClass="androidx.appcompat.widget.SearchView" /> + app:actionViewClass="androidx.appcompat.widget.SearchView" /> diff --git a/app/src/main/res/menu/extension_main.xml b/app/src/main/res/menu/extension_main.xml index dd634ce73..4e312b201 100644 --- a/app/src/main/res/menu/extension_main.xml +++ b/app/src/main/res/menu/extension_main.xml @@ -6,6 +6,6 @@ android:title="@string/action_search" android:icon="@drawable/ic_search_white_24dp" app:showAsAction="collapseActionView|ifRoom" - app:actionViewClass="android.support.v7.widget.SearchView"/> + app:actionViewClass="androidx.appcompat.widget.SearchView" /> diff --git a/app/src/main/res/menu/library.xml b/app/src/main/res/menu/library.xml index 2755475a3..484391dfd 100755 --- a/app/src/main/res/menu/library.xml +++ b/app/src/main/res/menu/library.xml @@ -7,7 +7,7 @@ android:id="@+id/action_search" android:icon="@drawable/ic_search_white_24dp" android:title="@string/action_search" - app:actionViewClass="android.support.v7.widget.SearchView" + app:actionViewClass="androidx.appcompat.widget.SearchView" app:showAsAction="collapseActionView|ifRoom"/> true @style/Theme.ActionBar.Light - @style/PreferenceThemeOverlay.Material + @style/PreferenceThemeOverlay.v14.Material @style/Theme.AlertDialog.Light @@ -73,7 +73,7 @@ true @style/ThemeOverlay.AppCompat.Dark.ActionBar @style/ThemeOverlay.AppCompat - @style/PreferenceThemeOverlay.Material + @style/PreferenceThemeOverlay.v14.Material @color/dialogDark @style/Theme.AlertDialog.Dark diff --git a/gradle.properties b/gradle.properties index 688a924f1..7e1cfef4a 100755 --- a/gradle.properties +++ b/gradle.properties @@ -22,4 +22,6 @@ android.enableBuildCache=true kotlin.incremental=true kapt.incremental.apt=true kapt.use.worker.api=true -kotlin.parallel.tasks.in.project=true \ No newline at end of file +kotlin.parallel.tasks.in.project=true +android.useAndroidX=true +android.enableJetifier=true \ No newline at end of file