User:Emre/tb/activitymanagerInterface: Difference between revisions

 
(8 intermediate revisions by the same user not shown)
Line 2: Line 2:
{{draft}}
{{draft}}


== Entities ==
== Overview ==
[[Image:ActivityManagerDiagram.png]]


* <b>Activity Manager</b>: A service to manage and persist the activities generated by the core components and extensions.
= Activity Manager Concepts and Interfaces =
{{draft}}


* <b>@mozilla.org/activity-process;1</b>: The default implementation of a Process activity. It knows how to deal with predefined activity types. In the majority of the cases the consumer of the activity management system can initiate this component and use for its purpose.
== Overview ==
[[Image:ActivityManagerDiagram.png]]


* <b>@mozilla.org/activity-event;1</b>: The default implementation of an Event activity. It knows how to deal with predefined activity types. In the majority of the cases the consumer of the activity management system can initiate this component and use for its purpose.
== Interfaces ==
 
<i>See <b>nsIActivityManager.idl</b> and <b>nsIActivity.idl</b> files for more documentation </i>
 
* <b>nsIActivityMgrListener</b>: The implementer of this interface gets notified when an activity is added/removed to/from the activity list managed by Activity Manager. Activity developers do not need to implement this interface in order to introduce new activities to the system.
 
* <b>nsIActivityManager</b>: It is implemented by the default Activity Manager component, used by the Activity Manager consumers. Activity developers do not need to implement this interface in order to introduce new activities to the system.
 
* <b>nsIActivityContextDisplayHelper</b>: It is implemented and registered by the Activity developer to customize the context display text.
 
* <b>nsIActivity</b>: Base interface for both nsIActivityProcess, nsIActivityWarning and nsIActivityEvent. Exposes common attributes and methods. Activity developers do not need to implement this interface in order to introduce new activities to the system.
 
