i'm currently trying to develop a package for a Flutter App, with Kotlin. My issue is that I need to provide the package with a config file, which should only be defined inside the main App. Since the config differs for the Dev and Prod environment, the app should pass through the path of the File via the Method Channel. The problem is that the package isn't able to access the assets folder of the calling application.
Path: "assets/config.json" (the root being the main application)
Steps I already tried:
Creating the file inside the res/raw & accessing the config file through a ressource id -> Kotlin gives me an "Unresolved reference" error, unless I create the file inside the packages res/raw
Instead of passing through the path, I tried passing through the content of the config & writing it into an empty temporary file. The code in Kotlin like this:
val config = File(applicationContext.filesDir,"config.json")
config.writeText(configContent)
-> This works, but it seems like a weird solution to the problem.
please let me know if I need to provide further information & thank you in advance!
edit:
The Java Method that is called during initialisation:
public static void createMultipleAccountPublicClientApplication(#NonNull final Context context,
#NonNull final File configFile,
#NonNull final IMultipleAccountApplicationCreatedListener listener)
Flutter assets aren't files - they are packaged up and only available through the rootBundle. So, if you want to make a file from a text asset, someone has to load the asset and write it to a file.
As your plugin user will be in charge of the asset, they will have to do the first part (and will end up with a String). The question arises of who should do the writing.
You could make the plugin user use path_provider to find the temporary directory and write it there and then pass you the file path. Eventually, down in the Java, you new File(theTempFilePath). Or they could pass the string to the Dart half of your plugin and you create the temp file in the same way.
It's probably more convenient if they pass your plugin the string, you pass that to the native side and have the native side create a temporary file and write the string there. (BTW, I assume we are talking about this config file: https://learn.microsoft.com/en-us/azure/active-directory/develop/msal-configuration#how-to-use-a-configuration-file )
See this answer for creating temporary files: Creating temporary files in Android
Note that there's actually no reason that your plugin user then needs to use an asset. They could, instead, just hard code the string in their code if the configuration never really changes.
There's an argument that as this is a JSON configuration file, you may not want to bother your user with the details of this JSON configuration file. You may want to default it in your Dart code (why not hard code it as a string, as above, if it never really changes) and then provide some methods to override particular values like the client id and the redirect uri, which may be the only things that users ever change in practice. So rather than making them supply a complete JSON file, they just give you those two strings and you plonk them into your default JSON. Maybe a version 2 feature :)
I have used the repo to make an android plugin for my unity game, wherein I reward the player with some stickers at each level. So basically Im successful in calling the add sticker function from the android plugin and can add the sticker pack according to the desired identifier and name. (Here the contents.json, is already in the aar.)
But since i now need to edit the contents.json (we cant modify .aar files) so i copy the assets file content checkout the content provider code https://pastebin.com/urhXTs7z
And now the contents.json is in my Android/data/packagename/files/
From Unity I deserialize and serialize that json and just add items to the sticker pack list. And then overwrite the contents.json if (Application.platform == RuntimePlatform.Android)
{
System.IO.File.WriteAllText(Application.persistentDataPath + "/contents.json", contents);
}
It works for the original contents.json but after updating the stickerpack, the new items are not read by the ReadContentFile function
But this doesnt work, the plugin just reads the original contents.json. (Because copy assets is called in OnCreate, I tried using sharepreferences of android to call the copyassets function only once. But then my app crashes.)
I found people with similar questions here : https://github.com/WhatsApp/stickers/issues/340
But im not able to figure out what to do.
I dont know even it is possible to handle stickers addition dynamically when the code is inside a plugin.
Please refer to the code in the pastebin link
Would love any suggestions to alternative methods to do this.
As Google is trying to enforcing apps to use SAF for storage access, I am trying to adapt my app to use SAF replacing java file io apis.
I have spent many hours study the SAF APIs (mainly DocumentFile and DocumentContract classes) but still have some difficulties.
First one is how to move a file to another directory? DocumentFile does have a method to rename a file, but it is just the display name of the file. How can I move a file to another folder, if it is a huge file which I don't want to copy it. Assume src and dst are on the same partition.
Second question is how to list child files efficiently. I checked the source code and found that DocumentFile.listFiles() impl only queries the child files with single projection [ID]. And later when I want to display the files in a list view with their names, the call to DocumentFile.getName() will trigger another query via content resolver for each file again. This is a huge impact on the performance of the code. Especially when I try to sort an array of DocumentFile by their names, 30+ files will cost 600+ms, which is far beyond the acceptable. I doubt whether I am using the correct API set. Could anyone point out a better way to list files with names (and other properties)?
Simple Storage is a library that simplify SAF across API levels. Suppose that you want to move a MP4 file from directory Video in external storage into directory Others in SD card. Let's assume that AAAA-BBBB as SD card's storage ID:
val source = DocumentFileCompat.fromSimplePath(context, basePath = "Video/Infinity War.mp4")
val targetFolder = DocumentFileCompat.fromSimplePath(context, storageId = "AAAA-BBBB", basePath = "Others")
// To move file:
source.moveFileTo(context, targetFolder, callback)
// To copy file:
source.copyFileTo(context, targetFolder, callback)
Using Eclipse, Android SDK.
I have a text file full of data that I need pulled in. (For now, it's the easier the way, eventually I'm going to need to scrape dynamic data from a URL, but for now I have the test data I need in this text file).
I've created a class to open this file, but no matter how I try to open it I keep geeting "file not found" exceptions.
I've tried putting my "data.txt" file in various relative paths (within my App):
- "/AppName/"
- "/AppName/src/com/example/appname/data.txt"
I've tried passing different relative paths. I've tried putting the text file in the same path of the .java class file that's trying to open it, and it still can't find it! What am I doing wrong?
What am I doing wrong?
You have two main options of where to store this file within your project directory: assets/ and res/raw/.
If you use assets/, you can call getAssets() on your Activity (or other Context), and on there call open() with the relative path within assets/ to get an InputStream on this file (e.g., assets/data.txt would be accessed via getAssets().open("data.txt")).
If you use res/raw/, you can call getResources() on your Activity (or other Context), and on there call openRawResource(), passing in the R.raw value based upon your filename (e.g., res/raw/data.txt would be accessed via getResources().openRawResource(R.raw.data)).
Use /res/raw directory. I read sound files from raw:
InputStream soundFile1 = context.getResources().openRawResource(soundOneId);
Where context is a base context of activity passed to the class.
http://developer.android.com/reference/android/content/Context.html
In the Android SDK documentation, all of the examples used with the #drawable/my_image xml syntax directly address images that are stored in the res/drawable directory in my project.
I am wondering if it is explicitly not okay to create a sub directory within the drawable directory.
For example, if I had the following directory layout:
res/drawable
-- sandwiches
-- tunaOnRye.png
-- hamAndSwiss.png
-- drinks
-- coldOne.png
-- hotTea.png
Could I reference the image of a tuna salad sandwich as #drawable/sandwiches/tunaOnRye
Or do I have to keep the hierarchy flat in the drawable directory.
No, the resources mechanism doesn't support subfolders in the drawable directory, so yes - you need to keep that hierarchy flat.
The directory layout you showed would result in none of the images being available.
From my own experiments it seems that having a subfolder with any items in it, within the res/drawable folder, will cause the resource compiler to fail -- preventing the R.java file from being generated correctly.
The workaround I'm using (and the one Android itself seems to favor) is to essentially substitute an underscore for a forward slash, so your structure would look something like this:
sandwich_tunaOnRye.png
sandwich_hamAndSwiss.png
drink_coldOne.png
drink_hotTea.png
The approach requires you to be meticulous in your naming and doesn't make it much easier to wrangle the files themselves (if you decided that drinks and sandwiches should really all be "food", you'd have to do a mass rename rather than simply moving them to the directory); but your programming logic's complexity doesn't suffer too badly compared to the folder structure equivalent.
This situation sucks indeed. Android is a mixed bag of wonderful and terrible design decisions. We can only hope for the latter portion to get weeded out with all due haste :)
Actually, on Android Studio it is possible. You can have nested resources as shown here :
There is also a plugin to group resources here.
I recommend to avoid this though.
Yes - it does suck :) However you can use the assets folder and have sub directories in there and load images that way.
Use assets folder.
sample code:
InputStream is = null;
try {
is = this.getResources().getAssets().open("test/sample.png");
} catch (IOException e) {
;
}
image = BitmapFactory.decodeStream(is);
I've wrote an eclipse plugin which allows to create virtual subfolder by separating the file name with two underscores __. The project is in early stages, but don't worry it won't crash your IDE
more details can be found here, feel free to fork and send pull requests:
https://github.com/kirill578/Android-Sorted-Res-Folder
I like to use a simple script to flatten an organized directory structure provided by designers to something that can be used to generate an R file.
Run with current path in drawable-hdpi:
#! /bin/bash
DIRS=`find * -type d`
for dir in ${DIRS} ; do
for file in `ls ${dir}` ; do
mv ${dir}/${file} ${dir}_${file};
done
rmdir ${dir};
done
In android studio with gradle you can have multiple source directors which will allow you to separate resources. For example:
android {
....
android.sourceSets {
main.res.srcDirs = ['src/main/extraresdirnamed_sandwiches', 'src/main/res']
}
....
}
However the names must not collide which means you will still need to have names such as sandwiches_tunaOnRye but you will be able to have a seperate section for all of your sandwiches.
This allows you to store your resources in different structures (useful for auto generated content such as actionbargenerator)
One way to partially get around the problem is to use the API Level suffix.
I use res/layout-v1, res/layout-v2 etc to hold multiple sub projects in the same apk.
This mechanism can be used for all resource types.
Obviously, this can only be used if you are targeting API levels above the res/layout-v? you are using.
Also, watch out for the bug in Android 1.5 and 1.6.
See Andoroid documentation about the API Level suffix.
With the advent of library system, creating a library per big set of assets could be a solution.
It is still problematic as one must avoid using the same names within all the assets but using a prefix scheme per library should help with that.
It's not as simple as being able to create folders but that helps keeping things sane...
There is a workaround for this situation: you can create a resVector (for example) folder on the same level as default res folder. There you can add any drawable-xxx resource folders there:
resVector
-drawable
-layout
-color
After that all you need is to add
sourceSets {
main.res.srcDirs += 'src/main/resVector'
}
into your build.gradle file (inside android { }).
This is not perfect methods. You have to implement same way which is display here.
You can also call the image under the folder through the code you can use
Resources res = getResources();
Drawable shape = res. getDrawable(R.drawable.gradient_box);
TextView tv = (TextView)findViewByID(R.id.textview);
tv.setBackground(shape);
Not mine but I found this thread when looking for this issue, if your using Android Studio and Gradle Build system its pretty easy no plugins necessary just a little build file editing
https://stackoverflow.com/a/22426467/618419
Gradle with Android Studio could do it this way (link).
It's in the paragraph "Configuring the Structure"
sourceSets {
main {
java {
srcDir 'src/java'
}
resources {
srcDir 'src/resources'
}
}
}
create a folder in main.
like: 'res_notification_btn'
and create tree folder in. like 'drawable' or 'layout'
then in 'build.gradle' add this
sourceSets
{
main
{
res
{
srcDirs = ['src/main/res_notification_btn', 'src/main/res']
or
srcDir 'src/main/res_notification_btn'
}
}
}
#!/usr/bin/env ruby
# current dir should be drawable-hdpi/ etc
# nuke all symlinks
Dir.foreach('.') {|f|
File.delete(f) if File.symlink?(f)
}
# symlink all resources renaming with underscores
Dir.glob("**/*.png") {|f|
system "ln -s #{f} #{f.gsub('/', '_')}" if f.include?("/")
}
Check Bash Flatten Folder script that converts folder hierarchy to a single folder
assets/
You can use it to store raw asset files. Files that you save here are compiled into an .apk file as-is, and the original filename is preserved. You can navigate this directory in the same way as a typical file system using URIs and read files as a stream of bytes using the AssetManager. For example, this is a good location for textures and game data.
http://developer.android.com/tools/projects/index.html
Subdirectories are not allowed, the resource must contain only [a-z0-9_.].
No you have uppercase letters, and no forward slashes.
It is possible to have multiple drawable folders by having an extra folder parallel to 'res' with a subdirectory 'drawable' and then add following to gradle:
sourceSets {
main {
res.srcDirs 'src/main/<extra_res>'
}
}
Tested with gradle 6.5.1
For anyone using Xamarin (either Xamarin.Android or Xamarin.Forms), there is a way to do this.
In the .csproj file for the Android project find the line for MonoAndroidResourcePrefix (documented, though rather poorly, here). Add the subdirectories you are wanting to use here, separating each entry by semicolons. When building, Visual Studio strips these prefixes so that all of the resources end up in a flat hierarchy. You may need to reload the solution after making these changes.
These directories do not need to be subdirectories of the default Resources directory in the project.
Make sure that files you add are getting the build action set to "AndroidResource".
For Xamarin.Android, the visual editor won't recognize images and will show the error "This resource URL cannot be resolved" but the project will build and the image will be visible at runtime.
Right click on Drawable
Select New ---> Directory
Enter the directory name. Eg: logo.png(the location will already show the drawable folder by default)
Copy and paste the images directly into the drawable folder. While pasting you get an option to choose mdpi/xhdpi/xxhdpi etc for each of the images from a list. Select the appropriate option and enter the name of the image. Make sure to keep the same name as the directory name i.e logo.png
Do the same for the remaining images. All of them will be placed under the logo.png main folder.