admin管理员组文章数量:1317898
My java (Java 21) application has a part of gui that lets you drag an image found on Google in web-browser to the application, the data flavor of the url is logged, and if thee is no valid data flavor a message is also logged.
On Windows it works fine (tested with Firefox)
On MacOS it doesnt work (tested with Firefox and Safari) and nothing is even logged. Based on suggestion VGR i now log if there is an exception and also I log when first enter the drop method.
Retested on MacOS and it doesnt even log the Started Drop method so it seems that MacOS is preventing the drag drop before it even gets to my code.
Whereas On MacOS it does work if you drop an image from Finder instead
What is causing the difference?
This is the class doing the work, key method is drop()
package com.jthink.songkong.ui.startdialog.editsongs;
import com.jthink.songkong.analyse.general.ArtworkHelper;
import com.jthink.songkong.analyse.musicbrainz.RemoteArtworkLookup;
import com.jthink.songkong.db.ImageCache;
import com.jthink.songkong.db.SongCache;
import com.jthink.songkong.preferences.UserOption;
import com.jthink.songkong.preferences.UserPreferences;
import com.jthink.songkong.ui.MainWindow;
import com.jthink.songkong.ui.startdialog.imagefilters.ImageFileFilter;
import com.jthink.songkong.ui.startdialog.imagefilters.ImageFilterFactory;
import com.jthink.songlayer.CoverImage;
import javax.imageio.IIOImage;
import javax.imageio.ImageIO;
import javax.imageio.ImageWriteParam;
import javax.imageio.ImageWriter;
import javax.imageio.stream.MemoryCacheImageOutputStream;
import javax.swing.*;
import java.awt.*;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.Transferable;
import java.awt.dnd.DnDConstants;
import java.awt.dnd.DropTarget;
import java.awt.dnd.DropTargetDropEvent;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.URI;
import java.URL;
import java.nio.file.Files;
import java.util.logging.Level;
import static com.jthink.songkong.ui.startdialog.editsongs.ArtworkTab.ARTWORK_SIZE;
import static com.jthink.songkong.ui.startdialog.editsongs.ImageSizeDefaults.MAX_IMAGE_SIZE;
import static com.jthink.songkong.ui.startdialog.editsongs.ImageSizeDefaults.MIN_IMAGE_SIZE;
public class ArtworkDropTarget extends DropTarget
{
private static CoverImage coverImage;
private JLabel displayArtwork;
public static DataFlavor uriListFlavor; //This is used by Linux for loading files
public static DataFlavor imageFlavor; //This is used by Firefox, when drag single image file
public static DataFlavor urlFlavour; //This is used by Firefox, when drag single image file
public static DataFlavor pictImageFlavor; //This is used by OSX for iTunes
public static DataFlavor imageIconDataFlavor; //When dragging from existingArtwork
static
{
try
{
uriListFlavor = new DataFlavor("text/uri-list;class=java.lang.String");
urlFlavour = new DataFlavor("application/x-java-url;class=java.URL");
imageFlavor = new DataFlavor("image/x-java-image;class=java.awt.Image");
pictImageFlavor = new DataFlavor("image/x-pict;class=java.io.InputStream");
imageIconDataFlavor = new DataFlavor(DataFlavor.javaJVMLocalObjectMimeType +
";class=\""+javax.swing.Icon.class.getName() + "\"");
}
catch (ClassNotFoundException cne)
{
throw new RuntimeException("unable to create uri-list flavor in drop target:" + cne.getMessage());
}
}
public ArtworkDropTarget(Component c, JLabel displayArtwork)
throws HeadlessException
{
super(c, DnDConstants.ACTION_COPY, null, true, null);
this.displayArtwork= displayArtwork;
}
public static void setCoverImage(CoverImage coverImage)
{
ArtworkDropTarget.coverImage = coverImage;
}
/**
* When used as Drop target
*
* @param dtde
*/
public synchronized void drop(DropTargetDropEvent dtde)
{
MainWindow.logger.severe("Starting Drop");
dtde.acceptDrop(DnDConstants.ACTION_COPY);
Transferable trans = dtde.getTransferable();
try
{
if (trans.isDataFlavorSupported(imageIconDataFlavor))
{
MainWindow.logger.severe("Received ImageIcon:"+trans.getTransferData(imageIconDataFlavor));
ImageIconWithOriginalImage icon = (ImageIconWithOriginalImage)trans.getTransferData(imageIconDataFlavor);
setExistingArtwork((BufferedImage)icon.getOriginalImage());
}
else if (trans.isDataFlavorSupported(DataFlavor.javaFileListFlavor) && ImageFilterFactory.getImageFilter().accept(((java.util.List<File>) trans.getTransferData(DataFlavor.javaFileListFlavor)).get(0)))
{
MainWindow.logger.severe("Received Files:"+trans.getTransferData(DataFlavor.javaFileListFlavor));
File file = ((java.util.List<File>) trans.getTransferData(DataFlavor.javaFileListFlavor)).get(0);
setExistingArtwork(file);
}
else if (trans.isDataFlavorSupported(imageFlavor) && trans.getTransferData(ArtworkDropTarget.imageFlavor) instanceof BufferedImage)
{
MainWindow.logger.severe("Received Image:"+trans.getTransferData(ArtworkDropTarget.imageFlavor));
setExistingArtwork((BufferedImage)trans.getTransferData(ArtworkDropTarget.imageFlavor));
}
else if (trans.isDataFlavorSupported(urlFlavour))
{
URI imageUri = ((URL) trans.getTransferData(ArtworkDropTarget.urlFlavour)).toURI();
MainWindow.logger.severe("Received ImageUrl:" + imageUri);
URI subUri = GoogleImageSearch.getImageUrlFromImageSearchUrl(imageUri);
if (GoogleImageSearch.isGoogleImageSearch(imageUri) && subUri!=null)
{
MainWindow.logger.severe("Received ImageSubUrl:" + subUri);
setExistingArtwork(subUri);
}
else
{
MainWindow.logger.severe("Received ImageUrl:" + imageUri);
setExistingArtwork(imageUri);
}
}
else
{
MainWindow.logger.severe("Unknown DataFlavor for ImageUrl");
for(DataFlavor next:trans.getTransferDataFlavors())
{
MainWindow.logger.severe(next.toString());
}
}
dtde.dropComplete(true);
}
catch(Exception ex)
{
MainWindow.logger.log(Level.SEVERE, ex.getMessage(), ex);
dtde.dropComplete(false);
}
}
/**
* TODO do we have to convert to JPG, cant we keep as PNG
* @param image
*/
private void setExistingArtwork(BufferedImage image)
{
try
{
byte[] imageData = writeCompressedJpegImageToByteArray(image, 1.0f);
coverImage = ImageCache.saveNewCoverImage(image, imageData, "internet", com.jthink.songlayer.CoverImage.createKeyFromData(imageData),
MIN_IMAGE_SIZE,
UserOption.ARTWORK_MAX_SIZE.getIntValue()
);
SongCache.saveImagesForReport(coverImage, null);
createNewArtworkImage(image);
}
catch (IOException ex)
{
MainWindow.logger.log(Level.SEVERE, ex.getMessage(), ex);
}
}
/**
* Write Compressed Jpeg, use minimum compression of 1f for best quality
*
* @param bi
* @param compressionLevel
* @return
* @throws IOException
*/
private static byte[] writeCompressedJpegImageToByteArray(BufferedImage bi, float compressionLevel) throws IOException
{
ImageWriter imageWriter = ImageIO.getImageWritersByFormatName( "jpg").next();
ImageWriteParam writeParams = imageWriter.getDefaultWriteParam();
writeParams.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
writeParams.setCompressionQuality(compressionLevel);
final ByteArrayOutputStream output = new ByteArrayOutputStream();
imageWriter.setOutput( new MemoryCacheImageOutputStream(output));
IIOImage outputImage = new IIOImage(bi, null, null);
imageWriter.write(null, outputImage, writeParams);
imageWriter.dispose();
output.flush();
final byte[] imageData = output.toByteArray();
return imageData;
}
/**
*
* @param imageUri
*/
private void setExistingArtwork(URI imageUri)
{
byte[] imageData = RemoteArtworkLookup.getDirectImage(imageUri.toString());
if (imageData != null)
{
BufferedImage image = ArtworkHelper.createBufferedImageFromRawData(imageData, imageUri.toString());
coverImage = ImageCache.saveNewCoverImage(image, imageData, "internet", com.jthink.songlayer.CoverImage.createKeyFromData(imageData),
UserPreferences.getInstance().getMinImageSize(),
(Integer) UserOption.ARTWORK_MAX_SIZE.getUserPref().getValue());
SongCache.saveImagesForReport(coverImage, null);
createNewArtworkImage(image);
}
else
{
MainWindow.logger.severe("Unable To Decode Image:"+imageUri.toString());
}
}
/**
*
* @param file
*/
private void setExistingArtwork(File file)
{
try
{
ImageFileFilter iff = ImageFilterFactory.getImageFilter();
if(iff.accept(file))
{
byte[] imageData = Files.readAllBytes(file.toPath());
BufferedImage image = ArtworkHelper.createBufferedImageFromRawData(imageData, file.getName());
coverImage = ImageCache.saveNewCoverImage(image, imageData, file.getName(),
com.jthink.songlayer.CoverImage.createKeyFromData(imageData),
UserPreferences.getInstance().getMinImageSize(),
(Integer) UserOption.ARTWORK_MAX_SIZE.getUserPref().getValue());
SongCache.saveImagesForReport(coverImage, null);
createNewArtworkImage(image);
}
}
catch(Exception ex)
{
MainWindow.logger.log(Level.SEVERE, ex.getMessage(), ex);
}
}
/**
* Update new artwork label with version of this image
*
* @param image
*/
private void createNewArtworkImage(BufferedImage image)
{
BufferedImage ret = new BufferedImage(ARTWORK_SIZE, ARTWORK_SIZE, BufferedImage.TYPE_INT_RGB);
ret.getGraphics().drawImage(image, 0, 0, ARTWORK_SIZE, ARTWORK_SIZE, null);
ImageIcon ii = new ImageIcon(ret);
displayArtwork.setIcon(ii);
displayArtwork.setText(image.getWidth() + " x " + image.getHeight());
ArtworkFont.formatArtworkLabel(displayArtwork);
}
public static CoverImage getCoverImage()
{
return coverImage;
}
}
Update
Removed the synchonized keyword as suggested by VGR, now gets into drop but throws an Exception
01/2025 12.52.09:GMT:ArtworkDropTarget:drop:SEVERE: Cannot invoke "String.length()" because "spec" is null
java.awt.dnd.InvalidDnDOperationException: Cannot invoke "String.length()" because "spec" is null
at java.desktop/sun.awt.dnd.SunDropTargetContextPeer.getTransferData(SunDropTargetContextPeer.java:274)
at java.desktop/sun.awt.datatransfer.TransferableProxy.getTransferData(TransferableProxy.java:73)
at java.desktop/java.awt.dnd.DropTargetContext$TransferableProxy.getTransferData(DropTargetContext.java:387)
at com.jthink.songkong.ui.startdialog.editsongs.ArtworkDropTarget.drop(ArtworkDropTarget.java:109)
at java.desktop/sun.awt.dnd.SunDropTargetContextPeer.processDropMessage(SunDropTargetContextPeer.java:548)
at java.desktop/sun.lwawt.macosx.CDropTargetContextPeer.processDropMessage(CDropTargetContextPeer.java:129)
at java.desktop/sun.awt.dnd.SunDropTargetContextPeer$EventDispatcher.dispatchDropEvent(SunDropTargetContextPeer.java:864)
at java.desktop/sun.awt.dnd.SunDropTargetContextPeer$EventDispatcher.dispatchEvent(SunDropTargetContextPeer.java:788)
at java.desktop/sun.awt.dnd.SunDropTargetEvent.dispatch(SunDropTargetEvent.java:48)
at java.desktop/java.awt.Component.dispatchEventImpl(Component.java:4861)
at java.desktop/java.awt.Container.dispatchEventImpl(Container.java:2324)
at java.desktop/java.awt.Component.dispatchEvent(Component.java:4828)
at java.desktop/java.awt.LightweightDispatcher.retargetMouseEvent(Container.java:4948)
at java.desktop/java.awt.LightweightDispatcher.processDropTargetEvent(Container.java:4649)
at java.desktop/java.awt.LightweightDispatcher.dispatchEvent(Container.java:4511)
at java.desktop/java.awt.Container.dispatchEventImpl(Container.java:2310)
at java.desktop/java.awt.Window.dispatchEventImpl(Window.java:2780)
at java.desktop/java.awt.Component.dispatchEvent(Component.java:4828)
at java.desktop/java.awt.EventQueue.dispatchEventImpl(EventQueue.java:775)
at java.desktop/java.awt.EventQueue$4.run(EventQueue.java:720)
at java.desktop/java.awt.EventQueue$4.run(EventQueue.java:714)
at java.base/java.security.AccessController.doPrivileged(AccessController.java:400)
at java.base/java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:87)
at java.base/java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:98)
at java.desktop/java.awt.EventQueue$5.run(EventQueue.java:747)
at java.desktop/java.awt.EventQueue$5.run(EventQueue.java:745)
at java.base/java.security.AccessController.doPrivileged(AccessController.java:400)
at java.base/java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:87)
at java.desktop/java.awt.EventQueue.dispatchEvent(EventQueue.java:744)
at java.desktop/java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:203)
at java.desktop/java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:124)
at java.desktop/java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:113)
at java.desktop/java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:109)
at java.desktop/java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:101)
at java.desktop/java.awt.EventDispatchThread.run(EventDispatchThread.java:90)
Possible related issue -
Update 2
As also suggsted by VGR removed multiple calls to getTransferData() so can only ever be called once per invocation of method, made no difference.
Update 3
Looked at the data flavors available and swapped round the if
statement so it looks for
uriListFlavor = new DataFlavor("text/uri-list;class=java.lang.String");
before
urlFlavour = new DataFlavor("application/x-java-url;class=java.URL");
and then it works.
So it is my opnion that since before failed calling getTransferData()
with
java.awt.dnd.InvalidDnDOperationException: Cannot invoke "String.length()" because "spec" is null
at java.desktop/sun.awt.dnd.SunDropTargetContextPeer.getTransferData(SunDropTargetContextPeer.java:274)
at java.desktop/sun.awt.datatransfer.TransferableProxy.getTransferData(TransferableProxy.java:73)
at java.desktop/java.awt.dnd.DropTargetContext$TransferableProxy.getTransferData(DropTargetContext.java:387)
for a flavor it had reported it had and before I did any other processing that this is actually a bug in the code that implements Java for MacOS
本文标签: Dropping of image url from webbrowser on Java app not working for macOSStack Overflow
版权声明:本文标题:Dropping of image url from webbrowser on Java app not working for macOS - Stack Overflow 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.betaflare.com/web/1742034464a2417076.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论