Add IME_FLAG_NO_PERSONALIZED_LEARNING flag to text input when incognito is enabled (#5801)

* Add IME_FLAG_NO_PERSONALIZED_LEARNING flag to text input when incognito is enabled

Tested with Gboard only.

* Revert "Add IME_FLAG_NO_PERSONALIZED_LEARNING flag to text input when incognito is enabled"

This reverts commit 068399db

* Add IME_FLAG_NO_PERSONALIZED_LEARNING flag to text inputs when incognito is enabled

Source preference is not affected.

* Source preference stuff

(cherry picked from commit 3ea84cf0ce23d37a1787663834ab9922f10577f8)

# Conflicts:
#	app/src/main/java/eu/kanade/tachiyomi/ui/browse/extension/details/SourcePreferencesController.kt
This commit is contained in:
Ivan Iskandar 2021-08-28 23:06:29 +07:00 committed by Jobobby04
parent 676d8c85a0
commit 8875c3f9bb
20 changed files with 241 additions and 22 deletions

View File

@ -27,6 +27,7 @@ import eu.kanade.tachiyomi.source.ConfigurableSource
import eu.kanade.tachiyomi.source.Source import eu.kanade.tachiyomi.source.Source
import eu.kanade.tachiyomi.source.getPreferenceKey import eu.kanade.tachiyomi.source.getPreferenceKey
import eu.kanade.tachiyomi.ui.base.controller.NucleusController import eu.kanade.tachiyomi.ui.base.controller.NucleusController
import eu.kanade.tachiyomi.widget.TachiyomiTextInputEditText.Companion.setIncognito
import exh.source.EnhancedHttpSource import exh.source.EnhancedHttpSource
import timber.log.Timber import timber.log.Timber
@ -124,6 +125,13 @@ class SourcePreferencesController(bundle: Bundle? = null) :
pref.isIconSpaceReserved = false pref.isIconSpaceReserved = false
pref.order = Int.MAX_VALUE // reset to default order pref.order = Int.MAX_VALUE // reset to default order
// Apply incognito IME for EditTextPreference
if (pref is EditTextPreference) {
pref.setOnBindEditTextListener {
it.setIncognito(viewScope)
}
}
newScreen.removePreference(pref) newScreen.removePreference(pref)
screen.addPreference(pref) screen.addPreference(pref)
} }

View File

@ -0,0 +1,47 @@
package eu.kanade.tachiyomi.widget
import android.content.Context
import android.util.AttributeSet
import androidx.appcompat.widget.SearchView
import androidx.core.view.inputmethod.EditorInfoCompat
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
import eu.kanade.tachiyomi.data.preference.asImmediateFlow
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.cancel
import kotlinx.coroutines.flow.launchIn
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
/**
* A custom [SearchView] that sets [EditorInfoCompat.IME_FLAG_NO_PERSONALIZED_LEARNING] to imeOptions
* if [PreferencesHelper.incognitoMode] is true. Some IMEs may not respect this flag.
*/
class TachiyomiSearchView @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = R.attr.searchViewStyle
) : SearchView(context, attrs, defStyleAttr) {
private var scope: CoroutineScope? = null
override fun onAttachedToWindow() {
super.onAttachedToWindow()
scope = CoroutineScope(SupervisorJob() + Dispatchers.Main)
Injekt.get<PreferencesHelper>().incognitoMode().asImmediateFlow {
imeOptions = if (it) {
imeOptions or EditorInfoCompat.IME_FLAG_NO_PERSONALIZED_LEARNING
} else {
imeOptions and EditorInfoCompat.IME_FLAG_NO_PERSONALIZED_LEARNING.inv()
}
}.launchIn(scope!!)
}
override fun onDetachedFromWindow() {
super.onDetachedFromWindow()
scope?.cancel()
scope = null
}
}

View File

