Search code examples
androidandroid-fragmentsscreenshotscreen-orientation

Taking Screenshot programmatically after orientation change


I'm developing an app which takes a screenshot of a layout, saves it to PDF and then sends it to be printed via Google Cloud Print. Everything works fine but I have a problem regarding the orientation of the screenshot.

For the host fragment I have made both a landscape and portrait layout. The screenshot has to be always on portrait so the idea is to programmaticaly rotate the screen to portrait, take the screenshot and then set the screen orientation back to Sensor. I change the configuration with this method:

public void lockScreenOrientation(String orientation) {     
    if (orientation.contains("portrait")) {
        mActivity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);            
    } else if (orientation.contains("auto")) {
        mActivity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR);
    }
}

The screenshot and PDF Creation occur in an AsyncTask which is triggered by a confirmation dialog in the Host Fragment

if (print) {            
        new AsyncPDF(mActivity).execute(field_client_name_txt, "birthday");
        prefs.edit().putBoolean("Print", false).commit();
}

When the orientation is in Portrait, everything works great! The problem happens when the orientation has to be rotated to portrait for the screenshot and the printing to take place. The screenshot captures the image of the landscape layout instead of the portrait one and the final pdf is messy and cropped!

For reference here is my Asynctask:

public class AsyncPDF extends AsyncTask<String, Void, Void> {

Activity mActivity;
Context mContext;

private static final int REQUEST_CODE = 4;

private static String SDCARD = Environment.getExternalStorageDirectory().getPath();
private static String DIRECTORY = SDCARD + "/Amorino";
private static String DIR_CACHE = DIRECTORY + "/Cache/";
private static String DIR_BIRTHDAY_ORDERS = DIRECTORY + "/Orders/Birthday/";
private static String DIR_WEDDING_ORDERS = DIRECTORY + "/Orders/Wedding/";
private static String DIR_OTHER_ORDERS = DIRECTORY + "/Orders/Other/";
private static String DIR_PRODUCTION = DIRECTORY + "/Production/";

String pdfName, pdfType = "";
Utils mUtility;

public AsyncPDF(Activity activity) {
    this.mActivity = activity;
    this.mContext = activity;
}

@Override
protected void onPreExecute() {
    mUtility = new Utils(mContext);
    Toast.makeText(mActivity, "Αποθήκευση αρχείου...", Toast.LENGTH_SHORT)
            .show();
        screenCapture();
};

@Override
protected Void doInBackground(String... params) {

    pdfName = params[0];
    pdfType = params[1];

    createPdf();

    return null;
}

@Override
protected void onProgressUpdate(Void... values) {
    super.onProgressUpdate(values);

}

@Override
protected void onPostExecute(Void result) {
    super.onPostExecute(result);
    clearCacheFolder(); 
    Toast.makeText(mActivity, "Το αρχείο αποθηκέυτηκε επιτυχώς!",
            Toast.LENGTH_SHORT).show();

}

private void screenCapture() {

    try {

        ScrollView mLayoutRoot = (ScrollView) mActivity
                .findViewById(R.id.print_screen_layout);

        mLayoutRoot.setDrawingCacheEnabled(true);
        mLayoutRoot.buildDrawingCache();

        Bitmap mBitmap = mLayoutRoot.getDrawingCache();
        File file, f = null;
        if (android.os.Environment.getExternalStorageState().equals(
                android.os.Environment.MEDIA_MOUNTED)) {
            file = new File(DIR_CACHE);
            if (!file.exists()) {
                file.mkdirs();
            }

            f = new File(file.getAbsolutePath() + File.separator
                    + "temp_layout" + ".png");
        }
        FileOutputStream ostream = new FileOutputStream(f);
        mBitmap.compress(CompressFormat.PNG, 10, ostream);
        ostream.close();

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

}

private void createPdf() {

    Document document = new Document();
    Image header_img, main_img;
    InputStream inputStream_header = null;
    String current_directory = "";

    try {

        AssetManager mngr = mContext.getAssets();

        if (pdfType.contains("birthday")) {
            inputStream_header = mngr.open("header_birthday.png");
            current_directory = DIR_BIRTHDAY_ORDERS;
        } else if (pdfType.contains("wedding")) {
            inputStream_header = mngr.open("header_wedding.png");
            current_directory = DIR_WEDDING_ORDERS;
        } else if (pdfType.contains("other")) {
            inputStream_header = mngr.open("header_order.png");
            current_directory = DIR_OTHER_ORDERS;
        } else if (pdfType.contains("production")) {
            inputStream_header = mngr.open("header_order.png");
            current_directory = DIR_PRODUCTION;
        }

        File file = new File(current_directory);
        if (!file.exists()) {
            file.mkdirs();
        }

        PdfWriter.getInstance(document, new FileOutputStream(
                current_directory + "/" + pdfName + ".pdf"));
        document.open();

        Bitmap bmp = BitmapFactory.decodeStream(inputStream_header);
        ByteArrayOutputStream stream_header = new ByteArrayOutputStream();
        bmp.compress(Bitmap.CompressFormat.PNG, 100, stream_header);
        header_img = Image.getInstance(stream_header.toByteArray());
        header_img.scalePercent(24f);
        header_img.setAbsolutePosition(-1f, 750f);
        document.add(header_img);

        Paragraph paragraph = new Paragraph(
                header_img.getScaledHeight() + 50f);
        document.add(paragraph);

        main_img = Image.getInstance(DIR_CACHE
                + "/temp_layout.png");

        main_img.scaleToFit(520f, 2000f);
        main_img.setAlignment(Image.ALIGN_CENTER);

        Log.d("Original Width: ", String.valueOf(main_img.getWidth()));
        Log.d("Original Height ", String.valueOf(main_img.getHeight()));
        Log.d("Scaled Width: ", String.valueOf(main_img.getScaledWidth()));
        Log.d("Scaled Height ", String.valueOf(main_img.getScaledHeight()));

        document.add(main_img);

        document.close();
        File f = new File(current_directory + "/" + pdfName + ".pdf");
        printPdf(f, pdfName);
    } catch (Exception e) {
        e.printStackTrace();
    }

}

private void printPdf(File f, String name) {
    Uri docUri = Uri.fromFile(f);
    Intent printIntent = new Intent(mContext, PrintDialogActivity.class);
    printIntent.setDataAndType(docUri, "application/pdf");
    printIntent.putExtra("title", name + ".pdf");
    mActivity.startActivityForResult(printIntent, REQUEST_CODE);
}

private void clearCacheFolder() {
    File dir = new File(DIR_CACHE);
    if (dir.isDirectory()) {
        String[] children = dir.list();
        for (int i = 0; i < children.length; i++) {
            new File(dir, children[i]).delete();
        }
    }
}

}

I believe that the glitch happens on the mActivity parameter. I think it passes the fragment with the original landscape layout instead of the portrait one. I tried to use a "print" flag stored in a SharedPreference and then execute the AsyncTask from the onResume method with no luck. Still the screen shot grabs the landscape layout. Any ideas?

After which point in the fragment lifecycle should I execute the AsyncTask in order to get the proper (portrait) layout?

P.S. First question asked!!


Solution

