Challenge solution


#1

So this took me a while to figure out. I’m sure this could be simplified somewhat but it seems to work. It could also be expanded upon to maybe put dialogs up to ask you whether you want to save to internal/external storage and maybe whether you want to load from internal/external storage. As is it tries to save and load from external storage before falling back to internal storage.
CriminalIntentJSONSerializer.java

[code]package com.bignerdranch.android.criminalintent;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.util.ArrayList;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONTokener;

import android.content.Context;
import android.util.Log;

public class CriminalIntentJSONSerializer {

// variables
private Context mContext;
private String mFilename;
private static final String TAG = "JSONSERIALIZER";

public CriminalIntentJSONSerializer(Context c, String f) {
	mContext = c;
	mFilename = f;
}

public ArrayList<Crime> loadCrimes() throws IOException, JSONException {
	ArrayList<Crime> crimes = new ArrayList<Crime>();
	BufferedReader reader = null;
	
	// see if the sd card is mounted and also check to see if the crimes file exists
	// find out if the SD card is mounted
	Boolean isSDPresent = android.os.Environment.getExternalStorageState().equals(android.os.Environment.MEDIA_MOUNTED);
	// set variable pointing to file so we can check if it exists
	File extCrimeFile = new File(mContext.getExternalFilesDir(null), mFilename);
	
	if (isSDPresent && extCrimeFile.exists()) {
		Log.e(TAG, "The loadCrimes method found the SD Card mounted and found that the crimes file exists");
		try {
			// Open and read the file into a StringBuilder
			FileInputStream extFileInputStream = new FileInputStream(extCrimeFile);
			reader = new BufferedReader(new InputStreamReader(extFileInputStream));
			StringBuilder jsonString = new StringBuilder();
			String line = null;
			
			while ((line = reader.readLine()) != null) {
				// Line breaks are omitted and irrelevant
				jsonString.append(line);
			}// end while loop
			// Parse the JSON using JSONTokener
			JSONArray array = (JSONArray) new JSONTokener(jsonString.toString()).nextValue();
			// Build the array of crimes from JSONObjects
			for (int i = 0; i < array.length(); i++) { 
				crimes.add(new Crime(array.getJSONObject(i)));
			}
		} catch (FileNotFoundException e) {
			// Ignore this one ; it happens when starting fresh
		} finally {
			if (reader != null) {
				reader.close();
			}
		}// end try/catch
		return crimes;
		
	} else {
		
		try {
			// Open and read the file into a StringBuilder
			InputStream in = mContext.openFileInput(mFilename);
			reader = new BufferedReader(new InputStreamReader(in));
			StringBuilder jsonString = new StringBuilder();
			String line = null;
			
			while ((line = reader.readLine()) != null) {
				// Line breaks are omitted and irrelevant
				jsonString.append(line);
			}// end while loop
			// Parse the JSON using JSONTokener
			JSONArray array = (JSONArray) new JSONTokener(jsonString.toString()).nextValue();
			// Build the array of crimes from JSONObjects
			for (int i = 0; i < array.length(); i++) { 
				crimes.add(new Crime(array.getJSONObject(i)));
			}
		} catch (FileNotFoundException e) {
			// Ignore this one ; it happens when starting fresh
		} finally {
			if (reader != null) {
				reader.close();
			}
		}// end try/catch
		return crimes;
		
	}
}

public void saveCrimes(ArrayList<Crime> crimes) throws JSONException, IOException {
	// build and array in JSON
	JSONArray array = new JSONArray();
	

	// find out if the SD card is mounted
	Boolean isSDPresent = android.os.Environment.getExternalStorageState().equals(android.os.Environment.MEDIA_MOUNTED);
	
	if (isSDPresent) {
		// yes SD-card is present
		Log.e(TAG, "The saveCrimes method found that the SD card is mounted");
		
		// get the external files dir
		File extDataDir = new File(mContext.getExternalFilesDir(null), mFilename);
		// log the dir to be written to
		Log.e(TAG, "The external files dir is: " + extDataDir.toString());
		
		for (Crime c : crimes) {
			array.put(c.toJSON());
			// write file to disk
			Writer writer = null;			
			try {
				File extCrimeFile = new File(extDataDir.toString());
				FileOutputStream extFOS = new FileOutputStream(extCrimeFile);
				writer = new OutputStreamWriter(extFOS);
				writer.write(array.toString());
			} finally {
				if (writer != null) {					
					writer.close();
				}// end if statement
			}// end try/catch
		}// end for statement
		
	} else {
		// The SD card was not mounted
		Log.e(TAG, "The SD card is not mounted");
		
		for (Crime c : crimes) {
			array.put(c.toJSON());
			// write file to disk
			Writer writer = null;			
			try {
				OutputStream out = mContext.openFileOutput(mFilename, Context.MODE_PRIVATE);
				writer = new OutputStreamWriter(out);
				writer.write(array.toString());
			} finally {
				if (writer != null) {					
					writer.close();
				}// end if statement
			}// end try/catch
		}// end for statement			
	}						
}

}
[/code]

