Load progress is equal 100 but not showing file.
Tried to change path
Tried change from Cyrillic to Latin
Tried externalStorageDir
#override
void initState() {
controller
..setJavaScriptMode(JavaScriptMode.unrestricted)
..enableZoom(true)
..setBackgroundColor(const Color(0x00000000))
..setNavigationDelegate(
NavigationDelegate(
onProgress: (int progress) {
printMessage("viewer progress : $progress");
},
onPageStarted: (String url) {},
onPageFinished: (String url) {},
onWebResourceError: (WebResourceError error) {
setState(() {
canRenderFile = false;
});
printMessage("error viewer ${error.description}");
},
),
);
super.initState();
}
#override
Widget build(BuildContext context) {
printMessage(widget._path ?? "");
if (canRenderFile) {
controller.loadFile(widget._path ?? "");
}
if (canRenderFile == true) {
return WebViewWidget(controller: controller);
} else {
return DownloadFileView(
fileName: widget.file?.name ?? "",
fileSize: widget.file?.fileSize.toUiSize().toString() ?? "",
onAction: () {
widget.onDownload?.call();
},
);
}
}
}
This is my code
canRenderFile is default = true
On android , if you will try to use webview to show any file (for ex PDF) then it will not show anything .. progress will show 100% and no error will be there but content will not show . So here you will have to use either some library to preview file or just do some code to show system popup that shows options to view that file
Related
I have used inappwebview in our app. I am getting different encodedUrl in shouldOverrideUrlLoading method for iOS and Android. While in iOS it is working fine but for android it is not giving proper url.
encodedURL
iOS : %22coordinates%22:%22%7B%5C%22y1%5C%22:193.0,%5C%22x1%5C%22:491.0,%5C%22y2%5C%22:162.0,%5C%22x2%5C%22:522.0%7D%22
android : %22coordinates%22:%22%7B/%22y1/%22:521.0,/%22x1/%22:753.0,/%22y2/%22:490.0,/%22x2/%22:784.0%7D%22
DecoddedURL
iOS : "coordinates":"{"y1":521.0,"x1":753.0,"y2":490.0,"x2":784.0}"
android : "coordinates":"{/"y1/":521.0,/"x1/":753.0,/"y2/":490.0,/"x2/":784.0}"
we are getting different different encoded string for iOS and Android. You can see “coordinates” value for iOS it is coming '%5C' and for android it is '/'.
InAppWebView(
key: webViewKey,
initialUrlRequest: URLRequest(url: Uri.parse(widget.url)),
initialOptions: inAppWebViewGroupOptions(),
onWebViewCreated: (InAppWebViewController webViewController) async {
_appWebViewController = webViewController;
if (widget.onWebViewCreated != null) {
widget.onWebViewCreated!(_appWebViewController!);
}
},
onLoadStart: (InAppWebViewController controller, Uri? url) async {
if (widget.onProgressChanged != null) {
widget.onPageStarted!(url.toString());
}
},
onLoadStop: (InAppWebViewController controller, Uri? url) async {
if (widget.onPageFinished != null) {
widget.onPageFinished!(url.toString());
}
},
onProgressChanged: (InAppWebViewController controller, int progress) {
if (widget.onProgressChanged != null) {
widget.onProgressChanged!(progress);
}
},
shouldOverrideUrlLoading: (controller, navigationAction) async {
var request = navigationAction.request;
var url = request.url;
bool absolute = url!.hasAbsolutePath;
if (absolute) {
setCookies(url.toString());
}
if (widget.shouldOverrideUrlLoading != null) {
widget.shouldOverrideUrlLoading!(controller, navigationAction);
}
// always allow all the other requests
return NavigationActionPolicy.ALLOW;
},
androidOnPermissionRequest: (controller, origin, resources) async {
return PermissionRequestResponse(
resources: resources,
action: PermissionRequestResponseAction.GRANT);
},
onLoadError: (_, __, int code, String message) {
if (widget.onPageError != null) {
widget.onPageError!(code, message);
}
_webViewCubit?.showLinearProgressIndicator(false, 1);
},
);
Please find below code for getting decodedURL:
String jsonString = Uri.decodeFull(data[1]);
So is there any way to get values same in android like iOS?
I'm using the following code to download a file (can be a PDF or a DOC) and then opening it using Linking.
const { dirs } = RNFetchBlob.fs;
let config = {
fileCache : true,
appendExt : extension,
addAndroidDownloads : {
useDownloadManager : false,
notification : false,
title : 'File',
description : 'A file.',
path: `${dirs.DownloadDir}/file.${extension}`,
},
};
RNFetchBlob.config(config)
.fetch(
method,
remoteUrl,
APIHelpers.getDefaultHeaders()
)
.then((res) => {
let status = res.info().status;
if (status == 200) {
Linking.canOpenURL(res.path())
.then((supported) => {
if (!supported) {
alert('Can\'t handle url: ' + res.path());
} else {
Linking.openURL(res.path())
.catch((err) => alert('An error occurred while opening the file. ' + err));
}
})
.catch((err) => alert('The file cannot be opened. ' + err));
} else {
alert('File was not found.')
}
})
.catch((errorMessage, statusCode) => {
alert('There was some error while downloading the file. ' + errorMessage);
});
However, I'm getting the following error:
An error occurred while opening the file. Error: Unable to open URL:
file:///Users/abhishekpokhriyal/Library/Developer/CoreSimulator/Devices/3E2A9C16-0222-40A6-8C1C-EC174B6EE9E8/data/Containers/Data/Application/A37B9D69-583D-4DC8-94B2-0F4AF8272310/Documents/RNFetchBlob_tmp/RNFetchBlobTmp_o259xexg7axbwq3fh6f4.pdf
I need to implement the solution for both iOS and Android.
I think the easiest way to do so is by using react-native-file-viewer package.
It allows you to Prompt the user to choose an app to open the file with (if there are multiple installed apps that support the mimetype).
import FileViewer from 'react-native-file-viewer';
const path = // absolute-path-to-my-local-file.
FileViewer.open(path, { showOpenWithDialog: true })
.then(() => {
// success
})
.catch(error => {
// error
});
So, I finally did this by replacing Linking by the package react-native-file-viewer.
In my APIHelpers.js:
async getRemoteFile(filePath, extension, method = 'GET') {
const remoteUrl = `${API_BASE_URL}/${encodeURIComponent(filePath)}`;
const { dirs } = RNFetchBlob.fs;
let config = {
fileCache : true,
appendExt : extension,
addAndroidDownloads : {
useDownloadManager : false,
notification : false,
title : 'File',
description : 'A file.',
path: `${dirs.DownloadDir}/file.${extension}`,
},
};
return new Promise(async (next, error) => {
try {
let response = await RNFetchBlob.config(config)
.fetch(
method,
remoteUrl,
this.getDefaultHeaders()
);
next(response);
} catch (err) {
error(err);
}
});
}
In my Actions.js
export function openDocument(docPath, ext) {
return async (dispatch) => {
dispatch(fetchingFile());
APIHelpers.getRemoteFile(docPath, ext).then(async function(response) {
dispatch(successFetchingFile());
let status = response.info().status;
if (status == 200) {
const path = response.path();
setTimeout(() => {
FileViewer.open(path, {
showOpenWithDialog: true,
showAppsSuggestions: true,
})
.catch(error => {
dispatch(errorOpeningFile(error));
});
}, 100);
} else {
dispatch(invalidFile());
}
}).catch(function(err) {
dispatch(errorFetchingFile(err));
});
}
}
In my Screen.js
import { openDocument } from 'path/to/Actions';
render() {
return <Button
title={'View file'}
onPress={() => this.props.dispatchOpenDocument(doc.filepath, doc.extension)}
/>;
}
.
.
.
const mapDispatchToProps = {
dispatchOpenDocument: (docPath, ext) => openDocument(docPath, ext),
}
Are you downloading it from the web? I can see the pdf path is attached at the end of the error path.
For web URLs, the protocol ("http://", "https://") must be set accordingly!
Try to append appropriate schemes to your path. Check it out from the link mentioned below.
This can be done with 'rn-fetch-blob'
RNFetchBlob.android.actionViewIntent(fileLocation, mimeType);
I'm working on an ionic3 application. I need to take an image from the user either by camera or gallery, first saves it to the local directory then upload the image to the server. I used the following step by step tutorial: https://devdactic.com/ionic-2-images/
Uploading the photo is working like a charm, but while saving the image to the local directory and save the path on local storage, after retrieving from storage it shows the following error: .
As it's obvious it complains about Not allowed to load local resource.
Next, I started to google for the solution, and I found this solution in StackOverflow and this in GitHub. As they both suggested, the problem is with cordova-plugin-ionic-webview, so I need to downgrade the version. When I tried their solution, the uploading and showing the image to the user is working perfectly, however, it creates problem other parts of the application which is loading data from asset no matter what; images, fonts. Shows the following error .Next I found a solutionf for the problem in GitHub here, as it suggested and accepted by most users we need to use the latest version of **cordova-plugin-ionic-webview **, which of course it would cause the first problem for me.
I'm gonna upload the codes here as well.`
getImage() {
this.presentActionSheet();
} //end getImage
public uploadImage() {
console.log('Uploading the image');
console.log(this.lastImageL);
var targetPath = this.pathForImage(this.lastImage);
console.log(targetPath);
var url = "https://dev.raihan.pomdev.net/wp-json/raihan/v1/profilePhoto";
var filename = this.lastImage;
console.log(' targetPath : ', targetPath);
console.log('File Name : ', filename)
console.log(url, " IS the URL");
var options = {
fileKey: "image",
fileName: filename,
chunkedMode: false,
mimeType: "multipart/form-data",
params: {
'image': filename,
'user_id': 79
}
};
const fileTransfer: TransferObject = this.transfer.create();
this.loading = this.loadingCtrl.create({
content: 'منتظر باشید',
});
this.loading.present();
// Use the FileTransfer to upload the image
fileTransfer.upload(targetPath, url, options).then(data => {
this.loading.dismissAll()
this.presentToast(' . عکس شما موفقانه ذخیره شد');
this.storage.set("Profile_Photo", targetPath).then((data) => {
console.log('response of uploading the image ', data);
console.log('Target Path ', targetPath);
console.log('In set storage ', targetPath);
$("#Photo").attr("src", targetPath);
$("#Photo2").attr("src", targetPath);
console.log('myphoto ', targetPath);
});
}, err => {
this.loading.dismissAll()
this.presentToast('مشکلی در قسمت ذخیره کردن عکس شما وجود دارد ' + err);
console.log('error sending the image');
console.log(err);
});
}
public takePicture(sourceType) {
var options = {
quality: 100,
sourceType: sourceType,
saveToPhotoAlbum: false,
correctOrientation: true
};
// Get the data of an image
this.camera.getPicture(options).then((imagePath) => {
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.');
});
}
ionViewDidLoad() {
console.log('ionViewDidLoad CaptureImagePage');
}
private createFileName() {
var d = new Date(),
n = d.getTime(),
newFileName = n + ".jpg";
return newFileName;
}
// Copy the image to a local folder
private copyFileToLocalDir(namePath, currentName, newFileName) {
this.file.copyFile(namePath, currentName, cordova.file.dataDirectory, newFileName).then(success => {
this.lastImage = newFileName;
this.uploadImage();
}, error => {
this.presentToast('Error while storing file. ' + error);
});
}
private presentToast(text) {
let toast = this.toastCtrl.create({
message: text,
duration: 5000,
position: 'center'
});
toast.present();
}
// Always get the accurate path to your apps folder
public pathForImage(img) {
if (img === null) {
return '';
} else {
return cordova.file.dataDirectory + img;
}
}
public presentActionSheet() {
let actionSheet = this.actionSheetCtrl.create({
title: 'Select Image Source',
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();
}
`
Now I'm confused which version of **cordova-plugin-ionic-webview ** I should use? Is there someone who could help me?
Note: Thanks for your patience to read all the questions.
I would try to use the latest version of the WebView if possible, and then use the window.Ionic.WebView.convertFileSrc() method on the file:/// path before putting it on a page for display. Those tips can be seen here:
https://ionicframework.com/docs/building/webview
Cordova and Capacitor apps are hosted on a local HTTP server and are
served with the http:// protocol. Some plugins, however, attempt to
access device files via the file:// protocol. To avoid difficulties
between http:// and file://, paths to device files must be rewritten
to use the local HTTP server. For example, file:///path/to/device/file
must be rewritten as http://://path/to/device/file
before being rendered in the app.
For Cordova apps, the Ionic Web View plugin provides a utility
function for converting File URIs:
window.Ionic.WebView.convertFileSrc(). There is also a corresponding
Ionic Native plugin: #ionic-native/ionic-webview.
Here is a sample method I use, which works fine in the 4.x webview:
getNormalizedUrl(path: string): SafeResourceUrl {
let newPath = this.domSanitizer.bypassSecurityTrustUrl(
window.Ionic.WebView.convertFileSrc(path));
return newPath;
}
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
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