Søg på DotNyt:
Denne blog er flyttet til www.nielsbrinch.com


torsdag den 28. august 2008

Applikationen kører ikke når telefonen er standby

skrevet af Niels Brinch

Titlen på dette indlæg, er et lille faktum man skal vide når man udvikler til mobiltelefoner. Når telefonen går standby, så kører applikationerne på telefonen ikke længere. CPU'en er simpelt hen slukket.

Det havde temmelig stor betydning for hvor godt min Timer-applikation fungerede. Den talte kun ned mens telefonen var i brug. Når jeg ikke brugte den og den gik i standby efter 1 minut, holdt den op med at tælle ned. Når telefonen aktiveres igen, er applikationerne stadig åbne og de er i den tilstand de var i da telefonen gik standby.

Hvad gør man så, hvis man som mig vil have telefonen til at reagere efter et stykke tid som er længere tid end telefonens standby-tid? Det kunne også være kontrol af en GPS-funktion hvert femte minut eller noget helt tredje jeg slet ikke kan forestille mig.

Der er to muligheder:

1. Aktivering via CeRunAppAtTime

Det er muligt at instruere sin telefon i at eksekvere en bestemt applikation på et bestemt tidspunkt, med følgende metode:

[DllImport("coredll")]
internal static extern bool CeRunAppAtTime(string pwszAppName, byte[] lpTime);

Den fungerer ok, men ikke perfekt. På min telefon er den i hvert fald ikke særligt præcis med hvornår den starter. Hvis jeg f.eks. sætter den til at starte om 10 sekunder, starter den bare med det samme. Upraktisk.

Et andet problem med den er, at den ikke kan starte/aktivere en applikation som allerede er startet. Derfor skal man starte en anden applikation, som enten udfører opgaven hurtigt (f.eks. afspiller alarm-lyden) og så lukker igen. Eller man skal lade den applikation der startes, sende en event til den anden applikation som allerede kører og som skal udføre arbejdet.

Alligevel er dette nok den mest fornuftige metode at anvende i de fleste tilfælde. Her er en god implementation:

http://www.rhinomobile.net/2007/03/run-application-at-time-and-time-change.html

2. Sørg for telefonen ikke går standby

Hvis telefonen ikke går standby, kører applikationerne videre hele tiden, alt imens telefonen bruger en masse strøm. Det er simpelt nok og fungerer ret godt at forhindre telefonen i at gå standby. Det gøres med følgende metode:

[DllImport("coredll.dll")]
public static extern void SystemIdleTimerReset();

Den metode skal bare kaldes efter mindre tid end den tid det tager før telefonen går standby. For nemheds skyld kalder jeg den bare hvert sekund.

 

Nyeste version, nu mere energislugende end nogensinde, kan downloades her: www.dotnyt.dk/timer.cab

0 kommentarer

onsdag den 27. august 2008

Installation af Smart Device applikation på Windows Mobile

skrevet af Niels Brinch

Jeg begynder at skrive dette blog-indlæg uden at vide om det faktisk vil lykkes mig at installere min lille Timer-applikation på min telefon. Jeg ved der måske er et problem i forhold til jeg har kodet den op mod .NET 3.5 og det er ikke installeret på min telefon.

Uanset hvad, skal jeg starte med at tilføje et nyt projekt til min solution, nemlig et Smart Device CAB Project.

image

Herefter bør man tage stilling til applikationens navn, som er en egenskab på selve det nye projekt. Du bør også ændre Manufacturer. Når applikationen senere er installeret, og du vil afinstallere, består applikationens navn af Manufacturer og ProductName til sammen.

For at vælge hvad det er du vil installere, skal du til din installer tilføje det projekt som indeholder din applikation. Det gør du i det skærmbillede som hedder File System. Hvis det ikke er synligt kan du højreklikke på dit installer-projekt og vælge View -> File System.

