Google Drive Extension | Secure | Database-like

GoogleDrive

An extension for MIT App Inventor 2.
Secure Google Drive Integration using Apps Script

:locked_with_key: A powerful and secure Google Drive extension built using Google Apps Script that works like a cloud database. No need for OAuth or Google console setup. Just deploy your script, set your API key and you’re ready to store, update, rename, delete, and manage files or folders!

:white_check_mark: Features:

  • Upload, edit, delete, rename, and read files from Google Drive
  • Read file contents directly from Kodular file picker URI
  • Obfuscated API key support with SecureApiKey

:puzzle_piece: Function Blocks

SetScriptUrl(url)

SetScriptUrl_Set_Property

  • Sets the URL of your deployed Google Apps Script.
  • Use this once to configure the base URL.

SetApiKey(key)

SetApiKey_Set_Property

  • Set your secret key to authenticate your API calls.
  • For production, use SecureApiKey instead.

SetFolderId(folderId)

SetFolderId_Set_Property

  • Set the Google Drive folder ID where files will be created.
  • Keep it reusable across multiple actions.

SecureApiKey(encodedKey, level)

SecureApiKey_Method

  • Protect your API key with layered Base64 encryption.
  • Call this instead of SetApiKey to deter hackers from reverse engineering your app.

CreateFile(name, filePath)

CreateFile_Method

  • Upload file content (from URI after file picker) to Google Drive.
  • Use this when saving notes, images, or any user-generated content.

EditFile(fileId, filePath)

EditFile_Method

  • Replace content of an existing file with new data from another file.
  • Ideal for updating saved documents or logs.

DeleteFile(fileId)

DeleteFile_Method

  • Soft-deletes (trashes) a file in Drive.
  • Recommended when offering delete options to users.

RenameFile(fileId, newName)

RenameFile_Method

  • Renames a file using Google Drive API.
  • Perfect for user-editable file management systems.

GetFileContent(fileId)

GetFileContent_Method

  • Downloads file content as string from Drive.
  • Can be used to preview text, config, or restore saved files.

GotResponse(response)

GotResponse_Event

  • Triggered whenever the extension receives a response from Apps Script.
  • Always parse this result to check status or output.

DownloadFile(fileId, saveAsFileName)

DownloadFile_Method

  • Downloads any file (PDF, image, APK, etc.) from Google Drive
  • Saves it to app-specific local storage on the device
  • Useful for letting users save backups, restore files, or fetch user-uploaded media

GetDirectDownloadLink(fileId)

GetDirectDownloadLink_Method

  • Generates a clean https://drive.google.com/uc?export=download&id=... link.
  • Use this for streaming files in a browser, video player, or direct download.

GetFiles()

GetFiles_Method

  • Fetches all files in the current folder.
  • Triggers GotFileList event with:
    • :memo: fileNames → list of file names
    • :package: fileSizes → file size in bytes
    • :link: fileUrls → download links
    • :date: lastUpdateDates → last modified timestamps
    • :id_button: fileIds → file IDs for further use

GotFileList_Event


:hammer_and_wrench: How to Use it with simple Five Step

  1. Go to Google Apps Script → New Project
  2. Copy the Apps Script and Set your API key in script
  3. :gear: Deploy the Script
  • Go to Deploy > Manage Deployments
  • Choose Web App
    • Execute as: Me
    • Who has access: Anyone (with API_KEY check)
  • Click Deploy
  • Copy the Web App URL
  1. In Kodular, use SetScriptUrl, SetFolderId, and SecureApiKey
  2. Start calling CreateFile, EditFile, etc.

:memo: Specifications


:package: Package:com.mahir.googledrive.aix (9.6 KB)
:mobile_phone: Minimum API Level: 7
:date: Updated On: 2025-07-05T18:00:00Z
:package: Version: 2
:laptop: Built & documented using: FAST-CLI

App Script

ADD YOUR OWN SECRET API KEY :key:

const API_KEY = "your-secret-key";

function doPost(e) {
  try {
    const data = JSON.parse(e.postData.contents);
    if (data.apiKey !== API_KEY) return output({ success: false, error: "Invalid API Key" });

    switch (data.action) {
      case "create": return createFile(data);
      case "edit": return editFile(data);
      case "delete": return deleteFile(data);
      case "rename": return renameFile(data);
      case "get": return getFile(data);
      case "download": return downloadFile(data);
      case "getlink": return getDirectLink(data);         
      case "list": return getFiles(data);                 
      default: return output({ success: false, error: "Unknown action" });
    }
  } catch (e) {
    return output({ success: false, error: e.toString() });
  }
}

function output(obj) {
  return ContentService.createTextOutput(JSON.stringify(obj)).setMimeType(ContentService.MimeType.JSON);
}

function createFile(data) {
  const file = DriveApp.getFolderById(data.folderId).createFile(data.name, data.content, "text/plain");
  return output({ success: true, fileId: file.getId() });
}

