Starcraft 2, Casten, Domotica, Android & Systeembeheer?!

Door Wiebbe op dinsdag 29 maart 2011 13:37 - Reacties (6)
Categorie: Random, Views: 3.415

De Intro

Ja, je leest het goed. Wat hebben Starcraft 2, Casten, Domotica, Android en Systeembeheer samen? Dat ze allemaal gaan over punten waar ik altijd mee bezig ben!
De Catch
Ja, heel flauw.. ik weet het, maar je moet toch wat om mensen je Blog te laten lezen? Het is nu bijna precies 1 jaar geleden dat ik begon te mekkeren over Macbooks in een Windows AD omgeving. En het is weer een extra jaar geleden sinds ik begon met Android programmeren (iets met eierwekkers enzo!)
Android!
Sinds vorig jaar zijn er toch altijd weer leuke dingen gebeurd! Android Treintijden is verkozen tot applicatie van 2010 door tweakers gebruikers. Daar was ik uiteraard erg blij mee, wel is het jammer dat Treintijden nu zo achterloopt op de rest van alle apps. De applicatie heeft een verouderde core voor de verwerking van de NS website en hij heeft een sterk verouderde en saai uiterlijk! De meeste apps zijn nu helemaal strak en netjes vormgegeven!

Toch staat het op de planning om TT 3.0 ooit nog uit te brengen. Complete rewrite van de engine, hele nieuwe interface en nieuwe betere features! Het klinkt altijd leuk, maar of het er gaat komen? Goede vraag, weinig tijd en zoveel te doen... Wel staat er wellicht nog wat op de planning voor het geven van Android workshops, maar misschien later daar nog meer over!

Starcraft 2

Op dit spel heb ik gewoon echt jaaaaaaaaaaaaren gewacht. Ik heb altijd al een echte RTS gamer geweest en Starcraft 2 moest voor mij de game worden die alles ging bieden. Is dat gelukt? Ja, op zich wel. Heb ondertussen iets van 650+ games erop zitten, gecombineerd 1vs1, 2vs2 en 3vs3 met wat vrienden. En ben (naar mijn eigen idee) ergens geranked in lage master.

Aangezien ik weinig tijd heb, zit ik nu nog steeds vast in Diamond, maar ik moet wel al tegen Diamond 3500+ mensen en lage masters speler. Volgens mij staat ook de ladder cast nu dus ik kan toch niet omhoog.
Casten!
Wat veel mensen niet weten is dat ik ongeveer 5/6 jaar lang warcraft III games heb gecast op Live en Offline events. Via de toen nog Netgamez Radio en op veeeel offline events (Lans ed) heb ik alle wedstrijden (meestal alleen) gecast.

Eigenlijk wil ik ook een goede cast gaan opzetten voor Starcraft 2. Naar mijn idee is er nog niet echt een goed channel voor Nederlandse casting van Starcraft 2. Het zal waarschijnlijk een soort combinatie worden van HuskyStarcraft en Day[9]. Nou is mijn kennis echt never nooit niet zo goed als die van Day[9] maar wel beter dan die van Husky. Dus wellicht kan ik daar ergens tussen in gaan zitten..

De vraag is hoe ik dat ga doen, youtube? Justin.tv? Posten op fora ed? Me aanbieden voor websites? Ik weet het allemaal niet! Ook de tijd die ik nodig heb is natuurlijk shaars tegenwoordig ;)

En dan nog, is er wel vraag naar? Of heb ik dan 10 views per week? Of moet ik het gewoon doen omdat ik het leuk vind om te doen? Misschien probeer ik of ik de games van aankomend tweakers tournooi mag casten naar de 2 al aanwezige casters..

Domotica

Ook zit ik als tweaker altijd naar nieuwe dingen te zoeken die ik kan aanpassen/tweaken. Ik heb nu al een jaartje een echte HTPC, met een AnySee eraan hangen. Heb dus nog maar 1 apparaat voor DVD,TV en Films/Series die ik download met CouchPotato/SickBeard en SABNZBd. Perfecte opstelling en mijn vrouw en kinderen willen ook echt niet meer zonder!

Maar.. ik wil meer! Ik wil eigenlijk al mijn lichten en verwarming koppelen aan mijn server. Zodat ik via een website ergens touchscreen schermen kan ophangen om mijn huis te biedenen als ik thuis ben of als ik zelfs op vakantie ben..

