From a9b7df328450328b1733a9d285fe2a494fbe5537 Mon Sep 17 00:00:00 2001 From: NekoInverter <42698724+NekoInverter@users.noreply.github.com> Date: Mon, 23 Mar 2020 14:26:54 +0800 Subject: [PATCH] Update dependencies Settings activity refactor --- app/build.gradle | 22 +- app/src/debug/res/xml-v25/shortcuts.xml | 20 +- .../app/AppCompatPreferenceActivity.java | 32 +- .../com/hippo/app/CheckBoxDialogBuilder.java | 2 + .../com/hippo/app/EditTextDialogBuilder.java | 2 + .../hippo/app/ListCheckBoxDialogBuilder.java | 4 +- .../hippo/app/PrettyPreferenceActivity.java | 113 --- .../java/com/hippo/app/ProgressDialog.java | 81 +- .../hippo/content/ContextLocalWrapper.java | 53 +- .../java/com/hippo/content/FileProvider.java | 532 +++++------ .../hippo/content/RecordingApplication.java | 107 ++- .../com/hippo/database/MSQLiteBuilder.java | 175 ++-- .../com/hippo/database/MSQLiteOpenHelper.java | 47 +- .../com/hippo/drawable/BatteryDrawable.java | 11 +- .../java/com/hippo/drawable/BitmapPool.java | 2 + .../com/hippo/drawable/DrawableWrapper.java | 293 +++--- .../hippo/drawable/DrawerArrowDrawable.java | 9 +- .../com/hippo/drawable/UnikeryDrawable.java | 26 +- .../java/com/hippo/drawable/WrapDrawable.java | 26 +- .../java/com/hippo/ehviewer/Analytics.java | 63 -- .../java/com/hippo/ehviewer/AppConfig.java | 8 +- .../main/java/com/hippo/ehviewer/Crash.java | 288 +++--- .../com/hippo/ehviewer/EhApplication.java | 343 ++++--- .../main/java/com/hippo/ehviewer/EhDB.java | 170 ++-- .../com/hippo/ehviewer/EhProxySelector.java | 126 +-- .../hippo/ehviewer/FavouriteStatusRouter.java | 70 +- .../main/java/com/hippo/ehviewer/GetText.java | 1 + .../main/java/com/hippo/ehviewer/Hosts.java | 586 ++++++------ .../com/hippo/ehviewer/ImageBitmapHelper.java | 2 + .../java/com/hippo/ehviewer/Settings.java | 498 +++++----- .../java/com/hippo/ehviewer/UrlOpener.java | 2 + .../com/hippo/ehviewer/client/EhClient.java | 26 +- .../com/hippo/ehviewer/client/EhConfig.java | 426 +++++---- .../hippo/ehviewer/client/EhCookieStore.java | 24 +- .../java/com/hippo/ehviewer/client/EhDns.java | 95 +- .../com/hippo/ehviewer/client/EhEngine.java | 74 +- .../com/hippo/ehviewer/client/EhFilter.java | 31 +- .../com/hippo/ehviewer/client/EhRequest.java | 36 +- .../hippo/ehviewer/client/EhTagDatabase.java | 586 ++++++------ .../java/com/hippo/ehviewer/client/EhUrl.java | 5 +- .../hippo/ehviewer/client/EhUrlOpener.java | 2 + .../com/hippo/ehviewer/client/EhUtils.java | 27 +- .../client/data/FavListUrlBuilder.java | 64 +- .../ehviewer/client/data/GalleryApiInfo.java | 66 +- .../ehviewer/client/data/GalleryComment.java | 59 +- .../client/data/GalleryCommentList.java | 64 +- .../ehviewer/client/data/GalleryDetail.java | 69 +- .../ehviewer/client/data/GalleryInfo.java | 109 ++- .../ehviewer/client/data/GalleryPreview.java | 49 +- .../ehviewer/client/data/GalleryTagGroup.java | 43 +- .../ehviewer/client/data/LargePreviewSet.java | 47 +- .../ehviewer/client/data/ListUrlBuilder.java | 104 ++- .../client/data/NormalPreviewSet.java | 53 +- .../client/parser/FavoritesParser.java | 20 +- .../ehviewer/client/parser/ForumsParser.java | 1 + .../client/parser/GalleryApiParser.java | 2 +- .../client/parser/GalleryDetailParser.java | 33 +- .../client/parser/GalleryDetailUrlParser.java | 3 +- .../client/parser/GalleryListParser.java | 48 +- .../client/parser/GalleryPageApiParser.java | 7 +- .../client/parser/GalleryPageParser.java | 2 + .../client/parser/GalleryPageUrlParser.java | 2 + .../client/parser/GalleryTokenApiParser.java | 13 +- .../ehviewer/client/parser/ProfileParser.java | 2 + .../client/parser/RateGalleryParser.java | 15 +- .../client/parser/VoteCommentParser.java | 14 +- .../ehviewer/download/DownloadManager.java | 251 +++-- .../ehviewer/download/DownloadService.java | 41 +- .../hippo/ehviewer/gallery/A7ZipArchive.java | 209 ++--- .../gallery/ArchiveGalleryProvider.java | 433 +++++---- .../ehviewer/gallery/DirGalleryProvider.java | 23 +- .../ehviewer/gallery/EhGalleryProvider.java | 3 + .../ehviewer/gallery/GalleryProvider2.java | 4 +- .../java/com/hippo/ehviewer/gallery/Pipe.java | 243 ++--- .../preference/CleanRedundancyPreference.java | 6 +- .../ClearDownloadPathCachePreference.java | 4 +- .../preference/DefaultCategoryPreference.java | 2 + .../ExcludedTagNamespacesPreference.java | 6 +- .../preference/ExportDataPreference.java | 83 +- .../preference/IdentityCookiePreference.java | 4 + .../ehviewer/preference/ProxyPreference.java | 12 +- .../preference/RestoreDownloadPreference.java | 49 +- .../SignInRequiredActivityPreference.java | 68 +- .../preference/SignOutPreference.java | 2 + .../ehviewer/preference/TaskPreference.java | 44 +- .../preference/VersionPreference.java | 2 +- .../ehviewer/shortcuts/ShortcutsActivity.java | 6 +- .../com/hippo/ehviewer/spider/SpiderDen.java | 85 +- .../com/hippo/ehviewer/spider/SpiderInfo.java | 8 +- .../hippo/ehviewer/spider/SpiderQueen.java | 223 +++-- .../hippo/ehviewer/ui/CommonOperations.java | 327 +++---- .../hippo/ehviewer/ui/DirPickerActivity.java | 3 + .../com/hippo/ehviewer/ui/EhActivity.java | 13 +- .../ehviewer/ui/EhPreferenceActivity.java | 98 -- .../ui/ExcludedLanguagesActivity.java | 2 + .../com/hippo/ehviewer/ui/FilterActivity.java | 3 + .../hippo/ehviewer/ui/GalleryActivity.java | 394 ++++---- .../com/hippo/ehviewer/ui/HostsActivity.java | 373 ++++---- .../hippo/ehviewer/ui/LicenseActivity.java | 2 + .../com/hippo/ehviewer/ui/MainActivity.java | 53 +- .../com/hippo/ehviewer/ui/MyTagsActivity.java | 123 +-- .../ehviewer/ui/SetSecurityActivity.java | 22 +- .../hippo/ehviewer/ui/SettingsActivity.java | 65 +- .../hippo/ehviewer/ui/ToolbarActivity.java | 2 + .../hippo/ehviewer/ui/UConfigActivity.java | 239 ++--- .../ui/dialog/SelectItemWithIconAdapter.java | 80 +- .../ehviewer/ui/fragment/AboutFragment.java | 29 +- .../ui/fragment/AdvancedFragment.java | 63 +- .../ui/fragment/DownloadFragment.java | 12 +- .../ehviewer/ui/fragment/EhFragment.java | 12 +- .../ehviewer/ui/fragment/PrivacyFragment.java | 31 +- .../ehviewer/ui/fragment/ReadFragment.java | 9 +- .../ui/fragment/SettingsFragment.java | 16 + .../ehviewer/ui/scene/AnalyticsScene.java | 6 +- .../hippo/ehviewer/ui/scene/BaseScene.java | 48 +- .../ehviewer/ui/scene/CookieSignInScene.java | 66 +- .../ui/scene/DownloadLabelsScene.java | 45 +- .../ehviewer/ui/scene/DownloadsScene.java | 56 +- .../hippo/ehviewer/ui/scene/EhCallback.java | 4 +- .../scene/EnterGalleryDetailTransaction.java | 4 +- .../ehviewer/ui/scene/FavoritesScene.java | 385 ++++---- .../ehviewer/ui/scene/GalleryAdapter.java | 22 +- .../ui/scene/GalleryCommentsScene.java | 419 +++++---- .../ehviewer/ui/scene/GalleryDetailScene.java | 412 ++++----- .../ehviewer/ui/scene/GalleryHolder.java | 2 + .../ehviewer/ui/scene/GalleryInfoScene.java | 5 +- .../ehviewer/ui/scene/GalleryListScene.java | 575 ++++++------ .../ui/scene/GalleryPreviewsScene.java | 115 +-- .../hippo/ehviewer/ui/scene/HistoryScene.java | 64 +- .../ehviewer/ui/scene/ProgressScene.java | 16 +- .../ehviewer/ui/scene/QuickSearchScene.java | 45 +- .../ehviewer/ui/scene/SecurityScene.java | 29 +- .../ehviewer/ui/scene/SelectSiteScene.java | 4 +- .../hippo/ehviewer/ui/scene/SignInScene.java | 4 +- .../hippo/ehviewer/ui/scene/SolidScene.java | 5 +- .../hippo/ehviewer/ui/scene/ToolbarScene.java | 6 +- .../hippo/ehviewer/ui/scene/WarningScene.java | 4 +- .../ehviewer/ui/scene/WebViewSignInScene.java | 12 +- .../ehviewer/widget/AdvanceSearchTable.java | 59 +- .../hippo/ehviewer/widget/CategoryTable.java | 44 +- .../widget/DialogWebChromeClient.java | 78 +- .../hippo/ehviewer/widget/EhDrawerLayout.java | 17 +- .../hippo/ehviewer/widget/EhDrawerView.java | 19 +- .../ehviewer/widget/EhNavigationView.java | 85 -- .../hippo/ehviewer/widget/EhStageLayout.java | 18 +- .../com/hippo/ehviewer/widget/FixedThumb.java | 95 +- .../ehviewer/widget/GalleryGuideView.java | 3 +- .../hippo/ehviewer/widget/GalleryHeader.java | 300 +++--- .../widget/GalleryInfoContentHelper.java | 146 +-- .../ehviewer/widget/GalleryRatingBar.java | 3 +- .../ehviewer/widget/ImageSearchLayout.java | 36 +- .../ehviewer/widget/ReversibleSeekBar.java | 1 + .../com/hippo/ehviewer/widget/SearchBar.java | 56 +- .../hippo/ehviewer/widget/SearchDatabase.java | 20 +- .../hippo/ehviewer/widget/SearchEditText.java | 3 + .../hippo/ehviewer/widget/SearchLayout.java | 39 +- .../hippo/ehviewer/widget/SeekBarPanel.java | 2 + .../ehviewer/widget/SimpleRatingView.java | 8 +- .../com/hippo/hardware/ShakeDetector.java | 8 +- .../com/hippo/io/FileInputStreamPipe.java | 2 + .../com/hippo/io/FileOutputStreamPipe.java | 2 + .../com/hippo/io/UniFileInputStreamPipe.java | 2 + .../com/hippo/io/UniFileOutputStreamPipe.java | 2 + .../com/hippo/network/CookieDatabase.java | 342 +++---- .../com/hippo/network/CookieRepository.java | 294 +++--- .../java/com/hippo/network/CookieSet.java | 130 +-- .../java/com/hippo/network/InetValidator.java | 7 +- .../main/java/com/hippo/network/Network.java | 3 +- .../hippo/network/StatusCodeException.java | 20 +- .../hippo/preference/ActivityPreference.java | 4 +- .../hippo/preference/DialogPreference.java | 164 ++-- .../com/hippo/preference/ListPreference.java | 125 +-- .../hippo/preference/MessagePreference.java | 2 + .../com/hippo/preference/PreferenceUtils.java | 99 -- .../hippo/preference/SeekBarPreference.java | 66 +- .../hippo/preference/SwitchPreference.java | 77 +- .../com/hippo/preference/UrlPreference.java | 3 +- .../com/hippo/reveal/ViewAnimationUtils.java | 10 +- .../main/java/com/hippo/scene/Announcer.java | 8 +- .../java/com/hippo/scene/SceneFragment.java | 28 +- .../java/com/hippo/scene/StageActivity.java | 55 +- .../com/hippo/scene/TransitionHelper.java | 1 + app/src/main/java/com/hippo/text/Html.java | 860 +++++++++--------- .../com/hippo/text/LinkMovementMethod2.java | 26 +- .../main/java/com/hippo/util/AppHelper.java | 4 +- .../main/java/com/hippo/util/BitmapUtils.java | 10 +- .../java/com/hippo/util/DrawableManager.java | 3 +- .../java/com/hippo/util/ExceptionUtils.java | 6 +- .../java/com/hippo/util/HashCodeUtils.java | 32 +- .../com/hippo/util/IoThreadPoolExecutor.java | 108 +-- .../main/java/com/hippo/util/JsoupUtils.java | 1 + .../java/com/hippo/util/MutableBoolean.java | 8 +- .../com/hippo/util/NaturalComparator.java | 242 ++--- .../java/com/hippo/util/PackageUtils.java | 5 +- .../com/hippo/util/PathNaturalComparator.java | 160 ++-- .../com/hippo/util/PermissionRequester.java | 7 +- .../java/com/hippo/util/ReadableTime.java | 13 +- .../main/java/com/hippo/util/SqlUtils.java | 2 +- .../java/com/hippo/util/SystemUiHelper.java | 33 +- .../com/hippo/util/SystemUiHelperImplHC.java | 4 +- .../com/hippo/util/SystemUiHelperImplICS.java | 2 +- .../com/hippo/util/SystemUiHelperImplJB.java | 2 +- .../com/hippo/util/SystemUiHelperImplKK.java | 6 +- app/src/main/java/com/hippo/util/TextUrl.java | 3 +- .../java/com/hippo/widget/AutoWrapLayout.java | 54 +- .../java/com/hippo/widget/BatteryView.java | 31 +- .../java/com/hippo/widget/CheckTextView.java | 40 +- .../java/com/hippo/widget/ContentLayout.java | 130 ++- .../java/com/hippo/widget/CuteSpinner.java | 2 + .../main/java/com/hippo/widget/DateUtils.java | 4 +- .../java/com/hippo/widget/DirExplorer.java | 63 +- .../java/com/hippo/widget/DrawerView.java | 2 +- .../main/java/com/hippo/widget/FabLayout.java | 8 +- .../hippo/widget/FixedAspectImageView.java | 26 +- .../hippo/widget/HackyTextInputEditText.java | 90 +- .../com/hippo/widget/IndicatingListView.java | 5 +- .../hippo/widget/IndicatingScrollView.java | 5 +- .../com/hippo/widget/LinkifyTextView.java | 3 +- .../java/com/hippo/widget/LoadImageView.java | 41 +- .../com/hippo/widget/MaxSizeContainer.java | 1 + .../com/hippo/widget/ObservedTextView.java | 2 + .../java/com/hippo/widget/OffsetLayout.java | 2 +- .../java/com/hippo/widget/ProgressView.java | 11 +- .../java/com/hippo/widget/RadioGridGroup.java | 8 +- .../hippo/widget/SafeCoordinatorLayout.java | 2 + .../java/com/hippo/widget/SearchBarMover.java | 15 +- .../com/hippo/widget/SensitiveCheckBox.java | 9 +- .../com/hippo/widget/ShadowLinearLayout.java | 4 +- .../widget/SimpleGridAutoSpanLayout.java | 30 +- .../com/hippo/widget/SimpleGridLayout.java | 1 - .../main/java/com/hippo/widget/Slider.java | 53 +- .../main/java/com/hippo/widget/TextClock.java | 29 +- .../widget/lockpattern/LockPatternUtils.java | 2 + .../widget/lockpattern/LockPatternView.java | 397 ++++---- .../recyclerview/AutoGridLayoutManager.java | 32 +- .../AutoStaggeredGridLayoutManager.java | 32 +- app/src/main/res/layout/activity_main.xml | 43 +- .../main/res/layout/activity_preference.xml | 11 + app/src/main/res/values/themes.xml | 6 +- app/src/main/res/xml/about_settings.xml | 55 +- app/src/main/res/xml/advanced_settings.xml | 51 +- app/src/main/res/xml/download_settings.xml | 34 +- app/src/main/res/xml/eh_settings.xml | 83 +- app/src/main/res/xml/privacy_settings.xml | 9 +- app/src/main/res/xml/read_settings.xml | 108 ++- app/src/main/res/xml/settings_headers.xml | 45 +- build.gradle | 5 +- gradle/wrapper/gradle-wrapper.properties | 3 +- 248 files changed, 8700 insertions(+), 8980 deletions(-) delete mode 100644 app/src/main/java/com/hippo/app/PrettyPreferenceActivity.java delete mode 100644 app/src/main/java/com/hippo/ehviewer/Analytics.java delete mode 100644 app/src/main/java/com/hippo/ehviewer/ui/EhPreferenceActivity.java create mode 100644 app/src/main/java/com/hippo/ehviewer/ui/fragment/SettingsFragment.java delete mode 100644 app/src/main/java/com/hippo/ehviewer/widget/EhNavigationView.java delete mode 100644 app/src/main/java/com/hippo/preference/PreferenceUtils.java create mode 100644 app/src/main/res/layout/activity_preference.xml diff --git a/app/build.gradle b/app/build.gradle index 81fdefad4..43de65d76 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -1,12 +1,7 @@ apply plugin: 'com.android.application' -if (file('google-services.json').exists()) { - apply plugin: 'com.google.gms.google-services' - apply plugin: 'io.fabric' -} - android { - compileSdkVersion 28 + compileSdkVersion 29 defaultConfig { applicationId "com.hippo.ehviewer" @@ -19,7 +14,7 @@ android { "es", "ja", "ko", "fr", "de", "th" testOptions.unitTests.includeAndroidResources = true ndk { - abiFilters "armeabi-v7a", "x86" + abiFilters "armeabi-v7a", "arm64-v8a", "x86", "x86_64" } } @@ -69,11 +64,9 @@ clean { dependencies { implementation fileTree(include: ['*.jar'], dir: 'libs') - implementation 'androidx.appcompat:appcompat:1.0.2' + implementation 'androidx.appcompat:appcompat:1.1.0' implementation 'androidx.cardview:cardview:1.0.0' - implementation 'com.google.android.material:material:1.0.0' - implementation 'com.google.firebase:firebase-core:16.0.9' - implementation 'com.crashlytics.sdk.android:crashlytics:2.10.1' + implementation 'com.google.android.material:material:1.1.0' implementation 'com.getkeepsafe.relinker:relinker:1.3.1' implementation 'com.github.amlcurran.showcaseview:library:5.4.3' implementation 'com.github.seven332.a7zip:extract-lite:d4ddec5793' @@ -82,7 +75,6 @@ dependencies { implementation 'com.github.seven332:animator:0.1.0' implementation 'com.github.seven332:beerbelly:0.1.4' implementation 'com.github.seven332:conaco:0.1.5-eh' - implementation 'com.github.seven332:drawerlayout:ea2bb388f0' implementation 'com.github.seven332:easyrecyclerview:0.1.1' implementation 'com.github.seven332:glgallery:0.1.2' implementation 'com.github.seven332:glview:0.1.0' @@ -98,10 +90,12 @@ dependencies { implementation 'com.github.seven332:yorozuya-thread:0.1.1' implementation 'com.github.seven332:yorozuya-collect:0.1.4' implementation 'com.h6ah4i.android.widget.advrecyclerview:advrecyclerview:1.0.0' - implementation 'com.squareup.okhttp3:okhttp:3.12.3' + implementation 'com.squareup.okhttp3:okhttp:3.14.7' + implementation 'com.takisoft.preferencex:preferencex:1.1.0' + implementation 'com.takisoft.preferencex:preferencex-simplemenu:1.1.0' implementation 'org.ccil.cowan.tagsoup:tagsoup:1.2.1' implementation 'org.greenrobot:greendao:2.2.1' - implementation 'org.jsoup:jsoup:1.12.1' + implementation 'org.jsoup:jsoup:1.12.2' testImplementation 'junit:junit:4.12' testImplementation 'org.robolectric:robolectric:4.2.1' testImplementation 'org.jooq:joor:0.9.6' diff --git a/app/src/debug/res/xml-v25/shortcuts.xml b/app/src/debug/res/xml-v25/shortcuts.xml index b84b778dd..54455a708 100644 --- a/app/src/debug/res/xml-v25/shortcuts.xml +++ b/app/src/debug/res/xml-v25/shortcuts.xml @@ -2,30 +2,30 @@ + android:shortcutShortLabel="@string/download_start_all"> + android:targetClass="com.hippo.ehviewer.shortcuts.ShortcutsActivity" + android:targetPackage="com.hippo.ehviewer.debug" /> + android:shortcutShortLabel="@string/download_stop_all"> + android:targetClass="com.hippo.ehviewer.shortcuts.ShortcutsActivity" + android:targetPackage="com.hippo.ehviewer.debug" /> diff --git a/app/src/main/java/com/hippo/app/AppCompatPreferenceActivity.java b/app/src/main/java/com/hippo/app/AppCompatPreferenceActivity.java index b3ba6de2b..7c09b6b6e 100644 --- a/app/src/main/java/com/hippo/app/AppCompatPreferenceActivity.java +++ b/app/src/main/java/com/hippo/app/AppCompatPreferenceActivity.java @@ -30,6 +30,7 @@ import android.view.MenuInflater; import android.view.View; import android.view.ViewGroup; import android.view.Window; + import androidx.annotation.CallSuper; import androidx.annotation.IdRes; import androidx.annotation.LayoutRes; @@ -48,7 +49,7 @@ import androidx.core.app.NavUtils; import androidx.core.app.TaskStackBuilder; public abstract class AppCompatPreferenceActivity extends PreferenceActivity implements AppCompatCallback, - TaskStackBuilder.SupportParentable, ActionBarDrawerToggle.DelegateProvider { + TaskStackBuilder.SupportParentable, ActionBarDrawerToggle.DelegateProvider { private AppCompatDelegate mDelegate; private int mThemeId = 0; @@ -187,7 +188,7 @@ public abstract class AppCompatPreferenceActivity extends PreferenceActivity imp final ActionBar ab = getSupportActionBar(); if (item.getItemId() == android.R.id.home && ab != null && - (ab.getDisplayOptions() & ActionBar.DISPLAY_HOME_AS_UP) != 0) { + (ab.getDisplayOptions() & ActionBar.DISPLAY_HOME_AS_UP) != 0) { return onSupportNavigateUp(); } return false; @@ -213,9 +214,8 @@ public abstract class AppCompatPreferenceActivity extends PreferenceActivity imp *

* * @param featureId The desired feature as defined in - * {@link android.view.Window} or {@link androidx.core.view.WindowCompat}. + * {@link android.view.Window} or {@link androidx.core.view.WindowCompat}. * @return Returns true if the requested feature is supported and now enabled. - * * @see android.app.Activity#requestWindowFeature * @see android.view.Window#requestFeature */ @@ -311,7 +311,7 @@ public abstract class AppCompatPreferenceActivity extends PreferenceActivity imp /** * Support version of {@link #onCreateNavigateUpTaskStack(android.app.TaskStackBuilder)}. * This method will be called on all platform versions. - * + *

* Define the synthetic task stack that will be generated during Up navigation from * a different task. * @@ -338,7 +338,7 @@ public abstract class AppCompatPreferenceActivity extends PreferenceActivity imp /** * Support version of {@link #onPrepareNavigateUpTaskStack(android.app.TaskStackBuilder)}. * This method will be called on all platform versions. - * + *

* Prepare the synthetic task stack that will be generated during Up navigation * from a different task. * @@ -374,7 +374,7 @@ public abstract class AppCompatPreferenceActivity extends PreferenceActivity imp * {@link #supportNavigateUpTo(android.content.Intent)} for help implementing custom Up navigation.

* * @return true if Up navigation completed successfully and this Activity was finished, - * false otherwise. + * false otherwise. */ public boolean onSupportNavigateUp() { Intent upIntent = getSupportParentActivityIntent(); @@ -429,7 +429,7 @@ public abstract class AppCompatPreferenceActivity extends PreferenceActivity imp * * @param targetIntent An intent representing the target destination for up navigation * @return true if navigating up should recreate a new task stack, false if the same task - * should be used for the destination + * should be used for the destination */ public boolean supportShouldUpRecreateTask(@NonNull Intent targetIntent) { return NavUtils.shouldUpRecreateTask(this, targetIntent); @@ -504,7 +504,7 @@ public abstract class AppCompatPreferenceActivity extends PreferenceActivity imp final int keyCode = event.getKeyCode(); final ActionBar actionBar = getSupportActionBar(); if (keyCode == KeyEvent.KEYCODE_MENU - && actionBar != null && actionBar.onMenuKeyEvent(event)) { + && actionBar != null && actionBar.onMenuKeyEvent(event)) { return true; } return super.dispatchKeyEvent(event); @@ -526,15 +526,13 @@ public abstract class AppCompatPreferenceActivity extends PreferenceActivity imp */ private boolean performMenuItemShortcut(int keycode, KeyEvent event) { if (!(Build.VERSION.SDK_INT >= 26) && !event.isCtrlPressed() - && !KeyEvent.metaStateHasNoModifiers(event.getMetaState()) - && event.getRepeatCount() == 0 - && !KeyEvent.isModifierKey(event.getKeyCode())) { + && !KeyEvent.metaStateHasNoModifiers(event.getMetaState()) + && event.getRepeatCount() == 0 + && !KeyEvent.isModifierKey(event.getKeyCode())) { final Window currentWindow = getWindow(); if (currentWindow != null && currentWindow.getDecorView() != null) { final View decorView = currentWindow.getDecorView(); - if (decorView.dispatchKeyShortcutEvent(event)) { - return true; - } + return decorView.dispatchKeyShortcutEvent(event); } } return false; @@ -553,7 +551,7 @@ public abstract class AppCompatPreferenceActivity extends PreferenceActivity imp public void openOptionsMenu() { ActionBar actionBar = getSupportActionBar(); if (getWindow().hasFeature(Window.FEATURE_OPTIONS_PANEL) - && (actionBar == null || !actionBar.openOptionsMenu())) { + && (actionBar == null || !actionBar.openOptionsMenu())) { super.openOptionsMenu(); } } @@ -563,7 +561,7 @@ public abstract class AppCompatPreferenceActivity extends PreferenceActivity imp public void closeOptionsMenu() { ActionBar actionBar = getSupportActionBar(); if (getWindow().hasFeature(Window.FEATURE_OPTIONS_PANEL) - && (actionBar == null || !actionBar.closeOptionsMenu())) { + && (actionBar == null || !actionBar.closeOptionsMenu())) { super.closeOptionsMenu(); } } diff --git a/app/src/main/java/com/hippo/app/CheckBoxDialogBuilder.java b/app/src/main/java/com/hippo/app/CheckBoxDialogBuilder.java index ca9a4c9d4..4d0ebfbbd 100644 --- a/app/src/main/java/com/hippo/app/CheckBoxDialogBuilder.java +++ b/app/src/main/java/com/hippo/app/CheckBoxDialogBuilder.java @@ -22,7 +22,9 @@ import android.view.LayoutInflater; import android.view.View; import android.widget.CheckBox; import android.widget.TextView; + import androidx.appcompat.app.AlertDialog; + import com.hippo.ehviewer.R; public class CheckBoxDialogBuilder extends AlertDialog.Builder { diff --git a/app/src/main/java/com/hippo/app/EditTextDialogBuilder.java b/app/src/main/java/com/hippo/app/EditTextDialogBuilder.java index 4c89ce9d3..183ada1ef 100644 --- a/app/src/main/java/com/hippo/app/EditTextDialogBuilder.java +++ b/app/src/main/java/com/hippo/app/EditTextDialogBuilder.java @@ -25,7 +25,9 @@ import android.view.View; import android.widget.Button; import android.widget.EditText; import android.widget.TextView; + import androidx.appcompat.app.AlertDialog; + import com.google.android.material.textfield.TextInputLayout; import com.hippo.ehviewer.R; diff --git a/app/src/main/java/com/hippo/app/ListCheckBoxDialogBuilder.java b/app/src/main/java/com/hippo/app/ListCheckBoxDialogBuilder.java index a84822787..1f054be37 100644 --- a/app/src/main/java/com/hippo/app/ListCheckBoxDialogBuilder.java +++ b/app/src/main/java/com/hippo/app/ListCheckBoxDialogBuilder.java @@ -24,7 +24,9 @@ import android.widget.AdapterView; import android.widget.ArrayAdapter; import android.widget.CheckBox; import android.widget.ListView; + import androidx.appcompat.app.AlertDialog; + import com.hippo.ehviewer.R; import com.hippo.yorozuya.ViewUtils; @@ -36,7 +38,7 @@ public class ListCheckBoxDialogBuilder extends AlertDialog.Builder { @SuppressLint("InflateParams") public ListCheckBoxDialogBuilder(Context context, CharSequence[] items, - final OnItemClickListener listener, String checkText, boolean checked) { + final OnItemClickListener listener, String checkText, boolean checked) { super(context); View view = LayoutInflater.from(getContext()).inflate(R.layout.dialog_list_checkbox_builder, null); setView(view); diff --git a/app/src/main/java/com/hippo/app/PrettyPreferenceActivity.java b/app/src/main/java/com/hippo/app/PrettyPreferenceActivity.java deleted file mode 100644 index 2ddb26b2c..000000000 --- a/app/src/main/java/com/hippo/app/PrettyPreferenceActivity.java +++ /dev/null @@ -1,113 +0,0 @@ -/* - * Copyright 2018 Hippo Seven - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.hippo.app; - -import android.content.Context; -import android.preference.PreferenceActivity; -import android.text.TextUtils; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.ArrayAdapter; -import android.widget.ImageView; -import android.widget.ListAdapter; -import android.widget.TextView; -import androidx.annotation.NonNull; -import com.hippo.ehviewer.R; -import java.util.ArrayList; -import java.util.List; - -public abstract class PrettyPreferenceActivity extends AppCompatPreferenceActivity { - - @Override - public void setListAdapter(ListAdapter adapter) { - if (adapter == null) { - super.setListAdapter(null); - return; - } - - int count = adapter.getCount(); - List headers = new ArrayList<>(count); - for (int i = 0; i < count; ++i) { - headers.add((PreferenceActivity.Header) adapter.getItem(i)); - } - - super.setListAdapter(new HeaderAdapter(this, headers, R.layout.item_preference_header, true)); - } - - private static class HeaderAdapter extends ArrayAdapter { - private static class HeaderViewHolder { - ImageView icon; - TextView title; - TextView summary; - } - - private LayoutInflater mInflater; - private int mLayoutResId; - private boolean mRemoveIconIfEmpty; - - private HeaderAdapter(Context context, List objects, int layoutResId, - boolean removeIconBehavior) { - super(context, 0, objects); - mInflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); - mLayoutResId = layoutResId; - mRemoveIconIfEmpty = removeIconBehavior; - } - - @NonNull - @Override - public View getView(int position, View convertView, @NonNull ViewGroup parent) { - HeaderViewHolder holder; - View view; - - if (convertView == null) { - view = mInflater.inflate(mLayoutResId, parent, false); - holder = new HeaderViewHolder(); - holder.icon = view.findViewById(android.R.id.icon); - holder.title = view.findViewById(android.R.id.title); - holder.summary = view.findViewById(android.R.id.summary); - view.setTag(holder); - } else { - view = convertView; - holder = (HeaderViewHolder) view.getTag(); - } - - // All view fields must be updated every time, because the view may be recycled - PreferenceActivity.Header header = getItem(position); - if (mRemoveIconIfEmpty) { - if (header.iconRes == 0) { - holder.icon.setVisibility(View.GONE); - } else { - holder.icon.setVisibility(View.VISIBLE); - holder.icon.setImageResource(header.iconRes); - } - } else { - holder.icon.setImageResource(header.iconRes); - } - holder.title.setText(header.getTitle(getContext().getResources())); - CharSequence summary = header.getSummary(getContext().getResources()); - if (!TextUtils.isEmpty(summary)) { - holder.summary.setVisibility(View.VISIBLE); - holder.summary.setText(summary); - } else { - holder.summary.setVisibility(View.GONE); - } - - return view; - } - } -} diff --git a/app/src/main/java/com/hippo/app/ProgressDialog.java b/app/src/main/java/com/hippo/app/ProgressDialog.java index 096dc048c..fc9b0a5cb 100644 --- a/app/src/main/java/com/hippo/app/ProgressDialog.java +++ b/app/src/main/java/com/hippo/app/ProgressDialog.java @@ -28,8 +28,11 @@ import android.view.LayoutInflater; import android.view.View; import android.widget.ProgressBar; import android.widget.TextView; + import androidx.appcompat.app.AlertDialog; + import com.hippo.ehviewer.R; + import java.text.NumberFormat; /** @@ -40,12 +43,14 @@ import java.text.NumberFormat; */ public class ProgressDialog extends AlertDialog { - /** Creates a ProgressDialog with a circular, spinning progress + /** + * Creates a ProgressDialog with a circular, spinning progress * bar. This is the default. */ public static final int STYLE_SPINNER = 0; - /** Creates a ProgressDialog with a horizontal progress bar. + /** + * Creates a ProgressDialog with a horizontal progress bar. */ public static final int STYLE_HORIZONTAL = 1; @@ -81,30 +86,24 @@ public class ProgressDialog extends AlertDialog { initFormats(); } - private void initFormats() { - mProgressNumberFormat = "%1d/%2d"; - mProgressPercentFormat = NumberFormat.getPercentInstance(); - mProgressPercentFormat.setMaximumFractionDigits(0); - } - public static ProgressDialog show(Context context, CharSequence title, - CharSequence message) { + CharSequence message) { return show(context, title, message, false); } public static ProgressDialog show(Context context, CharSequence title, - CharSequence message, boolean indeterminate) { + CharSequence message, boolean indeterminate) { return show(context, title, message, indeterminate, false, null); } public static ProgressDialog show(Context context, CharSequence title, - CharSequence message, boolean indeterminate, boolean cancelable) { + CharSequence message, boolean indeterminate, boolean cancelable) { return show(context, title, message, indeterminate, cancelable, null); } public static ProgressDialog show(Context context, CharSequence title, - CharSequence message, boolean indeterminate, - boolean cancelable, OnCancelListener cancelListener) { + CharSequence message, boolean indeterminate, + boolean cancelable, OnCancelListener cancelListener) { ProgressDialog dialog = new ProgressDialog(context); dialog.setTitle(title); dialog.setMessage(message); @@ -115,6 +114,12 @@ public class ProgressDialog extends AlertDialog { return dialog; } + private void initFormats() { + mProgressNumberFormat = "%1d/%2d"; + mProgressPercentFormat = NumberFormat.getPercentInstance(); + mProgressPercentFormat.setMaximumFractionDigits(0); + } + @Override protected void onCreate(Bundle savedInstanceState) { LayoutInflater inflater = LayoutInflater.from(getContext()); @@ -200,6 +205,13 @@ public class ProgressDialog extends AlertDialog { mHasStarted = false; } + public int getProgress() { + if (mProgress != null) { + return mProgress.getProgress(); + } + return mProgressVal; + } + public void setProgress(int value) { if (mHasStarted) { mProgress.setProgress(value); @@ -209,6 +221,13 @@ public class ProgressDialog extends AlertDialog { } } + public int getSecondaryProgress() { + if (mProgress != null) { + return mProgress.getSecondaryProgress(); + } + return mSecondaryProgressVal; + } + public void setSecondaryProgress(int secondaryProgress) { if (mProgress != null) { mProgress.setSecondaryProgress(secondaryProgress); @@ -218,20 +237,6 @@ public class ProgressDialog extends AlertDialog { } } - public int getProgress() { - if (mProgress != null) { - return mProgress.getProgress(); - } - return mProgressVal; - } - - public int getSecondaryProgress() { - if (mProgress != null) { - return mProgress.getSecondaryProgress(); - } - return mSecondaryProgressVal; - } - public int getMax() { if (mProgress != null) { return mProgress.getMax(); @@ -282,6 +287,13 @@ public class ProgressDialog extends AlertDialog { } } + public boolean isIndeterminate() { + if (mProgress != null) { + return mProgress.isIndeterminate(); + } + return mIndeterminate; + } + public void setIndeterminate(boolean indeterminate) { if (mProgress != null) { mProgress.setIndeterminate(indeterminate); @@ -290,13 +302,6 @@ public class ProgressDialog extends AlertDialog { } } - public boolean isIndeterminate() { - if (mProgress != null) { - return mProgress.isIndeterminate(); - } - return mIndeterminate; - } - @Override public void setMessage(CharSequence message) { if (mProgress != null) { @@ -318,9 +323,10 @@ public class ProgressDialog extends AlertDialog { * Change the format of the small text showing current and maximum units * of progress. The default is "%1d/%2d". * Should not be called during the number is progressing. + * * @param format A string passed to {@link String#format String.format()}; - * use "%1d" for the current number and "%2d" for the maximum. If null, - * nothing will be shown. + * use "%1d" for the current number and "%2d" for the maximum. If null, + * nothing will be shown. */ public void setProgressNumberFormat(String format) { mProgressNumberFormat = format; @@ -332,8 +338,9 @@ public class ProgressDialog extends AlertDialog { * The default is * {@link NumberFormat#getPercentInstance() NumberFormat.getPercentageInstnace().} * Should not be called during the number is progressing. + * * @param format An instance of a {@link NumberFormat} to generate the - * percentage text. If null, nothing will be shown. + * percentage text. If null, nothing will be shown. */ public void setProgressPercentFormat(NumberFormat format) { mProgressPercentFormat = format; diff --git a/app/src/main/java/com/hippo/content/ContextLocalWrapper.java b/app/src/main/java/com/hippo/content/ContextLocalWrapper.java index 87e2d5087..698caed61 100644 --- a/app/src/main/java/com/hippo/content/ContextLocalWrapper.java +++ b/app/src/main/java/com/hippo/content/ContextLocalWrapper.java @@ -26,36 +26,37 @@ import android.content.res.Configuration; import android.content.res.Resources; import android.os.Build; import android.os.LocaleList; + import java.util.Locale; public class ContextLocalWrapper extends ContextWrapper { - public ContextLocalWrapper(Context base) { - super(base); - } - - public static ContextLocalWrapper wrap(Context context, Locale newLocale) { - Resources res = context.getResources(); - Configuration configuration = res.getConfiguration(); - - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { - configuration.setLocale(newLocale); - - LocaleList localeList = new LocaleList(newLocale); - LocaleList.setDefault(localeList); - configuration.setLocales(localeList); - - context = context.createConfigurationContext(configuration); - - } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { - configuration.setLocale(newLocale); - context = context.createConfigurationContext(configuration); - - } else { - configuration.locale = newLocale; - res.updateConfiguration(configuration, res.getDisplayMetrics()); + public ContextLocalWrapper(Context base) { + super(base); } - return new ContextLocalWrapper(context); - } + public static ContextLocalWrapper wrap(Context context, Locale newLocale) { + Resources res = context.getResources(); + Configuration configuration = res.getConfiguration(); + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { + configuration.setLocale(newLocale); + + LocaleList localeList = new LocaleList(newLocale); + LocaleList.setDefault(localeList); + configuration.setLocales(localeList); + + context = context.createConfigurationContext(configuration); + + } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { + configuration.setLocale(newLocale); + context = context.createConfigurationContext(configuration); + + } else { + configuration.locale = newLocale; + res.updateConfiguration(configuration, res.getDisplayMetrics()); + } + + return new ContextLocalWrapper(context); + } } diff --git a/app/src/main/java/com/hippo/content/FileProvider.java b/app/src/main/java/com/hippo/content/FileProvider.java index 718b3920f..26407587f 100644 --- a/app/src/main/java/com/hippo/content/FileProvider.java +++ b/app/src/main/java/com/hippo/content/FileProvider.java @@ -16,9 +16,6 @@ package com.hippo.content; -import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT; -import static org.xmlpull.v1.XmlPullParser.START_TAG; - import android.content.ClipData; import android.content.ContentProvider; import android.content.ContentValues; @@ -36,13 +33,19 @@ import android.os.ParcelFileDescriptor; import android.provider.MediaStore; import android.text.TextUtils; import android.webkit.MimeTypeMap; + import androidx.core.content.ContextCompat; + +import org.xmlpull.v1.XmlPullParserException; + import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; import java.util.HashMap; import java.util.Map; -import org.xmlpull.v1.XmlPullParserException; + +import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT; +import static org.xmlpull.v1.XmlPullParser.START_TAG; /** * FileProvider is a special subclass of {@link ContentProvider} that facilitates secure sharing @@ -91,7 +94,7 @@ import org.xmlpull.v1.XmlPullParserException; * >android:grantUriPermissions attribute to true, to allow you * to grant temporary access to files. For example: *
- *<manifest>
+ * <manifest>
  *    ...
  *    <application>
  *        ...
@@ -104,7 +107,7 @@ import org.xmlpull.v1.XmlPullParserException;
  *        </provider>
  *        ...
  *    </application>
- *</manifest>
+ * </manifest> *

* If you want to override any of the default behavior of FileProvider methods, extend * the FileProvider class and use the fully-qualified class name in the android:name @@ -116,19 +119,19 @@ import org.xmlpull.v1.XmlPullParserException; * For example, the following paths element tells FileProvider that you intend to * request content URIs for the images/ subdirectory of your private file area. *

- *<paths xmlns:android="http://schemas.android.com/apk/res/android">
+ * <paths xmlns:android="http://schemas.android.com/apk/res/android">
  *    <files-path name="my_images" path="images/"/>
  *    ...
- *</paths>
- *
+ * </paths> + * *

* The <paths> element must contain one or more of the following child elements: *

*
*
*
- *<files-path name="name" path="path" />
- *
+ * <files-path name="name" path="path" /> + * *
*
* Represents files in the files/ subdirectory of your app's internal storage @@ -137,8 +140,8 @@ import org.xmlpull.v1.XmlPullParserException; *
*
*
- *<cache-path name="name" path="path" />
- *
+ * <cache-path name="name" path="path" /> + * *
*
* Represents files in the cache subdirectory of your app's internal storage area. The root path @@ -147,8 +150,8 @@ import org.xmlpull.v1.XmlPullParserException; *
*
*
- *<external-path name="name" path="path" />
- *
+ * <external-path name="name" path="path" /> + * *
*
* Represents files in the root of the external storage area. The root path of this subdirectory @@ -157,8 +160,8 @@ import org.xmlpull.v1.XmlPullParserException; *
*
*
- *<external-files-path name="name" path="path" />
- *
+ * <external-files-path name="name" path="path" /> + * *
*
* Represents files in the root of your app's external storage area. The root path of this @@ -167,8 +170,8 @@ import org.xmlpull.v1.XmlPullParserException; *
*
*
- *<external-cache-path name="name" path="path" />
- *
+ * <external-cache-path name="name" path="path" /> + * *
*
* Represents files in the root of your app's external cache area. The root path of this @@ -203,11 +206,11 @@ import org.xmlpull.v1.XmlPullParserException; * You must specify a child element of <paths> for each directory that contains * files for which you want content URIs. For example, these XML elements specify two directories: *
- *<paths xmlns:android="http://schemas.android.com/apk/res/android">
+ * <paths xmlns:android="http://schemas.android.com/apk/res/android">
  *    <files-path name="my_images" path="images/"/>
  *    <files-path name="my_docs" path="docs/"/>
- *</paths>
- *
+ * </paths> + * *

* Put the <paths> element and its children in an XML file in your project. * For example, you can add them to a new file called res/xml/file_paths.xml. @@ -219,7 +222,7 @@ import org.xmlpull.v1.XmlPullParserException; * to @xml/file_paths (notice that you don't specify the .xml * extension). For example: *

- *<provider
+ * <provider
  *    android:name="android.support.v4.content.FileProvider"
  *    android:authorities="com.mydomain.fileprovider"
  *    android:exported="false"
@@ -227,8 +230,8 @@ import org.xmlpull.v1.XmlPullParserException;
  *    <meta-data
  *        android:name="android.support.FILE_PROVIDER_PATHS"
  *        android:resource="@xml/file_paths" />
- *</provider>
- *
+ * </provider> + * *

Generating the Content URI for a File

*

* To share a file with another app using a content URI, your app has to generate the content URI. @@ -245,10 +248,10 @@ import org.xmlpull.v1.XmlPullParserException; * default_image.jpg in the images/ subdirectory of your internal storage * add the following code: *

- *File imagePath = new File(Context.getFilesDir(), "images");
- *File newFile = new File(imagePath, "default_image.jpg");
- *Uri contentUri = getUriForFile(getContext(), "com.mydomain.fileprovider", newFile);
- *
+ * File imagePath = new File(Context.getFilesDir(), "images"); + * File newFile = new File(imagePath, "default_image.jpg"); + * Uri contentUri = getUriForFile(getContext(), "com.mydomain.fileprovider", newFile); + * * As a result of the previous snippet, * {@link #getUriForFile(Context, String, File) getUriForFile()} returns the content URI * content://com.mydomain.fileprovider/my_images/default_image.jpg. @@ -348,37 +351,6 @@ public class FileProvider extends ContentProvider { private PathStrategy mStrategy; - /** - * The default FileProvider implementation does not need to be initialized. If you want to - * override this method, you must provide your own subclass of FileProvider. - */ - @Override - public boolean onCreate() { - return true; - } - - /** - * After the FileProvider is instantiated, this method is called to provide the system with - * information about the provider. - * - * @param context A {@link Context} for the current component. - * @param info A {@link ProviderInfo} for the new provider. - */ - @Override - public void attachInfo(Context context, ProviderInfo info) { - super.attachInfo(context, info); - - // Sanity check our security - if (info.exported) { - throw new SecurityException("Provider must not be exported"); - } - if (!info.grantUriPermissions) { - throw new SecurityException("Provider must grant uri permissions"); - } - - mStrategy = getPathStrategy(context, info.authority); - } - /** * Return a content URI for a given {@link File}. Specific temporary * permissions for the content URI can be set with @@ -390,168 +362,20 @@ public class FileProvider extends ContentProvider { * content {@link Uri} for file paths defined in their <paths> * meta-data element. See the Class Overview for more information. * - * @param context A {@link Context} for the current component. + * @param context A {@link Context} for the current component. * @param authority The authority of a {@link FileProvider} defined in a - * {@code } element in your app's manifest. - * @param file A {@link File} pointing to the filename for which you want a - * content {@link Uri}. + * {@code } element in your app's manifest. + * @param file A {@link File} pointing to the filename for which you want a + * content {@link Uri}. * @return A content URI for the file. * @throws IllegalArgumentException When the given {@link File} is outside - * the paths supported by the provider. + * the paths supported by the provider. */ public static Uri getUriForFile(Context context, String authority, File file) { final PathStrategy strategy = getPathStrategy(context, authority); return strategy.getUriForFile(file); } - /** - * Use a content URI returned by - * {@link #getUriForFile(Context, String, File) getUriForFile()} to get information about a file - * managed by the FileProvider. - * FileProvider reports the column names defined in {@link android.provider.MediaStore.MediaColumns}: - *
    - *
  • {@link android.provider.MediaStore.MediaColumns#DISPLAY_NAME}
  • - *
  • {@link android.provider.MediaStore.MediaColumns#SIZE}
  • - *
  • {@link android.provider.MediaStore.MediaColumns#DATA}
  • - *
- * For more information, see - * {@link ContentProvider#query(Uri, String[], String, String[], String) - * ContentProvider.query()}. - * - * @param uri A content URI returned by {@link #getUriForFile}. - * @param projection The list of columns to put into the {@link Cursor}. If null all columns are - * included. - * @param selection Selection criteria to apply. If null then all data that matches the content - * URI is returned. - * @param selectionArgs An array of {@link java.lang.String}, containing arguments to bind to - * the selection parameter. The query method scans selection from left to - * right and iterates through selectionArgs, replacing the current "?" character in - * selection with the value at the current position in selectionArgs. The - * values are bound to selection as {@link java.lang.String} values. - * @param sortOrder A {@link java.lang.String} containing the column name(s) on which to sort - * the resulting {@link Cursor}. - * @return A {@link Cursor} containing the results of the query. - * - */ - @Override - public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, - String sortOrder) { - // ContentProvider has already checked granted permissions - final File file = mStrategy.getFileForUri(uri); - - if (projection == null) { - projection = COLUMNS; - } - - String[] cols = new String[projection.length]; - Object[] values = new Object[projection.length]; - int i = 0; - for (String col : projection) { - if (MediaStore.MediaColumns.DISPLAY_NAME.equals(col)) { - cols[i] = MediaStore.MediaColumns.DISPLAY_NAME; - values[i++] = file.getName(); - } else if (MediaStore.MediaColumns.SIZE.equals(col)) { - cols[i] = MediaStore.MediaColumns.SIZE; - values[i++] = file.length(); - } else if (MediaStore.MediaColumns.DATA.equals(col)) { - cols[i] = MediaStore.MediaColumns.DATA; - values[i++] = file.getPath(); - } - } - - cols = copyOf(cols, i); - values = copyOf(values, i); - - final MatrixCursor cursor = new MatrixCursor(cols, 1); - cursor.addRow(values); - return cursor; - } - - /** - * Returns the MIME type of a content URI returned by - * {@link #getUriForFile(Context, String, File) getUriForFile()}. - * - * @param uri A content URI returned by - * {@link #getUriForFile(Context, String, File) getUriForFile()}. - * @return If the associated file has an extension, the MIME type associated with that - * extension; otherwise application/octet-stream. - */ - @Override - public String getType(Uri uri) { - // ContentProvider has already checked granted permissions - final File file = mStrategy.getFileForUri(uri); - - final int lastDot = file.getName().lastIndexOf('.'); - if (lastDot >= 0) { - final String extension = file.getName().substring(lastDot + 1); - final String mime = MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension); - if (mime != null) { - return mime; - } - } - - return "application/octet-stream"; - } - - /** - * By default, this method throws an {@link java.lang.UnsupportedOperationException}. You must - * subclass FileProvider if you want to provide different functionality. - */ - @Override - public Uri insert(Uri uri, ContentValues values) { - throw new UnsupportedOperationException("No external inserts"); - } - - /** - * By default, this method throws an {@link java.lang.UnsupportedOperationException}. You must - * subclass FileProvider if you want to provide different functionality. - */ - @Override - public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) { - throw new UnsupportedOperationException("No external updates"); - } - - /** - * Deletes the file associated with the specified content URI, as - * returned by {@link #getUriForFile(Context, String, File) getUriForFile()}. Notice that this - * method does not throw an {@link java.io.IOException}; you must check its return value. - * - * @param uri A content URI for a file, as returned by - * {@link #getUriForFile(Context, String, File) getUriForFile()}. - * @param selection Ignored. Set to {@code null}. - * @param selectionArgs Ignored. Set to {@code null}. - * @return 1 if the delete succeeds; otherwise, 0. - */ - @Override - public int delete(Uri uri, String selection, String[] selectionArgs) { - // ContentProvider has already checked granted permissions - final File file = mStrategy.getFileForUri(uri); - return file.delete() ? 1 : 0; - } - - /** - * By default, FileProvider automatically returns the - * {@link ParcelFileDescriptor} for a file associated with a content:// - * {@link Uri}. To get the {@link ParcelFileDescriptor}, call - * {@link android.content.ContentResolver#openFileDescriptor(Uri, String) - * ContentResolver.openFileDescriptor}. - * - * To override this method, you must provide your own subclass of FileProvider. - * - * @param uri A content URI associated with a file, as returned by - * {@link #getUriForFile(Context, String, File) getUriForFile()}. - * @param mode Access mode for the file. May be "r" for read-only access, "rw" for read and - * write access, or "rwt" for read and write access that truncates any existing file. - * @return A new {@link ParcelFileDescriptor} with which you can access the file. - */ - @Override - public ParcelFileDescriptor openFile(Uri uri, String mode) throws FileNotFoundException { - // ContentProvider has already checked granted permissions - final File file = mStrategy.getFileForUri(uri); - final int fileMode = modeToMode(mode); - return ParcelFileDescriptor.open(file, fileMode); - } - /** * Return {@link PathStrategy} for given authority, either by parsing or * returning from cache. @@ -633,6 +457,234 @@ public class FileProvider extends ContentProvider { return strat; } + /** + * Copied from ContentResolver.java + */ + private static int modeToMode(String mode) { + int modeBits; + if ("r".equals(mode)) { + modeBits = ParcelFileDescriptor.MODE_READ_ONLY; + } else if ("w".equals(mode) || "wt".equals(mode)) { + modeBits = ParcelFileDescriptor.MODE_WRITE_ONLY + | ParcelFileDescriptor.MODE_CREATE + | ParcelFileDescriptor.MODE_TRUNCATE; + } else if ("wa".equals(mode)) { + modeBits = ParcelFileDescriptor.MODE_WRITE_ONLY + | ParcelFileDescriptor.MODE_CREATE + | ParcelFileDescriptor.MODE_APPEND; + } else if ("rw".equals(mode)) { + modeBits = ParcelFileDescriptor.MODE_READ_WRITE + | ParcelFileDescriptor.MODE_CREATE; + } else if ("rwt".equals(mode)) { + modeBits = ParcelFileDescriptor.MODE_READ_WRITE + | ParcelFileDescriptor.MODE_CREATE + | ParcelFileDescriptor.MODE_TRUNCATE; + } else { + throw new IllegalArgumentException("Invalid mode: " + mode); + } + return modeBits; + } + + private static File buildPath(File base, String... segments) { + File cur = base; + for (String segment : segments) { + if (segment != null) { + cur = new File(cur, segment); + } + } + return cur; + } + + private static String[] copyOf(String[] original, int newLength) { + final String[] result = new String[newLength]; + System.arraycopy(original, 0, result, 0, newLength); + return result; + } + + private static Object[] copyOf(Object[] original, int newLength) { + final Object[] result = new Object[newLength]; + System.arraycopy(original, 0, result, 0, newLength); + return result; + } + + /** + * The default FileProvider implementation does not need to be initialized. If you want to + * override this method, you must provide your own subclass of FileProvider. + */ + @Override + public boolean onCreate() { + return true; + } + + /** + * After the FileProvider is instantiated, this method is called to provide the system with + * information about the provider. + * + * @param context A {@link Context} for the current component. + * @param info A {@link ProviderInfo} for the new provider. + */ + @Override + public void attachInfo(Context context, ProviderInfo info) { + super.attachInfo(context, info); + + // Sanity check our security + if (info.exported) { + throw new SecurityException("Provider must not be exported"); + } + if (!info.grantUriPermissions) { + throw new SecurityException("Provider must grant uri permissions"); + } + + mStrategy = getPathStrategy(context, info.authority); + } + + /** + * Use a content URI returned by + * {@link #getUriForFile(Context, String, File) getUriForFile()} to get information about a file + * managed by the FileProvider. + * FileProvider reports the column names defined in {@link android.provider.MediaStore.MediaColumns}: + *
    + *
  • {@link android.provider.MediaStore.MediaColumns#DISPLAY_NAME}
  • + *
  • {@link android.provider.MediaStore.MediaColumns#SIZE}
  • + *
  • {@link android.provider.MediaStore.MediaColumns#DATA}
  • + *
+ * For more information, see + * {@link ContentProvider#query(Uri, String[], String, String[], String) + * ContentProvider.query()}. + * + * @param uri A content URI returned by {@link #getUriForFile}. + * @param projection The list of columns to put into the {@link Cursor}. If null all columns are + * included. + * @param selection Selection criteria to apply. If null then all data that matches the content + * URI is returned. + * @param selectionArgs An array of {@link java.lang.String}, containing arguments to bind to + * the selection parameter. The query method scans selection from left to + * right and iterates through selectionArgs, replacing the current "?" character in + * selection with the value at the current position in selectionArgs. The + * values are bound to selection as {@link java.lang.String} values. + * @param sortOrder A {@link java.lang.String} containing the column name(s) on which to sort + * the resulting {@link Cursor}. + * @return A {@link Cursor} containing the results of the query. + */ + @Override + public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, + String sortOrder) { + // ContentProvider has already checked granted permissions + final File file = mStrategy.getFileForUri(uri); + + if (projection == null) { + projection = COLUMNS; + } + + String[] cols = new String[projection.length]; + Object[] values = new Object[projection.length]; + int i = 0; + for (String col : projection) { + if (MediaStore.MediaColumns.DISPLAY_NAME.equals(col)) { + cols[i] = MediaStore.MediaColumns.DISPLAY_NAME; + values[i++] = file.getName(); + } else if (MediaStore.MediaColumns.SIZE.equals(col)) { + cols[i] = MediaStore.MediaColumns.SIZE; + values[i++] = file.length(); + } else if (MediaStore.MediaColumns.DATA.equals(col)) { + cols[i] = MediaStore.MediaColumns.DATA; + values[i++] = file.getPath(); + } + } + + cols = copyOf(cols, i); + values = copyOf(values, i); + + final MatrixCursor cursor = new MatrixCursor(cols, 1); + cursor.addRow(values); + return cursor; + } + + /** + * Returns the MIME type of a content URI returned by + * {@link #getUriForFile(Context, String, File) getUriForFile()}. + * + * @param uri A content URI returned by + * {@link #getUriForFile(Context, String, File) getUriForFile()}. + * @return If the associated file has an extension, the MIME type associated with that + * extension; otherwise application/octet-stream. + */ + @Override + public String getType(Uri uri) { + // ContentProvider has already checked granted permissions + final File file = mStrategy.getFileForUri(uri); + + final int lastDot = file.getName().lastIndexOf('.'); + if (lastDot >= 0) { + final String extension = file.getName().substring(lastDot + 1); + final String mime = MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension); + if (mime != null) { + return mime; + } + } + + return "application/octet-stream"; + } + + /** + * By default, this method throws an {@link java.lang.UnsupportedOperationException}. You must + * subclass FileProvider if you want to provide different functionality. + */ + @Override + public Uri insert(Uri uri, ContentValues values) { + throw new UnsupportedOperationException("No external inserts"); + } + + /** + * By default, this method throws an {@link java.lang.UnsupportedOperationException}. You must + * subclass FileProvider if you want to provide different functionality. + */ + @Override + public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) { + throw new UnsupportedOperationException("No external updates"); + } + + /** + * Deletes the file associated with the specified content URI, as + * returned by {@link #getUriForFile(Context, String, File) getUriForFile()}. Notice that this + * method does not throw an {@link java.io.IOException}; you must check its return value. + * + * @param uri A content URI for a file, as returned by + * {@link #getUriForFile(Context, String, File) getUriForFile()}. + * @param selection Ignored. Set to {@code null}. + * @param selectionArgs Ignored. Set to {@code null}. + * @return 1 if the delete succeeds; otherwise, 0. + */ + @Override + public int delete(Uri uri, String selection, String[] selectionArgs) { + // ContentProvider has already checked granted permissions + final File file = mStrategy.getFileForUri(uri); + return file.delete() ? 1 : 0; + } + + /** + * By default, FileProvider automatically returns the + * {@link ParcelFileDescriptor} for a file associated with a content:// + * {@link Uri}. To get the {@link ParcelFileDescriptor}, call + * {@link android.content.ContentResolver#openFileDescriptor(Uri, String) + * ContentResolver.openFileDescriptor}. + *

+ * To override this method, you must provide your own subclass of FileProvider. + * + * @param uri A content URI associated with a file, as returned by + * {@link #getUriForFile(Context, String, File) getUriForFile()}. + * @param mode Access mode for the file. May be "r" for read-only access, "rw" for read and + * write access, or "rwt" for read and write access that truncates any existing file. + * @return A new {@link ParcelFileDescriptor} with which you can access the file. + */ + @Override + public ParcelFileDescriptor openFile(Uri uri, String mode) throws FileNotFoundException { + // ContentProvider has already checked granted permissions + final File file = mStrategy.getFileForUri(uri); + final int fileMode = modeToMode(mode); + return ParcelFileDescriptor.open(file, fileMode); + } + /** * Strategy for mapping between {@link File} and {@link Uri}. *

@@ -650,12 +702,12 @@ public class FileProvider extends ContentProvider { /** * Return a {@link Uri} that represents the given {@link File}. */ - public Uri getUriForFile(File file); + Uri getUriForFile(File file); /** * Return a {@link File} that represents the given {@link Uri}. */ - public File getFileForUri(Uri uri); + File getFileForUri(Uri uri); } /** @@ -761,54 +813,4 @@ public class FileProvider extends ContentProvider { return file; } } - - /** - * Copied from ContentResolver.java - */ - private static int modeToMode(String mode) { - int modeBits; - if ("r".equals(mode)) { - modeBits = ParcelFileDescriptor.MODE_READ_ONLY; - } else if ("w".equals(mode) || "wt".equals(mode)) { - modeBits = ParcelFileDescriptor.MODE_WRITE_ONLY - | ParcelFileDescriptor.MODE_CREATE - | ParcelFileDescriptor.MODE_TRUNCATE; - } else if ("wa".equals(mode)) { - modeBits = ParcelFileDescriptor.MODE_WRITE_ONLY - | ParcelFileDescriptor.MODE_CREATE - | ParcelFileDescriptor.MODE_APPEND; - } else if ("rw".equals(mode)) { - modeBits = ParcelFileDescriptor.MODE_READ_WRITE - | ParcelFileDescriptor.MODE_CREATE; - } else if ("rwt".equals(mode)) { - modeBits = ParcelFileDescriptor.MODE_READ_WRITE - | ParcelFileDescriptor.MODE_CREATE - | ParcelFileDescriptor.MODE_TRUNCATE; - } else { - throw new IllegalArgumentException("Invalid mode: " + mode); - } - return modeBits; - } - - private static File buildPath(File base, String... segments) { - File cur = base; - for (String segment : segments) { - if (segment != null) { - cur = new File(cur, segment); - } - } - return cur; - } - - private static String[] copyOf(String[] original, int newLength) { - final String[] result = new String[newLength]; - System.arraycopy(original, 0, result, 0, newLength); - return result; - } - - private static Object[] copyOf(Object[] original, int newLength) { - final Object[] result = new Object[newLength]; - System.arraycopy(original, 0, result, 0, newLength); - return result; - } } diff --git a/app/src/main/java/com/hippo/content/RecordingApplication.java b/app/src/main/java/com/hippo/content/RecordingApplication.java index 76ff3a4ad..d15715ebf 100644 --- a/app/src/main/java/com/hippo/content/RecordingApplication.java +++ b/app/src/main/java/com/hippo/content/RecordingApplication.java @@ -22,7 +22,9 @@ package com.hippo.content; import android.app.Activity; import android.os.Bundle; + import com.hippo.scene.SceneApplication; + import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.Iterator; @@ -31,60 +33,65 @@ import java.util.List; public abstract class RecordingApplication extends SceneApplication { - private List> list = new LinkedList<>(); + private List> list = new LinkedList<>(); - @Override - public void onCreate() { - super.onCreate(); + @Override + public void onCreate() { + super.onCreate(); - registerActivityLifecycleCallbacks(new ActivityLifecycleCallbacks() { - @Override - public void onActivityCreated(Activity activity, Bundle savedInstanceState) { - list.add(new java.lang.ref.WeakReference<>(activity)); - } + registerActivityLifecycleCallbacks(new ActivityLifecycleCallbacks() { + @Override + public void onActivityCreated(Activity activity, Bundle savedInstanceState) { + list.add(new java.lang.ref.WeakReference<>(activity)); + } - @Override - public void onActivityDestroyed(Activity activity) { - Iterator> iterator = list.iterator(); - while (iterator.hasNext()) { - WeakReference reference = iterator.next(); - Activity a = reference.get(); - // Remove current activity and null - if (a == null || a == activity) { - iterator.remove(); - } + @Override + public void onActivityDestroyed(Activity activity) { + Iterator> iterator = list.iterator(); + while (iterator.hasNext()) { + WeakReference reference = iterator.next(); + Activity a = reference.get(); + // Remove current activity and null + if (a == null || a == activity) { + iterator.remove(); + } + } + } + + @Override + public void onActivityStarted(Activity activity) { + } + + @Override + public void onActivityResumed(Activity activity) { + } + + @Override + public void onActivityPaused(Activity activity) { + } + + @Override + public void onActivityStopped(Activity activity) { + } + + @Override + public void onActivitySaveInstanceState(Activity activity, Bundle outState) { + } + }); + } + + public void recreate() { + // Copy list + ArrayList listCopy = new ArrayList<>(list.size()); + for (WeakReference reference : list) { + Activity activity = reference.get(); + if (activity == null) continue; + listCopy.add(activity); } - } - @Override - public void onActivityStarted(Activity activity) {} - - @Override - public void onActivityResumed(Activity activity) {} - - @Override - public void onActivityPaused(Activity activity) {} - - @Override - public void onActivityStopped(Activity activity) {} - - @Override - public void onActivitySaveInstanceState(Activity activity, Bundle outState) {} - }); - } - - public void recreate() { - // Copy list - ArrayList listCopy = new ArrayList<>(list.size()); - for (WeakReference reference: list) { - Activity activity = reference.get(); - if (activity == null) continue; - listCopy.add(activity); + // Finish all activities + for (int i = listCopy.size() - 1; i >= 0; --i) { + listCopy.get(i).recreate(); + } } - - // Finish all activities - for (int i = listCopy.size() - 1; i >= 0; --i) { - listCopy.get(i).recreate(); - } - } } diff --git a/app/src/main/java/com/hippo/database/MSQLiteBuilder.java b/app/src/main/java/com/hippo/database/MSQLiteBuilder.java index 956a881b0..1c0fabba9 100644 --- a/app/src/main/java/com/hippo/database/MSQLiteBuilder.java +++ b/app/src/main/java/com/hippo/database/MSQLiteBuilder.java @@ -23,6 +23,7 @@ package com.hippo.database; import android.content.Context; import android.database.sqlite.SQLiteOpenHelper; import android.util.SparseArray; + import java.util.ArrayList; import java.util.HashMap; import java.util.List; @@ -30,101 +31,101 @@ import java.util.Map; public class MSQLiteBuilder { - public static final String COLUMN_ID = "_id"; + public static final String COLUMN_ID = "_id"; - private static final Map JAVA_TYPE_TO_SQLITE_TYPE = new HashMap<>(); + private static final Map JAVA_TYPE_TO_SQLITE_TYPE = new HashMap<>(); - static { - JAVA_TYPE_TO_SQLITE_TYPE.put(boolean.class, "INTEGER NOT NULL DEFAULT 0"); - JAVA_TYPE_TO_SQLITE_TYPE.put(byte.class, "INTEGER NOT NULL DEFAULT 0"); - JAVA_TYPE_TO_SQLITE_TYPE.put(short.class, "INTEGER NOT NULL DEFAULT 0"); - JAVA_TYPE_TO_SQLITE_TYPE.put(int.class, "INTEGER NOT NULL DEFAULT 0"); - JAVA_TYPE_TO_SQLITE_TYPE.put(long.class, "INTEGER NOT NULL DEFAULT 0"); - JAVA_TYPE_TO_SQLITE_TYPE.put(float.class, "REAL NOT NULL DEFAULT 0"); - JAVA_TYPE_TO_SQLITE_TYPE.put(double.class, "REAL NOT NULL DEFAULT 0"); - JAVA_TYPE_TO_SQLITE_TYPE.put(String.class, "TEXT"); - } - - private static String javaTypeToSQLiteType(Class clazz) { - String type = JAVA_TYPE_TO_SQLITE_TYPE.get(clazz); - if (type == null) { - throw new IllegalStateException("Unknown type: " + clazz); + static { + JAVA_TYPE_TO_SQLITE_TYPE.put(boolean.class, "INTEGER NOT NULL DEFAULT 0"); + JAVA_TYPE_TO_SQLITE_TYPE.put(byte.class, "INTEGER NOT NULL DEFAULT 0"); + JAVA_TYPE_TO_SQLITE_TYPE.put(short.class, "INTEGER NOT NULL DEFAULT 0"); + JAVA_TYPE_TO_SQLITE_TYPE.put(int.class, "INTEGER NOT NULL DEFAULT 0"); + JAVA_TYPE_TO_SQLITE_TYPE.put(long.class, "INTEGER NOT NULL DEFAULT 0"); + JAVA_TYPE_TO_SQLITE_TYPE.put(float.class, "REAL NOT NULL DEFAULT 0"); + JAVA_TYPE_TO_SQLITE_TYPE.put(double.class, "REAL NOT NULL DEFAULT 0"); + JAVA_TYPE_TO_SQLITE_TYPE.put(String.class, "TEXT"); } - return type; - } - private int version = 0; - private List statements; - private SparseArray> statementsMap = new SparseArray<>(); + private int version = 0; + private List statements; + private SparseArray> statementsMap = new SparseArray<>(); - /** - * Bump database version. - */ - public MSQLiteBuilder version(int version) { - if (version <= this.version) { - throw new IllegalStateException("New version must be bigger than current version. " - + "current version: " + this.version + ", new version: " + version + "."); + private static String javaTypeToSQLiteType(Class clazz) { + String type = JAVA_TYPE_TO_SQLITE_TYPE.get(clazz); + if (type == null) { + throw new IllegalStateException("Unknown type: " + clazz); + } + return type; } - this.version = version; - this.statements = new ArrayList<>(); - this.statementsMap.put(version, this.statements); - return this; - } - /** - * Creates a table with int {@link #COLUMN_ID} primary key. - */ - public MSQLiteBuilder createTable(String table) { - return createTable(table, COLUMN_ID, int.class); - } - - /** - * Creates a table. - */ - public MSQLiteBuilder createTable(String table, String column, Class clazz) { - return statement("CREATE TABLE " + table + " (" + column + " " + javaTypeToSQLiteType(clazz) + " PRIMARY KEY);"); - } - - /** - * Drops a table. - */ - public MSQLiteBuilder dropTable(String table) { - return statement("DROP TABLE " + table + ";"); - } - - /** - * Inserts a column to the table. - */ - public MSQLiteBuilder insertColumn(String table, String column, Class clazz) { - return statement("ALTER TABLE " + table + " ADD COLUMN " + column + " " + javaTypeToSQLiteType(clazz) + ";"); - } - - /** - * Add a statement. - */ - public MSQLiteBuilder statement(String statement) { - if (version == 0 || statements == null) { - throw new IllegalStateException("Call version() first!"); + /** + * Bump database version. + */ + public MSQLiteBuilder version(int version) { + if (version <= this.version) { + throw new IllegalStateException("New version must be bigger than current version. " + + "current version: " + this.version + ", new version: " + version + "."); + } + this.version = version; + this.statements = new ArrayList<>(); + this.statementsMap.put(version, this.statements); + return this; } - statements.add(statement); - return this; - } - /** - * Build a SQLiteOpenHelper from it. - */ - public SQLiteOpenHelper build(Context context, String name, int version) { - return new MSQLiteOpenHelper(context, name, version, this); - } - - List getStatements(int oldVersion, int newVersion) { - List result = new ArrayList<>(); - for (int i = oldVersion + 1; i <= newVersion; ++i) { - List list = statementsMap.get(i); - if (list != null) { - result.addAll(list); - } + /** + * Creates a table with int {@link #COLUMN_ID} primary key. + */ + public MSQLiteBuilder createTable(String table) { + return createTable(table, COLUMN_ID, int.class); + } + + /** + * Creates a table. + */ + public MSQLiteBuilder createTable(String table, String column, Class clazz) { + return statement("CREATE TABLE " + table + " (" + column + " " + javaTypeToSQLiteType(clazz) + " PRIMARY KEY);"); + } + + /** + * Drops a table. + */ + public MSQLiteBuilder dropTable(String table) { + return statement("DROP TABLE " + table + ";"); + } + + /** + * Inserts a column to the table. + */ + public MSQLiteBuilder insertColumn(String table, String column, Class clazz) { + return statement("ALTER TABLE " + table + " ADD COLUMN " + column + " " + javaTypeToSQLiteType(clazz) + ";"); + } + + /** + * Add a statement. + */ + public MSQLiteBuilder statement(String statement) { + if (version == 0 || statements == null) { + throw new IllegalStateException("Call version() first!"); + } + statements.add(statement); + return this; + } + + /** + * Build a SQLiteOpenHelper from it. + */ + public SQLiteOpenHelper build(Context context, String name, int version) { + return new MSQLiteOpenHelper(context, name, version, this); + } + + List getStatements(int oldVersion, int newVersion) { + List result = new ArrayList<>(); + for (int i = oldVersion + 1; i <= newVersion; ++i) { + List list = statementsMap.get(i); + if (list != null) { + result.addAll(list); + } + } + return result; } - return result; - } } diff --git a/app/src/main/java/com/hippo/database/MSQLiteOpenHelper.java b/app/src/main/java/com/hippo/database/MSQLiteOpenHelper.java index 57cd40b84..2a74aeca1 100644 --- a/app/src/main/java/com/hippo/database/MSQLiteOpenHelper.java +++ b/app/src/main/java/com/hippo/database/MSQLiteOpenHelper.java @@ -23,34 +23,35 @@ package com.hippo.database; import android.content.Context; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteOpenHelper; + import com.hippo.util.SqlUtils; class MSQLiteOpenHelper extends SQLiteOpenHelper { - private final int version; - private final MSQLiteBuilder builder; + private final int version; + private final MSQLiteBuilder builder; - public MSQLiteOpenHelper(Context context, String name, int version, MSQLiteBuilder builder) { - super(context, name, null, version); - this.version = version; - this.builder = builder; - } - - @Override - public void onCreate(SQLiteDatabase db) { - onUpgrade(db, 0, version); - } - - @Override - public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { - for (String command : builder.getStatements(oldVersion, newVersion)) { - db.execSQL(command); + public MSQLiteOpenHelper(Context context, String name, int version, MSQLiteBuilder builder) { + super(context, name, null, version); + this.version = version; + this.builder = builder; } - } - @Override - public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) { - SqlUtils.dropAllTable(db); - onCreate(db); - } + @Override + public void onCreate(SQLiteDatabase db) { + onUpgrade(db, 0, version); + } + + @Override + public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { + for (String command : builder.getStatements(oldVersion, newVersion)) { + db.execSQL(command); + } + } + + @Override + public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) { + SqlUtils.dropAllTable(db); + onCreate(db); + } } diff --git a/app/src/main/java/com/hippo/drawable/BatteryDrawable.java b/app/src/main/java/com/hippo/drawable/BatteryDrawable.java index ff229c196..557edb171 100644 --- a/app/src/main/java/com/hippo/drawable/BatteryDrawable.java +++ b/app/src/main/java/com/hippo/drawable/BatteryDrawable.java @@ -28,21 +28,18 @@ import com.hippo.yorozuya.MathUtils; public class BatteryDrawable extends Drawable { + public static final int WARN_LIMIT = 15; @SuppressWarnings("unused") private static final String TAG = BatteryDrawable.class.getSimpleName(); - - public static final int WARN_LIMIT = 15; - - private int mColor = Color.WHITE; - private int mWarningColor = Color.RED; - private int mElect = -1; private final Paint mPaint; - private final Rect mTopRect; private final Rect mBottomRect; private final Rect mRightRect; private final Rect mHeadRect; private final Rect mElectRect; + private int mColor = Color.WHITE; + private int mWarningColor = Color.RED; + private int mElect = -1; private int mStart; private int mStop; diff --git a/app/src/main/java/com/hippo/drawable/BitmapPool.java b/app/src/main/java/com/hippo/drawable/BitmapPool.java index 6f6868ca9..81b3730d6 100644 --- a/app/src/main/java/com/hippo/drawable/BitmapPool.java +++ b/app/src/main/java/com/hippo/drawable/BitmapPool.java @@ -18,7 +18,9 @@ package com.hippo.drawable; import android.graphics.Bitmap; import android.util.Log; + import androidx.annotation.Nullable; + import java.lang.ref.WeakReference; import java.util.Iterator; import java.util.LinkedHashSet; diff --git a/app/src/main/java/com/hippo/drawable/DrawableWrapper.java b/app/src/main/java/com/hippo/drawable/DrawableWrapper.java index 7e6f1d86d..4457302d4 100644 --- a/app/src/main/java/com/hippo/drawable/DrawableWrapper.java +++ b/app/src/main/java/com/hippo/drawable/DrawableWrapper.java @@ -23,158 +23,159 @@ import android.graphics.PorterDuff; import android.graphics.Rect; import android.graphics.Region; import android.graphics.drawable.Drawable; + import androidx.annotation.NonNull; import androidx.core.graphics.drawable.DrawableCompat; public class DrawableWrapper extends Drawable implements Drawable.Callback { - private Drawable mDrawable; + private Drawable mDrawable; - public DrawableWrapper(Drawable drawable) { - this.setWrappedDrawable(drawable); - } - - public void draw(@NonNull Canvas canvas) { - this.mDrawable.draw(canvas); - } - - protected void onBoundsChange(Rect bounds) { - this.mDrawable.setBounds(bounds); - } - - public void setChangingConfigurations(int configs) { - this.mDrawable.setChangingConfigurations(configs); - } - - public int getChangingConfigurations() { - return this.mDrawable.getChangingConfigurations(); - } - - public void setDither(boolean dither) { - this.mDrawable.setDither(dither); - } - - public void setFilterBitmap(boolean filter) { - this.mDrawable.setFilterBitmap(filter); - } - - public void setAlpha(int alpha) { - this.mDrawable.setAlpha(alpha); - } - - public void setColorFilter(ColorFilter cf) { - this.mDrawable.setColorFilter(cf); - } - - public boolean isStateful() { - return this.mDrawable.isStateful(); - } - - public boolean setState(@NonNull int[] stateSet) { - return this.mDrawable.setState(stateSet); - } - - @NonNull - public int[] getState() { - return this.mDrawable.getState(); - } - - public void jumpToCurrentState() { - this.mDrawable.jumpToCurrentState(); - } - - @NonNull - public Drawable getCurrent() { - return this.mDrawable.getCurrent(); - } - - public boolean setVisible(boolean visible, boolean restart) { - return super.setVisible(visible, restart) || this.mDrawable.setVisible(visible, restart); - } - - public int getOpacity() { - return this.mDrawable.getOpacity(); - } - - public Region getTransparentRegion() { - return this.mDrawable.getTransparentRegion(); - } - - public int getIntrinsicWidth() { - return this.mDrawable.getIntrinsicWidth(); - } - - public int getIntrinsicHeight() { - return this.mDrawable.getIntrinsicHeight(); - } - - public int getMinimumWidth() { - return this.mDrawable.getMinimumWidth(); - } - - public int getMinimumHeight() { - return this.mDrawable.getMinimumHeight(); - } - - public boolean getPadding(@NonNull Rect padding) { - return this.mDrawable.getPadding(padding); - } - - public void invalidateDrawable(@NonNull Drawable who) { - this.invalidateSelf(); - } - - public void scheduleDrawable(@NonNull Drawable who, @NonNull Runnable what, long when) { - this.scheduleSelf(what, when); - } - - public void unscheduleDrawable(@NonNull Drawable who, @NonNull Runnable what) { - this.unscheduleSelf(what); - } - - protected boolean onLevelChange(int level) { - return this.mDrawable.setLevel(level); - } - - public void setAutoMirrored(boolean mirrored) { - DrawableCompat.setAutoMirrored(this.mDrawable, mirrored); - } - - public boolean isAutoMirrored() { - return DrawableCompat.isAutoMirrored(this.mDrawable); - } - - public void setTint(int tint) { - DrawableCompat.setTint(this.mDrawable, tint); - } - - public void setTintList(ColorStateList tint) { - DrawableCompat.setTintList(this.mDrawable, tint); - } - - public void setTintMode(@NonNull PorterDuff.Mode tintMode) { - DrawableCompat.setTintMode(this.mDrawable, tintMode); - } - - public void setHotspot(float x, float y) { - DrawableCompat.setHotspot(this.mDrawable, x, y); - } - - public void setHotspotBounds(int left, int top, int right, int bottom) { - DrawableCompat.setHotspotBounds(this.mDrawable, left, top, right, bottom); - } - - public Drawable getWrappedDrawable() { - return this.mDrawable; - } - - public void setWrappedDrawable(Drawable drawable) { - if (this.mDrawable != null) { - this.mDrawable.setCallback(null); + public DrawableWrapper(Drawable drawable) { + this.setWrappedDrawable(drawable); } - this.mDrawable = drawable; - if (drawable != null) { - drawable.setCallback(this); + public void draw(@NonNull Canvas canvas) { + this.mDrawable.draw(canvas); + } + + protected void onBoundsChange(Rect bounds) { + this.mDrawable.setBounds(bounds); + } + + public int getChangingConfigurations() { + return this.mDrawable.getChangingConfigurations(); + } + + public void setChangingConfigurations(int configs) { + this.mDrawable.setChangingConfigurations(configs); + } + + public void setDither(boolean dither) { + this.mDrawable.setDither(dither); + } + + public void setFilterBitmap(boolean filter) { + this.mDrawable.setFilterBitmap(filter); + } + + public void setAlpha(int alpha) { + this.mDrawable.setAlpha(alpha); + } + + public void setColorFilter(ColorFilter cf) { + this.mDrawable.setColorFilter(cf); + } + + public boolean isStateful() { + return this.mDrawable.isStateful(); + } + + public boolean setState(@NonNull int[] stateSet) { + return this.mDrawable.setState(stateSet); + } + + @NonNull + public int[] getState() { + return this.mDrawable.getState(); + } + + public void jumpToCurrentState() { + this.mDrawable.jumpToCurrentState(); + } + + @NonNull + public Drawable getCurrent() { + return this.mDrawable.getCurrent(); + } + + public boolean setVisible(boolean visible, boolean restart) { + return super.setVisible(visible, restart) || this.mDrawable.setVisible(visible, restart); + } + + public int getOpacity() { + return this.mDrawable.getOpacity(); + } + + public Region getTransparentRegion() { + return this.mDrawable.getTransparentRegion(); + } + + public int getIntrinsicWidth() { + return this.mDrawable.getIntrinsicWidth(); + } + + public int getIntrinsicHeight() { + return this.mDrawable.getIntrinsicHeight(); + } + + public int getMinimumWidth() { + return this.mDrawable.getMinimumWidth(); + } + + public int getMinimumHeight() { + return this.mDrawable.getMinimumHeight(); + } + + public boolean getPadding(@NonNull Rect padding) { + return this.mDrawable.getPadding(padding); + } + + public void invalidateDrawable(@NonNull Drawable who) { + this.invalidateSelf(); + } + + public void scheduleDrawable(@NonNull Drawable who, @NonNull Runnable what, long when) { + this.scheduleSelf(what, when); + } + + public void unscheduleDrawable(@NonNull Drawable who, @NonNull Runnable what) { + this.unscheduleSelf(what); + } + + protected boolean onLevelChange(int level) { + return this.mDrawable.setLevel(level); + } + + public boolean isAutoMirrored() { + return DrawableCompat.isAutoMirrored(this.mDrawable); + } + + public void setAutoMirrored(boolean mirrored) { + DrawableCompat.setAutoMirrored(this.mDrawable, mirrored); + } + + public void setTint(int tint) { + DrawableCompat.setTint(this.mDrawable, tint); + } + + public void setTintList(ColorStateList tint) { + DrawableCompat.setTintList(this.mDrawable, tint); + } + + public void setTintMode(@NonNull PorterDuff.Mode tintMode) { + DrawableCompat.setTintMode(this.mDrawable, tintMode); + } + + public void setHotspot(float x, float y) { + DrawableCompat.setHotspot(this.mDrawable, x, y); + } + + public void setHotspotBounds(int left, int top, int right, int bottom) { + DrawableCompat.setHotspotBounds(this.mDrawable, left, top, right, bottom); + } + + public Drawable getWrappedDrawable() { + return this.mDrawable; + } + + public void setWrappedDrawable(Drawable drawable) { + if (this.mDrawable != null) { + this.mDrawable.setCallback(null); + } + + this.mDrawable = drawable; + if (drawable != null) { + drawable.setCallback(this); + } } - } } diff --git a/app/src/main/java/com/hippo/drawable/DrawerArrowDrawable.java b/app/src/main/java/com/hippo/drawable/DrawerArrowDrawable.java index 402475718..7d78ac74b 100644 --- a/app/src/main/java/com/hippo/drawable/DrawerArrowDrawable.java +++ b/app/src/main/java/com/hippo/drawable/DrawerArrowDrawable.java @@ -27,7 +27,9 @@ import android.graphics.PixelFormat; import android.graphics.Rect; import android.graphics.drawable.Drawable; import android.os.Build; + import androidx.annotation.ColorInt; + import com.hippo.ehviewer.R; import com.hippo.yorozuya.MathUtils; @@ -36,10 +38,9 @@ import com.hippo.yorozuya.MathUtils; */ public class DrawerArrowDrawable extends Drawable { - private final Paint mPaint = new Paint(); - // The angle in degrees that the arrow head is inclined at. private static final float ARROW_HEAD_ANGLE = (float) Math.toRadians(45); + private final Paint mPaint = new Paint(); private final float mBarThickness; // The length of top and bottom bars when they merge into an arrow private final float mTopBottomArrowSize; @@ -56,12 +57,12 @@ public class DrawerArrowDrawable extends Drawable { private final Path mPath = new Path(); // The reported intrinsic size of the drawable. private final int mSize; + // the amount that overlaps w/ bar size when rotation is max + private final float mMaxCutForBarSize; // Whether we should mirror animation when animation is reversed. private boolean mVerticalMirror = false; // The interpolated version of the original progress private float mProgress; - // the amount that overlaps w/ bar size when rotation is max - private final float mMaxCutForBarSize; /** * @param context used to get the configuration for the drawable from diff --git a/app/src/main/java/com/hippo/drawable/UnikeryDrawable.java b/app/src/main/java/com/hippo/drawable/UnikeryDrawable.java index 1353e6e41..8d9ea4dae 100644 --- a/app/src/main/java/com/hippo/drawable/UnikeryDrawable.java +++ b/app/src/main/java/com/hippo/drawable/UnikeryDrawable.java @@ -18,7 +18,9 @@ package com.hippo.drawable; import android.graphics.drawable.Drawable; import android.util.Log; + import androidx.annotation.NonNull; + import com.hippo.conaco.Conaco; import com.hippo.conaco.ConacoTask; import com.hippo.conaco.Unikery; @@ -31,11 +33,9 @@ public class UnikeryDrawable extends WrapDrawable implements Unikery mConaco; + private int mTaskId = Unikery.INVALID_ID; private String mUrl; public UnikeryDrawable(ObservedTextView textView, Conaco conaco) { @@ -91,13 +91,13 @@ public class UnikeryDrawable extends WrapDrawable implements Unikery clazz = ((StageActivity) topActivity).getTopSceneClass(); - if (clazz != null) { - topSceneClazzName = clazz.getName(); - } + @NonNull + private static String avoidNull(String str) { + return null != str ? str : "null"; + } + + private static void collectInfo(Context context, FileWriter fw) throws IOException { + try { + PackageManager pm = context.getPackageManager(); + PackageInfo pi = pm.getPackageInfo(context.getPackageName(), PackageManager.GET_ACTIVITIES); + if (pi != null) { + String versionName = pi.versionName == null ? "null" : pi.versionName; + String versionCode = String.valueOf(pi.versionCode); + fw.write("======== PackageInfo ========\r\n"); + fw.write("PackageName="); + fw.write(pi.packageName); + fw.write("\r\n"); + fw.write("VersionName="); + fw.write(versionName); + fw.write("\r\n"); + fw.write("VersionCode="); + fw.write(versionCode); + fw.write("\r\n"); + String signature = PackageUtils.getSignature(context, pi.packageName); + fw.write("Signature="); + fw.write(null != signature ? signature : "null"); + fw.write("\r\n"); + fw.write("\r\n"); + } + } catch (PackageManager.NameNotFoundException e) { + fw.write("======== PackageInfo ========\r\n"); + fw.write("Can't get package information\r\n"); + fw.write("\r\n"); } - } - } catch (Throwable e) { - // Ignore - } - fw.write("======== Runtime ========\r\n"); - fw.write("TopActivity=");fw.write(avoidNull(topActivityClazzName));fw.write("\r\n"); - fw.write("TopScene=");fw.write(avoidNull(topSceneClazzName));fw.write("\r\n"); - fw.write("\r\n"); - // Device info - fw.write("======== DeviceInfo ========\r\n"); - fw.write("BOARD=");fw.write(Build.BOARD);fw.write("\r\n"); - fw.write("BOOTLOADER=");fw.write(Build.BOOTLOADER);fw.write("\r\n"); - fw.write("CPU_ABI=");fw.write(Build.CPU_ABI);fw.write("\r\n"); - fw.write("CPU_ABI2=");fw.write(Build.CPU_ABI2);fw.write("\r\n"); - fw.write("DEVICE=");fw.write(Build.DEVICE);fw.write("\r\n"); - fw.write("DISPLAY=");fw.write(Build.DISPLAY);fw.write("\r\n"); - fw.write("FINGERPRINT=");fw.write(Build.FINGERPRINT);fw.write("\r\n"); - fw.write("HARDWARE=");fw.write(Build.HARDWARE);fw.write("\r\n"); - fw.write("HOST=");fw.write(Build.HOST);fw.write("\r\n"); - fw.write("ID=");fw.write(Build.ID);fw.write("\r\n"); - fw.write("MANUFACTURER=");fw.write(Build.MANUFACTURER);fw.write("\r\n"); - fw.write("MODEL=");fw.write(Build.MODEL);fw.write("\r\n"); - fw.write("PRODUCT=");fw.write(Build.PRODUCT);fw.write("\r\n"); - fw.write("RADIO=");fw.write(Build.getRadioVersion());fw.write("\r\n"); - fw.write("SERIAL=");fw.write(Build.SERIAL);fw.write("\r\n"); - fw.write("TAGS=");fw.write(Build.TAGS);fw.write("\r\n"); - fw.write("TYPE=");fw.write(Build.TYPE);fw.write("\r\n"); - fw.write("USER=");fw.write(Build.USER);fw.write("\r\n"); - fw.write("CODENAME=");fw.write(Build.VERSION.CODENAME);fw.write("\r\n"); - fw.write("INCREMENTAL=");fw.write(Build.VERSION.INCREMENTAL);fw.write("\r\n"); - fw.write("RELEASE=");fw.write(Build.VERSION.RELEASE);fw.write("\r\n"); - fw.write("SDK=");fw.write(Integer.toString(Build.VERSION.SDK_INT));fw.write("\r\n"); - fw.write("MEMORY=");fw.write( - FileUtils.humanReadableByteCount(OSUtils.getAppAllocatedMemory(), false));fw.write("\r\n"); - fw.write("MEMORY_NATIVE=");fw.write(FileUtils.humanReadableByteCount(Debug.getNativeHeapAllocatedSize(), false));fw.write("\r\n"); - fw.write("MEMORY_MAX=");fw.write(FileUtils.humanReadableByteCount(OSUtils.getAppMaxMemory(), false));fw.write("\r\n"); - fw.write("MEMORY_TOTAL=");fw.write(FileUtils.humanReadableByteCount(OSUtils.getTotalMemory(), false));fw.write("\r\n"); - fw.write("\r\n"); - } + // Runtime + String topActivityClazzName = "null"; + String topSceneClazzName = "null"; + try { + Activity topActivity = ((EhApplication) context.getApplicationContext()).getTopActivity(); + if (null != topActivity) { + topActivityClazzName = topActivity.getClass().getName(); + if (topActivity instanceof StageActivity) { + Class clazz = ((StageActivity) topActivity).getTopSceneClass(); + if (clazz != null) { + topSceneClazzName = clazz.getName(); + } + } + } + } catch (Throwable e) { + // Ignore + } + fw.write("======== Runtime ========\r\n"); + fw.write("TopActivity="); + fw.write(avoidNull(topActivityClazzName)); + fw.write("\r\n"); + fw.write("TopScene="); + fw.write(avoidNull(topSceneClazzName)); + fw.write("\r\n"); + fw.write("\r\n"); - private static void getThrowableInfo(Throwable t, FileWriter fw) { - PrintWriter printWriter = new PrintWriter(fw); - t.printStackTrace(printWriter); - Throwable cause = t.getCause(); - while (cause != null) { - cause.printStackTrace(printWriter); - cause = cause.getCause(); - } - } - - public static void saveCrashLog(Context context, Throwable t) { - File dir = AppConfig.getExternalCrashDir(); - if (dir == null) { - return; + // Device info + fw.write("======== DeviceInfo ========\r\n"); + fw.write("BOARD="); + fw.write(Build.BOARD); + fw.write("\r\n"); + fw.write("BOOTLOADER="); + fw.write(Build.BOOTLOADER); + fw.write("\r\n"); + fw.write("CPU_ABI="); + fw.write(Build.CPU_ABI); + fw.write("\r\n"); + fw.write("CPU_ABI2="); + fw.write(Build.CPU_ABI2); + fw.write("\r\n"); + fw.write("DEVICE="); + fw.write(Build.DEVICE); + fw.write("\r\n"); + fw.write("DISPLAY="); + fw.write(Build.DISPLAY); + fw.write("\r\n"); + fw.write("FINGERPRINT="); + fw.write(Build.FINGERPRINT); + fw.write("\r\n"); + fw.write("HARDWARE="); + fw.write(Build.HARDWARE); + fw.write("\r\n"); + fw.write("HOST="); + fw.write(Build.HOST); + fw.write("\r\n"); + fw.write("ID="); + fw.write(Build.ID); + fw.write("\r\n"); + fw.write("MANUFACTURER="); + fw.write(Build.MANUFACTURER); + fw.write("\r\n"); + fw.write("MODEL="); + fw.write(Build.MODEL); + fw.write("\r\n"); + fw.write("PRODUCT="); + fw.write(Build.PRODUCT); + fw.write("\r\n"); + fw.write("RADIO="); + fw.write(Build.getRadioVersion()); + fw.write("\r\n"); + fw.write("SERIAL="); + fw.write(Build.SERIAL); + fw.write("\r\n"); + fw.write("TAGS="); + fw.write(Build.TAGS); + fw.write("\r\n"); + fw.write("TYPE="); + fw.write(Build.TYPE); + fw.write("\r\n"); + fw.write("USER="); + fw.write(Build.USER); + fw.write("\r\n"); + fw.write("CODENAME="); + fw.write(Build.VERSION.CODENAME); + fw.write("\r\n"); + fw.write("INCREMENTAL="); + fw.write(Build.VERSION.INCREMENTAL); + fw.write("\r\n"); + fw.write("RELEASE="); + fw.write(Build.VERSION.RELEASE); + fw.write("\r\n"); + fw.write("SDK="); + fw.write(Integer.toString(Build.VERSION.SDK_INT)); + fw.write("\r\n"); + fw.write("MEMORY="); + fw.write( + FileUtils.humanReadableByteCount(OSUtils.getAppAllocatedMemory(), false)); + fw.write("\r\n"); + fw.write("MEMORY_NATIVE="); + fw.write(FileUtils.humanReadableByteCount(Debug.getNativeHeapAllocatedSize(), false)); + fw.write("\r\n"); + fw.write("MEMORY_MAX="); + fw.write(FileUtils.humanReadableByteCount(OSUtils.getAppMaxMemory(), false)); + fw.write("\r\n"); + fw.write("MEMORY_TOTAL="); + fw.write(FileUtils.humanReadableByteCount(OSUtils.getTotalMemory(), false)); + fw.write("\r\n"); + fw.write("\r\n"); } - String nowString = ReadableTime.getFilenamableTime(System.currentTimeMillis()); - String fileName = "crash-" + nowString + ".log"; - File file = new File(dir, fileName); + private static void getThrowableInfo(Throwable t, FileWriter fw) { + PrintWriter printWriter = new PrintWriter(fw); + t.printStackTrace(printWriter); + Throwable cause = t.getCause(); + while (cause != null) { + cause.printStackTrace(printWriter); + cause = cause.getCause(); + } + } - FileWriter fw = null; - try { - fw = new FileWriter(file); - fw.write("TIME=");fw.write(nowString);fw.write("\r\n"); - fw.write("\r\n"); - collectInfo(context, fw); - fw.write("======== CrashInfo ========\r\n"); - getThrowableInfo(t, fw); - fw.write("\r\n"); - fw.flush(); - } catch (Exception e) { - file.delete(); - } finally { - IOUtils.closeQuietly(fw); + public static void saveCrashLog(Context context, Throwable t) { + File dir = AppConfig.getExternalCrashDir(); + if (dir == null) { + return; + } + + String nowString = ReadableTime.getFilenamableTime(System.currentTimeMillis()); + String fileName = "crash-" + nowString + ".log"; + File file = new File(dir, fileName); + + FileWriter fw = null; + try { + fw = new FileWriter(file); + fw.write("TIME="); + fw.write(nowString); + fw.write("\r\n"); + fw.write("\r\n"); + collectInfo(context, fw); + fw.write("======== CrashInfo ========\r\n"); + getThrowableInfo(t, fw); + fw.write("\r\n"); + fw.flush(); + } catch (Exception e) { + file.delete(); + } finally { + IOUtils.closeQuietly(fw); + } } - } } diff --git a/app/src/main/java/com/hippo/ehviewer/EhApplication.java b/app/src/main/java/com/hippo/ehviewer/EhApplication.java index a91792385..94307a964 100644 --- a/app/src/main/java/com/hippo/ehviewer/EhApplication.java +++ b/app/src/main/java/com/hippo/ehviewer/EhApplication.java @@ -28,9 +28,11 @@ import android.content.pm.PackageManager; import android.os.AsyncTask; import android.os.Debug; import android.util.Log; + import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.collection.LruCache; + import com.getkeepsafe.relinker.ReLinker; import com.hippo.a7zip.A7Zip; import com.hippo.a7zip.A7ZipExtractLite; @@ -58,21 +60,21 @@ import com.hippo.yorozuya.FileUtils; import com.hippo.yorozuya.IntIdGenerator; import com.hippo.yorozuya.OSUtils; import com.hippo.yorozuya.SimpleHandler; + import java.io.File; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.concurrent.TimeUnit; + import okhttp3.OkHttpClient; public class EhApplication extends RecordingApplication { + public static final boolean BETA = false; private static final String TAG = EhApplication.class.getSimpleName(); private static final String KEY_GLOBAL_STUFF_NEXT_ID = "global_stuff_next_id"; - - public static final boolean BETA = false; - private static final boolean DEBUG_CONACO = false; private static final boolean DEBUG_PRINT_NATIVE_MEMORY = false; private static final boolean DEBUG_PRINT_IMAGE_COUNT = false; @@ -82,6 +84,7 @@ public class EhApplication extends RecordingApplication { private final IntIdGenerator mIdGenerator = new IntIdGenerator(); private final HashMap mGlobalStuffMap = new HashMap<>(); + private final List mActivityList = new ArrayList<>(); private EhCookieStore mEhCookieStore; private EhClient mEhClient; private EhProxySelector mEhProxySelector; @@ -93,182 +96,12 @@ public class EhApplication extends RecordingApplication { private DownloadManager mDownloadManager; private Hosts mHosts; private FavouriteStatusRouter mFavouriteStatusRouter; - - private final List mActivityList = new ArrayList<>(); - private boolean initialized = false; public static EhApplication getInstance() { return instance; } - @SuppressLint("StaticFieldLeak") - @Override - public void onCreate() { - instance = this; - - Thread.UncaughtExceptionHandler handler = Thread.getDefaultUncaughtExceptionHandler(); - Thread.setDefaultUncaughtExceptionHandler((t, e) -> { - try { - // Always save crash file if onCreate() is not done - if (!initialized || Settings.getSaveCrashLog()) { - Crash.saveCrashLog(instance, e); - } - } catch (Throwable ignored) { } - - if (handler != null) { - handler.uncaughtException(t, e); - } - }); - - super.onCreate(); - - GetText.initialize(this); - StatusCodeException.initialize(this); - Settings.initialize(this); - ReadableTime.initialize(this); - Html.initialize(this); - AppConfig.initialize(this); - SpiderDen.initialize(this); - EhDB.initialize(this); - EhEngine.initialize(); - BitmapUtils.initialize(this); - Image.initialize(this); - A7Zip.loadLibrary(A7ZipExtractLite.LIBRARY, libname -> ReLinker.loadLibrary(EhApplication.this, libname)); - - if (EhDB.needMerge()) { - EhDB.mergeOldDB(this); - } - - if (Settings.getEnableAnalytics()) { - Analytics.start(this); - } - - // Do io tasks in new thread - new AsyncTask() { - @Override - protected Void doInBackground(Void... voids) { - // Check no media file - try { - UniFile downloadLocation = Settings.getDownloadLocation(); - if (Settings.getMediaScan()) { - CommonOperations.removeNoMediaFile(downloadLocation); - } else { - CommonOperations.ensureNoMediaFile(downloadLocation); - } - } catch (Throwable t) { - ExceptionUtils.throwIfFatal(t); - } - - // Clear temp files - try { - clearTempDir(); - } catch (Throwable t) { - ExceptionUtils.throwIfFatal(t); - } - - return null; - } - }.executeOnExecutor(IoThreadPoolExecutor.getInstance()); - - // Check app update - update(); - - // Update version code - try { - PackageInfo pi= getPackageManager().getPackageInfo(getPackageName(), 0); - Settings.putVersionCode(pi.versionCode); - } catch (PackageManager.NameNotFoundException e) { - // Ignore - } - - mIdGenerator.setNextId(Settings.getInt(KEY_GLOBAL_STUFF_NEXT_ID, 0)); - - if (DEBUG_PRINT_NATIVE_MEMORY || DEBUG_PRINT_IMAGE_COUNT) { - debugPrint(); - } - - initialized = true; - } - - private void clearTempDir() { - File dir = AppConfig.getTempDir(); - if (null != dir) { - FileUtils.deleteContent(dir); - } - dir = AppConfig.getExternalTempDir(); - if (null != dir) { - FileUtils.deleteContent(dir); - } - - // Add .nomedia to external temp dir - CommonOperations.ensureNoMediaFile(UniFile.fromFile(AppConfig.getExternalTempDir())); - } - - private void update() { - int version = Settings.getVersionCode(); - if (version < 52) { - Settings.putGuideGallery(true); - } - } - - public void clearMemoryCache() { - if (null != mConaco) { - mConaco.getBeerBelly().clearMemory(); - } - if (null != mGalleryDetailCache) { - mGalleryDetailCache.evictAll(); - } - } - - @Override - public void onTrimMemory(int level) { - super.onTrimMemory(level); - - if (level >= ComponentCallbacks2.TRIM_MEMORY_RUNNING_LOW) { - clearMemoryCache(); - } - } - - private void debugPrint() { - new Runnable() { - @Override - public void run() { - if (DEBUG_PRINT_NATIVE_MEMORY) { - Log.i(TAG, "Native memory: " + FileUtils.humanReadableByteCount( - Debug.getNativeHeapAllocatedSize(), false)); - } - if (DEBUG_PRINT_IMAGE_COUNT) { - Log.i(TAG, "Image count: " + Image.getImageCount()); - } - SimpleHandler.getInstance().postDelayed(this, DEBUG_PRINT_INTERVAL); - } - }.run(); - } - - public int putGlobalStuff(@NonNull Object o) { - int id = mIdGenerator.nextId(); - mGlobalStuffMap.put(id, o); - Settings.putInt(KEY_GLOBAL_STUFF_NEXT_ID, mIdGenerator.nextId()); - return id; - } - - public boolean containGlobalStuff(int id) { - return mGlobalStuffMap.containsKey(id); - } - - public Object getGlobalStuff(int id) { - return mGlobalStuffMap.get(id); - } - - public Object removeGlobalStuff(int id) { - return mGlobalStuffMap.remove(id); - } - - public boolean removeGlobalStuff(Object o) { - return mGlobalStuffMap.values().removeAll(Collections.singleton(o)); - } - public static EhCookieStore getEhCookieStore(@NonNull Context context) { EhApplication application = ((EhApplication) context.getApplicationContext()); if (application.mEhCookieStore == null) { @@ -410,6 +243,170 @@ public class EhApplication extends RecordingApplication { return "ehviewersu$gmail.com".replace('$', '@'); } + @SuppressLint("StaticFieldLeak") + @Override + public void onCreate() { + instance = this; + + Thread.UncaughtExceptionHandler handler = Thread.getDefaultUncaughtExceptionHandler(); + Thread.setDefaultUncaughtExceptionHandler((t, e) -> { + try { + // Always save crash file if onCreate() is not done + if (!initialized || Settings.getSaveCrashLog()) { + Crash.saveCrashLog(instance, e); + } + } catch (Throwable ignored) { + } + + if (handler != null) { + handler.uncaughtException(t, e); + } + }); + + super.onCreate(); + + GetText.initialize(this); + StatusCodeException.initialize(this); + Settings.initialize(this); + ReadableTime.initialize(this); + Html.initialize(this); + AppConfig.initialize(this); + SpiderDen.initialize(this); + EhDB.initialize(this); + EhEngine.initialize(); + BitmapUtils.initialize(this); + Image.initialize(this); + A7Zip.loadLibrary(A7ZipExtractLite.LIBRARY, libname -> ReLinker.loadLibrary(EhApplication.this, libname)); + + if (EhDB.needMerge()) { + EhDB.mergeOldDB(this); + } + + // Do io tasks in new thread + new AsyncTask() { + @Override + protected Void doInBackground(Void... voids) { + // Check no media file + try { + UniFile downloadLocation = Settings.getDownloadLocation(); + if (Settings.getMediaScan()) { + CommonOperations.removeNoMediaFile(downloadLocation); + } else { + CommonOperations.ensureNoMediaFile(downloadLocation); + } + } catch (Throwable t) { + ExceptionUtils.throwIfFatal(t); + } + + // Clear temp files + try { + clearTempDir(); + } catch (Throwable t) { + ExceptionUtils.throwIfFatal(t); + } + + return null; + } + }.executeOnExecutor(IoThreadPoolExecutor.getInstance()); + + // Check app update + update(); + + // Update version code + try { + PackageInfo pi = getPackageManager().getPackageInfo(getPackageName(), 0); + Settings.putVersionCode(pi.versionCode); + } catch (PackageManager.NameNotFoundException e) { + // Ignore + } + + mIdGenerator.setNextId(Settings.getInt(KEY_GLOBAL_STUFF_NEXT_ID, 0)); + + if (DEBUG_PRINT_NATIVE_MEMORY || DEBUG_PRINT_IMAGE_COUNT) { + debugPrint(); + } + + initialized = true; + } + + private void clearTempDir() { + File dir = AppConfig.getTempDir(); + if (null != dir) { + FileUtils.deleteContent(dir); + } + dir = AppConfig.getExternalTempDir(); + if (null != dir) { + FileUtils.deleteContent(dir); + } + + // Add .nomedia to external temp dir + CommonOperations.ensureNoMediaFile(UniFile.fromFile(AppConfig.getExternalTempDir())); + } + + private void update() { + int version = Settings.getVersionCode(); + if (version < 52) { + Settings.putGuideGallery(true); + } + } + + public void clearMemoryCache() { + if (null != mConaco) { + mConaco.getBeerBelly().clearMemory(); + } + if (null != mGalleryDetailCache) { + mGalleryDetailCache.evictAll(); + } + } + + @Override + public void onTrimMemory(int level) { + super.onTrimMemory(level); + + if (level >= ComponentCallbacks2.TRIM_MEMORY_RUNNING_LOW) { + clearMemoryCache(); + } + } + + private void debugPrint() { + new Runnable() { + @Override + public void run() { + if (DEBUG_PRINT_NATIVE_MEMORY) { + Log.i(TAG, "Native memory: " + FileUtils.humanReadableByteCount( + Debug.getNativeHeapAllocatedSize(), false)); + } + if (DEBUG_PRINT_IMAGE_COUNT) { + Log.i(TAG, "Image count: " + Image.getImageCount()); + } + SimpleHandler.getInstance().postDelayed(this, DEBUG_PRINT_INTERVAL); + } + }.run(); + } + + public int putGlobalStuff(@NonNull Object o) { + int id = mIdGenerator.nextId(); + mGlobalStuffMap.put(id, o); + Settings.putInt(KEY_GLOBAL_STUFF_NEXT_ID, mIdGenerator.nextId()); + return id; + } + + public boolean containGlobalStuff(int id) { + return mGlobalStuffMap.containsKey(id); + } + + public Object getGlobalStuff(int id) { + return mGlobalStuffMap.get(id); + } + + public Object removeGlobalStuff(int id) { + return mGlobalStuffMap.remove(id); + } + + public boolean removeGlobalStuff(Object o) { + return mGlobalStuffMap.values().removeAll(Collections.singleton(o)); + } + public void registerActivity(Activity activity) { mActivityList.add(activity); } diff --git a/app/src/main/java/com/hippo/ehviewer/EhDB.java b/app/src/main/java/com/hippo/ehviewer/EhDB.java index 81d5868b4..f6584a723 100644 --- a/app/src/main/java/com/hippo/ehviewer/EhDB.java +++ b/app/src/main/java/com/hippo/ehviewer/EhDB.java @@ -21,8 +21,10 @@ import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteOpenHelper; import android.util.Log; + import androidx.annotation.NonNull; import androidx.annotation.Nullable; + import com.hippo.ehviewer.client.data.GalleryInfo; import com.hippo.ehviewer.client.data.ListUrlBuilder; import com.hippo.ehviewer.dao.DaoMaster; @@ -47,9 +49,7 @@ import com.hippo.util.SqlUtils; import com.hippo.yorozuya.IOUtils; import com.hippo.yorozuya.ObjectUtils; import com.hippo.yorozuya.collect.SparseJLArray; -import de.greenrobot.dao.AbstractDao; -import de.greenrobot.dao.query.CloseableListIterator; -import de.greenrobot.dao.query.LazyList; + import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; @@ -59,6 +59,10 @@ import java.io.OutputStream; import java.util.ArrayList; import java.util.List; +import de.greenrobot.dao.AbstractDao; +import de.greenrobot.dao.query.CloseableListIterator; +import de.greenrobot.dao.query.LazyList; + public class EhDB { private static final String TAG = EhDB.class.getSimpleName(); @@ -70,34 +74,16 @@ public class EhDB { private static boolean sHasOldDB; private static boolean sNewDB; - private static class DBOpenHelper extends DaoMaster.OpenHelper { - - public DBOpenHelper(Context context, String name, SQLiteDatabase.CursorFactory factory) { - super(context, name, factory); - } - - @Override - public void onCreate(SQLiteDatabase db) { - super.onCreate(db); - sNewDB = true; - } - - @Override - public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { - upgradeDB(db, oldVersion); - } - } - private static void upgradeDB(SQLiteDatabase db, int oldVersion) { switch (oldVersion) { case 1: // 1 to 2, add FILTER FilterDao.createTable(db, true); case 2: // 2 to 3, add ENABLE column to table FILTER db.execSQL("CREATE TABLE " + "\"FILTER2\" (" + - "\"_id\" INTEGER PRIMARY KEY ," + - "\"MODE\" INTEGER NOT NULL ," + - "\"TEXT\" TEXT," + - "\"ENABLE\" INTEGER);"); + "\"_id\" INTEGER PRIMARY KEY ," + + "\"MODE\" INTEGER NOT NULL ," + + "\"TEXT\" TEXT," + + "\"ENABLE\" INTEGER);"); db.execSQL("INSERT INTO \"FILTER2\" (" + "_id, MODE, TEXT, ENABLE)" + "SELECT _id, MODE, TEXT, 1 FROM FILTER;"); @@ -105,48 +91,24 @@ public class EhDB { db.execSQL("ALTER TABLE FILTER2 RENAME TO FILTER"); case 3: // 3 to 4, add PAGE_FROM and PAGE_TO column to QUICK_SEARCH db.execSQL("CREATE TABLE " + "\"QUICK_SEARCH2\" (" + - "\"_id\" INTEGER PRIMARY KEY ," + - "\"NAME\" TEXT," + - "\"MODE\" INTEGER NOT NULL ," + - "\"CATEGORY\" INTEGER NOT NULL ," + - "\"KEYWORD\" TEXT," + - "\"ADVANCE_SEARCH\" INTEGER NOT NULL ," + - "\"MIN_RATING\" INTEGER NOT NULL ," + - "\"PAGE_FROM\" INTEGER NOT NULL ," + - "\"PAGE_TO\" INTEGER NOT NULL ," + - "\"TIME\" INTEGER NOT NULL );"); + "\"_id\" INTEGER PRIMARY KEY ," + + "\"NAME\" TEXT," + + "\"MODE\" INTEGER NOT NULL ," + + "\"CATEGORY\" INTEGER NOT NULL ," + + "\"KEYWORD\" TEXT," + + "\"ADVANCE_SEARCH\" INTEGER NOT NULL ," + + "\"MIN_RATING\" INTEGER NOT NULL ," + + "\"PAGE_FROM\" INTEGER NOT NULL ," + + "\"PAGE_TO\" INTEGER NOT NULL ," + + "\"TIME\" INTEGER NOT NULL );"); db.execSQL("INSERT INTO \"QUICK_SEARCH2\" (" + - "_id, NAME, MODE, CATEGORY, KEYWORD, ADVANCE_SEARCH, MIN_RATING, PAGE_FROM, PAGE_TO, TIME)" + - "SELECT _id, NAME, MODE, CATEGORY, KEYWORD, ADVANCE_SEARCH, MIN_RATING, -1, -1, TIME FROM QUICK_SEARCH;"); + "_id, NAME, MODE, CATEGORY, KEYWORD, ADVANCE_SEARCH, MIN_RATING, PAGE_FROM, PAGE_TO, TIME)" + + "SELECT _id, NAME, MODE, CATEGORY, KEYWORD, ADVANCE_SEARCH, MIN_RATING, -1, -1, TIME FROM QUICK_SEARCH;"); db.execSQL("DROP TABLE QUICK_SEARCH"); db.execSQL("ALTER TABLE QUICK_SEARCH2 RENAME TO QUICK_SEARCH"); } } - private static class OldDBHelper extends SQLiteOpenHelper { - - private static final String DB_NAME = "data"; - private static final int VERSION = 4; - - private static final String TABLE_GALLERY = "gallery"; - private static final String TABLE_LOCAL_FAVOURITE = "local_favourite"; - private static final String TABLE_TAG = "tag"; - private static final String TABLE_DOWNLOAD = "download"; - private static final String TABLE_HISTORY = "history"; - - public OldDBHelper(Context context) { - super(context, DB_NAME, null, VERSION); - } - - @Override - public void onCreate(SQLiteDatabase db) { - } - - @Override - public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { - } - } - public static void initialize(Context context) { sHasOldDB = context.getDatabasePath("data").exists(); @@ -362,7 +324,7 @@ public class EhDB { DownloadsDao dao = sDaoSession.getDownloadsDao(); List list = dao.queryBuilder().orderDesc(DownloadsDao.Properties.Time).list(); // Fix state - for (DownloadInfo info: list) { + for (DownloadInfo info : list) { if (info.state == DownloadInfo.STATE_WAIT || info.state == DownloadInfo.STATE_DOWNLOAD) { info.state = DownloadInfo.STATE_NONE; } @@ -491,7 +453,7 @@ public class EhDB { } public static synchronized List searchLocalFavorites(String query) { - query = SqlUtils.sqlEscapeString("%" + query+ "%"); + query = SqlUtils.sqlEscapeString("%" + query + "%"); LocalFavoritesDao dao = sDaoSession.getLocalFavoritesDao(); List list = dao.queryBuilder().orderDesc(LocalFavoritesDao.Properties.Time) .where(LocalFavoritesDao.Properties.Title.like(query)).list(); @@ -506,7 +468,7 @@ public class EhDB { public static synchronized void removeLocalFavorites(long[] gidArray) { LocalFavoritesDao dao = sDaoSession.getLocalFavoritesDao(); - for (long gid: gidArray) { + for (long gid : gidArray) { dao.deleteByKey(gid); } } @@ -531,7 +493,7 @@ public class EhDB { } public static synchronized void putLocalFavorites(List galleryInfoList) { - for (GalleryInfo gi: galleryInfoList) { + for (GalleryInfo gi : galleryInfoList) { putLocalFavorites(gi); } } @@ -607,7 +569,7 @@ public class EhDB { public static synchronized void putHistoryInfo(List historyInfoList) { HistoryDao dao = sDaoSession.getHistoryDao(); - for (HistoryInfo info: historyInfoList) { + for (HistoryInfo info : historyInfoList) { if (null == dao.load(info.gid)) { dao.insert(info); } @@ -670,14 +632,22 @@ public class EhDB { try (SQLiteDatabase db = helper.getWritableDatabase()) { DaoMaster daoMaster = new DaoMaster(db); DaoSession exportSession = daoMaster.newSession(); - if (!copyDao(sDaoSession.getDownloadsDao(), exportSession.getDownloadsDao())) return false; - if (!copyDao(sDaoSession.getDownloadLabelDao(), exportSession.getDownloadLabelDao())) return false; - if (!copyDao(sDaoSession.getDownloadDirnameDao(), exportSession.getDownloadDirnameDao())) return false; - if (!copyDao(sDaoSession.getHistoryDao(), exportSession.getHistoryDao())) return false; - if (!copyDao(sDaoSession.getQuickSearchDao(), exportSession.getQuickSearchDao())) return false; - if (!copyDao(sDaoSession.getLocalFavoritesDao(), exportSession.getLocalFavoritesDao())) return false; - if (!copyDao(sDaoSession.getBookmarksBao(), exportSession.getBookmarksBao())) return false; - if (!copyDao(sDaoSession.getFilterDao(), exportSession.getFilterDao())) return false; + if (!copyDao(sDaoSession.getDownloadsDao(), exportSession.getDownloadsDao())) + return false; + if (!copyDao(sDaoSession.getDownloadLabelDao(), exportSession.getDownloadLabelDao())) + return false; + if (!copyDao(sDaoSession.getDownloadDirnameDao(), exportSession.getDownloadDirnameDao())) + return false; + if (!copyDao(sDaoSession.getHistoryDao(), exportSession.getHistoryDao())) + return false; + if (!copyDao(sDaoSession.getQuickSearchDao(), exportSession.getQuickSearchDao())) + return false; + if (!copyDao(sDaoSession.getLocalFavoritesDao(), exportSession.getLocalFavoritesDao())) + return false; + if (!copyDao(sDaoSession.getBookmarksBao(), exportSession.getBookmarksBao())) + return false; + if (!copyDao(sDaoSession.getFilterDao(), exportSession.getFilterDao())) + return false; } // Copy export db to data dir @@ -737,7 +707,7 @@ public class EhDB { // Download dirname List downloadDirnameList = session.getDownloadDirnameDao().queryBuilder().list(); - for (DownloadDirname dirname: downloadDirnameList) { + for (DownloadDirname dirname : downloadDirnameList) { putDownloadDirname(dirname.getGid(), dirname.getDirname()); } @@ -748,9 +718,9 @@ public class EhDB { // QuickSearch List quickSearchList = session.getQuickSearchDao().queryBuilder().list(); List currentQuickSearchList = sDaoSession.getQuickSearchDao().queryBuilder().list(); - for (QuickSearch quickSearch: quickSearchList) { + for (QuickSearch quickSearch : quickSearchList) { String name = quickSearch.name; - for (QuickSearch q: currentQuickSearchList) { + for (QuickSearch q : currentQuickSearchList) { if (ObjectUtils.equal(q.name, name)) { // The same name name = null; @@ -765,7 +735,7 @@ public class EhDB { // LocalFavorites List localFavoriteInfoList = session.getLocalFavoritesDao().queryBuilder().list(); - for (LocalFavoriteInfo info: localFavoriteInfoList) { + for (LocalFavoriteInfo info : localFavoriteInfoList) { putLocalFavorites(info); } @@ -775,7 +745,7 @@ public class EhDB { // Filter List filterList = session.getFilterDao().queryBuilder().list(); List currentFilterList = sDaoSession.getFilterDao().queryBuilder().list(); - for (Filter filter: filterList) { + for (Filter filter : filterList) { if (!currentFilterList.contains(filter)) { addFilter(filter); } @@ -788,4 +758,46 @@ public class EhDB { return context.getString(R.string.cant_read_the_file); } } + + private static class DBOpenHelper extends DaoMaster.OpenHelper { + + public DBOpenHelper(Context context, String name, SQLiteDatabase.CursorFactory factory) { + super(context, name, factory); + } + + @Override + public void onCreate(SQLiteDatabase db) { + super.onCreate(db); + sNewDB = true; + } + + @Override + public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { + upgradeDB(db, oldVersion); + } + } + + private static class OldDBHelper extends SQLiteOpenHelper { + + private static final String DB_NAME = "data"; + private static final int VERSION = 4; + + private static final String TABLE_GALLERY = "gallery"; + private static final String TABLE_LOCAL_FAVOURITE = "local_favourite"; + private static final String TABLE_TAG = "tag"; + private static final String TABLE_DOWNLOAD = "download"; + private static final String TABLE_HISTORY = "history"; + + public OldDBHelper(Context context) { + super(context, DB_NAME, null, VERSION); + } + + @Override + public void onCreate(SQLiteDatabase db) { + } + + @Override + public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { + } + } } diff --git a/app/src/main/java/com/hippo/ehviewer/EhProxySelector.java b/app/src/main/java/com/hippo/ehviewer/EhProxySelector.java index 5a1177812..a631bfb18 100644 --- a/app/src/main/java/com/hippo/ehviewer/EhProxySelector.java +++ b/app/src/main/java/com/hippo/ehviewer/EhProxySelector.java @@ -17,8 +17,10 @@ package com.hippo.ehviewer; import android.text.TextUtils; + import com.hippo.network.InetValidator; import com.hippo.util.ExceptionUtils; + import java.io.IOException; import java.net.InetAddress; import java.net.InetSocketAddress; @@ -31,76 +33,78 @@ import java.util.List; public class EhProxySelector extends ProxySelector { - public static final int TYPE_DIRECT = 0; - public static final int TYPE_SYSTEM = 1; - public static final int TYPE_HTTP = 2; - public static final int TYPE_SOCKS = 3; + public static final int TYPE_DIRECT = 0; + public static final int TYPE_SYSTEM = 1; + public static final int TYPE_HTTP = 2; + public static final int TYPE_SOCKS = 3; - private ProxySelector delegation; - private ProxySelector alternative; + private ProxySelector delegation; + private ProxySelector alternative; - EhProxySelector() { - alternative = ProxySelector.getDefault(); - if (alternative == null) { - alternative = new NullProxySelector(); - } - - updateProxy(); - } - - public void updateProxy() { - switch (Settings.getProxyType()) { - case TYPE_DIRECT: - delegation = new NullProxySelector(); - break; - default: - case TYPE_SYSTEM: - delegation = alternative; - break; - case TYPE_HTTP: - case TYPE_SOCKS: - delegation = null; - break; - } - } - - @Override - public List select(URI uri) { - int type = Settings.getProxyType(); - if (type == TYPE_HTTP || type == TYPE_SOCKS) { - try { - String ip = Settings.getProxyIp(); - int port = Settings.getProxyPort(); - if (!TextUtils.isEmpty(ip) && InetValidator.isValidInetPort(port)) { - InetAddress inetAddress = InetAddress.getByName(ip); - SocketAddress socketAddress = new InetSocketAddress(inetAddress, port); - return Collections.singletonList(new Proxy(type == TYPE_HTTP ? Proxy.Type.HTTP : Proxy.Type.SOCKS, socketAddress)); + EhProxySelector() { + alternative = ProxySelector.getDefault(); + if (alternative == null) { + alternative = new NullProxySelector(); } - } catch (Throwable t) { - ExceptionUtils.throwIfFatal(t); - } + + updateProxy(); } - if (delegation != null) { - return delegation.select(uri); + public void updateProxy() { + switch (Settings.getProxyType()) { + case TYPE_DIRECT: + delegation = new NullProxySelector(); + break; + default: + case TYPE_SYSTEM: + delegation = alternative; + break; + case TYPE_HTTP: + case TYPE_SOCKS: + delegation = null; + break; + } } - return alternative.select(uri); - } - - @Override - public void connectFailed(URI uri, SocketAddress sa, IOException ioe) { - if (delegation != null) { - delegation.select(uri); - } - } - - private static class NullProxySelector extends ProxySelector { @Override public List select(URI uri) { - return Collections.singletonList(Proxy.NO_PROXY); + int type = Settings.getProxyType(); + if (type == TYPE_HTTP || type == TYPE_SOCKS) { + try { + String ip = Settings.getProxyIp(); + int port = Settings.getProxyPort(); + if (!TextUtils.isEmpty(ip) && InetValidator.isValidInetPort(port)) { + InetAddress inetAddress = InetAddress.getByName(ip); + SocketAddress socketAddress = new InetSocketAddress(inetAddress, port); + return Collections.singletonList(new Proxy(type == TYPE_HTTP ? Proxy.Type.HTTP : Proxy.Type.SOCKS, socketAddress)); + } + } catch (Throwable t) { + ExceptionUtils.throwIfFatal(t); + } + } + + if (delegation != null) { + return delegation.select(uri); + } + + return alternative.select(uri); } + @Override - public void connectFailed(URI uri, SocketAddress sa, IOException ioe) { } - } + public void connectFailed(URI uri, SocketAddress sa, IOException ioe) { + if (delegation != null) { + delegation.select(uri); + } + } + + private static class NullProxySelector extends ProxySelector { + @Override + public List select(URI uri) { + return Collections.singletonList(Proxy.NO_PROXY); + } + + @Override + public void connectFailed(URI uri, SocketAddress sa, IOException ioe) { + } + } } diff --git a/app/src/main/java/com/hippo/ehviewer/FavouriteStatusRouter.java b/app/src/main/java/com/hippo/ehviewer/FavouriteStatusRouter.java index e7bc4b9ad..52f556372 100644 --- a/app/src/main/java/com/hippo/ehviewer/FavouriteStatusRouter.java +++ b/app/src/main/java/com/hippo/ehviewer/FavouriteStatusRouter.java @@ -17,8 +17,10 @@ package com.hippo.ehviewer; import android.annotation.SuppressLint; + import com.hippo.ehviewer.client.data.GalleryInfo; import com.hippo.yorozuya.IntIdGenerator; + import java.util.ArrayList; import java.util.HashMap; import java.util.List; @@ -26,47 +28,47 @@ import java.util.Map; public class FavouriteStatusRouter { - private static final String KEY_DATA_MAP_NEXT_ID = "data_map_next_id"; + private static final String KEY_DATA_MAP_NEXT_ID = "data_map_next_id"; - private final IntIdGenerator idGenerator = new IntIdGenerator(Settings.getInt(KEY_DATA_MAP_NEXT_ID, 0)); - @SuppressLint("UseSparseArrays") - private final HashMap> maps = new HashMap<>(); + private final IntIdGenerator idGenerator = new IntIdGenerator(Settings.getInt(KEY_DATA_MAP_NEXT_ID, 0)); + @SuppressLint("UseSparseArrays") + private final HashMap> maps = new HashMap<>(); - private List listeners = new ArrayList<>(); + private List listeners = new ArrayList<>(); - public int saveDataMap(Map map) { - int id = idGenerator.nextId(); - maps.put(id, map); - Settings.putInt(KEY_DATA_MAP_NEXT_ID, idGenerator.nextId()); - return id; - } - - public Map restoreDataMap(int id) { - return maps.remove(id); - } - - public void modifyFavourites(long gid, int slot) { - for (Map map : maps.values()) { - GalleryInfo info = map.get(gid); - if (info != null) { - info.favoriteSlot = slot; - } + public int saveDataMap(Map map) { + int id = idGenerator.nextId(); + maps.put(id, map); + Settings.putInt(KEY_DATA_MAP_NEXT_ID, idGenerator.nextId()); + return id; } - for (Listener listener : listeners) { - listener.onModifyFavourites(gid, slot); + public Map restoreDataMap(int id) { + return maps.remove(id); } - } - public void addListener(Listener listener) { - listeners.add(listener); - } + public void modifyFavourites(long gid, int slot) { + for (Map map : maps.values()) { + GalleryInfo info = map.get(gid); + if (info != null) { + info.favoriteSlot = slot; + } + } - public void removeListener(Listener listener) { - listeners.remove(listener); - } + for (Listener listener : listeners) { + listener.onModifyFavourites(gid, slot); + } + } - public interface Listener { - void onModifyFavourites(long gid, int slot); - } + public void addListener(Listener listener) { + listeners.add(listener); + } + + public void removeListener(Listener listener) { + listeners.remove(listener); + } + + public interface Listener { + void onModifyFavourites(long gid, int slot); + } } diff --git a/app/src/main/java/com/hippo/ehviewer/GetText.java b/app/src/main/java/com/hippo/ehviewer/GetText.java index 1ccd2d507..eca9bc0f9 100644 --- a/app/src/main/java/com/hippo/ehviewer/GetText.java +++ b/app/src/main/java/com/hippo/ehviewer/GetText.java @@ -18,6 +18,7 @@ package com.hippo.ehviewer; import android.content.Context; import android.content.res.Resources; + import androidx.annotation.NonNull; import androidx.annotation.StringRes; diff --git a/app/src/main/java/com/hippo/ehviewer/Hosts.java b/app/src/main/java/com/hippo/ehviewer/Hosts.java index 76825a17b..880235921 100644 --- a/app/src/main/java/com/hippo/ehviewer/Hosts.java +++ b/app/src/main/java/com/hippo/ehviewer/Hosts.java @@ -26,9 +26,12 @@ import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteOpenHelper; import android.util.Pair; + import androidx.annotation.Nullable; + import com.hippo.database.MSQLiteBuilder; import com.hippo.util.SqlUtils; + import java.net.InetAddress; import java.net.UnknownHostException; import java.util.ArrayList; @@ -36,315 +39,310 @@ import java.util.List; public class Hosts { - private static final int VERSION_1 = 1; - private static final String TABLE_HOSTS = "HOSTS"; - private static final String COLUMN_HOST = "HOST"; - private static final String COLUMN_IP = "IP"; + private static final int VERSION_1 = 1; + private static final String TABLE_HOSTS = "HOSTS"; + private static final String COLUMN_HOST = "HOST"; + private static final String COLUMN_IP = "IP"; - private static final int DB_VERSION = VERSION_1; + private static final int DB_VERSION = VERSION_1; - private final SQLiteOpenHelper helper; - private final SQLiteDatabase db; + private final SQLiteOpenHelper helper; + private final SQLiteDatabase db; - public Hosts(Context context, String name) { - helper = new MSQLiteBuilder() - .version(VERSION_1) - .createTable(TABLE_HOSTS) - .insertColumn(TABLE_HOSTS, COLUMN_HOST, String.class) - .insertColumn(TABLE_HOSTS, COLUMN_IP, String.class) - .build(context, name, DB_VERSION); - db = helper.getWritableDatabase(); - } - - /** - * Gets a InetAddress with the host. - */ - @Nullable - public InetAddress get(String host) { - if (!isValidHost(host)) { - return null; + public Hosts(Context context, String name) { + helper = new MSQLiteBuilder() + .version(VERSION_1) + .createTable(TABLE_HOSTS) + .insertColumn(TABLE_HOSTS, COLUMN_HOST, String.class) + .insertColumn(TABLE_HOSTS, COLUMN_IP, String.class) + .build(context, name, DB_VERSION); + db = helper.getWritableDatabase(); } - Cursor cursor = db.rawQuery("SELECT * FROM " + TABLE_HOSTS + " WHERE " + COLUMN_HOST + " = ?;", new String[] {host}); - try { - if (cursor.moveToNext()) { - String ip = SqlUtils.getString(cursor, COLUMN_IP, null); - return toInetAddress(host, ip); - } else { - return null; - } - } finally { - cursor.close(); - } - } + @Nullable + public static InetAddress toInetAddress(String host, String ip) { + if (!isValidHost(host)) { + return null; + } - private boolean contains(String host) { - Cursor cursor = db.rawQuery("SELECT * FROM " + TABLE_HOSTS + " WHERE " + COLUMN_HOST + " = ?;", new String[] {host}); - try { - return cursor.moveToNext(); - } finally { - cursor.close(); - } - } - - /** - * Puts the host-ip pair into this hosts. - */ - public boolean put(String host, String ip) { - if (!isValidHost(host) || !isValidIp(ip)) { - return false; - } - - ContentValues values = new ContentValues(); - values.put(COLUMN_HOST, host); - values.put(COLUMN_IP, ip); - - if (contains(host)) { - db.update(TABLE_HOSTS, values, COLUMN_HOST + " = ?", new String[] { host }); - } else { - db.insert(TABLE_HOSTS, null, values); - } - - return true; - } - - /** - * Puts delete the entry with the host. - */ - public void delete(String host) { - db.delete(TABLE_HOSTS, COLUMN_HOST + " = ?", new String[] { host }); - } - - /** - * Get all data from this host. - */ - public List> getAll() { - List> result = new ArrayList<>(); - - Cursor cursor = db.rawQuery("SELECT * FROM " + TABLE_HOSTS + ";", null); - try { - while (cursor.moveToNext()) { - String host = SqlUtils.getString(cursor, COLUMN_HOST, null); - String ip = SqlUtils.getString(cursor, COLUMN_IP, null); + if (ip == null) { + return null; + } - InetAddress inetAddress = toInetAddress(host, ip); - if (inetAddress == null) { - continue; + byte[] bytes = parseV4(ip); + if (bytes == null) { + bytes = parseV6(ip); } - - result.add(new Pair<>(host, ip)); - } - } finally { - cursor.close(); - } - - return result; - } - - @Nullable - public static InetAddress toInetAddress(String host, String ip) { - if (!isValidHost(host)) { - return null; - } - - if (ip == null) { - return null; - } - - byte[] bytes = parseV4(ip); - if (bytes == null) { - bytes = parseV6(ip); - } - if (bytes == null) { - return null; - } - - try { - return InetAddress.getByAddress(host, bytes); - } catch (UnknownHostException e) { - return null; - } - } - - /** - * Returns true if the host is valid. - */ - public static boolean isValidHost(String host) { - if (host == null) { - return false; - } - - if (host.length() > 253) { - return false; - } - - int labelLength = 0; - for (int i = 0, n = host.length(); i < n; i++) { - char ch = host.charAt(i); - - if (ch == '.') { - if (labelLength < 1 || labelLength > 63) { - return false; + if (bytes == null) { + return null; } - labelLength = 0; - } else { - labelLength++; - } - - if ((ch < 'a' || ch > 'z') && (ch < '0' || ch > '9') && ch != '-' && ch != '.') { - return false; - } - } - - if (labelLength < 1 || labelLength > 63) { - return false; - } - - return true; - } - - /** - * Returns true if the ip is valid. - */ - public static boolean isValidIp(String ip) { - return ip != null && (parseV4(ip) != null || parseV6(ip) != null); - } - - // org.xbill.DNS.Address.parseV4 - @Nullable - private static byte[] parseV4(String s) { - int numDigits; - int currentOctet; - byte [] values = new byte[4]; - int currentValue; - int length = s.length(); - - currentOctet = 0; - currentValue = 0; - numDigits = 0; - for (int i = 0; i < length; i++) { - char c = s.charAt(i); - if (c >= '0' && c <= '9') { - /* Can't have more than 3 digits per octet. */ - if (numDigits == 3) - return null; - /* Octets shouldn't start with 0, unless they are 0. */ - if (numDigits > 0 && currentValue == 0) - return null; - numDigits++; - currentValue *= 10; - currentValue += (c - '0'); - /* 255 is the maximum value for an octet. */ - if (currentValue > 255) - return null; - } else if (c == '.') { - /* Can't have more than 3 dots. */ - if (currentOctet == 3) - return null; - /* Two consecutive dots are bad. */ - if (numDigits == 0) - return null; - values[currentOctet++] = (byte) currentValue; - currentValue = 0; - numDigits = 0; - } else - return null; - } - /* Must have 4 octets. */ - if (currentOctet != 3) - return null; - /* The fourth octet can't be empty. */ - if (numDigits == 0) - return null; - values[currentOctet] = (byte) currentValue; - return values; - } - - // org.xbill.DNS.Address.parseV6 - @Nullable - private static byte[] parseV6(String s) { - int range = -1; - byte [] data = new byte[16]; - - String [] tokens = s.split(":", -1); - - int first = 0; - int last = tokens.length - 1; - - if (tokens[0].length() == 0) { - // If the first two tokens are empty, it means the string - // started with ::, which is fine. If only the first is - // empty, the string started with :, which is bad. - if (last - first > 0 && tokens[1].length() == 0) - first++; - else - return null; - } - - if (tokens[last].length() == 0) { - // If the last two tokens are empty, it means the string - // ended with ::, which is fine. If only the last is - // empty, the string ended with :, which is bad. - if (last - first > 0 && tokens[last - 1].length() == 0) - last--; - else - return null; - } - - if (last - first + 1 > 8) - return null; - - int i, j; - for (i = first, j = 0; i <= last; i++) { - if (tokens[i].length() == 0) { - if (range >= 0) - return null; - range = j; - continue; - } - - if (tokens[i].indexOf('.') >= 0) { - // An IPv4 address must be the last component - if (i < last) - return null; - // There can't have been more than 6 components. - if (i > 6) - return null; - byte [] v4addr = parseV4(tokens[i]); - if (v4addr == null) - return null; - for (int k = 0; k < 4; k++) - data[j++] = v4addr[k]; - break; - } - try { - for (int k = 0; k < tokens[i].length(); k++) { - char c = tokens[i].charAt(k); - if (Character.digit(c, 16) < 0) + try { + return InetAddress.getByAddress(host, bytes); + } catch (UnknownHostException e) { return null; } - int x = Integer.parseInt(tokens[i], 16); - if (x > 0xFFFF || x < 0) - return null; - data[j++] = (byte)(x >>> 8); - data[j++] = (byte)(x & 0xFF); - } - catch (NumberFormatException e) { - return null; - } } - if (j < 16 && range < 0) - return null; + /** + * Returns true if the host is valid. + */ + public static boolean isValidHost(String host) { + if (host == null) { + return false; + } - if (range >= 0) { - int empty = 16 - j; - System.arraycopy(data, range, data, range + empty, j - range); - for (i = range; i < range + empty; i++) - data[i] = 0; + if (host.length() > 253) { + return false; + } + + int labelLength = 0; + for (int i = 0, n = host.length(); i < n; i++) { + char ch = host.charAt(i); + + if (ch == '.') { + if (labelLength < 1 || labelLength > 63) { + return false; + } + labelLength = 0; + } else { + labelLength++; + } + + if ((ch < 'a' || ch > 'z') && (ch < '0' || ch > '9') && ch != '-' && ch != '.') { + return false; + } + } + + return labelLength >= 1 && labelLength <= 63; } - return data; - } + /** + * Returns true if the ip is valid. + */ + public static boolean isValidIp(String ip) { + return ip != null && (parseV4(ip) != null || parseV6(ip) != null); + } + + // org.xbill.DNS.Address.parseV4 + @Nullable + private static byte[] parseV4(String s) { + int numDigits; + int currentOctet; + byte[] values = new byte[4]; + int currentValue; + int length = s.length(); + + currentOctet = 0; + currentValue = 0; + numDigits = 0; + for (int i = 0; i < length; i++) { + char c = s.charAt(i); + if (c >= '0' && c <= '9') { + /* Can't have more than 3 digits per octet. */ + if (numDigits == 3) + return null; + /* Octets shouldn't start with 0, unless they are 0. */ + if (numDigits > 0 && currentValue == 0) + return null; + numDigits++; + currentValue *= 10; + currentValue += (c - '0'); + /* 255 is the maximum value for an octet. */ + if (currentValue > 255) + return null; + } else if (c == '.') { + /* Can't have more than 3 dots. */ + if (currentOctet == 3) + return null; + /* Two consecutive dots are bad. */ + if (numDigits == 0) + return null; + values[currentOctet++] = (byte) currentValue; + currentValue = 0; + numDigits = 0; + } else + return null; + } + /* Must have 4 octets. */ + if (currentOctet != 3) + return null; + /* The fourth octet can't be empty. */ + if (numDigits == 0) + return null; + values[currentOctet] = (byte) currentValue; + return values; + } + + // org.xbill.DNS.Address.parseV6 + @Nullable + private static byte[] parseV6(String s) { + int range = -1; + byte[] data = new byte[16]; + + String[] tokens = s.split(":", -1); + + int first = 0; + int last = tokens.length - 1; + + if (tokens[0].length() == 0) { + // If the first two tokens are empty, it means the string + // started with ::, which is fine. If only the first is + // empty, the string started with :, which is bad. + if (last - first > 0 && tokens[1].length() == 0) + first++; + else + return null; + } + + if (tokens[last].length() == 0) { + // If the last two tokens are empty, it means the string + // ended with ::, which is fine. If only the last is + // empty, the string ended with :, which is bad. + if (last - first > 0 && tokens[last - 1].length() == 0) + last--; + else + return null; + } + + if (last - first + 1 > 8) + return null; + + int i, j; + for (i = first, j = 0; i <= last; i++) { + if (tokens[i].length() == 0) { + if (range >= 0) + return null; + range = j; + continue; + } + + if (tokens[i].indexOf('.') >= 0) { + // An IPv4 address must be the last component + if (i < last) + return null; + // There can't have been more than 6 components. + if (i > 6) + return null; + byte[] v4addr = parseV4(tokens[i]); + if (v4addr == null) + return null; + for (int k = 0; k < 4; k++) + data[j++] = v4addr[k]; + break; + } + + try { + for (int k = 0; k < tokens[i].length(); k++) { + char c = tokens[i].charAt(k); + if (Character.digit(c, 16) < 0) + return null; + } + int x = Integer.parseInt(tokens[i], 16); + if (x > 0xFFFF || x < 0) + return null; + data[j++] = (byte) (x >>> 8); + data[j++] = (byte) (x & 0xFF); + } catch (NumberFormatException e) { + return null; + } + } + + if (j < 16 && range < 0) + return null; + + if (range >= 0) { + int empty = 16 - j; + System.arraycopy(data, range, data, range + empty, j - range); + for (i = range; i < range + empty; i++) + data[i] = 0; + } + + return data; + } + + /** + * Gets a InetAddress with the host. + */ + @Nullable + public InetAddress get(String host) { + if (!isValidHost(host)) { + return null; + } + + Cursor cursor = db.rawQuery("SELECT * FROM " + TABLE_HOSTS + " WHERE " + COLUMN_HOST + " = ?;", new String[]{host}); + try { + if (cursor.moveToNext()) { + String ip = SqlUtils.getString(cursor, COLUMN_IP, null); + return toInetAddress(host, ip); + } else { + return null; + } + } finally { + cursor.close(); + } + } + + private boolean contains(String host) { + Cursor cursor = db.rawQuery("SELECT * FROM " + TABLE_HOSTS + " WHERE " + COLUMN_HOST + " = ?;", new String[]{host}); + try { + return cursor.moveToNext(); + } finally { + cursor.close(); + } + } + + /** + * Puts the host-ip pair into this hosts. + */ + public boolean put(String host, String ip) { + if (!isValidHost(host) || !isValidIp(ip)) { + return false; + } + + ContentValues values = new ContentValues(); + values.put(COLUMN_HOST, host); + values.put(COLUMN_IP, ip); + + if (contains(host)) { + db.update(TABLE_HOSTS, values, COLUMN_HOST + " = ?", new String[]{host}); + } else { + db.insert(TABLE_HOSTS, null, values); + } + + return true; + } + + /** + * Puts delete the entry with the host. + */ + public void delete(String host) { + db.delete(TABLE_HOSTS, COLUMN_HOST + " = ?", new String[]{host}); + } + + /** + * Get all data from this host. + */ + public List> getAll() { + List> result = new ArrayList<>(); + + Cursor cursor = db.rawQuery("SELECT * FROM " + TABLE_HOSTS + ";", null); + try { + while (cursor.moveToNext()) { + String host = SqlUtils.getString(cursor, COLUMN_HOST, null); + String ip = SqlUtils.getString(cursor, COLUMN_IP, null); + + InetAddress inetAddress = toInetAddress(host, ip); + if (inetAddress == null) { + continue; + } + + result.add(new Pair<>(host, ip)); + } + } finally { + cursor.close(); + } + + return result; + } } diff --git a/app/src/main/java/com/hippo/ehviewer/ImageBitmapHelper.java b/app/src/main/java/com/hippo/ehviewer/ImageBitmapHelper.java index c9040ed83..63cc28181 100644 --- a/app/src/main/java/com/hippo/ehviewer/ImageBitmapHelper.java +++ b/app/src/main/java/com/hippo/ehviewer/ImageBitmapHelper.java @@ -18,9 +18,11 @@ package com.hippo.ehviewer; import androidx.annotation.NonNull; import androidx.annotation.Nullable; + import com.hippo.conaco.ValueHelper; import com.hippo.image.ImageBitmap; import com.hippo.streampipe.InputStreamPipe; + import java.io.IOException; public class ImageBitmapHelper implements ValueHelper { diff --git a/app/src/main/java/com/hippo/ehviewer/Settings.java b/app/src/main/java/com/hippo/ehviewer/Settings.java index 274318317..1f5da023d 100755 --- a/app/src/main/java/com/hippo/ehviewer/Settings.java +++ b/app/src/main/java/com/hippo/ehviewer/Settings.java @@ -21,9 +21,11 @@ import android.content.SharedPreferences; import android.net.Uri; import android.preference.PreferenceManager; import android.util.Log; + import androidx.annotation.DimenRes; import androidx.annotation.NonNull; import androidx.annotation.Nullable; + import com.hippo.ehviewer.client.EhConfig; import com.hippo.ehviewer.client.EhUtils; import com.hippo.ehviewer.client.data.FavListUrlBuilder; @@ -36,13 +38,222 @@ import com.hippo.yorozuya.AssertUtils; import com.hippo.yorozuya.FileUtils; import com.hippo.yorozuya.MathUtils; import com.hippo.yorozuya.NumberUtils; + import java.io.File; import java.util.Locale; public class Settings { - private static final String TAG = Settings.class.getSimpleName(); + /******************** + ****** Eh + ********************/ + public static final String KEY_THEME = "theme"; + public static final int THEME_LIGHT = 0; + public static final int THEME_DARK = 1; + public static final int THEME_BLACK = 2; + public static final String KEY_APPLY_NAV_BAR_THEME_COLOR = "apply_nav_bar_theme_color"; + public static final String KEY_GALLERY_SITE = "gallery_site"; + public static final String KEY_LIST_MODE = "list_mode"; + public static final String KEY_DETAIL_SIZE = "detail_size"; + public static final String KEY_THUMB_SIZE = "thumb_size"; + public static final String KEY_THUMB_RESOLUTION = "thumb_resolution"; + public static final String KEY_SHOW_TAG_TRANSLATIONS = "show_tag_translations"; + public static final String KEY_DEFAULT_CATEGORIES = "default_categories"; + public static final int DEFAULT_DEFAULT_CATEGORIES = EhUtils.ALL_CATEGORY; + public static final String KEY_EXCLUDED_TAG_NAMESPACES = "excluded_tag_namespaces"; + public static final String KEY_EXCLUDED_LANGUAGES = "excluded_languages"; + /******************** + ****** Privacy and Security + ********************/ + public static final String KEY_SEC_SECURITY = "enable_secure"; + public static final boolean VALUE_SEC_SECURITY = false; + /******************** + ****** Download + ********************/ + public static final String KEY_DOWNLOAD_SAVE_SCHEME = "image_scheme"; + public static final String KEY_DOWNLOAD_SAVE_AUTHORITY = "image_authority"; + public static final String KEY_DOWNLOAD_SAVE_PATH = "image_path"; + public static final String KEY_DOWNLOAD_SAVE_QUERY = "image_query"; + public static final String KEY_DOWNLOAD_SAVE_FRAGMENT = "image_fragment"; + public static final String KEY_MEDIA_SCAN = "media_scan"; + public static final String KEY_IMAGE_RESOLUTION = "image_size"; + public static final String DEFAULT_IMAGE_RESOLUTION = EhConfig.IMAGE_SIZE_AUTO; + public static final int INVALID_DEFAULT_FAV_SLOT = -2; + public static final String KEY_ENABLE_ANALYTICS = "enable_analytics"; + /******************** + ****** Advanced + ********************/ + public static final String KEY_SAVE_PARSE_ERROR_BODY = "save_parse_error_body"; + public static final String KEY_SECURITY = "security"; + public static final String DEFAULT_SECURITY = ""; + public static final String KEY_ENABLE_FINGERPRINT = "enable_fingerprint"; + public static final String KEY_READ_CACHE_SIZE = "read_cache_size"; + public static final int DEFAULT_READ_CACHE_SIZE = 160; + public static final String KEY_BUILT_IN_HOSTS = "built_in_hosts"; + public static final String KEY_APP_LANGUAGE = "app_language"; + private static final String TAG = Settings.class.getSimpleName(); + private static final String KEY_VERSION_CODE = "version_code"; + private static final int DEFAULT_VERSION_CODE = 0; + private static final String KEY_DISPLAY_NAME = "display_name"; + private static final String DEFAULT_DISPLAY_NAME = null; + private static final String KEY_AVATAR = "avatar"; + private static final String DEFAULT_AVATAR = null; + private static final String KEY_SHOW_WARNING = "show_warning"; + private static final boolean DEFAULT_SHOW_WARNING = true; + private static final String KEY_REMOVE_IMAGE_FILES = "include_pic"; + private static final boolean DEFAULT_REMOVE_IMAGE_FILES = true; + private static final String KEY_NEED_SIGN_IN = "need_sign_in"; + private static final boolean DEFAULT_NEED_SIGN_IN = true; + private static final String KEY_SELECT_SITE = "select_site"; + private static final boolean DEFAULT_SELECT_SITE = true; + private static final String KEY_QUICK_SEARCH_TIP = "quick_search_tip"; + private static final boolean DEFAULT_QUICK_SEARCH_TIP = true; + private static final int DEFAULT_THEME = THEME_LIGHT; + private static final boolean DEFAULT_APPLY_NAV_BAR_THEME_COLOR = true; + private static final int DEFAULT_GALLERY_SITE = 1; + private static final String KEY_LAUNCH_PAGE = "launch_page"; + private static final int DEFAULT_LAUNCH_PAGE = 0; + private static final int DEFAULT_LIST_MODE = 0; + private static final int DEFAULT_DETAIL_SIZE = 0; + private static final int DEFAULT_THUMB_SIZE = 1; + private static final int DEFAULT_THUMB_RESOLUTION = 0; + private static final String KEY_FIX_THUMB_URL = "fix_thumb_url"; + private static final boolean DEFAULT_FIX_THUMB_URL = false; + private static final String KEY_SHOW_JPN_TITLE = "show_jpn_title"; + private static final boolean DEFAULT_SHOW_JPN_TITLE = false; + private static final String KEY_SHOW_GALLERY_PAGES = "show_gallery_pages"; + private static final boolean DEFAULT_SHOW_GALLERY_PAGES = false; + private static final boolean DEFAULT_SHOW_TAG_TRANSLATIONS = false; + private static final int DEFAULT_EXCLUDED_TAG_NAMESPACES = 0; + private static final String DEFAULT_EXCLUDED_LANGUAGES = null; + private static final String KEY_CELLULAR_NETWORK_WARNING = "cellular_network_warning"; + private static final boolean DEFAULT_CELLULAR_NETWORK_WARNING = false; + /******************** + ****** Read + ********************/ + private static final String KEY_SCREEN_ROTATION = "screen_rotation"; + private static final int DEFAULT_SCREEN_ROTATION = 0; + private static final String KEY_READING_DIRECTION = "reading_direction"; + private static final int DEFAULT_READING_DIRECTION = GalleryView.LAYOUT_RIGHT_TO_LEFT; + private static final String KEY_PAGE_SCALING = "page_scaling"; + private static final int DEFAULT_PAGE_SCALING = GalleryView.SCALE_FIT; + private static final String KEY_START_POSITION = "start_position"; + private static final int DEFAULT_START_POSITION = GalleryView.START_POSITION_TOP_RIGHT; + private static final String KEY_KEEP_SCREEN_ON = "keep_screen_on"; + private static final boolean DEFAULT_KEEP_SCREEN_ON = false; + private static final String KEY_SHOW_CLOCK = "gallery_show_clock"; + private static final boolean DEFAULT_SHOW_CLOCK = true; + private static final String KEY_SHOW_PROGRESS = "gallery_show_progress"; + private static final boolean DEFAULT_SHOW_PROGRESS = true; + private static final String KEY_SHOW_BATTERY = "gallery_show_battery"; + private static final boolean DEFAULT_SHOW_BATTERY = true; + private static final String KEY_SHOW_PAGE_INTERVAL = "gallery_show_page_interval"; + private static final boolean DEFAULT_SHOW_PAGE_INTERVAL = true; + private static final String KEY_VOLUME_PAGE = "volume_page"; + private static final boolean DEFAULT_VOLUME_PAGE = false; + private static final String KEY_READING_FULLSCREEN = "reading_fullscreen"; + private static final boolean VALUE_READING_FULLSCREEN = true; + private static final String KEY_CUSTOM_SCREEN_LIGHTNESS = "custom_screen_lightness"; + private static final boolean DEFAULT_CUSTOM_SCREEN_LIGHTNESS = false; + private static final String KEY_SCREEN_LIGHTNESS = "screen_lightness"; + private static final int DEFAULT_SCREEN_LIGHTNESS = 50; + private static final boolean DEFAULT_MEDIA_SCAN = false; + private static final String KEY_RECENT_DOWNLOAD_LABEL = "recent_download_label"; + private static final String DEFAULT_RECENT_DOWNLOAD_LABEL = null; + private static final String KEY_HAS_DEFAULT_DOWNLOAD_LABEL = "has_default_download_label"; + private static final boolean DEFAULT_HAS_DOWNLOAD_LABEL = false; + private static final String KEY_DEFAULT_DOWNLOAD_LABEL = "default_download_label"; + private static final String DEFAULT_DOWNLOAD_LABEL = null; + private static final String KEY_MULTI_THREAD_DOWNLOAD = "download_thread"; + private static final int DEFAULT_MULTI_THREAD_DOWNLOAD = 3; + private static final String KEY_PRELOAD_IMAGE = "preload_image"; + private static final int DEFAULT_PRELOAD_IMAGE = 5; + private static final String KEY_DOWNLOAD_ORIGIN_IMAGE = "download_origin_image"; + private static final boolean DEFAULT_DOWNLOAD_ORIGIN_IMAGE = false; + /******************** + ****** Favorites + ********************/ + private static final String KEY_FAV_CAT_0 = "fav_cat_0"; + private static final String KEY_FAV_CAT_1 = "fav_cat_1"; + private static final String KEY_FAV_CAT_2 = "fav_cat_2"; + private static final String KEY_FAV_CAT_3 = "fav_cat_3"; + private static final String KEY_FAV_CAT_4 = "fav_cat_4"; + private static final String KEY_FAV_CAT_5 = "fav_cat_5"; + private static final String KEY_FAV_CAT_6 = "fav_cat_6"; + private static final String KEY_FAV_CAT_7 = "fav_cat_7"; + private static final String KEY_FAV_CAT_8 = "fav_cat_8"; + private static final String KEY_FAV_CAT_9 = "fav_cat_9"; + private static final String DEFAULT_FAV_CAT_0 = "Favorites 0"; + private static final String DEFAULT_FAV_CAT_1 = "Favorites 1"; + private static final String DEFAULT_FAV_CAT_2 = "Favorites 2"; + private static final String DEFAULT_FAV_CAT_3 = "Favorites 3"; + private static final String DEFAULT_FAV_CAT_4 = "Favorites 4"; + private static final String DEFAULT_FAV_CAT_5 = "Favorites 5"; + private static final String DEFAULT_FAV_CAT_6 = "Favorites 6"; + private static final String DEFAULT_FAV_CAT_7 = "Favorites 7"; + private static final String DEFAULT_FAV_CAT_8 = "Favorites 8"; + private static final String DEFAULT_FAV_CAT_9 = "Favorites 9"; + private static final String KEY_FAV_COUNT_0 = "fav_count_0"; + private static final String KEY_FAV_COUNT_1 = "fav_count_1"; + private static final String KEY_FAV_COUNT_2 = "fav_count_2"; + private static final String KEY_FAV_COUNT_3 = "fav_count_3"; + private static final String KEY_FAV_COUNT_4 = "fav_count_4"; + private static final String KEY_FAV_COUNT_5 = "fav_count_5"; + private static final String KEY_FAV_COUNT_6 = "fav_count_6"; + private static final String KEY_FAV_COUNT_7 = "fav_count_7"; + private static final String KEY_FAV_COUNT_8 = "fav_count_8"; + private static final String KEY_FAV_COUNT_9 = "fav_count_9"; + private static final String KEY_FAV_LOCAL = "fav_local"; + private static final String KEY_FAV_CLOUD = "fav_cloud"; + private static final int DEFAULT_FAV_COUNT = 0; + private static final String KEY_RECENT_FAV_CAT = "recent_fav_cat"; + private static final int DEFAULT_RECENT_FAV_CAT = FavListUrlBuilder.FAV_CAT_ALL; + // -1 for local, 0 - 9 for cloud favorite, other for no default fav slot + private static final String KEY_DEFAULT_FAV_SLOT = "default_favorite_2"; + private static final int DEFAULT_DEFAULT_FAV_SLOT = INVALID_DEFAULT_FAV_SLOT; + /******************** + ****** Analytics + ********************/ + private static final String KEY_ASK_ANALYTICS = "ask_analytics"; + private static final boolean DEFAULT_ASK_ANALYTICS = true; + private static final boolean DEFAULT_ENABLE_ANALYTICS = false; + private static final String KEY_USER_ID = "user_id"; + private static final String FILENAME_USER_ID = ".user_id"; + private static final int LENGTH_USER_ID = 32; + /******************** + ****** Update + ********************/ + private static final String KEY_BETA_UPDATE_CHANNEL = "beta_update_channel"; + private static final boolean DEFAULT_BETA_UPDATE_CHANNEL = EhApplication.BETA; + private static final String KEY_SKIP_UPDATE_VERSION = "skip_update_version"; + private static final int DEFAULT_SKIP_UPDATE_VERSION = 0; + private static final boolean DEFAULT_SAVE_PARSE_ERROR_BODY = EhApplication.BETA; + private static final String KEY_SAVE_CRASH_LOG = "save_crash_log"; + private static final boolean DEFAULT_SAVE_CRASH_LOG = false; + private static final boolean DEFAULT_BUILT_IN_HOSTS = false; + private static final String DEFAULT_APP_LANGUAGE = "system"; + private static final String KEY_PROXY_TYPE = "proxy_type"; + private static final int DEFAULT_PROXY_TYPE = EhProxySelector.TYPE_SYSTEM; + private static final String KEY_PROXY_IP = "proxy_ip"; + private static final String DEFAULT_PROXY_IP = null; + private static final String KEY_PROXY_PORT = "proxy_port"; + private static final int DEFAULT_PROXY_PORT = -1; + /******************** + ****** Guide + ********************/ + private static final String KEY_GUIDE_QUICK_SEARCH = "guide_quick_search"; + private static final boolean DEFAULT_GUIDE_QUICK_SEARCH = true; + private static final String KEY_GUIDE_COLLECTIONS = "guide_collections"; + private static final boolean DEFAULT_GUIDE_COLLECTIONS = true; + private static final String KEY_GUIDE_DOWNLOAD_THUMB = "guide_download_thumb"; + private static final boolean DEFAULT_GUIDE_DOWNLOAD_THUMB = true; + private static final String KEY_GUIDE_DOWNLOAD_LABELS = "guide_download_labels"; + private static final boolean DEFAULT_GUIDE_DOWNLOAD_LABELS = true; + private static final String KEY_GUIDE_GALLERY = "guide_gallery"; + private static final boolean DEFAULT_GUIDE_GALLERY = true; + private static final String KEY_CLIPBOARD_TEXT_HASH_CODE = "clipboard_text_hash_code"; + private static final int DEFAULT_CLIPBOARD_TEXT_HASH_CODE = 0; private static Context sContext; private static SharedPreferences sSettingsPre; private static EhConfig sEhConfig; @@ -64,7 +275,7 @@ public class Settings { } private static EhConfig loadEhConfig() { - EhConfig ehConfig= new EhConfig(); + EhConfig ehConfig = new EhConfig(); ehConfig.imageSize = getImageResolution(); ehConfig.excludedLanguages = getExcludedLanguages(); ehConfig.defaultCategories = getDefaultCategories(); @@ -151,9 +362,6 @@ public class Settings { sSettingsPre.edit().putString(key, Integer.toString(value)).apply(); } - private static final String KEY_VERSION_CODE = "version_code"; - private static final int DEFAULT_VERSION_CODE = 0; - public static int getVersionCode() { return getInt(KEY_VERSION_CODE, DEFAULT_VERSION_CODE); } @@ -162,9 +370,6 @@ public class Settings { putInt(KEY_VERSION_CODE, value); } - private static final String KEY_DISPLAY_NAME = "display_name"; - private static final String DEFAULT_DISPLAY_NAME = null; - public static String getDisplayName() { return getString(KEY_DISPLAY_NAME, DEFAULT_DISPLAY_NAME); } @@ -173,9 +378,6 @@ public class Settings { putString(KEY_DISPLAY_NAME, value); } - private static final String KEY_AVATAR = "avatar"; - private static final String DEFAULT_AVATAR = null; - public static String getAvatar() { return getString(KEY_AVATAR, DEFAULT_AVATAR); } @@ -184,9 +386,6 @@ public class Settings { putString(KEY_AVATAR, value); } - private static final String KEY_SHOW_WARNING = "show_warning"; - private static final boolean DEFAULT_SHOW_WARNING = true; - public static boolean getShowWarning() { return getBoolean(KEY_SHOW_WARNING, DEFAULT_SHOW_WARNING); } @@ -195,9 +394,6 @@ public class Settings { putBoolean(KEY_SHOW_WARNING, value); } - private static final String KEY_REMOVE_IMAGE_FILES = "include_pic"; - private static final boolean DEFAULT_REMOVE_IMAGE_FILES = true; - public static boolean getRemoveImageFiles() { return getBoolean(KEY_REMOVE_IMAGE_FILES, DEFAULT_REMOVE_IMAGE_FILES); } @@ -210,9 +406,6 @@ public class Settings { return sEhConfig; } - private static final String KEY_NEED_SIGN_IN = "need_sign_in"; - private static final boolean DEFAULT_NEED_SIGN_IN = true; - public static boolean getNeedSignIn() { return getBoolean(KEY_NEED_SIGN_IN, DEFAULT_NEED_SIGN_IN); } @@ -221,9 +414,6 @@ public class Settings { putBoolean(KEY_NEED_SIGN_IN, value); } - private static final String KEY_SELECT_SITE = "select_site"; - private static final boolean DEFAULT_SELECT_SITE = true; - public static boolean getSelectSite() { return getBoolean(KEY_SELECT_SITE, DEFAULT_SELECT_SITE); } @@ -232,9 +422,6 @@ public class Settings { putBoolean(KEY_SELECT_SITE, value); } - private static final String KEY_QUICK_SEARCH_TIP = "quick_search_tip"; - private static final boolean DEFAULT_QUICK_SEARCH_TIP = true; - public static boolean getQuickSearchTip() { return getBoolean(KEY_QUICK_SEARCH_TIP, DEFAULT_QUICK_SEARCH_TIP); } @@ -243,16 +430,6 @@ public class Settings { putBoolean(KEY_QUICK_SEARCH_TIP, value); } - /******************** - ****** Eh - ********************/ - - public static final String KEY_THEME = "theme"; - public static final int THEME_LIGHT = 0; - public static final int THEME_DARK = 1; - public static final int THEME_BLACK = 2; - private static final int DEFAULT_THEME = THEME_LIGHT; - public static int getTheme() { return getIntFromStr(KEY_THEME, DEFAULT_THEME); } @@ -261,16 +438,10 @@ public class Settings { putIntToStr(KEY_THEME, theme); } - public static final String KEY_APPLY_NAV_BAR_THEME_COLOR = "apply_nav_bar_theme_color"; - private static final boolean DEFAULT_APPLY_NAV_BAR_THEME_COLOR = true; - public static boolean getApplyNavBarThemeColor() { return getBoolean(KEY_APPLY_NAV_BAR_THEME_COLOR, DEFAULT_APPLY_NAV_BAR_THEME_COLOR); } - public static final String KEY_GALLERY_SITE = "gallery_site"; - private static final int DEFAULT_GALLERY_SITE = 1; - public static int getGallerySite() { return getIntFromStr(KEY_GALLERY_SITE, DEFAULT_GALLERY_SITE); } @@ -279,9 +450,6 @@ public class Settings { putIntToStr(KEY_GALLERY_SITE, value); } - private static final String KEY_LAUNCH_PAGE = "launch_page"; - private static final int DEFAULT_LAUNCH_PAGE = 0; - public static String getLaunchPageGalleryListSceneAction() { int value = getIntFromStr(KEY_LAUNCH_PAGE, DEFAULT_LAUNCH_PAGE); switch (value) { @@ -295,16 +463,10 @@ public class Settings { } } - public static final String KEY_LIST_MODE = "list_mode"; - private static final int DEFAULT_LIST_MODE = 0; - public static int getListMode() { return getIntFromStr(KEY_LIST_MODE, DEFAULT_LIST_MODE); } - public static final String KEY_DETAIL_SIZE = "detail_size"; - private static final int DEFAULT_DETAIL_SIZE = 0; - public static int getDetailSize() { return getIntFromStr(KEY_DETAIL_SIZE, DEFAULT_DETAIL_SIZE); } @@ -320,9 +482,6 @@ public class Settings { } } - public static final String KEY_THUMB_SIZE = "thumb_size"; - private static final int DEFAULT_THUMB_SIZE = 1; - public static int getThumbSize() { return getIntFromStr(KEY_THUMB_SIZE, DEFAULT_THUMB_SIZE); } @@ -340,44 +499,26 @@ public class Settings { } } - public static final String KEY_THUMB_RESOLUTION = "thumb_resolution"; - private static final int DEFAULT_THUMB_RESOLUTION = 0; - public static int getThumbResolution() { return getIntFromStr(KEY_THUMB_RESOLUTION, DEFAULT_THUMB_RESOLUTION); } - private static final String KEY_FIX_THUMB_URL = "fix_thumb_url"; - private static final boolean DEFAULT_FIX_THUMB_URL = false; - public static boolean getFixThumbUrl() { return getBoolean(KEY_FIX_THUMB_URL, DEFAULT_FIX_THUMB_URL); } - private static final String KEY_SHOW_JPN_TITLE = "show_jpn_title"; - private static final boolean DEFAULT_SHOW_JPN_TITLE = false; - public static boolean getShowJpnTitle() { return getBoolean(KEY_SHOW_JPN_TITLE, DEFAULT_SHOW_JPN_TITLE); } - private static final String KEY_SHOW_GALLERY_PAGES = "show_gallery_pages"; - private static final boolean DEFAULT_SHOW_GALLERY_PAGES = false; - public static boolean getShowGalleryPages() { return getBoolean(KEY_SHOW_GALLERY_PAGES, DEFAULT_SHOW_GALLERY_PAGES); } - public static final String KEY_SHOW_TAG_TRANSLATIONS = "show_tag_translations"; - private static final boolean DEFAULT_SHOW_TAG_TRANSLATIONS = false; - public static boolean getShowTagTranslations() { return getBoolean(KEY_SHOW_TAG_TRANSLATIONS, DEFAULT_SHOW_TAG_TRANSLATIONS); } - public static final String KEY_DEFAULT_CATEGORIES = "default_categories"; - public static final int DEFAULT_DEFAULT_CATEGORIES = EhUtils.ALL_CATEGORY; - public static int getDefaultCategories() { return getInt(KEY_DEFAULT_CATEGORIES, DEFAULT_DEFAULT_CATEGORIES); } @@ -388,9 +529,6 @@ public class Settings { putInt(KEY_DEFAULT_CATEGORIES, value); } - public static final String KEY_EXCLUDED_TAG_NAMESPACES = "excluded_tag_namespaces"; - private static final int DEFAULT_EXCLUDED_TAG_NAMESPACES = 0; - public static int getExcludedTagNamespaces() { return getInt(KEY_EXCLUDED_TAG_NAMESPACES, DEFAULT_EXCLUDED_TAG_NAMESPACES); } @@ -401,9 +539,6 @@ public class Settings { putInt(KEY_EXCLUDED_TAG_NAMESPACES, value); } - public static final String KEY_EXCLUDED_LANGUAGES = "excluded_languages"; - private static final String DEFAULT_EXCLUDED_LANGUAGES = null; - public static String getExcludedLanguages() { return getString(KEY_EXCLUDED_LANGUAGES, DEFAULT_EXCLUDED_LANGUAGES); } @@ -414,19 +549,10 @@ public class Settings { putString(KEY_EXCLUDED_LANGUAGES, value); } - private static final String KEY_CELLULAR_NETWORK_WARNING = "cellular_network_warning"; - private static final boolean DEFAULT_CELLULAR_NETWORK_WARNING = false; - public static boolean getCellularNetworkWarning() { return getBoolean(KEY_CELLULAR_NETWORK_WARNING, DEFAULT_CELLULAR_NETWORK_WARNING); } - /******************** - ****** Read - ********************/ - private static final String KEY_SCREEN_ROTATION = "screen_rotation"; - private static final int DEFAULT_SCREEN_ROTATION = 0; - public static int getScreenRotation() { return getIntFromStr(KEY_SCREEN_ROTATION, DEFAULT_SCREEN_ROTATION); } @@ -435,9 +561,6 @@ public class Settings { putIntToStr(KEY_SCREEN_ROTATION, value); } - private static final String KEY_READING_DIRECTION = "reading_direction"; - private static final int DEFAULT_READING_DIRECTION = GalleryView.LAYOUT_RIGHT_TO_LEFT; - @GalleryView.LayoutMode public static int getReadingDirection() { return GalleryView.sanitizeLayoutMode(getIntFromStr(KEY_READING_DIRECTION, DEFAULT_READING_DIRECTION)); @@ -447,9 +570,6 @@ public class Settings { putIntToStr(KEY_READING_DIRECTION, value); } - private static final String KEY_PAGE_SCALING = "page_scaling"; - private static final int DEFAULT_PAGE_SCALING = GalleryView.SCALE_FIT; - @GalleryView.ScaleMode public static int getPageScaling() { return GalleryView.sanitizeScaleMode(getIntFromStr(KEY_PAGE_SCALING, DEFAULT_PAGE_SCALING)); @@ -459,9 +579,6 @@ public class Settings { putIntToStr(KEY_PAGE_SCALING, value); } - private static final String KEY_START_POSITION = "start_position"; - private static final int DEFAULT_START_POSITION = GalleryView.START_POSITION_TOP_RIGHT; - @GalleryView.StartPosition public static int getStartPosition() { return GalleryView.sanitizeStartPosition(getIntFromStr(KEY_START_POSITION, DEFAULT_START_POSITION)); @@ -471,9 +588,6 @@ public class Settings { putIntToStr(KEY_START_POSITION, value); } - private static final String KEY_KEEP_SCREEN_ON = "keep_screen_on"; - private static final boolean DEFAULT_KEEP_SCREEN_ON = false; - public static boolean getKeepScreenOn() { return getBoolean(KEY_KEEP_SCREEN_ON, DEFAULT_KEEP_SCREEN_ON); } @@ -482,9 +596,6 @@ public class Settings { putBoolean(KEY_KEEP_SCREEN_ON, value); } - private static final String KEY_SHOW_CLOCK = "gallery_show_clock"; - private static final boolean DEFAULT_SHOW_CLOCK = true; - public static boolean getShowClock() { return getBoolean(KEY_SHOW_CLOCK, DEFAULT_SHOW_CLOCK); } @@ -493,9 +604,6 @@ public class Settings { putBoolean(KEY_SHOW_CLOCK, value); } - private static final String KEY_SHOW_PROGRESS = "gallery_show_progress"; - private static final boolean DEFAULT_SHOW_PROGRESS = true; - public static boolean getShowProgress() { return getBoolean(KEY_SHOW_PROGRESS, DEFAULT_SHOW_PROGRESS); } @@ -504,9 +612,6 @@ public class Settings { putBoolean(KEY_SHOW_PROGRESS, value); } - private static final String KEY_SHOW_BATTERY = "gallery_show_battery"; - private static final boolean DEFAULT_SHOW_BATTERY = true; - public static boolean getShowBattery() { return getBoolean(KEY_SHOW_BATTERY, DEFAULT_SHOW_BATTERY); } @@ -515,9 +620,6 @@ public class Settings { putBoolean(KEY_SHOW_BATTERY, value); } - private static final String KEY_SHOW_PAGE_INTERVAL = "gallery_show_page_interval"; - private static final boolean DEFAULT_SHOW_PAGE_INTERVAL = true; - public static boolean getShowPageInterval() { return getBoolean(KEY_SHOW_PAGE_INTERVAL, DEFAULT_SHOW_PAGE_INTERVAL); } @@ -526,9 +628,6 @@ public class Settings { putBoolean(KEY_SHOW_PAGE_INTERVAL, value); } - private static final String KEY_VOLUME_PAGE = "volume_page"; - private static final boolean DEFAULT_VOLUME_PAGE = false; - public static boolean getVolumePage() { return getBoolean(KEY_VOLUME_PAGE, DEFAULT_VOLUME_PAGE); } @@ -537,9 +636,6 @@ public class Settings { putBoolean(KEY_VOLUME_PAGE, value); } - private static final String KEY_READING_FULLSCREEN = "reading_fullscreen"; - private static final boolean VALUE_READING_FULLSCREEN = true; - public static boolean getReadingFullscreen() { return getBoolean(KEY_READING_FULLSCREEN, VALUE_READING_FULLSCREEN); } @@ -548,9 +644,6 @@ public class Settings { putBoolean(KEY_READING_FULLSCREEN, value); } - private static final String KEY_CUSTOM_SCREEN_LIGHTNESS = "custom_screen_lightness"; - private static final boolean DEFAULT_CUSTOM_SCREEN_LIGHTNESS = false; - public static boolean getCustomScreenLightness() { return getBoolean(KEY_CUSTOM_SCREEN_LIGHTNESS, DEFAULT_CUSTOM_SCREEN_LIGHTNESS); } @@ -559,9 +652,6 @@ public class Settings { putBoolean(KEY_CUSTOM_SCREEN_LIGHTNESS, value); } - private static final String KEY_SCREEN_LIGHTNESS = "screen_lightness"; - private static final int DEFAULT_SCREEN_LIGHTNESS = 50; - public static int getScreenLightness() { return getInt(KEY_SCREEN_LIGHTNESS, DEFAULT_SCREEN_LIGHTNESS); } @@ -570,28 +660,14 @@ public class Settings { putInt(KEY_SCREEN_LIGHTNESS, value); } - /******************** - ****** Privacy and Security - ********************/ - public static final String KEY_SEC_SECURITY = "enable_secure"; - public static final boolean VALUE_SEC_SECURITY = false; - public static boolean getEnabledSecurity() { return getBoolean(KEY_SEC_SECURITY, VALUE_SEC_SECURITY); } + public static void putEnabledSecurity(boolean value) { putBoolean(KEY_READING_FULLSCREEN, value); } - /******************** - ****** Download - ********************/ - public static final String KEY_DOWNLOAD_SAVE_SCHEME = "image_scheme"; - public static final String KEY_DOWNLOAD_SAVE_AUTHORITY = "image_authority"; - public static final String KEY_DOWNLOAD_SAVE_PATH = "image_path"; - public static final String KEY_DOWNLOAD_SAVE_QUERY = "image_query"; - public static final String KEY_DOWNLOAD_SAVE_FRAGMENT = "image_fragment"; - @Nullable public static UniFile getDownloadLocation() { UniFile dir = null; @@ -625,16 +701,10 @@ public class Settings { } } - public static final String KEY_MEDIA_SCAN = "media_scan"; - private static final boolean DEFAULT_MEDIA_SCAN = false; - public static boolean getMediaScan() { return getBoolean(KEY_MEDIA_SCAN, DEFAULT_MEDIA_SCAN); } - private static final String KEY_RECENT_DOWNLOAD_LABEL = "recent_download_label"; - private static final String DEFAULT_RECENT_DOWNLOAD_LABEL = null; - public static String getRecentDownloadLabel() { return getString(KEY_RECENT_DOWNLOAD_LABEL, DEFAULT_RECENT_DOWNLOAD_LABEL); } @@ -643,9 +713,6 @@ public class Settings { putString(KEY_RECENT_DOWNLOAD_LABEL, value); } - private static final String KEY_HAS_DEFAULT_DOWNLOAD_LABEL = "has_default_download_label"; - private static final boolean DEFAULT_HAS_DOWNLOAD_LABEL = false; - public static boolean getHasDefaultDownloadLabel() { return getBoolean(KEY_HAS_DEFAULT_DOWNLOAD_LABEL, DEFAULT_HAS_DOWNLOAD_LABEL); } @@ -654,9 +721,6 @@ public class Settings { putBoolean(KEY_HAS_DEFAULT_DOWNLOAD_LABEL, hasDefaultDownloadLabel); } - private static final String KEY_DEFAULT_DOWNLOAD_LABEL = "default_download_label"; - private static final String DEFAULT_DOWNLOAD_LABEL = null; - public static String getDefaultDownloadLabel() { return getString(KEY_DEFAULT_DOWNLOAD_LABEL, DEFAULT_DOWNLOAD_LABEL); } @@ -665,9 +729,6 @@ public class Settings { putString(KEY_DEFAULT_DOWNLOAD_LABEL, value); } - private static final String KEY_MULTI_THREAD_DOWNLOAD = "download_thread"; - private static final int DEFAULT_MULTI_THREAD_DOWNLOAD = 3; - public static int getMultiThreadDownload() { return getIntFromStr(KEY_MULTI_THREAD_DOWNLOAD, DEFAULT_MULTI_THREAD_DOWNLOAD); } @@ -676,9 +737,6 @@ public class Settings { putIntToStr(KEY_MULTI_THREAD_DOWNLOAD, value); } - private static final String KEY_PRELOAD_IMAGE = "preload_image"; - private static final int DEFAULT_PRELOAD_IMAGE = 5; - public static int getPreloadImage() { return getIntFromStr(KEY_PRELOAD_IMAGE, DEFAULT_PRELOAD_IMAGE); } @@ -687,9 +745,6 @@ public class Settings { putIntToStr(KEY_PRELOAD_IMAGE, value); } - public static final String KEY_IMAGE_RESOLUTION = "image_size"; - public static final String DEFAULT_IMAGE_RESOLUTION = EhConfig.IMAGE_SIZE_AUTO; - public static String getImageResolution() { return getString(KEY_IMAGE_RESOLUTION, DEFAULT_IMAGE_RESOLUTION); } @@ -700,9 +755,6 @@ public class Settings { putString(KEY_IMAGE_RESOLUTION, value); } - private static final String KEY_DOWNLOAD_ORIGIN_IMAGE = "download_origin_image"; - private static final boolean DEFAULT_DOWNLOAD_ORIGIN_IMAGE = false; - public static boolean getDownloadOriginImage() { return getBoolean(KEY_DOWNLOAD_ORIGIN_IMAGE, DEFAULT_DOWNLOAD_ORIGIN_IMAGE); } @@ -711,46 +763,6 @@ public class Settings { putBoolean(KEY_DOWNLOAD_ORIGIN_IMAGE, value); } - /******************** - ****** Favorites - ********************/ - private static final String KEY_FAV_CAT_0 = "fav_cat_0"; - private static final String KEY_FAV_CAT_1 = "fav_cat_1"; - private static final String KEY_FAV_CAT_2 = "fav_cat_2"; - private static final String KEY_FAV_CAT_3 = "fav_cat_3"; - private static final String KEY_FAV_CAT_4 = "fav_cat_4"; - private static final String KEY_FAV_CAT_5 = "fav_cat_5"; - private static final String KEY_FAV_CAT_6 = "fav_cat_6"; - private static final String KEY_FAV_CAT_7 = "fav_cat_7"; - private static final String KEY_FAV_CAT_8 = "fav_cat_8"; - private static final String KEY_FAV_CAT_9 = "fav_cat_9"; - private static final String DEFAULT_FAV_CAT_0 = "Favorites 0"; - private static final String DEFAULT_FAV_CAT_1 = "Favorites 1"; - private static final String DEFAULT_FAV_CAT_2 = "Favorites 2"; - private static final String DEFAULT_FAV_CAT_3 = "Favorites 3"; - private static final String DEFAULT_FAV_CAT_4 = "Favorites 4"; - private static final String DEFAULT_FAV_CAT_5 = "Favorites 5"; - private static final String DEFAULT_FAV_CAT_6 = "Favorites 6"; - private static final String DEFAULT_FAV_CAT_7 = "Favorites 7"; - private static final String DEFAULT_FAV_CAT_8 = "Favorites 8"; - private static final String DEFAULT_FAV_CAT_9 = "Favorites 9"; - - private static final String KEY_FAV_COUNT_0 = "fav_count_0"; - private static final String KEY_FAV_COUNT_1 = "fav_count_1"; - private static final String KEY_FAV_COUNT_2 = "fav_count_2"; - private static final String KEY_FAV_COUNT_3 = "fav_count_3"; - private static final String KEY_FAV_COUNT_4 = "fav_count_4"; - private static final String KEY_FAV_COUNT_5 = "fav_count_5"; - private static final String KEY_FAV_COUNT_6 = "fav_count_6"; - private static final String KEY_FAV_COUNT_7 = "fav_count_7"; - private static final String KEY_FAV_COUNT_8 = "fav_count_8"; - private static final String KEY_FAV_COUNT_9 = "fav_count_9"; - - private static final String KEY_FAV_LOCAL = "fav_local"; - private static final String KEY_FAV_CLOUD = "fav_cloud"; - - private static final int DEFAULT_FAV_COUNT = 0; - public static String[] getFavCat() { String[] favCat = new String[10]; favCat[0] = sSettingsPre.getString(KEY_FAV_CAT_0, DEFAULT_FAV_CAT_0); @@ -829,9 +841,6 @@ public class Settings { sSettingsPre.edit().putInt(KEY_FAV_CLOUD, count).apply(); } - private static final String KEY_RECENT_FAV_CAT = "recent_fav_cat"; - private static final int DEFAULT_RECENT_FAV_CAT = FavListUrlBuilder.FAV_CAT_ALL; - public static int getRecentFavCat() { return getInt(KEY_RECENT_FAV_CAT, DEFAULT_RECENT_FAV_CAT); } @@ -840,11 +849,6 @@ public class Settings { putInt(KEY_RECENT_FAV_CAT, value); } - // -1 for local, 0 - 9 for cloud favorite, other for no default fav slot - private static final String KEY_DEFAULT_FAV_SLOT = "default_favorite_2"; - public static final int INVALID_DEFAULT_FAV_SLOT = -2; - private static final int DEFAULT_DEFAULT_FAV_SLOT = INVALID_DEFAULT_FAV_SLOT; - public static int getDefaultFavSlot() { return getInt(KEY_DEFAULT_FAV_SLOT, DEFAULT_DEFAULT_FAV_SLOT); } @@ -853,12 +857,6 @@ public class Settings { putInt(KEY_DEFAULT_FAV_SLOT, value); } - /******************** - ****** Analytics - ********************/ - private static final String KEY_ASK_ANALYTICS = "ask_analytics"; - private static final boolean DEFAULT_ASK_ANALYTICS = true; - public static boolean getAskAnalytics() { return getBoolean(KEY_ASK_ANALYTICS, DEFAULT_ASK_ANALYTICS); } @@ -867,9 +865,6 @@ public class Settings { putBoolean(KEY_ASK_ANALYTICS, value); } - public static final String KEY_ENABLE_ANALYTICS = "enable_analytics"; - private static final boolean DEFAULT_ENABLE_ANALYTICS = false; - public static boolean getEnableAnalytics() { return getBoolean(KEY_ENABLE_ANALYTICS, DEFAULT_ENABLE_ANALYTICS); } @@ -878,10 +873,6 @@ public class Settings { putBoolean(KEY_ENABLE_ANALYTICS, value); } - private static final String KEY_USER_ID = "user_id"; - private static final String FILENAME_USER_ID = ".user_id"; - private static final int LENGTH_USER_ID = 32; - public static String getUserID() { boolean writeXml = false; boolean writeFile = false; @@ -940,14 +931,6 @@ public class Settings { return true; } - /******************** - ****** Update - ********************/ - private static final String KEY_BETA_UPDATE_CHANNEL = "beta_update_channel"; - private static final boolean DEFAULT_BETA_UPDATE_CHANNEL = EhApplication.BETA; - private static final String KEY_SKIP_UPDATE_VERSION = "skip_update_version"; - private static final int DEFAULT_SKIP_UPDATE_VERSION = 0; - public static boolean getBetaUpdateChannel() { return getBoolean(KEY_BETA_UPDATE_CHANNEL, DEFAULT_BETA_UPDATE_CHANNEL); } @@ -964,12 +947,6 @@ public class Settings { putInt(KEY_SKIP_UPDATE_VERSION, value); } - /******************** - ****** Advanced - ********************/ - public static final String KEY_SAVE_PARSE_ERROR_BODY = "save_parse_error_body"; - private static final boolean DEFAULT_SAVE_PARSE_ERROR_BODY = EhApplication.BETA; - public static boolean getSaveParseErrorBody() { return getBoolean(KEY_SAVE_PARSE_ERROR_BODY, DEFAULT_SAVE_PARSE_ERROR_BODY); } @@ -978,16 +955,10 @@ public class Settings { putBoolean(KEY_SAVE_PARSE_ERROR_BODY, value); } - private static final String KEY_SAVE_CRASH_LOG = "save_crash_log"; - private static final boolean DEFAULT_SAVE_CRASH_LOG = false; - public static boolean getSaveCrashLog() { return getBoolean(KEY_SAVE_CRASH_LOG, DEFAULT_SAVE_CRASH_LOG); } - public static final String KEY_SECURITY = "security"; - public static final String DEFAULT_SECURITY = ""; - public static String getSecurity() { return getString(KEY_SECURITY, DEFAULT_SECURITY); } @@ -996,8 +967,6 @@ public class Settings { putString(KEY_SECURITY, value); } - public static final String KEY_ENABLE_FINGERPRINT = "enable_fingerprint"; - public static boolean getEnableFingerprint() { return getBoolean(KEY_ENABLE_FINGERPRINT, false); } @@ -1006,16 +975,10 @@ public class Settings { putBoolean(KEY_ENABLE_FINGERPRINT, value); } - public static final String KEY_READ_CACHE_SIZE = "read_cache_size"; - public static final int DEFAULT_READ_CACHE_SIZE = 160; - public static int getReadCacheSize() { return getIntFromStr(KEY_READ_CACHE_SIZE, DEFAULT_READ_CACHE_SIZE); } - public static final String KEY_BUILT_IN_HOSTS = "built_in_hosts"; - private static final boolean DEFAULT_BUILT_IN_HOSTS = false; - public static boolean getBuiltInHosts() { return getBoolean(KEY_BUILT_IN_HOSTS, DEFAULT_BUILT_IN_HOSTS); } @@ -1024,9 +987,6 @@ public class Settings { putBoolean(KEY_BUILT_IN_HOSTS, value); } - public static final String KEY_APP_LANGUAGE = "app_language"; - private static final String DEFAULT_APP_LANGUAGE = "system"; - public static String getAppLanguage() { return getString(KEY_APP_LANGUAGE, DEFAULT_APP_LANGUAGE); } @@ -1035,9 +995,6 @@ public class Settings { putString(KEY_APP_LANGUAGE, value); } - private static final String KEY_PROXY_TYPE = "proxy_type"; - private static final int DEFAULT_PROXY_TYPE = EhProxySelector.TYPE_SYSTEM; - public static int getProxyType() { return getInt(KEY_PROXY_TYPE, DEFAULT_PROXY_TYPE); } @@ -1046,9 +1003,6 @@ public class Settings { putInt(KEY_PROXY_TYPE, value); } - private static final String KEY_PROXY_IP = "proxy_ip"; - private static final String DEFAULT_PROXY_IP = null; - public static String getProxyIp() { return getString(KEY_PROXY_IP, DEFAULT_PROXY_IP); } @@ -1057,9 +1011,6 @@ public class Settings { putString(KEY_PROXY_IP, value); } - private static final String KEY_PROXY_PORT = "proxy_port"; - private static final int DEFAULT_PROXY_PORT = -1; - public static int getProxyPort() { return getInt(KEY_PROXY_PORT, DEFAULT_PROXY_PORT); } @@ -1068,12 +1019,6 @@ public class Settings { putInt(KEY_PROXY_PORT, value); } - /******************** - ****** Guide - ********************/ - private static final String KEY_GUIDE_QUICK_SEARCH = "guide_quick_search"; - private static final boolean DEFAULT_GUIDE_QUICK_SEARCH = true; - public static boolean getGuideQuickSearch() { return getBoolean(KEY_GUIDE_QUICK_SEARCH, DEFAULT_GUIDE_QUICK_SEARCH); } @@ -1082,9 +1027,6 @@ public class Settings { putBoolean(KEY_GUIDE_QUICK_SEARCH, value); } - private static final String KEY_GUIDE_COLLECTIONS = "guide_collections"; - private static final boolean DEFAULT_GUIDE_COLLECTIONS = true; - public static boolean getGuideCollections() { return getBoolean(KEY_GUIDE_COLLECTIONS, DEFAULT_GUIDE_COLLECTIONS); } @@ -1093,9 +1035,6 @@ public class Settings { putBoolean(KEY_GUIDE_COLLECTIONS, value); } - private static final String KEY_GUIDE_DOWNLOAD_THUMB = "guide_download_thumb"; - private static final boolean DEFAULT_GUIDE_DOWNLOAD_THUMB = true; - public static boolean getGuideDownloadThumb() { return getBoolean(KEY_GUIDE_DOWNLOAD_THUMB, DEFAULT_GUIDE_DOWNLOAD_THUMB); } @@ -1104,9 +1043,6 @@ public class Settings { putBoolean(KEY_GUIDE_DOWNLOAD_THUMB, value); } - private static final String KEY_GUIDE_DOWNLOAD_LABELS = "guide_download_labels"; - private static final boolean DEFAULT_GUIDE_DOWNLOAD_LABELS = true; - public static boolean getGuideDownloadLabels() { return getBoolean(KEY_GUIDE_DOWNLOAD_LABELS, DEFAULT_GUIDE_DOWNLOAD_LABELS); } @@ -1115,9 +1051,6 @@ public class Settings { putBoolean(KEY_GUIDE_DOWNLOAD_LABELS, value); } - private static final String KEY_GUIDE_GALLERY = "guide_gallery"; - private static final boolean DEFAULT_GUIDE_GALLERY = true; - public static boolean getGuideGallery() { return getBoolean(KEY_GUIDE_GALLERY, DEFAULT_GUIDE_GALLERY); } @@ -1126,9 +1059,6 @@ public class Settings { putBoolean(KEY_GUIDE_GALLERY, value); } - private static final String KEY_CLIPBOARD_TEXT_HASH_CODE = "clipboard_text_hash_code"; - private static final int DEFAULT_CLIPBOARD_TEXT_HASH_CODE = 0; - public static int getClipboardTextHashCode() { return getInt(KEY_CLIPBOARD_TEXT_HASH_CODE, DEFAULT_CLIPBOARD_TEXT_HASH_CODE); } diff --git a/app/src/main/java/com/hippo/ehviewer/UrlOpener.java b/app/src/main/java/com/hippo/ehviewer/UrlOpener.java index 9b30f84ce..03de474d2 100644 --- a/app/src/main/java/com/hippo/ehviewer/UrlOpener.java +++ b/app/src/main/java/com/hippo/ehviewer/UrlOpener.java @@ -22,7 +22,9 @@ import android.net.Uri; import android.provider.Browser; import android.text.TextUtils; import android.widget.Toast; + import androidx.annotation.NonNull; + import com.hippo.ehviewer.client.EhUrlOpener; import com.hippo.ehviewer.ui.MainActivity; import com.hippo.scene.Announcer; diff --git a/app/src/main/java/com/hippo/ehviewer/client/EhClient.java b/app/src/main/java/com/hippo/ehviewer/client/EhClient.java index 32d06f287..dd6d708d7 100644 --- a/app/src/main/java/com/hippo/ehviewer/client/EhClient.java +++ b/app/src/main/java/com/hippo/ehviewer/client/EhClient.java @@ -18,15 +18,18 @@ package com.hippo.ehviewer.client; import android.content.Context; import android.os.AsyncTask; + import com.hippo.ehviewer.EhApplication; import com.hippo.ehviewer.client.exception.CancelledException; import com.hippo.util.ExceptionUtils; import com.hippo.util.IoThreadPoolExecutor; import com.hippo.yorozuya.SimpleHandler; + import java.io.File; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicReference; + import okhttp3.Call; import okhttp3.OkHttpClient; @@ -70,14 +73,22 @@ public class EhClient { } } + public interface Callback { + + void onSuccess(E result); + + void onFailure(Exception e); + + void onCancel(); + } + public class Task extends AsyncTask { private final int mMethod; - private Callback mCallback; - private EhConfig mEhConfig; - private final AtomicReference mCall = new AtomicReference<>(); private final AtomicBoolean mStop = new AtomicBoolean(); + private Callback mCallback; + private EhConfig mEhConfig; public Task(int method, Callback callback, EhConfig ehConfig) { mMethod = method; @@ -202,13 +213,4 @@ public class EhClient { mCall.lazySet(null); } } - - public interface Callback { - - void onSuccess(E result); - - void onFailure(Exception e); - - void onCancel(); - } } diff --git a/app/src/main/java/com/hippo/ehviewer/client/EhConfig.java b/app/src/main/java/com/hippo/ehviewer/client/EhConfig.java index 026f93b39..53952e353 100644 --- a/app/src/main/java/com/hippo/ehviewer/client/EhConfig.java +++ b/app/src/main/java/com/hippo/ehviewer/client/EhConfig.java @@ -27,180 +27,14 @@ public class EhConfig implements Cloneable { * The Cookie key of uconfig */ public static final String KEY_UCONFIG = "uconfig"; - - /** - * The key of load images through the Hentai@Home Network - * @see #LOAD_FROM_HAH_YES - * @see #LOAD_FROM_HAH_NO - */ - private static final String KEY_LOAD_FROM_HAH = "uh"; - /** - * The key of Image Size Settings - * @see #IMAGE_SIZE_AUTO - * @see #IMAGE_SIZE_780X - * @see #IMAGE_SIZE_980X - * @see #IMAGE_SIZE_1280X - * @see #IMAGE_SIZE_1600X - * @see #IMAGE_SIZE_2400X - */ - private static final String KEY_IMAGE_SIZE = "xr"; - /** - * The key of scale images width - */ - private static final String KEY_SCALE_WIDTH = "rx"; - /** - * The key of scale images height - */ - private static final String KEY_SCALE_HEIGHT = "ry"; - /** - * The key of Gallery Name Display - * @see #GALLERY_TITLE_DEFAULT - * @see #GALLERY_TITLE_JAPANESE - */ - private static final String KEY_GALLERY_TITLE = "tl"; - /** - * The key of the behavior for downloading archiver - * @see #ARCHIVER_DOWNLOAD_MAMS - * @see #ARCHIVER_DOWNLOAD_MAAS - * @see #ARCHIVER_DOWNLOAD_AAMS - * @see #ARCHIVER_DOWNLOAD_AAAS - */ - private static final String KEY_ARCHIVER_DOWNLOAD = "ar"; - /** - * The key of display mode would you like to use on the front and search pages - * @see #LAYOUT_MODE_LIST - * @see #LAYOUT_MODE_THUMB - */ - private static final String KEY_LAYOUT_MODE = "dm"; - /** - * The key for show popular - * @see #POPULAR_YES - * @see #POPULAR_NO - */ - private static final String KEY_POPULAR = "prn"; - /** - * The key of categories would you like to view as default on the front page - */ - private static final String KEY_DEFAULT_CATEGORIES = "cats"; - /** - * The key for favorites sort - * @see #FAVORITES_SORT_GALLERY_UPDATE_TIME - * @see #FAVORITES_SORT_FAVORITED_TIME - */ - private static final String KEY_FAVORITES_SORT = "fs"; - /** - * The key of exclude certain namespaces from a default tag search - */ - private static final String KEY_EXCLUDED_NAMESPACES = "xns"; - /** - * The key of hide galleries in certain languages from the gallery list and searches - */ - private static final String KEY_EXCLUDED_LANGUAGES = "xl"; - /** - * The key of how many results would you like per page for the index/search page and torrent search pages - * @see #RESULT_COUNT_25 - * @see #RESULT_COUNT_50 - * @see #RESULT_COUNT_100 - * @see #RESULT_COUNT_200 - */ - private static final String KEY_RESULT_COUNT = "rc"; - /** - * The key of mouse-over thumb - * @see #MOUSE_OVER_YES - * @see #MOUSE_OVER_NO - */ - private static final String KEY_MOUSE_OVER = "lt"; - /** - * The key of preview size - * @see #PREVIEW_SIZE_NORMAL - * @see #PREVIEW_SIZE_LARGE - */ - private static final String KEY_PREVIEW_SIZE = "ts"; - /** - * The key of preview row per page - * @see #PREVIEW_ROW_4 - * @see #PREVIEW_ROW_10 - * @see #PREVIEW_ROW_20 - * @see #PREVIEW_ROW_40 - */ - private static final String KEY_PREVIEW_ROW = "tr"; - /** - * The key of sort order for gallery comments - * @see #COMMENTS_SORT_OLDEST_FIRST - * @see #COMMENTS_SORT_RECENT_FIRST - * @see #COMMENTS_SORT_HIGHEST_SCORE_FIRST - */ - private static final String KEY_COMMENTS_SORT = "cs"; - /** - * The key of show gallery comment votes - * @see #COMMENTS_VOTES_POP - * @see #COMMENTS_VOTES_ALWAYS - */ - private static final String KEY_COMMENTS_VOTES = "sc"; - /** - * The key of sort order for gallery tags - * @see #TAGS_SORT_ALPHABETICAL - * @see #TAGS_SORT_POWER - */ - private static final String KEY_TAGS_SORT = "to"; - /** - * The key of show gallery page numbers - * @see #SHOW_GALLERY_INDEX_NO - * @see #SHOW_GALLERY_INDEX_YES - */ - private static final String KEY_SHOW_GALLERY_INDEX = "pn"; - /** - * The key of the IP:Port of a proxy-enabled Hentai@Home Client - * to load all images - */ - private static final String KEY_HAH_CLIENT_IP_PORT = "hp"; - /** - * The key of the passkey of a proxy-enabled Hentai@Home Client - * to load all images - */ - private static final String KEY_HAH_CLIENT_PASSKEY = "hk"; - /** - * The key of enable Tag Flagging - * @see #ENABLE_TAG_FLAGGING_NO - * @see #ENABLE_TAG_FLAGGING_YES - */ - private static final String KEY_ENABLE_TAG_FLAGGING = "tf"; - /** - * The key of always display the original images instead of the resampled versions - * @see #ALWAYS_ORIGINAL_NO - * @see #ALWAYS_ORIGINAL_YES - */ - private static final String KEY_ALWAYS_ORIGINAL = "oi"; - /** - * The key of enable the multi-Page Viewer - * @see #MULTI_PAGE_NO - * @see #MULTI_PAGE_YES - */ - private static final String KEY_MULTI_PAGE = "qb"; - /** - * The key of multi-Page Viewer Display Style - * @see #MULTI_PAGE_STYLE_C - * @see #MULTI_PAGE_STYLE_N - * @see #MULTI_PAGE_STYLE_Y - */ - private static final String KEY_MULTI_PAGE_STYLE = "ms"; - /** - * The key of multi-Page Viewer Thumbnail Pane - * @see #MULTI_PAGE_THUMB_HIDE - * @see #MULTI_PAGE_THUMB_SHOW - */ - private static final String KEY_MULTI_PAGE_THUMB = "mt"; - /** * The Cookie key of lofi resolution */ public static final String KEY_LOFI_RESOLUTION = "xres"; - /** * The Cookie key of show warning */ public static final String KEY_CONTENT_WARNING = "nw"; - /** * load images through the Hentai@Home Network */ @@ -209,7 +43,6 @@ public class EhConfig implements Cloneable { * do not load images through the Hentai@Home Network */ public static final String LOAD_FROM_HAH_NO = "n"; - /** * Image Size Auto */ @@ -234,16 +67,6 @@ public class EhConfig implements Cloneable { * Image Size 2400x */ public static final String IMAGE_SIZE_2400X = "2400"; - - /** - * Default gallery title - */ - private static final String GALLERY_TITLE_DEFAULT = "r"; - /** - * Japanese gallery title - */ - private static final String GALLERY_TITLE_JAPANESE = "j"; - /** * Manual Accept, Manual Start */ @@ -260,7 +83,6 @@ public class EhConfig implements Cloneable { * Auto Accept, Auto Start */ public static final String ARCHIVER_DOWNLOAD_AAAS = "3"; - /** * List View on the front and search pages */ @@ -269,16 +91,6 @@ public class EhConfig implements Cloneable { * Thumbnail View on the front and search pages */ public static final String LAYOUT_MODE_THUMB = "t"; - - /** - * Show popular - */ - private static final String POPULAR_YES = "y"; - /** - * Don't show popular - */ - private static final String POPULAR_NO = "n"; - public static final int MISC = 0x1; public static final int DOUJINSHI = 0x2; public static final int MANGA = 0x4; @@ -290,7 +102,6 @@ public class EhConfig implements Cloneable { public static final int NON_H = 0x100; public static final int WESTERN = 0x200; public static final int ALL_CATEGORY = 0x3ff; - public static final int NAMESPACES_RECLASS = 0x1; public static final int NAMESPACES_LANGUAGE = 0x2; public static final int NAMESPACES_PARODY = 0x4; @@ -299,7 +110,6 @@ public class EhConfig implements Cloneable { public static final int NAMESPACES_ARTIST = 0x20; public static final int NAMESPACES_MALE = 0x40; public static final int NAMESPACES_FEMALE = 0x80; - public static final String JAPANESE_ORIGINAL = "0"; public static final String JAPANESE_TRANSLATED = "1024"; public static final String JAPANESE_REWRITE = "2048"; @@ -351,16 +161,6 @@ public class EhConfig implements Cloneable { public static final String OTHER_ORIGINAL = "255"; public static final String OTHER_TRANSLATED = "1279"; public static final String OTHER_REWRITE = "2303"; - - /** - * Sort favorites by last gallery update time - */ - private static final String FAVORITES_SORT_GALLERY_UPDATE_TIME = "p"; - /** - * Sort favorites by favorited time - */ - private static final String FAVORITES_SORT_FAVORITED_TIME = "f"; - /** * 25 results per page for the index/search page and torrent search pages */ @@ -377,7 +177,6 @@ public class EhConfig implements Cloneable { * 200 results per page for the index/search page and torrent search pages */ public static final String RESULT_COUNT_200 = "3"; - /** * On mouse-over */ @@ -386,7 +185,6 @@ public class EhConfig implements Cloneable { * On page load */ public static final String MOUSE_OVER_NO = "p"; - /** * Preview normal size */ @@ -395,7 +193,6 @@ public class EhConfig implements Cloneable { * Preview large size */ public static final String PREVIEW_SIZE_LARGE = "l"; - /** * 4 row preview per page */ @@ -412,7 +209,6 @@ public class EhConfig implements Cloneable { * 40 row preview per page */ public static final String PREVIEW_ROW_40 = "20"; - /** * Oldest comments first */ @@ -425,7 +221,6 @@ public class EhConfig implements Cloneable { * By highest score */ public static final String COMMENTS_SORT_HIGHEST_SCORE_FIRST = "s"; - /** * Show gallery comment votes On score hover or click */ @@ -434,7 +229,6 @@ public class EhConfig implements Cloneable { * Always show gallery comment votes */ public static final String COMMENTS_VOTES_ALWAYS = "1"; - /** * Sort order for gallery tags alphabetically */ @@ -443,7 +237,6 @@ public class EhConfig implements Cloneable { * Sort order for gallery tags by tag power */ public static final String TAGS_SORT_POWER = "p"; - /** * Show gallery page numbers */ @@ -452,7 +245,6 @@ public class EhConfig implements Cloneable { * Do not show gallery page numbers */ public static final String SHOW_GALLERY_INDEX_NO = "0"; - /** * Enable Tag Flagging */ @@ -461,7 +253,6 @@ public class EhConfig implements Cloneable { * Do not enable Tag Flagging */ public static final String ENABLE_TAG_FLAGGING_NO = "n"; - /** * Always display the original images */ @@ -470,7 +261,6 @@ public class EhConfig implements Cloneable { * Do not Always display the original images */ public static final String ALWAYS_ORIGINAL_NO = "n"; - /** * Enable the Multi-Page Viewe */ @@ -479,7 +269,6 @@ public class EhConfig implements Cloneable { * Do not enable the Multi-Page Viewe */ public static final String MULTI_PAGE_NO = "n"; - /** * Align left, only scale if image is larger than browser width */ @@ -492,7 +281,6 @@ public class EhConfig implements Cloneable { * Align center, Always scale images to fit browser width */ public static final String MULTI_PAGE_STYLE_Y = "y"; - /** * Show Multi-Page Viewer Thumbnail Pane */ @@ -501,32 +289,232 @@ public class EhConfig implements Cloneable { * Hide Multi-Page Viewer Thumbnail Pane */ public static final String MULTI_PAGE_THUMB_HIDE = "y"; - /** * 460x for lofi resolution */ public static final String LOFI_RESOLUTION_460X = "1"; - /** * 780X for lofi resolution */ public static final String LOFI_RESOLUTION_780X = "2"; - /** * 980X for lofi resolution */ public static final String LOFI_RESOLUTION_980X = "3"; - /** * show warning */ public static final String CONTENT_WARNING_SHOW = "0"; - /** * not show warning */ public static final String CONTENT_WARNING_NOT_SHOW = "1"; - + /** + * The key of load images through the Hentai@Home Network + * + * @see #LOAD_FROM_HAH_YES + * @see #LOAD_FROM_HAH_NO + */ + private static final String KEY_LOAD_FROM_HAH = "uh"; + /** + * The key of Image Size Settings + * + * @see #IMAGE_SIZE_AUTO + * @see #IMAGE_SIZE_780X + * @see #IMAGE_SIZE_980X + * @see #IMAGE_SIZE_1280X + * @see #IMAGE_SIZE_1600X + * @see #IMAGE_SIZE_2400X + */ + private static final String KEY_IMAGE_SIZE = "xr"; + /** + * The key of scale images width + */ + private static final String KEY_SCALE_WIDTH = "rx"; + /** + * The key of scale images height + */ + private static final String KEY_SCALE_HEIGHT = "ry"; + /** + * The key of Gallery Name Display + * + * @see #GALLERY_TITLE_DEFAULT + * @see #GALLERY_TITLE_JAPANESE + */ + private static final String KEY_GALLERY_TITLE = "tl"; + /** + * The key of the behavior for downloading archiver + * + * @see #ARCHIVER_DOWNLOAD_MAMS + * @see #ARCHIVER_DOWNLOAD_MAAS + * @see #ARCHIVER_DOWNLOAD_AAMS + * @see #ARCHIVER_DOWNLOAD_AAAS + */ + private static final String KEY_ARCHIVER_DOWNLOAD = "ar"; + /** + * The key of display mode would you like to use on the front and search pages + * + * @see #LAYOUT_MODE_LIST + * @see #LAYOUT_MODE_THUMB + */ + private static final String KEY_LAYOUT_MODE = "dm"; + /** + * The key for show popular + * + * @see #POPULAR_YES + * @see #POPULAR_NO + */ + private static final String KEY_POPULAR = "prn"; + /** + * The key of categories would you like to view as default on the front page + */ + private static final String KEY_DEFAULT_CATEGORIES = "cats"; + /** + * The key for favorites sort + * + * @see #FAVORITES_SORT_GALLERY_UPDATE_TIME + * @see #FAVORITES_SORT_FAVORITED_TIME + */ + private static final String KEY_FAVORITES_SORT = "fs"; + /** + * The key of exclude certain namespaces from a default tag search + */ + private static final String KEY_EXCLUDED_NAMESPACES = "xns"; + /** + * The key of hide galleries in certain languages from the gallery list and searches + */ + private static final String KEY_EXCLUDED_LANGUAGES = "xl"; + /** + * The key of how many results would you like per page for the index/search page and torrent search pages + * + * @see #RESULT_COUNT_25 + * @see #RESULT_COUNT_50 + * @see #RESULT_COUNT_100 + * @see #RESULT_COUNT_200 + */ + private static final String KEY_RESULT_COUNT = "rc"; + /** + * The key of mouse-over thumb + * + * @see #MOUSE_OVER_YES + * @see #MOUSE_OVER_NO + */ + private static final String KEY_MOUSE_OVER = "lt"; + /** + * The key of preview size + * + * @see #PREVIEW_SIZE_NORMAL + * @see #PREVIEW_SIZE_LARGE + */ + private static final String KEY_PREVIEW_SIZE = "ts"; + /** + * The key of preview row per page + * + * @see #PREVIEW_ROW_4 + * @see #PREVIEW_ROW_10 + * @see #PREVIEW_ROW_20 + * @see #PREVIEW_ROW_40 + */ + private static final String KEY_PREVIEW_ROW = "tr"; + /** + * The key of sort order for gallery comments + * + * @see #COMMENTS_SORT_OLDEST_FIRST + * @see #COMMENTS_SORT_RECENT_FIRST + * @see #COMMENTS_SORT_HIGHEST_SCORE_FIRST + */ + private static final String KEY_COMMENTS_SORT = "cs"; + /** + * The key of show gallery comment votes + * + * @see #COMMENTS_VOTES_POP + * @see #COMMENTS_VOTES_ALWAYS + */ + private static final String KEY_COMMENTS_VOTES = "sc"; + /** + * The key of sort order for gallery tags + * + * @see #TAGS_SORT_ALPHABETICAL + * @see #TAGS_SORT_POWER + */ + private static final String KEY_TAGS_SORT = "to"; + /** + * The key of show gallery page numbers + * + * @see #SHOW_GALLERY_INDEX_NO + * @see #SHOW_GALLERY_INDEX_YES + */ + private static final String KEY_SHOW_GALLERY_INDEX = "pn"; + /** + * The key of the IP:Port of a proxy-enabled Hentai@Home Client + * to load all images + */ + private static final String KEY_HAH_CLIENT_IP_PORT = "hp"; + /** + * The key of the passkey of a proxy-enabled Hentai@Home Client + * to load all images + */ + private static final String KEY_HAH_CLIENT_PASSKEY = "hk"; + /** + * The key of enable Tag Flagging + * + * @see #ENABLE_TAG_FLAGGING_NO + * @see #ENABLE_TAG_FLAGGING_YES + */ + private static final String KEY_ENABLE_TAG_FLAGGING = "tf"; + /** + * The key of always display the original images instead of the resampled versions + * + * @see #ALWAYS_ORIGINAL_NO + * @see #ALWAYS_ORIGINAL_YES + */ + private static final String KEY_ALWAYS_ORIGINAL = "oi"; + /** + * The key of enable the multi-Page Viewer + * + * @see #MULTI_PAGE_NO + * @see #MULTI_PAGE_YES + */ + private static final String KEY_MULTI_PAGE = "qb"; + /** + * The key of multi-Page Viewer Display Style + * + * @see #MULTI_PAGE_STYLE_C + * @see #MULTI_PAGE_STYLE_N + * @see #MULTI_PAGE_STYLE_Y + */ + private static final String KEY_MULTI_PAGE_STYLE = "ms"; + /** + * The key of multi-Page Viewer Thumbnail Pane + * + * @see #MULTI_PAGE_THUMB_HIDE + * @see #MULTI_PAGE_THUMB_SHOW + */ + private static final String KEY_MULTI_PAGE_THUMB = "mt"; + /** + * Default gallery title + */ + private static final String GALLERY_TITLE_DEFAULT = "r"; + /** + * Japanese gallery title + */ + private static final String GALLERY_TITLE_JAPANESE = "j"; + /** + * Show popular + */ + private static final String POPULAR_YES = "y"; + /** + * Don't show popular + */ + private static final String POPULAR_NO = "n"; + /** + * Sort favorites by last gallery update time + */ + private static final String FAVORITES_SORT_GALLERY_UPDATE_TIME = "p"; + /** + * Sort favorites by favorited time + */ + private static final String FAVORITES_SORT_FAVORITED_TIME = "f"; /** * Load images through the Hentai@Home Network
* key: {@link #KEY_LOAD_FROM_HAH}
@@ -538,7 +526,7 @@ public class EhConfig implements Cloneable { * Image Size
* key: {@link #KEY_IMAGE_SIZE}
* value: {@link #IMAGE_SIZE_AUTO}, {@link #IMAGE_SIZE_780X}, {@link #IMAGE_SIZE_980X}, - * {@link #IMAGE_SIZE_1280X}, {@link #IMAGE_SIZE_1600X}, {@link #IMAGE_SIZE_2400X} + * {@link #IMAGE_SIZE_1280X}, {@link #IMAGE_SIZE_1600X}, {@link #IMAGE_SIZE_2400X} */ public String imageSize = IMAGE_SIZE_AUTO; diff --git a/app/src/main/java/com/hippo/ehviewer/client/EhCookieStore.java b/app/src/main/java/com/hippo/ehviewer/client/EhCookieStore.java index 59f1daf14..7d3bafcd0 100644 --- a/app/src/main/java/com/hippo/ehviewer/client/EhCookieStore.java +++ b/app/src/main/java/com/hippo/ehviewer/client/EhCookieStore.java @@ -46,18 +46,8 @@ public class EhCookieStore extends CookieRepository { super(context, "okhttp3-cookie.db"); } - public void signOut() { - clear(); - } - - public boolean hasSignedIn() { - HttpUrl url = HttpUrl.parse(EhUrl.HOST_E); - return contains(url, KEY_IPD_MEMBER_ID) && - contains(url, KEY_IPD_PASS_HASH); - } - public static Cookie newCookie(Cookie cookie, String newDomain, boolean forcePersistent, - boolean forceLongLive, boolean forceNotHostOnly) { + boolean forceLongLive, boolean forceNotHostOnly) { Cookie.Builder builder = new Cookie.Builder(); builder.name(cookie.name()); builder.value(cookie.value()); @@ -84,6 +74,16 @@ public class EhCookieStore extends CookieRepository { return builder.build(); } + public void signOut() { + clear(); + } + + public boolean hasSignedIn() { + HttpUrl url = HttpUrl.parse(EhUrl.HOST_E); + return contains(url, KEY_IPD_MEMBER_ID) && + contains(url, KEY_IPD_PASS_HASH); + } + @Override public List loadForRequest(HttpUrl url) { List cookies = super.loadForRequest(url); @@ -93,7 +93,7 @@ public class EhCookieStore extends CookieRepository { if (checkTips) { List result = new ArrayList<>(cookies.size() + 1); // Add all but skip some - for (Cookie cookie: cookies) { + for (Cookie cookie : cookies) { String name = cookie.name(); if (EhConfig.KEY_CONTENT_WARNING.equals(name)) { continue; diff --git a/app/src/main/java/com/hippo/ehviewer/client/EhDns.java b/app/src/main/java/com/hippo/ehviewer/client/EhDns.java index f570fe00d..5e9eaca36 100644 --- a/app/src/main/java/com/hippo/ehviewer/client/EhDns.java +++ b/app/src/main/java/com/hippo/ehviewer/client/EhDns.java @@ -21,9 +21,11 @@ package com.hippo.ehviewer.client; */ import android.content.Context; + import com.hippo.ehviewer.EhApplication; import com.hippo.ehviewer.Hosts; import com.hippo.ehviewer.Settings; + import java.net.InetAddress; import java.net.UnknownHostException; import java.util.Arrays; @@ -31,60 +33,61 @@ import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; + import okhttp3.Dns; public class EhDns implements Dns { - private static final Map builtInHosts; + private static final Map builtInHosts; - static { - Map map = new HashMap<>(); - put(map, "e-hentai.org", "104.20.26.25"); - put(map, "repo.e-hentai.org", "94.100.29.73"); - put(map, "forums.e-hentai.org", "94.100.18.243"); - put(map, "ehgt.org", "81.171.14.118"); - put(map, "ul.ehgt.org", "94.100.24.82"); - put(map, "github.com", "192.30.255.112"); - put(map, "raw.githubusercontent.com", "151.101.0.133"); - builtInHosts = map; - } - - private static void put(Map map, String host, String ip) { - InetAddress address = Hosts.toInetAddress(host, ip); - if (address != null) { - map.put(host, address); - } - } - - private final Hosts hosts; - - public EhDns(Context context) { - hosts = EhApplication.getHosts(context); - } - - @Override - public List lookup(String hostname) throws UnknownHostException { - if (hostname == null) throw new UnknownHostException("hostname == null"); - - InetAddress inetAddress = hosts.get(hostname); - if (inetAddress != null) { - return Collections.singletonList(inetAddress); + static { + Map map = new HashMap<>(); + put(map, "e-hentai.org", "104.20.26.25"); + put(map, "repo.e-hentai.org", "94.100.29.73"); + put(map, "forums.e-hentai.org", "94.100.18.243"); + put(map, "ehgt.org", "81.171.14.118"); + put(map, "ul.ehgt.org", "94.100.24.82"); + put(map, "github.com", "192.30.255.112"); + put(map, "raw.githubusercontent.com", "151.101.0.133"); + builtInHosts = map; } - if (Settings.getBuiltInHosts()) { - inetAddress = builtInHosts.get(hostname); - if (inetAddress != null) { - return Collections.singletonList(inetAddress); - } + private final Hosts hosts; + + public EhDns(Context context) { + hosts = EhApplication.getHosts(context); } - try { - return Arrays.asList(InetAddress.getAllByName(hostname)); - } catch (NullPointerException e) { - UnknownHostException unknownHostException = - new UnknownHostException("Broken system behaviour for dns lookup of " + hostname); - unknownHostException.initCause(e); - throw unknownHostException; + private static void put(Map map, String host, String ip) { + InetAddress address = Hosts.toInetAddress(host, ip); + if (address != null) { + map.put(host, address); + } + } + + @Override + public List lookup(String hostname) throws UnknownHostException { + if (hostname == null) throw new UnknownHostException("hostname == null"); + + InetAddress inetAddress = hosts.get(hostname); + if (inetAddress != null) { + return Collections.singletonList(inetAddress); + } + + if (Settings.getBuiltInHosts()) { + inetAddress = builtInHosts.get(hostname); + if (inetAddress != null) { + return Collections.singletonList(inetAddress); + } + } + + try { + return Arrays.asList(InetAddress.getAllByName(hostname)); + } catch (NullPointerException e) { + UnknownHostException unknownHostException = + new UnknownHostException("Broken system behaviour for dns lookup of " + hostname); + unknownHostException.initCause(e); + throw unknownHostException; + } } - } } diff --git a/app/src/main/java/com/hippo/ehviewer/client/EhEngine.java b/app/src/main/java/com/hippo/ehviewer/client/EhEngine.java index 7a48cf6d5..0a2eb8fe5 100644 --- a/app/src/main/java/com/hippo/ehviewer/client/EhEngine.java +++ b/app/src/main/java/com/hippo/ehviewer/client/EhEngine.java @@ -19,7 +19,9 @@ package com.hippo.ehviewer.client; import android.text.TextUtils; import android.util.Log; import android.util.Pair; + import androidx.annotation.Nullable; + import com.hippo.ehviewer.AppConfig; import com.hippo.ehviewer.GetText; import com.hippo.ehviewer.R; @@ -49,11 +51,19 @@ import com.hippo.ehviewer.client.parser.VoteCommentParser; import com.hippo.network.StatusCodeException; import com.hippo.util.ExceptionUtils; import com.hippo.yorozuya.AssertUtils; + +import org.json.JSONArray; +import org.json.JSONObject; +import org.jsoup.Jsoup; +import org.jsoup.nodes.Document; +import org.jsoup.select.Elements; + import java.io.File; import java.util.ArrayList; import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; + import okhttp3.Call; import okhttp3.FormBody; import okhttp3.Headers; @@ -63,23 +73,15 @@ import okhttp3.OkHttpClient; import okhttp3.Request; import okhttp3.RequestBody; import okhttp3.Response; -import org.json.JSONArray; -import org.json.JSONObject; -import org.jsoup.Jsoup; -import org.jsoup.nodes.Document; -import org.jsoup.select.Elements; public class EhEngine { + public static final MediaType MEDIA_TYPE_JSON = MediaType.parse("application/json; charset=utf-8"); private static final String TAG = EhEngine.class.getSimpleName(); - private static final String SAD_PANDA_DISPOSITION = "inline; filename=\"sadpanda.jpg\""; private static final String SAD_PANDA_TYPE = "image/gif"; private static final String SAD_PANDA_LENGTH = "9615"; - private static final String KOKOMADE_URL = "https://exhentai.org/img/kokomade.jpg"; - - public static final MediaType MEDIA_TYPE_JSON = MediaType.parse("application/json; charset=utf-8"); private static final MediaType MEDIA_TYPE_JPEG = MediaType.parse("image/jpeg"); private static final Pattern PATTERN_NEED_HATH_CLIENT = Pattern.compile("(You must have a H@H client assigned to your account to use this feature\\.)"); @@ -91,7 +93,7 @@ public class EhEngine { } private static void doThrowException(Call call, int code, @Nullable Headers headers, - @Nullable String body, Throwable e) throws Throwable { + @Nullable String body, Throwable e) throws Throwable { if (call.isCanceled()) { throw new CancelledException(); } @@ -131,7 +133,7 @@ public class EhEngine { } private static void throwException(Call call, int code, @Nullable Headers headers, - @Nullable String body, Throwable e) throws Throwable { + @Nullable String body, Throwable e) throws Throwable { try { doThrowException(call, code, headers, body, e); } catch (Throwable error) { @@ -141,7 +143,7 @@ public class EhEngine { } public static String signIn(@Nullable EhClient.Task task, OkHttpClient okHttpClient, - String username, String password) throws Throwable { + String username, String password) throws Throwable { FormBody.Builder builder = new FormBody.Builder() .add("UserName", username) .add("PassWord", password) @@ -232,7 +234,7 @@ public class EhEngine { } public static GalleryListParser.Result getGalleryList(@Nullable EhClient.Task task, OkHttpClient okHttpClient, - String url) throws Throwable { + String url) throws Throwable { String referer = EhUrl.getReferer(); Log.d(TAG, url); Request request = new EhRequestBuilder(url, referer).build(); @@ -266,7 +268,7 @@ public class EhEngine { // At least, GalleryInfo contain valid gid and token public static List fillGalleryListByApi(@Nullable EhClient.Task task, OkHttpClient okHttpClient, - List galleryInfoList, String referer) throws Throwable { + List galleryInfoList, String referer) throws Throwable { // We can only request 25 items one time at most final int MAX_REQUEST_SIZE = 25; List requestItems = new ArrayList<>(MAX_REQUEST_SIZE); @@ -281,7 +283,7 @@ public class EhEngine { } private static void doFillGalleryListByApi(@Nullable EhClient.Task task, OkHttpClient okHttpClient, - List galleryInfoList, String referer) throws Throwable { + List galleryInfoList, String referer) throws Throwable { JSONObject json = new JSONObject(); json.put("method", "gdata"); JSONArray ja = new JSONArray(); @@ -324,7 +326,7 @@ public class EhEngine { } public static GalleryDetail getGalleryDetail(@Nullable EhClient.Task task, OkHttpClient okHttpClient, - String url) throws Throwable { + String url) throws Throwable { String referer = EhUrl.getReferer(); Log.d(TAG, url); Request request = new EhRequestBuilder(url, referer).build(); @@ -382,8 +384,8 @@ public class EhEngine { } public static RateGalleryParser.Result rateGallery(@Nullable EhClient.Task task, - OkHttpClient okHttpClient, long apiUid, String apiKey, long gid, - String token, float rating) throws Throwable { + OkHttpClient okHttpClient, long apiUid, String apiKey, long gid, + String token, float rating) throws Throwable { final JSONObject json = new JSONObject(); json.put("method", "rategallery"); json.put("apiuid", apiUid); @@ -423,7 +425,7 @@ public class EhEngine { } public static GalleryCommentList commentGallery(@Nullable EhClient.Task task, - OkHttpClient okHttpClient, String url, String comment, String id) throws Throwable { + OkHttpClient okHttpClient, String url, String comment, String id) throws Throwable { FormBody.Builder builder = new FormBody.Builder(); if (id == null) { builder.add("commenttext_new", comment); @@ -467,7 +469,7 @@ public class EhEngine { } public static String getGalleryToken(@Nullable EhClient.Task task, OkHttpClient okHttpClient, - long gid, String gtoken, int page) throws Throwable { + long gid, String gtoken, int page) throws Throwable { JSONObject json = new JSONObject() .put("method", "gtoken") .put("pagelist", new JSONArray().put( @@ -504,7 +506,7 @@ public class EhEngine { } public static FavoritesParser.Result getFavorites(@Nullable EhClient.Task task, OkHttpClient okHttpClient, - String url, boolean callApi) throws Throwable { + String url, boolean callApi) throws Throwable { String referer = EhUrl.getReferer(); Log.d(TAG, url); Request request = new EhRequestBuilder(url, referer).build(); @@ -538,10 +540,10 @@ public class EhEngine { /** * @param dstCat -1 for delete, 0 - 9 for cloud favorite, others throw Exception - * @param note max 250 characters + * @param note max 250 characters */ public static Void addFavorites(@Nullable EhClient.Task task, OkHttpClient okHttpClient, - long gid, String token, int dstCat, String note) throws Throwable { + long gid, String token, int dstCat, String note) throws Throwable { String catStr; if (dstCat == -1) { catStr = "favdel"; @@ -588,7 +590,7 @@ public class EhEngine { } public static Void addFavoritesRange(@Nullable EhClient.Task task, OkHttpClient okHttpClient, - long[] gidArray, String[] tokenArray, int dstCat) throws Throwable { + long[] gidArray, String[] tokenArray, int dstCat) throws Throwable { AssertUtils.assertEquals(gidArray.length, tokenArray.length); for (int i = 0, n = gidArray.length; i < n; i++) { addFavorites(task, okHttpClient, gidArray[i], tokenArray[i], dstCat, null); @@ -597,7 +599,7 @@ public class EhEngine { } public static FavoritesParser.Result modifyFavorites(@Nullable EhClient.Task task, OkHttpClient okHttpClient, - String url, long[] gidArray, int dstCat, boolean callApi) throws Throwable { + String url, long[] gidArray, int dstCat, boolean callApi) throws Throwable { String catStr; if (dstCat == -1) { catStr = "delete"; @@ -646,7 +648,7 @@ public class EhEngine { } public static Pair[] getTorrentList(@Nullable EhClient.Task task, OkHttpClient okHttpClient, - String url, long gid, String token) throws Throwable { + String url, long gid, String token) throws Throwable { String referer = EhUrl.getGalleryDetailUrl(gid, token); Log.d(TAG, url); Request request = new EhRequestBuilder(url, referer).build(); @@ -677,7 +679,7 @@ public class EhEngine { } public static Pair[]> getArchiveList(@Nullable EhClient.Task task, OkHttpClient okHttpClient, - String url, long gid, String token) throws Throwable { + String url, long gid, String token) throws Throwable { String referer = EhUrl.getGalleryDetailUrl(gid, token); Log.d(TAG, url); Request request = new EhRequestBuilder(url, referer).build(); @@ -708,7 +710,7 @@ public class EhEngine { } public static Void downloadArchive(@Nullable EhClient.Task task, OkHttpClient okHttpClient, - long gid, String token, String or, String res) throws Throwable { + long gid, String token, String or, String res) throws Throwable { if (or == null || or.length() == 0) { throw new EhException("Invalid form param or: " + or); } @@ -755,7 +757,7 @@ public class EhEngine { } private static ProfileParser.Result getProfileInternal(@Nullable EhClient.Task task, - OkHttpClient okHttpClient, String url, String referer) throws Throwable { + OkHttpClient okHttpClient, String url, String referer) throws Throwable { Log.d(TAG, url); Request request = new EhRequestBuilder(url, referer).build(); Call call = okHttpClient.newCall(request); @@ -782,7 +784,7 @@ public class EhEngine { } public static ProfileParser.Result getProfile(@Nullable EhClient.Task task, - OkHttpClient okHttpClient) throws Throwable { + OkHttpClient okHttpClient) throws Throwable { String url = EhUrl.URL_FORUMS; Log.d(TAG, url); Request request = new EhRequestBuilder(url, null).build(); @@ -810,7 +812,7 @@ public class EhEngine { } public static VoteCommentParser.Result voteComment(@Nullable EhClient.Task task, OkHttpClient okHttpClient, - long apiUid, String apiKey, long gid, String token, long commentId, int commentVote) throws Throwable { + long apiUid, String apiKey, long gid, String token, long commentId, int commentVote) throws Throwable { final JSONObject json = new JSONObject(); json.put("method", "votecomment"); json.put("apiuid", apiUid); @@ -854,7 +856,7 @@ public class EhEngine { * @param image Must be jpeg */ public static GalleryListParser.Result imageSearch(@Nullable EhClient.Task task, OkHttpClient okHttpClient, - File image, boolean uss, boolean osc, boolean se) throws Throwable { + File image, boolean uss, boolean osc, boolean se) throws Throwable { MultipartBody.Builder builder = new MultipartBody.Builder(); builder.setType(MultipartBody.FORM); builder.addPart( @@ -922,7 +924,7 @@ public class EhEngine { } public static GalleryPageParser.Result getGalleryPage(@Nullable EhClient.Task task, - OkHttpClient okHttpClient, String url, long gid, String token) throws Throwable { + OkHttpClient okHttpClient, String url, long gid, String token) throws Throwable { String referer = EhUrl.getGalleryDetailUrl(gid, token); Log.d(TAG, url); Request request = new EhRequestBuilder(url, referer).build(); @@ -950,7 +952,7 @@ public class EhEngine { } public static GalleryPageApiParser.Result getGalleryPageApi(@Nullable EhClient.Task task, - OkHttpClient okHttpClient, long gid, int index, String pToken, String showKey, String previousPToken) throws Throwable { + OkHttpClient okHttpClient, long gid, int index, String pToken, String showKey, String previousPToken) throws Throwable { final JSONObject json = new JSONObject(); json.put("method", "showpage"); json.put("gid", gid); @@ -966,8 +968,8 @@ public class EhEngine { String origin = EhUrl.getOrigin(); Log.d(TAG, url); Request request = new EhRequestBuilder(url, referer, origin) - .post(requestBody) - .build(); + .post(requestBody) + .build(); Call call = okHttpClient.newCall(request); // Put call diff --git a/app/src/main/java/com/hippo/ehviewer/client/EhFilter.java b/app/src/main/java/com/hippo/ehviewer/client/EhFilter.java index f73531a40..811e8e34f 100644 --- a/app/src/main/java/com/hippo/ehviewer/client/EhFilter.java +++ b/app/src/main/java/com/hippo/ehviewer/client/EhFilter.java @@ -27,27 +27,17 @@ import java.util.List; public final class EhFilter { - private static final String TAG = EhFilter.class.getSimpleName(); - public static final int MODE_TITLE = 0; public static final int MODE_UPLOADER = 1; public static final int MODE_TAG = 2; public static final int MODE_TAG_NAMESPACE = 3; - + private static final String TAG = EhFilter.class.getSimpleName(); + private static EhFilter sInstance; private final List mTitleFilterList = new ArrayList<>(); private final List mUploaderFilterList = new ArrayList<>(); private final List mTagFilterList = new ArrayList<>(); private final List mTagNamespaceFilterList = new ArrayList<>(); - private static EhFilter sInstance; - - public static EhFilter getInstance() { - if (sInstance == null) { - sInstance = new EhFilter(); - } - return sInstance; - } - private EhFilter() { List list = EhDB.getAllFilter(); for (int i = 0, n = list.size(); i < n; i++) { @@ -75,6 +65,13 @@ public final class EhFilter { } } + public static EhFilter getInstance() { + if (sInstance == null) { + sInstance = new EhFilter(); + } + return sInstance; + } + public List getTitleFilterList() { return mTitleFilterList; } @@ -216,11 +213,7 @@ public final class EhFilter { !tagNamespace.equals(filterNamespace)) { return false; } - if (!tagName.equals(filterName)) { - return false; - } - - return true; + return tagName.equals(filterName); } public synchronized boolean filterTag(GalleryInfo info) { @@ -232,7 +225,7 @@ public final class EhFilter { String[] tags = info.simpleTags; List filters = mTagFilterList; if (null != tags && filters.size() > 0) { - for (String tag: tags) { + for (String tag : tags) { for (int i = 0, n = filters.size(); i < n; i++) { if (filters.get(i).enable && matchTag(tag, filters.get(i).text)) { return false; @@ -267,7 +260,7 @@ public final class EhFilter { String[] tags = info.simpleTags; List filters = mTagNamespaceFilterList; if (null != tags && filters.size() > 0) { - for (String tag: tags) { + for (String tag : tags) { for (int i = 0, n = filters.size(); i < n; i++) { if (filters.get(i).enable && matchTagNamespace(tag, filters.get(i).text)) { return false; diff --git a/app/src/main/java/com/hippo/ehviewer/client/EhRequest.java b/app/src/main/java/com/hippo/ehviewer/client/EhRequest.java index b8d7c62e9..5cd308d80 100644 --- a/app/src/main/java/com/hippo/ehviewer/client/EhRequest.java +++ b/app/src/main/java/com/hippo/ehviewer/client/EhRequest.java @@ -20,51 +20,49 @@ import com.hippo.ehviewer.Settings; public class EhRequest { + EhClient.Task task; private int mMethod; private Object[] mArgs; private EhClient.Callback mCallback; private EhConfig mEhConfig; - - EhClient.Task task; - private boolean mCancel = false; + public int getMethod() { + return mMethod; + } + public EhRequest setMethod(int method) { mMethod = method; return this; } + public Object[] getArgs() { + return mArgs; + } + public EhRequest setArgs(Object... args) { mArgs = args; return this; } + public EhClient.Callback getCallback() { + return mCallback; + } + public EhRequest setCallback(EhClient.Callback callback) { mCallback = callback; return this; } + public EhConfig getEhConfig() { + return mEhConfig != null ? mEhConfig : Settings.getEhConfig(); + } + public EhRequest setEhConfig(EhConfig ehConfig) { mEhConfig = ehConfig; return this; } - public int getMethod() { - return mMethod; - } - - public Object[] getArgs() { - return mArgs; - } - - public EhClient.Callback getCallback() { - return mCallback; - } - - public EhConfig getEhConfig() { - return mEhConfig != null ? mEhConfig : Settings.getEhConfig(); - } - public void cancel() { if (!mCancel) { mCancel = true; diff --git a/app/src/main/java/com/hippo/ehviewer/client/EhTagDatabase.java b/app/src/main/java/com/hippo/ehviewer/client/EhTagDatabase.java index f09dfca74..b387381b0 100644 --- a/app/src/main/java/com/hippo/ehviewer/client/EhTagDatabase.java +++ b/app/src/main/java/com/hippo/ehviewer/client/EhTagDatabase.java @@ -18,7 +18,9 @@ package com.hippo.ehviewer.client; import android.content.Context; import android.util.Base64; + import androidx.annotation.Nullable; + import com.hippo.ehviewer.AppConfig; import com.hippo.ehviewer.EhApplication; import com.hippo.ehviewer.R; @@ -27,6 +29,7 @@ import com.hippo.util.IoThreadPoolExecutor; import com.hippo.util.TextUrl; import com.hippo.yorozuya.FileUtils; import com.hippo.yorozuya.IOUtils; + import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; @@ -39,6 +42,7 @@ import java.util.HashMap; import java.util.Map; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; + import okhttp3.Call; import okhttp3.OkHttpClient; import okhttp3.Request; @@ -49,309 +53,307 @@ import okio.Okio; public class EhTagDatabase { - private final String name; - private final byte[] tags; + private static final Map NAMESPACE_TO_PREFIX = new HashMap<>(); + private static volatile EhTagDatabase instance; + // TODO more lock for different language + private static Lock lock = new ReentrantLock(); - public EhTagDatabase(String name, BufferedSource source) throws IOException { - this.name = name; - int totalBytes = source.readInt(); - tags = new byte[totalBytes]; - source.readFully(tags); - } + static { + NAMESPACE_TO_PREFIX.put("artist", "a:"); + NAMESPACE_TO_PREFIX.put("character", "c:"); + NAMESPACE_TO_PREFIX.put("female", "f:"); + NAMESPACE_TO_PREFIX.put("group", "g:"); + NAMESPACE_TO_PREFIX.put("language", "l:"); + NAMESPACE_TO_PREFIX.put("male", "m:"); + NAMESPACE_TO_PREFIX.put("misc", ""); + NAMESPACE_TO_PREFIX.put("parody", "p:"); + NAMESPACE_TO_PREFIX.put("reclass", "r:"); + } - public String getTranslation(String tag) { - return search(tags, tag.getBytes(TextUrl.UTF_8)); - } + private final String name; + private final byte[] tags; - @Nullable - private String search(byte[] tags, byte[] tag) { - int low = 0; - int high = tags.length; - while (low < high) { - int start = (low + high) / 2; - // Look for the starting '\n' - while (start > -1 && tags[start] != '\n') { - start--; - } - start++; + public EhTagDatabase(String name, BufferedSource source) throws IOException { + this.name = name; + int totalBytes = source.readInt(); + tags = new byte[totalBytes]; + source.readFully(tags); + } - // Look for the middle '\r'. - int middle = 1; - while (tags[start + middle] != '\r') { - middle++; - } - - // Look for the ending '\n' - int end = middle + 1; - while (tags[start + end] != '\n') { - end++; - } - - int compare; - int tagIndex = 0; - int curIndex = start; - - for (;;) { - int tagByte = tag[tagIndex] & 0xff; - int curByte = tags[curIndex] & 0xff; - compare = tagByte - curByte; - if (compare != 0) { - break; + @Nullable + public static EhTagDatabase getInstance(Context context) { + if (isPossible(context)) { + return instance; + } else { + instance = null; + return null; } + } - tagIndex++; - curIndex++; - if (tagIndex == tag.length && curIndex == start + middle) { - break; + @Nullable + public static String namespaceToPrefix(String namespace) { + return NAMESPACE_TO_PREFIX.get(namespace); + } + + private static String[] getMetadata(Context context) { + String[] metadata = context.getResources().getStringArray(R.array.tag_translation_metadata); + if (metadata.length == 4) { + return metadata; + } else { + return null; } - if (tagIndex == tag.length) { - compare = -1; - break; - } - if (curIndex == start + middle) { - compare = 1; - break; - } - } - - if (compare < 0) { - high = start - 1; - } else if (compare > 0) { - low = start + end + 1; - } else { - byte[] bytes = Base64.decode(tags, start + middle + 1, end - middle - 1, Base64.DEFAULT); - return new String(bytes, TextUrl.UTF_8); - } - } - return null; - } - - - private static final Map NAMESPACE_TO_PREFIX = new HashMap<>(); - - static { - NAMESPACE_TO_PREFIX.put("artist", "a:"); - NAMESPACE_TO_PREFIX.put("character", "c:"); - NAMESPACE_TO_PREFIX.put("female", "f:"); - NAMESPACE_TO_PREFIX.put("group", "g:"); - NAMESPACE_TO_PREFIX.put("language", "l:"); - NAMESPACE_TO_PREFIX.put("male", "m:"); - NAMESPACE_TO_PREFIX.put("misc", ""); - NAMESPACE_TO_PREFIX.put("parody", "p:"); - NAMESPACE_TO_PREFIX.put("reclass", "r:"); - } - - private static volatile EhTagDatabase instance; - // TODO more lock for different language - private static Lock lock = new ReentrantLock(); - - @Nullable - public static EhTagDatabase getInstance(Context context) { - if (isPossible(context)) { - return instance; - } else { - instance = null; - return null; - } - } - - @Nullable - public static String namespaceToPrefix(String namespace) { - return NAMESPACE_TO_PREFIX.get(namespace); - } - - private static String[] getMetadata(Context context) { - String[] metadata = context.getResources().getStringArray(R.array.tag_translation_metadata); - if (metadata.length == 4) { - return metadata; - } else { - return null; - } - } - - public static boolean isPossible(Context context) { - return getMetadata(context) != null; - } - - @Nullable - private static byte[] getFileContent(File file, int length) { - try (BufferedSource source = Okio.buffer(Okio.source(file))) { - byte[] content = new byte[length]; - source.readFully(content); - return content; - } catch (IOException e) { - return null; - } - } - - @Nullable - private static byte[] getFileSha1(File file) { - try (InputStream is = new FileInputStream(file)) { - MessageDigest digest = MessageDigest.getInstance("SHA-1"); - int n; - byte[] buffer = new byte[4 * 1024]; - while ((n = is.read(buffer)) != -1) { - digest.update(buffer, 0, n); - } - return digest.digest(); - } catch (IOException e) { - return null; - } catch (NoSuchAlgorithmException e) { - return null; - } - } - - private static boolean equals(byte[] b1, byte[] b2) { - if (b1 == null && b2 == null) { - return true; - } - if (b1 == null || b2 == null) { - return false; } - if (b1.length != b2.length) { - return false; + public static boolean isPossible(Context context) { + return getMetadata(context) != null; } - for (int i = 0; i < b1.length; i++) { - if (b1[i] != b2[i]) { - return false; - } - } - - return true; - } - - private static boolean checkData(File sha1File, File dataFile) { - byte[] s1 = getFileContent(sha1File, 20); - if (s1 == null) { - return false; - } - - byte[] s2 = getFileSha1(dataFile); - if (s2 == null) { - return false; - } - - return equals(s1, s2); - } - - private static boolean save(OkHttpClient client, String url, File file) { - Request request = new Request.Builder().url(url).build(); - Call call = client.newCall(request); - try (Response response = call.execute()) { - if (!response.isSuccessful()) { - return false; - } - ResponseBody body = response.body(); - if (body == null) { - return false; - } - - try (InputStream is = body.byteStream(); OutputStream os = new FileOutputStream(file)) { - IOUtils.copy(is, os); - } - - return true; - } catch (Throwable t) { - ExceptionUtils.throwIfFatal(t); - return false; - } - } - - public static void update(Context context) { - String[] urls = getMetadata(context); - if (urls == null || urls.length != 4) { - // Clear tags if it's not possible - instance = null; - return; - } - - String sha1Name = urls[0]; - String sha1Url = urls[1]; - String dataName = urls[2]; - String dataUrl = urls[3]; - - // Clear tags if name if different - EhTagDatabase tmp = instance; - if (tmp != null && !tmp.name.equals(dataName)) { - instance = null; - } - - IoThreadPoolExecutor.getInstance().execute(() -> { - if (!lock.tryLock()) { - return; - } - - try { - File dir = AppConfig.getFilesDir("tag-translations"); - if (dir == null) { - return; - } - - // Check current sha1 and current data - File sha1File = new File(dir, sha1Name); - File dataFile = new File(dir, dataName); - if (!checkData(sha1File, dataFile)) { - FileUtils.delete(sha1File); - FileUtils.delete(dataFile); - } - - // Read current EhTagDatabase - if (instance == null && dataFile.exists()) { - try (BufferedSource source = Okio.buffer(Okio.source(dataFile))) { - instance = new EhTagDatabase(dataName, source); - } catch (IOException e) { - FileUtils.delete(sha1File); - FileUtils.delete(dataFile); - } - } - - OkHttpClient client = EhApplication.getOkHttpClient(EhApplication.getInstance()); - - // Save new sha1 - File tempSha1File = new File(dir, sha1Name + ".tmp"); - if (!save(client, sha1Url, tempSha1File)) { - FileUtils.delete(tempSha1File); - return; - } - - // Check new sha1 and current data - if (checkData(tempSha1File, dataFile)) { - // The data is the same - FileUtils.delete(tempSha1File); - return; - } - - // Save new data - File tempDataFile = new File(dir, dataName + ".tmp"); - if (!save(client, dataUrl, tempDataFile)) { - FileUtils.delete(tempDataFile); - return; - } - - // Check new sha1 and new data - if (!checkData(tempSha1File, tempDataFile)) { - FileUtils.delete(tempSha1File); - FileUtils.delete(tempDataFile); - return; - } - - // Replace current sha1 and current data with new sha1 and new data - FileUtils.delete(sha1File); - FileUtils.delete(dataFile); - tempSha1File.renameTo(sha1File); - tempDataFile.renameTo(dataFile); - - // Read new EhTagDatabase - try (BufferedSource source = Okio.buffer(Okio.source(dataFile))) { - instance = new EhTagDatabase(dataName, source); + @Nullable + private static byte[] getFileContent(File file, int length) { + try (BufferedSource source = Okio.buffer(Okio.source(file))) { + byte[] content = new byte[length]; + source.readFully(content); + return content; } catch (IOException e) { - // Ignore + return null; } - } finally { - lock.unlock(); - } - }); - } + } + + @Nullable + private static byte[] getFileSha1(File file) { + try (InputStream is = new FileInputStream(file)) { + MessageDigest digest = MessageDigest.getInstance("SHA-1"); + int n; + byte[] buffer = new byte[4 * 1024]; + while ((n = is.read(buffer)) != -1) { + digest.update(buffer, 0, n); + } + return digest.digest(); + } catch (IOException e) { + return null; + } catch (NoSuchAlgorithmException e) { + return null; + } + } + + private static boolean equals(byte[] b1, byte[] b2) { + if (b1 == null && b2 == null) { + return true; + } + if (b1 == null || b2 == null) { + return false; + } + + if (b1.length != b2.length) { + return false; + } + + for (int i = 0; i < b1.length; i++) { + if (b1[i] != b2[i]) { + return false; + } + } + + return true; + } + + private static boolean checkData(File sha1File, File dataFile) { + byte[] s1 = getFileContent(sha1File, 20); + if (s1 == null) { + return false; + } + + byte[] s2 = getFileSha1(dataFile); + if (s2 == null) { + return false; + } + + return equals(s1, s2); + } + + private static boolean save(OkHttpClient client, String url, File file) { + Request request = new Request.Builder().url(url).build(); + Call call = client.newCall(request); + try (Response response = call.execute()) { + if (!response.isSuccessful()) { + return false; + } + ResponseBody body = response.body(); + if (body == null) { + return false; + } + + try (InputStream is = body.byteStream(); OutputStream os = new FileOutputStream(file)) { + IOUtils.copy(is, os); + } + + return true; + } catch (Throwable t) { + ExceptionUtils.throwIfFatal(t); + return false; + } + } + + public static void update(Context context) { + String[] urls = getMetadata(context); + if (urls == null || urls.length != 4) { + // Clear tags if it's not possible + instance = null; + return; + } + + String sha1Name = urls[0]; + String sha1Url = urls[1]; + String dataName = urls[2]; + String dataUrl = urls[3]; + + // Clear tags if name if different + EhTagDatabase tmp = instance; + if (tmp != null && !tmp.name.equals(dataName)) { + instance = null; + } + + IoThreadPoolExecutor.getInstance().execute(() -> { + if (!lock.tryLock()) { + return; + } + + try { + File dir = AppConfig.getFilesDir("tag-translations"); + if (dir == null) { + return; + } + + // Check current sha1 and current data + File sha1File = new File(dir, sha1Name); + File dataFile = new File(dir, dataName); + if (!checkData(sha1File, dataFile)) { + FileUtils.delete(sha1File); + FileUtils.delete(dataFile); + } + + // Read current EhTagDatabase + if (instance == null && dataFile.exists()) { + try (BufferedSource source = Okio.buffer(Okio.source(dataFile))) { + instance = new EhTagDatabase(dataName, source); + } catch (IOException e) { + FileUtils.delete(sha1File); + FileUtils.delete(dataFile); + } + } + + OkHttpClient client = EhApplication.getOkHttpClient(EhApplication.getInstance()); + + // Save new sha1 + File tempSha1File = new File(dir, sha1Name + ".tmp"); + if (!save(client, sha1Url, tempSha1File)) { + FileUtils.delete(tempSha1File); + return; + } + + // Check new sha1 and current data + if (checkData(tempSha1File, dataFile)) { + // The data is the same + FileUtils.delete(tempSha1File); + return; + } + + // Save new data + File tempDataFile = new File(dir, dataName + ".tmp"); + if (!save(client, dataUrl, tempDataFile)) { + FileUtils.delete(tempDataFile); + return; + } + + // Check new sha1 and new data + if (!checkData(tempSha1File, tempDataFile)) { + FileUtils.delete(tempSha1File); + FileUtils.delete(tempDataFile); + return; + } + + // Replace current sha1 and current data with new sha1 and new data + FileUtils.delete(sha1File); + FileUtils.delete(dataFile); + tempSha1File.renameTo(sha1File); + tempDataFile.renameTo(dataFile); + + // Read new EhTagDatabase + try (BufferedSource source = Okio.buffer(Okio.source(dataFile))) { + instance = new EhTagDatabase(dataName, source); + } catch (IOException e) { + // Ignore + } + } finally { + lock.unlock(); + } + }); + } + + public String getTranslation(String tag) { + return search(tags, tag.getBytes(TextUrl.UTF_8)); + } + + @Nullable + private String search(byte[] tags, byte[] tag) { + int low = 0; + int high = tags.length; + while (low < high) { + int start = (low + high) / 2; + // Look for the starting '\n' + while (start > -1 && tags[start] != '\n') { + start--; + } + start++; + + // Look for the middle '\r'. + int middle = 1; + while (tags[start + middle] != '\r') { + middle++; + } + + // Look for the ending '\n' + int end = middle + 1; + while (tags[start + end] != '\n') { + end++; + } + + int compare; + int tagIndex = 0; + int curIndex = start; + + for (; ; ) { + int tagByte = tag[tagIndex] & 0xff; + int curByte = tags[curIndex] & 0xff; + compare = tagByte - curByte; + if (compare != 0) { + break; + } + + tagIndex++; + curIndex++; + if (tagIndex == tag.length && curIndex == start + middle) { + break; + } + if (tagIndex == tag.length) { + compare = -1; + break; + } + if (curIndex == start + middle) { + compare = 1; + break; + } + } + + if (compare < 0) { + high = start - 1; + } else if (compare > 0) { + low = start + end + 1; + } else { + byte[] bytes = Base64.decode(tags, start + middle + 1, end - middle - 1, Base64.DEFAULT); + return new String(bytes, TextUrl.UTF_8); + } + } + return null; + } } diff --git a/app/src/main/java/com/hippo/ehviewer/client/EhUrl.java b/app/src/main/java/com/hippo/ehviewer/client/EhUrl.java index 81eff50ec..c5240a0d6 100644 --- a/app/src/main/java/com/hippo/ehviewer/client/EhUrl.java +++ b/app/src/main/java/com/hippo/ehviewer/client/EhUrl.java @@ -17,10 +17,13 @@ package com.hippo.ehviewer.client; import androidx.annotation.NonNull; + import com.hippo.ehviewer.Settings; import com.hippo.network.UrlBuilder; + import java.util.List; import java.util.ListIterator; + import okhttp3.HttpUrl; public class EhUrl { @@ -207,7 +210,7 @@ public class EhUrl { public static String getThumbUrlPrefix() { switch (Settings.getGallerySite()) { default: - //case SITE_E: + //case SITE_E: return URL_PREFIX_THUMB_E; //case SITE_EX: // return URL_PREFIX_THUMB_EX; diff --git a/app/src/main/java/com/hippo/ehviewer/client/EhUrlOpener.java b/app/src/main/java/com/hippo/ehviewer/client/EhUrlOpener.java index a5dbdbdb3..e48fb5e28 100644 --- a/app/src/main/java/com/hippo/ehviewer/client/EhUrlOpener.java +++ b/app/src/main/java/com/hippo/ehviewer/client/EhUrlOpener.java @@ -19,7 +19,9 @@ package com.hippo.ehviewer.client; import android.os.Bundle; import android.text.TextUtils; import android.util.Log; + import androidx.annotation.Nullable; + import com.hippo.ehviewer.client.data.ListUrlBuilder; import com.hippo.ehviewer.client.parser.GalleryDetailUrlParser; import com.hippo.ehviewer.client.parser.GalleryListUrlParser; diff --git a/app/src/main/java/com/hippo/ehviewer/client/EhUtils.java b/app/src/main/java/com/hippo/ehviewer/client/EhUtils.java index 2230e0220..8fc437678 100644 --- a/app/src/main/java/com/hippo/ehviewer/client/EhUtils.java +++ b/app/src/main/java/com/hippo/ehviewer/client/EhUtils.java @@ -19,10 +19,13 @@ package com.hippo.ehviewer.client; import android.content.Context; import android.graphics.Color; import android.text.TextUtils; + import androidx.annotation.Nullable; + import com.hippo.ehviewer.EhApplication; import com.hippo.ehviewer.Settings; import com.hippo.ehviewer.client.data.GalleryInfo; + import java.util.regex.Pattern; public class EhUtils { @@ -64,20 +67,20 @@ public class EhUtils { EhConfig.ASIAN_PORN, EhConfig.NON_H, EhConfig.WESTERN, - UNKNOWN }; + UNKNOWN}; private static final String[][] CATEGORY_STRINGS = { - new String[] { "misc" }, - new String[] { "doujinshi" }, - new String[] { "manga" }, - new String[] { "artistcg", "Artist CG Sets", "Artist CG" }, - new String[] { "gamecg", "Game CG Sets", "Game CG" }, - new String[] { "imageset", "Image Sets", "Image Set" }, - new String[] { "cosplay" }, - new String[] { "asianporn", "Asian Porn" }, - new String[] { "non-h" }, - new String[] { "western" }, - new String[] { "unknown" } + new String[]{"misc"}, + new String[]{"doujinshi"}, + new String[]{"manga"}, + new String[]{"artistcg", "Artist CG Sets", "Artist CG"}, + new String[]{"gamecg", "Game CG Sets", "Game CG"}, + new String[]{"imageset", "Image Sets", "Image Set"}, + new String[]{"cosplay"}, + new String[]{"asianporn", "Asian Porn"}, + new String[]{"non-h"}, + new String[]{"western"}, + new String[]{"unknown"} }; public static int getCategory(String type) { diff --git a/app/src/main/java/com/hippo/ehviewer/client/data/FavListUrlBuilder.java b/app/src/main/java/com/hippo/ehviewer/client/data/FavListUrlBuilder.java index 68349846d..b9213dbb6 100644 --- a/app/src/main/java/com/hippo/ehviewer/client/data/FavListUrlBuilder.java +++ b/app/src/main/java/com/hippo/ehviewer/client/data/FavListUrlBuilder.java @@ -29,36 +29,56 @@ import java.net.URLEncoder; public class FavListUrlBuilder implements Parcelable { - private static final String TAG = FavListUrlBuilder.class.getSimpleName(); public static final int FAV_CAT_ALL = -1; public static final int FAV_CAT_LOCAL = -2; + public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { + @Override + public FavListUrlBuilder createFromParcel(Parcel source) { + return new FavListUrlBuilder(source); + } + + @Override + public FavListUrlBuilder[] newArray(int size) { + return new FavListUrlBuilder[size]; + } + }; + private static final String TAG = FavListUrlBuilder.class.getSimpleName(); private int mIndex; private String mKeyword; private int mFavCat = FAV_CAT_ALL; + public FavListUrlBuilder() { + } + + protected FavListUrlBuilder(Parcel in) { + this.mIndex = in.readInt(); + this.mKeyword = in.readString(); + this.mFavCat = in.readInt(); + } + + public static boolean isValidFavCat(int favCat) { + return favCat >= 0 && favCat <= 9; + } + public void setIndex(int index) { mIndex = index; } - public void setKeyword(String keyword) { - mKeyword = keyword; - } - - public void setFavCat(int favCat) { - mFavCat = favCat; - } - public String getKeyword() { return mKeyword; } + public void setKeyword(String keyword) { + mKeyword = keyword; + } + public int getFavCat() { return mFavCat; } - public static boolean isValidFavCat(int favCat) { - return favCat >= 0 && favCat <= 9; + public void setFavCat(int favCat) { + mFavCat = favCat; } public boolean isLocalFavCat() { @@ -102,26 +122,4 @@ public class FavListUrlBuilder implements Parcelable { dest.writeString(this.mKeyword); dest.writeInt(this.mFavCat); } - - public FavListUrlBuilder() { - } - - protected FavListUrlBuilder(Parcel in) { - this.mIndex = in.readInt(); - this.mKeyword = in.readString(); - this.mFavCat = in.readInt(); - } - - public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { - - @Override - public FavListUrlBuilder createFromParcel(Parcel source) { - return new FavListUrlBuilder(source); - } - - @Override - public FavListUrlBuilder[] newArray(int size) { - return new FavListUrlBuilder[size]; - } - }; } diff --git a/app/src/main/java/com/hippo/ehviewer/client/data/GalleryApiInfo.java b/app/src/main/java/com/hippo/ehviewer/client/data/GalleryApiInfo.java index 2286faf5f..642566995 100644 --- a/app/src/main/java/com/hippo/ehviewer/client/data/GalleryApiInfo.java +++ b/app/src/main/java/com/hippo/ehviewer/client/data/GalleryApiInfo.java @@ -18,10 +18,22 @@ package com.hippo.ehviewer.client.data; import android.os.Parcel; import android.os.Parcelable; + import androidx.annotation.Nullable; public class GalleryApiInfo implements Parcelable { + public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { + @Override + public GalleryApiInfo createFromParcel(Parcel source) { + return new GalleryApiInfo(source); + } + + @Override + public GalleryApiInfo[] newArray(int size) { + return new GalleryApiInfo[size]; + } + }; public long gid; public String token; public String archiverKey; @@ -39,6 +51,27 @@ public class GalleryApiInfo implements Parcelable { @Nullable public String[] tags; + public GalleryApiInfo() { + } + + protected GalleryApiInfo(Parcel in) { + this.gid = in.readLong(); + this.token = in.readString(); + this.archiverKey = in.readString(); + this.title = in.readString(); + this.titleJpn = in.readString(); + this.category = in.readInt(); + this.thumb = in.readString(); + this.uploader = in.readString(); + this.posted = in.readLong(); + this.filecount = in.readInt(); + this.filesize = in.readLong(); + this.expunged = in.readByte() != 0; + this.rating = in.readFloat(); + this.torrentcount = in.readInt(); + this.tags = in.createStringArray(); + } + @Override public int describeContents() { return 0; @@ -62,37 +95,4 @@ public class GalleryApiInfo implements Parcelable { dest.writeInt(this.torrentcount); dest.writeStringArray(this.tags); } - - public GalleryApiInfo() { - } - - protected GalleryApiInfo(Parcel in) { - this.gid = in.readLong(); - this.token = in.readString(); - this.archiverKey = in.readString(); - this.title = in.readString(); - this.titleJpn = in.readString(); - this.category = in.readInt(); - this.thumb = in.readString(); - this.uploader = in.readString(); - this.posted = in.readLong(); - this.filecount = in.readInt(); - this.filesize = in.readLong(); - this.expunged = in.readByte() != 0; - this.rating = in.readFloat(); - this.torrentcount = in.readInt(); - this.tags = in.createStringArray(); - } - - public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { - @Override - public GalleryApiInfo createFromParcel(Parcel source) { - return new GalleryApiInfo(source); - } - - @Override - public GalleryApiInfo[] newArray(int size) { - return new GalleryApiInfo[size]; - } - }; } diff --git a/app/src/main/java/com/hippo/ehviewer/client/data/GalleryComment.java b/app/src/main/java/com/hippo/ehviewer/client/data/GalleryComment.java index 04296bbf7..d276fc692 100644 --- a/app/src/main/java/com/hippo/ehviewer/client/data/GalleryComment.java +++ b/app/src/main/java/com/hippo/ehviewer/client/data/GalleryComment.java @@ -21,6 +21,17 @@ import android.os.Parcelable; public class GalleryComment implements Parcelable { + public static final Creator CREATOR = new Creator() { + @Override + public GalleryComment createFromParcel(Parcel source) { + return new GalleryComment(source); + } + + @Override + public GalleryComment[] newArray(int size) { + return new GalleryComment[size]; + } + }; // 0 for uploader comment. can't vote public long id; public int score; @@ -35,6 +46,24 @@ public class GalleryComment implements Parcelable { public String comment; public long lastEdited; + public GalleryComment() { + } + + protected GalleryComment(Parcel in) { + this.id = in.readLong(); + this.score = in.readInt(); + this.editable = in.readByte() != 0; + this.voteUpAble = in.readByte() != 0; + this.voteUpEd = in.readByte() != 0; + this.voteDownAble = in.readByte() != 0; + this.voteDownEd = in.readByte() != 0; + this.voteState = in.readString(); + this.time = in.readLong(); + this.user = in.readString(); + this.comment = in.readString(); + this.lastEdited = in.readLong(); + } + @Override public int describeContents() { return 0; @@ -55,34 +84,4 @@ public class GalleryComment implements Parcelable { dest.writeString(this.comment); dest.writeLong(this.lastEdited); } - - public GalleryComment() { - } - - protected GalleryComment(Parcel in) { - this.id = in.readLong(); - this.score = in.readInt(); - this.editable = in.readByte() != 0; - this.voteUpAble = in.readByte() != 0; - this.voteUpEd = in.readByte() != 0; - this.voteDownAble = in.readByte() != 0; - this.voteDownEd = in.readByte() != 0; - this.voteState = in.readString(); - this.time = in.readLong(); - this.user = in.readString(); - this.comment = in.readString(); - this.lastEdited = in.readLong(); - } - - public static final Creator CREATOR = new Creator() { - @Override - public GalleryComment createFromParcel(Parcel source) { - return new GalleryComment(source); - } - - @Override - public GalleryComment[] newArray(int size) { - return new GalleryComment[size]; - } - }; } diff --git a/app/src/main/java/com/hippo/ehviewer/client/data/GalleryCommentList.java b/app/src/main/java/com/hippo/ehviewer/client/data/GalleryCommentList.java index 23370155f..b4f51907f 100644 --- a/app/src/main/java/com/hippo/ehviewer/client/data/GalleryCommentList.java +++ b/app/src/main/java/com/hippo/ehviewer/client/data/GalleryCommentList.java @@ -18,48 +18,48 @@ package com.hippo.ehviewer.client.data; import android.os.Parcel; import android.os.Parcelable; + import java.util.Arrays; public class GalleryCommentList implements Parcelable { - public GalleryComment[] comments; - public boolean hasMore; + public static final Creator CREATOR = new Creator() { + @Override + public GalleryCommentList createFromParcel(Parcel in) { + return new GalleryCommentList(in); + } - @Override - public int describeContents() { - return 0; - } + @Override + public GalleryCommentList[] newArray(int size) { + return new GalleryCommentList[size]; + } + }; + public GalleryComment[] comments; + public boolean hasMore; - @Override - public void writeToParcel(Parcel dest, int flags) { - dest.writeParcelableArray(comments, flags); - dest.writeByte(hasMore ? (byte) 1 : (byte) 0); - } - - public GalleryCommentList(GalleryComment[] comments, boolean hasMore) { - this.comments = comments; - this.hasMore = hasMore; - } - - protected GalleryCommentList(Parcel in) { - Parcelable[] array = in.readParcelableArray(getClass().getClassLoader()); - if (array != null) { - comments = Arrays.copyOf(array, array.length, GalleryComment[].class); - } else { - comments = null; + public GalleryCommentList(GalleryComment[] comments, boolean hasMore) { + this.comments = comments; + this.hasMore = hasMore; } - hasMore = in.readByte() != 0; - } - public static final Creator CREATOR = new Creator() { - @Override - public GalleryCommentList createFromParcel(Parcel in) { - return new GalleryCommentList(in); + protected GalleryCommentList(Parcel in) { + Parcelable[] array = in.readParcelableArray(getClass().getClassLoader()); + if (array != null) { + comments = Arrays.copyOf(array, array.length, GalleryComment[].class); + } else { + comments = null; + } + hasMore = in.readByte() != 0; } @Override - public GalleryCommentList[] newArray(int size) { - return new GalleryCommentList[size]; + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeParcelableArray(comments, flags); + dest.writeByte(hasMore ? (byte) 1 : (byte) 0); } - }; } diff --git a/app/src/main/java/com/hippo/ehviewer/client/data/GalleryDetail.java b/app/src/main/java/com/hippo/ehviewer/client/data/GalleryDetail.java index 71e93615b..7db250b62 100644 --- a/app/src/main/java/com/hippo/ehviewer/client/data/GalleryDetail.java +++ b/app/src/main/java/com/hippo/ehviewer/client/data/GalleryDetail.java @@ -23,6 +23,17 @@ import java.util.Arrays; public class GalleryDetail extends GalleryInfo { + public static final Creator CREATOR = new Creator() { + @Override + public GalleryDetail createFromParcel(Parcel source) { + return new GalleryDetail(source); + } + + @Override + public GalleryDetail[] newArray(int size) { + return new GalleryDetail[size]; + } + }; public long apiUid = -1L; public String apiKey; public int torrentCount; @@ -41,31 +52,6 @@ public class GalleryDetail extends GalleryInfo { public int previewPages; public PreviewSet previewSet; - @Override - public int describeContents() { - return 0; - } - - @Override - public void writeToParcel(Parcel dest, int flags) { - super.writeToParcel(dest, flags); - dest.writeInt(this.torrentCount); - dest.writeString(this.torrentUrl); - dest.writeString(this.archiveUrl); - dest.writeString(this.parent); - dest.writeString(this.visible); - dest.writeString(this.language); - dest.writeString(this.size); - dest.writeInt(this.pages); - dest.writeInt(this.favoriteCount); - dest.writeByte(isFavorited ? (byte) 1 : (byte) 0); - dest.writeInt(this.ratingCount); - dest.writeParcelableArray(this.tags, flags); - dest.writeParcelable(this.comments, flags); - dest.writeInt(this.previewPages); - dest.writeParcelable(previewSet, flags); - } - public GalleryDetail() { } @@ -93,15 +79,28 @@ public class GalleryDetail extends GalleryInfo { this.previewSet = in.readParcelable(PreviewSet.class.getClassLoader()); } - public static final Creator CREATOR = new Creator() { - @Override - public GalleryDetail createFromParcel(Parcel source) { - return new GalleryDetail(source); - } + @Override + public int describeContents() { + return 0; + } - @Override - public GalleryDetail[] newArray(int size) { - return new GalleryDetail[size]; - } - }; + @Override + public void writeToParcel(Parcel dest, int flags) { + super.writeToParcel(dest, flags); + dest.writeInt(this.torrentCount); + dest.writeString(this.torrentUrl); + dest.writeString(this.archiveUrl); + dest.writeString(this.parent); + dest.writeString(this.visible); + dest.writeString(this.language); + dest.writeString(this.size); + dest.writeInt(this.pages); + dest.writeInt(this.favoriteCount); + dest.writeByte(isFavorited ? (byte) 1 : (byte) 0); + dest.writeInt(this.ratingCount); + dest.writeParcelableArray(this.tags, flags); + dest.writeParcelable(this.comments, flags); + dest.writeInt(this.previewPages); + dest.writeParcelable(previewSet, flags); + } } diff --git a/app/src/main/java/com/hippo/ehviewer/client/data/GalleryInfo.java b/app/src/main/java/com/hippo/ehviewer/client/data/GalleryInfo.java index a83ec3d76..2d6ffea9c 100644 --- a/app/src/main/java/com/hippo/ehviewer/client/data/GalleryInfo.java +++ b/app/src/main/java/com/hippo/ehviewer/client/data/GalleryInfo.java @@ -18,7 +18,9 @@ package com.hippo.ehviewer.client.data; import android.os.Parcel; import android.os.Parcelable; + import androidx.annotation.Nullable; + import java.util.regex.Pattern; public class GalleryInfo implements Parcelable { @@ -79,23 +81,34 @@ public class GalleryInfo implements Parcelable { }; public static final String[] S_LANG_TAGS = { - "language:english", - "language:chinese", - "language:spanish", - "language:korean", - "language:russian", - "language:french", - "language:portuguese", - "language:thai", - "language:german", - "language:italian", - "language:vietnamese", - "language:polish", - "language:hungarian", - "language:dutch", + "language:english", + "language:chinese", + "language:spanish", + "language:korean", + "language:russian", + "language:french", + "language:portuguese", + "language:thai", + "language:german", + "language:italian", + "language:vietnamese", + "language:polish", + "language:hungarian", + "language:dutch", }; + public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { - public long gid ; + @Override + public GalleryInfo createFromParcel(Parcel source) { + return new GalleryInfo(source); + } + + @Override + public GalleryInfo[] newArray(int size) { + return new GalleryInfo[size]; + } + }; + public long gid; public String token; public String title; public String titleJpn; @@ -108,22 +121,43 @@ public class GalleryInfo implements Parcelable { @Nullable public String[] simpleTags; public int pages; - public int thumbWidth; public int thumbHeight; - public int spanSize; public int spanIndex; public int spanGroupIndex; - /** * language from title */ public String simpleLanguage; - public int favoriteSlot = -2; public String favoriteName; + public GalleryInfo() { + } + + protected GalleryInfo(Parcel in) { + this.gid = in.readLong(); + this.token = in.readString(); + this.title = in.readString(); + this.titleJpn = in.readString(); + this.thumb = in.readString(); + this.category = in.readInt(); + this.posted = in.readString(); + this.uploader = in.readString(); + this.rating = in.readFloat(); + this.rated = in.readByte() != 0; + this.simpleLanguage = in.readString(); + this.simpleTags = in.createStringArray(); + this.thumbWidth = in.readInt(); + this.thumbHeight = in.readInt(); + this.spanSize = in.readInt(); + this.spanIndex = in.readInt(); + this.spanGroupIndex = in.readInt(); + this.favoriteSlot = in.readInt(); + this.favoriteName = in.readString(); + } + public final void generateSLang() { if (simpleTags != null) { generateSLangFromTags(); @@ -181,41 +215,4 @@ public class GalleryInfo implements Parcelable { dest.writeInt(this.favoriteSlot); dest.writeString(this.favoriteName); } - - public GalleryInfo() {} - - protected GalleryInfo(Parcel in) { - this.gid = in.readLong(); - this.token = in.readString(); - this.title = in.readString(); - this.titleJpn = in.readString(); - this.thumb = in.readString(); - this.category = in.readInt(); - this.posted = in.readString(); - this.uploader = in.readString(); - this.rating = in.readFloat(); - this.rated = in.readByte() != 0; - this.simpleLanguage = in.readString(); - this.simpleTags = in.createStringArray(); - this.thumbWidth = in.readInt(); - this.thumbHeight = in.readInt(); - this.spanSize = in.readInt(); - this.spanIndex = in.readInt(); - this.spanGroupIndex = in.readInt(); - this.favoriteSlot = in.readInt(); - this.favoriteName = in.readString(); - } - - public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { - - @Override - public GalleryInfo createFromParcel(Parcel source) { - return new GalleryInfo(source); - } - - @Override - public GalleryInfo[] newArray(int size) { - return new GalleryInfo[size]; - } - }; } diff --git a/app/src/main/java/com/hippo/ehviewer/client/data/GalleryPreview.java b/app/src/main/java/com/hippo/ehviewer/client/data/GalleryPreview.java index 6529481e2..b4fafee7f 100644 --- a/app/src/main/java/com/hippo/ehviewer/client/data/GalleryPreview.java +++ b/app/src/main/java/com/hippo/ehviewer/client/data/GalleryPreview.java @@ -23,6 +23,17 @@ import com.hippo.widget.LoadImageView; public class GalleryPreview implements Parcelable { + public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { + @Override + public GalleryPreview createFromParcel(Parcel source) { + return new GalleryPreview(source); + } + + @Override + public GalleryPreview[] newArray(int size) { + return new GalleryPreview[size]; + } + }; String imageKey; String imageUrl; String pageUrl; @@ -32,6 +43,19 @@ public class GalleryPreview implements Parcelable { int clipWidth = Integer.MIN_VALUE; int clipHeight = Integer.MIN_VALUE; + public GalleryPreview() { + } + + protected GalleryPreview(Parcel in) { + this.imageUrl = in.readString(); + this.pageUrl = in.readString(); + this.position = in.readInt(); + this.offsetX = in.readInt(); + this.offsetY = in.readInt(); + this.clipWidth = in.readInt(); + this.clipHeight = in.readInt(); + } + public int getPosition() { return position; } @@ -56,29 +80,4 @@ public class GalleryPreview implements Parcelable { dest.writeInt(this.clipWidth); dest.writeInt(this.clipHeight); } - - public GalleryPreview() { - } - - protected GalleryPreview(Parcel in) { - this.imageUrl = in.readString(); - this.pageUrl = in.readString(); - this.position = in.readInt(); - this.offsetX = in.readInt(); - this.offsetY = in.readInt(); - this.clipWidth = in.readInt(); - this.clipHeight = in.readInt(); - } - - public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { - @Override - public GalleryPreview createFromParcel(Parcel source) { - return new GalleryPreview(source); - } - - @Override - public GalleryPreview[] newArray(int size) { - return new GalleryPreview[size]; - } - }; } diff --git a/app/src/main/java/com/hippo/ehviewer/client/data/GalleryTagGroup.java b/app/src/main/java/com/hippo/ehviewer/client/data/GalleryTagGroup.java index c46d41e8d..7fdf36c13 100644 --- a/app/src/main/java/com/hippo/ehviewer/client/data/GalleryTagGroup.java +++ b/app/src/main/java/com/hippo/ehviewer/client/data/GalleryTagGroup.java @@ -24,8 +24,28 @@ import java.util.ArrayList; // TODO Add url field? public class GalleryTagGroup implements Parcelable { - public String groupName; + public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { + @Override + public GalleryTagGroup createFromParcel(Parcel source) { + return new GalleryTagGroup(source); + } + + @Override + public GalleryTagGroup[] newArray(int size) { + return new GalleryTagGroup[size]; + } + }; private final ArrayList mTagList; + public String groupName; + + public GalleryTagGroup() { + mTagList = new ArrayList<>(); + } + + protected GalleryTagGroup(Parcel in) { + this.groupName = in.readString(); + this.mTagList = in.createStringArrayList(); + } public void addTag(String tag) { mTagList.add(tag); @@ -49,25 +69,4 @@ public class GalleryTagGroup implements Parcelable { dest.writeString(this.groupName); dest.writeStringList(this.mTagList); } - - public GalleryTagGroup() { - mTagList = new ArrayList<>(); - } - - protected GalleryTagGroup(Parcel in) { - this.groupName = in.readString(); - this.mTagList = in.createStringArrayList(); - } - - public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { - @Override - public GalleryTagGroup createFromParcel(Parcel source) { - return new GalleryTagGroup(source); - } - - @Override - public GalleryTagGroup[] newArray(int size) { - return new GalleryTagGroup[size]; - } - }; } diff --git a/app/src/main/java/com/hippo/ehviewer/client/data/LargePreviewSet.java b/app/src/main/java/com/hippo/ehviewer/client/data/LargePreviewSet.java index b91ae6b3a..b2b1d7fb0 100644 --- a/app/src/main/java/com/hippo/ehviewer/client/data/LargePreviewSet.java +++ b/app/src/main/java/com/hippo/ehviewer/client/data/LargePreviewSet.java @@ -27,10 +27,33 @@ import java.util.ArrayList; public class LargePreviewSet extends PreviewSet { + public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { + @Override + public LargePreviewSet createFromParcel(Parcel source) { + return new LargePreviewSet(source); + } + + @Override + public LargePreviewSet[] newArray(int size) { + return new LargePreviewSet[size]; + } + }; private final IntList mPositionList; private final ArrayList mImageUrlList; private final ArrayList mPageUrlList; + public LargePreviewSet() { + mPositionList = new IntList(); + mImageUrlList = new ArrayList<>(); + mPageUrlList = new ArrayList<>(); + } + + protected LargePreviewSet(Parcel in) { + this.mPositionList = in.readParcelable(IntList.class.getClassLoader()); + this.mImageUrlList = in.createStringArrayList(); + this.mPageUrlList = in.createStringArrayList(); + } + public void addItem(int index, String imageUrl, String pageUrl) { mPositionList.add(index); mImageUrlList.add(imageUrl); @@ -80,28 +103,4 @@ public class LargePreviewSet extends PreviewSet { dest.writeStringList(this.mImageUrlList); dest.writeStringList(this.mPageUrlList); } - - public LargePreviewSet() { - mPositionList = new IntList(); - mImageUrlList = new ArrayList<>(); - mPageUrlList = new ArrayList<>(); - } - - protected LargePreviewSet(Parcel in) { - this.mPositionList = in.readParcelable(IntList.class.getClassLoader()); - this.mImageUrlList = in.createStringArrayList(); - this.mPageUrlList = in.createStringArrayList(); - } - - public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { - @Override - public LargePreviewSet createFromParcel(Parcel source) { - return new LargePreviewSet(source); - } - - @Override - public LargePreviewSet[] newArray(int size) { - return new LargePreviewSet[size]; - } - }; } diff --git a/app/src/main/java/com/hippo/ehviewer/client/data/ListUrlBuilder.java b/app/src/main/java/com/hippo/ehviewer/client/data/ListUrlBuilder.java index 20056c6d1..80893dd27 100644 --- a/app/src/main/java/com/hippo/ehviewer/client/data/ListUrlBuilder.java +++ b/app/src/main/java/com/hippo/ehviewer/client/data/ListUrlBuilder.java @@ -19,8 +19,10 @@ package com.hippo.ehviewer.client.data; import android.os.Parcel; import android.os.Parcelable; import android.text.TextUtils; + import androidx.annotation.IntDef; import androidx.annotation.Nullable; + import com.hippo.ehviewer.client.EhConfig; import com.hippo.ehviewer.client.EhUrl; import com.hippo.ehviewer.client.EhUtils; @@ -29,6 +31,7 @@ import com.hippo.ehviewer.widget.AdvanceSearchTable; import com.hippo.network.UrlBuilder; import com.hippo.yorozuya.NumberUtils; import com.hippo.yorozuya.StringUtils; + import java.io.UnsupportedEncodingException; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -37,10 +40,6 @@ import java.net.URLEncoder; public class ListUrlBuilder implements Cloneable, Parcelable { - @IntDef({MODE_NORMAL, MODE_UPLOADER, MODE_TAG, MODE_WHATS_HOT, MODE_IMAGE_SEARCH, MODE_SUBSCRIPTION}) - @Retention(RetentionPolicy.SOURCE) - private @interface Mode {} - // Mode public static final int MODE_NORMAL = 0x0; public static final int MODE_UPLOADER = 0x1; @@ -48,10 +47,19 @@ public class ListUrlBuilder implements Cloneable, Parcelable { public static final int MODE_WHATS_HOT = 0x3; public static final int MODE_IMAGE_SEARCH = 0x4; public static final int MODE_SUBSCRIPTION = 0x5; - public static final int DEFAULT_ADVANCE = AdvanceSearchTable.SNAME | AdvanceSearchTable.STAGS; public static final int DEFAULT_MIN_RATING = 2; + public static final Creator CREATOR = new Creator() { + @Override + public ListUrlBuilder createFromParcel(Parcel source) { + return new ListUrlBuilder(source); + } + @Override + public ListUrlBuilder[] newArray(int size) { + return new ListUrlBuilder[size]; + } + }; @Mode private int mMode = MODE_NORMAL; @@ -70,6 +78,25 @@ public class ListUrlBuilder implements Cloneable, Parcelable { private boolean mOnlySearchCovers; private boolean mShowExpunged; + public ListUrlBuilder() { + } + + @SuppressWarnings("WrongConstant") + protected ListUrlBuilder(Parcel in) { + this.mMode = in.readInt(); + this.mPageIndex = in.readInt(); + this.mCategory = in.readInt(); + this.mKeyword = in.readString(); + this.mAdvanceSearch = in.readInt(); + this.mMinRating = in.readInt(); + this.mPageFrom = in.readInt(); + this.mPageTo = in.readInt(); + this.mImagePath = in.readString(); + this.mUseSimilarityScan = in.readByte() != 0; + this.mOnlySearchCovers = in.readByte() != 0; + this.mShowExpunged = in.readByte() != 0; + } + /** * Make this ListUrlBuilder point to homepage */ @@ -197,6 +224,7 @@ public class ListUrlBuilder implements Cloneable, Parcelable { /** * Make them the same + * * @param lub The template */ public void set(ListUrlBuilder lub) { @@ -263,11 +291,7 @@ public class ListUrlBuilder implements Cloneable, Parcelable { if (q.pageFrom != mPageFrom) { return false; } - if (q.pageTo != mPageTo) { - return false; - } - - return true; + return q.pageTo == mPageTo; } /** @@ -497,17 +521,23 @@ public class ListUrlBuilder implements Cloneable, Parcelable { // Advance search if (mAdvanceSearch != -1) { ub.addQuery("advsearch", "1"); - if((mAdvanceSearch & AdvanceSearchTable.SNAME) != 0) ub.addQuery("f_sname", "on"); - if((mAdvanceSearch & AdvanceSearchTable.STAGS) != 0) ub.addQuery("f_stags", "on"); - if((mAdvanceSearch & AdvanceSearchTable.SDESC) != 0) ub.addQuery("f_sdesc", "on"); - if((mAdvanceSearch & AdvanceSearchTable.STORR) != 0) ub.addQuery("f_storr", "on"); - if((mAdvanceSearch & AdvanceSearchTable.STO) != 0) ub.addQuery("f_sto", "on"); - if((mAdvanceSearch & AdvanceSearchTable.SDT1) != 0) ub.addQuery("f_sdt1", "on"); - if((mAdvanceSearch & AdvanceSearchTable.SDT2) != 0) ub.addQuery("f_sdt2", "on"); - if((mAdvanceSearch & AdvanceSearchTable.SH) != 0) ub.addQuery("f_sh", "on"); - if((mAdvanceSearch & AdvanceSearchTable.SFL) != 0) ub.addQuery("f_sfl", "on"); - if((mAdvanceSearch & AdvanceSearchTable.SFU) != 0) ub.addQuery("f_sfu", "on"); - if((mAdvanceSearch & AdvanceSearchTable.SFT) != 0) ub.addQuery("f_sft", "on"); + if ((mAdvanceSearch & AdvanceSearchTable.SNAME) != 0) + ub.addQuery("f_sname", "on"); + if ((mAdvanceSearch & AdvanceSearchTable.STAGS) != 0) + ub.addQuery("f_stags", "on"); + if ((mAdvanceSearch & AdvanceSearchTable.SDESC) != 0) + ub.addQuery("f_sdesc", "on"); + if ((mAdvanceSearch & AdvanceSearchTable.STORR) != 0) + ub.addQuery("f_storr", "on"); + if ((mAdvanceSearch & AdvanceSearchTable.STO) != 0) ub.addQuery("f_sto", "on"); + if ((mAdvanceSearch & AdvanceSearchTable.SDT1) != 0) + ub.addQuery("f_sdt1", "on"); + if ((mAdvanceSearch & AdvanceSearchTable.SDT2) != 0) + ub.addQuery("f_sdt2", "on"); + if ((mAdvanceSearch & AdvanceSearchTable.SH) != 0) ub.addQuery("f_sh", "on"); + if ((mAdvanceSearch & AdvanceSearchTable.SFL) != 0) ub.addQuery("f_sfl", "on"); + if ((mAdvanceSearch & AdvanceSearchTable.SFU) != 0) ub.addQuery("f_sfu", "on"); + if ((mAdvanceSearch & AdvanceSearchTable.SFT) != 0) ub.addQuery("f_sft", "on"); // Set min star if (mMinRating != -1) { ub.addQuery("f_sr", "on"); @@ -576,34 +606,8 @@ public class ListUrlBuilder implements Cloneable, Parcelable { dest.writeByte(mShowExpunged ? (byte) 1 : (byte) 0); } - public ListUrlBuilder() { + @IntDef({MODE_NORMAL, MODE_UPLOADER, MODE_TAG, MODE_WHATS_HOT, MODE_IMAGE_SEARCH, MODE_SUBSCRIPTION}) + @Retention(RetentionPolicy.SOURCE) + private @interface Mode { } - - @SuppressWarnings("WrongConstant") - protected ListUrlBuilder(Parcel in) { - this.mMode = in.readInt(); - this.mPageIndex = in.readInt(); - this.mCategory = in.readInt(); - this.mKeyword = in.readString(); - this.mAdvanceSearch = in.readInt(); - this.mMinRating = in.readInt(); - this.mPageFrom = in.readInt(); - this.mPageTo = in.readInt(); - this.mImagePath = in.readString(); - this.mUseSimilarityScan = in.readByte() != 0; - this.mOnlySearchCovers = in.readByte() != 0; - this.mShowExpunged = in.readByte() != 0; - } - - public static final Creator CREATOR = new Creator() { - @Override - public ListUrlBuilder createFromParcel(Parcel source) { - return new ListUrlBuilder(source); - } - - @Override - public ListUrlBuilder[] newArray(int size) { - return new ListUrlBuilder[size]; - } - }; } diff --git a/app/src/main/java/com/hippo/ehviewer/client/data/NormalPreviewSet.java b/app/src/main/java/com/hippo/ehviewer/client/data/NormalPreviewSet.java index d2945f72c..de4962209 100644 --- a/app/src/main/java/com/hippo/ehviewer/client/data/NormalPreviewSet.java +++ b/app/src/main/java/com/hippo/ehviewer/client/data/NormalPreviewSet.java @@ -25,6 +25,17 @@ import java.util.ArrayList; public class NormalPreviewSet extends PreviewSet { + public static final Creator CREATOR = new Creator() { + @Override + public NormalPreviewSet createFromParcel(Parcel source) { + return new NormalPreviewSet(source); + } + + @Override + public NormalPreviewSet[] newArray(int size) { + return new NormalPreviewSet[size]; + } + }; private IntList mPositionList = new IntList(); private ArrayList mImageKeyList = new ArrayList<>(); private ArrayList mImageUrlList = new ArrayList<>(); @@ -34,6 +45,20 @@ public class NormalPreviewSet extends PreviewSet { private IntList mClipHeightList = new IntList(); private ArrayList mPageUrlList = new ArrayList<>(); + public NormalPreviewSet() { + } + + protected NormalPreviewSet(Parcel in) { + this.mPositionList = in.readParcelable(IntList.class.getClassLoader()); + this.mImageKeyList = in.createStringArrayList(); + this.mImageUrlList = in.createStringArrayList(); + this.mOffsetXList = in.readParcelable(IntList.class.getClassLoader()); + this.mOffsetYList = in.readParcelable(IntList.class.getClassLoader()); + this.mClipWidthList = in.readParcelable(IntList.class.getClassLoader()); + this.mClipHeightList = in.readParcelable(IntList.class.getClassLoader()); + this.mPageUrlList = in.createStringArrayList(); + } + private String getImageKey(String imageUrl) { int index = imageUrl.indexOf('/'); if (index >= 0) { @@ -44,7 +69,7 @@ public class NormalPreviewSet extends PreviewSet { } public void addItem(int position, String imageUrl, int xOffset, int yOffset, int width, - int height, String pageUrl) { + int height, String pageUrl) { mPositionList.add(position); mImageKeyList.add(getImageKey(imageUrl)); mImageUrlList.add(imageUrl); @@ -107,30 +132,4 @@ public class NormalPreviewSet extends PreviewSet { dest.writeParcelable(this.mClipHeightList, flags); dest.writeStringList(this.mPageUrlList); } - - public NormalPreviewSet() { - } - - protected NormalPreviewSet(Parcel in) { - this.mPositionList = in.readParcelable(IntList.class.getClassLoader()); - this.mImageKeyList = in.createStringArrayList(); - this.mImageUrlList = in.createStringArrayList(); - this.mOffsetXList = in.readParcelable(IntList.class.getClassLoader()); - this.mOffsetYList = in.readParcelable(IntList.class.getClassLoader()); - this.mClipWidthList = in.readParcelable(IntList.class.getClassLoader()); - this.mClipHeightList = in.readParcelable(IntList.class.getClassLoader()); - this.mPageUrlList = in.createStringArrayList(); - } - - public static final Creator CREATOR = new Creator() { - @Override - public NormalPreviewSet createFromParcel(Parcel source) { - return new NormalPreviewSet(source); - } - - @Override - public NormalPreviewSet[] newArray(int size) { - return new NormalPreviewSet[size]; - } - }; } diff --git a/app/src/main/java/com/hippo/ehviewer/client/parser/FavoritesParser.java b/app/src/main/java/com/hippo/ehviewer/client/parser/FavoritesParser.java index b6450b752..0eae53cd0 100644 --- a/app/src/main/java/com/hippo/ehviewer/client/parser/FavoritesParser.java +++ b/app/src/main/java/com/hippo/ehviewer/client/parser/FavoritesParser.java @@ -24,21 +24,15 @@ import com.hippo.ehviewer.client.exception.ParseException; import com.hippo.util.ExceptionUtils; import com.hippo.util.JsoupUtils; import com.hippo.yorozuya.AssertUtils; -import java.util.List; + import org.jsoup.Jsoup; import org.jsoup.nodes.Document; import org.jsoup.nodes.Element; import org.jsoup.select.Elements; -public class FavoritesParser { +import java.util.List; - public static class Result { - public String[] catArray; // Size 10 - public int[] countArray; // Size 10 - public int pages; - public int nextPage; - public List galleryInfoList; - } +public class FavoritesParser { public static Result parse(String body) throws Exception { if (body.contains("This page requires you to log on.

")) { @@ -77,4 +71,12 @@ public class FavoritesParser { return re; } + + public static class Result { + public String[] catArray; // Size 10 + public int[] countArray; // Size 10 + public int pages; + public int nextPage; + public List galleryInfoList; + } } diff --git a/app/src/main/java/com/hippo/ehviewer/client/parser/ForumsParser.java b/app/src/main/java/com/hippo/ehviewer/client/parser/ForumsParser.java index 2e591d362..c3b1bf3ad 100644 --- a/app/src/main/java/com/hippo/ehviewer/client/parser/ForumsParser.java +++ b/app/src/main/java/com/hippo/ehviewer/client/parser/ForumsParser.java @@ -19,6 +19,7 @@ package com.hippo.ehviewer.client.parser; import com.hippo.ehviewer.client.EhUrl; import com.hippo.ehviewer.client.exception.ParseException; import com.hippo.util.ExceptionUtils; + import org.jsoup.Jsoup; import org.jsoup.nodes.Document; import org.jsoup.nodes.Element; diff --git a/app/src/main/java/com/hippo/ehviewer/client/parser/GalleryApiParser.java b/app/src/main/java/com/hippo/ehviewer/client/parser/GalleryApiParser.java index 1d243d955..daf7f99d7 100644 --- a/app/src/main/java/com/hippo/ehviewer/client/parser/GalleryApiParser.java +++ b/app/src/main/java/com/hippo/ehviewer/client/parser/GalleryApiParser.java @@ -18,8 +18,8 @@ package com.hippo.ehviewer.client.parser; import com.hippo.ehviewer.client.EhUtils; import com.hippo.ehviewer.client.data.GalleryInfo; - import com.hippo.yorozuya.NumberUtils; + import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; diff --git a/app/src/main/java/com/hippo/ehviewer/client/parser/GalleryDetailParser.java b/app/src/main/java/com/hippo/ehviewer/client/parser/GalleryDetailParser.java index 27f9a6717..32b0f310b 100644 --- a/app/src/main/java/com/hippo/ehviewer/client/parser/GalleryDetailParser.java +++ b/app/src/main/java/com/hippo/ehviewer/client/parser/GalleryDetailParser.java @@ -18,6 +18,7 @@ package com.hippo.ehviewer.client.parser; import androidx.annotation.NonNull; import androidx.annotation.Nullable; + import com.hippo.ehviewer.Settings; import com.hippo.ehviewer.client.EhUrl; import com.hippo.ehviewer.client.EhUtils; @@ -37,6 +38,15 @@ import com.hippo.util.JsoupUtils; import com.hippo.util.MutableBoolean; import com.hippo.yorozuya.NumberUtils; import com.hippo.yorozuya.StringUtils; + +import org.jsoup.Jsoup; +import org.jsoup.nodes.Document; +import org.jsoup.nodes.Element; +import org.jsoup.nodes.Node; +import org.jsoup.select.Elements; +import org.jsoup.select.NodeTraversor; +import org.jsoup.select.NodeVisitor; + import java.text.DateFormat; import java.text.SimpleDateFormat; import java.util.ArrayList; @@ -47,13 +57,6 @@ import java.util.Locale; import java.util.TimeZone; import java.util.regex.Matcher; import java.util.regex.Pattern; -import org.jsoup.Jsoup; -import org.jsoup.nodes.Document; -import org.jsoup.nodes.Element; -import org.jsoup.nodes.Node; -import org.jsoup.select.Elements; -import org.jsoup.select.NodeTraversor; -import org.jsoup.select.NodeVisitor; public class GalleryDetailParser { @@ -74,16 +77,15 @@ public class GalleryDetailParser { private static final GalleryCommentList EMPTY_GALLERY_COMMENT_ARRAY = new GalleryCommentList(new GalleryComment[0], false); private static final DateFormat WEB_COMMENT_DATE_FORMAT = new SimpleDateFormat("dd MMMMM yyyy, HH:mm z", Locale.US); - - static { - WEB_COMMENT_DATE_FORMAT.setTimeZone(TimeZone.getTimeZone("UTC")); - } - private static final String OFFENSIVE_STRING = "

(And if you choose to ignore this warning, you lose all rights to complain about it in the future.)

"; private static final String PINING_STRING = "

This gallery is pining for the fjords.

"; + static { + WEB_COMMENT_DATE_FORMAT.setTimeZone(TimeZone.getTimeZone("UTC")); + } + public static GalleryDetail parse(String body) throws EhException { if (body.contains(OFFENSIVE_STRING)) { throw new OffensiveException(); @@ -491,7 +493,8 @@ public class GalleryDetailParser { } @Override - public void tail(Node node, int depth) { } + public void tail(Node node, int depth) { + } }, chd); return new GalleryCommentList(list.toArray(new GalleryComment[list.size()]), hasMore.value); @@ -663,8 +666,8 @@ public class GalleryDetailParser { continue; } String imageUrl = ParserUtils.trim(m.group(3)); - int xOffset = ParserUtils.parseInt(m.group(4), 0); - int yOffset = 0; + int xOffset = ParserUtils.parseInt(m.group(4), 0); + int yOffset = 0; int width = ParserUtils.parseInt(m.group(1), 0); if (width <= 0) { continue; diff --git a/app/src/main/java/com/hippo/ehviewer/client/parser/GalleryDetailUrlParser.java b/app/src/main/java/com/hippo/ehviewer/client/parser/GalleryDetailUrlParser.java index 8bd928963..a7d611e6c 100644 --- a/app/src/main/java/com/hippo/ehviewer/client/parser/GalleryDetailUrlParser.java +++ b/app/src/main/java/com/hippo/ehviewer/client/parser/GalleryDetailUrlParser.java @@ -17,14 +17,15 @@ package com.hippo.ehviewer.client.parser; import androidx.annotation.Nullable; + import com.hippo.ehviewer.client.EhUrl; import com.hippo.yorozuya.NumberUtils; + import java.util.regex.Matcher; import java.util.regex.Pattern; /** * Like http://exhentai.org/g/1234567/a1b2c3d4e5
- * */ public final class GalleryDetailUrlParser { diff --git a/app/src/main/java/com/hippo/ehviewer/client/parser/GalleryListParser.java b/app/src/main/java/com/hippo/ehviewer/client/parser/GalleryListParser.java index 0a4e5d12c..4d16ee8d5 100644 --- a/app/src/main/java/com/hippo/ehviewer/client/parser/GalleryListParser.java +++ b/app/src/main/java/com/hippo/ehviewer/client/parser/GalleryListParser.java @@ -18,7 +18,9 @@ package com.hippo.ehviewer.client.parser; import android.text.TextUtils; import android.util.Log; + import androidx.annotation.NonNull; + import com.hippo.ehviewer.EhDB; import com.hippo.ehviewer.client.EhUtils; import com.hippo.ehviewer.client.data.GalleryInfo; @@ -27,15 +29,17 @@ import com.hippo.ehviewer.client.exception.ParseException; import com.hippo.util.ExceptionUtils; import com.hippo.util.JsoupUtils; import com.hippo.yorozuya.NumberUtils; + +import org.jsoup.Jsoup; +import org.jsoup.nodes.Document; +import org.jsoup.nodes.Element; +import org.jsoup.select.Elements; + import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; -import org.jsoup.Jsoup; -import org.jsoup.nodes.Document; -import org.jsoup.nodes.Element; -import org.jsoup.select.Elements; public class GalleryListParser { @@ -47,26 +51,19 @@ public class GalleryListParser { private static final Pattern PATTERN_PAGES = Pattern.compile("(\\d+) page"); private static final Pattern PATTERN_NEXT_PAGE = Pattern.compile("page=(\\d+)"); - private static final String[][] FAVORITE_SLOT_RGB = new String[][] { - new String[] { "0", "0", "0"}, - new String[] { "240", "0", "0"}, - new String[] { "240", "160", "0"}, - new String[] { "208", "208", "0"}, - new String[] { "0", "128", "0"}, - new String[] { "144", "240", "64"}, - new String[] { "64", "176", "240"}, - new String[] { "0", "0", "240"}, - new String[] { "80", "0", "128"}, - new String[] { "224", "128", "224"}, + private static final String[][] FAVORITE_SLOT_RGB = new String[][]{ + new String[]{"0", "0", "0"}, + new String[]{"240", "0", "0"}, + new String[]{"240", "160", "0"}, + new String[]{"208", "208", "0"}, + new String[]{"0", "128", "0"}, + new String[]{"144", "240", "64"}, + new String[]{"64", "176", "240"}, + new String[]{"0", "0", "240"}, + new String[]{"80", "0", "128"}, + new String[]{"224", "128", "224"}, }; - public static class Result { - public int pages; - public int nextPage; - public boolean noWatchedTags; - public List galleryInfoList; - } - private static int parsePages(Document d, String body) throws ParseException { try { Elements es = d.getElementsByClass("ptt").first().child(0).child(0).children(); @@ -357,4 +354,11 @@ public class GalleryListParser { return result; } + + public static class Result { + public int pages; + public int nextPage; + public boolean noWatchedTags; + public List galleryInfoList; + } } diff --git a/app/src/main/java/com/hippo/ehviewer/client/parser/GalleryPageApiParser.java b/app/src/main/java/com/hippo/ehviewer/client/parser/GalleryPageApiParser.java index 6180f2267..c8b76cb0f 100644 --- a/app/src/main/java/com/hippo/ehviewer/client/parser/GalleryPageApiParser.java +++ b/app/src/main/java/com/hippo/ehviewer/client/parser/GalleryPageApiParser.java @@ -17,13 +17,16 @@ package com.hippo.ehviewer.client.parser; import android.text.TextUtils; + import com.hippo.ehviewer.client.exception.ParseException; import com.hippo.yorozuya.StringUtils; -import java.util.regex.Matcher; -import java.util.regex.Pattern; + import org.json.JSONException; import org.json.JSONObject; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + public class GalleryPageApiParser { private static final Pattern PATTERN_IMAGE_URL = Pattern.compile("]*src=\"([^\"]+)\" style"); diff --git a/app/src/main/java/com/hippo/ehviewer/client/parser/GalleryPageParser.java b/app/src/main/java/com/hippo/ehviewer/client/parser/GalleryPageParser.java index f612d5a03..957ea6cab 100644 --- a/app/src/main/java/com/hippo/ehviewer/client/parser/GalleryPageParser.java +++ b/app/src/main/java/com/hippo/ehviewer/client/parser/GalleryPageParser.java @@ -17,8 +17,10 @@ package com.hippo.ehviewer.client.parser; import android.text.TextUtils; + import com.hippo.ehviewer.client.exception.ParseException; import com.hippo.yorozuya.StringUtils; + import java.util.regex.Matcher; import java.util.regex.Pattern; diff --git a/app/src/main/java/com/hippo/ehviewer/client/parser/GalleryPageUrlParser.java b/app/src/main/java/com/hippo/ehviewer/client/parser/GalleryPageUrlParser.java index 0e746d7ce..dbdf8dc79 100644 --- a/app/src/main/java/com/hippo/ehviewer/client/parser/GalleryPageUrlParser.java +++ b/app/src/main/java/com/hippo/ehviewer/client/parser/GalleryPageUrlParser.java @@ -17,8 +17,10 @@ package com.hippo.ehviewer.client.parser; import androidx.annotation.Nullable; + import com.hippo.ehviewer.client.EhUrl; import com.hippo.yorozuya.NumberUtils; + import java.util.regex.Matcher; import java.util.regex.Pattern; diff --git a/app/src/main/java/com/hippo/ehviewer/client/parser/GalleryTokenApiParser.java b/app/src/main/java/com/hippo/ehviewer/client/parser/GalleryTokenApiParser.java index 774e988bf..4b0f8d19d 100644 --- a/app/src/main/java/com/hippo/ehviewer/client/parser/GalleryTokenApiParser.java +++ b/app/src/main/java/com/hippo/ehviewer/client/parser/GalleryTokenApiParser.java @@ -18,18 +18,19 @@ package com.hippo.ehviewer.client.parser; import com.hippo.ehviewer.client.exception.EhException; import com.hippo.util.ExceptionUtils; + import org.json.JSONObject; public class GalleryTokenApiParser { /** * { - * "tokenlist": [ - * { - * "gid":618395, - * "token":"0439fa3666" - * } - * ] + * "tokenlist": [ + * { + * "gid":618395, + * "token":"0439fa3666" + * } + * ] * } */ public static String parse(String body) throws Exception { diff --git a/app/src/main/java/com/hippo/ehviewer/client/parser/ProfileParser.java b/app/src/main/java/com/hippo/ehviewer/client/parser/ProfileParser.java index 37dc5661b..90a44d772 100644 --- a/app/src/main/java/com/hippo/ehviewer/client/parser/ProfileParser.java +++ b/app/src/main/java/com/hippo/ehviewer/client/parser/ProfileParser.java @@ -18,9 +18,11 @@ package com.hippo.ehviewer.client.parser; import android.text.TextUtils; import android.util.Log; + import com.hippo.ehviewer.client.EhUrl; import com.hippo.ehviewer.client.exception.ParseException; import com.hippo.util.ExceptionUtils; + import org.jsoup.Jsoup; import org.jsoup.nodes.Document; import org.jsoup.nodes.Element; diff --git a/app/src/main/java/com/hippo/ehviewer/client/parser/RateGalleryParser.java b/app/src/main/java/com/hippo/ehviewer/client/parser/RateGalleryParser.java index 7cd010d3b..ff0517865 100644 --- a/app/src/main/java/com/hippo/ehviewer/client/parser/RateGalleryParser.java +++ b/app/src/main/java/com/hippo/ehviewer/client/parser/RateGalleryParser.java @@ -23,22 +23,21 @@ import org.json.JSONObject; public class RateGalleryParser { - public static class Result { - public float rating; - public int ratingCount; - } - public static Result parse(String body) throws Exception { try { JSONObject jsonObject = new JSONObject(body); Result result = new Result(); - result.rating = (float)jsonObject.getDouble("rating_avg"); + result.rating = (float) jsonObject.getDouble("rating_avg"); result.ratingCount = jsonObject.getInt("rating_cnt"); return result; } catch (JSONException e) { - Exception exception = new ParseException("Can't parse rate gallery", body); - exception.initCause(e); + Exception exception = new ParseException("Can't parse rate gallery", body, e); throw exception; } } + + public static class Result { + public float rating; + public int ratingCount; + } } diff --git a/app/src/main/java/com/hippo/ehviewer/client/parser/VoteCommentParser.java b/app/src/main/java/com/hippo/ehviewer/client/parser/VoteCommentParser.java index acf7aa2d7..2884bc8f9 100644 --- a/app/src/main/java/com/hippo/ehviewer/client/parser/VoteCommentParser.java +++ b/app/src/main/java/com/hippo/ehviewer/client/parser/VoteCommentParser.java @@ -21,13 +21,6 @@ import org.json.JSONObject; public class VoteCommentParser { - public static class Result { - public long id; - public int score; - public int vote; - public int expectVote; - } - // {"comment_id":1253922,"comment_score":-19,"comment_vote":0} public static Result parse(String body, int vote) throws JSONException { Result result = new Result(); @@ -38,4 +31,11 @@ public class VoteCommentParser { result.expectVote = vote; return result; } + + public static class Result { + public long id; + public int score; + public int vote; + public int expectVote; + } } diff --git a/app/src/main/java/com/hippo/ehviewer/download/DownloadManager.java b/app/src/main/java/com/hippo/ehviewer/download/DownloadManager.java index d054ae2e6..213c95677 100644 --- a/app/src/main/java/com/hippo/ehviewer/download/DownloadManager.java +++ b/app/src/main/java/com/hippo/ehviewer/download/DownloadManager.java @@ -20,8 +20,10 @@ import android.annotation.SuppressLint; import android.content.Context; import android.os.AsyncTask; import android.util.Log; + import androidx.annotation.NonNull; import androidx.annotation.Nullable; + import com.hippo.ehviewer.EhDB; import com.hippo.ehviewer.client.data.GalleryInfo; import com.hippo.ehviewer.dao.DownloadInfo; @@ -39,6 +41,7 @@ import com.hippo.yorozuya.SimpleHandler; import com.hippo.yorozuya.collect.LongList; import com.hippo.yorozuya.collect.SparseIJArray; import com.hippo.yorozuya.collect.SparseJLArray; + import java.io.IOException; import java.util.ArrayList; import java.util.Collections; @@ -52,9 +55,13 @@ import java.util.Map; public class DownloadManager implements SpiderQueen.OnSpiderListener { private static final String TAG = DownloadManager.class.getSimpleName(); - + private static final Comparator DATE_DESC_COMPARATOR = new Comparator() { + @Override + public int compare(DownloadInfo lhs, DownloadInfo rhs) { + return lhs.time - rhs.time > 0 ? -1 : 1; + } + }; private final Context mContext; - // All download info list private final LinkedList mAllInfoList; // All download info map @@ -67,20 +74,16 @@ public class DownloadManager implements SpiderQueen.OnSpiderListener { private final LinkedList mDefaultInfoList; // Store download info wait to start private final LinkedList mWaitList; - private final SpeedReminder mSpeedReminder; - + private final List mDownloadInfoListeners; + private final ConcurrentPool mNotifyTaskPool = new ConcurrentPool<>(5); @Nullable private DownloadListener mDownloadListener; - private final List mDownloadInfoListeners; - @Nullable private DownloadInfo mCurrentTask; @Nullable private SpiderQueen mCurrentSpider; - private final ConcurrentPool mNotifyTaskPool = new ConcurrentPool<>(5); - public DownloadManager(Context context) { mContext = context; @@ -145,7 +148,7 @@ public class DownloadManager implements SpiderQueen.OnSpiderListener { return false; } - for (DownloadLabel raw: mLabelList) { + for (DownloadLabel raw : mLabelList) { if (label.equals(raw.getLabel())) { return true; } @@ -230,7 +233,7 @@ public class DownloadManager implements SpiderQueen.OnSpiderListener { // Notify state update List list = getInfoListForLabel(info.label); if (list != null) { - for (DownloadInfoListener l: mDownloadInfoListeners) { + for (DownloadInfoListener l : mDownloadInfoListeners) { l.onUpdate(info, list); } } @@ -256,7 +259,7 @@ public class DownloadManager implements SpiderQueen.OnSpiderListener { // Notify state update List list = getInfoListForLabel(info.label); if (list != null) { - for (DownloadInfoListener l: mDownloadInfoListeners) { + for (DownloadInfoListener l : mDownloadInfoListeners) { l.onUpdate(info, list); } } @@ -289,7 +292,7 @@ public class DownloadManager implements SpiderQueen.OnSpiderListener { EhDB.putDownloadInfo(info); // Notify - for (DownloadInfoListener l: mDownloadInfoListeners) { + for (DownloadInfoListener l : mDownloadInfoListeners) { l.onAdd(info, list, list.size() - 1); } // Make sure download is running @@ -326,7 +329,7 @@ public class DownloadManager implements SpiderQueen.OnSpiderListener { if (update) { // Notify Listener - for (DownloadInfoListener l: mDownloadInfoListeners) { + for (DownloadInfoListener l : mDownloadInfoListeners) { l.onUpdateAll(); } // Ensure download @@ -339,7 +342,7 @@ public class DownloadManager implements SpiderQueen.OnSpiderListener { // Start all STATE_NONE and STATE_FAILED item LinkedList allInfoList = mAllInfoList; LinkedList waitList = mWaitList; - for (DownloadInfo info: allInfoList) { + for (DownloadInfo info : allInfoList) { if (info.state == DownloadInfo.STATE_NONE || info.state == DownloadInfo.STATE_FAILED) { update = true; // Set state DownloadInfo.STATE_WAIT @@ -353,7 +356,7 @@ public class DownloadManager implements SpiderQueen.OnSpiderListener { if (update) { // Notify Listener - for (DownloadInfoListener l: mDownloadInfoListeners) { + for (DownloadInfoListener l : mDownloadInfoListeners) { l.onUpdateAll(); } // Ensure download @@ -362,7 +365,7 @@ public class DownloadManager implements SpiderQueen.OnSpiderListener { } public void addDownload(List downloadInfoList) { - for (DownloadInfo info: downloadInfoList) { + for (DownloadInfo info : downloadInfoList) { if (containDownloadInfo(info.gid)) { // Contain return; @@ -401,13 +404,13 @@ public class DownloadManager implements SpiderQueen.OnSpiderListener { Collections.sort(mAllInfoList, DATE_DESC_COMPARATOR); // Notify - for (DownloadInfoListener l: mDownloadInfoListeners) { + for (DownloadInfoListener l : mDownloadInfoListeners) { l.onReload(); } } public void addDownloadLabel(List downloadLabelList) { - for (DownloadLabel label: downloadLabelList) { + for (DownloadLabel label : downloadLabelList) { String labelString = label.getLabel(); if (!containLabel(labelString)) { mMap.put(labelString, new LinkedList()); @@ -444,19 +447,18 @@ public class DownloadManager implements SpiderQueen.OnSpiderListener { EhDB.putDownloadInfo(info); // Notify - for (DownloadInfoListener l: mDownloadInfoListeners) { + for (DownloadInfoListener l : mDownloadInfoListeners) { l.onAdd(info, list, list.size() - 1); } } - public void stopDownload(long gid) { DownloadInfo info = stopDownloadInternal(gid); if (info != null) { // Update listener List list = getInfoListForLabel(info.label); if (list != null) { - for (DownloadInfoListener l: mDownloadInfoListeners) { + for (DownloadInfoListener l : mDownloadInfoListeners) { l.onUpdate(info, list); } } @@ -471,7 +473,7 @@ public class DownloadManager implements SpiderQueen.OnSpiderListener { // Update listener List list = getInfoListForLabel(info.label); if (list != null) { - for (DownloadInfoListener l: mDownloadInfoListeners) { + for (DownloadInfoListener l : mDownloadInfoListeners) { l.onUpdate(info, list); } } @@ -484,7 +486,7 @@ public class DownloadManager implements SpiderQueen.OnSpiderListener { stopRangeDownloadInternal(gidList); // Update listener - for (DownloadInfoListener l: mDownloadInfoListeners) { + for (DownloadInfoListener l : mDownloadInfoListeners) { l.onUpdateAll(); } @@ -505,7 +507,7 @@ public class DownloadManager implements SpiderQueen.OnSpiderListener { stopCurrentDownloadInternal(); // Notify mDownloadInfoListener - for (DownloadInfoListener l: mDownloadInfoListeners) { + for (DownloadInfoListener l : mDownloadInfoListeners) { l.onUpdateAll(); } } @@ -528,7 +530,7 @@ public class DownloadManager implements SpiderQueen.OnSpiderListener { if (index >= 0) { list.remove(info); // Update listener - for (DownloadInfoListener l: mDownloadInfoListeners) { + for (DownloadInfoListener l : mDownloadInfoListeners) { l.onRemove(info, list, index); } } @@ -565,7 +567,7 @@ public class DownloadManager implements SpiderQueen.OnSpiderListener { } // Update listener - for (DownloadInfoListener l: mDownloadInfoListeners) { + for (DownloadInfoListener l : mDownloadInfoListeners) { l.onReload(); } @@ -626,7 +628,7 @@ public class DownloadManager implements SpiderQueen.OnSpiderListener { return stopCurrentDownloadInternal(); } - for (Iterator iterator = mWaitList.iterator(); iterator.hasNext();) { + for (Iterator iterator = mWaitList.iterator(); iterator.hasNext(); ) { DownloadInfo info = iterator.next(); if (info.gid == gid) { // Remove from wait list @@ -686,7 +688,7 @@ public class DownloadManager implements SpiderQueen.OnSpiderListener { } // Check all in wait list - for (Iterator iterator = mWaitList.iterator(); iterator.hasNext();) { + for (Iterator iterator = mWaitList.iterator(); iterator.hasNext(); ) { DownloadInfo info = iterator.next(); if (gidList.contains(info.gid)) { // Remove from wait list @@ -715,7 +717,7 @@ public class DownloadManager implements SpiderQueen.OnSpiderListener { return; } - for (DownloadInfo info: list) { + for (DownloadInfo info : list) { if (ObjectUtils.equal(info.label, label)) { continue; } @@ -735,7 +737,7 @@ public class DownloadManager implements SpiderQueen.OnSpiderListener { EhDB.putDownloadInfo(info); } - for (DownloadInfoListener l: mDownloadInfoListeners) { + for (DownloadInfoListener l : mDownloadInfoListeners) { l.onReload(); } } @@ -748,7 +750,7 @@ public class DownloadManager implements SpiderQueen.OnSpiderListener { mLabelList.add(EhDB.addDownloadLabel(label)); mMap.put(label, new LinkedList()); - for (DownloadInfoListener l: mDownloadInfoListeners) { + for (DownloadInfoListener l : mDownloadInfoListeners) { l.onUpdateLabels(); } } @@ -758,7 +760,7 @@ public class DownloadManager implements SpiderQueen.OnSpiderListener { mLabelList.add(toPosition, item); EhDB.moveDownloadLabel(fromPosition, toPosition); - for (DownloadInfoListener l: mDownloadInfoListeners) { + for (DownloadInfoListener l : mDownloadInfoListeners) { l.onUpdateLabels(); } } @@ -766,7 +768,7 @@ public class DownloadManager implements SpiderQueen.OnSpiderListener { public void renameLabel(@NonNull String from, @NonNull String to) { // Find in label list boolean found = false; - for (DownloadLabel raw: mLabelList) { + for (DownloadLabel raw : mLabelList) { if (from.equals(raw.getLabel())) { found = true; raw.setLabel(to); @@ -785,7 +787,7 @@ public class DownloadManager implements SpiderQueen.OnSpiderListener { } // Update info label - for (DownloadInfo info: list) { + for (DownloadInfo info : list) { info.label = to; // Update in DB EhDB.putDownloadInfo(info); @@ -794,7 +796,7 @@ public class DownloadManager implements SpiderQueen.OnSpiderListener { mMap.put(to, list); // Notify listener - for (DownloadInfoListener l: mDownloadInfoListeners) { + for (DownloadInfoListener l : mDownloadInfoListeners) { l.onRenameLabel(from, to); } } @@ -802,7 +804,7 @@ public class DownloadManager implements SpiderQueen.OnSpiderListener { public void deleteLabel(@NonNull String label) { // Find in label list and remove boolean found = false; - for (Iterator iterator = mLabelList.iterator(); iterator.hasNext();) { + for (Iterator iterator = mLabelList.iterator(); iterator.hasNext(); ) { DownloadLabel raw = iterator.next(); if (label.equals(raw.getLabel())) { found = true; @@ -821,7 +823,7 @@ public class DownloadManager implements SpiderQueen.OnSpiderListener { } // Update info label - for (DownloadInfo info: list) { + for (DownloadInfo info : list) { info.label = null; // Update in DB EhDB.putDownloadInfo(info); @@ -832,7 +834,7 @@ public class DownloadManager implements SpiderQueen.OnSpiderListener { Collections.sort(mDefaultInfoList, DATE_DESC_COMPARATOR); // Notify listener - for (DownloadInfoListener l: mDownloadInfoListeners) { + for (DownloadInfoListener l : mDownloadInfoListeners) { l.onChange(); } } @@ -911,6 +913,80 @@ public class DownloadManager implements SpiderQueen.OnSpiderListener { // Ignore } + + public interface DownloadInfoListener { + + /** + * Add the special info to the special position + */ + void onAdd(@NonNull DownloadInfo info, @NonNull List list, int position); + + /** + * The special info is changed + */ + void onUpdate(@NonNull DownloadInfo info, @NonNull List list); + + /** + * Maybe all data is changed, but size is the same + */ + void onUpdateAll(); + + /** + * Maybe all data is changed, maybe list is changed + */ + void onReload(); + + /** + * The list is gone, use default list please + */ + void onChange(); + + /** + * Rename label + */ + void onRenameLabel(String from, String to); + + /** + * Remove the special info from the special position + */ + void onRemove(@NonNull DownloadInfo info, @NonNull List list, int position); + + void onUpdateLabels(); + } + + public interface DownloadListener { + + /** + * Get 509 error + */ + void onGet509(); + + /** + * Start download + */ + void onStart(DownloadInfo info); + + /** + * Update download speed + */ + void onDownload(DownloadInfo info); + + /** + * Update page downloaded + */ + void onGetPage(DownloadInfo info); + + /** + * Download done + */ + void onFinish(DownloadInfo info); + + /** + * Download done + */ + void onCancel(DownloadInfo info); + } + private class NotifyTask implements Runnable { public static final int TYPE_ON_GET_PAGES = 0; @@ -985,7 +1061,7 @@ public class DownloadManager implements SpiderQueen.OnSpiderListener { info.total = mPages; List list = getInfoListForLabel(info.label); if (list != null) { - for (DownloadInfoListener l: mDownloadInfoListeners) { + for (DownloadInfoListener l : mDownloadInfoListeners) { l.onUpdate(info, list); } } @@ -1016,7 +1092,7 @@ public class DownloadManager implements SpiderQueen.OnSpiderListener { } List list = getInfoListForLabel(info.label); if (list != null) { - for (DownloadInfoListener l: mDownloadInfoListeners) { + for (DownloadInfoListener l : mDownloadInfoListeners) { l.onUpdate(info, list); } } @@ -1034,7 +1110,7 @@ public class DownloadManager implements SpiderQueen.OnSpiderListener { info.total = mTotal; List list = getInfoListForLabel(info.label); if (list != null) { - for (DownloadInfoListener l: mDownloadInfoListeners) { + for (DownloadInfoListener l : mDownloadInfoListeners) { l.onUpdate(info, list); } } @@ -1078,7 +1154,7 @@ public class DownloadManager implements SpiderQueen.OnSpiderListener { } List list = getInfoListForLabel(info.label); if (list != null) { - for (DownloadInfoListener l: mDownloadInfoListeners) { + for (DownloadInfoListener l : mDownloadInfoListeners) { l.onUpdate(info, list); } } @@ -1092,16 +1168,13 @@ public class DownloadManager implements SpiderQueen.OnSpiderListener { } } - class SpeedReminder implements Runnable { - private boolean mStop = true; - - private long mBytesRead; - private long oldSpeed = -1; - private final SparseIJArray mContentLengthMap = new SparseIJArray(); private final SparseIJArray mReceivedSizeMap = new SparseIJArray(); + private boolean mStop = true; + private long mBytesRead; + private long oldSpeed = -1; public void start() { if (mStop) { @@ -1174,7 +1247,7 @@ public class DownloadManager implements SpiderQueen.OnSpiderListener { } List list = getInfoListForLabel(info.label); if (list != null) { - for (DownloadInfoListener l: mDownloadInfoListeners) { + for (DownloadInfoListener l : mDownloadInfoListeners) { l.onUpdate(info, list); } } @@ -1187,84 +1260,4 @@ public class DownloadManager implements SpiderQueen.OnSpiderListener { } } } - - private static final Comparator DATE_DESC_COMPARATOR = new Comparator() { - @Override - public int compare(DownloadInfo lhs, DownloadInfo rhs) { - return lhs.time - rhs.time > 0 ? -1 : 1; - } - }; - - public interface DownloadInfoListener { - - /** - * Add the special info to the special position - */ - void onAdd(@NonNull DownloadInfo info, @NonNull List list, int position); - - /** - * The special info is changed - */ - void onUpdate(@NonNull DownloadInfo info, @NonNull List list); - - /** - * Maybe all data is changed, but size is the same - */ - void onUpdateAll(); - - /** - * Maybe all data is changed, maybe list is changed - */ - void onReload(); - - /** - * The list is gone, use default list please - */ - void onChange(); - - /** - * Rename label - */ - void onRenameLabel(String from, String to); - - /** - * Remove the special info from the special position - */ - void onRemove(@NonNull DownloadInfo info, @NonNull List list, int position); - - void onUpdateLabels(); - } - - public interface DownloadListener { - - /** - * Get 509 error - */ - void onGet509(); - - /** - * Start download - */ - void onStart(DownloadInfo info); - - /** - * Update download speed - */ - void onDownload(DownloadInfo info); - - /** - * Update page downloaded - */ - void onGetPage(DownloadInfo info); - - /** - * Download done - */ - void onFinish(DownloadInfo info); - - /** - * Download done - */ - void onCancel(DownloadInfo info); - } } diff --git a/app/src/main/java/com/hippo/ehviewer/download/DownloadService.java b/app/src/main/java/com/hippo/ehviewer/download/DownloadService.java index 63e83ff31..5ed29f7de 100644 --- a/app/src/main/java/com/hippo/ehviewer/download/DownloadService.java +++ b/app/src/main/java/com/hippo/ehviewer/download/DownloadService.java @@ -28,9 +28,11 @@ import android.os.Bundle; import android.os.IBinder; import android.os.SystemClock; import android.util.Log; + import androidx.annotation.IntDef; import androidx.annotation.Nullable; import androidx.core.app.NotificationCompat; + import com.hippo.ehviewer.EhApplication; import com.hippo.ehviewer.R; import com.hippo.ehviewer.client.EhUtils; @@ -45,6 +47,7 @@ import com.hippo.yorozuya.SimpleHandler; import com.hippo.yorozuya.collect.LongList; import com.hippo.yorozuya.collect.SparseJBArray; import com.hippo.yorozuya.collect.SparseJLArray; + import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -70,7 +73,11 @@ public class DownloadService extends Service implements DownloadManager.Download private static final int ID_DOWNLOADING = 1; private static final int ID_DOWNLOADED = 2; private static final int ID_509 = 3; - + private final static SparseJBArray sItemStateArray = new SparseJBArray(); + private final static SparseJLArray sItemTitleArray = new SparseJLArray<>(); + private static int sFailedCount; + private static int sFinishedCount; + private static int sDownloadedCount; @Nullable private NotificationManager mNotifyManager; @Nullable @@ -81,15 +88,6 @@ public class DownloadService extends Service implements DownloadManager.Download private NotificationDelay mDownloadingDelay; private NotificationDelay mDownloadedDelay; private NotificationDelay m509Delay; - - - private final static SparseJBArray sItemStateArray = new SparseJBArray(); - private final static SparseJLArray sItemTitleArray = new SparseJLArray<>(); - - private static int sFailedCount; - private static int sFinishedCount; - private static int sDownloadedCount; - private String CHANNEL_ID; public static void clear() { @@ -104,9 +102,9 @@ public class DownloadService extends Service implements DownloadManager.Download public void onCreate() { super.onCreate(); - CHANNEL_ID = getPackageName()+".download"; + CHANNEL_ID = getPackageName() + ".download"; mNotifyManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O){ + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { mNotifyManager.createNotificationChannel(new NotificationChannel(CHANNEL_ID, getString(R.string.download_service), NotificationManager.IMPORTANCE_LOW)); } @@ -433,7 +431,7 @@ public class DownloadService extends Service implements DownloadManager.Download continue; } style.addLine(getString(fin ? R.string.stat_download_done_line_succeeded : - R.string.stat_download_done_line_failed, title)); + R.string.stat_download_done_line_failed, title)); } } else { style = null; @@ -473,29 +471,21 @@ public class DownloadService extends Service implements DownloadManager.Download // Avoid frequent notification private static class NotificationDelay implements Runnable { - @IntDef({OPS_NOTIFY, OPS_CANCEL, OPS_START_FOREGROUND}) - @Retention(RetentionPolicy.SOURCE) - private @interface Ops {} - private static final int OPS_NOTIFY = 0; private static final int OPS_CANCEL = 1; private static final int OPS_START_FOREGROUND = 2; - private static final long DELAY = 1000; // 1s - - private Service mService; private final NotificationManager mNotifyManager; private final NotificationCompat.Builder mBuilder; private final int mId; - + private Service mService; private long mLastTime; private boolean mPosted; // false for show, true for cancel @Ops private int mOps; - public NotificationDelay(Service service, NotificationManager notifyManager, - NotificationCompat.Builder builder, int id) { + NotificationCompat.Builder builder, int id) { mService = service; mNotifyManager = notifyManager; mBuilder = builder; @@ -577,5 +567,10 @@ public class DownloadService extends Service implements DownloadManager.Download break; } } + + @IntDef({OPS_NOTIFY, OPS_CANCEL, OPS_START_FOREGROUND}) + @Retention(RetentionPolicy.SOURCE) + private @interface Ops { + } } } diff --git a/app/src/main/java/com/hippo/ehviewer/gallery/A7ZipArchive.java b/app/src/main/java/com/hippo/ehviewer/gallery/A7ZipArchive.java index 72529c135..7fafdc14e 100644 --- a/app/src/main/java/com/hippo/ehviewer/gallery/A7ZipArchive.java +++ b/app/src/main/java/com/hippo/ehviewer/gallery/A7ZipArchive.java @@ -23,6 +23,7 @@ import com.hippo.a7zip.PropID; import com.hippo.a7zip.PropType; import com.hippo.a7zip.SequentialOutStream; import com.hippo.unifile.UniRandomAccessFile; + import java.io.Closeable; import java.io.IOException; import java.io.OutputStream; @@ -31,126 +32,126 @@ import java.util.List; class A7ZipArchive implements Closeable { - private InArchive archive; - - private A7ZipArchive(InArchive archive) { - this.archive = archive; - } - - @Override - public void close() { - archive.close(); - } - - private static boolean isSupportedFilename(String name) { - for (String extension : GalleryProvider2.SUPPORT_IMAGE_EXTENSIONS) { - if (name.endsWith(extension)) { - return true; - } - } - return false; - } - - List getArchiveEntries() { - List entries = new ArrayList<>(); - - for (int i = 0, n = archive.getNumberOfEntries(); i < n; i++) { - if (!archive.getEntryBooleanProperty(i, PropID.ENCRYPTED) - && !archive.getEntryBooleanProperty(i, PropID.IS_DIR) - && !archive.getEntryBooleanProperty(i, PropID.IS_VOLUME) - && !archive.getEntryBooleanProperty(i, PropID.SOLID)) { - String path = archive.getEntryPath(i); - if (isSupportedFilename(path.toLowerCase())) { - entries.add(new A7ZipArchiveEntry(archive, i, path)); - } - } - } - - return entries; - } - - static A7ZipArchive create(UniRandomAccessFile file) throws ArchiveException { - InStream store = new UniRandomAccessFileInStream(file); - InArchive archive = InArchive.open(store); - if ((archive.getArchivePropertyType(PropID.ENCRYPTED) == PropType.BOOL && archive.getArchiveBooleanProperty(PropID.ENCRYPTED)) - || (archive.getArchivePropertyType(PropID.SOLID) == PropType.BOOL && archive.getArchiveBooleanProperty(PropID.SOLID)) - || (archive.getArchivePropertyType(PropID.IS_VOLUME) == PropType.BOOL && archive.getArchiveBooleanProperty(PropID.IS_VOLUME))) { - throw new ArchiveException("Unsupported archive"); - } - return new A7ZipArchive(archive); - } - - static class A7ZipArchiveEntry { - private InArchive archive; - private int index; - private String path; - private A7ZipArchiveEntry(InArchive archive, int index, String path) { - this.archive = archive; - this.index = index; - this.path = path; + private A7ZipArchive(InArchive archive) { + this.archive = archive; } - String getPath() { - return path; + private static boolean isSupportedFilename(String name) { + for (String extension : GalleryProvider2.SUPPORT_IMAGE_EXTENSIONS) { + if (name.endsWith(extension)) { + return true; + } + } + return false; } - void extract(OutputStream os) throws ArchiveException { - archive.extractEntry(index, new OutputStreamSequentialOutStream(os)); - } - } - - private static class UniRandomAccessFileInStream implements InStream { - - private UniRandomAccessFile file; - - public UniRandomAccessFileInStream(UniRandomAccessFile file) { - this.file = file; + static A7ZipArchive create(UniRandomAccessFile file) throws ArchiveException { + InStream store = new UniRandomAccessFileInStream(file); + InArchive archive = InArchive.open(store); + if ((archive.getArchivePropertyType(PropID.ENCRYPTED) == PropType.BOOL && archive.getArchiveBooleanProperty(PropID.ENCRYPTED)) + || (archive.getArchivePropertyType(PropID.SOLID) == PropType.BOOL && archive.getArchiveBooleanProperty(PropID.SOLID)) + || (archive.getArchivePropertyType(PropID.IS_VOLUME) == PropType.BOOL && archive.getArchiveBooleanProperty(PropID.IS_VOLUME))) { + throw new ArchiveException("Unsupported archive"); + } + return new A7ZipArchive(archive); } @Override - public void seek(long pos) throws IOException { - file.seek(pos); + public void close() { + archive.close(); } - @Override - public long tell() throws IOException { - return file.getFilePointer(); + List getArchiveEntries() { + List entries = new ArrayList<>(); + + for (int i = 0, n = archive.getNumberOfEntries(); i < n; i++) { + if (!archive.getEntryBooleanProperty(i, PropID.ENCRYPTED) + && !archive.getEntryBooleanProperty(i, PropID.IS_DIR) + && !archive.getEntryBooleanProperty(i, PropID.IS_VOLUME) + && !archive.getEntryBooleanProperty(i, PropID.SOLID)) { + String path = archive.getEntryPath(i); + if (isSupportedFilename(path.toLowerCase())) { + entries.add(new A7ZipArchiveEntry(archive, i, path)); + } + } + } + + return entries; } - @Override - public long size() throws IOException { - return file.length(); + static class A7ZipArchiveEntry { + + private InArchive archive; + private int index; + private String path; + + private A7ZipArchiveEntry(InArchive archive, int index, String path) { + this.archive = archive; + this.index = index; + this.path = path; + } + + String getPath() { + return path; + } + + void extract(OutputStream os) throws ArchiveException { + archive.extractEntry(index, new OutputStreamSequentialOutStream(os)); + } } - @Override - public int read(byte[] b, int off, int len) throws IOException { - return file.read(b, off, len); + private static class UniRandomAccessFileInStream implements InStream { + + private UniRandomAccessFile file; + + public UniRandomAccessFileInStream(UniRandomAccessFile file) { + this.file = file; + } + + @Override + public void seek(long pos) throws IOException { + file.seek(pos); + } + + @Override + public long tell() throws IOException { + return file.getFilePointer(); + } + + @Override + public long size() throws IOException { + return file.length(); + } + + @Override + public int read(byte[] b, int off, int len) throws IOException { + return file.read(b, off, len); + } + + @Override + public void close() throws IOException { + file.close(); + } } - @Override - public void close() throws IOException { - file.close(); + private static class OutputStreamSequentialOutStream implements SequentialOutStream { + + private OutputStream stream; + + public OutputStreamSequentialOutStream(OutputStream stream) { + this.stream = stream; + } + + @Override + public void write(byte[] b, int off, int len) throws IOException { + stream.write(b, off, len); + } + + @Override + public void close() throws IOException { + stream.close(); + } } - } - - private static class OutputStreamSequentialOutStream implements SequentialOutStream { - - private OutputStream stream; - - public OutputStreamSequentialOutStream(OutputStream stream) { - this.stream = stream; - } - - @Override - public void write(byte[] b, int off, int len) throws IOException { - stream.write(b, off, len); - } - - @Override - public void close() throws IOException { - stream.close(); - } - } } diff --git a/app/src/main/java/com/hippo/ehviewer/gallery/ArchiveGalleryProvider.java b/app/src/main/java/com/hippo/ehviewer/gallery/ArchiveGalleryProvider.java index 55c4ff217..e6eb5845b 100644 --- a/app/src/main/java/com/hippo/ehviewer/gallery/ArchiveGalleryProvider.java +++ b/app/src/main/java/com/hippo/ehviewer/gallery/ArchiveGalleryProvider.java @@ -19,8 +19,10 @@ package com.hippo.ehviewer.gallery; import android.content.Context; import android.net.Uri; import android.os.Process; + import androidx.annotation.NonNull; import androidx.annotation.Nullable; + import com.hippo.a7zip.ArchiveException; import com.hippo.ehviewer.GetText; import com.hippo.ehviewer.R; @@ -30,6 +32,7 @@ import com.hippo.unifile.UniFile; import com.hippo.unifile.UniRandomAccessFile; import com.hippo.util.NaturalComparator; import com.hippo.yorozuya.thread.PriorityThread; + import java.io.IOException; import java.io.InputStream; import java.util.Collections; @@ -43,239 +46,235 @@ import java.util.concurrent.atomic.AtomicInteger; public class ArchiveGalleryProvider extends GalleryProvider2 { - private static final AtomicInteger sIdGenerator = new AtomicInteger(); + private static final AtomicInteger sIdGenerator = new AtomicInteger(); + private static Comparator naturalComparator = new Comparator() { + private NaturalComparator comparator = new NaturalComparator(); - private final UniFile file; - - private Thread archiveThread; - private Thread decodeThread; - - private volatile int size = STATE_WAIT; - private String error; - - private final Stack requests = new Stack<>(); - private final AtomicInteger extractingIndex = new AtomicInteger(GalleryPageView.INVALID_INDEX); - private final LinkedHashMap streams = new LinkedHashMap<>(); - private final AtomicInteger decodingIndex = new AtomicInteger(GalleryPageView.INVALID_INDEX); - - public ArchiveGalleryProvider(Context context, Uri uri) { - file = UniFile.fromUri(context, uri); - } - - @Override - public void start() { - super.start(); - - int id = sIdGenerator.incrementAndGet(); - - archiveThread = new PriorityThread( - new ArchiveTask(), "ArchiveTask" + '-' + id, Process.THREAD_PRIORITY_BACKGROUND); - archiveThread.start(); - - decodeThread = new PriorityThread( - new DecodeTask(), "DecodeTask" + '-' + id, Process.THREAD_PRIORITY_BACKGROUND); - decodeThread.start(); - } - - @Override - public void stop() { - super.stop(); - - if (archiveThread != null) { - archiveThread.interrupt(); - archiveThread = null; - } - if (decodeThread != null) { - decodeThread.interrupt(); - decodeThread = null; - } - } - - @Override - public int size() { - return size; - } - - @Override - protected void onRequest(int index) { - boolean inDecodeTask; - synchronized (streams) { - inDecodeTask = streams.keySet().contains(index) || index == decodingIndex.get(); - } - - synchronized (requests) { - boolean inArchiveTask = requests.contains(index) || index == extractingIndex.get(); - if (!inArchiveTask && !inDecodeTask) { - requests.add(index); - requests.notify(); - } - } - notifyPageWait(index); - } - - @Override - protected void onForceRequest(int index) { - onRequest(index); - } - - @Override - protected void onCancelRequest(int index) { - synchronized (requests) { - requests.remove(Integer.valueOf(index)); - } - } - - @Override - public String getError() { - return error; - } - - @NonNull - @Override - public String getImageFilename(int index) { - // TODO - return Integer.toString(index); - } - - @Override - public boolean save(int index, @NonNull UniFile file) { - // TODO - return false; - } - - @Nullable - @Override - public UniFile save(int index, @NonNull UniFile dir, @NonNull String filename) { - // TODO - return null; - } - - private class ArchiveTask implements Runnable { - @Override - public void run() { - UniRandomAccessFile uraf = null; - if (file != null) { - try { - uraf = file.createRandomAccessFile("r"); - } catch (IOException e) { - e.printStackTrace(); + @Override + public int compare(A7ZipArchive.A7ZipArchiveEntry o1, A7ZipArchive.A7ZipArchiveEntry o2) { + return comparator.compare(o1.getPath(), o2.getPath()); } - } - if (uraf == null) { - size = STATE_ERROR; - error = GetText.getString(R.string.error_reading_failed); - notifyDataChanged(); - return; - } + }; + private final UniFile file; + private final Stack requests = new Stack<>(); + private final AtomicInteger extractingIndex = new AtomicInteger(GalleryPageView.INVALID_INDEX); + private final LinkedHashMap streams = new LinkedHashMap<>(); + private final AtomicInteger decodingIndex = new AtomicInteger(GalleryPageView.INVALID_INDEX); + private Thread archiveThread; + private Thread decodeThread; + private volatile int size = STATE_WAIT; + private String error; - A7ZipArchive archive = null; - try { - archive = A7ZipArchive.create(uraf); - } catch (ArchiveException e) { - e.printStackTrace(); - } - if (archive == null) { - size = STATE_ERROR; - error = GetText.getString(R.string.error_invalid_archive); - notifyDataChanged(); - return; - } + public ArchiveGalleryProvider(Context context, Uri uri) { + file = UniFile.fromUri(context, uri); + } - List entries = archive.getArchiveEntries(); - Collections.sort(entries, naturalComparator); + @Override + public void start() { + super.start(); - // Update size and notify changed - size = entries.size(); - notifyDataChanged(); + int id = sIdGenerator.incrementAndGet(); + + archiveThread = new PriorityThread( + new ArchiveTask(), "ArchiveTask" + '-' + id, Process.THREAD_PRIORITY_BACKGROUND); + archiveThread.start(); + + decodeThread = new PriorityThread( + new DecodeTask(), "DecodeTask" + '-' + id, Process.THREAD_PRIORITY_BACKGROUND); + decodeThread.start(); + } + + @Override + public void stop() { + super.stop(); + + if (archiveThread != null) { + archiveThread.interrupt(); + archiveThread = null; + } + if (decodeThread != null) { + decodeThread.interrupt(); + decodeThread = null; + } + } + + @Override + public int size() { + return size; + } + + @Override + protected void onRequest(int index) { + boolean inDecodeTask; + synchronized (streams) { + inDecodeTask = streams.containsKey(index) || index == decodingIndex.get(); + } - while (!Thread.currentThread().isInterrupted()) { - int index; synchronized (requests) { - if (requests.isEmpty()) { - try { - requests.wait(); - } catch (InterruptedException e) { - // Interrupted - break; + boolean inArchiveTask = requests.contains(index) || index == extractingIndex.get(); + if (!inArchiveTask && !inDecodeTask) { + requests.add(index); + requests.notify(); } - continue; - } - index = requests.pop(); - extractingIndex.lazySet(index); } - - // Check index valid - if (index < 0 || index >= entries.size()) { - extractingIndex.lazySet(GalleryPageView.INVALID_INDEX); - notifyPageFailed(index, GetText.getString(R.string.error_out_of_range)); - continue; - } - - Pipe pipe = new Pipe(4 * 1024); - - synchronized (streams) { - if (streams.get(index) != null) { - continue; - } - streams.put(index, pipe.getInputStream()); - streams.notify(); - } - - try { - entries.get(index).extract(pipe.getOutputStream()); - } catch (ArchiveException e) { - e.printStackTrace(); - } finally { - extractingIndex.lazySet(GalleryPageView.INVALID_INDEX); - } - } + notifyPageWait(index); } - } - private class DecodeTask implements Runnable { @Override - public void run() { - while (!Thread.currentThread().isInterrupted()) { - int index; - InputStream stream; - synchronized (streams) { - if (streams.isEmpty()) { - try { - streams.wait(); - } catch (InterruptedException e) { - // Interrupted - break; + protected void onForceRequest(int index) { + onRequest(index); + } + + @Override + protected void onCancelRequest(int index) { + synchronized (requests) { + requests.remove(Integer.valueOf(index)); + } + } + + @Override + public String getError() { + return error; + } + + @NonNull + @Override + public String getImageFilename(int index) { + // TODO + return Integer.toString(index); + } + + @Override + public boolean save(int index, @NonNull UniFile file) { + // TODO + return false; + } + + @Nullable + @Override + public UniFile save(int index, @NonNull UniFile dir, @NonNull String filename) { + // TODO + return null; + } + + private class ArchiveTask implements Runnable { + @Override + public void run() { + UniRandomAccessFile uraf = null; + if (file != null) { + try { + uraf = file.createRandomAccessFile("r"); + } catch (IOException e) { + e.printStackTrace(); + } + } + if (uraf == null) { + size = STATE_ERROR; + error = GetText.getString(R.string.error_reading_failed); + notifyDataChanged(); + return; } - continue; - } - Iterator> iterator = streams.entrySet().iterator(); - Map.Entry entry = iterator.next(); - iterator.remove(); - index = entry.getKey(); - stream = entry.getValue(); - decodingIndex.lazySet(index); + A7ZipArchive archive = null; + try { + archive = A7ZipArchive.create(uraf); + } catch (ArchiveException e) { + e.printStackTrace(); + } + if (archive == null) { + size = STATE_ERROR; + error = GetText.getString(R.string.error_invalid_archive); + notifyDataChanged(); + return; + } + + List entries = archive.getArchiveEntries(); + Collections.sort(entries, naturalComparator); + + // Update size and notify changed + size = entries.size(); + notifyDataChanged(); + + while (!Thread.currentThread().isInterrupted()) { + int index; + synchronized (requests) { + if (requests.isEmpty()) { + try { + requests.wait(); + } catch (InterruptedException e) { + // Interrupted + break; + } + continue; + } + index = requests.pop(); + extractingIndex.lazySet(index); + } + + // Check index valid + if (index < 0 || index >= entries.size()) { + extractingIndex.lazySet(GalleryPageView.INVALID_INDEX); + notifyPageFailed(index, GetText.getString(R.string.error_out_of_range)); + continue; + } + + Pipe pipe = new Pipe(4 * 1024); + + synchronized (streams) { + if (streams.get(index) != null) { + continue; + } + streams.put(index, pipe.getInputStream()); + streams.notify(); + } + + try { + entries.get(index).extract(pipe.getOutputStream()); + } catch (ArchiveException e) { + e.printStackTrace(); + } finally { + extractingIndex.lazySet(GalleryPageView.INVALID_INDEX); + } + } } + } - try { - Image image = Image.decode(stream, true); - if (image != null) { - notifyPageSucceed(index, image); - } else { - notifyPageFailed(index, GetText.getString(R.string.error_decoding_failed)); - } - } finally { - decodingIndex.lazySet(GalleryPageView.INVALID_INDEX); + private class DecodeTask implements Runnable { + @Override + public void run() { + while (!Thread.currentThread().isInterrupted()) { + int index; + InputStream stream; + synchronized (streams) { + if (streams.isEmpty()) { + try { + streams.wait(); + } catch (InterruptedException e) { + // Interrupted + break; + } + continue; + } + + Iterator> iterator = streams.entrySet().iterator(); + Map.Entry entry = iterator.next(); + iterator.remove(); + index = entry.getKey(); + stream = entry.getValue(); + decodingIndex.lazySet(index); + } + + try { + Image image = Image.decode(stream, true); + if (image != null) { + notifyPageSucceed(index, image); + } else { + notifyPageFailed(index, GetText.getString(R.string.error_decoding_failed)); + } + } finally { + decodingIndex.lazySet(GalleryPageView.INVALID_INDEX); + } + } } - } } - } - - private static Comparator naturalComparator = new Comparator() { - private NaturalComparator comparator = new NaturalComparator(); - @Override - public int compare(A7ZipArchive.A7ZipArchiveEntry o1, A7ZipArchive.A7ZipArchiveEntry o2) { - return comparator.compare(o1.getPath(), o2.getPath()); - } - }; } diff --git a/app/src/main/java/com/hippo/ehviewer/gallery/DirGalleryProvider.java b/app/src/main/java/com/hippo/ehviewer/gallery/DirGalleryProvider.java index 529ae7859..7ff39a717 100644 --- a/app/src/main/java/com/hippo/ehviewer/gallery/DirGalleryProvider.java +++ b/app/src/main/java/com/hippo/ehviewer/gallery/DirGalleryProvider.java @@ -18,8 +18,10 @@ package com.hippo.ehviewer.gallery; import android.os.Process; import android.util.Log; + import androidx.annotation.NonNull; import androidx.annotation.Nullable; + import com.hippo.ehviewer.GetText; import com.hippo.ehviewer.R; import com.hippo.glgallery.GalleryPageView; @@ -31,6 +33,7 @@ import com.hippo.yorozuya.FileUtils; import com.hippo.yorozuya.IOUtils; import com.hippo.yorozuya.StringUtils; import com.hippo.yorozuya.thread.PriorityThread; + import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; @@ -44,7 +47,16 @@ public class DirGalleryProvider extends GalleryProvider2 implements Runnable { private static final String TAG = DirGalleryProvider.class.getSimpleName(); private static final AtomicInteger sIdGenerator = new AtomicInteger(); + private static FilenameFilter imageFilter = + (dir, name) -> StringUtils.endsWith(name.toLowerCase(), SUPPORT_IMAGE_EXTENSIONS); + private static Comparator naturalComparator = new Comparator() { + private NaturalComparator comparator = new NaturalComparator(); + @Override + public int compare(UniFile o1, UniFile o2) { + return comparator.compare(o1.getName(), o2.getName()); + } + }; private final UniFile mDir; private final Stack mRequests = new Stack<>(); private final AtomicInteger mDecodingIndex = new AtomicInteger(GalleryPageView.INVALID_INDEX); @@ -242,15 +254,4 @@ public class DirGalleryProvider extends GalleryProvider2 implements Runnable { Log.i(TAG, "ImageDecoder end"); } - - private static FilenameFilter imageFilter = - (dir, name) -> StringUtils.endsWith(name.toLowerCase(), SUPPORT_IMAGE_EXTENSIONS); - - private static Comparator naturalComparator = new Comparator() { - private NaturalComparator comparator = new NaturalComparator(); - @Override - public int compare(UniFile o1, UniFile o2) { - return comparator.compare(o1.getName(), o2.getName()); - } - }; } diff --git a/app/src/main/java/com/hippo/ehviewer/gallery/EhGalleryProvider.java b/app/src/main/java/com/hippo/ehviewer/gallery/EhGalleryProvider.java index aee72473d..6f068c25e 100644 --- a/app/src/main/java/com/hippo/ehviewer/gallery/EhGalleryProvider.java +++ b/app/src/main/java/com/hippo/ehviewer/gallery/EhGalleryProvider.java @@ -17,14 +17,17 @@ package com.hippo.ehviewer.gallery; import android.content.Context; + import androidx.annotation.NonNull; import androidx.annotation.Nullable; + import com.hippo.ehviewer.client.data.GalleryInfo; import com.hippo.ehviewer.spider.SpiderQueen; import com.hippo.glgallery.GalleryProvider; import com.hippo.image.Image; import com.hippo.unifile.UniFile; import com.hippo.yorozuya.SimpleHandler; + import java.util.Locale; public class EhGalleryProvider extends GalleryProvider2 implements SpiderQueen.OnSpiderListener { diff --git a/app/src/main/java/com/hippo/ehviewer/gallery/GalleryProvider2.java b/app/src/main/java/com/hippo/ehviewer/gallery/GalleryProvider2.java index d9a2b9d9d..4ea4bd30c 100644 --- a/app/src/main/java/com/hippo/ehviewer/gallery/GalleryProvider2.java +++ b/app/src/main/java/com/hippo/ehviewer/gallery/GalleryProvider2.java @@ -18,6 +18,7 @@ package com.hippo.ehviewer.gallery; import androidx.annotation.NonNull; import androidx.annotation.Nullable; + import com.hippo.glgallery.GalleryProvider; import com.hippo.unifile.UniFile; @@ -35,7 +36,8 @@ public abstract class GalleryProvider2 extends GalleryProvider { return 0; } - public void putStartPage(int page) {} + public void putStartPage(int page) { + } /** * @return without extension diff --git a/app/src/main/java/com/hippo/ehviewer/gallery/Pipe.java b/app/src/main/java/com/hippo/ehviewer/gallery/Pipe.java index 0cdb4a169..c8f1f4746 100644 --- a/app/src/main/java/com/hippo/ehviewer/gallery/Pipe.java +++ b/app/src/main/java/com/hippo/ehviewer/gallery/Pipe.java @@ -17,146 +17,147 @@ package com.hippo.ehviewer.gallery; import androidx.annotation.NonNull; + import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; class Pipe { - private final int capacity; - private final byte[] buffer; + private final int capacity; + private final byte[] buffer; - private int head = 0; - private int tail = 0; - private boolean full = false; + private int head = 0; + private int tail = 0; + private boolean full = false; - private boolean inClosed = false; - private boolean outClosed = false; + private boolean inClosed = false; + private boolean outClosed = false; - private InputStream inputStream = new InputStream() { - @Override - public int read() throws IOException { - synchronized (Pipe.this) { - byte[] bytes = new byte[1]; - if (read(bytes, 0, 1) != -1) { - return bytes[0]; - } else { - return -1; + private InputStream inputStream = new InputStream() { + @Override + public int read() throws IOException { + synchronized (Pipe.this) { + byte[] bytes = new byte[1]; + if (read(bytes, 0, 1) != -1) { + return bytes[0]; + } else { + return -1; + } + } } - } - } - @Override - public int read(@NonNull byte[] b, int off, int len) throws IOException { - synchronized (Pipe.this) { - for (;;) { - if (inClosed) { - throw new IOException("The InputStream is closed"); - } - if (len == 0) { - return 0; - } + @Override + public int read(@NonNull byte[] b, int off, int len) throws IOException { + synchronized (Pipe.this) { + for (; ; ) { + if (inClosed) { + throw new IOException("The InputStream is closed"); + } + if (len == 0) { + return 0; + } - if (head == tail && !full) { - if (outClosed) { - // No bytes available and the OutputStream is closed. So it's the end. - return -1; - } else { - // Wait for OutputStream write bytes - try { - Pipe.this.wait(); - } catch (InterruptedException e) { - throw new IOException("The thread interrupted", e); - } + if (head == tail && !full) { + if (outClosed) { + // No bytes available and the OutputStream is closed. So it's the end. + return -1; + } else { + // Wait for OutputStream write bytes + try { + Pipe.this.wait(); + } catch (InterruptedException e) { + throw new IOException("The thread interrupted", e); + } + } + } else { + int read = Math.min(len, (head < tail ? tail : capacity) - head); + System.arraycopy(buffer, head, b, off, read); + head += read; + if (head == capacity) { + head = 0; + } + full = false; + Pipe.this.notifyAll(); + return read; + } + } } - } else { - int read = Math.min(len, (head < tail ? tail : capacity) - head); - System.arraycopy(buffer, head, b, off, read); - head += read; - if (head == capacity) { - head = 0; - } - full = false; - Pipe.this.notifyAll(); - return read; - } } - } - } - @Override - public void close() { - synchronized (Pipe.this) { - inClosed = true; - Pipe.this.notifyAll(); - } - } - }; - - private OutputStream outputStream = new OutputStream() { - @Override - public void write(int b) throws IOException { - synchronized (Pipe.this) { - byte[] bytes = new byte[] { (byte) b}; - write(bytes, 0, 1); - } - } - - @Override - public void write(@NonNull byte[] b, int off, int len) throws IOException { - synchronized (Pipe.this) { - while (len != 0) { - if (outClosed) { - throw new IOException("The OutputStream is closed"); - } - if (inClosed) { - throw new IOException("The InputStream is closed"); - } - - if (head == tail && full) { - // The buffer is full, wait for InputStream read bytes - try { - Pipe.this.wait(); - } catch (InterruptedException e) { - throw new IOException("The thread interrupted", e); + @Override + public void close() { + synchronized (Pipe.this) { + inClosed = true; + Pipe.this.notifyAll(); } - } else { - int write = Math.min(len, (head <= tail ? capacity : head) - tail); - System.arraycopy(b, off, buffer, tail, write); - off += write; - len -= write; - tail += write; - if (tail == capacity) { - tail = 0; - } - if (head == tail) { - full = true; - } - Pipe.this.notifyAll(); - } } - } + }; + + private OutputStream outputStream = new OutputStream() { + @Override + public void write(int b) throws IOException { + synchronized (Pipe.this) { + byte[] bytes = new byte[]{(byte) b}; + write(bytes, 0, 1); + } + } + + @Override + public void write(@NonNull byte[] b, int off, int len) throws IOException { + synchronized (Pipe.this) { + while (len != 0) { + if (outClosed) { + throw new IOException("The OutputStream is closed"); + } + if (inClosed) { + throw new IOException("The InputStream is closed"); + } + + if (head == tail && full) { + // The buffer is full, wait for InputStream read bytes + try { + Pipe.this.wait(); + } catch (InterruptedException e) { + throw new IOException("The thread interrupted", e); + } + } else { + int write = Math.min(len, (head <= tail ? capacity : head) - tail); + System.arraycopy(b, off, buffer, tail, write); + off += write; + len -= write; + tail += write; + if (tail == capacity) { + tail = 0; + } + if (head == tail) { + full = true; + } + Pipe.this.notifyAll(); + } + } + } + } + + @Override + public void close() { + synchronized (Pipe.this) { + outClosed = true; + Pipe.this.notifyAll(); + } + } + }; + + Pipe(int capacity) { + this.capacity = capacity; + this.buffer = new byte[capacity]; } - @Override - public void close() { - synchronized (Pipe.this) { - outClosed = true; - Pipe.this.notifyAll(); - } + InputStream getInputStream() { + return inputStream; } - }; - Pipe(int capacity) { - this.capacity = capacity; - this.buffer = new byte[capacity]; - } - - InputStream getInputStream() { - return inputStream; - } - - OutputStream getOutputStream() { - return outputStream; - } + OutputStream getOutputStream() { + return outputStream; + } } diff --git a/app/src/main/java/com/hippo/ehviewer/preference/CleanRedundancyPreference.java b/app/src/main/java/com/hippo/ehviewer/preference/CleanRedundancyPreference.java index 1c0abae65..f66207e44 100644 --- a/app/src/main/java/com/hippo/ehviewer/preference/CleanRedundancyPreference.java +++ b/app/src/main/java/com/hippo/ehviewer/preference/CleanRedundancyPreference.java @@ -19,7 +19,9 @@ package com.hippo.ehviewer.preference; import android.content.Context; import android.util.AttributeSet; import android.widget.Toast; + import androidx.annotation.NonNull; + import com.hippo.ehviewer.EhApplication; import com.hippo.ehviewer.R; import com.hippo.ehviewer.Settings; @@ -91,7 +93,7 @@ public class CleanRedundancyPreference extends TaskPreference { } int count = 0; - for (UniFile f: files) { + for (UniFile f : files) { if (clearFile(f)) { ++count; } @@ -110,7 +112,7 @@ public class CleanRedundancyPreference extends TaskPreference { } Toast.makeText(mApplication, 0 == count ? - mApplication.getString(R.string.settings_download_clean_redundancy_no_redundancy): + mApplication.getString(R.string.settings_download_clean_redundancy_no_redundancy) : mApplication.getString(R.string.settings_download_clean_redundancy_done, count), Toast.LENGTH_SHORT).show(); super.onPostExecute(o); } diff --git a/app/src/main/java/com/hippo/ehviewer/preference/ClearDownloadPathCachePreference.java b/app/src/main/java/com/hippo/ehviewer/preference/ClearDownloadPathCachePreference.java index 96a51fd14..8e68d35c4 100644 --- a/app/src/main/java/com/hippo/ehviewer/preference/ClearDownloadPathCachePreference.java +++ b/app/src/main/java/com/hippo/ehviewer/preference/ClearDownloadPathCachePreference.java @@ -18,7 +18,9 @@ package com.hippo.ehviewer.preference; import android.content.Context; import android.util.AttributeSet; + import androidx.appcompat.app.AlertDialog; + import com.hippo.ehviewer.EhDB; import com.hippo.ehviewer.R; import com.hippo.preference.MessagePreference; @@ -36,7 +38,7 @@ public class ClearDownloadPathCachePreference extends MessagePreference { } public ClearDownloadPathCachePreference(Context context, - AttributeSet attrs, int defStyleAttr) { + AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(context); } diff --git a/app/src/main/java/com/hippo/ehviewer/preference/DefaultCategoryPreference.java b/app/src/main/java/com/hippo/ehviewer/preference/DefaultCategoryPreference.java index ce3b57af1..ed2cd3a27 100644 --- a/app/src/main/java/com/hippo/ehviewer/preference/DefaultCategoryPreference.java +++ b/app/src/main/java/com/hippo/ehviewer/preference/DefaultCategoryPreference.java @@ -18,8 +18,10 @@ package com.hippo.ehviewer.preference; import android.content.Context; import android.util.AttributeSet; + import androidx.annotation.Nullable; import androidx.appcompat.app.AlertDialog; + import com.hippo.ehviewer.R; import com.hippo.ehviewer.Settings; import com.hippo.ehviewer.widget.CategoryTable; diff --git a/app/src/main/java/com/hippo/ehviewer/preference/ExcludedTagNamespacesPreference.java b/app/src/main/java/com/hippo/ehviewer/preference/ExcludedTagNamespacesPreference.java index f27ef6837..b65d629ea 100644 --- a/app/src/main/java/com/hippo/ehviewer/preference/ExcludedTagNamespacesPreference.java +++ b/app/src/main/java/com/hippo/ehviewer/preference/ExcludedTagNamespacesPreference.java @@ -20,7 +20,9 @@ import android.content.Context; import android.util.AttributeSet; import android.view.View; import android.widget.CheckBox; + import androidx.appcompat.app.AlertDialog; + import com.hippo.ehviewer.R; import com.hippo.ehviewer.Settings; import com.hippo.preference.DialogPreference; @@ -29,8 +31,6 @@ import com.hippo.yorozuya.ViewUtils; public class ExcludedTagNamespacesPreference extends DialogPreference { - private View mTableLayout; - private static final int[] EXCLUDED_TAG_GROUP_RES_ID = { R.id.tag_group_reclass, R.id.tag_group_language, @@ -41,7 +41,6 @@ public class ExcludedTagNamespacesPreference extends DialogPreference { R.id.tag_group_male, R.id.tag_group_female, }; - private static final int[] EXCLUDED_TAG_GROUP_ID = { 0x1, 0x2, @@ -52,6 +51,7 @@ public class ExcludedTagNamespacesPreference extends DialogPreference { 0x40, 0x80 }; + private View mTableLayout; public ExcludedTagNamespacesPreference(Context context) { super(context); diff --git a/app/src/main/java/com/hippo/ehviewer/preference/ExportDataPreference.java b/app/src/main/java/com/hippo/ehviewer/preference/ExportDataPreference.java index 6d647b66f..940e3a6c9 100644 --- a/app/src/main/java/com/hippo/ehviewer/preference/ExportDataPreference.java +++ b/app/src/main/java/com/hippo/ehviewer/preference/ExportDataPreference.java @@ -19,60 +19,63 @@ package com.hippo.ehviewer.preference; import android.content.Context; import android.util.AttributeSet; import android.widget.Toast; + import androidx.annotation.NonNull; + import com.hippo.ehviewer.AppConfig; import com.hippo.ehviewer.EhDB; import com.hippo.ehviewer.GetText; import com.hippo.ehviewer.R; import com.hippo.util.ReadableTime; + import java.io.File; public class ExportDataPreference extends TaskPreference { - public ExportDataPreference(Context context) { - super(context); - } - - public ExportDataPreference(Context context, AttributeSet attrs) { - super(context, attrs); - } - - public ExportDataPreference(Context context, AttributeSet attrs, int defStyleAttr) { - super(context, attrs, defStyleAttr); - } - - @NonNull - @Override - protected Task onCreateTask() { - return new ExportDataTask(getContext()); - } - - private static class ExportDataTask extends Task { - - public ExportDataTask(@NonNull Context context) { - super(context); + public ExportDataPreference(Context context) { + super(context); } + public ExportDataPreference(Context context, AttributeSet attrs) { + super(context, attrs); + } + + public ExportDataPreference(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + } + + @NonNull @Override - protected Object doInBackground(Void... voids) { - File dir = AppConfig.getExternalDataDir(); - if (dir != null) { - File file = new File(dir, ReadableTime.getFilenamableTime(System.currentTimeMillis()) + ".db"); - if (EhDB.exportDB(getApplication(), file)) { - return file; + protected Task onCreateTask() { + return new ExportDataTask(getContext()); + } + + private static class ExportDataTask extends Task { + + public ExportDataTask(@NonNull Context context) { + super(context); } - } - return null; - } - @Override - protected void onPostExecute(Object o) { - Toast.makeText(getApplication(), - (o instanceof File) - ? GetText.getString(R.string.settings_advanced_export_data_to, ((File) o).getPath()) - : GetText.getString(R.string.settings_advanced_export_data_failed), - Toast.LENGTH_SHORT).show(); - super.onPostExecute(o); + @Override + protected Object doInBackground(Void... voids) { + File dir = AppConfig.getExternalDataDir(); + if (dir != null) { + File file = new File(dir, ReadableTime.getFilenamableTime(System.currentTimeMillis()) + ".db"); + if (EhDB.exportDB(getApplication(), file)) { + return file; + } + } + return null; + } + + @Override + protected void onPostExecute(Object o) { + Toast.makeText(getApplication(), + (o instanceof File) + ? GetText.getString(R.string.settings_advanced_export_data_to, ((File) o).getPath()) + : GetText.getString(R.string.settings_advanced_export_data_failed), + Toast.LENGTH_SHORT).show(); + super.onPostExecute(o); + } } - } } diff --git a/app/src/main/java/com/hippo/ehviewer/preference/IdentityCookiePreference.java b/app/src/main/java/com/hippo/ehviewer/preference/IdentityCookiePreference.java index ee2964423..1a5e4e10a 100644 --- a/app/src/main/java/com/hippo/ehviewer/preference/IdentityCookiePreference.java +++ b/app/src/main/java/com/hippo/ehviewer/preference/IdentityCookiePreference.java @@ -21,15 +21,19 @@ import android.content.ClipboardManager; import android.content.Context; import android.util.AttributeSet; import android.widget.Toast; + import androidx.appcompat.app.AlertDialog; + import com.hippo.ehviewer.EhApplication; import com.hippo.ehviewer.R; import com.hippo.ehviewer.client.EhCookieStore; import com.hippo.ehviewer.client.EhUrl; import com.hippo.preference.MessagePreference; import com.hippo.text.Html; + import java.util.LinkedList; import java.util.List; + import okhttp3.Cookie; import okhttp3.HttpUrl; diff --git a/app/src/main/java/com/hippo/ehviewer/preference/ProxyPreference.java b/app/src/main/java/com/hippo/ehviewer/preference/ProxyPreference.java index c5e14a095..dadfe8a25 100644 --- a/app/src/main/java/com/hippo/ehviewer/preference/ProxyPreference.java +++ b/app/src/main/java/com/hippo/ehviewer/preference/ProxyPreference.java @@ -25,7 +25,9 @@ import android.util.AttributeSet; import android.view.View; import android.widget.EditText; import android.widget.Spinner; + import androidx.appcompat.app.AlertDialog; + import com.google.android.material.textfield.TextInputLayout; import com.hippo.ehviewer.EhApplication; import com.hippo.ehviewer.EhProxySelector; @@ -71,20 +73,20 @@ public class ProxyPreference extends DialogPreference implements View.OnClickLis private void updateSummary(int type, String ip, int port) { if ((type == EhProxySelector.TYPE_HTTP || type == EhProxySelector.TYPE_SOCKS) - && (TextUtils.isEmpty(ip) || !InetValidator.isValidInetPort(port)) ) { + && (TextUtils.isEmpty(ip) || !InetValidator.isValidInetPort(port))) { type = EhProxySelector.TYPE_SYSTEM; } if (type == EhProxySelector.TYPE_HTTP || type == EhProxySelector.TYPE_SOCKS) { Context context = getContext(); setSummary(context.getString(R.string.settings_advanced_proxy_summary_1, - getProxyTypeText(context, type), - ip, - port)); + getProxyTypeText(context, type), + ip, + port)); } else { Context context = getContext(); setSummary(context.getString(R.string.settings_advanced_proxy_summary_2, - getProxyTypeText(context, type))); + getProxyTypeText(context, type))); } } diff --git a/app/src/main/java/com/hippo/ehviewer/preference/RestoreDownloadPreference.java b/app/src/main/java/com/hippo/ehviewer/preference/RestoreDownloadPreference.java index 61b322301..6049e1046 100644 --- a/app/src/main/java/com/hippo/ehviewer/preference/RestoreDownloadPreference.java +++ b/app/src/main/java/com/hippo/ehviewer/preference/RestoreDownloadPreference.java @@ -19,10 +19,12 @@ package com.hippo.ehviewer.preference; import android.app.Activity; import android.content.Context; import android.os.Parcel; -import android.preference.Preference; import android.util.AttributeSet; import android.widget.Toast; + import androidx.annotation.NonNull; +import androidx.preference.Preference; + import com.hippo.ehviewer.EhApplication; import com.hippo.ehviewer.EhDB; import com.hippo.ehviewer.R; @@ -36,11 +38,13 @@ import com.hippo.ehviewer.spider.SpiderQueen; import com.hippo.unifile.UniFile; import com.hippo.util.ExceptionUtils; import com.hippo.yorozuya.IOUtils; + import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; import java.util.Collections; import java.util.List; + import okhttp3.OkHttpClient; public class RestoreDownloadPreference extends TaskPreference { @@ -123,7 +127,7 @@ public class RestoreDownloadPreference extends TaskPreference { return null; } - for (UniFile file: files) { + for (UniFile file : files) { RestoreItem restoreItem = getRestoreItem(file); if (null != restoreItem) { restoreItemList.add(restoreItem); @@ -184,27 +188,6 @@ public class RestoreDownloadPreference extends TaskPreference { private static class RestoreItem extends GalleryInfo { - public String dirname; - - @Override - public int describeContents() { - return 0; - } - - @Override - public void writeToParcel(Parcel dest, int flags) { - super.writeToParcel(dest, flags); - dest.writeString(this.dirname); - } - - public RestoreItem() { - } - - protected RestoreItem(Parcel in) { - super(in); - this.dirname = in.readString(); - } - public static final Creator CREATOR = new Creator() { @Override public RestoreItem createFromParcel(Parcel source) { @@ -216,5 +199,25 @@ public class RestoreDownloadPreference extends TaskPreference { return new RestoreItem[size]; } }; + public String dirname; + + public RestoreItem() { + } + + protected RestoreItem(Parcel in) { + super(in); + this.dirname = in.readString(); + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + super.writeToParcel(dest, flags); + dest.writeString(this.dirname); + } } } diff --git a/app/src/main/java/com/hippo/ehviewer/preference/SignInRequiredActivityPreference.java b/app/src/main/java/com/hippo/ehviewer/preference/SignInRequiredActivityPreference.java index 0160b1bd6..cf2fd3e82 100644 --- a/app/src/main/java/com/hippo/ehviewer/preference/SignInRequiredActivityPreference.java +++ b/app/src/main/java/com/hippo/ehviewer/preference/SignInRequiredActivityPreference.java @@ -19,54 +19,58 @@ package com.hippo.ehviewer.preference; import android.content.Context; import android.util.AttributeSet; import android.view.View; -import android.view.ViewGroup; import android.widget.Toast; + +import androidx.preference.PreferenceViewHolder; + import com.google.android.material.snackbar.Snackbar; import com.hippo.ehviewer.EhApplication; import com.hippo.ehviewer.R; import com.hippo.ehviewer.client.EhCookieStore; import com.hippo.ehviewer.client.EhUrl; import com.hippo.preference.ActivityPreference; + import okhttp3.HttpUrl; public class SignInRequiredActivityPreference extends ActivityPreference { - private View view; + private View view; - public SignInRequiredActivityPreference(Context context) { - super(context); - } + public SignInRequiredActivityPreference(Context context) { + super(context); + } - public SignInRequiredActivityPreference(Context context, AttributeSet attrs) { - super(context, attrs); - } + public SignInRequiredActivityPreference(Context context, AttributeSet attrs) { + super(context, attrs); + } - public SignInRequiredActivityPreference(Context context, AttributeSet attrs, int defStyleAttr) { - super(context, attrs, defStyleAttr); - } + public SignInRequiredActivityPreference(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + } - @Override - protected View onCreateView(ViewGroup parent) { - return view = super.onCreateView(parent); - } + @Override + public void onBindViewHolder(PreferenceViewHolder holder) { + view = holder.itemView; + super.onBindViewHolder(holder); + } - @Override - protected void onClick() { - EhCookieStore store = EhApplication.getEhCookieStore(getContext()); - HttpUrl e = HttpUrl.get(EhUrl.HOST_E); - HttpUrl ex = HttpUrl.get(EhUrl.HOST_EX); + @Override + protected void onClick() { + EhCookieStore store = EhApplication.getEhCookieStore(getContext()); + HttpUrl e = HttpUrl.get(EhUrl.HOST_E); + HttpUrl ex = HttpUrl.get(EhUrl.HOST_EX); - if (store.contains(e, EhCookieStore.KEY_IPD_MEMBER_ID) || - store.contains(e, EhCookieStore.KEY_IPD_PASS_HASH) || - store.contains(ex, EhCookieStore.KEY_IPD_MEMBER_ID) || - store.contains(ex, EhCookieStore.KEY_IPD_PASS_HASH)) { - super.onClick(); - } else { - if (view != null) { - Snackbar.make(view, R.string.error_please_login_first, 3000).show(); - } else { - Toast.makeText(getContext(), R.string.error_please_login_first, Toast.LENGTH_LONG).show(); - } + if (store.contains(e, EhCookieStore.KEY_IPD_MEMBER_ID) || + store.contains(e, EhCookieStore.KEY_IPD_PASS_HASH) || + store.contains(ex, EhCookieStore.KEY_IPD_MEMBER_ID) || + store.contains(ex, EhCookieStore.KEY_IPD_PASS_HASH)) { + super.onClick(); + } else { + if (view != null) { + Snackbar.make(view, R.string.error_please_login_first, 3000).show(); + } else { + Toast.makeText(getContext(), R.string.error_please_login_first, Toast.LENGTH_LONG).show(); + } + } } - } } diff --git a/app/src/main/java/com/hippo/ehviewer/preference/SignOutPreference.java b/app/src/main/java/com/hippo/ehviewer/preference/SignOutPreference.java index e180f7656..3e2b0b0ec 100644 --- a/app/src/main/java/com/hippo/ehviewer/preference/SignOutPreference.java +++ b/app/src/main/java/com/hippo/ehviewer/preference/SignOutPreference.java @@ -19,7 +19,9 @@ package com.hippo.ehviewer.preference; import android.content.Context; import android.util.AttributeSet; import android.widget.Toast; + import androidx.appcompat.app.AlertDialog; + import com.hippo.ehviewer.R; import com.hippo.ehviewer.client.EhUtils; import com.hippo.preference.MessagePreference; diff --git a/app/src/main/java/com/hippo/ehviewer/preference/TaskPreference.java b/app/src/main/java/com/hippo/ehviewer/preference/TaskPreference.java index 5aa2f525b..f81b6e86f 100644 --- a/app/src/main/java/com/hippo/ehviewer/preference/TaskPreference.java +++ b/app/src/main/java/com/hippo/ehviewer/preference/TaskPreference.java @@ -22,11 +22,13 @@ import android.os.AsyncTask; import android.os.Parcel; import android.os.Parcelable; import android.util.AttributeSet; + import androidx.annotation.CallSuper; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.appcompat.app.AlertDialog; import androidx.customview.view.AbsSavedState; + import com.hippo.ehviewer.EhApplication; import com.hippo.ehviewer.R; import com.hippo.preference.DialogPreference; @@ -81,15 +83,6 @@ public abstract class TaskPreference extends DialogPreference { @NonNull protected abstract Task onCreateTask(); - @Override - public void onActivityDestroy() { - super.onActivityDestroy(); - - if (null != mTask) { - mTask.setPreference(null); - } - } - @Override protected Parcelable onSaveInstanceState() { final Parcelable superState = super.onSaveInstanceState(); @@ -125,23 +118,6 @@ public abstract class TaskPreference extends DialogPreference { } private static class SavedState extends AbsSavedState { - int asyncTaskId; - - public SavedState(Parcel source) { - super(source, SavedState.class.getClassLoader()); - asyncTaskId = source.readInt(); - } - - @Override - public void writeToParcel(Parcel dest, int flags) { - super.writeToParcel(dest, flags); - dest.writeInt(asyncTaskId); - } - - public SavedState(Parcelable superState) { - super(superState); - } - public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { @Override @@ -154,6 +130,22 @@ public abstract class TaskPreference extends DialogPreference { return new SavedState[size]; } }; + int asyncTaskId; + + public SavedState(Parcel source) { + super(source, SavedState.class.getClassLoader()); + asyncTaskId = source.readInt(); + } + + public SavedState(Parcelable superState) { + super(superState); + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + super.writeToParcel(dest, flags); + dest.writeInt(asyncTaskId); + } } public abstract static class Task extends AsyncTask { diff --git a/app/src/main/java/com/hippo/ehviewer/preference/VersionPreference.java b/app/src/main/java/com/hippo/ehviewer/preference/VersionPreference.java index 1d5ad0e0b..08e1dcf68 100644 --- a/app/src/main/java/com/hippo/ehviewer/preference/VersionPreference.java +++ b/app/src/main/java/com/hippo/ehviewer/preference/VersionPreference.java @@ -19,7 +19,7 @@ package com.hippo.ehviewer.preference; import android.content.Context; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; -import android.preference.Preference; +import androidx.preference.Preference; import android.util.AttributeSet; import com.hippo.ehviewer.R; diff --git a/app/src/main/java/com/hippo/ehviewer/shortcuts/ShortcutsActivity.java b/app/src/main/java/com/hippo/ehviewer/shortcuts/ShortcutsActivity.java index 2987bde1e..f90a89f60 100644 --- a/app/src/main/java/com/hippo/ehviewer/shortcuts/ShortcutsActivity.java +++ b/app/src/main/java/com/hippo/ehviewer/shortcuts/ShortcutsActivity.java @@ -26,16 +26,16 @@ import com.hippo.ehviewer.download.DownloadService; * Created by onlymash on 3/25/18. */ -public class ShortcutsActivity extends Activity{ +public class ShortcutsActivity extends Activity { public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); String action = null; Intent intent = getIntent(); - if (intent != null){ + if (intent != null) { action = intent.getAction(); if (action != null && (action.equals(DownloadService.ACTION_START_ALL) || - action.equals(DownloadService.ACTION_STOP_ALL))){ + action.equals(DownloadService.ACTION_STOP_ALL))) { startService(new Intent(this, DownloadService.class).setAction(action)); } } diff --git a/app/src/main/java/com/hippo/ehviewer/spider/SpiderDen.java b/app/src/main/java/com/hippo/ehviewer/spider/SpiderDen.java index 321b30e41..66bcba26a 100644 --- a/app/src/main/java/com/hippo/ehviewer/spider/SpiderDen.java +++ b/app/src/main/java/com/hippo/ehviewer/spider/SpiderDen.java @@ -19,7 +19,9 @@ package com.hippo.ehviewer.spider; import android.content.Context; import android.graphics.BitmapFactory; import android.webkit.MimeTypeMap; + import androidx.annotation.Nullable; + import com.hippo.beerbelly.SimpleDiskCache; import com.hippo.ehviewer.EhDB; import com.hippo.ehviewer.Settings; @@ -37,6 +39,7 @@ import com.hippo.yorozuya.FileUtils; import com.hippo.yorozuya.IOUtils; import com.hippo.yorozuya.MathUtils; import com.hippo.yorozuya.Utilities; + import java.io.File; import java.io.IOException; import java.io.OutputStream; @@ -44,33 +47,23 @@ import java.util.Locale; public final class SpiderDen { - @Nullable - private final UniFile mDownloadDir; - private volatile int mMode = SpiderQueen.MODE_READ; - private final long mGid; - @Nullable private static SimpleDiskCache sCache; + @Nullable + private final UniFile mDownloadDir; + private final long mGid; + private volatile int mMode = SpiderQueen.MODE_READ; + + public SpiderDen(GalleryInfo galleryInfo) { + mGid = galleryInfo.gid; + mDownloadDir = getGalleryDownloadDir(galleryInfo); + } public static void initialize(Context context) { sCache = new SimpleDiskCache(new File(context.getCacheDir(), "image"), MathUtils.clamp(Settings.getReadCacheSize(), 40, 640) * 1024 * 1024); } - private static class StartWithFilenameFilter implements FilenameFilter { - - private final String mPrefix; - - public StartWithFilenameFilter(String prefix) { - mPrefix = prefix; - } - - @Override - public boolean accept(UniFile dir, String filename) { - return filename.startsWith(mPrefix); - } - } - public static UniFile getGalleryDownloadDir(GalleryInfo galleryInfo) { UniFile dir = Settings.getDownloadLocation(); if (dir != null) { @@ -116,9 +109,23 @@ public final class SpiderDen { } } - public SpiderDen(GalleryInfo galleryInfo) { - mGid = galleryInfo.gid; - mDownloadDir = getGalleryDownloadDir(galleryInfo); + /** + * @param extension with dot + */ + public static String generateImageFilename(int index, String extension) { + return String.format(Locale.US, "%08d%s", index + 1, extension); + } + + @Nullable + private static UniFile findImageFile(UniFile dir, int index) { + for (String extension : GalleryProvider2.SUPPORT_IMAGE_EXTENSIONS) { + String filename = generateImageFilename(index, extension); + UniFile file = dir.findFile(filename); + if (file != null) { + return file; + } + } + return null; } public void setMode(@SpiderQueen.Mode int mode) { @@ -158,25 +165,6 @@ public final class SpiderDen { return sCache.contain(key); } - /** - * @param extension with dot - */ - public static String generateImageFilename(int index, String extension) { - return String.format(Locale.US, "%08d%s", index + 1, extension); - } - - @Nullable - private static UniFile findImageFile(UniFile dir, int index) { - for (String extension : GalleryProvider2.SUPPORT_IMAGE_EXTENSIONS) { - String filename = generateImageFilename(index, extension); - UniFile file = dir.findFile(filename); - if (file != null) { - return file; - } - } - return null; - } - private boolean containInDownloadDir(int index) { UniFile dir = getDownloadDir(); if (dir == null) { @@ -334,7 +322,6 @@ public final class SpiderDen { } } - @Nullable private InputStreamPipe openCacheInputStreamPipe(int index) { if (sCache == null) { @@ -378,4 +365,18 @@ public final class SpiderDen { return null; } } + + private static class StartWithFilenameFilter implements FilenameFilter { + + private final String mPrefix; + + public StartWithFilenameFilter(String prefix) { + mPrefix = prefix; + } + + @Override + public boolean accept(UniFile dir, String filename) { + return filename.startsWith(mPrefix); + } + } } diff --git a/app/src/main/java/com/hippo/ehviewer/spider/SpiderInfo.java b/app/src/main/java/com/hippo/ehviewer/spider/SpiderInfo.java index 11ef800ed..80d65cbb7 100644 --- a/app/src/main/java/com/hippo/ehviewer/spider/SpiderInfo.java +++ b/app/src/main/java/com/hippo/ehviewer/spider/SpiderInfo.java @@ -19,11 +19,14 @@ package com.hippo.ehviewer.spider; import android.text.TextUtils; import android.util.Log; import android.util.SparseArray; + import androidx.annotation.NonNull; import androidx.annotation.Nullable; + import com.hippo.unifile.UniFile; import com.hippo.yorozuya.IOUtils; import com.hippo.yorozuya.NumberUtils; + import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; @@ -31,13 +34,10 @@ import java.io.OutputStreamWriter; public class SpiderInfo { + static final String TOKEN_FAILED = "failed"; private static final String TAG = SpiderInfo.class.getSimpleName(); - private static final String VERSION_STR = "VERSION"; private static final int VERSION = 2; - - static final String TOKEN_FAILED = "failed"; - public int startPage = 0; public long gid = -1; public String token = null; diff --git a/app/src/main/java/com/hippo/ehviewer/spider/SpiderQueen.java b/app/src/main/java/com/hippo/ehviewer/spider/SpiderQueen.java index e53e79917..532d4e51d 100644 --- a/app/src/main/java/com/hippo/ehviewer/spider/SpiderQueen.java +++ b/app/src/main/java/com/hippo/ehviewer/spider/SpiderQueen.java @@ -24,10 +24,12 @@ import android.text.TextUtils; import android.util.Log; import android.util.SparseArray; import android.webkit.MimeTypeMap; + import androidx.annotation.IntDef; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.annotation.UiThread; + import com.hippo.beerbelly.SimpleDiskCache; import com.hippo.ehviewer.EhApplication; import com.hippo.ehviewer.GetText; @@ -61,6 +63,7 @@ import com.hippo.yorozuya.Utilities; import com.hippo.yorozuya.collect.SparseJLArray; import com.hippo.yorozuya.thread.PriorityThread; import com.hippo.yorozuya.thread.PriorityThreadFactory; + import java.io.BufferedInputStream; import java.io.IOException; import java.io.InputStream; @@ -78,6 +81,7 @@ import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicReference; + import okhttp3.Call; import okhttp3.MediaType; import okhttp3.OkHttpClient; @@ -87,38 +91,23 @@ import okhttp3.ResponseBody; public final class SpiderQueen implements Runnable { - private static final String TAG = SpiderQueen.class.getSimpleName(); - private static final AtomicInteger sIdGenerator = new AtomicInteger(); - private static final boolean DEBUG_LOG = false; - private static final boolean DEBUG_PTOKEN = true; - - @IntDef({MODE_READ, MODE_DOWNLOAD}) - @Retention(RetentionPolicy.SOURCE) - public @interface Mode {} - - @IntDef({STATE_NONE, STATE_DOWNLOADING, STATE_FINISHED, STATE_FAILED}) - @Retention(RetentionPolicy.SOURCE) - public @interface State {} - public static final int MODE_READ = 0; public static final int MODE_DOWNLOAD = 1; - public static final int STATE_NONE = 0; public static final int STATE_DOWNLOADING = 1; public static final int STATE_FINISHED = 2; public static final int STATE_FAILED = 3; - public static final int DECODE_THREAD_NUM = 1; - public static final String SPIDER_INFO_FILENAME = ".ehviewer"; - + private static final String TAG = SpiderQueen.class.getSimpleName(); + private static final AtomicInteger sIdGenerator = new AtomicInteger(); + private static final boolean DEBUG_LOG = false; + private static final boolean DEBUG_PTOKEN = true; private static final String[] URL_509_SUFFIX_ARRAY = { "/509.gif", "/509s.gif" }; - private static final SparseJLArray sQueenMap = new SparseJLArray<>(); - @NonNull private final OkHttpClient mHttpClient; @NonNull @@ -127,54 +116,42 @@ public final class SpiderQueen implements Runnable { private final GalleryInfo mGalleryInfo; @NonNull private final SpiderDen mSpiderDen; - - private int mReadReference = 0; - private int mDownloadReference = 0; - - // It mQueenThread is null, failed or stopped - @Nullable - private volatile Thread mQueenThread; private final Object mQueenLock = new Object(); - private final Thread[] mDecodeThreadArray = new Thread[DECODE_THREAD_NUM]; private final int[] mDecodeIndexArray = new int[DECODE_THREAD_NUM]; private final Queue mDecodeRequestQueue = new LinkedList<>(); - private final Object mWorkerLock = new Object(); - private ThreadPoolExecutor mWorkerPoolExecutor; - private int mWorkerCount; - private final Object mPTokenLock = new Object(); private final AtomicReference mSpiderInfo = new AtomicReference<>(); private final Queue mRequestPTokenQueue = new ConcurrentLinkedQueue<>(); - private final Object mPageStateLock = new Object(); - private volatile int[] mPageStateArray; - // Store request page. The index may be invalid private final Queue mRequestPageQueue = new LinkedList<>(); // Store preload page. The index may be invalid private final Queue mRequestPageQueue2 = new LinkedList<>(); // Store force request page. The index may be invalid private final Queue mForceRequestPageQueue = new LinkedList<>(); - // For download, when it go to mPageStateArray.size(), done - private volatile int mDownloadPage = -1; - private final AtomicInteger mDownloadedPages = new AtomicInteger(0); private final AtomicInteger mFinishedPages = new AtomicInteger(0); - - private AtomicReference showKey = new AtomicReference<>(); private final Object showKeyLock = new Object(); - // Store page error private final ConcurrentHashMap mPageErrorMap = new ConcurrentHashMap<>(); // Store page download percent private final ConcurrentHashMap mPagePercentMap = new ConcurrentHashMap<>(); - private final List mSpiderListeners = new ArrayList<>(); - private final int mWorkerMaxCount; private final int mPreloadNumber; + private int mReadReference = 0; + private int mDownloadReference = 0; + // It mQueenThread is null, failed or stopped + @Nullable + private volatile Thread mQueenThread; + private ThreadPoolExecutor mWorkerPoolExecutor; + private int mWorkerCount; + private volatile int[] mPageStateArray; + // For download, when it go to mPageStateArray.size(), done + private volatile int mDownloadPage = -1; + private AtomicReference showKey = new AtomicReference<>(); private SpiderQueen(EhApplication application, @NonNull GalleryInfo galleryInfo) { mHttpClient = EhApplication.getOkHttpClient(application); @@ -194,6 +171,49 @@ public final class SpiderQueen implements Runnable { new PriorityThreadFactory(SpiderWorker.class.getSimpleName(), Process.THREAD_PRIORITY_BACKGROUND)); } + @UiThread + public static SpiderQueen obtainSpiderQueen(@NonNull Context context, + @NonNull GalleryInfo galleryInfo, @Mode int mode) { + OSUtils.checkMainLoop(); + + SpiderQueen queen = sQueenMap.get(galleryInfo.gid); + if (queen == null) { + EhApplication application = (EhApplication) context.getApplicationContext(); + queen = new SpiderQueen(application, galleryInfo); + sQueenMap.put(galleryInfo.gid, queen); + // Set mode + queen.setMode(mode); + queen.start(); + } else { + // Set mode + queen.setMode(mode); + } + return queen; + } + + @UiThread + public static void releaseSpiderQueen(@NonNull SpiderQueen queen, @Mode int mode) { + OSUtils.checkMainLoop(); + + // Clear mode + queen.clearMode(mode); + + if (queen.mReadReference == 0 && queen.mDownloadReference == 0) { + // Stop and remove if there is no reference + queen.stop(); + sQueenMap.remove(queen.mGalleryInfo.gid); + } + } + + public static boolean contain(int[] array, int value) { + for (int v : array) { + if (v == value) { + return true; + } + } + return false; + } + public void addOnSpiderListener(OnSpiderListener listener) { synchronized (mSpiderListeners) { mSpiderListeners.add(listener); @@ -288,40 +308,6 @@ public final class SpiderQueen implements Runnable { } } - @UiThread - public static SpiderQueen obtainSpiderQueen(@NonNull Context context, - @NonNull GalleryInfo galleryInfo, @Mode int mode) { - OSUtils.checkMainLoop(); - - SpiderQueen queen = sQueenMap.get(galleryInfo.gid); - if (queen == null) { - EhApplication application = (EhApplication) context.getApplicationContext(); - queen = new SpiderQueen(application, galleryInfo); - sQueenMap.put(galleryInfo.gid, queen); - // Set mode - queen.setMode(mode); - queen.start(); - } else { - // Set mode - queen.setMode(mode); - } - return queen; - } - - @UiThread - public static void releaseSpiderQueen(@NonNull SpiderQueen queen, @Mode int mode) { - OSUtils.checkMainLoop(); - - // Clear mode - queen.clearMode(mode); - - if (queen.mReadReference == 0 && queen.mDownloadReference == 0) { - // Stop and remove if there is no reference - queen.stop(); - sQueenMap.remove(queen.mGalleryInfo.gid); - } - } - private void updateMode() { int mode; if (mDownloadReference > 0) { @@ -481,8 +467,7 @@ public final class SpiderQueen implements Runnable { } /** - * @return - * String for error
+ * @return String for error
* Float for download percent
* null for wait */ @@ -496,7 +481,7 @@ public final class SpiderQueen implements Runnable { // Fix state for force if ((force && (state == STATE_FINISHED || state == STATE_FAILED)) || - (ignoreError && state == STATE_FAILED)) { + (ignoreError && state == STATE_FAILED)) { // Update state to none at once updatePageState(index, STATE_NONE); state = STATE_NONE; @@ -563,15 +548,6 @@ public final class SpiderQueen implements Runnable { return result; } - public static boolean contain(int[] array, int value) { - for (int v: array) { - if (v == value) { - return true; - } - } - return false; - } - private void ensureWorkers() { synchronized (mWorkerLock) { if (null == mWorkerPoolExecutor) { @@ -780,7 +756,7 @@ public final class SpiderQueen implements Runnable { String referer = EhUrl.getReferer(); if (DEBUG_PTOKEN) { Log.d(TAG, "index " + index + ", previewIndex " + previewIndex + - ", previewPerPage " + spiderInfo.previewPerPage+ ", url " + url); + ", previewPerPage " + spiderInfo.previewPerPage + ", url " + url); } Request request = new EhRequestBuilder(url, referer).build(); Response response = mHttpClient.newCall(request).execute(); @@ -1008,6 +984,41 @@ public final class SpiderQueen implements Runnable { } } + @IntDef({MODE_READ, MODE_DOWNLOAD}) + @Retention(RetentionPolicy.SOURCE) + public @interface Mode { + } + + @IntDef({STATE_NONE, STATE_DOWNLOADING, STATE_FINISHED, STATE_FAILED}) + @Retention(RetentionPolicy.SOURCE) + public @interface State { + } + + public interface OnSpiderListener { + + void onGetPages(int pages); + + void onGet509(int index); + + /** + * @param contentLength -1 for unknown + */ + void onPageDownload(int index, long contentLength, long receivedSize, int bytesRead); + + void onPageSuccess(int index, int finished, int downloaded, int total); + + void onPageFailure(int index, String error, int finished, int downloaded, int total); + + /** + * All workers end + */ + void onFinish(int finished, int downloaded, int total); + + void onGetImageSuccess(int index, Image image); + + void onGetImageFailure(int index, String error); + } + private class SpiderWorker implements Runnable { private final long mGid; @@ -1017,7 +1028,7 @@ public final class SpiderQueen implements Runnable { } private String getPageUrl(long gid, int index, String pToken, - String oldPageUrl, String skipHathKey) { + String oldPageUrl, String skipHathKey) { String pageUrl; if (oldPageUrl != null) { pageUrl = oldPageUrl; @@ -1276,7 +1287,7 @@ public final class SpiderQueen implements Runnable { isPipe.obtain(); InputStream inputStream = new BufferedInputStream(isPipe.open()); boolean isPlainTxt = true; - for (;;) { + for (; ; ) { int b = inputStream.read(); if (b == -1) { break; @@ -1381,7 +1392,7 @@ public final class SpiderQueen implements Runnable { // Check exist for not force request if (!force && mSpiderDen.contain(index)) { - updatePageState(index , STATE_FINISHED); + updatePageState(index, STATE_FINISHED); return true; } @@ -1481,7 +1492,8 @@ public final class SpiderQueen implements Runnable { Log.i(TAG, Thread.currentThread().getName() + ": start"); } - while (mSpiderDen.isReady() && !Thread.currentThread().isInterrupted() && runInternal()); + while (mSpiderDen.isReady() && !Thread.currentThread().isInterrupted() && runInternal()) + ; boolean finish; // Clear in spider worker array @@ -1621,29 +1633,4 @@ public final class SpiderQueen implements Runnable { mPipe.release(); } } - - public interface OnSpiderListener { - - void onGetPages(int pages); - - void onGet509(int index); - - /** - * @param contentLength -1 for unknown - */ - void onPageDownload(int index, long contentLength, long receivedSize, int bytesRead); - - void onPageSuccess(int index, int finished, int downloaded, int total); - - void onPageFailure(int index, String error, int finished, int downloaded, int total); - - /** - * All workers end - */ - void onFinish(int finished, int downloaded, int total); - - void onGetImageSuccess(int index, Image image); - - void onGetImageFailure(int index, String error); - } } diff --git a/app/src/main/java/com/hippo/ehviewer/ui/CommonOperations.java b/app/src/main/java/com/hippo/ehviewer/ui/CommonOperations.java index 8ae36e915..23fbd86eb 100644 --- a/app/src/main/java/com/hippo/ehviewer/ui/CommonOperations.java +++ b/app/src/main/java/com/hippo/ehviewer/ui/CommonOperations.java @@ -23,7 +23,9 @@ import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.os.AsyncTask; import android.util.Log; + import androidx.appcompat.app.AlertDialog; + import com.hippo.app.ListCheckBoxDialogBuilder; import com.hippo.ehviewer.EhApplication; import com.hippo.ehviewer.EhDB; @@ -44,16 +46,19 @@ import com.hippo.util.IoThreadPoolExecutor; import com.hippo.yorozuya.FileUtils; import com.hippo.yorozuya.IOUtils; import com.hippo.yorozuya.collect.LongList; + +import org.json.JSONException; +import org.json.JSONObject; + import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; import java.util.Collections; import java.util.List; + import okhttp3.OkHttpClient; import okhttp3.Request; import okhttp3.Response; -import org.json.JSONException; -import org.json.JSONObject; public final class CommonOperations { @@ -68,129 +73,8 @@ public final class CommonOperations { } } - private static final class UpdateTask extends AsyncTask { - - private final Activity mActivity; - private final OkHttpClient mHttpClient; - private final boolean mFeedback; - - public UpdateTask(Activity activity, boolean feedback) { - mActivity = activity; - mHttpClient = EhApplication.getOkHttpClient(activity); - mFeedback = feedback; - } - - private JSONObject fetchUpdateInfo(String url) throws IOException, JSONException { - Log.d(TAG, url); - Request request = new Request.Builder().url(url).build(); - Response response = mHttpClient.newCall(request).execute(); - return new JSONObject(response.body().string()); - } - - @Override - protected JSONObject doInBackground(Void... params) { - String url; - if (Settings.getBetaUpdateChannel()) { - url = "http://www.ehviewer.com/update_beta.json"; - } else { - url = "http://www.ehviewer.com/update.json"; - } - - try { - return fetchUpdateInfo(url); - } catch (Throwable e1) { - ExceptionUtils.throwIfFatal(e1); - - if (Settings.getBetaUpdateChannel()) { - url = "https://raw.githubusercontent.com/seven332/EhViewer/api/update_beta.json"; - } else { - url = "https://raw.githubusercontent.com/seven332/EhViewer/api/update.json"; - } - - try { - return fetchUpdateInfo(url); - } catch (Throwable e2) { - ExceptionUtils.throwIfFatal(e2); - return null; - } - } - } - - private void showUpToDateDialog() { - new AlertDialog.Builder(mActivity) - .setMessage(R.string.update_to_date) - .setPositiveButton(android.R.string.ok, null) - .show(); - } - - private void showUpdateDialog(String versionName, int versionCode, String size, CharSequence info, final String url) { - new AlertDialog.Builder(mActivity) - .setTitle(R.string.update) - .setMessage(mActivity.getString(R.string.update_plain, versionName, size, info)) - .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - UrlOpener.openUrl(mActivity, url, false); - } - }) - .setNegativeButton(R.string.update_ignore, new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - Settings.putSkipUpdateVersion(versionCode); - } - }).show(); - } - - private void handleResult(JSONObject jo) { - if (null == jo || mActivity.isFinishing()) { - return; - } - - String versionName; - int versionCode; - String size; - CharSequence info; - String url; - - try { - PackageManager pm = mActivity.getPackageManager(); - PackageInfo pi = pm.getPackageInfo(mActivity.getPackageName(), PackageManager.GET_ACTIVITIES); - int currentVersionCode = pi.versionCode; - versionCode = jo.getInt("version_code"); - if (currentVersionCode >= versionCode) { - // Update to date - if (mFeedback) { - showUpToDateDialog(); - } - return; - } - - versionName = jo.getString("version_name"); - size = FileUtils.humanReadableByteCount(jo.getLong("size"), false); - info = Html.fromHtml(jo.getString("info")); - url = jo.getString("url"); - } catch (Throwable e) { - ExceptionUtils.throwIfFatal(e); - return; - } - - if (mFeedback || versionCode != Settings.getSkipUpdateVersion()) { - showUpdateDialog(versionName, versionCode, size, info, url); - } - } - - @Override - protected void onPostExecute(JSONObject jsonObject) { - try { - handleResult(jsonObject); - } finally { - UPDATING = false; - } - } - } - private static void doAddToFavorites(Activity activity, GalleryInfo galleryInfo, - int slot, EhClient.Callback listener) { + int slot, EhClient.Callback listener) { if (slot == -1) { EhDB.putLocalFavorites(galleryInfo); listener.onSuccess(null); @@ -207,7 +91,7 @@ public final class CommonOperations { } public static void addToFavorites(final Activity activity, final GalleryInfo galleryInfo, - final EhClient.Callback listener) { + final EhClient.Callback listener) { int slot = Settings.getDefaultFavSlot(); String[] items = new String[11]; items[0] = activity.getString(R.string.local_favorites); @@ -220,7 +104,7 @@ public final class CommonOperations { new ListCheckBoxDialogBuilder(activity, items, (builder, dialog, position) -> { int slot1 = position - 1; - String newFavoriteName = (slot1 >= 0 && slot1 <= 9) ? items[slot1+1] : null; + String newFavoriteName = (slot1 >= 0 && slot1 <= 9) ? items[slot1 + 1] : null; doAddToFavorites(activity, galleryInfo, slot1, new DelegateFavoriteCallback(listener, galleryInfo, newFavoriteName, slot1)); if (builder.isChecked()) { Settings.putDefaultFavSlot(slot1); @@ -235,7 +119,7 @@ public final class CommonOperations { } public static void removeFromFavorites(Activity activity, GalleryInfo galleryInfo, - final EhClient.Callback listener) { + final EhClient.Callback listener) { EhDB.removeLocalFavorites(galleryInfo.gid); EhClient client = EhApplication.getEhClient(activity); EhRequest request = new EhRequest(); @@ -245,40 +129,6 @@ public final class CommonOperations { client.execute(request); } - private static class DelegateFavoriteCallback implements EhClient.Callback { - - private final EhClient.Callback delegate; - private final GalleryInfo info; - private final String newFavoriteName; - private final int slot; - - DelegateFavoriteCallback(EhClient.Callback delegate, GalleryInfo info, - String newFavoriteName, int slot) { - this.delegate = delegate; - this.info = info; - this.newFavoriteName = newFavoriteName; - this.slot = slot; - } - - @Override - public void onSuccess(Void result) { - info.favoriteName = newFavoriteName; - info.favoriteSlot = slot; - delegate.onSuccess(result); - EhApplication.getFavouriteStatusRouter().modifyFavourites(info.gid, slot); - } - - @Override - public void onFailure(Exception e) { - delegate.onFailure(e); - } - - @Override - public void onCancel() { - delegate.onCancel(); - } - } - public static void startDownload(final MainActivity activity, final GalleryInfo galleryInfo, boolean forceDefault) { startDownload(activity, Collections.singletonList(galleryInfo), forceDefault); } @@ -409,4 +259,159 @@ public final class CommonOperations { noMedia.delete(); } } + + private static final class UpdateTask extends AsyncTask { + + private final Activity mActivity; + private final OkHttpClient mHttpClient; + private final boolean mFeedback; + + public UpdateTask(Activity activity, boolean feedback) { + mActivity = activity; + mHttpClient = EhApplication.getOkHttpClient(activity); + mFeedback = feedback; + } + + private JSONObject fetchUpdateInfo(String url) throws IOException, JSONException { + Log.d(TAG, url); + Request request = new Request.Builder().url(url).build(); + Response response = mHttpClient.newCall(request).execute(); + return new JSONObject(response.body().string()); + } + + @Override + protected JSONObject doInBackground(Void... params) { + String url; + if (Settings.getBetaUpdateChannel()) { + url = "http://www.ehviewer.com/update_beta.json"; + } else { + url = "http://www.ehviewer.com/update.json"; + } + + try { + return fetchUpdateInfo(url); + } catch (Throwable e1) { + ExceptionUtils.throwIfFatal(e1); + + if (Settings.getBetaUpdateChannel()) { + url = "https://raw.githubusercontent.com/seven332/EhViewer/api/update_beta.json"; + } else { + url = "https://raw.githubusercontent.com/seven332/EhViewer/api/update.json"; + } + + try { + return fetchUpdateInfo(url); + } catch (Throwable e2) { + ExceptionUtils.throwIfFatal(e2); + return null; + } + } + } + + private void showUpToDateDialog() { + new AlertDialog.Builder(mActivity) + .setMessage(R.string.update_to_date) + .setPositiveButton(android.R.string.ok, null) + .show(); + } + + private void showUpdateDialog(String versionName, int versionCode, String size, CharSequence info, final String url) { + new AlertDialog.Builder(mActivity) + .setTitle(R.string.update) + .setMessage(mActivity.getString(R.string.update_plain, versionName, size, info)) + .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + UrlOpener.openUrl(mActivity, url, false); + } + }) + .setNegativeButton(R.string.update_ignore, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + Settings.putSkipUpdateVersion(versionCode); + } + }).show(); + } + + private void handleResult(JSONObject jo) { + if (null == jo || mActivity.isFinishing()) { + return; + } + + String versionName; + int versionCode; + String size; + CharSequence info; + String url; + + try { + PackageManager pm = mActivity.getPackageManager(); + PackageInfo pi = pm.getPackageInfo(mActivity.getPackageName(), PackageManager.GET_ACTIVITIES); + int currentVersionCode = pi.versionCode; + versionCode = jo.getInt("version_code"); + if (currentVersionCode >= versionCode) { + // Update to date + if (mFeedback) { + showUpToDateDialog(); + } + return; + } + + versionName = jo.getString("version_name"); + size = FileUtils.humanReadableByteCount(jo.getLong("size"), false); + info = Html.fromHtml(jo.getString("info")); + url = jo.getString("url"); + } catch (Throwable e) { + ExceptionUtils.throwIfFatal(e); + return; + } + + if (mFeedback || versionCode != Settings.getSkipUpdateVersion()) { + showUpdateDialog(versionName, versionCode, size, info, url); + } + } + + @Override + protected void onPostExecute(JSONObject jsonObject) { + try { + handleResult(jsonObject); + } finally { + UPDATING = false; + } + } + } + + private static class DelegateFavoriteCallback implements EhClient.Callback { + + private final EhClient.Callback delegate; + private final GalleryInfo info; + private final String newFavoriteName; + private final int slot; + + DelegateFavoriteCallback(EhClient.Callback delegate, GalleryInfo info, + String newFavoriteName, int slot) { + this.delegate = delegate; + this.info = info; + this.newFavoriteName = newFavoriteName; + this.slot = slot; + } + + @Override + public void onSuccess(Void result) { + info.favoriteName = newFavoriteName; + info.favoriteSlot = slot; + delegate.onSuccess(result); + EhApplication.getFavouriteStatusRouter().modifyFavourites(info.gid, slot); + } + + @Override + public void onFailure(Exception e) { + delegate.onFailure(e); + } + + @Override + public void onCancel() { + delegate.onCancel(); + } + } } diff --git a/app/src/main/java/com/hippo/ehviewer/ui/DirPickerActivity.java b/app/src/main/java/com/hippo/ehviewer/ui/DirPickerActivity.java index 971f3f472..85de2e9b5 100644 --- a/app/src/main/java/com/hippo/ehviewer/ui/DirPickerActivity.java +++ b/app/src/main/java/com/hippo/ehviewer/ui/DirPickerActivity.java @@ -23,16 +23,19 @@ import android.view.MenuItem; import android.view.View; import android.widget.TextView; import android.widget.Toast; + import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.appcompat.app.AlertDialog; import androidx.core.content.ContextCompat; + import com.hippo.android.resource.AttrResources; import com.hippo.ehviewer.AppConfig; import com.hippo.ehviewer.R; import com.hippo.ripple.Ripple; import com.hippo.widget.DirExplorer; import com.hippo.yorozuya.FileUtils; + import java.io.File; public class DirPickerActivity extends ToolbarActivity diff --git a/app/src/main/java/com/hippo/ehviewer/ui/EhActivity.java b/app/src/main/java/com/hippo/ehviewer/ui/EhActivity.java index f18e114c8..ac2d7360d 100644 --- a/app/src/main/java/com/hippo/ehviewer/ui/EhActivity.java +++ b/app/src/main/java/com/hippo/ehviewer/ui/EhActivity.java @@ -20,16 +20,17 @@ import android.content.Context; import android.os.Build; import android.os.Bundle; import android.view.WindowManager; + import androidx.annotation.Nullable; import androidx.annotation.StyleRes; import androidx.appcompat.app.AppCompatActivity; -import com.google.firebase.analytics.FirebaseAnalytics; + import com.hippo.android.resource.AttrResources; import com.hippo.content.ContextLocalWrapper; -import com.hippo.ehviewer.Analytics; import com.hippo.ehviewer.EhApplication; import com.hippo.ehviewer.R; import com.hippo.ehviewer.Settings; + import java.util.Locale; public abstract class EhActivity extends AppCompatActivity { @@ -45,10 +46,6 @@ public abstract class EhActivity extends AppCompatActivity { ((EhApplication) getApplication()).registerActivity(this); - if (Analytics.isEnabled()) { - FirebaseAnalytics.getInstance(this); - } - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && Settings.getApplyNavBarThemeColor()) { getWindow().setNavigationBarColor(AttrResources.getAttrColor(this, R.attr.colorPrimaryDark)); } @@ -64,10 +61,10 @@ public abstract class EhActivity extends AppCompatActivity { @Override protected void onResume() { super.onResume(); - if(Settings.getEnabledSecurity()){ + if (Settings.getEnabledSecurity()) { getWindow().setFlags(WindowManager.LayoutParams.FLAG_SECURE, WindowManager.LayoutParams.FLAG_SECURE); - }else{ + } else { getWindow().clearFlags(WindowManager.LayoutParams.FLAG_SECURE); } } diff --git a/app/src/main/java/com/hippo/ehviewer/ui/EhPreferenceActivity.java b/app/src/main/java/com/hippo/ehviewer/ui/EhPreferenceActivity.java deleted file mode 100644 index 10f3706ab..000000000 --- a/app/src/main/java/com/hippo/ehviewer/ui/EhPreferenceActivity.java +++ /dev/null @@ -1,98 +0,0 @@ -/* - * Copyright 2016 Hippo Seven - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.hippo.ehviewer.ui; - -import android.content.Context; -import android.content.res.Resources; -import android.os.Build; -import android.os.Bundle; -import android.view.WindowManager; -import androidx.annotation.Nullable; -import androidx.annotation.StyleRes; -import com.google.firebase.analytics.FirebaseAnalytics; -import com.hippo.android.resource.AttrResources; -import com.hippo.app.PrettyPreferenceActivity; -import com.hippo.content.ContextLocalWrapper; -import com.hippo.ehviewer.Analytics; -import com.hippo.ehviewer.EhApplication; -import com.hippo.ehviewer.R; -import com.hippo.ehviewer.Settings; -import java.util.Locale; - -public abstract class EhPreferenceActivity extends PrettyPreferenceActivity { - - @StyleRes - protected abstract int getThemeResId(int theme); - - @Override - protected void onCreate(@Nullable Bundle savedInstanceState) { - setTheme(getThemeResId(Settings.getTheme())); - - super.onCreate(savedInstanceState); - - ((EhApplication) getApplication()).registerActivity(this); - - if (Analytics.isEnabled()) { - FirebaseAnalytics.getInstance(this); - } - - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && Settings.getApplyNavBarThemeColor()) { - getWindow().setNavigationBarColor(AttrResources.getAttrColor(this, R.attr.colorPrimaryDark)); - } - } - - @Override - protected void onDestroy() { - super.onDestroy(); - - ((EhApplication) getApplication()).unregisterActivity(this); - } - - @Override - protected void onResume() { - super.onResume(); - if(Settings.getEnabledSecurity()){ - getWindow().setFlags(WindowManager.LayoutParams.FLAG_SECURE, - WindowManager.LayoutParams.FLAG_SECURE); - }else{ - getWindow().clearFlags(WindowManager.LayoutParams.FLAG_SECURE); - } - } - - @Override - protected void attachBaseContext(Context newBase) { - Locale locale = null; - String language = Settings.getAppLanguage(); - if (language != null && !language.equals("system")) { - String[] split = language.split("-"); - if (split.length == 1) { - locale = new Locale(split[0]); - } else if (split.length == 2) { - locale = new Locale(split[0], split[1]); - } else if (split.length == 3) { - locale = new Locale(split[0], split[1], split[2]); - } - } - - if (locale == null) { - locale = Resources.getSystem().getConfiguration().locale; - } - - newBase = ContextLocalWrapper.wrap(newBase, locale); - super.attachBaseContext(newBase); - } -} diff --git a/app/src/main/java/com/hippo/ehviewer/ui/ExcludedLanguagesActivity.java b/app/src/main/java/com/hippo/ehviewer/ui/ExcludedLanguagesActivity.java index 93ad8d6d1..2da9cf2c8 100644 --- a/app/src/main/java/com/hippo/ehviewer/ui/ExcludedLanguagesActivity.java +++ b/app/src/main/java/com/hippo/ehviewer/ui/ExcludedLanguagesActivity.java @@ -21,10 +21,12 @@ import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; import android.widget.TextView; + import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; + import com.hippo.android.resource.AttrResources; import com.hippo.easyrecyclerview.EasyRecyclerView; import com.hippo.ehviewer.R; diff --git a/app/src/main/java/com/hippo/ehviewer/ui/FilterActivity.java b/app/src/main/java/com/hippo/ehviewer/ui/FilterActivity.java index 068641afb..69b642254 100644 --- a/app/src/main/java/com/hippo/ehviewer/ui/FilterActivity.java +++ b/app/src/main/java/com/hippo/ehviewer/ui/FilterActivity.java @@ -29,10 +29,12 @@ import android.widget.EditText; import android.widget.ImageView; import android.widget.Spinner; import android.widget.TextView; + import androidx.annotation.Nullable; import androidx.appcompat.app.AlertDialog; import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; + import com.google.android.material.textfield.TextInputLayout; import com.hippo.easyrecyclerview.EasyRecyclerView; import com.hippo.ehviewer.R; @@ -41,6 +43,7 @@ import com.hippo.ehviewer.dao.Filter; import com.hippo.util.DrawableManager; import com.hippo.view.ViewTransition; import com.hippo.yorozuya.ViewUtils; + import java.util.List; public class FilterActivity extends ToolbarActivity { diff --git a/app/src/main/java/com/hippo/ehviewer/ui/GalleryActivity.java b/app/src/main/java/com/hippo/ehviewer/ui/GalleryActivity.java index 587555f06..497371301 100644 --- a/app/src/main/java/com/hippo/ehviewer/ui/GalleryActivity.java +++ b/app/src/main/java/com/hippo/ehviewer/ui/GalleryActivity.java @@ -44,10 +44,12 @@ import android.widget.SeekBar; import android.widget.Spinner; import android.widget.TextView; import android.widget.Toast; + import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.appcompat.app.AlertDialog; import androidx.appcompat.widget.SwitchCompat; + import com.hippo.android.resource.AttrResources; import com.hippo.ehviewer.AppConfig; import com.hippo.ehviewer.BuildConfig; @@ -78,6 +80,7 @@ import com.hippo.yorozuya.ResourcesUtils; import com.hippo.yorozuya.SimpleAnimatorListener; import com.hippo.yorozuya.SimpleHandler; import com.hippo.yorozuya.ViewUtils; + import java.io.File; import java.io.FileInputStream; import java.io.IOException; @@ -101,14 +104,13 @@ public class GalleryActivity extends EhActivity implements SeekBar.OnSeekBarChan private static final long HIDE_SLIDER_DELAY = 3000; private static final int WRITE_REQUEST_CODE = 43; - + private final ConcurrentPool mNotifyTaskPool = new ConcurrentPool<>(3); private String mAction; private String mFilename; private Uri mUri; private GalleryInfo mGalleryInfo; private int mPage; private String mCacheFileName; - @Nullable private GLRootView mGLRootView; @Nullable @@ -117,11 +119,9 @@ public class GalleryActivity extends EhActivity implements SeekBar.OnSeekBarChan private GalleryProvider2 mGalleryProvider; @Nullable private GalleryAdapter mGalleryAdapter; - @Nullable private SystemUiHelper mSystemUiHelper; private boolean mShowSystemUi; - @Nullable private ColorView mMaskView; @Nullable @@ -132,21 +132,6 @@ public class GalleryActivity extends EhActivity implements SeekBar.OnSeekBarChan private View mBattery; @Nullable private View mSeekBarPanel; - @Nullable - private TextView mLeftText; - @Nullable - private TextView mRightText; - @Nullable - private ReversibleSeekBar mSeekBar; - - private ObjectAnimator mSeekBarPanelAnimator; - - private int mLayoutMode; - private int mSize; - private int mCurrentIndex; - - private final ConcurrentPool mNotifyTaskPool = new ConcurrentPool<>(3); - private final ValueAnimator.AnimatorUpdateListener mUpdateSliderListener = new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { @@ -155,14 +140,19 @@ public class GalleryActivity extends EhActivity implements SeekBar.OnSeekBarChan } } }; - + @Nullable + private TextView mLeftText; + @Nullable + private TextView mRightText; + @Nullable + private ReversibleSeekBar mSeekBar; + private ObjectAnimator mSeekBarPanelAnimator; private final SimpleAnimatorListener mShowSliderListener = new SimpleAnimatorListener() { @Override public void onAnimationEnd(Animator animation) { mSeekBarPanelAnimator = null; } }; - private final SimpleAnimatorListener mHideSliderListener = new SimpleAnimatorListener() { @Override public void onAnimationEnd(Animator animation) { @@ -172,7 +162,6 @@ public class GalleryActivity extends EhActivity implements SeekBar.OnSeekBarChan } } }; - private final Runnable mHideSliderRunnable = new Runnable() { @Override public void run() { @@ -181,6 +170,9 @@ public class GalleryActivity extends EhActivity implements SeekBar.OnSeekBarChan } } }; + private int mLayoutMode; + private int mSize; + private int mCurrentIndex; @Override protected int getThemeResId(int theme) { @@ -233,7 +225,7 @@ public class GalleryActivity extends EhActivity implements SeekBar.OnSeekBarChan private void onRestore(@NonNull Bundle savedInstanceState) { mAction = savedInstanceState.getString(KEY_ACTION); mFilename = savedInstanceState.getString(KEY_FILENAME); - mUri= savedInstanceState.getParcelable(KEY_URI); + mUri = savedInstanceState.getParcelable(KEY_URI); mGalleryInfo = savedInstanceState.getParcelable(KEY_GALLERY_INFO); mPage = savedInstanceState.getInt(KEY_PAGE, -1); mCurrentIndex = savedInstanceState.getInt(KEY_CURRENT_INDEX); @@ -735,6 +727,184 @@ public class GalleryActivity extends EhActivity implements SeekBar.OnSeekBarChan w.setAttributes(lp); } + private void shareImage(int page) { + if (null == mGalleryProvider) { + return; + } + + File dir = AppConfig.getExternalTempDir(); + if (null == dir) { + Toast.makeText(this, R.string.error_cant_create_temp_file, Toast.LENGTH_SHORT).show(); + return; + } + UniFile file; + if (null == (file = mGalleryProvider.save(page, UniFile.fromFile(dir), mGalleryProvider.getImageFilename(page)))) { + Toast.makeText(this, R.string.error_cant_save_image, Toast.LENGTH_SHORT).show(); + return; + } + String filename = file.getName(); + if (filename == null) { + Toast.makeText(this, R.string.error_cant_save_image, Toast.LENGTH_SHORT).show(); + return; + } + + String mimeType = MimeTypeMap.getSingleton().getMimeTypeFromExtension( + MimeTypeMap.getFileExtensionFromUrl(filename)); + if (TextUtils.isEmpty(mimeType)) { + mimeType = "image/jpeg"; + } + + Uri uri = new Uri.Builder() + .scheme(ContentResolver.SCHEME_CONTENT) + .authority(BuildConfig.FILE_PROVIDER_AUTHORITY) + .appendPath("temp") + .appendPath(filename) + .build(); + + Intent intent = new Intent(); + intent.setAction(Intent.ACTION_SEND); + intent.putExtra(Intent.EXTRA_STREAM, uri); + intent.setType(mimeType); + + try { + startActivity(Intent.createChooser(intent, getString(R.string.share_image))); + } catch (Throwable e) { + ExceptionUtils.throwIfFatal(e); + Toast.makeText(this, R.string.error_cant_find_activity, Toast.LENGTH_SHORT).show(); + } + } + + private void saveImage(int page) { + if (null == mGalleryProvider) { + return; + } + + File dir = AppConfig.getExternalImageDir(); + if (null == dir) { + Toast.makeText(this, R.string.error_cant_save_image, Toast.LENGTH_SHORT).show(); + return; + } + UniFile file; + if (null == (file = mGalleryProvider.save(page, UniFile.fromFile(dir), mGalleryProvider.getImageFilename(page)))) { + Toast.makeText(this, R.string.error_cant_save_image, Toast.LENGTH_SHORT).show(); + return; + } + + Toast.makeText(this, getString(R.string.image_saved, file.getUri()), Toast.LENGTH_SHORT).show(); + + // Sync media store + sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, file.getUri())); + } + + private void saveImageTo(int page) { + if (null == mGalleryProvider) { + return; + } + File dir = getCacheDir(); + UniFile file; + if (null == (file = mGalleryProvider.save(page, UniFile.fromFile(dir), mGalleryProvider.getImageFilename(page)))) { + Toast.makeText(this, R.string.error_cant_save_image, Toast.LENGTH_SHORT).show(); + return; + } + String filename = file.getName(); + if (filename == null) { + Toast.makeText(this, R.string.error_cant_save_image, Toast.LENGTH_SHORT).show(); + return; + } + mCacheFileName = filename; + Intent intent = new Intent(Intent.ACTION_CREATE_DOCUMENT); + intent.addCategory(Intent.CATEGORY_OPENABLE); + intent.setType("image/*"); + intent.putExtra(Intent.EXTRA_TITLE, filename); + try { + startActivityForResult(intent, WRITE_REQUEST_CODE); + } catch (Throwable e) { + ExceptionUtils.throwIfFatal(e); + Toast.makeText(this, R.string.error_cant_find_activity, Toast.LENGTH_SHORT).show(); + } + } + + @Override + public void onActivityResult(int requestCode, int resultCode, Intent resultData) { + if (requestCode == WRITE_REQUEST_CODE && resultCode == Activity.RESULT_OK) { + if (resultData != null) { + Uri uri = resultData.getData(); + String filepath = getCacheDir() + "/" + mCacheFileName; + File cachefile = new File(filepath); + + InputStream is = null; + OutputStream os = null; + ContentResolver resolver = getContentResolver(); + + try { + is = new FileInputStream(cachefile); + os = resolver.openOutputStream(uri); + IOUtils.copy(is, os); + } catch (IOException e) { + e.printStackTrace(); + } finally { + IOUtils.closeQuietly(is); + IOUtils.closeQuietly(os); + } + + cachefile.delete(); + + Toast.makeText(this, getString(R.string.image_saved, uri.getPath()), Toast.LENGTH_SHORT).show(); + // Sync media store + sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, uri)); + } + } + } + + private void showPageDialog(final int page) { + Resources resources = GalleryActivity.this.getResources(); + AlertDialog.Builder builder = new AlertDialog.Builder(GalleryActivity.this); + builder.setTitle(resources.getString(R.string.page_menu_title, page + 1)); + + final CharSequence[] items; + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { + items = new CharSequence[]{ + getString(R.string.page_menu_refresh), + getString(R.string.page_menu_share), + getString(R.string.page_menu_save), + getString(R.string.page_menu_save_to)}; + } else { + items = new CharSequence[]{ + getString(R.string.page_menu_refresh), + getString(R.string.page_menu_share), + getString(R.string.page_menu_save)}; + } + pageDialogListener(builder, items, page); + builder.show(); + } + + private void pageDialogListener(AlertDialog.Builder builder, CharSequence[] items, int page) { + builder.setItems(items, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + if (mGalleryProvider == null) { + return; + } + + switch (which) { + case 0: // Refresh + mGalleryProvider.removeCache(page); + mGalleryProvider.forceRequest(page); + break; + case 1: // Share + shareImage(page); + break; + case 2: // Save + saveImage(page); + break; + case 3: // Save to + saveImageTo(page); + break; + } + } + }); + } + private class GalleryMenuHelper implements DialogInterface.OnClickListener { private final View mView; @@ -880,184 +1050,6 @@ public class GalleryActivity extends EhActivity implements SeekBar.OnSeekBarChan } } - private void shareImage(int page) { - if (null == mGalleryProvider) { - return; - } - - File dir = AppConfig.getExternalTempDir(); - if (null == dir) { - Toast.makeText(this, R.string.error_cant_create_temp_file, Toast.LENGTH_SHORT).show(); - return; - } - UniFile file; - if (null == (file = mGalleryProvider.save(page, UniFile.fromFile(dir), mGalleryProvider.getImageFilename(page)))) { - Toast.makeText(this, R.string.error_cant_save_image, Toast.LENGTH_SHORT).show(); - return; - } - String filename = file.getName(); - if (filename == null) { - Toast.makeText(this, R.string.error_cant_save_image, Toast.LENGTH_SHORT).show(); - return; - } - - String mimeType = MimeTypeMap.getSingleton().getMimeTypeFromExtension( - MimeTypeMap.getFileExtensionFromUrl(filename)); - if (TextUtils.isEmpty(mimeType)) { - mimeType = "image/jpeg"; - } - - Uri uri = new Uri.Builder() - .scheme(ContentResolver.SCHEME_CONTENT) - .authority(BuildConfig.FILE_PROVIDER_AUTHORITY) - .appendPath("temp") - .appendPath(filename) - .build(); - - Intent intent = new Intent(); - intent.setAction(Intent.ACTION_SEND); - intent.putExtra(Intent.EXTRA_STREAM, uri); - intent.setType(mimeType); - - try { - startActivity(Intent.createChooser(intent, getString(R.string.share_image))); - } catch (Throwable e) { - ExceptionUtils.throwIfFatal(e); - Toast.makeText(this, R.string.error_cant_find_activity, Toast.LENGTH_SHORT).show(); - } - } - - private void saveImage(int page) { - if (null == mGalleryProvider) { - return; - } - - File dir = AppConfig.getExternalImageDir(); - if (null == dir) { - Toast.makeText(this, R.string.error_cant_save_image, Toast.LENGTH_SHORT).show(); - return; - } - UniFile file; - if (null == (file = mGalleryProvider.save(page, UniFile.fromFile(dir), mGalleryProvider.getImageFilename(page)))) { - Toast.makeText(this, R.string.error_cant_save_image, Toast.LENGTH_SHORT).show(); - return; - } - - Toast.makeText(this, getString(R.string.image_saved, file.getUri()), Toast.LENGTH_SHORT).show(); - - // Sync media store - sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, file.getUri())); - } - - private void saveImageTo(int page) { - if (null == mGalleryProvider) { - return; - } - File dir = getCacheDir(); - UniFile file; - if (null == (file = mGalleryProvider.save(page, UniFile.fromFile(dir), mGalleryProvider.getImageFilename(page)))) { - Toast.makeText(this, R.string.error_cant_save_image, Toast.LENGTH_SHORT).show(); - return; - } - String filename = file.getName(); - if (filename == null) { - Toast.makeText(this, R.string.error_cant_save_image, Toast.LENGTH_SHORT).show(); - return; - } - mCacheFileName = filename; - Intent intent = new Intent(Intent.ACTION_CREATE_DOCUMENT); - intent.addCategory(Intent.CATEGORY_OPENABLE); - intent.setType("image/*"); - intent.putExtra(Intent.EXTRA_TITLE, filename); - try { - startActivityForResult(intent, WRITE_REQUEST_CODE); - } catch (Throwable e) { - ExceptionUtils.throwIfFatal(e); - Toast.makeText(this, R.string.error_cant_find_activity, Toast.LENGTH_SHORT).show(); - } - } - - @Override - public void onActivityResult(int requestCode, int resultCode, Intent resultData) { - if (requestCode == WRITE_REQUEST_CODE && resultCode == Activity.RESULT_OK) { - if (resultData != null){ - Uri uri = resultData.getData(); - String filepath = getCacheDir() + "/" + mCacheFileName; - File cachefile = new File(filepath); - - InputStream is = null; - OutputStream os = null; - ContentResolver resolver = getContentResolver(); - - try { - is = new FileInputStream(cachefile); - os = resolver.openOutputStream(uri); - IOUtils.copy(is, os); - } catch (IOException e) { - e.printStackTrace(); - } finally { - IOUtils.closeQuietly(is); - IOUtils.closeQuietly(os); - } - - cachefile.delete(); - - Toast.makeText(this, getString(R.string.image_saved, uri.getPath()), Toast.LENGTH_SHORT).show(); - // Sync media store - sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, uri)); - } - } - } - - private void showPageDialog(final int page) { - Resources resources = GalleryActivity.this.getResources(); - AlertDialog.Builder builder = new AlertDialog.Builder(GalleryActivity.this); - builder.setTitle(resources.getString(R.string.page_menu_title, page + 1)); - - final CharSequence[] items; - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT){ - items = new CharSequence[]{ - getString(R.string.page_menu_refresh), - getString(R.string.page_menu_share), - getString(R.string.page_menu_save), - getString(R.string.page_menu_save_to)}; - }else { - items = new CharSequence[]{ - getString(R.string.page_menu_refresh), - getString(R.string.page_menu_share), - getString(R.string.page_menu_save)}; - } - pageDialogListener(builder, items, page); - builder.show(); - } - - private void pageDialogListener(AlertDialog.Builder builder, CharSequence[] items, int page){ - builder.setItems(items, new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - if (mGalleryProvider == null) { - return; - } - - switch (which) { - case 0: // Refresh - mGalleryProvider.removeCache(page); - mGalleryProvider.forceRequest(page); - break; - case 1: // Share - shareImage(page); - break; - case 2: // Save - saveImage(page); - break; - case 3: // Save to - saveImageTo(page); - break; - } - } - }); - } - private class NotifyTask implements Runnable { public static final int KEY_LAYOUT_MODE = 0; diff --git a/app/src/main/java/com/hippo/ehviewer/ui/HostsActivity.java b/app/src/main/java/com/hippo/ehviewer/ui/HostsActivity.java index f1790c740..02eeb2b66 100644 --- a/app/src/main/java/com/hippo/ehviewer/ui/HostsActivity.java +++ b/app/src/main/java/com/hippo/ehviewer/ui/HostsActivity.java @@ -31,12 +31,14 @@ import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; import android.widget.TextView; + import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.appcompat.app.AlertDialog; import androidx.fragment.app.DialogFragment; import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; + import com.google.android.material.floatingactionbutton.FloatingActionButton; import com.google.android.material.textfield.TextInputLayout; import com.hippo.android.resource.AttrResources; @@ -47,229 +49,230 @@ import com.hippo.ehviewer.Hosts; import com.hippo.ehviewer.R; import com.hippo.ripple.Ripple; import com.hippo.yorozuya.LayoutUtils; + import java.util.List; import java.util.Locale; public class HostsActivity extends ToolbarActivity - implements EasyRecyclerView.OnItemClickListener, View.OnClickListener { + implements EasyRecyclerView.OnItemClickListener, View.OnClickListener { - private static final String DIALOG_TAG_ADD_HOST = AddHostDialogFragment.class.getName(); - private static final String DIALOG_TAG_EDIT_HOST = EditHostDialogFragment.class.getName(); + private static final String DIALOG_TAG_ADD_HOST = AddHostDialogFragment.class.getName(); + private static final String DIALOG_TAG_EDIT_HOST = EditHostDialogFragment.class.getName(); - private static final String KEY_HOST = "com.hippo.ehviewer.ui.HostsActivity.HOST"; - private static final String KEY_IP = "com.hippo.ehviewer.ui.HostsActivity.IP"; + private static final String KEY_HOST = "com.hippo.ehviewer.ui.HostsActivity.HOST"; + private static final String KEY_IP = "com.hippo.ehviewer.ui.HostsActivity.IP"; - private Hosts hosts; - private List> data; + private Hosts hosts; + private List> data; - private EasyRecyclerView recyclerView; - private View tip; - private HostsAdapter adapter; + private EasyRecyclerView recyclerView; + private View tip; + private HostsAdapter adapter; - @Override - protected void onCreate(@Nullable Bundle savedInstanceState) { - super.onCreate(savedInstanceState); + @Override + protected void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); - hosts = EhApplication.getHosts(this); - data = hosts.getAll(); + hosts = EhApplication.getHosts(this); + data = hosts.getAll(); - setContentView(R.layout.activity_hosts); - setNavigationIcon(R.drawable.v_arrow_left_dark_x24); - recyclerView = findViewById(R.id.recycler_view); - tip = findViewById(R.id.tip); - FloatingActionButton fab = findViewById(R.id.fab); + setContentView(R.layout.activity_hosts); + setNavigationIcon(R.drawable.v_arrow_left_dark_x24); + recyclerView = findViewById(R.id.recycler_view); + tip = findViewById(R.id.tip); + FloatingActionButton fab = findViewById(R.id.fab); - adapter = new HostsAdapter(); - recyclerView.setAdapter(adapter); - recyclerView.setLayoutManager(new LinearLayoutManager(this, RecyclerView.VERTICAL, false)); - LinearDividerItemDecoration decoration = new LinearDividerItemDecoration( - LinearDividerItemDecoration.VERTICAL, - AttrResources.getAttrColor(this, R.attr.dividerColor), - LayoutUtils.dp2pix(this, 1)); - decoration.setShowLastDivider(true); - recyclerView.addItemDecoration(decoration); - recyclerView.setSelector(Ripple.generateRippleDrawable(this, !AttrResources.getAttrBoolean(this, R.attr.isLightTheme), new ColorDrawable(Color.TRANSPARENT))); - recyclerView.setHasFixedSize(true); - recyclerView.setOnItemClickListener(this); - recyclerView.setPadding( - recyclerView.getPaddingLeft(), - recyclerView.getPaddingTop(), - recyclerView.getPaddingRight(), - recyclerView.getPaddingBottom() + getResources().getDimensionPixelOffset(R.dimen.gallery_padding_bottom_fab)); + adapter = new HostsAdapter(); + recyclerView.setAdapter(adapter); + recyclerView.setLayoutManager(new LinearLayoutManager(this, RecyclerView.VERTICAL, false)); + LinearDividerItemDecoration decoration = new LinearDividerItemDecoration( + LinearDividerItemDecoration.VERTICAL, + AttrResources.getAttrColor(this, R.attr.dividerColor), + LayoutUtils.dp2pix(this, 1)); + decoration.setShowLastDivider(true); + recyclerView.addItemDecoration(decoration); + recyclerView.setSelector(Ripple.generateRippleDrawable(this, !AttrResources.getAttrBoolean(this, R.attr.isLightTheme), new ColorDrawable(Color.TRANSPARENT))); + recyclerView.setHasFixedSize(true); + recyclerView.setOnItemClickListener(this); + recyclerView.setPadding( + recyclerView.getPaddingLeft(), + recyclerView.getPaddingTop(), + recyclerView.getPaddingRight(), + recyclerView.getPaddingBottom() + getResources().getDimensionPixelOffset(R.dimen.gallery_padding_bottom_fab)); - fab.setOnClickListener(this); + fab.setOnClickListener(this); - recyclerView.setVisibility(data.isEmpty() ? View.GONE : View.VISIBLE); - tip.setVisibility(data.isEmpty() ? View.VISIBLE : View.GONE); - } + recyclerView.setVisibility(data.isEmpty() ? View.GONE : View.VISIBLE); + tip.setVisibility(data.isEmpty() ? View.VISIBLE : View.GONE); + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + switch (item.getItemId()) { + case android.R.id.home: + finish(); + return true; + default: + return super.onOptionsItemSelected(item); + } + } + + @Override + public boolean onItemClick(EasyRecyclerView easyRecyclerView, View view, int position, long id) { + Pair pair = data.get(position); + Bundle args = new Bundle(); + args.putString(KEY_HOST, pair.first); + args.putString(KEY_IP, pair.second); + + DialogFragment fragment = new EditHostDialogFragment(); + fragment.setArguments(args); + fragment.show(getSupportFragmentManager(), DIALOG_TAG_EDIT_HOST); - @Override - public boolean onOptionsItemSelected(MenuItem item) { - switch (item.getItemId()) { - case android.R.id.home: - finish(); return true; - default: - return super.onOptionsItemSelected(item); - } - } - - @Override - public boolean onItemClick(EasyRecyclerView easyRecyclerView, View view, int position, long id) { - Pair pair = data.get(position); - Bundle args = new Bundle(); - args.putString(KEY_HOST, pair.first); - args.putString(KEY_IP, pair.second); - - DialogFragment fragment = new EditHostDialogFragment(); - fragment.setArguments(args); - fragment.show(getSupportFragmentManager(), DIALOG_TAG_EDIT_HOST); - - return true; - } - - @Override - public void onClick(View v) { - new AddHostDialogFragment().show(getSupportFragmentManager(), DIALOG_TAG_ADD_HOST); - } - - private void notifyHostsChanges() { - data = hosts.getAll(); - recyclerView.setVisibility(data.isEmpty() ? View.GONE : View.VISIBLE); - tip.setVisibility(data.isEmpty() ? View.VISIBLE : View.GONE); - adapter.notifyDataSetChanged(); - } - - private class HostsHolder extends RecyclerView.ViewHolder { - - public final TextView host; - public final TextView ip; - - public HostsHolder(View itemView) { - super(itemView); - host = itemView.findViewById(R.id.host); - ip = itemView.findViewById(R.id.ip); - } - } - - private class HostsAdapter extends RecyclerView.Adapter { - - private final LayoutInflater inflater = getLayoutInflater(); - - @NonNull - @Override - public HostsHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { - return new HostsHolder(inflater.inflate(R.layout.item_hosts, parent, false)); } @Override - public void onBindViewHolder(@NonNull HostsHolder holder, int position) { - Pair pair = data.get(position); - holder.host.setText(pair.first); - holder.ip.setText(pair.second); + public void onClick(View v) { + new AddHostDialogFragment().show(getSupportFragmentManager(), DIALOG_TAG_ADD_HOST); } - @Override - public int getItemCount() { - return data.size(); - } - } - - public abstract static class HostDialogFragment extends DialogFragment { - - private TextView host; - private TextView ip; - - @NonNull - @Override - public Dialog onCreateDialog(Bundle savedInstanceState) { - View view = getActivity().getLayoutInflater().inflate(R.layout.dialog_hosts, null, false); - host = view.findViewById(R.id.host); - ip = view.findViewById(R.id.ip); - - Bundle arguments = getArguments(); - if (savedInstanceState == null && arguments != null) { - host.setText(arguments.getString(KEY_HOST)); - ip.setText(arguments.getString(KEY_IP)); - } - - AlertDialog.Builder builder = new AlertDialog.Builder(getContext()).setView(view); - onCreateDialogBuilder(builder); - AlertDialog dialog = builder.create(); - dialog.setOnShowListener(d -> onCreateDialog((AlertDialog) d)); - - return dialog; + private void notifyHostsChanges() { + data = hosts.getAll(); + recyclerView.setVisibility(data.isEmpty() ? View.GONE : View.VISIBLE); + tip.setVisibility(data.isEmpty() ? View.VISIBLE : View.GONE); + adapter.notifyDataSetChanged(); } - protected abstract void onCreateDialogBuilder(AlertDialog.Builder builder); + public abstract static class HostDialogFragment extends DialogFragment { - protected abstract void onCreateDialog(AlertDialog dialog); + private TextView host; + private TextView ip; - protected void put(AlertDialog dialog) { - TextView host = dialog.findViewById(R.id.host); - TextView ip = dialog.findViewById(R.id.ip); - String hostString = host.getText().toString().trim().toLowerCase(Locale.US); - String ipString = ip.getText().toString().trim(); + @NonNull + @Override + public Dialog onCreateDialog(Bundle savedInstanceState) { + View view = getActivity().getLayoutInflater().inflate(R.layout.dialog_hosts, null, false); + host = view.findViewById(R.id.host); + ip = view.findViewById(R.id.ip); - if (!Hosts.isValidHost(hostString)) { - TextInputLayout hostInputLayout = dialog.findViewById(R.id.host_input_layout); - hostInputLayout.setError(getContext().getString(R.string.invalid_host)); - return; - } + Bundle arguments = getArguments(); + if (savedInstanceState == null && arguments != null) { + host.setText(arguments.getString(KEY_HOST)); + ip.setText(arguments.getString(KEY_IP)); + } - if (!Hosts.isValidIp(ipString)) { - TextInputLayout ipInputLayout = dialog.findViewById(R.id.ip_input_layout); - ipInputLayout.setError(getContext().getString(R.string.invalid_ip)); - return; - } + AlertDialog.Builder builder = new AlertDialog.Builder(getContext()).setView(view); + onCreateDialogBuilder(builder); + AlertDialog dialog = builder.create(); + dialog.setOnShowListener(d -> onCreateDialog((AlertDialog) d)); - HostsActivity activity = (HostsActivity) dialog.getOwnerActivity(); - activity.hosts.put(hostString, ipString); - activity.notifyHostsChanges(); + return dialog; + } - dialog.dismiss(); + protected abstract void onCreateDialogBuilder(AlertDialog.Builder builder); + + protected abstract void onCreateDialog(AlertDialog dialog); + + protected void put(AlertDialog dialog) { + TextView host = dialog.findViewById(R.id.host); + TextView ip = dialog.findViewById(R.id.ip); + String hostString = host.getText().toString().trim().toLowerCase(Locale.US); + String ipString = ip.getText().toString().trim(); + + if (!Hosts.isValidHost(hostString)) { + TextInputLayout hostInputLayout = dialog.findViewById(R.id.host_input_layout); + hostInputLayout.setError(getContext().getString(R.string.invalid_host)); + return; + } + + if (!Hosts.isValidIp(ipString)) { + TextInputLayout ipInputLayout = dialog.findViewById(R.id.ip_input_layout); + ipInputLayout.setError(getContext().getString(R.string.invalid_ip)); + return; + } + + HostsActivity activity = (HostsActivity) dialog.getOwnerActivity(); + activity.hosts.put(hostString, ipString); + activity.notifyHostsChanges(); + + dialog.dismiss(); + } + + protected void delete(AlertDialog dialog) { + TextView host = dialog.findViewById(R.id.host); + String hostString = host.getText().toString().trim().toLowerCase(Locale.US); + + HostsActivity activity = (HostsActivity) dialog.getOwnerActivity(); + activity.hosts.delete(hostString); + activity.notifyHostsChanges(); + + dialog.dismiss(); + } } - protected void delete(AlertDialog dialog) { - TextView host = dialog.findViewById(R.id.host); - String hostString = host.getText().toString().trim().toLowerCase(Locale.US); + public static class AddHostDialogFragment extends HostDialogFragment { - HostsActivity activity = (HostsActivity) dialog.getOwnerActivity(); - activity.hosts.delete(hostString); - activity.notifyHostsChanges(); + @Override + protected void onCreateDialogBuilder(AlertDialog.Builder builder) { + builder.setTitle(R.string.add_host); + builder.setPositiveButton(R.string.add_host_add, null); + } - dialog.dismiss(); - } - } - - public static class AddHostDialogFragment extends HostDialogFragment { - - @Override - protected void onCreateDialogBuilder(AlertDialog.Builder builder) { - builder.setTitle(R.string.add_host); - builder.setPositiveButton(R.string.add_host_add, null); + @Override + protected void onCreateDialog(AlertDialog dialog) { + dialog.getButton(DialogInterface.BUTTON_POSITIVE).setOnClickListener(v -> put(dialog)); + } } - @Override - protected void onCreateDialog(AlertDialog dialog) { - dialog.getButton(DialogInterface.BUTTON_POSITIVE).setOnClickListener(v -> put(dialog)); - } - } + public static class EditHostDialogFragment extends HostDialogFragment { - public static class EditHostDialogFragment extends HostDialogFragment { + @Override + protected void onCreateDialogBuilder(AlertDialog.Builder builder) { + builder.setTitle(R.string.edit_host); + builder.setPositiveButton(R.string.edit_host_confirm, null); + builder.setNegativeButton(R.string.edit_host_delete, null); + } - @Override - protected void onCreateDialogBuilder(AlertDialog.Builder builder) { - builder.setTitle(R.string.edit_host); - builder.setPositiveButton(R.string.edit_host_confirm, null); - builder.setNegativeButton(R.string.edit_host_delete, null); + @Override + protected void onCreateDialog(AlertDialog dialog) { + dialog.findViewById(R.id.host_input_layout).setEnabled(false); + dialog.getButton(DialogInterface.BUTTON_POSITIVE).setOnClickListener(v -> put(dialog)); + dialog.getButton(DialogInterface.BUTTON_NEGATIVE).setOnClickListener(v -> delete(dialog)); + } } - @Override - protected void onCreateDialog(AlertDialog dialog) { - dialog.findViewById(R.id.host_input_layout).setEnabled(false); - dialog.getButton(DialogInterface.BUTTON_POSITIVE).setOnClickListener(v -> put(dialog)); - dialog.getButton(DialogInterface.BUTTON_NEGATIVE).setOnClickListener(v -> delete(dialog)); + private class HostsHolder extends RecyclerView.ViewHolder { + + public final TextView host; + public final TextView ip; + + public HostsHolder(View itemView) { + super(itemView); + host = itemView.findViewById(R.id.host); + ip = itemView.findViewById(R.id.ip); + } + } + + private class HostsAdapter extends RecyclerView.Adapter { + + private final LayoutInflater inflater = getLayoutInflater(); + + @NonNull + @Override + public HostsHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { + return new HostsHolder(inflater.inflate(R.layout.item_hosts, parent, false)); + } + + @Override + public void onBindViewHolder(@NonNull HostsHolder holder, int position) { + Pair pair = data.get(position); + holder.host.setText(pair.first); + holder.ip.setText(pair.second); + } + + @Override + public int getItemCount() { + return data.size(); + } } - } } diff --git a/app/src/main/java/com/hippo/ehviewer/ui/LicenseActivity.java b/app/src/main/java/com/hippo/ehviewer/ui/LicenseActivity.java index 0f335e591..ae44e323f 100644 --- a/app/src/main/java/com/hippo/ehviewer/ui/LicenseActivity.java +++ b/app/src/main/java/com/hippo/ehviewer/ui/LicenseActivity.java @@ -19,7 +19,9 @@ package com.hippo.ehviewer.ui; import android.os.Bundle; import android.view.MenuItem; import android.webkit.WebView; + import androidx.annotation.Nullable; + import com.hippo.ehviewer.R; public class LicenseActivity extends ToolbarActivity { diff --git a/app/src/main/java/com/hippo/ehviewer/ui/MainActivity.java b/app/src/main/java/com/hippo/ehviewer/ui/MainActivity.java index 541984c35..587e02cad 100644 --- a/app/src/main/java/com/hippo/ehviewer/ui/MainActivity.java +++ b/app/src/main/java/com/hippo/ehviewer/ui/MainActivity.java @@ -36,14 +36,16 @@ import android.widget.Button; import android.widget.FrameLayout; import android.widget.TextView; import android.widget.Toast; + import androidx.annotation.IdRes; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.annotation.StringRes; import androidx.appcompat.app.AlertDialog; +import androidx.drawerlayout.widget.DrawerLayout; + import com.google.android.material.navigation.NavigationView; import com.google.android.material.snackbar.Snackbar; -import com.hippo.drawerlayout.DrawerLayout; import com.hippo.ehviewer.AppConfig; import com.hippo.ehviewer.EhApplication; import com.hippo.ehviewer.R; @@ -85,9 +87,9 @@ import com.hippo.util.BitmapUtils; import com.hippo.util.PermissionRequester; import com.hippo.widget.LoadImageView; import com.hippo.yorozuya.IOUtils; -import com.hippo.yorozuya.ResourcesUtils; import com.hippo.yorozuya.SimpleHandler; import com.hippo.yorozuya.ViewUtils; + import java.io.File; import java.io.FileOutputStream; import java.io.IOException; @@ -103,24 +105,6 @@ public final class MainActivity extends StageActivity private static final String KEY_NAV_CHECKED_ITEM = "nav_checked_item"; private static final String KEY_CLIP_TEXT_HASH_CODE = "clip_text_hash_code"; - /*--------------- - Whole life cycle - ---------------*/ - @Nullable - private EhDrawerLayout mDrawerLayout; - @Nullable - private NavigationView mNavView; - @Nullable - private FrameLayout mRightDrawer; - @Nullable - private LoadImageView mAvatar; - @Nullable - private TextView mDisplayName; - @Nullable - private Button mChangeTheme; - - private int mNavCheckedItem = 0; - static { registerLaunchMode(SecurityScene.class, SceneFragment.LAUNCH_MODE_SINGLE_TASK); registerLaunchMode(WarningScene.class, SceneFragment.LAUNCH_MODE_SINGLE_TASK); @@ -142,6 +126,23 @@ public final class MainActivity extends StageActivity registerLaunchMode(ProgressScene.class, SceneFragment.LAUNCH_MODE_STANDARD); } + /*--------------- + Whole life cycle + ---------------*/ + @Nullable + private EhDrawerLayout mDrawerLayout; + @Nullable + private NavigationView mNavView; + @Nullable + private FrameLayout mRightDrawer; + @Nullable + private LoadImageView mAvatar; + @Nullable + private TextView mDisplayName; + @Nullable + private Button mChangeTheme; + private int mNavCheckedItem = 0; + @Override protected int getThemeResId(int theme) { switch (theme) { @@ -334,7 +335,7 @@ public final class MainActivity extends StageActivity mDisplayName = (TextView) ViewUtils.$$(headerLayout, R.id.display_name); mChangeTheme = (Button) ViewUtils.$$(this, R.id.change_theme); - mDrawerLayout.setStatusBarColor(ResourcesUtils.getAttrColor(this, R.attr.colorPrimaryDark)); + //mDrawerLayout.setStatusBarColor(ResourcesUtils.getAttrColor(this, R.attr.colorPrimaryDark)); // Pre-L need shadow drawable if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) { mDrawerLayout.setDrawerShadow(R.drawable.drawer_shadow_left, Gravity.LEFT); @@ -524,7 +525,7 @@ public final class MainActivity extends StageActivity @Override public void onRequestPermissionsResult(int requestCode, - @NonNull String[] permissions, @NonNull int[] grantResults) { + @NonNull String[] permissions, @NonNull int[] grantResults) { if (requestCode == PERMISSION_REQUEST_WRITE_EXTERNAL_STORAGE) { if (grantResults.length == 1 && grantResults[0] != PackageManager.PERMISSION_GRANTED) { Toast.makeText(this, R.string.you_rejected_me, Toast.LENGTH_SHORT).show(); @@ -629,15 +630,9 @@ public final class MainActivity extends StageActivity } } - public void setDrawerGestureBlocker(DrawerLayout.GestureBlocker gestureBlocker) { - if (mDrawerLayout != null) { - mDrawerLayout.setGestureBlocker(gestureBlocker); - } - } - public boolean isDrawersVisible() { if (mDrawerLayout != null) { - return mDrawerLayout.isDrawersVisible(); + return mDrawerLayout.isDrawerVisible(mDrawerLayout); } else { return false; } diff --git a/app/src/main/java/com/hippo/ehviewer/ui/MyTagsActivity.java b/app/src/main/java/com/hippo/ehviewer/ui/MyTagsActivity.java index 7a1d07e13..5f3d7484a 100644 --- a/app/src/main/java/com/hippo/ehviewer/ui/MyTagsActivity.java +++ b/app/src/main/java/com/hippo/ehviewer/ui/MyTagsActivity.java @@ -26,84 +26,87 @@ import android.webkit.CookieManager; import android.webkit.CookieSyncManager; import android.webkit.WebView; import android.webkit.WebViewClient; + import androidx.annotation.Nullable; + import com.hippo.ehviewer.EhApplication; import com.hippo.ehviewer.R; import com.hippo.ehviewer.client.EhCookieStore; import com.hippo.ehviewer.client.EhUrl; import com.hippo.ehviewer.widget.DialogWebChromeClient; import com.hippo.widget.ProgressView; + import okhttp3.Cookie; import okhttp3.HttpUrl; public class MyTagsActivity extends ToolbarActivity { - private WebView webView; - private ProgressView progress; - private String url; + private WebView webView; + private ProgressView progress; + private String url; - @SuppressLint("SetJavaScriptEnabled") - @Override - protected void onCreate(@Nullable Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - - // http://stackoverflow.com/questions/32284642/how-to-handle-an-uncatched-exception - CookieManager cookieManager = CookieManager.getInstance(); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { - cookieManager.flush(); - cookieManager.removeAllCookies(null); - cookieManager.removeSessionCookies(null); - } else { - CookieSyncManager cookieSyncManager = CookieSyncManager.createInstance(this); - cookieSyncManager.startSync(); - cookieManager.removeAllCookie(); - cookieManager.removeSessionCookie(); - cookieSyncManager.stopSync(); - } - - // Copy cookies from okhttp cookie store to CookieManager - url = EhUrl.getMyTagsUrl(); - EhCookieStore store = EhApplication.getEhCookieStore(this); - for (Cookie cookie : store.getCookies(HttpUrl.parse(url))) { - cookieManager.setCookie(url, cookie.toString()); - } - - setContentView(R.layout.activity_my_tags); - setNavigationIcon(R.drawable.v_arrow_left_dark_x24); - webView = findViewById(R.id.webview); - webView.getSettings().setJavaScriptEnabled(true); - webView.setWebViewClient(new MyTagsWebViewClient()); - webView.setWebChromeClient(new DialogWebChromeClient(this)); - webView.loadUrl(url); - progress = findViewById(R.id.progress); - } - - @Override - public boolean onOptionsItemSelected(MenuItem item) { - switch (item.getItemId()) { - case android.R.id.home: - finish(); - return true; - default: - return super.onOptionsItemSelected(item); - } - } - - private class MyTagsWebViewClient extends WebViewClient { + @SuppressLint("SetJavaScriptEnabled") @Override - public boolean shouldOverrideUrlLoading(WebView view, String url) { - // Never load other urls - return !url.equals(MyTagsActivity.this.url); + protected void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + // http://stackoverflow.com/questions/32284642/how-to-handle-an-uncatched-exception + CookieManager cookieManager = CookieManager.getInstance(); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + cookieManager.flush(); + cookieManager.removeAllCookies(null); + cookieManager.removeSessionCookies(null); + } else { + CookieSyncManager cookieSyncManager = CookieSyncManager.createInstance(this); + cookieSyncManager.startSync(); + cookieManager.removeAllCookie(); + cookieManager.removeSessionCookie(); + cookieSyncManager.stopSync(); + } + + // Copy cookies from okhttp cookie store to CookieManager + url = EhUrl.getMyTagsUrl(); + EhCookieStore store = EhApplication.getEhCookieStore(this); + for (Cookie cookie : store.getCookies(HttpUrl.parse(url))) { + cookieManager.setCookie(url, cookie.toString()); + } + + setContentView(R.layout.activity_my_tags); + setNavigationIcon(R.drawable.v_arrow_left_dark_x24); + webView = findViewById(R.id.webview); + webView.getSettings().setJavaScriptEnabled(true); + webView.setWebViewClient(new MyTagsWebViewClient()); + webView.setWebChromeClient(new DialogWebChromeClient(this)); + webView.loadUrl(url); + progress = findViewById(R.id.progress); } @Override - public void onPageStarted(WebView view, String url, Bitmap favicon) { - progress.setVisibility(View.VISIBLE); + public boolean onOptionsItemSelected(MenuItem item) { + switch (item.getItemId()) { + case android.R.id.home: + finish(); + return true; + default: + return super.onOptionsItemSelected(item); + } } - @Override - public void onPageFinished(WebView view, String url) { - progress.setVisibility(View.GONE); + private class MyTagsWebViewClient extends WebViewClient { + @Override + public boolean shouldOverrideUrlLoading(WebView view, String url) { + // Never load other urls + return !url.equals(MyTagsActivity.this.url); + } + + @Override + public void onPageStarted(WebView view, String url, Bitmap favicon) { + progress.setVisibility(View.VISIBLE); + } + + @Override + public void onPageFinished(WebView view, String url) { + progress.setVisibility(View.GONE); + } } - } } diff --git a/app/src/main/java/com/hippo/ehviewer/ui/SetSecurityActivity.java b/app/src/main/java/com/hippo/ehviewer/ui/SetSecurityActivity.java index 30041fab0..3202e8dc1 100755 --- a/app/src/main/java/com/hippo/ehviewer/ui/SetSecurityActivity.java +++ b/app/src/main/java/com/hippo/ehviewer/ui/SetSecurityActivity.java @@ -23,8 +23,10 @@ import android.text.TextUtils; import android.view.MenuItem; import android.view.View; import android.widget.CheckBox; + import androidx.annotation.Nullable; import androidx.annotation.RequiresApi; + import com.hippo.ehviewer.R; import com.hippo.ehviewer.Settings; import com.hippo.widget.lockpattern.LockPatternUtils; @@ -42,6 +44,16 @@ public class SetSecurityActivity extends ToolbarActivity implements View.OnClick @Nullable private CheckBox mFingerprint; + @RequiresApi(api = Build.VERSION_CODES.M) + public static boolean hasEnrolledFingerprints(FingerprintManager fingerprintManager) { + try { + return fingerprintManager.isHardwareDetected() + && fingerprintManager.hasEnrolledFingerprints(); + } catch (Throwable e) { + return false; + } + } + @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -73,16 +85,6 @@ public class SetSecurityActivity extends ToolbarActivity implements View.OnClick mSet.setOnClickListener(this); } - @RequiresApi(api = Build.VERSION_CODES.M) - public static boolean hasEnrolledFingerprints(FingerprintManager fingerprintManager) { - try { - return fingerprintManager.isHardwareDetected() - && fingerprintManager.hasEnrolledFingerprints(); - } catch (Throwable e) { - return false; - } - } - @Override protected void onDestroy() { super.onDestroy(); diff --git a/app/src/main/java/com/hippo/ehviewer/ui/SettingsActivity.java b/app/src/main/java/com/hippo/ehviewer/ui/SettingsActivity.java index 54b2b53eb..3fe9e821a 100644 --- a/app/src/main/java/com/hippo/ehviewer/ui/SettingsActivity.java +++ b/app/src/main/java/com/hippo/ehviewer/ui/SettingsActivity.java @@ -16,14 +16,14 @@ package com.hippo.ehviewer.ui; -import android.app.Fragment; import android.content.Intent; import android.graphics.drawable.Drawable; import android.os.Bundle; import android.view.MenuItem; -import androidx.annotation.StringRes; + import androidx.appcompat.app.ActionBar; import androidx.appcompat.app.ActionBarDrawerToggle; + import com.hippo.ehviewer.R; import com.hippo.ehviewer.Settings; import com.hippo.ehviewer.ui.fragment.AboutFragment; @@ -32,10 +32,10 @@ import com.hippo.ehviewer.ui.fragment.DownloadFragment; import com.hippo.ehviewer.ui.fragment.EhFragment; import com.hippo.ehviewer.ui.fragment.PrivacyFragment; import com.hippo.ehviewer.ui.fragment.ReadFragment; +import com.hippo.ehviewer.ui.fragment.SettingsFragment; import com.hippo.util.DrawableManager; -import java.util.List; -public final class SettingsActivity extends EhPreferenceActivity { +public final class SettingsActivity extends EhActivity { private static final int REQUEST_CODE_FRAGMENT = 0; @@ -50,15 +50,15 @@ public final class SettingsActivity extends EhPreferenceActivity { @Override protected int getThemeResId(int theme) { - switch (theme) { - case Settings.THEME_LIGHT: - default: - return R.style.AppTheme_Settings; - case Settings.THEME_DARK: - return R.style.AppTheme_Settings_Dark; - case Settings.THEME_BLACK: - return R.style.AppTheme_Settings_Black; - } + switch (theme) { + case Settings.THEME_LIGHT: + default: + return R.style.AppTheme_Settings; + case Settings.THEME_DARK: + return R.style.AppTheme_Settings_Dark; + case Settings.THEME_BLACK: + return R.style.AppTheme_Settings_Black; + } } private void setActionBarUpIndicator(Drawable drawable) { @@ -73,10 +73,20 @@ public final class SettingsActivity extends EhPreferenceActivity { } } - @SuppressWarnings("deprecation") @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); + setContentView(R.layout.activity_preference); + if (savedInstanceState == null) { + getSupportFragmentManager() + .beginTransaction() + .replace(R.id.fragment, new SettingsFragment()) + .commitAllowingStateLoss(); + } + ActionBar bar = getSupportActionBar(); + if (bar != null) { + bar.setDisplayHomeAsUpEnabled(true); + } setActionBarUpIndicator(DrawableManager.getVectorDrawable(this, R.drawable.v_arrow_left_dark_x24)); } @@ -91,33 +101,6 @@ public final class SettingsActivity extends EhPreferenceActivity { } } - @Override - public void onBuildHeaders(List
target) { - loadHeadersFromResource(R.xml.settings_headers, target); - } - - @Override - public void startWithFragment(String fragmentName, Bundle args, - Fragment resultTo, int resultRequestCode, @StringRes int titleRes, - @StringRes int shortTitleRes) { - Intent intent = onBuildStartFragmentIntent(fragmentName, args, titleRes, shortTitleRes); - if (resultTo == null) { - startActivityForResult(intent, REQUEST_CODE_FRAGMENT); - } else { - resultTo.startActivityForResult(intent, resultRequestCode); - } - } - - @Override - protected boolean isValidFragment(String fragmentName) { - for (String fragment : ENTRY_FRAGMENTS) { - if (fragment.equals(fragmentName)) { - return true; - } - } - return false; - } - @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { if (requestCode == REQUEST_CODE_FRAGMENT) { diff --git a/app/src/main/java/com/hippo/ehviewer/ui/ToolbarActivity.java b/app/src/main/java/com/hippo/ehviewer/ui/ToolbarActivity.java index ed569bc4a..0fea81965 100644 --- a/app/src/main/java/com/hippo/ehviewer/ui/ToolbarActivity.java +++ b/app/src/main/java/com/hippo/ehviewer/ui/ToolbarActivity.java @@ -19,10 +19,12 @@ package com.hippo.ehviewer.ui; import android.graphics.drawable.Drawable; import android.view.View; import android.view.ViewGroup; + import androidx.annotation.DrawableRes; import androidx.annotation.LayoutRes; import androidx.annotation.Nullable; import androidx.appcompat.widget.Toolbar; + import com.hippo.ehviewer.R; import com.hippo.ehviewer.Settings; diff --git a/app/src/main/java/com/hippo/ehviewer/ui/UConfigActivity.java b/app/src/main/java/com/hippo/ehviewer/ui/UConfigActivity.java index 6a51aefed..f8eeac4d0 100644 --- a/app/src/main/java/com/hippo/ehviewer/ui/UConfigActivity.java +++ b/app/src/main/java/com/hippo/ehviewer/ui/UConfigActivity.java @@ -31,7 +31,9 @@ import android.webkit.CookieManager; import android.webkit.CookieSyncManager; import android.webkit.WebView; import android.webkit.WebViewClient; + import androidx.annotation.Nullable; + import com.google.android.material.snackbar.Snackbar; import com.hippo.ehviewer.EhApplication; import com.hippo.ehviewer.R; @@ -39,141 +41,142 @@ import com.hippo.ehviewer.client.EhCookieStore; import com.hippo.ehviewer.client.EhUrl; import com.hippo.ehviewer.widget.DialogWebChromeClient; import com.hippo.widget.ProgressView; + import okhttp3.Cookie; import okhttp3.HttpUrl; public class UConfigActivity extends ToolbarActivity { - private WebView webView; - private ProgressView progress; - private String url; - private boolean loaded; + private WebView webView; + private ProgressView progress; + private String url; + private boolean loaded; - @SuppressLint("SetJavaScriptEnabled") - @Override - protected void onCreate(@Nullable Bundle savedInstanceState) { - super.onCreate(savedInstanceState); + @SuppressLint("SetJavaScriptEnabled") + @Override + protected void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); - // http://stackoverflow.com/questions/32284642/how-to-handle-an-uncatched-exception - CookieManager cookieManager = CookieManager.getInstance(); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { - cookieManager.flush(); - cookieManager.removeAllCookies(null); - cookieManager.removeSessionCookies(null); - } else { - CookieSyncManager cookieSyncManager = CookieSyncManager.createInstance(this); - cookieSyncManager.startSync(); - cookieManager.removeAllCookie(); - cookieManager.removeSessionCookie(); - cookieSyncManager.stopSync(); + // http://stackoverflow.com/questions/32284642/how-to-handle-an-uncatched-exception + CookieManager cookieManager = CookieManager.getInstance(); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + cookieManager.flush(); + cookieManager.removeAllCookies(null); + cookieManager.removeSessionCookies(null); + } else { + CookieSyncManager cookieSyncManager = CookieSyncManager.createInstance(this); + cookieSyncManager.startSync(); + cookieManager.removeAllCookie(); + cookieManager.removeSessionCookie(); + cookieSyncManager.stopSync(); + } + + // Copy cookies from okhttp cookie store to CookieManager + url = EhUrl.getUConfigUrl(); + EhCookieStore store = EhApplication.getEhCookieStore(this); + for (Cookie cookie : store.getCookies(HttpUrl.parse(url))) { + cookieManager.setCookie(url, cookie.toString()); + } + + setContentView(R.layout.activity_u_config); + setNavigationIcon(R.drawable.v_arrow_left_dark_x24); + webView = (WebView) findViewById(R.id.webview); + webView.getSettings().setJavaScriptEnabled(true); + webView.setWebViewClient(new UConfigWebViewClient()); + webView.setWebChromeClient(new DialogWebChromeClient(this)); + webView.loadUrl(url); + progress = (ProgressView) findViewById(R.id.progress); + + Snackbar.make(webView, R.string.apply_tip, Snackbar.LENGTH_LONG).show(); } - // Copy cookies from okhttp cookie store to CookieManager - url = EhUrl.getUConfigUrl(); - EhCookieStore store = EhApplication.getEhCookieStore(this); - for (Cookie cookie : store.getCookies(HttpUrl.parse(url))) { - cookieManager.setCookie(url, cookie.toString()); + private void apply() { + webView.loadUrl("javascript:" + + "(function() {\n" + + " var apply = document.getElementById(\"apply\").children[0];\n" + + " apply.click();\n" + + "})();"); } - setContentView(R.layout.activity_u_config); - setNavigationIcon(R.drawable.v_arrow_left_dark_x24); - webView = (WebView) findViewById(R.id.webview); - webView.getSettings().setJavaScriptEnabled(true); - webView.setWebViewClient(new UConfigWebViewClient()); - webView.setWebChromeClient(new DialogWebChromeClient(this)); - webView.loadUrl(url); - progress = (ProgressView) findViewById(R.id.progress); - - Snackbar.make(webView, R.string.apply_tip, Snackbar.LENGTH_LONG).show(); - } - - private void apply() { - webView.loadUrl("javascript:" - + "(function() {\n" - + " var apply = document.getElementById(\"apply\").children[0];\n" - + " apply.click();\n" - + "})();"); - } - - @Override - public boolean onCreateOptionsMenu(Menu menu) { - super.onCreateOptionsMenu(menu); - getMenuInflater().inflate(R.menu.activity_u_config, menu); - return true; - } - - @Override - public boolean onOptionsItemSelected(MenuItem item) { - switch (item.getItemId()) { - case android.R.id.home: - finish(); + @Override + public boolean onCreateOptionsMenu(Menu menu) { + super.onCreateOptionsMenu(menu); + getMenuInflater().inflate(R.menu.activity_u_config, menu); return true; - case R.id.action_apply: - if (loaded) { - apply(); - } - return true; - default: - return super.onOptionsItemSelected(item); - } - } - - private Cookie longLive(Cookie cookie) { - return new Cookie.Builder() - .name(cookie.name()) - .value(cookie.value()) - .domain(cookie.domain()) - .path(cookie.path()) - .expiresAt(Long.MAX_VALUE) - .build(); - } - - @Override - protected void onDestroy() { - super.onDestroy(); - webView.destroy(); - - // Put cookies back to okhttp cookie store - CookieManager cookieManager = CookieManager.getInstance(); - String cookiesString = cookieManager.getCookie(url); - - if (cookiesString != null && !cookiesString.isEmpty()) { - EhCookieStore store = EhApplication.getEhCookieStore(this); - HttpUrl eUrl = HttpUrl.parse(EhUrl.HOST_E); - HttpUrl exUrl = HttpUrl.parse(EhUrl.HOST_EX); - - // The cookies saved in the uconfig page should be shared between e and ex - for (String header : cookiesString.split(";")) { - Cookie eCookie = Cookie.parse(eUrl, header); - if (eCookie != null) { - store.addCookie(longLive(eCookie)); - } - - Cookie exCookie = Cookie.parse(exUrl, header); - if (exCookie != null) { - store.addCookie(longLive(exCookie)); - } - } - } - } - - private class UConfigWebViewClient extends WebViewClient { - @Override - public boolean shouldOverrideUrlLoading(WebView view, String url) { - // Never load other urls - return true; } @Override - public void onPageStarted(WebView view, String url, Bitmap favicon) { - progress.setVisibility(View.VISIBLE); - loaded = false; + public boolean onOptionsItemSelected(MenuItem item) { + switch (item.getItemId()) { + case android.R.id.home: + finish(); + return true; + case R.id.action_apply: + if (loaded) { + apply(); + } + return true; + default: + return super.onOptionsItemSelected(item); + } + } + + private Cookie longLive(Cookie cookie) { + return new Cookie.Builder() + .name(cookie.name()) + .value(cookie.value()) + .domain(cookie.domain()) + .path(cookie.path()) + .expiresAt(Long.MAX_VALUE) + .build(); } @Override - public void onPageFinished(WebView view, String url) { - progress.setVisibility(View.GONE); - loaded = true; + protected void onDestroy() { + super.onDestroy(); + webView.destroy(); + + // Put cookies back to okhttp cookie store + CookieManager cookieManager = CookieManager.getInstance(); + String cookiesString = cookieManager.getCookie(url); + + if (cookiesString != null && !cookiesString.isEmpty()) { + EhCookieStore store = EhApplication.getEhCookieStore(this); + HttpUrl eUrl = HttpUrl.parse(EhUrl.HOST_E); + HttpUrl exUrl = HttpUrl.parse(EhUrl.HOST_EX); + + // The cookies saved in the uconfig page should be shared between e and ex + for (String header : cookiesString.split(";")) { + Cookie eCookie = Cookie.parse(eUrl, header); + if (eCookie != null) { + store.addCookie(longLive(eCookie)); + } + + Cookie exCookie = Cookie.parse(exUrl, header); + if (exCookie != null) { + store.addCookie(longLive(exCookie)); + } + } + } + } + + private class UConfigWebViewClient extends WebViewClient { + @Override + public boolean shouldOverrideUrlLoading(WebView view, String url) { + // Never load other urls + return true; + } + + @Override + public void onPageStarted(WebView view, String url, Bitmap favicon) { + progress.setVisibility(View.VISIBLE); + loaded = false; + } + + @Override + public void onPageFinished(WebView view, String url) { + progress.setVisibility(View.GONE); + loaded = true; + } } - } } diff --git a/app/src/main/java/com/hippo/ehviewer/ui/dialog/SelectItemWithIconAdapter.java b/app/src/main/java/com/hippo/ehviewer/ui/dialog/SelectItemWithIconAdapter.java index 124380cb2..492f2fb80 100644 --- a/app/src/main/java/com/hippo/ehviewer/ui/dialog/SelectItemWithIconAdapter.java +++ b/app/src/main/java/com/hippo/ehviewer/ui/dialog/SelectItemWithIconAdapter.java @@ -23,56 +23,58 @@ import android.view.View; import android.view.ViewGroup; import android.widget.BaseAdapter; import android.widget.TextView; + import androidx.appcompat.content.res.AppCompatResources; + import com.hippo.ehviewer.R; public class SelectItemWithIconAdapter extends BaseAdapter { - private Context context; - private LayoutInflater inflater; + private Context context; + private LayoutInflater inflater; - private CharSequence[] texts; - private int[] icons; + private CharSequence[] texts; + private int[] icons; - public SelectItemWithIconAdapter(Context context, CharSequence[] texts, int[] icons) { - int count = texts.length; - if (count != icons.length) { - throw new IllegalArgumentException("Length conflict"); + public SelectItemWithIconAdapter(Context context, CharSequence[] texts, int[] icons) { + int count = texts.length; + if (count != icons.length) { + throw new IllegalArgumentException("Length conflict"); + } + this.context = context; + this.inflater = LayoutInflater.from(context); + this.texts = texts; + this.icons = icons; } - this.context = context; - this.inflater = LayoutInflater.from(context); - this.texts = texts; - this.icons = icons; - } - @Override - public int getCount() { - return texts.length; - } - - @Override - public Object getItem(int position) { - return texts[position]; - } - - @Override - public long getItemId(int position) { - return position; - } - - @Override - public View getView(int position, View convertView, ViewGroup parent) { - if (convertView == null) { - convertView = inflater.inflate(R.layout.dialog_item_select_with_icon, parent, false); + @Override + public int getCount() { + return texts.length; } - TextView view = (TextView) convertView; - view.setText(texts[position]); + @Override + public Object getItem(int position) { + return texts[position]; + } - Drawable icon = AppCompatResources.getDrawable(context, icons[position]); - icon.setBounds(0, 0, icon.getIntrinsicWidth(), icon.getIntrinsicHeight()); - view.setCompoundDrawables(icon, null, null, null); + @Override + public long getItemId(int position) { + return position; + } - return view; - } + @Override + public View getView(int position, View convertView, ViewGroup parent) { + if (convertView == null) { + convertView = inflater.inflate(R.layout.dialog_item_select_with_icon, parent, false); + } + TextView view = (TextView) convertView; + + view.setText(texts[position]); + + Drawable icon = AppCompatResources.getDrawable(context, icons[position]); + icon.setBounds(0, 0, icon.getIntrinsicWidth(), icon.getIntrinsicHeight()); + view.setCompoundDrawables(icon, null, null, null); + + return view; + } } diff --git a/app/src/main/java/com/hippo/ehviewer/ui/fragment/AboutFragment.java b/app/src/main/java/com/hippo/ehviewer/ui/fragment/AboutFragment.java index 39ded05d1..ecdf74860 100644 --- a/app/src/main/java/com/hippo/ehviewer/ui/fragment/AboutFragment.java +++ b/app/src/main/java/com/hippo/ehviewer/ui/fragment/AboutFragment.java @@ -22,28 +22,36 @@ import android.content.Context; import android.content.Intent; import android.net.Uri; import android.os.Bundle; -import android.preference.Preference; -import android.preference.PreferenceFragment; import android.util.Base64; import android.widget.TextView; import android.widget.Toast; + +import androidx.annotation.Nullable; import androidx.appcompat.app.AlertDialog; +import androidx.preference.Preference; + import com.hippo.ehviewer.EhApplication; import com.hippo.ehviewer.R; import com.hippo.ehviewer.ui.CommonOperations; import com.hippo.util.AppHelper; -import java.io.UnsupportedEncodingException; +import com.takisoft.preferencex.PreferenceFragmentCompat; -public class AboutFragment extends PreferenceFragment +import java.nio.charset.StandardCharsets; + +public class AboutFragment extends PreferenceFragmentCompat implements Preference.OnPreferenceClickListener { private static final String KEY_AUTHOR = "author"; private static final String KEY_DONATE = "donate"; private static final String KEY_CHECK_FOR_UPDATES = "check_for_updates"; + private static String base64Decode(String encoded) { + byte[] bytes = Base64.decode(encoded, Base64.DEFAULT); + return new String(bytes, StandardCharsets.UTF_8); + } + @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); + public void onCreatePreferencesFix(@Nullable Bundle savedInstanceState, String rootKey) { addPreferencesFromResource(R.xml.about_settings); Preference author = findPreference(KEY_AUTHOR); @@ -88,15 +96,6 @@ public class AboutFragment extends PreferenceFragment dialog.findViewById(R.id.paypal_copy).setOnClickListener(v -> copyToClipboard(paypalStr)); } - private static String base64Decode(String encoded) { - byte[] bytes = Base64.decode(encoded, Base64.DEFAULT); - try { - return new String(bytes, "UTF-8"); - } catch (UnsupportedEncodingException e) { - throw new IllegalStateException(e); - } - } - private void copyToClipboard(String text) { ClipboardManager cmb = (ClipboardManager) getActivity().getSystemService(Context.CLIPBOARD_SERVICE); if (cmb != null) { diff --git a/app/src/main/java/com/hippo/ehviewer/ui/fragment/AdvancedFragment.java b/app/src/main/java/com/hippo/ehviewer/ui/fragment/AdvancedFragment.java index 323ef76ff..fe5bbb51c 100644 --- a/app/src/main/java/com/hippo/ehviewer/ui/fragment/AdvancedFragment.java +++ b/app/src/main/java/com/hippo/ehviewer/ui/fragment/AdvancedFragment.java @@ -18,33 +18,57 @@ package com.hippo.ehviewer.ui.fragment; import android.app.Activity; import android.content.Context; -import android.content.DialogInterface; import android.content.res.Resources; import android.os.Bundle; -import android.preference.Preference; -import android.preference.PreferenceFragment; import android.widget.Toast; + +import androidx.annotation.Nullable; import androidx.appcompat.app.AlertDialog; +import androidx.preference.Preference; + import com.hippo.ehviewer.AppConfig; import com.hippo.ehviewer.EhApplication; import com.hippo.ehviewer.EhDB; import com.hippo.ehviewer.R; import com.hippo.util.LogCat; import com.hippo.util.ReadableTime; +import com.takisoft.preferencex.PreferenceFragmentCompat; + import java.io.File; import java.util.Arrays; -public class AdvancedFragment extends PreferenceFragment - implements Preference.OnPreferenceClickListener, Preference.OnPreferenceChangeListener { +public class AdvancedFragment extends PreferenceFragmentCompat + implements Preference.OnPreferenceClickListener, Preference.OnPreferenceChangeListener { private static final String KEY_DUMP_LOGCAT = "dump_logcat"; private static final String KEY_CLEAR_MEMORY_CACHE = "clear_memory_cache"; private static final String KEY_APP_LANGUAGE = "app_language"; private static final String KEY_IMPORT_DATA = "import_data"; + private static void importData(final Context context) { + final File dir = AppConfig.getExternalDataDir(); + if (null == dir) { + Toast.makeText(context, R.string.cant_get_data_dir, Toast.LENGTH_SHORT).show(); + return; + } + final String[] files = dir.list(); + if (null == files || files.length <= 0) { + Toast.makeText(context, R.string.cant_find_any_data, Toast.LENGTH_SHORT).show(); + return; + } + Arrays.sort(files); + new AlertDialog.Builder(context).setItems(files, (dialog, which) -> { + File file = new File(dir, files[which]); + String error = EhDB.importDB(context, file); + if (null == error) { + error = context.getString(R.string.settings_advanced_import_data_successfully); + } + Toast.makeText(context, error, Toast.LENGTH_SHORT).show(); + }).show(); + } + @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); + public void onCreatePreferencesFix(@Nullable Bundle savedInstanceState, String rootKey) { addPreferencesFromResource(R.xml.advanced_settings); Preference dumpLogcat = findPreference(KEY_DUMP_LOGCAT); @@ -93,31 +117,6 @@ public class AdvancedFragment extends PreferenceFragment return false; } - private static void importData(final Context context) { - final File dir = AppConfig.getExternalDataDir(); - if (null == dir) { - Toast.makeText(context, R.string.cant_get_data_dir, Toast.LENGTH_SHORT).show(); - return; - } - final String[] files = dir.list(); - if (null == files || files.length <= 0) { - Toast.makeText(context, R.string.cant_find_any_data, Toast.LENGTH_SHORT).show(); - return; - } - Arrays.sort(files); - new AlertDialog.Builder(context).setItems(files, new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - File file = new File(dir, files[which]); - String error = EhDB.importDB(context, file); - if (null == error) { - error = context.getString(R.string.settings_advanced_import_data_successfully); - } - Toast.makeText(context, error, Toast.LENGTH_SHORT).show(); - } - }).show(); - } - @Override public boolean onPreferenceChange(Preference preference, Object newValue) { String key = preference.getKey(); diff --git a/app/src/main/java/com/hippo/ehviewer/ui/fragment/DownloadFragment.java b/app/src/main/java/com/hippo/ehviewer/ui/fragment/DownloadFragment.java index c2e511886..35ef4a1d3 100644 --- a/app/src/main/java/com/hippo/ehviewer/ui/fragment/DownloadFragment.java +++ b/app/src/main/java/com/hippo/ehviewer/ui/fragment/DownloadFragment.java @@ -22,19 +22,21 @@ import android.content.Intent; import android.net.Uri; import android.os.Build; import android.os.Bundle; -import android.preference.Preference; -import android.preference.PreferenceFragment; import android.widget.Toast; + import androidx.annotation.Nullable; import androidx.appcompat.app.AlertDialog; +import androidx.preference.Preference; + import com.hippo.ehviewer.R; import com.hippo.ehviewer.Settings; import com.hippo.ehviewer.ui.CommonOperations; import com.hippo.ehviewer.ui.DirPickerActivity; import com.hippo.unifile.UniFile; import com.hippo.util.ExceptionUtils; +import com.takisoft.preferencex.PreferenceFragmentCompat; -public class DownloadFragment extends PreferenceFragment implements +public class DownloadFragment extends PreferenceFragmentCompat implements Preference.OnPreferenceChangeListener, Preference.OnPreferenceClickListener { @@ -46,9 +48,9 @@ public class DownloadFragment extends PreferenceFragment implements @Nullable private Preference mDownloadLocation; + @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); + public void onCreatePreferencesFix(@Nullable Bundle savedInstanceState, String rootKey) { addPreferencesFromResource(R.xml.download_settings); Preference mediaScan = findPreference(Settings.KEY_MEDIA_SCAN); diff --git a/app/src/main/java/com/hippo/ehviewer/ui/fragment/EhFragment.java b/app/src/main/java/com/hippo/ehviewer/ui/fragment/EhFragment.java index 9f2831d61..be87309de 100644 --- a/app/src/main/java/com/hippo/ehviewer/ui/fragment/EhFragment.java +++ b/app/src/main/java/com/hippo/ehviewer/ui/fragment/EhFragment.java @@ -19,19 +19,21 @@ package com.hippo.ehviewer.ui.fragment; import android.app.Activity; import android.os.Build; import android.os.Bundle; -import android.preference.Preference; -import android.preference.PreferenceFragment; + +import androidx.annotation.Nullable; +import androidx.preference.Preference; + import com.hippo.ehviewer.EhApplication; import com.hippo.ehviewer.R; import com.hippo.ehviewer.Settings; import com.hippo.ehviewer.client.EhTagDatabase; +import com.takisoft.preferencex.PreferenceFragmentCompat; -public class EhFragment extends PreferenceFragment +public class EhFragment extends PreferenceFragmentCompat implements Preference.OnPreferenceChangeListener { @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); + public void onCreatePreferencesFix(@Nullable Bundle savedInstanceState, String rootKey) { addPreferencesFromResource(R.xml.eh_settings); Preference theme = findPreference(Settings.KEY_THEME); diff --git a/app/src/main/java/com/hippo/ehviewer/ui/fragment/PrivacyFragment.java b/app/src/main/java/com/hippo/ehviewer/ui/fragment/PrivacyFragment.java index 6757946ad..9cdb65625 100644 --- a/app/src/main/java/com/hippo/ehviewer/ui/fragment/PrivacyFragment.java +++ b/app/src/main/java/com/hippo/ehviewer/ui/fragment/PrivacyFragment.java @@ -1,29 +1,27 @@ package com.hippo.ehviewer.ui.fragment; import android.os.Bundle; -import android.preference.Preference; -import android.preference.PreferenceFragment; import android.text.TextUtils; -import com.hippo.ehviewer.Analytics; +import androidx.annotation.Nullable; +import androidx.preference.Preference; + import com.hippo.ehviewer.R; import com.hippo.ehviewer.Settings; +import com.takisoft.preferencex.PreferenceFragmentCompat; /** * Created by Mo10 on 2018/2/10. */ -public class PrivacyFragment extends PreferenceFragment - implements Preference.OnPreferenceChangeListener { +public class PrivacyFragment extends PreferenceFragmentCompat { private static final String KEY_PATTERN_PROTECTION = "pattern_protection"; - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - addPreferencesFromResource(R.xml.privacy_settings); - Preference enableAnalytics = findPreference(Settings.KEY_ENABLE_ANALYTICS); - enableAnalytics.setOnPreferenceChangeListener(this); + @Override + public void onCreatePreferencesFix(@Nullable Bundle savedInstanceState, String rootKey) { + addPreferencesFromResource(R.xml.privacy_settings); } + @Override public void onResume() { super.onResume(); @@ -33,15 +31,4 @@ public class PrivacyFragment extends PreferenceFragment R.string.settings_privacy_pattern_protection_set); } - @Override - public boolean onPreferenceChange(Preference preference, Object newValue) { - String key = preference.getKey(); - if (Settings.KEY_ENABLE_ANALYTICS.equals(key)) { - if (newValue instanceof Boolean && (Boolean) newValue) { - Analytics.start(getActivity()); - } - return true; - } - return true; - } } diff --git a/app/src/main/java/com/hippo/ehviewer/ui/fragment/ReadFragment.java b/app/src/main/java/com/hippo/ehviewer/ui/fragment/ReadFragment.java index 67613c187..ea512337c 100644 --- a/app/src/main/java/com/hippo/ehviewer/ui/fragment/ReadFragment.java +++ b/app/src/main/java/com/hippo/ehviewer/ui/fragment/ReadFragment.java @@ -17,15 +17,16 @@ package com.hippo.ehviewer.ui.fragment; import android.os.Bundle; -import android.preference.PreferenceFragment; + +import androidx.annotation.Nullable; import com.hippo.ehviewer.R; +import com.takisoft.preferencex.PreferenceFragmentCompat; -public class ReadFragment extends PreferenceFragment { +public class ReadFragment extends PreferenceFragmentCompat { @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); + public void onCreatePreferencesFix(@Nullable Bundle savedInstanceState, String rootKey) { addPreferencesFromResource(R.xml.read_settings); } } diff --git a/app/src/main/java/com/hippo/ehviewer/ui/fragment/SettingsFragment.java b/app/src/main/java/com/hippo/ehviewer/ui/fragment/SettingsFragment.java new file mode 100644 index 000000000..dfec2405e --- /dev/null +++ b/app/src/main/java/com/hippo/ehviewer/ui/fragment/SettingsFragment.java @@ -0,0 +1,16 @@ +package com.hippo.ehviewer.ui.fragment; + +import android.os.Bundle; + +import androidx.annotation.Nullable; + +import com.hippo.ehviewer.R; +import com.takisoft.preferencex.PreferenceFragmentCompat; + +public class SettingsFragment extends PreferenceFragmentCompat { + @Override + public void onCreatePreferencesFix(@Nullable Bundle savedInstanceState, String rootKey) { + + addPreferencesFromResource(R.xml.settings_headers); + } +} diff --git a/app/src/main/java/com/hippo/ehviewer/ui/scene/AnalyticsScene.java b/app/src/main/java/com/hippo/ehviewer/ui/scene/AnalyticsScene.java index 424add9eb..547a5efd6 100644 --- a/app/src/main/java/com/hippo/ehviewer/ui/scene/AnalyticsScene.java +++ b/app/src/main/java/com/hippo/ehviewer/ui/scene/AnalyticsScene.java @@ -22,8 +22,9 @@ import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.TextView; + import androidx.annotation.Nullable; -import com.hippo.ehviewer.Analytics; + import com.hippo.ehviewer.R; import com.hippo.ehviewer.Settings; import com.hippo.ehviewer.ui.MainActivity; @@ -46,7 +47,7 @@ public class AnalyticsScene extends SolidScene implements View.OnClickListener { @Nullable @Override public View onCreateView2(LayoutInflater inflater, @Nullable ViewGroup container, - @Nullable Bundle savedInstanceState) { + @Nullable Bundle savedInstanceState) { View view = inflater.inflate(R.layout.scene_analytics, container, false); mReject = ViewUtils.$$(view, R.id.reject); @@ -82,7 +83,6 @@ public class AnalyticsScene extends SolidScene implements View.OnClickListener { } else if (mAccept == v) { Settings.putEnableAnalytics(true); // Start Analytics - Analytics.start(context); } Settings.putAskAnalytics(false); diff --git a/app/src/main/java/com/hippo/ehviewer/ui/scene/BaseScene.java b/app/src/main/java/com/hippo/ehviewer/ui/scene/BaseScene.java index a9a4db218..a11709550 100644 --- a/app/src/main/java/com/hippo/ehviewer/ui/scene/BaseScene.java +++ b/app/src/main/java/com/hippo/ehviewer/ui/scene/BaseScene.java @@ -26,14 +26,15 @@ import android.view.Gravity; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; + import androidx.annotation.IdRes; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.annotation.StringRes; import androidx.annotation.StyleRes; +import androidx.drawerlayout.widget.DrawerLayout; import androidx.fragment.app.FragmentActivity; -import com.hippo.drawerlayout.DrawerLayout; -import com.hippo.ehviewer.Analytics; + import com.hippo.ehviewer.ui.MainActivity; import com.hippo.scene.SceneFragment; import com.hippo.util.AppHelper; @@ -44,7 +45,7 @@ public abstract class BaseScene extends SceneFragment { public static final int LENGTH_LONG = 1; public static final String KEY_DRAWER_VIEW_STATE = - "com.hippo.ehviewer.ui.scene.BaseScene:DRAWER_VIEW_STATE"; + "com.hippo.ehviewer.ui.scene.BaseScene:DRAWER_VIEW_STATE"; private Context mThemeContext; @@ -111,13 +112,6 @@ public abstract class BaseScene extends SceneFragment { } } - public void setDrawerGestureBlocker(DrawerLayout.GestureBlocker gestureBlocker) { - FragmentActivity activity = getActivity(); - if (activity instanceof MainActivity) { - ((MainActivity) activity).setDrawerGestureBlocker(gestureBlocker); - } - } - public boolean isDrawersVisible() { FragmentActivity activity = getActivity(); if (activity instanceof MainActivity) { @@ -127,16 +121,6 @@ public abstract class BaseScene extends SceneFragment { } } - /** - * @param resId 0 for clear - */ - public void setNavCheckedItem(@IdRes int resId) { - FragmentActivity activity = getActivity(); - if (activity instanceof MainActivity) { - ((MainActivity) activity).setNavCheckedItem(resId); - } - } - public void showTip(CharSequence message, int length) { FragmentActivity activity = getActivity(); if (activity instanceof MainActivity) { @@ -159,8 +143,18 @@ public abstract class BaseScene extends SceneFragment { return 0; } + /** + * @param resId 0 for clear + */ + public void setNavCheckedItem(@IdRes int resId) { + FragmentActivity activity = getActivity(); + if (activity instanceof MainActivity) { + ((MainActivity) activity).setNavCheckedItem(resId); + } + } + public final View createDrawerView(LayoutInflater inflater, - @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { + @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { drawerView = onCreateDrawerView(inflater, container, savedInstanceState); if (drawerView != null) { @@ -177,7 +171,7 @@ public abstract class BaseScene extends SceneFragment { } public View onCreateDrawerView(LayoutInflater inflater, - @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { + @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { return null; } @@ -198,13 +192,13 @@ public abstract class BaseScene extends SceneFragment { @Nullable @Override public final View onCreateView(LayoutInflater inflater, - @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { + @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { return onCreateView2(LayoutInflater.from(getContext2()), container, savedInstanceState); } @Nullable public View onCreateView2(LayoutInflater inflater, - @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { + @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { return null; } @@ -278,12 +272,6 @@ public abstract class BaseScene extends SceneFragment { } } - @Override - public void onResume() { - super.onResume(); - Analytics.onSceneView(this); - } - @Override public void onSaveInstanceState(@NonNull Bundle outState) { super.onSaveInstanceState(outState); diff --git a/app/src/main/java/com/hippo/ehviewer/ui/scene/CookieSignInScene.java b/app/src/main/java/com/hippo/ehviewer/ui/scene/CookieSignInScene.java index cb627c0e9..5e017019b 100644 --- a/app/src/main/java/com/hippo/ehviewer/ui/scene/CookieSignInScene.java +++ b/app/src/main/java/com/hippo/ehviewer/ui/scene/CookieSignInScene.java @@ -27,8 +27,10 @@ import android.view.View; import android.view.ViewGroup; import android.widget.EditText; import android.widget.TextView; + import androidx.annotation.Nullable; import androidx.appcompat.app.AlertDialog; + import com.google.android.material.textfield.TextInputLayout; import com.hippo.ehviewer.EhApplication; import com.hippo.ehviewer.R; @@ -37,6 +39,7 @@ import com.hippo.ehviewer.client.EhUrl; import com.hippo.ehviewer.client.EhUtils; import com.hippo.yorozuya.AssertUtils; import com.hippo.yorozuya.ViewUtils; + import okhttp3.Cookie; public class CookieSignInScene extends SolidScene implements EditText.OnEditorActionListener, @@ -60,6 +63,36 @@ public class CookieSignInScene extends SolidScene implements EditText.OnEditorAc @Nullable private View mOk; + // false for error + private static boolean checkIpbMemberId(String id) { + for (int i = 0, n = id.length(); i < n; i++) { + char ch = id.charAt(i); + if (!(ch >= '0' && ch <= '9')) { + return false; + } + } + return true; + } + + private static boolean checkIpbPassHash(String hash) { + if (32 != hash.length()) { + return false; + } + + for (int i = 0, n = hash.length(); i < n; i++) { + char ch = hash.charAt(i); + if (!(ch >= '0' && ch <= '9') && !(ch >= 'a' && ch <= 'z')) { + return false; + } + } + return true; + } + + private static Cookie newCookie(String name, String value, String domain) { + return new Cookie.Builder().name(name).value(value) + .domain(domain).expiresAt(Long.MAX_VALUE).build(); + } + @Override public boolean needShowLeftDrawer() { return false; @@ -68,7 +101,7 @@ public class CookieSignInScene extends SolidScene implements EditText.OnEditorAc @Nullable @Override public View onCreateView2(LayoutInflater inflater, - @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { + @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { View view = inflater.inflate(R.layout.scene_cookie_sign_in, container, false); mIpbMemberIdLayout = (TextInputLayout) ViewUtils.$$(view, R.id.ipb_member_id_layout); mIpbMemberId = mIpbMemberIdLayout.getEditText(); @@ -112,7 +145,6 @@ public class CookieSignInScene extends SolidScene implements EditText.OnEditorAc return view; } - @Override public void onViewCreated(View view, @Nullable Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); @@ -147,31 +179,6 @@ public class CookieSignInScene extends SolidScene implements EditText.OnEditorAc return true; } - // false for error - private static boolean checkIpbMemberId(String id) { - for (int i = 0, n = id.length(); i < n; i++) { - char ch = id.charAt(i); - if (!(ch >= '0' && ch <= '9')) { - return false; - } - } - return true; - } - - private static boolean checkIpbPassHash(String hash) { - if (32 != hash.length()) { - return false; - } - - for (int i = 0, n = hash.length(); i < n; i++) { - char ch = hash.charAt(i); - if (!(ch >= '0' && ch <= '9') && !(ch >= 'a' && ch <= 'z')) { - return false; - } - } - return true; - } - public void enter() { Context context = getContext2(); if (null == context || null == mIpbMemberIdLayout || null == mIpbPassHashLayout || @@ -218,11 +225,6 @@ public class CookieSignInScene extends SolidScene implements EditText.OnEditorAc } } - private static Cookie newCookie(String name, String value, String domain) { - return new Cookie.Builder().name(name).value(value) - .domain(domain).expiresAt(Long.MAX_VALUE).build(); - } - private void storeCookie(String id, String hash, String igneous) { Context context = getContext2(); if (null == context) { diff --git a/app/src/main/java/com/hippo/ehviewer/ui/scene/DownloadLabelsScene.java b/app/src/main/java/com/hippo/ehviewer/ui/scene/DownloadLabelsScene.java index 436f0f5fc..fc33115da 100644 --- a/app/src/main/java/com/hippo/ehviewer/ui/scene/DownloadLabelsScene.java +++ b/app/src/main/java/com/hippo/ehviewer/ui/scene/DownloadLabelsScene.java @@ -28,10 +28,12 @@ import android.view.View; import android.view.ViewGroup; import android.widget.Button; import android.widget.TextView; + import androidx.annotation.Nullable; import androidx.appcompat.app.AlertDialog; import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; + import com.h6ah4i.android.widget.advrecyclerview.animator.GeneralItemAnimator; import com.h6ah4i.android.widget.advrecyclerview.animator.SwipeDismissItemAnimator; import com.h6ah4i.android.widget.advrecyclerview.draggable.DraggableItemAdapter; @@ -47,6 +49,7 @@ import com.hippo.util.DrawableManager; import com.hippo.view.ViewTransition; import com.hippo.yorozuya.AssertUtils; import com.hippo.yorozuya.ViewUtils; + import java.util.List; public class DownloadLabelsScene extends ToolbarScene { @@ -83,7 +86,7 @@ public class DownloadLabelsScene extends ToolbarScene { @Nullable @Override public View onCreateView3(LayoutInflater inflater, - @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { + @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { View view = inflater.inflate(R.layout.scene_label_list, container, false); mRecyclerView = (EasyRecyclerView) ViewUtils.$$(view, R.id.recycler_view); @@ -233,7 +236,7 @@ public class DownloadLabelsScene extends ToolbarScene { private final int mPosition; public RenameLabelDialogHelper(EditTextDialogBuilder builder, AlertDialog dialog, - String originalLabel, int position) { + String originalLabel, int position) { mBuilder = builder; mDialog = dialog; mOriginalLabel = originalLabel; @@ -306,23 +309,23 @@ public class DownloadLabelsScene extends ToolbarScene { } else if (delete == v) { final DownloadLabel label = mList.get(position); new AlertDialog.Builder(context) - .setTitle(R.string.delete_label_title) - .setMessage(getString(R.string.delete_label_message, label.getLabel())) - .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - EhApplication.getDownloadManager(context).deleteLabel(label.getLabel()); - } - }) - .setOnDismissListener(new DialogInterface.OnDismissListener() { - @Override - public void onDismiss(DialogInterface dialog) { - if (null != mAdapter) { - mAdapter.notifyDataSetChanged(); + .setTitle(R.string.delete_label_title) + .setMessage(getString(R.string.delete_label_message, label.getLabel())) + .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + EhApplication.getDownloadManager(context).deleteLabel(label.getLabel()); } - updateView(); - } - }).show(); + }) + .setOnDismissListener(new DialogInterface.OnDismissListener() { + @Override + public void onDismiss(DialogInterface dialog) { + if (null != mAdapter) { + mAdapter.notifyDataSetChanged(); + } + updateView(); + } + }).show(); } } } @@ -385,9 +388,11 @@ public class DownloadLabelsScene extends ToolbarScene { } @Override - public void onItemDragStarted(int position) { } + public void onItemDragStarted(int position) { + } @Override - public void onItemDragFinished(int fromPosition, int toPosition, boolean result) { } + public void onItemDragFinished(int fromPosition, int toPosition, boolean result) { + } } } diff --git a/app/src/main/java/com/hippo/ehviewer/ui/scene/DownloadsScene.java b/app/src/main/java/com/hippo/ehviewer/ui/scene/DownloadsScene.java index 35ab58070..ae5ac3f47 100644 --- a/app/src/main/java/com/hippo/ehviewer/ui/scene/DownloadsScene.java +++ b/app/src/main/java/com/hippo/ehviewer/ui/scene/DownloadsScene.java @@ -43,14 +43,17 @@ import android.widget.ArrayAdapter; import android.widget.ListView; import android.widget.ProgressBar; import android.widget.TextView; + import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.appcompat.app.AlertDialog; import androidx.appcompat.widget.Toolbar; import androidx.core.view.ViewCompat; +import androidx.drawerlayout.widget.DrawerLayout; import androidx.recyclerview.widget.RecyclerView; import androidx.recyclerview.widget.SimpleItemAnimator; import androidx.recyclerview.widget.StaggeredGridLayoutManager; + import com.github.amlcurran.showcaseview.ShowcaseView; import com.github.amlcurran.showcaseview.SimpleShowcaseEventListener; import com.github.amlcurran.showcaseview.targets.PointTarget; @@ -60,7 +63,6 @@ import com.hippo.android.resource.AttrResources; import com.hippo.app.CheckBoxDialogBuilder; import com.hippo.conaco.DataContainer; import com.hippo.conaco.ProgressNotifier; -import com.hippo.drawerlayout.DrawerLayout; import com.hippo.easyrecyclerview.EasyRecyclerView; import com.hippo.easyrecyclerview.FastScroller; import com.hippo.easyrecyclerview.HandlerDrawable; @@ -97,6 +99,7 @@ import com.hippo.yorozuya.IOUtils; import com.hippo.yorozuya.ObjectUtils; import com.hippo.yorozuya.ViewUtils; import com.hippo.yorozuya.collect.LongList; + import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; @@ -110,15 +113,11 @@ public class DownloadsScene extends ToolbarScene EasyRecyclerView.OnItemLongClickListener, FabLayout.OnClickFabListener, FastScroller.OnDragHandlerListener { - private static final String TAG = DownloadsScene.class.getSimpleName(); - public static final String KEY_GID = "gid"; - public static final String KEY_ACTION = "action"; - private static final String KEY_LABEL = "label"; - public static final String ACTION_CLEAR_DOWNLOAD_SERVICE = "clear_download_service"; - + private static final String TAG = DownloadsScene.class.getSimpleName(); + private static final String KEY_LABEL = "label"; /*--------------- Whole life cycle ---------------*/ @@ -147,6 +146,20 @@ public class DownloadsScene extends ToolbarScene private int mInitPosition = -1; + private static void deleteFileAsync(UniFile... files) { + new AsyncTask() { + @Override + protected Void doInBackground(UniFile... params) { + for (UniFile file : params) { + if (file != null) { + file.delete(); + } + } + return null; + } + }.executeOnExecutor(IoThreadPoolExecutor.getInstance(), files); + } + @Override public int getNavCheckedItem() { return R.id.nav_downloads; @@ -276,7 +289,7 @@ public class DownloadsScene extends ToolbarScene @Nullable @Override public View onCreateView3(LayoutInflater inflater, - @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { + @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { View view = inflater.inflate(R.layout.scene_download, container, false); View content = ViewUtils.$$(view, R.id.content); @@ -515,7 +528,7 @@ public class DownloadsScene extends ToolbarScene @Override public View onCreateDrawerView(LayoutInflater inflater, - @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { + @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { View view = inflater.inflate(R.layout.drawer_list, container, false); final Context context = getContext2(); @@ -574,7 +587,7 @@ public class DownloadsScene extends ToolbarScene final List labels = new ArrayList<>(list.size() + 1); // Add default label name labels.add(getString(R.string.default_download_label_name)); - for (DownloadLabel raw: list) { + for (DownloadLabel raw : list) { labels.add(raw.getLabel()); } @@ -926,20 +939,6 @@ public class DownloadsScene extends ToolbarScene holder.speed.setText(FileUtils.humanReadableByteCount(speed, false) + "/S"); } - private static void deleteFileAsync(UniFile... files) { - new AsyncTask() { - @Override - protected Void doInBackground(UniFile... params) { - for (UniFile file: params) { - if (file != null) { - file.delete(); - } - } - return null; - } - }.executeOnExecutor(IoThreadPoolExecutor.getInstance(), files); - } - private class DeleteDialogHelper implements DialogInterface.OnClickListener { private final GalleryInfo mGalleryInfo; @@ -981,7 +980,7 @@ public class DownloadsScene extends ToolbarScene private final CheckBoxDialogBuilder mBuilder; public DeleteRangeDialogHelper(List downloadInfoList, - LongList gidList, CheckBoxDialogBuilder builder) { + LongList gidList, CheckBoxDialogBuilder builder) { mDownloadInfoList = downloadInfoList; mGidList = gidList; mBuilder = builder; @@ -1009,7 +1008,7 @@ public class DownloadsScene extends ToolbarScene if (checked) { UniFile[] files = new UniFile[mDownloadInfoList.size()]; int i = 0; - for (DownloadInfo info: mDownloadInfoList) { + for (DownloadInfo info : mDownloadInfoList) { // Remove download path EhDB.removeDownloadDirname(info.gid); // Put file @@ -1197,7 +1196,7 @@ public class DownloadsScene extends ToolbarScene } } - private class DownloadChoiceListener implements EasyRecyclerView.CustomChoiceListener { + private class DownloadChoiceListener implements EasyRecyclerView.CustomChoiceListener { @Override public void onIntoCustomChoice(EasyRecyclerView view) { @@ -1260,7 +1259,8 @@ public class DownloadsScene extends ToolbarScene } @Override - public void onUrlMoved(String requestUrl, String responseUrl) {} + public void onUrlMoved(String requestUrl, String responseUrl) { + } @Override public boolean save(InputStream is, long length, String mediaType, ProgressNotifier notify) { diff --git a/app/src/main/java/com/hippo/ehviewer/ui/scene/EhCallback.java b/app/src/main/java/com/hippo/ehviewer/ui/scene/EhCallback.java index 5bc1eacc7..e0f5c7176 100644 --- a/app/src/main/java/com/hippo/ehviewer/ui/scene/EhCallback.java +++ b/app/src/main/java/com/hippo/ehviewer/ui/scene/EhCallback.java @@ -18,14 +18,16 @@ package com.hippo.ehviewer.ui.scene; import android.content.Context; import android.widget.Toast; + import androidx.annotation.StringRes; + import com.hippo.ehviewer.EhApplication; import com.hippo.ehviewer.client.EhClient; import com.hippo.ehviewer.ui.MainActivity; import com.hippo.scene.SceneFragment; import com.hippo.scene.StageActivity; -public abstract class EhCallback implements EhClient.Callback { +public abstract class EhCallback implements EhClient.Callback { private final EhApplication mApplication; private final int mStageId; diff --git a/app/src/main/java/com/hippo/ehviewer/ui/scene/EnterGalleryDetailTransaction.java b/app/src/main/java/com/hippo/ehviewer/ui/scene/EnterGalleryDetailTransaction.java index 9f79e84eb..3f6b1e070 100644 --- a/app/src/main/java/com/hippo/ehviewer/ui/scene/EnterGalleryDetailTransaction.java +++ b/app/src/main/java/com/hippo/ehviewer/ui/scene/EnterGalleryDetailTransaction.java @@ -18,10 +18,12 @@ package com.hippo.ehviewer.ui.scene; import android.content.Context; import android.view.View; + import androidx.core.view.ViewCompat; import androidx.fragment.app.Fragment; import androidx.fragment.app.FragmentTransaction; import androidx.transition.TransitionInflater; + import com.hippo.ehviewer.R; import com.hippo.scene.TransitionHelper; @@ -35,7 +37,7 @@ class EnterGalleryDetailTransaction implements TransitionHelper { @Override public boolean onTransition(Context context, FragmentTransaction transaction, - Fragment exit, Fragment enter) { + Fragment exit, Fragment enter) { if (mThumb == null || !(enter instanceof GalleryDetailScene)) { return false; } diff --git a/app/src/main/java/com/hippo/ehviewer/ui/scene/FavoritesScene.java b/app/src/main/java/com/hippo/ehviewer/ui/scene/FavoritesScene.java index bf8a4c953..45d0e633e 100644 --- a/app/src/main/java/com/hippo/ehviewer/ui/scene/FavoritesScene.java +++ b/app/src/main/java/com/hippo/ehviewer/ui/scene/FavoritesScene.java @@ -39,6 +39,7 @@ import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; import android.widget.TextView; + import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.appcompat.app.AlertDialog; @@ -47,6 +48,7 @@ import androidx.drawerlayout.widget.DrawerLayout; import androidx.recyclerview.widget.DividerItemDecoration; import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; + import com.github.amlcurran.showcaseview.ShowcaseView; import com.github.amlcurran.showcaseview.SimpleShowcaseEventListener; import com.github.amlcurran.showcaseview.targets.PointTarget; @@ -89,6 +91,7 @@ import com.hippo.yorozuya.AssertUtils; import com.hippo.yorozuya.ObjectUtils; import com.hippo.yorozuya.SimpleHandler; import com.hippo.yorozuya.ViewUtils; + import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -106,7 +109,10 @@ public class FavoritesScene extends BaseScene implements private static final String KEY_SEARCH_MODE = "search_mode"; private static final String KEY_HAS_FIRST_REFRESH = "has_first_refresh"; private static final String KEY_FAV_COUNT_ARRAY = "fav_count_array"; - + // For modify action + private final List mModifyGiList = new ArrayList<>(); + public int current; // -1 for error + public int limit; // -1 for error @Nullable @ViewLifeCircle private EasyRecyclerView mRecyclerView; @@ -116,7 +122,6 @@ public class FavoritesScene extends BaseScene implements @Nullable @ViewLifeCircle private FabLayout mFabLayout; - @Nullable @ViewLifeCircle private FavoritesAdapter mAdapter; @@ -130,10 +135,8 @@ public class FavoritesScene extends BaseScene implements @ViewLifeCircle private DrawerArrowDrawable mLeftDrawable; private AddDeleteDrawable mActionFabDrawable; - @Nullable private EhDrawerLayout mDrawerLayout; - @Nullable @DrawerLifeCircle private FavDrawerAdapter mDrawerAdapter; @@ -149,13 +152,8 @@ public class FavoritesScene extends BaseScene implements @Nullable @WholeLifeCircle private FavListUrlBuilder mUrlBuilder; - - public int current; // -1 for error - public int limit; // -1 for error - private int mFavLocalCount = 0; private int mFavCountSum = 0; - private boolean mHasFirstRefresh; private boolean mSearchMode; // Avoid unnecessary search bar update @@ -167,11 +165,21 @@ public class FavoritesScene extends BaseScene implements // For modify action private int mModifyFavCat; // For modify action - private final List mModifyGiList = new ArrayList<>(); - // For modify action private boolean mModifyAdd; private ShowcaseView mShowcaseView; + private Runnable showNormalFabsRunnable = new Runnable() { + @Override + public void run() { + if (mFabLayout != null) { + mFabLayout.setSecondaryFabVisibilityAt(0, true); + mFabLayout.setSecondaryFabVisibilityAt(1, true); + mFabLayout.setSecondaryFabVisibilityAt(2, false); + mFabLayout.setSecondaryFabVisibilityAt(3, false); + mFabLayout.setSecondaryFabVisibilityAt(4, false); + } + } + }; @Override public int getNavCheckedItem() { @@ -243,7 +251,7 @@ public class FavoritesScene extends BaseScene implements @Nullable @Override public View onCreateView2(LayoutInflater inflater, - @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { + @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { View view = inflater.inflate(R.layout.scene_favorites, container, false); ContentLayout contentLayout = (ContentLayout) view.findViewById(R.id.content_layout); MainActivity activity = getActivity2(); @@ -426,72 +434,9 @@ public class FavoritesScene extends BaseScene implements mOldKeyword = null; } - private class FavDrawerHolder extends RecyclerView.ViewHolder { - - private final TextView key; - private final TextView value; - - private FavDrawerHolder(View itemView) { - super(itemView); - key = (TextView) ViewUtils.$$(itemView, R.id.key); - value = (TextView) ViewUtils.$$(itemView, R.id.value); - } - } - - private class FavDrawerAdapter extends RecyclerView.Adapter { - - private final LayoutInflater mInflater; - - private FavDrawerAdapter(LayoutInflater inflater) { - mInflater = inflater; - } - - @Override - public int getItemViewType(int position) { - return position; - } - - @NonNull - @Override - public FavDrawerHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { - return new FavDrawerHolder(mInflater.inflate(R.layout.item_drawer_favorites, parent, false)); - } - - @Override - @SuppressLint("SetTextI18n") - public void onBindViewHolder(@NonNull FavDrawerHolder holder, int position) { - if (0 == position) { - holder.key.setText(R.string.local_favorites); - holder.value.setText(Integer.toString(mFavLocalCount)); - holder.itemView.setEnabled(true); - } else if (1 == position){ - holder.key.setText(R.string.cloud_favorites); - holder.value.setText(Integer.toString(mFavCountSum)); - holder.itemView.setEnabled(true); - } else { - if (null == mFavCatArray || null == mFavCountArray || - mFavCatArray.length < (position - 1) || - mFavCountArray.length < (position - 1)) { - return; - } - holder.key.setText(mFavCatArray[position - 2]); - holder.value.setText(Integer.toString(mFavCountArray[position - 2])); - holder.itemView.setEnabled(true); - } - } - - @Override - public int getItemCount() { - if (null == mFavCatArray) { - return 2; - } - return 12; - } - } - @Override public View onCreateDrawerView(LayoutInflater inflater, - @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { + @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { View view = inflater.inflate(R.layout.drawer_list_rv, container, false); final Context context = getContext2(); Toolbar toolbar = (Toolbar) ViewUtils.$$(view, R.id.toolbar); @@ -583,7 +528,7 @@ public class FavoritesScene extends BaseScene implements @Override @Implemented(EasyRecyclerView.OnItemClickListener.class) public boolean onItemClick(EasyRecyclerView parent, View view, int position, long id) { - if (mDrawerLayout != null && mDrawerLayout.isDrawerOpen(Gravity.RIGHT)){ + if (mDrawerLayout != null && mDrawerLayout.isDrawerOpen(Gravity.RIGHT)) { // Skip if in search mode if (mRecyclerView != null && mRecyclerView.isInCustomChoice()) { return true; @@ -781,8 +726,8 @@ public class FavoritesScene extends BaseScene implements final EditTextDialogBuilder builder = new EditTextDialogBuilder(context, null, hint); builder.getEditText().setInputType(InputType.TYPE_CLASS_NUMBER | InputType.TYPE_NUMBER_FLAG_DECIMAL); final AlertDialog dialog = builder.setTitle(R.string.go_to) - .setPositiveButton(android.R.string.ok, null) - .show(); + .setPositiveButton(android.R.string.ok, null) + .show(); dialog.getButton(DialogInterface.BUTTON_POSITIVE).setOnClickListener(v -> { if (null == mHelper) { dialog.dismiss(); @@ -793,7 +738,7 @@ public class FavoritesScene extends BaseScene implements int goTo; try { goTo = Integer.parseInt(text) - 1; - } catch (NumberFormatException e){ + } catch (NumberFormatException e) { builder.setError(getString(R.string.error_invalid_number)); return; } @@ -880,19 +825,6 @@ public class FavoritesScene extends BaseScene implements } } - private Runnable showNormalFabsRunnable = new Runnable() { - @Override - public void run() { - if (mFabLayout != null) { - mFabLayout.setSecondaryFabVisibilityAt(0, true); - mFabLayout.setSecondaryFabVisibilityAt(1, true); - mFabLayout.setSecondaryFabVisibilityAt(2, false); - mFabLayout.setSecondaryFabVisibilityAt(3, false); - mFabLayout.setSecondaryFabVisibilityAt(4, false); - } - } - }; - private void showNormalFabs() { // Delay showing normal fabs to avoid mutation SimpleHandler.getInstance().removeCallbacks(showNormalFabsRunnable); @@ -953,7 +885,7 @@ public class FavoritesScene extends BaseScene implements } private void enterSearchMode(boolean animation) { - if (mSearchMode ||mSearchBar == null || mSearchBarMover == null || mLeftDrawable == null) { + if (mSearchMode || mSearchBar == null || mSearchBarMover == null || mLeftDrawable == null) { return; } mSearchMode = true; @@ -977,13 +909,13 @@ public class FavoritesScene extends BaseScene implements mHelper.isCurrentTask(taskId)) { if (mFavCatArray != null) { - System.arraycopy(result.catArray, 0, mFavCatArray, 0,10); + System.arraycopy(result.catArray, 0, mFavCatArray, 0, 10); } mFavCountArray = result.countArray; - if (mFavCountArray != null){ + if (mFavCountArray != null) { mFavCountSum = 0; - for (int i = 0; i < 10; i++ ){ + for (int i = 0; i < 10; i++) { mFavCountSum = mFavCountSum + mFavCountArray[i]; } Settings.putFavCloudCount(mFavCountSum); @@ -1031,6 +963,166 @@ public class FavoritesScene extends BaseScene implements } } + private static class AddFavoritesListener extends EhCallback { + + private final int mTaskId; + private final String mKeyword; + private final List mBackup; + + private AddFavoritesListener(Context context, int stageId, + String sceneTag, int taskId, String keyword, List backup) { + super(context, stageId, sceneTag); + mTaskId = taskId; + mKeyword = keyword; + mBackup = backup; + } + + @Override + public void onSuccess(Void result) { + FavoritesScene scene = getScene(); + if (scene != null) { + scene.onGetFavoritesLocal(mKeyword, mTaskId); + } + } + + @Override + public void onFailure(Exception e) { + // TODO It's a failure, add all of backup back to db. + // But how to known which one is failed? + EhDB.putLocalFavorites(mBackup); + + FavoritesScene scene = getScene(); + if (scene != null) { + scene.onGetFavoritesLocal(mKeyword, mTaskId); + } + } + + @Override + public void onCancel() { + } + + @Override + public boolean isInstance(SceneFragment scene) { + return scene instanceof FavoritesScene; + } + } + + private static class GetFavoritesListener extends EhCallback { + + private final int mTaskId; + // Local fav is shown now, but operation need be done for cloud fav + private final boolean mLocal; + private final String mKeyword; + + private GetFavoritesListener(Context context, int stageId, + String sceneTag, int taskId, boolean local, String keyword) { + super(context, stageId, sceneTag); + mTaskId = taskId; + mLocal = local; + mKeyword = keyword; + } + + @Override + public void onSuccess(FavoritesParser.Result result) { + // Put fav cat + Settings.putFavCat(result.catArray); + Settings.putFavCount(result.countArray); + FavoritesScene scene = getScene(); + if (scene != null) { + if (mLocal) { + scene.onGetFavoritesLocal(mKeyword, mTaskId); + } else { + scene.onGetFavoritesSuccess(result, mTaskId); + } + } + } + + @Override + public void onFailure(Exception e) { + FavoritesScene scene = getScene(); + if (scene != null) { + if (mLocal) { + e.printStackTrace(); + scene.onGetFavoritesLocal(mKeyword, mTaskId); + } else { + scene.onGetFavoritesFailure(e, mTaskId); + } + } + } + + @Override + public void onCancel() { + } + + @Override + public boolean isInstance(SceneFragment scene) { + return scene instanceof FavoritesScene; + } + } + + private class FavDrawerHolder extends RecyclerView.ViewHolder { + + private final TextView key; + private final TextView value; + + private FavDrawerHolder(View itemView) { + super(itemView); + key = (TextView) ViewUtils.$$(itemView, R.id.key); + value = (TextView) ViewUtils.$$(itemView, R.id.value); + } + } + + private class FavDrawerAdapter extends RecyclerView.Adapter { + + private final LayoutInflater mInflater; + + private FavDrawerAdapter(LayoutInflater inflater) { + mInflater = inflater; + } + + @Override + public int getItemViewType(int position) { + return position; + } + + @NonNull + @Override + public FavDrawerHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { + return new FavDrawerHolder(mInflater.inflate(R.layout.item_drawer_favorites, parent, false)); + } + + @Override + @SuppressLint("SetTextI18n") + public void onBindViewHolder(@NonNull FavDrawerHolder holder, int position) { + if (0 == position) { + holder.key.setText(R.string.local_favorites); + holder.value.setText(Integer.toString(mFavLocalCount)); + holder.itemView.setEnabled(true); + } else if (1 == position) { + holder.key.setText(R.string.cloud_favorites); + holder.value.setText(Integer.toString(mFavCountSum)); + holder.itemView.setEnabled(true); + } else { + if (null == mFavCatArray || null == mFavCountArray || + mFavCatArray.length < (position - 1) || + mFavCountArray.length < (position - 1)) { + return; + } + holder.key.setText(mFavCatArray[position - 2]); + holder.value.setText(Integer.toString(mFavCountArray[position - 2])); + holder.itemView.setEnabled(true); + } + } + + @Override + public int getItemCount() { + if (null == mFavCatArray) { + return 2; + } + return 12; + } + } + private class DeleteDialogHelper implements DialogInterface.OnClickListener, DialogInterface.OnCancelListener { @@ -1121,7 +1213,7 @@ public class FavoritesScene extends BaseScene implements private class FavoritesAdapter extends GalleryAdapter { public FavoritesAdapter(@NonNull LayoutInflater inflater, @NonNull Resources resources, - @NonNull RecyclerView recyclerView, int type) { + @NonNull RecyclerView recyclerView, int type) { super(inflater, resources, recyclerView, type, false); } @@ -1142,7 +1234,7 @@ public class FavoritesScene extends BaseScene implements @Override protected void getPageData(final int taskId, int type, int page) { MainActivity activity = getActivity2(); - if (null == activity || null == mUrlBuilder || null == mClient ) { + if (null == activity || null == mUrlBuilder || null == mClient) { return; } @@ -1266,99 +1358,4 @@ public class FavoritesScene extends BaseScene implements } } } - - private static class AddFavoritesListener extends EhCallback { - - private final int mTaskId; - private final String mKeyword; - private final List mBackup; - - private AddFavoritesListener(Context context, int stageId, - String sceneTag, int taskId, String keyword, List backup) { - super(context, stageId, sceneTag); - mTaskId = taskId; - mKeyword = keyword; - mBackup = backup; - } - - @Override - public void onSuccess(Void result) { - FavoritesScene scene = getScene(); - if (scene != null) { - scene.onGetFavoritesLocal(mKeyword, mTaskId); - } - } - - @Override - public void onFailure(Exception e) { - // TODO It's a failure, add all of backup back to db. - // But how to known which one is failed? - EhDB.putLocalFavorites(mBackup); - - FavoritesScene scene = getScene(); - if (scene != null) { - scene.onGetFavoritesLocal(mKeyword, mTaskId); - } - } - - @Override - public void onCancel() {} - - @Override - public boolean isInstance(SceneFragment scene) { - return scene instanceof FavoritesScene; - } - } - - private static class GetFavoritesListener extends EhCallback { - - private final int mTaskId; - // Local fav is shown now, but operation need be done for cloud fav - private final boolean mLocal; - private final String mKeyword; - - private GetFavoritesListener(Context context, int stageId, - String sceneTag, int taskId, boolean local, String keyword) { - super(context, stageId, sceneTag); - mTaskId = taskId; - mLocal = local; - mKeyword = keyword; - } - - @Override - public void onSuccess(FavoritesParser.Result result) { - // Put fav cat - Settings.putFavCat(result.catArray); - Settings.putFavCount(result.countArray); - FavoritesScene scene = getScene(); - if (scene != null) { - if (mLocal) { - scene.onGetFavoritesLocal(mKeyword, mTaskId); - } else { - scene.onGetFavoritesSuccess(result, mTaskId); - } - } - } - - @Override - public void onFailure(Exception e) { - FavoritesScene scene = getScene(); - if (scene != null) { - if (mLocal) { - e.printStackTrace(); - scene.onGetFavoritesLocal(mKeyword, mTaskId); - } else { - scene.onGetFavoritesFailure(e, mTaskId); - } - } - } - - @Override - public void onCancel() {} - - @Override - public boolean isInstance(SceneFragment scene) { - return scene instanceof FavoritesScene; - } - } } diff --git a/app/src/main/java/com/hippo/ehviewer/ui/scene/GalleryAdapter.java b/app/src/main/java/com/hippo/ehviewer/ui/scene/GalleryAdapter.java index 7183f380d..1bb47d773 100644 --- a/app/src/main/java/com/hippo/ehviewer/ui/scene/GalleryAdapter.java +++ b/app/src/main/java/com/hippo/ehviewer/ui/scene/GalleryAdapter.java @@ -23,12 +23,14 @@ import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.TextView; + import androidx.annotation.IntDef; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.core.view.ViewCompat; import androidx.recyclerview.widget.RecyclerView; import androidx.recyclerview.widget.StaggeredGridLayoutManager; + import com.hippo.drawable.TriangleDrawable; import com.hippo.easyrecyclerview.MarginItemDecoration; import com.hippo.ehviewer.EhApplication; @@ -41,35 +43,30 @@ import com.hippo.ehviewer.download.DownloadManager; import com.hippo.ehviewer.widget.TileThumb; import com.hippo.widget.recyclerview.AutoStaggeredGridLayoutManager; import com.hippo.yorozuya.ViewUtils; + import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; abstract class GalleryAdapter extends RecyclerView.Adapter { - @IntDef({TYPE_LIST, TYPE_GRID}) - @Retention(RetentionPolicy.SOURCE) - public @interface Type {} - public static final int TYPE_INVALID = -1; public static final int TYPE_LIST = 0; public static final int TYPE_GRID = 1; - private final LayoutInflater mInflater; private final Resources mResources; private final RecyclerView mRecyclerView; private final AutoStaggeredGridLayoutManager mLayoutManager; private final int mPaddingTopSB; - private MarginItemDecoration mListDecoration; - private MarginItemDecoration mGirdDecoration; private final int mListThumbWidth; private final int mListThumbHeight; + private MarginItemDecoration mListDecoration; + private MarginItemDecoration mGirdDecoration; private int mType = TYPE_INVALID; private boolean mShowFavourited; - private DownloadManager mDownloadManager; public GalleryAdapter(@NonNull LayoutInflater inflater, @NonNull Resources resources, - @NonNull RecyclerView recyclerView, int type, boolean showFavourited) { + @NonNull RecyclerView recyclerView, int type, boolean showFavourited) { mInflater = inflater; mResources = resources; mRecyclerView = recyclerView; @@ -208,7 +205,7 @@ abstract class GalleryAdapter extends RecyclerView.Adapter { holder.pages.setText(null); holder.pages.setVisibility(View.GONE); } else { - holder.pages.setText(Integer.toString(gi.pages) + "P"); + holder.pages.setText(gi.pages + "P"); holder.pages.setVisibility(View.VISIBLE); } if (TextUtils.isEmpty(gi.simpleLanguage)) { @@ -242,4 +239,9 @@ abstract class GalleryAdapter extends RecyclerView.Adapter { // Update transition name ViewCompat.setTransitionName(holder.thumb, TransitionNameFactory.getThumbTransitionName(gi.gid)); } + + @IntDef({TYPE_LIST, TYPE_GRID}) + @Retention(RetentionPolicy.SOURCE) + public @interface Type { + } } diff --git a/app/src/main/java/com/hippo/ehviewer/ui/scene/GalleryCommentsScene.java b/app/src/main/java/com/hippo/ehviewer/ui/scene/GalleryCommentsScene.java index 621f93537..a11887b9a 100644 --- a/app/src/main/java/com/hippo/ehviewer/ui/scene/GalleryCommentsScene.java +++ b/app/src/main/java/com/hippo/ehviewer/ui/scene/GalleryCommentsScene.java @@ -45,12 +45,14 @@ import android.view.ViewGroup; import android.widget.EditText; import android.widget.ImageView; import android.widget.TextView; + import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.appcompat.app.AlertDialog; import androidx.recyclerview.widget.DefaultItemAnimator; import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; + import com.google.android.material.floatingactionbutton.FloatingActionButton; import com.hippo.android.resource.AttrResources; import com.hippo.easyrecyclerview.EasyRecyclerView; @@ -87,6 +89,7 @@ import com.hippo.yorozuya.SimpleAnimatorListener; import com.hippo.yorozuya.StringUtils; import com.hippo.yorozuya.ViewUtils; import com.hippo.yorozuya.collect.IntList; + import java.util.ArrayList; import java.util.List; @@ -101,14 +104,15 @@ public final class GalleryCommentsScene extends ToolbarScene public static final String KEY_GID = "gid"; public static final String KEY_TOKEN = "token"; public static final String KEY_COMMENT_LIST = "comment_list"; - + private static final int TYPE_COMMENT = 0; + private static final int TYPE_MORE = 1; + private static final int TYPE_PROGRESS = 2; private long mApiUid; private String mApiKey; private long mGid; private String mToken; @Nullable private GalleryCommentList mCommentList; - @Nullable private EasyRecyclerView mRecyclerView; @Nullable @@ -125,13 +129,10 @@ public final class GalleryCommentsScene extends ToolbarScene private CommentAdapter mAdapter; @Nullable private ViewTransition mViewTransition; - private Drawable mSendDrawable; private Drawable mPencilDrawable; private long mCommentId; - private boolean mInAnimation = false; - private boolean mShowAllComments = false; private boolean mRefreshingComments = false; @@ -185,7 +186,7 @@ public final class GalleryCommentsScene extends ToolbarScene @Nullable @Override public View onCreateView3(LayoutInflater inflater, - @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { + @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { View view = inflater.inflate(R.layout.scene_gallery_comments, container, false); mRecyclerView = (EasyRecyclerView) ViewUtils.$$(view, R.id.recycler_view); TextView tip = (TextView) ViewUtils.$$(view, R.id.tip); @@ -291,18 +292,6 @@ public final class GalleryCommentsScene extends ToolbarScene EhApplication.getEhClient(context).execute(request); } - private class InfoHolder extends RecyclerView.ViewHolder { - - private final TextView key; - private final TextView value; - - public InfoHolder(View itemView) { - super(itemView); - key = (TextView) ViewUtils.$$(itemView, R.id.key); - value = (TextView) ViewUtils.$$(itemView, R.id.value); - } - } - @SuppressLint("InflateParams") public void showVoteStatusDialog(Context context, String voteStatus) { String[] temp = StringUtils.split(voteStatus, ','); @@ -389,7 +378,7 @@ public final class GalleryCommentsScene extends ToolbarScene @Override public void onClick(DialogInterface dialog, int which) { if (which < 0 || which >= menuId.size()) { - return; + return; } int id = menuId.get(which); switch (id) { @@ -445,9 +434,9 @@ public final class GalleryCommentsScene extends ToolbarScene if (url != null) { // Request EhRequest request = new EhRequest() - .setMethod(EhClient.METHOD_GET_GALLERY_DETAIL) - .setArgs(url) - .setCallback(new RefreshCommentListener(activity, activity.getStageId(), getTag())); + .setMethod(EhClient.METHOD_GET_GALLERY_DETAIL) + .setArgs(url) + .setCallback(new RefreshCommentListener(activity, activity.getStageId(), getTag())); EhApplication.getEhClient(activity).execute(request); } } @@ -649,9 +638,201 @@ public final class GalleryCommentsScene extends ToolbarScene } } - private static final int TYPE_COMMENT = 0; - private static final int TYPE_MORE = 1; - private static final int TYPE_PROGRESS = 2; + private void onRefreshGallerySuccess(GalleryCommentList result) { + if (mAdapter == null) { + return; + } + + mRefreshingComments = false; + mCommentList = result; + mAdapter.notifyDataSetChanged(); + + updateView(true); + + Bundle re = new Bundle(); + re.putParcelable(KEY_COMMENT_LIST, result); + setResult(SceneFragment.RESULT_OK, re); + } + + private void onRefreshGalleryFailure() { + if (mAdapter == null) { + return; + } + + mRefreshingComments = false; + int position = mAdapter.getItemCount() - 1; + if (position >= 0) { + mAdapter.notifyItemChanged(position); + } + } + + private void onCommentGallerySuccess(GalleryCommentList result) { + if (mAdapter == null) { + return; + } + + mCommentList = result; + mAdapter.notifyDataSetChanged(); + Bundle re = new Bundle(); + re.putParcelable(KEY_COMMENT_LIST, result); + setResult(SceneFragment.RESULT_OK, re); + + // Remove text + if (mEditText != null) { + mEditText.setText(""); + } + + updateView(true); + } + + private void onVoteCommentSuccess(VoteCommentParser.Result result) { + if (mAdapter == null || mCommentList == null || mCommentList.comments == null) { + return; + } + + int position = -1; + for (int i = 0, n = mCommentList.comments.length; i < n; i++) { + GalleryComment comment = mCommentList.comments[i]; + if (comment.id == result.id) { + position = i; + break; + } + } + + if (-1 == position) { + Log.d(TAG, "Can't find comment with id " + result.id); + return; + } + + // Update comment + GalleryComment comment = mCommentList.comments[position]; + comment.score = result.score; + if (result.expectVote > 0) { + comment.voteUpEd = 0 != result.vote; + comment.voteDownEd = false; + } else { + comment.voteDownEd = 0 != result.vote; + comment.voteUpEd = false; + } + + mAdapter.notifyItemChanged(position); + + Bundle re = new Bundle(); + re.putParcelable(KEY_COMMENT_LIST, mCommentList); + setResult(SceneFragment.RESULT_OK, re); + } + + private static class RefreshCommentListener extends EhCallback { + + public RefreshCommentListener(Context context, int stageId, String sceneTag) { + super(context, stageId, sceneTag); + } + + @Override + public void onSuccess(GalleryDetail result) { + GalleryCommentsScene scene = getScene(); + if (scene != null) { + scene.onRefreshGallerySuccess(result.comments); + } + } + + @Override + public void onFailure(Exception e) { + GalleryCommentsScene scene = getScene(); + if (scene != null) { + scene.onRefreshGalleryFailure(); + } + } + + @Override + public void onCancel() { + } + + @Override + public boolean isInstance(SceneFragment scene) { + return scene instanceof GalleryCommentsScene; + } + } + + private static class CommentGalleryListener extends EhCallback { + + private long mCommentId; + + public CommentGalleryListener(Context context, int stageId, String sceneTag, long commentId) { + super(context, stageId, sceneTag); + mCommentId = commentId; + } + + @Override + public void onSuccess(GalleryCommentList result) { + showTip(mCommentId != 0 ? R.string.edit_comment_successfully : R.string.comment_successfully, LENGTH_SHORT); + + GalleryCommentsScene scene = getScene(); + if (scene != null) { + scene.onCommentGallerySuccess(result); + } + } + + @Override + public void onFailure(Exception e) { + showTip(getContent().getString(mCommentId != 0 ? R.string.edit_comment_failed : R.string.comment_failed) + "\n" + ExceptionUtils.getReadableString(e), LENGTH_LONG); + } + + @Override + public void onCancel() { + } + + @Override + public boolean isInstance(SceneFragment scene) { + return scene instanceof GalleryCommentsScene; + } + } + + private static class VoteCommentListener extends EhCallback { + + public VoteCommentListener(Context context, int stageId, String sceneTag) { + super(context, stageId, sceneTag); + } + + @Override + public void onSuccess(VoteCommentParser.Result result) { + showTip(result.expectVote > 0 ? + (0 != result.vote ? R.string.vote_up_successfully : R.string.cancel_vote_up_successfully) : + (0 != result.vote ? R.string.vote_down_successfully : R.string.cancel_vote_down_successfully), + LENGTH_SHORT); + + GalleryCommentsScene scene = getScene(); + if (scene != null) { + scene.onVoteCommentSuccess(result); + } + } + + @Override + public void onFailure(Exception e) { + showTip(R.string.vote_failed, LENGTH_LONG); + } + + @Override + public void onCancel() { + } + + @Override + public boolean isInstance(SceneFragment scene) { + return scene instanceof GalleryCommentsScene; + } + } + + private class InfoHolder extends RecyclerView.ViewHolder { + + private final TextView key; + private final TextView value; + + public InfoHolder(View itemView) { + super(itemView); + key = (TextView) ViewUtils.$$(itemView, R.id.key); + value = (TextView) ViewUtils.$$(itemView, R.id.value); + } + } private abstract class CommentHolder extends RecyclerView.ViewHolder { public CommentHolder(LayoutInflater inflater, int resId, ViewGroup parent) { @@ -674,7 +855,7 @@ public final class GalleryCommentsScene extends ToolbarScene private CharSequence generateComment(Context context, ObservedTextView textView, GalleryComment comment) { SpannableStringBuilder ssb = Html.fromHtml(comment.comment, new URLImageGetter(textView, - EhApplication.getConaco(context)), null); + EhApplication.getConaco(context)), null); if (0 != comment.id && 0 != comment.score) { int score = comment.score; @@ -683,7 +864,7 @@ public final class GalleryCommentsScene extends ToolbarScene ss.setSpan(new RelativeSizeSpan(0.8f), 0, scoreString.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); ss.setSpan(new StyleSpan(Typeface.BOLD), 0, scoreString.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); ss.setSpan(new ForegroundColorSpan(AttrResources.getAttrColor(context, android.R.attr.textColorSecondary)) - , 0, scoreString.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); + , 0, scoreString.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); ssb.append(" ").append(ss); } @@ -693,7 +874,7 @@ public final class GalleryCommentsScene extends ToolbarScene ss.setSpan(new RelativeSizeSpan(0.8f), 0, str.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); ss.setSpan(new StyleSpan(Typeface.BOLD), 0, str.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); ss.setSpan(new ForegroundColorSpan(AttrResources.getAttrColor(context, android.R.attr.textColorSecondary)), - 0, str.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); + 0, str.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); ssb.append("\n\n").append(ss); } @@ -774,186 +955,4 @@ public final class GalleryCommentsScene extends ToolbarScene } } } - - private void onRefreshGallerySuccess(GalleryCommentList result) { - if (mAdapter == null) { - return; - } - - mRefreshingComments = false; - mCommentList = result; - mAdapter.notifyDataSetChanged(); - - updateView(true); - - Bundle re = new Bundle(); - re.putParcelable(KEY_COMMENT_LIST, result); - setResult(SceneFragment.RESULT_OK, re); - } - - private void onRefreshGalleryFailure() { - if (mAdapter == null) { - return; - } - - mRefreshingComments = false; - int position = mAdapter.getItemCount() - 1; - if (position >= 0) { - mAdapter.notifyItemChanged(position); - } - } - - private void onCommentGallerySuccess(GalleryCommentList result) { - if (mAdapter == null) { - return; - } - - mCommentList = result; - mAdapter.notifyDataSetChanged(); - Bundle re = new Bundle(); - re.putParcelable(KEY_COMMENT_LIST, result); - setResult(SceneFragment.RESULT_OK, re); - - // Remove text - if (mEditText != null) { - mEditText.setText(""); - } - - updateView(true); - } - - private void onVoteCommentSuccess(VoteCommentParser.Result result) { - if (mAdapter == null || mCommentList == null || mCommentList.comments == null ) { - return; - } - - int position = -1; - for (int i = 0, n = mCommentList.comments.length; i < n; i++) { - GalleryComment comment = mCommentList.comments[i]; - if (comment.id == result.id) { - position = i; - break; - } - } - - if (-1 == position) { - Log.d(TAG, "Can't find comment with id " + result.id); - return; - } - - // Update comment - GalleryComment comment = mCommentList.comments[position]; - comment.score = result.score; - if (result.expectVote > 0) { - comment.voteUpEd = 0 != result.vote; - comment.voteDownEd = false; - } else { - comment.voteDownEd = 0 != result.vote; - comment.voteUpEd = false; - } - - mAdapter.notifyItemChanged(position); - - Bundle re = new Bundle(); - re.putParcelable(KEY_COMMENT_LIST, mCommentList); - setResult(SceneFragment.RESULT_OK, re); - } - - private static class RefreshCommentListener extends EhCallback { - - public RefreshCommentListener(Context context, int stageId, String sceneTag) { - super(context, stageId, sceneTag); - } - - @Override - public void onSuccess(GalleryDetail result) { - GalleryCommentsScene scene = getScene(); - if (scene != null) { - scene.onRefreshGallerySuccess(result.comments); - } - } - - @Override - public void onFailure(Exception e) { - GalleryCommentsScene scene = getScene(); - if (scene != null) { - scene.onRefreshGalleryFailure(); - } - } - - @Override - public void onCancel() { } - - @Override - public boolean isInstance(SceneFragment scene) { - return scene instanceof GalleryCommentsScene; - } - } - - private static class CommentGalleryListener extends EhCallback { - - private long mCommentId; - - public CommentGalleryListener(Context context, int stageId, String sceneTag, long commentId) { - super(context, stageId, sceneTag); - mCommentId = commentId; - } - - @Override - public void onSuccess(GalleryCommentList result) { - showTip(mCommentId != 0 ? R.string.edit_comment_successfully : R.string.comment_successfully, LENGTH_SHORT); - - GalleryCommentsScene scene = getScene(); - if (scene != null) { - scene.onCommentGallerySuccess(result); - } - } - - @Override - public void onFailure(Exception e) { - showTip(getContent().getString(mCommentId != 0 ? R.string.edit_comment_failed : R.string.comment_failed) + "\n" + ExceptionUtils.getReadableString(e), LENGTH_LONG); - } - - @Override - public void onCancel() { - } - - @Override - public boolean isInstance(SceneFragment scene) { - return scene instanceof GalleryCommentsScene; - } - } - - private static class VoteCommentListener extends EhCallback { - - public VoteCommentListener(Context context, int stageId, String sceneTag) { - super(context, stageId, sceneTag); - } - - @Override - public void onSuccess(VoteCommentParser.Result result) { - showTip(result.expectVote > 0 ? - (0 != result.vote ? R.string.vote_up_successfully : R.string.cancel_vote_up_successfully) : - (0 != result.vote ? R.string.vote_down_successfully : R.string.cancel_vote_down_successfully), - LENGTH_SHORT); - - GalleryCommentsScene scene = getScene(); - if (scene != null) { - scene.onVoteCommentSuccess(result); - } - } - - @Override - public void onFailure(Exception e) { - showTip(R.string.vote_failed, LENGTH_LONG); - } - - @Override - public void onCancel() {} - - @Override - public boolean isInstance(SceneFragment scene) { - return scene instanceof GalleryCommentsScene; - } - } } diff --git a/app/src/main/java/com/hippo/ehviewer/ui/scene/GalleryDetailScene.java b/app/src/main/java/com/hippo/ehviewer/ui/scene/GalleryDetailScene.java index 96d826800..76c64b82e 100755 --- a/app/src/main/java/com/hippo/ehviewer/ui/scene/GalleryDetailScene.java +++ b/app/src/main/java/com/hippo/ehviewer/ui/scene/GalleryDetailScene.java @@ -33,10 +33,8 @@ import android.util.Pair; import android.view.Gravity; import android.view.LayoutInflater; import android.view.MenuItem; -import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; -import android.view.ViewParent; import android.widget.AdapterView; import android.widget.ArrayAdapter; import android.widget.ImageView; @@ -44,6 +42,7 @@ import android.widget.LinearLayout; import android.widget.ListView; import android.widget.RatingBar; import android.widget.TextView; + import androidx.annotation.IntDef; import androidx.annotation.NonNull; import androidx.annotation.Nullable; @@ -53,10 +52,10 @@ import androidx.core.view.ViewCompat; import androidx.fragment.app.Fragment; import androidx.fragment.app.FragmentTransaction; import androidx.transition.TransitionInflater; + import com.hippo.android.resource.AttrResources; import com.hippo.beerbelly.BeerBelly; import com.hippo.drawable.RoundSideRectDrawable; -import com.hippo.drawerlayout.DrawerLayout; import com.hippo.ehviewer.AppConfig; import com.hippo.ehviewer.EhApplication; import com.hippo.ehviewer.EhDB; @@ -109,6 +108,7 @@ import com.hippo.yorozuya.IOUtils; import com.hippo.yorozuya.IntIdGenerator; import com.hippo.yorozuya.SimpleHandler; import com.hippo.yorozuya.ViewUtils; + import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; @@ -116,38 +116,29 @@ import java.io.OutputStream; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.List; + import okhttp3.HttpUrl; public class GalleryDetailScene extends BaseScene implements View.OnClickListener, com.hippo.ehviewer.download.DownloadManager.DownloadInfoListener, - View.OnLongClickListener{ - - @IntDef({STATE_INIT, STATE_NORMAL, STATE_REFRESH, STATE_REFRESH_HEADER, STATE_FAILED}) - @Retention(RetentionPolicy.SOURCE) - private @interface State {} + View.OnLongClickListener { + public final static String KEY_ACTION = "action"; + public static final String ACTION_GALLERY_INFO = "action_gallery_info"; + public static final String ACTION_GID_TOKEN = "action_gid_token"; + public static final String KEY_GALLERY_INFO = "gallery_info"; + public static final String KEY_GID = "gid"; + public static final String KEY_TOKEN = "token"; + public static final String KEY_PAGE = "page"; private static final int REQUEST_CODE_COMMENT_GALLERY = 0; - private static final int STATE_INIT = -1; private static final int STATE_NORMAL = 0; private static final int STATE_REFRESH = 1; private static final int STATE_REFRESH_HEADER = 2; private static final int STATE_FAILED = 3; - - public final static String KEY_ACTION = "action"; - public static final String ACTION_GALLERY_INFO = "action_gallery_info"; - public static final String ACTION_GID_TOKEN = "action_gid_token"; - - public static final String KEY_GALLERY_INFO = "gallery_info"; - public static final String KEY_GID = "gid"; - public static final String KEY_TOKEN = "token"; - public static final String KEY_PAGE = "page"; - private static final String KEY_GALLERY_DETAIL = "gallery_detail"; private static final String KEY_REQUEST_ID = "request_id"; - private static final boolean TRANSITION_ANIMATION_DISABLED = true; - /*--------------- View life cycle ---------------*/ @@ -241,31 +232,81 @@ public class GalleryDetailScene extends BaseScene implements View.OnClickListene private ViewTransition mViewTransition2; @Nullable private PopupMenu mPopupMenu; - @WholeLifeCircle private int mDownloadState; - @Nullable private String mAction; @Nullable private GalleryInfo mGalleryInfo; private long mGid; private String mToken; - @Nullable private GalleryDetail mGalleryDetail; private int mRequestId = IntIdGenerator.INVALID_ID; - private Pair[] mTorrentList; - private String mArchiveFormParamOr; private Pair[] mArchiveList; - @State private int mState = STATE_INIT; - private boolean mModifingFavorites; + private static String getRatingText(float rating, Resources resources) { + int resId; + switch (Math.round(rating * 2)) { + case 0: + resId = R.string.rating0; + break; + case 1: + resId = R.string.rating1; + break; + case 2: + resId = R.string.rating2; + break; + case 3: + resId = R.string.rating3; + break; + case 4: + resId = R.string.rating4; + break; + case 5: + resId = R.string.rating5; + break; + case 6: + resId = R.string.rating6; + break; + case 7: + resId = R.string.rating7; + break; + case 8: + resId = R.string.rating8; + break; + case 9: + resId = R.string.rating9; + break; + case 10: + resId = R.string.rating10; + break; + default: + resId = R.string.rating_none; + break; + } + + return resources.getString(resId); + } + + @Nullable + private static String getArtist(GalleryTagGroup[] tagGroups) { + if (null == tagGroups) { + return null; + } + for (GalleryTagGroup tagGroup : tagGroups) { + if ("artist".equals(tagGroup.groupName) && tagGroup.size() > 0) { + return tagGroup.getTagAt(0); + } + } + return null; + } + private void handleArgs(Bundle args) { if (args == null) { return; @@ -407,7 +448,7 @@ public class GalleryDetailScene extends BaseScene implements View.OnClickListene @Nullable @Override public View onCreateView2(LayoutInflater inflater, @Nullable ViewGroup container, - @Nullable Bundle savedInstanceState) { + @Nullable Bundle savedInstanceState) { // Get download state long gid = getGid(); if (gid != -1) { @@ -430,7 +471,7 @@ public class GalleryDetailScene extends BaseScene implements View.OnClickListene AssertUtils.assertNotNull(context); View actionsScrollView = ViewUtils.$$(view, R.id.actions_scroll_view); - setDrawerGestureBlocker(new DrawerLayout.GestureBlocker() { + /*setDrawerGestureBlocker(new DrawerLayout.GestureBlocker() { private void transformPointToViewLocal(int[] point, View child) { ViewParent viewParent = child.getParent(); @@ -456,7 +497,7 @@ public class GalleryDetailScene extends BaseScene implements View.OnClickListene && point[0] > 0 && point[0] < actionsScrollView.getWidth() && point[1] > 0 && point[1] < actionsScrollView.getHeight(); } - }); + });*/ Drawable drawable = DrawableManager.getVectorDrawable(context, R.drawable.big_sad_pandroid); drawable.setBounds(0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight()); @@ -579,8 +620,6 @@ public class GalleryDetailScene extends BaseScene implements View.OnClickListene AssertUtils.assertNotNull(context); EhApplication.getDownloadManager(context).removeDownloadInfoListener(this); - setDrawerGestureBlocker(null); - mTip = null; mViewTransition = null; @@ -803,7 +842,7 @@ public class GalleryDetailScene extends BaseScene implements View.OnClickListene if (gd == null) { return; } - if (mHeart == null || mHeartOutline == null ) { + if (mHeart == null || mHeartOutline == null) { return; } @@ -992,38 +1031,6 @@ public class GalleryDetailScene extends BaseScene implements View.OnClickListene } } - private static String getRatingText(float rating, Resources resources) { - int resId; - switch (Math.round(rating * 2)) { - case 0: - resId = R.string.rating0; break; - case 1: - resId = R.string.rating1; break; - case 2: - resId = R.string.rating2; break; - case 3: - resId = R.string.rating3; break; - case 4: - resId = R.string.rating4; break; - case 5: - resId = R.string.rating5; break; - case 6: - resId = R.string.rating6; break; - case 7: - resId = R.string.rating7; break; - case 8: - resId = R.string.rating8; break; - case 9: - resId = R.string.rating9; break; - case 10: - resId = R.string.rating10; break; - default: - resId = R.string.rating_none; break; - } - - return resources.getString(resId); - } - private String getAllRatingText(float rating, int ratingCount) { Resources resources = getResources2(); AssertUtils.assertNotNull(resources); @@ -1075,19 +1082,6 @@ public class GalleryDetailScene extends BaseScene implements View.OnClickListene }); } - @Nullable - private static String getArtist(GalleryTagGroup[] tagGroups) { - if (null == tagGroups) { - return null; - } - for (GalleryTagGroup tagGroup: tagGroups) { - if ("artist".equals(tagGroup.groupName) && tagGroup.size() > 0) { - return tagGroup.getTagAt(0); - } - } - return null; - } - private void showSimilarGalleryList() { GalleryDetail gd = mGalleryDetail; if (null == gd) { @@ -1191,10 +1185,10 @@ public class GalleryDetailScene extends BaseScene implements View.OnClickListene CommonOperations.startDownload(activity, galleryInfo, false); } else { new AlertDialog.Builder(context) - .setTitle(R.string.download_remove_dialog_title) - .setMessage(getString(R.string.download_remove_dialog_message, galleryInfo.title)) - .setPositiveButton(android.R.string.ok, (dialog1, which1) -> EhApplication.getDownloadManager(context).deleteDownload(galleryInfo.gid)) - .show(); + .setTitle(R.string.download_remove_dialog_title) + .setMessage(getString(R.string.download_remove_dialog_message, galleryInfo.title)) + .setPositiveButton(android.R.string.ok, (dialog1, which1) -> EhApplication.getDownloadManager(context).deleteDownload(galleryInfo.gid)) + .show(); } } } else if (mRead == v) { @@ -1445,7 +1439,7 @@ public class GalleryDetailScene extends BaseScene implements View.OnClickListene protected void onSceneResult(int requestCode, int resultCode, Bundle data) { switch (requestCode) { case REQUEST_CODE_COMMENT_GALLERY: - if (resultCode != RESULT_OK || data == null){ + if (resultCode != RESULT_OK || data == null) { break; } GalleryCommentList comments = data.getParcelable(GalleryCommentsScene.KEY_COMMENT_LIST); @@ -1533,41 +1527,11 @@ public class GalleryDetailScene extends BaseScene implements View.OnClickListene } @Override - public void onRenameLabel(String from, String to) {} + public void onRenameLabel(String from, String to) { + } @Override - public void onUpdateLabels() {} - - private static class ExitTransaction implements TransitionHelper { - - private final View mThumb; - - public ExitTransaction(View thumb) { - mThumb = thumb; - } - - @Override - public boolean onTransition(Context context, - FragmentTransaction transaction, Fragment exit, Fragment enter) { - if (!(enter instanceof GalleryListScene) && !(enter instanceof DownloadsScene) && - !(enter instanceof FavoritesScene) && !(enter instanceof HistoryScene)) { - return false; - } - - String transitionName = ViewCompat.getTransitionName(mThumb); - if (transitionName != null) { - exit.setSharedElementReturnTransition( - TransitionInflater.from(context).inflateTransition(R.transition.trans_move)); - exit.setExitTransition( - TransitionInflater.from(context).inflateTransition(R.transition.trans_fade)); - enter.setSharedElementEnterTransition( - TransitionInflater.from(context).inflateTransition(R.transition.trans_move)); - enter.setEnterTransition( - TransitionInflater.from(context).inflateTransition(R.transition.trans_fade)); - transaction.addSharedElement(mThumb, transitionName); - } - return true; - } + public void onUpdateLabels() { } private void onGetGalleryDetailSuccess(GalleryDetail result) { @@ -1616,6 +1580,128 @@ public class GalleryDetailScene extends BaseScene implements View.OnClickListene mModifingFavorites = false; } + @IntDef({STATE_INIT, STATE_NORMAL, STATE_REFRESH, STATE_REFRESH_HEADER, STATE_FAILED}) + @Retention(RetentionPolicy.SOURCE) + private @interface State { + } + + private static class ExitTransaction implements TransitionHelper { + + private final View mThumb; + + public ExitTransaction(View thumb) { + mThumb = thumb; + } + + @Override + public boolean onTransition(Context context, + FragmentTransaction transaction, Fragment exit, Fragment enter) { + if (!(enter instanceof GalleryListScene) && !(enter instanceof DownloadsScene) && + !(enter instanceof FavoritesScene) && !(enter instanceof HistoryScene)) { + return false; + } + + String transitionName = ViewCompat.getTransitionName(mThumb); + if (transitionName != null) { + exit.setSharedElementReturnTransition( + TransitionInflater.from(context).inflateTransition(R.transition.trans_move)); + exit.setExitTransition( + TransitionInflater.from(context).inflateTransition(R.transition.trans_fade)); + enter.setSharedElementEnterTransition( + TransitionInflater.from(context).inflateTransition(R.transition.trans_move)); + enter.setEnterTransition( + TransitionInflater.from(context).inflateTransition(R.transition.trans_fade)); + transaction.addSharedElement(mThumb, transitionName); + } + return true; + } + } + + private static class GetGalleryDetailListener extends EhCallback { + + public GetGalleryDetailListener(Context context, int stageId, String sceneTag) { + super(context, stageId, sceneTag); + } + + @Override + public void onSuccess(GalleryDetail result) { + getApplication().removeGlobalStuff(this); + + // Put gallery detail to cache + EhApplication.getGalleryDetailCache(getApplication()).put(result.gid, result); + + // Add history + EhDB.putHistoryInfo(result); + + // Notify success + GalleryDetailScene scene = getScene(); + if (scene != null) { + scene.onGetGalleryDetailSuccess(result); + } + } + + @Override + public void onFailure(Exception e) { + getApplication().removeGlobalStuff(this); + GalleryDetailScene scene = getScene(); + if (scene != null) { + scene.onGetGalleryDetailFailure(e); + } + } + + @Override + public void onCancel() { + getApplication().removeGlobalStuff(this); + } + + @Override + public boolean isInstance(SceneFragment scene) { + return scene instanceof GalleryDetailScene; + } + } + + private static class RateGalleryListener extends EhCallback { + + private final long mGid; + + public RateGalleryListener(Context context, int stageId, String sceneTag, long gid) { + super(context, stageId, sceneTag); + mGid = gid; + } + + @Override + public void onSuccess(RateGalleryParser.Result result) { + showTip(R.string.rate_successfully, LENGTH_SHORT); + + GalleryDetailScene scene = getScene(); + if (scene != null) { + scene.onRateGallerySuccess(result); + } else { + // Update rating in cache + GalleryDetail gd = EhApplication.getGalleryDetailCache(getApplication()).get(mGid); + if (gd != null) { + gd.rating = result.rating; + gd.ratingCount = result.ratingCount; + } + } + } + + @Override + public void onFailure(Exception e) { + e.printStackTrace(); + showTip(R.string.rate_failed, LENGTH_LONG); + } + + @Override + public void onCancel() { + } + + @Override + public boolean isInstance(SceneFragment scene) { + return scene instanceof GalleryDetailScene; + } + } + private class ModifyFavoritesListener extends EhCallback { private final boolean mAddOrRemove; @@ -1683,7 +1769,8 @@ public class GalleryDetailScene extends BaseScene implements View.OnClickListene } @Override - public void onCancel() {} + public void onCancel() { + } @Override public boolean isInstance(SceneFragment scene) { @@ -1803,7 +1890,9 @@ public class GalleryDetailScene extends BaseScene implements View.OnClickListene } @Override - public void onCancel() { mRequest = null; } + public void onCancel() { + mRequest = null; + } } private class TorrentListDialogHelper implements AdapterView.OnItemClickListener, @@ -1932,49 +2021,6 @@ public class GalleryDetailScene extends BaseScene implements View.OnClickListene } } - private static class GetGalleryDetailListener extends EhCallback { - - public GetGalleryDetailListener(Context context, int stageId, String sceneTag) { - super(context, stageId, sceneTag); - } - - @Override - public void onSuccess(GalleryDetail result) { - getApplication().removeGlobalStuff(this); - - // Put gallery detail to cache - EhApplication.getGalleryDetailCache(getApplication()).put(result.gid, result); - - // Add history - EhDB.putHistoryInfo(result); - - // Notify success - GalleryDetailScene scene = getScene(); - if (scene != null) { - scene.onGetGalleryDetailSuccess(result); - } - } - - @Override - public void onFailure(Exception e) { - getApplication().removeGlobalStuff(this); - GalleryDetailScene scene = getScene(); - if (scene != null) { - scene.onGetGalleryDetailFailure(e); - } - } - - @Override - public void onCancel() { - getApplication().removeGlobalStuff(this); - } - - @Override - public boolean isInstance(SceneFragment scene) { - return scene instanceof GalleryDetailScene; - } - } - private class RateDialogHelper implements GalleryRatingBar.OnUserRateListener, DialogInterface.OnClickListener { @@ -2016,46 +2062,4 @@ public class GalleryDetailScene extends BaseScene implements View.OnClickListene EhApplication.getEhClient(context).execute(request); } } - - private static class RateGalleryListener extends EhCallback { - - private final long mGid; - - public RateGalleryListener(Context context, int stageId, String sceneTag, long gid) { - super(context, stageId, sceneTag); - mGid = gid; - } - - @Override - public void onSuccess(RateGalleryParser.Result result) { - showTip(R.string.rate_successfully, LENGTH_SHORT); - - GalleryDetailScene scene = getScene(); - if (scene != null) { - scene.onRateGallerySuccess(result); - } else { - // Update rating in cache - GalleryDetail gd = EhApplication.getGalleryDetailCache(getApplication()).get(mGid); - if (gd != null) { - gd.rating = result.rating; - gd.ratingCount = result.ratingCount; - } - } - } - - @Override - public void onFailure(Exception e) { - e.printStackTrace(); - showTip(R.string.rate_failed, LENGTH_LONG); - } - - @Override - public void onCancel() { - } - - @Override - public boolean isInstance(SceneFragment scene) { - return scene instanceof GalleryDetailScene; - } - } } diff --git a/app/src/main/java/com/hippo/ehviewer/ui/scene/GalleryHolder.java b/app/src/main/java/com/hippo/ehviewer/ui/scene/GalleryHolder.java index 925f77eb4..faacca7e6 100644 --- a/app/src/main/java/com/hippo/ehviewer/ui/scene/GalleryHolder.java +++ b/app/src/main/java/com/hippo/ehviewer/ui/scene/GalleryHolder.java @@ -19,7 +19,9 @@ package com.hippo.ehviewer.ui.scene; import android.view.View; import android.widget.ImageView; import android.widget.TextView; + import androidx.recyclerview.widget.RecyclerView; + import com.hippo.ehviewer.R; import com.hippo.ehviewer.widget.SimpleRatingView; import com.hippo.widget.LoadImageView; diff --git a/app/src/main/java/com/hippo/ehviewer/ui/scene/GalleryInfoScene.java b/app/src/main/java/com/hippo/ehviewer/ui/scene/GalleryInfoScene.java index 060a0d371..603d86458 100644 --- a/app/src/main/java/com/hippo/ehviewer/ui/scene/GalleryInfoScene.java +++ b/app/src/main/java/com/hippo/ehviewer/ui/scene/GalleryInfoScene.java @@ -27,10 +27,12 @@ import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.TextView; + import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; + import com.hippo.android.resource.AttrResources; import com.hippo.easyrecyclerview.EasyRecyclerView; import com.hippo.easyrecyclerview.LinearDividerItemDecoration; @@ -44,6 +46,7 @@ import com.hippo.ripple.Ripple; import com.hippo.yorozuya.AssertUtils; import com.hippo.yorozuya.LayoutUtils; import com.hippo.yorozuya.ViewUtils; + import java.util.ArrayList; public final class GalleryInfoScene extends ToolbarScene implements EasyRecyclerView.OnItemClickListener { @@ -159,7 +162,7 @@ public final class GalleryInfoScene extends ToolbarScene implements EasyRecycler @Nullable @Override public View onCreateView3(LayoutInflater inflater, - @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { + @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { View view = inflater.inflate(R.layout.scene_gallery_info, container, false); Context context = getContext2(); diff --git a/app/src/main/java/com/hippo/ehviewer/ui/scene/GalleryListScene.java b/app/src/main/java/com/hippo/ehviewer/ui/scene/GalleryListScene.java index a3f37c45a..385bc30b1 100644 --- a/app/src/main/java/com/hippo/ehviewer/ui/scene/GalleryListScene.java +++ b/app/src/main/java/com/hippo/ehviewer/ui/scene/GalleryListScene.java @@ -43,12 +43,15 @@ import android.widget.AdapterView; import android.widget.ArrayAdapter; import android.widget.ListView; import android.widget.TextView; + import androidx.annotation.IntDef; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.appcompat.app.AlertDialog; import androidx.appcompat.widget.Toolbar; +import androidx.drawerlayout.widget.DrawerLayout; import androidx.recyclerview.widget.RecyclerView; + import com.github.amlcurran.showcaseview.ShowcaseView; import com.github.amlcurran.showcaseview.SimpleShowcaseEventListener; import com.github.amlcurran.showcaseview.targets.PointTarget; @@ -58,7 +61,6 @@ import com.hippo.app.CheckBoxDialogBuilder; import com.hippo.app.EditTextDialogBuilder; import com.hippo.drawable.AddDeleteDrawable; import com.hippo.drawable.DrawerArrowDrawable; -import com.hippo.drawerlayout.DrawerLayout; import com.hippo.easyrecyclerview.EasyRecyclerView; import com.hippo.easyrecyclerview.FastScroller; import com.hippo.ehviewer.EhApplication; @@ -102,6 +104,7 @@ import com.hippo.yorozuya.MathUtils; import com.hippo.yorozuya.SimpleAnimatorListener; import com.hippo.yorozuya.StringUtils; import com.hippo.yorozuya.ViewUtils; + import java.io.File; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -114,31 +117,21 @@ public final class GalleryListScene extends BaseScene SearchLayout.Helper, SearchBarMover.Helper, View.OnClickListener, FabLayout.OnClickFabListener, FabLayout.OnExpandListener { - @IntDef({STATE_NORMAL, STATE_SIMPLE_SEARCH, STATE_SEARCH, STATE_SEARCH_SHOW_LIST}) - @Retention(RetentionPolicy.SOURCE) - private @interface State {} - - private static final int BACK_PRESSED_INTERVAL = 2000; - public final static int REQUEST_CODE_SELECT_IMAGE = 0; - public final static String KEY_ACTION = "action"; public final static String ACTION_HOMEPAGE = "action_homepage"; public final static String ACTION_SUBSCRIPTION = "action_subscription"; public final static String ACTION_WHATS_HOT = "action_whats_hot"; public final static String ACTION_LIST_URL_BUILDER = "action_list_url_builder"; - public final static String KEY_LIST_URL_BUILDER = "list_url_builder"; public final static String KEY_HAS_FIRST_REFRESH = "has_first_refresh"; public final static String KEY_STATE = "state"; - + private static final int BACK_PRESSED_INTERVAL = 2000; private final static int STATE_NORMAL = 0; private final static int STATE_SIMPLE_SEARCH = 1; private final static int STATE_SEARCH = 2; private final static int STATE_SEARCH_SHOW_LIST = 3; - private static final long ANIMATE_TIME = 300L; - /*--------------- Whole life cycle ---------------*/ @@ -146,7 +139,6 @@ public final class GalleryListScene extends BaseScene private EhClient mClient; @Nullable private ListUrlBuilder mUrlBuilder; - /*--------------- View life cycle ---------------*/ @@ -159,8 +151,26 @@ public final class GalleryListScene extends BaseScene @Nullable private View mSearchFab; @Nullable + private final Animator.AnimatorListener mSearchFabAnimatorListener = new SimpleAnimatorListener() { + @Override + public void onAnimationEnd(Animator animation) { + if (null != mSearchFab) { + mSearchFab.setVisibility(View.INVISIBLE); + } + } + }; + @Nullable private FabLayout mFabLayout; @Nullable + private final Animator.AnimatorListener mActionFabAnimatorListener = new SimpleAnimatorListener() { + @Override + public void onAnimationEnd(Animator animation) { + if (null != mFabLayout) { + ((View) mFabLayout.getPrimaryFab()).setVisibility(View.INVISIBLE); + } + } + }; + @Nullable private ViewTransition mViewTransition; @Nullable private GalleryListAdapter mAdapter; @@ -174,34 +184,15 @@ public final class GalleryListScene extends BaseScene private SearchBarMover mSearchBarMover; @Nullable private AddDeleteDrawable mActionFabDrawable; - - @Nullable - private final Animator.AnimatorListener mActionFabAnimatorListener = new SimpleAnimatorListener() { - @Override - public void onAnimationEnd(Animator animation) { - if (null != mFabLayout) { - ((View) mFabLayout.getPrimaryFab()).setVisibility(View.INVISIBLE); - } - } - }; - - @Nullable - private final Animator.AnimatorListener mSearchFabAnimatorListener = new SimpleAnimatorListener() { - @Override - public void onAnimationEnd(Animator animation) { - if (null != mSearchFab) { - mSearchFab.setVisibility(View.INVISIBLE); - } - } - }; - private int mHideActionFabSlop; private boolean mShowActionFab = true; - + @State + private int mState = STATE_NORMAL; @Nullable private final RecyclerView.OnScrollListener mOnScrollListener = new RecyclerView.OnScrollListener() { @Override - public void onScrollStateChanged(RecyclerView recyclerView, int newState) {} + public void onScrollStateChanged(RecyclerView recyclerView, int newState) { + } @Override public void onScrolled(RecyclerView recyclerView, int dx, int dy) { @@ -212,24 +203,60 @@ public final class GalleryListScene extends BaseScene } } }; - - @State - private int mState = STATE_NORMAL; - // Double click back exit private long mPressBackTime = 0; - private boolean mHasFirstRefresh = false; - private int mNavCheckedId = 0; - private ShowcaseView mShowcaseView; - private DownloadManager mDownloadManager; private DownloadManager.DownloadInfoListener mDownloadInfoListener; private FavouriteStatusRouter mFavouriteStatusRouter; private FavouriteStatusRouter.Listener mFavouriteStatusRouterListener; + @Nullable + private static String getSuitableTitleForUrlBuilder( + Resources resources, ListUrlBuilder urlBuilder, boolean appName) { + String keyword = urlBuilder.getKeyword(); + int category = urlBuilder.getCategory(); + + if (ListUrlBuilder.MODE_NORMAL == urlBuilder.getMode() && + EhUtils.NONE == category && + TextUtils.isEmpty(keyword) && + urlBuilder.getAdvanceSearch() == -1 && + urlBuilder.getMinRating() == -1 && + urlBuilder.getPageFrom() == -1 && + urlBuilder.getPageTo() == -1) { + return resources.getString(appName ? R.string.app_name : R.string.homepage); + } else if (ListUrlBuilder.MODE_SUBSCRIPTION == urlBuilder.getMode() && + EhUtils.NONE == category && + TextUtils.isEmpty(keyword) && + urlBuilder.getAdvanceSearch() == -1 && + urlBuilder.getMinRating() == -1 && + urlBuilder.getPageFrom() == -1 && + urlBuilder.getPageTo() == -1) { + return resources.getString(R.string.subscription); + } else if (ListUrlBuilder.MODE_WHATS_HOT == urlBuilder.getMode()) { + return resources.getString(R.string.whats_hot); + } else if (!TextUtils.isEmpty(keyword)) { + return keyword; + } else if (MathUtils.hammingWeight(category) == 1) { + return EhUtils.getCategory(category); + } else { + return null; + } + } + + public static void startScene(SceneFragment scene, ListUrlBuilder lub) { + scene.startScene(getStartAnnouncer(lub)); + } + + public static Announcer getStartAnnouncer(ListUrlBuilder lub) { + Bundle args = new Bundle(); + args.putString(KEY_ACTION, ACTION_LIST_URL_BUILDER); + args.putParcelable(KEY_LIST_URL_BUILDER, lub); + return new Announcer(GalleryListScene.class).setArgs(args); + } + @Override public int getNavCheckedItem() { return mNavCheckedId; @@ -287,32 +314,43 @@ public final class GalleryListScene extends BaseScene mAdapter.notifyDataSetChanged(); } } + @Override - public void onUpdate(@NonNull DownloadInfo info, @NonNull List list) { } + public void onUpdate(@NonNull DownloadInfo info, @NonNull List list) { + } + @Override - public void onUpdateAll() { } + public void onUpdateAll() { + } + @Override public void onReload() { if (mAdapter != null) { mAdapter.notifyDataSetChanged(); } } + @Override public void onChange() { if (mAdapter != null) { mAdapter.notifyDataSetChanged(); } } + @Override - public void onRenameLabel(String from, String to) { } + public void onRenameLabel(String from, String to) { + } + @Override public void onRemove(@NonNull DownloadInfo info, @NonNull List list, int position) { if (mAdapter != null) { mAdapter.notifyDataSetChanged(); } } + @Override - public void onUpdateLabels() { } + public void onUpdateLabels() { + } }; mDownloadManager.addDownloadInfoListener(mDownloadInfoListener); @@ -396,111 +434,6 @@ public final class GalleryListScene extends BaseScene }); } - private abstract class UrlSuggestion extends SearchBar.Suggestion { - @Override - public CharSequence getText(float textSize) { - Drawable bookImage = DrawableManager.getVectorDrawable(getContext2(), R.drawable.v_book_open_x24); - SpannableStringBuilder ssb = new SpannableStringBuilder(" "); - ssb.append(getResources2().getString(R.string.gallery_list_search_bar_open_gallery)); - int imageSize = (int) (textSize * 1.25); - if (bookImage != null) { - bookImage.setBounds(0, 0, imageSize, imageSize); - ssb.setSpan(new ImageSpan(bookImage), 1, 2, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); - } - return ssb; - } - - @Override - public void onClick() { - startScene(createAnnouncer()); - - if (mState == STATE_SIMPLE_SEARCH) { - setState(STATE_NORMAL); - } else if (mState == STATE_SEARCH_SHOW_LIST) { - setState(STATE_SEARCH); - } - } - - public abstract Announcer createAnnouncer(); - - @Override - public void onLongClick() { } - } - - private class GalleryDetailUrlSuggestion extends UrlSuggestion { - private long mGid; - private String mToken; - - private GalleryDetailUrlSuggestion(long gid, String token) { - mGid = gid; - mToken = token; - } - - @Override - public Announcer createAnnouncer() { - Bundle args = new Bundle(); - args.putString(GalleryDetailScene.KEY_ACTION, GalleryDetailScene.ACTION_GID_TOKEN); - args.putLong(GalleryDetailScene.KEY_GID, mGid); - args.putString(GalleryDetailScene.KEY_TOKEN, mToken); - return new Announcer(GalleryDetailScene.class).setArgs(args); - } - } - - private class GalleryPageUrlSuggestion extends UrlSuggestion { - private long mGid; - private String mPToken; - private int mPage; - - private GalleryPageUrlSuggestion(long gid, String pToken, int page) { - mGid = gid; - mPToken = pToken; - mPage = page; - } - - @Override - public Announcer createAnnouncer() { - Bundle args = new Bundle(); - args.putString(ProgressScene.KEY_ACTION, ProgressScene.ACTION_GALLERY_TOKEN); - args.putLong(ProgressScene.KEY_GID, mGid); - args.putString(ProgressScene.KEY_PTOKEN, mPToken); - args.putInt(ProgressScene.KEY_PAGE, mPage); - return new Announcer(ProgressScene.class).setArgs(args); - } - } - - @Nullable - private static String getSuitableTitleForUrlBuilder( - Resources resources, ListUrlBuilder urlBuilder, boolean appName) { - String keyword = urlBuilder.getKeyword(); - int category = urlBuilder.getCategory(); - - if (ListUrlBuilder.MODE_NORMAL == urlBuilder.getMode() && - EhUtils.NONE == category && - TextUtils.isEmpty(keyword) && - urlBuilder.getAdvanceSearch() == -1 && - urlBuilder.getMinRating() == -1 && - urlBuilder.getPageFrom() == -1 && - urlBuilder.getPageTo() == -1) { - return resources.getString(appName ? R.string.app_name : R.string.homepage); - } else if (ListUrlBuilder.MODE_SUBSCRIPTION == urlBuilder.getMode() && - EhUtils.NONE == category && - TextUtils.isEmpty(keyword) && - urlBuilder.getAdvanceSearch() == -1 && - urlBuilder.getMinRating() == -1 && - urlBuilder.getPageFrom() == -1 && - urlBuilder.getPageTo() == -1) { - return resources.getString(R.string.subscription); - } else if (ListUrlBuilder.MODE_WHATS_HOT == urlBuilder.getMode()) { - return resources.getString(R.string.whats_hot); - } else if (!TextUtils.isEmpty(keyword)) { - return keyword; - } else if (MathUtils.hammingWeight(category) == 1) { - return EhUtils.getCategory(category); - } else { - return null; - } - } - private String wrapTagKeyword(String keyword) { keyword = keyword.trim(); @@ -576,7 +509,7 @@ public final class GalleryListScene extends BaseScene @Nullable @Override public View onCreateView2(LayoutInflater inflater, @Nullable ViewGroup container, - @Nullable Bundle savedInstanceState) { + @Nullable Bundle savedInstanceState) { View view = inflater.inflate(R.layout.scene_gallery_list, container, false); Context context = getContext2(); @@ -732,7 +665,7 @@ public final class GalleryListScene extends BaseScene } private void showQuickSearchTipDialog(final List list, - final ArrayAdapter adapter, final ListView listView, final TextView tip) { + final ArrayAdapter adapter, final ListView listView, final TextView tip) { Context context = getContext2(); if (null == context) { return; @@ -752,7 +685,7 @@ public final class GalleryListScene extends BaseScene } private void showAddQuickSearchDialog(final List list, - final ArrayAdapter adapter, final ListView listView, final TextView tip) { + final ArrayAdapter adapter, final ListView listView, final TextView tip) { Context context = getContext2(); final ListUrlBuilder urlBuilder = mUrlBuilder; if (null == context || null == urlBuilder) { @@ -766,7 +699,7 @@ public final class GalleryListScene extends BaseScene } // Check duplicate - for (QuickSearch q: list) { + for (QuickSearch q : list) { if (urlBuilder.equalsQuickSearch(q)) { showTip(getString(R.string.duplicate_quick_search, q.name), LENGTH_LONG); return; @@ -790,7 +723,7 @@ public final class GalleryListScene extends BaseScene } // Check name duplicate - for (QuickSearch q: list) { + for (QuickSearch q : list) { if (text.equals(q.name)) { builder.setError(getString(R.string.duplicate_name)); return; @@ -818,7 +751,7 @@ public final class GalleryListScene extends BaseScene @Override public View onCreateDrawerView(LayoutInflater inflater, @Nullable ViewGroup container, - @Nullable Bundle savedInstanceState) { + @Nullable Bundle savedInstanceState) { View view = inflater.inflate(R.layout.drawer_list, container, false); Toolbar toolbar = (Toolbar) ViewUtils.$$(view, R.id.toolbar); final TextView tip = (TextView) ViewUtils.$$(view, R.id.tip); @@ -996,7 +929,7 @@ public final class GalleryListScene extends BaseScene int goTo; try { goTo = Integer.parseInt(text) - 1; - } catch (NumberFormatException e){ + } catch (NumberFormatException e) { builder.setError(getString(R.string.error_invalid_number)); return; } @@ -1065,16 +998,16 @@ public final class GalleryListScene extends BaseScene boolean downloaded = mDownloadManager.getDownloadState(gi.gid) != DownloadInfo.STATE_INVALID; boolean favourited = gi.favoriteSlot != -2; - CharSequence[] items = new CharSequence[] { - context.getString(R.string.read), - context.getString(downloaded ? R.string.delete_downloads : R.string.download), - context.getString(favourited ? R.string.remove_from_favourites : R.string.add_to_favourites), + CharSequence[] items = new CharSequence[]{ + context.getString(R.string.read), + context.getString(downloaded ? R.string.delete_downloads : R.string.download), + context.getString(favourited ? R.string.remove_from_favourites : R.string.add_to_favourites), }; - int[] icons = new int[] { - R.drawable.v_book_open_x24, - downloaded ? R.drawable.v_delete_x24 : R.drawable.v_download_x24, - favourited ? R.drawable.v_heart_broken_x24 : R.drawable.v_heart_x24, + int[] icons = new int[]{ + R.drawable.v_book_open_x24, + downloaded ? R.drawable.v_delete_x24 : R.drawable.v_download_x24, + favourited ? R.drawable.v_heart_broken_x24 : R.drawable.v_heart_x24, }; new AlertDialog.Builder(context) @@ -1090,10 +1023,10 @@ public final class GalleryListScene extends BaseScene case 1: // Download if (downloaded) { new AlertDialog.Builder(context) - .setTitle(R.string.download_remove_dialog_title) - .setMessage(getString(R.string.download_remove_dialog_message, gi.title)) - .setPositiveButton(android.R.string.ok, (dialog1, which1) -> mDownloadManager.deleteDownload(gi.gid)) - .show(); + .setTitle(R.string.download_remove_dialog_title) + .setMessage(getString(R.string.download_remove_dialog_message, gi.title)) + .setPositiveButton(android.R.string.ok, (dialog1, which1) -> mDownloadManager.deleteDownload(gi.gid)) + .show(); } else { CommonOperations.startDownload(activity, gi, false); } @@ -1452,17 +1385,6 @@ public final class GalleryListScene extends BaseScene return mState == STATE_SIMPLE_SEARCH || mState == STATE_SEARCH_SHOW_LIST; } - public static void startScene(SceneFragment scene, ListUrlBuilder lub) { - scene.startScene(getStartAnnouncer(lub)); - } - - public static Announcer getStartAnnouncer(ListUrlBuilder lub) { - Bundle args = new Bundle(); - args.putString(KEY_ACTION, ACTION_LIST_URL_BUILDER); - args.putParcelable(KEY_LIST_URL_BUILDER, lub); - return new Announcer(GalleryListScene.class).setArgs(args); - } - @Override public void onActivityResult(int requestCode, int resultCode, Intent data) { if (REQUEST_CODE_SELECT_IMAGE == requestCode) { @@ -1474,10 +1396,193 @@ public final class GalleryListScene extends BaseScene } } + private void onGetGalleryListSuccess(GalleryListParser.Result result, int taskId) { + if (mHelper != null && mSearchBarMover != null && + mHelper.isCurrentTask(taskId)) { + String emptyString = getResources2().getString(mUrlBuilder.getMode() == ListUrlBuilder.MODE_SUBSCRIPTION && result.noWatchedTags + ? R.string.gallery_list_empty_hit_subscription + : R.string.gallery_list_empty_hit); + mHelper.setEmptyString(emptyString); + mHelper.onGetPageData(taskId, result.pages, result.nextPage, result.galleryInfoList); + } + } + + private void onGetGalleryListFailure(Exception e, int taskId) { + if (mHelper != null && mSearchBarMover != null && + mHelper.isCurrentTask(taskId)) { + mHelper.onGetException(taskId, e); + } + } + + @IntDef({STATE_NORMAL, STATE_SIMPLE_SEARCH, STATE_SEARCH, STATE_SEARCH_SHOW_LIST}) + @Retention(RetentionPolicy.SOURCE) + private @interface State { + } + + private static class GetGalleryListListener extends EhCallback { + + private final int mTaskId; + + public GetGalleryListListener(Context context, int stageId, String sceneTag, int taskId) { + super(context, stageId, sceneTag); + mTaskId = taskId; + } + + @Override + public void onSuccess(GalleryListParser.Result result) { + GalleryListScene scene = getScene(); + if (scene != null) { + scene.onGetGalleryListSuccess(result, mTaskId); + } + } + + @Override + public void onFailure(Exception e) { + GalleryListScene scene = getScene(); + if (scene != null) { + scene.onGetGalleryListFailure(e, mTaskId); + } + } + + @Override + public void onCancel() { + } + + @Override + public boolean isInstance(SceneFragment scene) { + return scene instanceof GalleryListScene; + } + } + + private static class AddToFavoriteListener extends EhCallback { + + public AddToFavoriteListener(Context context, int stageId, String sceneTag) { + super(context, stageId, sceneTag); + } + + @Override + public void onSuccess(Void result) { + showTip(R.string.add_to_favorite_success, LENGTH_SHORT); + } + + @Override + public void onFailure(Exception e) { + showTip(R.string.add_to_favorite_failure, LENGTH_LONG); + } + + @Override + public void onCancel() { + } + + @Override + public boolean isInstance(SceneFragment scene) { + return scene instanceof GalleryListScene; + } + } + + private static class RemoveFromFavoriteListener extends EhCallback { + + public RemoveFromFavoriteListener(Context context, int stageId, String sceneTag) { + super(context, stageId, sceneTag); + } + + @Override + public void onSuccess(Void result) { + showTip(R.string.remove_from_favorite_success, LENGTH_SHORT); + } + + @Override + public void onFailure(Exception e) { + showTip(R.string.remove_from_favorite_failure, LENGTH_LONG); + } + + @Override + public void onCancel() { + } + + @Override + public boolean isInstance(SceneFragment scene) { + return scene instanceof GalleryListScene; + } + } + + private abstract class UrlSuggestion extends SearchBar.Suggestion { + @Override + public CharSequence getText(float textSize) { + Drawable bookImage = DrawableManager.getVectorDrawable(getContext2(), R.drawable.v_book_open_x24); + SpannableStringBuilder ssb = new SpannableStringBuilder(" "); + ssb.append(getResources2().getString(R.string.gallery_list_search_bar_open_gallery)); + int imageSize = (int) (textSize * 1.25); + if (bookImage != null) { + bookImage.setBounds(0, 0, imageSize, imageSize); + ssb.setSpan(new ImageSpan(bookImage), 1, 2, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); + } + return ssb; + } + + @Override + public void onClick() { + startScene(createAnnouncer()); + + if (mState == STATE_SIMPLE_SEARCH) { + setState(STATE_NORMAL); + } else if (mState == STATE_SEARCH_SHOW_LIST) { + setState(STATE_SEARCH); + } + } + + public abstract Announcer createAnnouncer(); + + @Override + public void onLongClick() { + } + } + + private class GalleryDetailUrlSuggestion extends UrlSuggestion { + private long mGid; + private String mToken; + + private GalleryDetailUrlSuggestion(long gid, String token) { + mGid = gid; + mToken = token; + } + + @Override + public Announcer createAnnouncer() { + Bundle args = new Bundle(); + args.putString(GalleryDetailScene.KEY_ACTION, GalleryDetailScene.ACTION_GID_TOKEN); + args.putLong(GalleryDetailScene.KEY_GID, mGid); + args.putString(GalleryDetailScene.KEY_TOKEN, mToken); + return new Announcer(GalleryDetailScene.class).setArgs(args); + } + } + + private class GalleryPageUrlSuggestion extends UrlSuggestion { + private long mGid; + private String mPToken; + private int mPage; + + private GalleryPageUrlSuggestion(long gid, String pToken, int page) { + mGid = gid; + mPToken = pToken; + mPage = page; + } + + @Override + public Announcer createAnnouncer() { + Bundle args = new Bundle(); + args.putString(ProgressScene.KEY_ACTION, ProgressScene.ACTION_GALLERY_TOKEN); + args.putLong(ProgressScene.KEY_GID, mGid); + args.putString(ProgressScene.KEY_PTOKEN, mPToken); + args.putInt(ProgressScene.KEY_PAGE, mPage); + return new Announcer(ProgressScene.class).setArgs(args); + } + } + private class GalleryListAdapter extends GalleryAdapter { public GalleryListAdapter(@NonNull LayoutInflater inflater, - @NonNull Resources resources, @NonNull RecyclerView recyclerView, int type) { + @NonNull Resources resources, @NonNull RecyclerView recyclerView, int type) { super(inflater, resources, recyclerView, type, true); } @@ -1572,106 +1677,4 @@ public final class GalleryListScene extends BaseScene } } } - - private void onGetGalleryListSuccess(GalleryListParser.Result result, int taskId) { - if (mHelper != null && mSearchBarMover != null && - mHelper.isCurrentTask(taskId)) { - String emptyString = getResources2().getString(mUrlBuilder.getMode() == ListUrlBuilder.MODE_SUBSCRIPTION && result.noWatchedTags - ? R.string.gallery_list_empty_hit_subscription - : R.string.gallery_list_empty_hit); - mHelper.setEmptyString(emptyString); - mHelper.onGetPageData(taskId, result.pages, result.nextPage, result.galleryInfoList); - } - } - - private void onGetGalleryListFailure(Exception e, int taskId) { - if (mHelper != null && mSearchBarMover != null && - mHelper.isCurrentTask(taskId)) { - mHelper.onGetException(taskId, e); - } - } - - private static class GetGalleryListListener extends EhCallback { - - private final int mTaskId; - - public GetGalleryListListener(Context context, int stageId, String sceneTag, int taskId) { - super(context, stageId, sceneTag); - mTaskId = taskId; - } - - @Override - public void onSuccess(GalleryListParser.Result result) { - GalleryListScene scene = getScene(); - if (scene != null) { - scene.onGetGalleryListSuccess(result, mTaskId); - } - } - - @Override - public void onFailure(Exception e) { - GalleryListScene scene = getScene(); - if (scene != null) { - scene.onGetGalleryListFailure(e, mTaskId); - } - } - - @Override - public void onCancel() {} - - @Override - public boolean isInstance(SceneFragment scene) { - return scene instanceof GalleryListScene; - } - } - - private static class AddToFavoriteListener extends EhCallback { - - public AddToFavoriteListener(Context context, int stageId, String sceneTag) { - super(context, stageId, sceneTag); - } - - @Override - public void onSuccess(Void result) { - showTip(R.string.add_to_favorite_success, LENGTH_SHORT); - } - - @Override - public void onFailure(Exception e) { - showTip(R.string.add_to_favorite_failure, LENGTH_LONG); - } - - @Override - public void onCancel() {} - - @Override - public boolean isInstance(SceneFragment scene) { - return scene instanceof GalleryListScene; - } - } - - private static class RemoveFromFavoriteListener extends EhCallback { - - public RemoveFromFavoriteListener(Context context, int stageId, String sceneTag) { - super(context, stageId, sceneTag); - } - - @Override - public void onSuccess(Void result) { - showTip(R.string.remove_from_favorite_success, LENGTH_SHORT); - } - - @Override - public void onFailure(Exception e) { - showTip(R.string.remove_from_favorite_failure, LENGTH_LONG); - } - - @Override - public void onCancel() {} - - @Override - public boolean isInstance(SceneFragment scene) { - return scene instanceof GalleryListScene; - } - } } diff --git a/app/src/main/java/com/hippo/ehviewer/ui/scene/GalleryPreviewsScene.java b/app/src/main/java/com/hippo/ehviewer/ui/scene/GalleryPreviewsScene.java index 81b6aa9af..bb0ac3d90 100644 --- a/app/src/main/java/com/hippo/ehviewer/ui/scene/GalleryPreviewsScene.java +++ b/app/src/main/java/com/hippo/ehviewer/ui/scene/GalleryPreviewsScene.java @@ -29,10 +29,12 @@ import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; import android.widget.TextView; + import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.appcompat.app.AlertDialog; import androidx.recyclerview.widget.RecyclerView; + import com.hippo.easyrecyclerview.EasyRecyclerView; import com.hippo.easyrecyclerview.MarginItemDecoration; import com.hippo.ehviewer.EhApplication; @@ -55,6 +57,7 @@ import com.hippo.widget.recyclerview.AutoGridLayoutManager; import com.hippo.yorozuya.AssertUtils; import com.hippo.yorozuya.LayoutUtils; import com.hippo.yorozuya.ViewUtils; + import java.util.ArrayList; import java.util.Locale; @@ -128,7 +131,7 @@ public class GalleryPreviewsScene extends ToolbarScene implements EasyRecyclerVi @Nullable @Override public View onCreateView3(LayoutInflater inflater, - @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { + @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { ContentLayout contentLayout = (ContentLayout) inflater.inflate( R.layout.scene_gallery_previews, container, false); contentLayout.hideFastScroll(); @@ -241,6 +244,61 @@ public class GalleryPreviewsScene extends ToolbarScene implements EasyRecyclerVi return true; } + private void onGetPreviewSetSuccess(Pair result, int taskId) { + if (null != mHelper && mHelper.isCurrentTask(taskId) && null != mGalleryInfo) { + PreviewSet previewSet = result.first; + int size = previewSet.size(); + ArrayList list = new ArrayList<>(size); + for (int i = 0; i < size; i++) { + list.add(previewSet.getGalleryPreview(mGalleryInfo.gid, i)); + } + + mHelper.onGetPageData(taskId, result.second, 0, list); + } + } + + private void onGetPreviewSetFailure(Exception e, int taskId) { + if (mHelper != null && mHelper.isCurrentTask(taskId)) { + mHelper.onGetException(taskId, e); + } + } + + private static class GetPreviewSetListener extends EhCallback> { + + private final int mTaskId; + + public GetPreviewSetListener(Context context, int stageId, String sceneTag, int taskId) { + super(context, stageId, sceneTag); + mTaskId = taskId; + } + + @Override + public void onSuccess(Pair result) { + GalleryPreviewsScene scene = getScene(); + if (scene != null) { + scene.onGetPreviewSetSuccess(result, mTaskId); + } + } + + @Override + public void onFailure(Exception e) { + GalleryPreviewsScene scene = getScene(); + if (scene != null) { + scene.onGetPreviewSetFailure(e, mTaskId); + } + } + + @Override + public void onCancel() { + + } + + @Override + public boolean isInstance(SceneFragment scene) { + return scene instanceof GalleryPreviewsScene; + } + } + private class GalleryPreviewHolder extends RecyclerView.ViewHolder { public LoadImageView image; @@ -337,61 +395,6 @@ public class GalleryPreviewsScene extends ToolbarScene implements EasyRecyclerVi } } - private void onGetPreviewSetSuccess(Pair result, int taskId) { - if (null != mHelper && mHelper.isCurrentTask(taskId) && null != mGalleryInfo) { - PreviewSet previewSet = result.first; - int size = previewSet.size(); - ArrayList list = new ArrayList<>(size); - for (int i = 0; i < size; i++) { - list.add(previewSet.getGalleryPreview(mGalleryInfo.gid, i)); - } - - mHelper.onGetPageData(taskId, result.second, 0, list); - } - } - - private void onGetPreviewSetFailure(Exception e, int taskId) { - if (mHelper != null && mHelper.isCurrentTask(taskId)) { - mHelper.onGetException(taskId, e); - } - } - - private static class GetPreviewSetListener extends EhCallback> { - - private final int mTaskId; - - public GetPreviewSetListener(Context context, int stageId, String sceneTag, int taskId) { - super(context, stageId, sceneTag); - mTaskId = taskId; - } - - @Override - public void onSuccess(Pair result) { - GalleryPreviewsScene scene = getScene(); - if (scene != null) { - scene.onGetPreviewSetSuccess(result, mTaskId); - } - } - - @Override - public void onFailure(Exception e) { - GalleryPreviewsScene scene = getScene(); - if (scene != null) { - scene.onGetPreviewSetFailure(e, mTaskId); - } - } - - @Override - public void onCancel() { - - } - - @Override - public boolean isInstance(SceneFragment scene) { - return scene instanceof GalleryPreviewsScene; - } - } - private class GoToDialogHelper implements View.OnClickListener, DialogInterface.OnDismissListener { diff --git a/app/src/main/java/com/hippo/ehviewer/ui/scene/HistoryScene.java b/app/src/main/java/com/hippo/ehviewer/ui/scene/HistoryScene.java index 19866e366..e86017b7e 100644 --- a/app/src/main/java/com/hippo/ehviewer/ui/scene/HistoryScene.java +++ b/app/src/main/java/com/hippo/ehviewer/ui/scene/HistoryScene.java @@ -29,11 +29,13 @@ import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; import android.widget.TextView; + import androidx.annotation.Nullable; import androidx.appcompat.app.AlertDialog; import androidx.core.view.ViewCompat; import androidx.recyclerview.widget.RecyclerView; import androidx.recyclerview.widget.StaggeredGridLayoutManager; + import com.h6ah4i.android.widget.advrecyclerview.animator.GeneralItemAnimator; import com.h6ah4i.android.widget.advrecyclerview.animator.SwipeDismissItemAnimator; import com.h6ah4i.android.widget.advrecyclerview.swipeable.RecyclerViewSwipeManager; @@ -68,11 +70,12 @@ import com.hippo.widget.LoadImageView; import com.hippo.widget.recyclerview.AutoStaggeredGridLayoutManager; import com.hippo.yorozuya.AssertUtils; import com.hippo.yorozuya.ViewUtils; + import de.greenrobot.dao.query.LazyList; public class HistoryScene extends ToolbarScene implements EasyRecyclerView.OnItemClickListener, - EasyRecyclerView.OnItemLongClickListener{ + EasyRecyclerView.OnItemLongClickListener { /*--------------- View life cycle @@ -94,7 +97,7 @@ public class HistoryScene extends ToolbarScene @Nullable @Override public View onCreateView3(LayoutInflater inflater, - @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { + @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { View view = inflater.inflate(R.layout.scene_history, container, false); View content = ViewUtils.$$(view, R.id.content); mRecyclerView = (EasyRecyclerView) ViewUtils.$$(content, R.id.recycler_view); @@ -292,6 +295,32 @@ public class HistoryScene extends ToolbarScene return true; } + private static class addToFavoriteListener extends EhCallback { + + public addToFavoriteListener(Context context, int stageId, String sceneTag) { + super(context, stageId, sceneTag); + } + + @Override + public void onSuccess(Void result) { + showTip(R.string.add_to_favorite_success, LENGTH_SHORT); + } + + @Override + public void onFailure(Exception e) { + showTip(R.string.add_to_favorite_failure, LENGTH_LONG); + } + + @Override + public void onCancel() { + } + + @Override + public boolean isInstance(SceneFragment scene) { + return scene instanceof HistoryScene; + } + } + private class HistoryHolder extends AbstractSwipeableItemViewHolder { public final View card; @@ -397,10 +426,12 @@ public class HistoryScene extends ToolbarScene } @Override - public void onSwipeItemStarted(HistoryHolder holder, int position) { } + public void onSwipeItemStarted(HistoryHolder holder, int position) { + } @Override - public void onSetSwipeBackground(HistoryHolder holder, int position, int type) {} + public void onSetSwipeBackground(HistoryHolder holder, int position, int type) { + } @Override public SwipeResultAction onSwipeItem(HistoryHolder holder, int position, int result) { @@ -437,29 +468,4 @@ public class HistoryScene extends ToolbarScene updateView(true); } } - - private static class addToFavoriteListener extends EhCallback { - - public addToFavoriteListener(Context context, int stageId, String sceneTag) { - super(context, stageId, sceneTag); - } - - @Override - public void onSuccess(Void result) { - showTip(R.string.add_to_favorite_success, LENGTH_SHORT); - } - - @Override - public void onFailure(Exception e) { - showTip(R.string.add_to_favorite_failure, LENGTH_LONG); - } - - @Override - public void onCancel() {} - - @Override - public boolean isInstance(SceneFragment scene) { - return scene instanceof HistoryScene; - } - } } diff --git a/app/src/main/java/com/hippo/ehviewer/ui/scene/ProgressScene.java b/app/src/main/java/com/hippo/ehviewer/ui/scene/ProgressScene.java index e9252ce43..13fa4f6d1 100644 --- a/app/src/main/java/com/hippo/ehviewer/ui/scene/ProgressScene.java +++ b/app/src/main/java/com/hippo/ehviewer/ui/scene/ProgressScene.java @@ -23,8 +23,10 @@ import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.TextView; + import androidx.annotation.NonNull; import androidx.annotation.Nullable; + import com.hippo.ehviewer.EhApplication; import com.hippo.ehviewer.R; import com.hippo.ehviewer.client.EhClient; @@ -45,14 +47,11 @@ public final class ProgressScene extends BaseScene implements View.OnClickListen public static final String KEY_ACTION = "action"; public static final String ACTION_GALLERY_TOKEN = "gallery_token"; - - private static final String KEY_VALID = "valid"; - private static final String KEY_ERROR = "error"; - public static final String KEY_GID = "gid"; public static final String KEY_PTOKEN = "ptoken"; public static final String KEY_PAGE = "page"; - + private static final String KEY_VALID = "valid"; + private static final String KEY_ERROR = "error"; private boolean mValid; private String mError; @@ -116,10 +115,7 @@ public final class ProgressScene extends BaseScene implements View.OnClickListen mGid = args.getLong(KEY_GID, -1); mPToken = args.getString(KEY_PTOKEN, null); mPage = args.getInt(KEY_PAGE, -1); - if (mGid == -1 || mPToken == null || mPage == -1) { - return false; - } - return true; + return mGid != -1 && mPToken != null && mPage != -1; } return false; @@ -162,7 +158,7 @@ public final class ProgressScene extends BaseScene implements View.OnClickListen @Nullable @Override public View onCreateView2(LayoutInflater inflater, - @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { + @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { View view = inflater.inflate(R.layout.scene_progress, container, false); View progress = ViewUtils.$$(view, R.id.progress); mTip = (TextView) ViewUtils.$$(view, R.id.tip); diff --git a/app/src/main/java/com/hippo/ehviewer/ui/scene/QuickSearchScene.java b/app/src/main/java/com/hippo/ehviewer/ui/scene/QuickSearchScene.java index 848a43f33..5dbb9dd22 100644 --- a/app/src/main/java/com/hippo/ehviewer/ui/scene/QuickSearchScene.java +++ b/app/src/main/java/com/hippo/ehviewer/ui/scene/QuickSearchScene.java @@ -25,10 +25,12 @@ import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.TextView; + import androidx.annotation.Nullable; import androidx.appcompat.app.AlertDialog; import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; + import com.h6ah4i.android.widget.advrecyclerview.animator.DraggableItemAnimator; import com.h6ah4i.android.widget.advrecyclerview.animator.GeneralItemAnimator; import com.h6ah4i.android.widget.advrecyclerview.draggable.DraggableItemAdapter; @@ -43,6 +45,7 @@ import com.hippo.util.DrawableManager; import com.hippo.view.ViewTransition; import com.hippo.yorozuya.AssertUtils; import com.hippo.yorozuya.ViewUtils; + import java.util.List; public final class QuickSearchScene extends ToolbarScene { @@ -79,7 +82,7 @@ public final class QuickSearchScene extends ToolbarScene { @Nullable @Override public View onCreateView3(LayoutInflater inflater, - @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { + @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { View view = inflater.inflate(R.layout.scene_label_list, container, false); mRecyclerView = (EasyRecyclerView) ViewUtils.$$(view, R.id.recycler_view); @@ -176,24 +179,24 @@ public final class QuickSearchScene extends ToolbarScene { final QuickSearch quickSearch = mQuickSearchList.get(position); new AlertDialog.Builder(context) - .setTitle(R.string.delete_quick_search_title) - .setMessage(getString(R.string.delete_quick_search_message, quickSearch.name)) - .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - EhDB.deleteQuickSearch(quickSearch); - mQuickSearchList.remove(position); - } - }) - .setOnDismissListener(new DialogInterface.OnDismissListener() { - @Override - public void onDismiss(DialogInterface dialog) { - if (null != mAdapter) { - mAdapter.notifyDataSetChanged(); + .setTitle(R.string.delete_quick_search_title) + .setMessage(getString(R.string.delete_quick_search_message, quickSearch.name)) + .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + EhDB.deleteQuickSearch(quickSearch); + mQuickSearchList.remove(position); } - updateView(); - } - }).show(); + }) + .setOnDismissListener(new DialogInterface.OnDismissListener() { + @Override + public void onDismiss(DialogInterface dialog) { + if (null != mAdapter) { + mAdapter.notifyDataSetChanged(); + } + updateView(); + } + }).show(); } } @@ -259,9 +262,11 @@ public final class QuickSearchScene extends ToolbarScene { } @Override - public void onItemDragStarted(int position) { } + public void onItemDragStarted(int position) { + } @Override - public void onItemDragFinished(int fromPosition, int toPosition, boolean result) { } + public void onItemDragFinished(int fromPosition, int toPosition, boolean result) { + } } } diff --git a/app/src/main/java/com/hippo/ehviewer/ui/scene/SecurityScene.java b/app/src/main/java/com/hippo/ehviewer/ui/scene/SecurityScene.java index 7ea37e845..5261b0af8 100755 --- a/app/src/main/java/com/hippo/ehviewer/ui/scene/SecurityScene.java +++ b/app/src/main/java/com/hippo/ehviewer/ui/scene/SecurityScene.java @@ -27,7 +27,9 @@ import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.ImageView; + import androidx.annotation.Nullable; + import com.hippo.ehviewer.R; import com.hippo.ehviewer.Settings; import com.hippo.ehviewer.ui.MainActivity; @@ -38,6 +40,7 @@ import com.hippo.widget.lockpattern.LockPatternView; import com.hippo.yorozuya.AssertUtils; import com.hippo.yorozuya.ObjectUtils; import com.hippo.yorozuya.ViewUtils; + import java.util.List; public class SecurityScene extends SolidScene implements @@ -62,6 +65,13 @@ public class SecurityScene extends SolidScene implements private CancellationSignal mFingerprintCancellationSignal; private int mRetryTimes; + private Runnable mResetFingerprintRunnable = new Runnable() { + @Override + public void run() { + if (mFingerprintIcon != null) + mFingerprintIcon.setImageResource(R.drawable.ic_fp_40px); + } + }; @Override public boolean needShowLeftDrawer() { @@ -165,7 +175,7 @@ public class SecurityScene extends SolidScene implements @Nullable @Override public View onCreateView2(LayoutInflater inflater, - @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { + @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { View view = inflater.inflate(R.layout.scene_security, container, false); mPatternView = (LockPatternView) ViewUtils.$$(view, R.id.pattern_view); @@ -187,13 +197,16 @@ public class SecurityScene extends SolidScene implements } @Override - public void onPatternStart() {} + public void onPatternStart() { + } @Override - public void onPatternCleared() {} + public void onPatternCleared() { + } @Override - public void onPatternCellAdded(List pattern) {} + public void onPatternCellAdded(List pattern) { + } @Override public void onPatternDetected(List pattern) { @@ -239,14 +252,6 @@ public class SecurityScene extends SolidScene implements && SetSecurityActivity.hasEnrolledFingerprints(mFingerprintManager); } - private Runnable mResetFingerprintRunnable = new Runnable() { - @Override - public void run() { - if (mFingerprintIcon != null) - mFingerprintIcon.setImageResource(R.drawable.ic_fp_40px); - } - }; - private void fingerprintError(boolean unrecoverable) { // Do not decrease mRetryTimes here since Android system will handle it :) mFingerprintIcon.setImageResource(R.drawable.fingerprint_error); diff --git a/app/src/main/java/com/hippo/ehviewer/ui/scene/SelectSiteScene.java b/app/src/main/java/com/hippo/ehviewer/ui/scene/SelectSiteScene.java index 8a08baec5..05bdd7669 100644 --- a/app/src/main/java/com/hippo/ehviewer/ui/scene/SelectSiteScene.java +++ b/app/src/main/java/com/hippo/ehviewer/ui/scene/SelectSiteScene.java @@ -22,7 +22,9 @@ import android.view.View; import android.view.ViewGroup; import android.widget.RadioGroup; import android.widget.Toast; + import androidx.annotation.Nullable; + import com.hippo.ehviewer.R; import com.hippo.ehviewer.Settings; import com.hippo.ehviewer.client.EhUrl; @@ -42,7 +44,7 @@ public class SelectSiteScene extends SolidScene implements View.OnClickListener @Nullable @Override public View onCreateView2(LayoutInflater inflater, @Nullable ViewGroup container, - @Nullable Bundle savedInstanceState) { + @Nullable Bundle savedInstanceState) { View view = inflater.inflate(R.layout.scene_select_site, container, false); mRadioGroup = (RadioGroup) ViewUtils.$$(view, R.id.radio_group); diff --git a/app/src/main/java/com/hippo/ehviewer/ui/scene/SignInScene.java b/app/src/main/java/com/hippo/ehviewer/ui/scene/SignInScene.java index cf4e9c2ea..1ef997899 100644 --- a/app/src/main/java/com/hippo/ehviewer/ui/scene/SignInScene.java +++ b/app/src/main/java/com/hippo/ehviewer/ui/scene/SignInScene.java @@ -26,8 +26,10 @@ import android.view.ViewGroup; import android.view.inputmethod.EditorInfo; import android.widget.EditText; import android.widget.TextView; + import androidx.annotation.Nullable; import androidx.appcompat.app.AlertDialog; + import com.google.android.material.textfield.TextInputLayout; import com.hippo.ehviewer.EhApplication; import com.hippo.ehviewer.R; @@ -113,7 +115,7 @@ public final class SignInScene extends SolidScene implements EditText.OnEditorAc @Nullable @Override public View onCreateView2(LayoutInflater inflater, @Nullable ViewGroup container, - @Nullable Bundle savedInstanceState) { + @Nullable Bundle savedInstanceState) { View view = inflater.inflate(R.layout.scene_login, container, false); View loginForm = ViewUtils.$$(view, R.id.login_form); diff --git a/app/src/main/java/com/hippo/ehviewer/ui/scene/SolidScene.java b/app/src/main/java/com/hippo/ehviewer/ui/scene/SolidScene.java index a60c3e0a4..fe480e48e 100644 --- a/app/src/main/java/com/hippo/ehviewer/ui/scene/SolidScene.java +++ b/app/src/main/java/com/hippo/ehviewer/ui/scene/SolidScene.java @@ -18,6 +18,7 @@ package com.hippo.ehviewer.ui.scene; import android.os.Bundle; import android.util.Log; + import com.hippo.ehviewer.Settings; import com.hippo.ehviewer.client.EhUtils; import com.hippo.scene.Announcer; @@ -27,16 +28,14 @@ import com.hippo.scene.Announcer; */ public class SolidScene extends BaseScene { - private static final String TAG = SolidScene.class.getSimpleName(); - public static final int CHECK_STEP_SECURITY = 0; public static final int CHECK_STEP_WARNING = 1; public static final int CHECK_STEP_ANALYTICS = 2; public static final int CHECK_STEP_SIGN_IN = 3; public static final int CHECK_STEP_SELECT_SITE = 4; - public static final String KEY_TARGET_SCENE = "target_scene"; public static final String KEY_TARGET_ARGS = "target_args"; + private static final String TAG = SolidScene.class.getSimpleName(); public void startSceneForCheckStep(int checkStep, Bundle args) { switch (checkStep) { diff --git a/app/src/main/java/com/hippo/ehviewer/ui/scene/ToolbarScene.java b/app/src/main/java/com/hippo/ehviewer/ui/scene/ToolbarScene.java index 48865d011..ceffa408a 100644 --- a/app/src/main/java/com/hippo/ehviewer/ui/scene/ToolbarScene.java +++ b/app/src/main/java/com/hippo/ehviewer/ui/scene/ToolbarScene.java @@ -24,10 +24,12 @@ import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; import android.widget.FrameLayout; + import androidx.annotation.DrawableRes; import androidx.annotation.Nullable; import androidx.annotation.StringRes; import androidx.appcompat.widget.Toolbar; + import com.hippo.ehviewer.R; public abstract class ToolbarScene extends BaseScene { @@ -39,14 +41,14 @@ public abstract class ToolbarScene extends BaseScene { @Nullable public View onCreateView3(LayoutInflater inflater, - @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { + @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { return null; } @Nullable @Override public final View onCreateView2(LayoutInflater inflater, - @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { + @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { View view = inflater.inflate(R.layout.scene_toolbar, container, false); Toolbar toolbar = (Toolbar) view.findViewById(R.id.toolbar); FrameLayout contentPanel = (FrameLayout) view.findViewById(R.id.content_panel); diff --git a/app/src/main/java/com/hippo/ehviewer/ui/scene/WarningScene.java b/app/src/main/java/com/hippo/ehviewer/ui/scene/WarningScene.java index 72300354d..c989afbae 100644 --- a/app/src/main/java/com/hippo/ehviewer/ui/scene/WarningScene.java +++ b/app/src/main/java/com/hippo/ehviewer/ui/scene/WarningScene.java @@ -20,7 +20,9 @@ import android.os.Bundle; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; + import androidx.annotation.Nullable; + import com.hippo.ehviewer.R; import com.hippo.ehviewer.Settings; import com.hippo.ehviewer.ui.MainActivity; @@ -43,7 +45,7 @@ public final class WarningScene extends SolidScene implements View.OnClickListen @Nullable @Override public View onCreateView2(LayoutInflater inflater, @Nullable ViewGroup container, - @Nullable Bundle savedInstanceState) { + @Nullable Bundle savedInstanceState) { View view = inflater.inflate(R.layout.scene_warning, container, false); mReject = ViewUtils.$$(view, R.id.reject); diff --git a/app/src/main/java/com/hippo/ehviewer/ui/scene/WebViewSignInScene.java b/app/src/main/java/com/hippo/ehviewer/ui/scene/WebViewSignInScene.java index a526e6bc4..435ccee9d 100644 --- a/app/src/main/java/com/hippo/ehviewer/ui/scene/WebViewSignInScene.java +++ b/app/src/main/java/com/hippo/ehviewer/ui/scene/WebViewSignInScene.java @@ -27,15 +27,19 @@ import android.webkit.CookieManager; import android.webkit.CookieSyncManager; import android.webkit.WebView; import android.webkit.WebViewClient; + import androidx.annotation.Nullable; + import com.hippo.ehviewer.EhApplication; import com.hippo.ehviewer.client.EhCookieStore; import com.hippo.ehviewer.client.EhUrl; import com.hippo.ehviewer.client.EhUtils; import com.hippo.yorozuya.AssertUtils; + import java.util.ArrayList; import java.util.Collections; import java.util.List; + import okhttp3.Cookie; import okhttp3.HttpUrl; @@ -57,7 +61,7 @@ public class WebViewSignInScene extends SolidScene { @SuppressWarnings("deprecation") @SuppressLint("SetJavaScriptEnabled") public View onCreateView2(LayoutInflater inflater, - @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { + @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { Context context = getContext2(); AssertUtils.assertNotNull(context); @@ -103,7 +107,7 @@ public class WebViewSignInScene extends SolidScene { List cookies = null; String[] pieces = cookieStrings.split(";"); - for (String piece: pieces) { + for (String piece : pieces) { Cookie cookie = Cookie.parse(url, piece); if (cookie == null) { continue; @@ -114,7 +118,7 @@ public class WebViewSignInScene extends SolidScene { cookies.add(cookie); } - return cookies != null ? cookies : Collections.emptyList(); + return cookies != null ? cookies : Collections.emptyList(); } private void addCookie(Context context, String domain, Cookie cookie) { @@ -136,7 +140,7 @@ public class WebViewSignInScene extends SolidScene { List cookies = parseCookies(httpUrl, cookieString); boolean getId = false; boolean getHash = false; - for (Cookie cookie: cookies) { + for (Cookie cookie : cookies) { if (EhCookieStore.KEY_IPD_MEMBER_ID.equals(cookie.name())) { getId = true; } else if (EhCookieStore.KEY_IPD_PASS_HASH.equals(cookie.name())) { diff --git a/app/src/main/java/com/hippo/ehviewer/widget/AdvanceSearchTable.java b/app/src/main/java/com/hippo/ehviewer/widget/AdvanceSearchTable.java index 480b91327..9714fe151 100644 --- a/app/src/main/java/com/hippo/ehviewer/widget/AdvanceSearchTable.java +++ b/app/src/main/java/com/hippo/ehviewer/widget/AdvanceSearchTable.java @@ -27,17 +27,12 @@ import android.widget.CheckBox; import android.widget.EditText; import android.widget.LinearLayout; import android.widget.Spinner; + import com.hippo.ehviewer.R; import com.hippo.yorozuya.NumberUtils; public class AdvanceSearchTable extends LinearLayout { - private static final String STATE_KEY_SUPER = "super"; - private static final String STATE_KEY_ADVANCE_SEARCH = "advance_search"; - private static final String STATE_KEY_MIN_RATING = "min_rating"; - private static final String STATE_KEY_PAGE_FROM = "page_from"; - private static final String STATE_KEY_PAGE_TO = "page_to"; - public static final int SNAME = 0x1; public static final int STAGS = 0x2; public static final int SDESC = 0x4; @@ -49,7 +44,11 @@ public class AdvanceSearchTable extends LinearLayout { public static final int SFL = 0x100; public static final int SFU = 0x200; public static final int SFT = 0x400; - + private static final String STATE_KEY_SUPER = "super"; + private static final String STATE_KEY_ADVANCE_SEARCH = "advance_search"; + private static final String STATE_KEY_MIN_RATING = "min_rating"; + private static final String STATE_KEY_PAGE_FROM = "page_from"; + private static final String STATE_KEY_PAGE_TO = "page_to"; private CheckBox mSname; private CheckBox mStags; private CheckBox mSdesc; @@ -139,29 +138,6 @@ public class AdvanceSearchTable extends LinearLayout { return advanceSearch; } - public int getMinRating() { - int position = mMinRating.getSelectedItemPosition(); - if (mSr.isChecked() && position >= 0) { - return position + 2; - } else { - return -1; - } - } - - public int getPageFrom() { - if (mSp.isChecked()) { - return NumberUtils.parseIntSafely(mSpf.getText().toString(), -1); - } - return -1; - } - - public int getPageTo() { - if (mSp.isChecked()) { - return NumberUtils.parseIntSafely(mSpt.getText().toString(), -1); - } - return -1; - } - public void setAdvanceSearch(int advanceSearch) { mSname.setChecked(NumberUtils.int2boolean(advanceSearch & SNAME)); mStags.setChecked(NumberUtils.int2boolean(advanceSearch & STAGS)); @@ -176,6 +152,15 @@ public class AdvanceSearchTable extends LinearLayout { mSft.setChecked(NumberUtils.int2boolean(advanceSearch & SFT)); } + public int getMinRating() { + int position = mMinRating.getSelectedItemPosition(); + if (mSr.isChecked() && position >= 0) { + return position + 2; + } else { + return -1; + } + } + public void setMinRating(int minRating) { if (minRating >= 2 && minRating <= 5) { mSr.setChecked(true); @@ -185,6 +170,13 @@ public class AdvanceSearchTable extends LinearLayout { } } + public int getPageFrom() { + if (mSp.isChecked()) { + return NumberUtils.parseIntSafely(mSpf.getText().toString(), -1); + } + return -1; + } + public void setPageFrom(int pageFrom) { if (pageFrom > 0) { mSpf.setText(Integer.toString(pageFrom)); @@ -195,6 +187,13 @@ public class AdvanceSearchTable extends LinearLayout { } } + public int getPageTo() { + if (mSp.isChecked()) { + return NumberUtils.parseIntSafely(mSpt.getText().toString(), -1); + } + return -1; + } + public void setPageTo(int pageTo) { if (pageTo > 0) { mSpt.setText(Integer.toString(pageTo)); diff --git a/app/src/main/java/com/hippo/ehviewer/widget/CategoryTable.java b/app/src/main/java/com/hippo/ehviewer/widget/CategoryTable.java index 7db60f842..b4a1cfbc9 100644 --- a/app/src/main/java/com/hippo/ehviewer/widget/CategoryTable.java +++ b/app/src/main/java/com/hippo/ehviewer/widget/CategoryTable.java @@ -24,6 +24,7 @@ import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.TableLayout; + import com.hippo.ehviewer.R; import com.hippo.ehviewer.client.EhConfig; import com.hippo.widget.CheckTextView; @@ -81,9 +82,9 @@ public class CategoryTable extends TableLayout implements View.OnLongClickListen mAsianPorn = (CheckTextView) row4.getChildAt(0); mMisc = (CheckTextView) row4.getChildAt(1); - mOptions = new CheckTextView[] { - mDoujinshi, mManga, mArtistCG, mGameCG, mWestern, - mNonH, mImageSets, mCosplay, mAsianPorn, mMisc + mOptions = new CheckTextView[]{ + mDoujinshi, mManga, mArtistCG, mGameCG, mWestern, + mNonH, mImageSets, mCosplay, mAsianPorn, mMisc }; for (CheckTextView option : mOptions) { @@ -105,26 +106,9 @@ public class CategoryTable extends TableLayout implements View.OnLongClickListen return true; } - /** - * Set each button checked or not according to category. - * - * @param category target category - */ - public void setCategory(int category) { - mDoujinshi.setChecked(!NumberUtils.int2boolean(category & EhConfig.DOUJINSHI), false); - mManga.setChecked(!NumberUtils.int2boolean(category & EhConfig.MANGA), false); - mArtistCG.setChecked(!NumberUtils.int2boolean(category & EhConfig.ARTIST_CG), false); - mGameCG.setChecked(!NumberUtils.int2boolean(category & EhConfig.GAME_CG), false); - mWestern.setChecked(!NumberUtils.int2boolean(category & EhConfig.WESTERN), false); - mNonH.setChecked(!NumberUtils.int2boolean(category & EhConfig.NON_H), false); - mImageSets.setChecked(!NumberUtils.int2boolean(category & EhConfig.IMAGE_SET), false); - mCosplay.setChecked(!NumberUtils.int2boolean(category & EhConfig.COSPLAY), false); - mAsianPorn.setChecked(!NumberUtils.int2boolean(category & EhConfig.ASIAN_PORN), false); - mMisc.setChecked(!NumberUtils.int2boolean(category & EhConfig.MISC), false); - } - /** * Get category according to button. + * * @return the category of this view */ public int getCategory() { @@ -142,6 +126,24 @@ public class CategoryTable extends TableLayout implements View.OnLongClickListen return category; } + /** + * Set each button checked or not according to category. + * + * @param category target category + */ + public void setCategory(int category) { + mDoujinshi.setChecked(!NumberUtils.int2boolean(category & EhConfig.DOUJINSHI), false); + mManga.setChecked(!NumberUtils.int2boolean(category & EhConfig.MANGA), false); + mArtistCG.setChecked(!NumberUtils.int2boolean(category & EhConfig.ARTIST_CG), false); + mGameCG.setChecked(!NumberUtils.int2boolean(category & EhConfig.GAME_CG), false); + mWestern.setChecked(!NumberUtils.int2boolean(category & EhConfig.WESTERN), false); + mNonH.setChecked(!NumberUtils.int2boolean(category & EhConfig.NON_H), false); + mImageSets.setChecked(!NumberUtils.int2boolean(category & EhConfig.IMAGE_SET), false); + mCosplay.setChecked(!NumberUtils.int2boolean(category & EhConfig.COSPLAY), false); + mAsianPorn.setChecked(!NumberUtils.int2boolean(category & EhConfig.ASIAN_PORN), false); + mMisc.setChecked(!NumberUtils.int2boolean(category & EhConfig.MISC), false); + } + @Override public Parcelable onSaveInstanceState() { final Bundle state = new Bundle(); diff --git a/app/src/main/java/com/hippo/ehviewer/widget/DialogWebChromeClient.java b/app/src/main/java/com/hippo/ehviewer/widget/DialogWebChromeClient.java index 41dcb092f..544de3290 100644 --- a/app/src/main/java/com/hippo/ehviewer/widget/DialogWebChromeClient.java +++ b/app/src/main/java/com/hippo/ehviewer/widget/DialogWebChromeClient.java @@ -25,53 +25,55 @@ import android.webkit.WebChromeClient; import android.webkit.WebView; import android.widget.EditText; import android.widget.TextView; + import androidx.appcompat.app.AlertDialog; + import com.hippo.ehviewer.R; public class DialogWebChromeClient extends WebChromeClient { - private Context context; + private Context context; - public DialogWebChromeClient(Context context) { - this.context = context; - } + public DialogWebChromeClient(Context context) { + this.context = context; + } - @Override - public boolean onJsAlert(WebView view, String url, String message, JsResult result) { - new AlertDialog.Builder(view.getContext()) - .setMessage(message) - .setPositiveButton(android.R.string.ok, (dialog, which) -> result.confirm()) - .setOnCancelListener(dialog -> result.cancel()) - .show(); - return true; - } + @Override + public boolean onJsAlert(WebView view, String url, String message, JsResult result) { + new AlertDialog.Builder(view.getContext()) + .setMessage(message) + .setPositiveButton(android.R.string.ok, (dialog, which) -> result.confirm()) + .setOnCancelListener(dialog -> result.cancel()) + .show(); + return true; + } - @Override - public boolean onJsConfirm(WebView view, String url, String message, JsResult result) { - new AlertDialog.Builder(view.getContext()) - .setMessage(message) - .setPositiveButton(android.R.string.ok, (dialog, which) -> result.confirm()) - .setNegativeButton(android.R.string.cancel, (dialog, which) -> result.cancel()) - .setOnCancelListener(dialog -> result.cancel()) - .show(); - return true; - } + @Override + public boolean onJsConfirm(WebView view, String url, String message, JsResult result) { + new AlertDialog.Builder(view.getContext()) + .setMessage(message) + .setPositiveButton(android.R.string.ok, (dialog, which) -> result.confirm()) + .setNegativeButton(android.R.string.cancel, (dialog, which) -> result.cancel()) + .setOnCancelListener(dialog -> result.cancel()) + .show(); + return true; + } - @Override - public boolean onJsPrompt(WebView view, String url, String message, String defaultValue, JsPromptResult result) { - LayoutInflater inflater = LayoutInflater.from(context); - View promptView = inflater.inflate(R.layout.dialog_js_prompt, null, false); - TextView messageView = promptView.findViewById(R.id.message); - messageView.setText(message); - final EditText valueView = promptView.findViewById(R.id.value); - valueView.setText(defaultValue); + @Override + public boolean onJsPrompt(WebView view, String url, String message, String defaultValue, JsPromptResult result) { + LayoutInflater inflater = LayoutInflater.from(context); + View promptView = inflater.inflate(R.layout.dialog_js_prompt, null, false); + TextView messageView = promptView.findViewById(R.id.message); + messageView.setText(message); + final EditText valueView = promptView.findViewById(R.id.value); + valueView.setText(defaultValue); - new AlertDialog.Builder(context) - .setView(promptView) - .setPositiveButton(android.R.string.ok, (dialog, which) -> result.confirm(valueView.getText().toString())) - .setOnCancelListener(dialog -> result.cancel()) - .show(); + new AlertDialog.Builder(context) + .setView(promptView) + .setPositiveButton(android.R.string.ok, (dialog, which) -> result.confirm(valueView.getText().toString())) + .setOnCancelListener(dialog -> result.cancel()) + .show(); - return true; - } + return true; + } } diff --git a/app/src/main/java/com/hippo/ehviewer/widget/EhDrawerLayout.java b/app/src/main/java/com/hippo/ehviewer/widget/EhDrawerLayout.java index 068202014..4237a734d 100644 --- a/app/src/main/java/com/hippo/ehviewer/widget/EhDrawerLayout.java +++ b/app/src/main/java/com/hippo/ehviewer/widget/EhDrawerLayout.java @@ -18,16 +18,18 @@ package com.hippo.ehviewer.widget; import android.animation.ValueAnimator; import android.content.Context; -import android.os.Build; import android.util.AttributeSet; import android.view.View; + import androidx.annotation.Nullable; import androidx.coordinatorlayout.widget.CoordinatorLayout; import androidx.core.view.ViewCompat; +import androidx.drawerlayout.widget.DrawerLayout; + import com.google.android.material.snackbar.Snackbar; -import com.hippo.drawerlayout.DrawerLayout; import com.hippo.ehviewer.R; import com.hippo.yorozuya.AnimationUtils; + import java.util.ArrayList; import java.util.List; @@ -74,18 +76,17 @@ public class EhDrawerLayout extends DrawerLayout { public static class Behavior extends CoordinatorLayout.Behavior { // We only support the FAB <> Snackbar shift movement on Honeycomb and above. This is // because we can use view translation properties which greatly simplifies the code. - private static final boolean SNACKBAR_BEHAVIOR_ENABLED = Build.VERSION.SDK_INT >= 11; @Override public boolean layoutDependsOn(CoordinatorLayout parent, - EhDrawerLayout child, View dependency) { + EhDrawerLayout child, View dependency) { // We're dependent on all SnackbarLayouts (if enabled) - return SNACKBAR_BEHAVIOR_ENABLED && dependency instanceof Snackbar.SnackbarLayout; + return dependency instanceof Snackbar.SnackbarLayout; } @Override public boolean onDependentViewChanged(CoordinatorLayout parent, EhDrawerLayout child, - View dependency) { + View dependency) { if (dependency instanceof Snackbar.SnackbarLayout) { for (int i = 0, n = child.getAboveSnackViewCount(); i < n; i++) { View view = child.getAboveSnackViewAt(i); @@ -96,7 +97,7 @@ public class EhDrawerLayout extends DrawerLayout { } private void updateChildTranslationForSnackbar(CoordinatorLayout parent, - EhDrawerLayout view, final View child) { + EhDrawerLayout view, final View child) { final float targetTransY = getChildTranslationYForSnackbar(parent, view); float childTranslationY = 0.0f; Object obj = child.getTag(R.id.fab_translation_y); @@ -147,7 +148,7 @@ public class EhDrawerLayout extends DrawerLayout { } private float getChildTranslationYForSnackbar(CoordinatorLayout parent, - EhDrawerLayout child) { + EhDrawerLayout child) { float minOffset = 0; final List dependencies = parent.getDependencies(child); for (int i = 0, z = dependencies.size(); i < z; i++) { diff --git a/app/src/main/java/com/hippo/ehviewer/widget/EhDrawerView.java b/app/src/main/java/com/hippo/ehviewer/widget/EhDrawerView.java index 73907f078..1d2d7dfbc 100644 --- a/app/src/main/java/com/hippo/ehviewer/widget/EhDrawerView.java +++ b/app/src/main/java/com/hippo/ehviewer/widget/EhDrawerView.java @@ -19,10 +19,9 @@ package com.hippo.ehviewer.widget; import android.content.Context; import android.util.AttributeSet; -import com.hippo.drawerlayout.DrawerLayoutChild; import com.hippo.widget.DrawerView; -public class EhDrawerView extends DrawerView implements DrawerLayoutChild { +public class EhDrawerView extends DrawerView { private int mWindowPaddingTop; private int mWindowPaddingBottom; @@ -38,20 +37,4 @@ public class EhDrawerView extends DrawerView implements DrawerLayoutChild { public EhDrawerView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); } - - @Override - public void onGetWindowPadding(int top, int bottom) { - mWindowPaddingTop = top; - mWindowPaddingBottom = bottom; - } - - @Override - public int getAdditionalTopMargin() { - return mWindowPaddingTop; - } - - @Override - public int getAdditionalBottomMargin() { - return mWindowPaddingBottom; - } } diff --git a/app/src/main/java/com/hippo/ehviewer/widget/EhNavigationView.java b/app/src/main/java/com/hippo/ehviewer/widget/EhNavigationView.java deleted file mode 100644 index 5851df0e6..000000000 --- a/app/src/main/java/com/hippo/ehviewer/widget/EhNavigationView.java +++ /dev/null @@ -1,85 +0,0 @@ -/* - * Copyright 2016 Hippo Seven - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.hippo.ehviewer.widget; - -import android.content.Context; -import android.graphics.Canvas; -import android.graphics.Paint; -import android.os.Build; -import android.util.AttributeSet; -import android.widget.LinearLayout; -import androidx.annotation.NonNull; -import com.hippo.drawerlayout.DrawerLayoutChild; - -public class EhNavigationView extends LinearLayout implements DrawerLayoutChild { - - private static final int SCRIM_COLOR = 0x44000000; - private static final boolean DRAW_SCRIM = Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP; - private Paint mPaint; - private int mWindowPaddingTop; - private int mWindowPaddingBottom; - - public EhNavigationView(Context context) { - super(context); - init(); - } - - public EhNavigationView(Context context, AttributeSet attrs) { - super(context, attrs); - init(); - } - - public EhNavigationView(Context context, AttributeSet attrs, int defStyleAttr) { - super(context, attrs, defStyleAttr); - init(); - } - - private void init() { - if (DRAW_SCRIM) { - setWillNotDraw(false); - } - } - - @Override - public void draw(@NonNull Canvas canvas) { - super.draw(canvas); - - if (DRAW_SCRIM && mWindowPaddingTop > 0) { - if (null == mPaint) { - mPaint = new Paint(); - mPaint.setColor(SCRIM_COLOR); - } - canvas.drawRect(0, 0, getWidth(), mWindowPaddingTop, mPaint); - } - } - - @Override - public void onGetWindowPadding(int top, int bottom) { - mWindowPaddingTop = top; - mWindowPaddingBottom = bottom; - } - - @Override - public int getAdditionalTopMargin() { - return 0; - } - - @Override - public int getAdditionalBottomMargin() { - return mWindowPaddingBottom; - } -} diff --git a/app/src/main/java/com/hippo/ehviewer/widget/EhStageLayout.java b/app/src/main/java/com/hippo/ehviewer/widget/EhStageLayout.java index e49bbe9b4..fa638a828 100644 --- a/app/src/main/java/com/hippo/ehviewer/widget/EhStageLayout.java +++ b/app/src/main/java/com/hippo/ehviewer/widget/EhStageLayout.java @@ -19,10 +19,9 @@ package com.hippo.ehviewer.widget; import android.content.Context; import android.util.AttributeSet; -import com.hippo.drawerlayout.DrawerLayoutChild; import com.hippo.scene.StageLayout; -public class EhStageLayout extends StageLayout implements DrawerLayoutChild { +public class EhStageLayout extends StageLayout { private int mWindowPaddingTop; private int mWindowPaddingBottom; @@ -39,19 +38,4 @@ public class EhStageLayout extends StageLayout implements DrawerLayoutChild { super(context, attrs, defStyleAttr); } - @Override - public void onGetWindowPadding(int top, int bottom) { - mWindowPaddingTop = top; - mWindowPaddingBottom = bottom; - } - - @Override - public int getAdditionalTopMargin() { - return mWindowPaddingTop; - } - - @Override - public int getAdditionalBottomMargin() { - return mWindowPaddingBottom; - } } diff --git a/app/src/main/java/com/hippo/ehviewer/widget/FixedThumb.java b/app/src/main/java/com/hippo/ehviewer/widget/FixedThumb.java index fd662c48f..5c1d1bb76 100644 --- a/app/src/main/java/com/hippo/ehviewer/widget/FixedThumb.java +++ b/app/src/main/java/com/hippo/ehviewer/widget/FixedThumb.java @@ -20,60 +20,61 @@ import android.content.Context; import android.content.res.TypedArray; import android.graphics.drawable.Drawable; import android.util.AttributeSet; + import com.hippo.ehviewer.R; import com.hippo.widget.LoadImageView; public class FixedThumb extends LoadImageView { - private float minAspect; - private float maxAspect; + private float minAspect; + private float maxAspect; - public FixedThumb(Context context) { - super(context); - init(context, null, 0, 0); - } - - public FixedThumb(Context context, AttributeSet attrs) { - super(context, attrs); - init(context, attrs, 0, 0); - } - - public FixedThumb(Context context, AttributeSet attrs, int defStyle) { - super(context, attrs, defStyle); - init(context, attrs, defStyle, 0); - } - - private void init(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { - TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.FixedThumb, defStyleAttr, defStyleRes); - minAspect = a.getFloat(R.styleable.FixedThumb_minAspect, 0.0f); - maxAspect = a.getFloat(R.styleable.FixedThumb_maxAspect, 0.0f); - a.recycle(); - } - - public void setFix(float minAspect, float maxAspect) { - this.minAspect = minAspect; - this.maxAspect = maxAspect; - } - - @Override - public void onPreSetImageDrawable(Drawable drawable, boolean isTarget) { - if (isTarget && drawable != null) { - int width = drawable.getIntrinsicWidth(); - int height = drawable.getIntrinsicHeight(); - if (width > 0 && height > 0) { - float aspect = (float) width / (float) height; - if (aspect < maxAspect && aspect > minAspect) { - setScaleType(ScaleType.CENTER_CROP); - return; - } - } + public FixedThumb(Context context) { + super(context); + init(context, null, 0, 0); } - setScaleType(ScaleType.FIT_CENTER); - } + public FixedThumb(Context context, AttributeSet attrs) { + super(context, attrs); + init(context, attrs, 0, 0); + } - @Override - public void onPreSetImageResource(int resId, boolean isTarget) { - setScaleType(ScaleType.FIT_CENTER); - } + public FixedThumb(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + init(context, attrs, defStyle, 0); + } + + private void init(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { + TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.FixedThumb, defStyleAttr, defStyleRes); + minAspect = a.getFloat(R.styleable.FixedThumb_minAspect, 0.0f); + maxAspect = a.getFloat(R.styleable.FixedThumb_maxAspect, 0.0f); + a.recycle(); + } + + public void setFix(float minAspect, float maxAspect) { + this.minAspect = minAspect; + this.maxAspect = maxAspect; + } + + @Override + public void onPreSetImageDrawable(Drawable drawable, boolean isTarget) { + if (isTarget && drawable != null) { + int width = drawable.getIntrinsicWidth(); + int height = drawable.getIntrinsicHeight(); + if (width > 0 && height > 0) { + float aspect = (float) width / (float) height; + if (aspect < maxAspect && aspect > minAspect) { + setScaleType(ScaleType.CENTER_CROP); + return; + } + } + } + + setScaleType(ScaleType.FIT_CENTER); + } + + @Override + public void onPreSetImageResource(int resId, boolean isTarget) { + setScaleType(ScaleType.FIT_CENTER); + } } diff --git a/app/src/main/java/com/hippo/ehviewer/widget/GalleryGuideView.java b/app/src/main/java/com/hippo/ehviewer/widget/GalleryGuideView.java index 8b90a5f32..3aee889c6 100644 --- a/app/src/main/java/com/hippo/ehviewer/widget/GalleryGuideView.java +++ b/app/src/main/java/com/hippo/ehviewer/widget/GalleryGuideView.java @@ -24,6 +24,7 @@ import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.TextView; + import com.hippo.android.resource.AttrResources; import com.hippo.ehviewer.R; import com.hippo.ehviewer.Settings; @@ -31,9 +32,9 @@ import com.hippo.yorozuya.ViewUtils; public class GalleryGuideView extends ViewGroup implements View.OnClickListener { + private final float[] mPoints = new float[3 * 4]; private int mBgColor; private Paint mPaint; - private final float[] mPoints = new float[3 * 4]; private int mStep; private TextView mLeftText; diff --git a/app/src/main/java/com/hippo/ehviewer/widget/GalleryHeader.java b/app/src/main/java/com/hippo/ehviewer/widget/GalleryHeader.java index 662951e40..f9549c67f 100644 --- a/app/src/main/java/com/hippo/ehviewer/widget/GalleryHeader.java +++ b/app/src/main/java/com/hippo/ehviewer/widget/GalleryHeader.java @@ -23,183 +23,185 @@ import android.util.AttributeSet; import android.view.DisplayCutout; import android.view.View; import android.view.ViewGroup; + import androidx.annotation.Nullable; import androidx.annotation.RequiresApi; + import com.hippo.ehviewer.R; import com.hippo.yorozuya.ObjectUtils; public class GalleryHeader extends ViewGroup { - private DisplayCutout displayCutout; + private DisplayCutout displayCutout; - private View battery; - private View progress; - private View clock; + private View battery; + private View progress; + private View clock; - private Rect batteryRect = new Rect(); - private Rect progressRect = new Rect(); - private Rect clockRect = new Rect(); + private Rect batteryRect = new Rect(); + private Rect progressRect = new Rect(); + private Rect clockRect = new Rect(); - private int[] location = new int[2]; + private int[] location = new int[2]; - private int lastX = 0; - private int lastY = 0; + private int lastX = 0; + private int lastY = 0; - public GalleryHeader(Context context, AttributeSet attrs) { - super(context, attrs); - } - - @RequiresApi(api = Build.VERSION_CODES.P) - public void setDisplayCutout(@Nullable DisplayCutout displayCutout) { - if (!ObjectUtils.equal(this.displayCutout, displayCutout)) { - this.displayCutout = displayCutout; - requestLayout(); - } - } - - @Override - protected void onFinishInflate() { - super.onFinishInflate(); - - battery = findViewById(R.id.battery); - progress = findViewById(R.id.progress); - clock = findViewById(R.id.clock); - } - - private void measureChild(Rect rect, View view, int width, int paddingLeft, int paddingRight) { - int left; - MarginLayoutParams lp = (MarginLayoutParams) view.getLayoutParams(); - if (view == battery) { - left = paddingLeft + lp.leftMargin; - } else if (view == progress) { - left = paddingLeft + (width - paddingLeft - paddingRight) / 2 - view.getMeasuredWidth() / 2; - } else { - left = width - paddingRight - lp.rightMargin - view.getMeasuredWidth(); - } - rect.set(left, lp.topMargin, left + view.getMeasuredWidth(), lp.topMargin + view.getMeasuredHeight()); - } - - @RequiresApi(api = Build.VERSION_CODES.P) - private boolean offsetVertically(Rect rect, View view, int width) { - int offset = 0; - - measureChild(rect, view, width, 0, 0); - rect.offset(lastX, lastY); - - for (Rect notch : displayCutout.getBoundingRects()) { - if (Rect.intersects(notch, rect)) { - offset = Math.max(offset, notch.bottom - lastY); - } + public GalleryHeader(Context context, AttributeSet attrs) { + super(context, attrs); } - if (offset != 0) { - rect.offset(-lastX, -lastY); - rect.offset(0, offset); - return true; - } else { - return false; - } - } - - @RequiresApi(api = Build.VERSION_CODES.P) - private int getOffsetLeft(Rect rect, View view, int width) { - int offset = 0; - - measureChild(rect, view, width, 0, 0); - rect.offset(lastX, lastY); - - for (Rect notch : displayCutout.getBoundingRects()) { - if (Rect.intersects(notch, rect)) { - offset = Math.max(offset, notch.right - lastX); - } + @RequiresApi(api = Build.VERSION_CODES.P) + public void setDisplayCutout(@Nullable DisplayCutout displayCutout) { + if (!ObjectUtils.equal(this.displayCutout, displayCutout)) { + this.displayCutout = displayCutout; + requestLayout(); + } } - return offset; - } + @Override + protected void onFinishInflate() { + super.onFinishInflate(); - @RequiresApi(api = Build.VERSION_CODES.P) - private int getOffsetRight(Rect rect, View view, int width) { - int offset = 0; - - measureChild(rect, view, width, 0, 0); - rect.offset(lastX, lastY); - - for (Rect notch : displayCutout.getBoundingRects()) { - if (Rect.intersects(notch, rect)) { - offset = Math.max(offset, lastX + width - notch.left); - } + battery = findViewById(R.id.battery); + progress = findViewById(R.id.progress); + clock = findViewById(R.id.clock); } - return offset; - } - - @Override - protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { - if (MeasureSpec.getMode(widthMeasureSpec) != MeasureSpec.EXACTLY) { - throw new IllegalStateException(); - } - int width = MeasureSpec.getSize(widthMeasureSpec); - - int height = 0; - for (int i = 0; i < getChildCount(); i++) { - View child = getChildAt(i); - measureChild(child, widthMeasureSpec, heightMeasureSpec); - MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams(); - height = Math.max(height, child.getMeasuredHeight() + lp.topMargin); + private void measureChild(Rect rect, View view, int width, int paddingLeft, int paddingRight) { + int left; + MarginLayoutParams lp = (MarginLayoutParams) view.getLayoutParams(); + if (view == battery) { + left = paddingLeft + lp.leftMargin; + } else if (view == progress) { + left = paddingLeft + (width - paddingLeft - paddingRight) / 2 - view.getMeasuredWidth() / 2; + } else { + left = width - paddingRight - lp.rightMargin - view.getMeasuredWidth(); + } + rect.set(left, lp.topMargin, left + view.getMeasuredWidth(), lp.topMargin + view.getMeasuredHeight()); } - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P && displayCutout != null) { - // Check progress covered - if (offsetVertically(progressRect, progress, width)) { - offsetVertically(batteryRect, battery, width); - offsetVertically(clockRect, clock, width); - height = Math.max(progressRect.bottom, Math.max(batteryRect.bottom, clockRect.bottom)); - } else { - // Clamp left and right - int paddingLeft = getOffsetLeft(batteryRect, battery, width); - int paddingRight = getOffsetRight(clockRect, clock, width); - measureChild(batteryRect, battery, width, paddingLeft, paddingRight); - measureChild(progressRect, progress, width, paddingLeft, paddingRight); - measureChild(clockRect, clock, width, paddingLeft, paddingRight); - } - } else { - measureChild(batteryRect, battery, width, 0, 0); - measureChild(progressRect, progress, width, 0, 0); - measureChild(clockRect, clock, width, 0, 0); + @RequiresApi(api = Build.VERSION_CODES.P) + private boolean offsetVertically(Rect rect, View view, int width) { + int offset = 0; + + measureChild(rect, view, width, 0, 0); + rect.offset(lastX, lastY); + + for (Rect notch : displayCutout.getBoundingRects()) { + if (Rect.intersects(notch, rect)) { + offset = Math.max(offset, notch.bottom - lastY); + } + } + + if (offset != 0) { + rect.offset(-lastX, -lastY); + rect.offset(0, offset); + return true; + } else { + return false; + } } - setMeasuredDimension(width, height); - } + @RequiresApi(api = Build.VERSION_CODES.P) + private int getOffsetLeft(Rect rect, View view, int width) { + int offset = 0; - @Override - protected void onLayout(boolean changed, int l, int t, int r, int b) { - battery.layout(batteryRect.left, batteryRect.top, batteryRect.right, batteryRect.bottom); - progress.layout(progressRect.left, progressRect.top, progressRect.right, progressRect.bottom); - clock.layout(clockRect.left, clockRect.top, clockRect.right, clockRect.bottom); + measureChild(rect, view, width, 0, 0); + rect.offset(lastX, lastY); - getLocationOnScreen(location); - if (lastX != location[0] || lastY != location[1]) { - lastX = location[0]; - lastY = location[1]; - requestLayout(); + for (Rect notch : displayCutout.getBoundingRects()) { + if (Rect.intersects(notch, rect)) { + offset = Math.max(offset, notch.right - lastX); + } + } + + return offset; } - } - @Override - public MarginLayoutParams generateLayoutParams(AttributeSet attrs) { - return new MarginLayoutParams(getContext(), attrs); - } + @RequiresApi(api = Build.VERSION_CODES.P) + private int getOffsetRight(Rect rect, View view, int width) { + int offset = 0; - @Override - protected boolean checkLayoutParams(ViewGroup.LayoutParams p) { - return p instanceof MarginLayoutParams; - } + measureChild(rect, view, width, 0, 0); + rect.offset(lastX, lastY); - @Override - protected MarginLayoutParams generateLayoutParams(ViewGroup.LayoutParams lp) { - if (lp instanceof MarginLayoutParams) { - return new MarginLayoutParams((MarginLayoutParams) lp); + for (Rect notch : displayCutout.getBoundingRects()) { + if (Rect.intersects(notch, rect)) { + offset = Math.max(offset, lastX + width - notch.left); + } + } + + return offset; + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + if (MeasureSpec.getMode(widthMeasureSpec) != MeasureSpec.EXACTLY) { + throw new IllegalStateException(); + } + int width = MeasureSpec.getSize(widthMeasureSpec); + + int height = 0; + for (int i = 0; i < getChildCount(); i++) { + View child = getChildAt(i); + measureChild(child, widthMeasureSpec, heightMeasureSpec); + MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams(); + height = Math.max(height, child.getMeasuredHeight() + lp.topMargin); + } + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P && displayCutout != null) { + // Check progress covered + if (offsetVertically(progressRect, progress, width)) { + offsetVertically(batteryRect, battery, width); + offsetVertically(clockRect, clock, width); + height = Math.max(progressRect.bottom, Math.max(batteryRect.bottom, clockRect.bottom)); + } else { + // Clamp left and right + int paddingLeft = getOffsetLeft(batteryRect, battery, width); + int paddingRight = getOffsetRight(clockRect, clock, width); + measureChild(batteryRect, battery, width, paddingLeft, paddingRight); + measureChild(progressRect, progress, width, paddingLeft, paddingRight); + measureChild(clockRect, clock, width, paddingLeft, paddingRight); + } + } else { + measureChild(batteryRect, battery, width, 0, 0); + measureChild(progressRect, progress, width, 0, 0); + measureChild(clockRect, clock, width, 0, 0); + } + + setMeasuredDimension(width, height); + } + + @Override + protected void onLayout(boolean changed, int l, int t, int r, int b) { + battery.layout(batteryRect.left, batteryRect.top, batteryRect.right, batteryRect.bottom); + progress.layout(progressRect.left, progressRect.top, progressRect.right, progressRect.bottom); + clock.layout(clockRect.left, clockRect.top, clockRect.right, clockRect.bottom); + + getLocationOnScreen(location); + if (lastX != location[0] || lastY != location[1]) { + lastX = location[0]; + lastY = location[1]; + requestLayout(); + } + } + + @Override + public MarginLayoutParams generateLayoutParams(AttributeSet attrs) { + return new MarginLayoutParams(getContext(), attrs); + } + + @Override + protected boolean checkLayoutParams(ViewGroup.LayoutParams p) { + return p instanceof MarginLayoutParams; + } + + @Override + protected MarginLayoutParams generateLayoutParams(ViewGroup.LayoutParams lp) { + if (lp instanceof MarginLayoutParams) { + return new MarginLayoutParams((MarginLayoutParams) lp); + } + return new MarginLayoutParams(lp); } - return new MarginLayoutParams(lp); - } } diff --git a/app/src/main/java/com/hippo/ehviewer/widget/GalleryInfoContentHelper.java b/app/src/main/java/com/hippo/ehviewer/widget/GalleryInfoContentHelper.java index fbe0dcdc3..76e05eae0 100644 --- a/app/src/main/java/com/hippo/ehviewer/widget/GalleryInfoContentHelper.java +++ b/app/src/main/java/com/hippo/ehviewer/widget/GalleryInfoContentHelper.java @@ -19,91 +19,93 @@ package com.hippo.ehviewer.widget; import android.annotation.SuppressLint; import android.os.Bundle; import android.os.Parcelable; + import com.hippo.ehviewer.EhApplication; import com.hippo.ehviewer.FavouriteStatusRouter; import com.hippo.ehviewer.client.data.GalleryInfo; import com.hippo.widget.ContentLayout; import com.hippo.yorozuya.IntIdGenerator; + import java.util.HashMap; import java.util.List; import java.util.Map; public abstract class GalleryInfoContentHelper extends ContentLayout.ContentHelper { - private static final String KEY_DATA_MAP = "data_map"; + private static final String KEY_DATA_MAP = "data_map"; - @SuppressLint("UseSparseArrays") - private Map map = new HashMap<>(); - private FavouriteStatusRouter.Listener listener; + @SuppressLint("UseSparseArrays") + private Map map = new HashMap<>(); + private FavouriteStatusRouter.Listener listener; - public GalleryInfoContentHelper() { - listener = (gid, slot) -> { - GalleryInfo info = map.get(gid); - if (info != null) { - info.favoriteSlot = slot; - } - }; - EhApplication.getFavouriteStatusRouter().addListener(listener); - } - - public void destroy() { - EhApplication.getFavouriteStatusRouter().removeListener(listener); - } - - @Override - protected void onAddData(GalleryInfo data) { - map.put(data.gid, data); - } - - @Override - protected void onAddData(List data) { - for (GalleryInfo info : data) { - map.put(info.gid, info); - } - } - - @Override - protected void onRemoveData(GalleryInfo data) { - map.remove(data.gid); - } - - @Override - protected void onRemoveData(List data) { - for (GalleryInfo info : data) { - map.remove(info.gid); - } - } - - @Override - protected void onClearData() { - map.clear(); - } - - @Override - protected Parcelable saveInstanceState(Parcelable superState) { - Bundle bundle = (Bundle) super.saveInstanceState(superState); - - // TODO It's a bad design - FavouriteStatusRouter router = EhApplication.getFavouriteStatusRouter(); - int id = router.saveDataMap(map); - bundle.putInt(KEY_DATA_MAP, id); - - return bundle; - } - - @Override - protected Parcelable restoreInstanceState(Parcelable state) { - Bundle bundle = (Bundle) state; - - int id = bundle.getInt(KEY_DATA_MAP, IntIdGenerator.INVALID_ID); - if (id != IntIdGenerator.INVALID_ID) { - FavouriteStatusRouter router = EhApplication.getFavouriteStatusRouter(); - Map map = router.restoreDataMap(id); - if (map != null) { - this.map = map; - } + public GalleryInfoContentHelper() { + listener = (gid, slot) -> { + GalleryInfo info = map.get(gid); + if (info != null) { + info.favoriteSlot = slot; + } + }; + EhApplication.getFavouriteStatusRouter().addListener(listener); } - return super.restoreInstanceState(state); - } + public void destroy() { + EhApplication.getFavouriteStatusRouter().removeListener(listener); + } + + @Override + protected void onAddData(GalleryInfo data) { + map.put(data.gid, data); + } + + @Override + protected void onAddData(List data) { + for (GalleryInfo info : data) { + map.put(info.gid, info); + } + } + + @Override + protected void onRemoveData(GalleryInfo data) { + map.remove(data.gid); + } + + @Override + protected void onRemoveData(List data) { + for (GalleryInfo info : data) { + map.remove(info.gid); + } + } + + @Override + protected void onClearData() { + map.clear(); + } + + @Override + protected Parcelable saveInstanceState(Parcelable superState) { + Bundle bundle = (Bundle) super.saveInstanceState(superState); + + // TODO It's a bad design + FavouriteStatusRouter router = EhApplication.getFavouriteStatusRouter(); + int id = router.saveDataMap(map); + bundle.putInt(KEY_DATA_MAP, id); + + return bundle; + } + + @Override + protected Parcelable restoreInstanceState(Parcelable state) { + Bundle bundle = (Bundle) state; + + int id = bundle.getInt(KEY_DATA_MAP, IntIdGenerator.INVALID_ID); + if (id != IntIdGenerator.INVALID_ID) { + FavouriteStatusRouter router = EhApplication.getFavouriteStatusRouter(); + Map map = router.restoreDataMap(id); + if (map != null) { + this.map = map; + } + } + + return super.restoreInstanceState(state); + } } diff --git a/app/src/main/java/com/hippo/ehviewer/widget/GalleryRatingBar.java b/app/src/main/java/com/hippo/ehviewer/widget/GalleryRatingBar.java index ec8e2dfe4..e6f564d63 100644 --- a/app/src/main/java/com/hippo/ehviewer/widget/GalleryRatingBar.java +++ b/app/src/main/java/com/hippo/ehviewer/widget/GalleryRatingBar.java @@ -20,6 +20,7 @@ import android.content.Context; import android.graphics.Canvas; import android.util.AttributeSet; import android.widget.RatingBar; + import androidx.appcompat.widget.AppCompatRatingBar; public class GalleryRatingBar extends AppCompatRatingBar @@ -38,7 +39,7 @@ public class GalleryRatingBar extends AppCompatRatingBar } public GalleryRatingBar(Context context, AttributeSet attrs, - int defStyle) { + int defStyle) { super(context, attrs, defStyle); init(); } diff --git a/app/src/main/java/com/hippo/ehviewer/widget/ImageSearchLayout.java b/app/src/main/java/com/hippo/ehviewer/widget/ImageSearchLayout.java index b4c4b6014..69660f5ff 100644 --- a/app/src/main/java/com/hippo/ehviewer/widget/ImageSearchLayout.java +++ b/app/src/main/java/com/hippo/ehviewer/widget/ImageSearchLayout.java @@ -30,7 +30,9 @@ import android.view.View; import android.widget.CheckBox; import android.widget.ImageView; import android.widget.LinearLayout; + import androidx.annotation.Nullable; + import com.hippo.ehviewer.AppConfig; import com.hippo.ehviewer.R; import com.hippo.ehviewer.client.data.ListUrlBuilder; @@ -40,6 +42,7 @@ import com.hippo.unifile.UniFile; import com.hippo.util.BitmapUtils; import com.hippo.yorozuya.IOUtils; import com.hippo.yorozuya.ViewUtils; + import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; @@ -195,8 +198,24 @@ public final class ImageSearchLayout extends LinearLayout implements View.OnClic setImagePath(ss.imagePath); } + public interface Helper { + void onSelectImage(); + } + private static class SavedState extends AbsSavedState { + public static final Parcelable.Creator CREATOR + = new Parcelable.Creator() { + @Override + public SavedState createFromParcel(Parcel in) { + return new SavedState(in); + } + + @Override + public SavedState[] newArray(int size) { + return new SavedState[size]; + } + }; String imagePath; /** @@ -219,22 +238,5 @@ public final class ImageSearchLayout extends LinearLayout implements View.OnClic super.writeToParcel(out, flags); out.writeString(imagePath); } - - public static final Parcelable.Creator CREATOR - = new Parcelable.Creator() { - @Override - public SavedState createFromParcel(Parcel in) { - return new SavedState(in); - } - - @Override - public SavedState[] newArray(int size) { - return new SavedState[size]; - } - }; - } - - public interface Helper { - void onSelectImage(); } } diff --git a/app/src/main/java/com/hippo/ehviewer/widget/ReversibleSeekBar.java b/app/src/main/java/com/hippo/ehviewer/widget/ReversibleSeekBar.java index e8e05706f..c6daa7ecf 100644 --- a/app/src/main/java/com/hippo/ehviewer/widget/ReversibleSeekBar.java +++ b/app/src/main/java/com/hippo/ehviewer/widget/ReversibleSeekBar.java @@ -20,6 +20,7 @@ import android.content.Context; import android.graphics.Canvas; import android.util.AttributeSet; import android.view.MotionEvent; + import androidx.appcompat.widget.AppCompatSeekBar; public class ReversibleSeekBar extends AppCompatSeekBar { diff --git a/app/src/main/java/com/hippo/ehviewer/widget/SearchBar.java b/app/src/main/java/com/hippo/ehviewer/widget/SearchBar.java index 4331734bb..8b3e26d4c 100644 --- a/app/src/main/java/com/hippo/ehviewer/widget/SearchBar.java +++ b/app/src/main/java/com/hippo/ehviewer/widget/SearchBar.java @@ -40,14 +40,17 @@ import android.widget.BaseAdapter; import android.widget.ImageView; import android.widget.ListView; import android.widget.TextView; + import androidx.annotation.NonNull; import androidx.cardview.widget.CardView; + import com.hippo.ehviewer.R; import com.hippo.view.ViewTransition; import com.hippo.yorozuya.AnimationUtils; import com.hippo.yorozuya.MathUtils; import com.hippo.yorozuya.SimpleAnimatorListener; import com.hippo.yorozuya.ViewUtils; + import java.util.ArrayList; import java.util.List; @@ -55,18 +58,14 @@ public class SearchBar extends CardView implements View.OnClickListener, TextView.OnEditorActionListener, TextWatcher, SearchEditText.SearchEditTextListener { - private static final String STATE_KEY_SUPER = "super"; - private static final String STATE_KEY_STATE = "state"; - - private static final long ANIMATE_TIME = 300L; - public static final int STATE_NORMAL = 0; public static final int STATE_SEARCH = 1; public static final int STATE_SEARCH_LIST = 2; - - private int mState = STATE_NORMAL; - + private static final String STATE_KEY_SUPER = "super"; + private static final String STATE_KEY_STATE = "state"; + private static final long ANIMATE_TIME = 300L; private final Rect mRect = new Rect(); + private int mState = STATE_NORMAL; private int mWidth; private int mHeight; private int mBaseHeight; @@ -218,14 +217,14 @@ public class SearchBar extends CardView implements View.OnClickListener, mSuggestionProvider = suggestionProvider; } - public void setText(String text) { - mEditText.setText(text); - } - public String getText() { return mEditText.getText().toString(); } + public void setText(String text) { + mEditText.setText(text); + } + public void cursorToEnd() { mEditText.setSelection(mEditText.getText().length()); } @@ -428,14 +427,14 @@ public class SearchBar extends CardView implements View.OnClickListener, } @SuppressWarnings("unused") - public void setProgress(float progress) { - mProgress = progress; - invalidate(); + public float getProgress() { + return mProgress; } @SuppressWarnings("unused") - public float getProgress() { - return mProgress; + public void setProgress(float progress) { + mProgress = progress; + invalidate(); } @Override @@ -496,10 +495,15 @@ public class SearchBar extends CardView implements View.OnClickListener, public interface Helper { void onClickTitle(); + void onClickLeftIcon(); + void onClickRightIcon(); + void onSearchEditTextClick(); + void onApplySearch(String query); + void onSearchEditTextBackPressed(); } @@ -513,6 +517,15 @@ public class SearchBar extends CardView implements View.OnClickListener, List providerSuggestions(String text); } + public abstract static class Suggestion { + + public abstract CharSequence getText(float textSize); + + public abstract void onClick(); + + public abstract void onLongClick(); + } + private class SuggestionAdapter extends BaseAdapter { private LayoutInflater mInflater; @@ -551,15 +564,6 @@ public class SearchBar extends CardView implements View.OnClickListener, } } - public abstract static class Suggestion { - - public abstract CharSequence getText(float textSize); - - public abstract void onClick(); - - public abstract void onLongClick(); - } - public class KeywordSuggestion extends Suggestion { private String mKeyword; diff --git a/app/src/main/java/com/hippo/ehviewer/widget/SearchDatabase.java b/app/src/main/java/com/hippo/ehviewer/widget/SearchDatabase.java index 408382cb6..a1054333e 100644 --- a/app/src/main/java/com/hippo/ehviewer/widget/SearchDatabase.java +++ b/app/src/main/java/com/hippo/ehviewer/widget/SearchDatabase.java @@ -32,19 +32,20 @@ import java.util.List; public final class SearchDatabase { - private static final String TAG = SearchDatabase.class.getSimpleName(); - public static final String COLUMN_QUERY = "query"; public static final String COLUMN_DATE = "date"; - + private static final String TAG = SearchDatabase.class.getSimpleName(); private static final String DATABASE_NAME = "search_database.db"; private static final String TABLE_SUGGESTIONS = "suggestions"; private static final int MAX_HISTORY = 100; - + private static SearchDatabase sInstance; private final SQLiteDatabase mDatabase; - private static SearchDatabase sInstance; + private SearchDatabase(Context context) { + DatabaseHelper databaseHelper = new DatabaseHelper(context); + mDatabase = databaseHelper.getWritableDatabase(); + } public static SearchDatabase getInstance(Context context) { if (sInstance == null) { @@ -53,11 +54,6 @@ public final class SearchDatabase { return sInstance; } - private SearchDatabase(Context context) { - DatabaseHelper databaseHelper = new DatabaseHelper(context); - mDatabase = databaseHelper.getWritableDatabase(); - } - public String[] getSuggestions(String prefix, int limit) { List queryList = new LinkedList<>(); limit = Math.max(0, limit); @@ -69,7 +65,7 @@ public final class SearchDatabase { .append(SqlUtils.sqlEscapeString(prefix)).append("%'"); } sb.append(" ORDER BY ").append(COLUMN_DATE).append(" DESC") - .append(" LIMIT ").append(limit); + .append(" LIMIT ").append(limit); try { Cursor cursor = mDatabase.rawQuery(sb.toString(), null); @@ -129,7 +125,7 @@ public final class SearchDatabase { selection = "_id IN " + "(SELECT _id FROM " + TABLE_SUGGESTIONS + " ORDER BY " + COLUMN_DATE + " DESC" + - " LIMIT -1 OFFSET " + String.valueOf(maxEntries) + ")"; + " LIMIT -1 OFFSET " + maxEntries + ")"; } mDatabase.delete(TABLE_SUGGESTIONS, selection, null); } catch (RuntimeException e) { diff --git a/app/src/main/java/com/hippo/ehviewer/widget/SearchEditText.java b/app/src/main/java/com/hippo/ehviewer/widget/SearchEditText.java index 16b91c49c..2597a80e6 100644 --- a/app/src/main/java/com/hippo/ehviewer/widget/SearchEditText.java +++ b/app/src/main/java/com/hippo/ehviewer/widget/SearchEditText.java @@ -20,8 +20,10 @@ import android.content.Context; import android.util.AttributeSet; import android.view.KeyEvent; import android.view.MotionEvent; + import androidx.annotation.NonNull; import androidx.appcompat.widget.AppCompatEditText; + import com.hippo.util.ExceptionUtils; public class SearchEditText extends AppCompatEditText { @@ -89,6 +91,7 @@ public class SearchEditText extends AppCompatEditText { public interface SearchEditTextListener { void onClick(); + void onBackPressed(); } } diff --git a/app/src/main/java/com/hippo/ehviewer/widget/SearchLayout.java b/app/src/main/java/com/hippo/ehviewer/widget/SearchLayout.java index aed164b40..a982a95a5 100644 --- a/app/src/main/java/com/hippo/ehviewer/widget/SearchLayout.java +++ b/app/src/main/java/com/hippo/ehviewer/widget/SearchLayout.java @@ -31,12 +31,14 @@ import android.widget.CompoundButton; import android.widget.FrameLayout; import android.widget.ImageView; import android.widget.TextView; + import androidx.annotation.IntDef; import androidx.annotation.NonNull; import androidx.appcompat.app.AlertDialog; import androidx.appcompat.widget.SwitchCompat; import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; + import com.hippo.android.resource.AttrResources; import com.hippo.easyrecyclerview.EasyRecyclerView; import com.hippo.easyrecyclerview.MarginItemDecoration; @@ -46,59 +48,44 @@ import com.hippo.ehviewer.client.exception.EhException; import com.hippo.ripple.Ripple; import com.hippo.widget.RadioGridGroup; import com.hippo.yorozuya.ViewUtils; + import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; public class SearchLayout extends EasyRecyclerView implements CompoundButton.OnCheckedChangeListener, View.OnClickListener, ImageSearchLayout.Helper { - @IntDef({SEARCH_MODE_NORMAL, SEARCH_MODE_IMAGE}) - @Retention(RetentionPolicy.SOURCE) - private @interface SearchMode {} - + public static final int SEARCH_MODE_NORMAL = 0; + public static final int SEARCH_MODE_IMAGE = 1; private static final String STATE_KEY_SUPER = "super"; private static final String STATE_KEY_SEARCH_MODE = "search_mode"; private static final String STATE_KEY_ENABLE_ADVANCE = "enable_advance"; - - public static final int SEARCH_MODE_NORMAL = 0; - public static final int SEARCH_MODE_IMAGE = 1; - private static final int ITEM_TYPE_NORMAL = 0; private static final int ITEM_TYPE_NORMAL_ADVANCE = 1; private static final int ITEM_TYPE_IMAGE = 2; private static final int ITEM_TYPE_ACTION = 3; - private static final int[] SEARCH_ITEM_COUNT_ARRAY = { 3, 2 }; - private static final int[][] SEARCH_ITEM_TYPE = { {ITEM_TYPE_NORMAL, ITEM_TYPE_NORMAL_ADVANCE, ITEM_TYPE_ACTION}, // SEARCH_MODE_NORMAL {ITEM_TYPE_IMAGE, ITEM_TYPE_ACTION}, // SEARCH_MODE_IMAGE }; - private LayoutInflater mInflater; - private int mSearchMode = SEARCH_MODE_NORMAL; private boolean mEnableAdvance = false; - private View mNormalView; private CategoryTable mCategoryTable; private RadioGridGroup mNormalSearchMode; private ImageView mNormalSearchModeHelp; private SwitchCompat mEnableAdvanceSwitch; - private View mAdvanceView; private AdvanceSearchTable mTableAdvanceSearch; - private ImageSearchLayout mImageView; - private View mActionView; private TextView mAction; - private LinearLayoutManager mLayoutManager; private SearchAdapter mAdapter; - private Helper mHelper; public SearchLayout(Context context, AttributeSet attrs) { @@ -334,6 +321,17 @@ public class SearchLayout extends EasyRecyclerView implements CompoundButton.OnC } } + @IntDef({SEARCH_MODE_NORMAL, SEARCH_MODE_IMAGE}) + @Retention(RetentionPolicy.SOURCE) + private @interface SearchMode { + } + + public interface Helper { + void onChangeSearchMode(); + + void onSelectImage(); + } + private class SimpleHolder extends RecyclerView.ViewHolder { public SimpleHolder(View itemView) { super(itemView); @@ -412,9 +410,4 @@ public class SearchLayout extends EasyRecyclerView implements CompoundButton.OnC // Empty, bind view in create view } } - - public interface Helper { - void onChangeSearchMode(); - void onSelectImage(); - } } diff --git a/app/src/main/java/com/hippo/ehviewer/widget/SeekBarPanel.java b/app/src/main/java/com/hippo/ehviewer/widget/SeekBarPanel.java index 40f3f8634..16fe4d7f9 100644 --- a/app/src/main/java/com/hippo/ehviewer/widget/SeekBarPanel.java +++ b/app/src/main/java/com/hippo/ehviewer/widget/SeekBarPanel.java @@ -22,7 +22,9 @@ import android.util.AttributeSet; import android.view.MotionEvent; import android.widget.LinearLayout; import android.widget.SeekBar; + import androidx.annotation.NonNull; + import com.hippo.ehviewer.R; import com.hippo.yorozuya.ViewUtils; diff --git a/app/src/main/java/com/hippo/ehviewer/widget/SimpleRatingView.java b/app/src/main/java/com/hippo/ehviewer/widget/SimpleRatingView.java index 01fd47eb2..b6c41356f 100644 --- a/app/src/main/java/com/hippo/ehviewer/widget/SimpleRatingView.java +++ b/app/src/main/java/com/hippo/ehviewer/widget/SimpleRatingView.java @@ -97,6 +97,10 @@ public class SimpleRatingView extends View { canvas.restoreToCount(saved); } + public float getRating() { + return mRating; + } + public void setRating(float rating) { if (mRating != rating) { mRating = rating; @@ -107,8 +111,4 @@ public class SimpleRatingView extends View { } } } - - public float getRating() { - return mRating; - } } diff --git a/app/src/main/java/com/hippo/hardware/ShakeDetector.java b/app/src/main/java/com/hippo/hardware/ShakeDetector.java index c7ea7f980..e00227c1a 100644 --- a/app/src/main/java/com/hippo/hardware/ShakeDetector.java +++ b/app/src/main/java/com/hippo/hardware/ShakeDetector.java @@ -27,10 +27,6 @@ public class ShakeDetector implements SensorEventListener { mListener = listener; } - public interface OnShakeListener { - void onShake(int count); - } - @Override public void onAccuracyChanged(Sensor sensor, int accuracy) { // ignore @@ -70,4 +66,8 @@ public class ShakeDetector implements SensorEventListener { } } } + + public interface OnShakeListener { + void onShake(int count); + } } diff --git a/app/src/main/java/com/hippo/io/FileInputStreamPipe.java b/app/src/main/java/com/hippo/io/FileInputStreamPipe.java index f63b668fc..6ff3e9f4f 100644 --- a/app/src/main/java/com/hippo/io/FileInputStreamPipe.java +++ b/app/src/main/java/com/hippo/io/FileInputStreamPipe.java @@ -17,8 +17,10 @@ package com.hippo.io; import androidx.annotation.NonNull; + import com.hippo.streampipe.InputStreamPipe; import com.hippo.yorozuya.IOUtils; + import java.io.File; import java.io.FileInputStream; import java.io.IOException; diff --git a/app/src/main/java/com/hippo/io/FileOutputStreamPipe.java b/app/src/main/java/com/hippo/io/FileOutputStreamPipe.java index 62c1c7a96..7d59e9dbb 100644 --- a/app/src/main/java/com/hippo/io/FileOutputStreamPipe.java +++ b/app/src/main/java/com/hippo/io/FileOutputStreamPipe.java @@ -17,8 +17,10 @@ package com.hippo.io; import androidx.annotation.NonNull; + import com.hippo.streampipe.OutputStreamPipe; import com.hippo.yorozuya.IOUtils; + import java.io.File; import java.io.FileOutputStream; import java.io.IOException; diff --git a/app/src/main/java/com/hippo/io/UniFileInputStreamPipe.java b/app/src/main/java/com/hippo/io/UniFileInputStreamPipe.java index 6623fbb30..fa3bbc27f 100644 --- a/app/src/main/java/com/hippo/io/UniFileInputStreamPipe.java +++ b/app/src/main/java/com/hippo/io/UniFileInputStreamPipe.java @@ -17,9 +17,11 @@ package com.hippo.io; import androidx.annotation.NonNull; + import com.hippo.streampipe.InputStreamPipe; import com.hippo.unifile.UniFile; import com.hippo.yorozuya.IOUtils; + import java.io.IOException; import java.io.InputStream; diff --git a/app/src/main/java/com/hippo/io/UniFileOutputStreamPipe.java b/app/src/main/java/com/hippo/io/UniFileOutputStreamPipe.java index 37a56c28f..1642cb9c4 100644 --- a/app/src/main/java/com/hippo/io/UniFileOutputStreamPipe.java +++ b/app/src/main/java/com/hippo/io/UniFileOutputStreamPipe.java @@ -17,9 +17,11 @@ package com.hippo.io; import androidx.annotation.NonNull; + import com.hippo.streampipe.OutputStreamPipe; import com.hippo.unifile.UniFile; import com.hippo.yorozuya.IOUtils; + import java.io.IOException; import java.io.OutputStream; diff --git a/app/src/main/java/com/hippo/network/CookieDatabase.java b/app/src/main/java/com/hippo/network/CookieDatabase.java index b277316ae..1c313899d 100644 --- a/app/src/main/java/com/hippo/network/CookieDatabase.java +++ b/app/src/main/java/com/hippo/network/CookieDatabase.java @@ -27,210 +27,214 @@ import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteOpenHelper; import android.database.sqlite.SQLiteStatement; import android.util.Log; + import androidx.annotation.Nullable; + import com.hippo.database.MSQLiteBuilder; import com.hippo.util.SqlUtils; + import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; + import okhttp3.Cookie; class CookieDatabase { - private static final String LOG_TAG = CookieDatabase.class.getSimpleName(); + private static final String LOG_TAG = CookieDatabase.class.getSimpleName(); - private static final int VERSION_1 = 1; - private static final String TABLE_COOKIE = "OK_HTTP_3_COOKIE"; - private static final String COLUMN_ID = "_id"; - private static final String COLUMN_NAME = "NAME"; - private static final String COLUMN_VALUE = "VALUE"; - private static final String COLUMN_EXPIRES_AT = "EXPIRES_AT"; - private static final String COLUMN_DOMAIN = "DOMAIN"; - private static final String COLUMN_PATH = "PATH"; - private static final String COLUMN_SECURE = "SECURE"; - private static final String COLUMN_HTTP_ONLY = "HTTP_ONLY"; - private static final String COLUMN_PERSISTENT = "PERSISTENT"; - private static final String COLUMN_HOST_ONLY = "HOST_ONLY"; + private static final int VERSION_1 = 1; + private static final String TABLE_COOKIE = "OK_HTTP_3_COOKIE"; + private static final String COLUMN_ID = "_id"; + private static final String COLUMN_NAME = "NAME"; + private static final String COLUMN_VALUE = "VALUE"; + private static final String COLUMN_EXPIRES_AT = "EXPIRES_AT"; + private static final String COLUMN_DOMAIN = "DOMAIN"; + private static final String COLUMN_PATH = "PATH"; + private static final String COLUMN_SECURE = "SECURE"; + private static final String COLUMN_HTTP_ONLY = "HTTP_ONLY"; + private static final String COLUMN_PERSISTENT = "PERSISTENT"; + private static final String COLUMN_HOST_ONLY = "HOST_ONLY"; - private static final int DB_VERSION = VERSION_1; + private static final int DB_VERSION = VERSION_1; - private final Map cookieIdMap = new HashMap<>(); - private final SQLiteOpenHelper helper; - private final SQLiteDatabase db; + private final Map cookieIdMap = new HashMap<>(); + private final SQLiteOpenHelper helper; + private final SQLiteDatabase db; - public CookieDatabase(Context context, String name) { - helper = new MSQLiteBuilder() - .version(VERSION_1) - .createTable(TABLE_COOKIE) - .insertColumn(TABLE_COOKIE, COLUMN_NAME, String.class) - .insertColumn(TABLE_COOKIE, COLUMN_VALUE, String.class) - .insertColumn(TABLE_COOKIE, COLUMN_EXPIRES_AT, long.class) - .insertColumn(TABLE_COOKIE, COLUMN_DOMAIN, String.class) - .insertColumn(TABLE_COOKIE, COLUMN_PATH, String.class) - .insertColumn(TABLE_COOKIE, COLUMN_SECURE, boolean.class) - .insertColumn(TABLE_COOKIE, COLUMN_HTTP_ONLY, boolean.class) - .insertColumn(TABLE_COOKIE, COLUMN_PERSISTENT, boolean.class) - .insertColumn(TABLE_COOKIE, COLUMN_HOST_ONLY, boolean.class) - .build(context, name, DB_VERSION); - db = helper.getWritableDatabase(); - } - - @Nullable - private static Cookie getCookie(Cursor cursor, long now) { - String name = SqlUtils.getString(cursor, COLUMN_NAME, null); - String value = SqlUtils.getString(cursor, COLUMN_VALUE, null); - long expiresAt = SqlUtils.getLong(cursor, COLUMN_EXPIRES_AT, 0); - String domain = SqlUtils.getString(cursor, COLUMN_DOMAIN, null); - String path = SqlUtils.getString(cursor, COLUMN_PATH, null); - boolean secure = SqlUtils.getBoolean(cursor, COLUMN_SECURE, false); - boolean httpOnly = SqlUtils.getBoolean(cursor, COLUMN_HTTP_ONLY, false); - boolean persistent = SqlUtils.getBoolean(cursor, COLUMN_PERSISTENT, false); - boolean hostOnly = SqlUtils.getBoolean(cursor, COLUMN_HOST_ONLY, false); - - if (name == null || domain == null || path == null) { - return null; + public CookieDatabase(Context context, String name) { + helper = new MSQLiteBuilder() + .version(VERSION_1) + .createTable(TABLE_COOKIE) + .insertColumn(TABLE_COOKIE, COLUMN_NAME, String.class) + .insertColumn(TABLE_COOKIE, COLUMN_VALUE, String.class) + .insertColumn(TABLE_COOKIE, COLUMN_EXPIRES_AT, long.class) + .insertColumn(TABLE_COOKIE, COLUMN_DOMAIN, String.class) + .insertColumn(TABLE_COOKIE, COLUMN_PATH, String.class) + .insertColumn(TABLE_COOKIE, COLUMN_SECURE, boolean.class) + .insertColumn(TABLE_COOKIE, COLUMN_HTTP_ONLY, boolean.class) + .insertColumn(TABLE_COOKIE, COLUMN_PERSISTENT, boolean.class) + .insertColumn(TABLE_COOKIE, COLUMN_HOST_ONLY, boolean.class) + .build(context, name, DB_VERSION); + db = helper.getWritableDatabase(); } - // Check non-persistent or expired - if (!persistent || expiresAt <= now) { - return null; - } + @Nullable + private static Cookie getCookie(Cursor cursor, long now) { + String name = SqlUtils.getString(cursor, COLUMN_NAME, null); + String value = SqlUtils.getString(cursor, COLUMN_VALUE, null); + long expiresAt = SqlUtils.getLong(cursor, COLUMN_EXPIRES_AT, 0); + String domain = SqlUtils.getString(cursor, COLUMN_DOMAIN, null); + String path = SqlUtils.getString(cursor, COLUMN_PATH, null); + boolean secure = SqlUtils.getBoolean(cursor, COLUMN_SECURE, false); + boolean httpOnly = SqlUtils.getBoolean(cursor, COLUMN_HTTP_ONLY, false); + boolean persistent = SqlUtils.getBoolean(cursor, COLUMN_PERSISTENT, false); + boolean hostOnly = SqlUtils.getBoolean(cursor, COLUMN_HOST_ONLY, false); - Cookie.Builder builder = new Cookie.Builder(); - builder.name(name); - builder.value(value); - if (hostOnly) { - builder.hostOnlyDomain(domain); - } else { - builder.domain(domain); - } - builder.path(path); - builder.expiresAt(expiresAt); - if (secure) builder.secure(); - if (httpOnly) builder.httpOnly(); - return builder.build(); - } + if (name == null || domain == null || path == null) { + return null; + } - public Map getAllCookies() { - long now = System.currentTimeMillis(); - Map map = new HashMap<>(); - List toRemove = new ArrayList<>(); + // Check non-persistent or expired + if (!persistent || expiresAt <= now) { + return null; + } - Cursor cursor = db.rawQuery("SELECT * FROM " + TABLE_COOKIE + ";", null); - try { - while (cursor.moveToNext()) { - long id = SqlUtils.getLong(cursor, COLUMN_ID, 0); - Cookie cookie = getCookie(cursor, now); - - if (cookie != null) { - // Save id of the cookie in db - cookieIdMap.put(cookie, id); - - // Put cookie to set - CookieSet set = map.get(cookie.domain()); - if (set == null) { - set = new CookieSet(); - map.put(cookie.domain(), set); - } - set.add(cookie); + Cookie.Builder builder = new Cookie.Builder(); + builder.name(name); + builder.value(value); + if (hostOnly) { + builder.hostOnlyDomain(domain); } else { - // Mark to remove the cookie - toRemove.add(id); + builder.domain(domain); } - } - } finally { - cursor.close(); + builder.path(path); + builder.expiresAt(expiresAt); + if (secure) builder.secure(); + if (httpOnly) builder.httpOnly(); + return builder.build(); } - // Remove invalid or expired cookie - if (!toRemove.isEmpty()) { - SQLiteStatement statement = db.compileStatement( - "DELETE FROM " + TABLE_COOKIE + " WHERE " + COLUMN_ID + " = ?;"); - db.beginTransaction(); - try { - for (long id : toRemove) { - statement.bindLong(1, id); - statement.executeUpdateDelete(); + public Map getAllCookies() { + long now = System.currentTimeMillis(); + Map map = new HashMap<>(); + List toRemove = new ArrayList<>(); + + Cursor cursor = db.rawQuery("SELECT * FROM " + TABLE_COOKIE + ";", null); + try { + while (cursor.moveToNext()) { + long id = SqlUtils.getLong(cursor, COLUMN_ID, 0); + Cookie cookie = getCookie(cursor, now); + + if (cookie != null) { + // Save id of the cookie in db + cookieIdMap.put(cookie, id); + + // Put cookie to set + CookieSet set = map.get(cookie.domain()); + if (set == null) { + set = new CookieSet(); + map.put(cookie.domain(), set); + } + set.add(cookie); + } else { + // Mark to remove the cookie + toRemove.add(id); + } + } + } finally { + cursor.close(); } - db.setTransactionSuccessful(); - } finally { - db.endTransaction(); - } + + // Remove invalid or expired cookie + if (!toRemove.isEmpty()) { + SQLiteStatement statement = db.compileStatement( + "DELETE FROM " + TABLE_COOKIE + " WHERE " + COLUMN_ID + " = ?;"); + db.beginTransaction(); + try { + for (long id : toRemove) { + statement.bindLong(1, id); + statement.executeUpdateDelete(); + } + db.setTransactionSuccessful(); + } finally { + db.endTransaction(); + } + } + + return map; } - return map; - } - - public ContentValues toContentValues(Cookie cookie) { - ContentValues contentValues = new ContentValues(9); - contentValues.put(COLUMN_NAME, cookie.name()); - contentValues.put(COLUMN_VALUE, cookie.value()); - contentValues.put(COLUMN_EXPIRES_AT, cookie.expiresAt()); - contentValues.put(COLUMN_DOMAIN, cookie.domain()); - contentValues.put(COLUMN_PATH, cookie.path()); - contentValues.put(COLUMN_SECURE, cookie.secure()); - contentValues.put(COLUMN_HTTP_ONLY, cookie.httpOnly()); - contentValues.put(COLUMN_PERSISTENT, cookie.persistent()); - contentValues.put(COLUMN_HOST_ONLY, cookie.hostOnly()); - return contentValues; - } - - public void add(Cookie cookie) { - long id = db.insert(TABLE_COOKIE, null, toContentValues(cookie)); - if (id != -1L) { - Long oldId = cookieIdMap.put(cookie, id); - if (oldId != null) Log.e(LOG_TAG, "Add a duplicate cookie"); - } else { - Log.e(LOG_TAG, "An error occurred when insert a cookie"); - } - } - - public void update(Cookie from, Cookie to) { - Long id = cookieIdMap.get(from); - if (id == null) { - Log.e(LOG_TAG, "Can't get id when update the cookie"); - return; + public ContentValues toContentValues(Cookie cookie) { + ContentValues contentValues = new ContentValues(9); + contentValues.put(COLUMN_NAME, cookie.name()); + contentValues.put(COLUMN_VALUE, cookie.value()); + contentValues.put(COLUMN_EXPIRES_AT, cookie.expiresAt()); + contentValues.put(COLUMN_DOMAIN, cookie.domain()); + contentValues.put(COLUMN_PATH, cookie.path()); + contentValues.put(COLUMN_SECURE, cookie.secure()); + contentValues.put(COLUMN_HTTP_ONLY, cookie.httpOnly()); + contentValues.put(COLUMN_PERSISTENT, cookie.persistent()); + contentValues.put(COLUMN_HOST_ONLY, cookie.hostOnly()); + return contentValues; } - ContentValues values = toContentValues(to); - String whereClause = COLUMN_ID + " = ?"; - String[] whereArgs = { id.toString() }; - int count = db.update(TABLE_COOKIE, values, whereClause, whereArgs); - if (count != 1) { - Log.e(LOG_TAG, "Bad result when update cookie: " + count); + public void add(Cookie cookie) { + long id = db.insert(TABLE_COOKIE, null, toContentValues(cookie)); + if (id != -1L) { + Long oldId = cookieIdMap.put(cookie, id); + if (oldId != null) Log.e(LOG_TAG, "Add a duplicate cookie"); + } else { + Log.e(LOG_TAG, "An error occurred when insert a cookie"); + } } - // Update it in cookie-id map - cookieIdMap.remove(from); - cookieIdMap.put(to, id); - } + public void update(Cookie from, Cookie to) { + Long id = cookieIdMap.get(from); + if (id == null) { + Log.e(LOG_TAG, "Can't get id when update the cookie"); + return; + } - public void remove(Cookie cookie) { - Long id = cookieIdMap.get(cookie); - if (id == null) { - Log.e(LOG_TAG, "Can't get id when remove the cookie"); - return; + ContentValues values = toContentValues(to); + String whereClause = COLUMN_ID + " = ?"; + String[] whereArgs = {id.toString()}; + int count = db.update(TABLE_COOKIE, values, whereClause, whereArgs); + if (count != 1) { + Log.e(LOG_TAG, "Bad result when update cookie: " + count); + } + + // Update it in cookie-id map + cookieIdMap.remove(from); + cookieIdMap.put(to, id); } - String whereClause = COLUMN_ID + " = ?"; - String[] whereArgs = { id.toString() }; - int count = db.delete(TABLE_COOKIE, whereClause, whereArgs); - if (count != 1) { - Log.e(LOG_TAG, "Bad result when remove cookie: " + count); + public void remove(Cookie cookie) { + Long id = cookieIdMap.get(cookie); + if (id == null) { + Log.e(LOG_TAG, "Can't get id when remove the cookie"); + return; + } + + String whereClause = COLUMN_ID + " = ?"; + String[] whereArgs = {id.toString()}; + int count = db.delete(TABLE_COOKIE, whereClause, whereArgs); + if (count != 1) { + Log.e(LOG_TAG, "Bad result when remove cookie: " + count); + } + + // Remove it from cookie-id map + cookieIdMap.remove(cookie); } - // Remove it from cookie-id map - cookieIdMap.remove(cookie); - } + public void clear() { + db.delete(TABLE_COOKIE, null, null); + cookieIdMap.clear(); + } - public void clear() { - db.delete(TABLE_COOKIE, null, null); - cookieIdMap.clear(); - } - - public void close() { - db.close(); - helper.close(); - } + public void close() { + db.close(); + helper.close(); + } } diff --git a/app/src/main/java/com/hippo/network/CookieRepository.java b/app/src/main/java/com/hippo/network/CookieRepository.java index cea756ee7..a4a469b04 100644 --- a/app/src/main/java/com/hippo/network/CookieRepository.java +++ b/app/src/main/java/com/hippo/network/CookieRepository.java @@ -21,180 +21,178 @@ package com.hippo.network; */ import android.content.Context; + import com.hippo.yorozuya.ObjectUtils; + import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.List; import java.util.Map; import java.util.regex.Pattern; + import okhttp3.Cookie; import okhttp3.CookieJar; import okhttp3.HttpUrl; public class CookieRepository implements CookieJar { - private CookieDatabase db; - private Map map; + /** + * Quick and dirty pattern to differentiate IP addresses from hostnames. This is an approximation + * of Android's private InetAddress#isNumeric API. + *

+ * This matches IPv6 addresses as a hex string containing at least one colon, and possibly + * including dots after the first colon. It matches IPv4 addresses as strings containing only + * decimal digits and dots. This pattern matches strings like "a:.23" and "54" that are neither IP + * addresses nor hostnames; they will be verified as IP addresses (which is a more strict + * verification). + */ + private static final Pattern VERIFY_AS_IP_ADDRESS = Pattern.compile("([0-9a-fA-F]*:[0-9a-fA-F:.]*)|([\\d.]+)"); + private CookieDatabase db; + private Map map; - public CookieRepository(Context context, String name) { - db = new CookieDatabase(context, name); - map = db.getAllCookies(); - } - - public synchronized void addCookie(Cookie cookie) { - // For cookie database - Cookie toAdd = null; - Cookie toUpdate = null; - Cookie toRemove = null; - - CookieSet set = map.get(cookie.domain()); - if (set == null) { - set = new CookieSet(); - map.put(cookie.domain(), set); + public CookieRepository(Context context, String name) { + db = new CookieDatabase(context, name); + map = db.getAllCookies(); } - if (cookie.expiresAt() <= System.currentTimeMillis()) { - toRemove = set.remove(cookie); - // If the cookie is not persistent, it's not in database - if (toRemove != null && !toRemove.persistent()) { - toRemove = null; - } - } else { - toAdd = cookie; - toUpdate = set.add(cookie); - // If the cookie is not persistent, it's not in database - if (!toAdd.persistent()) toAdd = null; - if (toUpdate != null && !toUpdate.persistent()) toUpdate = null; - // Remove the cookie if it updates to null - if (toAdd == null && toUpdate != null) { - toRemove = toUpdate; - toUpdate = null; - } + /** + * Returns true if `host` is not a host name and might be an IP address. + */ + private static boolean verifyAsIpAddress(String host) { + return VERIFY_AS_IP_ADDRESS.matcher(host).matches(); } - if (toRemove != null) { - db.remove(toRemove); - } - if (toAdd != null) { - if (toUpdate != null) { - db.update(toUpdate, toAdd); - } else { - db.add(toAdd); - } - } - } + // okhttp3.Cookie.domainMatch(HttpUrl, String) + protected static boolean domainMatch(HttpUrl url, String domain) { + String urlHost = url.host(); - public String getCookieHeader(HttpUrl url) { - List cookies = getCookies(url); - StringBuilder cookieHeader = new StringBuilder(); - for (int i = 0, size = cookies.size(); i < size; i++) { - if (i > 0) { - cookieHeader.append("; "); - } - Cookie cookie = cookies.get(i); - cookieHeader.append(cookie.name()).append('=').append(cookie.value()); - } - return cookieHeader.toString(); - } + if (urlHost.equals(domain)) { + return true; // As in 'example.com' matching 'example.com'. + } - public synchronized List getCookies(HttpUrl url) { - List accepted = new ArrayList<>(); - List expired = new ArrayList<>(); - - for (Map.Entry entry : map.entrySet()) { - String domain = entry.getKey(); - CookieSet cookieSet = entry.getValue(); - if (domainMatch(url, domain)) { - cookieSet.get(url, accepted, expired); - } + return urlHost.endsWith(domain) + && urlHost.charAt(urlHost.length() - domain.length() - 1) == '.' + && !verifyAsIpAddress(urlHost); // As in 'example.com' matching 'www.example.com'. } - for (Cookie cookie : expired) { - if (cookie.persistent()) { - db.remove(cookie); - } + public synchronized void addCookie(Cookie cookie) { + // For cookie database + Cookie toAdd = null; + Cookie toUpdate = null; + Cookie toRemove = null; + + CookieSet set = map.get(cookie.domain()); + if (set == null) { + set = new CookieSet(); + map.put(cookie.domain(), set); + } + + if (cookie.expiresAt() <= System.currentTimeMillis()) { + toRemove = set.remove(cookie); + // If the cookie is not persistent, it's not in database + if (toRemove != null && !toRemove.persistent()) { + toRemove = null; + } + } else { + toAdd = cookie; + toUpdate = set.add(cookie); + // If the cookie is not persistent, it's not in database + if (!toAdd.persistent()) toAdd = null; + if (toUpdate != null && !toUpdate.persistent()) toUpdate = null; + // Remove the cookie if it updates to null + if (toAdd == null && toUpdate != null) { + toRemove = toUpdate; + toUpdate = null; + } + } + + if (toRemove != null) { + db.remove(toRemove); + } + if (toAdd != null) { + if (toUpdate != null) { + db.update(toUpdate, toAdd); + } else { + db.add(toAdd); + } + } } - // RFC 6265 Section-5.4 step 2, sort the cookie-list - // Cookies with longer paths are listed before cookies with shorter paths. - // Ignore creation-time, we don't store them. - Collections.sort(accepted, new Comparator() { - @Override - public int compare(Cookie o1, Cookie o2) { - return o2.path().length() - o1.path().length(); - } - }); - - return accepted; - } - - public boolean contains(HttpUrl url, String name) { - for (Cookie cookie : getCookies(url)) { - if (ObjectUtils.equal(cookie.name(), name)) { - return true; - } - } - return false; - } - - /** - * Remove all cookies in this {@code CookieRepository}. - */ - public synchronized void clear() { - map.clear(); - db.clear(); - } - - public synchronized void close() { - db.close(); - } - - @Override - public void saveFromResponse(HttpUrl httpUrl, List list) { - for (Cookie cookie : list) { - addCookie(cookie); - } - } - - @Override - public List loadForRequest(HttpUrl httpUrl) { - return getCookies(httpUrl); - } - - /** - * Quick and dirty pattern to differentiate IP addresses from hostnames. This is an approximation - * of Android's private InetAddress#isNumeric API. - * - * This matches IPv6 addresses as a hex string containing at least one colon, and possibly - * including dots after the first colon. It matches IPv4 addresses as strings containing only - * decimal digits and dots. This pattern matches strings like "a:.23" and "54" that are neither IP - * addresses nor hostnames; they will be verified as IP addresses (which is a more strict - * verification). - */ - private static final Pattern VERIFY_AS_IP_ADDRESS = Pattern.compile("([0-9a-fA-F]*:[0-9a-fA-F:.]*)|([\\d.]+)"); - - /** - * Returns true if `host` is not a host name and might be an IP address. - */ - private static boolean verifyAsIpAddress(String host) { - return VERIFY_AS_IP_ADDRESS.matcher(host).matches(); - } - - // okhttp3.Cookie.domainMatch(HttpUrl, String) - protected static boolean domainMatch(HttpUrl url, String domain) { - String urlHost = url.host(); - - if (urlHost.equals(domain)) { - return true; // As in 'example.com' matching 'example.com'. + public String getCookieHeader(HttpUrl url) { + List cookies = getCookies(url); + StringBuilder cookieHeader = new StringBuilder(); + for (int i = 0, size = cookies.size(); i < size; i++) { + if (i > 0) { + cookieHeader.append("; "); + } + Cookie cookie = cookies.get(i); + cookieHeader.append(cookie.name()).append('=').append(cookie.value()); + } + return cookieHeader.toString(); } - if (urlHost.endsWith(domain) - && urlHost.charAt(urlHost.length() - domain.length() - 1) == '.' - && !verifyAsIpAddress(urlHost)) { - return true; // As in 'example.com' matching 'www.example.com'. + public synchronized List getCookies(HttpUrl url) { + List accepted = new ArrayList<>(); + List expired = new ArrayList<>(); + + for (Map.Entry entry : map.entrySet()) { + String domain = entry.getKey(); + CookieSet cookieSet = entry.getValue(); + if (domainMatch(url, domain)) { + cookieSet.get(url, accepted, expired); + } + } + + for (Cookie cookie : expired) { + if (cookie.persistent()) { + db.remove(cookie); + } + } + + // RFC 6265 Section-5.4 step 2, sort the cookie-list + // Cookies with longer paths are listed before cookies with shorter paths. + // Ignore creation-time, we don't store them. + Collections.sort(accepted, new Comparator() { + @Override + public int compare(Cookie o1, Cookie o2) { + return o2.path().length() - o1.path().length(); + } + }); + + return accepted; } - return false; - } + public boolean contains(HttpUrl url, String name) { + for (Cookie cookie : getCookies(url)) { + if (ObjectUtils.equal(cookie.name(), name)) { + return true; + } + } + return false; + } + + /** + * Remove all cookies in this {@code CookieRepository}. + */ + public synchronized void clear() { + map.clear(); + db.clear(); + } + + public synchronized void close() { + db.close(); + } + + @Override + public void saveFromResponse(HttpUrl httpUrl, List list) { + for (Cookie cookie : list) { + addCookie(cookie); + } + } + + @Override + public List loadForRequest(HttpUrl httpUrl) { + return getCookies(httpUrl); + } } diff --git a/app/src/main/java/com/hippo/network/CookieSet.java b/app/src/main/java/com/hippo/network/CookieSet.java index 0f4a1c9e7..ee43f055c 100644 --- a/app/src/main/java/com/hippo/network/CookieSet.java +++ b/app/src/main/java/com/hippo/network/CookieSet.java @@ -22,83 +22,85 @@ package com.hippo.network; import com.hippo.util.HashCodeUtils; import com.hippo.yorozuya.ObjectUtils; + import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; + import okhttp3.Cookie; import okhttp3.HttpUrl; class CookieSet { - private Map map = new HashMap<>(); + private Map map = new HashMap<>(); - /** - * Adds a cookie to this {@code CookieSet}. - * Returns a previous cookie with - * the same name, domain and path or {@code null}. - */ - public Cookie add(Cookie cookie) { - return map.put(new Key(cookie), cookie); - } - - /** - * Removes a cookie with the same name, - * domain and path as the cookie. - * Returns the removed cookie or {@code null}. - */ - public Cookie remove(Cookie cookie) { - return map.remove(new Key(cookie)); - } - - /** - * Get cookies for the url. Fill {@code accepted} and {@code expired}. - */ - public void get(HttpUrl url, List accepted, List expired) { - long now = System.currentTimeMillis(); - Iterator> iterator = map.entrySet().iterator(); - while (iterator.hasNext()) { - Cookie cookie = iterator.next().getValue(); - if (cookie.expiresAt() <= now) { - iterator.remove(); - expired.add(cookie); - } else if (cookie.matches(url)) { - accepted.add(cookie); - } - } - } - - static class Key { - - private String name; - private String domain; - private String path; - - public Key(Cookie cookie) { - this.name = cookie.name(); - this.domain = cookie.domain(); - this.path = cookie.path(); + /** + * Adds a cookie to this {@code CookieSet}. + * Returns a previous cookie with + * the same name, domain and path or {@code null}. + */ + public Cookie add(Cookie cookie) { + return map.put(new Key(cookie), cookie); } - @Override - public boolean equals(Object obj) { - if (obj == this) { - return true; - } - - if (obj instanceof Key) { - Key key = (Key) obj; - return ObjectUtils.equal(key.name, this.name) && - ObjectUtils.equal(key.domain, this.domain) && - ObjectUtils.equal(key.path, this.path); - } - - return false; + /** + * Removes a cookie with the same name, + * domain and path as the cookie. + * Returns the removed cookie or {@code null}. + */ + public Cookie remove(Cookie cookie) { + return map.remove(new Key(cookie)); } - @Override - public int hashCode() { - return HashCodeUtils.hashCode(name, domain, path); + /** + * Get cookies for the url. Fill {@code accepted} and {@code expired}. + */ + public void get(HttpUrl url, List accepted, List expired) { + long now = System.currentTimeMillis(); + Iterator> iterator = map.entrySet().iterator(); + while (iterator.hasNext()) { + Cookie cookie = iterator.next().getValue(); + if (cookie.expiresAt() <= now) { + iterator.remove(); + expired.add(cookie); + } else if (cookie.matches(url)) { + accepted.add(cookie); + } + } + } + + static class Key { + + private String name; + private String domain; + private String path; + + public Key(Cookie cookie) { + this.name = cookie.name(); + this.domain = cookie.domain(); + this.path = cookie.path(); + } + + @Override + public boolean equals(Object obj) { + if (obj == this) { + return true; + } + + if (obj instanceof Key) { + Key key = (Key) obj; + return ObjectUtils.equal(key.name, this.name) && + ObjectUtils.equal(key.domain, this.domain) && + ObjectUtils.equal(key.path, this.path); + } + + return false; + } + + @Override + public int hashCode() { + return HashCodeUtils.hashCode(name, domain, path); + } } - } } diff --git a/app/src/main/java/com/hippo/network/InetValidator.java b/app/src/main/java/com/hippo/network/InetValidator.java index 540f3a105..6403369d3 100644 --- a/app/src/main/java/com/hippo/network/InetValidator.java +++ b/app/src/main/java/com/hippo/network/InetValidator.java @@ -26,6 +26,9 @@ public final class InetValidator { private static final Pattern IPV4_PATTERN = Pattern.compile(IPV4_REGEX); + private InetValidator() { + } + public static boolean isValidInet4Address(String inet4Address) { if (null == inet4Address) { return false; @@ -45,7 +48,7 @@ public final class InetValidator { int iIpSegment; try { iIpSegment = Integer.parseInt(ipSegment); - } catch(NumberFormatException e) { + } catch (NumberFormatException e) { return false; } if (iIpSegment > 255) { @@ -61,6 +64,4 @@ public final class InetValidator { public static boolean isValidInetPort(int inetPort) { return inetPort >= 0 && inetPort <= 65535; } - - private InetValidator() {} } diff --git a/app/src/main/java/com/hippo/network/Network.java b/app/src/main/java/com/hippo/network/Network.java index b00c10d95..5367c93b5 100644 --- a/app/src/main/java/com/hippo/network/Network.java +++ b/app/src/main/java/com/hippo/network/Network.java @@ -21,7 +21,8 @@ import android.net.ConnectivityManager; import android.net.NetworkInfo; public final class Network { - private Network() {} + private Network() { + } public static int getActiveNetworkType(Context context) { ConnectivityManager cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); diff --git a/app/src/main/java/com/hippo/network/StatusCodeException.java b/app/src/main/java/com/hippo/network/StatusCodeException.java index d495fcb40..f371ff7b2 100644 --- a/app/src/main/java/com/hippo/network/StatusCodeException.java +++ b/app/src/main/java/com/hippo/network/StatusCodeException.java @@ -30,6 +30,16 @@ public class StatusCodeException extends Exception { private final int mResponseCode; private final String mMessage; + public StatusCodeException(int responseCode) { + mResponseCode = responseCode; + mMessage = ERROR_MESSAGE_ARRAY.get(responseCode, DEFAULT_ERROR_MESSAGE); + } + + public StatusCodeException(int responseCode, String message) { + mResponseCode = responseCode; + mMessage = message; + } + public static void initialize(Context context) { Resources resources = context.getApplicationContext().getResources(); @@ -60,16 +70,6 @@ public class StatusCodeException extends Exception { ERROR_MESSAGE_ARRAY.append(505, resources.getString(R.string.error_status_code_505)); } - public StatusCodeException(int responseCode) { - mResponseCode = responseCode; - mMessage = ERROR_MESSAGE_ARRAY.get(responseCode, DEFAULT_ERROR_MESSAGE); - } - - public StatusCodeException(int responseCode, String message) { - mResponseCode = responseCode; - mMessage = message; - } - public int getResponseCode() { return mResponseCode; } diff --git a/app/src/main/java/com/hippo/preference/ActivityPreference.java b/app/src/main/java/com/hippo/preference/ActivityPreference.java index 1fef14680..81e4ab2e1 100644 --- a/app/src/main/java/com/hippo/preference/ActivityPreference.java +++ b/app/src/main/java/com/hippo/preference/ActivityPreference.java @@ -19,10 +19,12 @@ package com.hippo.preference; import android.content.Context; import android.content.Intent; import android.content.res.TypedArray; -import android.preference.Preference; import android.util.AttributeSet; import android.util.Log; import android.widget.Toast; + +import androidx.preference.Preference; + import com.hippo.ehviewer.R; import com.hippo.util.ExceptionUtils; diff --git a/app/src/main/java/com/hippo/preference/DialogPreference.java b/app/src/main/java/com/hippo/preference/DialogPreference.java index 98da46141..aa85430ba 100644 --- a/app/src/main/java/com/hippo/preference/DialogPreference.java +++ b/app/src/main/java/com/hippo/preference/DialogPreference.java @@ -25,17 +25,18 @@ import android.graphics.drawable.Drawable; import android.os.Bundle; import android.os.Parcel; import android.os.Parcelable; -import android.preference.Preference; -import android.preference.PreferenceManager; import android.util.AttributeSet; import android.view.LayoutInflater; import android.view.View; import android.view.Window; import android.view.WindowManager; + import androidx.annotation.DrawableRes; import androidx.annotation.StringRes; import androidx.appcompat.app.AlertDialog; import androidx.core.content.ContextCompat; +import androidx.preference.Preference; + import com.hippo.ehviewer.R; /** @@ -44,8 +45,7 @@ import com.hippo.ehviewer.R; * actual preference controls. */ public abstract class DialogPreference extends Preference implements - DialogInterface.OnClickListener, DialogInterface.OnDismissListener, - PreferenceManager.OnActivityDestroyListener { + DialogInterface.OnClickListener, DialogInterface.OnDismissListener { private AlertDialog.Builder mBuilder; @@ -55,10 +55,14 @@ public abstract class DialogPreference extends Preference implements private CharSequence mNegativeButtonText; private int mDialogLayoutResId; - /** The dialog, if it is showing. */ + /** + * The dialog, if it is showing. + */ private AlertDialog mDialog; - /** Which button was clicked. */ + /** + * Which button was clicked. + */ private int mWhichButtonClicked; public DialogPreference(Context context) { @@ -91,6 +95,15 @@ public abstract class DialogPreference extends Preference implements a.recycle(); } + /** + * Returns the title to be shown on subsequent dialogs. + * + * @return The title. + */ + public CharSequence getDialogTitle() { + return mDialogTitle; + } + /** * Sets the title of the dialog. This will be shown on subsequent dialogs. * @@ -101,19 +114,20 @@ public abstract class DialogPreference extends Preference implements } /** - * @see #setDialogTitle(CharSequence) * @param dialogTitleResId The dialog title as a resource. + * @see #setDialogTitle(CharSequence) */ public void setDialogTitle(int dialogTitleResId) { setDialogTitle(getContext().getString(dialogTitleResId)); } /** - * Returns the title to be shown on subsequent dialogs. - * @return The title. + * Returns the icon to be shown on subsequent dialogs. + * + * @return The icon, as a {@link Drawable}. */ - public CharSequence getDialogTitle() { - return mDialogTitle; + public Drawable getDialogIcon() { + return mDialogIcon; } /** @@ -136,11 +150,13 @@ public abstract class DialogPreference extends Preference implements } /** - * Returns the icon to be shown on subsequent dialogs. - * @return The icon, as a {@link Drawable}. + * Returns the text of the positive button to be shown on subsequent + * dialogs. + * + * @return The text of the positive button. */ - public Drawable getDialogIcon() { - return mDialogIcon; + public CharSequence getPositiveButtonText() { + return mPositiveButtonText; } /** @@ -154,41 +170,13 @@ public abstract class DialogPreference extends Preference implements } /** - * @see #setPositiveButtonText(CharSequence) * @param positiveButtonTextResId The positive button text as a resource. + * @see #setPositiveButtonText(CharSequence) */ public void setPositiveButtonText(@StringRes int positiveButtonTextResId) { setPositiveButtonText(getContext().getString(positiveButtonTextResId)); } - /** - * Returns the text of the positive button to be shown on subsequent - * dialogs. - * - * @return The text of the positive button. - */ - public CharSequence getPositiveButtonText() { - return mPositiveButtonText; - } - - /** - * Sets the text of the negative button of the dialog. This will be shown on - * subsequent dialogs. - * - * @param negativeButtonText The text of the negative button. - */ - public void setNegativeButtonText(CharSequence negativeButtonText) { - mNegativeButtonText = negativeButtonText; - } - - /** - * @see #setNegativeButtonText(CharSequence) - * @param negativeButtonTextResId The negative button text as a resource. - */ - public void setNegativeButtonText(@StringRes int negativeButtonTextResId) { - setNegativeButtonText(getContext().getString(negativeButtonTextResId)); - } - /** * Returns the text of the negative button to be shown on subsequent * dialogs. @@ -200,13 +188,21 @@ public abstract class DialogPreference extends Preference implements } /** - * Sets the layout resource that is inflated as the {@link View} to be shown - * as the content View of subsequent dialogs. + * Sets the text of the negative button of the dialog. This will be shown on + * subsequent dialogs. * - * @param dialogLayoutResId The layout resource ID to be inflated. + * @param negativeButtonText The text of the negative button. */ - public void setDialogLayoutResource(int dialogLayoutResId) { - mDialogLayoutResId = dialogLayoutResId; + public void setNegativeButtonText(CharSequence negativeButtonText) { + mNegativeButtonText = negativeButtonText; + } + + /** + * @param negativeButtonTextResId The negative button text as a resource. + * @see #setNegativeButtonText(CharSequence) + */ + public void setNegativeButtonText(@StringRes int negativeButtonTextResId) { + setNegativeButtonText(getContext().getString(negativeButtonTextResId)); } /** @@ -219,6 +215,16 @@ public abstract class DialogPreference extends Preference implements return mDialogLayoutResId; } + /** + * Sets the layout resource that is inflated as the {@link View} to be shown + * as the content View of subsequent dialogs. + * + * @param dialogLayoutResId The layout resource ID to be inflated. + */ + public void setDialogLayoutResource(int dialogLayoutResId) { + mDialogLayoutResId = dialogLayoutResId; + } + /** * Prepares the dialog builder to be shown when the preference is clicked. * Use this to set custom properties on the dialog. @@ -262,7 +268,7 @@ public abstract class DialogPreference extends Preference implements onPrepareDialogBuilder(mBuilder); - PreferenceUtils.registerOnActivityDestroyListener(this, this); + //PreferenceUtils.registerOnActivityDestroyListener(this, this); // Create the dialog final AlertDialog dialog = mDialog = mBuilder.create(); @@ -317,9 +323,11 @@ public abstract class DialogPreference extends Preference implements * * @param view The content View of the dialog, if it is custom. */ - protected void onBindDialogView(View view) {} + protected void onBindDialogView(View view) { + } - protected void onDialogCreated(AlertDialog dialog) {} + protected void onDialogCreated(AlertDialog dialog) { + } @Override public void onClick(DialogInterface dialog, int which) { @@ -328,8 +336,6 @@ public abstract class DialogPreference extends Preference implements @Override public void onDismiss(DialogInterface dialog) { - PreferenceUtils.unregisterOnActivityDestroyListener(this, this); - mDialog = null; onDialogClosed(mWhichButtonClicked == DialogInterface.BUTTON_POSITIVE); } @@ -339,7 +345,7 @@ public abstract class DialogPreference extends Preference implements * the {@link SharedPreferences}. * * @param positiveResult Whether the positive button was clicked (true), or - * the negative button was clicked or the dialog was canceled (false). + * the negative button was clicked or the dialog was canceled (false). */ protected void onDialogClosed(boolean positiveResult) { } @@ -353,15 +359,6 @@ public abstract class DialogPreference extends Preference implements return mDialog; } - @Override - public void onActivityDestroy() { - if (mDialog == null || !mDialog.isShowing()) { - return; - } - - mDialog.dismiss(); - } - @Override protected Parcelable onSaveInstanceState() { final Parcelable superState = super.onSaveInstanceState(); @@ -391,26 +388,6 @@ public abstract class DialogPreference extends Preference implements } private static class SavedState extends BaseSavedState { - boolean isDialogShowing; - Bundle dialogBundle; - - public SavedState(Parcel source) { - super(source); - isDialogShowing = source.readInt() == 1; - dialogBundle = source.readBundle(DialogPreference.class.getClassLoader()); - } - - @Override - public void writeToParcel(Parcel dest, int flags) { - super.writeToParcel(dest, flags); - dest.writeInt(isDialogShowing ? 1 : 0); - dest.writeBundle(dialogBundle); - } - - public SavedState(Parcelable superState) { - super(superState); - } - public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { @Override @@ -423,5 +400,24 @@ public abstract class DialogPreference extends Preference implements return new SavedState[size]; } }; + boolean isDialogShowing; + Bundle dialogBundle; + + public SavedState(Parcel source) { + super(source); + isDialogShowing = source.readInt() == 1; + dialogBundle = source.readBundle(DialogPreference.class.getClassLoader()); + } + + public SavedState(Parcelable superState) { + super(superState); + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + super.writeToParcel(dest, flags); + dest.writeInt(isDialogShowing ? 1 : 0); + dest.writeBundle(dialogBundle); + } } } diff --git a/app/src/main/java/com/hippo/preference/ListPreference.java b/app/src/main/java/com/hippo/preference/ListPreference.java index 0d8ef31b6..a74a1451a 100644 --- a/app/src/main/java/com/hippo/preference/ListPreference.java +++ b/app/src/main/java/com/hippo/preference/ListPreference.java @@ -23,8 +23,10 @@ import android.os.Parcel; import android.os.Parcelable; import android.text.TextUtils; import android.util.AttributeSet; + import androidx.annotation.ArrayRes; import androidx.appcompat.app.AlertDialog; + import com.hippo.ehviewer.R; public class ListPreference extends DialogPreference { @@ -67,6 +69,15 @@ public class ListPreference extends DialogPreference { a.recycle(); } + /** + * The list of entries to be shown in the list in subsequent dialogs. + * + * @return The list as an array. + */ + public CharSequence[] getEntries() { + return mEntries; + } + /** * Sets the human-readable entries to be shown in the list. This will be * shown in subsequent dialogs. @@ -82,20 +93,20 @@ public class ListPreference extends DialogPreference { } /** - * @see #setEntries(CharSequence[]) * @param entriesResId The entries array as a resource. + * @see #setEntries(CharSequence[]) */ public void setEntries(@ArrayRes int entriesResId) { setEntries(getContext().getResources().getTextArray(entriesResId)); } /** - * The list of entries to be shown in the list in subsequent dialogs. + * Returns the array of values to be saved for the preference. * - * @return The list as an array. + * @return The array of values. */ - public CharSequence[] getEntries() { - return mEntries; + public CharSequence[] getEntryValues() { + return mEntryValues; } /** @@ -110,41 +121,13 @@ public class ListPreference extends DialogPreference { } /** - * @see #setEntryValues(CharSequence[]) * @param entryValuesResId The entry values array as a resource. + * @see #setEntryValues(CharSequence[]) */ public void setEntryValues(@ArrayRes int entryValuesResId) { setEntryValues(getContext().getResources().getTextArray(entryValuesResId)); } - /** - * Returns the array of values to be saved for the preference. - * - * @return The array of values. - */ - public CharSequence[] getEntryValues() { - return mEntryValues; - } - - /** - * Sets the value of the key. This should be one of the entries in - * {@link #getEntryValues()}. - * - * @param value The value to set for the key. - */ - public void setValue(String value) { - // Always persist/notify the first time. - final boolean changed = !TextUtils.equals(mValue, value); - if (changed || !mValueSet) { - mValue = value; - mValueSet = true; - persistString(value); - if (changed) { - notifyChanged(); - } - } - } - /** * Returns the summary of this ListPreference. If the summary * has a {@linkplain java.lang.String#format String formatting} @@ -182,17 +165,6 @@ public class ListPreference extends DialogPreference { } } - /** - * Sets the value to the given index from the entry values. - * - * @param index The index of the value to set. - */ - public void setValueIndex(int index) { - if (mEntryValues != null) { - setValue(mEntryValues[index].toString()); - } - } - /** * Returns the value of the key. This should be one of the entries in * {@link #getEntryValues()}. @@ -203,6 +175,25 @@ public class ListPreference extends DialogPreference { return mValue; } + /** + * Sets the value of the key. This should be one of the entries in + * {@link #getEntryValues()}. + * + * @param value The value to set for the key. + */ + public void setValue(String value) { + // Always persist/notify the first time. + final boolean changed = !TextUtils.equals(mValue, value); + if (changed || !mValueSet) { + mValue = value; + mValueSet = true; + persistString(value); + if (changed) { + notifyChanged(); + } + } + } + /** * Returns the entry corresponding to the current value. * @@ -234,6 +225,17 @@ public class ListPreference extends DialogPreference { return findIndexOfValue(mValue); } + /** + * Sets the value to the given index from the entry values. + * + * @param index The index of the value to set. + */ + public void setValueIndex(int index) { + if (mEntryValues != null) { + setValue(mEntryValues[index].toString()); + } + } + @Override protected void onPrepareDialogBuilder(AlertDialog.Builder builder) { super.onPrepareDialogBuilder(builder); @@ -309,23 +311,6 @@ public class ListPreference extends DialogPreference { } private static class SavedState extends BaseSavedState { - String value; - - public SavedState(Parcel source) { - super(source); - value = source.readString(); - } - - @Override - public void writeToParcel(Parcel dest, int flags) { - super.writeToParcel(dest, flags); - dest.writeString(value); - } - - public SavedState(Parcelable superState) { - super(superState); - } - public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { @Override @@ -338,6 +323,22 @@ public class ListPreference extends DialogPreference { return new SavedState[size]; } }; + String value; + + public SavedState(Parcel source) { + super(source); + value = source.readString(); + } + + public SavedState(Parcelable superState) { + super(superState); + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + super.writeToParcel(dest, flags); + dest.writeString(value); + } } } diff --git a/app/src/main/java/com/hippo/preference/MessagePreference.java b/app/src/main/java/com/hippo/preference/MessagePreference.java index fcc241fde..caa54b494 100644 --- a/app/src/main/java/com/hippo/preference/MessagePreference.java +++ b/app/src/main/java/com/hippo/preference/MessagePreference.java @@ -22,7 +22,9 @@ import android.text.method.LinkMovementMethod; import android.util.AttributeSet; import android.view.View; import android.widget.TextView; + import androidx.appcompat.app.AlertDialog; + import com.hippo.ehviewer.R; import com.hippo.text.Html; diff --git a/app/src/main/java/com/hippo/preference/PreferenceUtils.java b/app/src/main/java/com/hippo/preference/PreferenceUtils.java deleted file mode 100644 index 8c359fdc7..000000000 --- a/app/src/main/java/com/hippo/preference/PreferenceUtils.java +++ /dev/null @@ -1,99 +0,0 @@ -/* - * Copyright 2016 Hippo Seven - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.hippo.preference; - -import android.preference.Preference; -import android.preference.PreferenceManager; - -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; - -public final class PreferenceUtils { - private PreferenceUtils() {} - - private static final Method mRegisterOnActivityDestroyListener; - private static final Method mUnregisterOnActivityDestroyListener; - - static { - Method method; - Class clazz = PreferenceManager.class; - - method = null; - try { - method = clazz.getDeclaredMethod("registerOnActivityDestroyListener", - PreferenceManager.OnActivityDestroyListener.class); - if (null != method) { - method.setAccessible(true); - } - } catch (NoSuchMethodException e) { - e.printStackTrace(); - } - mRegisterOnActivityDestroyListener = method; - - method = null; - try { - method = clazz.getDeclaredMethod("unregisterOnActivityDestroyListener", - PreferenceManager.OnActivityDestroyListener.class); - if (null != method) { - method.setAccessible(true); - } - } catch (NoSuchMethodException e) { - e.printStackTrace(); - } - mUnregisterOnActivityDestroyListener = method; - } - - @SuppressWarnings("TryWithIdenticalCatches") - public static void registerOnActivityDestroyListener(Preference preference, - PreferenceManager.OnActivityDestroyListener listener) { - if (null == mRegisterOnActivityDestroyListener || null == preference || null == listener) { - return; - } - PreferenceManager preferenceManager = preference.getPreferenceManager(); - if (null == preferenceManager) { - return; - } - - try { - mRegisterOnActivityDestroyListener.invoke(preferenceManager, listener); - } catch (IllegalAccessException e) { - e.printStackTrace(); - } catch (InvocationTargetException e) { - e.printStackTrace(); - } - } - - @SuppressWarnings("TryWithIdenticalCatches") - public static void unregisterOnActivityDestroyListener(Preference preference, - PreferenceManager.OnActivityDestroyListener listener) { - if (null == mUnregisterOnActivityDestroyListener || null == preference || null == listener) { - return; - } - PreferenceManager preferenceManager = preference.getPreferenceManager(); - if (null == preferenceManager) { - return; - } - - try { - mUnregisterOnActivityDestroyListener.invoke(preferenceManager, listener); - } catch (IllegalAccessException e) { - e.printStackTrace(); - } catch (InvocationTargetException e) { - e.printStackTrace(); - } - } -} diff --git a/app/src/main/java/com/hippo/preference/SeekBarPreference.java b/app/src/main/java/com/hippo/preference/SeekBarPreference.java index 4183518bf..b85ae6eb6 100644 --- a/app/src/main/java/com/hippo/preference/SeekBarPreference.java +++ b/app/src/main/java/com/hippo/preference/SeekBarPreference.java @@ -20,11 +20,12 @@ import android.content.Context; import android.content.res.TypedArray; import android.os.Parcel; import android.os.Parcelable; -import android.preference.Preference; import android.util.AttributeSet; -import android.view.View; import android.widget.SeekBar; +import androidx.preference.Preference; +import androidx.preference.PreferenceViewHolder; + import com.hippo.ehviewer.R; public class SeekBarPreference extends Preference implements SeekBar.OnSeekBarChangeListener { @@ -57,13 +58,13 @@ public class SeekBarPreference extends Preference implements SeekBar.OnSeekBarCh } @Override - protected void onBindView(View view) { - super.onBindView(view); - SeekBar seekBar = (SeekBar) view.findViewById(R.id.seekbar); + public void onBindViewHolder(PreferenceViewHolder holder) { + SeekBar seekBar = (SeekBar) holder.itemView.findViewById(R.id.seekbar); seekBar.setOnSeekBarChangeListener(this); seekBar.setMax(mMax); seekBar.setProgress(mProgress); seekBar.setEnabled(isEnabled()); + super.onBindViewHolder(holder); } @Override @@ -84,10 +85,6 @@ public class SeekBarPreference extends Preference implements SeekBar.OnSeekBarCh } } - public void setProgress(int progress) { - setProgress(progress, true); - } - private void setProgress(int progress, boolean notifyChanged) { if (progress > mMax) { progress = mMax; @@ -108,6 +105,10 @@ public class SeekBarPreference extends Preference implements SeekBar.OnSeekBarCh return mProgress; } + public void setProgress(int progress) { + setProgress(progress, true); + } + /** * Persist the seekBar's progress value if callChangeListener * returns true, otherwise set the seekBar's progress to the stored value @@ -188,30 +189,6 @@ public class SeekBarPreference extends Preference implements SeekBar.OnSeekBarCh * It is important to always call through to super methods. */ private static class SavedState extends BaseSavedState { - int progress; - int max; - - public SavedState(Parcel source) { - super(source); - - // Restore the click counter - progress = source.readInt(); - max = source.readInt(); - } - - @Override - public void writeToParcel(Parcel dest, int flags) { - super.writeToParcel(dest, flags); - - // Save the click counter - dest.writeInt(progress); - dest.writeInt(max); - } - - public SavedState(Parcelable superState) { - super(superState); - } - @SuppressWarnings("unused") public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { @@ -225,5 +202,28 @@ public class SeekBarPreference extends Preference implements SeekBar.OnSeekBarCh return new SavedState[size]; } }; + int progress; + int max; + + public SavedState(Parcel source) { + super(source); + + // Restore the click counter + progress = source.readInt(); + max = source.readInt(); + } + + public SavedState(Parcelable superState) { + super(superState); + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + super.writeToParcel(dest, flags); + + // Save the click counter + dest.writeInt(progress); + dest.writeInt(max); + } } } diff --git a/app/src/main/java/com/hippo/preference/SwitchPreference.java b/app/src/main/java/com/hippo/preference/SwitchPreference.java index 8d7ee8f92..f29244aed 100644 --- a/app/src/main/java/com/hippo/preference/SwitchPreference.java +++ b/app/src/main/java/com/hippo/preference/SwitchPreference.java @@ -23,9 +23,12 @@ import android.util.AttributeSet; import android.view.View; import android.widget.Checkable; import android.widget.CompoundButton; + import androidx.annotation.StringRes; import androidx.appcompat.widget.SwitchCompat; + import com.hippo.ehviewer.R; + import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; @@ -43,26 +46,11 @@ public class SwitchPreference extends TwoStatePreference { } } + private final Listener mListener = new Listener(); // Switch text for on and off states private CharSequence mSwitchOn; private CharSequence mSwitchOff; - private final Listener mListener = new Listener(); - - private class Listener implements CompoundButton.OnCheckedChangeListener { - @Override - public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { - if (!callChangeListener(isChecked)) { - // Listener didn't like it, change it back. - // CompoundButton will make sure we don't recurse. - buttonView.setChecked(!isChecked); - return; - } - - SwitchPreference.this.setChecked(isChecked); - } - } - public SwitchPreference(Context context) { super(context); init(context, null, 0, 0); @@ -122,6 +110,12 @@ public class SwitchPreference extends TwoStatePreference { } } + /** + * @return The text that will be displayed on the switch widget in the on state + */ + public CharSequence getSwitchTextOn() { + return mSwitchOn; + } /** * Set the text displayed on the switch widget in the on state. @@ -134,17 +128,6 @@ public class SwitchPreference extends TwoStatePreference { notifyChanged(); } - /** - * Set the text displayed on the switch widget in the off state. - * This should be a very short string; one word if possible. - * - * @param offText Text to display in the off state - */ - public void setSwitchTextOff(CharSequence offText) { - mSwitchOff = offText; - notifyChanged(); - } - /** * Set the text displayed on the switch widget in the on state. * This should be a very short string; one word if possible. @@ -155,6 +138,24 @@ public class SwitchPreference extends TwoStatePreference { setSwitchTextOn(getContext().getString(resId)); } + /** + * @return The text that will be displayed on the switch widget in the off state + */ + public CharSequence getSwitchTextOff() { + return mSwitchOff; + } + + /** + * Set the text displayed on the switch widget in the off state. + * This should be a very short string; one word if possible. + * + * @param offText Text to display in the off state + */ + public void setSwitchTextOff(CharSequence offText) { + mSwitchOff = offText; + notifyChanged(); + } + /** * Set the text displayed on the switch widget in the off state. * This should be a very short string; one word if possible. @@ -165,17 +166,17 @@ public class SwitchPreference extends TwoStatePreference { setSwitchTextOff(getContext().getString(resId)); } - /** - * @return The text that will be displayed on the switch widget in the on state - */ - public CharSequence getSwitchTextOn() { - return mSwitchOn; - } + private class Listener implements CompoundButton.OnCheckedChangeListener { + @Override + public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { + if (!callChangeListener(isChecked)) { + // Listener didn't like it, change it back. + // CompoundButton will make sure we don't recurse. + buttonView.setChecked(!isChecked); + return; + } - /** - * @return The text that will be displayed on the switch widget in the off state - */ - public CharSequence getSwitchTextOff() { - return mSwitchOff; + SwitchPreference.this.setChecked(isChecked); + } } } diff --git a/app/src/main/java/com/hippo/preference/UrlPreference.java b/app/src/main/java/com/hippo/preference/UrlPreference.java index b5bba3f42..de7f32f99 100644 --- a/app/src/main/java/com/hippo/preference/UrlPreference.java +++ b/app/src/main/java/com/hippo/preference/UrlPreference.java @@ -18,9 +18,10 @@ package com.hippo.preference; import android.content.Context; import android.content.res.TypedArray; -import android.preference.Preference; import android.util.AttributeSet; +import androidx.preference.Preference; + import com.hippo.ehviewer.R; import com.hippo.ehviewer.UrlOpener; diff --git a/app/src/main/java/com/hippo/reveal/ViewAnimationUtils.java b/app/src/main/java/com/hippo/reveal/ViewAnimationUtils.java index 118f5f8f1..ecd9a7d35 100644 --- a/app/src/main/java/com/hippo/reveal/ViewAnimationUtils.java +++ b/app/src/main/java/com/hippo/reveal/ViewAnimationUtils.java @@ -25,21 +25,21 @@ import android.view.View; import com.hippo.yorozuya.SimpleAnimatorListener; public final class ViewAnimationUtils { - private ViewAnimationUtils() {} - public static final boolean API_SUPPORT_CIRCULAR_REVEAL = Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP; // http://developer.android.com/guide/topics/graphics/hardware-accel.html#unsupported public static final boolean API_SUPPORT_CANVAS_CLIP_PATH = Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2; + private ViewAnimationUtils() { + } @TargetApi(Build.VERSION_CODES.LOLLIPOP) public static Animator createCircularReveal(View view, - int centerX, int centerY, float startRadius, float endRadius) { + int centerX, int centerY, float startRadius, float endRadius) { if (API_SUPPORT_CIRCULAR_REVEAL) { return android.view.ViewAnimationUtils.createCircularReveal( view, centerX, centerY, startRadius, endRadius); - } else if (view instanceof Reveal){ + } else if (view instanceof Reveal) { return createRevealAnimator((Reveal) view, centerX, centerY, startRadius, endRadius); } else { @@ -49,7 +49,7 @@ public final class ViewAnimationUtils { } private static Animator createRevealAnimator(Reveal reveal, int centerX, int centerY, - float startRadius, float endRadius) { + float startRadius, float endRadius) { ValueAnimator animator = ValueAnimator.ofFloat(startRadius, endRadius); animator.addUpdateListener(new RevealAnimatorUpdateListener(reveal, centerX, centerY)); animator.addListener(new RevealAnimatorListener(reveal)); diff --git a/app/src/main/java/com/hippo/scene/Announcer.java b/app/src/main/java/com/hippo/scene/Announcer.java index 953f83801..49e9c16d5 100644 --- a/app/src/main/java/com/hippo/scene/Announcer.java +++ b/app/src/main/java/com/hippo/scene/Announcer.java @@ -34,15 +34,15 @@ public final class Announcer { return clazz; } + public Bundle getArgs() { + return args; + } + public Announcer setArgs(Bundle args) { this.args = args; return this; } - public Bundle getArgs() { - return args; - } - public Announcer setTranHelper(TransitionHelper tranHelper) { this.tranHelper = tranHelper; return this; diff --git a/app/src/main/java/com/hippo/scene/SceneFragment.java b/app/src/main/java/com/hippo/scene/SceneFragment.java index 27468cf72..d5a796c40 100644 --- a/app/src/main/java/com/hippo/scene/SceneFragment.java +++ b/app/src/main/java/com/hippo/scene/SceneFragment.java @@ -18,14 +18,17 @@ package com.hippo.scene; import android.os.Bundle; import android.view.View; + import androidx.annotation.IntDef; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.fragment.app.Fragment; import androidx.fragment.app.FragmentActivity; + import com.hippo.android.resource.AttrResources; import com.hippo.ehviewer.R; import com.hippo.yorozuya.collect.IntList; + import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; @@ -33,26 +36,24 @@ import java.util.List; public class SceneFragment extends Fragment { - @IntDef({LAUNCH_MODE_STANDARD, LAUNCH_MODE_SINGLE_TOP, LAUNCH_MODE_SINGLE_TASK}) - @Retention(RetentionPolicy.SOURCE) - public @interface LaunchMode {} - public static final int LAUNCH_MODE_STANDARD = 0; public static final int LAUNCH_MODE_SINGLE_TOP = 1; public static final int LAUNCH_MODE_SINGLE_TASK = 2; - - /** Standard scene result: operation canceled. */ - public static final int RESULT_CANCELED = 0; - /** Standard scene result: operation succeeded. */ + /** + * Standard scene result: operation canceled. + */ + public static final int RESULT_CANCELED = 0; + /** + * Standard scene result: operation succeeded. + */ public static final int RESULT_OK = -1; - int resultCode = RESULT_CANCELED; Bundle result = null; - List mRequestSceneTagList = new ArrayList<>(0); IntList mRequestCodeList = new IntList(0); - public void onNewArguments(@NonNull Bundle args) {} + public void onNewArguments(@NonNull Bundle args) { + } public void startScene(Announcer announcer) { FragmentActivity activity = getActivity(); @@ -156,4 +157,9 @@ public class SceneFragment extends Fragment { this.resultCode = resultCode; this.result = result; } + + @IntDef({LAUNCH_MODE_STANDARD, LAUNCH_MODE_SINGLE_TOP, LAUNCH_MODE_SINGLE_TASK}) + @Retention(RetentionPolicy.SOURCE) + public @interface LaunchMode { + } } diff --git a/app/src/main/java/com/hippo/scene/StageActivity.java b/app/src/main/java/com/hippo/scene/StageActivity.java index 1bd2a4b80..ded724096 100644 --- a/app/src/main/java/com/hippo/scene/StageActivity.java +++ b/app/src/main/java/com/hippo/scene/StageActivity.java @@ -20,15 +20,18 @@ import android.content.Intent; import android.os.Bundle; import android.util.Log; import android.view.View; + import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.fragment.app.Fragment; import androidx.fragment.app.FragmentManager; import androidx.fragment.app.FragmentTransaction; + import com.hippo.ehviewer.R; import com.hippo.ehviewer.ui.EhActivity; import com.hippo.yorozuya.AssertUtils; import com.hippo.yorozuya.IntIdGenerator; + import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; @@ -39,43 +42,19 @@ import java.util.concurrent.atomic.AtomicInteger; public abstract class StageActivity extends EhActivity { - private static final String TAG = StageActivity.class.getSimpleName(); - public static final String ACTION_START_SCENE = "start_scene"; - public static final String KEY_SCENE_NAME = "stage_activity_scene_name"; public static final String KEY_SCENE_ARGS = "stage_activity_scene_args"; - + private static final String TAG = StageActivity.class.getSimpleName(); private static final String KEY_STAGE_ID = "stage_activity_stage_id"; private static final String KEY_SCENE_TAG_LIST = "stage_activity_scene_tag_list"; private static final String KEY_NEXT_ID = "stage_activity_next_id"; - + private static final Map, Integer> sLaunchModeMap = new HashMap<>(); private final ArrayList mSceneTagList = new ArrayList<>(); private final ArrayList mDelaySceneTagList = new ArrayList<>(); private final AtomicInteger mIdGenerator = new AtomicInteger(); - - private int mStageId = IntIdGenerator.INVALID_ID; - private final SceneViewComparator mSceneViewComparator = new SceneViewComparator(); - - private final class SceneViewComparator implements Comparator { - - private int getIndex(View view) { - Object o = view.getTag(R.id.fragment_tag); - if (o instanceof String) { - return mDelaySceneTagList.indexOf(o); - } else { - return -1; - } - } - - @Override - public int compare(View lhs, View rhs) { - return getIndex(lhs) - getIndex(rhs); - } - } - - private static final Map, Integer> sLaunchModeMap = new HashMap<>(); + private int mStageId = IntIdGenerator.INVALID_ID; public static void registerLaunchMode(Class clazz, @SceneFragment.LaunchMode int launchMode) { if (launchMode != SceneFragment.LAUNCH_MODE_STANDARD && @@ -147,7 +126,8 @@ public abstract class StageActivity extends EhActivity { * Can't recognize intent in first time {@code onCreate} and {@code onNewIntent}, * null included. */ - protected void onUnrecognizedIntent(@Nullable Intent intent) {} + protected void onUnrecognizedIntent(@Nullable Intent intent) { + } /** * Call {@code setContentView} here. Do NOT call {@code startScene} here @@ -333,7 +313,7 @@ public abstract class StageActivity extends EhActivity { String tag = mSceneTagList.get(mSceneTagList.size() - 1); Fragment fragment = fragmentManager.findFragmentByTag(tag); if (fragment != null) { - AssertUtils.assertTrue(SceneFragment.class.isInstance(fragment)); + AssertUtils.assertTrue(fragment instanceof SceneFragment); currentScene = (SceneFragment) fragment; } } @@ -613,4 +593,21 @@ public abstract class StageActivity extends EhActivity { } return fragment.getClass(); } + + private final class SceneViewComparator implements Comparator { + + private int getIndex(View view) { + Object o = view.getTag(R.id.fragment_tag); + if (o instanceof String) { + return mDelaySceneTagList.indexOf(o); + } else { + return -1; + } + } + + @Override + public int compare(View lhs, View rhs) { + return getIndex(lhs) - getIndex(rhs); + } + } } diff --git a/app/src/main/java/com/hippo/scene/TransitionHelper.java b/app/src/main/java/com/hippo/scene/TransitionHelper.java index e7a384e70..6852526bf 100644 --- a/app/src/main/java/com/hippo/scene/TransitionHelper.java +++ b/app/src/main/java/com/hippo/scene/TransitionHelper.java @@ -17,6 +17,7 @@ package com.hippo.scene; import android.content.Context; + import androidx.fragment.app.Fragment; import androidx.fragment.app.FragmentTransaction; diff --git a/app/src/main/java/com/hippo/text/Html.java b/app/src/main/java/com/hippo/text/Html.java index 8811b79e5..0c26b6df0 100644 --- a/app/src/main/java/com/hippo/text/Html.java +++ b/app/src/main/java/com/hippo/text/Html.java @@ -44,12 +44,10 @@ import android.text.style.TextAppearanceSpan; import android.text.style.TypefaceSpan; import android.text.style.URLSpan; import android.text.style.UnderlineSpan; + import androidx.annotation.ColorInt; import androidx.annotation.NonNull; -import java.io.IOException; -import java.io.StringReader; -import java.util.HashMap; -import java.util.Locale; + import org.ccil.cowan.tagsoup.HTMLSchema; import org.ccil.cowan.tagsoup.Parser; import org.xml.sax.Attributes; @@ -59,6 +57,11 @@ import org.xml.sax.Locator; import org.xml.sax.SAXException; import org.xml.sax.XMLReader; +import java.io.IOException; +import java.io.StringReader; +import java.util.HashMap; +import java.util.Locale; + /** * This class processes HTML strings into displayable styled text. * Not all HTML tags are supported. @@ -67,41 +70,13 @@ public class Html { static Resources sResources; + private Html() { + } + public static void initialize(Context context) { sResources = context.getResources(); } - /** - * Retrieves images for HTML <img> tags. - */ - public interface ImageGetter { - /** - * This method is called when the HTML parser encounters an - * <img> tag. The source argument is the - * string from the "src" attribute; the return value should be - * a Drawable representation of the image or null - * for a generic replacement image. Make sure you call - * setBounds() on your Drawable if it doesn't already have - * its bounds set. - */ - Drawable getDrawable(String source); - } - - /** - * Is notified when HTML tags are encountered that the parser does - * not know how to interpret. - */ - public interface TagHandler { - /** - * This method will be called whenn the HTML parser encounters - * a tag that it does not know how to interpret. - */ - void handleTag(boolean opening, String tag, - Editable output, XMLReader xmlReader); - } - - private Html() { } - /** * Returns displayable styled text from the provided HTML string. * Any <img> tags in the HTML will display as a generic @@ -114,15 +89,6 @@ public class Html { return fromHtml(source, null, null); } - /** - * Lazy initialization holder for HTML parser. This class will - * a) be preloaded by the zygote, or b) not loaded until absolutely - * necessary. - */ - private static class HtmlParser { - private static final HTMLSchema schema = new HTMLSchema(); - } - /** * Returns displayable styled text from the provided HTML string. * Any <img> tags in the HTML will use the specified ImageGetter @@ -133,7 +99,7 @@ public class Html { *

This uses TagSoup to handle real HTML, including all of the brokenness found in the wild. */ public static SpannableStringBuilder fromHtml(String source, ImageGetter imageGetter, - TagHandler tagHandler) { + TagHandler tagHandler) { Parser parser = new Parser(); try { parser.setProperty(Parser.schemaProperty, HtmlParser.schema); @@ -184,7 +150,7 @@ public class Html { String elements = " "; boolean needDiv = false; - for(int j = 0; j < style.length; j++) { + for (int j = 0; j < style.length; j++) { if (style[j] instanceof AlignmentSpan) { Layout.Alignment align = ((AlignmentSpan) style[j]).getAlignment(); @@ -211,7 +177,7 @@ public class Html { } private static void withinDiv(StringBuilder out, Spanned text, - int start, int end) { + int start, int end) { int next; for (int i = start; i < end; i = next) { next = text.nextSpanTransition(i, end, QuoteSpan.class); @@ -248,7 +214,7 @@ public class Html { } private static void withinBlockquote(StringBuilder out, Spanned text, - int start, int end) { + int start, int end) { out.append(getOpenParaTagWithDirection(text, start, end)); int next; @@ -277,8 +243,8 @@ public class Html { /* Returns true if the caller should close and reopen the paragraph. */ private static boolean withinParagraph(StringBuilder out, Spanned text, - int start, int end, int nl, - boolean last) { + int start, int end, int nl, + boolean last) { int next; for (int i = start; i < end; i = next) { next = text.nextSpanTransition(i, end, CharacterStyle.class); @@ -401,7 +367,7 @@ public class Html { } private static void withinStyle(StringBuilder out, CharSequence text, - int start, int end) { + int start, int end) { for (int i = start; i < end; i++) { char c = text.charAt(i); @@ -434,6 +400,44 @@ public class Html { } } } + + /** + * Retrieves images for HTML <img> tags. + */ + public interface ImageGetter { + /** + * This method is called when the HTML parser encounters an + * <img> tag. The source argument is the + * string from the "src" attribute; the return value should be + * a Drawable representation of the image or null + * for a generic replacement image. Make sure you call + * setBounds() on your Drawable if it doesn't already have + * its bounds set. + */ + Drawable getDrawable(String source); + } + + /** + * Is notified when HTML tags are encountered that the parser does + * not know how to interpret. + */ + public interface TagHandler { + /** + * This method will be called whenn the HTML parser encounters + * a tag that it does not know how to interpret. + */ + void handleTag(boolean opening, String tag, + Editable output, XMLReader xmlReader); + } + + /** + * Lazy initialization holder for HTML parser. This class will + * a) be preloaded by the zygote, or b) not loaded until absolutely + * necessary. + */ + private static class HtmlParser { + private static final HTMLSchema schema = new HTMLSchema(); + } } class HtmlToSpannedConverter implements ContentHandler { @@ -441,6 +445,151 @@ class HtmlToSpannedConverter implements ContentHandler { private static final float[] HEADER_SIZES = { 1.5f, 1.4f, 1.3f, 1.2f, 1.1f, 1f, }; + private static final HashMap sColorNameMap; + + static { + sColorNameMap = new HashMap<>(); + sColorNameMap.put("aliceblue", 0xFFF0F8FF); + sColorNameMap.put("antiquewhite", 0xFFFAEBD7); + sColorNameMap.put("aqua", 0xFFFFFF); + sColorNameMap.put("aquamarine", 0xFF7FFFD4); + sColorNameMap.put("azure", 0xFFF0FFFF); + sColorNameMap.put("beige", 0xFFF5F5DC); + sColorNameMap.put("bisque", 0xFFFFE4C4); + sColorNameMap.put("black", 0xFF0); + sColorNameMap.put("blanchedalmond", 0xFFFFEBCD); + sColorNameMap.put("blue", 0xFFFF); + sColorNameMap.put("blueviolet", 0xFF8A2BE2); + sColorNameMap.put("brown", 0xFFA52A2A); + sColorNameMap.put("burlywood", 0xFFDEB887); + sColorNameMap.put("cadetblue", 0xFF5F9EA0); + sColorNameMap.put("chartreuse", 0xFF7FFF00); + sColorNameMap.put("chocolate", 0xFFD2691E); + sColorNameMap.put("coral", 0xFFFF7F50); + sColorNameMap.put("cornflowerblue", 0xFF6495ED); + sColorNameMap.put("cornsilk", 0xFFFFF8DC); + sColorNameMap.put("crimson", 0xFFDC143C); + sColorNameMap.put("cyan", 0xFFFFFF); + sColorNameMap.put("darkblue", 0xFF8B); + sColorNameMap.put("darkcyan", 0xFF8B8B); + sColorNameMap.put("darkgoldenrod", 0xFFB8860B); + sColorNameMap.put("darkgray", 0xFFA9A9A9); + sColorNameMap.put("darkgreen", 0xFF6400); + sColorNameMap.put("darkkhaki", 0xFFBDB76B); + sColorNameMap.put("darkmagenta", 0xFF8B008B); + sColorNameMap.put("darkolivegreen", 0xFF556B2F); + sColorNameMap.put("darkorange", 0xFFFF8C00); + sColorNameMap.put("darkorchid", 0xFF9932CC); + sColorNameMap.put("darkred", 0xFF8B0000); + sColorNameMap.put("darksalmon", 0xFFE9967A); + sColorNameMap.put("darkseagreen", 0xFF8FBC8F); + sColorNameMap.put("darkslateblue", 0xFF483D8B); + sColorNameMap.put("darkslategray", 0xFF2F4F4F); + sColorNameMap.put("darkturquoise", 0xFFCED1); + sColorNameMap.put("darkviolet", 0xFF9400D3); + sColorNameMap.put("deeppink", 0xFFFF1493); + sColorNameMap.put("deepskyblue", 0xFFBFFF); + sColorNameMap.put("dimgray", 0xFF696969); + sColorNameMap.put("dodgerblue", 0xFF1E90FF); + sColorNameMap.put("firebrick", 0xFFB22222); + sColorNameMap.put("floralwhite", 0xFFFFFAF0); + sColorNameMap.put("forestgreen", 0xFF228B22); + sColorNameMap.put("fuchsia", 0xFFFF00FF); + sColorNameMap.put("gainsboro", 0xFFDCDCDC); + sColorNameMap.put("ghostwhite", 0xFFF8F8FF); + sColorNameMap.put("gold", 0xFFFFD700); + sColorNameMap.put("goldenrod", 0xFFDAA520); + sColorNameMap.put("gray", 0xFF808080); + sColorNameMap.put("green", 0xFF8000); + sColorNameMap.put("greenyellow", 0xFFADFF2F); + sColorNameMap.put("honeydew", 0xFFF0FFF0); + sColorNameMap.put("hotpink", 0xFFFF69B4); + sColorNameMap.put("indianred", 0xFFCD5C5C); + sColorNameMap.put("indigo", 0xFF4B0082); + sColorNameMap.put("ivory", 0xFFFFFFF0); + sColorNameMap.put("khaki", 0xFFF0E68C); + sColorNameMap.put("lavender", 0xFFE6E6FA); + sColorNameMap.put("lavenderblush", 0xFFFFF0F5); + sColorNameMap.put("lawngreen", 0xFF7CFC00); + sColorNameMap.put("lemonchiffon", 0xFFFFFACD); + sColorNameMap.put("lightblue", 0xFFADD8E6); + sColorNameMap.put("lightcoral", 0xFFF08080); + sColorNameMap.put("lightcyan", 0xFFE0FFFF); + sColorNameMap.put("lightgoldenrodyellow", 0xFFFAFAD2); + sColorNameMap.put("lightgreen", 0xFF90EE90); + sColorNameMap.put("lightgrey", 0xFFD3D3D3); + sColorNameMap.put("lightpink", 0xFFFFB6C1); + sColorNameMap.put("lightsalmon", 0xFFFFA07A); + sColorNameMap.put("lightseagreen", 0xFF20B2AA); + sColorNameMap.put("lightskyblue", 0xFF87CEFA); + sColorNameMap.put("lightslategray", 0xFF778899); + sColorNameMap.put("lightsteelblue", 0xFFB0C4DE); + sColorNameMap.put("lightyellow", 0xFFFFFFE0); + sColorNameMap.put("lime", 0xFFFF00); + sColorNameMap.put("limegreen", 0xFF32CD32); + sColorNameMap.put("linen", 0xFFFAF0E6); + sColorNameMap.put("magenta", 0xFFFF00FF); + sColorNameMap.put("maroon", 0xFF800000); + sColorNameMap.put("mediumaquamarine", 0xFF66CDAA); + sColorNameMap.put("mediumblue", 0xFFCD); + sColorNameMap.put("mediumorchid", 0xFFBA55D3); + sColorNameMap.put("mediumpurple", 0xFF9370DB); + sColorNameMap.put("mediumseagreen", 0xFF3CB371); + sColorNameMap.put("mediumslateblue", 0xFF7B68EE); + sColorNameMap.put("mediumspringgreen", 0xFFFA9A); + sColorNameMap.put("mediumturquoise", 0xFF48D1CC); + sColorNameMap.put("mediumvioletred", 0xFFC71585); + sColorNameMap.put("midnightblue", 0xFF191970); + sColorNameMap.put("mintcream", 0xFFF5FFFA); + sColorNameMap.put("mistyrose", 0xFFFFE4E1); + sColorNameMap.put("moccasin", 0xFFFFE4B5); + sColorNameMap.put("navajowhite", 0xFFFFDEAD); + sColorNameMap.put("navy", 0xFF80); + sColorNameMap.put("oldlace", 0xFFFDF5E6); + sColorNameMap.put("olive", 0xFF808000); + sColorNameMap.put("olivedrab", 0xFF6B8E23); + sColorNameMap.put("orange", 0xFFFFA500); + sColorNameMap.put("orangered", 0xFFFF4500); + sColorNameMap.put("orchid", 0xFFDA70D6); + sColorNameMap.put("palegoldenrod", 0xFFEEE8AA); + sColorNameMap.put("palegreen", 0xFF98FB98); + sColorNameMap.put("paleturquoise", 0xFFAFEEEE); + sColorNameMap.put("palevioletred", 0xFFDB7093); + sColorNameMap.put("papayawhip", 0xFFFFEFD5); + sColorNameMap.put("peachpuff", 0xFFFFDAB9); + sColorNameMap.put("peru", 0xFFCD853F); + sColorNameMap.put("pink", 0xFFFFC0CB); + sColorNameMap.put("plum", 0xFFDDA0DD); + sColorNameMap.put("powderblue", 0xFFB0E0E6); + sColorNameMap.put("purple", 0xFF800080); + sColorNameMap.put("red", 0xFFFF0000); + sColorNameMap.put("rosybrown", 0xFFBC8F8F); + sColorNameMap.put("royalblue", 0xFF4169E1); + sColorNameMap.put("saddlebrown", 0xFF8B4513); + sColorNameMap.put("salmon", 0xFFFA8072); + sColorNameMap.put("sandybrown", 0xFFF4A460); + sColorNameMap.put("seagreen", 0xFF2E8B57); + sColorNameMap.put("seashell", 0xFFFFF5EE); + sColorNameMap.put("sienna", 0xFFA0522D); + sColorNameMap.put("silver", 0xFFC0C0C0); + sColorNameMap.put("skyblue", 0xFF87CEEB); + sColorNameMap.put("slateblue", 0xFF6A5ACD); + sColorNameMap.put("slategray", 0xFF708090); + sColorNameMap.put("snow", 0xFFFFFAFA); + sColorNameMap.put("springgreen", 0xFFFF7F); + sColorNameMap.put("steelblue", 0xFF4682B4); + sColorNameMap.put("tan", 0xFFD2B48C); + sColorNameMap.put("teal", 0xFF8080); + sColorNameMap.put("thistle", 0xFFD8BFD8); + sColorNameMap.put("tomato", 0xFFFF6347); + sColorNameMap.put("turquoise", 0xFF40E0D0); + sColorNameMap.put("violet", 0xFFEE82EE); + sColorNameMap.put("wheat", 0xFFF5DEB3); + sColorNameMap.put("white", 0xFFFFFFFF); + sColorNameMap.put("whitesmoke", 0xFFF5F5F5); + sColorNameMap.put("yellow", 0xFFFFFF00); + sColorNameMap.put("yellowgreen", 0xFF9ACD32); + } private String mSource; private XMLReader mReader; @@ -458,6 +607,216 @@ class HtmlToSpannedConverter implements ContentHandler { mReader = parser; } + private static void handleP(SpannableStringBuilder text) { + int len = text.length(); + + if (len >= 1 && text.charAt(len - 1) == '\n') { + if (len >= 2 && text.charAt(len - 2) == '\n') { + return; + } + + text.append("\n"); + return; + } + + if (len != 0) { + text.append("\n\n"); + } + } + + private static void handleBr(SpannableStringBuilder text) { + text.append("\n"); + } + + private static Object getLast(Spanned text, Class kind) { + /* + * This knows that the last returned object from getSpans() + * will be the most recently added. + */ + Object[] objs = text.getSpans(0, text.length(), kind); + + if (objs.length == 0) { + return null; + } else { + return objs[objs.length - 1]; + } + } + + private static void start(SpannableStringBuilder text, Object mark) { + int len = text.length(); + text.setSpan(mark, len, len, Spannable.SPAN_MARK_MARK); + } + + private static void end(SpannableStringBuilder text, Class kind, + Object repl) { + int len = text.length(); + Object obj = getLast(text, kind); + int where = text.getSpanStart(obj); + + text.removeSpan(obj); + + if (where != len) { + text.setSpan(repl, where, len, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); + } + } + + private static void startImg(SpannableStringBuilder text, + Attributes attributes, Html.ImageGetter img) { + String src = attributes.getValue("", "src"); + Drawable d = null; + + if (img != null) { + d = img.getDrawable(src); + } + + if (d == null) { + d = Resources.getSystem().getDrawable(android.R.drawable.ic_menu_report_image); + d.setBounds(0, 0, d.getIntrinsicWidth(), d.getIntrinsicHeight()); + } + + int len = text.length(); + text.append("\uFFFC"); + + text.setSpan(new ImageSpan(d, src), len, text.length(), + Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); + } + + private static void startFont(SpannableStringBuilder text, + Attributes attributes) { + String color = attributes.getValue("", "color"); + String face = attributes.getValue("", "face"); + + int len = text.length(); + text.setSpan(new Font(color, face), len, len, Spannable.SPAN_MARK_MARK); + } + + private static void endFont(SpannableStringBuilder text) { + int len = text.length(); + Object obj = getLast(text, Font.class); + int where = text.getSpanStart(obj); + + text.removeSpan(obj); + + if (where != len) { + Font f = (Font) obj; + + if (!TextUtils.isEmpty(f.mColor)) { + if (f.mColor.startsWith("@")) { + Resources res = Resources.getSystem(); + String name = f.mColor.substring(1); + int colorRes = res.getIdentifier(name, "color", "android"); + text.setSpan(new TextAppearanceSpan(null, 0, 0, ColorStateList.valueOf(res.getColor(colorRes)), null), + where, len, + Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); + } else { + try { + int c = getHtmlColor(f.mColor); + text.setSpan(new ForegroundColorSpan(c | 0xFF000000), + where, len, + Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); + } catch (IllegalArgumentException e) { + // Ignore + } + } + } + + if (f.mFace != null) { + text.setSpan(new TypefaceSpan(f.mFace), where, len, + Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); + } + } + } + + private static void startA(SpannableStringBuilder text, Attributes attributes) { + String href = attributes.getValue("", "href"); + + int len = text.length(); + text.setSpan(new Href(href), len, len, Spannable.SPAN_MARK_MARK); + } + + private static void endA(SpannableStringBuilder text) { + int len = text.length(); + Object obj = getLast(text, Href.class); + int where = text.getSpanStart(obj); + + text.removeSpan(obj); + + if (where != len) { + Href h = (Href) obj; + + if (h.mHref != null) { + text.setSpan(new URLSpan(h.mHref), where, len, + Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); + } + } + } + + private static void endHeader(SpannableStringBuilder text) { + int len = text.length(); + Object obj = getLast(text, Header.class); + + int where = text.getSpanStart(obj); + + text.removeSpan(obj); + + // Back off not to change only the text, not the blank line. + while (len > where && text.charAt(len - 1) == '\n') { + len--; + } + + if (where != len) { + Header h = (Header) obj; + + text.setSpan(new RelativeSizeSpan(HEADER_SIZES[h.mLevel]), + where, len, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); + text.setSpan(new StyleSpan(Typeface.BOLD), + where, len, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); + } + } + + /** + * Parse the color string, and return the corresponding color-int. + * If the string cannot be parsed, throws an IllegalArgumentException + * exception. Supported formats are: + * #RRGGBB + * #AARRGGBB + * rgb(255, 255, 255) + * or color name + */ + @ColorInt + public static int getHtmlColor(@NonNull String colorString) { + if ((colorString.length() == 7 || colorString.length() == 9) && colorString.charAt(0) == '#') { + // Use a long to avoid rollovers on #ffXXXXXX + long color; + try { + color = Long.parseLong(colorString.substring(1), 16); + } catch (NumberFormatException e) { + throw new IllegalArgumentException("Unknown color: " + colorString); + } + if (colorString.length() == 7) { + // Set the alpha value + color |= 0x00000000ff000000; + } + return (int) color; + } else if (colorString.startsWith("rgb(") && colorString.endsWith(")")) { + String str = colorString.substring(4, colorString.length() - 1); + String[] colors = str.split("[\\s]*,[\\s]*"); + if (colors.length == 3) { + try { + return Color.argb(0xff, Integer.valueOf(colors[0]), Integer.valueOf(colors[1]), Integer.valueOf(colors[2])); + } catch (NumberFormatException e) { + throw new IllegalArgumentException("Unknown color: " + colorString); + } + } + } else { + Integer color = sColorNameMap.get(colorString.toLowerCase(Locale.ROOT)); + if (color != null) { + return color; + } + } + throw new IllegalArgumentException("Unknown color: " + colorString); + } + public SpannableStringBuilder convert() { mReader.setContentHandler(this); @@ -611,173 +970,6 @@ class HtmlToSpannedConverter implements ContentHandler { } } - private static void handleP(SpannableStringBuilder text) { - int len = text.length(); - - if (len >= 1 && text.charAt(len - 1) == '\n') { - if (len >= 2 && text.charAt(len - 2) == '\n') { - return; - } - - text.append("\n"); - return; - } - - if (len != 0) { - text.append("\n\n"); - } - } - - private static void handleBr(SpannableStringBuilder text) { - text.append("\n"); - } - - private static Object getLast(Spanned text, Class kind) { - /* - * This knows that the last returned object from getSpans() - * will be the most recently added. - */ - Object[] objs = text.getSpans(0, text.length(), kind); - - if (objs.length == 0) { - return null; - } else { - return objs[objs.length - 1]; - } - } - - private static void start(SpannableStringBuilder text, Object mark) { - int len = text.length(); - text.setSpan(mark, len, len, Spannable.SPAN_MARK_MARK); - } - - private static void end(SpannableStringBuilder text, Class kind, - Object repl) { - int len = text.length(); - Object obj = getLast(text, kind); - int where = text.getSpanStart(obj); - - text.removeSpan(obj); - - if (where != len) { - text.setSpan(repl, where, len, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); - } - } - - private static void startImg(SpannableStringBuilder text, - Attributes attributes, Html.ImageGetter img) { - String src = attributes.getValue("", "src"); - Drawable d = null; - - if (img != null) { - d = img.getDrawable(src); - } - - if (d == null) { - d = Resources.getSystem().getDrawable(android.R.drawable.ic_menu_report_image); - d.setBounds(0, 0, d.getIntrinsicWidth(), d.getIntrinsicHeight()); - } - - int len = text.length(); - text.append("\uFFFC"); - - text.setSpan(new ImageSpan(d, src), len, text.length(), - Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); - } - - private static void startFont(SpannableStringBuilder text, - Attributes attributes) { - String color = attributes.getValue("", "color"); - String face = attributes.getValue("", "face"); - - int len = text.length(); - text.setSpan(new Font(color, face), len, len, Spannable.SPAN_MARK_MARK); - } - - private static void endFont(SpannableStringBuilder text) { - int len = text.length(); - Object obj = getLast(text, Font.class); - int where = text.getSpanStart(obj); - - text.removeSpan(obj); - - if (where != len) { - Font f = (Font) obj; - - if (!TextUtils.isEmpty(f.mColor)) { - if (f.mColor.startsWith("@")) { - Resources res = Resources.getSystem(); - String name = f.mColor.substring(1); - int colorRes = res.getIdentifier(name, "color", "android"); - text.setSpan(new TextAppearanceSpan(null, 0, 0, ColorStateList.valueOf(res.getColor(colorRes)), null), - where, len, - Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); - } else { - try { - int c = getHtmlColor(f.mColor); - text.setSpan(new ForegroundColorSpan(c | 0xFF000000), - where, len, - Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); - } catch (IllegalArgumentException e) { - // Ignore - } - } - } - - if (f.mFace != null) { - text.setSpan(new TypefaceSpan(f.mFace), where, len, - Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); - } - } - } - - private static void startA(SpannableStringBuilder text, Attributes attributes) { - String href = attributes.getValue("", "href"); - - int len = text.length(); - text.setSpan(new Href(href), len, len, Spannable.SPAN_MARK_MARK); - } - - private static void endA(SpannableStringBuilder text) { - int len = text.length(); - Object obj = getLast(text, Href.class); - int where = text.getSpanStart(obj); - - text.removeSpan(obj); - - if (where != len) { - Href h = (Href) obj; - - if (h.mHref != null) { - text.setSpan(new URLSpan(h.mHref), where, len, - Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); - } - } - } - - private static void endHeader(SpannableStringBuilder text) { - int len = text.length(); - Object obj = getLast(text, Header.class); - - int where = text.getSpanStart(obj); - - text.removeSpan(obj); - - // Back off not to change only the text, not the blank line. - while (len > where && text.charAt(len - 1) == '\n') { - len--; - } - - if (where != len) { - Header h = (Header) obj; - - text.setSpan(new RelativeSizeSpan(HEADER_SIZES[h.mLevel]), - where, len, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); - text.setSpan(new StyleSpan(Typeface.BOLD), - where, len, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); - } - } - @Override public void setDocumentLocator(Locator locator) { } @@ -810,7 +1002,7 @@ class HtmlToSpannedConverter implements ContentHandler { } @Override - public void characters(char ch[], int start, int length) throws SAXException { + public void characters(char[] ch, int start, int length) throws SAXException { StringBuilder sb = new StringBuilder(); /* @@ -849,7 +1041,7 @@ class HtmlToSpannedConverter implements ContentHandler { } @Override - public void ignorableWhitespace(char ch[], int start, int length) throws SAXException { + public void ignorableWhitespace(char[] ch, int start, int length) throws SAXException { } @Override @@ -860,16 +1052,35 @@ class HtmlToSpannedConverter implements ContentHandler { public void skippedEntity(String name) throws SAXException { } - private static class Bold { } - private static class Italic { } - private static class Underline { } - private static class Strike { } - private static class Big { } - private static class Small { } - private static class Monospace { } - private static class Blockquote { } - private static class Super { } - private static class Sub { } + private static class Bold { + } + + private static class Italic { + } + + private static class Underline { + } + + private static class Strike { + } + + private static class Big { + } + + private static class Small { + } + + private static class Monospace { + } + + private static class Blockquote { + } + + private static class Super { + } + + private static class Sub { + } private static class Font { public String mColor; @@ -896,193 +1107,4 @@ class HtmlToSpannedConverter implements ContentHandler { mLevel = level; } } - - /** - * Parse the color string, and return the corresponding color-int. - * If the string cannot be parsed, throws an IllegalArgumentException - * exception. Supported formats are: - * #RRGGBB - * #AARRGGBB - * rgb(255, 255, 255) - * or color name - */ - @ColorInt - public static int getHtmlColor(@NonNull String colorString) { - if ((colorString.length() == 7 || colorString.length() == 9) && colorString.charAt(0) == '#') { - // Use a long to avoid rollovers on #ffXXXXXX - long color; - try { - color = Long.parseLong(colorString.substring(1), 16); - } catch (NumberFormatException e) { - throw new IllegalArgumentException("Unknown color: " + colorString); - } - if (colorString.length() == 7) { - // Set the alpha value - color |= 0x00000000ff000000; - } - return (int) color; - } else if (colorString.startsWith("rgb(") && colorString.endsWith(")")) { - String str = colorString.substring(4, colorString.length() - 1); - String[] colors = str.split("[\\s]*,[\\s]*"); - if (colors.length == 3) { - try { - return Color.argb(0xff, Integer.valueOf(colors[0]), Integer.valueOf(colors[1]), Integer.valueOf(colors[2])); - } catch (NumberFormatException e) { - throw new IllegalArgumentException("Unknown color: " + colorString); - } - } - } else { - Integer color = sColorNameMap.get(colorString.toLowerCase(Locale.ROOT)); - if (color != null) { - return color; - } - } - throw new IllegalArgumentException("Unknown color: " + colorString); - } - - private static final HashMap sColorNameMap; - - static { - sColorNameMap = new HashMap<>(); - sColorNameMap.put("aliceblue", 0xFFF0F8FF); - sColorNameMap.put("antiquewhite", 0xFFFAEBD7); - sColorNameMap.put("aqua", 0xFFFFFF); - sColorNameMap.put("aquamarine", 0xFF7FFFD4); - sColorNameMap.put("azure", 0xFFF0FFFF); - sColorNameMap.put("beige", 0xFFF5F5DC); - sColorNameMap.put("bisque", 0xFFFFE4C4); - sColorNameMap.put("black", 0xFF0); - sColorNameMap.put("blanchedalmond", 0xFFFFEBCD); - sColorNameMap.put("blue", 0xFFFF); - sColorNameMap.put("blueviolet", 0xFF8A2BE2); - sColorNameMap.put("brown", 0xFFA52A2A); - sColorNameMap.put("burlywood", 0xFFDEB887); - sColorNameMap.put("cadetblue", 0xFF5F9EA0); - sColorNameMap.put("chartreuse", 0xFF7FFF00); - sColorNameMap.put("chocolate", 0xFFD2691E); - sColorNameMap.put("coral", 0xFFFF7F50); - sColorNameMap.put("cornflowerblue", 0xFF6495ED); - sColorNameMap.put("cornsilk", 0xFFFFF8DC); - sColorNameMap.put("crimson", 0xFFDC143C); - sColorNameMap.put("cyan", 0xFFFFFF); - sColorNameMap.put("darkblue", 0xFF8B); - sColorNameMap.put("darkcyan", 0xFF8B8B); - sColorNameMap.put("darkgoldenrod", 0xFFB8860B); - sColorNameMap.put("darkgray", 0xFFA9A9A9); - sColorNameMap.put("darkgreen", 0xFF6400); - sColorNameMap.put("darkkhaki", 0xFFBDB76B); - sColorNameMap.put("darkmagenta", 0xFF8B008B); - sColorNameMap.put("darkolivegreen", 0xFF556B2F); - sColorNameMap.put("darkorange", 0xFFFF8C00); - sColorNameMap.put("darkorchid", 0xFF9932CC); - sColorNameMap.put("darkred", 0xFF8B0000); - sColorNameMap.put("darksalmon", 0xFFE9967A); - sColorNameMap.put("darkseagreen", 0xFF8FBC8F); - sColorNameMap.put("darkslateblue", 0xFF483D8B); - sColorNameMap.put("darkslategray", 0xFF2F4F4F); - sColorNameMap.put("darkturquoise", 0xFFCED1); - sColorNameMap.put("darkviolet", 0xFF9400D3); - sColorNameMap.put("deeppink", 0xFFFF1493); - sColorNameMap.put("deepskyblue", 0xFFBFFF); - sColorNameMap.put("dimgray", 0xFF696969); - sColorNameMap.put("dodgerblue", 0xFF1E90FF); - sColorNameMap.put("firebrick", 0xFFB22222); - sColorNameMap.put("floralwhite", 0xFFFFFAF0); - sColorNameMap.put("forestgreen", 0xFF228B22); - sColorNameMap.put("fuchsia", 0xFFFF00FF); - sColorNameMap.put("gainsboro", 0xFFDCDCDC); - sColorNameMap.put("ghostwhite", 0xFFF8F8FF); - sColorNameMap.put("gold", 0xFFFFD700); - sColorNameMap.put("goldenrod", 0xFFDAA520); - sColorNameMap.put("gray", 0xFF808080); - sColorNameMap.put("green", 0xFF8000); - sColorNameMap.put("greenyellow", 0xFFADFF2F); - sColorNameMap.put("honeydew", 0xFFF0FFF0); - sColorNameMap.put("hotpink", 0xFFFF69B4); - sColorNameMap.put("indianred", 0xFFCD5C5C); - sColorNameMap.put("indigo", 0xFF4B0082); - sColorNameMap.put("ivory", 0xFFFFFFF0); - sColorNameMap.put("khaki", 0xFFF0E68C); - sColorNameMap.put("lavender", 0xFFE6E6FA); - sColorNameMap.put("lavenderblush", 0xFFFFF0F5); - sColorNameMap.put("lawngreen", 0xFF7CFC00); - sColorNameMap.put("lemonchiffon", 0xFFFFFACD); - sColorNameMap.put("lightblue", 0xFFADD8E6); - sColorNameMap.put("lightcoral", 0xFFF08080); - sColorNameMap.put("lightcyan", 0xFFE0FFFF); - sColorNameMap.put("lightgoldenrodyellow", 0xFFFAFAD2); - sColorNameMap.put("lightgreen", 0xFF90EE90); - sColorNameMap.put("lightgrey", 0xFFD3D3D3); - sColorNameMap.put("lightpink", 0xFFFFB6C1); - sColorNameMap.put("lightsalmon", 0xFFFFA07A); - sColorNameMap.put("lightseagreen", 0xFF20B2AA); - sColorNameMap.put("lightskyblue", 0xFF87CEFA); - sColorNameMap.put("lightslategray", 0xFF778899); - sColorNameMap.put("lightsteelblue", 0xFFB0C4DE); - sColorNameMap.put("lightyellow", 0xFFFFFFE0); - sColorNameMap.put("lime", 0xFFFF00); - sColorNameMap.put("limegreen", 0xFF32CD32); - sColorNameMap.put("linen", 0xFFFAF0E6); - sColorNameMap.put("magenta", 0xFFFF00FF); - sColorNameMap.put("maroon", 0xFF800000); - sColorNameMap.put("mediumaquamarine", 0xFF66CDAA); - sColorNameMap.put("mediumblue", 0xFFCD); - sColorNameMap.put("mediumorchid", 0xFFBA55D3); - sColorNameMap.put("mediumpurple", 0xFF9370DB); - sColorNameMap.put("mediumseagreen", 0xFF3CB371); - sColorNameMap.put("mediumslateblue", 0xFF7B68EE); - sColorNameMap.put("mediumspringgreen", 0xFFFA9A); - sColorNameMap.put("mediumturquoise", 0xFF48D1CC); - sColorNameMap.put("mediumvioletred", 0xFFC71585); - sColorNameMap.put("midnightblue", 0xFF191970); - sColorNameMap.put("mintcream", 0xFFF5FFFA); - sColorNameMap.put("mistyrose", 0xFFFFE4E1); - sColorNameMap.put("moccasin", 0xFFFFE4B5); - sColorNameMap.put("navajowhite", 0xFFFFDEAD); - sColorNameMap.put("navy", 0xFF80); - sColorNameMap.put("oldlace", 0xFFFDF5E6); - sColorNameMap.put("olive", 0xFF808000); - sColorNameMap.put("olivedrab", 0xFF6B8E23); - sColorNameMap.put("orange", 0xFFFFA500); - sColorNameMap.put("orangered", 0xFFFF4500); - sColorNameMap.put("orchid", 0xFFDA70D6); - sColorNameMap.put("palegoldenrod", 0xFFEEE8AA); - sColorNameMap.put("palegreen", 0xFF98FB98); - sColorNameMap.put("paleturquoise", 0xFFAFEEEE); - sColorNameMap.put("palevioletred", 0xFFDB7093); - sColorNameMap.put("papayawhip", 0xFFFFEFD5); - sColorNameMap.put("peachpuff", 0xFFFFDAB9); - sColorNameMap.put("peru", 0xFFCD853F); - sColorNameMap.put("pink", 0xFFFFC0CB); - sColorNameMap.put("plum", 0xFFDDA0DD); - sColorNameMap.put("powderblue", 0xFFB0E0E6); - sColorNameMap.put("purple", 0xFF800080); - sColorNameMap.put("red", 0xFFFF0000); - sColorNameMap.put("rosybrown", 0xFFBC8F8F); - sColorNameMap.put("royalblue", 0xFF4169E1); - sColorNameMap.put("saddlebrown", 0xFF8B4513); - sColorNameMap.put("salmon", 0xFFFA8072); - sColorNameMap.put("sandybrown", 0xFFF4A460); - sColorNameMap.put("seagreen", 0xFF2E8B57); - sColorNameMap.put("seashell", 0xFFFFF5EE); - sColorNameMap.put("sienna", 0xFFA0522D); - sColorNameMap.put("silver", 0xFFC0C0C0); - sColorNameMap.put("skyblue", 0xFF87CEEB); - sColorNameMap.put("slateblue", 0xFF6A5ACD); - sColorNameMap.put("slategray", 0xFF708090); - sColorNameMap.put("snow", 0xFFFFFAFA); - sColorNameMap.put("springgreen", 0xFFFF7F); - sColorNameMap.put("steelblue", 0xFF4682B4); - sColorNameMap.put("tan", 0xFFD2B48C); - sColorNameMap.put("teal", 0xFF8080); - sColorNameMap.put("thistle", 0xFFD8BFD8); - sColorNameMap.put("tomato", 0xFFFF6347); - sColorNameMap.put("turquoise", 0xFF40E0D0); - sColorNameMap.put("violet", 0xFFEE82EE); - sColorNameMap.put("wheat", 0xFFF5DEB3); - sColorNameMap.put("white", 0xFFFFFFFF); - sColorNameMap.put("whitesmoke", 0xFFF5F5F5); - sColorNameMap.put("yellow", 0xFFFFFF00); - sColorNameMap.put("yellowgreen", 0xFF9ACD32); - } } diff --git a/app/src/main/java/com/hippo/text/LinkMovementMethod2.java b/app/src/main/java/com/hippo/text/LinkMovementMethod2.java index 3dfdb9902..3d12ac39d 100644 --- a/app/src/main/java/com/hippo/text/LinkMovementMethod2.java +++ b/app/src/main/java/com/hippo/text/LinkMovementMethod2.java @@ -39,8 +39,18 @@ public class LinkMovementMethod2 extends ScrollingMovementMethod { private static final int CLICK = 1; private static final int UP = 2; private static final int DOWN = 3; + private static final Object FROM_BELOW = new NoCopySpan.Concrete(); + private static LinkMovementMethod2 sInstance; - private LinkMovementMethod2() {} + private LinkMovementMethod2() { + } + + public static MovementMethod getInstance() { + if (sInstance == null) + sInstance = new LinkMovementMethod2(); + + return sInstance; + } @Override public boolean canSelectArbitrarily() { @@ -49,7 +59,7 @@ public class LinkMovementMethod2 extends ScrollingMovementMethod { @Override protected boolean handleMovementKey(TextView widget, Spannable buffer, int keyCode, - int movementMetaState, KeyEvent event) { + int movementMetaState, KeyEvent event) { switch (keyCode) { case KeyEvent.KEYCODE_DPAD_CENTER: case KeyEvent.KEYCODE_ENTER: @@ -199,7 +209,7 @@ public class LinkMovementMethod2 extends ScrollingMovementMethod { @Override public boolean onTouchEvent(TextView widget, Spannable buffer, - MotionEvent event) { + MotionEvent event) { int action = event.getAction(); if (action == MotionEvent.ACTION_UP || @@ -258,14 +268,4 @@ public class LinkMovementMethod2 extends ScrollingMovementMethod { text.removeSpan(FROM_BELOW); } } - - public static MovementMethod getInstance() { - if (sInstance == null) - sInstance = new LinkMovementMethod2(); - - return sInstance; - } - - private static LinkMovementMethod2 sInstance; - private static final Object FROM_BELOW = new NoCopySpan.Concrete(); } diff --git a/app/src/main/java/com/hippo/util/AppHelper.java b/app/src/main/java/com/hippo/util/AppHelper.java index 2ba53ceac..157fb0620 100644 --- a/app/src/main/java/com/hippo/util/AppHelper.java +++ b/app/src/main/java/com/hippo/util/AppHelper.java @@ -25,14 +25,16 @@ import android.net.Uri; import android.view.View; import android.view.inputmethod.InputMethodManager; import android.widget.Toast; + import androidx.annotation.NonNull; import androidx.annotation.Nullable; + import com.hippo.ehviewer.R; public class AppHelper { public static boolean sendEmail(@NonNull Activity from, @NonNull String address, - @Nullable String subject, @Nullable String text) { + @Nullable String subject, @Nullable String text) { Intent i = new Intent(Intent.ACTION_SENDTO); i.setData(Uri.parse("mailto:" + address)); if (subject != null) { diff --git a/app/src/main/java/com/hippo/util/BitmapUtils.java b/app/src/main/java/com/hippo/util/BitmapUtils.java index 838b3e4ca..4b74b35cb 100644 --- a/app/src/main/java/com/hippo/util/BitmapUtils.java +++ b/app/src/main/java/com/hippo/util/BitmapUtils.java @@ -20,16 +20,20 @@ import android.app.ActivityManager; import android.content.Context; import android.graphics.Bitmap; import android.graphics.BitmapFactory; + import androidx.annotation.NonNull; + import com.hippo.streampipe.InputStreamPipe; import com.hippo.yorozuya.MathUtils; + import java.io.IOException; public final class BitmapUtils { - private BitmapUtils() {} - public static Context sContext; + private BitmapUtils() { + } + public static void initialize(Context context) { sContext = context.getApplicationContext(); } @@ -46,7 +50,7 @@ public final class BitmapUtils { } public static Bitmap decodeStream(@NonNull InputStreamPipe isp, int maxWidth, int maxHeight, - int pixels, boolean checkMemory, boolean justCalc, int[] sampleSize) { + int pixels, boolean checkMemory, boolean justCalc, int[] sampleSize) { try { isp.obtain(); diff --git a/app/src/main/java/com/hippo/util/DrawableManager.java b/app/src/main/java/com/hippo/util/DrawableManager.java index e25a97596..b9efc9e99 100644 --- a/app/src/main/java/com/hippo/util/DrawableManager.java +++ b/app/src/main/java/com/hippo/util/DrawableManager.java @@ -20,6 +20,7 @@ import android.content.Context; import android.content.res.Resources; import android.graphics.drawable.Drawable; import android.os.Build; + import androidx.annotation.DrawableRes; import androidx.annotation.NonNull; import androidx.annotation.Nullable; @@ -32,7 +33,7 @@ public final class DrawableManager { } public static Drawable getVectorDrawable(@NonNull Resources res, - @DrawableRes int resId, @Nullable Resources.Theme theme) { + @DrawableRes int resId, @Nullable Resources.Theme theme) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { return res.getDrawable(resId, theme); } else { diff --git a/app/src/main/java/com/hippo/util/ExceptionUtils.java b/app/src/main/java/com/hippo/util/ExceptionUtils.java index 963a344a8..3e7ea59db 100644 --- a/app/src/main/java/com/hippo/util/ExceptionUtils.java +++ b/app/src/main/java/com/hippo/util/ExceptionUtils.java @@ -17,17 +17,21 @@ package com.hippo.util; import androidx.annotation.NonNull; + import com.hippo.ehviewer.GetText; import com.hippo.ehviewer.R; import com.hippo.ehviewer.client.exception.EhException; import com.hippo.network.StatusCodeException; + +import org.apache.http.conn.ConnectTimeoutException; + import java.net.MalformedURLException; import java.net.ProtocolException; import java.net.SocketException; import java.net.SocketTimeoutException; import java.net.UnknownHostException; + import javax.net.ssl.SSLException; -import org.apache.http.conn.ConnectTimeoutException; public final class ExceptionUtils { diff --git a/app/src/main/java/com/hippo/util/HashCodeUtils.java b/app/src/main/java/com/hippo/util/HashCodeUtils.java index 705edd915..13e9b7f0f 100644 --- a/app/src/main/java/com/hippo/util/HashCodeUtils.java +++ b/app/src/main/java/com/hippo/util/HashCodeUtils.java @@ -21,22 +21,22 @@ package com.hippo.util; public class HashCodeUtils { - private static final int X = 31; + private static final int X = 31; - /** - *

{@code
-   * args[0].hashCode()*31^(n-1) + args[1].hashCode()*31^(n-2) + ... + args[n-1].hashCode()
-   * }
- * Returns 0 if {@code args == null}. - */ - public static int hashCode(Object... args) { - if (args == null) { - return 0; + /** + *
{@code
+     * args[0].hashCode()*31^(n-1) + args[1].hashCode()*31^(n-2) + ... + args[n-1].hashCode()
+     * }
+ * Returns 0 if {@code args == null}. + */ + public static int hashCode(Object... args) { + if (args == null) { + return 0; + } + int hash = 0; + for (Object o : args) { + hash = X * hash + (o != null ? o.hashCode() : 0); + } + return hash; } - int hash = 0; - for (Object o: args) { - hash = X * hash + (o != null ? o.hashCode() : 0); - } - return hash; - } } diff --git a/app/src/main/java/com/hippo/util/IoThreadPoolExecutor.java b/app/src/main/java/com/hippo/util/IoThreadPoolExecutor.java index bac34cc9b..ace5b9fc0 100644 --- a/app/src/main/java/com/hippo/util/IoThreadPoolExecutor.java +++ b/app/src/main/java/com/hippo/util/IoThreadPoolExecutor.java @@ -17,7 +17,9 @@ package com.hippo.util; import androidx.annotation.NonNull; + import com.hippo.yorozuya.thread.PriorityThreadFactory; + import java.util.concurrent.BlockingQueue; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.RejectedExecutionException; @@ -28,63 +30,63 @@ import java.util.concurrent.TimeUnit; public class IoThreadPoolExecutor extends ThreadPoolExecutor { - private final static ThreadPoolExecutor INSTANCE = - IoThreadPoolExecutor.newInstance(3, 32, 1L, TimeUnit.SECONDS, - new PriorityThreadFactory("IO",android.os.Process.THREAD_PRIORITY_BACKGROUND)); + private final static ThreadPoolExecutor INSTANCE = + IoThreadPoolExecutor.newInstance(3, 32, 1L, TimeUnit.SECONDS, + new PriorityThreadFactory("IO", android.os.Process.THREAD_PRIORITY_BACKGROUND)); - private IoThreadPoolExecutor( - int corePoolSize, - int maximumPoolSize, - long keepAliveTime, - TimeUnit unit, - BlockingQueue workQueue, - ThreadFactory threadFactory, - RejectedExecutionHandler handler - ) { - super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory, handler); - } - - public static ThreadPoolExecutor getInstance() { - return INSTANCE; - } - - private static ThreadPoolExecutor newInstance( - int corePoolSize, - int maximumPoolSize, - long keepAliveTime, - TimeUnit unit, - ThreadFactory threadFactory - ) { - ThreadQueue queue = new ThreadQueue(); - PutRunnableBackHandler handler = new PutRunnableBackHandler(); - IoThreadPoolExecutor executor = new IoThreadPoolExecutor( - corePoolSize, maximumPoolSize, keepAliveTime, unit, queue, threadFactory, handler); - queue.setThreadPoolExecutor(executor); - return executor; - } - - private static class ThreadQueue extends LinkedBlockingQueue { - - private ThreadPoolExecutor executor; - - void setThreadPoolExecutor(ThreadPoolExecutor executor) { - this.executor = executor; + private IoThreadPoolExecutor( + int corePoolSize, + int maximumPoolSize, + long keepAliveTime, + TimeUnit unit, + BlockingQueue workQueue, + ThreadFactory threadFactory, + RejectedExecutionHandler handler + ) { + super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory, handler); } - @Override - public boolean offer(@NonNull Runnable o) { - int allWorkingThreads = executor.getActiveCount() + super.size(); - return allWorkingThreads < executor.getPoolSize() && super.offer(o); + public static ThreadPoolExecutor getInstance() { + return INSTANCE; } - } - public static class PutRunnableBackHandler implements RejectedExecutionHandler { - public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) { - try { - executor.getQueue().put(r); - } catch (InterruptedException e) { - throw new RejectedExecutionException(e); - } + private static ThreadPoolExecutor newInstance( + int corePoolSize, + int maximumPoolSize, + long keepAliveTime, + TimeUnit unit, + ThreadFactory threadFactory + ) { + ThreadQueue queue = new ThreadQueue(); + PutRunnableBackHandler handler = new PutRunnableBackHandler(); + IoThreadPoolExecutor executor = new IoThreadPoolExecutor( + corePoolSize, maximumPoolSize, keepAliveTime, unit, queue, threadFactory, handler); + queue.setThreadPoolExecutor(executor); + return executor; + } + + private static class ThreadQueue extends LinkedBlockingQueue { + + private ThreadPoolExecutor executor; + + void setThreadPoolExecutor(ThreadPoolExecutor executor) { + this.executor = executor; + } + + @Override + public boolean offer(@NonNull Runnable o) { + int allWorkingThreads = executor.getActiveCount() + super.size(); + return allWorkingThreads < executor.getPoolSize() && super.offer(o); + } + } + + public static class PutRunnableBackHandler implements RejectedExecutionHandler { + public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) { + try { + executor.getQueue().put(r); + } catch (InterruptedException e) { + throw new RejectedExecutionException(e); + } + } } - } } diff --git a/app/src/main/java/com/hippo/util/JsoupUtils.java b/app/src/main/java/com/hippo/util/JsoupUtils.java index e2ed8c5b8..aaadd2c32 100644 --- a/app/src/main/java/com/hippo/util/JsoupUtils.java +++ b/app/src/main/java/com/hippo/util/JsoupUtils.java @@ -17,6 +17,7 @@ package com.hippo.util; import androidx.annotation.Nullable; + import org.jsoup.nodes.Document; import org.jsoup.nodes.Element; import org.jsoup.select.Elements; diff --git a/app/src/main/java/com/hippo/util/MutableBoolean.java b/app/src/main/java/com/hippo/util/MutableBoolean.java index e76150eb3..1d8d6d812 100644 --- a/app/src/main/java/com/hippo/util/MutableBoolean.java +++ b/app/src/main/java/com/hippo/util/MutableBoolean.java @@ -18,9 +18,9 @@ package com.hippo.util; public class MutableBoolean { - public boolean value; + public boolean value; - public MutableBoolean(boolean value) { - this.value = value; - } + public MutableBoolean(boolean value) { + this.value = value; + } } diff --git a/app/src/main/java/com/hippo/util/NaturalComparator.java b/app/src/main/java/com/hippo/util/NaturalComparator.java index faad7c353..d6236f354 100644 --- a/app/src/main/java/com/hippo/util/NaturalComparator.java +++ b/app/src/main/java/com/hippo/util/NaturalComparator.java @@ -23,130 +23,130 @@ import java.util.Comparator; */ public class NaturalComparator implements Comparator { - @Override - public int compare(String o1, String o2) { - if (o1 == null && o2 == null) { - return 0; - } - if (o1 == null) { - return -1; - } - if (o2 == null) { - return 1; + private static boolean isDigit(String str) { + // Just check the first char + char ch = str.charAt(0); + return ch >= '0' && ch <= '9'; } - int index1 = 0; - int index2 = 0; - while (true) { - String data1 = nextSlice(o1, index1); - String data2 = nextSlice(o2, index2); - - if (data1 == null && data2 == null) { - return 0; - } - if (data1 == null) { - return -1; - } - if (data2 == null) { - return 1; - } - - index1 += data1.length(); - index2 += data2.length(); - - int result; - if (isDigit(data1) && isDigit(data2)) { - result = compareNumberString(data1, data2); - } else { - result = data1.compareToIgnoreCase(data2); - } - - if (result != 0) { - return result; - } - } - } - - private static boolean isDigit(String str) { - // Just check the first char - char ch = str.charAt(0); - return ch >= '0' && ch <= '9'; - } - - private static String nextSlice(String str, int index) { - int length = str.length(); - if (index == length) { - return null; - } - - char ch = str.charAt(index); - if (ch == '.' || ch == ' ') { - return str.substring(index, index + 1); - } else if (ch >= '0' && ch <= '9') { - return str.substring(index, nextNumberBound(str, index + 1)); - } else { - return str.substring(index, nextOtherBound(str, index + 1)); - } - } - - private static int nextNumberBound(String str, int index) { - for (int length = str.length(); index < length; index++) { - char ch = str.charAt(index); - if (ch < '0' || ch > '9') { - break; - } - } - return index; - } - - private static int nextOtherBound(String str, int index) { - for (int length = str.length(); index < length; index++) { - char ch = str.charAt(index); - if (ch == '.' || ch == ' ' || (ch >= '0' && ch <= '9')) { - break; - } - } - return index; - } - - private static String removeLeadingZero(String s) { - if (s.length() < 1) { - return s; - } - - // At least keep the last number - for (int i = 0, n = s.length() - 1; i < n; i++) { - if (s.charAt(i) != '0') { - return s.substring(i); - } - } - - return s.substring(s.length() - 1); - } - - private static int compareNumberString(String s1, String s2) { - String p1 = removeLeadingZero(s1); - String p2 = removeLeadingZero(s2); - - int l1 = p1.length(); - int l2 = p2.length(); - - if (l1 > l2) { - return 1; - } else if (l1 < l2) { - return -1; - } else { - for (int i = 0; i < l1; i++) { - char c1 = p1.charAt(i); - char c2 = p2.charAt(i); - if (c1 > c2) { - return 1; - } else if (c1 < c2) { - return -1; + private static String nextSlice(String str, int index) { + int length = str.length(); + if (index == length) { + return null; + } + + char ch = str.charAt(index); + if (ch == '.' || ch == ' ') { + return str.substring(index, index + 1); + } else if (ch >= '0' && ch <= '9') { + return str.substring(index, nextNumberBound(str, index + 1)); + } else { + return str.substring(index, nextOtherBound(str, index + 1)); } - } } - return -Integer.compare(s1.length(), s2.length()); - } + private static int nextNumberBound(String str, int index) { + for (int length = str.length(); index < length; index++) { + char ch = str.charAt(index); + if (ch < '0' || ch > '9') { + break; + } + } + return index; + } + + private static int nextOtherBound(String str, int index) { + for (int length = str.length(); index < length; index++) { + char ch = str.charAt(index); + if (ch == '.' || ch == ' ' || (ch >= '0' && ch <= '9')) { + break; + } + } + return index; + } + + private static String removeLeadingZero(String s) { + if (s.length() < 1) { + return s; + } + + // At least keep the last number + for (int i = 0, n = s.length() - 1; i < n; i++) { + if (s.charAt(i) != '0') { + return s.substring(i); + } + } + + return s.substring(s.length() - 1); + } + + private static int compareNumberString(String s1, String s2) { + String p1 = removeLeadingZero(s1); + String p2 = removeLeadingZero(s2); + + int l1 = p1.length(); + int l2 = p2.length(); + + if (l1 > l2) { + return 1; + } else if (l1 < l2) { + return -1; + } else { + for (int i = 0; i < l1; i++) { + char c1 = p1.charAt(i); + char c2 = p2.charAt(i); + if (c1 > c2) { + return 1; + } else if (c1 < c2) { + return -1; + } + } + } + + return -Integer.compare(s1.length(), s2.length()); + } + + @Override + public int compare(String o1, String o2) { + if (o1 == null && o2 == null) { + return 0; + } + if (o1 == null) { + return -1; + } + if (o2 == null) { + return 1; + } + + int index1 = 0; + int index2 = 0; + while (true) { + String data1 = nextSlice(o1, index1); + String data2 = nextSlice(o2, index2); + + if (data1 == null && data2 == null) { + return 0; + } + if (data1 == null) { + return -1; + } + if (data2 == null) { + return 1; + } + + index1 += data1.length(); + index2 += data2.length(); + + int result; + if (isDigit(data1) && isDigit(data2)) { + result = compareNumberString(data1, data2); + } else { + result = data1.compareToIgnoreCase(data2); + } + + if (result != 0) { + return result; + } + } + } } diff --git a/app/src/main/java/com/hippo/util/PackageUtils.java b/app/src/main/java/com/hippo/util/PackageUtils.java index 364b6849d..a551d14b5 100644 --- a/app/src/main/java/com/hippo/util/PackageUtils.java +++ b/app/src/main/java/com/hippo/util/PackageUtils.java @@ -57,7 +57,7 @@ public final class PackageUtils { md = MessageDigest.getInstance("SHA1"); byte[] sha1 = md.digest(certRaw); int length = sha1.length; - for (int i = 0; i < length; i++) { + for (int i = 0; i < length; i++) { if (i != 0) { sb.append(':'); } @@ -70,8 +70,7 @@ public final class PackageUtils { } return sb.toString().toUpperCase(); - } - catch (NoSuchAlgorithmException ex) { + } catch (NoSuchAlgorithmException ex) { Log.e(TAG, "Can't final Algorithm SHA1", ex); return null; } diff --git a/app/src/main/java/com/hippo/util/PathNaturalComparator.java b/app/src/main/java/com/hippo/util/PathNaturalComparator.java index 62d15a39f..864aa7879 100644 --- a/app/src/main/java/com/hippo/util/PathNaturalComparator.java +++ b/app/src/main/java/com/hippo/util/PathNaturalComparator.java @@ -11,94 +11,94 @@ import java.util.Comparator; */ public class PathNaturalComparator implements Comparator { - private static final boolean TYPE_SEPARATOR = false; - private static final boolean TYPE_NORMAL = true; + private static final boolean TYPE_SEPARATOR = false; + private static final boolean TYPE_NORMAL = true; - private Comparator naturalComparator = new NaturalComparator(); + private Comparator naturalComparator = new NaturalComparator(); - @Override - public int compare(String o1, String o2) { - if (o1 == null && o2 == null) { - return 0; - } - if (o1 == null) { - return -1; - } - if (o2 == null) { - return 1; - } - - int index1 = 0; - int index2 = 0; - - while (true) { - String data1; - String data2; - - for (;;) { - int newIndex1 = nextSegmentStart(o1, index1); - if (newIndex1 == index1) { - data1 = null; - break; + private static int nextSegmentStart(String str, int index) { + if (index >= str.length()) { + return index; } - if (getType(o1.charAt(newIndex1 - 1)) == TYPE_NORMAL) { - data1 = o1.substring(index1, newIndex1); - index1 = newIndex1; - break; + + boolean type = getType(str.charAt(index)); + int i = index + 1; + while (i < str.length() && type == getType(str.charAt(i))) { + i += 1; } - index1 = newIndex1; - } - for (;;) { - int newIndex2 = nextSegmentStart(o2, index2); - if (newIndex2 == index2) { - data2 = null; - break; + return i; + } + + private static boolean getType(char c) { + if (c == '/' || c == '\\') { + return TYPE_SEPARATOR; + } else { + return TYPE_NORMAL; } - if (getType(o2.charAt(newIndex2 - 1)) == TYPE_NORMAL) { - data2 = o2.substring(index2, newIndex2); - index2 = newIndex2; - break; + } + + @Override + public int compare(String o1, String o2) { + if (o1 == null && o2 == null) { + return 0; + } + if (o1 == null) { + return -1; + } + if (o2 == null) { + return 1; } - index2 = newIndex2; - } - if (data1 == null && data2 == null) { - return 0; - } - if (data1 == null) { - return -1; - } - if (data2 == null) { - return 1; - } + int index1 = 0; + int index2 = 0; - int result = naturalComparator.compare(data1, data2); - if (result != 0) { - return result; - } + while (true) { + String data1; + String data2; + + for (; ; ) { + int newIndex1 = nextSegmentStart(o1, index1); + if (newIndex1 == index1) { + data1 = null; + break; + } + if (getType(o1.charAt(newIndex1 - 1)) == TYPE_NORMAL) { + data1 = o1.substring(index1, newIndex1); + index1 = newIndex1; + break; + } + index1 = newIndex1; + } + + for (; ; ) { + int newIndex2 = nextSegmentStart(o2, index2); + if (newIndex2 == index2) { + data2 = null; + break; + } + if (getType(o2.charAt(newIndex2 - 1)) == TYPE_NORMAL) { + data2 = o2.substring(index2, newIndex2); + index2 = newIndex2; + break; + } + index2 = newIndex2; + } + + if (data1 == null && data2 == null) { + return 0; + } + if (data1 == null) { + return -1; + } + if (data2 == null) { + return 1; + } + + int result = naturalComparator.compare(data1, data2); + if (result != 0) { + return result; + } + } } - } - - private static int nextSegmentStart(String str, int index) { - if (index >= str.length()) { - return index; - } - - boolean type = getType(str.charAt(index)); - int i = index + 1; - while (i < str.length() && type == getType(str.charAt(i))) { - i += 1; - } - - return i; - } - - private static boolean getType(char c) { - if (c == '/' || c == '\\') { - return TYPE_SEPARATOR; - } else { - return TYPE_NORMAL; - } - } } diff --git a/app/src/main/java/com/hippo/util/PermissionRequester.java b/app/src/main/java/com/hippo/util/PermissionRequester.java index 45c20a627..f7547bd8e 100644 --- a/app/src/main/java/com/hippo/util/PermissionRequester.java +++ b/app/src/main/java/com/hippo/util/PermissionRequester.java @@ -19,6 +19,7 @@ package com.hippo.util; import android.app.Activity; import android.content.DialogInterface; import android.content.pm.PackageManager; + import androidx.appcompat.app.AlertDialog; import androidx.core.app.ActivityCompat; @@ -56,9 +57,9 @@ public class PermissionRequester { } private static boolean requestPermissions( - final Activity activity, - final String[] permissions, - int requestCode + final Activity activity, + final String[] permissions, + int requestCode ) { try { ActivityCompat.requestPermissions(activity, permissions, requestCode); diff --git a/app/src/main/java/com/hippo/util/ReadableTime.java b/app/src/main/java/com/hippo/util/ReadableTime.java index 10ab35d00..4c5c13739 100644 --- a/app/src/main/java/com/hippo/util/ReadableTime.java +++ b/app/src/main/java/com/hippo/util/ReadableTime.java @@ -19,7 +19,9 @@ package com.hippo.util; import android.annotation.SuppressLint; import android.content.Context; import android.content.res.Resources; + import com.hippo.ehviewer.R; + import java.text.SimpleDateFormat; import java.util.Calendar; import java.util.Date; @@ -28,17 +30,13 @@ import java.util.Locale; @SuppressLint("SimpleDateFormat") public final class ReadableTime { - private static Resources sResources; - public static final long SECOND_MILLIS = 1000; public static final long MINUTE_MILLIS = 60 * SECOND_MILLIS; public static final long HOUR_MILLIS = 60 * MINUTE_MILLIS; public static final long DAY_MILLIS = 24 * HOUR_MILLIS; public static final long WEEK_MILLIS = 7 * DAY_MILLIS; public static final long YEAR_MILLIS = 365 * DAY_MILLIS; - public static final int SIZE = 5; - public static final long[] MULTIPLES = { YEAR_MILLIS, DAY_MILLIS, @@ -46,7 +44,6 @@ public final class ReadableTime { MINUTE_MILLIS, SECOND_MILLIS }; - public static final int[] UNITS = { R.plurals.year, R.plurals.day, @@ -54,21 +51,17 @@ public final class ReadableTime { R.plurals.minute, R.plurals.second }; - private static final Calendar sCalendar = Calendar.getInstance(); private static final Object sCalendarLock = new Object(); - private static final SimpleDateFormat DATE_FORMAT_WITHOUT_YEAR = new SimpleDateFormat("MMM d"); private static final SimpleDateFormat DATE_FORMAT_WITH_YEAR = new SimpleDateFormat("MMM d, yyyy"); - private static final SimpleDateFormat DATE_FORMAT_WITHOUT_YEAR_ZH = new SimpleDateFormat("M月d日"); private static final SimpleDateFormat DATE_FORMAT_WITH_YEAR_ZH = new SimpleDateFormat("yyyy年M月d日"); - private static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("yy-MM-dd HH:mm"); private static final Object sDateFormatLock1 = new Object(); - private static final SimpleDateFormat FILENAMABLE_DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss-SSS"); private static final Object sDateFormatLock2 = new Object(); + private static Resources sResources; public static void initialize(Context context) { sResources = context.getApplicationContext().getResources(); diff --git a/app/src/main/java/com/hippo/util/SqlUtils.java b/app/src/main/java/com/hippo/util/SqlUtils.java index e7ad4734e..851e03731 100644 --- a/app/src/main/java/com/hippo/util/SqlUtils.java +++ b/app/src/main/java/com/hippo/util/SqlUtils.java @@ -50,7 +50,7 @@ public class SqlUtils { } cursor.close(); - for(String tableName : tables) { + for (String tableName : tables) { dropTable(db, tableName); } } diff --git a/app/src/main/java/com/hippo/util/SystemUiHelper.java b/app/src/main/java/com/hippo/util/SystemUiHelper.java index 7537f5f40..4192405c2 100644 --- a/app/src/main/java/com/hippo/util/SystemUiHelper.java +++ b/app/src/main/java/com/hippo/util/SystemUiHelper.java @@ -93,11 +93,11 @@ public final class SystemUiHelper { * Construct a new SystemUiHelper. * * @param activity The Activity who's system UI should be changed - * @param level The level of hiding. Should be either {@link #LEVEL_LOW_PROFILE}, - * {@link #LEVEL_HIDE_STATUS_BAR}, {@link #LEVEL_LEAN_BACK} or - * {@link #LEVEL_IMMERSIVE} - * @param flags Additional options. See {@link #FLAG_LAYOUT_IN_SCREEN_OLDER_DEVICES} and - * {@link #FLAG_IMMERSIVE_STICKY} + * @param level The level of hiding. Should be either {@link #LEVEL_LOW_PROFILE}, + * {@link #LEVEL_HIDE_STATUS_BAR}, {@link #LEVEL_LEAN_BACK} or + * {@link #LEVEL_IMMERSIVE} + * @param flags Additional options. See {@link #FLAG_LAYOUT_IN_SCREEN_OLDER_DEVICES} and + * {@link #FLAG_IMMERSIVE_STICKY} */ public SystemUiHelper(Activity activity, int level, int flags) { this(activity, level, flags, null); @@ -107,15 +107,15 @@ public final class SystemUiHelper { * Construct a new SystemUiHelper. * * @param activity The Activity who's system UI should be changed - * @param level The level of hiding. Should be either {@link #LEVEL_LOW_PROFILE}, - * {@link #LEVEL_HIDE_STATUS_BAR}, {@link #LEVEL_LEAN_BACK} or - * {@link #LEVEL_IMMERSIVE} - * @param flags Additional options. See {@link #FLAG_LAYOUT_IN_SCREEN_OLDER_DEVICES} and - * {@link #FLAG_IMMERSIVE_STICKY} + * @param level The level of hiding. Should be either {@link #LEVEL_LOW_PROFILE}, + * {@link #LEVEL_HIDE_STATUS_BAR}, {@link #LEVEL_LEAN_BACK} or + * {@link #LEVEL_IMMERSIVE} + * @param flags Additional options. See {@link #FLAG_LAYOUT_IN_SCREEN_OLDER_DEVICES} and + * {@link #FLAG_IMMERSIVE_STICKY} * @param listener A listener which is called when the system visibility is changed */ public SystemUiHelper(Activity activity, int level, int flags, - OnVisibilityChangeListener listener) { + OnVisibilityChangeListener listener) { mHandler = new Handler(Looper.getMainLooper()); mHideRunnable = new HideRunnable(); @@ -136,7 +136,7 @@ public final class SystemUiHelper { /** * @return true if the system UI is currently showing. What this means depends on the mode this - * {@link SystemUiHelper} was instantiated with. + * {@link SystemUiHelper} was instantiated with. */ public boolean isShowing() { return mImpl.isShowing(); @@ -174,7 +174,7 @@ public final class SystemUiHelper { *

Any currently queued delayed hide requests will be removed. * * @param delayMillis The delay (in milliseconds) until the Runnable - * will be executed. + * will be executed. */ public void delayHide(long delayMillis) { // Ensure that any currently queued hide calls are removed @@ -208,7 +208,7 @@ public final class SystemUiHelper { * * @param visible True if the system UI is visible. */ - public void onVisibilityChange(boolean visible); + void onVisibilityChange(boolean visible); } static abstract class SystemUiHelperImpl { @@ -221,7 +221,7 @@ public final class SystemUiHelper { boolean mIsShowing = true; SystemUiHelperImpl(Activity activity, int level, int flags, - OnVisibilityChangeListener onVisibilityChangeListener) { + OnVisibilityChangeListener onVisibilityChangeListener) { mActivity = activity; mLevel = level; mFlags = flags; @@ -229,6 +229,7 @@ public final class SystemUiHelper { } abstract void show(); + abstract void hide(); boolean isShowing() { @@ -250,7 +251,7 @@ public final class SystemUiHelper { static class SystemUiHelperImplBase extends SystemUiHelperImpl { SystemUiHelperImplBase(Activity activity, int level, int flags, - OnVisibilityChangeListener onVisibilityChangeListener) { + OnVisibilityChangeListener onVisibilityChangeListener) { super(activity, level, flags, onVisibilityChangeListener); if ((mFlags & SystemUiHelper.FLAG_LAYOUT_IN_SCREEN_OLDER_DEVICES) != 0) { diff --git a/app/src/main/java/com/hippo/util/SystemUiHelperImplHC.java b/app/src/main/java/com/hippo/util/SystemUiHelperImplHC.java index c7195457e..43e782d84 100644 --- a/app/src/main/java/com/hippo/util/SystemUiHelperImplHC.java +++ b/app/src/main/java/com/hippo/util/SystemUiHelperImplHC.java @@ -30,7 +30,7 @@ class SystemUiHelperImplHC extends SystemUiHelper.SystemUiHelperImpl final View mDecorView; SystemUiHelperImplHC(Activity activity, int level, int flags, - SystemUiHelper.OnVisibilityChangeListener onVisibilityChangeListener) { + SystemUiHelper.OnVisibilityChangeListener onVisibilityChangeListener) { super(activity, level, flags, onVisibilityChangeListener); mDecorView = activity.getWindow().getDecorView(); @@ -80,7 +80,7 @@ class SystemUiHelperImplHC extends SystemUiHelper.SystemUiHelperImpl } protected int createShowFlags() { - return View.STATUS_BAR_VISIBLE; + return View.STATUS_BAR_VISIBLE; } protected int createHideFlags() { diff --git a/app/src/main/java/com/hippo/util/SystemUiHelperImplICS.java b/app/src/main/java/com/hippo/util/SystemUiHelperImplICS.java index dcf2e249b..8c88a98c2 100644 --- a/app/src/main/java/com/hippo/util/SystemUiHelperImplICS.java +++ b/app/src/main/java/com/hippo/util/SystemUiHelperImplICS.java @@ -25,7 +25,7 @@ import android.view.View; class SystemUiHelperImplICS extends SystemUiHelperImplHC { SystemUiHelperImplICS(Activity activity, int level, int flags, - SystemUiHelper.OnVisibilityChangeListener onVisibilityChangeListener) { + SystemUiHelper.OnVisibilityChangeListener onVisibilityChangeListener) { super(activity, level, flags, onVisibilityChangeListener); } diff --git a/app/src/main/java/com/hippo/util/SystemUiHelperImplJB.java b/app/src/main/java/com/hippo/util/SystemUiHelperImplJB.java index 26fb0c1f0..b49ffec7a 100644 --- a/app/src/main/java/com/hippo/util/SystemUiHelperImplJB.java +++ b/app/src/main/java/com/hippo/util/SystemUiHelperImplJB.java @@ -26,7 +26,7 @@ import android.view.View; class SystemUiHelperImplJB extends SystemUiHelperImplICS { SystemUiHelperImplJB(Activity activity, int level, int flags, - SystemUiHelper.OnVisibilityChangeListener onVisibilityChangeListener) { + SystemUiHelper.OnVisibilityChangeListener onVisibilityChangeListener) { super(activity, level, flags, onVisibilityChangeListener); } diff --git a/app/src/main/java/com/hippo/util/SystemUiHelperImplKK.java b/app/src/main/java/com/hippo/util/SystemUiHelperImplKK.java index 8bc5be6c3..4198bf322 100644 --- a/app/src/main/java/com/hippo/util/SystemUiHelperImplKK.java +++ b/app/src/main/java/com/hippo/util/SystemUiHelperImplKK.java @@ -25,7 +25,7 @@ import android.view.View; class SystemUiHelperImplKK extends SystemUiHelperImplJB { SystemUiHelperImplKK(Activity activity, int level, int flags, - SystemUiHelper.OnVisibilityChangeListener onVisibilityChangeListener) { + SystemUiHelper.OnVisibilityChangeListener onVisibilityChangeListener) { super(activity, level, flags, onVisibilityChangeListener); } @@ -40,8 +40,8 @@ class SystemUiHelperImplKK extends SystemUiHelperImplJB { // so we only do this on Android 4.4 and later (where IMMERSIVE is // present). flag |= ((mFlags & SystemUiHelper.FLAG_IMMERSIVE_STICKY) != 0) - ? View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY - : View.SYSTEM_UI_FLAG_IMMERSIVE; + ? View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY + : View.SYSTEM_UI_FLAG_IMMERSIVE; } return flag; diff --git a/app/src/main/java/com/hippo/util/TextUrl.java b/app/src/main/java/com/hippo/util/TextUrl.java index a7cd35d3d..284fe722f 100644 --- a/app/src/main/java/com/hippo/util/TextUrl.java +++ b/app/src/main/java/com/hippo/util/TextUrl.java @@ -21,12 +21,13 @@ import android.text.SpannableString; import android.text.style.URLSpan; import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; import java.util.regex.Matcher; import java.util.regex.Pattern; public final class TextUrl { - public static final Charset UTF_8 = Charset.forName("UTF-8"); + public static final Charset UTF_8 = StandardCharsets.UTF_8; private static final Pattern URL_PATTERN = Pattern.compile("(http|https)://[a-z0-9A-Z%-]+(\\.[a-z0-9A-Z%-]+)+(:\\d{1,5})?(/[a-zA-Z0-9-_~:#@!&',;=%/\\*\\.\\?\\+\\$\\[\\]\\(\\)]+)?/?"); diff --git a/app/src/main/java/com/hippo/widget/AutoWrapLayout.java b/app/src/main/java/com/hippo/widget/AutoWrapLayout.java index fca20c477..87608317c 100644 --- a/app/src/main/java/com/hippo/widget/AutoWrapLayout.java +++ b/app/src/main/java/com/hippo/widget/AutoWrapLayout.java @@ -33,29 +33,14 @@ import java.util.List; * A ViewGroup that can layout views in line and auto wrap * * @author Hippo - * */ public class AutoWrapLayout extends ViewGroup { + private static final Alignment[] sBaseLineArray = {Alignment.TOP, + Alignment.CENTER, Alignment.BOTTOM}; private final List rectList = new ArrayList<>(); - private Alignment mAlignment; - private static final Alignment[] sBaseLineArray = { Alignment.TOP, - Alignment.CENTER, Alignment.BOTTOM }; - - - public enum Alignment { - TOP(0), - CENTER(1), - BOTTOM(2); - - Alignment(int ni) { - nativeInt = ni; - } - - final int nativeInt; - } public AutoWrapLayout(Context context) { super(context); @@ -79,6 +64,10 @@ public class AutoWrapLayout extends ViewGroup { a.recycle(); } + public Alignment getAlignment() { + return mAlignment; + } + public void setAlignment(Alignment baseLine) { if (baseLine == null) { return; @@ -92,10 +81,6 @@ public class AutoWrapLayout extends ViewGroup { } } - public Alignment getAlignment() { - return mAlignment; - } - private void adjustBaseLine(int lineHeight, int startIndex, int endIndex) { if (mAlignment == Alignment.TOP) return; @@ -103,21 +88,19 @@ public class AutoWrapLayout extends ViewGroup { for (int index = startIndex; index < endIndex; index++) { final View child = getChildAt(index); final MarginLayoutParams lp = - (MarginLayoutParams)child.getLayoutParams(); + (MarginLayoutParams) child.getLayoutParams(); Rect rect = rectList.get(index); int offsetRaw = lineHeight - rect.height() - lp.topMargin - lp.bottomMargin; if (mAlignment == Alignment.CENTER) - rect.offset(0, offsetRaw/2); + rect.offset(0, offsetRaw / 2); else if (mAlignment == Alignment.BOTTOM) rect.offset(0, offsetRaw); } } - - // TODO Take vertical mode /** * each row or line at least show one child - * + *

* horizontal only show child can show or partly show in parent */ @SuppressLint("DrawAllocation") @@ -165,7 +148,7 @@ public class AutoWrapLayout extends ViewGroup { if (child.getVisibility() == View.GONE) continue; final MarginLayoutParams lp = - (MarginLayoutParams)child.getLayoutParams(); + (MarginLayoutParams) child.getLayoutParams(); childWidth = child.getMeasuredWidth(); childHeight = child.getMeasuredHeight(); @@ -239,10 +222,13 @@ public class AutoWrapLayout extends ViewGroup { setMeasuredDimension(measuredWidth, measuredHeight); } + + // TODO Take vertical mode + @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { final int count = rectList.size(); - for(int i = 0; i < count; i++){ + for (int i = 0; i < count; i++) { final View child = this.getChildAt(i); if (child.getVisibility() == View.GONE) continue; @@ -265,4 +251,16 @@ public class AutoWrapLayout extends ViewGroup { protected MarginLayoutParams generateLayoutParams(ViewGroup.LayoutParams p) { return new MarginLayoutParams(p); } + + public enum Alignment { + TOP(0), + CENTER(1), + BOTTOM(2); + + final int nativeInt; + + Alignment(int ni) { + nativeInt = ni; + } + } } diff --git a/app/src/main/java/com/hippo/widget/BatteryView.java b/app/src/main/java/com/hippo/widget/BatteryView.java index d3e04a19d..c7ab2b7bd 100644 --- a/app/src/main/java/com/hippo/widget/BatteryView.java +++ b/app/src/main/java/com/hippo/widget/BatteryView.java @@ -25,7 +25,9 @@ import android.content.res.TypedArray; import android.graphics.Color; import android.os.BatteryManager; import android.util.AttributeSet; + import androidx.appcompat.widget.AppCompatTextView; + import com.hippo.drawable.BatteryDrawable; import com.hippo.ehviewer.R; @@ -39,10 +41,22 @@ public class BatteryView extends AppCompatTextView { private boolean mCharging = false; private BatteryDrawable mDrawable; + private final Runnable mCharger = new Runnable() { + private int level = 0; + + @Override + public void run() { + level += 2; + if (level > 100) { + level = 0; + } + mDrawable.setElect(level, false); + getHandler().postDelayed(mCharger, 200); + } + }; private boolean mAttached = false; private boolean mIsChargerWorking = false; - private final BroadcastReceiver mIntentReceiver = new BroadcastReceiver() { @Override @@ -74,21 +88,6 @@ public class BatteryView extends AppCompatTextView { } }; - private final Runnable mCharger = new Runnable() { - - private int level = 0; - - @Override - public void run() { - level += 2; - if (level > 100) { - level = 0; - } - mDrawable.setElect(level, false); - getHandler().postDelayed(mCharger, 200); - } - }; - public BatteryView(Context context) { super(context); init(); diff --git a/app/src/main/java/com/hippo/widget/CheckTextView.java b/app/src/main/java/com/hippo/widget/CheckTextView.java index 92d653597..2f345e9d4 100644 --- a/app/src/main/java/com/hippo/widget/CheckTextView.java +++ b/app/src/main/java/com/hippo/widget/CheckTextView.java @@ -30,8 +30,10 @@ import android.util.AttributeSet; import android.view.View; import android.view.View.OnClickListener; import android.view.animation.Interpolator; + import androidx.annotation.NonNull; import androidx.appcompat.widget.AppCompatTextView; + import com.hippo.ehviewer.R; import com.hippo.hotspot.Hotspot; import com.hippo.hotspot.Hotspotable; @@ -45,19 +47,20 @@ public class CheckTextView extends AppCompatTextView implements OnClickListener, private static final String STATE_KEY_CHECKED = "checked"; private static final long ANIMATION_DURATION = 200; - + Animator mAnimator; + private final Animator.AnimatorListener mAnimatorListener = new SimpleAnimatorListener() { + @Override + public void onAnimationEnd(Animator animation) { + mAnimator = null; + } + }; private int mMaskColor; - private boolean mChecked = false; private boolean mPrepareAnimator = false; - private Paint mPaint; private float mRadius = 0f; private float mX; private float mY; - - Animator mAnimator; - private float mMaxRadius; public CheckTextView(Context context, AttributeSet attrs) { @@ -100,23 +103,16 @@ public class CheckTextView extends AppCompatTextView implements OnClickListener, mMaxRadius = MathUtils.coverageRadius(getWidth(), getHeight(), x, y); } + public float getRadius() { + return mRadius; + } + public void setRadius(float radius) { float bigger = Math.max(mRadius, radius); mRadius = radius; invalidate((int) (mX - bigger), (int) (mY - bigger), (int) (mX + bigger), (int) (mY + bigger)); } - public float getRadius() { - return mRadius; - } - - private final Animator.AnimatorListener mAnimatorListener = new SimpleAnimatorListener() { - @Override - public void onAnimationEnd(Animator animation) { - mAnimator = null; - } - }; - public void prepareAnimations() { mPrepareAnimator = true; } @@ -174,14 +170,10 @@ public class CheckTextView extends AppCompatTextView implements OnClickListener, } } - public void setChecked(boolean checked) { - setChecked(checked, true); - } - /** * Changes the checked state of this CheckTextView. * - * @param checked checked true to check the CheckTextView, false to uncheck it + * @param checked checked true to check the CheckTextView, false to uncheck it * @param animation true for show animation */ public void setChecked(boolean checked, boolean animation) { @@ -204,6 +196,10 @@ public class CheckTextView extends AppCompatTextView implements OnClickListener, return mChecked; } + public void setChecked(boolean checked) { + setChecked(checked, true); + } + @Override public Parcelable onSaveInstanceState() { final Bundle state = new Bundle(); diff --git a/app/src/main/java/com/hippo/widget/ContentLayout.java b/app/src/main/java/com/hippo/widget/ContentLayout.java index 49e0a5db3..9cf91019c 100644 --- a/app/src/main/java/com/hippo/widget/ContentLayout.java +++ b/app/src/main/java/com/hippo/widget/ContentLayout.java @@ -28,9 +28,11 @@ import android.view.ViewGroup; import android.widget.FrameLayout; import android.widget.TextView; import android.widget.Toast; + import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.recyclerview.widget.RecyclerView; + import com.hippo.android.resource.AttrResources; import com.hippo.easyrecyclerview.EasyRecyclerView; import com.hippo.easyrecyclerview.FastScroller; @@ -45,6 +47,7 @@ import com.hippo.view.ViewTransition; import com.hippo.yorozuya.IntIdGenerator; import com.hippo.yorozuya.LayoutUtils; import com.hippo.yorozuya.collect.IntList; + import java.util.ArrayList; import java.util.Collections; import java.util.Iterator; @@ -167,10 +170,18 @@ public class ContentLayout extends FrameLayout { public abstract static class ContentHelper implements ViewTransition.OnShowViewListener { + public static final int TYPE_REFRESH = 0; + public static final int TYPE_PRE_PAGE = 1; + public static final int TYPE_PRE_PAGE_KEEP_POS = 2; + public static final int TYPE_NEXT_PAGE = 3; + public static final int TYPE_NEXT_PAGE_KEEP_POS = 4; + public static final int TYPE_SOMEWHERE = 5; + public static final int TYPE_REFRESH_PAGE = 6; + public static final int REFRESH_TYPE_HEADER = 0; + public static final int REFRESH_TYPE_FOOTER = 1; + public static final int REFRESH_TYPE_PROGRESS_VIEW = 2; private static final String TAG = ContentHelper.class.getSimpleName(); - private static final int CHECK_DUPLICATE_RANGE = 50; - private static final String KEY_SUPER = "super"; private static final String KEY_SHOWN_VIEW = "shown_view"; private static final String KEY_TIP = "tip"; @@ -180,83 +191,53 @@ public class ContentLayout extends FrameLayout { private static final String KEY_START_PAGE = "start_page"; private static final String KEY_END_PAGE = "end_page"; private static final String KEY_PAGES = "pages"; - - public static final int TYPE_REFRESH = 0; - public static final int TYPE_PRE_PAGE = 1; - public static final int TYPE_PRE_PAGE_KEEP_POS = 2; - public static final int TYPE_NEXT_PAGE = 3; - public static final int TYPE_NEXT_PAGE_KEEP_POS = 4; - public static final int TYPE_SOMEWHERE = 5; - public static final int TYPE_REFRESH_PAGE = 6; - - public static final int REFRESH_TYPE_HEADER = 0; - public static final int REFRESH_TYPE_FOOTER = 1; - public static final int REFRESH_TYPE_PROGRESS_VIEW = 2; - - private ProgressView mProgressView; - private TextView mTipView; - private ViewGroup mContentView; - - private RefreshLayout mRefreshLayout; - private EasyRecyclerView mRecyclerView; - - private ViewTransition mViewTransition; - - /** - * Store data - */ - private ArrayList mData = new ArrayList<>(); - /** * Generate task id */ private final IntIdGenerator mIdGenerator = new IntIdGenerator(); - + private final LayoutManagerUtils.OnScrollToPositionListener mOnScrollToPositionListener = + new LayoutManagerUtils.OnScrollToPositionListener() { + @Override + public void onScrollToPosition(int position) { + ContentHelper.this.onScrollToPosition(position); + } + }; + private ProgressView mProgressView; + private TextView mTipView; + private ViewGroup mContentView; + private RefreshLayout mRefreshLayout; + private EasyRecyclerView mRecyclerView; + private ViewTransition mViewTransition; + /** + * Store data + */ + private ArrayList mData = new ArrayList<>(); /** * Store the page divider index - * + *

* For example, the data contain page 3, page 4, page 5, * page 3 size is 7, page 4 size is 8, page 5 size is 9, * so mPageDivider contain 7, 15, 24. */ private IntList mPageDivider = new IntList(); - /** * The first page in mData */ private int mStartPage; - /** * The last page + 1 in mData */ private int mEndPage; - /** * The available page count. */ private int mPages; - private int mNextPage; - private int mCurrentTaskId; private int mCurrentTaskType; private int mCurrentTaskPage; - private int mNextPageScrollSize; - private String mEmptyString = "No hint"; - - private final RecyclerView.OnScrollListener mOnScrollListener = new RecyclerView.OnScrollListener() { - @Override - public void onScrolled(RecyclerView recyclerView, int dx, int dy) { - if (!mRefreshLayout.isRefreshing() && mRefreshLayout.isAlmostBottom() && mEndPage < mPages) { - // Get next page - mRefreshLayout.setFooterRefreshing(true); - mOnRefreshListener.onFooterRefresh(); - } - } - }; - private final RefreshLayout.OnRefreshListener mOnRefreshListener = new RefreshLayout.OnRefreshListener() { @Override public void onHeaderRefresh() { @@ -297,14 +278,17 @@ public class ContentLayout extends FrameLayout { } } }; - - private final LayoutManagerUtils.OnScrollToPositionListener mOnScrollToPositionListener = - new LayoutManagerUtils.OnScrollToPositionListener() { - @Override - public void onScrollToPosition(int position) { - ContentHelper.this.onScrollToPosition(position); - } - }; + private final RecyclerView.OnScrollListener mOnScrollListener = new RecyclerView.OnScrollListener() { + @Override + public void onScrolled(RecyclerView recyclerView, int dx, int dy) { + if (!mRefreshLayout.isRefreshing() && mRefreshLayout.isAlmostBottom() && mEndPage < mPages) { + // Get next page + mRefreshLayout.setFooterRefreshing(true); + mOnRefreshListener.onFooterRefresh(); + } + } + }; + private int mSavedDataId = IntIdGenerator.INVALID_ID; private void init(ContentLayout contentLayout) { mNextPageScrollSize = LayoutUtils.dp2pix(contentLayout.getContext(), 48); @@ -338,7 +322,7 @@ public class ContentLayout extends FrameLayout { * Call {@link #onGetPageData(int, int, int, List)} when get data * * @param taskId task id - * @param page the page to get + * @param page the page to get */ protected abstract void getPageData(int taskId, int type, int page); @@ -350,10 +334,12 @@ public class ContentLayout extends FrameLayout { protected abstract void notifyItemRangeInserted(int positionStart, int itemCount); - protected void onScrollToPosition(int postion) {} + protected void onScrollToPosition(int postion) { + } @Override - public void onShowView(View hiddenView, View shownView) {} + public void onShowView(View hiddenView, View shownView) { + } public int getShownViewIndex() { return mViewTransition.getShownViewIndex(); @@ -376,8 +362,7 @@ public class ContentLayout extends FrameLayout { } /** - * @throws IndexOutOfBoundsException - * if {@code location < 0 || location >= size()} + * @throws IndexOutOfBoundsException if {@code location < 0 || location >= size()} */ public E getDataAt(int location) { return mData.get(location); @@ -437,7 +422,7 @@ public class ContentLayout extends FrameLayout { private void removeDuplicateData(List data, int start, int end) { start = Math.max(0, start); end = Math.min(mData.size(), end); - for (Iterator iterator = data.iterator(); iterator.hasNext();) { + for (Iterator iterator = data.iterator(); iterator.hasNext(); ) { E d = iterator.next(); for (int i = start; i < end; i++) { if (isDuplicate(d, mData.get(i))) { @@ -448,15 +433,20 @@ public class ContentLayout extends FrameLayout { } } - protected void onAddData(E data) { } + protected void onAddData(E data) { + } - protected void onAddData(List data) { } + protected void onAddData(List data) { + } - protected void onRemoveData(E data) { } + protected void onRemoveData(E data) { + } - protected void onRemoveData(List data) { } + protected void onRemoveData(List data) { + } - protected void onClearData() { } + protected void onClearData() { + } public void onGetPageData(int taskId, int pages, int nextPage, List data) { if (mCurrentTaskId == taskId) { @@ -927,8 +917,6 @@ public class ContentLayout extends FrameLayout { } } - private int mSavedDataId = IntIdGenerator.INVALID_ID; - protected Parcelable saveInstanceState(Parcelable superState) { Bundle bundle = new Bundle(); bundle.putParcelable(KEY_SUPER, superState); diff --git a/app/src/main/java/com/hippo/widget/CuteSpinner.java b/app/src/main/java/com/hippo/widget/CuteSpinner.java index ce8119f46..60ed2aab7 100644 --- a/app/src/main/java/com/hippo/widget/CuteSpinner.java +++ b/app/src/main/java/com/hippo/widget/CuteSpinner.java @@ -21,7 +21,9 @@ import android.content.res.Resources; import android.content.res.TypedArray; import android.util.AttributeSet; import android.widget.ArrayAdapter; + import androidx.appcompat.widget.AppCompatSpinner; + import com.hippo.ehviewer.R; public class CuteSpinner extends AppCompatSpinner { diff --git a/app/src/main/java/com/hippo/widget/DateUtils.java b/app/src/main/java/com/hippo/widget/DateUtils.java index 967214fe6..74f6ce608 100644 --- a/app/src/main/java/com/hippo/widget/DateUtils.java +++ b/app/src/main/java/com/hippo/widget/DateUtils.java @@ -18,8 +18,8 @@ package com.hippo.widget; final class DateUtils { - public static final char QUOTE = '\''; - public static final char SECONDS = 's'; + public static final char QUOTE = '\''; + public static final char SECONDS = 's'; public static boolean hasSeconds(CharSequence inFormat) { return hasDesignator(inFormat, SECONDS); diff --git a/app/src/main/java/com/hippo/widget/DirExplorer.java b/app/src/main/java/com/hippo/widget/DirExplorer.java index 5cad27e29..8a605c1c7 100644 --- a/app/src/main/java/com/hippo/widget/DirExplorer.java +++ b/app/src/main/java/com/hippo/widget/DirExplorer.java @@ -25,13 +25,16 @@ import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.TextView; + import androidx.recyclerview.widget.LinearLayoutManager; + import com.hippo.android.resource.AttrResources; import com.hippo.easyrecyclerview.EasyRecyclerView; import com.hippo.easyrecyclerview.LinearDividerItemDecoration; import com.hippo.ehviewer.R; import com.hippo.ripple.Ripple; import com.hippo.yorozuya.LayoutUtils; + import java.io.File; import java.io.FileFilter; import java.util.ArrayList; @@ -46,10 +49,8 @@ public class DirExplorer extends EasyRecyclerView implements EasyRecyclerView.On private static final File PARENT_DIR = null; private static final String PARENT_DIR_NAME = ".."; - - private File mCurrentFile; private final List mFiles = new ArrayList<>(); - + private File mCurrentFile; private DirAdapter mAdapter; private OnChangeDirListener mOnChangeDirListener; @@ -133,33 +134,9 @@ public class DirExplorer extends EasyRecyclerView implements EasyRecyclerView.On return true; } - private class DirHolder extends ViewHolder { + public interface OnChangeDirListener { - public TextView textView; - - public DirHolder(View itemView) { - super(itemView); - textView = (TextView) itemView; - } - } - - private class DirAdapter extends Adapter { - - @Override - public DirHolder onCreateViewHolder(ViewGroup parent, int viewType) { - return new DirHolder( LayoutInflater.from(getContext()).inflate(R.layout.item_dir_explorer, parent, false)); - } - - @Override - public void onBindViewHolder(DirHolder holder, int position) { - File file = mFiles.get(position); - holder.textView.setText(file == PARENT_DIR ? PARENT_DIR_NAME : file.getName()); - } - - @Override - public int getItemCount() { - return mFiles.size(); - } + void onChangeDir(File dir); } static class DirFilter implements FileFilter { @@ -182,8 +159,32 @@ public class DirExplorer extends EasyRecyclerView implements EasyRecyclerView.On } } - public interface OnChangeDirListener { + private class DirHolder extends ViewHolder { - void onChangeDir(File dir); + public TextView textView; + + public DirHolder(View itemView) { + super(itemView); + textView = (TextView) itemView; + } + } + + private class DirAdapter extends Adapter { + + @Override + public DirHolder onCreateViewHolder(ViewGroup parent, int viewType) { + return new DirHolder(LayoutInflater.from(getContext()).inflate(R.layout.item_dir_explorer, parent, false)); + } + + @Override + public void onBindViewHolder(DirHolder holder, int position) { + File file = mFiles.get(position); + holder.textView.setText(file == PARENT_DIR ? PARENT_DIR_NAME : file.getName()); + } + + @Override + public int getItemCount() { + return mFiles.size(); + } } } diff --git a/app/src/main/java/com/hippo/widget/DrawerView.java b/app/src/main/java/com/hippo/widget/DrawerView.java index 4b29dcb27..e9888c54d 100644 --- a/app/src/main/java/com/hippo/widget/DrawerView.java +++ b/app/src/main/java/com/hippo/widget/DrawerView.java @@ -27,7 +27,7 @@ public class DrawerView extends FrameLayout { private static final int DEFAULT_MAX_WIDTH = 280; - private static final int[] SIZE_ATTRS = new int[] { + private static final int[] SIZE_ATTRS = new int[]{ android.R.attr.maxWidth }; diff --git a/app/src/main/java/com/hippo/widget/FabLayout.java b/app/src/main/java/com/hippo/widget/FabLayout.java index cc8b7f672..c37a9030a 100644 --- a/app/src/main/java/com/hippo/widget/FabLayout.java +++ b/app/src/main/java/com/hippo/widget/FabLayout.java @@ -24,7 +24,9 @@ import android.util.AttributeSet; import android.view.View; import android.view.ViewGroup; import android.view.animation.Interpolator; + import androidx.annotation.NonNull; + import com.google.android.material.floatingactionbutton.FloatingActionButton; import com.hippo.ehviewer.R; import com.hippo.yorozuya.AnimationUtils; @@ -80,7 +82,7 @@ public class FabLayout extends ViewGroup implements View.OnClickListener { public void addView(@NonNull View child, int index, ViewGroup.LayoutParams params) { if (!(child instanceof FloatingActionButton)) { throw new IllegalStateException("FloatingActionBarLayout should only " + - "contain FloatingActionButton, but try to add "+ child.getClass().getName()); + "contain FloatingActionButton, but try to add " + child.getClass().getName()); } super.addView(child, index, params); } @@ -145,7 +147,7 @@ public class FabLayout extends ViewGroup implements View.OnClickListener { int bottom = getMeasuredHeight() - getPaddingBottom(); int count = getChildCount(); int i = count; - while(--i >= 0) { + while (--i >= 0) { View child = getChildAt(i); if (child.getVisibility() == View.GONE) { continue; @@ -288,7 +290,7 @@ public class FabLayout extends ViewGroup implements View.OnClickListener { } else { startRotation = 0.0f; endRotation = 0.0f; - startScale = 1.0f; + startScale = 1.0f; endScale = 0.0f; interpolator = AnimationUtils.SLOW_FAST_INTERPOLATOR; } diff --git a/app/src/main/java/com/hippo/widget/FixedAspectImageView.java b/app/src/main/java/com/hippo/widget/FixedAspectImageView.java index 471da0a6f..ed03b20e0 100644 --- a/app/src/main/java/com/hippo/widget/FixedAspectImageView.java +++ b/app/src/main/java/com/hippo/widget/FixedAspectImageView.java @@ -21,7 +21,9 @@ import android.content.res.TypedArray; import android.graphics.drawable.Drawable; import android.util.AttributeSet; import android.view.View; + import androidx.appcompat.widget.AppCompatImageView; + import com.hippo.ehviewer.R; import com.hippo.yorozuya.MathUtils; @@ -114,6 +116,10 @@ public class FixedAspectImageView extends AppCompatImageView { mAdjustViewBounds = adjustViewBounds; } + public float getAspect() { + return mAspect; + } + /** * Enable aspect will set AdjustViewBounds true. * Any negative float to disable it, @@ -130,15 +136,11 @@ public class FixedAspectImageView extends AppCompatImageView { requestLayout(); } - public float getAspect() { - return mAspect; - } - private int resolveAdjustedSize(int desiredSize, int minSize, int maxSize, - int measureSpec) { + int measureSpec) { int result = desiredSize; int specMode = MeasureSpec.getMode(measureSpec); - int specSize = MeasureSpec.getSize(measureSpec); + int specSize = MeasureSpec.getSize(measureSpec); switch (specMode) { case MeasureSpec.UNSPECIFIED: // Parent says we can be as big as we want. Just don't be smaller @@ -161,7 +163,7 @@ public class FixedAspectImageView extends AppCompatImageView { private boolean isSizeAcceptable(int size, int minSize, int maxSize, int measureSpec) { int specMode = MeasureSpec.getMode(measureSpec); - int specSize = MeasureSpec.getSize(measureSpec); + int specSize = MeasureSpec.getSize(measureSpec); switch (specMode) { case MeasureSpec.UNSPECIFIED: // Parent says we can be as big as we want. Just don't be smaller @@ -250,20 +252,20 @@ public class FixedAspectImageView extends AppCompatImageView { if (desiredAspect != 0.0f) { // See what our actual aspect ratio is - float actualAspect = (float)(widthSize - pLeft - pRight) / - (heightSize - pTop - pBottom); + float actualAspect = (float) (widthSize - pLeft - pRight) / + (heightSize - pTop - pBottom); if (Math.abs(actualAspect - desiredAspect) > 0.0000001) { boolean done = false; // Try adjusting width to be proportional to height if (resizeWidth) { - int newWidth = (int)(desiredAspect * (heightSize - pTop - pBottom)) + + int newWidth = (int) (desiredAspect * (heightSize - pTop - pBottom)) + pLeft + pRight; // Allow the width to outgrow its original estimate if height is fixed. //if (!resizeHeight) { - //widthSize = resolveAdjustedSize(newWidth, mMinWidth, mMaxWidth, widthMeasureSpec); + //widthSize = resolveAdjustedSize(newWidth, mMinWidth, mMaxWidth, widthMeasureSpec); //} if (isSizeAcceptable(newWidth, mMinWidth, mMaxWidth, widthMeasureSpec)) { @@ -274,7 +276,7 @@ public class FixedAspectImageView extends AppCompatImageView { // Try adjusting height to be proportional to width if (!done && resizeHeight) { - int newHeight = (int)((widthSize - pLeft - pRight) / desiredAspect) + + int newHeight = (int) ((widthSize - pLeft - pRight) / desiredAspect) + pTop + pBottom; // Allow the height to outgrow its original estimate if width is fixed. diff --git a/app/src/main/java/com/hippo/widget/HackyTextInputEditText.java b/app/src/main/java/com/hippo/widget/HackyTextInputEditText.java index b8dd51bb9..f6bd1dcd0 100644 --- a/app/src/main/java/com/hippo/widget/HackyTextInputEditText.java +++ b/app/src/main/java/com/hippo/widget/HackyTextInputEditText.java @@ -19,8 +19,10 @@ package com.hippo.widget; import android.content.Context; import android.util.AttributeSet; import android.widget.TextView; + import com.google.android.material.textfield.TextInputEditText; import com.hippo.util.ExceptionUtils; + import java.lang.reflect.Field; import java.lang.reflect.Method; @@ -29,56 +31,56 @@ import java.lang.reflect.Method; // https://stackoverflow.com/questions/51891415/nullpointerexception-on-meizu-devices-in-editor-updatecursorpositionmz/52001305 public class HackyTextInputEditText extends TextInputEditText { - private static final boolean HAS_METHOD_UPDATE_CURSOR_POSITION_MZ; - private static final Field FIELD_M_HINT; + private static final boolean HAS_METHOD_UPDATE_CURSOR_POSITION_MZ; + private static final Field FIELD_M_HINT; - static { - boolean hasMethodUpdateCursorPositionMz = false; - try { - Class clazz = ClassLoader.getSystemClassLoader().loadClass("android.widget.Editor"); - Method[] methods = clazz.getDeclaredMethods(); - for (Method method : methods) { - if ("updateCursorPositionMz".equals(method.getName())) { - hasMethodUpdateCursorPositionMz = true; + static { + boolean hasMethodUpdateCursorPositionMz = false; + try { + Class clazz = ClassLoader.getSystemClassLoader().loadClass("android.widget.Editor"); + Method[] methods = clazz.getDeclaredMethods(); + for (Method method : methods) { + if ("updateCursorPositionMz".equals(method.getName())) { + hasMethodUpdateCursorPositionMz = true; + } + } + } catch (Throwable e) { + ExceptionUtils.throwIfFatal(e); } - } - } catch (Throwable e) { - ExceptionUtils.throwIfFatal(e); + + Field fieldMHint = null; + if (hasMethodUpdateCursorPositionMz) { + try { + fieldMHint = TextView.class.getDeclaredField("mHint"); + fieldMHint.setAccessible(true); + } catch (Throwable e) { + ExceptionUtils.throwIfFatal(e); + } + } + + HAS_METHOD_UPDATE_CURSOR_POSITION_MZ = hasMethodUpdateCursorPositionMz; + FIELD_M_HINT = fieldMHint; } - Field fieldMHint = null; - if (hasMethodUpdateCursorPositionMz) { - try { - fieldMHint = TextView.class.getDeclaredField("mHint"); - fieldMHint.setAccessible(true); - } catch (Throwable e) { - ExceptionUtils.throwIfFatal(e); - } + public HackyTextInputEditText(Context context, AttributeSet attrs) { + super(context, attrs); } - HAS_METHOD_UPDATE_CURSOR_POSITION_MZ = hasMethodUpdateCursorPositionMz; - FIELD_M_HINT = fieldMHint; - } - - public HackyTextInputEditText(Context context, AttributeSet attrs) { - super(context, attrs); - } - - @Override - public CharSequence getHint() { - if (HAS_METHOD_UPDATE_CURSOR_POSITION_MZ && FIELD_M_HINT != null) { - try { - return getSuperHint(); - } catch (Throwable e) { - ExceptionUtils.throwIfFatal(e); - return super.getHint(); - } - } else { - return super.getHint(); + @Override + public CharSequence getHint() { + if (HAS_METHOD_UPDATE_CURSOR_POSITION_MZ && FIELD_M_HINT != null) { + try { + return getSuperHint(); + } catch (Throwable e) { + ExceptionUtils.throwIfFatal(e); + return super.getHint(); + } + } else { + return super.getHint(); + } } - } - private CharSequence getSuperHint() throws IllegalAccessException { - return (CharSequence) FIELD_M_HINT.get(this); - } + private CharSequence getSuperHint() throws IllegalAccessException { + return (CharSequence) FIELD_M_HINT.get(this); + } } diff --git a/app/src/main/java/com/hippo/widget/IndicatingListView.java b/app/src/main/java/com/hippo/widget/IndicatingListView.java index 1ce79e20e..b81e912eb 100644 --- a/app/src/main/java/com/hippo/widget/IndicatingListView.java +++ b/app/src/main/java/com/hippo/widget/IndicatingListView.java @@ -24,15 +24,16 @@ import android.graphics.Paint; import android.graphics.Rect; import android.util.AttributeSet; import android.widget.ListView; + import androidx.annotation.NonNull; + import com.hippo.ehviewer.R; public class IndicatingListView extends ListView { - private int mIndicatorHeight; - private final Paint mPaint = new Paint(); private final Rect mTemp = new Rect(); + private int mIndicatorHeight; public IndicatingListView(Context context) { super(context); diff --git a/app/src/main/java/com/hippo/widget/IndicatingScrollView.java b/app/src/main/java/com/hippo/widget/IndicatingScrollView.java index a503a24b9..315581a6d 100644 --- a/app/src/main/java/com/hippo/widget/IndicatingScrollView.java +++ b/app/src/main/java/com/hippo/widget/IndicatingScrollView.java @@ -24,15 +24,16 @@ import android.graphics.Paint; import android.graphics.Rect; import android.util.AttributeSet; import android.widget.ScrollView; + import androidx.annotation.NonNull; + import com.hippo.ehviewer.R; public class IndicatingScrollView extends ScrollView { - private int mIndicatorHeight; - private final Paint mPaint = new Paint(); private final Rect mTemp = new Rect(); + private int mIndicatorHeight; public IndicatingScrollView(Context context) { super(context); diff --git a/app/src/main/java/com/hippo/widget/LinkifyTextView.java b/app/src/main/java/com/hippo/widget/LinkifyTextView.java index 45eb356e5..833827cf3 100644 --- a/app/src/main/java/com/hippo/widget/LinkifyTextView.java +++ b/app/src/main/java/com/hippo/widget/LinkifyTextView.java @@ -22,6 +22,7 @@ import android.text.Spanned; import android.text.style.ClickableSpan; import android.util.AttributeSet; import android.view.MotionEvent; + import androidx.annotation.NonNull; public class LinkifyTextView extends ObservedTextView { @@ -75,7 +76,7 @@ public class LinkifyTextView extends ObservedTextView { int line = layout.getLineForVertical(y); int off = layout.getOffsetForHorizontal(line, x); - ClickableSpan[] spans = ((Spanned)getText()).getSpans(off, off, ClickableSpan.class); + ClickableSpan[] spans = ((Spanned) getText()).getSpans(off, off, ClickableSpan.class); if (spans.length > 0) { mCurrentSpan = spans[0]; diff --git a/app/src/main/java/com/hippo/widget/LoadImageView.java b/app/src/main/java/com/hippo/widget/LoadImageView.java index 41a3b98b4..28bbd5050 100644 --- a/app/src/main/java/com/hippo/widget/LoadImageView.java +++ b/app/src/main/java/com/hippo/widget/LoadImageView.java @@ -26,9 +26,11 @@ import android.graphics.drawable.TransitionDrawable; import android.util.AttributeSet; import android.util.Log; import android.view.View; + import androidx.annotation.DrawableRes; import androidx.annotation.IntDef; import androidx.annotation.NonNull; + import com.hippo.conaco.Conaco; import com.hippo.conaco.ConacoTask; import com.hippo.conaco.DataContainer; @@ -40,38 +42,28 @@ import com.hippo.image.ImageBitmap; import com.hippo.image.ImageDrawable; import com.hippo.image.RecycledException; import com.hippo.util.DrawableManager; + import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; public class LoadImageView extends FixedAspectImageView implements Unikery, View.OnClickListener, View.OnLongClickListener, Animatable { - private static final String TAG = LoadImageView.class.getSimpleName(); - - @IntDef({RETRY_TYPE_NONE, RETRY_TYPE_CLICK, RETRY_TYPE_LONG_CLICK}) - @Retention(RetentionPolicy.SOURCE) - private @interface RetryType {} - public static final int RETRY_TYPE_NONE = 0; public static final int RETRY_TYPE_CLICK = 1; public static final int RETRY_TYPE_LONG_CLICK = 2; - + private static final String TAG = LoadImageView.class.getSimpleName(); private int mTaskId = Unikery.INVALID_ID; - private Conaco mConaco; - private String mKey; private String mUrl; private DataContainer mContainer; private boolean mUseNetwork; - private int mOffsetX = Integer.MIN_VALUE; private int mOffsetY = Integer.MIN_VALUE; private int mClipWidth = Integer.MIN_VALUE; private int mClipHeight = Integer.MIN_VALUE; - private int mRetryType; - private boolean mFailed; public LoadImageView(Context context) { @@ -101,13 +93,13 @@ public class LoadImageView extends FixedAspectImageView implements Unikery * * @param id the unique id of the radio button to select in this group - * * @see #getCheckedRadioButtonId() * @see #clearCheck() */ @@ -153,7 +154,6 @@ public class RadioGridGroup extends SimpleGridLayout { * Upon empty selection, the returned value is -1.

* * @return the unique id of the selected radio button in this group - * * @see #check(int) * @see #clearCheck() */ @@ -193,7 +193,7 @@ public class RadioGridGroup extends SimpleGridLayout { *

Called when the checked radio button has changed. When the * selection is cleared, checkedId is -1.

* - * @param group the group in which the checked radio button has changed + * @param group the group in which the checked radio button has changed * @param checkedId the unique identifier of the newly checked radio button */ void onCheckedChanged(RadioGridGroup group, @IdRes int checkedId); diff --git a/app/src/main/java/com/hippo/widget/SafeCoordinatorLayout.java b/app/src/main/java/com/hippo/widget/SafeCoordinatorLayout.java index 339014964..4b22fa688 100644 --- a/app/src/main/java/com/hippo/widget/SafeCoordinatorLayout.java +++ b/app/src/main/java/com/hippo/widget/SafeCoordinatorLayout.java @@ -19,7 +19,9 @@ package com.hippo.widget; import android.content.Context; import android.util.AttributeSet; import android.view.MotionEvent; + import androidx.coordinatorlayout.widget.CoordinatorLayout; + import com.hippo.util.ExceptionUtils; public class SafeCoordinatorLayout extends CoordinatorLayout { diff --git a/app/src/main/java/com/hippo/widget/SearchBarMover.java b/app/src/main/java/com/hippo/widget/SearchBarMover.java index 928726fde..d910262b0 100644 --- a/app/src/main/java/com/hippo/widget/SearchBarMover.java +++ b/app/src/main/java/com/hippo/widget/SearchBarMover.java @@ -19,8 +19,10 @@ package com.hippo.widget; import android.animation.Animator; import android.animation.ValueAnimator; import android.view.View; + import androidx.annotation.Nullable; import androidx.recyclerview.widget.RecyclerView; + import com.hippo.yorozuya.MathUtils; import com.hippo.yorozuya.SimpleAnimatorListener; import com.hippo.yorozuya.ViewUtils; @@ -28,16 +30,15 @@ import com.hippo.yorozuya.ViewUtils; public class SearchBarMover extends RecyclerView.OnScrollListener { private static final long ANIMATE_TIME = 300L; - - private boolean mShow; - private ValueAnimator mSearchBarMoveAnimator; private final Helper mHelper; private final View mSearchBar; + private boolean mShow; + private ValueAnimator mSearchBarMoveAnimator; public SearchBarMover(Helper helper, View searchBar, RecyclerView... recyclerViews) { mHelper = helper; mSearchBar = searchBar; - for (RecyclerView recyclerView: recyclerViews) { + for (RecyclerView recyclerView : recyclerViews) { recyclerView.addOnScrollListener(this); } } @@ -49,7 +50,7 @@ public class SearchBarMover extends RecyclerView.OnScrollListener { } @Override - public void onScrollStateChanged(RecyclerView recyclerView, int newState){ + public void onScrollStateChanged(RecyclerView recyclerView, int newState) { if (newState == RecyclerView.SCROLL_STATE_IDLE && mHelper.isValidView(recyclerView)) { returnSearchBarPosition(); } @@ -87,7 +88,7 @@ public class SearchBarMover extends RecyclerView.OnScrollListener { } if (!recyclerView.isShown()) { show = true; - } else if (recyclerView.computeVerticalScrollOffset() < mSearchBar.getBottom()){ + } else if (recyclerView.computeVerticalScrollOffset() < mSearchBar.getBottom()) { show = true; } else { show = (int) ViewUtils.getY2(mSearchBar) > (mSearchBar.getHeight()) / 2; @@ -129,6 +130,7 @@ public class SearchBarMover extends RecyclerView.OnScrollListener { }); va.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { int lastValue; + @Override public void onAnimationUpdate(ValueAnimator animation) { int value = (Integer) animation.getAnimatedValue(); @@ -187,6 +189,7 @@ public class SearchBarMover extends RecyclerView.OnScrollListener { }); va.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { int lastValue; + @Override public void onAnimationUpdate(ValueAnimator animation) { int value = (Integer) animation.getAnimatedValue(); diff --git a/app/src/main/java/com/hippo/widget/SensitiveCheckBox.java b/app/src/main/java/com/hippo/widget/SensitiveCheckBox.java index ab33d1af3..71da0b060 100644 --- a/app/src/main/java/com/hippo/widget/SensitiveCheckBox.java +++ b/app/src/main/java/com/hippo/widget/SensitiveCheckBox.java @@ -18,6 +18,7 @@ package com.hippo.widget; import android.content.Context; import android.util.AttributeSet; + import androidx.appcompat.widget.AppCompatCheckBox; public class SensitiveCheckBox extends AppCompatCheckBox { @@ -70,10 +71,10 @@ public class SensitiveCheckBox extends AppCompatCheckBox { /** * Called when the checked state of a compound button has changed. * - * @param view The sensitive check box view whose state has changed. - * @param isChecked The new checked state of buttonView. - * @param fromUser True if the rating change was initiated by a user's - * touch gesture. + * @param view The sensitive check box view whose state has changed. + * @param isChecked The new checked state of buttonView. + * @param fromUser True if the rating change was initiated by a user's + * touch gesture. */ void onCheckedChanged(SensitiveCheckBox view, boolean isChecked, boolean fromUser); } diff --git a/app/src/main/java/com/hippo/widget/ShadowLinearLayout.java b/app/src/main/java/com/hippo/widget/ShadowLinearLayout.java index 79d798f4d..fce4069de 100644 --- a/app/src/main/java/com/hippo/widget/ShadowLinearLayout.java +++ b/app/src/main/java/com/hippo/widget/ShadowLinearLayout.java @@ -24,14 +24,16 @@ import android.os.Build; import android.util.AttributeSet; import android.view.ViewOutlineProvider; import android.widget.LinearLayout; + import androidx.annotation.NonNull; + import com.hippo.ehviewer.R; import com.hippo.yorozuya.LayoutUtils; public class ShadowLinearLayout extends LinearLayout { - private NinePatchDrawable mShadow; private final Rect mShadowPaddings = new Rect(); + private NinePatchDrawable mShadow; public ShadowLinearLayout(Context context) { super(context); diff --git a/app/src/main/java/com/hippo/widget/SimpleGridAutoSpanLayout.java b/app/src/main/java/com/hippo/widget/SimpleGridAutoSpanLayout.java index bccd8a252..8f1f6ad65 100644 --- a/app/src/main/java/com/hippo/widget/SimpleGridAutoSpanLayout.java +++ b/app/src/main/java/com/hippo/widget/SimpleGridAutoSpanLayout.java @@ -40,6 +40,21 @@ public class SimpleGridAutoSpanLayout extends SimpleGridLayout { super(context, attrs, defStyle); } + public static int getSpanCountForSuitableSize(int total, int single) { + int span = total / single; + if (span <= 0) { + return 1; + } + int span2 = span + 1; + float deviation = Math.abs(1 - (total / span / (float) single)); + float deviation2 = Math.abs(1 - (total / span2 / (float) single)); + return deviation < deviation2 ? span : span2; + } + + public static int getSpanCountForMinSize(int total, int single) { + return Math.max(1, total / single); + } + public void setColumnSize(int columnSize) { if (columnSize == mColumnSize) { return; @@ -56,21 +71,6 @@ public class SimpleGridAutoSpanLayout extends SimpleGridLayout { mColumnSizeChanged = true; } - public static int getSpanCountForSuitableSize(int total, int single) { - int span = total / single; - if (span <= 0) { - return 1; - } - int span2 = span + 1; - float deviation = Math.abs(1 - (total / span / (float) single)); - float deviation2 = Math.abs(1 - (total / span2 / (float) single)); - return deviation < deviation2 ? span : span2; - } - - public static int getSpanCountForMinSize(int total, int single) { - return Math.max(1, total / single); - } - @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int widthMode = MeasureSpec.getMode(widthMeasureSpec); diff --git a/app/src/main/java/com/hippo/widget/SimpleGridLayout.java b/app/src/main/java/com/hippo/widget/SimpleGridLayout.java index 490ea0b35..f2543adc6 100644 --- a/app/src/main/java/com/hippo/widget/SimpleGridLayout.java +++ b/app/src/main/java/com/hippo/widget/SimpleGridLayout.java @@ -30,7 +30,6 @@ import com.hippo.yorozuya.ViewUtils; * not scrollable * * @author Hippo - * */ public class SimpleGridLayout extends ViewGroup { diff --git a/app/src/main/java/com/hippo/widget/Slider.java b/app/src/main/java/com/hippo/widget/Slider.java index df27b2210..b6c3ee865 100644 --- a/app/src/main/java/com/hippo/widget/Slider.java +++ b/app/src/main/java/com/hippo/widget/Slider.java @@ -34,9 +34,11 @@ import android.view.View; import android.view.ViewConfiguration; import android.widget.AbsoluteLayout; import android.widget.PopupWindow; + import androidx.annotation.NonNull; import androidx.appcompat.widget.AppCompatImageView; import androidx.core.graphics.drawable.DrawableCompat; + import com.hippo.ehviewer.R; import com.hippo.yorozuya.AnimationUtils; import com.hippo.yorozuya.LayoutUtils; @@ -51,18 +53,14 @@ public class Slider extends View { private static final int BUBBLE_WIDTH = 26; private static final int BUBBLE_HEIGHT = 32; - - private Context mContext; - - private Paint mPaint; - private Paint mBgPaint; - private final RectF mLeftRectF = new RectF(); private final RectF mRightRectF = new RectF(); - + private final int[] mTemp = new int[2]; + private Context mContext; + private Paint mPaint; + private Paint mBgPaint; private PopupWindow mPopup; private BubbleView mBubble; - private int mStart; private int mEnd; private int mProgress; @@ -70,24 +68,17 @@ public class Slider extends View { private int mDrawProgress; private float mDrawPercent; private int mTargetProgress; - private float mThickness; private float mRadius; - private float mCharWidth; private float mCharHeight; - private int mBubbleWidth; private int mBubbleHeight; private int mBubbleMinWidth; private int mBubbleMinHeight; - private int mPopupX; private int mPopupY; private int mPopupWidth; - - private final int[] mTemp = new int[2]; - private boolean mReverse = false; private boolean mShowBubble; @@ -219,7 +210,7 @@ public class Slider extends View { mPopupX = (int) (mTemp[0] + mRadius - (mBubbleWidth / 2)); mPopupY = (int) (mTemp[1] - popupHeight + paddingTop + ((getHeight() - paddingTop - paddingBottom) / 2) - - mRadius -LayoutUtils.dp2pix(mContext, 2)); + mRadius - LayoutUtils.dp2pix(mContext, 2)); mPopup.update(mPopupX, mPopupY, mPopupWidth, popupHeight, false); } @@ -300,6 +291,10 @@ public class Slider extends View { updateBubbleSize(); } + public int getProgress() { + return mProgress; + } + public void setProgress(int progress) { progress = MathUtils.clamp(progress, mStart, mEnd); int oldProgress = mProgress; @@ -324,10 +319,6 @@ public class Slider extends View { } } - public int getProgress() { - return mProgress; - } - public void setReverse(boolean reverse) { if (mReverse != reverse) { mReverse = reverse; @@ -462,6 +453,15 @@ public class Slider extends View { return true; } + public interface OnSetProgressListener { + + void onSetProgress(Slider slider, int newProgress, int oldProgress, boolean byUser, boolean confirm); + + void onFingerDown(); + + void onFingerUp(); + } + @SuppressLint("ViewConstructor") private static class BubbleView extends AppCompatImageView { @@ -470,10 +470,8 @@ public class Slider extends View { private final Drawable mDrawable; private final Paint mTextPaint; - - private String mProgressStr = ""; - private final Rect mRect = new Rect(); + private String mProgressStr = ""; @SuppressWarnings("deprecation") public BubbleView(Context context, Paint paint) { @@ -516,15 +514,6 @@ public class Slider extends View { } } - public interface OnSetProgressListener { - - void onSetProgress(Slider slider, int newProgress, int oldProgress, boolean byUser, boolean confirm); - - void onFingerDown(); - - void onFingerUp(); - } - private final class CheckForShowBubble implements Runnable { @Override diff --git a/app/src/main/java/com/hippo/widget/TextClock.java b/app/src/main/java/com/hippo/widget/TextClock.java index 52bf6bf16..c9388217f 100644 --- a/app/src/main/java/com/hippo/widget/TextClock.java +++ b/app/src/main/java/com/hippo/widget/TextClock.java @@ -30,7 +30,9 @@ import android.provider.Settings; import android.text.format.DateFormat; import android.util.AttributeSet; import android.view.ViewDebug.ExportedProperty; + import androidx.appcompat.widget.AppCompatTextView; + import java.util.Calendar; import java.util.TimeZone; @@ -57,8 +59,17 @@ public class TextClock extends AppCompatTextView { private boolean mAttached; private Calendar mTime; - private String mTimeZone; + private final Runnable mTicker = new Runnable() { + @Override + public void run() { + onTimeChanged(); + long now = SystemClock.uptimeMillis(); + long next = now + (1000 - now % 1000); + + getHandler().postAtTime(mTicker, next); + } + }; private final ContentObserver mFormatChangeObserver = new ContentObserver(new Handler()) { @Override public void onChange(boolean selfChange) { @@ -72,7 +83,7 @@ public class TextClock extends AppCompatTextView { onTimeChanged(); } }; - + private String mTimeZone; private final BroadcastReceiver mIntentReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { @@ -84,18 +95,6 @@ public class TextClock extends AppCompatTextView { } }; - private final Runnable mTicker = new Runnable() { - @Override - public void run() { - onTimeChanged(); - - long now = SystemClock.uptimeMillis(); - long next = now + (1000 - now % 1000); - - getHandler().postAtTime(mTicker, next); - } - }; - public TextClock(Context context) { this(context, null); } @@ -168,7 +167,7 @@ public class TextClock extends AppCompatTextView { /** * Selects either one of {@link #getFormat12Hour()} or {@link #getFormat24Hour()} * depending on whether the user has selected 24-hour format. - * + *

* Calling this method does not schedule or unschedule the time ticker. */ private void chooseFormat() { diff --git a/app/src/main/java/com/hippo/widget/lockpattern/LockPatternUtils.java b/app/src/main/java/com/hippo/widget/lockpattern/LockPatternUtils.java index 1ecb5d65e..2669dd69a 100644 --- a/app/src/main/java/com/hippo/widget/lockpattern/LockPatternUtils.java +++ b/app/src/main/java/com/hippo/widget/lockpattern/LockPatternUtils.java @@ -26,6 +26,7 @@ public class LockPatternUtils { /** * Deserialize a pattern. + * * @param string The pattern serialized with {@link #patternToString} * @return The pattern. */ @@ -42,6 +43,7 @@ public class LockPatternUtils { /** * Serialize a pattern. + * * @param pattern The pattern. * @return The pattern in string form. */ diff --git a/app/src/main/java/com/hippo/widget/lockpattern/LockPatternView.java b/app/src/main/java/com/hippo/widget/lockpattern/LockPatternView.java index 8839af89d..24a6e8842 100644 --- a/app/src/main/java/com/hippo/widget/lockpattern/LockPatternView.java +++ b/app/src/main/java/com/hippo/widget/lockpattern/LockPatternView.java @@ -35,18 +35,21 @@ import android.view.MotionEvent; import android.view.View; import android.view.accessibility.AccessibilityManager; import android.view.animation.Interpolator; + import androidx.annotation.NonNull; import androidx.interpolator.view.animation.FastOutSlowInInterpolator; import androidx.interpolator.view.animation.LinearOutSlowInInterpolator; + import com.hippo.android.resource.AttrResources; import com.hippo.ehviewer.R; + import java.util.ArrayList; import java.util.List; /** * Displays and detects the user's unlock attempt, which is a drag of a finger * across 9 regions of the screen. - * + *

* Is also capable of displaying a static pattern in "in progress", "wrong" or * "correct" states. */ @@ -57,33 +60,24 @@ public class LockPatternView extends View { private static final int ASPECT_LOCK_HEIGHT = 2; // Fixed height; width will be minimum of (w,h) private static final boolean PROFILE_DRAWING = false; - private final CellState[][] mCellStates; - - private final int mDotSize; - private final int mDotSizeActivated; - private final int mPathWidth; - - private boolean mDrawingProfilingStarted = false; - - private final Paint mPaint = new Paint(); - private final Paint mPathPaint = new Paint(); - /** * How many milliseconds we spend animating each circle of a lock pattern * if the animating mode is set. The entire animation should take this * constant * the length of the pattern to complete. */ private static final int MILLIS_PER_CIRCLE_ANIMATING = 700; - /** * This can be used to avoid updating the display for very small motions or noisy panels. * It didn't seem to have much impact on the devices tested, so currently set to 0. */ private static final float DRAG_THRESHHOLD = 0.0f; - - private OnPatternListener mOnPatternListener; + private final CellState[][] mCellStates; + private final int mDotSize; + private final int mDotSizeActivated; + private final int mPathWidth; + private final Paint mPaint = new Paint(); + private final Paint mPathPaint = new Paint(); private final ArrayList mPattern = new ArrayList<>(9); - /** * Lookup table for the circles of the pattern we are currently drawing. * This will be the cells of the complete pattern unless we are animating, @@ -91,7 +85,18 @@ public class LockPatternView extends View { * progress animation. */ private final boolean[][] mPatternDrawLookup = new boolean[3][3]; - + private final float mHitFactor = 0.6f; + private final Path mCurrentPath = new Path(); + private final Rect mInvalidate = new Rect(); + private final Rect mTmpInvalidateRect = new Rect(); + private final int mAspect; + private final int mRegularColor; + private final int mErrorColor; + private final int mSuccessColor; + private final Interpolator mFastOutSlowInInterpolator; + private final Interpolator mLinearOutSlowInInterpolator; + private boolean mDrawingProfilingStarted = false; + private OnPatternListener mOnPatternListener; /** * the in progress point: * - during interaction: where the user's finger is @@ -99,149 +104,15 @@ public class LockPatternView extends View { */ private float mInProgressX = -1; private float mInProgressY = -1; - private long mAnimatingPeriodStart; - private DisplayMode mPatternDisplayMode = DisplayMode.Correct; private boolean mInputEnabled = true; private boolean mInStealthMode = false; private boolean mEnableHapticFeedback = true; private boolean mPatternInProgress = false; - - private final float mHitFactor = 0.6f; - private float mSquareWidth; private float mSquareHeight; - private final Path mCurrentPath = new Path(); - private final Rect mInvalidate = new Rect(); - private final Rect mTmpInvalidateRect = new Rect(); - - private final int mAspect; - private final int mRegularColor; - private final int mErrorColor; - private final int mSuccessColor; - - private final Interpolator mFastOutSlowInInterpolator; - private final Interpolator mLinearOutSlowInInterpolator; - - /** - * Represents a cell in the 3 X 3 matrix of the unlock pattern view. - */ - public static class Cell { - int row; - int column; - - // keep # objects limited to 9 - static Cell[][] sCells = new Cell[3][3]; - static { - for (int i = 0; i < 3; i++) { - for (int j = 0; j < 3; j++) { - sCells[i][j] = new Cell(i, j); - } - } - } - - /** - * @param row The row of the cell. - * @param column The column of the cell. - */ - private Cell(int row, int column) { - checkRange(row, column); - this.row = row; - this.column = column; - } - - public int getRow() { - return row; - } - - public int getColumn() { - return column; - } - - /** - * @param row The row of the cell. - * @param column The column of the cell. - */ - public static synchronized Cell of(int row, int column) { - checkRange(row, column); - return sCells[row][column]; - } - - private static void checkRange(int row, int column) { - if (row < 0 || row > 2) { - throw new IllegalArgumentException("row must be in range 0-2"); - } - if (column < 0 || column > 2) { - throw new IllegalArgumentException("column must be in range 0-2"); - } - } - - public String toString() { - return "(row=" + row + ",clmn=" + column + ")"; - } - } - - public static class CellState { - public float scale = 1.0f; - public float translateY = 0.0f; - public float alpha = 1.0f; - public float size; - public float lineEndX = Float.MIN_VALUE; - public float lineEndY = Float.MIN_VALUE; - public ValueAnimator lineAnimator; - } - - /** - * How to display the current pattern. - */ - public enum DisplayMode { - - /** - * The pattern drawn is correct (i.e draw it in a friendly color) - */ - Correct, - - /** - * Animate the pattern (for demo, and help). - */ - Animate, - - /** - * The pattern is wrong (i.e draw a foreboding color) - */ - Wrong - } - - /** - * The call back interface for detecting patterns entered by the user. - */ - public interface OnPatternListener { - - /** - * A new pattern has begun. - */ - void onPatternStart(); - - /** - * The pattern was cleared. - */ - void onPatternCleared(); - - /** - * The user extended the pattern currently being drawn by one cell. - * @param pattern The pattern with newly added cell. - */ - void onPatternCellAdded(List pattern); - - /** - * A pattern was detected from the user. - * @param pattern The pattern. - */ - void onPatternDetected(List pattern); - } - public LockPatternView(Context context) { this(context, null); } @@ -260,7 +131,7 @@ public class LockPatternView extends View { //} else if ("lock_height".equals(aspect)) { // mAspect = ASPECT_LOCK_HEIGHT; //} else { - mAspect = ASPECT_SQUARE; + mAspect = ASPECT_SQUARE; //} setClickable(true); @@ -270,7 +141,7 @@ public class LockPatternView extends View { mPathPaint.setDither(true); mRegularColor = getResources().getColor(AttrResources.getAttrBoolean(context, R.attr.isLightTheme) ? - R.color.lock_pattern_view_regular_color_light : R.color.lock_pattern_view_regular_color_dark); + R.color.lock_pattern_view_regular_color_light : R.color.lock_pattern_view_regular_color_dark); mErrorColor = getResources().getColor(R.color.lock_pattern_view_error_color); mSuccessColor = getResources().getColor(R.color.lock_pattern_view_success_color); @@ -322,13 +193,6 @@ public class LockPatternView extends View { return mInStealthMode; } - /** - * @return Whether the view has tactile feedback enabled. - */ - public boolean isTactileFeedbackEnabled() { - return mEnableHapticFeedback; - } - /** * Set whether the view is in stealth mode. If true, there will be no * visible feedback as the user enters the pattern. @@ -339,6 +203,13 @@ public class LockPatternView extends View { mInStealthMode = inStealthMode; } + /** + * @return Whether the view has tactile feedback enabled. + */ + public boolean isTactileFeedbackEnabled() { + return mEnableHapticFeedback; + } + /** * Set whether the view will use tactile feedback. If true, there will be * tactile feedback as the user enters the pattern. @@ -351,6 +222,7 @@ public class LockPatternView extends View { /** * Set the call back for pattern detection. + * * @param onPatternListener The call back. */ public void setOnPatternListener( @@ -361,8 +233,9 @@ public class LockPatternView extends View { /** * Set the pattern explicitely (rather than waiting for the user to input * a pattern). + * * @param displayMode How to display the pattern. - * @param pattern The pattern. + * @param pattern The pattern. */ public void setPattern(DisplayMode displayMode, List pattern) { mPattern.clear(); @@ -375,10 +248,20 @@ public class LockPatternView extends View { setDisplayMode(displayMode); } + /** + * Get the display mode of the current pattern. + * + * @return the display mode of the current pattern + */ + public DisplayMode getDisplayMode() { + return mPatternDisplayMode; + } + /** * Set the display mode of the current pattern. This can be useful, for * instance, after detecting a pattern to tell this view whether change the * in progress result to correct or wrong. + * * @param displayMode The display mode. */ public void setDisplayMode(DisplayMode displayMode) { @@ -397,14 +280,6 @@ public class LockPatternView extends View { invalidate(); } - /** - * Get the display mode of the current pattern. - * @return the display mode of the current pattern - */ - public DisplayMode getDisplayMode() { - return mPatternDisplayMode; - } - private void notifyCellAdded() { if (mOnPatternListener != null) { mOnPatternListener.onPatternCellAdded(mPattern); @@ -481,8 +356,7 @@ public class LockPatternView extends View { mSquareHeight = height / 3.0f; } - private int resolveMeasured(int measureSpec, int desired) - { + private int resolveMeasured(int measureSpec, int desired) { int result = 0; int specSize = MeasureSpec.getSize(measureSpec); switch (MeasureSpec.getMode(measureSpec)) { @@ -525,6 +399,7 @@ public class LockPatternView extends View { * Determines whether the point x, y will add a new point to the current * pattern (in addition to finding the cell, also makes heuristic choices * such as filling in gaps based on current pattern). + * * @param x The x coordinate. * @param y The y coordinate. */ @@ -562,7 +437,7 @@ public class LockPatternView extends View { if (mEnableHapticFeedback) { performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY, HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING - | HapticFeedbackConstants.FLAG_IGNORE_GLOBAL_SETTING); + | HapticFeedbackConstants.FLAG_IGNORE_GLOBAL_SETTING); } return cell; } @@ -582,18 +457,18 @@ public class LockPatternView extends View { final CellState cellState = mCellStates[cell.row][cell.column]; startSizeAnimation(mDotSize, mDotSizeActivated, 96, mLinearOutSlowInInterpolator, cellState, new Runnable() { - @Override - public void run() { - startSizeAnimation(mDotSizeActivated, mDotSize, 192, mFastOutSlowInInterpolator, - cellState, null); - } - }); + @Override + public void run() { + startSizeAnimation(mDotSizeActivated, mDotSize, 192, mFastOutSlowInInterpolator, + cellState, null); + } + }); startLineEndAnimation(cellState, mInProgressX, mInProgressY, getCenterXForColumn(cell.column), getCenterYForRow(cell.row)); } private void startLineEndAnimation(final CellState state, - final float startX, final float startY, final float targetX, final float targetY) { + final float startX, final float startY, final float targetX, final float targetY) { ValueAnimator valueAnimator = ValueAnimator.ofFloat(0, 1); valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override @@ -617,7 +492,7 @@ public class LockPatternView extends View { } private void startSizeAnimation(float start, float end, long duration, Interpolator interpolator, - final CellState state, final Runnable endRunnable) { + final CellState state, final Runnable endRunnable) { ValueAnimator valueAnimator = ValueAnimator.ofFloat(start, end); valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override @@ -659,6 +534,7 @@ public class LockPatternView extends View { /** * Helper method to find the row that y falls into. + * * @param y The y coordinate * @return The row that y falls in, or -1 if it falls in no row. */ @@ -680,6 +556,7 @@ public class LockPatternView extends View { /** * Helper method to find the column x fallis into. + * * @param x The x coordinate. * @return The column that x falls in, or -1 if it falls in no column. */ @@ -727,7 +604,7 @@ public class LockPatternView extends View { return false; } - switch(event.getAction()) { + switch (event.getAction()) { case MotionEvent.ACTION_DOWN: handleActionDown(event); return true; @@ -846,6 +723,7 @@ public class LockPatternView extends View { } } } + private void handleActionDown(MotionEvent event) { resetPattern(); final float x = event.getX(); @@ -1007,8 +885,8 @@ public class LockPatternView extends View { private float calculateLastSegmentAlpha(float x, float y, float lastX, float lastY) { float diffX = x - lastX; float diffY = y - lastY; - float dist = (float) Math.sqrt(diffX*diffX + diffY*diffY); - float frac = dist/mSquareWidth; + float dist = (float) Math.sqrt(diffX * diffX + diffY * diffY); + float frac = dist / mSquareWidth; return Math.min(1f, Math.max(0f, (frac - 0.3f) * 4f)); } @@ -1031,10 +909,10 @@ public class LockPatternView extends View { * @param partOfPattern Whether this circle is part of the pattern. */ private void drawCircle(Canvas canvas, float centerX, float centerY, float size, - boolean partOfPattern, float alpha) { + boolean partOfPattern, float alpha) { mPaint.setColor(getCurrentColor(partOfPattern)); mPaint.setAlpha((int) (alpha * 255)); - canvas.drawCircle(centerX, centerY, size/2, mPaint); + canvas.drawCircle(centerX, centerY, size / 2, mPaint); } @Override @@ -1059,11 +937,143 @@ public class LockPatternView extends View { mEnableHapticFeedback = ss.isTactileFeedbackEnabled(); } + /** + * How to display the current pattern. + */ + public enum DisplayMode { + + /** + * The pattern drawn is correct (i.e draw it in a friendly color) + */ + Correct, + + /** + * Animate the pattern (for demo, and help). + */ + Animate, + + /** + * The pattern is wrong (i.e draw a foreboding color) + */ + Wrong + } + + /** + * The call back interface for detecting patterns entered by the user. + */ + public interface OnPatternListener { + + /** + * A new pattern has begun. + */ + void onPatternStart(); + + /** + * The pattern was cleared. + */ + void onPatternCleared(); + + /** + * The user extended the pattern currently being drawn by one cell. + * + * @param pattern The pattern with newly added cell. + */ + void onPatternCellAdded(List pattern); + + /** + * A pattern was detected from the user. + * + * @param pattern The pattern. + */ + void onPatternDetected(List pattern); + } + + /** + * Represents a cell in the 3 X 3 matrix of the unlock pattern view. + */ + public static class Cell { + // keep # objects limited to 9 + static Cell[][] sCells = new Cell[3][3]; + + static { + for (int i = 0; i < 3; i++) { + for (int j = 0; j < 3; j++) { + sCells[i][j] = new Cell(i, j); + } + } + } + + int row; + int column; + + /** + * @param row The row of the cell. + * @param column The column of the cell. + */ + private Cell(int row, int column) { + checkRange(row, column); + this.row = row; + this.column = column; + } + + /** + * @param row The row of the cell. + * @param column The column of the cell. + */ + public static synchronized Cell of(int row, int column) { + checkRange(row, column); + return sCells[row][column]; + } + + private static void checkRange(int row, int column) { + if (row < 0 || row > 2) { + throw new IllegalArgumentException("row must be in range 0-2"); + } + if (column < 0 || column > 2) { + throw new IllegalArgumentException("column must be in range 0-2"); + } + } + + public int getRow() { + return row; + } + + public int getColumn() { + return column; + } + + public String toString() { + return "(row=" + row + ",clmn=" + column + ")"; + } + } + + public static class CellState { + public float scale = 1.0f; + public float translateY = 0.0f; + public float alpha = 1.0f; + public float size; + public float lineEndX = Float.MIN_VALUE; + public float lineEndY = Float.MIN_VALUE; + public ValueAnimator lineAnimator; + } + /** * The parecelable for saving and restoring a lock pattern view. */ private static class SavedState extends BaseSavedState { + public static final Creator CREATOR = + new Creator() { + @Override + public SavedState createFromParcel(Parcel in) { + return new SavedState(in); + } + + @Override + public SavedState[] newArray(int size) { + return new SavedState[size]; + } + }; private final String mSerializedPattern; private final int mDisplayMode; private final boolean mInputEnabled; @@ -1074,7 +1084,7 @@ public class LockPatternView extends View { * Constructor called from {@link LockPatternView#onSaveInstanceState()} */ private SavedState(Parcelable superState, String serializedPattern, int displayMode, - boolean inputEnabled, boolean inStealthMode, boolean tactileFeedbackEnabled) { + boolean inputEnabled, boolean inStealthMode, boolean tactileFeedbackEnabled) { super(superState); mSerializedPattern = serializedPattern; mDisplayMode = displayMode; @@ -1112,7 +1122,7 @@ public class LockPatternView extends View { return mInStealthMode; } - public boolean isTactileFeedbackEnabled(){ + public boolean isTactileFeedbackEnabled() { return mTactileFeedbackEnabled; } @@ -1125,18 +1135,5 @@ public class LockPatternView extends View { dest.writeValue(mInStealthMode); dest.writeValue(mTactileFeedbackEnabled); } - - public static final Creator CREATOR = - new Creator() { - @Override - public SavedState createFromParcel(Parcel in) { - return new SavedState(in); - } - - @Override - public SavedState[] newArray(int size) { - return new SavedState[size]; - } - }; } } diff --git a/app/src/main/java/com/hippo/widget/recyclerview/AutoGridLayoutManager.java b/app/src/main/java/com/hippo/widget/recyclerview/AutoGridLayoutManager.java index 9a1669603..a7bfaaf49 100644 --- a/app/src/main/java/com/hippo/widget/recyclerview/AutoGridLayoutManager.java +++ b/app/src/main/java/com/hippo/widget/recyclerview/AutoGridLayoutManager.java @@ -17,8 +17,10 @@ package com.hippo.widget.recyclerview; import android.content.Context; + import androidx.recyclerview.widget.GridLayoutManager; import androidx.recyclerview.widget.RecyclerView; + import java.util.ArrayList; import java.util.List; @@ -43,6 +45,21 @@ public class AutoGridLayoutManager extends GridLayoutManager { setColumnSize(columnSize); } + public static int getSpanCountForSuitableSize(int total, int single) { + int span = total / single; + if (span <= 0) { + return 1; + } + int span2 = span + 1; + float deviation = Math.abs(1 - (total / span / (float) single)); + float deviation2 = Math.abs(1 - (total / span2 / (float) single)); + return deviation < deviation2 ? span : span2; + } + + public static int getSpanCountForMinSize(int total, int single) { + return Math.max(1, total / single); + } + public void setColumnSize(int columnSize) { if (columnSize == mColumnSize) { return; @@ -59,21 +76,6 @@ public class AutoGridLayoutManager extends GridLayoutManager { mColumnSizeChanged = true; } - public static int getSpanCountForSuitableSize(int total, int single) { - int span = total / single; - if (span <= 0) { - return 1; - } - int span2 = span + 1; - float deviation = Math.abs(1 - (total / span / (float) single)); - float deviation2 = Math.abs(1 - (total / span2 / (float) single)); - return deviation < deviation2 ? span : span2; - } - - public static int getSpanCountForMinSize(int total, int single) { - return Math.max(1, total / single); - } - @Override public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) { if (mColumnSizeChanged && mColumnSize > 0) { diff --git a/app/src/main/java/com/hippo/widget/recyclerview/AutoStaggeredGridLayoutManager.java b/app/src/main/java/com/hippo/widget/recyclerview/AutoStaggeredGridLayoutManager.java index 5f30e494a..415e8e971 100644 --- a/app/src/main/java/com/hippo/widget/recyclerview/AutoStaggeredGridLayoutManager.java +++ b/app/src/main/java/com/hippo/widget/recyclerview/AutoStaggeredGridLayoutManager.java @@ -17,8 +17,10 @@ package com.hippo.widget.recyclerview; import android.view.View; + import androidx.recyclerview.widget.RecyclerView; import androidx.recyclerview.widget.StaggeredGridLayoutManager; + import java.util.ArrayList; import java.util.List; @@ -38,6 +40,21 @@ public class AutoStaggeredGridLayoutManager extends StaggeredGridLayoutManager { setColumnSize(columnSize); } + public static int getSpanCountForSuitableSize(int total, int single) { + int span = total / single; + if (span <= 0) { + return 1; + } + int span2 = span + 1; + float deviation = Math.abs(1 - (total / span / (float) single)); + float deviation2 = Math.abs(1 - (total / span2 / (float) single)); + return deviation < deviation2 ? span : span2; + } + + public static int getSpanCountForMinSize(int total, int single) { + return Math.max(1, total / single); + } + public void setColumnSize(int columnSize) { if (columnSize == mColumnSize) { return; @@ -54,21 +71,6 @@ public class AutoStaggeredGridLayoutManager extends StaggeredGridLayoutManager { mColumnSizeChanged = true; } - public static int getSpanCountForSuitableSize(int total, int single) { - int span = total / single; - if (span <= 0) { - return 1; - } - int span2 = span + 1; - float deviation = Math.abs(1 - (total / span / (float) single)); - float deviation2 = Math.abs(1 - (total / span2 / (float) single)); - return deviation < deviation2 ? span : span2; - } - - public static int getSpanCountForMinSize(int total, int single) { - return Math.max(1, total / single); - } - @Override public void onMeasure(RecyclerView.Recycler recycler, RecyclerView.State state, int widthSpec, int heightSpec) { if (mColumnSizeChanged && mColumnSize > 0) { diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index 7b0dc08bd..ce7f96372 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -1,5 +1,4 @@ - - - + android:layout_height="match_parent" /> - - - + android:layout_gravity="start" + app:headerLayout="@layout/nav_header_main" + app:menu="@menu/nav_drawer_main">