2016-12-01 74 views
2

我试图使用Android MediaPlayer框架播放mp3文件 (see this question)。无法设置音量,音量控制未转发至系统

当我设法使它工作之后,我很快意识到,音量增加/减少事件被类javafxports.android.KeyEventProcessor捕获并永远不会被转发。我试图绕过这一点,但无济于事。

有没有办法将事件发送到系统中不是被抓到了?

感谢和问候, 丹尼尔

+0

http://stackoverflow.com/questions/40562263/how-to-play-a-sound-on-android-using-gluon-javafx-project,https://开头到位桶.org/javafxports/android/issues/73/hardware-button-key-events-report – jns

+0

哇!多好的回应!只是提供两个链接,似乎表明它没有解决和不可解决,或什么?来吧!这是我们正在谈论的Android,它是可以解决的! - 看到我的答案,我将提供关于这个话题。 – dzim

+0

我明白了。既然你总是不断回答你自己的问题,你不会对可能提供有关该主题信息的链接感兴趣。 – jns

回答

2

虽然我讨厌不断地回答我自己的问题,我发现了几个小时的Android API上场后的解决方案,通过一些单证等挖掘。

我的解决方案部分基于@josé-pereda给出的关于主题"javafxports how to call android native Media Player"的答案。

我创建了一个接口的任务 “volumeUp”, “volumeDown” 和 “静音”:

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

然后,基于如何设置system volume on Android以下答案,我想出了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); 
     } 
    } 
} 

要整合它,我创建了approp在我的主类riate实例(我需要这个全球性 - 你会看到,为什么)

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是一个类变量。

于是我注册了一个事件处理程序上我Stage小号Scene

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

最后,该方法handleGlobalKeyEvents看起来是这样的:

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(); 
     } 
    } 
} 

最后,该解决方案是为清洁它变得并不复杂。只有这样,直到它的工作有点讨厌。

@JoséPereda:如果你想把这个解决方案作为一个魅力下拉插件,那么请尽情享受,但如果你愿意的话,它会很高兴被提及和通知。

问候, 丹尼尔

+0

这很好。对于应该在较低级别修复的问题,很好的解决方法。 –