Add user and captcha handling methods
This commit is contained in:
parent
23ac46eee8
commit
69b68ed1e4
9 changed files with 854 additions and 436 deletions
|
@ -8,6 +8,8 @@ import net.nosial.socialclient.exceptions.CryptographyException;
|
|||
import net.nosial.socialclient.exceptions.ResolutionException;
|
||||
import net.nosial.socialclient.exceptions.RpcException;
|
||||
import net.nosial.socialclient.objects.RpcRequest;
|
||||
import net.nosial.socialclient.objects.standard.ImageCaptcha;
|
||||
import net.nosial.socialclient.objects.standard.SelfUser;
|
||||
|
||||
import java.security.KeyPair;
|
||||
import java.security.PrivateKey;
|
||||
|
@ -17,8 +19,6 @@ import java.util.Map;
|
|||
|
||||
public class Client extends RpcClient
|
||||
{
|
||||
|
||||
|
||||
public Client(String domain) throws ResolutionException, CryptographyException
|
||||
{
|
||||
super(domain);
|
||||
|
@ -31,7 +31,8 @@ public class Client extends RpcClient
|
|||
|
||||
public String createSession(KeyPair keyPair) throws RpcException
|
||||
{
|
||||
RpcResult response = this.sendRequest(new RpcRequest("create_session", Utilities.randomCrc32(), new HashMap<>(){{
|
||||
RpcResult response = this.sendRequest(new RpcRequest("createSession", Utilities.randomCrc32(), new HashMap<>()
|
||||
{{
|
||||
put("public_key", Cryptography.exportPublicKey(keyPair));
|
||||
}}));
|
||||
|
||||
|
@ -78,4 +79,90 @@ public class Client extends RpcClient
|
|||
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean register(String username) throws RpcException
|
||||
{
|
||||
RpcResult response = this.sendRequest(new RpcRequest("register", Utilities.randomCrc32(), new HashMap<>()
|
||||
{{
|
||||
put("username", username);
|
||||
}}));
|
||||
|
||||
if(response == null)
|
||||
{
|
||||
throw new RpcException("Response is null");
|
||||
}
|
||||
|
||||
if(!response.isSuccess())
|
||||
{
|
||||
throw new RpcException(response);
|
||||
}
|
||||
|
||||
return (boolean)response.getResponse().getResult();
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public SelfUser getMe() throws RpcException
|
||||
{
|
||||
RpcResult response = this.sendRequest(new RpcRequest("getMe", Utilities.randomCrc32()));
|
||||
|
||||
if(response == null)
|
||||
{
|
||||
throw new RpcException("Response is null");
|
||||
}
|
||||
|
||||
if(!response.isSuccess())
|
||||
{
|
||||
throw new RpcException(response);
|
||||
}
|
||||
|
||||
if(response.getResponse().getResult() instanceof Map<?,?>)
|
||||
{
|
||||
return new SelfUser((Map<String, Object>) response.getResponse().getResult());
|
||||
}
|
||||
|
||||
throw new RpcException("Unexpected result type, expected Map<?,?>");
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public ImageCaptcha verificationGetImageCaptcha() throws RpcException
|
||||
{
|
||||
RpcResult response = this.sendRequest(new RpcRequest("verificationGetImageCaptcha", Utilities.randomCrc32()));
|
||||
|
||||
if(response == null)
|
||||
{
|
||||
throw new RpcException("Response is null");
|
||||
}
|
||||
|
||||
if(!response.isSuccess())
|
||||
{
|
||||
throw new RpcException(response);
|
||||
}
|
||||
|
||||
if(response.getResponse().getResult() instanceof Map<?,?>)
|
||||
{
|
||||
return new ImageCaptcha((Map<String, Object>) response.getResponse().getResult());
|
||||
}
|
||||
|
||||
throw new RpcException("Unexpected result type, expected Map<?,?>");
|
||||
}
|
||||
|
||||
public boolean verificationAnswerImageCaptcha(String answer) throws RpcException
|
||||
{
|
||||
RpcResult response = this.sendRequest(new RpcRequest("verificationAnswerImageCaptcha", Utilities.randomCrc32(), new HashMap<>()
|
||||
{{
|
||||
put("answer", answer);
|
||||
}}));
|
||||
|
||||
if(response == null)
|
||||
{
|
||||
throw new RpcException("Response is null");
|
||||
}
|
||||
|
||||
if(!response.isSuccess())
|
||||
{
|
||||
throw new RpcException(response);
|
||||
}
|
||||
|
||||
return (boolean)response.getResponse().getResult();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,8 +9,10 @@ import java.util.Hashtable;
|
|||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class Resolver {
|
||||
public static ResolvedServer resolveDomain(String domain) throws ResolutionException {
|
||||
public class Resolver
|
||||
{
|
||||
public static ResolvedServer resolveDomain(String domain) throws ResolutionException
|
||||
{
|
||||
final Pattern fullPattern = Pattern.compile("v=socialbox;sb-rpc=(https?://[^;]+);sb-key=([^;]+)");
|
||||
|
||||
Hashtable<String, String> env = new Hashtable<>();
|
||||
|
@ -18,18 +20,21 @@ public class Resolver {
|
|||
env.put("com.sun.jndi.dns.cache.ttl", "1");
|
||||
env.put("com.sun.jndi.dns.cache.negative.ttl", "0");
|
||||
|
||||
try {
|
||||
try
|
||||
{
|
||||
DirContext dirContext = new InitialDirContext(env);
|
||||
Attributes attributes = dirContext.getAttributes("dns:/" + domain, new String[]{"TXT"});
|
||||
Attribute attributeTXT = attributes.get("TXT");
|
||||
|
||||
if (attributeTXT == null) {
|
||||
if (attributeTXT == null)
|
||||
{
|
||||
throw new ResolutionException("Failed to resolve DNS TXT records for " + domain);
|
||||
}
|
||||
|
||||
StringBuilder fullRecordBuilder = new StringBuilder();
|
||||
|
||||
for (int i = 0; i < attributeTXT.size(); i++) {
|
||||
for (int i = 0; i < attributeTXT.size(); i++)
|
||||
{
|
||||
String value = (String) attributeTXT.get(i);
|
||||
fullRecordBuilder.append(value.replaceAll("^\"|\"$", "").trim());
|
||||
}
|
||||
|
@ -37,23 +42,30 @@ public class Resolver {
|
|||
String fullRecord = fullRecordBuilder.toString();
|
||||
Matcher matcher = fullPattern.matcher(fullRecord);
|
||||
|
||||
if (matcher.find()) {
|
||||
if (matcher.find())
|
||||
{
|
||||
String endpoint = matcher.group(1);
|
||||
String publicKey = matcher.group(2).replaceAll("\\s+", "");
|
||||
|
||||
if (endpoint == null || endpoint.isEmpty()) {
|
||||
if (endpoint == null || endpoint.isEmpty())
|
||||
{
|
||||
throw new ResolutionException("Failed to resolve RPC endpoint for " + domain);
|
||||
}
|
||||
|
||||
if (publicKey == null || publicKey.isEmpty()) {
|
||||
if (publicKey == null || publicKey.isEmpty())
|
||||
{
|
||||
throw new ResolutionException("Failed to resolve public key for " + domain);
|
||||
}
|
||||
|
||||
return new ResolvedServer(endpoint, publicKey);
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new ResolutionException("Failed to find valid SocialBox record for " + domain);
|
||||
}
|
||||
} catch (NamingException e) {
|
||||
}
|
||||
catch (NamingException e)
|
||||
{
|
||||
throw new ResolutionException("Error resolving domain: " + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,26 +1,26 @@
|
|||
package net.nosial.socialclient.classes;
|
||||
package net.nosial.socialclient.classes;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.core.type.TypeReference;
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.squareup.okhttp.*;
|
||||
import net.nosial.socialclient.abstracts.RpcResult;
|
||||
import net.nosial.socialclient.exceptions.CryptographyException;
|
||||
import net.nosial.socialclient.exceptions.ResolutionException;
|
||||
import net.nosial.socialclient.objects.ResolvedServer;
|
||||
import net.nosial.socialclient.objects.RpcRequest;
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.core.type.TypeReference;
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.squareup.okhttp.*;
|
||||
import net.nosial.socialclient.abstracts.RpcResult;
|
||||
import net.nosial.socialclient.exceptions.CryptographyException;
|
||||
import net.nosial.socialclient.exceptions.ResolutionException;
|
||||
import net.nosial.socialclient.objects.ResolvedServer;
|
||||
import net.nosial.socialclient.objects.RpcRequest;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.PrivateKey;
|
||||
import java.security.PublicKey;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.io.IOException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.PrivateKey;
|
||||
import java.security.PublicKey;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class RpcClient
|
||||
{
|
||||
public class RpcClient
|
||||
{
|
||||
private final static ObjectMapper OBJECT_MAPPER = new ObjectMapper();
|
||||
private final static MediaType MEDIA_TYPE_JSON = MediaType.parse("application/json; charset=utf-8");
|
||||
|
||||
|
@ -30,7 +30,7 @@ public class RpcClient
|
|||
private final OkHttpClient httpClient;
|
||||
|
||||
private String clientName = "SocialClient Java";
|
||||
private String clientVersion = "1.0.0";
|
||||
private String clientVersion = "1.0";
|
||||
|
||||
protected String sessionUuid;
|
||||
protected PrivateKey privateKey;
|
||||
|
@ -226,11 +226,9 @@ public class RpcClient
|
|||
try
|
||||
{
|
||||
// NOTE: Signature looks okay, it's base64 encoded.
|
||||
System.out.println("Hash: " + Cryptography.sha1(jsonData));
|
||||
System.out.println("Signature: " + Cryptography.signContent(jsonData, privateKey));
|
||||
this.addHeader("Signature", Cryptography.signContent(jsonData, privateKey));
|
||||
}
|
||||
catch(CryptographyException | NoSuchAlgorithmException e)
|
||||
catch(CryptographyException e)
|
||||
{
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
@ -258,6 +256,11 @@ public class RpcClient
|
|||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
if(responseString.isEmpty())
|
||||
{
|
||||
throw new RuntimeException("The request was successful but the server did not indicate a empty response");
|
||||
}
|
||||
|
||||
Object decoded = decode(responseString);
|
||||
|
||||
// Singular object response
|
||||
|
@ -420,4 +423,4 @@ public class RpcClient
|
|||
throw new RuntimeException("Failed to encode input to JSON Data", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,8 +11,15 @@ public enum StandardErrorCodes
|
|||
SERVER_UNAVAILABLE(-2001),
|
||||
|
||||
INVALID_PUBLIC_KEY(-3000),
|
||||
SESSION_NOT_FOUND(-3001),
|
||||
UNSUPPORTED_AUTHENTICATION_TYPE(-3002);
|
||||
UNSUPPORTED_AUTHENTICATION_TYPE(-3001),
|
||||
ALREADY_AUTHENTICATED(-3002),
|
||||
AUTHENTICATION_REQUIRED(-3003),
|
||||
SESSION_NOT_FOUND(-3004),
|
||||
SESSION_REQUIRED(-3005),
|
||||
|
||||
PEER_NOT_FOUND(-4000),
|
||||
INVALID_USERNAME(-4001),
|
||||
USERNAME_ALREADY_EXISTS(-4002);
|
||||
|
||||
private final int code;
|
||||
|
||||
|
|
|
@ -0,0 +1,39 @@
|
|||
package net.nosial.socialclient.enums.flags;
|
||||
|
||||
public enum PeerFlags
|
||||
{
|
||||
// Administrative Flags
|
||||
ADMIN(true, "ADMIN"),
|
||||
MODERATOR(true, "MODERATOR"),
|
||||
|
||||
// General Flags
|
||||
VERIFIED(true, "VERIFIED"),
|
||||
|
||||
// Verification Flags
|
||||
VER_SET_PASSWORD(false, "VER_SET_PASSWORD"),
|
||||
VER_SET_OTP(false, "VER_SET_OTP"),
|
||||
VER_SET_DISPLAY_NAME(false, "VER_SET_DISPLAY_NAME"),
|
||||
VER_EMAIL(false, "VER_EMAIL"),
|
||||
VER_SMS(false, "VER_SMS"),
|
||||
VER_PHONE_CALL(false, "VER_PHONE_CALL"),
|
||||
VER_SOLVE_IMAGE_CAPTCHA(false, "VER_SOLVE_IMAGE_CAPTCHA");
|
||||
|
||||
private final boolean isPublic;
|
||||
private final String flag;
|
||||
|
||||
PeerFlags(boolean isPublic, String flag)
|
||||
{
|
||||
this.isPublic = isPublic;
|
||||
this.flag = flag;
|
||||
}
|
||||
|
||||
public String getFlag()
|
||||
{
|
||||
return this.flag;
|
||||
}
|
||||
|
||||
public boolean isPublic()
|
||||
{
|
||||
return this.isPublic;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
package net.nosial.socialclient.objects.standard;
|
||||
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.Base64;
|
||||
import java.util.Map;
|
||||
|
||||
public class ImageCaptcha
|
||||
{
|
||||
private final int expires;
|
||||
private final String image;
|
||||
|
||||
public ImageCaptcha(Map<String, Object> data)
|
||||
{
|
||||
this.expires = (int) data.get("expires");
|
||||
this.image = (String) data.get("image");
|
||||
}
|
||||
|
||||
public int getExpires()
|
||||
{
|
||||
return expires;
|
||||
}
|
||||
|
||||
public String getImage()
|
||||
{
|
||||
return image;
|
||||
}
|
||||
|
||||
public BufferedImage getImageBuffer() throws IOException
|
||||
{
|
||||
String base64Image = this.image;
|
||||
if(this.image.contains(","))
|
||||
{
|
||||
base64Image = this.image.split(",")[1];
|
||||
}
|
||||
|
||||
byte[] imageBytes = Base64.getDecoder().decode(base64Image);
|
||||
|
||||
ByteArrayInputStream bis = new ByteArrayInputStream(imageBytes);
|
||||
BufferedImage image = javax.imageio.ImageIO.read(bis);
|
||||
bis.close();
|
||||
|
||||
return image;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,106 @@
|
|||
package net.nosial.socialclient.objects.standard;
|
||||
|
||||
import net.nosial.socialclient.enums.flags.PeerFlags;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class SelfUser
|
||||
{
|
||||
private final String uuid;
|
||||
private final boolean enabled;
|
||||
private final String address;
|
||||
private final String username;
|
||||
private final String displayName;
|
||||
private final List<PeerFlags> flags;
|
||||
private final int created;
|
||||
|
||||
/**
|
||||
* Constructs a SelfUser instance by populating fields from a provided data map.
|
||||
*
|
||||
* @param data a map containing user details such as uuid, username, display name, flags,
|
||||
* enabled status, and creation time.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public SelfUser(Map<String, Object> data)
|
||||
{
|
||||
this.uuid = (String) data.get("uuid");
|
||||
this.enabled = (boolean) data.get("enabled");
|
||||
this.username = (String) data.get("username");
|
||||
this.address = (String) data.get("address");
|
||||
this.displayName = (String) data.get("display_name");
|
||||
this.flags = ((List<String>) data.get("flags")).stream().map(PeerFlags::valueOf).collect(Collectors.toList());
|
||||
this.created = (int) data.get("created");
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the universally unique identifier (UUID) associated with the user.
|
||||
*
|
||||
* @return the UUID as a String
|
||||
*/
|
||||
public String getUuid()
|
||||
{
|
||||
return uuid;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the user account is enabled.
|
||||
*
|
||||
* @return true if the user account is enabled; false otherwise.
|
||||
*/
|
||||
public boolean isEnabled()
|
||||
{
|
||||
return enabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the username associated with this SelfUser instance.
|
||||
*
|
||||
* @return the username of the user
|
||||
*/
|
||||
public String getUsername()
|
||||
{
|
||||
return username;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the address associated with this SelfUser instance.
|
||||
*
|
||||
* @return the address of the user
|
||||
*/
|
||||
public String getAddress()
|
||||
{
|
||||
return address;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the display name of the user.
|
||||
*
|
||||
* @return the display name of the user
|
||||
*/
|
||||
public String getDisplayName()
|
||||
{
|
||||
return displayName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the list of flags associated with the user.
|
||||
*
|
||||
* @return a list of PeerFlags indicating various user permissions and statuses.
|
||||
*/
|
||||
public List<PeerFlags> getFlags()
|
||||
{
|
||||
return flags;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the creation timestamp of this user.
|
||||
*
|
||||
* @return the creation timestamp as an integer
|
||||
*/
|
||||
public int getCreated()
|
||||
{
|
||||
return created;
|
||||
}
|
||||
}
|
|
@ -1,5 +1,6 @@
|
|||
package net.nosial.socialclient;
|
||||
package net.nosial.socialclient.methods;
|
||||
|
||||
import net.nosial.socialclient.Client;
|
||||
import net.nosial.socialclient.classes.Cryptography;
|
||||
import net.nosial.socialclient.exceptions.CryptographyException;
|
||||
import net.nosial.socialclient.exceptions.ResolutionException;
|
||||
|
@ -10,11 +11,10 @@ import java.security.KeyPair;
|
|||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
class SessionTest
|
||||
public class CreateSessionTest
|
||||
{
|
||||
|
||||
@Test
|
||||
void createSessionTest()
|
||||
void testSessionCreation()
|
||||
{
|
||||
Client socialClient;
|
||||
KeyPair keyPair;
|
||||
|
@ -23,18 +23,9 @@ class SessionTest
|
|||
try
|
||||
{
|
||||
socialClient = new Client("n64.cc");
|
||||
}
|
||||
catch (ResolutionException | CryptographyException e)
|
||||
{
|
||||
fail(e.getMessage(), e);
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
keyPair = Cryptography.generateKeyPair();
|
||||
}
|
||||
catch (CryptographyException e)
|
||||
catch (ResolutionException | CryptographyException e)
|
||||
{
|
||||
fail(e.getMessage(), e);
|
||||
return;
|
127
src/test/java/net/nosial/socialclient/methods/RegisterTest.java
Normal file
127
src/test/java/net/nosial/socialclient/methods/RegisterTest.java
Normal file
|
@ -0,0 +1,127 @@
|
|||
package net.nosial.socialclient.methods;
|
||||
|
||||
import net.nosial.socialclient.Client;
|
||||
import net.nosial.socialclient.classes.Cryptography;
|
||||
import net.nosial.socialclient.exceptions.CryptographyException;
|
||||
import net.nosial.socialclient.exceptions.ResolutionException;
|
||||
import net.nosial.socialclient.exceptions.RpcException;
|
||||
import net.nosial.socialclient.objects.standard.ImageCaptcha;
|
||||
import net.nosial.socialclient.objects.standard.SelfUser;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.IOException;
|
||||
import java.security.KeyPair;
|
||||
import java.util.Random;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
public class RegisterTest
|
||||
{
|
||||
@Test
|
||||
public void registerTest()
|
||||
{
|
||||
// Step 1. Connect and establish a session to the server
|
||||
final Client socialClient;
|
||||
final KeyPair keyPair;
|
||||
final String sessionUuid;
|
||||
|
||||
try
|
||||
{
|
||||
socialClient = new Client("n64.cc");
|
||||
keyPair = Cryptography.generateKeyPair();
|
||||
sessionUuid = socialClient.createAndSetSession(keyPair);
|
||||
|
||||
}
|
||||
catch (ResolutionException | CryptographyException | RpcException e)
|
||||
{
|
||||
fail(e.getMessage(), e);
|
||||
return;
|
||||
}
|
||||
|
||||
System.out.println("Session UUID: " + sessionUuid);
|
||||
assertNotNull(sessionUuid);
|
||||
|
||||
final SelfUser selfUser;
|
||||
Random random = new Random();
|
||||
String username = "user" + random.nextInt(10000);
|
||||
|
||||
// Step 2. Register the username and getSelf
|
||||
try
|
||||
{
|
||||
assertTrue(socialClient.register(username));
|
||||
selfUser = socialClient.getMe();
|
||||
}
|
||||
catch (RpcException e)
|
||||
{
|
||||
fail(e.getMessage(), e);
|
||||
return;
|
||||
}
|
||||
|
||||
assertNotNull(selfUser);
|
||||
|
||||
// Step 3. Get the captcha
|
||||
ImageCaptcha captcha;
|
||||
|
||||
try
|
||||
{
|
||||
captcha = socialClient.verificationGetImageCaptcha();
|
||||
}
|
||||
catch(RpcException e)
|
||||
{
|
||||
fail(e.getMessage(), e);
|
||||
return;
|
||||
}
|
||||
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
try
|
||||
{
|
||||
ImageFrame frame = new ImageFrame(captcha.getImageBuffer());
|
||||
frame.setVisible(true);
|
||||
}
|
||||
catch(IOException e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
}
|
||||
});
|
||||
|
||||
assertNotNull(captcha);
|
||||
|
||||
// Step 4. Verify the captcha, get input from the user
|
||||
String captchaInput = JOptionPane.showInputDialog("Enter the captcha: ");
|
||||
assertNotNull(captchaInput);
|
||||
|
||||
// Step 5. Verify the captcha
|
||||
try
|
||||
{
|
||||
assertTrue(socialClient.verificationAnswerImageCaptcha(captchaInput));
|
||||
}
|
||||
catch(RpcException e)
|
||||
{
|
||||
fail(e.getMessage(), e);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
private static class ImageFrame extends JFrame {
|
||||
public ImageFrame(BufferedImage image) {
|
||||
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
|
||||
setResizable(false);
|
||||
|
||||
JPanel panel = new JPanel() {
|
||||
@Override
|
||||
protected void paintComponent(Graphics g) {
|
||||
super.paintComponent(g);
|
||||
g.drawImage(image, 0, 0, null);
|
||||
}
|
||||
};
|
||||
|
||||
panel.setPreferredSize(new Dimension(image.getWidth(), image.getHeight()));
|
||||
add(panel);
|
||||
pack();
|
||||
setLocationRelativeTo(null);
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Add table
Reference in a new issue