Wicket: redirecting to wicket page using setResponsePage - java

I have a wicket page which has a link ADD PRODUCT. On clicking the link a modal window open which takes the product information.
ProductAddPanel.java
public class ProductAddPanel extends Panel {
private InlineFrame uploadIFrame = null;
private ModalWindow window;
private Merchant merchant;
private Page redirectPage;
private List<Component> refreshables;
public ProductAddPanel(String id,final Merchant mct,ModalWindow window,List<Component> refreshables,Page p) {
super(id);
this.window = window;
merchant = mct;
redirectPage = p;
this.refreshables = refreshables;
setOutputMarkupId(true);
}
#Override
protected void onBeforeRender() {
super.onBeforeRender();
if (uploadIFrame == null) {
// the iframe should be attached to a page to be able to get its pagemap,
// that's why i'm adding it in onBeforRender
addUploadIFrame();
}
}
// Create the iframe containing the upload widget
private void addUploadIFrame() {
IPageLink iFrameLink = new IPageLink() {
#Override
public Page getPage() {
return new UploadIFrame(window,merchant,redirectPage,refreshables) {
#Override
protected String getOnUploadedCallback() {
return "onUpload_" + ProductAddPanel.this.getMarkupId();
}
};
}
#Override
public Class<UploadIFrame> getPageIdentity() {
return UploadIFrame.class;
}
};
uploadIFrame = new InlineFrame("upload", iFrameLink);
add(uploadIFrame);
}
}
ProductAddPanel.html
<wicket:panel>
<iframe wicket:id="upload" frameborder="0"style="height: 600px; width: 475px;overflow: hidden"></iframe>
</wicket:panel>
I am using a Iframe to upload the image. I have added a iframe to my ProductPanel.html. Because it is not possible to upload file using ajax submit.
UploadIframe.java
protected void onSubmit(AjaxRequestTarget target, Form<?> form) {
DynamicImage imageEntry = new DynamicImage();
if(uploadField.getFileUpload() != null && uploadField.getFileUpload().getClientFileName() != null){
FileUpload upload = uploadField.getFileUpload();
String ct = upload.getContentType();
if (!imgctypes.containsKey(ct)) {
hasError = true;
}
if(upload.getSize() > maximagesize){
hasError = true;
}
if(hasError == false){
System.out.println("######################## Image can be uploaded ################");
imageEntry.setContentType(upload.getContentType());
imageEntry.setImageName(upload.getClientFileName());
imageEntry.setImageSize(upload.getSize());
if(imageEntry != null){
try {
save(imageEntry,upload.getInputStream());
} catch (IOException e) {
e.printStackTrace();
}
}
}else{
target.appendJavaScript("$().toastmessage('showNoticeToast','Please select a valid image!!')");
System.out.println("#################### Error in image uploading ###################");
}
}else{
System.out.println("########################### Image not Selected #####################");
}
MerchantProduct mp =new MerchantProduct();
Product p = new Product();
Date d=new Date();
try {
p.setProductImage(imageEntry.getImageName());
mp.setProduct(p);
Ebean.save(mp);
} catch (Exception e) {
e.printStackTrace();
}
for(Component r: refreshables){
target.add(r);
}
window.close(target);
setResponsePage(MerchantProductPage.class);
}
public void save(DynamicImage imageEntry, InputStream imageStream) throws IOException{
//Read the image data
ByteArrayOutputStream baos = new ByteArrayOutputStream();
copy(imageStream,baos);
baos.close();
byte [] imageData = baos.toByteArray();
baos = null;
//Get the image suffix
String suffix = null;
if("image/gif".equalsIgnoreCase(imageEntry.getContentType())){
suffix = ".gif";
}else if ("image/jpeg".equalsIgnoreCase(imageEntry.getContentType())) {
suffix = ".jpeg";
} else if ("image/png".equalsIgnoreCase(imageEntry.getContentType())) {
suffix = ".png";
}
// Create a unique name for the file in the image directory and
// write the image data into it.
File newFile = createImageFile(suffix);
OutputStream outStream = new FileOutputStream(newFile);
outStream.write(imageData);
outStream.close();
imageEntry.setImageName(newFile.getAbsolutePath());
}
//copy data from src to dst
private void copy(InputStream source, OutputStream destination) throws IOException{
try {
// Transfer bytes from source to destination
byte[] buf = new byte[1024];
int len;
while ((len = source.read(buf)) > 0) {
destination.write(buf, 0, len);
}
source.close();
destination.close();
if (logger.isDebugEnabled()) {
logger.debug("Copying image...");
}
} catch (IOException ioe) {
logger.error(ioe);
throw ioe;
}
}
private File createImageFile(String suffix){
UUID uuid = UUID.randomUUID();
File file = new File(imageDir,uuid.toString() + suffix);
if(logger.isDebugEnabled()){
logger.debug("File "+ file.getAbsolutePath() + "created.");
}
return file;
}
}
}
I am using setResonsePage() to redirect to initial page on which "Add Product" link is present. So that i get the refreshed page having new product information.
My problem is that modal window is not closing on window.close() and inside that window i am getting the refreshed page.
My requirement is that Modal window should close and page should be refreshed. I am passing the Parentpage.class in my setResponsePage().
Any help and advices appreciated! Thanks in advance.

