web-dev-qa-db-ja.com

バックグラウンドサービスの音量ボタンを聞きますか?

アクティビティで音量ボタンを聞く方法を知っています。しかし、バックグラウンドサービスでそれを行うことはできますか?はいの場合、それを行う方法は?

41
user942821

このトピックに関する他のいくつかの質問から判断すると、いいえ。

その他の質問1その他の質問2

サービスは単にKeyEventコールバックを受信しません。

10
Tanis.7x

可能です。以下のコードを使用します(新しい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()
29
ssuukk

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>

これは、音楽アプリがフォアグラウンドにない場合でも機能します。それはあなたが望むものではありませんか?

19
Joe

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に移動し、アプリを見つけて、強制的に停止して音量ボタンを戻す必要があります。
7
Denis Kniazhev

サービスから空白のサウンドを再生する必要があり、それからあなただけがボリュームの変化を聞くことができます。次は私のために働いた

手順

1. rawフォルダーにblank.mp3を配置します( here からダウンロード)

2. onStartCommand()でメディアを開始します

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

3. mediaplayerを停止してリリースすることを選択する必要があります。 onDestroy()で行う方が良い

@Override
public void onDestroy() {

    mediaPlayer.stop();
    mediaPlayer.release();

    super.onDestroy();
}

4.ボリュームの変更をリッスンするブロードキャストレシーバーを作成する

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

5. onStartCommand()でブロードキャストレシーバーを登録します。

@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;
}

6.ブロードキャストレシーバーをonDestroy()で登録解除します

 @Override
public void onDestroy() {
    .....

    unregisterReceiver(broadcastReceiver);

    .....

    super.onDestroy();
}

それで全部です

5
Bikram

注:これはアクティビティに対してのみ機能し、質問のとおりサービスでは機能しません。

コールバックが必要なコンテキストによっては、代替ソリューションが利用できる場合があります。

ボリュームボタンを検出できるようにするには、アクティビティで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);
    }
0
Maurice Gavin

これにはLollipop(v5.0/API 21)以降が必要です

私の目標は、サービスからシステムの音量を調整することでした。ただし、プレスで任意のアクションを実行できます。

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

その場合、Androidはボリュームボタンとのやり取りに関するAPIを文書化しません。だから答えはノーだと思う…

0
Lincoln Hawk

残念ながら、これはAndroidの別の領域です。ここでは、「問題を解決する」ための5つの異なる方法がありますが、それらのほとんどはあまりうまくいきません。以下のさまざまなアプローチをすべてリストしてください。

解決策

1)MediaSession(サービスから)

デニス・クニアジェフによる回答: https://stackoverflow.com/a/43304591/2441655

欠点:
1。 Android SDK 21+(Android 9+)が必要です。

2)Android.media.VOLUME_CHANGED_ACTION(サービスから)

Nikhilの回答: https://stackoverflow.com/a/44040282/2441655

欠点:
1。 SDKの公式部分ではありません: https://stackoverflow.com/a/8974510/2441655
2。ボリュームキーの最初の押下を無視します(ボリュームバーのみを表示するため)。
3。 100%のときは音量アップキーを無視し、0%のときは音量ダウンキーを無視します。

3)ContentObserver(サービスから)

Ssuukkによる回答: https://stackoverflow.com/a/15292255/2441655 (最初の部分)

欠点:
1。 Androidの新しいバージョンでは機能しません: comment by dsemi
2。ボリュームキーの最初の押下を無視します(ボリュームバーのみを表示するため)。
3。 100%のときは音量アップキーを無視し、0%のときは音量ダウンキーを無視します。

4)AudioManager.registerMediaButtonEventReceiver(サービスから)

ジョーによる回答: https://stackoverflow.com/a/11510564/2441655

欠点:
1。ほとんどのROMでは動作しません: elguiによるコメント

5)onKeyDown(アクティビティから)

Dipaliによる回答: https://stackoverflow.com/a/21086563/2441655

欠点:
1。画面がオフの場合、別のアプリなどで機能しません。

6)dispatchKeyEvent(アクティビティから)

モーリス・ギャビンによる回答: https://stackoverflow.com/a/11462962/2441655

欠点:
1。画面がオフの場合、別のアプリなどでは機能しません。

結論

私が現在使用しているソリューションは#1です。理由は次のとおりです。

  1. これはSDKの公式部分です。
  2. サービスから使用できます。 (つまり、使用しているアプリに関係なく)
  3. 現在のボリューム/ UI状態に関係なく、すべてのボリュームキーを押します。
  4. 画面がオフのときに機能します。

他に何か見つかった場合、またはそれらのいくつかにさらに欠点を見つけた場合はお知らせください!

0
Venryx

checkout アプリの音量と再生の制御 ...これは問題を解決するのに役立ちます...複数のアプリケーションがバックグラウンドからボタンの押下をリッスンしたい場合があり、これがKeyEventsのみを処理できる理由である可能性がありますキーを押すユーザーへのインターフェイスであるため、アクティビティによって。

0
Ashish