Kost wel aardig wat geld, en als je net begint zijn er zoveeeeel systemen die met verschillende protocollen werken dat ik echt niet meer weet waar ik moet beginnen..

Je hebt X10, KaKu en HomeMatic en nog meer dingen. Allemaal andere prijzen en andere onderdelen..

Het mooie aan HomeMatic is dat er standaard een PC controller is die je kan kopen. Kost maar 30 euro ofzo maar je kan alles bedienen.. Nadeel? Software is in het duits!

KaKu heeft nu wel een USB achtig apparaat, maar of het werkt? Voor §179 vind ik het een dure aanschaf als het niet werkt..

X10 is leuk en aardig, maar niet bi-directioneel en werkt niet altijd. Als je een beetje vrouwen kent weet je dat verbeteringen in het huis qua techniek aardig zijn, maar ze moeten wel altijd werken.

Keuze's Keuze's!

Beheer

Nu heb ik lekker geen zin meer om het ook nog over Beheer te gaan hebben. Dat is sowieso meer mijn werk en niet mijn hobby dus dat bewaar ik voor een andere keer ;)

Als er een macbook over de dam is...

Door Wiebbe op zondag 4 april 2010 10:07 - Reacties (40)
Categorie: Random, Views: 5.731

Zo, hier ben je dan opeens weer na bijna 1 jaar lang. Qua Android ontwikkeling is het best hard gegaan, na mijn "simpele" tutorials ben ik verder gegaan met Android T-Belstatus en daarna met Treintijden. Ondanks dat, wil ik het niet hebben over Android of zelfs over Java!

Sinds mijn vorige post ben ik begonnen als docent Informatica op een middelbare school. Erg leuk werk om kids van 15/16 (ook al mag ik ze van hun geen kids meer noemen, ze zitten tenslotte al in 4/5Havo ;) ) te leren wat programmeren nou een beetje inhoudt en om ze alvast een basis mee te geven voor het HBO.

Maar naast het lesgeven doe ik ook aan systeembeheer/applicatiebeheer/werkplekbeheer, echt zo'n mengelmoesje aan beheer "dingen". En het verhaal waar ik het nu over ga hebben is een situatie die mij vreselijk irriteert!

Blijkbaar is het zo in het onderwijs (ondanks de financiŽle crisis ed :P) dat er toch nog overal potjes met geld liggen. Zo ook dus bij mij op school. Met dit potje met geld is besloten om voor al het personeel een regeling aan te bieden:

Elk personeels lid mag een keuze maken tussen een fiets of een laptop op kosten van de school. Deze worden uiteraard alleen maar zakelijk gebruikt ;). Aangezien ons gehele netwerk draait op Windows was de keuze voor een laptop met Windows nogal snel gemaakt. Wij hebben op alle workstations Windows XP (met uitzondering van mijn werkbak, die heeft Windows 7 aangezien beheren van Windows 2008 irritant is met de oude RSAT tools!) en 99% van mijn collega docenten hebben thuis en op school alleen met Windows XP gewerkt.

Qua budget is er 749 euro per docent gereserveerd, ruim voldoende voor een lekkere laptop waar je goed op kan officen, powerpointen en nog meer simpele dingen.

Maar aangezien mensen een gegeven paard altijd in de bek moeten kijken, krijg je bij een aantal alternatievelingen gezeik dat het een Windows laptop is geworden.. Goed, ik kan het me voorstellen dat je graag een macbooc wil hebben als je thuis en qua werk alleen met Mac's werkt. Het zijn vooral mensen in de techniek/muziek die een Apple willen hebben, iets wat ook komt door de software die daar beschikbaar is.

Helaas is het zo dat die mensen ook meteen de leken van de school gaan proberen te overtuigen dat een Apple toch echt beter is dan Windows. Dus wat gebeurt er nu?

Na het mailtje over de keuze, krijg je nu een "Allen Beantwoorden" mail wisseling over de gehele school over de Apple laptops. En dan krijg je (logischerwijs) vragen als:
Is het heel moeilijk en kostbaar de programma's die we nodig hebben, Office 2007 en toegang tot het school domein te installeren?
Ja leuk.. Maar nu gaan dus mensen die nog nooit met Apple hebben gewerkt kiezen voor een Apple? Ik heb ze allemaal heel duidelijk gemaakt dat de helpdesk op school ze 0% zal gaan helpen met het gebruik van hun Apple.. Maar aangezien door de Apple fanaten altijd wordt vertelt hoe simpel en snel Apple te gebruiken is moet dat wel perfect zijn :/

