0

I am trying to take screenshot of the top running activity view programmatically using the following code and then I'll share the bitmap to a socket server program every interval. After a long research I got this code working. But the issue is, This code is not taking top running activity (whichever the top running activity in my app), instead it is taking screenshot of the particular activity only where this code is written. It is not taking screenshot of any activity which is running on the foreground. Could someone please advise, what may be wrong here?

    private Runnable mUpdate = new Runnable() {

        public void run() {

            //ActivityManager mActivityManager = (ActivityManager) getSystemService(Activity.ACTIVITY_SERVICE);
            //List<ActivityManager.RunningTaskInfo> task = mActivityManager.getRunningTasks(1); 
            //ComponentName componentInfo = task.get(0).topActivity;
            //mActivityManager.getRunningTasks(1).get(0).topActivity.
            //View view = getWindow().getDecorView().findViewById(android.R.id.content);
            //getWindow().findViewById(android.R.id.content)


            View view = getWindow().getDecorView().findViewById(android.R.id.content);
            context = getApplicationContext(); 

            try {
               // HERE IS THE SCREENSHOT TAKEN PROGRAMMATICALLY
                bitmap = loadBitmapFromView(context,view);

                //bitmap = takeScreenshot();
                output = ((GlobalClass) RandomIDActivity.this.getApplication()).socket.getOutputStream();
                Log.d("ClientActivity", "C: image writing.");
                ByteArrayOutputStream stream = new ByteArrayOutputStream();
                bitmap.compress(CompressFormat.JPEG, 70, stream);
                byte[] imgbyte = stream.toByteArray();

                String endStr = "END";
                byte[] endByte = endStr.getBytes();

                byte[] finalByteToSend = new byte[imgbyte.length + endByte.length];
                System.arraycopy(imgbyte, 0, finalByteToSend, 0, imgbyte.length);
                System.arraycopy(endByte, 0, finalByteToSend, imgbyte.length, endByte.length);

                output.write(finalByteToSend,0,finalByteToSend.length);
                output.flush();
                imgbyte = null;
                endByte = null;
                finalByteToSend = null;

                }
                catch (Exception ex) {
                    ex.printStackTrace();
                } 

            shareHandler.postDelayed(this, 5000);
        }

        };

Extracting as bitmap here,

public static Bitmap loadBitmapFromView(Context context, View v) {

        DisplayMetrics dm = context.getResources().getDisplayMetrics();

        v.measure(MeasureSpec.makeMeasureSpec(dm.widthPixels, MeasureSpec.EXACTLY),
                MeasureSpec.makeMeasureSpec(dm.heightPixels, MeasureSpec.EXACTLY));

        v.layout(0, 0, v.getMeasuredWidth(), v.getMeasuredHeight());

        Bitmap returnedBitmap = Bitmap.createBitmap(v.getMeasuredWidth(),
                v.getMeasuredHeight(), Bitmap.Config.ARGB_8888);

        Canvas canvas = new Canvas(returnedBitmap);

        v.draw(canvas);
        return returnedBitmap;
    }

UPDATE CODE:

ActivityManager mActivityManager = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);

List<ActivityManager.RunningTaskInfo> task = mActivityManager.getRunningTasks(1); 

String currActivityString = task.get(0).topActivity.getClassName();

try {
            Log.d("currActivityString: ", "currActivityString: " + currActivityString);
            Log.d("Test 1111" , "Test 1111");
            myCurrClass = Class.forName(currActivityString);
            Log.d("Test 2222" , "Test 2222");
            } catch (ClassNotFoundException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
            }

            try {
            Log.d("Test 3333" , "Test 3333");
            activityObj = (Activity) myCurrClass.newInstance();
            Log.d("Test 4444" , "Test 4444");
            } catch (InstantiationException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
            } catch (IllegalAccessException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
            }
*// CRASHING IN THIS LINE as Fatal Exception: NullPointerException*
View view = activityObj.getWindow().getDecorView().getRootView();

