MonoTouch Load image in background - ios

I am having a problem trying to load an image and display it using System.Threading.Task
My Code is as follows
Task DownloadTask { get; set; }
public string Instrument { get; set; }
public PriceChartViewController(string Instrument) {
this.Instrument = Instrument;
DownloadTask = Task.Factory.StartNew(() => { });
}
private void LoadChart(ChartType chartType) {
NSData data = new NSData();
DownloadTask = DownloadTask.ContinueWith(prevTask => {
try {
UIApplication.SharedApplication.NetworkActivityIndicatorVisible = true;
NSUrl nsUrl = new NSUrl(chartType.Uri(Instrument));
data = NSData.FromUrl(nsUrl);
}
finally {
UIApplication.SharedApplication.NetworkActivityIndicatorVisible = false;
}
});
DownloadTask = DownloadTask.ContinueWith(t => {
UIImage image = new UIImage(data);
chartImageView = new UIImageView(image);
chartImageView.ContentScaleFactor = 2f;
View.AddSubview(chartImageView);
this.Title = chartType.Title;
}, CancellationToken.None, TaskContinuationOptions.OnlyOnRanToCompletion, TaskScheduler.FromCurrentSynchronizationContext());
}
The second Continue with does not seem to be being called?
Initially my code looked like the following without the background processing and it worked perfectly.
private void oldLoadChart(ChartType chartType) {
UIApplication.SharedApplication.NetworkActivityIndicatorVisible = true;
NSUrl nsUrl = new NSUrl(chartType.Uri(Instrument));
NSData data = NSData.FromUrl(nsUrl);
UIImage image = new UIImage(data);
chartImageView = new UIImageView(image);
chartImageView.ContentScaleFactor = 2f;
View.AddSubview(chartImageView);
this.Title = chartType.Title;
UIApplication.SharedApplication.NetworkActivityIndicatorVisible = false;
}
Does anyone know what I am doing wrong?

Your first part of the thread is crashing, that's why it is never getting to the second:
You are calling UIApplication.SharedApplication.NetworkActivityIndicatorVisible = true;
from a non-UI thread. Setting the network activity indicator is a UI operation and may only be executed on the main thread. You'll have to wrap it in InvokeOnMainThread().
If you add a try-catch, you'll see the exception.

Add catch before finally. Maybe you just have an exception so workflow doesn't go any further.

Related

System.OutOfMemoryException when tried with 70 files SharpZipLib

I have tried zipping 70 pdf documents but ended up having a System.OutOfMemoryException.
Please look into the following code and let me know whats wrong with it.
Please note that I have posted issue on GitHub as well.
public byte[] DownloadPaperList()
{
try
{
PaperDAO paperDAO = new PaperDAO();
List<PaperModel> papers = paperDAO.GetPaperListByPaperIds(paperIDs);
using (MemoryStream outputMemoryStream = new MemoryStream())
{
using (var zipOutputStream = new ZipOutputStream(outputMemoryStream))
{
papers = papers.OrderBy(x => x.Order).ToList();
foreach (PaperModel paper in papers)
{
byte[] decryptedPaper
= CryptoServices.DecryptFile(paper.FileData, paperDAO.GenerateCloseOpenFile(paper, string.Empty), paper.SEVersion);
ZipEntry zipFileEntry = new ZipEntry(paper.DocName + ".pdf")
{
Size = decryptedPaper.Length
};
zipOutputStream.SetLevel(3);
zipOutputStream.PutNextEntry(zipFileEntry); //EXCEPTION THROWS HERE!!!
StreamUtils.Copy(new MemoryStream(decryptedPaper), zipOutputStream, new byte[4096]);
}
zipOutputStream.CloseEntry();
// Stop ZipStream.Dispose() from also Closing the underlying stream.
zipOutputStream.IsStreamOwner = false;
outputMemoryStream.Position = 0;
}
return outputMemoryStream.ToArray();
}
}
catch (Exception)
{
throw;
}
}

how to control background audio from Dependecy-service in xamarin.ios

