Error: Hook failed with error code 1: - android

I am working on an IONIC Project,
while I am adding android platform, it gives me an error like this.
Error: Hook failed with error code 1: D:\IONIC Workspace\risecx-app\hooks\before_prepare\01_jshint.js
at C:\Users\HP\AppData\Roaming\npm\node_modules\cordova\node_modules\cordova-lib\src\hooks\HooksRunner.js:195:23
at _rejected (C:\Users\HP\AppData\Roaming\npm\node_modules\cordova\node_modules\q\q.js:797:24)
at C:\Users\HP\AppData\Roaming\npm\node_modules\cordova\node_modules\q\q.js:823:30
at Promise.when (C:\Users\HP\AppData\Roaming\npm\node_modules\cordova\node_modules\q\q.js:1035:31)
at Promise.promise.promiseDispatch (C:\Users\HP\AppData\Roaming\npm\node_modules\cordova\node_modules\q\q.js:741:41)
at C:\Users\HP\AppData\Roaming\npm\node_modules\cordova\node_modules\q\q.js:557:44
at flush (C:\Users\HP\AppData\Roaming\npm\node_modules\cordova\node_modules\q\q.js:108:17)
at doNTCallback0 (node.js:417:9)
at process._tickCallback (node.js:346:13)
my hooks\before_prepare\01_jshint.js file is like...
#!/usr/bin/env node
var fs = require('fs');
var path = require('path');
var jshint = require('jshint').JSHINT;
var async = require('async');
var foldersToProcess = [ 'js', 'js/services' ];
foldersToProcess.forEach(function(folder) {
processFiles("www/" + folder);
});
function processFiles(dir, callback) {
var errorCount = 0;
fs.readdir(dir, function(err, list) {
if (err) {
console.log('processFiles err: ' + err);
return;
}
async.eachSeries(list, function(file, innercallback) {
file = dir + '/' + file;
fs.stat(file, function(err, stat) {
if(!stat.isDirectory()) {
if(path.extname(file) === ".js") {
lintFile(file, function(hasError) {
if(hasError) {
errorCount++;
}
innercallback();
});
} else {
innercallback();
}
} else {
innercallback();
}
});
}, function(error) {
if(errorCount > 0) {
process.exit(1);
}
});
});
}
function lintFile(file, callback) {
console.log("Linting " + file);
fs.readFile(file, function(err, data) {
if(err) {
console.log('Error: ' + err);
return;
}
if(jshint(data.toString())) {
console.log('File ' + file + ' has no errors.');
console.log('-----------------------------------------');
callback(false);
} else {
console.log('Errors in file ' + file);
var out = jshint.data(),
errors = out.errors;
for(var j = 0; j < errors.length; j++) {
console.log(errors[j].line + ':' + errors[j].character + ' -> ' + errors[j].reason + ' -> ' +
errors[j].evidence);
}
console.log('-----------------------------------------');
callback(true);
}
});
}
I have worked with following commands....
npm install jshint --save
npm install q --save
npm install async
so any idea, where I am making a mistake....

Just delete your hooks directory and type this command.
ionic hooks add
It will solve your problem.

Please check your hooks directory have execute permissions.Give all permission to hooks and it's child folders

It also fails with error code 1 if you have a mistake in any of your files. Check to make sure it hasn't told you about any errors in you files before that.

In my case, I created an after_prepare hook to override cordova build process to use the actual versionCode specified in config.xml. I had the same error.
I solved it by adding "#!/usr/bin/env node" in my hook file(without quotes) at the top.That resolved everything. Hope it may help you .

I had a similar issue with a Moodle Mobile 2 build returning this error - I was missing the 'node_modules' folder from the main directory for the build to complete.
Hope this helps

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) { }".

Saving file to Downloads directory using Ionic 3

