android taking screenshot of offscreen page
Asked Answered
H

4

19

I am working on an android application. I have an activity, say A, which fills the entire screen with views..On a button click in A I want to start another activity, say B, which also has some views and controls. I want activity B to be offscreen , and want to take the screenshot of B from A . Is it possible?

Note: I am successful in taking the screenshot of page A by saving the drawing cache in to a bitmap, but struggling to take the offscreen page's screenshot.

Hognut answered 9/4, 2011 at 9:26 Comment(0)
H
5

Well I have achieved what I want. These are the steps I used.

  1. Start activity B with startActivityForResult() instead of startActivity().

    Intent bIntent = new Intent(A.this,B.class);
    startActivityForResult(bIntent,B_REQUEST_CODE);
    
  2. In onCreate of activity B take mainLayout of B and enable its drawing cache

    final LinearLayout layout = (LinearLayout)
                         findViewById(R.id.app_parent_layout);
    layout.setDrawingCacheEnabled(true);
    layout.setDrawingCacheQuality(LinearLayout.DRAWING_CACHE_QUALITY_HIGH);
    
  3. In activity B wait till its layout is drawn completely(This is very important). For that in onCreate() of activity B, get mainLayout of B, use ViewTreeObserver to get a call back when its layout is drawn completely.

    ViewTreeObserver vto = layout.getViewTreeObserver();
    vto.addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
     @Override
     public void onGlobalLayout() {
      layout.getViewTreeObserver().removeGlobalOnLayoutListener(this);
      getDrawingBitmap();
     }
    });
    
  4. Now getDrawingBitmap() gets called when layout is drawn completely. There take your screenshot.

    public void getDrawingBitmap(){
        LinearLayout mainLayout = (LinearLayout)
                            findViewById(R.id.app_parent_layout);
        Bitmap b = mainLayout.getDrawingCache();
    
        File file = saveBitmapAsFile(b);
    
        Intent resultIntent = new Intent();
        resultIntent.putExtra("FullFileName",file.getAbsolutePath());
        setResult(Activity.RESULT_OK,resultIntent);
        finish();
    }
    

    Here I am taking bitmap and saving it as a file and returning the filename as a result code. I am sure there are better method to send the bitmap to parent activity than saving as a file and sending filename. But I have a requirement anyway to save bitmap for sometime. So For me this is the easier method. After setting result finish activity.

  5. Now on Activity A onActivityResult() retrieve the filename.

    public void onActivityResult(int requestCode, int resultCode, Intent data) {
      super.onActivityResult(requestCode, resultCode, data);
      if(requestCode == B_REQUEST_CODE){
        if (resultCode == Activity.RESULT_OK) { 
          String newText = data.getStringExtra("FullFileName");
         File dir = new File (newText);
        } 
      }
    }
    
Hognut answered 19/9, 2013 at 7:16 Comment(5)
Question here is, how does this activity B still appear offscreen? You don't have any code here that handles that?Anitaanitra
@Anitaanitra Question here is, how does this activity B still appear offscreen? Activity B never appear at all. It gets finished (see finish() call in getDrawingBitmap method) before it comes on screen.Hognut
Ok, I understand now. However, when I try it myself, I see a short flash of the new activity, so a real solution it is not.Anitaanitra
@Anitaanitra Nope, I know. This is not the greatest solution. But when I used this, there was no flash. Can you check whether your getDrawingBitmap() method is getting called before onResume() call? I don't think there is any other way to call an activity off screen in android (Happy if someone corrects me).Hognut
Maybe better way to pass screenshot back to the activity is to serialise it into byte array (Serialise it compressed for example convert to png with value 90) and then set it to bundle extras. In other activity read this bitmap from byte array.Barrett
B
14

Yes it is possible...You should extend ActivityGroup in Activity 'A'. Then do this in your button click event...

 View view =getLocalActivityManager().startActivity("B",new Intent(this,B.class).addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)).getDecorView();

then convert that view as bitmap...

I think this is helpful for you...

Boxberry answered 16/4, 2011 at 9:37 Comment(8)
Thanks John..My A class is already extending Activity, and java does not allow multiple inheritance..So how can I make A extend ActivityGroup..Hognut
Oops I got inheritance part right...Just extend activityGroup instead of activity..Now trying to take screenshot..Hognut
Well I am not able to take the screenshot of this DecorView I got. How to get the Main layout in this decorView.For my onScreen activity I took the screenshot of the mainLayout. How to get the mainLayout of my offScreen from this decorViewHognut
How can I take the bitmap of this decorView()..getDrawingCache() of decorView always returns null.Hognut
In the end I didnt use the method you described. Still I started from your answer and in the end got what I wanted..But I think there are better methods (more straightforward).If you read this can you please give an example of how you would do this.Hognut
If i want to captured the screen layout that showing the camera preview then will this code works ???Ectopia
If you have better methods ("MORE STRAIGHTFORWARD") you should probably post that answer here.Chaffee
@Kantha I am removing the tick from your answer because I have decided to add my own answer which fully describe the steps (which I should have done earlier, rather than editing the question and adding answer there. I was new to SO then). Your answer certainly helped me on the way. Thanks for thatHognut
H
5

