From 411dda0e759a9e38e18ecf016773c4b252c80a30 Mon Sep 17 00:00:00 2001 From: NerdNumber9 Date: Sat, 21 Jul 2018 23:16:10 -0400 Subject: [PATCH] Introduce incognito open-in-browser support --- app/.gitignore | 1 + app/build.gradle | 4 +- app/src/main/AndroidManifest.xml | 82 ++++---- .../data/preference/PreferenceKeys.kt | 2 + .../data/preference/PreferencesHelper.kt | 2 + .../ui/manga/info/MangaInfoController.kt | 23 ++- .../ui/setting/SettingsGeneralController.kt | 7 + .../tachiyomi/util/ContextExtensions.kt | 13 +- .../java/exh/ui/webview/NestedWebView.java | 126 ++++++++++++ .../java/exh/ui/webview/WebViewActivity.kt | 191 ++++++++++++++++++ .../main/res/drawable/ic_close_white_24dp.xml | 9 + .../ic_desktop_windows_black_24dp.xml | 9 + app/src/main/res/layout/activity_webview.xml | 37 ++++ app/src/main/res/menu/menu_webview.xml | 19 ++ app/src/main/res/values/strings.xml | 14 +- 15 files changed, 477 insertions(+), 62 deletions(-) create mode 100644 app/src/main/java/exh/ui/webview/NestedWebView.java create mode 100644 app/src/main/java/exh/ui/webview/WebViewActivity.kt create mode 100644 app/src/main/res/drawable/ic_close_white_24dp.xml create mode 100644 app/src/main/res/drawable/ic_desktop_windows_black_24dp.xml create mode 100644 app/src/main/res/layout/activity_webview.xml create mode 100644 app/src/main/res/menu/menu_webview.xml diff --git a/app/.gitignore b/app/.gitignore index 4f8f315a1..329ef4c71 100755 --- a/app/.gitignore +++ b/app/.gitignore @@ -3,3 +3,4 @@ *.iml custom.gradle google-services.json +output.json \ No newline at end of file diff --git a/app/build.gradle b/app/build.gradle index c3b1b1887..a23a71402 100755 --- a/app/build.gradle +++ b/app/build.gradle @@ -213,7 +213,7 @@ dependencies { // Conductor implementation "com.github.inorichi.Conductor:conductor:be8b3c5" - implementation ("com.bluelinelabs:conductor-support:2.1.5-SNAPSHOT") { + implementation("com.bluelinelabs:conductor-support:2.1.5-SNAPSHOT") { exclude group: "com.bluelinelabs", module: "conductor" exclude group: "com.android.support" } @@ -246,7 +246,7 @@ dependencies { implementation 'com.andrognito.pinlockview:pinlockview:2.1.0' // Reprint (EH) - implementation 'com.github.ajalt.reprint:core:3.2.1@aar' // required: supports marshmallow devices + implementation 'com.github.ajalt.reprint:core:3.2.1@aar' implementation 'com.github.ajalt.reprint:rxjava:3.2.1@aar' // optional: the RxJava 1 interface // Swirl (EH) diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index ec71f8f1f..7fd0231cc 100755 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -12,31 +12,35 @@ - - + - + + - - + + + android:theme="@android:style/Theme.Translucent.NoTitleBar" /> - - @@ -90,22 +92,18 @@ - - - - + android:exported="false" /> + android:exported="false" /> - + - - + + + android:scheme="http" /> + android:scheme="https" /> + android:scheme="http" /> + android:scheme="https" /> + android:scheme="http" /> + android:scheme="https" /> + android:scheme="http" /> + android:scheme="https" /> + android:scheme="http" /> + android:scheme="https" /> + android:scheme="http" /> + android:scheme="https" /> + android:scheme="http" /> + android:scheme="https" /> + android:scheme="http" /> + android:scheme="https" /> + android:scheme="http" /> + android:scheme="http" /> + android:scheme="https" /> + android:scheme="https" /> - + - + \ No newline at end of file diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/preference/PreferenceKeys.kt b/app/src/main/java/eu/kanade/tachiyomi/data/preference/PreferenceKeys.kt index 7e5628e17..36289fb30 100755 --- a/app/src/main/java/eu/kanade/tachiyomi/data/preference/PreferenceKeys.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/preference/PreferenceKeys.kt @@ -178,4 +178,6 @@ object PreferenceKeys { const val eh_cacheSize = "eh_cache_size" const val eh_preserveReadingPosition = "eh_preserve_reading_position" + + const val eh_incogWebview = "eh_incognito_webview" } diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/preference/PreferencesHelper.kt b/app/src/main/java/eu/kanade/tachiyomi/data/preference/PreferencesHelper.kt index 90c1904e5..75fc92e0c 100755 --- a/app/src/main/java/eu/kanade/tachiyomi/data/preference/PreferencesHelper.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/preference/PreferencesHelper.kt @@ -249,4 +249,6 @@ class PreferencesHelper(val context: Context) { fun eh_cacheSize() = rxPrefs.getString(Keys.eh_cacheSize, "75") fun eh_preserveReadingPosition() = rxPrefs.getBoolean(Keys.eh_preserveReadingPosition, false) + + fun eh_incogWebview() = rxPrefs.getBoolean(Keys.eh_incogWebview, false) } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/info/MangaInfoController.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/info/MangaInfoController.kt index 945ba6bd7..3c32bcb28 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/info/MangaInfoController.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/info/MangaInfoController.kt @@ -20,19 +20,18 @@ import android.widget.Toast import com.afollestad.materialdialogs.MaterialDialog import com.bumptech.glide.load.engine.DiskCacheStrategy import com.bumptech.glide.load.resource.bitmap.RoundedCorners -import com.bumptech.glide.load.resource.bitmap.TransformationUtils.centerCrop import com.bumptech.glide.request.target.SimpleTarget import com.bumptech.glide.request.transition.Transition import com.jakewharton.rxbinding.support.v4.widget.refreshes import com.jakewharton.rxbinding.view.clicks import com.jakewharton.rxbinding.view.longClicks import eu.kanade.tachiyomi.R -import eu.kanade.tachiyomi.R.id.* import eu.kanade.tachiyomi.data.database.models.Category import eu.kanade.tachiyomi.data.database.models.Manga import eu.kanade.tachiyomi.data.glide.GlideApp import eu.kanade.tachiyomi.data.notification.NotificationReceiver import eu.kanade.tachiyomi.data.preference.PreferencesHelper +import eu.kanade.tachiyomi.data.preference.getOrDefault import eu.kanade.tachiyomi.source.Source import eu.kanade.tachiyomi.source.SourceManager import eu.kanade.tachiyomi.source.model.SManga @@ -50,6 +49,7 @@ import eu.kanade.tachiyomi.util.toast import eu.kanade.tachiyomi.util.truncateCenter import exh.EH_SOURCE_ID import exh.EXH_SOURCE_ID +import exh.ui.webview.WebViewActivity import jp.wasabeef.glide.transformations.CropSquareTransformation import jp.wasabeef.glide.transformations.MaskTransformation import kotlinx.android.synthetic.main.manga_info_controller.* @@ -310,11 +310,20 @@ class MangaInfoController : NucleusController(), val source = presenter.source as? HttpSource ?: return try { - val url = Uri.parse(source.mangaDetailsRequest(presenter.manga).url().toString()) - val intent = CustomTabsIntent.Builder() - .setToolbarColor(context.getResourceColor(R.attr.colorPrimary)) - .build() - intent.launchUrl(activity, url) + // --> EH + val urlString = source.mangaDetailsRequest(presenter.manga).url().toString() + if(preferences.eh_incogWebview().getOrDefault()) { + activity?.startActivity(Intent(activity, WebViewActivity::class.java).apply { + putExtra(WebViewActivity.KEY_URL, urlString) + }) + } else { + val url = Uri.parse(urlString) + val intent = CustomTabsIntent.Builder() + .setToolbarColor(context.getResourceColor(R.attr.colorPrimary)) + .build() + intent.launchUrl(activity, url) + } + // <-- EH } catch (e: Exception) { context.toast(e.message) } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsGeneralController.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsGeneralController.kt index 527b16ce5..8e4057bf8 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsGeneralController.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsGeneralController.kt @@ -186,6 +186,13 @@ class SettingsGeneralController : SettingsController() { defaultValue = false } + switchPreference { + key = Keys.eh_incogWebview + title = "Incognito 'Open in browser'" + summary = "Prevent pages viewed from the 'Open in browser' menu option from being placed into Chrome's browsing history. Some browser features will be unavailable." + defaultValue = false + } + preferenceCategory { title = "Application lock" diff --git a/app/src/main/java/eu/kanade/tachiyomi/util/ContextExtensions.kt b/app/src/main/java/eu/kanade/tachiyomi/util/ContextExtensions.kt index 78aa2c1b1..24b6b6cb4 100755 --- a/app/src/main/java/eu/kanade/tachiyomi/util/ContextExtensions.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/util/ContextExtensions.kt @@ -3,11 +3,8 @@ package eu.kanade.tachiyomi.util import android.app.ActivityManager import android.app.Notification import android.app.NotificationManager -import android.content.BroadcastReceiver -import android.content.Context +import android.content.* import android.content.Context.VIBRATOR_SERVICE -import android.content.Intent -import android.content.IntentFilter import android.content.pm.PackageManager import android.content.res.Resources import android.net.ConnectivityManager @@ -129,6 +126,14 @@ val Context.powerManager: PowerManager val Context.wifiManager: WifiManager get() = applicationContext.getSystemService(Context.WIFI_SERVICE) as WifiManager +// --> EH +/** + * Property to get the wifi manager from the context. + */ +val Context.clipboardManager: ClipboardManager + get() = applicationContext.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager +// <-- EH + /** * Function used to send a local broadcast asynchronous * diff --git a/app/src/main/java/exh/ui/webview/NestedWebView.java b/app/src/main/java/exh/ui/webview/NestedWebView.java new file mode 100644 index 000000000..d5d162b61 --- /dev/null +++ b/app/src/main/java/exh/ui/webview/NestedWebView.java @@ -0,0 +1,126 @@ +package exh.ui.webview; + +import android.content.Context; +import android.support.v4.view.MotionEventCompat; +import android.support.v4.view.NestedScrollingChild; +import android.support.v4.view.NestedScrollingChildHelper; +import android.support.v4.view.ViewCompat; +import android.util.AttributeSet; +import android.view.MotionEvent; +import android.webkit.WebView; + +public class NestedWebView extends WebView implements NestedScrollingChild { + private int mLastY; + private final int[] mScrollOffset = new int[2]; + private final int[] mScrollConsumed = new int[2]; + private int mNestedOffsetY; + private NestedScrollingChildHelper mChildHelper; + + public NestedWebView(Context context) { + this(context, null); + } + + public NestedWebView(Context context, AttributeSet attrs) { + this(context, attrs, android.R.attr.webViewStyle); + } + + public NestedWebView(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + mChildHelper = new NestedScrollingChildHelper(this); + setNestedScrollingEnabled(true); + } + + @Override + public boolean onTouchEvent(MotionEvent ev) { + boolean returnValue = false; + + MotionEvent event = MotionEvent.obtain(ev); + final int action = MotionEventCompat.getActionMasked(event); + if (action == MotionEvent.ACTION_DOWN) { + mNestedOffsetY = 0; + } + int eventY = (int) event.getY(); + event.offsetLocation(0, mNestedOffsetY); + switch (action) { + case MotionEvent.ACTION_MOVE: + int deltaY = mLastY - eventY; + // NestedPreScroll + if (dispatchNestedPreScroll(0, deltaY, mScrollConsumed, mScrollOffset)) { + deltaY -= mScrollConsumed[1]; + mLastY = eventY - mScrollOffset[1]; + event.offsetLocation(0, -mScrollOffset[1]); + mNestedOffsetY += mScrollOffset[1]; + } + returnValue = super.onTouchEvent(event); + + // NestedScroll + if (dispatchNestedScroll(0, mScrollOffset[1], 0, deltaY, mScrollOffset)) { + event.offsetLocation(0, mScrollOffset[1]); + mNestedOffsetY += mScrollOffset[1]; + mLastY -= mScrollOffset[1]; + } + break; + case MotionEvent.ACTION_DOWN: + returnValue = super.onTouchEvent(event); + mLastY = eventY; + // start NestedScroll + startNestedScroll(ViewCompat.SCROLL_AXIS_VERTICAL); + break; + case MotionEvent.ACTION_UP: + case MotionEvent.ACTION_CANCEL: + returnValue = super.onTouchEvent(event); + // end NestedScroll + stopNestedScroll(); + break; + } + return returnValue; + } + + // Nested Scroll implements + @Override + public void setNestedScrollingEnabled(boolean enabled) { + mChildHelper.setNestedScrollingEnabled(enabled); + } + + @Override + public boolean isNestedScrollingEnabled() { + return mChildHelper.isNestedScrollingEnabled(); + } + + @Override + public boolean startNestedScroll(int axes) { + return mChildHelper.startNestedScroll(axes); + } + + @Override + public void stopNestedScroll() { + mChildHelper.stopNestedScroll(); + } + + @Override + public boolean hasNestedScrollingParent() { + return mChildHelper.hasNestedScrollingParent(); + } + + @Override + public boolean dispatchNestedScroll(int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed, + int[] offsetInWindow) { + return mChildHelper.dispatchNestedScroll(dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed, offsetInWindow); + } + + @Override + public boolean dispatchNestedPreScroll(int dx, int dy, int[] consumed, int[] offsetInWindow) { + return mChildHelper.dispatchNestedPreScroll(dx, dy, consumed, offsetInWindow); + } + + @Override + public boolean dispatchNestedFling(float velocityX, float velocityY, boolean consumed) { + return mChildHelper.dispatchNestedFling(velocityX, velocityY, consumed); + } + + @Override + public boolean dispatchNestedPreFling(float velocityX, float velocityY) { + return mChildHelper.dispatchNestedPreFling(velocityX, velocityY); + } + +} \ No newline at end of file diff --git a/app/src/main/java/exh/ui/webview/WebViewActivity.kt b/app/src/main/java/exh/ui/webview/WebViewActivity.kt new file mode 100644 index 000000000..aa24b667c --- /dev/null +++ b/app/src/main/java/exh/ui/webview/WebViewActivity.kt @@ -0,0 +1,191 @@ +package exh.ui.webview + +import android.content.ClipData +import android.content.Intent +import android.graphics.Bitmap +import android.net.Uri +import android.os.Build +import android.os.Bundle +import android.util.TypedValue +import android.view.Menu +import android.view.MenuItem +import android.webkit.CookieManager +import android.webkit.WebView +import android.webkit.WebViewClient +import eu.kanade.tachiyomi.R +import eu.kanade.tachiyomi.data.preference.PreferencesHelper +import eu.kanade.tachiyomi.data.preference.getOrDefault +import eu.kanade.tachiyomi.ui.base.activity.BaseActivity +import eu.kanade.tachiyomi.util.clipboardManager +import eu.kanade.tachiyomi.util.toast +import exh.ui.login.LoginController +import kotlinx.android.synthetic.main.activity_webview.* +import uy.kohesive.injekt.injectLazy + +class WebViewActivity : BaseActivity() { + private val prefs: PreferencesHelper by injectLazy() + private var mobileUserAgent: String? = null + private var isDesktop: Boolean = false + + override fun onCreate(savedInstanceState: Bundle?) { + setTheme(when (prefs.theme()) { + 2 -> R.style.Theme_Tachiyomi_Dark + 3 -> R.style.Theme_Tachiyomi_Amoled + else -> R.style.Theme_Tachiyomi + }) + + super.onCreate(savedInstanceState) + setContentView(R.layout.activity_webview) + setSupportActionBar(toolbar) + + // Opaque status bar + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + val typedValue = TypedValue() + theme.resolveAttribute(R.attr.colorPrimaryDark, typedValue, true) + window.statusBarColor = typedValue.data + } + + // Show close button + supportActionBar?.setDisplayHomeAsUpEnabled(true) + supportActionBar?.setHomeAsUpIndicator(R.drawable.ic_close_white_24dp) + + val url = intent.getStringExtra(KEY_URL) + + // Set immediately (required for correct title after rotation) + title = url + // Configure webview + webview.settings.javaScriptEnabled = true + webview.settings.domStorageEnabled = true + webview.settings.databaseEnabled = true + webview.webViewClient = object : WebViewClient() { + override fun onPageStarted(view: WebView?, url: String?, favicon: Bitmap?) { + super.onPageStarted(view, url, favicon) + invalidateOptionsMenu() + appbar.setExpanded(true, true) + title = "Loading: $url" + } + + override fun onPageFinished(view: WebView?, url: String?) { + super.onPageFinished(view, url) + title = url + invalidateOptionsMenu() + } + } + + // Configure E-Hentai/ExHentai cookies + for(domain in listOf( + "www.exhentai.org", + "exhentai.org", + "www.e-hentai.org", + "e-hentai.org", + "g.e-hentai.org" + )) { + for(cookie in listOf( + LoginController.MEMBER_ID_COOKIE to prefs.memberIdVal().getOrDefault(), + LoginController.PASS_HASH_COOKIE to prefs.passHashVal().getOrDefault(), + LoginController.IGNEOUS_COOKIE to prefs.igneousVal().getOrDefault() + )) { + val cookieString = "${cookie.first}=${cookie.second}; domain=$domain; path=/;" + CookieManager.getInstance().setCookie(domain, cookieString) + } + } + + // Long-click to copy URL + toolbar.setOnLongClickListener { + toast("URL copied.") + clipboardManager.primaryClip = ClipData.newUri( + contentResolver, + webview.title, + Uri.parse(webview.url) + ) + true + } + + if(savedInstanceState == null) { + mobileUserAgent = webview.settings.userAgentString + + if(url == null) { + toast("No URL supplied!") + finish() + return + } + + webview.loadUrl(url) + } + } + + override fun onRestoreInstanceState(savedInstanceState: Bundle?) { + super.onRestoreInstanceState(savedInstanceState) + savedInstanceState?.getString(STATE_KEY_MOBILE_USER_AGENT)?.let { + mobileUserAgent = it + } + savedInstanceState?.getBoolean(STATE_KEY_IS_DESKTOP)?.let { + isDesktop = it + } + webview.restoreState(savedInstanceState) + } + + override fun onSaveInstanceState(outState: Bundle?) { + super.onSaveInstanceState(outState) + outState?.putString(STATE_KEY_MOBILE_USER_AGENT, mobileUserAgent) + outState?.putBoolean(STATE_KEY_IS_DESKTOP, isDesktop) + webview.saveState(outState) + } + + override fun onPrepareOptionsMenu(menu: Menu?): Boolean { + menu?.findItem(R.id.action_forward)?.isEnabled = webview.canGoForward() + menu?.findItem(R.id.action_desktop_site)?.isChecked = isDesktop + + return super.onPrepareOptionsMenu(menu) + } + + override fun onCreateOptionsMenu(menu: Menu?): Boolean { + menuInflater.inflate(R.menu.menu_webview, menu) + return super.onCreateOptionsMenu(menu) + } + + override fun onBackPressed() { + if(webview.canGoBack()) + webview.goBack() + else + super.onBackPressed() + } + + override fun onOptionsItemSelected(item: MenuItem?): Boolean { + when(item?.itemId) { + android.R.id.home -> finish() + R.id.action_refresh -> webview.reload() + R.id.action_forward -> webview.goForward() + R.id.action_desktop_site -> { + isDesktop = !item.isChecked + item.isChecked = isDesktop + + (if(isDesktop) { + mobileUserAgent?.replace("\\([^(]*(Mobile|Android)[^)]*\\)" + .toRegex(RegexOption.IGNORE_CASE), "") + ?.replace("Mobile", "", true) + ?.replace("Android", "", true) + } else { + mobileUserAgent + })?.let { + webview.settings.userAgentString = it + } + + webview.settings.useWideViewPort = isDesktop + webview.settings.loadWithOverviewMode = isDesktop + + webview.reload() + } + R.id.action_open_in_browser -> + startActivity(Intent(Intent.ACTION_VIEW, Uri.parse(webview.url))) + } + return super.onOptionsItemSelected(item) + } + + companion object { + const val KEY_URL = "url" + + const val STATE_KEY_MOBILE_USER_AGENT = "mobile_user_agent" + const val STATE_KEY_IS_DESKTOP = "is_desktop" + } +} diff --git a/app/src/main/res/drawable/ic_close_white_24dp.xml b/app/src/main/res/drawable/ic_close_white_24dp.xml new file mode 100644 index 000000000..c82041598 --- /dev/null +++ b/app/src/main/res/drawable/ic_close_white_24dp.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_desktop_windows_black_24dp.xml b/app/src/main/res/drawable/ic_desktop_windows_black_24dp.xml new file mode 100644 index 000000000..bec86e7b3 --- /dev/null +++ b/app/src/main/res/drawable/ic_desktop_windows_black_24dp.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/layout/activity_webview.xml b/app/src/main/res/layout/activity_webview.xml new file mode 100644 index 000000000..b320363c2 --- /dev/null +++ b/app/src/main/res/layout/activity_webview.xml @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + diff --git a/app/src/main/res/menu/menu_webview.xml b/app/src/main/res/menu/menu_webview.xml new file mode 100644 index 000000000..90d643df1 --- /dev/null +++ b/app/src/main/res/menu/menu_webview.xml @@ -0,0 +1,19 @@ + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index e7c13d3ea..f4778295c 100755 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -102,7 +102,7 @@ Updates - + General Reader Downloads @@ -111,7 +111,7 @@ Advanced About - + Library manga per row Portrait Landscape @@ -166,7 +166,7 @@ Language: %1$s There are no preferences to edit for this extension - + Fullscreen Lock orientation Page transitions @@ -217,7 +217,7 @@ A - + Downloads directory Only download over Wi-Fi Remove when marked as read @@ -232,7 +232,7 @@ Download new chapters Categories to include in download - + Services @@ -258,7 +258,7 @@ Restoring backup Creating backup - + Clear chapter cache Used: %1$s Cache cleared. %1$d files have been deleted @@ -275,7 +275,7 @@ Refresh tracking metadata Updates status, score and last chapter read from the tracking services - + Version Build time Check for updates