mirror of
				https://github.com/mihonapp/mihon.git
				synced 2025-10-31 14:27:57 +01:00 
			
		
		
		
	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:
		| @@ -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 {} | ||||
|     } | ||||
|  | ||||
| } | ||||
|   | ||||
| @@ -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 | ||||
|   | ||||
| @@ -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 | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -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 | ||||
|  | ||||
| } | ||||
| } | ||||
|   | ||||
| @@ -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 | ||||
|  | ||||
|   | ||||
| @@ -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 | ||||
|   | ||||
| @@ -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() { | ||||
|   | ||||
| @@ -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 | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -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) | ||||
|   | ||||
| @@ -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 | ||||
|   | ||||
| @@ -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) | ||||
|         } | ||||
|     } | ||||
|  | ||||
|   | ||||
| @@ -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 | ||||
|     } | ||||
|  | ||||
|  | ||||
| } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user