Screenshot einer Activity erstellen und abspeichern

Falls noch jemand hieran interessiert ist, ich starte nächste Woche einen Versuch mit CM7 für das ZTE Blade, wenn es funktioniert sollte es für alle CM7er-Geräte möglich sein. Vorher muss ich aber Windows neu installieren, Ubuntu neu installieren, tausen Programme neu installieren, Daten wieder herstellen, die komplette CM7-Source runterladen, meine Änderungen machen und...hm ja.


Edit: Ach ja, der Code lief damals im Emulator!
 
Zuletzt bearbeitet:
Folgender Code läuft problemlos in CM7:

Änderungen in der ActivityThread.java:
Code:
    //Begin own Code
    private int mBigThumbnailWidth = -1;
    private int mBigThumbnailHeight = -1;

    private final Bitmap createBigThumbnailBitmap(ActivityClientRecord r) {
        Bitmap bigThumbnail = null;
        try {
            int w = mBigThumbnailWidth;
            int h;
            if (w < 0) {
                Resources res = r.activity.getResources();
                mBigThumbnailHeight = h =
                    res.getDimensionPixelSize(com.android.internal.R.dimen.big_thumbnail_height);

                mBigThumbnailWidth = w =
                    res.getDimensionPixelSize(com.android.internal.R.dimen.big_thumbnail_width);
            } else {
                h = mBigThumbnailHeight;
            }

            // On platforms where we don't want thumbnails, set dims to (0,0)
            if ((w > 0) && (h > 0)) {
                View topView = r.activity.getWindow().getDecorView();

                // Maximize bitmap by capturing in native aspect.
                if (topView.getWidth() >= topView.getHeight()) {
                    bigThumbnail = Bitmap.createBitmap(w, h, THUMBNAIL_FORMAT);
                } else {
                    bigThumbnail = Bitmap.createBitmap(h, w, THUMBNAIL_FORMAT);
                }

                bigThumbnail.eraseColor(0);
                Canvas cv = new Canvas(bigThumbnail);
                if (!r.activity.onCreateThumbnail(bigThumbnail, cv)) {
                    bigThumbnail = null;
                }
            }

        } catch (Exception e) {
            if (!mInstrumentation.onException(r.activity, e)) {
                throw new RuntimeException(
                        "Unable to create bigThumbnail of "
                        + r.intent.getComponent().toShortString()
                        + ": " + e.toString(), e);
            }
            bigThumbnail = null;
        }

        return bigThumbnail;
    }
    //End own Code

Code:
private final void performStopActivityInner(ActivityClientRecord r,
            StopInfo info, boolean keepShown) {
        if (localLOGV) Slog.v(TAG, "Performing stop of " + r);
        if (r != null) {
            if (!keepShown && r.stopped) {
                if (r.activity.mFinished) {
                    // If we are finishing, we won't call onResume() in certain
                    // cases.  So here we likewise don't want to call onStop()
                    // if the activity isn't resumed.
                    return;
                }
                RuntimeException e = new RuntimeException(
                        "Performing stop of activity that is not resumed: "
                        + r.intent.getComponent().toShortString());
                Slog.e(TAG, e.getMessage(), e);
            }

            if (info != null) {
                try {
                    // First create a thumbnail for the activity...
                    info.thumbnail = createThumbnailBitmap(r);
                    info.description = r.activity.onCreateDescription();
                } catch (Exception e) {
                    if (!mInstrumentation.onException(r.activity, e)) {
                        throw new RuntimeException(
                                "Unable to save state of activity "
                                + r.intent.getComponent().toShortString()
                                + ": " + e.toString(), e);
                    }
                }
            }
            
            //Start of own Code
            
            Bitmap bigThumbnail = createBigThumbnailBitmap(r);
        	if (bigThumbnail != null){
        		try {
        			   FileOutputStream fos= r.activity.openFileOutput("applicationThumbnail.png",
        			      Context.MODE_WORLD_READABLE);

        			   bigThumbnail.compress(Bitmap.CompressFormat.PNG, 90, fos);

        			   fos.flush();
        			   fos.close();
        			   } catch (Exception e) {
        			   Log.e("BigThumbnailAddon", e.toString());
        			}
        	}
            //End of own Code

            if (!keepShown) {
                try {
                    // Now we are idle.
                    r.activity.performStop();
                } catch (Exception e) {
                    if (!mInstrumentation.onException(r.activity, e)) {
                        throw new RuntimeException(
                                "Unable to stop activity "
                                + r.intent.getComponent().toShortString()
                                + ": " + e.toString(), e);
                    }
                }
                r.stopped = true;
            }

            r.paused = true;
        }
    }


