Sorry about the red box, but we really need you to update your browser. Read this excellent article if you're wondering why we are no longer supporting this browser version. Go to Browse Happy for browser suggestions and how to update.

Unified search

The Unified Search Service is a search engine that is included with BlackBerry Device Software 6.0 and later. BlackBerry device users interact with the Unified Search Service primarily from the Universal search feature by clicking the search icon on the Home screen. Developers can use Unified Search Service through the Unified Search API. You can use the API to include your application data in the Service's content repository, and search the content index from your application.

For example, imagine that you have a large collection of books and you loan them to people frequently. It might be useful to create an application that allows you to catalog your books, and keep a list of people to whom you loan them. If this application used the Unified Search API, you could search for a book from the Home screen, or any application, to find the book and contact details for the person who holds it.

As a developer, you control whether your data appears in searches that users perform from the Home screen of a BlackBerry device, other applications on the device, or your application only. In addition, you can search data from other applications when that data is registered in the Unified Search Service.

Making data findable

There are two ways to include your data in search results from the Unified Search Service. If you want to expose the data in your application, you need to implement the EntityBasedSearchable interface. In this scenario, you must wrap your data in a class that implements the SearchableEntity interface. If you want to pass a search query to your own search engine, located on a network server or within your application, you need to implement the ExternalSearchProvider interface. Search engines that are available on a server might be located behind an organization's firewall or be a public web service.

Exposing your application data

The Unified Search Service maintains an index of content on a BlackBerry device. Your application uses the SearchRegistry, AppContentManager, and AppContentListener classes to communicate with the Unified Search Service. To make the data in your application findable, you need to complete five tasks.

Task

Description

Define an EntityBasedSearchable class for your SearchableEntity objects.

An EntityBasedSearchable object represents your searchable data to the Unified Search Service. When you register your EntityBasedSearchable, the Service requests that your application prepare data for submission by calling EntityBasedSearchable.load(). When your application data is ready for submission, you should invoke completed() on the NotificationListener parameter passed to load().

Encapsulate your data in a SearchableEntity object.

A SearchableEntity object is the smallest unit of data that an application can submit to the Unified Search Service. Your EntityBasedSearchable object sends your SearchableEntity objects to the Service when asked. You need to encapsulate your application data in a SearchableEntity object. The Service indexes the metadata that is exposed by these objects for consideration during search operations. Also, SearchableEntity objects are returned in search results from the Service.

The Unified Search Service may need to interrupt your load operation if the BlackBerry device is busy, or has a low battery power level. If an interruption is necessary, the Service invokes EntityBasedSearchable.pause(). When the Service detects that your operation can continue, it invokes EntityBasedSearchable.resume(). Respond to these events in a timely manner so that your application does not cause disruption to other applications on the device.

After you notify the Unified Search Service that your data is ready, it calls EntityBasedSearchable.getSearchableEntities() to index your data. The Service may invoke load() and getSearchableEntities() again if it detects that its content index and your data are not synchronized.

Register your EntityBasedSearchable object with the Unified Search Service.

After you prepare your data for search, and define how it will be indexed, you must register your EntityBasedSearchable with the Unified Search Service. Pass your EntityBasedSearchable when you invoke SearchRegistry.register(). The Service then retrieves and indexes your data for inclusion in search results.

Notify the Unified Search Service about changes to your data.

To notify the content index about changes to your application data, use the insert(), delete(), and update() methods in the AppContentManager object.

Listening for responses from the Unified Search Service.

When you notify the Unified Search Service about changes to your application data, you should provide an AppContentListener object where the Service can notify your application about the result of your request.

The following diagram illustrates the relationship between the components that are used in the previous tasks.

This diagram shows the relationship between the components used to add your content to the Unified Search Service.

Passing queries to other search engines

You can use the ExternalSearchProvider interface to pass queries to another search engine. For example, an application on a BlackBerry device that has an efficient way to search its own data could give users and other applications access to its data by implementing the ExternalSearchProvider. Alternatively, an insurance company may have an application that allows sales representatives to search an insurance policy database. The company could provide its sales representatives with access to the policy search engine behind the firewall, from a BlackBerry device, by implementing the ExternalSearchProvider.

