-
Notifications
You must be signed in to change notification settings - Fork 1
/
DistrictTrackerPanel.java
205 lines (183 loc) · 7.46 KB
/
DistrictTrackerPanel.java
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
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
/*
* This file is part of CustomLauncherRewrite.
*
* CustomLauncherRewrite is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* CustomLauncherRewrite is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with CustomLauncherRewrite. If not, see <https://www.gnu.org/licenses/>.
*/
package lol.hyper.customlauncher.districts;
import lol.hyper.customlauncher.tools.JSONUtils;
import org.json.JSONObject;
import javax.swing.Timer;
import javax.swing.*;
import javax.swing.table.DefaultTableModel;
import java.awt.*;
import java.awt.event.ActionListener;
import java.text.SimpleDateFormat;
import java.util.List;
import java.util.*;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
public class DistrictTrackerPanel extends JPanel {
/**
* All current districts saved locally.
*/
private final Map<String, District> districts = new HashMap<>();
/**
* The table for displaying districts.
*/
private final JTable districtTable;
/**
* Label for "total toons."
*/
private final JLabel totalPopulationLabel;
/**
* The model for storing districts.
*/
private final DefaultTableModel districtsTableModel;
/**
* Label for "last updated."
*/
private final JLabel lastFetchedLabel;
/**
* Date format for "Last updated."
*/
private final SimpleDateFormat lastFetchedFormat = new SimpleDateFormat("hh:mm:ss a");
/**
* When was the last time we made an API request.
*/
private long lastFetched = 0;
/**
* Tracks if the API is offline.
*/
public boolean isDown = false;
/**
* Scheduler for making API requests.
*/
private ScheduledExecutorService executor;
/**
* Creates a DistrictTrackerPanel. It handles the window and tracking of each district.
*/
public DistrictTrackerPanel() {
// GUI elements
setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
// create the table
districtTable = new JTable();
String[] columns = new String[]{"Name", "Population", "Status"};
// create the table model
districtsTableModel = (DefaultTableModel) districtTable.getModel();
districtsTableModel.setColumnIdentifiers(columns);
districtTable.setDefaultEditor(Object.class, null);
districtTable.getTableHeader().setReorderingAllowed(false);
districtTable.setFocusable(false);
JScrollPane scrollPane = new JScrollPane(districtTable);
add(scrollPane);
// district label
totalPopulationLabel = new JLabel("Fetching population...");
totalPopulationLabel.setAlignmentX(Component.CENTER_ALIGNMENT);
add(totalPopulationLabel);
// store when we last updated
lastFetchedLabel = new JLabel("Waiting to update...");
lastFetchedLabel.setAlignmentX(Component.CENTER_ALIGNMENT);
add(lastFetchedLabel);
// start the refresh for updating the table
ActionListener actionListener = e -> updateDistricts();
Timer timer = new Timer(0, actionListener);
timer.setDelay(500);
timer.start();
startDistrictRefresh();
}
/**
* Updates the district list on the actual GUI.
*/
private void updateDistricts() {
districtsTableModel.setRowCount(0);
// create a separate list of all the districts
List<District> sortedDistricts = new ArrayList<>();
for (Map.Entry<String, District> entry : districts.entrySet()) {
sortedDistricts.add(entry.getValue());
}
// sort this new list alphabetically
Collections.sort(sortedDistricts);
int totalPopulation = 0;
// add the district to the table
for (District district : sortedDistricts) {
String name = district.getDistrictName();
int population = district.getPopulation();
String status = district.getCurrentStatus();
String[] data = new String[]{name, String.valueOf(population), status};
districtsTableModel.addRow(data);
totalPopulation = totalPopulation + district.getPopulation();
}
districtTable.setModel(districtsTableModel);
totalPopulationLabel.setText("Total toons: " + totalPopulation);
Date currentTime = new Date(lastFetched);
lastFetchedLabel.setText("Last updated: " + lastFetchedFormat.format(currentTime));
}
/**
* Read population API every 30 seconds.
*/
public void startDistrictRefresh() {
executor = Executors.newSingleThreadScheduledExecutor();
executor.scheduleAtFixedRate(this::makeRequest, 0, 30, TimeUnit.SECONDS);
}
/**
* Make the request and handle the information it returns.
*/
private void makeRequest() {
String DISTRICT_URL = "https://www.toontownrewritten.com/api/population";
JSONObject lastResult = JSONUtils.requestJSON(DISTRICT_URL);
// if the request failed, stop the task
if (lastResult == null) {
isDown = true;
executor.shutdown();
return;
}
isDown = false; // make sure to set this to false since we can read the API
JSONObject populationByDistrict = lastResult.getJSONObject("populationByDistrict");
// iterate through each district
Iterator<String> populationKeys = populationByDistrict.keys();
while (populationKeys.hasNext()) {
String districtFromJSON = populationKeys.next();
// if we do not have that district stored, create a new district object
// and add it to the list
if (!districts.containsKey(districtFromJSON)) {
int population = populationByDistrict.getInt(districtFromJSON);
District district = new District(districtFromJSON);
district.setPopulation(population);
districts.put(districtFromJSON, district);
} else {
if (!districts.containsKey(districtFromJSON)) {
return; // JUST IN CASE
}
// if we already have it saved, update the population
District tempDistrict = districts.get(districtFromJSON);
int population = populationByDistrict.getInt(districtFromJSON);
tempDistrict.setPopulation(population);
}
}
JSONObject statusByDistrict = lastResult.getJSONObject("statusByDistrict");
// iterate through each district
Iterator<String> statusKeys = statusByDistrict.keys();
while (statusKeys.hasNext()) {
String districtFromJSON = statusKeys.next();
// only update the status of districts we track
if (districts.containsKey(districtFromJSON)) {
String status = statusByDistrict.getString(districtFromJSON);
District tempDistrict = districts.get(districtFromJSON);
tempDistrict.setCurrentStatus(status);
}
}
lastFetched = System.currentTimeMillis();
}
}