@ -0,0 +1,60 @@
package eu.kanade.tachiyomi.widget
import android.content.Context
import android.util.AttributeSet
import android.widget.EditText
import androidx.core.view.inputmethod.EditorInfoCompat
import com.google.android.material.textfield.TextInputEditText
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
import eu.kanade.tachiyomi.data.preference.asImmediateFlow
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.cancel
import kotlinx.coroutines.flow.launchIn
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
/**
* A custom [TextInputEditText] that sets [EditorInfoCompat.IME_FLAG_NO_PERSONALIZED_LEARNING] to imeOptions
* if [PreferencesHelper.incognitoMode] is true. Some IMEs may not respect this flag.
*
* @see setIncognito
*/
class TachiyomiTextInputEditText @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = R.attr.editTextStyle
) : TextInputEditText(context, attrs, defStyleAttr) {
private var scope: CoroutineScope? = null
override fun onAttachedToWindow() {
super.onAttachedToWindow()
scope = CoroutineScope(SupervisorJob() + Dispatchers.Main)
setIncognito(scope!!)
}
override fun onDetachedFromWindow() {
super.onDetachedFromWindow()
scope?.cancel()
scope = null
}
companion object {
/**
* Sets Flow to this [EditText] that sets [EditorInfoCompat.IME_FLAG_NO_PERSONALIZED_LEARNING] to imeOptions
* if [PreferencesHelper.incognitoMode] is true. Some IMEs may not respect this flag.
*/
fun EditText.setIncognito(viewScope: CoroutineScope) {
Injekt.get<PreferencesHelper>().incognitoMode().asImmediateFlow {
imeOptions = if (it) {
imeOptions or EditorInfoCompat.IME_FLAG_NO_PERSONALIZED_LEARNING
} else {
imeOptions and EditorInfoCompat.IME_FLAG_NO_PERSONALIZED_LEARNING.inv()
}
}.launchIn(viewScope)
}
}
}

View File

@ -0,0 +1,103 @@
package exh.md.handlers
import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.network.await
import eu.kanade.tachiyomi.source.model.Page
import exh.md.dto.MangaPlusSerializer
import kotlinx.serialization.protobuf.ProtoBuf
import okhttp3.Headers
import okhttp3.Interceptor
import okhttp3.MediaType.Companion.toMediaTypeOrNull
import okhttp3.OkHttpClient
import okhttp3.Request
import okhttp3.Response
import okhttp3.ResponseBody.Companion.toResponseBody
import java.util.UUID
class MangaPlusHandler(currentClient: OkHttpClient) {
val baseUrl = "https://jumpg-webapi.tokyo-cdn.com/api"
val headers = Headers.Builder()
.add("Origin", WEB_URL)
.add("Referer", WEB_URL)
.add("User-Agent", USER_AGENT)
.add("SESSION-TOKEN", UUID.randomUUID().toString()).build()
val client: OkHttpClient = currentClient.newBuilder()
.addInterceptor { imageIntercept(it) }
.build()
suspend fun fetchPageList(chapterId: String): List<Page> {
val response = client.newCall(pageListRequest(chapterId)).await()
return pageListParse(response)
}
private fun pageListRequest(chapterId: String): Request {
return GET(
"$baseUrl/manga_viewer?chapter_id=$chapterId&split=yes&img_quality=super_high",
headers
)
}
private fun pageListParse(response: Response): List<Page> {
val result = ProtoBuf.decodeFromByteArray(MangaPlusSerializer, response.body!!.bytes())
if (result.success == null) {
throw Exception("error getting images")
}
return result.success.mangaViewer!!.pages
.mapNotNull { it.page }
.mapIndexed { i, page ->
val encryptionKey =
if (page.encryptionKey == null) "" else "&encryptionKey=${page.encryptionKey}"
Page(i, "", "${page.imageUrl}$encryptionKey")
}
}
private fun imageIntercept(chain: Interceptor.Chain): Response {
var request = chain.request()
if (!request.url.queryParameterNames.contains("encryptionKey")) {
return chain.proceed(request)
}
val encryptionKey = request.url.queryParameter("encryptionKey")!!
// Change the url and remove the encryptionKey to avoid detection.
val newUrl = request.url.newBuilder().removeAllQueryParameters("encryptionKey").build()
request = request.newBuilder().url(newUrl).build()
val response = chain.proceed(request)
val image = decodeImage(encryptionKey, response.body!!.bytes())
val body = image.toResponseBody("image/jpeg".toMediaTypeOrNull())
return response.newBuilder().body(body).build()
}
private fun decodeImage(encryptionKey: String, image: ByteArray): ByteArray {
val keyStream = HEX_GROUP
.findAll(encryptionKey)
.map { it.groupValues[1].toInt(16) }
.toList()
val content = image
.map { it.toInt() }
.toIntArray()
val blockSizeInBytes = keyStream.size
content.forEachIndexed { i, value ->
content[i] = value xor keyStream[i % blockSizeInBytes]
}
return ByteArray(content.size) { pos -> content[pos].toByte() }
}
companion object {
private const val WEB_URL = "https://mangaplus.shueisha.co.jp"
private const val USER_AGENT =
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.92 Safari/537.36"
private val HEX_GROUP = "(.{1,2})".toRegex()
}
}

