time tracking: add overview tab
This commit is contained in:
@@ -0,0 +1,128 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2018, Daniel Teo <https://github.com/takuyakanbr>
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are met:
|
||||||
|
*
|
||||||
|
* 1. Redistributions of source code must retain the above copyright notice, this
|
||||||
|
* list of conditions and the following disclaimer.
|
||||||
|
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
* this list of conditions and the following disclaimer in the documentation
|
||||||
|
* and/or other materials provided with the distribution.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||||
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||||
|
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||||
|
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||||
|
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
|
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||||
|
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
package net.runelite.client.plugins.timetracking;
|
||||||
|
|
||||||
|
import java.awt.BorderLayout;
|
||||||
|
import java.awt.Color;
|
||||||
|
import java.awt.Cursor;
|
||||||
|
import java.awt.Dimension;
|
||||||
|
import java.awt.GridLayout;
|
||||||
|
import java.awt.event.MouseAdapter;
|
||||||
|
import java.awt.event.MouseEvent;
|
||||||
|
import javax.swing.ImageIcon;
|
||||||
|
import javax.swing.JLabel;
|
||||||
|
import javax.swing.JPanel;
|
||||||
|
import javax.swing.border.EmptyBorder;
|
||||||
|
import net.runelite.client.game.ItemManager;
|
||||||
|
import net.runelite.client.ui.ColorScheme;
|
||||||
|
import net.runelite.client.ui.FontManager;
|
||||||
|
import net.runelite.client.util.ImageUtil;
|
||||||
|
|
||||||
|
class OverviewItemPanel extends JPanel
|
||||||
|
{
|
||||||
|
private static final ImageIcon ARROW_RIGHT_ICON;
|
||||||
|
|
||||||
|
private static final Color HOVER_COLOR = ColorScheme.DARKER_GRAY_HOVER_COLOR;
|
||||||
|
|
||||||
|
private final JLabel statusLabel;
|
||||||
|
|
||||||
|
static
|
||||||
|
{
|
||||||
|
ARROW_RIGHT_ICON = new ImageIcon(ImageUtil.getResourceStreamFromClass(TimeTrackingPlugin.class, "/util/arrow_right.png"));
|
||||||
|
}
|
||||||
|
|
||||||
|
OverviewItemPanel(ItemManager itemManager, TimeTrackingPanel pluginPanel, Tab tab, String title)
|
||||||
|
{
|
||||||
|
setBackground(ColorScheme.DARKER_GRAY_COLOR);
|
||||||
|
setLayout(new BorderLayout());
|
||||||
|
setBorder(new EmptyBorder(7, 7, 7, 7));
|
||||||
|
|
||||||
|
JLabel iconLabel = new JLabel();
|
||||||
|
iconLabel.setMinimumSize(new Dimension(36, 32));
|
||||||
|
itemManager.getImage(tab.getItemID()).addTo(iconLabel);
|
||||||
|
add(iconLabel, BorderLayout.WEST);
|
||||||
|
|
||||||
|
JPanel textContainer = new JPanel();
|
||||||
|
textContainer.setBackground(ColorScheme.DARKER_GRAY_COLOR);
|
||||||
|
textContainer.setLayout(new GridLayout(2, 1));
|
||||||
|
textContainer.setBorder(new EmptyBorder(5, 7, 5, 7));
|
||||||
|
|
||||||
|
addMouseListener(new MouseAdapter()
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
public void mousePressed(MouseEvent mouseEvent)
|
||||||
|
{
|
||||||
|
pluginPanel.switchTab(tab);
|
||||||
|
setBackground(ColorScheme.DARKER_GRAY_COLOR);
|
||||||
|
textContainer.setBackground(ColorScheme.DARKER_GRAY_COLOR);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void mouseReleased(MouseEvent e)
|
||||||
|
{
|
||||||
|
setBackground(HOVER_COLOR);
|
||||||
|
textContainer.setBackground(HOVER_COLOR);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void mouseEntered(MouseEvent e)
|
||||||
|
{
|
||||||
|
setBackground(HOVER_COLOR);
|
||||||
|
textContainer.setBackground(HOVER_COLOR);
|
||||||
|
setCursor(new Cursor(Cursor.HAND_CURSOR));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void mouseExited(MouseEvent e)
|
||||||
|
{
|
||||||
|
setBackground(ColorScheme.DARKER_GRAY_COLOR);
|
||||||
|
textContainer.setBackground(ColorScheme.DARKER_GRAY_COLOR);
|
||||||
|
setCursor(new Cursor(Cursor.DEFAULT_CURSOR));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
JLabel titleLabel = new JLabel(title);
|
||||||
|
titleLabel.setForeground(Color.WHITE);
|
||||||
|
titleLabel.setFont(FontManager.getRunescapeSmallFont());
|
||||||
|
|
||||||
|
statusLabel = new JLabel();
|
||||||
|
statusLabel.setForeground(Color.GRAY);
|
||||||
|
statusLabel.setFont(FontManager.getRunescapeSmallFont());
|
||||||
|
|
||||||
|
textContainer.add(titleLabel);
|
||||||
|
textContainer.add(statusLabel);
|
||||||
|
|
||||||
|
add(textContainer, BorderLayout.CENTER);
|
||||||
|
|
||||||
|
JLabel arrowLabel = new JLabel(ARROW_RIGHT_ICON);
|
||||||
|
add(arrowLabel, BorderLayout.EAST);
|
||||||
|
}
|
||||||
|
|
||||||
|
void updateStatus(String text, Color color)
|
||||||
|
{
|
||||||
|
statusLabel.setText(text);
|
||||||
|
statusLabel.setForeground(color);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,138 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2018, Daniel Teo <https://github.com/takuyakanbr>
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are met:
|
||||||
|
*
|
||||||
|
* 1. Redistributions of source code must retain the above copyright notice, this
|
||||||
|
* list of conditions and the following disclaimer.
|
||||||
|
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
* this list of conditions and the following disclaimer in the documentation
|
||||||
|
* and/or other materials provided with the distribution.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||||
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||||
|
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||||
|
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||||
|
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
|
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||||
|
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
package net.runelite.client.plugins.timetracking;
|
||||||
|
|
||||||
|
import java.awt.Color;
|
||||||
|
import java.awt.GridLayout;
|
||||||
|
import java.time.Instant;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import net.runelite.client.game.ItemManager;
|
||||||
|
import net.runelite.client.plugins.timetracking.clocks.ClockManager;
|
||||||
|
import net.runelite.client.plugins.timetracking.farming.FarmingTracker;
|
||||||
|
import net.runelite.client.plugins.timetracking.farming.PatchImplementation;
|
||||||
|
import net.runelite.client.plugins.timetracking.hunter.BirdHouseTracker;
|
||||||
|
import net.runelite.client.ui.ColorScheme;
|
||||||
|
|
||||||
|
class OverviewTabPanel extends TabContentPanel
|
||||||
|
{
|
||||||
|
private final TimeTrackingConfig config;
|
||||||
|
private final FarmingTracker farmingTracker;
|
||||||
|
private final BirdHouseTracker birdHouseTracker;
|
||||||
|
private final ClockManager clockManager;
|
||||||
|
|
||||||
|
private final OverviewItemPanel timerOverview;
|
||||||
|
private final OverviewItemPanel stopwatchOverview;
|
||||||
|
private final Map<PatchImplementation, OverviewItemPanel> farmingOverviews;
|
||||||
|
private final OverviewItemPanel birdHouseOverview;
|
||||||
|
|
||||||
|
OverviewTabPanel(ItemManager itemManager, TimeTrackingConfig config, TimeTrackingPanel pluginPanel,
|
||||||
|
FarmingTracker farmingTracker, BirdHouseTracker birdHouseTracker, ClockManager clockManager)
|
||||||
|
{
|
||||||
|
this.config = config;
|
||||||
|
this.farmingTracker = farmingTracker;
|
||||||
|
this.birdHouseTracker = birdHouseTracker;
|
||||||
|
this.clockManager = clockManager;
|
||||||
|
|
||||||
|
setLayout(new GridLayout(0, 1, 0, 8));
|
||||||
|
setBackground(ColorScheme.DARK_GRAY_COLOR);
|
||||||
|
|
||||||
|
timerOverview = new OverviewItemPanel(itemManager, pluginPanel, Tab.CLOCK, "Timers");
|
||||||
|
add(timerOverview);
|
||||||
|
|
||||||
|
stopwatchOverview = new OverviewItemPanel(itemManager, pluginPanel, Tab.CLOCK, "Stopwatches");
|
||||||
|
add(stopwatchOverview);
|
||||||
|
|
||||||
|
birdHouseOverview = new OverviewItemPanel(itemManager, pluginPanel, Tab.BIRD_HOUSE, "Bird Houses");
|
||||||
|
add(birdHouseOverview);
|
||||||
|
|
||||||
|
farmingOverviews = new LinkedHashMap<>();
|
||||||
|
farmingOverviews.put(PatchImplementation.ALLOTMENT, new OverviewItemPanel(itemManager, pluginPanel, Tab.ALLOTMENT, "Allotment Patches"));
|
||||||
|
farmingOverviews.put(PatchImplementation.FLOWER, new OverviewItemPanel(itemManager, pluginPanel, Tab.FLOWER, "Flower Patches"));
|
||||||
|
farmingOverviews.put(PatchImplementation.HERB, new OverviewItemPanel(itemManager, pluginPanel, Tab.HERB, "Herb Patches"));
|
||||||
|
farmingOverviews.put(PatchImplementation.TREE, new OverviewItemPanel(itemManager, pluginPanel, Tab.TREE, "Tree Patches"));
|
||||||
|
farmingOverviews.put(PatchImplementation.FRUIT_TREE, new OverviewItemPanel(itemManager, pluginPanel, Tab.FRUIT_TREE, "Fruit Tree Patches"));
|
||||||
|
farmingOverviews.put(PatchImplementation.HOPS, new OverviewItemPanel(itemManager, pluginPanel, Tab.HOPS, "Hops Patches"));
|
||||||
|
farmingOverviews.put(PatchImplementation.BUSH, new OverviewItemPanel(itemManager, pluginPanel, Tab.BUSH, "Bush Patches"));
|
||||||
|
farmingOverviews.put(PatchImplementation.GRAPES, new OverviewItemPanel(itemManager, pluginPanel, Tab.GRAPE, "Grape Patches"));
|
||||||
|
|
||||||
|
for (OverviewItemPanel panel : farmingOverviews.values())
|
||||||
|
{
|
||||||
|
add(panel);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getUpdateInterval()
|
||||||
|
{
|
||||||
|
return 50; // 10 seconds
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void update()
|
||||||
|
{
|
||||||
|
final long timers = clockManager.getActiveTimerCount();
|
||||||
|
final long stopwatches = clockManager.getActiveStopwatchCount();
|
||||||
|
|
||||||
|
if (timers == 0)
|
||||||
|
{
|
||||||
|
timerOverview.updateStatus("No active timers", Color.GRAY);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
timerOverview.updateStatus(timers + " active timer" + (timers == 1 ? "" : "s"), ColorScheme.PROGRESS_COMPLETE_COLOR);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (stopwatches == 0)
|
||||||
|
{
|
||||||
|
stopwatchOverview.updateStatus("No active stopwatches", Color.GRAY);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
stopwatchOverview.updateStatus(stopwatches + " active stopwatch" + (stopwatches == 1 ? "" : "es"), ColorScheme.PROGRESS_COMPLETE_COLOR);
|
||||||
|
}
|
||||||
|
|
||||||
|
farmingOverviews.forEach((patchType, panel) -> updateItemPanel(panel, farmingTracker.getCompletionTime(patchType)));
|
||||||
|
updateItemPanel(birdHouseOverview, birdHouseTracker.getCompletionTime());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateItemPanel(OverviewItemPanel panel, long completionTime)
|
||||||
|
{
|
||||||
|
long duration = completionTime - Instant.now().getEpochSecond();
|
||||||
|
|
||||||
|
if (completionTime < 0)
|
||||||
|
{
|
||||||
|
panel.updateStatus("Unknown", Color.GRAY);
|
||||||
|
}
|
||||||
|
else if (duration <= 0)
|
||||||
|
{
|
||||||
|
panel.updateStatus("Ready", ColorScheme.PROGRESS_COMPLETE_COLOR);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
panel.updateStatus("Ready " + getFormattedEstimate(duration, config.estimateRelative()), Color.GRAY);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -32,6 +32,7 @@ import net.runelite.api.ItemID;
|
|||||||
@Getter
|
@Getter
|
||||||
public enum Tab
|
public enum Tab
|
||||||
{
|
{
|
||||||
|
OVERVIEW("Overview", ItemID.OLD_NOTES),
|
||||||
CLOCK("Timers & Stopwatches", ItemID.WATCH),
|
CLOCK("Timers & Stopwatches", ItemID.WATCH),
|
||||||
BIRD_HOUSE("Bird Houses", ItemID.OAK_BIRD_HOUSE),
|
BIRD_HOUSE("Bird Houses", ItemID.OAK_BIRD_HOUSE),
|
||||||
ALLOTMENT("Allotment Patches", ItemID.CABBAGE),
|
ALLOTMENT("Allotment Patches", ItemID.CABBAGE),
|
||||||
|
|||||||
@@ -25,6 +25,10 @@
|
|||||||
package net.runelite.client.plugins.timetracking;
|
package net.runelite.client.plugins.timetracking;
|
||||||
|
|
||||||
import java.awt.Dimension;
|
import java.awt.Dimension;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import java.time.format.TextStyle;
|
||||||
|
import java.time.temporal.ChronoUnit;
|
||||||
|
import java.util.Locale;
|
||||||
import javax.swing.JPanel;
|
import javax.swing.JPanel;
|
||||||
|
|
||||||
public abstract class TabContentPanel extends JPanel
|
public abstract class TabContentPanel extends JPanel
|
||||||
@@ -43,4 +47,41 @@ public abstract class TabContentPanel extends JPanel
|
|||||||
{
|
{
|
||||||
return super.getPreferredSize();
|
return super.getPreferredSize();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected static String getFormattedEstimate(long remainingSeconds, boolean useRelativeTime)
|
||||||
|
{
|
||||||
|
if (useRelativeTime)
|
||||||
|
{
|
||||||
|
StringBuilder sb = new StringBuilder("in ");
|
||||||
|
long duration = (remainingSeconds + 59) / 60;
|
||||||
|
long minutes = duration % 60;
|
||||||
|
long hours = (duration / 60) % 24;
|
||||||
|
long days = duration / (60 * 24);
|
||||||
|
if (days > 0)
|
||||||
|
{
|
||||||
|
sb.append(days).append("d ");
|
||||||
|
}
|
||||||
|
if (hours > 0)
|
||||||
|
{
|
||||||
|
sb.append(hours).append("h ");
|
||||||
|
}
|
||||||
|
if (minutes > 0)
|
||||||
|
{
|
||||||
|
sb.append(minutes).append("m ");
|
||||||
|
}
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
LocalDateTime endTime = LocalDateTime.now().plus(remainingSeconds, ChronoUnit.SECONDS);
|
||||||
|
LocalDateTime currentTime = LocalDateTime.now();
|
||||||
|
if (endTime.getDayOfWeek() != currentTime.getDayOfWeek())
|
||||||
|
{
|
||||||
|
sb.append(endTime.getDayOfWeek().getDisplayName(TextStyle.FULL, Locale.getDefault())).append(" ");
|
||||||
|
}
|
||||||
|
sb.append(String.format("at %d:%02d", endTime.getHour(), endTime.getMinute()));
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,6 +30,8 @@ import java.awt.Dimension;
|
|||||||
import java.awt.GridLayout;
|
import java.awt.GridLayout;
|
||||||
import java.awt.Image;
|
import java.awt.Image;
|
||||||
import java.awt.image.BufferedImage;
|
import java.awt.image.BufferedImage;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
import javax.swing.ImageIcon;
|
import javax.swing.ImageIcon;
|
||||||
import javax.swing.JPanel;
|
import javax.swing.JPanel;
|
||||||
@@ -55,6 +57,7 @@ class TimeTrackingPanel extends PluginPanel
|
|||||||
|
|
||||||
/* This is the panel the tabs' respective panels will be displayed on. */
|
/* This is the panel the tabs' respective panels will be displayed on. */
|
||||||
private final JPanel display = new JPanel();
|
private final JPanel display = new JPanel();
|
||||||
|
private final Map<Tab, MaterialTab> uiTabs = new HashMap<>();
|
||||||
private final MaterialTabGroup tabGroup = new MaterialTabGroup(display);
|
private final MaterialTabGroup tabGroup = new MaterialTabGroup(display);
|
||||||
|
|
||||||
private boolean active;
|
private boolean active;
|
||||||
@@ -81,6 +84,7 @@ class TimeTrackingPanel extends PluginPanel
|
|||||||
add(tabGroup, BorderLayout.NORTH);
|
add(tabGroup, BorderLayout.NORTH);
|
||||||
add(display, BorderLayout.CENTER);
|
add(display, BorderLayout.CENTER);
|
||||||
|
|
||||||
|
addTab(Tab.OVERVIEW, new OverviewTabPanel(itemManager, config, this, farmingTracker, birdHouseTracker, clockManager));
|
||||||
addTab(Tab.CLOCK, clockManager.getClockTabPanel());
|
addTab(Tab.CLOCK, clockManager.getClockTabPanel());
|
||||||
addTab(Tab.BIRD_HOUSE, birdHouseTracker.createBirdHouseTabPanel());
|
addTab(Tab.BIRD_HOUSE, birdHouseTracker.createBirdHouseTabPanel());
|
||||||
|
|
||||||
@@ -126,13 +130,20 @@ class TimeTrackingPanel extends PluginPanel
|
|||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
uiTabs.put(tab, materialTab);
|
||||||
tabGroup.addTab(materialTab);
|
tabGroup.addTab(materialTab);
|
||||||
|
|
||||||
if (config.activeTab() == tab)
|
if (config.activeTab() == tab)
|
||||||
{
|
{
|
||||||
tabGroup.select(materialTab);
|
tabGroup.select(materialTab);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void switchTab(Tab tab)
|
||||||
|
{
|
||||||
|
tabGroup.select(uiTabs.get(tab));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the update interval of the active tab panel, in units of 200 milliseconds.
|
* Gets the update interval of the active tab panel, in units of 200 milliseconds.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -111,6 +111,7 @@ public class TimeTrackingPlugin extends Plugin
|
|||||||
clockManager.loadTimers();
|
clockManager.loadTimers();
|
||||||
clockManager.loadStopwatches();
|
clockManager.loadStopwatches();
|
||||||
birdHouseTracker.loadFromConfig();
|
birdHouseTracker.loadFromConfig();
|
||||||
|
farmingTracker.loadCompletionTimes();
|
||||||
|
|
||||||
final BufferedImage icon = ImageUtil.getResourceStreamFromClass(getClass(), "watch.png");
|
final BufferedImage icon = ImageUtil.getResourceStreamFromClass(getClass(), "watch.png");
|
||||||
|
|
||||||
@@ -205,6 +206,7 @@ public class TimeTrackingPlugin extends Plugin
|
|||||||
public void onUsernameChanged(UsernameChanged e)
|
public void onUsernameChanged(UsernameChanged e)
|
||||||
{
|
{
|
||||||
farmingTracker.migrateConfiguration();
|
farmingTracker.migrateConfiguration();
|
||||||
|
farmingTracker.loadCompletionTimes();
|
||||||
birdHouseTracker.loadFromConfig();
|
birdHouseTracker.loadFromConfig();
|
||||||
panel.update();
|
panel.update();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -93,6 +93,16 @@ public class ClockManager
|
|||||||
SwingUtilities.invokeLater(clockTabPanel::rebuild);
|
SwingUtilities.invokeLater(clockTabPanel::rebuild);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public long getActiveTimerCount()
|
||||||
|
{
|
||||||
|
return timers.stream().filter(Timer::isActive).count();
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getActiveStopwatchCount()
|
||||||
|
{
|
||||||
|
return stopwatches.stream().filter(Stopwatch::isActive).count();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks if any timers have completed, and send notifications if required.
|
* Checks if any timers have completed, and send notifications if required.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -29,12 +29,8 @@ import com.google.common.base.Strings;
|
|||||||
import java.awt.GridBagConstraints;
|
import java.awt.GridBagConstraints;
|
||||||
import java.awt.GridBagLayout;
|
import java.awt.GridBagLayout;
|
||||||
import java.time.Instant;
|
import java.time.Instant;
|
||||||
import java.time.LocalDateTime;
|
|
||||||
import java.time.OffsetDateTime;
|
|
||||||
import java.time.format.TextStyle;
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Locale;
|
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import javax.swing.JLabel;
|
import javax.swing.JLabel;
|
||||||
import javax.swing.border.EmptyBorder;
|
import javax.swing.border.EmptyBorder;
|
||||||
@@ -215,40 +211,9 @@ public class FarmingTabPanel extends TabContentPanel
|
|||||||
{
|
{
|
||||||
panel.getEstimate().setText("Done");
|
panel.getEstimate().setText("Done");
|
||||||
}
|
}
|
||||||
else if (config.estimateRelative())
|
|
||||||
{
|
|
||||||
int remaining = (int) (59 + doneEstimate - unixNow) / 60;
|
|
||||||
StringBuilder f = new StringBuilder();
|
|
||||||
f.append("Done in ");
|
|
||||||
int min = remaining % 60;
|
|
||||||
int hours = (remaining / 60) % 24;
|
|
||||||
int days = remaining / (60 * 24);
|
|
||||||
if (days > 0)
|
|
||||||
{
|
|
||||||
f.append(days).append("d ");
|
|
||||||
}
|
|
||||||
if (hours > 0)
|
|
||||||
{
|
|
||||||
f.append(hours).append("h ");
|
|
||||||
}
|
|
||||||
if (min > 0)
|
|
||||||
{
|
|
||||||
f.append(min).append("m ");
|
|
||||||
}
|
|
||||||
panel.getEstimate().setText(f.toString());
|
|
||||||
}
|
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
StringBuilder f = new StringBuilder();
|
panel.getEstimate().setText("Done " + getFormattedEstimate(doneEstimate - unixNow, config.estimateRelative()));
|
||||||
LocalDateTime ldtTime = LocalDateTime.ofEpochSecond(doneEstimate, 0, OffsetDateTime.now().getOffset());
|
|
||||||
LocalDateTime ldtNow = LocalDateTime.now();
|
|
||||||
f.append("Done ");
|
|
||||||
if (ldtTime.getDayOfWeek() != ldtNow.getDayOfWeek())
|
|
||||||
{
|
|
||||||
f.append(ldtTime.getDayOfWeek().getDisplayName(TextStyle.FULL, Locale.getDefault())).append(" ");
|
|
||||||
}
|
|
||||||
f.append(String.format("at %d:%02d", ldtTime.getHour(), ldtTime.getMinute()));
|
|
||||||
panel.getEstimate().setText(f.toString());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|||||||
@@ -27,6 +27,8 @@ package net.runelite.client.plugins.timetracking.farming;
|
|||||||
import com.google.inject.Inject;
|
import com.google.inject.Inject;
|
||||||
import com.google.inject.Singleton;
|
import com.google.inject.Singleton;
|
||||||
import java.time.Instant;
|
import java.time.Instant;
|
||||||
|
import java.util.EnumMap;
|
||||||
|
import java.util.Map;
|
||||||
import net.runelite.api.Client;
|
import net.runelite.api.Client;
|
||||||
import net.runelite.api.Varbits;
|
import net.runelite.api.Varbits;
|
||||||
import net.runelite.api.coords.WorldPoint;
|
import net.runelite.api.coords.WorldPoint;
|
||||||
@@ -59,6 +61,15 @@ public class FarmingTracker
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The time at which all patches of a particular type will be ready to be harvested,
|
||||||
|
* or {@code -1} if we have no data about any patch of the given type.
|
||||||
|
*
|
||||||
|
* Each value is set to {@code 0} if all patches of that type have already completed
|
||||||
|
* when updating the value.
|
||||||
|
*/
|
||||||
|
private Map<PatchImplementation, Long> completionTimes = new EnumMap<>(PatchImplementation.class);
|
||||||
|
|
||||||
public FarmingTabPanel createTabPanel(Tab tab)
|
public FarmingTabPanel createTabPanel(Tab tab)
|
||||||
{
|
{
|
||||||
return new FarmingTabPanel(client, itemManager, configManager, config, farmingWorld.getTabs().get(tab));
|
return new FarmingTabPanel(client, itemManager, configManager, config, farmingWorld.getTabs().get(tab));
|
||||||
@@ -88,9 +99,10 @@ public class FarmingTracker
|
|||||||
// timetracking.<login-username>.<regionID>.<VarbitID>=<varbitValue>:<unix time>
|
// timetracking.<login-username>.<regionID>.<VarbitID>=<varbitValue>:<unix time>
|
||||||
String group = TimeTrackingConfig.CONFIG_GROUP + "." + client.getUsername() + "." + region.getRegionID();
|
String group = TimeTrackingConfig.CONFIG_GROUP + "." + client.getUsername() + "." + region.getRegionID();
|
||||||
long unixNow = Instant.now().getEpochSecond();
|
long unixNow = Instant.now().getEpochSecond();
|
||||||
for (Varbits varbit : region.getVarbits())
|
for (FarmingPatch patch : region.getPatches())
|
||||||
{
|
{
|
||||||
// Write the config value if it doesn't match what is current, or it is more than 5 minutes old
|
// Write the config value if it doesn't match what is current, or it is more than 5 minutes old
|
||||||
|
Varbits varbit = patch.getVarbit();
|
||||||
String key = Integer.toString(varbit.getId());
|
String key = Integer.toString(varbit.getId());
|
||||||
String strVarbit = Integer.toString(client.getVar(varbit));
|
String strVarbit = Integer.toString(client.getVar(varbit));
|
||||||
String storedValue = configManager.getConfiguration(group, key);
|
String storedValue = configManager.getConfiguration(group, key);
|
||||||
@@ -118,6 +130,7 @@ public class FarmingTracker
|
|||||||
|
|
||||||
String value = strVarbit + ":" + unixNow;
|
String value = strVarbit + ":" + unixNow;
|
||||||
configManager.setConfiguration(group, key, value);
|
configManager.setConfiguration(group, key, value);
|
||||||
|
updateCompletionTime(patch.getImplementation());
|
||||||
changed = true;
|
changed = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -125,6 +138,97 @@ public class FarmingTracker
|
|||||||
return changed;
|
return changed;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void loadCompletionTimes()
|
||||||
|
{
|
||||||
|
completionTimes.clear();
|
||||||
|
|
||||||
|
for (PatchImplementation patchType : PatchImplementation.values())
|
||||||
|
{
|
||||||
|
updateCompletionTime(patchType);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the overall completion time for the given patch type.
|
||||||
|
* @see #completionTimes
|
||||||
|
*/
|
||||||
|
public long getCompletionTime(PatchImplementation patchType)
|
||||||
|
{
|
||||||
|
Long completionTime = completionTimes.get(patchType);
|
||||||
|
return completionTime == null ? -1 : completionTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates the overall completion time for the given patch type.
|
||||||
|
* @see #completionTimes
|
||||||
|
*/
|
||||||
|
private void updateCompletionTime(PatchImplementation patchType)
|
||||||
|
{
|
||||||
|
long maxCompletionTime = 0;
|
||||||
|
boolean allUnknown = true;
|
||||||
|
|
||||||
|
for (FarmingPatch patch : farmingWorld.getPatchTypes().get(patchType))
|
||||||
|
{
|
||||||
|
String group = TimeTrackingConfig.CONFIG_GROUP + "." + client.getUsername() + "." + patch.getRegion().getRegionID();
|
||||||
|
String key = Integer.toString(patch.getVarbit().getId());
|
||||||
|
String storedValue = configManager.getConfiguration(group, key);
|
||||||
|
long unixTime = 0;
|
||||||
|
int value = 0;
|
||||||
|
|
||||||
|
if (storedValue != null)
|
||||||
|
{
|
||||||
|
String[] parts = storedValue.split(":");
|
||||||
|
if (parts.length == 2)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
value = Integer.parseInt(parts[0]);
|
||||||
|
unixTime = Long.parseLong(parts[1]);
|
||||||
|
}
|
||||||
|
catch (NumberFormatException e)
|
||||||
|
{
|
||||||
|
// ignored
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
PatchState state = unixTime <= 0 ? null : patch.getImplementation().forVarbitValue(value);
|
||||||
|
if (state == null || state.getProduce().getItemID() < 0)
|
||||||
|
{
|
||||||
|
continue; // unknown state
|
||||||
|
}
|
||||||
|
|
||||||
|
int tickrate = state.getTickRate() * 60;
|
||||||
|
int stage = state.getStage();
|
||||||
|
int stages = state.getStages();
|
||||||
|
|
||||||
|
if (state.getProduce() != Produce.WEEDS && state.getProduce() != Produce.SCARECROW)
|
||||||
|
{
|
||||||
|
// update max duration if this patch takes longer to grow
|
||||||
|
if (tickrate > 0)
|
||||||
|
{
|
||||||
|
long tickTime = unixTime / tickrate;
|
||||||
|
long doneEstimate = ((stages - 1 - stage) + tickTime) * tickrate;
|
||||||
|
maxCompletionTime = Math.max(maxCompletionTime, doneEstimate);
|
||||||
|
}
|
||||||
|
else if (state.getCropState() == CropState.GROWING && stage != stages - 1)
|
||||||
|
{
|
||||||
|
continue; // unknown state
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
allUnknown = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (allUnknown)
|
||||||
|
{
|
||||||
|
completionTimes.put(patchType, -1L);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
completionTimes.put(patchType, (maxCompletionTime <= Instant.now().getEpochSecond()) ? 0 : maxCompletionTime);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Migrates configuration data from {@code "farmingTracker"} key to {@code "timetracking"} key.
|
* Migrates configuration data from {@code "farmingTracker"} key to {@code "timetracking"} key.
|
||||||
* This method should be removed after a reasonable amount of time.
|
* This method should be removed after a reasonable amount of time.
|
||||||
|
|||||||
@@ -26,9 +26,12 @@
|
|||||||
package net.runelite.client.plugins.timetracking.farming;
|
package net.runelite.client.plugins.timetracking.farming;
|
||||||
|
|
||||||
import com.google.inject.Singleton;
|
import com.google.inject.Singleton;
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
|
import java.util.EnumMap;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.TreeMap;
|
import java.util.TreeMap;
|
||||||
@@ -47,6 +50,9 @@ class FarmingWorld
|
|||||||
@Getter
|
@Getter
|
||||||
private Map<Tab, Set<FarmingPatch>> tabs = new HashMap<>();
|
private Map<Tab, Set<FarmingPatch>> tabs = new HashMap<>();
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
private Map<PatchImplementation, List<FarmingPatch>> patchTypes = new EnumMap<>(PatchImplementation.class);
|
||||||
|
|
||||||
private final Comparator<FarmingPatch> tabSorter = Comparator
|
private final Comparator<FarmingPatch> tabSorter = Comparator
|
||||||
.comparing(FarmingPatch::getImplementation)
|
.comparing(FarmingPatch::getImplementation)
|
||||||
.thenComparing((FarmingPatch p) -> p.getRegion().getName())
|
.thenComparing((FarmingPatch p) -> p.getRegion().getName())
|
||||||
@@ -243,6 +249,10 @@ class FarmingWorld
|
|||||||
tabs
|
tabs
|
||||||
.computeIfAbsent(p.getImplementation().getTab(), k -> new TreeSet<>(tabSorter))
|
.computeIfAbsent(p.getImplementation().getTab(), k -> new TreeSet<>(tabSorter))
|
||||||
.add(p);
|
.add(p);
|
||||||
|
|
||||||
|
patchTypes
|
||||||
|
.computeIfAbsent(p.getImplementation(), k -> new ArrayList<>())
|
||||||
|
.add(p);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,12 +28,8 @@ package net.runelite.client.plugins.timetracking.hunter;
|
|||||||
|
|
||||||
import java.awt.Color;
|
import java.awt.Color;
|
||||||
import java.time.Instant;
|
import java.time.Instant;
|
||||||
import java.time.LocalDateTime;
|
|
||||||
import java.time.OffsetDateTime;
|
|
||||||
import java.time.format.TextStyle;
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Locale;
|
|
||||||
import net.runelite.api.ItemID;
|
import net.runelite.api.ItemID;
|
||||||
import net.runelite.client.game.ItemManager;
|
import net.runelite.client.game.ItemManager;
|
||||||
import net.runelite.client.plugins.timetracking.TabContentPanel;
|
import net.runelite.client.plugins.timetracking.TabContentPanel;
|
||||||
@@ -130,48 +126,17 @@ public class BirdHouseTabPanel extends TabContentPanel
|
|||||||
panel.getEstimate().setText("Built");
|
panel.getEstimate().setText("Built");
|
||||||
break;
|
break;
|
||||||
case SEEDED:
|
case SEEDED:
|
||||||
long doneEstimate = startTime + BirdHouseTracker.BIRD_HOUSE_DURATION;
|
long remainingTime = startTime + BirdHouseTracker.BIRD_HOUSE_DURATION - unixNow;
|
||||||
if (doneEstimate < unixNow)
|
if (remainingTime <= 0)
|
||||||
{
|
{
|
||||||
panel.getProgress().setValue(BirdHouseTracker.BIRD_HOUSE_DURATION);
|
panel.getProgress().setValue(BirdHouseTracker.BIRD_HOUSE_DURATION);
|
||||||
panel.getProgress().setForeground(COMPLETED_COLOR);
|
panel.getProgress().setForeground(COMPLETED_COLOR);
|
||||||
panel.getEstimate().setText("Done");
|
panel.getEstimate().setText("Done");
|
||||||
}
|
}
|
||||||
else if (config.estimateRelative())
|
|
||||||
{
|
|
||||||
int remainingSeconds = (int) (59 + doneEstimate - unixNow);
|
|
||||||
int remaining = remainingSeconds / 60;
|
|
||||||
panel.getProgress().setValue(BirdHouseTracker.BIRD_HOUSE_DURATION - remainingSeconds);
|
|
||||||
StringBuilder f = new StringBuilder("Done in ");
|
|
||||||
int min = remaining % 60;
|
|
||||||
int hours = (remaining / 60) % 24;
|
|
||||||
int days = remaining / (60 * 24);
|
|
||||||
if (days > 0)
|
|
||||||
{
|
|
||||||
f.append(days).append("d ");
|
|
||||||
}
|
|
||||||
if (hours > 0)
|
|
||||||
{
|
|
||||||
f.append(hours).append("h ");
|
|
||||||
}
|
|
||||||
if (min > 0)
|
|
||||||
{
|
|
||||||
f.append(min).append("m ");
|
|
||||||
}
|
|
||||||
panel.getEstimate().setText(f.toString());
|
|
||||||
}
|
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
StringBuilder f = new StringBuilder();
|
panel.getProgress().setValue((int) (BirdHouseTracker.BIRD_HOUSE_DURATION - remainingTime));
|
||||||
LocalDateTime ldtTime = LocalDateTime.ofEpochSecond(doneEstimate, 0, OffsetDateTime.now().getOffset());
|
panel.getEstimate().setText("Done " + getFormattedEstimate(remainingTime, config.estimateRelative()));
|
||||||
LocalDateTime ldtNow = LocalDateTime.now();
|
|
||||||
f.append("Done ");
|
|
||||||
if (ldtTime.getDayOfWeek() != ldtNow.getDayOfWeek())
|
|
||||||
{
|
|
||||||
f.append(ldtTime.getDayOfWeek().getDisplayName(TextStyle.FULL, Locale.getDefault())).append(" ");
|
|
||||||
}
|
|
||||||
f.append(String.format("at %d:%02d", ldtTime.getHour(), ldtTime.getMinute()));
|
|
||||||
panel.getEstimate().setText(f.toString());
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
|||||||
@@ -78,6 +78,7 @@ public class BirdHouseTracker
|
|||||||
* This is set to {@code 0} if the bird houses have already completed
|
* This is set to {@code 0} if the bird houses have already completed
|
||||||
* when updating it.
|
* when updating it.
|
||||||
*/
|
*/
|
||||||
|
@Getter
|
||||||
private long completionTime = -1;
|
private long completionTime = -1;
|
||||||
|
|
||||||
public BirdHouseTabPanel createBirdHouseTabPanel()
|
public BirdHouseTabPanel createBirdHouseTabPanel()
|
||||||
|
|||||||
Binary file not shown.
|
After Width: | Height: | Size: 122 B |
Reference in New Issue
Block a user