Screenshot

Published on March 24, 2022

🏷️ Tags: #extensions

A non-visible extension to take screenshots. This extension cannot run in the background, nor can it take screenshots of surface views (for example, realtime camera view).

Made with Niotron IDE at https://ide.niotron.com.

Current version: 2 Package name: com.gordonlu.screenshot.aix

For more information, read this MIT App Inventor Community thread.

πŸ“– Documentation

Event blocks

Failed - 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 - Fired when the extension has taken a screenshot in the given path.

Parameters: path = text

Method blocks

ApplicationSpecificDirectory - Returns an absolute path of the application specific directory of this application. Returns: text

IsWritePermissionGranted - Checks whether the application has the permission to write external storage. This permission is mandatory if you use this extension. Returns: boolean

TakeScreenshot - 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 - A compress format block. Returns: "JPEG"

Png - A compress format block. Returns: "PNG"

Webp - A compress format block. Returns: "WEBP"

⚠️ WARNING! The Webp block only works on devices before Android 11.

Open Source

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/";

}

}


Downloads

βœ”οΈ Tests

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

πŸ› οΈ Companion:

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

πŸ› οΈ 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.

πŸ› οΈ Requirements:

  • In Screen1 properties, set DefaultFileScope to Legacy.

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

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

TERMS AND CONDITIONS

By downloading or sharing my extension, you agree to follow these terms and conditions.

  1. Have fun using the extension!

  2. This, along with the MIT App Inventor original post above, are the only authorized places to download this extension. As an organization, you are not allowed to host and advertize this extension on your website, document, tweet, blog, article or any software you made without my authorization. You are also reminded not to include a direct download link of this extension on your website without my permission. If you are interested in the actions listed above, please contact me via email. Do remember that if you violate term no. 2, AICODE has the right to flag your website, document, tweet, blog or article as a violation of copyright, or other legal actions will be taken. Please contact me if you find anyone do this.

However, you can copy the link of this webpage and share it to someone as a personal identity (not as an organization), or sharing it on these authorized forums: App Inventor, Kodular, Niotron, Appzard and Android Builder communities. The term organization includes schools, markets and app stores, companies and social groups. Thank you for your understanding.

Those organizations are authorized to advertize or host my extension:

  • Kodular admins

  • MIT App Inventor admins

  • Pura Vida Apps / Taifun Baer

  1. Selling this extension/product to anyone is not allowed unless authorized by AICODE.

  2. These terms and conditions are written and issued on December 20, 2021. AICODE has the total permission to edit these terms and conditions anytime.