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

View File

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

View File

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

View File

@ -1,9 +1,22 @@
package eu.kanade.tachiyomi.network package eu.kanade.tachiyomi.network
import android.content.Context import android.content.Context
import android.os.Build
import okhttp3.Cache import okhttp3.Cache
import okhttp3.OkHttpClient import okhttp3.OkHttpClient
import java.io.File 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) { class NetworkHelper(context: Context) {
@ -16,6 +29,7 @@ class NetworkHelper(context: Context) {
val client = OkHttpClient.Builder() val client = OkHttpClient.Builder()
.cookieJar(cookieManager) .cookieJar(cookieManager)
.cache(Cache(cacheDir, cacheSize)) .cache(Cache(cacheDir, cacheSize))
.enableTLS12()
.build() .build()
val cloudflareClient = client.newBuilder() val cloudflareClient = client.newBuilder()
@ -25,4 +39,75 @@ class NetworkHelper(context: Context) {
val cookies: PersistentCookieStore val cookies: PersistentCookieStore
get() = cookieManager.store 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) { 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 com.jakewharton.rxbinding.widget.textChanges
import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.database.models.Track 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.TrackManager
import eu.kanade.tachiyomi.data.track.TrackService 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.ui.base.controller.DialogController
import eu.kanade.tachiyomi.util.plusAssign import eu.kanade.tachiyomi.util.plusAssign
import kotlinx.android.synthetic.main.track_search_dialog.view.* import kotlinx.android.synthetic.main.track_search_dialog.view.*
@ -114,14 +114,14 @@ class TrackSearchDialog : DialogController {
private fun search(query: String) { private fun search(query: String) {
val view = dialogView ?: return val view = dialogView ?: return
view.progress.visibility = View.VISIBLE view.progress.visibility = View.VISIBLE
view.track_search_list.visibility = View.GONE view.track_search_list.visibility = View.INVISIBLE
trackController.presenter.search(query, service) trackController.presenter.search(query, service)
} }
fun onSearchResults(results: List<TrackSearch>) { fun onSearchResults(results: List<TrackSearch>) {
selectedItem = null selectedItem = null
val view = dialogView ?: return val view = dialogView ?: return
view.progress.visibility = View.GONE view.progress.visibility = View.INVISIBLE
view.track_search_list.visibility = View.VISIBLE view.track_search_list.visibility = View.VISIBLE
adapter?.setItems(results) adapter?.setItems(results)
} }
@ -129,7 +129,7 @@ class TrackSearchDialog : DialogController {
fun onSearchResultsError() { fun onSearchResultsError() {
val view = dialogView ?: return val view = dialogView ?: return
view.progress.visibility = View.VISIBLE view.progress.visibility = View.VISIBLE
view.track_search_list.visibility = View.GONE view.track_search_list.visibility = View.INVISIBLE
adapter?.setItems(emptyList()) adapter?.setItems(emptyList())
} }

View File

@ -109,6 +109,7 @@ class SettingsAboutController : SettingsController() {
} }
} }
}, { error -> }, { error ->
activity?.toast(error.message)
Timber.e(error) 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. * @param duration the duration of the toast. Defaults to short.
*/ */
fun Context.toast(text: String?, duration: Int = Toast.LENGTH_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:text="122"
tools:visibility="visible" tools:visibility="visible"
android:layout_marginEnd="8dp" android:layout_marginEnd="8dp"
app:layout_constraintRight_toLeftOf="parent" app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" app:layout_constraintTop_toTopOf="parent"
android:layout_marginTop="8dp" android:layout_marginTop="8dp"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"

View File

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

View File

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

View File

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

View File

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

View File

@ -22,6 +22,20 @@
<changelogtext>Various bug fixes</changelogtext> <changelogtext>Various bug fixes</changelogtext>
</changelogversion> </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=""> <changelogversion versionName="v0.7.0" changeDate="">
<changelogtext>Added extensions support. You can now install and update extensions within the app. <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> 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() google()
} }
dependencies { 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.ben-manes:gradle-versions-plugin:0.17.0'
classpath 'com.github.zellius:android-shortcut-gradle-plugin:0.1.2' classpath 'com.github.zellius:android-shortcut-gradle-plugin:0.1.2'
// NOTE: Do not place your application dependencies here; they belong // 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 distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists 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