Users can extend a search in two ways. The Universal search feature from the Home screen lists external search providers at the end of the search result list. If a user clicks the icon for your application in the search result, the Unified Search Service invokes search() from your ExternalSearchProvider object. Your application is then responsible for creating a connection to the search provider (another application, or over a network), passing the query string, and displaying any results that you retrieve.

Other applications can invoke your application in a similar way. The UnifiedSearchServices.getSearchProviders() method returns a list of all external search providers that are installed on a BlackBerry device. In this way, an application can find and use your ExternalSearchProvider specifically, or allow the user to choose one from the list.

To ensure that your application appears in the list of external search providers, you must register your ExternalSearchProvider with the Unified Search Service. Pass your ExternalSearchProvider object when you invoke SearchRegistry.register().

The following diagram shows the relationship between some of the components that are required to implement ExternalSearchProvider.


This diagram shows the components involved in creating an external search provider.

Define an EntityBasedSearchable class for your SearchableEntity objects

Your implementation of the EntityBasedSearchable interface manages the relationship between the Unified Search Service and the searchable entities that you create. Each EntityBasedSearchable object manages a group of SearchableEntity objects that have the same searchable properties.

Import the required classes and interfaces.

import net.rim.device.api.unifiedsearch.SearchField;
import net.rim.device.api.unifiedsearch.searchables.EntityBasedSearchable;
import net.rim.device.api.unifiedsearch.searchables.SearchableContentTypeConstants;
import net.rim.device.api.unifiedsearch.searchables.Searchable;

Create instance variables to store information that is relevant to your EntityBasedSearchable.

private MySearchableEntity[] _myEntities;
private long _registrationID;
private SearchField[] _searchFields;
private Image _icon;
private final Object _monitor = new Object();
private boolean _wait = false;

In the constructor, create an array of SearchField objects. Each SearchField stores the name of one searchable property of your data. In this code sample, the application data objects that are managed by this class have three searchable properties. For example, if your application data modeled books, these fields might include the title, the publisher, and number of pages.

class MyEBS implements EntityBasedSearchable
{
    _searchFields = new SearchField[3];

Assign a meaningful name to each SearchField.

    _searchFields[0] = new SearchField("Title");
    _searchFields[1] = new SearchField("Publisher");
    _searchFields[2] = new SearchField("Number of pages");
}

Create an icon to appear in search results that include searchable entities managed by this EntityBasedSearchable. For more information about creating icons, see "Customizing the appearance of your data in search results."

Implement getType(). Return the content type that best matches your application data. For a list of valid content types, see the net.rim.device.api.unifiedsearch.searchables.SearchableContentTypeConstants interface in the API reference.

