Cum să prinzi butoanele de volume în aplicație android – problemă
Problema
Trebuie să fac un listener pe butonul de volume sau, mai bine zis, un listener pe o combinație de taste (ex: volume up, up, up, down, down, down, up, up, up).
Soluția 1
În interiorul unei activități este foarte ușor. Doar dau override lui onKeyDown și verific dacă key_press este VOLUME_DOWN sau VOLUME_UP.
1 2 3 4 5 6 7 8 9 10 11 12 |
@Override public boolean onKeyDown(int keyCode, KeyEvent event) { switch(keyCode) { case KeyEvent.KEYCODE_VOLUME_DOWN: break; case KeyEvent.KEYCODE_VOLUME_UP: break; } return false; } |
Problema apare prin faptul că eu am nevoie de listener-ul acesta și în afara activității (chiar și dacă sunt într-o altă aplicație). Adică într-un service.
Problema 2 apare prin faptul că într-un service nu am un onKeyDown căruia să îi dau override.
Soluția 2
Creez un ContentObserver care să observe orice change care ar putea să se întâmple legat de volum. Problema principală legată de această soluție este că există un lag.
Este o variantă foarte bună, dacă nu mă interesează.
1 2 3 4 5 6 7 8 9 10 11 |
public class SettingsContentObserver extends ContentObserver { int prevVolume; @Override public void onChange(boolean selfChange) { super.onChange(selfChange); AudioManager audio =(AudioManager) context.getSystemService(Context.AUDIO_SERVICE); int currentVolume = audio.getStreamVolume(AudioManager.STREAM_MUSIC); } } |
Pe lângă lag, uneori combinația de taste recepționate nu este și cea pe care am dat-o în aceeași ordine. Un: D U D U, uneori îmi venea DDUU sau DUUD. Marja de eroare era mică, dar eu aveam nevoie de ceva carre să funcționeze mereu.
Solutia 3
Soluția 3 are multe probleme, pe care le-am rezolvat pe parcurs. În primul rând, consumă baterie. În al doilea rând, blochează alte funcții alte telefonului. Însă, persoanele care vor rula aplicația mea nu vor fi deranjate de funcțiile blocate. În cel mai rău caz, de consumul de baterie, dar și pe ăla l-am mai aranjat.
Ideea
Am înregistrat aplicația mea ca fiind un player muzical, am făcut un serviciu care să dea play unei melodii în buclă infinită 🙂 Am început cu un sample gol de 1 secundă care rupea bateria (consumă mult când începe să îi dea play, CPU usage pe la vreo 9% în medie).
Am luat o melodie mp3 de pe net de vreo 3 minute, am pus-o, CPU usage la vreo 5%. Mi-am refăcut eu o melodie, mono, la 2000mhz, pe 8 biti, durată de o oră, dar MP3(era default). A scăzut CPU usage la 3%.
Am stat, m-am gândit, m-am întors cum aș putea să mai scad și atunci îmi dau seama că aș putea să salvez într-un format uncompressed. Am ales wav, am scăzut durata “melodiei” la 15 minute și am testat: spike-uri de 1% CPU usage 🙂
1 2 3 4 5 |
MediaButtonIntentReceiver.grtService = GRTService.this; mp = MediaPlayer.create(getBaseContext(), R.raw.song); mp.start(); mp.setLooping(true); |
În MediaButtonIntentReceiver este cel care va recepționa mesajul:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
public class MediaButtonIntentReceiver extends BroadcastReceiver { public MediaButtonIntentReceiver() { super(); } public static int oldVolume; @Override public void onReceive(Context context, Intent intent) { AudioManager audio = (AudioManager) context .getSystemService(Context.AUDIO_SERVICE); int ringVolume = audio.getStreamVolume(AudioManager.STREAM_MUSIC); int maxringVolume = audio.getStreamMaxVolume(AudioManager.STREAM_MUSIC); int blueToothVolume = audio.getStreamVolume(6); int maxBlueToothVolume = audio.getStreamMaxVolume(6); if (ringVolume == maxringVolume) { audio.setStreamVolume(AudioManager.STREAM_MUSIC, ringVolume - 1, AudioManager.FLAG_ALLOW_RINGER_MODES); } if (oldVolume<ringVolume) soundUp(); else soundDown(); } } |
Câteva chestii legate de codul de mai sus.
- Când pornesc aplicația, înregistrez în oldVolum volumul actual al device-ului.
- Unele device-uri android, dacă sunt la max volume, nu o să mai recepționeze volume up key, așa că trebuie eu să scad volumul cu 1 de câte ori ajunge la maxim.
- Aceeași poveste și la minim.
- Există mai multe tipuri de volume la care trebuie să fiu atent. Ring, music, system etc. Pentru că eu dau în permanență play unei melodii, mă interesează doar music și, dacă bluetooth-ul este pornit, și acesta.
- Căștile la android sunt considerate tot stream_music.
Metoda de mai sus consumă aproximativ 2% din baterie pe oră. Testat pe un Samsung Galaxy S4 și un Samsung Galaxy S3 în condiții de laborator (reset to factory settings, baterie virgină și fără alte aplicații rulând în paralel) Cam mult, dar merge 🙂
Edit: am căutat în documentația oficială, ei spun că ar consuma 1.6% din baterie pe oră (62 ore de playback) ceea ce este asemănător cu testele mele.
By: Adrian Coman