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:
		
							parent
							
								
									b55814a1c0
								
							
						
					
					
						commit
						0d5099f230
					
				| @ -4,7 +4,7 @@ | |||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| # Tachiyomi | # 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. | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -35,7 +35,7 @@ android { | |||||||
| 
 | 
 | ||||||
|     defaultConfig { |     defaultConfig { | ||||||
|         applicationId "eu.kanade.tachiyomi" |         applicationId "eu.kanade.tachiyomi" | ||||||
|         minSdkVersion 16 |         minSdkVersion 21 | ||||||
|         targetSdkVersion 28 |         targetSdkVersion 28 | ||||||
|         testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" |         testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" | ||||||
|         versionCode 41 |         versionCode 41 | ||||||
|  | |||||||
| @ -1,35 +1,20 @@ | |||||||
| package eu.kanade.tachiyomi.network | package eu.kanade.tachiyomi.network | ||||||
| 
 | 
 | ||||||
| import android.content.Context |  | ||||||
| import android.os.Build |  | ||||||
| import android.webkit.CookieManager | import android.webkit.CookieManager | ||||||
| import android.webkit.CookieSyncManager |  | ||||||
| import okhttp3.Cookie | import okhttp3.Cookie | ||||||
| import okhttp3.CookieJar | import okhttp3.CookieJar | ||||||
| import okhttp3.HttpUrl | import okhttp3.HttpUrl | ||||||
| 
 | 
 | ||||||
| class AndroidCookieJar(context: Context) : CookieJar { | class AndroidCookieJar : CookieJar { | ||||||
| 
 | 
 | ||||||
|     private val manager = CookieManager.getInstance() |     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>) { |     override fun saveFromResponse(url: HttpUrl, cookies: List<Cookie>) { | ||||||
|         val urlString = url.toString() |         val urlString = url.toString() | ||||||
| 
 | 
 | ||||||
|         for (cookie in cookies) { |         for (cookie in cookies) { | ||||||
|             manager.setCookie(urlString, cookie.toString()) |             manager.setCookie(urlString, cookie.toString()) | ||||||
|         } |         } | ||||||
|         if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) { |  | ||||||
|             syncManager.sync() |  | ||||||
|         } |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     override fun loadForRequest(url: HttpUrl): List<Cookie> { |     override fun loadForRequest(url: HttpUrl): List<Cookie> { | ||||||
| @ -39,7 +24,7 @@ class AndroidCookieJar(context: Context) : CookieJar { | |||||||
|     fun get(url: HttpUrl): List<Cookie> { |     fun get(url: HttpUrl): List<Cookie> { | ||||||
|         val cookies = manager.getCookie(url.toString()) |         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) } |             cookies.split(";").mapNotNull { Cookie.parse(url, it) } | ||||||
|         } else { |         } else { | ||||||
|             emptyList() |             emptyList() | ||||||
| @ -53,19 +38,10 @@ class AndroidCookieJar(context: Context) : CookieJar { | |||||||
|         cookies.split(";") |         cookies.split(";") | ||||||
|             .map { it.substringBefore("=") } |             .map { it.substringBefore("=") } | ||||||
|             .onEach { manager.setCookie(urlString, "$it=;Max-Age=-1") } |             .onEach { manager.setCookie(urlString, "$it=;Max-Age=-1") } | ||||||
| 
 |  | ||||||
|         if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) { |  | ||||||
|             syncManager.sync() |  | ||||||
|         } |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     fun removeAll() { |     fun removeAll() { | ||||||
|         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { |         manager.removeAllCookies {} | ||||||
|             manager.removeAllCookies {} |  | ||||||
|         } else { |  | ||||||
|             manager.removeAllCookie() |  | ||||||
|             syncManager.sync() |  | ||||||
|         } |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| } | } | ||||||
|  | |||||||
| @ -28,11 +28,7 @@ class CloudflareInterceptor(private val context: Context) : Interceptor { | |||||||
|      * Application class. |      * Application class. | ||||||
|      */ |      */ | ||||||
|     private val initWebView by lazy { |     private val initWebView by lazy { | ||||||
|         if (Build.VERSION.SDK_INT >= 17) { |         WebSettings.getDefaultUserAgent(context) | ||||||
|             WebSettings.getDefaultUserAgent(context) |  | ||||||
|         } else { |  | ||||||
|             null |  | ||||||
|         } |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     @Synchronized |     @Synchronized | ||||||
|  | |||||||
| @ -1,17 +1,9 @@ | |||||||
| package eu.kanade.tachiyomi.network | package eu.kanade.tachiyomi.network | ||||||
| 
 | 
 | ||||||
| import android.content.Context | import android.content.Context | ||||||
| import android.os.Build | import okhttp3.Cache | ||||||
| import okhttp3.* | import okhttp3.OkHttpClient | ||||||
| import java.io.File | import java.io.File | ||||||
| import java.io.IOException |  | ||||||
| import java.net.InetAddress |  | ||||||
| import java.net.Socket |  | ||||||
| import java.net.UnknownHostException |  | ||||||
| import java.security.KeyManagementException |  | ||||||
| import java.security.KeyStore |  | ||||||
| import java.security.NoSuchAlgorithmException |  | ||||||
| import javax.net.ssl.* |  | ||||||
| 
 | 
 | ||||||
| class NetworkHelper(context: Context) { | class NetworkHelper(context: Context) { | ||||||
| 
 | 
 | ||||||
| @ -19,99 +11,15 @@ class NetworkHelper(context: Context) { | |||||||
| 
 | 
 | ||||||
|     private val cacheSize = 5L * 1024 * 1024 // 5 MiB |     private val cacheSize = 5L * 1024 * 1024 // 5 MiB | ||||||
| 
 | 
 | ||||||
|     val cookieManager = AndroidCookieJar(context) |     val cookieManager = AndroidCookieJar() | ||||||
| 
 | 
 | ||||||
|     val client = OkHttpClient.Builder() |     val client = OkHttpClient.Builder() | ||||||
|             .cookieJar(cookieManager) |             .cookieJar(cookieManager) | ||||||
|             .cache(Cache(cacheDir, cacheSize)) |             .cache(Cache(cacheDir, cacheSize)) | ||||||
|             .enableTLS12() |  | ||||||
|             .build() |             .build() | ||||||
| 
 | 
 | ||||||
|     val cloudflareClient = client.newBuilder() |     val cloudflareClient = client.newBuilder() | ||||||
|             .addInterceptor(CloudflareInterceptor(context)) |             .addInterceptor(CloudflareInterceptor(context)) | ||||||
|             .build() |             .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 |  | ||||||
|     } |  | ||||||
| } | } | ||||||
|  | |||||||
| @ -1,6 +1,5 @@ | |||||||
| package eu.kanade.tachiyomi.ui.base.holder | package eu.kanade.tachiyomi.ui.base.holder | ||||||
| 
 | 
 | ||||||
| import android.os.Build |  | ||||||
| import android.view.View | import android.view.View | ||||||
| import android.view.ViewGroup | import android.view.ViewGroup | ||||||
| import eu.davidea.flexibleadapter.FlexibleAdapter | import eu.davidea.flexibleadapter.FlexibleAdapter | ||||||
| @ -51,10 +50,6 @@ interface SlicedHolder { | |||||||
|         slice.showRightTopRect(topRect) |         slice.showRightTopRect(topRect) | ||||||
|         slice.showLeftBottomRect(bottomRect) |         slice.showLeftBottomRect(bottomRect) | ||||||
|         slice.showRightBottomRect(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) |         setMargins(margin, if (topShadow) margin else 0, margin, if (bottomShadow) margin else 0) | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -8,7 +8,6 @@ import android.content.pm.ActivityInfo | |||||||
| import android.content.res.Configuration | import android.content.res.Configuration | ||||||
| import android.graphics.Bitmap | import android.graphics.Bitmap | ||||||
| import android.graphics.Color | import android.graphics.Color | ||||||
| import android.os.Build |  | ||||||
| import android.os.Bundle | import android.os.Bundle | ||||||
| import android.view.* | import android.view.* | ||||||
| import android.view.animation.Animation | 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.PreferencesHelper | ||||||
| import eu.kanade.tachiyomi.data.preference.getOrDefault | import eu.kanade.tachiyomi.data.preference.getOrDefault | ||||||
| import eu.kanade.tachiyomi.ui.base.activity.BaseRxActivity | 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.* | ||||||
| import eu.kanade.tachiyomi.ui.reader.ReaderPresenter.SetAsCoverResult.Error |  | ||||||
| import eu.kanade.tachiyomi.ui.reader.ReaderPresenter.SetAsCoverResult.Success |  | ||||||
| import eu.kanade.tachiyomi.ui.reader.model.ReaderChapter | import eu.kanade.tachiyomi.ui.reader.model.ReaderChapter | ||||||
| import eu.kanade.tachiyomi.ui.reader.model.ReaderPage | import eu.kanade.tachiyomi.ui.reader.model.ReaderPage | ||||||
| import eu.kanade.tachiyomi.ui.reader.model.ViewerChapters | import eu.kanade.tachiyomi.ui.reader.model.ViewerChapters | ||||||
| @ -276,10 +273,7 @@ class ReaderActivity : BaseRxActivity<ReaderPresenter>() { | |||||||
|                 toolbarAnimation.setAnimationListener(object : SimpleAnimationListener() { |                 toolbarAnimation.setAnimationListener(object : SimpleAnimationListener() { | ||||||
|                     override fun onAnimationStart(animation: Animation) { |                     override fun onAnimationStart(animation: Animation) { | ||||||
|                         // Fix status bar being translucent the first time it's opened. |                         // 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) |                 toolbar.startAnimation(toolbarAnimation) | ||||||
| @ -637,11 +631,7 @@ class ReaderActivity : BaseRxActivity<ReaderPresenter>() { | |||||||
|          */ |          */ | ||||||
|         private fun setFullscreen(enabled: Boolean) { |         private fun setFullscreen(enabled: Boolean) { | ||||||
|             systemUi = if (enabled) { |             systemUi = if (enabled) { | ||||||
|                 val level = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { |                 val level = SystemUiHelper.LEVEL_IMMERSIVE | ||||||
|                     SystemUiHelper.LEVEL_IMMERSIVE |  | ||||||
|                 } else { |  | ||||||
|                     SystemUiHelper.LEVEL_HIDE_STATUS_BAR |  | ||||||
|                 } |  | ||||||
|                 val flags = SystemUiHelper.FLAG_IMMERSIVE_STICKY or |                 val flags = SystemUiHelper.FLAG_IMMERSIVE_STICKY or | ||||||
|                         SystemUiHelper.FLAG_LAYOUT_IN_SCREEN_OLDER_DEVICES |                         SystemUiHelper.FLAG_LAYOUT_IN_SCREEN_OLDER_DEVICES | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -3,16 +3,14 @@ package eu.kanade.tachiyomi.ui.reader.viewer.webtoon | |||||||
| import android.animation.Animator | import android.animation.Animator | ||||||
| import android.animation.AnimatorSet | import android.animation.AnimatorSet | ||||||
| import android.animation.ValueAnimator | import android.animation.ValueAnimator | ||||||
| import android.annotation.TargetApi |  | ||||||
| import android.content.Context | import android.content.Context | ||||||
| import android.os.Build |  | ||||||
| import androidx.recyclerview.widget.LinearLayoutManager |  | ||||||
| import androidx.recyclerview.widget.RecyclerView |  | ||||||
| import android.util.AttributeSet | import android.util.AttributeSet | ||||||
| import android.view.HapticFeedbackConstants | import android.view.HapticFeedbackConstants | ||||||
| import android.view.MotionEvent | import android.view.MotionEvent | ||||||
| import android.view.ViewConfiguration | import android.view.ViewConfiguration | ||||||
| import android.view.animation.DecelerateInterpolator | import android.view.animation.DecelerateInterpolator | ||||||
|  | import androidx.recyclerview.widget.LinearLayoutManager | ||||||
|  | import androidx.recyclerview.widget.RecyclerView | ||||||
| import eu.kanade.tachiyomi.ui.reader.viewer.GestureDetectorWithLongTap | import eu.kanade.tachiyomi.ui.reader.viewer.GestureDetectorWithLongTap | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
| @ -58,7 +56,6 @@ open class WebtoonRecyclerView @JvmOverloads constructor( | |||||||
|         firstVisibleItemPosition = layoutManager.findFirstVisibleItemPosition() |         firstVisibleItemPosition = layoutManager.findFirstVisibleItemPosition() | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     @TargetApi(Build.VERSION_CODES.KITKAT) |  | ||||||
|     override fun onScrollStateChanged(state: Int) { |     override fun onScrollStateChanged(state: Int) { | ||||||
|         super.onScrollStateChanged(state) |         super.onScrollStateChanged(state) | ||||||
|         val layoutManager = layoutManager |         val layoutManager = layoutManager | ||||||
|  | |||||||
| @ -5,10 +5,9 @@ import android.app.Activity | |||||||
| import android.app.Dialog | import android.app.Dialog | ||||||
| import android.content.* | import android.content.* | ||||||
| import android.net.Uri | import android.net.Uri | ||||||
| import android.os.Build |  | ||||||
| import android.os.Bundle | import android.os.Bundle | ||||||
| import androidx.preference.PreferenceScreen |  | ||||||
| import android.view.View | import android.view.View | ||||||
|  | import androidx.preference.PreferenceScreen | ||||||
| import com.afollestad.materialdialogs.MaterialDialog | import com.afollestad.materialdialogs.MaterialDialog | ||||||
| import com.hippo.unifile.UniFile | import com.hippo.unifile.UniFile | ||||||
| import eu.kanade.tachiyomi.R | import eu.kanade.tachiyomi.R | ||||||
| @ -106,21 +105,12 @@ class SettingsBackupController : SettingsController() { | |||||||
|                 onClick { |                 onClick { | ||||||
|                     val currentDir = preferences.backupsDirectory().getOrDefault() |                     val currentDir = preferences.backupsDirectory().getOrDefault() | ||||||
|                     try{ |                     try{ | ||||||
|                         val intent = if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) { |                         val intent = Intent(Intent.ACTION_OPEN_DOCUMENT_TREE) | ||||||
|                         // Custom dir selected, open directory selector |  | ||||||
|                         preferences.context.getFilePicker(currentDir) |  | ||||||
|                         } else { |  | ||||||
|                           Intent(Intent.ACTION_OPEN_DOCUMENT_TREE) |  | ||||||
|                         } |  | ||||||
| 
 |  | ||||||
|                         startActivityForResult(intent, CODE_BACKUP_DIR) |                         startActivityForResult(intent, CODE_BACKUP_DIR) | ||||||
|                     } catch (e: ActivityNotFoundException){ |                     } catch (e: ActivityNotFoundException){ | ||||||
|                         //Fall back to custom picker on error |                         // Fall back to custom picker on error | ||||||
|                         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP){ |                         startActivityForResult(preferences.context.getFilePicker(currentDir), CODE_BACKUP_DIR) | ||||||
|                             startActivityForResult(preferences.context.getFilePicker(currentDir), CODE_BACKUP_DIR) |  | ||||||
|                         } |  | ||||||
|                     } |                     } | ||||||
| 
 |  | ||||||
|                 } |                 } | ||||||
| 
 | 
 | ||||||
|                 preferences.backupsDirectory().asObservable() |                 preferences.backupsDirectory().asObservable() | ||||||
| @ -154,37 +144,27 @@ class SettingsBackupController : SettingsController() { | |||||||
|                 // Get uri of backup folder. |                 // Get uri of backup folder. | ||||||
|                 val uri = data.data |                 val uri = data.data | ||||||
| 
 | 
 | ||||||
|                 // Get UriPermission so it's possible to write files post kitkat. |                 // Get UriPermission so it's possible to write files | ||||||
|                 if (Build.VERSION.SDK_INT > Build.VERSION_CODES.KITKAT) { |                 val flags = Intent.FLAG_GRANT_READ_URI_PERMISSION or | ||||||
|                     val flags = Intent.FLAG_GRANT_READ_URI_PERMISSION or |                         Intent.FLAG_GRANT_WRITE_URI_PERMISSION | ||||||
|                             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()) |                 preferences.backupsDirectory().set(uri.toString()) | ||||||
|             } |             } | ||||||
|             CODE_BACKUP_CREATE -> if (data != null && resultCode == Activity.RESULT_OK) { |             CODE_BACKUP_CREATE -> if (data != null && resultCode == Activity.RESULT_OK) { | ||||||
|                 val activity = activity ?: return |                 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) |                 val uri = data.data | ||||||
|                 } else { |                 val flags = Intent.FLAG_GRANT_READ_URI_PERMISSION or | ||||||
|                     val uri = data.data |                         Intent.FLAG_GRANT_WRITE_URI_PERMISSION | ||||||
|                     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) | ||||||
|                     val file = UniFile.fromUri(activity, uri) |                 val file = UniFile.fromUri(activity, uri) | ||||||
| 
 |  | ||||||
|                     file.uri |  | ||||||
|                 } |  | ||||||
| 
 | 
 | ||||||
|                 CreatingBackupDialog().showDialog(router, TAG_CREATING_BACKUP_DIALOG) |                 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) { |             CODE_BACKUP_RESTORE -> if (data != null && resultCode == Activity.RESULT_OK) { | ||||||
|                 val uri = data.data |                 val uri = data.data | ||||||
| @ -201,25 +181,17 @@ class SettingsBackupController : SettingsController() { | |||||||
|         val currentDir = preferences.backupsDirectory().getOrDefault() |         val currentDir = preferences.backupsDirectory().getOrDefault() | ||||||
| 
 | 
 | ||||||
|         try { |         try { | ||||||
|             // If API is lower than Lollipop use custom picker |             // Use Android's built-in file creator | ||||||
|             val intent = if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) { |             val intent = Intent(Intent.ACTION_CREATE_DOCUMENT) | ||||||
|                 preferences.context.getFilePicker(currentDir) |  | ||||||
|             } else { |  | ||||||
|                 // Use Androids build in file creator |  | ||||||
|                 Intent(Intent.ACTION_CREATE_DOCUMENT) |  | ||||||
|                     .addCategory(Intent.CATEGORY_OPENABLE) |                     .addCategory(Intent.CATEGORY_OPENABLE) | ||||||
|                     .setType("application/*") |                     .setType("application/*") | ||||||
|                     .putExtra(Intent.EXTRA_TITLE, Backup.getDefaultFilename()) |                     .putExtra(Intent.EXTRA_TITLE, Backup.getDefaultFilename()) | ||||||
|             } |  | ||||||
| 
 | 
 | ||||||
|             startActivityForResult(intent, CODE_BACKUP_CREATE) |             startActivityForResult(intent, CODE_BACKUP_CREATE) | ||||||
|         } catch (e: ActivityNotFoundException) { |         } catch (e: ActivityNotFoundException) { | ||||||
|             // Handle errors where the android ROM doesn't support the built in picker |             // 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() { |     class CreateBackupDialog : DialogController() { | ||||||
|  | |||||||
| @ -5,7 +5,6 @@ import android.app.Dialog | |||||||
| import android.content.ActivityNotFoundException | import android.content.ActivityNotFoundException | ||||||
| import android.content.Intent | import android.content.Intent | ||||||
| import android.net.Uri | import android.net.Uri | ||||||
| import android.os.Build |  | ||||||
| import android.os.Bundle | import android.os.Bundle | ||||||
| import android.os.Environment | import android.os.Environment | ||||||
| import androidx.core.content.ContextCompat | import androidx.core.content.ContextCompat | ||||||
| @ -107,11 +106,7 @@ class SettingsDownloadController : SettingsController() { | |||||||
| 
 | 
 | ||||||
|     override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { |     override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { | ||||||
|         when (requestCode) { |         when (requestCode) { | ||||||
|             DOWNLOAD_DIR_PRE_L -> if (data != null && resultCode == Activity.RESULT_OK) { |             DOWNLOAD_DIR -> 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) { |  | ||||||
|                 val context = applicationContext ?: return |                 val context = applicationContext ?: return | ||||||
|                 val uri = data.data |                 val uri = data.data | ||||||
|                 val flags = Intent.FLAG_GRANT_READ_URI_PERMISSION or |                 val flags = Intent.FLAG_GRANT_READ_URI_PERMISSION or | ||||||
| @ -132,19 +127,11 @@ class SettingsDownloadController : SettingsController() { | |||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     fun customDirectorySelected(currentDir: String) { |     fun customDirectorySelected(currentDir: String) { | ||||||
| 
 |         val intent = Intent(Intent.ACTION_OPEN_DOCUMENT_TREE) | ||||||
|         if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) { |         try { | ||||||
|             startActivityForResult(preferences.context.getFilePicker(currentDir), DOWNLOAD_DIR_PRE_L) |             startActivityForResult(intent, DOWNLOAD_DIR) | ||||||
|         } else { |         } catch (e: ActivityNotFoundException) { | ||||||
|             val intent = Intent(Intent.ACTION_OPEN_DOCUMENT_TREE) |             startActivityForResult(preferences.context.getFilePicker(currentDir), DOWNLOAD_DIR) | ||||||
|             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) |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| @ -183,7 +170,6 @@ class SettingsDownloadController : SettingsController() { | |||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     private companion object { |     private companion object { | ||||||
|         const val DOWNLOAD_DIR_PRE_L = 103 |         const val DOWNLOAD_DIR = 104 | ||||||
|         const val DOWNLOAD_DIR_L = 104 |  | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  | |||||||
| @ -3,7 +3,6 @@ package eu.kanade.tachiyomi.util | |||||||
| import android.content.Context | import android.content.Context | ||||||
| import android.content.Intent | import android.content.Intent | ||||||
| import android.net.Uri | import android.net.Uri | ||||||
| import android.os.Build |  | ||||||
| import android.os.Environment | import android.os.Environment | ||||||
| import androidx.core.content.ContextCompat | import androidx.core.content.ContextCompat | ||||||
| import androidx.core.os.EnvironmentCompat | 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 |         return directories | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| @ -79,11 +71,7 @@ object DiskUtil { | |||||||
|      * Scans the given file so that it can be shown in gallery apps, for example. |      * Scans the given file so that it can be shown in gallery apps, for example. | ||||||
|      */ |      */ | ||||||
|     fun scanMedia(context: Context, uri: Uri) { |     fun scanMedia(context: Context, uri: Uri) { | ||||||
|         val action = if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) { |         val action = Intent.ACTION_MEDIA_SCANNER_SCAN_FILE | ||||||
|             Intent.ACTION_MEDIA_MOUNTED |  | ||||||
|         } else { |  | ||||||
|             Intent.ACTION_MEDIA_SCANNER_SCAN_FILE |  | ||||||
|         } |  | ||||||
|         val mediaScanIntent = Intent(action) |         val mediaScanIntent = Intent(action) | ||||||
|         mediaScanIntent.data = uri |         mediaScanIntent.data = uri | ||||||
|         context.sendBroadcast(mediaScanIntent) |         context.sendBroadcast(mediaScanIntent) | ||||||
|  | |||||||
| @ -36,7 +36,6 @@ abstract class WebViewClientCompat : WebViewClient() { | |||||||
|         return shouldOverrideUrlCompat(view, url) |         return shouldOverrideUrlCompat(view, url) | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     @TargetApi(Build.VERSION_CODES.LOLLIPOP) |  | ||||||
|     final override fun shouldInterceptRequest( |     final override fun shouldInterceptRequest( | ||||||
|             view: WebView, |             view: WebView, | ||||||
|             request: WebResourceRequest |             request: WebResourceRequest | ||||||
|  | |||||||
| @ -16,32 +16,26 @@ class ElevationAppBarLayout @JvmOverloads constructor( | |||||||
|     private var origStateAnimator: StateListAnimator? = null |     private var origStateAnimator: StateListAnimator? = null | ||||||
| 
 | 
 | ||||||
|     init { |     init { | ||||||
|         if (Build.VERSION.SDK_INT >= 21) { |         origStateAnimator = stateListAnimator | ||||||
|             origStateAnimator = stateListAnimator |  | ||||||
|         } |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     fun enableElevation() { |     fun enableElevation() { | ||||||
|         if (Build.VERSION.SDK_INT >= 21) { |         stateListAnimator = origStateAnimator | ||||||
|             stateListAnimator = origStateAnimator |  | ||||||
|         } |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     fun disableElevation() { |     fun disableElevation() { | ||||||
|         if (Build.VERSION.SDK_INT >= 21) { |         stateListAnimator = StateListAnimator().apply { | ||||||
|             stateListAnimator = StateListAnimator().apply { |             val objAnimator = ObjectAnimator.ofFloat(this, "elevation", 0f) | ||||||
|                 val objAnimator = ObjectAnimator.ofFloat(this, "elevation", 0f) |  | ||||||
| 
 | 
 | ||||||
|                 // Enabled and collapsible, but not collapsed means not elevated |             // Enabled and collapsible, but not collapsed means not elevated | ||||||
|                 addState(intArrayOf(android.R.attr.enabled, R.attr.state_collapsible, -R.attr.state_collapsed), |             addState(intArrayOf(android.R.attr.enabled, R.attr.state_collapsible, -R.attr.state_collapsed), | ||||||
|                         objAnimator) |                     objAnimator) | ||||||
| 
 | 
 | ||||||
|                 // Default enabled state |             // Default enabled state | ||||||
|                 addState(intArrayOf(android.R.attr.enabled), objAnimator) |             addState(intArrayOf(android.R.attr.enabled), objAnimator) | ||||||
| 
 | 
 | ||||||
|                 // Disabled state |             // Disabled state | ||||||
|                 addState(IntArray(0), objAnimator) |             addState(IntArray(0), objAnimator) | ||||||
|             } |  | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -2,14 +2,11 @@ package eu.kanade.tachiyomi.widget | |||||||
| 
 | 
 | ||||||
| import android.animation.Animator | import android.animation.Animator | ||||||
| import android.animation.AnimatorListenerAdapter | import android.animation.AnimatorListenerAdapter | ||||||
| import android.annotation.TargetApi |  | ||||||
| import android.content.Context | import android.content.Context | ||||||
| import android.os.Build |  | ||||||
| import android.util.AttributeSet | import android.util.AttributeSet | ||||||
| import android.view.View | import android.view.View | ||||||
| import android.view.ViewAnimationUtils | import android.view.ViewAnimationUtils | ||||||
| 
 | 
 | ||||||
| @TargetApi(Build.VERSION_CODES.LOLLIPOP) |  | ||||||
| class RevealAnimationView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null) : | class RevealAnimationView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null) : | ||||||
|         View(context, attrs) { |         View(context, attrs) { | ||||||
| 
 | 
 | ||||||
| @ -21,28 +18,25 @@ class RevealAnimationView @JvmOverloads constructor(context: Context, attrs: Att | |||||||
|      * @param initialRadius size of radius of animation |      * @param initialRadius size of radius of animation | ||||||
|      */ |      */ | ||||||
|     fun hideRevealEffect(centerX: Int, centerY: Int, initialRadius: Int) { |     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. |         // Create the animation (the final radius is zero). | ||||||
|             this.visibility = View.VISIBLE |         val anim = ViewAnimationUtils.createCircularReveal( | ||||||
|  |                 this, centerX, centerY, initialRadius.toFloat(), 0f) | ||||||
| 
 | 
 | ||||||
|             // Create the animation (the final radius is zero). |         // Set duration of animation. | ||||||
|             val anim = ViewAnimationUtils.createCircularReveal( |         anim.duration = 500 | ||||||
|                     this, centerX, centerY, initialRadius.toFloat(), 0f) |  | ||||||
| 
 | 
 | ||||||
|             // Set duration of animation. |         // make the view invisible when the animation is done | ||||||
|             anim.duration = 500 |         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.start() | ||||||
|             anim.addListener(object : AnimatorListenerAdapter() { |  | ||||||
|                 override fun onAnimationEnd(animation: Animator) { |  | ||||||
|                     super.onAnimationEnd(animation) |  | ||||||
|                     this@RevealAnimationView.visibility = View.INVISIBLE |  | ||||||
|                 } |  | ||||||
|             }) |  | ||||||
| 
 |  | ||||||
|             anim.start() |  | ||||||
|         } |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
| @ -55,25 +49,20 @@ class RevealAnimationView @JvmOverloads constructor(context: Context, attrs: Att | |||||||
|      * @return sdk version lower then 21 |      * @return sdk version lower then 21 | ||||||
|      */ |      */ | ||||||
|     fun showRevealEffect(centerX: Int, centerY: Int, listener: Animator.AnimatorListener): Boolean { |     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 |         // Set duration of animation | ||||||
|             val anim = ViewAnimationUtils.createCircularReveal( |         anim.duration = 350 | ||||||
|                     this, centerX, centerY, 0f, height.toFloat()) |  | ||||||
| 
 | 
 | ||||||
|             // Set duration of animation |         anim.addListener(listener) | ||||||
|             anim.duration = 350 |         anim.start() | ||||||
| 
 |         return true | ||||||
|             anim.addListener(listener) |  | ||||||
|             anim.start() |  | ||||||
|             return true |  | ||||||
|         } |  | ||||||
|         return false |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| 
 |  | ||||||
| } | } | ||||||
|  | |||||||
| @ -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> |  | ||||||
| @ -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> |  | ||||||
| @ -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> |  | ||||||
| @ -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> |  | ||||||
| @ -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> |  | ||||||
| @ -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> |  | ||||||
| @ -1,10 +1,19 @@ | |||||||
| <?xml version="1.0" encoding="utf-8"?> | <?xml version="1.0" encoding="utf-8"?> | ||||||
| <selector android:exitFadeDuration="@android:integer/config_longAnimTime" | <ripple xmlns:android="http://schemas.android.com/apk/res/android" | ||||||
|     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_activated="true"> | ||||||
|     <item android:state_pressed="true" android:drawable="@color/selectorColorDark"/> |                 <color android:color="@color/selectorColorDark" /> | ||||||
|     <item android:state_activated="true" android:drawable="@color/selectorColorDark"/> |             </item> | ||||||
|     <item android:drawable="@color/md_black_1000"/> |  | ||||||
| 
 | 
 | ||||||
| </selector> |             <item> | ||||||
|  |                 <color android:color="@color/md_black_1000" /> | ||||||
|  |             </item> | ||||||
|  |         </selector> | ||||||
|  |     </item> | ||||||
|  | </ripple> | ||||||
|  | |||||||
| @ -1,10 +1,19 @@ | |||||||
| <?xml version="1.0" encoding="utf-8"?> | <?xml version="1.0" encoding="utf-8"?> | ||||||
| <selector android:exitFadeDuration="@android:integer/config_longAnimTime" | <ripple xmlns:android="http://schemas.android.com/apk/res/android" | ||||||
|     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_activated="true"> | ||||||
|     <item android:state_pressed="true" android:drawable="@color/selectorColorDark"/> |                 <color android:color="@color/selectorColorDark" /> | ||||||
|     <item android:state_activated="true" android:drawable="@color/selectorColorDark"/> |             </item> | ||||||
|     <item android:drawable="@color/backgroundDark"/> |  | ||||||
| 
 | 
 | ||||||
| </selector> |             <item> | ||||||
|  |                 <color android:color="@color/backgroundDark" /> | ||||||
|  |             </item> | ||||||
|  |         </selector> | ||||||
|  |     </item> | ||||||
|  | </ripple> | ||||||
|  | |||||||
| @ -1,10 +1,19 @@ | |||||||
| <?xml version="1.0" encoding="utf-8"?> | <?xml version="1.0" encoding="utf-8"?> | ||||||
| <selector android:exitFadeDuration="@android:integer/config_longAnimTime" | <ripple xmlns:android="http://schemas.android.com/apk/res/android" | ||||||
|     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_activated="true"> | ||||||
|     <item android:state_pressed="true" android:drawable="@color/selectorColorLight"/> |                 <color android:color="@color/selectorColorLight" /> | ||||||
|     <item android:state_activated="true" android:drawable="@color/selectorColorLight"/> |             </item> | ||||||
|     <item android:drawable="@color/backgroundLight"/> |  | ||||||
| 
 | 
 | ||||||
| </selector> |             <item> | ||||||
|  |                 <color android:color="@color/backgroundLight" /> | ||||||
|  |             </item> | ||||||
|  |         </selector> | ||||||
|  |     </item> | ||||||
|  | </ripple> | ||||||
|  | |||||||
| @ -1,10 +1,19 @@ | |||||||
| <?xml version="1.0" encoding="utf-8"?> | <?xml version="1.0" encoding="utf-8"?> | ||||||
| <selector xmlns:android="http://schemas.android.com/apk/res/android" | <ripple xmlns:android="http://schemas.android.com/apk/res/android" | ||||||
|     android:exitFadeDuration="@android:integer/config_longAnimTime"> |     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:state_activated="true"> | ||||||
|     <item android:drawable="@color/rippleColorDark" android:state_pressed="true"/> |                 <color android:color="@color/rippleColorDark" /> | ||||||
|     <item android:drawable="@color/rippleColorDark" android:state_activated="true"/> |             </item> | ||||||
|     <item android:drawable="@color/md_black_1000"/> |  | ||||||
| 
 | 
 | ||||||
| </selector> |             <item> | ||||||
|  |                 <color android:color="@color/md_black_1000" /> | ||||||
|  |             </item> | ||||||
|  |         </selector> | ||||||
|  |     </item> | ||||||
|  | </ripple> | ||||||
|  | |||||||
| @ -1,10 +1,19 @@ | |||||||
| <?xml version="1.0" encoding="utf-8"?> | <?xml version="1.0" encoding="utf-8"?> | ||||||
| <selector xmlns:android="http://schemas.android.com/apk/res/android" | <ripple xmlns:android="http://schemas.android.com/apk/res/android" | ||||||
|     android:exitFadeDuration="@android:integer/config_longAnimTime"> |     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:state_activated="true"> | ||||||
|     <item android:drawable="@color/rippleColorDark" android:state_pressed="true"/> |                 <color android:color="@color/rippleColorDark" /> | ||||||
|     <item android:drawable="@color/rippleColorDark" android:state_activated="true"/> |             </item> | ||||||
|     <item android:drawable="@color/dialogDark"/> |  | ||||||
| 
 | 
 | ||||||
| </selector> |             <item> | ||||||
|  |                 <color android:color="@color/dialogDark" /> | ||||||
|  |             </item> | ||||||
|  |         </selector> | ||||||
|  |     </item> | ||||||
|  | </ripple> | ||||||
|  | |||||||
| @ -1,10 +1,19 @@ | |||||||
| <?xml version="1.0" encoding="utf-8"?> | <?xml version="1.0" encoding="utf-8"?> | ||||||
| <selector xmlns:android="http://schemas.android.com/apk/res/android" | <ripple xmlns:android="http://schemas.android.com/apk/res/android" | ||||||
|     android:exitFadeDuration="@android:integer/config_longAnimTime"> |     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:state_activated="true"> | ||||||
|     <item android:drawable="@color/rippleColorLight" android:state_pressed="true"/> |                 <color android:color="@color/rippleColorLight" /> | ||||||
|     <item android:drawable="@color/rippleColorLight" android:state_activated="true"/> |             </item> | ||||||
|     <item android:drawable="@color/dialogLight"/> |  | ||||||
| 
 | 
 | ||||||
| </selector> |             <item> | ||||||
|  |                 <color android:color="@color/dialogLight" /> | ||||||
|  |             </item> | ||||||
|  |         </selector> | ||||||
|  |     </item> | ||||||
|  | </ripple> | ||||||
|  | |||||||
| @ -1,5 +0,0 @@ | |||||||
| <?xml version="1.0" encoding="utf-8"?> |  | ||||||
| <resources> |  | ||||||
|     <!--Nav header--> |  | ||||||
|     <dimen name="navigation_drawer_header_margin">41dp</dimen> |  | ||||||
| </resources> |  | ||||||
| @ -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> |  | ||||||
| @ -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> |  | ||||||
| @ -20,10 +20,9 @@ | |||||||
|     <dimen name="text_body">16sp</dimen> |     <dimen name="text_body">16sp</dimen> | ||||||
|     <dimen name="text_small_body">14sp</dimen> |     <dimen name="text_small_body">14sp</dimen> | ||||||
| 
 | 
 | ||||||
| 
 |  | ||||||
|     <!--Nav header--> |     <!--Nav header--> | ||||||
|     <dimen name="navigation_drawer_header_height">158dp</dimen> |     <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> |     <dimen name="bottom_sheet_width">0dp</dimen> | ||||||
| </resources> | </resources> | ||||||
|  | |||||||
| @ -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> |  | ||||||
| @ -50,7 +50,7 @@ | |||||||
|     </style> |     </style> | ||||||
| 
 | 
 | ||||||
|     <style name="TextAppearance.Regular"> |     <style name="TextAppearance.Regular"> | ||||||
|         <item name="android:fontFamily">@string/font_roboto_regular</item> |         <item name="android:fontFamily">sans-serif-regular</item> | ||||||
|     </style> |     </style> | ||||||
| 
 | 
 | ||||||
|     <style name="TextAppearance.Regular.Body1"> |     <style name="TextAppearance.Regular.Body1"> | ||||||
| @ -102,7 +102,7 @@ | |||||||
|     </style> |     </style> | ||||||
| 
 | 
 | ||||||
|     <style name="TextAppearance.Medium"> |     <style name="TextAppearance.Medium"> | ||||||
|         <item name="android:fontFamily">@string/font_roboto_medium</item> |         <item name="android:fontFamily">sans-serif-medium</item> | ||||||
|     </style> |     </style> | ||||||
| 
 | 
 | ||||||
|     <style name="TextAppearance.Medium.Title"> |     <style name="TextAppearance.Medium.Title"> | ||||||
|  | |||||||
| @ -39,8 +39,13 @@ | |||||||
|         <item name="icon_color">@color/iconColorLight</item> |         <item name="icon_color">@color/iconColorLight</item> | ||||||
|     </style> |     </style> | ||||||
| 
 | 
 | ||||||
|  |     <!--===========--> | ||||||
|  |     <!-- Main Theme--> | ||||||
|  |     <!--===========--> | ||||||
|     <style name="Theme.Tachiyomi" parent="Theme.Base"> |     <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> |     </style> | ||||||
| 
 | 
 | ||||||
|     <!--=============--> |     <!--=============--> | ||||||
| @ -61,6 +66,10 @@ | |||||||
|         <item name="android:divider">@color/dividerDark</item> |         <item name="android:divider">@color/dividerDark</item> | ||||||
|         <item name="android:listDivider">@drawable/line_divider_dark</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 --> |         <!-- Themes --> | ||||||
|         <item name="windowActionModeOverlay">true</item> |         <item name="windowActionModeOverlay">true</item> | ||||||
|         <item name="actionBarTheme">@style/ThemeOverlay.AppCompat.Dark.ActionBar</item> |         <item name="actionBarTheme">@style/ThemeOverlay.AppCompat.Dark.ActionBar</item> | ||||||
| @ -86,6 +95,10 @@ | |||||||
|     <style name="Theme.Tachiyomi.DarkBlue" parent="Theme.Base.Dark"> |     <style name="Theme.Tachiyomi.DarkBlue" parent="Theme.Base.Dark"> | ||||||
|         <item name="colorPrimary">@color/colorPrimary</item> |         <item name="colorPrimary">@color/colorPrimary</item> | ||||||
|         <item name="colorPrimaryDark">@color/colorPrimaryDark</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> |     </style> | ||||||
| 
 | 
 | ||||||
|     <!--==============--> |     <!--==============--> | ||||||
| @ -96,6 +109,10 @@ | |||||||
|         <item name="colorPrimaryDark">@color/colorAmoledPrimary</item> |         <item name="colorPrimaryDark">@color/colorAmoledPrimary</item> | ||||||
|         <item name="android:colorBackground">@color/md_black_1000</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--> |         <!-- Custom Attributes--> | ||||||
|         <item name="selectable_list_drawable">@drawable/list_item_selector_amoled</item> |         <item name="selectable_list_drawable">@drawable/list_item_selector_amoled</item> | ||||||
|         <item name="selectable_library_drawable">@drawable/library_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="colorPrimary">@color/colorDarkPrimary</item> | ||||||
|         <item name="colorPrimaryDark">@color/colorDarkPrimaryDark</item> |         <item name="colorPrimaryDark">@color/colorDarkPrimaryDark</item> | ||||||
|         <item name="android:colorBackground">@android:color/black</item> |         <item name="android:colorBackground">@android:color/black</item> | ||||||
|  | 
 | ||||||
|  |         <item name="android:statusBarColor">?colorPrimaryDark</item> | ||||||
|  |         <item name="android:navigationBarColor">?colorPrimaryDark</item> | ||||||
|     </style> |     </style> | ||||||
| 
 | 
 | ||||||
|     <style name="Theme.Base.Reader.Light" parent="Theme.Base"> |     <style name="Theme.Base.Reader.Light" parent="Theme.Base"> | ||||||
|         <item name="colorPrimary">@color/colorDarkPrimary</item> |         <item name="colorPrimary">@color/colorDarkPrimary</item> | ||||||
|         <item name="colorPrimaryDark">@color/colorDarkPrimaryDark</item> |         <item name="colorPrimaryDark">@color/colorDarkPrimaryDark</item> | ||||||
|         <item name="android:colorBackground">@android:color/white</item> |         <item name="android:colorBackground">@android:color/white</item> | ||||||
|  | 
 | ||||||
|  |         <item name="android:statusBarColor">?colorPrimaryDark</item> | ||||||
|  |         <item name="android:navigationBarColor">?colorPrimaryDark</item> | ||||||
|     </style> |     </style> | ||||||
| 
 | 
 | ||||||
|     <style name="Theme.Reader" parent="Theme.Base.Reader.Dark"> |     <style name="Theme.Reader" parent="Theme.Base.Reader.Dark"> | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 arkon
						arkon