<?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: EltaCrew</title>
    <description>The latest articles on DEV Community by EltaCrew (@eltacrew).</description>
    <link>https://dev.to/eltacrew</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%2F4065413%2F0cac4ae1-4067-4db9-907e-5cc8ce37fa43.png</url>
      <title>DEV Community: EltaCrew</title>
      <link>https://dev.to/eltacrew</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/eltacrew"/>
    <language>en</language>
    <item>
      <title>I turned my website directory into an offline Android app. Here's the one SQL trick that made it work</title>
      <dc:creator>EltaCrew</dc:creator>
      <pubDate>Sat, 12 Sep 2026 09:45:26 +0000</pubDate>
      <link>https://dev.to/eltacrew/i-turned-my-website-directory-into-an-offline-android-app-heres-the-one-sql-trick-that-made-it-1cag</link>
      <guid>https://dev.to/eltacrew/i-turned-my-website-directory-into-an-offline-android-app-heres-the-one-sql-trick-that-made-it-1cag</guid>
      <description>&lt;p&gt;For about two months I've run a small web directory of useful sites and AI tools at &lt;a href="https://eltacrew.com/sitedex" rel="noopener noreferrer"&gt;eltacrew.com/sitedex&lt;/a&gt;. It's a server-rendered Astro site with Postgres behind it. This week I shipped the same directory as an Android app — and the interesting part isn't the app, it's the constraint I gave myself: &lt;strong&gt;the app must work with the network off, and must show every user the same "today's picks" without asking a server.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Here's how that went.&lt;/p&gt;

&lt;h2&gt;
  
  
  The constraint
&lt;/h2&gt;

&lt;p&gt;A directory app that fetches a list from an API is a web page with extra steps. If the API is slow, the app is slow; if I take the server down, the app is dead. So the data (~800 sites, 9 categories, tags, pricing, descriptions in 5 languages) ships &lt;strong&gt;inside the APK&lt;/strong&gt; as a JSON asset and is loaded into a Room database on first run. Search, filter and browse all hit SQLite locally. The only things that touch the network are the site thumbnails (served from my box) and the ad SDK.&lt;/p&gt;

&lt;p&gt;That gave me a second problem.&lt;/p&gt;

&lt;h2&gt;
  
  
  "Today's picks" without a server
&lt;/h2&gt;

&lt;p&gt;On the web version the home page shuffles the featured sites once a day, so it looks alive but stays stable within the day. The server just does that. In the app there is no server in the loop, and I also wanted this to be a &lt;em&gt;Paging&lt;/em&gt; query — the list is long and I didn't want to load 800 rows to shuffle them in Kotlin.&lt;/p&gt;