public long getType() {
    return SearchableContentTypeConstants.CONTENT_TYPE_MEMO;

Choose the search initiators that should receive search results from your application. For more information about exposing your data to searches, see net.rim.device.api.unifiedsearch.entity.ExposureLevel class in the API reference.

public int getPrivacyLevel() {
    return ExposureLevel.LEVEL_PUBLIC;
}
Implement load(). If the BlackBerry device is busy, or the battery power level is low, you may need to pause the operation of this method, and resume it when requested.
  • For your EntityBasedSearchable, determine how many data objects currently exist in your application.
    public void load (NotificationListener observer, int loadType) {
        Vector myObjects = MyObjectManager.getDataObjects();
        if (myObjects != null) {
            int size = myObjects.size()
            if(size < 1) {
                _myEntities = new MySearchableEntity[0];
            } else {
                _myEntities = new MySearchableEntity[size];
            }
        } else {
            // Handle the condition where you have no data objects.
        }
  • Use a while loop to populate an array of searchable entities.
        Enumeration objects = myObjects.elements();
        int count = 0;
    
        while (objects.hasMoreElements()) {
  • Check whether the Unified Search Service has sent a pause command. If so, pause the operation that populates your arrray of searchable entities and notify the Service.
            if (_wait){ 
                try {
                    synchronized(_monitor) { 
                    observer.partiallyCompleted(this, null, 
                              NotificationListener.TYPE_SEARCHABLE);
                    _monitor.wait();
                    }
                } catch (InterruptedException e){
                    observer.error(e);
                    return;
                }
            }
  • When your application has control of the thread again, continue to populate your array of searchable entities.
            MyObject dataObject = (MyObject) objects.nextElement();
            _myEntities[count++] = new MySearchableEntity(dataObject, this);
        }
  • Notify the Unified Search Service that your are done loading your data.
        observer.completed(this, NotificationListener.TYPE_SEARCHABLE);
    }

    The Unified Search Service invokes getSearchableEntities() next to retrieve the array you populated.

In pause(), change the value of the _wait variable to true. The Unified Search Service calls this method when it wants your application to stop loading data.

public void pause() {
    _wait = true;
}

In resume(), change the value of the _wait variable to false. Also, notify the load() method that it can resume execution. The Unified Search Service invokes resume() when it wants your application to resume loading data.

public void resume() {
    _wait = false;
    synchronized(_monitor) {
        _monitor.notifyAll();
    }
}

In getSearchableEntities(), return the _myEntities array that you created.

public SearchableEntity[] getSearchableEntities() {
    return _myEntities;
}

Customizing the appearance of your data in search results

The getIcon() method, in the EntityBasedSearchable and SearchableEntity interfaces, specifies the icon that appears in search results. In the Universal search feature on the Home screen, search results are grouped by application. The EntityBasedSearchable.getIcon() method specifies the icon that appears for the group.

You can also specify an icon for different categories of related searchabe entities. For example, if you create an application that catalogs books you might want to have different icons for different categories of books, such as best sellers, oversized, and first editions. These categories are all types of books, so they would logically fall under one EntityBasedSearchable object that managed books. However, an application that displays them might want to give a visual cue to help a user quickly determine whether to find a book on a regular shelf, a large shelf, or in an environmentally controlled room. In this case, you could create three SearchableEntity classes to wrap three different kinds of book objects, each with its own implementation of getIcon(). All three classes would still be managed by a single EntityBasedSearchable.

Specify an icon in the EntityBasedSearchable or SearchableEntity class

Include a suitable graphic file for use as an icon in your project's resource folder. For more information about designing graphics for BlackBerry devices, see UI Guidelines.

Import the required classes.

import net.rim.device.api.ui.image.Image;
import net.rim.device.api.ui.image.ImageFactory;
import net.rim.device.api.system.Bitmap;

Create an instance variable to store the icon.

private Image _icon;

Import the graphic into your application from your resource folder.

Bitmap img = Bitmap.getBitmapResource("icon.png")

Check whether the import was successful, then create an icon.

if(img != null) {
    _icon = ImageFactory.createImage(img);
} else {
    _icon = null;
}

In getIcon(), return the image.

public Image getIcon() {
    return _icon;
}

Encapsulate your data in the SearchableEntity class

The Unified Search Service indexes the metadata that is exposed by a SearchableEntity object. The Service also returns a SearchableEntity as the content of a search result. You must prepare your application data for the Service by encapsulating it in your SearchableEntity class.

Import the required classes and interfaces.

import net.rim.device.api.unifiedsearch.SearchField;
import net.rim.device.api.unifiedsearch.SearchFieldCriteria;
import net.rim.device.api.unifiedsearch.SearchFieldCriteriaList;
import net.rim.device.api.unifiedsearch.entity.SearchableEntity;
import net.rim.device.api.unifiedsearch.searchables.Searchable;

Retrieve an array of SearchField objects from your EntityBasedSearchable.

SearchField[] searchfields = mySearchable.defineSupportedSearchFields();

Create a SearchFieldCriteria object for each of your searchable properties. In the following code sample, the application data object myObject has a getProperty() method that returns a string for a given index. For example, if myObject described a book, then indicies 0, 1, and 2 of getProperty() could return the name, the publisher, and the number of pages in the book.

int size = searchfields.length
SearchFieldCriteria[] criteria = new SearchFieldCriteria[size];
for (int i = size -1; i >= 0; --i) {
    criteria[i] = new SearchFieldCriteria(searchfields[i], new String[]{myObject.getProperty(i)});
}

Create and populate a SearchFieldCriteriaList object to hold your search field criteria.

SearchFieldCriteriaList _sfcl = new SearchFieldCriteriaList();
for (int i = size -1; i >= 0; --i) {
    sfcl.addCriteria(criteria[i]);
}

Implement getSearchCriteria() to return your SearchFieldCriteriaList.

pubic SearchFieldCriteriaList getSearchCriteria() {
    return _sfcl;
}

In getData(), return your application data object. To continue with the book example, this method would return an object that represents the book.

public Object getData() {
    return myObject;
}

In getTitle(), assign a title to this searchable entity. The title text appears in search results. To continue with the book example, myObject.getName() could return the name of a book.

public String getTitle() {
    return myObject.getName();
}

In getSummary(), provide a summary of the data in this searchable entity. The summary text appears in search results. Continuing with the book example, myObject.getDescription() could return a description of the book.

public String getSummary() {
    return myObject.getDescription();
}

Implement getSearchable(). Retrieve the EntityBasedSearchable that your application creates to manage this object, and return it.

public Searchable getSearchable() {
    return myPublisher.getSearchable();
}

Define what options should appear on the context menu when a BlackBerry device user clicks on your entity in a list of search results. For more information, see "Specify what users can do with your data in search results".

If you want this SearchableEntity to display a different icon than your application icon, provide the icon in getIcon(). For more information, see "Customize the appearance of your data in search results".

You can specify what operations users can perform on using your SearchableEntity by implementing the getUiActions(Object, UiAction[]) method.

Specifying what users can do with your data in search results

When a user selects your search result and presses the menu key, the BlackBerry device displays a list of operations the user can perform with that data. For example, if your application holds recipes, you might want to enable your user to email a recipe or add the ingredients to a shopping list from a list of search results. Each item in a search result represents a SearchableEntity object from an application that registered data with the Unified Search Service. The UiAction class helps you specify an operation that a user or an application can invoke on your SearchableEntity objects.

In your application, you must create a UiAction subclass for each operation you want to make available to your user. You can expose your UiAction objects by using the getUiActions(Object, UiAction[]) method of your SearchableEntity object. You must configure an alternate entry point for the Unified Search Service to execute the operation a user selects.

Specify an operation for a selected search result

A UiAction subclass defines an operation that is available to users when they select an item in a list of search results provided by the Unified Search Service. The following steps demonstrate how to create a class named DisplayBookInfo that enables a user to display detailed information about a book in an application that stores information about books.

Import the required classes and interfaces.

import net.rim.device.api.ui.image.Image;
import net.rim.device.api.ui.UiApplication;
import net.rim.device.api.unifiedsearch.action.UiAction;

Declare a class that extends UiAction, and create an empty constructor.

class DisplayBookInfo extends UiAction
{

    DisplayBookInfo ()
    {

    }

In the runAction() method, retrieve the SearchableEntity object that the user selected from a search result.

    protected void runAction() 
    {
        BookEntity book = (BookEntity) this.getSearchableEntity();

In runAction(), create an instance of the DisplayBookScreen class to display the book information. Provide the SearchableEntity object that you retrieved to the new screen object.

        DisplayBookScreen showBook = new DisplayBookScreen(book);

Invoke pushScreen() to make the new screen object visible.

       	UiApplication.getUiApplication().pushScreen(showBook);
    }

Implement the toString() method to provide a description of the operation this class performs.

    public String toString() {
        return "Show book info";
    }

Implement the getIcon() method to associate an icon with this operation. In this task, no icon is provided.

    public Image getIcon() {
        return null;
    }
}

In the getUiActions() method of your SearchableEntity class, populate the uiActions parameter with your actions like DisplayBookInfo. Change the uiActions array reference using the Arrays object. If you try to change the array without the Arrays object, you will create a local copy of the array. Your local copy is not passed back to the application that requested your UiAction objects.

In the following code sample, the ActionTwo and ActionThree classes were added to provide a more complete example.

public UiAction getUiActions(Object contextObject, UiAction[] uiActions) {
    ActionOne act1 = new ActionOne(this);
    Arrays.add(uiActions, act1);
    Arrays.add(uiActions, new ActionTwo());
    Arrays.add(uiActions, new ActionThree());

Return the UiAction object you want to highlight the menu of actions by default.

    return act1;
}

Provide an alternate entry point for your application

You can configure an alternate entry point for your application. For more information about declaring an alternate entry point in your project, see Specify the alternate entry point settings for a BlackBerry application project.

Your application must detect when the BlackBerry device activates it using an alternate entry point. The following steps demonstrate how to enable an application to accept an alternate entry point for a UiAction object.

Import the required classes and interfaces.

import net.rim.device.api.ui.UiApplication;

Create the application framework by extending the UiApplication class.

public class MyApp extends UiApplication
{
    public static void main(args[])
    {
    }

    public MyApp(String entryPoint)
    {
    }
}

In the main() method, indentify whether a user launched the application from the Home screen of a BlackBerry device, or through a search result. This step assumes that you configured an alternate entry point for your project named uiAction.

if( args != null && args.length > 0 && args[0].equals("uiAction"))
{

Create an application object. Provide the entry point type as a parameter.

    app = new MyApp("uiAction");
} else
{
    app = new MyApp("user");
}

Start your application.

app.enterEventDispatcher;

In your application constructor, indentify the entry point used to launch the application.

if(entryPoint.equals("normal")
{

If your appilcation was launched from the Home screen, display your first screen.

{
    pushScreen(new MyAppScreen());

If a user launched your application from a search result, do nothing. The application that provided the search result prompts your application to execute the UiAction automatically.

} else
{
}

Expose your UiAction objects

Create a class that implements the SearchableEntity interface.

You must implement the SearchableEntity.getUiActions(Object, UiActions[]) method to expose your UiAction objects. The following steps demonstrate how to expose an object based on a class named DisplayBookInfo.

In your SearchableEntity implementation, import the Arrays class.

import net.rim.device.api.util.Arrays;

In your SearchableEntity.getUiAction(Object, UiAction[]) method, instantiate a DisplayBookInfo object.

displayAction = new DisplayBookInfo();

Invoke the setEntryPointIndex() method and specify a single parameter with a value of 1. This configures the UiAction object to use an alternate entry point for your application.

displayAction.setEntryPointIndex(1);

In your SearchableEntity.getUiAction(Object, UiAction[]) method, invoke the Arrays.add() method to add displayAction to the UiAction[] parameter.

Arrays.add(uiActions, displayAction);

Return displayAction to set the default operation for SearchableEntity.

return displayAction;

You must provide an alternate entry point for your application.

Register your EntityBasedSearchable object with the Unified Search Service

After you have defined your SearchableEntity class, and created an EntityBasedSearchable class to represent your searchable entities to the Unified Search Service, use the SearchRegistry to register them with the Unified Search Service.

Import the required classes and interfaces.

import net.rim.device.api.unifiedsearch.registry.RegistrationToken;
import net.rim.device.api.unifiedsearch.registry.SearchRegistry;

Create your EntityBasedSearchable.

public class MyClass {

    private MySearchable _searchable;
    private RegistryToken _regToken;
        
    public MyClass() {
        _searchable = new MySearchable()

Register your EntityBasedSearchable with the Unified Search Service.

        _regToken = SearchRegistry.getInstance().register(_searchable);

It is a good practice to test whether the registration was successful.

        if (! _regToken.isValid()) {
            // Action to take if the registration is not valid.
        }
    }
}

The registration token is unique to your EntityBasedSearchable until the BlackBerry device restarts. You should store your registration token and reuse it when you update content using the AppContentManager class.

Notify the Unified Search Service about changes to your data

Retrieve a registration token to communicate with the Unified Search Service. An application retrieves a registration token when it registers an EntityBasedSearchable object.

Define a variable (such as _myListener in the following code sample) for an AppContentListener implementation.

When your application changes, deletes, or creates new searchable data, you can notify the Unified Search Service about the changes by using the AppContentManager object.

Import the required classes and interfaces.

import net.rim.device.api.unifiedsearch.content.AppContentManager;

To update a SearchableEntity object that the Unified Search Service has already indexed, invoke updateContent().

public void updateUSS(MyEntity entity, RegistrationToken regToken) {
    MyEntity[] toUpdate = new MyEntity[1];
    toUpdate[0] = entity;
    AppContentManager.getInstance().updateContent(toUpdate, _myListener, regToken);
}

To delete a SearchableEntity object, invoke deleteContent().

public void deleteUSS(MyEntity entity, RegistrationToken regToken) {
    MyEntity[] toDelete = new MyEntity[1];
    toDelete[0] = entity;
    AppContentManager.getInstance().deleteContent(toDelete, _myListener, regToken);
}

To insert a new SearchableEntity, invoke the insertContent() method.

public void insertUSS(MyEntity entity, RegistrationToken regToken) {
    MyEntity[] toInsert = new MyEntity[1];
    toInsert[0] = entity;
    AppContentManager.getInstance().insertContent(toInsert, _myListener, regToken);
}

Listening for responses from the Unified Search Service

When you manipulate content with AppContentManager methods, you should specify class that implements the AppContentListener interface. After the Unified Search Service has processed your request, it notifies the AppContentListener method that corresponds to the operation that you requested.

Each AppContentManager method has a parameter that tells you how many objects were affected by the operation. If the operation fails, the value of the parameter is 0.

public class MyListener implements AppContentListener {

    public void onDeleteComplete(int delCount) {
        // Action to take after records deleted.
    }

    public void onInsertComplete(int insertCount) {
        // Action to take after new records inserted.
    }

    public void onUpdateComplete(int updCount) {
        // Action to take after records updated.
    }
}

Removing your data from the content repository

Depending on your application life cycle, you might need to remove all of your application data from the Unified Search Service content repository. For example, a BlackBerry device user may have multiple accounts for your application. It may make sense for the application data to appear in search results only if the user is currently authenticated by your application. Alternatively, your application may need to respond to locale changes. In this case, you should remove data from the content repository, then repopulate it with the data that is appropriate to the current locale.

The following table describes two approaches to remove all of your application data from the Unified Search Service content repository.

Approach

Description

Deregister an EntityBasedSearchable object.

This approach removes your EntityBasedSearchable object, and all data associated with it, from the Unified Search Service content index. Your EntityBasedSearchable no longer appears in the list of registered searchable data sources on the BlackBerry device.

To deregister an EntityBasedSearchable, invoke SearchRegistry.deregister(), and pass the registration token returned when that EntityBasedSearchable was registered.

Remove all data from the content repository for an EntityBasedSearchable object.

This approach removes all of your application data from the repository. You can use this approach when you need to remove all data, but you expect that you will continue to populate the repository with new data in the near future.

To remove all data from the content repository for an EntityBasedSearchable, invoke UnifiedSearchServices.removeAllData() and pass the registration token that was returned when that EntityBasedSearchable was registered.

Using other search engines

The ExternalSearchProvider interface lets BlackBerry users search data outside of the Unified Search Service. If a user receives an unsatisfactory search result, they can click on other external search providers when using the Universal search feature from the Home screen. These search providers implement the ExternalSearchProvider interface.

When a user clicks a search provider, the Universal search feature invokes search() for that ExternalSearchProvider. The search provider is responsible for creating a network connection or interprocess communication connection, invoking a search on the remote server, retrieving the results, and displaying them to the user. If your application uses a remote search engine, you can consider inserting the search results in the Unified Search Service content repository to improve speed and efficiency for subsequent searches.

Some predefined external search providers include YouTube and Google. Third-party applications can also access external search providers. The UnifiedSearchServices.getSearchProviders() method returns a list of registered ExternalSearchProvider objects where an application can send a search.

Similar to EntityBasedSearchable objects, an ExternalSearchProvider must be registered with the Unified Search Service by using the SearchRegistry object. For more information, see "Register your EntityBasedSearchable object with the Unified Search Service".


import net.rim.device.api.ui.image.Image;
import net.rim.device.api.ui.image.ImageFactory;

import net.rim.device.api.unifiedsearch.searchables.ExternalSearchProvider;
import net.rim.device.api.unifiedsearch.searchables.SearchableContentTypeConstants;


public class MySearchProvider implements ExternalSearchProvider {
    // A unique registration ID for this provider.
    private long _regId;
    
    // The external search provider icon.
    private Image _icon;
    

    // Constructor
    public MySearchProvider() {
        // Read the icon from the resource bundle
        Bitmap img = Bitmap.getBitmapResource("myicon.png");
        
        if(img != null) {
            _icon = ImageFactory.createImage(img);
        } else {
            _icon = null;
        }
    }
    
    // The provider name to be displayed.
    public String getProviderName() {
        return "Sample External Search Provider";
    }
    
    // The provider icon to be displayed.
    public Image getProviderIcon() {
        return _icon;
    }
    
    // The content type this provider offers.
    public long getContentType() {
        return SearchableContentTypeConstants.CONTENT_TYPE_MEDIA_MEMO;
    }
    
    // The search initiator passes control to your app using this method.
    public void search(String keywords) {

        // Create network or IPC connections, send search keywords, 
        // and display the results.
    }
    
    // Allows the Unified Search Service and your application to 
    // keep track of this searchable's registration.
    public long getRegistrationID()
    {
        return _regId;
    }

    public setRegistrationID(long id)
    {
        _regId = id;
    }
}

Ensure that a device runs a single instance of your application

To improve the efficiency of your application, you should make sure that the BlackBerry device runs only one instance when search() is invoked in your ExternalSearchProvider implementation.

In your implementation of ExternalSearchProvider, import packages to help you discover which applications are running on the device.

import net.rim.device.api.system.ApplicationDescriptor;
import net.rim.device.api.system.ApplicationManager;
import net.rim.device.api.system.ApplicationManagerException;
import net.rim.device.api.system.CodeModuleManager;

In search(), retrieve a handle for your application.

public void search(String keywords) {
    int modHandle = CodeModuleManager.getModuleHandle("MyApplication");

Retrieve an array of objects that represent the applications that are running on the device.

    ApplicationDescriptor[] allApps = ApplicationManager
												.getApplicationManager().getVisibleApplications();

Examine each element of the array to determine whether your application is running on the device.

    for(int i = allApps.length -1; i >= 0; --i) {
        if(allApps[i].getModuleHandle() == modHandle) {

If your application is running, send the search keywords to it. Invoke postGlobalEvent() to send your application a message that it should display a screen with the results for the new keywords. You need to detect a global event that is posted to your GUID in your application's constructor.

            int procID = ApplicationManager.getApplicationManager()
																				.getProcessId(allApps[i]);
            ApplicationManager.getApplicationManager().postGlobalEvent(procID,
            your application's GUID, 0, 0, keywords, null);
        }
    }

If your application is not running on the device, retrieve an ApplicationDescriptor object that represents your application.

    ApplicationDescriptor[] myAppDes =  
												CodeModuleManager.getApplicationDescriptors(modHandle);

Start your application. Pass the search keywords as an argument. In your application's constructor, you need to detect whether there is an argument that contains search keywords.

    try {
        ApplicationManager.getApplicationManager()
							.runApplication(new ApplicationDescriptor(myAppDes[0], 
								new String[]{keywords}));
        }
        catch(ApplicationManagerException e)
        {
            // Process the error condition
        }
    }
}

In your application class, import the net.rim.device.api.system.GlobalEventListener package to listen for global events.

import net.rim.device.api.system.GlobalEventListener;

Import the UiApplication class.

import net.rim.device.api.ui.UiApplication;

Register your application to listen for global events, and display your first screen.

public class MySearchProviderApp extends 
								UiApplication implements GlobalEventListener {

    public MySearchProviderApp(String searchKeywords) {
        addGlobalEventListener(this);
        pushScreen(new MySearchScreen(searchKeywords));
    }

Implement eventOccured() to respond to global events.

    public void eventOccurred(long guid, int data0, int data1, 
													Object object0, Object object1) {

If your application is running on the device, close the existing screen and display another screen.

        if(guid == G53DDE84S97JHVEK390) {
            if(object0 instanceof String) {
                popScreen();
                pushScreeen(new MySearchScreen((String) object0));
                requestForeground();
            }
        }
    }

Test for search keywords in the arguments for the main method. Start your application with the keywords that are given, or with an empty string if no keywords are given.

    public static void main(String[] args) {
        String searchKeywords = "";
        if(args != null  && args.length > 0) {
            searchKeywords = args[0];
        }
        MySearchProviderApp app = new MySearchProviderApp(searchKeywords);
        app.enterEventDispatcher();
    }
}

Inserting data when a device starts

Each time a BlackBerry device restarts (for example, when the battery is removed), the Unified Search Service starts with an empty registry and content repository. If you want your application data to appear in search results before your application runs, you should create an alternate entry point that registers your searchable entities when the device starts.

Searching

You can use the UnifiedSearchServices class to configure and start a search operation from your application. In the simplest case, you can search all data sources when you submit a keyword to the Unified Search Service. However, you can limit your search to particular searchable data sources, and search fields within those sources.

When your search is complete, the Unified Search Service returns a SearchResponse object that contains searchable entities that matched your search criteria. The Service returns the data organized by searchable data source and search field. The structure of the result allows you to look at the metadata that is related to the result. In this way, you can make more detailed decisions about the relevance of search results to your users.

Configure and start a search

This task demonstrates how to start a search that is limited to the searchable entities that your application creates. The UnifiedSearchServices.search() method blocks thread execution, so doSearch() below invokes it in a separate thread.

Import the required classes and interfaces.

import net.rim.device.api.unifiedsearch.*;
import net.rim.device.api.unifiedsearch.searchables.*;
import java.util.*;

Create an instance variable to store a reference to the UnifiedSearchServices object.

public class MySearchScreen() {
    UnifiedSearchServices _uss = UnifiedSearchServices.getInstance();

Retrieve a reference to your EntityBasedSearchable object and assign it to an instance variable. When you retrieve a list of searchable applications on the device, change the SearchableContentTypeConstants value to one that is appropriate for your search. Make sure you take some kind of action if your EntityBasedSearchable is not found in the deviceSearchables vector.

MySearchable _searchable = null;
Vector deviceSearchables = _uss.getDeviceSearchables(
            SearchableContentTypeConstants.CONTENT_TYPE_MEMO);
for(int i = deviceSearchables.size() -1; i >=0; --i) {
    if(deviceSearchables.elementAt(i).getName().equals(
              "name of your searchable")) {
        _searchable = (MySearchable) deviceSearchables.elementAt(i);
    }
}
// Do something if _searchable is still null

Create a method to start the search. Accept the search keyword as a parameter, where your application specifies the search keywords that the BlackBerry device user provides. This method invokes a new thread, so declare the String parameter as final.

private void doSearch(final String keyword) {

Create the Thread object that will run your search.

    Thread searchThread = new Thread(new Runnable() {
        public void run() {
            try {

Start the search and retrieve the results, if any. If there is a result, send it to another method to be parsed.

                SearchResponse results = null;
                results = _uss.search(keyword, _searchable);
                if(results != null) {
                    parseResponse(results);
                }

Catch any errors that are thrown.

            } catch (Exception e) {
                // Do something about the error
            }

Complete the Thread definition and run it.

        }, "Search Thread");
    searchThread.start();

Implement parseResponse().

Process search results

Ensure that UnifiedSearchServices.search() returned a SearchResponse object with data. Pass the SearchResponse to parseResponse().

The SearchResponse object that you receive from the Unified Search Service structures your search results by the EntityBasedSearchable and search fields that matched your keywords. To access the data, you need to parse this object.

Import the required classes and interfaces.

import net.rim.device.api.unifiedsearch.entity.*;
import net.rim.device.api.util.Arrays;
import net.rim.device.api.system.Application;
import java.util.*;

Create an instance variable to store searchable entities to display to the BlackBerry device user.

MySearchableEntity[] _myEntities;

Implement parseResponse. This method runs after your search thread terminates, so declare the SearchResponse parameter as final.

private void parseResponse(final SearchResponse searchResult) {

Configure this method to run when your application regains control of the event thread.

    Application.getApplication().invokeLater(new Runnable() {
        public void run() {

From the search results, retrieve a Hashtable object that contains search fields and searchable entities for your EntityBasedSearchable.

            final Hashtable results = 
                        searchResult.getSearchResult(_searchable);

Retrieve an Enumeration object that contains the values in the Hashtable, and declare an array to store unique values from the enumeration.

            Enumeration values = results.elements();
            Object[] searchableEntities;

Initialize your array of searchable entities to display.

            _myEntities = new MySearchableEntity[0];

Iterate through the values enumeration and add unique values to searchableEntities.

            while(values.hasMoreElements()) {
                searchableEntities = (Object[]) values.nextElement();
                for(int i = searchableEntities - 1; i >= 0; --i) {
                    if(!Arrays.contains(_myEntities, searchableEntities[i]) {
                        Arrays.add(_myEntities, searchableEntities[i]);
                    }
                }
            }
        }
    });
} 

The _myEntities array contains a set of unique MySearchableEntity objects that you can use to display the search results to the user.