Sådan ser det ud når det er lykkedes.

image

Højreklik på Application Folder og Add -> Project Output

I dialogen der kommer frem skal du sikre dig din applikation er valgt i rullemenuen øverst. Det er den eneste valgmulighed, med mindre du har flere projekter i din solution.

image

Det er Primary Output, altså den øverste valgmulighed, som skal være valgt. Tryk OK.

Hvis alt er gået vel, ser det nu sådan ud:

image

Jeg gentager og vælger "Content Files", så jeg også får min TimerDB.sdf med i projektet.

Højreklik på output-ikonet til højre og vælg "Create shortcut ... (osv.)" så dit program er til at finde når det er installeret. Omdøb genvejen til noget passende, f.eks. applikationens navn.

Højreklik på dit installer-projekt og tryk build. Nu er der en CAB-fil i dit projekt, formentlig under "Debug"-biblioteket, hvis du ikke har kompileret til Release. Denne CAB-fil skal kopieres til din telefon og så skal den eksekveres fra din telefon. Det skulle være det.

Om det virker i mit tilfælde, finder jeg ud af nu ...

CAB-filen er kopieret til min telefon og installationen fungerer tilsyneladende helt fint. Jeg navigerer frem til applikationen gennem filsystemet og klikker på den.

"Programmet kræver en nyere version af Microsoft .NET Compact Framework end den version, som er installeret på enheden".

Normalt ville jeg bare ændre det framework der skal kompileres til, men den feature ser ikke ud til at være til stede for Smart Device-applikationer:

image

Det skal åbenbart vælges når projektet skal oprettes. Det tænkte jeg ikke på da jeg oprettede projektet i al hast og nu er jeg nødt til at starte et helt nyt projekt og migrere al koden fra 3.5-projektet til et 2.0-projekt, for at få timeren til at fungere.

Eller jeg kan kræve af brugerne at de skal have .NET 3.5 installeret. Nok ikke klogt, når min 1 måned gamle telefon med nyeste version af Windows Mobile ikke har det.

Sådan vælges hvilket framework man vil udvikle op mod, når man opretter sit projekt.

image

Og ret frækt, når man har gjort sit valg, bliver man også spurgt når man skal vælge emulator.

image

En 'morsom' lille fodnote omkring processen da jeg skulle migrere til den nye .NET 2.0 applikation ... under processen mistede jeg Settings.cs. Hvis jeg ikke netop havde skrevet et blogindlæg om det, ville jeg have skullet skrive al koden forfra. Det viste sig dog at SqlServerCe ikke var til at få til at fungere i .NET 2.0, så det endte med jeg kasserede den kode jeg lige havde genskabt og skrev en lignende Settings-fil med anvendelse af flade tekst-filer i stedet.

Efter vel endt migrering, fulgte jeg min egen guide til installationen og det hele fungerer uden videre. Timeren kører på min telefon og jeg glæder mig til at bruge den til at komme i tanke om at tage en lasagne ud af ovnen. Når jeg nu ikke kan lave lasagnen selv, er det da en trøst at vide, at jeg har lavet timeren selv.

Timeren kan hentes her og så kan du overføre den til din telefon, men har du internet på telefonen, er det nok endnu nemmere at indtaste www.dotnyt.dk/timer.cab i browseren på din telefon.

Edit: Efter at have prøvet timeren i praksis, fik jeg nogle ret brankede ovnkartofler ud af det. Det viser sig at tiden ikke går mens telefonen er på standby, hvilket på min telefon sker efter 30 sekunder. Jeg tror der må komme en version 1.1 af min Timer inden længe, som er lidt mere til at stole på.

0 kommentarer

lørdag den 23. august 2008

Håndtering af data i Windows Mobile

skrevet af Niels Brinch

Jeg har skruet farten lidt ned efter lynprogrammeringen af en lille applikation til min telefon. Dog er jeg ikke gået væk fra at kode mens der er kylling i ovnen.