View File

@ -11,7 +11,7 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content"> android:layout_height="wrap_content">
<com.google.android.material.textfield.TextInputEditText <eu.kanade.tachiyomi.widget.TachiyomiTextInputEditText
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" /> android:layout_height="wrap_content" />

View File

@ -25,7 +25,7 @@
android:tint="?attr/colorAccent" android:tint="?attr/colorAccent"
app:srcCompat="@drawable/ic_chevron_left_black_24dp" /> app:srcCompat="@drawable/ic_chevron_left_black_24dp" />
<com.google.android.material.textfield.TextInputEditText <eu.kanade.tachiyomi.widget.TachiyomiTextInputEditText
android:id="@+id/myNumber" android:id="@+id/myNumber"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"

View File

@ -46,7 +46,7 @@
android:layout_marginStart="16dp" android:layout_marginStart="16dp"
android:layout_marginEnd="16dp"> android:layout_marginEnd="16dp">
<com.google.android.material.textfield.TextInputEditText <eu.kanade.tachiyomi.widget.TachiyomiTextInputEditText
android:id="@+id/title" android:id="@+id/title"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
@ -63,7 +63,7 @@
android:layout_marginStart="16dp" android:layout_marginStart="16dp"
android:layout_marginEnd="16dp"> android:layout_marginEnd="16dp">
<com.google.android.material.textfield.TextInputEditText <eu.kanade.tachiyomi.widget.TachiyomiTextInputEditText
android:id="@+id/manga_author" android:id="@+id/manga_author"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
@ -80,7 +80,7 @@
android:layout_marginStart="16dp" android:layout_marginStart="16dp"
android:layout_marginEnd="16dp"> android:layout_marginEnd="16dp">
<com.google.android.material.textfield.TextInputEditText <eu.kanade.tachiyomi.widget.TachiyomiTextInputEditText
android:id="@+id/manga_artist" android:id="@+id/manga_artist"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
@ -97,7 +97,7 @@
android:layout_marginStart="16dp" android:layout_marginStart="16dp"
android:layout_marginEnd="16dp"> android:layout_marginEnd="16dp">
<com.google.android.material.textfield.TextInputEditText <eu.kanade.tachiyomi.widget.TachiyomiTextInputEditText
android:id="@+id/manga_description" android:id="@+id/manga_description"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"

View File

@ -26,7 +26,7 @@
app:layout_constraintRight_toRightOf="parent" app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" /> app:layout_constraintTop_toTopOf="parent" />
<com.google.android.material.textfield.TextInputEditText <eu.kanade.tachiyomi.widget.TachiyomiTextInputEditText
android:id="@+id/galleries_box" android:id="@+id/galleries_box"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"

View File