Hinzugefügt in der dimens.xml:
(Diese Änderung muss vor dem Einfügen des Codes oben erledigt werden, dann macht man einmal make damit Eclipse die Variablen benutzen kann.)

Code:
    <!-- The width that is used when creating big thumbnails of applications. -->
    <dimen name="big_thumbnail_width">381dp</dimen>
    <!-- The height that is used when creating big thumbnails of applications. -->
    <dimen name="big_thumbnail_height">240dp</dimen>




Und meine eigene, schnell zusammengeklaute App:
(Der größte Teil ist vom RecentApplicationsDialog, dem eigentlichen Lang-Home-Menü. Da hab ich auch das hidden Flag gefunden.)

Code:
package com.felixl.TaskSwitcher;

import android.app.Activity;
import android.os.Bundle;
import android.app.ActivityManager;
import android.content.ActivityNotFoundException;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import static android.util.Log.d;
import java.util.List;

//import android.util.Log;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import android.view.View.OnClickListener;
import android.widget.ImageView;
import android.widget.LinearLayout;

public class TaskSwitcherMainActivity extends Activity implements OnClickListener {
	private static String LOG_TAG = "ATaskSwitcherMainActivity";
	private ActivityManager am;
	private PackageManager pm;
	private static String myfilename = "applicationThumbnail.png";
	private LinearLayout ll;
	private ImageView[] myImageViews;
	
	private static int NUM_BUTTONS = 8;
    private static int MAX_RECENT_TASKS = NUM_BUTTONS * 2; // lieber etwas zu viele, mache fallen weg.
    //WindowManager.FLAGDIMBEHIND benutzen? Leistung!
    
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        pm = this.getPackageManager();
        am = (ActivityManager) this.getSystemService( ACTIVITY_SERVICE );
        ll = (LinearLayout) findViewById(R.id.ll);
    }
    
    @Override
    public void onResume(){
    	super.onResume();
    	
    	myImageViews = new ImageView[NUM_BUTTONS];
    	ll.removeAllViews();
    	
    	Intent homeIntent = new Intent(Intent.ACTION_MAIN);
    	homeIntent.addCategory(Intent.CATEGORY_HOME);
    	ActivityInfo homeInfo = homeIntent.resolveActivityInfo(pm, 0);

        List<ActivityManager.RecentTaskInfo> recentTasks = am.getRecentTasks(MAX_RECENT_TASKS,2);//);2 = .RECENT_IGNORE_UNAVAILABLE, hidden FLAG);
        int index = 0;
        int numTasks = recentTasks.size();
        for (int i = 0; i < numTasks && (index < NUM_BUTTONS); ++i) {
            final ActivityManager.RecentTaskInfo info = recentTasks.get(i);

            Intent intent = new Intent(info.baseIntent);
            if (info.origActivity != null) {
                intent.setComponent(info.origActivity);
            }

            // Skip the current home activity.
            if (homeInfo != null) {
                if (homeInfo.packageName.equals(
                        intent.getComponent().getPackageName())
                        && homeInfo.name.equals(
                                intent.getComponent().getClassName())) {
                    continue;
                }
            }

            intent.setFlags((intent.getFlags()&~Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED)
                    | Intent.FLAG_ACTIVITY_NEW_TASK);
            final ResolveInfo resolveInfo = pm.resolveActivity(intent, 0);
            if (resolveInfo != null) {
                final ActivityInfo activityInfo = resolveInfo.activityInfo;
                final String title = activityInfo.loadLabel(pm).toString();
                
                final String myPackageName = activityInfo.packageName;

                if (title != null && title.length() > 0 && myPackageName != null) {
                	
                	String filelocation = "data/data/"+myPackageName+"/files";
                	
                	java.io.File file = new java.io.File(filelocation , myfilename);
                	Bitmap b = null;
                	if (file.exists()) {
                		d(LOG_TAG, "file exists");
                		b = BitmapFactory.decodeFile(filelocation+"/"+ myfilename);
                	}else{
                		d(LOG_TAG, "file doesn't exist: " + filelocation);
                		continue;
                	}
                	if (b != null){
                		d(LOG_TAG, "	Bitmap found: "+b);
        	        	ImageView iv = new ImageView(this);
        	        	ViewGroup.MarginLayoutParams mlp = new ViewGroup.MarginLayoutParams(b.getWidth(), b.getHeight());
        	        	mlp.setMargins(10, 10, 10, 10);
        	        	iv.setLayoutParams(mlp);
        	        	iv.setImageBitmap(b);
        	        	ll.addView(iv);
        	        	iv.setTag(intent);
        	        	iv.setOnClickListener(this);
        	        	myImageViews[index]=iv;
        	        	++index;
                	}
                }
            }
        }
    }
    
    @Override
    public boolean onCreateThumbnail(Bitmap bitmap,Canvas canvas){
    	return false; //Verhindert das diese App ein Thumbnail erstellt bekommt.
    }

	@Override
	 public void onClick(View v) {

		for (ImageView b: myImageViews){
            if (b != null && b == v) {
                // prepare a launch intent and send it
                Intent intent = (Intent)b.getTag();
                if (intent != null) {
                    intent.addFlags(Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY);
                    try {
                        this.startActivity(intent);
                    } catch (ActivityNotFoundException e) {
                        Log.w("Recent", "Unable to launch recent task", e);
                    }
                }
                break;
            }
        }
        finish();
    }
}



