Android: How to manage images (existing vs non existing) in the project - android

The title seems ambigious but my goal is simple to understand.
I have a certain number of images in my project under res/drawable-hdpi.
In my app, I am geting a list of names from the server and I am saving them in the local databse.
Each name corresponds to the name of the image with ".png" at the end.
I am using the Galery and the user could choose one of the names from the database by clicking on his corresponding image.
Everything is fine until here.
But now, lets say that a new name will be added on the server side, so the image will not be in the application. In this case I have to update my app and put the right image inside.
In order to avoid that the user sees a "black -image" in the galery (because the image does not exists), I would like to create this image with android.
I am actually able to catch if the image does not exists in the project and to create a new one (white background with the name in the middle).
Now, the problem is how and where to store this new image. Apparently, it is not possible to store this in res/drawable folder. So, where and how to store?
Here is the part of the code where I am creating the new image:
if (imageId == 0)
{
Bitmap journal_template = BitmapFactory.decodeResource(context.getResources(), R.drawable.journals_template).copy(Bitmap.Config.ARGB_8888, true);
Canvas myCanvas = new Canvas(journal_template);
Paint myPaint = new Paint();
myPaint.setColor(Color.BLACK);
myPaint.setTextSize(25);
String journal_name = publicJournalsNameSystem.get(i).toLowerCase();
Paint textPaint = new Paint();
textPaint.setARGB(200, 254, 0, 0);
textPaint.setTextAlign(Align.CENTER);
int xPos = (myCanvas.getWidth() / 2);
int yPos = (int) ((myCanvas.getHeight() / 2) - ((textPaint.descent() + textPaint.ascent()) / 2)) ;
myCanvas.drawText("Your text", xPos, yPos, myPaint);
try {
FileOutputStream out = new FileOutputStream("/journals_"+journal_name+".png");
journal_template.compress(Bitmap.CompressFormat.PNG, 90, out);
} catch (Exception e) {
e.printStackTrace();
}
}

In order to use images from a web server, you need to first download the image on to the device then store it.
Here is a good example of how to download the image.
The developer website has methods on how to store data to the device. You will either want to store it internally or (more preferably) externally to the SD card. It would be recommended that you first check if the SD card is mounted on the device and usable. Store the image internally if the SD isn't available.
You must then keep track of the image's URI after downloading. Temporary images can simply be tracked with the app then deleted upon onDestroy(). Permanent image URIs should be stored via either SharedPreferences, SQLite database, or ContentProvider. A ContentProvider is preferred as it adds a layer of abstraction for how you want to store the image. It's usually backed by an SQLite database anyway, but other apps don't need to know that. It also allows other applications to easily access the image if you want (say, the Gallery for example). You can prevent access if you choose. SharedPreferences is easier to implement if you only have a few images. It's least recommended though.

Related

Android PdfDocument file size