Well I have achieved what I want. These are the steps I used.

  1. Start activity B with startActivityForResult() instead of startActivity().

    Intent bIntent = new Intent(A.this,B.class);
    startActivityForResult(bIntent,B_REQUEST_CODE);
    
  2. In onCreate of activity B take mainLayout of B and enable its drawing cache

    final LinearLayout layout = (LinearLayout)
                         findViewById(R.id.app_parent_layout);
    layout.setDrawingCacheEnabled(true);
    layout.setDrawingCacheQuality(LinearLayout.DRAWING_CACHE_QUALITY_HIGH);
    
  3. In activity B wait till its layout is drawn completely(This is very important). For that in onCreate() of activity B, get mainLayout of B, use ViewTreeObserver to get a call back when its layout is drawn completely.

    ViewTreeObserver vto = layout.getViewTreeObserver();
    vto.addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
     @Override
     public void onGlobalLayout() {
      layout.getViewTreeObserver().removeGlobalOnLayoutListener(this);
      getDrawingBitmap();
     }
    });
    
  4. Now getDrawingBitmap() gets called when layout is drawn completely. There take your screenshot.

    public void getDrawingBitmap(){
        LinearLayout mainLayout = (LinearLayout)
                            findViewById(R.id.app_parent_layout);
        Bitmap b = mainLayout.getDrawingCache();
    
        File file = saveBitmapAsFile(b);
    
        Intent resultIntent = new Intent();
        resultIntent.putExtra("FullFileName",file.getAbsolutePath());
        setResult(Activity.RESULT_OK,resultIntent);
        finish();
    }
    

    Here I am taking bitmap and saving it as a file and returning the filename as a result code. I am sure there are better method to send the bitmap to parent activity than saving as a file and sending filename. But I have a requirement anyway to save bitmap for sometime. So For me this is the easier method. After setting result finish activity.

  5. Now on Activity A onActivityResult() retrieve the filename.

    public void onActivityResult(int requestCode, int resultCode, Intent data) {
      super.onActivityResult(requestCode, resultCode, data);
      if(requestCode == B_REQUEST_CODE){
        if (resultCode == Activity.RESULT_OK) { 
          String newText = data.getStringExtra("FullFileName");
         File dir = new File (newText);
        } 
      }
    }
    
Hognut answered 19/9, 2013 at 7:16 Comment(5)
Question here is, how does this activity B still appear offscreen? You don't have any code here that handles that?Anitaanitra
@Anitaanitra Question here is, how does this activity B still appear offscreen? Activity B never appear at all. It gets finished (see finish() call in getDrawingBitmap method) before it comes on screen.Hognut
Ok, I understand now. However, when I try it myself, I see a short flash of the new activity, so a real solution it is not.Anitaanitra
@Anitaanitra Nope, I know. This is not the greatest solution. But when I used this, there was no flash. Can you check whether your getDrawingBitmap() method is getting called before onResume() call? I don't think there is any other way to call an activity off screen in android (Happy if someone corrects me).Hognut
Maybe better way to pass screenshot back to the activity is to serialise it into byte array (Serialise it compressed for example convert to png with value 90) and then set it to bundle extras. In other activity read this bitmap from byte array.Barrett
L
1

I think it would be very hard to do so, as I know no way to get in control of the drawing methods of those views, and the android OS won't draw them either unless visible.

However it may be possible to foreground the other View for only a short time to fill its drawing cache. Maybe it can be put background again before it even becomes visible, as for smooth experience the OS seems to draw views offscreen before they are actually composed to foreground.

Another trick could be to attach your foreground View as an child View onto the background, and give it a very slight alpha transparency. The WebView for example can be superimposed this way very well, actually forcing both the WebView and its background to be drawn. Maybe this works with other Views too.

Lighthearted answered 15/4, 2011 at 0:42 Comment(4)
"android OS won't draw them either unless visible" are you sure? If yes, that kills it..Hognut
"visible" is however no exact term. If for example only one pixel of a View is visible due to a small container View enclosing it, it's onDraw(Canvas) method need to be called either as the OS cannot know where on the Canvas onDraw will actually draw. So user-defined Wievs with some onDraw method will draw. However, a layouted view with UI elements inside must not: Every UI element is a View on its own and could be left out if not assumed visible by the system.Lighthearted
Maybe you could draw the View yourself by calling draw(Canvas) after invalidate() using your own Canvas.Lighthearted
"visible" is however no exact term. If for example only one pixel of a View is visible due to a small container View enclosing it, it's onDraw(Canvas) method need to be called " -> well you are right there..let me try that one..Hognut
O
0

i m using this way its working great but there is an issue i m just taking snapshots of one layout first time when i take the snapshoot it gives me right pic but after some changes i take it again it gives me previous snapshoot.

RelativeLayout layout = (RelativeLayout)findViewById(R.id.mainview);
    layout.setDrawingCacheEnabled(true);
    layout.setDrawingCacheQuality(LinearLayout.DRAWING_CACHE_QUALITY_HIGH);
    layout.buildDrawingCache();
    Bitmap bitmap = layout.getDrawingCache();

          File file =  new File(Environment.getExternalStorageDirectory().toString() + "/sPenimg.png");
          FileOutputStream ostream;
          try {
              ostream = new FileOutputStream(file);
              bitmap.compress(CompressFormat.PNG, 100, ostream);
              ostream.flush();
              ostream.close();      
          } catch (FileNotFoundException e) {
              // TODO Auto-generated catch block
              e.printStackTrace();
          } catch (IOException e) {
              // TODO Auto-generated catch block
              e.printStackTrace();
          }

This issue is solved to add ((RelativeLayout)findViewById(R.id.mainview)).destroyDrawingCache();

Octuple answered 9/4, 2011 at 9:26 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.