[F/OS] 📸 Simple Screenshot Extension!

:warning: Wait a minute!

If you want to screenshot only components, use my new [F/OS] 🖼️ ViewUtil: Convert a component into an image! extension.

Also, this extension is not capable of screenshooting surface views (e.g. realtime camera view). The view will be black if you screenshot it.

:computer: Introduction

A non-visible extension to take screenshots.

:package: Package name: com.gordonlu.screenshot.aix

:clock10: Version: 2

:date: Release date: 2022-03-23T10:00:00Z

I hae looked into Taifun's source code and made a few changes:

  • now, you can save the screenshot anywhere you want, even in the /storage/emulated/0/Download/ folder. However, the block will now require an absolute path.

  • I changed the Bitmap.CompressFormat thing, so now, you can also use PNG screenshots.

  • added a new event to handle errors.

  • added blocks for the ASD and detecting whether the write permission is granted. (note: you will need the read permission if you want to save the screenshot in somewhere else, but since this is not mandatory, I will not add a block for this.)

  • now, you have an option to set the quality of the screenshot (Taifun's extension is 100).

Find his extension here.

@Taifun It's OK if you want to apply the source code of this extension into yours.

:open_book: Documentation

Event blocks

Failed

image

This event is fired when the extension has encountered an error in taking the screenshot. Possible reasons are: wrong path, wrong quality value, etc.

Parameters: error = text

SavedScreenshot

image

This event is fired when the extension has taken a screenshot in the given path.

Parameters: path = text

Method blocks

ApplicationSpecificDirectory

image

Returns an absolute path of the application specific directory of this application.

Returns: text

IsWritePermissionGranted

image

Checks whether the application has the permission to write external storage. This permission is mandatory if you use this extension.

Returns: boolean

TakeScreenshot

image

Takes a screenshot of the current app. The compressFormat parameter shall be either JPEG or PNG, else by default, it will be JPEG. The quality parameter is a value between 0 and 100. The path parameter shall be an absolute path.

Parameters: quality = number (int), compressFormat = text, path = text

Property blocks

Jpeg

image

A compress format block.

Returns: "JPEG"

Png

image

A compress format block.

Returns: "PNG"

WebP

image

:warning: WARNING! This is deprecated in Android 11. For those of you who still want to use it, here you go.

A compress format block.

Returns: "WEBP"

:inbox_tray: Downloads

AIX:
com.gordonlu.screenshot.aix (8.7 KB)

AIA:
Screenshot_1.aia (9.9 KB)

:lock: Open Source

Since Taifun provided his extension open-sourcely in his website, I am also going to make this open source. (because even if I don't, someone would be able to guess the source code because it is available in Stack Overflow.)

package com.gordonlu.screenshot;

import android.app.Activity;
import android.content.Context;
import com.google.appinventor.components.annotations.*;
import com.google.appinventor.components.common.ComponentCategory;
import com.google.appinventor.components.runtime.AndroidNonvisibleComponent;
import com.google.appinventor.components.runtime.ComponentContainer;
import com.google.appinventor.components.runtime.EventDispatcher;

import com.google.appinventor.components.runtime.AndroidViewComponent;
import android.view.View;
import android.graphics.Bitmap;
import java.io.FileOutputStream;
import android.graphics.Bitmap.CompressFormat;
import android.content.pm.PackageManager;

import android.os.Environment;
import java.io.File;

import java.lang.Enum;

@DesignerComponent(
        version = 2,
        description = "A non-visible extension to take screenshots.",
        category = ComponentCategory.EXTENSION,
        nonVisible = true,
        iconName = "https://docs.google.com/drawings/d/e/2PACX-1vQCI87PHLBF0jb8QWyYmIRQSjjNW3EFXf-qpsWCvBYkUQ9vEgPAB8SpxcMpblxNpbIYrjCjLrRLIU2c/pub?w=16&h=16")

@SimpleObject(external = true)
//Libraries
@UsesLibraries(libraries = "")
//Permissions
@UsesPermissions(permissionNames = "")

public class Screenshot extends AndroidNonvisibleComponent {

    //Activity and Context
    private Context context;
    private Activity activity;

    public Screenshot(ComponentContainer container){
        super(container.$form());
        this.activity = container.$context();
        this.context = container.$context();
    }

    @SimpleFunction(description = "Takes a screenshot of the current app, with the given quality, compressFormat and saves the screenshot as the given path.")
    public void TakeScreenshot(int quality, String compressFormat, String path) {
        try {
            String mPath = path;
            CompressFormat cp = CompressFormat.JPEG;
            if (compressFormat == "PNG") {
            cp = CompressFormat.PNG;
            } else if (compressFormat == "WEBP") {
            cp = CompressFormat.WEBP;
            }
            View v1 = activity.getWindow().getDecorView().getRootView();
            v1.setDrawingCacheEnabled(true);
            Bitmap bitmap = Bitmap.createBitmap(v1.getDrawingCache());
            v1.setDrawingCacheEnabled(false);

            File imageFile = new File(mPath);
            FileOutputStream outputStream = new FileOutputStream(imageFile);
            bitmap.compress(cp, quality, new FileOutputStream(path));
            SavedScreenshot(imageFile.toString());
            outputStream.flush();
            outputStream.close();
        }
        catch(Throwable e) {
          Failed(e.getMessage());  
}
    }

    @SimpleProperty(description = "A compress format block.")
    public String Jpeg() {
        return "JPEG";
    }
    @SimpleProperty(description = "A compress format block.")
    public String Png() {
        return "PNG";
    }
    @SimpleProperty(description = "A compress format block. This block will only work for devices smaller than Android 10.")
    public String Webp() {
        return "WEBP";
    }
    @SimpleEvent(description = "This event is fired when the extension has failed to take the screenshot.")
    public void Failed (String error) {
        EventDispatcher.dispatchEvent(this, "Failed", error);
    }
    @SimpleEvent(description = "This event is fired when the extension has taken the screenshot in the given path.")
    public void SavedScreenshot (String path) {
        EventDispatcher.dispatchEvent(this, "SavedScreenshot", path);
    }
    @SimpleFunction(description = "Returns whether the application has the write permission. This is mandatory if you use this extension.")
    public boolean IsWritePermissionGranted() {
        String permission = android.Manifest.permission.WRITE_EXTERNAL_STORAGE;
        int res = context.checkCallingOrSelfPermission(permission);
        return (res == PackageManager.PERMISSION_GRANTED);            
    }
    @SimpleFunction(description = "Returns an absolute path of the application specific directory of this app.")
    public String ApplicationSpecificDirectory() {
        String pkgName = context.getPackageName();
        return "/storage/emulated/0/Android/data/" + pkgName + "/files/";
    }
}

:heavy_check_mark: Tests

All of these tests are successful in saving the image in storage/emulated/0/Download and the ASD (application-specific directory).

:hammer_and_wrench: Companion:

  • Android 11 API 30, Xiaomi 11 5G NE Lite.

:hammer_and_wrench: APK:

  • Android 8.1 API 27, Google Pixel XL emulator.

  • Android 9 API 28, Google Pixel 5 emulator.

  • Android 11 API 30, Xiaomi 11 5G NE Lite.

  • Android 11 API 30, Google Pixel 2 emulator.

:hammer_and_wrench: Requirements:

  • In Screen1 properties, set DefaultFileScope to Legacy.

  • You must have the write external storage permission android.permission.WRITE_EXTERNAL_STORAGE (a.k.a. Files/photos and media).

  • For details on how to read external storage, please search the forum.

If you have any bugs, reply here.


:+1: Rate my extension! :-1:

  • Good extension!
  • Bad extension.
0 voters

Made with Niotron IDE.

Kindly :email: PM me if you have any questions! Also, if you like my extension, please :heart: like it! It takes some effort for me to make it...

Votes and likes tell me the general user feedback of my extension. If you read this extension, please take 20 seconds to drop by and give a like!

If you have any features that you want to add and you know the code, PM me or directly reply below using the image button.

By downloading my extension, you agree the terms and conditions in my website


Gordon Lu

:speech_balloon: Message :earth_africa: Website :e-mail: E-mail

4 Likes

7 posts were merged into an existing topic: Any extensions and tools ideas please?

Version 2!

  • :bug: Bug fix for outputStream.

  • New blocks:

WebP

image

:warning: WARNING! This is deprecated in Android 11. For those of you who still want to use it, here you go.

A compress format block.

Returns: "WEBP"

EDIT: Added to here.

1 Like

I think there are more formats like WEBP LOSSLESS and WEBP LOSSY

Yes, there are. Unfortunately, my extension compiler does not recognize them in the Bitmap.CompressFormat class (I have seen other people struggling with this on Stack Overflow as well).

1 Like