diff --git a/README.md b/README.md index 0b2dbff..d0328ee 100644 --- a/README.md +++ b/README.md @@ -57,15 +57,15 @@ Then add the following dependencies in your app `build.gradle` or `build.gradle. **Groovy** ```groovy -def sentinelVersion = "1.3.3" +def sentinelVersion = "1.4.0" debugImplementation "com.infinum.sentinel:sentinel:$sentinelVersion" -releaseImplementation "com.infinum.sentinel:sentinel-noop:$sentinelVersion" +releaseImplementation "com.infinum.sentinel:sentinel-no-op:$sentinelVersion" ``` **KotlinDSL** ```kotlin -val sentinelVersion = "1.3.3" +val sentinelVersion = "1.4.0" debugImplementation("com.infinum.sentinel:sentinel:$sentinelVersion") releaseImplementation("com.infinum.sentinel:sentinel-no-op:$sentinelVersion") ``` diff --git a/config.gradle b/config.gradle index f1557fe..dfb8f51 100644 --- a/config.gradle +++ b/config.gradle @@ -1,7 +1,7 @@ ext { def major = 1 - def minor = 3 - def patch = 3 + def minor = 4 + def patch = 0 buildConfig = [ "minSdk" : 21, diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 1dadde7..0ab73b9 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,5 +1,5 @@ [versions] -sentinel = "1.3.3" +sentinel = "1.4.0" gradle = "8.3.2" kotlin = "1.9.22" coroutines = "1.8.0" diff --git a/sentinel/src/main/AndroidManifest.xml b/sentinel/src/main/AndroidManifest.xml index 5d3da56..61a5144 100644 --- a/sentinel/src/main/AndroidManifest.xml +++ b/sentinel/src/main/AndroidManifest.xml @@ -3,12 +3,17 @@ + + - - - - diff --git a/sentinel/src/main/kotlin/com/infinum/sentinel/data/models/raw/PreferencesData.kt b/sentinel/src/main/kotlin/com/infinum/sentinel/data/models/raw/PreferencesData.kt index 627390d..16ac160 100644 --- a/sentinel/src/main/kotlin/com/infinum/sentinel/data/models/raw/PreferencesData.kt +++ b/sentinel/src/main/kotlin/com/infinum/sentinel/data/models/raw/PreferencesData.kt @@ -2,5 +2,7 @@ package com.infinum.sentinel.data.models.raw internal data class PreferencesData( val name: String, - val values: List> + val values: List>, + val isSortedAscending: Boolean = false, + val isExpanded: Boolean = true, ) diff --git a/sentinel/src/main/kotlin/com/infinum/sentinel/data/sources/raw/collectors/PreferencesCollector.kt b/sentinel/src/main/kotlin/com/infinum/sentinel/data/sources/raw/collectors/PreferencesCollector.kt index 882f71c..b9660d7 100644 --- a/sentinel/src/main/kotlin/com/infinum/sentinel/data/sources/raw/collectors/PreferencesCollector.kt +++ b/sentinel/src/main/kotlin/com/infinum/sentinel/data/sources/raw/collectors/PreferencesCollector.kt @@ -29,6 +29,8 @@ internal class PreferencesCollector( prefsDirectory.list().orEmpty().toList().map { it.removeSuffix(PREFS_SUFFIX) } } else { listOf() + }.sortedBy { name -> + name }.map { name -> val allPrefs = getSharedPreferences(name, MODE_PRIVATE).all val tuples = allPrefs.keys.toSet().mapNotNull { diff --git a/sentinel/src/main/kotlin/com/infinum/sentinel/di/LibraryComponents.kt b/sentinel/src/main/kotlin/com/infinum/sentinel/di/LibraryComponents.kt index 72d71cb..68800d7 100644 --- a/sentinel/src/main/kotlin/com/infinum/sentinel/di/LibraryComponents.kt +++ b/sentinel/src/main/kotlin/com/infinum/sentinel/di/LibraryComponents.kt @@ -42,6 +42,7 @@ internal object LibraryComponents { tools.filterIsInstance().firstOrNull()?.userCertificates.orEmpty(), onTriggered ) + WorkManagerInitializer.init(domainComponent) domainComponent.setup() } diff --git a/sentinel/src/main/kotlin/com/infinum/sentinel/di/WorkManagerInitializer.kt b/sentinel/src/main/kotlin/com/infinum/sentinel/di/WorkManagerInitializer.kt new file mode 100644 index 0000000..49f278c --- /dev/null +++ b/sentinel/src/main/kotlin/com/infinum/sentinel/di/WorkManagerInitializer.kt @@ -0,0 +1,14 @@ +package com.infinum.sentinel.di + +import com.infinum.sentinel.di.component.DomainComponent +import com.infinum.sentinel.ui.certificates.observer.CertificateCheckWorker +import com.infinum.sentinel.ui.certificates.observer.DelegateWorker +import com.infinum.sentinel.ui.certificates.observer.SentinelWorkerFactory + +internal object WorkManagerInitializer { + + fun init(domainComponent: DomainComponent) { + DelegateWorker.workerFactories[CertificateCheckWorker.NAME] = + SentinelWorkerFactory(domainComponent.collectors, domainComponent.notificationFactory) + } +} diff --git a/sentinel/src/main/kotlin/com/infinum/sentinel/di/component/DomainComponent.kt b/sentinel/src/main/kotlin/com/infinum/sentinel/di/component/DomainComponent.kt index 2301cae..f994549 100644 --- a/sentinel/src/main/kotlin/com/infinum/sentinel/di/component/DomainComponent.kt +++ b/sentinel/src/main/kotlin/com/infinum/sentinel/di/component/DomainComponent.kt @@ -50,7 +50,6 @@ import com.infinum.sentinel.ui.bundles.callbacks.BundleMonitorNotificationCallba import com.infinum.sentinel.ui.bundles.details.BundleDetailsActivity import com.infinum.sentinel.ui.certificates.observer.CertificatesObserver import com.infinum.sentinel.ui.certificates.observer.SentinelWorkManager -import com.infinum.sentinel.ui.certificates.observer.SentinelWorkerFactory import com.infinum.sentinel.ui.crash.anr.SentinelAnrObserver import com.infinum.sentinel.ui.crash.anr.SentinelAnrObserverRunnable import com.infinum.sentinel.ui.crash.anr.SentinelUiAnrObserver @@ -91,8 +90,6 @@ internal abstract class DomainComponent( abstract val sentinelAnrObserver: SentinelAnrObserver - abstract val sentinelWorkerFactory: SentinelWorkerFactory - abstract val sentinelWorkManager: SentinelWorkManager abstract val sentinelAnrObserverRunnable: SentinelAnrObserverRunnable @@ -157,15 +154,10 @@ internal abstract class DomainComponent( fun sentinelAnrObserverRunnable(dao: CrashesDao): SentinelAnrObserverRunnable = SentinelAnrObserverRunnable(context, notificationFactory, dao) - @Provides - @DomainScope - fun sentinelWorkerFactory(): SentinelWorkerFactory = - SentinelWorkerFactory(collectors, notificationFactory) - @Provides @DomainScope fun sentinelWorkManager(): SentinelWorkManager = - SentinelWorkManager(context, sentinelWorkerFactory) + SentinelWorkManager(context) @Provides @DomainScope diff --git a/sentinel/src/main/kotlin/com/infinum/sentinel/ui/certificates/observer/DelegateWorker.kt b/sentinel/src/main/kotlin/com/infinum/sentinel/ui/certificates/observer/DelegateWorker.kt new file mode 100644 index 0000000..318599d --- /dev/null +++ b/sentinel/src/main/kotlin/com/infinum/sentinel/ui/certificates/observer/DelegateWorker.kt @@ -0,0 +1,54 @@ +package com.infinum.sentinel.ui.certificates.observer + +import android.content.Context +import androidx.annotation.UiThread +import androidx.work.ListenableWorker +import androidx.work.WorkerFactory +import androidx.work.WorkerParameters +import com.google.common.util.concurrent.ListenableFuture +import com.infinum.sentinel.ui.shared.Constants.Keys.WORKER_CLASS_NAME +import com.infinum.sentinel.ui.shared.Constants.Keys.WORKER_ID + +/** + * A worker to delegate work requests from within the library to workers + * that require factories with custom dependencies. + */ +internal class DelegateWorker( + appContext: Context, + parameters: WorkerParameters, +) : ListenableWorker(appContext, parameters) { + + private val workerClassName = + parameters.inputData.getString(WORKER_CLASS_NAME) ?: "" + private val workerId = parameters.inputData.getString(WORKER_ID) + private val delegateWorkerFactory = workerFactories[workerId] + private val delegatedWorker = delegateWorkerFactory?.createWorker(appContext, workerClassName, parameters) + + override fun startWork(): ListenableFuture { + return if (delegatedWorker != null) { + delegatedWorker.startWork() + } else { + val errorMessage = "No delegateWorker available for $workerId" + + " with workerClassName of $workerClassName. Is the " + + "DelegateWorker.workerFactories populated correctly?" + throw IllegalStateException(errorMessage) + } + } + + companion object { + const val DELEGATE_WORKER_ID = "com.infinum.sentinel.ui.certificates.observer.CertificateCheckWorker" + + val workerFactories = object : AbstractMutableMap() { + + private val backingWorkerMap = mutableMapOf() + + @UiThread + override fun put(key: String, value: WorkerFactory): WorkerFactory? { + return backingWorkerMap.put(key, value) + } + + override val entries: MutableSet> + get() = backingWorkerMap.entries + } + } +} diff --git a/sentinel/src/main/kotlin/com/infinum/sentinel/ui/certificates/observer/SentinelWorkManager.kt b/sentinel/src/main/kotlin/com/infinum/sentinel/ui/certificates/observer/SentinelWorkManager.kt index 944d7b3..6d3103e 100644 --- a/sentinel/src/main/kotlin/com/infinum/sentinel/ui/certificates/observer/SentinelWorkManager.kt +++ b/sentinel/src/main/kotlin/com/infinum/sentinel/ui/certificates/observer/SentinelWorkManager.kt @@ -4,14 +4,12 @@ import android.content.Context import android.os.Build import androidx.annotation.RequiresApi import androidx.lifecycle.asFlow -import androidx.work.Configuration import androidx.work.Constraints import androidx.work.ExistingPeriodicWorkPolicy import androidx.work.NetworkType import androidx.work.PeriodicWorkRequestBuilder import androidx.work.WorkInfo import androidx.work.WorkManager -import androidx.work.WorkerFactory import androidx.work.workDataOf import com.infinum.sentinel.BuildConfig import com.infinum.sentinel.data.models.local.CertificateMonitorEntity @@ -19,6 +17,8 @@ import com.infinum.sentinel.ui.shared.Constants.Keys.EXPIRE_IN_AMOUNT import com.infinum.sentinel.ui.shared.Constants.Keys.EXPIRE_IN_UNIT import com.infinum.sentinel.ui.shared.Constants.Keys.NOTIFY_INVALID_NOW import com.infinum.sentinel.ui.shared.Constants.Keys.NOTIFY_TO_EXPIRE +import com.infinum.sentinel.ui.shared.Constants.Keys.WORKER_CLASS_NAME +import com.infinum.sentinel.ui.shared.Constants.Keys.WORKER_ID import java.time.Duration import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.flowOf @@ -28,7 +28,6 @@ import me.tatarka.inject.annotations.Inject @Inject internal class SentinelWorkManager( private val context: Context, - private val workerFactory: WorkerFactory ) { companion object { @@ -36,36 +35,27 @@ internal class SentinelWorkManager( private const val RELEASE_INTERVAL = 1440L } - init { - WorkManager.initialize( - context, - Configuration.Builder() - .setMinimumLoggingLevel(android.util.Log.INFO) - .setWorkerFactory(workerFactory) - .build() - ) - } - @RequiresApi(Build.VERSION_CODES.O) - fun startCertificatesCheck(entity: CertificateMonitorEntity) = + fun startCertificatesCheck(entity: CertificateMonitorEntity) { + + val delegatedWorkData = workDataOf( + WORKER_CLASS_NAME to CertificateCheckWorker::class.qualifiedName, + WORKER_ID to CertificateCheckWorker.NAME, + NOTIFY_INVALID_NOW to entity.notifyInvalidNow, + NOTIFY_TO_EXPIRE to entity.notifyToExpire, + EXPIRE_IN_AMOUNT to entity.expireInAmount, + EXPIRE_IN_UNIT to entity.expireInUnit.name + ) WorkManager.getInstance(context) .enqueueUniquePeriodicWork( - CertificateCheckWorker.NAME, + DelegateWorker.DELEGATE_WORKER_ID, ExistingPeriodicWorkPolicy.CANCEL_AND_REENQUEUE, - PeriodicWorkRequestBuilder( + PeriodicWorkRequestBuilder( when (BuildConfig.DEBUG) { true -> Duration.ofMinutes(DEBUG_INTERVAL) false -> Duration.ofMinutes(RELEASE_INTERVAL) } - ) - .setInputData( - workDataOf( - NOTIFY_INVALID_NOW to entity.notifyInvalidNow, - NOTIFY_TO_EXPIRE to entity.notifyToExpire, - EXPIRE_IN_AMOUNT to entity.expireInAmount, - EXPIRE_IN_UNIT to entity.expireInUnit.name - ) - ) + ).setInputData(delegatedWorkData) .setConstraints( Constraints.Builder() .setRequiredNetworkType(NetworkType.NOT_REQUIRED) @@ -80,6 +70,7 @@ internal class SentinelWorkManager( ) .build() ) + } fun certificatesCheckState(): Flow = WorkManager.getInstance(context) diff --git a/sentinel/src/main/kotlin/com/infinum/sentinel/ui/main/preferences/PreferencesFragment.kt b/sentinel/src/main/kotlin/com/infinum/sentinel/ui/main/preferences/PreferencesFragment.kt index 8ec5464..d1337e5 100644 --- a/sentinel/src/main/kotlin/com/infinum/sentinel/ui/main/preferences/PreferencesFragment.kt +++ b/sentinel/src/main/kotlin/com/infinum/sentinel/ui/main/preferences/PreferencesFragment.kt @@ -64,32 +64,51 @@ internal class PreferencesFragment : } } - @Suppress("UNCHECKED_CAST") private fun createItemView(data: PreferencesData): View = SentinelViewItemPreferenceBinding.inflate(layoutInflater, binding.contentLayout, false) .apply { nameView.text = data.name - data.values.forEach { tuple -> - prefsLayout.addView( - SentinelViewItemTextBinding.inflate(layoutInflater, prefsLayout, false) - .apply { - labelView.isAllCaps = false - labelView.text = tuple.second - valueView.text = tuple.third.toString() - root.setOnClickListener { _ -> - viewModel.cache( - data.name, - tuple - ) - } - root.setOnLongClickListener { - it.context.copyToClipboard( - key = tuple.second, - value = tuple.third.toString() - ) - } - }.root - ) + sortImageView.setOnClickListener { + viewModel.onSortClicked(data) + } + hideExpandImageView.setOnClickListener { + viewModel.onHideExpandClicked(data) + } + + if (data.isExpanded) { + prefsLayout.visibility = View.VISIBLE + sortImageView.visibility = View.VISIBLE + hideExpandImageView.setImageResource(R.drawable.sentinel_ic_minus) + showPreferenceData(data) + } else { + prefsLayout.visibility = View.GONE + sortImageView.visibility = View.GONE + hideExpandImageView.setImageResource(R.drawable.sentinel_ic_plus) } }.root + + private fun SentinelViewItemPreferenceBinding.showPreferenceData(data: PreferencesData) { + data.values.forEach { (preferenceType, label, value) -> + prefsLayout.addView( + SentinelViewItemTextBinding.inflate(layoutInflater, prefsLayout, false) + .apply { + labelView.isAllCaps = false + labelView.text = label + valueView.text = value.toString() + root.setOnClickListener { _ -> + viewModel.cache( + name = data.name, + tuple = Triple(preferenceType, label, value) + ) + } + root.setOnLongClickListener { + it.context.copyToClipboard( + key = label, + value = value.toString() + ) + } + }.root + ) + } + } } diff --git a/sentinel/src/main/kotlin/com/infinum/sentinel/ui/main/preferences/PreferencesViewModel.kt b/sentinel/src/main/kotlin/com/infinum/sentinel/ui/main/preferences/PreferencesViewModel.kt index 0299b8c..b81e27e 100644 --- a/sentinel/src/main/kotlin/com/infinum/sentinel/ui/main/preferences/PreferencesViewModel.kt +++ b/sentinel/src/main/kotlin/com/infinum/sentinel/ui/main/preferences/PreferencesViewModel.kt @@ -1,6 +1,7 @@ package com.infinum.sentinel.ui.main.preferences import com.infinum.sentinel.data.models.raw.PreferenceType +import com.infinum.sentinel.data.models.raw.PreferencesData import com.infinum.sentinel.domain.Factories import com.infinum.sentinel.domain.Repositories import com.infinum.sentinel.domain.preference.models.PreferenceParameters @@ -38,4 +39,40 @@ internal class PreferencesViewModel( } emitEvent(PreferencesEvent.Cached()) } + + fun onSortClicked(data: PreferencesData) { + val currentValues = (stateFlow.value as? PreferencesState.Data)?.value.orEmpty() + val sortedData = if (data.isSortedAscending) { + data.values.sortedByDescending { it.second } + } else { + data.values.sortedBy { it.second } + } + val changedValues = currentValues.map { preferencesData -> + if (preferencesData.name == data.name) { + preferencesData.copy( + values = sortedData, + isSortedAscending = !preferencesData.isSortedAscending + ) + } else { + preferencesData + } + } + setState(PreferencesState.Data(value = changedValues)) + } + + fun onHideExpandClicked(data: PreferencesData) { + (stateFlow.value as? PreferencesState.Data)?.let { state -> + val currentValues = state.value + val changedValues = currentValues.map { preferencesData -> + if (preferencesData.name == data.name) { + preferencesData.copy( + isExpanded = !preferencesData.isExpanded + ) + } else { + preferencesData + } + } + setState(PreferencesState.Data(value = changedValues)) + } + } } diff --git a/sentinel/src/main/kotlin/com/infinum/sentinel/ui/shared/Constants.kt b/sentinel/src/main/kotlin/com/infinum/sentinel/ui/shared/Constants.kt index 3e4004f..5ae33f0 100644 --- a/sentinel/src/main/kotlin/com/infinum/sentinel/ui/shared/Constants.kt +++ b/sentinel/src/main/kotlin/com/infinum/sentinel/ui/shared/Constants.kt @@ -14,5 +14,7 @@ internal object Constants { const val NOTIFY_TO_EXPIRE: String = "KEY_NOTIFY_TO_EXPIRE" const val EXPIRE_IN_AMOUNT: String = "KEY_EXPIRE_IN_AMOUNT" const val EXPIRE_IN_UNIT: String = "KEY_EXPIRE_IN_UNIT" + const val WORKER_CLASS_NAME = "WORKER_CLASS_NAME" + const val WORKER_ID = "WORKER_ID" } } diff --git a/sentinel/src/main/res/drawable/sentinel_ic_sort.xml b/sentinel/src/main/res/drawable/sentinel_ic_sort.xml new file mode 100644 index 0000000..7e84ccd --- /dev/null +++ b/sentinel/src/main/res/drawable/sentinel_ic_sort.xml @@ -0,0 +1,11 @@ + + + + + diff --git a/sentinel/src/main/res/layout/sentinel_view_item_preference.xml b/sentinel/src/main/res/layout/sentinel_view_item_preference.xml index b894bb5..d1bbd0d 100644 --- a/sentinel/src/main/res/layout/sentinel_view_item_preference.xml +++ b/sentinel/src/main/res/layout/sentinel_view_item_preference.xml @@ -1,18 +1,49 @@ - + android:orientation="horizontal" + android:paddingBottom="8dp"> + + + + + + + + + + Fragment arguments Fragment Saved State + Sort preferences by name + Hide/expand the preferences Preferences editor Key Current value diff --git a/tool-timber/src/main/kotlin/com/infinum/sentinel/SentinelFileTree.kt b/tool-timber/src/main/kotlin/com/infinum/sentinel/SentinelFileTree.kt index b09cb45..3fdbde5 100644 --- a/tool-timber/src/main/kotlin/com/infinum/sentinel/SentinelFileTree.kt +++ b/tool-timber/src/main/kotlin/com/infinum/sentinel/SentinelFileTree.kt @@ -4,9 +4,11 @@ import android.content.Context import com.infinum.sentinel.ui.logger.models.BaseEntry import com.infinum.sentinel.ui.logger.models.FlowBuffer import com.infinum.sentinel.ui.logger.models.Level +import com.infinum.sentinel.ui.shared.TimberToolConstants.LOG_DATE_TIME_FORMAT import com.infinum.sentinel.ui.shared.LogFileResolver import java.io.File -import java.util.Calendar +import java.text.SimpleDateFormat +import java.util.Locale import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.MainScope import kotlinx.coroutines.launch @@ -21,6 +23,8 @@ internal class SentinelFileTree( private val logFileResolver = LogFileResolver(context) override fun log(priority: Int, tag: String?, message: String, t: Throwable?) { + val dateTimeFormat = SimpleDateFormat(LOG_DATE_TIME_FORMAT, Locale.getDefault()) + MainScope().launch { withContext(Dispatchers.IO) { val entry = Entry( @@ -34,7 +38,7 @@ internal class SentinelFileTree( buffer.enqueue(entry) val file: File = logFileResolver.createOrOpenFile() - val line = entry.asLineString() + val line = entry.asLineString(dateTimeFormat) file.appendText(line) } diff --git a/tool-timber/src/main/kotlin/com/infinum/sentinel/TimberInitializer.kt b/tool-timber/src/main/kotlin/com/infinum/sentinel/TimberInitializer.kt index 8125b82..804a728 100644 --- a/tool-timber/src/main/kotlin/com/infinum/sentinel/TimberInitializer.kt +++ b/tool-timber/src/main/kotlin/com/infinum/sentinel/TimberInitializer.kt @@ -3,7 +3,6 @@ package com.infinum.sentinel import android.content.Context import androidx.startup.Initializer import com.infinum.sentinel.ui.logger.models.FlowBuffer -import kotlinx.coroutines.MainScope import timber.log.Timber public class TimberInitializer : Initializer> { diff --git a/tool-timber/src/main/kotlin/com/infinum/sentinel/ui/logger/LoggerActivity.kt b/tool-timber/src/main/kotlin/com/infinum/sentinel/ui/logger/LoggerActivity.kt index fc6a2c9..c0235f9 100644 --- a/tool-timber/src/main/kotlin/com/infinum/sentinel/ui/logger/LoggerActivity.kt +++ b/tool-timber/src/main/kotlin/com/infinum/sentinel/ui/logger/LoggerActivity.kt @@ -36,7 +36,6 @@ import kotlinx.coroutines.launch import kotlinx.coroutines.withContext import timber.log.Timber - public class LoggerActivity : AppCompatActivity() { private companion object { @@ -94,21 +93,21 @@ public class LoggerActivity : AppCompatActivity() { with(binding) { toolbar.setNavigationOnClickListener { finish() } toolbar.subtitle = ( - packageManager.getApplicationLabel( - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { - packageManager.getApplicationInfo( - packageName, - PackageManager.ApplicationInfoFlags.of(PackageManager.GET_META_DATA.toLong()) - ) - } else { - @Suppress("DEPRECATION") - packageManager.getApplicationInfo( - packageName, - PackageManager.GET_META_DATA - ) - } - ) as? String - ) ?: getString(R.string.sentinel_name) + packageManager.getApplicationLabel( + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { + packageManager.getApplicationInfo( + packageName, + PackageManager.ApplicationInfoFlags.of(PackageManager.GET_META_DATA.toLong()) + ) + } else { + @Suppress("DEPRECATION") + packageManager.getApplicationInfo( + packageName, + PackageManager.GET_META_DATA + ) + } + ) as? String + ) ?: getString(R.string.sentinel_name) toolbar.setOnMenuItemClickListener { when (it.itemId) { R.id.search -> { diff --git a/tool-timber/src/main/kotlin/com/infinum/sentinel/ui/logger/LoggerAdapter.kt b/tool-timber/src/main/kotlin/com/infinum/sentinel/ui/logger/LoggerAdapter.kt index 46d750f..05c0004 100644 --- a/tool-timber/src/main/kotlin/com/infinum/sentinel/ui/logger/LoggerAdapter.kt +++ b/tool-timber/src/main/kotlin/com/infinum/sentinel/ui/logger/LoggerAdapter.kt @@ -5,12 +5,17 @@ import android.view.ViewGroup import androidx.recyclerview.widget.ListAdapter import com.infinum.sentinel.SentinelFileTree import com.infinum.sentinel.databinding.SentinelItemLogBinding +import com.infinum.sentinel.ui.shared.TimberToolConstants.LOG_DATE_TIME_FORMAT +import java.text.SimpleDateFormat +import java.util.Locale internal class LoggerAdapter( private val onListChanged: (Boolean) -> Unit, private val onClick: (SentinelFileTree.Entry) -> Unit ) : ListAdapter(LoggerDiffUtil()) { + private val dateTimeFormat = SimpleDateFormat(LOG_DATE_TIME_FORMAT, Locale.getDefault()) + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): LoggerViewHolder = LoggerViewHolder( SentinelItemLogBinding.inflate( @@ -21,7 +26,11 @@ internal class LoggerAdapter( ) override fun onBindViewHolder(holder: LoggerViewHolder, position: Int) { - holder.bind(getItem(position), onClick) + holder.bind( + item = getItem(position), + dateTimeFormat = dateTimeFormat, + onClick = onClick + ) } override fun onViewRecycled(holder: LoggerViewHolder) { diff --git a/tool-timber/src/main/kotlin/com/infinum/sentinel/ui/logger/LoggerViewHolder.kt b/tool-timber/src/main/kotlin/com/infinum/sentinel/ui/logger/LoggerViewHolder.kt index 5be2992..ce2361e 100644 --- a/tool-timber/src/main/kotlin/com/infinum/sentinel/ui/logger/LoggerViewHolder.kt +++ b/tool-timber/src/main/kotlin/com/infinum/sentinel/ui/logger/LoggerViewHolder.kt @@ -14,7 +14,11 @@ internal class LoggerViewHolder( private val binding: SentinelItemLogBinding ) : RecyclerView.ViewHolder(binding.root) { - fun bind(item: SentinelFileTree.Entry?, onClick: (SentinelFileTree.Entry) -> Unit) { + fun bind( + item: SentinelFileTree.Entry?, + dateTimeFormat: SimpleDateFormat, + onClick: (SentinelFileTree.Entry) -> Unit + ) { item?.let { entry -> with(binding) { levelView.setBackgroundColor( @@ -31,7 +35,7 @@ internal class LoggerViewHolder( } ) ) - timestampView.text = SimpleDateFormat.getDateTimeInstance().format(Date(entry.timestamp)) + timestampView.text = dateTimeFormat.format(Date(entry.timestamp)) tagView.text = entry.tag entry.stackTrace?.let { stackTraceView.text = it diff --git a/tool-timber/src/main/kotlin/com/infinum/sentinel/ui/logger/models/BaseEntry.kt b/tool-timber/src/main/kotlin/com/infinum/sentinel/ui/logger/models/BaseEntry.kt index 7ec2e2b..9feadbf 100644 --- a/tool-timber/src/main/kotlin/com/infinum/sentinel/ui/logger/models/BaseEntry.kt +++ b/tool-timber/src/main/kotlin/com/infinum/sentinel/ui/logger/models/BaseEntry.kt @@ -1,6 +1,7 @@ package com.infinum.sentinel.ui.logger.models -import java.io.File +import java.text.SimpleDateFormat +import java.util.Date import org.json.JSONObject internal open class BaseEntry( @@ -10,7 +11,6 @@ internal open class BaseEntry( open val message: String? = null, open val stackTrace: String? = null ) { - fun asJSONString(): String = JSONObject() .put("level", level) @@ -20,9 +20,9 @@ internal open class BaseEntry( .put("stackTrace", stackTrace) .toString() - fun asLineString(): String = + fun asLineString(dateTimeFormat: SimpleDateFormat): String = buildString { - append(timestamp) + append(dateTimeFormat.format(Date(timestamp))) append(" LEVEL: ") append(level) append(" TAG: ") diff --git a/tool-timber/src/main/kotlin/com/infinum/sentinel/ui/logger/models/FlowBuffer.kt b/tool-timber/src/main/kotlin/com/infinum/sentinel/ui/logger/models/FlowBuffer.kt index d51ee6d..19c1af5 100644 --- a/tool-timber/src/main/kotlin/com/infinum/sentinel/ui/logger/models/FlowBuffer.kt +++ b/tool-timber/src/main/kotlin/com/infinum/sentinel/ui/logger/models/FlowBuffer.kt @@ -28,8 +28,8 @@ internal class FlowBuffer { } else { queue.reversed().filter { it.tag?.lowercase()?.contains(query.lowercase()) == true || - it.message?.lowercase()?.contains(query.lowercase()) == true || - it.stackTrace?.lowercase()?.contains(query.lowercase()) == true + it.message?.lowercase()?.contains(query.lowercase()) == true || + it.stackTrace?.lowercase()?.contains(query.lowercase()) == true } } ) diff --git a/tool-timber/src/main/kotlin/com/infinum/sentinel/ui/logs/LogsActivity.kt b/tool-timber/src/main/kotlin/com/infinum/sentinel/ui/logs/LogsActivity.kt index 65a0da0..8f3e1e6 100644 --- a/tool-timber/src/main/kotlin/com/infinum/sentinel/ui/logs/LogsActivity.kt +++ b/tool-timber/src/main/kotlin/com/infinum/sentinel/ui/logs/LogsActivity.kt @@ -29,7 +29,6 @@ import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.launch import kotlinx.coroutines.withContext - public class LogsActivity : AppCompatActivity() { private companion object { @@ -79,21 +78,21 @@ public class LogsActivity : AppCompatActivity() { with(binding) { toolbar.setNavigationOnClickListener { finish() } toolbar.subtitle = ( - packageManager.getApplicationLabel( - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { - packageManager.getApplicationInfo( - packageName, - PackageManager.ApplicationInfoFlags.of(PackageManager.GET_META_DATA.toLong()) - ) - } else { - @Suppress("DEPRECATION") - packageManager.getApplicationInfo( - packageName, - PackageManager.GET_META_DATA - ) - } - ) as? String - ) ?: getString(R.string.sentinel_name) + packageManager.getApplicationLabel( + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { + packageManager.getApplicationInfo( + packageName, + PackageManager.ApplicationInfoFlags.of(PackageManager.GET_META_DATA.toLong()) + ) + } else { + @Suppress("DEPRECATION") + packageManager.getApplicationInfo( + packageName, + PackageManager.GET_META_DATA + ) + } + ) as? String + ) ?: getString(R.string.sentinel_name) recyclerView.layoutManager = LinearLayoutManager( recyclerView.context, diff --git a/tool-timber/src/main/kotlin/com/infinum/sentinel/ui/logs/LogsAdapter.kt b/tool-timber/src/main/kotlin/com/infinum/sentinel/ui/logs/LogsAdapter.kt index 8913309..55abf53 100644 --- a/tool-timber/src/main/kotlin/com/infinum/sentinel/ui/logs/LogsAdapter.kt +++ b/tool-timber/src/main/kotlin/com/infinum/sentinel/ui/logs/LogsAdapter.kt @@ -3,10 +3,11 @@ package com.infinum.sentinel.ui.logs import android.view.LayoutInflater import android.view.ViewGroup import androidx.recyclerview.widget.ListAdapter -import com.infinum.sentinel.SentinelFileTree -import com.infinum.sentinel.databinding.SentinelItemLogBinding import com.infinum.sentinel.databinding.SentinelItemLogFileBinding +import com.infinum.sentinel.ui.shared.TimberToolConstants.LOG_DATE_TIME_FORMAT import java.io.File +import java.text.SimpleDateFormat +import java.util.Locale internal class LogsAdapter( private val onListChanged: (Boolean) -> Unit, @@ -14,6 +15,8 @@ internal class LogsAdapter( private val onShare: (File) -> Unit ) : ListAdapter(LogsDiffUtil()) { + private val dateTimeFormat = SimpleDateFormat(LOG_DATE_TIME_FORMAT, Locale.getDefault()) + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): LogsViewHolder = LogsViewHolder( SentinelItemLogFileBinding.inflate( @@ -24,7 +27,12 @@ internal class LogsAdapter( ) override fun onBindViewHolder(holder: LogsViewHolder, position: Int) { - holder.bind(getItem(position), onDelete, onShare) + holder.bind( + item = getItem(position), + dateTimeFormat = dateTimeFormat, + onDelete = onDelete, + onShare = onShare + ) } override fun onViewRecycled(holder: LogsViewHolder) { diff --git a/tool-timber/src/main/kotlin/com/infinum/sentinel/ui/logs/LogsViewHolder.kt b/tool-timber/src/main/kotlin/com/infinum/sentinel/ui/logs/LogsViewHolder.kt index 61c2d60..da7df10 100644 --- a/tool-timber/src/main/kotlin/com/infinum/sentinel/ui/logs/LogsViewHolder.kt +++ b/tool-timber/src/main/kotlin/com/infinum/sentinel/ui/logs/LogsViewHolder.kt @@ -1,12 +1,7 @@ package com.infinum.sentinel.ui.logs -import androidx.core.content.ContextCompat -import androidx.core.view.isVisible import androidx.recyclerview.widget.RecyclerView -import com.infinum.sentinel.R -import com.infinum.sentinel.SentinelFileTree import com.infinum.sentinel.databinding.SentinelItemLogFileBinding -import com.infinum.sentinel.ui.logger.models.Level import java.io.File import java.text.SimpleDateFormat import java.util.Date @@ -15,11 +10,11 @@ internal class LogsViewHolder( private val binding: SentinelItemLogFileBinding ) : RecyclerView.ViewHolder(binding.root) { - fun bind(item: File?, onDelete: (File) -> Unit, onShare: (File) -> Unit) { + fun bind(item: File?, dateTimeFormat: SimpleDateFormat, onDelete: (File) -> Unit, onShare: (File) -> Unit) { item?.let { entry -> with(binding) { messageView.text = entry.name - timestampView.text = SimpleDateFormat.getDateTimeInstance().format(Date(entry.lastModified())) + timestampView.text = dateTimeFormat.format(Date(entry.lastModified())) deleteButton.setOnClickListener { onDelete(entry) } shareButton.setOnClickListener { onShare(entry) } } diff --git a/tool-timber/src/main/kotlin/com/infinum/sentinel/ui/shared/TimberToolConstants.kt b/tool-timber/src/main/kotlin/com/infinum/sentinel/ui/shared/TimberToolConstants.kt new file mode 100644 index 0000000..1ae9d57 --- /dev/null +++ b/tool-timber/src/main/kotlin/com/infinum/sentinel/ui/shared/TimberToolConstants.kt @@ -0,0 +1,5 @@ +package com.infinum.sentinel.ui.shared + +internal object TimberToolConstants { + const val LOG_DATE_TIME_FORMAT = "yyyy-MM-dd'T'HH:mm:ss.SSSZ" +}