fix: migrate to SLF4J and Logback logging implementations

This commit is contained in:
Akash Yadav
2024-03-17 00:04:43 +05:30
parent 940c7b0aa2
commit ab101fb59d
409 changed files with 2735 additions and 2731 deletions
+6 -1
View File
@@ -1,11 +1,16 @@
<component name="InspectionProjectProfileManager">
<profile version="1.0">
<option name="myName" value="Project Default" />
<inspection_tool class="AndroidLintUnsafeImplicitIntentLaunch" enabled="false" level="ERROR" enabled_by_default="false" />
<inspection_tool class="InstanceofChain" enabled="true" level="WARNING" enabled_by_default="true">
<option name="ignoreInstanceofOnLibraryClasses" value="false" />
</inspection_tool>
<inspection_tool class="KotlinLoggerInitializedWithForeignClass" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="LoggingConditionDisagreesWithLogLevelStatement" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="SpellCheckingInspection" enabled="true" level="INFORMATION" enabled_by_default="true">
<option name="processCode" value="true" />
<option name="processLiterals" value="true" />
<option name="processComments" value="true" />
</inspection_tool>
<inspection_tool class="StringConcatenationArgumentToLogCall" enabled="true" level="ERROR" enabled_by_default="true" editorAttributes="ERRORS_ATTRIBUTES" />
</profile>
</component>
@@ -26,7 +26,6 @@ import com.itsaky.androidide.actions.ActionsRegistry
import com.itsaky.androidide.actions.FillMenuParams
import com.itsaky.androidide.actions.OnActionClickListener
import com.itsaky.androidide.actions.locations.CodeActionsMenu
import com.itsaky.androidide.utils.ILogger
import com.itsaky.androidide.utils.withStopWatch
import kotlinx.coroutines.CoroutineName
import kotlinx.coroutines.CoroutineScope
@@ -35,6 +34,7 @@ import kotlinx.coroutines.Job
import kotlinx.coroutines.launch
import kotlinx.coroutines.plus
import kotlinx.coroutines.withContext
import org.slf4j.LoggerFactory
import java.util.concurrent.ConcurrentHashMap
/**
@@ -45,13 +45,16 @@ import java.util.concurrent.ConcurrentHashMap
@AutoService(ActionsRegistry::class)
class DefaultActionsRegistry : ActionsRegistry() {
private val log = ILogger.newInstance("DefaultActionsRegistry")
private val actions = ConcurrentHashMap<String, ConcurrentHashMap<String, ActionItem>>()
private val listeners = HashSet<ActionExecListener>()
private val actionsCoroutineScope = CoroutineScope(Dispatchers.Default) +
CoroutineName("DefaultActionsRegistry")
companion object {
private val log = LoggerFactory.getLogger(DefaultActionsRegistry::class.java)
}
init {
registerAction(CodeActionsMenu)
}
@@ -217,7 +220,7 @@ class DefaultActionsRegistry : ActionsRegistry() {
}.also { job ->
job.invokeOnCompletion { error ->
if (error != null) {
log.error("An error occurred when performing action '${action.id}'", error)
log.error("An error occurred when performing action '{}'", action.id, error)
}
}
}
+22 -1
View File
@@ -1,8 +1,11 @@
@file:Suppress("UnstableApiUsage")
import ch.qos.logback.core.util.EnvUtil
import com.itsaky.androidide.build.config.BuildConfig
import com.itsaky.androidide.desugaring.ch.qos.logback.core.util.DesugarEnvUtil
import com.itsaky.androidide.desugaring.utils.JavaIOReplacements.applyJavaIOReplacements
import com.itsaky.androidide.plugins.AndroidIDEAssetsPlugin
import kotlin.reflect.jvm.javaMethod
plugins {
id("com.android.application")
@@ -17,6 +20,13 @@ apply {
plugin(AndroidIDEAssetsPlugin::class.java)
}
buildscript {
dependencies {
classpath(libs.logging.logback.core)
classpath(libs.composite.desugaringCore)
}
}
android {
namespace = BuildConfig.packageName
@@ -45,9 +55,20 @@ kapt { arguments { arg("eventBusIndex", "${BuildConfig.packageName}.events.AppEv
desugaring {
replacements {
includePackage("org.eclipse.jgit")
includePackage(
"org.eclipse.jgit",
"ch.qos.logback.classic.util",
)
applyJavaIOReplacements()
// EnvUtil.logbackVersion() uses newer Java APIs like Class.getModule() which is not available
// on Android. We replace the method usage with DesugarEnvUtil.logbackVersion() which
// always returns null
replaceMethod(
EnvUtil::logbackVersion.javaMethod!!,
DesugarEnvUtil::logbackVersion.javaMethod!!
)
}
}
@@ -19,7 +19,6 @@ package com.itsaky.androidide.actions
import com.itsaky.androidide.lookup.Lookup
import com.itsaky.androidide.projects.builder.BuildService
import com.itsaky.androidide.utils.ILogger
/**
* Marker class for actions that execute build related tasks.
@@ -28,7 +27,6 @@ import com.itsaky.androidide.utils.ILogger
*/
abstract class BaseBuildAction : EditorActivityAction() {
protected val log: ILogger = ILogger.newInstance(javaClass.simpleName)
protected val buildService: BuildService?
get() = Lookup.getDefault().lookup(BuildService.KEY_BUILD_SERVICE)
@@ -42,6 +42,7 @@ import com.itsaky.androidide.utils.resolveAttr
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import org.slf4j.LoggerFactory
import java.io.File
/**
@@ -55,6 +56,11 @@ import java.io.File
class QuickRunWithCancellationAction(context: Context, override val order: Int) :
BaseBuildAction() {
companion object {
private val log = LoggerFactory.getLogger(QuickRunWithCancellationAction::class.java)
}
init {
label = context.getString(R.string.quick_run_debug)
icon = ContextCompat.getDrawable(context, R.drawable.ic_run_outline)
@@ -130,7 +136,7 @@ class QuickRunWithCancellationAction(context: Context, override val order: Int)
) {
val buildService = this.buildService ?: run {
log.error("Cannot execute task '$taskName'. BuildService not found.")
log.error("Cannot execute task '{}'. BuildService not found.", taskName)
return
}
@@ -143,7 +149,7 @@ class QuickRunWithCancellationAction(context: Context, override val order: Int)
data.getActivity()
?: run {
log.error(
"Cannot execute task '$taskName'. Activity instance not provided in ActionData.")
"Cannot execute task '{}'. Activity instance not provided in ActionData.", taskName)
return
}
@@ -154,17 +160,17 @@ class QuickRunWithCancellationAction(context: Context, override val order: Int)
buildService.executeTasks(taskName).get()
}
log.debug("Task execution result:", result)
log.debug("Task execution result: {}", result)
if (result?.isSuccessful != true) {
log.error("Tasks failed to execute: '$taskName'")
log.error("Tasks failed to execute: '{}'", taskName)
return@launch
}
handleResult(data, result, module, variant)
}.invokeOnCompletion { error ->
if (error != null) {
log.error("Failed to run '$taskName'", error)
log.error("Failed to run '{}'", taskName, error)
}
}
}
@@ -186,7 +192,7 @@ class QuickRunWithCancellationAction(context: Context, override val order: Int)
if (!result.wasEnqueued) {
log.warn(
"Unable to enqueue cancellation request",
"Unable to enqueue cancellation request reason={} reason.message={}",
result.failureReason,
result.failureReason!!.message
)
@@ -210,7 +216,7 @@ class QuickRunWithCancellationAction(context: Context, override val order: Int)
return
}
log.debug("Installing APK(s) for project: '${module.path}' variant: '${variant.name}'")
log.debug("Installing APK(s) for project: '{}' variant: '{}'", module.path, variant.name)
val main = variant.mainArtifact
val outputListingFile = main.assembleTaskOutputListingFile
@@ -219,15 +225,15 @@ class QuickRunWithCancellationAction(context: Context, override val order: Int)
return
}
log.verbose("Parsing metadata")
log.trace("Parsing metadata")
val apkFile = ApkMetadata.findApkFile(outputListingFile)
if (apkFile == null) {
log.error("No apk file specified in output listing file:", outputListingFile)
log.error("No apk file specified in output listing file: {}", outputListingFile)
return
}
if (!apkFile.exists()) {
log.error("APK file specified in output listing file does not exist!", apkFile)
log.error("APK file specified in output listing file does not exist! {}", apkFile)
return
}
@@ -243,7 +249,7 @@ class QuickRunWithCancellationAction(context: Context, override val order: Int)
}
activity.runOnUiThread {
log.debug("Installing APK:", apk)
log.debug("Installing APK: {}", apk)
if (!apk.exists()) {
log.error("APK file does not exist!")
@@ -18,9 +18,9 @@
package com.itsaky.androidide.actions.editor
import android.content.Context
import com.itsaky.androidide.resources.R
import com.itsaky.androidide.actions.ActionData
import com.itsaky.androidide.actions.BaseEditorAction
import com.itsaky.androidide.resources.R
/** @author Akash Yadav */
class CopyAction(context: Context, override val order: Int) : BaseEditorAction() {
@@ -19,10 +19,10 @@ package com.itsaky.androidide.actions.editor
import android.content.Context
import androidx.core.content.ContextCompat
import com.itsaky.androidide.resources.R
import com.itsaky.androidide.actions.ActionData
import com.itsaky.androidide.actions.ActionItem
import com.itsaky.androidide.actions.EditorRelatedAction
import com.itsaky.androidide.resources.R
/**
* Expands the current selection using the language server set in editor.
@@ -19,9 +19,9 @@ package com.itsaky.androidide.actions.etc
import android.content.Context
import androidx.core.content.ContextCompat
import com.itsaky.androidide.resources.R
import com.itsaky.androidide.actions.ActionData
import com.itsaky.androidide.actions.EditorRelatedAction
import com.itsaky.androidide.resources.R
/** @author Akash Yadav */
class FindInFileAction() : EditorRelatedAction() {
@@ -19,11 +19,11 @@ package com.itsaky.androidide.actions.etc
import android.content.Context
import androidx.core.content.ContextCompat
import com.itsaky.androidide.resources.R
import com.itsaky.androidide.actions.ActionData
import com.itsaky.androidide.actions.EditorActivityAction
import com.itsaky.androidide.actions.markInvisible
import com.itsaky.androidide.projects.IProjectManager
import com.itsaky.androidide.resources.R
/** @author Akash Yadav */
class FindInProjectAction() : EditorActivityAction() {
@@ -25,11 +25,10 @@ import com.itsaky.androidide.actions.ActionData
import com.itsaky.androidide.actions.EditorActivityAction
import com.itsaky.androidide.actions.markInvisible
import com.itsaky.androidide.actions.openApplicationModuleChooser
import com.itsaky.androidide.builder.model.UNKNOWN_PACKAGE
import com.itsaky.androidide.projects.IProjectManager
import com.itsaky.androidide.utils.ILogger
import com.itsaky.androidide.utils.IntentUtils
import com.itsaky.androidide.utils.flashError
import org.slf4j.LoggerFactory
/**
* An action to launch the already installed application on the device.
@@ -47,7 +46,7 @@ class LaunchAppAction(context: Context, override val order: Int) : EditorActivit
}
companion object {
private val log = ILogger.newInstance("LaunchAppAction")
private val log = LoggerFactory.getLogger(LaunchAppAction::class.java)
}
override fun prepare(data: ActionData) {
@@ -67,7 +66,7 @@ class LaunchAppAction(context: Context, override val order: Int) : EditorActivit
openApplicationModuleChooser(data) { app ->
val variant = app.getSelectedVariant()
log.debug("Selected variant: ${variant?.name}")
log.debug("Selected variant: {}", variant?.name)
if (variant == null) {
flashError(R.string.err_selected_variant_not_found)
@@ -81,7 +80,7 @@ class LaunchAppAction(context: Context, override val order: Int) : EditorActivit
return@openApplicationModuleChooser
}
log.info("Launching application: $applicationId")
log.info("Launching application: {}", applicationId)
val activity = data.requireActivity()
IntentUtils.launchApp(activity, applicationId, logError = false)
@@ -20,15 +20,11 @@ package com.itsaky.androidide.actions.etc
import android.content.Context
import androidx.core.content.ContextCompat
import androidx.lifecycle.lifecycleScope
import com.blankj.utilcode.util.ThreadUtils
import com.itsaky.androidide.R
import com.itsaky.androidide.actions.ActionData
import com.itsaky.androidide.actions.EditorActivityAction
import com.itsaky.androidide.editor.schemes.IDEColorSchemeProvider
import com.itsaky.androidide.tasks.executeAsyncProvideError
import com.itsaky.androidide.tasks.executeWithProgress
import com.itsaky.androidide.tasks.launchAsyncWithProgress
import com.itsaky.androidide.utils.ILogger
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
@@ -46,8 +42,6 @@ class ReloadColorSchemesAction(context: Context, override val order: Int) : Edit
// the UI thread
override var requiresUIThread: Boolean = true
private val log = ILogger.newInstance("ReloadColorSchemesAction")
init {
label = context.getString(R.string.title_reload_color_schemes)
icon = ContextCompat.getDrawable(context, R.drawable.ic_reload)
@@ -19,9 +19,9 @@ package com.itsaky.androidide.actions.file
import android.content.Context
import androidx.core.content.ContextCompat
import com.itsaky.androidide.activities.editor.EditorHandlerActivity
import com.itsaky.androidide.R
import com.itsaky.androidide.actions.ActionData
import com.itsaky.androidide.activities.editor.EditorHandlerActivity
/**
* Closes all opened files.
@@ -19,9 +19,9 @@ package com.itsaky.androidide.actions.file
import android.content.Context
import androidx.core.content.ContextCompat
import com.itsaky.androidide.activities.editor.EditorHandlerActivity
import com.itsaky.androidide.R
import com.itsaky.androidide.actions.ActionData
import com.itsaky.androidide.activities.editor.EditorHandlerActivity
/**
* Closes the current file.
@@ -19,9 +19,9 @@ package com.itsaky.androidide.actions.file
import android.content.Context
import androidx.core.content.ContextCompat
import com.itsaky.androidide.activities.editor.EditorHandlerActivity
import com.itsaky.androidide.R
import com.itsaky.androidide.actions.ActionData
import com.itsaky.androidide.activities.editor.EditorHandlerActivity
/**
* Closes all opened files except the selected one.
@@ -17,12 +17,12 @@
package com.itsaky.androidide.actions.file
import com.itsaky.androidide.activities.editor.EditorHandlerActivity
import com.itsaky.androidide.actions.ActionData
import com.itsaky.androidide.actions.ActionItem.Location
import com.itsaky.androidide.actions.ActionItem.Location.EDITOR_FILE_TABS
import com.itsaky.androidide.actions.EditorActivityAction
import com.itsaky.androidide.actions.markInvisible
import com.itsaky.androidide.activities.editor.EditorHandlerActivity
/**
* Action related to file tabs. Shown only when there is at least one file opened.
@@ -19,10 +19,10 @@ package com.itsaky.androidide.actions.file
import android.content.Context
import androidx.core.content.ContextCompat
import com.itsaky.androidide.resources.R
import com.itsaky.androidide.actions.ActionData
import com.itsaky.androidide.actions.ActionItem
import com.itsaky.androidide.actions.EditorRelatedAction
import com.itsaky.androidide.resources.R
/**
* Action that formats the code in the editor.
@@ -24,19 +24,20 @@ import com.itsaky.androidide.actions.EditorRelatedAction
import com.itsaky.androidide.models.SaveResult
import com.itsaky.androidide.projects.ProjectManagerImpl
import com.itsaky.androidide.resources.R
import com.itsaky.androidide.utils.ILogger
import com.itsaky.androidide.utils.flashError
import com.itsaky.androidide.utils.flashSuccess
import org.slf4j.LoggerFactory
/** @author Akash Yadav */
class SaveFileAction(context: Context, override val order: Int) : EditorRelatedAction() {
private val log = ILogger.newInstance("SaveFileAction")
override val id: String = "ide.editor.files.saveAll"
override var requiresUIThread: Boolean = false
companion object {
private val log = LoggerFactory.getLogger(SaveFileAction::class.java)
}
init {
label = context.getString(R.string.save)
icon = ContextCompat.getDrawable(context, R.drawable.ic_save)
@@ -29,8 +29,8 @@ import com.itsaky.androidide.tasks.executeAsync
import com.itsaky.androidide.utils.DialogUtils
import com.itsaky.androidide.utils.FlashType
import com.itsaky.androidide.utils.flashMessage
import java.io.File
import org.greenrobot.eventbus.EventBus
import java.io.File
/**
* File tree action to delete files.
@@ -31,7 +31,6 @@ import com.itsaky.androidide.projects.IProjectManager
import com.itsaky.androidide.resources.R
import com.itsaky.androidide.utils.DialogUtils
import com.itsaky.androidide.utils.Environment
import com.itsaky.androidide.utils.ILogger
import com.itsaky.androidide.utils.ProjectWriter
import com.itsaky.androidide.utils.SingleTextWatcher
import com.itsaky.androidide.utils.flashError
@@ -39,6 +38,7 @@ import com.itsaky.androidide.utils.flashSuccess
import com.unnamed.b.atv.model.TreeNode
import jdkx.lang.model.SourceVersion
import org.greenrobot.eventbus.EventBus
import org.slf4j.LoggerFactory
import java.io.File
import java.util.Objects
import java.util.regex.Pattern
@@ -58,13 +58,14 @@ class NewFileAction(context: Context, override val order: Int) :
override val id: String = "ide.editor.fileTree.newFile"
companion object {
const val RES_PATH_REGEX = "/.*/src/.*/res"
const val LAYOUT_RES_PATH_REGEX = "/.*/src/.*/res/layout"
const val MENU_RES_PATH_REGEX = "/.*/src/.*/res/menu"
const val DRAWABLE_RES_PATH_REGEX = "/.*/src/.*/res/drawable"
const val JAVA_PATH_REGEX = "/.*/src/.*/java"
private val log = ILogger.newInstance("NewFileAction")
private val log = LoggerFactory.getLogger(NewFileAction::class.java)
}
override suspend fun execAction(data: ActionData) {
@@ -74,7 +75,7 @@ class NewFileAction(context: Context, override val order: Int) :
try {
createNewFile(context, node, file, false)
} catch (e: Exception) {
log.error(e)
log.error("Failed to create new file", e)
flashError(e.cause?.message ?: e.message)
}
}
@@ -163,7 +164,7 @@ class NewFileAction(context: Context, override val order: Int) :
try {
doCreateJavaFile(binding, file, context, node)
} catch (e: Exception) {
log.error(e)
log.error("Failed to create Java file", e)
flashError(e.cause?.message ?: e.message)
}
}
@@ -370,7 +371,7 @@ class NewFileAction(context: Context, override val order: Int) :
try {
createFile(context, node, folder, name, content)
} catch (e: Exception) {
log.error(e)
log.error("Failed to create file", e)
flashError(e.cause?.message ?: e.message)
}
}
@@ -32,10 +32,10 @@ import com.itsaky.androidide.utils.DialogUtils
import com.itsaky.androidide.utils.FlashType
import com.itsaky.androidide.utils.flashMessage
import com.unnamed.b.atv.model.TreeNode
import java.io.File
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import org.greenrobot.eventbus.EventBus
import java.io.File
/**
* Action to rename the selected file.
@@ -28,7 +28,8 @@ import com.itsaky.androidide.utils.flashError
/**
* @see openApplicationModuleChooser
*/
inline fun openApplicationModuleChooser(data: ActionData, crossinline callback: (AndroidModule) -> Unit) =
inline fun openApplicationModuleChooser(data: ActionData,
crossinline callback: (AndroidModule) -> Unit) =
openApplicationModuleChooser(data.requireContext(), callback)
/**
@@ -38,13 +39,13 @@ inline fun openApplicationModuleChooser(data: ActionData, crossinline callback:
*
* @param
*/
inline fun openApplicationModuleChooser(context: Context, crossinline callback: (AndroidModule) -> Unit) {
val log = ILogger.newInstance("ActionUtils")
inline fun openApplicationModuleChooser(context: Context,
crossinline callback: (AndroidModule) -> Unit) {
val projectManager = IProjectManager.getInstance()
val applications = projectManager.getAndroidAppModules()
if (applications.isEmpty()) {
flashError(R.string.msg_launch_failure_no_app_module)
log.error("Cannot run application. No application modules found in project.")
ILogger.ROOT.error("Cannot run application. No application modules found in project.")
return
}
@@ -62,7 +63,7 @@ inline fun openApplicationModuleChooser(context: Context, crossinline callback:
applications.map { it.path }.toTypedArray(),
0) { selection ->
val app = applications[selection]
log.info("Selected application: '${app.path}'")
ILogger.ROOT.info("Selected application: '{}'", app.path)
callback(app)
}
@@ -20,10 +20,10 @@ package com.itsaky.androidide.actions.sidebar
import android.content.Context
import androidx.core.content.ContextCompat
import androidx.fragment.app.Fragment
import com.itsaky.androidide.resources.R
import com.itsaky.androidide.actions.ActionData
import com.itsaky.androidide.actions.requireContext
import com.itsaky.androidide.activities.editor.BaseEditorActivity
import com.itsaky.androidide.resources.R
import kotlin.reflect.KClass
/**
@@ -22,10 +22,10 @@ import android.content.Context
import android.view.MenuItem
import androidx.core.content.ContextCompat
import com.blankj.utilcode.util.KeyboardUtils
import com.itsaky.androidide.resources.R
import com.itsaky.androidide.actions.ActionData
import com.itsaky.androidide.actions.EditorRelatedAction
import com.itsaky.androidide.actions.markInvisible
import com.itsaky.androidide.resources.R
/** @author Akash Yadav */
class RedoAction(context: Context, override val order: Int) : EditorRelatedAction() {
@@ -19,9 +19,9 @@ package com.itsaky.androidide.actions.text
import android.content.Context
import androidx.core.content.ContextCompat
import com.itsaky.androidide.resources.R
import com.itsaky.androidide.actions.ActionData
import com.itsaky.androidide.actions.EditorRelatedAction
import com.itsaky.androidide.resources.R
/** @author Akash Yadav */
class UndoAction(context: Context, override val order: Int) : EditorRelatedAction() {
@@ -23,7 +23,6 @@ import android.os.Bundle
import android.text.SpannableStringBuilder
import android.text.style.ForegroundColorSpan
import android.view.View
import android.widget.TextView
import androidx.annotation.ColorInt
import androidx.annotation.DrawableRes
import androidx.annotation.StringRes
@@ -26,7 +26,6 @@ import com.itsaky.androidide.R
import com.itsaky.androidide.adapters.ContributorsGridAdapter
import com.itsaky.androidide.app.LimitlessIDEActivity
import com.itsaky.androidide.databinding.ActivityContributorsBinding
import com.itsaky.androidide.utils.flashError
import com.itsaky.androidide.utils.getConnectionInfo
import com.itsaky.androidide.viewmodel.ContributorsViewModel
@@ -19,7 +19,6 @@ package com.itsaky.androidide.activities;
import android.os.Bundle;
import android.view.View;
import com.itsaky.androidide.app.IDEActivity;
import com.itsaky.androidide.databinding.ActivityCrashHandlerBinding;
import com.itsaky.androidide.fragments.CrashReportFragment;
@@ -30,18 +30,15 @@ import androidx.transition.doOnEnd
import com.google.android.material.transition.MaterialSharedAxis
import com.itsaky.androidide.activities.editor.EditorActivityKt
import com.itsaky.androidide.app.LimitlessIDEActivity
import com.itsaky.androidide.app.configuration.IDEBuildConfigProvider
import com.itsaky.androidide.databinding.ActivityMainBinding
import com.itsaky.androidide.preferences.internal.NO_OPENED_PROJECT
import com.itsaky.androidide.preferences.internal.autoOpenProjects
import com.itsaky.androidide.preferences.internal.confirmProjectOpen
import com.itsaky.androidide.preferences.internal.lastOpenedProject
import com.itsaky.androidide.preferences.internal.statConsentDialogShown
import com.itsaky.androidide.projects.ProjectManagerImpl
import com.itsaky.androidide.resources.R.string
import com.itsaky.androidide.templates.ITemplateProvider
import com.itsaky.androidide.utils.DialogUtils
import com.itsaky.androidide.utils.Environment
import com.itsaky.androidide.utils.flashInfo
import com.itsaky.androidide.viewmodel.MainViewModel
import com.itsaky.androidide.viewmodel.MainViewModel.Companion.SCREEN_MAIN
@@ -75,7 +75,6 @@ import com.itsaky.androidide.uidesigner.UIDesignerActivity
import com.itsaky.androidide.utils.ActionMenuUtils.createMenu
import com.itsaky.androidide.utils.ApkInstallationSessionCallback
import com.itsaky.androidide.utils.DialogUtils.newMaterialDialogBuilder
import com.itsaky.androidide.utils.ILogger
import com.itsaky.androidide.utils.InstallationResultHandler.onResult
import com.itsaky.androidide.utils.IntentUtils
import com.itsaky.androidide.utils.flashError
@@ -87,6 +86,8 @@ import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import org.greenrobot.eventbus.Subscribe
import org.greenrobot.eventbus.ThreadMode.MAIN
import org.slf4j.Logger
import org.slf4j.LoggerFactory
import java.io.File
/**
@@ -95,9 +96,7 @@ import java.io.File
* @author Akash Yadav
*/
@Suppress("MemberVisibilityCanBePrivate")
abstract class BaseEditorActivity :
IDEActivity(),
TabLayout.OnTabSelectedListener,
abstract class BaseEditorActivity : IDEActivity(), TabLayout.OnTabSelectedListener,
DiagnosticClickListener {
protected val mLifecycleObserver = EditorActivityLifecyclerObserver()
@@ -108,8 +107,6 @@ abstract class BaseEditorActivity :
var isDestroying = false
protected set
protected val log: ILogger = ILogger.newInstance("EditorActivity")
/**
* Editor activity's [CoroutineScope] for executing tasks in the background.
*/
@@ -127,8 +124,7 @@ abstract class BaseEditorActivity :
override val subscribeToEvents: Boolean
get() = true
private val onBackPressedCallback: OnBackPressedCallback =
object : OnBackPressedCallback(true) {
private val onBackPressedCallback: OnBackPressedCallback = object : OnBackPressedCallback(true) {
override fun handleOnBackPressed() {
if (binding.root.isDrawerOpen(GravityCompat.START)) {
binding.root.closeDrawer(GravityCompat.START)
@@ -144,6 +140,9 @@ abstract class BaseEditorActivity :
companion object {
@JvmStatic
protected val log: Logger = LoggerFactory.getLogger(BaseEditorActivity::class.java)
private const val OPTIONS_MENU_INVALIDATION_DELAY = 150L
const val EDITOR_CONTAINER_SCALE_FACTOR = 0.87f
@@ -210,8 +209,7 @@ abstract class BaseEditorActivity :
}
Snackbar.make(binding.realContainer, string.msg_action_open_application, Snackbar.LENGTH_LONG)
.setAction(string.yes) { IntentUtils.launchApp(this, packageName) }
.show()
.setAction(string.yes) { IntentUtils.launchApp(this, packageName) }.show()
}
override fun onCreate(savedInstanceState: Bundle?) {
@@ -240,8 +238,8 @@ abstract class BaseEditorActivity :
setupContainers()
setupDiagnosticInfo()
uiDesignerResultLauncher =
registerForActivityResult(StartActivityForResult(), this::handleUiDesignerResult)
uiDesignerResultLauncher = registerForActivityResult(StartActivityForResult(),
this::handleUiDesignerResult)
}
override fun onPause() {
@@ -320,18 +318,13 @@ abstract class BaseEditorActivity :
open fun handleSearchResults(map: Map<File, List<SearchResult>>?) {
val results = map ?: emptyMap()
setSearchResultAdapter(
SearchListAdapter(
results,
{ file ->
setSearchResultAdapter(SearchListAdapter(results, { file ->
doOpenFile(file, null)
hideBottomSheet()
}
) { match ->
}) { match ->
doOpenFile(match.file, match)
hideBottomSheet()
}
)
})
showSearchResults()
doDismissSearchProgress()
@@ -356,8 +349,8 @@ abstract class BaseEditorActivity :
editorBottomSheet?.state = BottomSheetBehavior.STATE_EXPANDED
}
val index =
binding.bottomSheet.pagerAdapter.findIndexOfFragmentByClass(SearchResultFragment::class.java)
val index = binding.bottomSheet.pagerAdapter.findIndexOfFragmentByClass(
SearchResultFragment::class.java)
if (index >= 0 && index < binding.bottomSheet.binding.tabs.tabCount) {
binding.bottomSheet.binding.tabs.getTabAt(index)?.select()
@@ -373,19 +366,15 @@ abstract class BaseEditorActivity :
}
open fun showFirstBuildNotice() {
newMaterialDialogBuilder(this)
.setPositiveButton(android.R.string.ok, null)
.setTitle(string.title_first_build)
.setMessage(string.msg_first_build)
.setCancelable(false)
.create()
.show()
newMaterialDialogBuilder(this).setPositiveButton(android.R.string.ok, null)
.setTitle(string.title_first_build).setMessage(string.msg_first_build).setCancelable(false)
.create().show()
}
open fun getFileTreeFragment(): FileTreeFragment? {
if (filesTreeFragment == null) {
filesTreeFragment =
supportFragmentManager.findFragmentByTag(FileTreeFragment.TAG) as FileTreeFragment?
filesTreeFragment = supportFragmentManager.findFragmentByTag(
FileTreeFragment.TAG) as FileTreeFragment?
}
return filesTreeFragment
}
@@ -411,7 +400,7 @@ abstract class BaseEditorActivity :
private fun handleUiDesignerResult(result: ActivityResult) {
if (result.resultCode != RESULT_OK || result.data == null) {
log.warn("UI Designer returned invalid result", result.resultCode, result.data)
log.warn("UI Designer returned invalid result: resultCode={}, data={}", result.resultCode, result.data)
return
}
val generated = result.data!!.getStringExtra(UIDesignerActivity.RESULT_GENERATED_XML)
@@ -429,14 +418,8 @@ abstract class BaseEditorActivity :
}
private fun setupDrawers() {
val toggle =
ActionBarDrawerToggle(
this,
binding.editorDrawerLayout,
binding.editorToolbar,
string.app_name,
string.app_name
)
val toggle = ActionBarDrawerToggle(this, binding.editorDrawerLayout, binding.editorToolbar,
string.app_name, string.app_name)
binding.editorDrawerLayout.addDrawerListener(toggle)
toggle.syncState()
@@ -480,31 +463,25 @@ abstract class BaseEditorActivity :
setupNoEditorView()
setupBottomSheet()
if (
!app.prefManager.getBoolean(KEY_BOTTOM_SHEET_SHOWN) &&
editorBottomSheet?.state != BottomSheetBehavior.STATE_EXPANDED
if (!app.prefManager.getBoolean(
KEY_BOTTOM_SHEET_SHOWN) && editorBottomSheet?.state != BottomSheetBehavior.STATE_EXPANDED
) {
editorBottomSheet?.state = BottomSheetBehavior.STATE_EXPANDED
ThreadUtils.runOnUiThreadDelayed(
{
ThreadUtils.runOnUiThreadDelayed({
editorBottomSheet?.state = BottomSheetBehavior.STATE_COLLAPSED
app.prefManager.putBoolean(KEY_BOTTOM_SHEET_SHOWN, true)
},
1500
)
}, 1500)
}
}
private fun setupNoEditorView() {
binding.noEditorSummary.movementMethod = LinkMovementMethod()
val filesSpan: ClickableSpan =
object : ClickableSpan() {
val filesSpan: ClickableSpan = object : ClickableSpan() {
override fun onClick(widget: View) {
binding.root.openDrawer(GravityCompat.START)
}
}
val bottomSheetSpan: ClickableSpan =
object : ClickableSpan() {
val bottomSheetSpan: ClickableSpan = object : ClickableSpan() {
override fun onClick(widget: View) {
editorBottomSheet?.state = BottomSheetBehavior.STATE_EXPANDED
}
@@ -536,8 +513,7 @@ abstract class BaseEditorActivity :
private fun setupBottomSheet() {
editorBottomSheet = BottomSheetBehavior.from<View>(binding.bottomSheet)
editorBottomSheet?.addBottomSheetCallback(
object : BottomSheetCallback() {
editorBottomSheet?.addBottomSheetCallback(object : BottomSheetCallback() {
override fun onStateChanged(bottomSheet: View, newState: Int) {
if (newState == BottomSheetBehavior.STATE_EXPANDED) {
val editor = provideCurrentEditor()
@@ -553,11 +529,9 @@ abstract class BaseEditorActivity :
this.viewContainer.scaleY = editorScale
}
}
}
)
})
val observer: OnGlobalLayoutListener =
object : OnGlobalLayoutListener {
val observer: OnGlobalLayoutListener = object : OnGlobalLayoutListener {
override fun onGlobalLayout() {
binding.let {
it.viewContainer.pivotY = 0f
@@ -163,7 +163,7 @@ open class EditorHandlerActivity : ProjectHandlerActivity(), IEditorHandler {
if (selectedFile == null || openedFiles.isEmpty()) {
editorViewModel.writeOpenedFiles(null)
editorViewModel.openedFilesCache = null
log.debug("[onPause]", "No opened files.", "Opened files cache reset to null.")
log.debug("[onPause] No opened files. Opened files cache reset to null.")
isOpenedFilesSaved.set(true)
return
}
@@ -172,7 +172,7 @@ open class EditorHandlerActivity : ProjectHandlerActivity(), IEditorHandler {
editorViewModel.writeOpenedFiles(cache)
editorViewModel.openedFilesCache = if (!isDestroying) cache else null
log.debug("[onPause]", "Opened files cache reset to ${editorViewModel.openedFilesCache}")
log.debug("[onPause] Opened files cache reset to {}", editorViewModel.openedFilesCache)
isOpenedFilesSaved.set(true)
}
@@ -307,8 +307,7 @@ open class EditorHandlerActivity : ProjectHandlerActivity(), IEditorHandler {
return try {
getEditorAtIndex(index)
} catch (th: Throwable) {
log.error("Unable to get editor fragment at opened file index", index)
log.error(th)
log.error("Unable to get editor fragment at opened file index {}", index, th)
null
}
}
@@ -325,7 +324,7 @@ open class EditorHandlerActivity : ProjectHandlerActivity(), IEditorHandler {
val position = editorViewModel.getOpenedFileCount()
log.info("Opening file at index:", position, "file: ", file)
log.info("Opening file at index {} file:{}", position, file)
val editor = CodeEditorView(this, file, selection!!)
editor.layoutParams = LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)
@@ -504,11 +503,11 @@ open class EditorHandlerActivity : ProjectHandlerActivity(), IEditorHandler {
}
val opened = editorViewModel.getOpenedFile(index)
log.info("Closing file:", opened)
log.info("Closing file: {}", opened)
val editor = getEditorAtIndex(index)
if (editor?.isModified == true) {
log.info("File has been modified:", opened)
log.info("File has been modified: {}", opened)
notifyFilesUnsaved(listOf(editor)) {
closeFile(index, runAfter)
}
@@ -554,7 +553,7 @@ open class EditorHandlerActivity : ProjectHandlerActivity(), IEditorHandler {
val editor = getEditorAtIndex(index)
if (editor == null) {
log.error("Unable to save file at index:", index)
log.error("Unable to save file at index {}", index)
continue
}
@@ -583,7 +582,7 @@ open class EditorHandlerActivity : ProjectHandlerActivity(), IEditorHandler {
// Files were already saved, close all files one by one
for (i in 0 until count) {
getEditorAtIndex(i)?.close() ?: run {
log.error("Unable to close file at index:", i)
log.error("Unable to close file at index {}", i)
}
}
@@ -18,13 +18,13 @@ package com.itsaky.androidide.activities.editor
import android.os.Process
import com.itsaky.androidide.utils.Environment
import com.itsaky.androidide.utils.ILogger
import com.itsaky.androidide.utils.transferToStream
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.cancel
import kotlinx.coroutines.launch
import org.slf4j.LoggerFactory
import java.io.File
import java.text.SimpleDateFormat
import java.util.Date
@@ -37,10 +37,14 @@ import java.util.Locale
*/
class IDELogcatReader {
private val log = ILogger.newInstance("IDELogcatReader")
private var job: Job? = null
private var shouldRun = false
companion object {
private val log = LoggerFactory.getLogger(IDELogcatReader::class.java)
}
/**
* Start reading the logs.
*/
@@ -71,7 +75,7 @@ class IDELogcatReader {
val outputFile = File(Environment.ANDROIDIDE_HOME,
"logs/AndroidIDE-LOG-${dateFormat.format(date)}.txt")
log.debug("Creating output file: $outputFile")
log.debug("Creating output file: {}", outputFile)
outputFile.parentFile!!.mkdirs()
try {
@@ -96,7 +100,7 @@ class IDELogcatReader {
process.inputStream.transferToStream(writer)
writer.flush()
log.info("Process ended with exit code :", process.waitFor())
log.info("Process ended with exit code: {}", process.waitFor())
} catch (err: Throwable) {
log.error("Failed to read logs", err)
}
@@ -334,7 +334,7 @@ abstract class ProjectHandlerActivity : BaseEditorActivity() {
newSelections.putAll(buildVariantsViewModel.updatedBuildVariants)
initializeProject {
newSelections.mapToSelectedVariants().also {
log.debug("Initializing project with new build variant selections: $it")
log.debug("Initializing project with new build variant selections: {}", it)
}
}
return
@@ -363,7 +363,7 @@ abstract class ProjectHandlerActivity : BaseEditorActivity() {
}
val initialized = manager.projectInitialized && manager.cachedInitResult != null
log.debug("Is project initialized: $initialized")
log.debug("Is project initialized: {}", initialized)
// When returning after a configuration change between the initialization process,
// we do not want to start another project initialization
if (isFromSavedInstance && initialized && !shouldInitialize) {
@@ -21,7 +21,6 @@ import android.view.LayoutInflater
import android.view.ViewGroup
import android.widget.ArrayAdapter
import androidx.core.widget.addTextChangedListener
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.RecyclerView
import com.itsaky.androidide.R
import com.itsaky.androidide.databinding.LayoutBuildVariantItemBinding
@@ -17,7 +17,6 @@
package com.itsaky.androidide.adapters
import android.content.Intent
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
@@ -29,7 +28,6 @@ import com.itsaky.androidide.R
import com.itsaky.androidide.app.IDEApplication
import com.itsaky.androidide.contributors.Contributor
import com.itsaky.androidide.databinding.LayoutContributorsItemBinding
import com.itsaky.androidide.templates.Template
/**
* @author Akash Yadav
@@ -20,17 +20,14 @@ package com.itsaky.androidide.adapters;
import android.graphics.PorterDuff;
import android.view.LayoutInflater;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.core.content.ContextCompat;
import androidx.recyclerview.widget.RecyclerView;
import com.itsaky.androidide.resources.R;
import com.itsaky.androidide.databinding.LayoutDiagnosticItemBinding;
import com.itsaky.androidide.interfaces.DiagnosticClickListener;
import com.itsaky.androidide.lsp.models.DiagnosticItem;
import com.itsaky.androidide.lsp.models.DiagnosticSeverity;
import com.itsaky.androidide.resources.R;
import java.io.File;
import java.util.List;
import java.util.Objects;
@@ -20,15 +20,12 @@ package com.itsaky.androidide.adapters;
import android.view.LayoutInflater;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import com.itsaky.androidide.databinding.LayoutDiagnosticGroupBinding;
import com.itsaky.androidide.interfaces.DiagnosticClickListener;
import com.itsaky.androidide.models.DiagnosticGroup;
import java.util.List;
import java.util.Objects;
@@ -23,21 +23,20 @@ import androidx.collection.LongSparseArray;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentActivity;
import androidx.viewpager2.adapter.FragmentStateAdapter;
import com.itsaky.androidide.resources.R;
import com.itsaky.androidide.fragments.output.AppLogFragment;
import com.itsaky.androidide.fragments.DiagnosticsListFragment;
import com.itsaky.androidide.fragments.output.IDELogFragment;
import com.itsaky.androidide.fragments.SearchResultFragment;
import com.itsaky.androidide.fragments.output.AppLogFragment;
import com.itsaky.androidide.fragments.output.BuildOutputFragment;
import com.itsaky.androidide.utils.ILogger;
import com.itsaky.androidide.fragments.output.IDELogFragment;
import com.itsaky.androidide.resources.R;
import java.util.ArrayList;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class EditorBottomSheetTabAdapter extends FragmentStateAdapter {
private static final ILogger LOG = ILogger.newInstance("EditorBottomSheetTabAdapter");
private static final Logger LOG = LoggerFactory.getLogger(EditorBottomSheetTabAdapter.class);
private final List<Tab> fragments;
public EditorBottomSheetTabAdapter(@NonNull FragmentActivity fragmentActivity) {
@@ -22,12 +22,9 @@ package com.itsaky.androidide.adapters;
import android.view.LayoutInflater;
import android.view.ViewGroup;
import androidx.recyclerview.widget.RecyclerView;
import com.itsaky.androidide.databinding.LayoutOptionssheetItemBinding;
import com.itsaky.androidide.models.SheetOption;
import java.util.List;
public class OptionsSheetAdapter extends RecyclerView.Adapter<OptionsSheetAdapter.VH> {
@@ -17,14 +17,12 @@
*/
package com.itsaky.androidide.adapters;
import static com.itsaky.androidide.preferences.utils.EditorUtilKt.getIndentationString;
import static com.itsaky.androidide.utils.ResourceUtilsKt.resolveAttr;
import android.annotation.SuppressLint;
import android.view.LayoutInflater;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.DiffUtil;
import androidx.recyclerview.widget.RecyclerView;
import com.itsaky.androidide.R;
import com.itsaky.androidide.databinding.LayoutSymbolItemBinding;
@@ -32,7 +30,6 @@ import com.itsaky.androidide.editor.ui.IDEEditor;
import com.itsaky.androidide.models.Symbol;
import io.github.rosemoe.sora.widget.SelectionMovement;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
@@ -21,12 +21,9 @@ import android.content.res.ColorStateList
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.core.view.isVisible
import androidx.recyclerview.widget.RecyclerView
import com.itsaky.androidide.R
import com.itsaky.androidide.databinding.LayoutOnboardingItemBinding
import com.itsaky.androidide.models.OnboardingItem
import com.itsaky.androidide.utils.resolveAttr
/**
* Default implmentation of [RecyclerView.Adapter] for showing [OnboardingItem]s.
@@ -27,7 +27,6 @@ import com.google.android.material.button.MaterialButton
import com.itsaky.androidide.R
import com.itsaky.androidide.databinding.LayoutOnboardingPermissionItemBinding
import com.itsaky.androidide.models.OnboardingPermissionItem
import com.itsaky.androidide.utils.ILogger
/**
* @author Akash Yadav
@@ -72,8 +71,6 @@ class OnboardingPermissionsAdapter(private val permissions: List<OnboardingPermi
}
}
private val log = ILogger.newInstance("OnboardingPermissionsAdapter")
override fun getItemCount(): Int {
return permissions.size
}
@@ -24,16 +24,13 @@ import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.LinearLayout;
import androidx.transition.ChangeImageTransform;
import androidx.transition.TransitionManager;
import com.blankj.utilcode.util.SizeUtils;
import com.itsaky.androidide.databinding.LayoutFiletreeItemBinding;
import com.itsaky.androidide.models.FileExtension;
import com.itsaky.androidide.resources.R;
import com.unnamed.b.atv.model.TreeNode;
import java.io.File;
public class FileTreeViewHolder extends TreeNode.BaseNodeViewHolder<File> {
@@ -58,7 +58,6 @@ import com.itsaky.androidide.tasks.executeAsync
import com.itsaky.androidide.treesitter.TreeSitter
import com.itsaky.androidide.ui.themes.IDETheme
import com.itsaky.androidide.ui.themes.IThemeManager
import com.itsaky.androidide.utils.ILogger
import com.itsaky.androidide.utils.RecyclableObjectPool
import com.itsaky.androidide.utils.VMUtils
import com.itsaky.androidide.utils.flashError
@@ -67,6 +66,7 @@ import io.github.rosemoe.sora.widget.schemes.EditorColorScheme
import org.greenrobot.eventbus.EventBus
import org.greenrobot.eventbus.Subscribe
import org.greenrobot.eventbus.ThreadMode
import org.slf4j.LoggerFactory
import java.lang.Thread.UncaughtExceptionHandler
import java.time.Duration
import kotlin.system.exitProcess
@@ -75,7 +75,6 @@ class IDEApplication : TermuxApplication() {
private var uncaughtExceptionHandler: UncaughtExceptionHandler? = null
private var ideLogcatReader: IDELogcatReader? = null
private val log = ILogger.newInstance("IDEApplication")
init {
if (!VMUtils.isJvm()) {
@@ -94,21 +93,16 @@ class IDEApplication : TermuxApplication() {
if (BuildConfig.DEBUG) {
StrictMode.setVmPolicy(
StrictMode.VmPolicy.Builder(StrictMode.getVmPolicy()).penaltyLog().detectAll().build()
)
StrictMode.VmPolicy.Builder(StrictMode.getVmPolicy()).penaltyLog().detectAll().build())
if (dumpLogs) {
startLogcatReader()
}
}
EventBus.builder()
.addIndex(AppEventsIndex())
.addIndex(EditorEventsIndex())
.addIndex(ProjectsApiEventsIndex())
.addIndex(LspApiEventsIndex())
.addIndex(LspJavaEventsIndex())
.installDefaultEventBus(true)
EventBus.builder().addIndex(AppEventsIndex()).addIndex(EditorEventsIndex())
.addIndex(ProjectsApiEventsIndex()).addIndex(LspApiEventsIndex())
.addIndex(LspJavaEventsIndex()).installDefaultEventBus(true)
EventBus.getDefault().register(this)
@@ -139,7 +133,7 @@ class IDEApplication : TermuxApplication() {
exitProcess(1)
} catch (error: Throwable) {
LOG.error("Unable to show crash handler activity", error)
log.error("Unable to show crash handler activity", error)
}
}
@@ -154,7 +148,7 @@ class IDEApplication : TermuxApplication() {
try {
startActivity(intent)
} catch (th: Throwable) {
LOG.error("Unable to start activity to show changelog", th)
log.error("Unable to start activity to show changelog", th)
flashError("Unable to start activity")
}
}
@@ -167,23 +161,20 @@ class IDEApplication : TermuxApplication() {
}
val constraints = Constraints.Builder().setRequiredNetworkType(NetworkType.CONNECTED).build()
val request = PeriodicWorkRequestBuilder<StatUploadWorker>(Duration.ofHours(24))
.setInputData(AndroidIDEStats.statData.toInputData())
.setConstraints(constraints)
.addTag(StatUploadWorker.WORKER_WORK_NAME)
.build()
val request = PeriodicWorkRequestBuilder<StatUploadWorker>(Duration.ofHours(24)).setInputData(
AndroidIDEStats.statData.toInputData()).setConstraints(constraints)
.addTag(StatUploadWorker.WORKER_WORK_NAME).build()
val workManager = WorkManager.getInstance(this)
log.info("reportStatsIfNecessary: Enqueuing StatUploadWorker...")
val operation = workManager
.enqueueUniquePeriodicWork(StatUploadWorker.WORKER_WORK_NAME,
val operation = workManager.enqueueUniquePeriodicWork(StatUploadWorker.WORKER_WORK_NAME,
ExistingPeriodicWorkPolicy.UPDATE, request)
operation.state.observeForever(object : Observer<Operation.State> {
override fun onChanged(t: Operation.State) {
override fun onChanged(value: Operation.State) {
operation.state.removeObserver(this)
log.debug("reportStatsIfNecessary: WorkManager enqueue result: $t")
log.debug("reportStatsIfNecessary: WorkManager enqueue result: {}", value)
}
})
}
@@ -238,16 +229,16 @@ class IDEApplication : TermuxApplication() {
val operation = WorkManager.getInstance(this)
.cancelUniqueWork(StatUploadWorker.WORKER_WORK_NAME)
operation.state.observeForever(object : Observer<Operation.State> {
override fun onChanged(t: Operation.State) {
override fun onChanged(value: Operation.State) {
operation.state.removeObserver(this)
log.info("StatUploadWorker: Cancellation result state: $t")
log.info("StatUploadWorker: Cancellation result state: {}", value)
}
})
}
companion object {
private val LOG = ILogger.newInstance("IDEApplication")
private val log = LoggerFactory.getLogger(IDEApplication::class.java)
@JvmStatic
lateinit var instance: IDEApplication
@@ -21,8 +21,8 @@ import com.google.auto.service.AutoService
import com.itsaky.androidide.models.JdkDistribution
import com.itsaky.androidide.preferences.internal.javaHome
import com.itsaky.androidide.utils.Environment
import com.itsaky.androidide.utils.ILogger
import com.itsaky.androidide.utils.JdkUtils
import org.slf4j.LoggerFactory
import java.io.File
/**
@@ -33,7 +33,7 @@ class JdkDistributionProviderImpl : IJdkDistributionProvider {
companion object {
private val log = ILogger.newInstance("JdkDistributionProviderImpl")
private val log = LoggerFactory.getLogger(JdkDistributionProviderImpl::class.java)
}
private var _installedDistributions: List<JdkDistribution>? = null
@@ -79,7 +79,7 @@ class JdkDistributionProviderImpl : IJdkDistributionProvider {
java.setExecutable(true)
}
log.debug("Setting Environment.JAVA_HOME to $javaHome")
log.debug("Setting Environment.JAVA_HOME to {}", javaHome)
Environment.JAVA_HOME = File(javaHome)
Environment.JAVA = Environment.JAVA_HOME.resolve("bin/java")
@@ -17,9 +17,9 @@
package com.itsaky.androidide.contributors
import com.itsaky.androidide.utils.ILogger
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import org.slf4j.LoggerFactory
import retrofit2.Call
import retrofit2.Retrofit
import retrofit2.converter.gson.GsonConverterFactory
@@ -29,7 +29,7 @@ import retrofit2.converter.gson.GsonConverterFactory
*/
internal object Contributors {
val log: ILogger = ILogger.newInstance("Contributors")
private val log = LoggerFactory.getLogger(Contributors::class.java)
@JvmStatic
suspend inline fun <reified Service, reified Model : Contributor> getAllContributors(
@@ -24,7 +24,6 @@ import android.view.ViewGroup
import androidx.preference.PreferenceCategory
import androidx.preference.PreferenceGroup
import com.google.android.material.transition.MaterialSharedAxis
import com.itsaky.androidide.R
import com.itsaky.androidide.preferences.IPreference
import com.itsaky.androidide.preferences.IPreferenceGroup
import com.itsaky.androidide.preferences.IPreferenceScreen
@@ -1,6 +1,5 @@
package com.itsaky.androidide.fragments
import android.content.DialogInterface
import android.content.Intent
import android.os.Bundle
import android.view.LayoutInflater
@@ -9,7 +8,6 @@ import android.view.ViewGroup
import android.widget.TextView
import androidx.appcompat.app.AlertDialog
import androidx.fragment.app.viewModels
import com.blankj.utilcode.util.ThreadUtils
import com.google.android.material.progressindicator.LinearProgressIndicator
import com.itsaky.androidide.activities.MainActivity
import com.itsaky.androidide.activities.PreferencesActivity
@@ -19,15 +17,12 @@ import com.itsaky.androidide.app.BaseApplication
import com.itsaky.androidide.app.BaseIDEActivity
import com.itsaky.androidide.common.databinding.LayoutDialogProgressBinding
import com.itsaky.androidide.databinding.FragmentMainBinding
import com.itsaky.androidide.lsp.java.utils.CancelChecker
import com.itsaky.androidide.models.MainScreenAction
import com.itsaky.androidide.preferences.databinding.LayoutDialogTextInputBinding
import com.itsaky.androidide.resources.R.string
import com.itsaky.androidide.tasks.executeAsyncProvideError
import com.itsaky.androidide.tasks.runOnUiThread
import com.itsaky.androidide.utils.DialogUtils
import com.itsaky.androidide.utils.Environment
import com.itsaky.androidide.utils.ILogger
import com.itsaky.androidide.utils.flashError
import com.itsaky.androidide.utils.flashSuccess
import com.itsaky.androidide.viewmodel.MainViewModel
@@ -37,6 +32,7 @@ import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import org.eclipse.jgit.api.Git
import org.eclipse.jgit.lib.ProgressMonitor
import org.slf4j.LoggerFactory
import java.io.File
import java.util.concurrent.CancellationException
@@ -46,7 +42,10 @@ class MainFragment : BaseFragment() {
ownerProducer = { requireActivity() })
private var binding: FragmentMainBinding? = null
private val log = ILogger.newInstance("MainFragment")
companion object {
private val log = LoggerFactory.getLogger(MainFragment::class.java)
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
@@ -127,7 +126,7 @@ class MainFragment : BaseFragment() {
private fun doClone(repo: String?) {
if (repo.isNullOrBlank()) {
log.warn("Unable to clone repo. Invalid repo URL : '$repo'")
log.warn("Unable to clone repo. Invalid repo URL : {}'", repo)
return
}
@@ -51,13 +51,13 @@ import com.itsaky.androidide.projects.builder.BuildService
import com.itsaky.androidide.resources.R
import com.itsaky.androidide.tasks.executeAsync
import com.itsaky.androidide.tooling.api.models.GradleTask
import com.itsaky.androidide.utils.ILogger
import com.itsaky.androidide.utils.SingleTextWatcher
import com.itsaky.androidide.utils.doOnApplyWindowInsets
import com.itsaky.androidide.utils.flashError
import com.itsaky.androidide.utils.flashInfo
import com.itsaky.androidide.utils.updateSystemBarColors
import com.itsaky.androidide.viewmodel.RunTasksViewModel
import org.slf4j.LoggerFactory
/**
* A bottom sheet dialog fragment to show UI which allows the users to select and execute Gradle
@@ -71,9 +71,9 @@ class RunTasksDialogFragment : BottomSheetDialogFragment() {
private lateinit var run: LayoutRunTaskBinding
private val viewModel: RunTasksViewModel by viewModels()
private val log = ILogger.newInstance("RunTasksDialogFragment")
companion object {
private val log = LoggerFactory.getLogger(RunTasksDialogFragment::class.java)
private const val CHILD_LOADING = 0
private const val CHILD_TASKS = 1
private const val CHILD_CONFIRMATION = 2
@@ -33,11 +33,11 @@ import com.itsaky.androidide.templates.ProjectTemplateRecipeResult
import com.itsaky.androidide.templates.StringParameter
import com.itsaky.androidide.templates.Template
import com.itsaky.androidide.templates.impl.ConstraintVerifier
import com.itsaky.androidide.utils.ILogger
import com.itsaky.androidide.utils.TemplateRecipeExecutor
import com.itsaky.androidide.utils.flashError
import com.itsaky.androidide.utils.flashSuccess
import com.itsaky.androidide.viewmodel.MainViewModel
import org.slf4j.LoggerFactory
/**
* A fragment which shows a wizard-like interface for creating templates.
@@ -48,10 +48,14 @@ class TemplateDetailsFragment :
FragmentWithBinding<FragmentTemplateDetailsBinding>(
R.layout.fragment_template_details, FragmentTemplateDetailsBinding::bind) {
private val log = ILogger.newInstance("TemplateDetailsFragment")
private val viewModel by viewModels<MainViewModel>(
ownerProducer = { requireActivity() })
companion object {
private val log = LoggerFactory.getLogger(TemplateDetailsFragment::class.java)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
@@ -99,8 +103,7 @@ class TemplateDetailsFragment :
viewModel.creatingProject.value = false
if (result == null || err != null || result !is ProjectTemplateRecipeResult) {
err?.printStackTrace()
log.error(
"Failed to create project. result=$result, err=${err?.message}")
log.error("Failed to create project. result={}, err={}", result, err?.message)
if (err != null) {
flashError(err.cause?.message ?: err.message)
} else {
@@ -30,8 +30,8 @@ import com.itsaky.androidide.databinding.FragmentTemplateListBinding
import com.itsaky.androidide.templates.ITemplateProvider
import com.itsaky.androidide.templates.ProjectTemplate
import com.itsaky.androidide.utils.FlexboxUtils
import com.itsaky.androidide.utils.ILogger
import com.itsaky.androidide.viewmodel.MainViewModel
import org.slf4j.LoggerFactory
/**
* A fragment to show the list of available templates.
@@ -49,6 +49,11 @@ class TemplateListFragment :
private val viewModel by viewModels<MainViewModel>(ownerProducer = { requireActivity() })
companion object {
private val log = LoggerFactory.getLogger(TemplateListFragment::class.java)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
@@ -84,8 +89,6 @@ class TemplateListFragment :
super.onDestroyView()
}
private val log = ILogger.newInstance("TemplateListFragment")
private fun reloadTemplates() {
_binding ?: return
@@ -33,7 +33,6 @@ import android.text.Html
import android.text.Spannable
import android.text.SpannableStringBuilder
import android.text.style.URLSpan
import android.util.Log
import android.view.ViewGroup
import android.widget.ArrayAdapter
import androidx.core.content.getSystemService
@@ -31,7 +31,7 @@ import com.itsaky.androidide.services.log.LogReceiverImpl
import com.itsaky.androidide.services.log.LogReceiverService
import com.itsaky.androidide.services.log.LogReceiverServiceConnection
import com.itsaky.androidide.services.log.lookupLogService
import com.itsaky.androidide.utils.ILogger
import org.slf4j.LoggerFactory
import java.util.concurrent.atomic.AtomicBoolean
/**
@@ -40,7 +40,6 @@ import java.util.concurrent.atomic.AtomicBoolean
*/
class AppLogFragment : LogViewFragment() {
private val log = ILogger.newInstance("AppLogFragment")
private val isBoundToLogReceiver = AtomicBoolean(false)
private var logServiceConnection: LogReceiverServiceConnection? = null
@@ -77,6 +76,11 @@ class AppLogFragment : LogViewFragment() {
}
}
companion object {
private val log = LoggerFactory.getLogger(AppLogFragment::class.java)
}
override fun isSimpleFormattingEnabled() = false
override fun getFilename() = "app_logs"
@@ -130,8 +134,8 @@ class AppLogFragment : LogViewFragment() {
}
val context = context ?: return
val intent = Intent(context, LogReceiverService::class.java)
.setAction(LogReceiverService.ACTION_CONNECT_LOG_CONSUMER)
val intent = Intent(context, LogReceiverService::class.java).setAction(
LogReceiverService.ACTION_CONNECT_LOG_CONSUMER)
val serviceConnection = logServiceConnection ?: LogReceiverServiceConnection { binder ->
logReceiverImpl = binder
@@ -19,8 +19,12 @@ package com.itsaky.androidide.fragments.output
import android.os.Bundle
import android.view.View
import androidx.lifecycle.Lifecycle
import ch.qos.logback.classic.Logger
import ch.qos.logback.classic.LoggerContext
import com.itsaky.androidide.R
import com.itsaky.androidide.utils.ILogger
import com.itsaky.androidide.logging.LifecycleAwareAppender
import org.slf4j.LoggerFactory
/**
* Fragment to show IDE logs.
@@ -28,21 +32,7 @@ import com.itsaky.androidide.utils.ILogger
*/
class IDELogFragment : LogViewFragment() {
private var logListener: ILogger.LogListener? =
ILogger.LogListener { level, tag, message ->
if (message.contains("\n")) {
val split = message.split("\n").toTypedArray()
for (line in split) {
logLine(level, tag, line)
}
} else {
logLine(level, tag, message)
}
}
init {
ILogger.addLogListener(this.logListener)
}
private val lifecycleAwareAppender = LifecycleAwareAppender(Lifecycle.State.CREATED)
override fun isSimpleFormattingEnabled() = true
override fun getFilename() = "ide_logs"
@@ -50,11 +40,24 @@ class IDELogFragment : LogViewFragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
emptyStateViewModel.emptyMessage.value = getString(R.string.msg_emptyview_idelogs)
lifecycleAwareAppender.consumer = this::appendLine
lifecycleAwareAppender.attachTo(viewLifecycleOwner)
val loggerContext = LoggerFactory.getILoggerFactory() as LoggerContext
val rootLogger = LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME) as Logger
lifecycleAwareAppender.context = loggerContext
lifecycleAwareAppender.start()
rootLogger.addAppender(lifecycleAwareAppender)
}
override fun onDestroy() {
super.onDestroy()
ILogger.removeLogListener(logListener)
logListener = null
lifecycleAwareAppender.stop()
val logger = LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME) as Logger
logger.detachAppender(lifecycleAwareAppender)
}
}
@@ -30,10 +30,10 @@ import com.itsaky.androidide.editor.schemes.IDEColorScheme
import com.itsaky.androidide.editor.schemes.IDEColorSchemeProvider
import com.itsaky.androidide.fragments.EmptyStateFragment
import com.itsaky.androidide.models.LogLine
import com.itsaky.androidide.utils.ILogger
import com.itsaky.androidide.utils.ILogger.Level
import com.itsaky.androidide.utils.jetbrainsMono
import io.github.rosemoe.sora.widget.style.CursorAnimator
import org.slf4j.LoggerFactory
import java.util.concurrent.ArrayBlockingQueue
import java.util.concurrent.atomic.AtomicBoolean
import java.util.concurrent.locks.ReentrantLock
@@ -51,6 +51,8 @@ abstract class LogViewFragment :
companion object {
private val log = LoggerFactory.getLogger(LogViewFragment::class.java)
/** The maximum number of characters to append to the editor in case of huge log texts. */
const val MAX_CHUNK_SIZE = 10000
@@ -87,13 +89,10 @@ abstract class LogViewFragment :
private val cache = StringBuilder()
private var cacheLineTrack = ArrayBlockingQueue<Int>(MAX_LINE_COUNT, true)
private val log = ILogger.newInstance("LogViewFragment")
private val isTrimming = AtomicBoolean(false)
private val logHandler = Handler(Looper.getMainLooper())
private val logRunnable =
object : Runnable {
private val logRunnable = object : Runnable {
override fun run() {
cacheLock.withLock {
if (cacheLineTrack.size == MAX_LINE_COUNT) {
@@ -125,8 +124,7 @@ abstract class LogViewFragment :
fun appendLog(line: LogLine) {
var lineString =
if (isSimpleFormattingEnabled()) {
val lineString = if (isSimpleFormattingEnabled()) {
line.toSimpleString()
} else {
line.toString()
@@ -134,8 +132,13 @@ abstract class LogViewFragment :
line.recycle()
if (!lineString.endsWith("\n")) {
lineString += "\n"
appendLine(lineString)
}
protected fun appendLine(line: String) {
var lineStr = line
if (!lineStr.endsWith("\n")) {
lineStr += "\n"
}
if (isTrimming.get() || cache.isNotEmpty() || System.currentTimeMillis() - lastLog <= LOG_FREQUENCY) {
@@ -143,7 +146,7 @@ abstract class LogViewFragment :
logHandler.removeCallbacks(logRunnable)
// If the log lines are too frequent, cache the lines to log them later at once
cache.append(lineString)
cache.append(lineStr)
logHandler.postDelayed(logRunnable, LOG_DELAY)
lastLog = System.currentTimeMillis()
@@ -159,7 +162,7 @@ abstract class LogViewFragment :
lastLog = System.currentTimeMillis()
append(lineString)
append(lineStr)
trimLinesAtStart()
}
@@ -188,7 +191,7 @@ abstract class LogViewFragment :
isTrimming.set(true)
val lastLine = lineCount - MAX_LINE_COUNT
log.debug("Deleting log text till line $lastLine")
log.debug("Deleting log text till line {}", lastLine)
delete(0, 0, lastLine, getColumnCount(lastLine))
isTrimming.set(false)
}
@@ -240,11 +243,8 @@ abstract class LogViewFragment :
}
}
IDEColorSchemeProvider.readSchemeAsync(
context = requireContext(),
coroutineScope = editor.editorScope,
type = LogLanguage.TS_TYPE
) { scheme ->
IDEColorSchemeProvider.readSchemeAsync(context = requireContext(),
coroutineScope = editor.editorScope, type = LogLanguage.TS_TYPE) { scheme ->
val language = TreeSitterLanguageProvider.forType(LogLanguage.TS_TYPE, requireContext())
checkNotNull(language) { "No TreeSitterLanguage found for type ${LogLanguage.TS_TYPE}" }
@@ -26,7 +26,6 @@ import com.itsaky.androidide.adapters.BuildVariantsAdapter
import com.itsaky.androidide.databinding.FragmentBuildVariantsBinding
import com.itsaky.androidide.fragments.EmptyStateFragment
import com.itsaky.androidide.tooling.api.models.BuildVariantInfo
import com.itsaky.androidide.tooling.api.models.mapToSelectedVariants
import com.itsaky.androidide.viewmodel.BuildVariantsViewModel
import com.itsaky.androidide.viewmodel.EditorViewModel
@@ -21,7 +21,8 @@ import android.content.Context
import com.itsaky.androidide.activities.editor.EditorHandlerActivity
import com.itsaky.androidide.eventbus.events.Event
import com.itsaky.androidide.eventbus.events.EventReceiver
import com.itsaky.androidide.utils.ILogger
import org.slf4j.Logger
import org.slf4j.LoggerFactory
/**
* Base class for event handlers.
@@ -30,13 +31,17 @@ import com.itsaky.androidide.utils.ILogger
*/
abstract class BaseEventHandler : EventReceiver {
protected val log = ILogger.newInstance(javaClass.simpleName)
companion object {
@JvmStatic
protected val log: Logger = LoggerFactory.getLogger(BaseEventHandler::class.java)
}
protected open fun checkIsEditorActivity(event: Event): Boolean {
return event.get(Context::class.java) is EditorHandlerActivity
}
protected open fun logCannotHandle(event: Event) {
log.warn("Context is not EditorActivity. Cannot handle ${event.javaClass.simpleName} event.")
log.warn("Context is not EditorActivity. Cannot handle {} event.", event.javaClass.simpleName)
}
}
@@ -26,9 +26,9 @@ import com.itsaky.androidide.tooling.api.messages.result.BuildInfo
import com.itsaky.androidide.tooling.events.ProgressEvent
import com.itsaky.androidide.tooling.events.configuration.ProjectConfigurationStartEvent
import com.itsaky.androidide.tooling.events.task.TaskStartEvent
import com.itsaky.androidide.utils.ILogger
import com.itsaky.androidide.utils.flashError
import com.itsaky.androidide.utils.flashSuccess
import org.slf4j.LoggerFactory
import java.lang.ref.WeakReference
/**
@@ -42,7 +42,7 @@ class EditorBuildEventListener : GradleBuildService.EventListener {
companion object {
private val log = ILogger.newInstance("EditorBuildEventListener")
private val log = LoggerFactory.getLogger(EditorBuildEventListener::class.java)
}
private val _activity: EditorHandlerActivity?
@@ -137,7 +137,7 @@ class EditorBuildEventListener : GradleBuildService.EventListener {
return _activity.also {
if (it == null) {
log.warn("[$action] Activity reference has been destroyed!")
log.warn("[{}] Activity reference has been destroyed!", action)
enabled = false
}
}
@@ -83,7 +83,7 @@ class FileTreeActionHandler : BaseEventHandler() {
if (MB_10 < event.file.length()) {
flashError("File is too big!")
log.warn(
"Cannot open ${event.file.name} as it is too big. File size: ${event.file.length()} bytes")
"Cannot open {} as it is too big. File size: {} bytes", event.file, event.file.length())
return
}
@@ -17,9 +17,8 @@
**************************************************************************************/
package com.itsaky.androidide.interfaces;
import com.itsaky.androidide.models.DiagnosticGroup;
import com.itsaky.androidide.lsp.models.DiagnosticItem;
import com.itsaky.androidide.models.DiagnosticGroup;
import java.io.File;
public interface DiagnosticClickListener {
@@ -0,0 +1,96 @@
/*
* This file is part of AndroidIDE.
*
* AndroidIDE is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* AndroidIDE is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
*/
package com.itsaky.androidide.logging
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleEventObserver
import androidx.lifecycle.LifecycleOwner
import ch.qos.logback.classic.encoder.PatternLayoutEncoder
import ch.qos.logback.classic.spi.ILoggingEvent
import ch.qos.logback.core.AppenderBase
import ch.qos.logback.core.Context
/**
* An [AppenderBase] implementation to show the logs in the GUI.
*
* @author Akash Yadav
*/
class LifecycleAwareAppender @JvmOverloads constructor(
private val requireLifecycleState: Lifecycle.State = Lifecycle.State.CREATED,
var consumer: ((String) -> Unit)? = null
) : AppenderBase<ILoggingEvent>(), LifecycleEventObserver {
private var currentState: Lifecycle.State? = null
private val encoder = PatternLayoutEncoder()
init {
setName("LifecycleAwareAppender")
encoder.pattern = "%date %-5level [%thread] %logger{0}:"
encoder.isOutputPatternAsHeader = true
}
fun attachTo(lifecycleOwner: LifecycleOwner) = attachTo(lifecycleOwner.lifecycle)
fun attachTo(lifecycle: Lifecycle) = lifecycle.addObserver(this)
fun detachFrom(lifecycleOwner: LifecycleOwner) = detachFrom(lifecycleOwner.lifecycle)
fun detachFrom(lifecycle: Lifecycle) = lifecycle.removeObserver(this)
override fun onStateChanged(source: LifecycleOwner, event: Lifecycle.Event) {
this.currentState = when (event) {
Lifecycle.Event.ON_ANY -> null
else -> event.targetState
}
}
override fun isStarted(): Boolean {
return (super.isStarted()
&& currentState?.isAtLeast(this.requireLifecycleState) == true
&& consumer != null)
}
override fun start() {
this.encoder.start()
super.start()
}
override fun stop() {
super.stop()
this.encoder.stop()
this.consumer = null
}
override fun setContext(context: Context?) {
super.setContext(context)
this.encoder.context = context
}
override fun append(eventObject: ILoggingEvent?) {
if (eventObject == null || !isStarted) {
return
}
// When rendering the logs in the GUI, we need to ensure that the message does not span multiple lines
// if it does, we need to prefix the message with the layout header
val prefix = encoder.layout.doLayout(eventObject)
eventObject.formattedMessage.split('\n').forEach {
consumer?.invoke("$prefix $it")
}
}
}
@@ -48,7 +48,6 @@ import com.itsaky.androidide.tasks.TaskExecutor;
import com.itsaky.androidide.ui.CodeEditorView;
import com.itsaky.androidide.utils.FlashbarActivityUtilsKt;
import com.itsaky.androidide.utils.FlashbarUtilsKt;
import com.itsaky.androidide.utils.ILogger;
import com.itsaky.androidide.utils.LSPUtils;
import io.github.rosemoe.sora.lang.diagnostic.DiagnosticsContainer;
import io.github.rosemoe.sora.text.Content;
@@ -64,6 +63,8 @@ import java.util.Set;
import java.util.TreeSet;
import java.util.stream.Collectors;
import kotlin.Unit;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* AndroidIDE specific implementation of the LanguageClient
@@ -72,7 +73,7 @@ public class IDELanguageClientImpl implements ILanguageClient {
public static final int MAX_DIAGNOSTIC_FILES = 10;
public static final int MAX_DIAGNOSTIC_ITEMS_PER_FILE = 20;
protected static final ILogger LOG = ILogger.newInstance("AbstractLanguageClient");
protected static final Logger LOG = LoggerFactory.getLogger(IDELanguageClientImpl.class);
private static IDELanguageClientImpl mInstance;
private final Map<File, List<DiagnosticItem>> diagnostics = new HashMap<>();
protected EditorHandlerActivity activity;
@@ -168,10 +169,7 @@ public class IDELanguageClientImpl implements ILanguageClient {
final var action = params.getAction();
if (!canUseActivity()) {
LOG.error(
"Unable to perform code action",
"activity=" + null,
"action=" + action);
LOG.error("Unable to perform code action activity=null action={}", action);
FlashbarUtilsKt.flashError(string.msg_cannot_perform_fix);
return;
}
@@ -199,8 +197,7 @@ public class IDELanguageClientImpl implements ILanguageClient {
(result, throwable) -> {
progress.dismiss();
if (result == null || throwable != null || !result) {
LOG.error(
"Unable to perform code action", "result=" + result, "throwable=" + throwable);
LOG.error("Unable to perform code action result={}", result, throwable);
FlashbarActivityUtilsKt.flashError(this.activity, string.msg_cannot_perform_fix);
} else if (editor != null) {
editor.executeCommand(action.getCommand());
@@ -328,10 +325,8 @@ public class IDELanguageClientImpl implements ILanguageClient {
// Trim the diagnostics list if we have too many diagnostic items.
// Including a lot of diagnostic items will result in UI lag when they are shown
if (fileDiagnostics.size() > MAX_DIAGNOSTIC_ITEMS_PER_FILE) {
LOG.warn(
"Limiting diagnostics to",
LOG.warn("Limiting diagnostics to {} items for file {}",
MAX_DIAGNOSTIC_ITEMS_PER_FILE,
"items for file",
file.getName());
fileDiagnostics = fileDiagnostics.subList(0, MAX_DIAGNOSTIC_ITEMS_PER_FILE);
@@ -18,23 +18,37 @@
package com.itsaky.androidide.models
import com.google.gson.annotations.SerializedName
import com.itsaky.androidide.utils.ILogger
import com.itsaky.androidide.utils.JSONUtility.gson
import com.itsaky.androidide.utils.ListingFileRedirect.getListingFile
import org.slf4j.LoggerFactory
import java.io.File
import java.io.FileNotFoundException
import java.io.FileReader
class ApkMetadata {
@SerializedName("version") var version = 0
@SerializedName("artifactType") var artifactType: ArtifactType? = null
@SerializedName("applicationId") var applicationId: String? = null
@SerializedName("variantName") var variantName: String? = null
@SerializedName("elements") var elements: List<MetadataElement>? = null
@SerializedName("elementType") var elementType: String? = null
@SerializedName("version")
var version = 0
@SerializedName("artifactType")
var artifactType: ArtifactType? = null
@SerializedName("applicationId")
var applicationId: String? = null
@SerializedName("variantName")
var variantName: String? = null
@SerializedName("elements")
var elements: List<MetadataElement>? = null
@SerializedName("elementType")
var elementType: String? = null
companion object {
private val log = ILogger.newInstance("ApkMetadata")
private val log = LoggerFactory.getLogger(ApkMetadata::class.java)
fun findApkFile(listingFIle: File): File? {
return try {
// This sometimes might be a redirect to the listing file
@@ -45,20 +59,20 @@ class ApkMetadata {
FileReader(redirectedFile).use {
val metadata = gson.fromJson(it, ApkMetadata::class.java)
if (!isValid(metadata)) {
log.warn("Invalid APK metadata:", metadata)
log.warn("Invalid APK metadata: {}", metadata)
return@use null
}
for (element in metadata.elements!!) {
if (element.outputFile == null) {
log.warn("No output file specified in APK metadata element:", element)
log.warn("No output file specified in APK metadata element: {}", element)
continue
}
if (element.outputFile!!.endsWith(".apk")) {
val apk = element.outputFile?.let { File(dir, it) } ?: continue
if (apk.exists() && apk.isFile) {
log.info("Found apk in metadata:", apk)
log.info("Found apk in metadata: {}", apk)
return@use apk
}
}
@@ -75,24 +89,24 @@ class ApkMetadata {
private fun isValid(metadata: ApkMetadata?): Boolean {
// Null checks
if (metadata?.artifactType?.type == null || metadata.elements == null) {
log.warn("APK metadata null check failed. Metadata:", metadata)
log.warn("APK metadata null check failed. Metadata: {}", metadata)
return false
}
val type = metadata.artifactType
val elements = metadata.elements
if (type!!.type != ArtifactType.TYPE_APK) {
log.warn("Artifact is not of type APK. Metadata:", metadata)
log.warn("Artifact is not of type APK. Metadata: {}", metadata)
return false
}
if (elements!!.isEmpty()) {
log.warn("No output elements found for metadata:", metadata)
log.warn("No output elements found for metadata: {}", metadata)
return false
}
var atLeastOneApk = false
for (element in elements) {
if (element.outputFile?.endsWith(".apk") == false) {
log.warn("Skipping output element because file is not APK:", element)
log.warn("Skipping output element because file is not APK: {}", element)
continue
}
atLeastOneApk = true
@@ -18,7 +18,6 @@
package com.itsaky.androidide.models;
import com.itsaky.androidide.lsp.models.DiagnosticItem;
import java.io.File;
import java.util.List;
@@ -20,7 +20,7 @@ package com.itsaky.androidide.models
import com.google.gson.Gson
import com.google.gson.annotations.SerializedName
import com.google.gson.stream.JsonReader
import com.itsaky.androidide.utils.ILogger
import org.slf4j.LoggerFactory
/**
* A file that is opened in editor.
@@ -36,7 +36,7 @@ data class OpenedFile(
private const val KEY_FILE = "file"
private const val KEY_SELECTION = "selection"
private val log = ILogger.newInstance("OpenedFile")
private val log = LoggerFactory.getLogger(OpenedFile::class.java)
fun readFrom(reader: JsonReader): OpenedFile? {
return try {
@@ -21,7 +21,7 @@ import com.google.gson.Gson
import com.google.gson.annotations.SerializedName
import com.google.gson.reflect.TypeToken
import com.google.gson.stream.JsonReader
import com.itsaky.androidide.utils.ILogger
import org.slf4j.LoggerFactory
import java.io.Reader
/**
@@ -34,7 +34,7 @@ data class OpenedFilesCache(@SerializedName(KEY_SELECTED_FILE) val selectedFile:
private const val KEY_SELECTED_FILE = "selectedFile"
private const val KEY_ALL_FILES = "allFiles"
private val log = ILogger.newInstance("OpenedFilesCache")
private val log = LoggerFactory.getLogger(OpenedFilesCache::class.java)
@JvmStatic
fun parse(contentReader: Reader): OpenedFilesCache? {
@@ -18,7 +18,6 @@
package com.itsaky.androidide.models;
import android.content.Context;
import androidx.annotation.DrawableRes;
import androidx.annotation.StringRes;
@@ -42,7 +42,6 @@ import com.itsaky.androidide.preferences.internal.launchAppAfterInstall
import com.itsaky.androidide.resources.R.drawable
import com.itsaky.androidide.resources.R.string
import com.itsaky.androidide.tasks.executeAsync
import com.itsaky.androidide.utils.Environment
import com.itsaky.androidide.utils.Environment.GRADLE_USER_HOME
import com.itsaky.androidide.utils.flashError
import com.itsaky.androidide.utils.flashSuccess
@@ -19,7 +19,6 @@ package com.itsaky.androidide.preferences
import androidx.preference.Preference
import com.itsaky.androidide.utils.uncheckedCast
import kotlinx.parcelize.IgnoredOnParcel
import kotlin.reflect.KMutableProperty0
internal abstract class PropertyBasedMultiChoicePreference : MultiChoicePreference() {
@@ -18,7 +18,6 @@
package com.itsaky.androidide.preferences
import androidx.preference.Preference
import com.itsaky.androidide.resources.R.string
import com.itsaky.androidide.preferences.internal.CLOSING_BRACKET_NEW_LINE
import com.itsaky.androidide.preferences.internal.EMPTY_ELEMENTS_BEHAVIOR
import com.itsaky.androidide.preferences.internal.INSERT_FINAL_NEW_LINE
@@ -49,9 +48,10 @@ import com.itsaky.androidide.preferences.internal.splitAttributes
import com.itsaky.androidide.preferences.internal.splitAttributesIndentSize
import com.itsaky.androidide.preferences.internal.trimFinalNewLine
import com.itsaky.androidide.preferences.internal.trimTrailingWhitespace
import com.itsaky.androidide.resources.R.string
import kotlinx.parcelize.IgnoredOnParcel
import org.eclipse.lemminx.dom.builder.EmptyElements
import kotlinx.parcelize.Parcelize
import org.eclipse.lemminx.dom.builder.EmptyElements
@Parcelize
class XMLPreferences(
@@ -27,19 +27,17 @@ import android.provider.DocumentsContract.Document;
import android.provider.DocumentsContract.Root;
import android.provider.DocumentsProvider;
import android.webkit.MimeTypeMap;
import androidx.annotation.NonNull;
import com.itsaky.androidide.resources.R;
import com.itsaky.androidide.utils.Environment;
import com.itsaky.androidide.utils.ILogger;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.Collections;
import java.util.LinkedList;
import java.util.Locale;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* A document provider for the Storage Access Framework which exposes the files in the $HOME/
@@ -58,12 +56,12 @@ import java.util.Locale;
public class IDEDocumentsProvider extends DocumentsProvider {
private static final String ALL_MIME_TYPES = "*/*";
private static final ILogger LOG = ILogger.newInstance("IDEDocumentsProvider");
private static final Logger LOG = LoggerFactory.getLogger(IDEDocumentsProvider.class);
private static final File BASE_DIR = getBaseDir();
// The default columns to return information about a root if no specific
// columns are requested in a query.
private static final String[] DEFAULT_ROOT_PROJECTION =
new String[] {
new String[]{
Root.COLUMN_ROOT_ID,
Root.COLUMN_MIME_TYPES,
Root.COLUMN_FLAGS,
@@ -76,7 +74,7 @@ public class IDEDocumentsProvider extends DocumentsProvider {
// The default columns to return information about a document if no specific
// columns are requested in a query.
private static final String[] DEFAULT_DOCUMENT_PROJECTION =
new String[] {
new String[]{
Document.COLUMN_DOCUMENT_ID,
Document.COLUMN_MIME_TYPE,
Document.COLUMN_DISPLAY_NAME,
@@ -276,15 +274,21 @@ public class IDEDocumentsProvider extends DocumentsProvider {
int flags = 0;
if (file.isDirectory()) {
if (file.canWrite()) flags |= Document.FLAG_DIR_SUPPORTS_CREATE;
if (file.canWrite()) {
flags |= Document.FLAG_DIR_SUPPORTS_CREATE;
}
} else if (file.canWrite()) {
flags |= Document.FLAG_SUPPORTS_WRITE;
}
if (file.getParentFile().canWrite()) flags |= Document.FLAG_SUPPORTS_DELETE;
if (file.getParentFile().canWrite()) {
flags |= Document.FLAG_SUPPORTS_DELETE;
}
final String displayName = file.getName();
final String mimeType = getMimeType(file);
if (mimeType.startsWith("image/")) flags |= Document.FLAG_SUPPORTS_THUMBNAIL;
if (mimeType.startsWith("image/")) {
flags |= Document.FLAG_SUPPORTS_THUMBNAIL;
}
final MatrixCursor.RowBuilder row = result.newRow();
row.add(Document.COLUMN_DOCUMENT_ID, docId);
@@ -296,10 +300,14 @@ public class IDEDocumentsProvider extends DocumentsProvider {
row.add(Document.COLUMN_ICON, R.mipmap.ic_launcher);
}
/** Get the file given a document id (the reverse of {@link #getDocIdForFile(File)}). */
/**
* Get the file given a document id (the reverse of {@link #getDocIdForFile(File)}).
*/
private static File getFileForDocId(String docId) throws FileNotFoundException {
final File f = new File(docId);
if (!f.exists()) throw new FileNotFoundException(f.getAbsolutePath() + " not found");
if (!f.exists()) {
throw new FileNotFoundException(f.getAbsolutePath() + " not found");
}
return f;
}
@@ -312,7 +320,9 @@ public class IDEDocumentsProvider extends DocumentsProvider {
if (lastDot >= 0) {
final String extension = name.substring(lastDot + 1).toLowerCase(Locale.ROOT);
final String mime = MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension);
if (mime != null) return mime;
if (mime != null) {
return mime;
}
}
return "application/octet-stream";
}
@@ -61,16 +61,15 @@ import com.itsaky.androidide.tooling.api.messages.result.BuildResult
import com.itsaky.androidide.tooling.api.messages.result.GradleWrapperCheckResult
import com.itsaky.androidide.tooling.api.messages.result.InitializeResult
import com.itsaky.androidide.tooling.api.messages.result.TaskExecutionResult
import com.itsaky.androidide.tooling.api.messages.toLogLine
import com.itsaky.androidide.tooling.events.ProgressEvent
import com.itsaky.androidide.utils.Environment
import com.itsaky.androidide.utils.ILogger
import com.termux.shared.termux.shell.command.environment.TermuxShellEnvironment
import kotlinx.coroutines.CoroutineName
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.launch
import org.slf4j.LoggerFactory
import java.io.File
import java.io.IOException
import java.io.InputStream
@@ -129,10 +128,9 @@ class GradleBuildService : Service(), BuildService, IToolingApiClient,
companion object {
private val log = ILogger.newInstance("GradleBuildService")
private val serverLogger = ILogger.newInstance("ToolingApiServer")
private val log = LoggerFactory.getLogger(GradleBuildService::class.java)
private val NOTIFICATION_ID = R.string.app_name
private val SERVER_System_err = ILogger.newInstance("ToolingApiErrorStream")
private val SERVER_System_err = LoggerFactory.getLogger("ToolingApiErrorStream")
}
override fun onCreate() {
@@ -188,7 +186,7 @@ class GradleBuildService : Service(), BuildService, IToolingApiClient,
mBinder?.release()
mBinder = null
log.info("Service is being destroyed.", "Dismissing the shown notification...")
log.info("Service is being destroyed. Dismissing the shown notification...")
notificationManager!!.cancel(NOTIFICATION_ID)
val lookup = Lookup.getDefault()
@@ -237,7 +235,7 @@ class GradleBuildService : Service(), BuildService, IToolingApiClient,
}
override fun onServerExited(exitCode: Int) {
log.warn("Tooling API process terminated with exit code:", exitCode)
log.warn("Tooling API process terminated with exit code: {}", exitCode)
stopForeground(STOP_FOREGROUND_REMOVE)
}
@@ -249,8 +247,15 @@ class GradleBuildService : Service(), BuildService, IToolingApiClient,
}
override fun logMessage(params: LogMessageParams) {
val line = params.toLogLine()
serverLogger.log(line.level, line.formattedTagAndMessage())
val logger = LoggerFactory.getLogger(params.tag)
when (params.level) {
'D' -> logger.debug(params.message)
'W' -> logger.warn(params.message)
'E' -> logger.error(params.message)
'I' -> logger.info(params.message)
else -> logger.trace(params.message)
}
}
override fun logOutput(line: String) {
@@ -20,7 +20,7 @@ package com.itsaky.androidide.services.builder
import android.content.ComponentName
import android.content.ServiceConnection
import android.os.IBinder
import com.itsaky.androidide.utils.ILogger
import org.slf4j.LoggerFactory
/**
* [ServiceConnection] for [GradleBuildService].
@@ -30,7 +30,11 @@ import com.itsaky.androidide.utils.ILogger
class GradleBuildServiceConnnection : ServiceConnection {
internal var onConnected: ((GradleBuildService) -> Unit)? = null
private val log = ILogger.newInstance("GradleBuildServiceConnnection")
companion object {
private val log = LoggerFactory.getLogger(GradleBuildServiceConnnection::class.java)
}
override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
val serviceBinder = service as GradleServiceBinder
@@ -25,7 +25,6 @@ import com.itsaky.androidide.tooling.api.IToolingApiClient
import com.itsaky.androidide.tooling.api.IToolingApiServer
import com.itsaky.androidide.tooling.api.util.ToolingApiLauncher
import com.itsaky.androidide.utils.Environment
import com.itsaky.androidide.utils.ILogger
import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.CoroutineName
import kotlinx.coroutines.CoroutineScope
@@ -33,6 +32,7 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.joinAll
import kotlinx.coroutines.launch
import org.slf4j.LoggerFactory
import java.io.InputStream
import java.util.concurrent.atomic.AtomicBoolean
@@ -59,7 +59,7 @@ internal class ToolingServerRunner(
companion object {
private val log = ILogger.newInstance("ToolingServerRunner")
private val log = LoggerFactory.getLogger(ToolingServerRunner::class.java)
}
fun setListener(listener: OnServerStartListener?) {
@@ -109,7 +109,7 @@ internal class ToolingServerRunner(
val processJob = launch(Dispatchers.IO) {
try {
process?.waitFor()
log.info("Tooling API process exited with code : ${process?.exitValue() ?: "<unknown>"}")
log.info("Tooling API process exited with code : {}", process?.exitValue() ?: "<unknown>")
process = null
} finally {
log.info("Destroying Tooling API process...")
@@ -22,7 +22,7 @@ import com.itsaky.androidide.logsender.ILogReceiver
import com.itsaky.androidide.logsender.ILogSender
import com.itsaky.androidide.models.LogLine
import com.itsaky.androidide.tasks.executeAsyncProvideError
import com.itsaky.androidide.utils.ILogger
import org.slf4j.LoggerFactory
import java.util.concurrent.atomic.AtomicBoolean
import java.util.concurrent.locks.ReentrantLock
import kotlin.concurrent.withLock
@@ -34,7 +34,6 @@ import kotlin.concurrent.withLock
*/
class LogReceiverImpl(consumer: ((LogLine) -> Unit)? = null) : ILogReceiver.Stub(), AutoCloseable {
private val log = ILogger.newInstance("LogReceiverImpl")
private val senderHandler = MultiLogSenderHandler()
private val senders = LogSendersRegistry()
private val consumerLock = ReentrantLock(true)
@@ -48,6 +47,11 @@ class LogReceiverImpl(consumer: ((LogLine) -> Unit)? = null) : ILogReceiver.Stub
senderHandler.consumer = value?.let { synchronizeConsumer(value) }
}
companion object {
private val log = LoggerFactory.getLogger(LogReceiverImpl::class.java)
}
private fun synchronizeConsumer(consumer: (LogLine) -> Unit): (LogLine) -> Unit {
return { line -> consumerLock.withLock { consumer(line) } }
}
@@ -98,7 +102,7 @@ class LogReceiverImpl(consumer: ((LogLine) -> Unit)? = null) : ILogReceiver.Stub
// logging this also makes sure that the package name, pid and sender ID are
// cached when the sender binds to the service
// these fields are then used on disconnectAll()
log.info("Connecting to client ${caching.packageName}:${caching.id}:${caching.pid}")
log.info("Connecting to client {}:{}:{}", caching.packageName, caching.pid, caching.id)
this.senders.put(caching)
@@ -117,7 +121,7 @@ class LogReceiverImpl(consumer: ((LogLine) -> Unit)? = null) : ILogReceiver.Stub
doAsync("startReaders") {
senders.getPendingSenders().forEach { sender ->
log.info("Notifying sender '${sender.packageName}' to start reading logs...")
log.info("Notifying sender '{}' to start reading logs...", sender.packageName)
sender.startReader(sender.port)
}
}
@@ -153,7 +157,7 @@ class LogReceiverImpl(consumer: ((LogLine) -> Unit)? = null) : ILogReceiver.Stub
}
private fun disconnectSender(packageName: String, senderId: String) {
log.info("Disconnecting from client: '${packageName}'")
log.info("Disconnecting from client: '{}'", packageName)
this.senderHandler.removeClient(senderId)
this.senders.remove(packageName)
logTotalConnected()
@@ -172,7 +176,7 @@ class LogReceiverImpl(consumer: ((LogLine) -> Unit)? = null) : ILogReceiver.Stub
private fun doAsync(actionName: String, action: () -> Unit) {
executeAsyncProvideError(action::invoke) { _, error ->
if (error != null) {
log.error("Failed to perform action '$actionName'", error)
log.error("Failed to perform action '{}'", actionName, error)
}
}
}
@@ -182,6 +186,6 @@ class LogReceiverImpl(consumer: ((LogLine) -> Unit)? = null) : ILogReceiver.Stub
}
private fun logTotalConnected() {
log.info("Total clients connected: ${senders.size}")
log.info("Total clients connected: {}", senders.size)
}
}
@@ -25,7 +25,7 @@ import com.itsaky.androidide.logsender.LogSender
import com.itsaky.androidide.lookup.Lookup
import com.itsaky.androidide.models.LogLine
import com.itsaky.androidide.preferences.logsenderEnabled
import com.itsaky.androidide.utils.ILogger
import org.slf4j.LoggerFactory
import java.util.concurrent.Executors
import java.util.concurrent.TimeUnit
import java.util.concurrent.atomic.AtomicBoolean
@@ -42,10 +42,11 @@ class LogReceiverService : Service() {
private val isBoundToConsumer = AtomicBoolean(false)
private val scheduledExecutor = Executors.newSingleThreadScheduledExecutor()
private val log = ILogger.newInstance("LogReceiverService")
companion object {
private val log = LoggerFactory.getLogger(LogReceiverService::class.java)
internal const val ACTION_CONNECT_LOG_CONSUMER = "com.itsaky.androidide.logrecevier.CONNECT_LOG_CONSUMER"
internal const val ACTION_CONNECTION_UPDATE = "com.itsaky.androidide.logreceiver.CONNECTION_UPDATE"
@@ -62,7 +63,7 @@ class LogReceiverService : Service() {
}
override fun onBind(intent: Intent?): IBinder? {
log.debug("Received bind request: $intent")
log.debug("Received bind request: {}", intent)
if (!logsenderEnabled) {
log.debug("Rejecting bind request. LogReceiver is disabled.")
@@ -83,7 +84,7 @@ class LogReceiverService : Service() {
}
if (intent?.action != LogSender.SERVICE_ACTION) {
log.debug("Rejecting bind request: action=${intent?.action}")
log.debug("Rejecting bind request: action={}", intent?.action)
return null
}
@@ -19,10 +19,10 @@ package com.itsaky.androidide.services.log
import com.itsaky.androidide.logsender.socket.SenderInfoCommand
import com.itsaky.androidide.models.LogLine
import com.itsaky.androidide.utils.ILogger
import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import org.slf4j.LoggerFactory
import java.net.Socket
import java.net.SocketException
@@ -31,15 +31,16 @@ import java.net.SocketException
*
* @author Akash Yadav
*/
class LogSenderHandler(
private val sender: SenderInfoCommand,
private val socket: Socket,
class LogSenderHandler(private val sender: SenderInfoCommand, private val socket: Socket,
internal var consumer: ((LogLine) -> Unit)? = null,
private var onClose: ((String) -> Unit)? = null
) : AutoCloseable {
private var onClose: ((String) -> Unit)? = null) : AutoCloseable {
private var manuallyClosed = false
private val log = ILogger.newInstance("LogSenderHandler")
companion object {
private val log = LoggerFactory.getLogger(LogSenderHandler::class.java)
}
suspend fun startAsync() = withContext(Dispatchers.IO) {
try {
@@ -54,8 +55,7 @@ class LogSenderHandler(
}
} catch (err: SocketException) {
if (!manuallyClosed) {
log.error("An error occurred while reading from socket")
log.error(err)
log.error("An error occurred while reading from socket", err)
}
} finally {
close()
@@ -21,16 +21,15 @@ import com.itsaky.androidide.logsender.socket.SenderInfoCommand
import com.itsaky.androidide.logsender.socket.SocketCommandParser
import com.itsaky.androidide.models.LogLine
import com.itsaky.androidide.tasks.cancelIfActive
import com.itsaky.androidide.utils.ILogger
import kotlinx.coroutines.CoroutineName
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import org.slf4j.LoggerFactory
import java.lang.Thread.currentThread
import java.net.ServerSocket
import java.util.concurrent.CancellationException
import java.util.concurrent.ConcurrentHashMap
import java.util.concurrent.atomic.AtomicBoolean
import java.util.concurrent.atomic.AtomicInteger
@@ -42,7 +41,6 @@ import java.util.concurrent.atomic.AtomicInteger
*/
class MultiLogSenderHandler(consumer: ((LogLine) -> Unit)? = null) : AutoCloseable {
private val log = ILogger.newInstance("MultiLogSenderHandler")
private val clients = ConcurrentHashMap<String, LogSenderHandler>()
private val port = AtomicInteger(-1)
private var isAlive = AtomicBoolean(false)
@@ -56,6 +54,10 @@ class MultiLogSenderHandler(consumer: ((LogLine) -> Unit)? = null) : AutoCloseab
clients.forEach { (_, client) -> client.consumer = value }
}
companion object {
private val log = LoggerFactory.getLogger(MultiLogSenderHandler::class.java)
}
fun getPort(): Int {
return port.get()
}
@@ -71,7 +73,7 @@ class MultiLogSenderHandler(consumer: ((LogLine) -> Unit)? = null) : AutoCloseab
try {
port.set(server.localPort)
log.info("Starting log receiver server socket at port ${getPort()}")
log.info("Starting log receiver server socket at port {}", getPort())
while (job?.isCancelled != true && isAlive.get()) {
val clientSocket = server.accept()
@@ -21,10 +21,8 @@
package com.itsaky.androidide.tasks.callables;
import android.content.Context;
import com.itsaky.androidide.adapters.viewholders.FileTreeViewHolder;
import com.unnamed.b.atv.model.TreeNode;
import java.io.File;
import java.io.FileFilter;
import java.util.Arrays;
@@ -21,9 +21,7 @@
package com.itsaky.androidide.tasks.callables;
import android.text.TextUtils;
import com.blankj.utilcode.util.FileUtils;
import java.io.File;
import java.io.FileFilter;
@@ -21,7 +21,6 @@
package com.itsaky.androidide.tasks.callables;
import com.blankj.utilcode.util.ZipUtils;
import java.io.File;
import java.util.List;
import java.util.concurrent.Callable;
@@ -75,7 +75,6 @@ import com.itsaky.androidide.preferences.internal.wordwrap
import com.itsaky.androidide.syntax.colorschemes.SchemeAndroidIDE
import com.itsaky.androidide.tasks.cancelIfActive
import com.itsaky.androidide.tasks.runOnUiThread
import com.itsaky.androidide.utils.ILogger
import com.itsaky.androidide.utils.customOrJBMono
import io.github.rosemoe.sora.text.Content
import io.github.rosemoe.sora.text.LineSeparator
@@ -92,6 +91,7 @@ import kotlinx.coroutines.withContext
import org.greenrobot.eventbus.EventBus
import org.greenrobot.eventbus.Subscribe
import org.greenrobot.eventbus.ThreadMode
import org.slf4j.LoggerFactory
import java.io.Closeable
import java.io.File
@@ -150,7 +150,7 @@ class CodeEditorView(
companion object {
private val log = ILogger.newInstance("CodeEditorView")
private val log = LoggerFactory.getLogger(CodeEditorView::class.java)
}
init {
@@ -232,7 +232,7 @@ class CodeEditorView(
val file = this.file ?: return false
if (!isModified && file.exists()) {
log.info("File was not modified. Skipping save operation.", file.name)
log.info("File was not modified. Skipping save operation for file {}", file.name)
return false
}
@@ -51,10 +51,10 @@ import com.itsaky.androidide.resources.R.string
import com.itsaky.androidide.tasks.TaskExecutor.CallbackWithError
import com.itsaky.androidide.tasks.TaskExecutor.executeAsync
import com.itsaky.androidide.tasks.TaskExecutor.executeAsyncProvideError
import com.itsaky.androidide.utils.ILogger
import com.itsaky.androidide.utils.IntentUtils.shareFile
import com.itsaky.androidide.utils.Symbols.forFile
import com.itsaky.androidide.utils.flashError
import org.slf4j.LoggerFactory
import java.io.File
import java.io.IOException
import java.nio.charset.StandardCharsets
@@ -87,10 +87,10 @@ constructor(
val pagerAdapter: EditorBottomSheetTabAdapter
private val log = ILogger.newInstance("EditorBottomSheet")
companion object {
private val log = LoggerFactory.getLogger(EditorBottomSheet::class.java)
const val CHILD_HEADER = 0
const val CHILD_SYMBOL_INPUT = 1
const val CHILD_ACTION = 2
@@ -129,7 +129,7 @@ constructor(
val fragment = pagerAdapter.getFragmentAtIndex(binding.tabs.selectedTabPosition)
if (fragment !is ShareableOutputFragment) {
log.error("Unknown fragment:", fragment)
log.error("Unknown fragment: {}", fragment)
return@setOnClickListener
}
@@ -148,7 +148,7 @@ constructor(
binding.clearFab.setOnClickListener {
val fragment: Fragment = pagerAdapter.getFragmentAtIndex(binding.tabs.selectedTabPosition)
if (fragment !is ShareableOutputFragment) {
log.error("Unknown fragment:", fragment)
log.error("Unknown fragment: {}", fragment)
return@setOnClickListener
}
(fragment as ShareableOutputFragment).clearOutput()
@@ -25,8 +25,6 @@ import com.itsaky.androidide.adapters.SymbolInputAdapter;
import com.itsaky.androidide.editor.ui.IDEEditor;
import com.itsaky.androidide.models.Symbol;
import com.itsaky.androidide.utils.Symbols;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class SymbolInputView extends RecyclerView {
@@ -38,10 +38,10 @@ package com.itsaky.androidide.ui.transition;
* </changeBounds>
* }</pre>
*/
import android.content.Context;
import android.graphics.Path;
import android.util.AttributeSet;
import androidx.transition.PathMotion;
public class ArcMotionPlus extends PathMotion {
@@ -22,10 +22,8 @@ import android.animation.ObjectAnimator;
import android.util.Property;
import android.view.ViewGroup;
import android.widget.ProgressBar;
import androidx.transition.Transition;
import androidx.transition.TransitionValues;
import com.itsaky.androidide.utils.IntProperty;
public class ProgressTransition extends Transition {
@@ -20,17 +20,21 @@ package com.itsaky.androidide.utils
import com.itsaky.androidide.R.string
import com.itsaky.androidide.activities.editor.BaseEditorActivity
import com.itsaky.androidide.ui.EditorBottomSheet
import org.slf4j.LoggerFactory
/** @author Akash Yadav */
class ApkInstallationSessionCallback(private var activity: BaseEditorActivity?) :
SingleSessionCallback() {
private val log = ILogger.newInstance("InstallationSessionCallback")
private var sessionId = -1
companion object {
private val log = LoggerFactory.getLogger(ApkInstallationSessionCallback::class.java)
}
override fun onCreated(sessionId: Int) {
this.sessionId = sessionId
log.debug("Created package installation session:", sessionId)
log.debug("Created package installation session: {}", sessionId)
activity?._binding?.apply {
bottomSheet.setActionText(activity!!.getString(string.msg_installing_apk))
bottomSheet.setActionProgress(0)
@@ -65,7 +69,7 @@ class ApkInstallationSessionCallback(private var activity: BaseEditorActivity?)
try {
packageInstaller.abandonSession(info.sessionId)
} catch (ex: Exception) {
log.error("Failed to abandon session ${info.sessionId} : ${ex.cause?.message ?: ex.message}")
log.error("Failed to abandon session {} : {}", info.sessionId, ex.cause?.message ?: ex.message)
}
}
}
@@ -19,7 +19,6 @@ package com.itsaky.androidide.utils
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import com.itsaky.androidide.preferences.internal.tabSize
import com.itsaky.androidide.preferences.utils.indentationString
import com.squareup.javapoet.JavaFile
import com.squareup.javapoet.MethodSpec
@@ -1,155 +0,0 @@
/*
* This file is part of AndroidIDE.
*
* AndroidIDE is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* AndroidIDE is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
*/
package com.itsaky.androidide.utils;
import static com.itsaky.androidide.lsp.models.HighlightTokenKind.ANNOTATION;
import static com.itsaky.androidide.lsp.models.HighlightTokenKind.CONSTRUCTOR;
import static com.itsaky.androidide.lsp.models.HighlightTokenKind.ENUM;
import static com.itsaky.androidide.lsp.models.HighlightTokenKind.ENUM_TYPE;
import static com.itsaky.androidide.lsp.models.HighlightTokenKind.EXCEPTION_PARAMETER;
import static com.itsaky.androidide.lsp.models.HighlightTokenKind.FIELD;
import static com.itsaky.androidide.lsp.models.HighlightTokenKind.INSTANCE_INIT;
import static com.itsaky.androidide.lsp.models.HighlightTokenKind.INTERFACE;
import static com.itsaky.androidide.lsp.models.HighlightTokenKind.LOCAL_VARIABLE;
import static com.itsaky.androidide.lsp.models.HighlightTokenKind.METHOD_DECLARATION;
import static com.itsaky.androidide.lsp.models.HighlightTokenKind.METHOD_INVOCATION;
import static com.itsaky.androidide.lsp.models.HighlightTokenKind.PACKAGE_NAME;
import static com.itsaky.androidide.lsp.models.HighlightTokenKind.PARAMETER;
import static com.itsaky.androidide.lsp.models.HighlightTokenKind.RESOURCE_VARIABLE;
import static com.itsaky.androidide.lsp.models.HighlightTokenKind.STATIC_FIELD;
import static com.itsaky.androidide.lsp.models.HighlightTokenKind.STATIC_INIT;
import static com.itsaky.androidide.lsp.models.HighlightTokenKind.TYPE_NAME;
import static com.itsaky.androidide.lsp.models.HighlightTokenKind.TYPE_PARAMETER;
import com.itsaky.androidide.lsp.models.HighlightToken;
import com.itsaky.androidide.lsp.models.HighlightTokenKind;
import com.itsaky.androidide.models.Position;
import java.util.List;
public class HighlightRangeHelper {
private static final ILogger LOG = ILogger.newInstance("HighlightRangeHelper");
private final List<HighlightToken> highlights;
public HighlightRangeHelper(List<HighlightToken> highlights) {
this.highlights = highlights;
}
public boolean isEnumType(int line, int column) {
return isOfKind(line, column, ENUM_TYPE);
}
/**
* Check if the token at the given index has the given token kind.
*
* @param line The line to check.
* @param column The column to check.
* @param kind The kind to check.
* @return {@code true} if the token at the given position has the given kind, {@code false}
* otherwise.
*/
public boolean isOfKind(int line, int column, HighlightTokenKind kind) {
final var position = new Position(line, column);
int left = 0;
int right = this.highlights.size();
int mid;
while (left < right) {
mid = (left + right) / 2;
var token = this.highlights.get(mid);
var range = token.getRange();
var pos = range.getStart().compareTo(position);
if (pos < 0) {
left = mid + 1;
} else if (pos > 0) {
right = mid - 1;
} else {
return token.getKind() == kind;
}
}
return false;
}
public boolean isAnnotationType(int line, int column) {
return isOfKind(line, column, ANNOTATION);
}
public boolean isInterface(int line, int column) {
return isOfKind(line, column, INTERFACE);
}
public boolean isEnum(int line, int column) {
return isOfKind(line, column, ENUM);
}
public boolean isParameter(int line, int column) {
return isOfKind(line, column, PARAMETER);
}
public boolean isExceptionParam(int line, int column) {
return isOfKind(line, column, EXCEPTION_PARAMETER);
}
public boolean isConstructor(int line, int column) {
return isOfKind(line, column, CONSTRUCTOR);
}
public boolean isStaticInit(int line, int column) {
return isOfKind(line, column, STATIC_INIT);
}
public boolean isInstanceInit(int line, int column) {
return isOfKind(line, column, INSTANCE_INIT);
}
public boolean isTypeParam(int line, int column) {
return isOfKind(line, column, TYPE_PARAMETER);
}
public boolean isResourceVariable(int line, int column) {
return isOfKind(line, column, RESOURCE_VARIABLE);
}
public boolean isPackageName(int line, int column) {
return isOfKind(line, column, PACKAGE_NAME);
}
public boolean isClassName(int line, int column) {
return isOfKind(line, column, TYPE_NAME);
}
public boolean isField(int line, int column) {
return isOfKind(line, column, FIELD);
}
public boolean isStaticField(int line, int column) {
return isOfKind(line, column, STATIC_FIELD);
}
public boolean isMethodDeclaration(int line, int column) {
return isOfKind(line, column, METHOD_DECLARATION);
}
public boolean isMethodInvocation(int line, int column) {
return isOfKind(line, column, METHOD_INVOCATION);
}
public boolean isLocal(int line, int column) {
return isOfKind(line, column, LOCAL_VARIABLE);
}
}
@@ -23,6 +23,7 @@ import android.content.Intent
import android.content.IntentSender
import android.content.pm.PackageInstaller
import com.itsaky.androidide.services.InstallationResultReceiver
import org.slf4j.LoggerFactory
/**
* Handles result of APK installation.
@@ -31,11 +32,11 @@ import com.itsaky.androidide.services.InstallationResultReceiver
*/
object InstallationResultHandler {
private val log = ILogger.newInstance("InstallationResultHandler")
private const val INSTALL_PACKAGE_REQ_CODE = 2304
private const val INSTALL_PACKAGE_ACTION = "com.itsaky.androidide.installer.INSTALL_PACKAGE"
private val log = LoggerFactory.getLogger(InstallationResultHandler::class.java)
@JvmStatic
fun createEditorActivitySender(context: Context): IntentSender {
val intent = Intent(context, InstallationResultReceiver::class.java)
@@ -52,7 +53,7 @@ object InstallationResultHandler {
@JvmStatic
fun onResult(context: Context?, intent: Intent?): String? {
if (context == null || intent == null || intent.action != INSTALL_PACKAGE_ACTION) {
log.warn("Invalid broadcast received", "action=${intent?.action}")
log.warn("Invalid broadcast received. action={}", intent?.action)
return null
}
@@ -79,10 +80,12 @@ object InstallationResultHandler {
}
null
}
PackageInstaller.STATUS_SUCCESS -> {
log.info("Package installed successfully!")
packageName
}
PackageInstaller.STATUS_FAILURE,
PackageInstaller.STATUS_FAILURE_ABORTED,
PackageInstaller.STATUS_FAILURE_BLOCKED,
@@ -90,11 +93,12 @@ object InstallationResultHandler {
PackageInstaller.STATUS_FAILURE_INCOMPATIBLE,
PackageInstaller.STATUS_FAILURE_INVALID,
PackageInstaller.STATUS_FAILURE_STORAGE -> {
log.error("Package installation failed with status code", status, "and message:", message)
log.error("Package installation failed with status code {} and message {}", status, message)
null
}
else -> {
log.warn("Invalid status code received in broadcast:", status)
log.warn("Invalid status code received in broadcast: {}", status)
null
}
}
@@ -117,7 +117,7 @@ object IntentUtils {
} catch (e: Throwable) {
flashError(R.string.msg_app_launch_failed)
if (logError) {
ILogger.instance().error("Failed to launch application with package name '$packageName'", e)
ILogger.ROOT.error("Failed to launch application with package name '{}'", packageName, e)
}
return false
}
@@ -138,7 +138,7 @@ object IntentUtils {
} catch (e: Throwable) {
flashError(R.string.msg_app_launch_failed)
if (logError) {
ILogger.instance().error("Failed to launch app", e)
ILogger.ROOT.error("Failed to launch app", e)
}
false
}
@@ -23,6 +23,7 @@ import com.itsaky.androidide.app.IDEApplication
import com.itsaky.androidide.models.JdkDistribution
import com.itsaky.androidide.shell.executeProcessAsync
import com.termux.shared.termux.shell.command.environment.TermuxShellEnvironment
import org.slf4j.LoggerFactory
import java.io.File
import java.nio.file.Files
@@ -33,7 +34,7 @@ import java.nio.file.Files
*/
object JdkUtils {
private val log = ILogger.newInstance("JdkUtils")
private val log = LoggerFactory.getLogger(JdkUtils::class.java)
/**
* Finds the available JDK installations and returns the JAVA_HOME for each installation.
@@ -65,7 +66,7 @@ object JdkUtils {
return@mapNotNull getDistFromJavaBin(java)
} ?: run {
log.error("Failed to list files in $optDir")
log.error("Failed to list files in {}", optDir)
emptyList()
}
}
@@ -92,12 +93,12 @@ object JdkUtils {
fun getDistFromJavaBin(java: File): JdkDistribution? {
if (!java.exists() || !java.isFile || !java.canExecute()) {
log.error(
"Failed to lookup JDK installation. File '$java' does not exist or cannot be executed.")
"Failed to lookup JDK installation. File '{}' does not exist or cannot be executed.", java)
return null
}
val properties = readProperties(java) ?: run {
log.error("Failed to retrieve Java properties from java binary: '$java'")
log.error("Failed to retrieve Java properties from java binary: '{}'", java)
return null
}
@@ -107,7 +108,7 @@ object JdkUtils {
@VisibleForTesting
internal fun readDistFromProps(properties: String): JdkDistribution? {
val javaHome = Regex("java\\.home\\s*=\\s*(.*)").find(properties)?.groupValues?.get(1) ?: run {
log.error("Failed to determine property 'java.home'. Properties:", properties)
log.error("Failed to determine property 'java.home'. Properties: {}", properties)
return null
}
@@ -115,11 +116,11 @@ object JdkUtils {
val javaVersion = Regex("java\\.version\\s*=\\s*(.*)").find(properties)?.groupValues?.get(1)
?: run {
log.error("Failed to determine property 'java.version'. Properties:", properties)
log.error("Failed to determine property 'java.version'. Properties: {}", properties)
return null
}
log.debug("Found java.version=${javaVersion}")
log.debug("Found java.version={}", javaVersion)
return JdkDistribution(javaVersion, javaHome)
}
@@ -149,7 +150,8 @@ object JdkUtils {
if (!canExecute(shell)) {
log.warn(
"Unable to determine JDK installations. Command ${shell.absolutePath} not found or is not executable.")
"Unable to determine JDK installations. Command {} not found or is not executable.",
shell.absolutePath)
return null
}
@@ -21,15 +21,10 @@ import static java.lang.Character.isUpperCase;
import static java.lang.Character.toLowerCase;
import androidx.annotation.NonNull;
import com.blankj.utilcode.util.ResourceUtils;
import com.blankj.utilcode.util.ThreadUtils;
import com.itsaky.androidide.interfaces.ProjectWriterCallback;
import java.io.File;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import kotlin.text.StringsKt;
public class ProjectWriter {
@@ -25,7 +25,8 @@ import com.itsaky.androidide.models.Position;
import com.itsaky.androidide.models.Range;
import com.itsaky.androidide.models.SearchResult;
import com.itsaky.androidide.tasks.TaskExecutor;
import io.github.rosemoe.sora.text.CharPosition;
import io.github.rosemoe.sora.text.Content;
import java.io.File;
import java.io.FileFilter;
import java.util.ArrayList;
@@ -36,9 +37,6 @@ import java.util.concurrent.Callable;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import io.github.rosemoe.sora.text.CharPosition;
import io.github.rosemoe.sora.text.Content;
/**
* This class provides API to search in files recursively
*
@@ -46,8 +44,6 @@ import io.github.rosemoe.sora.text.Content;
*/
public class RecursiveFileSearcher {
private static final ILogger logger = ILogger.newInstance("RecursiveFileSearcher");
/**
* Search the given text in files recursively in given search directories
*
@@ -97,9 +93,13 @@ public class RecursiveFileSearcher {
FileUtils.listFilesInDirWithFilter(dir, new MultiFileFilter(exts), true);
for (int j = 0; files != null && j < files.size(); j++) {
final File file = files.get(j);
if (file.isDirectory()) continue;
if (file.isDirectory()) {
continue;
}
final String text = FileIOUtils.readFile2String(file);
if (text == null || text.trim().isEmpty()) continue;
if (text == null || text.trim().isEmpty()) {
continue;
}
final Content content = new Content(text);
final List<SearchResult> ranges = new ArrayList<>();
Matcher matcher = Pattern.compile(Pattern.quote(this.query)).matcher(text);
@@ -157,6 +157,7 @@ public class RecursiveFileSearcher {
}
public static interface Callback {
void onResult(Map<File, List<SearchResult>> results);
}
}
@@ -19,7 +19,6 @@ package com.itsaky.androidide.utils;
import android.text.Editable;
import android.text.TextWatcher;
import androidx.annotation.NonNull;
/**
@@ -19,7 +19,6 @@ package com.itsaky.androidide.viewmodel
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import com.itsaky.androidide.projects.ProjectManagerImpl
import com.itsaky.androidide.tooling.api.models.BuildVariantInfo
/**
@@ -254,7 +254,7 @@ class EditorViewModel : ViewModel() {
fun handleOpenedFilesCacheJobCompletion(it: Job, operation: String) {
it.invokeOnCompletion { err ->
if (err != null) {
ILogger.instance().error("[EditorViewModel] Failed to $operation opened files cache", err)
ILogger.ROOT.error("[EditorViewModel] Failed to {} opened files cache", operation, err)
}
}
}
@@ -19,7 +19,6 @@ package com.itsaky.androidide.viewmodel
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import com.itsaky.androidide.R
/**
* @author Akash Yadav
@@ -18,9 +18,7 @@
package com.itsaky.androidide.adapters;
import androidx.annotation.NonNull;
import com.itsaky.androidide.models.IconTextListItem;
import java.util.List;
/**
@@ -21,10 +21,8 @@ import android.view.View
import androidx.appcompat.app.AppCompatActivity
import androidx.fragment.app.Fragment
import com.itsaky.androidide.common.R
import com.itsaky.androidide.tasks.cancelIfActive
import com.itsaky.androidide.ui.themes.IThemeManager
import com.itsaky.androidide.utils.ILogger
import com.itsaky.androidide.utils.resolveAttr
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
@@ -84,10 +82,4 @@ abstract class BaseIDEActivity : AppCompatActivity() {
protected open fun preSetContentLayout() {}
protected abstract fun bindLayout(): View
companion object {
const val REQCODE_STORAGE = 1009
protected var LOG = ILogger.newInstance("StudioActivity")
}
}
@@ -31,15 +31,12 @@ import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.ViewGroup;
import android.widget.LinearLayout;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.core.content.ContextCompat;
import com.google.android.material.slider.Slider;
import com.itsaky.androidide.common.R;
import com.itsaky.androidide.common.databinding.LayoutColorPickerBinding;
import com.itsaky.androidide.utils.ILogger;
/**
* A color picker view.
@@ -48,7 +45,6 @@ import com.itsaky.androidide.utils.ILogger;
*/
public class ColorPickerView extends LinearLayout {
private static final ILogger LOG = ILogger.newInstance("ColorPickerView");
private final LayoutColorPickerBinding binding;
private final int DEFAULT_COLOR;
private int alpha;
@@ -142,11 +138,14 @@ public class ColorPickerView extends LinearLayout {
this.mPickListener = listener;
}
/** A TextWatcher that is used to listen for text updates in the hex color input field. */
/**
* A TextWatcher that is used to listen for text updates in the hex color input field.
*/
private class ColorTextWatcher implements TextWatcher {
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
@@ -160,8 +159,10 @@ public class ColorPickerView extends LinearLayout {
}
@Override
public void afterTextChanged(Editable s) {}
public void afterTextChanged(Editable s) {
}
}
/**
* Set the color value of this picker to the given hex color code.
*
@@ -187,6 +188,7 @@ public class ColorPickerView extends LinearLayout {
*/
void onPick(int color, String hexCode);
}
/**
* Set the color value for this color picker. this will in turn update value of the sliders and
* the color preview.
@@ -217,7 +219,9 @@ public class ColorPickerView extends LinearLayout {
return argb(alpha, red, green, blue);
}
/** Call this method to update the preview of the current color. */
/**
* Call this method to update the preview of the current color.
*/
private void updatePreview() {
this.binding.colorPreview.setBackgroundColor(getColor());
}

Some files were not shown because too many files have changed in this diff Show More