I have code that was using the windows FolderPicker. After updating to Windows version 10.0.18362, my use of the FoldePicker has stopped working.
I have attached some code that I used in order to get the access to file is denied result.
using System;
using System.IO;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.Storage.Pickers;
using Windows.Storage;
using Windows.Storage.AccessCache;
namespace FolderPickerTest
{
public sealed partial class MainPage : Page
{
private static string path = #"filepath";
string[] lines;
public MainPage()
{
this.InitializeComponent();
test();
}
public async void test()
{
var folderPicker = new FolderPicker();
folderPicker.SuggestedStartLocation = PickerLocationId.Desktop;
folderPicker.FileTypeFilter.Add("*");
StorageFolder folder = await folderPicker.PickSingleFolderAsync();
if (folder != null)
{
StorageApplicationPermissions.FutureAccessList.AddOrReplace("PickedFolderToken", folder);
}
}
private void open_click(object sender, RoutedEventArgs e)
{
lines = new string[2];
try
{
lines = File.ReadAllLines(path);
}
catch(Exception ex)
{
errorText.Text = ($"Error: {ex.Message.ToString()}");
}
}
}
}
The error message that I am currently getting is:
Access to the path 'filepath' is denied
The problem here is that you are trying to read an arbitrary path using System.IO APIs. In one of the releases this was actually working when you declared the broadFileSystemAccess capability, but this is no longer the case. You now must use the StorageFile APIs to achieve your goal.
If you pick a folder with FolderPicker, you get a StorageFolder instance back. You can call GetFileAsync method on this instance to get a file by name. This is an instance of StorageFile which you can read using FileIO.ReadLinesAsync method.
To explain better the answer of Martin Zikmund, in Win 10 1803 (april 2018), broadFileSystemAccess capability was set automatically to ON in the user Settings.
Starting from Win 10 1809 (October 2018), this System setting is set to OFF by default.
You need to ask the user to explicitly set the setting ON in the Settings app, even by referencing the specific setting page directly.
Related
using Xamarin.Essentials;
namespace myNamespace
{
class myClass
{
public async void OpenFile(string path)
{
await Launcher.OpenAsync(new OpenFileRequest("File", new ReadOnlyFile(path)));
}
}
}
It works well if path is a real file system path, and doesn't work if it is a URI, giving an error like:
System.IO.DirectoryNotFoundException: 'Could not find a part of the path "content://com.android.providers.downloads.documents/something".'
(I'm getting path using Xamarin.Plugin.FilePicker, so it should be a real file, but sometimes the FilePicker returns URI instead of a path. For example, if I pick an image from camera it returns a path, and if I pick a file from Downloads, it returns a URI).
I found the solution myself. File can be launched using ActionView intent if the 'path ' is a content URI.
namespace myNamespace
{
class myClass
{
public void OpenFile(string path)
{
var uri = Android.Net.Uri.Parse(path);
var intent = new Intent(Intent.ActionView, uri);
intent.AddFlags(ActivityFlags.GrantReadUriPermission);
if (intent.ResolveActivity(Activity.PackageManager) != null)
StartActivity(intent);
}
}
}
If it is a path, your app must be a FileProvider to use this method, as stated here: android.os.FileUriExposedException: file:///storage/emulated/0/test.txt exposed beyond app through Intent.getData()
In this case, use Xamarin.Essentials.Launcher because it adds FileProvider functionality to your app.
I am trying to build my own Music Player android app.
I want it to be very simple, just find all valid music files (perhaps I set file types such as mp3, m4a, wma etc for it to look for?) and play them in order of how they are found.
So far I have it so that I can find the default music folder path using
string musicDirectoryPath = Android.OS.Environment.GetExternalStoragePublicDirectory(Android.OS.Environment.DirectoryMusic).ToString();
What I now need is to check every file (and folder) inside that folder to see if it is mp3/m4a etc and then add them to the list. I've been messing and trying some foreach loops but cannot get anything useful.
I am very confused and quite new to Android development.
As you will see, if I specify a song that I know is on my phone in the default directory (*/NewPipe/Intergalatic - Beastie.m4a), then the music is playing.
So, how can I get a List of all those strings such as the "NewPipe/Beastie.." etc (along with the other 500+ folders inside my default music directory?
Here is my entire class so far (it's the only class in the project):
using System;
using Android.App;
using Android.OS;
using Android.Runtime;
using Android.Support.Design.Widget;
using Android.Support.V7.App;
using Android.Views;
using Android.Widget;
using Android.Media;
using Xamarin.Android;
using System.IO;
using System.Collections.Generic;
using Android.Util;
namespace Music_Player_v001
{
[Activity(Label = "#string/app_name", Theme = "#style/AppTheme.NoActionBar", MainLauncher = true)]
public class MainActivity : AppCompatActivity
{
protected MediaPlayer mediaPlayer;
List<string> filepaths_audio = new List<string>();
public void StartMediaPlayer(String filePath)
{
mediaPlayer.Reset();
mediaPlayer.SetDataSource(filePath);
mediaPlayer.Prepare();
mediaPlayer.Start();
}
protected override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
SetContentView(Resource.Layout.activity_main);
Android.Support.V7.Widget.Toolbar toolbar = FindViewById<Android.Support.V7.Widget.Toolbar>(Resource.Id.toolbar);
SetSupportActionBar(toolbar);
FloatingActionButton fab = FindViewById<FloatingActionButton>(Resource.Id.fab);
fab.Click += FabOnClick;
string musicDirectoryPath = Android.OS.Environment.GetExternalStoragePublicDirectory(Android.OS.Environment.DirectoryMusic).ToString();
Log.Info("FILEPATHS: ", "music directory path is: " + musicDirectoryPath);
mediaPlayer = new MediaPlayer();
StartMediaPlayer(musicDirectoryPath + "/NewPipe/Intergalactic - Beastie Boys (HD).m4a");
//foreach(AudioTrack track in Android.OS.Environment.DirectoryMusic)
//{
// Log.Info("BULLSHIT TAG","track count =======================" + tracks);
// tracks++;
//}
}
public override bool OnCreateOptionsMenu(IMenu menu)
{
MenuInflater.Inflate(Resource.Menu.menu_main, menu);
return true;
}
public override bool OnOptionsItemSelected(IMenuItem item)
{
int id = item.ItemId;
if (id == Resource.Id.action_settings)
{
return true;
}
return base.OnOptionsItemSelected(item);
}
private void FabOnClick(object sender, EventArgs eventArgs)
{
View view = (View) sender;
Snackbar.Make(view, "Replace with your own action", Snackbar.LengthLong)
.SetAction("Action", (Android.Views.View.IOnClickListener)null).Show();
}
}
}
Additional
I also tried this:
foreach (File f in Android.OS.Environment.GetExternalStoragePublicDirectory(Android.OS.Environment.DirectoryMusic))
{
}
It sounds like your main issue is recursively iterating through all the files in the directory and its subdirectories. Here are a few relevant links:
How to recursively list all the files in a directory in C#?
Best way to iterate folders and subfolders
https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/file-system/how-to-iterate-through-a-directory-tree
The easiest approach is probably Directory.EnumerateFiles, used like this: Directory.EnumerateFiles(musicDirectoryPath, "*", SearchOption.AllDirectories) to ensure that it also enumerates through the sub-directories.
foreach (string musicFilePath in Directory.EnumerateFiles(musicDirectoryPath, "*", SearchOption.AllDirectories)) {
Log.Info("Music File", "Music file path is " + musicFilePath);
// do whatever you want to with the file here
}
Note that this may fail if there is an access-restricted directory (or some other error) somewhere in the tree. If you'd like your solution to work around this, see the links I posted above, particularly the bottom one.
Also note, you can change that "*" (wildcard) in the method call to "*.mp3" or "*.wav" or so on, to only match files with those extensions. However, as the method doesn't support regex, I don't think there's any way to match multiple file extensions (like BOTH mp3 and wav). I think you'd have to use the "*" and then just check within the foreach if the file is a valid music file.
I am trying to add BLE functionality into a classic (WinForms?) C# desktop application, and have added references (Windows.winmd and System.Runtime.WindowsRuntime) to allow me to access the new BLE API recently introduced by Microsoft for Windows 10 UWP applications. I need to create a classic desktop application, as I need to use an older driver device wrapper (teVirtualMIDI) and want to create a .exe, not an app package.
I am referencing the aformentioned libraries from the following locations...
C:\Program Files (x86)\Windows Kits\10\UnionMetadata\Facade\Windows.WinMD
C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework.NETCore\v4.5\System.Runtime.WindowsRuntime.dll
C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework.NETCore\v4.5\System.Runtime.WindowsRuntime.UI.Xaml.dll
At this point, I simply want to be able to view connected services and characteristics in the debug output window, as is done in this blog post...
https://blogs.msdn.microsoft.com/cdndevs/2017/04/28/uwp-working-with-bluetooth-devices-part-1/
It seems that I am getting errors because the BLE API needs to perform async operations, but I am honestly at a loss. The code I have written so far is included below. Essentially, I am receiving errors when trying to call the "GetGattServicesAsync()" method, as Visual Studio says that class "BluetoothLEDevice" does not contain such a definition. That method is included in the online documentation though, and I am wondering why I am not able to access it.
I hope I have given sufficient information, and any help in solving this problem will be more than appreciated. Thank you all for all the helpful advice you give!
using System;
using System.Diagnostics;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Runtime.InteropServices;
using Windows.Devices.Bluetooth;
using Windows.Devices.Midi;
using Windows.Devices.Bluetooth.Advertisement;
using Windows.Devices.Bluetooth.GenericAttributeProfile;
using Windows.Devices.Enumeration;
using Windows.Foundation;
using Windows.Foundation.Collections;
using Windows.Storage.Streams;
using Windows.UI.Core;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Navigation;
namespace BDBMidiClient
{
public class BLEHandlingDiscovery : Page
{
//private ObservableCollection<BluetoothLEAttributeDisplay> ServiceCollection = new ObservableCollection<BluetoothLEAttributeDisplay>();
//private ObservableCollection<BluetoothLEAttributeDisplay> CharacteristicCollection = new ObservableCollection<BluetoothLEAttributeDisplay>();
public ObservableCollection<BluetoothLEDeviceDisplay> KnownDevices = new ObservableCollection<BluetoothLEDeviceDisplay>();
//private List<DeviceInformation> UnknownDevices = new List<DeviceInformation>();
//private DeviceWatcher deviceWatcher;
//private BluetoothLEDevice bluetoothLeDevice = null;
//private GattCharacteristic selectedCharacteristic;
private void StartBLEDeviceWatcher()
{
string[] requestedProperties = { "System.Devices.Aep.DeviceAddress", "System.Devices.Aep.IsConnected" };
DeviceWatcher deviceWatcher =
DeviceInformation.CreateWatcher(
BluetoothLEDevice.GetDeviceSelectorFromPairingState(false),
requestedProperties,
DeviceInformationKind.AssociationEndpoint);
/*
DeviceWatcher deviceWatcher =
DeviceInformation.CreateWatcher(
"System.ItemNameDisplay:~~\"BDB\"",
requestedProperties,
DeviceInformationKind.AssociationEndpoint);*/
deviceWatcher.Added += DeviceWatcher_Added;
deviceWatcher.Updated += DeviceWatcher_Updated;
deviceWatcher.Removed += DeviceWatcher_Removed;
deviceWatcher.Start();
//Debug.WriteLine(requestedProperties);
}
private async void DeviceWatcher_Added(DeviceWatcher sender, DeviceInformation deviceInfo)
{
Guid gattService = new Guid();
var device = await BluetoothLEDevice.FromIdAsync(deviceInfo.Id);
var services=await device.GetGattServicesAsync();
foreach (var service in services.Services)
{
Debug.WriteLine($"Service: {service.Uuid}");
var characteristics = await service.GetCharacteristicsAsync();
foreach (var character in characteristics.Characteristics)
{
Debug.WriteLine($"Characteristic: {character.Uuid}");
}
}
}
private void DeviceWatcher_Updated(DeviceWatcher sender, DeviceInformationUpdate deviceInfoUpdate)
{
}
private void DeviceWatcher_Removed(DeviceWatcher sender, DeviceInformationUpdate deviceInfoUpdate)
{
}
async void ConnectToBLEDevice(DeviceInformation deviceInformation)
{
BluetoothLEDevice bluetoothLeDevice = await BluetoothLEDevice.FromIdAsync("BDB");
}
private BluetoothLEDeviceDisplay FindBluetoothLEDeviceDisplay(string id)
{
foreach (BluetoothLEDeviceDisplay bleDeviceDisplay in KnownDevices)
{
if (bleDeviceDisplay.Id == id)
{
return bleDeviceDisplay;
}
}
return null;
}
}
The doc says the API belongs to "Windows 10 Creators Update (introduced v10.0.15063.0)". So please try to add the one from "C:\Program Files (x86)\Windows Kits\10\UnionMetadata\10.0.15063.0\Windows.winmd"
Here is the result from my project
You can see my code works well.
I am reading 'Xamarin in Action' by Manning Publications and working on Countr example. I am using below code to retrieve Sqlite3 file as has been mentioned in chapter 7 of book.
var local = FileSystem.Current.LocalStorage.Path;
var datafile = PortablePath.Combine(local, "counters.db3");
connection = new SQLiteAsyncConnection(datafile);
connection.GetConnection();
I wanted to add one more database which has static content that I already prepared. I need to place this database inside app's assets or resources and read it when app loads. I realized that on Windows, above code is looking for database at "C:\Users\\AppData\Local\Microsoft.TestHost.x86\15.6.2\". For testing Model, ViewModel I placed the database file at that location and that helped. However, I wanted to know what is right place to put database file and how to access it so I can test in Android and iPhone emulators?
Just use Environment.GetFolderPath and store the database the location Android and IOS gives your app to store data without further permissions:
string dbPath = Path.Combine (
Environment.GetFolderPath (Environment.SpecialFolder.Personal),
"counters.db3");
You can find more information on this article:
https://developer.xamarin.com/guides/android/data-and-cloud-services/data-access/part-3-using-sqlite-orm/
In Visual Studio, Xamarin Solution by default has multiple projects -
App.Core that has libraries needed for designing Model and ViewModels
App.Droid that has Android SDK libraries to build view for Android devices
App.iOS that has iOS SDK libaries to build View for Apple devices
It is not possible to add below code in App.Core library as that project has no information about App's file system. Only App.Droid and App.iOS projects carry this information.
string dbPath = Path.Combine (
Environment.GetFolderPath (Environment.SpecialFolder.Personal),
"counters.db3");
Since we want to copy a database with pre-existing content to LocalDirectory, Setup.cs in App.Droid and App.iOS must be altered. For example, for App.Droid, below are the steps needed (This is one way and there are of course alternatives)
Place Sqlite DB in App.Droid/Assets folder
Create a class file (let's call it AndroidConfiguration.cs) and add a method which I call copyElementDatabaseToLocalStorage() (Code for this method can be found after these list of steps)
In SetUp.cs, call copyElementDatabaseToLocalStorage() method
Code for copyElementDatabaseToLocalStorage Method:
using System;
using Android.App;
using System.IO;
using PCLStorage;
namespace App.Droid.AndroidSpecificSetters
{
public class AndroidConfiguration
public async void copyElementDatabaseToLocalStorage()
{
string localStorage = FileSystem.Current.LocalStorage.Path;
string databaseLocation = Path.Combine(localStorage, "counters.db3");
try
{
if (!File.Exists(databaseLocation))
{
using (var binaryReader = new BinaryReader(Application.Context.Assets.Open("counters.db3")))
{
using (var binaryWriter = new BinaryWriter(new FileStream(databaseLocation, FileMode.Create)))
{
byte[] buffer = new byte[2048];
int length = 0;
while ((length = binaryReader.Read(buffer, 0, buffer.Length)) > 0)
{
binaryWriter.Write(buffer, 0, length);
}
}
}
}
} catch (Exception e)
{
...
}
}
}
}
Code Inside App.Droid Setup.cs constructor
public Setup(Context applicationContext) : base(applicationContext)
{
AndroidConfiguration androidConfiguration = new AndroidConfiguration();
androidConfiguration.copyElementDatabaseToLocalStorage();
}
Have installed NUGet packages for Xamarin.Forms for PCL. I have 4 projects for Droid, iOS, Win 8.1 and WinPhone 8.1. Tried to connect my database, but encountered trouble: my Win and WinPhone projects don`t see it or return me the wrong path. Followed official Xamarin.Forms forum.
Interface:
public interface ISQLite
{
string GetDatabasePath(string filename);
}
WInPhone Class:
using System.IO;
using Windows.Storage;
using Baumizer.WinPhone;
using Xamarin.Forms;
[assembly: Dependency(typeof(SQLite_WinPhone))]
namespace Baumizer.WinPhone
{
public class SQLite_WinPhone:ISQLite
{
public SQLite_WinPhone() { }
public string GetDatabasePath(string filename)
{
return Path.Combine(ApplicationData.Current.LocalFolder.Path, filename);
}
}}
This class for selecting info:
public class DataBase
{
SQLiteConnection connection;
public DataBase()
{
connection= new SQLiteConnection(DependencyService.Get<ISQLite>().GetDatabasePath("Database.db"));
}
public IEnumerable<Group> GetGroups()
{
return connection.Query<Group>("SELECT * FROM [Scedule] WHERE [facultyName] =" + "'" + Data.CurrentFaculty + "'");
}
}
It works well on Android. On WinPhone I get exception of SQLite - no such table: Scedule. I open the local directory for emulator where db was - 0Kb.
I put db to Assets and set BuildInAction to Content. What`s wrong? Need help
On forum find this code, putted to OnLaunched(...) in WinPhone App.cs:
if(await ApplicationData.Current.LocalFolder.GetItemAsync(Data.DataBaseName) == null)
{
StorageFile databaseFile = await Package.Current.InstalledLocation.GetFileAsync($"Assets\\{Data.DataBaseName}");
await databaseFile.CopyAsync(ApplicationData.Current.LocalFolder);
}
It copies DB if it's not exist, but it is. May be I need to delete DB 0Kb, but I don't know how to do this.
It's work, OnLaunched():
try
{
await ApplicationData.Current.LocalFolder.GetItemAsync("Scedule.db");
}
catch (System.IO.FileNotFoundException)
{
StorageFile databaseFile =
await Package.Current.InstalledLocation.GetFileAsync($"Assets\\{"Scedule.db"}");
await databaseFile.CopyAsync(ApplicationData.Current.LocalFolder);
}
DB was uncorrectly copied. May be there are any another ways.