niedziela, 31 maja 2009

Przykłady w Symbian C++ dla S60

Spodziewam się bardzo dużego zainteresowania programowaniem na Symbiana w C++ czy w OpenC na bazie frameworka UI dla S60 AVKON a w przyszłości pod frameworkiem Qt for S60 Natomiast programowaniem UI dla UIQ można zainteresować się w przypadku gdy jest taka potrzeba programowania na komórki SonyEricssona.

Poniższy spis przykładów będzie raczej skierowany dla początkujących, aby mogli zapoznać się z kodami źródłowymi i na podstawie tych kodów systematycznie pracowali nad poznawaniem możliwości programowania w C++.

Głównym źródłem przykładów do nauki pozostaje SDK dla danej wersji Symbiana, kody źródłowe dołączone do książek Symbian Press, Wiki z Forum Nokii, oraz nieliczne projekty na repozytoriach kodów źródłowych, takich jak SourceForge, Google Code.

Zaczniemy od przykładów na Forum Nokia. Tytułem wprowadzenia w programowanie UI polecam najpierw zapoznanie się z dokumentem Scalable UI Support v4.3. Nokia zazwyczaj przygotowała materiały szkoleniowe na ten temat: S60 Scalable UI Guidelines eLearning, S60 UI Architecture eLearning, S60 UI Controls eLearning, S60 List Controls eLearning


Bardzo polecałbym też zapoznanie się z dokumentem Avkon UI Resources v1.1 opisującym wszystkie kontrolki UI AVKONa. Settings Screen Example to przykład tego jak tworzyć UI które pozwala na zapisywanie ustawień. Więcej informacji o tym można przeczytać w dokumencie Implementing Settings Screens v2.0. Bardzie złożonym przykładem tego jak można dynamicznie zmieniać ustawienia aplikacji jest przykład Dynamic Setting List Example v1.1 Kolejny przykład Custom UI and Screen Rotation Example - pokazuje jak robić obrót ekranu i obsługiwać kontrolki reagujące na obrót ekranu. Następny kod z Scalable Screen-Drawing Example jest bardzo interesującym przykładem jak można tworzyć własny interfejs użytkownika pod katem zastosowań multimedialnych - jest nawet dokumentacja do tego przykładu (dla lepszego zrozumienia obsługi SVG-T zalecam poczytanie tego dokumentu Vector Graphics Optimization)
Istotny jest kod z przykładu Scalable UI Example, który jest prostą gierką (kółko i krzyżyk) wprowadzajacą w temat obsługi skalowalności kontrolek UI AVKONA. To trzeba brać czesto pod uwagę ze względu na to, że rozmiary ekranów w komórkach są różne. Ten temat porusza dokument Introduction To The S60 Scalable UI v1.4

Po zapoznaniu się z podstawami UI dla S60 AVKON można już przejść do analizy bardziej ciekawszych przykładów: konwertera obrazków Image Converter Example i jak prostego edytora Rich Text Editor Example

Jeżeli kogoś bardziej interesuje tworzenie gier to polecam zapoznanie się z przykładami kodów gier takich jak Sudoku, a także spróbować wykorzystać pisanie kodu w OpenC do napisania silnika gry Memory gdzie oczywiście interfejs użytkownika będzie w AVKONie. Dla programistów gier pod Symbianem jest dokument wprowadzający w architekturę programowania gier pod tą platformę Programming Games In C++ v2.0. Pod kątem programowania gier na Symbiana szczególnie polecam zapoznanie się przykładem, który może zostać użyty do tworzenia silników różnych gier czy aplikacji multimedialnych. Jest to: Isometric Adventure Game Example wraz z dokumentem Designing Isometric Adventure Games, 2D Game Engine Example

Dość złożonym tematem jest programowanie aplikacji wykorzystujących OpenGL ES, tutaj odsyłam do przykładów w SDK. Tytułem wprowadzenia proponuje zapoznanie się z dokumentem Introduction to 3D Graphics in C++ (with Example) v1.1 a potem przyjrzeć się kodowi 3D Game Engine Example with Sensor Support v1.2 ze względu na przykład z programowanie sensora

Dość specyficzne jest tworzenie aplikacji UI tylko dla obsługi interfejsu Touch (reagowania na dotyk) ze względu na możliwości Symbiana 9.4 (S60 5 edycji). Gdyby kogoś interesowało to co się zmieniło w S60 5 edycji dla programistów i jak napisać kod obsługujący obydwie platformy to należy przeczytać Porting applications to S60 5th Edition Tutaj polecam zapoznanie się z przykładami Solitaire Game Example i jak Mobile Paint Example. Dla dociekliwych polecam ściągniecie S60 5th Edition C++ Developer's Library v2.0 (Standalone) i przestudiowanie tego.

Komórki z tym systemem są często kupowane ze względu na możliwości multimedialne. W tym można powiedzieć że umiejętność zaprogramowania multimediów jest ważną cechą programisty w Symbianie. Nokia zrobiła świetny materiał szkoleniowy wproawadzajacy w tym temacie S60 Multimedia Framework eLearning. Na początek polecam zapoznanie się z dokumentami Multimedia Framework Architecture in S60 Devices v1.1 i Image and Video Capturing, które pokazują jaka jest skomplikowana fragmentaryzacja technologiczna, jeżeli chodzi o programowanie video i dźwięku. Często jest tak w tym temacie trzeba zaprogramować pod określone modele komórek. W tym przypadku proponuje zanalizować przykłady takie jak Video Example v2.0 i Camera Example i należy zwrócić uwagę na którą wersję Symbiana programuje się. Zalecam poczytać dokumentację Creating Video Applications In C++ a także Using the camera in applications

Programista multimediów w Symbianie powinien również wiedzieć jak programuje się obsługę dźwięku. Więcej o programowaniu muzyki warto poczytać w Music Application Developer's Guide Po zapoznaniu się z tym dokumentem można przejść do czytania kodów z następujących przykładów takich jak Audio Streaming Example, Audio Output Streaming Example przykład ten pokazuje jak zapisać dźwięk oraz Sound Mixer Example który jest przykładem miksera pokazującym jak równocześnie odtwarzać kilka dźwięków.

Programowanie na komórki nie ogranicza się tylko do multimediów. Współczesne komórki też mają możliwości zaprogramowanie baz danych do przechowywania danych, skorzystania z możliwości lokalizacyjnych (LBS i GPS), komunikacji za pośrednictwem Bluetootha, programowania wiadomości (SMS i MMS) a także skorzystania z możliwości obsługi komunikacji dźwiękowej (telefon, SIP czy VoIP).

