How do can you generate a bitmap from HTML in Android?
Can the WebView be used for this or is there a better approach (like maybe using the WebView rendering engine directly)? How?
I would like to implement the following method...
public Bitmap toBitmap(Context context, String html, Rect rect);
...where html is the html to render and rect is the frame of the desired bitmap.
A synchronous method that generates a bitmap from an HTML string using a WebView, and can be used within an AsyncTask:
public Bitmap getBitmap(final WebView w, int containerWidth, int containerHeight, final String baseURL, final String content) {
final CountDownLatch signal = new CountDownLatch(1);
final Bitmap b = Bitmap.createBitmap(containerWidth, containerHeight, Bitmap.Config.ARGB_8888);
final AtomicBoolean ready = new AtomicBoolean(false);
w.post(new Runnable() {
#Override
public void run() {
w.setWebViewClient(new WebViewClient() {
#Override
public void onPageFinished(WebView view, String url) {
ready.set(true);
}
});
w.setPictureListener(new PictureListener() {
#Override
public void onNewPicture(WebView view, Picture picture) {
if (ready.get()) {
final Canvas c = new Canvas(b);
view.draw(c);
w.setPictureListener(null);
signal.countDown();
}
}
});
w.layout(0, 0, rect.width(), rect.height());
w.loadDataWithBaseURL(baseURL, content, "text/html", "UTF-8", null);
}});
try {
signal.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
return b;
}
It has some limitations, but it's a start.
You can use the draw method to let it draw in a Bitmap of your choice. I made an example, don't forget internet and external storage rights of your manifest:
public class MainActivity extends Activity {
private WebView mWebView;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mWebView = new WebView(this);
setContentView(mWebView);
mWebView.loadUrl("http://tea.ch");
}
#Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if (keyCode != KeyEvent.KEYCODE_BACK) return super.onKeyDown(keyCode, event);
Bitmap bm = Bitmap.createBitmap(200, 300, Bitmap.Config.ARGB_8888);
Canvas c = new Canvas(bm);
mWebView.draw(c);
OutputStream stream = null;
try {
stream = new FileOutputStream(Environment.getExternalStorageDirectory() +"/teach.png");
bm.compress(CompressFormat.PNG, 80, stream);
if (stream != null) stream.close();
} catch (IOException e) {
} finally {
bm.recycle();
}
return super.onKeyDown(keyCode, event);
}
}
Why not use the WebView method : capturePicture() which returns a Picture and is available since API level 1 ?
It returns a picture of the entire document.
You could then crop the result with your rectangle and save the bitmap from there.
This example shows how to capture webView content last picture (it waits until webview complete rendering picture), it is an example of convert HTML to PNG using Android
Activity Code
public class HtmlViewer extends Activity {
private String HTML;
private Context ctx;
private Picture pic = null;
private int i=0; suppose this is the last pic
private int oldi = 0;
private Timer myTimer; // timer for waiting until last picture loaded
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_html_viewer);
Intent intent = getIntent();
HTML = intent.getStringExtra("HTML");
ctx = this;
WebView wv = (WebView)findViewById(R.id.webView1);
wv.setPictureListener(new PictureListener(){
public void onNewPicture(WebView view, Picture picture) {
Log.w("picture", "loading.." + String.valueOf(view.getProgress()));
pic = picture;
i++;
}
});
wv.loadData(HTML, "text/html; charset=utf-8", null);
wv.setWebViewClient(new WebViewClient()
{
public void onPageFinished(WebView wv, String url)
{
Picture p = wv.capturePicture();
myTimer = new Timer();
myTimer.schedule(new TimerTask() {
#Override
public void run() {
if (i > oldi)
oldi = i;
else
if (i != 0)
{
Log.w("picture", "finished");
cancel();
Picture picture = pic;
Log.w("picture", "onNewPicture- Height"+ picture.getHeight());
Log.w("picture", "onNewPicture- Width"+ picture.getWidth());
File sdCard = Environment.getExternalStorageDirectory();
if (picture != null)
{
Log.w("picture", " P OK");
Bitmap image = Bitmap.createBitmap(picture.getWidth(),picture.getHeight(), Config.ARGB_8888);
Canvas canvas = new Canvas(image);
picture.draw(canvas);
Log.w("picture", "C OK");
if (image != null) {
Log.w("picture", "I OK");
ByteArrayOutputStream mByteArrayOS = new ByteArrayOutputStream();
image.compress(Bitmap.CompressFormat.PNG, 90, mByteArrayOS);
try {
File file = new File(sdCard, "AccountView.PNG");
FileOutputStream fos = new FileOutputStream(file);
fos.write(mByteArrayOS.toByteArray());
fos.flush();
fos.close();
Log.w("picture", "F OK " + String.valueOf(mByteArrayOS.size()) + " ? " + String.valueOf(file.length()));
Intent sharingIntent = new Intent(Intent.ACTION_SEND);
Uri screenshotUri = Uri.fromFile(file);
sharingIntent.setType("image/png");
sharingIntent.putExtra(Intent.EXTRA_STREAM, screenshotUri);
startActivity(Intent.createChooser(sharingIntent, getResources().getString(R.string.ACCOUNT_VIEW_TITLE)));
((Activity)ctx).finish();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
}, 0, 1000);
Log.w("picture", "done");
loadcompleted = true;
}
});
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.activity_html_viewer, menu);
return true;
}
}
Layout
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".HtmlViewer" >
<WebView
android:id="#+id/webView1"
android:layout_width="match_parent"
android:layout_height="match_parent" />
This is a good library that can be used to convert any HTML content to bitmap.
It supports both URL and HTML String
https://github.com/iZettle/android-html2bitmap
Related
Im working on a augmented reality app which displays 3d models on the users face.I made a class which extends ArFragment in which i set the front camera to be opened once a model is selected.I made a custom button for capturing the image,but i dont know how to implement the capture of the image itself with the 3d model displayed and store it in the phone(preferably without the custom made buttons being shown in the picture).Tried some code found on internet for programatical screenshoting but it screenshots only the buttons(the arrow and the circle).Here is how the activity looks like:
Here is the class that extends ArFragment:
public class CustomArFragment extends ArFragment {
#Override
protected Config getSessionConfiguration(Session session) {
Config config = new Config(session);
config.setAugmentedFaceMode(Config.AugmentedFaceMode.MESH3D);
this.getArSceneView().setupSession(session);
return config;
}
#Override
protected Set<Session.Feature> getSessionFeatures() {
return EnumSet.of(Session.Feature.FRONT_CAMERA);
}
#Override
public View onCreateView(LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
FrameLayout frameLayout = (FrameLayout) super.onCreateView(inflater, container, savedInstanceState);
getPlaneDiscoveryController().hide();
getPlaneDiscoveryController().setInstructionView(null);
return frameLayout;
}
}
Here is the activity in which i set the 3d model:
public class ArActivity extends AppCompatActivity {
int pos;
private int[] images = {R.raw.glasses1, R.raw.glasses3, R.raw.glasses4, R.raw.glasses5,
R.raw.glasses6, R.raw.glasses7, R.raw.glasses8, R.raw.glasses9, R.raw.glasses10};
private ModelRenderable modelRenderable;
private boolean isAdded = false;
#Override
protected void onCreate(Bundle savedInstanceState) {
Bundle extras = getIntent().getExtras();
if (extras != null) {
pos = extras.getInt("pos");
}
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_ar);
CustomArFragment customArFragment = (CustomArFragment) getSupportFragmentManager().findFragmentById(R.id.arFragment);
ModelRenderable
.builder().setSource(this, images[pos])
.build()
.thenAccept(renderable -> {
modelRenderable = renderable;
modelRenderable.setShadowCaster(false);
modelRenderable.setShadowReceiver(false);
});
customArFragment.getArSceneView().setCameraStreamRenderPriority(Renderable.RENDER_PRIORITY_FIRST);
customArFragment.getArSceneView().getScene().addOnUpdateListener(frameTime -> {
if (modelRenderable == null)
return;
Frame frame = customArFragment.getArSceneView().getArFrame();
Collection<AugmentedFace> augmentedFaces = frame.getUpdatedTrackables(AugmentedFace.class);
for (AugmentedFace augmentedFace : augmentedFaces) {
if (isAdded)
return;
AugmentedFaceNode augmentedFaceNode = new AugmentedFaceNode(augmentedFace);
augmentedFaceNode.setParent(customArFragment.getArSceneView().getScene());
augmentedFaceNode.setFaceRegionsRenderable(modelRenderable);
isAdded = true;
}
});
}
}
I had a similar problem before.
Capturing the Camera image with placed 3d models.
i solved it with PixelCopy.
I'm adding my code of that part below.
I'm sorry that it's not going to be the straight-answer to your problem but i hope you get some ideas
ImageButton btn3 = (ImageButton)findViewById(R.id.camera_btn);
btn3.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
takePhoto();
}
});
private String generateFilename() {
//현재시간을 기준으로 파일 이름 생성
String date =
new SimpleDateFormat("yyyyMMddHHmmss", java.util.Locale.getDefault()).format(new Date());
return Environment.getExternalStoragePublicDirectory(
Environment.DIRECTORY_PICTURES) + File.separator + "IM/" + date + "_screenshot.jpg";
}
private void saveBitmapToDisk(Bitmap bitmap, String filename) throws IOException {
//사용자의 갤러리에 IM 디렉토리 생성 및 Bitmap 을 JPEG 형식으로 갤러리에 저장
File out = new File(filename);
if (!out.getParentFile().exists()) {
out.getParentFile().mkdirs();
}
try (FileOutputStream outputStream = new FileOutputStream(filename);
ByteArrayOutputStream outputData = new ByteArrayOutputStream()) {
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, outputData);
outputData.writeTo(outputStream);
outputStream.flush();
outputStream.close();
} catch (IOException ex) {
throw new IOException("Failed to save bitmap to disk", ex);
}
}
private void takePhoto(){
//PixelCopy 를 사용하여 카메라 화면과 object 를 bitmap 으로 생성
final String filename = generateFilename();
ArSceneView view = arFragment.getArSceneView();
final Bitmap bitmap = Bitmap.createBitmap(view.getWidth(),view.getHeight(),
Bitmap.Config.ARGB_8888);
final HandlerThread handlerThread = new HandlerThread("PixelCopier");
handlerThread.start();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
PixelCopy.request(view, bitmap, (copyResult) -> {
if (copyResult == PixelCopy.SUCCESS) {
try {
saveBitmapToDisk(bitmap, filename);
//Media Scanning 실시
Uri uri = Uri.parse("file://" + filename);
Intent i = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
i.setData(uri);
sendBroadcast(i);
} catch (IOException e) {
Toast toast = Toast.makeText(AR_Activity.this, e.toString(),
Toast.LENGTH_LONG);
toast.show();
return;
}
Snackbar snackbar = Snackbar.make(findViewById(android.R.id.content),
"스크린샷이 저장되었습니다.", Snackbar.LENGTH_LONG);
snackbar.setAction("갤러리에서 보기", v -> {
//어플 내에서 저장한 스크린샷을 확인 가능
File photoFile = new File(filename);
Uri photoURI = FileProvider.getUriForFile(AR_Activity.this,
AR_Activity.this.getPackageName() + ".ar.codelab.name.provider",
photoFile);
Intent intent = new Intent(Intent.ACTION_VIEW, photoURI);
intent.setDataAndType(photoURI, "image/*");
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
startActivity(intent);
});
snackbar.show();
} else {
Toast toast = Toast.makeText(AR_Activity.this,
"스크린샷 저장 실패!: " + copyResult, Toast.LENGTH_LONG);
toast.show();
}
handlerThread.quitSafely();
}, new Handler(handlerThread.getLooper()));
}
}
I need to show picture of user in my application and I retrieve that picture from server since my application also works in offline mode so I need to save that picture from server to my SD card , also I when i sync data from server next time If picture has changed then i need to change that picture in my SD card too how to determine if picture for particular user has changed
currently i save the image from server as follow though I use hardcoded url as of now and static user id
public class fetchImage extends Activity implements OnClickListener {
int id;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
id = 1;// declaring static as of now
}
{
new BackgroundTask().execute();
File storagePath = Environment.getExternalStorageDirectory();
File imgFile = new File(storagePath, "/Pictures/" + id + ".jpg");
if (imgFile.exists()) {
Bitmap myBitmap = BitmapFactory.decodeFile(imgFile
.getAbsolutePath());
}
}
class BackgroundTask extends AsyncTask<Void, Void, Void> {
ProgressDialog mDialog;
protected void onPreExecute() {
mDialog = ProgressDialog.show(fetchImage.this, "",
getString(R.string.progress_bar_loading), true);
};
#Override
protected Void doInBackground(Void... params) {
try {
savesd(id, null);
} catch (final Exception e) {
}
return null;
}
private void savesd(int id, URL uri) throws IOException {
URL url;
if (uri == null) {
url = new URL("http://i.zdnet.com/blogs/3-29-androids.jpg");
} else {
url = uri;
}
InputStream input = url.openStream();
try {
File storagePath = Environment.getExternalStorageDirectory();
OutputStream output = new FileOutputStream(new File(
storagePath, "/Pictures/" + id + ".jpg"));
try {
byte[] buffer = new byte[20000];
int bytesRead = 0;
while ((bytesRead = input.read(buffer, 0, buffer.length)) >= 0) {
output.write(buffer, 0, bytesRead);
}
} finally {
output.close();
}
} catch (Exception e) {
e.printStackTrace();
} finally {
input.close();
}
}
protected void onPostExecute(Void result) {
mDialog.dismiss();
};
}
#Override
public void onClick(View v) {
// TODO Auto-generated method stub
}
}
Also i've got one problem that when I uninstall this app from Device it should also clear these user images from sd card
I used timestamp to save the time when I last sync data and download files only after that time stamp
I am trying to display the images in galleryViews which are in ListView. I am downloading images from server to SDCard and then displaying it, first I am checking images in cache if image is not there then I am loading it from sdcard.
when the user starts the app for the first time I am downloading the images from server and saving them to sdcard in the mean time iam showing the activity with text only
I want if the image is not there in sdcard the after downloading the image it should display the image as soon as the image is download here is what I am doing.
public class AsyncImageLoader {
private boolean isImageView;
private final LinkedHashMap<String, Bitmap> cache =
new LinkedHashMap<String, Bitmap>(60, (float) 1.0, true);
Handler handler = new Handler() {
#Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
LoadImageFromSdCard loadImage = (LoadImageFromSdCard) msg.obj;
if (isImageView) {
loadImage.imageView.setImage(loadImage.bmp);
} else {
Thread thread = new Thread(new LoadImageFromSdCard(loadImage.uri, loadImage.imageView));
try {
Log.i("AsyncImageLoader", "SECOND THREAD STARTED");
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
thread.start();
}
}
};
public Bitmap loadImage(String uri,
ImageTextComboControl imageView) {
if (cache.containsKey(uri)) {
return cache.get(uri);
} else {
handler.post(new LoadImageFromSdCard(uri, imageView));
}
return null;
}
private class LoadImageFromSdCard implements Runnable {
String uri;
ImageTextComboControl imageView;
ImageView image;
Bitmap bmp = null;
public LoadImageFromSdCard(String uri, ImageTextComboControl imageView) {
this.uri = uri;
this.imageView = imageView;
}
public void run() {
FileInputStream fis;
try {
fis = new FileInputStream(new File(uri));
} catch (FileNotFoundException e) {
e.printStackTrace();
return;
}
bmp = BitmapFactory.decodeStream(fis);
if (imageView != null) {
isImageView = true;
cache.put(uri, bmp);
Message message = new Message();
message.obj = this;
handler.sendMessage(message);
}
}
}
}
Thanx
Is much better use Async Task class http://developer.android.com/guide/topics/fundamentals/processes-and-threads.html (using AsyncTask section) and, important, take care about large bitmap
http://developer.android.com/training/displaying-bitmaps/index.html
I solved my problem using onContentChanged() method in adapter. I am saving images to sdCard and getting the sdCard path saving it into sqlite database so when ever the data is changing in sqlite database onContentChanged method is called because I am using Cursor Adapter.
I am taking a screenshot of a RelativeLayout but it cause the error.
error:
05-10 17:43:44.249: ERROR/AndroidRuntime(7721): Caused by: java.lang.NullPointerException
05-10 17:43:44.249: ERROR/AndroidRuntime(7721): at android.graphics.Bitmap.createBitmap(Bitmap.java:358)
05-10 17:43:44.249: ERROR/AndroidRuntime(7721): at com.API.Connect.share(FacebookConnect.java:110)
code:
public class Connect extends Activity implements LoginListener {
public static String news = " ";
RelativeLayout bitmapimage;
Bitmap bitmap;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.image);
bitmapimage = (RelativeLayout) findViewById(R.id.bitmapimg);
TextView txtlove = (TextView) findViewById(R.id.txtlove);
txtlove.setText(news.toString());
share();
}
public void share() {
View v1 = bitmapimage;
v1.setDrawingCacheEnabled(true);
bitmap = Bitmap.createBitmap(v1.getDrawingCache());
v1.setDrawingCacheEnabled(false);
saveImage();
// TODO Auto-generated method stub
}
void saveImage() {
String state = Environment.getExternalStorageState();
Environment.MEDIA_MOUNTED.equals(state);
File myDir = new File("/mnt/sdcard/DCIM/Camera");
myDir.mkdirs();
String fname = "mytry.jpg";
File file = new File(myDir, fname);
if (file.exists())
file.delete();
try {
FileOutputStream out = new FileOutputStream(file);
// create a Bitmap object from our image path.
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, out);
out.flush();
out.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
What you are trying to do is correct. But in onCreate() your view wouldn't have drawn. It's just inflated. So you might have to consider using the below method. try this out.
#Override
public void onWindowFocusChanged(boolean hasFocus)
{
super.onWindowFocusChanged(hasFocus);
share(); //Call your share() here.
}
This method gets called once your view is drawn, and hence you will be able to get the bitmap by overriding this method.
I think i have found your error.
getdrawingcache already returns a bitmap, so you do not have to create a new one
that probably results into a nullpointer exception.
so you should try
bitmap = v1.getDrawingCache();
instead of
bitmap = Bitmap.createBitmap(v1.getDrawingCache());
let me know if this works.
Greets,
Wottah
I have a webview which loads a local html page and I need to know when it has finished loading so I was using the function onPageFinished() which is fired straight away but for some reason it works fine on websites e.g. google.co.uk.
public class TestwebviewActivity extends Activity {
/** Called when the activity is first created. */
WebView webview = null;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
webview = (WebView) findViewById(R.id.webview);
Log.d("Webview", "created");
webview.getSettings().setJavaScriptEnabled(true);
webview.setWebViewClient(new WebViewClient()
{
public void onPageFinished(WebView view, String url)
{
Log.d("Webview", "Finished Loading");
Picture picture = view.capturePicture();
Bitmap b = Bitmap.createBitmap( 300, 300, Bitmap.Config.ARGB_8888);
Canvas c = new Canvas( b );
picture.draw( c );
FileOutputStream fos = null;
try {
fos = new FileOutputStream( "/sdcard/google_" + System.currentTimeMillis() + ".jpg" );
if ( fos != null )
{
b.compress(Bitmap.CompressFormat.JPEG, 90, fos );
fos.close();
Log.d("Webview", "Image Created");
}
} catch( Exception e )
{
//...
}
}
});
webview.loadUrl("file:///android_asset/htmlpage.html");
}
}
This has only happened since upgraded to android 3.2 - previously on 2.3
Anyone come across the same issues or any suggestions?
Thanks if anyone can help.
Try to use deprecated PictureListener:
wv.setPictureListener(new PictureListener() {
#Override
public void onNewPicture(WebView view, Picture picture) {
if (i < pageList.size()) {
wv.loadUrl("javascript: highlightSearchTerms('" + searchKey + "');");
} else listener.onFinishSearch();
i++;
}
});