Windows Phone ScheduleAgent grabbing images on set period - c#

I'm attempting to follow an Windows Phone tutorial, tweaking it a bit to fit my needs. I am trying to grab a new picture every X seconds (30 for debugging) and set it as the new lockscreen background. I created a ScheduleAgent object to call it, however, it seems to be skipping a function. It does not skip this when in the MainPage.xaml.cs, which runs the exact same function.
ScheduleAgent.cs
static ScheduledAgent()
{
....<irrelevant code>....
protected override void OnInvoke(ScheduledTask task)
{
.... < Code went here that shouldn't matter > ....
}
Debug.WriteLine("Imagename = " + imageName);
DownloadImagefromServer("https://s3-us-west-2.amazonaws.com/qoutescreen/0" + imageName + ".jpg");
m.ReleaseMutex();
}
// If debugging is enabled, launch the agent again in one minute.
// debug, so run in every 30 secs
#if(DEBUG_AGENT)
ScheduledActionService.LaunchForTest(task.Name, TimeSpan.FromSeconds(30));
System.Diagnostics.Debug.WriteLine("Periodic task is started again: " + task.Name);
#endif
// Call NotifyComplete to let the system know the agent is done working.
NotifyComplete();
}
//===============================================================================
private async void LockScreenChange(string filePathOfTheImage, bool isAppResource)
{
if (!LockScreenManager.IsProvidedByCurrentApplication)
{
// If you're not the provider, this call will prompt the user for permission.
// Calling RequestAccessAsync from a background agent is not allowed.
await LockScreenManager.RequestAccessAsync();
}
// Only do further work if the access is granted.
if (LockScreenManager.IsProvidedByCurrentApplication)
{
// At this stage, the app is the active lock screen background provider.
// The following code example shows the new URI schema.
// ms-appdata points to the root of the local app data folder.
// ms-appx points to the Local app install folder, to reference resources bundled in the XAP package
var schema = isAppResource ? "ms-appx:///" : "ms-appdata:///Local/";
var uri = new Uri(schema + filePathOfTheImage, UriKind.Absolute);
// Set the lock screen background image.
LockScreen.SetImageUri(uri);
// Get the URI of the lock screen background image.
var currentImage = LockScreen.GetImageUri();
System.Diagnostics.Debug.WriteLine("The new lock screen background image is set to {0}", currentImage.ToString());
}
}
private async Task DownloadImagefromServer(string imgUrl)
{
Debug.WriteLine("Attempting to Get Image from Server...");
WebClient client = new WebClient();
try
{
// I GET A "Cannot await Void" problem here?!?!?
//=================================================================
var result = await client.OpenReadAsync(new Uri(imgUrl, UriKind.Absolute));
BitmapImage bitmap = new BitmapImage();
bitmap.SetSource(result);
return result;
}
catch (Exception e)
{
Debug.WriteLine(e);
}
client.OpenReadCompleted += new OpenReadCompletedEventHandler(client_OpenReadCompleted);
//client.OpenReadAsync(new Uri(imgUrl, UriKind.Absolute));
}
//------------------------------------------------------------------
//THIS FUNCTION IS NEVER HIT WHEN RUN IN THE ScheduleAgent.cs CODE, HOWEVER
// WHEN IT IS RUN INT HE MainPage.xaml.cs CODE (Which is a copy/paste) IT RUNS FINE
// WHY IS IT NOT HITTING THIS? HOW CAN I FIX IT?
//------------------------------------------------------------------
void client_OpenReadCompleted(object sender, OpenReadCompletedEventArgs e)
{
BitmapImage bitmap = new BitmapImage();
bitmap.SetSource(e.Result);
//img.Source = bitmap;
// Create a filename for JPEG file in isolated storage.
String tempJPEG = "DownloadedWalleper.jpg";
// Create virtual store and file stream. Check for duplicate tempJPEG files.
using (IsolatedStorageFile myIsolatedStorage = IsolatedStorageFile.GetUserStoreForApplication())
{
if (myIsolatedStorage.FileExists(tempJPEG))
{
myIsolatedStorage.DeleteFile(tempJPEG);
}
IsolatedStorageFileStream fileStream = myIsolatedStorage.CreateFile(tempJPEG);
StreamResourceInfo sri = null;
Uri uri = new Uri(tempJPEG, UriKind.Relative);
sri = Application.GetResourceStream(uri);
//BitmapImage bitmap = new BitmapImage();
//bitmap.SetSource(sri.Stream);
WriteableBitmap wb = new WriteableBitmap(bitmap);
// Encode WriteableBitmap object to a JPEG stream.
Extensions.SaveJpeg(wb, fileStream, wb.PixelWidth, wb.PixelHeight, 0, 85);
//wb.SaveJpeg(fileStream, wb.PixelWidth, wb.PixelHeight, 0, 85);
fileStream.Close();
}
LockScreenChange("DownloadedWalleper.jpg", false);
}
}