AndroidManifest.xml

[code]<?xml version="1.0" encoding="utf-8"?>

<uses-sdk
    android:minSdkVersion="8"
    android:targetSdkVersion="17" />

//the line below allows the app to write to external storage, remove this comment line in the actual file though :)
<uses-permission android:name="android.permissions.WRITE_EXTERNAL_STORAGE" />

<application
    android:allowBackup="true"
    android:icon="@drawable/ic_launcher"
    android:label="@string/app_name"
    android:theme="@style/AppTheme" >
    <activity android:name=".CrimeListActivity">
        <intent-filter>
            <action android:name="android.intent.action.MAIN" />
            <category android:name="android.intent.category.LAUNCHER" />
        </intent-filter>
    </activity>
    <activity
        android:name=".CrimePagerActivity"
        android:label="@string/app_name" >
        <meta-data android:name="android.support.PARENT_ACTIVITY"
            android:value=".CrimeListActivity"/>
    </activity>
</application>
[/code]

#2

This is a great solution. You helped me alot.
Also, wanted to comment that you should use Log.e only for errors. Also, avoid using double code.
Here is my suggestion:

	public ArrayList<Crime> loadCrimes() throws IOException, JSONException {
		ArrayList<Crime> crimes = new ArrayList<Crime>();
		BufferedReader reader = null;

		// find out if the SD card is mounted
		Boolean isSDPresent = android.os.Environment.getExternalStorageState().equals(android.os.Environment.MEDIA_MOUNTED);
		// set variable pointing to file so we can check if it exists
		File extCrimeFile = new File(mContext.getExternalFilesDir(null), mFilename);
		try {
			if (isSDPresent && extCrimeFile.exists()) {
				// Open and read the file into a StringBuilder
				FileInputStream extFileInputStream = new FileInputStream(extCrimeFile);
				reader = new BufferedReader(new InputStreamReader(extFileInputStream));
			} else {
				// Open and read the file into a StringBuilder
				InputStream in = mContext.openFileInput(mFilename);
				reader = new BufferedReader(new InputStreamReader(in));
			}
			StringBuilder jsonString = new StringBuilder();
			String line = null;

			while ((line = reader.readLine()) != null) {
				// Line breaks are omitted and irrelevant
				jsonString.append(line);
			}// end while loop
			// Parse the JSON using JSONTokener
			JSONArray array = (JSONArray) new JSONTokener(jsonString.toString()).nextValue();
			// Build the array of crimes from JSONObjects
			for (int i = 0; i < array.length(); i++) { 
				crimes.add(new Crime(array.getJSONObject(i)));
			}
		} catch (FileNotFoundException e) {
			// Ignore this one ; it happens when starting fresh
		} finally {
			if (reader != null) {
				reader.close();
			}
		}// end try/catch
		return crimes;
	}

	public void saveCrimes(ArrayList<Crime> crimes) throws JSONException, IOException {
		// build and array in JSON
		JSONArray array = new JSONArray();


		// find out if the SD card is mounted
		Boolean isSDPresent = android.os.Environment.getExternalStorageState().equals(android.os.Environment.MEDIA_MOUNTED);
		for (Crime c : crimes) 
			array.put(c.toJSON());
		Writer writer = null;     
		try {
			if (isSDPresent) {
				// yes SD-card is present
				// get the external files dir
				File extDataDir = new File(mContext.getExternalFilesDir(null), mFilename);
				File extCrimeFile = new File(extDataDir.toString());
				FileOutputStream extFOS = new FileOutputStream(extCrimeFile);
				writer = new OutputStreamWriter(extFOS);
			} else {
				OutputStream out = mContext.openFileOutput(mFilename, Context.MODE_PRIVATE);
				writer = new OutputStreamWriter(out);
			}
			writer.write(array.toString());
		} finally {
			if (writer != null) {               
				writer.close();
			}// end if statement
		}// end try/catch
	}// end for statement
}