Xamarin iOS: How to open a .pdf file using the default reader? - c#

I'm using Xamarin iOS. I'd like to have our application open a .pdf file in the default reader. For Android I managed to get it done by following a two-fold process: (1) copy the file from the Assets folder to the Android device's Downloads folder, and (2) starting an activity to view the file.
I cannot find any similar resources for the iOS. We don't want to use the control in the Xamarin website, as it involves downloading lots of JavaScript (?) code, making the size of the app bigger, and also doesn't support pinching the screen to zoom. We want to use the OS's default pdf reader.
Thank you.

Method 1:
Set the Build Action to BundleResource. You can set the build action for a file by right-clicking on that file and and choosing Build Action in the menu that opens.
Create a UIWebView and add it to a view:
webView = new UIWebView (View.Bounds);
View.AddSubview(webView);
Load the file using NSUrl and NSUrlRequest classes:
string fileName = "Loading a Web Page.pdf"; // remember case-sensitive
string localDocUrl = Path.Combine (NSBundle.MainBundle.BundlePath, fileName);
webView.LoadRequest(new NSUrlRequest(new NSUrl(localDocUrl, false)));
webView.ScalesPageToFit = true;
Method 2:
public void OpenFile (NSUrl fileUrl)
{
var docControl = UIDocumentInteractionController.FromUrl (fileUrl);
var window = UIApplication.SharedApplication.KeyWindow;
var subViews = window.Subviews;
var lastView = subViews.Last ();
var frame = lastView.Frame;
docControl.PresentOpenInMenu (frame, lastView, true);
}

Related

UIDocumentInteractionController always creates a copy

