Compare commits

...

15 Commits

Author SHA1 Message Date
a2b1b9e746 Release v0.7.2 2018-04-08 19:10:18 +02:00
8017324033 Fix #1351 2018-04-08 18:58:28 +02:00
7464497c88 Use our OkHttpClient in updates checker. It should fix the updater on KitKat due to TLS 2018-04-06 10:02:01 +02:00
499def3daa Fix downloaded text drawn outside the screen 2018-04-06 08:29:17 +02:00
6931b75cc5 Release v0.7.1 2018-04-05 23:01:32 +02:00
f853610578 Show last update if date > 0 2018-04-05 22:55:23 +02:00
69f51b88bf Fix typo in layout 2018-04-05 22:08:23 +02:00
e0d680201a Update constraint layout & fix broken layouts 2018-04-05 21:50:44 +02:00
1566b8f8b8 Provide accept & accept-language to cloudflare 2018-04-05 19:12:17 +02:00
4bbf78e840 Don't send cache control with cloudflare challenge 2018-04-05 11:58:28 +02:00
7ab16a69df Update travis script 2018-04-05 10:56:16 +02:00
95e60ed775 Update cloudflare interceptor and android studio 2018-04-05 10:36:29 +02:00
d38cd2547a Enable TLS 1.1 and TLS 1.2 on Android KitKat (and older) (#1316)
* Enable TLS 1.1 and TLS 1.2 on Android KitKat (and older)

* enable SSLv3

* use extension function
2018-03-25 17:08:29 +02:00
2159b72e69 Dialog color fix (#1308) 2018-03-14 18:01:30 +01:00
81c23bbf9d Update Batoto toString() method to support downloaded chapters 2018-03-14 12:54:31 +01:00
19 changed files with 161 additions and 37 deletions

View File

@ -1,7 +1,7 @@
language: android
android:
components:
- build-tools-27.0.2
- build-tools-27.0.3
- android-27
- extra-android-m2repository
- extra-google-m2repository

View File

@ -30,7 +30,7 @@ ext {
android {
compileSdkVersion 27
buildToolsVersion "27.0.2"
buildToolsVersion '27.0.3'
publishNonDefault true
defaultConfig {
@ -38,8 +38,8 @@ android {
minSdkVersion 16
targetSdkVersion 27
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
versionCode 33
versionName "0.7.0"
versionCode 35
versionName "0.7.2"
buildConfigField "String", "COMMIT_COUNT", "\"${getCommitCount()}\""
buildConfigField "String", "COMMIT_SHA", "\"${getGitSha()}\""
@ -116,7 +116,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

@ -24,4 +24,8 @@ class Batoto : Source {
return Observable.error(Exception("RIP Batoto"))
}
override fun toString(): String {
return "$name (EN)"
}
}

View File

@ -261,7 +261,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

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

View File

@ -36,7 +36,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

@ -259,32 +259,38 @@
<android.support.v4.widget.NestedScrollView
android:id="@+id/description_scrollview"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_height="wrap_content"
app:layout_constrainedHeight="true"
app:layout_constraintBottom_toTopOf="@id/manga_genres_tags"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@id/manga_summary_label"
app:layout_constraintBottom_toTopOf="@id/manga_genres_tags">
app:layout_constraintVertical_bias="0.0"
app:layout_constraintVertical_chainStyle="packed">
<TextView
android:id="@+id/manga_summary"
style="@style/TextAppearance.Regular.Body1.Secondary"
android:layout_marginLeft="16dp"
android:layout_marginRight="16dp"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textIsSelectable="false"/>
android:layout_marginLeft="16dp"
android:layout_marginRight="16dp"
android:textIsSelectable="false" />
</android.support.v4.widget.NestedScrollView>
<me.gujun.android.taggroup.TagGroup
android:id="@+id/manga_genres_tags"
style="@style/TagGroup"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginEnd="8dp"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
android:layout_marginLeft="16dp"
android:layout_marginRight="16dp"
android:layout_marginBottom="8dp"
app:layout_constrainedHeight="true"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@id/description_scrollview"

View File

@ -1,5 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<changelog bulletedList="true">
<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

@ -27,7 +27,7 @@
<!-- Attributes specific for SDK 21 and up -->
<item name="android:windowDrawsSystemBarBackgrounds">true</item>
<item name="android:statusBarColor">@android:color/transparent</item>
<item name="android:navigationBarColor">@color/colorAmoled</item>
<item name="android:navigationBarColor">@color/colorAmoledPrimary</item>
</style>
<!--==============-->

View File

@ -1,12 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- Application Colors -->
<color name="colorPrimary">#54759e</color>
<color name="colorPrimaryDark">#435e7e</color>
<color name="colorPrimary">#54759E</color>
<color name="colorPrimaryDark">#435E7E</color>
<!-- Dark Application Colors -->
<color name="colorDarkPrimary">#212121</color>
<color name="colorDarkPrimaryDark">#1c1c1d</color>
<color name="colorAmoled">@color/md_black_1000</color>
<color name="colorDarkPrimaryDark">#1C1C1D</color>
<color name="colorAmoledPrimary">@color/md_black_1000</color>
<!-- Light Theme -->
<color name="colorAccentLight">@color/md_blue_A400</color>
@ -27,7 +27,7 @@
<color name="iconColorLight">@color/md_black_1000</color>
<!-- Dark Theme -->
<color name="colorAccentDark">#3399ff</color>
<color name="colorAccentDark">#3399FF</color>
<color name="textColorPrimaryDark">@color/md_white_1000</color>
<color name="textColorSecondaryDark">@color/md_white_1000_70</color>
<color name="textColorHintDark">@color/md_white_1000_50</color>
@ -36,7 +36,7 @@
<color name="statusBarDark">@color/md_black_1000</color>
<color name="appBarDark">@color/md_grey_900</color>
<color name="backgroundDark">#1c1c1d</color>
<color name="backgroundDark">@color/colorDarkPrimaryDark</color>
<color name="dialogDark">@color/colorDarkPrimary</color>
<color name="dialog_amoled">@color/colorDarkPrimaryDark</color>

View File

@ -66,6 +66,7 @@
<item name="actionBarTheme">@style/ThemeOverlay.AppCompat.Dark.ActionBar</item>
<item name="actionBarPopupTheme">@style/ThemeOverlay.AppCompat</item>
<item name="preferenceTheme">@style/PreferenceThemeOverlay.Material</item>
<item name="md_background_color">@color/dialogDark</item>
<item name="alertDialogTheme">@style/Theme.AlertDialog.Dark</item>
@ -86,8 +87,8 @@
<!-- Amoled Theme -->
<!--==============-->
<style name="Theme.Base.Amoled" parent="Theme.Base.Dark">
<item name="colorPrimary">@color/colorAmoled</item>
<item name="colorPrimaryDark">@color/colorAmoled</item>
<item name="colorPrimary">@color/colorAmoledPrimary</item>
<item name="colorPrimaryDark">@color/colorAmoledPrimary</item>
<item name="android:colorBackground">@color/md_black_1000</item>
<!-- Custom Attributes-->

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