In the ParentPage.class on which modal window is open i called setWindowClosedCallback() method in which I am adding getPage() to target so that page will refresh when modal window is closed.
Here is the code for same
modalDialog.setWindowClosedCallback(new ModalWindow.WindowClosedCallback()
{
private static final long serialVersionUID = 1L;
#Override
public void onClose(AjaxRequestTarget target)
{
target.addComponent(getPage());
}
});

Related

How can I read a picture(s) with Vaadin's upload method?

I have created a upload button, or upload buttons with Vaadin 14. Works like a charm. Perfect. But how do I access the images I uploaded? I have tried to read the tutorials on Vaadin's web page, but they only show the example I have post below. Not how to access the picture. I want to get all the pixels in a matrix form and turn them all into 0..255 gray scale.
Question:
Do you know what method to use to get the images, when I have upload or upload the pictures with this code?
#Data
public class PictureUpload {
private Upload upload;
public PictureUpload() {
// Add picture uploader
upload = new Upload();
addPictureUploader();
}
private void addPictureUploader() {
Div output = new Div();
MultiFileMemoryBuffer buffer = new MultiFileMemoryBuffer();
upload.setReceiver(buffer);
upload.setAcceptedFileTypes("image/jpeg", "image/png", "image/gif");
upload.addSucceededListener(event -> {
Component component = createComponent(event.getMIMEType(), event.getFileName(), buffer.getInputStream(event.getFileName()));
showOutput(event.getFileName(), component, output);
});
}
private Component createComponent(String mimeType, String fileName, InputStream stream) {
if (mimeType.startsWith("text")) {
return createTextComponent(stream);
} else if (mimeType.startsWith("image")) {
Image image = new Image();
try {
byte[] bytes = IOUtils.toByteArray(stream);
image.getElement().setAttribute("src", new StreamResource(fileName, () -> new ByteArrayInputStream(bytes)));
try (ImageInputStream in = ImageIO.createImageInputStream(new ByteArrayInputStream(bytes))) {
final Iterator<ImageReader> readers = ImageIO.getImageReaders(in);
if (readers.hasNext()) {
ImageReader reader = readers.next();
try {
reader.setInput(in);
image.setWidth(reader.getWidth(0) + "px");
image.setHeight(reader.getHeight(0) + "px");
} finally {
reader.dispose();
}
}
}
} catch (IOException e) {
e.printStackTrace();
}
return image;
}
Div content = new Div();
String text = String.format("Mime type: '%s'\nSHA-256 hash: '%s'", mimeType, MessageDigestUtil.sha256(stream.toString()));
content.setText(text);
return content;
}
private Component createTextComponent(InputStream stream) {
String text;
try {
text = IOUtils.toString(stream, StandardCharsets.UTF_8);
} catch (IOException e) {
text = "exception reading stream";
}
return new Text(text);
}
private void showOutput(String text, Component content, HasComponents outputContainer) {
HtmlComponent p = new HtmlComponent(Tag.P);
p.getElement().setText(text);
outputContainer.add(p);
outputContainer.add(content);
}
}
Update:
I did some test with Mr. Lund's example code below in the comments. It seems that I have trouble to show a picture with this code:
#Data
public class LoadExportTemplate {
private VerticalLayout subjectCounterExportButtonUploaders;
public LoadExportTemplate() {
subjectCounterExportButtonUploaders = new VerticalLayout();
Upload pictureUpload = new PictureUpload().getUpload();
Div output = new PictureUpload().getOutput();
subjectCounterExportButtonUploaders.add(pictureUpload, output);
}
}
Where I insert the subjectCounterExportButtonUploaders with this MainView code. I can't see the picture when I upload it.
#Route("")
#Viewport("width=device-width, minimum-scale=1, initial-scale=1, user-scalable=yes, viewport-fit=cover")
#PreserveOnRefresh
public class MainView extends AppLayout {
/**
*
*/
private static final long serialVersionUID = 1L;
public MainView() {
// Get the components
VerticalLayout buildPredictValidateTemplate = new BuildPredictValidateTemplate().getBuildButtonPredictButtonValidateButtonTextArea();
VerticalLayout subjectCounterExportButtonUpload = new LoadExportTemplate().getSubjectCounterExportButtonUploaders();
// Create logo and drawer
Image barImage = new Image("img/barImage.png", "Fisherfaces Logo");
barImage.setHeight("55px");
addToNavbar(new DrawerToggle(), barImage);
// Create tabs and add listeners to them
Tab buildPredictValidate = new Tab("Build & Predict & Validate");
buildPredictValidate.getElement().addEventListener("click", e -> {
getContent().getChildren().forEach(component -> {
boolean visible = component.equals(buildPredictValidateTemplate);
component.setVisible(visible);
});
});
Tab loadExport = new Tab("Load & Export");
loadExport.getElement().addEventListener("click", e -> {
// Walk around from the bug
getContent().getChildren().forEach(component -> {
boolean visible = component.equals(subjectCounterExportButtonUpload);
component.setVisible(visible);
});
});
// Set the contents
setContent(new Div(buildPredictValidateTemplate, subjectCounterExportButtonUpload));
subjectCounterExportButtonUpload.setVisible(false);
// Add them and place them as vertical
Tabs tabs = new Tabs(buildPredictValidate, loadExport);
tabs.setOrientation(Tabs.Orientation.VERTICAL);
addToDrawer(tabs);
}
}
But this example works. Here I can see the picture when I upload it.
#Route(value = UploadView.ROUTE)
#PageTitle(UploadView.TITLE)
public class UploadView extends AppLayout{
/**
*
*/
private static final long serialVersionUID = 1L;
public static final String ROUTE = "upload";
public static final String TITLE = "Upload";
public UploadView() {
PictureUpload pictureUpload = new PictureUpload();
VerticalLayout vl = new VerticalLayout();
vl.add(pictureUpload.getUpload(),pictureUpload.getOutput());
setContent(vl);
}
}
Do you know why?
In the comments you clarified that all you want is to get the byte[] of the image after the upload. Here is how you could do that.
Variant 1: MultiFileMemoryBuffer
MultiFileMemoryBuffer buffer = new MultiFileMemoryBuffer();
upload.setReceiver(buffer);
upload.setAcceptedFileTypes("image/jpeg", "image/png", "image/gif");
upload.addSucceededListener(event -> {
byte[] imageBytes = IOUtils.toByteArray(buffer.getInputStream(event.getFileName()));
});
Variant 2: be your own Receiver interface
public class UploadView implements Receiver {
private FastByteArrayOutputStream outputStream;
private Upload upload;
private Button actualUploadButton;
public UploadView(){
upload = new Upload(this);
upload.setAcceptedFileTypes("image/jpeg", "image/png", "image/gif");
upload.addSucceededListener(event -> {
// uploaded file is now in outputStream
byte[] newImageBytes = outputStream.toByteArray();
Notification.show("We have now got the uploaded images bytearray!");
});
upload.setMaxFiles(10);
actualUploadButton = new Button(getTranslation("upload-image"), VaadinIcon.UPLOAD.create());
actualUploadButton.setWidth("100%");
upload.setUploadButton(actualUploadButton);
add(upload);
}
#Override
public OutputStream receiveUpload(String s, String s1) {
return outputStream = new FastByteArrayOutputStream();
}
}

