/*
* UnifiedSearchDemoSearchScreen.java
*
* Copyright � 1998-2011 Research In Motion Limited
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* Note: For the sake of simplicity, this sample application may not leverage
* resource bundles and resource strings. However, it is STRONGLY recommended
* that application developers make use of the localization features available
* within the BlackBerry development platform to ensure a seamless application
* experience across a variety of languages and geographies. For more information
* on localizing your application, please refer to the BlackBerry Java Development
* Environment Development Guide associated with this release.
*/
package com.rim.samples.device.unifiedsearchdemo;
import java.util.Vector;
import net.rim.device.api.command.Command;
import net.rim.device.api.command.CommandHandler;
import net.rim.device.api.command.ReadOnlyCommandMetadata;
import net.rim.device.api.system.Application;
import net.rim.device.api.system.Characters;
import net.rim.device.api.ui.ContextMenu;
import net.rim.device.api.ui.Field;
import net.rim.device.api.ui.FieldChangeListener;
import net.rim.device.api.ui.Graphics;
import net.rim.device.api.ui.MenuItem;
import net.rim.device.api.ui.Screen;
import net.rim.device.api.ui.XYRect;
import net.rim.device.api.ui.component.BasicEditField;
import net.rim.device.api.ui.component.EditField;
import net.rim.device.api.ui.component.ListField;
import net.rim.device.api.ui.component.ListFieldCallback;
import net.rim.device.api.ui.component.Menu;
import net.rim.device.api.ui.component.SeparatorField;
import net.rim.device.api.ui.component.TextField;
import net.rim.device.api.ui.container.MainScreen;
import net.rim.device.api.ui.container.VerticalFieldManager;
import net.rim.device.api.ui.image.Image;
import net.rim.device.api.unifiedsearch.SearchField;
import net.rim.device.api.unifiedsearch.SearchResponse;
import net.rim.device.api.unifiedsearch.UnifiedSearchServices;
import net.rim.device.api.unifiedsearch.action.UiAction;
import net.rim.device.api.unifiedsearch.entity.SearchableEntity;
import net.rim.device.api.unifiedsearch.searchables.Searchable;
import net.rim.device.api.unifiedsearch.searchables.SearchableContentTypeConstantsInfo;
import net.rim.device.api.util.StringProvider;
/**
* A MainScreen with a search field and a result list
*/
public class UnifiedSearchDemoSearchScreen extends MainScreen implements
ListFieldCallback, FieldChangeListener {
private final EditField _searchInput;
private Vector _displayables;
private final ListField _listField;
private final GraphicsHelper _graphicsHelper;
/**
* Creates a new UnifiedSearchDemoSearchScreen
*/
public UnifiedSearchDemoSearchScreen() {
super(NO_VERTICAL_SCROLL);
_graphicsHelper = new GraphicsHelper();
setTitle("Search Screen");
// This manager will facilitate input being directed to the search field
final SearchScreenVerticalFieldManager scvfm =
new SearchScreenVerticalFieldManager();
// This manager will contain the search result list field
final VerticalFieldManager vfm =
new VerticalFieldManager(VERTICAL_SCROLL);
_searchInput = new EditField("Search:", "", 30, TextField.NO_NEWLINE);
_searchInput.setChangeListener(this);
_listField = new DemoListField();
_listField.setCallback(this);
// Add fields to respective managers
vfm.add(_listField);
scvfm.add(_searchInput);
scvfm.add(new SeparatorField());
scvfm.add(vfm);
add(scvfm);
}
/**
* @see Screen#keyChar(char, int, int)
*/
protected boolean keyChar(final char c, final int status, final int time) {
// Open the menu if the enter key is pressed while a list item is
// selected
if (c == Characters.ENTER && _listField.getSelectedIndex() >= 0) {
onMenu(Menu.INSTANCE_DEFAULT);
return true;
}
return super.keyChar(c, status, time);
}
/**
* @see Screen#invokeAction(int)
*/
protected boolean invokeAction(final int action) {
if (action != Field.ACTION_INVOKE) {
return false;
}
// Open the menu if a list item is clicked
onMenu(Menu.INSTANCE_DEFAULT);
return true;
}
/**
* @see Screen#onSavePrompt()
*/
protected boolean onSavePrompt() {
// Suppress the save dialog
return true;
}
/**
* Stops any current search and begins a new search on the given text
*
* @param keyword
* Text to search on
*/
private void search(final String keyword) {
if (_displayables == null) {
_displayables = new Vector();
} else {
_displayables.removeAllElements();
}
SearchResponse results = null;
final UnifiedSearchServices services =
UnifiedSearchServices.getInstance();
// Retrieve searchables
final Vector vector =
services.getDeviceSearchables(SearchableContentTypeConstantsInfo
.getAllContentTypes());
final Searchable[] searchables = new Searchable[vector.size()];
vector.copyInto(searchables);
// Get search fields for each searchable
final SearchField[][] searchFields = new SearchField[vector.size()][0];
for (int i = 0; i < searchables.length; i++) {
searchFields[i] = searchables[i].defineSupportedSearchFields();
}
// Get SearchResponse for keyword
try {
results = services.search(keyword, searchables, searchFields);
} catch (final Exception e) {
UnifiedSearchDemo.errorDialog(e.toString());
}
if (results != null) {
// Parse the results
parseResponse(results);
}
}
/**
* Parse the search results
*
* @param results
* The search results to parse
*/
private void parseResponse(final SearchResponse results) {
if (results != null) {
final Searchable[] dataSources = results.getSearchables();
if (dataSources != null) {
SearchField[] dataSourceFields;
for (int i = 0; i < dataSources.length; i++) {
dataSourceFields = results.getSearchFields(dataSources[i]);
for (int j = 0; j < dataSourceFields.length; j++) {
final Object[] objects =
results.getSearchResults(dataSources[i],
dataSourceFields[j]);
for (int id = 0; id < objects.length; id++) {
final Object obj = objects[id];
if (obj instanceof UnifiedSearchDemoEntity
&& !_displayables.contains(obj)) {
_displayables.addElement(obj);
}
}
}
}
synchronized (Application.getEventLock()) {
// Update result list
_listField.setSize(_displayables.size());
}
}
}
}
/**
* @see FieldChangeListener#fieldChanged(Field, int)
*/
public void fieldChanged(final Field field, final int context) {
if (field == _searchInput) {
final BasicEditField searchField = (BasicEditField) field;
final String keyword = searchField.getText().trim();
if (keyword.length() > 0) {
final Thread worker = new Thread(new Runnable() {
/**
* @see Runnable#run()
*/
public void run() {
try {
search(keyword);
} catch (final Exception e) {
UnifiedSearchDemo.errorDialog(e.toString());
}
}
}, "Unified Search Demo: Search thread");
worker.start();
} else {
_listField.setSize(0);
}
}
}
/**
* @see ListFieldCallback#drawListRow(ListField, Graphics, int, int, int)
*/
public void drawListRow(final ListField listField, final Graphics graphics,
final int index, final int y, final int width) {
if (_displayables.isEmpty()) {
return;
}
final Object element = _displayables.elementAt(index);
if (element instanceof SearchableEntity) {
final SearchableEntity item = (SearchableEntity) element;
final int rowHeight = listField.getRowHeight();
// Paint icon for this item/searchable
Image image = item.getIcon();
if (image == null) {
image = item.getSearchable().getIcon();
}
if (image != null) {
// Paint icon with equal height and width
image.paint(graphics, 3, y - y % rowHeight, rowHeight,
rowHeight);
}
// Build up display text
final StringBuffer buffer = new StringBuffer();
final String title = item.getTitle();
final String summary = item.getSummary();
final boolean equal = title.equals(summary);
if (title != null) {
buffer.append(title);
if (summary != null && summary.length() != 0 && !equal) {
buffer.append(", ");
}
}
buffer.append(summary == null || equal ? "" : summary);
// Draw text with 10 pixels padding between icon and text
final XYRect rect =
new XYRect(rowHeight + 10, y, listField.getWidth(),
rowHeight);
_graphicsHelper.drawTextWithHighlight(graphics, buffer.toString(),
_searchInput.getText(), rect);
}
}
/**
* @see ListFieldCallback#get(ListField, int)
*/
public Object get(final ListField listField, final int i) {
// Not implemented
return null;
}
/**
* @see ListFieldCallback#getPreferredWidth(ListField)
*/
public int getPreferredWidth(final ListField listField) {
return Integer.MAX_VALUE;
}
/**
* @see ListFieldCallback#indexOfList(ListField, String, int)
*/
public int indexOfList(final ListField listField, final String prefix,
final int start) {
// Not applicable (user input is redirected to the input field)
return -1;
}
/**
* A VerticalFieldManager that will direct input to the screen's input field
*/
final class SearchScreenVerticalFieldManager extends VerticalFieldManager {
/**
* @see Field#keyChar(char, int, int)
*/
protected boolean keyChar(final char ch, final int status,
final int time) {
_searchInput.setFocus();
return super.keyChar(ch, status, time);
}
}
/**
* A ListField with a custom context menu
*/
private class DemoListField extends ListField {
/**
* @see ListField#getContextMenu()
*/
public ContextMenu getContextMenu() {
final ContextMenu menu = super.getContextMenu();
if (_displayables != null && !isEmpty()) {
menu.clear();
final int i = getSelectedIndex();
final Object element = _displayables.elementAt(i);
if (element instanceof UnifiedSearchDemoEntity) {
final UnifiedSearchDemoEntity item =
(UnifiedSearchDemoEntity) element;
final UiAction action = item.getUiActions(null, null);
if (action != null) {
// Add the appropriate UiAction to the context menu
final MenuItem performActionItem =
new MenuItem(new StringProvider(action
.toString()), 0x230010, 0);
performActionItem.setCommand(new Command(
new CommandHandler() {
/**
* @see net.rim.device.api.command.CommandHandler#execute(ReadOnlyCommandMetadata,
* Object)
*/
public
void
execute(final ReadOnlyCommandMetadata metadata,
final Object context) {
action.performAction(item);
}
}));
menu.addItem(performActionItem);
}
}
}
return menu;
}
}
}