Ik heb nu al genoeg aanmeldingen van gebruikers die al niet hun wachtwoord in windows kunnen wijzigen aan de hand van de GPO (8 tekens, 1 hoofdletter, 1 kleine letter en een getal) en die gaan nu een totaal nieuw systeem gebruiken? Hoe goed Apple ook is, een drempel om het nieuwe systeem te leren is er altijd...

Maar goed, de keuze is gemaakt ,ze gaan voor de Apple. Als het aan mij had gelegen had ik de mensen niet een de keuze geboden, een Windows laptop/Fiets.. thats it! Maar helaas werkt het dus niet zo! Ik ben erg benieuwd wat voor prachtige situaties we gaan krijgen en of de "Apple boys" in school de ondersteuning gaan bieden voor de installatie/hulp van de software :P

Dit is trouwens geen Apple bash maar meer een "stomme" gebruikers bash. Ik ben zelf altijd een Windows man geweest en heb niks met Apple.. maar aan sommige dingen irriteer ik me dan mateloos -_-

Android, Eieren, Services en Actions?

Door Wiebbe op maandag 22 juni 2009 14:59 - Reacties (1)
CategorieŽn: android, java, Views: 3.418

Eier wekkers

Zoals lezers van mijn vorige 2 blogs misschien hebben gelezen ben ik nogal bezig met het schrijven van AppWidgets voor Android Cupcake. Waar het oorspronkelijk mee begon was eigenlijk een stopwatch/eier wekker timer voor het homescreen. Een klokje leek mij de meest logische stap!

Nou heb ik nu al 2 manieren besproken om een klokje weer te geven, de eerste is door elke seconde een update te verzenden, de tweede doormiddel van een Widget chronometer die zelf telt en zelfs ook nog secondes laat zien.

Maar dan kom ik nog steeds niet op een manier die handig is voor een eierwekker/countdown timer.

Als ik de eerste manier gebruik, dan zal ik het zeer makkelijk kunnen maken. Het grote nadeel is dat ongeacht of je bezig bent met een timer je ALTIJD elke seconde een update uitvoert. Is ontzettend zonde van de performance en je batterij!

De tweede manier is leuk, maar kan alleen naar boven tellen. Alhoewel ik het misschien negatief kan gebruiken (dus -01:00 opgeven) gaat me dat waarschijnlijk alleen maar nog meer problemen opleveren.

Dus wat nu?

Ik kon het me niet anders voorstellen dat de grote bij google hier niet aan gedacht zouden hebben. Als je kijkt naar bijvoorbeeld de AnalogClock AppWidget van het homescreen dan zie je dat hij precies gelijk loopt op de minuut, standaard is ingesteld op elke Android phone en ook niet je batterij leeg slurpt. Dus hoe zullen ze dat dan doen?

Android Intents/Actions

De Android ontwikkelaar hebben zogenaamde Intents ingebouwd. Intents kunnen op meerdere manieren gebruikt worden, voornamelijk worden ze gebruikt in een applicatie om een Action uit te voeren.

Je kan bijvoorbeeld de ACTION_VIEW intent gebruikt om een call naar buiten uit te voeren. Op het moment dat een application op de Android phone een Intent uitvoert, zal deze vaak ook een broadcastIntent doen en een systemwijd event starten waar je voor kan registreren deze te ontvangen.

Dit ontvangen gebeurd via een zogenaamde BroadcastReceiver. Deze registreer je, geeft aan met een filter op welke intents je wilt reageren en deze kan je hierna dan ontvangen.

Zoals mede-tweaker Snake al poste in een eerdere post, kan je een service registreren in Android. Op zich is dit niet nodig, alles wat we doen zou je gewoon in de application zelf kunnen doen.

Het registreren van de BroadCastReceiver en de callback die hij dan krijgt doormiddel van de Intents gaat als volgt:

Java:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
private BroadcastReceiver mClockInfoReceiver = new BroadcastReceiver() { 
            @Override 
            public void onReceive(Context context, Intent intent) {
                
                RemoteViews updateViews = null;
                
                 String action = intent.getAction(); 
                 if (Intent.ACTION_TIME_TICK.equals(action)) { 

                     Log.d(TAG, "ACTION_TIME_TICK Has been hit!");
                     
                     updateViews = new RemoteViews(context.getPackageName(), R.layout.widget);

                     final Calendar c = Calendar.getInstance();
                     
                     updateViews.setTextViewText(R.id.Klok, new StringBuilder().append(DateFormat.format("kk:mm", c)));
                     updateViews.setTextViewText(R.id.Datum, new StringBuilder().append(DateFormat.format("EE, dd MMM yy", c)));
                     
                     ComponentName thisWidget = new ComponentName(context, DigitaleKlokService.class);
                     AppWidgetManager manager = AppWidgetManager.getInstance(context);
                     manager.updateAppWidget(thisWidget, updateViews);
                 } 
             } 
       };



