Remove old code that was used on devices with api level <21

Signed-off-by: Tilo Spannagel <development@tilosp.de>
This commit is contained in:
Tilo Spannagel 2020-08-17 18:02:01 +02:00
parent 1b9b18851d
commit 8c7956d935
45 changed files with 135 additions and 489 deletions

View File

@ -32,6 +32,7 @@ Other changes:
- Rename package `im.vector.riotx.attachmentviewer` to `im.vector.lib.attachmentviewer` - Rename package `im.vector.riotx.attachmentviewer` to `im.vector.lib.attachmentviewer`
- Rename package `im.vector.riotx.multipicker` to `im.vector.lib.multipicker` - Rename package `im.vector.riotx.multipicker` to `im.vector.lib.multipicker`
- Rename package `im.vector.riotx` to `im.vector.app` - Rename package `im.vector.riotx` to `im.vector.app`
- Remove old code that was used on devices with api level <21
Changes in Element 1.0.4 (2020-08-03) Changes in Element 1.0.4 (2020-08-03)
=================================================== ===================================================

View File

@ -18,7 +18,6 @@
package org.matrix.android.sdk.internal.crypto.store.db package org.matrix.android.sdk.internal.crypto.store.db
import android.util.Base64 import android.util.Base64
import org.matrix.android.sdk.internal.util.CompatUtil
import io.realm.Realm import io.realm.Realm
import io.realm.RealmConfiguration import io.realm.RealmConfiguration
import io.realm.RealmObject import io.realm.RealmObject
@ -26,6 +25,7 @@ import java.io.ByteArrayInputStream
import java.io.ByteArrayOutputStream import java.io.ByteArrayOutputStream
import java.io.ObjectOutputStream import java.io.ObjectOutputStream
import java.util.zip.GZIPInputStream import java.util.zip.GZIPInputStream
import java.util.zip.GZIPOutputStream
/** /**
* Get realm, invoke the action, close realm, and return the result of the action * Get realm, invoke the action, close realm, and return the result of the action
@ -78,7 +78,7 @@ fun serializeForRealm(o: Any?): String? {
} }
val baos = ByteArrayOutputStream() val baos = ByteArrayOutputStream()
val gzis = CompatUtil.createGzipOutputStream(baos) val gzis = GZIPOutputStream(baos)
val out = ObjectOutputStream(gzis) val out = ObjectOutputStream(gzis)
out.use { out.use {
it.writeObject(o) it.writeObject(o)

View File

@ -44,10 +44,7 @@ import javax.crypto.CipherInputStream
import javax.crypto.CipherOutputStream import javax.crypto.CipherOutputStream
import javax.crypto.KeyGenerator import javax.crypto.KeyGenerator
import javax.crypto.SecretKey import javax.crypto.SecretKey
import javax.crypto.SecretKeyFactory
import javax.crypto.spec.GCMParameterSpec import javax.crypto.spec.GCMParameterSpec
import javax.crypto.spec.IvParameterSpec
import javax.crypto.spec.PBEKeySpec
import javax.crypto.spec.SecretKeySpec import javax.crypto.spec.SecretKeySpec
import javax.inject.Inject import javax.inject.Inject
import javax.security.auth.x500.X500Principal import javax.security.auth.x500.X500Principal
@ -127,9 +124,8 @@ internal class SecretStoringUtils @Inject constructor(private val context: Conte
@Throws(Exception::class) @Throws(Exception::class)
fun securelyStoreString(secret: String, keyAlias: String): ByteArray? { fun securelyStoreString(secret: String, keyAlias: String): ByteArray? {
return when { return when {
Build.VERSION.SDK_INT >= Build.VERSION_CODES.M -> encryptStringM(secret, keyAlias) Build.VERSION.SDK_INT >= Build.VERSION_CODES.M -> encryptStringM(secret, keyAlias)
Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT -> encryptStringK(secret, keyAlias) else -> encryptString(secret, keyAlias)
else -> encryptForOldDevicesNotGood(secret, keyAlias)
} }
} }
@ -139,25 +135,22 @@ internal class SecretStoringUtils @Inject constructor(private val context: Conte
@Throws(Exception::class) @Throws(Exception::class)
fun loadSecureSecret(encrypted: ByteArray, keyAlias: String): String? { fun loadSecureSecret(encrypted: ByteArray, keyAlias: String): String? {
return when { return when {
Build.VERSION.SDK_INT >= Build.VERSION_CODES.M -> decryptStringM(encrypted, keyAlias) Build.VERSION.SDK_INT >= Build.VERSION_CODES.M -> decryptStringM(encrypted, keyAlias)
Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT -> decryptStringK(encrypted, keyAlias) else -> decryptString(encrypted, keyAlias)
else -> decryptForOldDevicesNotGood(encrypted, keyAlias)
} }
} }
fun securelyStoreObject(any: Any, keyAlias: String, output: OutputStream) { fun securelyStoreObject(any: Any, keyAlias: String, output: OutputStream) {
when { when {
Build.VERSION.SDK_INT >= Build.VERSION_CODES.M -> saveSecureObjectM(keyAlias, output, any) Build.VERSION.SDK_INT >= Build.VERSION_CODES.M -> saveSecureObjectM(keyAlias, output, any)
Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT -> saveSecureObjectK(keyAlias, output, any) else -> saveSecureObject(keyAlias, output, any)
else -> saveSecureObjectOldNotGood(keyAlias, output, any)
} }
} }
fun <T> loadSecureSecret(inputStream: InputStream, keyAlias: String): T? { fun <T> loadSecureSecret(inputStream: InputStream, keyAlias: String): T? {
return when { return when {
Build.VERSION.SDK_INT >= Build.VERSION_CODES.M -> loadSecureObjectM(keyAlias, inputStream) Build.VERSION.SDK_INT >= Build.VERSION_CODES.M -> loadSecureObjectM(keyAlias, inputStream)
Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT -> loadSecureObjectK(keyAlias, inputStream) else -> loadSecureObject(keyAlias, inputStream)
else -> loadSecureObjectOldNotGood(keyAlias, inputStream)
} }
} }
@ -188,7 +181,6 @@ internal class SecretStoringUtils @Inject constructor(private val context: Conte
- Store the encrypted AES - Store the encrypted AES
Generate a key pair for encryption Generate a key pair for encryption
*/ */
@RequiresApi(Build.VERSION_CODES.KITKAT)
fun getOrGenerateKeyPairForAlias(alias: String): KeyStore.PrivateKeyEntry { fun getOrGenerateKeyPairForAlias(alias: String): KeyStore.PrivateKeyEntry {
val privateKeyEntry = (keyStore.getEntry(alias, null) as? KeyStore.PrivateKeyEntry) val privateKeyEntry = (keyStore.getEntry(alias, null) as? KeyStore.PrivateKeyEntry)
@ -238,8 +230,7 @@ internal class SecretStoringUtils @Inject constructor(private val context: Conte
return String(cipher.doFinal(encryptedText), Charsets.UTF_8) return String(cipher.doFinal(encryptedText), Charsets.UTF_8)
} }
@RequiresApi(Build.VERSION_CODES.KITKAT) private fun encryptString(text: String, keyAlias: String): ByteArray? {
private fun encryptStringK(text: String, keyAlias: String): ByteArray? {
// we generate a random symmetric key // we generate a random symmetric key
val key = ByteArray(16) val key = ByteArray(16)
secureRandom.nextBytes(key) secureRandom.nextBytes(key)
@ -256,47 +247,13 @@ internal class SecretStoringUtils @Inject constructor(private val context: Conte
return format1Make(encryptedKey, iv, encryptedBytes) return format1Make(encryptedKey, iv, encryptedBytes)
} }
private fun encryptForOldDevicesNotGood(text: String, keyAlias: String): ByteArray { private fun decryptString(data: ByteArray, keyAlias: String): String? {
val salt = ByteArray(8)
secureRandom.nextBytes(salt)
val factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256")
val spec = PBEKeySpec(keyAlias.toCharArray(), salt, 10000, 128)
val tmp = factory.generateSecret(spec)
val sKey = SecretKeySpec(tmp.encoded, "AES")
val cipher = Cipher.getInstance(AES_MODE)
cipher.init(Cipher.ENCRYPT_MODE, sKey)
val iv = cipher.iv
val encryptedBytes: ByteArray = cipher.doFinal(text.toByteArray(Charsets.UTF_8))
return format2Make(salt, iv, encryptedBytes)
}
private fun decryptForOldDevicesNotGood(data: ByteArray, keyAlias: String): String? {
val (salt, iv, encrypted) = format2Extract(ByteArrayInputStream(data))
val factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256")
val spec = PBEKeySpec(keyAlias.toCharArray(), salt, 10_000, 128)
val tmp = factory.generateSecret(spec)
val sKey = SecretKeySpec(tmp.encoded, "AES")
val cipher = Cipher.getInstance(AES_MODE)
// cipher.init(Cipher.ENCRYPT_MODE, sKey)
// val encryptedBytes: ByteArray = cipher.doFinal(text.toByteArray(Charsets.UTF_8))
val specIV = if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) IvParameterSpec(iv) else GCMParameterSpec(128, iv)
cipher.init(Cipher.DECRYPT_MODE, sKey, specIV)
return String(cipher.doFinal(encrypted), Charsets.UTF_8)
}
@RequiresApi(Build.VERSION_CODES.KITKAT)
private fun decryptStringK(data: ByteArray, keyAlias: String): String? {
val (encryptedKey, iv, encrypted) = format1Extract(ByteArrayInputStream(data)) val (encryptedKey, iv, encrypted) = format1Extract(ByteArrayInputStream(data))
// we need to decrypt the key // we need to decrypt the key
val sKeyBytes = rsaDecrypt(keyAlias, ByteArrayInputStream(encryptedKey)) val sKeyBytes = rsaDecrypt(keyAlias, ByteArrayInputStream(encryptedKey))
val cipher = Cipher.getInstance(AES_MODE) val cipher = Cipher.getInstance(AES_MODE)
val spec = if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) IvParameterSpec(iv) else GCMParameterSpec(128, iv) val spec = GCMParameterSpec(128, iv)
cipher.init(Cipher.DECRYPT_MODE, SecretKeySpec(sKeyBytes, "AES"), spec) cipher.init(Cipher.DECRYPT_MODE, SecretKeySpec(sKeyBytes, "AES"), spec)
return String(cipher.doFinal(encrypted), Charsets.UTF_8) return String(cipher.doFinal(encrypted), Charsets.UTF_8)
@ -323,8 +280,7 @@ internal class SecretStoringUtils @Inject constructor(private val context: Conte
output.write(doFinal) output.write(doFinal)
} }
@RequiresApi(Build.VERSION_CODES.KITKAT) private fun saveSecureObject(keyAlias: String, output: OutputStream, writeObject: Any) {
private fun saveSecureObjectK(keyAlias: String, output: OutputStream, writeObject: Any) {
// we generate a random symmetric key // we generate a random symmetric key
val key = ByteArray(16) val key = ByteArray(16)
secureRandom.nextBytes(key) secureRandom.nextBytes(key)
@ -352,32 +308,6 @@ internal class SecretStoringUtils @Inject constructor(private val context: Conte
output.write(bos1.toByteArray()) output.write(bos1.toByteArray())
} }
private fun saveSecureObjectOldNotGood(keyAlias: String, output: OutputStream, writeObject: Any) {
val salt = ByteArray(8)
secureRandom.nextBytes(salt)
val factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256")
val tmp = factory.generateSecret(PBEKeySpec(keyAlias.toCharArray(), salt, 10000, 128))
val secretKey = SecretKeySpec(tmp.encoded, "AES")
val cipher = Cipher.getInstance(AES_MODE)
cipher.init(Cipher.ENCRYPT_MODE, secretKey)
val iv = cipher.iv
val bos1 = ByteArrayOutputStream()
ObjectOutputStream(bos1).use {
it.writeObject(writeObject)
}
// Have to do it like that if i encapsulate the output stream, the cipher could fail saying reuse IV
val doFinal = cipher.doFinal(bos1.toByteArray())
output.write(FORMAT_2.toInt())
output.write(salt.size)
output.write(salt)
output.write(iv.size)
output.write(iv)
output.write(doFinal)
}
// @RequiresApi(Build.VERSION_CODES.M) // @RequiresApi(Build.VERSION_CODES.M)
// @Throws(IOException::class) // @Throws(IOException::class)
// fun saveSecureObjectM(keyAlias: String, file: File, writeObject: Any) { // fun saveSecureObjectM(keyAlias: String, file: File, writeObject: Any) {
@ -418,15 +348,14 @@ internal class SecretStoringUtils @Inject constructor(private val context: Conte
} }
} }
@RequiresApi(Build.VERSION_CODES.KITKAT)
@Throws(IOException::class) @Throws(IOException::class)
private fun <T> loadSecureObjectK(keyAlias: String, inputStream: InputStream): T? { private fun <T> loadSecureObject(keyAlias: String, inputStream: InputStream): T? {
val (encryptedKey, iv, encrypted) = format1Extract(inputStream) val (encryptedKey, iv, encrypted) = format1Extract(inputStream)
// we need to decrypt the key // we need to decrypt the key
val sKeyBytes = rsaDecrypt(keyAlias, ByteArrayInputStream(encryptedKey)) val sKeyBytes = rsaDecrypt(keyAlias, ByteArrayInputStream(encryptedKey))
val cipher = Cipher.getInstance(AES_MODE) val cipher = Cipher.getInstance(AES_MODE)
val spec = if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) IvParameterSpec(iv) else GCMParameterSpec(128, iv) val spec = GCMParameterSpec(128, iv)
cipher.init(Cipher.DECRYPT_MODE, SecretKeySpec(sKeyBytes, "AES"), spec) cipher.init(Cipher.DECRYPT_MODE, SecretKeySpec(sKeyBytes, "AES"), spec)
val encIS = ByteArrayInputStream(encrypted) val encIS = ByteArrayInputStream(encrypted)
@ -440,31 +369,6 @@ internal class SecretStoringUtils @Inject constructor(private val context: Conte
} }
} }
@Throws(Exception::class)
private fun <T> loadSecureObjectOldNotGood(keyAlias: String, inputStream: InputStream): T? {
val (salt, iv, encrypted) = format2Extract(inputStream)
val factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256")
val tmp = factory.generateSecret(PBEKeySpec(keyAlias.toCharArray(), salt, 10000, 128))
val sKey = SecretKeySpec(tmp.encoded, "AES")
// we need to decrypt the key
val cipher = Cipher.getInstance(AES_MODE)
val spec = if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) IvParameterSpec(iv) else GCMParameterSpec(128, iv)
cipher.init(Cipher.DECRYPT_MODE, sKey, spec)
val encIS = ByteArrayInputStream(encrypted)
CipherInputStream(encIS, cipher).use {
ObjectInputStream(it).use { ois ->
val readObject = ois.readObject()
@Suppress("UNCHECKED_CAST")
return readObject as? T
}
}
}
@RequiresApi(Build.VERSION_CODES.KITKAT)
@Throws(Exception::class) @Throws(Exception::class)
private fun rsaEncrypt(alias: String, secret: ByteArray): ByteArray { private fun rsaEncrypt(alias: String, secret: ByteArray): ByteArray {
val privateKeyEntry = getOrGenerateKeyPairForAlias(alias) val privateKeyEntry = getOrGenerateKeyPairForAlias(alias)
@ -480,7 +384,6 @@ internal class SecretStoringUtils @Inject constructor(private val context: Conte
return outputStream.toByteArray() return outputStream.toByteArray()
} }
@RequiresApi(Build.VERSION_CODES.KITKAT)
@Throws(Exception::class) @Throws(Exception::class)
private fun rsaDecrypt(alias: String, encrypted: InputStream): ByteArray { private fun rsaDecrypt(alias: String, encrypted: InputStream): ByteArray {
val privateKeyEntry = getOrGenerateKeyPairForAlias(alias) val privateKeyEntry = getOrGenerateKeyPairForAlias(alias)

View File

@ -17,7 +17,6 @@
package org.matrix.android.sdk.internal.session.widgets package org.matrix.android.sdk.internal.session.widgets
import android.os.Build
import android.webkit.JavascriptInterface import android.webkit.JavascriptInterface
import android.webkit.WebView import android.webkit.WebView
import com.squareup.moshi.Moshi import com.squareup.moshi.Moshi
@ -172,11 +171,7 @@ internal class DefaultWidgetPostAPIMediator @Inject constructor(private val mosh
val functionLine = "sendResponseFromRiotAndroid('" + eventData["_id"] + "' , " + jsString + ");" val functionLine = "sendResponseFromRiotAndroid('" + eventData["_id"] + "' , " + jsString + ");"
Timber.v("BRIDGE sendResponse: $functionLine") Timber.v("BRIDGE sendResponse: $functionLine")
// call the javascript method // call the javascript method
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) { webView?.evaluateJavascript(functionLine, null)
webView?.loadUrl("javascript:$functionLine")
} else {
webView?.evaluateJavascript(functionLine, null)
}
} catch (e: Exception) { } catch (e: Exception) {
Timber.e(e, "## sendResponse() failed ") Timber.e(e, "## sendResponse() failed ")
} }

View File

@ -27,7 +27,6 @@ import android.security.KeyPairGeneratorSpec
import android.security.keystore.KeyGenParameterSpec import android.security.keystore.KeyGenParameterSpec
import android.security.keystore.KeyProperties import android.security.keystore.KeyProperties
import android.util.Base64 import android.util.Base64
import androidx.annotation.RequiresApi
import androidx.core.content.edit import androidx.core.content.edit
import timber.log.Timber import timber.log.Timber
import java.io.IOException import java.io.IOException
@ -48,7 +47,6 @@ import java.security.cert.CertificateException
import java.security.spec.AlgorithmParameterSpec import java.security.spec.AlgorithmParameterSpec
import java.security.spec.RSAKeyGenParameterSpec import java.security.spec.RSAKeyGenParameterSpec
import java.util.Calendar import java.util.Calendar
import java.util.zip.GZIPOutputStream
import javax.crypto.Cipher import javax.crypto.Cipher
import javax.crypto.CipherInputStream import javax.crypto.CipherInputStream
import javax.crypto.CipherOutputStream import javax.crypto.CipherOutputStream
@ -82,22 +80,6 @@ object CompatUtil {
*/ */
private val prng: SecureRandom by lazy(LazyThreadSafetyMode.NONE) { SecureRandom() } private val prng: SecureRandom by lazy(LazyThreadSafetyMode.NONE) { SecureRandom() }
/**
* Create a GZIPOutputStream instance
* Special treatment on KitKat device, force the syncFlush param to false
* Before Kitkat, this param does not exist and after Kitkat it is set to false by default
*
* @param outputStream the output stream
*/
@Throws(IOException::class)
fun createGzipOutputStream(outputStream: OutputStream): GZIPOutputStream {
return if (Build.VERSION.SDK_INT == Build.VERSION_CODES.KITKAT) {
GZIPOutputStream(outputStream, false)
} else {
GZIPOutputStream(outputStream)
}
}
/** /**
* Returns the AES key used for local storage encryption/decryption with AES/GCM. * Returns the AES key used for local storage encryption/decryption with AES/GCM.
* The key is created if it does not exist already in the keystore. * The key is created if it does not exist already in the keystore.
@ -107,7 +89,6 @@ object CompatUtil {
* *
* @param context the context holding the application shared preferences * @param context the context holding the application shared preferences
*/ */
@RequiresApi(Build.VERSION_CODES.KITKAT)
@Synchronized @Synchronized
@Throws(KeyStoreException::class, @Throws(KeyStoreException::class,
CertificateException::class, CertificateException::class,
@ -249,10 +230,6 @@ object CompatUtil {
KeyStoreException::class, KeyStoreException::class,
IllegalBlockSizeException::class) IllegalBlockSizeException::class)
fun createCipherOutputStream(out: OutputStream, context: Context): OutputStream? { fun createCipherOutputStream(out: OutputStream, context: Context): OutputStream? {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) {
return out
}
val keyAndVersion = getAesGcmLocalProtectionKey(context) val keyAndVersion = getAesGcmLocalProtectionKey(context)
val cipher = Cipher.getInstance(AES_GCM_CIPHER_TYPE) val cipher = Cipher.getInstance(AES_GCM_CIPHER_TYPE)
@ -298,10 +275,6 @@ object CompatUtil {
InvalidAlgorithmParameterException::class, InvalidAlgorithmParameterException::class,
IOException::class) IOException::class)
fun createCipherInputStream(`in`: InputStream, context: Context): InputStream? { fun createCipherInputStream(`in`: InputStream, context: Context): InputStream? {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) {
return `in`
}
val iv_len = `in`.read() val iv_len = `in`.read()
if (iv_len != AES_GCM_IV_LENGTH) { if (iv_len != AES_GCM_IV_LENGTH) {
Timber.e(TAG, "Invalid IV length $iv_len") Timber.e(TAG, "Invalid IV length $iv_len")

View File

@ -22,7 +22,6 @@ import android.content.Context
import android.hardware.Camera import android.hardware.Camera
import android.hardware.camera2.CameraCharacteristics import android.hardware.camera2.CameraCharacteristics
import android.hardware.camera2.CameraManager import android.hardware.camera2.CameraManager
import android.os.Build
import androidx.core.content.getSystemService import androidx.core.content.getSystemService
import javax.inject.Inject import javax.inject.Inject
@ -33,10 +32,6 @@ class HardwareInfo @Inject constructor(
* Tell if the device has a back (or external) camera * Tell if the device has a back (or external) camera
*/ */
fun hasBackCamera(): Boolean { fun hasBackCamera(): Boolean {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
return Camera.getNumberOfCameras() > 0
}
val manager = context.getSystemService<CameraManager>() ?: return Camera.getNumberOfCameras() > 0 val manager = context.getSystemService<CameraManager>() ?: return Camera.getNumberOfCameras() > 0
return manager.cameraIdList.any { return manager.cameraIdList.any {

View File

@ -20,7 +20,6 @@ import android.content.ClipData
import android.content.ClipDescription import android.content.ClipDescription
import android.content.Intent import android.content.Intent
import android.net.Uri import android.net.Uri
import android.os.Build
import androidx.core.util.PatternsCompat.WEB_URL import androidx.core.util.PatternsCompat.WEB_URL
/** /**
@ -87,13 +86,9 @@ fun analyseIntent(intent: Intent): List<ExternalIntentData> {
} }
} }
var clipData: ClipData? = null val clipData: ClipData? = intent.clipData
var mimeTypes: List<String>? = null var mimeTypes: List<String>? = null
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
clipData = intent.clipData
}
// multiple data // multiple data
if (null != clipData) { if (null != clipData) {
if (null != clipData.description) { if (null != clipData.description) {

View File

@ -19,7 +19,6 @@ package im.vector.app.core.ui.views
import android.content.Context import android.content.Context
import android.content.res.ColorStateList import android.content.res.ColorStateList
import android.graphics.drawable.Drawable import android.graphics.drawable.Drawable
import android.os.Build
import android.util.AttributeSet import android.util.AttributeSet
import android.view.View import android.view.View
import android.widget.FrameLayout import android.widget.FrameLayout
@ -105,13 +104,7 @@ class BottomSheetActionButton @JvmOverloads constructor(
var tint: Int? = null var tint: Int? = null
set(value) { set(value) {
field = value field = value
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { leftIconImageView.imageTintList = value?.let { ColorStateList.valueOf(value) }
leftIconImageView.imageTintList = value?.let { ColorStateList.valueOf(value) }
} else {
leftIcon?.let {
leftIcon = ThemeUtils.tintDrawable(context, it, value ?: ThemeUtils.getColor(context, android.R.attr.textColor))
}
}
} }
init { init {

View File

@ -134,9 +134,7 @@ fun openFileSelection(activity: Activity,
allowMultipleSelection: Boolean, allowMultipleSelection: Boolean,
requestCode: Int) { requestCode: Int) {
val fileIntent = Intent(Intent.ACTION_GET_CONTENT) val fileIntent = Intent(Intent.ACTION_GET_CONTENT)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) { fileIntent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, allowMultipleSelection)
fileIntent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, allowMultipleSelection)
}
fileIntent.addCategory(Intent.CATEGORY_OPENABLE) fileIntent.addCategory(Intent.CATEGORY_OPENABLE)
fileIntent.type = "*/*" fileIntent.type = "*/*"

View File

@ -101,15 +101,10 @@ fun startNotificationSettingsIntent(activity: AppCompatActivity, requestCode: In
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
intent.action = Settings.ACTION_APP_NOTIFICATION_SETTINGS intent.action = Settings.ACTION_APP_NOTIFICATION_SETTINGS
intent.putExtra(Settings.EXTRA_APP_PACKAGE, activity.packageName) intent.putExtra(Settings.EXTRA_APP_PACKAGE, activity.packageName)
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { } else {
intent.action = Settings.ACTION_APP_NOTIFICATION_SETTINGS intent.action = Settings.ACTION_APP_NOTIFICATION_SETTINGS
intent.putExtra("app_package", activity.packageName) intent.putExtra("app_package", activity.packageName)
intent.putExtra("app_uid", activity.applicationInfo?.uid) intent.putExtra("app_uid", activity.applicationInfo?.uid)
} else {
intent.action = Settings.ACTION_APPLICATION_DETAILS_SETTINGS
intent.addCategory(Intent.CATEGORY_DEFAULT)
val uri = Uri.fromParts("package", activity.packageName, null)
intent.data = uri
} }
activity.startActivityForResult(intent, requestCode) activity.startActivityForResult(intent, requestCode)
} }
@ -140,11 +135,7 @@ fun startAddGoogleAccountIntent(context: AppCompatActivity, requestCode: Int) {
fun startSharePlainTextIntent(fragment: Fragment, chooserTitle: String?, text: String, subject: String? = null, requestCode: Int? = null) { fun startSharePlainTextIntent(fragment: Fragment, chooserTitle: String?, text: String, subject: String? = null, requestCode: Int? = null) {
val share = Intent(Intent.ACTION_SEND) val share = Intent(Intent.ACTION_SEND)
share.type = "text/plain" share.type = "text/plain"
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { share.addFlags(Intent.FLAG_ACTIVITY_NEW_DOCUMENT)
share.addFlags(Intent.FLAG_ACTIVITY_NEW_DOCUMENT)
} else {
share.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK or Intent.FLAG_ACTIVITY_NEW_TASK)
}
// Add data to the intent, the receiving app will decide what to do with it. // Add data to the intent, the receiving app will decide what to do with it.
share.putExtra(Intent.EXTRA_SUBJECT, subject) share.putExtra(Intent.EXTRA_SUBJECT, subject)
share.putExtra(Intent.EXTRA_TEXT, text) share.putExtra(Intent.EXTRA_TEXT, text)

View File

@ -18,10 +18,8 @@ package im.vector.app.features.attachments
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.graphics.drawable.BitmapDrawable import android.graphics.drawable.BitmapDrawable
import android.os.Build
import android.util.Pair import android.util.Pair
import android.view.Gravity import android.view.Gravity
import android.view.LayoutInflater import android.view.LayoutInflater
@ -109,25 +107,19 @@ class AttachmentTypeSelectorView(context: Context,
showAtLocation(anchor, Gravity.NO_GRAVITY, 0, anchorCoordinates[1] - contentViewHeight) showAtLocation(anchor, Gravity.NO_GRAVITY, 0, anchorCoordinates[1] - contentViewHeight)
} }
contentView.doOnNextLayout { contentView.doOnNextLayout {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { animateWindowInCircular(anchor, contentView)
animateWindowInCircular(anchor, contentView)
} else {
animateWindowInTranslate(contentView)
}
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
animateButtonIn(galleryButton, ANIMATION_DURATION / 2)
animateButtonIn(cameraButton, ANIMATION_DURATION / 2)
animateButtonIn(fileButton, ANIMATION_DURATION / 4)
animateButtonIn(audioButton, ANIMATION_DURATION / 2)
animateButtonIn(contactButton, ANIMATION_DURATION / 4)
animateButtonIn(stickersButton, 0)
} }
animateButtonIn(galleryButton, ANIMATION_DURATION / 2)
animateButtonIn(cameraButton, ANIMATION_DURATION / 2)
animateButtonIn(fileButton, ANIMATION_DURATION / 4)
animateButtonIn(audioButton, ANIMATION_DURATION / 2)
animateButtonIn(contactButton, ANIMATION_DURATION / 4)
animateButtonIn(stickersButton, 0)
} }
override fun dismiss() { override fun dismiss() {
val capturedAnchor = anchor val capturedAnchor = anchor
if (capturedAnchor != null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { if (capturedAnchor != null) {
animateWindowOutCircular(capturedAnchor, contentView) animateWindowOutCircular(capturedAnchor, contentView)
} else { } else {
animateWindowOutTranslate(contentView) animateWindowOutTranslate(contentView)
@ -144,7 +136,6 @@ class AttachmentTypeSelectorView(context: Context,
button.startAnimation(animation) button.startAnimation(animation)
} }
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
private fun animateWindowInCircular(anchor: View, contentView: View) { private fun animateWindowInCircular(anchor: View, contentView: View) {
val coordinates = getClickCoordinates(anchor, contentView) val coordinates = getClickCoordinates(anchor, contentView)
val animator = ViewAnimationUtils.createCircularReveal(contentView, val animator = ViewAnimationUtils.createCircularReveal(contentView,
@ -162,7 +153,6 @@ class AttachmentTypeSelectorView(context: Context,
getContentView().startAnimation(animation) getContentView().startAnimation(animation)
} }
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
private fun animateWindowOutCircular(anchor: View, contentView: View) { private fun animateWindowOutCircular(anchor: View, contentView: View) {
val coordinates = getClickCoordinates(anchor, contentView) val coordinates = getClickCoordinates(anchor, contentView)
val animator = ViewAnimationUtils.createCircularReveal(getContentView(), val animator = ViewAnimationUtils.createCircularReveal(getContentView(),

View File

@ -23,7 +23,6 @@ import android.content.Intent.FLAG_ACTIVITY_CLEAR_TOP
import android.os.Build import android.os.Build
import android.os.Bundle import android.os.Bundle
import android.os.Parcelable import android.os.Parcelable
import android.view.KeyEvent
import android.view.View import android.view.View
import android.view.Window import android.view.Window
import android.view.WindowManager import android.view.WindowManager
@ -334,20 +333,6 @@ class VectorCallActivity : VectorBaseActivity(), CallControlsView.InteractionLis
surfaceRenderersAreInitialized = true surfaceRenderersAreInitialized = true
} }
override fun onKeyDown(keyCode: Int, event: KeyEvent?): Boolean {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
// for newer version, it will be passed automatically to active media session
// in call service
when (keyCode) {
KeyEvent.KEYCODE_HEADSETHOOK -> {
callViewModel.handle(VectorCallViewActions.HeadSetButtonPressed)
return true
}
}
}
return super.onKeyDown(keyCode, event)
}
private fun handleViewEvents(event: VectorCallViewEvents?) { private fun handleViewEvents(event: VectorCallViewEvents?) {
Timber.v("## VOIP handleViewEvents $event") Timber.v("## VOIP handleViewEvents $event")
when (event) { when (event) {

View File

@ -18,8 +18,6 @@ package im.vector.app.features.call
import android.content.Context import android.content.Context
import android.hardware.camera2.CameraManager import android.hardware.camera2.CameraManager
import android.os.Build
import androidx.annotation.RequiresApi
import androidx.core.content.getSystemService import androidx.core.content.getSystemService
import im.vector.app.ActiveSessionDataSource import im.vector.app.ActiveSessionDataSource
import im.vector.app.core.services.BluetoothHeadsetReceiver import im.vector.app.core.services.BluetoothHeadsetReceiver
@ -363,11 +361,6 @@ class WebRtcPeerConnectionManager @Inject constructor(
} }
} }
else -> { else -> {
// Fallback for old android, try to restart capture when attached
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP && capturerIsInError && call.mxCall.isVideoCall) {
// try to restart capture?
videoCapturer?.startCapture(currentCaptureMode.width, currentCaptureMode.height, currentCaptureMode.fps)
}
// sink existing tracks (configuration change, e.g screen rotation) // sink existing tracks (configuration change, e.g screen rotation)
attachViewRenderersInternal() attachViewRenderersInternal()
} }
@ -478,12 +471,10 @@ class WebRtcPeerConnectionManager @Inject constructor(
// We then register in order to restart capture as soon as the camera is available again // We then register in order to restart capture as soon as the camera is available again
Timber.v("## VOIP onCameraClosed") Timber.v("## VOIP onCameraClosed")
this@WebRtcPeerConnectionManager.capturerIsInError = true this@WebRtcPeerConnectionManager.capturerIsInError = true
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { val restarter = CameraRestarter(cameraInUse?.name ?: "", callContext.mxCall.callId)
val restarter = CameraRestarter(cameraInUse?.name ?: "", callContext.mxCall.callId) callContext.cameraAvailabilityCallback = restarter
callContext.cameraAvailabilityCallback = restarter val cameraManager = context.getSystemService<CameraManager>()!!
val cameraManager = context.getSystemService<CameraManager>()!! cameraManager.registerAvailabilityCallback(restarter, null)
cameraManager.registerAvailabilityCallback(restarter, null)
}
} }
}) })
@ -792,10 +783,8 @@ class WebRtcPeerConnectionManager @Inject constructor(
currentCall?.localVideoTrack?.setEnabled(false) currentCall?.localVideoTrack?.setEnabled(false)
currentCall?.cameraAvailabilityCallback?.let { cameraAvailabilityCallback -> currentCall?.cameraAvailabilityCallback?.let { cameraAvailabilityCallback ->
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { val cameraManager = context.getSystemService<CameraManager>()!!
val cameraManager = context.getSystemService<CameraManager>()!! cameraManager.unregisterAvailabilityCallback(cameraAvailabilityCallback)
cameraManager.unregisterAvailabilityCallback(cameraAvailabilityCallback)
}
} }
if (originatedByMe) { if (originatedByMe) {
@ -1041,7 +1030,6 @@ class WebRtcPeerConnectionManager @Inject constructor(
} }
} }
@RequiresApi(Build.VERSION_CODES.LOLLIPOP)
inner class CameraRestarter(val cameraId: String, val callId: String) : CameraManager.AvailabilityCallback() { inner class CameraRestarter(val cameraId: String, val callId: String) : CameraManager.AvailabilityCallback() {
override fun onCameraAvailable(cameraId: String) { override fun onCameraAvailable(cameraId: String) {

View File

@ -82,9 +82,7 @@ class VectorConfiguration @Inject constructor(private val context: Context) {
} else { } else {
@Suppress("DEPRECATION") @Suppress("DEPRECATION")
configuration.locale = locale configuration.locale = locale
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { configuration.setLayoutDirection(locale)
configuration.setLayoutDirection(locale)
}
@Suppress("DEPRECATION") @Suppress("DEPRECATION")
resources.updateConfiguration(configuration, resources.displayMetrics) resources.updateConfiguration(configuration, resources.displayMetrics)
return context return context

View File

@ -16,7 +16,6 @@
package im.vector.app.features.login package im.vector.app.features.login
import android.os.Build
import android.os.Bundle import android.os.Bundle
import android.view.View import android.view.View
import androidx.annotation.CallSuper import androidx.annotation.CallSuper
@ -48,9 +47,7 @@ abstract class AbstractLoginFragment : VectorBaseFragment(), OnBackPressed {
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { sharedElementEnterTransition = TransitionInflater.from(context).inflateTransition(android.R.transition.move)
sharedElementEnterTransition = TransitionInflater.from(context).inflateTransition(android.R.transition.move)
}
} }
@CallSuper @CallSuper

View File

@ -22,7 +22,6 @@ import android.annotation.SuppressLint
import android.content.DialogInterface import android.content.DialogInterface
import android.graphics.Bitmap import android.graphics.Bitmap
import android.net.http.SslError import android.net.http.SslError
import android.os.Build
import android.os.Bundle import android.os.Bundle
import android.view.KeyEvent import android.view.KeyEvent
import android.view.View import android.view.View
@ -102,14 +101,6 @@ class LoginWebFragment @Inject constructor(
launchWebView(state) launchWebView(state)
} else { } else {
if (!cookieManager.hasCookies()) { if (!cookieManager.hasCookies()) {
launchWebView(state)
} else if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
try {
cookieManager.removeAllCookie()
} catch (e: Exception) {
Timber.e(e, " cookieManager.removeAllCookie() fails")
}
launchWebView(state) launchWebView(state)
} else { } else {
try { try {

View File

@ -19,12 +19,10 @@ package im.vector.app.features.media
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.graphics.drawable.Drawable import android.graphics.drawable.Drawable
import android.os.Build
import android.os.Bundle import android.os.Bundle
import android.view.MenuItem import android.view.MenuItem
import android.view.View import android.view.View
import android.view.ViewTreeObserver import android.view.ViewTreeObserver
import androidx.annotation.RequiresApi
import androidx.appcompat.widget.Toolbar import androidx.appcompat.widget.Toolbar
import androidx.core.net.toUri import androidx.core.net.toUri
import androidx.core.transition.addListener import androidx.core.transition.addListener
@ -84,7 +82,7 @@ class ImageMediaViewerActivity : VectorBaseActivity() {
configureToolbar(imageMediaViewerToolbar, mediaData) configureToolbar(imageMediaViewerToolbar, mediaData)
if (isFirstCreation() && Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && addTransitionListener()) { if (isFirstCreation() && addTransitionListener()) {
// Encrypted image // Encrypted image
imageTransitionView.isVisible = true imageTransitionView.isVisible = true
imageMediaViewerImageView.isVisible = false imageMediaViewerImageView.isVisible = false
@ -183,7 +181,6 @@ class ImageMediaViewerActivity : VectorBaseActivity() {
* *
* @return true if we were successful in adding a listener to the enter transition * @return true if we were successful in adding a listener to the enter transition
*/ */
@RequiresApi(Build.VERSION_CODES.LOLLIPOP)
private fun addTransitionListener(): Boolean { private fun addTransitionListener(): Boolean {
val transition = window.sharedElementEnterTransition val transition = window.sharedElementEnterTransition

View File

@ -20,6 +20,7 @@ import android.content.Context
import android.graphics.Canvas import android.graphics.Canvas
import android.graphics.Color import android.graphics.Color
import android.graphics.Typeface import android.graphics.Typeface
import android.os.Trace
import android.text.StaticLayout import android.text.StaticLayout
import android.text.TextPaint import android.text.TextPaint
import android.util.AttributeSet import android.util.AttributeSet
@ -43,7 +44,7 @@ class EmojiDrawView @JvmOverloads constructor(
var emoji: String? = null var emoji: String? = null
override fun onDraw(canvas: Canvas?) { override fun onDraw(canvas: Canvas?) {
EmojiRecyclerAdapter.beginTraceSession("EmojiDrawView.onDraw") Trace.beginSection("EmojiDrawView.onDraw")
super.onDraw(canvas) super.onDraw(canvas)
canvas?.save() canvas?.save()
val space = abs((width - emojiSize) / 2f) val space = abs((width - emojiSize) / 2f)
@ -52,7 +53,7 @@ class EmojiDrawView @JvmOverloads constructor(
mLayout!!.draw(canvas) mLayout!!.draw(canvas)
} }
canvas?.restore() canvas?.restore()
EmojiRecyclerAdapter.endTraceSession() Trace.endSection()
} }
companion object { companion object {

View File

@ -125,7 +125,7 @@ class EmojiRecyclerAdapter @Inject constructor(
} }
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
beginTraceSession("MyAdapter.onCreateViewHolder") Trace.beginSection("MyAdapter.onCreateViewHolder")
val inflater = LayoutInflater.from(parent.context) val inflater = LayoutInflater.from(parent.context)
val itemView = inflater.inflate(viewType, parent, false) val itemView = inflater.inflate(viewType, parent, false)
itemView.setOnClickListener(itemClickListener) itemView.setOnClickListener(itemClickListener)
@ -133,16 +133,16 @@ class EmojiRecyclerAdapter @Inject constructor(
R.layout.grid_section_header -> SectionViewHolder(itemView) R.layout.grid_section_header -> SectionViewHolder(itemView)
else -> EmojiViewHolder(itemView) else -> EmojiViewHolder(itemView)
} }
endTraceSession() Trace.endSection()
return viewHolder return viewHolder
} }
override fun getItemViewType(position: Int): Int { override fun getItemViewType(position: Int): Int {
beginTraceSession("MyAdapter.getItemViewType") Trace.beginSection("MyAdapter.getItemViewType")
if (isSection(position)) { if (isSection(position)) {
return R.layout.grid_section_header return R.layout.grid_section_header
} }
endTraceSession() Trace.endSection()
return R.layout.grid_item_emoji return R.layout.grid_item_emoji
} }
@ -183,7 +183,7 @@ class EmojiRecyclerAdapter @Inject constructor(
} }
override fun onBindViewHolder(holder: ViewHolder, position: Int) { override fun onBindViewHolder(holder: ViewHolder, position: Int) {
beginTraceSession("MyAdapter.onBindViewHolder") Trace.beginSection("MyAdapter.onBindViewHolder")
val sectionNumber = getSectionForAbsoluteIndex(position) val sectionNumber = getSectionForAbsoluteIndex(position)
if (isSection(position)) { if (isSection(position)) {
holder.bind(dataSource.rawData.categories[sectionNumber].name) holder.bind(dataSource.rawData.categories[sectionNumber].name)
@ -202,7 +202,7 @@ class EmojiRecyclerAdapter @Inject constructor(
holder.bind(null) holder.bind(null)
} }
} }
endTraceSession() Trace.endSection()
} }
override fun onViewRecycled(holder: ViewHolder) { override fun onViewRecycled(holder: ViewHolder) {
@ -259,18 +259,6 @@ class EmojiRecyclerAdapter @Inject constructor(
} }
companion object { companion object {
fun endTraceSession() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
Trace.endSection()
}
}
fun beginTraceSession(sectionName: String) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
Trace.beginSection(sectionName)
}
}
private val staticLayoutCache = HashMap<String, StaticLayout>() private val staticLayoutCache = HashMap<String, StaticLayout>()
private fun getStaticLayoutForEmoji(emoji: String): StaticLayout { private fun getStaticLayoutForEmoji(emoji: String): StaticLayout {

View File

@ -18,7 +18,6 @@ package im.vector.app.features.settings
import android.content.Context import android.content.Context
import android.content.res.Configuration import android.content.res.Configuration
import android.os.Build
import androidx.core.content.edit import androidx.core.content.edit
import androidx.preference.PreferenceManager import androidx.preference.PreferenceManager
import im.vector.app.BuildConfig import im.vector.app.BuildConfig
@ -108,13 +107,11 @@ object VectorLocale {
putString(APPLICATION_LOCALE_VARIANT_KEY, variant) putString(APPLICATION_LOCALE_VARIANT_KEY, variant)
} }
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { val script = locale.script
val script = locale.script if (script.isEmpty()) {
if (script.isEmpty()) { remove(APPLICATION_LOCALE_SCRIPT_KEY)
remove(APPLICATION_LOCALE_SCRIPT_KEY) } else {
} else { putString(APPLICATION_LOCALE_SCRIPT_KEY, script)
putString(APPLICATION_LOCALE_SCRIPT_KEY, script)
}
} }
} }
} }
@ -128,40 +125,15 @@ object VectorLocale {
* @return the localized string * @return the localized string
*/ */
private fun getString(context: Context, locale: Locale, resourceId: Int): String { private fun getString(context: Context, locale: Locale, resourceId: Int): String {
val result: String val config = Configuration(context.resources.configuration)
config.setLocale(locale)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { return try {
val config = Configuration(context.resources.configuration) context.createConfigurationContext(config).getText(resourceId).toString()
config.setLocale(locale) } catch (e: Exception) {
result = try { Timber.e(e, "## getString() failed")
context.createConfigurationContext(config).getText(resourceId).toString() // use the default one
} catch (e: Exception) { context.getString(resourceId)
Timber.e(e, "## getString() failed")
// use the default one
context.getString(resourceId)
}
} else {
val resources = context.resources
val conf = resources.configuration
@Suppress("DEPRECATION")
val savedLocale = conf.locale
@Suppress("DEPRECATION")
conf.locale = locale
@Suppress("DEPRECATION")
resources.updateConfiguration(conf, null)
// retrieve resources from desired locale
result = resources.getString(resourceId)
// restore original locale
@Suppress("DEPRECATION")
conf.locale = savedLocale
@Suppress("DEPRECATION")
resources.updateConfiguration(conf, null)
} }
return result
} }
/** /**
@ -194,22 +166,18 @@ object VectorLocale {
} }
val list = knownLocalesSet.mapNotNull { (language, country, script) -> val list = knownLocalesSet.mapNotNull { (language, country, script) ->
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { try {
try { Locale.Builder()
Locale.Builder() .setLanguage(language)
.setLanguage(language) .setRegion(country)
.setRegion(country) .setScript(script)
.setScript(script) .build()
.build() } catch (exception: IllformedLocaleException) {
} catch (exception: IllformedLocaleException) { if (BuildConfig.DEBUG) {
if (BuildConfig.DEBUG) { throw exception
throw exception
}
// Ignore this locale in production
null
} }
} else { // Ignore this locale in production
Locale(language, country) null
} }
} }
// sort by human display names // sort by human display names
@ -229,9 +197,7 @@ object VectorLocale {
return buildString { return buildString {
append(locale.getDisplayLanguage(locale)) append(locale.getDisplayLanguage(locale))
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP if (locale.script != ISO_15924_LATN && locale.getDisplayScript(locale).isNotEmpty()) {
&& locale.script != ISO_15924_LATN
&& locale.getDisplayScript(locale).isNotEmpty()) {
append(" - ") append(" - ")
append(locale.getDisplayScript(locale)) append(locale.getDisplayScript(locale))
} }
@ -254,7 +220,7 @@ object VectorLocale {
return buildString { return buildString {
append("[") append("[")
append(locale.displayLanguage) append(locale.displayLanguage)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && locale.script != ISO_15924_LATN) { if (locale.script != ISO_15924_LATN) {
append(" - ") append(" - ")
append(locale.displayScript) append(locale.displayScript)
} }

View File

@ -15,7 +15,6 @@
*/ */
package im.vector.app.features.settings.troubleshoot package im.vector.app.features.settings.troubleshoot
import android.os.Build
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
@ -87,9 +86,7 @@ class NotificationTroubleshootRecyclerViewAdapter(val tests: ArrayList<Troublesh
statusIconImage.visibility = View.VISIBLE statusIconImage.visibility = View.VISIBLE
statusIconImage.setImageResource(R.drawable.unit_test_ko) statusIconImage.setImageResource(R.drawable.unit_test_ko)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { statusIconImage.imageTintList = null
statusIconImage.imageTintList = null
}
descriptionText.setTextColor(ContextCompat.getColor(context, R.color.riotx_notice)) descriptionText.setTextColor(ContextCompat.getColor(context, R.color.riotx_notice))
} }

View File

@ -18,7 +18,6 @@ package im.vector.app.features.webview
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.os.Build
import android.webkit.WebChromeClient import android.webkit.WebChromeClient
import android.webkit.WebView import android.webkit.WebView
import androidx.annotation.CallSuper import androidx.annotation.CallSuper
@ -70,10 +69,8 @@ class VectorWebViewActivity : VectorBaseActivity() {
displayZoomControls = false displayZoomControls = false
} }
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { val cookieManager = android.webkit.CookieManager.getInstance()
val cookieManager = android.webkit.CookieManager.getInstance() cookieManager.setAcceptThirdPartyCookies(simple_webview, true)
cookieManager.setAcceptThirdPartyCookies(simple_webview, true)
}
val url = intent.extras?.getString(EXTRA_URL) val url = intent.extras?.getString(EXTRA_URL)
val title = intent.extras?.getString(EXTRA_TITLE, USE_TITLE_FROM_WEB_PAGE) val title = intent.extras?.getString(EXTRA_TITLE, USE_TITLE_FROM_WEB_PAGE)

View File

@ -26,7 +26,6 @@ import android.webkit.WebResourceRequest
import android.webkit.WebResourceResponse import android.webkit.WebResourceResponse
import android.webkit.WebView import android.webkit.WebView
import android.webkit.WebViewClient import android.webkit.WebViewClient
import androidx.annotation.RequiresApi
/** /**
* This class inherits from WebViewClient. It has to be used with a WebView. * This class inherits from WebViewClient. It has to be used with a WebView.
@ -58,7 +57,6 @@ class VectorWebViewClient(private val eventListener: WebViewEventListener) : Web
} }
} }
@RequiresApi(Build.VERSION_CODES.LOLLIPOP)
override fun onReceivedHttpError(view: WebView, request: WebResourceRequest, errorResponse: WebResourceResponse) { override fun onReceivedHttpError(view: WebView, request: WebResourceRequest, errorResponse: WebResourceResponse) {
super.onReceivedHttpError(view, request, errorResponse) super.onReceivedHttpError(view, request, errorResponse)
eventListener.onHttpError(request.url.toString(), eventListener.onHttpError(request.url.toString(),

View File

@ -16,7 +16,6 @@
package im.vector.app.features.widgets.permissions package im.vector.app.features.widgets.permissions
import android.content.DialogInterface import android.content.DialogInterface
import android.os.Build
import android.text.Spannable import android.text.Spannable
import android.text.SpannableStringBuilder import android.text.SpannableStringBuilder
import android.text.style.BulletSpan import android.text.style.BulletSpan
@ -71,18 +70,7 @@ class RoomWidgetPermissionBottomSheet : VectorBaseBottomSheetDialogFragment() {
permissionData.permissionsList.forEach { permissionData.permissionsList.forEach {
infoBuilder.append("\n") infoBuilder.append("\n")
val bulletPoint = getString(it) val bulletPoint = getString(it)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { infoBuilder.append(bulletPoint, BulletSpan(resources.getDimension(R.dimen.quote_gap).toInt()), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
infoBuilder.append(bulletPoint, BulletSpan(resources.getDimension(R.dimen.quote_gap).toInt()), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
} else {
val start = infoBuilder.length
infoBuilder.append(bulletPoint)
infoBuilder.setSpan(
BulletSpan(resources.getDimension(R.dimen.quote_gap).toInt()),
start,
bulletPoint.length,
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE
)
}
} }
infoBuilder.append("\n") infoBuilder.append("\n")
widgetPermissionSharedInfo.text = infoBuilder widgetPermissionSharedInfo.text = infoBuilder

View File

@ -17,7 +17,6 @@
package im.vector.app.features.widgets.webview package im.vector.app.features.widgets.webview
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.os.Build
import android.view.ViewGroup import android.view.ViewGroup
import android.webkit.CookieManager import android.webkit.CookieManager
import android.webkit.PermissionRequest import android.webkit.PermissionRequest
@ -68,10 +67,8 @@ fun WebView.setupForWidget(webViewEventListener: WebViewEventListener) {
} }
webViewClient = VectorWebViewClient(webViewEventListener) webViewClient = VectorWebViewClient(webViewEventListener)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { val cookieManager = CookieManager.getInstance()
val cookieManager = CookieManager.getInstance() cookieManager.setAcceptThirdPartyCookies(this, false)
cookieManager.setAcceptThirdPartyCookies(this, false)
}
} }
fun WebView.clearAfterWidget() { fun WebView.clearAfterWidget() {

View File

@ -1,6 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- End and Start attribute has to be set here -->
</resources>

View File

@ -1,10 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<dimen name="floating_action_button_margin">16dp</dimen>
<!-- Improve size (+20dp) to take status bar height into account -->
<dimen name="navigation_view_height">196dp</dimen>
<dimen name="navigation_avatar_top_margin">44dp</dimen>
</resources>

View File

@ -1,5 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
</resources>

View File

@ -1,18 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="AppTheme.Black.v21" parent="AppTheme.Base.Black">
<item name="android:statusBarColor">@color/riotx_header_panel_background_black</item>
<item name="android:navigationBarColor">@color/riotx_header_panel_background_black</item>
<!-- enable window content transitions -->
<item name="android:windowContentTransitions">true</item>
<!-- specify shared element enter and exit transitions -->
<item name="android:windowSharedElementEnterTransition">@transition/image_preview_transition</item>
<item name="android:windowSharedElementExitTransition">@transition/image_preview_transition</item>
</style>
<style name="AppTheme.Black" parent="AppTheme.Black.v21" />
</resources>

View File

@ -1,16 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="AppTheme.Launcher.v21" parent="AppTheme.Launcher.Base">
<item name="android:statusBarColor">@color/riotx_accent</item>
<item name="android:navigationBarColor">@color/riotx_accent</item>
</style>
<style name="AppTheme.Launcher" parent="AppTheme.Launcher.v21"/>
<style name="AppTheme.AttachmentsPreview" parent="AppTheme.Base.Black">
<item name="android:statusBarColor">@android:color/transparent</item>
<item name="android:navigationBarColor">@android:color/transparent</item>
</style>
</resources>

View File

@ -1,18 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="AppTheme.Dark.v21" parent="AppTheme.Base.Dark">
<item name="android:statusBarColor">@color/riotx_header_panel_background_dark</item>
<item name="android:navigationBarColor">@color/riotx_header_panel_background_dark</item>
<!-- enable window content transitions -->
<item name="android:windowContentTransitions">true</item>
<!-- specify shared element enter and exit transitions -->
<item name="android:windowSharedElementEnterTransition">@transition/image_preview_transition</item>
<item name="android:windowSharedElementExitTransition">@transition/image_preview_transition</item>
</style>
<style name="AppTheme.Dark" parent="AppTheme.Dark.v21" />
</resources>

View File

@ -1,20 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="AppTheme.Light.v21" parent="AppTheme.Base.Light">
<!-- Use dark color, to have enough contrast with icons color. windowLightStatusBar is only available in API 23+ -->
<item name="android:statusBarColor">@color/riotx_header_panel_background_dark</item>
<!-- Use dark color, to have enough contrast with icons color. windowLightNavigationBar is only available in API 27+ -->
<item name="android:navigationBarColor">@color/riotx_header_panel_background_dark</item>
<!-- enable window content transitions -->
<item name="android:windowContentTransitions">true</item>
<!-- specify shared element enter and exit transitions -->
<item name="android:windowSharedElementEnterTransition">@transition/image_preview_transition</item>
<item name="android:windowSharedElementExitTransition">@transition/image_preview_transition</item>
</style>
<style name="AppTheme.Light" parent="AppTheme.Light.v21"/>
</resources>

View File

@ -1,20 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="AppTheme.Status.v21" parent="AppTheme.Base.Status">
<!-- Use dark color, to have enough contrast with icons color. windowLightStatusBar is only available in API 23+ -->
<item name="android:statusBarColor">@color/riotx_header_panel_background_dark</item>
<!-- Use dark color, to have enough contrast with icons color. windowLightNavigationBar is only available in API 27+ -->
<item name="android:navigationBarColor">@color/riotx_header_panel_background_dark</item>
<!-- enable window content transitions -->
<item name="android:windowContentTransitions">true</item>
<!-- specify shared element enter and exit transitions -->
<item name="android:windowSharedElementEnterTransition">@transition/image_preview_transition</item>
<item name="android:windowSharedElementExitTransition">@transition/image_preview_transition</item>
</style>
<style name="AppTheme.Status" parent="AppTheme.Status.v21" />
</resources>

View File

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<resources> <resources>
<style name="AppTheme.Black.v23" parent="AppTheme.Black.v21"> <style name="AppTheme.Black.v23" parent="AppTheme.Base.Black">
<item name="android:windowLightStatusBar">false</item> <item name="android:windowLightStatusBar">false</item>
</style> </style>

View File

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<resources> <resources>
<style name="AppTheme.Dark.v23" parent="AppTheme.Dark.v21"> <style name="AppTheme.Dark.v23" parent="AppTheme.Base.Dark">
<item name="android:windowLightStatusBar">false</item> <item name="android:windowLightStatusBar">false</item>
</style> </style>

View File

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<resources> <resources>
<style name="AppTheme.Light.v23" parent="AppTheme.Light.v21"> <style name="AppTheme.Light.v23" parent="AppTheme.Base.Light">
<item name="android:statusBarColor">@color/riotx_header_panel_background_light</item> <item name="android:statusBarColor">@color/riotx_header_panel_background_light</item>
<item name="android:windowLightStatusBar">true</item> <item name="android:windowLightStatusBar">true</item>
</style> </style>

View File

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<resources> <resources>
<style name="AppTheme.Status.v23" parent="AppTheme.Status.v21"> <style name="AppTheme.Status.v23" parent="AppTheme.Base.Status">
<item name="android:statusBarColor">@color/riotx_header_panel_background_light</item> <item name="android:statusBarColor">@color/riotx_header_panel_background_light</item>
<item name="android:windowLightStatusBar">true</item> <item name="android:windowLightStatusBar">true</item>
</style> </style>

View File

@ -9,9 +9,9 @@
<dimen name="layout_vertical_margin_big">32dp</dimen> <dimen name="layout_vertical_margin_big">32dp</dimen>
<dimen name="profile_avatar_size">50dp</dimen> <dimen name="profile_avatar_size">50dp</dimen>
<dimen name="floating_action_button_margin">0dp</dimen> <dimen name="floating_action_button_margin">16dp</dimen>
<dimen name="navigation_view_height">172dp</dimen> <dimen name="navigation_view_height">196dp</dimen>
<dimen name="navigation_avatar_top_margin">20dp</dimen> <dimen name="navigation_avatar_top_margin">44dp</dimen>
<dimen name="item_decoration_left_margin">72dp</dimen> <dimen name="item_decoration_left_margin">72dp</dimen>
<dimen name="chat_avatar_size">40dp</dimen> <dimen name="chat_avatar_size">40dp</dimen>

View File

@ -1,7 +1,6 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<resources> <resources>
<!-- On post Lollipop use default background drawable instead of color -->
<style name="Vector.PopupMenu" parent="Vector.PopupMenuBase" /> <style name="Vector.PopupMenu" parent="Vector.PopupMenuBase" />
</resources> </resources>

View File

@ -63,14 +63,6 @@
<item name="android:dropDownVerticalOffset">0dp</item> <item name="android:dropDownVerticalOffset">0dp</item>
</style> </style>
<style name="Vector.PopupMenu" parent="Vector.PopupMenuBase">
<!--
Before Lollipop the popup background is white on dark theme, so force color here.
(v21 will revert back to default drawable)
-->
<item name="android:popupBackground">?colorBackgroundFloating</item>
</style>
<!-- actionbar icons color --> <!-- actionbar icons color -->
<style name="Vector.ActionBarTheme" parent="ThemeOverlay.MaterialComponents.ActionBar"> <style name="Vector.ActionBarTheme" parent="ThemeOverlay.MaterialComponents.ActionBar">
<item name="colorControlNormal">@android:color/white</item> <item name="colorControlNormal">@android:color/white</item>

View File

@ -81,6 +81,15 @@
<item name="riotx_bottom_nav_background_color">@color/riotx_bottom_nav_background_color_black</item> <item name="riotx_bottom_nav_background_color">@color/riotx_bottom_nav_background_color_black</item>
<item name="riotx_bottom_nav_background_border_color">@color/riotx_bottom_nav_background_border_color_black</item> <item name="riotx_bottom_nav_background_border_color">@color/riotx_bottom_nav_background_border_color_black</item>
<item name="android:statusBarColor">@color/riotx_header_panel_background_black</item>
<item name="android:navigationBarColor">@color/riotx_header_panel_background_black</item>
<!-- enable window content transitions -->
<item name="android:windowContentTransitions">true</item>
<!-- specify shared element enter and exit transitions -->
<item name="android:windowSharedElementEnterTransition">@transition/image_preview_transition</item>
<item name="android:windowSharedElementExitTransition">@transition/image_preview_transition</item>
</style> </style>
<style name="AppTheme.Black" parent="AppTheme.Base.Black" /> <style name="AppTheme.Black" parent="AppTheme.Base.Black" />

View File

@ -6,11 +6,16 @@
<style name="AppTheme.Launcher.Base" parent="Theme.MaterialComponents.Light.NoActionBar"> <style name="AppTheme.Launcher.Base" parent="Theme.MaterialComponents.Light.NoActionBar">
<item name="android:windowBackground">@drawable/splash</item> <item name="android:windowBackground">@drawable/splash</item>
<item name="android:statusBarColor">@color/riotx_accent</item>
<item name="android:navigationBarColor">@color/riotx_accent</item>
<item name="colorPrimaryDark">@color/primary_color_dark</item> <item name="colorPrimaryDark">@color/primary_color_dark</item>
</style> </style>
<style name="AppTheme.AttachmentsPreview" parent="AppTheme.Base.Black"/> <style name="AppTheme.AttachmentsPreview" parent="AppTheme.Base.Black">
<item name="android:statusBarColor">@android:color/transparent</item>
<item name="android:navigationBarColor">@android:color/transparent</item>
</style>
<style name="AppTheme.Transparent" parent="AppTheme.Base.Black"> <style name="AppTheme.Transparent" parent="AppTheme.Base.Black">
<item name="windowActionBar">false</item> <item name="windowActionBar">false</item>

View File

@ -182,6 +182,15 @@
<item name="pf_fingerprint_button">@style/PinCodeFingerprintButtonStyle</item> <item name="pf_fingerprint_button">@style/PinCodeFingerprintButtonStyle</item>
<item name="pf_next">@style/PinCodeNextButtonStyle</item> <item name="pf_next">@style/PinCodeNextButtonStyle</item>
<item name="android:statusBarColor">@color/riotx_header_panel_background_dark</item>
<item name="android:navigationBarColor">@color/riotx_header_panel_background_dark</item>
<!-- enable window content transitions -->
<item name="android:windowContentTransitions">true</item>
<!-- specify shared element enter and exit transitions -->
<item name="android:windowSharedElementEnterTransition">@transition/image_preview_transition</item>
<item name="android:windowSharedElementExitTransition">@transition/image_preview_transition</item>
</style> </style>
<style name="AppTheme.Dark" parent="AppTheme.Base.Dark" /> <style name="AppTheme.Dark" parent="AppTheme.Base.Dark" />

View File

@ -182,6 +182,17 @@
<item name="pf_fingerprint_button">@style/PinCodeFingerprintButtonStyle</item> <item name="pf_fingerprint_button">@style/PinCodeFingerprintButtonStyle</item>
<item name="pf_next">@style/PinCodeNextButtonStyle</item> <item name="pf_next">@style/PinCodeNextButtonStyle</item>
<!-- Use dark color, to have enough contrast with icons color. windowLightStatusBar is only available in API 23+ -->
<item name="android:statusBarColor">@color/riotx_header_panel_background_dark</item>
<!-- Use dark color, to have enough contrast with icons color. windowLightNavigationBar is only available in API 27+ -->
<item name="android:navigationBarColor">@color/riotx_header_panel_background_dark</item>
<!-- enable window content transitions -->
<item name="android:windowContentTransitions">true</item>
<!-- specify shared element enter and exit transitions -->
<item name="android:windowSharedElementEnterTransition">@transition/image_preview_transition</item>
<item name="android:windowSharedElementExitTransition">@transition/image_preview_transition</item>
</style> </style>
<style name="AppTheme.Light" parent="AppTheme.Base.Light" /> <style name="AppTheme.Light" parent="AppTheme.Base.Light" />

View File

@ -99,6 +99,18 @@
<item name="preferenceTheme">@style/PreferenceThemeOverlay.v14.Material</item> <item name="preferenceTheme">@style/PreferenceThemeOverlay.v14.Material</item>
<item name="bottomSheetDialogTheme">@style/Vector.BottomSheet.Status</item> <item name="bottomSheetDialogTheme">@style/Vector.BottomSheet.Status</item>
<!-- Use dark color, to have enough contrast with icons color. windowLightStatusBar is only available in API 23+ -->
<item name="android:statusBarColor">@color/riotx_header_panel_background_dark</item>
<!-- Use dark color, to have enough contrast with icons color. windowLightNavigationBar is only available in API 27+ -->
<item name="android:navigationBarColor">@color/riotx_header_panel_background_dark</item>
<!-- enable window content transitions -->
<item name="android:windowContentTransitions">true</item>
<!-- specify shared element enter and exit transitions -->
<item name="android:windowSharedElementEnterTransition">@transition/image_preview_transition</item>
<item name="android:windowSharedElementExitTransition">@transition/image_preview_transition</item>
</style> </style>
<style name="AppTheme.Status" parent="AppTheme.Base.Status" /> <style name="AppTheme.Status" parent="AppTheme.Base.Status" />