I would recommend using the "await" keyword. This will cause the code to pause and wait for the action to complete without needing to worry about mutexes or other thread-blocking strategies.
Unfortunately Windows Phone is missing the non-async methods on WebClient, so there's a little bit of setup, blatantly stolen from this answer.
To use async with WebClient, install the Microsoft.Bcl.Async NuGet package, and then you can use OpenReadTaskAsync
private async void DownloadImagefromServer(string imgUrl)
{
Debug.WriteLine("Attempting to Get Image from Server...");
WebClient client = new WebClient();
var result = await client.OpenReadTaskAsync(new Uri(imgUrl, UriKind.Absolute));
BitmapImage bitmap = new BitmapImage();
bitmap.SetSource(result);
//img.Source = bitmap;
... rest of your code from OpenReadCompleted goes here
}
So what happens is that execution waits for the "OpenRead" call to complete before it processes the result and then returns control.

Related

Asynchronously Load Image from URL into ImageView in Xamarin Android

I have a ListView of multiple items and each item in the list should have an image associated with it. I have created an Array Adapter to hold each list item and have the url of the image I wish to load.
I am trying to asynchronously load the image using a web request and have it set the image and update it in the view once it has been loaded but the view should render immediatly with a default image.
Here is the method I am using to try to load the image
private async Task<Bitmap> GetImageBitmapFromUrl(string url, ImageView imageView)
{
Bitmap imageBitmap = null;
try
{
var uri = new Uri(url);
var response = httpClient.GetAsync(uri).Result;
if (response.IsSuccessStatusCode)
{
var imageBytes = await response.Content.ReadAsByteArrayAsync();
if (imageBytes != null && imageBytes.Length > 0)
{
imageBitmap = BitmapFactory.DecodeByteArray(imageBytes, 0, imageBytes.Length);
imageView.SetImageBitmap(imageBitmap);
}
}
}
catch (Exception e)
{
Console.WriteLine(e);
}
return imageBitmap;
}
I am initializing my HttpClient like so:
httpClient = new HttpClient();
httpClient.Timeout = new TimeSpan(0, 0, 10);
httpClient.MaxResponseContentBufferSize = 256000;
And here is how I am calling the GetImageFromUrl Method
ImageView myImage = convertView.FindViewById<ImageView>(Resource.Id.AsyncImageView)
myImage.SetImageBitmap(null);
string url = GetItem(position);
GetImageBitmapFromUrl(url, myImage);
Using this code the request eventually comes back with a bad request but when I view the url I am using and paste it into a browser window it brings up the image I am expecting.
You don't need to care about downloading images.
There exist two good libraries to load an image into a ImageView async in Xamarin.Android.
Picasso component (source) usage:
Picasso.With(context)
.Load("http://i.imgur.com/DvpvklR.png")
.Into(imageView);
FFImageLoading (source) usage:
ImageService.Instance.LoadUrl(urlToImage).Into(_imageView);
Note (from documentation):
Unlike Picasso you cannot use FFImageLoading with standard ImageViews.
Instead simply load your images into ImageViewAsync instances.
Updating your code is very easy since ImageViewAsync inherits from
ImageView.

