<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Getinfo Toyou</title>
    <description>The latest articles on DEV Community by Getinfo Toyou (@getinfotoyou).</description>
    <link>https://dev.to/getinfotoyou</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3794901%2Fcdf356ed-ee53-474c-a1a2-73ee4d5bbeb5.png</url>
      <title>DEV Community: Getinfo Toyou</title>
      <link>https://dev.to/getinfotoyou</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/getinfotoyou"/>
    <language>en</language>
    <item>
      <title>I built Json To Toon — Save 40-60% on AI tokens instantly by converting your JSON to TOON.</title>
      <dc:creator>Getinfo Toyou</dc:creator>
      <pubDate>Wed, 23 Sep 2026 14:30:21 +0000</pubDate>
      <link>https://dev.to/getinfotoyou/i-built-json-to-toon-save-40-60-on-ai-tokens-instantly-by-converting-your-json-to-toon-2e5p</link>
      <guid>https://dev.to/getinfotoyou/i-built-json-to-toon-save-40-60-on-ai-tokens-instantly-by-converting-your-json-to-toon-2e5p</guid>
      <description>&lt;p&gt;error: invalid model selection (--model "Gemini 3.5 Flash (High)" --effort ""): model Gemini 3.5 Flash (High) is not recognized as a known model or custom model in settings&lt;br&gt;
Available models:&lt;br&gt;
  Gemini 3.8 Flash (High)&lt;br&gt;
  Gemini 3.8 Flash (Medium)&lt;br&gt;
  Gemini 3.8 Flash (Low)&lt;br&gt;
  Gemini 3.7 Flash (High)&lt;br&gt;
  Gemini 3.7 Flash (Medium)&lt;br&gt;
  Gemini 3.7 Flash (Low)&lt;br&gt;
  Gemini 3.6 Flash (High)&lt;br&gt;
  Gemini 3.6 Flash (Medium)&lt;br&gt;
  Gemini 3.6 Flash (Low)&lt;br&gt;
  Gemini 3.1 Pro (High)&lt;br&gt;
  Gemini 3.1 Pro (Low)&lt;br&gt;
  Claude Sonnet 4.6 (Thinking)&lt;br&gt;
  Claude Opus 4.6 (Thinking)&lt;br&gt;
  GPT-OSS 120B (Medium)&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>ai</category>
      <category>programming</category>
    </item>
    <item>
      <title>I built Photoconvert — Securely resize and convert images right in your browser.</title>
      <dc:creator>Getinfo Toyou</dc:creator>
      <pubDate>Mon, 21 Sep 2026 14:30:16 +0000</pubDate>
      <link>https://dev.to/getinfotoyou/i-built-photoconvert-securely-resize-and-convert-images-right-in-your-browser-12pm</link>
      <guid>https://dev.to/getinfotoyou/i-built-photoconvert-securely-resize-and-convert-images-right-in-your-browser-12pm</guid>
      <description>&lt;p&gt;error: invalid model selection (--model "Gemini 3.5 Flash (High)" --effort ""): model Gemini 3.5 Flash (High) is not recognized as a known model or custom model in settings&lt;br&gt;
Available models:&lt;br&gt;
  Gemini 3.8 Flash (High)&lt;br&gt;
  Gemini 3.8 Flash (Medium)&lt;br&gt;
  Gemini 3.8 Flash (Low)&lt;br&gt;
  Gemini 3.7 Flash (High)&lt;br&gt;
  Gemini 3.7 Flash (Medium)&lt;br&gt;
  Gemini 3.7 Flash (Low)&lt;br&gt;
  Gemini 3.6 Flash (High)&lt;br&gt;
  Gemini 3.6 Flash (Medium)&lt;br&gt;
  Gemini 3.6 Flash (Low)&lt;br&gt;
  Gemini 3.1 Pro (High)&lt;br&gt;
  Gemini 3.1 Pro (Low)&lt;br&gt;
  Claude Sonnet 4.6 (Thinking)&lt;br&gt;
  Claude Opus 4.6 (Thinking)&lt;br&gt;
  GPT-OSS 120B (Medium)&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>tools</category>
      <category>performance</category>
    </item>
    <item>
      <title>I built Home Loan EMI Calculator — Calculate your home loan EMI and view repayment schedules instantly.</title>
      <dc:creator>Getinfo Toyou</dc:creator>
      <pubDate>Fri, 18 Sep 2026 14:40:58 +0000</pubDate>
      <link>https://dev.to/getinfotoyou/i-built-home-loan-emi-calculator-calculate-your-home-loan-emi-and-view-repayment-schedules-26pp</link>
      <guid>https://dev.to/getinfotoyou/i-built-home-loan-emi-calculator-calculate-your-home-loan-emi-and-view-repayment-schedules-26pp</guid>
      <description>&lt;p&gt;error: invalid model selection (--model "Gemini 3.5 Flash (High)" --effort ""): model Gemini 3.5 Flash (High) is not recognized as a known model or custom model in settings&lt;br&gt;
Available models:&lt;br&gt;
  Gemini 3.8 Flash (High)&lt;br&gt;
  Gemini 3.8 Flash (Medium)&lt;br&gt;
  Gemini 3.8 Flash (Low)&lt;br&gt;
  Gemini 3.7 Flash (High)&lt;br&gt;
  Gemini 3.7 Flash (Medium)&lt;br&gt;
  Gemini 3.7 Flash (Low)&lt;br&gt;
  Gemini 3.6 Flash (High)&lt;br&gt;
  Gemini 3.6 Flash (Medium)&lt;br&gt;
  Gemini 3.6 Flash (Low)&lt;br&gt;
  Gemini 3.1 Pro (High)&lt;br&gt;
  Gemini 3.1 Pro (Low)&lt;br&gt;
  Claude Sonnet 4.6 (Thinking)&lt;br&gt;
  Claude Opus 4.6 (Thinking)&lt;br&gt;
  GPT-OSS 120B (Medium)&lt;/p&gt;