I want to generate a PDF File from a View using the PdfDocument android class introduced in KitKat. I managed to do it, and the file is so far generated ok, ending up having a correct PDF. The only problem is the file is huge, 12Mb for just one page. Is there a way to reduce the File size?
The code I am using to generate the PDF is:
public static File generateDocument(Activity activity, String fileName, ViewGroup container) throws IOException{
File f = new File(activity.getExternalFilesDir(null), fileName);
PdfDocument document = new PdfDocument();
try{
for(int i=0;i<container.getChildCount();i++){
View v = container.getChildAt(i);
PdfDocument.PageInfo.Builder pageBuilder = new PdfDocument.PageInfo.Builder(v.getWidth(), v.getHeight(), i);
Page page = document.startPage(pageBuilder.create());
v.draw(page.getCanvas());
document.finishPage(page);
}
document.writeTo(new FileOutputStream(f));
} finally{
if(document!=null){
document.close();
}
}
return f;
}
In case anyone is still looking for a solution... I was working on a project to generate PDF from images and not satisfied with the file size generated by both Android's PdfDocument and 3rd party AndroidPdfWriter APW.
After some trials I ended up using Apache's PdfBox, which gave me a PDF file (A4 size with a single 1960x1080 image) for around 80K, while it's usually 2~3M with PdfDocument or AndroidPdfWriter.
PDDocument document = new PDDocument();
PDPage page = new PDPage(PDRectangle.A4);
document.addPage(page);
// Define a content stream for adding to the PDF
contentStream = new PDPageContentStream(document, page);
Bitmap bimap = _get_your_bitmap_();
// Here you have great control of the compression rate and DPI on your image.
// Update 2017/11/22: The DPI param actually is useless as of current version v1.8.9.1 if you take a look into the source code. Compression rate is enough to achieve a much smaller file size.
PDImageXObject ximage = JPEGFactory.createFromImage(document, bitmap, 0.75, 72);
// You may want to call PDPage.getCropBox() in order to place your image
// somewhere inside this page rect with (x, y) and (width, height).
contentStream.drawImage(ximage, 0, 0);
// Make sure that the content stream is closed:
contentStream.close();
document.save(_your_file_path_);
document.close();
=====
btw. I guess the reason why they generate a huge file size is because they don't compress the image data while writing to PDF file. If you take a look into AndroidPdfWriter's XObjectImage.deflateImageData() method you will see it's using java.util.zip.Deflater.NO_COMPRESSION option to write the image data which is kind of horrible if you've got a picture with size 1960x1080. If you change the options to e.g. Deflater.BEST_COMPRESSION you get much smaller file size however it takes up to 3-4 seconds for me to handle one single page which is not acceptable.
There are a few main things that increases the size of a PDF file:
hi-resolution pictures (where lo-res would suffice)
embedded fonts (where content would still be readable "good enough" without them)
PDF content not required any more for the current version/view (older version of certain objects)
embedded ICC profiles
embedded third-party files (using the PDF as a container)
embedded job tickets (for printing)
embedded Javascript
and a few more
Try using iText. Following links give a basice idea for iText in android.
http://technotransit.wordpress.com/2011/06/17/using-itext-in-android/
http://www.mysamplecode.com/2013/05/android-itext-pdf-bluetooth-printer.html
https://stackoverflow.com/a/21025162/3110609
Using PDFDocument, be sure to downscale your images prior to drawing them in the canvas.
When drawing to the screen, this is enough to scale the bitmap :
canvas.drawBitmap(bmp, src, dst, paint);
However, when using the canvas from PdfDocument.Page.getCanvas, this canvas will not downscale the bitmap, it will just squeeze it into a smaller zone. Instead you should do something like this:
// Scale bitmap : filter = false since we are always downSampling
Bitmap scaledBitmap = Bitmap.createScaledBitmap(bmp, dstWidth, dstHeight,
false); // filter=false if downscaling, true if upscaling
canvas.drawBitmap(scaledBitmap, null, dst, paint);
scaledBitmap.recycle();
This is embedded in Android so it is much easier than using a third-party library. (The above was tested on a Marshmallow platform)
This seems to just be a bug in PdfDocument. The PDF file I created with PdfDocument was 5.6 megabytes. The same document generated through the iOS equivalent was 500K. If I take the Android PDF and run it through Adobe Acrobat's pdf optimization, without compressing any images, the 5.6MB file becomes 350K. They look identical, and I applied no compression in Adobe Acrobat.
In the actual PDF code, the Android image object dictionary is this
<</Type /XObject
/Subtype /Image
/Width 1224
/Height 1584
/ColorSpace /DeviceRGB
/BitsPerComponent 8
/Length 5816448
>>
The PDF from iOS has this dict
<< /Length 8 0 R
/Type /XObject
/Subtype /Image
/Width 1224
/Height 1584
/ColorSpace /DeviceRGB
/SMask 9 0 R
/BitsPerComponent 8
/Filter /FlateDecode >>
I think the problem is the lack of the FlateDecode filter in the Android version. When I run it through the Adobe Acrobat PDF optimizer, it gets the FlateDecode filter.

A way to customize app resources and colors on the fly

