summaryrefslogtreecommitdiff
path: root/base/src/main/java/com/smartdevicelink/managers/screen/menu/DynamicMenuUpdateAlgorithm.java
blob: c2a504aae7fafc0225157bed4b9fa100440ca531 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
package com.smartdevicelink.managers.screen.menu;

import java.util.ArrayList;
import java.util.List;

class DynamicMenuUpdateAlgorithm {
    // Cell state that tells the menu manager what it should do with a given MenuCell
    enum MenuCellState {
        DELETE, // Marks the cell to be deleted
        ADD, // Marks the cell to be added
        KEEP // Marks the cell to be kept
    }

    static DynamicMenuUpdateRunScore compatibilityRunScoreWithOldMenuCells(List<MenuCell> oldMenuCells, List<MenuCell> updatedMenuCells) {
        return new DynamicMenuUpdateRunScore(buildAllDeleteStatusesForMenu(oldMenuCells), buildAllAddStatusesForMenu(updatedMenuCells), updatedMenuCells.size());
    }

    static DynamicMenuUpdateRunScore dynamicRunScoreOldMenuCells(List<MenuCell> oldMenuCells, List<MenuCell> updatedMenuCells) {
        if (!oldMenuCells.isEmpty() && updatedMenuCells.isEmpty()) {
            // Deleting all cells
            return new DynamicMenuUpdateRunScore(buildAllDeleteStatusesForMenu(oldMenuCells), new ArrayList<MenuCellState>(), 0);
        } else if (oldMenuCells.isEmpty() && !updatedMenuCells.isEmpty()) {
            // No cells to delete
            return new DynamicMenuUpdateRunScore(new ArrayList<MenuCellState>(), buildAllAddStatusesForMenu(updatedMenuCells), updatedMenuCells.size());
        } else if (oldMenuCells.isEmpty() && updatedMenuCells.isEmpty()) {
            // Empty menu to empty menu
            return new DynamicMenuUpdateRunScore(new ArrayList<MenuCellState>(), new ArrayList<MenuCellState>(), 0);
        }
        return startCompareAtRun(0, oldMenuCells, updatedMenuCells);
    }

    private static DynamicMenuUpdateRunScore startCompareAtRun(int startRun, List<MenuCell> oldMenuCells, List<MenuCell> updatedMenuCells) {
        DynamicMenuUpdateRunScore bestScore = new DynamicMenuUpdateRunScore(new ArrayList<MenuCellState>(), new ArrayList<MenuCellState>(), 0);

        for (int run = startRun; run < oldMenuCells.size(); run++) {
            // Set the menu status as a 1-1 list, start off will oldMenus = all Deletes, newMenu = all Adds
            List<MenuCellState> oldMenuStatus = buildAllDeleteStatusesForMenu(oldMenuCells);
            List<MenuCellState> newMenuStatus = buildAllAddStatusesForMenu(updatedMenuCells);

            int startIndex = 0;
            for (int oldCellIndex = run; oldCellIndex < oldMenuCells.size(); oldCellIndex++) {
                // For each old item, create inner loop to compare old cells to new cells to find a match
                // if a match if found we mark the index at match for both the old and the new status to
                // keep since we do not want to send RPCs for those cases
                for (int newCellIndex = startIndex; newCellIndex < updatedMenuCells.size(); newCellIndex++) {
                    if (oldMenuCells.get(oldCellIndex).equalsWithUniqueTitle(updatedMenuCells.get(newCellIndex))) {
                        oldMenuStatus.set(oldCellIndex, MenuCellState.KEEP);
                        newMenuStatus.set(newCellIndex, MenuCellState.KEEP);
                        startIndex = newCellIndex + 1;
                        break;
                    }
                }
            }

            // Add RPC are the biggest operation so we need to find the run with the least amount of Adds.
            // We will reset the run we use each time a runScore is less than the current score.
            int numberOfAdds = 0;
            for (int status = 0; status < newMenuStatus.size(); status++) {
                if (newMenuStatus.get(status).equals(MenuCellState.ADD)) {
                    numberOfAdds++;
                }
            }

            // As soon as we a run that requires 0 Adds we will use it since we cant do better then 0
            if (numberOfAdds == 0) {
                bestScore = new DynamicMenuUpdateRunScore(oldMenuStatus, newMenuStatus, numberOfAdds);
                return bestScore;
            }

            // if we haven't create the bestScore object or if the current score beats the old score then we will create a new bestScore
            if (bestScore.isEmpty() || numberOfAdds < bestScore.getScore()) {
                bestScore = new DynamicMenuUpdateRunScore(oldMenuStatus, newMenuStatus, numberOfAdds);
            }

        }
        return bestScore;
    }

    /**
     * Builds a 1-1 list of Deletes for every element in the array
     * @param oldMenu The old menu list
     */
    static List<MenuCellState> buildAllDeleteStatusesForMenu (List<MenuCell> oldMenu){
        List<MenuCellState> oldMenuStatus = new ArrayList<>(oldMenu.size());
        for (int index = 0; index < oldMenu.size(); index++) {
            oldMenuStatus.add(MenuCellState.DELETE);
        }
        return oldMenuStatus;
    }

    /**
     * Builds a 1-1 list of Adds for every element in the list
     * @param newMenu The new menu list
     */
    static List<MenuCellState> buildAllAddStatusesForMenu (List<MenuCell> newMenu){
        List<MenuCellState> newMenuStatus = new ArrayList<>(newMenu.size());
        for (int index = 0; index < newMenu.size(); index++) {
            newMenuStatus.add(MenuCellState.ADD);
        }
        return newMenuStatus;
    }
}