Monday, February 11, 2013

ADF Mobile Email - Send Multiple File Attachments with amx:selectManyCheckbox option

Here's a use case: A presenter is showing presentation on their Android/iPhone/iPad devices to the client. After the presentation, client may asks for the presentation files for there further understanding. Now presenter wants to attach all the files used in presentation and send mail to the client.

Application screen looks like below when it deployed and run on Android Device/Emulator. Displays the Employees List in first screen.


Clicking on any employee will leads to the Compose screen, where the selected employee email will be pre-populated in "To" field.


Enter the subject and message details need to be send. Click on "Attach a file" button, a popup window will open and files under the "/mnt/sdcard/Download" folder will be displayed. Now select the files that as to be attached and click on Ok button.


Now notice the attached files will be displayed in-front of  Attached Files. Next clicking on Send Email button will take you to the next screen where all the email clients installed in the device will be listed, select any client and send the email.


Tested in Android device, I'm able to send email with multiple attachments. You can download the sample workspace from here.

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 ViewController project, locate and expand the Application Sources folder, create a Employee.java file and add the below code.
public class Employee {
    protected int id;
    protected String firstName;
    protected String lastName;
    protected String email;
    private transient PropertyChangeSupport propertyChangeSupport = new PropertyChangeSupport(this);

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

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

    public Employee() {
        super();
    }

    public Employee(int id, String firstName, String lastName, String email) {
        super();
        this.id = id;
        this.firstName = firstName;
        this.lastName = lastName;
        this.email = email;
    }

    public void setId(int id) {
        int oldId = this.id;
        this.id = id;
        propertyChangeSupport.firePropertyChange("id", oldId, id);
    }

    public int getId() {
        return id;
    }

    public void setFirstName(String firstName) {
        String oldFirstName = this.firstName;
        this.firstName = firstName;
        propertyChangeSupport.firePropertyChange("first", oldFirstName, firstName);
    }

    public String getFirstName() {
        return firstName;
    }

    public void setLastName(String lastName) {
        String oldLastName = this.lastName;
        this.lastName = lastName;
        propertyChangeSupport.firePropertyChange("last", oldLastName, lastName);
    }

    public String getLastName() {
        return lastName;
    }

    public void setEmail(String email) {
        String oldEmail = this.email;
        this.email = email;
        propertyChangeSupport.firePropertyChange("email", oldEmail, email);
    }

    public String getEmail() {
        return email;
    }
}
Create EmployeeListDC.java file and add the below code, create DataControl based on EmployeeListDC.java file.
public class EmployeeListDC {
    private static List s_employees = null;
    private transient ProviderChangeSupport providerChangeSupport = new ProviderChangeSupport(this);


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

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

    public EmployeeListDC() {
        super();
        if (s_employees == null) {
            s_employees = new ArrayList();
            s_employees.add(new Employee(1, "Praveen", "Thulasiraman", "praveen.thulasiraman@xyz.com"));
            s_employees.add(new Employee(2, "Arul", "wilson", "arul.wilson@xyz.com"));
            s_employees.add(new Employee(3, "Deepak", "Siddappa", "deepak.siddappa@xyz.com"));
            s_employees.add(new Employee(4, "Arun", "Sridhran", "arun.x.sridharan@xyz.com"));
        }
    }

    public Employee[] getEmployees() {
        Employee e[] = null;
        e = (Employee[])s_employees.toArray(new Employee[s_employees.size()]);
        return e;
    }
}
Create FileAttachment.java file and add below code.
public class FileAttachment {
    private int fileIndex;
    private String fileName;
    private String filePath;
    private transient PropertyChangeSupport propertyChangeSupport = new PropertyChangeSupport(this);

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

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

    public FileAttachment() {
        super();
    }

    public FileAttachment(int fileIndex, String fileName, String filePath) {
        super();
        this.fileName = fileName;
        this.filePath = filePath;
        this.fileIndex = fileIndex;
    }

    public void setFileIndex(int fileIndex) {
        int oldFileIndex = this.fileIndex;
        this.fileIndex = fileIndex;
        propertyChangeSupport.firePropertyChange("fileIndex", oldFileIndex, fileIndex);
    }

    public int getFileIndex() {
        return fileIndex;
    }

    public void setFileName(String fileName) {
        String oldFileName = this.fileName;
        this.fileName = fileName;
        propertyChangeSupport.firePropertyChange("fileName", oldFileName, fileName);
    }

