Showing PDF-File with MVVM Cross on Android and IOS - c#

I want to open a PDF on the Phone via the File-Path but i cant figure out how i could do this properly without using 3rd party packages.
You have any suggestion for this?
I already tried to use this on Android:
public void OpenFile(string filePath)
{
var fileToOpen = new Java.IO.File(filePath);
var uri = FileProvider.GetUriForFile(Application.Context, Application.Context.PackageName + ".fileprovider", fileToOpen);
var intent = new Intent();
var mime = IOUtil.GetMimeType(uri.ToString());
intent.SetAction(Intent.ActionView);
intent.SetDataAndType(uri, mime);
intent.SetFlags(ActivityFlags.NewTask);
intent.AddFlags(ActivityFlags.GrantReadUriPermission);
Application.Context.StartActivity(intent);
}
But i get the following Error:
Unhandled Exception:
Java.Lang.NullPointerException: Attempt to invoke virtual method
'android.content.res.XmlResourceParser
android.content.pm.ProviderInfo.loadXmlMetaData(android.content.pm.PackageManager,
java.lang.String)' on a null object reference

first you should addd this code to your manifest file :
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="${applicationId}.easyphotopicker.fileprovider"
android:exported="false"
android:grantUriPermissions="true"
tools:replace="android:authorities">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="#xml/filepaths"
tools:replace="android:resource"/>
</provider>
and create filepaths :
<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<root-path name="root" path="" /> //root directory of the device new File("/");
<files-path name="files" path="" /> //context.getFilesDir()
<cache-path name="cache" path="" /> //context.getCacheDir()
<external-path name="external" path="" /> //Environment.getExternalStorageDirectory()
<external-files-path name="name" path="path" /> //context.getExternalFilesDirs()
<external-cache-path name="name" path="path" /> //getExternalCacheDirs()
</paths>

Your error is telling us that there is no file at the location matching that's passed into the function. There's a few ways of doing this, one of them is as shown. After accepting permissions to access folders and files, this should be one of the simplest ways. You seem to be close:
public void OpenPdfFile(string filename)
{
var f = new Java.IO.File(filename);
if (f.Exists())
{
System.Diagnostics.Debug.WriteLine("File exists!");
try
{
var openFileIntent = new Intent(Intent.ActionView);
openFileIntent.SetDataAndType(Android.Net.Uri.FromFile(f), "application/pdf");
openFileIntent.SetFlags(ActivityFlags.NoHistory);
StartActivity(Intent.CreateChooser(openFileIntent, "Open pdf file"));
}
catch (ActivityNotFoundException)
{
//handle when no available apps
}
}
}
I haven't tested your work, but the first thing would be to see if you added this to the Manifest file
android:authorities="com.{package}.{name}.fileprovider"
since your code says Application.Context.PackageName + ".fileprovider"

Related

UWP application unable to access USB drive even though permissions are set

