Files
doormile_milderapp/lib/helpers/miler_router.dart
Thiru-tenext f975344c7d Rename package to com.doormile.miler, bump to 1.2.30+146
Play rejected the upload: the listing expects com.doormile.miler and the build
carried com.doormile.partner. This is a new app rather than an update — a
package rename gives Play a different app, so nothing carries over from the old
listing and versionCode restarts against an empty history.

applicationId moves; `namespace` and the Kotlin `package com.doormile.partner`
declarations deliberately do not. Those are the code's own package and are
allowed to differ from the application id — renaming them would mean moving
source directories to change a string nothing outside the build reads.

Four things did have to follow the id, because each of them names the app to
something outside it:

  * the SHIFT_END_ALARM broadcast action, or two builds installed side by side
    would answer each other's shift alarms;
  * the update checker's androidId in main.dart and UpdateScreen.dart, which
    looks the app up ON PLAY — left stale it would poll a different listing and
    report a rider up to date when he is not;
  * the Play URL the update screen sends him to, which would have opened the
    store page for an app he does not have;
  * the map tile and routing user-agents, which identify this app to OSM and
    OSRM.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EqVJPB9B4QuieZnBAAKgYQ
2026-09-18 16:31:16 +05:30

99 lines
3.8 KiB
Dart

import 'dart:convert';
import 'package:flutter/foundation.dart';
import 'package:http/http.dart' as http;
import 'package:latlong2/latlong.dart' show LatLng;
// ── Routing ─────────────────────────────────────────────────────────────────
/// A driving route between two points, from OSRM's public router.
///
/// Replaces the Directions API call, which needed the revoked key. Same shape
/// of answer — an ordered list of coordinates — so the callers' route drawing,
/// distance table and camera fitting are untouched.
class MilerRouter {
static const String _host = 'https://router.project-osrm.org';
/// Sent with every request. OSRM's demo server, like OSM's tile server,
/// blocks anonymous clients first.
static const String _agent = 'com.doormile.miler';
/// The road geometry from [start] to [end], or an empty list.
///
/// Never throws: a route screen with no line is a degraded screen, but a
/// route screen that crashed is a rider standing in the street with nothing.
/// Callers fall back to the direct line on an empty answer.
static Future<List<LatLng>> route(LatLng start, LatLng end) async {
final uri = Uri.parse(
'$_host/route/v1/driving/'
'${start.longitude},${start.latitude};${end.longitude},${end.latitude}'
'?overview=full&geometries=geojson',
);
try {
final res = await http
.get(uri, headers: const {'User-Agent': _agent})
.timeout(const Duration(seconds: 8));
if (res.statusCode != 200) {
debugPrint(
'[MAP] OSRM ${res.statusCode} — falling back to a direct line.',
);
return const [];
}
final body = jsonDecode(res.body) as Map<String, dynamic>;
if (body['code'] != 'Ok') {
debugPrint('[MAP] OSRM said ${body['code']} — direct line instead.');
return const [];
}
final routes = body['routes'] as List<dynamic>?;
if (routes == null || routes.isEmpty) return const [];
final coords =
(routes.first as Map<String, dynamic>)['geometry']['coordinates']
as List<dynamic>;
// GeoJSON is [longitude, latitude] — the opposite order to everything
// else in this app, and a silent way to end up routing through the sea.
return [
for (final c in coords)
LatLng((c[1] as num).toDouble(), (c[0] as num).toDouble()),
];
} catch (e) {
debugPrint('[MAP] OSRM unreachable ($e) — direct line instead.');
return const [];
}
}
/// Road distance in metres between two points, or null when the router
/// cannot answer.
///
/// Replaces a direct call to the Google Directions API in
/// `PickupsController`, which was reading the same revoked key and therefore
/// silently falling back to straight-line distance on every trip — the number
/// riders are *paid* on.
static Future<double?> roadDistanceMetres(LatLng start, LatLng end) async {
final uri = Uri.parse(
'$_host/route/v1/driving/'
'${start.longitude},${start.latitude};${end.longitude},${end.latitude}'
'?overview=false',
);
try {
final res = await http
.get(uri, headers: const {'User-Agent': _agent})
.timeout(const Duration(seconds: 6));
if (res.statusCode != 200) return null;
final body = jsonDecode(res.body) as Map<String, dynamic>;
if (body['code'] != 'Ok') return null;
final routes = body['routes'] as List<dynamic>?;
if (routes == null || routes.isEmpty) return null;
final d = (routes.first as Map<String, dynamic>)['distance'];
return d is num ? d.toDouble() : null;
} catch (e) {
debugPrint('[ROUTE] OSRM distance unavailable ($e)');
return null;
}
}
}