Showing posts with label Android Emulator. Show all posts
Showing posts with label Android Emulator. Show all posts

Tuesday, April 2, 2013

Sync Device Back Button With ADF Mobile App's Back Button In TaskFlows

This article is the continuation of my previous article on Handling the Device Back button in ADF Mobile Application. Scenario in this article is to use the device back button to go back to previous page within taskflows, Android device has a back button and our apps should take an advantage of that.

Thanks for Bryan Laipple, in his forum reply he provided the solution on how to programmatically navigate within task flows. Application screen looks like below when it is deployed and run on the Android Device/Emulator. In the below screen Employee list will be displayed.


Clicking on any employee will take you to the selected Employee details page. Notice in the below screen there is no back button provided explicitly, here we can take an advantage of device back button to navigate back to previous page. Now click on the device back button to go back to previous page.


You can download the sample workspace from here. I have checked in Android Device/Emulator and it's working fine.

In below section I will not provide all the steps to create the application from scratch, only key ones are shown. Created DeviceBackButtonTaskFlow.xml taskflow in this application looks like below.


Programmatically navigate within task flows in two ways: -
  • Javascript - Create Javascript file under the feature and add the below code which catches the event that fires when the user presses the device back button. In Javascript function directly call adf.mf.api.amx.doNavigation(outcome), here outcome is the variable that represents the navigation string corresponding to the control flow case.
//This is an event that fires when the user presses the device back button
document.addEventListener("deviceready", onDeviceReady, false);

function onDeviceReady() {
    document.addEventListener("backbutton", backKeyDown, true);
}

function backKeyDown() {
    //Check the device back button action happened in Employee.amx
    if ($('#EmployeeId').length) {
  //Navigate within taskflows using javascript
  adf.mf.api.amx.doNavigation("backToEmployeeList");
    }
}
  • Java - Create Javascript file under the feature and add the below code which catches the event that fires when the user presses the device back button.
//This is an event that fires when the user presses the device back button
document.addEventListener("deviceready", onDeviceReady, false);

function onDeviceReady() {
    document.addEventListener("backbutton", backKeyDown, true);
}

function backKeyDown() {
    //Check the device back button action happened in Employee.amx
    if ($('#EmployeeId').length) {
        //Call the java method in managed bean
        adf.mf.api.invokeMethod("mobile.EmployeeManagedBean", "handleNavigation", onInvokeSuccess, onFail);
    }
}

function onInvokeSuccess(param) {
    //To do code after success
};

function onFail() {
    //To do code after failure
};
Next create a EmployeeManagedBean class under DeviceBackButtonTaskFlow. Add the below code to navigate within the taskflow. This method will be called from javascript function.
public void handleNavigation() {
        //Code to naviagte within task flows programmatically
        AdfmfContainerUtilities.invokeContainerJavaScriptFunction(AdfmfJavaUtilities.getFeatureName(),
                                                                  "adf.mf.api.amx.doNavigation",
                                                                  new Object[] { "backToEmployeeList" });
}

Sunday, March 31, 2013

Handling the Device Back button in ADF Mobile Application

While developing an ADF Mobile application for Android/IOS, you may require to capture the device back button on user click. Here we will add little logic to exit the application once the user clicks on device back button.

We can directly use the phone gap backbutton api to override the default back button behaviour, you can register an event listener for the 'backbutton' event. It is no longer necessary to call any other method to over ride the back button behaviour. Now, you only need to register an event listener for 'backbutton'.

Application screen looks like below when it is deployed and run on the Android Device/Emulator. Click on the device back button will launch the confirm dialog, user can click OK to exit the application.


You can download the sample workspace from here. I have checked in Android Device/Emulator and it's working fine, not sure in IOS device.

Implementation Steps

Create an ADF Mobile Application, the application consists of two projects. Application Controller project of Application LifeCycle, Listeners, Device Features DataControl and ViewController project contains mobile features content like AMX Files, Task Flows and DataControl.

Right click on the ViewController project and in New Gallery window, select Web Tier-> HTML -> JavaScript File and name as "index.js". Next locate and expand the Application Sources folder, then expand the META-INF folder. You will see the adfmf-feature.xml file, click on the adfmf-feature.xml file to launch the Feature editor. Add a new feature by clicking the green plus sign on the Features table near top of the editor this will launch the new Create ADF Mobile Feature dialog, leave the default values and click ok.

