web-dev-qa-db-fra.com

Écouter des boutons de volume dans le service d’arrière-plan?

Je sais écouter les boutons de volume d'une activité. Mais puis-je le faire dans un service d'arrière-plan? Si oui, comment faire ça?

37
user942821

À en juger par les quelques autres questions sur ce sujet, non.

Autre question 1 , Autre question 2

Les services ne reçoivent tout simplement pas les rappels KeyEvent.

10
Tanis.7x

CELA IS POSSIBLE. Utilisez le code ci-dessous (POUR LES NOUVELLES ANDROÏDES, ESP. Guimauve, VOIR LE FOND DE LA RÉPONSE):

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

Puis dans votre service onCreate, enregistrez-le avec:

mSettingsContentObserver = new SettingsContentObserver(this,new Handler());
getApplicationContext().getContentResolver().registerContentObserver(Android.provider.Settings.System.CONTENT_URI, true, mSettingsContentObserver );

Puis désinscrire dans onDestroy:

getApplicationContext().getContentResolver().unregisterContentObserver(mSettingsContentObserver);

Notez que cet exemple juge par le changement de volume de média, si vous voulez utiliser un autre volume, changez-le!

METTRE À JOUR:

La méthode ci-dessus ne fonctionne apparemment pas sur Marshmallow, MAIS il existe un moyen bien meilleur depuis l'introduction de MediaSession! Donc, vous devez d'abord migrer votre code vers le modèle MediaController/MediaSession, puis utiliser ce code:

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

En quelque sorte, les pressions sur les boutons de volume sont détectées même avec l'écran éteint (assurez-vous simplement d'enregistrer le bon récepteur d'intention de bouton de support, le cas échéant, sur votre plate-forme!)

UPDATE 2 depuis que GalDude a demandé plus d’informations sur l’acquisition de Media MediaSession/MediaController. Désolé, mais depuis que j'ai cessé d'utiliser Java, ce sera dans 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

L'application Musique AOSP dispose d'un service ( MediaPlaybackService ) qui répond aux événements de clé de volume en enregistrant BroadcastReceiver ( MediaButtonIntentReceiver ). 

Voici l'extrait de code où il inscrit le destinataire:

    mAudioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
    ComponentName rec = new ComponentName(getPackageName(),
            MediaButtonIntentReceiver.class.getName());
    mAudioManager.registerMediaButtonEventReceiver(rec);

En outre, n'oubliez pas de manifeste:

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

Cela fonctionne même si l'application Musique n'est pas au premier plan. N'est-ce pas ce que tu veux?

19
Joe

Vous devez jouer un son vierge du service, alors vous seul pouvez écouter les changements de volume. Suivre a travaillé pour moi

Pas

1. Mettez blank.mp3 dans un dossier raw (Télécharger depuis ici )

2. Lancez le support sur 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. Vous devez choisir d'arrêter et de libérer mediaplayer. Il vaut mieux le faire dans onDestroy ()

@Override
public void onDestroy() {

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

    super.onDestroy();
}

4. Créer un récepteur de diffusion qui écoutera les changements de volume

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. Enregistrez le récepteur de diffusion dans 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. Annuler l'enregistrement du récepteur broadccast dans onDestroy ()

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

    unregisterReceiver(broadcastReceiver);

    .....

    super.onDestroy();
}

C'est tout

2
Bikram Pandit

Remarque: cela ne fonctionne que pour les activités et non pour les services, comme l'indique la question.

En fonction du contexte dans lequel le rappel est requis, une autre solution peut être disponible.

Pour être capable de détecter le bouton de volume, une activité doit remplacer la fonction dispatchKeyEvent. Pour que cela soit présent dans plusieurs activités, vous pourriez écrire une super-classe contenant la fonction remplacée qui est étendue à toutes les activités suivantes.

Voici le code pour détecter les pressions sur les touches Volume Haut/Bas:

    // 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

Cela nécessite Lollipop (v5.0/API 21) ou supérieur

Mon objectif était d'ajuster le volume du système à partir d'un service. Toute action peut cependant être prise sur presse.

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 ne documente pas les API sur l'interaction avec les boutons de volume dans ce cas. Donc je suppose que la réponse est non…

0
Lincoln Hawk

checkout Contrôle du volume et de la lecture de votre application ... Cela aidera à résoudre votre problème ... plusieurs applications peuvent vouloir écouter les appuis sur les boutons en arrière-plan, cela peut être la raison pour laquelle KeyEvents ne peut être géré que par Activités ils sont l'interface avec l'utilisateur en appuyant sur les touches.

0
Ashish