  • I finally figured it out about a week ago, and here's the solution!

    The issue resolved with a hint from this answer. I added a GlobalLayoutListener, in order to initiate the AsyncPDF class after the new layout was settled, and the problem was almost solved. Later on I had to deal with a new problem...!

    The PDF now had the right layout printed on it, but some of the EditText fields came out empty!! After a lot frustration, head scratching and trial and error tests, I figured out what the problem was. The GlobalLayoutListener was working as intended, but it seems that it was initiating the printing function a little too soon, so the fields didn't had the time to be properly drawn/filled.

    To cope with that, I added a minimal delay (100ms) using the Handler class and after that everything worked as they were supposed to be! Here's the code of the print method:

    public void print(String orientation) {
    
        print = prefs.getBoolean("print", false);
    
        if (print)
            if (orientation.contains("portrait")) {
                new AsyncPDF(mActivity, v).execute(field_client_name_txt,
                        "birthday");
            } else {
                ViewTreeObserver observer = v.getViewTreeObserver();
                observer.addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
                    @Override
                    public void onGlobalLayout() {
                        /* Adds a minimal wait time for the view to settle */
                        final Handler handler = new Handler();
                        handler.postDelayed(new Runnable() {
                            @Override
                            public void run() {
                                new AsyncPDF(mActivity, v).execute(
                                        field_client_name_txt, "birthday");
                            }
                        }, 100);
    
                        v.getViewTreeObserver().removeOnGlobalLayoutListener(
                                this);
                    }
                });
            }
        /* Reset the print flag to false */
        prefs.edit().putBoolean("print", false).commit();
    }
    

    The method initiates from a print button in the ActionBar, and in case we are already in Portrait, it starts the AsyncPDF class imm. In case of landscape orientation, it waits for the portrait layout to settle, then adds a 100ms delay and then it initiates the AsyncTask.

    public void onClick(DialogInterface dialog, int id) {
         prefs.edit().putBoolean("print", true).commit();
         if (mUtility.getScreenOrientation() == 1)
              print("portrait");
         else
              mUtility.lockScreenOrientation("portrait");
         }
    });
    

    The print(String orientation) method, is placed inside the onActivityCreated() method, so when the configuration change happens, it checks if the print flag was triggered and acts accordingly.