In the Features table, select the newly created feature "feature1". Under the Features table, click the Content tab, and locate the Content table. Notice that the content item feature1.1 is created by default. Next add a new file by clicking the green plus sign and select ADF Mobile AMX Page, this will launch the new Create ADF Mobile AMX Page dialog, modify the File Name as index.amx.

Next in Includes section, click the green plus sign and Insert Include the javascript file as shown below.

Open the index.js file and below code catches the event that fires when the user presses the device back button.
//This is an event that fires when the user presses the device back button
document.addEventListener("deviceready", onDeviceReady, false);

function onDeviceReady() {
    document.addEventListener("backbutton", backKeyDown, true);
}

function backKeyDown() {
    var cFirm = confirm("Are you sure you want to exit the application?");
    if (cFirm == true) {
        //Code to exit the application
        navigator.app.exitApp();
    }
}

Monday, March 11, 2013

Get ADF Mobile Form values in managed bean using Accessor Iterator

Scenario is how to get the ADF Mobile form values in managed bean programmatically. Accessor Iterator can be used with AdfmfJavaUtilities.getValueExpression() method to get values.  getValueExpression method is for expressions that refer to values, this method should perform syntactic validation of the expression.

Below is one of the way to access from values, here is the code below.
ValueExpression ve =
            AdfmfJavaUtilities.getValueExpression("#{bindings.editEmployeeIterator.currentRow.dataProvider}",
                                                  Object.class);
        Object obj = ve.getValue(AdfmfJavaUtilities.getAdfELContext());
        Employee empRes = new Employee(); 
        if (obj instanceof ConcreteJavaBeanObject) {
            ConcreteJavaBeanObject cjbo = (ConcreteJavaBeanObject)obj;
            empRes = (Employee)cjbo.getInstance();
        }
        System.out.println(empRes.getId());
        System.out.println(empRes.getEmail());
        System.out.println(empRes.getFirstName());
        System.out.println(empRes.getLastName());
You can download the sample workspace from here. Application screen looks like below when it deployed and run on Android Device/Emulator. In first screen Employee list will be displayed, Next click on Add button to navigate for Employee form and looks like below.


Click on the Save button, check the employee form values by enabling debugger mode as shown below. You can check on how to Debugging ADF Mobile Apps on Android.


Thursday, January 24, 2013

ADF Mobile - Access Device Native SQLite Database to Store Data

Today I learnt on how to access device native SQLite database to store data and perform CRUD operations. This is my first article on ADF Mobile, In this article I will show on how to read data for display, create and save new record to the database.

You can find more examples in Oracle ADF Mobile Samples, by looking into the samples and Debugging ADF Mobile Apps on Android. I was able to use SQLite database to store the data.

Download the sample workspace from here and application screen looks like below when it deployed to Android Emulator. Displaying the Departments List is fetched from SQLite, click on the Add button to create new department record.


Enter the details in the department form and click on Save button. The Department information will saved to the database and moved to Dept List screen.


Department list will get updated by newly added department record.


Implementation Steps

Create an ADF Mobile Application, the application consists of two projects. Application Controller project of Application LifeCycle, Listeners, Device Features DataControl and ViewController project contains mobile features content like AMX Files, Task Flows and DataControl.

In Application Controller project. Create a DBConnectionFactory.java class and copy the below code, this class contains code for connecting to SQLlite DB. Connecting to the SQLite database is somewhat different from opening a connection to an Oracle database, here use the java.sql.Connection object associated with your application to connect to the SQLite database.
public class DBConnectionFactory {
    protected static Connection conn = null;

    public DBConnectionFactory() {
        super();
    }