&lt;p&gt;So the shuffle has to be an &lt;code&gt;ORDER BY&lt;/code&gt; expression that SQLite can evaluate, seeded by the date, and I need Kotlin to be able to compute the &lt;em&gt;same&lt;/em&gt; ordering (for the detail screen's "next / previous" navigation).&lt;/p&gt;

&lt;p&gt;The seed is just today's date:&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;fun&lt;/span&gt; &lt;span class="nf"&gt;today&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="nc"&gt;Int&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;c&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Calendar&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getInstance&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;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Calendar&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;YEAR&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;10000&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Calendar&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;MONTH&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Calendar&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;DAY_OF_MONTH&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;And the ordering key is a linear congruential hash of &lt;code&gt;(id, seed)&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;1103515245&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;seed&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;12345&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;2147483647&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;key&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;seed&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Int&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="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toLong&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;1103515245L&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt; &lt;span class="n"&gt;seed&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toLong&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;12345L&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;2147483647L&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Two things bit me here, and they're the reason this post exists:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;SQLite has no &lt;code&gt;md5()&lt;/code&gt;, no XOR, no &lt;code&gt;random(seed)&lt;/code&gt;.&lt;/strong&gt; Anything fancier than multiply/add/modulo isn't available in a Room query without a custom function. So the hash is deliberately dumb.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;SQLite integers are 64-bit; Kotlin &lt;code&gt;Int&lt;/code&gt; is 32-bit and wraps.&lt;/strong&gt; My first Kotlin version used &lt;code&gt;Int&lt;/code&gt; and produced a &lt;em&gt;different&lt;/em&gt; order from the SQL, so "next" on the detail screen jumped somewhere random. Do the arithmetic in &lt;code&gt;Long&lt;/code&gt; on the Kotlin side and it matches exactly.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Pull-to-refresh uses a seed derived from the current time instead, so it's a genuinely new shuffle that can never collide with the day's seed.&lt;/p&gt;

&lt;h2&gt;
  
  
  What else is in the app
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Three view modes (mixed cards, list, grid), search, category and pricing filters.&lt;/li&gt;
&lt;li&gt;Five languages (en, ko, ja, zh, es) — the descriptions are bundled, not translated at runtime.&lt;/li&gt;
&lt;li&gt;A submit form that posts to the web directory, so app users and web users feed the same catalogue.&lt;/li&gt;
&lt;li&gt;No login, no account, no billing code. Ads are one banner in a reserved slot plus a dismissible native card on start and exit — no interstitials, app-open or rewarded ads (&lt;code&gt;InterstitialAd&lt;/code&gt;/&lt;code&gt;AppOpenAd&lt;/code&gt;/&lt;code&gt;RewardedAd&lt;/code&gt;: zero occurrences).&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What I'd tell past me
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Bundling the data was the right call. First-run is instant and the app is indifferent to my server's uptime. The cost is that adding sites means shipping an app update — fine for a directory that changes weekly, wrong for anything real-time.&lt;/li&gt;
&lt;li&gt;If Kotlin and SQL both need the same ordering, write the SQL first and make Kotlin imitate it, not the other way around.&lt;/li&gt;
&lt;li&gt;Paging treats an empty table as "loaded, zero rows", not "loading" — so on first run, before the JSON import finished, the list was a blank white screen with no spinner. Gate the list on the import, not on the query.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It went live this week; the web version has been up since July. If you try the app and something's off — especially a site that's dead or miscategorised — the submit form goes straight to me.&lt;/p&gt;

&lt;p&gt;Play Store: &lt;a href="https://play.google.com/store/apps/details?id=com.eltacrew.sitedex" rel="noopener noreferrer"&gt;https://play.google.com/store/apps/details?id=com.eltacrew.sitedex&lt;/a&gt;&lt;br&gt;
Web: &lt;a href="https://eltacrew.com/sitedex" rel="noopener noreferrer"&gt;https://eltacrew.com/sitedex&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;&lt;em&gt;I'm the developer of both the site and the app.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>android</category>
      <category>showdev</category>
      <category>kotlin</category>
      <category>sqlite</category>
    </item>
    <item>
      <title>I read 13,861 reviews of 5 countdown / D-day apps. Half the anger is about one feature being put behind a paywall</title>
      <dc:creator>EltaCrew</dc:creator>
      <pubDate>Sat, 12 Sep 2026 09:45:00 +0000</pubDate>
      <link>https://dev.to/eltacrew/i-read-13861-reviews-of-5-countdown-d-day-apps-half-the-anger-is-about-one-feature-being-put-4100</link>
      <guid>https://dev.to/eltacrew/i-read-13861-reviews-of-5-countdown-d-day-apps-half-the-anger-is-about-one-feature-being-put-4100</guid>
      <description>&lt;p&gt;I just shipped a small exam-countdown app for Android called ExamDay. The category ("D-day" apps, as they're called in Korea) is old and crowded, so before writing the listing I pulled the public reviews of the five most-installed countdown apps on Google Play and read everything rated 1–3★.&lt;/p&gt;

&lt;p&gt;This is about the category, not my app. If you're building anything with a home-screen widget, the middle section is the part to read.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I looked at
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;App&lt;/th&gt;
&lt;th&gt;Installs&lt;/th&gt;
&lt;th&gt;Rating&lt;/th&gt;
&lt;th&gt;Text reviews&lt;/th&gt;
&lt;th&gt;1–3★&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;TheDayBefore&lt;/td&gt;
&lt;td&gt;10M+&lt;/td&gt;
&lt;td&gt;4.58 (112k)&lt;/td&gt;
&lt;td&gt;4,222&lt;/td&gt;
&lt;td&gt;459&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Countdown Widget&lt;/td&gt;
&lt;td&gt;10M+&lt;/td&gt;
&lt;td&gt;4.16 (135k)&lt;/td&gt;
&lt;td&gt;3,122&lt;/td&gt;
&lt;td&gt;998&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Time Until&lt;/td&gt;
&lt;td&gt;5M+&lt;/td&gt;
&lt;td&gt;4.55 (50k)&lt;/td&gt;
&lt;td&gt;3,017&lt;/td&gt;
&lt;td&gt;615&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Days To&lt;/td&gt;
&lt;td&gt;1M+&lt;/td&gt;
&lt;td&gt;4.66 (28k)&lt;/td&gt;
&lt;td&gt;3,002&lt;/td&gt;
&lt;td&gt;771&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Days counter&lt;/td&gt;
&lt;td&gt;1M+&lt;/td&gt;
&lt;td&gt;4.61 (7k)&lt;/td&gt;
&lt;td&gt;498&lt;/td&gt;
&lt;td&gt;54&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Method: &lt;code&gt;google-play-scraper&lt;/code&gt; (Python), &lt;code&gt;en&lt;/code&gt; + &lt;code&gt;ko&lt;/code&gt;, newest first, up to 3,000 per language per app. Play only returns reviews that have text. 2,897 reviews rated 1–3★, keyword-bucketed, sorted by count and by "helpful" votes.&lt;/p&gt;

&lt;h2&gt;
  
  
  The complaints, ranked
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;#&lt;/th&gt;
&lt;th&gt;Complaint&lt;/th&gt;
&lt;th&gt;Reviews&lt;/th&gt;
&lt;th&gt;👍 helpful votes&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;Too many ads&lt;/td&gt;
&lt;td&gt;858&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;1,451&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;Widget (paywalled, doesn't refresh, wrong size)&lt;/td&gt;
&lt;td&gt;541&lt;/td&gt;
&lt;td&gt;1,016&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;Paywall / subscription&lt;/td&gt;
&lt;td&gt;448&lt;/td&gt;
&lt;td&gt;1,105&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;Got worse after an update&lt;/td&gt;
&lt;td&gt;187&lt;/td&gt;
&lt;td&gt;540&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;Too complicated for a countdown&lt;/td&gt;
&lt;td&gt;190&lt;/td&gt;
&lt;td&gt;211&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;Notifications forced&lt;/td&gt;
&lt;td&gt;95&lt;/td&gt;
&lt;td&gt;208&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;7&lt;/td&gt;
&lt;td&gt;Status bar / lock screen&lt;/td&gt;
&lt;td&gt;43&lt;/td&gt;
&lt;td&gt;58&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;(A "date calculation" bucket also scored high, but it's mostly the word "count" matching — I only quote reviews I read.)&lt;/p&gt;

&lt;p&gt;Buckets 1, 3 and 4 are largely the &lt;em&gt;same&lt;/em&gt; story told three ways.&lt;/p&gt;

&lt;h2&gt;
  
  
  The one-time purchase that became $15 a week
&lt;/h2&gt;

&lt;p&gt;Countdown Widget has the lowest rating in the set (4.16) and the most 1★ reviews (841). Almost all of the upvoted ones say this (70, 62, 51, 47, 38, 33 👍):&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"I previously paid to unlock the premium features as it used to be a one time payment. Now I've lost what I've had all this time, am slammed with ads non-stop, and to get the premium version back, I have to pay $14.99 a week?!?"&lt;/p&gt;

&lt;p&gt;"THEY'RE TRYING TO SIGN PEOPLE UP FOR $780/YEAR!!! …for a simple timer."&lt;/p&gt;

&lt;p&gt;"ridiculous amount of ads. edit for dev: of course the paid version doesn't have ads. it's also 18 dollars a week. for a countdown."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Whatever you think about pricing, the mechanics are instructive: users who had &lt;em&gt;paid&lt;/em&gt; were moved to the free tier, and the free tier got heavier ads at the same time. That's where the 👍 comes from — not the price, the reversal.&lt;/p&gt;

&lt;h2&gt;
  
  
  Widgets: the whole point, and the thing that breaks
&lt;/h2&gt;

&lt;p&gt;Days To (47, 32, 27 👍):&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"I downloaded this app to set up a widget, only to find that having a widget is a 'premium' feature. I'd say the widget aspect is pretty integral to having a countdown on your phone."&lt;/p&gt;

&lt;p&gt;"It's a lovely app with beautiful background options… My only complaint is it doesn't have widgets."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Time Until (43, 32 👍):&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"I paid for this. What happened to the widget? It's useless now. The whole point of the countdown is to be able to see the countdown. The widget does not refresh anymore. You got to open and 'apply' each time."&lt;/p&gt;

&lt;p&gt;"Homescreen widget 'literally never' loads or works anymore… It's been broken for like a year or more"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;And the most-upvoted review in the whole dataset (89 👍), also Time Until: the widget doesn't honour the display options, custom image placement, or scale its text when the widget is enlarged.&lt;/p&gt;

&lt;p&gt;Nobody is asking for much. They want the number on the home screen, updated at midnight, in the size they chose.&lt;/p&gt;

&lt;h2&gt;
  
  
  Ads, specifically
&lt;/h2&gt;

&lt;p&gt;TheDayBefore (79 👍): "the app itself seems great, but I just can't stand the ads, they pop up way too often and you have to actually watch it for 20+ seconds to close it." Time Until (37 👍): "every three taps, I get another ad… My phone is set to mute. The full screen ads when opening completely ignored that." Days To (21 👍): "Creating a countdown takes 3 or 4 steps, and there is an advertisement after each."&lt;/p&gt;

&lt;h2&gt;
  
  
  Small things people cared about
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Can't create a countdown &lt;em&gt;without&lt;/em&gt; a reminder (Time Until, 24 👍).&lt;/li&gt;
&lt;li&gt;Full-screen view is partially hidden under the status bar; long event names break mid-word (24 👍).&lt;/li&gt;
&lt;li&gt;Wants a &lt;em&gt;recurring&lt;/em&gt; daily countdown ("time until wake up, 6 am every day") (25 👍).&lt;/li&gt;
&lt;li&gt;Korean review: D-17 works but D+17 (days since) doesn't (4 👍).&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What I took from this
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Don't paywall the widget.&lt;/strong&gt; In a countdown app it isn't a premium feature, it's the product. Three of the five apps got their worst reviews for exactly this.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Never move paying users down.&lt;/strong&gt; The reversal is what people upvote.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ads in a countdown app have almost no surface to hide behind&lt;/strong&gt; — the app is opened for three seconds. A full-screen ad is 100% of the session.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The widget must update by itself at midnight&lt;/strong&gt; and survive reboots; "open the app and press apply" is a bug, not a workaround.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  What ExamDay does about it (and what it doesn't)
&lt;/h2&gt;

&lt;p&gt;Only things you can check in the app or listing:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Widgets are free and built in&lt;/strong&gt; — a single-exam widget and a multi-exam widget (grid or list), both configurable from the home screen. No premium tier exists; there is no billing code in the app.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ads:&lt;/strong&gt; one banner in a reserved slot plus a dismissible native card at start and on exit. &lt;code&gt;InterstitialAd&lt;/code&gt;, &lt;code&gt;AppOpenAd&lt;/code&gt;, &lt;code&gt;RewardedAd&lt;/code&gt;: zero occurrences. The banner is folded away on onboarding, the notification explainer and the full-screen view.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No login, no server of ours.&lt;/strong&gt; Backup is a single file you export and import yourself.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reminders are opt-in&lt;/strong&gt; (D-30/7/3/1/day, with a "Not now" on the explainer) and scheduled with &lt;code&gt;setExactAndAllowWhileIdle&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The day count is computed from midnight to midnight and rounded&lt;/strong&gt;, so the 23- and 25-hour DST days don't shift it by one (unit-tested — the #1 app in this list has an old review about exactly that bug).&lt;/li&gt;
&lt;li&gt;21 exam presets, photo backgrounds (5 built-in or your gallery), full-screen view, study timer, stats, spaced-repetition review reminders (1/3/7/15 days).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;What it &lt;em&gt;doesn't&lt;/em&gt; have yet: sharing a countdown as an image, recurring countdowns, calendar import, and a persistent status-bar countdown — all things people asked for above. It shipped this week, so I can't yet tell you how reliably the widget refreshes on every launcher; if it doesn't on yours, tell me.&lt;/p&gt;

&lt;p&gt;Play Store: &lt;a href="https://play.google.com/store/apps/details?id=com.eltacrew.examday" rel="noopener noreferrer"&gt;https://play.google.com/store/apps/details?id=com.eltacrew.examday&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Data collected 2026-09-12 with google-play-scraper. Quotes are verbatim from public Google Play reviews; Korean ones are my translation. I'm the developer of ExamDay; competitor names identify the public listings analysed.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>android</category>
      <category>showdev</category>
      <category>ux</category>
      <category>mobile</category>
    </item>
    <item>
      <title>I read 18,464 reviews of 6 habit tracker apps. The #1 upvoted complaint is something none of them fixed</title>
      <dc:creator>EltaCrew</dc:creator>
      <pubDate>Sat, 12 Sep 2026 09:43:52 +0000</pubDate>
      <link>https://dev.to/eltacrew/i-read-18464-reviews-of-6-habit-tracker-apps-the-1-upvoted-complaint-is-something-none-of-them-go9</link>
      <guid>https://dev.to/eltacrew/i-read-18464-reviews-of-6-habit-tracker-apps-the-1-upvoted-complaint-is-something-none-of-them-go9</guid>
      <description>&lt;p&gt;I just released a small habit tracker for Android called HabitDeck. Before writing its store listing I wanted to know what people actually complain about in the apps that already have millions of installs — not what I &lt;em&gt;assumed&lt;/em&gt; they complain about. So I pulled every public review I could for six of the biggest habit trackers on Google Play and read the 1–3★ ones.&lt;/p&gt;

&lt;p&gt;This is about the category. If you're building or choosing a habit app, the ranking below is the useful part.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I looked at
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;App&lt;/th&gt;
&lt;th&gt;Installs&lt;/th&gt;
&lt;th&gt;Rating&lt;/th&gt;
&lt;th&gt;Text reviews&lt;/th&gt;
&lt;th&gt;1–3★&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Habit Tracker – Habit Diary&lt;/td&gt;
&lt;td&gt;10M+&lt;/td&gt;
&lt;td&gt;4.75 (135k)&lt;/td&gt;
&lt;td&gt;3,119&lt;/td&gt;
&lt;td&gt;225&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;HabitNow&lt;/td&gt;
&lt;td&gt;5M+&lt;/td&gt;
&lt;td&gt;4.73 (93k)&lt;/td&gt;
&lt;td&gt;3,226&lt;/td&gt;
&lt;td&gt;230&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Loop Habit Tracker&lt;/td&gt;
&lt;td&gt;5M+&lt;/td&gt;
&lt;td&gt;4.71 (63k)&lt;/td&gt;
&lt;td&gt;3,567&lt;/td&gt;
&lt;td&gt;216&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Goal &amp;amp; Habit Tracker Calendar&lt;/td&gt;
&lt;td&gt;5M+&lt;/td&gt;
&lt;td&gt;4.77 (52k)&lt;/td&gt;
&lt;td&gt;3,582&lt;/td&gt;
&lt;td&gt;164&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Routinery (Routine Planner)&lt;/td&gt;
&lt;td&gt;1M+&lt;/td&gt;
&lt;td&gt;4.52 (18k)&lt;/td&gt;
&lt;td&gt;3,746&lt;/td&gt;
&lt;td&gt;858&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;HabitKit&lt;/td&gt;
&lt;td&gt;500k+&lt;/td&gt;
&lt;td&gt;4.60 (12k)&lt;/td&gt;
&lt;td&gt;1,224&lt;/td&gt;
&lt;td&gt;227&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Method: &lt;code&gt;google-play-scraper&lt;/code&gt; (Python), &lt;code&gt;en&lt;/code&gt; + &lt;code&gt;ko&lt;/code&gt;, newest first, up to 3,000 per language per app. Play only returns reviews with text. I keyword-bucketed the 1,920 reviews rated 1–3★ and sorted by count and by "helpful" votes.&lt;/p&gt;

&lt;h2&gt;
  
  
  The complaints, ranked
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;#&lt;/th&gt;
&lt;th&gt;Complaint&lt;/th&gt;
&lt;th&gt;Reviews&lt;/th&gt;
&lt;th&gt;👍 helpful votes&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;Paywall / subscription&lt;/td&gt;
&lt;td&gt;450&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;1,197&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;Notifications don't fire (or fire wrong)&lt;/td&gt;
&lt;td&gt;267&lt;/td&gt;
&lt;td&gt;779&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;Crashes / slow to open&lt;/td&gt;
&lt;td&gt;171&lt;/td&gt;
&lt;td&gt;525&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;Lost data / backup&lt;/td&gt;
&lt;td&gt;149&lt;/td&gt;
&lt;td&gt;368&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;Widget&lt;/td&gt;
&lt;td&gt;141&lt;/td&gt;
&lt;td&gt;240&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;Confusing / can't do X&lt;/td&gt;
&lt;td&gt;98&lt;/td&gt;
&lt;td&gt;319&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;7&lt;/td&gt;
&lt;td&gt;Ads&lt;/td&gt;
&lt;td&gt;100&lt;/td&gt;
&lt;td&gt;246&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;8&lt;/td&gt;
&lt;td&gt;Tracking / privacy&lt;/td&gt;
&lt;td&gt;52&lt;/td&gt;
&lt;td&gt;184&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Two things surprised me.&lt;/p&gt;

&lt;h2&gt;
  
  
  The single most-upvoted review is about editing yesterday
&lt;/h2&gt;

&lt;p&gt;119 👍, Korean, on Loop (the open-source one, 5M+ installs):&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"If I forget to check or check the wrong thing and the day changes, the previous day isn't shown, so I can't check it or fix it. Annoying. And you can't set a 7-day view — it only shows 4 days."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Nothing else in 18,464 reviews got close to that vote count. It's not a crash, not a paywall — it's &lt;em&gt;I missed a tap at 11:58 pm and now my record is wrong forever.&lt;/em&gt; Every habit app has a streak; not every habit app lets you repair it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Paywall complaints are mostly about not being told
&lt;/h2&gt;

&lt;p&gt;Routinery's low ratings are dominated by one story (45 👍):&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"No indication that you're on a 7-day trial. I had to find out why my routines were 'archived.' On the second day of using this app, I'm prompted to resubscribe for a membership plan to have more than two routines."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;A Korean review of Habit Diary (29 👍): after ignoring the premium prompt for a few days, they could no longer &lt;em&gt;create&lt;/em&gt; habits. And a fair one (42 👍): "Problem is the cost, 20 USD per year? I pay less for far more comprehensive apps."&lt;/p&gt;

&lt;p&gt;The pattern isn't "free users hate paying." It's &lt;em&gt;the limit appeared after I'd already invested days of data.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Notifications: the thing a habit app exists for
&lt;/h2&gt;

&lt;p&gt;HabitNow, 47 👍, written at 3:19 am:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"DO NOT USE THE ALARMS!!! …I paid for the premium version, but I'm writing this at 3:19 in the morning after my 10:30pm alarm has yet again gone off hours differently"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Routinery, 30 👍: "notifications not working (they only pop up when you open the app, so don't actually notify you when it's time to start a routine)". Goal &amp;amp; Habit Tracker, 60 👍: the reminder sound options they'd used since 2016 were removed in an update.&lt;/p&gt;

&lt;p&gt;Android has made exact, background alarms progressively harder (Doze, exact-alarm permission, OEM battery killers). Apps that schedule with &lt;code&gt;setRepeating&lt;/code&gt; or a &lt;code&gt;WorkManager&lt;/code&gt; job drift or get dropped; users experience it as "the app forgot me."&lt;/p&gt;

&lt;h2&gt;
  
  
  Fifteen seconds to tick a box
&lt;/h2&gt;

&lt;p&gt;Habit Diary, 55 and 51 👍:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"I love this app and UI, but the load time is atrocious. It's a full 15 seconds every single time I open the app for the first time that day."&lt;/p&gt;

&lt;p&gt;"marking them as done HAS to be a lightweight task and not something I have to spend 5-10 seconds staring at a loading screen (and then an ad, but…)"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The dev's reply blamed connectivity — which tells you the app waits on a network round-trip before it lets you tap a checkbox.&lt;/p&gt;

&lt;h2&gt;
  
  
  Widgets and backups, briefly
&lt;/h2&gt;

&lt;p&gt;Widget: "Widgets freeze often. Those are the main reason I use the app" (Loop, 18 👍); a request to &lt;em&gt;invert&lt;/em&gt; the check widget so undone habits are the bright ones (36 👍). Backup: "App crashes whenever I try to sign in. Extremely frustrating, since I'm paying for online backups" (Routinery, 16 👍).&lt;/p&gt;

&lt;h2&gt;
  
  
  What I took from this
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Let people fix yesterday.&lt;/strong&gt; The most-agreed-with complaint in the category is a missing edit affordance.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;If there's a limit, show it on day one, not day three.&lt;/strong&gt; The anger is about surprise, not price.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A habit app's notification is its product.&lt;/strong&gt; Schedule each alarm individually with exact timing and reschedule after it fires; anything periodic gets dropped on real phones.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;The tap that marks a habit done must not wait on the network.&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  What HabitDeck does about it (and what it doesn't)
&lt;/h2&gt;

&lt;p&gt;Only things you can check in the app or listing:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;No subscription, no billing code, no habit-count limit.&lt;/strong&gt; Everything in the app is free, and there is no "trial" state to fall out of.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No login, no server of ours.&lt;/strong&gt; Data is a local Room database; there's no network wait before the home screen. The only network traffic is the ad SDK.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ads:&lt;/strong&gt; one banner in a reserved slot plus a dismissible native card at start and exit. &lt;code&gt;InterstitialAd&lt;/code&gt;, &lt;code&gt;AppOpenAd&lt;/code&gt;, &lt;code&gt;RewardedAd&lt;/code&gt;: zero occurrences. The banner is folded away on the onboarding, notification-explainer and PIN screens.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reminders&lt;/strong&gt; are scheduled one at a time with &lt;code&gt;setExactAndAllowWhileIdle&lt;/code&gt; and re-armed after each fires — no &lt;code&gt;setRepeating&lt;/code&gt;, no &lt;code&gt;WorkManager&lt;/code&gt;. It asks for exact-alarm and battery-optimisation exemption up front and explains why before the system dialog.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Backup is a single file you export/import yourself&lt;/strong&gt; — no account, free.&lt;/li&gt;
&lt;li&gt;7 tracker types (water, sleep, mood, quit-smoking, counters…), calendar + stats, focus timer, PIN lock.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;What it &lt;em&gt;doesn't&lt;/em&gt; have yet, honestly: &lt;strong&gt;no home-screen widget&lt;/strong&gt;, and &lt;strong&gt;you can't edit a past day&lt;/strong&gt; — the calendar is read-only. That second one is the #1 complaint above, and it's the next thing I'm building. The app shipped this week, so I have no stability data to quote. If it breaks, tell me.&lt;/p&gt;

&lt;p&gt;Play Store: &lt;a href="https://play.google.com/store/apps/details?id=com.eltacrew.habitdeck" rel="noopener noreferrer"&gt;https://play.google.com/store/apps/details?id=com.eltacrew.habitdeck&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Data collected 2026-09-12 with google-play-scraper. Quotes are verbatim from public Google Play reviews; Korean ones are my translation. I'm the developer of HabitDeck; competitor names identify the public listings analysed.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>android</category>
      <category>showdev</category>
      <category>productivity</category>
      <category>ux</category>
    </item>
    <item>
      <title>I read 30,118 reviews of 7 phone-sensor apps (compass, ruler, sound meter…). The top complaint isn't accuracy</title>
      <dc:creator>EltaCrew</dc:creator>
      <pubDate>Sat, 12 Sep 2026 09:43:49 +0000</pubDate>
      <link>https://dev.to/eltacrew/i-read-30118-reviews-of-7-phone-sensor-apps-compass-ruler-sound-meter-the-top-complaint-pk5</link>
      <guid>https://dev.to/eltacrew/i-read-30118-reviews-of-7-phone-sensor-apps-compass-ruler-sound-meter-the-top-complaint-pk5</guid>
      <description>&lt;p&gt;I just shipped a small Android app called MeasureKit — nine phone-sensor tools in one place (compass, ruler, sound meter, magnifier, bubble level, clinometer, vibration meter, protractor, light meter). Before deciding what to put on its first Play Store screenshot, I wanted to know what people actually hate about the apps that already own this category. So I pulled every public review I could get for the most-installed app in each tool's niche and read the low-rated ones.&lt;/p&gt;

&lt;p&gt;This post is about the &lt;em&gt;category&lt;/em&gt;, not my app. If you build anything that turns a phone sensor into a "measurement", the pattern below will look familiar.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I looked at
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Tool&lt;/th&gt;
&lt;th&gt;App&lt;/th&gt;
&lt;th&gt;Installs&lt;/th&gt;
&lt;th&gt;Rating&lt;/th&gt;
&lt;th&gt;Text reviews&lt;/th&gt;
&lt;th&gt;1–3★&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Compass&lt;/td&gt;
&lt;td&gt;Digital Compass&lt;/td&gt;
&lt;td&gt;10M+&lt;/td&gt;
&lt;td&gt;4.66 (616k)&lt;/td&gt;
&lt;td&gt;6,000&lt;/td&gt;
&lt;td&gt;765&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Ruler&lt;/td&gt;
&lt;td&gt;Ruler (nixgame)&lt;/td&gt;
&lt;td&gt;10M+&lt;/td&gt;
&lt;td&gt;4.37 (115k)&lt;/td&gt;
&lt;td&gt;4,411&lt;/td&gt;
&lt;td&gt;556&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Sound meter&lt;/td&gt;
&lt;td&gt;Sound meter: SPL &amp;amp; dB&lt;/td&gt;
&lt;td&gt;5M+&lt;/td&gt;
&lt;td&gt;4.62 (53k)&lt;/td&gt;
&lt;td&gt;3,271&lt;/td&gt;
&lt;td&gt;294&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Magnifier&lt;/td&gt;
&lt;td&gt;Magnifier + Flashlight&lt;/td&gt;
&lt;td&gt;10M+&lt;/td&gt;
&lt;td&gt;4.86 (242k)&lt;/td&gt;
&lt;td&gt;6,000&lt;/td&gt;
&lt;td&gt;187&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Bubble level&lt;/td&gt;
&lt;td&gt;Bubble Level (gamma)&lt;/td&gt;
&lt;td&gt;10M+&lt;/td&gt;
&lt;td&gt;4.69 (334k)&lt;/td&gt;
&lt;td&gt;6,000&lt;/td&gt;
&lt;td&gt;435&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Vibration&lt;/td&gt;
&lt;td&gt;Vibration Meter (sira)&lt;/td&gt;
&lt;td&gt;5M+&lt;/td&gt;
&lt;td&gt;4.39 (36k)&lt;/td&gt;
&lt;td&gt;3,118&lt;/td&gt;
&lt;td&gt;368&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Light meter&lt;/td&gt;
&lt;td&gt;Light Meter - Lux Meter&lt;/td&gt;
&lt;td&gt;1M+&lt;/td&gt;
&lt;td&gt;4.78 (17k)&lt;/td&gt;
&lt;td&gt;1,316&lt;/td&gt;
&lt;td&gt;86&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Two more (a protractor and a clinometer) had one review each, so they're out. Method, so you can reproduce it: &lt;code&gt;google-play-scraper&lt;/code&gt; (Python), languages &lt;code&gt;en&lt;/code&gt; + &lt;code&gt;ko&lt;/code&gt;, newest first, up to 3,000 per language per app. Play only returns reviews that have text, so those are text reviews, not total ratings. I then bucketed the 2,693 reviews rated 1–3★ by keyword and sorted the buckets two ways: by count, and by how many other people clicked "helpful" on them.&lt;/p&gt;

&lt;h2&gt;
  
  
  The complaints, ranked
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;#&lt;/th&gt;
&lt;th&gt;Complaint&lt;/th&gt;
&lt;th&gt;Reviews&lt;/th&gt;
&lt;th&gt;👍 helpful votes&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;Too many ads&lt;/td&gt;
&lt;td&gt;482&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;1,147&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;Not accurate&lt;/td&gt;
&lt;td&gt;223&lt;/td&gt;
&lt;td&gt;567&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;Scale / units wrong&lt;/td&gt;
&lt;td&gt;136&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;868&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;Confusing / can't get out&lt;/td&gt;
&lt;td&gt;76&lt;/td&gt;
&lt;td&gt;198&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;Calibration / sensor&lt;/td&gt;
&lt;td&gt;58&lt;/td&gt;
&lt;td&gt;345&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;Privacy / tracking&lt;/td&gt;
&lt;td&gt;37&lt;/td&gt;
&lt;td&gt;337&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;7&lt;/td&gt;
&lt;td&gt;Paywall&lt;/td&gt;
&lt;td&gt;31&lt;/td&gt;
&lt;td&gt;180&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;(The keyword classifier is crude — a "crash" bucket got 453 votes from a single Korean compass review that is really about accuracy. I only quote reviews I read myself.)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Accuracy is not the #1 complaint for a category whose entire job is being accurate.&lt;/strong&gt; Ads are, by count and by votes. And the ad complaints aren't "there are ads" — they're about a specific trick.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Ads that cover the Android navigation buttons
&lt;/h2&gt;

&lt;p&gt;The most-upvoted English review in the whole set (136 👍), on the sound meter:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Once opened, you can't exit the app. Once opened, all the usual Android buttons and menus disappeared."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;And the next three, same app (81, 72, 71 👍):&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"It purposely obscures access to the android soft buttons, uses intrusive ad tactics to increase your 'interaction'…"&lt;/p&gt;

&lt;p&gt;"The ads run right on top of the buttons for the back button and the home screen."&lt;/p&gt;

&lt;p&gt;"…positioning the ads into the screen space that the bottom bar, where all the close app controls are, along with a very short reclose time is kind of devious"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;On the bubble level (42 👍): "the ads pop up every time my screen turns off. There is no close button so I have to hit back." A level is something you hold with one hand against a shelf. Turning the screen off &lt;em&gt;is&lt;/em&gt; the use case.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. The ruler that needs a ruler
&lt;/h2&gt;

&lt;p&gt;The single most-upvoted review in the dataset is Korean, on the ruler (192 👍):&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"It looks really convenient with all the fitting functions, but the divisions don't set themselves per phone, so if you want to use it you need an actual ruler anyway. I wonder if the people leaving reviews had 1 cm set correctly, lol."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That's the whole problem with a screen ruler in one sentence: Android reports a nominal DPI, real panels differ, and if the app doesn't make calibration obvious, every measurement is off by a few percent and the user finds out at the worst time. Another one (71 👍) tried to calibrate against a real 4-inch mark and found the 1, 2, 3 inch marks still slightly misaligned — the calibration UI itself was imprecise.&lt;/p&gt;

&lt;p&gt;Tablet users notice it most (15 👍): "on a tablet the size is way off."&lt;/p&gt;

&lt;h2&gt;
  
  
  3. The sound meter floor
&lt;/h2&gt;

&lt;p&gt;61 👍:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"It doesn't matter how quiet it doesn't display below around 30–35 dB. Makes me wonder if it's a calibration issue or a compatibility issue with my phone or if my phone's mic is just bad."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This is a real physical limit — phone mics and their AGC don't go much lower — but the app gave no hint, so the user blamed themselves and the app equally.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Privacy consent as a wall
&lt;/h2&gt;

&lt;p&gt;124 👍 on the ruler, 51 and 39 on the compass:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"It takes over half an hour to manually go through and uncheck every data tracking company that wants to track your data through this app."&lt;/p&gt;

&lt;p&gt;"Privacy nightmare! It's a decent compass but I don't appreciate several hundred companies claiming they have a 'legitimate interest' in tracking me… you have to disable each individually."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;A compass. It needs one sensor and zero network. The users know that, which is why the reaction is so strong.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Units and background
&lt;/h2&gt;

&lt;p&gt;Vibration meter (64 👍): "Uses Mercalli instead of any unit of measure of amplitude or frequency. Completely useless." And (50 👍): "does not work in background, once cellphone is in standby state screen goes dark, the app does not detect anymore."&lt;/p&gt;

&lt;h2&gt;
  
  
  What I took from this
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;In a tool app, the ad placement matters more than the ad count.&lt;/strong&gt; Nobody in 2,693 low reviews complained about a banner in its own slot. They complained about ads over the back button, ads on screen-off, and ads with a 1-second close delay.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Say what the sensor can't do, on the screen.&lt;/strong&gt; The ruler and sound meter reviews are people discovering a hardware limit with no warning. A one-line "phone mics aren't calibrated instruments — adjust the offset in settings" would have turned a 3★ into a shrug.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Calibration has to be a first-class feature, not a hidden menu.&lt;/strong&gt; The ruler's top complaint in two languages is the same thing.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  What MeasureKit does about it (and what it doesn't)
&lt;/h2&gt;

&lt;p&gt;Only things you can verify in the app or the listing:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;No interstitial, app-open or rewarded ads.&lt;/strong&gt; &lt;code&gt;InterstitialAd&lt;/code&gt;, &lt;code&gt;AppOpenAd&lt;/code&gt;, &lt;code&gt;RewardedAd&lt;/code&gt;: zero occurrences in the codebase. There is one banner in a reserved slot at the bottom of the screen — it never overlays the tool or the system buttons — plus a dismissible native card at start and on exit.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No subscription, no billing code, no login, no server of ours.&lt;/strong&gt; The only network traffic is the ad SDK.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Permissions are per tool and optional:&lt;/strong&gt; mic (sound), camera (magnifier), location (compass — true north, sunrise/sunset, altitude; the compass works without it). Deny one and only that tool shows an in-screen note.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ruler calibration is in Settings and on the ruler screen itself&lt;/strong&gt; (a CAL button), with cm and fractional-inch scales.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Sound meter shows dB(A)&lt;/strong&gt; with a user offset, and the screen says outright that a phone mic is not a calibrated instrument.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Vibration meter reports m/s² and g&lt;/strong&gt; (RMS, peak, 8-hour limit), not just a Mercalli word.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Compass shows a sensor-accuracy warning&lt;/strong&gt; and only applies true-north correction when it actually has a location.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;What it &lt;em&gt;doesn't&lt;/em&gt; do: it won't measure vibration with the screen off (no foreground service), it's a screen ruler, not a 3-metre tape, and its sensor readings are only as good as your phone's — same as everyone else's. It shipped this week, so I have no stability data worth quoting. If it breaks for you, tell me; that's the data I don't have.&lt;/p&gt;

&lt;p&gt;Play Store: &lt;a href="https://play.google.com/store/apps/details?id=com.eltacrew.measurekit" rel="noopener noreferrer"&gt;https://play.google.com/store/apps/details?id=com.eltacrew.measurekit&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Data collected 2026-09-12 with google-play-scraper. Quotes are verbatim from public Google Play reviews; the Korean ones are my translation. I'm the developer of MeasureKit; competitor names are used only to identify the public listings analysed.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>android</category>
      <category>showdev</category>
      <category>ux</category>
      <category>mobile</category>
    </item>
    <item>
      <title>I read 524 reviews of 5 AdMob dashboard apps. Here's what people actually complain about</title>
      <dc:creator>EltaCrew</dc:creator>
      <pubDate>Fri, 11 Sep 2026 13:53:26 +0000</pubDate>
      <link>https://dev.to/eltacrew/i-read-524-reviews-of-5-admob-dashboard-apps-heres-what-people-actually-complain-about-2ooc</link>
      <guid>https://dev.to/eltacrew/i-read-524-reviews-of-5-admob-dashboard-apps-heres-what-people-actually-complain-about-2ooc</guid>
      <description>&lt;p&gt;I build a small AdMob/AdSense revenue dashboard for Android called AdDeck. Last week I needed to decide what to put on its first Play Store screenshot, and I didn't want to guess. So I pulled every public review I could get for the five most-installed AdMob dashboard apps on Google Play and read the low-rated ones, one by one.&lt;/p&gt;

&lt;p&gt;This post is the result. It's mostly about the &lt;em&gt;category&lt;/em&gt;, not my app — if you're building or choosing any tool that touches your ad revenue, the pattern is worth knowing.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I looked at
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;App&lt;/th&gt;
&lt;th&gt;Installs&lt;/th&gt;
&lt;th&gt;Rating&lt;/th&gt;
&lt;th&gt;Text reviews collected&lt;/th&gt;
&lt;th&gt;1★&lt;/th&gt;
&lt;th&gt;2★&lt;/th&gt;
&lt;th&gt;3★&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Dash For AdMob&lt;/td&gt;
&lt;td&gt;50,000+&lt;/td&gt;
&lt;td&gt;4.55 (897 ratings)&lt;/td&gt;
&lt;td&gt;283&lt;/td&gt;
&lt;td&gt;51&lt;/td&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;td&gt;15&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AdMobile – AdMob Metrics&lt;/td&gt;
&lt;td&gt;50,000+&lt;/td&gt;
&lt;td&gt;4.63 (950 ratings)&lt;/td&gt;
&lt;td&gt;201&lt;/td&gt;
&lt;td&gt;16&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;8&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AdMob Dashboard (sirmaur)&lt;/td&gt;
&lt;td&gt;1,000+&lt;/td&gt;
&lt;td&gt;–&lt;/td&gt;
&lt;td&gt;23&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Ad Earnings – Admob Dashboard&lt;/td&gt;
&lt;td&gt;1,000+&lt;/td&gt;
&lt;td&gt;–&lt;/td&gt;
&lt;td&gt;12&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AdMoola&lt;/td&gt;
&lt;td&gt;1,000+&lt;/td&gt;
&lt;td&gt;–&lt;/td&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Method, so you can reproduce it: &lt;code&gt;google-play-scraper&lt;/code&gt; (Python), languages &lt;code&gt;en&lt;/code&gt; + &lt;code&gt;ko&lt;/code&gt;, sort by newest, all pages. Only reviews with text are returned by Play, so the counts above are text reviews, not total ratings. I then classified the 103 reviews rated 1–3★ by keyword into eight buckets and summed the "helpful" votes (👍) on each.&lt;/p&gt;

&lt;p&gt;Two apps carry almost all the signal — Dash For AdMob and AdMobile. The other three simply don't have enough reviews to say anything.&lt;/p&gt;

&lt;h2&gt;
  
  
  The eight complaints, ranked
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;#&lt;/th&gt;
&lt;th&gt;Complaint&lt;/th&gt;
&lt;th&gt;Reviews&lt;/th&gt;
&lt;th&gt;👍 helpful votes&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;Crashes / won't open&lt;/td&gt;
&lt;td&gt;21&lt;/td&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;Infinite loading / data never shows&lt;/td&gt;
&lt;td&gt;14&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;40&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;Too many ads&lt;/td&gt;
&lt;td&gt;11&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;48&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;Trust / privacy&lt;/td&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;td&gt;7&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;Paywall / subscription&lt;/td&gt;
&lt;td&gt;7&lt;/td&gt;
&lt;td&gt;8&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;Numbers don't match the console&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;8&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;7&lt;/td&gt;
&lt;td&gt;Feature requests (widget, CTR, same-name apps)&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;8&lt;/td&gt;
&lt;td&gt;Session expires / re-login&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Counting by &lt;em&gt;number of reviews&lt;/em&gt;, crashes win. Counting by &lt;em&gt;how many other people clicked "helpful"&lt;/em&gt;, the ranking flips: &lt;strong&gt;ads and loading problems are what the silent majority agreed with.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What each bucket actually says
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;1. Crashes.&lt;/strong&gt; Most of these cluster around one update in April 2026 ("crashing on launch after update… check 16kb page size"), plus a steady drip of "keeps closing" across 2024–2025. This is the cost of an app that shipped years ago and is maintained in bursts. Nothing to learn here except: the category is old.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Infinite loading.&lt;/strong&gt; The single most-upvoted review in the whole dataset (21 👍) is:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"…it has been unable to load anything besides their own ads. It will not display my own in[formation]."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;And the second (6 👍):&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"I have reported it for more than 1 year, the monthly and yearly report only shows loading forever."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;3. Too many ads.&lt;/strong&gt; This is the bucket with the most helpful votes (48), and the reviews are almost funny in their consistency:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Littered with ads so people can see their ad revenue. Not for me."&lt;/p&gt;

&lt;p&gt;"The ads are too much now, UX is dead. Implement ads tastefully."&lt;/p&gt;

&lt;p&gt;"Interstitial ads are not organic. Find another way to use them or remove them." (11 👍)&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Think about the user here. They are a developer who earns money from ads. They open an app to check that money. And the app makes them watch a full-screen video ad first. Of course they hate it — it's their own business model pointed back at them.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Trust and privacy.&lt;/strong&gt; The apps ask for Google account access, and a meaningful number of users refuse:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"I can't give you full permission of my google Account. I can't trust this type of publisher."&lt;/p&gt;

&lt;p&gt;"After registration I started receiving spam emails."&lt;/p&gt;

&lt;p&gt;"Disaster application asking for personal information too." (AdMobile)&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Some of these are misunderstandings (AdMob's reporting API &lt;em&gt;does&lt;/em&gt; need an OAuth grant). But the fear is rational: this is financial data, and the user cannot see where it goes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;5. Paywall.&lt;/strong&gt; AdMobile's low ratings are mostly this:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"You have to buy a subscription to see anything useful. Google's Admob mobile website is much better."&lt;/p&gt;

&lt;p&gt;"Basic functionalities as premium paid features."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;And a Korean Dash For AdMob review describes being charged ₩13,000/year the moment they tapped "payment settings" while trying to cancel.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;6. Wrong numbers.&lt;/strong&gt; "Always shows 100% match rate while AdMob says 54.98%." "Not showing the accurate earnings." Small bucket, but it's the scariest one for a dashboard.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;7–8. The rest.&lt;/strong&gt; People want a home-screen widget, CTR as a metric, and a way to tell an Android and iOS app with the same name apart. Sessions expiring daily annoy a couple of people.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I took from this
&lt;/h2&gt;

&lt;p&gt;Three things, in order of how sure I am:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;An ad-revenue app must not be ad-heavy.&lt;/strong&gt; The users are ad professionals. Full-screen and video ads in this category are self-sabotage, and the helpful-vote count says the community agrees.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Trust has to be visible, not claimed.&lt;/strong&gt; "We respect your privacy" means nothing. What users can actually verify: the OAuth scope the app asks for, whether there's a server, whether there's a subscription.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Crashes and loading are the symptoms of age.&lt;/strong&gt; I can't promise my app won't crash — it has ~10 installs and I have no crash data worth quoting. What I &lt;em&gt;can&lt;/em&gt; do is not repeat the choices above.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  What AdDeck does about it (and what it doesn't)
&lt;/h2&gt;

&lt;p&gt;I'll keep this to things you can check in the app or on the Play listing, not adjectives.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;No interstitial or video ads.&lt;/strong&gt; The app has a native ad and a banner. That's it. (&lt;code&gt;InterstitialAd&lt;/code&gt;, &lt;code&gt;AppOpenAd&lt;/code&gt;, &lt;code&gt;RewardedAd&lt;/code&gt;: zero occurrences in the codebase.)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No subscription, no paywall.&lt;/strong&gt; There is no billing code in the app. Everything is free.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Read-only OAuth scopes only&lt;/strong&gt; — &lt;code&gt;admob.readonly&lt;/code&gt; and &lt;code&gt;adsense.readonly&lt;/code&gt;. The app cannot change anything in your account.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No server of ours.&lt;/strong&gt; The only hosts the app talks to are &lt;code&gt;*.googleapis.com&lt;/code&gt;. Tokens and data stay on the device.&lt;/li&gt;
&lt;li&gt;Widget, CTR, per-app ranking, mediation totals — the things people asked for above — are in.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;What I &lt;em&gt;can't&lt;/em&gt; claim: that it never crashes, that it's fast, or that its numbers are more accurate than anyone else's. It reads the same AdMob and AdSense reporting APIs everyone else does, and it's too new to have meaningful stability data. If you try it and it breaks, tell me — that's exactly the data I don't have yet.&lt;/p&gt;

&lt;p&gt;Play Store: &lt;a href="https://play.google.com/store/apps/details?id=com.eltacrew.addeck" rel="noopener noreferrer"&gt;https://play.google.com/store/apps/details?id=com.eltacrew.addeck&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Data collected 2026-09-11 with google-play-scraper. Review quotes are verbatim from public Google Play reviews. I'm the developer of AdDeck; the competitor apps' names are used only to identify the public listings analyzed.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>android</category>
      <category>admob</category>
      <category>monetization</category>
      <category>showdev</category>
    </item>
    <item>
      <title>How to track ad revenue across AdMob, AdSense and mediation networks in one dashboard</title>
      <dc:creator>EltaCrew</dc:creator>
      <pubDate>Mon, 07 Sep 2026 10:56:20 +0000</pubDate>
      <link>https://dev.to/eltacrew/how-to-track-ad-revenue-across-admob-adsense-and-mediation-networks-in-one-dashboard-1glo</link>
      <guid>https://dev.to/eltacrew/how-to-track-ad-revenue-across-admob-adsense-and-mediation-networks-in-one-dashboard-1glo</guid>
      <description>&lt;p&gt;If you monetize apps or sites with more than one ad network, you've probably hit this wall: your revenue is real, but it's &lt;strong&gt;scattered&lt;/strong&gt;. AdMob reports one number, AdSense another, and your mediation partners — AppLovin, Unity, Meta Audience Network — each have their own console. There's no single place that tells you what you actually earned yesterday.&lt;/p&gt;

&lt;p&gt;This post walks through why that happens, the options for pulling it together, and the trade-offs of each.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why ad revenue ends up scattered
&lt;/h2&gt;

&lt;p&gt;Each network is its own product with its own reporting API and its own definition of a "day":&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;AdMob&lt;/strong&gt; reports mobile ad revenue, and can also surface mediation earnings &lt;em&gt;if&lt;/em&gt; you route those networks through AdMob mediation.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AdSense&lt;/strong&gt; is a separate product for web/content, with a separate login and API.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Mediation networks&lt;/strong&gt; each keep their own dashboard, and their totals don't always match what AdMob attributes to them (different time zones, different rounding, reporting lag).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The result: to answer "what did I make yesterday, across everything?" you open several tabs and add it up by hand. Do that every morning and it gets old fast.&lt;/p&gt;

&lt;h2&gt;
  
  
  Option 1: A spreadsheet
&lt;/h2&gt;

&lt;p&gt;The zero-cost starting point. Export CSVs from each console, paste into one sheet, sum.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Pros:&lt;/strong&gt; free, full control, works with any network that offers an export.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cons:&lt;/strong&gt; manual, error-prone, and always a day behind. Time zones and currency conversion bite you. It doesn't scale past a couple of networks.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Option 2: The network APIs + your own script
&lt;/h2&gt;

&lt;p&gt;Each major network exposes a reporting API (AdMob has the AdMob API; AdSense has the AdSense Management API; most mediation networks have reporting endpoints). You can pull them on a schedule and combine.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Pros:&lt;/strong&gt; automated, exactly the fields you want.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cons:&lt;/strong&gt; you're now maintaining OAuth flows, token refresh, rate limits, and a place to run it. That's real engineering time for something that isn't your product.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A few things to get right if you go this route:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Use read-only scopes.&lt;/strong&gt; For revenue, you only need read access. Never request a scope that can modify account settings.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Normalize time zones&lt;/strong&gt; before you sum. AdMob's "today" and a mediation network's "today" can differ by hours.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Expect reporting lag.&lt;/strong&gt; Same-day numbers are estimates and get revised. Don't treat the last data point as final.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Option 3: A dashboard that does the merge for you
&lt;/h2&gt;

&lt;p&gt;If you don't want to build and babysit the pipeline, a purpose-built dashboard reads the networks for you and presents one combined view. The things worth checking before you connect your accounts:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;What OAuth scope does it request?&lt;/strong&gt; Revenue reporting only needs &lt;em&gt;read&lt;/em&gt; access. If a tool asks for write scopes, ask why.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Where does your data live?&lt;/strong&gt; Some tools pull your revenue into their own servers. An on-device app that never routes data through a third-party server has a much smaller blast radius.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Can you revoke it easily?&lt;/strong&gt; OAuth access should be revocable from your Google account in one click.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  A concrete example
&lt;/h2&gt;

&lt;p&gt;I built a small Android app called &lt;strong&gt;AdDeck&lt;/strong&gt; for exactly this, and it's a useful illustration of Option 3 done conservatively:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It connects with a &lt;strong&gt;read-only OAuth scope&lt;/strong&gt; — it can read revenue and nothing else.&lt;/li&gt;
&lt;li&gt;It runs &lt;strong&gt;100% on-device&lt;/strong&gt;: numbers go from the ad networks straight to your phone, with no backend in the middle, so no server is sitting on your earnings data.&lt;/li&gt;
&lt;li&gt;It shows &lt;strong&gt;today's total, a per-app ranking with day-over-day change, and the month-to-date trend&lt;/strong&gt;, and lets you tap any app for its own 7-day trend plus eCPM, CTR and fill rate. A home-screen widget shows the number without opening the app.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It's free on Android: &lt;a href="https://play.google.com/store/apps/details?id=com.eltacrew.addeck" rel="noopener noreferrer"&gt;https://play.google.com/store/apps/details?id=com.eltacrew.addeck&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The takeaway
&lt;/h2&gt;

&lt;p&gt;There's no single "right" answer — a spreadsheet is fine for two networks, a script makes sense if reporting is core to your workflow, and a dashboard saves time if you'd rather not maintain one. Whichever you pick, the two rules that matter are: &lt;strong&gt;read-only access&lt;/strong&gt;, and &lt;strong&gt;know where your revenue data is stored&lt;/strong&gt;. Everything else is convenience.&lt;/p&gt;

</description>
      <category>android</category>
      <category>analytics</category>
      <category>webdev</category>
      <category>startup</category>
    </item>
    <item>
      <title>I got tired of opening four ad dashboards every morning, so I built one</title>
      <dc:creator>EltaCrew</dc:creator>
      <pubDate>Mon, 07 Sep 2026 10:54:23 +0000</pubDate>
      <link>https://dev.to/eltacrew/i-got-tired-of-opening-four-ad-dashboards-every-morning-so-i-built-one-1jg9</link>
      <guid>https://dev.to/eltacrew/i-got-tired-of-opening-four-ad-dashboards-every-morning-so-i-built-one-1jg9</guid>
      <description>&lt;p&gt;I run a handful of small Android apps on the side. The code is the fun part. The part that quietly wore me down was the reporting.&lt;/p&gt;

&lt;p&gt;My ad income lands in a few different places — AdMob, AdSense, and mediation networks like AppLovin and Unity. Every morning I'd open three or four dashboards, log into each one, line up the dates, and add the numbers in my head just to answer one dumb question: &lt;strong&gt;how much did I make yesterday?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;So I built &lt;strong&gt;AdDeck&lt;/strong&gt; to answer that on one screen.&lt;/p&gt;

&lt;h2&gt;
  
  
  What it does
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Connect once with a read-only OAuth scope&lt;/strong&gt;, and it merges every network's revenue: today's total, a per-app ranking with each app's day-over-day change, and the month-to-date trend.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tap any app&lt;/strong&gt; for its own dashboard — earnings, a 7-day trend, and the metrics that actually drive decisions (eCPM, CTR, fill rate).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A home-screen widget&lt;/strong&gt; shows yesterday's number without opening the app. This is the part I use most.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Two things I refused to compromise on
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Read-only.&lt;/strong&gt; The OAuth scope only reads revenue reports. AdDeck can't change your account settings or touch payouts, and you can revoke access anytime from your Google account. When you're asking someone to connect their ad accounts, "what's the worst this app can do?" has to have a boring answer.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;No backend of mine.&lt;/strong&gt; A lot of tools like this pull your data into their own servers and process it there. AdDeck doesn't. The numbers go from the ad networks straight to your device, and the math happens on-device. Nothing routes through a server I control — which means I have no way to look at your revenue, and I'm not sitting on a pile of other people's earnings data.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where it's at
&lt;/h2&gt;

&lt;p&gt;It's free and Android-only for now. Early, honest state: it does the merge, the per-app drill-down, and the widget well; the network list is still growing.&lt;/p&gt;

&lt;p&gt;If you run apps across multiple networks, two questions I'd love answers to:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Which ad network should I support next?&lt;/li&gt;
&lt;li&gt;If the widget could show exactly one number, which would you want — yesterday's earnings, month-to-date, or progress toward a target?&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Play Store: &lt;a href="https://play.google.com/store/apps/details?id=com.eltacrew.addeck" rel="noopener noreferrer"&gt;https://play.google.com/store/apps/details?id=com.eltacrew.addeck&lt;/a&gt;&lt;/p&gt;

</description>
      <category>androiddev</category>
      <category>indiehackers</category>
      <category>showdev</category>
      <category>mobile</category>
    </item>
    <item>
      <title>md to html without uploading your files: a browser-based converter that doesn't choke on Mermaid or KaTeX</title>
      <dc:creator>EltaCrew</dc:creator>
      <pubDate>Mon, 10 Aug 2026 23:49:42 +0000</pubDate>
      <link>https://dev.to/eltacrew/md-to-html-without-uploading-your-files-a-browser-based-converter-that-doesnt-choke-on-mermaid-or-1h3a</link>
      <guid>https://dev.to/eltacrew/md-to-html-without-uploading-your-files-a-browser-based-converter-that-doesnt-choke-on-mermaid-or-1h3a</guid>
      <description>&lt;p&gt;Every few weeks I need to hand a Markdown document to someone who doesn't live in a text editor. A README for a client, meeting notes, a short spec with a couple of diagrams. And every time, the same two problems showed up:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Anything beyond plain GFM breaks.&lt;/strong&gt; The moment a document contains a Mermaid diagram or LaTeX math, a lot of the converters I tried give up. You get a code fence where a flowchart should be, or raw &lt;code&gt;$...$&lt;/code&gt; soup in the middle of a paragraph.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The documents I convert are the ones I least want to upload.&lt;/strong&gt; Internal notes, drafts, client material. A lot of online converters process your files on their servers, and for work documents that always made me uncomfortable.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;So I built a small tool that fixes both for me: &lt;a href="https://eltacrew.com/mdhtml/" rel="noopener noreferrer"&gt;a free md ↔ html converter&lt;/a&gt; that runs entirely in your browser. No install, no sign-up, and your files are never uploaded — conversion happens client-side.&lt;/p&gt;

&lt;h2&gt;
  
  
  What it does
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Both directions.&lt;/strong&gt; Markdown → HTML and HTML → Markdown, on the same page. The HTML → Markdown side accepts a &lt;code&gt;.html&lt;/code&gt; file, pasted HTML source, content copied straight from a web page, or a URL.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;GFM support&lt;/strong&gt;: tables, task lists, strikethrough.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Code blocks&lt;/strong&gt; come out syntax-highlighted.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Mermaid diagrams and KaTeX math actually render.&lt;/strong&gt; The rendering engines are lazy-loaded only when your document uses them — a plain Markdown file never triggers those downloads at all.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Batch conversion&lt;/strong&gt;: drop several files at once, then download results individually or grab everything as one ZIP.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Print / save as PDF&lt;/strong&gt; with print-friendly styling, straight from the preview.&lt;/li&gt;
&lt;li&gt;UI is available in English and Korean.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The part I actually built it for: single-file HTML export
&lt;/h2&gt;

&lt;p&gt;My least favorite thing about sharing converted Markdown used to be the &lt;code&gt;images/&lt;/code&gt; folder. You send the HTML, forget the folder, and the recipient gets a page full of broken image icons.&lt;/p&gt;

&lt;p&gt;Here's how the tool handles it: drop your &lt;code&gt;.md&lt;/code&gt; files &lt;strong&gt;and&lt;/strong&gt; your image files into the page together. The converter matches images referenced in the Markdown by filename, encodes each one as a base64 data URI, and embeds it directly into the generated HTML.&lt;/p&gt;

&lt;p&gt;The output is one self-contained &lt;code&gt;.html&lt;/code&gt; file. You can email it, drop it into a chat, or archive it, and the images display offline — no asset folder to zip alongside, nothing to fetch from a server later.&lt;/p&gt;

&lt;p&gt;One trade-off worth knowing: base64 encoding makes embedded images take more bytes than the original files, so a screenshot-heavy document produces a noticeably chunkier HTML file. For the documents I share, that trade has been worth it every time.&lt;/p&gt;

&lt;h2&gt;
  
  
  Honest limitations
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;The tool is client-side with &lt;strong&gt;one exception&lt;/strong&gt;: the optional "import from URL" feature. Browsers can't fetch arbitrary cross-origin pages, so that single path goes through a small proxy on my server. That is the only point where anything touches a server — if you never use URL import, nothing does. I'd rather say that plainly than have someone discover it in the network tab.&lt;/li&gt;
&lt;li&gt;It's a converter, not an editor or a static site generator. It converts the documents you give it; it won't build a site, navigation, or a theme around them.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Wrapping up
&lt;/h2&gt;

&lt;p&gt;The link is up top if you want to poke at it. It's free and there's nothing to sign up for, so the cost of trying it is one browser tab.&lt;/p&gt;

&lt;p&gt;If you convert Markdown to HTML (or back) regularly — what's the thing that most often breaks for you? Footnotes, front matter, deeply nested lists, tables with inline code? I'd genuinely like to know what to test against next.&lt;/p&gt;

</description>
      <category>markdown</category>
      <category>webdev</category>
      <category>showdev</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Reading Markdown on Android was worse than it should be, so I built a viewer</title>
      <dc:creator>EltaCrew</dc:creator>
      <pubDate>Thu, 06 Aug 2026 07:42:52 +0000</pubDate>
      <link>https://dev.to/eltacrew/reading-markdown-on-android-was-worse-than-it-should-be-so-i-built-a-viewer-1pl8</link>
      <guid>https://dev.to/eltacrew/reading-markdown-on-android-was-worse-than-it-should-be-so-i-built-a-viewer-1pl8</guid>
      <description>&lt;p&gt;Markdown is the default format for notes, docs and READMEs, but reading it on Android is oddly painful. Most apps I tried would break one of: tables, LaTeX, code highlighting, or wiki links. I wanted GitHub-quality rendering in my pocket.&lt;/p&gt;

&lt;p&gt;So I built MdViewer. The parts I care about most:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Rendering correctness.&lt;/strong&gt; Tables, task lists, inline/block LaTeX, syntax highlighting for common languages, images and &lt;code&gt;[[wiki links]]&lt;/code&gt; all render the way desktop tools show them.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Folder workflow.&lt;/strong&gt; You point it at folders (your Obsidian vault, a notes dir, a cloned repo) and it lists every &lt;code&gt;.md&lt;/code&gt; inside. Pull to refresh.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Offline GitHub import.&lt;/strong&gt; Type &lt;code&gt;owner/repo&lt;/code&gt; and it downloads the repo's &lt;code&gt;.md&lt;/code&gt; files for offline reading. Private repos work with a token.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Small extras that add up:&lt;/strong&gt; heading outline, in-doc search, highlights, bookmarks, PDF/HTML export, an editor with live preview, on-device OCR.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;It is free (ads) and Android only:&lt;br&gt;
&lt;a href="https://play.google.com/store/apps/details?id=com.eltacrew.mdviewer" rel="noopener noreferrer"&gt;https://play.google.com/store/apps/details?id=com.eltacrew.mdviewer&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you find a file it renders wrong, I genuinely want to know.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Disclosure: I am the developer.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>android</category>
      <category>markdown</category>
      <category>showdev</category>
      <category>productivity</category>
    </item>
  </channel>
</rss>
