001// License: GPL. For details, see LICENSE file. 002package org.openstreetmap.josm.io; 003 004import static org.openstreetmap.josm.tools.I18n.tr; 005import static org.openstreetmap.josm.tools.I18n.trn; 006 007import java.awt.GridBagLayout; 008import java.net.Authenticator.RequestorType; 009import java.util.concurrent.Executors; 010import java.util.concurrent.ScheduledExecutorService; 011import java.util.concurrent.ScheduledFuture; 012import java.util.concurrent.TimeUnit; 013 014import javax.swing.JLabel; 015import javax.swing.JOptionPane; 016import javax.swing.JPanel; 017 018import org.openstreetmap.josm.Main; 019import org.openstreetmap.josm.data.osm.UserInfo; 020import org.openstreetmap.josm.data.preferences.BooleanProperty; 021import org.openstreetmap.josm.data.preferences.IntegerProperty; 022import org.openstreetmap.josm.gui.JosmUserIdentityManager; 023import org.openstreetmap.josm.gui.Notification; 024import org.openstreetmap.josm.gui.progress.NullProgressMonitor; 025import org.openstreetmap.josm.gui.util.GuiHelper; 026import org.openstreetmap.josm.gui.widgets.UrlLabel; 027import org.openstreetmap.josm.io.auth.CredentialsAgentException; 028import org.openstreetmap.josm.io.auth.CredentialsAgentResponse; 029import org.openstreetmap.josm.io.auth.CredentialsManager; 030import org.openstreetmap.josm.io.auth.JosmPreferencesCredentialAgent; 031import org.openstreetmap.josm.tools.GBC; 032 033/** 034 * Notifies user periodically of new received (unread) messages 035 * @since 6349 036 */ 037public final class MessageNotifier { 038 039 private MessageNotifier() { 040 // Hide default constructor for utils classes 041 } 042 043 /** Property defining if this task is enabled or not */ 044 public static final BooleanProperty PROP_NOTIFIER_ENABLED = new BooleanProperty("message.notifier.enabled", true); 045 /** Property defining the update interval in minutes */ 046 public static final IntegerProperty PROP_INTERVAL = new IntegerProperty("message.notifier.interval", 5); 047 048 private static final ScheduledExecutorService EXECUTOR = Executors.newSingleThreadScheduledExecutor(); 049 050 private static final Runnable WORKER = new Worker(); 051 052 private static ScheduledFuture<?> task = null; 053 054 private static class Worker implements Runnable { 055 056 private int lastUnreadCount = 0; 057 058 @Override 059 public void run() { 060 try { 061 final UserInfo userInfo = new OsmServerUserInfoReader().fetchUserInfo(NullProgressMonitor.INSTANCE); 062 final int unread = userInfo.getUnreadMessages(); 063 if (unread > 0 && unread != lastUnreadCount) { 064 GuiHelper.runInEDT(new Runnable() { 065 @Override 066 public void run() { 067 JPanel panel = new JPanel(new GridBagLayout()); 068 panel.add(new JLabel(trn("You have {0} unread message.", "You have {0} unread messages.", unread, unread)), GBC.eol()); 069 panel.add(new UrlLabel(Main.OSM_WEBSITE + "/user/"+userInfo.getDisplayName()+"/inbox", tr("Click here to see your inbox.")), GBC.eol()); 070 panel.setOpaque(false); 071 new Notification().setContent(panel) 072 .setIcon(JOptionPane.INFORMATION_MESSAGE) 073 .setDuration(Notification.TIME_LONG) 074 .show(); 075 } 076 }); 077 lastUnreadCount = unread; 078 } 079 } catch (OsmTransferException e) { 080 Main.warn(e); 081 } 082 } 083 } 084 085 /** 086 * Starts the message notifier task if not already started and if user is fully identified 087 */ 088 public static void start() { 089 int interval = PROP_INTERVAL.get(); 090 if (!isRunning() && interval > 0 && isUserEnoughIdentified()) { 091 task = EXECUTOR.scheduleAtFixedRate(WORKER, 0, interval * 60, TimeUnit.SECONDS); 092 Main.info("Message notifier active (checks every "+interval+" minute"+(interval>1?"s":"")+")"); 093 } 094 } 095 096 /** 097 * Stops the message notifier task if started 098 */ 099 public static void stop() { 100 if (isRunning()) { 101 task.cancel(false); 102 Main.info("Message notifier inactive"); 103 task = null; 104 } 105 } 106 107 /** 108 * Determines if the message notifier is currently running 109 * @return {@code true} if the notifier is running, {@code false} otherwise 110 */ 111 public static boolean isRunning() { 112 return task != null; 113 } 114 115 /** 116 * Determines if user set enough information in JOSM preferences to make the request to OSM API without 117 * prompting him for a password. 118 * @return {@code true} if user chose an OAuth token or supplied both its username and password, {@code false otherwise} 119 */ 120 public static boolean isUserEnoughIdentified() { 121 JosmUserIdentityManager identManager = JosmUserIdentityManager.getInstance(); 122 if (identManager.isFullyIdentified()) { 123 return true; 124 } else { 125 CredentialsManager credManager = CredentialsManager.getInstance(); 126 try { 127 if (JosmPreferencesCredentialAgent.class.equals(credManager.getCredentialsAgentClass())) { 128 if (OsmApi.isUsingOAuth()) { 129 return credManager.lookupOAuthAccessToken() != null; 130 } else { 131 String username = Main.pref.get("osm-server.username", null); 132 String password = Main.pref.get("osm-server.password", null); 133 return username != null && !username.isEmpty() && password != null && !password.isEmpty(); 134 } 135 } else { 136 CredentialsAgentResponse credentials = credManager.getCredentials( 137 RequestorType.SERVER, OsmApi.getOsmApi().getHost(), false); 138 if (credentials != null) { 139 String username = credentials.getUsername(); 140 char[] password = credentials.getPassword(); 141 return username != null && !username.isEmpty() && password != null && password.length > 0; 142 } 143 } 144 } catch (CredentialsAgentException e) { 145 Main.warn("Unable to get credentials: "+e.getMessage()); 146 } 147 } 148 return false; 149 } 150}