Drop support for Android 4.x (#2440)

* Bump minSdkVersion

* Remove Android 4.x specific logic

* Consolidate res assets

* Add note about minimum Android version to README

* Restore incorrectly removed method, remove unneeded Lollipop TargetApi annotations
This commit is contained in:
arkon 2020-01-07 18:46:31 -05:00 committed by GitHub
parent b55814a1c0
commit 0d5099f230
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
33 changed files with 200 additions and 530 deletions

View File

@ -4,7 +4,7 @@
# ![app icon](./.github/readme-images/app-icon.png)Tachiyomi
Tachiyomi is a free and open source manga reader for Android.
Tachiyomi is a free and open source manga reader for Android 5.0 and above.
![screenshots of app](./.github/readme-images/screens.png)

View File

@ -35,7 +35,7 @@ android {
defaultConfig {
applicationId "eu.kanade.tachiyomi"
minSdkVersion 16
minSdkVersion 21
targetSdkVersion 28
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
versionCode 41

View File

@ -1,35 +1,20 @@
package eu.kanade.tachiyomi.network
import android.content.Context
import android.os.Build
import android.webkit.CookieManager
import android.webkit.CookieSyncManager
import okhttp3.Cookie
import okhttp3.CookieJar
import okhttp3.HttpUrl
class AndroidCookieJar(context: Context) : CookieJar {
class AndroidCookieJar : CookieJar {
private val manager = CookieManager.getInstance()
private val syncManager by lazy { CookieSyncManager.createInstance(context) }
init {
// Init sync manager when using anything below L
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
syncManager
}
}
override fun saveFromResponse(url: HttpUrl, cookies: List<Cookie>) {
val urlString = url.toString()
for (cookie in cookies) {
manager.setCookie(urlString, cookie.toString())
}
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
syncManager.sync()
}
}
override fun loadForRequest(url: HttpUrl): List<Cookie> {
@ -39,7 +24,7 @@ class AndroidCookieJar(context: Context) : CookieJar {
fun get(url: HttpUrl): List<Cookie> {
val cookies = manager.getCookie(url.toString())
return if (cookies != null && !cookies.isEmpty()) {
return if (cookies != null && cookies.isNotEmpty()) {
cookies.split(";").mapNotNull { Cookie.parse(url, it) }
} else {
emptyList()
@ -53,19 +38,10 @@ class AndroidCookieJar(context: Context) : CookieJar {
cookies.split(";")
.map { it.substringBefore("=") }
.onEach { manager.setCookie(urlString, "$it=;Max-Age=-1") }
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
syncManager.sync()
}
}
fun removeAll() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
manager.removeAllCookies {}
} else {
manager.removeAllCookie()
syncManager.sync()
}
manager.removeAllCookies {}
}
}

View File

@ -28,11 +28,7 @@ class CloudflareInterceptor(private val context: Context) : Interceptor {
* Application class.
*/
private val initWebView by lazy {
if (Build.VERSION.SDK_INT >= 17) {
WebSettings.getDefaultUserAgent(context)
} else {
null
}
WebSettings.getDefaultUserAgent(context)
}
@Synchronized

View File

@ -1,17 +1,9 @@
package eu.kanade.tachiyomi.network
import android.content.Context
import android.os.Build
import okhttp3.*
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.*
class NetworkHelper(context: Context) {
@ -19,99 +11,15 @@ class NetworkHelper(context: Context) {
private val cacheSize = 5L * 1024 * 1024 // 5 MiB
val cookieManager = AndroidCookieJar(context)
val cookieManager = AndroidCookieJar()
val client = OkHttpClient.Builder()
.cookieJar(cookieManager)
.cache(Cache(cacheDir, cacheSize))
.enableTLS12()
.build()
val cloudflareClient = client.newBuilder()
.addInterceptor(CloudflareInterceptor(context))
.build()
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)
}
val specCompat = ConnectionSpec.Builder(ConnectionSpec.MODERN_TLS)
.tlsVersions(TlsVersion.TLS_1_2, TlsVersion.TLS_1_1, TlsVersion.TLS_1_0)
.cipherSuites(
*ConnectionSpec.MODERN_TLS.cipherSuites.orEmpty().toTypedArray(),
CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,
CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA
)
.build()
val specs = listOf(specCompat, ConnectionSpec.CLEARTEXT)
connectionSpecs(specs)
return this
}
}

View File

@ -1,6 +1,5 @@
package eu.kanade.tachiyomi.ui.base.holder
import android.os.Build
import android.view.View
import android.view.ViewGroup
import eu.davidea.flexibleadapter.FlexibleAdapter
@ -51,10 +50,6 @@ interface SlicedHolder {
slice.showRightTopRect(topRect)
slice.showLeftBottomRect(bottomRect)
slice.showRightBottomRect(bottomRect)
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
slice.showTopEdgeShadow(topShadow)
slice.showBottomEdgeShadow(bottomShadow)
}
setMargins(margin, if (topShadow) margin else 0, margin, if (bottomShadow) margin else 0)
}
@ -68,4 +63,4 @@ interface SlicedHolder {
val margin
get() = 8.dpToPx
}
}

View File

@ -8,7 +8,6 @@ import android.content.pm.ActivityInfo
import android.content.res.Configuration
import android.graphics.Bitmap
import android.graphics.Color
import android.os.Build
import android.os.Bundle
import android.view.*
import android.view.animation.Animation
@ -21,9 +20,7 @@ import eu.kanade.tachiyomi.data.database.models.Manga
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
import eu.kanade.tachiyomi.data.preference.getOrDefault
import eu.kanade.tachiyomi.ui.base.activity.BaseRxActivity
import eu.kanade.tachiyomi.ui.reader.ReaderPresenter.SetAsCoverResult.AddToLibraryFirst
import eu.kanade.tachiyomi.ui.reader.ReaderPresenter.SetAsCoverResult.Error
import eu.kanade.tachiyomi.ui.reader.ReaderPresenter.SetAsCoverResult.Success
import eu.kanade.tachiyomi.ui.reader.ReaderPresenter.SetAsCoverResult.*
import eu.kanade.tachiyomi.ui.reader.model.ReaderChapter
import eu.kanade.tachiyomi.ui.reader.model.ReaderPage
import eu.kanade.tachiyomi.ui.reader.model.ViewerChapters
@ -276,10 +273,7 @@ class ReaderActivity : BaseRxActivity<ReaderPresenter>() {
toolbarAnimation.setAnimationListener(object : SimpleAnimationListener() {
override fun onAnimationStart(animation: Animation) {
// Fix status bar being translucent the first time it's opened.
if (Build.VERSION.SDK_INT >= 21) {
window.addFlags(
WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS)
}
window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS)
}
})
toolbar.startAnimation(toolbarAnimation)
@ -637,11 +631,7 @@ class ReaderActivity : BaseRxActivity<ReaderPresenter>() {
*/
private fun setFullscreen(enabled: Boolean) {
systemUi = if (enabled) {
val level = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
SystemUiHelper.LEVEL_IMMERSIVE
} else {
SystemUiHelper.LEVEL_HIDE_STATUS_BAR
}
val level = SystemUiHelper.LEVEL_IMMERSIVE
val flags = SystemUiHelper.FLAG_IMMERSIVE_STICKY or
SystemUiHelper.FLAG_LAYOUT_IN_SCREEN_OLDER_DEVICES

View File

@ -3,16 +3,14 @@ package eu.kanade.tachiyomi.ui.reader.viewer.webtoon
import android.animation.Animator
import android.animation.AnimatorSet
import android.animation.ValueAnimator
import android.annotation.TargetApi
import android.content.Context
import android.os.Build
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import android.util.AttributeSet
import android.view.HapticFeedbackConstants
import android.view.MotionEvent
import android.view.ViewConfiguration
import android.view.animation.DecelerateInterpolator
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import eu.kanade.tachiyomi.ui.reader.viewer.GestureDetectorWithLongTap
/**
@ -58,7 +56,6 @@ open class WebtoonRecyclerView @JvmOverloads constructor(
firstVisibleItemPosition = layoutManager.findFirstVisibleItemPosition()
}
@TargetApi(Build.VERSION_CODES.KITKAT)
override fun onScrollStateChanged(state: Int) {
super.onScrollStateChanged(state)
val layoutManager = layoutManager

View File

@ -5,10 +5,9 @@ import android.app.Activity
import android.app.Dialog
import android.content.*
import android.net.Uri
import android.os.Build
import android.os.Bundle
import androidx.preference.PreferenceScreen
import android.view.View
import androidx.preference.PreferenceScreen
import com.afollestad.materialdialogs.MaterialDialog
import com.hippo.unifile.UniFile
import eu.kanade.tachiyomi.R
@ -106,21 +105,12 @@ class SettingsBackupController : SettingsController() {
onClick {
val currentDir = preferences.backupsDirectory().getOrDefault()
try{
val intent = if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
// Custom dir selected, open directory selector
preferences.context.getFilePicker(currentDir)
} else {
Intent(Intent.ACTION_OPEN_DOCUMENT_TREE)
}
val intent = Intent(Intent.ACTION_OPEN_DOCUMENT_TREE)
startActivityForResult(intent, CODE_BACKUP_DIR)
} catch (e: ActivityNotFoundException){
//Fall back to custom picker on error
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP){
startActivityForResult(preferences.context.getFilePicker(currentDir), CODE_BACKUP_DIR)
}
// Fall back to custom picker on error
startActivityForResult(preferences.context.getFilePicker(currentDir), CODE_BACKUP_DIR)
}
}
preferences.backupsDirectory().asObservable()
@ -154,37 +144,27 @@ class SettingsBackupController : SettingsController() {
// Get uri of backup folder.
val uri = data.data
// Get UriPermission so it's possible to write files post kitkat.
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.KITKAT) {
val flags = Intent.FLAG_GRANT_READ_URI_PERMISSION or
Intent.FLAG_GRANT_WRITE_URI_PERMISSION
// Get UriPermission so it's possible to write files
val flags = Intent.FLAG_GRANT_READ_URI_PERMISSION or
Intent.FLAG_GRANT_WRITE_URI_PERMISSION
activity.contentResolver.takePersistableUriPermission(uri, flags)
}
activity.contentResolver.takePersistableUriPermission(uri, flags)
// Set backup Uri.
// Set backup Uri
preferences.backupsDirectory().set(uri.toString())
}
CODE_BACKUP_CREATE -> if (data != null && resultCode == Activity.RESULT_OK) {
val activity = activity ?: return
val uri = if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
val dir = data.data.path
val file = File(dir, Backup.getDefaultFilename())
Uri.fromFile(file)
} else {
val uri = data.data
val flags = Intent.FLAG_GRANT_READ_URI_PERMISSION or
Intent.FLAG_GRANT_WRITE_URI_PERMISSION
val uri = data.data
val flags = Intent.FLAG_GRANT_READ_URI_PERMISSION or
Intent.FLAG_GRANT_WRITE_URI_PERMISSION
activity.contentResolver.takePersistableUriPermission(uri, flags)
val file = UniFile.fromUri(activity, uri)
file.uri
}
activity.contentResolver.takePersistableUriPermission(uri, flags)
val file = UniFile.fromUri(activity, uri)
CreatingBackupDialog().showDialog(router, TAG_CREATING_BACKUP_DIALOG)
BackupCreateService.makeBackup(activity, uri, backupFlags)
BackupCreateService.makeBackup(activity, file.uri, backupFlags)
}
CODE_BACKUP_RESTORE -> if (data != null && resultCode == Activity.RESULT_OK) {
val uri = data.data
@ -201,25 +181,17 @@ class SettingsBackupController : SettingsController() {
val currentDir = preferences.backupsDirectory().getOrDefault()
try {
// If API is lower than Lollipop use custom picker
val intent = if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
preferences.context.getFilePicker(currentDir)
} else {
// Use Androids build in file creator
Intent(Intent.ACTION_CREATE_DOCUMENT)
// Use Android's built-in file creator
val intent = Intent(Intent.ACTION_CREATE_DOCUMENT)
.addCategory(Intent.CATEGORY_OPENABLE)
.setType("application/*")
.putExtra(Intent.EXTRA_TITLE, Backup.getDefaultFilename())
}
startActivityForResult(intent, CODE_BACKUP_CREATE)
} catch (e: ActivityNotFoundException) {
// Handle errors where the android ROM doesn't support the built in picker
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP){
startActivityForResult(preferences.context.getFilePicker(currentDir), CODE_BACKUP_CREATE)
}
startActivityForResult(preferences.context.getFilePicker(currentDir), CODE_BACKUP_CREATE)
}
}
class CreateBackupDialog : DialogController() {

View File

@ -5,7 +5,6 @@ import android.app.Dialog
import android.content.ActivityNotFoundException
import android.content.Intent
import android.net.Uri
import android.os.Build
import android.os.Bundle
import android.os.Environment
import androidx.core.content.ContextCompat
@ -107,11 +106,7 @@ class SettingsDownloadController : SettingsController() {
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
when (requestCode) {
DOWNLOAD_DIR_PRE_L -> if (data != null && resultCode == Activity.RESULT_OK) {
val uri = Uri.fromFile(File(data.data.path))
preferences.downloadsDirectory().set(uri.toString())
}
DOWNLOAD_DIR_L -> if (data != null && resultCode == Activity.RESULT_OK) {
DOWNLOAD_DIR -> if (data != null && resultCode == Activity.RESULT_OK) {
val context = applicationContext ?: return
val uri = data.data
val flags = Intent.FLAG_GRANT_READ_URI_PERMISSION or
@ -132,19 +127,11 @@ class SettingsDownloadController : SettingsController() {
}
fun customDirectorySelected(currentDir: String) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
startActivityForResult(preferences.context.getFilePicker(currentDir), DOWNLOAD_DIR_PRE_L)
} else {
val intent = Intent(Intent.ACTION_OPEN_DOCUMENT_TREE)
try {
startActivityForResult(intent, DOWNLOAD_DIR_L)
} catch (e: ActivityNotFoundException) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
startActivityForResult(preferences.context.getFilePicker(currentDir), DOWNLOAD_DIR_L)
}
}
val intent = Intent(Intent.ACTION_OPEN_DOCUMENT_TREE)
try {
startActivityForResult(intent, DOWNLOAD_DIR)
} catch (e: ActivityNotFoundException) {
startActivityForResult(preferences.context.getFilePicker(currentDir), DOWNLOAD_DIR)
}
}
@ -183,7 +170,6 @@ class SettingsDownloadController : SettingsController() {
}
private companion object {
const val DOWNLOAD_DIR_PRE_L = 103
const val DOWNLOAD_DIR_L = 104
const val DOWNLOAD_DIR = 104
}
}

View File

@ -3,7 +3,6 @@ package eu.kanade.tachiyomi.util
import android.content.Context
import android.content.Intent
import android.net.Uri
import android.os.Build
import android.os.Environment
import androidx.core.content.ContextCompat
import androidx.core.os.EnvironmentCompat
@ -45,13 +44,6 @@ object DiskUtil {
}
}
if (Build.VERSION.SDK_INT < 21) {
val extStorages = System.getenv("SECONDARY_STORAGE")
if (extStorages != null) {
directories += extStorages.split(":").map(::File)
}
}
return directories
}
@ -79,11 +71,7 @@ object DiskUtil {
* Scans the given file so that it can be shown in gallery apps, for example.
*/
fun scanMedia(context: Context, uri: Uri) {
val action = if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) {
Intent.ACTION_MEDIA_MOUNTED
} else {
Intent.ACTION_MEDIA_SCANNER_SCAN_FILE
}
val action = Intent.ACTION_MEDIA_SCANNER_SCAN_FILE
val mediaScanIntent = Intent(action)
mediaScanIntent.data = uri
context.sendBroadcast(mediaScanIntent)

View File

@ -36,7 +36,6 @@ abstract class WebViewClientCompat : WebViewClient() {
return shouldOverrideUrlCompat(view, url)
}
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
final override fun shouldInterceptRequest(
view: WebView,
request: WebResourceRequest

View File

@ -16,32 +16,26 @@ class ElevationAppBarLayout @JvmOverloads constructor(
private var origStateAnimator: StateListAnimator? = null
init {
if (Build.VERSION.SDK_INT >= 21) {
origStateAnimator = stateListAnimator
}
origStateAnimator = stateListAnimator
}
fun enableElevation() {
if (Build.VERSION.SDK_INT >= 21) {
stateListAnimator = origStateAnimator
}
stateListAnimator = origStateAnimator
}
fun disableElevation() {
if (Build.VERSION.SDK_INT >= 21) {
stateListAnimator = StateListAnimator().apply {
val objAnimator = ObjectAnimator.ofFloat(this, "elevation", 0f)
stateListAnimator = StateListAnimator().apply {
val objAnimator = ObjectAnimator.ofFloat(this, "elevation", 0f)
// Enabled and collapsible, but not collapsed means not elevated
addState(intArrayOf(android.R.attr.enabled, R.attr.state_collapsible, -R.attr.state_collapsed),
objAnimator)
// Enabled and collapsible, but not collapsed means not elevated
addState(intArrayOf(android.R.attr.enabled, R.attr.state_collapsible, -R.attr.state_collapsed),
objAnimator)
// Default enabled state
addState(intArrayOf(android.R.attr.enabled), objAnimator)
// Default enabled state
addState(intArrayOf(android.R.attr.enabled), objAnimator)
// Disabled state
addState(IntArray(0), objAnimator)
}
// Disabled state
addState(IntArray(0), objAnimator)
}
}

View File

@ -2,14 +2,11 @@ package eu.kanade.tachiyomi.widget
import android.animation.Animator
import android.animation.AnimatorListenerAdapter
import android.annotation.TargetApi
import android.content.Context
import android.os.Build
import android.util.AttributeSet
import android.view.View
import android.view.ViewAnimationUtils
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
class RevealAnimationView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null) :
View(context, attrs) {
@ -21,28 +18,25 @@ class RevealAnimationView @JvmOverloads constructor(context: Context, attrs: Att
* @param initialRadius size of radius of animation
*/
fun hideRevealEffect(centerX: Int, centerY: Int, initialRadius: Int) {
if (Build.VERSION.SDK_INT >= 21) {
// Make the view visible.
this.visibility = View.VISIBLE
// Make the view visible.
this.visibility = View.VISIBLE
// Create the animation (the final radius is zero).
val anim = ViewAnimationUtils.createCircularReveal(
this, centerX, centerY, initialRadius.toFloat(), 0f)
// Create the animation (the final radius is zero).
val anim = ViewAnimationUtils.createCircularReveal(
this, centerX, centerY, initialRadius.toFloat(), 0f)
// Set duration of animation.
anim.duration = 500
// Set duration of animation.
anim.duration = 500
// make the view invisible when the animation is done
anim.addListener(object : AnimatorListenerAdapter() {
override fun onAnimationEnd(animation: Animator) {
super.onAnimationEnd(animation)
this@RevealAnimationView.visibility = View.INVISIBLE
}
})
// make the view invisible when the animation is done
anim.addListener(object : AnimatorListenerAdapter() {
override fun onAnimationEnd(animation: Animator) {
super.onAnimationEnd(animation)
this@RevealAnimationView.visibility = View.INVISIBLE
}
})
anim.start()
}
anim.start()
}
/**
@ -55,25 +49,20 @@ class RevealAnimationView @JvmOverloads constructor(context: Context, attrs: Att
* @return sdk version lower then 21
*/
fun showRevealEffect(centerX: Int, centerY: Int, listener: Animator.AnimatorListener): Boolean {
if (Build.VERSION.SDK_INT >= 21) {
this.visibility = View.VISIBLE
this.visibility = View.VISIBLE
val height = this.height
val height = this.height
// Create animation
val anim = ViewAnimationUtils.createCircularReveal(
this, centerX, centerY, 0f, height.toFloat())
// Create animation
val anim = ViewAnimationUtils.createCircularReveal(
this, centerX, centerY, 0f, height.toFloat())
// Set duration of animation
anim.duration = 350
// Set duration of animation
anim.duration = 350
anim.addListener(listener)
anim.start()
return true
}
return false
anim.addListener(listener)
anim.start()
return true
}
}

View File

@ -1,21 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<ripple
xmlns:android="http://schemas.android.com/apk/res/android"
android:color="@color/selectorColorDark"
>
<item>
<selector>
<item android:state_selected="true">
<color android:color="@color/selectorColorDark"/>
</item>
<item android:state_activated="true">
<color android:color="@color/selectorColorDark"/>
</item>
<item>
<color android:color="@color/md_black_1000"/>
</item>
</selector>
</item>
</ripple>

View File

@ -1,21 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<ripple
xmlns:android="http://schemas.android.com/apk/res/android"
android:color="@color/colorAccentDark"
>
<item>
<selector>
<item android:state_selected="true">
<color android:color="@color/selectorColorDark"/>
</item>
<item android:state_activated="true">
<color android:color="@color/selectorColorDark"/>
</item>
<item>
<color android:color="@color/backgroundDark"/>
</item>
</selector>
</item>
</ripple>

View File

@ -1,21 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<ripple
xmlns:android="http://schemas.android.com/apk/res/android"
android:color="@color/colorAccentLight"
>
<item>
<selector>
<item android:state_selected="true">
<color android:color="@color/selectorColorLight"/>
</item>
<item android:state_activated="true">
<color android:color="@color/selectorColorLight"/>
</item>
<item>
<color android:color="@color/backgroundLight"/>
</item>
</selector>
</item>
</ripple>

View File

@ -1,19 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
android:color="@color/rippleColorDark">
<item>
<selector>
<item android:state_selected="true">
<color android:color="@color/rippleColorDark"/>
</item>
<item android:state_activated="true">
<color android:color="@color/rippleColorDark"/>
</item>
<item>
<color android:color="@color/md_black_1000"/>
</item>
</selector>
</item>
</ripple>

View File

@ -1,19 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
android:color="@color/rippleColorDark">
<item>
<selector>
<item android:state_selected="true">
<color android:color="@color/rippleColorDark"/>
</item>
<item android:state_activated="true">
<color android:color="@color/rippleColorDark"/>
</item>
<item>
<color android:color="@color/dialogDark"/>
</item>
</selector>
</item>
</ripple>

View File

@ -1,19 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
android:color="@color/rippleColorLight">
<item>
<selector>
<item android:state_selected="true">
<color android:color="@color/rippleColorLight"/>
</item>
<item android:state_activated="true">
<color android:color="@color/rippleColorLight"/>
</item>
<item>
<color android:color="@color/dialogLight"/>
</item>
</selector>
</item>
</ripple>

View File

@ -1,10 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<selector android:exitFadeDuration="@android:integer/config_longAnimTime"
xmlns:android="http://schemas.android.com/apk/res/android">
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
android:color="@color/selectorColorDark">
<item>
<selector>
<item android:state_selected="true">
<color android:color="@color/selectorColorDark" />
</item>
<item android:state_focused="true" android:drawable="@color/selectorColorDark"/>
<item android:state_pressed="true" android:drawable="@color/selectorColorDark"/>
<item android:state_activated="true" android:drawable="@color/selectorColorDark"/>
<item android:drawable="@color/md_black_1000"/>
<item android:state_activated="true">
<color android:color="@color/selectorColorDark" />
</item>
</selector>
<item>
<color android:color="@color/md_black_1000" />
</item>
</selector>
</item>
</ripple>

View File

@ -1,10 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<selector android:exitFadeDuration="@android:integer/config_longAnimTime"
xmlns:android="http://schemas.android.com/apk/res/android">
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
android:color="@color/colorAccentDark">
<item>
<selector>
<item android:state_selected="true">
<color android:color="@color/selectorColorDark" />
</item>
<item android:state_focused="true" android:drawable="@color/selectorColorDark"/>
<item android:state_pressed="true" android:drawable="@color/selectorColorDark"/>
<item android:state_activated="true" android:drawable="@color/selectorColorDark"/>
<item android:drawable="@color/backgroundDark"/>
<item android:state_activated="true">
<color android:color="@color/selectorColorDark" />
</item>
</selector>
<item>
<color android:color="@color/backgroundDark" />
</item>
</selector>
</item>
</ripple>

View File

@ -1,10 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<selector android:exitFadeDuration="@android:integer/config_longAnimTime"
xmlns:android="http://schemas.android.com/apk/res/android">
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
android:color="@color/colorAccentLight">
<item>
<selector>
<item android:state_selected="true">
<color android:color="@color/selectorColorLight" />
</item>
<item android:state_focused="true" android:drawable="@color/selectorColorLight"/>
<item android:state_pressed="true" android:drawable="@color/selectorColorLight"/>
<item android:state_activated="true" android:drawable="@color/selectorColorLight"/>
<item android:drawable="@color/backgroundLight"/>
<item android:state_activated="true">
<color android:color="@color/selectorColorLight" />
</item>
</selector>
<item>
<color android:color="@color/backgroundLight" />
</item>
</selector>
</item>
</ripple>

View File

@ -1,10 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android"
android:exitFadeDuration="@android:integer/config_longAnimTime">
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
android:color="@color/rippleColorDark">
<item>
<selector>
<item android:state_selected="true">
<color android:color="@color/rippleColorDark" />
</item>
<item android:drawable="@color/rippleColorDark" android:state_focused="true"/>
<item android:drawable="@color/rippleColorDark" android:state_pressed="true"/>
<item android:drawable="@color/rippleColorDark" android:state_activated="true"/>
<item android:drawable="@color/md_black_1000"/>
<item android:state_activated="true">
<color android:color="@color/rippleColorDark" />
</item>
</selector>
<item>
<color android:color="@color/md_black_1000" />
</item>
</selector>
</item>
</ripple>

View File

@ -1,10 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android"
android:exitFadeDuration="@android:integer/config_longAnimTime">
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
android:color="@color/rippleColorDark">
<item>
<selector>
<item android:state_selected="true">
<color android:color="@color/rippleColorDark" />
</item>
<item android:drawable="@color/rippleColorDark" android:state_focused="true"/>
<item android:drawable="@color/rippleColorDark" android:state_pressed="true"/>
<item android:drawable="@color/rippleColorDark" android:state_activated="true"/>
<item android:drawable="@color/dialogDark"/>
<item android:state_activated="true">
<color android:color="@color/rippleColorDark" />
</item>
</selector>
<item>
<color android:color="@color/dialogDark" />
</item>
</selector>
</item>
</ripple>

View File

@ -1,10 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android"
android:exitFadeDuration="@android:integer/config_longAnimTime">
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
android:color="@color/rippleColorLight">
<item>
<selector>
<item android:state_selected="true">
<color android:color="@color/rippleColorLight" />
</item>
<item android:drawable="@color/rippleColorLight" android:state_focused="true"/>
<item android:drawable="@color/rippleColorLight" android:state_pressed="true"/>
<item android:drawable="@color/rippleColorLight" android:state_activated="true"/>
<item android:drawable="@color/dialogLight"/>
<item android:state_activated="true">
<color android:color="@color/rippleColorLight" />
</item>
</selector>
<item>
<color android:color="@color/dialogLight" />
</item>
</selector>
</item>
</ripple>

View File

@ -1,5 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!--Nav header-->
<dimen name="navigation_drawer_header_margin">41dp</dimen>
</resources>

View File

@ -1,6 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- String Fonts -->
<string name="font_roboto_medium" translatable="false">sans-serif-medium</string>
<string name="font_roboto_regular" translatable="false">sans-serif-regular</string>
</resources>

View File

@ -1,58 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!--===========-->
<!-- Main Theme-->
<!--===========-->
<style name="Theme.Tachiyomi" parent="Theme.Base">
<!-- 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/colorPrimaryDark</item>
</style>
<!--=============-->
<!-- Dark Themes -->
<!--=============-->
<style name="Theme.Tachiyomi.Dark" parent="Theme.Base.Dark">
<!-- 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/colorDarkPrimaryDark</item>
</style>
<style name="Theme.Tachiyomi.DarkBlue" parent="Theme.Base.Dark">
<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
<!-- 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/colorDarkPrimaryDark</item>
</style>
<!--==============-->
<!-- Amoled Theme -->
<!--==============-->
<style name="Theme.Tachiyomi.Amoled" parent="Theme.Base.Amoled">
<!-- 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">@android:color/transparent</item>
</style>
<!--==============-->
<!-- Reader Theme -->
<!--==============-->
<style name="Theme.Reader" parent="Theme.Base.Reader.Dark">
<!-- Attributes specific for SDK 21 and up -->
<item name="android:statusBarColor">?colorPrimaryDark</item>
<item name="android:navigationBarColor">?colorPrimaryDark</item>
</style>
<style name="Theme.Reader.Light" parent="Theme.Base.Reader.Light">
<!-- Attributes specific for SDK 21 and up -->
<item name="android:statusBarColor">?colorPrimaryDark</item>
<item name="android:navigationBarColor">?colorPrimaryDark</item>
</style>
</resources>

View File

@ -20,10 +20,9 @@
<dimen name="text_body">16sp</dimen>
<dimen name="text_small_body">14sp</dimen>
<!--Nav header-->
<dimen name="navigation_drawer_header_height">158dp</dimen>
<dimen name="navigation_drawer_header_margin">16dp</dimen>
<dimen name="navigation_drawer_header_margin">41dp</dimen>
<dimen name="bottom_sheet_width">0dp</dimen>
</resources>

View File

@ -1,7 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- String Fonts -->
<string name="font_roboto_medium" translatable="false">sans-serif</string>
<string name="font_roboto_regular" translatable="false">sans-serif</string>
</resources>

View File

@ -50,7 +50,7 @@
</style>
<style name="TextAppearance.Regular">
<item name="android:fontFamily">@string/font_roboto_regular</item>
<item name="android:fontFamily">sans-serif-regular</item>
</style>
<style name="TextAppearance.Regular.Body1">
@ -102,7 +102,7 @@
</style>
<style name="TextAppearance.Medium">
<item name="android:fontFamily">@string/font_roboto_medium</item>
<item name="android:fontFamily">sans-serif-medium</item>
</style>
<style name="TextAppearance.Medium.Title">

View File

@ -39,8 +39,13 @@
<item name="icon_color">@color/iconColorLight</item>
</style>
<!--===========-->
<!-- Main Theme-->
<!--===========-->
<style name="Theme.Tachiyomi" parent="Theme.Base">
<!-- Attributes specific for SDK 16 to SDK 20 -->
<item name="android:windowDrawsSystemBarBackgrounds">true</item>
<item name="android:statusBarColor">@android:color/transparent</item>
<item name="android:navigationBarColor">@color/colorPrimaryDark</item>
</style>
<!--=============-->
@ -61,6 +66,10 @@
<item name="android:divider">@color/dividerDark</item>
<item name="android:listDivider">@drawable/line_divider_dark</item>
<item name="android:windowDrawsSystemBarBackgrounds">true</item>
<item name="android:statusBarColor">@android:color/transparent</item>
<item name="android:navigationBarColor">@color/colorDarkPrimaryDark</item>
<!-- Themes -->
<item name="windowActionModeOverlay">true</item>
<item name="actionBarTheme">@style/ThemeOverlay.AppCompat.Dark.ActionBar</item>
@ -86,6 +95,10 @@
<style name="Theme.Tachiyomi.DarkBlue" parent="Theme.Base.Dark">
<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
<item name="android:windowDrawsSystemBarBackgrounds">true</item>
<item name="android:statusBarColor">@android:color/transparent</item>
<item name="android:navigationBarColor">@color/colorDarkPrimaryDark</item>
</style>
<!--==============-->
@ -96,6 +109,10 @@
<item name="colorPrimaryDark">@color/colorAmoledPrimary</item>
<item name="android:colorBackground">@color/md_black_1000</item>
<item name="android:windowDrawsSystemBarBackgrounds">true</item>
<item name="android:statusBarColor">@android:color/transparent</item>
<item name="android:navigationBarColor">@android:color/transparent</item>
<!-- Custom Attributes-->
<item name="selectable_list_drawable">@drawable/list_item_selector_amoled</item>
<item name="selectable_library_drawable">@drawable/library_item_selector_amoled</item>
@ -113,12 +130,18 @@
<item name="colorPrimary">@color/colorDarkPrimary</item>
<item name="colorPrimaryDark">@color/colorDarkPrimaryDark</item>
<item name="android:colorBackground">@android:color/black</item>
<item name="android:statusBarColor">?colorPrimaryDark</item>
<item name="android:navigationBarColor">?colorPrimaryDark</item>
</style>
<style name="Theme.Base.Reader.Light" parent="Theme.Base">
<item name="colorPrimary">@color/colorDarkPrimary</item>
<item name="colorPrimaryDark">@color/colorDarkPrimaryDark</item>
<item name="android:colorBackground">@android:color/white</item>
<item name="android:statusBarColor">?colorPrimaryDark</item>
<item name="android:navigationBarColor">?colorPrimaryDark</item>
</style>
<style name="Theme.Reader" parent="Theme.Base.Reader.Dark">