@@ -36,15 +36,26 @@ public class CycleMatrixActivityMenu implements IActivityMenu {
3636
3737 TransitionMatrixModel _matrix ;
3838 IStudentDataModel _student ;
39+ PromotionMechanism _promotionMechanism ;
40+ private static final int MIN_NUM_ATTEMPTS = 3 ;
3941
4042 public CycleMatrixActivityMenu (TransitionMatrixModel matrix , IStudentDataModel student ) {
4143 this ._matrix = matrix ;
4244 this ._student = student ;
45+ this ._promotionMechanism = new PromotionMechanism (this ._student , this ._matrix );
4346 }
4447
4548 @ Override
4649 public String getLayoutName () {
47- return "ask_activity_selector_2x2" ;
50+ if (this ._promotionMechanism .performance .getNumberAttempts () >= MIN_NUM_ATTEMPTS
51+ &&
52+ this ._promotionMechanism .performance .getTotalNumberQuestions () >= 3
53+ &&
54+ this ._promotionMechanism .performance .getNumberCorrect () / this ._promotionMechanism .performance .getNumberAttempts () > PlacementPromotionRules .HIGH_PERFORMANCE_THRESHOLD
55+ )
56+ return "ask_activity_selector_2x2_elevate" ;
57+ else
58+ return "ask_activity_selector_2x2" ;
4859 }
4960
5061 @ Override
@@ -100,9 +111,62 @@ public CAt_Data[] getTutorsToShow() {
100111 Log .e ("SUPER_PLACEMENT" , "not ready yet" );
101112 nextTutors [0 ] = (CAt_Data ) transitionMap .get (tutorId );
102113 nextTutors [1 ] = nextTutors [0 ];
114+
115+ // Add null check for nextTutors[0]
116+ if (nextTutors [0 ] == null ) {
117+ Log .e (MENU_BUG_TAG , "Could not find tutor with ID: " + tutorId + " in placement mode" );
118+ // Try to get root tutor as fallback
119+ String rootTutor = "" ;
120+ switch (_student .getActiveSkill ()) {
121+ case SELECT_WRITING :
122+ rootTutor = _matrix .getRootSkillByContentArea (SELECT_WRITING );
123+ break ;
124+ case SELECT_STORIES :
125+ rootTutor = _matrix .getRootSkillByContentArea (SELECT_STORIES );
126+ break ;
127+ case SELECT_MATH :
128+ rootTutor = _matrix .getRootSkillByContentArea (SELECT_MATH );
129+ break ;
130+ }
131+ nextTutors [0 ] = (CAt_Data ) transitionMap .get (rootTutor );
132+ nextTutors [1 ] = nextTutors [0 ];
133+ }
103134 } else {
104135 nextTutors [0 ] = (CAt_Data ) transitionMap .get (tutorId ); // N
105- nextTutors [1 ] = isPlacementMode ? nextTutors [0 ] : (CAt_Data ) transitionMap .get (nextTutors [0 ].next ); // N or N + 1
136+
137+ // Add null checking here
138+ if (nextTutors [0 ] != null && nextTutors [0 ].next != null ) {
139+ CAt_Data nextTutor = (CAt_Data ) transitionMap .get (nextTutors [0 ].next );
140+ nextTutors [1 ] = isPlacementMode ? nextTutors [0 ] : nextTutor ;
141+
142+ // Add null check for nextTutors[1]
143+ if (nextTutors [1 ] == null ) {
144+ nextTutors [1 ] = nextTutors [0 ]; // Use the same tutor if next is null
145+ Log .e (MENU_BUG_TAG , "Next tutor is null, using same tutor twice: " + tutorId );
146+ }
147+ } else {
148+ // If there's no valid next tutor, use the same tutor twice
149+ if (nextTutors [0 ] == null ) {
150+ Log .e (MENU_BUG_TAG , "Current tutor is null for ID: " + tutorId );
151+ // Try to get root tutor as fallback
152+ String rootTutor = "" ;
153+ switch (_student .getActiveSkill ()) {
154+ case SELECT_WRITING :
155+ rootTutor = _matrix .getRootSkillByContentArea (SELECT_WRITING );
156+ break ;
157+ case SELECT_STORIES :
158+ rootTutor = _matrix .getRootSkillByContentArea (SELECT_STORIES );
159+ break ;
160+ case SELECT_MATH :
161+ rootTutor = _matrix .getRootSkillByContentArea (SELECT_MATH );
162+ break ;
163+ }
164+ nextTutors [0 ] = (CAt_Data ) transitionMap .get (rootTutor );
165+ }
166+
167+ nextTutors [1 ] = nextTutors [0 ];
168+ Log .d (MENU_BUG_TAG , "No next tutor available, using the same tutor twice" );
169+ }
106170 }
107171
108172 return nextTutors ;
@@ -143,10 +207,22 @@ public CAsk_Data initializeActiveLayout() {
143207 activeLayout .items [1 ].help = "something different" ;
144208
145209 activeLayout .items [2 ] = new CAskElement ();
146- activeLayout .items [2 ].componentID = "SbuttonRepeat" ;
147- activeLayout .items [2 ].behavior = AS_CONST .SELECT_REPEAT ;
148- activeLayout .items [2 ].prompt = "lets do it again" ;
149- activeLayout .items [2 ].help = "lets do it again" ;
210+ if (this ._promotionMechanism .performance .getNumberAttempts () >= MIN_NUM_ATTEMPTS
211+ &&
212+ this ._promotionMechanism .performance .getTotalNumberQuestions () >= 3
213+ &&
214+ this ._promotionMechanism .performance .getNumberCorrect () / this ._promotionMechanism .performance .getNumberAttempts () > PlacementPromotionRules .HIGH_PERFORMANCE_THRESHOLD ){
215+ activeLayout .items [2 ].componentID = "Sbutton1" ;
216+ activeLayout .items [2 ].behavior = AS_CONST .ELEVATE ;
217+ activeLayout .items [2 ].prompt = "I want something harder" ;
218+ activeLayout .items [2 ].help = "something harder" ;
219+ }
220+ else {
221+ activeLayout .items [2 ].componentID = "SbuttonRepeat" ;
222+ activeLayout .items [2 ].behavior = AS_CONST .SELECT_REPEAT ;
223+ activeLayout .items [2 ].prompt = "lets do it again" ;
224+ activeLayout .items [2 ].help = "lets do it again" ;
225+ }
150226
151227 activeLayout .items [3 ] = new CAskElement ();
152228 activeLayout .items [3 ].componentID = "SbuttonExit" ;
@@ -163,7 +239,18 @@ public Map<String, String> getButtonBehaviorMap() {
163239
164240 map .put (SELECT_OPTION_0 , AS_CONST .QUEUEMAP_KEYS .BUTTON_BEHAVIOR );
165241 map .put (SELECT_OPTION_1 , AS_CONST .QUEUEMAP_KEYS .BUTTON_BEHAVIOR );
166- map .put (AS_CONST .SELECT_REPEAT , AS_CONST .QUEUEMAP_KEYS .BUTTON_BEHAVIOR );
242+
243+ if (this ._promotionMechanism .performance .getNumberAttempts () >= MIN_NUM_ATTEMPTS
244+ &&
245+ this ._promotionMechanism .performance .getTotalNumberQuestions () >= 3
246+ &&
247+ this ._promotionMechanism .performance .getNumberCorrect () / this ._promotionMechanism .performance .getNumberAttempts () > PlacementPromotionRules .HIGH_PERFORMANCE_THRESHOLD ){
248+ map .put (AS_CONST .ELEVATE , AS_CONST .QUEUEMAP_KEYS .BUTTON_BEHAVIOR );
249+ }
250+ else {
251+ map .put (AS_CONST .SELECT_REPEAT , AS_CONST .QUEUEMAP_KEYS .BUTTON_BEHAVIOR );
252+ }
253+
167254 map .put (AS_CONST .SELECT_EXIT , AS_CONST .QUEUEMAP_KEYS .EXIT_BUTTON_BEHAVIOR );
168255 return map ;
169256 }
@@ -177,7 +264,7 @@ public CAt_Data getTutorToLaunch(String buttonBehavior) {
177264 String rootTutor = "" ;
178265
179266 String activeSkill = null ;
180- String chosenTutorId ;
267+ String chosenTutorId = null ;
181268
182269 Log .d ("OH_BEHAVE" , "some behavior here should be different..." );
183270
@@ -189,12 +276,49 @@ public CAt_Data getTutorToLaunch(String buttonBehavior) {
189276 transitionMap = _matrix .getTransitionMapByContentArea (activeSkill );
190277 rootTutor = _matrix .getRootSkillByContentArea (activeSkill );
191278 RoboTutor .logManager .postEvent_I (REPEAT_DEBUG_TAG , String .format ("activeSkill=%s;chosenTutorId=%s;transitionMap=%s;rootTutor=%s" , activeSkill , chosenTutorId , activeSkill , rootTutor ));
279+ }
280+ else if (buttonBehavior .equals (AS_CONST .ELEVATE )) {
281+ activeSkill = _student .getLastSkill ();
282+ transitionMap = _matrix .getTransitionMapByContentArea (activeSkill );
283+ chosenTutorId = _student .getLastTutor ();
192284
285+ if (activeSkill != null ) {
286+ // If the last activity is not null then go to that activity but update the placement index by 1
287+ if (activeSkill .equals (SELECT_MATH )) {
288+ rootTutor = _matrix .getRootSkillByContentArea (SELECT_MATH );
289+ CAt_Data transitionData = (CAt_Data ) transitionMap .get (chosenTutorId );
290+ if (transitionData != null && transitionData .harder != null ) {
291+ chosenTutorId = transitionData .harder ;
292+ } else {
293+ // If no harder level available, stay at current level
294+ Log .e (MENU_BUG_TAG , "No harder math level available for " + chosenTutorId );
295+ }
296+ }
297+ else if (activeSkill .equals (SELECT_WRITING )) {
298+ rootTutor = _matrix .getRootSkillByContentArea (SELECT_WRITING );
299+ CAt_Data transitionData = (CAt_Data ) transitionMap .get (chosenTutorId );
300+ if (transitionData != null && transitionData .harder != null ) {
301+ chosenTutorId = transitionData .harder ;
302+ } else {
303+ // If no harder level available, stay at current level
304+ Log .e (MENU_BUG_TAG , "No harder writing level available for " + chosenTutorId );
305+ }
306+ }
307+ else if (activeSkill .equals (SELECT_STORIES )) {
308+ rootTutor = _matrix .getRootSkillByContentArea (SELECT_STORIES );
309+ CAt_Data transitionData = (CAt_Data ) transitionMap .get (chosenTutorId );
310+ if (transitionData != null && transitionData .harder != null ) {
311+ chosenTutorId = transitionData .harder ;
312+ } else {
313+ // If no harder level available for stories, stay at current level
314+ Log .e (MENU_BUG_TAG , "No harder stories level available for " + chosenTutorId );
315+ }
316+ }
317+ }
193318 } else {
194-
195319 activeSkill = _student .getActiveSkill ();
196320 CAt_Data zeroIndexedTutor ;
197- String [] nextTutors = new String [3 ];
321+ String [] nextTutorIds = new String [3 ];
198322
199323 boolean inPlacementMode = false ;
200324
@@ -207,6 +331,11 @@ public CAt_Data getTutorToLaunch(String buttonBehavior) {
207331
208332 case SELECT_STORIES :
209333 zeroIndexedTutorId = _student .getStoryTutorID ();
334+ if (zeroIndexedTutorId == null ) {
335+ // If story tutor ID is null, get the root story tutor
336+ zeroIndexedTutorId = _matrix .getRootSkillByContentArea (SELECT_STORIES );
337+ Log .e (MENU_BUG_TAG , "Story tutor ID was null, falling back to root: " + zeroIndexedTutorId );
338+ }
210339 Log .d ("REPEAT_ME" , "storyTutor=" + zeroIndexedTutorId );
211340 break ;
212341
@@ -221,26 +350,79 @@ public CAt_Data getTutorToLaunch(String buttonBehavior) {
221350 RoboTutor .logManager .postEvent_I (MENU_BUG_TAG , "CycleMatrixActivityMenu: activeSkill=" + activeSkill + " -- activeTutorId=" + zeroIndexedTutorId );
222351 transitionMap = _matrix .getTransitionMapByContentArea (activeSkill );
223352 rootTutor = _matrix .getRootSkillByContentArea (activeSkill );
353+
354+ // Additional safety for stories
355+ if (activeSkill != null && activeSkill .equals (SELECT_STORIES ) && (transitionMap == null || transitionMap .isEmpty ())) {
356+ Log .e (MENU_BUG_TAG , "Story transition map is null or empty!" );
357+ // Create a minimal transition map if none exists
358+ if (transitionMap == null ) {
359+ transitionMap = new HashMap ();
360+ }
361+ // Make sure the root tutor exists in the map
362+ if (rootTutor != null && !transitionMap .containsKey (rootTutor )) {
363+ Log .e (MENU_BUG_TAG , "Adding root story tutor to map as fallback: " + rootTutor );
364+ CAt_Data rootData = new CAt_Data ();
365+ rootData .tutor_id = rootTutor ;
366+ rootData .next = rootTutor ; // Point to itself as fallback
367+ rootData .same = rootTutor ;
368+ transitionMap .put (rootTutor , rootData );
369+ }
370+ }
371+
224372 zeroIndexedTutor = (CAt_Data ) transitionMap .get (zeroIndexedTutorId );
225- nextTutors [0 ] = zeroIndexedTutor .tutor_id ;
226- nextTutors [1 ] = inPlacementMode ? nextTutors [0 ] : ((CAt_Data ) transitionMap .get (zeroIndexedTutor .next )).tutor_id ; // next hardest tutor!!! (I don't know WHY this is implemented twice)
373+
374+ if (zeroIndexedTutor != null ) {
375+ nextTutorIds [0 ] = zeroIndexedTutor .tutor_id ;
376+
377+ if (zeroIndexedTutor .next != null ) {
378+ CAt_Data nextTutor = (CAt_Data ) transitionMap .get (zeroIndexedTutor .next );
379+ nextTutorIds [1 ] = inPlacementMode ? nextTutorIds [0 ] : (nextTutor != null ? nextTutor .tutor_id : nextTutorIds [0 ]);
380+
381+ // Initialize nextTutorIds[2] to prevent null when SELECT_OPTION_2 is used
382+ if (nextTutor != null && nextTutor .next != null ) {
383+ CAt_Data nextNextTutor = (CAt_Data ) transitionMap .get (nextTutor .next );
384+ nextTutorIds [2 ] = nextNextTutor != null ? nextNextTutor .tutor_id : nextTutorIds [1 ];
385+ } else {
386+ nextTutorIds [2 ] = nextTutorIds [1 ]; // Use previous tutor if no "next next" is available
387+ }
388+ } else {
389+ nextTutorIds [1 ] = nextTutorIds [0 ];
390+ nextTutorIds [2 ] = nextTutorIds [0 ]; // Initialize index 2 as well
391+ Log .d (MENU_BUG_TAG , "No next tutor defined, using the same tutor twice" );
392+ }
393+ } else {
394+ // Handle case where zeroIndexedTutor is null
395+ Log .e (MENU_BUG_TAG , "Could not find tutor with ID: " + zeroIndexedTutorId );
396+ // Use root tutor as fallback
397+ CAt_Data rootTutorData = (CAt_Data ) transitionMap .get (rootTutor );
398+ if (rootTutorData != null ) {
399+ nextTutorIds [0 ] = rootTutorData .tutor_id ;
400+ nextTutorIds [1 ] = rootTutorData .tutor_id ;
401+ } else {
402+ // Critical error - no valid tutor found
403+ Log .e (MENU_BUG_TAG , "Critical error: No valid tutor found in transition map" );
404+ // Set to null and handle later
405+ nextTutorIds [0 ] = null ;
406+ nextTutorIds [1 ] = null ;
407+ }
408+ }
227409
228410 switch (buttonBehavior .toUpperCase ()) {
229411
230412 case SELECT_OPTION_0 : // TRACE_PROMOTION looks good...
231- chosenTutorId = nextTutors [0 ];
413+ chosenTutorId = nextTutorIds [0 ];
232414 break ;
233415
234416 case SELECT_OPTION_1 : // TRACE_PROMOTION looks good...
235417 // launch the next tutor
236418 // something like this...
237- chosenTutorId = nextTutors [1 ];
419+ chosenTutorId = nextTutorIds [1 ];
238420
239421 break ;
240422
241423 case SELECT_OPTION_2 :
242424 // launch the next.next tutor
243- chosenTutorId = nextTutors [2 ];
425+ chosenTutorId = nextTutorIds [2 ];
244426 break ;
245427
246428 default :
@@ -250,7 +432,7 @@ public CAt_Data getTutorToLaunch(String buttonBehavior) {
250432
251433 // If they choose the second option, update our position in thematrix //
252434 // will only be updated if SELECT_OPTION_1 or SELECT_OPTION_2 were selected //
253- if (!chosenTutorId .equals (zeroIndexedTutorId )) {
435+ if (!chosenTutorId .equals (zeroIndexedTutorId ) && ! buttonBehavior . equals ( AS_CONST . ELEVATE ) ) {
254436 switch (activeSkill ) {
255437 case SELECT_WRITING :
256438 _student .updateWritingTutorID (chosenTutorId , true );
@@ -265,12 +447,18 @@ public CAt_Data getTutorToLaunch(String buttonBehavior) {
265447 break ;
266448 }
267449 }
268-
269450 }
270451
271452
272453 // TRACE_PROMOTION doesn't save tutorToLaunch when we choose the second option!!!
273454 // the next tutor to be launched
455+
456+ // Final check to ensure chosenTutorId is not null
457+ if (chosenTutorId == null ) {
458+ Log .e (MENU_BUG_TAG , "Critical error: chosenTutorId is null, using root tutor as fallback" );
459+ chosenTutorId = rootTutor ;
460+ }
461+
274462 CAt_Data tutorToLaunch = (CAt_Data ) transitionMap .get (chosenTutorId );
275463 Log .wtf (MENU_BUG_TAG , chosenTutorId + " " + activeSkill );
276464
@@ -292,4 +480,4 @@ public CAt_Data getTutorToLaunch(String buttonBehavior) {
292480 public String getDebugMenuSkill () {
293481 return RoboTutor .STUDENT_CHOSE_REPEAT ? _student .getLastSkill () : _student .getActiveSkill (); // DEBUG_MENU_LOGIC (x) lastSkill vs ActiveSkill...
294482 }
295- }
483+ }
0 commit comments