/* * Copyright 2000-2006 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package jetbrains.communicator.idea.monitor; import com.intellij.openapi.Disposable; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.components.ApplicationComponent; import com.intellij.openapi.project.ProjectUtil; import jetbrains.communicator.core.IDEtalkOptions; import jetbrains.communicator.core.Pico; import jetbrains.communicator.core.transport.Transport; import jetbrains.communicator.core.users.PresenceMode; import jetbrains.communicator.core.users.UserPresence; import org.apache.log4j.Logger; import org.jetbrains.annotations.NotNull; import java.awt.*; import java.awt.event.AWTEventListener; /** * @author Kir * * Monitors user's activity in IDEA and updates own UserPresence. * * @see Transport#setOwnPresence(UserPresence) */ public class UserActivityMonitor implements Disposable, Runnable, ApplicationComponent { private static final Logger LOG = Logger.getLogger(UserActivityMonitor.class); private static final int REFRESH_INTERVAL = 60 * 1000; public static final int AWAY_MINS = 7; public static final int EXTENDED_AWAY_MINS = 30; private final Object myMonitor = new Object(); private long myLastActionTimestamp; private volatile boolean myStop; private int myRefreshInterval = REFRESH_INTERVAL; private volatile boolean myThreadDisposed = true; @Override public void initComponent() { if (ApplicationManager.getApplication().isUnitTestMode()) return; KeyboardFocusManager.getCurrentKeyboardFocusManager().addKeyEventDispatcher(e -> { activity(); return false; }); AWTEventListener listener = new AWTEventListener() { @Override public void eventDispatched(AWTEvent event) { activity(); } }; Toolkit.getDefaultToolkit().addAWTEventListener(listener, AWTEvent.MOUSE_EVENT_MASK | AWTEvent.MOUSE_MOTION_EVENT_MASK); activity(); ProjectUtil.runWhenProjectOpened(project -> { //noinspection HardCodedStringLiteral Thread t = new Thread(this, getComponentName() + " thread"); t.setDaemon(true); t.start(); }); } @Override public void dispose() { myStop = true; try { while (!myThreadDisposed) { synchronized (myMonitor) { myMonitor.notifyAll(); } Thread.sleep(100); } } catch (InterruptedException ignored) { } } void activity() { synchronized (myMonitor) { if (System.currentTimeMillis() - myLastActionTimestamp > myRefreshInterval) { myLastActionTimestamp = System.currentTimeMillis(); myMonitor.notifyAll(); } myLastActionTimestamp = System.currentTimeMillis(); } } void setRefreshInterval(int refreshInterval) { synchronized(myMonitor) { myRefreshInterval = refreshInterval; } } @Override public void run() { try { myThreadDisposed = false; while (!myStop) { UserPresence userPresence = calculatePresence(); LOG.debug("Calculated own presence: " + userPresence); for (Object o : Pico.getInstance().getComponentInstancesOfType(Transport.class)) { Transport transport = (Transport) o; if (transport.isOnline()) { transport.setOwnPresence(userPresence); } } synchronized (myMonitor) { myMonitor.wait(myRefreshInterval); } } } catch (InterruptedException e) { LOG.info("Interrupted"); } finally { myThreadDisposed = true; } } @NotNull private UserPresence calculatePresence() { synchronized(myMonitor) { double inactivitySecs = (System.currentTimeMillis() - myLastActionTimestamp)/1000.0; if (inactivitySecs < timeout(IDEtalkOptions.TIMEOUT_AWAY_MIN, AWAY_MINS)) { return new UserPresence(PresenceMode.AVAILABLE); } if (inactivitySecs < timeout(IDEtalkOptions.TIMEOUT_XA_MIN, EXTENDED_AWAY_MINS)){ return new UserPresence(PresenceMode.AWAY); } return new UserPresence(PresenceMode.EXTENDED_AWAY); } } private static double timeout(String option, int defaultVal) { IDEtalkOptions options = Pico.getOptions(); if (options == null) return -1; return options.getNumber(option, defaultVal) * 60; } }