Więc warto poznać kolejne przykłady z kodami źródłowymi. Przykład SMS Example pokazuje jak można zaprogramować obsługę SMSów. Przykład MMS Example pokazuje jak można programistyczne tworzyć MMSy. Przykład POP/IMAP Example ukazuje możliwości programistycznej obsługi poczty internetowej. Istnieje możliwość programowania aplikacji internetowych w Symbianie. Programista powinien zapoznać się z wstępnymi informacjami o komunikacji w dokumentacji w dziale Communication, bądź przeczytać wprowadzenie Overview To Networking. Są też przykłady End-to-End HTTP API Example v1.3 i jak End-to-End Sockets API Example v1.3 czy też HTTP Client Example

Do komunikacji pomiędzy urządzeniami i komórkami trzeba wykorzystać protokół Bluetooth. Na początek należy zapoznać się z wprowadzeniem do programowania Bluetootha w dokumencie Bluetooth API Developer's Guide Interesujący jest kod z przykładów Bluetooth Point-to-Multipoint Example, i jak Bluetooth OBEX Example

W komórkach mamy też funkcjonalności które są właściwie częścią API Symbiana. Jest API które odpowiada za korzystanie z książki adresowej. Przykład Contacts Model API Example pokazuje jak eksportować dane do formatu vCard. API za korzystanie z kalendarza jest ukazany w Calendar Interim API Example. Natomiast obsługę API do zarządzania plikami można poznać z kodu FileList Example, a API z obsługi baz danych można przestudiować z kodu przykładu DBMS Example.

Spora część API została udostępniona jako S60 Platform Services dla WRT (Web Runtime), Flash Lite 3, czy Pythona oraz Qt for S60, co umożliwia innym programistom korzystać z możliwości API, którze wcześniej było dostepne tylko dla programistów C++.

sobota, 30 maja 2009

Kod aplikacji C++ Symbian S60 UI

Tak jak pisałem na forum Symbianos.pl i z obserwacji początkujących największym problemem jest zrozumienie kodu aplikacji GUI dla S60 . Jest to specyficzne ze względu na to że użytkownicy Carbide.C++ już od razu chcą robić aplikacje w Designerze UI bez wcześniejszego teoretycznego przygotowania. Carbide.C++ to tylko narzędzie które trzeba dostosować do własnych potrzeb programistycznych. W praktyce to warto głównie traktować jako edytor programistyczny dla zaawansowanych programistów Symbiana.

W tej sytuacji przyjrzymy się jak powstaje aplikacja na Symbiana S60. Na początek pracujemy na kodzie w jakimś szybkim lekkim edytorze programistycznym na przykład Notepad++

Zrobimy na początek główny katalog PierwszaAplikacja z podkatalogami data, group, gfx, inc, src, sis.

Wchodzimy do katalogu inc i musimy mieć na początek identyfikator aplikacji
Z jednej strony ustalamy zmienną _UID3 w tym celu żeby w całej aplikacji potem móc zaplanować też nazewnictwo aplikacji i strukturę plików

z drugiej strony istotne jest fakt że typ ennumeratywny THelloWorldIds przechowuje informacje o poleceniach jakie będą używane w menu.


/*
============================================================================
Name : Pierwsza Aplikacja.hrh
Author : Michał Małaj
Copyright : LGPL
Description : To jest plik deklaracji identyfikatora aplikacji.
Ten plik będzie dołączany do plików zasobów podczas kompilacji.
============================================================================
*/
#ifndef __PIERWSZAAPLIKACJA_HRH__
#define __PIERWSZAAPLIKACJA_HRH__

#define _UID3 0xE82211C4

// Wyliczenie identyfikatorów dla poleceń wykonywanych z AVKONA
enum TPierwszaAplikacjaIds
{
ECommand1 = 0x6001, // pierwsza wartość musi rozpoczynać się od wartości
ECommand2
};

#endif // __PIERWSZAAPLIKACJA_HRH__


Następnie w folderze data robimy plik MojaAplikacja.rls jest to plik który zawiera informacje o innych plikach lokalizacyjnych z tłumaczeniami pod określony język, bądź inne informacje które mogą zmieniać się w przypadku zastosowania odpowiedniej wersji językowej.


/*
============================================================================
Name : PierwszaAplikacja.rls
Author : Michał Małaj
Copyright : LGPL
Description : To jest plik lokalizacyjny dla programu PierwszaAplikacja.
Ten plik zawiera informacje dla kompilatora żeby zrobił z plików
lokalizacyjnych wersje zasobów w odpowiednim języku dla wersji Symbiana.
============================================================================
*/

#ifdef LANGUAGE_SC
#include "PierwszaAplikacja_01.rls" // default U.K. English
#elif defined LANGUAGE_01
#include "PierwszaAplikacja_01.rls" // U.K. English
#elif defined LANGUAGE_27
#include "PierwszaAplikacja_27.rls" // Polish
#endif
// End of File


Istotne jest to że kod w pliku zasobów dla języka polskiego musi być kodowany w UTF-8. O tym kompilator zostaje poinformowany przez zamieszczenie makra CHARACTER_SET UTF8. Inne pliki mają być kodowane po ASCII (w praktyce i tak programujemy pod kodowanie systemu operacyjnego Windows) Oto wersja z tłumaczeniem dla języka polskiego.


/* ============================================================================
Name : PierwszaAplikacja_27.rls
Author : Michał Małaj Copyright : LGPL
Description : To jest plik lokalizacyjny w języku polskim dla programu PierwszaAplikacja
============================================================================
*/
CHARACTER_SET UTF8
// LOCALISATION STRINGS
// Tekst nagłówka aplikacji.
#define qtn_caption_string "Pierwsza Aplikacja"
// Pierwsza pozycja w menu "Options"
#define qtn_command1 "Wiadomość"
// kiedy użytkownik będzie wywoływać polecenie z menu,
// poniższy tekst zostanie pokazany
#define qtn_command1_text "Witaj Świecie!"
// trzeba podać miejsce pliku gdzie będzie przechowywany zasób
#define qtn_loc_resource_file_1 "\\resource\\apps\\PierwszaAplikacja_0xE82211C4"
// End of File

Poniżej zamieszczamy kod dla wersji w języku angielskim.

/*
============================================================================
Name : PierwszaAplikacja_01.rls
Author : Michał Małaj
Copyright : LGPL
Description : To jest plik lokalizacyjny w języku angielskim
============================================================================
*/

// Tekst nagłówka aplikacji.
#define qtn_caption_string "First Application"
// Pierwsza pozycja w menu "Options"
#define qtn_command1 "Message"
// kiedy użytkownik będzie wywoływał polecenie z menu,
// poniższy tekst zostanie pokazany
#define qtn_command1_text "Hello World!"
// trzeba podać miejsce pliku gdzie będzie przechowywany zasób
#define qtn_loc_resource_file_1 "\\resource\\apps\\PierwszaAplikacja_0xE82211C4"
// End of File