i know this link: https://cordova.apache.org/docs/en/latest/reference/cordova-plugin-file/#where-to-store-files
but i would like to save the file in Downloads directory. Is this possible to save the file in any path using Ionic? If so, please, share the example.
Here's the code:
downloadImage(image) {
this.platform.ready().then(() => {
const fileTransfer: TransferObject = this.transfer.create();
const imageLocation = `${cordova.file.applicationDirectory}www/assets/img/${image}`;
fileTransfer.download(imageLocation, cordova.file.externalDataDirectory + image).then((entry) => {
const alertSuccess = this.alertCtrl.create({
title: `Download Succeeded!`,
subTitle: `${image} was successfully downloaded to: ${entry.toURL()}`,
buttons: ['Ok']
});
alertSuccess.present();
}, (error) => {
const alertFailure = this.alertCtrl.create({
title: `Download Failed!`,
subTitle: `${image} was not successfully downloaded. Error code: ${error.code}`,
buttons: ['Ok']
});
alertFailure.present();
});
});
}
Basically I want save the file in location that is visible to the user.
the problem was lack of permission. Here is the working code that can download file to downloads directory:
async downloadFile() {
await this.fileTransfer.download("https://cdn.pixabay.com/photo/2017/01/06/23/21/soap-bubble-1959327_960_720.jpg", this.file.externalRootDirectory +
'/Download/' + "soap-bubble-1959327_960_720.jpg");
}
getPermission() {
this.androidPermissions.hasPermission(this.androidPermissions.PERMISSION.READ_EXTERNAL_STORAGE)
.then(status => {
if (status.hasPermission) {
this.downloadFile();
}
else {
this.androidPermissions.requestPermission(this.androidPermissions.PERMISSION.READ_EXTERNAL_STORAGE)
.then(status => {
if(status.hasPermission) {
this.downloadFile();
}
});
}
});
}
To download the File to the Download directory you need to use Cordova File and FileTransfer Plugins.
import { File } from '#ionic-native/file';
import { FileTransfer } from '#ionic-native/file-transfer';
constructor(private transfer: FileTransfer) { }
fileTransfer: FileTransferObject = this.transfer.create();
//Use your File Url and name
downloadFile(file) {
// Some Loading
this.fileTransfer.download(url, this.file.externalRootDirectory +
'/Download/' + file).then(response => {
console.log(response);
this.dismissLoading();
this.presentToast('File has been downloaded to the Downloads folder. View
it..')
})
.catch(err => {
this.dismissLoading();
console.log(err)
});
}
Hope it helps.
import { File } from '#ionic-native/file';
import { FileTransfer } from '#ionic-native/file-transfer';
constructor(private file: File, private transfer: FileTransfer){}
let link = 'url_to_download_file';
let path = '';
let dir_name = 'Download'; // directory to download - you can also create new directory
let file_name = 'file.txt'; //any file name you like
const fileTransfer: FileTransferObject = this.transfer.create();
let result = this.file.createDir(this.file.externalRootDirectory, dir_name, true);
result.then((resp) => {
path = resp.toURL();
console.log(path);
fileTransfer.download(link, path + file_name).then((entry) => {
console.log('download complete: ' + entry.toURL());
}, (error) => {
console.log(error)
});
}, (err) => {
console.log('error on creating path : ' + err);
});
I know this is late, but I've always had issues with the FileTransfer plugin. Maybe it is just me. I've instead had success with the writeFile() method of the File plugin.
I'm still working on iOS, but for Android here is what I have:
import { File } from "#ionic-native/file";
constructor(private fileSystem: File) {}
Then, in whatever function you have the logic to save the file, we have:
let path = this.fileSystem.externalRootDirectory + '/Download/'; // for Android
let filename = 'myNewFile.pdf';
this.fileSystem.writeFile(path, filename, File, { replace: true }).then(() => {
this.toastCtrl.showToast('File has been downloaded. Please check your downloads folder.');
}, (err) => {
alert("Sorry. An error occurred downloading the file: " + err);
}
);
As I said, I'm still looking out for what path to use for iOS. And I'm still wondering how to pop up the notification that usually comes up when a download actually goes to the download folder. But at least I am able to save directly in the download folder of Android.
This code - ionic 3 capacitor - from josh morony takes a photo from the tmp directory and writes to the Document directory in this section using the FileSystem API the retrieves and manipulates the path
Filesystem.writeFile({
data: result.data,
path: fileName,
directory: FilesystemDirectory.Data
})
getFromPhotos() {
let options = {
resultType: CameraResultType.Uri
};
Camera.getPhoto(options).then(
(photo) => {
Filesystem.readFile({
path: photo.path
}).then((result) => {
// let date = new Date(),
// time = date.getTime(),
time = 'bilder',
fileName = time + '.jpeg';
Filesystem.writeFile({
data: result.data,
path: fileName,
directory: FilesystemDirectory.Data
}).then((result) => {
Filesystem.getUri({
directory: FilesystemDirectory.Data,
path: fileName
}).then((result) => {
console.log(result);
let path = result.uri.replace('file://', '_capacitor_');
this.image = this.sanitizer.bypassSecurityTrustResourceUrl(path);
}, (err) => {
console.log(err);
});
}, (err) => {
console.log(err);
});
}, (err) => {
console.log(err);
});
}, (err) => {
console.log(err);
}
);
}
In ionic 3 you have to use the cordova File plugin - please google. It is pretty straight forward to understand: you define the original directory where the file is, the original name of the file, the target directory, and a new name for the file inside that function. The principle is the same.
To download the File to the Download directory you need to use Cordova File Plugin:
import { File } from '#ionic-native/file/ngx';
constructor(
private file: File,
) { }
this.file.writeFile(this.file.externalRootDirectory + '/Download/', user_log.xlsx, blob, { replace: true })
.then(() => {
alert('File has been downloaded. Please check your downloads folder.')
enter code here
},
(err) => {
alert("Sorry. An error occurred downloading the file: " + err);
enter code here
});
})
It works in Ionic 4 as well.