Zoals je hier ziet declareer ik hier een nieuwe variabele van het type BroadcastReceiver genaamd mClockInfoReceiver. In deze declaratie MOET je van eclipse de OnReceive method overriden. Deze method ontvangt de callback/broadcast die gedaan wordt door alle Intents in de gehele system.

Aangezien we deze maar 1x hoeven te registreren zou je in de OnEnabled in plaats van de OnUpdate van je AppWidgetProvider de receiver kunnen registreren:


Java:
1
2
3
4
5
6
7
8
9
public class DigitaleKlokService extends AppWidgetProvider {

    final static String TAG = "DigitaleKlokService";
    
        public void onEnabled(Context context) 
        {
            registerReceiver(mClockInfoReceiver, new IntentFilter(Intent.ACTION_TIME_TICK));
            
        }



Hiermee registreer je hem, hij start de receiver en poef, we wachten op alle results. Zoals je ook ziet heb ik de Intent ACTION_TIME_TICK geregistreerd, of op gefilterd. Als je kijkt op de Intent pagina zie je dat deze:
Broadcast Action: The current time has changed. Sent every minute.
Dat is inderdaad precies waar ik naar zocht! Er staat er nog veeeeeel meer, maar voor nu hebben we alleen deze nodig.

De oplossing die we nu hebben werkt, maar helaas niet goed. Op het moment dat we de receiver starten gaat jou application zitten wachten op de Intent ACTION_TIME_CHANGED. Terwijl hij dit doet vind de Android software dat hij niet respond en wil hem killen. Erg onhandig dus.

De oplossing hiervoor is om gebruik te maken van een Service.

Android Services

Services in Android doen veel hetzelfde als in Windows. Ze draaien op de achtergrond en doen daar updates of reageren op callbacks. In Android kan je ze dus gebruiken om updates uit te voeren zonder dat jou AppWidget een ANR krijgt.

Het gebruik hiervan is net zoals de rest van het hele Android programmeren, simpel!

Eerst moet je nog in je AndroidManifest.xml aangeven dat je gebruik maakt van een service, en als volgt:


XML:
1
<service android:name=".DigitaleKlokService$UpdateService" />



Hiermee geef je aan dat de service UpdateService heet en onderdeel is van de DigitaleKlokService class!

Om hem te kunnen gebruiken moeten we een nieuwe class aanmaken die we laten overerven van de Service class. Wij noemen hem UpdateService


Java:
1
public static class UpdateService extends Service {



Deze class staat overigens in de DigitaleKlokService klasse en we willen hem maar 1x gedeclareerd hebben. Vandaar de static. Je zou hem ook gewoon buiten je class kunnen laten, maar dit is zoals de Android chaps het hebben gedaan in hun eigen AppWidgets!

De service heeft eigenlijk maar 1 belangrijke method namelijk de OnStart() method. Deze method wordt aangeroepen op het moment dat we de service gaan starten. Het enige wat wij willen doen is dus een Broadcast receiver starten!


Java:
1
2
3
4
5
@Override
        public void onStart(Intent intent, int startId) {
        
            registerReceiver(mClockInfoReceiver, new IntentFilter(Intent.ACTION_TIME_TICK));
        }



hier maken we net als hiervoor de BroadCastReceiver aan zodat hij de tijd kan ontvangen.

Op het moment dat er nu een ACTION_TIME_TICK event binnen komt op de service, krijgt hij een schop en zal hij de widget gaan updaten. Een belangrijk punt is dat hij de update nu alleen maar uit voert voor de eerste widget die je plaatst.

Lees verder »

Android DigitaleKlok widget Redux

Door Wiebbe op zaterdag 20 juni 2009 09:13 - Reacties (6)
CategorieŽn: android, java, Views: 4.759

Het werkt niet :'(

Na mijn post van gister liep ik toch tegen een aantal punten aan waar ik niet tevreden mee was. Misschien heeft een van jullie het wel ook gemaakt, of zag hij meteen het probleem al met zijn leet-code ogen.

De refresh rate van de widget is 60.000 ms. Wat dus precies 1 minuut is. Op zich zou dat natuurlijk genoeg moeten zijn om de klok altijd goed te houden. Maar hier komt het probleem.

Stel nou dat de plugin wordt toegevoegd op 16:00:30. Je zal dan netjes zien staan: 16:00 in het klokje. Maar de timer wordt ook precies om deze tijd gestart, dus na 1 minuut als de timer afloopt zal hij de update geven.

Maar dit is dus het probleem, de "echte" tijd zal om prcies 16:01:00 omspringen en dit weergeven. Maar onze digitale klok laat pas de goede tijd zien als de update eraan komt, dus om 16:01:30 springt hij pas op 16:01!

Nu is dit natuurlijk makkelijk op te lossen door de tijd gewoon op 1 seconde te zetten, of door je Android phone precies om 16:00:00 aan te zetten 8)7. Maar met die eerste oplossing ben je de hele tijd updates aan het doen voor echt iets loos. Ten eerste laten we geen seconden zien, dus lekker loos om het zo vaak te updaten, en als we dat wel zouden doen, kost dit gewoon een stuk meer aan batterij verbruik voor iets wat eigenlijk niks doet!

De oplossing?

Natuurlijk kom ik hier met een oplossing. Het eerste wat ik ging doen is kijken hoe het analoge klokje als widget zijn werk doet. Die update namelijk ook netjes op de goede tijd! Maar de analoge klok widget is een android.widget.

Ja, lekker duidelijk inderdaan 8)7 . Alle "controls" die we in android gebruiken noemen ze widget. Dus je progressbar, TextView en Buttons zijn allemaal widgets. De widgets die we zien op ons homescreen noemen we AppWidgets.