    public String getFileName() {
        return fileName;
    }

    public void setFilePath(String filePath) {
        String oldFilePath = this.filePath;
        this.filePath = filePath;
        propertyChangeSupport.firePropertyChange("fileName", oldFilePath, filePath);
    }

    public String getFilePath() {
        return filePath;
    }
}
Create FileAttachmentListDC.java file and add the below code, create DataControl based on FileAttachmentListDC.java file.
public class FileAttachmentListDC {
    private static List s_FileAttachment = null;
    private transient ProviderChangeSupport providerChangeSupport = new ProviderChangeSupport(this);

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

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

    public FileAttachmentListDC() {
        super();
        if (s_FileAttachment == null) {
            s_FileAttachment = new ArrayList();
            executeReadDir();
        }
    }

    public void executeReadDir() {
        //  String dirName = AdfmfJavaUtilities.getDirectoryPathRoot(AdfmfJavaUtilities.DeviceOnlyDirectory);
        //Hard Coding the Folder path
        String dirName = "/mnt/sdcard/Download";
        try {
            showDir(new File(dirName));
            providerChangeSupport.fireProviderRefresh("FileAttachment");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * Recursively read the dir and get the file list
     * @param file
     * @throws IOException
     */
    static void showDir(File file) throws IOException {
        int indexCounter = 0;
        if (file.isDirectory()) {
            File[] files = file.listFiles();
            for (int i = 0; i < files.length; i++)
                showDir(files[i]);
        } else {
            s_FileAttachment.add(new FileAttachment(indexCounter, file.getName(), file.getPath()));
            indexCounter++;
        }
    }

    public FileAttachment[] getFileAttachment() {
        FileAttachment a[] = null;
        a = (FileAttachment[])s_FileAttachment.toArray(new FileAttachment[s_FileAttachment.size()]);
        return a;
    }

    /**
     * Clear the PageScopeVariables
     */
    public void clearScopeVariables() {
        ValueExpression ve1 = AdfmfJavaUtilities.getValueExpression("#{pageFlowScope.filePaths}", String.class);
        ve1.setValue(AdfmfJavaUtilities.getAdfELContext(), "");

        ValueExpression ve2 = AdfmfJavaUtilities.getValueExpression("#{pageFlowScope.fileNames}", String.class);
        ve2.setValue(AdfmfJavaUtilities.getAdfELContext(), "");
    }
}
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 Email. Under the Features table, click the Content tab, and locate the Content table. Notice that the content item Email.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 EmailTaskflow.xml to open the file in taskflow editor and follow the below steps.
1) Create two views and name them as EmpList and Email respectively.
2) Draw the control flow case from EmpList to Email and Outcome as "composeEmail", Behavior->Transition as "flipRight".
3) Draw the control flow case from Email to EmpList and Outcome as "empList", Behavior->Transition as "slideLeft".

Double click on EmpList view will launch Create ADF Mobile AMX Page dialog. Open the EmpList.amx page and to source tab and follow the below steps:
1) In Header facet, amx:outputText set the value as "Employee List"
2) From DC palette drag and drop EmployeeListDC->employees->ADF Mobile List View and select the default options
3) From Component palette drag and drop amx:setPropertyListener into amx:listItem and modify the values as shown in below code.
<amx:listView var="row" value="#{bindings.employees.collectionModel}"
			  fetchSize="#{bindings.employees.rangeSize}" id="lv1">
	<amx:listItem id="li1" action="composeEmail">
		<amx:outputText value="#{row.firstName} #{row.lastName}" id="ot2"/>
		<amx:setPropertyListener from="#{row.email}" to="#{pageFlowScope.email}" type="action"/>
	</amx:listItem>
</amx:listView>
Double click on Email view will launch Create ADF Mobile AMX Page dialog, in page facets select Header. Open the Email.amx page and to source tab and follow the below steps:

1) In Header facet, amx:outputText set the value as "Compose"
2) From DC palette drag and drop DeviceFeatures->sendEmail->ADF Mobile Parameter Form
3) Drop the command button and showPopupBehavior on the button
3) From Component palette drag and drop Popup component on the Panel Page
4) Inside the Popup, drop FileAttachmentListDC->fileAttachment->MultipleSelection as ADF Mobile    Select Many Checkbox and in Edit List binding dialog select values as below
    Multi Select Base Attribute : fileName
    Multi Select Display Attribute : filePath