</description>
      <category>finance</category>
      <category>webdev</category>
      <category>tools</category>
    </item>
    <item>
      <title>I built Aimarkdownpro — Convert Markdown to clean HTML with AI-optimized precision.</title>
      <dc:creator>Getinfo Toyou</dc:creator>
      <pubDate>Wed, 16 Sep 2026 14:30:30 +0000</pubDate>
      <link>https://dev.to/getinfotoyou/i-built-aimarkdownpro-convert-markdown-to-clean-html-with-ai-optimized-precision-58c0</link>
      <guid>https://dev.to/getinfotoyou/i-built-aimarkdownpro-convert-markdown-to-clean-html-with-ai-optimized-precision-58c0</guid>
      <description>&lt;p&gt;error: invalid model selection (--model "Gemini 3.5 Flash (High)" --effort ""): model Gemini 3.5 Flash (High) is not recognized as a known model or custom model in settings&lt;br&gt;
Available models:&lt;br&gt;
  Gemini 3.8 Flash (High)&lt;br&gt;
  Gemini 3.8 Flash (Medium)&lt;br&gt;
  Gemini 3.8 Flash (Low)&lt;br&gt;
  Gemini 3.7 Flash (High)&lt;br&gt;
  Gemini 3.7 Flash (Medium)&lt;br&gt;
  Gemini 3.7 Flash (Low)&lt;br&gt;
  Gemini 3.6 Flash (High)&lt;br&gt;
  Gemini 3.6 Flash (Medium)&lt;br&gt;
  Gemini 3.6 Flash (Low)&lt;br&gt;
  Gemini 3.1 Pro (High)&lt;br&gt;
  Gemini 3.1 Pro (Low)&lt;br&gt;
  Claude Sonnet 4.6 (Thinking)&lt;br&gt;
  Claude Opus 4.6 (Thinking)&lt;br&gt;
  GPT-OSS 120B (Medium)&lt;/p&gt;

</description>
      <category>markdown</category>
      <category>webdev</category>
      <category>productivity</category>
    </item>
    <item>
      <title>I built Javascript — Interactive programming lessons to master coding step-by-step.</title>
      <dc:creator>Getinfo Toyou</dc:creator>
      <pubDate>Fri, 11 Sep 2026 14:30:13 +0000</pubDate>
      <link>https://dev.to/getinfotoyou/i-built-javascript-interactive-programming-lessons-to-master-coding-step-by-step-49l1</link>
      <guid>https://dev.to/getinfotoyou/i-built-javascript-interactive-programming-lessons-to-master-coding-step-by-step-49l1</guid>
      <description>&lt;p&gt;error: invalid model selection (--model "Gemini 3.5 Flash (High)" --effort ""): model Gemini 3.5 Flash (High) is not recognized as a known model or custom model in settings&lt;br&gt;
Available models:&lt;br&gt;
  Gemini 3.8 Flash (High)&lt;br&gt;
  Gemini 3.8 Flash (Medium)&lt;br&gt;
  Gemini 3.8 Flash (Low)&lt;br&gt;
  Gemini 3.7 Flash (High)&lt;br&gt;
  Gemini 3.7 Flash (Medium)&lt;br&gt;
  Gemini 3.7 Flash (Low)&lt;br&gt;
  Gemini 3.6 Flash (High)&lt;br&gt;
  Gemini 3.6 Flash (Medium)&lt;br&gt;
  Gemini 3.6 Flash (Low)&lt;br&gt;
  Gemini 3.1 Pro (High)&lt;br&gt;
  Gemini 3.1 Pro (Low)&lt;br&gt;
  Claude Sonnet 4.6 (Thinking)&lt;br&gt;
  Claude Opus 4.6 (Thinking)&lt;br&gt;
  GPT-OSS 120B (Medium)&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>beginners</category>
      <category>learning</category>
    </item>
    <item>
      <title>I built Imageslim — Fast, private image resizing and compression in your browser.</title>
      <dc:creator>Getinfo Toyou</dc:creator>
      <pubDate>Wed, 09 Sep 2026 14:30:15 +0000</pubDate>
      <link>https://dev.to/getinfotoyou/i-built-imageslim-fast-private-image-resizing-and-compression-in-your-browser-55pb</link>
      <guid>https://dev.to/getinfotoyou/i-built-imageslim-fast-private-image-resizing-and-compression-in-your-browser-55pb</guid>
      <description>&lt;p&gt;error: invalid model selection (--model "Gemini 3.5 Flash (High)" --effort ""): model Gemini 3.5 Flash (High) is not recognized as a known model or custom model in settings&lt;br&gt;
Available models:&lt;br&gt;
  Gemini 3.8 Flash (High)&lt;br&gt;
  Gemini 3.8 Flash (Medium)&lt;br&gt;
  Gemini 3.8 Flash (Low)&lt;br&gt;
  Gemini 3.7 Flash (High)&lt;br&gt;
  Gemini 3.7 Flash (Medium)&lt;br&gt;
  Gemini 3.7 Flash (Low)&lt;br&gt;
  Gemini 3.6 Flash (High)&lt;br&gt;
  Gemini 3.6 Flash (Medium)&lt;br&gt;
  Gemini 3.6 Flash (Low)&lt;br&gt;
  Gemini 3.1 Pro (High)&lt;br&gt;
  Gemini 3.1 Pro (Low)&lt;br&gt;
  Claude Sonnet 4.6 (Thinking)&lt;br&gt;
  Claude Opus 4.6 (Thinking)&lt;br&gt;
  GPT-OSS 120B (Medium)&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>tools</category>
      <category>productivity</category>
    </item>
    <item>
      <title>Optimizing CameraX and ML Kit for Real-World Workflows: Building Sharp QR</title>
      <dc:creator>Getinfo Toyou</dc:creator>
      <pubDate>Mon, 07 Sep 2026 14:31:12 +0000</pubDate>
      <link>https://dev.to/getinfotoyou/optimizing-camerax-and-ml-kit-for-real-world-workflows-building-sharp-qr-19fp</link>
      <guid>https://dev.to/getinfotoyou/optimizing-camerax-and-ml-kit-for-real-world-workflows-building-sharp-qr-19fp</guid>
      <description>&lt;h1&gt;
  
  
  Optimizing CameraX and ML Kit for Real-World Workflows: Building Sharp QR
