开发者
How to open Google Maps in turn-by-turn navigation mode from a PWA (Android)
The next attempt was the standard Maps URL: window . open ( `https://maps.google.com/maps?daddr= ${ lat } , ${ lng } ` , ' _blank ' ); This opens Maps, but in the browser — not the app. And it shows the route preview, not turn-by-turn navigation. What worked: Android Intent URLs Android supports a special URL scheme that tells Chrome to launch a native app directly: window . location . href = `intent://navigation/now?ll= ${ lat } , ${ lng } &title=Next+stop#Intent;scheme=google.navigation;package=com.google.android.apps.maps;end` ; Breaking it down: intent:// — tells Chrome this is an Android intent navigation/now?ll=${lat},${lng} — opens Maps in navigation mode, starting immediately #Intent;scheme=google.navigation — the URI scheme to use package=com.google.android.apps.maps — the target app package end — closes the intent syntax This opens the Google Maps app directly and starts turn-by-turn navigation automatically — no extra taps needed. It also works with Android Auto. The full function export function openNavigation ( destination : { lat : number ; lng : number }): void { window . location . href = `intent://navigation/now?ll= ${ destination . lat } , ${ destination . lng } &title=Next+stop#Intent;scheme=google.navigation;package=com.google.android.apps.maps;end` ; } Call it on any user gesture (tap, click) and it works without being blocked by the browser. The app The full PWA is open source if you want to see the context: 🔗 GitHub repo 🌐 Live app Built with React + TypeScript + Vite + Dexie.js + @vis .gl/react-google-maps. If you're building a PWA that needs to hand off to Google Maps navigation on Android, this intent URL is the cleanest solution I found. Hope it saves you the hour I spent figuring it out.
开发者
Android verification is coming: Google confirms timeline and supported app stores
A new system service will roll out this month ahead of big changes starting in September.
开发者
Testing Real Time Features in Delivery Apps: Maps, Live Tracking, and ETA Updates
The moment a customer taps "Place Order," the most anxiety-driven part of the delivery experience...
AI 资讯
97% of My App's Code Is in commonMain — A Field Report on Shipping 100% Compose Multiplatform
I shipped a small dev-news reader to Google Play with the entire client written in one Compose Multiplatform codebase — every screen in commonMain , no per-platform UI. This is an honest field report on what that actually costs in production: the numbers, the native seams, and the parts that still hurt (hi, iOS). The repo is open source (MIT), so everything here is checkable. TL;DR — For a content/list/detail app, CMP is comfortably production-ready on Android. 96.9% of the shared module is commonMain ; the native cost is concentrated in ~10 expect/actual seams. iOS compiles and renders, but isn't polished yet. The numbers Source set Files Lines Share commonMain 59 ~7,700 96.9% androidMain 2 123 1.6% iosMain 3 127 1.6% All 17 screens live in commonMain — trending list, aggregated feed, README detail, profile with paging, settings, favorites — and there isn't a single if (isAndroid) branch in the UI. The 3% that isn't shared The native cost isn't spread thinly across the codebase. It's concentrated in ~10 expect/actual seams, and this is the entire list: Platform info — app version, system language, User-Agent string System interaction — open URL, open app settings, share sheet Analytics — a trackEvent hook (Android → Aptabase; iOS is deliberately a no-op for now) WebView — the messy one (below) Everything that touches a platform API is small and enumerable. Everything else came for free. Why expect/actual and not an interface + DI? For these ~10 seams, expect/actual was the least ceremony: no DI wiring, and the compiler refuses to build until every target implements the declaration. The moment a seam has more than one implementation, or I'd want to fake it in tests, an interface in commonMain with injected impls is the better tool. For a fixed set of platform primitives, expect/actual wins on friction. The ugliest boundary: WebView I have two WebView paths, and I'll be precise because the repo is open: Rendering GitHub READMEs from an HTML string — inline expect/act
AI 资讯
I reverse-engineered my motorcycle's Bluetooth protocol to put Google Maps on the dashboard
My motorcycle has a Bluetooth instrument cluster. It pairs with the manufacturer's phone app and shows turn-by-turn navigation right on the dash, which sounds great until you actually use it. The nav is routed through a maps provider I don't love, the app is clunky, and there's no way to extend any of it. I kept thinking: it's just my bike talking to my phone over Bluetooth. How locked down can it really be? So one weekend I decided to find out, and a few weeks later I had Google Maps navigation running on the cluster through an app I wrote myself. Here's how that went. There are no docs Of course there aren't. It's a proprietary protocol, and the only reference that exists is the manufacturer's own app, in compiled form. So step one was just watching. I started with a GATT walk on the live bike, which is the Bluetooth equivalent of knocking on every door to see what's there. The cluster exposes one vendor service with two characteristics: one the phone writes to, one the bike sends notifications back on. That's the entire conversation surface. Then I captured the actual bytes going across. Android can log every Bluetooth packet through its HCI snoop log, so I paired the phone with the bike, rode around, and pulled the capture. Now I had real traffic, and absolutely no idea what any of it meant. Reading the app to read the protocol You can stare at hex forever and still guess wrong. The faster path was the app itself. I pulled the APK, ran it through JADX to decompile it, and got something close to readable source. Most of the class names weren't even obfuscated, which was a gift. From there it was cross-referencing: take a message I saw on the wire, find the code that builds it, and work out what each byte is. Frida helped a lot here. It lets you hook a running app and watch functions get called with their real arguments, so I could catch the exact moment the app turned "next turn is a left in 200m" into bytes and shipped them to the bike. Slowly the shape came out
开发者
All the latest news on Android 17, Wear OS 7, and Android XR
Google’s Android 17 update includes highlights like new floating “Bubble” app windows for easier multitasking, a Screen Reaction recording mode, and a 50/50 split gaming mode for foldable phones. Meanwhile, Wear OS 7 brings Live Updates, better battery life for smart watches, and prepares connections for new Android XR smart glasses that will launch this […]
科技前沿
Android 17 starts hitting Pixel phones and watches today
Pixels will get their OTA in the coming weeks, but don't expect monumental changes.
AI 资讯
Android 17 launches with new multitasking tools as Google expands Gemini features
Google has released Android 17 and Wear OS 7, introducing new multitasking features, parental controls, security tools, and smartwatch upgrades. The launch is also accompanied by a Pixel Drop that brings Google’s latest AI models to its devices.
AI 资讯
Android 17 arrives on Pixel phones today
Following its official debut last month, Google is now rolling out Android 17 to compatible Pixel phones, alongside additional exclusive features as part of the June Pixel Drop. Not every feature announced alongside the OS at the pre-I/O Android Show is available today though. Android 17 itself is arriving on Pixel phones today, and Google […]
AI 资讯
The Google / Xreal Aura XR glasses are now available to preorder
The Project Aura glasses collaboration between Xreal and Google is now one step closer to being something you can buy. Reservations for the second Android XR device, now dubbed the Xreal Aura, are available for $99 starting today, with a full launch in the US, UK, Japan, Canada, and South Korea expected sometime this Fall. […]
开发者
How to Test a Food Delivery App: 30 Test Cases from Order to Doorstep
Every food delivery app has the same promise: you tap a button, food shows up at your door. Testing...
AI 资讯
android doze kills your react native background tasks--here's why and how to fix it
android doze kills your background tasks and nobody explains why properly been building a react native app that schedules stuff to run later. worked fine every time i tested it. shipped it, and it started missing schedules. only when the phone had been sitting idle for a while. never on my desk. took me way too long to figure out what was going on so writing it up here. what happens you schedule something for 1am. check logs next morning: 01:00:00 alarm fired 01:00:02 connected (while back grounded, 2 seconds) 01:18:xx the actual send ran the connection came up fine. in 2 seconds. while the phone was back grounded. but the code that was supposed to do something with that connection ran 18 minutes later when something else woke the phone up. why doze mode freezes javascript timers. setTimeout, setInterval, any polling loop on the js thread-all frozen. but native events (connection callbacks, lifecycle events, native module bridges) keep firing. i had a setInterval checking "are we connected yet" every second. doze froze that loop. the connection came up, nobody noticed for 18 minutes because the thing checking for it was asleep. the phone could do the work. my code just couldn't tell. stuff i tried that didn't fix it foreground service — keeps the process alive but doesn't unfreeze js timers. not the problem. more setTimeout/setInterval variations, literally the thing causing it. spent two days making the problem worse. HeadlessJS dropped in without changes compiled, never ran on newer RN. lost a few hours there. the actual fix move everything off timers. put your work directly in the event handler. instead of polling to check if you're connected: js // this is frozen by doze. don't. setInterval (() => { if ( isReady ()) doWork () }, 1000 ) do this : jsconnection . on ( ' status ' , ( state ) => { if ( state === ' connected ' ) { doWork ( job ) } }) native events survive doze. timers don't. that's the whole thing. for waking up at the right time — native AlarmManager
AI 资讯
Shipping an Android podcast player with on-device Whisper — 6 lessons from a Czech indie launch
After a year of nights and weekends, I shipped Lucidcast — an Android podcast player I built because every mainstream app I tried was missing the same three things. The app went live on Google Play this week. This post is for indie devs thinking about shipping their own Android project — six lessons I learned the expensive way. What Lucidcast does (90-second context) A podcast player with on-device AI: Whisper transcribes downloaded episodes locally (audio never leaves the phone) AI episode summaries via Gemini, with a live progress ring "Podcast" wake-word voice commands that work on the lock screen and in Android Auto Smart Pause auto-pauses on loud noises or when someone talks to you Plus the usual: chapters, transcripts, value tags (Podcasting 2.0), 22 languages, Android Auto, live radio, no accounts, no ads. Free includes the full podcast player. Pro one-time unlocks the audio intelligence engine. AI Pack subscription enables Whisper + summaries. Made in EU by a one-person Czech indie team (Prismatic s.r.o.). Play Store | Landing OK, lessons. 1. On-device Whisper is harder than it looks (NDK 29 patch needed) I wanted local transcription so audio doesn't leave the device. whisper_ggml is the only Flutter binding I found that works end-to-end. Catch: it needs Android NDK 29.0.13113456 while most other plugins are still on 27. Setting ndkVersion = "29..." in android/app/build.gradle.kts works for new builds, but the plugin's auto-detected version was sometimes off — needed a small patch script ( flutter/tool/patch_whisper_ggml.sh ) to enforce it during CI. Battery cost is real: I gate transcription to charging-only mode by default with a configurable battery threshold (10-80 %, default 50 %). When the user toggles "transcribe downloaded episodes," I queue the work but only execute when the phone is plugged in. Users on r/podcasts would otherwise notice a 5-10 % overnight battery drain. 2. Sharing the microphone is a contract nobody documents Smart Pause uses noise
AI 资讯
Chinese cybercrime operation that used AI to scam ‘hundreds of thousands of victims’ sued by Google
The tech giant said a group called "Outsider Enterprise" used AI to scam hundreds of thousands of victims, sending 2.5 million text messages over a span of two weeks.
AI 资讯
Google sues alleged Chinese cybercrime operation that used AI to send scam texts
The tech giant said a group called "Outsider Enterprise" used AI to scam hundreds of thousands of victims, sending 2.5 million text messages over a span of two weeks.
AI 资讯
What Does Google Actually Look For During the 14-Day Closed Test?
You’ve spent weeks, maybe months, tracking down bugs, optimizing your user interface, and wrestling with backend security rules. You compile your native release build or run your final production compilations, thinking the hardest part of the journey is officially behind you. Then you open the Google Play Console, and you’re hit with the ultimate indie developer roadblock: the mandatory 12-tester and 14-day closed testing requirement . Many independent creators view this process as a simple download checklist. You might think, "I'll just find 12 people to download the app, leave it on their phones for two weeks, and wait it out." However, treating the testing phase as a static metric is the fastest way to get rejected during the final production access review. So, what is Google actually tracking in the background during these two weeks? Let’s take a deep dive into the core algorithmic requirement that determines your success: Continuous Engagement . 🔄 Decoding "Continuous Engagement" Google Play policies are not designed as a simple box-checking exercise. The underlying goal of the algorithm is to verify if your application is genuinely functional, stable, and being tested by an organic user base before it reaches millions of production users. To enforce this, Google's advanced systems actively monitor the devices connected to your closed test track over the 14-day timeline: Background Device Pings: Google Play Services regularly collects background automated signals (ping logs) from the devices where your test build is active. Real User Interaction: Leaving an app to rot in an application drawer without ever opening it is instantly flagged by the algorithm. Google measures whether the app is actively opened daily and tracks active interaction metrics within the build. Feedback Loops: The system monitors whether your test community is utilizing the internal testing channel on the Play Store to send private developer feedback and crash reports. 📉 The Illusion of "Ju
AI 资讯
Configuring Firebase AI Logic for Android to Use Gemini Models
What device do we use almost all the time? Our mobile phone, almost certainly. If we wanted to...
AI 资讯
Injecting WorkManager into ViewModels with Dagger Hilt. No Context, No Boilerplate, Always a WorkQuery Back
WorkManager is the right tool for deferrable, guaranteed background work in Android. But the default setup pushes you toward boilerplate fast: you end up calling WorkManager.getInstance(context) inside ViewModels, "passing Context where it doesn't belong", re-registering observers scattered across the codebase, and getting no consistent way to query the state of your enqueued work. This tutorial shows how to build a clean, injectable WorkManagerHandler using Dagger Hilt, a single interface that any ViewModel can receive through constructor injection, with zero Context and a guaranteed WorkQuery callback on every call so you always know what to observe. By the end, you'll have: A WorkManager singleton provided through Hilt A custom Configuration.Provider that plugs Hilt's HiltWorkerFactory into WorkManager at initialization A WorkManagerHandler interface with a WorkManagerHandlerImpl that encapsulates enqueueing, chaining, and query registration ViewModels that declare WorkManagerHandler as a plain constructor dependency The pattern scales cleanly as you add workers: each new worker is one method on the handler, and the ViewModel never knows or cares how work is scheduled underneath. Prerequisites : familiarity with Dagger Hilt basics, Jetpack WorkManager fundamentals, and Kotlin coroutines. A working Android project with Hilt already configured is assumed. Part 1: Application Setup and Custom Configuration.Provider By default, WorkManager initializes itself automatically using its own internal factory. The problem is that Hilt-injected workers need Hilt's factory HiltWorkerFactory to resolve their @Inject constructor dependencies. If you let WorkManager self-initialize, your workers won't have access to any of your Hilt bindings. The fix is to disable auto-initialization and take manual control via Configuration.Provider and AndroidManifest.xml 1. Disable auto-initialization In your AndroidManifest.xml , remove WorkManager's default initializer: <application ... > <
开发者
Why Checkout Flows Break More Than Anything Else in Delivery Apps
Every QA team knows the feeling. The home screen works. Browse works. Search works. Cart works. And...
AI 资讯
I Built a Feature That Automatically Switches Android from USB to Wi-Fi — Here's How It Works
All tests run on an 8-year-old MacBook Air. All results from shipping 7 Mac apps as a solo developer. No sponsored opinion. You plug in your Android device. A few seconds later, you unplug the cable. The connection stays alive — wirelessly, automatically, without touching a single setting. That's Seamless Link. The Problem ADB over USB is reliable. ADB over Wi-Fi is convenient. But switching between them manually is friction: Plug in USB Run adb tcpip 5555 Find the device IP Run adb connect <IP>:5555 Unplug cable Every. Single. Time. If you're working with multiple Android devices, or doing this across multiple sessions per day, it adds up fast. What Seamless Link Does The moment you plug in a USB cable, Seamless Link runs that entire flow automatically in the background: Detects the USB connection Runs adb tcpip 5555 Grabs the device IP Establishes a Wi-Fi ADB connection By the time you've sat back down, the device is already connected wirelessly. Pull the cable out — everything keeps working. No manual steps. No IP hunting. No re-running commands every session. Working with Multiple Devices This gets more useful the more devices you have. Plug in Device A → Seamless Link connects it wirelessly. Plug in Device B → same thing, simultaneously. Each device goes through the full handover flow independently, in parallel. The more devices on your desk, the more time this saves. Android 16 Compatibility Android 16 changed how wireless debugging ports are assigned — random ports instead of the fixed 5555. Seamless Link handles this automatically. You don't need to know which port the device is using. If you're on an older Android version, it works the same way it always has. Why This Matters for Daily Workflows If you're an Android developer on Mac, you probably already have a USB cable on your desk. Seamless Link just makes that cable optional after the first few seconds. It's one of those features that's hard to go back from once you've used it. The cable becomes a "char