Der Code ist noch optimierbar. ;)
Interessanterweise kann ich damit fast jede App öffnen, ganz ohne Permission (die afaik eigentlich nötig wäre), nur bei k9 bekomme ich hin und wieder deshalb eine Exception.
 
Zuletzt bearbeitet:
[YT]http://www.youtube.com/watch?v=ACw5lyDljzg[/YT]
35653d1305611533-screenshot-einer-activity-erstellen-abspeichern-device.png


You can see a screenshot (made with ddms ;)) above, and find the app I'm currently using below (needs no permissions, is signed and can be installed easily with a file manager or pushed into the update.zip). Note that the app will only work on a system with the changes described one post above and still has some problems, I had no will to solve the permission-based error with k9 till now, so the app may FC if you try to open k9. This is an alpha, okay?

Unten noch ein Screenshot und die App die ich ins LongPress-Menü gelegt habe. Die App wird nur auf einem System mit den oben gezeigten Änderungen funktionieren. Wie im Video zu sehen hat sie noch ein-zwei Probleme, z.B. hatte ich bis jetzt keine Lust das k9-Problem zu lösen, und die App wird einen FC erzeugen.



Thanks to Google (for Android), ruqqq (who had the original idea), team Cyanogen (for making Android even more awesome), HCDRJacob and the other guys over at Modaco who make CM available for the Blade, and nicandris (for his shellscript). You rock :)


TODO:
-better screenshots
-settings inside the cm-settings?
 

Anhänge

  • device.png
    device.png
    54,5 KB · Aufrufe: 2.173
Zuletzt bearbeitet:
  • Danke
Reaktionen: salsaholic und Thektorium
erstmal danke für die app :)

hab die ActivityThread.java und dimens.xml so bearbeitet wie du beschrieben hast.

dannach einmal:

Code:
 . build/envsetup.sh && brunch blade
ausgeführt.

ins fertige ROM dannach noch die CMTaskSwitcher.apk reingepackt.


a) bei mir werden unter data/data/ keine Screens angelegt (Ordner existiert nicht mal :/)

b) wenn ich die apk "öffne" zeigt es nichts an (da warsch. keine screens vorhanden sind)


iwas mache ich falsch... bin leider nicht so gut mit eclipse vertraut.

Edit: nach dem make im android/system/ folder muss ich ja wohl diesen code nochmal ausführen oder?

