package dk.dr.radio.data;
import android.annotation.TargetApi;
import android.app.DownloadManager;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.database.Cursor;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Build;
import android.os.Environment;
import android.support.v4.app.FragmentActivity;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentTransaction;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.Serializable;
import java.net.URI;
import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Scanner;
import dk.dr.radio.akt.Hentede_udsendelser_frag;
import dk.dr.radio.akt.Hovedaktivitet;
import dk.dr.radio.diverse.FilCache;
import dk.dr.radio.diverse.App;
import dk.dr.radio.diverse.Log;
import dk.dr.radio.diverse.Serialisering;
import dk.dr.radio.diverse.Sidevisning;
import dk.dr.radio.v3.R;
/**
* Created by j on 01-03-14.
*/
@TargetApi(Build.VERSION_CODES.GINGERBREAD)
public class HentedeUdsendelser {
public static final String NØGLE_placeringAfHentedeFiler = "placeringAfHentedeFiler";
private DownloadManager downloadService = null;
public static class Data implements Serializable {
// Fix for https://www.bugsense.com/dashboard/project/cd78aa05/errors/1415558087
// - at proguard obfuskering havde
// Se også http://stackoverflow.com/questions/16210831/serialization-deserialization-proguard
private static final long serialVersionUID = -3292059648694915445L;
/** slug -> downloadId */
private Map<String, Long> downloadIdFraSlug = new LinkedHashMap<String, Long>();
/** DownloadId -> udsendelse */
private Map<Long, Udsendelse> udsendelseFraDownloadId = new LinkedHashMap<Long, Udsendelse>();
private ArrayList<Udsendelse> udsendelser = new ArrayList<Udsendelse>();
/** slug -> filnavn */
private Map<String, HentetStatus> hentetStatusFraSlug = new LinkedHashMap<>();
}
private Data data;
public List<Runnable> observatører = new ArrayList<Runnable>();
/** Understøttes ikke på Android 2.2 og tidligere */
public boolean virker() {
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.GINGERBREAD;
}
private final String FILNAVN;
public HentedeUdsendelser() {
if (virker() && App.instans != null) { // App.instans==null standard JVM (udenfor Android)
downloadService = (DownloadManager) App.instans.getSystemService(Context.DOWNLOAD_SERVICE);
FILNAVN = App.instans.getFilesDir() + "/HentedeUdsendelser.ser";
} else {
FILNAVN = "/tmp/HentedeUdsendelser.ser";
}
}
public Collection<Udsendelse> getUdsendelser() {
tjekDataOprettet();
return data.udsendelser;
}
public HentetStatus getHentetStatus(Udsendelse udsendelse) {
if (!virker()) return null;
tjekDataOprettet();
HentetStatus hs = data.hentetStatusFraSlug.get(udsendelse.slug);
if (hs != null && (hs.status==DownloadManager.STATUS_SUCCESSFUL || hs.status == DownloadManager.STATUS_FAILED)) {
hs.statustekst = lavStatustekst(hs);
return hs;
}
Long downloadId = data.downloadIdFraSlug.get(udsendelse.slug);
if (downloadId == null) return null;
DownloadManager.Query query = new DownloadManager.Query();
query.setFilterById(downloadId);
Cursor c = downloadService.query(query);
if (c==null) return null; // fix for https://mint.splunk.com/dashboard/project/cd78aa05/errors/4066198043
if (!c.moveToFirst()) {
c.close();
return null;
}
if (hs == null) {
hs = new HentetStatus();
data.hentetStatusFraSlug.put(udsendelse.slug, hs);
}
hs.status = c.getInt(c.getColumnIndex(DownloadManager.COLUMN_STATUS));
hs.iAlt = c.getInt(c.getColumnIndex(DownloadManager.COLUMN_TOTAL_SIZE_BYTES)) / 1000000;
hs.hentet = c.getInt(c.getColumnIndex(DownloadManager.COLUMN_BYTES_DOWNLOADED_SO_FAR)) / 1000000;
hs.startUri = c.getString(c.getColumnIndex(DownloadManager.COLUMN_LOCAL_URI));
hs.statustekst = lavStatustekst(hs);
if (hs.status==DownloadManager.STATUS_FAILED || hs.status==DownloadManager.STATUS_PAUSED) {
int grund = c.getInt(c.getColumnIndex(DownloadManager.COLUMN_REASON));
Log.d(udsendelse.slug+ " hs.grund til fejl "+ grund);
if (grund==DownloadManager.ERROR_INSUFFICIENT_SPACE) hs.statustekst += " - Ikke nok plads";
}
c.close();
//if (!App.PRODUKTION) hs.statustekst+="\n"+hs.startUri+"\n"+hs.destinationFil; // til fejlfinding
return hs;
}
public static String lavStatustekst(HentetStatus hs) {
if (hs.status == DownloadManager.STATUS_SUCCESSFUL) {
if (hs.statusFlytningIGang) return App.res.getString(R.string.Flytter__);
if (new File(hs.destinationFil).canRead()) return App.instans.getString(R.string.Klar___mb_, hs.iAlt);
return App.instans.getString(R.string._ikke_tilgængelig_);
} else if (hs.status == DownloadManager.STATUS_FAILED) {
return App.instans.getString(R.string.Mislykkedes);
} else if (hs.status == DownloadManager.STATUS_PENDING) {
return App.instans.getString(R.string.Venter___);
} else if (hs.status == DownloadManager.STATUS_PAUSED) {
return App.instans.getString(R.string.Hentning_pauset__) + App.instans.getString(R.string.Hentet___mb_af___mb, hs.hentet, hs.iAlt);
}
// RUNNING
if (hs.hentet > 0 || hs.iAlt > 0) return App.instans.getString(R.string.Hentet___mb_af___mb, hs.hentet, hs.iAlt);
return App.instans.getString(R.string.Henter__);
}
public void tjekOmHentet(Udsendelse udsendelse) {
if (!virker()) return;
if (udsendelse.hentetStream == null) {
HentetStatus hs = getHentetStatus(udsendelse);
if (hs == null) return;
if (hs.status != DownloadManager.STATUS_SUCCESSFUL) return;
File file = new File(hs.destinationFil);
if (file.exists()) {
udsendelse.hentetStream = new Lydstream();
udsendelse.hentetStream.url = hs.destinationFil;
udsendelse.hentetStream.score = 500; // Rigtig god!
udsendelse.kanHøres = true;
Log.registrérTestet("Afspille hentet udsendelse", udsendelse.slug);
} else {
Log.rapporterFejl(new IllegalStateException("Fil " + file + " hentet, men fandtes ikke alligevel??!"));
}
}
/*
if (udsendelse.hentetStream!=null && !new File(udsendelse.hentetStream.url).canRead()) {
Log.d("Fil findes pt ikke" + udsendelse.hentetStream.url);
udsendelse.hentetStream = null;
}
*/
}
private void tjekDataOprettet() {
if (data != null) return;
if (new File(FILNAVN).exists()) try {
data = (Data) Serialisering.hent(FILNAVN);
if (data.hentetStatusFraSlug == null) { // Feltet data.udsendelser kom med 26. dec 2015 - tjek kan slettes efter sommer 2016
data.hentetStatusFraSlug = new LinkedHashMap<>();
}
if (data.udsendelser == null) { // Feltet data.udsendelser kom med 2. okt 2014 - tjek kan slettes efter sommer 2015
data.udsendelser = new ArrayList<Udsendelse>(data.udsendelseFraDownloadId.values());
}
// Sæt korrekt hentetStream på alle hentede udsendelser
for (Udsendelse serialiseretUds : data.udsendelser) {
Udsendelse u = DRData.instans.udsendelseFraSlug.get(serialiseretUds.slug);
if (u==null) {
// Serialiserede udsendelser skal med i slug-listen
DRData.instans.udsendelseFraSlug.put(serialiseretUds.slug, serialiseretUds);
tjekOmHentet(serialiseretUds);
} else {
tjekOmHentet(u);
}
}
return;
} catch (Exception e) {
Log.rapporterFejl(e);
}
data = new Data();
gemListe(); // For at undgå at fejl rapporteres mere end 1 gang
}
private void gemListe() {
try {
long tid = System.currentTimeMillis();
Serialisering.gem(data, FILNAVN);
Log.d("Hentning: Gemning tog " + (System.currentTimeMillis() - tid) + " ms - filstr:" + new File(FILNAVN).length());
} catch (IOException e) {
Log.rapporterFejl(e);
}
}
public void hent(Udsendelse udsendelse) {
tjekDataOprettet();
try {
List<Lydstream> prioriteretListe = udsendelse.findBedsteStreams(true);
if (prioriteretListe == null || prioriteretListe.size() < 1) {
Log.rapporterFejl(new IllegalStateException("ingen streamurl"), udsendelse.slug);
App.langToast(R.string.Beklager_udsendelsen_kunne_ikke_hentes);
return;
}
Uri uri = Uri.parse(prioriteretListe.get(0).url);
File dir = findPlaceringAfHentedeFilerFraPrefs();
Log.d("Hent uri=" + uri +" til "+dir);
dir = new File(dir, App.instans.getString(R.string.HENTEDE_UDS_MAPPENAVN));
dir.mkdirs();
if (!dir.exists()) throw new IOException("kunne ikke oprette " + dir);
File destination = new File(dir, udsendelse.slug.replace(':','_') + ".mp3");
String externalPath = Environment.getExternalStorageDirectory().getAbsolutePath();
if (!dir.getPath().startsWith(externalPath)) {
dir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PODCASTS);
dir.mkdirs();
Log.d("DownloadManager kan ikke direkte hente til "+destination+"\nGem midlertidigt i "+dir);
if (!App.PRODUKTION) App.langToast("DownloadManager kan ikke direkte hente til\n"+destination+".\n\nGem midlertidigt i\n"+dir);
}
int typer = App.prefs.getBoolean("hentKunOverWifi", false) ?
DownloadManager.Request.NETWORK_WIFI :
DownloadManager.Request.NETWORK_WIFI | DownloadManager.Request.NETWORK_MOBILE;
DownloadManager.Request req = new DownloadManager.Request(uri)
.setAllowedNetworkTypes(typer)
.setAllowedOverRoaming(false)
.setTitle(udsendelse.titel)
.setDescription(udsendelse.beskrivelse);
req.setDestinationUri(Uri.fromFile(new File(dir, destination.getName())));
if (Build.VERSION.SDK_INT >= 11) req.allowScanningByMediaScanner();
long downloadId = downloadService.enqueue(req);
HentetStatus hs = new HentetStatus();
hs.destinationFil = destination.getPath();
data.hentetStatusFraSlug.put(udsendelse.slug, hs);
data.downloadIdFraSlug.put(udsendelse.slug, downloadId);
data.udsendelseFraDownloadId.put(downloadId, udsendelse);
if (!data.udsendelser.contains(udsendelse)) data.udsendelser.add(udsendelse);
Log.d("Hentning: hent() data.udsendelseFraDownloadId= " + data.udsendelseFraDownloadId);
Log.d("Hentning: hent() data.downloadIdFraSlug=" + data.downloadIdFraSlug);
gemListe();
for (Runnable obs : new ArrayList<Runnable>(observatører)) obs.run();
} catch (Exception e) {
Log.rapporterFejl(e);
App.langToast(R.string.Kunne_ikke_få_adgang_til_eksternt_lager__se_evt__);
}
}
public static File findPlaceringAfHentedeFilerFraPrefs() {
String brugervalg = App.prefs.getString(NØGLE_placeringAfHentedeFiler, null);
File dir = null;
if (brugervalg != null) dir = new File(brugervalg);
if (dir == null || !dir.canWrite()) {
dir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PODCASTS);
}
return dir;
}
/** Sletter udsendelsen, men viser den stadig på listen, hvis brugern vil hente den igen senere */
public void stop(Udsendelse u) {
tjekDataOprettet();
HentetStatus hs = getHentetStatus(u);
if (hs != null && hs.startUri!=null) new File(URI.create(hs.startUri)).delete(); // Hvis ikke hentet færdig endnu er hs.startUri==null
if (hs != null && hs.destinationFil!=null) new File(hs.destinationFil).delete();
data.hentetStatusFraSlug.remove(u.slug);
Long id = data.downloadIdFraSlug.remove(u.slug);
if (id == null) {
Log.d("stop() udsendelse " + u + " ikke i data.downloadIdFraSlug - den er nok allerede stoppet");
} else {
data.udsendelseFraDownloadId.remove(id);
downloadService.remove(id);
}
u.hentetStream = null;
gemListe();
for (Runnable obs : new ArrayList<Runnable>(observatører)) obs.run();
}
/** Sletter udsendelsen fuldstændigt fra listen */
public void slet(Udsendelse u) {
data.udsendelser.remove(u);
stop(u); // kald til sidst, da listen gemmes her
}
public static class DownloadServiceReciever extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
Log.d("HentedeUdsendelser DLS " + intent);
if (DownloadManager.ACTION_DOWNLOAD_COMPLETE.equals(action)) try {
long downloadId = intent.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID, 0);
DRData.instans.hentedeUdsendelser.tjekDataOprettet(); // Fix for https://mint.splunk.com/dashboard/project/cd78aa05/errors/803968027
final Udsendelse u = DRData.instans.hentedeUdsendelser.data.udsendelseFraDownloadId.get(downloadId);
if (u == null) {
Log.d("Ingen udsendelse for hentning for " + downloadId + " den er nok blevet slettet");
return;
}
DownloadManager.Query query = new DownloadManager.Query();
query.setFilterById(downloadId);
Cursor c = DRData.instans.hentedeUdsendelser.downloadService.query(query);
if (c.moveToFirst()) {
Log.d("HentedeUdsendelser DLS " + c + " " + c.getInt(c.getColumnIndex(DownloadManager.COLUMN_STATUS)));
if (DownloadManager.STATUS_SUCCESSFUL == c.getInt(c.getColumnIndex(DownloadManager.COLUMN_STATUS))) {
App.langToast(App.instans.getString(R.string.Udsendelsen___blev_hentet, u.titel));
Log.registrérTestet("Hente udsendelse", u.slug);
final HentetStatus hs = DRData.instans.hentedeUdsendelser.getHentetStatus(u);
hs.startUri = c.getString(c.getColumnIndex(DownloadManager.COLUMN_LOCAL_URI));
final File hentet = new File(URI.create(hs.startUri));
final File dest = new File(hs.destinationFil);
if (!hentet.equals(dest)) {
Log.d("HentedeUdsendelser flytter fil fra " + hentet + " til " + hs.destinationFil);
if (App.fejlsøgning) App.kortToast("flytter fra\n" + hentet + " til\n" + hs.destinationFil);
dest.getParentFile().mkdirs();
hs.statusFlytningIGang = true;
if (App.fejlsøgning) hs.statustekst+="\n"+hentet + " til " + hs.destinationFil;
new AsyncTask() {
@Override
protected Object doInBackground(Object[] params) {
try {
FilCache.kopierOgLuk(new FileInputStream(hentet), new FileOutputStream(dest));
hentet.delete();
} catch (Exception e) {
e.printStackTrace();
App.langToast(App.instans.getString(R.string.Kunne_ikke_flytte__, u.titel, dest));
}
return null;
}
@Override
protected void onPostExecute(Object o) {
hs.statusFlytningIGang = false;
hs.statustekst = lavStatustekst(hs);
DRData.instans.hentedeUdsendelser.gemListe();
for (Runnable obs : new ArrayList<Runnable>(DRData.instans.hentedeUdsendelser.observatører)) obs.run();
}
}.execute();
}
} else {
App.langToast(App.instans.getString(R.string.Det_lykkedes_ikke_at_hente_udsendelsen___tjek_at___, u.titel));
}
}
c.close();
DRData.instans.hentedeUdsendelser.gemListe();
for (Runnable obs : new ArrayList<Runnable>(DRData.instans.hentedeUdsendelser.observatører)) obs.run();
Sidevisning.vist(HentedeUdsendelser.class, u.slug);
} catch (Exception e) {
Log.rapporterFejl(e);
}
else if (DownloadManager.ACTION_NOTIFICATION_CLICKED.equals(action)) {
// Åbn app'en, under hentninger
if (App.aktivitetIForgrunden instanceof FragmentActivity) {
// Skift til Hentede_frag
try {
FragmentManager fm = ((FragmentActivity) App.aktivitetIForgrunden).getSupportFragmentManager();
FragmentTransaction ft = fm.beginTransaction();
ft.replace(R.id.indhold_frag, new Hentede_udsendelser_frag());
ft.addToBackStack("Hentning");
ft.commit();
} catch (Exception e1) {
Log.rapporterFejl(e1);
}
} else {
// Åbn hovedaktivitet
Intent i = new Intent(context, Hovedaktivitet.class)
.putExtra(Hovedaktivitet.VIS_FRAGMENT_KLASSE, Hentede_udsendelser_frag.class.getName());
i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(i);
}
Sidevisning.vist(HentedeUdsendelser.class);
/*
Intent dm = new Intent(DownloadManager.ACTION_VIEW_DOWNLOADS);
dm.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(dm);
*/
}
}
}
/**
* Finder stien til et eksternt SD-kort - altså ikke til den 'external storage' der fra Android 4.2
* oftest er intern.
* Se også http://source.android.com/devices/tech/storage/,
* http://stackoverflow.com/questions/13646669/android-securityexception-destination-must-be-on-external-storage og
* http://www.androidpolice.com/2014/02/17/external-blues-google-has-brought-big-changes-to-sd-cards-in-kitkat-and-even-samsung-may-be-implementing-them/
* @return en liste af stier, hvor en af dem muligvis er til et eksternt SD-kort
*/
public static ArrayList<File> findMuligeEksternLagerstier() {
// Hjælpemetode til at tjekke
class Res {
LinkedHashMap<File, File> res = new LinkedHashMap<File, File>();
public void put(File dir) {
if (dir == null) return;
File nøgle = dir;
try {
nøgle = nøgle.getCanonicalFile();
} catch (IOException e) {
e.printStackTrace();
}
if (!res.containsKey(nøgle)) {
// Se om der er en mappe, eller vi kan lave en
boolean fandtesFørMkdirs = dir.exists();
dir.mkdirs();
if (dir.isDirectory()) res.put(nøgle, dir);
if (!fandtesFørMkdirs) dir.delete(); // ryd op
}
}
}
Res res = new Res();
if (Build.VERSION.SDK_INT>=19) try {
for (File f : App.instans.getExternalFilesDirs(Environment.DIRECTORY_PODCASTS)) {
res.put(f);
}
} catch (Exception e) { Log.rapporterFejl(e); }
else {
res.put(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PODCASTS));
}
File fstab = new File("/etc/vold.fstab"); // læs i vold.fstab hvor der t.o.m Android 4.2 er nævnt det rigtige SD-kort
if (fstab.canRead()) {
try {
Scanner scanner = new Scanner(fstab);
while (scanner.hasNext()) {
String s = scanner.nextLine().trim();
if (s.startsWith("dev_mount")) {
// dev_mount sdcard /mnt/sdcard auto /devices/platform/goldfish_mmc.0 /devices/platform/msm_sdcc.2/mmc_host/mmc1
String sti = s.split("\\s")[2]; // /mnt/sdcard
Log.d("findStiTilRigtigtSDKort - fandt " + sti);
res.put(new File(sti, Environment.DIRECTORY_PODCASTS));
}
}
scanner.close();
} catch (Exception e) {
Log.rapporterFejl(e);
}
}
Log.d("findMuligeEksternLagerstier: " + res.res);
ArrayList<File> liste = new ArrayList<File>(res.res.values());
return liste;
}
}