first commit

This commit is contained in:
Hamza-Ayed
2026-10-04 00:19:45 +03:00
commit 05f1c9ec6b
93 changed files with 10782 additions and 0 deletions
+103
View File
@@ -0,0 +1,103 @@
/// أدوات تنسيق الوقت والمسافة والسرعة
class Formatters {
/// Format seconds to HH:MM:SS or MM:SS
static String time(int seconds) {
int h = seconds ~/ 3600;
int m = (seconds % 3600) ~/ 60;
int s = seconds % 60;
String pad(int n) => n.toString().padLeft(2, '0');
if (h > 0) return '${pad(h)}:${pad(m)}:${pad(s)}';
return '${pad(m)}:${pad(s)}';
}
/// Format meters to km or m
static String distance(double meters) {
if (meters >= 1000) {
return '${(meters / 1000).toStringAsFixed(2)} km';
}
return '${meters.toStringAsFixed(0)} m';
}
/// Format distance short (e.g., "5.2")
static String distanceKm(double meters) {
return (meters / 1000).toStringAsFixed(2);
}
/// Format speed (m/s) to pace (min/km)
static String pace(double mps) {
if (mps <= 0) return '--:--';
double kmh = mps * 3.6;
if (kmh <= 0) return '--:--';
double paceMinKm = 60.0 / kmh;
int min = paceMinKm.toInt();
int sec = ((paceMinKm - min) * 60).toInt();
return '${min.toString().padLeft(2, '0')}:${sec.toString().padLeft(2, '0')}';
}
/// Format pace with unit
static String paceWithUnit(double mps) {
return '${pace(mps)} min/km';
}
/// Format speed m/s to km/h
static String speedKmh(double mps) {
return '${(mps * 3.6).toStringAsFixed(1)} km/h';
}
/// Format calories
static String calories(double cal) {
return '${cal.toStringAsFixed(0)} kcal';
}
/// Format elevation
static String elevation(double meters) {
return '${meters.toStringAsFixed(0)} m';
}
/// Format date
static String date(DateTime dt) {
final months = [
'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',
'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec',
];
return '${dt.day} ${months[dt.month - 1]} ${dt.year}';
}
/// Format time of day
static String timeOfDay(DateTime dt) {
String pad(int n) => n.toString().padLeft(2, '0');
return '${pad(dt.hour)}:${pad(dt.minute)}';
}
/// Format date and time
static String dateTime(DateTime dt) {
return '${date(dt)} ${timeOfDay(dt)}';
}
/// Format duration in human readable form
static String durationHuman(int seconds) {
if (seconds < 60) return '${seconds}s';
if (seconds < 3600) return '${seconds ~/ 60}m ${seconds % 60}s';
int h = seconds ~/ 3600;
int m = (seconds % 3600) ~/ 60;
return '${h}h ${m}m';
}
/// Get workout type display name
static String workoutType(String type) {
switch (type) {
case 'running':
return '🏃 Running';
case 'walking':
return '🚶 Walking';
default:
return type;
}
}
/// Format improvement percentage
static String improvement(double percent) {
String sign = percent >= 0 ? '+' : '';
return '$sign${percent.toStringAsFixed(1)}%';
}
}
+46
View File
@@ -0,0 +1,46 @@
import 'dart:math';
/// أدوات حساب المسافات والاتجاهات الجغرافية
class GeoUtils {
static const double _earthRadiusMeters = 6371000;
/// Haversine formula - حساب المسافة بين نقطتين
static double haversineDistance(
double lat1, double lng1,
double lat2, double lng2,
) {
final dLat = _toRad(lat2 - lat1);
final dLng = _toRad(lng2 - lng1);
final a = sin(dLat / 2) * sin(dLat / 2) +
cos(_toRad(lat1)) * cos(_toRad(lat2)) *
sin(dLng / 2) * sin(dLng / 2);
final c = 2 * atan2(sqrt(a), sqrt(1 - a));
return _earthRadiusMeters * c;
}
/// Calculate bearing between two points (degrees from north)
static double bearing(
double lat1, double lng1,
double lat2, double lng2,
) {
final dLng = _toRad(lng2 - lng1);
final y = sin(dLng) * cos(_toRad(lat2));
final x = cos(_toRad(lat1)) * sin(_toRad(lat2)) -
sin(_toRad(lat1)) * cos(_toRad(lat2)) * cos(dLng);
return (_toDeg(atan2(y, x)) + 360) % 360;
}
/// Calculate speed between two points (m/s)
static double speed(
double lat1, double lng1, DateTime t1,
double lat2, double lng2, DateTime t2,
) {
final dist = haversineDistance(lat1, lng1, lat2, lng2);
final seconds = t2.difference(t1).inMilliseconds / 1000.0;
if (seconds <= 0) return 0;
return dist / seconds;
}
static double _toRad(double deg) => deg * pi / 180;
static double _toDeg(double rad) => rad * 180 / pi;
}
+41
View File
@@ -0,0 +1,41 @@
import 'dart:convert';
import 'package:crypto/crypto.dart';
/// أداة توليد توقيع HMAC-SHA256 للمصادقة مع الباك إند
class HmacUtility {
/// Generate HMAC-SHA256 signature
/// message = timestamp|api_key|payload
static String generateSignature({
required String apiKey,
required String apiSecret,
required String payload,
required int timestamp,
}) {
final message = '$timestamp|$apiKey|$payload';
final hmac = Hmac(sha256, utf8.encode(apiSecret));
final digest = hmac.convert(utf8.encode(message));
return digest.toString();
}
/// Build authenticated headers for API requests
static Map<String, String> buildHeaders({
required String apiKey,
required String apiSecret,
required String payload,
}) {
final timestamp = DateTime.now().millisecondsSinceEpoch ~/ 1000;
final signature = generateSignature(
apiKey: apiKey,
apiSecret: apiSecret,
payload: payload,
timestamp: timestamp,
);
return {
'Content-Type': 'application/json',
'X-API-Key': apiKey,
'X-Signature': signature,
'X-Timestamp': timestamp.toString(),
};
}
}
+84
View File
@@ -0,0 +1,84 @@
import '../models/gps_coordinate.dart';
/// أداة ترميز وفك ترميز Polyline
/// تستخدم خوارزمية Google Encoded Polyline لضغط المسارات بنسبة ~95%
class PolylineUtility {
/// Encode a list of GPS coordinates to a polyline string
static String encode(List<GpsCoordinate> coordinates) {
if (coordinates.isEmpty) return '';
final buffer = StringBuffer();
int prevLat = 0;
int prevLng = 0;
for (final point in coordinates) {
int lat = (point.latitude * 1e5).round();
int lng = (point.longitude * 1e5).round();
_encodeValue(lat - prevLat, buffer);
_encodeValue(lng - prevLng, buffer);
prevLat = lat;
prevLng = lng;
}
return buffer.toString();
}
/// Decode a polyline string back to coordinates
static List<GpsCoordinate> decode(String encoded) {
if (encoded.isEmpty) return [];
final coordinates = <GpsCoordinate>[];
int index = 0;
int lat = 0;
int lng = 0;
while (index < encoded.length) {
lat += _decodeValue(encoded, index);
index = _lastIndex;
if (index >= encoded.length) break;
lng += _decodeValue(encoded, index);
index = _lastIndex;
coordinates.add(GpsCoordinate(
latitude: lat / 1e5,
longitude: lng / 1e5,
timestamp: DateTime.now(), // Polylines don't store timestamps
));
}
return coordinates;
}
// Internal tracking for decode position
static int _lastIndex = 0;
static void _encodeValue(int value, StringBuffer buffer) {
int v = value < 0 ? ~(value << 1) : (value << 1);
while (v >= 0x20) {
buffer.writeCharCode((0x20 | (v & 0x1f)) + 63);
v >>= 5;
}
buffer.writeCharCode(v + 63);
}
static int _decodeValue(String encoded, int startIndex) {
int result = 0;
int shift = 0;
int index = startIndex;
while (index < encoded.length) {
int byte = encoded.codeUnitAt(index) - 63;
index++;
result |= (byte & 0x1f) << shift;
shift += 5;
if (byte < 0x20) break;
}
_lastIndex = index;
return (result & 1) != 0 ? ~(result >> 1) : (result >> 1);
}
}