Maar goed, de analoge klok is dus een AppWidget die de Widget AnalogClock laat zien. Dit is op zich best sneaky, want die control wordt op een slimme interne manier afgehandeld zonder requests te verzenden. De update timer staat dan ook op 0! wat dus betekend dat hij nooit een update doet.

Maar dit kan ik niet gebruiken voor een digitale klok, simpelweg omdat de analogclock widget altijd zo'n mooi klokje weergeeft.

Nu zou je denken, "jongen, zeik niet zo en schrijf een nieuwe control die inherit van TextView maar ook meteen een klok is die je net zo mooi update als de AnalogControl". Ja, precies, dacht ik ook. Maar helaas. De RemoteView (waar onze controls op staan in de AppWidget) staat maar een zeer beperkt aantal Widgets toe. Dit is volgens de Google Gurus gedaan om er voor te zorgen dat ze remote kunnen worden gebruikt
It's an intentional limitation. Only views that are implemented by the
framework can be used, and only those that are annotated as safe for
remoting.
I don't know if there is a list provided in the SDK docs, but as of 1.5 the
supported Views are:
AbsoluteLayout
AnalogClock
Button
Chronometer
FrameLayout
ImageButton
ImageView
LinearLayout
ProgressBar
RelativeLayout
TextView
(overgens in deze discussie staat er veel nuttige info voor developers, met opmerkingen door de google dudes: App widgets and remote views

Dus, we zitten met die Widgets. TextView hebben wel al gebruikt, en das niet echt nuttig, AnalogClock is ook niet bruikbaar. En toen kwamen we bij "Chronometer". Dit is echt one weird ass Widget al zeg ik het zelf!

Chronometer

De control lijkt eerst gewoon een soort stopwatch te zijn. Hij heeft de methods start()en stop().

Maar er zit een grote MAAR aan vast. De widget werkt eigenlijk op deze manier:

Je start de timer met, Chronometer.start(). Hij neemt een snapshot van de huidige SystemClock.elapsedRealtime() (wat de huidige uptime is van je gPhone) en gaat daar het verschil van nemen en laat die zien.

Dit is ook meteen het probleem. Stel dat je hem op pause zet met Chronometer.stop(). Je ziet de tijd stoppen (bijvoorbeeld 00:10:00). Als je hem 10 seconden later weer start, dan verwacht je dat hij verder gaat met 00:11:00, maar helaas!

De tijd die hij laat zien is 00:20:00, omdat hij als basis de "snapshot" neemt en aangezien de tijd is doorgelopen en nu dis 10 seconden later is, is die tijd ook groter geworden. Voor een stopwatch is het dus echt een knudde control.

Maar als klok? Gelukkig kan dat wel wat handiger, met wat "maaren" natuurlijk. De tijd die hij als basis neemt is dus de uptime, hoe kan je dit gebruiken om de klok weer te geven?

In principe is de "tijd" niets meer dan het aantal miliseconden sinds het begin van de dag, dus om 20-06-2009 00:00:00.

Stel dat we zelf de start tijd mogen aangeven van de chronometer en vanaf wanneer hij ging tellen. Dan hoeven we simpel we de timestamp van 20-06-2009 00:00:00 op te vissen mee te geven en te starten. Maar uiteraard werkt dat niet voor onze control.

De Chronometer heeft de functie setBase (long base). Met deze functie geeft je het verschil mee ten op zichte van SystemClock.elapsedRealtime() en wanneer hij moet gaan tellen.

Dus als je invoert -60000, dan trekt hij 1 minuut af van de SystemClock.elapsedRealtime() klok en laat die tijd dan zien.

Dus als we dan de goede tijd willen zien, moeten we dus zorgen dat de tijd waarop de chronometer "start" precies om 00:00:00 is, want dan krijgen we PRECIES de goede tijd! Dus elapsed tijd weten we, dus moeten we van de huidige tijd precies het aantal miliseconden tellen. Nou dan dit waarschijnlijk weer een stuk netter, maar ik ken niet alle libraries en dit werkt ;)