Not seeing files that are store on the Android device using cordova -file plugin

I am trying to display a list of file that are store on the android device in my cordova app and I am using the cordova -file plugin .However, I am not seeing the file when I select the browse button in my app but I see the file in the system "My Files" android app.
Here is the list of folder that I am iterating thru
[cordova.file.externalRootDirectory,cordova.file.dataDirectory]
On the android phone, I see the files bu doing the following:
select /settings/storage/internal storage
selecting explore
On the emnu the heading says MyFiles>device storage
select data or download ..
On the device
cordova.file.externalRootDirectory resolve to file:///storage/emulated/0/download
However, I don't see any files
Here my code
$scope.showLocalFileOnAndroid = function () {
$scope.showLocalAndroidFiles = true;
var localURLs = [cordova.file.externalRootDirectory,cordova.file.dataDirectory
];
var index = 0;
var i;
var errorStr = '';
var fileList = [];
var addFileEntry = function (entry) {
var dirReader = entry.createReader();
dirReader.readEntries(
function (entries) {
var i;
for (i = 0; i < entries.length; i++) {
if (entries[i].isDirectory === true) {
// Recursive -- call back into this subdirectory
addFileEntry(entries[i]);
} else {
var ext = entries[i].name.split('.').pop();
if (ext === 'doc' || ext === 'docx' ||
ext === 'rdf' || ext === 'pdf' || ext === 'txt' ||
ext === 'odt') {
fileList.push(entries[i]); // << replace with something useful
}
index++;
}
}
},
function (error) {
console.log('readEntries error: ' + error.code);
errorStr += '<p>readEntries error: ' + error.code + '</p>';
}
);
};
var addError = function (error) {
console.log('getDirectory error: ' + error.code);
errorStr += '<p>getDirectory error: ' + error.code + ', ' + error.message + '</p>';
};
for (i = 0; i < localURLs.length; i++) {
if (localURLs[i] === null || localURLs[i].length === 0) {
continue; // skip blank / non-existent paths for this platform
}
window.resolveLocalFileSystemURL(localURLs[i], addFileEntry, addError);
}
$scope.fileList = fileList;
$scope.localFileError = errorStr;
};
Here's something. Maybe you'll have to do something like this. This is probably not as portible between devices as using the cordova plugin though.
window.requestFileSystem(LocalFileSystem.PERSISTENT, 0, onFileSystemSuccess, fail);
var localURLs = [cordova.file.externalRootDirectory,
cordova.file.dataDirectory,
"file:///Download"]; // or "file:///sdcard/Download" or "file:///storage/Download" or "file:///storage/download" or something
I used this as reference.
Perhaps all you were missing was the call to requestFileSystem.

