/* * Copyright 2013 Elina Vartiainen and Simon Robinson * * This file is part of Com-Me. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package ac.robinson.mediaphonejavame.provider; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.OutputStreamWriter; import java.util.Vector; import javax.microedition.rms.RecordStore; import javax.microedition.rms.RecordStoreException; import javax.microedition.rms.RecordStoreFullException; import org.kxml2.io.KXmlParser; import org.kxml2.io.KXmlSerializer; import org.kxml2.kdom.Document; import org.kxml2.kdom.Element; import org.kxml2.kdom.Node; import ac.robinson.mediaphonejavame.MediaPhone; /** * Manages the narratives. The narratives are saved to the record store in XML format. The XML is read in at launch and * saved after each modification. */ public class NarrativeManager { private static NarrativeManager mInstance = new NarrativeManager(); private Vector mNarratives = new Vector(); private NarrativeManager() { } public static NarrativeManager getInstance() { return mInstance; } public Vector getNarratives() { if (mNarratives.isEmpty()) { loadNarratives(); } return mNarratives; } public void addNarrative(Narrative narrative) { getNarratives(); if (!mNarratives.contains(narrative)) { mNarratives.addElement(narrative); } saveNarratives(); } public void removeNarrative(Narrative narrative) { getNarratives(); if (mNarratives.contains(narrative)) { mNarratives.removeElement(narrative); } saveNarratives(); } private void saveNarratives() { // some phones corrupt rewrites of the same data, so delete the existing key rather than replacing try { RecordStore.deleteRecordStore(MediaPhone.NARRATIVES_KEY); } catch (Exception e) { } MediaPhone.log("Saving narrative XML..."); RecordStore recordStore = null; try { recordStore = RecordStore.openRecordStore(MediaPhone.NARRATIVES_KEY, true); byte[] narratives = generateNarratives(); recordStore.addRecord(narratives, 0, narratives.length); int size = recordStore.getNumRecords(); MediaPhone.log("Narratives saved (" + size + " records)"); } catch (RecordStoreFullException e) { MediaPhone.log("Narrative saving failed (RecordStore full)"); e.printStackTrace(); } catch (RecordStoreException e) { MediaPhone.log("Narrative saving failed (RecordStore)"); e.printStackTrace(); } finally { try { recordStore.closeRecordStore(); } catch (Exception e) { } } } private void loadNarratives() { RecordStore recordStore = null; try { recordStore = RecordStore.openRecordStore(MediaPhone.NARRATIVES_KEY, true); int size = recordStore.getNumRecords(); if (size == 1) { parseNarratives(recordStore.getRecord(1)); } else { MediaPhone.log("No narratives found to load (" + size + " records)"); } } catch (RecordStoreException e) { MediaPhone.log("Narrative loading failed (RecordStore)"); e.printStackTrace(); } finally { try { recordStore.closeRecordStore(); } catch (Exception e) { } } } private void parseNarratives(byte[] narrativesData) { ByteArrayInputStream inputStream = null; try { inputStream = new ByteArrayInputStream(narrativesData); KXmlParser xmlParser = new KXmlParser(); xmlParser.setInput(inputStream, "ISO-8859-1"); xmlParser.nextTag(); xmlParser.require(KXmlParser.START_TAG, null, "narratives"); while (xmlParser.nextTag() == KXmlParser.START_TAG) { xmlParser.require(KXmlParser.START_TAG, null, "narrative"); Narrative currentNarrative = new Narrative(); currentNarrative.setGUID(xmlParser.getAttributeValue(null, "guid")); while (xmlParser.nextTag() == KXmlParser.START_TAG) { xmlParser.require(KXmlParser.START_TAG, null, "frame"); Frame currentFrame = new Frame(); while (xmlParser.nextTag() == KXmlParser.START_TAG) { if (xmlParser.getName().equals("image")) { String imagePath = xmlParser.getAttributeValue(null, "path"); currentFrame.setImage(imagePath.length() > 0 ? imagePath : null); xmlParser.nextTag(); xmlParser.require(KXmlParser.END_TAG, null, "image"); } else if (xmlParser.getName().equals("audio")) { String audioPath = xmlParser.getAttributeValue(null, "path"); currentFrame.setAudio(audioPath.length() > 0 ? audioPath : null); String audioDuration = xmlParser.getAttributeValue(null, "duration"); if (audioDuration.length() > 0) { int audioDurationMilliseconds = 0; try { audioDurationMilliseconds = Integer.parseInt(audioDuration); } catch (NumberFormatException e) { } currentFrame.setAudioDurationMilliseconds(audioDurationMilliseconds); } xmlParser.nextTag(); xmlParser.require(KXmlParser.END_TAG, null, "audio"); } else if (xmlParser.getName().equals("text")) { String textContent = xmlParser.getAttributeValue(null, "content"); currentFrame.setText(textContent.length() > 0 ? textContent : null); xmlParser.nextTag(); xmlParser.require(KXmlParser.END_TAG, null, "text"); } } currentNarrative.addFrame(currentFrame); xmlParser.require(KXmlParser.END_TAG, null, "frame"); } xmlParser.require(KXmlParser.END_TAG, null, "narrative"); mNarratives.addElement(currentNarrative); } xmlParser.require(KXmlParser.END_TAG, null, "narratives"); MediaPhone.log("Loaded " + mNarratives.size() + " narratives"); } catch (Exception e) { MediaPhone.log("Narrative loading failed (XML)"); e.printStackTrace(); } finally { try { inputStream.close(); } catch (Exception e) { } } } private byte[] generateNarratives() { Document xmlDocument = new Document(); xmlDocument.setEncoding("ISO-8859-1"); Element rootElement = xmlDocument.createElement(null, "narratives"); xmlDocument.addChild(Node.ELEMENT, rootElement); for (int i = 0, n = mNarratives.size(); i < n; i++) { Narrative currentNarrative = (Narrative) mNarratives.elementAt(i); Element narrativeElement = rootElement.createElement(null, "narrative"); narrativeElement.setAttribute(null, "guid", currentNarrative.getGUID()); rootElement.addChild(Node.ELEMENT, narrativeElement); Vector currentFrames = currentNarrative.getFrames(); for (int j = 0, p = currentFrames.size(); j < p; j++) { Frame frame = (Frame) currentFrames.elementAt(j); Element frameElement = narrativeElement.createElement(null, "frame"); narrativeElement.addChild(Node.ELEMENT, frameElement); if (frame.getImage() != null) { Element imageElement = frameElement.createElement(null, "image"); imageElement.setAttribute(null, "path", frame.getImage()); frameElement.addChild(Node.ELEMENT, imageElement); } if (frame.getAudio() != null) { Element audioElement = frameElement.createElement(null, "audio"); audioElement.setAttribute(null, "path", frame.getAudio()); audioElement.setAttribute(null, "duration", Integer.toString(frame.getAudioDurationMilliseconds())); frameElement.addChild(Node.ELEMENT, audioElement); } if (frame.getText() != null) { Element textElement = frameElement.createElement(null, "text"); textElement.setAttribute(null, "content", frame.getText()); frameElement.addChild(Node.ELEMENT, textElement); } } } ByteArrayOutputStream outputStream = null; OutputStreamWriter streamWriter = null; KXmlSerializer xmlSerializer = new KXmlSerializer(); try { outputStream = new ByteArrayOutputStream(); streamWriter = new OutputStreamWriter(outputStream); xmlSerializer.setOutput(streamWriter); xmlDocument.write(xmlSerializer); MediaPhone.log("Saved " + mNarratives.size() + " narratives"); } catch (IOException e) { MediaPhone.log("Narrative saving failed (ByteStream)"); e.printStackTrace(); } finally { try { streamWriter.close(); } catch (Exception e) { } } return outputStream.toByteArray(); } }