Teraz robimy plik zasobów MojaAplikacja.rss, który tak naprawdę definiuje strukturę menu w aplikacji.



/*
============================================================================
Name : PierwszaAplikacja.rss
Author : Michał Małaj
Copyright : LGPL
Description : Ten plik zawiera wszystkie dane zasobów interfejsu użytkownika
potrzebne dla programu Pierwsza Aplikacja.
============================================================================
*/
// Identyfikator zasobu
NAME PIER // jest czteroznakowy


// INCLUDES
#include <eikon.rh>
#include <avkon.rsg>
#include <avkon.rh>
#include <appinfo.rh>
// potrzebne są dane do wyliczania poleceń w programie
#include "PierwszaAplikacja.hrh"
// dołączamy plik zasobów z przetłumaczonym wyrażeniami
#include "PierwszaAplikacja.rls"

// DEFINICJE ZASOBÓW
// -----------------------------------------------------------------------------
//
// Określamy sygnaturę pliku z zasobami
//
// -----------------------------------------------------------------------------
//
RESOURCE RSS_SIGNATURE
{
}

// -----------------------------------------------------------------------------
//
// Domyślna nazwa dokumentu AVKONA
//
// -----------------------------------------------------------------------------
//
RESOURCE TBUF r_default_document_name
{
buf="PIER";
}

// -----------------------------------------------------------------------------
//
// Określamy strukturę menu i klawiszy CBA.
//
// -----------------------------------------------------------------------------
//
RESOURCE EIK_APP_INFO
{
menubar = r_menubar;
cba = R_AVKON_SOFTKEYS_OPTIONS_EXIT;
}


// -----------------------------------------------------------------------------
//
// Zasób odpowiadający za główne menu
// r_menubar
//
//
// -----------------------------------------------------------------------------
//
RESOURCE MENU_BAR r_menubar
{
titles =
{
MENU_TITLE { menu_pane = r_menu; }
};
}


// -----------------------------------------------------------------------------
//
// r_menu
// Zasób za menu "Opcje"
//
// -----------------------------------------------------------------------------
//
RESOURCE MENU_PANE r_menu
{
items =
{
// dodajemy nową pozycję w menu Opcje
MENU_ITEM
{
command = ECommand1;
txt = qtn_command1;
}
};
}

// -----------------------------------------------------------------------------
//
// Zasoby dla komunikatów
//
// -----------------------------------------------------------------------------
//
RESOURCE TBUF32 r_caption_string { buf=qtn_caption_string; }
RESOURCE TBUF r_command1_text { buf=qtn_command1_text; }


// ----------------------------------------------------------------------------
//
// zasób odpowiadający za wersje lokalizacyjne i
// za grafikę - r_localisable_app_info
//
// ----------------------------------------------------------------------------
//
RESOURCE LOCALISABLE_APP_INFO r_localisable_app_info
{
short_caption = qtn_caption_string;
caption_and_icon =
CAPTION_AND_ICON_INFO
{
caption = qtn_caption_string;

number_of_icons = 1;
icon_file = "\\resource\\apps\\PierwszaAplikacja_0xE82211C4.mif";
};
}
// End of File



Jak widać plik zasobów już wymaga ustalenia co do informacji o strukturze menu i jak odnośnie informacji o ikonie wyświetlanej podczas działania aplikacji. Pliki o rozszerzeniu *.mif są plikami, które zawierają wszystkie odpowiednie pliki graficzne z których zasób może skorzystać.

Po kompilacji pliku zasobu plik ten przyjmuje nazwę MojaAplikacja.rsg
Aby w przyszłości nie było problemów z obsługą różnych wersji plików zasobów warto tworzyć unikalną nazwę w oparciu o UID3 czyli plik może nazywać się MojaAplikacja_0x082DDBA4.rsg

Na koniec trzeba jeszcze zrobić plik zasobów dla instalatora, tak żeby instalator wiedział jak uruchomić naszą aplikację i wyświetlił odpowiednie informacje w folderze instalacje.



/*
============================================================================
Name : PierwszaAplikacja_reg.rss
Author : Michał Małaj
Copyright : LGPL
Description : ten plik zaweira zasób potrzebny dla instalatora.
============================================================================
*/

#include "PierwszaAplikacja.hrh"
#include "PierwszaAplikacja.rls"
#include <appinfo.rh>
#include <PierwszaAplikacja_0xE82211C4.rsg>

UID2 KUidAppRegistrationResourceFile
UID3 _UID3

RESOURCE APP_REGISTRATION_INFO
{
app_file="PierwszaAplikacja_0xE82211C4";
localisable_resource_file = qtn_loc_resource_file_1;
localisable_resource_id = R_LOCALISABLE_APP_INFO;
embeddability=KAppNotEmbeddable;
newfile=KAppDoesNotSupportNewFile;
}


Teraz zajmiemy się tworzeniem i poznawaniem API szablonu aplikacji UI
Jest określona hierarchia zależności API dla graficznego interfejsu.
Nasza aplikacja jest pochodną frameworka UI Avkon wiec najpierw musimy określić co ma być uruchomione przez ten framework, więc w folderze inc mamy pliki nagłówkowe definiujące API

/*
============================================================================
Name : PierwszaAplikacjaApplication.h
Author : Michał Małaj
Copyright : LGPL
Description : Deklaracje głównej klasy aplikacji
============================================================================
*/

#ifndef __PIERWSZAAPLIKACJAAPPLICATION_H__
#define __PIERWSZAAPLIKACJAAPPLICATION_H__

// Zawiera inne deklaracje
#include <aknapp.h>
// z tego pliku pobierany będzie identyfikator aplikacji
#include "PierwszaAplikacja.hrh"

// UID dla aplikacji ;
// to powinno być tym samym identyfikatorem który będzie w pliku mmp.
const TUid KUidPierwszaAplikacjaApp =
{
_UID3
};

// Deklaracja Klasy

/**
* Klasa CPierwszaAplikacjaApplication
* Dostarcza fabrykę do tworzenia obiektu dokumentu AVKON
* Instancja klasy CPierwszaAplikacjaApplication jest
* obsługiwana przez klasy serwera aplikacyjnego AVKON
*/
class CPierwszaAplikacjaApplication : public CAknApplication
{
public:
// publiczne składowe z klasy bazowej

/**
* Zmiennia AppDllUid.
* @return Zwraca identyfikator UID aplikacji (KUidPierwszaAplikacjaApp).
*/
TUid AppDllUid() const;

protected:
// składowe chronione

/**
* From CApaApplication, CreateDocumentL.
* Tworzy obiekt dokumentu AVKONA CPierwszaAplikacjaDocument
* Zwrócoy wskażnik nie jest już zarządzany przez obiekt
* CPierwszaAplikacjaApplication
* @return Zwaraca wskażnik do utworzonrgo dokumentu AVKONA
*/
CApaDocument* CreateDocumentL();
};
#endif
// __PIERWSZAAPLIKACJAAPPLICATION_H__
// End of File