&lt;/h1&gt;

&lt;p&gt;Most modern Android phones ship with a default camera app capable of detecting QR codes. Yet, if you look at the Play Store, barcode and QR scanning utilities remain persistently popular. &lt;/p&gt;

&lt;p&gt;Why? Because default camera implementations are designed for casual photography, not high-throughput, specialized utility tasks. Most third-party alternatives swing too far in the opposite direction: bloated with intrusive ads, full-screen video interruptions, and sketchy permissions.&lt;/p&gt;

&lt;p&gt;When I set out to build &lt;a href="https://play.google.com/store/apps/details?id=com.getinfotoyou.sharpqr" rel="noopener noreferrer"&gt;Sharp QR&lt;/a&gt;, my goal was to build a clean, reliable scanner and generator focused on performance and practical utility.&lt;/p&gt;

&lt;p&gt;Here is a look at who this tool was built for, the technical challenges behind it, and what I learned building it with modern Android tooling.&lt;/p&gt;




&lt;h2&gt;
  
  
  Who Actually Needs a Dedicated QR Tool?
&lt;/h2&gt;

&lt;p&gt;While everyday users occasionally scan a restaurant menu, certain professional workflows demand something far more dependable and focused:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Event Organizers and Check-in Staff:&lt;/strong&gt; When you need to process hundreds of attendees entering a venue, a standard camera app hunting for focus on wrinkled paper or dim phone screens causes massive bottlenecks. They need instant feedback, persistent history logs, and offline reliability.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Marketers and Print Designers:&lt;/strong&gt; Marketers regularly generate URLs, vCards, and Wi-Fi credentials for packaging, posters, and business cards. They need a tool that can instantly generate precise, high-contrast QR matrices, verify how they parse across different data schemas, and test them locally before sending assets to the print shop.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Field Technicians and IT Administrators:&lt;/strong&gt; Technicians regularly scan asset tags, network router credentials, and serial numbers in poorly lit server racks or warehouses. They need direct access to torch controls, instant clipboard copying, and zero ad popups blocking their workflow.&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  The Tech Stack
&lt;/h2&gt;

