feat: add AIDL based LogSender API

This commit is contained in:
Akash Yadav
2023-04-11 22:30:25 +05:30
parent 5152053125
commit e52e62136e
41 changed files with 1295 additions and 86 deletions
+1
View File
@@ -126,6 +126,7 @@ dependencies {
implementation(projects.subprojects.xmlUtils)
implementation(projects.subprojects.projects)
implementation(projects.subprojects.toolingApi)
implementation(projects.logsenderApi)
implementation(projects.lsp.api)
implementation(projects.lsp.java)
implementation(projects.lsp.xml)
+17 -10
View File
@@ -2,6 +2,14 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<permission
android:name="com.itsaky.androidide.permission.BIND_LOG_SERVICE"
android:description="@string/permdesc_bind_logger_service"
android:label="@string/permlab_bind_logger_service"
android:protectionLevel="normal" />
<uses-permission android:name="com.itsaky.androidide.permission.BIND_LOG_SERVICE" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
@@ -46,8 +54,8 @@
</activity>
<activity
android:name=".activities.editor.EditorActivityKt"
android:launchMode="singleTop"
android:configChanges="orientation|screenSize"
android:launchMode="singleTop"
android:windowSoftInputMode="adjustResize" />
<activity
android:name=".activities.PreferencesActivity"
@@ -63,6 +71,14 @@
<service
android:name=".services.builder.GradleBuildService"
android:exported="false" />
<service
android:name=".services.log.LogReceiverService"
android:exported="true"
android:permission="com.itsaky.androidide.permission.BIND_LOG_SERVICE">
<intent-filter>
<action android:name="com.itsaky.androidide.LOG_SERVICE_ACTION"/>
</intent-filter>
</service>
<provider
android:name=".provider.IDEDocumentsProvider"
@@ -84,15 +100,6 @@
android:resource="@xml/ide_file_provider_paths" />
</provider>
<receiver
android:name=".services.LogReceiver"
android:enabled="true"
android:exported="true"
tools:ignore="ExportedReceiver">
<intent-filter>
<action android:name="com.itsaky.androidide.logs.APPEND_LOG" />
</intent-filter>
</receiver>
<receiver
android:name=".services.InstallationResultReceiver"
android:enabled="true"
Binary file not shown.
@@ -18,7 +18,6 @@
package com.itsaky.androidide.activities.editor
import android.content.Intent
import android.content.IntentFilter
import android.content.pm.PackageInstaller.SessionCallback
import android.graphics.drawable.GradientDrawable
import android.os.Bundle
@@ -69,6 +68,7 @@ import com.itsaky.androidide.fragments.SearchResultFragment
import com.itsaky.androidide.handlers.EditorActivityLifecyclerObserver
import com.itsaky.androidide.handlers.LspHandler.registerLanguageServers
import com.itsaky.androidide.interfaces.DiagnosticClickListener
import com.itsaky.androidide.logsender.LogSender
import com.itsaky.androidide.lookup.Lookup
import com.itsaky.androidide.lsp.models.DiagnosticItem
import com.itsaky.androidide.models.DiagnosticGroup
@@ -79,7 +79,9 @@ import com.itsaky.androidide.models.SearchResult
import com.itsaky.androidide.projects.ProjectManager.getProjectDirPath
import com.itsaky.androidide.projects.ProjectManager.projectPath
import com.itsaky.androidide.projects.builder.BuildService
import com.itsaky.androidide.services.LogReceiver
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.ui.editor.CodeEditorView
import com.itsaky.androidide.uidesigner.UIDesignerActivity
import com.itsaky.androidide.utils.ActionMenuUtils.createMenu
@@ -121,7 +123,9 @@ abstract class BaseEditorActivity :
protected var isDestroying = false
protected val log: ILogger = ILogger.newInstance("EditorActivity")
protected val logReceiver: LogReceiver = LogReceiver().setLogListener(::appendApkLog)
protected val logServiceConnection = LogReceiverServiceConnection {
lookupLogService()?.setConsumer(this::appendApkLog)
}
internal var installationCallback: ApkInstallationSessionCallback? = null
@@ -162,10 +166,13 @@ abstract class BaseEditorActivity :
protected open fun preDestroy() {
installationCallback?.destroy()
installationCallback = null
try {
unregisterReceiver(logReceiver)
} catch (th: Throwable) {
log.error("Failed to release resources", th)
lookupLogService()?.setConsumer(null)
logServiceConnection.onConnected = null
unbindService(logServiceConnection)
} catch (e: Exception) {
log.error("Failed to unbind LogReceiver service")
}
}
@@ -225,7 +232,7 @@ abstract class BaseEditorActivity :
setupViews()
KeyboardUtils.registerSoftInputChangedListener(this) { onSoftInputChanged() }
registerLogReceiver()
startLogReceiver()
setupContainers()
setupDiagnosticInfo()
@@ -626,10 +633,14 @@ abstract class BaseEditorActivity :
startActivity(intent)
}
private fun registerLogReceiver() {
val filter = IntentFilter()
filter.addAction(LogReceiver.APPEND_LOG)
registerReceiver(logReceiver, filter)
private fun startLogReceiver() {
try {
val intent = Intent(this, LogReceiverService::class.java).setAction(LogSender.SERVICE_ACTION)
check(bindService(intent, logServiceConnection, BIND_AUTO_CREATE or BIND_IMPORTANT))
log.info("LogReceiver service is being started")
} catch (err: Throwable) {
log.error("Failed to start LogReceiver service", err)
}
}
private fun showNeedHelpDialog() {
@@ -1,65 +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.services;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import com.itsaky.androidide.models.LogLine;
import com.itsaky.androidide.utils.ILogger;
public class LogReceiver extends BroadcastReceiver {
public static final String APPEND_LOG = "com.itsaky.androidide.logs.APPEND_LOG";
public static final String EXTRA_LINE = "log_line";
private final ILogger LOG = ILogger.newInstance("LogReceiver");
private LogListener listener;
public LogReceiver setLogListener(LogListener listener) {
this.listener = listener;
return this;
}
@Override
public void onReceive(Context context, Intent intent) {
if (intent.getAction().equals(APPEND_LOG) && intent.hasExtra(EXTRA_LINE)) {
String line = intent.getStringExtra(EXTRA_LINE);
if (line == null) return;
try {
sendLogLine(line);
} catch (Throwable th) {
LOG.error("Unable to parse log line from app.", th);
}
}
}
private void sendLogLine(String line) {
final var log = LogLine.forLogString(line);
if (listener != null) {
listener.appendLogLine(log);
}
}
public interface LogListener {
void appendLogLine(LogLine line);
}
}
@@ -0,0 +1,45 @@
/*
* 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.services.log
import com.itsaky.androidide.logsender.ILogReceiver
import com.itsaky.androidide.logsender.ILogSender
import com.itsaky.androidide.models.LogLine
/**
* @author Akash Yadav
*/
class LogReceiverImpl(internal var consumer: ((LogLine) -> Unit)? = null) : ILogReceiver.Stub(), AutoCloseable {
private var sender: ILogSender? = null
override fun connect(sender: ILogSender?) {
this.sender = sender
this.sender?.startReader()
}
override fun onLog(line: String?) {
LogLine.forLogString(line)?.let {
consumer?.invoke(it)
}
}
override fun close() {
consumer = null
}
}
@@ -0,0 +1,65 @@
/*
* 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.services.log
import android.app.Service
import android.content.Intent
import android.os.IBinder
import com.itsaky.androidide.logsender.LogSender
import com.itsaky.androidide.lookup.Lookup
import com.itsaky.androidide.models.LogLine
/**
* Service for handling log lines that are sent by the client applications.
*
* @author Akash Yadav
*/
class LogReceiverService : Service() {
private val binder = LogReceiverImpl()
companion object {
@JvmStatic
internal val LOOKUP_KEY = Lookup.Key<LogReceiverService>()
}
override fun onCreate() {
super.onCreate()
Lookup.DEFAULT.update(LOOKUP_KEY, this)
}
override fun onBind(intent: Intent?): IBinder? {
if (intent?.action != LogSender.SERVICE_ACTION) {
return null
}
return binder
}
override fun onDestroy() {
super.onDestroy()
Lookup.DEFAULT.unregister(LOOKUP_KEY)
}
fun setConsumer(consumer: ((LogLine) -> Unit)?) {
binder.consumer = consumer
}
}
fun lookupLogService() : LogReceiverService? {
return Lookup.DEFAULT.lookup(LogReceiverService.LOOKUP_KEY)
}
@@ -0,0 +1,38 @@
/*
* 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.services.log
import android.content.ComponentName
import android.content.ServiceConnection
import android.os.IBinder
/**
* [ServiceConnection] for [LogReceiverService].
*
* @author Akash Yadav
*/
class LogReceiverServiceConnection(var onConnected: (() -> Unit)? = null) : ServiceConnection {
override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
onConnected?.invoke()
}
override fun onServiceDisconnected(name: ComponentName?) {
onConnected = null
}
}
@@ -0,0 +1,32 @@
/*
* 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 android.content.ComponentName
import android.content.ServiceConnection
import android.os.IBinder
/**
* A no-op [ServiceConnection].
*
* @author Akash Yadav
*/
class NoOpServiceConnection : ServiceConnection {
override fun onServiceConnected(name: ComponentName?, service: IBinder?) {}
override fun onServiceDisconnected(name: ComponentName?) {}
}
+20
View File
@@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8"?><!--
~ 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/>.
-->
<resources>
<bool name="logsender_auto_install">false</bool>
</resources>
@@ -79,6 +79,9 @@ public class LogLine {
}
public static LogLine forLogString(final String log) {
if (log == null) {
return null;
}
try {
final var split = log.split("\\s", 7);
return new LogLine(
+1
View File
@@ -0,0 +1 @@
/build
+38
View File
@@ -0,0 +1,38 @@
/*
* 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/>.
*/
plugins {
id("com.android.library")
id("kotlin-android")
}
android {
namespace = "com.itsaky.androidide.logsender"
buildFeatures.aidl = true
}
dependencies {
implementation(projects.logger)
implementation(libs.androidx.ktx)
implementation(libs.google.material)
implementation(libs.androidx.appcompat)
testImplementation(libs.tests.junit)
testImplementation(libs.tests.robolectric)
testImplementation(libs.tests.google.truth)
}
View File
+21
View File
@@ -0,0 +1,21 @@
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile
@@ -0,0 +1,42 @@
/*
* 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.logsender
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.ext.junit.runners.AndroidJUnit4
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.Assert.*
/**
* Instrumented test, which will execute on an Android device.
*
* See [testing documentation](http://d.android.com/tools/testing).
*/
@RunWith(AndroidJUnit4::class)
class ExampleInstrumentedTest {
@Test
fun useAppContext() {
// Context of the app under test.
val appContext = InstrumentationRegistry.getInstrumentation().targetContext
assertEquals("com.itsaky.androidide.logsender.test", appContext.packageName)
}
}
@@ -0,0 +1,30 @@
<?xml version="1.0" encoding="utf-8"?><!--
~ 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/>.
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<queries>
<package android:name="com.itsaky.androidide"/>
</queries>
<uses-permission android:name="com.itsaky.androidide.permission.BIND_LOG_SERVICE"/>
<application>
<provider
android:name=".utils.LogSenderInstaller"
android:authorities="${applicationId}.logsender-installer"
android:enabled="@bool/logsender_enabled"
android:exported="false" />
</application>
</manifest>
@@ -0,0 +1,19 @@
package com.itsaky.androidide.logsender;
import com.itsaky.androidide.logsender.ILogSender;
/**
* The LogReceiver interface.
*/
oneway interface ILogReceiver {
/**
* Called by the client applications to connect to log receiver in AndroidIDE.
*/
void connect(ILogSender sender);
/**
* Called when a new log line is available.
*/
void onLog(String line);
}
@@ -0,0 +1,22 @@
package com.itsaky.androidide.logsender;
/**
* The LogSender interface.
*/
interface ILogSender {
/**
* Asks the sender to start reading the logs.
*/
void startReader();
/**
* Get the process ID.
*/
int getPid();
/**
* Get the package name of the sender.
*/
String getPackageName();
}
@@ -0,0 +1,105 @@
/*
* 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.logsender
import android.app.Application
import android.content.ComponentName
import android.content.Intent
import android.content.ServiceConnection
import android.os.IBinder
import android.os.Process
import com.itsaky.androidide.logsender.utils.LogReader
import com.itsaky.androidide.logsender.utils.error
import com.itsaky.androidide.logsender.utils.info
import com.itsaky.androidide.logsender.utils.warn
import java.util.concurrent.atomic.AtomicBoolean
/**
* Entry point for LogSender.
*
* @author Akash Yadav
*/
object LogSender : ILogSender.Stub(), ServiceConnection {
private var installed = AtomicBoolean(false)
private var reader: LogReader? = null
private var receiver: ILogReceiver? = null
private var packageName: String? = null
/** Action for the LogSender service. */
const val SERVICE_ACTION = "com.itsaky.androidide.LOG_SERVICE_ACTION"
/** Constant used to indicate that the package name of the application cannot be determined. */
const val PACKAGE_UNKNOWN = "<unknown-package-name>"
/** AndroidIDE's package name. */
private const val PACKAGE_ANDROIDIDE = "com.itsaky.androidide"
fun install(app: Application) {
check(!installed.getAndSet(true)) { "LogSender has already been installed" }
this.packageName = app.packageName
if (PACKAGE_ANDROIDIDE == this.packageName) {
return
}
Intent(SERVICE_ACTION).setPackage(PACKAGE_ANDROIDIDE).let {
if (app.bindService(it, this, 0)) {
info("Binding to AndroidIDE's LogReceiver service")
} else {
error("Failed to bind to log receiver service")
}
}
installed.set(true)
}
override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
this.receiver = ILogReceiver.Stub.asInterface(service)
checkNotNull(this.receiver) { "Failed to get receiver instance" }
this.receiver!!.connect(this)
info("Connected to AndroidIDE's LogRecevier")
}
override fun onServiceDisconnected(name: ComponentName?) {
info("Disconnected from AndroidIDE's LogRecevier")
this.installed.set(false)
this.reader?.interrupt()
this.receiver = null
this.reader = null
}
override fun startReader() {
if (reader?.isAlive == true) {
warn("LogReader has already been started")
return
}
reader = LogReader { receiver?.onLog(it) }
reader?.start()
}
override fun getPid(): Int {
return Process.myPid()
}
override fun getPackageName(): String {
return packageName ?: PACKAGE_UNKNOWN
}
}
@@ -0,0 +1,48 @@
/*
* 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.logsender.utils
/**
* Reads application logs with `logcat`.
*
* @author Akash Yadav
*/
class LogReader(command: Array<String> = defaultCmd(), private val consume: (String) -> Unit) : Thread("AndroidIDE-LogReader") {
private val process = buildProcess(command)
override fun run() {
info("Starting to read logs...")
try {
val process = process.start()
process.inputStream.bufferedReader().forEachLine(consume)
} catch (err: Throwable) {
error("Failed to start logcat process", err)
}
}
private fun buildProcess(command: Array<String>): ProcessBuilder {
return ProcessBuilder(*command).apply {
this.redirectErrorStream(true)
}
}
}
fun defaultCmd(): Array<String> {
return arrayOf("logcat", "-v", "threadtime")
}
@@ -0,0 +1,75 @@
/*
* 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.logsender.utils
import android.app.Application
import android.content.ContentProvider
import android.content.ContentValues
import android.database.Cursor
import android.net.Uri
import com.itsaky.androidide.logsender.LogSender
/**
* Content providers are loaded before the application class is created. [LogSenderInstaller] is
* used to install [LogSender] on application start.
*
* [LogSenderInstaller] automatically sets up the LogSender code that runs in the main app process.
*
* @author Akash Yadav
* @see <a
* href="https://github.com/square/leakcanary/blob/main/leakcanary-object-watcher-android/src/main/java/leakcanary/internal/MainProcessAppWatcherInstaller.kt">MainProcessAppWatcherInstaller</a>
*/
internal class LogSenderInstaller : ContentProvider() {
override fun onCreate(): Boolean {
val app = context!!.applicationContext as Application
LogSender.install(app)
return true
}
override fun query(
uri: Uri,
projection: Array<out String>?,
selection: String?,
selectionArgs: Array<out String>?,
sortOrder: String?
): Cursor? {
return null
}
override fun getType(uri: Uri): String? {
return null
}
override fun insert(uri: Uri, values: ContentValues?): Uri? {
return null
}
override fun delete(uri: Uri, selection: String?, selectionArgs: Array<out String>?): Int {
return 0
}
override fun update(
uri: Uri,
values: ContentValues?,
selection: String?,
selectionArgs: Array<out String>?
): Int {
return 0
}
}
@@ -0,0 +1,28 @@
/*
* 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.logsender.utils
import com.itsaky.androidide.utils.ILogger
private val log = ILogger.newInstance("LogSender")
fun error(vararg messages: Any?) = log.error(*messages)
fun warn(vararg messages: Any?) = log.warn(*messages)
fun info(vararg messages: Any?) = log.info(*messages)
fun debug(vararg messages: Any?) = log.debug(*messages)
fun verbose(vararg messages: Any?) = log.verbose(*messages)
@@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8"?><!--
~ 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/>.
-->
<resources>
<bool name="logsender_enabled">true</bool>
</resources>
@@ -0,0 +1,35 @@
/*
* 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.logsender
import org.junit.Test
import org.junit.Assert.*
/**
* Example local unit test, which will execute on the development machine (host).
*
* See [testing documentation](http://d.android.com/tools/testing).
*/
class ExampleUnitTest {
@Test
fun addition_isCorrect() {
assertEquals(4, 2 + 2)
}
}
+1
View File
@@ -0,0 +1 @@
/build
+45
View File
@@ -0,0 +1,45 @@
/*
* 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/>.
*/
plugins {
id("com.android.application")
id("kotlin-android")
}
android {
namespace = "com.itsaky.androidide.logsender.sample"
defaultConfig {
vectorDrawables {
useSupportLibrary = true
}
}
}
dependencies {
implementation(projects.logsenderApi)
implementation(libs.androidx.ktx)
implementation(libs.androidx.appcompat)
implementation(libs.androidx.constraintlayout)
implementation(libs.google.material)
testImplementation(libs.tests.junit)
testImplementation(libs.tests.robolectric)
testImplementation(libs.tests.google.truth)
}
+21
View File
@@ -0,0 +1,21 @@
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile
@@ -0,0 +1,39 @@
<?xml version="1.0" encoding="utf-8"?><!--
~ 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/>.
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<application
android:allowBackup="true"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@drawable/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.AndroidIDE">
<activity
android:name=".MainActivity"
android:exported="true"
android:theme="@style/Theme.AndroidIDE">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
@@ -0,0 +1,55 @@
/*
* 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.logsender.sample
import android.os.Bundle
import android.os.Handler
import android.os.Looper
import android.util.Log
import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.WindowCompat
import com.itsaky.androidide.logsender.sample.databinding.ActivityMainBinding
import java.util.concurrent.atomic.AtomicInteger
class MainActivity : AppCompatActivity() {
private lateinit var binding: ActivityMainBinding
private val count = AtomicInteger(0)
private val handler = Handler(Looper.getMainLooper())
private val logger =
object : Runnable {
override fun run() {
Log.d("LogSenderSample", "Log #${count.getAndAdd(1)}")
if (count.get() == 20) {
return
}
handler.postDelayed(this, 1000)
}
}
override fun onCreate(savedInstanceState: Bundle?) {
WindowCompat.setDecorFitsSystemWindows(window, false)
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
handler.postDelayed(logger, 1000)
}
}
@@ -0,0 +1,23 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ 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/>.
-->
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@drawable/ic_launcher_background" />
<foreground android:drawable="@drawable/ic_launcher_foreground" />
<monochrome android:drawable="@drawable/ic_launcher_foreground" />
</adaptive-icon>
@@ -0,0 +1,187 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ 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/>.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="108dp"
android:height="108dp"
android:viewportWidth="108"
android:viewportHeight="108">
<path
android:fillColor="#3DDC84"
android:pathData="M0,0h108v108h-108z" />
<path
android:fillColor="#00000000"
android:pathData="M9,0L9,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,0L19,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M29,0L29,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M39,0L39,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M49,0L49,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M59,0L59,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M69,0L69,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M79,0L79,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M89,0L89,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M99,0L99,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,9L108,9"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,19L108,19"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,29L108,29"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,39L108,39"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,49L108,49"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,59L108,59"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,69L108,69"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,79L108,79"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,89L108,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,99L108,99"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,29L89,29"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,39L89,39"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,49L89,49"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,59L89,59"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,69L89,69"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,79L89,79"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M29,19L29,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M39,19L39,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M49,19L49,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M59,19L59,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M69,19L69,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M79,19L79,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
</vector>
@@ -0,0 +1,47 @@
<!--
~ 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/>.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:aapt="http://schemas.android.com/aapt"
android:width="108dp"
android:height="108dp"
android:viewportWidth="108"
android:viewportHeight="108">
<path android:pathData="M31,63.928c0,0 6.4,-11 12.1,-13.1c7.2,-2.6 26,-1.4 26,-1.4l38.1,38.1L107,108.928l-32,-1L31,63.928z">
<aapt:attr name="android:fillColor">
<gradient
android:endX="85.84757"
android:endY="92.4963"
android:startX="42.9492"
android:startY="49.59793"
android:type="linear">
<item
android:color="#44000000"
android:offset="0.0" />
<item
android:color="#00000000"
android:offset="1.0" />
</gradient>
</aapt:attr>
</path>
<path
android:fillColor="#FFFFFF"
android:fillType="nonZero"
android:pathData="M65.3,45.828l3.8,-6.6c0.2,-0.4 0.1,-0.9 -0.3,-1.1c-0.4,-0.2 -0.9,-0.1 -1.1,0.3l-3.9,6.7c-6.3,-2.8 -13.4,-2.8 -19.7,0l-3.9,-6.7c-0.2,-0.4 -0.7,-0.5 -1.1,-0.3C38.8,38.328 38.7,38.828 38.9,39.228l3.8,6.6C36.2,49.428 31.7,56.028 31,63.928h46C76.3,56.028 71.8,49.428 65.3,45.828zM43.4,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2c-0.3,-0.7 -0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C45.3,56.528 44.5,57.328 43.4,57.328L43.4,57.328zM64.6,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2s-0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C66.5,56.528 65.6,57.328 64.6,57.328L64.6,57.328z"
android:strokeWidth="1"
android:strokeColor="#00000000" />
</vector>
@@ -0,0 +1,23 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ 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/>.
-->
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@drawable/ic_launcher_background" />
<foreground android:drawable="@drawable/ic_launcher_foreground" />
<monochrome android:drawable="@drawable/ic_launcher_foreground" />
</adaptive-icon>
@@ -0,0 +1,31 @@
<?xml version="1.0" encoding="utf-8"?><!--
~ 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/>.
-->
<androidx.appcompat.widget.LinearLayoutCompat xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
android:gravity="center"
tools:context=".MainActivity">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello World!" />
</androidx.appcompat.widget.LinearLayoutCompat>
@@ -0,0 +1,24 @@
<!--
~ 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/>.
-->
<resources xmlns:tools="http://schemas.android.com/tools">
<!-- Base application theme. -->
<style name="Base.Theme.AndroidIDE" parent="Theme.Material3.DayNight.NoActionBar">
<!-- Customize your dark theme here. -->
<!-- <item name="colorPrimary">@color/my_dark_primary</item> -->
</style>
</resources>
@@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ 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/>.
-->
<resources>
</resources>
@@ -0,0 +1,20 @@
<!--
~ 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/>.
-->
<resources>
<string name="app_name">LogsenderSample</string>
</resources>
@@ -0,0 +1,26 @@
<!--
~ 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/>.
-->
<resources xmlns:tools="http://schemas.android.com/tools">
<!-- Base application theme. -->
<style name="Base.Theme.AndroidIDE" parent="Theme.Material3.DayNight.NoActionBar">
<!-- Customize your light theme here. -->
<!-- <item name="colorPrimary">@color/my_light_primary</item> -->
</style>
<style name="Theme.AndroidIDE" parent="Base.Theme.AndroidIDE" />
</resources>
@@ -548,4 +548,8 @@
<string name="title_fix_imports">Fix imports</string>
<string name="msg_no_unresolved_classes">No unresolved class names found</string>
<string name="title_class_chooser">Which %1$s?</string>
<string name="permlab_bind_logger_service">bind to log service</string>
<string name="permdesc_bind_logger_service">Allows this application to bind to AndroidIDE\'s logging service. This permission is required for AndroidIDE to read logs of this application.</string>
</resources>
+2
View File
@@ -54,6 +54,8 @@ include(
":eventbus-events",
":lexers",
":logger",
":logsender-api",
":logsender-sample",
":lookup",
":preferences",
":resources",