@ -16,7 +16,7 @@
android:layout_weight="1" android:layout_weight="1"
android:gravity="center_vertical|start"> android:gravity="center_vertical|start">
<com.google.android.material.textfield.TextInputEditText <eu.kanade.tachiyomi.widget.TachiyomiTextInputEditText
android:id="@+id/nav_view_item" android:id="@+id/nav_view_item"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"

View File

@ -13,7 +13,7 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:hint="@string/username"> android:hint="@string/username">
<com.google.android.material.textfield.TextInputEditText <eu.kanade.tachiyomi.widget.TachiyomiTextInputEditText
android:id="@+id/username" android:id="@+id/username"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
@ -28,7 +28,7 @@
android:hint="@string/password" android:hint="@string/password"
app:endIconMode="password_toggle"> app:endIconMode="password_toggle">
<com.google.android.material.textfield.TextInputEditText <eu.kanade.tachiyomi.widget.TachiyomiTextInputEditText
android:id="@+id/password" android:id="@+id/password"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"

View File

@ -22,7 +22,7 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:hint="@string/username"> android:hint="@string/username">
<com.google.android.material.textfield.TextInputEditText <eu.kanade.tachiyomi.widget.TachiyomiTextInputEditText
android:id="@+id/username" android:id="@+id/username"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
@ -37,7 +37,7 @@
android:hint="@string/password" android:hint="@string/password"
app:endIconMode="password_toggle"> app:endIconMode="password_toggle">
<com.google.android.material.textfield.TextInputEditText <eu.kanade.tachiyomi.widget.TachiyomiTextInputEditText
android:id="@+id/password" android:id="@+id/password"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
@ -55,11 +55,12 @@
android:visibility="gone" android:visibility="gone"
tools:visibility="visible"> tools:visibility="visible">
<com.google.android.material.textfield.TextInputEditText <eu.kanade.tachiyomi.widget.TachiyomiTextInputEditText
android:id="@+id/two_factor_edit" android:id="@+id/two_factor_edit"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:inputType="textNoSuggestions" /> android:inputType="textNoSuggestions" />
</com.google.android.material.textfield.TextInputLayout> </com.google.android.material.textfield.TextInputLayout>
<com.google.android.material.checkbox.MaterialCheckBox <com.google.android.material.checkbox.MaterialCheckBox

View File

@ -38,7 +38,7 @@
android:hint="@string/title" android:hint="@string/title"
app:endIconMode="clear_text"> app:endIconMode="clear_text">
<com.google.android.material.textfield.TextInputEditText <eu.kanade.tachiyomi.widget.TachiyomiTextInputEditText
android:id="@+id/title_input_edit_text" android:id="@+id/title_input_edit_text"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"

View File

@ -5,7 +5,7 @@
android:id="@+id/action_search" android:id="@+id/action_search"
android:icon="@drawable/ic_search_24dp" android:icon="@drawable/ic_search_24dp"
android:title="@string/action_search" android:title="@string/action_search"
app:actionViewClass="androidx.appcompat.widget.SearchView" app:actionViewClass="eu.kanade.tachiyomi.widget.TachiyomiSearchView"
app:iconTint="?attr/colorOnToolbar" app:iconTint="?attr/colorOnToolbar"
app:showAsAction="collapseActionView|ifRoom" /> app:showAsAction="collapseActionView|ifRoom" />

View File

@ -5,7 +5,7 @@
android:id="@+id/action_search" android:id="@+id/action_search"
android:icon="@drawable/ic_travel_explore_24dp" android:icon="@drawable/ic_travel_explore_24dp"
android:title="@string/action_global_search" android:title="@string/action_global_search"
app:actionViewClass="androidx.appcompat.widget.SearchView" app:actionViewClass="eu.kanade.tachiyomi.widget.TachiyomiSearchView"
app:iconTint="?attr/colorOnToolbar" app:iconTint="?attr/colorOnToolbar"
app:showAsAction="collapseActionView|ifRoom" /> app:showAsAction="collapseActionView|ifRoom" />