&lt;p&gt;To keep the footprint small and the UI responsive, I relied on modern, first-party Android libraries:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Language:&lt;/strong&gt; Kotlin&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;UI Layer:&lt;/strong&gt; Jetpack Compose (Material 3)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Camera Pipeline:&lt;/strong&gt; AndroidX CameraX (&lt;code&gt;camera-camera2&lt;/code&gt;, &lt;code&gt;camera-lifecycle&lt;/code&gt;, &lt;code&gt;camera-view&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Vision Processing:&lt;/strong&gt; Google ML Kit Barcode Scanning API (bundled model)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Local Persistence:&lt;/strong&gt; Room Database with Kotlin Coroutines and Flow&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;QR Generation:&lt;/strong&gt; ZXing Core (strictly used for the encoding matrix logic)&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Technical Challenges
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Frame Analysis Bottlenecks with CameraX
&lt;/h3&gt;

&lt;p&gt;Setting up CameraX with &lt;code&gt;ImageAnalysis.Analyzer&lt;/code&gt; is straightforward on paper, but keeping the frame rate steady across diverse hardware is tricky. Feeding every 30fps YUV frame directly into ML Kit's detector quickly leads to thermal throttling and dropped frames on budget devices.&lt;/p&gt;

&lt;p&gt;To solve this, I decoupled frame delivery from analysis. Using &lt;code&gt;ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST&lt;/code&gt; ensured the camera pipeline never blocked waiting for ML Kit to complete inference on the previous frame. Additionally, I scoped the target analysis resolution to 1080p, which provides the sweet spot between reading dense, small QR codes and maintaining sub-50ms inference times.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Eliminating Detection Jitter
&lt;/h3&gt;

&lt;p&gt;When scanning in continuous mode or saving scan history, ML Kit will report the same barcode across 15 consecutive frames. Without debouncing, your database fills up with duplicates instantly.&lt;/p&gt;

&lt;p&gt;I built a simple time-window debounce mechanism using Kotlin Flows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="py"&gt;lastScannedValue&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;
&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="py"&gt;lastScannedTimestamp&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Long&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0L&lt;/span&gt;
&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;scanCooldownMs&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1500L&lt;/span&gt;

&lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;onBarcodeDetected&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rawValue&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;now&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;currentTimeMillis&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rawValue&lt;/span&gt; &lt;span class="p"&gt;!=&lt;/span&gt; &lt;span class="n"&gt;lastScannedValue&lt;/span&gt; &lt;span class="p"&gt;||&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;now&lt;/span&gt; &lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="n"&gt;lastScannedTimestamp&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;scanCooldownMs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;lastScannedValue&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;rawValue&lt;/span&gt;
        &lt;span class="n"&gt;lastScannedTimestamp&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;now&lt;/span&gt;
        &lt;span class="nf"&gt;processBarcode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rawValue&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3. Asynchronous Code Generation
&lt;/h3&gt;

&lt;p&gt;Generating QR codes with custom error correction levels (L, M, Q, H) using ZXing can cause perceptible frame drops if executed on the main dispatcher, particularly for large payloads like vCards. I offloaded all BitMatrix calculations and Bitmap rendering to &lt;code&gt;Dispatchers.Default&lt;/code&gt;, passing the finished bitmap to Compose via a state holder.&lt;/p&gt;




&lt;h2&gt;
  
  
  Lessons Learned
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Don't overcomplicate the vision pipeline:&lt;/strong&gt; Google ML Kit's bundled barcode model adds minimal app size while running fully on-device without an active internet connection. It consistently outperformed custom OpenCV pipelines for standard 2D formats.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Haptic feedback matters:&lt;/strong&gt; In high-speed workflows (like scanning asset tags), visual cues on screen aren't enough. Adding subtle, immediate haptic feedback via &lt;code&gt;Vibrator&lt;/code&gt; upon successful parsing drastically improves the operator's scanning rhythm.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Try It Out
&lt;/h2&gt;

&lt;p&gt;Sharp QR is built to do one job with speed and precision, without paywalls or distracting banner ads.&lt;/p&gt;

&lt;p&gt;If you regularly work with QR codes or need a reliable utility for your workflow, you can download &lt;strong&gt;Sharp QR&lt;/strong&gt; on &lt;a href="https://play.google.com/store/apps/details?id=com.getinfotoyou.sharpqr" rel="noopener noreferrer"&gt;Google Play&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;You can also explore more independent utilities built for practical use cases at &lt;a href="https://getinfotoyou.com" rel="noopener noreferrer"&gt;getinfotoyou.com&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>android</category>
      <category>kotlin</category>
      <category>camerax</category>
      <category>mobile</category>
    </item>
    <item>
      <title>Building a Zero-Bloat Android Runner: Object Pooling, Low-Latency Input, and Optimization</title>
      <dc:creator>Getinfo Toyou</dc:creator>
      <pubDate>Fri, 04 Sep 2026 14:31:08 +0000</pubDate>
      <link>https://dev.to/getinfotoyou/building-a-zero-bloat-android-runner-object-pooling-low-latency-input-and-optimization-2lbc</link>
      <guid>https://dev.to/getinfotoyou/building-a-zero-bloat-android-runner-object-pooling-low-latency-input-and-optimization-2lbc</guid>
      <description>&lt;p&gt;Modern mobile gaming often comes with an unspoken tax: 500MB download sizes, endless splash screens, and aggressive background services that drain batteries. When you just want a quick three-minute distraction while standing in line or riding the train, waiting through loading bars and shader pre-compilations ruins the experience.&lt;/p&gt;

&lt;p&gt;I built &lt;strong&gt;Echo Runner&lt;/strong&gt; to solve that specific annoyance. It is a lean, fast-paced endless runner designed to launch instantly, run at a locked 60 frames per second on budget devices, and deliver straightforward arcade reflex gameplay without unnecessary bloat.&lt;/p&gt;

&lt;p&gt;Here is a look at the technical decisions behind Echo Runner, the engineering challenges of low-spec Android optimization, and who gains the most from this approach.&lt;/p&gt;




&lt;h3&gt;
  
  
  Who Benefits Most From This Architecture?
&lt;/h3&gt;

&lt;p&gt;Before diving into the code, it helps to understand the target profile. Echo Runner was designed specifically for two groups:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Commuters and casual mobile gamers on budget or aging hardware:&lt;/strong&gt; Players who do not own flagship phones, have limited storage, or deal with spotty network connections. They need a responsive game that opens in two seconds, consumes minimal battery, and does not hitch when an obstacle appears.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Players who value reflex-driven gameplay over pay-to-win systems:&lt;/strong&gt; Many modern runners introduce artificial speed caps or energy meters that recharge with microtransactions. Echo Runner is built on clean, pure skill progression where level difficulty scales mathematically rather than commercially.&lt;/li&gt;
&lt;/ol&gt;




&lt;h3&gt;
  
  
  The Tech Stack
&lt;/h3&gt;

&lt;p&gt;To balance rapid development with low-level control, I used:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Engine:&lt;/strong&gt; Unity (stripped-down Universal Render Pipeline with all post-processing passes removed except minimal bloom).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Language:&lt;/strong&gt; C# using strictly allocation-free patterns during the main loop.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Target Platform:&lt;/strong&gt; Android (targeting API level 34, backward-compatible to Android 8.0).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Asset Pipeline:&lt;/strong&gt; Low-poly meshes, unlit vertex-colored shaders, and compressed audio buffers to keep total APK download size low.&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  Technical Challenges and Solutions
&lt;/h3&gt;

&lt;h4&gt;
  
  
  1. Eliminating Garbage Collection Spikes
&lt;/h4&gt;

&lt;p&gt;In an endless runner, spawning and destroying platforms, obstacles, and pick-ups dynamically is the standard pattern. In C#, frequent calls to &lt;code&gt;Instantiate()&lt;/code&gt; and &lt;code&gt;Destroy()&lt;/code&gt; trigger the Mono runtime's garbage collector. When the GC runs on low-end Android hardware, it causes noticeable micro-stutter (50-100ms frame drops)—which instantly kills a player in a precision reflex game.&lt;/p&gt;

&lt;p&gt;To fix this, I implemented an aggressive generic object pooling system:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ObjectPool&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;where&lt;/span&gt; &lt;span class="n"&gt;T&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Component&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="n"&gt;Queue&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;_availableObjects&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="n"&gt;T&lt;/span&gt; &lt;span class="n"&gt;_prefab&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="n"&gt;Transform&lt;/span&gt; &lt;span class="n"&gt;_parent&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;ObjectPool&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt; &lt;span class="n"&gt;prefab&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;initialSize&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Transform&lt;/span&gt; &lt;span class="n"&gt;parent&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;_prefab&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;prefab&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="n"&gt;_parent&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;parent&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;initialSize&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;++)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;T&lt;/span&gt; &lt;span class="n"&gt;instance&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Instantiate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_prefab&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_parent&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="n"&gt;instance&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;gameObject&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SetActive&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="n"&gt;_availableObjects&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Enqueue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;instance&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;T&lt;/span&gt; &lt;span class="nf"&gt;Rent&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;T&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;_availableObjects&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Count&lt;/span&gt; &lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="n"&gt;_availableObjects&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Dequeue&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Instantiate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_prefab&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_parent&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;gameObject&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SetActive&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;Return&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;gameObject&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SetActive&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;_availableObjects&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Enqueue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Every track segment, barrier, and power-up is pre-warmed during initial scene setup. During active gameplay, allocations per frame drop to zero bytes.&lt;/p&gt;

