Show pending notifications on OSX and Windows

sip-call-params
Ingo Bauersachs 10 years ago
parent 7f08c0d239
commit 5c24dad320

@ -19,6 +19,7 @@
import java.awt.*;
import java.awt.event.*;
import java.awt.image.*;
import java.net.*;
import javax.swing.*;
@ -27,6 +28,7 @@
import net.java.sip.communicator.impl.osdependent.*;
import net.java.sip.communicator.impl.osdependent.systemtray.SystemTray;
import net.java.sip.communicator.impl.osdependent.systemtray.TrayIcon;
import net.java.sip.communicator.impl.osdependent.windows.*;
import net.java.sip.communicator.service.gui.*;
import net.java.sip.communicator.service.protocol.*;
import net.java.sip.communicator.service.systray.*;
@ -452,6 +454,83 @@ public boolean checkInitialized()
return initialized;
}
/**
* Set the number of pending notifications to the the application icon
* (Dock on OSX, TaskBar on Windows, nothing on Linux currently).
*/
@Override
public void setNotificationCount(int count)
{
if (OSUtils.IS_MAC)
{
Application application = Application.getApplication();
application.setDockIconBadge(new Integer(count).toString());
}
else if (OSUtils.IS_WINDOWS)
{
UIService uiService = OsDependentActivator.getUIService();
if (uiService == null)
{
return;
}
ExportedWindow mainWindow =
uiService.getExportedWindow(ExportedWindow.MAIN_WINDOW);
if (mainWindow == null
|| !(mainWindow.getSource() instanceof Component))
{
return;
}
BufferedImage img = null;
if (count > 0)
{
img = createOverlayImage(new Integer(count).toString());
}
try
{
TaskBarList3.getInstance().SetOverlayIcon(
(Component) mainWindow.getSource(), img, null);
}
catch (Exception ex)
{
logger.error("Could not set the notification count.", ex);
}
}
}
private BufferedImage createOverlayImage(String text)
{
int size = 16;
BufferedImage image =
new BufferedImage(size, size, BufferedImage.TYPE_INT_ARGB);
Graphics2D g = image.createGraphics();
g.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
//background
g.setPaint(new Color(0, 0, 0, 102));
g.fillRoundRect(0, 0, size, size, size, size);
//filling
int mainRadius = 14;
g.setPaint(new Color(255, 98, 89));
g.fillRoundRect(size / 2 - mainRadius / 2, size / 2 - mainRadius / 2,
mainRadius, mainRadius, size, size);
//text
Font font = g.getFont();
g.setFont(new Font(font.getName(), Font.BOLD, 9));
FontMetrics fontMetrics = g.getFontMetrics();
int textWidth = fontMetrics.stringWidth(text);
g.setColor(Color.white);
g.drawString(text, size / 2 - textWidth / 2,
size / 2 - fontMetrics.getHeight() / 2 + fontMetrics.getAscent());
return image;
}
/**
* @return the trayIcon
*/

@ -5,21 +5,23 @@ Bundle-Vendor: jitsi.org
Bundle-Version: 0.0.1
Bundle-SymbolicName: net.java.sip.communicator.osdependent
Export-Package: net.java.sip.communicator.service.desktop
Import-Package: org.osgi.framework,
com.apple.cocoa.application,
Import-Package: com.apple.cocoa.application,
com.apple.cocoa.foundation,
com.apple.eawt,
org.jitsi.service.configuration,
com.sun.jna,
com.sun.jna.platform.win32,
com.sun.jna.platform.win32.COM,
com.sun.jna.ptr,
com.sun.jna.win32,
net.java.sip.communicator.service.gui,
net.java.sip.communicator.service.gui.event,
net.java.sip.communicator.service.protocol,
net.java.sip.communicator.service.protocol.globalstatus,
net.java.sip.communicator.service.protocol.event,
org.jitsi.service.resources, net.java.sip.communicator.service.resources,
net.java.sip.communicator.service.resources,
net.java.sip.communicator.service.shutdown,
net.java.sip.communicator.service.systray,
net.java.sip.communicator.service.systray.event,
org.jitsi.util,
net.java.sip.communicator.util,
net.java.sip.communicator.plugin.desktoputil,
net.java.sip.communicator.plugin.desktoputil.presence,
@ -36,4 +38,8 @@ Import-Package: org.osgi.framework,
javax.swing.text,
javax.swing.text.html,
javax.swing.tree,
javax.swing.undo
javax.swing.undo,
org.jitsi.service.configuration,
org.jitsi.service.resources,
org.jitsi.util,
org.osgi.framework

