I'm using the Plugin.Media from #JamesMontemagno version 2.4.0-beta (which fixes picture orientation), it's working on Adroind 4.1.2 (Jelly Bean) and Marshmallow, but NOT on my Galaxy S5 Neo with Android version 5.1.1.
Basically when I take a picture it never returns back on the page from where I started the process; always returns back to the initial home page.
On devices where it works, when I take a picture, I see that first of all the application fires OnSleep, then after taking the picture fires OnResume.
On my device where is NOT working it fires OnSleep and after taking the picture doesn't fire OnResume, it fires the initialization page and then OnStart.
For this reason it doesn't open the page where I was when taking the picture.
What should I do to make sure it fires OnResume returning to the correct page and not OnStart which returns on initial fome page ?
In addition, when I take a picture it takes almost 30 seconds to get back to the code after awaiting TakePhotoAsync process, and it's too slow!
Following my code:
MyTapGestureRecognizerEditPicture.Tapped += async (sender, e) =>
{
//Display action sheet
String MyActionResult = await DisplayActionSheet(AppLocalization.UserInterface.EditImage,
AppLocalization.UserInterface.Cancel,
AppLocalization.UserInterface.Delete,
AppLocalization.UserInterface.TakePhoto,
AppLocalization.UserInterface.PickPhoto);
//Execute action result
if (MyActionResult == AppLocalization.UserInterface.TakePhoto)
{
//-----------------------------------------------------------------------------------------------------------------------------------------------
//Take photo
await CrossMedia.Current.Initialize();
if (!CrossMedia.Current.IsCameraAvailable || !CrossMedia.Current.IsTakePhotoSupported)
{
await DisplayAlert(AppLocalization.UserInterface.Alert, AppLocalization.UserInterface.NoCameraAvailable, AppLocalization.UserInterface.Ok);
}
else
{
var MyPhotoFile = await CrossMedia.Current.TakePhotoAsync(new Plugin.Media.Abstractions.StoreCameraMediaOptions
{
Directory = "MyApp",
Name = "MyAppProfile.jpg",
SaveToAlbum = true,
PhotoSize = Plugin.Media.Abstractions.PhotoSize.Small
});
if (MyPhotoFile != null)
{
//Render image
MyProfilePicture.Source = ImageSource.FromFile(MyPhotoFile.Path);
//Save image on database
MemoryStream MyMemoryStream = new MemoryStream();
MyPhotoFile.GetStream().CopyTo(MyMemoryStream);
byte[] MyArrBytePicture = MyMemoryStream.ToArray();
await SaveProfilePicture(MyArrBytePicture);
MyPhotoFile.Dispose();
MyMemoryStream.Dispose();
}
}
}
if (MyActionResult == AppLocalization.UserInterface.PickPhoto)
{
//-----------------------------------------------------------------------------------------------------------------------------------------------
//Pick photo
await CrossMedia.Current.Initialize();
if (!CrossMedia.Current.IsPickPhotoSupported)
{
await DisplayAlert(AppLocalization.UserInterface.Alert, AppLocalization.UserInterface.PermissionNotGranted, AppLocalization.UserInterface.Ok);
}
else
{
var MyPhotoFile = await CrossMedia.Current.PickPhotoAsync();
if (MyPhotoFile != null)
{
//Render image
MyProfilePicture.Source = ImageSource.FromFile(MyPhotoFile.Path);
//Save image on database
MemoryStream MyMemoryStream = new MemoryStream();
MyPhotoFile.GetStream().CopyTo(MyMemoryStream);
byte[] MyArrBytePicture = MyMemoryStream.ToArray();
await SaveProfilePicture(MyArrBytePicture);
MyPhotoFile.Dispose();
MyMemoryStream.Dispose();
}
}
}
};
Please help!! We need to deploy this app but we cannot do it with this problem.
Thank you in advance!
It is perfectly normal to have the Android OS terminate and restart an Activity. As you are seeing, your app's Activity it will be automatically restarted when the camera app exits and the OS returns control to your app. The odds are it just needed more memory in order to take that photo with the Neo's 16MP camera, you can watch the logcat output to confirm that.
Restarted – It is possible for an activity that is anywhere from paused to stopped in the lifecycle to be removed from memory by Android. If the user navigates back to the activity it must be restarted, restored to its previously saved state, and then displayed to the user.
What to do:
So on the Xamarin.Forms OnStart lifecycle method you need to restore your application to a valid running state (initializing variables, preforming any bindings, etc...).
Plug code:
The Android platform code for the TakePhotoAsync method looks fine to me, but remember that the memory for that image that is passed back via the Task will be doubled as it is marshaled from the ART VM back the Mono VM. Calling GC.Collect() as soon as possible after the return will help (but your Activity is restarting anyway...)
public async Task<MediaFile> TakePhotoAsync(StoreCameraMediaOptions options)
{
~~~
var media = await TakeMediaAsync("image/*", MediaStore.ActionImageCapture, options);
In turn calls:
this.context.StartActivity(CreateMediaIntent(id, type, action, options));
Not much less you can really do within the Android OS to popup the Camera.
In addition, when I take a picture it takes almost 30 seconds to get back to the code after awaiting TakePhotoAsync process, and it's too slow!
Is that on your Neo? Or all devices?
I would call that very suspect (ie. a bug) as even flushing all the Java memory after the native Camera Intent/Activity and the restart time for your app's Activity should not take 30 seconds on a oct-core 1.6 GHz Cortex... but I do not have your device, app and code in front of me....
Related
whit this code i can get gps data and retrive address all works fine in background, for some minuts, after 20 minuts about,the app semms stop do what i aspect.
i tryed battery concession and other possible things, ive tryed backgroud service.
But nothing seems to wor for get position in background, after lomg time.
Any suggestion.
this.backgroundGeolocation.configure(config).then(() => {
console.log('backgroundGeolocation configconfigconfigconfigconfigconfig');
this.backgroundGeolocation.on(BackgroundGeolocationEvents.location).subscribe((location: BackgroundGeolocationResponse) => {
console.log(location);
this.ngZone.run(() => {
this.geolocationService.nativeGeocoder.reverseGeocode(location.latitude,
location.longitude).then((result: NativeGeocoderResult[]) =>{
console.log("JSON.stringify(result[0]",JSON.stringify(result[0]));
this.comunenoreplace = result[0]['locality'];
this.comune = result[0]['locality'].replace(/[^A-Z0-9]+/ig, "-");
this.provincia = result[0]['subAdministrativeArea'].replace(new RegExp('Città Metropolitana di|Provincia di', 'g'), '');
this.globalLatitude = location.latitude;
this.globalLongitude = location.longitude;
}).catch((error: any) => console.log("reverseGeocode", error));
});
// IMPORTANT: You must execute the finish method here to inform the native plugin that you're finished,
// and the background-task may be completed. You must do this regardless if your operations are successful or not.
// IF YOU DON'T, ios will CRASH YOUR APP for spending too much time in the background.
// this.backgroundGeolocation.finish;
});
});
you can use the https://ionicframework.com/docs/native/background-mode
it just modify the sleep mode for apps to make them work in background (without killing the process).
If the process is killed, so the app may stop sending datas.
Then you have "cordova.plugins.backgroundMode.excludeFromTaskList()"; to make it disappear from the tasklist (Android 5.0+)
Hope this help :)
I need to ensure that a certain HTTP request was send successfully. Therefore, I'm wondering if a simple way exists to move such a request into a background service task.
The background of my question is the following:
We're developing a survey application using flutter. Unfortunately, the app is intended to be used in an environment where no mobile internet connection can be guaranteed. Therefore, I’m not able to simply post the result of the survey one time but I have to retry it if it fails due to network problems. My current code looks like the following. The problem with my current solution is that it only works while the app is active all the time. If the user minimizes or closes the app, the data I want to upload is lost.
Therefore, I’m looking for a solution to wrap the upload process in a background service task so that it will be processed even when the user closes the app. I found several posts and plugins (namely https://medium.com/flutter-io/executing-dart-in-the-background-with-flutter-plugins-and-geofencing-2b3e40a1a124 and https://pub.dartlang.org/packages/background_fetch) but they don’t help in my particular use case. The first describes a way how the app could be notified when a certain event (namely the geofence occurred) and the second only works every 15 minutes and focuses a different scenario as well.
Does somebody knows a simple way how I can ensure that a request was processed even when there is a bad internet connection (or even none at the moment) while allowing the users to minimize or even close the app?
Future _processUploadQueue() async {
int retryCounter = 0;
Future.doWhile(() {
if(retryCounter == 10){
print('Abborted after 10 tries');
return false;
}
if (_request.uploaded) {
print('Upload ready');
return false;
}
if(! _request.uploaded) {
_networkService.sendRequest(request: _request.entry)
.then((id){
print(id);
setState(() {
_request.uploaded = true;
});
}).catchError((e) {
retryCounter++;
print(e);
});
}
// e ^ retryCounter, min 0 Sec, max 10 minutes
int waitTime = min(max(0, exp(retryCounter)).round(), 600);
print('Waiting $waitTime seconds till next try');
return new Future.delayed(new Duration(seconds: waitTime), () {
print('waited $waitTime seconds');
return true;
});
})
.then(print)
.catchError(print);
}
You can use the plugin shared_preferences to save each HTTP response to the device until the upload completes successfully. Like this:
requests: [
{
id: 8eh1gc,
request: "..."
},
...
],
Then whenever the app is launched, check if any requests are in the list, retry them, and delete them if they complete. You could also use the background_fetch to do this every 15 minutes.
https://yourstop.info is a PWA. When I run lighthouse on it, the PWA score comes up as 73 and performance as 60, see image below.
It is giving a time to interactive of 19.1 seconds which is the reason for the low scoring. What happens on startup is that when the user's location is determined, a list of relevant bus stops is downloaded in the background. The code for this is
var firstLocationUpdate = false;
//var firstLocationUpdate = true;
if (navigator.geolocation) {
navigator.geolocation.watchPosition(updatePosition, showError, geoOptions);
}
else {
window.ys.userLocationError = "Geolocation is not supported by this browser";
}
function updatePosition(position) {
if (position != null) {
window.ys.userLat = position.coords.latitude;
window.ys.userLon = position.coords.longitude;
try {
// When google maps was not loaded this was throwing an exception
window.ys.userLocation = new google.maps.LatLng(window.ys.userLat, window.ys.userLon);
}
catch (e) {
console.log("Google maps not loaded when initializing geo.js, userLocation LatLng not set");
}
window.ys.userLocationError = "";
if (window.ys.userLocation != null && window.ys.userLocationTripMarker != null) {
window.ys.userLocationTripMarker.setPosition(window.ys.userLocation);
}
if (window.ys.userLocation != null && window.ys.userLocationSearchMarker != null) {
window.ys.userLocationSearchMarker.setPosition(window.ys.userLocation);
}
if (firstLocationUpdate == false) {
var event = document.createEvent("HTMLEvents");
event.initEvent('firstlocationupdate', true, true);
document.dispatchEvent(event);
firstLocationUpdate = true;
}
}
}
This does however not impact at all on the readiness of the PWA, the page is already fully rendered and the user can perform other tasks while the background function is in progress. If in the code snippet above, firstLocationUpdate is initialized to true and the background download is prevented, then the lighthouse scoring is much higher as shown here
Both cases however show the exact same user experience as far as I can tell. But the benefit of doing the background stop download is that the Locate the Nearest Stops operation happens much quicker.
The problem with the lower score for PWA is that chrome no longer automatically display the web app install banner. So I am in a situation where I need to make a choice and my current preference is for the background download to occur.
So the question is, is there a workaround which will allow me to have BOTH a high PWA score AND do the background download ?
Or is there any way of influencing how lighthouse scores PWAs, in my eyes anyway the background download resulting is lighthouse lengthening the time to interactive is not correct behavior ?
I'm creating a research experiment that uses WebAudio API to record audio files spoken by the user.
I came up with a solution for this using recorder.js and everything was working fine... until I tried it yesterday.
I am now getting this error in Chrome:
"The AudioContext was not allowed to start. It must be resumed (or
created) after a user gesture on the page."
And it refers to this link: Web Audio API policy.
This appears to be a consequence of Chrome's new policy outlined at the link above.
So I attempted to solve the problem by using resume() like this:
var gumStream; //stream from getUserMedia()
var rec; //Recorder.js object
var input; //MediaStreamAudioSourceNode we'll be recording
// shim for AudioContext when it's not avb.
var AudioContext = window.AudioContext || window.webkitAudioContext;
var audioContext = new AudioContext; //new audio context to help us record
function startUserMedia() {
var constraints = { audio: true, video:false };
audioContext.resume().then(() => { // This is the new part
console.log('context resumed successfully');
});
navigator.mediaDevices.getUserMedia(constraints).then(function(stream) {
console.log("getUserMedia() success, stream created, initializing Recorder.js");
gumStream = stream;
input = audioContext.createMediaStreamSource(stream);
rec = new Recorder(input, {numChannels:1});
audio_recording_allowed = true;
}).catch(function(err) {
console.log("Error");
});
}
Now in the console I'm getting:
Error
context resumed successfully
And the stream is not initializing.
This happens in both Firefox and Chrome.
What do I need to do?
I just had this exact same problem! And technically, you helped me to find this answer. My error message wasn't as complete as yours for some reason and the link to those policy changes had the answer :)
Instead of resuming, it's best practise to create the audio context after the user interacted with the document (when I say best practise, if you have a look at padenot's first comment of 28 Sept 2018 on this thread, he mentions why in the first bullet point).
So instead of this:
var audioContext = new AudioContext; //new audio context to help us record
function startUserMedia() {
audioContext.resume().then(() => { // This is the new part
console.log('context resumed successfully');
});
}
Just set the audio context like this:
var audioContext;
function startUserMedia() {
if(!audioContext){
audioContext = new AudioContext;
}
}
This should work, as long as startUserMedia() is executed after some kind of user gesture.
I am using phonegap 2.0.0 on iOS and using basically the stock image capture code. After the image is captured it is saved to the phone, the URI is saved to the database along with the rest of the items info and the image is displayed on the page.
All this is working on both iOS and android. The issue is that when the iOS phone is turned off and allowed to sit for a period of time (overnight) the images no longer display. The rest of the data is retrieved from the database and displayed but the images just show a black square where the image should be (indicating the info is still inn the database)
Does iOS rename images after being turned off and allowed to sit for a some time? any suggestions? if the phone is turned off and back on this does not happen.. only after the phone sits for some time...
this seems very relevant...
Capturing and storing a picture taken with the Camera into a local database / PhoneGap / Cordova / iOS
for anybody else having this issue the following code worked for me...
function capturePhoto() {
navigator.camera.getPicture(movePic, onFail,{ quality : 70 });
}
function movePic(file){
window.resolveLocalFileSystemURI(file, resolveOnSuccess, resOnError);
}
function resolveOnSuccess(entry){
var d = new Date();
var n = d.getTime();
var newFileName = n + ".jpg";
var myFolderApp = "myPhotos";
window.requestFileSystem(LocalFileSystem.PERSISTENT, 0, function(fileSys) {
fileSys.root.getDirectory( myFolderApp,
{create:true, exclusive: false},
function(directory) {
entry.moveTo(directory, newFileName, successMove, resOnError);
},
resOnError);
},
resOnError);
}
function successMove(imageUri) {
document.getElementById('smallImage').src = imageUri.fullPath;
//hidden input used to save the file path to the database
document.getElementById('site_front_pic').value = "file://"+imageUri.fullPath;
smallImage.style.display = 'block';
}
function onFail(message) {
alert('Failed to load picture because: ' + message);
}
function resOnError(error) {
alert(error.code);
}