Highlighted

[Android] Playing custom asset sound in ANE without android.permission.WRITE_EXTERNAL_STORAGE

Community Beginner ,
Feb 26, 2014

Copy link to clipboard

Copied

For our game, we want one of the sounds in the game to be the notification sound when we display a local, or push notification, on Android.

Currently we have a solution we're not really happy that's below, because it requires extra Android permissions that we really feel is unnecessary.  Basically you pass down the asset path, say "audio/core/reward.mp3", which is in the root "assets" folder, and then on the Android side, it pulls that file out into an InputStream, writes that to a file on the external storage, and Android's MediaPlayer plays it from there.

Obviously this mustn't be what AIR is doing whenever you play a file on Android, because you don't need "WRITE_EXTERNAL_STORAGE" to play a sound, so for our circumstance, when the app's not currently actively running, but a Push Notification comes in, how would I be able to play this file, which is packaged and used on the AS3 side of things?

The recommended way of doing this is,

        MediaPlayer m = new MediaPlayer();

        AssetFileDescriptor afd = context.getAssets().openFd(soundPath);

        m.setDataSource(afd.getFileDescriptor(), afd.getStartOffset(), afd.getLength());

        m.prepare();

        m.setVolume(1f, 1f);

        m.start();

        playSound = false;

But that doesn't work with an error of

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

So I need to do the unpreferred method,

    public static void HandleMessage(Context context, CharSequence title, CharSequence message, String soundPath)

    {

        log("HandleMessage (message: '" + message + "', title: '" + title +"', icon: " + Extension.getIcon(context) + ", class: " + Extension.getEntryClass(context));

        long when = System.currentTimeMillis();

        NotificationManager nm = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);

        Intent notificationIntent = new Intent(context, Extension.getEntryClass(context));

        PendingIntent contentIntent = PendingIntent.getActivity(context, 0, notificationIntent, 0);

        NotificationCompat.Builder builder = new NotificationCompat.Builder(context);

        Notification notification = builder

            .setContentText(message)

            .setContentIntent(contentIntent)

            .setTicker(message)

            .setSmallIcon(Extension.getIcon(context))

            .setWhen(when)

            .setAutoCancel(true)

            .setVibrate(new long[] { 1000, 400 })

            .build();

        Boolean playSound = true;

        if(null != soundPath && "" != soundPath)

        {

            try {

                MediaPlayer m = new MediaPlayer();

                String newPath = checkWriteFile(context, soundPath);

                log(newPath);

                if(null != newPath)

                {

                    m.setDataSource(newPath);

                    m.prepare();

                    m.setVolume(1f, 1f);

                    m.start();

                    playSound = false;

                }

            } catch (IOException e) {

                log(e.getLocalizedMessage());

                e.printStackTrace();

            }

        }

        if(playSound)

        {

            notification.sound = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);

        }

        nm.notify(NotifId, notification);

        NotifId++;

    }

    public static String checkWriteFile(Context context, String relPath)

    {

        Boolean exists = false;

        String newPath = null;

        String fullPath = Environment.getExternalStorageDirectory().getPath() + "/" + context.getPackageName() + "/temp/" + relPath;

        int index = fullPath.lastIndexOf("/") + 1;

        String file = fullPath.substring(index);

        String path = fullPath.substring(0, index);

        log(relPath + ", " + fullPath + ", " + (new File(fullPath)).exists());

        if (!(new File(fullPath)).exists()) {

            try {

                byte[] buffer = null;

                InputStream fIn = context.getAssets().open(relPath);

                log("InputStream: " + fIn);

                int size=0;

                try {

                    size = fIn.available();

                    buffer = new byte[size];

                    fIn.read(buffer);

                    fIn.close();

                } catch (IOException e) {

                    e.printStackTrace();

                }

                exists = (new File(path)).exists();

                if (!exists) {

                    new File(path).mkdirs();

                }

                FileOutputStream save;

                try {

                    save = new FileOutputStream(path + file);

                    save.write(buffer);

                    save.flush();

                    save.close();

                    newPath = fullPath;

                } catch (FileNotFoundException e) {

                    e.printStackTrace();

                } catch (IOException e) {

                    e.printStackTrace();

                }

            }

            catch (Exception e) {

            }

        }

        else {

            newPath = fullPath;

        }

        return newPath;

    }

Message was edited by: seaders6m Add the not working AssetFileDescriptor method

TOPICS
Development

Views

1.2K

Likes

Translate

Translate

Report

Report
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more

[Android] Playing custom asset sound in ANE without android.permission.WRITE_EXTERNAL_STORAGE

Community Beginner ,
Feb 26, 2014

Copy link to clipboard

Copied

For our game, we want one of the sounds in the game to be the notification sound when we display a local, or push notification, on Android.

Currently we have a solution we're not really happy that's below, because it requires extra Android permissions that we really feel is unnecessary.  Basically you pass down the asset path, say "audio/core/reward.mp3", which is in the root "assets" folder, and then on the Android side, it pulls that file out into an InputStream, writes that to a file on the external storage, and Android's MediaPlayer plays it from there.