&lt;h4&gt;
  
  
  2. Input Latency Calibration
&lt;/h4&gt;

&lt;p&gt;Touch latency on Android varies wildly across manufacturers due to display refresh rates and touch-polling implementations. In a runner game where a lane switch requires split-second timing, queued touch events caused players to feel like controls were sluggish.&lt;/p&gt;

&lt;p&gt;Instead of reading touch inputs in Unity's standard &lt;code&gt;Update()&lt;/code&gt; loop with raw delta checks, I migrated the input stack to Unity's modern Input System, consuming tap and swipe events directly from the hardware queue during &lt;code&gt;FixedUpdate()&lt;/code&gt; sync phases. This cut perceived input lag significantly on 60Hz panels.&lt;/p&gt;




&lt;h3&gt;
  
  
  Lessons Learned
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Profiling on real low-end hardware is mandatory:&lt;/strong&gt; Testing on modern Snapdragon processors hides memory leaks and fill-rate limits. Testing on an entry-level MediaTek chip exposed visual bottlenecks in the particle systems within five minutes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Simplicity beats feature bloat:&lt;/strong&gt; Trimming complex UI menus and third-party analytics SDKs dropped load times from four seconds to under one second.&lt;/li&gt;
&lt;/ol&gt;




&lt;h3&gt;
  
  
  Try It Out
&lt;/h3&gt;

&lt;p&gt;If you want a lightweight, distraction-free arcade game for your daily commute, you can download &lt;strong&gt;Echo Runner&lt;/strong&gt; directly on Google Play:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Google Play Store:&lt;/strong&gt; &lt;a href="https://play.google.com/store/apps/details?id=com.echorunner.game" rel="noopener noreferrer"&gt;Download Echo Runner&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Web &amp;amp; Info:&lt;/strong&gt; &lt;a href="https://echorunner.getinfotoyou.com" rel="noopener noreferrer"&gt;echorunner.getinfotoyou.com&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Feedback on performance across different Android hardware configurations is always welcome in the comments.&lt;/p&gt;

</description>
      <category>android</category>
      <category>gamedev</category>
      <category>unity3d</category>
      <category>performance</category>
    </item>
    <item>
      <title>How I Built a Markdown Editor for Android That Doesn't Squeeze Out Your Productivity</title>
      <dc:creator>Getinfo Toyou</dc:creator>
      <pubDate>Mon, 31 Aug 2026 14:30:36 +0000</pubDate>
      <link>https://dev.to/getinfotoyou/how-i-built-a-markdown-editor-for-android-that-doesnt-squeeze-out-your-productivity-1e28</link>
      <guid>https://dev.to/getinfotoyou/how-i-built-a-markdown-editor-for-android-that-doesnt-squeeze-out-your-productivity-1e28</guid>
      <description>&lt;p&gt;Writing documentation, drafting blog posts, or taking structured notes on a phone is generally a frustrating experience. While desktop markdown editors have evolved to offer smooth, distraction-free environments, mobile tools often force you to choose between a clunky text input area or a heavily restricted UI.&lt;/p&gt;

&lt;p&gt;As a developer, I frequently get ideas for technical posts or need to update repository readmes while away from my desk. The struggle of manually typing backticks, hashes, and brackets on a standard mobile keyboard—coupled with the lack of instant previews—led me to build AIMarkdownPro Editor.&lt;/p&gt;

&lt;p&gt;Here is the story of how I tackled the challenges of mobile markdown editing, the tech stack I chose, and what I learned along the way.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Main Problem: Mobile Friction
&lt;/h3&gt;

&lt;p&gt;The core issue with editing Markdown on mobile is friction. Keyboards aren’t optimized for syntax symbols, and switching back and forth between edit mode and preview mode breaks your writing flow. Furthermore, standard AI writing assistants often strip away your markdown formatting or return poorly structured blocks that require manual cleanup.&lt;/p&gt;

&lt;p&gt;I wanted an app that solved these specific issues:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Quick access to markdown syntax elements.&lt;/li&gt;
&lt;li&gt;A live, side-by-side or tabbed preview that renders correctly.&lt;/li&gt;
&lt;li&gt;An AI assistant that respects and works directly with Markdown structure.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  The Tech Stack
&lt;/h3&gt;

&lt;p&gt;To build a responsive editor, I selected a modern Android stack:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Kotlin&lt;/strong&gt;: For concise and safe code.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Jetpack Compose&lt;/strong&gt;: To build a declarative, fluid UI that adapts easily to different screen sizes and orientations.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Markwon / Flexmark (adapted)&lt;/strong&gt;: For parsing and rendering Markdown to rich text in the preview.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Coroutines&lt;/strong&gt;: To handle parsing and AI generation off the main thread, keeping the UI responsive.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Technical Challenges
&lt;/h3&gt;

&lt;p&gt;Two main challenges arose during development:&lt;/p&gt;

&lt;h4&gt;
  
  
  1. Real-Time Syntax Highlighting
&lt;/h4&gt;

&lt;p&gt;In Jetpack Compose, the &lt;code&gt;TextField&lt;/code&gt; component is powerful but can lag if you attempt heavy parsing on every keystroke. Applying syntax highlighting to the raw editor text in real-time meant writing a highly efficient, incremental parser. I had to throttle the highlighting updates to prevent the keyboard input from stuttering, especially on larger documents.&lt;/p&gt;

&lt;h4&gt;
  
  
  2. AI Integration that Understands Context
&lt;/h4&gt;

&lt;p&gt;Integrating an AI writer meant teaching the assistant to output raw Markdown that fits seamlessly into the user's document. Instead of replacing the entire text, the AI needs to understand the cursor's location and generate contextually relevant headings, lists, or code blocks. This required carefully structured prompts and parsing the stream of incoming tokens to dynamically update the active editing block.&lt;/p&gt;