I'm currently developing a Xamarin.iOS App that gets a document from a web service that should then be edited offline on the tablet after being downloaded to the internal storage.
The most common answer was to use the UIDocumentInteractionController. However if I use the UIDocumentInteractionController then I can only create a copy of my original file and open this copy. To get it back into my app I have to make the user select the document from the 'UIDocumentPickerViewController'.
Is there a better way to make the `UIDocumentInteractionController' not create and open a copy of the original, or to at least get the url from the new documentcopy?
Code to open the file:
public void OpenFile()
{
var url = NSUrl.FromFilename(FilePath);
var controller = new UIDocumentInteractionController();
controller.Url = url;
controller.PresentOpenInMenu(table.Frame, table, true);
}
If that is not Possible: Are there different tools or controlls i could use to open and edit a MS-Office file directly?

Xamarin media plugin get android image path

I am using the media plugin for xamarin forms (by james montemagno) and the actual taking of the picture and storing it works fine, I have debugged the creation of the image on the emulator and it is stored in
/storage/emulated/0/Android/data/{APPNAME}.Android/files/Pictures/{DIRECTORYNAME}/{IMAGENAME}
however in my app it will get a list of file names from an API I want to check if the image exists in that folder.
The following works fine on IOS
var documentsDirectory = Environment.GetFolderPath(Environment.SpecialFolder.Personal);
string jpgFilename = System.IO.Path.Combine(documentsDirectory, App.IMAGE_FOLDER_NAME);
jpgFilename = System.IO.Path.Combine(jpgFilename, name);
I have tried the 2 following methods for getting it on android but both are incorrect
var documentsDirectory = System.Environment.GetFolderPath(System.Environment.SpecialFolder.Personal);
string jpgFilename = System.IO.Path.Combine(documentsDirectory, App.IMAGE_FOLDER_NAME);
jpgFilename = System.IO.Path.Combine(jpgFilename, name);
Java.IO.File dir = Android.OS.Environment.GetExternalStoragePublicDirectory(Android.OS.Environment.DataDirectory + "/" + App.IMAGE_FOLDER_NAME + "/" + name);
dir ends up as
/storage/emulated/0/data/{DIRECTORY}/{IMAGENAME}
jpgFileName ends up as /data/data/{APPNAME}.Android/files/{DIRECTORYNAME}/{IMAGENAME}
I dont want to hardcode anything in the paths but neither of these are right. I could not find anything in the GIT documentation for getting the file path except by looking at the path of the file created when taking a picture
The problem
I had the same kind of issue with Xamarin Media Plugin. For me, the problem is:
we don't really know where the plugin save the picture on android.
After reading all documentation I found, I just noted this:
... When your user takes a photo it will still store temporary data, but also if needed make a copy to the public gallery (based on platform). In the MediaFile you will now see a AlbumPath that you can query as well.
(from: Github plugin page)
so you can save your photo to your phone gallery (it will be public) but the path is known.
and we don't know what means "store the temporary data".
Solution
After investigating on how/where an app can store data, I found where the plugin stores photos on Android >> so I can generate back the full file names
In your Android app, the base path you are looking for is:
var basePath = Android.App.Application.Context.GetExternalFilesDir(null).AbsolutePath
It references your app's external private folder. It looks like that:
/storage/emulated/0/Android/data/com.mycompany.myapp/files
So finally to get your full file's path:
var fullPath = basePath + {DIRECTORYNAME} + {FILENAME};
I suggest you to make a dependency service, for instance 'ILocalFileService', that will expose this 'base path' property.
Please let me know if it works for you !
I resolved a similar problem. I wanted to collect all files for my app in a folder visible to all users.
var documentsDirectory = Android.OS.Environment.GetExternalStoragePublicDirectory(
Android.OS.Environment.DirectoryDocuments);
If you want to create Directory, add to your class using System.IO; and you have the same functions in a normal .NET application.
if (!Directory.Exists(path))
Directory.CreateDirectory(path);
If you want to create files or directory, you can use PCLStorage.
public async Task<bool> CreateFileAsync(string name, string context)
{
// get hold of the file system
IFolder folder = FileSystem.Current.LocalStorage;
// create a file, overwriting any existing file
IFile file = await folder.CreateFileAsync(name,
CreationCollisionOption.ReplaceExisting);
// populate the file with some text
await file.WriteAllTextAsync(context);
return true;
}
to get the private path
var privatePath = file.Path;
to get the public Album path
var publicAlbumPath = file.AlbumPath;
se the documentation here https://github.com/jamesmontemagno/MediaPlugin

C# Awesomium Web Control - automatically download and open files

I currently have an Awesomium Webvcontrol in my WinForms application. When I click a download link it prompts with a save file dialog.
I need it to download the file to a preset location and automatically open it. I am using the latest version of Awesomium.
References:
using Awesomium.Windows.Forms;
using Awesomium.Core;
Has anyone an idea how to make the control point to a preset location?
I managed to figure a way around this.
I added an method to the download event of webcore.
Awesomium.Core.WebCore.Download += onDownload;
The method looks like this.
public static void onDownload(Object sender, DownloadEventArgs e)
{
e.Handled = true;
using (WebClient Client = new WebClient())
{
FileInfo file = new FileInfo("Your Path");
//replace Your Path with the path you wish to save the file including filename and extension
Client.DownloadFile(e.Url.ToString(), file.FullName);
//System.Windows.Forms.MessageBox.Show("Downloaded!");
Process.Start(file.FullName);
}
}
This now downloads and opens the file. In my case it was a .exe application.

Can I reference local files in a webview when I use navigatetostring?

I'm using a webview to display certain data in my windows 8 app. I would like to user an include to a local js file as well as use locally stored images.
Is this possible?
I haven't had any luck by putting the local path where the files are located.
According to WebView documentation you can only reference other files using the ms-appx-web protocol, i.e. to load the files stored in Windows.ApplicationModel.Package.Current.InstalledLocation, meaning that they need to be distributed as content along with your application. The control doesn't support ms-appdata protocol for security reasons, i.e. you can't open files stored Windows.Storage.ApplicationData.Current.LocalFolder Windows.Storage.ApplicationData.Current.RemoteFolder or Windows.Storage.ApplicationData.Current.TempFolder where you'd need to put them if you were generating or downloading them at runtime.
In JavaScript apps WebView is a bit more flexible: it does support ms-appdata protocol as well, but only for media files such as images. It cannot open any potentially executable code, such as script or CSS.
If you want to open some local .html file or atc. you should download it in InstalledLocation folder. If you haven't option to create a new file you can just use file.CopyAsync(htmlFolder, fname + ".html");
For example I create some .html file:
StorageFolder htmlFolder = await Windows.ApplicationModel.Package.Current.InstalledLocation.CreateFolderAsync(#"HtmlFiles", CreationCollisionOption.GenerateUniqueName);
IStorageFile file = await htmlFolder .CreateFileAsync(fname + ".html", CreationCollisionOption.GenerateUniqueName);
and than I can easily open this .html file with or without FileOpenPicker:
var fop = new FileOpenPicker();
fop.FileTypeFilter.Add(".html");
var file = await fop.PickSingleFileAsync();
if (file != null)
{
string myPath = file.Path.Substring(file.Path.IndexOf("HtmlFiles"));
myWebview.Navigate(new Uri("ms-appx-web:///" + myPath));
}
And don't forget - just only from InstalledLocation you can open it with ms-appx-web:///
If the WebView is IE10 based, FIleReader may be what you are looking for. Here is a snippet of code that I use on an image ipload page to show images in a page when they are selected via a File Open dialog:
$('input:file').each(function(index, evt){
if(index===0)
{
var files = evt.files;
for(var i=0;i<files.length;i++)
{
if(files[i].name===filename)
{
var reader = new FileReader();
reader.onload=(function(theFile){
return function(e){
var line= uploader.fineUploader('getItemByFileId',id);
if(line!=undefined)
$(line).append('<img class="fileimage" id="fileImage-' + id + '" src="'+e.target.result+'" />');
};
})(files[i]);
reader.readAsDataURL(files[i]);
break;
}
}
}
I hope this points you in the right direction!

Use a ShellFile object using Windows API Code Pack for Microsoft

Windows API Code Pack for Microsoft can be downloaded from here. That is a really nice library and it has great examples. For example if I open the solution WindowsAPICodePack10 that comes in the zip from downloading the code pack (it only contains the libraries I added a win forms and wpf application)
then I am able to use the library very easily for example in my wpf application I can drag:
ExplorerBrowser user control (note I have to add references to the libraries that came with the solution)
and then with a button I can populate that control with this lines of code:
// Create a new CommonOpenFileDialog to allow users to select a folder/library
CommonOpenFileDialog cfd = new CommonOpenFileDialog();
// Set options to allow libraries and non filesystem items to be selected
cfd.IsFolderPicker = true;
cfd.AllowNonFileSystemItems = true;
// Show the dialog
CommonFileDialogResult result = cfd.ShowDialog();
// if the user didn't cancel
if (result == CommonFileDialogResult.Ok)
{
// Update the location on the ExplorerBrowser
ShellObject resultItem = cfd.FileAsShellObject;
explorerBrowser1.NavigationTarget = resultItem;
//explorerBrowser1.Navigate(resultItem);
}
and after that I am able to have something like:
That is amazing but I don't understand Microsoft. If they give you those libraries they should make it easy to customize that user control. the reason why I downloaded those libraries is because I need to place files from a specific directory on a stackpanel and be able to have the same functionality that you get with files on explorer (able to drag files, get context menu when right clicking file, dropping files to that container etc)
anyways I don't need all that functionality. from studing the library I think that user control contains a ShellContainer object and it's childern are ShellFiles maybe.
So from this library I will like to create a ShellFile object and place it in a StackPanel. after tedious studing of the library I finally found out how to instantiate an object from shellFile (ShellFile class is abstract) :
string filename = #"C:\Program Files (x86)\FileZilla FTP Client\filezilla.exe"; \\random file
ShellFile shellFile = ShellFile.FromFilePath(filename);
now it will be nice if I could place that file in a container. I am not able to instantiate a ShellConteiner object becaue it is abstract too. so how Will I bee able to place that shell file on a canvas for example?
or maybe I could extract the properties that I need and create a user control that will represent a shellFile. I know how to get the thumbnail I can do something like:
string filename = #"C:\Program Files (x86)\FileZilla FTP Client\filezilla.exe";
ShellFile shellFile = ShellFile.FromFilePath(filename);
System.Drawing.Bitmap btm = shellFile.Thumbnail.ExtraLargeBitmap;

Categories