I am working on an app that is responsible for formatting a USB drive and prepare it for further use on an embedded system.
I am formatting the drive using the following method that I found on stack overflow (unfortunately I did not save the link. I'll post it there if I find it again)
public static bool FormatUSB(string driveLetter, string fileSystem = "FAT32", bool quickFormat = true,
int clusterSize = 4096, string label = "USB_0000", bool enableCompression = false)
{
//add logic to format Usb drive
//verify conditions for the letter format: driveLetter[0] must be letter. driveLetter[1] must be ":" and all the characters mustn't be more than 2
if (driveLetter.Length != 2 || driveLetter[1] != ':' || !char.IsLetter(driveLetter[0]))
return false;
//query and format given drive
//best option is to use ManagementObjectSearcher
var files = Directory.GetFiles(driveLetter);
var directories = Directory.GetDirectories(driveLetter);
foreach (var item in files)
{
try
{
File.Delete(item);
}
catch (UnauthorizedAccessException) { }
catch (IOException) { }
}
foreach (var item in directories)
{
try
{
Directory.Delete(item);
}
catch (UnauthorizedAccessException) { }
catch (IOException) { }
}
ManagementObjectSearcher searcher = new ManagementObjectSearcher(#"select * from Win32_Volume WHERE DriveLetter = '" + driveLetter + "'");
foreach (ManagementObject vi in searcher.Get())
{
try
{
var completed = false;
var watcher = new ManagementOperationObserver();
watcher.Completed += (sender, args) =>
{
Console.WriteLine("USB format completed " + args.Status);
completed = true;
};
watcher.Progress += (sender, args) =>
{
Console.WriteLine("USB format in progress " + args.Current);
};
vi.InvokeMethod(watcher, "Format", new object[] { fileSystem, quickFormat, clusterSize, label, enableCompression });
while (!completed) { System.Threading.Thread.Sleep(1000); }
}
catch
{
}
}
return true;
}
I also added all the capabilities that should be required (I think) in order to access a removable drive in my manifest:
<?xml version="1.0" encoding="utf-8"?>
<Package
xmlns="http://schemas.microsoft.com/appx/manifest/foundation/windows10"
xmlns:mp="http://schemas.microsoft.com/appx/2014/phone/manifest"
xmlns:uap="http://schemas.microsoft.com/appx/manifest/uap/windows10"
xmlns:rescap="http://schemas.microsoft.com/appx/manifest/foundation/windows10/restrictedcapabilities"
xmlns:iot="http://schemas.microsoft.com/appx/manifest/iot/windows10"
IgnorableNamespaces="uap mp rescap iot">
<Identity
Name="7b9becad-6afd-4872-bcb7-7f414c098edf"
Publisher="CN=vitto"
Version="1.0.0.0" />
<mp:PhoneIdentity PhoneProductId="7b9becad-6afd-4872-bcb7-7f414c098edf" PhonePublisherId="00000000-0000-0000-0000-000000000000"/>
<Properties>
<DisplayName>DiskMakerApp</DisplayName>
<PublisherDisplayName>vitto</PublisherDisplayName>
<Logo>Assets\StoreLogo.png</Logo>
</Properties>
<Dependencies>
<TargetDeviceFamily Name="Windows.Universal" MinVersion="10.0.0.0" MaxVersionTested="10.0.0.0" />
</Dependencies>
<Resources>
<Resource Language="x-generate"/>
</Resources>
<Applications>
<Application Id="App"
Executable="$targetnametoken$.exe"
EntryPoint="DiskMakerApp.App">
<uap:VisualElements
DisplayName="DiskMakerApp"
Square150x150Logo="Assets\Square150x150Logo.png"
Square44x44Logo="Assets\Square44x44Logo.png"
Description="DiskMakerApp"
BackgroundColor="transparent">
<uap:DefaultTile Wide310x150Logo="Assets\Wide310x150Logo.png"/>
<uap:SplashScreen Image="Assets\SplashScreen.png" />
</uap:VisualElements>
</Application>
</Applications>
<Capabilities>
<rescap:Capability Name="broadFileSystemAccess" />
<rescap:Capability Name="appCaptureSettings" />
<Capability Name="internetClient" />
<uap:Capability Name="removableStorage" />
<iot:Capability Name="systemManagement"/>
<DeviceCapability Name="usb"/>
</Capabilities>
</Package>
And also allowed access to the file system in Window's settings page:
But I am still getting:
I am wondering if I am missing anything. Is there a way I could run the app as an administrator^ Would that solve the issue? (In any case, only admins would be able to run that app in a real life scenario)
UWP application unable to access USB drive even though permissions are set
Directory.GetFiles can't not use to access file with path in UWP platform. And you can only use Windows Storage API to access file with path (enable broadFileSystemAccess ), by the way, System.Management Namespace is not work for UWP platform, and if you want to format USB device within UWP app, please use desktop extension to process. for more please refer stefan' blog UWP with Desktop Extension

FileProvider.GetUriForFile crashes with null reference exception in Xamarin-Android

I have a Xamarin application in which I want to open files attached to a data row with the default application for that type (doc, pdf, txt, jpg, etc.) So when the user taps on an ListItem containing the names of the attached files I want to open that file.
I am testing the Android part of it and when it gets to
FileProvider.GetUriForFile(blablabla see below
it crashes with:
Attempt to invoke virtual method 'android.content.res.XmlResourceParser android.content.pm.ProviderInfo.loadXmlMetaData(android.content.pm.PackageManager, java.lang.String)' on a null object reference
I get the attached files from the application server and write it to
Environment.SpecialFolder.Personal
I have checked if it is really written there and it is there.
The code is from here: https://forums.xamarin.com/discussion/124361/unable-to-open-pdf-into-the-third-party-app
and it is:
public void OpenFileByName(string filenameWithPath)
{
try
{
string application = "";
string filename = Path.GetFileName(filenameWithPath);
string extension = Path.GetExtension(filename);
// get mimeType
if (extension != null)
switch (extension.ToLower())
{
case ".txt":
application = "text/plain";
break;
case ".doc":
case ".docx":
application = "application/msword";
break;
case ".pdf":
application = "application/pdf";
break;
case ".xls":
case ".xlsx":
application = "application/vnd.ms-excel";
break;
case ".jpg":
case ".jpeg":
case ".png":
application = "image/jpeg";
break;
default:
application = "*/*";
break;
}
Java.IO.File file = new Java.IO.File(filenameWithPath);
file.SetReadable(true);
Android.Net.Uri uri = Android.Support.V4.Content.FileProvider.GetUriForFile(Android.App.Application.Context, "com.example.asd.fileprovider", file);
Intent intent = new Intent(Intent.ActionView);
intent.SetDataAndType(uri, application);
intent.AddFlags(ActivityFlags.GrantReadUriPermission);
intent.AddFlags(ActivityFlags.NoHistory);
intent.AddFlags(ActivityFlags.ClearWhenTaskReset | ActivityFlags.NewTask);
try
{
Android.App.Application.Context.StartActivity(intent);
}
catch (Exception)
{
Toast.MakeText(Android.App.Application.Context, "No Application Available to View this file.", ToastLength.Short).Show();
}
}
catch (Exception ex)
{
Toast.MakeText(Android.App.Application.Context, ex.Message, ToastLength.Short).Show();
}
}
AndroidManifest.xml:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
android:versionCode="1"
android:versionName="1.0"
package="com.example.asd"
android:installLocation="auto">
<provider
android:name="android.support.v4.content.fileProvider"
android:authorities="com.example.asd.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="#xml/filepaths" />
</provider>
<uses-sdk android:minSdkVersion="21" android:targetSdkVersion="27" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
</manifest>
Android/Resource/xml/filepaths.xml:
<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<external-path name="external_files" path="."/>
<files-path name="media"/>
<files-path name="images"/>
<files-path name="docs"/>
<files-path name="download"/>
</paths>
Android/Resources/drawable/file_provider_path.xml:
<?xml version="1.0" encoding="utf-8" ?>
<paths>
<external-path name="download" path="download/"/>
</paths>
What is wrong here?
To fix the error, put the <provider> element in the <application> element in the app manifest as shown here.
I'm not sure what file_provider_path.xml is for but it should not be in the drawable directory as it's not an Android drawable.

C# App.Config File Path combine

I'm trying without success to use app.config keys with file path based.
This is my App.Config:
<appSettings>
<add key="States" value="NY,CA,MA" />
<add key="SourceFile_NY" value="C:\NY\file.xlsx" />
<add key="SourceFile_CA" value="C:\CA\file.xlsx" />
<add key="SourceFile_MA" value="C:\MA\file.xlsx" />
Now, on the main class, i'd like to obtain the file for each state given:
static void Main(string[] args)
{
var states = "NY,CA,MA".Split(',');
foreach (var state in states)
{
new SomeMethodFromOtherClass().class(SourceFile_ + XX);
}
}
I'm trying to get the file path based on the state on the SourceFile_ + XX part, but i can't find any good way to accomplish the call to the app.config value for each state
Make sure you add a reference in your project to System.Configuration.
Then you just do:
var ny = ConfigurationManager.AppSettings[sourcefile + "_NY"];

How to add content file to Wix installer

<?xml version="1.0" encoding="UTF-8"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
<Product Id="*" Name="MyApp" Language="1033" Version="1.0.0.0" Manufacturer="MyAppDev" UpgradeCode="067ac37f-0d36-4173-a24a-5037927bd6da">
<Package InstallerVersion="200" Compressed="yes" InstallScope="perMachine" />
<MajorUpgrade DowngradeErrorMessage="A newer version of [ProductName] is already installed." />
<MediaTemplate />
<Feature Id="ProductFeature" Title="MyApp" Level="1">
<ComponentGroupRef Id="ProductComponents" />
</Feature>
</Product>
<Fragment>
<Directory Id="TARGETDIR" Name="SourceDir">
<Directory Id="ProgramFilesFolder">
<Directory Id="INSTALLFOLDER" Name="MyApp" />
</Directory>
</Directory>
</Fragment>
<Fragment>
<ComponentGroup Id="ProductComponents" Directory="INSTALLFOLDER">
<!-- TODO: Remove the comments around this Component element and the ComponentRef below in order to add resources to this installer. -->
<!-- <Component Id="ProductComponent"> -->
<!-- TODO: Insert files, registry keys, and other resources here. -->
<!-- </Component> -->
<Component>
<File Source="$(var.MyApp.TargetPath)" />
</Component>
<Component>
<File Id="Postcodes.txt" Source="C:\Project\MyApp\MyApp\Files\Postcodes.txt" KeyPath="yes" />
</Component>
</ComponentGroup>
<Feature Id="MainApplication" Title="Main Application" Level="1">
<ComponentRef Id="Postcodes.txt" />
</Feature>
</Fragment>
</Wix>
My Windows form application using a text file which is stored in application directory. And with the help of WIX toolset. I have created an installer But the problem is content text file not exist. That's why i am getting File not found exception.
Please help me, How can i add this content text file to WIX installer? Or what would i need to add "Product.wxs" file?
You need to add a custom action. This is a direct copy of my work minus the company name that it was produced for.
<CustomAction Id="RestAction" BinaryKey="myCompanyAgentSetup.WixExtension.Package.dll" DllEntry="Execute" Execute="immediate" Return="check" />
This is a class file \ dll that is created in the Wix project. This is my actual dll \ class file
using System;
using System.Collections.Generic;
using System.Data;
using System.IO;
using System.Linq;
using System.Net;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using Microsoft.Deployment.WindowsInstaller;
namespace myCompanyAgentSetup.WixExtension
{
public static class myCompanyAgentSetupWixExtension
{
[CustomAction]
public static ActionResult Execute(Session session)
{
var errorMsg = string.Empty;
var record = new Record();
var token = Environment.GetEnvironmentVariable("RX_JOB_NO");
var restUser = session["RESTUSER"];
var restPass = session["RESTPASS"];
var restUrl = string.Format(session["RESTURL"], token);
var request = (HttpWebRequest)WebRequest.Create(restUrl);
var encoded = Convert.ToBase64String(Encoding.Default.GetBytes(restUser + ":" + restPass));
request.Headers.Add(HttpRequestHeader.Authorization, "Basic " + encoded);
request.Credentials = new NetworkCredential(restUser, restPass);
Console.WriteLine("attempting to get API Key");
try
{
var response = (HttpWebResponse)request.GetResponse();
if (response.StatusCode.ToString() != "OK")
{
record = new Record
{
FormatString = string.Format(response.StatusDescription)
};
session.Message(InstallMessage.Error, record);
Console.WriteLine("Unable to get API Key");
Console.WriteLine("Adding RX_CLOUDBACKUP_API Environment Variable with no value");
UpdateConfigFiles("");
}
else
{
var apiKey = new StreamReader(response.GetResponseStream()).ReadToEnd();
if (apiKey.Contains("Error"))
{
record = new Record
{
FormatString = string.Format(apiKey)
};
session.Message(InstallMessage.Error, record);
session.Message(InstallMessage.Terminate, record);
}
Console.WriteLine("Adding RX_CLOUDBACKUP_API with value - " + apiKey);
UpdateConfigFiles(apiKey);
return ActionResult.Success;
}
}
catch (Exception e)
{
record = new Record
{
FormatString = string.Format(e.Message)
};
session.Message(InstallMessage.Error, record);
session.Message(InstallMessage.Terminate, record);
}
//An error has occurred, set the exception property and return failure.
session.Log(errorMsg);
session["CA_ERRORMESSAGE"] = errorMsg;
record = new Record
{
FormatString = string.Format("Something has gone wrong!")
};
session.Message(InstallMessage.Error, record);
session.Message(InstallMessage.Terminate, record);
return ActionResult.Failure;
}
private static void UpdateConfigFiles(string apiKey)
{
if (!string.IsNullOrEmpty(apiKey))
{
Environment.SetEnvironmentVariable("RX_CLOUDBACKUP_API", null, EnvironmentVariableTarget.Machine);
Environment.SetEnvironmentVariable("RX_CLOUDBACKUP_API", apiKey, EnvironmentVariableTarget.Machine);
}
else
{
Environment.SetEnvironmentVariable("RX_CLOUDBACKUP_API", "", EnvironmentVariableTarget.Machine);
}
}
}
}
Hopefully this will get you going. If you need anything else let me know

ActivityLibrary for Sharepoint Designer

I'm trying to implement an MSDN tutorial to create an activity library for Sharepoint Designer, and it gives me this error:
Namespace1.CreateTaskInListOnSite' does not contain a definition for 'ListName' and no extension method 'ListName' accepting a ...
and the same for AssignedTo and 'SiteUrl`.
Shouldn't 'AssignedTo', 'ListName' and 'SiteUrl' be inherited members from SequenceActivity class?
Here is the code which is erred:
protected override ActivityExecutionStatus
Execute(ActivityExecutionContext executionContext)
{
try
{
SPSite sitecollection = new SPSite(this.SiteUrl); //here
SPWeb web = sitecollection.OpenWeb();
SPUser user = web.Users[this.AssignTo[0].ToString()]; // here
SPList list = web.Lists[this.ListName]; // and here
SPListItem item = list.Items.Add();
item["Title"] = this.TaskTitle;
item["AssignedTo"] = user;
item.Update();
}
catch (Exception ex)
{
EventLog.WriteEntry("MSDN Workflow", ex.ToString());
}
return ActivityExecutionStatus.Closed;
}
Here is the .actions file:
<?xml version="1.0" encoding="utf-8" ?>
<WorkflowInfo>
<Actions Sequential="then" Parallel="and">
<Action Name="Create Task in List On Site"
ClassName="Namespace1.CreateTaskInListOnSite"
Assembly="CustomWorkflowActivities, Version=1.0.0.0, Culture=neutral, PublicKeyToken=a3170baa21b0a3e9"
AppliesTo="all" Category="Cross Site Actions">
<RuleDesigner
Sentence="Create task titled %1 for %2 on site %3 in
list %4">
<FieldBind Field="TaskTitle" DesignerType="TextArea"
Id="1"/>
<FieldBind Field="AssignTo" DesignerType="Person"
Text="this user" Id="2"/>
<FieldBind Field="SiteUrl" DesignerType="TextArea" Id="3"/>
<FieldBind Field="ListName" DesignerType="TextArea" Id="4"/>
</RuleDesigner>
<Parameters>
<Parameter Name="TaskTitle" Type="System.String, mscorlib"
Direction="In" />
<Parameter Name="AssignTo" Type="System.String, mscorlib"
Direction="In" />
<Parameter Name="SiteUrl" Type="System.String, mscorlib"
Direction="In" />
<Parameter Name="ListName" Type="System.String, mscorlib"
Direction="In" />
</Parameters>
</Action>
The .ACTIONS file calls the ClassName as MSDN.HowTo.CreateTaskInListOnSite, where as in your code file you seem to use the namespace Namespace1. The two have to match.
So either you change .ACTIONS to this:
<Action Name="Create Task in List On Site"
ClassName="MSDN.HowTo.CreateTaskInListOnSite" .... />
or change the namespace definition in your code file to MSDN.HowTo.
The assembly attribute values also have to be correct. You can use the sn.exe tool to retrieve the public key token for your assembly.

Categories

Resources