lg
Thek
 
Zuletzt bearbeitet:
Ich bin mir noch nicht sicher ob die Sache mit der dimens.xml funktioniert, im ersten Code hatte ich das nicht drin.
Ersetz doch mal (ganz oben in dem einen Abschnitt)
private int mBigThumbnailWidth = -1;
private int mBigThumbnailHeight = -1;

mit
private int mBigThumbnailWidth = 400;
private int mBigThumbnailHeight = 240;
damit die Sache mit der dimens.xml nicht ausgeführt wird.

Ansonsten:
a) bei mir werden unter data/data/ keine Screens angelegt (Ordner existiert nicht mal :/)

Der Ordner data/data muss existieren, ist ein Systembestandteil. Brauchst einen File-Explorer mit Rootrechten, wahrscheinlich. Bei mir tut es EStrongs.
Ansonsten hilft "adb logcat" in der shell, das Gerät muss natürlich angeschlossen sein und die Entwicklungssachen aktiviert. Du benutzt nicht zufällig IRC?
 

Anhänge

  • CMTaskSwitcher.apk
    16,6 KB · Aufrufe: 268
Zuletzt bearbeitet:
werd ich sofort mal probieren :)

und ist der /data/data/ ordner jz der auf der SD-Card oder der im System?
weil ich hab nämlich beide ordner:
/data
udn
/sdcard/data

aber der unterordner data ist bei keinen der beiden existent... :/

edit;

aha GEFUNDEN!!! adb shell und dann mittels cd. in den Ordner... ;) naja OI Dateimanager funtzt also mal nicht :D

im ordner com.facebook.katana (z.B.)

hab ich auch das thumbnail :)


klar hab ich IRC :p

lg
Thek
 
Zuletzt bearbeitet:
Dann komm mal in #zteblade in freenode.
hab aber nicht mehr lange Zeit heute.

Hast du jetzt nur den Ordner gefunden, oder wurden auch die Bilder gespeichert?
Wenn die Bilder nicht gemacht wurden:
Alle veränderten Dateien löschen.
repo sync
schauen das die Datein wieder da sind
cd frameworks/base
git fetch Gerrit Code Review refs/changes/56/5356/4 && git cherry-pick FETCH_HEAD

:)
Gerrit Code Review
 
Neue App
 

Anhänge

  • CMTaskSwitcher.apk
    16,6 KB · Aufrufe: 264
  • Danke
Reaktionen: Thektorium
neue App funktioniert ..

ab gehts mit verbessern und auf in den Cynanogen Sourcecode damit :)

wäre doch gelacht wenn wir das nicht schaffen ;P


aja btw.: die Long-Press-Home activity ist die nicht Launcher abhängig..?

Edit: Nein ist sie nicht: settings=>cynanogenmod=>input-settings=> Longpress Home=> Choose another app..

ansonsten wäre mein neuer Build fertig (Sourcecode + app) wird gerade geupped..

lg
Thek
 
Zuletzt bearbeitet:
Mh da frage ich mich doch, kann MetaMorph java Klassen patchen?
 
  • Danke
Reaktionen: FelixL
Hrhr :D
Gute Frage. Das ist ja alles in der framework.apk versteckt. Ich schreib den mal an.
 
hmpf hab die beiden Methoden bei mir ins framework kopiert aber da werden keine pics erstellt >_>
 
Auch die dimens.xml verändert? Was sagt logcat?

Ansonsten geht auch das (mindestens bei CM7-Source)
cd frameworks/base
git fetch Gerrit Code Review refs/changes/56/5356/4 && git cherry-pick FETCH_HEAD
 
logcat sagt nix, die größe hatte ich hard gecoded.
Da wurden einfach keine Bilder erstellt, auch nicht die fehlenden ../files ordner.
Flash jetzt grade cm7^^ da müsste es dann ja hinhaun ;)
 
Tut sich hier noch was?
Hab mir selbst mein CM7 ROM mit diesem Patch gebaut und bin begeistert davon.
Warum steht im commit DO NOT MERGE? Klar bei einigen wenigen Programmen bzw. Spielen bekommt man nur ein schwarzes Bild, aber sonnst funktionierts schon sehr gut.

