Compare commits

...

28 Commits

Author SHA1 Message Date
f8a03226ee Release v0.7.3 2018-04-28 11:10:29 +02:00
32db1e3045 Run downloader in foreground service 2018-04-28 10:54:27 +02:00
303e6c0102 Reorganize reader settings. Update Conductor version 2018-04-28 10:40:08 +02:00
18883f1ba3 Crop borders for webtoon now have a separate setting. Close #972 2018-04-27 16:55:12 +02:00
5c31271e91 Workaround a crash related to saving instance state and child controllers 2018-04-25 16:26:46 +02:00
00981cf4e8 Include firebase analytics 2018-04-25 13:46:57 +02:00
968f4a69e8 Separate 'en' locale into 'en-US' and 'en-GB' for displaying dates 2018-04-22 13:15:47 +02:00
e7e1a9bf50 Fix #1073 2018-04-15 13:26:33 +02:00
fe1becb001 Update strings.xml (#1324) 2018-04-15 12:59:52 +02:00
7789171c71 Remove F-Droid.org link (#1357)
* remove fdroid.org

* Update README.md

* fix broken links
2018-04-15 12:48:03 +02:00
3fd2222c99 Update russian sources (#1362)
1) moved headerBuilder to imageRequest
2) changed the method of gets thumbnails
3) updated js for genres
4) update genre list
2018-04-15 12:47:39 +02:00
6de36a88c0 Fix tracking search layout 2018-04-13 16:28:09 +02:00
b37685542d Add new translation link (#1355) 2018-04-09 16:25:30 +02:00
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
43 changed files with 480 additions and 182 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

@ -2,6 +2,8 @@
git fetch --unshallow #required for commit count
cp .travis/google-services.json app/
if [ -z "$TRAVIS_TAG" ]; then
./gradlew clean assembleStandardDebug

View File

@ -0,0 +1,73 @@
{
"project_info": {
"project_number": "777921915939",
"firebase_url": "https://tachiyomi-47364.firebaseio.com",
"project_id": "tachiyomi-47364",
"storage_bucket": "tachiyomi-47364.appspot.com"
},
"client": [
{
"client_info": {
"mobilesdk_app_id": "1:777921915939:android:36544cd2d96c50c7",
"android_client_info": {
"package_name": "eu.kanade.tachiyomi"
}
},
"oauth_client": [
{
"client_id": "777921915939-9q25jvgbdtpk91daqlk7sa1cbdcg77o6.apps.googleusercontent.com",
"client_type": 3
}
],
"api_key": [
{
"current_key": "AIzaSyAHr8RxyeiSPC_MxJTnivz-hmdo5oX0QQQ"
}
],
"services": {
"analytics_service": {
"status": 1
},
"appinvite_service": {
"status": 1,
"other_platform_oauth_client": []
},
"ads_service": {
"status": 2
}
}
},
{
"client_info": {
"mobilesdk_app_id": "1:777921915939:android:564fdc1d62efd1de",
"android_client_info": {
"package_name": "eu.kanade.tachiyomi.debug"
}
},
"oauth_client": [
{
"client_id": "777921915939-9q25jvgbdtpk91daqlk7sa1cbdcg77o6.apps.googleusercontent.com",
"client_type": 3
}
],
"api_key": [
{
"current_key": "AIzaSyAHr8RxyeiSPC_MxJTnivz-hmdo5oX0QQQ"
}
],
"services": {
"analytics_service": {
"status": 1
},
"appinvite_service": {
"status": 1,
"other_platform_oauth_client": []
},
"ads_service": {
"status": 2
}
}
}
],
"configuration_version": "1"
}

View File

@ -1,6 +1,6 @@
| Build | Download | F-Droid | Contribute | Contact |
| Build | Stable | Dev | Contribute | Contact |
|-------|----------|---------|------------|---------|
| [![Travis](https://img.shields.io/travis/inorichi/tachiyomi.svg)](https://travis-ci.org/inorichi/tachiyomi) | [![stable release](https://img.shields.io/github/release/inorichi/tachiyomi.svg?maxAge=3600&label=stable)](https://github.com/inorichi/tachiyomi/releases) [![latest dev build](https://img.shields.io/badge/dev-latest%20build-blue.svg)](http://tachiyomi.kanade.eu/latest) | [![fdroid release](https://img.shields.io/badge/stable-f--droid.org-blue.svg)](https://f-droid.org/repository/browse/?fdid=eu.kanade.tachiyomi) [![fdroid dev](https://img.shields.io/badge/dev-wiki-blue.svg)](//github.com/inorichi/tachiyomi/wiki/FDroid-for-dev-versions) | [![Translation status](http://weblate.j2ghz.com/widgets/tachiyomi/-/svg-badge.svg)](https://github.com/inorichi/tachiyomi/wiki/Translation) | [![Discord](https://img.shields.io/discord/349436576037732353.svg)](https://discord.gg/2dDQBv2) |
| [![Travis](https://img.shields.io/travis/inorichi/tachiyomi.svg)](https://travis-ci.org/inorichi/tachiyomi) | [![stable release](https://img.shields.io/github/release/inorichi/tachiyomi.svg?maxAge=3600&label=download%20(autoupdate%20included))](https://github.com/inorichi/tachiyomi/releases) | [![latest dev build](https://img.shields.io/badge/download-latest%20build-blue.svg)](http://tachiyomi.kanade.eu/latest) [![fdroid dev](https://img.shields.io/badge/autoupdate-wiki-blue.svg)](//github.com/inorichi/tachiyomi/wiki/F-Droid-for-dev-versions) | [![Translation status](https://hosted.weblate.org/widgets/tachiyomi/-/svg-badge.svg)](https://hosted.weblate.org/engage/tachiyomi/?utm_source=widget) | [![Discord](https://img.shields.io/discord/349436576037732353.svg)](https://discord.gg/2dDQBv2) |
# ![app icon](./.github/readme-images/app-icon.png)Tachiyomi
@ -21,9 +21,9 @@ Features include:
* Create backups locally or to your cloud service of choice
## Download
Get the app from our [releases page](https://github.com/inorichi/tachiyomi/releases) or from [f-droid](https://f-droid.org/packages/eu.kanade.tachiyomi/).
Get the app from our [releases page](https://github.com/inorichi/tachiyomi/releases).
If you want to try new features before they get to the stable release, you can download the dev version [here](http://tachiyomi.kanade.eu/latest) (auto-updates not included), or add our [F-Droid repo](https://github.com/inorichi/tachiyomi/wiki/FDroid-for-dev-versions).
If you want to try new features before they get to the stable release, you can download the dev version [here](http://tachiyomi.kanade.eu/latest) (auto-updates not included), or add our [F-Droid repo](https://github.com/inorichi/tachiyomi/wiki/F-Droid-for-dev-versions).
## Issues, Feature Requests and Contributing

3
app/.gitignore vendored
View File

@ -1,4 +1,5 @@
/build
*iml
*.iml
custom.gradle
custom.gradle
google-services.json

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 36
versionName "0.7.3"
buildConfigField "String", "COMMIT_COUNT", "\"${getCommitCount()}\""
buildConfigField "String", "COMMIT_SHA", "\"${getGitSha()}\""
@ -116,10 +116,12 @@ 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'
standardImplementation 'com.google.firebase:firebase-core:12.0.1'
// ReactiveX
implementation 'io.reactivex:rxandroid:1.2.1'
implementation 'io.reactivex:rxjava:1.3.6'
@ -153,7 +155,7 @@ dependencies {
// Job scheduling
implementation 'com.evernote:android-job:1.2.4'
implementation 'com.google.android.gms:play-services-gcm:11.8.0'
implementation 'com.google.android.gms:play-services-gcm:12.0.1'
// Changelog
implementation 'com.github.gabrielemariotti.changeloglib:changelog:2.1.0'
@ -201,7 +203,7 @@ dependencies {
implementation 'me.gujun.android.taggroup:library:1.4@aar'
// Conductor
implementation "com.github.inorichi.Conductor:conductor:05c4d4d"
implementation "com.github.inorichi.Conductor:conductor:be8b3c5"
implementation ("com.bluelinelabs:conductor-support:2.1.5-SNAPSHOT") {
exclude group: "com.bluelinelabs", module: "conductor"
}
@ -254,3 +256,7 @@ kotlin {
androidExtensions {
experimental = true
}
if (getGradle().getStartParameter().getTaskRequests().toString().contains("Standard")) {
apply plugin: 'com.google.gms.google-services'
}

View File

@ -1,16 +1,20 @@
package eu.kanade.tachiyomi.data.download
import android.app.Notification
import android.app.Service
import android.content.Context
import android.content.Intent
import android.net.NetworkInfo.State.CONNECTED
import android.net.NetworkInfo.State.DISCONNECTED
import android.os.Build
import android.os.IBinder
import android.os.PowerManager
import android.support.v4.app.NotificationCompat
import com.github.pwittchen.reactivenetwork.library.Connectivity
import com.github.pwittchen.reactivenetwork.library.ReactiveNetwork
import com.jakewharton.rxrelay.BehaviorRelay
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.notification.Notifications
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
import eu.kanade.tachiyomi.util.connectivityManager
import eu.kanade.tachiyomi.util.plusAssign
@ -41,7 +45,12 @@ class DownloadService : Service() {
* @param context the application context.
*/
fun start(context: Context) {
context.startService(Intent(context, DownloadService::class.java))
val intent = Intent(context, DownloadService::class.java)
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
context.startService(intent)
} else {
context.startForegroundService(intent)
}
}
/**
@ -81,6 +90,7 @@ class DownloadService : Service() {
*/
override fun onCreate() {
super.onCreate()
startForeground(Notifications.ID_DOWNLOAD_CHAPTER, getPlaceholderNotification())
runningRelay.call(true)
subscriptions = CompositeSubscription()
listenDownloaderState()
@ -176,4 +186,10 @@ class DownloadService : Service() {
if (!isHeld) acquire()
}
private fun getPlaceholderNotification(): Notification {
return NotificationCompat.Builder(this, Notifications.CHANNEL_DOWNLOADER)
.setContentTitle(getString(R.string.download_notifier_downloader_title))
.build()
}
}

View File

@ -39,6 +39,8 @@ object PreferenceKeys {
const val cropBorders = "crop_borders"
const val cropBordersWebtoon = "crop_borders_webtoon"
const val readWithTapping = "reader_tap"
const val readWithVolumeKeys = "reader_volume_keys"

View File

@ -67,6 +67,8 @@ class PreferencesHelper(val context: Context) {
fun cropBorders() = rxPrefs.getBoolean(Keys.cropBorders, false)
fun cropBordersWebtoon() = rxPrefs.getBoolean(Keys.cropBordersWebtoon, false)
fun readWithTapping() = rxPrefs.getBoolean(Keys.readWithTapping, true)
fun readWithVolumeKeys() = rxPrefs.getBoolean(Keys.readWithVolumeKeys, false)

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,32 +44,33 @@ 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

@ -49,8 +49,12 @@ class Mangachan : ParsedHttpSource() {
}
}
}
is OrderBy -> { if (filter.state!!.ascending && filter.state!!.index == 0) { statusParam = false } }
is Status -> status = arrayOf("", "all_done", "end", "ongoing", "new_ch")[filter.state]
is OrderBy -> {
if (filter.state!!.ascending && filter.state!!.index == 0) {
statusParam = false
}
}
is Status -> status = arrayOf("", "all_done", "end", "ongoing", "new_ch")[filter.state]
}
}
@ -103,6 +107,7 @@ class Mangachan : ParsedHttpSource() {
override fun popularMangaFromElement(element: Element): SManga {
val manga = SManga.create()
manga.thumbnail_url = element.select("div.manga_images img").first().attr("src")
element.select("h2 > a").first().let {
manga.setUrlWithoutDomain(it.attr("href"))
manga.title = it.text()
@ -220,32 +225,22 @@ class Mangachan : ParsedHttpSource() {
GenreList(getGenreList())
)
// private class StatusList(status: List<Status>) : Filter.Group<Status>("Статус", status)
// private class Status(name: String, val id: String) : Filter.CheckBox(name, false)
// private fun getStatusList() = listOf(
// Status("Перевод завершен", "/all_done"),
// Status("Выпуск завершен", "/end"),
// Status("Онгоинг", "/ongoing"),
// Status("Новые главы", "/new_ch")
// )
/* [...document.querySelectorAll("li.sidetag > a:nth-child(1)")].map((el,i) =>
* { const link=el.getAttribute('href');const id=link.substr(6,link.length);
* return `Genre("${id.replace("_", " ")}")` }).join(',\n')
/* [...document.querySelectorAll("li.sidetag > a:nth-child(1)")]
* .map(el => `Genre("${el.getAttribute('href').substr(6)}")`).join(',\n')
* on http://mangachan.me/
*/
private fun getGenreList() = listOf(
Genre("18 плюс"),
Genre("18_плюс"),
Genre("bdsm"),
Genre("арт"),
Genre("боевик"),
Genre("боевые искусства"),
Genre("боевые_искусства"),
Genre("вампиры"),
Genre("веб"),
Genre("гарем"),
Genre("гендерная интрига"),
Genre("героическое фэнтези"),
Genre("гендерная_интрига"),
Genre("героическое_фэнтези"),
Genre("детектив"),
Genre("дзёсэй"),
Genre("додзинси"),
@ -262,13 +257,13 @@ class Mangachan : ParsedHttpSource() {
Genre("меха"),
Genre("мистика"),
Genre("музыка"),
Genre("научная фантастика"),
Genre("научная_фантастика"),
Genre("повседневность"),
Genre("постапокалиптика"),
Genre("приключения"),
Genre("психология"),
Genre("романтика"),
Genre("самурайский боевик"),
Genre("самурайский_боевик"),
Genre("сборник"),
Genre("сверхъестественное"),
Genre("сказка"),
@ -279,7 +274,6 @@ class Mangachan : ParsedHttpSource() {
Genre("сёдзё-ай"),
Genre("сёнэн"),
Genre("сёнэн-ай"),
Genre("темное фэнтези"),
Genre("тентакли"),
Genre("трагедия"),
Genre("триллер"),

View File

@ -24,23 +24,19 @@ class Mintmanga : ParsedHttpSource() {
override val supportsLatest = true
override fun headersBuilder() = Headers.Builder().apply {
add("User-Agent", "Mozilla/5.0 (Windows NT 6.3; WOW64)")
add("Referer", baseUrl)
}
override fun popularMangaRequest(page: Int): Request =
GET("$baseUrl/list?sortType=rate&offset=${70 * (page - 1)}&max=70", headers)
override fun latestUpdatesRequest(page: Int): Request =
GET("$baseUrl/list?sortType=updated&offset=${70 * (page - 1)}&max=70", headers)
override fun popularMangaSelector() = "div.desc"
override fun popularMangaSelector() = "div.tile"
override fun latestUpdatesSelector() = "div.desc"
override fun latestUpdatesSelector() = "div.tile"
override fun popularMangaFromElement(element: Element): SManga {
val manga = SManga.create()
manga.thumbnail_url = element.select("img.lazy").first().attr("data-original")
element.select("h3 > a").first().let {
manga.setUrlWithoutDomain(it.attr("href"))
manga.title = it.attr("title")
@ -90,10 +86,15 @@ class Mintmanga : ParsedHttpSource() {
override fun chapterFromElement(element: Element): SChapter {
val urlElement = element.select("a").first()
val urlText = urlElement.text()
val chapter = SChapter.create()
chapter.setUrlWithoutDomain(urlElement.attr("href") + "?mature=1")
chapter.name = urlElement.text().replace(" новое", "")
chapter.setUrlWithoutDomain(urlElement.attr("href") + "?mtr=1")
if (urlText.endsWith(" новое")) {
chapter.name = urlText.dropLast(6)
} else {
chapter.name = urlText
}
chapter.date_upload = element.select("td:eq(1)").first()?.text()?.let {
SimpleDateFormat("dd/MM/yy", Locale.US).parse(it).time
} ?: 0
@ -143,11 +144,19 @@ class Mintmanga : ParsedHttpSource() {
override fun imageUrlParse(document: Document) = ""
override fun imageRequest(page: Page): Request {
val imgHeader = Headers.Builder().apply {
add("User-Agent", "Mozilla/5.0 (Windows NT 6.3; WOW64)")
add("Referer", baseUrl)
}.build()
return GET(page.imageUrl!!, imgHeader)
}
private class Genre(name: String, val id: String) : Filter.TriState(name)
/* [...document.querySelectorAll("tr.advanced_option:nth-child(1) > td:nth-child(3) span.js-link")].map((el,i) => {
* const onClick=el.getAttribute('onclick');const id=onClick.substr(31,onClick.length-33);
* return `Genre("${el.textContent.trim()}", "${id}")` }).join(',\n')
/* [...document.querySelectorAll("tr.advanced_option:nth-child(1) > td:nth-child(3) span.js-link")]
* .map(el => `Genre("${el.textContent.trim()}", $"{el.getAttribute('onclick')
* .substr(31,el.getAttribute('onclick').length-33)"})`).join(',\n')
* on http://mintmanga.com/search/advanced
*/
override fun getFilterList() = FilterList(

View File

@ -24,14 +24,9 @@ class Readmanga : ParsedHttpSource() {
override val supportsLatest = true
override fun headersBuilder() = Headers.Builder().apply {
add("User-Agent", "Mozilla/5.0 (Windows NT 6.3; WOW64)")
add("Referer", baseUrl)
}
override fun popularMangaSelector() = "div.tile"
override fun popularMangaSelector() = "div.desc"
override fun latestUpdatesSelector() = "div.desc"
override fun latestUpdatesSelector() = "div.tile"
override fun popularMangaRequest(page: Int): Request =
GET("$baseUrl/list?sortType=rate&offset=${70 * (page - 1)}&max=70", headers)
@ -41,6 +36,7 @@ class Readmanga : ParsedHttpSource() {
override fun popularMangaFromElement(element: Element): SManga {
val manga = SManga.create()
manga.thumbnail_url = element.select("img.lazy").first().attr("data-original")
element.select("h3 > a").first().let {
manga.setUrlWithoutDomain(it.attr("href"))
manga.title = it.attr("title")
@ -90,10 +86,15 @@ class Readmanga : ParsedHttpSource() {
override fun chapterFromElement(element: Element): SChapter {
val urlElement = element.select("a").first()
val urlText = urlElement.text()
val chapter = SChapter.create()
chapter.setUrlWithoutDomain(urlElement.attr("href") + "?mature=1")
chapter.name = urlElement.text().replace(" новое", "")
chapter.setUrlWithoutDomain(urlElement.attr("href") + "?mtr=1")
if (urlText.endsWith(" новое")) {
chapter.name = urlText.dropLast(6)
} else {
chapter.name = urlText
}
chapter.date_upload = element.select("td:eq(1)").first()?.text()?.let {
SimpleDateFormat("dd/MM/yy", Locale.US).parse(it).time
} ?: 0
@ -143,11 +144,19 @@ class Readmanga : ParsedHttpSource() {
override fun imageUrlParse(document: Document) = ""
override fun imageRequest(page: Page): Request {
val imgHeader = Headers.Builder().apply {
add("User-Agent", "Mozilla/5.0 (Windows NT 6.3; WOW64)")
add("Referer", baseUrl)
}.build()
return GET(page.imageUrl!!, imgHeader)
}
private class Genre(name: String, val id: String) : Filter.TriState(name)
/* [...document.querySelectorAll("tr.advanced_option:nth-child(1) > td:nth-child(3) span.js-link")].map((el,i) => {
* const onClick=el.getAttribute('onclick');const id=onClick.substr(31,onClick.length-33);
* return `Genre("${el.textContent.trim()}", "${id}")` }).join(',\n')
/* [...document.querySelectorAll("tr.advanced_option:nth-child(1) > td:nth-child(3) span.js-link")]
* .map(el => `Genre("${el.textContent.trim()}", $"{el.getAttribute('onclick')
* .substr(31,el.getAttribute('onclick').length-33)"})`).join(',\n')
* on http://readmanga.me/search/advanced
*/
override fun getFilterList() = FilterList(

View File

@ -28,7 +28,7 @@ public class NucleusConductorDelegate<P extends Presenter> {
Bundle onSaveInstanceState() {
Bundle bundle = new Bundle();
getPresenter();
// getPresenter(); // Workaround a crash related to saving instance state with child routers
if (presenter != null) {
presenter.save(bundle);
}

View File

@ -34,7 +34,7 @@ class LibraryCategoryAdapter(view: LibraryCategoryView) :
* @param manga the manga to find.
*/
fun indexOf(manga: Manga): Int {
return mangas.indexOfFirst { it.manga.id == manga.id }
return currentItems.indexOfFirst { it.manga.id == manga.id }
}
fun performFilter() {

View File

@ -73,7 +73,7 @@ class LibraryController(
/**
* Currently selected mangas.
*/
val selectedMangas = mutableListOf<Manga>()
val selectedMangas = mutableSetOf<Manga>()
private var selectedCoverManga: Manga? = null
@ -429,11 +429,13 @@ class LibraryController(
*/
fun setSelection(manga: Manga, selected: Boolean) {
if (selected) {
selectedMangas.add(manga)
selectionRelay.call(LibrarySelectionEvent.Selected(manga))
if (selectedMangas.add(manga)) {
selectionRelay.call(LibrarySelectionEvent.Selected(manga))
}
} else {
selectedMangas.remove(manga)
selectionRelay.call(LibrarySelectionEvent.Unselected(manga))
if (selectedMangas.remove(manga)) {
selectionRelay.call(LibrarySelectionEvent.Unselected(manga))
}
}
}

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

@ -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())
}
@ -141,4 +141,4 @@ class TrackSearchDialog : DialogController {
const val KEY_SERVICE = "service_id"
}
}
}

View File

@ -9,6 +9,7 @@ import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
import eu.kanade.tachiyomi.data.preference.getOrDefault
import eu.kanade.tachiyomi.util.plusAssign
import eu.kanade.tachiyomi.util.visibleIf
import eu.kanade.tachiyomi.widget.IgnoreFirstSpinnerListener
import kotlinx.android.synthetic.main.reader_settings_dialog.view.*
import rx.Observable
@ -91,6 +92,23 @@ class ReaderSettingsDialog : DialogFragment() {
crop_borders.setOnCheckedChangeListener { _, isChecked ->
preferences.cropBorders().set(isChecked)
}
crop_borders_webtoon.isChecked = preferences.cropBordersWebtoon().getOrDefault()
crop_borders_webtoon.setOnCheckedChangeListener { _, isChecked ->
preferences.cropBordersWebtoon().set(isChecked)
}
val readerActivity = activity as? ReaderActivity
val isWebtoonViewer = if (readerActivity != null) {
val mangaViewer = readerActivity.presenter.manga.viewer
val viewer = if (mangaViewer == 0) preferences.defaultViewer() else mangaViewer
viewer == ReaderActivity.WEBTOON
} else {
false
}
crop_borders.visibleIf { !isWebtoonViewer }
crop_borders_webtoon.visibleIf { isWebtoonViewer }
}
override fun onDestroyView() {
@ -98,4 +116,4 @@ class ReaderSettingsDialog : DialogFragment() {
super.onDestroyView()
}
}
}

View File

@ -4,7 +4,12 @@ import android.os.Build
import android.os.Bundle
import android.support.v7.widget.RecyclerView
import android.util.DisplayMetrics
import android.view.*
import android.view.Display
import android.view.GestureDetector
import android.view.LayoutInflater
import android.view.MotionEvent
import android.view.View
import android.view.ViewGroup
import android.view.ViewGroup.LayoutParams.MATCH_PARENT
import android.view.ViewGroup.LayoutParams.WRAP_CONTENT
import eu.kanade.tachiyomi.source.model.Page
@ -123,7 +128,7 @@ class WebtoonReader : BaseReader() {
.distinctUntilChanged()
.subscribe { refreshAdapter() })
subscriptions.add(readerActivity.preferences.cropBorders()
subscriptions.add(readerActivity.preferences.cropBordersWebtoon()
.asObservable()
.doOnNext { cropBorders = it }
.skip(1)

View File

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

View File

@ -29,8 +29,9 @@ class SettingsGeneralController : SettingsController() {
listPreference {
key = Keys.lang
titleRes = R.string.pref_language
entryValues = arrayOf("", "ar", "bg", "bn", "de", "en", "es", "fr", "hi", "hu", "id",
"it", "ja", "ko", "lv", "ms", "nl", "pl", "pt", "pt-BR", "ro", "ru", "vi")
entryValues = arrayOf("", "ar", "bg", "bn", "de", "en-US", "en-GB", "es", "fr", "hi",
"hu", "id", "it", "ja", "ko", "lv", "ms", "nl", "pl", "pt", "pt-BR", "ro",
"ru", "vi")
entries = entryValues.map { value ->
val locale = LocaleHelper.getLocaleFromString(value.toString())
locale?.getDisplayName(locale)?.capitalize() ?:

View File

@ -76,8 +76,8 @@ class SettingsReaderController : SettingsController() {
defaultValue = true
}
switchPreference {
key = Keys.enableTransitions
titleRes = R.string.pref_page_transitions
key = Keys.keepScreenOn
titleRes = R.string.pref_keep_screen_on
defaultValue = true
}
switchPreference {
@ -85,15 +85,28 @@ class SettingsReaderController : SettingsController() {
titleRes = R.string.pref_show_page_number
defaultValue = true
}
switchPreference {
key = Keys.cropBorders
titleRes = R.string.pref_crop_borders
defaultValue = false
preferenceCategory {
titleRes = R.string.pager_viewer
switchPreference {
key = Keys.enableTransitions
titleRes = R.string.pref_page_transitions
defaultValue = true
}
switchPreference {
key = Keys.cropBorders
titleRes = R.string.pref_crop_borders
defaultValue = false
}
}
switchPreference {
key = Keys.keepScreenOn
titleRes = R.string.pref_keep_screen_on
defaultValue = true
preferenceCategory {
titleRes = R.string.webtoon_viewer
switchPreference {
key = Keys.cropBordersWebtoon
titleRes = R.string.pref_crop_borders
defaultValue = false
}
}
preferenceCategory {
titleRes = R.string.pref_reader_navigation
@ -116,4 +129,4 @@ class SettingsReaderController : SettingsController() {
}
}
}
}

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

@ -47,6 +47,10 @@ inline fun View.gone() {
visibility = View.GONE
}
inline fun View.visibleIf(block: () -> Boolean) {
visibility = if (block()) View.VISIBLE else View.GONE
}
/**
* Returns a TextDrawable determined by input
*
@ -63,4 +67,4 @@ fun View.getRound(text: String, random : Boolean = true): TextDrawable {
.useFont(Typeface.DEFAULT)
.endConfig()
.buildRound(text, if (random) ColorGenerator.MATERIAL.randomColor else ColorGenerator.MATERIAL.getColor(text))
}
}

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

@ -171,10 +171,16 @@
android:layout_height="wrap_content"
android:text="@string/pref_crop_borders"/>
<android.support.v7.widget.SwitchCompat
android:id="@+id/crop_borders_webtoon"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/pref_crop_borders"/>
<android.support.v7.widget.SwitchCompat
android:id="@+id/fullscreen"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/pref_fullscreen"/>
</LinearLayout>
</LinearLayout>

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"
@ -161,4 +160,4 @@
app:layout_constraintGuide_begin="150dp"/>
</android.support.constraint.ConstraintLayout>
</android.support.v7.widget.CardView>
</android.support.v7.widget.CardView>

View File

@ -1,5 +1,33 @@
<?xml version="1.0" encoding="utf-8"?>
<changelog bulletedList="true">
<changelogversion versionName="v0.7.3" changeDate="">
<changelogtext>Fixed the tracking search layout when there are many results.</changelogtext>
<changelogtext>Separate english language into american and british so that dates are formatted according to that locale.</changelogtext>
<changelogtext>Added Firebase analytics, for Android API distribution.</changelogtext>
<changelogtext>Crop borders for webtoons now has a separate setting.</changelogtext>
<changelogtext>The downloader now runs in a foreground service to prevent it from being killed.</changelogtext>
<changelogtext>Fixed a few weird crashes.</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

@ -11,7 +11,7 @@
<string name="label_recent_updates">Nouveautés bibliothèque</string>
<string name="label_latest_updates">Dernières nouveautés</string>
<string name="label_categories">Catégories</string>
<string name="label_selected">Sélectionné: %1$d</string>
<string name="label_selected">Sélectionné : %1$d</string>
<string name="label_backup">Sauvegarde</string>
<!-- Actions -->
@ -57,7 +57,7 @@
<string name="action_retry">Réessayer</string>
<string name="action_remove">Supprimer</string>
<string name="action_resume">Reprendre</string>
<string name="action_open_in_browser">Ouvre dans le navigateur</string>
<string name="action_open_in_browser">Ouvrir dans le navigateur</string>
<string name="action_add_to_home_screen">Ajouter à l\'écran d\'accueil</string>
<string name="action_display_mode">Changer le mode d\'affichage</string>
<string name="action_display">Affichage</string>
@ -152,9 +152,9 @@
<string name="rotation_force_portrait">Forcer portrait</string>
<string name="rotation_force_landscape">Forcer paysage</string>
<string name="color_filter_r_value">R</string>
<string name="color_filter_g_value">G</string>
<string name="color_filter_g_value">V</string>
<string name="color_filter_b_value">B</string>
<string name="color_filter_a_value">A</string>
<string name="color_filter_a_value">O</string>
<!-- Downloads section -->
@ -164,7 +164,7 @@
<string name="pref_remove_after_read">Supprimer après avoir lu</string>
<string name="disabled">Désactivé</string>
<string name="last_read_chapter">Dernier chapitre lu</string>
<string name="second_to_last">Avant-dernier chapiture lu</string>
<string name="second_to_last">Avant-dernier chapitre lu</string>
<string name="third_to_last">Du troisième au dernier chapitre</string>
<string name="fourth_to_last">Du quatrième au dernier chapitre</string>
<string name="pref_download_new">Télécharger les nouveaux chapitres</string>
@ -286,7 +286,7 @@
<!-- Reader activity -->
<string name="custom_filter">Filtre personnalisé</string>
<string name="set_as_cover">Mettre comme couverture</string>
<string name="set_as_cover">Ajouter comme couverture</string>
<string name="cover_updated">Couverture mise à jour</string>
<string name="page_downloaded">Page copiée vers %1$s</string>
<string name="downloading">En cours de téléchargement…</string>
@ -313,7 +313,7 @@
<string name="notification_update_progress">Progression mise à jour: %1$d/%2$d</string>
<string name="notification_new_chapters">Des nouveaux chapitres ont été trouvés</string>
<string name="notification_cover_update_failed">La mise à jour de la couverture a échoué</string>
<string name="notification_first_add_to_library">Veuillez ajouter le manga dans votre bibliothèque avant de faire cela</string>
<string name="notification_first_add_to_library">Veuillez ajouter le manga dans votre bibliothèque avant</string>
<string name="notification_not_connected_to_ac_body">Non branché</string>
<string name="notification_no_connection_title">Synchronisation annulée</string>
<string name="notification_no_connection_body">Connexion non disponible</string>
@ -324,15 +324,15 @@
<string name="file_select_icon">Sélectionner une icône de raccourci</string>
<!--UpdateCheck-->
<string name="update_check_title">Nouvelle mise à jour disponible!</string>
<string name="update_check_title">Nouvelle mise à jour disponible !</string>
<string name="update_check_confirm">Télécharger</string>
<string name="update_check_ignore">Ignorer</string>
<string name="update_check_no_new_updates">Aucun mise à jour disponible</string>
<string name="update_check_no_new_updates">Aucune mise à jour disponible</string>
<string name="update_check_download_started">Téléchargement commencé</string>
<string name="update_check_look_for_updates">Vérification des mises à jour</string>
<!--UpdateCheck Notifications-->
<string name="update_check_notification_file_download">Télécharger mise à jour</string>
<string name="update_check_notification_file_download">Télécharger la mise à jour</string>
<string name="update_check_notification_download_in_progress">Téléchargement en cours</string>
<string name="update_check_notification_download_complete">Téléchargement terminé</string>
<string name="update_check_notification_download_error">Erreur lors du téléchargement</string>
@ -400,12 +400,12 @@
<string name="pref_backup_directory">Dossier de sauvegarde</string>
<string name="pref_backup_service_category">Service</string>
<string name="source_not_found">Source introuvable</string>
<string name="restore_completed">Restauration terminé</string>
<string name="restore_completed">Restauration terminée</string>
<string name="error_opening_log">Impossible d\'ouvrir le fichier journal</string>
<string name="file_saved">Fichier enregistré dans %1$s</string>
<string name="backup_choice">Que voulez-vous sauvegarder?</string>
<string name="backup_choice">Que voulez-vous sauvegarder ?</string>
<string name="invalid_combination">Le défaut ne peut pas être selectionné avec des autres catégories</string>
<string name="delete_downloads_for_manga">Supprimer les chapitres téléchargés?</string>
<string name="delete_downloads_for_manga">Supprimer les chapitres téléchargés ?</string>
<string name="notification_new_chapters_text">Pour %1$s chapitres</string>
<string name="track">Suivi</string>
@ -439,7 +439,7 @@
<string name="pref_read_with_volume_keys_inverted">Inverse les boutons de volume</string>
<string name="pref_crop_borders">Rogner les bordures</string>
<string name="backup_restore_content">La restauration utilise le source pour obtenir des données, des frais de l\'opérateur peuvent s\'appliquer.
<string name="backup_restore_content">La restauration utilise la source pour obtenir des données, des frais de l\'opérateur peuvent s\'appliquer.
Assurez-vous que vous êtes connecté à des sources qui le demande avant de commencer la restauration.</string>
<string name="action_global_search">Recherche globale</string>
<string name="action_open">Ouvrir</string>
@ -447,7 +447,7 @@ Assurez-vous que vous êtes connecté à des sources qui le demande avant de com
<string name="other_source">Autre</string>
<string name="action_global_search_hint">Recherche globale…</string>
<string name="no_results">Aucun résultat!</string>
<string name="no_results">Aucun résultat !</string>
<string name="latest">Récents</string>
<string name="browse">Explorer</string>

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

@ -190,6 +190,7 @@
<string name="right_to_left_viewer">Right to left</string>
<string name="vertical_viewer">Vertical</string>
<string name="webtoon_viewer">Webtoon</string>
<string name="pager_viewer">Pager</string>
<string name="pref_image_decoder">Image decoder</string>
<string name="pref_image_scale_type">Scale type</string>
<string name="scale_type_fit_screen">Fit screen</string>

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,9 +7,10 @@ buildscript {
google()
}
dependencies {
classpath 'com.android.tools.build:gradle:3.0.1'
classpath 'com.android.tools.build:gradle:3.1.2'
classpath 'com.github.ben-manes:gradle-versions-plugin:0.17.0'
classpath 'com.github.zellius:android-shortcut-gradle-plugin:0.1.2'
classpath 'com.google.gms:google-services:3.2.0'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}

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