I have an app that should deploy for few different costumers.
For each costumer I want to allow different coloring and resources.
Are there any ways to enable the app to load resources and configurations from the internet on startup, and then using them on the app.
I know I can use Google Tag Manager for loading configuration values from the internet. Is there some platform I can use for doing something similar for Drawable resources?
You'll need to download the remote resources to the SD card. Then you can create drawables on the fly with:
Drawable d = Drawable.createFromPath(new File(Environment.getExternalStorageDirectory(), "yourDownloadedBackground.png").getAbsolutePath());
and then set the layout background with setBackGroundDrawable() or setBackGround(), the latter only if you're targetting API 16 or more.
The other way, is to put webviews instead of the images in your layout. This will allow you to load remote images, local files and HTML snippets. Put a webview in your layout and try this:
android.webkit.WebView v = (android.webkit.WebView) findViewById(R.id.webView1);
v.loadUrl("http://developer.android.com/assets/images/dac_logo.png");
You can use color filters, see this
Modifying the color of an android drawable
http://blog.syedgakbar.com/2012/07/changing-color-of-the-drawable-or-imageview-at-runtime-in-android/
int color=Color.rgb(colorR, colorG, colorB);
public static BitmapDrawable changeColor(Bitmap bitmap, Context context, int color){
Bitmap bitmapCopy = Bitmap.createBitmap(bitmap.getWidth(), bitmap.getHeight(), Config.ARGB_8888);
Canvas c = new Canvas(bitmapCopy);
Paint p = new Paint();
p.setColorFilter(color, PorterDuff.Mode.MULTIPLY);
c.drawBitmap(bitmap, 0, 0, p);
return new BitmapDrawable(context.getResources(),bitmapCopy);
}
Just be sure thats the filter you want
Consider the implications of this: if you start this application on a non-internet connected device, it will still need a local cache of drawables. If your customers want a tight user experience, do you really want to be dependent upon a remote source to load the UI of your application?
Perhaps your best (though less extensible) solution would be to create multiple APKs, one for each customer. For "a few different customers", this is probably simpler than hosting UI components. For large numbers of customers, then you might start thinking about more creative solutions.
EDIT: please take a look at this answer for some productive ideas on how to efficiently manage multiple packages.

High resolution screen shot in Android