Java:
1
long mseconds = c.get(Calendar.HOUR_OF_DAY)  * 60 * 60 * 1000 + c.get(Calendar.MINUTE)  * 60 * 1000 + c.get(Calendar.SECOND)  * 1000 + c.get(Calendar.MILLISECOND);


Hiermee, krijgen we precies het huidige aantal miliseconden van de dag.

We willen dus het verschil tussen de elapsed tijd en de huidige tijd weten, dus trekken we van de elapsed tijd de huidige tijd af:


Java:
1
mseconds = SystemClock.elapsedRealtime() - mseconds;



Nu hebben we dus het aantal miliseconden wat we van SystemClock.elapsedRealtime() zouden moeten afhalen om precies op 00:00:00 uit te komen!

Als we dus dit (negatieve getal dus) mee geven aan de setBase() krijgen we precies de goede tijd te zien!

De gehele code van de DigitaleKlok/java:


Java:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
        final int N = appWidgetIds.length;
        final Calendar c = Calendar.getInstance();
        
        long mseconds = c.get(Calendar.HOUR_OF_DAY)  * 60 * 60 * 1000 + c.get(Calendar.MINUTE)  * 60 * 1000 + c.get(Calendar.SECOND)  * 1000  + c.get(Calendar.MILLISECOND);
        
        mseconds = SystemClock.elapsedRealtime() - mseconds;

        // Perform this loop procedure for each App Widget that belongs to this provider
        for (int i=0; i<N; i++) {
            int appWidgetId = appWidgetIds[i];

        // Get the layout for the App Widget and attach an on-click listener to the button
            RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.widget);
           
            views.setChronometer(R.id.Klok, mseconds, "%s", true);
            
            // Tell the AppWidgetManager to perform an update on the current App Widget
            appWidgetManager.updateAppWidget(appWidgetIds[0], views);

            views.setTextViewText(R.id.Datum,    new StringBuilder()
            .append(DateFormat.format("EE, dd MMM yy", c)));
            
            // Tell the AppWidgetManager to perform an update on the current App Widget
            appWidgetManager.updateAppWidget(appWidgetId, views);
        }



Het voordeel van deze manier is dat we nu eigenlijk nog maar elke uur een update hoeven uit te voeren. De interne afhandeling van de remote view is veel goedkoper dan de BroadcastReceiver voor updates en kost dus minder van de gebruikers batterij.

Het nadeel (of niet) is dat de Chronometer geen format functie heeft. De tijd is dus ALTIJD weergegeven op de manier van: MM:SS of H:MM:SS. Je kan gelukkig wel extra text meegeven.

Dus als we doen: setFormat ("0 %s"), wordt de %s vervangen met H:MM:SS en dus 0 wordt er voorgeplaatst. Met wat snelle ifjes kunnen we kijken of de tijd onder het uur is (dus na middernacht) en dan "00:" er aan vast te plakken of als het onder 10 uur is (met 1 getal dus) "0" er voor te plakken.

