package com.miniabs; import android.app.Activity; import android.app.AlertDialog; import android.content.DialogInterface; import android.content.Intent; import android.os.Handler; import android.view.ContextMenu; import android.view.MenuItem; import android.view.View; import android.widget.ImageButton; import android.widget.ProgressBar; import android.widget.TextView; import android.widget.Toast; /** Shared bits: the mini player bar, a 1-second UI tick, sign-in errors and episode actions. */ abstract class BaseActivity extends Activity { protected final Handler handler = new Handler(); private boolean ticking; private View mini; private TextView miniTitle, miniSub; private ImageButton miniPlay; private ProgressBar miniProgress; private final Runnable ticker = new Runnable() { public void run() { onTick(); if (ticking) handler.postDelayed(this, 1000); } }; @Override protected void onResume() { super.onResume(); ticking = true; handler.post(ticker); } @Override protected void onPause() { ticking = false; handler.removeCallbacks(ticker); super.onPause(); } protected void onTick() { updateMiniBar(); } protected void toast(String s) { Toast.makeText(this, s, s.length() > 60 ? Toast.LENGTH_LONG : Toast.LENGTH_SHORT).show(); } /** Shows an error; sends the user to the login screen if their sign-in expired. */ protected void showError(Exception e) { if (e instanceof Api.AuthException) { Prefs.get(this).logout(); App.clear(); HomeActivity.clearCache(); startActivity(new Intent(this, LoginActivity.class) .putExtra("message", e.getMessage()) .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)); finish(); return; } if (offerTrust(null)) return; toast(Util.describe(e)); } /** If a request just failed on an unrecognised certificate, ask about it. retry runs after trusting. */ protected boolean offerTrust(final Runnable retry) { return TrustPrompt.offer(this, new Runnable() { public void run() { if (retry != null) retry.run(); else toast("Certificado aceptado. Téntao de novo."); } }); } // ------------------------------------------------------------ mini player protected void setupMiniBar() { mini = findViewById(R.id.mini_bar); if (mini == null) return; miniTitle = (TextView) findViewById(R.id.mini_title); miniSub = (TextView) findViewById(R.id.mini_sub); miniPlay = (ImageButton) findViewById(R.id.mini_play); miniProgress = (ProgressBar) findViewById(R.id.mini_progress); findViewById(R.id.mini_info).setOnClickListener(new View.OnClickListener() { public void onClick(View v) { startActivity(new Intent(BaseActivity.this, PlayerActivity.class)); } }); miniPlay.setOnClickListener(new View.OnClickListener() { public void onClick(View v) { PlayerService ps = PlayerService.get(); if (ps != null && ps.episode() != null) { PlayerService.send(BaseActivity.this, PlayerService.A_TOGGLE); } else { Episode last = Prefs.get(BaseActivity.this).lastEpisode(); if (last != null) PlayerService.play(BaseActivity.this, last); } handler.postDelayed(new Runnable() { public void run() { updateMiniBar(); } }, 300); } }); } protected void updateMiniBar() { if (mini == null) return; PlayerService ps = PlayerService.get(); Episode e = ps != null ? ps.episode() : null; if (e == null) e = Prefs.get(this).lastEpisode(); if (e == null) { mini.setVisibility(View.GONE); return; } mini.setVisibility(View.VISIBLE); miniTitle.setText(e.title); boolean playing = ps != null && ps.episode() != null && ps.isPlaying(); miniPlay.setImageResource(playing ? R.drawable.ic_pause_small : R.drawable.ic_play_small); int pos, dur; String sub; if (ps != null && ps.episode() != null) { pos = ps.positionMs(); dur = ps.durationMs(); int st = ps.state(); if (st == PlayerService.LOADING || st == PlayerService.ERROR) sub = ps.status(); else sub = Util.join(e.show, dur > 0 ? "quedan " + Util.clock(dur - pos) : ""); } else { double[] p = App.progress(e.id); double cur = p != null ? p[0] : Math.max(0, App.localPosition(this, e.id)); pos = (int) (cur * 1000); dur = (int) (e.duration * 1000); sub = Util.join(e.show, "En pausa"); } miniSub.setText(sub); miniProgress.setProgress(dur > 0 ? (int) (1000L * pos / dur) : 0); } // ------------------------------------------------------------ episode actions protected static final int M_PLAY = 1, M_DOWNLOAD = 2, M_DELETE = 3, M_PLAYED = 4, M_UNPLAYED = 5, M_SHOW = 6, M_CANCEL = 7; protected void episodeMenu(ContextMenu menu, Episode e, boolean offerShow) { menu.setHeaderTitle(e.title); menu.add(0, M_PLAY, 0, "Reproducir"); if (DownloadService.busyWith(e.id)) menu.add(0, M_CANCEL, 0, "Cancelar descarga"); else if (Downloads.has(this, e.id)) menu.add(0, M_DELETE, 0, "Eliminar descarga"); else menu.add(0, M_DOWNLOAD, 0, "Descargar"); double[] p = App.progress(e.id); if (p != null && p[2] > 0) menu.add(0, M_UNPLAYED, 0, "Marcar como non escoitado"); else menu.add(0, M_PLAYED, 0, "Marcar como escoitado"); if (offerShow) menu.add(0, M_SHOW, 0, "Ir ao programa"); } /** Returns true if handled. onChanged runs on the UI thread after the list should refresh. */ protected boolean episodeAction(int id, final Episode e, final Runnable onChanged) { switch (id) { case M_PLAY: playEpisode(e); return true; case M_DOWNLOAD: if (Prefs.get(this).wifiOnly() && !Util.onWifi(this)) { new AlertDialog.Builder(this) .setMessage("Non estás nunha wifi. Descargar con datos móbiles esta vez?") .setPositiveButton("Descargar", new DialogInterface.OnClickListener() { public void onClick(DialogInterface d, int w) { Prefs.get(BaseActivity.this).sp.edit().putBoolean("wifi_only", false).commit(); DownloadService.enqueue(BaseActivity.this, e); toast("Descargando. Desactivouse «só con wifi» (podes cambialo en Axustes)."); if (onChanged != null) onChanged.run(); } }) .setNegativeButton("Cancelar", null) .show(); } else { DownloadService.enqueue(this, e); toast("Descargando…"); } if (onChanged != null) onChanged.run(); return true; case M_CANCEL: DownloadService.cancel(e.id); toast("Descarga cancelada"); if (onChanged != null) onChanged.run(); return true; case M_DELETE: PlayerService ps = PlayerService.get(); if (ps != null && ps.episode() != null && ps.episode().id.equals(e.id) && ps.isLocal()) { ps.stop(); } Downloads.delete(this, e); toast("Descarga eliminada"); if (onChanged != null) onChanged.run(); return true; case M_PLAYED: case M_UNPLAYED: setPlayed(e, id == M_PLAYED, onChanged); return true; case M_SHOW: startActivity(new Intent(this, PodcastActivity.class) .putExtra("itemId", e.itemId).putExtra("title", e.show)); return true; default: return false; } } protected void playEpisode(Episode e) { PlayerService.play(this, e); startActivity(new Intent(this, PlayerActivity.class).putExtra("ep", e.toJson().toString())); } private void setPlayed(final Episode e, final boolean played, final Runnable onChanged) { final double dur = e.duration; final double[] before = App.progress(e.id); App.setProgress(e.id, played ? dur : 0, dur, played); if (onChanged != null) onChanged.run(); new Job() { Void work() throws Exception { Api.setProgress(BaseActivity.this, e.itemId, e.id, played ? dur : 0, dur, Boolean.valueOf(played)); App.forgetLocal(BaseActivity.this, e.id); return null; } void done(Void r) { toast(played ? "Marcado como escoitado" : "Marcado como non escoitado"); } void failed(Exception ex) { if (before != null) App.setProgress(e.id, before[0], before[1], before[2] > 0); else App.setProgress(e.id, 0, dur, false); if (onChanged != null) onChanged.run(); showError(ex); } }.start(); } protected boolean onEpisodeContextItem(MenuItem item, Episode e, Runnable onChanged) { return e != null && episodeAction(item.getItemId(), e, onChanged); } }