Nasza aplikacja więc najpierw pobiera identyfikator UID i tworzy dokument AVKON.

/*
============================================================================
Name : PierwszaAplikacjaDocument.h
Author : Michał Małaj
Copyright : LGPL
Description : Deklaracja klasy dokumentu AVKON
============================================================================
*/

#ifndef __PIERWSZAAPLIKACJADOCUMENT_h__
#define __PIERWSZAAPLIKACJADOCUMENT_h__

// INCLUDES
#include <akndoc.h>

// Deklarujemy jakie klasy będą używane w tym pliku
class CPierwszaAplikacjaAppUi;
class CEikApplication;

// Deklaracja klasy

/**
* Klasa CPierwszaAplikacjaDocument.
* Instancja klasy CPierwszaAplikacjaDocument jest pochodną (dziedziczy)
* od obiektów Document z frammeworka AVKON
*/
class CPierwszaAplikacjaDocument : public CAknDocument
{
public:
// Konstruktor i destruktor

/**
* NewL.
* Podwójny konstruktor
* Tworzymy dokument AVKONA dla aplikacji AVKON używając
* mechanizmu podwójnego konstruktora i
* zwraca wskaźnik dla utworzonego obiektu
* @param parametrem ma być zmienna aApp typu CEikApplication
* @return zwraca wskaźnik do utworzonej instancji obiektu
* CPierwszaAplikacjaDocument.
*/
static CPierwszaAplikacjaDocument* NewL(CEikApplication& aApp);

/**
* NewLC.
* Tworzymy dokument AVKONA dla aplikacji AVKON używając
* mechanizmu podwójnego konstruktora i
* zwraca wskaźnik dla utworzonego obiektu
* @param parametrem ma być zmienna aApp typu CEikApplication
* @return zwraca wskaźnik do utworzonej instancji obiektu
* CPierwszaAplikacjaDocument.
*/
static CPierwszaAplikacjaDocument* NewLC(CEikApplication& aApp);

/**
* ~CPierwszaAplikacjaDocument
* Wirtualny destruktor.
*/
virtual ~CPierwszaAplikacjaDocument();

public:
// Składowe z klasy bazowej CAknDocument

/**
* CreateAppUiL
* From CEikDocument, CreateAppUiL.
* Tworzy obiekt CPierwszaAplikacjaAppUi i zwraca wskaźnik do tego miejsca.
* Za ten obiekt odpowiada framework Uikon (abstrakcyjny interfejs).
* @return zwraca wskaźnik do utworzonej instancji obiektu AppUi.
*/
CEikAppUi* CreateAppUiL();

private:
// Konstruktor

/**
* ConstructL
* Drugofazowy kontruktor
*/
void ConstructL();

/**
* CPierwszaAplikacjaDocument.
* Domyślny konstruktor C++
* @param paratmetrem jest obiekt aplikacji tworzony przez
* ten document AVKONA
*/
CPierwszaAplikacjaDocument(CEikApplication& aApp);
};

#endif // __PIERWSZAAPLIKACJADOCUMENT_h__
// End of File


Warto zwrócić uwagę na podwójny konstruktor i jak nazewnictwo klas. Przyrostek C oznacza że klasa będzie tworzona na stercie, w praktyce nie wiemy ile pamięci będzie zawierać, końcówka L informuje że dana metoda może zgłosić problem podczas działania a jak to wystąpi to może wywołać "panikę" i zamknąć całą aplikację. Jak zauważyć można było że powyższa deklaracja klasy wymaga podania z jakich deklaracji innej klasy się korzysta


/*
============================================================================
Name : PierwszaAplikacjaAppUi.h
Author : Michał Małaj
Copyright : LGPL
Description : Deklarujemy klasę UI.
============================================================================
*/

#ifndef __PIERWSZAAPLIKACJAAPPUI_h__
#define __PIERWSZAAPLIKACJAAPPUI_h__

// INCLUDES
#include <aknappui.h>
// Klasa używana w tej deklaracji
// wiec trzeba poinformować kompilator że ma brać pod uwagę
// klasy które są potrzebne
// Ta klasa odpowiada za to co będzie brane w widoku UI
class CPierwszaAplikacjaAppView;

//
/**
* Klasa UI CPierwszaAplikacjaAppUi
* Odpowiada za interakcję użytkownika poprzez interfejs UI i
* komunikaty z obsługujących klas
*
*/
class CPierwszaAplikacjaAppUi : public CAknAppUi
{
public:
// Konstruktory i destruktory

/**
* ConstructL.
* Drugofazowy konstruktor
*/
void ConstructL();

/**
* CPierwszaAplikacjaAppUi.
* Domyślny konstruktor C++ to jest potrzebne
* z powodu sposobu tworzenia AppUi
*
*/
CPierwszaAplikacjaAppUi();

/**
* ~CPierwszaAplikacjaAppUi.
* Wirtualny destruktor
*/
virtual ~CPierwszaAplikacjaAppUi();

private:
// Funkcje z klasy bazowej

/**
* From CEikAppUi, HandleCommandL.
* Funkcja która obsługuje polecenia
* @param aCommand numer polecenia.
*/
void HandleCommandL(TInt aCommand);

/**
* HandleStatusPaneSizeChange.
* Wywoływana przez framework kiedy zmienia się rozmiar wyświetlacza
*/
void HandleStatusPaneSizeChange();


private:
// Dane

/**
* Widok aplikacji
* Za nią odpowiada klasa CPierwszaAplikacjaAppUi
*/
CPierwszaAplikacjaAppView* iAppView;

};

#endif // __PIERWSZAAPLIKACJAAPPUI_h__
// End of File

Jak widać obsługa konstrukcji praktycznie opiera się na kompozycji niż dziedziczeniu - dziedziczy się biblioteki odpowiedzialne za UI natomiast sami odpowiadamy za działanie naszej aplikacji. Ta deklaracja wskazuje na to że my sami odpowiadamy za obsługę menu i jak za załadowanie widoku. Poniżej kod deklaracji widoku który jest kontrolką ( bo dziedziczy od CCoeControl) która może aplikacja sobie załadować.