&lt;h3&gt;
  
  
  Lessons Learned
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Keep it local first&lt;/strong&gt;: Relying entirely on cloud APIs can make a mobile app feel sluggish. Keeping as many editing operations local as possible—and streamlining network calls—is crucial for a smooth user experience.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Keyboard accessory bars are essential&lt;/strong&gt;: Adding a simple helper row above the virtual keyboard for quick symbols like hashes, asterisks, backticks, and brackets reduces the friction of writing Markdown by at least 80%.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Try It Out
&lt;/h3&gt;

&lt;p&gt;If you've ever wanted to write or edit your technical documentation on the go without the usual hassle, you can try AIMarkdownPro Editor. It is available on &lt;a href="https://play.google.com/store/apps/details?id=com.aimarkdownpro.app" rel="noopener noreferrer"&gt;Google Play&lt;/a&gt;. You can also learn more about the project at the &lt;a href="https://aimarkdownpro.getinfotoyou.com" rel="noopener noreferrer"&gt;AIMarkdownPro website&lt;/a&gt;, which is part of my solo developer portfolio at &lt;a href="https://getinfotoyou.com" rel="noopener noreferrer"&gt;getinfotoyou.com&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I'd love to hear how you handle mobile writing workflows and what features you find most helpful.&lt;/p&gt;

</description>
      <category>markdown</category>
      <category>android</category>
      <category>productivity</category>
      <category>showdev</category>
    </item>
    <item>
      <title>Building a High-Performance Batch Image Compressor for Android: Native Bitmap Optimization and Pipeline Challenges</title>
      <dc:creator>Getinfo Toyou</dc:creator>
      <pubDate>Wed, 26 Aug 2026 14:30:30 +0000</pubDate>
      <link>https://dev.to/getinfotoyou/building-a-high-performance-batch-image-compressor-for-android-native-bitmap-optimization-and-2j3</link>
      <guid>https://dev.to/getinfotoyou/building-a-high-performance-batch-image-compressor-for-android-native-bitmap-optimization-and-2j3</guid>
      <description>&lt;p&gt;As a mobile developer, I frequently find myself needing to tweak, resize, or compress image files on the go. While web-based compression tools exist, uploading multi-megapixel source files over cellular data is slow, insecure, and expensive. I wanted a way to process high-resolution photos locally on Android, matching the precision and batch capabilities of desktop software.&lt;/p&gt;

&lt;p&gt;This led me to build ImageSlim Pro. Throughout the process, I focused on addressing the specific needs of three groups of people who rely heavily on mobile media processing: content creators, web designers, and field photographers.&lt;/p&gt;

&lt;h3&gt;
  
  
  Who Benefits Most?
&lt;/h3&gt;

&lt;p&gt;During development, I prioritized features that solve distinct friction points for specific users:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;On-the-go Content Creators&lt;/strong&gt;: When you are uploading to social platforms or publishing articles from the field, bandwidth is precious. Content creators need to reduce file sizes drastically without introducing blocky compression artifacts. By allowing precise control over quality percentage and resolution dials, they can find the sweet spot between visual fidelity and small file sizes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Mobile Web Designers&lt;/strong&gt;: Mobile layouts require optimized WebP or PNG assets. Designers using tablets or phones to manage websites can convert and batch-resize assets to exact pixel dimensions, ensuring fast page load speeds for their clients.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Photographers Sharing Drafts&lt;/strong&gt;: Photographers often need to send quick proofing galleries to clients. Sending raw files is impractical, but using low-quality automated compressors strips away crucial color profiles. ImageSlim Pro preserves EXIF metadata and maintains color integrity while shrinking the file footprint.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  The Technical Stack
&lt;/h3&gt;

&lt;p&gt;To keep the application responsive during heavy batches, I chose:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Kotlin &amp;amp; Jetpack Compose&lt;/strong&gt;: For a lightweight, modern UI.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Kotlin Coroutines &amp;amp; Flow&lt;/strong&gt;: To manage background processing queues and stream real-time progress updates to the UI.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Android NDK (Native Development Kit)&lt;/strong&gt;: Specifically utilizing native libraries for encoding WebP and JPEG format variations, bypassing some of the higher-overhead Java-level Bitmap APIs.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Overcoming the Memory Bottleneck
&lt;/h3&gt;

&lt;p&gt;The biggest technical hurdle was Android's strict memory management. If a user drops twenty 48-megapixel images into a batch queue, decoding them all into memory simultaneously will instantly trigger an Out Of Memory (OOM) crash.&lt;/p&gt;

&lt;p&gt;To solve this, I designed a pipeline that processes images sequentially using a worker queue. Instead of decoding full-resolution bitmaps directly, the app reads the image dimensions first using &lt;code&gt;inJustDecodeBounds = true&lt;/code&gt;. Based on the target output dimensions, it calculates the optimal &lt;code&gt;inSampleSize&lt;/code&gt; to sub-sample the image during the actual decode phase. This keeps the memory footprint low and predictable, even when processing dozens of files.&lt;/p&gt;

&lt;h3&gt;
  
  
  Lessons Learned
&lt;/h3&gt;

&lt;p&gt;Building this app taught me a lot about garbage collection (GC) churn. In early iterations, allocating new byte arrays for every image compression operation caused frequent GC pauses, leading to visible UI stuttering despite using background threads. Transitioning to a reusable byte buffer pool resolved the issue, smoothing out the performance.&lt;/p&gt;

&lt;p&gt;Additionally, I learned the importance of preserving metadata. Stripping EXIF data is easy, but keeping it intact while rebuilding the image structure requires carefully parsing and writing the JPEG APP1 segments.&lt;/p&gt;

&lt;h3&gt;
  
  
  Give it a Try
&lt;/h3&gt;

&lt;p&gt;If you are a photographer, designer, or creator looking for a clean, local-first tool to handle your mobile image processing, you can try the app on the &lt;a href="https://play.google.com/store/apps/details?id=com.getinfotoyou.imageslim.pro" rel="noopener noreferrer"&gt;Google Play Store&lt;/a&gt; or read more about the project at &lt;a href="https://imageslim.getinfotoyou.com" rel="noopener noreferrer"&gt;imageslim.getinfotoyou.com&lt;/a&gt;. You can also view my other projects at &lt;a href="https://getinfotoyou.com" rel="noopener noreferrer"&gt;getinfotoyou.com&lt;/a&gt;. I would love to hear your feedback on the processing pipeline or any features you would like to see added.&lt;/p&gt;

