Я знаю, как слушать кнопки громкости в действии. Но могу ли я сделать это в фоновом режиме? Если да, как это сделать?
Слушать кнопки регулировки громкости в фоновом режиме?
Ответ 1
Судя по парам других вопросов по этой теме, нет.
Другой вопрос 1, Другой вопрос 2
Сервисы просто не получают обратные вызовы KeyEvent.
Ответ 2
Это возможно. Используйте код ниже (для более новых версий Android, особенно Marshmallow, см. Нижнюю часть ответа):
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);
Каким-то образом нажатия кнопок громкости обнаруживаются даже при выключенном экране (просто обязательно зарегистрируйте правильный приемник мультимедийных кнопок, если это применимо для вашей платформы!)
ОБНОВЛЕНИЕ 2, так как GalDude запросил дополнительную информацию о получении Media MediaSession/MediaController. Извините, но так как я перестал использовать Java, он будет в Kotlin:
lateinit var mediaSession: MediaSessionCompat // you have to initialize it in your onCreate method
val kontroler: MediaControllerCompat
get() = mediaSession.controller // in Java it 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()
Ответ 3
В приложении AOSP Music есть служба (MediaPlaybackService), которая реагирует на события громкости, регистрируя BroadcastReceiver (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>
Это работает, даже если приложение "Музыка" не находится на переднем плане. Разве это не то, что вы хотите?
Ответ 4
Мне удалось заставить его работать на устройствах Android 5+, используя MediaSession
. Однако ContentObserver
, предложенный @ssuukk, не работал у меня как на устройствах 4.4, так и на 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));
}
Есть несколько вещей, о которых нужно знать:
- Он полностью перехватывает кнопки громкости, поэтому, пока этот код работает, вы не сможете отрегулировать громкость звонка с помощью кнопок регулировки громкости. Это можно было бы исправить, я просто не пробовал.
- Если вы запустите пример as -s, кнопки регулировки громкости будут оставаться под контролем приложения, даже если экран выключен, и приложение было удалено из списка "Недавние приложения". Вам нужно перейти в
Settings->Applications
, найти приложение и принудительно остановить его, чтобы вернуть кнопки громкости.
Ответ 5
Вам нужно проигрывать пустой звук из сервиса, тогда только вы можете слушать изменения громкости. Следующее сработало для меня
меры
1. Поместите blank.mp3 в папку raw (скачать отсюда)
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. Вы должны остановить и выпустить медиаплеер. Это лучше сделать в onDestroy()
@Override
public void onDestroy() {
mediaPlayer.stop();
mediaPlayer.release();
super.onDestroy();
}
4. Создайте приемник Broadcast, который будет прослушивать изменения громкости
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();
}
Все это
Ответ 6
Android не документирует API при взаимодействии с кнопками громкости в этом случае. Поэтому я думаю, что ответ не...
Ответ 7
Примечание: это работает только для операций, а не для служб, о которых говорится в вопросе.
В зависимости от контекста, в котором требуется обратный вызов, может быть доступно альтернативное решение.
Чтобы быть способным обнаруживать кнопку громкости, необходимо будет переопределить функцию 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);
}
Ответ 8
checkout Управление объемом и воспроизведением ваших приложений... Это поможет решить вашу проблему... несколько приложений могут захотеть прослушать это может быть причиной, по которой KeyEvents могут обрабатываться только действиями, поскольку они являются интерфейсом для пользователя, нажимающего клавиши.
Ответ 9
Это требует 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 onDestroy method
public void destroy() {
if (mMediaSession != null) {
mMediaSession.release();
}
}
}