diff --git a/app/build.gradle.kts b/app/build.gradle.kts index e0262dece..12444ae59 100755 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -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) diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index acdd297d9..545b084a0 100755 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -2,6 +2,14 @@ + + + + @@ -46,8 +54,8 @@ + + + + + - - - - - . - * - **************************************************************************************/ - -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); - } -} diff --git a/app/src/main/java/com/itsaky/androidide/services/log/LogReceiverImpl.kt b/app/src/main/java/com/itsaky/androidide/services/log/LogReceiverImpl.kt new file mode 100644 index 000000000..db704cee1 --- /dev/null +++ b/app/src/main/java/com/itsaky/androidide/services/log/LogReceiverImpl.kt @@ -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 . + */ + +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 + } +} \ No newline at end of file diff --git a/app/src/main/java/com/itsaky/androidide/services/log/LogReceiverService.kt b/app/src/main/java/com/itsaky/androidide/services/log/LogReceiverService.kt new file mode 100644 index 000000000..b62fe3ca7 --- /dev/null +++ b/app/src/main/java/com/itsaky/androidide/services/log/LogReceiverService.kt @@ -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 . + */ + +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() + } + + 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) +} \ No newline at end of file diff --git a/app/src/main/java/com/itsaky/androidide/services/log/LogReceiverServiceConnection.kt b/app/src/main/java/com/itsaky/androidide/services/log/LogReceiverServiceConnection.kt new file mode 100644 index 000000000..dbe89c942 --- /dev/null +++ b/app/src/main/java/com/itsaky/androidide/services/log/LogReceiverServiceConnection.kt @@ -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 . + */ + +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 + } +} diff --git a/app/src/main/java/com/itsaky/androidide/utils/NoOpServiceConnection.kt b/app/src/main/java/com/itsaky/androidide/utils/NoOpServiceConnection.kt new file mode 100644 index 000000000..47bbeefb8 --- /dev/null +++ b/app/src/main/java/com/itsaky/androidide/utils/NoOpServiceConnection.kt @@ -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 . + */ + +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?) {} +} \ No newline at end of file diff --git a/app/src/main/res/values/bools.xml b/app/src/main/res/values/bools.xml new file mode 100644 index 000000000..44214b0e3 --- /dev/null +++ b/app/src/main/res/values/bools.xml @@ -0,0 +1,20 @@ + + + + false + \ No newline at end of file diff --git a/logger/src/main/java/com/itsaky/androidide/models/LogLine.java b/logger/src/main/java/com/itsaky/androidide/models/LogLine.java index 59a661f52..c510560f5 100644 --- a/logger/src/main/java/com/itsaky/androidide/models/LogLine.java +++ b/logger/src/main/java/com/itsaky/androidide/models/LogLine.java @@ -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( diff --git a/logsender-api/.gitignore b/logsender-api/.gitignore new file mode 100644 index 000000000..42afabfd2 --- /dev/null +++ b/logsender-api/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/logsender-api/build.gradle.kts b/logsender-api/build.gradle.kts new file mode 100644 index 000000000..a4f454255 --- /dev/null +++ b/logsender-api/build.gradle.kts @@ -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 . + */ + +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) +} \ No newline at end of file diff --git a/logsender-api/consumer-rules.pro b/logsender-api/consumer-rules.pro new file mode 100644 index 000000000..e69de29bb diff --git a/logsender-api/proguard-rules.pro b/logsender-api/proguard-rules.pro new file mode 100644 index 000000000..481bb4348 --- /dev/null +++ b/logsender-api/proguard-rules.pro @@ -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 \ No newline at end of file diff --git a/logsender-api/src/androidTest/java/com/itsaky/androidide/logsender/ExampleInstrumentedTest.kt b/logsender-api/src/androidTest/java/com/itsaky/androidide/logsender/ExampleInstrumentedTest.kt new file mode 100644 index 000000000..c2cfb3b65 --- /dev/null +++ b/logsender-api/src/androidTest/java/com/itsaky/androidide/logsender/ExampleInstrumentedTest.kt @@ -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 . + */ + +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) + } +} \ No newline at end of file diff --git a/logsender-api/src/main/AndroidManifest.xml b/logsender-api/src/main/AndroidManifest.xml new file mode 100644 index 000000000..07e948f1a --- /dev/null +++ b/logsender-api/src/main/AndroidManifest.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/logsender-api/src/main/aidl/com/itsaky/androidide/logsender/ILogReceiver.aidl b/logsender-api/src/main/aidl/com/itsaky/androidide/logsender/ILogReceiver.aidl new file mode 100644 index 000000000..5d36598bd --- /dev/null +++ b/logsender-api/src/main/aidl/com/itsaky/androidide/logsender/ILogReceiver.aidl @@ -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); +} \ No newline at end of file diff --git a/logsender-api/src/main/aidl/com/itsaky/androidide/logsender/ILogSender.aidl b/logsender-api/src/main/aidl/com/itsaky/androidide/logsender/ILogSender.aidl new file mode 100644 index 000000000..b2a3f11c0 --- /dev/null +++ b/logsender-api/src/main/aidl/com/itsaky/androidide/logsender/ILogSender.aidl @@ -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(); +} \ No newline at end of file diff --git a/logsender-api/src/main/java/com/itsaky/androidide/logsender/LogSender.kt b/logsender-api/src/main/java/com/itsaky/androidide/logsender/LogSender.kt new file mode 100644 index 000000000..43784ee08 --- /dev/null +++ b/logsender-api/src/main/java/com/itsaky/androidide/logsender/LogSender.kt @@ -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 . + */ + +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 = "" + + /** 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 + } +} diff --git a/logsender-api/src/main/java/com/itsaky/androidide/logsender/utils/LogReader.kt b/logsender-api/src/main/java/com/itsaky/androidide/logsender/utils/LogReader.kt new file mode 100644 index 000000000..dbbd740cb --- /dev/null +++ b/logsender-api/src/main/java/com/itsaky/androidide/logsender/utils/LogReader.kt @@ -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 . + */ + +package com.itsaky.androidide.logsender.utils + +/** + * Reads application logs with `logcat`. + * + * @author Akash Yadav + */ +class LogReader(command: Array = 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): ProcessBuilder { + return ProcessBuilder(*command).apply { + this.redirectErrorStream(true) + } + } +} + +fun defaultCmd(): Array { + return arrayOf("logcat", "-v", "threadtime") +} diff --git a/logsender-api/src/main/java/com/itsaky/androidide/logsender/utils/LogSenderInstaller.kt b/logsender-api/src/main/java/com/itsaky/androidide/logsender/utils/LogSenderInstaller.kt new file mode 100644 index 000000000..89a70ce14 --- /dev/null +++ b/logsender-api/src/main/java/com/itsaky/androidide/logsender/utils/LogSenderInstaller.kt @@ -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 . + */ + +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 MainProcessAppWatcherInstaller + */ +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?, + selection: String?, + selectionArgs: Array?, + 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?): Int { + return 0 + } + + override fun update( + uri: Uri, + values: ContentValues?, + selection: String?, + selectionArgs: Array? + ): Int { + return 0 + } +} diff --git a/logsender-api/src/main/java/com/itsaky/androidide/logsender/utils/logger.kt b/logsender-api/src/main/java/com/itsaky/androidide/logsender/utils/logger.kt new file mode 100644 index 000000000..59ddb1dc8 --- /dev/null +++ b/logsender-api/src/main/java/com/itsaky/androidide/logsender/utils/logger.kt @@ -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 . + */ + +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) diff --git a/logsender-api/src/main/res/values/bools.xml b/logsender-api/src/main/res/values/bools.xml new file mode 100644 index 000000000..999039414 --- /dev/null +++ b/logsender-api/src/main/res/values/bools.xml @@ -0,0 +1,20 @@ + + + + true + \ No newline at end of file diff --git a/logsender-api/src/test/java/com/itsaky/androidide/logsender/ExampleUnitTest.kt b/logsender-api/src/test/java/com/itsaky/androidide/logsender/ExampleUnitTest.kt new file mode 100644 index 000000000..f61a236d6 --- /dev/null +++ b/logsender-api/src/test/java/com/itsaky/androidide/logsender/ExampleUnitTest.kt @@ -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 . + */ + +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) + } +} \ No newline at end of file diff --git a/logsender-sample/.gitignore b/logsender-sample/.gitignore new file mode 100644 index 000000000..42afabfd2 --- /dev/null +++ b/logsender-sample/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/logsender-sample/build.gradle.kts b/logsender-sample/build.gradle.kts new file mode 100644 index 000000000..fed3c544f --- /dev/null +++ b/logsender-sample/build.gradle.kts @@ -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 . + */ + +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) +} \ No newline at end of file diff --git a/logsender-sample/proguard-rules.pro b/logsender-sample/proguard-rules.pro new file mode 100644 index 000000000..481bb4348 --- /dev/null +++ b/logsender-sample/proguard-rules.pro @@ -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 \ No newline at end of file diff --git a/logsender-sample/src/main/AndroidManifest.xml b/logsender-sample/src/main/AndroidManifest.xml new file mode 100644 index 000000000..c9acc996c --- /dev/null +++ b/logsender-sample/src/main/AndroidManifest.xml @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/logsender-sample/src/main/java/com/itsaky/androidide/logsender/sample/MainActivity.kt b/logsender-sample/src/main/java/com/itsaky/androidide/logsender/sample/MainActivity.kt new file mode 100644 index 000000000..bdb0e2259 --- /dev/null +++ b/logsender-sample/src/main/java/com/itsaky/androidide/logsender/sample/MainActivity.kt @@ -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 . + */ + +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) + } +} diff --git a/logsender-sample/src/main/res/drawable/ic_launcher.xml b/logsender-sample/src/main/res/drawable/ic_launcher.xml new file mode 100644 index 000000000..648fa7121 --- /dev/null +++ b/logsender-sample/src/main/res/drawable/ic_launcher.xml @@ -0,0 +1,23 @@ + + + + + + + + \ No newline at end of file diff --git a/logsender-sample/src/main/res/drawable/ic_launcher_background.xml b/logsender-sample/src/main/res/drawable/ic_launcher_background.xml new file mode 100644 index 000000000..fd5fa75c6 --- /dev/null +++ b/logsender-sample/src/main/res/drawable/ic_launcher_background.xml @@ -0,0 +1,187 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/logsender-sample/src/main/res/drawable/ic_launcher_foreground.xml b/logsender-sample/src/main/res/drawable/ic_launcher_foreground.xml new file mode 100644 index 000000000..60ef504d3 --- /dev/null +++ b/logsender-sample/src/main/res/drawable/ic_launcher_foreground.xml @@ -0,0 +1,47 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/logsender-sample/src/main/res/drawable/ic_launcher_round.xml b/logsender-sample/src/main/res/drawable/ic_launcher_round.xml new file mode 100644 index 000000000..648fa7121 --- /dev/null +++ b/logsender-sample/src/main/res/drawable/ic_launcher_round.xml @@ -0,0 +1,23 @@ + + + + + + + + \ No newline at end of file diff --git a/logsender-sample/src/main/res/layout/activity_main.xml b/logsender-sample/src/main/res/layout/activity_main.xml new file mode 100644 index 000000000..2e2dcb3a8 --- /dev/null +++ b/logsender-sample/src/main/res/layout/activity_main.xml @@ -0,0 +1,31 @@ + + + + + + + \ No newline at end of file diff --git a/logsender-sample/src/main/res/values-night/themes.xml b/logsender-sample/src/main/res/values-night/themes.xml new file mode 100644 index 000000000..34446cc75 --- /dev/null +++ b/logsender-sample/src/main/res/values-night/themes.xml @@ -0,0 +1,24 @@ + + + + + + \ No newline at end of file diff --git a/logsender-sample/src/main/res/values/colors.xml b/logsender-sample/src/main/res/values/colors.xml new file mode 100644 index 000000000..bbabce030 --- /dev/null +++ b/logsender-sample/src/main/res/values/colors.xml @@ -0,0 +1,20 @@ + + + + + \ No newline at end of file diff --git a/logsender-sample/src/main/res/values/strings.xml b/logsender-sample/src/main/res/values/strings.xml new file mode 100644 index 000000000..12e13298a --- /dev/null +++ b/logsender-sample/src/main/res/values/strings.xml @@ -0,0 +1,20 @@ + + + + LogsenderSample + \ No newline at end of file diff --git a/logsender-sample/src/main/res/values/themes.xml b/logsender-sample/src/main/res/values/themes.xml new file mode 100644 index 000000000..a6087265d --- /dev/null +++ b/logsender-sample/src/main/res/values/themes.xml @@ -0,0 +1,26 @@ + + + + + + +