I am doing a Xamarin Forms project with iOS and Android, and I am trying to retrieve a txt file from the assets folder for Android and from the Resources folder in iOS. It works for iOS but it doesn't work for Android. To be exact, in the Android project, the path for my text file is "Assets/versions/latestVersion.txt"
I have this Base URL for Android:
[assembly: Dependency(typeof(BaseUrl_Android))]
namespace WorkingWithWebview.Android
{
public class BaseUrl_Android : IBaseUrl
{
public string Get()
{
return "file:///android_asset/";
}
}
}
Now, in my code, I have:
string versioningFile = Xamarin.Forms.DependencyService.Get<IBaseUrl>().Get();
string path = Path.Combine(versioningFile, "versions/latestVersion.txt");
string file = File.ReadAllText(path);
My IBaseUrl is
public interface IBaseUrl { string Get(); }
It doesn't recognize the path, the error states "Could not find a part of the path /file:/android_asset/versions/latestVersion.txt".
What is the problem ? The path is clearly what i want it to be.
About Assete,it's read using an AssetManager.
You could also use File System Helpers of Xamarin.Essentials.
using (var stream = await FileSystem.OpenAppPackageFileAsync(fileName))
{
using (var reader = new StreamReader(stream))
{
var fileContents = await reader.ReadToEndAsync();
}
}
Note: Add any file into the Assets folder in the Android project and mark the Build Action as AndroidAsset
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 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();
}
I am trying to load a local HTML page in a webview with Xamarin forms.
I am using the basic example in the dev docs although I can get a URL to load I can't get my own HTML pages to load. This only needs to be done through Android so there is no worries about about IOS and Windows.
The Xaml:
<WebView
x:Name="webviewjava"></WebView>
The code behind:
public partial class javscriptExample : ContentPage
{
public interface IBaseUrl { string Get(); }
public javscriptExample()
{
InitializeComponent();
var source = new HtmlWebViewSource();
source.BaseUrl = DependencyService.Get<IBaseUrl>().Get();
webviewjava.Source = source;
}
}
The platform specific file (LocalFile.cs):
Just to note this has been set as an Android asset.
[assembly: Dependency(typeof(LocalFiles))]
namespace maptesting.Droid
{
public class LocalFiles: IBaseUrl
{
public string Get()
{
return "file:///android_asset/";
}
}
}
and under the asset's folder there is a 'TestWebPage.html', also set as an Android asset.
Although I dont know what the problem is I have put it through debug and the base url is coming back blank. Just to be clear im not getting a file not found, the screen is simply blank.
Also, and Im not sure if this makes a difference. There is no syntax highlighting on 'IBaseUrl' in the LocalFiles.cs file. So I'm not sure if it can 'see' it.
Any ideas?
I am also suffering with the same issue,but I resolved in the following way
Use "UrlWebViewSource" instead of "HtmlWebViewSource"
var urlSource = new UrlWebViewSource();
string baseUrl = DependencyService.Get<IWebViewBaseUrl>().GetBaseUrl();
string filePathUrl = Path.Combine(baseUrl, "imprint.html");
urlSource.Url = filePathUrl;
WebBrowser.Source = urlSource;
You must check the file properties for Build Action = BundleResource
Try this code to load local html file
var source = new HtmlWebViewSource();
string url = DependencyService.Get<IBaseUrl>().GetBaseUrl();
string TempUrl = Path.Combine(url, "terms.html");
source.BaseUrl = url;
string html;
try
{
using (var sr = new StreamReader(TempUrl))
{
html = sr.ReadToEnd();
source.Html = html;
}
}
catch(Exception ex){
Console.WriteLine(ex.Message);
}
Implementations of the interface for each platform must then be provided
iOS
[assembly: Dependency(typeof(BaseUrl))]
namespace yournamespace
{
public class BaseUrl: IBaseUrl
{
public string GetBaseUrl()
{
return NSBundle.MainBundle.BundlePath;
}
}
}
Android
[assembly: Dependency (typeof(BaseUrl))]
namespace yournamespace {
public class BaseUrl_Android : IBaseUrl {
public string Get() {
return "file:///android_asset/";
}
}
}
WebView.BaseUrl only tells the WebView where to start looking for files. It's the root folder of the "web site". By default browsers will load the file index.html, so if you rename your file to index.html I believe it should load automatically.
I think this should be possible too:
webviewjava.BaseUrl = DependencyService.Get<IBaseUrl>().Get();
webviewjava.Source = "TestWebPage.html";
Here you're saying "use this location as the default place to look for files" and "look up this file and use it as the source for the HTML".
This is an old post but It may help someone looking to implement with Android, iOS and UWP with just one HTML file. With this approach you only use one HTML file for all platforms.
https://learn.microsoft.com/en-us/xamarin/xamarin-forms/app-fundamentals/files?tabs=vsmac#loading-files-embedded-as-resources
Im not sure if this counts but I found a work around. Instead of taking the above route I simply did this:
webviewjava.Source = "file:///android_asset/TestWebPage.html";
in the code behind, and just left out the IBaseUrl call altogether.
This works as its supposed to.
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.
Problem:
I can't seem to figure out the right signature for Unity cloud build's post export method. According to the documentation:
The fully-qualified name of a public static method you want us to call
after we finish the Unity build process (but before Xcode). For
example: ClassName.CoolMethod or NameSpace.ClassName.CoolMethod. No
trailing parenthesis, and it can't have the same name as your
Pre-Export method! This method must accept a string parameter, which
will receive the path to the exported Unity player (or Xcode project
in the case of iOS).
Here is my code:
public static void OnPostprocessDevBuildIOS(string ExportPath)
{
var projPath = ExportPath + "/Unity-iPhone.xcodeproj/project.pbxproj";
var proj = new PBXProject();
var nativeTarget =
proj.TargetGuidByName(PBXProject.GetUnityTargetName());
var testTarget =
proj.TargetGuidByName(PBXProject.GetUnityTestTargetName());
string[] buildTargets = {nativeTarget, testTarget};
proj.ReadFromString(File.ReadAllText(projPath));
proj.SetBuildProperty(buildTargets, "ENABLE_BITCODE", "NO");
File.WriteAllText(projPath, proj.WriteToString());
}
and here is the error:
I've tried multiple test method signatures and can't seem to get anything to work. I've even tried just a method that logs out the path.
Additional Information:
Unity Version: 5.3.1f
Unity Cloud Build: 5.3.1f
Target: iOS 8.0+
Also, my cloud build settings script is located in the editor folder as required.
Ok so I got the the bitCode disabling post process to work with the following code, but only when I build manually. When I build from cloud build, with no error the app freezes at the splash screen. When I build from my local machine, the app runs just fine.
[PostProcessBuild]
public static void OnPostprocessBuild(BuildTarget buildTarget, string path)
{
if (buildTarget == BuildTarget.iOS)
{
string projPath = path + "/Unity-iPhone.xcodeproj/project.pbxproj";
PBXProject proj = new PBXProject();
proj.ReadFromString(File.ReadAllText(projPath));
string nativeTarget = proj.TargetGuidByName(PBXProject.GetUnityTargetName());
string testTarget = proj.TargetGuidByName(PBXProject.GetUnityTestTargetName());
string[] buildTargets = new string[]{nativeTarget, testTarget};
proj.SetBuildProperty(buildTargets, "ENABLE_BITCODE", "NO");
File.WriteAllText(projPath, proj.WriteToString());
}
}
I too had the same issue "splash screen stuck" right after launch....
I solved this issue. Please use the below code.
Tested in Unity 5.4.1p2 and Xcode 7.3.
using UnityEngine;
using System.Collections;
using UnityEditor;
using System.IO;
using UnityEditor.Callbacks;
#if UNITY_IOS
using UnityEditor.iOS.Xcode;
#endif
public class Postprocessor : AssetPostprocessor
{
#if UNITY_IOS
[PostProcessBuild]
public static void OnPostprocessBuild(BuildTarget buildTarget, string path)
{
if (buildTarget == BuildTarget.iOS)
{
string projPath = path + "/Unity-iPhone.xcodeproj/project.pbxproj";
PBXProject proj = new PBXProject();
proj.ReadFromString(File.ReadAllText(projPath));
string target = proj.TargetGuidByName("Unity-iPhone");
proj.SetBuildProperty(target, "ENABLE_BITCODE", "false");
File.WriteAllText(projPath, proj.WriteToString());
// Add url schema to plist file
string plistPath = path + "/Info.plist";
PlistDocument plist = new PlistDocument();
plist.ReadFromString(File.ReadAllText(plistPath));
// Get root
PlistElementDict rootDict = plist.root;
rootDict.SetBoolean("UIRequiresFullScreen",true);
plist.WriteToFile(plistPath);
}
}
#endif
}
In fact OnPostprocessBuild is always called, so you don't have to put anything in post export method field, which is designed for more specific methods.