How to restore file from GDrive?

I am making an app which stores its SQLite Database backup on GDrive. I succeeded in signing in and uploading the file in the drive but failed to restore it. Following is the code.
I use SQLiteDatabase to store the fileID so that when it is required while updating and restoring, it can be used. I am looking for a method which will make use of FileID to restore.
Error occurs at file.getDownloadUrl() and file.getContent().
class DriveClassHelper
{
private final Executor mExecutor = Executors.newSingleThreadExecutor();
private static Drive mDriveService;
private String FileID = null;
private static String filePath = "/data/data/com.example.gdrivebackup/databases/Data.db";
DriveClassHelper(Drive mDriveService)
{
DriveClassHelper.mDriveService = mDriveService;
}
// ---------------------------------- TO BackUp on Drive -------------------------------------------
public Task<String> createFile()
{
return Tasks.call(mExecutor, () ->
{
File fileMetaData = new File();
fileMetaData.setName("Backup");
java.io.File file = new java.io.File(filePath);
String mimeType = MimeTypeMap.getSingleton().getExtensionFromMimeType("application/x-sqlite-3");
FileContent mediaContent = new FileContent(mimeType, file);
File myFile = null;
FileID = getFileIDFromDatabase();
try {
if (FileID != null) {
Log.i("CALLED : ", FileID);
//mDriveService.files().delete().execute();
myFile = mDriveService.files().update(FileID, fileMetaData, mediaContent).execute();
} else {
myFile = mDriveService.files().create(fileMetaData, mediaContent).execute();
MainActivity.demoSQLite.insertData(myFile.getId());
}
} catch (Exception e) {
e.printStackTrace();
}
if (myFile == null) {
throw new IOException("Null Result when requesting file creation");
}
Log.i("ID:", myFile.getId());
return myFile.getId();
}
);
}
// -------------------------------------------------------------------------------------------------
// ---------------------------------- TO get File ID -------------------------------------------
private static String getFileIDFromDatabase()
{
String FileIDFromMethod = null;
Cursor result = MainActivity.demoSQLite.getData();
if (result.getCount() == 0) {
Log.i("CURSOR :", "NO ENTRY");
return null;
} else {
while (result.moveToNext()) {
FileIDFromMethod = result.getString(0);
}
return FileIDFromMethod;
}
}
// -------------------------------------------------------------------------------------------------
// ---------------------------------- TO Restore -------------------------------------------
public static class Restore extends AsyncTask<Void, Void, String>
{
#Override
protected String doInBackground(Void... params) {
String fileId = null;
try
{
fileId = getFileIDFromDatabase();
if (fileId != null)
{
File file = mDriveService.files().get(fileId).execute();
downloadFile(file);
}
else
{
return null;
}
}
catch (Exception e)
{
e.printStackTrace();
}
return fileId;
}
private void downloadFile(File file)
{
InputStream mInput = null;
FileOutputStream mOutput = null;
if (file.getDownloadUrl() != null && file.getDownloadUrl().length() > 0) //Error occurs at file.getDownloadUrl()
{
try
{
HttpResponse resp = mDriveService.getRequestFactory().buildGetRequest(new GenericUrl(file.getDownloadUrl())).execute();
mInput = resp.getContent();
String outFileName = "file://" + Environment.getDataDirectory().getPath() + filePath;
// Log.e("com.example.myapp", "getDatabasePath="+ getDatabasePath(""));
//Log.e("com.example.myapp", "outFileName="+outFileName);
// String outFileName = "../databases/" + "Quickpay.db";
mOutput = new FileOutputStream(outFileName);
byte[] mBuffer = new byte[1024];
int mLength;
while ((mLength = mInput.read(mBuffer)) > 0)
{
mOutput.write(mBuffer, 0, mLength);
}
mOutput.flush();
}
catch (IOException e)
{
// An error occurred.
e.printStackTrace();
// return null;
}
finally
{
try
{
//Close the streams
if (mOutput != null)
{
mOutput.close();
}
if (mInput != null)
{
mInput.close();
}
}
catch (IOException e)
{
Log.e("com.example.myapp", "failed to close databases");
}
}
}
else
{
// The file doesn't have any content stored on Drive.
// return null;
Log.e("com.example.myapp", "No content on Drive");
}
}
}
}
The Gradle file is like
implementation 'com.google.android.gms:play-services-auth:16.0.1'
implementation('com.google.apis:google-api-services-drive:v3-rev136-1.25.0')
{
exclude group: 'org.apache.httpcomponents'
}
implementation('com.google.api-client:google-api-client-android:1.26.0')
{
exclude group: 'org.apache.httpcomponents'
}
implementation 'com.google.http-client:google-http-client-gson:1.26.0'
As far as i know Download URL is only avalibale in Google drive api v2 and not in V3.
Short lived download URL for the file. This field is only populated for files with content stored in Google Drive; it is not populated for Google Docs or shortcut files.
It was not very stable in my opinion as not all file types would return a download url.
Using Google Drive v3 you should download the file using a stream.
String fileId = "0BwwA4oUTeiV1UVNwOHItT0xfa2M";
OutputStream outputStream = new ByteArrayOutputStream();
driveService.files().get(fileId)
.executeMediaAndDownloadTo(outputStream);
This should work with the restore. Let me know if it doesnt and i will have a look its been a while since i have tried restore.