bitmap = loadBitmapFromView(activityObj,view);
Stella
  • 1,728
  • 5
  • 41
  • 95
  • Are you trying to take screenshots of activities belonging to other apps? I doubt you will be able to do that using your approach although it might be suited for your own activities. – Tobias Jun 14 '14 at 12:48
  • That means you want screenshots for activity which is on foreground...right? – Pratik Dasa Jun 14 '14 at 12:50
  • No, not other apps. Yes, I want to take screenshot of the foreground activity view only inside of my app only. But the current code takes only the particular activity only where this code is written, but i want to take screenshot of any activity inside my activity which is in foreground. Thanks. – Stella Jun 14 '14 at 12:53
  • @Catherine you can create one interface for this, and define one method there and implement that interface where you want to take screenshot, I mean in all the classes, and Create one Constant class and inside it put your capture code there and just call that method when you implement your Interface in Each and Every activity. – Pratik Dasa Jun 14 '14 at 12:58
  • Oh sorry, i initially tried that way, but my requirement is I should not touch any existing source. I will just add this one activity, which should be able to take screenshot programmatically whicever view is running on the foreground in my existing app. – Stella Jun 14 '14 at 13:18
  • u can do that by using service. and service starts as double press of power buttons. Then it captures the screen – Maveňツ Jun 19 '14 at 09:55
  • Maven, Could you give some sample or link where I can find the info further? – Stella Jun 20 '14 at 09:31
  • have you assigned anything to `activityObj`? you need to pass the reference of your top most activity to `activityObj`. – SMR Jun 23 '14 at 06:39

3 Answers3

1

getWindow() is a method of Activity. Each activity has a different Window instance.

To find the top (foreground) activity, see: Android: How can I get the current foreground activity (from a service)?

Community
  • 1
  • 1
yoah
  • 7,180
  • 2
  • 30
  • 30
  • What should I add in "loadBitmapFromView(context,view);" ? – Stella Jun 18 '14 at 06:32
  • The link you shared may not help me. As I said earlier, I should not touch any existing source. I will just add this one activity class in my existing app, and it should be able to take screenshot programmatically whichever the view is running on the foreground in my existing app. – Stella Jun 18 '14 at 10:04
  • First up, while your activity is in a paused state I'm not sure how it could possibly access the foreground activity's view components. You should probably be thinking of a service/or some other background component to achieve this objective. – Sdr Jun 19 '14 at 00:56
  • Why you mention activity is in paused state? Could you clarify please? If you could see my code in post, I am running a runnable thread in certain interval. So, this will be running always and understands whatever the activity is on top. That is fine. No issues on that. – Stella Jun 20 '14 at 09:34
0

There is a very good way to go about this. Create a class names CommonUtils and create a method there which will be used to take screen shots and send it to stream like this:

CommonUtils.java

public static byte[] takeScreenShot(Activity context){
    View view = context.getWindow().getDecorView().getRootView();
    view.setDrawingCacheEnabled(true);
    Bitmap bitmap = Bitmap.createBitmap(view.getDrawingCache());
    view.setDrawingCacheEnabled(false);
    ByteArrayOutputStream stream = new ByteArrayOutputStream();
    bitmap.compress(CompressFormat.JPEG, 70, stream);
    return stream.toByteArray();
    // now that you have the byte array you can send it when ever you want.
}

now all you have to do is call this mehod inside any of your activity like this:

CommonUtils.takeScreenShot(MyCurrentActivity.this);

[Note]: you can either send it right away inside the same method or anywhere else by using the byte[]

[EDIT]: As you have mentioned in the comments that you are able to get the top running Activity. Store its instance and make the following changes in your Runnable:

Lets say you have stored the top activity's instance as topActivity

Activity topActivity = //your top activity;
View view = topActivity.getWindow().getDecorView().getRootView();