Jeg går nu lidt dybere ned og kigger på de forskellige detaljer som kan drille, når man som Windows- eller webudvikler kaster sig over at udvikle en Windows Mobile applikation. I dette tilfælde mangler jeg en "Settings"-mulighed i applikationens menu. Der plejer jeg at gemme simple indstillinger, såsom brugerens valg fra sidste gang, så brugeren ikke behøver ændre indstillinger, hvis brugeren bare vil have applikationen til at fungere ligesom sidst.

image

Men det menupunkt findes ikke i en applikation til Windows Mobile (Smart Device project). Derfor må jeg finde en anden måde at gemme brugerindstillinger på.

Efter en Google-søgning synes den mest oplagte løsning at være at lægge en database i forbindelse med mit projekt. Det kan hurtigt blive relevant i andre sammenhænge, at have en database knyttet til sin applikation, så øvelsen er fin.

OBS: Denne løsning er kun relevant for applikationer der skal køre .NET 3.5. Skal applikationen køre på .NET 2.0 anbefales det at anvende en løsning baseret på txt eller xml filer i stedet.

I Visual Studio, højreklik på projektet, add new item og vælg Data -> Database File.

image

Når nu der ikke følger en Settings-klasse med i projektet, må jeg lave en selv. Den skal indeholde gymnastik til at hente og gemme indstillinger i databasen og så skal den gøre det let at anvende indstillingerne fra applikationen.

I databasen opretter jeg en tabel, Settings, med to kolonner: Key og Value, som skal give mig mulighed for dynamisk at tilføje lige så mange indstillinger som jeg har lyst til. Dermed kan jeg også bruge denne klasse til senere projekter der skal anvende Windows Mobile.

For at skabe forbindelse til databasen anvendes en SqlCeConnection, hvor Ce formentlig henviser til det faktum, at tidligere versioner af Windows til mobilen hed Windows CE. Det gøres som vist herunder:

public static SqlCeConnection GetSqlCeConnection()
{
string connString = Assembly.GetExecutingAssembly().GetName().CodeBase;
connString = connString.Replace("Timer.exe", "TimerDB.sdf");
SqlCeConnection conn = new SqlCeConnection(String.Format("Data Source={0}", connString));
return conn;
}

Stien til databasen findes ret kreativt ved at anvende Reflection til at finde stien til applikationen selv og så udskifte applikationens navn i stien med databasefilens navn, som jo ligger samme sted. Jeg fandt tricket på nettet - tror det var i CodeProject et sted.


Koden til at hente en indstilling fra databasen er meget velkendt. Dog et lille kuriosum at jeg ikke kunne få parametre til at virke. Ellers ville jeg selvfølgelig have brugt @key i stedet for det noget mindre elegante: '"+ key + "'.


Man kan heller ikke uden videre bruge "reader.HasRows" på en reader, men kan selvfølgelig benytte "Read()".


private static string GetSetting(string key)
{
SqlCeConnection conn = GetSqlCeConnection();
conn.Open();

string query = "SELECT [value] " +
"from Settings " +
"where [key] = '"+key+"'";

SqlCeCommand cmd = new SqlCeCommand(query, conn);
cmd.CommandType = CommandType.Text;

SqlCeDataReader reader = cmd.ExecuteReader();

string value = null;

if (reader.Read())
{
value = reader.IsDBNull(0) ? null : reader.GetString(0);
}

reader.Close();

return value;
}

Jeg har selvfølgelig også den metode der modsvarer GetSetting, nemlig SetSetting. Denne tager af en eller anden grund gerne mod parametre - ved stadig ikke hvad jeg gjorde forkert i GetSetting, siden den ikke ville godtage dem. Pyt.


For komplethedens skyld er her koden til SetSetting.


