How to use ImageCache Nativescript core module - android

I'm trying to save and load images from cache using ImageCache NativeScript Core module but it won't work.
<template>
<Page>
<StackLayout>
<Image v-for="exampleImage in exampleImages" :src="getCachedImage(exampleImage.url)"/>
</StackLayout>
</Page>
</template>
<script>
import * as imageCache from 'tns-core-modules/ui/image-cache'
import * as imageSource from 'tns-core-modules/image-source'
export defualt {
data() {
return {
exampleImages: [
{url: 'https://image.tmdb.org/t/p/w600_and_h900_bestv2/kY2c7wKgOfQjvbqe7yVzLTYkxJO.jpg'},
{url: 'https://image.tmdb.org/t/p/w600_and_h900_bestv2/svIDTNUoajS8dLEo7EosxvyAsgJ.jpg'},
{url: 'https://image.tmdb.org/t/p/w600_and_h900_bestv2/A7XkpLfNH0El2yyDLc4b0KLAKvE.jpg'},
]
}
},
methods: {
getCachedImage(imgUrl) {
const cache = new imageCache.Cache();
cache.enableDownload();
const image = cache.get(imgUrl);
let cachedImageSource;
if (image) {
console.log('getting image from cache')
cachedImageSource = imageSource.fromNativeSource(image)
} else {
console.log('downloading image, setting it in cache, and getting from cache')
cache.push({
key: imgUrl,
url: imgUrl,
completed: (image, key) => {
if (imgUrl === key) {
cachedImageSource = imageSource.fromNativeSource(image);
console.log(cachedImageSource)
}
},
error: () => {
console.log('Error')
}
});
}
cache.disableDownload();
return cachedImageSource;
}
}
}
</script>
But then, the output in my console is the following:
iOS:
{ ios: {} }
Android:
{ android:
{ constructor:
{ [Function]
[length]: 0,
[name]: '',
[arguments]: null,
[caller]: null,
[prototype]: [Object],
createBitmap: [Object],
createScaledBitmap: [Object],
extend: [Object],
CREATOR: [Object],
DENSITY_NONE: 0,
CONTENTS_FILE_DESCRIPTOR: 1,
PARCELABLE_WRITE_RETURN_VALUE: 1,
null: [Circular],
class: [Object],
CompressFormat: [Object],
Config: [Object] } } }
And of course is always outputing: downloading image, setting it in cache, and getting from cache and never getting image from cache. The image is never displayed, never saved in cache and never obtained from cache.
I don't know what I'm I doing wrong.
Thanks in advance.

Image download is asynchronously, so you can not use a direct return statement. You have to wait for the complete callback and update your data with image url.
<template>
<Page class="page">
<ActionBar title="Home" class="action-bar" />
<ScrollView>
<StackLayout>
<Image v-for="exampleImage in exampleImages" :src="exampleImage.src" />
</StackLayout>
</ScrollView>
</Page>
</template>
<script>
import * as imageCache from "tns-core-modules/ui/image-cache";
import * as imageSource from "tns-core-modules/image-source";
export default {
data() {
return {
exampleImages: [{
url: "https://image.tmdb.org/t/p/w600_and_h900_bestv2/kY2c7wKgOfQjvbqe7yVzLTYkxJO.jpg",
src: null
},
{
url: "https://image.tmdb.org/t/p/w600_and_h900_bestv2/svIDTNUoajS8dLEo7EosxvyAsgJ.jpg",
src: null
},
{
url: "https://image.tmdb.org/t/p/w600_and_h900_bestv2/A7XkpLfNH0El2yyDLc4b0KLAKvE.jpg",
src: null
}
]
};
},
methods: {
getCachedImage(exampleImage) {
const cache = new imageCache.Cache();
cache.enableDownload();
const image = cache.get(exampleImage.url);
let cachedImageSource;
if (image) {
console.log("getting image from cache");
exampleImage.src = imageSource.fromNativeSource(image);
} else {
console.log(
"downloading image, setting it in cache, and getting from cache"
);
cache.push({
key: exampleImage.url,
url: exampleImage.url,
completed: (image, key) => {
exampleImage.src = imageSource.fromNativeSource(
image);
},
error: () => {
console.log("Error");
}
});
}
// cache.disableDownload();
}
},
created() {
for (let x in this.exampleImages) {
this.getCachedImage(this.exampleImages[x]);
}
}
};
</script>
Updated Playground

