Commits

Romain Vernoux committed 85f1690 Draft

last version

Comments (0)

Files changed (8)

-<?xml version="1.0" encoding="UTF-8"?>
-<classpath>
-	<classpathentry kind="src" path="src"/>
-	<classpathentry kind="src" path="gen"/>
-	<classpathentry kind="con" path="com.android.ide.eclipse.adt.ANDROID_FRAMEWORK"/>
-	<classpathentry kind="lib" path="libs/commons-io-2.0.jar"/>
-	<classpathentry kind="output" path="bin"/>
-</classpath>
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+	<classpathentry kind="src" path="src"/>
+	<classpathentry kind="src" path="gen"/>
+	<classpathentry kind="con" path="com.android.ide.eclipse.adt.ANDROID_FRAMEWORK"/>
+	<classpathentry kind="lib" path="libs/commons-io-2.0.jar"/>
+	<classpathentry exported="true" kind="con" path="com.android.ide.eclipse.adt.LIBRARIES"/>
+	<classpathentry kind="output" path="bin/classes"/>
+</classpath>

AndroidManifest.xml

 <?xml version="1.0" encoding="utf-8"?>
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
       package="fr.vernoux.ocaml"
-      android:versionCode="1"
-      android:versionName="1.0 Release">
+      android:versionCode="2"
+      android:versionName="1.1">
     <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />   
     <application android:icon="@drawable/icon" 
     			 android:label="@string/app_name">

res/values/strings.xml

     <string name="existing_file">This file already exists. Do you want to overwrite it?</string>
     <string name="file_directory_not_found">Directory not found or empty.</string>
     <string name="data_lost">Data has been lost. Please compile again.</string>
-    <string name="toplevel_start_fail">/!\\ Fatal Error : unable to start the Toplevel /!\\\nPlease contact us to report this bug !</string>   
+    <string name="toplevel_start_fail">/!\\ Fatal Error : unable to start the Toplevel /!\\\nPlease restart the application.</string>   
     <string name='copyright'>Copyright &#169; 2011 Keigo IMAI\n
 Copyright &#169; 2011 Romain VERNOUX\n
 Copyright &#169; 1995-2011 INRIA\n\n