Ich hätte sogar eine Idee wie man das mit den schwarzen Screenshots ändern könnte.
Soweit ich gesehen habe haben die schwarzen png Dateien immer die selbe MD5 Summe (hab es kurz mit Angry Birds und Angry Birds Rio probiert).
3f026ec7eb0ec5d6e1eb2f6e88cfcd8a applicationThumbnail.png
Anhand dieser könnte man erkennen das es ein schwarzes Bild ist und statt dessen das App Icon ohne den Screenshot einblenden.

Ausserdem würde ich dem Titel (Appnamen) einen schwarzen Hintergrund verpassen, bei einem hellen Hintergrundbild kann man den nämlich schlecht lesen.
 
Vielen Dank ;)
Hatte leider gerade die letzte Woche wenig Zeit, war mit der Uni eine Woche unterwegs.

Wegen den schwarzen Screenshots: es gibt hoffentlich eine Möglichkeit den Framebuffer abzufangen, das wäre dann ein echter Screenshot. Ohne schwarze Stellen. Aber da muss ich nochmal suchen, es kann sein das dann jede App root bräuchte, das wäre nicht so gut ;). Das Auslassen von schwarzen Screenshots wäre für mich nur das letzte Mittel.

Wenn du eine einfache Möglichkeit für eine App findest von sich selbst ohne weitere Rechte einen Screenshot zu machen, einfach weitergeben ;)

Wegen dem "DO NOT MERGE": Erst mal müsste noch eine Einstellung eingebaut werden damit man das deaktivieren kann (wenn man es nicht will oder das Gerät zu langsam würde), oder/und eine für die Auflösung der Screenshots. Ich werde mich sobald ich ne ruhige Minute habe dransetzen :)
 
wow, ich bin wirklich begeistert. Ich habe seit gestern die Anwendung als meinen Standard "RecentApps"-Dialog und es funktioniert wirklich sehr gut. Ich habe es mir erlaubt, ein wenig an der TasksSwitcher rumzuspielen (siehe Anhang) :D
Leider hast du die letzte Änderung (mit Icons und Text) noch nicht veröffentlicht, aber egal.
Arbeitest du momentan noch daran?
 

Anhänge

  • taskswitcher.png
    taskswitcher.png
    38 KB · Aufrufe: 274
Sehr gut finde ich etwas übertrieben, aber danke ;)
Benutz mal von Google "Androidify", das schmiert nach dem Beenden ab, wegen meinem Code. Und dann ist da die Sache das die Methode die ich zum Screenshot erstellen benutze SurfaceViews nicht ablichten kann. Theoretisch arbeite ich noch dran, bräuchte aber vor allem einen besseren Screenshot-Code. Die veränderte App kann ich gerne auch noch mal online stellen, vielleicht lege ich mir auch mal ein github zu ;)

wegen dem verbesserten Screenshot-Code: Das Einzige was Sinn macht wäre das Abfangen des Framebuffers. Das muss dann aber so gemacht sein das sichergestellt ist das nur die App selbst den Mechanismus benutzen kann, und niemand sobald die Banking-App läuft alle halbe Sekunde einen macht um den PIN rauszufinden ;)

So was ähnliches wie das hier:
android-fb2png - Capture framebuffer and save it in png format - Google Project Hosting
 
ja, Androidify stürzt ab und aTrackDog wird komischerweise kleiner dargestellt als die anderen... :blink:
Trotzdem will ich nicht mehr ohne den TaskSwitcher leben :D
Und zu fb2png: Die jeweilige Anwendung müsste doch Root-Rechte besitzen, um einen Screenshot zu erstellen, oder? Wäre doch super. Außerdem wären PNGs eh besser für den Speicher :)
 
Zuletzt bearbeitet:

Ähnliche Themen

DerOhneNick
Antworten
3
Aufrufe
1.081
DerOhneNick
DerOhneNick
J
Antworten
1
Aufrufe
1.120
mblaster4711
mblaster4711
Foh
Antworten
8
Aufrufe
1.701
Foh
Foh
Zurück
Oben Unten