Allow user to specify Gradle dependency for Cordova plugin

I am writing a Cordova plugin for android that has a Gradle dependency.
I want the developers to be able to specify what version of the dependency they want without editing the plugin directly.
Right now I have this in my plugin.xml:
<hook type="before_plugin_install" src="modifyPluginXML.js" />
<framework src="resources/custom.gradle" custom="true" type="gradleReference"/>
<framework src="$TO_BE_DEFINED_PACKAGE" type="gradleReference"/>
and I have a hook that takes the command line argument and replaces $TO_BE_DEFINED_PACKAGE in the plugin.xml with the package path/name provided in the argument.
Here is the modifyPluginXML.js hook:
module.exports = function(context) {
var Q = context.requireCordovaModule('q');
var deferral = new Q.defer();
var fs = require('fs'),
xml2js = require('xml2js'),
path = require('path'),
util = require('util');
var parser = new xml2js.Parser({explicitArray:false});
var cb = function(data){
console.log("plugin.xml updated");
deferral.resolve();
}
fs.readFile(__dirname + '/plugin.xml', function(err, data) {
parser.parseString(data, function (err, result) {
//console.log(util.inspect(result, false, null));
var externalDep = "";
for (var i = 0; i < process.argv.length;i++){
if(process.argv[i].indexOf('EXTERNAL_DEP') >= 0){
externalDep = process.argv[i].replace("EXTERNAL_DEP=", "");
console.log(externalDep);
}
}
result.plugin.platform.framework[1]['$'].src = externalDep;
var builder = new xml2js.Builder();
var xml = builder.buildObject(result);
var filepath = path.normalize(path.join(__dirname, '/plugin.xml'));
fs.writeFile(filepath, xml, cb);
});
});
return deferral.promise;
}
As of right now if you add the plugin with cordova plugin add plugin-name EXTERNAL_DEP=5.0 it will correctly replace the framework src in the plugin.xml with the source specified in the command line argument.
The problem I am running into is that cordova doesn't seem to care about the new plugin.xml. It still uses the old plugin.xml's framework tags.
In the generate build.gradle file I still see this:
dependencies {
compile fileTree(dir: 'libs', include: '*.jar')
// SUB-PROJECT DEPENDENCIES START
debugCompile project(path: "CordovaLib", configuration: "debug")
releaseCompile project(path: "CordovaLib", configuration: "release")
compile "$TO_BE_DEFINED_PACKAGE"
// SUB-PROJECT DEPENDENCIES END
}
So even though the plugin.xml is getting updated correctly with the before_plugin_install hook, cordova uses the old value to generate the build.gradle file.
Does anyone have any suggestions, or different routes I could take?
You don't have to write in the plugin.xml because that is only read on plugin install.
If you want to edit the gradle references you can write on the platforms/android/build.gradle file, that is where the references from the plugin.xml are copied.
Anyway, I don't think is a good idea to allow users to choose the version to use, you, as developer of the plugin, should choose the version that you tested and you are sure that works fine, using a different version might do the plugin to not work correctly or not work at all.
Took #jcesarmobile advice and instead wrote to the build.gradle file. I first check the cmd line for any arguments than I check the config.xml for any variables set for the specific plugin. Here is the script I came up with.
module.exports = function (context) {
var Q = context.requireCordovaModule('q');
var deferral = new Q.defer();
var result = null;
var externalDep = null;
var fs = require('fs'),
xml2js = require('xml2js'),
path = require('path'),
util = require('util');
var gradleLocation = process.cwd() + "/platforms/android/build.gradle";
var parser = new xml2js.Parser({explicitArray: false});
function findPluginVarInConfig() {
fs.readFile(process.cwd() + '/config.xml', function (err, data) {
parser.parseString(data, function (err, result) {
//console.log(util.inspect(result, false, null));
for (var i = 0; i < result.widget.plugin.length; i++) {
if (result.widget.plugin[i]['$'].name == 'plugin-name') {
externalDep = result.widget.plugin[i].variable['$'].value;
}
}
if (externalDep) {
console.log("found " + externalDep + " in config.xml");
replaceGradleReference();
} else {
console.log("plugin-name could not find defined dependency defined in config.xml or cmd line args defaulting to 0.0.1");
externalDep = "0.0.1";
replaceGradleReference();
}
});
});
}
function findPluginVarInCmdArgs() {
for (var i = 0; i < process.argv.length; i++) {
if (process.argv[i].indexOf('EXTERNAL_DEP') >= 0) {
externalDep = process.argv[i].replace("EXTERNAL_DEP=", "");
}
}
if (externalDep) {
console.log("found " + externalDep + " in command line args");
replaceGradleReference();
} else {
findPluginVarInConfig();
}
}
function replaceGradleReference() {
fs.readFile(gradleLocation, 'utf8', function (err, data) {
if (err) {
return console.log(err);
}
var replaced = false;
if (data.indexOf('$INITIAL_PBR_SOURCE' >= 0)) {
result = data.replace('$INITIAL_PBR_SOURCE', function (text) {
replaced = true;
return externalDep
});
if (!replaced) {
console.log("FAILED TO ADD " + externalDep + " TO BUILD SCRIPT");
}
}
if (result) {
fs.writeFile(gradleLocation, result, 'utf8', function (err) {
if (err) {
console.log(err);
} else {
console.log("Succesfully added " + externalDep + " to buildscript");
}
deferral.resolve();
});
} else {
console.log("PBR external dependency is already added to buildscript");
}
});
}
findPluginVarInCmdArgs();
return deferral.promise;
};