@ -0,0 +1,172 @@
/*
* Jitsi, the OpenSource Java VoIP and Instant Messaging client.
*
* Copyright 2000-2016 JetBrains s.r.o.
* Copyright @ 2015 Atlassian Pty Ltd
*
* 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 net.java.sip.communicator.impl.osdependent.windows;
import java.awt.image.*;
import java.nio.*;
import com.sun.jna.*;
import com.sun.jna.platform.win32.*;
import com.sun.jna.platform.win32.WinDef.*;
/**
* Image conversion utilities.
*
* Parts of this code are based on AppIcon.java from IntelliJ community.
* Licensed under Apache 2.0, Copyright 2000-2016 JetBrains s.r.o.
*/
public class ImageConverter
{
/**
* Converts the <tt>BufferedImage</tt> to an ICONDIR structure.
* @param src The source image to convert
* @return an ICONDIR structure with the data of the passed source image
*/
public static byte[] writeTransparentIcoImage(BufferedImage src)
{
int bitCount = 32;
int scanline_size = (bitCount * src.getWidth() + 7) / 8;
if ((scanline_size % 4) != 0)
scanline_size += 4 - (scanline_size % 4); // pad scanline to 4 byte
// size.
int t_scanline_size = (src.getWidth() + 7) / 8;
if ((t_scanline_size % 4) != 0)
t_scanline_size += 4 - (t_scanline_size % 4); // pad scanline to 4
// byte size.
int imageSize = 40 + src.getHeight() * scanline_size
+ src.getHeight() * t_scanline_size;
// sizeof(ICONDIR)
// + sizeof(ICONDIRENTRY)
// + (sizeof(BITMAPINFOHEADER)+data)
ByteBuffer bos = ByteBuffer.allocate(6 + 16 + imageSize);
bos.order(ByteOrder.LITTLE_ENDIAN);
// ICONDIR
bos.putShort((short) 0); // reserved
bos.putShort((short) 1); // 1=ICO, 2=CUR
bos.putShort((short) 1); // count
// ICONDIRENTRY
int iconDirEntryWidth = src.getWidth();
int iconDirEntryHeight = src.getHeight();
if (iconDirEntryWidth > 255 || iconDirEntryHeight > 255)
{
iconDirEntryWidth = 0;
iconDirEntryHeight = 0;
}
bos.put((byte) iconDirEntryWidth);
bos.put((byte) iconDirEntryHeight);
bos.put((byte) 0);
bos.put((byte) 0); // reserved
bos.putShort((short) 1); // color planes
bos.putShort((short) bitCount);
bos.putInt(imageSize);
bos.putInt(22); // image offset
// BITMAPINFOHEADER
bos.putInt(40); // size
bos.putInt(src.getWidth());
bos.putInt(2 * src.getHeight());
bos.putShort((short) 1); // planes
bos.putShort((short) bitCount);
bos.putInt(0); // compression
bos.putInt(0); // image size
bos.putInt(0); // x pixels per meter
bos.putInt(0); // y pixels per meter
bos.putInt(0); // colors used, 0 = (1 << bitCount) (ignored)
bos.putInt(0); // colors important
int bit_cache = 0;
int bits_in_cache = 0;
int row_padding = scanline_size - (bitCount * src.getWidth() + 7) / 8;
for (int y = src.getHeight() - 1; y >= 0; y--)
{
for (int x = 0; x < src.getWidth(); x++)
{
int argb = src.getRGB(x, y);
bos.put((byte) (0xff & argb));
bos.put((byte) (0xff & (argb >> 8)));
bos.put((byte) (0xff & (argb >> 16)));
bos.put((byte) (0xff & (argb >> 24)));
}
for (int x = 0; x < row_padding; x++)
bos.put((byte) 0);
}
int t_row_padding = t_scanline_size - (src.getWidth() + 7) / 8;
for (int y = src.getHeight() - 1; y >= 0; y--)
{
for (int x = 0; x < src.getWidth(); x++)
{
int argb = src.getRGB(x, y);
int alpha = 0xff & (argb >> 24);
bit_cache <<= 1;
if (alpha == 0)
bit_cache |= 1;
bits_in_cache++;
if (bits_in_cache >= 8)
{
bos.put((byte) (0xff & bit_cache));
bit_cache = 0;
bits_in_cache = 0;
}
}
if (bits_in_cache > 0)
{
bit_cache <<= (8 - bits_in_cache);
bos.put((byte) (0xff & bit_cache));
bit_cache = 0;
bits_in_cache = 0;
}
for (int x = 0; x < t_row_padding; x++)
bos.put((byte) 0);
}
byte[] result = new byte[bos.position()];
System.arraycopy(bos.array(), 0, result, 0, bos.position());
return result;
}
/**
* Converts an ICONDIR ico to an HICON handle
* @param ico the image data
* @return A Windows HICON handle
*/
public static HICON createIcon(byte[] ico)
{
Memory buffer = new Memory(ico.length);
buffer.write(0, ico, 0, ico.length);
int nSize = 100;
int offset = User32Ex.INSTANCE.LookupIconIdFromDirectoryEx(buffer, true,
nSize, nSize, 0);
if (offset != 0)
{
return User32Ex.INSTANCE.CreateIconFromResourceEx(
buffer.share(offset), new WinDef.DWORD(0), true,
new WinDef.DWORD(0x00030000), nSize, nSize, 0);
}
return null;
}
}

