Code Sketch
yoi live
Category: Programming
import java.awt._
import java.awt.event._
import java.awt.geom._
import java.net.{HttpURLConnection, URI, URL}
import java.io.{BufferedReader, InputStreamReader}
import java.nio.charset.StandardCharsets
import java.security.MessageDigest
import java.text.SimpleDateFormat
import java.util.Date
import javax.swing._
import javax.swing.border._
import javax.swing.table.DefaultTableModel
import scala.collection.mutable.ArrayBuffer
// ============================================================
// CRICKET LIVE CENTER
// Scala + Java Swing/AWT
// Kojo friendly - no Scala Swing
// ============================================================
object CricketConfig {
val APP_TITLE = "CRICKET LIVE CENTER"
val HOME_URL =
"https://www.cricbuzz.com/cricket-match/live-scores"
val FALLBACK_URL =
"https://www.cricbuzz.com/cricket-match/live-scores"
val USER_AGENT =
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 " +
"(KHTML, like Gecko) Chrome/153.0.0.0 Safari/537.36"
// SHA-256 of: bixuu@cricketlive
val PASSWORD_HASH =
"07a3ccbb2c0983f152db1bc75b540d945c82372fe1771d71f3c025e89e4bd79b"
val AUTO_REFRESH_MS = 20000
}
// ============================================================
// DATA CLASSES
// ============================================================
case class MatchInfo(
name: String,
url: String,
status: String
)
case class CricketBatter(
name: String,
runs: String,
balls: String,
fours: String,
sixes: String,
sr: String
)
case class CricketBowler(
name: String,
overs: String,
maidens: String,
runs: String,
wickets: String,
economy: String
)
case class LiveScoreData(
matchName: String,
state: String,
score: String,
team: String,
overs: String,
crr: String,
req: String,
target: String,
venue: String,
toss: String,
partnership: String,
striker: String,
nonStriker: String,
currentBowler: String,
lastBall: String,
recentOver: String,
updated: String,
crowd: String,
batters: ArrayBuffer[CricketBatter],
bowlers: ArrayBuffer[CricketBowler],
commentary: ArrayBuffer[String]
)
// ============================================================
// UTILITY
// ============================================================
object TextUtil {
def cleanHtml(s: String): String = {
if (s == null) {
return ""
}
var x = s
x = x.replaceAll("(?is)<script[^>]*>.*?</script>", " ")
x = x.replaceAll("(?is)<style[^>]*>.*?</style>", " ")
x = x.replaceAll("(?is)<br\\s*/?>", " ")
x = x.replaceAll("(?is)</p>", " ")
x = x.replaceAll("(?is)</div>", " ")
x = x.replaceAll("(?is)</li>", " ")
x = x.replaceAll("(?is)</tr>", " ")
x = x.replaceAll("(?is)<[^>]+>", " ")
x = x
.replace(" ", " ")
.replace("&", "&")
.replace(""", "\"")
.replace("'", "'")
.replace("'", "'")
.replace("<", "<")
.replace(">", ">")
x = x.replaceAll("\\s+", " ").trim
x
}
def safe(s: String): String = {
if (s == null || s.trim.isEmpty) "?" else s.trim
}
def shorten(s: String, max: Int): String = {
val x = safe(s)
if (x.length <= max) x
else x.substring(0, max - 1) + "?"
}
def nowTime(): String = {
new SimpleDateFormat("HH:mm:ss").format(new Date())
}
}
// ============================================================
// WEB READER
// ============================================================
object CricketWeb {
def get(urlText: String): String = {
var connection: HttpURLConnection = null
var reader: BufferedReader = null
try {
val url = new URL(urlText)
connection = url.openConnection().asInstanceOf[HttpURLConnection]
connection.setRequestMethod("GET")
connection.setRequestProperty("User-Agent", CricketConfig.USER_AGENT)
connection.setRequestProperty("Accept", "text/html,application/xhtml+xml")
connection.setRequestProperty("Accept-Language", "en-US,en;q=0.9")
connection.setConnectTimeout(10000)
connection.setReadTimeout(15000)
connection.setInstanceFollowRedirects(true)
val code = connection.getResponseCode
if (code < 200 || code >= 400) {
throw new RuntimeException("HTTP " + code)
}
reader = new BufferedReader(
new InputStreamReader(
connection.getInputStream,
StandardCharsets.UTF_8
)
)
val result = new StringBuilder
var line: String = null
line = reader.readLine()
while (line != null) {
result.append(line)
result.append("\n")
line = reader.readLine()
}
result.toString()
} finally {
if (reader != null) {
try reader.close() catch {
case _: Throwable =>
}
}
if (connection != null) {
connection.disconnect()
}
}
}
}
// ============================================================
// PASSWORD
// ============================================================
object PasswordUtil {
def sha256(text: String): String = {
val digest = MessageDigest.getInstance("SHA-256")
val bytes =
digest.digest(text.getBytes(StandardCharsets.UTF_8))
val out = new StringBuilder
bytes.foreach { b =>
out.append(String.format("%02x", Byte.box(b)))
}
out.toString()
}
def verify(password: String): Boolean = {
sha256(password) == CricketConfig.PASSWORD_HASH
}
}
// ============================================================
// MATCH LIST PARSER
// ============================================================
object MatchListParser {
def parse(html: String): ArrayBuffer[MatchInfo] = {
val result = ArrayBuffer[MatchInfo]()
if (html == null || html.isEmpty) {
return result
}
val anchorPattern =
"""(?is)<a[^>]+href=["']([^"']*live-cricket-(?:scores|scorecard)[^"']*)["'][^>]*>(.*?)</a>""".r
anchorPattern.findAllMatchIn(html).foreach { m =>
val href = m.group(1)
val rawText = m.group(2)
val name = TextUtil.cleanHtml(rawText)
if (name.length >= 6) {
val absoluteUrl =
if (href.startsWith("http")) href
else if (href.startsWith("/")) "https://www.cricbuzz.com" + href
else "https://www.cricbuzz.com/" + href
val lower = name.toLowerCase
var status = "MATCH"
if (lower.contains("live")) {
status = "LIVE"
} else if (lower.contains("won") || lower.contains("result")) {
status = "RECENT"
} else if (
lower.contains("today") ||
lower.contains("tomorrow")
) {
status = "UPCOMING"
}
var duplicate = false
result.foreach { existing =>
if (existing.url == absoluteUrl) {
duplicate = true
}
}
if (!duplicate) {
result += MatchInfo(
TextUtil.shorten(name, 90),
absoluteUrl,
status
)
}
}
}
result
}
}
// ============================================================
// LIVE SCORE PARSER
// ============================================================
object LiveParser {
private def first(pattern: String, text: String): String = {
try {
val r = pattern.r
val found = r.findFirstMatchIn(text)
if (found.isDefined) {
found.get.group(1)
} else {
""
}
} catch {
case _: Throwable => ""
}
}
private def all(pattern: String, text: String): ArrayBuffer[String] = {
val out = ArrayBuffer[String]()
try {
pattern.r.findAllMatchIn(text).foreach { m =>
if (m.groupCount >= 1) {
out += m.group(1).trim
}
}
} catch {
case _: Throwable =>
}
out
}
private def guessTeam(text: String): String = {
val scorePattern =
"""([A-Za-z][A-Za-z .&'-]{2,40})\s+(\d{1,4}/\d{1,2})\s*\(?(\d{1,3}(?:\.\d+)?)\s*ov""".r
val matchFound = scorePattern.findFirstMatchIn(text)
if (matchFound.isDefined) {
matchFound.get.group(1).trim
} else {
"Batting Team"
}
}
private def guessScore(text: String): String = {
val pattern1 =
"""(\d{1,4}/\d{1,2})\s*\(?\s*(\d{1,3}(?:\.\d+)?)\s*ov""".r
val m1 = pattern1.findFirstMatchIn(text)
if (m1.isDefined) {
return m1.get.group(1) + " (" + m1.get.group(2) + " ov)"
}
val pattern2 =
"""(\d{1,4}/\d{1,2})\s+(\d{1,3}(?:\.\d+)?)\s*overs?""".r
val m2 = pattern2.findFirstMatchIn(text)
if (m2.isDefined) {
return m2.get.group(1) + " (" + m2.get.group(2) + " ov)"
}
"?"
}
private def guessOvers(text: String): String = {
val p =
"""(?:\d{1,4}/\d{1,2})\s*\((\d{1,3}(?:\.\d+)?)\s*ov\)""".r
val m = p.findFirstMatchIn(text)
if (m.isDefined) {
m.get.group(1) + " overs"
} else {
"?"
}
}
private def guessCRR(text: String): String = {
val p =
"""(?i)(?:current run rate|crr)\s*[:\-]?\s*(\d+(?:\.\d+)?)""".r
val x = first(p.regex, text)
if (x.nonEmpty) x else "?"
}
private def guessREQ(text: String): String = {
val p =
"""(?i)(?:required run rate|req(?:uired)?\.?\s*rr)\s*[:\-]?\s*(\d+(?:\.\d+)?)""".r
val x = first(p.regex, text)
if (x.nonEmpty) x else "?"
}
private def guessTarget(text: String): String = {
val p =
"""(?i)(?:target)\s*[:\-]?\s*(\d{1,4})""".r
val x = first(p.regex, text)
if (x.nonEmpty) x else "?"
}
private def guessVenue(text: String): String = {
val p =
"""(?i)(?:venue|ground|stadium)\s*[:\-]\s*([A-Za-z0-9 ,.'()&-]{5,120})""".r
val x = first(p.regex, text)
if (x.nonEmpty) TextUtil.shorten(x, 100)
else "?"
}
private def guessToss(text: String): String = {
val p =
"""(?i)(?:toss)\s*[:\-]?\s*([A-Za-z0-9 ,.'()&-]{8,150})""".r
val x = first(p.regex, text)
if (x.nonEmpty) TextUtil.shorten(x, 120)
else "?"
}
private def guessPartnership(text: String): String = {
val p =
"""(?i)(?:partnership)\s*[:\-]?\s*(\d+\s*\(\s*\d+\s*\))""".r
val x = first(p.regex, text)
if (x.nonEmpty) x else "?"
}
private def guessLastBall(text: String): String = {
val patterns = Array(
"""(?i)(\d+\.\d+\s+[A-Za-z .'-]+\s+to\s+[A-Za-z .'-]+\s*,\s*[^.]{2,100}\.)""",
"""(?i)(\d+\.\d+\s+[A-Za-z .'-]+\s+to\s+[A-Za-z .'-]+\s*-\s*[^.]{2,100}\.)"""
)
var answer = ""
patterns.foreach { p =>
if (answer.isEmpty) {
answer = first(p, text)
}
}
if (answer.nonEmpty) TextUtil.shorten(answer, 110)
else "?"
}
private def guessStrikers(text: String): (String, String) = {
// Common display form:
// Player Name 45 (38)
val p =
"""([A-Z][A-Za-z.' -]{2,40})\s+(\d{1,3})\s*\(\s*(\d{1,3})\s*\)""".r
val names = ArrayBuffer[String]()
p.findAllMatchIn(text).foreach { m =>
val n = m.group(1).trim
if (!names.contains(n) && n.length >= 3) {
names += n
}
}
if (names.length >= 2) {
(names(0), names(1))
} else if (names.length == 1) {
(names(0), "?")
} else {
("?", "?")
}
}
private def guessBowler(text: String): String = {
val p =
"""(?i)([A-Z][A-Za-z.' -]{2,35})\s+\d+(?:\.\d+)?-\d+-\d+-\d+""".r
val m = p.findFirstMatchIn(text)
if (m.isDefined) {
m.get.group(1).trim
} else {
"?"
}
}
private def guessRecentOver(text: String): String = {
val p =
"""(?i)(?:recent over|this over|last over)\s*[:\-]?\s*([0-9WwNnBb+. -]{3,50})""".r
val x = first(p.regex, text)
if (x.nonEmpty) TextUtil.shorten(x, 35)
else "?"
}
private def guessState(text: String): String = {
val lower = text.toLowerCase
if (lower.contains("live")) {
"LIVE"
} else if (
lower.contains("match yet to begin") ||
lower.contains("upcoming") ||
lower.contains("starts at")
) {
"UPCOMING"
} else if (
lower.contains("won by") ||
lower.contains("result")
) {
"COMPLETED"
} else {
"MATCH"
}
}
private def parseBatters(text: String): ArrayBuffer[CricketBatter] = {
val result = ArrayBuffer[CricketBatter]()
val p =
"""([A-Z][A-Za-z.' -]{2,45})\s+(\d{1,3})\s+(\d{1,3})\s+(\d+)\s+(\d+)\s+(\d+(?:\.\d+)?)""".r
p.findAllMatchIn(text).foreach { m =>
val name = m.group(1).trim
if (
result.length < 12 &&
!name.toLowerCase.contains("player") &&
!name.toLowerCase.contains("batter")
) {
result += CricketBatter(
name,
m.group(2),
m.group(3),
m.group(4),
m.group(5),
m.group(6)
)
}
}
result
}
private def parseBowlers(text: String): ArrayBuffer[CricketBowler] = {
val result = ArrayBuffer[CricketBowler]()
val p =
"""([A-Z][A-Za-z.' -]{2,45})\s+(\d+(?:\.\d+)?)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+(?:\.\d+)?)""".r
p.findAllMatchIn(text).foreach { m =>
val name = m.group(1).trim
if (
result.length < 10 &&
!name.toLowerCase.contains("bowler")
) {
result += CricketBowler(
name,
m.group(2),
m.group(3),
m.group(4),
m.group(5),
m.group(6)
)
}
}
result
}
private def parseCommentary(text: String): ArrayBuffer[String] = {
val result = ArrayBuffer[String]()
val p =
"""(\d+\.\d+\s+[A-Za-z][A-Za-z.' -]{2,35}\s+to\s+[A-Za-z][A-Za-z.' -]{2,35}[^.]{1,160}\.)""".r
p.findAllMatchIn(text).foreach { m =>
val line = TextUtil.shorten(m.group(1).trim, 180)
if (!result.contains(line)) {
result += line
}
}
if (result.isEmpty) {
val fallback =
"""(?i)((?:FOUR|SIX|OUT|WICKET|NO BALL|WIDE|SINGLE|DOUBLE|DOT)[^.]{0,150}\.)""".r
fallback.findAllMatchIn(text).foreach { m =>
val line = m.group(1).trim
if (!result.contains(line)) {
result += line
}
}
}
val maxItems = 20
if (result.length > maxItems) {
result.takeRight(maxItems)
} else {
result
}
}
def parse(matchName: String, url: String, html: String): LiveScoreData = {
val clean = TextUtil.cleanHtml(html)
val score = guessScore(clean)
val overs = guessOvers(clean)
val crr = guessCRR(clean)
val req = guessREQ(clean)
val target = guessTarget(clean)
val team = guessTeam(clean)
val state = guessState(clean)
val venue = guessVenue(clean)
val toss = guessToss(clean)
val partnership = guessPartnership(clean)
val strikers = guessStrikers(clean)
val striker = strikers._1
val nonStriker = strikers._2
val currentBowler = guessBowler(clean)
val lastBall = guessLastBall(clean)
val recentOver = guessRecentOver(clean)
val comments = parseCommentary(clean)
val batters = parseBatters(clean)
val bowlers = parseBowlers(clean)
LiveScoreData(
TextUtil.safe(matchName),
state,
score,
team,
overs,
crr,
req,
target,
venue,
toss,
partnership,
striker,
nonStriker,
currentBowler,
lastBall,
recentOver,
TextUtil.nowTime(),
"Crowd has really picked up now. Lots of noise, good atmosphere.",
batters,
bowlers,
comments
)
}
}
// ============================================================
// COLORS
// ============================================================
object UIColors {
val bg = new Color(10, 14, 24)
val panel = new Color(18, 24, 38)
val panel2 = new Color(24, 31, 48)
val panel3 = new Color(29, 37, 57)
val white = new Color(245, 247, 250)
val muted = new Color(160, 169, 184)
val accent = new Color(45, 190, 120)
val blue = new Color(55, 145, 255)
val orange = new Color(242, 159, 67)
val red = new Color(235, 84, 84)
val yellow = new Color(245, 205, 75)
val border = new Color(49, 60, 82)
val greenDark = new Color(20, 68, 52)
}
// ============================================================
// ROUNDED PANEL
// ============================================================
class RoundedPanel(radius: Int, fill: Color) extends JPanel {
setOpaque(false)
override protected def paintComponent(g: Graphics): Unit = {
val g2 = g.asInstanceOf[Graphics2D]
g2.setRenderingHint(
RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON
)
g2.setColor(fill)
g2.fillRoundRect(
0,
0,
getWidth(),
getHeight(),
radius,
radius
)
super.paintComponent(g)
}
}
// ============================================================
// GLOW BUTTON
// ============================================================
class ModernButton(textValue: String) extends JButton(textValue) {
setFocusPainted(false)
setBorderPainted(false)
setContentAreaFilled(false)
setForeground(UIColors.white)
setFont(new Font("Arial", Font.BOLD, 13))
setCursor(new Cursor(Cursor.HAND_CURSOR))
setOpaque(false)
addMouseListener(new MouseAdapter {
override def mouseEntered(e: MouseEvent): Unit = {
setForeground(UIColors.accent)
repaint()
}
override def mouseExited(e: MouseEvent): Unit = {
setForeground(UIColors.white)
repaint()
}
})
override protected def paintComponent(g: Graphics): Unit = {
val g2 = g.asInstanceOf[Graphics2D]
g2.setRenderingHint(
RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON
)
g2.setColor(UIColors.panel3)
g2.fillRoundRect(
0,
0,
getWidth(),
getHeight(),
14,
14
)
super.paintComponent(g)
}
}
// ============================================================
// DASHBOARD CARD
// ============================================================
class InfoCard(
caption: String,
initialValue: String
) extends RoundedPanel(18, UIColors.panel2) {
private val captionLabel =
new JLabel(caption.toUpperCase)
private val valueLabel =
new JLabel(initialValue)
setLayout(new BorderLayout())
captionLabel.setForeground(UIColors.muted)
captionLabel.setFont(new Font("Arial", Font.BOLD, 11))
valueLabel.setForeground(UIColors.white)
valueLabel.setFont(new Font("Arial", Font.BOLD, 17))
val top =
new JPanel(new BorderLayout())
top.setOpaque(false)
top.setBorder(new EmptyBorder(14, 16, 0, 16))
top.add(captionLabel, BorderLayout.WEST)
add(top, BorderLayout.NORTH)
val center =
new JPanel(new BorderLayout())
center.setOpaque(false)
center.setBorder(new EmptyBorder(8, 16, 14, 16))
center.add(valueLabel, BorderLayout.CENTER)
add(center, BorderLayout.CENTER)
}
// ============================================================
// RUN GRAPH
// ============================================================
class RunGraph extends JPanel {
private val runs = ArrayBuffer[Int](
0, 1, 4, 5, 8, 8, 14, 15, 20, 26,
29, 31, 36, 38, 42, 45, 49, 54, 58
)
setPreferredSize(new Dimension(700, 230))
setOpaque(false)
def updateData(newData: ArrayBuffer[Int]): Unit = {
runs.clear()
runs ++= newData
if (runs.isEmpty) {
runs += 0
}
repaint()
}
override protected def paintComponent(g: Graphics): Unit = {
val g2 = g.asInstanceOf[Graphics2D]
g2.setRenderingHint(
RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON
)
val w = getWidth()
val h = getHeight()
g2.setColor(UIColors.panel2)
g2.fillRoundRect(0, 0, w, h, 18, 18)
val left = 40
val top = 28
val right = w - 24
val bottom = h - 35
g2.setColor(UIColors.border)
var grid = 0
while (grid <= 5) {
val y =
bottom - ((bottom - top) * grid / 5)
g2.drawLine(left, y, right, y)
grid += 1
}
var maxValue = 10
runs.foreach { n =>
if (n > maxValue) {
maxValue = n
}
}
val denominator =
if (runs.length <= 1) 1 else runs.length - 1
var oldX = left
var oldY = bottom
var i = 0
while (i < runs.length) {
val x =
left + ((right - left) * i / denominator)
val y =
bottom -
((bottom - top) * runs(i) / maxValue)
g2.setColor(UIColors.accent)
if (i > 0) {
g2.drawLine(oldX, oldY, x, y)
}
g2.fillOval(x - 4, y - 4, 8, 8)
oldX = x
oldY = y
i += 1
}
g2.setColor(UIColors.muted)
g2.setFont(new Font("Arial", Font.PLAIN, 11))
g2.drawString("RUN PROGRESSION", 16, 18)
}
}
// ============================================================
// SCORE STRIP
// ============================================================
class TVScoreStrip extends JPanel {
setPreferredSize(new Dimension(1000, 92))
setLayout(new GridLayout(1, 5, 1, 0))
setBackground(UIColors.bg)
private val strikerPanel =
createBox("STRIKER")
private val nonStrikerPanel =
createBox("NON-STRIKER")
private val bowlerPanel =
createBox("CURRENT BOWLER")
private val lastBallPanel =
createBox("LAST BALL")
private val overPanel =
createBox("RECENT OVER")
add(strikerPanel._1)
add(nonStrikerPanel._1)
add(bowlerPanel._1)
add(lastBallPanel._1)
add(overPanel._1)
private def createBox(
heading: String
): (JPanel, JLabel) = {
val p =
new JPanel(new BorderLayout())
p.setBackground(UIColors.panel)
p.setBorder(
new CompoundBorder(
new MatteBorder(0, 1, 0, 0, UIColors.border),
new EmptyBorder(8, 12, 8, 12)
)
)
val head =
new JLabel(heading)
head.setForeground(UIColors.muted)
head.setFont(new Font("Arial", Font.BOLD, 10))
val value =
new JLabel("?")
value.setForeground(UIColors.white)
value.setFont(new Font("Arial", Font.BOLD, 14))
p.add(head, BorderLayout.NORTH)
p.add(value, BorderLayout.CENTER)
(p, value)
}
def setValues(data: LiveScoreData): Unit = {
strikerPanel._2.setText(
TextUtil.shorten(data.striker, 24)
)
nonStrikerPanel._2.setText(
TextUtil.shorten(data.nonStriker, 24)
)
bowlerPanel._2.setText(
TextUtil.shorten(data.currentBowler, 24)
)
lastBallPanel._2.setText(
TextUtil.shorten(data.lastBall, 32)
)
overPanel._2.setText(
TextUtil.shorten(data.recentOver, 32)
)
}
}
// ============================================================
// LOGIN WINDOW
// ============================================================
class LoginWindow(onSuccess: () => Unit) extends JFrame {
setTitle(CricketConfig.APP_TITLE)
setSize(480, 380)
setLocationRelativeTo(null)
setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE)
setResizable(false)
val root =
new JPanel(new BorderLayout())
root.setBackground(UIColors.bg)
setContentPane(root)
val center =
new JPanel()
center.setOpaque(false)
center.setLayout(
new BoxLayout(center, BoxLayout.Y_AXIS)
)
center.setBorder(new EmptyBorder(35, 45, 25, 45))
val appName =
new JLabel("CRICKET LIVE CENTER")
appName.setAlignmentX(Component.CENTER_ALIGNMENT)
appName.setForeground(UIColors.white)
appName.setFont(new Font("Arial", Font.BOLD, 27))
val subtitle =
new JLabel("Live cricket dashboard")
subtitle.setAlignmentX(Component.CENTER_ALIGNMENT)
subtitle.setForeground(UIColors.muted)
subtitle.setFont(new Font("Arial", Font.PLAIN, 14))
val usernameLabel =
new JLabel("USERNAME")
usernameLabel.setAlignmentX(Component.CENTER_ALIGNMENT)
usernameLabel.setForeground(UIColors.muted)
usernameLabel.setFont(new Font("Arial", Font.BOLD, 11))
val username =
new JTextField()
username.setMaximumSize(new Dimension(330, 40))
username.setBackground(UIColors.panel2)
username.setForeground(UIColors.white)
username.setCaretColor(UIColors.white)
username.setBorder(
new CompoundBorder(
new LineBorder(UIColors.border, 1, true),
new EmptyBorder(6, 10, 6, 10)
)
)
val passwordLabel =
new JLabel("PASSWORD")
passwordLabel.setAlignmentX(Component.CENTER_ALIGNMENT)
passwordLabel.setForeground(UIColors.muted)
passwordLabel.setFont(new Font("Arial", Font.BOLD, 11))
val password =
new JPasswordField()
password.setMaximumSize(new Dimension(330, 40))
password.setBackground(UIColors.panel2)
password.setForeground(UIColors.white)
password.setCaretColor(UIColors.white)
password.setBorder(
new CompoundBorder(
new LineBorder(UIColors.border, 1, true),
new EmptyBorder(6, 10, 6, 10)
)
)
val loginButton =
new ModernButton("LOGIN")
loginButton.setAlignmentX(Component.CENTER_ALIGNMENT)
loginButton.setPreferredSize(new Dimension(160, 42))
loginButton.setMaximumSize(new Dimension(160, 42))
val message =
new JLabel(" ")
message.setAlignmentX(Component.CENTER_ALIGNMENT)
message.setForeground(UIColors.red)
message.setFont(new Font("Arial", Font.BOLD, 12))
center.add(appName)
center.add(Box.createVerticalStrut(5))
center.add(subtitle)
center.add(Box.createVerticalStrut(28))
center.add(usernameLabel)
center.add(Box.createVerticalStrut(5))
center.add(username)
center.add(Box.createVerticalStrut(13))
center.add(passwordLabel)
center.add(Box.createVerticalStrut(5))
center.add(password)
center.add(Box.createVerticalStrut(22))
center.add(loginButton)
center.add(Box.createVerticalStrut(10))
center.add(message)
root.add(center, BorderLayout.CENTER)
def doLogin(): Unit = {
val entered =
new String(password.getPassword)
if (PasswordUtil.verify(entered)) {
message.setForeground(UIColors.accent)
message.setText("LOGIN SUCCESS")
loginButton.setEnabled(false)
SwingUtilities.invokeLater(new Runnable {
def run(): Unit = {
dispose()
onSuccess()
}
})
} else {
message.setForeground(UIColors.red)
message.setText("Wrong password")
password.setText("")
}
}
loginButton.addActionListener(
new ActionListener {
def actionPerformed(e: ActionEvent): Unit = {
doLogin()
}
}
)
password.addActionListener(
new ActionListener {
def actionPerformed(e: ActionEvent): Unit = {
doLogin()
}
}
)
}
// ============================================================
// MAIN WINDOW
// ============================================================
class CricketLiveCenter extends JFrame {
private val allMatches =
ArrayBuffer[MatchInfo]()
private var selectedMatch: MatchInfo =
MatchInfo(
"Live Cricket",
CricketConfig.FALLBACK_URL,
"LIVE"
)
private var latestData:
LiveScoreData = LiveScoreData(
"Live Cricket",
"LIVE",
"?",
"?",
"?",
"?",
"?",
"?",
"?",
"?",
"?",
"?",
"?",
"?",
"?",
"?",
TextUtil.nowTime(),
"Crowd has really picked up now. Lots of noise, good atmosphere.",
ArrayBuffer[CricketBatter](),
ArrayBuffer[CricketBowler](),
ArrayBuffer[String]()
)
private val statusLabel =
new JLabel("Starting...")
private val matchCountLabel =
new JLabel("0 MATCHES")
private val selectedMatchLabel =
new JLabel("LIVE CRICKET")
private val bigScoreLabel =
new JLabel("?")
private val bigTeamLabel =
new JLabel("?")
private val stateLabel =
new JLabel("LIVE")
private val crrLabel =
new JLabel("CRR ?")
private val reqLabel =
new JLabel("REQ ?")
private val targetLabel =
new JLabel("TARGET ?")
private val venueLabel =
new JLabel("Venue: ?")
private val tossLabel =
new JLabel("Toss: ?")
private val partnershipLabel =
new JLabel("Partnership: ?")
private val updatedLabel =
new JLabel("Updated: ?")
private val sourceButton =
new ModernButton("SOURCE")
private val refreshButton =
new ModernButton("REFRESH NOW")
private val filterCombo =
new JComboBox[String](
Array(
"ALL",
"LIVE",
"UPCOMING",
"RECENT"
)
)
private val matchList =
new JPanel()
private val contentCards =
new JPanel()
private val cardStack =
new java.awt.CardLayout()
private val scorecardTableModel =
new DefaultTableModel(
Array[Object](
"BATTER",
"R",
"B",
"4s",
"6s",
"SR"
),
0
) {
override def isCellEditable(
row: Int,
column: Int
): Boolean = false
}
private val scorecardTable =
new JTable(scorecardTableModel)
private val bowlersTableModel =
new DefaultTableModel(
Array[Object](
"BOWLER",
"O",
"M",
"R",
"W",
"ECON"
),
0
) {
override def isCellEditable(
row: Int,
column: Int
): Boolean = false
}
private val bowlersTable =
new JTable(bowlersTableModel)
private val commentaryArea =
new JTextArea()
private val runGraph =
new RunGraph()
private val tvStrip =
new TVScoreStrip()
private val autoTimer =
new javax.swing.Timer(
CricketConfig.AUTO_REFRESH_MS,
new ActionListener {
def actionPerformed(e: ActionEvent): Unit = {
refreshSelectedMatch()
}
}
)
setTitle(CricketConfig.APP_TITLE)
setSize(1420, 900)
setMinimumSize(new Dimension(1100, 720))
setLocationRelativeTo(null)
setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE)
buildWindow()
// ==========================================================
// BUILD WINDOW
// ==========================================================
private def buildWindow(): Unit = {
val root =
new JPanel(new BorderLayout())
root.setBackground(UIColors.bg)
setContentPane(root)
root.add(createTopBar(), BorderLayout.NORTH)
root.add(createCenterArea(), BorderLayout.CENTER)
root.add(tvStrip, BorderLayout.SOUTH)
autoTimer.start()
SwingUtilities.invokeLater(
new Runnable {
def run(): Unit = {
loadMatchList()
}
}
)
}
// ==========================================================
// TOP BAR
// ==========================================================
private def createTopBar(): JPanel = {
val bar =
new JPanel(new BorderLayout())
bar.setBackground(UIColors.panel)
bar.setBorder(
new MatteBorder(
0,
0,
1,
0,
UIColors.border
)
)
bar.setPreferredSize(new Dimension(1000, 78))
val left =
new JPanel()
left.setOpaque(false)
left.setLayout(
new BoxLayout(left, BoxLayout.Y_AXIS)
)
left.setBorder(
new EmptyBorder(10, 20, 10, 10)
)
val titleLabel =
new JLabel(CricketConfig.APP_TITLE)
titleLabel.setForeground(UIColors.white)
titleLabel.setFont(
new Font("Arial", Font.BOLD, 23)
)
val subtitle =
new JLabel(
"LIVE SCORES ? COMMENTARY ? SCORECARD ? ANALYTICS"
)
subtitle.setForeground(UIColors.muted)
subtitle.setFont(
new Font("Arial", Font.PLAIN, 11)
)
left.add(titleLabel)
left.add(Box.createVerticalStrut(3))
left.add(subtitle)
val right =
new JPanel(
new FlowLayout(
FlowLayout.RIGHT,
8,
15
)
)
right.setOpaque(false)
filterCombo.setBackground(UIColors.panel2)
filterCombo.setForeground(UIColors.white)
filterCombo.setFocusable(false)
filterCombo.addActionListener(
new ActionListener {
def actionPerformed(e: ActionEvent): Unit = {
rebuildMatchList()
}
}
)
sourceButton.addActionListener(
new ActionListener {
def actionPerformed(e: ActionEvent): Unit = {
openSource()
}
}
)
refreshButton.addActionListener(
new ActionListener {
def actionPerformed(e: ActionEvent): Unit = {
refreshSelectedMatch()
}
}
)
right.add(filterCombo)
right.add(sourceButton)
right.add(refreshButton)
bar.add(left, BorderLayout.WEST)
bar.add(right, BorderLayout.EAST)
bar
}
// ==========================================================
// CENTER AREA
// ==========================================================
private def createCenterArea(): JPanel = {
val outer =
new JPanel(new BorderLayout())
outer.setBackground(UIColors.bg)
outer.add(
createSidebar(),
BorderLayout.WEST
)
outer.add(
createMainContent(),
BorderLayout.CENTER
)
outer
}
// ==========================================================
// SIDEBAR
// ==========================================================
private def createSidebar(): JPanel = {
val sidebar =
new JPanel(new BorderLayout())
sidebar.setBackground(UIColors.panel)
sidebar.setPreferredSize(new Dimension(340, 100))
val heading =
new JPanel(new BorderLayout())
heading.setOpaque(false)
heading.setBorder(
new EmptyBorder(15, 14, 12, 14)
)
val liveTitle =
new JLabel("MATCHES")
liveTitle.setForeground(UIColors.white)
liveTitle.setFont(
new Font("Arial", Font.BOLD, 15)
)
matchCountLabel.setForeground(UIColors.muted)
matchCountLabel.setFont(
new Font("Arial", Font.BOLD, 10)
)
heading.add(
liveTitle,
BorderLayout.WEST
)
heading.add(
matchCountLabel,
BorderLayout.EAST
)
matchList.setOpaque(true)
matchList.setBackground(UIColors.panel)
matchList.setLayout(
new BoxLayout(
matchList,
BoxLayout.Y_AXIS
)
)
val scroll =
new JScrollPane(matchList)
scroll.setBorder(null)
scroll.getVerticalScrollBar.setUnitIncrement(16)
sidebar.add(
heading,
BorderLayout.NORTH
)
sidebar.add(
scroll,
BorderLayout.CENTER
)
sidebar
}
// ==========================================================
// MATCH LIST
// ==========================================================
private def rebuildMatchList(): Unit = {
matchList.removeAll()
val selectedFilter =
String.valueOf(filterCombo.getSelectedItem)
var shown = 0
allMatches.foreach { m =>
val statusUpper =
m.status.toUpperCase
var allowed = false
if (selectedFilter == "ALL") {
allowed = true
} else if (selectedFilter == statusUpper) {
allowed = true
}
if (allowed) {
matchList.add(
createMatchButton(m)
)
matchList.add(
Box.createVerticalStrut(5)
)
shown += 1
}
}
if (shown == 0) {
val empty =
new JLabel(
"<html><div style='padding:15px'>" +
"No matches found.<br>" +
"Press REFRESH NOW.</div></html>"
)
empty.setForeground(UIColors.muted)
empty.setAlignmentX(Component.LEFT_ALIGNMENT)
matchList.add(empty)
}
matchCountLabel.setText(
shown + " MATCHES"
)
matchList.revalidate()
matchList.repaint()
}
private def createMatchButton(
m: MatchInfo
): JPanel = {
val selected =
m.url == selectedMatch.url
val bg =
if (selected)
UIColors.greenDark
else
UIColors.panel2
val box =
new JPanel(new BorderLayout())
box.setBackground(bg)
box.setMaximumSize(
new Dimension(
330,
83
)
)
box.setBorder(
new CompoundBorder(
new LineBorder(
if (selected)
UIColors.accent
else
UIColors.border,
1,
true
),
new EmptyBorder(
10,
11,
10,
11
)
)
)
box.setCursor(
new Cursor(Cursor.HAND_CURSOR)
)
val name =
new JLabel(
"<html>" +
TextUtil.shorten(m.name, 52) +
"</html>"
)
name.setForeground(UIColors.white)
name.setFont(
new Font("Arial", Font.BOLD, 12)
)
val bottom =
new JPanel(new BorderLayout())
bottom.setOpaque(false)
val state =
new JLabel(
m.status.toUpperCase
)
state.setFont(
new Font("Arial", Font.BOLD, 10)
)
if (m.status.toUpperCase == "LIVE") {
state.setForeground(UIColors.accent)
} else if (m.status.toUpperCase == "UPCOMING") {
state.setForeground(UIColors.orange)
} else {
state.setForeground(UIColors.muted)
}
val click =
new JLabel("OPEN ?")
click.setForeground(UIColors.muted)
click.setFont(
new Font("Arial", Font.BOLD, 9)
)
bottom.add(
state,
BorderLayout.WEST
)
bottom.add(
click,
BorderLayout.EAST
)
box.add(
name,
BorderLayout.CENTER
)
box.add(
bottom,
BorderLayout.SOUTH
)
box.addMouseListener(
new MouseAdapter {
override def mouseClicked(
e: MouseEvent
): Unit = {
selectedMatch = m
selectedMatchLabel.setText(
TextUtil.shorten(
m.name.toUpperCase,
85
)
)
rebuildMatchList()
refreshSelectedMatch()
}
override def mouseEntered(
e: MouseEvent
): Unit = {
box.setBorder(
new CompoundBorder(
new LineBorder(
UIColors.accent,
1,
true
),
new EmptyBorder(
10,
11,
10,
11
)
)
)
}
override def mouseExited(
e: MouseEvent
): Unit = {
box.setBorder(
new CompoundBorder(
new LineBorder(
if (selectedMatch.url == m.url)
UIColors.accent
else
UIColors.border,
1,
true
),
new EmptyBorder(
10,
11,
10,
11
)
)
)
}
}
)
box
}
// ==========================================================
// MAIN CONTENT
// ==========================================================
private def createMainContent(): JPanel = {
val root =
new JPanel(new BorderLayout())
root.setBackground(UIColors.bg)
root.setBorder(
new EmptyBorder(
15,
15,
15,
15
)
)
root.add(
createScoreHeader(),
BorderLayout.NORTH
)
contentCards.setOpaque(false)
contentCards.setLayout(cardStack)
contentCards.add(
createOverviewPanel(),
"overview"
)
contentCards.add(
createScorecardPanel(),
"scorecard"
)
contentCards.add(
createCommentaryPanel(),
"commentary"
)
contentCards.add(
createAnalyticsPanel(),
"analytics"
)
root.add(
contentCards,
BorderLayout.CENTER
)
root
}
// ==========================================================
// SCORE HEADER
// ==========================================================
private def createScoreHeader(): JPanel = {
val root =
new RoundedPanel(
20,
UIColors.panel
)
root.setBorder(
new EmptyBorder(
18,
20,
18,
20
)
)
root.setLayout(
new BorderLayout()
)
val left =
new JPanel()
left.setOpaque(false)
left.setLayout(
new BoxLayout(
left,
BoxLayout.Y_AXIS
)
)
selectedMatchLabel.setForeground(UIColors.white)
selectedMatchLabel.setFont(
new Font("Arial", Font.BOLD, 20)
)
bigTeamLabel.setForeground(UIColors.muted)
bigTeamLabel.setFont(
new Font("Arial", Font.BOLD, 13)
)
bigScoreLabel.setForeground(UIColors.white)
bigScoreLabel.setFont(
new Font("Arial", Font.BOLD, 44)
)
venueLabel.setForeground(UIColors.muted)
venueLabel.setFont(
new Font("Arial", Font.PLAIN, 11)
)
tossLabel.setForeground(UIColors.muted)
tossLabel.setFont(
new Font("Arial", Font.PLAIN, 11)
)
left.add(selectedMatchLabel)
left.add(Box.createVerticalStrut(5))
left.add(bigTeamLabel)
left.add(Box.createVerticalStrut(2))
left.add(bigScoreLabel)
left.add(Box.createVerticalStrut(5))
left.add(venueLabel)
left.add(Box.createVerticalStrut(3))
left.add(tossLabel)
val right =
new JPanel()
right.setOpaque(false)
right.setLayout(
new BoxLayout(
right,
BoxLayout.Y_AXIS
)
)
stateLabel.setHorizontalAlignment(
SwingConstants.CENTER
)
stateLabel.setForeground(
UIColors.accent
)
stateLabel.setFont(
new Font(
"Arial",
Font.BOLD,
12
)
)
stateLabel.setBorder(
new CompoundBorder(
new LineBorder(
UIColors.accent,
1,
true
),
new EmptyBorder(
6,
15,
6,
15
)
)
)
crrLabel.setForeground(UIColors.white)
reqLabel.setForeground(UIColors.white)
targetLabel.setForeground(UIColors.white)
partnershipLabel.setForeground(UIColors.white)
updatedLabel.setForeground(UIColors.muted)
crrLabel.setFont(
new Font("Arial", Font.BOLD, 13)
)
reqLabel.setFont(
new Font("Arial", Font.BOLD, 13)
)
targetLabel.setFont(
new Font("Arial", Font.BOLD, 13)
)
partnershipLabel.setFont(
new Font("Arial", Font.BOLD, 13)
)
updatedLabel.setFont(
new Font("Arial", Font.PLAIN, 10)
)
stateLabel.setAlignmentX(
Component.RIGHT_ALIGNMENT
)
right.add(stateLabel)
right.add(Box.createVerticalStrut(12))
right.add(crrLabel)
right.add(Box.createVerticalStrut(5))
right.add(reqLabel)
right.add(Box.createVerticalStrut(5))
right.add(targetLabel)
right.add(Box.createVerticalStrut(5))
right.add(partnershipLabel)
right.add(Box.createVerticalStrut(8))
right.add(updatedLabel)
root.add(
left,
BorderLayout.WEST
)
root.add(
right,
BorderLayout.EAST
)
val tabs =
new JPanel(
new FlowLayout(
FlowLayout.LEFT,
6,
0
)
)
tabs.setOpaque(false)
val overviewButton =
new ModernButton("OVERVIEW")
val scorecardButton =
new ModernButton("SCORECARD")
val commentaryButton =
new ModernButton("COMMENTARY")
val analyticsButton =
new ModernButton("ANALYTICS")
overviewButton.addActionListener(
new ActionListener {
def actionPerformed(e: ActionEvent): Unit = {
cardStack.show(
contentCards,
"overview"
)
}
}
)
scorecardButton.addActionListener(
new ActionListener {
def actionPerformed(e: ActionEvent): Unit = {
cardStack.show(
contentCards,
"scorecard"
)
}
}
)
commentaryButton.addActionListener(
new ActionListener {
def actionPerformed(e: ActionEvent): Unit = {
cardStack.show(
contentCards,
"commentary"
)
}
}
)
analyticsButton.addActionListener(
new ActionListener {
def actionPerformed(e: ActionEvent): Unit = {
cardStack.show(
contentCards,
"analytics"
)
}
}
)
tabs.add(overviewButton)
tabs.add(scorecardButton)
tabs.add(commentaryButton)
tabs.add(analyticsButton)
val wrapper =
new JPanel(new BorderLayout())
wrapper.setOpaque(false)
wrapper.add(root, BorderLayout.CENTER)
wrapper.add(tabs, BorderLayout.SOUTH)
wrapper
}
// ==========================================================
// OVERVIEW
// ==========================================================
private def createOverviewPanel(): JPanel = {
val root =
new JPanel(new BorderLayout())
root.setOpaque(false)
root.setBorder(
new EmptyBorder(
12,
0,
0,
0
)
)
val cards =
new JPanel(
new GridLayout(
2,
4,
10,
10
)
)
cards.setOpaque(false)
cards.add(
new InfoCard(
"Striker",
"?"
)
)
cards.add(
new InfoCard(
"Non-Striker",
"?"
)
)
cards.add(
new InfoCard(
"Current Bowler",
"?"
)
)
cards.add(
new InfoCard(
"Last Ball",
"?"
)
)
cards.add(
new InfoCard(
"Recent Over",
"?"
)
)
cards.add(
new InfoCard(
"Partnership",
"?"
)
)
cards.add(
new InfoCard(
"Target",
"?"
)
)
cards.add(
new InfoCard(
"Live Update",
"Auto"
)
)
root.add(
cards,
BorderLayout.NORTH
)
val info =
new RoundedPanel(
18,
UIColors.panel2
)
info.setBorder(
new EmptyBorder(
15,
18,
15,
18
)
)
info.setLayout(
new BoxLayout(
info,
BoxLayout.Y_AXIS
)
)
val heading =
new JLabel(
"LIVE MATCH INFORMATION"
)
heading.setForeground(
UIColors.white
)
heading.setFont(
new Font(
"Arial",
Font.BOLD,
14
)
)
val line1 =
new JLabel(
"Direct webpage reader: Cricbuzz"
)
line1.setForeground(
UIColors.muted
)
line1.setFont(
new Font(
"Arial",
Font.PLAIN,
12
)
)
val line2 =
new JLabel(
"Auto refresh: every 20 seconds"
)
line2.setForeground(
UIColors.muted
)
line2.setFont(
new Font(
"Arial",
Font.PLAIN,
12
)
)
val line3 =
new JLabel(
"The app displays only information successfully read from the source."
)
line3.setForeground(
UIColors.muted
)
line3.setFont(
new Font(
"Arial",
Font.PLAIN,
12
)
)
info.add(heading)
info.add(Box.createVerticalStrut(10))
info.add(line1)
info.add(Box.createVerticalStrut(5))
info.add(line2)
info.add(Box.createVerticalStrut(5))
info.add(line3)
root.add(
info,
BorderLayout.CENTER
)
root
}
// ==========================================================
// SCORECARD
// ==========================================================
private def styleTable(
table: JTable
): Unit = {
table.setRowHeight(30)
table.setShowVerticalLines(false)
table.setShowHorizontalLines(true)
table.setGridColor(UIColors.border)
table.setBackground(UIColors.panel2)
table.setForeground(UIColors.white)
table.getTableHeader.setBackground(
UIColors.panel3
)
table.getTableHeader.setForeground(
UIColors.muted
)
table.getTableHeader.setFont(
new Font(
"Arial",
Font.BOLD,
11
)
)
table.setFont(
new Font(
"Arial",
Font.PLAIN,
12
)
)
}
private def createScorecardPanel(): JPanel = {
val root =
new JPanel(new BorderLayout())
root.setOpaque(false)
root.setBorder(
new EmptyBorder(
12,
0,
0,
0
)
)
styleTable(scorecardTable)
styleTable(bowlersTable)
val battingTitle =
new JLabel("BATTING")
battingTitle.setForeground(
UIColors.white
)
battingTitle.setFont(
new Font(
"Arial",
Font.BOLD,
14
)
)
val bowlingTitle =
new JLabel("BOWLING")
bowlingTitle.setForeground(
UIColors.white
)
bowlingTitle.setFont(
new Font(
"Arial",
Font.BOLD,
14
)
)
val battingBox =
new JPanel(new BorderLayout())
battingBox.setOpaque(false)
battingBox.add(
battingTitle,
BorderLayout.NORTH
)
battingBox.add(
new JScrollPane(
scorecardTable
),
BorderLayout.CENTER
)
val bowlingBox =
new JPanel(new BorderLayout())
bowlingBox.setOpaque(false)
bowlingBox.add(
bowlingTitle,
BorderLayout.NORTH
)
bowlingBox.add(
new JScrollPane(
bowlersTable
),
BorderLayout.CENTER
)
val split =
new JSplitPane(
JSplitPane.VERTICAL_SPLIT,
battingBox,
bowlingBox
)
split.setResizeWeight(0.58)
split.setBorder(null)
root.add(
split,
BorderLayout.CENTER
)
root
}
// ==========================================================
// COMMENTARY
// ==========================================================
private def createCommentaryPanel(): JPanel = {
val root =
new JPanel(new BorderLayout())
root.setOpaque(false)
root.setBorder(
new EmptyBorder(
12,
0,
0,
0
)
)
commentaryArea.setEditable(false)
commentaryArea.setLineWrap(true)
commentaryArea.setWrapStyleWord(true)
commentaryArea.setBackground(
UIColors.panel2
)
commentaryArea.setForeground(
UIColors.white
)
commentaryArea.setCaretColor(
UIColors.white
)
commentaryArea.setFont(
new Font(
"Consolas",
Font.PLAIN,
13
)
)
commentaryArea.setBorder(
new EmptyBorder(
15,
15,
15,
15
)
)
root.add(
new JScrollPane(
commentaryArea
),
BorderLayout.CENTER
)
root
}
// ==========================================================
// ANALYTICS
// ==========================================================
private def createAnalyticsPanel(): JPanel = {
val root =
new JPanel(new BorderLayout())
root.setOpaque(false)
root.setBorder(
new EmptyBorder(
12,
0,
0,
0
)
)
root.add(
runGraph,
BorderLayout.CENTER
)
root
}
// ==========================================================
// MATCH LIST LOADING
// ==========================================================
private def loadMatchList(): Unit = {
statusLabel.setText(
"Loading matches..."
)
val worker =
new SwingWorker[ArrayBuffer[MatchInfo], Void] {
override def doInBackground()
: ArrayBuffer[MatchInfo] = {
try {
val html =
CricketWeb.get(
CricketConfig.HOME_URL
)
MatchListParser.parse(html)
} catch {
case _: Throwable =>
ArrayBuffer[MatchInfo]()
}
}
override def done(): Unit = {
try {
val matches = get()
allMatches.clear()
allMatches ++= matches
if (allMatches.nonEmpty) {
var foundLive =
false
allMatches.foreach { m =>
if (
!foundLive &&
m.status.toUpperCase == "LIVE"
) {
selectedMatch = m
foundLive = true
}
}
if (!foundLive) {
selectedMatch = allMatches(0)
}
}
selectedMatchLabel.setText(
TextUtil.shorten(
selectedMatch.name.toUpperCase,
85
)
)
rebuildMatchList()
statusLabel.setText(
"Matches loaded ? " +
TextUtil.nowTime()
)
refreshSelectedMatch()
} catch {
case _: Throwable =>
statusLabel.setText(
"Could not load match list"
)
}
}
}
worker.execute()
}
// ==========================================================
// REFRESH SELECTED
// ==========================================================
private def refreshSelectedMatch(): Unit = {
refreshButton.setEnabled(false)
statusLabel.setText(
"Reading live source..."
)
val worker =
new SwingWorker[LiveScoreData, Void] {
override def doInBackground()
: LiveScoreData = {
val html =
CricketWeb.get(
selectedMatch.url
)
LiveParser.parse(
selectedMatch.name,
selectedMatch.url,
html
)
}
override def done(): Unit = {
try {
latestData = get()
updateUIWithData(
latestData
)
statusLabel.setText(
"LIVE SOURCE OK ? " +
TextUtil.nowTime()
)
} catch {
case ex: Throwable =>
statusLabel.setText(
"Source read failed ? " +
TextUtil.shorten(
ex.getMessage,
90
)
)
}
refreshButton.setEnabled(true)
}
}
worker.execute()
}
// ==========================================================
// UPDATE UI
// ==========================================================
private def updateUIWithData(
data: LiveScoreData
): Unit = {
selectedMatchLabel.setText(
TextUtil.shorten(
data.matchName.toUpperCase,
85
)
)
bigTeamLabel.setText(
"BATTING: " +
TextUtil.safe(data.team)
)
bigScoreLabel.setText(
TextUtil.safe(data.score)
)
stateLabel.setText(
TextUtil.safe(data.state)
)
val stateLower =
data.state.toLowerCase
if (stateLower.contains("live")) {
stateLabel.setForeground(
UIColors.accent
)
stateLabel.setBorder(
new CompoundBorder(
new LineBorder(
UIColors.accent,
1,
true
),
new EmptyBorder(
6,
15,
6,
15
)
)
)
} else if (
stateLower.contains("upcoming")
) {
stateLabel.setForeground(
UIColors.orange
)
stateLabel.setBorder(
new CompoundBorder(
new LineBorder(
UIColors.orange,
1,
true
),
new EmptyBorder(
6,
15,
6,
15
)
)
)
} else {
stateLabel.setForeground(
UIColors.muted
)
stateLabel.setBorder(
new CompoundBorder(
new LineBorder(
UIColors.muted,
1,
true
),
new EmptyBorder(
6,
15,
6,
15
)
)
)
}
crrLabel.setText(
"CRR " +
TextUtil.safe(data.crr)
)
reqLabel.setText(
"REQ " +
TextUtil.safe(data.req)
)
targetLabel.setText(
"TARGET " +
TextUtil.safe(data.target)
)
venueLabel.setText(
"Venue: " +
TextUtil.safe(data.venue)
)
tossLabel.setText(
"Toss: " +
TextUtil.safe(data.toss)
)
partnershipLabel.setText(
"Partnership: " +
TextUtil.safe(data.partnership)
)
updatedLabel.setText(
"Updated: " +
TextUtil.safe(data.updated)
)
tvStrip.setValues(data)
updateScorecard(data)
updateCommentary(data)
updateGraph(data)
}
// ==========================================================
// SCORECARD UPDATE
// ==========================================================
private def updateScorecard(
data: LiveScoreData
): Unit = {
scorecardTableModel.setRowCount(0)
data.batters.foreach { b =>
scorecardTableModel.addRow(
Array[Object](
b.name,
b.runs,
b.balls,
b.fours,
b.sixes,
b.sr
)
)
}
bowlersTableModel.setRowCount(0)
data.bowlers.foreach { b =>
bowlersTableModel.addRow(
Array[Object](
b.name,
b.overs,
b.maidens,
b.runs,
b.wickets,
b.economy
)
)
}
}
// ==========================================================
// COMMENTARY UPDATE
// ==========================================================
private def updateCommentary(
data: LiveScoreData
): Unit = {
commentaryArea.setText("")
if (data.commentary.isEmpty) {
commentaryArea.append(
"No commentary was available in the webpage response.\n\n"
)
commentaryArea.append(
"This can happen when the website loads live commentary " +
"inside JavaScript after the initial HTML response."
)
} else {
var i = 0
data.commentary.reverse.foreach { line =>
commentaryArea.append(
"? " +
line +
"\n\n"
)
i += 1
}
}
commentaryArea.setCaretPosition(0)
}
// ==========================================================
// GRAPH UPDATE
// ==========================================================
private def updateGraph(
data: LiveScoreData
): Unit = {
val scoreDigits =
"""\d{1,4}""".r
val firstNumber =
scoreDigits.findFirstIn(data.score)
if (firstNumber.isDefined) {
val score =
try {
firstNumber.get.toInt
} catch {
case _: Throwable => 0
}
val graphData =
ArrayBuffer[Int]()
val points = 18
var i = 0
while (i < points) {
val value =
score * (i + 1) / points
graphData += value
i += 1
}
runGraph.updateData(
graphData
)
}
}
// ==========================================================
// SOURCE BUTTON
// ==========================================================
private def openSource(): Unit = {
try {
if (Desktop.isDesktopSupported) {
Desktop.getDesktop.browse(
new URI(
selectedMatch.url
)
)
} else {
JOptionPane.showMessageDialog(
this,
selectedMatch.url,
"Source",
JOptionPane.INFORMATION_MESSAGE
)
}
} catch {
case _: Throwable =>
JOptionPane.showMessageDialog(
this,
selectedMatch.url,
"Source URL",
JOptionPane.INFORMATION_MESSAGE
)
}
}
}
// ============================================================
// START APP
// ============================================================
object StartCricketLiveCenter {
def main(
args: Array[String]
): Unit = {
SwingUtilities.invokeLater(
new Runnable {
def run(): Unit = {
val login =
new LoginWindow(
() => {
val app =
new CricketLiveCenter()
app.setVisible(true)
}
)
login.setVisible(true)
}
}
)
}
}
StartCricketLiveCenter.main(
Array[String]()
)