Obviously this mustn't be what AIR is doing whenever you play a file on Android, because you don't need "WRITE_EXTERNAL_STORAGE" to play a sound, so for our circumstance, when the app's not currently actively running, but a Push Notification comes in, how would I be able to play this file, which is packaged and used on the AS3 side of things?

The recommended way of doing this is,

        MediaPlayer m = new MediaPlayer();

        AssetFileDescriptor afd = context.getAssets().openFd(soundPath);

        m.setDataSource(afd.getFileDescriptor(), afd.getStartOffset(), afd.getLength());

        m.prepare();

        m.setVolume(1f, 1f);

        m.start();

        playSound = false;

But that doesn't work with an error of

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

So I need to do the unpreferred method,

    public static void HandleMessage(Context context, CharSequence title, CharSequence message, String soundPath)

    {

        log("HandleMessage (message: '" + message + "', title: '" + title +"', icon: " + Extension.getIcon(context) + ", class: " + Extension.getEntryClass(context));

        long when = System.currentTimeMillis();

        NotificationManager nm = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);

        Intent notificationIntent = new Intent(context, Extension.getEntryClass(context));

        PendingIntent contentIntent = PendingIntent.getActivity(context, 0, notificationIntent, 0);

        NotificationCompat.Builder builder = new NotificationCompat.Builder(context);

        Notification notification = builder

            .setContentText(message)

            .setContentIntent(contentIntent)

            .setTicker(message)

            .setSmallIcon(Extension.getIcon(context))

            .setWhen(when)

            .setAutoCancel(true)

            .setVibrate(new long[] { 1000, 400 })

            .build();

        Boolean playSound = true;

        if(null != soundPath && "" != soundPath)

        {

            try {

                MediaPlayer m = new MediaPlayer();

                String newPath = checkWriteFile(context, soundPath);

                log(newPath);

                if(null != newPath)

                {

                    m.setDataSource(newPath);

                    m.prepare();

                    m.setVolume(1f, 1f);

                    m.start();

                    playSound = false;

                }

            } catch (IOException e) {

                log(e.getLocalizedMessage());

                e.printStackTrace();

            }

        }

        if(playSound)

        {

            notification.sound = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);

        }

        nm.notify(NotifId, notification);

        NotifId++;

    }

    public static String checkWriteFile(Context context, String relPath)

    {

        Boolean exists = false;

        String newPath = null;

        String fullPath = Environment.getExternalStorageDirectory().getPath() + "/" + context.getPackageName() + "/temp/" + relPath;

        int index = fullPath.lastIndexOf("/") + 1;

        String file = fullPath.substring(index);

        String path = fullPath.substring(0, index);

        log(relPath + ", " + fullPath + ", " + (new File(fullPath)).exists());

        if (!(new File(fullPath)).exists()) {

            try {

                byte[] buffer = null;

                InputStream fIn = context.getAssets().open(relPath);

                log("InputStream: " + fIn);

                int size=0;

                try {

                    size = fIn.available();

                    buffer = new byte[size];

                    fIn.read(buffer);

                    fIn.close();

                } catch (IOException e) {

                    e.printStackTrace();

                }

                exists = (new File(path)).exists();

                if (!exists) {

                    new File(path).mkdirs();

                }

                FileOutputStream save;

                try {

                    save = new FileOutputStream(path + file);

                    save.write(buffer);

                    save.flush();

                    save.close();

                    newPath = fullPath;

                } catch (FileNotFoundException e) {

                    e.printStackTrace();

                } catch (IOException e) {

                    e.printStackTrace();

                }

            }

            catch (Exception e) {

            }

        }

        else {

            newPath = fullPath;

        }

        return newPath;

    }

Message was edited by: seaders6m Add the not working AssetFileDescriptor method

TOPICS
Development

Views

1.2K

Likes

Translate

Translate

Report

Report
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
Feb 26, 2014 0
Community Beginner ,
Feb 26, 2014

Copy link to clipboard

Copied

Feeling fairly dumb, you can just change to write to cache, or internal storage instead, changing the call to external to cache like,

        

        String fullPath = context.getCacheDir() + "/" + context.getPackageName() + "/temp/" + relPath;

sorts it all out.

Likes

Translate

Translate

Report

Report
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
Reply
Loading...
Feb 26, 2014 0
Engaged ,
Jun 10, 2014

Copy link to clipboard

Copied

Hi,

I think the problem here is actually something to do with the way AIR is packaging the assets. I've been investigating the issue trying to get access to video files packaged in an AIR app from an ANE but they are sometimes compressed in the APK rather than just being stored. Once they are compressed you get the error you've encountered:

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

I think this has to be an error in the AIR packaging, as they shouldn't be compressed. Any files like mp3, mp4, png etc shouldn't be compressed according to the Android packager.

air native extensions // https://airnativeextensions.com

Likes

Translate

Translate

Report

Report
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
Reply
Loading...
Jun 10, 2014 0