Can't load sound files LibGdx - java

I would like to load files directly from expansion OBB file by using AssetManager. I implemented my own FileHandleResolver
public class CustomFileHandleResolver implements FileHandleResolver
{
#Override
public FileHandle resolve(String fileName) {
return new CustomFileHandle(fileName);
}
}
I set it to my AssetManager. I created my own FileHandle and I override read() function
#Override
public InputStream read()
{
InputStream input = null;
try {
input = GameInfo.expansionFile.getInputStream(file.getPath().replace('\\', '/'));
} catch (IOException e) {
e.printStackTrace();
}
return input;
}
It loads all the files like .PNG, .PACK, .FNT, except .OGG files, so I guess that all sound files won't be loaded. I'm getting this error:
com.badlogic.gdx.utils.GdxRuntimeException: com.badlogic.gdx.utils.GdxRuntimeException: Couldn't load dependencies of asset: SFx/button_click.ogg
And this error:
com.badlogic.gdx.utils.GdxRuntimeException: java.lang.ClassCastException: com.solidgamesstudio.threedefendersn.framework.CustomFileHandle cannot be cast to com.badlogic.gdx.backends.android.AndroidFileHandle
I read that zip can not be compressed. In 7zip I selected compression to "Store" so that it's not compressed at all, but still this problem occurs.
I traversed what is happening when files are being loaded and I found that AssetManager calls my CustomFileHandleResolver which creates CustomFileHandle. For every file that is not .OGG it calls InputStream read(). In this function it loads the file from the zip and it's fine. But as I said when it comes to loading .OGG it doesn't call this function. So it's not even trying yet to get the file from the zip. Question is, why .OGG file doesn't call InputStream read() in CustomFileHandle()?
UPDATE
I traversed more and I found out that it won't call InputStream read() because it can't create a Sound from FileHandle somehow. Clue to this is
CustomFileHandle cannot be cast to AndroidFileHandle
While to create a sound you need to pass fileHandle.
public Sound newSound (FileHandle fileHandle);
This is called from SoundLoader
#Override
public void loadAsync (AssetManager manager, String fileName, FileHandle file, SoundParameter parameter) {
sound = Gdx.audio.newSound(file);
}
And that soundLoader uses my CustomFileHandleResolver. I don't know if Sounds are handled differently then other types of files. But by default AssetManager uses
public class InternalFileHandleResolver implements FileHandleResolver {
#Override
public FileHandle resolve (String fileName) {
return Gdx.files.internal(fileName);
}
}
I can't get into Gdx.files.internal to see if there are any special handling for Sounds.
UPDATE
Further analysis give me clue that the main problem is this as mentioned before.
CustomFileHandle cannot be cast to AndroidFileHandle
I don't know why it's casting my FileHandle to AndroidFileHandle while loading OGG file. If it loads fine other type of files, that probably means it doesn't do casting for them. That means that OGG is special and it needs casting. Any clues?

I have not found a way to load sound files from the zip file. Problem is that AssetManager loads sound files differently than other file types. Problem was that it is casting FileHandle to AndroidFileHandle, and since CustomFileHandle extends FileHandle it's impossible to cast it to AndroidFileHandle. I found no way to go around this, because it's deeply rooted.
CustomFileHandle cannot be cast to AndroidFileHandle
In this situation I had to take out all sound files from the OBB file and put them together with the app. I created another instance of AssetManager just for loading sounds. So, sounds are loaded normally as you would with AssetManager and for any other type of file I used AssetManager that uses my own FileHandlerResolver which uses my own FileHandle class that returns a file from the zip. The only problem with this approach is that you are limited to having sounds files only up to 50 MB.

