Adam Bandel


InstaNote

Feb 2026
Type: desktop
Code: 0k lines
Files: 31
Stack:
KotlinAndroid SDKJetpack ComposeGmail APIGoogle Sign-In
Tags:
mobileproductivityandroid

Overview

InstaNote is a lightweight Android application designed for capturing thoughts instantly. The app features a home screen widget that opens a note entry dialog with a single tap, allowing users to quickly jot down ideas without navigating through multiple screens. Notes can be sent directly to your Gmail inbox with one tap, making it easy to transfer quick captures to a more permanent location.

The app integrates with Google Sign-In for authentication and uses the Gmail API to send notes (with optional file attachments) directly to the user’s own email address, effectively turning your inbox into a note repository.

Screenshots

Notes List

Note Entry

Home Screen Widget

Problem

Traditional note-taking apps require multiple steps: unlock phone, find app, wait for load, tap new note, then finally start typing. This friction means fleeting thoughts are often lost before they can be captured.

Additionally, quick notes often need to reach a more permanent destination—email inboxes, where they can be processed alongside other tasks. The standard Android share flow adds unnecessary steps when you just want to email yourself.

Approach

Built a minimal-friction capture flow with two key features:

Stack

Challenges

Outcomes

The app achieves its core goal of minimal-friction note capture. The widget provides one-tap access, and the email integration means notes automatically arrive in Gmail for later processing.

Key technical learnings:

Implementation Notes

Email with Attachments

The core email sending logic handles both plain text and multipart messages:

private fun createEmailWithAttachments(
    to: String, from: String, subject: String,
    uris: List<Uri>, bodyText: String
): MimeMessage {
    val email = MimeMessage(session)
    email.setFrom(InternetAddress(from))
    email.addRecipient(Message.RecipientType.TO, InternetAddress(to))
    email.subject = subject

    val multipart = MimeMultipart()

    // Text body
    val textPart = MimeBodyPart()
    textPart.setText(bodyText)
    multipart.addBodyPart(textPart)

    // Attachments
    for (uri in uris) {
        val attachmentPart = MimeBodyPart()
        val inputStream = contentResolver.openInputStream(uri)
        val dataSource = ByteArrayDataSource(inputStream, contentResolver.getType(uri))
        attachmentPart.dataHandler = DataHandler(dataSource)
        attachmentPart.fileName = getFileName(uri)
        multipart.addBodyPart(attachmentPart)
    }

    email.setContent(multipart)
    return email
}

Delete Confirmation Pattern

The adapter implements a timed confirmation without dialogs:

holder.binding.deleteButton.setOnClickListener {
    if (isPendingDeletion) {
        onDelete(adapterPos)
        pendingDeletionPosition = null
    } else {
        pendingDeletionPosition = adapterPos
        notifyDataSetChanged()

        handler.postDelayed({
            if (pendingDeletionPosition == adapterPos) {
                pendingDeletionPosition = null
                notifyDataSetChanged()
            }
        }, 2000)
    }
}

Project Structure

InstaNote/
├── app/src/main/
│   ├── java/com/instanote/
│   │   ├── MainActivity.kt        # Note list management
│   │   ├── NoteEntryActivity.kt   # Note creation/editing + Gmail
│   │   ├── NoteAdapter.kt         # RecyclerView with delete confirm
│   │   ├── SettingsActivity.kt    # Google auth management
│   │   └── NoteWidgetProvider.kt  # Home screen widget
│   └── res/
│       ├── layout/                # 5 layout files
│       └── xml/note_widget_info.xml
└── build.gradle.kts               # Dependencies + SDK config

Related Posts