I have a question regarding accessing data from another application.
I need access to files that are inside another, already installed, application on iPhone so they can be uploaded via REST POST method.
When we tried testing this with simple picker [FileData fileData = await CrossFilePicker.Current.PickFile();] and navigating to the other application we got something along the lines of 'access denied' message.
We've done this on Android while using Android.App.Application.Context(StartActivity and StartActivityForResault) and Intents (ActionGetContent).
I've read a few articles on Accessing data from another application but I did not fully understand the concept of 'application sandbox'.
Is it even possible? If so, can you give me an example or link to follow so I can see how it's done?
You can have a try with Xamarin.Essentials: Share to share data between apps.
Files:
var fn = "Attachment.txt";
var file = Path.Combine(FileSystem.CacheDirectory, fn);
File.WriteAllText(file, "Hello World");
await Share.RequestAsync(new ShareFileRequest
{
Title = Title,
File = new ShareFile(file)
});
Related
I received a link to shared folder from e-commerce company. The link is public and not shared with my dropbox account directly.
How do I get an url to the image that I can pass to either DownloadAsync method of the same sdk or simply HttpClient and well ... download it?
Ideally it would be the same link I get when I click on the image when viewing that shared folder in a browser.
https://www.dropbox.com/sh/{folder_hash}/{file_hash_maybe}/{filename}?dl=0
This is what I have tried:
using Dropbox.Api;
using Dropbox.Api.Files;
...
var accessToken = "abracadabra";
var sharedFolderUrl = "https://www.dropbox.com/sh/{folder_hash}?dl=0";
using (var dbx = new DropboxClient(accessToken))
{
var sharedLink = new SharedLink(sharedFolderUrl);
var sharedFiles = await dbx.Files.ListFolderAsync(path: "", sharedLink: sharedLink);
// sharedFiles - has over 13,000 entries, I use cursor to get them all.
foreach (var file in sharedFiles.Entries)
{
if (file.IsFile)
{
// tried this, but:
// 1. it's crazy to loop through all
// 2. link.Response.Url gives me the same url to a shared folder for all items
var link = await dbx.Sharing.GetSharedLinkFileAsync(url: sharedFolderUrl, path: "/" + file.Name);
}
}
}
Using the GetSharedLinkFileAsync method is the right way to programmatically download a file from a shared link. It actually gives both the metadata (in the link.Response in your code like you mentioned), as well as the file data directly (not via a URL).
To access the file data, you can use any of the GetContentAs... methods documented under IDownloadResponse as returned by GetSharedLinkFileAsync. In your code, that would look something like: link.GetContentAsStreamAsync() (or whichever one you want).
Alternatively, if you want to download the entire folder from the shared link, you can use the URL parameters documented in this help center article. (That may fail for very large folders though.)
I'm creating a Xamarin.Forms app and I want to have an option to create a Desktop shortcut (I already implemented on Android).
I have a protocol that I can use to launch my app, so this is not a concern, and I already saved another type of file in the Desktop to test, my only problem is that I can't create a .lnk file.
In my researches I saw a lot of explanations as to how to create a .lnk file programmatically, but all tutorials were related to WindowsForms or PowerShell scripts (as far as I know, UWP doesn't allow executing PS scripts).
So, is there a way of creating Desktop Shortcuts?
Edit after answer
When I create the shortcut using the Dialog, everything is fine, but when I create it in the app, the shortcut's icon doesn't change to my app's icon, it displays a confirmation when I run it and after I confirm, it displays an error message saying The target "" of this Internet Shortcut is not valid.
But if I change the file in any way (rename it, copy and paste it or change it's contents), it changes the icon to the correct one and when I confirm the dialog, it opens my app as expected. This proves that nothing is wrong with the file or its contents, but I don't know how to workaround this.
Edit
StorageFile shortcutFile = null;
try
{
shortcutFile = await ApplicationData.Current.LocalFolder.GetFileAsync("shortcut.url");
}
catch (Exception) { }
if (shortcutFile == null)
{
shortcutFile = await ApplicationData.Current.LocalFolder.CreateFileAsync("shortcut.tmp", CreationCollisionOption.ReplaceExisting);
await FileIO.WriteLinesAsync(shortcutFile, new string[] { "[InternetShortcut]", "URL=yourapp:///" });
string newShortcutName = shortcutFile.Path.Substring(0, shortcutFile.Path.Length - 4) + ".url";
File.Move(shortcutFile.Path, newShortcutName);
shortcutFile = await ApplicationData.Current.LocalFolder.GetFileAsync(newShortcutName.SplitWithoutEmpty('\\').Last());
}
FolderPicker savePicker = new FolderPicker
{
SuggestedStartLocation = PickerLocationId.Desktop
};
savePicker.FileTypeFilter.Add(".url");
StorageFolder folder = await savePicker.PickSingleFolderAsync();
if (folder != null)
{
// Works fine until here, then it throws an exception (Permission Denied)
await shortcutFile.CopyAsync(folder, shortcutFile.Name);
}
The Windows 10 alternative to desktop shortcut is a Live Tile, which is basically a live version of an Android icon. You can create a desktop shortuct by dragging the Live Tile to the desktop manually.
If you want a programmatic way and have already a registered URI protocol, you should be able to create a url shortcut instead of classic one. Let's see this step by step:
You can create a URL shortuct manually using the dialog:
Now this will create a .url file on your desktop, which behaves as a shortcut. You can see it in the listing of dir command:
Now try to rename the file to have a different extension using the ren command, for example:
ren YourApp.url YourApp.urltext
You can now open this file normally in a text editor to see its contents:
[{000352A0-0000-0000-C000-000000000012}]
Prop3=19,0
[InternetShortcut]
IDList=
URL=yourapp:///
From my testing it seems that the first two rows are unnecessary and the IDList property is also redundant. This means you can just do with the following:
[InternetShortcut]
URL=yourapp:///
If you rename the file back to have .url extension and double-click it, it will launch the app associated with the yourapp protocol, which is exactly what you need.
So to do this programmatically, you have to save a .url file with the contents we just came up with and save it where the user wants to have the shortcut created.
There is one disadvantage, that you have to keep in mind - UWP apps are sandboxed, so you cannot directly create files on desktop without the user's consent. The best approach I can see now is to use a FileSavePicker to let the user choose a folder where she wants to save the shortcut. It is still quite user friendly and it even gives her the flexibility to have the shortcut elsewhere than on the desktop.
Update
I have found this SO answer with an extremely detailed analysis of this problem. It thoroughly explains why we are hitting these problems - the URL files are interpreted by Internet Explorer and once interpreted, the URL info is stored as NTFS metadata. I would suggest creating the file with a temporary extension first and only then rename it to .url. I am not at my computer right now, but I will try when I get there :-)
Update 2
After some playing I have come up with the following and it "partially works on my PC". The shortcut is successfully created and even has the right icon, BUT each time it is opened it shows the "file from different computer" confirmation dialog, which can be turned off only through Properties.
FileSavePicker savePicker = new FileSavePicker
{
SuggestedStartLocation = PickerLocationId.Desktop
};
savePicker.FileTypeChoices.Add("Shortcut", new [] { ".url" });
var saveFile = await savePicker.PickSaveFileAsync();
if (saveFile != null)
{
await FileIO.WriteLinesAsync(
saveFile,
new string[] { "[InternetShortcut]", "URL=yourapp:///" });
}
I have found a much simpler way.
You just need to create a normal shortcut with the target
C:\Windows\explorer.exe shell:AppsFolder{PackageFamilyName}!App
I use the following code to generate the shortcut the first time the application runs (You'll need to add a COM reference to Windows Scripting Host):
string shortcutLocation = Path.Combine(Environment.SpecialFolder.Desktop, shortcutName + ".lnk");
WshShell shell = new();
IWshShortcut shortcut = (IWshShortcut)shell.CreateShortcut(shortcutLocation);
shortcut.IconLocation = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "icon.ico");
shortcut.Arguments = $"shell:AppsFolder\\{insert your PackageFamilyName}!App";
shortcut.TargetPath = Path.Combine(Environment.GetEnvironmentVariable("windir"), "explorer.exe");
shortcut.Save();
PackageFamilyName:
Your app family name should be a giant alphanumeric hash, the easiest way to get that is to install it somewhere and use the powershell command Get-AppxPackage https://learn.microsoft.com/en-us/powershell/module/appx/get-appxpackage?view=windowsserver2019-ps. It can also be obtained in code with Package.Current.Id.FamilyName.
Icon location: This is not necessary it should use the default icon for your app. I changed this because it used a version with an ugly blue background. Adding an icon to your build output folder and linking it as above lets you choose the icon.
It should also be noted that the mouseover text for the shortcut is windows explorer. Please let me know if anyone finds a way around this.
Another option might be to add an ExecutionAlias and create the shortcut directly to the alias.
In Package.appxmanifest
xmlns:uap5="http://schemas.microsoft.com/appx/manifest/uap/windows10/5"
<Extensions>
<uap5:Extension Category="windows.appExecutionAlias" EntryPoint="Windows.FullTrustApplication" Executable="MyApp\MyApp.exe">
<uap5:AppExecutionAlias>
<uap5:ExecutionAlias Alias="MyApp.exe" />
</uap5:AppExecutionAlias>
</uap5:Extension>
</Extensions>
Then make the shortcut for MyApp.exe
Directory:
App1
- MainPage.xaml.cs
- Sample.xaml
im trying to do is getting the xaml content from the sample as a string but it doesnt work since it cant find the file:
var x = Path.GetFullPath(#"sample.xaml");
FileStream s = new FileStream(x, FileMode.Open);
How can I fix this?
I haven't tried this give it a try.
Save your file in a location eg (Assets Folder)Now, make sure that the
build action is set to Content.
var storageFile = await StorageFile.GetFileFromApplicationUriAsync(
new Uri("ms-appx:///Assets/youfile.xaml"));
The StorageFile you get is of course read-only, but it can be passed to any API that expects a StorageFile.
If you want to read you can try.
var result = storageFile.OpenReadAsync()
StorageFile Documentation
The source files (.xaml, .cs) are compiled, and in the deployed app they do not exist as physical files, so you can't open them this way.
I have used PCL storage package to create a folder for my application. I referred to this. Here is my code sample:
public ListPage()
{
testFile();
Content = new StackLayout
{
Children = {
new Label { Text = "Hello ContentPage" }
}
};
}
async public void testFile()
{
// get hold of the file system
IFolder rootFolder = FileSystem.Current.LocalStorage;
// create a folder, if one does not exist already
IFolder folder = await rootFolder.CreateFolderAsync("MySubFolder", CreationCollisionOption.OpenIfExists);
// create a file, overwriting any existing file
IFile file = await folder.CreateFileAsync("MyFile.txt", CreationCollisionOption.ReplaceExisting);
// populate the file with some text
await file.WriteAllTextAsync("Sample Text...");
}
The folder for files is getting created under sdcard/android/data/ directory but it does not create "MySubFolder" folder under files.
I have set WRITE_EXTERNAL_STORAGE and READ_EXTERNAL_STORAGE for my android project. Am I missing any other configurations?
Having run into similar issues (though on iOS), I now have this working, maybe it helps you. The issues are properly dealing with the async calls and other threading fun.
First, my use case is that I bundle a number of file resources with the app, provided for the user at first run, but from then on updated online. Therefore, I take the bundles resources and copy them into the filesystem proper:
var root = FileSystem.Current.LocalStorage;
// already run at least once, don't overwrite what's there
if (root.CheckExistsAsync(TestFolder).Result == ExistenceCheckResult.FolderExists)
{
_testFolderPath = root.GetFolderAsync(TestFolder).Result;
return;
}
_testFolderPath = await root.CreateFolderAsync(TestFolder, CreationCollisionOption.FailIfExists).ConfigureAwait(false);
foreach (var resource in ResourceList)
{
var resourceContent = ResourceLoader.GetEmbeddedResourceString(_assembly, resource);
var outfile = await _testFolderPath.CreateFileAsync(ResourceToFile(resource), CreationCollisionOption.OpenIfExists);
await outfile.WriteAllTextAsync(resourceContent);
}
Notice the .ConfigureAwait(false). I learned this from the excellent
MSDN Best Practises article on async/await.
Before, I was going back and forth between the method NOT creating directories or files - as in your question - or the thread hanging. The article talks about the latter in detail.
The ResourceLoader class is from here:
Embedded Resource
The ResourceToFile() method is just a helper that turns the long resource names in iOS to shorted file names, as I prefer those. It's not germaine here (IOW: it's a kludge I'm ashamed to show ;)
I think I understand threading better day by day, and if I understand correctly, the art here is to ensure you wait for the async method that load and write files to finish, but make sure you do that on a thread pool that will not deadlock with the main UI thread.
Is there any other way of checking whether a file exists in a Windows Store app?
try
{
var file = await ApplicationData.Current.LocalFolder.GetFileAsync("Test.xml");
//no exception means file exists
}
catch (FileNotFoundException ex)
{
//find out through exception
}
According to the accepted answer in this post, there is no other way at the moment. However, the File IO team is considering changing the the api so that it returns null instead of throwing an exception.
Quote from the linked post:
Currently the only way to check if a file exists is to catch the
FileNotFoundException. As has been pointed out having an explicit
check and the opening is a race condition and as such I don't expect
there to be any file exists API's added. I believe the File IO team
(I'm not on that team so I don't know for sure but this is what I've
heard) is considering having this API return null instead of throwing
if the file doesn't exist.
This may be old, but it looks like they've changed how they want you to approach this.
You're supposed to attempt to make the file, then back down if the file already exists. Here is the documentation on it. I'm updating this because this was the first result on my Google search for this problem.
So, in my case I want to open a file, or create it if it doesn't exist. What I do is create a file, and open it if it already exists. Like so:
save = await dir.CreateFileAsync(myFile, CreationCollisionOption.OpenIfExists);
I stumbled on to this blog post by Shashank Yerramilli which provides a much better answer.
I have tested this for windows phone 8 and it works. Haven't tested it on windows store though
I am copying the answer here
For windows RT app:
public async Task<bool> isFilePresent(string fileName)
{
var item = await ApplicationData.Current.LocalFolder.TryGetItemAsync(fileName);
return item != null;
}
For Windows Phone 8
public bool IsFilePresent(string fileName)
{
return System.IO.File.Exists(string.Format(#"{0}\{1}", ApplicationData.Current.LocalFolder.Path, fileName);
}
Check if a file exists in Windows Phone 8 and WinRT without exception
You can use the old Win32 call like this to test if directory exist or not:
GetFileAttributesExW(path, GetFileExInfoStandard, &info);
return (info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) ? false: true;
It works in Desktop and Metro apps:
http://msdn.microsoft.com/en-us/library/windows/desktop/aa364946%28v=vs.85%29.aspx
Microsoft has added a new function to StorageFile in Windows 8.1 to allow user engineers to determine if a file can be accessed: IsAvailable
The other means to check is by getting files in local folder
var collection = ApplicationData.Current.LocalFolder.GetFilesAsync()
Using this method and then iterating over all the elements in the collection and check for its availability.
I tried to write my own using old tricks:
GetFileAttributesEx() always seems to end up with ERROR_ACCESS_DENIED if file selected via FileOpenPicker;
Ditto for FindFirstFileEx();
_stat() always ends up with ENOENT when file selected via FileOpenPicker;
CreateFile2() with CREATE_NEW option works -- if file does exist it will fail with INVALID_HANDLE_VALUE return value and ERROR_FILE_EXISTS last error; if file does not exist you have to remember to delete created file afterwards.
All in all -- you're better of sticking with exception handling method.
8.1 got something like this, I tried it worked.
var folder = ApplicationData.Current.LocalFolder;
var file = await folder.TryGetItemAsync("mytext.txt") as IStorageFile;
if (file == null)
{
//do what you want
}
else
{
//do what you want
}
http://marcominerva.wordpress.com/2013/11/19/how-to-check-if-a-file-exists-in-a-windows-8-1-store-apps-no-more-exception-handling/
Dim myPath As StorageFolder
If (From i In Await KnownFolders.MusicLibrary.GetFoldersAsync() Where i.Name = "PodBong").Count = 1 Then
myPath = Await KnownFolders.MusicLibrary.GetFolderAsync("PodBong")
Else
myPath = Await KnownFolders.MusicLibrary.CreateFolderAsync("PodBong")
End If
The documentation for TryGetItemAsync says, "This example shows how to checkfor the existence of a file." It seems that this API is officially intended to serve that purpose.