5) Next drop a command button inside the popup window and closePopupBehavior onto the command button.
6) Create the valueChangeListener for amx:selectManyCheckbox
<amx:panelPage id="pp1">
	<amx:facet name="header">
		<amx:outputText value="Compose" id="ot1"/>
	</amx:facet>
	<amx:panelFormLayout id="pfl1" labelPosition="topStart" fieldHalign="start">
		<amx:inputText value="#{pageFlowScope.email}" label="To" id="it4" readOnly="true"/>
		<amx:inputText value="#{bindings.subject.inputValue}" label="Subject" id="it1"/>
		<amx:inputText value="#{bindings.body.inputValue}" label="Body" id="it5"/>
		<amx:inputText value="Attached Files: #{pageFlowScope.fileNames}" label="" id="it3" readOnly="true"/>
	</amx:panelFormLayout>
	<amx:commandButton text="Attach a file" id="cb3" styleClass="adfmf-commandButton-rounded">
		<amx:showPopupBehavior popupId="AttachmentPopup" type="action" align="overlapBottom" alignId="pp1"/>
	</amx:commandButton>
	<amx:commandButton actionListener="#{bindings.sendEmail.execute}" text="Send Email"
					   disabled="#{!bindings.sendEmail.enabled}" id="cb1" action="empList"
					   styleClass="adfmf-commandButton-rounded"/>
</amx:panelPage>
<amx:popup id="AttachmentPopup" animation="slideUp">
	<amx:selectManyCheckbox value="#{bindings.fileAttachment.inputValue}" id="smc1"
							valueChangeListener="#{pageFlowScope.EmailBean.fetchSelectedFileNames}">
		<amx:selectItems value="#{bindings.fileAttachment.items}"/>
	</amx:selectManyCheckbox>
	<amx:commandButton text="Ok" id="cb2" styleClass="adfmf-commandButton-rounded ">
		<amx:closePopupBehavior popupId="AttachmentPopup" type="action"/>
	</amx:commandButton>
</amx:popup>
7) Create a pageFlowScope managed bean and copy the below method code.
/**
 * Get the selected filePaths
 * @param valueChangeEvent
 */
public void fetchSelectedFileNames(ValueChangeEvent valueChangeEvent) {
	Object[] objArr = (Object[])valueChangeEvent.getNewValue();

	ValueExpression ve =
		AdfmfJavaUtilities.getValueExpression("#{bindings.fileAttachment1.collectionModel}", AmxCollectionModel.class);
	AmxCollectionModel model = (AmxCollectionModel)ve.getValue(AdfmfJavaUtilities.getAdfELContext());

	StringBuffer sbFilePath = new StringBuffer();
	StringBuffer sbFileName = new StringBuffer();
	for (int x = 0; x < objArr.length; x++) {
		Object obj = objArr[x];

		Map provider = (Map)model.getProviders().get(obj);
		String filePathVal = provider.get("filePath").toString();
		sbFilePath.append("," + filePathVal.toString());

		String fileNameval = provider.get("fileName").toString();
		sbFileName.append("," + fileNameval.toString());
	}

	ValueExpression ve1 = AdfmfJavaUtilities.getValueExpression("#{pageFlowScope.filePaths}", String.class);
	ve1.setValue(AdfmfJavaUtilities.getAdfELContext(), sbFilePath.substring(1));

	ValueExpression ve2 = AdfmfJavaUtilities.getValueExpression("#{pageFlowScope.fileNames}", String.class);
	ve2.setValue(AdfmfJavaUtilities.getAdfELContext(), sbFileName.substring(1));
}
8) Go to the bindings tab and modify the values as below for sendEmail Method


9) Create a tree binding as shown below.


10) Create a Action binding


11) Click on Create Executable binding and select Invoke action and follow as shown in below image.


Edit clearVars invoke action and set the Refresh to always, so when ever page loads clearScopeVariables method will get executed to clear all the pageFlowScope variable values.

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. Next select Deploy - New Deployment deployment profile. In the subsequent dialog box, select Deploy application to device/emulator/package, and click Finish.

2 comments:

  1. Thanks for sharing information.
    recharge plans
    Videocon Telecom comes up with LOWEST & UNMATCHED TARIFF in Haryana; Offers ALL LOCAL CALLS at only 25P/min.

    ReplyDelete
  2. Thanks for sharing information.
    recharge plans
    Videocon Telecom comes up with LOWEST & UNMATCHED TARIFF in Haryana; Offers ALL LOCAL CALLS at only 25P/min.

    ReplyDelete