Modify the BlackBerry Hybrid Web Container so that Hybrid Apps appear in a tree view.
/*
Copyright (c) SAP, Inc. 2012 All rights reserved.
In addition to the license terms set out in the SAP License Agreement for
the SAP Mobile Platform ("Program"), the following additional or different
rights and accompanying obligations and restrictions shall apply to the source
code in this file ("Code"). SAP grants you a limited, non-exclusive,
non-transferable, revocable license to use, reproduce, and modify the Code
solely for purposes of (i) maintaining the Code as reference material to better
understand the operation of the Program, and (ii) development and testing of
applications created in connection with your licensed use of the Program.
The Code may not be transferred, sold, assigned, sublicensed or otherwise
conveyed (whether by operation of law or otherwise) to another party without
SAP's prior written consent. The following provisions shall apply to any
modifications you make to the Code: (i) SAP will not provide any maintenance
or support for modified Code or problems that result from use of modified Code;
(ii) SAP expressly disclaims any warranties and conditions, express or
implied, relating to modified Code or any problems that result from use of the
modified Code; (iii) SAP SHALL NOT BE LIABLE FOR ANY LOSS OR DAMAGE RELATING
TO MODIFICATIONS MADE TO THE CODE OR FOR ANY DAMAGES RESULTING FROM USE OF THE
MODIFIED CODE, INCLUDING, WITHOUT LIMITATION, ANY INACCURACY OF DATA, LOSS OF
PROFITS OR DIRECT, INDIRECT, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES, EVEN
IF SAP HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES; (iv) you agree
to indemnify, hold harmless, and defend SAP from and against any claims or
lawsuits, including attorney's fees, that arise from or are related to the
modified Code or from use of the modified Code.
*/
package com.sybase.hwc.amp;
import com.sybase.mo.*;
import com.sybase.hybridApp.*;
import java.util.Enumeration;
import net.rim.device.api.i18n.ResourceBundle;
import net.rim.device.api.system.*;
import net.rim.device.api.ui.*;
import net.rim.device.api.ui.component.*;
import net.rim.device.api.ui.container.*;
import net.rim.device.api.util.SimpleSortingVector;
import com.sybase.hwc.*;
// BLACKBERRY_CUSTOMIZATION_POINT_AUTOSTART
// BLACKBERRY_CUSTOMIZATION_POINT_COLORS
// BLACKBERRY_CUSTOMIZATION_POINT_FONTS
// BLACKBERYY_CUSTOMIZATION_POINT_HYBRIDAPPLIST
/**
* This class displays a list of user invokable widgets currently present on the
* device.
*/
public class TreeViewAppScreen extends MainScreen {
// Create a ResourceBundle object to contain the localized resources.
// Here is a little bit of MAGIC. How do you know what there is a class HybridWebContainerResource? (hint: its not from the docs)
// It is auto generated by the JDE. Convention is AppNameResource.BUNDLE_ID, AppNameResource.BUNDLE_NAME
// http://www.codeproject.com/KB/mobile/EndToEndBBApp5.aspx
public static final ResourceBundle RESOURCE =
ResourceBundle.getBundle(
HybridWebContainerResource.BUNDLE_ID,
HybridWebContainerResource.BUNDLE_NAME );
public TreeViewAppScreen() {
super(Manager.VERTICAL_SCROLL | Manager.NO_HORIZONTAL_SCROLLBAR);
setTitle( RESOURCE.getString( HybridWebContainerResource.IDS_HYBRIDAPPS ) );
// Sort apps by their display name
m_oApps = new SimpleSortingVector();
m_oApps.setSortComparator( CustomizationHelper.getInstance().getHybridAppComparator() );
m_oApps.setSort(false);
// Populate and sort list
BBHybridAppHelper.addAppStoreListener( m_oAppListener );
// Add list field to screen
m_oTreeField = new TreeField( m_oTreeFieldCallback, TreeField.FOCUSABLE );
m_oTreeField.setEmptyString( BBHybridWebContainer.getMocaStringResource( MocaClientLibResource.LBL_NO_WIDGETS_FOUND ), DrawStyle.HCENTER );
// set the size of the indentation
m_oTreeField.setIndentWidth( 30 );
populateList();
updateScreen();
// add the tree field to the screen
add( m_oTreeField );
}
/**
* Handle clicking on an application
*/
protected boolean navigationClick(int status, int time)
{
Field oField = getFieldWithFocus();
// only handle if it was the tree field that was clicked
if ( oField instanceof TreeField )
{
Object obj = m_oTreeField.getCookie( ( ( TreeField ) oField ).getCurrentNode() );
// only handle the click if it was a hybrid app (not a tree label)
if( obj instanceof HybridApp )
{
// launch the clicked hybrid app
HybridApp oApp = ( HybridApp ) obj;
XmlHybridApp.startHybridApp( oApp.getModuleId(), oApp.getVersion(), false );
return true;
}
}
return super.navigationClick(status, time);
}
/**
* Override the default Screen.close method
*/
public void close()
{
BBHybridAppHelper.removeAppStoreListener( m_oAppListener );
UiApplication oApp = UiApplication.getUiApplication();
oApp.popScreen(this);
if ( oApp.getScreenCount() == 0 )
{
oApp.requestBackground();
}
}
protected void makeMenu( Menu menu, int instance )
{
menu.deleteAll();
if ( CustomizationHelper.getInstance().enableSettings() )
{
menu.add(m_mniSettings);
}
menu.add(MenuItem.getPrefab(MenuItem.CLOSE));
}
/**
* Fills in list of apps
*/
private void populateList()
{
m_oApps.removeAllElements();
for ( Enumeration e = BBHybridAppHelper.getClientHybridApps().elements(); e.hasMoreElements(); )
{
HybridApp oHybridApp = ( HybridApp )e.nextElement();
m_oApps.addElement( oHybridApp );
}
m_oApps.reSort();
}
/**
* Updates the screen
*/
private void updateScreen()
{
// have to do stuff to the UI on a separate thread
UiApplication.getUiApplication().invokeLater(
new Runnable()
{
public void run()
{
m_oTreeField.deleteAll();
// if there're no hybrid apps then we do not even want to add the tree labels
// so that the empty string will be displayed
if( m_oApps.size() > 0 )
{
// In this example, there are 3 top level categories of hybrid apps: Forms, Expense, and Miscellaneous.
// Forms has a sub-category of SpecialForms. In practice you can have as many or as few categories
// and sub-categories as you like. Here the category of a hybrid app is determined by whether
// keywords exist in the display name of that hybrid app, but you could use anything else (for example
// you could determine the category of a hybrid app by its icon).
int iMiscel = m_oTreeField.addChildNode( 0, "Miscellaneous Hybrid Apps");
int iForms = m_oTreeField.addChildNode( 0, "Form Hybrid Apps");
int iSpecialForms = m_oTreeField.addChildNode( iForms, "Special Forms");
int iExpense = m_oTreeField.addChildNode( 0, "Expense Hybrid Apps");
//have to iterate backwards through m_oApps since addChildNode adds the new node
//to the first position (appears above the nodes previously added).
for( int index = m_oApps.size()-1; index >= 0; index-- )
{
HybridApp oHybridApp = (HybridApp) m_oApps.elementAt( index );
int iParent = iMiscel;
if( oHybridApp.getDisplayName().indexOf("Expense") >= 0 )
{
iParent = iExpense;
}
else if( oHybridApp.getDisplayName().indexOf("Form") >= 0 )
{
if( oHybridApp.getDisplayName().indexOf("Special") >= 0 )
{
iParent = iSpecialForms;
}
else
{
iParent = iForms;
}
}
m_oTreeField.addChildNode( iParent, m_oApps.elementAt( index ) );
}
}
}
} );
}
// Settings menu item
private MenuItem m_mniSettings =
new MenuItem( m_res.getString(HybridWebContainerResource.IDS_SETTINGS),
100001,
10)
{
public void run()
{
XmlHybridApp.startHybridAppSettings(false);
}
};
// Listener for app changes
private HybridAppsListener m_oAppListener =
new HybridAppsListener()
{
public void onRefreshRequired()
{
populateList();
updateScreen();
}
public void onHybridAppAdded(HybridApp oHybridApp)
{
populateList();
updateScreen();
}
public void onHybridAppRemoved(HybridApp oHybridApp)
{
populateList();
updateScreen();
}
public void onHybridAppUpdated(HybridApp oHybridApp)
{
populateList();
updateScreen();
}
};
private SimpleSortingVector m_oApps;
private TreeField m_oTreeField;
private static ResourceBundle m_res = ResourceBundle.getBundle(
HybridWebContainerResource.BUNDLE_ID,
HybridWebContainerResource.BUNDLE_NAME );
private TreeFieldCallback m_oTreeFieldCallback = new TreeFieldCallback()
{
public void drawTreeItem( TreeField oTree, Graphics oGraphics, int iNode, int iY, int iWidth, int iIndent )
{
Object obj = oTree.getCookie( iNode );
if( obj instanceof String )
{
oGraphics.setColor( Color.BLACK );
oGraphics.drawText( (String)obj, iIndent, iY);
}
else if( obj instanceof HybridApp )
{
// y parameter is already offset to center text
int iOffset = (oTree.getRowHeight() - getFont().getHeight()) >> 1;
// Draw a background color for the hybrid apps to distinguish them from the tree labels.
// However, if this node has focus we don't want to draw the grey rectangle because it
// will cover up the blue color indicating the node is selected.
if( iNode != m_oTreeField.getCurrentNode() )
{
oGraphics.setColor( Color.LIGHTGRAY );
oGraphics.fillRect( iIndent, iY - iOffset, iWidth, m_oTreeField.getRowHeight() );
}
HybridApp oApp = ( HybridApp ) obj;
final int iMargin = 2;
// Draw image
EncodedImage oImage = EncodedImage.getEncodedImageResource( "ampicon" + oApp.getIconIndex() + ".png" );
int iBitmapWidth = 0;
if ( oImage != null )
{
CustomIcon oIcon = oApp.getDefaultCustomIcon();
if ( oIcon != null )
{
EncodedImage oImageTmp = oApp.getCustomIconImage( oIcon );
if ( oImageTmp != null )
{
if ( oImageTmp.getHeight() != oImage.getHeight() || oImageTmp.getWidth() != oImage.getWidth() )
{
MocaLog.getAmpHostLog().logMessage(
"Icon image size doesn't match the built-in icon size, the layout result could be different.",
MocaLog.eMocaLogLevel.Normal );
}
oImage = oImageTmp;
}
}
Bitmap oBitmap = oImage.getBitmap();
int iRowHeight = oTree.getRowHeight();
int iSize = oImage.getHeight() > oImage.getWidth() ? oImage.getHeight() : oImage.getWidth();
if ( iSize >= iRowHeight )
{
oBitmap = HWCMessagesListField.getScaledBitmapImage( oImage, iRowHeight - iMargin, iSize );
}
oGraphics.drawBitmap(
iMargin + iIndent,
iY - iOffset + ( oTree.getRowHeight() - oBitmap.getHeight() ) / 2,
oBitmap.getWidth(), oBitmap.getHeight(), oBitmap, 0, 0 );
iBitmapWidth = oBitmap.getWidth();
}
else
{
MocaLog.getAmpHostLog().logMessage( "Can not find application icon image of application " +
oApp.getDisplayName() + ".", MocaLog.eMocaLogLevel.Normal );
}
// Draw text
oGraphics.setColor( Color.BLACK );
oGraphics.drawText( oApp.getDisplayName(), 2 * iMargin + iBitmapWidth + iIndent, iY );
}
}
};
}