How to set Cordova Android project to use NDK - android

I am trying to do what this question/answer has
java.lang.UnsatisfiedLinkError: dalvik.system.PathClassLoader
but in cordova instead.
android {
....
defaultConfig {
....
ndk {
abiFilters "armeabi", "armeabi-v7a", "x86", "mips"
}
}
}
This works if I edit build.gradle manually in platforms/android/build.gradle Using Cordova Android 6.4.0 (7.1 seems to break almost every plugin, including some cordova plugins such as cordova-network-information, so I've been unable to upgrade so far, and am looking for other solutions).
Editing manually isn't ideal, is there a way to set this automatically? Possibly with a hook or config.xml change?
Thanks
(edit)
Updated to 7.1 successfully, 64 bit still broken.

I was able to do this using the build-extras option, combined with Android 7.1
In your project root, create a file called build-extras.gradle
Put this inside it and save
android {
defaultConfig {
ndk {
abiFilters "armeabi", "armeabi-v7a", "x86", "mips"
}
}
}
Next, in your scripts folder, make a new script called update_build_gradle.js
Put this inside it and save
module.exports = function (context) {
if (context.opts.cordova.platforms.indexOf('android') < 0) {
return;
}
console.log("Starting gradle modifications");
const path = require('path');
const fs = require('fs');
const gradlePath = path.join(context.opts.projectRoot, 'platforms/android/app/build-extras.gradle');
const gradleExtraPath = path.join(context.opts.projectRoot, 'build-extras.gradle');
return new Promise(function (resolve, reject) {
fs.copyFile(gradleExtraPath, gradlePath, function (err) {
if (err) {
console.error("Failed to copy to " + gradlePath + " from " + gradleExtraPath);
reject(err);
} else {
console.log("Copied to " + gradlePath + " successfully");
resolve();
}
});
});
};
Lastly, open up your config.xml, find your <platform name="android"> tree and enter this new hook
<hook src="scripts/update_build_gradle.js" type="before_build" />
Note that the documentation here https://cordova.apache.org/docs/en/latest/guide/platforms/android/index.html#extending-buildgradle is wrong.
. This file must be placed in the android platform directory (/platforms/android), so it is recommended that you copy it over via a script attached to the before_build hook.
It actually needs to be in /platforms/android/app
-Edit
As of cordova version 9, you can't use requireCordovaModule anymore. But you can safely replace with just require.

Related

Add Custom values to build.gradle file via build-extras.gradle in Cordova

I had updated cordova-android version to 6.4.0 and before that I had 5.1.1 installed. Here the problem was that when updated to 6.4.0 version, while building the project I was getting error. So to overcome that issue I had to add the below code
configurations.all {
resolutionStrategy {
force 'com.android.support:support-v4:27.1.0'
}
}
Now the problem is every time I build the project I have to edit build.gradle file, which is generated while adding the platform to the project in Cordova. As this is not part of Source Control.
To overcome this I have used the solution from this post. Here I am adding the Javascript file and adding the hook in the config.xml
Java script file
var fs = require('fs');
var rootdir = process.argv[2];
var android_dir = rootdir + '/platforms/android';
var gradle_file = rootdir + '/build-extras.gradle';
var dest_gradle_file = android_dir + '/build-extras.gradle';
if (fs.existsSync(android_dir) && fs.existsSync(gradle_file)) {
console.log('Copy ' + gradle_file + ' to ' + android_dir);
fs.createReadStream(gradle_file).pipe(fs.createWriteStream (dest_gradle_file));
} else {
console.log(gradle_file + ' not found. Skipping');
}
Build-extras.gradle
ext.postBuildExtras = {
android {
configurations.all {
resolutionStrategy {
force 'com.android.support:support-v4:27.1.0'
}
}
}
}
Hooks in Config.xml
<platform name="android">
<hook src="scripts/buildGradleHook.js" type="before_build" />
</platform>
The hooks added is not reflecting in the generated android folder. That is build-extras.gradle file is not reflected in android folder.
I tried your solution and found that the vars declared to define the different paths are wrong.
I changed your hook code for this:
module.exports = function(ctx) {
var fs = ctx.requireCordovaModule('fs'),
path = ctx.requireCordovaModule('path'),
rootdir = ctx.opts.projectRoot,
android_dir = path.join(ctx.opts.projectRoot, 'platforms/android');
gradle_file = rootdir + '/build-extras.gradle';
dest_gradle_file = android_dir + '/build-extras.gradle';
/*
console.log("Before-Build Hook - rootdir", rootdir);
console.log("Before-Build Hook - android_dir", android_dir);
console.log("Before-Build Hook - gradle_file", gradle_file);
console.log("Before-Build Hook - dest_gradle_file", dest_gradle_file);
*/
if(!fs.existsSync(gradle_file)){
console.log(gradle_file + ' not found. Skipping');
return;
}else if(!fs.existsSync(android_dir)){
console.log(android_dir + ' not found. Skipping');
return;
}
console.log('Copy ' + gradle_file + ' to ' + android_dir);
fs.createReadStream(gradle_file).pipe(fs.createWriteStream(dest_gradle_file));
}
Also, in the Hook doc says it must be executable, so it needs to be wrapped by " module.exports = function(ctx) { }".

Qt QBS and android

I'm trying to find a simple tutorial: how to make a simple application for android using gbps. The following links were found:
Stack oferflow. The answer to this question has not been
received, although the version of the cbs has already been updated
to 1.11 and the support of android is included.
AndroidApk Item in QBS Documentation. In this case I get warning: '../Application/src/main/AndroidManifest.xml' does not exist.
I unfortunately could not find any new information. I ask for help.
Update:
For Qmake I just create standard widget project like this one:
QT += core gui
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
TARGET = androidtest
TEMPLATE = app
DEFINES += QT_DEPRECATED_WARNINGS
SOURCES += \
main.cpp \
mainwindow.cpp
HEADERS += \
mainwindow.h
FORMS += \
mainwindow.ui
CONFIG += mobility
MOBILITY =
And this is works and builds fine. QtCreator automatically create all necessary files and than run app on my phone
In Qbs I try to make same application. For this reason I have QBS-file:
import qbs
Project {
CppApplication {
name: "helloworld"
Depends {
name: "Qt"
submodules: [
"core",
"widgets"
]
}
Depends { name: "Android.ndk" }
Android.ndk.appStl: "gnustl_shared"
Group {
name: "src"
files: [
"main*.*"
]
}
}
AndroidApk {
name: "helloworld_android"
Depends {name: "helloworld" }
packageName: "com.example.android.helloworld"
}
}
At the end I have Done with HelloWorld product (libhelloworld.so). But first error of "helloworld_android" is a fail at android manifest. This file is undefined. What I should do next?
qmake has some built-in magic when building for Android, like using resources provided by Qt (including a manifest template) and running the android-deployqt tool. None of this is currently done by qbs.
Ok, I think I made it. It's not a good solution but this is work. Resulting APK you can find in "\install-root\$productName$\build\outputs\apk\$productName$-debug.apk
import qbs
import qbs.TextFile
import qbs.Process
import qbs.File
Project {
//Main Application
CppApplication {
name: "helloworld";
Depends {
name: "Qt"
submodules: [
"core",
"widgets"
]
}
Depends { name: "Android.ndk" }
Android.ndk.appStl: "gnustl_shared"
Group {
name: "src"
files: [
"main*.*"
]
}
Group {
qbs.install: true
fileTagsFilter: "dynamiclibrary"
qbs.installPrefix : product.name+"/libs/"+Android.ndk.abi+"/"
}
}
//Preparation
Product {
name: "Prepared2Deploy"
type: "prepared2deploy"
Depends { name: "helloworld" }
Depends { name: "Qt.core" }
Depends { name: "Android.ndk" }
Depends { name: "Android.sdk" }
Rule {
inputsFromDependencies: "installable"
Artifact {
filePath: input.fileName+".json"
fileTags: "prepared2deploy"
}
prepare: {
var cmd = new JavaScriptCommand();
cmd.description = "prepare for androidDeployQt";
cmd.highlight = "install";
cmd.sourceCode = function() {
var outputFile = new TextFile(output.filePath, TextFile.WriteOnly);
outputFile.writeLine("{");
outputFile.writeLine(" \"qt\": \"" + product.Qt.core.binPath.replace(/\/bin$/,"") + "\",");
outputFile.writeLine(" \"sdk\": \"" + product.Android.sdk.sdkDir + "\",");
outputFile.writeLine(" \"sdkBuildToolsRevision\": \"" + product.Android.sdk.buildToolsVersion + "\",");
var ndkDir = product.Android.ndk.ndkDir.replace(/\\/g,"/"); //why sdk ndk get wrong slashes?
outputFile.writeLine(" \"ndk\": \""+ndkDir+"\",");
var toolchain = product.cpp.toolchainPrefix.replace(/-$/,"");
outputFile.writeLine(" \"toolchain-prefix\": \"" + toolchain + "\",");
outputFile.writeLine(" \"tool-prefix\": \"" + toolchain + "\",");
outputFile.writeLine(" \"toolchain-version\": \"4.9\","); //how I can get it ???
outputFile.writeLine(" \"ndk-host\": \"windows-x86_64\","); //how I can get it ???
var abi = product.Android.ndk.abi
outputFile.writeLine(" \"target-architecture\": \""+abi+"\",");
outputFile.writeLine(" \"stdcpp-path\": \""+ndkDir+"/sources/cxx-stl/gnu-libstdc++/4.9/libs/" + //how I can get it ???
abi+"/lib"+product.Android.ndk.appStl+".so\",");
outputFile.writeLine(" \"application-binary\": \""+ input.filePath+"\"");
outputFile.writeLine("}");
outputFile.close();
}
return cmd;
}
}
}
//Deployer
Product {
name: "AndroidDeployQt"
Depends { name: "helloworld" }
id: androidDeployQt
type: "androidDeployQt"
Depends {name: "Qt.core" }
Rule {
inputsFromDependencies: "prepared2deploy"
alwaysRun: true
Artifact {
filePath: "log.txt"
fileTags: "androidDeployQt"
}
prepare: {
var cmd = new JavaScriptCommand();
cmd.description = "androidDeployQt";
cmd.highlight = "install";
cmd.sourceCode = function() {
var logFile = new TextFile(output.filePath, TextFile.WriteOnly);
logFile.writeLine(input.fileName);
var productName = input.fileName.replace(/.so.json$/,"").replace(/^(lib)/,"");
var androidDeployProcess = new Process();
var exitCode = androidDeployProcess.exec(product.Qt.core.binPath+"/androiddeployqt.exe",
[
"--input", input.filePath,
"--output", project.buildDirectory+"/install-root/"+productName,
"--android-platform", "android-25", //???
"--gradle"
])
if (exitCode) {
console.error("Error at androidDeployProcess. Error code: "+exitCode);
console.error(androidDeployProcess.readStdErr());
console.error("FULL_LOG: ");
console.error(androidDeployProcess.readStdOut());
}
logFile.close();
}
return cmd;
}
}
}
}
QBS 1.13, released on February this year (2019) makes deploying an Android application as simple as with qmake. In practice, you don't need to do anything special. For example, I took the contactlist application from the Qt examples and added this QBS file:
import qbs 1.0
Project {
QtGuiApplication {
name: "contactlist"
install: true
files: [
"contactmodel.h",
"contactmodel.cpp",
"main.cpp",
]
Group {
files: [
"*.qml",
"designer/Backend/*.qml",
"designer/Backend/qmldir",
]
fileTags: ["qt.core.resource_data"]
}
Depends { name: "Qt.quick" }
}
}
As you can see, there's nothing specific to Android here. The only trick I'm using is to assign the tag qt.core.resource_data to the QML files in order to have them compiled as resource files — but it's not even required.
With qbs run the application will be run in your connected Android device.

Not able to set the path to files correctly in android.gradle

task copyLibs(type: Copy) {
mybuildpath('path.name').string.split(" ")
.collect {
mybuildpath('[' + it + ']pkg.lib').string
}
.findAll {
it.length() > 0
}
.each {
from(new File(it)) {
if (!getCurrentFlavor().contains('SomeKeyWord')) {
include '**/folder1/**/*.so'
include '**/folder2/**/*.so'
// include '**/lib1.so'
// include '**/lib2.so'
// include '**/lib3.so'
// include '**/lib4.so'
} else {
include '**/*.so'
}
}
into new File(buildDir, 'native-libs-folder')
}
}
This in android.gradle of my project I have to include few .so files during compile time.
folder1 and folder2 have all those libraries [lib1,lib2,... etc], I want to include them all.
but this code is not working and it's not importing any library in my native-lib-folder,
But when I comment those 2 lines and uncomment the below 4 [those specific **/lib1.so , .. so on ,, ] those 4 libraries are getting copied in native-lib-folder.
I have searched and I feel that regular expression syntax is correct but still can't understand what I am doing wrong.
Please guide
Ii is Ant path style not regex.
Try
include '/folder1/*.so'
include '/folder2/*.so'
instead.

ngCordova write file on Android

I'm trying to write file to local storage on Android device, using ngCordova function found on ionic forum. This is how the function looks:
$scope.exportClicked = function(options) {
var deferred = $q.defer();
$window.resolveLocalFileSystemURL($window.cordova.file.dataDirectory,
function(dir) {
dir.getFile('text.txt', {
create: true
}, function(fileEntry) {
fileEntry.createWriter(
function(fileWriter) {
if (options['append'] === true) {
fileWriter.seek(fileWriter.length);
}
fileWriter.onwriteend = function(evt) {
evt.fileEntry = fileEntry;
deferred.resolve(evt);
};
fileWriter.write(data);
},
function(error) {
deferred.reject(error);
}
);
}, function(er) {
deferred.reject(error);
});
});
return deferred.promise;
};
When I'm running app through ionic in webbrowser, it gives me an error:
TypeError: Cannot read property 'file' of undefined
at Scope.$scope.exportClicked (app.js:27)
I've installed cordova file plugin, but it looks like it can't find cordova.file functionality.
On Android device it won't work either. Any ideas?
You forgot to install file cordova plugin. https://github.com/apache/cordova-plugin-file
$window.cordova isn't defined. Since you are using ngCordova, and ngCordova has official support for the file plugin, I would start with the ngCordova documentation for the file plugin. Here is the bit you might be interested in:
$cordovaFile.writeFile(cordova.file.dataDirectory, "file.txt", "text", true)
.then(function (success) {
// success
}, function (error) {
// error
});
You also get the added bonus have having more readable code when you use the ngCordova implementation.
If you would rather follow your original example more closely, try replacing $window.cordova with window.cordova, or simply cordova.

Package name does not correspond to the file path - Gradle configuration

I'm trying migrating a normal Android Studio (IntelliJ) project to Gradle project recently. And currently I'm encounter a problem: IntelliJ gives me a warning on the beginning of every file says that my 'package name does not correspond to the file path'. e.g.
The first line of my some/prefixes/a/b/c/d/E.java is:
package a.b.c.d;
....
IntelliJ thinks the package name should be 'c.d' instead of 'a.b.c.d'. Because I set
SourceSets {
main.java.srcDirs = ["some/prefixes/a/b"]
}
in the module's build.gradle.
I know I could do the change below to make IntelliJ happy:
SourceSets {
main.java.srcDirs = ['some/prefixes']
}
But I can't do that because there're huge numbers of projects under 'some/prefixes' and I definitely don't want to introduce all of them into this module.
I used to add a packagePrefix="a.b" in my 'module.iml' in my original Android studio project and it works well:
https://www.jetbrains.com/idea/help/configuring-content-roots.html#d2814695e312
But I don't know how to accomplish similar fix after migrating to Gradle project.
I end up to write a task for gradle.
The task add the famous packagePrefix to the *.iml file.
This solution only work for intelliJ, I hope someone have a better solution.
task addPackagePrefix << {
println 'addPackagePrefix'
def imlFile = file(MODULE_NAME+".iml")
if (!imlFile.exists()) {
println 'no module find '
return
}
def parsedXml = (new XmlParser()).parse(imlFile)
if(parsedXml.component[1] && parsedXml.component[1].content){
parsedXml.component[1].content.findAll { Node node ->
node.sourceFolder.findAll { Node s ->
def url = s.attribute("url").toString()
if (url.endsWith(SRC_DIR)) {
println 'Node founded '
def attr = s.attribute('packagePrefix')
if (attr == null) {
// add prefix
println 'Adding package prefix'
s.attributes().put('packagePrefix', PACKAGE_NAME)
println s.toString()
// writing
def writer = new StringWriter()
new XmlNodePrinter(new PrintWriter(writer)).print(parsedXml)
imlFile.text = writer.toString()
}
}
}
}
}

Categories

Resources