I made a Dependency-Service for playing audio and background control in Xamarin.ios .
Audio was executed in background well.
but, Lockscreen controller was not shown.
I don't know what is problem.
I registered "UIBackgroundMode" and "audio" in "info.plist"
and set the AVAudioSession as playback.
I used play_pause(string url) function for playing audio and setting the MPNowPlayingInfo.
[assembly: Dependency(typeof(AudioService))]
namespace BibleHymn.iOS
{
class AudioService : IAudio
{
private bool isPlaying = false;
private bool interrupted = false;
int audioNum = 0;
AVFoundation.AVPlayer player;
public AudioService()
{
var avSession = AVAudioSession.SharedInstance();
avSession.SetCategory(AVAudioSessionCategory.Playback);
NSError activationError = null;
avSession.SetActive(true, out activationError);
}
public bool Play_Pause(string url)
{
if(player == null)
{
this.player = new AVFoundation.AVPlayer();
this.player = AVFoundation.AVPlayer.FromUrl(Foundation.NSUrl.FromString(url));
this.player.Play();
UpdateNotification();
}
isPlaying !=isPlaying;
return isPlaying;
}
public void UpdateNotification()
{
var item = new MPNowPlayingInfo
{
Title = "My Title"
};
MPNowPlayingInfoCenter.DefaultCenter.NowPlaying = item;
Device.BeginInvokeOnMainThread(() => {
UIKit.UIApplication.SharedApplication.BeginReceivingRemoteControlEvents();
});
}
}
I used play_pause from viewModel through button control's command.
init like ...
public IAudio streamService = DependencyService.Get<IAudio>();
and in command ...
streamService.Play_Pause(url);
You need to register background support for Audio in the info.plist by following screenshot
Also did you import Mediaplayer.framework

Showing "Import with Instagram" in UIActivityViewController

I am trying to add Instagram in "Share To" functionality in my app. I have seen the Instagram's iPhone hooks documents. I have created custom UIActivty which works fine but my question is, is there a way to add "Import with Instagram" functionality as it can be seen in iOS's Photos app iOS Photo App:
In my app for some reason, it does not show that "Import with Instagram". my app Share view :
I do not want to share only with Instagram so no ".igo"
EDIT: All of this is specifically for iOS versions < 10. For some reasons Instagram Share Extension works fine (for my app) in devices with iOS >= 10.
EDIT: I am trying to share image and video with ".jpeg" and ".mov" formats respectively
I have seen/read that Instagram added share extension in release 8.2, so technically all the apps should show "Instagram" in share tray, i.e. it can be seen in Google Photos app.
public void NativeShareImage(UIView sourceView, CGRect sourceRect,
UIImage image, string shareCaption, string emailSubject)
{
string filename = Path.Combine(FileSystemUtils.GetTemporaryDataPath(), "Image.jpg");
NSError err = null;
using(var imgData = image.AsJPEG(JpgImageQuality))
{
if(imgData.Save(filename, false, out err))
{
Logger.Information("Image saved before native share as {FileName}", filename);
}
else
{
Logger.Error("Image NOT saved before native share as to path {FileName}. {Error}", filename, err.Description);
return;
}
}
// this are the items that needs to be shared
// Instagram ignores the caption, that is known
var activityItems = new List<NSObject>
{
new NSString(shareCaption),
new NSUrl(new Uri(filename).AbsoluteUri)
};
// Here i add the custom UIActivity for Instagram
UIActivity[] applicationActivities =
{
new InstagramActivity(image, sourceRect, sourceView),
}
var activityViewController = new UIActivityViewController(activityItems.ToArray(), applicationActivities);
activityViewController.SetValueForKey(new NSString(emailSubject), new NSString("subject"));
activityViewController.CompletionWithItemsHandler = (activityType, completed, returnedItems, error) =>
{
UserSharedTo(activityType, completed);
};
// Hide some of the less used activity types so that Instagram shows up in the list. Otherwise it's pushed off the activity view
// and the user has to scroll to see it.
activityViewController.ExcludedActivityTypes = new[] { UIActivityType.AssignToContact, UIActivityType.CopyToPasteboard, UIActivityType.Print };
if(UIDevice.CurrentDevice.UserInterfaceIdiom == UIUserInterfaceIdiom.Phone)
{
PresentViewController(activityViewController, true, null);
}
else
{
activityViewController.ModalPresentationStyle = UIModalPresentationStyle.Popover;
PresentViewController(activityViewController, true, null);
// Get the popover presentation controller and configure it.
UIPopoverPresentationController presentationController = activityViewController.PopoverPresentationController;
presentationController.PermittedArrowDirections = UIPopoverArrowDirection.Down;
presentationController.SourceRect = sourceRect;
presentationController.SourceView = sourceView;
}
}
// when opening custom activity use ".igo" to only show instagram
public class InstagramActivity : UIActivity
{
public InstagramActivity(UIImage imageToShare, CGRect frame, UIView view, string shareCaption = "")
{
_ImageToShare = imageToShare;
_Frame = frame;
_View = view;
}
public override UIImage Image { get { return UIImage.FromBundle("Instagram"); } }
public override string Title { get { return "Instagram"; } }
public override NSString Type { get { return new NSString("PostToInstagram"); } }
public string Caption { get; set; }
public override bool CanPerform(NSObject[] activityItems)
{
return UIApplication.SharedApplication.CanOpenUrl(NSUrl.FromString("instagram://app"));
}
public override void Prepare(NSObject[] activityItems)
{
}
public override void Perform()
{
string filename = Path.Combine(FileSystemUtils.GetTemporaryDataPath(), "Image.igo");
NSError err = null;
using(var imgData = _ImageToShare.AsJPEG(JpgImageQuality))
{
if(imgData.Save(filename, false, out err))
{
Logger.Information("Instagram image saved as {FileName}", filename);
}
else
{
Logger.Error("Instagram image NOT saved as to path {FileName}. {Error}", filename, err.Description);
Finished(false);
return;
}
}
var url = NSUrl.FromFilename(filename);
_DocumentController = UIDocumentInteractionController.FromUrl(url);
_DocumentController.DidEndSendingToApplication += (o, e) => Finished(true);
_DocumentController.Uti = "com.instagram.exclusivegram";
if(!string.IsNullOrEmpty(ShareCaption))
{
_DocumentController.Annotation = NSDictionary.FromObjectAndKey(new NSString(ShareCaption), new NSString("InstagramCaption"));
}
_DocumentController.PresentOpenInMenu(_Frame, _View, true);
}
UIImage _ImageToShare;
CGRect _Frame;
UIView _View;
UIDocumentInteractionController _DocumentController;
}

Memory crash due to imagestoring

This method takes images from the in-built gallery and submits them to the screen in form of a Texture. I also store their choosen image in a file to be presented everytime the user enters that screen. The issue is that when I upload, lets say 3 images in a row I receive memory warning in console, later leading to the app crashing due to memory error:
AppLauncher failed with an exception:
java.lang.RunTimeException: The app crashed: Terminated due to Memory Error
It's hard to pinpoint the exact reason for this. But I've debugged and logged a bit and it doesn't seem that the writing of the image to a file is causing it. It's more related to the action of removing an already existing Image and then uploading a replacement. When I let the user delete their image, the whole file is deleted and the texture is disposed. I also noticed that when the user have uploaded some images and exit the screen, it crashes with that memory error.
FileHandle fileIOS1 = Gdx.files.external("iosImage1.png");
FileHandle fileIOS2 = Gdx.files.external("iosImage2.png");
FileHandle fileIOS3 = Gdx.files.external("iosImage3.png");
FileHandle fileIOS4 = Gdx.files.external("iosImage4_.png");
FileHandle fileIOS5 = Gdx.files.external("iosImage5.png");
FileHandle fileIOS6 = Gdx.files.external("iosImage6.png");
FileHandle fileIOS7 = Gdx.files.external("iosImage7.png");
FileHandle fileIOS8 = Gdx.files.external("iosImage8.png");
FileHandle fileIOS9 = Gdx.files.external("iosImage9.png");
FileHandle fileIOS10 = Gdx.files.external("iosImage10.png");
FileHandle fileIOS11 = Gdx.files.external("iosImage11.png");
FileHandle fileIOS12 = Gdx.files.external("iosImage12.png");
public GalleryScreen(MainClass game) {
if(game.isIOS()){
if(fileIOS1.exists()) {
imageSelected1 = new Texture(fileIOS1);
}
if(fileIOS2.exists()) {
imageSelected2 = new Texture(fileIOS2);
}
if(fileIOS3.exists()) {
imageSelected3 = new Texture(fileIOS3);
}
if(fileIOS4.exists()) {
imageSelected4 = new Texture(fileIOS4);
}
if(fileIOS5.exists()) {
imageSelected5 = new Texture(fileIOS5);
}
if(fileIOS6.exists()) {
imageSelected6 = new Texture(fileIOS6);
}
if(fileIOS7.exists()) {
imageSelected7 = new Texture(fileIOS7);
}
if(fileIOS8.exists()) {
imageSelected8 = new Texture(fileIOS8);
}
if(fileIOS9.exists()) {
imageSelected9 = new Texture(fileIOS9);
}
if(fileIOS10.exists()) {
imageSelected10 = new Texture(fileIOS10);
}
if(fileIOS11.exists()) {
imageSelected11 = new Texture(fileIOS11);
}
if(fileIOS12.exists()) {
imageSelected12 = new Texture(fileIOS12);
}
}
#Override
public void show() {
if(game.isIOS()){
if(fileIOS1.exists()) {
selected1 = new TextureRegionDrawable(new TextureRegion(imageSelected1));
style2.up = skin.newDrawable(skin.newDrawable(selected1));
style2.down = skin.newDrawable(skin.newDrawable(selected1));
}
if(!fileIOS1.exists()){
style2.up = skin.newDrawable(skin.newDrawable(temp));
style2.down = skin.newDrawable(skin.newDrawable(temp));
}
if(fileIOS2.exists()) {
selected2 = new TextureRegionDrawable(new TextureRegion(imageSelected2));
style3.up = skin.newDrawable(skin.newDrawable(selected2));
style3.down = skin.newDrawable(skin.newDrawable(selected2));
}
if(!fileIOS2.exists()){
style3.up = skin.newDrawable(skin.newDrawable(temp));
style3.down = skin.newDrawable(skin.newDrawable(temp));
}
if(fileIOS3.exists()){
selected3 = new TextureRegionDrawable(new TextureRegion(imageSelected3));
style4.up = skin.newDrawable(skin.newDrawable(selected3));
style4.down = skin.newDrawable(skin.newDrawable(selected3));
}
if(!fileIOS3.exists()){
style4.up = skin.newDrawable(skin.newDrawable(temp));
style4.down = skin.newDrawable(skin.newDrawable(temp));
}
Gdx.app.log("HERE:", "6");
if(fileIOS4.exists()){
selected4 = new TextureRegionDrawable(new TextureRegion(imageSelected4));
style5.up = skin.newDrawable(skin.newDrawable(selected4));
style5.down = skin.newDrawable(skin.newDrawable(selected4));
}
Gdx.app.log("HERE:", "7");
if(!fileIOS4.exists()){
style5.up = skin.newDrawable(skin.newDrawable(temp));
style5.down = skin.newDrawable(skin.newDrawable(temp));
}
if(fileIOS5.exists()){
selected5 = new TextureRegionDrawable(new TextureRegion(imageSelected5));
style6.up = skin.newDrawable(skin.newDrawable(selected5));
style6.down = skin.newDrawable(skin.newDrawable(selected5));
}
if(!fileIOS5.exists()){
style6.up = skin.newDrawable(skin.newDrawable(temp));
style6.down = skin.newDrawable(skin.newDrawable(temp));
}
if(fileIOS6.exists()){
selected6 = new TextureRegionDrawable(new TextureRegion(imageSelected6));
style7.up = skin.newDrawable(skin.newDrawable(selected6));
style7.down = skin.newDrawable(skin.newDrawable(selected6));
}
if(!fileIOS6.exists()){
style7.up = skin.newDrawable(skin.newDrawable(temp));
style7.down = skin.newDrawable(skin.newDrawable(temp));
}
if(fileIOS7.exists()){
selected7 = new TextureRegionDrawable(new TextureRegion(imageSelected7));
style8.up = skin.newDrawable(skin.newDrawable(selected7));
style8.down = skin.newDrawable(skin.newDrawable(selected7));
}
if(!fileIOS7.exists()){
style8.up = skin.newDrawable(skin.newDrawable(temp));
style8.down = skin.newDrawable(skin.newDrawable(temp));
}
if(fileIOS8.exists()){
selected8 = new TextureRegionDrawable(new TextureRegion(imageSelected8));
style9.up = skin.newDrawable(skin.newDrawable(selected8));
style9.down = skin.newDrawable(skin.newDrawable(selected8));
}
if(!fileIOS8.exists()){
style9.up = skin.newDrawable(skin.newDrawable(temp));
style9.down = skin.newDrawable(skin.newDrawable(temp));
}
if(fileIOS9.exists()){
selected9 = new TextureRegionDrawable(new TextureRegion(imageSelected9));
style10.up = skin.newDrawable(skin.newDrawable(selected9));
style10.down = skin.newDrawable(skin.newDrawable(selected9));
}
if(!fileIOS9.exists()){
style10.up = skin.newDrawable(skin.newDrawable(temp));
style10.down = skin.newDrawable(skin.newDrawable(temp));
}
if(fileIOS10.exists()){
selected10 = new TextureRegionDrawable(new TextureRegion(imageSelected10));
style11.up = skin.newDrawable(skin.newDrawable(selected10));
style11.down = skin.newDrawable(skin.newDrawable(selected10));
}
if(!fileIOS10.exists()){
style11.up = skin.newDrawable(skin.newDrawable(temp));
style11.down = skin.newDrawable(skin.newDrawable(temp));
}
if(fileIOS11.exists()){
selected11 = new TextureRegionDrawable(new TextureRegion(imageSelected11));
style12.up = skin.newDrawable(skin.newDrawable(selected11));
style12.down = skin.newDrawable(skin.newDrawable(selected11));
}
if(!fileIOS11.exists()){
style12.up = skin.newDrawable(skin.newDrawable(temp));
style12.down = skin.newDrawable(skin.newDrawable(temp));
}
if(fileIOS12.exists()){
selected12 = new TextureRegionDrawable(new TextureRegion(imageSelected12));
style13.up = skin.newDrawable(skin.newDrawable(selected12));
style13.down = skin.newDrawable(skin.newDrawable(selected12));
}
if(!fileIOS12.exists()){
style13.up = skin.newDrawable(skin.newDrawable(temp));
style13.down = skin.newDrawable(skin.newDrawable(temp));
}
}boxImage1.addListener(new ChangeListener() {
#Override
public void changed(ChangeEvent event, Actor actor) {
if(!fileIOS1.exists()) {
new Thread(new Runnable() {
#Override
public void run() {
gallery.iosPickImage1();
Gdx.app.postRunnable(new Runnable() {
#Override
public void run() {
imageSelected1 = new Texture(fileIOS1);
selected1 = new TextureRegionDrawable(new TextureRegion(imageSelected1));
style2.up = skin.newDrawable(skin.newDrawable(selected1));
style2.down = skin.newDrawable(skin.newDrawable(selected1));
}
});
}
}).start();
}
if(!fileIOS2.exists()) {
new Thread(new Runnable() {
#Override
public void run() {
gallery.iosPickImage2();
Gdx.app.postRunnable(new Runnable() {
#Override
public void run() {
Gdx.app.log("CREATING IMAGE" , "1");
imageSelected2 = new Texture(fileIOS2);
Gdx.app.log("CREATING IMAGE" , "2");
selected2 = new TextureRegionDrawable(new TextureRegion(imageSelected2));
Gdx.app.log("CREATING IMAGE" , "3");
style3.up = skin.newDrawable(skin.newDrawable(selected2));
style3.down = skin.newDrawable(skin.newDrawable(selected2));
}
});
}
}).start();
}
First of all fix your typo public GalleryScreen(MainClass gam**e** ), secondary you are doing like 12 times the same thing which is memory consuming depending on the image size. Imagine you load 12 Images of 12mb each. Sure this will need alot of memory especially if you decode them to a drawable. If you would just load them as they are you would already consume 144mb of ram which is alot! Decoded they might be way bigger!
Here are some hints which should help
First of write a clean method, which loads an image and resizes
it to a thumbnail to show. Use a final local variable for the huge image, may call the GC manually after you loaded some pictures and resized them. Try to avoid the GC call but sometimes it may be needed. Take a look into: why-is-it-bad-practice-to-call-system-gc
Clean your code write methods. You are checking if the image exists
but why do you check it doulbe? If it exist its loaded so if its
loaded you don’t need to check it again since it's in memory as an
object. Why don’t you load it directly into a TextureRegionDrawable?
Moreover don’t do 12 member variables of the same type. Use a
ArrayList and write dynamic code. Else you'll have to check the whole
Code again if you change something to the picture count.
libgdx is not threadsafe. Spawn a runnable in a runnable might not be a good choice. The runnable in libgdx get started before the next frame. So nothing parallele here so there is no need to spawn a thread to post a runnable. Take a look into the desktop backend app: Dektop backend Frame method
Do not spawn threads on a mobile device if you do not need to. You got the pictures loaded in memory and still spawn a thread to put them to the box. This doesn‘t make sense if they are loaded.

How make my own Stream

I have already try to understand the API doc, the articles about them, and this post: How do you create a Stream in Dart
I'm making a simple web app using WebSocket. Actually, it's working well, but I want add a feature (enjoy learn).
This is my class (can be optimized I guess)
library Ask;
import 'dart:html';
import 'dart:async';
import 'dart:convert';
class Ask {
final String addr;
String _protocol;
String _port;
WebSocket _ws;
bool openned;
Map<int, Completer> _completer_list = {};
int _counter = 0;
static final Map<String, Ask> _cache = <String, Ask>{};
factory Ask(String addr) {
if (_cache.containsKey(addr)) {
return _cache[addr];
} else {
final ask_server = new Ask._internal(addr);
_cache[addr] = ask_server;
return ask_server;
}
}
Ask._internal(this.addr);
Future<bool> open() {
if (openned)
return true;
_completer_list[0] = new Completer();
if (window.location.protocol == 'http:') {
_port = ':8080/ws';
_protocol = 'ws://';
} else {
_port = ':8443/ws';
_protocol = 'wss://';
}
_ws = new WebSocket(_protocol + addr + _port);
_ws.onOpen.listen((e) {
_get_data();
_get_close();
openned = true;
_completer_list[0].complete(true);
});
return _completer_list[0].future;
}
Future<String> send(Map data) {
bool check = false;
int id;
_completer_list.forEach((k, v) {
if (v.isCompleted) {
id = data['ws_id'] = k;
_completer_list[k] = new Completer();
_ws.send(JSON.encode(data));
check = true;
}
});
if (!check) {
_counter++;
id = data['ws_id'] = _counter;
_completer_list[id] = new Completer();
_ws.send(JSON.encode(data));
}
return _completer_list[id].future;
}
void _get_data() {
_ws.onMessage.listen((MessageEvent data) {
var response = JSON.decode(data.data);
_completer_list[response['ws_id']].complete(response);
});
}
void _get_close() {
_ws.onClose.listen((_) {
print('Server have been lost. Try to reconnect in 3 seconds.');
new Timer(new Duration(seconds: 3), () {
_ws = new WebSocket(_protocol + addr + _port);
_get_data();
_get_close();
_ws.onOpen.listen((e) => print('Server is alive again.'));
});
});
}
}
Example of use:
void showIndex() {
Element main = querySelector('main');
Ask connect = new Ask('127.0.0.1');
Map request = {};
request['index'] = true;
connect.open().then((_) {
connect.send(request).then((data) {
main.setInnerHtml(data['response']);
});
});
}
I would replace the then by a listen who will be canceled when the message will completed. By this way, I can add a progress bar, I think...
So my question, my send function can be a stream and keep my concept of one websocket for all ? (yes, if my function is used when a request is in progress, it's sent and if she's finish before the first, I recovered her properly. Thank you ws_id).
Thank you.
I think what you need is a StreamController
https://api.dartlang.org/apidocs/channels/stable/dartdoc-viewer/dart-async.StreamController

Resources