</description>
      <category>android</category>
      <category>kotlin</category>
      <category>performance</category>
      <category>mobile</category>
    </item>
    <item>
      <title>How I Built a Zero-Network Android Image Compressor to Solve the 10MB Photo Problem</title>
      <dc:creator>Getinfo Toyou</dc:creator>
      <pubDate>Mon, 24 Aug 2026 14:30:35 +0000</pubDate>
      <link>https://dev.to/getinfotoyou/how-i-built-a-zero-network-android-image-compressor-to-solve-the-10mb-photo-problem-1cgl</link>
      <guid>https://dev.to/getinfotoyou/how-i-built-a-zero-network-android-image-compressor-to-solve-the-10mb-photo-problem-1cgl</guid>
      <description>&lt;p&gt;Modern phone cameras are incredible, but they have a massive side effect: file size. A single photo taken on a mid-range Android phone can easily range from 6MB to 15MB. While that detail is great for printing posters, it is complete overkill for sharing on chat apps, uploading to a blog, or storing on your phone.&lt;/p&gt;

&lt;p&gt;As my device storage filled up and my cloud storage warnings started popping up, I realized I needed a quick way to shrink my photos. I looked at web-based tools, but I didn't feel comfortable uploading private family photos to random servers just to resize them. I looked at existing apps, but they were bloated with ads, trackers, and demanded internet access.&lt;/p&gt;

&lt;p&gt;So, I decided to build ImageSlim Free, an offline-first Android app designed to solve this exact problem: shrinking image file sizes by up to 90% without sacrificing visible quality.&lt;/p&gt;

&lt;p&gt;Here is how I built it, the technical hurdles I faced, and what I learned along the way.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Core Problem: Mobile Storage and Bandwidth Bloat
&lt;/h3&gt;

&lt;p&gt;When you share a 10MB photo over a weak mobile connection, it takes forever. If you run a self-hosted blog, uploading multiple 10MB images destroys your page load speeds and hikes up your hosting bills. The core value of ImageSlim is simple: let users select one or multiple images, scale them down, compress the bytes, and save them—all in a few seconds, and entirely on-device.&lt;/p&gt;

&lt;h3&gt;
  
  
  Technical Challenges of On-Device Processing
&lt;/h3&gt;

&lt;p&gt;Processing high-resolution images on Android is notoriously difficult due to memory limitations.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;The OutOfMemory (OOM) Trap&lt;/strong&gt;: If you load a 108 megapixel photo directly into Android's memory as a Bitmap, it can require hundreds of megabytes of RAM. Android will instantly kill your app process if you exceed the heap limit.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;UI Thread Blocking&lt;/strong&gt;: Image compression is CPU-intensive. Running it on the main thread freezes the user interface, causing the system to throw an "Application Not Responding" (ANR) dialog.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;To tackle the OOM issue, I used sub-sampling. Instead of loading the full-resolution image into memory just to resize it, I used &lt;code&gt;BitmapFactory.Options&lt;/code&gt; with &lt;code&gt;inJustDecodeBounds = true&lt;/code&gt; to query the image dimensions first. Once I knew the original size, I calculated an appropriate &lt;code&gt;inSampleSize&lt;/code&gt; to decode a downscaled version directly into memory, dramatically reducing the RAM footprint.&lt;/p&gt;

&lt;p&gt;To keep the app responsive, I wrapped the entire compression workflow inside Kotlin Coroutines, specifically utilizing &lt;code&gt;Dispatchers.Default&lt;/code&gt; for CPU-bound tasks. This keeps the UI buttery smooth even when processing a batch of images.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Tech Stack
&lt;/h3&gt;

&lt;p&gt;I wanted the project to be lightweight and modern:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Language&lt;/strong&gt;: Kotlin&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;UI Framework&lt;/strong&gt;: Jetpack Compose. This allowed me to build a clean, minimal interface without the boilerplate of XML layouts.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Concurrency&lt;/strong&gt;: Kotlin Coroutines and Flow to manage background processing states.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Graphics Pipeline&lt;/strong&gt;: Native Android Graphics libraries (&lt;code&gt;android.graphics.Bitmap&lt;/code&gt;), utilizing JPEG/WEBP compression algorithms.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Lessons Learned
&lt;/h3&gt;

&lt;p&gt;Building this app taught me a lot about native memory management. Unlike Java objects, Bitmaps in older Android versions allocated memory in the native heap, but even in modern versions, garbage collection behavior can be unpredictable during heavy image manipulation. Explicitly calling &lt;code&gt;bitmap.recycle()&lt;/code&gt; and ensuring references are cleared as soon as the file is written made a massive difference in stability.&lt;/p&gt;

&lt;p&gt;Additionally, I learned that restricting your app's capabilities can actually be a feature. By choosing not to request the Internet permission (&lt;code&gt;android.permission.INTERNET&lt;/code&gt;) in the manifest, I made it impossible for the app to send data anywhere. This built immediate trust with privacy-conscious users.&lt;/p&gt;

&lt;h3&gt;
  
  
  Try It Out
&lt;/h3&gt;

&lt;p&gt;If you're tired of running out of phone space or waiting for photo uploads to finish, you can download ImageSlim Free on &lt;a href="https://play.google.com/store/apps/details?id=com.getinfotoyou.imageslim.free" rel="noopener noreferrer"&gt;Google Play&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;For more details on the project and other tools, feel free to check out the portfolio page at &lt;a href="https://imageslim.getinfotoyou.com" rel="noopener noreferrer"&gt;imageslim.getinfotoyou.com&lt;/a&gt;. Let me know your thoughts or if you have any questions about the implementation!&lt;/p&gt;

</description>
      <category>android</category>
      <category>kotlin</category>
      <category>performance</category>
      <category>showdev</category>
    </item>
    <item>
      <title>Why I Built an Offline-First Android App to Manage My AI Prompts</title>
      <dc:creator>Getinfo Toyou</dc:creator>
      <pubDate>Wed, 19 Aug 2026 14:30:25 +0000</pubDate>
      <link>https://dev.to/getinfotoyou/why-i-built-an-offline-first-android-app-to-manage-my-ai-prompts-2caa</link>
      <guid>https://dev.to/getinfotoyou/why-i-built-an-offline-first-android-app-to-manage-my-ai-prompts-2caa</guid>
      <description>&lt;p&gt;As developers, we are constantly switching contexts. On any given day, we might be jump-starting a new project, writing tests, or debugging legacy code. Over the last couple of years, large language models (LLMs) like ChatGPT, Claude, and Gemini have become core parts of my daily coding and documentation workflow. But as my usage grew, I noticed a frustrating bottleneck: prompt management.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Problem: Where Did I Put That Prompt?
&lt;/h3&gt;

&lt;p&gt;I started with a fragmented setup. I had a markdown file on my desktop for coding prompts, a note-taking app on my phone for writing prompts, and a few drafts saved in my email. Whenever I needed a specific prompt that had worked perfectly a week ago, I had to search through multiple apps, copy the text, tweak the variables, and paste it into the browser.&lt;/p&gt;

&lt;p&gt;This process was inefficient. I wanted a single, dedicated library on my mobile device—something I could access instantly, search through, and copy from with a single tap. Since I couldn't find a lightweight, privacy-focused solution that fit my needs without unnecessary complexity, I decided to build one myself: AI Prompt Vault.&lt;/p&gt;

&lt;h3&gt;
  
  
  Choosing the Tech Stack
&lt;/h3&gt;

&lt;p&gt;Since speed and privacy were my top priorities, I chose a modern, native Android tech stack:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Language:&lt;/strong&gt; Kotlin, because of its safety features and concise syntax.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;UI Framework:&lt;/strong&gt; Jetpack Compose. Building UIs declaratively allowed me to quickly prototype the interface and handle dynamic states (like search queries and category filters) cleanly.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Local Database:&lt;/strong&gt; Room Database. I wanted the app to be completely offline-first. Your prompts should remain your own data, stored locally on your device.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Dependency Injection:&lt;/strong&gt; Hilt, to keep the codebase clean, modular, and testable.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Technical Challenges
&lt;/h3&gt;

&lt;p&gt;Building a simple utility app doesn't mean there aren't interesting engineering challenges. Three aspects stood out during development:&lt;/p&gt;

&lt;h4&gt;
  
  
  1. Instant Full-Text Search
&lt;/h4&gt;

&lt;p&gt;When you have dozens of prompts, scrolling is too slow. I needed instant search that matches terms in both the prompt title and the body. To achieve this without relying on a backend server, I integrated SQLite's FTS4 (Full-Text Search) module via Room.&lt;/p&gt;

&lt;p&gt;Setting up the virtual FTS table required mapping the database entities correctly to ensure search queries were executed in milliseconds. The result is a highly responsive search bar that filters your prompt library as you type.&lt;/p&gt;

&lt;h4&gt;
  
  
  2. State Management and MVI
&lt;/h4&gt;

&lt;p&gt;In Jetpack Compose, managing UI state across configuration changes (like screen rotation) can get tricky, especially when dealing with active search queries, category selections, and list states. I adopted a Model-View-Intent (MVI) architecture. By exposing a single immutable state flow from the ViewModel, the UI remains predictable. When a user selects a category, it triggers an action that updates the state, which in turn recalculates the filtered list via Room's Flow integration. This means the database is the single source of truth, and the UI reacts instantly to any changes.&lt;/p&gt;

&lt;h4&gt;
  
  
  3. Clean Clipboard Integration
&lt;/h4&gt;

&lt;p&gt;The core user action in the app is copying a prompt to the clipboard. Android handles clipboard access via the &lt;code&gt;ClipboardManager&lt;/code&gt;. However, ensuring a seamless user experience across different Android versions (especially with the clipboard overlay introduced in Android 13) meant I had to carefully handle background threads and system notifications. I implemented a simple one-tap copy gesture that updates the clipboard and gives subtle haptic feedback to confirm the action.&lt;/p&gt;

&lt;h3&gt;
  
  
  Lessons Learned
&lt;/h3&gt;

&lt;p&gt;Building AI Prompt Vault taught me a valuable lesson in product scope. Initially, I wanted to build a cloud sync feature. I planned to use Firebase, set up user authentication, and synchronize prompts across multiple devices.&lt;/p&gt;

&lt;p&gt;But as I talked to potential users (and looked at my own habits), I realized that a cloud database introduced unnecessary friction. Users didn't want to create another account just to store prompts. They valued privacy and speed. By stripping away the cloud synchronization and focusing entirely on a fast, local SQLite database, I delivered a more secure and responsive product.&lt;/p&gt;

&lt;p&gt;I also realized how important it is to design for variable inputs. Often, a prompt is not static—it has placeholders (e.g., &lt;code&gt;[Insert code here]&lt;/code&gt;). Handling dynamic variables within a local database and presenting them in a clean UI is a feature I'm actively refining.&lt;/p&gt;

&lt;h3&gt;
  
  
  Try It Out
&lt;/h3&gt;

&lt;p&gt;If you find yourself copying and pasting the same instructions into LLMs or losing track of your best prompts, you can download AI Prompt Vault on Google Play:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://play.google.com/store/apps/details?id=com.getinfotoyou.aipromptvaultpro" rel="noopener noreferrer"&gt;AI Prompt Vault on Google Play Store&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It is a simple, practical utility designed to make your daily AI interactions a little more productive. I would love to hear your feedback on how you manage your prompts and what features you would find useful.&lt;/p&gt;

</description>
      <category>android</category>
      <category>kotlin</category>
      <category>productivity</category>
      <category>ai</category>
    </item>
  </channel>
</rss>
