Java tutorial
/** 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.diverse; /** * * @author j */ import android.annotation.SuppressLint; import android.annotation.TargetApi; import android.app.Activity; import android.app.AlertDialog; import android.app.Application; import android.app.NotificationManager; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.content.IntentFilter; import android.content.SharedPreferences; import android.content.pm.ApplicationInfo; import android.content.pm.PackageInfo; import android.content.res.Resources; import android.graphics.Typeface; import android.media.AudioManager; import android.net.ConnectivityManager; import android.net.NetworkInfo; import android.net.Uri; import android.net.http.AndroidHttpClient; import android.os.AsyncTask; import android.os.Build; import android.os.Handler; import android.preference.PreferenceManager; import android.view.accessibility.AccessibilityManager; import android.widget.Toast; import com.android.volley.Network; import com.android.volley.Request; import com.android.volley.RequestQueue; import com.android.volley.toolbox.HttpClientStack; import com.android.volley.toolbox.HttpStack; import com.android.volley.toolbox.HurlStack; import com.androidquery.callback.BitmapAjaxCallback; import com.bugsense.trace.BugSenseHandler; import org.json.JSONObject; import java.io.File; import java.io.FileOutputStream; import java.util.Date; import java.util.LinkedHashMap; import dk.dr.radio.afspilning.Afspiller; import dk.dr.radio.afspilning.Fjernbetjening; import dk.dr.radio.akt.Basisaktivitet; import dk.dr.radio.data.DRData; import dk.dr.radio.data.Grunddata; import dk.dr.radio.data.Kanal; import dk.dr.radio.net.Diverse; import dk.dr.radio.net.Netvaerksstatus; import dk.dr.radio.net.volley.DrBasicNetwork; import dk.dr.radio.net.volley.DrDiskBasedCache; import dk.dr.radio.net.volley.DrVolleyResonseListener; import dk.dr.radio.net.volley.DrVolleyStringRequest; import dk.dr.radio.v3.BuildConfig; import dk.dr.radio.v3.R; public class App extends Application { public static final String P4_FORETRUKKEN_GT_FRA_STEDPLACERING="P4_FORETRUKKEN_GT_FRA_STEDPLACERING"; public static final String P4_FORETRUKKEN_AF_BRUGER = "P4_FORETRUKKEN_AF_BRUGER"; public static final String FORETRUKKEN_KANAL = "FORETRUKKEN_kanal"; public static final String NGLE_advaretOmInstalleretPSDKort="erInstalleretPSDKort"; public static final boolean PRODUKTION = !BuildConfig.DEBUG; private static final String DRAMA_OG_BOG__A__INDLST="DRAMA_OG_BOG__A__INDLST"; public static boolean EMULATOR = true; // St i onCreate(), ellers virker det ikke i std Java public static App instans; public static SharedPreferences prefs; public static ConnectivityManager connectivityManager; public static String versionsnavn = "(ukendt)"; public static NotificationManager notificationManager; public static AudioManager audioManager; public static boolean fejlsgning=false; public static Handler forgrundstrd; public static Typeface skrift_gibson; public static Typeface skrift_gibson_fed; public static Typeface skrift_georgia; public static Netvaerksstatus netvrk; public static Fjernbetjening fjernbetjening; public static RequestQueue volleyRequestQueue; public static boolean erInstalleretPSDKort; private DrDiskBasedCache volleyCache; public static EgenTypefaceSpan skrift_gibson_fed_span; public static DRFarver color; public static Resources res; /** Tidsstempel der kan bruges til at afgre hvilke filer der faktisk er brugt efter denne opstart */ private static long TIDSSTEMPEL_VED_OPSTART; public static AccessibilityManager accessibilityManager; private static SharedPreferences grunddata_prefs; @SuppressLint("NewApi") @Override public void onCreate() { TIDSSTEMPEL_VED_OPSTART = System.currentTimeMillis(); instans = this; netvrk = new Netvaerksstatus(); EMULATOR = Build.PRODUCT.contains("sdk") || Build.MODEL.contains("Emulator"); if (!EMULATOR) BugSenseHandler.initAndStartSession(this, getString(PRODUKTION ? R.string.bugsense_ngle : R.string.bugsense_testngle)); super.onCreate(); forgrundstrd = new Handler(); connectivityManager = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE); notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); audioManager = (AudioManager) App.instans.getSystemService(Context.AUDIO_SERVICE); prefs = PreferenceManager.getDefaultSharedPreferences(this); fejlsgning = prefs.getBoolean("fejlsgning", false); res = App.instans.getResources(); App.color = new DRFarver(); // HTTP-forbindelser havde en fejl pr froyo, men jeg har ogs set problemet p Xperia Play, der er 2.3.4 (!) if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) { System.setProperty("http.keepAlive", "false"); } String packageName = getPackageName(); try { if ("dk.dr.radio".equals(packageName)) { if (!PRODUKTION) App.langToast("St PRODUKTIONs-flaget"); } else { if (PRODUKTION) App.langToast("Testudgave - fjern PRODUKTIONs-flaget"); } //noinspection ConstantConditions PackageInfo pi = getPackageManager().getPackageInfo(packageName, 0); App.versionsnavn = packageName + "/" + pi.versionName; if (EMULATOR) App.versionsnavn += " EMU"; Log.d("App.versionsnavn=" + App.versionsnavn); App.erInstalleretPSDKort = 0!=(pi.applicationInfo.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE); /* check for API level 7 - check files dir try { String filesDir = context.getFilesDir().getAbsolutePath(); if (filesDir.startsWith("/data/")) { return false; } else if (filesDir.contains("/mnt/") || filesDir.contains("/sdcard/")) { return true; } } catch (Throwable e) { // ignore } */ if (!App.erInstalleretPSDKort) prefs.edit().remove(NGLE_advaretOmInstalleretPSDKort).commit(); Class.forName("android.os.AsyncTask"); // Fix for http://code.google.com/p/android/issues/detail?id=20915 } catch (Exception e) { Log.rapporterFejl(e); } accessibilityManager = (AccessibilityManager) getSystemService(Context.ACCESSIBILITY_SERVICE); // Initialisering af Volley // Prior to Gingerbread, HttpUrlConnection was unreliable. // See: http://android-developers.blogspot.com/2011/09/androids-http-clients.html HttpStack stack = Build.VERSION.SDK_INT >= 9 ? new HurlStack() : Build.VERSION.SDK_INT >= 8 ? new HttpClientStack(AndroidHttpClient.newInstance(App.versionsnavn)) // : new HttpClientStack(new DefaultHttpClient()); // Android 2.1 - : new HurlStack(); // Android 2.1 // HTTP connection reuse was buggy pre-froyo if (Build.VERSION.SDK_INT < Build.VERSION_CODES.FROYO) { System.setProperty("http.keepAlive", "false"); } // Vi bruger vores eget Netvrkslag, da DRs Varnish-servere ofte svarer med HTTP-kode 500, // som skal hndteres som et timeout og at der skal prves igen Network network = new DrBasicNetwork(stack); // Vi bruger vores egen DrDiskBasedCache, da den indbyggede i Volley // har en opstartstid p flere sekunder // Mappe ndret fra standardmappen "volley" til "dr_volley" 19. nov 2014. // Det skyldtes at et hukommelsesdump viste, at Volley indekserede alle filerne i standardmappen, // uden om vores implementation, hvilket gav et undvendigt overhead p ~ 1MB File cacheDir = new File(getCacheDir(), "dr_volley"); volleyCache = new DrDiskBasedCache(cacheDir); volleyRequestQueue = new RequestQueue(volleyCache, network); volleyRequestQueue.start(); // P4 stedplacering skal ske s tidligt som muligt - ellers // nr P4-valgskrmbilledet at blive instantieret med ukendt placering og foreslr derfor Kbenhavn if (prefs.getString(P4_FORETRUKKEN_GT_FRA_STEDPLACERING, null) == null) startP4stedplacering(); try { DRData.instans = new DRData(); DRData.instans.grunddata = new Grunddata(); // Indlsning af grunddata/stamdata. // Frst tjekkes om vi har en udgave i prefs, og ellers bruges den i raw-mappen // P et senere tidspunkt henter vi nye grunddata grunddata_prefs = App.instans.getSharedPreferences("grunddata", 0); String grunddata = grunddata_prefs.getString(DRData.GRUNDDATA_URL, null); if (grunddata == null) { grunddata = App.prefs.getString(DRData.GRUNDDATA_URL, null); if (grunddata!=null) { // 28 nov 2014 - flyt data fra flles prefs til separat fil - kan fjernes ultimo 2015 App.prefs.edit().remove(DRData.GRUNDDATA_URL).commit(); grunddata_prefs.edit().putString(DRData.GRUNDDATA_URL, grunddata).commit(); } } if (App.prefs.contains("stamdata23") || App.prefs.contains("stamdata24")) { // 24 feb 2015 - fjern gamle stamdata fra prefs - kan fjernes primo 2016 App.prefs.edit().remove("stamdata22").remove("stamdata23").remove("stamdata24").commit(); } if (grunddata == null) grunddata = Diverse.lsStreng(res.openRawResource(App.PRODUKTION ? R.raw.grunddata : R.raw.grunddata_udvikling)); DRData.instans.grunddata.parseFllesGrunddata(grunddata); if (App.fejlsgning && DRData.instans.grunddata.udelukHLS) App.kortToast("HLS er udelukket"); String pn = App.instans.getPackageName(); for (final Kanal k : DRData.instans.grunddata.kanaler) { k.kanallogo_resid = res.getIdentifier("kanalappendix_" + k.kode.toLowerCase().replace('', 'o').replace('', 'a'), "drawable", pn); } String kanalkode = prefs.getString(FORETRUKKEN_KANAL, null); // Hvis brugeren foretrkker P4 er vi ndt til at finde underkanalen kanalkode = tjekP4OgVlgUnderkanal(kanalkode); Kanal aktuelKanal = DRData.instans.grunddata.kanalFraKode.get(kanalkode); if (aktuelKanal == null || aktuelKanal == Grunddata.ukendtKanal) { aktuelKanal = DRData.instans.grunddata.forvalgtKanal; Log.d("forvalgtKanal=" + aktuelKanal); } if (!aktuelKanal.harStreams()) { // ikke && App.erOnline(), det kan vre vi har en cachet udgave final Kanal kanal = aktuelKanal; Request<?> req = new DrVolleyStringRequest(aktuelKanal.getStreamsUrl(), new DrVolleyResonseListener() { @Override public void fikSvar(String json, boolean fraCache, boolean undret) throws Exception { if (undret) return; // ingen grund til at parse det igen JSONObject o = new JSONObject(json); kanal.setStreams(o); Log.d("hentStreams akt fraCache=" + fraCache + " => " + kanal); } }) { public Priority getPriority() { return Priority.HIGH; }};App.volleyRequestQueue.add(req);} DRData.instans.afspiller=new Afspiller();DRData.instans.afspiller.setLydkilde(aktuelKanal); registerReceiver(netvrk,new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION));netvrk.onReceive(this,null); // F opdateret netvrksstatus fjernbetjening=new Fjernbetjening(); // udestendeInitialisering kaldes nr aktivitet bliver synlig frste gang // - muligvis aldrig hvis app'en kun betjenes via levende ikon }catch(Exception ex) { // Burde der vre popop-advarsel til bruger om intern fejl og rapporter til udvikler-dialog ? Log.rapporterFejl(ex); } try { // DRs skrifttyper er ikke offentliggjort i SVN, derfor kan flgende fejle: skrift_gibson = Typeface.createFromAsset(getAssets(), "Gibson-Regular.otf"); skrift_gibson_fed = Typeface.createFromAsset(getAssets(), "Gibson-SemiBold.otf"); skrift_georgia = Typeface.createFromAsset(getAssets(), "Georgia.ttf"); }catch( Exception e) { Log.e("DRs skrifttyper er ikke tilgngelige", e); skrift_gibson = Typeface.DEFAULT; skrift_gibson_fed = Typeface.DEFAULT_BOLD; skrift_georgia = Typeface.DEFAULT; }skrift_gibson_fed_span=new EgenTypefaceSpan("Gibson fed",App.skrift_gibson_fed); Log.d("onCreate tog "+(System.currentTimeMillis()-TIDSSTEMPEL_VED_OPSTART)+" ms");} public static String tjekP4OgVlgUnderkanal(String kanalkode) { if (Kanal.P4kode.equals(kanalkode)) { kanalkode = App.prefs.getString(App.P4_FORETRUKKEN_AF_BRUGER, null); if (kanalkode == null) kanalkode = App.prefs.getString(App.P4_FORETRUKKEN_GT_FRA_STEDPLACERING, "KH4"); Log.d("P4 underkanal=" + kanalkode); } return kanalkode; } public static void advarEvtOmAlarmerHvisInstalleretPSDkort(Activity akt) { if(App.erInstalleretPSDKort&&prefs.getBoolean(NGLE_advaretOmInstalleretPSDKort,false)){AlertDialog.Builder dialog=new AlertDialog.Builder(akt);dialog.setTitle("SD-kort");dialog.setIcon(R.drawable.dri_advarsel_hvid);dialog.setMessage("Vkning fungerer muligvis ikke altid, nr DR Radio er flyttet til SD-kort");dialog.setPositiveButton(android.R.string.ok,new AlertDialog.OnClickListener(){public void onClick(DialogInterface arg0,int arg1){prefs.edit().putBoolean(NGLE_advaretOmInstalleretPSDKort,true).commit();}});dialog.show();} } private void startP4stedplacering() { new AsyncTask(){@Override protected Object doInBackground(Object[]params){try{String p4kanal=P4Stedplacering.findP4KanalnavnFraIP();if(App.fejlsgning)App.langToast("p4kanal: "+p4kanal);if(p4kanal!=null)prefs.edit().putString(P4_FORETRUKKEN_GT_FRA_STEDPLACERING,p4kanal).commit(); //if (!App.PRODUKTION) Log.rapporterFejl(new Exception("Ny enhed - fundet P4-kanal " + p4kanal)); }catch(Exception e){e.printStackTrace();}return null;}}.execute(); } /** * Initialisering af resterende data. * Dette sker nr app'en er synlig og telefonen er online */ private Runnable onlineinitialisering = new Runnable() { int forsinkelse = 15000; @Override public void run() { if (!erOnline()) return; boolean frdig = true; Log.d("Onlineinitialisering starter efter " + (System.currentTimeMillis() - TIDSSTEMPEL_VED_OPSTART) + " ms"); if (App.netvrk.status == Netvaerksstatus.Status.WIFI) { // Tjek at alle kanaler har deres streamsurler for (final Kanal kanal : DRData.instans.grunddata.kanaler) { if (kanal.harStreams()) continue; // Log.d("run()1 " + (System.currentTimeMillis() - TIDSSTEMPEL_VED_OPSTART) + " ms"); Request<?> req = new DrVolleyStringRequest(kanal.getStreamsUrl(), new DrVolleyResonseListener() { @Override public void fikSvar(String json, boolean fraCache, boolean undret) throws Exception { if (undret) return; JSONObject o = new JSONObject(json); kanal.setStreams(o); Log.d("hentStreams app fraCache=" + fraCache + " => " + kanal); } }) { public Priority getPriority() { return Priority.LOW; } }; App.volleyRequestQueue.add(req);}} if(DRData.instans.favoritter.getAntalNyeUdsendelser()<0){frdig=false;DRData.instans.favoritter.startOpdaterAntalNyeUdsendelser.run();} if(!frdig){Log.d("Onlineinitialisering ikke frdig - prver igen om "+forsinkelse/1000+" sekunder");App.forgrundstrd.removeCallbacks(this);App.forgrundstrd.postDelayed(this,forsinkelse); // prv igen om 15 sekunder og se om alle data er klar der forsinkelse=15*forsinkelse/10;} if(prefs.getString(P4_FORETRUKKEN_GT_FRA_STEDPLACERING,null)==null){if(DRData.instans.grunddata.android_json.optBoolean("P4stedplacering",false)){frdig=false;startP4stedplacering();}else{prefs.edit().putString(P4_FORETRUKKEN_GT_FRA_STEDPLACERING,"defekt").commit();}} // Forsg at indlse Drama&Bog og alle kanaler A- n gang ved opstart // Der er givetvis en del der sjldent bruger disse funktioner, // og hvis telefonen tror den er online men man ikke kan f forbindelse, // kan der komme rigtig mange store anomdninger i k // - det gres kun n gang, hvilket skulle dkke de fleste scenarier // TODO den rigtige lsning burde vre at svarene for Drama&Bog og A- bliver hngende i cachen, tjekket her burde vre om de er i cachen eller ej if(frdig&&!prefs.getBoolean(DRAMA_OG_BOG__A__INDLST,false)){prefs.edit().putBoolean(DRAMA_OG_BOG__A__INDLST,true);frdig=false;DRData.instans.dramaOgBog.startHentData();DRData.instans.programserierAtil.startHentData();}if(frdig){netvrk.observatrer.remove(this); // Hold ikke mere je med om vi kommer online onlineinitialisering=null;Log.d("Onlineinitialisering frdig");}}}; public static Runnable hentEvtNyeGrunddata = new Runnable() { long sidstTjekket = 0; @Override public void run() { if (!App.erOnline()) return; if (sidstTjekket + (App.EMULATOR ? 1000 : DRData.instans.grunddata.opdaterGrunddataEfterMs) > System.currentTimeMillis()) return; sidstTjekket = System.currentTimeMillis(); Log.d("hentEvtNyeGrunddata " + (sidstTjekket - App.TIDSSTEMPEL_VED_OPSTART)); Request<?> req = new DrVolleyStringRequest(DRData.GRUNDDATA_URL, new DrVolleyResonseListener() { @Override public void fikSvar(String nyeGrunddata, boolean fraCache, boolean undret) throws Exception { if (undret || fraCache) return; // ingen grund til at parse det igen String gamleGrunddata = grunddata_prefs.getString(DRData.GRUNDDATA_URL, null); if (nyeGrunddata.equals(gamleGrunddata)) return; // Det samme som var i prefs Log.d("Vi fik nye grunddata: fraCache=" + fraCache + nyeGrunddata); if (!PRODUKTION || App.fejlsgning) App.kortToast("Vi fik nye grunddata"); DRData.instans.grunddata.parseFllesGrunddata(nyeGrunddata); String pn = App.instans.getPackageName(); for (final Kanal k : DRData.instans.grunddata.kanaler) { k.kanallogo_resid = res.getIdentifier("kanalappendix_" + k.kode.toLowerCase().replace('', 'o').replace('', 'a'), "drawable", pn); } // fix for https://mint.splunk.com/dashboard/project/cd78aa05/errors/2774928662 for (Runnable r : DRData.instans.grunddata.observatrer) r.run(); // Er vi net hertil s gik parsning godt - gem de nye stamdata i prefs, s de ogs bruges ved nste opstart grunddata_prefs.edit().putString(DRData.GRUNDDATA_URL, nyeGrunddata).commit(); } }) { public Priority getPriority() { return Priority.LOW; } }; App.volleyRequestQueue.add(req); }}; /* * Kilde: http://developer.android.com/training/basics/network-ops/managing.html */ public static boolean erOnline() { NetworkInfo networkInfo = connectivityManager.getActiveNetworkInfo(); return (networkInfo != null && networkInfo.isConnected()); } public static Activity aktivitetIForgrunden = null; public static Activity senesteAktivitetIForgrunden = null; private static int erIGang = 0; private static LinkedHashMap<String, Integer> hvadErIGang = new LinkedHashMap<String, Integer>(); /** * Signalerer over for brugeren at netvrskommunikation er pbegyndt eller afsluttet. * Forrsager at det 'drejende hjul' (ProgressBar) vises p den aktivitet der er synlig p.t. * @param netvrkErIGang true for pbegyndt og false for afsluttet. */ public static synchronized void stErIGang(boolean netvrkErIGang, String hvad) { boolean fr = erIGang > 0; if (App.EMULATOR) { Integer antal = hvadErIGang.get(hvad); antal = (antal==null?0:antal) + (netvrkErIGang?1:-1); hvadErIGang.put(hvad, antal); if (antal>1) Log.d("stErIGang: "+hvad+" har "+antal+" samtidige anmodninger"); else if (antal<0) Log.e(new IllegalStateException("erIGang manglede " + hvad)); else if (netvrkErIGang) Log.d("stErIGang: "+hvad); } erIGang += netvrkErIGang ? 1 : -1; boolean nu = erIGang > 0; if (fejlsgning) Log.d("erIGang = " + erIGang); if (erIGang < 0) { if (App.EMULATOR) Log.e(new IllegalStateException("erIGang er " + erIGang + " hvadErIGang="+hvadErIGang)); erIGang = 0; } if (fr != nu && aktivitetIForgrunden != null) forgrundstrd.post(stProgressbar); // Fejltjek } private static Runnable stProgressbar = new Runnable() { public void run() { if (aktivitetIForgrunden instanceof Basisaktivitet) { ((Basisaktivitet) aktivitetIForgrunden).stProgressBar(erIGang > 0); } } }; public void aktivitetStartet(Activity akt) { senesteAktivitetIForgrunden=aktivitetIForgrunden=akt;stProgressbar.run();if(onlineinitialisering!=null){if(App.erOnline()){App.forgrundstrd.postDelayed(onlineinitialisering,250); // Initialisr onlinedata }else{App.netvrk.observatrer.add(onlineinitialisering); // Vent p at vi kommer online og lav s et tjek }}if(krFrsteGangAppIkkeMereErSynlig!=null)forgrundstrd.removeCallbacks(krFrsteGangAppIkkeMereErSynlig); } public void aktivitetStoppet(Activity akt) { if (akt != aktivitetIForgrunden) return; // en anden aktivitet er allerede startet aktivitetIForgrunden = null; if (krFrsteGangAppIkkeMereErSynlig != null) forgrundstrd.postDelayed(krFrsteGangAppIkkeMereErSynlig, 1000); } /** * Kres et sekund efter at app'en ikke mere er synlig. * Her rydder vi op i filer */ private Runnable krFrsteGangAppIkkeMereErSynlig = new Runnable() { @Override public void run() { if (aktivitetIForgrunden != null) return; if (App.fejlsgning) App.kortToast("krFrsteGangAppIkkeMereErSynlig"); final int DAGE = 24 * 60 * 60 * 1000; int volleySlettet = volleyCache.sletFilerldreEnd(TIDSSTEMPEL_VED_OPSTART-10*DAGE); int aqSlettet = Diverse.sletFilerldreEnd(new File(getCacheDir(), "aquery"), TIDSSTEMPEL_VED_OPSTART-4*DAGE); // Mappe ndret fra standardmappen "volley" til "dr_volley" 19. nov 2014. // Det skyldtes at et hukommelsesdump viste, at Volley indekserede alle filerne i standardmappen, // uden om vores implementation, hvilket gav et undvendigt overhead p ~ 1MB File gammelVolleyCacheDir = new File(getCacheDir(), "volley"); Diverse.sletFilerldreEnd(gammelVolleyCacheDir, TIDSSTEMPEL_VED_OPSTART-7*DAGE); if (fejlsgning) { App.kortToast("volleyCache: " + volleySlettet / 1000 + " kb frigivet"); App.kortToast("AQ: " + aqSlettet / 1000 + " kb kunne frigivet"); } krFrsteGangAppIkkeMereErSynlig = null; } }; private static Toast forrigeToast; public static void langToast(String txt) { Log.d("langToast("+txt);if(aktivitetIForgrunden==null)txt="DR Radio:\n"+txt;final String txt2=txt;forgrundstrd.post(new Runnable(){@Override public void run(){ // lange toasts br blive hngende if(forrigeToast!=null&&forrigeToast.getDuration()==Toast.LENGTH_SHORT&&!App.fejlsgning&&!App.EMULATOR)forrigeToast.cancel();forrigeToast=Toast.makeText(instans,txt2,Toast.LENGTH_LONG);forrigeToast.show();}}); } public static void kortToast(String txt) { Log.d("kortToast("+txt);if(aktivitetIForgrunden==null)txt="DR Radio:\n"+txt;final String txt2=txt;forgrundstrd.post(new Runnable(){@Override public void run(){ // lange toasts br blive hngende if(forrigeToast!=null&&forrigeToast.getDuration()==Toast.LENGTH_SHORT&&!App.fejlsgning&&!App.EMULATOR)forrigeToast.cancel();forrigeToast=Toast.makeText(instans,txt2,Toast.LENGTH_SHORT);forrigeToast.show();}}); } public static void kortToast(int resId) { kortToast(instans.getResources().getString(resId)); } public static void langToast(int resId) { langToast(instans.getResources().getString(resId)); } public static void kontakt(Activity akt, String emne, String txt, String vedhftning) { String[] modtagere; try { modtagere = Diverse.jsonArrayTilArrayListString(DRData.instans.grunddata.android_json.getJSONArray("kontakt_modtagere")).toArray(new String[0]); } catch (Exception ex) { Log.e(ex); modtagere = new String[]{"jacob.nordfalk@gmail.com"}; } Intent i = new Intent(Intent.ACTION_SEND); i.setType("plain/text"); i.putExtra(Intent.EXTRA_EMAIL, modtagere); i.putExtra(Intent.EXTRA_SUBJECT, emne); android.util.Log.d("KONTAKT", txt); if (vedhftning != null) try { String logfil = "programlog.txt"; @SuppressLint("WorldReadableFiles") FileOutputStream fos = akt.openFileOutput(logfil, akt.MODE_WORLD_READABLE); fos.write(vedhftning.getBytes()); fos.close(); Uri uri = Uri.fromFile(new File(akt.getFilesDir().getAbsolutePath(), logfil)); txt += "\n\nRul op verst i meddelelsen og giv din feedback, tak."; i.putExtra(Intent.EXTRA_STREAM, uri); } catch (Exception e) { Log.e(e); txt += "\n" + e; } i.putExtra(Intent.EXTRA_TEXT, txt); // akt.startActivity(Intent.createChooser(i, "Send meddelelse...")); try { akt.startActivity(i); } catch (Exception e) { App.langToast(e.toString()); Log.rapporterFejl(e); } } @Override public void onLowMemory() { // Ryd op nr der mangler RAM BitmapAjaxCallback.clearCache(); super.onLowMemory(); } @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH) @Override public void onTrimMemory(int level) { if (level >= TRIM_MEMORY_BACKGROUND) BitmapAjaxCallback.clearCache(); super.onTrimMemory(level); } /** * I fald telefonens ur gr forkert kan det ses her - alle HTTP-svar bliver jo stemplet med servertiden */ private static long serverkorrektionTilKlienttidMs = 0; /** * Giver et aktuelt tidsstempel p hvad serverens ur viser * @return tiden, i millisekunder siden 1. Januar 1970 00:00:00.0 UTC. */ public static long serverCurrentTimeMillis() { return System.currentTimeMillis() + serverkorrektionTilKlienttidMs; } public static void stServerCurrentTimeMillis(long servertid) { long serverkorrektionTilKlienttidMs2 = servertid - System.currentTimeMillis(); if (Math.abs(App.serverkorrektionTilKlienttidMs - serverkorrektionTilKlienttidMs2) > 120 * 1000) { Log.d("SERVERTID korrigerer tid med " + ((serverkorrektionTilKlienttidMs2 + App.serverkorrektionTilKlienttidMs) / 1000 / 60) + " minutter fra " + new Date(serverCurrentTimeMillis()) + " til " + new Date(servertid)); App.serverkorrektionTilKlienttidMs = serverkorrektionTilKlienttidMs2; new Exception("SERVERTID korrigeret med " + serverkorrektionTilKlienttidMs2 / 1000 / 60 + " min til " + new Date(servertid)).printStackTrace(); } } /** Kan kaldet til at afgre om vi er igang med at teste noget fra en main()-metode eller app'en rent faktisk krer */ public static boolean testFraMain() { return instans == null; } /** * Lille klasse der holder nogle farver vi ikke gider sl op i resurser efter hele tiden */ public static class DRFarver { public int gr40 = res.getColor(R.color.gr40); public int bl = res.getColor(R.color.bl); public int gr60 = res.getColor(R.color.gr60); } }