Java - HttpServer not serving images

I have a simple setup including a HttpServer with some context to load image and CSS files:
public class Server {
private HttpServer httpServer;
public Server(int port, String path, HttpHandler handler) {
try {
httpServer = HttpServer.create(new InetSocketAddress(port), 0);
httpServer.createContext(path, handler);
// load css files
List<String> cssFiles = new ArrayList<String>();
Files.walk(Paths.get("../css")).forEach(filePath -> {
if (Files.isRegularFile(filePath)) {
cssFiles.add(filePath.getFileName().toString());
}
});
for (String cssFile : cssFiles) {
httpServer.createContext("/css/" + cssFile, new CssHandler(cssFile));
}
// load image files
List<String> imgFiles = new ArrayList<String>();
Files.walk(Paths.get("../images")).forEach(filePath -> {
if (Files.isRegularFile(filePath)) {
imgFiles.add(filePath.getFileName().toString());
}
});
for (String imgFile : imgFiles) {
httpServer.createContext("/images/" + imgFile, new ImageHandler(imgFile));
}
httpServer.setExecutor(null);
} catch (IOException e) {
e.printStackTrace();
}
}
public void start() {
this.httpServer.start();
}
}
In addition to that there is a css handler which works perfectly fine and and an image handler class, which serves images defined in html tags, BUT images which are included via css tag "background-image" cannot be loaded.. why?
ImageHandler:
class ImageHandler implements HttpHandler {
private String img;
public ImageHandler(String img) {
this.img = img;
}
#Override
public void handle(HttpExchange http) throws IOException {
if (http.getRequestMethod().equals("GET")) {
System.out.println("img transfered..." + img);
try {
StringBuilder contentBuilder = new StringBuilder();
try {
BufferedReader in = new BufferedReader(new FileReader("../images/" + img));
String str;
while ((str = in.readLine()) != null) {
contentBuilder.append(str);
}
in.close();
} catch (IOException e) {
}
String response = contentBuilder.toString();
http.sendResponseHeaders(200, response.length());
OutputStream os = http.getResponseBody();
os.write(response.getBytes());
os.flush();
os.close();
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
}
Sooo,
this works:
<img src="images/example.png"/>
But this one doesn't work:
background-image: url("images/example.png");
Could anybody explain why and suggest how to solve this problem?
Since partial URLs are relative to the source of the style sheet, and your paths structure looks like this:
/images/example.png
/css/example.css
So image URLs in example.css should either be absolute (/images/example.png), or have the correct relative path (../images/example.png).

Creating a file using ByteArrayOutputStream.writeTo(FileOutputStream) fails when using Vaadin StreamVariable in DropHandler?

I am learning Vaadin from the Vaadin 7 CookBook, on chapter 3 the authors show an example of a drag and drop uploader using StreamVariable and Html5File, here is the code:
public class DragAndDropUploader extends VerticalLayout {
/**
*
*/
private static final long serialVersionUID = 1L;
private static final String REPOSITORY = VaadinService.getCurrent().getBaseDirectory().getAbsolutePath()+"/WEB-INF/vaadin-repo/";
public DragAndDropUploader() {
final Table table = new Table();
table.setSizeFull();
table.addContainerProperty("File name", String.class, null);
table.addContainerProperty("Size", String.class, null);
table.addContainerProperty("Progress", ProgressBar.class, null);
DragAndDropWrapper dropTable = new DragAndDropWrapper(table);
dropTable.setDropHandler(new DropHandler() {
/**
*
*/
private static final long serialVersionUID = 1L;
#Override
public void drop(DragAndDropEvent event) {
WrapperTransferable transferred = (WrapperTransferable) event.getTransferable();
Html5File[] files = transferred.getFiles();
if (files != null) {
for (Html5File file : files) {
ProgressBar progressBar = new ProgressBar();
progressBar.setSizeFull();
UI.getCurrent().setPollInterval(100);
table.addItem(new Object[] { file.getFileName(),
getSizeAsString(file.getFileSize()),
progressBar
}, null);
StreamVariable streamVariable = createStreamVariable(file, progressBar);
file.setStreamVariable(streamVariable);
}
}
else {
Notification.show("Usupported file type", Type.ERROR_MESSAGE);
}
}
#Override
public AcceptCriterion getAcceptCriterion() {
return AcceptAll.get();
}
});
addComponent(dropTable);
setSizeUndefined();
}
private StreamVariable createStreamVariable(final Html5File file, final ProgressBar progressBar) {
final ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
return new StreamVariable() {
/**
*
*/
private static final long serialVersionUID = 1L;
#Override
public void streamingStarted(StreamingStartEvent event) {
}
#Override
public void streamingFinished(StreamingEndEvent event) {
try {
FileOutputStream fos = new FileOutputStream(REPOSITORY+file.getFileName());
System.out.println(outputStream.size()+ ": "+outputStream.toString() + " - " +fos.toString());
outputStream.writeTo(fos);
} catch (IOException e) {
Notification.show("Streaming finished failing, please, retry", Type.ERROR_MESSAGE);
}
progressBar.setValue(new Float(1.0));
}
#Override
public void streamingFailed(StreamingErrorEvent event) {
Notification.show("Streaming failed, please, try again", Type.ERROR_MESSAGE);
}
#Override
public void onProgress(StreamingProgressEvent event) {
progressBar.setValue((float) event.getBytesReceived() / file.getFileSize());
}
#Override
public boolean listenProgress() {
return true;
}
#Override
public boolean isInterrupted() {
return false;
}
#Override
public OutputStream getOutputStream() {
return outputStream;
}
};
}
private String getSizeAsString(long size) {
String unit = "B";
int kB, MB;
if (size >= (kB = (2 << 10))) {
size = size / kB;
unit = "kB";
}
else if (size >= (MB = 2 << 20)) {
size = size / MB;
}
return size + " " + unit;
}
}
REPOSITORY is a path to a vaadin-repo folder inside WebContent/WEB-INF.
My problem is that outputStream.writeTo(fos); where actually the file should be written to the server:
#Override
public void streamingFinished(StreamingEndEvent event) {
try {
FileOutputStream fos = new FileOutputStream(REPOSITORY+file.getFileName());
System.out.println(outputStream.size()+ ": "+outputStream.toString() + " - " +fos.toString());
outputStream.writeTo(fos);
} catch (IOException e) {
Notification.show("Streaming finished failing, please, retry", Type.ERROR_MESSAGE);
}
progressBar.setValue(new Float(1.0));
}
But it's not. When I make an upload and then check that vaadin-repo folder, it remains empty...
I do not get any exceptions (no FileNotFoundException, no IOException), so the problem is not that. REPOSITORY path have some spaces (but I think that this is not the problem (as I said I don't get any FileNotFoundException), and I have implemented Vaadin's uploaders previously (via the Upload.Receiver inner interface)).
Where is the problem?
Your code seems to be correct and a similar version is working for me. The only problem seems to be that you're not closing the streams. Make sure you close both outputstream and the FileOutputStream fos.

SkImageDecoder Factory returned null on Downloaded image

I am building an app with a self-made LruDiskCache to reduce loading times. The LruDiskCache downloads a file if it is not already present and returns it via a callback. I'm having a very weird issue where the first image isn't loaded properly. This only happends the first time a activity is started. If you open the same activity a secon time the image is loaded properly.
After debugging i found that i get the following debug message: --- SkImageDecoder Factory returned null. I've read multiple issues regarding this problem, but the all involve downloading an image through an inputstream, while i'm first downloading the image to persistent storage.
My cache-class:
public class LruDiskCache {
private static final String LOGTAG = "LruDiskCache";
//Cache size
private final long cacheMaxSize;
private volatile long currentCacheSize;
public static final int DEFAULT_CACHE_SIZE_KB = 1024;
public static final int MINIMUM_CACHE_SIZE_KB = 128;
//Preferences
private final String cachePreferencesName;
private final String chachePreferencesSize = "cacheSize";
private final SharedPreferences cachePreferences;
//Lock
private final Object mDiskCacheLock = new Object();
//Cache Path
private final File cachePath;
//Initialisation
private boolean openingCache;
//Static variables
private static final long BYTES_IN_KB = 1024;
public LruDiskCache (Context c, String cacheName, int cacheMaxSizeKB){
//Preferences
cachePreferencesName = cacheName + "CachePreferences";
cachePreferences = c.getSharedPreferences(cachePreferencesName, Context.MODE_PRIVATE);
//Paths
cachePath = new File(c.getFilesDir().getPath()+"/"+cacheName);
//Cache size
if(cacheMaxSizeKB < MINIMUM_CACHE_SIZE_KB){
throw new IllegalArgumentException
("Invalid cache size, size must be bigger than "
+ MINIMUM_CACHE_SIZE_KB + "kb.");
}
cacheMaxSize = cacheMaxSizeKB * BYTES_IN_KB;
//Initialize
openingCache = true;
new InitializeCache().execute();
}
//PUBLIC METHODS
public synchronized void getRemoteFile(URL remoteFile, Callback c){
if(openingCache){
try {
mDiskCacheLock.wait(1000);
} catch (InterruptedException e) {
c.onRequestedFileRetrieved(null, false);
}
}
String path = createFilePath(remoteFile);
File f = new File(path);
if(f.exists() && f.isFile()){
f.setLastModified(System.currentTimeMillis());
c.onRequestedFileRetrieved(f, true);
} else {
new RemoteFileDownloadTask(remoteFile, f, c).execute();
}
}
//PRIVATE METHODS
private String createFilePath(URL key){
return cachePath + "/" + key.getHost() + key.getPath();
}
private synchronized void cleanUpCache(){
long cacheSize = getDirSize(cachePath);
int failedToDeleteCounter = 0;
while(cacheSize > cacheMaxSize){
File toDelete = getFirstRequestedFile(cachePath);
Log.w("LruDiskCache", "File to be deleted: " + toDelete.getName() + ".");
long toDeleteSize = toDelete.length();
if(!toDelete.delete()){
failedToDeleteCounter++;
} else {
Log.w("LruDiskCache", "Deleted file to clean cache.");
cacheSize -= toDeleteSize;
}
if(failedToDeleteCounter > 100){
Log.w("LruDiskCache", "Failed to clean cache, could not delete files.");
break;
}
}
}
private File getFirstRequestedFile(File directory){
File first = null;
for(File f : directory.listFiles()){
if(f.isFile()){
if(first == null){
first = f;
} else {
if(f.lastModified() < first.lastModified()){
first = f;
}
}
} else if (f.isDirectory()) {
File firstReqInDir = getFirstRequestedFile(f);
if(first == null){
first = firstReqInDir;
} else if (firstReqInDir.lastModified() < first.lastModified()){
first = firstReqInDir;
}
}
}
return first;
}
private long getDirSize(File dir) {
long bytes = 0;
for (File f : dir.listFiles()) {
if (f.isDirectory()) {
bytes += getDirSize(f);
} else {
bytes += f.length();
}
}
return bytes;
}
//ASYNC TASKS
private class InitializeCache implements Runnable{
public void execute(){
new Thread(this).start();
}
#Override
public void run() {
synchronized (mDiskCacheLock) {
Log.d("Initialize cache", "Starting init...");
if (!cachePath.exists()) {
if (!cachePath.mkdirs()) {
mDiskCacheLock.notifyAll();
openingCache = false;
}
}
currentCacheSize = getDirSize(cachePath);
Log.d("LruDiskCache", "Cache size: " + currentCacheSize / BYTES_IN_KB + "kb.");
if(currentCacheSize > cacheMaxSize){
cleanUpCache();
}
Log.d("Initialize cache", "Did init...");
mDiskCacheLock.notifyAll();
openingCache = false;
}
}
}
private class RemoteFileDownloadTask extends AsyncTask<Void, Void, File> {
private final Callback c;
private URL remoteFile;
private File localFile;
protected RemoteFileDownloadTask(URL remoteFile, File localFile, Callback c){
this.c = c;
this.remoteFile = remoteFile;
this.localFile = localFile;
}
#Override
protected File doInBackground(Void... params) {
if(retrieveRemoteFile(remoteFile, localFile)){
return localFile;
} else {
return null;
}
}
#Override
protected void onPostExecute(File file) {
super.onPostExecute(file);
currentCacheSize += file.length();
if(currentCacheSize > cacheMaxSize){
cleanUpCache();
}
c.onRequestedFileRetrieved(file, true);
}
public boolean retrieveRemoteFile(URL remote, File filePath) {
try {
if (filePath.exists() && filePath.isFile()) {
return true;
} else {
if (!filePath.getParentFile().exists()) {
if (!filePath.getParentFile().mkdirs()) {
return false;
}
}
}
} catch (Exception e){
return false;
}
Log.w("LruDiskCache", "Created empty file: " + filePath.getPath());
Log.w("LruDiskCache", "Downloading source from: " + remote.toString());
try {
FileOutputStream fos;
InputStream is;
BufferedInputStream bis;
fos = new FileOutputStream(filePath);
HttpURLConnection con = (HttpURLConnection) remote.openConnection();
if(con.getResponseCode() != HttpURLConnection.HTTP_OK){
Log.e("Receiver", "HTTP Response code is not OK");
return false;
}
is = con.getInputStream();
bis = new BufferedInputStream(is);
while(bis.available() > 0){
fos.write(bis.read());
}
bis.close();
is.close();
fos.close();
} catch (Exception e) {
return false;
}
Log.d("LruDiskCacheReceiver", filePath.getPath() + " received, " + filePath.length() + " bytes.");
return true;
}
}
//CALLBACK INTERFACE
public interface Callback{
public abstract void onRequestedFileRetrieved(File f, boolean success);
}
}
And the implementation is shown here:
URL emblemURL = new URL(data[position].getEmblemUrl());
cache.getRemoteFile(emblemURL, new LruDiskCache.Callback() {
#Override
public void onRequestedFileRetrieved(File f, boolean success) {
if (success && f.exists()) {
Drawable bitmap = Drawable.createFromPath(f.getAbsolutePath());
emblem.setImageDrawable(bitmap);
}
}
});
Any help is appreciated.

Categories

Resources