I am currently building this Android application, where I will be taking a screenshot of a "TableLayout" and then emailing it as an attachment. Here is the part of the code which takes the screenshot.
However, when I try to attach the file, using the following code, it says that "File Size Too Large for Attachment". Can anyone suggest any other measures that I can take, apart from Bitmap.Compress, in order to make my file size even smaller? Thanks in advance!
private void getScreen()
{
View content = findViewById(R.id.TransactionLog);
content.setDrawingCacheEnabled(true);
content.buildDrawingCache(true);
Bitmap bitmap = Bitmap.createBitmap(content.getDrawingCache());
content.setDrawingCacheEnabled(false); // clear drawing cache
File file = new File(Environment.getExternalStorageDirectory() +
File.separator + "whatever2.png");
try
{
file.createNewFile();
FileOutputStream ostream = new FileOutputStream(file);
bitmap.compress(CompressFormat.PNG, 0, ostream);
ostream.flush();
ostream.close();
}
catch (Exception e)
{
e.printStackTrace();
}
}
Try using Bitmap.createScaledBitmap:
public static Bitmap createScaledBitmap (Bitmap src, int dstWidth, int
dstHeight, boolean filter)
Since: API Level 1
Creates a new bitmap, scaled from an existing bitmap.
Parameters
src - The source bitmap.
dstWidth - The new bitmap's desired width.
dstHeight - The new bitmap's desired height.
filter - true if the source should be filtered.
FYI, the quality parameter passed to compress does not affect the file size when you are using CompressFormat.PNG. Try using CompressFormat.JPEG instead, then try different quality values.
Alternatively, try this:
http://thinkandroid.wordpress.com/2009/12/25/resizing-a-bitmap/
Have you tried use a higher value for the quality parameter? (currently you're using 0 and it could go up to 100).
I suggest trying 80.
boolean success = bitmap.compress(CompressFormat.PNG, 80, ostream);
Don't forget to test if it was successful (log the return value of that method).
You can also try to use another format (jpeg?).
To make things simpler, I suggest you to try to save it to the sdcard first and check if the size is something you'd be expecting. You might have some problem on the code that sends the email or it might not let you send large attachments.
How big is your image? An image made from an app, saved as a png, assuming its not a screenshot of a 'picture', should be pretty small. FAR smaller than what email should be able to accept, unless there's an arbitrarily small attachment size.
If what's in your table is an image, or has quite a bit of variance, you might consider jpeg instead of png. Otherwise, my guess is something else is going on.
Related
how to make a screenshot (preview) without using an emulator (or device)?
I need any quick mechanism in order to take a bunch of screenshots (png) for different screen sizes/layouts
I'm not sure any of the layout stuff will work in just a JVM. So would need a headless emulator, emulator or Device to have a screen to screenshot. Robo Electric might allow to to run the code without emulator or Device.
Then you can get layout to draw in to a Bitmap backed Canvas and then save the bitmap to file, this does require you to run the code,
Note this will save the whole layout including any off screen elements.
e.g. A scollview will normal be made to fit the screen, so it will only show what is on screen at the time (same goes for things like recyclerview)
But for things like TableLayouts inside of a scrollview you will get the whole TableLayout including off screen elements
Code:
tableLayout = findViewById(R.id.table);
Canvas bitmapCanvas = new Canvas();
// Double the bitmap size and scale the Canvas to get a better resolution picture
// (Basically doubling the virtual screen resolution (Optional)
Bitmap bitmap = Bitmap.createBitmap(tableLayout.getWidth()*2, tableLayout.getHeight()*2, Bitmap.Config.ARGB_8888);
bitmapCanvas.setBitmap(bitmap);
bitmapCanvas.scale(2.0f, 2.0f);
tableLayout.draw(bitmapCanvas);
// uri is from SAF in this instance but any fileOutputStream can be used
try{
ParcelFileDescriptor pfd =
getApplicationContext().getContentResolver().
openFileDescriptor(uri, "w");
FileOutputStream fileOutputStream =
new FileOutputStream(
pfd.getFileDescriptor());
bitmap.compress(Bitmap.CompressFormat.PNG, 100, fileOutputStream);
// PNG is a lossless format, the compression factor (100) is ignored
fileOutputStream.close();
pfd.checkError();
pfd.close();
} catch (Exception e) {
Log.e("N", e.toString());
}
Update:
If they view has not been laid out on a screen, then you can do it manually instead of findViewById
e.g.
TableLayout tableLayout = new TableLayout();
tableLayout.setLayoutParams(new TableLayout.LayoutParams(TabLayout.LayoutParams.WRAP_CONTENT,
TabLayout.LayoutParams.WRAP_CONTENT));
tableLayout.measure(View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED),
View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED));
tableLayout.layout(0, 0, tableLayout.getMeasuredWidth(), tableLayout.getMeasuredHeight());
But I still think that without something like Robo Electric providing all the "Android" classes to a standard JVM even this won't work.
I use the following code to obtain a bitmap from an ImageView. This image is not saved anywhere else on my device. I want to upload this image into an online mysqli database. However, to do so I need to decrease the size of the file first. I found a lot of links about this, however they all require the file to be saved on the device and then use FileOutputStream. I am looking for a way to reduce the file size so that it can be comfortably transferred using the Volley API ( i am currently receiving either run out of memory exceptions or broken pipe errors). Hence I am looking for a way to modify this code to be able to significantly decrease the file size, whilst still maintaining a quality which can be comfortably shown on a mobile device. The original image is taken straight from the camera hence the size is quite large. Here is my code:
ImageView pic_holder = (ImageView) findViewById(R.id.picturedisplay);
Bitmap bitmap = ((BitmapDrawable)pic_holder.getDrawable()).getBitmap();
ByteArrayOutputStream stream=new ByteArrayOutputStream();
bitmap.compress(Bitmap.CompressFormat.PNG, 90, stream);
byte[] image=stream.toByteArray();
String img_str = Base64.encodeToString(image, 0);
I would like to decrease the size of the img_str which is passed to my Volley method.
I've wrote an app that encrypt/decrypt pictures. When the encrypted image is stored, in the image gallery we can see the "picture no available" icon for this encrypted image. I would like to change this to default icon, but I don't know where is this default stored...
thanks!
EDIT
What I am trying to do now, is with BitMapFactory convert the encripted file to a bitmap. The icon it's not possible to change and EXIF algorithm is not exactly what I want.
With this code I obtain a black bitmap, what I would like to do is to obtain a bitmap from the encripted file that shows colour pixels. Any idea?
Bittmap bitmap=BitmapFactory.decodeFile("/sdcard/abc.jpg");
Bitmap bm = bitmap.createBitmap(30, 30, Bitmap.Config.ARGB_8888);
ByteArrayOutputStream bos = new ByteArrayOutputStream();
bm.compress(CompressFormat.JPEG, 30, bos);
bos.toByteArray();
FileOutputStream ft = new FileOutputStream(new File("/sdcard/ab.jpg"));
bos.writeTo(ft);
bos.flush();
ft.flush();
bos.close();
ft.close();
Since your app encrypts images I guess that you want them all to be viewable only by your app. So why don't you just change the file extension to something other so that the Android Image gallery won't recognize the file as image and try to display it.
This is outside of your control. Default Gallery appliaction (and probably 1000 others on Google Play) display their own image when parsing fails the way they want.
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.
In Android, how do you display an image (of any size) from the SD card, without getting an out of memory error?
Is it necessary to put the image in the Media Store first?
A pseudo-code example would be greatly appreciated. Extra points if the displayed image is as big as the memory level of the device allows.
Edit: this question has actually been already answered at Strange out of memory issue while loading an image to a Bitmap object (the two highest voted answers). It does also use the inSampleSize option, but with a small method to automatically get the appropriate value.
My original answer:
The inSampleSize of the BitmapFactory.Options class can solve your issue (http://developer.android.com/reference/android/graphics/BitmapFactory.Options.html#inSampleSize). It works by making a bitmap with the resulting width and height 1/inSampleSize than the original, thus reducing memory consumption (by inSampleSize^2?). You should read the doc before using it.
Example:
BitmapFactory.Options options = new BitmapFactory.Options();
// will results in a much smaller image than the original
options.inSampleSize = 8;
// don't ever use a path to /sdcard like this, but I'm sure you have a sane way to do that
// in this case nebulae.jpg is a 19MB 8000x3874px image
final Bitmap b = BitmapFactory.decodeFile("/sdcard/nebulae.jpg", options);
final ImageView iv = (ImageView)findViewById(R.id.image_id);
iv.setImageBitmap(b);
Log.d("ExampleImage", "decoded bitmap dimensions:" + b.getWidth() + "x" + b.getHeight()); // 1000x485
However here it will only work for images up to, I guess, inSampleSize^2 times the size of the allowed memory and will reduce the quality of small images.
The trick would be to find the appropriate inSampleSize.
I'm displaying any sized images using code:
ImageView imageView=new ImageView(this);
imageView.setScaleType(ImageView.ScaleType.CENTER_INSIDE);
imageView.setAdjustViewBounds(true);
FileInputStream fis=new FileInputStream(file);
BitmapFactory.Options options=new BitmapFactory.Options();
options.inSampleSize=2; //try to decrease decoded image
options.inPurgeable=true; //if necessary purge pixels into disk
options.inScaled=true; //scale down image to actual device density
Bitmap bm=BitmapFactory.decodeStream(is, null, options);
imageView.setImageBitmap(bm);
fis.close();
For example:
yourImgView.setImageBitmap(BitmapFactory.decodeFile("/sdcard/1.jpg"));
http://www.developer.com/ws/other/article.php/3748281/Working-with-Images-in-Googles-Android.htm covers all you'll ever need to know on the subject of images, including getting them from an SD card. You'll notice the code to do so there, copied below:
try {
FileOutputStream fos = super.openFileOutput("output.jpg",
MODE_WORLD_READABLE);
mBitmap.compress(CompressFormat.JPEG, 75, fos);
fos.flush();
fos.close();
} catch (Exception e) {
Log.e("MyLog", e.toString());
}