    /**
     * Connect to the SQLite database
     * @return
     * @throws Exception
     */
    public static Connection getConnection() throws Exception {
        if (conn == null) {
            try {
                // ApplicationDirectory returns the private read-write sandbox area
                // of the mobile device's file system that this application can access.
                // This is where the database is created
                String Dir = AdfmfJavaUtilities.getDirectoryPathRoot(AdfmfJavaUtilities.ApplicationDirectory);
                String connStr = "jdbc:sqlite:" + Dir + "/DEMO.db";

                // If the database does not exist, a new database is automatically
                // created when the SQLite JDBC connection is created
                conn = new SQLite.JDBCDataSource(connStr).getConnection();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        return conn;
    }
}
Note:- demo.sql file should be put in .adf/META-INF/ folder. You can get the sql file by downloading the MobileDemo application.

Open LifeCycleListenerImpl.java file and add the below method code.
/**
* This method will read the sql file and
* commit the sql statements to the SQLite DB
*/
private void InitDB() {
	try {
		// Since the SQL script has been packaged as a resource within
		// the application, the getResourceAsStream method is used
		ClassLoader cl = Thread.currentThread().getContextClassLoader();
		InputStream is = cl.getResourceAsStream(".adf/META-INF/demo.sql");
		if (is == null) {
			System.err.println("Could not look up : .adf/META-INF/demo.sql");
			return;
		}

		BufferedReader bReader = new BufferedReader(new InputStreamReader(is));
		List stmts = new ArrayList();
		String strstmt = "";
		String ln = bReader.readLine();

		// The while loop iterates over all the lines in the SQL script,
		// assembling them into valid SQL statements and executing them as
		// a terminating semicolon is encountered
		while (ln != null) {
			// Skipping blank lines, comments, and COMMIT statements
			if (ln.startsWith("REM") || ln.startsWith("COMMIT")) {
				ln = bReader.readLine();
				continue;
			}
			strstmt = strstmt + ln;
			if (strstmt.endsWith(";")) {
				stmts.add(strstmt);
				strstmt = "";
				ln = bReader.readLine();
				continue;
			}
			ln = bReader.readLine();
		}

		// To improve performance, the statements are executed
		// one at a time in the context of a single transaction
		DBConnectionFactory.getConnection().setAutoCommit(false);
		for (int i = 0; i < stmts.size(); i++) {
			Statement pStmt = DBConnectionFactory.getConnection().createStatement();
			pStmt.executeUpdate((String)stmts.get(i));
		}
		DBConnectionFactory.getConnection().commit();
	} catch (Exception e) {
		e.printStackTrace();
	}
}
In Start() method LifeCycleListenerImpl.java file, add the below code.
try {
	//Getting the connection to the database
	Statement stat = DBConnectionFactory.getConnection().createStatement();
	ResultSet rs = stat.executeQuery("SELECT * FROM DEPARTMENTS;");
  } catch (SQLException e) {
	// if the error message is "out of memory",
	// it probably means no database file is found
	InitDB();
  } catch (Exception e) {
	e.printStackTrace();
}
Expand the ViewController project. Locate and expand the Application Sources folder, create a Department.java file and add the below code.
public class Department {
    protected int deptId;
    protected String deptName;
    protected int mgrId;
    protected int locId;
    private transient PropertyChangeSupport propertyChangeSupport = new PropertyChangeSupport(this);

    public Department() {
        super();
    }

    public Department(int deptId, String deptName, int mgrId, int locId) {
        setDeptId(deptId);
        setDeptName(deptName);
        setMgrId(mgrId);
        setLocId(locId);
    }

    Department(Department newDept) {
        setDeptId(newDept.getDeptId());
        setDeptName(newDept.getDeptName());
        setMgrId(newDept.getMgrId());
        setLocId(newDept.getLocId());
    }

    public String getKey() {
        Integer i = new Integer(deptId);
        return i.toString();
    }

    public void addPropertyChangeListener(PropertyChangeListener l) {
        propertyChangeSupport.addPropertyChangeListener(l);
    }

    public void removePropertyChangeListener(PropertyChangeListener l) {
        propertyChangeSupport.removePropertyChangeListener(l);
    }


    public void setDeptId(int deptId) {
        int oldDeptId = this.deptId;
        this.deptId = deptId;
        propertyChangeSupport.firePropertyChange("deptId", oldDeptId, deptId);
    }

    public int getDeptId() {
        return deptId;
    }

    public void setDeptName(String deptName) {
        String oldDeptName = this.deptName;
        this.deptName = deptName;
        propertyChangeSupport.firePropertyChange("deptName", oldDeptName, deptName);
    }

    public String getDeptName() {
        return deptName;
    }

    public void setMgrId(int mgrId) {
        int oldMgrId = this.mgrId;
        this.mgrId = mgrId;
        propertyChangeSupport.firePropertyChange("mgrId", oldMgrId, mgrId);
    }

    public int getMgrId() {
        return mgrId;
    }

    public void setLocId(int locId) {
        int oldLocId = this.locId;
        this.locId = locId;
        propertyChangeSupport.firePropertyChange("locId", oldLocId, locId);
    }

    public int getLocId() {
        return locId;
    }
}
Create DepartmentList.java file and add the below code. Create DataControl based on DepartmentList.java file.
public class DepartmentList {
    private static List s_departments = null;
    private Department editDepartment = new Department();

    private transient ProviderChangeSupport providerChangeSupport = new ProviderChangeSupport(this);

    public void addProviderChangeListener(ProviderChangeListener l) {
        providerChangeSupport.addProviderChangeListener(l);
    }

    public void removeProviderChangeListener(ProviderChangeListener l) {
        providerChangeSupport.removeProviderChangeListener(l);
    }

    public DepartmentList() {
        if (s_departments == null) {
            s_departments = new ArrayList();
            Execute();
        }
    }

    /**
     * Method will query the database and populate
     * the result list to s_departments
     */
    public void Execute() {
        Trace.log(Utility.ApplicationLogger, Level.INFO, DepartmentList.class, "Execute",
                  "Coming Inside Execute Method");
        try {
            Connection conn = DBConnectionFactory.getConnection();
            s_departments.clear();
            conn.setAutoCommit(false);
            PreparedStatement stat = conn.prepareStatement("SELECT * from DEPARTMENTS ORDER BY DEPARTMENT_NAME");
            ResultSet rs = stat.executeQuery();
            while (rs.next()) {
                int id = rs.getInt("DEPARTMENT_ID");
                String deptName = rs.getString("DEPARTMENT_NAME");
                int mgrId = rs.getInt("MANAGER_ID");
                int locId = rs.getInt("LOCATION_ID");
                Department d = new Department(id, deptName, mgrId, locId);
                s_departments.add(d);
            }
            rs.close();
            Trace.log(Utility.ApplicationLogger, Level.INFO, DepartmentList.class, "Execute",
                      "Exiting from Execute Method");
            //  providerChangeSupport.fireProviderRefresh("departments");
        } catch (SQLException e) {
            System.err.println(e.getMessage());
        } catch (Exception e) {
            System.err.println(e.getMessage());
        }
    }

    /**
     * Method will get count of departments in database
     * @return
     */
    public int getDepartmentCount() {
        return s_departments.size();
    }

    /**
     * Method will get the list of departments
     * @return
     */
    public Department[] getDepartments() {
        Department d[] = null;
        d = (Department[])s_departments.toArray(new Department[s_departments.size()]);
        return d;
    }

    /**
     * Method will create new deptartment object instance
     */
    public void AddDepartment() {
        Trace.log(Utility.ApplicationLogger, Level.INFO, DepartmentList.class, "AddDepartment",
                  "Coming inside AddDepartment");
        //Incrementing the intial deptId by 10
        int deptId = getDepartmentCount() + 10;
        setEditDepartment(new Department(deptId, "", 0, 0));
        Trace.log(Utility.ApplicationLogger, Level.INFO, DepartmentList.class, "AddDepartment",
                  "Exiting AddDepartment");
    }

    /**
     * Method will commit the details of newly created department object
     * @return
     */
    public boolean AddDepartmentToDB() {
        Trace.log(Utility.ApplicationLogger, Level.INFO, DepartmentList.class, "AddDepartmentToDB",
                  "Coming inside AddDepartmentToDB");
        boolean result = false;
        try {
            Connection conn = DBConnectionFactory.getConnection();
            conn.setAutoCommit(false);
            String insertSQL =
                "Insert into DEPARTMENTS (DEPARTMENT_ID,DEPARTMENT_NAME,MANAGER_ID,LOCATION_ID) values (?,?,?,?)";
            PreparedStatement pStmt = conn.prepareStatement(insertSQL);
            pStmt.setInt(1, editDepartment.getDeptId());
            pStmt.setString(2, editDepartment.getDeptName());
            pStmt.setInt(3, editDepartment.getMgrId());
            pStmt.setInt(4, editDepartment.getLocId());
            pStmt.execute();
            conn.commit();
            result = true;
        } catch (SQLException e) {
            System.err.println(e.getMessage());
        } catch (Exception e) {
            System.err.println(e.getMessage());
        }
        Trace.log(Utility.ApplicationLogger, Level.INFO, DepartmentList.class, "AddDepartment",
                  "Exiting AddDepartmentToDB");
        return result;
    }

    /**
     * Method will call the AddDepartmentToDB
     * Call PropertyChangeSupport listener to push data changes to the UI
     */
    public void saveDepartment() {
        Trace.log(Utility.ApplicationLogger, Level.INFO, DepartmentList.class, "saveDepartment",
                  "Coming inside saveDepartment");
        if (AddDepartmentToDB()) {
            Department newDepartment = new Department(editDepartment);
            s_departments.add(0, newDepartment);
            providerChangeSupport.fireProviderCreate("departments", newDepartment.getKey(), newDepartment);
        }
        Trace.log(Utility.ApplicationLogger, Level.INFO, DepartmentList.class, "saveDepartment",
                  "Exiting saveDepartment");
    }

    public void setEditDepartment(Department editDepartment) {
        this.editDepartment = editDepartment;
    }

    public Department getEditDepartment() {
        return editDepartment;
    }
}
Note:- Import Error might be showing in DepartmentList java, in viewController project properties->Dependencies add the ApplicationContoller.jpr

In ViewController project. Locate and expand the Application Sources folder, then expand the META-INF folder. You will see the adfmf-feature.xml file, click on the adfmf-feature.xml file to launch the Feature editor. Add a new feature by clicking the green plus sign on the Features table near top of the editor this will launch the new Create ADF Mobile Feature dialog, modify the values as shown below.


In the Features table, select the newly created feature Departments. Under the Features table, click the Content tab, and locate the Content table. Notice that the content item Departments.1 is created by default. Next add a new file by clicking the green plus sign and select taskflow option, this will launch the new Create ADF Mobile Task Flow dialog, modify the value as shown below.


Click on the DepartmentsTaskflow.xml to open the file in taskflow editor and follow the below steps.
1) Create views and name them as deptList and editDept respectively.
2) Draw the control flow case from deptList to editDept and Outcome as "add", Behavior->Transition as "flipRight".
3) Draw the control flow case from editDept to deptList and Outcome as "list", Behavior->Transition as "slideRight".

