Skip to content
OcramsretepPublic

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Repository files navigation

⚠️Development stoped, because WhatsApp does not allow a time limit for the emotes.

TEWA — Twitch Emotes to WhatsApp (scaffold)

This repository contains a scaffolding of the app described in tewa_specification.md — a Flutter UI with platform integration stubs to export Twitch emotes as WhatsApp sticker packs.

What’s implemented now:

  • Flutter app shell with bottom navigation (Channels, Favorites, Packs, Search, Settings)
  • Hive initialized with TypeAdapters registered and boxes opened: Channel (1), EmotePack (2), Favorite (3), Sticker (4), StickerExportState (5), StickerPack (6)
  • Emote (7) TypeAdapter added
  • Boxes opened at startup: channels, emotes, favorites, emote_packs, sticker_packs
  • Emote metadata persisted to Hive and loaded on app start
  • Favorites persisted to Hive with in‑UI star toggle (Channels) and Favorites grid
  • EmotePack and StickerPack management:
    • Create EmotePack drafts from Favorites, rename, and delete
    • Export an EmotePack via Rust to produce a WhatsApp‑ready StickerPack (files + manifest)
    • Persist exported StickerPacks in Hive and list them in the Packs tab
  • Android: WhatsApp integration via a ContentProvider per official contract (implemented)
    • The provider now enumerates exported packs by reading manifest.json under files/stickers/{pack_id}/
    • It returns real cursors for packs and stickers and serves tray.webp and sticker files via content:// URIs
  • iOS: WAStickersThirdParty import trigger implemented (official path)
    • LSApplicationQueriesSchemes configured for whatsapp and whatsapp-business
    • Settings → “Import to WhatsApp (iOS)” opens WhatsApp’s sticker import flow for the latest exported pack
  • Basic widget test updated to reflect the new app shell
  • Twitch OAuth login using Authorization Code + PKCE (no client secret on device)
  • Secure token storage (Keychain/Keystore) with refresh handling
  • Twitch API client for “Get User” and “Get User Emotes” (scope user:read:emotes)
  • Settings screen: Sign in/Sign out + “Sync Emotes” action with status
  • Channels screen: Sign-in CTA and a grid of synced emotes with favorites toggle
  • Channel grouping UI (by emote owner) with tabs per channel and an "All" tab
  • Search tab: global search across stored emotes (case/diacritic‑insensitive) with favorites toggle
  • Rust core (static + animated) with FFI bridge:
    • Static: tewa_convert_emotes_to_stickers performs decode → sRGB → Lanczos3 resize → optional 2px outline → lossy WebP with size targeting (≤ 100 KB) → tray.webp (96×96, ≤ 50 KB) → manifest.json
    • Animated: GIF and animated WebP inputs are decoded/demuxed to frames, capped to ≤ 30 fps and ≤ 3 s total, resized/outlined per frame, and assembled into Animated WebP with quality search ≤ 500 KB. Per-sticker is_animated is recorded in manifest.
    • tewa_validate_sticker enforces dimensions and size caps (tray vs. sticker) and, for animated WebP, also enforces ≤ 3 s total duration and ≤ 30 fps by parsing the animation timeline
    • Settings → “Rust Core → Check” calls tewa_version() to verify the native library is loaded
  • Export sample flow: Settings → “Export sample pack (Rust)” downloads 3 emotes and generates a WhatsApp‑ready pack in the app’s support directory
  • Diagnostics screen: Settings → Diagnostics shows login state, counts, last sync error, Rust core version, and cache sizes; buttons to clear temp cache and sticker outputs
  • About page: Settings → About with attributions and a tappable Privacy Policy URL (configure in assets/config)

Onboarding and accessibility

  • First‑run onboarding: A short, non‑blocking modal sheet explains usage and rights (personal use only, export while entitled, official APIs). You can revisit guidance by clearing the prefs box.
  • Minimum tap target sizes: The app theme enables MaterialTapTargetSize.padded globally; key action buttons are ElevatedButton/Navigation destinations which meet size guidance.
  • Semantics and labels: Emote tiles in Channels, Favorites, and Search include Semantics labels; favorite toggles and pack actions have semantic labels and tooltips to improve screen‑reader support.
  • Dynamic text sizing: UI avoids hard‑coded sizes where practical and uses ellipsis/overflow to remain usable with larger fonts; grid tiles remain tappable. We will continue to refine based on user feedback.

How to sign in with Twitch

This app uses Twitch OAuth 2.0 Authorization Code with PKCE. No client secret is stored in the app; only the public client ID is needed. Follow these steps to get login working and understand the flow.

