Commits

Clayton Sims committed 3d1fc61

Fixed default user reg password requirement, and fixed some outstanding issues with how SMS messages are encoded and split up for non ASCII text

Comments (0)

Files changed (8)

core/src/org/javarosa/model/xform/SMSSerializingVisitor.java

 		}
 		model.accept(this);
 		if (theSmsStr != null) {
-			return theSmsStr.getBytes("UTF-8");
+			//Encode in UTF-16 by default, since it's the default for complex messages
+			return theSmsStr.getBytes("UTF-16BE");
 		} else {
 			return null;
 		}
 		}
 		model.accept(this);
 		if (theSmsStr != null) {
-			byte[] form = theSmsStr.getBytes("UTF-8");
+			byte[] form = theSmsStr.getBytes("UTF-16");
 			return new ByteArrayPayload(form, null, IDataPayload.PAYLOAD_TYPE_SMS);
 		} else {
 			return null;

j2me/communication/src/org/javarosa/services/transport/SubmissionTransportHelper.java

 			//URL
 			String phoneUri = profile.getAction();
 		
-			String payload = new String(new SMSSerializingVisitor().serializeInstance(instance, profile.getRef()));
-			return new org.javarosa.services.transport.impl.sms.SMSTransportMessage(payload,phoneUri);
+			byte[] data = new SMSSerializingVisitor().serializeInstance(instance, profile.getRef());
+			
+			String payload = new String(data,"UTF-16BE");
+			
+			return new org.javarosa.services.transport.impl.sms.SMSTransportMessage(payload,phoneUri);	
 			
 			//#else
 			//# throw new RuntimeException("SMS Messages not enabled on current device"); 

j2me/communication/src/org/javarosa/services/transport/impl/binarysms/BinarySMSTransportMessage.java

 import java.io.DataInputStream;
 import java.io.DataOutputStream;
 import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+import java.util.Vector;
 
 import javax.microedition.io.Connector;
 import javax.wireless.messaging.BinaryMessage;
  * 
  */
 public class BinarySMSTransportMessage extends BasicTransportMessage {
-	
-	byte[] content;
+		
+	byte[] raw;
+	Vector<byte[]> content;
 
 	/**
 	 * 
 	public BinarySMSTransportMessage() {
 		//ONLY FOR DESERIALIZING
 	}
+	
+	/**
+	 * @param str
+	 * @param destinationURL
+	 * @throws UnsupportedEncodingException 
+	 */
+	public BinarySMSTransportMessage(String str, String destinationURL) throws UnsupportedEncodingException {
+		this.destinationURL = destinationURL;
+		setContent(str.getBytes("UTF-16"));
+	}
+	
 	/**
 	 * @param str
 	 * @param destinationURL
 	 */
 	public BinarySMSTransportMessage(byte[] bytes, String destinationURL) {
 		this.destinationURL = destinationURL;
-		this.content = bytes;
+		setContent(bytes);
+	}
+	
+	private void setContent(byte[] bytes) {
+		raw = bytes;
+		content = new Vector<byte[]>();
+		for(int i = 0 ; i <= bytes.length / 140; ++i) {
+			int chunk = 140;
+			if(i == bytes.length / 140) {
+				//Last chunk, doesn't need to be as long 
+				chunk = bytes.length % 140;
+			}
+			if(chunk == 0) {
+				continue;
+			}
+			
+			byte[] message = new byte[chunk];
+			
+			for(int j = 0 ; j < message.length ; ++j) {
+				message[j] = bytes[i*140 + j]; 
+			}
+			content.addElement(message);
+		}
 	}
 
 	public boolean isCacheable() {
 
 			// create a MessageConnection
 			conn = getConnection(this.getDestinationURL());
-			sendMessage((byte[]) this.getContent(), conn);
+			Vector<byte[]> data = (Vector<byte[]>)this.getContent();
+			for(byte[] messageContent : data) {
+				sendMessage(messageContent, conn);
+			}
 
 		} catch (Exception e) {
 			System.out.println("Connection failed: ");
 			throws IOException, DeserializationException {
 		super.readExternal(in, pf);
 		destinationURL = ExtUtil.readString(in);
-		content = ExtUtil.readBytes(in);
+		setContent(ExtUtil.readBytes(in));
 	}
 
 	public void writeExternal(DataOutputStream out) throws IOException {
 		super.writeExternal(out);
 		ExtUtil.writeString(out, destinationURL);
-		ExtUtil.writeBytes(out, content);
+		ExtUtil.writeBytes(out, raw);
 	}
 
 }

j2me/communication/src/org/javarosa/services/transport/impl/sms/SMSTransportMessage.java

 import java.io.DataInputStream;
 import java.io.DataOutputStream;
 import java.io.IOException;
+import java.io.UnsupportedEncodingException;
 import java.util.Vector;
 
 import javax.microedition.io.Connector;
 public class SMSTransportMessage extends BasicTransportMessage {
 
 	/**
-	 * SMS messages can be no longer than 140 characters in length
+	 * SMS messages can be no longer than 160 bytes in length
 	 */
-	public final static int MAX_SMS_LENGTH = 140;
+	public final static int MAX_SMS_BYTES = 140;
 
 	/**
 	 * 
 	/**
 	 * @param str
 	 * @param destinationURL
+	 * @throws UnsupportedEncodingException 
 	 */
-	public SMSTransportMessage(String str, String destinationURL) {
+	public SMSTransportMessage(String payload, String destinationURL) throws UnsupportedEncodingException {
 		this.destinationURL = destinationURL;
-		content = splitSMS(str);
+		
+		//Try to convert the string to ascii by outputting UTF-8 and re-importing it. Incompatible
+		//(non-7-bit) characters will be read in as extra chars
+		String asciiString = new String(payload.getBytes("UTF-8"),"ASCII");
+		//A UTF-16 string should always represent the string properly, so it's our gold standard
+		String unicodeString = new String(payload.getBytes("UTF-16BE"),"UTF-16BE");
+		
+		if(asciiString.length() != unicodeString.length()) {
+			content = splitSMS(payload, "UTF-16BE");
+		} else {			
+			content = splitSMS(payload, "ASCII");
+		}
 	}
-
+	
 	public boolean isCacheable() {
 		return false;
 	}
 
 	/**
 	 * 
-	 * SMS can be of maximum 140 characters in length.
-	 * 
+	 * SMS messages must be 160 bytes or less each.
+	 *  
 	 * If the message to be sent is greater, it is partitioned.
 	 * 
 	 * @param str
 	 * @return Vector of strings to be sent as separate messages
+	 * @throws UnsupportedEncodingException 
 	 */
-	private Vector splitSMS(String str) {
-		String message = str;
+	private Vector splitSMS(String str, String encoding) throws UnsupportedEncodingException {
 		Vector v = new Vector();
+		
+		String currentMessage = "";
 
-		// if message is too long split it
-		while (message.length() > MAX_SMS_LENGTH) {
-			String part = message.substring(0, MAX_SMS_LENGTH);
-			v.addElement(part);
-			message = message.substring(MAX_SMS_LENGTH + 1);
+		//TODO: This might take a _looooooooooooong_ time if our message is huge. Test.
+		
+		// Go through one char at a time building output strings in
+		// the given encoding
+		for(int i = 0 ; i < str.length() ; ++i) {
+			if((currentMessage + str.charAt(i)).getBytes(encoding).length <= MAX_SMS_BYTES) {
+				currentMessage += str.charAt(i);
+			} else {
+				v.addElement(currentMessage);
+				currentMessage = "";
+				currentMessage += str.charAt(i);
+			}
 		}
-
-		// whatever remaining of the message after
-		// chopping out 140 character length chunks
-		// must also be added
-		if (message.length() > 0)
-			v.addElement(message);
+		
+		v.addElement(currentMessage);
+		
+		
 		return v;
 	}
 
 		this.destinationURL = destinationURL;
 	}
 	
-	
-	
-	
-	
-	
 	public void send() {
 		MessageConnection conn = null;
 		try {

j2me/javarosa-app/src/org/javarosa/demo/applogic/JRDemoContext.java

 import javax.microedition.midlet.MIDlet;
 
 import org.javarosa.core.model.CoreModelModule;
+import org.javarosa.core.model.FormDef;
 import org.javarosa.core.model.SubmissionProfile;
 import org.javarosa.core.model.condition.IFunctionHandler;
 import org.javarosa.core.model.instance.FormInstance;
 import org.javarosa.core.services.PropertyManager;
 import org.javarosa.core.services.locale.Localization;
 import org.javarosa.core.services.properties.JavaRosaPropertyRules;
+import org.javarosa.core.services.storage.StorageFullException;
+import org.javarosa.core.services.storage.StorageManager;
 import org.javarosa.core.util.JavaRosaCoreModule;
 import org.javarosa.core.util.PropertyUtils;
 import org.javarosa.demo.properties.DemoAppProperties;
 import org.javarosa.user.activity.UserModule;
 import org.javarosa.user.model.User;
 import org.javarosa.user.utility.UserUtility;
+import org.javarosa.xform.util.XFormUtils;
 
 public class JRDemoContext {
 
 			
 		UserUtility.populateAdminUser(m);
 		loadRootTranslator();
+		
+		try {
+			StorageManager.getStorage(FormDef.STORAGE_KEY).write(XFormUtils.getFormFromResource("/smstest.xhtml"));
+		} catch (StorageFullException e) {
+			// TODO Auto-generated catch block
+			e.printStackTrace();
+		}
 	}	
 
 	private void loadModules() {

j2me/objects/org.javarosa.cases/src/org/javarosa/cases/api/CaseManagementController.java

-/**
- * 
- */
-package org.javarosa.cases.api;
-
-import java.util.Hashtable;
-import java.util.Vector;
-
-import javax.microedition.lcdui.Command;
-import javax.microedition.lcdui.Displayable;
-import javax.microedition.lcdui.List;
-
-import org.javarosa.cases.api.transitions.CaseManagementTransitions;
-import org.javarosa.cases.util.ICaseType;
-import org.javarosa.cases.view.CaseManagementScreen;
-import org.javarosa.chsreferral.util.PatientReferralUtil;
-import org.javarosa.core.services.locale.Localization;
-import org.javarosa.j2me.log.CrashHandler;
-import org.javarosa.j2me.log.HandledCommandListener;
-import org.javarosa.j2me.view.J2MEDisplay;
-
-/**
- * @author ctsims
- *
- */
-public class CaseManagementController implements HandledCommandListener {
-    public static final String NEW = "menu.NewCase";
-    public static final String FOLLOWUP = "menu.FollowUp";
-    public static final String REFERRAL = "menu.Referral";
-    public static final String VIEW_OPEN = "menu.ViewOpen";
-    public static final String RESOLVE = "menu.Resolve";
-    
-    ICaseType type;
-	CaseManagementScreen view;
-	CaseManagementTransitions transitions;
-
-	Vector indexMapping = new Vector();
-	
-	public CaseManagementController(ICaseType type) {
-		this.type = type;
-		
-		view = new CaseManagementScreen(Localization.get("case.title"));
-		configView();
-		view.setCommandListener(this);
-	}
-	
-	public void setTransitions (CaseManagementTransitions transitions) {
-		this.transitions = transitions;
-	}
-
-	public void start() {
-		J2MEDisplay.setView(view);
-	}
-	
-	private void configView() {
-		int[] order = type.getActionListing();
-		if (order != null) {
-			for (int i = 0; i < order.length; i++) {
-				addOption(order[i]);
-			}
-		} else {
-			addOption(ICaseType.ACTION_NEW);
-			addOption(ICaseType.ACTION_FOLLOWUP);
-			addOption(ICaseType.ACTION_REFERRALS);
-			addOption(ICaseType.ACTION_CLOSE);
-			addOption(ICaseType.ACTION_BROWSE);		
-		}
-	}
-
-	private String captionForAction (int action) {
-		Hashtable capov = type.getCaptionOverrides();
-		String locKey = (String)capov.get(new Integer(action));
-		if (locKey == null) {		
-			switch (action) {
-			case ICaseType.ACTION_NEW: locKey = NEW; break;
-			case ICaseType.ACTION_FOLLOWUP: locKey = FOLLOWUP; break;
-			case ICaseType.ACTION_REFERRALS: locKey = REFERRAL; break;
-			case ICaseType.ACTION_CLOSE: locKey = RESOLVE; break;
-			case ICaseType.ACTION_BROWSE: locKey = VIEW_OPEN; break;
-			}
-		}
-
-		String caption;
-		if (action == ICaseType.ACTION_REFERRALS) {
-			caption = Localization.get(locKey, new String[] {String.valueOf(PatientReferralUtil.getNumberOfOpenReferralsByType(null))});			
-		} else {
-			caption = Localization.get(locKey);
-		}
-		return caption;
-	}
-	
-	private void addOption (int action) {
-		String caption = captionForAction(action);
-		indexMapping.addElement(new Integer(action));
-		view.append(caption, null);
-	}
-
-	public void commandAction(Command c, Displayable d) {
-		CrashHandler.commandAction(this, c, d);
-	}  
-
-	public void _commandAction(Command c, Displayable d) {
-		if(c.equals(CaseManagementScreen.SELECT) || c.equals(List.SELECT_COMMAND)) {
-			int action = ((Integer)indexMapping.elementAt(view.getSelectedIndex())).intValue();
-			
-			switch(action) {
-				case ICaseType.ACTION_NEW:
-					transitions.newCase();
-					break;
-				case ICaseType.ACTION_FOLLOWUP:
-					transitions.followUpOnCase();
-					break;
-				case ICaseType.ACTION_REFERRALS:
-					transitions.viewReferrals();
-					break;
-				case ICaseType.ACTION_CLOSE:
-					transitions.closeCase();
-					break;
-				case ICaseType.ACTION_BROWSE:
-					transitions.viewOpen();
-					break;
-			}
-		}
-		else if(c.equals(CaseManagementScreen.BACK)) {
-			transitions.exit();
-		}
-	}
-
-
-}

j2me/objects/org.javarosa.cases/src/org/javarosa/cases/api/CaseManagementState.java

-package org.javarosa.cases.api;
-
-import org.javarosa.cases.api.transitions.CaseManagementTransitions;
-import org.javarosa.cases.util.ICaseType;
-import org.javarosa.core.api.State;
-
-public abstract class CaseManagementState implements CaseManagementTransitions, State {
-	protected ICaseType caseType;
-	
-	public CaseManagementState (ICaseType caseType) {
-		this.caseType = caseType;
-	}
-	
-	public void start () {
-		CaseManagementController controller = getController();
-		controller.setTransitions(this);
-		controller.start();
-	}
-	
-	protected CaseManagementController getController () {
-		return new CaseManagementController(caseType);
-	}
-}

j2me/shared-resources/resources/register_user.xhtml

 			<bind nodeset="registration/date" type="xsd:dateTime" jr:preload="timestamp" jr:preloadParams="start"/>
 			<bind nodeset="registration/registering_phone_id" type="xsd:string" jr:preload="property" jr:preloadParams="DeviceID" />
 			
-			<bind nodeset="confirmpass" type="xsd:int" jr:preload="user" jr:preloadParams="password" jr:constraint="/user_registration/registration/password = ." jr:constraintMsg="Passwords do not match!"/>
+			<bind nodeset="confirmpass" required="true()" type="xsd:int" jr:preload="user" jr:preloadParams="password" jr:constraint="/user_registration/registration/password = ." jr:constraintMsg="Passwords do not match!"/>
 			<bind nodeset="loadedguid" type="xsd:string" jr:preload="user" jr:preloadParams="uuid" />
 			<bind nodeset="freshguid" type="xsd:string" jr:preload="uid" jr:preloadParams="general" />