/* * Copyright 2008-2014 Mirko Solazzi - OBEROn Platform [www.oberonplatform.com] * * 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. * */ package com.oberon.client; import java.util.*; import javax.servlet.http.HttpSession; import android.app.Activity; import android.app.AlertDialog; import android.content.Context; import android.content.DialogInterface; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; import android.view.Gravity; import android.view.LayoutInflater; import android.view.View; import android.view.Window; import android.view.WindowManager; import android.view.View.OnClickListener; import android.view.ViewGroup; import android.widget.ArrayAdapter; import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.TextView; import com.oberon.client.ApplicationSession; import com.oberon.client.Configuration; import com.oberon.ooql.connection.ConnectionManager; import com.oberon.ooql.connection.HTTPConnection; import com.oberon.ooql.connection.OBConnection; import com.oberon.ooql.parser.ParserCreationException; import com.oberon.ooql.parser.ParserLogException; import com.oberon.ooql.sdk.*; import com.oberon.ooql.sdk.Dictionary; import com.oberon.ooql.sdk.MenuItem.OnMenuItemClickListener; import com.oberon.util.StringUtils; /** * Basic class for Android Applications. * Includes connection and utility functions. * * @author Mirko Solazzi * @version 5.0 */ public abstract class Application extends Activity { /** Default separator token */ public final static String sToken="<|>"; // Web Links /** OBEROn Web Site */ public final static String URL_OBERON = "http://www.oberonplatform.com/"; /** OOQL syntax documentation web link */ public final static String URL_OOQL = URL_OBERON+"ooql.php"; /** Java API documentation web link */ public final static String URL_JAVAAPI = URL_OBERON+"api/"; // Display Parameters /** The current Application */ private static Application app; /** The Application title */ public static String name = "OBEROn Platform - Application Client"; /** The Current screen width */ public static int screenWidth = 400; /** The Current screen height */ public static int screenHeight = 400; /** The Current screen density */ public static float screenDensity = 1.0f; // User Context /** The Application session */ public ApplicationSession session ; /** OBEROn user framework */ public Framework framework; /** Default Locale */ public Locale locale = Locale.ENGLISH; /** Select Application from a list */ public boolean selectApplication = false; /** The Application dialog registry */ public final static Hashtable dialogs = new Hashtable() ; // Configuration and Connection /** Configuration object */ public Configuration conf; // Application Parameters (Defaults) /** The Application main menu */ public static String application_MainMenu="Application"; /** Dictionary section for Menu labels*/ public static String dictionary_menu_section="Menu"; /** Dictionary section for Common labels*/ public static String dictionary_common_section="Common"; /** Dictionary section for Application labels*/ public static String dictionary_section="Application"; /** The default dictionary language */ public static String dictionary_default_language = "EN"; /** Show the Object class in the result lists */ public static boolean show_object_class = true; /** Show the Object name / revision in the result lists */ public static boolean show_object_namerev = true; /** Use the search patterns for text fields */ public static boolean use_search_patterns = true; /** Use the fixed decimal separator */ public static String number_separator_symbols = ""; /** Default name-revision separator */ public static String name_revision_separator = "."; /** Label "All" for search selection item */ public static String search_all_label = "All"; // If add new parameters: update the MenuDlg class and createMenu method /** "ID" */ public final static String ID = "ID"; /** "FILE" */ public final static String CONF_FILE_SECTION = "FILE"; /** "VIEW" */ public final static String CONF_VIEW_SECTION = "VIEW"; private Vector vCoords; public static float w_multiplier = 1.5f ; public static float h_multiplier = 1.5f ; public Application () { } public synchronized void setApp(Application application) { app = application; } public static Application getApp() { return app; } //---------------------------- CONFIGURATION FILE PROPERTIES ------------------------ /** * Get a configuration property value * * @param section configuration file section * @param property property name */ public String getProperty(String section, String property) { if (conf!=null) { String sValue = conf.getValue(section,property); if (sValue!=null) { return sValue; } } return ""; } /** * Get a configuration property value from the "CONNECTION" section * * @param property property name */ public String getConnectionProperty(String property) { if (conf!=null) { String sValue = conf.getValue(OBConnection.CONN_SECTION,property); if (sValue!=null) { return sValue; } } return ""; } //----------------------- DATABASE CONNECTIONS --------------------------- /** * Open OBEROn Connections * @throws Exception */ public void openConnections() throws Exception { session = new ApplicationSession(); if (conf!=null) { conf.setValue(OBConnection.CONN_SECTION,OBConnection.CONN_TYPE,HTTPConnection.CONN_TYPE); } ConnectionManager.openConnections(this); String username = getConnectionProperty(ConnectionManager.PROP_OBUSR); String password = getConnectionProperty(ConnectionManager.PROP_OBPWD); if ( username!=null && username.length()>0 ) { openFramework(username,password!=null?password:""); if (framework.getLanguage().length()==0) { framework.setLanguage(dictionary_default_language); } session.setAttribute(Arg.Language , framework.getLanguage() ); } } /** Close all OBEROn connections * * @since 4.1 */ public void closeConnections() { ConnectionManager.closeConnections(this); } /** * Get OBEROn connection * * @see ConnectionManager#getConnection(Framework) */ public OBConnection getConnection() { if (framework!=null) framework.clearResult(); return ConnectionManager.getConnection(framework); } /** * Release an OBEROn connection * * @param conn The OBEROn connection * * @see ConnectionManager#releaseConnection(OBConnection,Framework) */ public void releaseConnection(OBConnection conn) { ConnectionManager.releaseConnection(conn,framework); } /** * Check if the Application is connected to the Database or to RMI/HTTP server * * @see ConnectionManager#isConnected() */ public boolean isConnected() { return ConnectionManager.isConnected(); } /** * Check if the Connection is established directly to the DB * * @see ConnectionManager#isDBConnection() */ public boolean isDBConnection() { return ConnectionManager.isDBConnection(); } //---------------------------- OBEROn ACTIONS ------------------------ /** * Retrieve the current framework from the application session */ public static Framework getFramework(HttpSession session) { return (Framework)session.getAttribute(Arg.Framework); } /** * Save the current framework into the application session */ public static void setFramework(HttpSession session,Framework framework) { session.setAttribute(Arg.Framework,framework); } /** * Login into OBEROn * * @param sUserName the user name * @param sPassword the user password */ public void openFramework(String sUserName,String sPassword) throws OberonException { if ( !isConnected() ) { throw new OberonException("NotConnected",new String[0]); } if (StringUtils.isBlank(sUserName)) { throw new OberonException("UserNameNotDefined",new String[0]); } Framework preFramework=framework; framework=new Framework(sUserName,sPassword); if (preFramework==null) { preFramework=framework; } framework.setVerbose(false); framework.setSQLTrace(false); framework.setRenderingEngine("Android"); // link the framework try { framework.link(); log("OBEROn Login for user '"+sUserName+"' successful!" ); setFramework(session,framework); String sResultLog; if ((sResultLog=framework.getResultLog().trim()).length()>0) { log(sResultLog);} framework.clearResultLog(); } catch (OberonException e) { if (preFramework.isLinked()) { framework = preFramework; setFramework(session,framework); } log("OBEROn Login for user '"+sUserName+"' failed: "+e.getMessage() ); throw e; } } /** * Perform one or more OBEROn OOQL commands with current application Framework * * @param sOOQLCommands command text * @return a Vector of String(s) where each element represents the result for the corresponding command * */ public Vector performOOQLCommands( String sOOQLCommands ) throws OberonException, ParserLogException, ParserCreationException { if (framework==null || !framework.isLinked()) { throw new OberonException("Framework not Linked!"); } Vector vRes; String sResultLog; OOQLCommand ooql = new OOQLCommand(sOOQLCommands ); try { vRes = ooql.execute(framework); setFramework(session,framework); } finally { vCoords=ooql.getCommandCoords(); if ((sResultLog=ooql.getResultLog().trim()).length()>0) { log("Result LOG:\n"+sResultLog); framework.clearResultLog(); } } return vRes; } /** * Parse the OOQL command text * * @param sOOQLCommands command text * * @return a report representing the OOQL language parsed tree */ public String parseOOQLCommands(String sOOQLCommands ) throws OberonException, ParserLogException, ParserCreationException { if (framework==null || !framework.isLinked()) { throw new OberonException("Framework not Linked!"); } String result; OOQLCommand ooql = new OOQLCommand(sOOQLCommands ); try { result = ooql.parse(framework); setFramework(session,framework); } finally { vCoords=ooql.getCommandCoords(); } return result; } /** * Get the command text coordinates for each OOQL command * for the last performOOQLCommands execution * * @return Vector of int[4]{startLine,startColumn,endLine,endColumn} */ public Vector getCommandCoords(){ return vCoords; } /** * Turn off the verbose option */ public void setNoVerbose() { if (framework!=null) { framework.setSQLTrace(false); framework.setVerbose(false); framework.setFilterHidden(false); } } //----------------------------- IMAGE MANIPULATION --------------------------------------- /** * Retrieve an image/icon from the Drawable folder * * @param iconName the icon/image file name */ public static Drawable getIcon(String iconName) { int imageId = app.getResources().getIdentifier(iconName, "drawable",app.getPackageName()); if (imageId!=0) { Drawable img = app.getResources().getDrawable(imageId); if (img!=null) { img.setBounds( 0, 0, img.getIntrinsicWidth(), img.getIntrinsicHeight() ); return img; } } return null; } /** * Convert and resize a byte[] image * * @param baImage byte array image * @param scx resize width (if 0 is ignored) * @param scy resize height (if 0 is ignored) * * @return Bitmap Image */ public static Bitmap convertImage(byte[] baImage,int scx,int scy) { if (baImage!=null && baImage.length>0) { Bitmap image = BitmapFactory.decodeByteArray(baImage,0,baImage.length); if (scx!=0 || scy!=0) { if (scx!=0 && scy!=0) { image = Bitmap.createScaledBitmap(image, scx, scy, false); } else if (scx!=0) { image = Bitmap.createScaledBitmap(image, scx, (int)Math.round(scy*scx/(float)image.getWidth()), false); } else if (scy!=0) { image = Bitmap.createScaledBitmap(image, (int)Math.round(scx*scy/(float)image.getHeight()), scy, false); } } return image; } else { return null; } } /** * Retrieve the administrative object icon from the OBEROn DB and resize it * * @param sAdminType administrative object type * @param sAdminName administrative object name * @param scx resize width (if 0 is ignored) * @param scy resize height (if 0 is ignored) * */ public Bitmap getImage(String sAdminType,String sAdminName ,int scx , int scy ) { try { return convertImage(AdminBase.getImage(sAdminType,sAdminName),scx,scy); } catch (Exception ex) { return null; } } /** * Retrieve the administrative object icon using the internal ID and resize it * * @param iAdminId the internal ID * @param scx resize width (if 0 is ignored) * @param scy resize height (if 0 is ignored) */ public Bitmap getImage(int iAdminId ,int scx , int scy ) { try { return convertImage(AdminBase.getImage(iAdminId),scx,scy); } catch (Exception ex) { return null; } } /** * Retrieve a object instance icon and resize it * * @param sObjectID the Object id * @param scx resize width (if 0 is ignored) * @param scy resize height (if 0 is ignored) */ public Bitmap getObjImage(String sObjectID,int scx,int scy ) { try { return convertImage(ObjectObj.getImage(sObjectID),scx,scy); } catch (Exception ex) { return null; } } //----------------------------------- DISPLAY & UTILITY METHODS --------------------- /** * Display an Error Message for a thread Exception * * @param exception the thread Exception * @param sTitle the message box title * @param postrun runnable to be executed after confirmation */ public static void errorMessage(Activity activity, Exception exception,String sTitle,final Runnable postrun) { try { String sMessage=exception.getMessage(); if (activity!=null) { if (sMessage==null) { sMessage = "No message"; } int iline=sMessage.indexOf(", on line"); if (iline>0) sMessage=sMessage.substring(0,iline); new AlertDialog.Builder(activity) .setIcon(android.R.drawable.ic_dialog_alert) .setTitle(sTitle) .setCancelable(false) .setMessage(sMessage) .setPositiveButton("OK", new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int whichButton) { if (postrun!=null) postrun.run(); } }).show(); app.log(sMessage); } else { System.out.println(sMessage); } } catch (Exception ex) {} } /** * Display a Message * * @param sMessage the showed message * @param sTitle the message box title * @param postrun runnable to be executed after confirmation */ public static void showMessage(Activity activity, String sMessage ,String sTitle,final Runnable postrun) { try { if (activity!=null) { if (sMessage==null) { sMessage = "No message"; } int iline=sMessage.indexOf(", on line"); if (iline>0) sMessage=sMessage.substring(0,iline); new AlertDialog.Builder(activity) .setIcon(android.R.drawable.ic_dialog_info) .setTitle(sTitle) .setMessage(sMessage) .setCancelable(false) .setPositiveButton("OK", new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int whichButton) { if (postrun!=null) postrun.run(); } }).show(); } else { System.out.println(sMessage); } } catch (Exception ex) {} } /** * Add a dialog popup to the dialog-window registry * * @param sName the dialog window name * @param dialog the dialog view */ public static void addDialog(String sName,View dialog) { dialogs.put(sName, dialog); } /** * Retrieve a dialog popup from the dialog-window registry * * @param sName the dialog window name */ public static View getDialog(String sName) { View dialog = (View)dialogs.get(sName); if (dialog!=null) { try { return dialog; } catch (Exception ex) { dialogs.remove(sName); } } return null; } /** * Compose the object Class-Name-Revision string */ public static String getClassNameRevision(ObjectObj object,ApplicationSession session) throws Exception{ String sCNR = getTranslation(dictionary_section+"*",object.getClassName()); return sCNR+" "+getNameRevision(object.getName(),object.getRevision()); } /** * Compose the Name-Revision string separated by name_revision_separator */ public static String getNameRevision(String sName,String sRevision) { sRevision=sRevision.trim(); if (sRevision.length()>0 && !sRevision.equals("-")) { return sName+name_revision_separator+sRevision; } else { return sName; } } /** * Compose the link identification string as "Linktype From(Class/Name/Revision) -> To(Class/Name/Revision)" * use the name_revision_separator between the object name and the revision */ public static String getLinkData(Link link,ApplicationSession session) throws Exception{ String sCNRF = getTranslation(dictionary_section+"*",link.getFromClassName()); if (link.getFromRevision().length()>0 && !link.getFromRevision().equals("-")) { sCNRF+=" "+link.getFromName()+name_revision_separator+link.getFromRevision(); } else { sCNRF+=" "+link.getFromName(); } String sCNRT = getTranslation(dictionary_section+"*",link.getToClassName()); if (link.getToRevision().length()>0 && !link.getToRevision().equals("-")) { sCNRT+=" "+link.getToName()+name_revision_separator+link.getToRevision(); } else { sCNRT+=" "+link.getToName(); } String sType = getTranslation(dictionary_section+"*",link.getLinkType()); return sType+" "+sCNRF+" -> "+sCNRT; } /** * Translate a keyword searching in the Application Common dictionary-Section */ public static String getCommonTranslation(String key) { if (key!=null && key.length()>0) { try { return Dictionary.getKey(dictionary_common_section, key , getApp().framework); } catch (Exception ex) { return key; } } else { return key; } } /** * Translate a keyword searching in the Application Menu dictionary-Section */ public static String getMenuTranslation(String key) { if (key!=null && key.length()>0) { try { return Dictionary.getKey(dictionary_menu_section, key , getApp().framework); } catch (Exception ex) { return key; } } else { return key; } } /** * Translate a keyword searching in the Application dictionary-Section */ public static String getTranslation(String key) { if (key!=null && key.length()>0) { try { return Dictionary.getKey(dictionary_section, key , getApp().framework); } catch (Exception ex) { return key; } } else { return key; } } /** * Translate a keyword searching in a specific dictionary-Section */ public static String getTranslation(String section,String key) { if (section!=null && section.length()>0 && key!=null && key.length()>0) { try { return Dictionary.getKey(section, key , getApp().framework); } catch (Exception ex) { return key; } } else { return key; } } /** * Translate a keyword to a specific language, searching in a specific dictionary-Section */ public static String getTranslation(String language,String section,String key) { if (language!=null && language.length()>0 && section!=null && section.length()>0 && key!=null && key.length()>0) { try { return Dictionary.getKey(language,section, key , getApp().framework); } catch (Exception ex) { return key; } } else { return key; } } /** * Generate the application main Menu * * @param menuName the name of {@link Menu} * @param resIds resource Ids (menu_item layout,action icon,action text,empty/expand/expanded icon) * @param imgIds image Ids (no submenus, ) */ public ArrayAdapter createMenu(final Context context, String menuName, final int[] resIds , final int[] imgIds) { // Load application parameters Menu mainmenu =null; try { mainmenu = Menu.open(menuName,framework,null); Vector vFeatures = mainmenu.getFeatures(framework); if (vFeatures!=null) { for (int i=0;i vMenuSections = StringUtils.StringTokensToVector(dictionary_menu_section, ","); Vector vCommonSections = StringUtils.StringTokensToVector(dictionary_common_section, ","); Vector vAppSections = StringUtils.StringTokensToVector(dictionary_section, ","); Vector vApplications = mainmenu.getOwnSubMenus(); for (int i=0;i list = new ArrayList( mainmenu.getActions(new Vector(0), app.framework) ); // Remove all sub-levels [sub-menus are added inside the adapter] for ( int i = list.size()-1;i>=0;i-- ) { if ( list.get(i).getLevel()>0 ) list.remove(i); } // Prepare the adapter return new ArrayAdapter(context,resIds[0],list ) { final ArrayAdapter adapter = this; ViewHolder holder; public View getView( final int position, View convertView, ViewGroup parent ) { if (convertView == null) { // Inflate the menu item layout and save it into a cache LayoutInflater vi = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE); convertView = vi.inflate(resIds[0], parent, false); // menu_item layout holder = new ViewHolder(); holder.item1 = (ImageView) convertView.findViewById(resIds[1]); // action icon holder.item2 = (TextView) convertView.findViewById(resIds[2]); // action text holder.item3 = (ImageView) convertView.findViewById(resIds[3]); // empty/expand/expanded icon convertView.setTag(holder); } else { // Retrieve the menu item layout from the cache holder=(ViewHolder)convertView.getTag(); } final MenuItem item = getItem(position); if (item != null) { // set the action icon holder.item1.setImageDrawable(new BitmapDrawable(getResources(),convertImage(item.getIcon(),0,0))); // set the action text (indented) String sSpacer= ""; for (int i=0;i0 ) { // Open the action href OnMenuItemClickListener listener = new ToolBarHandlerApp(item.getHRef()); listener.onMenuItemClick(item); } else if (item.getSubItems()!=null) { // Expand / Compress the sub-items ArrayList submenu = new ArrayList(item.getSubItems()); int j = position+1; int iSize = adapter.getCount(); for (int i=iSize-1;i>=0;i--) { MenuItem adpitem = adapter.getItem(i); if ( adpitem.getLevel()>item.getLevel() ) { adapter.remove(adpitem); if (j>i) j--; } else if ( adpitem.getLevel()==item.getLevel() ) { adpitem.setExpanded(false); } } for (int i=0;i(context,resIds[0]); } private static class ViewHolder{ public ImageView item1; public TextView item2; public ImageView item3; } // Handle menu action HRef private class ToolBarHandlerApp implements MenuItem.OnMenuItemClickListener { String sCommand; ToolBarHandlerApp(String sCommand){ this.sCommand=sCommand; }; public boolean onMenuItemClick(MenuItem item) { handleRequest(new ApplicationRequest(sCommand,(ApplicationSession)session)); return false; } }; /** * Set custom property to Android view * * @param view the Android view * @param tag the property name * @param value the property value */ public static void setData(android.view.View view,String tag,Object value) { Hashtable map = (Hashtable)view.getTag(); if ( map ==null ) { map = new Hashtable(); view.setTag(map); } if (value!=null) map.put(tag, value); } /** * Get a custom property value from Android view * @param view the Android view * @param tag the property name */ public static Object getData(android.view.View view,String tag) { Hashtable map = (Hashtable)view.getTag(); if ( map != null ) { return map.get(tag); } else return null; } /** * Open new PopUp window (dialog) * * @param parent the parent shell * @param request Application request with target url * @param windowName the dialog window name * @param style the HTML window style (width,height,resizable,scrollable...) * * @return the dialog Shell */ public static AlertDialog windowOpen(View parent,ApplicationRequest request,String windowName,String style) { LinearLayout popDialog = (LinearLayout)getDialog(windowName); if (popDialog==null) { popDialog =new LinearLayout(parent.getContext()); addDialog(windowName, popDialog); AlertDialog.Builder builder = new AlertDialog.Builder(parent.getContext()); builder.setView(popDialog); builder.setCancelable(false); AlertDialog winDialog = builder.create(); winDialog.hide(); setData(popDialog,TAGS.WINDOW,winDialog); setData(popDialog,TAGS.OPENER,getWindow(parent)); } Window window = ((AlertDialog)getData(popDialog,TAGS.WINDOW)).getWindow(); try { StringTokenizer sT = new StringTokenizer(style,","); int width=-1; int height=-1; int left=-1; int top=-1; while (sT.hasMoreTokens()) { String param = sT.nextToken().trim(); StringTokenizer sTV=new StringTokenizer(param,"="); param=sTV.nextToken().trim(); if (sTV.hasMoreTokens()) { String value = sTV.nextToken(); if (param.equals("width")) { width=Math.min(Math.round(w_multiplier*Integer.parseInt(value)),screenWidth); } else if (param.equals("height")) { height=Math.min(Math.round(h_multiplier*Integer.parseInt(value)),screenHeight); } else if (param.equals("left")) { left=Integer.parseInt(value); } else if (param.equals("top")) { top=Integer.parseInt(value); } else if (param.equals("titlebar")) { setData(popDialog,TAGS.pagetitle,!(value.equals("no") || value.equals("0"))); } else if (param.equals("scrollbars")) { setData(popDialog,TAGS.scrollbars,!(value.equals("no") || value.equals("0"))); } } if (width>0 && height>0) { window.setLayout(width, height); } } /* SUPPORTED in Android titlebar=yes|no|1|0 Whether or not to display the title bar. Ignored unless the calling application is an HTML Application or a trusted dialog box. Default is yes scrollbars=yes|no|1|0 Whether or not to display scroll bars. Default is yes NOT SUPPORTED resizable=yes|no|1|0 Whether or not to display window is resizable. Default is yes toolbar=yes|no|1|0 Whether or not to display the browser toolbar. Default is yes location=yes|no|1|0 Whether or not to display the address field. Default is yes status=yes|no|1|0 Whether or not to add a status bar. Default is yes */ WindowManager.LayoutParams wmlp = window.getAttributes(); if (left>=0) { wmlp.gravity = Gravity.LEFT; wmlp.x=left; } if (top>=0) { if (left>=0) wmlp.gravity = Gravity.LEFT | Gravity.TOP ; else wmlp.gravity = Gravity.TOP; wmlp.y=top; } window.setAttributes(wmlp); setData(popDialog,TAGS.REQUEST,request); setData(popDialog,TAGS.PARAMS,style); request.setReferer(parent); request.setTarget(windowName); getApp().handleRequest(request); } catch (Exception ex) {} return (AlertDialog)getData(popDialog,TAGS.WINDOW); } public static View getWindow(View view) { while ( view!=null && getData(view,TAGS.WINDOW)==null ) { view = (View)view.getParent(); } return view; } /** * Show a message in the application Log panel or in the status bar */ public abstract void log(String message); /** * Handle an Application request * * @return true when the request page is recognized as valid page */ public abstract boolean handleRequest(ApplicationRequest request); /** * Convert the language code from the Application format to standard ISO code */ public abstract String translateOberonLanguageToISO639Language(String language); }