Skip to content Skip to sidebar Skip to footer

Can't Set Volume, Volume Control Is Not Forwarded To The System

I tried to use the Android MediaPlayer framework to play a mp3 file (see this question). After I managed to make it work, I quickly recognized, that the volume up/down events are c

Solution 1:

While I hate to constantly answer my own questions, I found a solution after a couple of hours playing with the Android API, digging through some documentations and so on.

My solution is partially based the answer, given by @josé-pereda on the topic "javafxports how to call android native Media Player".

I created an interface for the tasks "volumeUp", "volumeDown" and "mute":

publicinterfaceNativeVolumeService {
    voidvolumeUp();
    voidvolumeDown();
    voidmute();
}

Then, based on the following answer on how to set the system volume on Android, I came up with the following implementation on Android:

import java.util.ArrayList;
import java.util.logging.Logger;

import android.content.Context;
import android.media.AudioManager;
import android.view.KeyEvent;
import android.view.View;
import android.view.ViewGroup;
import my.package.platform.NativeVolumeService;
import javafxports.android.FXActivity;

publicclassNativeVolumeServiceAndroidimplementsNativeVolumeService {

    privatestaticfinalLoggerLOG= Logger.getLogger(NativeVolumeServiceAndroid.class.getName());

    privatefinal AudioManager audioManager;

    privatefinalint maxVolume;
    privateintpreMuteVolume=0;
    privateintcurrentVolume=0;

    publicNativeVolumeServiceAndroid() {
        audioManager = (AudioManager) FXActivity.getInstance().getSystemService(Context.AUDIO_SERVICE);
        maxVolume = audioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC);
    }

    @OverridepublicvoidvolumeUp() {
        LOG.info("dispatch volume up event");
        KeyEventevent=newKeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_VOLUME_UP);
        dispatchEvent(event, true, false);
    }

    @OverridepublicvoidvolumeDown() {
        LOG.info("dispatch volume down event");
        KeyEventevent=newKeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_VOLUME_DOWN);
        dispatchEvent(event, false, false);
    }

    @Overridepublicvoidmute() {
        LOG.info("dispatch volume mute event");
        KeyEventevent=newKeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_VOLUME_MUTE);
        dispatchEvent(event, false, true);
    }

    privatevoiddispatchEvent(KeyEvent event, boolean up, boolean mute) {

        // hardware key events (amongst others) get caught by the JavaFXPorts engine (or better: the Dalvik impl from Oracle)// to circumvent this, we need to do the volume adjustment the hard way: via the AudioManager// see: https://developer.android.com/reference/android/media/AudioManager.html// see: https://stackoverflow.com/questions/9164347/setting-the-android-system-volume?rq=1// reason: // FXActivity registers a FXDalvikEntity, which etends the surface view and passing a key processor // called KeyEventProcessor - this one catches all key events and matches them to JavaFX representations.// Unfortunately, we cannot bypass this, so we need the AudioManager

        currentVolume = audioManager.getStreamVolume(AudioManager.STREAM_MUSIC);
        if (mute) {
            if (currentVolume > 0) {
                preMuteVolume = currentVolume;
                audioManager.adjustStreamVolume(AudioManager.STREAM_MUSIC, AudioManager.ADJUST_SAME,
                        AudioManager.FLAG_REMOVE_SOUND_AND_VIBRATE | AudioManager.FLAG_SHOW_UI);
            } else {
                preMuteVolume = 0;
                audioManager.adjustStreamVolume(AudioManager.STREAM_MUSIC, preMuteVolume, AudioManager.FLAG_SHOW_UI);
            }
        } elseif (up) {
            audioManager.adjustStreamVolume(AudioManager.STREAM_MUSIC,
                    (currentVolume + 1) <= maxVolume ? AudioManager.ADJUST_RAISE : AudioManager.ADJUST_SAME, AudioManager.FLAG_SHOW_UI);
        } elseif (!up) {
            audioManager.adjustStreamVolume(AudioManager.STREAM_MUSIC,
                    (currentVolume - 1) >= 0 ? AudioManager.ADJUST_LOWER : AudioManager.ADJUST_SAME, AudioManager.FLAG_SHOW_UI);
        }
    }
}

to integrate it, I created the appropriate instance in my main class (I need this globally - you will see, why)

privatevoidinstantiateNativeVolumeService() {
    String serviceName = NativeVolumeService.class.getName();
    if (Platform.isDesktop()) {
        serviceName += "Desktop";
    } elseif (Platform.isAndroid()) {
        serviceName += "Android";
    }
    try {
        volumeService = (NativeVolumeService) Class.forName(serviceName).newInstance();
    } catch (ClassNotFoundException | InstantiationException | IllegalAccessException e) {
        LOG.log(Level.SEVERE, "Could not get an instance of NativeAudioService for platform " + Platform.getCurrent(), e);
    }
}

volumeService is a class variable.

Then I registered an event handler on my Stages Scene:

@Overridepublicvoidstart(Stage stage)throws Exception {
    // initiate everything
    scene.addEventFilter(KeyEvent.ANY, this::handleGlobalKeyEvents);
    // do more stuff, if needed
}

And finally, the method handleGlobalKeyEvents looks like this:

privatevoidhandleGlobalKeyEvents(KeyEvent event) {
    // use a more specific key event type like// --> KeyEvent.KEY_RELEASED == event.getEventType()// --> KeyEvent.KEY_PRESSED == event.getEventType()// without it, we would react on both events, thus doing one operation too muchif (event.getCode().equals(KeyCode.VOLUME_UP) && KeyEvent.KEY_PRESSED == event.getEventType()) {
        if (volumeService != null) {
            volumeService.volumeUp();
            event.consume();
        }
    }
    if (event.getCode().equals(KeyCode.VOLUME_DOWN) && KeyEvent.KEY_PRESSED == event.getEventType()) {
        if (volumeService != null) {
            volumeService.volumeDown();
            event.consume();
        }
    }
}

In the end, the solution is as clean as it gets and not too complicated. Only the way until it worked was a bit nasty.

@JoséPereda: If you want to integrate this solution as a charm down plugin or so, please feel free, but it would be nice to be mentioned and notified, if you do.

Regards, Daniel

Post a Comment for "Can't Set Volume, Volume Control Is Not Forwarded To The System"