mirror of
https://gitlab.com/biskuteri-cafe/JKomasto2.git
synced 2024-11-20 06:24:50 +01:00
337 lines
8.2 KiB
Java
337 lines
8.2 KiB
Java
/* copyright
|
|
|
|
This file is part of JKomasto2.
|
|
Written in 2022 by Usawashi <usawashi16@yahoo.co.jp>
|
|
|
|
This program 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.
|
|
|
|
This program 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 this program. If not, see <https://www.gnu.org/licenses/>.
|
|
|
|
copyright */
|
|
|
|
import java.util.List;
|
|
import java.util.ArrayList;
|
|
import java.io.IOException;
|
|
import cafe.biskuteri.hinoki.Tree;
|
|
import javax.sound.sampled.AudioSystem;
|
|
import javax.sound.sampled.Clip;
|
|
import javax.sound.sampled.AudioInputStream;
|
|
import javax.sound.sampled.UnsupportedAudioFileException;
|
|
import javax.sound.sampled.LineUnavailableException;
|
|
import java.net.URL;
|
|
|
|
class
|
|
WindowUpdater {
|
|
|
|
private JKomasto
|
|
primaire;
|
|
|
|
private MastodonApi
|
|
api;
|
|
|
|
// - -%- -
|
|
|
|
private List<TimelineWindow>
|
|
timelineWindows;
|
|
|
|
private List<NotificationsWindow>
|
|
notificationWindows;
|
|
|
|
private Clip
|
|
notificationSound;
|
|
|
|
private Connection
|
|
publicConn,
|
|
userConn;
|
|
|
|
// ---%-@-%---
|
|
|
|
public synchronized void
|
|
add(TimelineWindow updatee)
|
|
{
|
|
if (!timelineWindows.contains(updatee))
|
|
timelineWindows.add(updatee);
|
|
|
|
publicConn.reevaluate();
|
|
userConn.reevaluate();
|
|
}
|
|
|
|
public synchronized void
|
|
add(NotificationsWindow updatee)
|
|
{
|
|
if (!notificationWindows.contains(updatee))
|
|
notificationWindows.add(updatee);
|
|
|
|
userConn.reevaluate();
|
|
}
|
|
|
|
public synchronized void
|
|
remove(TimelineWindow updatee)
|
|
{
|
|
timelineWindows.remove(updatee);
|
|
publicConn.reevaluate();
|
|
userConn.reevaluate();
|
|
}
|
|
|
|
public synchronized void
|
|
remove(NotificationsWindow updatee)
|
|
{
|
|
notificationWindows.remove(updatee);
|
|
userConn.reevaluate();
|
|
}
|
|
|
|
// - -%- -
|
|
|
|
public static void
|
|
printStackTrace(Thread thread)
|
|
{
|
|
for (StackTraceElement e: thread.getStackTrace())
|
|
System.err.println(e);
|
|
}
|
|
|
|
// ---%-@-%---
|
|
|
|
private class
|
|
Connection
|
|
implements Runnable, ServerSideEventsListener {
|
|
|
|
private TimelineType
|
|
type;
|
|
|
|
// -=-
|
|
|
|
private Thread
|
|
thread;
|
|
|
|
private boolean
|
|
stopping;
|
|
|
|
private StringBuilder
|
|
event, data;
|
|
|
|
// -=%=-
|
|
|
|
public void
|
|
start()
|
|
{
|
|
stopping = false;
|
|
thread = new Thread(this);
|
|
thread.setDaemon(true);
|
|
try
|
|
{
|
|
synchronized (thread)
|
|
{
|
|
thread.start();
|
|
thread.wait();
|
|
}
|
|
}
|
|
catch (InterruptedException eIt)
|
|
{
|
|
assert false;
|
|
}
|
|
}
|
|
|
|
public void
|
|
stop()
|
|
{
|
|
stopping = true;
|
|
thread.interrupt();
|
|
try
|
|
{
|
|
thread.join(3000);
|
|
/*
|
|
* That thread should notice it is
|
|
* interrupted ppromptly, and close.
|
|
*/
|
|
if (thread.isAlive()) printStackTrace(thread);
|
|
}
|
|
catch (InterruptedException eIt)
|
|
{
|
|
assert false;
|
|
}
|
|
thread = null;
|
|
}
|
|
|
|
public void
|
|
reevaluate()
|
|
{
|
|
boolean hasUpdatee = false;
|
|
|
|
for (NotificationsWindow w: notificationWindows)
|
|
if (responsibleFor(w)) hasUpdatee = true;
|
|
|
|
for (TimelineWindow w: timelineWindows)
|
|
if (responsibleFor(w)) hasUpdatee = true;
|
|
|
|
if (!hasUpdatee && thread != null) stop();
|
|
if (hasUpdatee && thread == null) start();
|
|
}
|
|
|
|
// -=-
|
|
|
|
public void
|
|
run()
|
|
{
|
|
synchronized (thread)
|
|
{
|
|
thread.notifyAll();
|
|
}
|
|
event = new StringBuilder();
|
|
data = new StringBuilder();
|
|
api.monitorTimeline(type, this);
|
|
// monitorTimeline should not return until
|
|
// the connection is closed, or this thread
|
|
// is interrupted.
|
|
|
|
System.err.println(
|
|
"Stopped monitoring."
|
|
+ thread + " " + Thread.currentThread()
|
|
);
|
|
if (thread == Thread.currentThread()) thread = null;
|
|
/*
|
|
* This isn't thread safe. But I'd like the
|
|
* restart after sleep mode, so.
|
|
*/
|
|
}
|
|
|
|
public void
|
|
lineReceived(String line)
|
|
{
|
|
if (line.startsWith(":")) return;
|
|
|
|
if (line.isEmpty())
|
|
{
|
|
handle(event.toString(), data.toString());
|
|
event.delete(0, event.length());
|
|
data.delete(0, event.length());
|
|
}
|
|
|
|
if (line.startsWith("data: "))
|
|
data.append(line.substring("data: ".length()));
|
|
|
|
if (line.startsWith("event: "))
|
|
event.append(line.substring("event: ".length()));
|
|
|
|
/*
|
|
* Note that I ignore https://html.spec.whatwg.org
|
|
* /multipage/server-sent-events.html#dispatchMessage.
|
|
* That is because I am not a browser.
|
|
*/
|
|
}
|
|
|
|
private void
|
|
handle(String event, String data)
|
|
{
|
|
assert !data.isEmpty();
|
|
if (event.isEmpty()) return;
|
|
|
|
boolean newPost = event.equals("update");
|
|
boolean newNotif = event.equals("notification");
|
|
if (!(newPost || newNotif)) return;
|
|
|
|
if (newNotif)
|
|
{
|
|
notificationSound.setFramePosition(0);
|
|
notificationSound.start();
|
|
}
|
|
|
|
synchronized (WindowUpdater.this)
|
|
{
|
|
for (TimelineWindow w: timelineWindows)
|
|
{
|
|
if (!responsibleFor(w)) continue;
|
|
w.refresh();
|
|
}
|
|
}
|
|
|
|
synchronized (WindowUpdater.this)
|
|
{
|
|
for (NotificationsWindow w: notificationWindows)
|
|
{
|
|
if (!responsibleFor(w)) continue;
|
|
w.refresh();
|
|
}
|
|
}
|
|
}
|
|
|
|
private boolean
|
|
responsibleFor(TimelineWindow updatee)
|
|
{
|
|
return type == updatee.getTimelineType();
|
|
}
|
|
|
|
private boolean
|
|
responsibleFor(NotificationsWindow updatee)
|
|
{
|
|
return type == TimelineType.HOME;
|
|
}
|
|
|
|
public void
|
|
connectionFailed(IOException eIo)
|
|
{
|
|
// sais pas dois-je faire..
|
|
eIo.printStackTrace();
|
|
}
|
|
|
|
public void
|
|
requestFailed(int httpCode, Tree<String> json)
|
|
{
|
|
// mo shiranu
|
|
System.err.println(httpCode + ", " + json.get("error").value);
|
|
}
|
|
|
|
}
|
|
|
|
// ---%-@-%---
|
|
|
|
WindowUpdater(JKomasto primaire)
|
|
{
|
|
this.primaire = primaire;
|
|
this.api = primaire.getMastodonApi();
|
|
|
|
this.timelineWindows = new ArrayList<>();
|
|
this.notificationWindows = new ArrayList<>();
|
|
|
|
publicConn = new Connection();
|
|
publicConn.type = TimelineType.FEDERATED;
|
|
|
|
userConn = new Connection();
|
|
userConn.type = TimelineType.HOME;
|
|
|
|
loadNotificationSound();
|
|
}
|
|
|
|
void
|
|
loadNotificationSound()
|
|
{
|
|
URL url = getClass().getResource("KDE_Dialog_Appear.wav");
|
|
try {
|
|
Clip clip = AudioSystem.getClip();
|
|
clip.open(AudioSystem.getAudioInputStream(url));
|
|
notificationSound = clip;
|
|
}
|
|
catch (LineUnavailableException eLu) {
|
|
assert false;
|
|
}
|
|
catch (UnsupportedAudioFileException eUa) {
|
|
assert false;
|
|
}
|
|
catch (IOException eIo) {
|
|
assert false;
|
|
}
|
|
catch (IllegalArgumentException eIa) {
|
|
assert false;
|
|
}
|
|
}
|
|
|
|
}
|