I want to use the main logo from Flutter resources in my native Android code — for example, to display a notification. As you know, in Android you usually get resources from drawable folder like this R.drawable.icon. But then I have to copy the icon to Android drawable folder as well. Maybe there is a way to access Flutter resources (read — assets) from native code?
There's a nice section sort-of about this in the flutter documentation.
Given an asset "icons/heart.png", for android (since 1.22):
FlutterLoader loader = FlutterInjector.instance().flutterLoader();
String key = loader.getLookupKeyForAsset("icons/heart.png");
For iOS (swift):
let key = registrar.lookupKey(forAsset: "icons/heart.png");
let path = Bundle.main.path(forResource: key, ofType: nil)
For iOS (obj-c):
NSString* key = [registrar lookupKeyForAsset:#"icons/heart.png"];
NSString* path = [[NSBundle mainBundle] pathForResource:key
ofType:nil];
For android previous to Flutter version 1.22 (deprecated):
AssetManager assetManager = registrar.context().getAssets();
String key = registrar.lookupKeyForAsset("icons/heart.png");
AssetFileDescriptor fd = assetManager.openFd(key);
However, you won't be able to share a drawable item directly to flutter, and the main icon is a bit of a special case that you definitely can't share with flutter either. Flutter doesn't know what a 'drawable' is but rather deals with resources its own cross-platform way.
UPDATE FOR NEW VERSION
For flutter release 1.22 or above, FlutterLoader is only accessible via FlutterInjector.
import io.flutter.FlutterInjector;
FlutterLoader loader = FlutterInjector.instance().flutterLoader();
String key = loader.getLookupKeyForAsset("assets_file_name");
You need to get relative path of asset file.
Below is the helper method to access the file/image path from swift native code.
func flutterAssetLottieFilePath(imageName : String) -> String {
var flutterViewController = FlutterViewController()
flutterViewController = UIWindow.init(frame: UIScreen.main.bounds).rootViewController as! FlutterViewController
let key = flutterViewController.lookupKey(forAsset:"assets/images/\(imageName).png")
let path = Bundle.main.path(forResource: key, ofType: nil)
return path ?? ""
}
As of when I wrote this, the documentation was out of date. On my version of flutter, 1.17.1 here is an kotlin android example with the necessary imports above:
import android.content.Context
import io.flutter.embedding.engine.loader.FlutterLoader
import java.io.InputStream
val myAssetPath : Sting = "assets/my_asset"
val assetLookupKey = FlutterLoader.getInstance().getLookupKeyForAsset(myAssetPath)
val inputStream: InputStream = applicationContext.assets.open(assetLookupKey)
// Read in the file from the InputStream...
It should be noted that the value returned from this getLookupKeyForAsset method is usually your asset path, as included it on your pubspec.yaml under assets, simply prefixed with "flutter_assets/". So in the case of the above example, it would return flutter_assets/assets/my_asset
Related
For a robot simulation I use a csv file with data. I read data as follows:
string dbPath = ""; string realPath; // Android string oriPath = System.IO.Path.Combine(Application.streamingAssetsPath, "Data.csv");
// Android only use WWW to read file
WWW reader = new WWW(oriPath);
while (!reader.isDone) { }
realPath = Application.persistentDataPath + "/db";
System.IO.File.WriteAllBytes(realPath, reader.bytes);
dbPath = realPath;
using (StreamReader sr = new StreamReader(dbPath))
{...}
In the Unity Editor version the simulation works as expected but on Android the arm ofthe robot moves in a strange way as you can see in the videos. I compared the two db files (on the Windows and Android paths) and the content is identical. What could be the reason of the strange movemenz?
Editor https://streamable.com/7z4qob Android https://streamable.com/rv4nm2
Thank you!
To get StreamingAssets WWW you need to add prefix jar:file:///
To get Application.persistentDataPath you need to add prefix file:///
If files are identical then problem is not in getFile code, why did you provide it?
I want to save my logs to a folder which I can access with windows explorer. For example I want to create my log in the following path
This PC\Galaxy A5 (2017)\Phone\Android\data\MyApp\files
So I tried to use Environment variables... I get such as
/data/user/...
But here i cannot see the file what I created (using code I can access the path but I want to see in the explorer).
how I can create a path like above with code?
When I tried this code
var finalPath2 = Android.OS.Environment.GetExternalStoragePublicDirectory
(Android.OS.Environment.DataDirectory.AbsolutePath);
I get the path "/storage/emulated/0/data"
and
If i use the code
var logDirectory =Path.Combine(System.Environment.GetFolderPath
(System.Environment.SpecialFolder.ApplicationData),"logs");
I get the following path like:
/data/user/0/MyApp/files/.config/logs
and
var logDirectory =Path.Combine(System.Environment.GetFolderPath
(System.Environment.SpecialFolder.MyDocuments),"logs");
"/data/user/0/IM.OneApp.Presentation.Android/files/logs"
but unfortunately I cannot access this folder by explorer....
This PC\Galaxy A5 (2017)\Phone\Android\data\MyApp\files
So how to find out this path in c# by using environments?
Update:
when I give the following path hardcoded, it creates the file where I want..
logDirectory = "/storage/emulated/0/Android/data/MyApp/files/logs";
is there any environment to create this path? I can combine 2 environments and do some string processing in order to create this path. But maybe there is an easier way?
You are looking for the root of GetExternalFilesDir, just pass a null:
Example:
var externalAppPathNoSec = GetExternalFilesDir(string.Empty).Path;
Note: This is a Context-based instance method, you can access it via the Android application context, an Activity, etc... (see the link below to the Android Context docs)
Shared storage may not always be available, since removable media can be ejected by the user. Media state can be checked using Environment.getExternalStorageState(File).
There is no security enforced with these files. For example, any application holding Manifest.permission.WRITE_EXTERNAL_STORAGE can write to these files.
re: https://developer.android.com/reference/android/content/Context#getExternalFilesDir(java.lang.String)
string docFolder = Path.Combine(System.Environment.GetFolderPath
(System.Environment.SpecialFolder.MyDocuments), "logs");
string libFolder = Path.Combine(docFolder, "/storage/emulated/0/Android/data/MyApp/files/logs");
if (!Directory.Exists(libFolder))
{
Directory.CreateDirectory(libFolder);
}
string destinationDatabasePath = Path.Combine(libFolder, "temp.db3");
db.Backup( destinationDatabasePath, "main");
Dropbox now have an apsolutely new API, which is absolutely differ from the old one (it's interesting why), but there's no ANY actual examples in the internet, so I've found only some code in their examples. Here is it:
// Download the file.
try (OutputStream outputStream = new FileOutputStream (file)) {
mDbxClient.files ()
.download (metadata.getPathLower (), metadata.getRev ())
.download (outputStream);
}
I need to download file from remote folder to the local one, so I need to use this path for example:
.download ("Backups/backup.ab", "/storage/sdcard/Folder/backup.ab")
I've tried it, but get a error
IllegalArgumentException: String 'rev' does not match pattern'
Do you know, what it can be, and metadata.getPathLower () and metadata.getRev () methods are using for? I've learned, that metadata var gets from the first argv from execute (), but what this functions do?
Thanks a lot!
Not sure if if works for android. I have posted the following method just in case someone is looking for a C# .net solution.
private async Task Download(DropboxClient dbx, string folder, string file, string localFilePath)
{
using (var response = await dbx.Files.DownloadAsync(folder + "/" + file))
{
using (var fileStream = File.Create(localFilePath))
{
(await response.GetContentAsStreamAsync()).CopyTo(fileStream);
}
}
}
Parameter example:
file = "YourFileName.pdf";
folder = "/YourDropboxFolderName";
localFilePath = #"C:\Users\YourUserName\YourFileName.pdf";
The Dropbox API v2 Java SDK's download method takes these two parameters:
String path
String rev
Per the download method documentation there, the first is the remote path of the file in Dropbox you want to download, and the second is the identifier for the revision of the file you want. The second parameter is not the local path where you want to save the file, as it appears you're supplying in your code. Instead, you save the file content using the .download (outputStream); portion of the sample code you posted, e.g., as also shown in this sample code.
Also, as stated in the documentation, the second parameter is deprecated and should no longer be used. You can just use the version of the download method that only takes the one parameter. The code for using it is otherwise the same as the sample.
For reference, in the sample, the metadata object is an instance of FileMetadata. You can find more information on the getPathLower and getRev methods in the documentation as well.
I have some level definitions in xml format (with .txt extension) inside (without any subfolders) my project's rescources folder
I for more scalability, I have a plain text file naming all these level definition XMLs
I read that file using
TextAsset WorldList = (TextAsset)Resources.Load("WorldList");
And then I load the needed world:
TextAsset TA = (TextAsset)Resources.Load(/*"LevelDefs/" +*/ Worlds[worldToload]);
xps.parseXml(TA.text);
loadLevel(levelToLoad);
(you see that I have moved these rescources out of subfolder to reduce the chance of them not loading)
worldToload here is the index number of that world
program works fine on windows but nothing loads on my android test device.
I seem to have problems debugging on device so I only guess something went wrong in loading phase.
any suggestions?
From Unity Documentation:
Most assets in Unity are combined into the project when it is built.
However, it is sometimes useful to place files into the normal
filesystem on the target machine to make them accessible via a
pathname. An example of this is the deployment of a movie file on iOS
devices; the original movie file must be available from a location in
the filesystem to be played by the PlayMovie function.
Any files placed in a folder called StreamingAssets in a Unity project
will be copied verbatim to a particular folder on the target machine.
You can retrieve the folder using the Application.streamingAssetsPath
property. It’s always best to use Application.streamingAssetsPath to
get the location of the StreamingAssets folder, it will always point
to the correct location on the platform where the application is
running.
So below snippet should work:
// Put your file to "YOUR_UNITY_PROJ/Assets/StreamingAssets"
// example: "YOUR_UNITY_PROJ/Assets/StreamingAssets/Your.xml"
if (Application.platform == RuntimePlatform.Android)
{
// Android
string oriPath = System.IO.Path.Combine(Application.streamingAssetsPath, "Your.xml");
// Android only use WWW to read file
WWW reader = new WWW(oriPath);
while ( ! reader.isDone) {}
realPath = Application.persistentDataPath + "/Your"; // no extension ".xml"
var contents = System.IO.File.ReadAll(realPath);
}
else // e.g. iOS
{
dbPath = System.IO.Path.Combine(Application.streamingAssetsPath, "Your.xml");
}
Thanks to David I have resolved this problem.
for more precision I add some small details:
1-As David points out in his answer (and as stated in unity documentations), we should use StreamingAssets if we want to have the same folder structure. One should use Application.streamingAssetsPathto retrieve the path.
However, files are still in the apk package in android's case, so they should be accessed using unity android's www class.
a good practice here is to create a method to read the file (even mandatory if you are going to read more than one file)
here is one such method:
private IEnumerator LoadDataFromResources(string v)
{
WWW fileInfo = new WWW(v);
yield return fileInfo;
if (string.IsNullOrEmpty(fileInfo.error))
{
fileContents = fileInfo.text;
}
else
fileContents = fileInfo.error;
}
This is a coroutine, and to use it we need to call it using
yield return StartCoroutine(LoadDataFromResources(path + "/fileName.ext"));
Yet another remark: we can not write into this file or modify it as it's still inside the apk package.
I am trying to upload a file inside a repository on Alfresco.
I am using the Alfresco Mobile SDK for Android that is well documented and easy to use.
The problem is that I didn't find how to create an object ContentFile from a file (the one that I want to upload), in order to use the method:
public Document createDocument(Folder folder, String nameFile,
Map<String,Serializable> properties, ContentFile contentFile)
(this method works great, I am able to create a file without content).
I am pretty sure it is not big deal but I am looking around and don'y manage to find what I need.
Thanks in advance for your help.
ServiceRegistry serviceRegistry = Login.session.getServiceRegistry();
DocumentFolderService documentFolderService =
serviceRegistry.getDocumentFolderService();
Folder folder = (Folder)
documentFolderService.getNodeByIdentifier(repository.getIdentifier());
Map<String,Serializable> properties = new HashMap<String,Serializable>();
// here I would like to take the content from the fileFrom:
ContentFile contentFile = null;
// fileFrom is a File object:
String nameFile = fileFrom.getName();
documentFolderService.createDocument(folder, nameFile, properties, contentFile);
You should use the ContentFileImpl class:
//...
String location = "..."; // wherever you have to point to
ContentFile contentFile = new ContentFileImpl(new File(location));
//...