I'm following all official expansion files guides, but i can't find it. I'm unable to access the contained obb file i need.
I need 6 audio files (80Mb) that i "stored" (uncompressed) in a zip file and renamed as 'main.2001.test.expansion.proj.obb' and stored in '/mnt/sdcard/Android/obb/test.expansion.proj/'
I'll try to access to the files
String mainFileName = Helpers.getExpansionAPKFileName(this,true,2001);
if(!Helpers.doesFileExist(this, mainFileName, 27959282L, false))
{
//download
} else {
Log.d("test_file","file exist");
}
ZipResourceFile expansionFile = APKExpansionSupport.getAPKExpansionZipFile(this,2001,2001);
if(expansionFile!=null)
{
ZipEntryRO[] ziro = expansionFile.getAllEntries();
for (ZipEntryRO entry : ziro) {
Log.d("test_files_zip", "fileZip filename: "+entry.getZipFileName());
try{
AssetFileDescriptor ro = entry.getAssetFileDescriptor();
Log.d("test_files_zip", "--fileZip getfiledescriptor.tostring: "+ro.getFileDescriptor().toString());
Log.d("test_files_zip", "--fileZip createinputstring.tostring: "+ro.createInputStream().toString());
AssetFileDescriptor assetFileDescriptor = expansionFile.getAssetFileDescriptor(entry.getZipFileName()+"/audio02.mp3");
if(assetFileDescriptor!=null) {
Log.d("test_files_mp3", "length: "+assetFileDescriptor.getLength()); //checking it exists
}
}catch (IOException e){ Log.e("test_exp","IoExcp: "+e.getMessage()); }
}
}
In -> assetFileDescriptor = expansionFile.getAssetFileDescriptor(.....); i've tried all i figuret out and found in different places, but i was unable to take the file.
Is there any way to get the file from its name if it's inside the zip?
The app should play these files in an specificorder and we don't want to unzip the files and make them ""public"".
Edited. Answer to myself
Found, it was a line i didn't understand when i firstly read it or i simply miss it.
ZipResourceFile expansionFile = APKExpansionSupport.getAPKExpansionZipFile(this,2001,2001);
if(expansionFile!=null){
FileDescriptor fd = expansionFile.getAssetFileDescriptor("audio_01.mp3");
//or
InputStream is = expansionFile.getInputStream("audio_01.mp3");
}
As Aarolama Bluenk suggested, here's the answer code (repited)
Found, it was a line i didn't understand when i firstly read it or i simply miss it.
ZipResourceFile expansionFile = APKExpansionSupport.getAPKExpansionZipFile(this,2001,2001);
if(expansionFile!=null){
FileDescriptor fd = expansionFile.getAssetFileDescriptor("audio_01.mp3");
//or
InputStream is = expansionFile.getInputStream("audio_01.mp3");
}
Related
I have been developing an App which need to play sounds and have large set of sounds files in .wav format that boast arounds near 1GB size. I have followed the official documentation for making the Expansion file.
First i made a .zip file with 0 compressions, all .wav files are under that files (without any subfolder. that is main.1.package contains all the sounds file).Then i renamed the .zip folder into .obb. After that i put that .obb file inside the apps folder under shared storage obb folder (com.moinul.app is my package name, so the folder name was it, also the expansion file were named main.1.com.moinul.app.obb )
Problem is when i try to read the files from my expansion files i get null pointer exceptions:
try
{
String param = (String) v.getTag();
ZipResourceFile expansionFile = APKExpansionSupport.getAPKExpansionZipFile(context, 1, 0);
AssetFileDescriptor fd = expansionFile.getAssetFileDescriptor("p25_1.wav");
MediaPlayer mPlayer = new MediaPlayer();
mPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
mPlayer.setDataSource(fd.getFileDescriptor());
mPlayer.prepare();
mPlayer.start();
}
catch (Exception e)
{
Log.w(TAG, e.getMessage() + "");
e.printStackTrace();
}
I get exceptions saying that fd is null. Any help would be appreciated.
Thanks
Try use: AssetFileDescriptor fd = expansionFile.getAssetFileDescriptor("assets/p25_1.wav");
Try
`public class Test extends Activity {
#Override
protected void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
String TAG = "Test";
Context context = this;
try {
MediaPlayer mediaPlayer = new MediaPlayer();
ZipResourceFile expansionFile = APKExpansionSupport.getAPKExpansionZipFile(context, 1, 0);
AssetFileDescriptor assetFileDescriptor = expansionFile.getAssetFileDescriptor("assets/p25_1.wav");
if (assetFileDescriptor != null) {
final FileInputStream fis = assetFileDescriptor.createInputStream();
if (fis != null) {
mediaPlayer.setDataSource(fis.getFD());
fis.close();
mediaPlayer.prepare();
mediaPlayer.start();
} else {
Log.w(TAG, "/fis: null");
}
}
} catch (Exception e) {
Log.w(TAG, e.getMessage() + "");
}
}
}`
Ok so it worked finally. After changing into :
AssetFileDescriptor fd = expansionFile.getAssetFileDescriptor("Sounds/p25_1.wav");
Its working now. Its weird that i had to add Sounds, which was the folder name before compress (i actually compressed this folder and all .wav are under it). I think the compressor made another subfolder inside the .zip.
Anyway thanks for the help.
It looks like I can not play videos stored in my internal application directory.
I have videos stored in /data/data/my.package.org/files/
And I'm trying to play a file from there using
String fpath = "/data/data/my.package.org/files/video.mpg"
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setDataAndType(Uri.parse(fpath), "video/*");
But both Android default video player and some external videoplayer (MX player) say 'this video can not be played".
Whereas when I'm saving videos to SD card they are played fine.
Why is that?
Put your video in assets folder and use this code to play video with MediaPlayer
InputStream is = getResources().getAssets().open("video.mpg");
OR
Put your video in assets folder and...
String fpath = "/data/data/my.package.org/assets/video.mpg"
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setDataAndType(Uri.parse(fpath), "video/*");
put your video in the res\raw folder. Then copy it out to the external directories where apps like video player can read them. Only your app can read /data/data/blah....
if its in res\raw, then
typeName = sourceSink.Types.video.toString();
for (sourceSink.VideoFiles video: sourceSink.VideoFiles.values() ){
resourceName = video.toString();
fileName = resourceName + ".mp4";
resource = getResources().getIdentifier(resourceName, "raw", "com.invodo.allshareplay");
createExternalStoragePublicFile(typeName,fileName,resource);
}
and then you can use this to copy it:
void createExternalStoragePublicFile(String fType, String fname, int res ) {
// Create a path where we will place our picture in the user's
// public pictures directory. Note that you should be careful about
// what you place here, since the user often manages these files. For
// pictures and other media owned by the application, consider
// Context.getExternalMediaDir().
File path = null;
if (((fType.equals(sourceSink.Types.photo.toString())) || (fType.equals(sourceSink.Types.file.toString())) ) ){
path = Environment.getExternalStoragePublicDirectory(
Environment.DIRECTORY_PICTURES);
}
if (fType.equals(sourceSink.Types.music.toString())) {
path = Environment.getExternalStoragePublicDirectory(
Environment.DIRECTORY_MUSIC);
}
if (fType.equals(sourceSink.Types.video.toString())) {
path = Environment.getExternalStoragePublicDirectory(
Environment.DIRECTORY_MOVIES);
}
File file = new File(path, "/" + fname);
try {
// Make sure the Pictures directory exists.
path.mkdirs();
// Very simple code to copy a picture from the application's
// resource into the external file. Note that this code does
// no error checking, and assumes the picture is small (does not
// try to copy it in chunks). Note that if external storage is
// not currently mounted this will silently fail.
InputStream is = getResources().openRawResource(res);
OutputStream os = new FileOutputStream(file);
byte[] data = new byte[is.available()];
is.read(data);
os.write(data);
is.close();
os.close();
scanMedia(file);
} catch (IOException e) {
// Unable to create file, likely because external storage is
// not currently mounted.
Log.w("ExternalStorage", "Error writing " + file, e);
}
}
then it's easy - just do the second half of the answer I provided and write them out. /data/data/ folder is never going to be viewed by anything other than your app.
I know there are already a few questions like this on SO, but they relate to extracting the file before playing it.
In the Android docs here it explains that you can play files directly from a .zip file without extracting it.
Tip: If you're packaging media files into a ZIP, you can use media
playback calls on the files with offset and length controls (such as
MediaPlayer.setDataSource() and SoundPool.load()) without the need to
unpack your ZIP. In order for this to work, you must not perform
additional compression on the media files when creating the ZIP
packages. For example, when using the zip tool, you should use the -n
option to specify the file suffixes that should not be compressed:
zip -n .mp4;.ogg main_expansion media_files
I've made an (uncompressed) zip package, but I cannot figure out how to get from a ZipEntry to a FileDescriptor, and they don't explain any further. How do I get a FileDescriptor without unpacking the zip file?
You can use The APK Expansion Zip Library to do that even if you do not use APK Expansions in your app.
Follow this documentation to get the library: Using the APK Expansion Zip Library
Zip your sound files without compression using your favorite zip tool.
Use this code to load the music:
ZipResourceFile expansionFile = new ZipResourceFile("myZipFile.zip");
AssetFileDescriptor assetFileDescriptor = expansionFile.getAssetFileDescriptor("myMusic.mp3");
try {
mediaPlayer.setDataSource(assetFileDescriptor.getFileDescriptor());
mediaPlayer.prepare();
mediaPlayer.start();
}
catch (IOException e) {
// Handle exception
}
There aren't many recent solutions for this issue, so I was stuck on this, too – until I created a new instance of MediaPlayer in the function where I was trying to read the zip file. Suddenly playback started without problems. Now, instead, I'm passing my (global) MediaPlayer to the function like this: (Kotlin)
private fun preparePlayer(mp: MediaPlayer, position: Int) {
// Path of shared storage
val root: File = Environment.getExternalStorageDirectory()
Log.i("ROOT", root.toString())
// path of the zip file
val zipFilePath = File(root.absolutePath+"/Android/obb/MY_PACKAGE_NAME/MY_ZIP_FILE.zip")
// Is zip file recognized?
val zipFileExists = zipFilePath.exists()
Log.i("Does zip file exist?", zipFileExists.toString())
// Define the zip file as ZipResourceFile
val expansionFile = ZipResourceFile(zipFilePath.absolutePath)
// Your media in the zip file
val afd = expansionFile.getAssetFileDescriptor("track_01.mp3")
// val mp = MediaPlayer() // not necessary if you pass it as a function parameter
mp.setDataSource(afd.fileDescriptor, afd.startOffset, afd.length)
mp.prepare()
mp.start() // Music should start playing automatically
Maybe this can help someone else. Good luck!
try {
ZipFile zf= new ZipFile(filename);
ZipEntry ze = zip.getEntry(fileName);
if (ze!= null) {
InputStream in = zf.getInputStream(ze);
File f = File.createTempFile("_AUDIO_", ".wav");
FileOutputStream out = new FileOutputStream(f);
IOUtils.copy(in, out);
// play f
}
} catch (IOException e) {
}
possible duplicate question
If you want to use a media file from a zip without unzipping you have to add also start offset and length to setDataSource.
ZipResourceFile expansionFile = new ZipResourceFile("myZipFile.zip");
AssetFileDescriptor assetFileDescriptor = expansionFile.getAssetFileDescriptor("myMusic.mp3");
try {
mediaPlayer.setDataSource(assetFileDescriptor.getFileDescriptor(),
assetFileDescriptor.getStartOffset(),
assetFileDescriptor.getLength());
mediaPlayer.prepare();
mediaPlayer.start();
}
catch (IOException e) {
// Handle exception
}
You can create a custom MediaDataSource like this:
public class ZipMediaDataSource extends MediaDataSource {
private InputStream inputStream;
public ZipMediaDataSource(ZipFile file, ZipEntry zipEntry) throws IOException {
inputStream = file.getInputStream(zipEntry);
}
#Override
public int readAt(long position, byte[] buffer, int offset, int size) throws IOException {
return inputStream.read(buffer, offset, size);
}
#Override
public long getSize() throws IOException {
return inputStream.available();
}
#Override
public void close() throws IOException {
if (inputStream != null) {
inputStream.close();
inputStream = null;
}
}
}
usage is:
ZipMediaDataSource zipMediaDataSource = new ZipMediaDataSource(zipFile, zipEntry);
mediaPlayer.setDataSource(zipMediaDataSource);
Reading mp3 file from expansion file
I have create and expansion file with name
"main.1.com.example.app.obb"
which contains and audio file name "about_eng.mp3"
Now the issue i have written the following code to read and play the mp3 file
private final static String EXP_PATH = "/Android/obb/";
static String[] getAPKExpansionFiles(Context ctx, int mainVersion, int patchVersion) {
String packageName = ctx.getPackageName();
Vector<String> ret = new Vector<String>();
if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
// Build the full path to the app's expansion files
File root = Environment.getExternalStorageDirectory();
File expPath = new File(root.toString() + EXP_PATH + packageName);
// Check that expansion file path exists
if (expPath.exists()) {
if ( mainVersion > 0 ) {
String strMainPath = expPath + File.separator + "main." +
mainVersion + "." + packageName + ".obb";
File main = new File(strMainPath);
if ( main.isFile() ) {
ret.add(strMainPath);
}
}
if ( patchVersion > 0 ) {
String strPatchPath = expPath + File.separator + "patch." +
mainVersion + "." + packageName + ".obb";
File main = new File(strPatchPath);
if ( main.isFile() ) {
ret.add(strPatchPath);
}
}
}
}
String[] retArray = new String[ret.size()];
ret.toArray(retArray);
return retArray;
}
#Override
protected void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
// Get a ZipResourceFile representing a merger of both the main and patch files
try {
ZipResourceFile expansionFile = APKExpansionSupport.getAPKExpansionZipFile(this,1,1);
if(expansionFile!=null){
AssetFileDescriptor fd = expansionFile.getAssetFileDescriptor("about_eng.mp3");
//or
MediaPlayer mediaPlayer = new MediaPlayer();
mediaPlayer.setDataSource( fd.getFileDescriptor(),
fd.getStartOffset(),fd.getLength());
mediaPlayer.prepare();
mediaPlayer.start();
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
// Get an input stream for a known file inside the expansion file ZIPs
}
But it is always throwing exception at this line
mediaPlayer.setDataSource( fd.getFileDescriptor(),
fd.getStartOffset(),fd.getLength());
mediaPlayer.prepare();
because the variable fd is null.
Can any body help me to solve this
i have googled and found that we shold have to make .zip with 0% (No compression) that is mention in http://developer.android.com/google/play/expansion-files.html
Tip: If you're packaging media files into a ZIP, you can use media playback calls on the files with offset and length controls (such as MediaPlayer.setDataSource() and SoundPool.load()) without the need to unpack your ZIP. In order for this to work, you must not perform additional compression on the media files when creating the ZIP packages. For example, when using the zip tool, you should use the -n option to specify the file suffixes that should not be compressed:
zip -n .mp4;.ogg main_expansion media_files
so How to make 0% compression zip using winrar?
here see the compression method
so we should have to upload this zip in play store.
so you not need to use ZipHelper.java that is mentioned in other SO answer
just simply use
ZipResourceFile expansionFile=null;
try {
expansionFile = APKExpansionSupport.getAPKExpansionZipFile(getApplicationContext(),3,0);
AssetFileDescriptor fd = expansionFile.getAssetFileDescriptor("test.mp3");
MediaPlayer mPlayer = new MediaPlayer();
mPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
mPlayer.setDataSource(fd.getFileDescriptor(),fd.getStartOffset(),fd.getLength());
mPlayer.prepare();
mPlayer.start();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
Try to use expansionFile.getAssetFileDescriptor("main_expansion/about_eng.mp3").
If it doesn't work, call this:
ZipResourceFile.ZipEntryRO [] entries = expansionFile.getAllEntries();
and look at the file names:
entries[0].mFileName;.
That could be a hint how the path should look like.
I know that the question is old, but maybe it helps someone.
To read mp3, with mediaplayer, you can do it 3 ways
for example. my application package name is
com.bluewindsolution.android.sampleapp
then my expansion file should be
main.1.com.bluewindsolution.android.sampleapp.obb
in detail, you can followed in here
[main|patch].< expansion-version >.< package-name >.obb
From expansion you can get it via 3 ways:
via AssetFileDescriptor
// this one work with image file, media file
// Get a ZipResourceFile representing a specific expansion file
// mainContext, version no. of your main expansion, version no of your patch
ZipResourceFile expansionFile = APKExpansionSupport.getAPKExpansionZipFile(this, 1, 0);
AssetFileDescriptor afd_img1 = expansionFile.getAssetFileDescriptor("your_path/your_file.jpg");
AssetFileDescriptor afd_mpbg = expansionFile.getAssetFileDescriptor("your_path/your_file.mp3");
// to set image
ImageView imageView1 = (ImageView)findViewById(R.id.iv1);
imageView1.setImageBitmap(BitmapFactory.decodeFileDescriptor(afd_iv1.getFileDescriptor()));
// to set sound
try {
mpbg.setDataSource(afd_mpbg.getFileDescriptor(), afd_mpbg.getStartOffset(), afd_mpbg.getDeclaredLength());
mpbg.prepareAsync();
mpbg.start();
} catch (IllegalStateException e) {
Log.w("Error=====", "Failed to prepare media player", e);
}
via InputStream
// this one work with image file, media file
// Get a ZipResourceFile representing a specific expansion file
ZipResourceFile expansionFile = new ZipResourceFile(filePathToMyZip);
// Get an input stream for a known file inside the expansion file ZIPs
InputStream fileStream = expansionFile.getInputStream(pathToFileInsideZip);
via Uri
//this one can use in almost thing such as jpg, mp3, mp4, pdf, etc.
//you need to create new class to override APEZProvider
public class ZipFileContentProvider extends APEZProvider {
#Override
public String getAuthority() {
return "com.bluewindsolution.android.sampleapp";
}
}
//you need to add <provider> in AndroidManifest.xml
<provider
android:name="com.bluewindsolution.android.sampleapp.ZipFileContentProvider"
android:authorities="com.bluewindsolution.android.sampleapp"
android:exported="false"
android:multiprocess="true"
>
<meta-data android:name="mainVersion"
android:value="1"></meta-data>
</provider>
//after that you can use it any time by this code:
String filename2 = "image/i_commu_a_1_1_1_1.jpg"; // suppose you store put your file in directory in .obb
String uriString2 = "content://com.bluewindsolution.android.sampleapp" + File.separator + filename2;
Uri uri2 = Uri.parse(uriString2);
imageView2.setImageURI(uri2);
// Filename inside my expansion file
String filename = "video/v_do_1_1_1.mp4"; // suppose you store put your file in directory in .obb
String uriString = "content://com.bw.sample_extension_download_main" + File.separator + filename;
Uri uri = Uri.parse(uriString);
v1 = (VideoView)findViewById(R.id.videoView1);
v1.setVideoURI(uri);
v1.start();
I have been trying to get the URI path for an asset file.
uri = Uri.fromFile(new File("//assets/mydemo.txt"));
When I check if the file exists I see that file doesn't exist
File f = new File(filepath);
if (f.exists() == true) {
Log.e(TAG, "Valid :" + filepath);
} else {
Log.e(TAG, "InValid :" + filepath);
}
Can some one tell me how I can mention the absolute path for a file existing in the asset folder
There is no "absolute path for a file existing in the asset folder". The content of your project's assets/ folder are packaged in the APK file. Use an AssetManager object to get an InputStream on an asset.
For WebView, you can use the file Uri scheme in much the same way you would use a URL. The syntax for assets is file:///android_asset/... (note: three slashes) where the ellipsis is the path of the file from within the assets/ folder.
The correct url is:
file:///android_asset/RELATIVEPATH
where RELATIVEPATH is the path to your resource relative to the assets folder.
Note the 3 /'s in the scheme. Web view would not load any of my assets without the 3. I tried 2 as (previously) commented by CommonsWare and it wouldn't work. Then I looked at CommonsWare's source on github and noticed the extra forward slash.
This testing though was only done on the 1.6 Android emulator but I doubt its different on a real device or higher version.
EDIT: CommonsWare updated his answer to reflect this tiny change. So I've edited this so it still makes sense with his current answer.
Finally, I found a way to get the path of a file which is present in assets from this answer in Kotlin. Here we are copying the assets file to cache and getting the file path from that cache file.
#Throws(IOException::class)
fun getFileFromAssets(context: Context, fileName: String): File = File(context.cacheDir, fileName)
.also {
if (!it.exists()) {
it.outputStream().use { cache ->
context.assets.open(fileName).use { inputStream ->
inputStream.copyTo(cache)
}
}
}
}
Get the path to the file like:
val filePath = getFileFromAssets(context, "fileName.extension").absolutePath
Please try this code working fine
Uri imageUri = Uri.fromFile(new File("//android_asset/luc.jpeg"));
/* 2) Create a new Intent */
Intent imageEditorIntent = new AdobeImageIntent.Builder(this)
.setData(imageUri)
.build();
Be sure ,your assets folder put in correct position.
Works for WebView but seems to fail on URL.openStream(). So you need to distinguish file:// protocols and handle them via AssetManager as suggested.
Try this out, it works:
InputStream in_s =
getClass().getClassLoader().getResourceAsStream("TopBrands.xml");
If you get a Null Value Exception, try this (with class TopBrandData):
InputStream in_s1 =
TopBrandData.class.getResourceAsStream("/assets/TopBrands.xml");
InputStream is = getResources().getAssets().open("terms.txt");
String textfile = convertStreamToString(is);
public static String convertStreamToString(InputStream is)
throws IOException {
Writer writer = new StringWriter();
char[] buffer = new char[2048];
try {
Reader reader = new BufferedReader(new InputStreamReader(is, "UTF-8"));
int n;
while ((n = reader.read(buffer)) != -1) {
writer.write(buffer, 0, n);
}
} finally {
is.close();
}
String text = writer.toString();
return text;
}
try this :
Uri uri = Uri.parse("android.resource://"+getPackageName()+"/"+R.raw.cat);
I had did it and it worked
Yeah you can't access your drive folder from you android phone or emulator because your computer and android are two different OS.I would go for res folder of android because it has good resources management methods. Until and unless you have very good reason to put you file in assets folder. Instead You can do this
try {
Resources res = getResources();
InputStream in_s = res.openRawResource(R.raw.yourfile);
byte[] b = new byte[in_s.available()];
in_s.read(b);
String str = new String(b);
} catch (Exception e) {
Log.e(LOG_TAG, "File Reading Error", e);
}
If you are okay with not using assets folder and want to get a URI without storing it in another directory, you can use res/raw directory and create a helper function to get the URI from resID:
internal fun Context.getResourceUri(#AnyRes resourceId: Int): Uri =
Uri.Builder()
.scheme(ContentResolver.SCHEME_ANDROID_RESOURCE)
.authority(packageName)
.path(resourceId.toString())
.build()
Now if you have a mydemo.txt file under res/raw directory you can simply get the URI by calling the above helper method
context.getResourceUri(R.raw.mydemo)
Reference: https://stackoverflow.com/a/57719958
Worked for me Try this code
uri = Uri.fromFile(new File("//assets/testdemo.txt"));
String testfilepath = uri.getPath();
File f = new File(testfilepath);
if (f.exists() == true) {
Toast.makeText(getApplicationContext(),"valid :" + testfilepath, 2000).show();
} else {
Toast.makeText(getApplicationContext(),"invalid :" + testfilepath, 2000).show();
}