Related

I'm getting 'undefined is not an object (near '...}).fs.writeFile(' when trying to download base64 image png in react native React Native

Here is my code:
const saveImg = async (base64Img: string, success: Function, fail:Function) => {
const isAndroid = Platform.OS === "android"
const isIos = Platform.OS === 'ios'
const dirs = isIos? RNFS.LibraryDirectoryPath : RNFS.ExternalDirectoryPath;
const certificateTitle = 'certificate-'+((Math.random() * 10000000) | 0)
const downloadDest = `${dirs}/${certificateTitle}.png`;
const imageDatas = base64Img.split('data:image/png;base64,');
const imageData = imageDatas[1];
try{
await RNFetchBlob.config({
addAndroidDownloads:{
notification:true,
description:'certificate',
mime:'image/png',
title:certificateTitle +'.png',
path:downloadDest
}
}).fs.writeFile(downloadDest, imageData, 'base64')
if (isAndroid) {
} else {
RNFetchBlob.ios.previewDocument(downloadDest);
}
success()
}catch(error:any){
console.log(error)
fail()
}
}
I get this error:
undefined is not an object (near '...}).fs.writeFile(downloadD...')
at node_modules/react-native-webview/lib/WebView.android.js:207:16 in _this.onMessage
When I hit the download button and this runs I get the mentioned Error.
I use to get the download done with the below code modification, but I really need to show the download feedback from both android and IOS.
This works (but without notification)
await RNFetchBlob.fs.writeFile(downloadDest, imageData, 'base64')
I am using expo
I discovered that the react-fetch-blob does not work with expo, to solve it, I used the following libraries:
expo-file-system, expo-media-library, expo-image-picker,expo-notifications
This was the code to convert, download and show the notification of the image in the "expo way":
import * as FileSystem from 'expo-file-system';
import * as MediaLibrary from 'expo-media-library';
import * as ImagePicker from 'expo-image-picker';
import * as Notifications from 'expo-notifications';
const saveImg = async (base64Img: string, success: Function, fail:Function) => {
const imageDatas = base64Img.split('data:image/png;base64,');
const imageData = imageDatas[1];
try {
const certificateName = 'certificate-'+((Math.random() * 10000000) | 0) + ".png"
const certificatePathInFileSystem = FileSystem.documentDirectory +certificateName ;
await FileSystem.writeAsStringAsync(certificatePathInFileSystem, imageData, {
encoding: FileSystem.EncodingType.Base64,
});
await MediaLibrary.saveToLibraryAsync(certificatePathInFileSystem);
Notifications.setNotificationHandler({
handleNotification: async () => ({
shouldShowAlert: true,
shouldPlaySound: false,
shouldSetBadge: true,
}),
});
await Notifications.scheduleNotificationAsync({
content: {
title: certificateName +' saved !',
body: "Click to show the certificate",
},
trigger: null,
});
setCertificatePath(certificatePathInFileSystem)
success()
} catch (e) {
console.error(e);
fail()
}
}
In order to open the images gallery on click I used this code:
useEffect(()=>{
if(certificatePath){
Notifications.addNotificationResponseReceivedListener( async (event )=> {
await ImagePicker.launchImageLibraryAsync({
mediaTypes: ImagePicker.MediaTypeOptions.All,
allowsEditing: true,
})
})
}
},[certificatePath])
Try to call fetch after create RNFetchBlob.config
If you just wanna display an Image and not store you can show image as fallows (https://reactnative.dev/docs/next/images#uri-data-images)
<Image
style={{
width: 51,
height: 51,
resizeMode: 'contain'
}}
source={{
uri: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADMAAAAzCAYAAAA6oTAqAAAAEXRFWHRTb2Z0d2FyZQBwbmdjcnVzaEB1SfMAAABQSURBVGje7dSxCQBACARB+2/ab8BEeQNhFi6WSYzYLYudDQYGBgYGBgYGBgYGBgYGBgZmcvDqYGBgmhivGQYGBgYGBgYGBgYGBgYGBgbmQw+P/eMrC5UTVAAAAABJRU5ErkJggg=='
}}
/>
Call fetch on config object:
try{
const fetchConfig = await RNFetchBlob.config({
addAndroidDownloads:{
notification:true,
description:'certificate',
mime:'image/png',
title:certificateTitle +'.png',
path:downloadDest
}
})
fetchConfig.fetch('your.domain.com').fs.writeFile(downloadDest, imageData, 'base64')
if (isAndroid) {
} else {
RNFetchBlob.ios.previewDocument(downloadDest);
}
success()
}catch(error:any){
console.log(error)
fail()
}

ionic 4 (android) get image (OBJECT FILE) from gallery (FILE_URI) and upload via API

I'm trying to implement a simple application using Ionic v4 angular and cordova. Just select a photo and upload it to a parse server (back4app.com). But I couldn't do it.
This is my code:
home.page.ts
import { ParseService } from '../service/parse.service';
import { Camera, CameraOptions } from '#ionic-native/camera/ngx';
import { File } from '#ionic-native/file/ngx';
import { WebView } from '#ionic-native/ionic-webview/ngx';
selectPhoto() {
const options: CameraOptions = {
quality: 100,
sourceType: this.camera.PictureSourceType.PHOTOLIBRARY,
destinationType: this.camera.DestinationType.FILE_URI,
encodingType: this.camera.EncodingType.JPEG,
mediaType: this.camera.MediaType.PICTURE,
correctOrientation: true
}
this.camera.getPicture(options).then((imageData) => {
// imageData is either a base64 encoded string or a file URI
// If it's base64 (DATA_URL):
// let base64Image = 'data:image/jpeg;base64,' + imageData;
// console.log(imageData);
this.fileToUpload = imageData;
this.phototoshow = this.webview.convertFileSrc(imageData);
}, (err) => {
// Handle error
});
}
onSubmit() {
this.presentLoading();
// return this.apiService.upload(this.phototoshow).subscribe((data: any) => {
return this.apiService.upload(this.fileToUpload).subscribe((data: any) => {
console.log(data);
}
}
service.ts
upload(img1): Observable<any> {
// console.log(data);
return this.http.post(this.apiURL + '/files/img.jpg', img1,{
headers: {
'X-Parse-Application-Id': this.APP_ID,
'X-Parse-REST-API-Key': this.REST_API_KEY,
'Content-Type':'image/jpeg'
}
})
.pipe(
retry(1),
catchError(this.handleError)
)
}
I was able to upload the image with "input type = file" in the form ... but selecting the image from the gallery with the cordova plugin camera ... it only returns FILE_URI but I need the OBJECT FILE to upload via api rest.
I have read enough info on the web but it is old information that does not help me. I hope someone can help me with the problem. thanks
I managed to solve the problem:
startUpload() {
this.file.resolveLocalFilesystemUrl(this.fileToUpload)
// this.file.resolveLocalFilesystemUrl(imgEntry.filePath)
.then(entry => {
(entry as FileEntry).file(file => this.readFile(file))
})
.catch(err => {
alert('Error while reading file.');
});
}
readFile(file: any) {
const reader = new FileReader();
reader.onload = () => {
const imgBlob = new Blob([reader.result], {
type: file.type
});
this.onSubmit(imgBlob);
};
reader.readAsArrayBuffer(file);
}

File download in angular js

How to download a file in a mobile device while giving the URL from the server, In Angularjs mobile application(Platform Cordova). The file can be of type pdf, image etc...
You have to do something like this:
This is Angular JS part:
import { Http, ResponseContentType } from '#angular/http';
...
constructor(
private http: Http,
) { }
downloadFile() {
return this.http
.get('http://www.africau.edu/images/default', {
responseType: ResponseContentType.Blob,
search: // query string if have
})
.map(res => {
return {
filename: 'sample.pdf',
data: res.blob()
};
})
.subscribe(res => {
console.log('start download:',res);
var url = window.URL.createObjectURL(res.data);
var a = document.createElement('a');
document.body.appendChild(a);
a.setAttribute('style', 'display: none');
a.href = url;
a.download = res.filename;
a.click();
window.URL.revokeObjectURL(url);
a.remove(); // remove the element
}, error => {
console.log('download error:', JSON.stringify(error));
}, () => {
console.log('Completed file download.')
});
}
This is HTML part:
<button class="btn btn-primary" (click)="downloadFile()"><i class="fa fa-file-pdf-o"></i> Download</button>

How to display images returned by Cordova PhotoLibrary?

I seem to have trouble displaying images in the Image Gallery on Android. The PhotoLibrary plugin returns the list of files, but when I feed the image URLs to img tags, they don't load.
window['cordova']['plugins']['photoLibrary'].getLibrary(
result => console.log(libraryItem),
err => console.log(err);
},
{
thumbnailWidth: 512,
thumbnailHeight: 384,
quality: 0.8,
includeAlbumData: true
});
This will retrieve the URLs to the images, but they can't be used to actually display them. I get things like:
creationDate: Fri Nov 03 2017 20:06:01 GMT-0400 (EDT)
fileName: "2017-10-4-1.jpg"
height: 960
id: "1907;/storage/emulated/0/Pictures/Timelapser/2017-10-4-1.jpg"
latitude: 0
longitude: 0
photoURL: "cdvphotolibrary://photo?photoId=1907%3B%2Fstorage%2Femulated%2F0%2FPictures%2FTimelapser%2F2017-10-4-1.jpg"
thumbnailURL: "cdvphotolibrary://thumbnail?photoId=1907%3B%2Fstorage%2Femulated%2F0%2FPictures%2FTimelapser%2F2017-10-4-1.jpg&width=512&height=384&quality=0.8"
width: 1280
Feeding photoURL or thumbnailURL to img src doesn't work. I tried to decodeURI them, use the part before or after the ; and nothing.
You need to use Native Photo Library plugin and cdvphotolibrary pipe as shown below.
Here is working Git project
html
<ion-grid no-padding margin-top>
<ion-row class="row">
<ion-col col-6 *ngFor="let data of library">
<img [src]="data?.thumbnailURL | cdvPhotoLibrary">
</ion-col>
</ion-row>
</ion-grid>
ts
//fetch Photos
fetchPhotos() {
this.platform.ready().then(() => {
this.library = [];
this.photoLibrary.getLibrary({ thumbnailWidth: THUMBNAIL_WIDTH, thumbnailHeight: THUMBNAIL_HEIGHT }).subscribe({
next: (chunk) => {
this.library = this.library.concat(chunk);
this.cd.detectChanges();
},
error: (err: string) => {
if (err.startsWith('Permission')) {
this.platform.ready().then(() => {
this.photoLibrary.requestAuthorization({ read: true })
.then(() => {
}).catch((err) => {
let message = 'requestAuthorization error: ${err}';
this.showToast.showErrorToast(message);
});
});
} else { // Real error
let message: 'getLibrary error: ${err}';
this.showToast.showErrorToast(message);
}
},
complete: () => {
// Library completely loaded
}
});
});
}
cdv-photo-library.ts (pipe)
import { Pipe, PipeTransform } from '#angular/core';
import { DomSanitizer } from '#angular/platform-browser';
#Pipe({
name: 'cdvPhotoLibrary',
})
export class CdvPhotoLibraryPipe implements PipeTransform {
constructor(private sanitizer: DomSanitizer) { }
transform(url: string) {
if (url != null) {
return url.startsWith('cdvphotolibrary://') ? this.sanitizer.bypassSecurityTrustUrl(url) : url;
}
}
}

How to manually change the src file in Videogular?

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

Categories

Resources