I am developing an ionic app. I am using cordova's FileTransfer plugin to download pdf file. I am able to download the file to my internal memory,but not able to display single progress bar for downloaidng.
What is the best way of displaying progress for downloading.
Controller
var url = 'http://someurl.com/api/pdf_download/' + id;
// Android
var targetPath = 'file:///storage/sdcard0/' + id + '.pdf';
var trustHosts = true;
var options = {};
$cordovaFileTransfer.download(url, targetPath, options, trustHosts)
.then(function(result) {
console.log(result);
}, function() {
var alertPopup = $ionicPopup.alert({
title: 'No internet access',
buttons: [{
text: 'OK',
type: 'button-assertive'
}]
});
alertPopup.then(function() {});
}, function(progress) {
$timeout(function() {
$scope.downloadProgress = (progress.loaded / progress.total) * 100;
})
console.log('progress--->', $scope.downloadProgress);
});
I have used cordovaToast plugin for this feature.Here is the example for showing pdf download progress
html
<ion-view >
<div class="bar bar-subheader bar-positive" style="padding:0px;height: 8px;" >
<progress id="progressbar" max="100" value="{{ downloadProgress }}" class="progress"> </progress>
</div>
<ion-content>
</ion-content>
</ion-view>
css
.progress {
margin: 0 px;
height: 8 px;
background - color: #F1292B!important;
border - radius: 2 px;
box - shadow: 0 2 px 5 px rgba(0, 0, 0, 0.25) inset;
}
js
if (window.cordova) {
var url = '{{base_url}}/pdf_download/' + id;
// Android
var targetPath = 'file:///storage/sdcard0/' + 'fpl_' + id + '.pdf';
var trustHosts = true;
var options = {};
$cordovaFileTransfer.download(url, targetPath, options, trustHosts)
.then(function(result) {
$cordovaToast
.show('File downloaded successfully..', 'short', 'center')
.then(function() {
// success
}, function() {
// error
});
console.log(result);
}, function() {
var alertPopup = $ionicPopup.alert({
title: 'No internet access',
buttons: [{
text: 'OK',
type: 'button-assertive'
}]
});
alertPopup.then(function() {});
}, function(progress) {
var dnldpgs = progress.loaded.toString().substring(0, 2);
$scope.downloadProgress = parseInt(dnldpgs);
});
}
As benka already answered to my question, you should use the <progress> html element.
The full answer can be found over here -> https://stackoverflow.com/a/25553044/3671726
Related
I have the error like in question, when I'm trying to design my application to call native.camera, I see my console in ionic 3 project, I saw this error :
Native : tried calling Camera.getPicture, but Cordova is not available. Make sure to include cordova.js or run in a device / simulator.
Here is the code that I used to called native camera.
This is the code in my problem.html
<button class="logoCamera" ion-button (click)="presentActionSheet()">
<ion-icon name="camera" ></ion-icon>
This is the code in my problem.ts
import { File } from '#ionic-native/file';
import { Transfer, TransferObject} from '#ionic-native/transfer';
import { FilePath } from '#ionic-native/file-path';
import { Camera } from '#ionic-native/camera';
public presentActionSheet(){
let actionSheet = this.actionSheetCtrl.create({
title: 'Select Image',
buttons: [
{
text: 'Load from Library',
handler: () => {
this.takePicture(this.camera.PictureSourceType.PHOTOLIBRARY);
}
},
{
text: 'Use Camera',
handler: () => {
this.takePicture(this.camera.PictureSourceType.CAMERA);
}
},
{
text: 'Cancel',
role: 'cancel'
}
]
});
actionSheet.present();
}
public takePicture(sourceType){
//Create option for the Camera dialog
var options = {
quality: 100,
sourceType : sourceType,
saveToPhotoAlbum: false,
correctOrientation: true
};
//Get the data of an image
this.camera.getPicture(options).then((imagePath) => {
//special handling for android lib
if(this.platform.is('android') && sourceType === this.camera.PictureSourceType.PHOTOLIBRARY) {
this.filePath.resolveNativePath(imagePath)
.then(filePath => {
let correctPath = filePath.substr(0, filePath.lastIndexOf('/') + 1 );
let currentName = imagePath.substring(imagePath.lastIndexOf('/') + 1, imagePath.lastIndexOf('?'));
this.copyFileToLocalDir(correctPath, currentName, this.createFileName());
});
} else {
var currentName = imagePath.substr(imagePath.lastIndexOf('/') + 1);
var correctPath = imagePath.substr(0, imagePath.lastIndexOf('/')+ 1);
this.copyFileToLocalDir(correctPath, currentName, this.createFileName());
}
}, (err) => {
this.presentToast('Error while selecting Image.');
});
}
//Create a new name for image
private createFileName() {
var d = new Date(),
n = d.getTime(),
newFileName = n + ".jpg";
return newFileName;
}
//copy image to local folder
private copyFileToLocalDir(namePath, currentName, newFileName) {
this.file.copyFile(namePath, currentName, cordova.file.dataDirectory, newFileName).then(success => {
this.lastImage = newFileName;
}, error => {
this.presentToast('Error while storing file.');
});
}
private presentToast(text) {
let toast = this.toastCtrl.create({
message: text,
duration: 3000,
position: 'middle'
});
toast.present();
}
public pathForImage(img){
if (img === null) {
return '';
} else {
return cordova.file.dataDirectory + img;
}
}
public uploadImage() {
//destination URL
var url = "";
//file to upload
var targetPath = this.pathForImage(this.lastImage);
//file name only
var filename = this.lastImage;
var options = {
fileKey: "file",
fileName: filename,
chunkedMode: false,
mimeType: "multipart/form-data",
params: {'fileName': filename}
};
const fileTransfer: TransferObject = this.transfer.create();
this.loading = this.loadingCtrl.create({
content: 'Uploading...',
});
this.loading.present();
//use FileTransfer to upload image
fileTransfer.upload(targetPath, url, options).then(data => {
this.loading.dismissAll()
this.presentToast('Image successful uploaded.');
}, err => {
this.loading.dismissAll()
this.presentToast('Error while uploading file.');
});
}
When I run ionic serve, everything is smooth, no error, no nothing.
But when I click my button to access natve camera, the error shows, please help me figure out the problem, I check a lot of web, and none of it solve my question.
After I try run ionic cordova run ios --simulator, there are error coming out, but I am pretty sure that this error does not exist before I run this command.
May I know how to solve this problem ??
The error message is pretty accurate here:
Native : tried calling Camera.getPicture, but Cordova is not available. Make sure to include cordova.js or run in a device / simulator.
Running ionic serve does not include cordova.js nor does it run your application in a simulator or on a device which is why you get the error. You can fix it either by running your application on the device or simulator:
ionic cordova run android/ios --device/--simulator
Or by adding the browser platform:
cordova platform add browser
And running the browser platform:
ionic cordova run browser
Video recording works fine on IOS, Android can't catch data.
problem seems to be the
var curActivity = Ti.Android.currentActivity;
curActivity.startActivityForResult(intent, function(e) { ....
there was some advise to use win.getActivity() instead, but i have no variable I can use.
$.cameraWin
this
is not working.
Any advise?
index.xml
<Alloy>
<TabGroup>
<Tab id="websiteTab" title="Web">
<Require backgroundColor="black" color="white" id="webTab" src="website" type="view"/>
</Tab>
<Tab id="cameraTab" title="Camera">
<Require backgroundColor="black" color="white" id="cameraTab" src="camera" type="view"/>
</Tab>
<Tab backgroundColor="black" color="white" id="loginTab" title="Login">
<Require backgroundColor="black" color="white" id="loginTab" src="login" type="view"/>
</Tab>
<Tab backgroundColor="black" color="white" id="registerTab" title="Map">
<Require backgroundColor="black" color="white" id="registerTab" src="register" type="view"/>
</Tab>
</TabGroup>
</Alloy>
camera.xml
<Alloy>
<Window id="cameraWin">
<Label id="Label_1" text="Gib Deinem Video einen Namen"/>
<TextField id="TextField_1"/>
<Button id="Button_1" onClick="doClick" title="Aufnehmen und hochladen"/>
<ProgressBar id="ProgressBar_1"/>
<Picker id="Picker_1" selectionIndicator="true" useSpinner="true">
<PickerColumn id="PickerColumn_1" title="Kategorie">
<Row title="Fußball"/>
<Row title="Handball"/>
<Row title="Schifahren"/>
<Row title="Einkehren"/>
</PickerColumn>
</Picker>
<Label id="Label_2" text="Veranstaltung/Kategorie"/>
</Window>
</Alloy>
camera.js
function doClick(e) {
Ti.API.info(Titanium.Platform.osname);
if (Titanium.Platform.osname == 'iphone') {
//record for iphone
$.ProgressBar_1.value = 0;
$.ProgressBar_1.message = "Hochladen"
Titanium.Media.showCamera({
success: function(event) {
var video = event.media;
movieFile = Titanium.Filesystem.getFile(
Titanium.Filesystem.applicationDataDirectory,
'mymovie.mov');
movieFile.write(video);
videoFile = movieFile.nativePath;
var xhr = Titanium.Network.createHTTPClient();
xhr.onload = function(e) {
Ti.UI.createAlertDialog({
title: 'Success',
message: 'status code ' + this.status
}).show();
Ti.API.info(this.responseText);
};
xhr.open('POST', 'XXXXXXXXXX');
xhr.send({
Filedata: event.media,
/* event.media holds blob from gallery */
title: $.TextField_1.value,
catid: 17
});
// onsendstream called repeatedly, use the progress property to
// update the progress bar
xhr.onsendstream = function(e) {
$.ProgressBar_1.value = e.progress * 100;
$.ProgressBar_1.message = "Hochladen von Video";
Ti.API.info('ONSENDSTREAM - PROGRESS: ' + e.progress * 100);
};
},
cancel: function() {},
error: function(error) {
// create alert
var a =
Titanium.UI.createAlertDialog({
title: 'Video'
});
// set message
if (error.code == Titanium.Media.NO_VIDEO) {
a.setMessage('Device does not have video recording
capabilities ');
} else {
a.setMessage('Unexpected error: ' + error.code);
}
// show alert
a.show();
},
mediaTypes: Titanium.Media.MEDIA_TYPE_VIDEO,
videoMaximumDuration: 120000,
videoQuality: Titanium.Media.QUALITY_MEDIUM
});
} else {
var intent = Titanium.Android.createIntent({
action: 'android.media.action.VIDEO_CAPTURE'
});
Ti.API.info('Intent created. ..');
var curActivity = Ti.Android.currentActivity;
curActivity.startActivityForResult(intent, function(e) {
if (e.error) {
Ti.UI.createNotification({
duration: Ti.UI.NOTIFICATION_DURATION_LONG,
message: 'Error: ' + e.error
}).show();
} else {
Ti.API.info('Drinnen');
Ti.Api.info(e.resultCode);
if (e.resultCode === Titanium.Android.RESULT_OK) {
Ti.API.info('Drinnen');
videoFile = e.intent.data;
var source = Ti.Filesystem.getFile(videoFile);
var movieFile =
Titanium.Filesystem.getFile(Ti.Filesystem.applicationDataDirectory, 'mymovie.3gp');
Ti.API.info('Sichert');
source.copy(movieFile.nativePath);
Titanium.Media.saveToPhotoGallery(movieFile);
var xhr = Titanium.Network.createHTTPClient();
xhr.onload = function(e) {
Ti.UI.createAlertDialog({
title: 'Success',
message: 'status code ' + this.status
}).show();
Ti.API.info(this.responseText);
};
var fileContent = movieFile.read();
Ti.API.info('Video rauf');
xhr.open('POST', 'XXXXXXXXXXX'
Filedata: fileContent,
/* event.media holds blob from gallery */
title: $.TextField_1.value,
catid: 17
});
// onsendstream called repeatedly, use the progress property to
// update the progress bar
xhr.onsendstream = function(e) {
$.ProgressBar_1.value = e.progress * 100;
$.ProgressBar_1.message = "Hochladen von Video";
Ti.API.info('ONSENDSTREAM - PROGRESS: ' + e.progress * 100);
};
} else {
Ti.UI.createNotification({
duration: Ti.UI.NOTIFICATION_DURATION_LONG,
message: 'Canceled/Error? Result code: ' +
e.resultCode
}).show();
}
}
});
}
};
Alternative way:
I'm currently working on getting video recording to android in the normal SDK:
https://github.com/appcelerator/titanium_mobile/pull/7929
It is already working (Samsung Galaxy S6 has a problem a the moment I need to fix: you need to rotate the phone ones to have the proper preview size) but needs some more testing. With this you can use the back/front camera to record video the same way as on iOS.
But it still needs some time to be in the GA version. You can always compile an own sdk for yourself if you need it right away (I can provide a linux build).
I have a similar project, though mine is taking stills, not video, but for all intents and purposes they should behave the same.
In my code I have:
var win = $.camera_view;
This allows me later to start my activity with:
win.activity.startActivityForResult(...
Per your example you would probably set curActivity like this:
var curActivity = $.cameraWin.activity;
[EDIT]
If you still get no result you can try using .putExtraUri to specify the location to store the video:
var tempfile = Ti.Filesystem.getFile(Ti.Filesystem.externalStorageDirectory, "tempvideo.mpg");
intent.putExtraUri("output",tempfile.nativePath);
You would set this before calling startActivityForResult.
Am trying to user the $cordovaFileTransfer plugin from ngCordovato upload images in my ionic app.
However the images, never seem to upload. The app is running on my android phone and am debugging with chrome remote inspector, the network tab does not show any request for the upload.
This method gets images from the device
$scope.getImages = function() {
var options = {
maximumImagesCount: 2,
width: 800,
height: 800,
quality: 80
};
$cordovaImagePicker.getPictures(options)
.then(function (results) {
for (var i = 0; i < results.length; i++) {
$scope.queue.push({
filepath: results[i],
progress: 0.00
});
}
$scope.$emit('process:queue');
}, function(error) {
// error getting photos
});
};
And am handling the 'process:queue' event with this method
$scope.startUploadQueue = function() {
var server = encodeURI(serviceUrl());
var options = {
fileKey: "image",
httpMethod: 'PUT',
headers: {
"Authorization": "Bearer "+$user.getToken()
}
};
angular.forEach($scope.queue, function(item) {
$cordovaFileTransfer.upload(server, item.filepath, options, true)
.then(function (result) {
console.log(result);
$scope.media.push(result.data);
}, function (error) {
}, function (progress) {
item.progress = (progress.loaded / progress.total) * 100;
});
});
};
Am doing something incorrectly?. Any help will be appreciated
Let's say I have a API that stores some .mp3 music.
The sample link here:
https://118.69.201.34:8882/api/ApiMusic/Download?songId=2000
Now I want to write an API calling function in Angularjs to download the music to my Android devices with the song's Id number as in the link.
How can I do that? Please help :(
You can use the ngCordova FileTransfer library here: http://ngcordova.com/docs/plugins/fileTransfer/
Here's example code from that page, tweaked to your example URL:
document.addEventListener('deviceready', function () {
var fileid = "2000";
var url = "https://118.69.201.34:8882/api/ApiMusic/Download?songId=" + fileid;
var targetPath = cordova.file.documentsDirectory + fileid + ".mp3";
var trustHosts = true
var options = {};
$cordovaFileTransfer.download(url, targetPath, options, trustHosts)
.then(function(result) {
// Success!
}, function(err) {
// Error
}, function (progress) {
$timeout(function () {
$scope.downloadProgress = (progress.loaded / progress.total) * 100;
})
});
}, false);
I did it finally, here is my code. Just share for those who want to refer to this issue in the future. Thanks you guys for your answers
$scope.download = function(songId, songName) {
$ionicLoading.show({
template: 'Downloading...'
});
window.requestFileSystem(LocalFileSystem.PERSISTENT, 0, function (fs) {
fs.root.getDirectory(
"fMusic",
{
create: true
},
function (dirEntry) {
dirEntry.getFile(
songName + ".mp3",
{
create: true,
exclusive: false
},
function gotFileEntry(fe) {
var p = fe.toURL();
fe.remove();
ft = new FileTransfer();
ft.download(
encodeURI(APIUrl + songId),
p,
function (entry) {
$ionicLoading.hide();
$scope.mp3File = entry.toURL();
},
function (error) {
$ionicLoading.hide();
alert("Download Error Source --> " + error.source);
},
false,
null
);
},
function () {
$ionicLoading.hide();
console.log("Get the file failed");
}
);
}
);
},
function () {
$ionicLoading.hide();
console.log("Request for filesystem failed");
});
}
I am trying to implement Videogular in my AngularJS App. The out of the box example works nicely, no issue. But I am unable to ask the player manually to play a different file, instead of the running audio.
Here is my HTML.
<div ng-controller="HomeCtrl as controller" class="videogular-container" ng-model="sharedProperty">
sharedProperty.data = {{sharedProperty.data}}
<button ng-click="SetValue('http://example.com/myfile.mp3')" type="button" class="btn btn-default">Change Audio</button>
<videogular vg-theme="controller.config.theme.url" class="videogular-container audio">
<vg-media vg-src="controller.config.sources"></vg-media>
<vg-controls>
<vg-play-pause-button></vg-play-pause-button>
<vg-time-display>{{ currentTime | date:'mm:ss' }}</vg-time-display>
<vg-scrub-bar>
<vg-scrub-bar-current-time></vg-scrub-bar-current-time>
</vg-scrub-bar>
<vg-time-display>{{ timeLeft | date:'mm:ss' }}</vg-time-display>
<vg-volume>
<vg-mute-button></vg-mute-button>
</vg-volume>
</vg-controls>
</videogular>
</div>
And this is the controller code:
app.controller('HomeCtrl', ["$sce","$scope", "$window", "sharedProperties",
function($sce, $scope, $window, sharedProperties) {
$scope.sharedProperty = sharedProperties.getProperty();
$scope.SetValue = function (msg)
{
$window.alert( $scope.sharedProperty.data );
$scope.setProperty = sharedProperties.setProperty;
$scope.setProperty(msg);
$window.alert( $scope.sharedProperty.data );
}
$window.alert( $scope.sharedProperty.data );
this.config = {
sources: [{
src: $sce.trustAsResourceUrl( $scope.sharedProperty.data ),
type: "audio/mpeg"
}, {
src: $sce.trustAsResourceUrl("http://static.videogular.com/assets/audios/videogular.ogg"),
type: "audio/ogg"
}],
theme: {
url: "http://www.videogular.com/styles/themes/default/latest/videogular.css"
}
};
}
]
);
The Service code is given here:
app.service('sharedProperties', function () {
var property = {
data: "http://example.com/firstaudio.mp3"
};
return {
getProperty:function () {
return property;
},
setProperty:function (value) {
property.data = value;
}
};
});
When I click on Set Value button, I am able to change the value of sharedProperty.data successfully but I don't know how to ask the player to stop the current audio and play the new file instead.
I'm the creator of Videogular.
If you have set a binding with:
<vg-media vg-src="controller.config.sources"></vg-media>
You only need to change your controller.config.sources and that's all:
app.controller('HomeCtrl', ["$sce","$scope", "$window", "sharedProperties",
function($sce, $scope, $window, sharedProperties) {
$scope.sharedProperty = sharedProperties.getProperty();
$scope.SetValue = function (msg)
{
$window.alert( $scope.sharedProperty.data );
$scope.setProperty = sharedProperties.setProperty;
$scope.setProperty(msg);
$window.alert( $scope.sharedProperty.data );
}
$scope.changeSource = function (source) {
// source should be an array of objects with src and type
this.config.sources = source;
}
$window.alert( $scope.sharedProperty.data );
this.config = {
sources: [{
src: $sce.trustAsResourceUrl( $scope.sharedProperty.data ),
type: "audio/mpeg"
}, {
src: $sce.trustAsResourceUrl("http://static.videogular.com/assets/audios/videogular.ogg"),
type: "audio/ogg"
}],
theme: {
url: "http://www.videogular.com/styles/themes/default/latest/videogular.css"
}
};
}
]);
You have an example here:
https://github.com/2fdevs/videogular/blob/master/app/scripts/controllers/main.js#L102