/*
============================================================================
Name : PierwszaAplikacjaAppView.h
Author : Michał Małaj
Copyright : LGPL
Description : Deklarujemy klasę widoku dla aplikacji
============================================================================
*/

#ifndef __PIERWSZAAPLIKACJAAPPVIEW_h__
#define __PIERWSZAAPLIKACJAAPPVIEW_h__

// pobieramy nagłówkowy odpowiedzialny za kontrolki
#include <coecntrl.h>

// Deklaracja klasy
class CPierwszaAplikacjaAppView : public CCoeControl
{
public:
// Nowe składowe

/**
* NewL.
* Dwufazozwy konstruktor.
* Tworzy obiekt CPierwszaAplikacjaAppView, który będzie siebie
* rysował do obszaru wyznaczonego przez zmienną aRect.
* @param aRect Prostokątny obszar który ma być narysowany
* @return zwraca wskaźnik do utworzonej instancji CPierwszaAplikacjaAppView.
*/
static CPierwszaAplikacjaAppView* NewL(const TRect& aRect);

/**
* NewLC.
* Dwufazozwy konstruktor.
* Tworzy obiekt CPierwszaAplikacjaAppView, który będzie siebie rysował do
* obszaru wyznaczonego przez zmienną aRect.
* @param aRect - prostokątny obszar który ma być narysowany
* @return - zwraca wskaźnik do utworzonej instancji CPierwszaAplikacjaAppView.
*/
static CPierwszaAplikacjaAppView* NewLC(const TRect& aRect);

/**
* ~CPierwszaAplikacjaAppView
* Wirtualny destruktor
*/
virtual ~CPierwszaAplikacjaAppView();

public:
// Zawiera składowe z bazowej klasy

/**
* From CCoeControl, Draw
* Rysuje CPierwszaAplikacjaAppView na ekranie.
* @param aRect obszar prostokątny który ma być przerysowany
*/
void Draw(const TRect& aRect) const;

/**
* From CoeControl, SizeChanged.
* Wirtualna metoda wywoływana przez framework kiedy
* rozmiar wyświetlania zmienia się
*/
virtual void SizeChanged();

/**
* From CoeControl, HandlePointerEventL.
* Wywołuje przez framework kiedy ktoś obsługuje dotyk
* Ta metoda jest zgodna z S60 3 edycji ale nie wykonywana
* @param aPointerEvent pobiera informację o tym gdzie kto kliknął.
*/
virtual void HandlePointerEventL(const TPointerEvent& aPointerEvent);

private:
// Konstruktory

/**
* ConstructL
* Drugi konstruktor obiektu
* CPierwszaAplikacjaAppView.
* @param aRect Obszar który ma być przerysowany
*/
void ConstructL(const TRect& aRect);

/**
* CPierwszaAplikacjaAppView.
* Domyślny konstruktor C++.
*/
CPierwszaAplikacjaAppView();
};

#endif // __PIERWSZAAPLIKACJAAPPVIEW_h__
// End of File

Zazwyczaj kod aplikacji jest taki że po plikach z deklaracją trzeba napisać ich implementacje. Pliki implementacji trzymamy w folderze src
Tak naprawdę całą aplikacja zostaje uruchomiona przez metodę E32Main() w pliku
PierwszaAplikacja.cpp

/*
============================================================================
Name : PierwszaAplikacja.cpp
Author : Michał Małaj
Copyright : LGPL
Description : Główna klasa do uruchomienia
============================================================================
*/

// INCLUDE FILES
#include <eikstart.h>
#include "PierwszaAplikacjaApplication.h"

LOCAL_C CApaApplication* NewApplication()
{
return new CPierwszaAplikacjaApplication;
}

GLDEF_C TInt E32Main()
{
return EikStart::RunApplication(NewApplication);
}

Tak więc ten kod uruchamia nową instancję klasy CPierwszaAplikacjaApplication a co za tym idzie ta instancja, utworzy następnie instancję klasy CPierwszaAplikacjaDocument, który uruchomi z kolei instancję klasy CPierwszaAplikacjaAppUi odpowiedzialną za interfejs UI, a ta klasa uruchomi instancję klasy CPierwszaAplikacjaAppView z widokiem z kontrolkami w samym UI.

/*
============================================================================
Name : PierwszaAplikacjaDocument.cpp
Author : Michał Małaj
Copyright : LGPL
Description : Implementacja klasy dokumentu CPierwszaAplikacjaDocument
============================================================================
*/

// INCLUDE FILES
#include "PierwszaAplikacjaAppUi.h"
#include "PierwszaAplikacjaDocument.h"

// ============================ Składowe funkcje ===============================

// -----------------------------------------------------------------------------
// CPierwszaAplikacjaDocument::NewL()
// Dwufazowy konstruktor
// -----------------------------------------------------------------------------
//
CPierwszaAplikacjaDocument* CPierwszaAplikacjaDocument::NewL(CEikApplication& aApp)
{
//wywołujemy siebie samego
CPierwszaAplikacjaDocument* self = NewLC(aApp);
//wysyłamy na stos
CleanupStack::Pop(self);
//zwracamy siebie samą
return self;
}

// -----------------------------------------------------------------------------
// CPierwszaAplikacjaDocument::NewLC()
// Dwufazowy konstruktor
// -----------------------------------------------------------------------------
//
CPierwszaAplikacjaDocument* CPierwszaAplikacjaDocument::NewLC(CEikApplication& aApp)
{
// Tworzymy instancję obiektu i rzutując go jako typ ELeave
CPierwszaAplikacjaDocument* self = new (ELeave) CPierwszaAplikacjaDocument(aApp);
// wrzucamy go na stos obiektów typu Leave
CleanupStack::PushL(self);
// wykonujemy metodę dla tego obiektu
self->ConstructL();
// zwracamy siebie samą
return self;
}

// -----------------------------------------------------------------------------
// CPierwszaAplikacjaDocument::ConstructL()
// To jest drugofazowy konstruktor może obsłużyć
// problemy w tworzeniu instancji
// -----------------------------------------------------------------------------
//
void CPierwszaAplikacjaDocument::ConstructL()
{
// żadna implementacja jest wymagana,
// bo podczas wycieku nie powinny być
// wykonywane dodatkowe obliczenia
}

// -----------------------------------------------------------------------------
// CPierwszaAplikacjaDocument::CPierwszaAplikacjaDocument()
// Domyślny konstruktor C++
// -----------------------------------------------------------------------------
//
CPierwszaAplikacjaDocument::CPierwszaAplikacjaDocument(CEikApplication& aApp) : CAknDocument(aApp)
{
// Nie wymaga implementacji
}

