/** DR Radio 2 is developed by Jacob Nordfalk, Hanafi Mughrabi and Frederik Aagaard. Some parts of the code are loosely based on Sveriges Radio Play for Android. DR Radio 2 for Android is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License version 2 as published by the Free Software Foundation. DR Radio 2 for Android is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with DR Radio 2 for Android. If not, see <http://www.gnu.org/licenses/>. */ package dk.dr.radio.afspilning; import android.annotation.SuppressLint; import android.annotation.TargetApi; import android.app.Activity; import android.appwidget.AppWidgetManager; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.media.AudioManager; import android.media.AudioManager.OnAudioFocusChangeListener; import android.media.MediaPlayer; import android.media.RingtoneManager; import android.net.Uri; import android.net.wifi.WifiManager; import android.net.wifi.WifiManager.WifiLock; import android.os.Build; import android.os.Handler; import android.os.PowerManager; import android.os.Vibrator; import android.telephony.PhoneStateListener; import android.telephony.TelephonyManager; import com.android.volley.Request; import com.android.volley.VolleyError; import java.util.ArrayList; import java.util.Date; import java.util.List; import dk.dr.radio.afspilning.wrapper.ExoPlayerWrapper; import dk.dr.radio.afspilning.wrapper.MediaPlayerLytter; import dk.dr.radio.afspilning.wrapper.MediaPlayerWrapper; import dk.dr.radio.afspilning.wrapper.Wrapperfabrikering; import dk.dr.radio.data.DRData; import dk.dr.radio.data.Kanal; import dk.dr.radio.data.Lydkilde; import dk.dr.radio.data.Lydstream; import dk.dr.radio.data.Playlisteelement; import dk.dr.radio.data.Udsendelse; import dk.dr.radio.diverse.App; import dk.dr.radio.diverse.Log; import dk.dr.radio.diverse.Sidevisning; import dk.dr.radio.net.volley.DrVolleyResonseListener; import dk.dr.radio.net.volley.DrVolleyStringRequest; import dk.dr.radio.v3.R; import dk.dr.radio.vaekning.AlarmAlertWakeLock; /** * @author j */ public class Afspiller { private final GemiusStatistik gemiusStatistik; class Afspillerlyd { MediaPlayer start; MediaPlayer fejl; MediaPlayer spiller; MediaPlayer stop; MediaPlayer forbinder; { start = MediaPlayer.create(App.instans, R.raw.afspiller_start); stop = MediaPlayer.create(App.instans, R.raw.afspiller_stop); forbinder = MediaPlayer.create(App.instans, R.raw.afspiller_forbinder); fejl = MediaPlayer.create(App.instans, R.raw.afspiller_fejl); spiller = MediaPlayer.create(App.instans, R.raw.afspiller_spiller); } } Afspillerlyd afspillerlyd; boolean afspillerlyde = false; public Status afspillerstatus = Status.STOPPET; // Burde være en del af afspillerstatus private boolean afspilningPåPause; private MediaPlayerWrapper mediaPlayer; private MediaPlayerLytter lytter = new MediaPlayerLytterImpl(); public List<Runnable> observatører = new ArrayList<Runnable>(); public List<Runnable> forbindelseobservatører = new ArrayList<Runnable>(); public List<Runnable> positionsobservatører = new ArrayList<Runnable>(); private HovedtelefonFjernetReciever hovedtelefonFjernetReciever = new HovedtelefonFjernetReciever(); private Lydstream lydstream; private int forbinderProcent; private Lydkilde lydkilde; public boolean vækningIGang; public PowerManager.WakeLock vækkeurWakeLock; AudioManager audioManager = (AudioManager) App.instans.getSystemService(Context.AUDIO_SERVICE); private static void sætMediaPlayerLytter(MediaPlayerWrapper mediaPlayer, MediaPlayerLytter lytter) { mediaPlayer.setMediaPlayerLytter(lytter); if (lytter != null) { // http://developer.android.com/guide/topics/media/mediaplayer.html#wakelocks if (App.prefs.getBoolean("cpulås", true)) mediaPlayer.setWakeMode(App.instans,PowerManager.PARTIAL_WAKE_LOCK); } } private WifiLock wifilock = null; /** * Forudsætter DRData er initialiseret */ public Afspiller() { mediaPlayer = Wrapperfabrikering.opret(); sætMediaPlayerLytter(mediaPlayer, this.lytter); // Indlæs gamle værdier så vi har nogle... // Fjernet. Skulle ikke være nødvendigt. Jacob 22/10-2011 // kanalNavn = p.getString("kanalNavn", "P1"); // lydUrl = p.getString("lydUrl", "rtsp://live-rtsp.dr.dk/rtplive/_definst_/Channel5_LQ.stream"); /* // Gem værdi hvis den ikke findes, sådan at indstillingsskærm viser det rigtige if (!App.prefs.contains(NØGLEholdSkærmTændt)) { // Xperia Play har brug for at holde skærmen tændt. Muligvis også andre.... boolean holdSkærmTændt = "R800i".equals(Build.MODEL); App.prefs.edit().putBoolean(NØGLEholdSkærmTændt, holdSkærmTændt).commit(); } */ wifilock = ((WifiManager) App.instans.getSystemService(Context.WIFI_SERVICE)).createWifiLock(WifiManager.WIFI_MODE_FULL_HIGH_PERF, "DR Radio"); wifilock.setReferenceCounted(false); Opkaldshaandtering opkaldshåndtering = new Opkaldshaandtering(this); TelephonyManager tm = (TelephonyManager) App.instans.getSystemService(Context.TELEPHONY_SERVICE); tm.listen(opkaldshåndtering, PhoneStateListener.LISTEN_CALL_STATE); /* // Opret en baggrundstråd med en Handler til at sende Runnables ind i new Thread() { public void run() { Looper.prepare(); baggrundstråd = new Handler(); Looper.loop(); } }.start(); */ if (App.fejlsøgning) tjekLydAktiv.run(); gemiusStatistik = new GemiusStatistik(); } private int onErrorTæller; private long onErrorTællerNultid; public void startAfspilning() { if (App.fejlsøgning) App.kortToast("startAfspilning() "+mediaPlayer); if (lydkilde.hentetStream == null && !App.erOnline()) { App.kortToast(R.string.Internetforbindelse_mangler); if (vækningIGang) ringDenAlarm(); return; } if (!lydkilde.harStreams()) { Request<?> req = new DrVolleyStringRequest(lydkilde.getStreamsUrl(), new DrVolleyResonseListener() { @Override public void fikSvar(String json, boolean fraCache, boolean uændret) throws Exception { if (uændret) return; // ingen grund til at parse det igen lydkilde.setStreams(json); Log.d("hentStreams afsp fraCache=" + fraCache + " => " + lydkilde); if (onErrorTæller++>2) { App.kortToast(R.string.Kunne_ikke_oprette_forbindelse_til_DR); //Log.rapporterFejl(new Exception("onErrorTæller++>10, uendelig løkke afværget"), lydkilde); if (vækningIGang) ringDenAlarm(); } else { startAfspilning(); // Opdatér igen - men kun én gang } } @Override protected void fikFejl(VolleyError error) { App.kortToast(R.string.Kunne_ikke_oprette_forbindelse_til_DR); if (vækningIGang) ringDenAlarm(); super.fikFejl(error); } }) { public Priority getPriority() { return Priority.IMMEDIATE; } }; App.volleyRequestQueue.add(req); return; } Log.d("startAfspilning() " + lydkilde); onErrorTæller = 0; onErrorTællerNultid = System.currentTimeMillis(); if (afspillerstatus == Status.STOPPET) { App.fjernbetjening.registrér(); //opdaterNotification(); // Start afspillerservicen så programmet ikke bliver lukket // når det kører i baggrunden under afspilning App.instans.startService(new Intent(App.instans, HoldAppIHukommelsenService.class)); if (App.prefs.getBoolean("wifilås", true) && wifilock != null) { wifilock.acquire(); } if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.FROYO) { // Se http://developer.android.com/training/managing-audio/audio-focus.html int result = audioManager.requestAudioFocus(getOnAudioFocusChangeListener(), AudioManager.STREAM_MUSIC, AudioManager.AUDIOFOCUS_GAIN); Log.d("requestAudioFocus res=" + result); if (result == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) { App.fjernbetjening.registrér(); } } if (!hovedtelefonFjernetReciever.aktiv) { hovedtelefonFjernetReciever.aktiv = true; App.instans.registerReceiver(hovedtelefonFjernetReciever, hovedtelefonFjernetReciever.FILTER); } startAfspilningIntern(); // Skru op til 1/5 styrke hvis volumen er lavere end det tjekVolumenMindst5tedele(1); Sidevisning.i().vist("afspilning_start", lydkilde.slug); } else Log.d(" forkert status=" + afspillerstatus); // Hvis det er en favorit så opdater favoritter så der ikke mere optræder nye udsendelser i denne programserie if (lydkilde instanceof Udsendelse) { String programserieSlug = ((Udsendelse) lydkilde).programserieSlug; if (DRData.instans.favoritter.erFavorit(programserieSlug)) { DRData.instans.favoritter.sætFavorit(programserieSlug, true); } } afspillerlyde = App.prefs.getBoolean("afspillerlyde", false); if (afspillerlyde && afspillerlyd==null) afspillerlyd = new Afspillerlyd(); if (afspillerlyde) afspillerlyd.start.start(); } /** Sørg for at volumen er skruet op til en minimumsværdi, angivet i 5'tedele af fuld styrke */ public void tjekVolumenMindst5tedele(int min5) { int max = audioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC); int nu = audioManager.getStreamVolume(AudioManager.STREAM_MUSIC); if (nu < min5 * max / 5) { audioManager.setStreamVolume(AudioManager.STREAM_MUSIC, min5 * max / 5, AudioManager.FLAG_SHOW_UI); } } /** * Typen er OnAudioFocusChangeListener, men da den ikke findes i API<8 kan vi ikke bruge klassen her */ Object onAudioFocusChangeListener; /** * Responding to the loss of audio focus */ @SuppressLint("NewApi") private OnAudioFocusChangeListener getOnAudioFocusChangeListener() { if (onAudioFocusChangeListener == null) onAudioFocusChangeListener = new OnAudioFocusChangeListener() { //private int lydstyreFørDuck = -1; @TargetApi(Build.VERSION_CODES.FROYO) public void onAudioFocusChange(int focusChange) { Log.d("onAudioFocusChange " + focusChange); AudioManager am = (AudioManager) App.instans.getSystemService(Context.AUDIO_SERVICE); switch (focusChange) { // Kommer ved f.eks. en SMS eller taleinstruktion i Google Maps case (AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK): Log.d("JPER duck"); if (afspillerstatus != Status.STOPPET) { // Vi 'dukker' lyden mens den vigtigere lyd høres // Sæt lydstyrken ned til en 1/3-del //lydstyreFørDuck = am.getStreamVolume(AudioManager.STREAM_MUSIC); //am.setStreamVolume(AudioManager.STREAM_MUSIC, (lydstyreFørDuck + 2) / 3, 0); mediaPlayer.setVolume(0.1f, 0.1f); // logaritmisk skala - 0.1 svarer til 1/3-del } break; // Dette sker ved f.eks. opkald case (AudioManager.AUDIOFOCUS_LOSS_TRANSIENT): Log.d("JPER pause"); if (afspillerstatus != Status.STOPPET) { pauseAfspilning(); // sætter afspilningPåPause=false if (afspillerlyde) afspillerlyd.stop.start(); afspilningPåPause = true; } break; // Dette sker hvis en anden app med lyd startes, f.eks. et spil case (AudioManager.AUDIOFOCUS_LOSS): Log.d("JPER stop"); // stopAfspilning(); pauseAfspilning(); am.abandonAudioFocus(this); break; // Dette sker når opkaldet er slut og ved f.eks. opkald case (AudioManager.AUDIOFOCUS_GAIN): Log.d("JPER Gain"); if (afspillerstatus == Status.STOPPET) { if (afspilningPåPause) startAfspilningIntern(); } else { // Genskab lydstyrke før den blev dukket mediaPlayer.setVolume(1f, 1f); //if (lydstyreFørDuck > 0) { // am.setStreamVolume(AudioManager.STREAM_MUSIC, lydstyreFørDuck, 0); //} // Genstart ikke afspilning, der spilles allerede! //startAfspilningIntern(); } } } }; return (OnAudioFocusChangeListener) onAudioFocusChangeListener; } long setDataSourceTid = 0; boolean setDataSourceLyd = false; private String mpTils() { AudioManager ar = (AudioManager) App.instans.getSystemService(App.AUDIO_SERVICE); //return mediaPlayer.getCurrentPosition()+ "/"+mediaPlayer.getDuration() + " "+mediaPlayer.isPlaying()+ar.isMusicActive(); if (!setDataSourceLyd && ar.isMusicActive()) { setDataSourceLyd = true; String str = "Det tog " + (System.currentTimeMillis() - setDataSourceTid) / 100 / 10.0 + " sek før lyden kom"; Log.d(str); if (App.fejlsøgning) { App.langToast(str); } } return " " + ar.isMusicActive() + " dt=" + (System.currentTimeMillis() - setDataSourceTid) + "ms"; } synchronized private void startAfspilningIntern() { afspillerstatus = Status.FORBINDER; afspilningPåPause = false; sendOnAfspilningForbinder(-1); opdaterObservatører(); handler.removeCallbacks(startAfspilningIntern); // mediaPlayer.setDataSource() bør kaldes fra en baggrundstråd da det kan ske // at den hænger under visse netværksforhold new Thread() { public void run() { setDataSourceTid = System.currentTimeMillis(); setDataSourceLyd = false; try { List<Lydstream> bs = lydkilde.findBedsteStreams(false); if (bs.size() == 0) { Log.rapporterFejl(new IllegalStateException("Ingen passende lydUrl for " + lydkilde)); App.kortToast(R.string.Kunne_ikke_oprette_forbindelse_til_DR); return; } lydstream = bs.get(0); gemiusStatistik.setLydkilde(lydkilde); DRData.instans.senestLyttede.registrérLytning(lydkilde); Log.d("mediaPlayer.setDataSource( " + lydstream); mediaPlayer.setDataSource(lydstream.url); Log.d("mediaPlayer.setDataSource() slut"); mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC); Log.d("mediaPlayer.setDataSource() slut " + mpTils()); mediaPlayer.prepare(); Log.d("mediaPlayer.prepare() slut " + mpTils()); } catch (Exception ex) { if (!App.PRODUKTION) Log.rapporterFejl(ex); else Log.e("Fejl for lyd-stream " + lydstream, ex); //ex = new Exception("spiller "+kanalNavn+" "+lydUrl, ex); //Log.kritiskFejlStille(ex); handler.post(new Runnable() { public void run() { // Stop afspilleren fra forgrundstråden. Jacob 14/11 lytter.onError(null, 42, 42); // kalder stopAfspilning(); og forsøger igen senere og melder fejl til bruger efter 10 forsøg } }); } } }.start(); } synchronized private void pauseAfspilningIntern() { handler.removeCallbacks(startAfspilningIntern); // Da mediaPlayer.reset() erfaringsmæssigt kan hænge i dette tilfælde afregistrerer vi // alle lyttere og bruger en ny final MediaPlayerWrapper gammelMediaPlayer = mediaPlayer; sætMediaPlayerLytter(gammelMediaPlayer, null); // afregistrér alle lyttere new Thread() { @Override public void run() { try { try { // Ignorér IllegalStateException, det er fordi den allerede er stoppet gammelMediaPlayer.stop(); } catch (IllegalStateException e) { } Log.d("P gammelMediaPlayer.reset() "+gammelMediaPlayer); gammelMediaPlayer.reset(); Log.d("P gammelMediaPlayer.release()"); gammelMediaPlayer.release(); Log.d("P gammelMediaPlayer færdig"); } catch (IllegalStateException e) { e.printStackTrace(); } catch (Exception e) { Log.rapporterFejl(e); } } }.start(); mediaPlayer = Wrapperfabrikering.opret(); sætMediaPlayerLytter(mediaPlayer, this.lytter); // registrér lyttere på den nye instans afspillerstatus = Status.STOPPET; afspilningPåPause = false; opdaterObservatører(); } synchronized public void pauseAfspilning() { long pos = gemPosition(); pauseAfspilningIntern(); if (hovedtelefonFjernetReciever.aktiv) { hovedtelefonFjernetReciever.aktiv = false; App.instans.unregisterReceiver(hovedtelefonFjernetReciever); } if (wifilock != null) wifilock.release(); gemiusStatistik.registérHændelse(GemiusStatistik.PlayerAction.Pause, pos / 1000); if (vækkeurWakeLock != null) { vækkeurWakeLock.release(); vækkeurWakeLock = null; } } /** * Gem position - og spol herhen næste gang udsendelsen spiller */ private long gemPosition() { if (!lydkilde.erDirekte() && afspillerstatus == Status.SPILLER) { long pos = mediaPlayer.getCurrentPosition(); if (pos > 0) { //senestLyttet.getUdsendelse().startposition = pos; Log.d("senestLyttede.sætStartposition("+lydkilde+" , "+pos); DRData.instans.senestLyttede.sætStartposition(lydkilde, (int) pos); } return pos; } return 0; } synchronized public void stopAfspilning() { Log.d("Afspiller stopAfspilning"); gemiusStatistik.registérHændelse(GemiusStatistik.PlayerAction.Stopped, getCurrentPosition() / 1000); Sidevisning.vist("afspilning_stop"); gemPosition(); pauseAfspilningIntern(); if (wifilock != null) wifilock.release(); // Stop afspillerservicen App.instans.stopService(new Intent(App.instans, HoldAppIHukommelsenService.class)); if (vækkeurWakeLock != null) { vækkeurWakeLock.release(); vækkeurWakeLock = null; } if (afspillerlyde) afspillerlyd.stop.start(); vækningIGang = false; App.fjernbetjening.afregistrér(); } public void setLydkilde(Lydkilde lydkilde) { Log.d("setLydkilde(" + lydkilde); if (lydkilde == this.lydkilde) return; if (lydkilde == null) { Log.rapporterFejl(new IllegalStateException("setLydkilde(null")); return; } if (lydkilde instanceof Kanal && Kanal.P4kode.equals(((Kanal) lydkilde).kode)) { // TODO - fjern tjek 9.okt 2014 // Nærmere fix for https://www.bugsense.com/dashboard/project/cd78aa05/errors/820758400 Log.rapporterFejl(new IllegalStateException("setLydkilde(P4F")); // return; // Nyt fix - vi vælger bare en underkanal. String kanalkode = App.tjekP4OgVælgUnderkanal(((Kanal) lydkilde).kode); lydkilde = DRData.instans.grunddata.kanalFraKode.get(kanalkode); } // Tjek om der er en hentet udsendelse - det sker også i brugergrænsefladen men det kan være den ikke har været i spil if (lydkilde.hentetStream==null && lydkilde instanceof Udsendelse) { DRData.instans.hentedeUdsendelser.tjekOmHentet((Udsendelse) lydkilde); } if ((afspillerstatus == Status.SPILLER) || (afspillerstatus == Status.FORBINDER)) { pauseAfspilning(); // gemmer lydkildens position this.lydkilde = lydkilde; startAfspilning(); // sætter afspilleren til den nye lydkildes position } else { this.lydkilde = lydkilde; } opdaterObservatører(); } private void opdaterObservatører() { AppWidgetManager mAppWidgetManager = AppWidgetManager.getInstance(App.instans); int[] appWidgetId = mAppWidgetManager.getAppWidgetIds(new ComponentName(App.instans, AfspillerIkonOgNotifikation.class)); for (int id : appWidgetId) { AfspillerIkonOgNotifikation.opdaterUdseende(App.instans, mAppWidgetManager, id); } // Notificér alle i observatørlisen - fra en kopi, sådan at de kan fjerne // sig selv fra listen uden at det giver ConcurrentModificationException for (Runnable observatør : new ArrayList<Runnable>(observatører)) { observatør.run(); } } public Status getAfspillerstatus() { return afspillerstatus; } public int getForbinderProcent() { return forbinderProcent; } public Lydkilde getLydkilde() { return lydkilde; } Handler handler = new Handler(); Runnable startAfspilningIntern = new Runnable() { public void run() { startAfspilningIntern(); } }; Runnable venterPåAtKommeOnline = new Runnable() { @Override public void run() { App.netværk.observatører.remove(venterPåAtKommeOnline); //if (afspillerstatus==Status.STOPPET) return; // Spiller ikke if (lydkilde.hentetStream != null) return; // Offline afspilning - ignorér try { if (!App.erOnline()) Log.e(new IllegalStateException("Burde være online her??!")); long dt = System.currentTimeMillis() - onErrorTællerNultid; Log.d("Vi kom online igen efter " + dt + " ms"); if (dt < 5 * 60 * 1000) { Log.d("Genstart afspilning"); startAfspilningIntern(); // Genstart } else { Log.d("Brugeren har nok glemt os, afslut"); stopAfspilning(); } } catch (Exception e) { Log.rapporterFejl(e); } } }; private void sendOnAfspilningForbinder(int procent) { forbinderProcent = procent; for (Runnable runnable : forbindelseobservatører) { runnable.run(); } } /** Flyt til position (i millisekunder) */ public void seekTo(long offsetMs) { Log.d("afspiler seekTo " + offsetMs); mediaPlayer.seekTo(offsetMs); gemiusStatistik.registérHændelse(GemiusStatistik.PlayerAction.Seeking, offsetMs / 1000); for (Runnable runnable : positionsobservatører) { runnable.run(); } } /** Længde i millisekunder */ public long getDuration() { if (afspillerstatus == Status.SPILLER) return mediaPlayer.getDuration(); return 0; } /** Position i millisekunder */ public long getCurrentPosition() { if (afspillerstatus == Status.SPILLER) return mediaPlayer.getCurrentPosition(); return 0; } public void forrige() { if (lydkilde.erDirekte()) { Kanal k = lydkilde.getKanal(); // Skift til forrige kanal if (k.p4underkanal) k = DRData.instans.grunddata.kanalFraKode.get(Kanal.P4kode); // P4 overkanal int index = DRData.instans.grunddata.kanaler.indexOf(k) - 1; if (index < 0) index = DRData.instans.grunddata.kanaler.size() - 1; k = DRData.instans.grunddata.kanaler.get(index); if (k.p4underkanal) { // Vi er kommet til P4 - vælg brugerens foretrukne underkanal k = DRData.instans.grunddata.kanalFraKode.get(App.tjekP4OgVælgUnderkanal(Kanal.P4kode)); } setLydkilde(k); return; } Udsendelse u = lydkilde.getUdsendelse(); if (u == null) return; long posMs = getCurrentPosition(); // spol hen til sang, der var 10 sekunder før denne // TODO hvis posMs<0, skal der så skiftes til forrige udsendelse/lytning? int index = u.findPlaylisteElemTilTid(posMs-10000, 0); if (index < 0) { // Skift 5% af udsendelsens varighed posMs = posMs - getDuration()*5/100; if (posMs<0) posMs=0; seekTo(posMs); return; } Playlisteelement pl = u.playliste.get(index); if (posMs-10000 < pl.offsetMs) seekTo(0); // Før føste sang else seekTo(pl.offsetMs); } public void næste() { if (lydkilde.erDirekte()) { // Skift til næste kanal Kanal k = lydkilde.getKanal(); int index = DRData.instans.grunddata.kanaler.indexOf(k) + 1; if (index == DRData.instans.grunddata.kanaler.size()) index = 0; while (k.p4underkanal && DRData.instans.grunddata.kanaler.get(index).p4underkanal) index++; // skip underkanaler k = DRData.instans.grunddata.kanaler.get(index); // Tjek om vi er kommet til P4 - vælg brugerens foretrukne underkanal String kanalkode = App.tjekP4OgVælgUnderkanal(k.kode); k = DRData.instans.grunddata.kanalFraKode.get(kanalkode); if (k==null) { Log.rapporterFejl(new IllegalStateException( "næste() fra "+lydkilde.getKanal().kode+" gav null i="+index+" kk="+kanalkode)); return; } setLydkilde(k); return; } Udsendelse u = lydkilde.getUdsendelse(); if (u == null) return; long posMs = getCurrentPosition(); int index = u.findPlaylisteElemTilTid(posMs, 0); if (index < 0) { // Skift 5% af udsendelsens varighed posMs = posMs + getDuration()*5/100; if (posMs>getDuration()) pauseAfspilning(); else seekTo(posMs); return; } if (index + 1 == u.playliste.size()) return; // TODO næste udsendelse? Playlisteelement pl = u.playliste.get(index + 1); seekTo(pl.offsetMs); } // // TILBAGEKALD FRA MEDIAPLAYER // class MediaPlayerLytterImpl implements MediaPlayerLytter { public void onPrepared(MediaPlayer mp) { Log.d("onPrepared " + mpTils()); afspillerstatus = Status.SPILLER; //No longer buffering opdaterObservatører(); // Det ser ud til kaldet til start() kan tage lang tid på Android 4.1 Jelly Bean // (i hvert fald på Samsung Galaxy S III), så vi kalder det i baggrunden new Thread() { public void run() { try { // Fix for https://www.bugsense.com/dashboard/project/cd78aa05/errors/825188032 Log.d("mediaPlayer.start() " + mpTils()); int startposition = DRData.instans.senestLyttede.getStartposition(lydkilde); long varighed = mediaPlayer.getDuration(); Log.d("mediaPlayer genoptager afspilning ved " + startposition + " varighed="+varighed); if (varighed>0 && startposition>0.95*varighed) { Log.d("mediaPlayer nej, det er for langt henne, starter ved starten"); startposition = 0; } gemiusStatistik.registérHændelse(GemiusStatistik.PlayerAction.Play, startposition / 1000); if (startposition > 0) { if (mediaPlayer instanceof ExoPlayerWrapper) { Log.d("exoplayer.seekTo() er slået fra - TODO fix"); //TODO fix } else { mediaPlayer.seekTo(startposition); } } mediaPlayer.start(); if (afspillerlyde) afspillerlyd.spiller.start(); Log.d("mediaPlayer.start() slut " + mpTils()); Thread.sleep(5000); // Vent lidt før data sendes if (App.netværk.erOnline()) { gemiusStatistik.startSendData(); } // Ellers venter vi, det kan være vi er heldige at brugeren er online ved næste hændelse } catch (Exception e) { Log.rapporterFejl(e); } } }.start(); } public void onCompletion(MediaPlayer mp_UBRUGT) { Log.d("AfspillerService onCompletion!"); // Hvis forbindelsen mistes kommer der en onCompletion() og vi er derfor // nødt til at genstarte, medmindre brugeren trykkede stop if (afspillerstatus == Status.SPILLER) { mediaPlayer.stop(); // mediaPlayer.reset(); // Da mediaPlayer.reset() erfaringsmæssigt kan hænge i dette tilfælde afregistrerer vi // alle lyttere og bruger en ny final MediaPlayerWrapper gammelMediaPlayer = mediaPlayer; sætMediaPlayerLytter(gammelMediaPlayer, null); // afregistrér alle lyttere new Thread() { public void run() { try { Log.d("COMPL gammelMediaPlayer.reset() "+gammelMediaPlayer); gammelMediaPlayer.reset(); Log.d("COMPL gammelMediaPlayer.release()"); gammelMediaPlayer.release(); Log.d("COMPL gammelMediaPlayer.release()"); } catch (Exception e) { Log.d(e); } } }.start(); if (lydkilde.erDirekte()) { Log.d("Genstarter afspilning!"); mediaPlayer = Wrapperfabrikering.opret(); sætMediaPlayerLytter(mediaPlayer, this); // registrér lyttere på den nye instans startAfspilningIntern(); if (afspillerlyde) afspillerlyd.forbinder.start(); } else { DRData.instans.senestLyttede.sætStartposition(lydkilde, 0); gemiusStatistik.registérHændelse(GemiusStatistik.PlayerAction.Completed, getCurrentPosition() / 1000); stopAfspilning(); } } } public boolean onError(MediaPlayer mp_UBRUGT, int hvad, int extra) { //Log.d("onError(" + MedieafspillerInfo.fejlkodeTilStreng(hvad) + "(" + hvad + ") " + extra+ " onErrorTæller="+onErrorTæller); Log.d("onError(" + hvad + ") " + extra + " onErrorTæller=" + onErrorTæller); if (vækningIGang) { ringDenAlarm(); return true; } if (Build.VERSION.SDK_INT == Build.VERSION_CODES.JELLY_BEAN && hvad == MediaPlayer.MEDIA_ERROR_UNKNOWN && "GT-I9300".equals(Build.MODEL) && mediaPlayer.isPlaying()) { // Ignorer, da Samsung Galaxy SIII på Android 4.1 Jelly Bean // sender denne fejl (onError(1) -110) men i øvrigt spiller fint videre! return true; } // Iflg http://developer.android.com/guide/topics/media/index.html : // "It's important to remember that when an error occurs, the MediaPlayer moves to the Error // state and you must reset it before you can use it again." if (afspillerstatus == Status.SPILLER || afspillerstatus == Status.FORBINDER) { // Hvis der har været // 1) færre end 10 fejl eller // 2) der højest er 1 fejl pr 20 sekunder så prøv igen long dt = System.currentTimeMillis() - onErrorTællerNultid; if (onErrorTæller++ < (App.fejlsøgning ? 2 : 10) || (dt / onErrorTæller > 20000)) { pauseAfspilningIntern(); //mediaPlayer.stop(); //mediaPlayer.reset(); if (App.erOnline()) { // Vi venter længere og længere tid her int n = onErrorTæller; if (n > 11) n = 11; int ventetid = 10 + 5 * (1 << n); // fra n=0:10 msek til n=10:5 sek til max n=11:10 sek Log.d("Ventetid før vi prøver igen: " + ventetid + " n=" + n + " " + onErrorTæller); handler.postDelayed(startAfspilningIntern, ventetid); } else { Log.d("Vent på at vi kommer online igen"); onErrorTællerNultid = System.currentTimeMillis(); App.netværk.observatører.add(venterPåAtKommeOnline); if (afspillerlyde) afspillerlyd.fejl.start(); } } else { pauseAfspilning(); // Vi giver op efter 10. forsøg App.langToast(R.string.Beklager_kan_ikke_spille_radio); App.langToast(R.string.Tjek_din_internetforbindelse_og___); if (afspillerlyde) afspillerlyd.fejl.start(); } } else { mediaPlayer.reset(); } return true; } public void onBufferingUpdate(MediaPlayer mp, int procent) { if (App.fejlsøgning) Log.d("Afspiller onBufferingUpdate : " + procent + " " + mpTils()); Log.d("Afspiller onBufferingUpdate : " + procent); if (procent < -100) procent = -1; // Ignorér vilde tal sendOnAfspilningForbinder(procent); } public void onSeekComplete(MediaPlayer mp) { Log.d("AfspillerService onSeekComplete"); //opdaterObservatører(); } } Runnable tjekLydAktiv = new Runnable() { @Override public void run() { App.forgrundstråd.removeCallbacks(this); Log.d("tjekLydAktiv " + audioManager.isMusicActive() + " " + mediaPlayer.isPlaying() + " " + getCurrentPosition() + " " + getDuration() + " " + new Date()); App.forgrundstråd.postDelayed(this, 10000); } }; void ringDenAlarm() { // Fix for https://mint.splunk.com/dashboard/project/cd78aa05/errors/2819028090 // - undgå at wrappe lydkilde i AlarmLydkilde igen og igen if (!(lydkilde instanceof AlarmLydkilde)) { Uri alert = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_ALARM); if (alert == null) { // alert is null, using backup alert = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION); if (alert == null) { // I can't see this ever being null (as always have a default notification) but just incase // alert backup is null, using 2nd backup alert = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_RINGTONE); } } lydkilde = new AlarmLydkilde(alert.toString(), lydkilde); } handler.postDelayed(startAfspilningIntern, 100); vibru(4000); } private void vibru(int ms) { Log.d("vibru " + ms); try { Vibrator vibrator = (Vibrator) App.instans.getSystemService(Activity.VIBRATOR_SERVICE); vibrator.vibrate(ms); // Tenu telefonon veka por 1/2a sekundo AlarmAlertWakeLock.createPartialWakeLock(App.instans).acquire(500); } catch (Exception e) { e.printStackTrace(); } } public String toString() { return mediaPlayer + " " + afspillerstatus+" "+lydstream; } }