added platforms to gitignore
This commit is contained in:
parent
ced01b5152
commit
9bd0d63162
5486 changed files with 4 additions and 292943 deletions
|
@ -20,17 +20,12 @@ android {
|
|||
applicationId 'com.vishnuraghav.enrecipes'
|
||||
minSdkVersion 21
|
||||
generatedDensities = []
|
||||
// ndk {
|
||||
// abiFilters.clear()
|
||||
// abiFilters.addAll(['x86','armeabi-v7a','arm64-v8a'])
|
||||
// }
|
||||
ndk {
|
||||
abiFilters.clear()
|
||||
abiFilters.addAll(['arm64-v8a', 'armeabi-v7a', 'x86'])
|
||||
}
|
||||
}
|
||||
aaptOptions {
|
||||
additionalParameters "--no-version-vectors"
|
||||
}
|
||||
// bundle {
|
||||
// language {
|
||||
// enableSplit = false
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -1,2 +0,0 @@
|
|||
#Wed Dec 30 13:12:10 IST 2020
|
||||
gradle.version=6.4
|
Binary file not shown.
Binary file not shown.
|
@ -1,82 +0,0 @@
|
|||
{
|
||||
"@nativescript-community/ui-material-button": {
|
||||
"/mnt/82e5ff15-70b8-44a5-bb66-55688fc2381f/Freelance/Projects/WIP/EnRecipes/Developement/EnRecipes-App/node_modules/@nativescript-community/ui-material-button/platforms/android/include.gradle": "aa9556ead0428a00cf38da660884c16334afaf04",
|
||||
"/mnt/82e5ff15-70b8-44a5-bb66-55688fc2381f/Freelance/Projects/WIP/EnRecipes/Developement/EnRecipes-App/node_modules/@nativescript-community/ui-material-button/platforms/android/res/layout/material_button.xml": "3af1596c168b216b5554cce0a5785ecedda84fc1",
|
||||
"/mnt/82e5ff15-70b8-44a5-bb66-55688fc2381f/Freelance/Projects/WIP/EnRecipes/Developement/EnRecipes-App/node_modules/@nativescript-community/ui-material-button/platforms/android/res/layout/material_button_flat.xml": "416b334bc8a4c74dd2f85996c1fc29a7f3f7423d",
|
||||
"/mnt/82e5ff15-70b8-44a5-bb66-55688fc2381f/Freelance/Projects/WIP/EnRecipes/Developement/EnRecipes-App/node_modules/@nativescript-community/ui-material-button/platforms/android/res/layout/material_button_flat_icon.xml": "f20b2f5879c767a84e64609c8a98a217c4d35faf",
|
||||
"/mnt/82e5ff15-70b8-44a5-bb66-55688fc2381f/Freelance/Projects/WIP/EnRecipes/Developement/EnRecipes-App/node_modules/@nativescript-community/ui-material-button/platforms/android/res/layout/material_button_icon.xml": "459d986fcaa1c27d789fb89fac5b90be23f7940d",
|
||||
"/mnt/82e5ff15-70b8-44a5-bb66-55688fc2381f/Freelance/Projects/WIP/EnRecipes/Developement/EnRecipes-App/node_modules/@nativescript-community/ui-material-button/platforms/android/res/layout/material_button_outline.xml": "e2b838c88048616006216e1d89ce324a0c14a7d7",
|
||||
"/mnt/82e5ff15-70b8-44a5-bb66-55688fc2381f/Freelance/Projects/WIP/EnRecipes/Developement/EnRecipes-App/node_modules/@nativescript-community/ui-material-button/platforms/android/res/layout/material_button_outline_icon.xml": "a4699f1a2a65d184638233714b95d45b0c26fb07",
|
||||
"/mnt/82e5ff15-70b8-44a5-bb66-55688fc2381f/Freelance/Projects/WIP/EnRecipes/Developement/EnRecipes-App/node_modules/@nativescript-community/ui-material-button/platforms/android/res/layout/material_button_text.xml": "11f39271e457bdbe466454e6d2b321aa590c58df",
|
||||
"/mnt/82e5ff15-70b8-44a5-bb66-55688fc2381f/Freelance/Projects/WIP/EnRecipes/Developement/EnRecipes-App/node_modules/@nativescript-community/ui-material-button/platforms/android/res/layout/material_button_text_icon.xml": "221d0ee71b569721b2f9ebae01beecad8c4b3802",
|
||||
"/mnt/82e5ff15-70b8-44a5-bb66-55688fc2381f/Freelance/Projects/WIP/EnRecipes/Developement/EnRecipes-App/node_modules/@nativescript-community/ui-material-button/platforms/android/res/values/materialbuttonstyles.xml": "8a1f523878b2ad3ec92c3df5c69ed86e6bee5a24",
|
||||
"/mnt/82e5ff15-70b8-44a5-bb66-55688fc2381f/Freelance/Projects/WIP/EnRecipes/Developement/EnRecipes-App/node_modules/@nativescript-community/ui-material-button/platforms/android/ui_material_button.aar": "8f1cf1ee77462d7806909cfddac8412547ec5433"
|
||||
},
|
||||
"@nativescript/core": {
|
||||
"/mnt/82e5ff15-70b8-44a5-bb66-55688fc2381f/Freelance/Projects/WIP/EnRecipes/Developement/EnRecipes-App/node_modules/@nativescript/core/platforms/android/native-api-usage.json": "34ee0e9771ad7dd9d388c191734d71507a04565a",
|
||||
"/mnt/82e5ff15-70b8-44a5-bb66-55688fc2381f/Freelance/Projects/WIP/EnRecipes/Developement/EnRecipes-App/node_modules/@nativescript/core/platforms/android/widgets-release.aar": "ea387e7a96a4ccb0df5c645b313c5dcf8042a0bb"
|
||||
},
|
||||
"@nativescript/social-share": {
|
||||
"/mnt/82e5ff15-70b8-44a5-bb66-55688fc2381f/Freelance/Projects/WIP/EnRecipes/Developement/EnRecipes-App/node_modules/@nativescript/social-share/platforms/android/AndroidManifest.xml": "ca6e2ceca22448641563fa6f6c773ef10b292e10",
|
||||
"/mnt/82e5ff15-70b8-44a5-bb66-55688fc2381f/Freelance/Projects/WIP/EnRecipes/Developement/EnRecipes-App/node_modules/@nativescript/social-share/platforms/android/res/xml/provider_paths.xml": "dbe562dfa44616ea40d88a3cbd74b3e8f44c54f3"
|
||||
},
|
||||
"@nativescript/zip": {
|
||||
"/mnt/82e5ff15-70b8-44a5-bb66-55688fc2381f/Freelance/Projects/WIP/EnRecipes/Developement/EnRecipes-App/node_modules/@nativescript/zip/platforms/android/include.gradle": "81c5f85b571d2665da42be015e2d1dfd271ee4e6"
|
||||
},
|
||||
"nativescript-couchbase-plugin": {
|
||||
"/mnt/82e5ff15-70b8-44a5-bb66-55688fc2381f/Freelance/Projects/WIP/EnRecipes/Developement/EnRecipes-App/node_modules/nativescript-couchbase-plugin/platforms/android/fancycouchbase-release.aar": "498e1ee2405bdf6a35cd5f0402b2b279205f53d9",
|
||||
"/mnt/82e5ff15-70b8-44a5-bb66-55688fc2381f/Freelance/Projects/WIP/EnRecipes/Developement/EnRecipes-App/node_modules/nativescript-couchbase-plugin/platforms/android/include.gradle": "697825ccd0e49caa429d63214894357437919716"
|
||||
},
|
||||
"nativescript-feedback": {
|
||||
"/mnt/82e5ff15-70b8-44a5-bb66-55688fc2381f/Freelance/Projects/WIP/EnRecipes/Developement/EnRecipes-App/node_modules/nativescript-feedback/platforms/android/include.gradle": "162f973f93b517e2be13dfabf0a4c6d6d0020aac",
|
||||
"/mnt/82e5ff15-70b8-44a5-bb66-55688fc2381f/Freelance/Projects/WIP/EnRecipes/Developement/EnRecipes-App/node_modules/nativescript-feedback/platforms/android/nativescript_feedback.aar": "6bb6b05373f74debd4f7fb0f47794a240794cc40"
|
||||
},
|
||||
"nativescript-imagecropper": {
|
||||
"/mnt/82e5ff15-70b8-44a5-bb66-55688fc2381f/Freelance/Projects/WIP/EnRecipes/Developement/EnRecipes-App/node_modules/nativescript-imagecropper/platforms/android/AndroidManifest.xml": "de9e14f14dae0473e1f887952a39df5e32951275",
|
||||
"/mnt/82e5ff15-70b8-44a5-bb66-55688fc2381f/Freelance/Projects/WIP/EnRecipes/Developement/EnRecipes-App/node_modules/nativescript-imagecropper/platforms/android/include.gradle": "61ad278ee80be73a906afb5eddd2126c585ba549",
|
||||
"/mnt/82e5ff15-70b8-44a5-bb66-55688fc2381f/Freelance/Projects/WIP/EnRecipes/Developement/EnRecipes-App/node_modules/nativescript-imagecropper/platforms/android/nativescript_imagecropper.aar": "1cbfff3087414762c1785e5bbfccd593bc3331e2"
|
||||
},
|
||||
"nativescript-plugin-filepicker": {
|
||||
"/mnt/82e5ff15-70b8-44a5-bb66-55688fc2381f/Freelance/Projects/WIP/EnRecipes/Developement/EnRecipes-App/node_modules/nativescript-plugin-filepicker/platforms/android/README.md": "45f2f63427be0a461a7e2dfd5ff0ad5bab64068f",
|
||||
"/mnt/82e5ff15-70b8-44a5-bb66-55688fc2381f/Freelance/Projects/WIP/EnRecipes/Developement/EnRecipes-App/node_modules/nativescript-plugin-filepicker/platforms/android/nativescript_plugin_filepicker.aar": "e53441dd997d4849f45247b4b4eda20fac860b5d",
|
||||
"/mnt/82e5ff15-70b8-44a5-bb66-55688fc2381f/Freelance/Projects/WIP/EnRecipes/Developement/EnRecipes-App/node_modules/nativescript-plugin-filepicker/platforms/android/include.gradle": "06e7e1be49eb6d614094596003280aa7eea789f3"
|
||||
},
|
||||
"nativescript-ui-calendar": {
|
||||
"/mnt/82e5ff15-70b8-44a5-bb66-55688fc2381f/Freelance/Projects/WIP/EnRecipes/Developement/EnRecipes-App/node_modules/nativescript-ui-calendar/platforms/android/typings/calendar.d.ts": "6e196553478b7d7fbe92645b60d0ac7bfa5339db",
|
||||
"/mnt/82e5ff15-70b8-44a5-bb66-55688fc2381f/Freelance/Projects/WIP/EnRecipes/Developement/EnRecipes-App/node_modules/nativescript-ui-calendar/platforms/android/TNSCalendar-release.aar": "7c0dd1f9120ddeae8c09c2b03423889c1250f541"
|
||||
},
|
||||
"nativescript-ui-listview": {
|
||||
"/mnt/82e5ff15-70b8-44a5-bb66-55688fc2381f/Freelance/Projects/WIP/EnRecipes/Developement/EnRecipes-App/node_modules/nativescript-ui-listview/platforms/android/include.gradle": "47ffe675113a982585b2386750b2d4305b66b6ca",
|
||||
"/mnt/82e5ff15-70b8-44a5-bb66-55688fc2381f/Freelance/Projects/WIP/EnRecipes/Developement/EnRecipes-App/node_modules/nativescript-ui-listview/platforms/android/typings/listview.d.ts": "d3b76f21820076bdd93468055d6da69e9b41d98e",
|
||||
"/mnt/82e5ff15-70b8-44a5-bb66-55688fc2381f/Freelance/Projects/WIP/EnRecipes/Developement/EnRecipes-App/node_modules/nativescript-ui-listview/platforms/android/TNSListView-release.aar": "e9677c69bc3e7fa63aee988efe824a992c1fc0ab"
|
||||
},
|
||||
"nativescript-ui-sidedrawer": {
|
||||
"/mnt/82e5ff15-70b8-44a5-bb66-55688fc2381f/Freelance/Projects/WIP/EnRecipes/Developement/EnRecipes-App/node_modules/nativescript-ui-sidedrawer/platforms/android/TNSSideDrawer-release.aar": "ae03340e741bdf430de4849d4dd117bf07197851",
|
||||
"/mnt/82e5ff15-70b8-44a5-bb66-55688fc2381f/Freelance/Projects/WIP/EnRecipes/Developement/EnRecipes-App/node_modules/nativescript-ui-sidedrawer/platforms/android/typings/sidedrawer.d.ts": "8256a2295125597755fe6a6faee866b0750e65f2"
|
||||
},
|
||||
"nativescript-vibrate": {
|
||||
"/mnt/82e5ff15-70b8-44a5-bb66-55688fc2381f/Freelance/Projects/WIP/EnRecipes/Developement/EnRecipes-App/node_modules/nativescript-vibrate/platforms/android/README.md": "45f2f63427be0a461a7e2dfd5ff0ad5bab64068f",
|
||||
"/mnt/82e5ff15-70b8-44a5-bb66-55688fc2381f/Freelance/Projects/WIP/EnRecipes/Developement/EnRecipes-App/node_modules/nativescript-vibrate/platforms/android/include.gradle": "06e7e1be49eb6d614094596003280aa7eea789f3",
|
||||
"/mnt/82e5ff15-70b8-44a5-bb66-55688fc2381f/Freelance/Projects/WIP/EnRecipes/Developement/EnRecipes-App/node_modules/nativescript-vibrate/platforms/android/nativescript_vibrate.aar": "a60d480c8553e72412f49655a113759fe3321ccb"
|
||||
},
|
||||
"@nativescript-community/ui-material-core": {
|
||||
"/mnt/82e5ff15-70b8-44a5-bb66-55688fc2381f/Freelance/Projects/WIP/EnRecipes/Developement/EnRecipes-App/node_modules/@nativescript-community/ui-material-core/platforms/android/AndroidManifest.xml": "9b6f49aa9c71e8a45ec570027e025274ffcadc96",
|
||||
"/mnt/82e5ff15-70b8-44a5-bb66-55688fc2381f/Freelance/Projects/WIP/EnRecipes/Developement/EnRecipes-App/node_modules/@nativescript-community/ui-material-core/platforms/android/include.gradle": "ec90dc697edab3c184ec95b0a42c9f206900f048",
|
||||
"/mnt/82e5ff15-70b8-44a5-bb66-55688fc2381f/Freelance/Projects/WIP/EnRecipes/Developement/EnRecipes-App/node_modules/@nativescript-community/ui-material-core/platforms/android/java/com/nativescript/material/core/Utils.java": "3813226945705be3d52cf19dc945ea9bf2c77e12",
|
||||
"/mnt/82e5ff15-70b8-44a5-bb66-55688fc2381f/Freelance/Projects/WIP/EnRecipes/Developement/EnRecipes-App/node_modules/@nativescript-community/ui-material-core/platforms/android/res/layout/material_page.xml": "29f912255e893697d6abd438f67a854149136292",
|
||||
"/mnt/82e5ff15-70b8-44a5-bb66-55688fc2381f/Freelance/Projects/WIP/EnRecipes/Developement/EnRecipes-App/node_modules/@nativescript-community/ui-material-core/platforms/android/ui_material_core.aar": "16ba6c7b1ca76423844a6e8a94bdbeed395c1432"
|
||||
},
|
||||
"@nativescript-community/text": {
|
||||
"/mnt/82e5ff15-70b8-44a5-bb66-55688fc2381f/Freelance/Projects/WIP/EnRecipes/Developement/EnRecipes-App/node_modules/@nativescript-community/text/platforms/android/AndroidManifest.xml": "b80912f909195692d3bd071566274feba0b37660",
|
||||
"/mnt/82e5ff15-70b8-44a5-bb66-55688fc2381f/Freelance/Projects/WIP/EnRecipes/Developement/EnRecipes-App/node_modules/@nativescript-community/text/platforms/android/java/com/nativescript/text/CustomTypefaceSpan.java": "421f984280aa806183057f2ddd53febdbc32d2b1",
|
||||
"/mnt/82e5ff15-70b8-44a5-bb66-55688fc2381f/Freelance/Projects/WIP/EnRecipes/Developement/EnRecipes-App/node_modules/@nativescript-community/text/platforms/android/java/com/nativescript/text/CustomBackgroundSpan.java": "ae9e0e6d699e669f96ed7042f2145a1ab0bf6727",
|
||||
"/mnt/82e5ff15-70b8-44a5-bb66-55688fc2381f/Freelance/Projects/WIP/EnRecipes/Developement/EnRecipes-App/node_modules/@nativescript-community/text/platforms/android/java/com/nativescript/text/Font.java": "fb4d246ad367d178e11ef08c34ec182418d30890",
|
||||
"/mnt/82e5ff15-70b8-44a5-bb66-55688fc2381f/Freelance/Projects/WIP/EnRecipes/Developement/EnRecipes-App/node_modules/@nativescript-community/text/platforms/android/java/com/nativescript/text/HtmlToSpannedConverter.java": "38643430e4b8cac690b89a07adc3a86f7060f93d",
|
||||
"/mnt/82e5ff15-70b8-44a5-bb66-55688fc2381f/Freelance/Projects/WIP/EnRecipes/Developement/EnRecipes-App/node_modules/@nativescript-community/text/platforms/android/java/com/nativescript/text/URLSpanNoUnderline.java": "b3da85c1d16eff30c54fb5725e1e46fea265d7ce",
|
||||
"/mnt/82e5ff15-70b8-44a5-bb66-55688fc2381f/Freelance/Projects/WIP/EnRecipes/Developement/EnRecipes-App/node_modules/@nativescript-community/text/platforms/android/text.aar": "77d471b2127e5f1f2b78b89656e7287bcf488b22"
|
||||
},
|
||||
"nativescript-ui-core": {
|
||||
"/mnt/82e5ff15-70b8-44a5-bb66-55688fc2381f/Freelance/Projects/WIP/EnRecipes/Developement/EnRecipes-App/node_modules/nativescript-ui-core/platforms/android/typings/core.d.ts": "5c032652edfa1719ef8c8190fc0f66d3b1c313c2",
|
||||
"/mnt/82e5ff15-70b8-44a5-bb66-55688fc2381f/Freelance/Projects/WIP/EnRecipes/Developement/EnRecipes-App/node_modules/nativescript-ui-core/platforms/android/TNSCore-release.aar": "0e2fe0b867821ad56b8f0b9c8ac4c90f4c399e3c"
|
||||
}
|
||||
}
|
|
@ -1,7 +0,0 @@
|
|||
{
|
||||
"time": "Wed Dec 30 2020 14:50:17 GMT+0530 (India Standard Time)",
|
||||
"nativePlatformStatus": "3",
|
||||
"changesRequireBuild": true,
|
||||
"projectFileHash": "de8936b04a8811e04d93e9717fc4448bf88e2e84157623dd6556d8a57dadf3a1",
|
||||
"changesRequireBuildTime": "Wed Dec 30 2020 14:50:17 GMT+0530 (India Standard Time)"
|
||||
}
|
|
@ -1,983 +0,0 @@
|
|||
/*
|
||||
* Script builds apk in release or debug mode
|
||||
* To run:
|
||||
* gradle assembleRelease -Prelease (release mode)
|
||||
* gradle assembleDebug (debug mode -> default)
|
||||
* Options:
|
||||
* -Prelease //this flag will run build in release mode
|
||||
* -PksPath=[path_to_keystore_file]
|
||||
* -PksPassword=[password_for_keystore_file]
|
||||
* -Palias=[alias_to_use_from_keystore_file]
|
||||
* -Ppassword=[password_for_alias]
|
||||
*
|
||||
* -PtargetSdk=[target_sdk]
|
||||
* -PbuildToolsVersion=[build_tools_version]
|
||||
* -PcompileSdk=[compile_sdk_version]
|
||||
* -PandroidXLegacy=[androidx_legacy_version]
|
||||
* -PandroidXAppCompat=[androidx_appcompat_version]
|
||||
* -PandroidXMaterial=[androidx_material_version]
|
||||
* -PappPath=[app_path]
|
||||
* -PappResourcesPath=[app_resources_path]
|
||||
*/
|
||||
|
||||
|
||||
import groovy.io.FileType
|
||||
import groovy.json.JsonSlurper
|
||||
|
||||
import javax.inject.Inject
|
||||
import java.nio.file.Files
|
||||
import java.nio.file.Paths
|
||||
import java.nio.file.StandardCopyOption
|
||||
import java.security.MessageDigest
|
||||
import org.gradle.internal.logging.text.StyledTextOutputFactory
|
||||
import static org.gradle.internal.logging.text.StyledTextOutput.Style
|
||||
|
||||
apply plugin: "com.android.application"
|
||||
apply from: "gradle-helpers/BuildToolTask.gradle"
|
||||
apply from: "gradle-helpers/CustomExecutionLogger.gradle"
|
||||
apply from: "gradle-helpers/AnalyticsCollector.gradle"
|
||||
|
||||
def enableKotlin = (project.hasProperty("useKotlin") && project.useKotlin == "true")
|
||||
|
||||
if (enableKotlin) {
|
||||
apply plugin: 'kotlin-android'
|
||||
apply plugin: 'kotlin-android-extensions'
|
||||
}
|
||||
|
||||
def onlyX86 = project.hasProperty("onlyX86")
|
||||
if (onlyX86) {
|
||||
outLogger.withStyle(Style.Info).println "OnlyX86 build triggered."
|
||||
}
|
||||
|
||||
//common
|
||||
def BUILD_TOOLS_PATH = "$rootDir/build-tools"
|
||||
def PASSED_TYPINGS_PATH = System.getenv("TNS_TYPESCRIPT_DECLARATIONS_PATH")
|
||||
def TYPINGS_PATH = "$BUILD_TOOLS_PATH/typings"
|
||||
if (PASSED_TYPINGS_PATH != null) {
|
||||
TYPINGS_PATH = PASSED_TYPINGS_PATH
|
||||
}
|
||||
|
||||
def PACKAGE_JSON = "package.json"
|
||||
|
||||
//static binding generator
|
||||
def SBG_JAVA_DEPENDENCIES = "sbg-java-dependencies.txt"
|
||||
def SBG_INPUT_FILE = "sbg-input-file.txt"
|
||||
def SBG_OUTPUT_FILE = "sbg-output-file.txt"
|
||||
def SBG_JS_PARSED_FILES = "sbg-js-parsed-files.txt"
|
||||
def SBG_BINDINGS_NAME = "sbg-bindings.txt"
|
||||
def SBG_INTERFACE_NAMES = "sbg-interface-names.txt"
|
||||
def INPUT_JS_DIR = "$projectDir/src/main/assets/app"
|
||||
def OUTPUT_JAVA_DIR = "$projectDir/src/main/java"
|
||||
|
||||
//metadata generator
|
||||
def MDG_OUTPUT_DIR = "mdg-output-dir.txt"
|
||||
def MDG_JAVA_DEPENDENCIES = "mdg-java-dependencies.txt"
|
||||
def METADATA_OUT_PATH = "$projectDir/src/main/assets/metadata"
|
||||
|
||||
// paths to jar libraries
|
||||
def pluginsJarLibraries = new LinkedList<String>()
|
||||
def allJarLibraries = new LinkedList<String>()
|
||||
|
||||
def computeKotlinVersion = { -> project.hasProperty("kotlinVersion") ? kotlinVersion : "1.3.41" }
|
||||
def computeCompileSdkVersion = { -> project.hasProperty("compileSdk") ? compileSdk : 29 }
|
||||
def computeTargetSdkVersion = { -> project.hasProperty("targetSdk") ? targetSdk : 29 }
|
||||
def computeBuildToolsVersion = { ->
|
||||
project.hasProperty("buildToolsVersion") ? buildToolsVersion : "29.0.2"
|
||||
}
|
||||
|
||||
def enableAnalytics = (project.hasProperty("gatherAnalyticsData") && project.gatherAnalyticsData == "true")
|
||||
def enableVerboseMDG = project.gradle.startParameter.logLevel.name() == 'DEBUG'
|
||||
def analyticsFilePath = "$rootDir/analytics/build-statistics.json"
|
||||
def analyticsCollector = project.ext.AnalyticsCollector.withOutputPath(analyticsFilePath)
|
||||
if (enableAnalytics) {
|
||||
analyticsCollector.markUseKotlinPropertyInApp(enableKotlin)
|
||||
analyticsCollector.writeAnalyticsFile()
|
||||
}
|
||||
|
||||
project.ext.selectedBuildType = project.hasProperty("release") ? "release" : "debug"
|
||||
|
||||
buildscript {
|
||||
// we have moved these initializations here as we need getAppResourcesPath to search for buildscript.gradle files,
|
||||
// but the buildscript is executed before executing anything else from the gradle file
|
||||
def initialize = { ->
|
||||
// the build script will not work with previous versions of the CLI (3.1 or earlier)
|
||||
def dependenciesJson = file("$rootDir/dependencies.json")
|
||||
if (!dependenciesJson.exists()) {
|
||||
throw new BuildCancelledException("""
|
||||
'dependencies.json' file not found. Check whether the NativeScript CLI has prepared the project beforehand,
|
||||
and that your NativeScript version is 3.3, or a more recent one. To build an android project with the current
|
||||
version of the {N} CLI install a previous version of the runtime package - 'tns platform add android@3.2'.
|
||||
""")
|
||||
}
|
||||
|
||||
project.ext.extractedDependenciesDir = "${project.buildDir}/exploded-dependencies"
|
||||
project.ext.cleanupAllJarsTimestamp = "${project.buildDir}/cleanupAllJars.timestamp"
|
||||
project.ext.extractAllJarsTimestamp = "${project.buildDir}/extractAllJars.timestamp"
|
||||
project.ext.nativescriptDependencies = new JsonSlurper().parseText(dependenciesJson.text)
|
||||
project.ext.PLATFORMS_ANDROID = "platforms/android"
|
||||
project.ext.USER_PROJECT_ROOT = "$rootDir/../.."
|
||||
project.ext.outLogger = services.get(StyledTextOutputFactory).create("colouredOutputLogger")
|
||||
|
||||
project.ext.getAppPath = { ->
|
||||
def relativePathToApp = "app"
|
||||
def nsConfigFile = file("$USER_PROJECT_ROOT/nsconfig.json")
|
||||
def nsConfig
|
||||
|
||||
if (nsConfigFile.exists()) {
|
||||
nsConfig = new JsonSlurper().parseText(nsConfigFile.getText("UTF-8"))
|
||||
}
|
||||
|
||||
if (project.hasProperty("appPath")) {
|
||||
// when appPath is passed through -PappPath=/path/to/app
|
||||
// the path could be relative or absolute - either case will work
|
||||
relativePathToApp = appPath
|
||||
} else if (nsConfig != null && nsConfig.appPath != null) {
|
||||
relativePathToApp = nsConfig.appPath
|
||||
}
|
||||
|
||||
project.ext.appPath = java.nio.file.Paths.get(USER_PROJECT_ROOT).resolve(relativePathToApp).toAbsolutePath()
|
||||
|
||||
return project.ext.appPath
|
||||
}
|
||||
|
||||
project.ext.getAppResourcesPath = { ->
|
||||
def relativePathToAppResources
|
||||
def absolutePathToAppResources
|
||||
def nsConfigFile = file("$USER_PROJECT_ROOT/nsconfig.json")
|
||||
def nsConfig
|
||||
|
||||
if (nsConfigFile.exists()) {
|
||||
nsConfig = new JsonSlurper().parseText(nsConfigFile.getText("UTF-8"))
|
||||
}
|
||||
|
||||
if (project.hasProperty("appResourcesPath")) {
|
||||
// when appResourcesPath is passed through -PappResourcesPath=/path/to/App_Resources
|
||||
// the path could be relative or absolute - either case will work
|
||||
relativePathToAppResources = appResourcesPath
|
||||
absolutePathToAppResources = java.nio.file.Paths.get(USER_PROJECT_ROOT).resolve(relativePathToAppResources).toAbsolutePath()
|
||||
} else if (nsConfig != null && nsConfig.appResourcesPath != null) {
|
||||
relativePathToAppResources = nsConfig.appResourcesPath
|
||||
absolutePathToAppResources = java.nio.file.Paths.get(USER_PROJECT_ROOT).resolve(relativePathToAppResources).toAbsolutePath()
|
||||
} else {
|
||||
absolutePathToAppResources = "${getAppPath()}/App_Resources"
|
||||
}
|
||||
|
||||
project.ext.appResourcesPath = absolutePathToAppResources
|
||||
|
||||
return absolutePathToAppResources
|
||||
}
|
||||
}
|
||||
def applyBuildScriptConfigurations = { ->
|
||||
def absolutePathToAppResources = getAppResourcesPath()
|
||||
def pathToBuildScriptGradle = "$absolutePathToAppResources/Android/buildscript.gradle"
|
||||
def buildScriptGradle = file(pathToBuildScriptGradle)
|
||||
if (buildScriptGradle.exists()) {
|
||||
outLogger.withStyle(Style.SuccessHeader).println "\t + applying user-defined buildscript from ${buildScriptGradle}"
|
||||
apply from: pathToBuildScriptGradle, to: buildscript
|
||||
}
|
||||
|
||||
nativescriptDependencies.each { dep ->
|
||||
def pathToPluginBuildScriptGradle = "$rootDir/${dep.directory}/$PLATFORMS_ANDROID/buildscript.gradle"
|
||||
def pluginBuildScriptGradle = file(pathToPluginBuildScriptGradle)
|
||||
if (pluginBuildScriptGradle.exists()) {
|
||||
outLogger.withStyle(Style.SuccessHeader).println "\t + applying user-defined buildscript from dependency ${pluginBuildScriptGradle}"
|
||||
apply from: pathToPluginBuildScriptGradle, to: buildscript
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
initialize()
|
||||
applyBuildScriptConfigurations()
|
||||
}
|
||||
////////////////////////////////////////////////////////////////////////////////////
|
||||
///////////////////////////// CONFIGURATIONS ///////////////////////////////////////
|
||||
////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
def applyBeforePluginGradleConfiguration = { ->
|
||||
def appResourcesPath = getAppResourcesPath()
|
||||
def pathToBeforePluginGradle = "$appResourcesPath/Android/before-plugins.gradle"
|
||||
def beforePluginGradle = file(pathToBeforePluginGradle)
|
||||
if (beforePluginGradle.exists()) {
|
||||
outLogger.withStyle(Style.SuccessHeader).println "\t + applying user-defined configuration from ${beforePluginGradle}"
|
||||
apply from: pathToBeforePluginGradle
|
||||
}
|
||||
}
|
||||
|
||||
def applyAppGradleConfiguration = { ->
|
||||
def appResourcesPath = getAppResourcesPath()
|
||||
def pathToAppGradle = "$appResourcesPath/Android/app.gradle"
|
||||
def appGradle = file(pathToAppGradle)
|
||||
if (appGradle.exists()) {
|
||||
outLogger.withStyle(Style.SuccessHeader).println "\t + applying user-defined configuration from ${appGradle}"
|
||||
apply from: pathToAppGradle
|
||||
} else {
|
||||
outLogger.withStyle(Style.Info).println "\t + couldn't load user-defined configuration from ${appGradle}. File doesn't exist."
|
||||
}
|
||||
}
|
||||
|
||||
def applyPluginGradleConfigurations = { ->
|
||||
nativescriptDependencies.each { dep ->
|
||||
def includeGradlePath = "$rootDir/${dep.directory}/$PLATFORMS_ANDROID/include.gradle"
|
||||
if (file(includeGradlePath).exists()) {
|
||||
apply from: includeGradlePath
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def getAppIdentifier = { packageJsonMap ->
|
||||
def appIdentifier = ""
|
||||
if (packageJsonMap && packageJsonMap.nativescript) {
|
||||
appIdentifier = packageJsonMap.nativescript.id
|
||||
if (!(appIdentifier instanceof String)) {
|
||||
appIdentifier = appIdentifier.android
|
||||
}
|
||||
}
|
||||
|
||||
return appIdentifier
|
||||
}
|
||||
|
||||
def setAppIdentifier = { ->
|
||||
outLogger.withStyle(Style.SuccessHeader).println "\t + setting applicationId"
|
||||
File packageJsonFile = new File("$USER_PROJECT_ROOT/$PACKAGE_JSON")
|
||||
|
||||
if (packageJsonFile.exists()) {
|
||||
def content = packageJsonFile.getText("UTF-8")
|
||||
def jsonSlurper = new JsonSlurper()
|
||||
def packageJsonMap = jsonSlurper.parseText(content)
|
||||
def appIdentifier = getAppIdentifier(packageJsonMap)
|
||||
|
||||
if (appIdentifier) {
|
||||
project.ext.nsApplicationIdentifier = appIdentifier
|
||||
android.defaultConfig.applicationId = appIdentifier
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
android {
|
||||
|
||||
if (enableKotlin) {
|
||||
kotlinOptions {
|
||||
jvmTarget = '1.8'
|
||||
}
|
||||
}
|
||||
|
||||
compileSdkVersion computeCompileSdkVersion()
|
||||
buildToolsVersion computeBuildToolsVersion()
|
||||
|
||||
defaultConfig {
|
||||
def manifest = new XmlSlurper().parse(file(android.sourceSets.main.manifest.srcFile))
|
||||
def minSdkVer = manifest."uses-sdk"."@android:minSdkVersion".text() ?: 17
|
||||
minSdkVersion minSdkVer
|
||||
targetSdkVersion computeTargetSdkVersion()
|
||||
ndk {
|
||||
if (onlyX86) {
|
||||
abiFilters 'x86'
|
||||
} else {
|
||||
abiFilters 'x86', 'x86_64', 'armeabi-v7a', 'arm64-v8a'
|
||||
}
|
||||
}
|
||||
dexOptions {
|
||||
jumboMode = true
|
||||
}
|
||||
}
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility JavaVersion.VERSION_1_8
|
||||
targetCompatibility JavaVersion.VERSION_1_8
|
||||
}
|
||||
|
||||
sourceSets.main {
|
||||
jniLibs.srcDirs = ["$projectDir/libs/jni", "$projectDir/snapshot-build/build/ndk-build/libs"]
|
||||
}
|
||||
|
||||
signingConfigs {
|
||||
release {
|
||||
if (project.hasProperty("release")) {
|
||||
if (project.hasProperty("ksPath") &&
|
||||
project.hasProperty("ksPassword") &&
|
||||
project.hasProperty("alias") &&
|
||||
project.hasProperty("password")) {
|
||||
|
||||
storeFile file(ksPath)
|
||||
storePassword ksPassword
|
||||
keyAlias alias
|
||||
keyPassword password
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
buildTypes {
|
||||
release {
|
||||
signingConfig signingConfigs.release
|
||||
}
|
||||
}
|
||||
|
||||
setAppIdentifier()
|
||||
applyBeforePluginGradleConfiguration()
|
||||
applyPluginGradleConfigurations()
|
||||
applyAppGradleConfiguration()
|
||||
|
||||
def initializeMergedAssetsOutputPath = { ->
|
||||
android.applicationVariants.all { variant ->
|
||||
if (variant.buildType.name == project.selectedBuildType) {
|
||||
def task
|
||||
if (variant.metaClass.respondsTo(variant, "getMergeAssetsProvider")) {
|
||||
def provider = variant.getMergeAssetsProvider()
|
||||
task = provider.get();
|
||||
} else {
|
||||
// fallback for older android gradle plugin versions
|
||||
task = variant.getMergeAssets()
|
||||
}
|
||||
for (File file : task.getOutputs().getFiles()) {
|
||||
if (!file.getPath().contains("${File.separator}incremental${File.separator}")) {
|
||||
project.ext.mergedAssetsOutputPath = file.getPath()
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
initializeMergedAssetsOutputPath()
|
||||
}
|
||||
|
||||
def externalRuntimeExists = !findProject(':runtime').is(null)
|
||||
def pluginDependencies
|
||||
|
||||
repositories {
|
||||
// used for local *.AAR files
|
||||
pluginDependencies = nativescriptDependencies.collect {
|
||||
"$rootDir/${it.directory}/$PLATFORMS_ANDROID"
|
||||
}
|
||||
|
||||
// some plugins may have their android dependencies in a /libs subdirectory
|
||||
pluginDependencies.addAll(nativescriptDependencies.collect {
|
||||
"$rootDir/${it.directory}/$PLATFORMS_ANDROID/libs"
|
||||
})
|
||||
|
||||
if (!externalRuntimeExists) {
|
||||
pluginDependencies.add("libs/runtime-libs")
|
||||
}
|
||||
|
||||
def appResourcesPath = getAppResourcesPath()
|
||||
def localAppResourcesLibraries = "$appResourcesPath/Android/libs"
|
||||
|
||||
pluginDependencies.add(localAppResourcesLibraries)
|
||||
|
||||
if (pluginDependencies.size() > 0) {
|
||||
flatDir {
|
||||
dirs pluginDependencies
|
||||
}
|
||||
}
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
def androidXLegacyVersion = "1.0.0"
|
||||
if (project.hasProperty("androidXLegacy")) {
|
||||
androidXLegacyVersion = androidXLegacy
|
||||
}
|
||||
|
||||
def androidXAppCompatVersion = "1.1.0"
|
||||
if (project.hasProperty("androidXAppCompat")) {
|
||||
androidXAppCompatVersion = androidXAppCompat
|
||||
}
|
||||
|
||||
def androidXMaterialVersion = "1.1.0"
|
||||
if (project.hasProperty("androidXMaterial")) {
|
||||
androidXMaterialVersion = androidXMaterial
|
||||
}
|
||||
|
||||
def androidXExifInterfaceVersion = "1.2.0"
|
||||
if (project.hasProperty("androidXExifInterface")) {
|
||||
androidxExifInterfaceVersion = androidXExifInterface
|
||||
}
|
||||
|
||||
def androidXViewPagerVersion = "1.0.0"
|
||||
if (project.hasProperty("androidXViewPager")) {
|
||||
androidXViewPagerVersion = androidXViewPager
|
||||
}
|
||||
|
||||
def androidXFragmentVersion = "1.2.5"
|
||||
if (project.hasProperty("androidXFragment")) {
|
||||
androidXFragmentVersion = androidXFragment
|
||||
}
|
||||
|
||||
def androidXTransitionVersion = "1.3.1"
|
||||
if (project.hasProperty("androidXTransition")) {
|
||||
androidXTransitionVersion = androidXTransition
|
||||
}
|
||||
|
||||
outLogger.withStyle(Style.SuccessHeader).println "\t + using android X library androidx.legacy:legacy-support-v4:$androidXLegacyVersion"
|
||||
|
||||
implementation "androidx.multidex:multidex:2.0.1"
|
||||
implementation "androidx.legacy:legacy-support-v4:$androidXLegacyVersion"
|
||||
implementation "androidx.appcompat:appcompat:$androidXAppCompatVersion"
|
||||
implementation "com.google.android.material:material:$androidXMaterialVersion"
|
||||
implementation "androidx.exifinterface:exifinterface:$androidXExifInterfaceVersion"
|
||||
implementation "androidx.viewpager2:viewpager2:$androidXViewPagerVersion"
|
||||
implementation "androidx.fragment:fragment:$androidXFragmentVersion"
|
||||
implementation "androidx.transition:transition:$androidXTransitionVersion"
|
||||
|
||||
def useV8Symbols = false
|
||||
|
||||
def appPackageJsonFile = file("${getAppPath()}/$PACKAGE_JSON")
|
||||
if (appPackageJsonFile.exists()) {
|
||||
def appPackageJson = new JsonSlurper().parseText(appPackageJsonFile.text)
|
||||
useV8Symbols = appPackageJson.android && appPackageJson.android.useV8Symbols
|
||||
}
|
||||
|
||||
if (!useV8Symbols) {
|
||||
// check whether any of the dependencies require v8 symbols
|
||||
useV8Symbols = nativescriptDependencies.any {
|
||||
def packageJsonFile = file("$rootDir/${it.directory}/$PACKAGE_JSON")
|
||||
def packageJson = new JsonSlurper().parseText(packageJsonFile.text)
|
||||
return packageJson.nativescript && packageJson.nativescript.useV8Symbols
|
||||
}
|
||||
}
|
||||
|
||||
if (!externalRuntimeExists) {
|
||||
def runtime = "nativescript-optimized-with-inspector"
|
||||
|
||||
if (project.gradle.startParameter.taskNames.any { it.toLowerCase().contains('release') }) {
|
||||
runtime = "nativescript-optimized"
|
||||
}
|
||||
|
||||
if (useV8Symbols) {
|
||||
runtime = "nativescript-regular"
|
||||
}
|
||||
|
||||
outLogger.withStyle(Style.SuccessHeader).println "\t + adding nativescript runtime package dependency: $runtime"
|
||||
project.dependencies.add("implementation", [name: runtime, ext: "aar"])
|
||||
} else {
|
||||
implementation project(':runtime')
|
||||
}
|
||||
|
||||
def kotlinVersion = computeKotlinVersion()
|
||||
if (enableKotlin) {
|
||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlinVersion"
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////
|
||||
///////////////////////////// CONFIGURATION PHASE //////////////////////////////////
|
||||
////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
task addDependenciesFromNativeScriptPlugins {
|
||||
nativescriptDependencies.each { dep ->
|
||||
def aarFiles = fileTree(dir: file("$rootDir/${dep.directory}/$PLATFORMS_ANDROID"), include: ["**/*.aar"])
|
||||
aarFiles.each { aarFile ->
|
||||
def length = aarFile.name.length() - 4
|
||||
def fileName = aarFile.name[0..<length]
|
||||
outLogger.withStyle(Style.SuccessHeader).println "\t + adding aar plugin dependency: " + aarFile.getAbsolutePath()
|
||||
project.dependencies.add("implementation", [name: fileName, ext: "aar"])
|
||||
}
|
||||
|
||||
def jarFiles = fileTree(dir: file("$rootDir/${dep.directory}/$PLATFORMS_ANDROID"), include: ["**/*.jar"])
|
||||
jarFiles.each { jarFile ->
|
||||
def jarFileAbsolutePath = jarFile.getAbsolutePath()
|
||||
outLogger.withStyle(Style.SuccessHeader).println "\t + adding jar plugin dependency: $jarFileAbsolutePath"
|
||||
pluginsJarLibraries.add(jarFile.getAbsolutePath())
|
||||
}
|
||||
|
||||
project.dependencies.add("implementation", jarFiles)
|
||||
}
|
||||
}
|
||||
|
||||
task addDependenciesFromAppResourcesLibraries {
|
||||
def appResourcesPath = getAppResourcesPath()
|
||||
def appResourcesLibraries = file("$appResourcesPath/Android/libs")
|
||||
if (appResourcesLibraries.exists()) {
|
||||
def aarFiles = fileTree(dir: appResourcesLibraries, include: ["**/*.aar"])
|
||||
aarFiles.each { aarFile ->
|
||||
def length = aarFile.name.length() - 4
|
||||
def fileName = aarFile.name[0..<length]
|
||||
outLogger.withStyle(Style.SuccessHeader).println "\t + adding aar library dependency: " + aarFile.getAbsolutePath()
|
||||
project.dependencies.add("implementation", [name: fileName, ext: "aar"])
|
||||
}
|
||||
|
||||
def jarFiles = fileTree(dir: appResourcesLibraries, include: ["**/*.jar"])
|
||||
jarFiles.each { jarFile ->
|
||||
def jarFileAbsolutePath = jarFile.getAbsolutePath()
|
||||
outLogger.withStyle(Style.SuccessHeader).println "\t + adding jar plugin dependency: $jarFileAbsolutePath"
|
||||
pluginsJarLibraries.add(jarFile.getAbsolutePath())
|
||||
}
|
||||
|
||||
project.dependencies.add("implementation", jarFiles)
|
||||
}
|
||||
}
|
||||
|
||||
if (failOnCompilationWarningsEnabled()) {
|
||||
tasks.withType(JavaCompile) {
|
||||
options.compilerArgs << '-Xlint:all' << "-Werror"
|
||||
options.deprecation = true
|
||||
}
|
||||
}
|
||||
|
||||
tasks.whenTaskAdded({ org.gradle.api.DefaultTask currentTask ->
|
||||
if (currentTask =~ /generate.+BuildConfig/) {
|
||||
currentTask.finalizedBy(extractAllJars)
|
||||
extractAllJars.finalizedBy(collectAllJars)
|
||||
}
|
||||
if (currentTask =~ /compile.+JavaWithJavac/) {
|
||||
currentTask.dependsOn(runSbg)
|
||||
currentTask.finalizedBy(buildMetadata)
|
||||
}
|
||||
if (currentTask =~ /merge.*Assets/) {
|
||||
currentTask.shouldRunAfter(buildMetadata)
|
||||
}
|
||||
if (currentTask =~ /assemble.*Debug/ || currentTask =~ /assemble.*Release/) {
|
||||
currentTask.finalizedBy("validateAppIdMatch")
|
||||
}
|
||||
})
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////
|
||||
///////////////////////////// EXECUTUION PHASE /////////////////////////////////////
|
||||
////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
task runSbg(type: BuildToolTask) {
|
||||
dependsOn "collectAllJars"
|
||||
if (!findProject(':static-binding-generator').is(null)) {
|
||||
dependsOn ':static-binding-generator:jar'
|
||||
}
|
||||
|
||||
outputs.dir("$OUTPUT_JAVA_DIR/com/tns/gen")
|
||||
inputs.dir(INPUT_JS_DIR)
|
||||
inputs.dir(extractedDependenciesDir)
|
||||
|
||||
workingDir "$BUILD_TOOLS_PATH"
|
||||
main "-jar"
|
||||
|
||||
def paramz = new ArrayList<String>()
|
||||
paramz.add("static-binding-generator.jar")
|
||||
|
||||
if (failOnCompilationWarningsEnabled()) {
|
||||
paramz.add("-show-deprecation-warnings")
|
||||
}
|
||||
|
||||
setOutputs outLogger
|
||||
|
||||
args paramz
|
||||
|
||||
doFirst {
|
||||
new File("$OUTPUT_JAVA_DIR/com/tns/gen").deleteDir()
|
||||
}
|
||||
}
|
||||
|
||||
def failOnCompilationWarningsEnabled() {
|
||||
return project.hasProperty("failOnCompilationWarnings") && (failOnCompilationWarnings || failOnCompilationWarnings.toBoolean())
|
||||
}
|
||||
|
||||
def explodeAar(File compileDependency, File outputDir) {
|
||||
logger.info("explodeAar: Extracting ${compileDependency.path} -> ${outputDir.path}")
|
||||
|
||||
if (compileDependency.name.endsWith(".aar")) {
|
||||
java.util.jar.JarFile jar = new java.util.jar.JarFile(compileDependency)
|
||||
Enumeration enumEntries = jar.entries()
|
||||
while (enumEntries.hasMoreElements()) {
|
||||
java.util.jar.JarEntry file = (java.util.jar.JarEntry) enumEntries.nextElement()
|
||||
if (file.name.endsWith(".jar")) {
|
||||
def targetFile = new File(outputDir, file.name)
|
||||
InputStream inputStream = jar.getInputStream(file)
|
||||
new File(targetFile.parent).mkdirs()
|
||||
Files.copy(inputStream, targetFile.toPath(), StandardCopyOption.REPLACE_EXISTING)
|
||||
}
|
||||
if (file.isDirectory()) {
|
||||
continue
|
||||
}
|
||||
}
|
||||
jar.close()
|
||||
} else if (compileDependency.name.endsWith(".jar")) {
|
||||
copy {
|
||||
from compileDependency.absolutePath
|
||||
into outputDir
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def md5(String string) {
|
||||
MessageDigest digest = MessageDigest.getInstance("MD5")
|
||||
digest.update(string.bytes)
|
||||
return new BigInteger(1, digest.digest()).toString(16).padLeft(32, '0')
|
||||
}
|
||||
|
||||
class WorkerTask extends DefaultTask {
|
||||
@Inject
|
||||
WorkerExecutor getWorkerExecutor() {
|
||||
throw new UnsupportedOperationException()
|
||||
}
|
||||
}
|
||||
|
||||
class EmptyRunnable implements Runnable {
|
||||
void run() {
|
||||
}
|
||||
}
|
||||
|
||||
def getMergedAssetsOutputPath() {
|
||||
if (!project.hasProperty("mergedAssetsOutputPath")) {
|
||||
// mergedAssetsOutputPath not found fallback to the default value for android gradle plugin 3.5.1
|
||||
project.ext.mergedAssetsOutputPath = "$projectDir/build/intermediates/merged_assets/" + project.selectedBuildType + "/out"
|
||||
}
|
||||
return project.ext.mergedAssetsOutputPath
|
||||
}
|
||||
|
||||
// Discover all jars and dynamically create tasks for the extraction of each of them
|
||||
project.ext.allJars = []
|
||||
afterEvaluate { project ->
|
||||
def buildType = project.selectedBuildType == "release" ? "Release" : "Debug"
|
||||
def jars = []
|
||||
def artifactType = Attribute.of('artifactType', String)
|
||||
configurations.all { config ->
|
||||
if (config.name.toLowerCase().endsWith("${buildType}RuntimeClasspath".toLowerCase())) {
|
||||
config.incoming.artifactView {
|
||||
attributes {
|
||||
it.attribute(artifactType, 'jar')
|
||||
}
|
||||
}.artifacts.each {
|
||||
processJar(it.file, jars)
|
||||
}
|
||||
|
||||
def projectDependencies = config.getAllDependencies().withType(ProjectDependency)
|
||||
def dependentProjects = projectDependencies*.dependencyProject
|
||||
dependentProjects.findAll {
|
||||
// if there's a project dependency search for its result jar file in the build/intermediates/runtime_library_classes folder
|
||||
// this is the output folder in gradle 5.1.1, but it can be changed in the future versions of gradle
|
||||
def jarDir = new File("${it.getBuildDir()}/intermediates/runtime_library_classes/${buildType.toLowerCase()}")
|
||||
if (jarDir.exists()) {
|
||||
jarDir.eachFileRecurse(FileType.FILES) { file ->
|
||||
if (file.path.endsWith(".jar")) {
|
||||
processJar(file, jars)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
outLogger.withStyle(Style.Info).println "WARNING: Folder ${jarDir.path} does not exists, the dependent project's classes won't be included in the metadata"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def processJar(File jar, jars) {
|
||||
if (!jars.contains(jar)) {
|
||||
jars.add(jar)
|
||||
def destDir = md5(jar.path)
|
||||
def outputDir = new File(Paths.get(extractedDependenciesDir, destDir).normalize().toString())
|
||||
|
||||
def taskName = "extract_${jar.name}_to_${destDir}"
|
||||
logger.debug("Creating dynamic task ${taskName}")
|
||||
|
||||
// Add discovered jars as dependencies of cleanupAllJars.
|
||||
// This is cruicial for cloud builds because they are different
|
||||
// on each incremental build (as each time the gradle user home
|
||||
// directory is a randomly generated string)
|
||||
cleanupAllJars.inputs.files jar
|
||||
|
||||
task "${taskName}"(type: WorkerTask) {
|
||||
dependsOn cleanupAllJars
|
||||
extractAllJars.dependsOn it
|
||||
|
||||
// This dependency seems redundant but probably due to some Gradle issue with workers,
|
||||
// without it `runSbg` sporadically starts before all extraction tasks have finished and
|
||||
// fails due to missing JARs
|
||||
runSbg.dependsOn it
|
||||
|
||||
inputs.files jar
|
||||
outputs.dir outputDir
|
||||
|
||||
doLast {
|
||||
// Runing in parallel no longer seems to bring any benefit.
|
||||
// It mattered only when we were extracting JARs from AARs.
|
||||
// To try it simply remove the following comments.
|
||||
// workerExecutor.submit(EmptyRunnable.class) {
|
||||
explodeAar(jar, outputDir)
|
||||
// }
|
||||
}
|
||||
}
|
||||
project.ext.allJars.add([file: jar, outputDir: outputDir])
|
||||
}
|
||||
}
|
||||
|
||||
task cleanupAllJars {
|
||||
// We depend on the list of libs directories that might contain aar or jar files
|
||||
// and on the list of all discovered jars
|
||||
inputs.files(pluginDependencies)
|
||||
|
||||
outputs.files cleanupAllJarsTimestamp
|
||||
|
||||
doLast {
|
||||
def allDests = project.ext.allJars*.outputDir*.name
|
||||
def dir = new File(extractedDependenciesDir)
|
||||
if (dir.exists()) {
|
||||
dir.eachDir {
|
||||
// An old directory which is no longer a dependency (e.g. orphaned by a deleted plugin)
|
||||
if (!allDests.contains(it.name)) {
|
||||
logger.info("Task cleanupAllJars: Deleting orphaned ${it.path}")
|
||||
org.apache.commons.io.FileUtils.deleteDirectory(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
new File(cleanupAllJarsTimestamp).write ""
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Placeholder task which depends on all dynamically generated extraction tasks
|
||||
task extractAllJars {
|
||||
dependsOn cleanupAllJars
|
||||
outputs.files extractAllJarsTimestamp
|
||||
|
||||
doLast {
|
||||
new File(cleanupAllJarsTimestamp).write ""
|
||||
}
|
||||
}
|
||||
|
||||
task collectAllJars {
|
||||
dependsOn extractAllJars
|
||||
description "gathers all paths to jar dependencies before building metadata with them"
|
||||
|
||||
def sdkPath = android.sdkDirectory.getAbsolutePath()
|
||||
def androidJar = sdkPath + "/platforms/" + android.compileSdkVersion + "/android.jar"
|
||||
|
||||
doFirst {
|
||||
def allJarPaths = new LinkedList<String>()
|
||||
allJarPaths.add(androidJar)
|
||||
allJarPaths.addAll(pluginsJarLibraries)
|
||||
def ft = fileTree(dir: extractedDependenciesDir, include: "**/*.jar")
|
||||
ft.each { currentJarFile ->
|
||||
allJarPaths.add(currentJarFile.getAbsolutePath())
|
||||
}
|
||||
|
||||
new File("$BUILD_TOOLS_PATH/$SBG_JAVA_DEPENDENCIES").withWriter { out ->
|
||||
allJarPaths.each { out.println it }
|
||||
}
|
||||
new File("$BUILD_TOOLS_PATH/$MDG_JAVA_DEPENDENCIES").withWriter { out ->
|
||||
allJarPaths.each {
|
||||
if (it.endsWith(".jar")) {
|
||||
out.println it
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
new File("$BUILD_TOOLS_PATH/$SBG_INPUT_FILE").withWriter { out ->
|
||||
out.println INPUT_JS_DIR
|
||||
}
|
||||
new File("$BUILD_TOOLS_PATH/$SBG_OUTPUT_FILE").withWriter { out ->
|
||||
out.println OUTPUT_JAVA_DIR
|
||||
}
|
||||
|
||||
allJarLibraries.addAll(allJarPaths)
|
||||
}
|
||||
}
|
||||
|
||||
task copyMetadataFilters(type: Copy) {
|
||||
from "$rootDir/whitelist.mdg", "$rootDir/blacklist.mdg"
|
||||
into "$BUILD_TOOLS_PATH"
|
||||
}
|
||||
|
||||
task copyMetadata {
|
||||
doLast {
|
||||
copy {
|
||||
from "$projectDir/src/main/assets/metadata"
|
||||
into getMergedAssetsOutputPath() + "/metadata"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
task buildMetadata(type: BuildToolTask) {
|
||||
if (!findProject(':android-metadata-generator').is(null)) {
|
||||
dependsOn ':android-metadata-generator:jar'
|
||||
}
|
||||
|
||||
dependsOn copyMetadataFilters
|
||||
|
||||
// As some external gradle plugins can reorder the execution order of the tasks it may happen that buildMetadata is executed after merge{Debug/Release}Assets
|
||||
// in that case the metadata won't be included in the result apk and it will crash, so to avoid this we are adding the copyMetadata task which will manually copy
|
||||
// the metadata files in the merge assets folder and they will be added to the result apk
|
||||
|
||||
// The next line is added to avoid adding another copyData implementation from the firebase plugin - https://github.com/EddyVerbruggen/nativescript-plugin-firebase/blob/3943bb9147f43c41599e801d026378eba93d3f3a/publish/scripts/installer.js#L1105
|
||||
//buildMetadata.finalizedBy(copyMetadata)
|
||||
finalizedBy copyMetadata
|
||||
|
||||
description "builds metadata with provided jar dependencies"
|
||||
|
||||
inputs.files("$MDG_JAVA_DEPENDENCIES")
|
||||
|
||||
// make MDG aware of whitelist.mdg and blacklist.mdg files
|
||||
inputs.files(project.fileTree(dir: "$rootDir", include: "**/*.mdg"))
|
||||
|
||||
def classesDir = "$buildDir/intermediates/javac"
|
||||
inputs.dir(classesDir)
|
||||
|
||||
def kotlinClassesDir = "$buildDir/tmp/kotlin-classes"
|
||||
if (file(kotlinClassesDir).exists()) {
|
||||
inputs.dir(kotlinClassesDir)
|
||||
}
|
||||
|
||||
outputs.files("$METADATA_OUT_PATH/treeNodeStream.dat", "$METADATA_OUT_PATH/treeStringsStream.dat", "$METADATA_OUT_PATH/treeValueStream.dat")
|
||||
|
||||
workingDir "$BUILD_TOOLS_PATH"
|
||||
main "-jar"
|
||||
|
||||
doFirst {
|
||||
// get compiled classes to pass to metadata generator
|
||||
// these need to be called after the classes have compiled
|
||||
assert file(classesDir).exists()
|
||||
|
||||
new File(getMergedAssetsOutputPath() + "/metadata").deleteDir()
|
||||
|
||||
def classesSubDirs = new File(classesDir).listFiles()
|
||||
def selectedBuildType = project.ext.selectedBuildType
|
||||
|
||||
def generatedClasses = new LinkedList<String>()
|
||||
for (File subDir : classesSubDirs) {
|
||||
if (subDir.getName().equals(selectedBuildType)) {
|
||||
generatedClasses.add(subDir.getAbsolutePath())
|
||||
}
|
||||
}
|
||||
|
||||
if (file(kotlinClassesDir).exists()) {
|
||||
def kotlinClassesSubDirs = new File(kotlinClassesDir).listFiles()
|
||||
for (File subDir : kotlinClassesSubDirs) {
|
||||
if (subDir.getName() == selectedBuildType) {
|
||||
generatedClasses.add(subDir.getAbsolutePath())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
new File("$BUILD_TOOLS_PATH/$MDG_OUTPUT_DIR").withWriter { out ->
|
||||
out.println "$METADATA_OUT_PATH"
|
||||
}
|
||||
|
||||
new File("$BUILD_TOOLS_PATH/$MDG_JAVA_DEPENDENCIES").withWriterAppend { out ->
|
||||
generatedClasses.each { out.println it }
|
||||
}
|
||||
|
||||
setOutputs outLogger
|
||||
|
||||
def paramz = new ArrayList<String>()
|
||||
paramz.add("android-metadata-generator.jar")
|
||||
|
||||
if(enableAnalytics){
|
||||
paramz.add("analyticsFilePath=$analyticsFilePath")
|
||||
}
|
||||
|
||||
if(enableVerboseMDG){
|
||||
paramz.add("verbose")
|
||||
}
|
||||
|
||||
args paramz.toArray()
|
||||
}
|
||||
}
|
||||
|
||||
task generateTypescriptDefinitions(type: BuildToolTask) {
|
||||
if (!findProject(':dts-generator').is(null)) {
|
||||
dependsOn ':dts-generator:jar'
|
||||
}
|
||||
|
||||
def paramz = new ArrayList<String>()
|
||||
def includeDirs = ["com.android.support", "/platforms/" + android.compileSdkVersion]
|
||||
|
||||
workingDir "$BUILD_TOOLS_PATH"
|
||||
main "-jar"
|
||||
|
||||
doFirst {
|
||||
delete "$TYPINGS_PATH"
|
||||
|
||||
paramz.add("dts-generator.jar")
|
||||
paramz.add("-input")
|
||||
|
||||
for (String jarPath : allJarLibraries) {
|
||||
// don't generate typings for runtime jars and classes
|
||||
if (shouldIncludeDirForTypings(jarPath, includeDirs)) {
|
||||
paramz.add(jarPath)
|
||||
}
|
||||
}
|
||||
|
||||
paramz.add("-output")
|
||||
paramz.add("$TYPINGS_PATH")
|
||||
|
||||
new File("$TYPINGS_PATH").mkdirs()
|
||||
|
||||
logger.info("Task generateTypescriptDefinitions: Call dts-generator.jar with arguments: " + paramz.toString().replaceAll(',', ''))
|
||||
outLogger.withStyle(Style.SuccessHeader).println "Task generateTypescriptDefinitions: Call dts-generator.jar with arguments: " + paramz.toString().replaceAll(',', '')
|
||||
|
||||
setOutputs outLogger
|
||||
|
||||
args paramz.toArray()
|
||||
}
|
||||
}
|
||||
|
||||
generateTypescriptDefinitions.onlyIf {
|
||||
(project.hasProperty("generateTypings") && Boolean.parseBoolean(project.generateTypings)) || PASSED_TYPINGS_PATH != null
|
||||
}
|
||||
|
||||
collectAllJars.finalizedBy(generateTypescriptDefinitions)
|
||||
|
||||
static def shouldIncludeDirForTypings(path, includeDirs) {
|
||||
for (String p : includeDirs) {
|
||||
if (path.indexOf(p) > -1) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
task copyTypings {
|
||||
doLast {
|
||||
outLogger.withStyle(Style.Info).println "Copied generated typings to application root level. Make sure to import android.d.ts in reference.d.ts"
|
||||
|
||||
copy {
|
||||
from "$TYPINGS_PATH"
|
||||
into "$USER_PROJECT_ROOT"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
copyTypings.onlyIf { generateTypescriptDefinitions.didWork }
|
||||
generateTypescriptDefinitions.finalizedBy(copyTypings)
|
||||
|
||||
task validateAppIdMatch {
|
||||
doLast {
|
||||
def lineSeparator = System.getProperty("line.separator")
|
||||
|
||||
if (project.hasProperty("nsApplicationIdentifier") && !project.hasProperty("release")) {
|
||||
if (project.nsApplicationIdentifier != android.defaultConfig.applicationId) {
|
||||
def errorMessage = "${lineSeparator}WARNING: The Application identifier is different from the one inside \"package.json\" file.$lineSeparator" +
|
||||
"NativeScript CLI might not work properly.$lineSeparator" +
|
||||
"Remove applicationId from app.gradle and update the \"nativescript.id\" in package.json.$lineSeparator" +
|
||||
"Actual: ${android.defaultConfig.applicationId}$lineSeparator" +
|
||||
"Expected(from \"package.json\"): ${project.nsApplicationIdentifier}$lineSeparator"
|
||||
|
||||
logger.error(errorMessage)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////
|
||||
////////////////////////////// OPTIONAL TASKS //////////////////////////////////////
|
||||
////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
//////// custom clean ///////////
|
||||
task cleanSbg(type: Delete) {
|
||||
delete "$BUILD_TOOLS_PATH/$SBG_JS_PARSED_FILES",
|
||||
"$BUILD_TOOLS_PATH/$SBG_JAVA_DEPENDENCIES",
|
||||
"$BUILD_TOOLS_PATH/$SBG_INTERFACE_NAMES",
|
||||
"$BUILD_TOOLS_PATH/$SBG_BINDINGS_NAME",
|
||||
"$BUILD_TOOLS_PATH/$SBG_INPUT_FILE",
|
||||
"$BUILD_TOOLS_PATH/$SBG_OUTPUT_FILE",
|
||||
"$OUTPUT_JAVA_DIR/com/tns/gen"
|
||||
}
|
||||
|
||||
task cleanMdg(type: Delete) {
|
||||
delete "$BUILD_TOOLS_PATH/$MDG_OUTPUT_DIR",
|
||||
"$BUILD_TOOLS_PATH/whitelist.mdg",
|
||||
"$BUILD_TOOLS_PATH/blacklist.mdg",
|
||||
"$BUILD_TOOLS_PATH/$MDG_JAVA_DEPENDENCIES",
|
||||
"$METADATA_OUT_PATH"
|
||||
}
|
||||
|
||||
cleanSbg.dependsOn(cleanMdg)
|
||||
clean.dependsOn(cleanSbg)
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -1,4 +0,0 @@
|
|||
{
|
||||
"prepareTime": "Wed Dec 30 2020 14:50:17 GMT+0530 (India Standard Time)",
|
||||
"buildTime": "Wed Dec 30 2020 14:50:40 GMT+0530 (India Standard Time)"
|
||||
}
|
|
@ -1,57 +0,0 @@
|
|||
{
|
||||
"/mnt/82e5ff15-70b8-44a5-bb66-55688fc2381f/Freelance/Projects/WIP/EnRecipes/Developement/EnRecipes-App/platforms/android/app/src/main/assets/app/tns-java-classes.js": "da39a3ee5e6b4b0d3255bfef95601890afd80709",
|
||||
"/mnt/82e5ff15-70b8-44a5-bb66-55688fc2381f/Freelance/Projects/WIP/EnRecipes/Developement/EnRecipes-App/platforms/android/app/src/main/assets/app/fonts/Orkney-Medium.otf": "c11f050eaecbb1cc95c90bcc25f47bc0dcd35ec4",
|
||||
"/mnt/82e5ff15-70b8-44a5-bb66-55688fc2381f/Freelance/Projects/WIP/EnRecipes/Developement/EnRecipes-App/platforms/android/app/src/main/assets/app/fonts/Orkney-Regular.otf": "eca386e11c6c51a2800a24a456bebcbc2a699b98",
|
||||
"/mnt/82e5ff15-70b8-44a5-bb66-55688fc2381f/Freelance/Projects/WIP/EnRecipes/Developement/EnRecipes-App/platforms/android/app/src/main/assets/app/package.json": "66e3387f8d71589213a52a9179935c1f0c93181b",
|
||||
"/mnt/82e5ff15-70b8-44a5-bb66-55688fc2381f/Freelance/Projects/WIP/EnRecipes/Developement/EnRecipes-App/platforms/android/app/src/main/assets/app/resources/Android/src/main/res/drawable-hdpi/camera.png": "73911d2bab8115193301e1638d74e6a9efdb7305",
|
||||
"/mnt/82e5ff15-70b8-44a5-bb66-55688fc2381f/Freelance/Projects/WIP/EnRecipes/Developement/EnRecipes-App/platforms/android/app/src/main/assets/app/resources/Android/src/main/res/drawable-hdpi/detail.png": "dde1aae9a0523df821a8ce26841ed37d2ef7b89c",
|
||||
"/mnt/82e5ff15-70b8-44a5-bb66-55688fc2381f/Freelance/Projects/WIP/EnRecipes/Developement/EnRecipes-App/platforms/android/app/src/main/assets/app/resources/Android/src/main/res/drawable-hdpi/ic_launcher.png": "1f6cb451f5e8f74fca7dd14a6e2078bb40c3def4",
|
||||
"/mnt/82e5ff15-70b8-44a5-bb66-55688fc2381f/Freelance/Projects/WIP/EnRecipes/Developement/EnRecipes-App/platforms/android/app/src/main/assets/app/resources/Android/src/main/res/drawable-hdpi/ic_launcher_foreground.png": "aeb5840f7f88a0a044eff038ad7f65a1f12b93fc",
|
||||
"/mnt/82e5ff15-70b8-44a5-bb66-55688fc2381f/Freelance/Projects/WIP/EnRecipes/Developement/EnRecipes-App/platforms/android/app/src/main/assets/app/resources/Android/src/main/res/drawable-hdpi/logo.png": "2db8a76318f10c944f5492ad65711b775d572e84",
|
||||
"/mnt/82e5ff15-70b8-44a5-bb66-55688fc2381f/Freelance/Projects/WIP/EnRecipes/Developement/EnRecipes-App/platforms/android/app/src/main/assets/app/resources/Android/src/main/res/drawable-hdpi/photo.png": "0bbf60c565e64e27e66afa8ac3fe27e33332ebd3",
|
||||
"/mnt/82e5ff15-70b8-44a5-bb66-55688fc2381f/Freelance/Projects/WIP/EnRecipes/Developement/EnRecipes-App/platforms/android/app/src/main/assets/app/resources/Android/src/main/res/drawable-hdpi/plus.png": "85f2e2ab1e61d49be720fee26b418309aff124bd",
|
||||
"/mnt/82e5ff15-70b8-44a5-bb66-55688fc2381f/Freelance/Projects/WIP/EnRecipes/Developement/EnRecipes-App/platforms/android/app/src/main/assets/app/resources/Android/src/main/res/drawable-hdpi/share.png": "016114dff194ffb303294fbcaf8f6456401236fe",
|
||||
"/mnt/82e5ff15-70b8-44a5-bb66-55688fc2381f/Freelance/Projects/WIP/EnRecipes/Developement/EnRecipes-App/platforms/android/app/src/main/assets/app/resources/Android/src/main/res/drawable-ldpi/camera.png": "1fc838c8e4afcfbbc3bfd10b7211b9da8e8995af",
|
||||
"/mnt/82e5ff15-70b8-44a5-bb66-55688fc2381f/Freelance/Projects/WIP/EnRecipes/Developement/EnRecipes-App/platforms/android/app/src/main/assets/app/resources/Android/src/main/res/drawable-ldpi/detail.png": "9ab81bd03e2e7b2f9c8cfc5041f8b62d0bb67fe3",
|
||||
"/mnt/82e5ff15-70b8-44a5-bb66-55688fc2381f/Freelance/Projects/WIP/EnRecipes/Developement/EnRecipes-App/platforms/android/app/src/main/assets/app/resources/Android/src/main/res/drawable-ldpi/ic_launcher.png": "ae5dd9afe7e5cdabacb5490d9ece9df40c11b929",
|
||||
"/mnt/82e5ff15-70b8-44a5-bb66-55688fc2381f/Freelance/Projects/WIP/EnRecipes/Developement/EnRecipes-App/platforms/android/app/src/main/assets/app/resources/Android/src/main/res/drawable-ldpi/logo.png": "59db1214bf003baf49b6cdd0d7ab3969d0dfdbab",
|
||||
"/mnt/82e5ff15-70b8-44a5-bb66-55688fc2381f/Freelance/Projects/WIP/EnRecipes/Developement/EnRecipes-App/platforms/android/app/src/main/assets/app/resources/Android/src/main/res/drawable-ldpi/photo.png": "3ca5bda741d56796587353034abdf3c03e2679e7",
|
||||
"/mnt/82e5ff15-70b8-44a5-bb66-55688fc2381f/Freelance/Projects/WIP/EnRecipes/Developement/EnRecipes-App/platforms/android/app/src/main/assets/app/resources/Android/src/main/res/drawable-ldpi/plus.png": "9a4bb87916b4aa350358f46b23d88bd6503af349",
|
||||
"/mnt/82e5ff15-70b8-44a5-bb66-55688fc2381f/Freelance/Projects/WIP/EnRecipes/Developement/EnRecipes-App/platforms/android/app/src/main/assets/app/resources/Android/src/main/res/drawable-ldpi/share.png": "bf57c3eff355c51f220f6b65ae1e534774dc92ae",
|
||||
"/mnt/82e5ff15-70b8-44a5-bb66-55688fc2381f/Freelance/Projects/WIP/EnRecipes/Developement/EnRecipes-App/platforms/android/app/src/main/assets/app/resources/Android/src/main/res/drawable-mdpi/camera.png": "47a299e4cbaaa0b30897ecfdb7cec2b82ac72df5",
|
||||
"/mnt/82e5ff15-70b8-44a5-bb66-55688fc2381f/Freelance/Projects/WIP/EnRecipes/Developement/EnRecipes-App/platforms/android/app/src/main/assets/app/resources/Android/src/main/res/drawable-mdpi/detail.png": "4d914f8b5312bc96eb81eb9dc052eef07bfd52cb",
|
||||
"/mnt/82e5ff15-70b8-44a5-bb66-55688fc2381f/Freelance/Projects/WIP/EnRecipes/Developement/EnRecipes-App/platforms/android/app/src/main/assets/app/resources/Android/src/main/res/drawable-mdpi/ic_launcher.png": "c52483af0ce6eec905fbef292013d901fc2b9110",
|
||||
"/mnt/82e5ff15-70b8-44a5-bb66-55688fc2381f/Freelance/Projects/WIP/EnRecipes/Developement/EnRecipes-App/platforms/android/app/src/main/assets/app/resources/Android/src/main/res/drawable-mdpi/ic_launcher_foreground.png": "9fd235ecf6548d5ab9e1fe3224172ce8a1558c8a",
|
||||
"/mnt/82e5ff15-70b8-44a5-bb66-55688fc2381f/Freelance/Projects/WIP/EnRecipes/Developement/EnRecipes-App/platforms/android/app/src/main/assets/app/resources/Android/src/main/res/drawable-mdpi/logo.png": "944eb84979a774da3082bbaa323371f63e6945f4",
|
||||
"/mnt/82e5ff15-70b8-44a5-bb66-55688fc2381f/Freelance/Projects/WIP/EnRecipes/Developement/EnRecipes-App/platforms/android/app/src/main/assets/app/resources/Android/src/main/res/drawable-mdpi/photo.png": "9399bee456e2cc3f4f4f071d3ef46073f9d05e59",
|
||||
"/mnt/82e5ff15-70b8-44a5-bb66-55688fc2381f/Freelance/Projects/WIP/EnRecipes/Developement/EnRecipes-App/platforms/android/app/src/main/assets/app/resources/Android/src/main/res/drawable-mdpi/plus.png": "2d75915cae762fe13238921209d7a1fbc896ef31",
|
||||
"/mnt/82e5ff15-70b8-44a5-bb66-55688fc2381f/Freelance/Projects/WIP/EnRecipes/Developement/EnRecipes-App/platforms/android/app/src/main/assets/app/resources/Android/src/main/res/drawable-mdpi/share.png": "5f4976a18eeacc8509e7bd71d9cc2dae051af149",
|
||||
"/mnt/82e5ff15-70b8-44a5-bb66-55688fc2381f/Freelance/Projects/WIP/EnRecipes/Developement/EnRecipes-App/platforms/android/app/src/main/assets/app/resources/Android/src/main/res/drawable-xhdpi/camera.png": "56e67577db1d4ddd5cf78371d523559a9054c477",
|
||||
"/mnt/82e5ff15-70b8-44a5-bb66-55688fc2381f/Freelance/Projects/WIP/EnRecipes/Developement/EnRecipes-App/platforms/android/app/src/main/assets/app/resources/Android/src/main/res/drawable-xhdpi/detail.png": "ec0da35c95f4773c8c26e70647b6a974103e9944",
|
||||
"/mnt/82e5ff15-70b8-44a5-bb66-55688fc2381f/Freelance/Projects/WIP/EnRecipes/Developement/EnRecipes-App/platforms/android/app/src/main/assets/app/resources/Android/src/main/res/drawable-xhdpi/ic_launcher.png": "681a23ee3bcbb5c744cb80f224a4baedad3f0075",
|
||||
"/mnt/82e5ff15-70b8-44a5-bb66-55688fc2381f/Freelance/Projects/WIP/EnRecipes/Developement/EnRecipes-App/platforms/android/app/src/main/assets/app/resources/Android/src/main/res/drawable-xhdpi/ic_launcher_foreground.png": "58375b6039b0bf0fc65802a162d84cba8f6fb368",
|
||||
"/mnt/82e5ff15-70b8-44a5-bb66-55688fc2381f/Freelance/Projects/WIP/EnRecipes/Developement/EnRecipes-App/platforms/android/app/src/main/assets/app/resources/Android/src/main/res/drawable-xhdpi/logo.png": "d55b18eecf5018f9a3adc6ebcc24d309dbed4493",
|
||||
"/mnt/82e5ff15-70b8-44a5-bb66-55688fc2381f/Freelance/Projects/WIP/EnRecipes/Developement/EnRecipes-App/platforms/android/app/src/main/assets/app/resources/Android/src/main/res/drawable-xhdpi/photo.png": "7a9b1b430fe189e3a918743044bcb77a932b6ccf",
|
||||
"/mnt/82e5ff15-70b8-44a5-bb66-55688fc2381f/Freelance/Projects/WIP/EnRecipes/Developement/EnRecipes-App/platforms/android/app/src/main/assets/app/resources/Android/src/main/res/drawable-xhdpi/plus.png": "7d34efbfa76ce57eb39717c8d647b653a0e94790",
|
||||
"/mnt/82e5ff15-70b8-44a5-bb66-55688fc2381f/Freelance/Projects/WIP/EnRecipes/Developement/EnRecipes-App/platforms/android/app/src/main/assets/app/resources/Android/src/main/res/drawable-xhdpi/share.png": "aa8db89c92d31454c73ef9703f6e4e86e713cf14",
|
||||
"/mnt/82e5ff15-70b8-44a5-bb66-55688fc2381f/Freelance/Projects/WIP/EnRecipes/Developement/EnRecipes-App/platforms/android/app/src/main/assets/app/resources/Android/src/main/res/drawable-xxhdpi/camera.png": "b1957b0ed4e5e87a5f6ace6206f7275f9018a43e",
|
||||
"/mnt/82e5ff15-70b8-44a5-bb66-55688fc2381f/Freelance/Projects/WIP/EnRecipes/Developement/EnRecipes-App/platforms/android/app/src/main/assets/app/resources/Android/src/main/res/drawable-xxhdpi/detail.png": "b6ba2ab8f9ca73192121cfa43c04fd9949eea1d1",
|
||||
"/mnt/82e5ff15-70b8-44a5-bb66-55688fc2381f/Freelance/Projects/WIP/EnRecipes/Developement/EnRecipes-App/platforms/android/app/src/main/assets/app/resources/Android/src/main/res/drawable-xxhdpi/ic_launcher.png": "2e42d3e335309a0b6c625927bdc2c3a44e51db15",
|
||||
"/mnt/82e5ff15-70b8-44a5-bb66-55688fc2381f/Freelance/Projects/WIP/EnRecipes/Developement/EnRecipes-App/platforms/android/app/src/main/assets/app/resources/Android/src/main/res/drawable-xxhdpi/ic_launcher_foreground.png": "9e04a41c390fe7cce4bd27aacbfcffa7e569e0aa",
|
||||
"/mnt/82e5ff15-70b8-44a5-bb66-55688fc2381f/Freelance/Projects/WIP/EnRecipes/Developement/EnRecipes-App/platforms/android/app/src/main/assets/app/resources/Android/src/main/res/drawable-xxhdpi/logo.png": "6ef92b82e503374659d0235651cf1336dbb7ecd4",
|
||||
"/mnt/82e5ff15-70b8-44a5-bb66-55688fc2381f/Freelance/Projects/WIP/EnRecipes/Developement/EnRecipes-App/platforms/android/app/src/main/assets/app/resources/Android/src/main/res/drawable-xxhdpi/photo.png": "7b18cd100f4257093462b37a66dd668af79ffa6e",
|
||||
"/mnt/82e5ff15-70b8-44a5-bb66-55688fc2381f/Freelance/Projects/WIP/EnRecipes/Developement/EnRecipes-App/platforms/android/app/src/main/assets/app/resources/Android/src/main/res/drawable-xxhdpi/plus.png": "76176fc03af105bd08cdce12f6215b873242c1b5",
|
||||
"/mnt/82e5ff15-70b8-44a5-bb66-55688fc2381f/Freelance/Projects/WIP/EnRecipes/Developement/EnRecipes-App/platforms/android/app/src/main/assets/app/resources/Android/src/main/res/drawable-xxhdpi/share.png": "c93bd7ad2384dc64e9bb1f91c0c75339fa57c046",
|
||||
"/mnt/82e5ff15-70b8-44a5-bb66-55688fc2381f/Freelance/Projects/WIP/EnRecipes/Developement/EnRecipes-App/platforms/android/app/src/main/assets/app/resources/Android/src/main/res/drawable-xxxhdpi/camera.png": "ded407b62061adf527a977dc02d81ce9b8f8d99c",
|
||||
"/mnt/82e5ff15-70b8-44a5-bb66-55688fc2381f/Freelance/Projects/WIP/EnRecipes/Developement/EnRecipes-App/platforms/android/app/src/main/assets/app/resources/Android/src/main/res/drawable-xxxhdpi/detail.png": "5f8269086112fef82cd870d7280390e554d21c8d",
|
||||
"/mnt/82e5ff15-70b8-44a5-bb66-55688fc2381f/Freelance/Projects/WIP/EnRecipes/Developement/EnRecipes-App/platforms/android/app/src/main/assets/app/resources/Android/src/main/res/drawable-xxxhdpi/ic_launcher.png": "21525ef0646e7613f63b1b0ffe5cf7a8e7eac25b",
|
||||
"/mnt/82e5ff15-70b8-44a5-bb66-55688fc2381f/Freelance/Projects/WIP/EnRecipes/Developement/EnRecipes-App/platforms/android/app/src/main/assets/app/resources/Android/src/main/res/drawable-xxxhdpi/ic_launcher_foreground.png": "81b06e543dd83ca98b6848dabf1bf6ad81be64f5",
|
||||
"/mnt/82e5ff15-70b8-44a5-bb66-55688fc2381f/Freelance/Projects/WIP/EnRecipes/Developement/EnRecipes-App/platforms/android/app/src/main/assets/app/resources/Android/src/main/res/drawable-xxxhdpi/logo.png": "680357c1903bd7f4d7e7dd7f76a3a742a4c966ab",
|
||||
"/mnt/82e5ff15-70b8-44a5-bb66-55688fc2381f/Freelance/Projects/WIP/EnRecipes/Developement/EnRecipes-App/platforms/android/app/src/main/assets/app/resources/Android/src/main/res/drawable-xxxhdpi/photo.png": "6500603c21d7cc1738db44ba26157ab47b621747",
|
||||
"/mnt/82e5ff15-70b8-44a5-bb66-55688fc2381f/Freelance/Projects/WIP/EnRecipes/Developement/EnRecipes-App/platforms/android/app/src/main/assets/app/resources/Android/src/main/res/drawable-xxxhdpi/plus.png": "02eab956eabbe68bb27973582c569b822f130d2a",
|
||||
"/mnt/82e5ff15-70b8-44a5-bb66-55688fc2381f/Freelance/Projects/WIP/EnRecipes/Developement/EnRecipes-App/platforms/android/app/src/main/assets/app/resources/Android/src/main/res/drawable-xxxhdpi/share.png": "ebe8381c0e77d40abd5f41daf94532248caaa9dc",
|
||||
"/mnt/82e5ff15-70b8-44a5-bb66-55688fc2381f/Freelance/Projects/WIP/EnRecipes/Developement/EnRecipes-App/platforms/android/app/src/main/assets/app/runtime.js": "e46a5b846bc23d10ebce68cde16be7cd70e102f4",
|
||||
"/mnt/82e5ff15-70b8-44a5-bb66-55688fc2381f/Freelance/Projects/WIP/EnRecipes/Developement/EnRecipes-App/platforms/android/app/src/main/assets/app/fonts/boxicons.ttf": "6c9944326ed3c901afcb78a9ed07906f04f9cbb2",
|
||||
"/mnt/82e5ff15-70b8-44a5-bb66-55688fc2381f/Freelance/Projects/WIP/EnRecipes/Developement/EnRecipes-App/platforms/android/app/src/main/assets/app/bundle.js": "3fbfd0ad9e7ca4368f83a8f1900ecb003c11948a",
|
||||
"/mnt/82e5ff15-70b8-44a5-bb66-55688fc2381f/Freelance/Projects/WIP/EnRecipes/Developement/EnRecipes-App/platforms/android/app/src/main/assets/app/vendor.js": "cfe29102f0c4d6f7ff8d95a4194ca064f5444a3a"
|
||||
}
|
|
@ -1,48 +0,0 @@
|
|||
import groovy.json.JsonBuilder
|
||||
|
||||
import java.nio.charset.StandardCharsets
|
||||
import java.nio.file.Files
|
||||
import java.nio.file.Paths
|
||||
import java.nio.file.Path
|
||||
|
||||
class AnalyticsCollector{
|
||||
|
||||
private final String analyticsFilePath
|
||||
private boolean hasUseKotlinPropertyInApp = false
|
||||
private boolean hasKotlinRuntimeClasses = false
|
||||
|
||||
private AnalyticsCollector(String analyticsFilePath){
|
||||
this.analyticsFilePath = analyticsFilePath
|
||||
}
|
||||
|
||||
static AnalyticsCollector withOutputPath(String analyticsFilePath){
|
||||
return new AnalyticsCollector(analyticsFilePath)
|
||||
}
|
||||
|
||||
void markUseKotlinPropertyInApp(boolean useKotlin) {
|
||||
hasUseKotlinPropertyInApp = useKotlin
|
||||
}
|
||||
|
||||
void writeAnalyticsFile() {
|
||||
def jsonBuilder = new JsonBuilder()
|
||||
def kotlinUsageData = new Object()
|
||||
kotlinUsageData.metaClass.hasUseKotlinPropertyInApp = hasUseKotlinPropertyInApp
|
||||
kotlinUsageData.metaClass.hasKotlinRuntimeClasses = hasKotlinRuntimeClasses
|
||||
jsonBuilder(kotlinUsage: kotlinUsageData)
|
||||
def prettyJson = jsonBuilder.toPrettyString()
|
||||
|
||||
|
||||
|
||||
Path statisticsFilePath = Paths.get(analyticsFilePath)
|
||||
|
||||
if (Files.notExists(statisticsFilePath)) {
|
||||
Files.createDirectories(statisticsFilePath.getParent())
|
||||
Files.createFile(statisticsFilePath)
|
||||
}
|
||||
|
||||
Files.write(statisticsFilePath, prettyJson.getBytes(StandardCharsets.UTF_8))
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
ext.AnalyticsCollector = AnalyticsCollector
|
|
@ -1,50 +0,0 @@
|
|||
import static org.gradle.internal.logging.text.StyledTextOutput.Style
|
||||
|
||||
class BuildToolTask extends JavaExec {
|
||||
void setOutputs(def logger) {
|
||||
def logFile = new File("$workingDir/${name}.log")
|
||||
if(logFile.exists()) {
|
||||
logFile.delete()
|
||||
}
|
||||
standardOutput new FileOutputStream(logFile)
|
||||
errorOutput new FailureOutputStream(logger, logFile)
|
||||
}
|
||||
}
|
||||
|
||||
class FailureOutputStream extends OutputStream {
|
||||
private logger
|
||||
private File logFile
|
||||
private currentLine = ""
|
||||
private firstWrite = true
|
||||
FailureOutputStream(inLogger, inLogFile) {
|
||||
logger = inLogger
|
||||
logFile = inLogFile
|
||||
}
|
||||
|
||||
@Override
|
||||
void write(int i) throws IOException {
|
||||
if(firstWrite) {
|
||||
println ""
|
||||
firstWrite = false
|
||||
}
|
||||
currentLine += String.valueOf((char) i)
|
||||
}
|
||||
|
||||
@Override
|
||||
void flush() {
|
||||
if(currentLine?.trim()) {
|
||||
logger.withStyle(Style.Failure).println currentLine.trim()
|
||||
currentLine = ""
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
void close() {
|
||||
if(!firstWrite && logFile.exists()) {
|
||||
logger.withStyle(Style.Info).println "Detailed log here: ${logFile.getAbsolutePath()}\n"
|
||||
}
|
||||
super.close()
|
||||
}
|
||||
}
|
||||
|
||||
ext.BuildToolTask = BuildToolTask
|
|
@ -1,52 +0,0 @@
|
|||
import org.gradle.internal.logging.text.StyledTextOutputFactory
|
||||
|
||||
import static org.gradle.internal.logging.text.StyledTextOutput.Style
|
||||
def outLogger = services.get(StyledTextOutputFactory).create("colouredOutputLogger")
|
||||
|
||||
class CustomExecutionLogger extends BuildAdapter implements TaskExecutionListener {
|
||||
private logger
|
||||
private failedTask
|
||||
|
||||
CustomExecutionLogger(passedLogger) {
|
||||
logger = passedLogger
|
||||
}
|
||||
|
||||
void buildStarted(Gradle gradle) {
|
||||
failedTask = null
|
||||
}
|
||||
|
||||
void beforeExecute(Task task) {
|
||||
}
|
||||
|
||||
void afterExecute(Task task, TaskState state) {
|
||||
def failure = state.getFailure()
|
||||
if(failure) {
|
||||
failedTask = task
|
||||
}
|
||||
}
|
||||
|
||||
void buildFinished(BuildResult result) {
|
||||
def failure = result.getFailure()
|
||||
if(failure) {
|
||||
if(failedTask && (failedTask.getClass().getName().contains("BuildToolTask"))) {
|
||||
// the error from this task is already logged
|
||||
return
|
||||
}
|
||||
|
||||
println ""
|
||||
logger.withStyle(Style.FailureHeader).println failure.getMessage()
|
||||
|
||||
def causeException = failure.getCause()
|
||||
while (causeException != null) {
|
||||
failure = causeException
|
||||
causeException = failure.getCause()
|
||||
}
|
||||
if(failure != causeException) {
|
||||
logger.withStyle(Style.Failure).println failure.getMessage()
|
||||
}
|
||||
println ""
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
gradle.useLogger(new CustomExecutionLogger(outLogger))
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -1,426 +0,0 @@
|
|||
package com.tns;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.io.PrintStream;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
|
||||
import android.Manifest;
|
||||
import android.app.Activity;
|
||||
import android.app.PendingIntent;
|
||||
import android.app.PendingIntent.CanceledException;
|
||||
import android.content.ClipData;
|
||||
import android.content.ClipboardManager;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.os.Environment;
|
||||
import com.google.android.material.tabs.TabLayout;
|
||||
import androidx.core.app.ActivityCompat;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.fragment.app.FragmentManager;
|
||||
import androidx.fragment.app.FragmentStatePagerAdapter;
|
||||
import androidx.viewpager.widget.ViewPager;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.appcompat.widget.Toolbar;
|
||||
import android.text.method.ScrollingMovementMethod;
|
||||
import android.util.Log;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.Button;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
|
||||
class ErrorReport implements TabLayout.OnTabSelectedListener {
|
||||
public static final String ERROR_FILE_NAME = "hasError";
|
||||
private static AppCompatActivity activity;
|
||||
|
||||
private TabLayout tabLayout;
|
||||
private ViewPager viewPager;
|
||||
private Context context;
|
||||
|
||||
private static String exceptionMsg;
|
||||
private static String logcatMsg;
|
||||
|
||||
private static boolean checkingForPermissions = false;
|
||||
|
||||
private final static String EXTRA_NATIVESCRIPT_ERROR_REPORT = "NativeScriptErrorMessage";
|
||||
private final static String EXTRA_ERROR_REPORT_MSG = "msg";
|
||||
private final static String EXTRA_PID = "pID";
|
||||
private final static int EXTRA_ERROR_REPORT_VALUE = 1;
|
||||
|
||||
private static final int REQUEST_EXTERNAL_STORAGE = 1;
|
||||
private static String[] PERMISSIONS_STORAGE = {
|
||||
Manifest.permission.READ_EXTERNAL_STORAGE,
|
||||
Manifest.permission.WRITE_EXTERNAL_STORAGE
|
||||
};
|
||||
|
||||
// Will prevent error activity from killing process if permission request dialog pops up
|
||||
public static boolean isCheckingForPermissions() {
|
||||
return checkingForPermissions;
|
||||
}
|
||||
|
||||
public static void resetCheckingForPermissions() {
|
||||
checkingForPermissions = false;
|
||||
}
|
||||
|
||||
// The following will not compile if uncommented with compileSdk lower than 23
|
||||
public static void verifyStoragePermissions(Activity activity) {
|
||||
// Check if we have write permission
|
||||
final int version = Build.VERSION.SDK_INT;
|
||||
if (version >= 23) {
|
||||
try {
|
||||
// Necessary to work around compile errors with compileSdk 22 and lower
|
||||
Method checkSelfPermissionMethod;
|
||||
try {
|
||||
checkSelfPermissionMethod = ActivityCompat.class.getMethod("checkSelfPermission", Context.class, String.class);
|
||||
} catch (NoSuchMethodException e) {
|
||||
// method wasn't found, so there is no need to handle permissions explicitly
|
||||
if (Util.isDebuggableApp(activity)) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
int permission = (int) checkSelfPermissionMethod.invoke(null, activity, Manifest.permission.WRITE_EXTERNAL_STORAGE);
|
||||
|
||||
if (permission != PackageManager.PERMISSION_GRANTED) {
|
||||
// We don't have permission so prompt the user
|
||||
Method requestPermissionsMethod = ActivityCompat.class.getMethod("requestPermissions", Activity.class, PERMISSIONS_STORAGE.getClass(), int.class);
|
||||
|
||||
checkingForPermissions = true;
|
||||
|
||||
requestPermissionsMethod.invoke(null, activity, PERMISSIONS_STORAGE, REQUEST_EXTERNAL_STORAGE);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Toast.makeText(activity, "Couldn't resolve permissions", Toast.LENGTH_LONG).show();
|
||||
if (Util.isDebuggableApp(activity)) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public ErrorReport(AppCompatActivity activity) {
|
||||
ErrorReport.activity = activity;
|
||||
this.context = activity.getApplicationContext();
|
||||
}
|
||||
|
||||
static boolean startActivity(final Context context, String errorMessage) {
|
||||
final Intent intent = getIntent(context);
|
||||
if (intent == null) {
|
||||
return false; // (if in release mode) don't do anything
|
||||
}
|
||||
|
||||
intent.putExtra(EXTRA_ERROR_REPORT_MSG, errorMessage);
|
||||
|
||||
String PID = Integer.toString(android.os.Process.myPid());
|
||||
intent.putExtra(EXTRA_PID, PID);
|
||||
|
||||
createErrorFile(context);
|
||||
|
||||
try {
|
||||
startPendingErrorActivity(context, intent);
|
||||
} catch (CanceledException e) {
|
||||
Log.d("ErrorReport", "Couldn't send pending intent! Exception: " + e.getMessage());
|
||||
}
|
||||
|
||||
killProcess(context);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void killProcess(Context context) {
|
||||
// finish current activity and all below it first
|
||||
if (context instanceof Activity) {
|
||||
((Activity) context).finishAffinity();
|
||||
}
|
||||
|
||||
// kill process
|
||||
android.os.Process.killProcess(android.os.Process.myPid());
|
||||
}
|
||||
|
||||
static void startPendingErrorActivity(Context context, Intent intent) throws CanceledException {
|
||||
PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT);
|
||||
|
||||
pendingIntent.send(context, 0, intent);
|
||||
}
|
||||
|
||||
static String getErrorMessage(Throwable ex) {
|
||||
String content;
|
||||
PrintStream ps = null;
|
||||
|
||||
try {
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
ps = new PrintStream(baos);
|
||||
ex.printStackTrace(ps);
|
||||
|
||||
try {
|
||||
content = baos.toString("UTF-8");
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
content = e.getMessage();
|
||||
}
|
||||
} finally {
|
||||
if (ps != null) {
|
||||
ps.close();
|
||||
}
|
||||
}
|
||||
|
||||
return content;
|
||||
}
|
||||
|
||||
/*
|
||||
* Gets the process Id of the running app and filters all
|
||||
* output that doesn't belong to that process
|
||||
* */
|
||||
public static String getLogcat(String pId) {
|
||||
String content;
|
||||
|
||||
try {
|
||||
String logcatCommand = "logcat -d";
|
||||
Process process = java.lang.Runtime.getRuntime().exec(logcatCommand);
|
||||
|
||||
BufferedReader bufferedReader = new BufferedReader(
|
||||
new InputStreamReader(process.getInputStream()));
|
||||
|
||||
StringBuilder log = new StringBuilder();
|
||||
String line = "";
|
||||
String lineSeparator = System.getProperty("line.separator");
|
||||
while ((line = bufferedReader.readLine()) != null) {
|
||||
if (line.contains(pId)) {
|
||||
log.append(line);
|
||||
log.append(lineSeparator);
|
||||
}
|
||||
}
|
||||
|
||||
content = log.toString();
|
||||
} catch (IOException e) {
|
||||
content = "Failed to read logcat";
|
||||
Log.e("TNS.Android", content);
|
||||
}
|
||||
|
||||
return content;
|
||||
}
|
||||
|
||||
static Intent getIntent(Context context) {
|
||||
Class<?> errorActivityClass;
|
||||
|
||||
if (Util.isDebuggableApp(context)) {
|
||||
errorActivityClass = ErrorReportActivity.class;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
|
||||
Intent intent = new Intent(context, errorActivityClass);
|
||||
|
||||
intent.putExtra(EXTRA_NATIVESCRIPT_ERROR_REPORT, EXTRA_ERROR_REPORT_VALUE);
|
||||
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
|
||||
return intent;
|
||||
}
|
||||
|
||||
static boolean hasIntent(Intent intent) {
|
||||
int value = intent.getIntExtra(EXTRA_NATIVESCRIPT_ERROR_REPORT, 0);
|
||||
|
||||
return value == EXTRA_ERROR_REPORT_VALUE;
|
||||
}
|
||||
|
||||
void buildUI() {
|
||||
Intent intent = activity.getIntent();
|
||||
|
||||
exceptionMsg = intent.getStringExtra(EXTRA_ERROR_REPORT_MSG);
|
||||
|
||||
String processId = intent.getStringExtra(EXTRA_PID);
|
||||
logcatMsg = getLogcat(processId);
|
||||
|
||||
int errActivityId = this.context.getResources().getIdentifier("error_activity", "layout", this.context.getPackageName());
|
||||
|
||||
activity.setContentView(errActivityId);
|
||||
|
||||
int toolBarId = this.context.getResources().getIdentifier("toolbar", "id", this.context.getPackageName());
|
||||
|
||||
Toolbar toolbar = (Toolbar) activity.findViewById(toolBarId);
|
||||
activity.setSupportActionBar(toolbar);
|
||||
|
||||
final int tabLayoutId = this.context.getResources().getIdentifier("tabLayout", "id", this.context.getPackageName());
|
||||
|
||||
tabLayout = (TabLayout) activity.findViewById(tabLayoutId);
|
||||
tabLayout.addTab(tabLayout.newTab().setText("Exception"));
|
||||
tabLayout.addTab(tabLayout.newTab().setText("Logcat"));
|
||||
tabLayout.setTabGravity(TabLayout.GRAVITY_FILL);
|
||||
int pagerId = this.context.getResources().getIdentifier("pager", "id", this.context.getPackageName());
|
||||
|
||||
viewPager = (ViewPager) activity.findViewById(pagerId);
|
||||
|
||||
Pager adapter = new Pager(activity.getSupportFragmentManager(), tabLayout.getTabCount());
|
||||
|
||||
viewPager.setAdapter(adapter);
|
||||
viewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
|
||||
@Override
|
||||
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPageSelected(int position) {
|
||||
tabLayout.getTabAt(position).select();
|
||||
viewPager.setCurrentItem(position);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPageScrollStateChanged(int state) {
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
this.addOnTabSelectedListener(tabLayout);
|
||||
}
|
||||
|
||||
private void addOnTabSelectedListener(TabLayout tabLayout) {
|
||||
tabLayout.addOnTabSelectedListener(this);
|
||||
}
|
||||
private static void createErrorFile(final Context context) {
|
||||
try {
|
||||
File errFile = new File(context.getFilesDir(), ERROR_FILE_NAME);
|
||||
errFile.createNewFile();
|
||||
} catch (IOException e) {
|
||||
Log.d("ErrorReport", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTabSelected(TabLayout.Tab tab) {
|
||||
viewPager.setCurrentItem(tab.getPosition());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTabUnselected(TabLayout.Tab tab) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTabReselected(TabLayout.Tab tab) {
|
||||
viewPager.setCurrentItem(tab.getPosition());
|
||||
}
|
||||
|
||||
private class Pager extends FragmentStatePagerAdapter {
|
||||
|
||||
int tabCount;
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
public Pager(FragmentManager fm, int tabCount) {
|
||||
super(fm);
|
||||
this.tabCount = tabCount;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Fragment getItem(int position) {
|
||||
switch (position) {
|
||||
case 0:
|
||||
return new ExceptionTab();
|
||||
case 1:
|
||||
return new LogcatTab();
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCount() {
|
||||
return tabCount;
|
||||
}
|
||||
}
|
||||
|
||||
public static class ExceptionTab extends Fragment {
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||
int exceptionTabId = container.getContext().getResources().getIdentifier("exception_tab", "layout", container.getContext().getPackageName());
|
||||
View view = inflater.inflate(exceptionTabId, container, false);
|
||||
|
||||
int txtViewId = container.getContext().getResources().getIdentifier("txtErrorMsg", "id", container.getContext().getPackageName());
|
||||
TextView txtErrorMsg = (TextView) view.findViewById(txtViewId);
|
||||
txtErrorMsg.setText(exceptionMsg);
|
||||
txtErrorMsg.setMovementMethod(new ScrollingMovementMethod());
|
||||
|
||||
int btnCopyExceptionId = container.getContext().getResources().getIdentifier("btnCopyException", "id", container.getContext().getPackageName());
|
||||
Button copyToClipboard = (Button) view.findViewById(btnCopyExceptionId);
|
||||
copyToClipboard.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
ClipboardManager clipboard = (ClipboardManager) activity.getSystemService(Context.CLIPBOARD_SERVICE);
|
||||
ClipData clip = ClipData.newPlainText("nsError", exceptionMsg);
|
||||
clipboard.setPrimaryClip(clip);
|
||||
}
|
||||
});
|
||||
|
||||
return view;
|
||||
}
|
||||
}
|
||||
|
||||
public static class LogcatTab extends Fragment {
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||
int logcatTabId = container.getContext().getResources().getIdentifier("logcat_tab", "layout", container.getContext().getPackageName());
|
||||
View view = inflater.inflate(logcatTabId, container, false);
|
||||
|
||||
int textViewId = container.getContext().getResources().getIdentifier("logcatMsg", "id", container.getContext().getPackageName());
|
||||
TextView txtlogcatMsg = (TextView) view.findViewById(textViewId);
|
||||
txtlogcatMsg.setText(logcatMsg);
|
||||
|
||||
txtlogcatMsg.setMovementMethod(new ScrollingMovementMethod());
|
||||
|
||||
final String logName = "Log-" + new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss").format(new Date());
|
||||
|
||||
int btnCopyLogcatId = container.getContext().getResources().getIdentifier("btnCopyLogcat", "id", container.getContext().getPackageName());
|
||||
Button copyToClipboard = (Button) view.findViewById(btnCopyLogcatId);
|
||||
copyToClipboard.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
verifyStoragePermissions(activity);
|
||||
|
||||
if (!isCheckingForPermissions()) {
|
||||
try {
|
||||
File dir = new File(Environment.getExternalStorageDirectory().getPath() + "/logcat-reports/");
|
||||
dir.mkdirs();
|
||||
|
||||
File logcatReportFile = new File(dir, logName);
|
||||
FileOutputStream stream = new FileOutputStream(logcatReportFile);
|
||||
OutputStreamWriter writer = new OutputStreamWriter(stream, "UTF-8");
|
||||
writer.write(logcatMsg);
|
||||
writer.close();
|
||||
|
||||
String logPath = dir.getPath() + "/" + logName;
|
||||
|
||||
ClipboardManager clipboard = (ClipboardManager) activity.getSystemService(Context.CLIPBOARD_SERVICE);
|
||||
ClipData clip = ClipData.newPlainText("logPath", logPath);
|
||||
clipboard.setPrimaryClip(clip);
|
||||
|
||||
Toast.makeText(activity, "Path copied to clipboard: " + logPath, Toast.LENGTH_LONG).show();
|
||||
} catch (Exception e) {
|
||||
String err = "Could not write logcat report to sdcard. Make sure you have allowed access to external storage!";
|
||||
Toast.makeText(activity, err, Toast.LENGTH_LONG).show();
|
||||
if (Util.isDebuggableApp(container.getContext())) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return view;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,51 +0,0 @@
|
|||
package com.tns;
|
||||
|
||||
import android.app.Application;
|
||||
import android.os.Bundle;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import android.widget.Toast;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import static com.tns.ErrorReport.isCheckingForPermissions;
|
||||
import static com.tns.ErrorReport.resetCheckingForPermissions;
|
||||
|
||||
public class ErrorReportActivity extends AppCompatActivity {
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
setTheme(androidx.appcompat.R.style.Theme_AppCompat_NoActionBar);
|
||||
|
||||
super.onCreate(savedInstanceState);
|
||||
Application app = this.getApplication();
|
||||
Logger logger = new LogcatLogger(app);
|
||||
|
||||
RuntimeHelper.initLiveSync(null, logger, app);
|
||||
|
||||
new ErrorReport(this).buildUI();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onUserLeaveHint() {
|
||||
super.onUserLeaveHint();
|
||||
|
||||
if (!isCheckingForPermissions()) {
|
||||
ErrorReport.killProcess(this);
|
||||
}
|
||||
}
|
||||
|
||||
// @Override
|
||||
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
|
||||
try {
|
||||
Method onRequestPermissionsResultMethod = AppCompatActivity.class.getMethod("onRequestPermissionsResult", int.class, permissions.getClass(), grantResults.getClass());
|
||||
onRequestPermissionsResultMethod.invoke(new AppCompatActivity() /* never do this */, requestCode, permissions, grantResults);
|
||||
|
||||
resetCheckingForPermissions();
|
||||
} catch (Exception e) {
|
||||
if (Util.isDebuggableApp(this)) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
Toast.makeText(this, "Couldn't resolve permissions", Toast.LENGTH_LONG).show();
|
||||
resetCheckingForPermissions();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,346 +0,0 @@
|
|||
package com.tns;
|
||||
|
||||
import java.io.Closeable;
|
||||
import java.io.DataInputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileFilter;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.PackageManager.NameNotFoundException;
|
||||
import android.net.LocalServerSocket;
|
||||
import android.net.LocalSocket;
|
||||
import android.util.Log;
|
||||
|
||||
public class NativeScriptSyncService {
|
||||
private static String SYNC_ROOT_SOURCE_DIR = "/data/local/tmp/";
|
||||
private static final String SYNC_SOURCE_DIR = "/sync/";
|
||||
private static final String FULL_SYNC_SOURCE_DIR = "/fullsync/";
|
||||
private static final String REMOVED_SYNC_SOURCE_DIR = "/removedsync/";
|
||||
|
||||
private final Runtime runtime;
|
||||
private static Logger logger;
|
||||
private final Context context;
|
||||
|
||||
private final String syncPath;
|
||||
private final String fullSyncPath;
|
||||
private final String removedSyncPath;
|
||||
private final File fullSyncDir;
|
||||
private final File syncDir;
|
||||
private final File removedSyncDir;
|
||||
|
||||
private LocalServerSocketThread localServerThread;
|
||||
private Thread localServerJavaThread;
|
||||
|
||||
public NativeScriptSyncService(Runtime runtime, Logger logger, Context context) {
|
||||
this.runtime = runtime;
|
||||
NativeScriptSyncService.logger = logger;
|
||||
this.context = context;
|
||||
|
||||
syncPath = SYNC_ROOT_SOURCE_DIR + context.getPackageName() + SYNC_SOURCE_DIR;
|
||||
fullSyncPath = SYNC_ROOT_SOURCE_DIR + context.getPackageName() + FULL_SYNC_SOURCE_DIR;
|
||||
removedSyncPath = SYNC_ROOT_SOURCE_DIR + context.getPackageName() + REMOVED_SYNC_SOURCE_DIR;
|
||||
fullSyncDir = new File(fullSyncPath);
|
||||
syncDir = new File(syncPath);
|
||||
removedSyncDir = new File(removedSyncPath);
|
||||
}
|
||||
|
||||
public void sync() {
|
||||
if (logger != null && logger.isEnabled()) {
|
||||
logger.write("Sync is enabled:");
|
||||
logger.write("Sync path : " + syncPath);
|
||||
logger.write("Full sync path : " + fullSyncPath);
|
||||
logger.write("Removed files sync path: " + removedSyncPath);
|
||||
}
|
||||
|
||||
if (fullSyncDir.exists()) {
|
||||
executeFullSync(context, fullSyncDir);
|
||||
return;
|
||||
}
|
||||
|
||||
if (syncDir.exists()) {
|
||||
executePartialSync(context, syncDir);
|
||||
}
|
||||
|
||||
if (removedSyncDir.exists()) {
|
||||
executeRemovedSync(context, removedSyncDir);
|
||||
}
|
||||
}
|
||||
|
||||
private class LocalServerSocketThread implements Runnable {
|
||||
private volatile boolean running;
|
||||
private final String name;
|
||||
|
||||
private ListenerWorker commThread;
|
||||
private LocalServerSocket serverSocket;
|
||||
|
||||
public LocalServerSocketThread(String name) {
|
||||
this.name = name;
|
||||
this.running = false;
|
||||
}
|
||||
|
||||
public void stop() {
|
||||
this.running = false;
|
||||
try {
|
||||
serverSocket.close();
|
||||
} catch (IOException e) {
|
||||
if (com.tns.Runtime.isDebuggable()) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void run() {
|
||||
running = true;
|
||||
try {
|
||||
serverSocket = new LocalServerSocket(this.name);
|
||||
while (running) {
|
||||
LocalSocket socket = serverSocket.accept();
|
||||
commThread = new ListenerWorker(socket);
|
||||
new Thread(commThread).start();
|
||||
}
|
||||
} catch (IOException e) {
|
||||
if (com.tns.Runtime.isDebuggable()) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class ListenerWorker implements Runnable {
|
||||
private final DataInputStream input;
|
||||
private Closeable socket;
|
||||
private OutputStream output;
|
||||
|
||||
public ListenerWorker(LocalSocket socket) throws IOException {
|
||||
this.socket = socket;
|
||||
input = new DataInputStream(socket.getInputStream());
|
||||
output = socket.getOutputStream();
|
||||
}
|
||||
|
||||
public void run() {
|
||||
try {
|
||||
int length = input.readInt();
|
||||
input.readFully(new byte[length]); // ignore the payload
|
||||
executePartialSync(context, syncDir);
|
||||
executeRemovedSync(context, removedSyncDir);
|
||||
|
||||
runtime.runScript(new File(NativeScriptSyncService.this.context.getFilesDir(), "internal/livesync.js"));
|
||||
try {
|
||||
output.write(1);
|
||||
} catch (IOException e) {
|
||||
if (com.tns.Runtime.isDebuggable()) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
socket.close();
|
||||
} catch (IOException e) {
|
||||
if (com.tns.Runtime.isDebuggable()) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void startServer() {
|
||||
localServerThread = new LocalServerSocketThread(context.getPackageName() + "-livesync");
|
||||
localServerJavaThread = new Thread(localServerThread);
|
||||
localServerJavaThread.start();
|
||||
}
|
||||
|
||||
public static boolean isSyncEnabled(Context context) {
|
||||
int flags;
|
||||
boolean shouldExecuteSync = false;
|
||||
try {
|
||||
flags = context.getPackageManager().getPackageInfo(context.getPackageName(), 0).applicationInfo.flags;
|
||||
shouldExecuteSync = ((flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0);
|
||||
} catch (NameNotFoundException e) {
|
||||
if (com.tns.Runtime.isDebuggable()) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
return shouldExecuteSync;
|
||||
}
|
||||
|
||||
final FileFilter deletingFilesFilter = new FileFilter() {
|
||||
@Override
|
||||
public boolean accept(File pathname) {
|
||||
if (pathname.isDirectory()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
boolean success = pathname.delete();
|
||||
if (!success) {
|
||||
logger.write("Syncing: file not deleted: " + pathname.getAbsolutePath().toString());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
private void deleteDir(File directory) {
|
||||
File[] subDirectories = directory.listFiles(deletingFilesFilter);
|
||||
if (subDirectories != null) {
|
||||
for (int i = 0; i < subDirectories.length; i++) {
|
||||
File subDir = subDirectories[i];
|
||||
deleteDir(subDir);
|
||||
}
|
||||
}
|
||||
|
||||
boolean success = directory.delete();
|
||||
if (!success && directory.exists()) {
|
||||
logger.write("Syncing: directory not deleted: " + directory.getAbsolutePath().toString());
|
||||
}
|
||||
}
|
||||
|
||||
private void moveFiles(File sourceDir, String sourceRootAbsolutePath, String targetRootAbsolutePath) {
|
||||
File[] files = sourceDir.listFiles();
|
||||
|
||||
if (files != null) {
|
||||
if (logger.isEnabled()) {
|
||||
logger.write("Syncing total number of fiiles: " + files.length);
|
||||
}
|
||||
|
||||
for (int i = 0; i < files.length; i++) {
|
||||
File file = files[i];
|
||||
if (file.isFile()) {
|
||||
if (logger.isEnabled()) {
|
||||
logger.write("Syncing: " + file.getAbsolutePath().toString());
|
||||
}
|
||||
|
||||
String targetFilePath = file.getAbsolutePath().replace(sourceRootAbsolutePath, targetRootAbsolutePath);
|
||||
File targetFileDir = new File(targetFilePath);
|
||||
|
||||
File targetParent = targetFileDir.getParentFile();
|
||||
if (targetParent != null) {
|
||||
targetParent.mkdirs();
|
||||
}
|
||||
|
||||
boolean success = copyFile(file.getAbsolutePath(), targetFilePath);
|
||||
if (!success) {
|
||||
logger.write("Sync failed: " + file.getAbsolutePath().toString());
|
||||
}
|
||||
} else {
|
||||
moveFiles(file, sourceRootAbsolutePath, targetRootAbsolutePath);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (logger.isEnabled()) {
|
||||
logger.write("Can't move files. Source is empty.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// this removes only the app directory from the device to preserve
|
||||
// any existing files in /files directory on the device
|
||||
private void executeFullSync(Context context, final File sourceDir) {
|
||||
String appPath = context.getFilesDir().getAbsolutePath() + "/app";
|
||||
final File appDir = new File(appPath);
|
||||
|
||||
if (appDir.exists()) {
|
||||
deleteDir(appDir);
|
||||
moveFiles(sourceDir, sourceDir.getAbsolutePath(), appDir.getAbsolutePath());
|
||||
}
|
||||
}
|
||||
|
||||
private void executePartialSync(Context context, File sourceDir) {
|
||||
String appPath = context.getFilesDir().getAbsolutePath() + "/app";
|
||||
final File appDir = new File(appPath);
|
||||
|
||||
if (!appDir.exists()) {
|
||||
Log.e("TNS", "Application dir does not exists. Partial Sync failed. appDir: " + appPath);
|
||||
return;
|
||||
}
|
||||
|
||||
if (logger.isEnabled()) {
|
||||
logger.write("Syncing sourceDir " + sourceDir.getAbsolutePath() + " with " + appDir.getAbsolutePath());
|
||||
}
|
||||
|
||||
moveFiles(sourceDir, sourceDir.getAbsolutePath(), appDir.getAbsolutePath());
|
||||
}
|
||||
|
||||
private void deleteRemovedFiles(File sourceDir, String sourceRootAbsolutePath, String targetRootAbsolutePath) {
|
||||
if (!sourceDir.exists()) {
|
||||
if (logger.isEnabled()) {
|
||||
logger.write("Directory does not exist: " + sourceDir.getAbsolutePath());
|
||||
}
|
||||
}
|
||||
|
||||
File[] files = sourceDir.listFiles();
|
||||
|
||||
if (files != null) {
|
||||
for (int i = 0; i < files.length; i++) {
|
||||
File file = files[i];
|
||||
String targetFilePath = file.getAbsolutePath().replace(sourceRootAbsolutePath, targetRootAbsolutePath);
|
||||
File targetFile = new File(targetFilePath);
|
||||
if (file.isFile()) {
|
||||
if (logger.isEnabled()) {
|
||||
logger.write("Syncing removed file: " + file.getAbsolutePath().toString());
|
||||
}
|
||||
|
||||
targetFile.delete();
|
||||
} else {
|
||||
deleteRemovedFiles(file, sourceRootAbsolutePath, targetRootAbsolutePath);
|
||||
|
||||
// this is done so empty folders, if any, are deleted after we're don deleting files.
|
||||
if (targetFile.listFiles().length == 0) {
|
||||
targetFile.delete();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void executeRemovedSync(final Context context, final File sourceDir) {
|
||||
String appPath = context.getFilesDir().getAbsolutePath() + "/app";
|
||||
deleteRemovedFiles(sourceDir, sourceDir.getAbsolutePath(), appPath);
|
||||
}
|
||||
|
||||
private boolean copyFile(String sourceFile, String destinationFile) {
|
||||
FileInputStream fis = null;
|
||||
FileOutputStream fos = null;
|
||||
|
||||
try {
|
||||
fis = new FileInputStream(sourceFile);
|
||||
fos = new FileOutputStream(destinationFile, false);
|
||||
|
||||
byte[] buffer = new byte[4096];
|
||||
int read = 0;
|
||||
|
||||
while ((read = fis.read(buffer)) != -1) {
|
||||
fos.write(buffer, 0, read);
|
||||
}
|
||||
} catch (FileNotFoundException e) {
|
||||
logger.write("Error copying file " + sourceFile);
|
||||
if (com.tns.Runtime.isDebuggable()) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return false;
|
||||
} catch (IOException e) {
|
||||
logger.write("Error copying file " + sourceFile);
|
||||
if (com.tns.Runtime.isDebuggable()) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return false;
|
||||
} finally {
|
||||
try {
|
||||
if (fis != null) {
|
||||
fis.close();
|
||||
}
|
||||
if (fos != null) {
|
||||
fos.close();
|
||||
}
|
||||
} catch (IOException e) {
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
|
@ -1,448 +0,0 @@
|
|||
package com.tns;
|
||||
|
||||
import android.content.Context;
|
||||
import android.net.LocalServerSocket;
|
||||
import android.net.LocalSocket;
|
||||
|
||||
import java.io.Closeable;
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.security.DigestInputStream;
|
||||
import java.security.MessageDigest;
|
||||
import java.io.OutputStream;
|
||||
import java.util.Arrays;
|
||||
|
||||
public class NativeScriptSyncServiceSocketImpl {
|
||||
private static String DEVICE_APP_DIR;
|
||||
|
||||
private final Runtime runtime;
|
||||
private static Logger logger;
|
||||
private final Context context;
|
||||
|
||||
private LocalServerSocketThread localServerThread;
|
||||
private Thread localServerJavaThread;
|
||||
|
||||
public NativeScriptSyncServiceSocketImpl(Runtime runtime, Logger logger, Context context) {
|
||||
this.runtime = runtime;
|
||||
NativeScriptSyncServiceSocketImpl.logger = logger;
|
||||
this.context = context;
|
||||
DEVICE_APP_DIR = this.context.getFilesDir().getAbsolutePath() + "/app";
|
||||
}
|
||||
|
||||
private class LocalServerSocketThread implements Runnable {
|
||||
|
||||
private volatile boolean running;
|
||||
private final String name;
|
||||
|
||||
private LiveSyncWorker livesyncWorker;
|
||||
private LocalServerSocket deviceSystemSocket;
|
||||
|
||||
public LocalServerSocketThread(String name) {
|
||||
this.name = name;
|
||||
this.running = false;
|
||||
}
|
||||
|
||||
public void stop() {
|
||||
this.running = false;
|
||||
try {
|
||||
deviceSystemSocket.close();
|
||||
} catch (IOException e) {
|
||||
if (com.tns.Runtime.isDebuggable()) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void run() {
|
||||
running = true;
|
||||
try {
|
||||
deviceSystemSocket = new LocalServerSocket(this.name);
|
||||
while (running) {
|
||||
LocalSocket systemSocket = deviceSystemSocket.accept();
|
||||
livesyncWorker = new LiveSyncWorker(systemSocket);
|
||||
Thread liveSyncThread = setUpLivesyncThread();
|
||||
liveSyncThread.start();
|
||||
}
|
||||
} catch (IOException e) {
|
||||
if (com.tns.Runtime.isDebuggable()) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
catch (java.security.NoSuchAlgorithmException e) {
|
||||
if (com.tns.Runtime.isDebuggable()) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Thread setUpLivesyncThread() {
|
||||
Thread livesyncThread = new Thread(livesyncWorker);
|
||||
livesyncThread.setUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
|
||||
@Override
|
||||
public void uncaughtException(Thread t, Throwable e) {
|
||||
logger.write(String.format("%s(%s): %s", t.getName(), t.getId(), e.toString()));
|
||||
}
|
||||
});
|
||||
livesyncThread.setName("Livesync Thread");
|
||||
return livesyncThread;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void finalize() throws Throwable {
|
||||
deviceSystemSocket.close();
|
||||
}
|
||||
}
|
||||
|
||||
public void startServer() {
|
||||
localServerThread = new LocalServerSocketThread(context.getPackageName() + "-livesync");
|
||||
localServerJavaThread = new Thread(localServerThread);
|
||||
localServerJavaThread.setName("Livesync Server Thread");
|
||||
localServerJavaThread.start();
|
||||
}
|
||||
|
||||
private class LiveSyncWorker implements Runnable {
|
||||
public static final int OPERATION_BYTE_SIZE = 1;
|
||||
public static final int HASH_BYTE_SIZE = 16;
|
||||
public static final int LENGTH_BYTE_SIZE = 1;
|
||||
public static final int OPERATION_ID_BYTE_SIZE = 32;
|
||||
public static final int DELETE_FILE_OPERATION = 7;
|
||||
public static final int CREATE_FILE_OPERATION = 8;
|
||||
public static final int DO_SYNC_OPERATION = 9;
|
||||
public static final int ERROR_REPORT_CODE = 1;
|
||||
public static final int OPERATION_END_NO_REFRESH_REPORT_CODE = 3;
|
||||
public static final int OPERATION_END_REPORT_CODE = 2;
|
||||
public static final int REPORT_CODE_SIZE = 1;
|
||||
public static final int DO_REFRESH_LENGTH = 1;
|
||||
public static final int DO_REFRESH_VALUE = 1;
|
||||
public static final String FILE_NAME = "fileName";
|
||||
public static final String FILE_NAME_LENGTH = FILE_NAME + "Length";
|
||||
public static final String OPERATION = "operation";
|
||||
public static final String FILE_CONTENT = "fileContent";
|
||||
public static final String FILE_CONTENT_LENGTH = FILE_CONTENT + "Length";
|
||||
public static final int DEFAULT_OPERATION = -1;
|
||||
private static final String PROTOCOL_VERSION = "0.2.0";
|
||||
private byte[] handshakeMessage;
|
||||
private final DigestInputStream input;
|
||||
private Closeable livesyncSocket;
|
||||
private OutputStream output;
|
||||
|
||||
public LiveSyncWorker(LocalSocket systemSocket) throws IOException, java.security.NoSuchAlgorithmException {
|
||||
this.livesyncSocket = systemSocket;
|
||||
MessageDigest md = MessageDigest.getInstance("MD5");
|
||||
input = new DigestInputStream(systemSocket.getInputStream(), md);
|
||||
output = systemSocket.getOutputStream();
|
||||
handshakeMessage = getHandshakeMessage();
|
||||
}
|
||||
|
||||
public void run() {
|
||||
try {
|
||||
output.write(handshakeMessage);
|
||||
output.flush();
|
||||
|
||||
} catch (IOException e) {
|
||||
logger.write(String.format("Error while LiveSyncing: Client socket might be closed!", e.toString()));
|
||||
if (com.tns.Runtime.isDebuggable()) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
try {
|
||||
do {
|
||||
int operation = getOperation();
|
||||
|
||||
if (operation == DELETE_FILE_OPERATION) {
|
||||
|
||||
String fileName = getFileName();
|
||||
validateData();
|
||||
deleteRecursive(new File(DEVICE_APP_DIR, fileName));
|
||||
|
||||
} else if (operation == CREATE_FILE_OPERATION) {
|
||||
|
||||
String fileName = getFileName();
|
||||
int contentLength = getFileContentLength(fileName);
|
||||
validateData();
|
||||
byte[] content = getFileContent(fileName, contentLength);
|
||||
validateData();
|
||||
createOrOverrideFile(fileName, content);
|
||||
|
||||
} else if (operation == DO_SYNC_OPERATION) {
|
||||
byte[] operationUid = readNextBytes(OPERATION_ID_BYTE_SIZE);
|
||||
byte doRefresh = readNextBytes(DO_REFRESH_LENGTH)[0];
|
||||
int doRefreshInt = (int)doRefresh;
|
||||
int operationReportCode;
|
||||
|
||||
validateData();
|
||||
if(runtime != null && doRefreshInt == DO_REFRESH_VALUE) {
|
||||
runtime.runScript(new File(NativeScriptSyncServiceSocketImpl.this.context.getFilesDir(), "internal/livesync.js"), false);
|
||||
operationReportCode = OPERATION_END_REPORT_CODE;
|
||||
} else {
|
||||
operationReportCode = OPERATION_END_NO_REFRESH_REPORT_CODE;
|
||||
}
|
||||
|
||||
output.write(getReportMessageBytes(operationReportCode, operationUid));
|
||||
output.flush();
|
||||
|
||||
} else if (operation == DEFAULT_OPERATION) {
|
||||
logger.write("LiveSync: input stream is empty!");
|
||||
break;
|
||||
} else {
|
||||
throw new IllegalArgumentException(String.format("\nLiveSync: Operation not recognised. Received operation is %s.", operation));
|
||||
}
|
||||
|
||||
} while (true);
|
||||
} catch (Exception e) {
|
||||
String message = String.format("Error while LiveSyncing: %s", e.toString());
|
||||
this.closeWithError(message);
|
||||
} catch (Throwable e) {
|
||||
String message = String.format("%s(%s): Error while LiveSyncing.\nOriginal Exception: %s", Thread.currentThread().getName(), Thread.currentThread().getId(), e.toString());
|
||||
this.closeWithError(message);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private byte[] getErrorMessageBytes(String message) {
|
||||
return this.getReportMessageBytes(ERROR_REPORT_CODE, message.getBytes());
|
||||
}
|
||||
|
||||
private byte[] getReportMessageBytes(int reportType, byte[] messageBytes) {
|
||||
byte[] reportBytes = new byte[]{(byte)reportType};
|
||||
byte[] combined = new byte[messageBytes.length + REPORT_CODE_SIZE];
|
||||
|
||||
System.arraycopy(reportBytes,0,combined, 0, REPORT_CODE_SIZE);
|
||||
System.arraycopy(messageBytes,0,combined, REPORT_CODE_SIZE, messageBytes.length);
|
||||
|
||||
return combined;
|
||||
}
|
||||
|
||||
private byte[] getHandshakeMessage() {
|
||||
byte[] protocolVersionBytes = PROTOCOL_VERSION.getBytes();
|
||||
byte[] versionLength = new byte[]{(byte)protocolVersionBytes.length};
|
||||
byte[] packageNameBytes = context.getPackageName().getBytes();
|
||||
byte[] combined = new byte[protocolVersionBytes.length + packageNameBytes.length + versionLength.length];
|
||||
|
||||
System.arraycopy(versionLength,0,combined, 0, versionLength.length);
|
||||
System.arraycopy(protocolVersionBytes,0,combined, versionLength.length, protocolVersionBytes.length);
|
||||
System.arraycopy(packageNameBytes,0,combined,protocolVersionBytes.length + versionLength.length,packageNameBytes.length);
|
||||
|
||||
return combined;
|
||||
}
|
||||
|
||||
private void validateData() throws IOException {
|
||||
MessageDigest messageDigest = input.getMessageDigest();
|
||||
byte[] digest = messageDigest.digest();
|
||||
input.on(false);
|
||||
byte[] inputMD5 = readNextBytes(HASH_BYTE_SIZE);
|
||||
input.on(true);
|
||||
|
||||
|
||||
if(!Arrays.equals(digest, inputMD5)){
|
||||
throw new IllegalStateException(String.format("\nLiveSync: Validation of data failed.\nComputed hash: %s\nOriginal hash: %s ", Arrays.toString(digest), Arrays.toString(inputMD5)));
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Tries to read operation input stream
|
||||
* If the stream is empty, method returns -1
|
||||
* */
|
||||
private int getOperation() {
|
||||
Integer operation;
|
||||
byte[] operationBuff = null;
|
||||
try {
|
||||
operationBuff = readNextBytes(OPERATION_BYTE_SIZE);
|
||||
if (operationBuff == null) {
|
||||
return DEFAULT_OPERATION;
|
||||
}
|
||||
operation = Integer.parseInt(new String(operationBuff));
|
||||
|
||||
} catch (Exception e) {
|
||||
if(operationBuff == null){
|
||||
operationBuff = new byte[]{};
|
||||
}
|
||||
throw new IllegalStateException(String.format("\nLiveSync: failed to parse %s. Bytes read: $s %s\nOriginal Exception: %s", OPERATION, Arrays.toString(operationBuff), e.toString()));
|
||||
}
|
||||
return operation;
|
||||
}
|
||||
|
||||
private String getFileName() {
|
||||
byte[] fileNameBuffer;
|
||||
int fileNameLength = -1;
|
||||
|
||||
try {
|
||||
fileNameLength = getLength();
|
||||
} catch (Exception e) {
|
||||
throw new IllegalStateException(String.format("\nLiveSync: failed to parse %s. \nOriginal Exception: %s", FILE_NAME_LENGTH, e.toString()));
|
||||
}
|
||||
|
||||
if(fileNameLength <= 0) {
|
||||
throw new IllegalStateException(String.format("\nLiveSync: File name length must be positive number or zero. Provided length: %s.", fileNameLength));
|
||||
}
|
||||
|
||||
try {
|
||||
fileNameBuffer = readNextBytes(fileNameLength);
|
||||
} catch (Exception e) {
|
||||
throw new IllegalStateException(String.format("\nLiveSync: failed to parse %s.\nOriginal Exception: %s", FILE_NAME, e.toString()));
|
||||
}
|
||||
|
||||
if (fileNameBuffer == null) {
|
||||
throw new IllegalStateException(String.format("\nLiveSync: Missing %s bytes.", FILE_NAME));
|
||||
}
|
||||
|
||||
String fileName = new String(fileNameBuffer);
|
||||
if (fileName.trim().length() < fileNameLength) {
|
||||
logger.write(String.format("WARNING: %s parsed length is less than %s. We read less information than you specified!", FILE_NAME, FILE_NAME_LENGTH));
|
||||
}
|
||||
|
||||
return fileName.trim();
|
||||
}
|
||||
|
||||
private int getFileContentLength(String fileName) throws IllegalStateException {
|
||||
int contentLength;
|
||||
|
||||
try {
|
||||
contentLength = getLength();
|
||||
} catch (Exception e) {
|
||||
throw new IllegalStateException(String.format("\nLiveSync: failed to read %s for %s.\nOriginal Exception: %s", FILE_CONTENT_LENGTH, fileName, e.toString()));
|
||||
}
|
||||
|
||||
if(contentLength < 0){
|
||||
throw new IllegalStateException(String.format("\nLiveSync: Content length must be positive number or zero. Provided content length: %s.", contentLength));
|
||||
}
|
||||
|
||||
return contentLength;
|
||||
}
|
||||
|
||||
private byte[] getFileContent(String fileName, int contentLength) throws IllegalStateException {
|
||||
byte[] contentBuff = null;
|
||||
|
||||
try {
|
||||
if(contentLength > 0) {
|
||||
contentBuff = readNextBytes(contentLength);
|
||||
} else if(contentLength == 0){
|
||||
contentBuff = new byte[]{};
|
||||
}
|
||||
} catch (Exception e) {
|
||||
throw new IllegalStateException(String.format("\nLiveSync: failed to parse content of file: %s.\nOriginal Exception: %s", fileName, e.toString()));
|
||||
}
|
||||
|
||||
if (contentLength != 0 && contentBuff == null) {
|
||||
throw new IllegalStateException(String.format("\nLiveSync: Missing %s bytes for file: %s. Did you send %s bytes?", FILE_CONTENT, fileName, contentLength));
|
||||
}
|
||||
|
||||
return contentBuff;
|
||||
}
|
||||
|
||||
private int getLength() {
|
||||
byte[] lengthBuffer;
|
||||
int lengthInt;
|
||||
|
||||
try {
|
||||
byte lengthSize = readNextBytes(LENGTH_BYTE_SIZE)[0];
|
||||
//Cast signed byte to unsigned int
|
||||
int lengthSizeInt = ((int) lengthSize) & 0xFF;
|
||||
lengthBuffer = readNextBytes(lengthSizeInt);
|
||||
|
||||
if (lengthBuffer == null) {
|
||||
throw new IllegalStateException(String.format("\nLiveSync: Missing size length bytes."));
|
||||
}
|
||||
} catch (Exception e) {
|
||||
throw new IllegalStateException(String.format("\nLiveSync: Failed to read size length. \nOriginal Exception: %s", e.toString()));
|
||||
}
|
||||
|
||||
try {
|
||||
lengthInt = Integer.valueOf(new String(lengthBuffer));
|
||||
} catch (Exception e) {
|
||||
throw new IllegalStateException(String.format("\nLiveSync: Failed to parse size length. \nOriginal Exception: %s", e.toString()));
|
||||
}
|
||||
|
||||
return lengthInt;
|
||||
}
|
||||
|
||||
private void createOrOverrideFile(String fileName, byte[] content) throws IOException {
|
||||
File fileToCreate = prepareFile(fileName);
|
||||
try {
|
||||
|
||||
fileToCreate.getParentFile().mkdirs();
|
||||
FileOutputStream fos = new FileOutputStream(fileToCreate.getCanonicalPath());
|
||||
if(runtime != null) {
|
||||
runtime.lock();
|
||||
}
|
||||
fos.write(content);
|
||||
fos.close();
|
||||
|
||||
} catch (Exception e) {
|
||||
throw new IOException(String.format("\nLiveSync: failed to write file: %s\nOriginal Exception: %s", fileName, e.toString()));
|
||||
} finally {
|
||||
if(runtime != null) {
|
||||
runtime.unlock();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void deleteRecursive(File fileOrDirectory) {
|
||||
if (fileOrDirectory.isDirectory())
|
||||
for (File child : fileOrDirectory.listFiles()) {
|
||||
deleteRecursive(child);
|
||||
}
|
||||
|
||||
fileOrDirectory.delete();
|
||||
}
|
||||
|
||||
private File prepareFile(String fileName) {
|
||||
File fileToCreate = new File(DEVICE_APP_DIR, fileName);
|
||||
if (fileToCreate.exists()) {
|
||||
fileToCreate.delete();
|
||||
}
|
||||
return fileToCreate;
|
||||
}
|
||||
|
||||
/*
|
||||
* Reads next bites from input stream. Bytes read depend on passed parameter.
|
||||
* */
|
||||
private byte[] readNextBytes(int size) throws IOException {
|
||||
byte[] buffer = new byte[size];
|
||||
int bytesRead = 0;
|
||||
int bufferWriteOffset = bytesRead;
|
||||
try {
|
||||
do {
|
||||
|
||||
bytesRead = this.input.read(buffer, bufferWriteOffset, size);
|
||||
if (bytesRead == -1) {
|
||||
if (bufferWriteOffset == 0) {
|
||||
return null;
|
||||
}
|
||||
break;
|
||||
}
|
||||
size -= bytesRead;
|
||||
bufferWriteOffset += bytesRead;
|
||||
} while (size > 0);
|
||||
} catch (IOException e) {
|
||||
String message = e.getMessage();
|
||||
if (message != null && message.equals("Try again")) {
|
||||
throw new IllegalStateException("Error while LiveSyncing: Read operation timed out.");
|
||||
} else {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
private void closeWithError(String message) {
|
||||
try {
|
||||
output.write(getErrorMessageBytes(message));
|
||||
output.flush();
|
||||
logger.write(message);
|
||||
this.livesyncSocket.close();
|
||||
} catch (IOException e) {
|
||||
if (com.tns.Runtime.isDebuggable()) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void finalize() throws Throwable {
|
||||
this.livesyncSocket.close();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,39 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
tools:context="com.tns.ErrorReportActivity"
|
||||
android:theme="@style/Widget.AppCompat.Light.ActionBar">
|
||||
|
||||
<androidx.appcompat.widget.Toolbar
|
||||
android:id="@+id/toolbar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:minHeight="?attr/actionBarSize"/>
|
||||
|
||||
<androidx.viewpager.widget.ViewPager
|
||||
android:id="@+id/pager"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="fill_parent"
|
||||
android:layout_below="@+id/toolbar"
|
||||
android:scrollbarAlwaysDrawVerticalTrack="false">
|
||||
|
||||
</androidx.viewpager.widget.ViewPager>
|
||||
|
||||
<com.google.android.material.tabs.TabLayout
|
||||
android:id="@+id/tabLayout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="?attr/colorPrimary"
|
||||
android:minHeight="?attr/actionBarSize"
|
||||
app:tabIndicatorColor="@color/nativescript_blue"
|
||||
android:layout_alignParentTop="true"
|
||||
android:layout_alignParentStart="true"
|
||||
android:scrollbarStyle="insideOverlay"
|
||||
android:scrollbars="vertical"
|
||||
tools:tabBackground="@android:color/darker_gray"
|
||||
android:scrollbarAlwaysDrawVerticalTrack="false"
|
||||
/>
|
||||
</RelativeLayout>
|
|
@ -1,52 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical"
|
||||
android:paddingBottom="10dp"
|
||||
android:paddingLeft="16dp"
|
||||
android:paddingRight="16dp"
|
||||
android:paddingTop="10dp">
|
||||
|
||||
<Button
|
||||
android:id="@+id/btnCopyException"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentBottom="true"
|
||||
android:layout_centerHorizontal="true"
|
||||
android:background="@color/nativescript_blue"
|
||||
android:paddingLeft="20dp"
|
||||
android:paddingRight="20dp"
|
||||
android:text="Copy to clipboard"
|
||||
android:textAlignment="textStart"
|
||||
android:textColor="@android:color/white"
|
||||
tools:layout_width="match_parent"/>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/linearLayout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_above="@+id/btnCopyException"
|
||||
android:layout_alignParentStart="true"
|
||||
android:layout_alignParentTop="true"
|
||||
android:orientation="vertical"
|
||||
android:paddingBottom="10dp"
|
||||
android:weightSum="100">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/txtErrorMsg"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text=""
|
||||
android:textAppearance="?android:attr/textAppearanceSmall"
|
||||
android:paddingLeft="2dp"
|
||||
android:paddingRight="2dp"
|
||||
android:scrollbars="vertical"
|
||||
android:scrollbarStyle="outsideOverlay"
|
||||
android:scrollbarAlwaysDrawVerticalTrack="true"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
|
||||
</RelativeLayout>
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue