5252/** Canvas-menu and native external-surface client for {@link EmulationService}. */
5353public final class MainActivity extends Activity implements RuntimeObserver {
5454
55- private static final int STATE_MENU_MIN_SLOT = 0 ;
56- private static final int STATE_MENU_MAX_SLOT = 3 ;
55+ private static final int STATE_MENU_MIN_SLOT = StateRef . MIN_SLOT ;
56+ private static final int STATE_MENU_MAX_SLOT = StateRef . MAX_SLOT ;
5757
5858 private static final int OPEN_ROM_REQUEST = 1 ;
5959 private static final int IMPORT_BATTERY_REQUEST = 2 ;
@@ -1805,12 +1805,7 @@ private MenuPreview statePreview(String preferredFocus) {
18051805 }
18061806
18071807 private MenuPageSpec statePage (MenuPreview preview , String preferredFocus ) {
1808- ArrayList <MenuPageSpec .Item > items = new ArrayList <>();
1809- for (int index = STATE_MENU_MIN_SLOT ; index <= STATE_MENU_MAX_SLOT ; index ++) {
1810- // Every stable slot remains focusable in both modes. LOAD treats an empty or
1811- // unavailable slot as a no-op; SAVE overwrites directly without confirmation.
1812- items .add (new MenuPageSpec .Item ("slot:" + index , "SLOT " + index , "" , true ));
1813- }
1808+ List <MenuPageSpec .Item > items = stateMenuItems (stateSlots );
18141809 String mode = stateMenuMode == StateMenuMode .SAVE ? "SAVE" : "LOAD" ;
18151810 return new MenuPageSpec (MenuRoute .SAVE_STATES , "COFFEE GB" , mode + " STATES" , "" , "" ,
18161811 stateSavedAtLines (stateSlot (preferredFocus == null
@@ -1820,6 +1815,30 @@ private MenuPageSpec statePage(MenuPreview preview, String preferredFocus) {
18201815 preferredFocus == null ? "slot:" + StateRef .MIN_SLOT : preferredFocus , preview );
18211816 }
18221817
1818+ /** Stable state rows shared by SAVE and LOAD; persisted rows carry a visual-only seal hint. */
1819+ static List <MenuPageSpec .Item > stateMenuItems (List <AndroidStateSlot > catalog ) {
1820+ ArrayList <MenuPageSpec .Item > items = new ArrayList <>();
1821+ for (int index = STATE_MENU_MIN_SLOT ; index <= STATE_MENU_MAX_SLOT ; index ++) {
1822+ // Every stable slot remains focusable in both modes. LOAD treats an empty or
1823+ // unavailable slot as a no-op; SAVE overwrites directly without confirmation.
1824+ boolean used = catalogContainsLoadableSlot (catalog , index );
1825+ // The compositor uses this semantic marker to distinguish a persisted state from an
1826+ // empty but still focusable slot. It is intentionally not exposed as row copy.
1827+ items .add (new MenuPageSpec .Item ("slot:" + index , "SLOT " + index ,
1828+ used ? "USED" : "" , true ));
1829+ }
1830+ return List .copyOf (items );
1831+ }
1832+
1833+ private static boolean catalogContainsLoadableSlot (List <AndroidStateSlot > catalog , int index ) {
1834+ for (AndroidStateSlot slot : catalog ) {
1835+ if (slot .index () == index && slot .loadable ()) {
1836+ return true ;
1837+ }
1838+ }
1839+ return false ;
1840+ }
1841+
18231842 private static List <String > stateSavedAtLines (AndroidStateSlot slot ) {
18241843 String formatted = formatStateSavedAt (slot == null ? null : slot .savedAt ());
18251844 return formatted == null ? List .of () : List .of (formatted );
0 commit comments