Is there any way to get a high resolution screen shot of a certain view in an activity.
I want to convert html content of my webview to PDF. For that I tried to take screen shot of the webview content and then converted it to PDF using itext. The resulted PDF is not in much more clarity.
My code:
protected void takeimg() {
Picture picture = mWebView.capturePicture();
Bitmap b = Bitmap.createBitmap(picture.getWidth(), picture.getHeight(),
Bitmap.Config.ARGB_8888);
Canvas c = new Canvas(b);
picture.draw(c);
// byte[] bt = b.getNinePatchChunk();
// Bitmap b;
// View v1 = mWebView.getRootView();
// v1.setDrawingCacheEnabled(true);
// b = Bitmap.createBitmap(v1.getDrawingCache());
// v1.setDrawingCacheEnabled(false);
FileOutputStream fos = null;
try {
File root = new File(Environment.getExternalStorageDirectory(),
"Sample");
if (!root.exists()) {
root.mkdir();
}
String sdcardhtmlpath = root.getPath().toString() + "/"
+ "temp_1.png";
fos = new FileOutputStream(sdcardhtmlpath);
// fos = openFileOutput("samsp_1.jpg", MODE_WORLD_WRITEABLE);
if (fos != null) {
b.compress(Bitmap.CompressFormat.PNG, 100, fos);
// fos.write(bt);
fos.close();
}
} catch (Exception e) {
Log.e("takeimg", e.toString());
e.printStackTrace();
}
}
protected void pdfimg() {
Document mydoc = new Document(PageSize.A3);
try {
File root = new File(Environment.getExternalStorageDirectory(),
"Sample");
if (!root.exists()) {
root.mkdir();
}
String sdcardhtmlpath = root.getPath().toString() + "/";
mydoc.setMargins(0, 0, 0, 0);
PdfWriter.getInstance(mydoc, new FileOutputStream(sdcardhtmlpath
+ PDFfilename));
mydoc.open();
Image image1 = Image.getInstance(sdcardhtmlpath + "temp_1.jpg");
image1.scalePercent(95f);
mydoc.add(image1);
// mydoc.newPage();
mydoc.close();
} catch (Exception e) {
Log.e("pdi name", e.toString());
}
}
Update: See Edit 3 for an answer to op's original question
There are two options:
Use a library to convert the HTML to PDF. This is by far the best option, since it will (probably) preserve text as vectors.
Get a high resolution render of the HTML and save it as a PNG (not PDF surely!).
For HTML to PDF, wkhtmltopdf looks like a good option, but it relies on Qt which you can't really use on Android. There are some other libraries but I doubt they do the PDF rendering very well.
For getting a high-res webview, you could try creating your own WebView and calling onMeasure(...) and onLayout(...) and pass appropriate parameters so the view is really big. Then call onDraw(myOwnCanvas) and the webview will draw itself to your canvas, which can be backed by a Bitmap using Canvas.setBitmap().
You can probably copy the state into the new WebView using something like
screenshotterWebview.onRestoreInstanceState(mWebView.onSaveInstanceState());
Orrr it may even be possible to use the same WebView, just temporarily resize it to be large, onDraw() it to your canvas, and resize it back again. That's getting very hacky though!
You might run into memory issues if you make it too big.
Edit 1
I thought of a third, exactly-what-you-want option, but it's kind of hardcore. You can create a custom Canvas, that writes to a PDF. In fact, it is almost easy, because underlying Canvas is Skia, which actually includes a PDF backend. Unfortunately you don't get access to it on Android, so you'll basically have to build your own copy of it on Android (there are instructions), and duplicate/override all the Canvas methods to point to your Skia instead of Androids. Note that there is a tempting Picture.writeToStream() method which serializes the Skia data, but unfortunately this format is not forwards or backwards compatible so if you use it your code will probably only work on a few versions of Android.
I'll update if/when I have fully working code.
Edit 2
Actually it is impossible to make your own "intercepting" Canvas. I started doing it and went through the tedious process of serializing all function calls. A few you can't do because they are hidden, but those didn't look important. But right at the end I came to serializing Path only to discover that it is write-only. That seems like a killer to me, so the only option is to interpret the result of Picture.writeToStream(). Fortunately there are only two versions of that format in use, and they are nearly identical.
Edit 3 - Really simple way to get a high resolution Bitmap of a view
Ok, it turns out just getting a high res bitmap of a view (which can be the entire app) is trivial. Here is how to get double resolution. Obviously all the bitmaps look a bit crap, but the text is rendered at full resolution:
View window = activity.getWindow().getDecorView()
Canvas bitmapCanvas = new Canvas();
Bitmap bitmap = Bitmap.createBitmap(window.getWidth()*2, window.getHeight()*2, Bitmap.Config.ARGB_8888);
bitmapCanvas.setBitmap(bitmap);
bitmapCanvas.scale(2.0f, 2.0f);
window.draw(bitmapCanvas);
bitmap.compress(Bitmap.CompressFormat.PNG, 0, myOutputStream);
Works like a charm. I've now given up on getting a PDF screenshot with vector text. It's certainly possible, but very difficult. Instead I am working on getting a high-res PSD where each draw operation is a separate layer, which should be much easier.
Edit 4
Woa this is getting a bit long, but success! I've generated an .xcf (GIMP) and PDF where each layer is a different canvas drawing operation. It's not quite as fine-grained as I was expecting, but still, pretty useful!
Actually my code just outputs full-size PNGs and I used "Open as layers..." and "Autocrop layer" in GIMP to make these files, but of course you can do that in code if you like. I think I will turn this into a blog post.
Download the GIMP or Photoshop demo file (rendered at 3x resolution).
When you capture the view, just screen bound will capture ( due to control weight and android render pipeline ).
Capturing screenshot for converting to PDF is tricky way. I think two way is more reasonable solutions.
Solution #1
Write a parser ( it's simple ) to convert webview content ( that is HTML ) to iText format.
You can refer to this article for more information.
http://www.vogella.com/articles/JavaPDF/article.html
Also to write a parser you can use REGEX and provide your own methods like parseTable, parseImage, ...
Solution #2 Internet Required
Provide a URL ( or webservice ) to convert HTML to PDF using PHP or C# that has a lot of nice libraries. Next you can send download link to the Client ( Android Device ).
So you can also dynamically add some Tags, Banners, ... to the PDF from server side.
Screen Shot is nothing but picture of your device display which usually depend upon your phone absolute pixels, if your phone is 480x800 screen shot will be same and generally applicable for all scenarios.
Sure, Use this:
Bitmap bitmap;
View v1 = MyView.getRootView();
v1.setDrawingCacheEnabled(true);
bitmap = Bitmap.createBitmap(v1.getDrawingCache());
v1.setDrawingCacheEnabled(false);
Here MyView is the View you need a screenshot of.

not accessing images from cache properly in listview

I am downloading images from server into the ListView and storing it into SD Card.
And when next time listview appears i am accessing it from SD card only using Async method, i
use this approch so that every thing user does not need to access server.
But when all the images are being loaded into listview from SD Card and if i scroll it
pretty fast then every time it tries to access it from the SD Card only rather then from Caches i guess.
I was facing the same problem when images are being downloaded from server also , and thats why i thought to store it into SD Card. but i am facing the same issue.
here is my code ListImageDownloader . In that there is a function called downloadBitmap(String) and i have created another function named downloadSDBitmap(String) whose code is as follows
Bitmap downloadSDBitmap(String urlId) {
Bitmap bitmap = null;
File file = new File(
fileLoc +"/"+ urlId+".png");
if(file.exists()){
Log.d("PATH" , file.getAbsolutePath());
bitmap = BitmapFactory.decodeFile(file.getAbsolutePath());
}
return bitmap;
}
apart from this the whole caching code and all are same. so can anyone help me how can i improve it as in a Gtalk android application when i scroll fast it loads the images only once after that if i scroll fast the images remains as it is and doesnt fetch from network
Update
these are my parameters
final static int MAX_ENTRIES = 150;
private static final int HARD_CACHE_CAPACITY =50;
private static final int DELAY_BEFORE_PURGE = 10 * 1000; // in milliseconds
Caching fundamentally relies on available memory. If there is memory left for your application you will need to implement a good solution that caches your bitmaps.
In the past was SoftReference/WeakReference a popular method to cache bitmaps (I did try it a year ago, you can read about my question about this here). But in later APIs of Android the garbage collector has become more aggressive collecting those and therefore they are not no longer recommended.
Now it is recommended to use an LRU cache. There is an example available on the Android developers website.

Android resource loading (icons and strings) at runtime

I'm stuck with the following scenario and appreciate any help/advice..
Requirement
I have number of categories and subcatergories in my application. Say for example, I have a category "Food" and under which I have subcategories: Mexican, Chinese, Italian etc.. I have around 20 categories and each category has around 30 subcategories.
Each subcategory/category has an Icon associated with it
User would be able to select one or more of these sub-categories, so the UI would be a seectable list view.
Questions:
What's the best way to store and retrieve this data (Strings and Icons), serverside or client side ?
Is there a way to load icons dynamically at runtime, when I show the subcategories? (using http?)
Thanks in advance !
Why would you fetch icon-data from a server? 20 * 30 = 600 icons.
You probably will save hard drive space with respect to the installation. But personally I wouldn't go for that solution.
If you're not in need of a client/server - approach then don't use it. What if the server for example breaks down, or you don't have an internet connection?. The application will then be useless :)
You can load images dynamically as:
BitmapFactory.Options options = new BitmapFactory.Options();
options.outWidth= IMAGE_WIDTH;
options.outHeight= IMAGE_HEIGHT;
Bitmap bm = BitmapFactory.decodeFile("icon image file path", options);
imageView.setImageBitmap(bm);
In your case, you can download all the required icons on mobile's sdcard. So in future,if sub categories increases then you can download new icons for that and can dynamically render on UI.

Categories

Resources