Prerequisites (one‑time setup for developers/testers)

  • Create or use an existing Twitch application at https://dev.twitch.tv/console/apps.
  • Copy the Client ID from your Twitch app.
  • Add the redirect URI tewa://oauth/callback to the “OAuth Redirect URLs” list for your Twitch app.
  • Provide the Client ID to the app in one of two ways:
    1. Preferred: Create a file assets/config/app_settings.json (bundled locally, not committed if it contains secrets) with at least:
      "twitch": {
        "clientId": "YOUR_REAL_TWITCH_CLIENT_ID",
        "redirectUris": { "android": "tewa://oauth/callback", "ios": "tewa://oauth/callback" }
        }
      }
      
      The project is configured to bundle the entire assets/config/ directory.
    2. Alternatively (for debugging): write a secure override to key tw_client_id_override in FlutterSecureStorage. The app will prefer this override over the asset config.

Important: Placeholder values like "YOUR_CLIENT_ID" are treated as invalid and will cause the app to show a clear setup error instead of hitting Twitch.

User steps in the app

  1. Build and run the app after configuring the Client ID.
  2. Open the Channels tab (or Settings). If you’re not signed in, you’ll see a “Sign in” button.
  3. Tap “Sign in”. The app opens the Twitch authorization page in a browser or custom tab.
  4. Log in to Twitch (if not already) and review the requested scope user:read:emotes.
  5. Approve access. Twitch redirects to tewa://oauth/callback, which returns control to the app.
  6. The app exchanges the authorization code for tokens (using PKCE), stores them securely (Keychain/Keystore), and marks you as signed in. The Channels tab can now sync and show your emotes.

What happens under the hood

  • Authorization: The app builds an authorize URL with your effective Client ID, the redirect URI tewa://oauth/callback, response_type=code, scope=user:read:emotes, and a PKCE S256 code challenge. force_verify=true ensures the consent screen can appear.
  • Callback handling: FlutterWebAuth2 listens for the tewa scheme and returns the final callback URL to the app.
  • Token exchange: The app posts to https://id.twitch.tv/oauth2/token with client_id, code, redirect_uri, and the PKCE code_verifier. Access and refresh tokens are stored securely.
  • Token refresh: When needed, the app refreshes tokens using the same Client ID. All Twitch API calls include the Authorization: Bearer header and the matching Client-Id header.

Troubleshooting

  • “invalid client” immediately after tapping Sign in: The effective Client ID is missing or a placeholder. Ensure assets/config/app_settings.json contains your real Client ID, or set the secure override tw_client_id_override. Also confirm your Twitch app lists tewa://oauth/callback as a valid redirect URI.
  • Redirect never returns to the app: Make sure the redirect URI is exactly tewa://oauth/callback and that the app is installed/built with the intent filter (Android) and URL scheme (iOS). This repo already configures the scheme; only the Twitch app needs to be set.
  • Stuck or canceled auth: Simply try again. You can also sign out in Settings to clear tokens and then sign in.
  • API calls failing after some time: The app refreshes tokens automatically. If refresh fails (e.g., revoked), you’ll be signed out and can sign in again.

Notes

  • The app consistently uses a single “effective” Client ID across login, refresh, and API calls. If you set a secure override, it will be used everywhere; otherwise, the value from assets/config/app_settings.json is used. Placeholders/whitespace are ignored.
  • No client secret is required or stored; this is a public client using PKCE.

Rust core integration

This repo now contains a minimal Rust library (FFI) scaffold in rust_core/ and a Dart bridge in lib/ffi/tewa_rust.dart.

What works now

  • tewa_version() is exported and invoked from Flutter via the Settings screen → “Rust Core” → “Check” button. If the native library is missing, a helpful message is shown.
  • tewa_convert_emotes_to_stickers(json, outDir) performs conversion and writes files to {appSupport}/stickers/{pack_id} (Android maps to app files dir). It outputs: 0001.webp, 0002.webp, …, tray.webp, and manifest.json.
    • Static stickers: ≤ 100 KB each; 512×512.
    • Animated stickers (GIF inputs): ≤ 500 KB each; ≤ 3 s; ≤ 30 fps; 512×512.
  • tewa_validate_sticker(path) validates basic constraints for static/tray.

Planned implementation (see tewa_specification.md)

  • Pack manifest fields extended as needed; full Android/iOS import flows.

Build the Rust library (Android)

Prereqs

  • Rust toolchain (rustup), and cargo-ndk installed: cargo install cargo-ndk
  • Android NDK (Flutter embeds an NDK; align with your local setup if needed)

From project root:

cd rust_core
cargo ndk -t arm64-v8a -o ../android/app/src/main/jniLibs build --release
cargo ndk -t armeabi-v7a -o ../android/app/src/main/jniLibs build --release
cargo ndk -t x86_64 -o ../android/app/src/main/jniLibs build --release

This will place .so files under:

  • android/app/src/main/jniLibs/arm64-v8a/librust_core.so
  • android/app/src/main/jniLibs/armeabi-v7a/librust_core.so
  • android/app/src/main/jniLibs/x86_64/librust_core.so

Run the app on Android and open Settings → Rust Core → Check. You should see a snackbar like “Loaded: tewa_rust 0.1.0”.

Build the Rust library (iOS)

On iOS, the Rust library must be linked statically into the app and loaded via DynamicLibrary.process().

Steps to produce an xcframework and link it in Xcode:

  1. Install iOS Rust targets
rustup target add aarch64-apple-ios x86_64-apple-ios aarch64-apple-ios-sim
  1. Build static libraries for device and simulators
cd rust_core
cargo build --release --target aarch64-apple-ios
cargo build --release --target x86_64-apple-ios
cargo build --release --target aarch64-apple-ios-sim

This produces:

  • target/aarch64-apple-ios/release/librust_core.a
  • target/x86_64-apple-ios/release/librust_core.a
  • target/aarch64-apple-ios-sim/release/librust_core.a
  1. Create an xcframework
mkdir -p build_ios
cp target/aarch64-apple-ios/release/librust_core.a build_ios/librust_core_ios_arm64.a
cp target/x86_64-apple-ios/release/librust_core.a build_ios/librust_core_ios_x86_64.a
cp target/aarch64-apple-ios-sim/release/librust_core.a build_ios/librust_core_ios_sim_arm64.a

xcodebuild -create-xcframework \
  -library build_ios/librust_core_ios_arm64.a -headers . \
  -library build_ios/librust_core_ios_x86_64.a -headers . \
  -library build_ios/librust_core_ios_sim_arm64.a -headers . \
  -output build_ios/rust_core.xcframework
  1. Add the xcframework to Xcode
  • Open ios/Runner.xcworkspace in Xcode.
  • Drag build_ios/rust_core.xcframework into the Runner project (e.g., under a new "Frameworks" group). Choose "Copy items if needed".
  • Select the Runner target → General → Frameworks, Libraries, and Embedded Content → add rust_core.xcframework and set it to "Do Not Embed".
  1. Ensure symbols are visible to Flutter
  • No additional Dart-side change is required; lib/ffi/tewa_rust.dart loads symbols from the current process on iOS.
  • Build and run on a device/simulator, then open Settings → Rust Core → Check; it should show the version string.

Notes:

  • If you see a linker error, verify the library name is librust_core.a (matching crate-type = ["staticlib"]) and that the xcframework is added to the correct target.
  • The libwebp vendored by libwebp-sys2 is statically linked into the Rust lib.
  • We’ll automate xcframework creation in CI in a later step.

Using the FFI in Dart

  • The bridge is in lib/ffi/tewa_rust.dart.
  • On Android it dynamically loads librust_core.so.
  • On iOS it loads from the current process (requires static linking).
  • API surface today:
    • TewaRustCore.instance.version() → String? (null if not loaded)
    • TewaRustCore.instance.validateSticker(path) → int? (0 = ok)
    • TewaRustCore.instance.convertEmotesToStickers(json, outDir) → int?

Try the sample export

  1. Sign in with Twitch (Settings → Sign in) and Sync Emotes so at least 3 emotes are available.
  2. Build and place the Rust library on Android (see below), or link on iOS.
  3. Open Settings → “Export sample pack (Rust)”.
  4. On success, files are written under:
    • Android: <app files>/stickers/<pack_id>/
    • iOS: Library/Application Support/TEWA/stickers/<pack_id>/
  5. On Android, trigger WhatsApp import using the ContentProvider (see spec Appendix A for the intent). The provider exposes (authority is based on your applicationId):
    • Packs: content://${applicationId}.stickerprovider/packs
    • Stickers of a pack: content://${applicationId}.stickerprovider/packs/{pack_id}/stickers
    • Tray icon bytes: content://${applicationId}.stickerprovider/packs/{pack_id}/tray
    • Sticker file bytes: content://${applicationId}.stickerprovider/packs/{pack_id}/sticker/{file_name}
  6. On iOS, open Settings → “Import to WhatsApp (iOS)” to launch WhatsApp’s WAStickersThirdParty import flow for the most recently exported pack.