private static void SetSetting(string key, string value)
{
SqlCeConnection conn = GetSqlCeConnection();

conn.Open();

string query = null;

if (GetSetting(key) == null)
{
query = "INSERT INTO Settings "+
"([key], [value]) " +
"VALUES " +
"(@key, @value)";
}
else
{
query = "UPDATE Settings " +
"SET [value] = @value " +
"WHERE " +
"[key] = @key";
}

SqlCeCommand cmd = new SqlCeCommand(query, conn);
cmd.CommandType = CommandType.Text;
cmd.Parameters.Add("@key", SqlDbType.NVarChar).Value = key;
cmd.Parameters.Add("@value", SqlDbType.NVarChar).Value = value;

cmd.ExecuteNonQuery();

conn.Close();
}

De tre metoder herover kan jeg benytte direkte fra min applikation ved at kalde GetSetting og SetSetting direkte, men som du kan se, har jeg gjort dem private. Jeg ønsker ikke de bliver kaldt direkte, da det ville opfordre til at skrive en masse strengværdier som keys under anvendelsen.


I stedet laver jeg en property for hver indstilling der skal kunne anvendes.


public static string SoundPath
{
get { return GetSetting("SoundPath"); }
set { SetSetting("SoundPath", value); }
}

Der er nu en komplet klasse som kan anvendes til at håndtere data, f.eks. brugerindstillinger, i en Windows Mobile applikation. Den er temmelig generel, så jeg kan anvende den igen og igen. Og det kan du også.


Nyd her til sidst metoden der tillader brugeren at vælge hvilken lyd applikationen skal afspille.



   1:  private void btnSound_Click(object sender, EventArgs e)
   2:  {
   3:      DialogResult result = openFileDialog1.ShowDialog();
   4:   
   5:      if (result == DialogResult.OK)
   6:      {
   7:          player.SoundLocation = openFileDialog1.FileName;
   8:          player.Load();
   9:   
  10:          Settings.SoundPath = player.SoundLocation;
  11:      }
  12:  }

Indstillingen sættes i linje 10, så applikationen kan huske den valgte lyd til næste gang applikationen starter.

0 kommentarer

torsdag den 14. august 2008

Timer til Windows Mobile

skrevet af Niels Brinch

Klokken er nu 16:55.

Jeg købte ny telefon for en måneds tid siden. En HTC Touch Diamond med Windows Mobile 6.1 installeret. Den er måske en smule langsom, men har ellers givet mig det som jeg havde forventet af den. Den er let at bruge, internet fungerer, synkronisering med Exchange fungerer - og man kan ringe med den. Ikke dårligt.

...men der er ingen timer. Jeg finder stadig min gamle telefon frem når jeg skal lave mad for det er meget lettere at tage tid på den, end det er på min nye fine HTC Touch Diamond. Jeg har netop sat kylling i ovnen og ærgrer mig over det tog næsten lige så lang tid at sætte alarm på min telefon, som det tager at ovnstege kyllingen.

Derfor har jeg tænkt mig at kode en applikation til min telefon som skal gøre det let at tage tid. Jeg skulle gerne nå at blive færdig med at skrive dette indlæg og kode applikationen, inden kyllingen er færdig. Dertil kommer jeg aldrig har kodet til Windows Mobile og jeg gør det på min bærbare inde fra sengen. Det bliver en udfordring :)

Jeg starter med at oprette et nyt projekt i Visual Studio 2008. Et Smart Device projekt. Det første jeg ser efter at have oprettet projektet, er en brugergrænseflade som vises i et billede af en telefon. Et tryk på F5 og jeg får min lille ikke-eksisterende applikation vist i en emulator.

image

Jeg har trukket en NumericUpDown ind i brugergrænsefladen til at angive tiden. Jeg har lavet en ComboBox hvor man kan angive enheden, sekunder, minutter eller timer ... og så har jeg indsat en Label som skal indeholde den tid der er tilbage.