@ -0,0 +1,150 @@
/*
* Jitsi, the OpenSource Java VoIP and Instant Messaging client.
*
* Copyright @ 2015 Atlassian Pty Ltd
*
* 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 net.java.sip.communicator.impl.osdependent.windows;
import java.awt.*;
import java.awt.image.*;
import org.jitsi.util.*;
import com.sun.jna.*;
import com.sun.jna.platform.win32.*;
import com.sun.jna.platform.win32.COM.*;
import com.sun.jna.platform.win32.Guid.*;
import com.sun.jna.platform.win32.WinDef.*;
import com.sun.jna.platform.win32.WinNT.*;
import com.sun.jna.ptr.*;
/**
* JNA wrapper for the ITaskBarList3 COM interface.
* https://msdn.microsoft.com/en-us/library/dd391696(v=vs.85).aspx
*
* @author Ingo Bauersachs
*/
public class TaskBarList3
extends Unknown
{
private static final GUID CLSID_TaskbarList =
new GUID("{56FDF344-FD6D-11d0-958A-006097C9A090}");
private static final GUID IID_ITaskbarList3 =
new GUID("{ea1afb91-9e28-4b86-90e9-9e9f8a5eefaf}");
private static TaskBarList3 instance;
/**
* Gets the ITaskBarList3 interface and initializes it with HrInit
* @return A ready to use TaskBarList3 object.
* @throws COMException when the interface could not be accessed
*/
public static TaskBarList3 getInstance()
{
if (instance == null && OSUtils.IS_WINDOWS)
{
Ole32.INSTANCE.CoInitializeEx(Pointer.NULL, 0);
PointerByReference p = new PointerByReference();
WinNT.HRESULT hr =
Ole32.INSTANCE.CoCreateInstance(CLSID_TaskbarList, Pointer.NULL,
ObjBase.CLSCTX_ALL, IID_ITaskbarList3, p);
COMUtils.checkRC(hr);
instance = new TaskBarList3(p.getValue());
}
return instance;
}
private TaskBarList3(Pointer p)
{
super(p);
HrInit();
}
// VTable
// ------
// IUnknown:
// 0: AddRef
// 1: QueryInterface
// 2: Release
//
// ITaskBarList:
// 3: HrInit
// 4: AddTab
// 5: DeleteTab
// 6: ActivateTab
// 7: SetActiveAlt
//
// ITaskBarList2
// 8: MarkFullscreenWindow
//
// ITaskBarList3:
// 9: SetProgressValue
// 10: SetProgressState
// 11: RegisterTab
// 12: UnregisterTab
// 13: SetTabOrder
// 14: SetTabActive
// 15: ThumbBarAddButtons
// 16: ThumbBarAddButtons
// 17: ThumbBarSetImageList
// 18: SetOverlayIcon
// 19: SetThumbnailTooltip
// 20: SetThumbnailClip
//
// ITaskbarList4:
// 21: SetTabProperties
/**
* https://msdn.microsoft.com/en-us/library/bb774650(v=vs.85).aspx
*/
private void HrInit()
{
int hr = this._invokeNativeInt(3, new Object[]
{ this.getPointer() });
COMUtils.checkRC(new HRESULT(hr));
}
/**
* https://msdn.microsoft.com/en-us/library/dd391696(v=vs.85).aspx
*/
private void SetOverlayIcon(HWND hwnd, HICON hIcon, String pszDescription)
{
int hr = this._invokeNativeInt(18, new Object[]
{ this.getPointer(), hwnd, hIcon, pszDescription });
COMUtils.checkRC(new HRESULT(hr));
}
/**
* Sets an overlay image to the taskbar icon.
* @param frame The window that should receive the overlay
* @param image The overlay image, can be <tt>null</tt> to clear the overlay
* @param description An optional tooltip text, can be <tt>null</tt>
*/
public void SetOverlayIcon(Component frame, BufferedImage image,
String description)
{
HICON ico = null;
if (image != null)
{
byte[] iconBytes = ImageConverter.writeTransparentIcoImage(image);
ico = ImageConverter.createIcon(iconBytes);
}
HWND hwnd = new HWND(Native.getComponentPointer(frame));
SetOverlayIcon(hwnd, ico, description);
}
}

