• Thứ Tư, 31/12/2008 13:52 (GMT+7)

    Tạo screen saver trên Nokia S60

    Cũng như màn hình PC, màn hình ĐTDĐ có trình bảo vệ màn hình - screen saver. Các loại ĐTDĐ S60 (2nd) của Nokia thường có screen saver hiển thị 2 dòng gồm ngày/tháng/năm giờ:phút trên dòng thứ nhất và các thông tin về Profile, SMS, cuộc gọi nhỡ. Tuy nhiên vẫn thiếu: thứ của ngày hiện tại, đôi khi bạn làm việc quá chăm chỉ mà quên mất hôm nay là thứ mấy trong tuần. Bài viết này giới thiệu cách tạo 1 screen saver cho ĐTDĐ, để đơn giản tôi mô phỏng chính trình screen saver của Nokia, nhưng có thêm 1 phần tử mới: thứ trong tuần. Ngoài ra, tôi cũng thay đổi font chữ to hơn cho dễ nhìn.

    Công cụ phát triển

    Các phần mềm cần thiết bao gồm:

    1. S60 SDK 2nd

    2. Carbide C++ 1.2

    3. Một đầu đọc thẻ nhớ (tương ứng với thẻ nhớ bạn đang dùng trong ĐTDĐ), có thể dùng 1 thiết bị bluetooth để truyền file từ PC sang ĐTDĐ.

    Bạn có thể vào trang www.forum.nokia.com để tải mục 1 và 2, hoàn toàn miễn phí, và xem bài viết hướng dẫn cài đặt và sử dụng Carbide trên TGVT số tháng 12/2006 (trang 127) với tiêu đề "Phát triển ứng dụng trên Symbian OS với Carbide. C++ Express".

    Khó khăn và thuận lợi

    Khó khăn:

    Phiên bản Carbide chỉ miễn phí khi bạn dùng bản Express, đối với bản này, bạn không thể debug trên thiết bị (ĐTDĐ) thực, ngay cả Emulator trên PC cũng không hỗ trợ chạy screen saver. Nếu bạn viết 1 screen saver mà vì lý do nào đó không thực thi được, sẽ chẳng có thông báo nào cho bạn biết cả. Bạn phải tự mình rà soát lại code để xác định lỗi.

    Có SDK của S60 Symbian không có nghĩa là bạn có thể viết bất cứ ứng dụng nào, Nokia vẫn giấu một số DLL, các hàm quan trọng mà chỉ khi nào bạn là 1 Gold Member của Forum Nokia thì mới có thể nhận được tài liệu về các hàm, hay DLL ẩn này. Do đó nếu có hàm nào Nokia sử dụng hay khuyến cáo nên dùng để "bỏ qua" vài hàm DLL không thực thi thì bạn cũng đừng quá ngạc nhiên.

    Thuận lợi:

    Nếu chịu khó tìm trên trang web của Nokia, bạn sẽ thấy file zip chứa mã nguồn của 1 screen saver do chính Nokia cung cấp làm mẫu. Screen saver này không giống với nguyên bản đang chạy trên ĐT, tuy nhiên chúng ta có thể dùng để tra cứu các hàm cần thiết cũng như cách thức viết 1 screen saver cho S60.

    Thực hiện

    Việc đầu tiên cần làm tất nhiên là mở Carbide lên và bắt tay viết 1 screen saver.

    Trong Carbide IDE, chọn menu File -> New -> Symbian OS C++ Project..., tiếp tục chọn Basic dynamically linked library (DLL).

    Thực ra 1 screen saver trong S60 đơn giản là 1 dynamic DLL, được HĐH gọi tự động. Do đó bạn cần 1 DLL chứ không phải là 1 ứng dụng nào khác.

    Chọn Next, đặt tên cho Project, ở đây tôi đặt là BaiVietPCWorld. Lưu ý, để cho việc build project suôn sẻ, bạn nên chọn thư mục lưu Project nằm cùng ổ đĩa cài Carbide (đổi Workspace sao cho nằm cùng ổ đĩa với Carbide), có thể Carbide không build được project nếu khác ổ đĩa.

    Nhấn Next tiếp, lúc này Carbide hỏi bạn build trên hệ thống nào, cách đơn giản là chọn hết, tuy nhiên như đã nói ở trên, chúng ta không thể debug trên PC, do đó chỉ nên chọn duy nhất mục Phone Release (ARMI). Đây chính là hệ thống trên máy ĐTDĐ của chúng ta. Nhấn Finish và Carbide sẽ tạo cho chúng ta một sườn DLL.

    Hình tiếp theo là cấu trúc một ứng dụng/DLL mà Carbide tạo ra.

    Để viết screen saver, bạn chỉ cần 1 class kế thừa từ MScreensaverPlugin, điều này là bắt buộc. Ở đây class chính của chúng ta là 2 file BaivietPCWorld.hBaivietPCWorld.cpp, ngoài ra còn có file BaivietPCWorld.mpp và file BaivietPCWorld.pkg, một file dùng để thêm các file lib cần cho quá trình build và file kia dành cho việc tạo ra file sis (file cài đặt của Symbian).

    Mở file BaivietPCWorld.h, như đã nói ở trên, chúng ta cần cho class chính này kế thừa MscreensaverPlugin. Bạn thay thế dòng khai báo class chính như sau:

    class CBaiVietPCWorld : public CCoeControl,public MScreensaverPlugin

    Khai báo các biến và hàm cần thiết như sau:

    private: // new functions
    void InitialzeGraphics (CWindowGc& aGc, TRect &aClearArea);
    void RandomDrawingPosition(TInt aRamdomRangeX,TInt aRandomRangeY, TInt& x , TInt& y);

    void AppendDayinWeek(TDes& aDes, const TTime& aTime);
    void AppendDayMonthYear(TDes& aDes, const TDateTime& aDateTime);

    TRgb RandomBackColor();
    TRgb RandomForeColor();

    private: // data
    MScreensaverPluginHost* iHost;
    TBool iStatusCallorMsg;
    TInt64 iRandomSeed;
    const CFont* iFontUsed;

    Và tất nhiên, còn các khai báo cho các hàm virtual mà chúng ta thừa kế từ MScreensaverPlugin, bao gồm:

    virtual TInt InitializeL(MScreensaverPluginHost *aHost);
    virtual TInt Draw(CWindowGc& aGc);
    virtual const TDesC16& Name() const;
    virtual TInt HandleScreensaverEventL (TScreensaverEvent aEvent, TAny *aData);
    virtual TInt PluginFunction (TScPluginCaps, TAny *)
    {
    return KErrNone;
    }

    Cài đặt trong file BaivietPCWorld.cpp như sau:

    _LIT(KBaiVietPCWorldName,"PCWorldScreenSaver");

    EXPORT_C MScreensaverPlugin* CreatePluginModule()
    {
    return new BaivietPCWorld;
    }

    CBaivietPCWorld::~ CBaivietPCWorld ()
    {
    }

    const TDesC16& CBaivietPCWorld::Name() const
    {
    return KBaiVietPCWorldName;
    }

    TInt CBaivietPCWorld::HandleScreensaverEventL(TScreensaverEvent /*aEvent*/, TAny */*aData*/)
    {
    return KErrNone;
    }

    Đừng quên include các file .h, gồm: gdi.h, eikenv.h, settinginfo.h, AknKeyLock.h và e32math.h. Đi kèm các file include này còn có các file .lib, gồm: euser.lib, cone.lib, avkon.lib, gdi.lib, platformenv.lib, eikcore.lib, apparc.lib, eikcoctl.lib.

    Để thêm các file lib này vào project, bạn chọn file BaiVietPCWorld.mmp, mở ra và chọn tab Libraries nằm bên dưới, sau đó lần lượt thêm các file lib thiếu vào.

    Tiếp theo, định nghĩa các hằng số được dùng:

    _LIT(KSeparator,":");
    _LIT(KZero,"0");
    _LIT(KSpace," ");
    _LIT(KSlash,"/");
    _LIT(KMon,"MON");
    _LIT(KTue,"TUE");
    _LIT(KWed,"WED");

    _LIT(KThu,"THU");
    _LIT(KFri,"FRI");
    _LIT(KSat,"SAT");
    _LIT(KSun,"SUN");

    Thêm vài hằng số kiểu integer:

    const TInt KScreenSizeX = 176;
    const TInt KscreenSizeY = 208;

    Đây chính là kích thước chuẩn của màn hình S60 2nd.

    Giờ chúng ta bắt đầu cài đặt các hàm chính. Ở đây, quan trọng nhất là hàm Draw dùng để hiển thị tất cả những gì chúng ta muốn. Nhưng đầu tiên, cần khởi tạo các biến trong hàm InitializeL.

    Tint CBaivietPCWorld::InitializeL(MScreensaverPluginHost *aHost){
    iHost = aHost;
    iHost->OverrideStandardIndicators();
    //Call Draw function for each 10 seconds
    iHost->SetRefreshTimerValue(10000000);
    TTime time;
    time.HomeTime();
    TDateTime dateTime = time.DateTime();
    TInt seed = dateTime.MicroSecond();
    iRandomSeed = seed;
    return KErrNone;
    }

    InitialzeGraphics: dùng xóa màn hình, đặt lại màu vẽ, kiểu vẽ và kiểu tô.

    void CBaivietPCWorld::InitialzeGraphics(CWindowGc& aGc, TRect &aClearArea)
    {
    aGc.Clear(aClearArea);
    aGc.SetBrushStyle(CGraphicsContext::ESolidBrush);
    aGc.SetPenStyle(CGraphicsContext::ESolidPen);
    }

    Theo sau là hàm tạo giá trị random cho các vị trí vẽ khác nhau. Như đã nói ở trên, dòng đầu tiên chúng ta sẽ vẽ theo dạng "thứ ngày/tháng/năm giờ:phút", dòng này được vẽ một cách ngẫu nhiên nằm ngang màn hình. Ở đây ta chỉ tạo ngẫu nhiên theo giá trị y mà thôi.

    void CBaivietPCWorld::RandomDrawingPosition(TInt aRamdomRangeX,TInt aRandomRangeY, TInt& ax, TInt& ay)
    {
    TReal yReal = Math::FRand(iRandomSeed);
    TInt y = (TInt(yReal*aRandomRangeY));
    ax = 0 ;
    ay = y ;
    }

    Hàm dùng tính toán thứ trong ngày:

    void CBaivietPCWorld::AppendDayinWeek(TDes& strDay, const TTime& aTime){
    TDay dayInWeek = aTime.DayNoInWeek();
    switch (dayInWeek){
    case EMonday:
    strDay.Append(KMon);
    break;
    case ETueday:
    strDay.Append(KTue);
    break;
    . . . . . . .
    //tương tự cho các thứ khác trong tuần
    }
    }

    Hàm tạo random giá trị cho màu nền của dòng vẽ "thứ ngày/tháng/năm giờ:phút"

    TRgb CBaivietPCWorld::RandomBackColor(){
    TRgb iReturnColor ;
    TInt iRandomNumber;
    iRandomNumber = Math::Random() % 7;
    switch (iRandomNumber){
    case 1:
    iReturnColor= KRgbDarkGray ;
    break;
    case 2:
    iReturnColor= KRgbDarkBlue ;
    break;
    . . . . . . . .
    }
    return iReturnColor;
    }

    Hàm dùng nối thêm tháng cho chuỗi hiển thị ngày/tháng/năm.

    void CNHPDateTime_ScreenSaver::AppendDayMonthYear(TDes& clockTime,const TDateTime& aDateTime){
    TMonth month ;
    month = aDateTime.Month();
    switch (month) {
    case EJanuary:
    clockTime.Append(KZero);
    clockTime.AppendNum(1);
    break;
    case EFebruary:
    clockTime.Append(KZero);
    clockTime.AppendNum(2);
    break;
    . . . . . .
    }
    }

    Chúng ta đã có hết những hàm cần thiết, giờ là lúc tập trung cho hàm chính: Draw. Đầu tiên khai báo các biến local, gồm:

    TInt iHightOfFont;//Chiều cao của font chữ được dùng
    TInt x; //vị trí vẽ x
    TInt y; //vi trí vẽ y
    TRgb iBackColor; //màu nền
    TRect clearArea; //vùng chữ nhật cần làm sạch truớc khi vẽ

    Tạo ra màu nền ngẫu nhiên:
    iBackColor = RandomBackColor();

    Cần đặt font chữ trước khi vẽ, ta sẽ dùng TitleFont:

    iFontUsed = iEikonEnv->TitleFont();
    gc.UseFont(iFontUsed);
    iHightOfFont = iFontUsed->HeightInPixels();

    Cần tạo vị trí random cho tọa độ vẽ, đơn giản chỉ gọi hàm như sau:

    RandomDrawingPosition(KScreenSizeX, KScreenSizeY, x,y);

    Định nghĩa vùng cần xoá, để đơn giản ta cho xóa toàn bộ màn hình:

    clearArea = TRect(TPoint(0,0), TPoint(KscreenSizeX, KScreenSizeY)) ;
    và thực hiện xóa màn hình : InitialzeGraphics(gc, clearArea);

    Giờ phải tính toán lại tọa độ y cần vẽ, lý do: nếu y = 0 -> dòng vẽ sẽ khuất phía trên màn hình, ngược lại nếu y = KscreenSizeY, chữ sẽ khuất cuối màn hình. Trong cả 2 trường hợp này, ta cần đặt lại giá trị y cho phù hợp:

    if(y < iHightOfFont){
    y = iHightOfFont ;
    }

    if ( (y + iHightOfFont + 10) > KScreenSizeY ){
    y = KScreenSizeY - (iHightOfFont + 10);
    }


    Giờ ta sẽ lấy thứ trong tuần và thời gian hiện tại:

    TTime time;
    time.HomeTime();
    TDateTime dateTime = time.DateTime();
    TBuf<3> strDay; // chuỗi thứ trong tuần
    TBuf<20> clockTime;//chuỗi thứ và ngày/tháng/năm giờ:phút

    Trích lấy thứ trong tuần thông qua hàm: AppendDayinWeek(strDay,time);

    Thêm vào chuỗi clockTime:
    clockTime.Append(strDay);
    clockTime.Append(KSpace);

    Có thứ rồi, giờ lấy ngày/tháng/năm:

    if(dateTime.Day() < 10)
    {
    clockTime.Append(KZero);
    }
    clockTime.AppendNum(dateTime.Day()+ 1);//ngày tính từ 0, do đó ta phải thêm 1
    clockTime.Append(KSlash);

    Với tháng, ta cần trích ra:

    AppendDayMonthYear(clockTime,dateTime);
    clockTime.Append(KSlash);
    và năm :
    clockTime.AppendNum(dateTime.Year());
    clockTime.Append(KSpace);

    Tương tự cho giờ và phút, thông qua dateTime.Hour() và dateTime.Minute().

    Đã lấy ra được chuỗi có dạng "FRI 10/10/2008 10:30", cần tính toán để vẽ. Khai báo 2 biến chứa độ cao và độ rộng của font chữ đang dùng, từ đó ta sẽ tính ra vùng chữ nhật đủ bao quanh dòng text này:

    width = iFontUsed->TextWidthInPixels(clockTime);
    height = iFontUsed->AscentInPixels() ;

    Đặt màu nền và vẽ hình chữ nhật với màu nền:

    gc.SetDrawMode(CGraphicsContext::EDrawModeXOR);
    gc.SetBrushStyle(CGraphicsContext::ESolidBrush);
    gc.SetBrushColor(iBackColor);
    gc.DrawRect(TRect( TPoint (0,y - iHightOfFont-height),
    TSize(KScreenSizeX,
    height + iHightOfFont + iFontUsed->DescentInPixels())));

    Chúng ta sẽ vẽ sao cho dòng text này nằm giữa màn hình.

    gc.SetPenColor(KRgbWhite);
    gc.SetPenStyle(CGraphicsContext::ESolidPen);
    x = KScreenSizeX/2 - width/2 ;
    gc.DrawText(clockTime,TPoint(x,y));

    Về việc vẽ biểu tượng key, Profile..., bạn có thể tham khảo thêm trong mã nguồn đính kèm.

    Giờ chúng ta bắt đầu tạo ra file cài đặt (.sis). Trước tiên bạn cần build project thành công đã, sau khi có được file DLL, sẽ tiến hành tạo file cài đặt. Mở file BaiVietPCWorld.pkg, thay đổi như sau:
    "$(EPOCROOT)Epoc32\release\$(PLATFORM)\$(TARGET)\BaiVietPCWorld.dll" -"!:\system\screensavers\BaiVietPCWorld.sc"

    Thật ra bạn chỉ cần sửa lại nơi cần chép DLL vào trong thư mục đích trên máy ĐTDĐ mà thôi. Còn không bạn chỉ việc copy DLL này vào trong thư mục C:\System\screensavers\ và đổi phần mở rộng thành .sc.

    Nhấn phải vào file BaiVietPCWorld.pkg và chọn Build Package, nhớ refesh lại thư mục để thấy file sis đã được tạo thành công.

    Sau khi chép screen saver này lên ĐTDĐ của bạn và cài đặt, chúng ta cần chỉnh cho ĐT dùng screen saver mới này. Làm như sau:

    Vào mục Themes, chọn theme bạn đang dùng, chọn Options/Edit, chọn tiếp Screen saver. Tại đây bạn sẽ thấy screen saver mới của mình.

    Nguyễn Hoài Phong
    Email: pnguyen43@csc.com

    ---------------------------------------------
    Tài liệu tham khảo:
    - ScreenSaverExamplePlugin_Nokia_S60_2nd.zip
    - www.forum.nokia.com
    - www.newLc.com

    ID: A0812_136
    File đính kèm