Derudover har jeg indsat en Timer hvor jeg har sat Interval til 1000 millisekunder. Det skulle gerne betyde der kører en event hvert sekund, så jeg kan opdatere hvor lang tid der er tilbage.

image

I bunden har jeg ganske enkelt trykket og skrevet Start og Stop, hvilket giver mig de to fine vagmuligheder. Med et dobbeltklik på Start, kommer jeg ind i metoden, hvor jeg hurtigt skriver det der skal til for at oprette et TimeSpan ud  fra de valgte tider og så kører jeg timer.Enabled = true, så min event starter. Jeg satser på eventen fra timeren virkelig kører en gang i sekundet og tæller altså bare mit TimeSpan ned med et sekund hvert sekund. Men hvis eventen kører sjældnere, vil det tage længere tid end angivet før der nås 0.

image

SelectedText viste sig ikke at være det rigtige, så jeg lavede et hurtigt workaround og brugte SelectedIndex i stedet for. Det der tager længst tid er faktisk at afprøve ændringer i emulatoren. Hver gang der anvendes F5 til at prøve programmet, skal programmet installeres i emulatoren, hvilket tager 1½ minut eller lignende, på min ellers ret hurtigere computer.

Efter 3-4 rekompileringer og dum ventetid, lykkedes det at få den til at reagere korrekt. Følgende er den kode jeg har anvendt i min timers event som køres hvert sekund. Det fungerer.

image

Men det eneste der sker når den har nået 0 er, at timeren stopper. Det skal den selvfølgelig, men den skulle også helst spille en lyd så jeg ikke behøver holde øje med timeren hele tiden.

Efter en Google-søgning kom jeg frem til der findes en SoundPlayer-klasse til .NET 3.5 som kan bruges. For at kunne afspille en lyd skal wav-filen indlæses i SoundPlayer-klassen. Enten fra filsystemet eller hukommelsen. Men hvordan gør jeg det på en telefon? Kan jeg antage der ligger en lydfil et bestemt sted? Eller skal jeg til at lave en funktion så brugeren selv skal vælge hvilken lyd der bruges. Det ville selvfølgelig være fint, men tiden går! Under en halv time tilbage til kyllingen er færdig.

Det er for dumt at hardkode stien til en lyd, så jeg ændrer min "Stop"-knap til en "Vælg lyd"-knap. Jeg indsætter en "OpenFileDialog" i mit vindue og i klik-eventen på "Vælg lyd"-knappen, skriver jeg følgende:

image

Det ser ud til at virke, men der er ikke nogle lyde at vælge på emulatoren, så jeg kan ikke umiddelbart finde ud af det. Jeg ville midlertidigt hardkode en sti til en fil som jeg ved ligger direkte i Windows-mappen på telefonen, men det ser umiddelbart ikke ud til at være muligt at tilgå filer fra applikationen som ligger i Windows-mappen. Jeg må vente med at teste til jeg får installeret min applikation på min telefon.

Til mit Tick-event fra før, tilføjer jeg den handling, at den fil som er indlæst, skal afspilles. Play, hedder metoden:

image

Nu skulle applikationen gerne virke. Jeg når ikke at finde ud af hvordan man installere på min telefon - der findes en projekttype som vist gør det muligt at lave en cab-fil, som er lige til at flytte til telefonen og installere fra.

Jeg når heller ikke at finpudse detaljer. F.eks. skulle det gerne gemmes fra gang til gang hvilken lyd man har valgt skal afspilles.

Men faktum er, at jeg fik udviklet en applikation til min telefon på næsten ingen tid. Jeg supplerer senere med et indlæg som forklarer hvordan installationen klares og som også afslører om det virkelig passer at applikationen virker i sin nuværende form, kun en time efter jeg gik i gang med både at udvikle og skrive dette indlæg.

Klokken er nu 17:53

0 kommentarer


 
Til forsiden

Niels Brinch

- Seneste indlæg