function editFile(data) {
  DriveApp.getFileById(data.fileId).setContent(data.content);
  return output({ success: true });
}

function deleteFile(data) {
  DriveApp.getFileById(data.fileId).setTrashed(true);
  return output({ success: true });
}

function renameFile(data) {
  DriveApp.getFileById(data.fileId).setName(data.newName);
  return output({ success: true });
}

function getFile(data) {
  const content = DriveApp.getFileById(data.fileId).getBlob().getDataAsString();
  return output({ success: true, content });
}

function downloadFile(data) {
  try {
    const file = DriveApp.getFileById(data.fileId);
    const blob = file.getBlob();
    const base64 = Utilities.base64Encode(blob.getBytes());
    return output({
      success: true,
      fileName: file.getName(),
      mimeType: blob.getContentType(),
      base64: base64
    });
  } catch (e) {
    return output({ success: false, error: e.toString() });
  }
}

function getDirectLink(data) {
  try {
    const id = data.fileId;
    const url = "https://drive.google.com/uc?export=download&id=" + id;
    return output({ success: true, url });
  } catch (e) {
    return output({ success: false, error: e.toString() });
  }
}

function getFiles(data) {
  try {
    const folder = DriveApp.getFolderById(data.folderId);
    const files = folder.getFiles();
    const names = [], sizes = [], urls = [], dates = [], ids = [];

    while (files.hasNext()) {
      const file = files.next();
      names.push(file.getName());
      sizes.push(file.getSize());
      ids.push(file.getId());
      urls.push("https://drive.google.com/uc?export=download&id=" + file.getId());
      dates.push(file.getLastUpdated().toISOString());
    }

    return output({
      success: true,
      fileNames: names,
      fileSizes: sizes,
      fileUrls: urls,
      lastUpdateDates: dates,
      fileIds: ids
    });
  } catch (e) {
    return output({ success: false, error: e.toString() });
  }
}


Leave your Suggestions and If like , give :heart: for me
Updating the extensionfor Good thing :wink:

4 Likes

nice extension, wouldn’t it make sense to use it to download files too?

Yeah, it’s just a basic structure. I am working to upgrade it to the next level. :grin:

1 Like

:pushpin: Updated Block Documentation (version 2 2025-07-05T18:00:00Z)

DownloadFile(fileId, saveAsFileName)

DownloadFile_Method

  • Downloads any file (PDF, image, APK, etc.) from Google Drive
  • Saves it to app-specific local storage on the device
  • Useful for letting users save backups, restore files, or fetch user-uploaded media

GetDirectDownloadLink(fileId)

GetDirectDownloadLink_Method

  • Generates a clean https://drive.google.com/uc?export=download&id=... link.
  • Use this for streaming files in a browser, video player, or direct download.

GetFiles()

GetFiles_Method

  • Fetches all files in the current folder.
  • Triggers GotFileList event with:
    • :memo: fileNames → list of file names
    • :package: fileSizes → file size in bytes
    • :link: fileUrls → download links
    • :date: lastUpdateDates → last modified timestamps
    • :id_button: fileIds → file IDs for further use

GotFileList_Event

APP Script UPDATED !! :wink:

LEaVe Your Suggestions here :head_shaking_horizontally:

2 Likes

great, what about saving the downloaded file to the app’s files?
That is, additional files can be downloaded to the asset section of the application via drive.

1 Like

Yes, great point!

:downwards_button: I’ve now working in a function called DownloadFileToAppFiles(fileId, fileName)
It lets you download any file from Google Drive and save it directly into the app’s private internal storage — perfect for use as runtime assets, configs, or hidden files.

This avoids the need for storage permissions, keeps files secure, and they’re accessible via

getFilesDir() paths.

I’m looking forward to it, it would be a really nice addition to download files to the asset section of the application without storage permission.

Thanks for the suggestion!

Yes, it would definitely be a great feature to download files to the app’s “asset-like” section without needing storage permission.

Unfortunately, in Kodular (and Android in general), the assets folder is read-only after the app is built, so we can’t directly save files to that section at runtime.

However, I’m considering adding a function to download files into the app’s internal storage (getFilesDir) — which works similarly:

  • No storage permission required
  • Secure and private to the app
  • Can be used for dynamic runtime assets like configs, images, etc.

Appreciate the feedback — will keep this idea in mind for updates! :blush:

1 Like

No problem, it can be done by getting storage permission. Such examples are also available in large applications, it will be perfect for downloading additional files because making very large applications in code brings problems.

1 Like

No problem — yes, it can definitely be done using storage permissions if needed.

Many large applications follow this approach by downloading extra files (like media, assets, modules) at runtime instead of bundling everything inside the APK.

This helps reduce initial app size and avoids issues that can come with very large builds.
I’ll consider adding both internal and external save options in future updates to give developers more control. :+1:

1 Like

I am following your project closely and waiting for your update.

This update will be published tomorrow. Today, I am very busy with other work. Thanks foryour valuable feedback. :slight_smile:

1 Like

Did you update?