and you need to do this very minor change in your loadBitmapFromView() method:

public static Bitmap loadBitmapFromView(Activity context, View v) {

you dont need to change the rest in this method.

and call this like this inside your Runnable:

bitmap = loadBitmapFromView(topActivity,view);

Hope it helps :)

Community
  • 1
  • 1
SMR
  • 6,628
  • 2
  • 35
  • 56
  • Hi, Thanks! But you are repeating the same thing here. I don't want to include any of the code into my existing source files. Please check my queries above. Your sample is again, I need to include this one liner code everywhere in all existing activities, I don't want that way. – Stella Jun 22 '14 at 17:12
  • how can you can expect anything without adding some code anywhere? and it is just 1 line of code which will do the desired task without changing the existing much. if you want to get something done then you have to add code in that activity. if you dont want this then you have to create a service for do the task but you will need the procedure to check if any of your activity is running or not and even when you switch the activity. moreover you need to determine when to take a screen shot and I guess that situation can only be determined inside an activity. – SMR Jun 22 '14 at 18:42
  • Hi, As I mentioned in my code, i am already getting whatever the top running activity. Because I am having runnable thread, it will be started in a certain activity and then it will keep on monitoring(running at 5 secs interval) whoever is on top. My issue is, I need to pass "Context" and "View" in screenshot function. I don't know how to get this from ActivityManager mActivityManager = (ActivityManager) getSystemService(Activity.ACTIVITY_SERVICE); etc. code – Stella Jun 22 '14 at 18:59
  • are you able to get the context? – SMR Jun 22 '14 at 19:54
  • Can you check my updated code in the original post, I tried that, but CRASHING as Fatal Exception: NullPointerException in a particular line. – Stella Jun 23 '14 at 05:35
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/56110/discussion-between-smr-and-catherine). – SMR Jun 23 '14 at 12:47
  • When I printed activityObj, it shows perfectly whatever the activity is on top at that time. But, When I use this line, activityObj = (Activity) myCurrClass.newInstance(); It creates the new instance rather than the top running activity instance, I doubt. – Stella Jun 23 '14 at 12:49
0

Problem may not with your screen capture function(which I didn't check).

when your code execute it became top running activity and take screenshot of it. best solution i think make your activity invisible and take screenshot then it take screenshot of toprunning activity before execute your activity.

to make your activity invisible apply this style and check

style.xml

<?xml version="1.0" encoding="UTF-8"?>
<resources>
    <style name="Invisible" parent="@android:style/Theme">
        <item name="android:windowBackground">@android:color/transparent</item>
        <item name="android:windowIsTranslucent">true</item>
        <item name="android:windowIsFloating">true</item>
        <item name="android:windowNoTitle">true</item>
        <item name="android:windowContentOverlay">@null</item>
        <item name="android:backgroundDimEnabled">false</item>
    </style>
</resources>

and in AndroidManifet apply this to your activity

<activity
    android:name=".MyActivity"
    android:label="@string/app_name"
    android:theme="@style/Invisible"
>

to make activity visible after capture screen, you can setTheam to default

check this also How do I create a transparent Activity on Android?

Community
  • 1
  • 1
UdayaLakmal
  • 4,035
  • 4
  • 29
  • 40
  • No, I have already mentioned that I am not seeing the activity where the runnable code is written, behaves as top running activity. It is properly giving top running activity even I moved to different activity. There is no issue on that. Only problem I face is how to convert it as "Context" and "View" and pass in loadBitmapFromView function. When I use this line, activityObj = (Activity) myCurrClass.newInstance(); It creates the new instance rather than the top running activity instance, I doubt. – Stella Jun 23 '14 at 12:45
  • When I printed activityObj, it shows perfectly whatever the activity is on top at that time. But, When I use this line, activityObj = (Activity) myCurrClass.newInstance(); It creates the new instance rather than the top running activity instance, I doubt. – Stella Jun 23 '14 at 12:48