Export preview and validation (new)

  • From Packs → Draft Emote Packs, tap “Preview & Export”. The app will:
    • Run a conversion into a preview directory using the Rust core.
    • Validate the tray (96×96, ≤ 50 KB) and each sticker (static ≤ 100 KB; animated ≤ 500 KB, ≤ 3 s, ≤ 30 fps; 512×512).
    • Show a per‑file OK/Error list with human‑readable reasons (size/dimensions/fps/duration/decoder issues).
    • Block finalization until all items pass validation. You can re‑run the preview after adjusting the draft.
  • When all items pass, tap “Finalize export” to persist the StickerPack and move files into the final pack directory.

Manage and export packs (new)

  • Go to the Favorites tab and star at least 3 emotes.
  • Open the Packs tab:
    • Tap “New from Favorites” to create an EmotePack draft from your current favorites.
    • Rename the draft if desired (max 10 chars).
    • Tap Export to run the Rust converter and generate a WhatsApp‑ready pack. The exported pack will appear under “Exported Sticker Packs”.
  • Import into WhatsApp:
    • Android: use the ContentProvider import intent (see spec Appendix A) from your app flow.
    • iOS: use Settings → “Import to WhatsApp (iOS)”.

Continuous Integration (GitHub Actions)

This repo includes a GitHub Actions workflow that runs on push and PR to main/master:

  • Format check: dart format --set-exit-if-changed .
  • Static analysis: flutter analyze
  • Tests: flutter test
  • Android build: flutter build apk --debug

Workflow file: .github/workflows/ci.yml

To see CI status in your repo, you can add a badge like:

![CI](https://github.com/<your-org-or-user>/<your-repo>/actions/workflows/ci.yml/badge.svg)

Diagnostics and cache management

  • Open Settings → Diagnostics to view:
    • Login status and user label
    • Counts for stored Emotes, Favorites, Emote drafts, and exported Sticker packs
    • The last emote sync error (if any)
    • Rust core version (if the native library is loaded)
    • Cache usage: Temporary cache and Sticker outputs directories
  • Use the Clear buttons to remove temporary files or previously exported sticker artifacts.

About and Privacy Policy

  • Open Settings → About to see attributions and legal notes.
  • Configure your Privacy Policy URL in assets/config/app_settings.example.json under legal.privacyPolicyUrl.

Setup

  1. Requirements
  • Flutter stable, Android SDK, Xcode
  • Rust toolchain (for later FFI work)
  1. Install deps
flutter pub get
  1. Config placeholder
  • See assets/config/app_settings.example.json. Copy and adapt as needed. Secrets (like Twitch Client ID) should be stored using in‑app encrypted storage; do not commit real values.
  1. Run
flutter run

Twitch login and emote sync

  1. Create a Twitch application (https://dev.twitch.tv/console/apps)

    • Add OAuth Redirect URL: tewa://oauth/callback (for both Android and iOS)
    • Copy your Client ID.
  2. Configure the app

    • Edit assets/config/app_settings.example.json and set twitch.clientId to your Client ID.
    • The redirect URI defaults to tewa://oauth/callback and is already wired in AndroidManifest and iOS Info.plist.
  3. Login

    • Open the app → Settings tab → press “Sign in”. Complete Twitch login/consent; tokens are stored securely.
  4. Sync emotes

    • In Settings, tap “Sync” under “Sync Emotes” or press the “Sync” FAB on Channels.
    • The Channels tab will show a grid of your available Twitch emotes.

Scopes and endpoints

  • Scope: user:read:emotes
  • Endpoints: GET /helix/users, GET /helix/users/emotes

Troubleshooting

  • If login is not returning to the app, ensure the scheme tewa:// is not claimed by another app on your device/emulator.
  • Verify your Twitch app redirect URI is exactly tewa://oauth/callback.
  • Android requires INTERNET permission (declared). iOS requires the custom URL scheme (added).

Android: Sticker Content Provider

  • Manifest registers com.example.tewa.stickers.StickerContentProvider with authority ${applicationId}.stickerprovider.
  • The provider computes its authority at runtime from the app package name to ensure it always matches the current applicationId declared in Gradle.
  • The provider reads exported packs by parsing manifest.json under files/stickers/{pack_id}/ and returns real cursors for packs and stickers, serving tray.webp and sticker files via content:// URIs.

iOS

  • Info.plist includes WhatsApp URL schemes and the OAuth callback URL type (scheme tewa).
  • WAStickersThirdParty import trigger is wired: use Settings → “Import to WhatsApp (iOS)”. Ensure you have exported a pack first.

Spec

  • See tewa_specification.md for the full product/tech specification and platform checklists.

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages