アクティビティで音量ボタンを聞く方法を知っています。しかし、バックグラウンドサービスでそれを行うことはできますか?はいの場合、それを行う方法は?
可能です。以下のコードを使用します(新しいAndroidバージョン、特にマシュマロについては、回答の下部を参照してください):
public class SettingsContentObserver extends ContentObserver {
int previousVolume;
Context context;
public SettingsContentObserver(Context c, Handler handler) {
super(handler);
context=c;
AudioManager audio = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
previousVolume = audio.getStreamVolume(AudioManager.STREAM_MUSIC);
}
@Override
public boolean deliverSelfNotifications() {
return super.deliverSelfNotifications();
}
@Override
public void onChange(boolean selfChange) {
super.onChange(selfChange);
AudioManager audio = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
int currentVolume = audio.getStreamVolume(AudioManager.STREAM_MUSIC);
int delta=previousVolume-currentVolume;
if(delta>0)
{
Logger.d("Ściszył!"); // volume decreased.
previousVolume=currentVolume;
}
else if(delta<0)
{
Logger.d("Zrobił głośniej!"); // volume increased.
previousVolume=currentVolume;
}
}
}
次に、サービスのonCreateで登録します。
mSettingsContentObserver = new SettingsContentObserver(this,new Handler());
getApplicationContext().getContentResolver().registerContentObserver(Android.provider.Settings.System.CONTENT_URI, true, mSettingsContentObserver );
次に、onDestroyで登録解除します。
getApplicationContext().getContentResolver().unregisterContentObserver(mSettingsContentObserver);
この例では、メディアのボリュームの変更によって判断することに注意してください。他のボリュームを使用する場合は、変更してください。
更新:
上記の方法はマシュマロでは機能しないと思われますが、MediaSessionが導入されて以来、はるかに良い方法があります!したがって、最初にコードをMediaController/MediaSessionパターンに移行してから、次のコードを使用する必要があります。
private VolumeProviderCompat myVolumeProvider = null;
myVolumeProvider = new VolumeProviderCompat(VolumeProviderCompat.VOLUME_CONTROL_RELATIVE, maxVolume, currentVolume) {
@Override
public void onAdjustVolume(int direction) {
// <0 volume down
// >0 volume up
}
};
mSession.setPlaybackToRemote(myVolumeProvider);
何らかの理由で、画面をオフにしても音量ボタンが押されたことが検出されます(プラットフォームに該当する場合は、適切なメディアボタンインテントレシーバーを必ず登録してください)
GalDudeがメディアMediaSession/MediaControllerの取得に関する追加情報を要求したため、更新2。申し訳ありませんが、Javaの使用を停止したため、Kotlinになります:
lateinit var mediaSession: MediaSessionCompat // you have to initialize it in your onCreate method
val kontroler: MediaControllerCompat
get() = mediaSession.controller // in Java it's just getController() on mediaSession
// in your onCreate/start method:
mediaSession = MediaSessionCompat(this, "YourPlayerName", receiver, null)
mediaSession.setFlags(MediaSessionCompat.FLAG_HANDLES_MEDIA_BUTTONS or MediaSessionCompat.FLAG_HANDLES_TRANSPORT_CONTROLS)
mediaSession.isActive = true
if (ratingIsWorking) // note: rating crashes on some machines you have to check it!
mediaSession.setRatingType(RatingCompat.RATING_5_STARS)
mediaSession.setCallback(object : MediaSessionCompat.Callback() {
...
// here you have to implement what happens with your player when play/pause/stop/ffw etc. is requested - see exaples elsewhere
})
// onDestroy/exit method:
mediaSession.isActive = false
mediaSession.release()
AOSPミュージックアプリには、BroadcastReceiverを登録することでボリュームキーイベントに応答するサービス( MediaPlaybackService )があります( MediaButtonIntentReceiver )。
以下に、レシーバーを登録するコードスニペットを示します。
mAudioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
ComponentName rec = new ComponentName(getPackageName(),
MediaButtonIntentReceiver.class.getName());
mAudioManager.registerMediaButtonEventReceiver(rec);
また、マニフェストについても忘れないでください。
<receiver Android:name="com.Android.music.MediaButtonIntentReceiver">
<intent-filter>
<action Android:name="Android.intent.action.MEDIA_BUTTON" />
<action Android:name="Android.media.AUDIO_BECOMING_NOISY" />
</intent-filter>
</receiver>
これは、音楽アプリがフォアグラウンドにない場合でも機能します。それはあなたが望むものではありませんか?
MediaSession
を使用して、Android 5+デバイスで動作するようにできました。ただし、@ ssuukkによって提案されたContentObserver
は、両方の4.4で動作しませんでした(少なくとも私がテストしてきたROMでは)7.0デバイス。Android 5+。
サービス:
import Android.app.Service;
import Android.content.Intent;
import Android.os.IBinder;
import Android.support.v4.media.VolumeProviderCompat;
import Android.support.v4.media.session.MediaSessionCompat;
import Android.support.v4.media.session.PlaybackStateCompat;
public class PlayerService extends Service {
private MediaSessionCompat mediaSession;
@Override
public void onCreate() {
super.onCreate();
mediaSession = new MediaSessionCompat(this, "PlayerService");
mediaSession.setFlags(MediaSessionCompat.FLAG_HANDLES_MEDIA_BUTTONS |
MediaSessionCompat.FLAG_HANDLES_TRANSPORT_CONTROLS);
mediaSession.setPlaybackState(new PlaybackStateCompat.Builder()
.setState(PlaybackStateCompat.STATE_PLAYING, 0, 0) //you simulate a player which plays something.
.build());
//this will only work on Lollipop and up, see https://code.google.com/p/Android/issues/detail?id=224134
VolumeProviderCompat myVolumeProvider =
new VolumeProviderCompat(VolumeProviderCompat.VOLUME_CONTROL_RELATIVE, /*max volume*/100, /*initial volume level*/50) {
@Override
public void onAdjustVolume(int direction) {
/*
-1 -- volume down
1 -- volume up
0 -- volume button released
*/
}
};
mediaSession.setPlaybackToRemote(myVolumeProvider);
mediaSession.setActive(true);
}
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public void onDestroy() {
super.onDestroy();
mediaSession.release();
}
}
AndroidManifest.xml
::
<application ...>
...
<service Android:name=".PlayerService"/>
</application>
あなたの活動:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
...
startService(new Intent(this, PlayerService.class));
}
知っておくべきことがいくつかあります:
Settings->Applications
に移動し、アプリを見つけて、強制的に停止して音量ボタンを戻す必要があります。サービスから空白のサウンドを再生する必要があり、それからあなただけがボリュームの変化を聞くことができます。次は私のために働いた
private MediaPlayer mediaPlayer;
public MyService() {
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
........
mediaPlayer = MediaPlayer.create(this, R.raw.blank);
mediaPlayer.setLooping(true);
mediaPlayer.start();
.......
return START_STICKY;
}
@Override
public void onDestroy() {
mediaPlayer.stop();
mediaPlayer.release();
super.onDestroy();
}
int volumePrev = 0;
private BroadcastReceiver broadcastReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
if ("Android.media.VOLUME_CHANGED_ACTION".equals(intent.getAction())) {
int volume = intent.getIntExtra("Android.media.EXTRA_VOLUME_STREAM_VALUE",0);
Log.i(TAG, "volume = " + volume);
if (volumePrev < volume) {
Log.i(TAG, "You have pressed volume up button");
} else {
Log.i(TAG, "You have pressed volume down button");
}
volumePrev = volume;
}
}
};
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
.....
IntentFilter filter = new IntentFilter();
filter.addAction("Android.media.VOLUME_CHANGED_ACTION");
registerReceiver(broadcastReceiver, filter);
....
return START_STICKY;
}
@Override
public void onDestroy() {
.....
unregisterReceiver(broadcastReceiver);
.....
super.onDestroy();
}
注:これはアクティビティに対してのみ機能し、質問のとおりサービスでは機能しません。
コールバックが必要なコンテキストによっては、代替ソリューションが利用できる場合があります。
ボリュームボタンを検出できるようにするには、アクティビティでdispatchKeyEvent
関数をオーバーライドする必要があります。これが複数のアクティビティに存在するためには、後続のすべてのアクティビティによって拡張されるオーバーライドされた関数を含むスーパークラスを作成できます。
ボリュームアップ/ダウンキーの押下を検出するコードは次のとおりです。
// Over-ride this function to define what should happen when keys are pressed (e.g. Home button, Back button, etc.)
@Override
public boolean dispatchKeyEvent(KeyEvent event)
{
if (event.getAction() == KeyEvent.ACTION_DOWN)
{
switch (event.getKeyCode())
{
case KeyEvent.KEYCODE_VOLUME_UP:
// Volume up key detected
// Do something
return true;
case KeyEvent.KEYCODE_VOLUME_DOWN:
// Volume down key detected
// Do something
return true;
}
}
return super.dispatchKeyEvent(event);
}
私の目標は、サービスからシステムの音量を調整することでした。ただし、プレスで任意のアクションを実行できます。
public class VolumeKeyController {
private MediaSessionCompat mMediaSession;
private final Context mContext;
public VolumeKeyController(Context context) {
mContext = context;
}
private void createMediaSession() {
mMediaSession = new MediaSessionCompat(mContext, KeyUtil.log);
mMediaSession.setFlags(MediaSessionCompat.FLAG_HANDLES_MEDIA_BUTTONS |
MediaSessionCompat.FLAG_HANDLES_TRANSPORT_CONTROLS);
mMediaSession.setPlaybackState(new Builder()
.setState(PlaybackStateCompat.STATE_PLAYING, 0, 0)
.build());
mMediaSession.setPlaybackToRemote(getVolumeProvider());
mMediaSession.setActive(true);
}
private VolumeProviderCompat getVolumeProvider() {
final AudioManager audio = mContext.getSystemService(Context.AUDIO_SERVICE);
int STREAM_TYPE = AudioManager.STREAM_MUSIC;
int currentVolume = audio.getStreamVolume(STREAM_TYPE);
int maxVolume = audio.getStreamMaxVolume(STREAM_TYPE);
final int VOLUME_UP = 1;
final int VOLUME_DOWN = -1;
return new VolumeProviderCompat(VolumeProviderCompat.VOLUME_CONTROL_RELATIVE, maxVolume, currentVolume) {
@Override
public void onAdjustVolume(int direction) {
// Up = 1, Down = -1, Release = 0
// Replace with your action, if you don't want to adjust system volume
if (direction == VOLUME_UP) {
audio.adjustStreamVolume(STREAM_TYPE,
AudioManager.ADJUST_RAISE, AudioManager.FLAG_REMOVE_SOUND_AND_VIBRATE);
}
else if (direction == VOLUME_DOWN) {
audio.adjustStreamVolume(STREAM_TYPE,
AudioManager.ADJUST_LOWER, AudioManager.FLAG_REMOVE_SOUND_AND_VIBRATE);
}
setCurrentVolume(audio.getStreamVolume(STREAM_TYPE));
}
};
}
// Call when control needed, add a call to constructor if needed immediately
public void setActive(boolean active) {
if (mMediaSession != null) {
mMediaSession.setActive(active);
return;
}
createMediaSession();
}
// Call from Service's onDestroy method
public void destroy() {
if (mMediaSession != null) {
mMediaSession.release();
}
}
}
その場合、Androidはボリュームボタンとのやり取りに関するAPIを文書化しません。だから答えはノーだと思う…
残念ながら、これはAndroidの別の領域です。ここでは、「問題を解決する」ための5つの異なる方法がありますが、それらのほとんどはあまりうまくいきません。以下のさまざまなアプローチをすべてリストしてください。
MediaSession
(サービスから)デニス・クニアジェフによる回答: https://stackoverflow.com/a/43304591/2441655
欠点:
1。 Android SDK 21+(Android 9+)が必要です。
Android.media.VOLUME_CHANGED_ACTION
(サービスから)Nikhilの回答: https://stackoverflow.com/a/44040282/2441655
欠点:
1。 SDKの公式部分ではありません: https://stackoverflow.com/a/8974510/2441655
2。ボリュームキーの最初の押下を無視します(ボリュームバーのみを表示するため)。
3。 100%のときは音量アップキーを無視し、0%のときは音量ダウンキーを無視します。
ContentObserver
(サービスから)Ssuukkによる回答: https://stackoverflow.com/a/15292255/2441655 (最初の部分)
欠点:
1。 Androidの新しいバージョンでは機能しません: comment by dsemi
2。ボリュームキーの最初の押下を無視します(ボリュームバーのみを表示するため)。
3。 100%のときは音量アップキーを無視し、0%のときは音量ダウンキーを無視します。
AudioManager.registerMediaButtonEventReceiver
(サービスから)ジョーによる回答: https://stackoverflow.com/a/11510564/2441655
欠点:
1。ほとんどのROMでは動作しません: elguiによるコメント
onKeyDown
(アクティビティから)Dipaliによる回答: https://stackoverflow.com/a/21086563/2441655
欠点:
1。画面がオフの場合、別のアプリなどで機能しません。
dispatchKeyEvent
(アクティビティから)モーリス・ギャビンによる回答: https://stackoverflow.com/a/11462962/2441655
欠点:
1。画面がオフの場合、別のアプリなどでは機能しません。
私が現在使用しているソリューションは#1です。理由は次のとおりです。
他に何か見つかった場合、またはそれらのいくつかにさらに欠点を見つけた場合はお知らせください!
checkout アプリの音量と再生の制御 ...これは問題を解決するのに役立ちます...複数のアプリケーションがバックグラウンドからボタンの押下をリッスンしたい場合があり、これがKeyEventsのみを処理できる理由である可能性がありますキーを押すユーザーへのインターフェイスであるため、アクティビティによって。