I had been fiddling with the idea of making an application completely
from scratch in Symbian so that I could know all the aspects of
application development. After pondering over some ideas , I
decided to make a TaskManager for Series 60 v2.0 devices.
The following article is a collection of my experiences while making
the application. I have added some tips,code examples and also
workarounds for common problems so that if another programmer
wants to do the same thing, he does not have to run to the forums.
Before starting to code the application I had decided to include
the following features in my task manager :
Task list : Show the user a task list. Well elaborate the difference between
task and process later.
Switch to app : Switch to another task window.
Kill task : Kill another running task.
Run : Allow the user to launch any app/exe by just entering the path and file name of the app/exe.
Compress heap : Allow the user to free unused heap.
Restart device : Restart the device.
Process list : Show the user a list of all processes (includes all the servers).
System info : Show the user details about the device and the OS.
Process info: Show some info about the process selected.
Switch view: Switch to another view.
Kill process : Kill a selected process.
Adding icon: Add an icon to the app, to replace the default icon.
Task info : Show some detailed info about the task selected.
Changing wallpaper : Allow the user to change the wallpaper of the device to any image selected.
From the above list I was not able to complete feature (14), the reasons I will elaborate
later. Well so lets see how I went about coding my TaskManager.
* -===[Where is my list?]===-
I had started by copying an application and changing its UID(I do that always).
I also got through quickly coding the menu and other UI, but I got stuck at showing
the list. The problem mainly was that I had done all the initialization, filled in the
data but still the list was not showing up. I ran to the forum for help, checking old
posts and saw that this is one of the most common issues while displaying the list. But
the solution for each of them was different. I will just tell you how I solved my problem.
My original listing was :
**********************Code starts here*******************
//Delete the list box if already exists.
if ( iListBox ){
delete iListBox;
iListBox = NULL;
}
iListBox = new (ELeave ) CAknDoubleStyleListBox();
iListBox->ConstructL(this, EAknListBoxSelectionList );
iListBox->SetContainerWindowL(*this);
//add scroll bar to the list
iListBox->CreateScrollBarFrameL(ETrue);
iListBox->ScrollBarFrame()->SetScrollBarVisibilityL(CEikScrollBarFrame::EOn,CEikScrollBarFrame::EAuto);
//Fill list box with the data
InitTaskList();
iListBox->HandleItemAdditionL();
iListBox->ActivateL();
iListBox->DrawNow();
*********************Code ends here*************************
The above listing does not show the list box, b'coz this line is missing :
**********************Code starts here**********************
const TPoint aPoint(0,0);
const TSize aSize(180,150);
//most important line , else the lst box simply
disappears
//iListBox->SetExtent(aPoint,aSize);
**********************Code ends here************************
This line must be added after the scroll bar frame is set and before initializing the
task list items.
So what does this magical line , we will see.
CCoecontrol's SetExtent() causes the controls size and position to be set.
So now you are setting the area within which your control will be drawn , without which
the view is not sure about where to draw your control.
Some more theory : Whenever we derive from CCoeControl we also need to implement
two important functions mainly:
//returns the number of controls in our view
TInt CountComponentControls() const;
//returns the control reference depeding on the number in the present view
CCoeControl* ComponentControl(TInt aIndex) const;
Our view uses these two functions to call those controls draw automatically whenever
they need to be re-drawn.
So I had to just call this simple line and voila my list was visible.
* -===[Please tell me the list of tasks]===-
To put it simply a task is an instance of an UI bearing process. A process may be a application or a server which may not have any UI. I hope the difference is clear. Now moving to the easier parts. Geting the task list is easy. Just get the complete task list using
class RApaLsSession and from that list get those tasks that do exist and then you can
get all the related info from a TApaTask class.The minimal code for it is as follows:
**********************Code starts here**********************
RApaLsSession RSession;
TInt AAppCount = 0;//get the number of applications
RSession.Connect();
RSession.AppCount(AAppCount);//number of tasks
iAppCount = AAppCount;
iAppList = new (ELeave) CDesCArrayFlat(iAppCount);
RSession.GetAllApps();//get all the tasks in the RApaLsSession obj
if(AAppCount > 0){
//if the tasj is present get info about it in AppInfo
TApaAppInfo AppInfo;
TApaTaskList aList(CEikonEnv::Static()->WsSession());
for(TInt i=0;i<AAppCount;i++){
RSession.GetNextApp(AppInfo);
TApaTask ATask3 = aList.FindApp(AppInfo.iUid);
if(ATask3.Exists())
{
//get task related info such as
//Application caption : AppInfo.iCaption
//Full path name of the task : AppInfo.iFullName
//Uid of the task : AppInfo.iUid
//do all the realted processing here
}
}
}
**********************Code ends here************************
You can get a lot more information about the task using the Rthread class.
* -===[Device internals uncovered]===-
For getting all the information about the device you have to use the HAL class.
This is the one class that has all the info you wish to have about the device.
Check the same in the accompanying source. If you wish to have info such as
signal strength and battery strength you can use the RSystemAgent class.
There is a nice 'Sysinfo' example that provides a lot more info. I would
recommend that you go through the code. As far as using HAL class , this is how
its done:
**********************Code starts here**********************
HAL::Get(HALData::ECPU,aResult);
if(aResult == HALData::ECPU_ARM){
aCpu.Copy(_L("ARM"));
}else
if(aResult == HALData::ECPU_MCORE){
aCpu.Copy(_L("MCORE"));
}else{
aCpu.Copy(_L("X86"));
}
**********************Code ends here************************
This is the way, you just do a HAL::get using a attribute and then compare it with
the standard enumerations to determine the results.
The properties that can be queried can be found in the enum TAttribute of HALData class in
hal_data.h.
* -===[Compressing memory and its call related problems]===-
I am not so sure what compressing heap can achieve and if it reallt frees
some RAM or is just for some show.Anyway the call User::CompressAllHeaps
does compress the heaps in all the chunks.You can get the amount of the
heap freed by checking the free ram before and after this call.
The following code does the same:
**********************Code starts here**********************
TBuf<200> aBuf;
TInt BeforeFree=0,AfterFree=0;
TInt FreeHeapSize = 0;
_LIT(KHeap,"Total heap freed \n [ %d kb ]");
CAknInformationNote* informationNote;
HAL::Get(HALData::EMemoryRAMFree,BeforeFree);
User::CompressAllHeaps();
HAL::Get(HALData::EMemoryRAMFree,AfterFree);
FreeHeapSize = (AfterFree - BeforeFree) / 1024;
aBuf.Format(KHeap,FreeHeapSize);
informationNote = new (ELeave) CAknInformationNote;
informationNote->ExecuteLD(aBuf);
**********************Code ends here************************
* -===[Changing the app icon and its issues]===-
Changing the application's icon gave me some trouble.The main steps in creating the
icon involves creating the bmp's, packaging them into an mbm, writing the .rss and
then finally creating the aif.
For every icon you need to create toe bmp's : one contains the icon and the other is
its mask. In the mask ,any region that is marked white is transparent to the user.
No Separate packing of the bmp's into a mbm is required this can be done in the mmp file itself.
Just include the following line in you mmp file :
AIF taskmgr.aif ..\aif taskmgraif.rss c24 logo.bmp logo_mask.bmp
where:
AIF is the directive
taskmgr.aif - name of the aif file
..\aif - directory where the bmp and the <appname>aif.rss exist
c24 - is the color depth of the bmp's
logo.bmp logo_mask.bmp - is the icon and its mask bmp.
You can have any number of icons in you aif , just remember to have the bmp in this
order only i.e. the bmp and its mask.
The taskmgraif.rss has the following lines.
**********************Code starts here**********************
#include <aiftool.rh>
RESOURCE AIF_DATA
{
app_uid = 0x10005B60;
num_icons=1;
embeddability=KAppNotEmbeddable;
newfile=KAppDoesNotSupportNewFile;
}
**********************Code ends here************************
Most of the times you will be changing just the app_uid and the num_icons.
A single icon is a bmp and its mask. so even though I have two bmps but they both
make a single icon.
The most important point to remember is that the aif must have the same name as the app.
If your app is names Hello.app your aif must also be named Hello.aif , else the system
not find the aif file and it loads the default icons.
* -===[Restart device and issues]===-
It is not recommended to restart the device in such a way as it can corrupt the
filesystem and lead to other problems.The two methods to restart the device are:
Using UserSvr::ResetMachine(EStartupWarmReset).
Using RDebug::Fault(0).
I cannot say which of the methods are more safe , but they do the work quite nicely. The ideal way is to first try to close all existing apps and then only restart, this way you give all apps to save their unsaved data.
* -===[Kill process! Its not that simple]===-
Killing a process is not as simple as killing a task , as a process can be a sever an
app or a system server. It is not always possible kill a process as the user does not have
sufficient privileges to kill a process. This feature must only be used to kill user created
development servers which are not visible in the task list. For killing a process you first
need to open a handle to it and then only can you kill it. The following code tries to kill
a process.
**********************Code starts here**********************
TFindProcess aProcess2(_L("*"));
TFullName aResult;
while(aProcess2.Next(aResult)==KErrNone){
TInt idx = aPtr.Find(aResult);
if(idx!=KErrNotFound){//found process
RProcess aProcess;
aProcess.Open(aProcess2);
aProcess.Kill(0);
aProcess.Close();
InitProcessList();
break;
}
}
**********************Code ends here************************
This code first gets a complete process list and then finds the process that matches the
selected process and then kills it.
Killing a process does not guarantee that the phone will behave properly after it, so use
this function at your own discretion.
* -===[Getting a scrolling read-only message query]===-
When I was about to implement all the 'INFO' features I realized that I needed to display
a lot of info on the screen. I was thinking about using a dialog and then writing the
info on the dialog itself. But it did not seem to be a nice idea as t would involve
calculating all the coords and setting the fonts and all that. I got this solution
after searching around the forums , to use CAknMessageQueryDialog.
This dialog is cool for showing lots of text.
Its resource and the accompanying C++ call follows:
//Resource starts here
RESOURCE DIALOG r_message_query
{
flags = EGeneralQueryFlags | EEikDialogFlagNoBorder | EEikDialogFlagNoShadow;
buttons = R_AVKON_SOFTKEYS_OK_EMPTY;
items=
{
DLG_LINE
{
type = EAknCtPopupHeadingPane;
id = EAknMessageQueryHeaderId;
itemflags = EEikDlgItemNonFocusing;
control = AVKON_HEADING
{
};
},
DLG_LINE
{
type = EAknCtMessageQuery;
id = EAknMessageQueryContentId;
control = AVKON_MESSAGE_QUERY
{
};
}
};
}
//C++ calls starts here
HBufC *aTitle = iEikonEnv->AllocReadResourceLC(
aTitleResId);
CAknMessageQueryDialog* dlg = new (ELeave)CAknMessageQueryDialog();
dlg->PrepareLC( R_MESSAGE_QUERY );
dlg->SetMessageTextL(aInfoTxt);
dlg->QueryHeading()->SetTextL( aTitle->Des() );
dlg->RunLD();
CleanupStack::PopAndDestroy( aTitle );
**********************Code ends here************************
* -===[Arrgh ! I want to change the wallpaper]===-
Most of the TaskManager's functionality was completed within 3 days. Now I wanted
to implement the feature to change the device's wallpaper. I went to the forum
to check if this was possible. I found some post's that said, it was
possible, but not without restarting the 'TELEPHONE' application.
I thought that even this must not be a great deal , until I realized that the
'TELEPHONE' application was the most important application on the device, and killing
it would kill all other apps in the memory. That is when I gave up the idea of implementing
the feature. I am still optimistic that some hidden api will let me change the wallpaper.
* -===[Plug those memory leaks]===-
There have been many articles to find memory leaks and fix them, but I think that
its much easier to avoid memory leaks than to find and fix them.The most important thing
to remember is to whenever some in code you allocate some memory at that very moment
write the code to free it at the right place.Lets see an example , like when I am allocating
memory for aTitle at the same time I have added code to deallocate it, so now I am sure that
atleast I will suerly free this piece of heap.I can continue coding between these lines of code.
*********************Code starts here********************
HBufC *aTitle = iEikonEnv->AllocReadResourceLC( aTitleResId);
// add whatever usage code here between the alocation and deallocation part
CleanupStack::PopAndDestroy( aTitle );
**********************Code ends here*********************
This can be extended to R classes also,whenever you write code for opening the resource
at the same time write the code to close the resource and then write code that uses the resource
between these two lines .
* -===[Download]===-
* -===[The END]===-
As you would have noticed till now that I have always first searched the forums for an answer and then only went ahead with solving the problem. I do this b'coz I believe that 'Somewhere, someone has already faced the problem'. So its always good to check what path had they chosen for solving the problem. And also if there is no ready-made solution for the same you can always get some pointers for solving it.
Any suggestions or complains are welcome.I am thinking about porting the same to UIQ only if I get some positive feedback.
build error
I'm unable to build the sources for an older version of sdk.
I'm using the sdk 1.2 which is compatible with older devices such as my Nokia 7650 but I get this error when building:
Taskmgrappui.cpp - `EAknEnableSkin' undeclared (first use this function). I cannot find this term anywhere else.
Also when building for an older sdk be careful to set the target uid in the .pkg file like this:
(0x101F6F88 ), 0, 0, 0, "Series60ProductID"
Please make it compatible for all types of series 60 devices if possible
Thank You.
> build error
I just got enough time to port the same code to UIQ.But I don't have enough time to make it compatible to older devices. Any patch for the same is welcome. Thanks.
— Mayur.
Note : Pls include your name or some alias to address , I find it wierd to send mails addressed to none ;-)
> build error
Hi,
Can you please help me to find a way to get the CPU Load? I tried GetCpuTime but it always return 0
> My experiences making a Task Manager
Here comes a quick port for S60v1.
I took away some stuff related to skins and some HAL parameters that were not supported.
NOTE1: I took me less than hour to make the port, so it is not unlikely that the code may crash when wrong item gets selected etc. I is not tested much at all. Seems to be working N3660.
Compiled Borland C++,SDK V1.2.
NOTE2: bldmake bldfiles abld build armib urel makesis -s ..\sis\taskmgr.
NOTE3: some paths are hardcoded.
NOTE4: This code uses S7.0 way of doing, that has equally good S6.1 compatible stuff.
//JKJK TProcessMemoryInfo aInfo ;
//JKJK aProcess.GetMemoryInfo(aInfo);
TInt aCodeSize,aConstDataSize,aInitialisedDataSize, aUninitialisedDataSize;
aProcess.GetRamSizes(aCodeSize,aConstDataSize, aInitialisedDataSize,aUninitialisedDataSize);
TUint32 aSize = aCodeSize+aConstDataSize + aInitialisedDataSize + aUninitialisedDataSize;
URL of S60v1 port
http://www.hut.fi/~jkekoni/taskmgrS60v1.zip
!! Please Embed the software on the forum. The link will not live forever. !!
> URL of S60v1 port
> URL of S60v1 port
— Mayur.
> URL of S60v1 port
> My experiences making a Task Manager
This is very useful program. The source code also have helped me a lot to develop a personal taskmanager.
The task list doesn't show the application that are hidden using CApaWindowGroupName::SetHidden() method. Is there any way to access the application which are hidden in the above method?
> My experiences making a Task Manager
LINK : fatal error LNK1104: cannot open file "\Symbian\6.1\Series60\EPOC32\RELEASE\WINS\UDEB\commondialogs.lib"
How can ı find this dll.
ı use sdk1.1 but it has no commondialogs.lib
if it is a 2.0 library how the v1x source will be work. Thanks...
> My experiences making a Task Manager
> My experiences making a Task Manager
Hi!
this is really very fantastic article which is very encouraging for beginners . It is really very helpful . It's very good to learn from an experienced person's article.
IN FUTURE WRITE THIS TYPE OF ARTICLE IF IT IS POSSIBLE FOR U.
REALLY GOOD JOB
help me plz
My experiences making a Task Manager
In S60 3rd edition check the AknsWallpaperUtils.Check if it helps.