mirror of
https://github.com/Aviortheking/Neo-Store.git
synced 2025-06-16 04:19:19 +00:00
Reformated all the code
This commit is contained in:
@ -3,16 +3,16 @@ package com.looker.droidify.utility
|
||||
import android.os.Parcel
|
||||
import android.os.Parcelable
|
||||
|
||||
interface KParcelable: Parcelable {
|
||||
override fun describeContents(): Int = 0
|
||||
override fun writeToParcel(dest: Parcel, flags: Int) = Unit
|
||||
interface KParcelable : Parcelable {
|
||||
override fun describeContents(): Int = 0
|
||||
override fun writeToParcel(dest: Parcel, flags: Int) = Unit
|
||||
|
||||
companion object {
|
||||
inline fun <reified T> creator(crossinline create: (source: Parcel) -> T): Parcelable.Creator<T> {
|
||||
return object: Parcelable.Creator<T> {
|
||||
override fun createFromParcel(source: Parcel): T = create(source)
|
||||
override fun newArray(size: Int): Array<T?> = arrayOfNulls(size)
|
||||
}
|
||||
companion object {
|
||||
inline fun <reified T> creator(crossinline create: (source: Parcel) -> T): Parcelable.Creator<T> {
|
||||
return object : Parcelable.Creator<T> {
|
||||
override fun createFromParcel(source: Parcel): T = create(source)
|
||||
override fun newArray(size: Int): Array<T?> = arrayOfNulls(size)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -4,119 +4,134 @@ import android.content.Context
|
||||
import android.content.pm.PackageItemInfo
|
||||
import android.content.pm.PermissionInfo
|
||||
import android.content.res.Resources
|
||||
import com.looker.droidify.utility.extension.android.*
|
||||
import java.util.Locale
|
||||
import com.looker.droidify.utility.extension.android.Android
|
||||
import java.util.*
|
||||
|
||||
object PackageItemResolver {
|
||||
class LocalCache {
|
||||
internal val resources = mutableMapOf<String, Resources>()
|
||||
}
|
||||
class LocalCache {
|
||||
internal val resources = mutableMapOf<String, Resources>()
|
||||
}
|
||||
|
||||
private data class CacheKey(val locales: List<Locale>, val packageName: String, val resId: Int)
|
||||
private data class CacheKey(val locales: List<Locale>, val packageName: String, val resId: Int)
|
||||
|
||||
private val cache = mutableMapOf<CacheKey, String?>()
|
||||
private val cache = mutableMapOf<CacheKey, String?>()
|
||||
|
||||
private fun load(context: Context, localCache: LocalCache, packageName: String,
|
||||
nonLocalized: CharSequence?, resId: Int): CharSequence? {
|
||||
return when {
|
||||
nonLocalized != null -> {
|
||||
nonLocalized
|
||||
}
|
||||
resId != 0 -> {
|
||||
val locales = if (Android.sdk(24)) {
|
||||
val localesList = context.resources.configuration.locales
|
||||
(0 until localesList.size()).map(localesList::get)
|
||||
} else {
|
||||
@Suppress("DEPRECATION")
|
||||
listOf(context.resources.configuration.locale)
|
||||
}
|
||||
val cacheKey = CacheKey(locales, packageName, resId)
|
||||
if (cache.containsKey(cacheKey)) {
|
||||
cache[cacheKey]
|
||||
} else {
|
||||
val resources = localCache.resources[packageName] ?: run {
|
||||
val resources = try {
|
||||
val resources = context.packageManager.getResourcesForApplication(packageName)
|
||||
@Suppress("DEPRECATION")
|
||||
resources.updateConfiguration(context.resources.configuration, null)
|
||||
resources
|
||||
} catch (e: Exception) {
|
||||
null
|
||||
private fun load(
|
||||
context: Context, localCache: LocalCache, packageName: String,
|
||||
nonLocalized: CharSequence?, resId: Int
|
||||
): CharSequence? {
|
||||
return when {
|
||||
nonLocalized != null -> {
|
||||
nonLocalized
|
||||
}
|
||||
resId != 0 -> {
|
||||
val locales = if (Android.sdk(24)) {
|
||||
val localesList = context.resources.configuration.locales
|
||||
(0 until localesList.size()).map(localesList::get)
|
||||
} else {
|
||||
@Suppress("DEPRECATION")
|
||||
listOf(context.resources.configuration.locale)
|
||||
}
|
||||
val cacheKey = CacheKey(locales, packageName, resId)
|
||||
if (cache.containsKey(cacheKey)) {
|
||||
cache[cacheKey]
|
||||
} else {
|
||||
val resources = localCache.resources[packageName] ?: run {
|
||||
val resources = try {
|
||||
val resources =
|
||||
context.packageManager.getResourcesForApplication(packageName)
|
||||
@Suppress("DEPRECATION")
|
||||
resources.updateConfiguration(context.resources.configuration, null)
|
||||
resources
|
||||
} catch (e: Exception) {
|
||||
null
|
||||
}
|
||||
resources?.let { localCache.resources[packageName] = it }
|
||||
resources
|
||||
}
|
||||
val label = resources?.getString(resId)
|
||||
cache[cacheKey] = label
|
||||
label
|
||||
}
|
||||
}
|
||||
else -> {
|
||||
null
|
||||
}
|
||||
resources?.let { localCache.resources[packageName] = it }
|
||||
resources
|
||||
}
|
||||
val label = resources?.getString(resId)
|
||||
cache[cacheKey] = label
|
||||
label
|
||||
}
|
||||
}
|
||||
else -> {
|
||||
null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun loadLabel(context: Context, localCache: LocalCache, packageItemInfo: PackageItemInfo): CharSequence? {
|
||||
return load(context, localCache, packageItemInfo.packageName,
|
||||
packageItemInfo.nonLocalizedLabel, packageItemInfo.labelRes)
|
||||
}
|
||||
|
||||
fun loadDescription(context: Context, localCache: LocalCache, permissionInfo: PermissionInfo): CharSequence? {
|
||||
return load(context, localCache, permissionInfo.packageName,
|
||||
permissionInfo.nonLocalizedDescription, permissionInfo.descriptionRes)
|
||||
}
|
||||
|
||||
fun getPermissionGroup(permissionInfo: PermissionInfo): String? {
|
||||
return if (Android.sdk(29)) {
|
||||
// Copied from package installer (Utils.java)
|
||||
when (permissionInfo.name) {
|
||||
android.Manifest.permission.READ_CONTACTS,
|
||||
android.Manifest.permission.WRITE_CONTACTS,
|
||||
android.Manifest.permission.GET_ACCOUNTS ->
|
||||
android.Manifest.permission_group.CONTACTS
|
||||
android.Manifest.permission.READ_CALENDAR,
|
||||
android.Manifest.permission.WRITE_CALENDAR ->
|
||||
android.Manifest.permission_group.CALENDAR
|
||||
android.Manifest.permission.SEND_SMS,
|
||||
android.Manifest.permission.RECEIVE_SMS,
|
||||
android.Manifest.permission.READ_SMS,
|
||||
android.Manifest.permission.RECEIVE_MMS,
|
||||
android.Manifest.permission.RECEIVE_WAP_PUSH,
|
||||
"android.permission.READ_CELL_BROADCASTS" ->
|
||||
android.Manifest.permission_group.SMS
|
||||
android.Manifest.permission.READ_EXTERNAL_STORAGE,
|
||||
android.Manifest.permission.WRITE_EXTERNAL_STORAGE,
|
||||
android.Manifest.permission.ACCESS_MEDIA_LOCATION ->
|
||||
android.Manifest.permission_group.STORAGE
|
||||
android.Manifest.permission.ACCESS_FINE_LOCATION,
|
||||
android.Manifest.permission.ACCESS_COARSE_LOCATION,
|
||||
android.Manifest.permission.ACCESS_BACKGROUND_LOCATION ->
|
||||
android.Manifest.permission_group.LOCATION
|
||||
android.Manifest.permission.READ_CALL_LOG,
|
||||
android.Manifest.permission.WRITE_CALL_LOG,
|
||||
@Suppress("DEPRECATION") android.Manifest.permission.PROCESS_OUTGOING_CALLS ->
|
||||
android.Manifest.permission_group.CALL_LOG
|
||||
android.Manifest.permission.READ_PHONE_STATE,
|
||||
android.Manifest.permission.READ_PHONE_NUMBERS,
|
||||
android.Manifest.permission.CALL_PHONE,
|
||||
android.Manifest.permission.ADD_VOICEMAIL,
|
||||
android.Manifest.permission.USE_SIP,
|
||||
android.Manifest.permission.ANSWER_PHONE_CALLS,
|
||||
android.Manifest.permission.ACCEPT_HANDOVER ->
|
||||
android.Manifest.permission_group.PHONE
|
||||
android.Manifest.permission.RECORD_AUDIO ->
|
||||
android.Manifest.permission_group.MICROPHONE
|
||||
android.Manifest.permission.ACTIVITY_RECOGNITION ->
|
||||
android.Manifest.permission_group.ACTIVITY_RECOGNITION
|
||||
android.Manifest.permission.CAMERA ->
|
||||
android.Manifest.permission_group.CAMERA
|
||||
android.Manifest.permission.BODY_SENSORS ->
|
||||
android.Manifest.permission_group.SENSORS
|
||||
else -> null
|
||||
}
|
||||
} else {
|
||||
permissionInfo.group
|
||||
fun loadLabel(
|
||||
context: Context,
|
||||
localCache: LocalCache,
|
||||
packageItemInfo: PackageItemInfo
|
||||
): CharSequence? {
|
||||
return load(
|
||||
context, localCache, packageItemInfo.packageName,
|
||||
packageItemInfo.nonLocalizedLabel, packageItemInfo.labelRes
|
||||
)
|
||||
}
|
||||
|
||||
fun loadDescription(
|
||||
context: Context,
|
||||
localCache: LocalCache,
|
||||
permissionInfo: PermissionInfo
|
||||
): CharSequence? {
|
||||
return load(
|
||||
context, localCache, permissionInfo.packageName,
|
||||
permissionInfo.nonLocalizedDescription, permissionInfo.descriptionRes
|
||||
)
|
||||
}
|
||||
|
||||
fun getPermissionGroup(permissionInfo: PermissionInfo): String? {
|
||||
return if (Android.sdk(29)) {
|
||||
// Copied from package installer (Utils.java)
|
||||
when (permissionInfo.name) {
|
||||
android.Manifest.permission.READ_CONTACTS,
|
||||
android.Manifest.permission.WRITE_CONTACTS,
|
||||
android.Manifest.permission.GET_ACCOUNTS ->
|
||||
android.Manifest.permission_group.CONTACTS
|
||||
android.Manifest.permission.READ_CALENDAR,
|
||||
android.Manifest.permission.WRITE_CALENDAR ->
|
||||
android.Manifest.permission_group.CALENDAR
|
||||
android.Manifest.permission.SEND_SMS,
|
||||
android.Manifest.permission.RECEIVE_SMS,
|
||||
android.Manifest.permission.READ_SMS,
|
||||
android.Manifest.permission.RECEIVE_MMS,
|
||||
android.Manifest.permission.RECEIVE_WAP_PUSH,
|
||||
"android.permission.READ_CELL_BROADCASTS" ->
|
||||
android.Manifest.permission_group.SMS
|
||||
android.Manifest.permission.READ_EXTERNAL_STORAGE,
|
||||
android.Manifest.permission.WRITE_EXTERNAL_STORAGE,
|
||||
android.Manifest.permission.ACCESS_MEDIA_LOCATION ->
|
||||
android.Manifest.permission_group.STORAGE
|
||||
android.Manifest.permission.ACCESS_FINE_LOCATION,
|
||||
android.Manifest.permission.ACCESS_COARSE_LOCATION,
|
||||
android.Manifest.permission.ACCESS_BACKGROUND_LOCATION ->
|
||||
android.Manifest.permission_group.LOCATION
|
||||
android.Manifest.permission.READ_CALL_LOG,
|
||||
android.Manifest.permission.WRITE_CALL_LOG,
|
||||
@Suppress("DEPRECATION") android.Manifest.permission.PROCESS_OUTGOING_CALLS ->
|
||||
android.Manifest.permission_group.CALL_LOG
|
||||
android.Manifest.permission.READ_PHONE_STATE,
|
||||
android.Manifest.permission.READ_PHONE_NUMBERS,
|
||||
android.Manifest.permission.CALL_PHONE,
|
||||
android.Manifest.permission.ADD_VOICEMAIL,
|
||||
android.Manifest.permission.USE_SIP,
|
||||
android.Manifest.permission.ANSWER_PHONE_CALLS,
|
||||
android.Manifest.permission.ACCEPT_HANDOVER ->
|
||||
android.Manifest.permission_group.PHONE
|
||||
android.Manifest.permission.RECORD_AUDIO ->
|
||||
android.Manifest.permission_group.MICROPHONE
|
||||
android.Manifest.permission.ACTIVITY_RECOGNITION ->
|
||||
android.Manifest.permission_group.ACTIVITY_RECOGNITION
|
||||
android.Manifest.permission.CAMERA ->
|
||||
android.Manifest.permission_group.CAMERA
|
||||
android.Manifest.permission.BODY_SENSORS ->
|
||||
android.Manifest.permission_group.SENSORS
|
||||
else -> null
|
||||
}
|
||||
} else {
|
||||
permissionInfo.group
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2,28 +2,32 @@ package com.looker.droidify.utility
|
||||
|
||||
import java.io.InputStream
|
||||
|
||||
class ProgressInputStream(private val inputStream: InputStream,
|
||||
private val callback: (Long) -> Unit): InputStream() {
|
||||
private var count = 0L
|
||||
class ProgressInputStream(
|
||||
private val inputStream: InputStream,
|
||||
private val callback: (Long) -> Unit
|
||||
) : InputStream() {
|
||||
private var count = 0L
|
||||
|
||||
private inline fun <reified T: Number> notify(one: Boolean, read: () -> T): T {
|
||||
val result = read()
|
||||
count += if (one) 1L else result.toLong()
|
||||
callback(count)
|
||||
return result
|
||||
}
|
||||
private inline fun <reified T : Number> notify(one: Boolean, read: () -> T): T {
|
||||
val result = read()
|
||||
count += if (one) 1L else result.toLong()
|
||||
callback(count)
|
||||
return result
|
||||
}
|
||||
|
||||
override fun read(): Int = notify(true) { inputStream.read() }
|
||||
override fun read(b: ByteArray): Int = notify(false) { inputStream.read(b) }
|
||||
override fun read(b: ByteArray, off: Int, len: Int): Int = notify(false) { inputStream.read(b, off, len) }
|
||||
override fun skip(n: Long): Long = notify(false) { inputStream.skip(n) }
|
||||
override fun read(): Int = notify(true) { inputStream.read() }
|
||||
override fun read(b: ByteArray): Int = notify(false) { inputStream.read(b) }
|
||||
override fun read(b: ByteArray, off: Int, len: Int): Int =
|
||||
notify(false) { inputStream.read(b, off, len) }
|
||||
|
||||
override fun available(): Int {
|
||||
return inputStream.available()
|
||||
}
|
||||
override fun skip(n: Long): Long = notify(false) { inputStream.skip(n) }
|
||||
|
||||
override fun close() {
|
||||
inputStream.close()
|
||||
super.close()
|
||||
}
|
||||
override fun available(): Int {
|
||||
return inputStream.available()
|
||||
}
|
||||
|
||||
override fun close() {
|
||||
inputStream.close()
|
||||
super.close()
|
||||
}
|
||||
}
|
||||
|
@ -11,73 +11,78 @@ import okhttp3.Call
|
||||
import okhttp3.Response
|
||||
|
||||
object RxUtils {
|
||||
private class ManagedDisposable(private val cancel: () -> Unit): Disposable {
|
||||
@Volatile var disposed = false
|
||||
override fun isDisposed(): Boolean = disposed
|
||||
private class ManagedDisposable(private val cancel: () -> Unit) : Disposable {
|
||||
@Volatile
|
||||
var disposed = false
|
||||
override fun isDisposed(): Boolean = disposed
|
||||
|
||||
override fun dispose() {
|
||||
disposed = true
|
||||
cancel()
|
||||
override fun dispose() {
|
||||
disposed = true
|
||||
cancel()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun <T, R> managedSingle(create: () -> T, cancel: (T) -> Unit, execute: (T) -> R): Single<R> {
|
||||
return Single.create {
|
||||
val task = create()
|
||||
val thread = Thread.currentThread()
|
||||
val disposable = ManagedDisposable {
|
||||
thread.interrupt()
|
||||
cancel(task)
|
||||
}
|
||||
it.setDisposable(disposable)
|
||||
if (!disposable.isDisposed) {
|
||||
val result = try {
|
||||
execute(task)
|
||||
} catch (e: Throwable) {
|
||||
Exceptions.throwIfFatal(e)
|
||||
if (!disposable.isDisposed) {
|
||||
try {
|
||||
it.onError(e)
|
||||
} catch (inner: Throwable) {
|
||||
Exceptions.throwIfFatal(inner)
|
||||
RxJavaPlugins.onError(CompositeException(e, inner))
|
||||
private fun <T, R> managedSingle(
|
||||
create: () -> T,
|
||||
cancel: (T) -> Unit,
|
||||
execute: (T) -> R
|
||||
): Single<R> {
|
||||
return Single.create {
|
||||
val task = create()
|
||||
val thread = Thread.currentThread()
|
||||
val disposable = ManagedDisposable {
|
||||
thread.interrupt()
|
||||
cancel(task)
|
||||
}
|
||||
it.setDisposable(disposable)
|
||||
if (!disposable.isDisposed) {
|
||||
val result = try {
|
||||
execute(task)
|
||||
} catch (e: Throwable) {
|
||||
Exceptions.throwIfFatal(e)
|
||||
if (!disposable.isDisposed) {
|
||||
try {
|
||||
it.onError(e)
|
||||
} catch (inner: Throwable) {
|
||||
Exceptions.throwIfFatal(inner)
|
||||
RxJavaPlugins.onError(CompositeException(e, inner))
|
||||
}
|
||||
}
|
||||
null
|
||||
}
|
||||
if (result != null && !disposable.isDisposed) {
|
||||
it.onSuccess(result)
|
||||
}
|
||||
}
|
||||
}
|
||||
null
|
||||
}
|
||||
if (result != null && !disposable.isDisposed) {
|
||||
it.onSuccess(result)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun <R> managedSingle(execute: () -> R): Single<R> {
|
||||
return managedSingle({ Unit }, { }, { execute() })
|
||||
}
|
||||
|
||||
fun callSingle(create: () -> Call): Single<Response> {
|
||||
return managedSingle(create, Call::cancel, Call::execute)
|
||||
}
|
||||
|
||||
fun <T> querySingle(query: (CancellationSignal) -> T): Single<T> {
|
||||
return Single.create {
|
||||
val cancellationSignal = CancellationSignal()
|
||||
it.setCancellable {
|
||||
try {
|
||||
cancellationSignal.cancel()
|
||||
} catch (e: OperationCanceledException) {
|
||||
// Do nothing
|
||||
}
|
||||
}
|
||||
val result = try {
|
||||
query(cancellationSignal)
|
||||
} catch (e: OperationCanceledException) {
|
||||
null
|
||||
}
|
||||
if (result != null) {
|
||||
it.onSuccess(result)
|
||||
}
|
||||
fun <R> managedSingle(execute: () -> R): Single<R> {
|
||||
return managedSingle({ }, { }, { execute() })
|
||||
}
|
||||
|
||||
fun callSingle(create: () -> Call): Single<Response> {
|
||||
return managedSingle(create, Call::cancel, Call::execute)
|
||||
}
|
||||
|
||||
fun <T> querySingle(query: (CancellationSignal) -> T): Single<T> {
|
||||
return Single.create {
|
||||
val cancellationSignal = CancellationSignal()
|
||||
it.setCancellable {
|
||||
try {
|
||||
cancellationSignal.cancel()
|
||||
} catch (e: OperationCanceledException) {
|
||||
// Do nothing
|
||||
}
|
||||
}
|
||||
val result = try {
|
||||
query(cancellationSignal)
|
||||
} catch (e: OperationCanceledException) {
|
||||
null
|
||||
}
|
||||
if (result != null) {
|
||||
it.onSuccess(result)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -9,92 +9,100 @@ import android.os.LocaleList
|
||||
import android.provider.Settings
|
||||
import com.looker.droidify.BuildConfig
|
||||
import com.looker.droidify.R
|
||||
import com.looker.droidify.utility.extension.android.*
|
||||
import com.looker.droidify.utility.extension.resources.*
|
||||
import com.looker.droidify.utility.extension.text.*
|
||||
import com.looker.droidify.utility.extension.android.Android
|
||||
import com.looker.droidify.utility.extension.resources.getColorFromAttr
|
||||
import com.looker.droidify.utility.extension.resources.getDrawableCompat
|
||||
import com.looker.droidify.utility.extension.text.hex
|
||||
import java.security.MessageDigest
|
||||
import java.security.cert.Certificate
|
||||
import java.security.cert.CertificateEncodingException
|
||||
import java.util.Locale
|
||||
import java.util.*
|
||||
|
||||
object Utils {
|
||||
private fun createDefaultApplicationIcon(context: Context, tintAttrResId: Int): Drawable {
|
||||
return context.getDrawableCompat(R.drawable.ic_application_default).mutate()
|
||||
.apply { setTintList(context.getColorFromAttr(tintAttrResId)) }
|
||||
}
|
||||
|
||||
fun getDefaultApplicationIcons(context: Context): Pair<Drawable, Drawable> {
|
||||
val progressIcon: Drawable = createDefaultApplicationIcon(context, android.R.attr.textColorSecondary)
|
||||
val defaultIcon: Drawable = createDefaultApplicationIcon(context, android.R.attr.colorAccent)
|
||||
return Pair(progressIcon, defaultIcon)
|
||||
}
|
||||
|
||||
fun getToolbarIcon(context: Context, resId: Int): Drawable {
|
||||
val drawable = context.getDrawableCompat(resId).mutate()
|
||||
drawable.setTintList(context.getColorFromAttr(android.R.attr.textColorPrimary))
|
||||
return drawable
|
||||
}
|
||||
|
||||
fun calculateHash(signature: Signature): String? {
|
||||
return MessageDigest.getInstance("MD5").digest(signature.toCharsString().toByteArray()).hex()
|
||||
}
|
||||
|
||||
fun calculateFingerprint(certificate: Certificate): String {
|
||||
val encoded = try {
|
||||
certificate.encoded
|
||||
} catch (e: CertificateEncodingException) {
|
||||
null
|
||||
private fun createDefaultApplicationIcon(context: Context, tintAttrResId: Int): Drawable {
|
||||
return context.getDrawableCompat(R.drawable.ic_application_default).mutate()
|
||||
.apply { setTintList(context.getColorFromAttr(tintAttrResId)) }
|
||||
}
|
||||
return encoded?.let(::calculateFingerprint).orEmpty()
|
||||
}
|
||||
|
||||
fun calculateFingerprint(key: ByteArray): String {
|
||||
return if (key.size >= 256) {
|
||||
try {
|
||||
val fingerprint = MessageDigest.getInstance("SHA-256").digest(key)
|
||||
val builder = StringBuilder()
|
||||
for (byte in fingerprint) {
|
||||
builder.append("%02X".format(Locale.US, byte.toInt() and 0xff))
|
||||
fun getDefaultApplicationIcons(context: Context): Pair<Drawable, Drawable> {
|
||||
val progressIcon: Drawable =
|
||||
createDefaultApplicationIcon(context, android.R.attr.textColorSecondary)
|
||||
val defaultIcon: Drawable =
|
||||
createDefaultApplicationIcon(context, android.R.attr.colorAccent)
|
||||
return Pair(progressIcon, defaultIcon)
|
||||
}
|
||||
|
||||
fun getToolbarIcon(context: Context, resId: Int): Drawable {
|
||||
val drawable = context.getDrawableCompat(resId).mutate()
|
||||
drawable.setTintList(context.getColorFromAttr(android.R.attr.titleTextColor))
|
||||
return drawable
|
||||
}
|
||||
|
||||
fun calculateHash(signature: Signature): String {
|
||||
return MessageDigest.getInstance("MD5").digest(signature.toCharsString().toByteArray())
|
||||
.hex()
|
||||
}
|
||||
|
||||
fun calculateFingerprint(certificate: Certificate): String {
|
||||
val encoded = try {
|
||||
certificate.encoded
|
||||
} catch (e: CertificateEncodingException) {
|
||||
null
|
||||
}
|
||||
builder.toString()
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
""
|
||||
}
|
||||
} else {
|
||||
""
|
||||
return encoded?.let(::calculateFingerprint).orEmpty()
|
||||
}
|
||||
}
|
||||
|
||||
fun configureLocale(context: Context): Context {
|
||||
val supportedLanguages = BuildConfig.LANGUAGES.toSet()
|
||||
val configuration = context.resources.configuration
|
||||
val currentLocales = if (Android.sdk(24)) {
|
||||
val localesList = configuration.locales
|
||||
(0 until localesList.size()).map(localesList::get)
|
||||
} else {
|
||||
@Suppress("DEPRECATION")
|
||||
listOf(configuration.locale)
|
||||
fun calculateFingerprint(key: ByteArray): String {
|
||||
return if (key.size >= 256) {
|
||||
try {
|
||||
val fingerprint = MessageDigest.getInstance("SHA-256").digest(key)
|
||||
val builder = StringBuilder()
|
||||
for (byte in fingerprint) {
|
||||
builder.append("%02X".format(Locale.US, byte.toInt() and 0xff))
|
||||
}
|
||||
builder.toString()
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
""
|
||||
}
|
||||
} else {
|
||||
""
|
||||
}
|
||||
}
|
||||
val compatibleLocales = currentLocales
|
||||
.filter { it.language in supportedLanguages }
|
||||
.let { if (it.isEmpty()) listOf(Locale.US) else it }
|
||||
Locale.setDefault(compatibleLocales.first())
|
||||
val newConfiguration = Configuration(configuration)
|
||||
if (Android.sdk(24)) {
|
||||
newConfiguration.setLocales(LocaleList(*compatibleLocales.toTypedArray()))
|
||||
} else {
|
||||
@Suppress("DEPRECATION")
|
||||
newConfiguration.locale = compatibleLocales.first()
|
||||
}
|
||||
return context.createConfigurationContext(newConfiguration)
|
||||
}
|
||||
|
||||
fun areAnimationsEnabled(context: Context): Boolean {
|
||||
return if (Android.sdk(26)) {
|
||||
ValueAnimator.areAnimatorsEnabled()
|
||||
} else {
|
||||
Settings.Global.getFloat(context.contentResolver, Settings.Global.ANIMATOR_DURATION_SCALE, 1f) != 0f
|
||||
fun configureLocale(context: Context): Context {
|
||||
val supportedLanguages = BuildConfig.LANGUAGES.toSet()
|
||||
val configuration = context.resources.configuration
|
||||
val currentLocales = if (Android.sdk(24)) {
|
||||
val localesList = configuration.locales
|
||||
(0 until localesList.size()).map(localesList::get)
|
||||
} else {
|
||||
@Suppress("DEPRECATION")
|
||||
listOf(configuration.locale)
|
||||
}
|
||||
val compatibleLocales = currentLocales
|
||||
.filter { it.language in supportedLanguages }
|
||||
.let { if (it.isEmpty()) listOf(Locale.US) else it }
|
||||
Locale.setDefault(compatibleLocales.first())
|
||||
val newConfiguration = Configuration(configuration)
|
||||
if (Android.sdk(24)) {
|
||||
newConfiguration.setLocales(LocaleList(*compatibleLocales.toTypedArray()))
|
||||
} else {
|
||||
@Suppress("DEPRECATION")
|
||||
newConfiguration.locale = compatibleLocales.first()
|
||||
}
|
||||
return context.createConfigurationContext(newConfiguration)
|
||||
}
|
||||
|
||||
fun areAnimationsEnabled(context: Context): Boolean {
|
||||
return if (Android.sdk(26)) {
|
||||
ValueAnimator.areAnimatorsEnabled()
|
||||
} else {
|
||||
Settings.Global.getFloat(
|
||||
context.contentResolver,
|
||||
Settings.Global.ANIMATOR_DURATION_SCALE,
|
||||
1f
|
||||
) != 0f
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
@file:Suppress("PackageDirectoryMismatch")
|
||||
|
||||
package com.looker.droidify.utility.extension.android
|
||||
|
||||
import android.app.NotificationManager
|
||||
@ -10,67 +11,68 @@ import android.database.sqlite.SQLiteDatabase
|
||||
import android.os.Build
|
||||
|
||||
fun Cursor.asSequence(): Sequence<Cursor> {
|
||||
return generateSequence { if (moveToNext()) this else null }
|
||||
return generateSequence { if (moveToNext()) this else null }
|
||||
}
|
||||
|
||||
fun Cursor.firstOrNull(): Cursor? {
|
||||
return if (moveToFirst()) this else null
|
||||
return if (moveToFirst()) this else null
|
||||
}
|
||||
|
||||
fun SQLiteDatabase.execWithResult(sql: String) {
|
||||
rawQuery(sql, null).use { it.count }
|
||||
rawQuery(sql, null).use { it.count }
|
||||
}
|
||||
|
||||
val Context.notificationManager: NotificationManager
|
||||
get() = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
|
||||
get() = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
|
||||
|
||||
val PackageInfo.versionCodeCompat: Long
|
||||
get() = if (Android.sdk(28)) longVersionCode else @Suppress("DEPRECATION") versionCode.toLong()
|
||||
get() = if (Android.sdk(28)) longVersionCode else @Suppress("DEPRECATION") versionCode.toLong()
|
||||
|
||||
val PackageInfo.singleSignature: Signature?
|
||||
get() {
|
||||
return if (Android.sdk(28)) {
|
||||
val signingInfo = signingInfo
|
||||
if (signingInfo?.hasMultipleSigners() == false) signingInfo.apkContentsSigners
|
||||
?.let { if (it.size == 1) it[0] else null } else null
|
||||
} else {
|
||||
@Suppress("DEPRECATION")
|
||||
signatures?.let { if (it.size == 1) it[0] else null }
|
||||
get() {
|
||||
return if (Android.sdk(28)) {
|
||||
val signingInfo = signingInfo
|
||||
if (signingInfo?.hasMultipleSigners() == false) signingInfo.apkContentsSigners
|
||||
?.let { if (it.size == 1) it[0] else null } else null
|
||||
} else {
|
||||
@Suppress("DEPRECATION")
|
||||
signatures?.let { if (it.size == 1) it[0] else null }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
object Android {
|
||||
val sdk: Int
|
||||
get() = Build.VERSION.SDK_INT
|
||||
val sdk: Int
|
||||
get() = Build.VERSION.SDK_INT
|
||||
|
||||
val name: String
|
||||
get() = "Android ${Build.VERSION.RELEASE}"
|
||||
val name: String
|
||||
get() = "Android ${Build.VERSION.RELEASE}"
|
||||
|
||||
val platforms = Build.SUPPORTED_ABIS.toSet()
|
||||
val platforms = Build.SUPPORTED_ABIS.toSet()
|
||||
|
||||
val primaryPlatform: String?
|
||||
get() = Build.SUPPORTED_64_BIT_ABIS?.firstOrNull() ?: Build.SUPPORTED_32_BIT_ABIS?.firstOrNull()
|
||||
val primaryPlatform: String?
|
||||
get() = Build.SUPPORTED_64_BIT_ABIS?.firstOrNull()
|
||||
?: Build.SUPPORTED_32_BIT_ABIS?.firstOrNull()
|
||||
|
||||
fun sdk(sdk: Int): Boolean {
|
||||
return Build.VERSION.SDK_INT >= sdk
|
||||
}
|
||||
fun sdk(sdk: Int): Boolean {
|
||||
return Build.VERSION.SDK_INT >= sdk
|
||||
}
|
||||
|
||||
object PackageManager {
|
||||
// GET_SIGNATURES should always present for getPackageArchiveInfo
|
||||
val signaturesFlag: Int
|
||||
get() = (if (sdk(28)) android.content.pm.PackageManager.GET_SIGNING_CERTIFICATES else 0) or
|
||||
@Suppress("DEPRECATION") android.content.pm.PackageManager.GET_SIGNATURES
|
||||
}
|
||||
object PackageManager {
|
||||
// GET_SIGNATURES should always present for getPackageArchiveInfo
|
||||
val signaturesFlag: Int
|
||||
get() = (if (sdk(28)) android.content.pm.PackageManager.GET_SIGNING_CERTIFICATES else 0) or
|
||||
@Suppress("DEPRECATION") android.content.pm.PackageManager.GET_SIGNATURES
|
||||
}
|
||||
|
||||
object Device {
|
||||
val isHuaweiEmui: Boolean
|
||||
get() {
|
||||
return try {
|
||||
Class.forName("com.huawei.android.os.BuildEx")
|
||||
true
|
||||
} catch (e: Exception) {
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
object Device {
|
||||
val isHuaweiEmui: Boolean
|
||||
get() {
|
||||
return try {
|
||||
Class.forName("com.huawei.android.os.BuildEx")
|
||||
true
|
||||
} catch (e: Exception) {
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,106 +1,106 @@
|
||||
@file:Suppress("PackageDirectoryMismatch")
|
||||
|
||||
package com.looker.droidify.utility.extension.json
|
||||
|
||||
import com.fasterxml.jackson.core.JsonFactory
|
||||
import com.fasterxml.jackson.core.JsonGenerator
|
||||
import com.fasterxml.jackson.core.JsonParseException
|
||||
import com.fasterxml.jackson.core.JsonParser
|
||||
import com.fasterxml.jackson.core.JsonToken
|
||||
import com.fasterxml.jackson.core.*
|
||||
|
||||
object Json {
|
||||
val factory = JsonFactory()
|
||||
val factory = JsonFactory()
|
||||
}
|
||||
|
||||
fun JsonParser.illegal(): Nothing {
|
||||
throw JsonParseException(this, "Illegal state")
|
||||
throw JsonParseException(this, "Illegal state")
|
||||
}
|
||||
|
||||
interface KeyToken {
|
||||
val key: String
|
||||
val token: JsonToken
|
||||
val key: String
|
||||
val token: JsonToken
|
||||
|
||||
fun number(key: String): Boolean = this.key == key && this.token.isNumeric
|
||||
fun string(key: String): Boolean = this.key == key && this.token == JsonToken.VALUE_STRING
|
||||
fun boolean(key: String): Boolean = this.key == key && this.token.isBoolean
|
||||
fun dictionary(key: String): Boolean = this.key == key && this.token == JsonToken.START_OBJECT
|
||||
fun array(key: String): Boolean = this.key == key && this.token == JsonToken.START_ARRAY
|
||||
fun number(key: String): Boolean = this.key == key && this.token.isNumeric
|
||||
fun string(key: String): Boolean = this.key == key && this.token == JsonToken.VALUE_STRING
|
||||
fun boolean(key: String): Boolean = this.key == key && this.token.isBoolean
|
||||
fun dictionary(key: String): Boolean = this.key == key && this.token == JsonToken.START_OBJECT
|
||||
fun array(key: String): Boolean = this.key == key && this.token == JsonToken.START_ARRAY
|
||||
}
|
||||
|
||||
inline fun JsonParser.forEachKey(callback: JsonParser.(KeyToken) -> Unit) {
|
||||
var passKey = ""
|
||||
var passToken = JsonToken.NOT_AVAILABLE
|
||||
val keyToken = object: KeyToken {
|
||||
override val key: String
|
||||
get() = passKey
|
||||
override val token: JsonToken
|
||||
get() = passToken
|
||||
}
|
||||
while (true) {
|
||||
val token = nextToken()
|
||||
if (token == JsonToken.FIELD_NAME) {
|
||||
passKey = currentName
|
||||
passToken = nextToken()
|
||||
callback(keyToken)
|
||||
} else if (token == JsonToken.END_OBJECT) {
|
||||
break
|
||||
} else {
|
||||
illegal()
|
||||
var passKey = ""
|
||||
var passToken = JsonToken.NOT_AVAILABLE
|
||||
val keyToken = object : KeyToken {
|
||||
override val key: String
|
||||
get() = passKey
|
||||
override val token: JsonToken
|
||||
get() = passToken
|
||||
}
|
||||
while (true) {
|
||||
val token = nextToken()
|
||||
if (token == JsonToken.FIELD_NAME) {
|
||||
passKey = currentName
|
||||
passToken = nextToken()
|
||||
callback(keyToken)
|
||||
} else if (token == JsonToken.END_OBJECT) {
|
||||
break
|
||||
} else {
|
||||
illegal()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun JsonParser.forEach(requiredToken: JsonToken, callback: JsonParser.() -> Unit) {
|
||||
while (true) {
|
||||
val token = nextToken()
|
||||
if (token == JsonToken.END_ARRAY) {
|
||||
break
|
||||
} else if (token == requiredToken) {
|
||||
callback()
|
||||
} else if (token.isStructStart) {
|
||||
skipChildren()
|
||||
while (true) {
|
||||
val token = nextToken()
|
||||
if (token == JsonToken.END_ARRAY) {
|
||||
break
|
||||
} else if (token == requiredToken) {
|
||||
callback()
|
||||
} else if (token.isStructStart) {
|
||||
skipChildren()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun <T> JsonParser.collectNotNull(requiredToken: JsonToken, callback: JsonParser.() -> T?): List<T> {
|
||||
val list = mutableListOf<T>()
|
||||
forEach(requiredToken) {
|
||||
val result = callback()
|
||||
if (result != null) {
|
||||
list += result
|
||||
fun <T> JsonParser.collectNotNull(
|
||||
requiredToken: JsonToken,
|
||||
callback: JsonParser.() -> T?
|
||||
): List<T> {
|
||||
val list = mutableListOf<T>()
|
||||
forEach(requiredToken) {
|
||||
val result = callback()
|
||||
if (result != null) {
|
||||
list += result
|
||||
}
|
||||
}
|
||||
}
|
||||
return list
|
||||
return list
|
||||
}
|
||||
|
||||
fun JsonParser.collectNotNullStrings(): List<String> {
|
||||
return collectNotNull(JsonToken.VALUE_STRING) { valueAsString }
|
||||
return collectNotNull(JsonToken.VALUE_STRING) { valueAsString }
|
||||
}
|
||||
|
||||
fun JsonParser.collectDistinctNotEmptyStrings(): List<String> {
|
||||
return collectNotNullStrings().asSequence().filter { it.isNotEmpty() }.distinct().toList()
|
||||
return collectNotNullStrings().asSequence().filter { it.isNotEmpty() }.distinct().toList()
|
||||
}
|
||||
|
||||
inline fun <T> JsonParser.parseDictionary(callback: JsonParser.() -> T): T {
|
||||
if (nextToken() == JsonToken.START_OBJECT) {
|
||||
val result = callback()
|
||||
if (nextToken() != null) {
|
||||
illegal()
|
||||
if (nextToken() == JsonToken.START_OBJECT) {
|
||||
val result = callback()
|
||||
if (nextToken() != null) {
|
||||
illegal()
|
||||
}
|
||||
return result
|
||||
} else {
|
||||
illegal()
|
||||
}
|
||||
return result
|
||||
} else {
|
||||
illegal()
|
||||
}
|
||||
}
|
||||
|
||||
inline fun JsonGenerator.writeDictionary(callback: JsonGenerator.() -> Unit) {
|
||||
writeStartObject()
|
||||
callback()
|
||||
writeEndObject()
|
||||
writeStartObject()
|
||||
callback()
|
||||
writeEndObject()
|
||||
}
|
||||
|
||||
inline fun JsonGenerator.writeArray(fieldName: String, callback: JsonGenerator.() -> Unit) {
|
||||
writeArrayFieldStart(fieldName)
|
||||
callback()
|
||||
writeEndArray()
|
||||
writeArrayFieldStart(fieldName)
|
||||
callback()
|
||||
writeEndArray()
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
@file:Suppress("PackageDirectoryMismatch")
|
||||
|
||||
package com.looker.droidify.utility.extension.resources
|
||||
|
||||
import android.content.Context
|
||||
@ -16,79 +17,84 @@ import android.widget.ImageView
|
||||
import android.widget.TextView
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.vectordrawable.graphics.drawable.VectorDrawableCompat
|
||||
import com.looker.droidify.utility.extension.android.Android
|
||||
import com.squareup.picasso.Picasso
|
||||
import com.squareup.picasso.RequestCreator
|
||||
import com.looker.droidify.utility.extension.android.*
|
||||
import org.xmlpull.v1.XmlPullParser
|
||||
import kotlin.math.*
|
||||
import kotlin.math.roundToInt
|
||||
|
||||
object TypefaceExtra {
|
||||
val medium = Typeface.create("sans-serif-medium", Typeface.NORMAL)!!
|
||||
val light = Typeface.create("sans-serif-light", Typeface.NORMAL)!!
|
||||
val medium = Typeface.create("sans-serif-medium", Typeface.NORMAL)!!
|
||||
val light = Typeface.create("sans-serif-light", Typeface.NORMAL)!!
|
||||
}
|
||||
|
||||
fun Context.getDrawableCompat(resId: Int): Drawable {
|
||||
val drawable = if (!Android.sdk(24)) {
|
||||
val fileName = TypedValue().apply { resources.getValue(resId, this, true) }.string
|
||||
if (fileName.endsWith(".xml")) {
|
||||
resources.getXml(resId).use {
|
||||
val eventType = generateSequence { it.next() }
|
||||
.find { it == XmlPullParser.START_TAG || it == XmlPullParser.END_DOCUMENT }
|
||||
if (eventType == XmlPullParser.START_TAG) {
|
||||
when (it.name) {
|
||||
"vector" -> VectorDrawableCompat.createFromXmlInner(resources, it, Xml.asAttributeSet(it), theme)
|
||||
else -> null
|
||||
}
|
||||
val drawable = if (!Android.sdk(24)) {
|
||||
val fileName = TypedValue().apply { resources.getValue(resId, this, true) }.string
|
||||
if (fileName.endsWith(".xml")) {
|
||||
resources.getXml(resId).use { it ->
|
||||
val eventType = generateSequence { it.next() }
|
||||
.find { it == XmlPullParser.START_TAG || it == XmlPullParser.END_DOCUMENT }
|
||||
if (eventType == XmlPullParser.START_TAG) {
|
||||
when (it.name) {
|
||||
"vector" -> VectorDrawableCompat.createFromXmlInner(
|
||||
resources,
|
||||
it,
|
||||
Xml.asAttributeSet(it),
|
||||
theme
|
||||
)
|
||||
else -> null
|
||||
}
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
} else {
|
||||
null
|
||||
null
|
||||
}
|
||||
}
|
||||
} else {
|
||||
null
|
||||
null
|
||||
}
|
||||
} else {
|
||||
null
|
||||
}
|
||||
return drawable ?: ContextCompat.getDrawable(this, resId)!!
|
||||
return drawable ?: ContextCompat.getDrawable(this, resId)!!
|
||||
}
|
||||
|
||||
fun Context.getColorFromAttr(attrResId: Int): ColorStateList {
|
||||
val typedArray = obtainStyledAttributes(intArrayOf(attrResId))
|
||||
val (colorStateList, resId) = try {
|
||||
Pair(typedArray.getColorStateList(0), typedArray.getResourceId(0, 0))
|
||||
} finally {
|
||||
typedArray.recycle()
|
||||
}
|
||||
return colorStateList ?: ContextCompat.getColorStateList(this, resId)!!
|
||||
val typedArray = obtainStyledAttributes(intArrayOf(attrResId))
|
||||
val (colorStateList, resId) = try {
|
||||
Pair(typedArray.getColorStateList(0), typedArray.getResourceId(0, 0))
|
||||
} finally {
|
||||
typedArray.recycle()
|
||||
}
|
||||
return colorStateList ?: ContextCompat.getColorStateList(this, resId)!!
|
||||
}
|
||||
|
||||
fun Context.getDrawableFromAttr(attrResId: Int): Drawable {
|
||||
val typedArray = obtainStyledAttributes(intArrayOf(attrResId))
|
||||
val resId = try {
|
||||
typedArray.getResourceId(0, 0)
|
||||
} finally {
|
||||
typedArray.recycle()
|
||||
}
|
||||
return getDrawableCompat(resId)
|
||||
val typedArray = obtainStyledAttributes(intArrayOf(attrResId))
|
||||
val resId = try {
|
||||
typedArray.getResourceId(0, 0)
|
||||
} finally {
|
||||
typedArray.recycle()
|
||||
}
|
||||
return getDrawableCompat(resId)
|
||||
}
|
||||
|
||||
fun Resources.sizeScaled(size: Int): Int {
|
||||
return (size * displayMetrics.density).roundToInt()
|
||||
return (size * displayMetrics.density).roundToInt()
|
||||
}
|
||||
|
||||
fun TextView.setTextSizeScaled(size: Int) {
|
||||
val realSize = (size * resources.displayMetrics.scaledDensity).roundToInt()
|
||||
setTextSize(TypedValue.COMPLEX_UNIT_PX, realSize.toFloat())
|
||||
val realSize = (size * resources.displayMetrics.scaledDensity).roundToInt()
|
||||
setTextSize(TypedValue.COMPLEX_UNIT_PX, realSize.toFloat())
|
||||
}
|
||||
|
||||
fun ViewGroup.inflate(layoutResId: Int): View {
|
||||
return LayoutInflater.from(context).inflate(layoutResId, this, false)
|
||||
return LayoutInflater.from(context).inflate(layoutResId, this, false)
|
||||
}
|
||||
|
||||
fun ImageView.load(uri: Uri, builder: RequestCreator.() -> Unit) {
|
||||
Picasso.get().load(uri).noFade().apply(builder).into(this)
|
||||
Picasso.get().load(uri).noFade().apply(builder).into(this)
|
||||
}
|
||||
|
||||
fun ImageView.clear() {
|
||||
Picasso.get().cancelRequest(this)
|
||||
Picasso.get().cancelRequest(this)
|
||||
}
|
||||
|
@ -1,59 +1,62 @@
|
||||
@file:Suppress("PackageDirectoryMismatch")
|
||||
|
||||
package com.looker.droidify.utility.extension.text
|
||||
|
||||
import android.util.Log
|
||||
import java.util.Locale
|
||||
import java.util.*
|
||||
|
||||
fun <T: CharSequence> T.nullIfEmpty(): T? {
|
||||
return if (isNullOrEmpty()) null else this
|
||||
fun <T : CharSequence> T.nullIfEmpty(): T? {
|
||||
return if (isNullOrEmpty()) null else this
|
||||
}
|
||||
|
||||
private val sizeFormats = listOf("%.0f B", "%.0f kB", "%.1f MB", "%.2f GB")
|
||||
|
||||
fun Long.formatSize(): String {
|
||||
val (size, index) = generateSequence(Pair(this.toFloat(), 0)) { (size, index) -> if (size >= 1000f)
|
||||
Pair(size / 1000f, index + 1) else null }.take(sizeFormats.size).last()
|
||||
return sizeFormats[index].format(Locale.US, size)
|
||||
val (size, index) = generateSequence(Pair(this.toFloat(), 0)) { (size, index) ->
|
||||
if (size >= 1000f)
|
||||
Pair(size / 1000f, index + 1) else null
|
||||
}.take(sizeFormats.size).last()
|
||||
return sizeFormats[index].format(Locale.US, size)
|
||||
}
|
||||
|
||||
fun Char.halfByte(): Int {
|
||||
return when (this) {
|
||||
in '0' .. '9' -> this - '0'
|
||||
in 'a' .. 'f' -> this - 'a' + 10
|
||||
in 'A' .. 'F' -> this - 'A' + 10
|
||||
else -> -1
|
||||
}
|
||||
return when (this) {
|
||||
in '0'..'9' -> this - '0'
|
||||
in 'a'..'f' -> this - 'a' + 10
|
||||
in 'A'..'F' -> this - 'A' + 10
|
||||
else -> -1
|
||||
}
|
||||
}
|
||||
|
||||
fun CharSequence.unhex(): ByteArray? {
|
||||
return if (length % 2 == 0) {
|
||||
val ints = windowed(2, 2, false).map {
|
||||
val high = it[0].halfByte()
|
||||
val low = it[1].halfByte()
|
||||
if (high >= 0 && low >= 0) {
|
||||
(high shl 4) or low
|
||||
} else {
|
||||
-1
|
||||
}
|
||||
return if (length % 2 == 0) {
|
||||
val ints = windowed(2, 2, false).map {
|
||||
val high = it[0].halfByte()
|
||||
val low = it[1].halfByte()
|
||||
if (high >= 0 && low >= 0) {
|
||||
(high shl 4) or low
|
||||
} else {
|
||||
-1
|
||||
}
|
||||
}
|
||||
if (ints.any { it < 0 }) null else ints.map { it.toByte() }.toByteArray()
|
||||
} else {
|
||||
null
|
||||
}
|
||||
if (ints.any { it < 0 }) null else ints.map { it.toByte() }.toByteArray()
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
fun ByteArray.hex(): String {
|
||||
val builder = StringBuilder()
|
||||
for (byte in this) {
|
||||
builder.append("%02x".format(Locale.US, byte.toInt() and 0xff))
|
||||
}
|
||||
return builder.toString()
|
||||
val builder = StringBuilder()
|
||||
for (byte in this) {
|
||||
builder.append("%02x".format(Locale.US, byte.toInt() and 0xff))
|
||||
}
|
||||
return builder.toString()
|
||||
}
|
||||
|
||||
fun Any.debug(message: String) {
|
||||
val tag = this::class.java.name.let {
|
||||
val index = it.lastIndexOf('.')
|
||||
if (index >= 0) it.substring(index + 1) else it
|
||||
}.replace('$', '.')
|
||||
Log.d(tag, message)
|
||||
val tag = this::class.java.name.let {
|
||||
val index = it.lastIndexOf('.')
|
||||
if (index >= 0) it.substring(index + 1) else it
|
||||
}.replace('$', '.')
|
||||
Log.d(tag, message)
|
||||
}
|
||||
|
Reference in New Issue
Block a user