Get WriteableBitmap from url in UWP?

So my app gets a BitmapImage from a url, which works, and I then need to assign that image to a writeablebitmap. For the moment I have this:
Debug.WriteLine("Poster opened for loading. Url=" + e);
BitmapImage img = new BitmapImage(new Uri(e));
Backdrop.Source = img;
img.ImageOpened += async (s, e1) =>
{
WriteableBitmap wbm = new WriteableBitmap(((BitmapImage)s).PixelWidth, ((BitmapImage)s).PixelHeight);
var storageFile = await StorageFile.GetFileFromApplicationUriAsync(new Uri(e));
using (var stream = await storageFile.OpenReadAsync())
{
await wbm.SetSourceAsync(stream);
}
};
Which is really awful code, but it all works until StorageFile.GetFileFromApplicationUriAsync(), where I'm told the Value falls within an unexpected range. I'm guessing this is because it only acceps the "ms-appx:///" format, and not "http://". Is there any way to get this working? And eventually in a cleaner way?
I thought this would be easy, but it's a nightmare. I've been trying to do this since yesterday.
P.S. I know this might appear like a duplicate, but I have found absolutely nothing on StackOverflow that actually works.
Try this:
var httpClient = new HttpClient();
var buffer = await httpClient.GetBufferAsync(pictureUri);
var writeableBitmap = new WriteableBitmap(width, height);
using (var stream = buffer.AsStream())
{
await writeableBitmap.SetSourceAsync(stream.AsRandomAccessStream());
}

How to download image from URL

