script "revSaveAsAndroidStandalone" ################################################################################ local sJavaRoot ################################################################################ command revSaveAsMobileStandalone pStack, pApkFile, pTarget local tTargetType if pTarget contains "build" then put "android release" into tTargetType else put "android test" into tTargetType end if try put the cMobileSupport["android.jdk"] of stack "revPreferences" into sJavaRoot dispatch "savingMobileStandalone" to stack pStack with tTargetType, pApkFile end try try revSaveAsMobileStandaloneMain pStack, pApkFile, pTarget catch tError end try try if tError is not empty then put empty into pApkFile end if dispatch "mobileStandaloneSaved" to stack pStack with tTargetType, pApkFile end try if tError is not empty then throw tError end if end revSaveAsMobileStandalone private function pathIsRelative pPath if pPath is empty then throw "empty path" if pPath begins with "/" then return false if char 2 of pPath is ":" then return false return true end pathIsRelative // IM-2013-05-21: [[ BZ 10904 ]] This function replicates the functionality of the apkbuilder script which has now been removed from the Android SDK platform tools private command buildAPK pUnalignedApkFile, pUnsignedParam, pAssetArchive, pClassesFile, pLibsBuildFolder local tResult executeShellCommand pathToJava(), "-Xmx128M", "-classpath", pathToSDKClasses(), "com.android.sdklib.build.ApkBuilderMain", pUnalignedApkFile, pUnsignedParam, "-v", "-z", pAssetArchive, "-f", pClassesFile, "-nf", pLibsBuildFolder put the result into tResult return tResult end buildAPK private command revSaveAsMobileStandaloneMain pStack, pApkFile, pTarget start using stack "revSBLibrary" dispatch "revIDEDeployAndroidInitialize" to stack "revDeployLibrary" local tSettings put the customProperties["cRevStandaloneSettings"] of stack pStack into tSettings -- Compute the base folder local tBaseFolder set the itemDelimiter to slash put item 1 to -2 of the effective filename of stack pStack into tBaseFolder set the itemDelimiter to comma -- Fetch the various options we need to initially build the app-bundle local tName, tFiles, tVersionName, tVersionCode, tExternals, tIcon, tSplash, tKey, tUnsigned, tStatusBarIcon put tSettings["name"] into tName put tSettings["files"] into tFiles put tSettings["android,icon"] into tIcon put tSettings["android,key"] into tKey if tSettings["android,label"] is empty then put tName into tSettings["android,label"] end if if tSettings["android,identifier"] is empty then put "com.yourcompany.yourapp" into tSettings["android,identifier"] end if if tSettings["android,version name"] is empty then put "1.0.0" into tSettings["android,version name"] end if if tSettings["android,version code"] is empty then put "1" into tSettings["android,version code"] end if if tSettings["android,initial orientation"] is empty then put "portrait" into tSettings["android,initial orientation"] end if if tSettings["android,status bar hidden"] is empty then put "false" into tSettings["android,status bar hidden"] end if if tSettings["android,minimum version"] is empty then put 8 into tSettings["android,minimum version"] end if put tSettings["android,statusBarIcon"] into tStatusBarIcon if tStatusBarIcon is empty then // put default sb icon path into tStatusBarIcon // SN-2015-01-22: [[ Bug 13213 ]] If mapFilePath returns empty, an error will be triggered // line 629. put mapFilePath(revMobileRuntimeFolder(pTarget) & slash & "notify_icon.png") into tStatusBarIcon end if switch tSettings["android,signing"] case "sign with my key" put true into tUnsigned break case "sign for development only" put false into tUnsigned put empty into tKey break; case "do not sign" put true into tUnsigned put empty into tKey break; default put true into tUnsigned break; end switch -- Compute the icon path if tIcon is not empty and pathIsRelative(tIcon) then put tBaseFolder & slash before tIcon end if -- Compute the key path if tKey is not empty and pathIsRelative(tKey) then put tBaseFolder & slash before tKey end if -- Compute the status bar icon path if tStatusBarIcon is not empty and pathIsRelative(tStatusBarIcon) then put tBaseFolder & slash before tStatusBarIcon end if -- Compute the externals list (includes drivers on Android) repeat for each word tExternal in "revzip revxml dbsqlite dbmysql dbodbc dbpostgresql revpdfprinter" if tSettings["android,include" && tExternal] then if tExternal begins with "db" then if "revdb" is not among the lines of tExternals then put "revdb" & return after tExternals end if put tExternal & return after tExternals else put tExternal & return after tExternals end if end if end repeat delete the last char of tExternals -- Compute the stackfiles list local tStackFiles lock messages // SN-2014-10-16: [[ ScriptifiedStack ]] revRelativeStackFilesList is no longer in the messagePath put "revRelativeStackFilesList(" & quote & pStack & quote & ")" into tFunction put value(tFunction, stack "revStandaloneSettings") into tStackFiles unlock messages -- Make sure the app-bundle isn't already there if there is a file pApkFile then delete file pApkFile if there is a file pApkFile then throw "unable to remove existing apk" end if end if try ------- Create the build folder local tBuildFolder, tResBuildFolder, tDrawableResBuildFolder, tLayoutResBuildFolder, tClassesBuildFolder, tLibsBuildFolder, tArmLibsBuildFolder put tempName() into tBuildFolder create folder tBuildFolder if the platform is "win32" then put shortFilePath(tBuildFolder) into tBuildFolder end if put tBuildFolder & slash & "classes_app" into tClassesBuildFolder put tBuildFolder & slash & "libs" into tLibsBuildFolder put tLibsBuildFolder & slash & "armeabi" into tArmLibsBuildFolder put tBuildFolder & slash & "res" into tResBuildFolder put tResBuildFolder & slash & "drawable" into tDrawableResBuildFolder put tResBuildFolder & slash & "layout" into tLayoutResBuildFolder create folder tClassesBuildFolder create folder tLibsBuildFolder create folder tArmLibsBuildFolder create folder tResBuildFolder create folder tDrawableResBuildFolder create folder tLayoutResBuildFolder if there is no folder tBuildFolder or there is no folder tClassesBuildFolder or \ there is no folder tLibsBuildFolder or there is no folder tArmLibsBuildFolder or \ there is no folder tResBuildFolder or there is no folder tDrawableResBuildFolder or \ there is no folder tLayoutResBuildFolder then throw "could not create build folders" end if if the platform is "win32" then put shortFilePath(tBuildFolder) into tBuildFolder end if -------- Unpack all the files used by the externals // MERG-2014-11-18: [[ TemplateManifest ]] Look for a template manifest provided by the developer in copy files local tManifestFile revStandaloneProgress "Integrating externals..." if tExternals is not empty then put return after tExternals end if set the itemDelimiter to "." repeat for each line tFile in tFiles local tFilePath, tFileName computeAssetLocation tBaseFolder, tFile, tFilePath, tFileName -- Look for manifest template if tFilePath ends with "AndroidManifest.xml" then put url ("binfile:" & tFilePath) into tManifest end if -- Only look for lcext files. if the last item of tFilePath is not "lcext" then next repeat end if -- Only process if the target exists if there is no file tFilePath then throw "could not find referenced external -" && tFile end if -- MERG-2013-09-05: [[ Bug 11152 ]] Only list the external if it was successfully -- extracted. -- Now attempt to process the external if addExternalFromFile(tFilePath, tClassesBuildFolder, tArmLibsBuildFolder) then -- Add the external to the list to load put char 1 to -7 of tFileName & return after tExternals end if end repeat delete the last char of tExternals set the itemDelimiter to comma -------- Create the Manifest revStandaloneProgress "Writing manifest..." if tManifestFile is empty then put tBuildFolder & slash & "AndroidManifest.xml" into tManifestFile end if if tManifest is empty then put url ("binfile:" & mapFilePath(revMobileRuntimeFolder(pTarget) & slash & "Manifest.xml")) into tManifest end if // SN-2015-01-22: [[ Bug 13213 ]] Throw an error in case an error occured if the result is not empty then throw "Cannot find the template Manifest" end if replace "${LABEL}" with tSettings["android,label"] in tManifest replace "${NAME}" with ".mblandroid" in tManifest replace "${IDENTIFIER}" with tSettings["android,identifier"] in tManifest replace "${VERSION_NAME}" with tSettings["android,version name"] in tManifest replace "${VERSION_CODE}" with tSettings["android,version code"] in tManifest if there is a file tIcon then replace "${ICON}" with "android:icon=" & quote & "@drawable/icon" & quote in tManifest else replace "${ICON}" with empty in tManifest end if if tSettings["android,status bar hidden"] then replace "${THEME}" with "@android:style/Theme.NoTitleBar.Fullscreen" in tManifest else replace "${THEME}" with "@android:style/Theme.NoTitleBar" in tManifest end if replace "${ORIENTATION}" with tSettings["android,initial orientation"] in tManifest replace "${MIN_SDK_VERSION}" with tSettings["android,minimum version"] in tManifest local tUsesFeature, tUsesPermission, tFeatures, tPermissions, tAd repeat for each line tFeature in the keys of tSettings["android,device capabilities"] put " " into tUsesFeature replace "${REQUIRED}" with tSettings["android,device capabilities"][tFeature] in tUsesFeature put tUsesFeature & return after tFeatures end repeat repeat for each line tPermission in the keys of tSettings["android,capability permissions"] put " " into tUsesPermission put tUsesPermission & return after tPermissions end repeat repeat for each line tPermission in the keys of tSettings["android,application permissions"] if tPermission is "AD" then put true into tAd put " " & return into tUsesPermission put " " after tUsesPermission else put " " into tUsesPermission end if put tUsesPermission & return after tPermissions end repeat replace "${USES_FEATURE}" with tFeatures in tManifest --replace "${USES_PERMISSION}" with tPermissions in tManifest local tService, tReceiver -- IM-2012-02-28: need barebones receiver for local notifications put "" into tReceiver -- if tBilling then -- -- create service & receiver sections for billing support -- put "" into tService -- put "" & return after tReceiver -- repeat for each item tMsgName in "IN_APP_NOTIFY,RESPONSE_CODE,PURCHASE_STATE_CHANGED" -- put "" & return after tReceiver -- end repeat -- put "" after tReceiver -- else -- put empty into tService -- end if put "" after tReceiver -- PM-2014-03-17: [[ In App Purchasing Updates ]] Include new store setting in manifest. if tSettings["android,inAppPurchasing"] then get tSettings["android,billingProvider"] if it is "Google" then put " " & return after tPermissions end if if it is "Samsung" then put " " & return after tPermissions end if if it is "Amazon" and revLicenseType() is "commercial" then local tAmazonReceiver put "" into tAmazonReceiver put "" & return after tAmazonReceiver put "" & return after tAmazonReceiver put "" after tAmazonReceiver put "" after tAmazonReceiver put tAmazonReceiver after tReceiver end if end if replace "${SERVICE}" with tService in tManifest replace "${RECEIVER}" with tReceiver in tManifest replace "${USES_PERMISSION}" with tPermissions in tManifest if tAd is true then replace "${AD_ACTIVITY}" with "" in tManifest else replace "${AD_ACTIVITY}" with empty in tManifest end if local tInstallLocation switch tSettings["android,installLocation"] case "Internal Storage Only" case empty put empty into tInstallLocation break case "Allow External Storage" put "android:installLocation=""e&"auto""e into tInstallLocation break case "Prefer External Storage" put "android:installLocation=""e&"preferExternal""e into tInstallLocation break end switch replace "${INSTALL_LOCATION}" with tInstallLocation in tManifest local tCustomUrlFilter if tSettings["android,customUrlScheme"] is not empty then put "" & \ "" & \ "" & \ "" & \ "" & \ "" into tCustomUrlFilter else put empty into tCustomUrlFilter end if replace "${CUSTOM_URL_FILTER}" with tCustomUrlFilter in tManifest local tPushPermissions local tPushReceiver if tSettings["android,pushSenderID"] is not empty then put "" & \ "" & \ "" into tPushPermissions put "" & \ "" & \ "" & \ "" & \ "" & \ "" & \ "" & \ "" & \ "" & \ "" into tPushReceiver else put empty into tPushPermissions put empty into tPushReceiver end if replace "${PUSH_PERMISSIONS}" with tPushPermissions in tManifest replace "${PUSH_RECEIVER}" with tPushReceiver in tManifest -- SN-2014-02-07: Add the email attachments provider in the manifest local tProvider put "" & return after tProvider put " " & return after tProvider replace "${PROVIDER}" with tProvider in tManifest #put tManifest into url ("binfile:c:/scratch/manifestdump.xml") put tManifest into url ("binfile:" & tManifestFile) if the result is not empty then throw "could not create manifest" end if -------- Build the Classes revStandaloneProgress "Building classes..." -- MW-2012-07-05: Make sure we record all classes we build so we can clean up. local tCompiledClassFilePath, tBuiltClassFiles put empty into tBuiltClassFiles put tSettings["android,identifier"] into tCompiledClassFilePath replace "." with "/" in tCompiledClassFilePath local tClass, tClassFile put tBuildFolder & slash & "classes_app/mblandroid.java" into tClassFile put "package" && tSettings["android,identifier"] & ";" & return into tClass put "import com.runrev.android.LiveCodeActivity;" & return after tClass put "public class mblandroid extends LiveCodeActivity {}" & return after tClass put tClass into url ("binfile:" & tClassFile) if the result is not empty then throw "could not create activity class" end if put tClassFile & return after tBuiltClassFiles local tLiveCodeClassesFile put tBuildFolder & slash & "LiveCode.jar" into tLiveCodeClassesFile put url ("binfile:" & mapFilePath(revMobileRuntimeFolder(pTarget) & slash & "Classes")) into url ("binfile:" & tLiveCodeClassesFile) if the result is not empty then throw "could not prepare LiveCode classes" end if local tClassPath if the platform is "win32" then put pathToRootClasses() & ";" & tLiveCodeClassesFile into tClassPath else put pathToRootClasses() & ":" & tLiveCodeClassesFile into tClassPath end if -- MM-2011-08-16: Set to target JDK version 1.5. This is expected target bytecode that will be encoded by dex. executeShellCommand pathToJavaC(), "-target 1.5 -source 1.5 -Xlint:none -d", tClassesBuildFolder, "-cp", tClassPath, tClassFile if the result is not empty then throw "could not compile application class" end if put tBuildFolder & slash & "classes_app/" & tCompiledClassFilePath & "/mblandroid.class" & return after tBuiltClassFiles -- IM-2012-02-28: need barebones receiver for local notifications // build receiver class put tBuildFolder & slash & "classes_app/AppReceiver.java" into tClassFile put "package" && tSettings["android,identifier"] & ";" & return into tClass put "import com.runrev.android.EngineReceiver;" & return after tClass put "public class AppReceiver extends EngineReceiver {}" & return after tClass put tClass into url ("binfile:" & tClassFile) if the result is not empty then throw "could not create billing support class" end if put tClassFile & return after tBuiltClassFiles executeShellCommand pathToJavaC(), "-target 1.5 -source 1.5 -Xlint:none -d", tClassesBuildFolder, "-cp", tClassPath, tClassFile if the result is not empty then throw "could not compile billing support class" end if put tBuildFolder & slash & "classes_app/" & tCompiledClassFilePath & "/AppReceiver.class" & return after tBuiltClassFiles -- IM-2011-10-26: Generate and compile billing service & receiver classes -- if tBilling then -- local tBillingClass, tBillingClassFile -- // build service class -- put tBuildFolder & slash & "classes_app/AppService.java" into tBillingClassFile -- put "package" && tSettings["android,identifier"] & ";" & return into tBillingClass -- put "import com.runrev.android.billing.BillingService;" & return after tBillingClass -- put "public class AppService extends BillingService {}" & return after tBillingClass -- put tBillingClass into url ("binfile:" & tBillingClassFile) -- if the result is not empty then -- throw "could not create billing support class" -- end if -- put tBillingClassFile & return after tBuiltClassFiles -- executeShellCommand pathToJavaC(), "-target 1.5 -source 1.5 -Xlint:none -d", tClassesBuildFolder, "-cp", tClassPath, tBillingClassFile -- if the result is not empty then -- throw "could not compile billing support class" -- end if -- put tBuildFolder & slash & "classes_app/" & tCompiledClassFilePath & "/AppService.class" & return after tBuiltClassFiles -- end if // IM-2012-02-24: Generate push notification listener class if tSettings["android,pushSenderID"] is not empty then local tPushReceiverClass, tPushReceiverClassFile put tBuildFolder & slash & "classes_app/PushReceiver.java" into tPushReceiverClassFile put "package" && tSettings["android,identifier"] & ";" & return into tPushReceiverClass put "import com.runrev.android.*;" & return after tPushReceiverClass put "public class PushReceiver extends com.runrev.android.PushReceiver {}" & return after tPushReceiverClass put tPushReceiverClass into url ("binfile:" & tPushReceiverClassFile) if the result is not empty then throw "could not create push notification support class" end if put tPushReceiverClassFile & return after tBuiltClassFiles executeShellCommand pathToJavac(), "-target 1.5 -source 1.5 -Xlint:none -d", tClassesBuildFolder, "-cp", tClassPath, tPushReceiverClassFile if the result is not empty then throw "could not compile push notification support class" end if put tBuildFolder & slash & "classes_app/" & tCompiledClassFilePath & "/PushReceiver.class" & return after tBuiltClassFiles end if -- SN-2014-02-07: Generate email attachment provider local tAttachmentClass, tAttachmentClassFile put tBuildFolder & slash & "classes_app/AppProvider.java" into tAttachmentClassFile put "package" && tSettings["android,identifier"] & ";" & return into tAttachmentClass put "import android.content.*;" & return after tAttachmentClass put "import android.net.Uri;" & return after tAttachmentClass put "import android.app.*;" & return after tAttachmentClass put "import android.database.*;" & return after tAttachmentClass put "import java.io.*;" & return after tAttachmentClass put "import android.util.Log;" & return after tAttachmentClass put "import android.os.ParcelFileDescriptor;" & return after tAttachmentClass put "import com.runrev.android.AttachmentProvider;" & return after tAttachmentClass put "public class AppProvider extends ContentProvider {" & return after tAttachmentClass put "public static final String URI = " & quote & "content://" & tSettings["android,identifier"] & ".attachmentprovider" & quote & ";" & return after tAttachmentClass put "public static final String AUTHORITY = " & quote & tSettings["android,identifier"] & ".attachmentprovider" & quote & ";" & return after tAttachmentClass put "private com.runrev.android.AttachmentProvider m_attachments;" & return after tAttachmentClass put "@Override" & return & "public boolean onCreate() { m_attachments = new AttachmentProvider(getContext()); return true; }" & return after tAttachmentClass put "@Override" & return & "public ParcelFileDescriptor openFile(Uri uri, String mode) throws FileNotFoundException { Log.i(" & quote & "revandroid" & quote & ", uri.toString()); return m_attachments.doOpenFile(uri); }" & return after tAttachmentClass put "@Override" & return & "public Cursor query (Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) { Log.i(" & quote & "revandroid" & quote & ", " & quote & "query: " & quote & " + uri.toString()); return m_attachments.doQuery(uri, projection, selection, selectionArgs, sortOrder); }" & return after tAttachmentClass put "@Override" & return & "public Uri insert (Uri uri, ContentValues p_values) { return m_attachments.doInsert(uri, p_values); }" & return after tAttachmentClass put "@Override" & return & "public int update (Uri uri, ContentValues values, String selection, String[] selectionArgs) { return 0; }" & return after tAttachmentClass put "@Override" & return & "public int delete (Uri uri, String selection, String[] selectionArgs) { return m_attachments.doDelete(uri, selection, selectionArgs); }" & return after tAttachmentClass put "@Override" & return & "public String getType (Uri uri){ return m_attachments.doGetType(uri); }" & return after tAttachmentClass put "}" & return after tAttachmentClass put tAttachmentClass into url ("binfile:" & tAttachmentClassFile) if the result is not empty then throw "could not create email attachment provider" end if put tAttachmentClassFile & return after tBuiltClassFiles executeShellCommand pathToJavac(), "-target 1.5 -source 1.5 -Xlint:none -d", tClassesBuildFolder, "-cp", tClassPath, tAttachmentClassFile if the result is not empty then throw "could not compile email attachment provider" & return & the result end if put tBuildFolder & slash & "classes_app/" & tCompiledClassFilePath & "/AppProvider.class" & return after tBuiltClassFiles local tClassesFile put tBuildFolder & slash & "classes.dex" into tClassesFile executeShellCommand pathToDex(), "--dex", "--output=" & quote & tClassesFile & quote, tClassesBuildFolder, tLiveCodeClassesFile if the result is not empty then throw "could not encode class bundle" end if ------- Compile the Standalone revStandaloneProgress "Building executable..." local tDeploy -- Engine to use put mapFilePath(revMobileRuntimeFolder(pTarget) & slash & "Standalone") into tDeploy["engine"] // SN-2015-01-22: [[ Bug 13213 ]] Throw an error in case an error occured if there is no file tDeploy["engine"] then throw "Could not find standalone engine" end if -- Stackfile to use put the effective filename of stack pStack into tDeploy["stackfile"] -- If the datagrid is used, then include the library as an aux stack if "data grid templates" is in the subStacks of stack pStack and there is a stack "revDataGridLibrary" then put the effective filename of stack "revDataGridLibrary" into tDeploy["auxiliary_stackfiles"] end if -- Externals to reference (on Android we include drivers in this list) repeat for each line tEntry in tExternals put tEntry & return after tDeploy["externals"] end repeat delete the last char of tDeploy["externals"] -- Output file to create put tArmLibsBuildFolder & slash & "librevandroid.so" into tDeploy["output"] -- Splash to use (if non-commercial) if line 3 of the revLicenseInfo is among the words of "Educational Personal" then -- MW-2011-03-17: Make sure absolute paths work correctly if tSettings["android,splash"] is not empty and pathIsRelative(tSettings["android,splash"]) then put tBaseFolder & slash & tSettings["android,splash"] into tDeploy["splash"] else put tSettings["android,splash"] into tDeploy["splash"] end if end if _internal deploy android tDeploy if the result is not empty then throw the result end if -------- Assembling Assets revStandaloneProgress "Assembling assets..." -- MW-2012-07-05: Record all asset files that are copied into /tmp local tAssetFiles put empty into tAssetFiles local tAssetArchive put tBuildFolder & slash & "lcandroid.ap_" into tAssetArchive if there is a file tAssetArchive then delete file tAssetArchive end if local tLayoutXML put mapFilePath(revMobileRuntimeFolder(pTarget) & slash & "livecode_inputcontrol.xml") into tLayoutXML put url ("binfile:" & tLayoutXML) into url ("binfile:" & tLayoutResBuildFolder & slash & "livecode_inputcontrol.xml") if the result is not empty then throw "could not copy layout file '" & tLayoutXML & "'" end if put tLayoutResBuildFolder & slash & "livecode_inputcontrol.xml" & return after tAssetFiles if there is a file tIcon then put url ("binfile:" & tIcon) into url ("binfile:" & tDrawableResBuildFolder & slash & "icon.png") if the result is not empty then throw "could not copy icon '" & tIcon & "'" end if put tDrawableResBuildFolder & slash & "icon.png" & return after tAssetFiles end if if there is a file tStatusBarIcon then put url ("binfile:" & tStatusBarIcon) into url ("binfile:" & tDrawableResBuildFolder & slash & "notify_icon.png") if the result is not empty then throw "could not copy icon '" & tStatusBarIcon & "'" end if put tDrawableResBuildFolder & slash & "notify_icon.png" & return after tAssetFiles end if -- MW-2012-07-04: On Linux, 'aapt' can complain about libz version info -- so we delete that line from the result before checking for an error. if there is a folder (tBuildFolder & slash & "res") then executeShellCommand pathToAapt(), "package", "-f", "-M", tManifestFile, "-I", pathToRootClasses(),"-F", tAssetArchive, "-S", (tBuildFolder & slash & "res") else executeShellCommand pathToAapt(), "package", "-f", "-M", tManifestFile, "-I", pathToRootClasses(),"-F", tAssetArchive end if get the result if line 1 of it contains "no version information available" then delete line 1 of it end if // SN-2014-10-27: [[ Bug 13800 ]] libpng 1.6+ raises a warning for the PNG files having embedded sRGB profile if line 1 of it contains "iCCP: Not recognizing known sRGB profile that has been edited" then delete line 1 of it end if if it is not empty then throw "could not generate package manifest" end if revZipOpenArchive tAssetArchive, "update" if the result is not empty then throw "could not open asset archive" end if addAssetsToArchive tFiles, tBaseFolder, tAssetArchive revZipCloseArchive tAssetArchive if the result is not empty then throw "asset archive creation failed" end if ------- Prepare the Package revStandaloneProgress "Preparing package..." local tUnalignedApkFile put tBuildFolder & slash & "lcandroid-unaligned.apk" into tUnalignedApkFile if there is a file tUnalignedApkFile then delete file tUnalignedApkFile end if -- MM-2014-01-29: [[ OpenSSL ]] Include the revsecurity library. if tSettings["android,include revsecurity"] then put url ("binfile:" & mapFilePath(revMobileRuntimeFolder(pTarget) & slash & "revsecurity")) into url ("binfile:" & tArmLibsBuildFolder & slash & "librevsecurity.so") // SN-2015-01-22: [[ Bug 13213 ]] Throw an error in case an error occured if the result is not empty then throw "Could not find revsecurity library" end if end if -- Copy across the externals into the libs folder repeat for each line tEntry in tExternals -- Only copy across an external from the Runtime folder if we haven't -- already copied from a lcext. if there is a file (tArmLibsBuildFolder & slash & "lib" & tEntry & ".so") then next repeat end if put url ("binfile:" & mapFilePath(revMobileRuntimeFolder(pTarget) & slash & tEntry)) into url ("binfile:" & tArmLibsBuildFolder & slash & "lib" & tEntry & ".so") // SN-2015-01-22: [[ Bug 13213 ]] Throw an error in case an error occured if the result is not empty then throw "Could not find " & tEntry && "library" end if end repeat -- Work out whether to produce an unsigned apk local tUnsignedParam if (pTarget contains "build") and tUnsigned then put "-u" into tUnsignedParam else put empty into tUnsignedParam end if -- Build the APK // IM-2013-05-21: [[ BZ 10904 ]] call our own internal function instead of (removed) apkbuilder script buildAPK tUnalignedApkFile, tUnsignedParam, tAssetArchive, tClassesFile, tLibsBuildFolder if the result contains "Debug Certificate Expired" then throw "debug certificate has expired, please refresh" end if if not (the result contains "=> lib/armeabi/librevandroid.so") then throw "apk preparation failed" end if ------- Signing the Package if pTarget contains "build" and tKey is not empty then revStandaloneProgress "Signing package..." if not tKey begins with "/" then put tBaseFolder & slash before tKey end if if there is no file tKey then throw "could not file keystore '" & tKey & "'" end if local tKeystoreAlias ask "Please provide the keystore alias to use" if it is empty then throw "no keystore alias provided for keystore" end if put it into tKeystoreAlias local tKeystorePassword ask password clear "Please provide the keystore password" if it is empty then throw "no password provided for keystore" end if put it into tKeystorePassword local tPrivateKeyPassword ask password clear "Please provide the private key password" if it is empty then throw "no password provided for private key" end if put it into tPrivateKeyPassword // IM-2014-09-04: [[ Bug 13343 ]] Specify SHA1 when signing APK as specified in android signing docs. executeShellCommand pathToJarSigner(), "-sigalg", "SHA1withRSA", "-digestalg", "SHA1", "-keystore", tKey, "-storepass", tKeystorePassword, "-keypass", tPrivateKeyPassword, tUnalignedApkFile, tKeystoreAlias if the result begins with "jarsigner error" then throw "signing failed -" && word 4 to -1 of the result else if the result begins with "jarsigner: Certificate chain not found" then throw "signing failed -" && word 2 to -1 of the result else if (the result begins with "jarsigner: key associated with") and (line 1 of the result ends with "not a private key") then throw "signing failed - key password was incorrect or" && word 2 to -1 of the result end if end if ------- Finalize the Package revStandaloneProgress "Finalizing package..." executeShellCommand pathToZipAlign(), "-f", "-v", "4", tUnalignedApkFile, pApkFile if not (line -1 of the result begins with "Verification succes") or there is no file pApkFile then throw "apk finalization failed" end if catch tError end try if there is a file tUnalignedApkFile then delete file tUnalignedApkFile if there is a file tAssetArchive then delete file tAssetArchive if there is a file tLiveCodeClassesFile then delete file tLiveCodeClassesFile if there is a file (tArmLibsBuildFolder & slash & "librevandroid.so") then delete file (tArmLibsBuildFolder & slash & "librevandroid.so") if there is a file (tArmLibsBuildFolder & slash & "librevsecurity.so") then delete file (tArmLibsBuildFolder & slash & "librevsecurity.so") repeat for each line tEntry in tExternals if there is a file (tArmLibsBuildFolder & slash & "lib" & tEntry & ".so") then delete file (tArmLibsBuildFolder & slash & "lib" & tEntry & ".so") end repeat if there is a folder tArmLibsBuildFolder then delete folder tArmLibsBuildFolder if there is a folder tLibsBuildFolder then delete folder tLibsBuildFolder if there is a file tClassesFile then delete file tClassesFile repeat for each line tClassFile in tBuiltClassFiles if there is a file tClassFile then delete file tClassFile end repeat set the itemDelimiter to slash repeat while there is a folder (tBuildFolder & slash & "classes_app" & slash & tCompiledClassFilePath) delete folder (tBuildFolder & slash & "classes_app" & slash & tCompiledClassFilePath) if the result is not empty then exit repeat end if delete the last item of tCompiledClassFilePath end repeat set the itemDelimiter to comma repeat for each line tFIle in tAssetFiles if there is a file tFile then delete file tFile end repeat if there is a folder tLayoutResBuildFolder then delete folder tLayoutResBuildFolder if there is a folder tDrawableResBuildFolder then delete folder tDrawableResBuildFolder if there is a folder tResBuildFolder then delete folder tResBuildFolder if there is a file tManifestFile then delete file tManifestFile if there is a folder tClassesBuildFolder then delete folder tClassesBuildFolder if there is a folder tBuildFolder then delete folder tBuildFolder if tError is not empty then throw tError end if end revSaveAsMobileStandaloneMain ################################################################################ private command computeAssetLocation pBaseFolder, pFile, @rPath, @rName local tIsAbsolute put pFile begins with "/" or char 2 to 3 of pFile is ":/" into tIsAbsolute set the itemDelimiter to slash if the last item of pFile is "*" then delete the last item of pFile end if if tIsAbsolute then put item -1 of pFile into rName put pFile into rPath else -- MM-2011-09-07 [[ Bug 10345 ]] Use the full path to the file (rather than just name), ensureing we create sub-folders. put pFile into rName put pBaseFolder & slash & pFile into rPath end if end computeAssetLocation private command addAssetsToArchive pFiles, pBaseFolder, pArchive local tDeviceConfig, tConfigFile, tCustomConfigFile, tFonts put mapFIlePath(revMobileRuntimeFolder() & slash & "lc_device_config.txt") into tConfigFile if there is not a file tConfigFile then put empty into tConfigFile end if repeat for each line tFile in pFiles local tName computeAssetLocation pBaseFolder, tFile, tFile, tName if there is a file tFile then if tConfigFile is not empty and tName is "lc_device_config.txt" then // need to merge config files put tFile into tCustomConfigFile else if tFile ends with ".ttf" or tFile ends with ".ttc" then -- MM-2012-03-07: [[ Custom fonts ]] If any fonts are found, add to the list so that they are handled correctly put tFile & return after tFonts else if tFile ends with ".lcext" or tFile ends with ".dylib" then -- MW-2012-07-05: [[ Externals ]] If an 'lcext' file or dylib file is found then we ignore it. else if tFile ends with "AndroidManifest.xml" then -- MW-2015-04-24: [[ TemplateAppMetadata ]] If an template manifest is found then we ignore it. else revZipAddUncompressedItemWithFile pArchive, "assets/" & tName, tFile end if else if there is a folder tFile then addAssetsFromFolderToArchive tFile, "assets" & slash & tName, pArchive, tFonts else throw "could not find referenced asset to include -" && tFile end if end repeat if tCustomConfigFile is empty then if tConfigFile is not empty then revZipAddUncompressedItemWithFile pArchive, "assets/lc_device_config.txt", tConfigFile end if else put url ("binfile:" & tConfigFile) & return into tDeviceConfig put url ("binfile:" & tCustomConfigFile) after tDeviceConfig revZipAddUncompressedItemWithData pArchive, "assets/lc_device_config.txt", "tDeviceConfig" end if -- MM-2012-03-07: [[ Custom fonts ]] Add any fonts found to the assets/fonts folder set the itemDel to slash repeat for each line tFont in tFonts if tFont is not empty then revZipAddUncompressedItemWithFile pArchive, "assets/fonts/" & item -1 of tFont, tFont end if end repeat set the itemDel to comma end addAssetsToArchive private command addAssetsFromFolderToArchive pFolder, pBaseName, pArchive, @pFonts set the itemDelimiter to slash -- Ignore any .svn folders if the last item of pFolder is ".svn" then exit addAssetsFromFolderToArchive end if local tOldFolder put the folder into tOldFolder set the folder to pFolder if the folder is not pFolder then throw "could not descend into referenced asset folder -" && pFolder end if repeat for each line tFile in the files if tFile ends with ".ttf" or tFile ends with ".ttc" then -- MM-2012-03-07: [[ Custom fonts ]] If any fonts are found, add to the list so that they are handled correctly put the folder & slash & tFile & return after pFonts else if tFile ends with ".lcext" or tFile ends with ".dylib" then -- MW-2012-07-05: [[ Externals ]] If an 'lcext' file or dylib file is found then we ignore it. else revZipAddUncompressedItemWithFile pArchive, pBaseName & slash & tFile, the folder & slash & tFile end if end repeat repeat for each line tFolder in the folders if tFolder is ".." then next repeat end if addAssetsFromFolderToArchive pFolder & slash & tFolder, pBaseName & slash & tFolder, pArchive, pFonts end repeat set the folder to tOldFolder end addAssetsFromFolderToArchive ################################################################################ private function fileIsZipArchive pFile open file pFile for binary read if the result is empty then read from file pFile for 4 bytes close file pFile end if if it is empty then throw "could not open external -" && pFile end if return byte 1 of it is "P" and byte 2 of it is "K" and byte 3 of it is numToChar(3) and byte 4 of it is numToChar(4) end fileIsZipArchive -- MERG-2013-09-05: [[ Bug 11152 ]] Change to function - returns true if external was successfully -- extracted. private function addExternalFromFile pExternal, pClassesFolder, pLibsFolder -- If the file is not a zip archive then there is nothing to do. if not fileIsZipArchive(pExternal) then exit addExternalFromFile end if -- Get the name of the external. set the itemDelimiter to slash get the last item of pExternal set the itemDelimiter to "." get the first item of it set the itemDelimiter to comma -- Compute the class / lib names. local tClassesFile, tLibFile put pClassesFolder & slash & "lib" & it & ".jar" into tClassesFile put pLibsFolder & slash & "lib" & it & ".so" into tLibFile -- Otherwise open the archive and check for appropriate android components revZipOpenArchive pExternal, "read" if the result is empty then revZipExtractItemToFile pExternal, "Android/Classes", tClassesFile revZipExtractItemToFile pExternal, "Android/External-armeabi", tLibFile revZipCloseArchive pExternal end if if there is a file tClassesFile and there is no file tLibFile then throw "android external contains classes but no library" end if return there is a file tLibFile end addExternalFromFile ################################################################################ function revMobileRuntimeFolder pTarget local tOverridePath, tUserPath // IM-2013-12-16: [[ Bug 11594 ]] Check override is not empty to avoid accidentally detecting "/Android" path if revOverrideRuntimePath() is not empty then put revOverrideRuntimePath() & slash & "Android" into tOverridePath if there is a folder tOverridePath then return tOverridePath end if end if // IM-2013-12-16: [[ Bug 11594 ]] Check override is not empty to avoid accidentally detecting "/Android" path if revEnvironmentUserRuntimePath() is not empty then put revEnvironmentUserRuntimePath() & slash & "Android" into tUserPath if there is a folder tUserPath then return tUserPath end if end if return revEnvironmentRuntimePath() & slash & "Android" end revMobileRuntimeFolder ################################################################################ private command executeAdbCommand pCommand local tAdb put pathToAdb() into tAdb if tAdb is empty or there is not a file tAdb then exit executeAdbCommand end if get doShellCommand(tAdb, pCommand) return it end executeAdbCommand private command executeShellCommand local tShell repeat with i = 2 to the paramCount if param(i) contains space and not (param(i) begins with "-") then put quote & param(i) & quote & space after tShell else put param(i) & space after tShell end if end repeat get doShellCommand(param(1), tShell) return it end executeShellCommand private function doShellCommand pCommand, pArguments --makeBuildLogEntry "Executing command:" && pCommand && pArguments local tCommandPath set the itemDelimiter to slash put item 1 to -2 of pCommand into tCommandPath if the platform is "win32" then put shortFilePath(tCommandPath) into tCommandPath end if local tOldPath put $PATH into tOldPath if the platform is "win32" then replace slash with backslash in tCommandPath put tCommandPath & ";" & sJavaRoot & "\bin" & ";" before $PATH else if the platform is "macos" then put tCommandPath & ":" before $PATH else if the platform is "linux" then put tCommandPath & ":" & sJavaRoot & "/bin" & ":" before $PATH end if set the hideConsoleWindows to true write item -1 of pCommand && pArguments & return to stderr get shell(item -1 of pCommand && pArguments) put tOldPath into $PATH return it end doShellCommand ################################################################################ private function pathToRootClasses pRoot // SN-2014-10-16: [[ ScriptifiedStack ]] Stack name updated dispatch function "pathToRootClasses" to stack "revDeployLibraryAndroid" with pRoot return the result end pathToRootClasses private function pathToSDKClasses pRoot // SN-2014-10-16: [[ ScriptifiedStack ]] Stack name updated dispatch function "pathToSDKClasses" to stack "revDeployLibraryAndroid" with pRoot return the result end pathToSDKClasses private function pathToAapt pRoot // SN-2014-10-16: [[ ScriptifiedStack ]] Stack name updated dispatch function "pathToAapt" to stack "revDeployLibraryAndroid" with pRoot return the result end pathToAapt private function pathToAdb pRoot // SN-2014-10-16: [[ ScriptifiedStack ]] Stack name updated dispatch function "pathToAdb" to stack "revDeployLibraryAndroid" with pRoot return the result end pathToAdb private function pathToZipAlign pRoot // SN-2014-10-16: [[ ScriptifiedStack ]] Stack name updated dispatch function "pathToZipAlign" to stack "revDeployLibraryAndroid" with pRoot return the result end pathToZipAlign private function pathToDex pRoot // SN-2014-10-16: [[ ScriptifiedStack ]] Stack name updated dispatch function "pathToDex" to stack "revDeployLibraryAndroid" with pRoot return the result end pathToDex private function pathToJavaC pRoot // SN-2014-10-16: [[ ScriptifiedStack ]] Stack name updated dispatch function "pathToJavaC" to stack "revDeployLibraryAndroid" with pRoot return the result end pathToJavaC private function pathToJava pRoot // SN-2014-10-16: [[ ScriptifiedStack ]] Stack name updated dispatch function "pathToJava" to stack "revDeployLibraryAndroid" with pRoot return the result end pathToJava private function pathToJarSigner pRoot // SN-2014-10-16: [[ ScriptifiedStack ]] Stack name updated dispatch function "pathToJarSigner" to stack "revDeployLibraryAndroid" with pRoot return the result end pathToJarSigner ################################################################################ -- MW-2013-06-13: [[ CloneAndRun ]] Map a file path from where the sb expects them to be to local -- files in the build folder. function mapFilePath pPath if revEnvironmentIsInstalled() then // SN-2015-01-19: [[ Bug 13213 ]] Linux filepaths are case-sensitive. // so we try to caselessly match the targetted file, with the actual files. set the itemDelimiter to slash // Get the list of files in the targetted directory put the defaultFolder into tOldFolder set the defaultFolder to item 1 to -2 of pPath put the files into tFiles set the defaultFolder to tOldFolder put lineOffset(last item of pPath, tFiles) into tLineOffset // We return empty if we can't find the specified file // That will allow us to throw an error in such a case. if tLineOffset is 0 then return empty else put line tLineOffset of tFiles into item -1 of pPath end if return pPath end if local tPath local tRepo, tComponent, tBuild, tSuffix set the itemDelimiter to slash put revEnvironmentRepositoryPath() into tRepo put the last item of pPath into tComponent if revEnvironmentBinariesPath() contains "/Debug" then put "debug" into tBuild else put "release" into tBuild end if put revLicenseType() into tSuffix put toUpper(char 1 of tSuffix) into char 1 of tSuffix switch tComponent case "notify_icon.png" put merge("[[tRepo]]/_build/android/[[tBuild]]/notify_icon.png") into tPath break case "Manifest.xml" put merge("[[tRepo]]/_build/android/[[tBuild]]/Manifest.xml") into tPath break case "livecode_inputcontrol.xml" put merge("[[tRepo]]/_build/android/[[tBuild]]/livecode_inputcontrol.xml") into tPath break case "Classes" put merge("[[tRepo]]/_build/android/[[tBuild]]/Classes-[[tSuffix]]") into tPath break case "Standalone" put merge("[[tRepo]]/_build/android/[[tBuild]]/Standalone-[[tSuffix]]") into tPath break case "revzip" put merge("[[tRepo]]/_build/android/[[tBuild]]/RevZip") into tPath break case "revdb" put merge("[[tRepo]]/_build/android/[[tBuild]]/RevDb") into tPath break case "revxml" put merge("[[tRepo]]/_build/android/[[tBuild]]/RevXml") into tPath break case "dbsqlite" put merge("[[tRepo]]/_build/android/[[tBuild]]/DbSqlite") into tPath break case "dbmysql" put merge("[[tRepo]]/_build/android/[[tBuild]]/DbMysql") into tPath break default put pPath into tPath break end switch return tPath end mapFilePath ################################################################################