// ---------------------------------------------------------------------------
// CPierwszaAplikacjaDocument::~CPierwszaAplikacjaDocument()
// Dekonstruktor
// ---------------------------------------------------------------------------
//
CPierwszaAplikacjaDocument::~CPierwszaAplikacjaDocument()
{
// nie wymaga implementacji
}

// ---------------------------------------------------------------------------
// CPierwszaAplikacjaDocument::CreateAppUiL()
// Tworzymy nowy obiekt CreateAppUi.
// ---------------------------------------------------------------------------
//
CEikAppUi* CPierwszaAplikacjaDocument::CreateAppUiL()
{
// Tworzymy interfejs użytkownika aplikacji i zwracamy wskaźnik do tego
// framework UiKon przejmuje odpowiedzialność za ten obiekt
return new (ELeave) CPierwszaAplikacjaAppUi;
}
// End of File



Celem dokumentu AVKONA jest pilnowanie integralności aplikacji, bo ta klasa trzyma dostęp do UID aplikacji. Logikę aplikacji zazwyczaj zamieszczamy w pliku
PierwszaAplikacjaAppUi.cpp


/*
============================================================================
Name : PierwszaAplikacjaAppUi.cpp
Author : Michał Małaj
Copyright : LGPL
Description : Implementacja klasy CPierwszaAplikacjaAppUi
============================================================================
*/

// INCLUDE FILES
#include <avkon.hrh>
#include <aknmessagequerydialog.h>
#include <aknnotewrappers.h>
#include <stringloader.h>
#include <f32file.h>
#include <s32file.h>
#include <hlplch.h>
#include <PierwszaAplikacja_0xE82211C4.rsg>
#include "PierwszaAplikacja.hrh"
#include "PierwszaAplikacja.pan"
#include "PierwszaAplikacjaApplication.h"
#include "PierwszaAplikacjaAppUi.h"
#include "PierwszaAplikacjaAppView.h"


// ============================ Składowe ===============================


// -----------------------------------------------------------------------------
// CPierwszaAplikacjaAppUi::ConstructL()
// Drugofazowy konstruktor
// -----------------------------------------------------------------------------
//
void CPierwszaAplikacjaAppUi::ConstructL()
{
// Inicjalizacja API UI z obsługą zastosowanego motywu
BaseConstructL(CAknAppUi::EAknEnableSkin);

// Tworzymy obiekt widoku
iAppView = CPierwszaAplikacjaAppView::NewL(ClientRect());
}
// -----------------------------------------------------------------------------
// CPierwszaAplikacjaAppUi::CPierwszaAplikacjaAppUi()
// Domyślny konstruktor C++ nie może zawierać dowolnego kodu
// -----------------------------------------------------------------------------
//
CPierwszaAplikacjaAppUi::CPierwszaAplikacjaAppUi()
{
// Nie wymaga implementacji
}

// -----------------------------------------------------------------------------
// CPierwszaAplikacjaAppUi::~CPierwszaAplikacjaAppUi()
// dekonstruktor sprząta też po widoku
// -----------------------------------------------------------------------------
//
CPierwszaAplikacjaAppUi::~CPierwszaAplikacjaAppUi()
{
if (iAppView)
{
delete iAppView;
iAppView = NULL;
}

}

// -----------------------------------------------------------------------------
// CPierwszaAplikacjaAppUi::HandleCommandL()
// Obsługa poleceń menu
// -----------------------------------------------------------------------------
//
void CPierwszaAplikacjaAppUi::HandleCommandL(TInt aCommand)
{
switch (aCommand)
{
case EEikCmdExit:
case EAknSoftkeyExit:
Exit();
break;

case ECommand1:
{

// Ładujemy ciąg znaków z pliku zasobów i wyświetlamy go
HBufC* textResource = StringLoader::LoadLC(R_COMMAND1_TEXT);
CAknInformationNote* informationNote;

informationNote = new (ELeave) CAknInformationNote;

// pokazujemy okno dialogowe informacyjne z tekstem z
// załadowanym z treścią z StringLoader.

informationNote->ExecuteLD(*textResource);

// wyrzucamy HBuf z CleanUpStack i niszczymy to z pamięci
CleanupStack::PopAndDestroy(textResource);
}
break;
default:
// gdy wybierze się inne polecenie niż te które
// mamy opisane to zamykamy aplikację
Panic(EPierwszaAplikacjaUi);
break;
}
}
// -----------------------------------------------------------------------------
// Wywoływane przez framework kiedy zmieni sie położenie rozmiarów.
// Przekazuje nowy obszar rozmiaru aplikacji do AppView.
// -----------------------------------------------------------------------------
//
void CPierwszaAplikacjaAppUi::HandleStatusPaneSizeChange()
{
iAppView->SetRect(ClientRect());
}


// End of File

Do tego jeszcze dochodzi plik klasy z widokiem


/*
============================================================================
Name : PierwszaAplikacjaAppView.cpp
Author : Michał Małaj
Copyright : LGPL
Description : Implementacja widoku aplikacji.
============================================================================
*/

// INCLUDE FILES
#include <coemain.h>
#include "PierwszaAplikacjaAppView.h"

// ============================ Składowe ===============================

// -----------------------------------------------------------------------------
// CPierwszaAplikacjaAppView::NewL()
// Dwufazowy konstruktor
// -----------------------------------------------------------------------------
//
CPierwszaAplikacjaAppView* CPierwszaAplikacjaAppView::NewL(const TRect& aRect)
{
CPierwszaAplikacjaAppView* self = CPierwszaAplikacjaAppView::NewLC(aRect);
CleanupStack::Pop(self);
return self;
}

// -----------------------------------------------------------------------------
// CPierwszaAplikacjaAppView::NewLC()
// Dwufazowy konstruktor
// -----------------------------------------------------------------------------
//
CPierwszaAplikacjaAppView* CPierwszaAplikacjaAppView::NewLC(const TRect& aRect)
{
CPierwszaAplikacjaAppView* self = new (ELeave) CPierwszaAplikacjaAppView;
CleanupStack::PushL(self);
self->ConstructL(aRect);
return self;
}

// -----------------------------------------------------------------------------
// CPierwszaAplikacjaAppView::ConstructL()
// Drugi konstruktor może pozwolić na wywołanie "paniki"
// -----------------------------------------------------------------------------
//
void CPierwszaAplikacjaAppView::ConstructL(const TRect& aRect)
{
// wywołujemy metodę na tworzenie okna w tej aplikacji
CreateWindowL();

// ustawiamy rozmiary okna
SetRect(aRect);

// aktywujemy okno, które przerysowane na wyświetlaczu
ActivateL();
}

