Aix extension for file

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