mirror of https://github.com/kurisufriend/Clover
parent
0b0ac8748b
commit
8745fc364c
@ -1,163 +0,0 @@ |
||||
/* |
||||
* Clover - 4chan browser https://github.com/Floens/Clover/
|
||||
* Copyright (C) 2014 Floens |
||||
* |
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/ |
||||
package org.floens.chan.core.manager; |
||||
|
||||
import android.content.Context; |
||||
|
||||
import org.floens.chan.ChanApplication; |
||||
import org.floens.chan.core.model.Loadable; |
||||
import org.floens.chan.core.model.Pin; |
||||
|
||||
import java.util.ArrayList; |
||||
import java.util.List; |
||||
|
||||
public class PinnedManager { |
||||
private final List<PinListener> listeners = new ArrayList<>(); |
||||
private final List<Pin> pins; |
||||
|
||||
public PinnedManager(Context context) { |
||||
pins = ChanApplication.getDatabaseManager().getPinned(); |
||||
} |
||||
|
||||
public void addPinListener(PinListener l) { |
||||
listeners.add(l); |
||||
} |
||||
|
||||
public void removePinListener(PinListener l) { |
||||
listeners.remove(l); |
||||
} |
||||
|
||||
/** |
||||
* Look for a pin that has an loadable that is equal to the supplied |
||||
* loadable. |
||||
* |
||||
* @param other |
||||
* @return The pin whose loadable is equal to the supplied loadable, or null |
||||
* if no pin was found. |
||||
*/ |
||||
public Pin findPinByLoadable(Loadable other) { |
||||
for (Pin pin : pins) { |
||||
if (pin.loadable.equals(other)) { |
||||
return pin; |
||||
} |
||||
} |
||||
|
||||
return null; |
||||
} |
||||
|
||||
public Pin findPinById(int id) { |
||||
for (Pin pin : pins) { |
||||
if (pin.id == id) { |
||||
return pin; |
||||
} |
||||
} |
||||
|
||||
return null; |
||||
} |
||||
|
||||
public List<Pin> getPins() { |
||||
return pins; |
||||
} |
||||
|
||||
public List<Pin> getWatchingPins() { |
||||
List<Pin> l = new ArrayList<>(); |
||||
|
||||
for (Pin p : pins) { |
||||
if (p.watching) |
||||
l.add(p); |
||||
} |
||||
|
||||
return l; |
||||
} |
||||
|
||||
/** |
||||
* Add a pin |
||||
* |
||||
* @param pin |
||||
* @return true if it was added, false if it wasn't (duplicated) |
||||
*/ |
||||
public boolean add(Pin pin) { |
||||
// No duplicates
|
||||
for (Pin e : pins) { |
||||
if (e.loadable.equals(pin.loadable)) { |
||||
return false; |
||||
} |
||||
} |
||||
|
||||
pins.add(pin); |
||||
ChanApplication.getDatabaseManager().addPin(pin); |
||||
|
||||
onPinsChanged(); |
||||
|
||||
return true; |
||||
} |
||||
|
||||
/** |
||||
* Remove a pin |
||||
* |
||||
* @param pin |
||||
*/ |
||||
public void remove(Pin pin) { |
||||
pins.remove(pin); |
||||
ChanApplication.getDatabaseManager().removePin(pin); |
||||
pin.destroyWatcher(); |
||||
|
||||
onPinsChanged(); |
||||
} |
||||
|
||||
/** |
||||
* Update the pin in the database |
||||
* |
||||
* @param pin |
||||
*/ |
||||
public void update(Pin pin) { |
||||
ChanApplication.getDatabaseManager().updatePin(pin); |
||||
|
||||
onPinsChanged(); |
||||
} |
||||
|
||||
/** |
||||
* Updates all the pins to the database. This will run in a new thread |
||||
* because it can be an expensive operation. (this will be an huge headache |
||||
* later on when we get concurrent problems) |
||||
*/ |
||||
public void updateAll() { |
||||
new Thread(new Runnable() { |
||||
@Override |
||||
public void run() { |
||||
ChanApplication.getDatabaseManager().updatePins(pins); |
||||
} |
||||
}).start(); |
||||
} |
||||
|
||||
public void onPinViewed(Pin pin) { |
||||
pin.getPinWatcher().onViewed(); |
||||
|
||||
onPinsChanged(); |
||||
} |
||||
|
||||
public void onPinsChanged() { |
||||
for (PinListener l : listeners) { |
||||
l.onPinsChanged(); |
||||
} |
||||
} |
||||
|
||||
public static interface PinListener { |
||||
public void onPinsChanged(); |
||||
} |
||||
} |
@ -0,0 +1,320 @@ |
||||
/* |
||||
* Clover - 4chan browser https://github.com/Floens/Clover/
|
||||
* Copyright (C) 2014 Floens |
||||
* |
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/ |
||||
package org.floens.chan.core.manager; |
||||
|
||||
import android.content.Context; |
||||
import android.content.Intent; |
||||
|
||||
import org.floens.chan.ChanApplication; |
||||
import org.floens.chan.core.ChanPreferences; |
||||
import org.floens.chan.core.model.Loadable; |
||||
import org.floens.chan.core.model.Pin; |
||||
import org.floens.chan.ui.service.WatchNotifier; |
||||
import org.floens.chan.utils.Logger; |
||||
import org.floens.chan.utils.Utils; |
||||
|
||||
import java.util.ArrayList; |
||||
import java.util.Collections; |
||||
import java.util.List; |
||||
import java.util.concurrent.Executors; |
||||
import java.util.concurrent.ScheduledExecutorService; |
||||
import java.util.concurrent.ScheduledFuture; |
||||
import java.util.concurrent.TimeUnit; |
||||
|
||||
public class WatchManager implements ChanApplication.ForegroundChangedListener { |
||||
private static final String TAG = "WatchManager"; |
||||
private static final int FOREGROUND_TIME = 10; |
||||
|
||||
private final Context context; |
||||
private final List<PinListener> listeners = new ArrayList<>(); |
||||
private final List<Pin> pins; |
||||
private ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor(); |
||||
private PendingTimer pendingTimer; |
||||
|
||||
public WatchManager(Context context) { |
||||
this.context = context; |
||||
|
||||
pins = ChanApplication.getDatabaseManager().getPinned(); |
||||
|
||||
ChanApplication.getInstance().addForegroundChangedListener(this); |
||||
|
||||
updateTimerState(); |
||||
updateNotificationServiceState(); |
||||
} |
||||
|
||||
/** |
||||
* Look for a pin that has an loadable that is equal to the supplied |
||||
* loadable. |
||||
* |
||||
* @param other |
||||
* @return The pin whose loadable is equal to the supplied loadable, or null |
||||
* if no pin was found. |
||||
*/ |
||||
public Pin findPinByLoadable(Loadable other) { |
||||
for (Pin pin : pins) { |
||||
if (pin.loadable.equals(other)) { |
||||
return pin; |
||||
} |
||||
} |
||||
|
||||
return null; |
||||
} |
||||
|
||||
public Pin findPinById(int id) { |
||||
for (Pin pin : pins) { |
||||
if (pin.id == id) { |
||||
return pin; |
||||
} |
||||
} |
||||
|
||||
return null; |
||||
} |
||||
|
||||
public List<Pin> getPins() { |
||||
return pins; |
||||
} |
||||
|
||||
public List<Pin> getWatchingPins() { |
||||
if (ChanPreferences.getWatchEnabled()) { |
||||
List<Pin> l = new ArrayList<>(); |
||||
|
||||
for (Pin p : pins) { |
||||
if (p.watching) |
||||
l.add(p); |
||||
} |
||||
|
||||
return l; |
||||
} else { |
||||
return Collections.emptyList(); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Add a pin |
||||
* |
||||
* @param pin |
||||
* @return true if it was added, false if it wasn't (duplicated) |
||||
*/ |
||||
public boolean addPin(Pin pin) { |
||||
// No duplicates
|
||||
for (Pin e : pins) { |
||||
if (e.loadable.equals(pin.loadable)) { |
||||
return false; |
||||
} |
||||
} |
||||
|
||||
pins.add(pin); |
||||
ChanApplication.getDatabaseManager().addPin(pin); |
||||
|
||||
onPinsChanged(); |
||||
|
||||
return true; |
||||
} |
||||
|
||||
/** |
||||
* Remove a pin |
||||
* |
||||
* @param pin |
||||
*/ |
||||
public void removePin(Pin pin) { |
||||
pins.remove(pin); |
||||
ChanApplication.getDatabaseManager().removePin(pin); |
||||
pin.destroyWatcher(); |
||||
|
||||
onPinsChanged(); |
||||
} |
||||
|
||||
/** |
||||
* Update the pin in the database |
||||
* |
||||
* @param pin |
||||
*/ |
||||
public void updatePin(Pin pin) { |
||||
ChanApplication.getDatabaseManager().updatePin(pin); |
||||
|
||||
onPinsChanged(); |
||||
} |
||||
|
||||
/** |
||||
* Updates all the pins to the database. This will run in a new thread |
||||
* because it can be an expensive operation. (this will be an huge headache |
||||
* later on when we get concurrent problems) |
||||
*/ |
||||
public void updateDatabase() { |
||||
new Thread(new Runnable() { |
||||
@Override |
||||
public void run() { |
||||
ChanApplication.getDatabaseManager().updatePins(pins); |
||||
} |
||||
}).start(); |
||||
} |
||||
|
||||
public void onPinViewed(Pin pin) { |
||||
pin.getPinWatcher().onViewed(); |
||||
|
||||
onPinsChanged(); |
||||
} |
||||
|
||||
public void addPinListener(PinListener l) { |
||||
listeners.add(l); |
||||
} |
||||
|
||||
public void removePinListener(PinListener l) { |
||||
listeners.remove(l); |
||||
} |
||||
|
||||
public void onPinsChanged() { |
||||
for (PinListener l : listeners) { |
||||
l.onPinsChanged(); |
||||
} |
||||
|
||||
updateTimerState(); |
||||
updateNotificationServiceState(); |
||||
} |
||||
|
||||
public void pausePins() { |
||||
List<Pin> watchingPins = getWatchingPins(); |
||||
for (Pin pin : watchingPins) { |
||||
pin.watching = false; |
||||
} |
||||
|
||||
onPinsChanged(); |
||||
updateDatabase(); |
||||
} |
||||
|
||||
public void onWatchEnabledChanged(boolean watchEnabled) { |
||||
updateNotificationServiceState(watchEnabled, getWatchBackgroundEnabled()); |
||||
updateTimerState(watchEnabled, getWatchBackgroundEnabled()); |
||||
} |
||||
|
||||
public void onBackgroundWatchingChanged(boolean backgroundEnabled) { |
||||
updateNotificationServiceState(getWatchEnabled(), backgroundEnabled); |
||||
updateTimerState(getWatchEnabled(), backgroundEnabled); |
||||
} |
||||
|
||||
@Override |
||||
public void onForegroundChanged(final boolean foreground) { |
||||
updateNotificationServiceState(); |
||||
updateTimerState(); |
||||
} |
||||
|
||||
public boolean getWatchEnabled() { |
||||
// getWatchingPins returns an empty list when ChanPreferences.getWatchEnabled() is false
|
||||
return getWatchingPins().size() > 0; |
||||
} |
||||
|
||||
public boolean getWatchBackgroundEnabled() { |
||||
return ChanPreferences.getWatchBackgroundEnabled(); |
||||
} |
||||
|
||||
private void updateNotificationServiceState() { |
||||
updateNotificationServiceState(getWatchEnabled(), getWatchBackgroundEnabled()); |
||||
} |
||||
|
||||
private void updateNotificationServiceState(boolean watchEnabled, boolean backgroundEnabled) { |
||||
if (watchEnabled && backgroundEnabled) { |
||||
// Also calls onStartCommand, which updates the notification
|
||||
context.startService(new Intent(context, WatchNotifier.class)); |
||||
} else { |
||||
context.stopService(new Intent(context, WatchNotifier.class)); |
||||
} |
||||
} |
||||
|
||||
private void updateTimerState() { |
||||
updateTimerState(getWatchEnabled(), getWatchBackgroundEnabled()); |
||||
} |
||||
|
||||
private void updateTimerState(boolean watchEnabled, boolean backgroundEnabled) { |
||||
if (watchEnabled) { |
||||
if (ChanApplication.getInstance().getApplicationInForeground()) { |
||||
setTimer(FOREGROUND_TIME); |
||||
} else { |
||||
if (backgroundEnabled) { |
||||
setTimer(ChanPreferences.getWatchBackgroundTimeout()); |
||||
} else { |
||||
if (pendingTimer != null) { |
||||
pendingTimer.cancel(); |
||||
pendingTimer = null; |
||||
Logger.d(TAG, "Canceled timer"); |
||||
} |
||||
} |
||||
} |
||||
} else { |
||||
if (pendingTimer != null) { |
||||
pendingTimer.cancel(); |
||||
pendingTimer = null; |
||||
Logger.d(TAG, "Canceled timer"); |
||||
} |
||||
} |
||||
} |
||||
|
||||
private void setTimer(int time) { |
||||
if (pendingTimer != null && pendingTimer.time == time) { |
||||
return; |
||||
} |
||||
|
||||
if (pendingTimer != null) { |
||||
pendingTimer.cancel(); |
||||
pendingTimer = null; |
||||
Logger.d(TAG, "Canceled timer"); |
||||
} |
||||
|
||||
ScheduledFuture scheduledFuture = executor.schedule(new Runnable() { |
||||
@Override |
||||
public void run() { |
||||
Utils.runOnUiThread(new Runnable() { |
||||
@Override |
||||
public void run() { |
||||
timerFired(); |
||||
} |
||||
}); |
||||
} |
||||
}, time, TimeUnit.SECONDS); |
||||
pendingTimer = new PendingTimer(scheduledFuture, time); |
||||
Logger.d(TAG, "Timer firing in " + time + " seconds"); |
||||
} |
||||
|
||||
private void timerFired() { |
||||
Logger.d(TAG, "Timer fired"); |
||||
pendingTimer = null; |
||||
|
||||
for (Pin pin : getWatchingPins()) { |
||||
pin.updateWatch(); |
||||
} |
||||
|
||||
updateTimerState(); |
||||
} |
||||
|
||||
public static interface PinListener { |
||||
public void onPinsChanged(); |
||||
} |
||||
|
||||
private static class PendingTimer { |
||||
public ScheduledFuture scheduledFuture; |
||||
public int time; |
||||
|
||||
public PendingTimer(ScheduledFuture scheduledFuture, int time) { |
||||
this.scheduledFuture = scheduledFuture; |
||||
this.time = time; |
||||
} |
||||
|
||||
public void cancel() { |
||||
scheduledFuture.cancel(false); |
||||
} |
||||
} |
||||
} |
@ -1,231 +0,0 @@ |
||||
/* |
||||
* Clover - 4chan browser https://github.com/Floens/Clover/
|
||||
* Copyright (C) 2014 Floens |
||||
* |
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/ |
||||
package org.floens.chan.service; |
||||
|
||||
import android.app.Service; |
||||
import android.content.Context; |
||||
import android.content.Intent; |
||||
import android.os.IBinder; |
||||
|
||||
import org.floens.chan.ChanApplication; |
||||
import org.floens.chan.core.ChanPreferences; |
||||
import org.floens.chan.core.model.Pin; |
||||
import org.floens.chan.core.watch.WatchNotifier; |
||||
import org.floens.chan.utils.Logger; |
||||
import org.floens.chan.utils.Utils; |
||||
|
||||
import java.util.List; |
||||
|
||||
public class WatchService extends Service { |
||||
private static final String TAG = "WatchService"; |
||||
|
||||
private static final long FOREGROUND_INTERVAL = 10000L; |
||||
|
||||
private static WatchService instance; |
||||
private static boolean activityInForeground = false; |
||||
|
||||
private Thread loadThread; |
||||
private boolean running = true; |
||||
private WatchNotifier watchNotifier; |
||||
|
||||
public static void onActivityStart() { |
||||
activityInForeground = true; |
||||
if (instance != null) { |
||||
instance.onActivityInForeground(); |
||||
} |
||||
} |
||||
|
||||
public static void onActivityStop() { |
||||
activityInForeground = false; |
||||
if (instance != null) { |
||||
instance.onActivityInBackground(); |
||||
} |
||||
} |
||||
|
||||
public static boolean getActivityInForeground() { |
||||
return activityInForeground; |
||||
} |
||||
|
||||
public static void updateRunningState(Context context) { |
||||
if (ChanPreferences.getWatchEnabled()) { |
||||
if (ChanApplication.getPinnedManager().getWatchingPins().size() == 0) { |
||||
if (getRunning()) { |
||||
disable(context); |
||||
} |
||||
} else { |
||||
if (!getRunning()) { |
||||
enable(context); |
||||
} |
||||
} |
||||
} else { |
||||
if (getRunning()) { |
||||
disable(context); |
||||
} |
||||
} |
||||
} |
||||
|
||||
public static void enable(Context context) { |
||||
if (!getRunning()) { |
||||
context.startService(new Intent(context, WatchService.class)); |
||||
} |
||||
} |
||||
|
||||
public static void disable(Context context) { |
||||
if (getRunning()) { |
||||
context.stopService(new Intent(context, WatchService.class)); |
||||
|
||||
List<Pin> pins = ChanApplication.getPinnedManager().getWatchingPins(); |
||||
for (Pin pin : pins) { |
||||
pin.destroyWatcher(); |
||||
} |
||||
|
||||
instance.watchNotifier.destroy(); |
||||
} |
||||
} |
||||
|
||||
public static boolean getRunning() { |
||||
return instance != null; |
||||
} |
||||
|
||||
public static void onPinWatcherResult() { |
||||
Utils.runOnUiThread(new Runnable() { |
||||
@Override |
||||
public void run() { |
||||
ChanApplication.getPinnedManager().onPinsChanged(); |
||||
if (instance != null) { |
||||
instance.watchNotifier.update(); |
||||
} |
||||
} |
||||
}); |
||||
} |
||||
|
||||
@Override |
||||
public void onCreate() { |
||||
super.onCreate(); |
||||
|
||||
instance = this; |
||||
|
||||
watchNotifier = new WatchNotifier(this); |
||||
|
||||
Logger.i(TAG, "WatchService created"); |
||||
|
||||
startThread(); |
||||
} |
||||
|
||||
@Override |
||||
public void onDestroy() { |
||||
super.onDestroy(); |
||||
|
||||
instance = null; |
||||
|
||||
running = false; |
||||
if (loadThread != null) { |
||||
loadThread.interrupt(); |
||||
} |
||||
|
||||
Logger.i(TAG, "WatchService destroyed"); |
||||
} |
||||
|
||||
@Override |
||||
public int onStartCommand(Intent intent, int flags, int startId) { |
||||
if (intent != null && intent.getExtras() != null && intent.getExtras().getBoolean("pause_pins", false)) { |
||||
if (watchNotifier != null) { |
||||
watchNotifier.onPausePinsClicked(); |
||||
} |
||||
} |
||||
|
||||
return START_STICKY; |
||||
} |
||||
|
||||
private void startThread() { |
||||
running = true; |
||||
|
||||
if (loadThread == null) { |
||||
loadThread = new Thread(new Runnable() { |
||||
@Override |
||||
public void run() { |
||||
while (running) { |
||||
Logger.d(TAG, "Loadthread loop"); |
||||
|
||||
if (!running) |
||||
return; |
||||
|
||||
long timeout = activityInForeground ? FOREGROUND_INTERVAL : getBackgroundTimeout(); |
||||
if (timeout < 0L) { |
||||
Logger.d(TAG, "Waiting for interrupt..."); |
||||
try { |
||||
Object o = new Object(); |
||||
synchronized (o) { |
||||
o.wait(); |
||||
} |
||||
} catch (InterruptedException e) { |
||||
Logger.d(TAG, "Interrupted!"); |
||||
} |
||||
} else { |
||||
update(); |
||||
|
||||
try { |
||||
Thread.sleep(timeout); |
||||
} catch (InterruptedException e) { |
||||
Logger.d(TAG, "Interrupted!"); |
||||
} |
||||
} |
||||
} |
||||
} |
||||
}); |
||||
|
||||
loadThread.start(); |
||||
} |
||||
} |
||||
|
||||
private void onActivityInForeground() { |
||||
if (loadThread != null) { |
||||
loadThread.interrupt(); |
||||
} |
||||
watchNotifier.onForegroundChanged(); |
||||
} |
||||
|
||||
private void onActivityInBackground() { |
||||
watchNotifier.onForegroundChanged(); |
||||
} |
||||
|
||||
/** |
||||
* Returns the sleep time the user specified for background iteration |
||||
* |
||||
* @return the sleep time in ms, or -1 if background reloading is disabled |
||||
*/ |
||||
private long getBackgroundTimeout() { |
||||
if (ChanPreferences.getWatchBackgroundEnabled()) { |
||||
return ChanPreferences.getWatchBackgroundTimeout(); |
||||
} else { |
||||
return -1; |
||||
} |
||||
} |
||||
|
||||
private void update() { |
||||
List<Pin> pins = ChanApplication.getPinnedManager().getWatchingPins(); |
||||
for (Pin pin : pins) { |
||||
pin.updateWatch(); |
||||
} |
||||
} |
||||
|
||||
@Override |
||||
public IBinder onBind(Intent intent) { |
||||
return null; |
||||
} |
||||
} |
Loading…
Reference in new issue