// -----------------------------------------------------------------------------
// CPierwszaAplikacjaAppView::CPierwszaAplikacjaAppView()
// Domyślny konstruktor C++ nie zawiera kodu
// -----------------------------------------------------------------------------
//
CPierwszaAplikacjaAppView::CPierwszaAplikacjaAppView()
{
// Brak kodu
}

// -----------------------------------------------------------------------------
// CPierwszaAplikacjaAppView::~CPierwszaAplikacjaAppView()
// Dekonstruktor
// -----------------------------------------------------------------------------
//
CPierwszaAplikacjaAppView::~CPierwszaAplikacjaAppView()
{
// Brak kodu
}

// -----------------------------------------------------------------------------
// CPierwszaAplikacjaAppView::Draw()
// Przerysowujemy wyświetlać
// -----------------------------------------------------------------------------
//
void CPierwszaAplikacjaAppView::Draw(const TRect& /*aRect*/) const
{

// pobieramy kontekst graficzny
CWindowGc& gc = SystemGc();

// pobieramy rozmiary do przerysowania
// rysujemy na danym obszarze
TRect drawRect(Rect());

// Czyścimy kontekst z pamięci (a zostaje widoczny na wyświetlaczu
gc.Clear(drawRect);

}

// -----------------------------------------------------------------------------
// CPierwszaAplikacjaAppView::SizeChanged()
// Zostanie to wywołane przez framework kiedy rozmiar widoku zmieni się
// -----------------------------------------------------------------------------
//
void CPierwszaAplikacjaAppView::SizeChanged()
{
DrawNow();
}

// -----------------------------------------------------------------------------
// CPierwszaAplikacjaAppView::HandlePointerEventL()
// Called by framework to handle pointer touch events.
// Note: although this method is compatible with earlier SDKs,
// it will not be called in SDKs without Touch support.
// -----------------------------------------------------------------------------
//
void CPierwszaAplikacjaAppView::HandlePointerEventL(
const TPointerEvent& aPointerEvent)
{

// Call base class HandlePointerEventL()
CCoeControl::HandlePointerEventL(aPointerEvent);
}

// End of File


Teraz napiszemy przygotujemy grafikę dla ikonki aplikacji. robimy ją w formacie SVG-T w folderze gfx. Pozostaje też zrobienie pliku graficznego HelloWorld.svg w tym folderze. Następnie trzeba będzie napisać kod skryptu dla kompilatora aby kompilator zmienił to w format *.mif. Plik skryptu icons.mk dla kompilatora znajduje się w podfolderze groups


# ============================================================================
# Name : icons.mk
#
# Description: to jest skrypt dla kompilatora to tworzenia pliku z mif z ikonką.
#
# ============================================================================


ifeq (WINS,$(findstring WINS, $(PLATFORM)))
ZDIR=$(EPOCROOT)epoc32\release\$(PLATFORM)\$(CFG)\Z
else
ZDIR=$(EPOCROOT)epoc32\data\z
endif

TARGETDIR=$(ZDIR)\resource\apps
ICONTARGETFILENAME=$(TARGETDIR)\PierwszaAplikacja_0xE82211C4.mif

ICONDIR=..\gfx

do_nothing :
@rem do_nothing
MAKMAKE : do_nothing
BLD : do_nothing
CLEAN : do_nothing
LIB : do_nothing
CLEANLIB : do_nothing
RESOURCE :
mifconv $(ICONTARGETFILENAME) \
/c32 /X $(ICONDIR)\HelloWorld.svg

FREEZE : do_nothing
SAVESPACE : do_nothing
RELEASABLES :
@echo $(ICONTARGETFILENAME)
FINAL : do_nothing


A teraz pozostało jeszcze przzygotowanie skryptu kompilacji PierwszaAplikacja.mmp
Ten plik będzie w folderze group

/*
============================================================================
Name : PierwszaAplikacja.mmp
Author : Michał Małaj
Copyright : LGPL
Description : To jest plik specyfikacji projektu dla PierwszaAplikacja.
============================================================================
*/


TARGET PierwszaAplikacja_0xE82211C4.exe
TARGETTYPE exe
UID 0x100039CE 0xE82211C4

SOURCEPATH ..\src
SOURCE PierwszaAplikacja.cpp
SOURCE PierwszaAplikacjaApplication.cpp
SOURCE PierwszaAplikacjaAppView.cpp
SOURCE PierwszaAplikacjaAppUi.cpp
SOURCE PierwszaAplikacjaDocument.cpp

SOURCEPATH ..\data

START RESOURCE PierwszaAplikacja.rss
HEADER
TARGET PierwszaAplikacja_0xE82211C4
TARGETPATH resource\apps
END //RESOURCE

START RESOURCE PierwszaAplikacja_reg.rss
TARGET PierwszaAplikacja_0xE82211C4_reg
TARGETPATH \private\10003a3f\apps
END //RESOURCE

USERINCLUDE ..\inc
SYSTEMINCLUDE \epoc32\include
LIBRARY euser.lib
LIBRARY apparc.lib
LIBRARY cone.lib
LIBRARY eikcore.lib
LIBRARY avkon.lib
LIBRARY commonengine.lib
LIBRARY efsrv.lib
LIBRARY estor.lib
LIBRARY aknnotify.lib
LIBRARY hlplch.lib

LANG SC 01 27

VENDORID 0
SECUREID 0xE82211C4
// End of file


Na koniec piszemy plik dla kompilatora bld.inf


/*
============================================================================
Name : bld.inf
Author : Michał Małaj
Copyright : LGPL
Description : Polecenia dla kompilatora
============================================================================
*/

PRJ_PLATFORMS
WINSCW GCCE

PRJ_MMPFILES
gnumakefile icons.mk
PierwszaAplikacja.mmp




Ale to jeszcze nie koniec trzeba też przygotować pliki prosto w paczkę instalacyjną
więc do katalogu sis trzeba zrobić plik do tworzenia paczki sis

; Plik instalacyjny PierwszaAplikacja
;
;Language - standard language definitions
&EN, PL

; standard SIS file header
#{"First Application", "Pierwsza Aplikacja"},(0xE82211C4),1,0,0

;Localised Vendor name
%{"Vendor-EN", "Vendor-PL"}

;Unique Vendor name
:"Vendor"

;Supports Series 60 v 3.0
[0x101F7961], 0, 0, 0, {"Series60ProductID", "S60ProductID"}

