/** * Copyright (c) 2013, Sana * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * Neither the name of the Sana nor the * names of its contributors may be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL Sana BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.sana.android.app; import java.util.Stack; import android.content.Intent; import android.net.Uri; import android.os.Bundle; import android.util.Pair; import android.util.SparseArray; import org.sana.android.content.Intents; import org.sana.android.content.Uris; import org.sana.android.provider.Concepts; import org.sana.android.provider.Encounters; import org.sana.android.provider.Events; import org.sana.android.provider.Instructions; import org.sana.android.provider.Notifications; import org.sana.android.provider.Observations; import org.sana.android.provider.Observers; import org.sana.android.provider.Procedures; import org.sana.android.provider.Subjects; /** * Maintains state for the application and starts activities based on the * supplied data * * current state next state Intent launches * INITIAL, MAIN PICK AuthenticationAcitivity * PICK RUN Sana * RUN MAIN no action * default INITIAL AuthenticationAcitivity * * @author Sana Development * */ public abstract class ActivityRunner implements IntentRunner{ // Initialize to launch activity to open session protected Uri mConcept = Concepts.CONTENT_URI; protected Uri mEncounter = Encounters.CONTENT_URI; protected Uri mEvent = Events.CONTENT_URI; protected Uri mInstruction = Instructions.CONTENT_URI; protected Uri mObserver = Observers.CONTENT_URI; protected Uri mObservation = Observations.CONTENT_URI; protected Uri mNotification = Notifications.CONTENT_URI; protected Uri mProcedure = Procedures.CONTENT_URI; protected Uri mSubject = Subjects.CONTENT_URI; protected Stack<Intent> stack = new Stack<Intent>(); // Session intents /** An Intent to pick a session */ public static final Intent PICK_SESSION = new Intent(Intent.ACTION_PICK); static{ } public static final Intent PICK_OBSERVER = new Intent(Intent.ACTION_PICK); static{ PICK_OBSERVER.setDataAndType(Observers.CONTENT_URI, Observers.CONTENT_TYPE); } /** An intent for the Sana navigation screen */ public static final Intent HOME = new Intent(Intents.ACTION_PICK_ACTIVITY); static{ } // Encounter Intents /** An Intent to pick an Encounter */ public static final Intent PICK_ENCOUNTER= new Intent(Intent.ACTION_PICK); static{ PICK_ENCOUNTER.setDataAndType(Encounters.CONTENT_URI, Encounters.CONTENT_TYPE); } /** An intent to pick a subject */ public static final Intent PICK_SUBJECT = new Intent(Intent.ACTION_PICK); static{ PICK_SUBJECT.setDataAndType(Subjects.CONTENT_URI, Subjects.CONTENT_TYPE); } /** An Intent to pick a procedure */ public static final Intent PICK_PROCEDURE = new Intent(Intent.ACTION_PICK); static{ PICK_PROCEDURE.setDataAndType(Procedures.CONTENT_URI, Procedures.CONTENT_TYPE); } public static final Intent RUN_PROCEDURE = new Intent(Intent.ACTION_VIEW); /** An Intent to pick an Encounter */ public static final Intent PICK_NOTIFICATION= new Intent(Intent.ACTION_PICK); static{ PICK_ENCOUNTER.setDataAndType(Notifications.CONTENT_URI, Notifications.CONTENT_TYPE); } //-------------------------------------------------------------------------- // MAPPINGS //-------------------------------------------------------------------------- /* * The next state is calculated by * * result code + action + data --> request code, intent * * Hence, two maps for result code: * * Activity.RESULT_OK * Ativity.RESULT_CANCEL * * */ private static final SparseArray<Pair<Integer,Intent>> resultOk = new SparseArray<Pair<Integer,Intent>>(8); static{ // Default resultOk.append(Uris.NULL, Pair.create(Uris.SUBJECT_ITEM,PICK_SUBJECT)); // session item --> select patient resultOk.append(Uris.SUBJECT_DIR, Pair.create(Uris.SUBJECT_ITEM,PICK_SUBJECT)); // subject selected --> pick_procedure resultOk.append(Uris.SUBJECT_ITEM, Pair.create(Uris.PROCEDURE_ITEM,PICK_PROCEDURE)); resultOk.append(Uris.SUBJECT_UUID, Pair.create(Uris.PROCEDURE_ITEM,PICK_PROCEDURE)); // procedure selected --> run procedure resultOk.append(Uris.PROCEDURE_ITEM, Pair.create(Uris.ENCOUNTER_ITEM, RUN_PROCEDURE)); resultOk.append(Uris.PROCEDURE_UUID, Pair.create(Uris.ENCOUNTER_ITEM, RUN_PROCEDURE)); // encounter completed --> next procedure resultOk.append(Uris.ENCOUNTER_ITEM, Pair.create(Uris.PROCEDURE_ITEM, PICK_PROCEDURE)); resultOk.append(Uris.ENCOUNTER_UUID, Pair.create(Uris.PROCEDURE_ITEM, PICK_PROCEDURE)); } // Cancel mappings private static final SparseArray<Pair<Integer,Intent>> resultCancel = new SparseArray<Pair<Integer,Intent>>(4); static{ resultCancel.append(Uris.PROCEDURE_DIR, Pair.create(Uris.PROCEDURE_ITEM,PICK_PROCEDURE)); resultCancel.append(Uris.PROCEDURE_ITEM, Pair.create(Uris.PROCEDURE_ITEM,PICK_PROCEDURE)); } public ActivityRunner(Intent intent){ stack.push(intent); } public ActivityRunner(){ this(new Intent(Intent.ACTION_MAIN)); } /* * (non-Javadoc) * @see org.sana.analytics.Runner#next(java.lang.Object) */ @Override public Intent next(Intent intent) { // get the previous state Intent request = (stack.size() >= 1)? stack.pop(): null; // check for a null intent equivalent to CANCEL if(intent == null){ stack.push(handleCancel(request)); } else { stack.push(handleOk(request, intent)); } return Intents.copyOf(stack.peek()); } /* * Unwinds the INTENT stack to */ protected Intent goTo(int requestCode){ while(!stack.isEmpty()){ if (Intents.parseActionDescriptor(stack.peek()) == requestCode) return stack.pop(); else stack.pop(); } return new Intent(Intents.ACTION_FINISH); } protected abstract Intent handleCancel(Intent request); protected abstract Intent handleOk(Intent request, Intent response); public int getState(){ return Intents.parseActionDescriptor(stack.peek()); } public Intent getIntent(){ Intent intent = new Intent(); if(!stack.empty()){ intent = Intents.copyOf(stack.peek()); intent.putExtras(getDataBundle()); } return intent; } public Intent setData(Intent intent, Uri data){ switch(Uris.getDescriptor(data)){ case Uris.CONCEPT_DIR: case Uris.CONCEPT_ITEM: case Uris.CONCEPT_UUID: intent.putExtra(Intents.EXTRA_CONCEPT, data); break; case Uris.ENCOUNTER_DIR: case Uris.ENCOUNTER_ITEM: case Uris.ENCOUNTER_UUID: intent.putExtra(Intents.EXTRA_ENCOUNTER, data); break; case Uris.EVENT_DIR: case Uris.EVENT_ITEM: case Uris.EVENT_UUID: intent.putExtra(Intents.EXTRA_EVENT, data); break; case Uris.INSTRUCTION_DIR: case Uris.INSTRUCTION_ITEM: case Uris.INSTRUCTION_UUID: intent.putExtra(Intents.EXTRA_INSTRUCTION, data); break; case Uris.OBSERVATION_DIR: case Uris.OBSERVATION_ITEM: case Uris.OBSERVATION_UUID: intent.putExtra(Intents.EXTRA_OBSERVATION, data); break; case Uris.OBSERVER_DIR: case Uris.OBSERVER_ITEM: case Uris.OBSERVER_UUID: intent.putExtra(Intents.EXTRA_OBSERVER, data); break; case Uris.NOTIFICATION_DIR: case Uris.NOTIFICATION_ITEM: case Uris.NOTIFICATION_UUID: intent.putExtra(Intents.EXTRA_NOTIFICATION, data); break; case Uris.PROCEDURE_DIR: case Uris.PROCEDURE_ITEM: case Uris.PROCEDURE_UUID: intent.putExtra(Intents.EXTRA_PROCEDURE, data); break; case Uris.SUBJECT_DIR: case Uris.SUBJECT_ITEM: case Uris.SUBJECT_UUID: intent.putExtra(Intents.EXTRA_SUBJECT, data); break; } return intent; } public void setData(Uri uri){ int code = Uris.getTypeDescriptor(uri); setData(code, uri); } public void setData(int code, Uri uri){ switch(code){ case Uris.CONCEPT_DIR: case Uris.CONCEPT_ITEM: case Uris.CONCEPT_UUID: mConcept = uri; break; case Uris.ENCOUNTER_DIR: case Uris.ENCOUNTER_ITEM: case Uris.ENCOUNTER_UUID: mEncounter = uri; break; case Uris.EVENT_DIR: case Uris.EVENT_ITEM: case Uris.EVENT_UUID: mEvent = uri; break; case Uris.INSTRUCTION_DIR: case Uris.INSTRUCTION_ITEM: case Uris.INSTRUCTION_UUID: mInstruction = uri; break; case Uris.OBSERVATION_DIR: case Uris.OBSERVATION_ITEM: case Uris.OBSERVATION_UUID: mObservation = uri; break; case Uris.OBSERVER_DIR: case Uris.OBSERVER_ITEM: case Uris.OBSERVER_UUID: mObserver = uri; break; case Uris.NOTIFICATION_DIR: case Uris.NOTIFICATION_ITEM: case Uris.NOTIFICATION_UUID: mNotification = uri; break; case Uris.PROCEDURE_DIR: case Uris.PROCEDURE_ITEM: case Uris.PROCEDURE_UUID: mProcedure = uri; break; case Uris.SUBJECT_DIR: case Uris.SUBJECT_ITEM: case Uris.SUBJECT_UUID: mSubject = uri; break; } } /** * Returns the data state bundle * @return */ private Bundle getDataBundle(){ Bundle bundle = new Bundle(); bundle.putParcelable(Intents.EXTRA_CONCEPT, mConcept); bundle.putParcelable(Intents.EXTRA_ENCOUNTER, mEncounter); bundle.putParcelable(Intents.EXTRA_EVENT, mEvent); bundle.putParcelable(Intents.EXTRA_INSTRUCTION, mInstruction); bundle.putParcelable(Intents.EXTRA_NOTIFICATION, mNotification); bundle.putParcelable(Intents.EXTRA_OBSERVATION, mObservation); bundle.putParcelable(Intents.EXTRA_OBSERVER, mObserver); bundle.putParcelable(Intents.EXTRA_PROCEDURE, mProcedure); bundle.putParcelable(Intents.EXTRA_SUBJECT, mSubject); return bundle; } }