I solved this problem extracting the zip to a specific folder,
and then reading from that external folder.
The extraction of the zip is done by these methods:
public void extract(){
String packageName = getPackageName();
File root = Environment.getExternalStorageDirectory();
File expPath = new File(root.toString() + "/Android/obb/" + packageName);
if (expPath.exists()) {
String strMainPath = null;
try {
strMainPath = expPath + File.separator + "main."
+ getPackageManager().getPackageInfo(
getPackageName(), 0).versionCode + "."
+ packageName + ".obb";
Log.e("Extract File path", "===>"+strMainPath);
File f=new File(strMainPath);
if(f.exists()){
Log.e("Extract From File path", "===> not exist");
}
else
{
Log.e("Extract From File path", "===> exist");
}
String pathToExtract = Environment.getExternalStorageDirectory()+"/"+Cons.FOLDERNAME;
Log.e("Extract to path", "===>"+pathToExtract);
flag = extractZip(strMainPath,pathToExtract);
Log.e("After Extract Zip", "===>"+flag);
} catch (NameNotFoundException e) {
e.printStackTrace();
}
}
}
private boolean extractZip(String pathOfZip,String pathToExtract)
{
int BUFFER_SIZE = 1024;
int size;
byte[] buffer = new byte[BUFFER_SIZE];
try {
File f = new File(pathToExtract);
if(!f.isDirectory()) {
f.mkdirs();
}
ZipInputStream zin = new ZipInputStream(new BufferedInputStream(new FileInputStream(pathOfZip), BUFFER_SIZE));
fileNum=0;
try {
ZipEntry ze = null;
while ((ze = zin.getNextEntry()) != null) {
String path = pathToExtract +"/"+ ze.getName();
if (ze.isDirectory()) {
File unzipFile = new File(path);
if(!unzipFile.isDirectory()) {
unzipFile.mkdirs();
}
}
else {
updateFileNum();
FileOutputStream out = new FileOutputStream(path, false);
BufferedOutputStream fout = new BufferedOutputStream(out, BUFFER_SIZE);
try {
while ( (size = zin.read(buffer, 0, BUFFER_SIZE)) != -1 ) {
fout.write(buffer, 0, size);
}
zin.closeEntry();
}catch (Exception e) {
Log.e("Exception", "Unzip exception 1:" + e.toString());
}
finally {
fout.flush();
fout.close();
}
}
}
}catch (Exception e) {
Log.e("Exception", "Unzip exception2 :" + e.toString());
}
finally {
zin.close();
}
return true;
}
catch (Exception e) {
Log.e("Exception", "Unzip exception :" + e.toString());
}
return false;
}
Note: Extract it to .Android folder, otherwhise users will have direct acces to the assets. For example they will see the images in the Gallery app.

Well, I'm doing this currently. Whenever you need to get a real FileHandle in order for the sound loading mechanism to work (or in any other case were the casting to AndroidFileHandle is bothering you), unzip that file to a local directory and reuse it if needed:
public static FileHandle getRealFileHandle(String zipEntryPath, ZipFile zipFile) {
if (Gdx.files.local(zipEntryPath).exists()) {
return Gdx.files.local(zipEntryPath);
} else {
Gdx.app.log(TAG, "Unzipping file '" + zipEntryPath + "'...");
try {
FileHandle unzippedFile;
ZipEntry entry = zipFile.getEntry(zipEntryPath);
if (entry != null) {
unzippedFile = Gdx.files.local(zipEntryPath);
InputStream is = zipFile.getInputStream(entry);
byte[] buffer = new byte[65536];
int readLength;
while ((readLength = is.read(buffer)) >= 0) {
unzippedFile.writeBytes(buffer, 0, readLength, true);
}
return unzippedFile;
} else {
Gdx.app.error(TAG, "Entry '" + zipEntryPath + "' not found inside zip file.");
}
} catch (IOException ioe) {
Gdx.app.error(TAG, "A problem occurred while writing to the local file.");
}
}
return null;
}

Related

Android FileNotFoundException with getAssets