View File

@ -5,7 +5,7 @@
android:id="@+id/action_search" android:id="@+id/action_search"
android:icon="@drawable/ic_search_24dp" android:icon="@drawable/ic_search_24dp"
android:title="@string/action_search" android:title="@string/action_search"
app:actionViewClass="androidx.appcompat.widget.SearchView" app:actionViewClass="eu.kanade.tachiyomi.widget.TachiyomiSearchView"
app:iconTint="?attr/colorOnToolbar" app:iconTint="?attr/colorOnToolbar"
app:showAsAction="collapseActionView|ifRoom" /> app:showAsAction="collapseActionView|ifRoom" />

View File

@ -6,7 +6,7 @@
android:id="@+id/action_search" android:id="@+id/action_search"
android:icon="@drawable/ic_search_24dp" android:icon="@drawable/ic_search_24dp"
android:title="@string/action_search" android:title="@string/action_search"
app:actionViewClass="androidx.appcompat.widget.SearchView" app:actionViewClass="eu.kanade.tachiyomi.widget.TachiyomiSearchView"
app:iconTint="?attr/colorOnToolbar" app:iconTint="?attr/colorOnToolbar"
app:showAsAction="ifRoom|collapseActionView" /> app:showAsAction="ifRoom|collapseActionView" />

View File

@ -7,7 +7,7 @@
android:id="@+id/action_search" android:id="@+id/action_search"
android:icon="@drawable/ic_search_24dp" android:icon="@drawable/ic_search_24dp"
android:title="@string/action_search" android:title="@string/action_search"
app:actionViewClass="androidx.appcompat.widget.SearchView" app:actionViewClass="eu.kanade.tachiyomi.widget.TachiyomiSearchView"
app:iconTint="?attr/colorOnToolbar" app:iconTint="?attr/colorOnToolbar"
app:showAsAction="collapseActionView|ifRoom" /> app:showAsAction="collapseActionView|ifRoom" />

View File

@ -5,7 +5,7 @@
android:id="@+id/action_search" android:id="@+id/action_search"
android:icon="@drawable/ic_search_24dp" android:icon="@drawable/ic_search_24dp"
android:title="@string/action_search" android:title="@string/action_search"
app:actionViewClass="androidx.appcompat.widget.SearchView" app:actionViewClass="eu.kanade.tachiyomi.widget.TachiyomiSearchView"
app:iconTint="?attr/colorOnToolbar" app:iconTint="?attr/colorOnToolbar"
app:showAsAction="collapseActionView|ifRoom" /> app:showAsAction="collapseActionView|ifRoom" />

View File

@ -7,7 +7,7 @@
android:icon="@drawable/ic_search_24dp" android:icon="@drawable/ic_search_24dp"
app:showAsAction="collapseActionView|ifRoom" app:showAsAction="collapseActionView|ifRoom"
app:iconTint="?attr/colorOnToolbar" app:iconTint="?attr/colorOnToolbar"
app:actionViewClass="androidx.appcompat.widget.SearchView"/> app:actionViewClass="eu.kanade.tachiyomi.widget.TachiyomiSearchView"/>
<item <item
android:id="@+id/action_sort" android:id="@+id/action_sort"

View File

@ -5,7 +5,7 @@
android:id="@+id/action_search" android:id="@+id/action_search"
android:icon="@drawable/ic_search_24dp" android:icon="@drawable/ic_search_24dp"
android:title="@string/action_search" android:title="@string/action_search"
app:actionViewClass="androidx.appcompat.widget.SearchView" app:actionViewClass="eu.kanade.tachiyomi.widget.TachiyomiSearchView"
app:iconTint="?attr/colorOnToolbar" app:iconTint="?attr/colorOnToolbar"
app:showAsAction="collapseActionView|ifRoom" /> app:showAsAction="collapseActionView|ifRoom" />