2016-12-01 1 views
2

Ich habe versucht, das Android MediaPlayer Framework zu verwenden, um eine mp3 Datei (see this question) zu spielen.Lautstärke kann nicht eingestellt werden, die Lautstärkeregelung wird nicht an das System weitergeleitet

Nachdem ich es geschafft habe zu arbeiten, erkannte ich schnell, dass die Lautstärke hoch/runter Ereignisse von der Klasse javafxports.android.KeyEventProcessor gefangen und nie weitergeleitet werden. Ich habe versucht, das zu umgehen, aber ohne Erfolg.

Gibt es Mittel, um das Ereignis an das System zu versenden, wo es nicht erwischt wird?

Danke und Gruß, Daniel

+0

http://stackoverflow.com/questions/40562263/how-to-play-a-sound-on-android-using-gluon-javafx-project, https: // bitbucket .org/javafxports/android/issues/73/hardware-button-schlüsselereignisse-bericht – jns

+0

Wow! Was für eine großartige Antwort! Es gibt nur zwei Links, die darauf hindeuten, dass es nicht gelöst und nicht lösbar ist, oder was? Komm schon! Das ist Android, von dem wir sprechen, und es ist lösbar! - Siehe meine Antwort, ich werde zu diesem Thema liefern. – dzim

+0

Ich verstehe.Da Sie sowieso ständig Ihre eigenen Fragen beantworten, sind Sie nicht an Links interessiert, die einige Informationen zum Thema liefern könnten. – jns

Antwort

2

Während ich hasse meine eigenen Fragen ständig zu beantworten, finde ich eine Lösung, die nach ein paar Stunden mit dem Android-API zu spielen, durch einige Dokumentationen zu graben und so weiter.

Meine Lösung basiert teilweise auf der Antwort, gegeben von @ josé-pereda zum Thema "javafxports how to call android native Media Player".

ich eine Schnittstelle für die Aufgaben erstellt "Lautstärke +", "volumeDown" und "mute":

public interface NativeVolumeService { 
    void volumeUp(); 
    void volumeDown(); 
    void mute(); 
} 

Dann wird basierend auf der folgenden Antwort auf wie die system volume on Android zu setzen, Ich kam mit der folgenden Umsetzung auf 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; 

public class NativeVolumeServiceAndroid implements NativeVolumeService { 

    private static final Logger LOG = Logger.getLogger(NativeVolumeServiceAndroid.class.getName()); 

    private final AudioManager audioManager; 

    private final int maxVolume; 
    private int preMuteVolume = 0; 
    private int currentVolume = 0; 

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

    @Override 
    public void volumeUp() { 
     LOG.info("dispatch volume up event"); 
     KeyEvent event = new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_VOLUME_UP); 
     dispatchEvent(event, true, false); 
    } 

    @Override 
    public void volumeDown() { 
     LOG.info("dispatch volume down event"); 
     KeyEvent event = new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_VOLUME_DOWN); 
     dispatchEvent(event, false, false); 
    } 

    @Override 
    public void mute() { 
     LOG.info("dispatch volume mute event"); 
     KeyEvent event = new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_VOLUME_MUTE); 
     dispatchEvent(event, false, true); 
    } 

    private void dispatchEvent(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); 
      } 
     } else if (up) { 
      audioManager.adjustStreamVolume(AudioManager.STREAM_MUSIC, 
        (currentVolume + 1) <= maxVolume ? AudioManager.ADJUST_RAISE : AudioManager.ADJUST_SAME, AudioManager.FLAG_SHOW_UI); 
     } else if (!up) { 
      audioManager.adjustStreamVolume(AudioManager.STREAM_MUSIC, 
        (currentVolume - 1) >= 0 ? AudioManager.ADJUST_LOWER : AudioManager.ADJUST_SAME, AudioManager.FLAG_SHOW_UI); 
     } 
    } 
} 

, um es zu integrieren, erstellte ich die approp riate Instanz in meinem Hauptklasse (Ich brauche diese global - Sie werden sehen, warum)

private void instantiateNativeVolumeService() { 
    String serviceName = NativeVolumeService.class.getName(); 
    if (Platform.isDesktop()) { 
     serviceName += "Desktop"; 
    } else if (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 eine Klasse variabel ist.

Dann registriert ich einen Event-Handler auf meinem Stage s Scene:

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

Und schließlich sieht das Verfahren handleGlobalKeyEvents wie folgt aus:

private void handleGlobalKeyEvents(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 much 
    if (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(); 
     } 
    } 
} 

Am Ende wird die Lösung so sauber ist wie es wird und nicht zu kompliziert. Nur der Weg bis es funktionierte war ein bisschen eklig.

@ JoséPereda: Wenn Sie diese Lösung als Charm Down-Plugin oder so integrieren möchten, bitte zögern Sie nicht, aber es wäre nett, erwähnt und benachrichtigt zu werden, wenn Sie dies tun.

Grüße, Daniel

+0

Das funktioniert super. Eine gute Lösung für ein Problem, das auf einer niedrigeren Ebene behoben werden sollte. –

Verwandte Themen