De datum kan tot een uur nu achter lopen om middernacht, aangezien de update precies om 11:59 kan vallen. Maar daar maak ik mij een stuk minder zorgen om dat de tijd die 1 minuut achter loopt en je batterij leeg vreet ;)

Android homescreen widgets bouwen

Door Wiebbe op vrijdag 19 juni 2009 09:59 - Reacties (11)
CategorieŽn: android, java, Views: 5.837

Android
Tis alweer een hele tijd geleden sinds ik gepost heb. Helaas is het onderhouden van een Blog een rare taak en als je niks te schrijven hebt (of iets interessants dan, want Faust kan er ook wat van :P) kan je beter ook maar niks neer zetten.

Sinds kort heb ik een Android G1 van T-mobile in mijn bezit. Ik heb hem niet geroot, maar je kan er nog steeds hele leuke dingen voor schrijven. Het eerste wat ik deed was hem updaten naar Cupcake 1.5.

Leuk, want nu kan je widgets op je homescreen plaatsen, wat het meteen een stuk uitgebreider maakt!

Maar nu komt het, ik vond hem in het nederlands wel prettig (ondanks dat ik al mijn WinMo toestellen in het engels had door de roms die ik flashde) maar volgens mij zijn bijna 99% van alle widgets in het engels gemaakt :(

Heb je een hele mooie digitale klok (DigitalClock Widget) te pakken, flikkert die neer op je home screen krijg je inderdaad een mooie digitale klok. Maar, de datum en dag van de week zijn in het engels :(

Ook een hele mooie SMS Counter, waarbij je het sms icoontje van berichten/messages vervangt met een icoon waarbij je een getal zit komen te staan als je nieuwe ongelezen berichten hebt. Leuk, maar hoe heet hij dan? Messages ipv Berichten. Nou kan je dit simpel oplossen door hem weer in het engels te zetten, of je schrijft gewoon alles zelf! (Zoals het een tweaker betaamt!)

Vreemd genoeg vind ik de documentatie over widgets bouwen redelijk beperkt, maar een simpele digitale klok moest toch wel lukken. En behold, hier is dan mijn digitale klok!

Eclipse
Ik schrijf alles in Eclipse en uiteraard in java. Nou programmeer ik zelf over het algemeen in PHP of C#. Tijdens mijn studie heb ik wel veel java gekregen (waardoor ik er een gruwelijke hekel aan gekregen heb ;)) maar het was roestig. Gelukkig lijkt het erg veel op C# en alle programmeer methodes zijn zowat precies hetzelfde. Ik hoefde eigenlijk niks op een andere manier te doen.

Maar ja, widgets! Een widget in Android wordt op een speciale manier gemaakt. (Vind ik dan!) Het gedeelte op het Homescreen is niks meer dan een frontend met een "RemoteView". Deze remote view doet op een door jou ingestelde update tijd een request naar een class die jij opgeeft. Die class wordt dan aangeroepen en kan de widget gaan updaten etc..

Ik zal hier gewoon simpel weg mijn klok laten zien. Anyway, on to the code!

In principe heb je maar een aantal bestanden nodig. Ik zal hier in het kort uitlichten wat we hebben:
  • DigitaleKlok.java
  • res/layout/main.xml
  • res/layout/widget.xml
  • res/xml.widget_info.xml
  • AndroidManifest
Het eerste wat we moeten aanpassen is de AndroidManifest.xml, hiermee laten we Android weten dat de applicatie een widget is en waar de widget moet kijken voor zijn update class.

XML:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
      package="com.wiebbe.DigitaleKlok"
      android:versionCode="1"
      android:versionName="1.0">
    <application android:icon="@drawable/icon" android:label="@string/app_name">
            <receiver android:name="DigitaleKlok" >
                <intent-filter>
                    <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
                </intent-filter>
                <meta-data android:name="android.appwidget.provider"
                           android:resource="@xml/widget_info" />
            </receiver>
    </application>
    <uses-sdk android:minSdkVersion="3" />
</manifest>


Zoals je ziet declareer ik hier een <receiver>. De name van de receiver geeft aan welke class moet worden aangeroepen die van het type AppWidgetProvider moet zijn.

Doormiddel van deze receiver en de intent filter declareer je dat hij moet reageren op de APPWIDGET_UPDATE action. Daarnaast geef je aan dat dat hij voor de appwidget.provider de widget_info.xml moet uitlezen.

In de widget_info.xml staat de volgende informatie:

XML:
1
2
3
4
5
6
7
<?xml version="1.0" encoding="utf-8"?>
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
    android:minWidth="144dip"
    android:minHeight="72dip"
    android:updatePeriodMillis="60000"
    android:initialLayout="@layout/widget"
    />



Hier mee geef je aan hoe groot hij is, volgens de android blog moet je het als volgt doen:

code:
1
Minimum size in dip = (Number of cells * 74dip) - 2dip



De update tijd is netjes in miliseconden, die nu dus staat op 60 seconden, elke minuut updaten.

Ook geven aan hoe hij eruit moeten te komen zien:

widget.xml

XML:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent">
<TextView  
    android:id="@+id/Klok"
    android:layout_width="144dp" 
    android:textSize="52dp"
    android:textStyle="bold"
    android:shadowColor="#000"
    android:shadowDx="1.1"
    android:shadowDy="1.1"
    android:text="tijd" 
    android:layout_gravity="center" 
    android:shadowRadius="1.1" android:gravity="top|center_horizontal" android:layout_height="60dp"/>
<TextView  
    android:id="@+id/Datum"
    android:layout_width="144dp" 
    android:textSize="20dp"
    android:textStyle="bold"
    android:shadowColor="#000"
    android:shadowDx="1.1"
    android:shadowDy="1.1"
    android:shadowRadius="1.1"
    android:text="datum"
    android:layout_gravity="center"
    android:gravity="center_horizontal"
    android:layout_height="28dp"/>
</LinearLayout>



Hiermee krijg je een 2x1 cells widget met daarin een grote tekst die opgebouwd is uit een grote tekst voor de tijd en een kleine tekst voor de datum.

Nu moeten we nog de DigitaleKlok class schrijven die dus een AppWidgetProvider moet overerven en moet reageren op de UPDATE intent van de receiver.


Java:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
package com.wiebbe.DigitaleKlok;

import java.util.Calendar;

import android.appwidget.AppWidgetManager;
import android.appwidget.AppWidgetProvider;
import android.content.Context;
import android.widget.RemoteViews;

public class DigitaleKlok extends AppWidgetProvider {

    private int mHour;
    private int mMinute;
    private int mMaand;
    private int mDag;
    private int mJaar;
    
    private static String pad(int c) {
        if (c >= 10)
            return String.valueOf(c);
        else
            return "0" + String.valueOf(c);
    }
    
    public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
        final int N = appWidgetIds.length;

        for (int i=0; i<N; i++) {
            int appWidgetId = appWidgetIds[i];

            RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.widget);

            // get the current time
            final Calendar c = Calendar.getInstance();
            mHour = c.get(Calendar.HOUR_OF_DAY);
            mMinute = c.get(Calendar.MINUTE);
            
            mMaand = c.get(Calendar.MONTH);
            mDag = c.get(Calendar.DATE);
            mJaar = c.get(Calendar.YEAR);
            
            views.setTextViewText(R.id.Klok,  new StringBuilder()
            .append(pad(mHour)).append(":")
            .append(pad(mMinute)));

            views.setTextViewText(R.id.Datum,  new StringBuilder().append(pad(mDag)).append("-").append(pad(mMaand)).append("-").append(pad(mJaar)));
            
            appWidgetManager.updateAppWidget(appWidgetId, views);
        }
    }
}



Zoals je hier ziet, zit er 1 functie (nou twee, maar eentje is gewoon om de tijd netjes te padden..) die voor de updates zorgt. De BroadcastReceiver geeft dus een seintje aan de DigitaleKlok class dat hij de OnUpdate moet draaien.

Omdat je meerdere widgets kan hebben die allemaal hier gebruik van maken zorg ik ervoor dat alle widget geupdate worden. Het kan veel netter natuurlijk, op zich zou ik niet de tijd/data elke keer moeten ophalen in de loop, maar whatever ;)

Als je deze code nu runt en de widget plaatst krijg je heel netjes een klok met datum in het beeld a la zo:

Digitale Klok widget

De code die ik heb gebruikt heb ik voornamelijk hier vandaan:

http://android-developers.blogspot.com/search/label/Widgets

Het gehele project vind je hierzo:

DigitaleKlokSrc.zip

[edit]
Aangezien we het natuurlijk deden om het nederlands te maken, heb ik deze localisation zooi erin geknalt en ik krijg nu een hele mooie nederlandse datum tekst.

Java:
1
2
views.setTextViewText(R.id.Datum,    new StringBuilder()
            .append(DateFormat.format("EE, dd MMM yy", c)));