# Conflicts:
#	.travis.yml
#	README.md
#	app/build.gradle
#	app/src/main/java/eu/kanade/tachiyomi/ui/manga/info/MangaInfoController.kt
#	app/src/main/res/layout/manga_info_controller.xml
#	app/src/main/res/raw/changelog_release.xml
This commit is contained in:
NerdNumber9 2018-04-14 21:11:29 -04:00
commit df2a4779bf
16 changed files with 182 additions and 74 deletions

View File

@ -32,7 +32,7 @@ ext {
android {
compileSdkVersion 27
buildToolsVersion "27.0.2"
buildToolsVersion '27.0.3'
publishNonDefault true
defaultConfig {
@ -40,8 +40,8 @@ android {
minSdkVersion 16
targetSdkVersion 27
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
versionCode 7000
versionName "v7.0.0-EH"
versionCode 7200
versionName "v7.2.0-EH"
buildConfigField "String", "COMMIT_COUNT", "\"${getCommitCount()}\""
buildConfigField "String", "COMMIT_SHA", "\"${getGitSha()}\""
@ -121,7 +121,7 @@ dependencies {
implementation "com.android.support:support-annotations:$support_library_version"
implementation "com.android.support:customtabs:$support_library_version"
implementation 'com.android.support.constraint:constraint-layout:1.0.2'
implementation 'com.android.support.constraint:constraint-layout:1.1.0-beta6'
implementation 'com.android.support:multidex:1.0.2'

View File

@ -1,10 +1,13 @@
package eu.kanade.tachiyomi.data.updater
import eu.kanade.tachiyomi.network.NetworkHelper
import retrofit2.Retrofit
import retrofit2.adapter.rxjava.RxJavaCallAdapterFactory
import retrofit2.converter.gson.GsonConverterFactory
import retrofit2.http.GET
import rx.Observable
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
/**
* Used to connect with the Github API.
@ -17,6 +20,7 @@ interface GithubService {
.baseUrl("https://api.github.com")
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
.client(Injekt.get<NetworkHelper>().client)
.build()
return restAdapter.create(GithubService::class.java)

View File

@ -1,6 +1,7 @@
package eu.kanade.tachiyomi.network
import com.squareup.duktape.Duktape
import okhttp3.CacheControl
import okhttp3.HttpUrl
import okhttp3.Interceptor
import okhttp3.Request
@ -21,7 +22,7 @@ class CloudflareInterceptor : Interceptor {
val response = chain.proceed(chain.request())
// Check if Cloudflare anti-bot is on
if (response.code() == 503 && serverCheck.contains(response.header("Server"))) {
if (response.code() == 503 && response.header("Server") in serverCheck) {
return chain.proceed(resolveChallenge(response))
}
@ -43,31 +44,32 @@ class CloudflareInterceptor : Interceptor {
val pass = passPattern.find(content)?.groups?.get(1)?.value
if (operation == null || challenge == null || pass == null) {
throw RuntimeException("Failed resolving Cloudflare challenge")
throw Exception("Failed resolving Cloudflare challenge")
}
val js = operation
.replace(Regex("""a\.value =(.+?) \+.*"""), "$1")
.replace(Regex("""a\.value = (.+ \+ t\.length).+"""), "$1")
.replace(Regex("""\s{3,}[a-z](?: = |\.).+"""), "")
.replace("t.length", "${domain.length}")
.replace("\n", "")
val result = (duktape.evaluate(js) as Double).toInt()
val answer = "${result + domain.length}"
val result = duktape.evaluate(js) as Double
val cloudflareUrl = HttpUrl.parse("${url.scheme()}://$domain/cdn-cgi/l/chk_jschl")!!
.newBuilder()
.addQueryParameter("jschl_vc", challenge)
.addQueryParameter("pass", pass)
.addQueryParameter("jschl_answer", answer)
.addQueryParameter("jschl_answer", "$result")
.toString()
val cloudflareHeaders = originalRequest.headers()
.newBuilder()
.add("Referer", url.toString())
.add("Accept", "text/html,application/xhtml+xml,application/xml")
.add("Accept-Language", "en")
.build()
return GET(cloudflareUrl, cloudflareHeaders)
return GET(cloudflareUrl, cloudflareHeaders, cache = CacheControl.Builder().build())
}
}

View File

@ -1,9 +1,22 @@
package eu.kanade.tachiyomi.network
import android.content.Context
import android.os.Build
import okhttp3.Cache
import okhttp3.OkHttpClient
import java.io.File
import java.io.IOException
import java.net.InetAddress
import java.net.Socket
import java.net.UnknownHostException
import java.security.KeyManagementException
import java.security.KeyStore
import java.security.NoSuchAlgorithmException
import javax.net.ssl.SSLContext
import javax.net.ssl.SSLSocket
import javax.net.ssl.SSLSocketFactory
import javax.net.ssl.TrustManagerFactory
import javax.net.ssl.X509TrustManager
class NetworkHelper(context: Context) {
@ -16,6 +29,7 @@ class NetworkHelper(context: Context) {
val client = OkHttpClient.Builder()
.cookieJar(cookieManager)
.cache(Cache(cacheDir, cacheSize))
.enableTLS12()
.build()
val cloudflareClient = client.newBuilder()
@ -25,4 +39,75 @@ class NetworkHelper(context: Context) {
val cookies: PersistentCookieStore
get() = cookieManager.store
private fun OkHttpClient.Builder.enableTLS12(): OkHttpClient.Builder {
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.KITKAT) {
return this
}
val trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm())
trustManagerFactory.init(null as KeyStore?)
val trustManagers = trustManagerFactory.trustManagers
if (trustManagers.size == 1 && trustManagers[0] is X509TrustManager) {
class TLSSocketFactory @Throws(KeyManagementException::class, NoSuchAlgorithmException::class)
constructor() : SSLSocketFactory() {
private val internalSSLSocketFactory: SSLSocketFactory
init {
val context = SSLContext.getInstance("TLS")
context.init(null, null, null)
internalSSLSocketFactory = context.socketFactory
}
override fun getDefaultCipherSuites(): Array<String> {
return internalSSLSocketFactory.defaultCipherSuites
}
override fun getSupportedCipherSuites(): Array<String> {
return internalSSLSocketFactory.supportedCipherSuites
}
@Throws(IOException::class)
override fun createSocket(): Socket? {
return enableTLSOnSocket(internalSSLSocketFactory.createSocket())
}
@Throws(IOException::class)
override fun createSocket(s: Socket, host: String, port: Int, autoClose: Boolean): Socket? {
return enableTLSOnSocket(internalSSLSocketFactory.createSocket(s, host, port, autoClose))
}
@Throws(IOException::class, UnknownHostException::class)
override fun createSocket(host: String, port: Int): Socket? {
return enableTLSOnSocket(internalSSLSocketFactory.createSocket(host, port))
}
@Throws(IOException::class, UnknownHostException::class)
override fun createSocket(host: String, port: Int, localHost: InetAddress, localPort: Int): Socket? {
return enableTLSOnSocket(internalSSLSocketFactory.createSocket(host, port, localHost, localPort))
}
@Throws(IOException::class)
override fun createSocket(host: InetAddress, port: Int): Socket? {
return enableTLSOnSocket(internalSSLSocketFactory.createSocket(host, port))
}
@Throws(IOException::class)
override fun createSocket(address: InetAddress, port: Int, localAddress: InetAddress, localPort: Int): Socket? {
return enableTLSOnSocket(internalSSLSocketFactory.createSocket(address, port, localAddress, localPort))
}
private fun enableTLSOnSocket(socket: Socket?): Socket? {
if (socket != null && socket is SSLSocket) {
socket.enabledProtocols = socket.supportedProtocols
}
return socket
}
}
sslSocketFactory(TLSSocketFactory(), trustManagers[0] as X509TrustManager)
}
return this
}
}

View File

@ -277,7 +277,11 @@ class MangaInfoController : NucleusController<MangaInfoPresenter>(),
}
fun setLastUpdateDate(date: Date) {
manga_last_update?.text = DateFormat.getDateInstance(DateFormat.SHORT).format(date)
if (date.time != 0L) {
manga_last_update?.text = DateFormat.getDateInstance(DateFormat.SHORT).format(date)
} else {
manga_last_update?.text = resources?.getString(R.string.unknown)
}
}
/**

View File

@ -8,9 +8,9 @@ import com.jakewharton.rxbinding.widget.itemClicks
import com.jakewharton.rxbinding.widget.textChanges
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.database.models.Track
import eu.kanade.tachiyomi.data.track.model.TrackSearch
import eu.kanade.tachiyomi.data.track.TrackManager
import eu.kanade.tachiyomi.data.track.TrackService
import eu.kanade.tachiyomi.data.track.model.TrackSearch
import eu.kanade.tachiyomi.ui.base.controller.DialogController
import eu.kanade.tachiyomi.util.plusAssign
import kotlinx.android.synthetic.main.track_search_dialog.view.*
@ -114,14 +114,14 @@ class TrackSearchDialog : DialogController {
private fun search(query: String) {
val view = dialogView ?: return
view.progress.visibility = View.VISIBLE
view.track_search_list.visibility = View.GONE
view.track_search_list.visibility = View.INVISIBLE
trackController.presenter.search(query, service)
}
fun onSearchResults(results: List<TrackSearch>) {
selectedItem = null
val view = dialogView ?: return
view.progress.visibility = View.GONE
view.progress.visibility = View.INVISIBLE
view.track_search_list.visibility = View.VISIBLE
adapter?.setItems(results)
}
@ -129,7 +129,7 @@ class TrackSearchDialog : DialogController {
fun onSearchResultsError() {
val view = dialogView ?: return
view.progress.visibility = View.VISIBLE
view.track_search_list.visibility = View.GONE
view.track_search_list.visibility = View.INVISIBLE
adapter?.setItems(emptyList())
}

View File

@ -109,6 +109,7 @@ class SettingsAboutController : SettingsController() {
}
}
}, { error ->
activity?.toast(error.message)
Timber.e(error)
})
}

View File

@ -43,7 +43,7 @@ fun Context.toast(@StringRes resource: Int, duration: Int = Toast.LENGTH_SHORT)
* @param duration the duration of the toast. Defaults to short.
*/
fun Context.toast(text: String?, duration: Int = Toast.LENGTH_SHORT) {
Toast.makeText(this, text, duration).show()
Toast.makeText(this, text.orEmpty(), duration).show()
}
/**

View File

@ -105,7 +105,7 @@
tools:text="122"
tools:visibility="visible"
android:layout_marginEnd="8dp"
app:layout_constraintRight_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
android:layout_marginTop="8dp"
app:layout_constraintBottom_toBottomOf="parent"

View File

@ -63,7 +63,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:srcCompat="@drawable/ic_more_vert_black_24dp"
app:layout_constraintRight_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
android:paddingStart="24dp"
android:paddingEnd="16dp"
@ -82,7 +82,7 @@
android:layout_height="wrap_content"
tools:text="DOWNLOADED"
android:textAllCaps="true"
app:layout_constraintRight_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
android:layout_marginRight="16dp"/>

View File

@ -27,13 +27,16 @@
<TextView
android:id="@+id/ext_title"
style="@style/TextAppearance.Regular"
android:layout_width="wrap_content"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginRight="4dp"
android:layout_marginEnd="4dp"
android:ellipsize="end"
android:maxLines="1"
android:textAppearance="@style/TextAppearance.Regular.SubHeading"
android:textSize="14sp"
app:layout_constraintStart_toEndOf="@id/image"
app:layout_constraintEnd_toStartOf="@id/ext_button"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toTopOf="@id/lang"
app:layout_constraintVertical_chainStyle="packed"

View File

@ -1,10 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_height="match_parent"
android:orientation="vertical">
<EditText
@ -14,52 +13,49 @@
android:hint="@string/title"
android:layout_marginEnd="16dp"
android:layout_marginStart="16dp"
android:maxLines="1"
app:layout_constraintTop_toTopOf="parent"/>
android:inputType="text"
android:maxLines="1"/>
<ProgressBar
android:id="@+id/progress"
style="?android:attr/progressBarStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="32dp"
android:layout_marginTop="32dp"
android:visibility="gone"
app:layout_constraintBottom_toTopOf="@id/divider1"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/track_search"
tools:visibility="visible"/>
<ListView
android:id="@+id/track_search_list"
style="@style/Theme.Widget.CardView"
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginTop="40dp"
android:choiceMode="singleChoice"
android:clipToPadding="false"
android:divider="@null"
android:dividerHeight="10dp"
android:footerDividersEnabled="true"
android:headerDividersEnabled="true"
android:listSelector="?attr/selectable_list_drawable"
android:paddingBottom="4dp"
android:paddingTop="4dp"
android:scrollbars="none"
android:visibility="gone"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@+id/track_search"
tools:listitem="@layout/track_search_item"
tools:visibility="visible"/>
android:layout_height="0dp"
android:layout_weight="1">
<ProgressBar
android:id="@+id/progress"
style="?android:attr/progressBarStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="32dp"
android:layout_marginTop="32dp"
android:layout_gravity="center"
android:visibility="invisible"
tools:visibility="visible"/>
<ListView
android:id="@+id/track_search_list"
style="@style/Theme.Widget.CardView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clipToPadding="false"
android:choiceMode="singleChoice"
android:divider="@null"
android:dividerHeight="10dp"
android:footerDividersEnabled="true"
android:headerDividersEnabled="true"
android:listSelector="?attr/selectable_list_drawable"
android:paddingBottom="4dp"
android:paddingTop="4dp"
android:scrollbars="none"
android:visibility="invisible"
tools:listitem="@layout/track_search_item"
tools:visibility="visible"/>
</FrameLayout>
<View
android:id="@+id/divider1"
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="?android:attr/divider"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintTop_toBottomOf="@+id/track_search_list"/>
android:background="?android:attr/divider"/>
</android.support.constraint.ConstraintLayout>
</LinearLayout>

View File

@ -12,7 +12,6 @@
android:layout_height="216dp"
android:background="?attr/selectable_list_drawable"
android:orientation="horizontal">
>
<ImageView
android:id="@+id/track_search_cover"

View File

@ -22,6 +22,20 @@
<changelogtext>Various bug fixes</changelogtext>
</changelogversion>
<changelogversion versionName="v0.7.2" changeDate="">
<changelogtext>Fixed missing downloaded label in chapters screen.</changelogtext>
<changelogtext>Fixed updater in KitKat and lower due to TLS.</changelogtext>
</changelogversion>
<changelogversion versionName="v0.7.1" changeDate="">
<changelogtext>Updated Cloudflare bypass.</changelogtext>
<changelogtext>Enabled TLS 1.1 and TLS 1.2 on Android KitKat and lower.</changelogtext>
<changelogtext>Minor UI changes.</changelogtext>
</changelogversion>
<changelogversion versionName="v0.7.0" changeDate="">
<changelogtext>Added extensions support. You can now install and update extensions within the app.
If you installed any extension previously through F-Droid, you'll have to uninstall them first.</changelogtext>

View File

@ -7,7 +7,7 @@ buildscript {
google()
}
dependencies {
classpath 'com.android.tools.build:gradle:3.0.1'
classpath 'com.android.tools.build:gradle:3.1.0'
classpath 'com.github.ben-manes:gradle-versions-plugin:0.17.0'
classpath 'com.github.zellius:android-shortcut-gradle-plugin:0.1.2'
// NOTE: Do not place your application dependencies here; they belong

View File

@ -1,6 +1,6 @@
#Wed Oct 25 23:17:30 CEST 2017
#Thu Apr 05 09:21:32 CEST 2018
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-4.1-all.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-4.4-all.zip