src/fr/vernoux/ocaml/FileOpen.java

 	
 		lv.setOnItemClickListener(new OnItemClickListener() {
 		    
-			  @Override
 			  public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
 		      Intent intent = new Intent(FileOpen.this, MainActivity.class);
 		      intent.putExtra("file", path + File.separator + liste[position]);

src/fr/vernoux/ocaml/FileOverwrite.java

 		button_yes.setOnClickListener(
 				new OnClickListener() {
 
-					@Override
 					public void onClick(View arg0) {
-						// TODO Auto-generated method stub
 							setResult(Activity.RESULT_OK);
 							FileOverwrite.this.finish();
 						}
 		button_no.setOnClickListener(
 				new OnClickListener() {
 
-					@Override
 					public void onClick(View arg0) {
-						// TODO Auto-generated method stub
 							setResult(Activity.RESULT_CANCELED);
 							FileOverwrite.this.finish();
 						}

src/fr/vernoux/ocaml/FileSave.java

 		button_save.setOnClickListener(
 				new OnClickListener() {
 
-					@Override
 					public void onClick(View arg0) {
-						// TODO Auto-generated method stub
 						String result;
 						if(!(result = fileName.getText().toString()).equals("")){
 							Intent intent = new Intent(FileSave.this, MainActivity.class);
 		button_cancel.setOnClickListener(
 			new OnClickListener() {
 
-				@Override
 				public void onClick(View arg0) {
-					// TODO Auto-generated method stub
 					setResult(Activity.RESULT_CANCELED);
 					FileSave.this.finish();
 				}

src/fr/vernoux/ocaml/MainActivity.java

  - Minor fixes
  - Release \o/
  
+ 18/12/11
+ - Saving/opening files with preserved panels
+ - Output display performance improvements
+ 
 */
 
 /*   TO DO
- 
+ TODO requestRectangle on listview?
 */
 
 
  
  Why Asynctask ?
  	-> They are running in a separate thread and permit to access datas from the UI Thread.
- 	-> We can thus easily modify (TextView outView) from another thread : cool !
+ 	-> We can thus easily modify (TextView outView) from another thread : cool!
  	-> Didn't have time to try, but it is apparently easy to keep such threads alive during change orientation.
  	
  Why a Runnable to scroll ?
 import java.io.InputStreamReader;
 import java.io.OutputStreamWriter;
 import java.io.PrintWriter;
-import fr.vernoux.ocaml.R;
+import java.util.regex.Pattern;
+
 
 
 import android.app.Activity;
 import android.os.AsyncTask;
 import android.os.Bundle;
 import android.os.Environment;
+import android.os.Handler;
 import android.text.Html;
 import android.util.Log;
 import android.view.LayoutInflater;
 	public static final String USER_START_TAG = "<b>";
 	public static final String USER_END_TAG = "</b>";
 	public static final String TAG = "ocaml-android";
+	public static final String PANEL_TAG = "(* OCAML-ANDROID-PANEL *)";
+	public static final long REFRESH_INTERVAL = 100;
 	
 	private TabHost tabHost;
-	private static TextView outView; 
-	private static String output_text = "&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"; //Static is to avoid painful java stuff on orientation changes
-	private ScrollView scrollView_toplevel;
+	private TextView outView; 
+	private static StringBuilder output_text; //Static is to avoid painful java stuff on orientation changes
+	private static ScrollView scrollView_toplevel;
 	private ScrollView scrollView_editor;
 	private PrintWriter out = null;
 	private InputStream in = null;
 	private LinearLayout editPanelView = null;
 	private int editPanel_number;
 	private String fileName = "";
+	private Handler outputUpdater = new Handler();
+	private static final Object lock = new Object();
+	private static boolean output_upToDate = true;
 	
 	
 	@Override
-	public void onCreate(Bundle savedInstanceState) {
+ 	public void onCreate(Bundle savedInstanceState) {
 		super.onCreate(savedInstanceState);
 		setContentView(R.layout.main);
 		
 		editPanelView = (LinearLayout) findViewById(R.id.editPanelView);
 		editPanel_number = 1;
 		
+		
+		
+		
 		Object[] tab = (Object[]) getLastNonConfigurationInstance();
 		if (tab == null){ // The application has just been launched by the user
+			output_text = new StringBuilder("&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;");
 			FileInputStream fis = null;;
 			InputStreamReader isr = null;;
 			BufferedReader br = null;
-			String content = "";
+			StringBuilder content = new StringBuilder();
+			addEditPanel(0);
 			try {
 				fis = openFileInput("autosave");
 				isr = new InputStreamReader(fis);
 		        br = new BufferedReader(isr);
 		        String line;
 		        while ((line = br.readLine()) != null) {
-		             content = content + line + "\n";
+		             content.append(line).append("\n");
 		        }
+		        if(content.length() != 0){
+					String[] parsed_source = parse(content.substring(0, content.length()-1));
+			        for(String panel: parsed_source){
+			        	if (!(panel = panel.trim()).equals("")){
+			        		int current = editPanel_number-1;
+			        		addEditPanel(current-1);
+			        		((EditText) findViewById(10*current + EDITTEXT)).setText(panel);	
+			        	}
+			        }
+				}
 			} catch (FileNotFoundException e) {
 				//It's okay, first launch for example. Don't care anyway.
+				addEditPanel(0);
 			} catch (IOException e) {
-				e.printStackTrace();
+				addEditPanel(0);
 			}
-			addEditPanel(0);
+			
 			
 			tabHost.setCurrentTab(1);
-			EditText et = (EditText)findViewById(10 + EDITTEXT);
-			if(content != ""){
-				et.setText(content.substring(0, content.length()-1));
-				et.selectAll();
-			}
 			
 		}
 		else { // The application just resumed from an orientation change
 		
 		println("",FROM_USER); // Causes the view to display the old text after a orientation change.
 		
+		/*
+		outputUpdater = new Timer("outputUpdater");
+		outputUpdater.schedule(new OutputUpdaterTask(), 0, 2000);
+		*/
+		
+		outputUpdater.post(outputUpdaterTask);
 		backThread = new BackThread();
 		backThread.execute();	
 		
 	
 	public void onPause(){
 		super.onPause();
+		outputUpdater.removeCallbacks(outputUpdaterTask);
 		FileOutputStream fos = null;;
 		try {
 			fos = openFileOutput("autosave", Context.MODE_PRIVATE);
 			fos.write(merge().getBytes());
 			fos.close();
 		} catch (FileNotFoundException e) {
-			// TODO Auto-generated catch block
 			e.printStackTrace();
 		} catch (IOException e) {
-			// TODO Auto-generated catch block
 			e.printStackTrace();
 		}   
 	}
+	
+	public void onResume(){
+		super.onResume();
+		outputUpdater.post(outputUpdaterTask);
+	}
 
 	@Override
 	public Object onRetainNonConfigurationInstance() {
 		compile_button.setOnClickListener(
 			new OnClickListener() {
 
-				@Override
 				public void onClick(View arg0) {
-					// TODO Auto-generated method stub
 					if(out!=null && !"".equals(((EditText) findViewById((arg0.getId()/10)*10 + EDITTEXT)).getText().toString().trim())) {
 						
 						// Hide the soft Keyboard manually
 		delete_button.setOnClickListener(
 				new OnClickListener() {
 
-					@Override
 					public void onClick(View arg0) {
-						// TODO Auto-generated method stub
 						if(editPanel_number > 2){
 							delEditPanel(arg0.getId()/10);
 						} else {
 		panel_up_button.setOnClickListener(
 				new OnClickListener() {
 
-					@Override
 					public void onClick(View arg0) {
-						// TODO Auto-generated method stub
 						addEditPanel(arg0.getId()/10-1);
 					}
 					
 		panel_down_button.setOnClickListener(
 				new OnClickListener() {
 
-					@Override
 					public void onClick(View arg0) {
-						// TODO Auto-generated method stub
 						addEditPanel(arg0.getId()/10);
 					}
 					
 	}
 	
 	public void println(String line, int origin) { 
-		//Log.d(TAG, "console output: "+line);
-		String line2 = line.replace("<", "&lt;").replace("\n", "<br />");
-		if(origin == FROM_USER){
-			output_text = output_text.concat(USER_START_TAG).concat(line2).concat(USER_END_TAG);
-		}
-		else {
-			output_text = output_text.concat(TOPLEVEL_START_TAG).concat(line2).concat(TOPLEVEL_END_TAG);
-		}
-		outView.setText(Html.fromHtml(output_text));
-		scrollView_toplevel.post(new Runnable() { 
-		    public void run() { 
-		        scrollView_toplevel.clearAnimation();
-		        scrollView_toplevel.smoothScrollTo(0, outView.getHeight()); 
-		    } 
-		}); 
+		synchronized(lock){
+			String line2 = line.replace("<", "&lt;").replace("\n", "<br />");
+			if(origin == FROM_USER){
+				output_text.append(USER_START_TAG).append(line2).append(USER_END_TAG);
+			}
+			else {
+				output_text.append(TOPLEVEL_START_TAG).append(line2).append(TOPLEVEL_END_TAG);
+			}
+			output_upToDate = false;
+		}		
 	}
-
+	
 	private void clear() { 
 		Log.d(TAG, "console clear");
-		output_text = "";
+		output_text.delete(0, output_text.length());
 		println("# ", FROM_TOPLEVEL);
 		tabHost.setCurrentTab(1);
 		Toast.makeText(MainActivity.this, R.string.clear_toast, Toast.LENGTH_SHORT).show();
 	}
 	
-	private String merge(){
-		String content = "";
+ 	private String merge(){
+		StringBuilder content = new StringBuilder();
 		for(int k = 1; k < editPanel_number; k++){
-			content = content.concat(((EditText) findViewById(10*k+ EDITTEXT)).getText().toString()).concat("\n\n");
+			content.append(PANEL_TAG)
+				.append("\n")
+				.append(((EditText) findViewById(10*k+ EDITTEXT)).getText().toString())
+				.append("\n\n");
 		}
-		content = content.substring(0, content.length()-2);
-		return content;
+		return(content.substring(0, content.length()-2));
+	}
+	
+	private String[] parse(String text){
+		return(text.split(Pattern.quote(PANEL_TAG)));
 	}
 	
 	private void saveFile(String name, boolean overwrite) {
 	    		Intent intent = new Intent(MainActivity.this, FileOverwrite.class);
 	    		startActivityForResult(intent, OVERWRITE);
 	    	} else {
-			try{ 
-	           fOut = new FileOutputStream(file);       
-	            osw = new OutputStreamWriter(fOut); 
-	            osw.write(merge()); 
-                Toast.makeText(MainActivity.this, R.string.file_external_toast, Toast.LENGTH_LONG).show();
+				try{ 
+		           fOut = new FileOutputStream(file);       
+		            osw = new OutputStreamWriter(fOut); 
+		            osw.write(merge()); 
+	                Toast.makeText(MainActivity.this, R.string.file_external_toast, Toast.LENGTH_LONG).show();
 	            } catch (Exception e) {       
 	            	Toast.makeText(MainActivity.this, R.string.file_write_fail, Toast.LENGTH_LONG).show();
 	            } finally {
 	            	try {
 						osw.close();
 					} catch (IOException e) {
-						// TODO Auto-generated catch block
 						e.printStackTrace();
 					} 
 	            }
 		FileInputStream fis = null; 
         InputStreamReader isr = null;
         BufferedReader br = null;
-        String content = "";
+        StringBuilder content = new StringBuilder();
 	   	try{ 
            fis = new FileInputStream(name);  
            isr = new InputStreamReader(fis);
            br = new BufferedReader(isr);
            String line;
            while ((line = br.readLine()) != null) {
-                content = content + line + "\n";
+                content.append(line).append("\n");
               }
           
           } catch (Exception e) {
         	  try {
 					br.close();
 				} catch (IOException e) {
-					// TODO Auto-generated catch block
 					e.printStackTrace();
 				}
           }
         Log.d(TAG,"Sources imported from " + name + ".");
-	    int current = editPanel_number;
-		addEditPanel(current-1);
-		((EditText) findViewById(10*current + EDITTEXT)).setText(content.substring(0, content.length()-1));		
+        String[] parsed_source = parse(content.substring(0, content.length()-1));
+		int last_panel_empty = (((EditText) findViewById(10*(editPanel_number-1) + EDITTEXT)).getText().toString().equals("")?1:0); 
+        for(String panel: parsed_source){
+        	if (!(panel = panel.trim()).equals("")){
+        		int current = editPanel_number - last_panel_empty;
+        		addEditPanel(current-1);
+        		((EditText) findViewById(10*current + EDITTEXT)).setText(panel);	
+        	}
+        }
 	}
 	
 	private void menuSave(){
 				fos.write(merge().getBytes());
 				fos.close();
 			} catch (FileNotFoundException e) {
-				// TODO Auto-generated catch block
 				e.printStackTrace();
 			} catch (IOException e) {
-				// TODO Auto-generated catch block
 				e.printStackTrace();
 			}
 			android.os.Process.killProcess(android.os.Process.myPid());
 		}
 	}
 	
-	private class BackThread extends AsyncTask<Void,String,Void> { // NEW
-				
+	private Runnable outputUpdaterTask = new Runnable() {
+
+		public void run() {
+			// takes the lock, updates the outview, and says the output is up-to-date
+			synchronized(lock){
+				if(!output_upToDate){
+					outView.setText(Html.fromHtml(output_text.toString()));
+					//outView.postInvalidate();
+			        /*scrollView_toplevel.clearAnimation();
+			        scrollView_toplevel.smoothScrollTo(0, outView.getHeight()); */
+					scrollView_toplevel.requ
+					output_upToDate = true;
+					Log.v(TAG, "bip!");
+				}
+			}
+			outputUpdater.postDelayed(this, REFRESH_INTERVAL);
+		}
+
+	};
+	
+	private class BackThread extends AsyncTask<Void,String,Void> { 
 				
 		@Override
 		protected void onPreExecute(){
 					try {
 						toplevel = new OcamlTop(MainActivity.this);
 					} catch (Exception e) {
-						// TODO Auto-generated catch block
-						output_text="";
+						output_text.delete(0, output_text.length());
 						println(getString(R.string.toplevel_start_fail),FROM_TOPLEVEL);
 					}
 					Log.d(TAG,"New Toplevel created");
 					toplevel = (OcamlTop) tab[0];
 					Log.d(TAG,"Old Toplevel found");
 				}
-				in = toplevel.stream.getInputStream();
+				if (toplevel == null || toplevel.stream == null){
+					println(getString(R.string.toplevel_start_fail),FROM_TOPLEVEL);
+					Log.e(TAG,"Unable to start the toplevel");
+				} else {
+					in = toplevel.stream.getInputStream();
+					out = new PrintWriter(new OutputStreamWriter(
+							toplevel.stream.getOutputStream(), "utf-8"));
+				}
 				
-				out = new PrintWriter(new OutputStreamWriter(
-						toplevel.stream.getOutputStream(), "utf-8"));
+				
+
+				
 			} catch (IOException e) {
 				Log.e(TAG, "error in main", e);
 			}
 		
 		@Override
 		protected Void doInBackground(Void... params) {
-			// TODO Auto-generated method stub
-			try {
-				while (true) {
-					int i = in.read();
-					if(i==-1) {
-						break;
+			if (in == null){
+				output_text.delete(0, output_text.length());
+				println(getString(R.string.toplevel_start_fail),FROM_TOPLEVEL);
+			} else {
+			
+				try {
+					while (true) {
+						int i = in.read();
+						if(i==-1) {
+							break;
+						}
+						int len = in.available();
+						byte[] b = new byte[len+1];
+						b[0] = (byte)(i <= 127 ? i : i - 256);
+						in.read(b, 1, len);
+						publishProgress(new String(b,"utf-8"));
+											 
 					}
-					int len = in.available();
-					byte[] b = new byte[len+1];
-					b[0] = (byte)(i <= 127 ? i : i - 256);
-					in.read(b, 1, len);
-					publishProgress(new String(b,"utf-8"));
-										 
+				} catch (IOException e) {	
+					Log.e(TAG, "Datas has been lost", e); 
+					Toast.makeText(MainActivity.this, R.string.data_lost, Toast.LENGTH_LONG).show();
 				}
-			} catch (IOException e) {	
-				Log.e(TAG, "Datas has been lost", e); 
-				Toast.makeText(MainActivity.this, R.string.data_lost, Toast.LENGTH_LONG).show();
 			}
-			
 			return null;  
 		}
 			
 		@Override
 		protected void onProgressUpdate(String... strings){
 			println(strings[0],FROM_TOPLEVEL);
+			Log.v(TAG,strings[0]);
 		}
 	}
 	

src/fr/vernoux/ocaml/OcamlTop.java

 		try {
 			prepareStdlib(context);
 		} catch (Exception e) {
-			// TODO Auto-generated catch block
 			throw(e);
 		}
 		Native.chdir(context.getCacheDir().getAbsolutePath());