Files
podlite/src/com/miniabs/DownloadService.java
T
xockoandClaude Opus 5.5 275bee3c11 MiniABS 1.2: Galician interface, larger text and touch targets
- Translate the interface to Galician (tools/translate_gl.py), including
  Galician month abbreviations for dates, which Android 2.3 lacks
- Resize for the Galaxy Mini's 240x320 screen: larger text, rows, tabs and
  buttons; all sizes now live in res/values/dimens.xml
- Redraw play/pause/refresh icons for every density; slightly brighter
  secondary text colours

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 21:29:17 +02:00

231 lines
8.3 KiB
Java

package com.miniabs;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.util.ArrayList;
import java.util.List;
import org.json.JSONObject;
import android.app.IntentService;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.net.wifi.WifiManager;
import android.os.PowerManager;
/** Downloads episodes one at a time to the SD card, resuming partial files. */
public class DownloadService extends IntentService {
private static final int NOTIF_ID = 2;
private static final List<Episode> queue = new ArrayList<Episode>();
static volatile Episode current;
static volatile int percent;
static volatile String lastError;
private static volatile String cancelId;
public DownloadService() {
super("MiniABS downloads");
}
static void enqueue(Context c, Episode e) {
synchronized (queue) {
if (current != null && current.id.equals(e.id)) return;
for (Episode q : queue) if (q.id.equals(e.id)) return;
queue.add(e);
}
lastError = null;
c.startService(new Intent(c, DownloadService.class).putExtra("ep", e.toJson().toString()));
}
static void cancel(String epId) {
synchronized (queue) {
for (int i = queue.size() - 1; i >= 0; i--) if (queue.get(i).id.equals(epId)) queue.remove(i);
}
Episode cur = current;
if (cur != null && cur.id.equals(epId)) cancelId = epId;
}
static List<Episode> queued() {
synchronized (queue) {
return new ArrayList<Episode>(queue);
}
}
static boolean busyWith(String epId) {
Episode cur = current;
if (cur != null && cur.id.equals(epId)) return true;
synchronized (queue) {
for (Episode q : queue) if (q.id.equals(epId)) return true;
}
return false;
}
@Override
protected void onHandleIntent(Intent intent) {
Episode e;
try {
e = Episode.fromStored(new JSONObject(intent.getStringExtra("ep")));
} catch (Exception ex) {
return;
}
synchronized (queue) {
boolean found = false;
for (int i = 0; i < queue.size(); i++) {
if (queue.get(i).id.equals(e.id)) {
queue.remove(i);
found = true;
break;
}
}
if (!found) return; // cancelled while waiting
current = e;
}
percent = 0;
cancelId = null;
PowerManager.WakeLock wake = ((PowerManager) getSystemService(POWER_SERVICE))
.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "MiniABS download");
WifiManager.WifiLock wifi = ((WifiManager) getSystemService(WIFI_SERVICE))
.createWifiLock(WifiManager.WIFI_MODE_FULL, "MiniABS download");
wake.acquire();
wifi.acquire();
try {
download(e);
if (e.id.equals(cancelId)) {
Downloads.delete(this, e);
}
} catch (Exception ex) {
if (!e.id.equals(cancelId)) {
lastError = "Fallou a descarga: " + Util.describe(ex);
notifyDone(e.title, lastError);
}
} finally {
current = null;
cancelId = null;
stopForeground(true);
wifi.release();
wake.release();
}
}
private void download(Episode e) throws IOException {
if (Prefs.get(this).wifiOnly() && !Util.onWifi(this)) {
throw new IOException("non hai wifi (Axustes: Descargar só con wifi)");
}
File dir = Downloads.dir(this);
if (Downloads.audio(this, e) != null) {
Downloads.saved(this, e);
return;
}
if (e.ino.length() == 0) throw new IOException("este episodio non ten ficheiro de audio");
File part = new File(dir, e.id + ".part");
Notification n = progressNotification(e, 0);
startForeground(NOTIF_ID, n);
Api.ensureFresh(this, 600);
HttpURLConnection conn = null;
int code = 0;
long have = part.exists() ? part.length() : 0;
boolean refreshed = false;
String[] urls = Api.downloadUrls(this, e);
for (int i = 0; i < urls.length; i++) {
conn = Api.open(this, urls[i], "GET");
conn.setReadTimeout(60000);
if (have > 0) conn.setRequestProperty("Range", "bytes=" + have + "-");
code = Api.responseCode(conn);
if (code == 401 && !refreshed && Prefs.get(this).canRefresh()) {
conn.disconnect();
Api.refresh(this, true);
refreshed = true;
urls = Api.downloadUrls(this, e);
i--;
continue;
}
if (code == 200 || code == 206) break;
conn.disconnect();
if (code == 416 && have > 0) { // stale partial file: start over
part.delete();
have = 0;
i--;
continue;
}
if (code != 404 && code != 403) break;
}
if (code != 200 && code != 206) {
if (code == 401) throw new Api.AuthException("a sesión caducou; inicia sesión de novo");
if (code == 403) throw new IOException("a túa conta non ten permiso para descargar");
throw new Api.ApiException(code, "");
}
try {
boolean append = code == 206 && have > 0;
if (!append) have = 0;
long len = conn.getContentLength();
long total = len > 0 ? have + len : (e.size > 0 ? e.size : -1);
InputStream in = conn.getInputStream();
OutputStream out = new FileOutputStream(part, append);
try {
byte[] buf = new byte[16384];
long done = have, lastUi = 0;
int r;
while ((r = in.read(buf)) > 0) {
if (e.id.equals(cancelId)) return;
out.write(buf, 0, r);
done += r;
long now = System.currentTimeMillis();
if (now - lastUi > 1500) {
lastUi = now;
percent = total > 0 ? (int) (done * 100 / total) : 0;
n = progressNotification(e, percent);
nm().notify(NOTIF_ID, n);
}
}
} finally {
out.close();
in.close();
}
} finally {
conn.disconnect();
}
if (e.id.equals(cancelId)) return;
File dest = new File(dir, e.id + e.ext);
if (!part.renameTo(dest)) throw new IOException("non se puido gardar o ficheiro");
Downloads.saved(this, e);
percent = 100;
}
private NotificationManager nm() {
return (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
}
private PendingIntent openApp() {
Intent i = new Intent(this, HomeActivity.class).putExtra("tab", HomeActivity.TAB_SAVED)
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
return PendingIntent.getActivity(this, 1, i, PendingIntent.FLAG_UPDATE_CURRENT);
}
private Notification progressNotification(Episode e, int pct) {
Notification n = new Notification(android.R.drawable.stat_sys_download, null, System.currentTimeMillis());
n.flags |= Notification.FLAG_ONGOING_EVENT | Notification.FLAG_ONLY_ALERT_ONCE;
int left;
synchronized (queue) {
left = queue.size();
}
String text = "Descargando " + pct + " %" + (left > 0 ? " · " + left + " máis na cola" : "");
n.setLatestEventInfo(this, e.title, text, openApp());
return n;
}
private void notifyDone(String title, String text) {
Notification n = new Notification(android.R.drawable.stat_notify_error, text, System.currentTimeMillis());
n.flags |= Notification.FLAG_AUTO_CANCEL;
n.setLatestEventInfo(this, title, text, openApp());
nm().notify(3, n);
}
}