Double click on deptList view will launch Create ADF Mobile AMX Page dialog, in page facets select Header and Secondary Action. Open the deptList.amx page and go to page bindings, in Bindings section click on create control binding and select methodAction item. In create action binding wizard select DepartmentList->AddDepartment


Go to source tab and follow the below steps:
1) In Header facet, amx:outputText set the value as "Dept List"
2) In Seconday Action facet, for amx:commandButton modify the values
    text: Add, action: add, actionListener: #{bindings.AddDepartment.execute}
3) From DC palette drag and drop departments->ADF Mobile List View and select the default options

Double click on editDept view will launch Create ADF Mobile AMX Page dialog, in page facets select Header, Primary Action and Secondary Action. Open the deptList.amx page and go to page bindings, in Bindings section click on create control binding and select methodAction item. In create action binding wizard select DepartmentList->saveDepartment


Go to source tab and follow the below steps:
1) In Header facet, amx:outputText set the value as "Add Dept"
2) In Primary Action facet, for amx:commandButton modify the values
    text:Cancel, action:_back
3) In Seconday Action facet, for amx:commandButton modify the values
     text: Save, action: _back, actionListener: #{bindings.saveDepartment.execute}
4)  From DC palette drag and drop editDepartments->Form as ADF Mobile form, In Edit Form fields delete "key" value binding

Preview of the deptList.amx and editDept.amx will looks like below.


Now last configuration, in Application Resources palette expand Descriptor->ADF META-INF. You will see the adfmf-application.xml, click on the adfmf-application.xml file to launch the adfmf-application editor. In Application section for Lifecycle Event Listener set as "application.LifeCycleListenerImpl".

Make sure the below Libraries and Classpath present in the ViewController project.


In the Application menu, select Deploy - New Deployment Profile to start the Create Deployment Profile dialog box. In the Profile Type drop-down list, ensure ADF Mobile for Android/IOS is selected and then click OK.


In the ADF Mobile for Android Deployment Profile Properties dialog box, Select the Android Options node and confirm your values look like the ones below.


In the Application menu, select Deploy - New Deployment deployment profile. In the subsequent dialog box, select Deploy application to device/emulator/package, and click Finish.


Watch the Deployment Log window for any errors.