* <b>nsIActivityProcess</b>: [https://wiki.mozilla.org/Thunderbird:Activity_Manager#Processes Process] activity types should implement this interface. For the majority of the cases, default process component (@mozilla.org/activity-process;1) should suffice. In cases where additional functionality needed by the process, Activity developers can implement their own version of Process activity and extend the system.
 
* <b>nsIActivityEvent</b>: [https://wiki.mozilla.org/Thunderbird:Activity_Manager#Events Event] activity types should implement this interface. For the majority of the cases, default event component (@mozilla.org/activity-event;1) should suffice. In cases where additional functionality needed by the event, Activity developers can implement their own version of Event activity and extend the system.
 
* <b>nsIActivityWarning</b>: Warning activity types should implement this interface. For the majority of the cases, default warning component (@mozilla.org/activity-warning;1) should suffice. In cases where additional functionality needed by the warning, Activity developers can implement their own version of Warning activity and extend the system.
 
* <b>nsIActivityListener</b>: The implementer of this interface gets notified when the subscribed activity changes state, progress and more.
 
* <b>nsIActivityPauseHandler</b>: If provided with the activity, it allows the user to pause/resume the activity during its progress. Process types only. Activity developers might implement a component realizing this interface and associate with the activity in question. When done, default binding (XBL) representing the activity in the Activity Manager window will automatically show a pause/resume button attached to the activity.
 
* <b>nsIActivityRetryHandler</b>: If provided with the activity, it allows the user to retry the failed activity. Process types only. Activity developers might implement a component realizing this interface and associate with the activity in question. When done, default binding (XBL) representing the activity in the Activity Manager window will automatically show a retry button attached to the activity.
 
* <b>nsIActivityCancelHandler</b>: If provided with the activity, it allows the user to cancel the activity. Process types only. Activity developers might implement a component realizing this interface and associate with the activity in question. When done, default binding (XBL) representing the activity in the Activity Manager window will automatically show a cancel button attached to the activity.
 
* <b>nsIActivityUndoHandler</b>: If provided with the activity, it allows the user to undo the operation subject of the event. Persisted Events might not be undone.<i> This feature hopefully will be supported in the future when we have a better undo mechanism in place.</i> Obviously Event types only. Activity developers might implement a component realizing this interface and associate with the activity in question. When done, default binding (XBL) representing the activity in the Activity Manager window will automatically show a undo button attached to the activity.
 
* <b>nsIActivityRecoveryHandler</b>: If provided with the activity, it allows the user to activste the operation to recover from the sitation causing the warning. Warning types only. Activity developers might implement a component realizing this interface and associate with the activity in question. When done, default binding (XBL) representing the activity in the Activity Manager window will automatically show a pause/resume button attached to the activity.


* <b>Activity Initiator</b>: An entity that generates the activity, such as a component of TB or an Extension. It is internal to the activity and can be anything represented by a nsISupports interface. It is optional for user defined activities.
== Default Components ==
** For example, <i>sync</i> activity stores the reference of nsIAutoSyncManager service in this property to be used by nsIActivityCancelable implementation in order to cancel the activity.


* <b>Activity Context</b>: A context given by the initiator. This property is used by the Activity Manager UI to group the activities sharing the same <i>contextType</i> and <i>contextVal</i> values. Context type can be one of the predefined contexts or an user defined one. Activity Manager knows how to show the <i>Context Display Text</i> of predefined types. For user defined types, either the user should register an implementation of nsIActivityContextDisplayHelper, or should pass the display text to <i>contextVal</i> property of the activity.
* <b>@mozilla.org/activity-manager;1</b>: Implements the nsIActivityManager. Provides the facilities to manage activities introduced by the backend, extensions, and the user.  
**Predefined contexts are;
*** Account: it groups the activities by given nsIMsgIncomingServer. For example if the contextType is 'account' and contextVal is (nsIMsgIncomingServer of myname@gmail.com acocunt) then all activities belong to this server show up under the same group. See [https://wiki.mozilla.org/Thunderbird:Activity_Manager#Account%20Activity Account Activity]. 
*** Smtp: Similarly, it groups the activities by given smtp server.
*** Calendar: It groups the activities by the calendar. Since there is only one calendar, <i>contextVal</i> can be omitted.
*** Addressbook: It groups the activities by the addressbook. <i>contextVal</i> should represents the proper address book.


* <b>Activity Type</b>: Type of the activity. Can be one of the predefined types or an user defined one. Activity Manager knows how to localize and display the predefined types on the UI. For user defined types, either the user should provide the localized texts or should provide a ...(Not sure we need an interface here) interface.
* <b>@mozilla.org/activity-process;1</b>: The default implementation of a Process activity. It knows how to deal with different process states. In majority of the cases it is enough to instantiate this component to add a new Process activity to the Activity Management system - see samples below.
** Predefined activity types are:
*** send: Sending an email.
*** sync: Synchronizing the local store with the server.
*** copy: Copying messages from one location to another.
*** move: Moving messages from one location to another.
*** delete: Deleting messages.
*** add_item: Generic item adding activity. Can be adding a new contact to the address book, or a new event to the calendar.
*** remove_item: Similar to add_item type.
*** update: Updating a folder (?? probably we don't need that)
*** compacting: Compacting a folder.
*** indexing: Indexing the messages in a folder (OS indexing service integration component or gloda are possible initiators of this type).
*** undo: Undo operation on an activity event.


* <b>Processes</b>: Processes are activities that happens in a time-span with possibility of failure. They are distinct entities, and they are not mapped to Events automatically because of the following reasons;
* <b>@mozilla.org/activity-event;1</b>: The default implementation of an Event activity. In majority of the cases it is enough to instantiate this component to add a new Event activity to the Activity Management system - see samples below.
** Initiator could prefer to generate multiple processes but one event for the activity. For example, Auto-sync manager generates a process each time it syncs a folder but only one "Account is syncd" event when the account is fully syncd with the server.
** One-shot activities do not require processes. For example Addressbook generates an add_item event each time the user adds a new contact to the address book without generating a process for it.
** Processes are not persisted by Activity Manager.


* <b>Event</b>: Events are not Processes in COMPLETED state. They are distinct entities, and they represents a finished and undoable (if nsIActivityUndoable is provided) activity. Events are persisted by Activity Manager.
* <b>@mozilla.org/activity-warning;1</b>: The default implementation of a Warning activity. In majority of the cases it is enough to instantiate this component to add a new Warning activity to the Activity Management system - see samples below.


* <b>Subject List</b>: A list of the entities are subject to the activity. It can store any activity specific entity. It is optional.
== Error Handling ==
**For example, Move operation stores source and target folders in this list to be used by nsIActivityRetyable and/or nsIActivityUndoable implementations of the activity, if provided.
TBD


* <b>UI elements</b>:
== Extending the Activity System ==
** Context Display Text: The short description of the context. User defined contexts should be localized by nsIActivityContextDisplayHelper::getContextDisplayText() implementation. Predefined contexts are localized by Activity Manager.
* If the default implementation of nsIActivityProcess, nsIActivityWarning and nsIActivityEvent are not sufficient for the activity initiator, Activity developers can provide their own components to extend the capabilities. <i>Default implementations can be found in nsActivity.js file.</i>
** Display Text: The short description of the activity. Should be localized during the initialization of the activity by the initiator. i.e.  "Synchronizing Folder 'bugs'".
 
** Status Text: The text describing the status of the activity. Should be localized by the entity setting the progress of the activity. i.e. "100 of 300 messages"
* If Activity developers would like to extend the default UI representation of the activity types, they can provide their own XBL elements for their own activity types. All custom activity XBL elements should inherit from "activity-base" binding. For XBL sample please see <i>activity.xml</i> and <i>activity.css</i> files.
** Progress bar: <i>Self explanatory</i>
 
== Scenarios ==
 
=== Showing an user-defined activity on the activity manager window ===
 
The following sample will show a process and an event for junk processing on the activity manager window.
// Step 1: Adding a Process into the activity manager
const nsIAP = Ci.nsIActivityProcess;
const nsIAE = Ci.nsIActivityEvent;
const nsIAM = Ci.nsIActivityManager;
let gActivityManager = Cc["@mozilla.org/activity-manager;1"].getService(nsIAM);
let process = Cc["@mozilla.org/activity-process;1"].createInstance(nsIAP);
// Assuming <i>folder</i> is an instance of nsIMsgFolder interface
// Localization is omitted, initiator is not provided
process.init("Processing folder: " + folder.prettiestName,
              null);
// Note that we don't define a custom icon, default process icon
// will be used
process.contextType = "account";    // group this activity by account
process.contextObj = folder.server;  // account in question
gActivityManager.addActivity(process);
// Step 2: Showing some progress
let percent = 50;
process.setProgress(percent, "Junk processing 25 of 50 messages", 25, 50);
// Step 3: Removing the process and adding an Event using Process' attributes
process.state = Components.interfaces.nsIActivityProcess.STATE_COMPLETED;
gActivityManager.removeActivity(process.id);
let event = Cc["@mozilla.org/activity-event;1"].createInstance(nsIAE);
// Localization is omitted, initiator is omitted
event.init(folder.prettiestName + " is processed",
            null,
            "No junk found",
            process.startTime,  // start time
            Date.now());        // completion time
event.contextType = process.contextType; // optional
event.contextObj = process.contextObj;  // optional
             
gActivityManager.addActivity(event);
 
=== Showing an user-defined activity with cancel capability (JS)===
 
This sample will improve the previous one by providing an nsIActivityCancelHandler to allow the user cancel the process.
 
// Step 1: Create a nsIActivityCancelHandler implementation
function CancelJunkProcess()
{
  // user stuff here..
}
CancelJunkProcess.prototype = {
  cancel: function(aActivity) {
    let initiator = aActivity.initiator;
    if (initiator) {
      let folder = aActivity.getSubjects({})[0];
      ....
      // assuming that the initiator has some way to cancel
      // the junk processing for given folder
      if (initiator.cancelFolderOp(folder)) {
        aActivity.state = Components.interfaces.nsIActivityProcess.STATE_CANCELED;
        gActivityManager.removeActivity(aActivity.id);
        return Cr.NS_SUCCESS;
      }
    }
  return Cr.NS_FAILURE;
  }
}
// Step 2: Modify the previous sample to add initiator, subject
// and associate a nsIActivityCancelHandler component
...
// assuming that gJunkProcessor is the entity initiating the junk processing
// activity, and it has cancelFolderOp method that cancels the operations on folders
process.init("Processing folder: " + folder.prettiestName,
              gJunkProcessor);
// folder is being filtered/processed
process.addSubject(folder);
process.cancelHandler = new CancelJunkProcess();
...
 
Since nsIActivityCancelHandler is provided with the activity, UI will show a cancel button besides the activity. You can extrapolate this sample to nsIActivityRetryHandler and nsIActivityPauseHandler as well.
 
=== Showing an user-defined activity with undo capability (C++) ===
 
.....
#include "nsIActivity.h"
#include "nsIActivityManager.h"
.....
 
//////////////////////////////////////////////////////////////////////////////
//// Undo handler implementation
class myCopyEventUndo : public nsIActivityUndoHandler
  public:
  NS_DECL_ISUPPORTS
  NS_DECL_NSIACTIVITYUNDOHANDLER
  myCopyEventUndo() {}
  private:
  ~myCopyEventUndo() {}
};
NS_IMPL_ISUPPORTS1(myCopyEventUndo, nsIActivityUndoHandler)
 
NS_IMETHODIMP myCopyEventUndo::Undo(nsIActivityEvent *event, nsresult *result)
{
  nsresult rv;
  // get the subjects of this copy event
  PRUint32 length;
  nsIVariant **subjectList;
  rv = event->GetSubjects(&length, &subjectList);
  if(NS_FAILED(rv))
    return rv;
 
  // first subject in the list is the source folder in this particular case
  nsCOMPtr<nsIMsgFolder> folder = do_QueryInterface(subjectList[0]);
 
  // get the initiator
  nsIVariant *initiator;
  event->GetInitiator(&initiator);
   
  if (initiator)
  {
    nsISupports* ptr;
    rv = object->GetAsISupports(&ptr);
    if(NS_FAILED(rv))
      return rv;
     
    nsCOMPtr<nsIMsgCopyService> copyService = do_QueryInterface(ptr);
    if (copyService)
      copyService->Undo(folder);
  }
 
  return (*result = NS_OK);
  }
 
/////////////////////////////////////////////////////////////////////////////
//// Creating an undoable copy event
nsCOMPtr<nsIActivityUndoHandler> undoHandler = new myCopyEventUndo();
nsCOMPtr<nsIActivityEvent> copyEvent(do_CreateInstance("@mozilla.org/activity-event;1"));
// The initiator of this particular event is CopyService. We want to
// associate it with the event since we are going to use it in the
// undo handler.
// Same for the source folder, it is required for undo operation, but
// since it is the subject of this activity (event), it goes into the
// subject list.
// wrap copyservice in a nsvariant component
nsCOMPtr<nsIWritableVariant> initiator = do_CreateInstance(NS_VARIANT_CONTRACTID);
initiator->SetAsISupports(reinterpret_cast<nsISupports*>(copyService));
 
// subject of the delete operation is the imap folder
// wrap it in a nsvariant component
nsCOMPtr<nsIWritableVariant> srcFolder = do_CreateInstance(NS_VARIANT_CONTRACTID);
srcFolder->SetAsISupports(reinterpret_cast<nsISupports*>(imapFolder));
copyEvent->AddSubject(srcFolder);
copyEvent->Init(NS_LITERAL_STRING("Message copy event"),
                initiator,
                NS_LITERAL_STRING("Completed successfully"),
                PR_Now() / PR_USEC_PER_MSEC,  // start time
                PR_Now() / PR_USEC_PER_MSEC);  // completion time
// Do not forget to increase the ref counter if needed
copyEvent->SetUndoHandler(undoHandler); 
 
////////////////////////////////////////////////////////////////
//// Adding the event into Activity Manager
nsCOMPtr<nsIActivityManager> activityMgr(do_GetService("@mozilla.org/activity-manager;1"));
PRUint32 id;
activityMgr->AddActivity(copyEvent, &id);
 
=== Adding an activity with custom context type ===
 
This sample shows how to provide a custom context type for the junk processing. As a result, all junk processing activities (assuming that we process accounts in parallel) processing the messages coming from the same sender will be grouped together.
//optional: define some convenience constants
  const nsActProcess = Components.Constructor("@mozilla.org/activity-process;1",
                                            "nsIActivityProcess", "init");
const nsActEvent = Components.Constructor("@mozilla.org/activity-event;1",
                                          "nsIActivityEvent", "init");
  // Step 1: Implement nsIActivityContextDisplayHelper to show a
  //        customized  display text for our context type
function SenderContextDisplayHelper()
{
  // user stuff here..
}
SenderContextDisplayHelper.prototype = {
  getContextDisplayText: function(contextType, contextObj) {
    // in this particular example we know that contextType is "Sender"
    // since we also pass the contextType along with the contextObject
    //  in some cases, one helper can be registered for a group of context types
    // we know that the context object is the author of the message
    // Localization is omitted
    return "Messages coming from " + contextObj.surname + ", " + contextObj.firstname;
  }
}
// Step 2: Register the helper for this context type
gActivityManager.registerContextDisplayHelper("Sender",
                                              new SenderContextDisplayHelper());
// Step 3: Create the process
...
let process = new nsActProcess("Processing folder: " + folder.prettiestName,
                                gJunkProcessor);
process.cancelHandler = new CancelJunkProcess();
// folder is being filtered/processed
process.addSubject(folder);
process.contextType = "Sender";
// assuming msg is an instance of nsIMsgHdr
process.contextObj = msg.author;
...
 
=== Adding a fully customized activity ===
 
In complex scenarios, it might be inevitable for extensions to implement their own version nsIActivityProcess, nsIActivityWarning and nsIActivityEvent interfaces. In such case, nsActivity.js can be used as a model.
 
=== Changing the activity bindings ===
TBD
 
== Relevant Links ==
 
* https://wiki.mozilla.org/Thunderbird:Activity_Manager
* https://wiki.mozilla.org/Thunderbird:Activity_Manager/Window
* https://wiki.mozilla.org/Thunderbird:Interactive_Status_Bar
* http://clarkbw.net/designs/interactive-status-bar/activity%20manager.svg
 
== Relevant Bugs ==
{{bug|257942}}


== Interfaces ==
== Interfaces ==
Line 68: Line 322:
** ACTIVITY_WAITINGFORRETRY: A failure has been occurred and the activity is waiting to be retried by the user, or by the Activity Manager.
** ACTIVITY_WAITINGFORRETRY: A failure has been occurred and the activity is waiting to be retried by the user, or by the Activity Manager.


* <b>nsIActivityEvent</b>:[https://wiki.mozilla.org/Thunderbird:Activity_Manager#Events Event] activity types should implement this interface. ACTIVITY_COMPLETED is the only valid state for this type.  
* <b>nsIActivityEvent</b>:[https://wiki.mozilla.org/Thunderbird:Activity_Manager#Events Event] activity types should implement this interface. ACTIVITY_COMPLETED is the only valid state for this type.
 
* <b>nsIActivityWarning</b>:TBD


* <b>nsIActivityListener</b>: The implementor of this interface gets notified when the activity in question changes state or changes progress.  
* <b>nsIActivityListener</b>: The implementor of this interface gets notified when the activity in question changes state or changes progress.  
Line 79: Line 335:


* <b>nsIActivityUndoable</b>: If provided with the activity, it allows the user to undo the operation subject of the event. Persisted Events cannot be undo.<i> This feature will be supported in the future when we have a better undo mechanism in place.</i>
* <b>nsIActivityUndoable</b>: If provided with the activity, it allows the user to undo the operation subject of the event. Persisted Events cannot be undo.<i> This feature will be supported in the future when we have a better undo mechanism in place.</i>
== Error Handling ==
TBD


== Extending the Activity System ==
== Extending the Activity System ==
Line 86: Line 345:


== Scenarios ==
== Scenarios ==
<i> A sample extension and C++ samples will follow </i>
=== Showing an user-defined activity on the activity manager window ===
The following sample will show a process and an event for junk processing on the activity manager window.
// Step 1: Adding a Process into the activity manager
const nsIAP = Ci.nsIActivityProcess;
const nsIAE = Ci.nsIActivityEvent;
const nsIAM = Ci.nsIActivityManager;
let gActivityManager = Cc["@mozilla.org/activity-manager;1"].getService(nsIAM);
let process = Cc["@mozilla.org/activity-process;1"].createInstance(nsIAP);
// Assuming <i>folder</i> is an instance of nsIMsgFolder interface
// Localization is omitted, initiator is not provided
process.init("Processing folder: " + folder.prettiestName, null);
process.type = "junk_filtering";    // type of the activity
process.contextType = "account";    // group this activity by account
process.contextVal = folder.server;  // account in question
gActivityManager.addActivity(process);
// Step 2: Showing some progress
let percent = 50;
process.setProgress(percent, "Junk processing 25 of 50 messages", 25, 50);
// Step 3: Removing the process and adding an Event using Process' properties
process.state = ci.nsIActivity.ACTIVITY_COMPLETED;
gActivityManager.removeActivity(process.id);
let event = Cc["@mozilla.org/activity-event;1"].createInstance(nsIAE);
// Localization is omitted
event.init(folder.prettiestName + " is processed", "No junk found",
            process.startTime, Date.now());
event.type = process.type;              // optional
event.contextType = process.contextType; // optional
event.contextVal = process.contextVal;  // optional
             
gActivityManager.addActivity(event);
Optionally, if the initiator prefers to show each activity of this activity type standalone, it can do so by registering junk_filtering as VIEW_TYPE_STANDALONE:
gActivityManager.registerActivityViewType("junk_filtering", VIEW_TYPE_STANDALONE);
=== Showing an user-defined activity with cancel capability (JS)===
This sample will improve the previous one by providing an nsIActivityCancelable to allow the user cancel the process.
 
// Step 1: Create a nsIActivityCancelable implementation
function CancelJunkProcess()
{
  // user stuff here..
}
CancelJunkProcess.prototype = {
  cancel: function(aActivity) {
    let initiator = aActivity.initiator;
    let folder = aActivity.getSubjects({})[0];
    ....
    // assuming that the initiator has a method to cancel the
    // junk processing for given folder
    if (initiator.Cancel(folder)) {
    aActivity.state = Ci.nsIActivity.ACTIVITY_CANCELED;
    gActivityManager.removeActivity(aActivity.id);
    return Cr.NS_SUCCESS;
    }
  return Cr.NS_FAILURE;
  }
}
// Step 2: Modify the previous sample to add initiator, subject
// and nsIActivityCancelable interface
...
// assuming that gJunkProcessor is the entity initiating the junk processing
// activity
process.initWithHandlers("Processing folder: " + folder.prettiestName,
                          gJunkProcessor,
                          new CancelJunkProcess(),
                          null, null);
// folder is being filtered/processed
process.addSubject(folder);
...
Since nsIActivityCancelable is provided with the activity, UI will show a cancel button besides the activity. You can extrapolate this sample to nsIActivityRetryable and nsIActivityPausable as well.
=== Showing an user-defined activity with undo capability (C++) ===
.....
#include "nsIActivity.h"
#include "nsIActivityManager.h"
.....
//////////////////////////////////////////////////////////////////////////////
//// Undo handler implementation
class myCopyEventUndo : public nsIActivityUndoHandler
  public:
  NS_DECL_ISUPPORTS
  NS_DECL_NSIACTIVITYUNDOHANDLER
  myCopyEventUndo() {}
  private:
  ~myCopyEventUndo() {}
};
NS_IMPL_ISUPPORTS1(myCopyEventUndo, nsIActivityUndoHandler)
NS_IMETHODIMP myCopyEventUndo::Undo(nsIActivityEvent *event, nsresult *result)
{
  nsresult rv;
  // get the subjects of this copy event
  PRUint32 length;
  nsIVariant **subjectList;
  rv = event->GetSubjects(&length, &subjectList);
  if(NS_FAILED(rv))
    return rv;
 
  // first subject in the list is the source folder in this particular case
  nsCOMPtr<nsIMsgFolder> folder = do_QueryInterface(subjectList[0]);
 
  // get the initiator
  nsIVariant *initiator;
  event->GetInitiator(&initiator);
   
  if (initiator)
  {
    nsISupports* ptr;
    rv = object->GetAsISupports(&ptr);
    if(NS_FAILED(rv))
      return rv;
     
    nsCOMPtr<nsIMsgCopyService> copyService = do_QueryInterface(ptr);
    if (copyService)
      copyService->Undo(folder);
  }
 
  *result = NS_OK;
  return NS_OK;
  }
/////////////////////////////////////////////////////////////////////////////
//// Creating an undoable copy event
nsCOMPtr<nsIActivityUndoHandler> undoHandler = new myCopyEventUndo();
nsCOMPtr<nsIActivityEvent> copyEvent(do_CreateInstance("@mozilla.org/activity-event;1"));
// The initiator of this particular event is CopyService. We want to
// associate it with the event since we are going to use it in the
// undo handler.
// Same for the source folder, it is required for undo operation, but
// since it is the subject of this activity (event), it goes into the
// subject list.
// wrap copyservice in a nsvariant component
nsCOMPtr<nsIWritableVariant> initiator = do_CreateInstance(NS_VARIANT_CONTRACTID);
initiator->SetAsISupports(reinterpret_cast<nsISupports*>(copyService));
 
// subject of the delete operation is the imap folder
// wrap it in a nsvariant component
nsCOMPtr<nsIWritableVariant> srcFolder = do_CreateInstance(NS_VARIANT_CONTRACTID);
srcFolder->SetAsISupports(reinterpret_cast<nsISupports*>(imapFolder));
copyEvent->AddSubject(srcFolder);
copyEvent->InitWithHandlers(
                          NS_LITERAL_STRING("Message copy event"),
                          NS_LITERAL_STRING("Completed successfully"),
                          PR_Now() / PR_USEC_PER_MSEC,  // start time
                          PR_Now() / PR_USEC_PER_MSEC,  // completion time
                          initiator,
                          undoHandler);
 
////////////////////////////////////////////////////////////////
//// Adding the event into Activity Manager
nsCOMPtr<nsIActivityManager> activityMgr(do_GetService("@mozilla.org/activity-manager;1"));
PRUint32 id;
activityMgr->AddActivity(copyEvent, &id);
=== Adding an activity with custom context type ===
This sample shows how to provide a custom context type for the junk processing. As a result, all junk processing activities (assuming that we process accounts in parallel) processing the messages coming from the same sender will be grouped together.
  // Step 1: Implement nsIActivityContextDisplayHelper to show a
  //        customized  display text for our context type
function SenderContextDisplayHelper()
{
  // user stuff here..
}
SenderContextDisplayHelper.prototype = {
  getContextDisplayText: function(aActivity) {
    let context = aActivity.contextVal;
    ...
    // we know that the context object is the author of the message
    // Localization is omitted
    return "Messages coming from " + context;
  }
}
// Step 2: Register the helper for this context type
gActivityManager.registerContextDisplayHelper("Sender",
                                              new SenderContextDisplayHelper());
// Step 3: Create the process
...
// assuming msg is an instance of nsIMsgHdr
process.initWithHandlers("Processing folder: " + folder.prettiestName,
                          gJunkProcessor,
                          new CancelJunkProcess(),
                          null, null);
// folder is being filtered/processed
process.addSubject(folder);
process.contextType = "Sender";
process.contextVal = msg.author;
...
=== Adding a fully customized activity ===
In complex scenarios, it might be inevitable for extensions to implement their own version nsIActivityProcess and nsIActivityEvent interfaces. In such case, nsActivity.js can be used as a model.


* Adding an activity for junk processing
=== Changing the activity bindings ===
* Adding an activity with cancelable capability
TBD
*


== Relevant Links ==
== Relevant Links ==
Line 96: Line 573:
* https://wiki.mozilla.org/Thunderbird:Activity_Manager/Window
* https://wiki.mozilla.org/Thunderbird:Activity_Manager/Window
* https://wiki.mozilla.org/Thunderbird:Interactive_Status_Bar
* https://wiki.mozilla.org/Thunderbird:Interactive_Status_Bar
* http://clarkbw.net/designs/interactive-status-bar/activity%20manager.svg


== Relevant Bugs ==
== Relevant Bugs ==
{{bug|257942}}

Latest revision as of 23:01, 19 December 2008

Activity Manager Concepts and Interfaces

  THIS PAGE IS A WORKING DRAFT  
The page may be difficult to navigate, and some information on its subject might be incomplete and/or evolving rapidly.
If you have any questions or ideas, please add them as a new topic on the discussion page.

Overview

 

Activity Manager Concepts and Interfaces

  THIS PAGE IS A WORKING DRAFT  
The page may be difficult to navigate, and some information on its subject might be incomplete and/or evolving rapidly.
If you have any questions or ideas, please add them as a new topic on the discussion page.

Overview

 

Interfaces

See nsIActivityManager.idl and nsIActivity.idl files for more documentation

  • nsIActivityMgrListener: The implementer of this interface gets notified when an activity is added/removed to/from the activity list managed by Activity Manager. Activity developers do not need to implement this interface in order to introduce new activities to the system.
  • nsIActivityManager: It is implemented by the default Activity Manager component, used by the Activity Manager consumers. Activity developers do not need to implement this interface in order to introduce new activities to the system.
  • nsIActivityContextDisplayHelper: It is implemented and registered by the Activity developer to customize the context display text.
  • nsIActivity: Base interface for both nsIActivityProcess, nsIActivityWarning and nsIActivityEvent. Exposes common attributes and methods. Activity developers do not need to implement this interface in order to introduce new activities to the system.
  • nsIActivityProcess: Process activity types should implement this interface. For the majority of the cases, default process component (@mozilla.org/activity-process;1) should suffice. In cases where additional functionality needed by the process, Activity developers can implement their own version of Process activity and extend the system.
  • nsIActivityEvent: Event activity types should implement this interface. For the majority of the cases, default event component (@mozilla.org/activity-event;1) should suffice. In cases where additional functionality needed by the event, Activity developers can implement their own version of Event activity and extend the system.
  • nsIActivityWarning: Warning activity types should implement this interface. For the majority of the cases, default warning component (@mozilla.org/activity-warning;1) should suffice. In cases where additional functionality needed by the warning, Activity developers can implement their own version of Warning activity and extend the system.
  • nsIActivityListener: The implementer of this interface gets notified when the subscribed activity changes state, progress and more.
  • nsIActivityPauseHandler: If provided with the activity, it allows the user to pause/resume the activity during its progress. Process types only. Activity developers might implement a component realizing this interface and associate with the activity in question. When done, default binding (XBL) representing the activity in the Activity Manager window will automatically show a pause/resume button attached to the activity.
  • nsIActivityRetryHandler: If provided with the activity, it allows the user to retry the failed activity. Process types only. Activity developers might implement a component realizing this interface and associate with the activity in question. When done, default binding (XBL) representing the activity in the Activity Manager window will automatically show a retry button attached to the activity.
  • nsIActivityCancelHandler: If provided with the activity, it allows the user to cancel the activity. Process types only. Activity developers might implement a component realizing this interface and associate with the activity in question. When done, default binding (XBL) representing the activity in the Activity Manager window will automatically show a cancel button attached to the activity.
  • nsIActivityUndoHandler: If provided with the activity, it allows the user to undo the operation subject of the event. Persisted Events might not be undone. This feature hopefully will be supported in the future when we have a better undo mechanism in place. Obviously Event types only. Activity developers might implement a component realizing this interface and associate with the activity in question. When done, default binding (XBL) representing the activity in the Activity Manager window will automatically show a undo button attached to the activity.
  • nsIActivityRecoveryHandler: If provided with the activity, it allows the user to activste the operation to recover from the sitation causing the warning. Warning types only. Activity developers might implement a component realizing this interface and associate with the activity in question. When done, default binding (XBL) representing the activity in the Activity Manager window will automatically show a pause/resume button attached to the activity.

Default Components

  • @mozilla.org/activity-manager;1: Implements the nsIActivityManager. Provides the facilities to manage activities introduced by the backend, extensions, and the user.
  • @mozilla.org/activity-process;1: The default implementation of a Process activity. It knows how to deal with different process states. In majority of the cases it is enough to instantiate this component to add a new Process activity to the Activity Management system - see samples below.
  • @mozilla.org/activity-event;1: The default implementation of an Event activity. In majority of the cases it is enough to instantiate this component to add a new Event activity to the Activity Management system - see samples below.
  • @mozilla.org/activity-warning;1: The default implementation of a Warning activity. In majority of the cases it is enough to instantiate this component to add a new Warning activity to the Activity Management system - see samples below.

Error Handling

TBD

Extending the Activity System

  • If the default implementation of nsIActivityProcess, nsIActivityWarning and nsIActivityEvent are not sufficient for the activity initiator, Activity developers can provide their own components to extend the capabilities. Default implementations can be found in nsActivity.js file.
  • If Activity developers would like to extend the default UI representation of the activity types, they can provide their own XBL elements for their own activity types. All custom activity XBL elements should inherit from "activity-base" binding. For XBL sample please see activity.xml and activity.css files.

Scenarios

Showing an user-defined activity on the activity manager window

The following sample will show a process and an event for junk processing on the activity manager window.

// Step 1: Adding a Process into the activity manager
const nsIAP = Ci.nsIActivityProcess;
const nsIAE = Ci.nsIActivityEvent;
const nsIAM = Ci.nsIActivityManager;

let gActivityManager = Cc["@mozilla.org/activity-manager;1"].getService(nsIAM);
let process = Cc["@mozilla.org/activity-process;1"].createInstance(nsIAP);

// Assuming folder is an instance of nsIMsgFolder interface

// Localization is omitted, initiator is not provided
process.init("Processing folder: " + folder.prettiestName, 
             null);
// Note that we don't define a custom icon, default process icon
// will be used
process.contextType = "account";     // group this activity by account 
process.contextObj = folder.server;  // account in question

gActivityManager.addActivity(process);

// Step 2: Showing some progress

let percent = 50;
process.setProgress(percent, "Junk processing 25 of 50 messages", 25, 50);

// Step 3: Removing the process and adding an Event using Process' attributes
process.state = Components.interfaces.nsIActivityProcess.STATE_COMPLETED;
gActivityManager.removeActivity(process.id);

let event = Cc["@mozilla.org/activity-event;1"].createInstance(nsIAE);

// Localization is omitted, initiator is omitted
event.init(folder.prettiestName + " is processed",
           null, 
           "No junk found", 
           process.startTime,  // start time 
           Date.now());        // completion time

event.contextType = process.contextType; // optional
event.contextObj = process.contextObj;   // optional
             
gActivityManager.addActivity(event);

Showing an user-defined activity with cancel capability (JS)

This sample will improve the previous one by providing an nsIActivityCancelHandler to allow the user cancel the process.

// Step 1: Create a nsIActivityCancelHandler implementation
function CancelJunkProcess() 
{
 // user stuff here..
}
CancelJunkProcess.prototype = {
 cancel: function(aActivity) {
   let initiator = aActivity.initiator;
   if (initiator) {
     let folder = aActivity.getSubjects({})[0];
     ....
     // assuming that the initiator has some way to cancel 
     // the junk processing for given folder
     if (initiator.cancelFolderOp(folder)) {
       aActivity.state = Components.interfaces.nsIActivityProcess.STATE_CANCELED;
       gActivityManager.removeActivity(aActivity.id);
       return Cr.NS_SUCCESS;
     }
   }
  return Cr.NS_FAILURE;
 }
}

// Step 2: Modify the previous sample to add initiator, subject
// and associate a nsIActivityCancelHandler component
...
// assuming that gJunkProcessor is the entity initiating the junk processing 
// activity, and it has cancelFolderOp method that cancels the operations on folders
process.init("Processing folder: " + folder.prettiestName, 
             gJunkProcessor);

// folder is being filtered/processed 
process.addSubject(folder);
process.cancelHandler = new CancelJunkProcess();
...

Since nsIActivityCancelHandler is provided with the activity, UI will show a cancel button besides the activity. You can extrapolate this sample to nsIActivityRetryHandler and nsIActivityPauseHandler as well.

Showing an user-defined activity with undo capability (C++)

.....
#include "nsIActivity.h"
#include "nsIActivityManager.h"
.....
//////////////////////////////////////////////////////////////////////////////
//// Undo handler implementation

class myCopyEventUndo : public nsIActivityUndoHandler
{  
 public:
  NS_DECL_ISUPPORTS
  NS_DECL_NSIACTIVITYUNDOHANDLER
  myCopyEventUndo() {}
 private:
  ~myCopyEventUndo() {}
};
NS_IMPL_ISUPPORTS1(myCopyEventUndo, nsIActivityUndoHandler)
NS_IMETHODIMP myCopyEventUndo::Undo(nsIActivityEvent *event, nsresult *result)
{
  nsresult rv;
  // get the subjects of this copy event
  PRUint32 length;
  nsIVariant **subjectList;
  rv = event->GetSubjects(&length, &subjectList);
  if(NS_FAILED(rv)) 
    return rv;
  
  // first subject in the list is the source folder in this particular case
  nsCOMPtr<nsIMsgFolder> folder = do_QueryInterface(subjectList[0]);
  
  // get the initiator
  nsIVariant *initiator;
  event->GetInitiator(&initiator);
   
  if (initiator)
  {
    nsISupports* ptr;
    rv = object->GetAsISupports(&ptr);
    if(NS_FAILED(rv)) 
      return rv;
     
    nsCOMPtr<nsIMsgCopyService> copyService = do_QueryInterface(ptr);
    if (copyService)
      copyService->Undo(folder); 
  }
  
  return (*result = NS_OK);
 }
/////////////////////////////////////////////////////////////////////////////
//// Creating an undoable copy event

nsCOMPtr<nsIActivityUndoHandler> undoHandler = new myCopyEventUndo();
nsCOMPtr<nsIActivityEvent> copyEvent(do_CreateInstance("@mozilla.org/activity-event;1"));

// The initiator of this particular event is CopyService. We want to
// associate it with the event since we are going to use it in the
// undo handler.
// Same for the source folder, it is required for undo operation, but
// since it is the subject of this activity (event), it goes into the
// subject list.

// wrap copyservice in a nsvariant component
nsCOMPtr<nsIWritableVariant> initiator = do_CreateInstance(NS_VARIANT_CONTRACTID);
initiator->SetAsISupports(reinterpret_cast<nsISupports*>(copyService));
 
// subject of the delete operation is the imap folder
// wrap it in a nsvariant component
nsCOMPtr<nsIWritableVariant> srcFolder = do_CreateInstance(NS_VARIANT_CONTRACTID);
srcFolder->SetAsISupports(reinterpret_cast<nsISupports*>(imapFolder));
copyEvent->AddSubject(srcFolder);

copyEvent->Init(NS_LITERAL_STRING("Message copy event"),
                initiator,
                NS_LITERAL_STRING("Completed successfully"), 
                PR_Now() / PR_USEC_PER_MSEC,   // start time
                PR_Now() / PR_USEC_PER_MSEC);  // completion time

// Do not forget to increase the ref counter if needed
copyEvent->SetUndoHandler(undoHandler);  
////////////////////////////////////////////////////////////////
//// Adding the event into Activity Manager

nsCOMPtr<nsIActivityManager> activityMgr(do_GetService("@mozilla.org/activity-manager;1"));
PRUint32 id;
activityMgr->AddActivity(copyEvent, &id);

Adding an activity with custom context type

This sample shows how to provide a custom context type for the junk processing. As a result, all junk processing activities (assuming that we process accounts in parallel) processing the messages coming from the same sender will be grouped together.

//optional: define some convenience constants
const nsActProcess = Components.Constructor("@mozilla.org/activity-process;1",
                                           "nsIActivityProcess", "init");
const nsActEvent = Components.Constructor("@mozilla.org/activity-event;1",
                                         "nsIActivityEvent", "init");

 // Step 1: Implement nsIActivityContextDisplayHelper to show a 
 //         customized   display text for our context type
function SenderContextDisplayHelper() 
{
 // user stuff here..
}
SenderContextDisplayHelper.prototype = {
 getContextDisplayText: function(contextType, contextObj) {
   // in this particular example we know that contextType is "Sender"
   // since we also pass the contextType along with the contextObject 
   //  in some cases, one helper can be registered for a group of context types

   // we know that the context object is the author of the message
   // Localization is omitted
   return "Messages coming from " + contextObj.surname + ", " + contextObj.firstname;
 }
}

// Step 2: Register the helper for this context type
gActivityManager.registerContextDisplayHelper("Sender", 
                                             new SenderContextDisplayHelper());


// Step 3: Create the process
...
let process = new nsActProcess("Processing folder: " + folder.prettiestName,
                               gJunkProcessor);

process.cancelHandler = new CancelJunkProcess();

// folder is being filtered/processed 
process.addSubject(folder);
process.contextType = "Sender";
// assuming msg is an instance of nsIMsgHdr
process.contextObj = msg.author;
...

Adding a fully customized activity

In complex scenarios, it might be inevitable for extensions to implement their own version nsIActivityProcess, nsIActivityWarning and nsIActivityEvent interfaces. In such case, nsActivity.js can be used as a model.

Changing the activity bindings

TBD

Relevant Links

Relevant Bugs

bug 257942

Interfaces

  • nsIActivityMgrListener: The implementor of this interface gets notified when an activity is added/removed to/from the activities list managed by Activity Manager.
  • nsIActivityManager: Keeps the list of the recent Processes and Events. It is also responsible of serialization/deserialization of the Events. Activity Manager doesn't add/remove activities but might change the state of the activities.
  • nsIActivity: Base interface for both nsIActivityProcess and nsIActivityEvent. Exposes common properties and methods.
  • nsIActivityProcess: Processes activity types should implement this interface. Possible states are:
    • ACTIVITY_NOTSTARTED: State of an uninitialized activity. Represents an invalid activity.
    • ACTIVITY_INPROGESS: Initial state for a successfully initialized process activity. The progress of the activity can only be set in this state.
    • ACTIVITY_COMPLETED: Activity is successfully completed (Finished). The activity should be removed from the activity list by the initiator.
    • ACTIVITY_FAILED: Activity failed due to an error. At this point, the initiator can set the state to ACTIVITY_WAITINGFORRETRY, if nsIActivityRetryable is supported.
    • ACTIVITY_CANCELED: Activity is canceled by the user. This is possible if the activity supports nsIActivityCancelable interface.
    • ACTIVITY_PAUSED: Activity is paused by the user. This is possible if the activity supports nsIActivityPausable interface.
    • ACTIVITY_WAITINGFORINPUT: A failure has been occurred, and the user needs to provides data to recover from the problem, such as account credentials, password etc..
    • ACTIVITY_WAITINGFORRETRY: A failure has been occurred and the activity is waiting to be retried by the user, or by the Activity Manager.
  • nsIActivityEvent:Event activity types should implement this interface. ACTIVITY_COMPLETED is the only valid state for this type.
  • nsIActivityWarning:TBD
  • nsIActivityListener: The implementor of this interface gets notified when the activity in question changes state or changes progress.
  • nsIActivityPausable: If provided with the activity, it allows the user to pause/resume the activity during its progress. Processes only.
  • nsIActivityRetryable: If provided with the activity, it allows the user to retry the failed activity. Processes only.
  • nsIActivityCancelable: If provided with the activity, it allows the user to cancel the activity. Processes only.
  • nsIActivityUndoable: If provided with the activity, it allows the user to undo the operation subject of the event. Persisted Events cannot be undo. This feature will be supported in the future when we have a better undo mechanism in place.

Error Handling

TBD

Extending the Activity System

  • If the default implementation of nsIActivityProcess and nsIActivityEvent are not sufficient for the activity initiator, it can provides its own implementation to extend the capabilities. Default implementations can be found in nsActivity.js file.
  • If the activity initiator would like to extend the default UI representation of the activity types, it can provides its own XBL elements for its own activity types. All custom activity XBL elements should inherit from "activity-base" binding. For XBL sample please see activity.xml and activity.css files.

Scenarios

A sample extension and C++ samples will follow

Showing an user-defined activity on the activity manager window

The following sample will show a process and an event for junk processing on the activity manager window.

// Step 1: Adding a Process into the activity manager
const nsIAP = Ci.nsIActivityProcess;
const nsIAE = Ci.nsIActivityEvent;
const nsIAM = Ci.nsIActivityManager;

let gActivityManager = Cc["@mozilla.org/activity-manager;1"].getService(nsIAM);
let process = Cc["@mozilla.org/activity-process;1"].createInstance(nsIAP);

// Assuming folder is an instance of nsIMsgFolder interface

// Localization is omitted, initiator is not provided
process.init("Processing folder: " + folder.prettiestName, null);
process.type = "junk_filtering";     // type of the activity
process.contextType = "account";     // group this activity by account 
process.contextVal = folder.server;  // account in question

gActivityManager.addActivity(process);

// Step 2: Showing some progress

let percent = 50;
process.setProgress(percent, "Junk processing 25 of 50 messages", 25, 50);

// Step 3: Removing the process and adding an Event using Process' properties
process.state = ci.nsIActivity.ACTIVITY_COMPLETED;
gActivityManager.removeActivity(process.id);

let event = Cc["@mozilla.org/activity-event;1"].createInstance(nsIAE);

// Localization is omitted
event.init(folder.prettiestName + " is processed", "No junk found", 
           process.startTime, Date.now());
event.type = process.type;               // optional
event.contextType = process.contextType; // optional
event.contextVal = process.contextVal;   // optional
             
gActivityManager.addActivity(event);

Optionally, if the initiator prefers to show each activity of this activity type standalone, it can do so by registering junk_filtering as VIEW_TYPE_STANDALONE:

gActivityManager.registerActivityViewType("junk_filtering", VIEW_TYPE_STANDALONE);

Showing an user-defined activity with cancel capability (JS)

This sample will improve the previous one by providing an nsIActivityCancelable to allow the user cancel the process.

// Step 1: Create a nsIActivityCancelable implementation
function CancelJunkProcess() 
{
 // user stuff here..
}
CancelJunkProcess.prototype = {
 cancel: function(aActivity) {
   let initiator = aActivity.initiator;
   let folder = aActivity.getSubjects({})[0];
   ....
   // assuming that the initiator has a method to cancel the
   // junk processing for given folder
   if (initiator.Cancel(folder)) {
    aActivity.state = Ci.nsIActivity.ACTIVITY_CANCELED;
    gActivityManager.removeActivity(aActivity.id);
    return Cr.NS_SUCCESS;
   }
  return Cr.NS_FAILURE;
 }
}

// Step 2: Modify the previous sample to add initiator, subject
// and nsIActivityCancelable interface
...
// assuming that gJunkProcessor is the entity initiating the junk processing 
// activity
process.initWithHandlers("Processing folder: " + folder.prettiestName, 
                          gJunkProcessor, 
                          new CancelJunkProcess(), 
                          null, null);
// folder is being filtered/processed 
process.addSubject(folder);
...

Since nsIActivityCancelable is provided with the activity, UI will show a cancel button besides the activity. You can extrapolate this sample to nsIActivityRetryable and nsIActivityPausable as well.

Showing an user-defined activity with undo capability (C++)

.....
#include "nsIActivity.h"
#include "nsIActivityManager.h"
.....
//////////////////////////////////////////////////////////////////////////////
//// Undo handler implementation

class myCopyEventUndo : public nsIActivityUndoHandler
{  
 public:
  NS_DECL_ISUPPORTS
  NS_DECL_NSIACTIVITYUNDOHANDLER
  myCopyEventUndo() {}
 private:
  ~myCopyEventUndo() {}
};
NS_IMPL_ISUPPORTS1(myCopyEventUndo, nsIActivityUndoHandler)
NS_IMETHODIMP myCopyEventUndo::Undo(nsIActivityEvent *event, nsresult *result)
{
  nsresult rv;
  // get the subjects of this copy event
  PRUint32 length;
  nsIVariant **subjectList;
  rv = event->GetSubjects(&length, &subjectList);
  if(NS_FAILED(rv)) 
    return rv;
  
  // first subject in the list is the source folder in this particular case
  nsCOMPtr<nsIMsgFolder> folder = do_QueryInterface(subjectList[0]);
  
  // get the initiator
  nsIVariant *initiator;
  event->GetInitiator(&initiator);
   
  if (initiator)
  {
    nsISupports* ptr;
    rv = object->GetAsISupports(&ptr);
    if(NS_FAILED(rv)) 
      return rv;
     
    nsCOMPtr<nsIMsgCopyService> copyService = do_QueryInterface(ptr);
    if (copyService)
      copyService->Undo(folder); 
  }
  
  *result = NS_OK;
  return NS_OK;
 }
/////////////////////////////////////////////////////////////////////////////
//// Creating an undoable copy event

nsCOMPtr<nsIActivityUndoHandler> undoHandler = new myCopyEventUndo();
nsCOMPtr<nsIActivityEvent> copyEvent(do_CreateInstance("@mozilla.org/activity-event;1"));

// The initiator of this particular event is CopyService. We want to
// associate it with the event since we are going to use it in the
// undo handler.
// Same for the source folder, it is required for undo operation, but
// since it is the subject of this activity (event), it goes into the
// subject list.

// wrap copyservice in a nsvariant component
nsCOMPtr<nsIWritableVariant> initiator = do_CreateInstance(NS_VARIANT_CONTRACTID);
initiator->SetAsISupports(reinterpret_cast<nsISupports*>(copyService));
 
// subject of the delete operation is the imap folder
// wrap it in a nsvariant component
nsCOMPtr<nsIWritableVariant> srcFolder = do_CreateInstance(NS_VARIANT_CONTRACTID);
srcFolder->SetAsISupports(reinterpret_cast<nsISupports*>(imapFolder));
copyEvent->AddSubject(srcFolder);

copyEvent->InitWithHandlers(
                         NS_LITERAL_STRING("Message copy event"), 
                         NS_LITERAL_STRING("Completed successfully"), 
                         PR_Now() / PR_USEC_PER_MSEC,   // start time
                         PR_Now() / PR_USEC_PER_MSEC,   // completion time
                         initiator,
                         undoHandler);
 	
////////////////////////////////////////////////////////////////
//// Adding the event into Activity Manager

nsCOMPtr<nsIActivityManager> activityMgr(do_GetService("@mozilla.org/activity-manager;1"));
PRUint32 id;
activityMgr->AddActivity(copyEvent, &id);

Adding an activity with custom context type

This sample shows how to provide a custom context type for the junk processing. As a result, all junk processing activities (assuming that we process accounts in parallel) processing the messages coming from the same sender will be grouped together.

 // Step 1: Implement nsIActivityContextDisplayHelper to show a 
 //         customized   display text for our context type
function SenderContextDisplayHelper() 
{
 // user stuff here..
}
SenderContextDisplayHelper.prototype = {
 getContextDisplayText: function(aActivity) {
   let context = aActivity.contextVal;
   ...
   // we know that the context object is the author of the message
   // Localization is omitted
   return "Messages coming from " + context;
 }
}

// Step 2: Register the helper for this context type
gActivityManager.registerContextDisplayHelper("Sender", 
                                             new SenderContextDisplayHelper());


// Step 3: Create the process
...
// assuming msg is an instance of nsIMsgHdr
process.initWithHandlers("Processing folder: " + folder.prettiestName, 
                          gJunkProcessor, 
                          new CancelJunkProcess(), 
                          null, null);
// folder is being filtered/processed 
process.addSubject(folder);
process.contextType = "Sender";
process.contextVal = msg.author;
...

Adding a fully customized activity

In complex scenarios, it might be inevitable for extensions to implement their own version nsIActivityProcess and nsIActivityEvent interfaces. In such case, nsActivity.js can be used as a model.

Changing the activity bindings

TBD

Relevant Links

Relevant Bugs

bug 257942