Automatically skip unwanted Reels & Shorts using on-device OCR.
An Android automation app that detects user-defined keywords on short-form videos and automatically scrolls past matching content โ 100% on-device, no cloud, no data upload.
Features โข How It Works โข Architecture โข Installation โข Permissions โข Limitations
Screen recordings:
AppSetup.mp4
AppUsage.mp4
VideoSkipper running on top of short-form video applications.
VideoSkipper automatically skips short-form video content when it detects user-defined keywords on the screen.
It currently works with:
- Instagram Reels
VideoSkipper monitors the foreground screen, captures a screenshot when a genuine scroll occurs, extracts visible text using Google ML Kit's on-device OCR, and checks the detected text against the user's saved keywords. If a keyword matches, it automatically performs a swipe gesture to move to the next video โ all processed locally on the device.
flowchart TD
A([User Scrolls]) --> B[AccessibilityService]
B --> C[Detect Scroll Event]
C --> D[Capture Screenshot]
D --> E[Downscale Bitmap]
E --> F[ML Kit OCR]
F --> G[Extract Text]
G --> H{Match Keyword?}
H -- Yes --> I[Swipe to Next Video]
H -- No --> J[Do Nothing]
| ๐ Real-Time Detection | Detects text appearing on Reels and Shorts using on-device OCR. |
| ๐ค Automatic Skipping | Automatically performs a swipe when a configured keyword is detected. |
| ๐ฌ Floating Bubble | Add keywords and control detection without leaving the currently opened app. |
| ๐ Battery Conscious | Uses event-based detection, foreground-app gating, bitmap optimization and automatic safeguards. |
| ๐ Fully On-Device | OCR processing happens locally โ screenshots are never uploaded to a server. |
| ๐๏ธ Independent Controls | Text and image detection can be toggled independently. |
VideoSkipper follows a Clean Architecture + MVVM approach with clear separation between presentation, domain logic, and data sources.
VideoSkipper
โ
โโโ presentation
โ โโโ screens
โ โโโ components
โ โโโ floating overlay UI
โ
โโโ viewmodel
โ โโโ MonitoringViewModel
โ โโโ TextViewModel
โ
โโโ domain
โ โโโ repository
โ โโโ KeywordRepository
โ โโโ MonitoringRepository
โ โโโ AutoScrollDetectionRepository
โ โโโ ScreenActionController
โ
โโโ data
โ โโโ repository
โ โโโ Room-backed repositories
โ โโโ DataStore-backed repositories
โ โโโ ML Kit-backed repositories
โ
โโโ service
โ โโโ PizzaDetectorAccessibilityService
โ โโโ OverlayService
โ
โโโ di
โโโ DatabaseModule
โโโ RepositoryModule
Layer flow:
flowchart TD
UI[Compose UI] --> VM[ViewModel / StateFlow]
VM --> DOM[Domain Layer โ Repository Interfaces]
DOM --> DATA[Data Layer โ Room / DataStore / ML Kit]
DATA --> SVC[Android Services โ Accessibility / WindowManager]
| Category | Technology |
|---|---|
| Language | Kotlin |
| UI | Jetpack Compose |
| Design | Material 3 |
| Architecture | Clean Architecture + MVVM |
| Dependency Injection | Hilt |
| Local Database | Room |
| Preferences | DataStore |
| Concurrency | Kotlin Coroutines |
| Reactive State | Kotlin Flow / StateFlow |
| OCR | Google ML Kit Text Recognition |
| Screen Capture | AccessibilityService takeScreenshot() |
| Automation | AccessibilityService dispatchGesture() |
| Overlay | WindowManager + ComposeView |
| Minimum Android Version | Android 11 / API 30 |
OverlayService creates a draggable floating bubble using WindowManager. The overlay lets users add keywords, enable/disable detection, and control VideoSkipper without leaving Instagram, YouTube, or TikTok.
PizzaDetectorAccessibilityService listens for accessibility events generated by the watched application. Instead of reacting to every accessibility event, VideoSkipper focuses specifically on TYPE_VIEW_SCROLLED, which avoids unnecessary OCR triggered by:
- Video progress updates
- Caption animations
- UI changes
- Content refreshes
When a genuine scroll is detected, the accessibility service captures the screen via takeScreenshot(). The bitmap is downscaled before OCR processing to reduce memory usage and CPU consumption.
The processed bitmap is passed to ML Kit Text Recognition โ no cloud OCR service required.
flowchart LR
A[Screenshot] --> B[Bitmap Processing] --> C[ML Kit] --> D[Recognized Text]
The recognized text is compared against the user's active keyword list, stored locally.
flowchart LR
A[Detected Text] --> B[Active Keywords] --> C[String Matching] --> D{Keyword Found?}
On a match, the service calls dispatchGesture() to perform a synthetic swipe past the current video.
flowchart LR
A[Keyword Found] --> B["dispatchGesture()"] --> C[Swipe Up] --> D[Next Reel / Short]
Performance was a major part of the implementation.
Event precision โ The initial implementation relied on TYPE_WINDOW_CONTENT_CHANGED, which fired excessively as content changed. It was replaced with TYPE_VIEW_SCROLLED, drastically cutting unnecessary screenshot/OCR cycles.
Foreground-app gating โ OCR only runs when a supported app is actually in the foreground.
flowchart TD
A[Instagram / YouTube / TikTok] --> B{App in Foreground?}
B -- Yes --> C[Run OCR]
B -- No --> D[Do Nothing]
Bitmap optimization โ Screenshots are downscaled before processing.
flowchart LR
A[1080px Screenshot] --> B[Bitmap Downscale] --> C[720px Processing Image] --> D[ML Kit OCR]
Mutex protection โ Detection cycles are protected by a Mutex, preventing overlapping screenshot/OCR operations when scroll events fire rapidly.
Automatic safety stop โ A 6-hour automatic stop safeguard prevents an unattended accessibility session from continuously draining battery.
During development, the accessibility service appeared as Connected / Enabled in Android system settings, but received zero accessibility events.
Inspecting the runtime AccessibilityServiceInfo revealed:
eventTypes = 0
typeAllMask = -1
The service configuration was never actually applied โ the root cause was a one-character typo in the manifest metadata key.
| Metadata Key | |
|---|---|
| โ Incorrect | android:name="android.accessibility.service" |
| โ Correct | android:name="android.accessibilityservice" |
Android silently ignored the incorrect key, breaking the entire event-processing pipeline.
- Android Studio
- Kotlin
- Android SDK
- A physical Android device (required โ the app depends on Accessibility APIs and
takeScreenshot(), which don't behave reliably on emulators) - Android 11 / API 30 or higher
# 1. Clone the repository
git clone https://github.com/mohitdamke/VideoSkipper.git
# 2. Open the project in Android Studio
# 3. Let Gradle sync and build
# 4. Run on a physical Android 11+ deviceVideoSkipper needs special Android permissions to interact with other apps.
| Permission | Path | Why |
|---|---|---|
| Display over other apps | Settings โ Apps โ Special app access โ Display over other apps โ VideoSkipper โ Allow |
Powers the floating overlay bubble |
| Accessibility Service | Settings โ Accessibility โ Installed / Downloaded apps โ VideoSkipper โ Enable |
Detects scrolls, captures screenshots, and performs swipes |
| Restricted settings (Android 13+, sideloaded installs) | App Info โ โฎ โ Allow Restricted Settings |
Required before Accessibility can be enabled on sideloaded builds |
- Requires Android 11 / API 30+
- OCR accuracy depends on text visibility, size, contrast, and styling
- Highly stylized or animated text may not be detected
- Current keyword matching is substring-based
- Accessibility-based automation apps may face Google Play policy restrictions
- Image detection UI exists, but the full image-detection pipeline isn't implemented yet
- Continuous screen analysis can still consume battery despite optimization
VideoSkipper is built around on-device processing. The OCR pipeline never uploads screenshots to a remote server, and captured screen content is not intentionally persisted to disk as part of the detection pipeline.
โ ๏ธ Accessibility services have powerful capabilities on Android. Only enable VideoSkipper if you understand and trust the permissions being granted.
|
Android Fundamentals
|
Architecture
|
Systems & Performance
|
This project currently does not specify a license. If you plan to open-source the repository, consider adding MIT or Apache-2.0.








