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