diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..0f1d137 --- /dev/null +++ b/.gitignore @@ -0,0 +1,9 @@ +*.iml +.gradle +/local.properties +/.idea +.DS_Store +/build +/captures +.externalNativeBuild +.cxx diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..e7e9d11 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,2 @@ +# Default ignored files +/workspace.xml diff --git a/.idea/.name b/.idea/.name new file mode 100644 index 0000000..8f2c97f --- /dev/null +++ b/.idea/.name @@ -0,0 +1 @@ +PinLog Library \ No newline at end of file diff --git a/.idea/codeStyles b/.idea/codeStyles new file mode 100644 index 0000000..320a4bc --- /dev/null +++ b/.idea/codeStyles @@ -0,0 +1,137 @@ + + + + + + + + + + + + + +
+ + + + xmlns:android + + ^$ + + + +
+
+ + + + xmlns:.* + + ^$ + + + BY_NAME + +
+
+ + + + .*:id + + http://schemas.android.com/apk/res/android + + + +
+
+ + + + .*:name + + http://schemas.android.com/apk/res/android + + + +
+
+ + + + name + + ^$ + + + +
+
+ + + + style + + ^$ + + + +
+
+ + + + .* + + ^$ + + + BY_NAME + +
+
+ + + + .* + + http://schemas.android.com/apk/res/android + + + ANDROID_ATTRIBUTE_ORDER + +
+
+ + + + .* + + .* + + + BY_NAME + +
+
+
+
+
+
+
\ No newline at end of file diff --git a/.idea/gradle.xml b/.idea/gradle.xml new file mode 100644 index 0000000..ac6b0ae --- /dev/null +++ b/.idea/gradle.xml @@ -0,0 +1,21 @@ + + + + + + + \ No newline at end of file diff --git a/.idea/jarRepositories.xml b/.idea/jarRepositories.xml new file mode 100644 index 0000000..4dcbce1 --- /dev/null +++ b/.idea/jarRepositories.xml @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..37a7509 --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,9 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/runConfigurations.xml b/.idea/runConfigurations.xml new file mode 100644 index 0000000..7f68460 --- /dev/null +++ b/.idea/runConfigurations.xml @@ -0,0 +1,12 @@ + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..94a25f7 --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 0000000..261eeb9 --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/README.md b/README.md new file mode 100644 index 0000000..8e6184d --- /dev/null +++ b/README.md @@ -0,0 +1 @@ +# PinLog Library diff --git a/app/.gitignore b/app/.gitignore new file mode 100644 index 0000000..42afabf --- /dev/null +++ b/app/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/app/build.gradle b/app/build.gradle new file mode 100644 index 0000000..2cb53c6 --- /dev/null +++ b/app/build.gradle @@ -0,0 +1,33 @@ +apply plugin: 'com.android.library' +apply plugin: 'kotlin-android' + +android { + compileSdkVersion 31 + buildToolsVersion "30.0.3" + + defaultConfig { + minSdkVersion 19 + targetSdkVersion 31 + versionCode 1 + versionName "0.0.1" + + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + } + + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' + } + } +} + +dependencies { + implementation fileTree(dir: "libs", include: ["*.jar"]) + implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" + implementation 'androidx.core:core-ktx:1.7.0' + implementation 'androidx.appcompat:appcompat:1.4.1' + testImplementation 'junit:junit:4.12' + androidTestImplementation 'androidx.test.ext:junit:1.1.3' + androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0' +} \ No newline at end of file diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro new file mode 100644 index 0000000..481bb43 --- /dev/null +++ b/app/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/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml new file mode 100644 index 0000000..82b6d39 --- /dev/null +++ b/app/src/main/AndroidManifest.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/app/src/main/java/com/adityaamolbavadekar/pinlog/ApplicationLogsContract.kt b/app/src/main/java/com/adityaamolbavadekar/pinlog/ApplicationLogsContract.kt new file mode 100644 index 0000000..197bf31 --- /dev/null +++ b/app/src/main/java/com/adityaamolbavadekar/pinlog/ApplicationLogsContract.kt @@ -0,0 +1,81 @@ +/******************************************************************************* + * Copyright (c) 2022 Aditya Bavadekar + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ******************************************************************************/ + +package com.adityaamolbavadekar.pinlog + +import android.database.sqlite.SQLiteDatabase +import android.provider.BaseColumns +import com.adityaamolbavadekar.pinlog.ApplicationLogsContract.ApplicationLogEntry.DATABASE_CREATE +import com.adityaamolbavadekar.pinlog.ApplicationLogsContract.ApplicationLogEntry.TABLE_NAME + +internal object ApplicationLogsContract { + + object ApplicationLogEntry : BaseColumns { + const val DATABASE_VERSION: Int = 1 + + // const val DATABASE_NAME: String = "com.adityaamolbavadekar.pinlog.database.logs.db" + const val TABLE_NAME: String = "pin_logger_logs" + const val COLUMN_NAME_ID: String = "_id" + const val COLUMN_NAME_LOG: String = "logs" + const val COLUMN_NAME_LOG_LEVEL: String = "log_level" + + const val DATABASE_CREATE = ("CREATE TABLE IF NOT EXISTS " + + TABLE_NAME + + " (" + + COLUMN_NAME_ID + " INTEGER PRIMARY KEY AUTOINCREMENT, " + + COLUMN_NAME_LOG + " TEXT, " + + COLUMN_NAME_LOG_LEVEL + " INTEGER" + + ");") + } + + + fun onUpgrade(db: SQLiteDatabase?, oldVersion: Int, newVersion: Int) { + if (db == null) { + return + } + try { + db.execSQL("DROP TABLE IF EXISTS $TABLE_NAME") + onCreate(db) + PinLog.logInfo( + "PinLogTable onUpgrade called. Executing drop_table query to delete old logs." + ) + } catch (e: java.lang.Exception) { + e.printStackTrace() + PinLog.logError( + "PinLogTable: Exception occurred while executing Database in onUpgrade: $e", e + ) + } + } + + fun onCreate(db: SQLiteDatabase?) { + if (db == null) return + + try { + db.execSQL(DATABASE_CREATE) + PinLog.logInfo( + "PinLogTable: Successfully created PinLogs Database" + ) + + } catch (e: Exception) { + e.printStackTrace() + PinLog.logError( + "PinLogTable: Exception occurred while executing Database in onCreate: $e", e + ) + } + } + +} + diff --git a/app/src/main/java/com/adityaamolbavadekar/pinlog/DefaultApplicationLoggingStyle.kt b/app/src/main/java/com/adityaamolbavadekar/pinlog/DefaultApplicationLoggingStyle.kt new file mode 100644 index 0000000..3d3e9b6 --- /dev/null +++ b/app/src/main/java/com/adityaamolbavadekar/pinlog/DefaultApplicationLoggingStyle.kt @@ -0,0 +1,49 @@ +/******************************************************************************* + * Copyright (c) 2022 Aditya Bavadekar + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ******************************************************************************/ + +package com.adityaamolbavadekar.pinlog + +import java.util.* + +/** + * + * ``` + * Example : + * "Vr[1.0] 05-17 20:04:11.603 18494-18629/? D/PinLog: Hello World from PinLog" + * + * ``` + * + * */ +class DefaultApplicationLoggingStyle : LoggingStyle() { + + override fun getFormattedLogData( + TAG: String, + m: String, + e: Throwable?, + dateLong: Long, + level: PinLog.LogLevel, + VERSION_NAME: String, + VERSION_CODE: String, + PACKAGE_NAME: String + ): String { + var string = + "Vr/[${VERSION_NAME}] " + "${Date(dateLong)}" + "/ " + "${level.SHORT_NAME}/" + "$TAG : " + m + if (e != null) { + string += "\n" + PinLog.getStackTraceString(e) + } + return string + } +} \ No newline at end of file diff --git a/app/src/main/java/com/adityaamolbavadekar/pinlog/LogData.kt b/app/src/main/java/com/adityaamolbavadekar/pinlog/LogData.kt new file mode 100644 index 0000000..309a53c --- /dev/null +++ b/app/src/main/java/com/adityaamolbavadekar/pinlog/LogData.kt @@ -0,0 +1,25 @@ +/******************************************************************************* + * Copyright (c) 2022 Aditya Bavadekar + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ******************************************************************************/ + +package com.adityaamolbavadekar.pinlog + +data class LogData( + var TAG: String, + var m: String?, + var e: Throwable?, + var level: PinLog.LogLevel, + val dateLong : Long = System.currentTimeMillis() +) \ No newline at end of file diff --git a/app/src/main/java/com/adityaamolbavadekar/pinlog/LoggingStyle.kt b/app/src/main/java/com/adityaamolbavadekar/pinlog/LoggingStyle.kt new file mode 100644 index 0000000..cbff008 --- /dev/null +++ b/app/src/main/java/com/adityaamolbavadekar/pinlog/LoggingStyle.kt @@ -0,0 +1,55 @@ +/******************************************************************************* + * Copyright (c) 2022 Aditya Bavadekar + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ******************************************************************************/ + +package com.adityaamolbavadekar.pinlog + +import android.content.Context + + +/** + * Created by Aditya Bavadekar on 18 May 2022 + * + * + * Set this property in [PinLog.setLogFormatting]. Override the [LoggingStyle.getFormattedLogData] method to implement custom logging string. + * + * *Default implementation is [DefaultApplicationLoggingStyle]* + * + * */ +abstract class LoggingStyle { + + private var context:Context? =null + + constructor() + + /** + * Called before storing log to Database. + * + * *Default implementation is [DefaultApplicationLoggingStyle.getFormattedLogData]* + * */ + abstract fun getFormattedLogData( + TAG: String, + m: String, + e: Throwable?, + dateLong: Long, + level: PinLog.LogLevel, + VERSION_NAME: String, + VERSION_CODE: String, + PACKAGE_NAME: String + ): String + + + +} \ No newline at end of file diff --git a/app/src/main/java/com/adityaamolbavadekar/pinlog/PinLog.kt b/app/src/main/java/com/adityaamolbavadekar/pinlog/PinLog.kt new file mode 100644 index 0000000..cd1b4f2 --- /dev/null +++ b/app/src/main/java/com/adityaamolbavadekar/pinlog/PinLog.kt @@ -0,0 +1,1028 @@ +/******************************************************************************* + * Copyright (c) 2022 Aditya Bavadekar + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ******************************************************************************/ + +package com.adityaamolbavadekar.pinlog + +import android.app.Application +import android.content.Context +import android.content.Intent +import android.util.Log +import androidx.annotation.NonNull +import com.adityaamolbavadekar.pinlog.database.ApplicationLogDatabaseHelper +import com.adityaamolbavadekar.pinlog.database.ApplicationLogModel +import com.adityaamolbavadekar.pinlog.extensions.submitLog +import org.json.JSONArray +import org.json.JSONObject +import java.io.BufferedWriter +import java.io.File +import java.io.FileWriter +import java.util.* +import java.util.concurrent.ExecutorService +import java.util.concurrent.Executors +import kotlin.math.pow +import kotlin.system.exitProcess + + +/** + * Created by Aditya Bavadekar on 18 May 2022 + * + * **About** : + * + * PinLog is an easy-to-use android Logging Library. + * PinLog supports storing logs for later retrieval, saving logs in a file, saving logs in a zip file and more. + * It is made by Aditya Bavadekar and written in Kotlin Language. + * The library is Open-Source with its LICENCE bieng Apache 2.0. + * + * **Usage**: + * - [PinLog] should be initialised in the [Application] Class : + * ``` + * PinLog.initialise(this) + * PinLog.setDevLogging(true) + * PinLog.setBuildConfigClass(BuildConfig::class.java) + * ``` + * *OR* + * ``` + * //For Debuggable Builds + * PinLog.initialiseDebug(this@App) + * + * //For Release Builds + * PinLog.initialiseRelease(this@App) + * ``` + * - *Logging* + * Methods Available : + * 1. [PinLog.logE] + * 2. [PinLog.logW] + * 3. [PinLog.logI] + * 4. [PinLog.logD] + * + * You can also use these methods directly + * in classes which extend ContextWrapper (You dont have to add TAG property) like this : + * ``` + * + * class MainActivity : AppCompatActivity() { + * override fun onCreate(savedInstanceState: Bundle?) { + * super.onCreate(savedInstanceState) + * setContentView(R.layout.activity_main) + * + * //like here no TAG is required + * logI("onCreate") + * + * } + * } + * ``` + * + * + * - You can get Logs stored in database by [PinLog.getAllPinLogs] method. + * *Warning : This may return null or empty list if you've set [PinLog.setDevLogging] to `false`* + * + * @author Aditya Bavadekar + * @since 19 May 2022 + * + * */ +object PinLog { + + private var isDevLoggingEnabled = false + private var buildConfigClass: Class<*>? = null + private var shouldStoreLogs: Boolean = true + private var isInitialised: Boolean = false + private var app: Application? = null + private var applicationLogDataSource: ApplicationLogDatabaseHelper? = null + private var pinLoggerService: ExecutorService? = null + private var loggingStyle: LoggingStyle? = null + private var defaultExceptionHandler: Thread.UncaughtExceptionHandler? = null + private var exceptionHandlerEnabled: Boolean = false + private var sendToEmail: String? = null + private var listeners: MutableList = mutableListOf() + private var callerPackageName: String = "" + private var callerVersionName: String = "" + private var callerVersionCode: String = "" + private var callerAppName: String = "" + + private fun instance(@NonNull application: Application): Boolean { + synchronized(PinLog::class.java) { + return if (app == null) { + app = application + isInitialised = true + onInitialisation() + true + } else { + notInitialised() + false + } + } + } + + private fun onInitialisation() { + logInfo("Dev Logging is enabled") + getAppInfo() + logInfo("PinLog was initialised successfully for $callerPackageName") + if (applicationLogDataSource == null) { + applicationLogDataSource = ApplicationLogDatabaseHelper(getContext()) + } + collectBuildConfigData() + setupExceptionHandler() + } + + private fun setupExceptionHandler() { + if (exceptionHandlerEnabled) { + if (getContext().mainLooper.thread == Thread.currentThread()) { + defaultExceptionHandler = Thread.getDefaultUncaughtExceptionHandler() + Thread.setDefaultUncaughtExceptionHandler(PinLogUncaughtExceptionHandler()) + } + } + } + + private fun getContext(): Context { + return app!!.applicationContext + } + + private fun getAppInfo() { + val packageInfo = getContext().packageManager.getPackageInfo(getContext().packageName, 0) + callerVersionName = packageInfo.versionName + callerVersionCode = "${packageInfo.versionCode}" + callerPackageName = packageInfo.packageName + callerAppName = + (packageInfo.applicationInfo.loadLabel(getContext().packageManager) ?: "") as String + + } + + internal fun logError(m: String, e: Exception?=null) { + logE(CLASS_TAG, m, e) + } + + internal fun logWarning(m: String) { + logW(CLASS_TAG, m) + } + + internal fun logInfo(m: String) { + logI(CLASS_TAG, m) + } + + private fun notInitialised() { + logWarning("Could not initialise PinLog as it was previously initialised for $callerPackageName") + } + + private fun isAppInitialised(): Boolean { + return when { + app == null -> { + logWarning("Context is null. PinLog is not initialised a method was called before initialising PinLog") + false + } + applicationLogDataSource == null -> { + logWarning("Database is not created yet!") + false + } + else -> true + } + } + + + /*Public Methods*/ + + /** + * Provided [LogLevel] it logs to the specific function. + * + * */ + @JvmStatic + fun log(TAG: String, m: String, logLevel: LogLevel = LogLevel.INFO) { + when (logLevel) { + LogLevel.INFO -> logI(TAG, m) + LogLevel.WARN -> logW(TAG, m) + LogLevel.ERROR -> logE(TAG, m) + LogLevel.DEBUG -> logD(TAG, m) + } + } + + /** + * Provided [LogLevel] it logs to the specific function. + * + * */ + @JvmStatic + fun log(TAG: String, m: String, e: Throwable?, logLevel: LogLevel = LogLevel.INFO) { + when (logLevel) { + LogLevel.INFO -> logI(TAG, m, e) + LogLevel.WARN -> logW(TAG, m, e) + LogLevel.ERROR -> logE(TAG, m, e) + LogLevel.DEBUG -> logD(TAG, m, e) + } + } + + /** + * @return [String] Creates a log and returns it in form of a string. + * */ + @JvmStatic + fun getLogAsString( + TAG: String, + m: String, + e: Throwable?, + logLevel: LogLevel = LogLevel.INFO + ): String? { + if (!isAppInitialised()) { + return null + } + return loggingStyle!!.getFormattedLogData( + TAG, m, e, System.currentTimeMillis(), logLevel, + callerVersionName, callerVersionCode, callerPackageName + ) + } + + /*ERROR*/ + + /** + * **Logs an Error Log** + * + * *Logs to Logcat if [PinLog.setDevLogging] is set to `true`* + * + * *Stores the log if [PinLog.setDoStoreLogs] is set to `true`* + * + * + * */ + @JvmStatic + fun logE(TAG: String, m: String) { + if (isDevLoggingEnabled) Log.e(TAG, m) + storeLog(LogData(TAG, m, null, LogLevel.ERROR)) + } + + /** + * **Logs an Error Log** + * + * *Logs to Logcat if [PinLog.setDevLogging] is set to `true`* + * + * *Stores the log if [PinLog.setDoStoreLogs] is set to `true`* + * + * + * */ + @JvmStatic + fun logE(TAG: String, m: String?, e: Throwable?) { + if (isDevLoggingEnabled) Log.e(TAG, m, e) + storeLog(LogData(TAG, m, e, LogLevel.ERROR)) + } + + /*WARN*/ + + /** + * **Logs a Warning Log** + * + * *Logs to Logcat if [PinLog.setDevLogging] is set to `true`* + * + * *Stores the log if [PinLog.setDoStoreLogs] is set to `true`* + * + * + * */ + @JvmStatic + fun logW(TAG: String, m: String?, e: Throwable?) { + if (isDevLoggingEnabled) Log.w(TAG, m, e) + storeLog(LogData(TAG, m, e, LogLevel.WARN)) + } + + /** + * **Logs a Warning Log** + * + * *Logs to Logcat if [PinLog.setDevLogging] is set to `true`* + * + * *Stores the log if [PinLog.setDoStoreLogs] is set to `true`* + * + * + * */ + @JvmStatic + fun logW(TAG: String, m: String) { + if (isDevLoggingEnabled) Log.w(TAG, m) + storeLog(LogData(TAG, m, null, LogLevel.WARN)) + } + + /*INFO*/ + + /** + * **Logs an Info Log** + * + * *Logs to Logcat if [PinLog.setDevLogging] is set to `true`* + * + * *Stores the log if [PinLog.setDoStoreLogs] is set to `true`* + * + * + * */ + @JvmStatic + fun logI(TAG: String, m: String?, e: Throwable?) { + if (isDevLoggingEnabled) Log.i(TAG, m, e) + + storeLog(LogData(TAG, m, e, LogLevel.INFO)) + } + + /** + * **Logs an Info Log** + * + * *Logs to Logcat if [PinLog.setDevLogging] is set to `true`* + * + * *Stores the log if [PinLog.setDoStoreLogs] is set to `true`* + * + * + * */ + @JvmStatic + fun logI(TAG: String, m: String) { + if (isDevLoggingEnabled) Log.i(TAG, m) + storeLog(LogData(TAG, m, null, LogLevel.INFO)) + } + + /*DEBUG*/ + + /** + * **Logs a Debug Log** + * + * *Logs to Logcat if [PinLog.setDevLogging] is set to `true`* + * + * *Stores the log if [PinLog.setDoStoreLogs] is set to `true`* + * + * + * */ + @JvmStatic + fun logD(TAG: String, m: String?, e: Throwable?) { + if (isDevLoggingEnabled) Log.d(TAG, m, e) + storeLog(LogData(TAG, m, e, LogLevel.DEBUG)) + } + + /** + * **Logs a Debug Log** + * + * *Logs to Logcat if [PinLog.setDevLogging] is set to `true`* + * + * *Stores the log if [PinLog.setDoStoreLogs] is set to `true`* + * + * + * */ + @JvmStatic + fun logD(TAG: String, m: String) { + if (isDevLoggingEnabled) Log.d(TAG, m) + storeLog(LogData(TAG, m, null, LogLevel.DEBUG)) + } + + /*STACKTRACE*/ + /** + * **Returns Stack-Trace for the provided [Throwable]** + * + * */ + @JvmStatic + fun getStackTraceString(tr: Throwable): String { + return Log.getStackTraceString(tr) + } + + private fun storeLog(data: LogData) { + //Filter PinLog Class's logs as they are intended only for Logcat logging + when { + data.TAG == CLASS_TAG -> return + !isAppInitialised() -> { + return + } + else -> { + val log = loggingStyle!!.getFormattedLogData( + data.TAG, data.m ?: "", + data.e, + data.dateLong, + data.level, + callerVersionName, + callerVersionCode, + callerPackageName + ) + + if (pinLoggerService == null) { + pinLoggerService = Executors.newSingleThreadExecutor() + } + + if (shouldStoreLogs) { + val runnable = Runnable { + try { + applicationLogDataSource?.insertPinLog( + ApplicationLogModel( + id = 0, + LOG = log, + LOG_LEVEL = data.level.LEVEL_INT + ) + ) + } catch (ex: java.lang.Exception) { + ex.printStackTrace() + } + } + pinLoggerService?.submit(runnable) + } + + listeners.submitLog(log) + } + } + } + + enum class LogLevel(val LEVEL_INT: Int, val SHORT_NAME: String) { + ERROR(0, "E"), + WARN(1, "W"), + INFO(2, "I"), + DEBUG(3, "D") + } + + interface OnLogAddedListener { + fun onLogAdded(log: String) + } + + /** + * Call this to add a [OnLogAddedListener] which notifies you when a new log is added + * @return [Boolean] whether listener was added. + * + * */ + @JvmStatic + fun addListener(onLogAddedListener: OnLogAddedListener): Boolean { + return if (listeners.size >= 5) { + logWarning("Could not add new listener as max limit(5) has reached") + false + } else { + listeners.add(onLogAddedListener) + true + } + } + + /** + * Call this to remove a previously added [OnLogAddedListener] + * @return [Boolean] whether listener was removed. + * + * */ + @JvmStatic + fun removeListener(onLogAddedListener: OnLogAddedListener): Boolean { + return if (!listeners.contains(onLogAddedListener)) { + logWarning("Could not remove listener as it was not found") + false + } else { + listeners.remove(onLogAddedListener) + true + } + } + + /** + * + * @return [Int] Total Count of Logs that are stored in PinLog's database + * May return 0 if PinLog was not initialised. + * + * */ + @JvmStatic + fun getPinLogsCount(): Int { + return when { + !isAppInitialised() -> { + 0 + } + else -> { + applicationLogDataSource!!.getPinLogsCount() + } + } + } + + /** + * Call this to Get Logs + * + * *Generally you should call this method in + * a CoroutineJob or another Thread so it does not block main-ui thread* + * + * @return List of all logs that are stored in PinLog's database + * May return emptyList if PinLog was not initialised. + * + * */ + @JvmStatic + fun getAllPinLogs(): List { + return getAllPinLogs(false, 0) + } + + + /** + * Call this to Get Logs + * + * *Generally you should call this method in + * a CoroutineJob or another Thread so it does not block main-ui thread* + * + * @param maxLogsLimit The max number of logs that should be returned. + * By default all logs are returned. + * @return List of all logs that are stored in PinLog's database + * May return emptyList if PinLog was not initialised. + * + * */ + @JvmStatic + fun getAllPinLogs(maxLogsLimit: Int): List { + return getAllPinLogs(false, maxLogsLimit) + } + + /** + * Call this to Get Logs + * + * *Generally you should call this method in + * a CoroutineJob or another Thread so it does not block main-ui thread* + * + * @param shouldDeleteExistingLogs Whether the logs returned should be deleted from + * the database. *By Default logs are deleted.* + * @param maxLogsLimit The max number of logs that should be returned. + * By default all logs are returned. + * @return List of all logs that are stored in PinLog's database + * May return emptyList if PinLog was not initialised. + * + * */ + @JvmStatic + fun getAllPinLogs( + shouldDeleteExistingLogs: Boolean = true, + maxLogsLimit: Int + ): List { + val logs: MutableList = mutableListOf() + + if (!isAppInitialised()) { + return emptyList() + } + + if (applicationLogDataSource != null && app != null) { + logs.addAll(applicationLogDataSource!!.getAllPinLogs()) + if (shouldDeleteExistingLogs) deleteAllPinLogs() + } + + if (maxLogsLimit >= 1 && logs.size > maxLogsLimit) { + return logs.dropLast(logs.size - maxLogsLimit).toMutableList() + } + + return logs + } + + /** + * Call this to Get Logs as a String List + * + * *Generally you should call this method in + * a CoroutineJob or another Thread so it does not block main-ui thread* + * + * @return List of all logs that are stored in PinLog's database + * May return emptyList if PinLog was not initialised. + * + * */ + @JvmStatic + fun getAllPinLogsAsStringList(): List { + return getAllPinLogsAsStringList(false) + } + + /** + * Call this to Get Logs as a String List + * + * *Generally you should call this method in + * a CoroutineJob or another Thread so it does not block main-ui thread* + * + * @param shouldDeleteExistingLogs Whether the logs returned should be deleted from + * the database. *By Default logs are deleted.* + * @return List of all logs that are stored in PinLog's database + * May return emptyList if PinLog was not initialised. + * + * */ + @JvmStatic + fun getAllPinLogsAsStringList(shouldDeleteExistingLogs: Boolean = true): List { + val logs: MutableList = mutableListOf() + + if (!isAppInitialised()) { + return emptyList() + } + + if (applicationLogDataSource != null) { + logs.addAll(applicationLogDataSource!!.getAllPinLogsAsStringList()) + if (shouldDeleteExistingLogs) deleteAllPinLogs() + } + return logs + } + + /** + * Call this to Get Logs as a single String. + * + * *Generally you should call this method in + * a CoroutineJob or another Thread so it does not block main-ui thread* + * + * @return String containing all logs that are stored in PinLog's database. + * The log element is appended by \n. May return `null` if PinLog was not initialised + * + * + * */ + @JvmStatic + fun getAllPinLogsAsString(): String? { + var logs: String? = null + if (!isAppInitialised()) { + return null + } else { + val list = (applicationLogDataSource!!.getAllPinLogsAsStringList()) + logs = "" + list.forEach { + logs += it + "\n" + } + } + return logs + } + + /** + * Call this to Get all saved Logs in a File. + * + * + * @return A (.txt) File that contains all logs or + * null if [PinLog] was not initialised or if some exception occured + * while creating that file. + * + * */ + fun getAllPinLogsInFileWithName(fileName: String?): File? { + return getAllPinLogsInFile(fileName = fileName, extraEndingLine = null) + } + + /** + * Call this to Get all saved Logs in a File. + * + * + * @param extraEndingLine An extra line (String) that you want to add at the last line of file like + * *userIdentifier or sharedPrefsData or anything.* + * + * Pass null if not interested. + * + * @return A (.txt) File that contains all logs or + * null if [PinLog] was not initialised or if some exception occured + * while creating that file. + * + * */ + fun getAllPinLogsInFile(extraEndingLine: String?): File? { + return getAllPinLogsInFile(fileName = null, extraEndingLine = extraEndingLine) + } + + /** + * Call this to Get all saved Logs in a File. + * + * + * @param fileName a File Name. Default is + * "`{YOUR_APP_PACKAGE_NAME}_LOG_FILE_{DATE}`" + * + * @param extraEndingLine An extra line (String) that you want to add at the last line of file like + * *userIdentifier or sharedPrefsData or anything.* + * + * Pass null if not interested. + * + * @return A (.txt) File that contains all logs or + * null if [PinLog] was not initialised or if some exception occured + * while creating that file. + * + * */ + @JvmStatic + fun getAllPinLogsInFile(fileName: String?, extraEndingLine: String?): File? { + if (!isAppInitialised()) { + return null + } + val logsList = getAllPinLogsAsStringList() + if (logsList.isNotEmpty()) { + val dirPath: String = + getContext().getExternalFilesDir(null)!!.absolutePath + "/ApplicationLogFiles" + try { + //Create a directory if it doesn't already exist. + val filePath = File(dirPath) + if (!filePath.exists()) { + if (!filePath.mkdirs()) { + logW( + CLASS_TAG, + "Error occurred while creating directory for log files." + ) + return null + } + } + + //Create a new file with file name + val logFile: File = if (fileName == null) { + File( + filePath, + "${callerPackageName}_LOG_FILE_${System.currentTimeMillis()}" + ) + } else File(filePath, fileName) + + val writer = FileWriter(logFile, true) + val bufferedWriter = BufferedWriter(writer, 4 * 1024.0.pow(2.0).toInt()) + for (logString in logsList) { + bufferedWriter.write(logString + "\n") + } + + extraEndingLine?.let { + bufferedWriter.write(it + "\n") + } + + writer.flush() + writer.close() + + logI(CLASS_TAG, "The logs are save in a file - ${logFile.absolutePath}") + + return logFile + } catch (e: Exception) { + e.printStackTrace() + } + } + return null + } + + /** + * + * Call this to **delete all** of the logs stored in PinLog's database + * + * */ + @JvmStatic + fun deleteAllPinLogs() { + if (!isAppInitialised()) { + return + } + if (applicationLogDataSource == null) return + else { + logWarning("Delete Logs request was called") + applicationLogDataSource!!.deleteAllPinLogs() + } + } + + /** + * if set to `true` Enables dev logs + * + * Uses built-in Android [Log] util to log to Logcat + * *Defaults to `false` + * + * @param enabled Whether Dev Logs are enabled defaults to `false` + * */ + fun setDevLogging(enabled: Boolean) { + isDevLoggingEnabled = enabled + } + + /** + * Uses built-in room to store logs + * *Defaults to `true` + * + * *Note : Logs are stored for max time-period of seven days of logging. + * After that they are deleted by [PinLog] service.* + * + * @param boolean Whether to store the logs + * + * */ + fun setDoStoreLogs(boolean: Boolean) { + shouldStoreLogs = boolean + } + + /** + * Useful if you want build info in pre-logs + * @param buildConfig The BuildConfig generated by gradle + * */ + @JvmStatic + fun setBuildConfigClass(buildConfig: Class<*>) { + buildConfigClass = buildConfig + } + + /** + * Initialises [PinLog] + * Defaults : + * - [isDevLoggingEnabled] = `false` + * - [shouldStoreLogs] = `true` + * + * *Note : that the properties will be overwritten by new one if [PinLog] was previously initialised.* + * @param application Application. Commonly used inside [Application.onCreate] method of your [Application] class + * @return [Boolean] - If [PinLog] was previously initialised then `false` else `true`. + * + * */ + @JvmStatic + fun initialise(@NonNull application: Application): Boolean { + return initialise( + application = application, + setDevLoggingEnabled = false, + setDoStoreLogs = true, + buildConfig = null + ) + } + + /** + * Initialises [PinLog] + * + * + * *Note : that the properties will be overwritten by new one if [PinLog] was previously initialised.* + * @param application Application. Commonly used inside [Application.onCreate] method of your [Application] class + * @return [Boolean] - If [PinLog] was previously initialised then `false` else `true`. + * + * */ + @JvmStatic + fun initialise(@NonNull application: Application, setDevLoggingEnabled: Boolean): Boolean { + return initialise( + application = application, + setDevLoggingEnabled = setDevLoggingEnabled, + setDoStoreLogs = true, + buildConfig = null + ) + } + + /** + * Initialises [PinLog] + * + * *Note : that the properties will be overwritten by new one if [PinLog] was previously initialised.* + * @param application Application. Commonly used inside [Application.onCreate] method of your [Application] class + * @return [Boolean] - If [PinLog] was previously initialised then `false` else `true`. + * + * */ + @JvmStatic + fun initialise( + @NonNull application: Application, + setDevLoggingEnabled: Boolean, + buildConfig: Class<*>? + ): Boolean { + return initialise( + application = application, + setDevLoggingEnabled = setDevLoggingEnabled, + setDoStoreLogs = true, + buildConfig = buildConfig + ) + } + + /** + * Initialises [PinLog] + * + * *Note : that the properties will be overwritten by new one if [PinLog] was previously initialised.* + * @param application Application. Commonly used inside [Application.onCreate] method of your [Application] class + * @return [Boolean] - If [PinLog] was previously initialised then `false` else `true`. + * + * */ + @JvmStatic + fun initialise( + @NonNull application: Application, + setDevLoggingEnabled: Boolean, + setDoStoreLogs: Boolean, + buildConfig: Class<*>? + ): Boolean { + if (loggingStyle == null) { + loggingStyle = DefaultApplicationLoggingStyle() + } + return instance(application) + } + + + /** + * Uses built-in database to store logs + * *Defaults to [customLoggingStyle] = [DefaultApplicationLoggingStyle] + * + * @param customLoggingStyle To set this property to custom string log format override [LoggingStyle.getFormattedLogData] method + * + * */ + fun setLogFormatting(customLoggingStyle: LoggingStyle?) { + loggingStyle = customLoggingStyle + } + + /** + * If set to `true`, uses built-in PinLog [Thread.UncaughtExceptionHandler] , this + * is created in a way that it starts a Share Intent with logs. + * *Defaults to `false` + * + * If you are setting [enabled] to `true` then also add toEmail + * + * */ + @JvmStatic + fun setPinLogExceptionHandlerEnabled(enabled: Boolean, toEmail: String?) { + if (exceptionHandlerEnabled && !enabled) { + if (Thread.getDefaultUncaughtExceptionHandler() == PinLogUncaughtExceptionHandler()) { + Thread.setDefaultUncaughtExceptionHandler(defaultExceptionHandler) + } + } else { + exceptionHandlerEnabled = enabled + sendToEmail = toEmail + logInfo("Exception handling is setTo PinLog") + setupExceptionHandler() + } + } + + /** + * Initialises [PinLog] in Debug mode that is with all Debug Properties like [isDevLoggingEnabled] to true. + * More suitable **for Debug Builds**. + * + * *Note : that the properties will be overwritten by new one if [PinLog] was previously initialised.* + * @param application Application. Commonly used inside [Application.onCreate] method of your [Application] class. + * @param buildConfig The BuildConfig class generated by Gradle when you build the project. + * @param logingStyle Custom implementation of the [LoggingStyle] class. Default is [DefaultApplicationLoggingStyle] + * @return [Boolean] - If [PinLog] was previously initialised then `false` else `true`. + * + * */ + @JvmStatic + fun initialiseDebug( + @NonNull application: Application, + buildConfig: Class<*>? = null, + logingStyle: LoggingStyle? = null + ): Boolean { + isDevLoggingEnabled = true + shouldStoreLogs = true + buildConfig?.let { + buildConfigClass = it + } + loggingStyle = if (loggingStyle == null) { + DefaultApplicationLoggingStyle() + } else logingStyle + + return instance(application) + } + + /** + * Initialises [PinLog] in Release mode that is with all Non-Debug Properties like [setDevLogging] to `false`. + * This mode is great **for Release Builds** where your app should not + * print any logs with the [Log] util class. + * + * **This means PinLog will not print the Logs to the Logcat, but store them instead + * which you can get by [PinLog.getAllPinLogs] method** + * + * *Note : that the properties will be overwritten by new one if [PinLog] was previously initialised.* + * @param application Application. Commonly used inside [Application.onCreate] method of your [Application] class. + * @param buildConfig The BuildConfig class generated by Gradle when you build the project. + * @param logingStyle Custom implementation of the [LoggingStyle] class. Default is [DefaultApplicationLoggingStyle] + * @return [Boolean] - If [PinLog] was previously initialised then `false` else `true`. + * + * */ + @JvmStatic + fun initialiseRelease( + @NonNull application: Application, + buildConfig: Class<*>? = null, + logingStyle: LoggingStyle? = null + ): Boolean { + isDevLoggingEnabled = false + shouldStoreLogs = true + buildConfig?.let { + buildConfigClass = it + } + loggingStyle = if (loggingStyle == null) { + DefaultApplicationLoggingStyle() + } else logingStyle + return instance(application) + } + + private fun collectBuildConfigData() { + val data = JSONObject() + buildConfigClass?.let { someClass -> + val fields = someClass.fields + for (field in fields) { + try { + val value: Any? = field[null] + + value?.let { + logI("BuildConfig", " ${field.name} -> $it ") + if (field.type.isArray) { + data.put(field.name, JSONArray(listOf(*it as Array<*>))) + } else { + data.put(field.name, it) + } + } + + } catch (e: IllegalArgumentException) { + e.printStackTrace() + } catch (e: IllegalAccessException) { + e.printStackTrace() + } + } + } + if (data.has("DEBUG")) { + val isDebug = data.getBoolean("DEBUG") + if (isDebug) logI(CLASS_TAG, "Application Build is of Type Debug") + } + } + + private class PinLogUncaughtExceptionHandler : Thread.UncaughtExceptionHandler { + + override fun uncaughtException(t: Thread, e: Throwable) { + logWarning("An UncaughtException was caught by PinLog in ${t.name}") + val stackTrace = getStackTraceString(e) + logE("PinLogExceptionInfo", stackTrace, e) + try { + val i = Intent(Intent.ACTION_SEND) + i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) + sendToEmail?.let { + i.putExtra(Intent.EXTRA_EMAIL, it) + } + val text = + "\n$callerAppName crashed due to an unknown error on ${Date()}" + + "\n\n" + + "$callerAppName crashed unexpectedly" + + "\n\n" + + "**Crash Information**\n" + + "------------------------beginning of crash\n" + + stackTrace + + "\n------------------------end of crash\n" + i.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) + i.putExtra(Intent.EXTRA_TEXT, text) + i.type = "text/plain" + getContext().applicationContext.startActivity(i) + exitProcess(0) + + } catch (e: Exception) { + e.printStackTrace() + defaultExceptionHandler?.uncaughtException(t, e) + } + } + } + + /** + * + * **Warning : Do not use this value as your TAG property as logs + * having this [CLASS_TAG] are ignored.** + * + * */ + internal const val CLASS_TAG = "PinLog" + +} + diff --git a/app/src/main/java/com/adityaamolbavadekar/pinlog/database/ApplicationLogDataSource.kt b/app/src/main/java/com/adityaamolbavadekar/pinlog/database/ApplicationLogDataSource.kt new file mode 100644 index 0000000..0bc6065 --- /dev/null +++ b/app/src/main/java/com/adityaamolbavadekar/pinlog/database/ApplicationLogDataSource.kt @@ -0,0 +1,28 @@ +/******************************************************************************* + * Copyright (c) 2022 Aditya Bavadekar + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ******************************************************************************/ + +package com.adityaamolbavadekar.pinlog.database + +internal interface ApplicationLogDataSource { + fun getPinLogsCount(): Int + fun getPinLogsGroupCount(): Int + fun insertPinLog(applicationLog: ApplicationLogModel) : Boolean + fun deleteAllPinLogs() + fun getPinLogs(group: Int): List? + fun getAllPinLogs(): List + fun getAllPinLogsAsStringList() : List + fun deletedExpiredPinLogs(expiryTimeInSeconds: Int) +} \ No newline at end of file diff --git a/app/src/main/java/com/adityaamolbavadekar/pinlog/database/ApplicationLogDatabaseHelper.kt b/app/src/main/java/com/adityaamolbavadekar/pinlog/database/ApplicationLogDatabaseHelper.kt new file mode 100644 index 0000000..25d0d25 --- /dev/null +++ b/app/src/main/java/com/adityaamolbavadekar/pinlog/database/ApplicationLogDatabaseHelper.kt @@ -0,0 +1,219 @@ +/******************************************************************************* + * Copyright (c) 2022 Aditya Bavadekar + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ******************************************************************************/ + +package com.adityaamolbavadekar.pinlog.database + +import android.content.ContentValues +import android.content.Context +import android.database.Cursor +import android.database.DatabaseUtils +import android.database.sqlite.SQLiteDatabase +import android.database.sqlite.SQLiteOpenHelper +import com.adityaamolbavadekar.pinlog.ApplicationLogsContract +import com.adityaamolbavadekar.pinlog.ApplicationLogsContract.ApplicationLogEntry.COLUMN_NAME_ID +import com.adityaamolbavadekar.pinlog.ApplicationLogsContract.ApplicationLogEntry.COLUMN_NAME_LOG +import com.adityaamolbavadekar.pinlog.ApplicationLogsContract.ApplicationLogEntry.COLUMN_NAME_LOG_LEVEL +import com.adityaamolbavadekar.pinlog.ApplicationLogsContract.ApplicationLogEntry.DATABASE_VERSION +import com.adityaamolbavadekar.pinlog.ApplicationLogsContract.ApplicationLogEntry.TABLE_NAME +import com.adityaamolbavadekar.pinlog.PinLog +import com.adityaamolbavadekar.pinlog.PinLog.CLASS_TAG + +internal class ApplicationLogDatabaseHelper(c: Context) : SQLiteOpenHelper( + c, + TABLE_NAME, + null, + DATABASE_VERSION +), ApplicationLogDataSource { + + private var database: SQLiteDatabase? = null + + private fun initializePinLogsDatabase() { + if (database == null) { + database = this.writableDatabase + } + } + + /*START [SQLiteOpenHelper]*/ + override fun onCreate(db: SQLiteDatabase?) { + ApplicationLogsContract.onCreate(db) + } + + override fun onUpgrade(db: SQLiteDatabase?, oldVersion: Int, newVersion: Int) { + ApplicationLogsContract.onUpgrade(db, oldVersion, newVersion) + } + + /*END [SQLiteOpenHelper]*/ + + override fun getPinLogsCount(): Int { + // Initialize SQLiteDatabase if it is null + initializePinLogsDatabase() + var count = 0 + try { + if (database != null) { + count = DatabaseUtils.queryNumEntries(database, TABLE_NAME).toInt() + } + } catch (e: Exception) { + e.printStackTrace() + PinLog.logError( + "PinLogTable: Exception occurred in Database while executing getCount: $e", e + ) + } + return count + } + + override fun getPinLogsGroupCount(): Int { + val c = getPinLogsCount() + return if (c <= 0) { + 0 + } else { + (c / 5000) + } + } + + override fun insertPinLog(applicationLog: ApplicationLogModel): Boolean { + val errorCode: Long = -1 + var rowId: Long = -1 + database?.let { db -> + val contentValues = ContentValues() + contentValues.put(COLUMN_NAME_LOG, applicationLog.LOG) + contentValues.put(COLUMN_NAME_LOG_LEVEL, applicationLog.LOG_LEVEL) + + try { + rowId = db.insert(TABLE_NAME, null, contentValues) + } catch (e: java.lang.Exception) { + e.printStackTrace() + PinLog.logError( + "PinLogTable: Exception occurred in Database while executing insertPinLog: $e", + e + ) + } + } + return (rowId != errorCode) + } + + override fun deleteAllPinLogs() { + database?.let { db -> + + try { + db.delete(TABLE_NAME, null, null) + } catch (e: java.lang.Exception) { + e.printStackTrace() + PinLog.logError( + "PinLogTable: Exception occurred in Database while executing deleteAllPinLogs: $e", + e + ) + } + + } + } + + override fun getPinLogs(group: Int): List { + return emptyList()//Not implemented + } + + private fun getCursor(db: SQLiteDatabase): Cursor? { + return db.query( + TABLE_NAME, + arrayOf( + COLUMN_NAME_ID,//0 + COLUMN_NAME_LOG,//1 + COLUMN_NAME_LOG_LEVEL//2 + ), + null, + null, + null, + null, + null, + null + ) + } + + override fun getAllPinLogs(): List { + val applicationLogsList: MutableList = mutableListOf() + database?.let { db -> + val c = getCursor(db) + + if (c == null || c.isClosed) return emptyList() + else try { + if (c.moveToFirst()) { + do { + if (c.isClosed) { + break + } + val id: Int = c.getInt(0) + val log: String = c.getString(1) + val level: Int = c.getInt(2) + val pinLog = ApplicationLogModel(id, log, level) + applicationLogsList.add(pinLog) + + } while (c.moveToNext()) + } + } catch (e: Exception) { + e.printStackTrace() + PinLog.logE( + CLASS_TAG, + "PinLogTable: Exception occurred in Database while executing getPinLogs: $e" + ) + } + c.close() + return applicationLogsList.toList() + + } + return applicationLogsList.toList() + } + + override fun getAllPinLogsAsStringList(): List { + val pinLogsList: MutableList = mutableListOf() + database?.let { db -> + val c = getCursor(db) + + if (c == null || c.isClosed) return emptyList() + else try { + if (c.moveToFirst()) { + do { + if (c.isClosed) { + break + } + val log: String = c.getString(1) + pinLogsList.add(log) + } while (c.moveToNext()) + } + } catch (e: Exception) { + e.printStackTrace() + PinLog.logError( + "PinLogTable: Exception occurred in Database while executing getAllPinLogsAsStringList: $e", + e + ) + } + c.close() + return pinLogsList.toList() + + } + return pinLogsList.toList() + } + + override fun deletedExpiredPinLogs(expiryTimeInSeconds: Int) { + if (database == null) { + return + } + //Not yet implemented + } + + init { + initializePinLogsDatabase() + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/adityaamolbavadekar/pinlog/database/ApplicationLogModel.kt b/app/src/main/java/com/adityaamolbavadekar/pinlog/database/ApplicationLogModel.kt new file mode 100644 index 0000000..f91a503 --- /dev/null +++ b/app/src/main/java/com/adityaamolbavadekar/pinlog/database/ApplicationLogModel.kt @@ -0,0 +1,19 @@ +/******************************************************************************* + * Copyright (c) 2022 Aditya Bavadekar + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ******************************************************************************/ + +package com.adityaamolbavadekar.pinlog.database + +data class ApplicationLogModel(val id: Int, var LOG: String, var LOG_LEVEL: Int) \ No newline at end of file diff --git a/app/src/main/java/com/adityaamolbavadekar/pinlog/extensions/Extensions.kt b/app/src/main/java/com/adityaamolbavadekar/pinlog/extensions/Extensions.kt new file mode 100644 index 0000000..cc47ad0 --- /dev/null +++ b/app/src/main/java/com/adityaamolbavadekar/pinlog/extensions/Extensions.kt @@ -0,0 +1,65 @@ +/******************************************************************************* + * Copyright (c) 2022 Aditya Bavadekar + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ******************************************************************************/ + +package com.adityaamolbavadekar.pinlog.extensions + +import android.app.Application +import com.adityaamolbavadekar.pinlog.PinLog +import com.adityaamolbavadekar.pinlog.PinLog.CLASS_TAG + +fun Application.initPinLogger(): Boolean { + return PinLog.initialise(this) +} + +fun Application.initPinLoggerInDebugMode(): Boolean { + return PinLog.initialiseDebug(this) +} + +fun Application.initPinLoggerInReleaseMode(): Boolean { + return PinLog.initialiseRelease(this) +} + +inline fun debug(m: () -> String) { + PinLog.logD("?", m.invoke()) +} + +inline fun warn(m: () -> String) { + PinLog.logW("?", m.invoke()) +} + +inline fun info(m: () -> String) { + PinLog.logI("?", m.invoke()) +} + +inline fun error(m: () -> String) { + PinLog.logE("?", m.invoke()) +} + +inline fun error(e: Exception, m: () -> String) { + PinLog.logE("?", m.invoke(), e) +} + +fun MutableList.submitLog(log: String) { + for (it in this) { + try { + it.onLogAdded(log) + } catch (e: Exception) { + PinLog.logW(CLASS_TAG, "Could not notify $it about newly added log") + } + } + +} + diff --git a/app/src/main/java/com/adityaamolbavadekar/pinlog/extensions/PinLoggingExtensions.kt b/app/src/main/java/com/adityaamolbavadekar/pinlog/extensions/PinLoggingExtensions.kt new file mode 100644 index 0000000..cc6a64f --- /dev/null +++ b/app/src/main/java/com/adityaamolbavadekar/pinlog/extensions/PinLoggingExtensions.kt @@ -0,0 +1,147 @@ +/******************************************************************************* + * Copyright (c) 2022 Aditya Bavadekar + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ******************************************************************************/ + +package com.adityaamolbavadekar.pinlog.extensions + +import android.content.Context +import com.adityaamolbavadekar.pinlog.PinLog + +/*ERROR LOGGING*/ + +/** + * **Logs an Error Log** + * + * **Logs to Logcat if [PinLog.setDevLogging] is set to `true`** + * + * **Stores the log if [PinLog.setDoStoreLogs] is set to `true`** + * + * @see [PinLog.logE] + * + * */ +fun Context.logE(s: String?) { + PinLog.logE(getName(), s ?: "") +} + +/** + * **Logs an Error Log** + * + * **Logs to Logcat if [PinLog.setDevLogging] is set to `true`** + * + * **Stores the log if [PinLog.setDoStoreLogs] is set to `true`** + * + * @see [PinLog.logE] + * + * */ +fun Context.logE(s: String?, throwable: Throwable?) { + PinLog.logE(getName(), s, throwable ?: Exception("")) +} + +/*INFO LOGGING*/ + +/** + * **Logs an Info Log** + * + * **Logs to Logcat if [PinLog.setDevLogging] is set to `true`** + * + * **Stores the log if [PinLog.setDoStoreLogs] is set to `true`** + * + * @see [PinLog.logI] + * + * */ +fun Context.logI(s: String?) { + PinLog.logI(getName(), s ?: "") +} + +/** + * **Logs an Info Log** + * + * **Logs to Logcat if [PinLog.setDevLogging] is set to `true`** + * + * **Stores the log if [PinLog.setDoStoreLogs] is set to `true`** + * + * @see [PinLog.logI] + * + * */ +fun Context.logI(s: String?, throwable: Throwable?) { + PinLog.logI(getName(), s, throwable ?: Exception("")) +} + +/*WARNING LOGGING*/ + +/** + * **Logs an Warn Log** + * + * **Logs to Logcat if [PinLog.setDevLogging] is set to `true`** + * + * **Stores the log if [PinLog.setDoStoreLogs] is set to `true`** + * + * @see [PinLog.logW] + * + * */ +fun Context.logW(s: String?) { + PinLog.logW(getName(), s ?: "") +} + +/** + * **Logs an Warn Log** + * + * **Logs to Logcat if [PinLog.setDevLogging] is set to `true`** + * + * **Stores the log if [PinLog.setDoStoreLogs] is set to `true`** + * + * @see [PinLog.logW] + * + * */ +fun Context.logW(s: String?, throwable: Throwable?) { + PinLog.logW(getName(), s, throwable ?: Exception("")) +} + +/*DEBUG LOGGING*/ + +/** + * **Logs an Debug Log** + * + * **Logs to Logcat if [PinLog.setDevLogging] is set to `true`** + * + * **Stores the log if [PinLog.setDoStoreLogs] is set to `true`** + * + * @see [PinLog.logD] + * + * */ +fun Context.logD(s: String?) { + PinLog.logD(getName(), s ?: "") +} + +/** + * **Logs an Debug Log** + * + * **Logs to Logcat if [PinLog.setDevLogging] is set to `true`** + * + * **Stores the log if [PinLog.setDoStoreLogs] is set to `true`** + * + * @see [PinLog.logD] + * + * */ +fun Context.logD(s: String?, throwable: Throwable?) { + PinLog.logD(getName(), s, throwable ?: Exception("")) +} + + +/*Others*/ + +private fun Context.getName(): String { + return this.javaClass.simpleName +} \ No newline at end of file diff --git a/build.gradle b/build.gradle new file mode 100644 index 0000000..28c73bc --- /dev/null +++ b/build.gradle @@ -0,0 +1,26 @@ +// Top-level build file where you can add configuration options common to all sub-projects/modules. +buildscript { + ext.kotlin_version = "1.4.21" + repositories { + google() + mavenCentral() + } + dependencies { + classpath "com.android.tools.build:gradle:4.0.0-alpha09" + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" + + // NOTE: Do not place your application dependencies here; they belong + // in the individual module build.gradle files + } +} + +allprojects { + repositories { + google() + mavenCentral() + } +} + +task clean(type: Delete) { + delete rootProject.buildDir +} \ No newline at end of file diff --git a/gradle.properties b/gradle.properties new file mode 100644 index 0000000..e6a071d --- /dev/null +++ b/gradle.properties @@ -0,0 +1,21 @@ +# Project-wide Gradle settings. +# IDE (e.g. Android Studio) users: +# Gradle settings configured through the IDE *will override* +# any settings specified in this file. +# For more details on how to configure your build environment visit +# http://www.gradle.org/docs/current/userguide/build_environment.html +# Specifies the JVM arguments used for the daemon process. +# The setting is particularly useful for tweaking memory settings. +org.gradle.jvmargs=-Xmx1024m +# When configured, Gradle will run in incubating parallel mode. +# This option should only be used with decoupled projects. More details, visit +# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects +# org.gradle.parallel=true +# AndroidX package structure to make it clearer which packages are bundled with the +# Android operating system, and which are packaged with your app"s APK +# https://developer.android.com/topic/libraries/support-library/androidx-rn +android.useAndroidX=true +# Automatically convert third-party libraries to use AndroidX +android.enableJetifier=true +# Kotlin code style for this project: "official" or "obsolete": +kotlin.code.style=official \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000..f6b961f Binary files /dev/null and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..7b55fd6 --- /dev/null +++ b/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +#Thu May 26 12:29:15 IST 2022 +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-6.1.1-all.zip diff --git a/gradlew b/gradlew new file mode 100644 index 0000000..cccdd3d --- /dev/null +++ b/gradlew @@ -0,0 +1,172 @@ +#!/usr/bin/env sh + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS="" + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn () { + echo "$*" +} + +die () { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; + NONSTOP* ) + nonstop=true + ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin, switch paths to Windows format before running java +if $cygwin ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + JAVACMD=`cygpath --unix "$JAVACMD"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=$((i+1)) + done + case $i in + (0) set -- ;; + (1) set -- "$args0" ;; + (2) set -- "$args0" "$args1" ;; + (3) set -- "$args0" "$args1" "$args2" ;; + (4) set -- "$args0" "$args1" "$args2" "$args3" ;; + (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Escape application args +save () { + for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done + echo " " +} +APP_ARGS=$(save "$@") + +# Collect all arguments for the java command, following the shell quoting and substitution rules +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" + +# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong +if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then + cd "$(dirname "$0")" +fi + +exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat new file mode 100644 index 0000000..f955316 --- /dev/null +++ b/gradlew.bat @@ -0,0 +1,84 @@ +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS= + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto init + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto init + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:init +@rem Get command-line arguments, handling Windows variants + +if not "%OS%" == "Windows_NT" goto win9xME_args + +:win9xME_args +@rem Slurp the command line arguments. +set CMD_LINE_ARGS= +set _SKIP=2 + +:win9xME_args_slurp +if "x%~1" == "x" goto execute + +set CMD_LINE_ARGS=%* + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/settings.gradle b/settings.gradle new file mode 100644 index 0000000..823f94d --- /dev/null +++ b/settings.gradle @@ -0,0 +1,2 @@ +include ':app' +rootProject.name = "PinLog Library" \ No newline at end of file