So the intention is simple, I want to copy the files from the APK's assets folder to the device storage. I achieved that with the below code and it was actually working,
public void copyAssets() {
AssetManager assetManager = getAssets();
String[] files = null;
try {
files = getAssets().list("sourcedir");
} catch (IOException e) {
Log.e("tag", "Failed to get asset file list.", e);
}
if (files != null) for (String filename : files) {
InputStream in = null;
OutputStream out = null;
try {
in = assetManager.open(filename);
File folder = new File(Environment.getExternalStorageDirectory() + File.separator + "TargetFol");
if (!folder.exists()) {
folder.mkdirs();
}
File outFile = new File(folder, filename);
out = new FileOutputStream(outFile);
copyFile(in, out);
} catch (IOException e) {
Log.e("tag", "Failed to copy asset file: " + filename, e);
} finally {
if (in != null) {
try {
in.close();
} catch (IOException e) {
// NOOP
}
}
if (out != null) {
try {
out.close();
} catch (IOException e) {
// NOOP
}
}
}
}
}
private void copyFile(InputStream in, OutputStream out) throws IOException {
byte[] buffer = new byte[1024];
int read;
while ((read = in.read(buffer)) != -1) {
out.write(buffer, 0, read);
}
}
getAssets().list("sourcedir") actually lists all the images in the sourcedir in the assets folder of the APK.
But when the method called it throws FileNotFoundException: myfile.png for few files only.
For example, I put 5 png in the sourcedir (1.png, 2.png, 3.png, myfile.png, zip.png) All files copied except the myfile.png
I verified that there is no different extension or whatsoever also I had analyzed the APK and all these 5 images are actually there in assets/sourcedir
LOGCAT:
E/tag: Failed to copy asset file: myfile.png
java.io.FileNotFoundException: myfile.png
at android.content.res.AssetManager.nativeOpenAsset(Native Method)
at android.content.res.AssetManager.open(AssetManager.java:833)
at android.content.res.AssetManager.open(AssetManager.java:810)
at org.pac.pac.act.copyAssets(act.java:97)
at org.pac.pac.act$2.run(act.java:78)
at java.lang.Thread.run(Thread.java:919)
2020-10-04 21:06:13.189 30767-30999/packagename E/tag: Failed to copy asset file: myfile2.png
java.io.FileNotFoundException: myfile2.png
at android.content.res.AssetManager.nativeOpenAsset(Native Method)
at android.content.res.AssetManager.open(AssetManager.java:833)
at android.content.res.AssetManager.open(AssetManager.java:810)
at org.pac.pac.act.copyAssets(act.java:97)
at org.pac.pac.act$2.run(act.java:78)
I added another one PNG with the name of myfile2.png and gives the same issue.
UPDATE:
So instead of getting all files I tried to add myfile.png in the inputstream but this also returns the same exception even though the file was exists at the assets/sourcedir folder.
Found the solution, it may help others too. So if you're getting multiple files from the subfolder in the assets directory, make sure to include the subfolder name while calling. In my case it was,
in = assetManager.open(filename);
So adding the subfolder name along with the filename fixed the issue.
in = assetManager.open("sourcedir/" + filename);
Thanks to #CommonsWare who let me know this mistake. Fun fact I copied this method from one StackOverflow's question with high upvotes and accepted as an answer but nobody mentioned this issue in the comment. Yeah, it's a simple mistake, my bad.

Image not overwriting on same name

