diff --git a/README.md b/README.md index 80d10ce..ceaeb17 100644 --- a/README.md +++ b/README.md @@ -68,14 +68,15 @@ Android 2.x's media player can't stream https:// URLs, so for HTTPS servers Mini - **Shows**: every podcast; tap one for its episodes. Menu key → sort oldest/newest, hide played. - **Saved**: episodes downloaded to the SD card, plus download progress. - **Tap** an episode to play. **Long-press** for Download, Delete download, Mark as played/unplayed, Go to show. -- **Player**: skip back/forward (set the amounts in Settings), seek bar, show notes. Menu key → sleep timer, mark as played, download, stop. +- **Player**: skip back/forward (set the amounts in Settings), seek bar, show notes. Menu key → sleep timer, mark as played, download, stop, close MiniABS. +- **Close**: Menu key → *Pechar MiniABS* (on the Latest/Shows/Saved screen or the player) saves the position, stops playback and closes the app. - **Headset buttons**: play/pause; next/previous skip forward/back. Playback pauses when headphones are unplugged and during calls. Colored dots: blue = new, amber = in progress (with a bar), grey = played. ## Progress sync -Playback opens an Audiobookshelf session and syncs every 15 seconds and on pause, so progress follows you to other apps. Saved episodes play offline; their positions are kept on the phone and sent to the server next time you're online (or tap refresh on the Saved tab). +Playback opens an Audiobookshelf session and syncs every 15 seconds and on pause, so progress follows you to other apps. An episode only counts as played when playback gets within a minute of its end: a stream that drops or a saved file cut short is retried or reported, never marked as played. If playback says it's playing but stops moving for 12 seconds, MiniABS says so. Saved episodes play offline; their positions are kept on the phone and sent to the server next time you're online (or tap refresh on the Saved tab). ## Limits diff --git a/src/com/miniabs/HomeActivity.java b/src/com/miniabs/HomeActivity.java index e0f8f38..2019af7 100644 --- a/src/com/miniabs/HomeActivity.java +++ b/src/com/miniabs/HomeActivity.java @@ -101,6 +101,7 @@ public class HomeActivity extends BaseActivity { @Override protected void onNewIntent(Intent intent) { super.onNewIntent(intent); + if (intent.getBooleanExtra("exit", false)) { closeApp(); return; } // "Pechar MiniABS" from the player if (intent.hasExtra("tab")) selectTab(intent.getIntExtra("tab", TAB_LATEST)); } @@ -495,9 +496,17 @@ public class HomeActivity extends BaseActivity { menu.add(0, 2, 0, "Biblioteca").setIcon(android.R.drawable.ic_menu_agenda); menu.add(0, 3, 0, "En reprodución").setIcon(android.R.drawable.ic_media_play); menu.add(0, 4, 0, "Axustes").setIcon(android.R.drawable.ic_menu_preferences); + menu.add(0, 5, 0, "Pechar MiniABS").setIcon(android.R.drawable.ic_menu_close_clear_cancel); return true; } + /** Stops playback (saving the position) and closes the app. */ + private void closeApp() { + PlayerService ps = PlayerService.get(); + if (ps != null) ps.stop(); + finish(); + } + @Override public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { @@ -513,6 +522,9 @@ public class HomeActivity extends BaseActivity { case 4: startActivity(new Intent(this, SettingsActivity.class)); return true; + case 5: + closeApp(); + return true; default: return super.onOptionsItemSelected(item); } diff --git a/src/com/miniabs/PlayerActivity.java b/src/com/miniabs/PlayerActivity.java index 284d322..ac4a986 100644 --- a/src/com/miniabs/PlayerActivity.java +++ b/src/com/miniabs/PlayerActivity.java @@ -245,11 +245,17 @@ public class PlayerActivity extends BaseActivity { } menu.add(0, 4, 0, "Ir ao programa").setIcon(android.R.drawable.ic_menu_view); if (active) menu.add(0, 5, 0, "Deter").setIcon(android.R.drawable.ic_menu_close_clear_cancel); + menu.add(0, 6, 0, "Pechar MiniABS").setIcon(android.R.drawable.ic_lock_power_off); return true; } @Override public boolean onOptionsItemSelected(MenuItem item) { + if (item.getItemId() == 6) { // close the app: HomeActivity (the root) stops playback and finishes + startActivity(new Intent(this, HomeActivity.class).addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP).putExtra("exit", true)); + finish(); + return true; + } final Episode e = current(); if (e == null) return false; PlayerService ps = PlayerService.get(); diff --git a/src/com/miniabs/PlayerService.java b/src/com/miniabs/PlayerService.java index e0c70c2..b5a408c 100644 --- a/src/com/miniabs/PlayerService.java +++ b/src/com/miniabs/PlayerService.java @@ -85,6 +85,9 @@ public class PlayerService extends Service implements MediaPlayer.OnPreparedList private long playingSince; private long lastTick; + private int stallPos = -1; // position last seen moving + private long stallSince; + private boolean stallWarned; private float listened; // seconds listened since the last sync private long lastSync; private volatile boolean syncing; @@ -552,10 +555,16 @@ public class PlayerService extends Service implements MediaPlayer.OnPreparedList public void onCompletion(MediaPlayer p) { if (p != mp) return; int pos = positionMs(), dur = durationMs(); - // Old Android's streamer "completes" when the connection drops. Treat that as an error. - if (!local && dur > 0 && pos < dur - 30000) { + // Only playback that got to (nearly) the end counts as listened; anything else is never + // reported as played. Old Android's streamer "completes" when the connection drops, and a + // saved file cut short (SD card, interrupted copy) "completes" early too. + if (dur > 0 && pos < dur - 60000) { lastPosMs = pos; - retryStream("Perdeuse a conexión, reconectando…"); + if (!local && contentUrl != null) { + retryStream("Perdeuse a conexión, reconectando…"); + } else { + fail("O ficheiro rematou antes de tempo (" + Util.clock(pos) + "). Proba a descargalo de novo."); + } return; } state = ENDED; @@ -672,6 +681,16 @@ public class PlayerService extends Service implements MediaPlayer.OnPreparedList App.setProgress(ep.id, lastPosMs / 1000.0, dur, false); if (now - lastSync > SYNC_EVERY) syncNow(false); if (now - playingSince > 60000) retries = 0; + // "playing" but not moving for 12 s: say so instead of staying silent + if (lastPosMs != stallPos) { + stallPos = lastPosMs; + stallSince = now; + stallWarned = false; + } else if (!stallWarned && now - stallSince > 12000) { + stallWarned = true; + status = "A reprodución quedou parada. Proba a pausar e reproducir de novo."; + android.widget.Toast.makeText(this, status, android.widget.Toast.LENGTH_LONG).show(); + } if (sleepAt > 0 && now >= sleepAt) { sleepAt = 0; pause();