;Files to install
"C:\Symbian\9.2\S60_3rd_FP1\Epoc32\release\gcce\urel\PierwszaAplikacja_0xE82211C4.exe" -"!:\sys\bin\PierwszaAplikacja_0xE82211C4.exe"
{
"C:\Symbian\9.2\S60_3rd_FP1\Epoc32\data\z\resource\apps\PierwszaAplikacja_0xE82211C4.rsc"
"C:\Symbian\9.2\S60_3rd_FP1\Epoc32\data\z\resource\apps\PierwszaAplikacja_0xE82211C4.r27"
} -"!:\resource\apps\PierwszaAplikacja_0xE82211C4.rsc"
"C:\Symbian\9.2\S60_3rd_FP1\Epoc32\data\z\private\10003a3f\apps\PierwszaAplikacja_0xE82211C4_reg.rsc" -"!:\private\10003a3f\import\apps\PierwszaAplikacja_0xE82211C4_reg.rsc"
"C:\Symbian\9.2\S60_3rd_FP1\Epoc32\data\z\resource\apps\PierwszaAplikacja_0xE82211C4.mif" -"!:\resource\apps\PierwszaAplikacja_0xE82211C4.mif"

;required for application to be covered by backup/restore facility
"..\sis\backup_registration.xml" -"!:\private\E82211C4\backup_registration.xml"


Następną rzeczą jest zrobienie własnego certyfikatu do podpisywania
Albo najlepszym wg mnie rozwiązaniem jest wysłanie swojej aplikacji do podpisania na SymbianSigninig albo podpisac ją własnym podpisem developerskim.

środa, 20 maja 2009

Złote serduszko od fundacji Symbian

Dzisiaj o sukcesie w branży technologicznej decydują klienci i dobry marketing. Od pewnego czasu bardzo cieszę się z zmian jakie zachodzą w fundacji zarządzającej rozwojem systemu operacyjnego Symbian.
To co teraz się tam dzieje się to próba pokazania światu kim są ludzie którzy tworzą historię smartphonów. Dzisiaj trzeba się czymś wyróżniać. I wskazać cechy technologii które sprawi że użytkownicy zechcą identyfikować się z marką.
Czy marka "Symbian" ma jakieś przesłanie? Przeciętnemu człowiekowi nic nie mówi. Czyli pierwszą cechą będzie hermetyczność i elitarność. Elitarność dla programistów bo ludzi naprawdę zajmujących programowaniem w Symbianie C++ jest niewielu, chyba nawet o wiele mniej niż na Linux. A ludzi korzystających ze smartphonów z Symbianem więcej niż ludzi znających Linuxa, Druga cecha która rzuca się wśród ludzi zajmujących się Symbianem to innowacyjność i wyprzedzanie trendów. Można napisać, to co obecnie spowszedniało to kiedyś było bardzo innowacyjne. Dzisiaj w sytuacji gdzie innowacja sprzętowa już nie robi takiego wrażenia to jednak coraz bardziej liczy się interakcja użytkownika z urządzeniem. Dzisiaj sukces może polegać na tym to co bardziej zainteresuje użytkownika. Aby zainteresować zwykłego użytkownika to trzeba z niego zrobić geeka. Kogoś kto mógłby czuć się że jest mu dobrze pomiędzy zaawansowanymi funkcjami systemu Symbian. Z drugiej strony jak coś jest "zakazane" to bardziej skłania do działań ludzi do tego co jest zakazane, bądź obchodzenia tego. Tak to jest z "łamaniem" zabezpieczeń Symbiana i jak z zdejmowaniem simlocków.
Taka "ciemna" strona też sprzyja popularyzacji pośród zwykłych ludzi.
Ponieważ coraz większą rolę w tym elitarnej społeczności zaczynają pełnić kobiety, które chcą mieć dopowiedzenia o tym jak ma wyglądać interfejs użytkownika, to trzeba wymyślić coś co zainteresuje kobiety. Wystarczy kobietom pokazać jak Symbian jest systemem z interfejsem bardzo przyjaznym kobiecie oraz dodać trochę konserwatyzmu i romantyczności w kampanii marketingowej. Wizerunek jest bardzo ważną cechą reklamy. Teraz symbolem Symbiana stało się... złote serduszko.



Komiksowy sposób prezentacji staje się bardzo popularny. Zresztą też bardzo atrakcyjny w kulturze geeków. Oto teledysk Symbian Sketchbook



Stylizowana reklama dla managerów IT: What it's all about for a developer



Jak fajnie być w elitarnym gronie: Symbian Foundation Party



Jestem pod wrażeniem, że coraz więcej ludzi będzie chciało być w takim towarzystwie.

niedziela, 10 maja 2009

Ozone Web Browser

Już całkiem niedawno pisałem o tym że przeglądarki internetowe mobilne czeka dużo do zrobienia. Jeżeli zakup iPhone z Apple był powodem do tego że całkiem dobrze wyświetla strony internetowe i że nawigacja po internecie staje się coraz lepsza to wobec konkurencji przewagą było to że ma ekran dotykowy (wykorzystującego pojemność elektryczną) i posiada dobrą przeglądarkę internetową opartą na silniku WebKit. Obecnie w ślady iPhone zmierza platforma Android, który też posiada przeglądarkę internetową oparta na WebKit. Natomiast posiadacze smartphonów z Symbianem (tych jest bardzo dużo w porównaniu od tych z iPhonem czy Windows Mobile), dla nich pojawiła ostatnio bardzo dobra przeglądarka internetowa, która staje się konkurencją dla Opery Mini. Ozone Web Browser.
Zainstalowałem wersję 0.9 na swojej komórce E51 i okazało się że lepszej przeglądarki jeszcze nie widziałem w swojej komórce.
To co mnie zaskoczyło to: zmiana trybów przeglądania z komputerowego na mobilny, szybkość ładowania dużych stron, Ze strony technologicznej to już wspierają tryb offline, CSS3, i posiadają SquirrielFish jeden z wydajnych silników JavaScriptu.
Test Acid3 przeszedł z wynikiem 94/100. Zaintrygowany chciałem zobaczyć jak się przedstawia w logach i tutaj mnie kompletnie zaskoczyło: przedstawia się jako iPhone.
Oto pełny UserAgent tej przeglądarki:

Mozilla/5.0 (iPhone; U; CPU iPhone OS 2_2_1 like MacOS X; en-us ozone 0.9) AppleWebKit/525.18.1 (KHTML like Gecko) Version/3.1.1 Mobile/5F136 Safari/525.20

Pomyślałem że skoro wiele stron jest robionych pod iPhone to jak ktoś z tej przeglądarki zechce wejść na strony dla iPhone to może się czuć czasami zawiedziony (miałem na myśli API dla dotyku). Tą przeglądarkę tworzy Węgier Zalan Bujtas który kiedyś tworzył S60 Web Browser czy TeaShark
Zastanowiło mnie, to że rozwój przeglądarki internetowej dla Symbiana zależy od tego człowieka.