I'm developing image editor app.. so each time the user have to save the image.
So first i inserted
String savedImageURL = MediaStore.Images.Media.insertImage(
getContentResolver(),
bitmap,
"Bird",
"Image of bird"
);
this code, but it creating new file instead of overwriting.
So i use another method
public String saveImage(String folderName, String imageName) {
String selectedOutputPath = "";
if (isSDCARDMounted()) {
File mediaStorageDir = new File(
Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES), folderName);
// Create a storage directory if it does not exist
if (!mediaStorageDir.exists()) {
if (!mediaStorageDir.mkdirs()) {
Log.d("PhotoEditorSDK", "Failed to create directory");
}
}
// Create a media file name
selectedOutputPath = mediaStorageDir.getPath() + File.separator + imageName;
Log.d("PhotoEditorSDK", "selected camera path " + selectedOutputPath);
File file = new File(selectedOutputPath);
try {
FileOutputStream out = new FileOutputStream(file,true);
if (parentView != null) {
parentView.setDrawingCacheEnabled(true);
parentView.getDrawingCache().compress(Bitmap.CompressFormat.JPEG, 80, out);
}
out.flush();
out.close();
} catch (Exception e) {
e.printStackTrace();
}
}
return selectedOutputPath;
}
But it also didn't work.
Does anyone know about overwrite a bitmap in the same name?
Pass false as 2nd argument, to set append to false, so that you will overwrite the existing file:
FileOutputStream out = new FileOutputStream(file,false);
Check out the constructor documentation:
here is your code:
public String saveImage(String folderName, String imageName) {
String selectedOutputPath = "";
if (isSDCARDMounted()) {
File mediaStorageDir = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES), folderName);
// Create a storage directory if it does not exist
if (!mediaStorageDir.exists()) {
if (!mediaStorageDir.mkdirs()) {
Log.d("PhotoEditorSDK", "Failed to create directory");
}
}
// Create a media file name
selectedOutputPath = mediaStorageDir.getPath() + File.separator + imageName;
Log.d("PhotoEditorSDK", "selected camera path " + selectedOutputPath);
File file = new File(selectedOutputPath);
if (file.exists())
{
try {
file.delete();
} catch (IOException e) {
e.printStackTrace();
}
}
try {
file.createNewFile();
FileOutputStream out = new FileOutputStream(file,false);
if (parentView != null) {
parentView.setDrawingCacheEnabled(true);
parentView.getDrawingCache().compress(Bitmap.CompressFormat.JPEG, 80, out);
}
out.flush();
out.close();
} catch (Exception e) {
e.printStackTrace();
}
}
return selectedOutputPath;
}
I also had this situation, but it turns out that this is not a problem with saving, but with displaying in ImageViev. I used Glide, and it turns out to be stored in the cache when outputting. And I did not change the name and path of the file. That is, I rewrote them. But Glide did not know this. He thought they were the same file. To fix this problem, I added the following
Glide.with(context)
.load(file)
.diskCacheStrategy(DiskCacheStrategy.NONE)
.skipMemoryCache(true)
.into(view)
If you also have this situation and these solutions helped you, I'm glad to this.

Android File.exists() is always true

I'm trying to copy files from the assets folder to the device folder using this function:
public static void copyJSON(Context aContext) {
AssetManager assetManager = aContext.getResources().getAssets();
String[] pFiles = null;
try {
pFiles = assetManager.list("ConfigurationFiles");
} catch (IOException e) {
Log.e("tag", "Failed to get asset file list.", e);
}
if (pFiles != null) for (String pJsonFileName : pFiles) {
InputStream tIn = null;
OutputStream tOut = null;
try {
tIn = assetManager.open("ConfigurationFiles" + File.separator + pJsonFileName);
String[] pList = aContext.getFilesDir().list(); //just for test
File pOutFile = new File(aContext.getFilesDir(), pJsonFileName);
tOut = new FileOutputStream(pOutFile);
if (pOutFile.exists()) {
copyFile(tIn, tOut);
}
} catch (IOException e) {
Log.e("tag", "Failed to copy asset file: " + pJsonFileName, e);
} finally {
if (tIn != null) {
try {
tIn.close();
} catch (IOException e) {
Log.e("tag", "Fail closing", e);
}
}
if (tOut != null) {
try {
tOut.close();
} catch (IOException e) {
Log.e("tag", "Fail closing", e);
}
}
}
}
}
If I delete the App and run the code, the variable pList is empty as I expect but the pOutFile.exists()returns true ALWAYS!!.
I don't want to copy them again every time I open my App, and I'm doing this because all my app uses JSON to navigate thru all the screens, so If I change any value in my BBDD a WS send a new JSON file and the App respond in accordance for example a button is no longer needed, so the first time you download my App I copy the original JSON and then if you use the app an if you have internet connection you will download a new JSON file that it is more accurate than the one that is in the Bundle and it will be override, this is because as far as I know I can't change the files that are in the assets folder.
I have read everywhere and all say the same use this:
File pOutFile = new File(aContext.getFilesDir(), pJsonFileName);
And then ask for this:
pOutFile.exists()
I don't know what I'm doing wrong.
Thanks for all your help.
put it this way:
File pOutFile = new File(aContext.getFilesDir(), pJsonFileName);
if (pOutFile.exists()) {
tOut = new FileOutputStream(pOutFile);
copyFile(tIn, tOut);
}
and everything should work fine. Remember the FileOutputStream creates the file it should stream to if possible and non existing
The problem is you're essentially creating a file and then checking if it exists.
try {
tIn = assetManager.open("ConfigurationFiles" + File.separator + pJsonFileName);
String[] pList = aContext.getFilesDir().list(); //just for test
File pOutFile = new File(aContext.getFilesDir(), pJsonFileName);
// See here: you're creating a file right here
tOut = new FileOutputStream(pOutFile);
// And that file will be created in the exact location of the file
// you're trying to check:
if (pOutFile.exists()) { // Will always be true if FileOutputStream was successful
copyFile(tIn, tOut);
}
}
You should instead create your FileOutputStream AFTER you've done your existence check.
Source: http://docs.oracle.com/javase/7/docs/api/java/io/FileOutputStream.html
A file that you have just created without getting an exception always exists. The test is pointless. Remove it.

