I need to use a prepopulated database in my Xamarin.Forms application, so I searched for possible solutions.
I've found this article and tested with Android - it worked okay.
However, it uses Windows Phone 8 - that is not compatible with Windows 8.1.
So I tried to modify this Windows Phone 8 code:
public static void CopyDatabaseIfNotExists(string dbPath)
{
var storageFile = IsolatedStorageFile.GetUserStoreForApplication();
if (!storageFile.FileExists(dbPath))
{
using (var resourceStream = Application.GetResourceStream(new Uri("people.db3", UriKind.Relative)).Stream)
{
using (var fileStream = storageFile.CreateFile(dbPath))
{
byte[] readBuffer = new byte[4096];
int bytes = -1;
while ((bytes = resourceStream.Read(readBuffer, 0, readBuffer.Length)) > 0)
{
fileStream.Write(readBuffer, 0, bytes);
}
}
}
}
}
Into this code:
public static async void CopyDatabaseIfNotExists(string dbPath)
{
IStorageFolder applicationFolder = ApplicationData.Current.LocalFolder;
StorageFile existingFile = await Windows.Storage.StorageFile.GetFileFromPathAsync("prep.db");
IStorageFile storageFile = await applicationFolder.GetFileAsync(dbPath);
if (storageFile == null)
{
await existingFile.CopyAndReplaceAsync(storageFile);
However, it does not work, I can't provide a proper filepath for my existing db file (it is in the root of the project), it always gives me this error:
Value does not fall within the expected range.
How could I get a proper path to my prepopulated file?
Also, why do I need to use a stream based "copy" when I could simply copy the file itself?
The following code works for Windows Phone 8.1 and UWP:
public async void CopyDatabaseIfNotExists(string dbPath)
{
IStorageFolder applicationFolder = ApplicationData.Current.LocalFolder;
var existingFile = await Windows.ApplicationModel.Package.Current.InstalledLocation.GetFileAsync(myDBFileName);
if (!await CheckForExistingFile(myDBFileName))
await existingFile.CopyAsync(applicationFolder);
}
private async Task<bool> CheckForExistingFile(string filePath)
{
try
{
var file = await ApplicationData.Current.LocalFolder.GetFileAsync(Uri.EscapeDataString(filePath));
//no exception means file exists
return true;
}
catch (FileNotFoundException ex)
{
//find out through exception
return false;
}
}
Related
During the UWP conversion of an application that was previously designed as a winform, errors occurred in my logging method and I had to change the System.IO reference to Storage. As a result of this change, when the synchronous method started to work asynchronously, I got errors about the request to use the file at the same time. Then I came up with a simple solution by creating the class I mentioned below but this quick amateurish approach do you think is correct?
public class Logging_UWP
{
private List<string> CompletedLog = new List<string>();
private string machineCode = string.Empty;
public Logging_UWP(string MachineCode)
{
machineCode = MachineCode;
Task.Factory.StartNew(()=> WriteLog());
}
public void WriteLogSync(string NewLog)
{
CompletedLog.Add(NewLog);
}
private async void WriteLog()
{
while (true)
{
try
{
foreach (string NewLog in CompletedLog)
{
StorageFolder storageFolder = ApplicationData.Current.LocalFolder;
StorageFile sampleFile = await storageFolder.CreateFileAsync(machineCode + ".log", CreationCollisionOption.OpenIfExists);
var stream = await sampleFile.OpenAsync(Windows.Storage.FileAccessMode.ReadWrite);
using (var outputStream = stream.GetOutputStreamAt(stream.Size + 1))
{
using (var dataWriter = new Windows.Storage.Streams.DataWriter(outputStream))
{
dataWriter.WriteString(String.Format("\n[{0}-{1}] >>> {2}", DateTime.Now.ToShortDateString(), DateTime.Now.ToLongTimeString(), NewLog));
await dataWriter.StoreAsync();
await outputStream.FlushAsync();
}
}
}
if (CompletedLog.Count > 0)
CompletedLog.Clear();
await Task.Delay(500);
}
catch (Exception)
{ }
}
}
} '''
I am developing a Xamarin app which retrives info from DB, take/choose photo and upload them to remote server, display this images from the remote server and the user can delete them by tap on and press a button and download the images from the remote server to the local device.
Everything works without problem, but when I download the image and after I go to the gallery for check it, the image does not appear, whereas I can see it and open in the file explorer. When I reboot the phone, the image appear in the gallery.
Below my current button download method:
private void button_download_image_Clicked(object sender, EventArgs e)
{
Uri image_url_format = new Uri(image_url);
WebClient webClient = new WebClient();
try
{
byte[] bytes_image = webClient.DownloadData(image_url_format);
Stream image_stream = new MemoryStream(bytes_image);
string dest_folder = Android.OS.Environment.GetExternalStoragePublicDirectory(Android.OS.Environment.DirectoryDownloads).ToString();
string file_name = System.IO.Path.GetFileName(image_url_format.LocalPath);
string dest_path = System.IO.Path.Combine(dest_folder, file_name);
using (var fileStream = new FileStream(dest_path, FileMode.Create, FileAccess.Write))
{
image_stream.CopyTo(fileStream);
}
}
catch (Exception ex)
{
DisplayAlert("Error", ex.ToString(), "OK");
}
DisplayAlert("Alert", "Download completed!", "OK");
}
I tried in another device, but I got the same behavior.
Probably there is a sort of thing which does not refresh the gallery.
Any idea how to force the gallery to refresh or something similar?
You need to refresh your gallery after inserting or deleting any pictures in storage.
You can try this.
var mediaScanIntent = new Intent(Intent.ActionMediaScannerScanFile);
mediaScanIntent.SetData(Android.Net.Uri.FromFile(new Java.IO.File(dest_path)));
Xamarin.Forms.Forms.Context.SendBroadcast(mediaScanIntent);
Add these lines below your code.
Make it like
private void button_download_image_Clicked(object sender, EventArgs e)
{
Uri image_url_format = new Uri(image_url);
WebClient webClient = new WebClient();
try
{
byte[] bytes_image = webClient.DownloadData(image_url_format);
Stream image_stream = new MemoryStream(bytes_image);
string dest_folder = Android.OS.Environment.GetExternalStoragePublicDirectory(Android.OS.Environment.DirectoryDownloads).ToString();
string file_name = System.IO.Path.GetFileName(image_url_format.LocalPath);
string dest_path = System.IO.Path.Combine(dest_folder, file_name);
using (var fileStream = new FileStream(dest_path, FileMode.Create, FileAccess.Write))
{
image_stream.CopyTo(fileStream);
}
var mediaScanIntent = new Intent(Intent.ActionMediaScannerScanFile);
mediaScanIntent.SetData(Android.Net.Uri.FromFile(new Java.IO.File(dest_path)));
//for old xamarin forms version
Xamarin.Forms.Forms.Context.SendBroadcast(mediaScanIntent);
//for new xamarin forms version
//Android.App.Application.SendBroadcast(mediaScanIntent);
}
catch (Exception ex)
{
DisplayAlert("Error", ex.ToString(), "OK");
return;
}
DisplayAlert("Alert", "Download completed!", "OK");
}
You need to just refresh the file you have downloaded. It's helpful.
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT){
Intent mediaScanIntent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
File f = new File("file://"+ Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES));
Uri contentUri = Uri.fromFile(f);
mediaScanIntent.setData(contentUri);
this.sendBroadcast(mediaScanIntent);
}else{
sendBroadcast(new Intent(Intent.ACTION_MEDIA_MOUNTED, Uri.parse("file://" + Environment.getExternalStorageDirectory())));
}
Make sure required permission given on both platforms.
Use in your class:
bool success = await DependencyService.Get<IPhotoLibrary>().SavePhotoAsync(data, folder, filename);
Common Interface
public interface IPhotoLibrary
{
Task<bool> SavePhotoAsync(byte[] data, string folder, string filename);
}
In Android service
public async Task<bool> SavePhotoAsync(byte[] data, string folder, string filename)
{
try
{
File picturesDirectory = Environment.GetExternalStoragePublicDirectory(Environment.DirectoryPictures);
File folderDirectory = picturesDirectory;
if (!string.IsNullOrEmpty(folder))
{
folderDirectory = new File(picturesDirectory, folder);
folderDirectory.Mkdirs();
}
using (File bitmapFile = new File(folderDirectory, filename))
{
bitmapFile.CreateNewFile();
using (FileOutputStream outputStream = new FileOutputStream(bitmapFile))
{
await outputStream.WriteAsync(data);
}
// Make sure it shows up in the Photos gallery promptly.
MediaScannerConnection.ScanFile(MainActivity.Instance,
new string[] { bitmapFile.Path },
new string[] { "image/png", "image/jpeg" }, null);
}
}
catch (System.Exception ex)
{
return false;
}
return true;
}
In iOS service:
public Task<bool> SavePhotoAsync(byte[] data, string folder, string filename)
{
NSData nsData = NSData.FromArray(data);
UIImage image = new UIImage(nsData);
TaskCompletionSource<bool> taskCompletionSource = new TaskCompletionSource<bool>();
image.SaveToPhotosAlbum((UIImage img, NSError error) =>
{
taskCompletionSource.SetResult(error == null);
});
return taskCompletionSource.Task;
}
also you can refer this one just to save an image and to reflect it in media, no need to use skiasharp for that. https://learn.microsoft.com/en-us/xamarin/xamarin-forms/user-interface/graphics/skiasharp/bitmaps/saving
Hope this may resolve your issue.
Refer to Blu's answer,
I changed this Xamarin.Forms.Forms.Context.SendBroadcast(mediaScanIntent); to Android.App.Application.Context.SendBroadcast(mediaScanIntent); and all works.
I want to add a custom tile to the Microsoft Band through Microsoft Band SDK in a UWP app for Windows Phone. Here is my sample code.
private async void ButtonBase_OnClick(object sender, RoutedEventArgs e)
{
try
{
// Get the list of Microsoft Bands paired to the phone.
var pairedBands = await BandClientManager.Instance.GetBandsAsync();
if (pairedBands.Length < 1)
{
Debug.WriteLine("This sample app requires a Microsoft Band paired to your device.Also make sure that you have the latest firmware installed on your Band, as provided by the latest Microsoft Health app.");
return;
}
// Connect to Microsoft Band.
using (var bandClient = await BandClientManager.Instance.ConnectAsync(pairedBands[0]))
{
// Create a Tile with a TextButton on it.
var myTileId = new Guid("12408A60-13EB-46C2-9D24-F14BF6A033C6");
var myTile = new BandTile(myTileId)
{
Name = "My Tile",
TileIcon = await LoadIcon("ms-appx:///Assets/SampleTileIconLarge.png"),
SmallIcon = await LoadIcon("ms-appx:///Assets/SampleTileIconSmall.png")
};
// Remove the Tile from the Band, if present. An application won't need to do this everytime it runs.
// But in case you modify this sample code and run it again, let's make sure to start fresh.
await bandClient.TileManager.RemoveTileAsync(myTileId);
// Create the Tile on the Band.
await bandClient.TileManager.AddTileAsync(myTile);
// Subscribe to Tile events.
}
}
catch (Exception ex)
{
Debug.WriteLine(ex);
}
}
private async Task<BandIcon> LoadIcon(string uri)
{
StorageFile imageFile = await StorageFile.GetFileFromApplicationUriAsync(new Uri(uri));
using (IRandomAccessStream fileStream = await imageFile.OpenAsync(FileAccessMode.Read))
{
WriteableBitmap bitmap = new WriteableBitmap(1, 1);
await bitmap.SetSourceAsync(fileStream);
return bitmap.ToBandIcon();
}
}
If I run this code nothing happend. The app connected to Microsoft Band, but is not able to add a tile. The method AddTileAsync(myTile); Returns false and doesn't add a tile to the Microsoft Band.
If I try this code in a Windows Phone 8.1 app it works, but not in the UWP app.
Any ideas?
Update
Here is the sample app as download. Maybe this can help.
maybe this would help, coming from the documentation of MS Band
using Microsoft.Band.Tiles;
...
try
{
IEnumerable<BandTile> tiles = await bandClient.TileManager.GetTilesAsync();
}
catch (BandException ex)
{
//handle exception
}
//determine if there is space for tile
try
{
int tileCapacity = await bandClient.TileManager.GetRemainingTileCapacityAsync();
}
catch (BandException ex)
{
//handle ex
}
//create tile
WriteAbleBitmap smallIconBit = new WriteAbleBitmap(24, 24);
BandIcon smallIcon = smallIconBit.ToBandIcon();
WriteAbleBitmap largeIconBit = new WriteAbleBitmap(48, 48);//46, 46 for MS band 1
BandIcon largeIcon = largeIconBit.ToBandIcon();
Guid guid = Guid.NewGuid();
BandTile tile = new BandTile(guid)
{
//enable Badging
IsBadgingEnabled = true,
Name = "MYNAME"
SmallIcon = smallIcon;
TileIcon = largeIcon;
};
try
{
if(await bandClient.TileManager.AddTileAsync(tile))
{
///console print something
}
}
catch(BandException ex)
{
//blabla handle
}
I think the issue may be you're setting the writeable bitmap size to (1,1)?
I have this method working:
public static class BandIconUtil
{
public static async Task<BandIcon> FromAssetAsync(string iconFileName, int size = 24)
{
string uri = "ms-appx:///" + iconFileName;
StorageFile imageFile = await StorageFile.GetFileFromApplicationUriAsync(new Uri(uri, UriKind.RelativeOrAbsolute));
using (IRandomAccessStream fileStream = await imageFile.OpenAsync(FileAccessMode.Read))
{
WriteableBitmap bitmap = new WriteableBitmap(size, size);
await bitmap.SetSourceAsync(fileStream);
return bitmap.ToBandIcon();
}
}
}
I tried this below code, the audio file is saving but it shows 0 bytes, I tried a lot if any one know about this please help me...
WP8.1 Universal Apps
Code in Mainpage.cs:
private async void pick_Click(object sender, RoutedEventArgs e)
{
string path = #"Assets\Audio\DeviPrasad.mp3";
StorageFolder folder = Windows.ApplicationModel.Package.Current.InstalledLocation;
StorageFile file = await folder.GetFileAsync(path);
var st =await file.OpenAsync(Windows.Storage.FileAccessMode.Read);
var size = st.Size;
FileSavePicker savePicker = new FileSavePicker();
//savePicker.SuggestedStartLocation = PickerLocationId.DocumentsLibrary;
savePicker.SuggestedSaveFile = file;
savePicker.FileTypeChoices.Add("MP3", new List<string>() { ".mp3" });
savePicker.ContinuationData.Add("SourceSound", path);
savePicker.SuggestedFileName = "DeviPrasad";
savePicker.PickSaveFileAndContinue();
}
internal async void ContinueFileOpenPicker(FileSavePickerContinuationEventArgs e)
{
var file = e.File;
var ff= file.Properties;
if(file!=null)
{
CachedFileManager.DeferUpdates(file);
await FileIO.WriteTextAsync(file, file.Name);
FileUpdateStatus status = await CachedFileManager.CompleteUpdatesAsync(file);
}
}
Code In App.xaml.cs:
protected async override void OnActivated(IActivatedEventArgs args)
{
var root = Window.Current.Content as Frame;
var mainPage = root.Content as MainPage;
if (mainPage != null && args is FileSavePickerContinuationEventArgs)
{
mainPage.ContinueFileOpenPicker(args as FileSavePickerContinuationEventArgs);
}
}
The FileSavePicker will not help you to write or copy the content of the file to the destination.
Actually, I think you just simply copy the following code from somewhere but don't exactly know what it is doing.
await FileIO.WriteTextAsync(file, file.Name);
This is to write things to the file, and seems it is from a sample which handles txt file. For your case, we need to do the following things:
Add the source file path to ContinuationData in pick_Click event. Change the code to:
savePicker.ContinuationData.Add("SourceSound", file.Path);
Read the source file path and destination file in ContinueFileOpenPicker to write the content as below:
var file = e.File;
string soundPath = (string)e.ContinuationData["SourceSound"];
//var ff = file.Properties;
if (file != null)
{
CachedFileManager.DeferUpdates(file);
StorageFile srcFile = await StorageFile.GetFileFromPathAsync(soundPath);
await srcFile.CopyAndReplaceAsync(file);
FileUpdateStatus status = await CachedFileManager.CompleteUpdatesAsync(file);
}
Directory.Exists(imgFolder.Path);
alternate method in win8.1 store app,
im tried search online but i do get result for file exists only not to check folder exists
In Windows 8.1 you need to do something like following.
The following method will check if files exists or not:
public async Task<bool> isFilePresent(string fileName)
{
bool fileExists = true;
Stream fileStream = null;
StorageFile file = null;
try
{
file = await ApplicationData.Current.LocalFolder.GetFileAsync(fileName);
fileStream = await file.OpenStreamForReadAsync();
fileStream.Dispose();
}
catch (FileNotFoundException)
{
// If the file dosn't exits it throws an exception, make fileExists false in this case
fileExists = false;
}
finally
{
if (fileStream != null)
{
fileStream.Dispose();
}
}
return fileExists;
}
or:
public async Task<bool> isFilePresent(string fileName)
{
var item = await ApplicationData.Current.LocalFolder.TryGetItemAsync(fileName);
return item != null;
}
From Check If File Exists in Windows Phone 8 and Win RT