Asssssiwant to make an extension for kodular
/*
* Extension generated with AIX Generator
* https://aixgenerator.xyz/
* Support: aixgeneratorpro@gmail.com
* Last modification: 08/01/2026, 00:42:12
*/
package com.sajjad.mediascanner;
import android.Manifest;
import android.content.ContentUris;
import android.content.Context;
import android.database.Cursor;
import android.net.Uri;
import android.os.Build;
import android.provider.MediaStore;
import androidx.core.content.ContextCompat;
import com.google.appinventor.components.annotations.DesignerComponent;
import com.google.appinventor.components.annotations.SimpleEvent;
import com.google.appinventor.components.annotations.SimpleFunction;
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.Form;
import org.json.JSONArray;
import org.json.JSONObject;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
@DesignerComponent(
version = 1,
description = "Extensão para escanear arquivos de mídia (audios, videos) e arquivos no armazenamento e retornar os resultados como JSON.",
category = ComponentCategory.EXTENSION,
nonVisible = true,
iconName = "")
public class MediaScanner extends AndroidNonvisibleComponent {
private final Context context;
private final Form form;
private final ExecutorService executor;
private String lastScanResult = "[]";
public MediaScanner(ComponentContainer container) {
super(container.$form());
this.context = container.$context();
this.form = container.$form();
this.executor = Executors.newSingleThreadExecutor();
}
@SimpleFunction(description = "Verifica se a permissão de leitura de armazenamento está concedida.")
public boolean HasPermission() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
// Android 13+ separa READ_MEDIA_AUDIO/VIDEO/IMAGES
boolean aud = ContextCompat.checkSelfPermission(context, Manifest.permission.READ_MEDIA_AUDIO) == android.content.pm.PackageManager.PERMISSION_GRANTED;
boolean vid = ContextCompat.checkSelfPermission(context, Manifest.permission.READ_MEDIA_VIDEO) == android.content.pm.PackageManager.PERMISSION_GRANTED;
boolean img = ContextCompat.checkSelfPermission(context, Manifest.permission.READ_MEDIA_IMAGES) == android.content.pm.PackageManager.PERMISSION_GRANTED;
return aud || vid || img;
} else {
return ContextCompat.checkSelfPermission(context, Manifest.permission.READ_EXTERNAL_STORAGE) == android.content.pm.PackageManager.PERMISSION_GRANTED;
}
}
@SimpleFunction(description = "Inicia o scan para áudios, vídeos e arquivos. Dispara OnScanCompleted(json) quando terminar ou OnScanFailed(message) se houver erro ou sem permissão.")
public void ScanAll() {
executor.submit(() -> {
try {
if (!HasPermission()) {
final String msg = "Permission not granted. Please add READ_EXTERNAL_STORAGE (or READ_MEDIA_* on Android 13+) in the app and grant it at runtime.";
form.runOnUiThread(() -> OnScanFailed(msg));
return;
}
JSONArray combined = new JSONArray();
JSONArray audios = scanAudio();
for (int i = 0; i < audios.length(); i++) combined.put(audios.get(i));
JSONArray videos = scanVideo();
for (int i = 0; i < videos.length(); i++) combined.put(videos.get(i));
JSONArray files = scanFiles();
for (int i = 0; i < files.length(); i++) combined.put(files.get(i));
lastScanResult = combined.toString();
final String result = lastScanResult;
form.runOnUiThread(() -> OnScanCompleted(result));
} catch (final Exception e) {
final String msg = "Scan failed: " + e.getMessage();
form.runOnUiThread(() -> OnScanFailed(msg));
}
});
}
@SimpleFunction(description = "Retorna o último resultado do scan como JSON string.")
public String GetLastScanResult() {
return lastScanResult;
}
// Evento: OnScanCompleted
@SimpleEvent(description = "Disparado quando o scan for concluído. Retorna um JSON com uma lista de objetos de arquivos.")
public void OnScanCompleted(String json) {
EventDispatcher.dispatchEvent(this, "OnScanCompleted", json);
}
// Evento: OnScanFailed
@SimpleEvent(description = "Disparado quando o scan falhar ou permissão não concedida. Retorna mensagem de erro.")
public void OnScanFailed(String message) {
EventDispatcher.dispatchEvent(this, "OnScanFailed", message);
}
// --- Helpers para escanear ---
private JSONArray scanAudio() {
JSONArray array = new JSONArray();
Uri uri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
String[] projection = new String[]{
MediaStore.Audio.Media._ID,
MediaStore.Audio.Media.TITLE,
MediaStore.Audio.Media.DISPLAY_NAME,
MediaStore.Audio.Media.SIZE,
MediaStore.Audio.Media.MIME_TYPE,
MediaStore.Audio.Media.DURATION,
MediaStore.Audio.Media.DATA,
MediaStore.Audio.Media.ARTIST,
MediaStore.Audio.Media.ALBUM
};
Cursor cursor = null;
try {
cursor = context.getContentResolver().query(uri, projection, null, null, null);
if (cursor == null) return array;
int idCol = cursor.getColumnIndexOrThrow(MediaStore.Audio.Media._ID);
int titleCol = cursor.getColumnIndex(MediaStore.Audio.Media.TITLE);
int nameCol = cursor.getColumnIndex(MediaStore.Audio.Media.DISPLAY_NAME);
int sizeCol = cursor.getColumnIndex(MediaStore.Audio.Media.SIZE);
int mimeCol = cursor.getColumnIndex(MediaStore.Audio.Media.MIME_TYPE);
int durCol = cursor.getColumnIndex(MediaStore.Audio.Media.DURATION);
int dataCol = cursor.getColumnIndex(MediaStore.Audio.Media.DATA);
int artistCol = cursor.getColumnIndex(MediaStore.Audio.Media.ARTIST);
int albumCol = cursor.getColumnIndex(MediaStore.Audio.Media.ALBUM);
while (cursor.moveToNext()) {
long id = cursor.getLong(idCol);
JSONObject obj = new JSONObject();
try {
obj.put("type", "audio");
obj.put("id", id);
obj.put("title", safeString(cursor, titleCol));
obj.put("displayName", safeString(cursor, nameCol));
obj.put("size", safeLong(cursor, sizeCol));
obj.put("mimeType", safeString(cursor, mimeCol));
obj.put("duration", safeLong(cursor, durCol));
obj.put("path", safeString(cursor, dataCol));
obj.put("artist", safeString(cursor, artistCol));
obj.put("album", safeString(cursor, albumCol));
Uri contentUri = ContentUris.withAppendedId(uri, id);
obj.put("uri", contentUri.toString());
} catch (Exception ignored) {}
array.put(obj);
}
} catch (Exception e) {
// ignore, return what we have
} finally {
if (cursor != null) cursor.close();
}
return array;
}
private JSONArray scanVideo() {
JSONArray array = new JSONArray();
Uri uri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;
String[] projection = new String[]{
MediaStore.Video.Media._ID,
MediaStore.Video.Media.TITLE,
MediaStore.Video.Media.DISPLAY_NAME,
MediaStore.Video.Media.SIZE,
MediaStore.Video.Media.MIME_TYPE,
MediaStore.Video.Media.DURATION,
MediaStore.Video.Media.DATA
};
Cursor cursor = null;
try {
cursor = context.getContentResolver().query(uri, projection, null, null, null);
if (cursor == null) return array;
int idCol = cursor.getColumnIndexOrThrow(MediaStore.Video.Media._ID);
int titleCol = cursor.getColumnIndex(MediaStore.Video.Media.TITLE);
int nameCol = cursor.getColumnIndex(MediaStore.Video.Media.DISPLAY_NAME);
int sizeCol = cursor.getColumnIndex(MediaStore.Video.Media.SIZE);
int mimeCol = cursor.getColumnIndex(MediaStore.Video.Media.MIME_TYPE);
int durCol = cursor.getColumnIndex(MediaStore.Video.Media.DURATION);
int dataCol = cursor.getColumnIndex(MediaStore.Video.Media.DATA);
while (cursor.moveToNext()) {
long id = cursor.getLong(idCol);
JSONObject obj = new JSONObject();
try {
obj.put("type", "video");
obj.put("id", id);
obj.put("title", safeString(cursor, titleCol));
obj.put("displayName", safeString(cursor, nameCol));
obj.put("size", safeLong(cursor, sizeCol));
obj.put("mimeType", safeString(cursor, mimeCol));
obj.put("duration", safeLong(cursor, durCol));
obj.put("path", safeString(cursor, dataCol));
Uri contentUri = ContentUris.withAppendedId(uri, id);
obj.put("uri", contentUri.toString());
} catch (Exception ignored) {}
array.put(obj);
}
} catch (Exception e) {
// ignore
} finally {
if (cursor != null) cursor.close();
}
return array;
}
private JSONArray scanFiles() {
JSONArray array = new JSONArray();
Uri uri = MediaStore.Files.getContentUri("external");
String[] projection = new String[]{
MediaStore.Files.FileColumns._ID,
MediaStore.Files.FileColumns.DISPLAY_NAME,
MediaStore.Files.FileColumns.MIME_TYPE,
MediaStore.Files.FileColumns.SIZE,
MediaStore.Files.FileColumns.DATE_MODIFIED,
MediaStore.Files.FileColumns.DATA
};
Cursor cursor = null;
try {
String selection = null; // we could filter by size or mime type if desired
cursor = context.getContentResolver().query(uri, projection, selection, null, null);
if (cursor == null) return array;
int idCol = cursor.getColumnIndexOrThrow(MediaStore.Files.FileColumns._ID);
int nameCol = cursor.getColumnIndex(MediaStore.Files.FileColumns.DISPLAY_NAME);
int mimeCol = cursor.getColumnIndex(MediaStore.Files.FileColumns.MIME_TYPE);
int sizeCol = cursor.getColumnIndex(MediaStore.Files.FileColumns.SIZE);
int dateCol = cursor.getColumnIndex(MediaStore.Files.FileColumns.DATE_MODIFIED);
int dataCol = cursor.getColumnIndex(MediaStore.Files.FileColumns.DATA);
while (cursor.moveToNext()) {
long id = cursor.getLong(idCol);
JSONObject obj = new JSONObject();
try {
obj.put("type", "file");
obj.put("id", id);
obj.put("displayName", safeString(cursor, nameCol));
obj.put("mimeType", safeString(cursor, mimeCol));
obj.put("size", safeLong(cursor, sizeCol));
obj.put("dateModified", safeLong(cursor, dateCol));
obj.put("path", safeString(cursor, dataCol));
Uri contentUri = ContentUris.withAppendedId(uri, id);
obj.put("uri", contentUri.toString());
} catch (Exception ignored) {}
array.put(obj);
}
} catch (Exception e) {
// ignore
} finally {
if (cursor != null) cursor.close();
}
return array;
}
// Util helpers
private String safeString(Cursor c, int idx) {
try {
if (idx < 0) return "";
String s = c.getString(idx);
return s == null ? "" : s;
} catch (Exception e) {
return "";
}
}
private long safeLong(Cursor c, int idx) {
try {
if (idx < 0) return 0L;
return c.getLong(idx);
} catch (Exception e) {
return 0L;
}
}
}
Hey @Sajjad_ali_Sajjad_ali welcome to Kodular community
What’s wrong with your code, can you explain more? And what are you trying to create, though I noticed it’s related file accessing by scanning the device but you didn’t mention why you posted it here, and where we should come in