seaders
seaders

Reputation: 4096

Android - Play sound from assets without android.permission.WRITE_EXTERNAL_STORAGE

For our game, written in Adobe AIR, 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);

    Notification notification = new NotificationCompat.Builder(context)
        .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) {
            log(e.getLocalizedMessage());
            e.printStackTrace();
        }
    }
    else {
        newPath = fullPath;
    }

    return newPath;
}

Upvotes: 0

Views: 1295

Answers (1)

njzk2
njzk2

Reputation: 39406

If you want to avoid the android.permission.WRITE_EXTERNAL_STORAGE, you can extract you file to internal storage instead of external.

String fullPath = Environment.getExternalStorageDirectory() (...)

can be replaced by

String fullPath = context.getFilesDir() (...)

Upvotes: 1

Related Questions