Reading a file from a Res folder

I have put a file "template.html" inside RAW folder and I want to read it into a InputStream. But it is returning me null. Can't understand what is wrong in the below code
e.g. fileName passed as parameter is "res/raw/testtemplate.html"
public String getFile(String fileName) {
InputStream input = this.getClass().getClassLoader().getResourceAsStream(fileName);
return getStringFromInputStream(input);
}
Also, there might be a better solution by putting these files in a particular subfolder and putting it inside Asset folder but then I believe I would need to pass context in AssetManager. I don't understand that solution, sorry I am new to android development. Can someone shed some light regarding how this approach can be achieved.
EDIT
I have started implementing this solution with Assets. Below method is supposed to return a string containing the entire text of the file stored as template.html.
getFile("template.html") // I am sending extension this time
Problem getting error getAssets() is undefined.
public String getFile(String fileName) {
BufferedReader reader = null;
StringBuilder sb = new StringBuilder();
String line;
try {
reader = new BufferedReader(new InputStreamReader(getAssets().open(fileName)));
while ((line = reader.readLine()) != null) {
sb.append(line);
}
}
catch (IOException e) {
e.printStackTrace();
} finally {
if (reader != null) {
try {
reader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return sb.toString();
}
use this
new BufferedInputStream(getResources().openRawResource(filepath));
this will return a buffered input stream
The file name should be without extension :
InputStream ins = getResources().openRawResource(
getResources().getIdentifier("raw/FILENAME_WITHOUT_EXTENSION",
"raw", getPackageName()));
For this purposes uses assets folder:
assets/
This is empty. 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.
So, you could easy get access at assets with context: context.getAssets()
BufferedReader reader = null;
try {
reader = new BufferedReader(
new InputStreamReader(context.getAssets().open("filename.txt")));
}
} catch (IOException e) {
//log the exception
} finally {
if (reader != null) {
try {
reader.close();
} catch (IOException e) {
//log the exception
}
}
}

java.io.FileNotFoundException: This file can not be opened as a file descriptor; it is probably compressed

i am programming a soundboard from android.
the problem is that some sounds works, and some dont work.
here is the traceback that i get for the sounds that doesnt work
05-31 13:23:04.227 18440 18603 W System.err: java.io.FileNotFoundException: This file can not be opened as a file descriptor; it is probably compressed
05-31 13:23:04.227 18440 18603 W System.err: at android.content.res.AssetManager.openAssetFd(Native Method)
05-31 13:23:04.227 18440 18603 W System.err: at android.content.res.AssetManager.openFd(AssetManager.java:331)
05-31 13:23:04.227 18440 18603 W System.err: at com.phonegap.AudioPlayer.startPlaying(AudioPlayer.java:201)
05-31 13:23:04.227 18440 18603 W System.err: at com.phonegap.AudioHandler.startPlayingAudio(AudioHandler.java:181)
05-31 13:23:04.235 18440 18603 W System.err: at com.phonegap.AudioHandler.execute(AudioHandler.java:64)
05-31 13:23:04.235 18440 18603 W System.err: at com.phonegap.api.PluginManager$1.run(PluginManager.java:86)
05-31 13:23:04.235 18440 18603 W System.err: at java.lang.Thread.run(Thread.java:1096)
any ideas?
You can disable asset compression for certain extensions like so:
android {
aaptOptions {
noCompress "pdf"
}
}
Source
People working with Tensorflow Lite file running into this issue,
Add the following lines to your Gradle file (android/app/build.gradle) inside the android{} block.
aaptOptions {
noCompress "tflite"
}
There is a limitations on opening compressed files in the assets folder. This is because uncompressed files can be directly memory mapped into the processes virtual address space, therefore avoiding needing the same amount of memory again for decompression.
Dealing with Asset Compression in Android Apps discusses some techniques in dealing with compressed files. You can trick aapt into not compressing the file by using an extension that is not compressed (e.g. mp3) or you can manually add them to the apk without compression instead of getting aapt to do the work.
You should disable compression for that file. Simply add:
aaptOptions {
noCompress "your-file-name"
}
To your app level build.gradle file inside android { }
This decidedly irritating situation comes about because when the .apk is built, some assets are compressed before storing them, whereas other are treated as already compressed (e.g. images, video) and are left alone. The latter group can be opened using openAssetFd, the former group can't - if you try, you get the "This file can not be opened as a file descriptor; it is probably compressed" error.
One option is to trick the build system into not compressing the assets (see the link in #nicstrong's answer), but this is fiddly. Better to try and work around the problem in a more predictable fashion.
The solution I cam up with uses the fact that while you can't open an AssetFileDescriptor for the asset, you can still open an InputStream. You can use this to copy the asset into the application's file cache, and then return a descriptor for that:
#Override
public AssetFileDescriptor openAssetFile(final Uri uri, final String mode) throws FileNotFoundException
{
final String assetPath = uri.getLastPathSegment(); // or whatever
try
{
final boolean canBeReadDirectlyFromAssets = ... // if your asset going to be compressed?
if (canBeReadDirectlyFromAssets)
{
return getContext().getAssets().openFd(assetPath);
}
else
{
final File cacheFile = new File(getContext().getCacheDir(), assetPath);
cacheFile.getParentFile().mkdirs();
copyToCacheFile(assetPath, cacheFile);
return new AssetFileDescriptor(ParcelFileDescriptor.open(cacheFile, MODE_READ_ONLY), 0, -1);
}
}
catch (FileNotFoundException ex)
{
throw ex;
}
catch (IOException ex)
{
throw new FileNotFoundException(ex.getMessage());
}
}
private void copyToCacheFile(final String assetPath, final File cacheFile) throws IOException
{
final InputStream inputStream = getContext().getAssets().open(assetPath, ACCESS_BUFFER);
try
{
final FileOutputStream fileOutputStream = new FileOutputStream(cacheFile, false);
try
{
//using Guava IO lib to copy the streams, but could also do it manually
ByteStreams.copy(inputStream, fileOutputStream);
}
finally
{
fileOutputStream.close();
}
}
finally
{
inputStream.close();
}
}
This does mean that your app will leave cache files lying about, but that's fine. It also doesn't attempt to re-use existing cache files, which you may or may not care about.
You should get this exception only if trying to open the FileDesriptor. For just reading the file you can go the way through the InputStream (AssetManager.open("filename.ext")). This worked for me.
If you need the file size in advance, you need the FileDescriptor (and therefore an uncompressed file) to call its getLength() method, otherwise you have to read the whole stream to determine its size.
This exception can be thrown by calling:
final AssetFileDescriptor afd = activity.getAssets().openFd(path);
I fixed the problem by saving the file in res/raw directory instead of assets folder, then get the AssetFileDescriptor this way:
final AssetFileDescriptor afd = activity.getResources().openRawResourceFd(rawId);
Then the FileNotFoundException is gone, and the file is not compressed anymore.
I have done a walk around, I use:
ParcelFileDescriptor mFileDescriptor = context.getAssets().openFd(file).getParcelFileDescriptor();
But that return: java.io.FileNotFoundException: This file can not be opened as a file descriptor; it is probably compressed.
Instead of this implementation I open the file directly using functions form ParcelFileDescriptor.
private void openRenderer(Context context,String fileName) throws IOException {
File file= FileUtils.fileFromAsset(context, fileName);
ParcelFileDescriptor parcelFileDescriptor = ParcelFileDescriptor.open(file,ParcelFileDescriptor.MODE_READ_WRITE);
mPdfRenderer = new PdfRenderer(parcelFileDescriptor);
}`
public class FileUtils {
private FileUtils() {
}
public static File fileFromAsset(Context context, String assetName) throws IOException {
File outFile = new File(context.getCacheDir(), assetName );
copy(context.getAssets().open(assetName), outFile);
return outFile;
}
public static void copy(InputStream inputStream, File output) throws IOException {
FileOutputStream outputStream = null;
try {
outputStream = new FileOutputStream(output);
boolean read = false;
byte[] bytes = new byte[1024];
int read1;
while((read1 = inputStream.read(bytes)) != -1) {
outputStream.write(bytes, 0, read1);
}
} finally {
try {
if(inputStream != null) {
inputStream.close();
}
} finally {
if(outputStream != null) {
outputStream.close();
}
}
}
}
}
Just added the extension of my file like below in the build.gradle and solved my issue
android {
aaptOptions {
noCompress "tflite"
noCompress "txt"
noCompress "pdf"
}
}
If the file to be obtained from the assets folder is bigger than 1MB, then what has worked for me is to compress the file as a zip file, then unzip it before using it, and store it in the external storage uncompressed.
InputStream fileInputStream = getAssets().open("your_file.your_file_extension.zip");
unzipInputStream(fileInputStream, "your_folder_in_external_storage");
The unzipInputStream method I've used is this one:
public static void unzipInputStream(InputStream inputStream, String location)
{
try {
if ( !location.endsWith(File.separator) ) {
location += File.separator;
}
File f = new File(location);
if(!f.isDirectory()) {
f.mkdirs();
}
ZipInputStream zin = new ZipInputStream(new BufferedInputStream(inputStream, BUFFER_SIZE));
try {
ZipEntry ze;
while ((ze = zin.getNextEntry()) != null) {
String path = location + ze.getName();
File unzipFile = new File(path);
if (ze.isDirectory()) {
if(!unzipFile.isDirectory()) {
unzipFile.mkdirs();
}
} else {
createParentDirectoriesIfMissing(unzipFile);
unzipFile(zin, unzipFile);
}
}
} finally {
zin.close();
}
} catch (Exception e) {
Log.e("", "Unzip exception", e);
}
}
private static void createParentDirectoriesIfMissing(File unzipFile)
{
File parentDir = unzipFile.getParentFile();
if ( null != parentDir ) {
if ( !parentDir.isDirectory() ) {
parentDir.mkdirs();
}
}
}
private static void unzipFile(ZipInputStream zin, File unzipFile) throws IOException
{
int size;
byte[] buffer = new byte[BUFFER_SIZE];
FileOutputStream out = new FileOutputStream(unzipFile, false);
BufferedOutputStream fout = new BufferedOutputStream(out, BUFFER_SIZE);
try {
while ( (size = zin.read(buffer, 0, BUFFER_SIZE)) != -1 ) {
fout.write(buffer, 0, size);
}
zin.closeEntry();
} finally {
fout.flush();
fout.close();
}
}
I just ran into the same problem, because the gnome-sound-recorder creates OGG files, which I can't play using the MediaPlayer. So I converted them to MP3 with ffmpeg and it worked. So I guess this is the easiest way.
ffmpeg -i youroggfile yournewfile.mp3
What I also noticed is that it does still show up with a question mark in the resources and that when I access it with R.raw.yournewfile I do not write the ".mp3" extension in the code.
I got the same issue, and I copy the file from res/raw to /data/data/your.app.pkg/cache folder, then everything go well :D
AssetFileDescriptor afd = null;
try {
File cache = new File(getCacheDir(), "my_data.dat");
if (!cache.exists()) {
copyInputStreamToFile(getResources().openRawResource(R.raw.my_data), cache);
}
afd = new AssetFileDescriptor(ParcelFileDescriptor.open(cache, ParcelFileDescriptor.MODE_READ_ONLY), 0, AssetFileDescriptor.UNKNOWN_LENGTH);
} catch (Exception e) {
e.printStackTrace();
}
private void copyInputStreamToFile(InputStream in, File file) {
BufferedOutputStream bfos = null;
try {
bfos = new BufferedOutputStream(new FileOutputStream(file));
byte[] buf = new byte[4096];
int len;
while ((len = in.read(buf)) != -1) {
bfos.write(buf, 0, len);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (bfos != null) {
bfos.close();
}
in.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
In my case, it caused by resource.arsc is compressed. Rebuilding with uncompressed resource.arsc solve the problem.

Categories