I'm trying to get all the available media files -Or files with specific extensions- in the device -Either Android or IOS- but I can't find a way to do this, so instead I was trying to get all the device storages and I'll iterate through them with my own code to get the files I need.
However, I can't find a way to get the storages paths as well, but I know I get the main storage path in Android using Android.OS.Environment.ExternalStorageDirectory.AbsolutePath so maybe I can make an interface for the same for IOS, but still, that gets the main storage only, not any attached USB or Memory Cards...
So any help would be appreciated in either approaches. Thanks in advance.
Android:
Android groups the filesystem into two different types of storage: Internal Storage, External Storage.
Internal Storag: https://learn.microsoft.com/en-us/xamarin/android/platform/files/#working-with-internal-storage
You could use System.Environment.GetFolderPath() to retrieve internal storage directory like below:
/data/user/0/com.companyname/files
External Storage: https://learn.microsoft.com/en-us/xamarin/android/platform/files/external-storage?tabs=windows
The directory of the private external files would be:
/storage/emulated/0/Android/data/com.companyname.app/files/
You could get it via dependency service.
DependencyService: https://learn.microsoft.com/en-us/xamarin/xamarin-forms/app-
fundamentals/dependency-service/introduction
Create an interface IExternalStorage:
public interface IExternalStorage
{
string GetPath();
}
The implementation on the Android:
[assembly:Dependency(typeof(AndroidImplementation))]
namespace App.Droid
{
public class AndroidImplementation: IExternalStorage
{
public string GetPath()
{
Context context = Android.App.Application.Context;
var filePath = context.GetExternalFilesDir("");
return filePath.Path;
}
}
}
Then you could use it in Xamarin.Forms:
var folderPath = DependencyService.Get<IExternalStorage>(). GetPath();
IOS:
Each iOS app has its own sandbox directory. https://developer.apple.com/library/archive/documentation/FileManagement/Conceptual/FileSystemProgrammingGuide/FileSystemOverview/FileSystemOverview.html
Generally, we store user's data in the Documents folder. And we could access its path on Forms directly:
var path = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
Please note: IOS only could access photo gallery and iCloud folder.
File system access in Xamarin.iOS: https://learn.microsoft.com/en-us/xamarin/ios/app-fundamentals/file-system
Related
this question follows my previous question.
I have a c# ASP.NET application and i want to provide support for plugins. Plugins can be custom c# classes, javascript, html, css, images, etc.
I see no problem as long as my application is extended with c# classes because all the user has to do is create a new "class library" project in visual studio and implement the interfaces, i provide. Then build a dll out of it and upload it to my server. The plugin-developer can add static files (html, js, css, etc.) into this project as well but i found some problems with that:
Every static file i add to the plugin project gets the build action "content" and it seems i cannot read those files from my server. (see my previously answered question). I have to manually select "Embedded Resource" on each file, so it is packed with the plugin dll.
I want to support Typescript for the plugins. The Typescript compiler generates javascript files in the same directory as the typescript-files. But the javascript files are not included in the project and therefore i have to include these in the plugin project and then set the correct build action. I don't want the plugin developers to do that all the time.
If the static files have the build action "enbedded resources", then the server can pickup these files by using the assembly.GetManifestResourceNames() method. This method returns the resources as a string. The path is not separated by \ or / but with a dot instead. So i am not able to distinguish between file path (this is relevant) or filename (also relevant to pickup the correct files), because the original filename can also have dots.
So i am starting to question the "class library" project type is right for my needs. Is there a way to get around of my issues or do i have to use another project type?
Thank you for any help!
Edit: Changed the question a little bit so it is better to understand.
You could make a zip package with the plugin dll and files. NuGet also uses this mechanism. A .nupkg is also just a zip file.
I would start by looking at MEF (Managed Extensibility Framework).
MSDN information can be found here: https://msdn.microsoft.com/en-us/library/dd460648(v=vs.110).aspx
From that link you can get more information and I believe there is a tutorial as well.
Oh, for me it seems very simple.
Let the developer create the plugin freestyle and put all the additional files in a directory, let's call it extras
To implement the your interface they will need your assembly so I guess you will ship it via nuget, or just some link. No matter what the case, provide them with some powershell script what will be required to run before the final build
The script would create zip archive from the extras directory and add it to the ClassLibrary project as EmbeddedResource.
As you mentioned earlier, you can access EmbeddedResource. So all you would do is to unpack it and you would have the exact directory tree.
The best idea would be to provide project template with script included, and also the empty zip archive added as embedded resource (it will be easier to just pack the files in the script and replace the file), and pre-build action set to run the script.
Am I missing something?
What about this.
In your web application, you could add a function that loop into your plugin directory and find DLL implementing an Iplugin (name is up to you) interface.
The interface is defined in a class library that both your web application and plugins have to implement.
You can use the Httpcontext Server mappath to read javascript and other files.
Here is a very basic implementation
First, you have the plugin interface (a class library implemented both by the web application and the individual plugins) I implemented sample properties and methods...
using System.Web;
public interface IPlugin
{
string Name { get; set; }
string Output { get; set; }
void Load(ref httpcontext Context);
void Dispose();
void Display();
}
Next, you have the Actual plugin class library we want to implement.
using System.Web;
using IPlugins;
public class AwesomePlugin : IPlugins.IPlugin
{
private string _Name = "AwesomePlugin";
private HttpContext _Context;
public string Name {
get { return _Name; }
set { _Name = value; }
}
public string Output {
get { return "Yay !!!"; }
set {
throw new NotImplementedException();
}
}
public void Display()
{
throw new NotImplementedException();
}
public void Dispose()
{
throw new NotImplementedException();
}
public void Load(ref Web.HttpContext Context)
{
}
}
Finally, you dynamically load your plugins so you can use them in your application.
private Dictionary<string, IPlugins.IPlugin> _Plugins = new Dictionary<string, IPlugins.IPlugin>();
public void LoadPlugins()
{
lock (static_LoadPlugins_IpluginType_Init) {
try {
if (InitStaticVariableHelper(static_LoadPlugins_IpluginType_Init)) {
static_LoadPlugins_IpluginType = typeof(IPlugins.IPlugin);
}
} finally {
static_LoadPlugins_IpluginType_Init.State = 1;
}
}
string ServerPath = HttpContext.Current.Server.MapPath("~") + "Plugins";
dynamic Plugins = io.Directory.GetFiles(ServerPath);
foreach (string PluginPath in Plugins) {
dynamic Assembly = system.Reflection.Assembly.LoadFile(PluginPath);
Type PluginClass = Assembly.GetTypes.Where(T => T.GetInterface("IPlugin") != null).First;
IPlugins.IPlugin MyPlugin = Activator.CreateInstance(PluginClass);
MyPlugin.Load(httpcontext.Current);
_Plugins.#add(PluginClass.ToString, MyPlugin);
}
}
static bool InitStaticVariableHelper(Microsoft.VisualBasic.CompilerServices.StaticLocalInitFlag flag)
{
if (flag.State == 0) {
flag.State = 2;
return true;
} else if (flag.State == 2) {
throw new Microsoft.VisualBasic.CompilerServices.IncompleteInitialization();
} else {
return false;
}
}
That way, you can implement whatever you want in your plugin.
I believe you could load your plugins in a separate appdomain with restricted permissions to everything.
The files (Javascript / CSS / Html) should be available by accessing the full path of the file.
string ServerPath = HttpContext.Current.Server.MapPath("~") + "Plugins";
If the resources is embedded into the plugin DLL, you could read the stream from the loaded assembly or let the plugin manage its own embedded files.
For question Number 2, you can use
MS Build
to change the contenttype during build process.
You have to make yourself confident with MS Build
I have found myself having to rely on having to use some local folders. No problem normally, but the main issue is that if transfer the project to another machine, even during development the structure reports back an error.
The error is that is page can't be displayed on a local website and an IO error when I go to read from the file.
The directory is as follows
C:\Users\Keith\OneDrive\Documents\Project 2016\Lingerie\Lingerie\Corset\Corset
The folders in question are
Master Audio
Images
User Audio
The actual folder as they're used
private String folderPlay = #"C:\Users\Keith\OneDrive\Documents\Project 2016\Lingerie\Lingerie\Corset\Corset\Master Audio\";
private String folderRecord = #"C:\Users\Keith\OneDrive\Documents\Project 2016\Lingerie\Lingerie\Corset\Corset\User Audio\";
private String folderImages = #"C:\Users\Keith\OneDrive\Documents\Project 2016\Lingerie\Lingerie\Corset\Corset\Images\";
What I would like to do is be able to call those file relative to the code? Also if it's not too much trouble could a small note be attached so I can understand/
Use only the path in your project rather than whole physical path
private String folderPlay = #"~/Master Audio";
private String folderRecord = #"~/User Audio";
private String folderImages = #"~/Images";
and my suggestion is use path like Master_Audio, User_Audio. Don't put space between word
What I found useful in similar situation is to actually inject content root dir to the class that needs to know the location of particular resources:
E.g.
class Foo{
private string readonly _contentRoot;
public Foo (string contentRoot)
{
_contentRoot = contentRoot
}
public void DoSomethingWithImage()
{
string imgRelativePath = "/img/img1.png";
string imgPath = Path.Combine(_contentRoot, imgRelativePath);
// do something with imgPath
}
}
The benefit of this is that you can now write tests for your methods (which might be difficult otherwise). Sometimes unit testing framework copies libs to a temp location, but you still want to run the tests.
You can resolve root directory from several locations, depending how you run the code:
For unit tests, use Assembly.GetExecutingAssembly().GetName().CodeBase
For Web app HttpRuntime or HttpContext
For Windows app AppDomain.BaseDirectory or similar
Rule of thumb:
You should not have any hardcoded absolute paths (pain with testing, running on another machine, deployment)
Instead, use relative paths (usually relative to project root or root/content)
I have a solution with a Windows Store app project and a Class Library project and I want to add Localization support.
How can I add the all the Resource files to my Class Library and use them in both my App and Class Library?
In order to avoid writing a lot of boilerplate and error prone code when you add a new resource string like:
Add it to .resw file
Add it to your Static class that provides access to the resource
Add it to each language specific .resw file (ex: en, fr, pt, etc)
You can use the following approach:
Create "Strings" folder and add there just one folder for default language (for example "en-US") in your Class Library
Add Resources.resw file to "en-US" folder with required keys/values
Install Multilingual App Toolkit
Enable MAT in VS for your Class Library (VS->Tools->Enable Multilingual App Toolkit)
Add required languages to your Class Library (VS->Project->Add Translation languages...)
Install ResW File Code Generator VS extension
Go to Resources.resw file properties and set Custom Tool to "ReswFileCodeGenerator" (you can also specify namespace in Custom Tool Namespace)
To solve issue with supported languages detection(currently generated manifest contains supported languages according to folder structure "Strings/en-US") you need to add folders for all required languages in your App library ("fr-FR", "bg-BG", etc) and put Resources.resw file with only one fake key.
Build your solution and enjoy!
With this approach all your resources are available via static class generated by ReswFileCodeGenerator and all of them work with x:uid in XAML. You don't need to care about keys synchronization between different languages. Also MAT can translate your resources for you.
Ok, I found how to do this and with a sample project found here
Basically the implementation is the following:
In the ClassLibrary create a folder named "Strings"
Inside the Strings folder create one for each language (ex: en, fr, pt, etc)
And add a Resources.resw in each of those folders with your keys/values
Now add a new Class in your ClassLibrary that has the following code(adapted to your project):
using System;
using Windows.ApplicationModel.Resources;
namespace MyClassLibraryName.Tools {
public static class LocalizationTool {
static ResourceLoader resourceLoader = null;
public static string MyStringOne {
get {
String name;
GetLibraryName("MyStringOne", out name);
return name;
}
}
private static void GetLibraryName(string resourceName, out string resourceValue) {
if(resourceLoader == null) {
resourceLoader = ResourceLoader.GetForCurrentView("MyClassLibraryName/Resources");
}
resourceValue = resourceLoader.GetString(resourceName);
}
}
}
And in your ClassLibrary or MainApp just call the following:
string text = LocalizationTool.MyStringOne;
I have an ASP.Net website that references a class library. In the class library I need to read a file into memory.
At the top level of my class library there is a folder called EmailTemplateHtml containing the file MailTemplate.html that I want to read in.
How can I do this?
In Visual Studio, you can configure your library such that the file is copied into the build directory of any project that depends upon it. Then you can get the path to the build directory at runtime in order to read your file.
Step by step instructions, starting from a fresh solution:
Create your application project and your class library project.
Add a reference to the class library project from the application project via Properties->Add->Reference from the application's context menu in Solution Explorer:
Create the file in your class library project that you need to read, then set its Copy to Output Directory property to either Copy always or Copy if newer via the Properties pane in Solution Explorer:
From within either the class library project or your application (either will work with exactly the same code), reference your file relative to Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location). For example:
using System.Reflection;
using System.IO;
namespace MyLibrary
{
public class MyClass
{
public static string ReadFoo()
{
var buildDir = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
var filePath = buildDir + #"\foo.txt";
return File.ReadAllText(filePath);
}
}
}
(Note that back before .NET Core, you could use a file path relative to System.IO.Directory.GetCurrentDirectory() instead, but this doesn't work in a .NET Core application because the initial working directory for .NET Core apps is the source directory instead of the build directory, apparently because this was needed by ASP.NET Core.)
Go ahead and call your library code from your application code, and everything will work fine. e.g.:
using Microsoft.AspNetCore.Mvc;
using MyLibrary;
namespace AspCoreAppWithLib.Controllers
{
public class HelloWorldController : Controller
{
[HttpGet("/read-file")]
public string ReadFileFromLibrary()
{
return MyClass.ReadFoo();
}
}
}
using System.IO;
using System.Reflection;
public static string ExecutionDirectoryPathName()
{
var dirPath = Assembly.GetExecutingAssembly().Location;
dirPath = Path.GetDirectoryName(dirPath);
return Path.GetFullPath(Path.Combine(dirPath, "\EmailTemplateHtml\MailTemplate.html"));
}
If you want to find the path where the assembly is located; from within the assembly then use the following code:
public static string ExecutionDirectoryPathName
{
get
{
var dirPath = Assembly.GetExecutingAssembly().Location;
dirPath = Path.GetDirectoryName(dirPath);
return dirPath + #"\";
}
}
I am not sure what you mean by a folder in the class library but you can use the current working directory if you wish to build a path as follows:
System.IO.Directory.GetCurrentDirectory()
You can then use the Path.Combine() method to build file paths.
You can add a Static class in your class library with some static methods/properties to set. Set the values from Global.ascx.cs on start app method. Now you can get the values of class library.
Hope this makes clear.
Happy coding
I have a DLL with a static method which would like to know the current directory. I load the library
c:\temp> add-type -path "..."
...and call the method
c:\temp> [MyNamespace.MyClass]::MyMethod()
but both Directory.GetCurrentDirectory() and .Environment.CurrentDirectory get the current directory wrong...
what is the correct way to do this?
There are two possible "directories" you can have in powershell. One is the current directory of the process, available via Environment.CurrentDirectory or Directory.GetCurrentDirectory(). The other "directory" is the current location in the current Powershell Provider. This is what you see at the command line and is available via the get-location cmdlet. When you use set-location (alias cd) you are changing this internal path, not the process's current directory.
If you want some .NET library that uses the process's current directory to get the current location then you need to set it explicitly:
[Environment]::CurrentDirectory = get-location
Powershell has an extensible model allowing varying data sources to be mounted like drives in a file system. The File System is just one of many providers. You can see the other providers via get-psprovider. For example, the Registry provider allows the Windows Registry to be navigated like a file system. Another "Function" lets you see all functions via dir function:.
If the command in your DLL inherits from System.Management.Automation.PSCmdLet, the current PS location is available in SessionState.Path.
public class SomeCommand : PSCmdlet
{
protected override void BeginProcessing()
{
string currentDir = this.SessionState.Path.CurrentLocation.Path;
}
}
To get to the path without a session reference, this code seems to work. This solution I found after going through code that makes GIT autocomplete in PS GIT Completions, specifically this function here.
public class Test
{
public static IEnumerable<string> GetPath()
{
using (var ps = PowerShell.Create(RunspaceMode.CurrentRunspace))
{
ps.AddScript("pwd");
var path = ps.Invoke<PathInfo>();
return path.Select(p => p.Path);
}
}
}
Output:
PS C:\some\folder> [Test]::GetPath()
C:\some\folder