Is there a way to download an image directly from a url in c# if the url does not have an image format at the end of the link? Example of URL:
https://fbcdn-sphotos-h-a.akamaihd.net/hphotos-ak-xpf1/v/t34.0-12/10555140_10201501435212873_1318258071_n.jpg?oh=97ebc03895b7acee9aebbde7d6b002bf&oe=53C9ABB0&__gda__=1405685729_110e04e71d969d392b63b27ec4f4b24a
I know how to download the image when the url ends with an image format. Eg:
http://img1.wikia.nocookie.net/__cb20101219155130/uncyclopedia/images/7/70/Facebooklogin.png
Simply
You can use following methods.
using (WebClient client = new WebClient())
{
client.DownloadFile(new Uri(url), #"c:\temp\image35.png");
// OR
client.DownloadFileAsync(new Uri(url), #"c:\temp\image35.png");
}
These methods are almost same as DownloadString(..) and DownloadStringAsync(...). They store the file in Directory rather than in C# string and no need of Format extension in URi
If You don't know the Format(.png, .jpeg etc) of Image
public void SaveImage(string imageUrl, string filename, ImageFormat format)
{
WebClient client = new WebClient();
Stream stream = client.OpenRead(imageUrl);
Bitmap bitmap; bitmap = new Bitmap(stream);
if (bitmap != null)
{
bitmap.Save(filename, format);
}
stream.Flush();
stream.Close();
client.Dispose();
}
Using it
try
{
SaveImage("--- Any Image URL---", "--- Any Image Path ---", ImageFormat.Png)
}
catch(ExternalException)
{
// Something is wrong with Format -- Maybe required Format is not
// applicable here
}
catch(ArgumentNullException)
{
// Something wrong with Stream
}
Depending whether or not you know the image format, here are ways you can do it :
Download Image to a file, knowing the image format
using (WebClient webClient = new WebClient())
{
webClient.DownloadFile("http://yoururl.com/image.png", "image.png") ;
}
Download Image to a file without knowing the image format
You can use Image.FromStream to load any kind of usual bitmaps (jpg, png, bmp, gif, ... ), it will detect automaticaly the file type and you don't even need to check the url extension (which is not a very good practice). E.g:
using (WebClient webClient = new WebClient())
{
byte [] data = webClient.DownloadData("https://fbcdn-sphotos-h-a.akamaihd.net/hphotos-ak-xpf1/v/t34.0-12/10555140_10201501435212873_1318258071_n.jpg?oh=97ebc03895b7acee9aebbde7d6b002bf&oe=53C9ABB0&__gda__=1405685729_110e04e71d9");
using (MemoryStream mem = new MemoryStream(data))
{
using (var yourImage = Image.FromStream(mem))
{
// If you want it as Png
yourImage.Save("path_to_your_file.png", ImageFormat.Png) ;
// If you want it as Jpeg
yourImage.Save("path_to_your_file.jpg", ImageFormat.Jpeg) ;
}
}
}
Note : ArgumentException may be thrown by Image.FromStream if the downloaded content is not a known image type.
Check this reference on MSDN to find all format available.
Here are reference to WebClient and Bitmap.
.NET has changed a bit over the years, making the other answers on this post pretty dated:
They use Image from System.Drawing (which is not available for .NET Core) to find the image format
They use System.Net.WebClient which is deprecated
We don't recommend that you use the WebClient class for new development. Instead, use the System.Net.Http.HttpClient class.
.NET Core asynchronous solution
Getting the file extension
The first part of getting the file extension is to remove all the unnecessary parts from the URL.
We can use Uri.GetLeftPart() with UriPartial.Path to get everything from the Scheme up to the Path.
In other words, https://www.example.com/image.png?query&with.dots becomes https://www.example.com/image.png.
After that, we can use Path.GetExtension() to get only the extension (in my previous example, .png).
var uriWithoutQuery = uri.GetLeftPart(UriPartial.Path);
var fileExtension = Path.GetExtension(uriWithoutQuery);
Downloading the image
From here it should be straight forward. Download the image with HttpClient.GetByteArrayAsync, create the path, ensure the directory exists and then write the bytes to the path with File.WriteAllBytesAsync()
private async Task DownloadImageAsync(string directoryPath, string fileName, Uri uri)
{
using var httpClient = new HttpClient();
// Get the file extension
var uriWithoutQuery = uri.GetLeftPart(UriPartial.Path);
var fileExtension = Path.GetExtension(uriWithoutQuery);
// Create file path and ensure directory exists
var path = Path.Combine(directoryPath, $"{fileName}{fileExtension}");
Directory.CreateDirectory(directoryPath);
// Download the image and write to the file
var imageBytes = await httpClient.GetByteArrayAsync(uri);
await File.WriteAllBytesAsync(path, imageBytes);
}
Note that you need the following using directives.
using System;
using System.IO;
using System.Threading.Tasks;
using System.Net.Http;
Example usage
var folder = "images";
var fileName = "test";
var url = "https://cdn.discordapp.com/attachments/458291463663386646/592779619212460054/Screenshot_20190624-201411.jpg?query&with.dots";
await DownloadImageAsync(folder, fileName, new Uri(url));
Notes
It's bad practice to create a new HttpClient for every method call. It is supposed to be reused throughout the application. I wrote a short example of an ImageDownloader(50 lines) with more documentation that correctly reuses the HttpClient and properly disposes of it that you can find here.
For anyone who wants to download an image WITHOUT saving it to a file:
Image DownloadImage(string fromUrl)
{
using (System.Net.WebClient webClient = new System.Net.WebClient())
{
using (Stream stream = webClient.OpenRead(fromUrl))
{
return Image.FromStream(stream);
}
}
}
.net Framework allows PictureBox Control to Load Images from url
and Save image in Laod Complete Event
protected void LoadImage() {
pictureBox1.ImageLocation = "PROXY_URL;}
void pictureBox1_LoadCompleted(object sender, AsyncCompletedEventArgs e) {
pictureBox1.Image.Save(destination); }
Most of the posts that I found will timeout after a second iteration. Particularly if you are looping through a bunch if images as I have been. So to improve the suggestions above here is the entire method:
public System.Drawing.Image DownloadImage(string imageUrl)
{
System.Drawing.Image image = null;
try
{
System.Net.HttpWebRequest webRequest = (System.Net.HttpWebRequest)System.Net.HttpWebRequest.Create(imageUrl);
webRequest.AllowWriteStreamBuffering = true;
webRequest.Timeout = 30000;
webRequest.ServicePoint.ConnectionLeaseTimeout = 5000;
webRequest.ServicePoint.MaxIdleTime = 5000;
using (System.Net.WebResponse webResponse = webRequest.GetResponse())
{
using (System.IO.Stream stream = webResponse.GetResponseStream())
{
image = System.Drawing.Image.FromStream(stream);
}
}
webRequest.ServicePoint.CloseConnectionGroup(webRequest.ConnectionGroupName);
webRequest = null;
}
catch (Exception ex)
{
throw new Exception(ex.Message, ex);
}
return image;
}
This method did it for me,
I got the main code from here
then using this fix
I was able to make a method that could get around the dreaded forbidden 403 error
Here is the method
private static void DownloadImage(string url, string saveFilename)
{
var httpWebRequest = (HttpWebRequest)WebRequest.Create(url);
// we need the user agent and default credentials if not,
// we get a forbidden request 303 error, which pretty much means the server thinks we are a bot -- which we are.... hehehehehehe
httpWebRequest.UserAgent = "Case Banana"; // note -- this string can be anything you like, I recommend making it atleast 10 characters
httpWebRequest.UseDefaultCredentials = true;
var httpWebResponse = (HttpWebResponse)httpWebRequest.GetResponse();
if ((httpWebResponse.StatusCode != HttpStatusCode.OK &&
httpWebResponse.StatusCode != HttpStatusCode.Moved &&
httpWebResponse.StatusCode != HttpStatusCode.Redirect)
|| !httpWebResponse.ContentType.StartsWith("image", StringComparison.OrdinalIgnoreCase))
{
return;
}
using (var stream = httpWebResponse.GetResponseStream())
{
using (var fileStream = File.OpenWrite(saveFilename))
{
var bytes = new byte[4096];
var read = 0;
do
{
if (stream == null)
{
continue;
}
read = stream.Read(bytes, 0, bytes.Length);
fileStream.Write(bytes, 0, read);
} while (read != 0);
}
}
}
Everyone has given a great solution for this problem but theere is a main issue behind all solutions given by everyone and that it will not create SSL/TLS secure channel if the image is hosted on https So, what should we do then? Answer is simple just add these 2 lines before creating a WebClient request
ServicePointManager.Expect100Continue = true;
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
Try this it worked for me
Write this in your Controller
public class DemoController: Controller
public async Task<FileStreamResult> GetLogoImage(string logoimage)
{
string str = "" ;
var filePath = Server.MapPath("~/App_Data/" + SubfolderName);//If subfolder exist otherwise leave.
// DirectoryInfo dir = new DirectoryInfo(filePath);
string[] filePaths = Directory.GetFiles(#filePath, "*.*");
foreach (var fileTemp in filePaths)
{
str= fileTemp.ToString();
}
return File(new MemoryStream(System.IO.File.ReadAllBytes(str)), System.Web.MimeMapping.GetMimeMapping(str), Path.GetFileName(str));
}
Here is my view
<div>Download Logo</div>

Unlock file generated from Bitmap object

I have this method to get a thumbnail from an image on disk:
public static BitmapImage GetThumbnail()
{
var dlg = new OpenFileDialog {Filter = "Imágenes|" + extensionesImagenes};
var result = dlg.ShowDialog();
if (result == true)
{
var tempFolder = Path.GetTempPath() + "MyTempFolder\\";
if (!Directory.Exists(tempFolder))
Directory.CreateDirectory(tempFolder);
using (var thumbnail = new Bitmap(170, 170))
{
using (var gr = Graphics.FromImage(thumbnail))
{
gr.SmoothingMode = SmoothingMode.HighQuality;
gr.InterpolationMode = InterpolationMode.HighQualityBicubic;
gr.PixelOffsetMode = PixelOffsetMode.HighQuality;
gr.DrawImage(new Bitmap(dlg.FileName), new Rectangle(0, 0, 170, 170));
}
thumbnail.Save(tempFolder + "foto.jpg", ImageFormat.Jpeg);
}
return new BitmapImage(new Uri(tempFolder + "foto.jpg"));
}
return null;
}
If I run it for the first time there's no problem. But when I run it for the second time I get "A generic error occurred in GDI+" exception because the file is being used by my app.
The method could be invoked several times depending of the actions made by users and the file should be overwritten. As a matter of fact, if the file exist when I invoke the method for the first time it's successfully overwritten.
The file is generated by the Save() method of the Bitmap class and I'm not using any stream.
How can I unlock the file?
TIA
EDIT
I'm not using MemoryStream because I need the file in another method: when an user invokes the GetThumbnail method the thumbnail will be shown in a StackPanel (the thumbnail is the ImageSource of the StackPanel.)
Later the user could save to database the info shown in a WPF form, image included, and the method that saves to database need to read the bytes from the saved image.
EDIT 2
Before using thumbnails I was using this method:
public static BitmapImage GetImage()
{
var dlg = new OpenFileDialog {Filter = "Imágenes|" + extensionesImagenes};
var result = dlg.ShowDialog();
return result == true ? new BitmapImage(new Uri(dlg.FileName)) : null;
}
It was working ok.
Now that I'm using thumbnails the problem with a MemoryStream is that I can't set the Uri for the image.
For example I was using:
imagenNueva = InterfazUtil.GetImage(); // GetImage() is now GetThumbnail()
var rutaFoto = (imagenNueva != null) ? imagenNueva.ToString() : null;
The string rutaFoto was passed to the method that save the info to the database and it uses this variable to read the image from disk.
In the GetThumbnail method I did convert the Image thumbnail to a BitmapImage and I did try to set the SourceUri and BaseUri with:
new Uri(dialog.FileName);
But when I invoke imagenNueva.ToString() I don't get a valid Uri.
gr.DrawImage(new Bitmap(dlg.FileName), new Rectangle(0, 0, 170, 170));
Using a bitmap like that is very troublesome. It locks the "dlg.FileName" file and it will take a while before the garbage collector releases it. You'll need to write it like this instead:
using (var bmp = new Bitmap(dlg.FileName)) {
gr.DrawImage(bmp, new Rectangle(0, 0, 170, 170));
}
Now it gets disposed immediately after use and that releases the lock on the file as well.
Btw, you can completely avoid writing code like this if you use a MemoryStream instead. Just save the image to the MemoryStream so you don't need a file. Assuming the images are not that big or you can count on running on a 64-bit operating system. Not otherwise a reason to avoid disposing bitmaps.

Setting Windows 8 account picture with .NET/WinRT hybrid

Basically I'm trying to set a Windows 8 account picture from a "Console Application". The only API to set the picture that I found is in the WinRT API. Basically the same question (Windows 8 Set User Account Image) led me at least being able to build the hybrid, but it doesn't seem to be working.
private static async Task SetAccountPicture(string employeeId)
{
// Download image
string imagePath = DownloadImage(employeeId);
// Set account picture
Stream fileStream = File.Open(imagePath, FileMode.Open);
IRandomAccessStream winRTStream = await DotNetToWinRTStream(fileStream);
SetAccountPictureResult result = await UserInformation.SetAccountPicturesFromStreamsAsync(null, winRTStream, null);
// Clean up download file
File.Delete(imagePath);
}
public static async Task<IRandomAccessStream> DotNetToWinRTStream(Stream dotNetStream)
{
IBuffer buffer;
var inputStream = dotNetStream.AsInputStream();
using (var reader = new DataReader(inputStream))
{
await reader.LoadAsync((uint)dotNetStream.Length);
buffer = reader.DetachBuffer();
}
var memoryStream = new InMemoryRandomAccessStream();
await memoryStream.WriteAsync(buffer);
return memoryStream;
}
The SetAccountPictureResult says 'failure' without any other information. What could be happening here?

Categories