package com.gk.simpleworkoutjournal; import android.app.Activity; import android.app.LoaderManager; import android.content.Intent; import android.content.Loader; import android.database.Cursor; import android.os.Bundle; import android.util.Log; import android.view.Menu; import android.view.MenuItem; import android.view.MotionEvent; import android.view.View; import android.view.View.OnTouchListener; import android.widget.AdapterView; import android.widget.AdapterView.OnItemClickListener; import android.widget.AutoCompleteTextView; import android.widget.EditText; import android.widget.ImageButton; import android.widget.LinearLayout; import android.widget.ListView; import android.widget.TextView; import android.widget.Toast; import com.gk.datacontrol.DBClass; import com.gk.datacontrol.ExerciseDataCursorLoader; import com.gk.datacontrol.SetDataCursorLoader; import static com.gk.simpleworkoutjournal.WorkoutDataAdapter.*; public class WorkoutJournal extends Activity implements OnItemClickListener, OnTouchListener, LoaderManager.LoaderCallbacks<Cursor> { public enum TriggerEvent { NONE, INIT, ADD, DELETE, NOTEADD, TRIG_BY_EX, EDIT } private static final String APP_NAME = "SWJournal"; private static final boolean DEBUG_FLAG = false; public static final int EXERCISES = 0; public static final int SETS = 1; Subject currSubj; TriggerEvent setsUpTrigger; TriggerEvent exUpTrigger; LinearLayout notesLayout; AutoCompleteTextView exerciseTextView; EditText repsEdit, weightEdit; ListView exercisesLv, setsLv; TextView exerciseNoteTv, setNoteTv; WorkoutDataAdapter exerciseLogAdapter, setsLogAdapter; SetDataCursorLoader setsListDataLoader; ExerciseDataCursorLoader exListDataLoader; ImageButton switchBtn; boolean inContextMode; boolean addSetIfSameDate; String prevExLogId; WJContext exercisesContextualMode; WJContext setsContextualMode; boolean notesShowed = false; DBClass dbmediator; //WorkoutTimer workoutTimer; int initExPos; int initSetPos; protected void onSaveInstanceState(Bundle outState) { if (outState == null) throw new AssertionError("outState is null when onSaveInstanceState of workoutjournal "); super.onSaveInstanceState(outState); outState.putInt("exPos", exerciseLogAdapter.getIdxOfCurrent()); outState.putInt("setPos", setsLogAdapter.getIdxOfCurrent()); } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_workout_journal); // fetch all required UI items notesLayout = (LinearLayout) findViewById(R.id.notesLayout); setsLv = (ListView) findViewById(R.id.setsLv); setNoteTv = (TextView) findViewById(R.id.setNoteTv); exercisesLv = (ListView) findViewById(R.id.exercisesLv); exerciseNoteTv = (TextView) findViewById(R.id.exerciseNoteTv); exerciseTextView = (AutoCompleteTextView) findViewById(R.id.addExerciseACTV); repsEdit = (EditText) findViewById(R.id.editReps); weightEdit = (EditText) findViewById(R.id.editWeight); switchBtn = (ImageButton) findViewById(R.id.CancelBtn); // set notes touch listeners for exercise and set exerciseNoteTv.setOnTouchListener(this); // set click / touch listeners setsLv.setOnItemClickListener(this); exercisesLv.setOnItemClickListener(this); setsLv.setOnTouchListener(this); exercisesLv.setOnTouchListener(this); dbmediator = new DBClass(this); Cursor exCursor = dbmediator.fetchExerciseHistory(); exerciseLogAdapter = new WorkoutDataAdapter(this, exCursor, WorkoutDataAdapter.Subject.EXERCISES); exerciseTextView.setAdapter( new ExerciseEntrylistAdapter( this, dbmediator )); setsLogAdapter = null; //fill the text view now exercisesLv.setAdapter(exerciseLogAdapter); currSubj = Subject.EXERCISES; //TODO: show appropriate sets exercisesContextualMode = new WJContext(this, Subject.EXERCISES); setsContextualMode = new WJContext(this, Subject.SETS); exercisesLv.setMultiChoiceModeListener( exercisesContextualMode ); setsLv.setMultiChoiceModeListener( setsContextualMode ); inContextMode = false; addSetIfSameDate = false; prevExLogId = ""; if ( savedInstanceState != null ) { initExPos= savedInstanceState.getInt("exPos"); initSetPos = savedInstanceState.getInt("setPos"); } else { initExPos = -1; initSetPos = -1; } initiateListUpdate( Subject.ALL, TriggerEvent.INIT ); } @Override public boolean onCreateOptionsMenu(Menu menu) { if ( DEBUG_FLAG ) Log.v(APP_NAME, "WorkoutJournal :: onCreateOptionsMenu()"); // Inflate the menu; this adds items to the action bar if it is present. getMenuInflater().inflate(R.menu.workout_options_menu, menu); //workoutTimer = new WorkoutTimer( this, menu.findItem( R.id.action_timer) ); //workoutTimer.enable(); return true; } @Override public boolean onOptionsItemSelected(MenuItem item) { if ( DEBUG_FLAG ) Log.v(APP_NAME, "WorkoutJournal :: onOptionsItemSelected " + item.getItemId()); switch (item.getItemId()) { case R.id.action_moreinfo_icon: if (notesShowed) { notesLayout.setVisibility(View.GONE); notesShowed = false; } else { notesLayout.setVisibility(View.VISIBLE); notesShowed = true; } break; case R.id.action_timer: //workoutTimer.nextStep(); break; } return super.onOptionsItemSelected(item); } @Override public void onDestroy() { //workoutTimer.stop( true ); setsListDataLoader.reset(); exListDataLoader.reset(); dbmediator.close(); super.onDestroy(); } /* * Contains logic for initiating updates of lists */ public void initiateListUpdate( Subject trigSubj, TriggerEvent trigEvent ) { if ( DEBUG_FLAG ) Log.v(APP_NAME, "WorkoutJournal :: initiateListUpdate subj: " + trigSubj.toString() + " caused by: " + trigEvent.toString()); int subject = -1; switch ( trigSubj ) { case EXERCISES: exUpTrigger = trigEvent; subject = EXERCISES; break; case SETS: setsUpTrigger = trigEvent; subject = SETS; break; case ALL: exUpTrigger = trigEvent; setsUpTrigger = trigEvent; break; } if ( trigEvent == TriggerEvent.NOTEADD && trigSubj != Subject.EXERCISES && trigSubj != Subject.SETS ) { if ( DEBUG_FLAG ) Log.v( APP_NAME, "WorkoutJournal :: initiateListUpdate : incoming parameters are messed" ); return; } switch ( trigEvent ) { case INIT: // getLoaderManager().initLoader( EXERCISES, null, this); // getLoaderManager().initLoader( SETS, null, this); getLoaderManager().restartLoader( EXERCISES, null, this ); getLoaderManager().restartLoader( SETS, null, this ); break; case TRIG_BY_EX: //no need to do anything with exercises, but should renew sets if ( trigSubj == Subject.SETS ) { getLoaderManager().getLoader( SETS ).forceLoad(); } break; case EDIT: case DELETE: // set update may be not required if deleted ex is not current case NOTEADD: case ADD: // ex added - should renew both since focus changed. set added - only set lv to update getLoaderManager().getLoader( subject ).forceLoad(); break; default: Log.e( APP_NAME, "WorkoutJournal :: initiateListUpdate : unexpected trigger event: "+trigEvent.toString() ); } } public void onBackButtonPressed(View v) { if ( DEBUG_FLAG ) Log.v(APP_NAME, "WorkoutJournal :: onBackButtonPressed()"); if (exerciseTextView.getVisibility() == View.GONE) { showEditsForSubject( Subject.EXERCISES ); } else if (exerciseLogAdapter.getIdxOfCurrent() != -1) { showEditsForSubject( Subject.SETS ); } } /* * In exercise edit mode: check if exercise text field is not empty * and log exrcise. I exercise not exist in db - * add exercise to db * scroll listview to last item * ans set is as checked */ public void onAddButtonPressed(View v) { //we are trying both to log exercise to log DB and to add it into exercise DB and list view if (exerciseTextView.getVisibility() == View.VISIBLE) { String incomingName = exerciseTextView.getText().toString(); incomingName = incomingName.trim(); if ( DEBUG_FLAG ) Log.v(APP_NAME, "WorkoutJournal :: onAddButtonPressed(). Exercise in edit text: \"" + incomingName + "\" Amount of exercises before btn pressed: "+exerciseLogAdapter.getCount() ); if (incomingName.length() == 0) { Toast.makeText(this, R.string.empty_not_allowed, Toast.LENGTH_SHORT).show(); return; } dbmediator.addExercise(incomingName); // may fail since exercise is in db - it's ok dbmediator.logExercise(incomingName); exerciseTextView.setText(""); //populate list view with renewed data exerciseLogAdapter.setIdxOfCurrent(exerciseLogAdapter.getCount()); //no need to decrement since item is not renewed in the list yet: count will be larger setsListDataLoader.renewTargetEx( incomingName ); initiateListUpdate( Subject.EXERCISES, TriggerEvent.ADD ); //we are trying to add reps and weight } else { if ( DEBUG_FLAG ) Log.v(APP_NAME, "WorkoutJournal :: onAddButtonPressed() Reps: " + repsEdit.getText() + " Weight: " + weightEdit.getText() + " Curr ex idx: " + exerciseLogAdapter.getIdxOfCurrent()); String repString = repsEdit.getText().toString(); String weiString = weightEdit.getText().toString(); setsLogAdapter.setIdxOfCurrent(setsLogAdapter.getCount()); //no need to decrement since item is not renewed in the list yet: count will be larger if (repString.trim().length() == 0 || weiString.trim().length() == 0) { Toast.makeText(this, R.string.empty_not_allowed, Toast.LENGTH_SHORT).show(); // TODO: make a string resources for this toast return; } if ( DEBUG_FLAG ) Log.v(APP_NAME, "WorkoutJournal :: onAddButtonPressed(). current exercise: "+ exerciseLogAdapter.getIdxOfCurrent()); String exerciseName = exerciseLogAdapter.getNameForCurrent(); String exerciseLogId = exerciseLogAdapter.getIdForCurrent(); if ( !prevExLogId.equals( exerciseLogId ) ) { addSetIfSameDate = false; } prevExLogId = exerciseLogId; weiString = ( weiString.equals(",") ) ? "0" : weiString; if ( DBClass.EX_IN_PAST == dbmediator.insertSet( exerciseName, exerciseLogId, repString, weiString, addSetIfSameDate) ) { addSetIfSameDate = true; Toast.makeText(this, R.string.press_again_to_add, Toast.LENGTH_SHORT).show(); } else { addSetIfSameDate = false; } //refresh cursor initiateListUpdate(Subject.SETS, TriggerEvent.ADD); } //set note (possible only for exercise!) } /* * * */ @Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) { if ( DEBUG_FLAG ) Log.v(APP_NAME, "WorkoutJournal :: onItemClick :: position: " + position + " id: "+id + " view ID: "+view.getId() ); switch (view.getId()) { case R.id.exercise_entry_container: currSubj = Subject.EXERCISES; repsEdit.setText(""); weightEdit.setText(""); // obtain sets for this exercise // fetch new sets only if exercise entry changed if (exerciseLogAdapter.getIdxOfCurrent() == position) { if ( DEBUG_FLAG ) Log.v(APP_NAME, "WorkoutJournal :: onItemClick :: same item clicked, nothing to do."); } else { exerciseLogAdapter.setIdxOfCurrent(position); showEditsForSubject( Subject.SETS ); moveToSelected( Subject.EXERCISES, false ); //empty hint box for set since we have chosen other exercise setNoteTv.setHint(getString(R.string.workout_set_no_note_hint)); setNoteTv.setText(""); //need to update sets according to new item setsListDataLoader.renewTargetEx( (Cursor) exerciseLogAdapter.getItem( exerciseLogAdapter.getIdxOfCurrent() ) ); initiateListUpdate( Subject.SETS, TriggerEvent.TRIG_BY_EX); exerciseLogAdapter.notifyDataSetChanged(); } break; case R.id.set_entry_container: currSubj = Subject.SETS; exerciseTextView.setText(""); setsLogAdapter.setIdxOfCurrent(position); //same code for exs and sets. update note String noteSet = setsLogAdapter.getNoteForCurrent(); //if ( DEBUG_FLAG ) Log.v(APP_NAME, "WorkoutJournal :: onItemClick :: note : " + noteSet); if ( noteSet == null || noteSet.isEmpty() ) { setNoteTv.setHint( getString(R.string.workout_set_no_note_hint) ); setNoteTv.setText( "" ); } else { setNoteTv.setText(noteSet); } // show required exercise for selected date //PROBLEM possibly set if is not set at that moment setsLogAdapter.notifyDataSetChanged(); if ( syncListPositions( Subject.SETS ) ) { exerciseLogAdapter.notifyDataSetChanged(); exercisesLv.setSelection( exerciseLogAdapter.getIdxOfCurrent() ); } break; } } /* * Case if reps list view is touched - change edit panel for reps. * otherwise do nothing (we will return to exercise mode of panel only when appropriate button is pressed * */ @Override public boolean onTouch(View view, MotionEvent event) { //switch lower pane edits depending on touch target ( exercise list view or sets list view) if (event.getAction() == MotionEvent.ACTION_UP) { switch (view.getId()) { case R.id.setsLv: if (exerciseLogAdapter.getIdxOfCurrent() != -1) { showEditsForSubject( Subject.SETS ); } break; case R.id.exercisesLv: break; } } return false; } /* * return false if new position is not set, true if set */ public boolean syncListPositions( Subject baseSubject ) { if ( DEBUG_FLAG ) Log.v(APP_NAME, "WorkoutJournal :: syncListPositions :: started. base subject: " + baseSubject.toString() ); WorkoutDataAdapter baseAdapter, targetAdapter; Integer sourceKeyFieldIdx, targetKeyFieldIdx; if ( baseSubject == Subject.EXERCISES ) { baseAdapter = exerciseLogAdapter; targetAdapter = setsLogAdapter; targetKeyFieldIdx = setsLogAdapter.getCursor().getColumnIndex( DBClass.KEY_EX_LOG_ID ); sourceKeyFieldIdx = exerciseLogAdapter.getCursor().getColumnIndex( DBClass.KEY_ID ); } else if (baseSubject == Subject.SETS ) { baseAdapter = setsLogAdapter; targetAdapter = exerciseLogAdapter; targetKeyFieldIdx = exerciseLogAdapter.getCursor().getColumnIndex( DBClass.KEY_ID ); sourceKeyFieldIdx = setsLogAdapter.getCursor().getColumnIndex( DBClass.KEY_EX_LOG_ID ); } else { Log.e(APP_NAME, "WorkoutJournal :: syncListPositions :: unexpected subject: " + baseSubject.toString() ); return false; } if ( baseAdapter.getIdxOfCurrent() == -1 ) { Log.d(APP_NAME, "WorkoutJournal :: syncListPositions :: doing nothing since current for one of the adapters is not set" ); return false; } /* if ( DEBUG_FLAG ) Log.v(APP_NAME, "*** exercise cursor with current "+exerciseLogAdapter.getIdxOfCurrent() +" actual pos: "+exerciseLogAdapter.getCursor().getPosition()); DatabaseUtils.dumpCursor(exerciseLogAdapter.getCursor()); if ( DEBUG_FLAG ) Log.v(APP_NAME, "*** sets cursor with current "+setsLogAdapter.getIdxOfCurrent() +" actual pos: "+setsLogAdapter.getCursor().getPosition()); DatabaseUtils.dumpCursor(setsLogAdapter.getCursor()); */ baseAdapter.getCursor().moveToPosition( baseAdapter.getIdxOfCurrent() ); long sourceId = baseAdapter.getCursor().getLong( sourceKeyFieldIdx ); int initialTargetPos = targetAdapter.getIdxOfCurrent(); int targetPos = -1; Cursor trgtCs = targetAdapter.getCursor(); trgtCs.moveToFirst(); for ( ; !trgtCs.isAfterLast() ; trgtCs.moveToNext() ) { if ( DEBUG_FLAG ) Log.v(APP_NAME, "WorkoutJournal :: syncListPositions :: sourceId: "+sourceId+" targetId:"+trgtCs.getInt( targetKeyFieldIdx ) ); //may be several appropriate items in a sequence if ( trgtCs.getInt( targetKeyFieldIdx ) == sourceId ){ targetPos = trgtCs.getPosition(); if ( DEBUG_FLAG ) Log.v(APP_NAME, "WorkoutJournal :: syncListPositions :: intermediate position of target: "+targetPos ); continue; } //no need to iterate further since we already got target pos if ( targetPos != -1) { break; } } //move to new pos if found target or just restore initial if (targetPos != -1) { targetAdapter.setIdxOfCurrent( targetPos ); Subject targetSubject = (baseSubject == Subject.EXERCISES) ? Subject.SETS : baseSubject; moveToSelected( targetSubject, true ); if ( DEBUG_FLAG ) Log.v(APP_NAME, "WorkoutJournal :: syncListPositions :: resulting target found. SourceId: "+sourceId+" Target moved from "+initialTargetPos+ " to "+targetPos ); return true; } else { trgtCs.moveToPosition( initialTargetPos ); if ( DEBUG_FLAG ) Log.v(APP_NAME, "WorkoutJournal :: syncListPositions :: target not found ( sourceId: "+sourceId+")" ); return false; } } /* * will change current adapter to appropriate one */ public void onNotesTap(View v) { if ( DEBUG_FLAG ) Log.v(APP_NAME, "WorkoutJournal :: onNotesTap"); String headText; String targetId; WorkoutDataAdapter subjAdapter; switch (v.getId()) { case R.id.exerciseNoteTv: subjAdapter = exerciseLogAdapter; targetId = exerciseLogAdapter.getNameForCurrent(); headText = targetId; break; case R.id.setNoteTv: subjAdapter = setsLogAdapter; targetId = setsLogAdapter.getIdForCurrent(); headText = exerciseLogAdapter.getNameForCurrent() + " " + setsLogAdapter.getNameForCurrent(); break; default: return; } if ( subjAdapter.getCount() == 0 || subjAdapter.getIdxOfCurrent() == -1 ) { if ( DEBUG_FLAG ) Log.v(APP_NAME, "WorkoutJournal :: onNotesTap : doing nothing since no source for note exist"); return; } String note = subjAdapter.getNoteForCurrent(); if ( DEBUG_FLAG ) Log.v(APP_NAME, "WorkoutJournal :: onNotesTap : subj: "+subjAdapter.getSubject().toString()+" current: "+subjAdapter.getIdxOfCurrent()+ " note: " + note); Intent dialogIntent = new Intent(this, NotesDialog.class); dialogIntent.putExtra("targetId", targetId); dialogIntent.putExtra("headText", headText); dialogIntent.putExtra("note", note); // id is exercise name or sets id startActivityForResult(dialogIntent, (subjAdapter.getSubject() == Subject.EXERCISES) ? 1 : 0); } /* * note add window closed */ @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { if ( DEBUG_FLAG ) Log.v(APP_NAME, "WorkoutJournal :: onActivityResult"); if (resultCode == RESULT_OK) { //currCursor.moveToPosition(currAdapter.getIdxOfCurrent()); String note = data.getStringExtra("note"); String targetId = data.getStringExtra("targetId"); if ( DEBUG_FLAG ) Log.v(APP_NAME, "WorkoutJournal :: onActivityResult once got item"); switch (requestCode) { case 1: //exercise if ( DEBUG_FLAG ) Log.v(APP_NAME, "WorkoutJournal :: onActivityResult : gonna insert note \"" + note + "\" for ex \"" + exerciseLogAdapter.getNameForCurrent()+"\"" ); dbmediator.insertExerciseNote( targetId, note); exerciseNoteTv.setText(note); initiateListUpdate( Subject.EXERCISES, TriggerEvent.NOTEADD ); break; case 0: //set dbmediator.insertSetNote( targetId, note); setNoteTv.setText(note); initiateListUpdate(Subject.SETS, TriggerEvent.NOTEADD); break; } } } public void onContextButtonPressed(View contextualActionButton) { //TODO pay attention to curr handling when no more sets/exercises left! will it work? if ( DEBUG_FLAG ) Log.v(APP_NAME, "WorkoutJournal :: onContextButtonPressed :: active subject: " + currSubj.toString() ); WJContext cmcb; switch ( currSubj ) { case EXERCISES: cmcb = exercisesContextualMode; break; case SETS: cmcb = setsContextualMode; break; default: Log.e(APP_NAME, "WorkoutJournal :: onContextButtonPressed. Weird state met"); return; } switch (contextualActionButton.getId()) { case R.id.ctx_copySelectedBtn: cmcb.copyButtonPressed(); break; case R.id.ctx_deleteLogEntriesBtn: cmcb.onDeleteLogEntriesPressed(); break; case R.id.ctx_cancelBtn: cmcb.onRestoreContextLookBtnPressed(); break; case R.id.ctx_addEditedBtn: cmcb.onAddEditedBtnPressed(); break; default: Log.e(APP_NAME, "WorkoutJournal :: onContextButtonPressed. handler for passed view is missing"); } } /* @return 3 if no items left to operate on 2 if idx of checked must be decremented. 1 0 if no extra actions required */ /*public int adjustAfterExDeleted( int idxOfDeleted ) { if ( DEBUG_FLAG ) Log.v(APP_NAME, "WorkoutJournal :: adjustAfterExDeleted"); int retCode = 0; // remember - these are values before adjustment - DB is already changed! int sumOfElements = exercisesLv.getCount(); //if it was the only left element - need to report to handle checked items. if ( sumOfElements <= 1 ) { if ( DEBUG_FLAG ) Log.v(APP_NAME, "WorkoutJournal :: adjustAfterExDeleted detected deletion of very last item. Sum of items before deletion: "+sumOfElements+" deleted item idx: "+idxOfDeleted); exerciseLogAdapter.setIdxOfCurrent(-1); retCode = 3; } //if deleted item was last in a row - need to decrement current else if ( idxOfDeleted == sumOfElements-1 ) { if ( DEBUG_FLAG ) Log.v(APP_NAME, "WorkoutJournal :: adjustAfterExDeleted detected deletion of last in a row item."); exerciseLogAdapter.setIdxOfCurrent(idxOfDeleted - 1); retCode = 2; } else if ( idxOfDeleted == 0 ) { if ( DEBUG_FLAG ) Log.v(APP_NAME, "WorkoutJournal :: adjustAfterExDeleted detected deletion of first in a row item."); //selected is still the same. However, it's position may be changed now if one of prev entries was deleted or selected was last item in the list if ( idxOfDeleted < exerciseLogAdapter.getIdxOfCurrent() || exerciseLogAdapter.getIdxOfCurrent() == sumOfElements-1 ) { exerciseLogAdapter.setIdxOfCurrent(exerciseLogAdapter.getIdxOfCurrent() - 1); } retCode = 1; } //show renewed data for exercises initiateListUpdate( Subject.EXERCISES, TriggerEvent.DELETE ); //make sure exercise edit is active showEditsForSubject( Subject.EXERCISES ); // obtain sets for new exercise // fetch new sets only if exercise entry changed //empty hint box for set since we have chosen other exercise setNoteTv.setHint(getString(R.string.workout_set_no_note_hint)); setNoteTv.setText(""); //no sets if no exercises if ( retCode == 3 ) { setsLv.setAdapter(null); return retCode; } //update sets list view accordingly initiateListUpdate( Subject.SETS, TriggerEvent.DELETE ); if (setsLv.getCount() != 0) { syncListPositions( Subject.EXERCISES ); } return retCode; }*/ public void showEditsForSubject( Subject subj ) { switch ( subj ) { case EXERCISES: exerciseTextView.clearAnimation(); exerciseTextView.setVisibility(View.VISIBLE); repsEdit.setVisibility(View.GONE); weightEdit.setVisibility(View.GONE); switchBtn.setImageResource(R.drawable.ic_custom_circledforward); break; case SETS: exerciseTextView.setVisibility(View.GONE); repsEdit.setVisibility(View.VISIBLE); weightEdit.setVisibility(View.VISIBLE); switchBtn.setImageResource(R.drawable.ic_custom_circledback); break; default: Log.e(APP_NAME, "showEditsForSubject :: met unexpected subject : "+subj ); } } /* * Handler for cases when we dont need to update list, but only change selected to one was previosly set. */ public void moveToSelected( Subject subj, boolean needToScroll ) { if ( DEBUG_FLAG ) Log.v(APP_NAME, "WorkoutJournal :: moveToSelected :: subject : "+subj.toString()+" need to scroll: "+needToScroll ); WorkoutDataAdapter targetAdapter; ListView targetLv; switch ( subj ) { case EXERCISES: targetAdapter = exerciseLogAdapter; targetLv = exercisesLv; //empty sets note since new exercise was selected //but what if new setes adapter have note? TODO setNoteTv.setText(""); setNoteTv.setHint( R.string.workout_set_no_note_hint ); break; case SETS: targetAdapter = setsLogAdapter; targetLv = setsLv; break; default: Log.e(APP_NAME,"WorkoutJournal :: moveToSelected :: unknown target subject :: subj: "+subj); return; } updateNoteView( targetAdapter ); if ( needToScroll ) { targetLv.setSelection( targetAdapter.getIdxOfCurrent() ); } } /* * Will update note text view based on the info in cursor of adapter. Appropriate position must be set prior. */ public void updateNoteView( WorkoutDataAdapter sourceAdapter ) { if ( DEBUG_FLAG ) Log.v(APP_NAME, "WorkoutJournal :: updateNoteView for "+sourceAdapter.getSubject().toString() ); if ( sourceAdapter.getIdxOfCurrent() == -1 ) { return; } String newNoteHint; TextView targetTV; switch ( sourceAdapter.getSubject() ) { case EXERCISES: newNoteHint = getString(R.string.workout_exercise_newnote_hint); targetTV = exerciseNoteTv; break; case SETS: newNoteHint = getString(R.string.workout_set_newnote_hint); targetTV = setNoteTv; break; default: Log.e(APP_NAME, "WorkoutJournal :: updateNoteView :: unexpected subject of adapter" ); return; } String noteString = sourceAdapter.getNoteForCurrent(); if ( noteString == null || noteString.isEmpty()) { targetTV.setHint( newNoteHint ); targetTV.setText(""); } else { targetTV.setText( noteString ); } } @Override public Loader<Cursor> onCreateLoader(int id, Bundle args) { if ( DEBUG_FLAG ) Log.v(APP_NAME, "onCreateLoader :: Id " + id ); if ( id == EXERCISES ) { return exListDataLoader = new ExerciseDataCursorLoader(this, dbmediator ); } else { return setsListDataLoader = new SetDataCursorLoader(this, dbmediator, exerciseLogAdapter.getCursor() ); } } @Override public void onLoadFinished(Loader<Cursor> loader, Cursor data) { if ( DEBUG_FLAG ) Log.v(APP_NAME, "WorkoutJournal :: onLoadFinished : id " + loader.getId() + " trigger: "+ ( ( loader.getId() == 0 ) ? exUpTrigger.toString() : setsUpTrigger.toString()) ); if ( ( loader.getId() == EXERCISES && exUpTrigger == TriggerEvent.NONE ) || ( loader.getId() == SETS && setsUpTrigger == TriggerEvent.NONE ) ) { Log.e(APP_NAME, "WorkoutJournal :: onLoadFinished : cannot update since trigger event is not set for loader "+ loader.getId() ); return; } int current = -1; switch ( loader.getId() ) { case EXERCISES: if ( exUpTrigger == TriggerEvent.INIT ) { //on orientation change position my be not last before if ( initExPos != -1) { current = initExPos; initExPos = -1; } else { current = exerciseLogAdapter.getCount() - 1; } } else { current = exerciseLogAdapter.getIdxOfCurrent(); } exerciseLogAdapter.swapCursor( data ); exerciseLogAdapter.setIdxOfCurrent( current ); //this will move new cursor to initial position // empty notes box for sets since we might lost focus from sets. if ( exUpTrigger != TriggerEvent.NOTEADD && exUpTrigger != TriggerEvent.EDIT ) { setNoteTv.setText(""); setNoteTv.setHint(R.string.workout_set_no_note_hint); } // if add button clicked if ( exUpTrigger == TriggerEvent.ADD || exUpTrigger == TriggerEvent.EDIT || exUpTrigger == TriggerEvent.INIT ) { moveToSelected(Subject.EXERCISES, true); } if ( exUpTrigger != TriggerEvent.NOTEADD && exUpTrigger != TriggerEvent.EDIT ) { //set list behavior for add is the same as for ex click setsUpTrigger = TriggerEvent.TRIG_BY_EX; setsListDataLoader.renewTargetEx((Cursor) exerciseLogAdapter.getItem(exerciseLogAdapter.getIdxOfCurrent())); getLoaderManager().getLoader(SETS).forceLoad(); } if (exerciseLogAdapter.getCount() != 0) { updateNoteView( exerciseLogAdapter ); } else { exerciseNoteTv.setText(""); exerciseNoteTv.setHint( R.string.workout_exercise_no_note_hint ); } //if no exercises left - enforce ex adding toolbar if ( exerciseLogAdapter.getCount() == 0 ) { showEditsForSubject( Subject.EXERCISES ); } /* if ( exerciseLogAdapter.getIdxOfCurrent() != -1 ) { setsListDataLoader.renewTargetEx((Cursor) exerciseLogAdapter.getItem(exerciseLogAdapter.getIdxOfCurrent())); }*/ exUpTrigger = TriggerEvent.NONE; break; case SETS: //setsLv.setAdapter( setsLogAdapter = new WorkoutDataAdapter(this, data, WorkoutDataAdapter.Subject.SETS) ); if ( setsLogAdapter == null ) { setsLv.setAdapter( setsLogAdapter = new WorkoutDataAdapter(this, data, WorkoutDataAdapter.Subject.SETS) ); } else { setsLogAdapter.swapCursor( data ); } if ( setsLogAdapter != null && ( setsUpTrigger == TriggerEvent.DELETE || setsUpTrigger == TriggerEvent.NOTEADD || setsUpTrigger == TriggerEvent.EDIT ) ) { current = setsLogAdapter.getIdxOfCurrent(); } else if ( setsUpTrigger == TriggerEvent.INIT || setsUpTrigger == TriggerEvent.ADD ) { current = setsLogAdapter.getCount() - 1; } setsLogAdapter.setIdxOfCurrent(current); if ( ( setsUpTrigger == TriggerEvent.TRIG_BY_EX || setsUpTrigger == TriggerEvent.DELETE ) && setsLv.getCount() != 0) { //on orientation change position is known if ( initSetPos != -1 ) { setsLogAdapter.setIdxOfCurrent( initSetPos ); initSetPos = -1; } else { syncListPositions( Subject.EXERCISES ); } } if ( setsUpTrigger == TriggerEvent.ADD || setsUpTrigger == TriggerEvent.INIT ) { //move and update note moveToSelected(Subject.SETS, true); } else { //only update note if not an add event if (setsLogAdapter.getCount() != 0) { updateNoteView( setsLogAdapter ); } } setsUpTrigger = TriggerEvent.NONE; break; } } @Override public void onLoaderReset(Loader<Cursor> loader) { } }