Cordova FileOpener Android - can't find a local file location

Using Cordova FileOpener2, and many others plugins, like Cordova File, Cordova FileTransfer.. I can't found my local PDF location in Android.
This file is in the www/offline-files/ directory and when I opened it in iOS (with window.open(encodeURI('offline-files/myFile.pdf'), '_blank');) it works fine !
If I'm trying the same thing in Android, it doesn't work. For example, one of my many tries:
function getPath()
{
// Get local path for Cordova
var path = window.location.pathname;
path = path.substr(path, path.length - 10);
return 'file://' + path;
}
cordova.plugins.fileOpener2.open(
getPath() + 'offline-files/myFile.pdf',
'application/pdf',
{
error: function (e)
{
console.log('Error status: ' + e.status + ' - Error message: ' + e.message);
},
success: function ()
{
console.log('file opened successfully');
}
}
);
Cordova returns me the log: "Error status: 9 - Error message: File not found"
There's also a strange error: Cordova file plugin returns me an error file not found for the classic directory:
window.resolveLocalFileSystemURL(cordova.file.applicationDirectory,
function (dir)
{
/** SOME CODE **/
}
);
What version of Cordova are you using? In any case, I wouldn't hardcode the filepath and get it from the LocalFileSystem object instead.
var fs;
function fsSuccess(fileSystem)
{
fs = fileSystem;
}
function fsFail(event)
{
console.log(event.target.error.code);
}
window.requestFileSystem = window.requestFileSystem || window.webkitRequestFileSystem;
window.requestFileSystem(LocalFileSystem.PERSISTENT, 0, fsSuccess, fsFail);
Then access the root path via:
fs.root.toURL() + "yourfilename"
Hope this helps!

Categories

Resources