@ -0,0 +1,47 @@
/*
* Jitsi, the OpenSource Java VoIP and Instant Messaging client.
*
* Copyright @ 2015 Atlassian Pty Ltd
*
* 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 net.java.sip.communicator.impl.osdependent.windows;
import com.sun.jna.*;
import com.sun.jna.platform.win32.*;
import com.sun.jna.win32.*;
/**
* Extension to missing user32 Windows APIs
*
* @author Ingo Bauersachs
*/
interface User32Ex
extends StdCallLibrary
{
User32Ex INSTANCE = (User32Ex) Native.loadLibrary("user32", User32Ex.class,
W32APIOptions.DEFAULT_OPTIONS);
/**
* https://msdn.microsoft.com/en-us/library/windows/desktop/ms648074(v=vs.85).aspx
*/
int LookupIconIdFromDirectoryEx(Memory presbits, boolean fIcon,
int cxDesired, int cyDesired, int Flags);
/**
* https://msdn.microsoft.com/en-us/library/windows/desktop/ms648061(v=vs.85).aspx
*/
WinDef.HICON CreateIconFromResourceEx(Pointer pbIconBits,
WinDef.DWORD cbIconBits, boolean fIcon, WinDef.DWORD dwVersion,
int cxDesired, int cyDesired, int uFlags);
}
Loading…
Cancel
Save