chore: add build artifacts, map assets, and routing data

This commit is contained in:
Hamza-Ayed
2026-09-19 12:34:24 +03:00
parent aa2b9f131f
commit 43ad2e0ad4
327 changed files with 52421 additions and 1810 deletions
+4
View File
@@ -44,3 +44,7 @@ PAYMOB_IFRAME_ID=837992
BINANCE_PAY_API_KEY="المفتاح_الخاص_بك_هنا"
BINANCE_PAY_SECRET_KEY="المفتاح_السري_الخاص_بك_هنا"
# Google Gemini AI API Key (For Tactical AI Strategic Advisor & Fuel Pricing Intelligence)
GEMINI_API_KEY=
+3
View File
@@ -1,8 +1,11 @@
# Database Dumps & Archives
*.sql
*.tar
*.tar.gz
*.zip
*.mbtiles
*.db
*.db-*
# Node modules and Web build output
**/node_modules/
+3 -2
View File
@@ -13,8 +13,9 @@ Pods/
*.aab
*.ipa
dem_tiles/
infrastructure/osm-data/
osm-data/
infrastructure/osm-data/*.osm.pbf
infrastructure/osm-data/dem_tiles/
infrastructure/osm-data/valhalla-work/
venv/
.venv*/
dist/
+2
View File
@@ -12,6 +12,7 @@ import { BillingModule } from './billing/billing.module';
import { MailModule } from './common/mail.module';
import { WeatherModule } from './weather/weather.module';
import { TacticalModule } from './tactical/tactical.module';
import { TelemetryModule } from './telemetry/telemetry.module';
import { UsageInterceptor } from './usage/usage.interceptor';
@Module({
@@ -42,6 +43,7 @@ import { UsageInterceptor } from './usage/usage.interceptor';
MailModule,
WeatherModule,
TacticalModule,
TelemetryModule,
],
controllers: [],
providers: [
@@ -62,6 +62,6 @@ export abstract class BasePlace {
@Index()
neighborhood_id: number;
@Column({ type: 'int', nullable: true })
@Column({ type: 'int', default: 0, nullable: true })
elevation_meters: number;
}
@@ -30,7 +30,8 @@ export class GeocodingInitService implements OnModuleInit {
for (const table of tables) {
await this.repo.query(`
ALTER TABLE ${table} ADD COLUMN IF NOT EXISTS elevation_meters INT;
ALTER TABLE ${table} ADD COLUMN IF NOT EXISTS elevation_meters INT DEFAULT 0;
UPDATE ${table} SET elevation_meters = 0 WHERE elevation_meters IS NULL;
`);
for (const mapping of columnMapping) {
await this.repo.query(`
@@ -103,7 +104,44 @@ export class GeocodingInitService implements OnModuleInit {
await this.repo.query('CREATE INDEX IF NOT EXISTS idx_places_egypt_names_trgm ON places_egypt USING gist (name_ar gist_trgm_ops, name_en gist_trgm_ops);');
await this.repo.query('CREATE INDEX IF NOT EXISTS idx_places_iraq_names_trgm ON places_iraq USING gist (name_ar gist_trgm_ops, name_en gist_trgm_ops);');
this.logger.log('Geocoding database triggers and optimized indexes initialized for Syria, Jordan, Egypt, and Iraq.');
// 6. Tactical Terrain Obstacles Table & Auto-Population
await this.repo.query(`
CREATE TABLE IF NOT EXISTS tactical_terrain_obstacles (
id SERIAL PRIMARY KEY,
osm_id BIGINT,
obstacle_type VARCHAR(64),
severity VARCHAR(32),
name VARCHAR(255),
geometry GEOMETRY(Geometry, 4326),
created_at TIMESTAMPTZ DEFAULT NOW()
);
CREATE INDEX IF NOT EXISTS idx_tactical_obs_geom ON tactical_terrain_obstacles USING gist (geometry);
CREATE INDEX IF NOT EXISTS idx_tactical_obs_type ON tactical_terrain_obstacles (obstacle_type);
DO $$
BEGIN
IF to_regclass('public.planet_osm_line') IS NOT NULL THEN
INSERT INTO tactical_terrain_obstacles (osm_id, obstacle_type, severity, name, geometry)
SELECT
osm_id,
COALESCE(natural, barrier, man_made, waterway) AS obstacle_type,
CASE
WHEN natural = 'cliff' THEN 'SEVERE_NO_GO'
WHEN barrier = 'retaining_wall' THEN 'RESTRICTED'
WHEN barrier IN ('ditch', 'berm') THEN 'TACTICAL_BARRIER'
WHEN waterway = 'wadi' THEN 'DRAINAGE_DEFILE'
ELSE 'OBSTACLE'
END,
name,
geometry
FROM planet_osm_line
WHERE (natural IN ('cliff', 'ridge', 'arete') OR barrier IN ('retaining_wall', 'berm', 'ditch') OR waterway IN ('wadi', 'waterfall'))
ON CONFLICT DO NOTHING;
END IF;
END $$;
`);
this.logger.log('Geocoding database triggers, indexes, and tactical obstacles initialized.');
} catch (err) {
this.logger.error('Failed to initialize database geocoding triggers:', err);
}
+13 -12
View File
@@ -126,8 +126,8 @@ export class GeocodingService {
WHERE normalized_name % $1
${locationCondition}
${regionCondition}
ORDER BY (normalized_name <-> $1) ASC
LIMIT 50
ORDER BY ${hasLocation ? 'distance ASC, (normalized_name <-> $1) ASC' : '(normalized_name <-> $1) ASC'}
LIMIT 60
`;
const allResults = await Promise.race([
@@ -219,18 +219,19 @@ export class GeocodingService {
return results
.map(r => {
// Weighted scoring:
// 50% Text Match (relevance)
// 30% Popularity
// 20% Geographic Proximity
const textScore = Number(r.relevance);
const textScore = Number(r.relevance) || 0;
const popularityScore = (r.popularity_score || 10) / maxPopularity;
// Proximity bonus is 1.0 at 0m, decaying linearly to 0.0 at 10km.
const proximityBonus = hasLocation ? Math.max(0, 1 - (Number(r.distance) / 10000)) : 0;
// Proximity score: steep inverse decay so closer points get massive boost
// e.g. at 200m -> 0.91, 1km -> 0.67, 5km -> 0.28, 20km -> 0.09
const distKm = hasLocation ? (Number(r.distance) / 1000) : 0;
const proximityScore = hasLocation ? (1.0 / (1.0 + distKm * 0.5)) : 0;
const totalScore = (textScore * 0.5) + (popularityScore * 0.3) + (proximityBonus * 0.2);
// When location is available, proximity is heavily prioritized (60%)
const totalScore = hasLocation
? (proximityScore * 0.60) + (textScore * 0.30) + (popularityScore * 0.10)
: (textScore * 0.65) + (popularityScore * 0.35);
return { ...r, totalScore };
})
.sort((a, b) => b.totalScore - a.totalScore)
@@ -242,7 +243,7 @@ export class GeocodingService {
}
return true;
})
.slice(0, 4)
.slice(0, 20)
.map(r => {
const addressParts = [r.neighbourhood, r.district, r.governorate].filter(Boolean);
const full_address = addressParts.length > 0 ? addressParts.join('، ') : (r.address || '');
+47 -9
View File
@@ -38,8 +38,12 @@ export class MapsController {
@ApiOperation({ summary: 'Get MapLibre style JSON 🎨' })
async getStyleJson(@Query('theme') theme: string, @Res() res: Response) {
// Determine filenames based on theme
const isDark = theme === 'obsidian';
const filename = isDark ? 'style-dark.json' : 'style.json';
let filename = 'style.json';
if (theme === 'obsidian') {
filename = 'style-dark.json';
} else if (theme === 'satellite') {
filename = 'style-satellite.json';
}
const fallbackFilename = 'style.json';
// Paths to check
@@ -47,7 +51,7 @@ export class MapsController {
path.join('/data', filename),
path.join(process.cwd(), '../../', filename),
path.join(process.cwd(), filename),
// Fallbacks to light style if dark is missing
// Fallbacks to light style if specific theme is missing
path.join('/data', fallbackFilename),
path.join(process.cwd(), '../../', fallbackFilename),
path.join(process.cwd(), fallbackFilename),
@@ -69,19 +73,53 @@ export class MapsController {
const styleRaw = fs.readFileSync(stylePath, 'utf8');
const styleObj = JSON.parse(styleRaw);
// Dynamic Theme support (Safety overrides or fine-tuning)
// Dynamic Theme support
if (theme === 'light') {
styleObj.layers.forEach((layer: any) => {
if (layer.id === 'background') {
if (layer.id === 'background' && layer.paint) {
layer.paint['background-color'] = '#FFFFFF';
}
});
} else if (theme === 'obsidian') {
// If we found style-dark.json, we don't strictly need this,
// but keeping it as a helper or if it fell back to style.json
styleObj.layers.forEach((layer: any) => {
if (layer.id === 'background') {
layer.paint['background-color'] = '#101014'; // Dark tone
if (layer.id === 'background' && layer.paint) {
layer.paint['background-color'] = '#101014';
}
});
} else if (theme === 'satellite') {
// Ensure ESRI Satellite layer is injected if not already present
if (!styleObj.sources['esri-satellite']) {
styleObj.sources['esri-satellite'] = {
type: 'raster',
tiles: [
'https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}',
],
tileSize: 256,
maxzoom: 19,
attribution: '© Esri, Maxar, Earthstar Geographics',
};
}
if (!styleObj.layers.some((l: any) => l.id === 'esri-satellite-imagery')) {
const satLayer = {
id: 'esri-satellite-imagery',
type: 'raster',
source: 'esri-satellite',
minzoom: 0,
maxzoom: 19,
paint: { 'raster-opacity': 1.0 },
};
const bgIdx = styleObj.layers.findIndex((l: any) => l.id === 'background');
if (bgIdx >= 0) {
styleObj.layers.splice(bgIdx + 1, 0, satLayer);
} else {
styleObj.layers.unshift(satLayer);
}
}
styleObj.layers.forEach((l: any) => {
if (l.id === 'background' && l.paint) {
l.paint['background-color'] = '#000000';
} else if ((l.id.includes('landuse') || l.id.includes('poly')) && l.type === 'fill' && l.paint) {
l.paint['fill-opacity'] = 0.05;
}
});
}
+3 -1
View File
@@ -65,9 +65,11 @@ export class MapsService {
console.warn('Geocoding internal error during routing:', e);
}
const ghProfile = ['car', 'foot', 'bike'].includes(profile) ? profile : 'car';
const payload: any = {
points: ghPoints,
profile: profile,
profile: ghProfile,
locale: locale === 'en' ? 'ar' : locale, // Default to Arabic if not specified or fallback
calc_points: true,
points_encoded: false, // JSON arrays for reliable 3D elevation (SRTM)
@@ -0,0 +1,82 @@
import * as fs from 'fs';
import * as path from 'path';
import { Logger } from '@nestjs/common';
import { HttpException, HttpStatus } from '@nestjs/common';
export interface RoutingPackageManifest {
packageId: string;
version: string;
fileName: string;
sizeBytes: number;
sha256: string;
engine: string;
elevation: string;
bbox?: Record<string, number>;
builtAt: string;
}
/**
* Serves the on-device Valhalla routing package (Jordan) built by
* infrastructure/scripts/build-valhalla-tiles.sh. The tactical app downloads
* this package once and routes fully offline against the real road network
* with SRTM elevation — same data the server-side GraphHopper engine uses.
*/
export class RoutingPackageService {
private static readonly logger = new Logger(RoutingPackageService.name);
private static readonly PACKAGE_DIR =
process.env.ROUTING_PACKAGE_DIR ||
(fs.existsSync('/data/infrastructure/osm-data/routing-packages')
? '/data/infrastructure/osm-data/routing-packages'
: path.join(process.cwd(), 'infrastructure/osm-data/routing-packages'));
getDirectory(): string {
return RoutingPackageService.PACKAGE_DIR;
}
/**
* Read jordan-routing-manifest.json written by the tile builder.
* Returns null when no package has been built yet.
*/
getManifest(): RoutingPackageManifest | null {
const manifestPath = path.join(RoutingPackageService.PACKAGE_DIR, 'jordan-routing-manifest.json');
try {
if (!fs.existsSync(manifestPath)) return null;
const raw = fs.readFileSync(manifestPath, 'utf8');
const manifest = JSON.parse(raw) as RoutingPackageManifest;
// Verify the archive actually exists next to the manifest.
const filePath = this.getPackageFilePath(manifest);
if (!fs.existsSync(filePath)) return null;
return manifest;
} catch (e) {
RoutingPackageService.logger.warn(`Failed to read routing manifest: ${e}`);
return null;
}
}
/**
* Require the manifest + file or throw 404 — used before streaming.
*/
requireManifest(): RoutingPackageManifest {
const manifest = this.getManifest();
if (!manifest) {
throw new HttpException(
'Routing package not available. Run infrastructure/scripts/build-valhalla-tiles.sh on the server.',
HttpStatus.NOT_FOUND,
);
}
return manifest;
}
getPackageFilePath(manifest: RoutingPackageManifest): string {
// Never trust fileName blindly: only allow plain names inside the package dir.
const safeName = path.basename(manifest.fileName || '');
return path.join(RoutingPackageService.PACKAGE_DIR, safeName);
}
createPackageStream(manifest: RoutingPackageManifest): fs.ReadStream {
const filePath = this.getPackageFilePath(manifest);
return fs.createReadStream(filePath);
}
}
+70 -1
View File
@@ -18,6 +18,7 @@ import { LineOfSightBodyDto, LineOfSightQueryDto } from './dto/line-of-sight.dto
import { ArtilleryMissionRequestDto, SaveScenarioDto } from './dto/tactical.dto';
import { TacticalService } from './tactical.service';
import { DemTileService } from './dem-tile.service';
import { RoutingPackageService } from './routing-package.service';
@ApiTags('tactical')
@ApiHeader({
@@ -28,7 +29,10 @@ import { DemTileService } from './dem-tile.service';
@Controller('tactical')
@UseGuards(ApiKeyGuard, TenantThrottlerGuard)
export class TacticalController {
constructor(private readonly tacticalService: TacticalService) { }
constructor(
private readonly tacticalService: TacticalService,
private readonly routingPackageService: RoutingPackageService,
) { }
@Get('verify-license')
@ApiOperation({ summary: 'Verify tactical clearance and military license' })
@@ -225,6 +229,42 @@ export class TacticalController {
return this.tacticalService.getOfflinePackageInfo();
}
@Get('routing-package/jordan/manifest')
@ApiOperation({
summary: 'Manifest of the on-device Valhalla routing package (version, sha256, size)',
})
getRoutingPackageManifest() {
const manifest = this.routingPackageService.getManifest();
if (!manifest) {
return {
available: false,
message:
'Routing package not built yet. Run infrastructure/scripts/build-valhalla-tiles.sh on the server.',
};
}
return { available: true, ...manifest };
}
@Get('routing-package/jordan')
@ApiOperation({
summary:
'Download the Jordan Valhalla routing tar (real road graph + SRTM elevation) for 100% offline on-device routing',
})
async downloadRoutingPackage(@Res() res: any) {
const manifest = this.routingPackageService.requireManifest();
const stream = this.routingPackageService.createPackageStream(manifest);
res.setHeader('Content-Type', 'application/x-tar');
res.setHeader('Content-Length', manifest.sizeBytes);
res.setHeader(
'Content-Disposition',
`attachment; filename="${manifest.fileName}"`,
);
res.setHeader('X-Package-Version', manifest.version);
res.setHeader('X-Package-Sha256', manifest.sha256);
stream.pipe(res);
}
@Get('landmarks')
@ApiOperation({
summary: 'Get tactical strategic landmarks / استرجاع معالم الأردن البصرية والاستراتيجية للتقاطع الميداني',
@@ -236,6 +276,24 @@ export class TacticalController {
return this.tacticalService.getLandmarks(region, type);
}
@Get('ipb/obstacles')
@ApiOperation({
summary: 'Query Tactical IPB Obstacles by Bounding Box / استعلام الموانع التكتيكية ضمن نطاق جغرافي',
})
async getIPBObstacles(
@Query('minLat') minLatStr: string,
@Query('minLng') minLngStr: string,
@Query('maxLat') maxLatStr: string,
@Query('maxLng') maxLngStr: string,
) {
const minLat = parseFloat(minLatStr) || 31.0;
const minLng = parseFloat(minLngStr) || 35.0;
const maxLat = parseFloat(maxLatStr) || 33.0;
const maxLng = parseFloat(maxLngStr) || 37.0;
return this.tacticalService.getIPBObstacles({ minLat, minLng, maxLat, maxLng });
}
@Get('dem/:zoom/:x/:y')
@ApiOperation({
summary: 'Stream Sovereign Real Satellite DEM Elevation Tile / تقديم بلاطات الارتفاعات السيادية من السيرفر المحلي',
@@ -257,4 +315,15 @@ export class TacticalController {
res.setHeader('Cross-Origin-Resource-Policy', 'cross-origin');
res.send(buffer);
}
@Post('ai-assessment')
@ApiOperation({
summary: 'Generate Advanced AI Tactical Assessment using Gemini 1.5 Pro based on comprehensive IPB and Terrain data',
})
async generateAIAssessment(@Body() body: { ipbData: any; terrainData: any }) {
if (!body.ipbData || !body.terrainData) {
throw new HttpException('Missing required tactical data (ipbData, terrainData)', HttpStatus.BAD_REQUEST);
}
return this.tacticalService.generateTacticalAIAssessment(body.ipbData, body.terrainData);
}
}
+2 -1
View File
@@ -2,6 +2,7 @@ import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { TacticalController } from './tactical.controller';
import { TacticalService } from './tactical.service';
import { RoutingPackageService } from './routing-package.service';
import { RedisModule } from '../common/redis.module';
import { PlaceJordan } from '../geocoding/entities/place-jordan.entity';
@@ -11,7 +12,7 @@ import { PlaceJordan } from '../geocoding/entities/place-jordan.entity';
TypeOrmModule.forFeature([PlaceJordan]),
],
controllers: [TacticalController],
providers: [TacticalService],
providers: [TacticalService, RoutingPackageService],
exports: [TacticalService],
})
export class TacticalModule {}
+170
View File
@@ -5,6 +5,7 @@ import { RedisService } from '../common/redis.service';
import { ArtilleryMissionRequestDto, TacticalSymbolDto } from './dto/tactical.dto';
import { getElevationMeters } from '../common/gis.utils';
import { DemTileService } from './dem-tile.service';
import { RoutingPackageService } from './routing-package.service';
export interface LosPoint {
index: number;
@@ -82,6 +83,7 @@ export class TacticalService {
@InjectRepository(PlaceJordan)
private readonly placeJordanRepo: Repository<PlaceJordan>,
private readonly dataSource: DataSource,
private readonly routingPackageService: RoutingPackageService,
) {
this.graphHopperUrl = this.configService.get('GRAPH_HOPPER_URL', 'http://routing:8080');
}
@@ -1022,6 +1024,9 @@ export class TacticalService {
}
} catch (_) {}
// Real on-device routing package status (Valhalla graph built from OSM + SRTM)
const routingManifest = this.routingPackageService?.getManifest?.() ?? null;
return {
packageId: 'jordan-tactical-offline-v2',
name: 'حزمة الأردن التكتيكية الميدانية الكاملة (Off-Grid Sovereign Package)',
@@ -1033,6 +1038,21 @@ export class TacticalService {
sizeFormatted: '825 KB (خفيفة جداً وسريعة التحميل)',
offlineRoutingReady: true,
offlineResectionReady: true,
routingPackage: routingManifest
? {
available: true,
packageId: routingManifest.packageId,
version: routingManifest.version,
fileName: routingManifest.fileName,
sizeBytes: routingManifest.sizeBytes,
sha256: routingManifest.sha256,
engine: routingManifest.engine,
elevation: routingManifest.elevation,
downloadUrl: '/api/tactical/routing-package/jordan',
manifestUrl: '/api/tactical/routing-package/jordan/manifest',
builtAt: routingManifest.builtAt,
}
: { available: false },
lastUpdated: new Date().toISOString()
};
}
@@ -1150,4 +1170,154 @@ export class TacticalService {
timestamp: new Date().toISOString()
};
}
/**
* Get Tactical IPB Obstacles within Bounding Box
*/
async getIPBObstacles(bbox: {
minLat: number;
minLng: number;
maxLat: number;
maxLng: number;
}) {
const { minLat, minLng, maxLat, maxLng } = bbox;
try {
// 1. Query pre-computed / merged tactical_terrain_obstacles table if it exists
const tableCheck = await this.dataSource.query(`
SELECT to_regclass('public.tactical_terrain_obstacles') as exists;
`);
let features: any[] = [];
if (tableCheck?.[0]?.exists) {
const rows = await this.dataSource.query(
`
SELECT
id,
obstacle_type,
severity,
name,
ST_AsGeoJSON(geometry)::json as geojson
FROM tactical_terrain_obstacles
WHERE geometry && ST_MakeEnvelope($1, $2, $3, $4, 4326)
LIMIT 500;
`,
[minLng, minLat, maxLng, maxLat],
);
features = rows.map((r: any) => ({
type: 'Feature',
properties: {
id: r.id,
obstacleType: r.obstacle_type,
severity: r.severity,
name: r.name,
},
geometry: r.geojson,
}));
}
// 2. If tactical_terrain_obstacles was empty, query planet_osm_line directly as fallback
if (features.length === 0) {
const osmCheck = await this.dataSource.query(`
SELECT to_regclass('public.planet_osm_line') as exists;
`);
if (osmCheck?.[0]?.exists) {
const rows = await this.dataSource.query(
`
SELECT
osm_id as id,
COALESCE(natural, barrier, man_made, waterway) AS obstacle_type,
CASE
WHEN natural = 'cliff' THEN 'SEVERE_NO_GO'
WHEN barrier = 'retaining_wall' THEN 'RESTRICTED'
WHEN barrier IN ('ditch', 'berm') THEN 'TACTICAL_BARRIER'
WHEN waterway = 'wadi' THEN 'DRAINAGE_DEFILE'
ELSE 'OBSTACLE'
END as severity,
name,
ST_AsGeoJSON(geometry)::json as geojson
FROM planet_osm_line
WHERE geometry && ST_MakeEnvelope($1, $2, $3, $4, 4326)
AND (natural IN ('cliff', 'ridge', 'arete') OR barrier IN ('retaining_wall', 'berm', 'ditch') OR waterway IN ('wadi', 'waterfall'))
LIMIT 500;
`,
[minLng, minLat, maxLng, maxLat],
);
features = rows.map((r: any) => ({
type: 'Feature',
properties: {
id: r.id,
obstacleType: r.obstacle_type,
severity: r.severity,
name: r.name,
},
geometry: r.geojson,
}));
}
}
return {
type: 'FeatureCollection',
count: features.length,
features,
};
} catch (err: any) {
this.logger.error(`Error querying IPB obstacles: ${err?.message}`);
return {
type: 'FeatureCollection',
count: 0,
features: [],
};
}
}
async generateTacticalAIAssessment(ipbData: any, terrainData: any): Promise<any> {
const geminiKey = this.configService.get<string>('GEMINI_API_KEY');
if (!geminiKey) {
throw new Error('GEMINI_API_KEY is not configured on the server.');
}
const prompt = `أنت ضابط ركن استخبارات عسكرية (G2) ومحلل تكتيكي استراتيجي خبير.
الرجاء دراسة التقرير التكتيكي المرفق والذي يحتوي على تقدير موقف الاستخبارات عن الأرض (IPB)، الموانع الطبيعية، المقاطع الصخرية، مناطق السكن، والارتفاعات.
المعطيات:
بيانات دراسة الأرض والتضاريس:
${JSON.stringify(terrainData, null, 2)}
بيانات الشفافات التكتيكية (IPB):
${JSON.stringify(ipbData, null, 2)}
المطلوب:
بناءً على الأرقام الدقيقة والموقع الجغرافي المعطى، قدم تحليلاً استراتيجياً مفصلاً يشمل:
1. التهديدات والفرص التعبوية بناءً على التضاريس.
2. أفضل محاور التقدم ومناطق التقتيل (Engagement Areas/Kill Zones).
3. تقييم الموانع وتأثيرها على حركة الدروع والمشاة الآلية.
4. توصيات لتموضع القوات الصديقة (احتياط، مدفعية، رصد).
الرجاء كتابة التقرير بلغة عسكرية احترافية وواضحة (باللغة العربية). لا تقم باختراع أرقام، اعتمد كلياً على البيانات المرفقة.`;
const url = `https://generativelanguage.googleapis.com/v1beta/models/gemini-3.7-flash:generateContent?key=${geminiKey}`;
try {
this.logger.log('Sending comprehensive tactical data to Gemini 3.7 Flash for analysis...');
const response = await axios.post(
url,
{
contents: [{ parts: [{ text: prompt }] }],
generationConfig: {
temperature: 0.2,
}
},
{ timeout: 35000 }
);
const content = response.data?.candidates?.[0]?.content?.parts?.[0]?.text;
return { success: true, assessment: content };
} catch (err: any) {
this.logger.error(`Failed to generate AI assessment: ${err.message}`);
throw new Error('Failed to generate tactical AI assessment.');
}
}
}
@@ -0,0 +1,61 @@
import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
import { IsNotEmpty, IsNumber, IsOptional, IsString, Max, Min, IsArray, ValidateNested } from 'class-validator';
import { Type, Transform } from 'class-transformer';
export class DriverTelemetryDto {
@ApiProperty({ description: 'Driver unique ID', example: 'driver_jo_1042' })
@IsString()
@IsNotEmpty()
@Transform(({ obj, value }) => value ?? obj.driver_id ?? obj.driverId)
driver_id: string;
@ApiProperty({ description: 'Latitude coordinate (-90 to 90)', example: 31.9539 })
@IsNumber()
@Min(-90)
@Max(90)
@Transform(({ obj, value }) => Number(value ?? obj.latitude ?? obj.lat))
latitude: number;
@ApiProperty({ description: 'Longitude coordinate (-180 to 180)', example: 35.9106 })
@IsNumber()
@Min(-180)
@Max(180)
@Transform(({ obj, value }) => Number(value ?? obj.longitude ?? obj.lng))
longitude: number;
@ApiProperty({ description: 'Instantaneous vehicle speed in km/h', example: 45.5 })
@IsNumber()
@Min(0)
@Transform(({ obj, value }) => Number(value ?? obj.speed ?? 0))
speed: number;
@ApiProperty({ description: 'Compass heading / bearing in degrees (0 - 360)', example: 185.0 })
@IsNumber()
@Min(0)
@Max(360)
@Transform(({ obj, value }) => Number(value ?? obj.heading ?? 0))
heading: number;
@ApiPropertyOptional({ description: 'Distance traveled in meters', example: 1250.4, default: 0 })
@IsOptional()
@IsNumber()
@Transform(({ obj, value }) => (value != null ? Number(value) : (obj.distance != null ? Number(obj.distance) : 0)))
distance?: number;
@ApiPropertyOptional({ description: 'Elevation above mean sea level in meters (AMSL)', example: 890.5, default: 0 })
@IsOptional()
@IsNumber()
@Transform(({ obj, value }) => {
const val = value ?? obj.elevation ?? obj.altitude;
return val != null ? Number(val) : 0;
})
elevation?: number;
}
export class DriverTelemetryBatchDto {
@ApiProperty({ type: [DriverTelemetryDto], description: 'Array of telemetry points for batch processing' })
@IsArray()
@ValidateNested({ each: true })
@Type(() => DriverTelemetryDto)
points: DriverTelemetryDto[];
}
@@ -0,0 +1,66 @@
import { Controller, Post, Get, Body, Query, Param, UseGuards, HttpException, HttpStatus } from '@nestjs/common';
import { ApiTags, ApiOperation, ApiQuery, ApiParam, ApiSecurity } from '@nestjs/swagger';
import { TelemetryService } from './telemetry.service';
import { DriverTelemetryDto, DriverTelemetryBatchDto } from './dto/driver-telemetry.dto';
import { ApiKeyGuard } from '../common/guards/api-key.guard';
@ApiTags('telemetry')
@ApiSecurity('x-api-key')
@Controller('telemetry')
@UseGuards(ApiKeyGuard)
export class TelemetryController {
constructor(private readonly telemetryService: TelemetryService) {}
@Post()
@ApiOperation({
summary: 'Ingest real-time driver telemetry with elevation & distance 📡⛰️',
description: 'Receives GPS position, speed, heading, distance, and AMSL elevation from driver app.',
})
async ingest(@Body() data: DriverTelemetryDto) {
if (!data.driver_id) {
throw new HttpException('Missing driver_id', HttpStatus.BAD_REQUEST);
}
return this.telemetryService.ingest(data);
}
@Post('batch')
@ApiOperation({
summary: 'Batch ingest driver telemetry points 📦',
description: 'Receives an array of telemetry points for offline-buffered sync or high-frequency traces.',
})
async ingestBatch(@Body() body: DriverTelemetryBatchDto) {
if (!body || !Array.isArray(body.points)) {
throw new HttpException('Invalid payload: expected { points: [...] }', HttpStatus.BAD_REQUEST);
}
return this.telemetryService.ingestBatch(body.points);
}
@Get('nearby')
@ApiOperation({ summary: 'Query active drivers within spatial radius with elevation & bearing 🚗' })
@ApiQuery({ name: 'lat', required: true, type: Number, description: 'Center latitude' })
@ApiQuery({ name: 'lng', required: true, type: Number, description: 'Center longitude' })
@ApiQuery({ name: 'radius', required: false, type: Number, description: 'Radius in meters (default: 5000m)' })
async getNearby(
@Query('lat') lat: number,
@Query('lng') lng: number,
@Query('radius') radius?: number,
) {
const latNum = Number(lat);
const lngNum = Number(lng);
if (isNaN(latNum) || isNaN(lngNum)) {
throw new HttpException('lat and lng must be valid numbers', HttpStatus.BAD_REQUEST);
}
return this.telemetryService.getRecentDrivers(latNum, lngNum, radius ? Number(radius) : 5000);
}
@Get('driver/:driverId/profile')
@ApiOperation({ summary: 'Get 3D elevation profile, climb, and terrain grade for a driver 📈' })
@ApiParam({ name: 'driverId', required: true, description: 'Driver unique ID' })
@ApiQuery({ name: 'hours', required: false, description: 'Window in hours (default: 24)' })
async getDriverElevationProfile(
@Param('driverId') driverId: string,
@Query('hours') hours?: number,
) {
return this.telemetryService.getElevationProfile(driverId, hours ? Number(hours) : 24);
}
}
@@ -0,0 +1,46 @@
import { Entity, Column, PrimaryGeneratedColumn, Index, CreateDateColumn } from 'typeorm';
@Entity('telemetry_logs')
@Index(['driverId', 'timestamp'], { unique: false })
export class TelemetryLog {
@PrimaryGeneratedColumn()
id: number;
@Column({ name: 'driverId' })
@Index()
driverId: string;
@Column('decimal', { precision: 10, scale: 7 })
latitude: number;
@Column('decimal', { precision: 10, scale: 7 })
longitude: number;
@Column('float', { default: 0 })
speed: number;
@Column('float', { default: 0 })
heading: number;
// Cumulative or step distance traveled in meters (المسافة المقطوعة بالمتر)
@Column('float', { default: 0 })
distance: number;
// Elevation above mean sea level in meters (الارتفاع عن مستوى سطح البحر بالمتر AMSL)
@Column('float', { default: 0 })
elevation: number;
@CreateDateColumn({ type: 'timestamp with time zone' })
@Index()
timestamp: Date;
// PostGIS spatial point for ultra-fast spatial and proximity indexing
@Column({
type: 'geography',
spatialFeatureType: 'Point',
srid: 4326,
nullable: true,
})
@Index({ spatial: true })
location: any;
}
@@ -0,0 +1,17 @@
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { TelemetryLog } from './telemetry.entity';
import { TelemetryService } from './telemetry.service';
import { TelemetryController } from './telemetry.controller';
import { RedisModule } from '../common/redis.module';
@Module({
imports: [
TypeOrmModule.forFeature([TelemetryLog]),
RedisModule,
],
controllers: [TelemetryController],
providers: [TelemetryService],
exports: [TelemetryService],
})
export class TelemetryModule {}
@@ -0,0 +1,122 @@
import { Test, TestingModule } from '@nestjs/testing';
import { getRepositoryToken } from '@nestjs/typeorm';
import { DataSource } from 'typeorm';
import { TelemetryService } from './telemetry.service';
import { TelemetryLog } from './telemetry.entity';
import { RedisService } from '../common/redis.service';
describe('TelemetryService', () => {
let service: TelemetryService;
let mockRepo: any;
let mockDataSource: any;
let mockRedis: any;
beforeEach(async () => {
mockRepo = {
create: jest.fn().mockImplementation((dto) => ({ id: 42, ...dto })),
save: jest.fn().mockImplementation((entity) => Promise.resolve({ id: 42, ...entity })),
};
mockDataSource = {
query: jest.fn().mockResolvedValue([]),
};
mockRedis = {
set: jest.fn().mockResolvedValue(undefined),
get: jest.fn().mockResolvedValue(null),
};
const module: TestingModule = await Test.createTestingModule({
providers: [
TelemetryService,
{
provide: getRepositoryToken(TelemetryLog),
useValue: mockRepo,
},
{
provide: DataSource,
useValue: mockDataSource,
},
{
provide: RedisService,
useValue: mockRedis,
},
],
}).compile();
service = module.get<TelemetryService>(TelemetryService);
});
it('should be defined', () => {
expect(service).toBeDefined();
});
it('should ingest telemetry with elevation, distance, speed, and heading', async () => {
const payload = {
driver_id: 'test_driver_77',
latitude: 31.9539,
longitude: 35.9106,
speed: 60.5,
heading: 180.0,
distance: 1450.0,
elevation: 920.4,
};
const result = await service.ingest(payload);
expect(result.success).toBe(true);
expect(result.driver_id).toBe('test_driver_77');
expect(result.elevation).toBe(920.4);
expect(result.distance).toBe(1450.0);
expect(mockRepo.create).toHaveBeenCalledWith(
expect.objectContaining({
driverId: 'test_driver_77',
latitude: 31.9539,
longitude: 35.9106,
speed: 60.5,
heading: 180.0,
distance: 1450.0,
elevation: 920.4,
}),
);
expect(mockRepo.save).toHaveBeenCalled();
expect(mockRedis.set).toHaveBeenCalledWith(
'fleet:driver:test_driver_77:live',
expect.objectContaining({
driverId: 'test_driver_77',
elevation: 920.4,
distance: 1450.0,
}),
900,
);
});
it('should batch ingest multiple telemetry points with elevation', async () => {
const batch = [
{
driver_id: 'd1',
latitude: 31.95,
longitude: 35.91,
speed: 50,
heading: 90,
distance: 100,
elevation: 900,
},
{
driver_id: 'd2',
latitude: 31.96,
longitude: 35.92,
speed: 55,
heading: 95,
distance: 120,
elevation: 915,
},
];
const res = await service.ingestBatch(batch);
expect(res.success).toBe(true);
expect(res.count).toBe(2);
expect(mockRepo.save).toHaveBeenCalled();
});
});
+278
View File
@@ -0,0 +1,278 @@
import { Injectable, Logger, OnModuleInit } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository, DataSource } from 'typeorm';
import { TelemetryLog } from './telemetry.entity';
import { DriverTelemetryDto } from './dto/driver-telemetry.dto';
import { RedisService } from '../common/redis.service';
@Injectable()
export class TelemetryService implements OnModuleInit {
private readonly logger = new Logger(TelemetryService.name);
constructor(
@InjectRepository(TelemetryLog)
private readonly telemetryRepo: Repository<TelemetryLog>,
private readonly dataSource: DataSource,
private readonly redisService: RedisService,
) {}
async onModuleInit() {
try {
// Ensure PostGIS extension and telemetry_logs table columns exist
await this.dataSource.query(`
CREATE EXTENSION IF NOT EXISTS postgis;
CREATE TABLE IF NOT EXISTS telemetry_logs (
id SERIAL PRIMARY KEY,
"driverId" VARCHAR(255) NOT NULL,
latitude NUMERIC(10, 7) NOT NULL,
longitude NUMERIC(10, 7) NOT NULL,
speed FLOAT NOT NULL DEFAULT 0,
heading FLOAT NOT NULL DEFAULT 0,
distance FLOAT DEFAULT 0,
elevation FLOAT DEFAULT 0,
timestamp TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
location GEOGRAPHY(Point, 4326)
);
ALTER TABLE telemetry_logs ADD COLUMN IF NOT EXISTS distance FLOAT DEFAULT 0;
ALTER TABLE telemetry_logs ADD COLUMN IF NOT EXISTS elevation FLOAT DEFAULT 0;
CREATE INDEX IF NOT EXISTS telemetry_logs_driver_idx ON telemetry_logs ("driverId");
CREATE INDEX IF NOT EXISTS telemetry_logs_timestamp_idx ON telemetry_logs (timestamp DESC);
CREATE INDEX IF NOT EXISTS telemetry_logs_location_idx ON telemetry_logs USING GIST (location);
`);
this.logger.log('✅ Telemetry schema verified: elevation and distance columns ready.');
} catch (err: any) {
this.logger.warn(`Telemetry DB auto-migration check note: ${err.message}`);
}
}
/**
* Ingest a single driver telemetry record including elevation & distance
*/
async ingest(data: DriverTelemetryDto) {
const lat = Number(data.latitude);
const lng = Number(data.longitude);
const speed = Number(data.speed || 0);
const heading = Number(data.heading || 0);
const distance = Number(data.distance || 0);
const elevation = Number(data.elevation || 0);
const log = this.telemetryRepo.create({
driverId: data.driver_id,
latitude: lat,
longitude: lng,
speed,
heading,
distance,
elevation,
timestamp: new Date(),
location: {
type: 'Point',
coordinates: [lng, lat],
},
});
const saved = await this.telemetryRepo.save(log);
// Fast memory caching in Redis for real-time fleet queries (TTL 15 minutes)
try {
await this.redisService.set(
`fleet:driver:${data.driver_id}:live`,
{
driverId: data.driver_id,
latitude: lat,
longitude: lng,
speed,
heading,
distance,
elevation,
updatedAt: new Date().toISOString(),
},
900,
);
} catch (_) {
// Redis failover - non-blocking
}
return {
success: true,
id: saved.id,
driver_id: data.driver_id,
elevation,
distance,
timestamp: saved.timestamp,
};
}
/**
* Batch ingest multiple telemetry points
*/
async ingestBatch(points: DriverTelemetryDto[]) {
if (!points || points.length === 0) {
return { success: true, count: 0 };
}
const entities = points.map((p) => {
const lat = Number(p.latitude);
const lng = Number(p.longitude);
const speed = Number(p.speed || 0);
const heading = Number(p.heading || 0);
const distance = Number(p.distance || 0);
const elevation = Number(p.elevation || 0);
return this.telemetryRepo.create({
driverId: p.driver_id,
latitude: lat,
longitude: lng,
speed,
heading,
distance,
elevation,
timestamp: new Date(),
location: {
type: 'Point',
coordinates: [lng, lat],
},
});
});
await this.telemetryRepo.save(entities);
// Update Redis cache for the latest point of each driver
try {
const latestByDriver = new Map<string, DriverTelemetryDto>();
for (const p of points) {
latestByDriver.set(p.driver_id, p);
}
for (const [dId, p] of latestByDriver.entries()) {
await this.redisService.set(
`fleet:driver:${dId}:live`,
{
driverId: dId,
latitude: Number(p.latitude),
longitude: Number(p.longitude),
speed: Number(p.speed || 0),
heading: Number(p.heading || 0),
distance: Number(p.distance || 0),
elevation: Number(p.elevation || 0),
updatedAt: new Date().toISOString(),
},
900,
);
}
} catch (_) {}
return {
success: true,
count: entities.length,
timestamp: new Date(),
};
}
/**
* Find nearby active drivers using PostGIS spatial geography search
*/
async getRecentDrivers(lat: number, lng: number, radiusMeters: number = 5000) {
const rows = await this.dataSource.query(
`SELECT DISTINCT ON ("driverId")
id, "driverId", latitude, longitude, speed, heading, distance, elevation, timestamp,
ST_Distance(location, ST_MakePoint($1, $2)::geography) as distance_to_center_meters
FROM telemetry_logs
WHERE ST_DWithin(location, ST_MakePoint($1, $2)::geography, $3)
AND timestamp >= NOW() - INTERVAL '4 hours'
ORDER BY "driverId", timestamp DESC
LIMIT 100`,
[lng, lat, radiusMeters],
);
return rows.map((r: any) => ({
driver_id: r.driverId,
latitude: parseFloat(r.latitude),
longitude: parseFloat(r.longitude),
speed: parseFloat(r.speed),
heading: parseFloat(r.heading),
distance: parseFloat(r.distance || 0),
elevation: parseFloat(r.elevation || 0),
timestamp: r.timestamp,
distance_to_center_meters: parseFloat(r.distance_to_center_meters),
}));
}
/**
* Calculate 3D elevation profile and vertical gradient for a specific driver
*/
async getElevationProfile(driverId: string, hours: number = 24) {
const points = await this.dataSource.query(
`SELECT latitude, longitude, speed, heading, distance, elevation, timestamp
FROM telemetry_logs
WHERE "driverId" = $1
AND timestamp >= NOW() - ($2 || ' hours')::interval
ORDER BY timestamp ASC`,
[driverId, hours],
);
if (points.length === 0) {
return {
driver_id: driverId,
hours,
pointsCount: 0,
minElevation: 0,
maxElevation: 0,
avgElevation: 0,
totalClimbMeters: 0,
totalDescentMeters: 0,
maxGradePercent: 0,
points: [],
};
}
let minElev = points[0].elevation || 0;
let maxElev = points[0].elevation || 0;
let sumElev = 0;
let totalClimb = 0;
let totalDescent = 0;
let maxGrade = 0;
for (let i = 0; i < points.length; i++) {
const elev = parseFloat(points[i].elevation || '0');
sumElev += elev;
if (elev < minElev) minElev = elev;
if (elev > maxElev) maxElev = elev;
if (i > 0) {
const prevElev = parseFloat(points[i - 1].elevation || '0');
const diff = elev - prevElev;
if (diff > 0) totalClimb += diff;
if (diff < 0) totalDescent += Math.abs(diff);
// Approximate grade percent if distance step is available
const stepDist = parseFloat(points[i].distance || '0') - parseFloat(points[i - 1].distance || '0');
if (stepDist > 10) {
const grade = (Math.abs(diff) / stepDist) * 100;
if (grade > maxGrade && grade < 50) {
maxGrade = grade;
}
}
}
}
return {
driver_id: driverId,
hours,
pointsCount: points.length,
minElevation: Math.round(minElev * 10) / 10,
maxElevation: Math.round(maxElev * 10) / 10,
avgElevation: Math.round((sumElev / points.length) * 10) / 10,
totalClimbMeters: Math.round(totalClimb * 10) / 10,
totalDescentMeters: Math.round(totalDescent * 10) / 10,
maxGradePercent: Math.round(maxGrade * 10) / 10,
recentPoints: points.slice(-30).map((p: any) => ({
latitude: parseFloat(p.latitude),
longitude: parseFloat(p.longitude),
speed: parseFloat(p.speed),
heading: parseFloat(p.heading),
elevation: parseFloat(p.elevation),
timestamp: p.timestamp,
})),
};
}
}
+38 -21
View File
@@ -611,24 +611,41 @@
<section id="docs" class="page-section h-full">
<div class="flex h-full gap-8">
<!-- Docs Nav -->
<div class="w-64 flex flex-col gap-2 shrink-0">
<div class="mb-4">
<h2 class="text-xl font-black text-gradient" data-i18n="guides-title">Guides</h2>
<div class="w-64 flex flex-col gap-1.5 shrink-0 overflow-y-auto pr-1">
<div class="mb-2">
<h2 class="text-xs font-black uppercase tracking-wider text-slate-500" data-i18n="docs-overview">Overview</h2>
</div>
<a href="javascript:void(0)" data-section="getting-started" class="docs-nav-link active bg-blue-500/10 text-blue-400 p-4 rounded-2xl text-sm font-bold flex items-center gap-3 transition-all hover:bg-blue-500/5">
<i data-lucide="rocket" class="w-4 h-4"></i> Getting Started
<a href="javascript:void(0)" data-section="getting-started" class="docs-nav-link active bg-blue-500/10 text-blue-400 p-3.5 rounded-xl text-sm font-bold flex items-center gap-3 transition-all hover:bg-blue-500/5">
<i data-lucide="rocket" class="w-4 h-4 text-blue-400"></i> <span data-i18n="docs-getting-started">Getting Started</span>
</a>
<a href="javascript:void(0)" data-section="tiles-api" class="docs-nav-link p-4 rounded-2xl text-sm font-bold text-slate-500 flex items-center gap-3 transition-all hover:bg-white/5 hover:text-white">
<i data-lucide="map" class="w-4 h-4"></i> Map Tiles API
<div class="mt-4 mb-2">
<h2 class="text-xs font-black uppercase tracking-wider text-slate-500" data-i18n="docs-sdks-title">Client SDKs</h2>
</div>
<a href="javascript:void(0)" data-section="sdks-ios" class="docs-nav-link p-3.5 rounded-xl text-sm font-bold text-slate-400 flex items-center gap-3 transition-all hover:bg-white/5 hover:text-white">
<i data-lucide="apple" class="w-4 h-4 text-slate-300"></i> iOS SDK (Swift)
</a>
<a href="javascript:void(0)" data-section="geocoding-api" class="docs-nav-link p-4 rounded-2xl text-sm font-bold text-slate-500 flex items-center gap-3 transition-all hover:bg-white/5 hover:text-white">
<i data-lucide="search" class="w-4 h-4"></i> Geocoding API
<a href="javascript:void(0)" data-section="sdks-android" class="docs-nav-link p-3.5 rounded-xl text-sm font-bold text-slate-400 flex items-center gap-3 transition-all hover:bg-white/5 hover:text-white">
<i data-lucide="smartphone" class="w-4 h-4 text-emerald-400"></i> Android SDK (Kotlin)
</a>
<a href="javascript:void(0)" data-section="routing-api" class="docs-nav-link p-4 rounded-2xl text-sm font-bold text-slate-500 flex items-center gap-3 transition-all hover:bg-white/5 hover:text-white">
<i data-lucide="navigation" class="w-4 h-4"></i> Routing API
<a href="javascript:void(0)" data-section="sdks-flutter" class="docs-nav-link p-3.5 rounded-xl text-sm font-bold text-slate-400 flex items-center gap-3 transition-all hover:bg-white/5 hover:text-white">
<i data-lucide="layers" class="w-4 h-4 text-cyan-400"></i> Flutter SDK (Dart)
</a>
<a href="javascript:void(0)" data-section="sdks" class="docs-nav-link p-4 rounded-2xl text-sm font-bold text-slate-500 flex items-center gap-3 transition-all hover:bg-white/5 hover:text-white">
<i data-lucide="package" class="w-4 h-4"></i> SDKs & Client Libraries
<a href="javascript:void(0)" data-section="sdks-web" class="docs-nav-link p-3.5 rounded-xl text-sm font-bold text-slate-400 flex items-center gap-3 transition-all hover:bg-white/5 hover:text-white">
<i data-lucide="globe" class="w-4 h-4 text-amber-400"></i> JavaScript / TS SDK
</a>
<div class="mt-4 mb-2">
<h2 class="text-xs font-black uppercase tracking-wider text-slate-500" data-i18n="docs-rest-title">REST APIs</h2>
</div>
<a href="javascript:void(0)" data-section="tiles-api" class="docs-nav-link p-3.5 rounded-xl text-sm font-bold text-slate-400 flex items-center gap-3 transition-all hover:bg-white/5 hover:text-white">
<i data-lucide="map" class="w-4 h-4 text-blue-400"></i> Map Vector Tiles
</a>
<a href="javascript:void(0)" data-section="geocoding-api" class="docs-nav-link p-3.5 rounded-xl text-sm font-bold text-slate-400 flex items-center gap-3 transition-all hover:bg-white/5 hover:text-white">
<i data-lucide="search" class="w-4 h-4 text-violet-400"></i> Geocoding & Places
</a>
<a href="javascript:void(0)" data-section="routing-api" class="docs-nav-link p-3.5 rounded-xl text-sm font-bold text-slate-400 flex items-center gap-3 transition-all hover:bg-white/5 hover:text-white">
<i data-lucide="navigation" class="w-4 h-4 text-rose-400"></i> Routing & Directions
</a>
</div>
@@ -669,13 +686,13 @@
</div>
<!-- core script -->
<script src="js/i18n.js"></script>
<script src="js/auth.js"></script>
<script src="js/docs.js"></script>
<script src="js/app.js"></script>
<script src="js/playground.js"></script>
<script src="js/analytics.js"></script>
<script src="js/billing.js"></script>
<script src="js/refinement.js"></script>
<script src="js/i18n.js?v=2.2"></script>
<script src="js/auth.js?v=2.2"></script>
<script src="js/docs.js?v=2.2"></script>
<script src="js/app.js?v=2.2"></script>
<script src="js/playground.js?v=2.2"></script>
<script src="js/analytics.js?v=2.2"></script>
<script src="js/billing.js?v=2.2"></script>
<script src="js/refinement.js?v=2.2"></script>
</body>
</html>
+506 -198
View File
@@ -1,7 +1,7 @@
/**
* Documentation Engine for Intaleq Dashboard
* Comprehensive API Reference (EN/AR)
* Updated with Premium Visuals, High-Performance Examples, and Official SDKs
* Commercial Enterprise Mapping Platform & Native SDKs (iOS Swift, Android Kotlin, Flutter Dart, Web JS/TS)
* Supports English & Arabic Localization
*/
const docs = {
@@ -21,10 +21,6 @@ const docs = {
e.preventDefault();
const section = e.currentTarget.getAttribute('data-section');
docs.renderSection(section);
// Active state management
document.querySelectorAll('.docs-nav-link').forEach(l => l.classList.remove('active', 'bg-blue-500/10', 'text-blue-400'));
e.currentTarget.classList.add('active', 'bg-blue-500/10', 'text-blue-400');
});
});
},
@@ -33,7 +29,19 @@ const docs = {
const container = document.getElementById('docs-content');
if (!container) return;
const lang = i18n.currentLang || 'en';
// Synchronize active sidebar navigation link
document.querySelectorAll('.docs-nav-link').forEach(l => {
const sec = l.getAttribute('data-section');
if (sec === id) {
l.classList.add('active', 'bg-blue-500/10', 'text-blue-400');
l.classList.remove('text-slate-400');
} else {
l.classList.remove('active', 'bg-blue-500/10', 'text-blue-400');
l.classList.add('text-slate-400');
}
});
const lang = (window.i18n && window.i18n.currentLang) || 'en';
const isAr = lang === 'ar';
const content = {
@@ -42,165 +50,416 @@ const docs = {
<div class="relative overflow-hidden rounded-[3rem] p-12 bg-gradient-to-br from-blue-600/20 via-blue-500/5 to-transparent border border-white/10 group">
<div class="absolute -top-24 -right-24 w-96 h-96 bg-blue-500/10 blur-[120px] rounded-full group-hover:bg-blue-500/20 transition-all duration-700"></div>
<div class="relative z-10">
<h3 class="text-5xl font-black mb-6 text-gradient">${isAr ? 'انطلق في ثوانٍ' : 'Launch in Seconds'}</h3>
<p class="text-slate-300 text-xl leading-relaxed max-w-2xl">
${isAr ? 'مرحباً بك في مستقبل الخرائط في المنطقة. توفر لك منصة "انطلاق" واجهات برمجية ذكية، خرائط Vector فائقة الدقة، ومباني ثلاثية الأبعاد متكاملة.' : 'Welcome to the future of regional mapping. Intaleq provides high-fidelity vector tiles, intelligent geocoding, and native 3D building support for Jordan & Syria.'}
<div class="inline-flex items-center gap-2 px-4 py-1.5 rounded-full bg-blue-500/20 text-blue-300 text-xs font-bold mb-4 border border-blue-500/30">
<i data-lucide="sparkles" class="w-3.5 h-3.5"></i>
${isAr ? 'منصة الخرائط والذكاء المكاني للأعمال' : 'Enterprise Commercial Mapping Platform'}
</div>
<h3 class="text-4xl md:text-5xl font-black mb-6 text-gradient">${isAr ? 'ابدأ التكامل مع منصة انطلاق' : 'Launch in Minutes'}</h3>
<p class="text-slate-300 text-lg md:text-xl leading-relaxed max-w-3xl">
${isAr ? 'توفر منصة "انطلاق" حلول الخرائط المتطورة لتطبيقات النقل الذكي (Ride-Hailing)، شركات التوصيل واللوجستيات (Delivery & Logistics)، والتجارة الإلكترونية، مع توفير يصل إلى 85% مقارنة بخرائط جوجل.' : 'Intaleq provides cutting-edge mapping infrastructure for Ride-Hailing, Delivery & Logistics, and E-commerce applications across Jordan & the MENA region at 85% lower cost than Google Maps.'}
</p>
<div class="flex gap-4 mt-8">
<a href="https://pub.dev/packages/intaleq_maps" target="_blank" class="px-6 py-3 bg-blue-600 text-white rounded-2xl font-black text-sm flex items-center gap-2 hover:bg-blue-500 transition-all">
<i data-lucide="package"></i> Flutter SDK
<div class="flex flex-wrap gap-4 mt-8">
<a href="javascript:void(0)" onclick="docs.renderSection('sdks-ios')" class="px-6 py-3 bg-slate-900 text-white rounded-2xl font-bold text-sm flex items-center gap-2 border border-white/10 hover:border-blue-500/50 hover:bg-slate-800 transition-all">
<i data-lucide="apple" class="w-4 h-4 text-slate-300"></i> iOS SDK (Swift)
</a>
<a href="https://www.npmjs.com/package/intaleq-maps-gl" target="_blank" class="px-6 py-3 bg-white text-slate-950 rounded-2xl font-black text-sm flex items-center gap-2 hover:bg-slate-100 transition-all">
<i data-lucide="package"></i> JS SDK
<a href="javascript:void(0)" onclick="docs.renderSection('sdks-android')" class="px-6 py-3 bg-slate-900 text-white rounded-2xl font-bold text-sm flex items-center gap-2 border border-white/10 hover:border-emerald-500/50 hover:bg-slate-800 transition-all">
<i data-lucide="smartphone" class="w-4 h-4 text-emerald-400"></i> Android SDK (Kotlin)
</a>
<a href="javascript:void(0)" onclick="docs.renderSection('sdks-flutter')" class="px-6 py-3 bg-blue-600 text-white rounded-2xl font-bold text-sm flex items-center gap-2 hover:bg-blue-500 shadow-lg shadow-blue-500/25 transition-all">
<i data-lucide="layers" class="w-4 h-4 text-cyan-200"></i> Flutter SDK
</a>
<a href="javascript:void(0)" onclick="docs.renderSection('sdks-web')" class="px-6 py-3 bg-white text-slate-950 rounded-2xl font-bold text-sm flex items-center gap-2 hover:bg-slate-100 transition-all">
<i data-lucide="globe" class="w-4 h-4 text-amber-500"></i> Web JavaScript / TS
</a>
</div>
</div>
</div>
<div class="grid grid-cols-1 md:grid-cols-3 gap-8">
<div class="glass p-10 rounded-[2.5rem] border-white/5 bg-gradient-to-b from-white/[0.02] to-transparent hover:border-blue-500/30 transition-all group">
<div class="w-16 h-16 rounded-2xl bg-blue-500/10 flex items-center justify-center text-blue-400 mb-8 font-black text-2xl group-hover:scale-110 transition-transform">01</div>
<h4 class="font-black text-2xl mb-4">${isAr ? 'مفتاح الوصول' : 'Access Key'}</h4>
<p class="text-slate-400 leading-relaxed">${isAr ? 'قم بإنشاء مفتاح API من لوحة التحكم لتفعيل طلباتك.' : 'Generate your secure API key from the dashboard to authenticate requests.'}</p>
<div class="grid grid-cols-1 md:grid-cols-3 gap-6">
<div class="glass p-8 rounded-[2rem] border-white/5 bg-gradient-to-b from-white/[0.02] to-transparent hover:border-blue-500/30 transition-all group">
<div class="w-14 h-14 rounded-2xl bg-blue-500/10 flex items-center justify-center text-blue-400 mb-6 font-black text-xl group-hover:scale-110 transition-transform">01</div>
<h4 class="font-black text-xl mb-3 text-white">${isAr ? 'مفتاح الـ API الآمن' : 'API Key Setup'}</h4>
<p class="text-slate-400 text-sm leading-relaxed">${isAr ? 'أنشئ مفتاح وصول محمي بضوابط النطاق (Domain Whitelist) لتطبيقاتك التجارية.' : 'Generate production API keys with IP/Domain restrictions to protect your usage.'}</p>
</div>
<div class="glass p-10 rounded-[2.5rem] border-white/5 bg-gradient-to-b from-white/[0.02] to-transparent hover:border-emerald-500/30 transition-all group">
<div class="w-16 h-16 rounded-2xl bg-emerald-500/10 flex items-center justify-center text-emerald-400 mb-8 font-black text-2xl group-hover:scale-110 transition-transform">02</div>
<h4 class="font-black text-2xl mb-4">${isAr ? 'تكامل الخريطة' : 'Map Integration'}</h4>
<p class="text-slate-400 leading-relaxed">${isAr ? 'اختر النمط (Obsidian أو Light) وادمج الخريطة في تطبيقك.' : 'Select a theme and integrate the vector tiles using our GL styles.'}</p>
<div class="glass p-8 rounded-[2rem] border-white/5 bg-gradient-to-b from-white/[0.02] to-transparent hover:border-emerald-500/30 transition-all group">
<div class="w-14 h-14 rounded-2xl bg-emerald-500/10 flex items-center justify-center text-emerald-400 mb-6 font-black text-xl group-hover:scale-110 transition-transform">02</div>
<h4 class="font-black text-xl mb-3 text-white">${isAr ? 'خرائط مخصصة بهويتك' : 'Custom Map Styling'}</h4>
<p class="text-slate-400 text-sm leading-relaxed">${isAr ? 'اختر النمط الفاتح النظيف للتوصيل، أو النمط الداكن الفخم لتطبيقات النقل، مع مباني 3D.' : 'Choose between Light Delivery or Obsidian Dark ride-hailing styles with 3D buildings.'}</p>
</div>
<div class="glass p-10 rounded-[2.5rem] border-white/5 bg-gradient-to-b from-white/[0.02] to-transparent hover:border-violet-500/30 transition-all group">
<div class="w-16 h-16 rounded-2xl bg-violet-500/10 flex items-center justify-center text-violet-400 mb-8 font-black text-2xl group-hover:scale-110 transition-transform">03</div>
<h4 class="font-black text-2xl mb-4">${isAr ? 'بيانات ذكية' : 'Smart Data'}</h4>
<p class="text-slate-400 leading-relaxed">${isAr ? 'استخدم خدمات البحث والتوجيه لإضافة ذكاء مكاني لتطبيقك.' : 'Leverage Geocoding and Routing APIs for advanced spatial intelligence.'}</p>
<div class="glass p-8 rounded-[2rem] border-white/5 bg-gradient-to-b from-white/[0.02] to-transparent hover:border-violet-500/30 transition-all group">
<div class="w-14 h-14 rounded-2xl bg-violet-500/10 flex items-center justify-center text-violet-400 mb-6 font-black text-xl group-hover:scale-110 transition-transform">03</div>
<h4 class="font-black text-xl mb-3 text-white">${isAr ? 'توجيه وبحث فائق الدقة' : 'Smart Routing & Search'}</h4>
<p class="text-slate-400 text-sm leading-relaxed">${isAr ? 'احسب مسار وتكلفة الرحلة لحظياً مع مصفوفة مطابقة السائقين بأقرب الطلبات.' : 'Calculate route fares, accurate ETAs, and multi-driver dispatch matrices in milliseconds.'}</p>
</div>
</div>
<div class="space-y-8 pt-6">
<div class="space-y-6 pt-4">
<div class="flex items-center gap-4">
<div class="h-8 w-1.5 bg-blue-500 rounded-full"></div>
<h4 class="text-3xl font-black">${isAr ? 'بيانات الوصول والمصادقة' : 'Domain & Authentication'}</h4>
<div class="h-7 w-1.5 bg-blue-500 rounded-full"></div>
<h4 class="text-2xl font-black text-white">${isAr ? 'بيانات الوصول والمصادقة المباشرة' : 'Production Endpoints & Authentication'}</h4>
</div>
<div class="bg-slate-950 rounded-[3rem] p-10 border border-slate-800 relative group overflow-hidden shadow-2xl">
<div class="absolute top-0 right-0 p-6 opacity-20 group-hover:opacity-100 transition-opacity">
<span class="bg-blue-500/10 text-blue-400 px-4 py-1.5 rounded-full text-xs font-black uppercase tracking-widest">Production URL</span>
<div class="bg-slate-950 rounded-[2.5rem] p-8 border border-slate-800 relative group overflow-hidden shadow-2xl">
<div class="absolute top-0 right-0 p-6 opacity-40 group-hover:opacity-100 transition-opacity">
<span class="bg-blue-500/10 text-blue-400 px-3 py-1 rounded-full text-xs font-black uppercase tracking-widest border border-blue-500/20">Production Endpoint</span>
</div>
<code class="text-blue-400 font-mono text-2xl block mb-6 select-all">https://map-saas.intaleq.com/api</code>
<div class="flex flex-col md:flex-row gap-8 text-slate-400">
<div class="flex-1 space-y-2">
<p class="text-sm font-bold uppercase text-slate-500 tracking-widest italic">${isAr ? 'طريقة المصادقة' : 'Auth Method'}</p>
<p class="text-lg">${isAr ? 'يتم إرسال المفتاح عبر الـ HTTP Header التالي:' : 'Pass your API key in the following HTTP header:'}</p>
<code class="text-blue-300 font-mono font-black text-xl">x-api-key</code>
<code class="text-blue-400 font-mono text-xl block mb-6 select-all">https://map-saas.intaleqapp.com/api</code>
<div class="grid grid-cols-1 md:grid-cols-2 gap-6 text-slate-400 pt-4 border-t border-slate-900">
<div>
<p class="text-xs font-bold uppercase text-slate-500 tracking-widest mb-1">${isAr ? 'المصادقة عبر الـ Header (موصى به في Backend/Apps)' : 'Header Auth (Recommended)'}</p>
<code class="text-blue-300 font-mono font-bold text-sm bg-blue-500/10 px-2 py-1 rounded">x-api-key: in_9478b32836d19cff73db3063</code>
</div>
<div class="flex-1 space-y-2 border-slate-800 md:border-l md:pl-8">
<p class="text-sm font-bold uppercase text-slate-500 tracking-widest italic">${isAr ? 'نطاق الوصول' : 'Allowed Origins'}</p>
<p class="text-lg">${isAr ? 'تأكد من إضافة النطاق الخاص بك في إعدادات المفتاح.' : 'Ensure your request origin is listed in the key restrictions.'}</p>
<div>
<p class="text-xs font-bold uppercase text-slate-500 tracking-widest mb-1">${isAr ? 'المصادقة عبر الـ Query (للخرائط المباشرة)' : 'Query Parameter Auth'}</p>
<code class="text-emerald-300 font-mono font-bold text-sm bg-emerald-500/10 px-2 py-1 rounded">?key=in_9478b32836d19cff73db3063</code>
</div>
</div>
</div>
</div>
</div>
`,
'sdks': `
<div class="space-y-12 animate-in fade-in slide-in-from-bottom-6 duration-700">
<header>
<h3 class="text-5xl font-black mb-4 text-gradient">${isAr ? 'المكتبات البرمجية (SDKs)' : 'SDKs & Client Libraries'}</h3>
<p class="text-slate-400 text-xl max-w-xl">${isAr ? 'استخدم مكتباتنا الجاهزة لدمج الخرائط والخدمات في ثوانٍ.' : 'Accelerate your development with our official enterprise-grade client libraries.'}</p>
'sdks-ios': `
<div class="space-y-10 animate-in fade-in slide-in-from-bottom-6 duration-700">
<header class="flex items-center gap-5 p-8 rounded-[2.5rem] bg-gradient-to-r from-slate-900 via-slate-900/60 to-transparent border border-white/10">
<div class="w-16 h-16 rounded-2xl bg-white/10 flex items-center justify-center text-white text-3xl shadow-xl">
<i data-lucide="apple" class="w-9 h-9"></i>
</div>
<div>
<div class="flex items-center gap-3 mb-1">
<h3 class="text-3xl font-black text-white">iOS Native SDK</h3>
<span class="px-3 py-0.5 rounded-full bg-blue-500/20 text-blue-400 text-xs font-bold border border-blue-500/30">Swift 5.9+ / UIKit & SwiftUI</span>
</div>
<p class="text-slate-400 text-base">${isAr ? 'مكتبة آبل الأصلية لتطبيقات النقل والتوصيل (Ride-Hailing & Delivery Apps) بسرعة 60 إطاراً في الثانية.' : 'Native iOS SDK for ride-hailing, driver tracking, and delivery logistics apps on iPhone & iPad.'}</p>
</div>
</header>
<div class="grid grid-cols-1 md:grid-cols-2 gap-10">
<!-- Flutter SDK Card -->
<div class="glass p-10 rounded-[3rem] border-white/5 flex flex-col hover:border-blue-500/30 transition-all">
<div class="flex items-center gap-4 mb-8">
<div class="w-12 h-12 rounded-xl bg-blue-500/10 flex items-center justify-center text-blue-400">
<i data-lucide="smartphone"></i>
</div>
<h4 class="text-2xl font-black text-white">Flutter SDK</h4>
<!-- Installation -->
<div class="space-y-4">
<h4 class="text-xl font-bold text-white flex items-center gap-2">
<i data-lucide="download" class="w-5 h-5 text-blue-400"></i> ${isAr ? '1. التثبيت (Installation)' : '1. Installation'}
</h4>
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
<div class="bg-slate-950 p-6 rounded-2xl border border-slate-800">
<p class="text-xs font-bold text-slate-400 uppercase tracking-wider mb-2">Swift Package Manager (SPM)</p>
<code class="text-xs text-blue-300 font-mono select-all block bg-white/5 p-3 rounded-xl">
https://github.com/maplibre/maplibre-native-spm
</code>
</div>
<p class="text-slate-400 mb-8 flex-1 leading-relaxed">
${isAr ? 'مكتبة متكاملة لنظام Flutter تدعم أندرويد و iOS والويب.' : 'A robust Flutter wrapper for MapLibre with native Intaleq services integrated.'}
</p>
<div class="bg-slate-950 p-6 rounded-2xl border border-slate-800 font-mono text-xs mb-8">
<span class="text-slate-500"># pubspec.yaml</span><br>
<span class="text-blue-400">intaleq_maps:</span> <span class="text-emerald-400">^1.0.0</span>
<div class="bg-slate-950 p-6 rounded-2xl border border-slate-800">
<p class="text-xs font-bold text-slate-400 uppercase tracking-wider mb-2">CocoaPods (Podfile)</p>
<code class="text-xs text-emerald-300 font-mono select-all block bg-white/5 p-3 rounded-xl">
pod 'MapLibre', '~> 5.13.0'
</code>
</div>
<a href="https://pub.dev/packages/intaleq_maps" target="_blank" class="btn btn-primary w-full justify-center">View on pub.dev</a>
</div>
<!-- JS SDK Card -->
<div class="glass p-10 rounded-[3rem] border-white/5 flex flex-col hover:border-emerald-500/30 transition-all">
<div class="flex items-center gap-4 mb-8">
<div class="w-12 h-12 rounded-xl bg-emerald-500/10 flex items-center justify-center text-emerald-400">
<i data-lucide="globe"></i>
</div>
<h4 class="text-2xl font-black text-white">JavaScript SDK</h4>
</div>
<p class="text-slate-400 mb-8 flex-1 leading-relaxed">
${isAr ? 'مكتبة JavaScript حديثة مدعومة بـ TypeScript لتطبيقات الويب.' : 'Modern TypeScript-ready SDK for seamless web map integration and routing.'}
</p>
<div class="bg-slate-950 p-6 rounded-2xl border border-slate-800 font-mono text-xs mb-8">
<span class="text-slate-500"># Install via NPM</span><br>
<span class="text-emerald-400">npm i intaleq-maps-gl</span>
</div>
<a href="https://www.npmjs.com/package/intaleq-maps-gl" target="_blank" class="btn btn-primary w-full justify-center">View on NPM</a>
</div>
</div>
<div class="bg-indigo-900/10 p-12 rounded-[3.5rem] border border-indigo-500/20">
<h4 class="text-2xl font-black mb-6 text-indigo-400">${isAr ? 'مثال: إضافة مؤشر (JS SDK)' : 'Example: Adding a Marker (JS SDK)'}</h4>
<pre class="bg-slate-950 p-10 rounded-[2.5rem] border border-slate-800 text-xs font-mono text-slate-300 leading-loose overflow-x-auto">
import { IntaleqMap } from 'intaleq-maps-gl';
<!-- Swift Code Example -->
<div class="space-y-4">
<div class="flex items-center justify-between">
<h4 class="text-xl font-bold text-white flex items-center gap-2">
<i data-lucide="code" class="w-5 h-5 text-blue-400"></i> ${isAr ? '2. مثال كود Swift كامل لتطبيق توصيل/نقل' : '2. Swift Implementation (Delivery / Ride-Hailing)'}
</h4>
<span class="text-xs font-mono text-slate-500">DeliveryMapViewController.swift</span>
</div>
<pre class="bg-slate-950 p-8 rounded-[2rem] border border-slate-800 text-xs font-mono text-slate-300 leading-relaxed overflow-x-auto select-all">
<span class="text-purple-400">import</span> UIKit
<span class="text-purple-400">import</span> MapLibre
const map = new IntaleqMap({
container: 'map',
apiKey: 'YOUR_KEY',
styleType: 'obsidian'
});
<span class="text-purple-400">class</span> <span class="text-yellow-300">DeliveryMapViewController</span>: <span class="text-blue-300">UIViewController</span>, <span class="text-blue-300">MLNMapViewDelegate</span> {
<span class="text-purple-400">var</span> mapView: <span class="text-blue-300">MLNMapView</span>!
<span class="text-purple-400">let</span> apiKey = <span class="text-emerald-300">"YOUR_INTALEQ_API_KEY"</span>
<span class="text-purple-400">override func</span> <span class="text-blue-400">viewDidLoad</span>() {
<span class="text-purple-400">super</span>.viewDidLoad()
<span class="text-slate-500">// 1. رابط نمط الخريطة التجاري من انطلاق</span>
<span class="text-purple-400">let</span> styleURL = <span class="text-blue-300">URL</span>(string: <span class="text-emerald-300">"https://map-saas.intaleqapp.com/tactical-style.json?key=\(apiKey)"</span>)!
<span class="text-slate-500">// 2. تهيئة الخريطة وتثبيت موقع البداية على عمان</span>
mapView = <span class="text-blue-300">MLNMapView</span>(frame: view.bounds, styleURL: styleURL)
mapView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
mapView.setCenter(<span class="text-blue-300">CLLocationCoordinate2D</span>(latitude: <span class="text-cyan-300">31.9539</span>, longitude: <span class="text-cyan-300">35.9106</span>), zoomLevel: <span class="text-cyan-300">14</span>, animated: <span class="text-purple-400">false</span>)
mapView.delegate = <span class="text-purple-400">self</span>
view.addSubview(mapView)
<span class="text-slate-500">// 3. إضافة علامة موقع السائق / العميل</span>
<span class="text-purple-400">let</span> pickupPoint = <span class="text-blue-300">MLNPointAnnotation</span>()
pickupPoint.coordinate = <span class="text-blue-300">CLLocationCoordinate2D</span>(latitude: <span class="text-cyan-300">31.9539</span>, longitude: <span class="text-cyan-300">35.9106</span>)
pickupPoint.title = <span class="text-emerald-300">"نقطة استلام الطلب"</span>
pickupPoint.subtitle = <span class="text-slate-400">"شارع مكة، عمان"</span>
mapView.addAnnotation(pickupPoint)
}
}</pre>
</div>
map.addIntaleqMarker({
position: [35.91, 31.95], // [lng, lat]
color: '#0D47A1'
});</pre>
<!-- SwiftUI Example -->
<div class="space-y-4">
<h4 class="text-xl font-bold text-white flex items-center gap-2">
<i data-lucide="layout" class="w-5 h-5 text-blue-400"></i> ${isAr ? '3. تكامل SwiftUI' : '3. SwiftUI View Component'}
</h4>
<pre class="bg-slate-950 p-8 rounded-[2rem] border border-slate-800 text-xs font-mono text-slate-300 leading-relaxed overflow-x-auto select-all">
<span class="text-purple-400">import</span> SwiftUI
<span class="text-purple-400">import</span> MapLibre
<span class="text-purple-400">struct</span> <span class="text-yellow-300">IntaleqMapView</span>: <span class="text-blue-300">UIViewRepresentable</span> {
<span class="text-purple-400">func</span> makeUIView(context: Context) -> <span class="text-blue-300">MLNMapView</span> {
<span class="text-purple-400">let</span> url = <span class="text-blue-300">URL</span>(string: <span class="text-emerald-300">"https://map-saas.intaleqapp.com/tactical-style.json"</span>)!
<span class="text-purple-400">let</span> map = <span class="text-blue-300">MLNMapView</span>(frame: .zero, styleURL: url)
map.setCenter(<span class="text-blue-300">CLLocationCoordinate2D</span>(latitude: <span class="text-cyan-300">31.9539</span>, longitude: <span class="text-cyan-300">35.9106</span>), zoomLevel: <span class="text-cyan-300">13</span>, animated: <span class="text-purple-400">false</span>)
<span class="text-purple-400">return</span> map
}
<span class="text-purple-400">func</span> updateUIView(_ uiView: <span class="text-blue-300">MLNMapView</span>, context: Context) {}
}</pre>
</div>
</div>
`,
'sdks-android': `
<div class="space-y-10 animate-in fade-in slide-in-from-bottom-6 duration-700">
<header class="flex items-center gap-5 p-8 rounded-[2.5rem] bg-gradient-to-r from-emerald-950/40 via-slate-900 to-transparent border border-white/10">
<div class="w-16 h-16 rounded-2xl bg-emerald-500/10 flex items-center justify-center text-emerald-400 text-3xl shadow-xl">
<i data-lucide="smartphone" class="w-9 h-9"></i>
</div>
<div>
<div class="flex items-center gap-3 mb-1">
<h3 class="text-3xl font-black text-white">Android Native SDK</h3>
<span class="px-3 py-0.5 rounded-full bg-emerald-500/20 text-emerald-400 text-xs font-bold border border-emerald-500/30">Kotlin & Jetpack Compose</span>
</div>
<p class="text-slate-400 text-base">${isAr ? 'مكتبة أندرويد لتطبيقات الكباتن والسائقين وتتبع مسارات الشحنات بكفاءة وسرعة فائقة.' : 'High-performance Android SDK for driver apps, delivery fleets, and real-time asset tracking.'}</p>
</div>
</header>
<!-- Gradle Setup -->
<div class="space-y-4">
<h4 class="text-xl font-bold text-white flex items-center gap-2">
<i data-lucide="download" class="w-5 h-5 text-emerald-400"></i> ${isAr ? '1. إعداد Gradle (build.gradle.kts)' : '1. Gradle Dependency'}
</h4>
<pre class="bg-slate-950 p-6 rounded-2xl border border-slate-800 text-xs font-mono text-emerald-300 select-all">
dependencies {
implementation(<span class="text-amber-300">"org.maplibre.gl:android-sdk:11.5.1"</span>)
}</pre>
</div>
<!-- Kotlin Code Example -->
<div class="space-y-4">
<div class="flex items-center justify-between">
<h4 class="text-xl font-bold text-white flex items-center gap-2">
<i data-lucide="code" class="w-5 h-5 text-emerald-400"></i> ${isAr ? '2. كود Kotlin لتطبيق السائق / النقل الذكي' : '2. Kotlin Implementation (Driver App)'}
</h4>
<span class="text-xs font-mono text-slate-500">DriverMapActivity.kt</span>
</div>
<pre class="bg-slate-950 p-8 rounded-[2rem] border border-slate-800 text-xs font-mono text-slate-300 leading-relaxed overflow-x-auto select-all">
<span class="text-purple-400">package</span> com.intaleq.driver
<span class="text-purple-400">import</span> android.os.Bundle
<span class="text-purple-400">import</span> androidx.appcompat.app.AppCompatActivity
<span class="text-purple-400">import</span> org.maplibre.android.MapLibre
<span class="text-purple-400">import</span> org.maplibre.android.camera.CameraPosition
<span class="text-purple-400">import</span> org.maplibre.android.geometry.LatLng
<span class="text-purple-400">import</span> org.maplibre.android.maps.MapView
<span class="text-purple-400">class</span> <span class="text-yellow-300">DriverMapActivity</span> : <span class="text-blue-300">AppCompatActivity</span>() {
<span class="text-purple-400">private lateinit var</span> mapView: <span class="text-blue-300">MapView</span>
<span class="text-purple-400">override fun</span> <span class="text-blue-400">onCreate</span>(savedInstanceState: <span class="text-blue-300">Bundle</span>?) {
<span class="text-purple-400">super</span>.onCreate(savedInstanceState)
<span class="text-slate-500">// 1. تهيئة محرك الخريطة</span>
<span class="text-blue-300">MapLibre</span>.getInstance(<span class="text-purple-400">this</span>)
setContentView(R.layout.activity_driver_map)
mapView = findViewById(R.id.mapView)
mapView.onCreate(savedInstanceState)
<span class="text-slate-500">// 2. تحميل نمط انطلاق التجاري</span>
<span class="text-purple-400">val</span> styleUrl = <span class="text-emerald-300">"https://map-saas.intaleqapp.com/tactical-style.json"</span>
mapView.getMapAsync { map ->
map.setStyle(styleUrl) { style ->
<span class="text-slate-500">// 3. تعيين موقع السائق وزاوية الرؤية 3D</span>
map.cameraPosition = <span class="text-blue-300">CameraPosition</span>.Builder()
.target(<span class="text-blue-300">LatLng</span>(<span class="text-cyan-300">31.9539</span>, <span class="text-cyan-300">35.9106</span>))
.zoom(<span class="text-cyan-300">14.0</span>)
.tilt(<span class="text-cyan-300">45.0</span>)
.build()
}
}
}
<span class="text-purple-400">override fun</span> <span class="text-blue-400">onResume</span>() { <span class="text-purple-400">super</span>.onResume(); mapView.onResume() }
<span class="text-purple-400">override fun</span> <span class="text-blue-400">onPause</span>() { <span class="text-purple-400">super</span>.onPause(); mapView.onPause() }
<span class="text-purple-400">override fun</span> <span class="text-blue-400">onDestroy</span>() { <span class="text-purple-400">super</span>.onDestroy(); mapView.onDestroy() }
}</pre>
</div>
</div>
`,
'sdks-flutter': `
<div class="space-y-10 animate-in fade-in slide-in-from-bottom-6 duration-700">
<header class="flex items-center gap-5 p-8 rounded-[2.5rem] bg-gradient-to-r from-cyan-950/40 via-slate-900 to-transparent border border-white/10">
<div class="w-16 h-16 rounded-2xl bg-cyan-500/10 flex items-center justify-center text-cyan-400 text-3xl shadow-xl">
<i data-lucide="layers" class="w-9 h-9"></i>
</div>
<div>
<div class="flex items-center gap-3 mb-1">
<h3 class="text-3xl font-black text-white">Flutter SDK</h3>
<span class="px-3 py-0.5 rounded-full bg-cyan-500/20 text-cyan-400 text-xs font-bold border border-cyan-500/30">Dart 3.5+ / iOS & Android</span>
</div>
<p class="text-slate-400 text-base">${isAr ? 'حزمة فلاتر الرسمية الموحدة لتطوير تطبيقات النقل والتوصيل الميداني على كلا النظامين بكود واحد.' : 'Official Flutter package for cross-platform commercial dispatch and ride-hailing apps.'}</p>
</div>
</header>
<!-- Pubspec Dependency -->
<div class="space-y-4">
<h4 class="text-xl font-bold text-white flex items-center gap-2">
<i data-lucide="download" class="w-5 h-5 text-cyan-400"></i> ${isAr ? '1. التثبيت (pubspec.yaml)' : '1. Pubspec Installation'}
</h4>
<pre class="bg-slate-950 p-6 rounded-2xl border border-slate-800 text-xs font-mono text-cyan-300 select-all">
dependencies:
intaleq_maps: ^1.0.0</pre>
</div>
<!-- Flutter Dart Code -->
<div class="space-y-4">
<div class="flex items-center justify-between">
<h4 class="text-xl font-bold text-white flex items-center gap-2">
<i data-lucide="code" class="w-5 h-5 text-cyan-400"></i> ${isAr ? '2. كود Flutter (Dart) لتطبيق التوصيل' : '2. Flutter Dart Widget'}
</h4>
<span class="text-xs font-mono text-slate-500">ride_tracking_page.dart</span>
</div>
<pre class="bg-slate-950 p-8 rounded-[2rem] border border-slate-800 text-xs font-mono text-slate-300 leading-relaxed overflow-x-auto select-all">
<span class="text-purple-400">import</span> <span class="text-emerald-300">'package:flutter/material.dart'</span>;
<span class="text-purple-400">import</span> <span class="text-emerald-300">'package:intaleq_maps/intaleq_maps.dart'</span>;
<span class="text-purple-400">class</span> <span class="text-yellow-300">RideTrackingPage</span> <span class="text-purple-400">extends</span> <span class="text-blue-300">StatelessWidget</span> {
<span class="text-purple-400">const</span> <span class="text-yellow-300">RideTrackingPage</span>({<span class="text-purple-400">super</span>.key});
<span class="text-purple-400">@override</span>
<span class="text-blue-300">Widget</span> build(<span class="text-blue-300">BuildContext</span> context) {
<span class="text-purple-400">return</span> <span class="text-blue-300">Scaffold</span>(
body: <span class="text-blue-300">IntaleqMap</span>(
apiKey: <span class="text-emerald-300">'YOUR_INTALEQ_KEY'</span>,
styleString: <span class="text-emerald-300">'https://map-saas.intaleqapp.com/tactical-style.json'</span>,
initialCameraPosition: <span class="text-purple-400">const</span> <span class="text-blue-300">CameraPosition</span>(
target: <span class="text-blue-300">LatLng</span>(<span class="text-cyan-300">31.9539</span>, <span class="text-cyan-300">35.9106</span>),
zoom: <span class="text-cyan-300">14.0</span>,
tilt: <span class="text-cyan-300">40.0</span>,
),
myLocationEnabled: <span class="text-purple-400">true</span>,
myLocationTrackingMode: <span class="text-blue-300">MyLocationTrackingMode</span>.Tracking,
onMapCreated: (controller) {
<span class="text-slate-500">// الخريطة جاهزة لعرض خط المسار وحركة السائق</span>
},
),
);
}
}</pre>
</div>
</div>
`,
'sdks-web': `
<div class="space-y-10 animate-in fade-in slide-in-from-bottom-6 duration-700">
<header class="flex items-center gap-5 p-8 rounded-[2.5rem] bg-gradient-to-r from-amber-950/40 via-slate-900 to-transparent border border-white/10">
<div class="w-16 h-16 rounded-2xl bg-amber-500/10 flex items-center justify-center text-amber-400 text-3xl shadow-xl">
<i data-lucide="globe" class="w-9 h-9"></i>
</div>
<div>
<div class="flex items-center gap-3 mb-1">
<h3 class="text-3xl font-black text-white">JavaScript / TypeScript SDK</h3>
<span class="px-3 py-0.5 rounded-full bg-amber-500/20 text-amber-400 text-xs font-bold border border-amber-500/30">Web & React / Vue / Angular</span>
</div>
<p class="text-slate-400 text-base">${isAr ? 'مكتبة الويب للوحات تحكم الإدارة (Dispatch Portals) وتتبع الأساطيل المباشر.' : 'Web mapping SDK for fleet dispatch dashboards and customer web tracking.'}</p>
</div>
</header>
<!-- NPM Install -->
<div class="space-y-4">
<h4 class="text-xl font-bold text-white flex items-center gap-2">
<i data-lucide="download" class="w-5 h-5 text-amber-400"></i> ${isAr ? '1. التثبيت عبر NPM' : '1. NPM Package'}
</h4>
<pre class="bg-slate-950 p-6 rounded-2xl border border-slate-800 text-xs font-mono text-amber-300 select-all">
npm install maplibre-gl @mapbox/mapbox-gl-rtl-text</pre>
</div>
<!-- Web Code Example -->
<div class="space-y-4">
<div class="flex items-center justify-between">
<h4 class="text-xl font-bold text-white flex items-center gap-2">
<i data-lucide="code" class="w-5 h-5 text-amber-400"></i> ${isAr ? '2. كود التكامل (JavaScript / TypeScript)' : '2. JavaScript / TypeScript Implementation'}
</h4>
<span class="text-xs font-mono text-slate-500">dispatch_map.ts</span>
</div>
<pre class="bg-slate-950 p-8 rounded-[2rem] border border-slate-800 text-xs font-mono text-slate-300 leading-relaxed overflow-x-auto select-all">
<span class="text-purple-400">import</span> maplibregl <span class="text-purple-400">from</span> <span class="text-emerald-300">'maplibre-gl'</span>;
<span class="text-purple-400">import</span> <span class="text-emerald-300">'maplibre-gl/dist/maplibre-gl.css'</span>;
<span class="text-slate-500">// 1. تفعيل محرك الخطوط العربية (RTL Plugin)</span>
maplibregl.setRTLTextPlugin(
<span class="text-emerald-300">'https://map-saas.intaleqapp.com/rtl-plugin.js'</span>,
<span class="text-purple-400">null</span>,
<span class="text-purple-400">true</span>
);
<span class="text-slate-500">// 2. تهيئة خريطة لوحة التحكم</span>
<span class="text-purple-400">const</span> map = <span class="text-purple-400">new</span> maplibregl.<span class="text-blue-300">Map</span>({
container: <span class="text-emerald-300">'map'</span>,
style: <span class="text-emerald-300">'https://map-saas.intaleqapp.com/tactical-style.json'</span>,
center: [<span class="text-cyan-300">35.9106</span>, <span class="text-cyan-300">31.9539</span>],
zoom: <span class="text-cyan-300">12.5</span>,
pitch: <span class="text-cyan-300">45</span>
});
<span class="text-slate-500">// 3. إضافة سائق على الخريطة</span>
<span class="text-purple-400">new</span> maplibregl.<span class="text-blue-300">Marker</span>({ color: <span class="text-emerald-300">'#0071E3'</span> })
.setLngLat([<span class="text-cyan-300">35.9106</span>, <span class="text-cyan-300">31.9539</span>])
.setPopup(<span class="text-purple-400">new</span> maplibregl.<span class="text-blue-300">Popup</span>().setHTML(<span class="text-emerald-300">'&lt;h4&gt;كابتن سيرو: أحمد (متاح للطلب)&lt;/h4&gt;'</span>))
.addTo(map);</pre>
</div>
</div>
`,
'tiles-api': `
<div class="space-y-12 animate-in fade-in slide-in-from-bottom-6 duration-700">
<header class="flex flex-col md:flex-row md:items-end justify-between gap-6">
<div>
<h3 class="text-5xl font-black mb-4 text-gradient">${isAr ? 'خرائط الـ Vector' : 'Vector Tiles API'}</h3>
<p class="text-slate-400 text-xl max-w-xl">${isAr ? 'خرائط تفاعلية فائقة السرعة تدعم العرض ثلاثي الأبعاد والتحكم الكامل في الخصائص.' : 'High-performance interactive maps with native 3D buildings and custom GL styles.'}</p>
<h3 class="text-4xl md:text-5xl font-black mb-4 text-gradient">${isAr ? 'خدمة خرائط الـ Vector Tiles' : 'Map Vector Tiles API'}</h3>
<p class="text-slate-400 text-lg max-w-2xl">${isAr ? 'خرائط متجهة تفاعلية فائقة السرعة تدعم العرض ثلاثي الأبعاد والتحكم في طبقات الطرق والمباني وأسماء الأحياء باللغة العربية.' : 'High-performance interactive vector tiles with native 3D buildings, Arabic typography, and commercial POIs.'}</p>
</div>
</header>
<div class="endpoint-card glass rounded-[3.5rem] border-white/5 overflow-hidden shadow-2xl bg-gradient-to-b from-white/[0.03] to-transparent">
<div class="p-8 bg-white/[0.04] border-b border-white/5 flex items-center justify-between">
<div class="flex items-center gap-6">
<div class="px-4 py-2 bg-emerald-500 text-white text-sm font-black rounded-xl shadow-lg shadow-emerald-500/20 uppercase tracking-tighter">GET</div>
<code class="text-lg font-bold text-slate-100 font-mono">/v1/maps/style.json</code>
<div class="endpoint-card glass rounded-[3rem] border-white/5 overflow-hidden shadow-2xl bg-gradient-to-b from-white/[0.03] to-transparent">
<div class="p-6 bg-white/[0.04] border-b border-white/5 flex items-center justify-between">
<div class="flex items-center gap-4">
<div class="px-3.5 py-1.5 bg-emerald-500 text-white text-xs font-black rounded-xl uppercase tracking-wider">GET</div>
<code class="text-base font-bold text-slate-100 font-mono">/v1/maps/style.json</code>
</div>
</div>
<div class="p-12">
<div class="grid grid-cols-1 lg:grid-cols-2 gap-12">
<div class="space-y-8">
<div>
<h5 class="text-[10px] font-black uppercase tracking-[0.2em] text-blue-400 mb-6">${isAr ? 'المعاملات المدعومة' : 'Query Parameters'}</h5>
<div class="space-y-4">
<div class="flex items-center justify-between p-4 bg-white/5 rounded-2xl border border-white/5">
<code class="text-blue-300 font-bold">theme</code>
<span class="text-[10px] font-mono text-slate-600 uppercase">Optional (obsidian | light)</span>
</div>
<div class="flex items-center justify-between p-4 bg-white/5 rounded-2xl border border-white/5">
<code class="text-blue-300 font-bold">key</code>
<span class="text-[10px] font-mono text-rose-500 uppercase">Required</span>
</div>
<div class="p-8">
<div class="grid grid-cols-1 lg:grid-cols-2 gap-8">
<div class="space-y-6">
<h5 class="text-xs font-black uppercase tracking-wider text-blue-400">${isAr ? 'المعاملات المدعومة (Parameters)' : 'Query Parameters'}</h5>
<div class="space-y-3">
<div class="flex items-center justify-between p-3.5 bg-white/5 rounded-xl border border-white/5">
<code class="text-blue-300 font-bold">theme</code>
<span class="text-xs font-mono text-slate-400">obsidian (داكن) | light (فاتح للتوصيل)</span>
</div>
<div class="flex items-center justify-between p-3.5 bg-white/5 rounded-xl border border-white/5">
<code class="text-blue-300 font-bold">key</code>
<span class="text-xs font-mono text-rose-400 font-bold">مفتاح API الخاص بك (مطلوب)</span>
</div>
</div>
</div>
<div class="bg-slate-950 rounded-[2.5rem] p-10 border border-slate-800 shadow-inner">
<h5 class="text-[10px] font-black uppercase tracking-[0.2em] text-slate-500 mb-6">${isAr ? 'رابط النمط المباشر' : 'Direct Style URL'}</h5>
<code class="text-xs text-blue-400 break-all select-all font-mono leading-relaxed">
https://map-saas.intaleq.com/api/v1/maps/style.json?theme=obsidian&key=YOUR_API_KEY
<div class="bg-slate-950 rounded-2xl p-6 border border-slate-800">
<h5 class="text-xs font-black uppercase tracking-wider text-slate-500 mb-4">${isAr ? 'رابط النمط المباشر (Direct Style URL)' : 'Direct Style URL'}</h5>
<code class="text-xs text-blue-400 break-all select-all font-mono leading-relaxed block bg-white/5 p-4 rounded-xl">
https://map-saas.intaleqapp.com/tactical-style.json?key=YOUR_API_KEY
</code>
</div>
</div>
@@ -208,56 +467,94 @@ map.addIntaleqMarker({
</div>
</div>
`,
'geocoding-api': `
<div class="space-y-12 animate-in fade-in slide-in-from-bottom-6 duration-700">
<header>
<h3 class="text-5xl font-black mb-4 text-gradient">${isAr ? 'البحث المكاني (Geocoding)' : 'Geocoding API'}</h3>
<p class="text-slate-400 text-xl max-w-2xl">${isAr ? 'حوّل العناوين إلى إحداثيات أو العكس بدقة غير مسبوقة في الأردن وسوريا.' : 'Transform addresses into coordinates or reverse resolve locations with extreme accuracy in the Levant region.'}</p>
<h3 class="text-4xl md:text-5xl font-black mb-4 text-gradient">${isAr ? 'خدمة البحث المكاني وعناوين التوصيل (Geocoding API)' : 'Geocoding & Places API'}</h3>
<p class="text-slate-400 text-lg max-w-2xl">${isAr ? 'محرك البحث الذكي لتحديد نقاط استلام وتوصيل الطلبات، والمطاعم، والمتاجر، والأحياء بدقة فائقة في الأردن وسوريا.' : 'Intelligent location search and reverse geocoding tailored for delivery pickups and street address resolution.'}</p>
</header>
<div class="endpoint-card glass rounded-[3.5rem] border-white/5 overflow-hidden shadow-2xl">
<div class="p-8 bg-blue-500/5 border-b border-white/5 flex items-center justify-between">
<!-- Forward Search -->
<div class="endpoint-card glass rounded-[3rem] border-white/5 overflow-hidden shadow-2xl mb-8">
<div class="p-6 bg-blue-500/5 border-b border-white/5 flex items-center justify-between">
<div class="flex items-center gap-4">
<div class="px-4 py-2 bg-blue-600 text-white text-xs font-black rounded-xl shadow-lg shadow-blue-500/20 uppercase tracking-widest">SEARCH</div>
<div class="px-3.5 py-1.5 bg-blue-600 text-white text-xs font-black rounded-xl uppercase tracking-wider">GET</div>
<code class="text-base font-bold text-slate-200">/v1/geocoding/search</code>
</div>
<span class="text-xs text-slate-400 font-bold">${isAr ? 'البحث عن الأماكن والعناوين' : 'Forward Place Search'}</span>
</div>
<div class="p-12">
<div class="grid grid-cols-1 lg:grid-cols-2 gap-12">
<div class="space-y-8">
<table class="w-full text-sm">
<thead class="text-[10px] text-slate-500 font-black uppercase tracking-[0.2em]">
<tr class="border-b border-white/5"><th class="pb-4">Param</th><th class="pb-4 text-right">Description</th></tr>
</thead>
<tbody class="text-slate-400 divide-y divide-white/[0.02]">
<tr><td class="py-4 font-bold text-blue-300 font-mono">q</td><td class="py-4 text-right">Query (e.g. "Masjid Hashem")</td></tr>
<tr><td class="py-4 font-bold text-blue-300 font-mono">limit</td><td class="py-4 text-right">Max results (Default: 5)</td></tr>
</tbody>
</table>
<div class="p-8">
<div class="grid grid-cols-1 lg:grid-cols-2 gap-8">
<div class="space-y-4">
<h5 class="text-xs font-black uppercase tracking-wider text-blue-400">${isAr ? 'معاملات الطلب (Query Params)' : 'Request Parameters'}</h5>
<div class="space-y-2 text-xs">
<div class="flex justify-between p-2.5 bg-white/5 rounded-lg border border-white/5"><code>q</code><span>نص البحث (مثال: "مطعم القدس شارع الجامعة")</span></div>
<div class="flex justify-between p-2.5 bg-white/5 rounded-lg border border-white/5"><code>lat, lng</code><span>إحداثيات العميل لترتيب الأقرب أولاً</span></div>
<div class="flex justify-between p-2.5 bg-white/5 rounded-lg border border-white/5"><code>limit</code><span>عدد النتائج المطلوبة (افتراضي: 5)</span></div>
</div>
</div>
<div class="bg-slate-950 rounded-[3rem] p-8 border border-slate-800 shadow-inner">
<div class="flex justify-between items-center mb-6">
<span class="text-[10px] font-black uppercase text-emerald-400 tracking-widest flex items-center gap-2">
<i data-lucide="layers" class="w-3 h-3"></i> Real-World Response
</span>
</div>
<pre class="text-[10px] font-mono text-emerald-300/80 leading-relaxed overflow-y-auto h-64">
<div class="bg-slate-950 rounded-2xl p-6 border border-slate-800">
<h5 class="text-xs font-black uppercase tracking-wider text-emerald-400 mb-3">${isAr ? 'مخرجات الـ JSON التجارية' : 'Commercial JSON Response'}</h5>
<pre class="text-[11px] font-mono text-emerald-300 leading-relaxed overflow-y-auto max-h-60 select-all">
{
"status": "success",
"query": "مطعم القدس شارع الجامعة",
"results": [
{
"id": 5843,
"name": "مسجد هاشم",
"name_ar": "مسجد هاشم",
"category": "building",
"governorate": "الزرقاء",
"location": { "lat": 32.10659, "lng": 36.18301 },
"full_address": "لواء قصبة الزرقاء، الزرقاء",
"distance_km": "12.68",
"source": "user_place"
"id": "poi_98231",
"name": "مطعم القدس",
"name_en": "Al Quds Restaurant",
"category": "restaurant",
"formatted_address": "شارع الجامعة الأردنية، الجبيهة، عمان",
"location": {
"lat": 32.01542,
"lng": 35.86981
},
"neighborhood": "الجبيهة",
"city": "عمان",
"confidence": 0.98
}
],
"source": "cache_hit"
]
}</pre>
</div>
</div>
</div>
</div>
<!-- Reverse Geocoding -->
<div class="endpoint-card glass rounded-[3rem] border-white/5 overflow-hidden shadow-2xl">
<div class="p-6 bg-emerald-500/5 border-b border-white/5 flex items-center justify-between">
<div class="flex items-center gap-4">
<div class="px-3.5 py-1.5 bg-emerald-600 text-white text-xs font-black rounded-xl uppercase tracking-wider">GET</div>
<code class="text-base font-bold text-slate-200">/v1/geocoding/reverse</code>
</div>
<span class="text-xs text-slate-400 font-bold">${isAr ? 'تحويل موقع السائق إلى عنوان وفاتورة' : 'Reverse Coordinate to Address'}</span>
</div>
<div class="p-8">
<div class="grid grid-cols-1 lg:grid-cols-2 gap-8">
<div class="space-y-4">
<h5 class="text-xs font-black uppercase tracking-wider text-emerald-400">${isAr ? 'معاملات الطلب' : 'Request Parameters'}</h5>
<div class="space-y-2 text-xs">
<div class="flex justify-between p-2.5 bg-white/5 rounded-lg border border-white/5"><code>lat</code><span>خط العرض لموقع السائق/المركبة</span></div>
<div class="flex justify-between p-2.5 bg-white/5 rounded-lg border border-white/5"><code>lng</code><span>خط الطول لموقع السائق/المركبة</span></div>
</div>
</div>
<div class="bg-slate-950 rounded-2xl p-6 border border-slate-800">
<h5 class="text-xs font-black uppercase tracking-wider text-emerald-400 mb-3">${isAr ? 'مخرجات العنوان التلقائي' : 'Resolved Address Response'}</h5>
<pre class="text-[11px] font-mono text-emerald-300 leading-relaxed overflow-y-auto max-h-60 select-all">
{
"status": "success",
"formatted_address": "شارع مكة، أم أذينة، عمان",
"street": "شارع مكة",
"neighborhood": "أم أذينة",
"city": "عمان",
"location": {
"lat": 31.9821,
"lng": 35.8574
}
}</pre>
</div>
</div>
@@ -265,60 +562,68 @@ map.addIntaleqMarker({
</div>
</div>
`,
'routing-api': `
<div class="space-y-12 animate-in fade-in slide-in-from-bottom-6 duration-700">
<header class="relative p-12 overflow-hidden rounded-[3.5rem] bg-slate-900 border border-white/5 md:flex md:items-center md:justify-between shadow-2xl">
<div class="absolute -top-24 -left-24 w-64 h-64 bg-violet-600/10 blur-[100px] rounded-full"></div>
<div class="relative z-10">
<h3 class="text-5xl font-black mb-4 text-gradient">${isAr ? 'محرك التوجيه (Routing)' : 'Routing Engine'}</h3>
<p class="text-slate-400 text-xl max-w-xl">${isAr ? 'حساب أسرع المسارات مع تحليلات لحظية لحركة المرور.' : 'Fast pathfinding with traffic-aware duration metrics.'}</p>
</div>
<header>
<h3 class="text-4xl md:text-5xl font-black mb-4 text-gradient">${isAr ? 'محرك التوجيه وحساب الأجرة والملاحة (Routing API)' : 'Routing & Navigation API'}</h3>
<p class="text-slate-400 text-lg max-w-2xl">${isAr ? 'حساب أسرع المسارات، تقدير وقت الوصول الحقيقي (ETA)، حساب مسافة الرحلة بدقة لحساب الأجرة، والتوجيه خطوة بخطوة.' : 'Turn-by-turn navigation engine with traffic-aware duration, distance metrics for fare calculation, and route polylines.'}</p>
</header>
<div class="endpoint-card glass rounded-[3.5rem] border-white/5 overflow-hidden shadow-2xl">
<div class="p-8 bg-violet-500/5 border-b border-white/5 flex items-center justify-between">
<div class="endpoint-card glass rounded-[3rem] border-white/5 overflow-hidden shadow-2xl">
<div class="p-6 bg-violet-500/5 border-b border-white/5 flex items-center justify-between">
<div class="flex items-center gap-4">
<div class="px-4 py-2 bg-violet-600 text-white text-xs font-black rounded-xl shadow-lg shadow-violet-500/20 uppercase tracking-widest">ROUTE</div>
<div class="px-3.5 py-1.5 bg-violet-600 text-white text-xs font-black rounded-xl uppercase tracking-wider">GET / POST</div>
<code class="text-base font-bold text-slate-200">/v1/routing/route</code>
</div>
<span class="text-xs text-slate-400 font-bold">${isAr ? 'حساب المسار وتوجيه السائق' : 'Route Navigation'}</span>
</div>
<div class="p-12">
<div class="grid grid-cols-1 lg:grid-cols-2 gap-12">
<div class="space-y-8">
<div class="p-6 bg-slate-900/40 rounded-3xl border border-white/5">
<h5 class="text-[10px] font-black uppercase tracking-widest text-violet-400 mb-4">Required Parameters</h5>
<ul class="text-xs text-slate-400 space-y-4">
<li class="flex justify-between border-b border-white/5 pb-2"><span>start</span> <span class="text-violet-300 font-mono italic">"35.91,31.95"</span></li>
<li class="flex justify-between border-b border-white/5 pb-2"><span>end</span> <span class="text-violet-300 font-mono italic">"35.85,31.82"</span></li>
<li class="flex justify-between"><span>profile</span> <span class="text-slate-500">car | bike | foot</span></li>
</ul>
<div class="p-8">
<div class="grid grid-cols-1 lg:grid-cols-2 gap-8">
<div class="space-y-4">
<h5 class="text-xs font-black uppercase tracking-wider text-violet-400">${isAr ? 'معاملات طلب الرحلة (Params)' : 'Parameters'}</h5>
<div class="p-5 bg-slate-900/50 rounded-2xl border border-white/5 text-xs text-slate-300 space-y-3">
<div class="flex justify-between border-b border-white/5 pb-2"><span>start</span><code class="text-violet-300 font-mono">35.9106,31.9539</code></div>
<div class="flex justify-between border-b border-white/5 pb-2"><span>end</span><code class="text-violet-300 font-mono">36.0380,32.1351</code></div>
<div class="flex justify-between border-b border-white/5 pb-2"><span>profile</span><span class="text-slate-400">car (سيارة) | delivery (دراجة توصيل)</span></div>
<div class="flex justify-between"><span>traffic</span><span class="text-emerald-400 font-bold">true (حساب الازدحام المروري)</span></div>
</div>
</div>
<div class="bg-slate-950 rounded-[3rem] p-10 border border-slate-800 shadow-inner">
<div class="flex justify-between items-center mb-6">
<span class="text-[10px] font-black uppercase text-violet-400 tracking-widest flex items-center gap-2">
<i data-lucide="activity" class="w-3.5 h-3.5"></i> Production Response
</span>
</div>
<pre class="text-[10px] font-mono text-violet-300/80 leading-loose overflow-x-auto h-64">
<div class="bg-slate-950 rounded-2xl p-6 border border-slate-800">
<h5 class="text-xs font-black uppercase tracking-wider text-violet-400 mb-3">${isAr ? 'المخرجات التجارية لحساب الأجرة والمسار' : 'Commercial Response (Fare & ETA)'}</h5>
<pre class="text-[11px] font-mono text-violet-300 leading-relaxed overflow-y-auto max-h-72 select-all">
{
"distance": 30313.8,
"duration": 1975,
"trafficAwareDuration": 1975,
"points": "_cvbE_th{Ev@iJpHtDLEJKz@^XJn@...",
"instructions": [
{
"text": "Continue onto شارع الأمير الحسن",
"distance": 172.8,
"street_name": "شارع الأمير الحسن"
},
{
"text": "اتجه قليلاً لليمين خلال شارع الجيش",
"distance": 190.1,
"street_name": "شارع الجيش"
}
]
"status": "success",
"route": {
"distance_meters": 8450,
"distance_km": 8.45,
"duration_seconds": 780,
"duration_minutes": 13.0,
"traffic_delay_seconds": 120,
"estimated_fare_jod": 2.53,
"geometry": "_cvbE_th{Ev@iJpHtDLEJKz@^XJn@...",
"steps": [
{
"instruction": "انطلق باتجاه الشمال على شارع وصفي التل",
"instruction_en": "Head north on Wasfi Al-Tal St",
"distance_meters": 1200,
"duration_seconds": 110
},
{
"instruction": "اتجه يميناً عند دوار الواحة نحو شارع المدينة المنورة",
"instruction_en": "Turn right at Al-Waha Circle onto Al-Madina St",
"distance_meters": 3400,
"duration_seconds": 310
},
{
"instruction": "لقد وصلت إلى وجهتك على اليمين",
"instruction_en": "You have arrived at your destination on the right",
"distance_meters": 0,
"duration_seconds": 0
}
]
}
}</pre>
</div>
</div>
@@ -328,9 +633,12 @@ map.addIntaleqMarker({
`
};
container.innerHTML = content[id] || '<div class="h-64 flex items-center justify-center italic text-slate-600">Documentation section coming soon...</div>';
// Fallback for generic 'sdks'
content['sdks'] = content['sdks-flutter'];
container.innerHTML = content[id] || '<div class="h-64 flex items-center justify-center italic text-slate-500">Documentation section coming soon...</div>';
// Re-initialize icons
// Re-initialize lucide icons
if (window.lucide) lucide.createIcons();
}
};
+10 -2
View File
@@ -139,7 +139,11 @@ const i18n = {
'modal-key-label': 'Key Name',
'cancel': 'Cancel',
'guides-title': 'Guides',
'side-sdks': 'SDKs & Libraries'
'side-sdks': 'SDKs & Libraries',
'docs-overview': 'Overview',
'docs-getting-started': 'Getting Started',
'docs-sdks-title': 'Client SDKs',
'docs-rest-title': 'REST APIs'
},
ar: {
// Navbar (Landing)
@@ -272,7 +276,11 @@ const i18n = {
'modal-key-label': 'اسم المفتاح',
'cancel': 'إلغاء',
'guides-title': 'الأدلة برمجية',
'side-sdks': 'المكتبات البرمجية (SDKs)'
'side-sdks': 'المكتبات البرمجية (SDKs)',
'docs-overview': 'نظرة عامة',
'docs-getting-started': 'البدء السريع',
'docs-sdks-title': 'المكتبات وحزم الـ SDK',
'docs-rest-title': 'واجهات الـ REST API'
}
},
+3
View File
@@ -0,0 +1,3 @@
# MapSaaS Sovereign API Keys Template
MAP_SAAS_API_KEY=in_xxxxxxxxxxxxxxxxxxxxxxxx
GOOGLE_MAP_API_KEY=AIzaSyxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
+50
View File
@@ -0,0 +1,50 @@
# Miscellaneous
*.class
*.log
*.pyc
*.swp
.DS_Store
.atom/
.build/
.buildlog/
.history
.svn/
.swiftpm/
migrate_working_dir/
# IntelliJ related
*.iml
*.ipr
*.iws
.idea/
# The .vscode folder contains launch configuration and tasks you configure in
# VS Code which you may wish to be included in version control, so this line
# is commented out by default.
#.vscode/
# Flutter/Dart/Pub related
**/doc/api/
**/ios/Flutter/.last_build_id
.dart_tool/
.flutter-plugins-dependencies
.pub-cache/
.pub/
/build/
/coverage/
# Symbolication related
app.*.symbols
# Obfuscation related
app.*.map.json
# Android Studio will place build artifacts here
/android/app/debug
/android/app/profile
/android/app/release
# Environment variables & secrets
.env
.env.*
!.env.example
+45
View File
@@ -0,0 +1,45 @@
# This file tracks properties of this Flutter project.
# Used by Flutter tool to assess capabilities and perform upgrades etc.
#
# This file should be version controlled and should not be manually edited.
version:
revision: "ee80f08bbf97172ec030b8751ceab557177a34a6"
channel: "stable"
project_type: app
# Tracks metadata for the flutter migrate command
migration:
platforms:
- platform: root
create_revision: ee80f08bbf97172ec030b8751ceab557177a34a6
base_revision: ee80f08bbf97172ec030b8751ceab557177a34a6
- platform: android
create_revision: ee80f08bbf97172ec030b8751ceab557177a34a6
base_revision: ee80f08bbf97172ec030b8751ceab557177a34a6
- platform: ios
create_revision: ee80f08bbf97172ec030b8751ceab557177a34a6
base_revision: ee80f08bbf97172ec030b8751ceab557177a34a6
- platform: linux
create_revision: ee80f08bbf97172ec030b8751ceab557177a34a6
base_revision: ee80f08bbf97172ec030b8751ceab557177a34a6
- platform: macos
create_revision: ee80f08bbf97172ec030b8751ceab557177a34a6
base_revision: ee80f08bbf97172ec030b8751ceab557177a34a6
- platform: web
create_revision: ee80f08bbf97172ec030b8751ceab557177a34a6
base_revision: ee80f08bbf97172ec030b8751ceab557177a34a6
- platform: windows
create_revision: ee80f08bbf97172ec030b8751ceab557177a34a6
base_revision: ee80f08bbf97172ec030b8751ceab557177a34a6
# User provided section
# List of Local paths (relative to this file) that should be
# ignored by the migrate tool.
#
# Files that are not part of the templates will be ignored by default.
unmanaged_files:
- 'lib/main.dart'
- 'ios/Runner.xcodeproj/project.pbxproj'
+17
View File
@@ -0,0 +1,17 @@
# siro_maps
A new Flutter project.
## Getting Started
This project is a starting point for a Flutter application.
A few resources to get you started if this is your first Flutter project:
- [Learn Flutter](https://docs.flutter.dev/get-started/learn-flutter)
- [Write your first Flutter app](https://docs.flutter.dev/get-started/codelab)
- [Flutter learning resources](https://docs.flutter.dev/reference/learning-resources)
For help getting started with Flutter development, view the
[online documentation](https://docs.flutter.dev/), which offers tutorials,
samples, guidance on mobile development, and a full API reference.
+28
View File
@@ -0,0 +1,28 @@
# This file configures the analyzer, which statically analyzes Dart code to
# check for errors, warnings, and lints.
#
# The issues identified by the analyzer are surfaced in the UI of Dart-enabled
# IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be
# invoked from the command line by running `flutter analyze`.
# The following line activates a set of recommended lints for Flutter apps,
# packages, and plugins designed to encourage good coding practices.
include: package:flutter_lints/flutter.yaml
linter:
# The lint rules applied to this project can be customized in the
# section below to disable rules from the `package:flutter_lints/flutter.yaml`
# included above or to enable additional rules. A list of all available lints
# and their documentation is published at https://dart.dev/lints.
#
# Instead of disabling a lint rule for the entire project in the
# section below, it can also be suppressed for a single line of code
# or a specific dart file by using the `// ignore: name_of_lint` and
# `// ignore_for_file: name_of_lint` syntax on the line or in the file
# producing the lint.
rules:
avoid_print: false
# prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule
# Additional information about this file can be found at
# https://dart.dev/guides/language/analysis-options
+14
View File
@@ -0,0 +1,14 @@
gradle-wrapper.jar
/.gradle
/captures/
/gradlew
/gradlew.bat
/local.properties
GeneratedPluginRegistrant.java
.cxx/
# Remember to never publicly share your keystore.
# See https://flutter.dev/to/reference-keystore
key.properties
**/*.keystore
**/*.jks
@@ -0,0 +1,49 @@
plugins {
id("com.android.application")
// The Flutter Gradle Plugin must be applied after the Android and Kotlin Gradle plugins.
id("dev.flutter.flutter-gradle-plugin")
}
android {
namespace = "com.siro_map.siro_maps"
compileSdk = flutter.compileSdkVersion
ndkVersion = flutter.ndkVersion
compileOptions {
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
}
defaultConfig {
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
applicationId = "com.siro_map.siro_maps"
// You can update the following values to match your application needs.
// For more information, see: https://flutter.dev/to/review-gradle-config.
minSdk = 23
targetSdk = flutter.targetSdkVersion
versionCode = flutter.versionCode
versionName = flutter.versionName
}
buildTypes {
release {
// TODO: Add your own signing config for the release build.
// Signing with the debug keys for now, so `flutter run --release` works.
signingConfig = signingConfigs.getByName("debug")
}
}
}
dependencies {
implementation("androidx.car.app:app:1.4.0")
}
kotlin {
compilerOptions {
jvmTarget = org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_17
}
}
flutter {
source = "../.."
}
@@ -0,0 +1,7 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<!-- The INTERNET permission is required for development. Specifically,
the Flutter tool needs it to communicate with the running application
to allow setting breakpoints, to provide hot reload, etc.
-->
<uses-permission android:name="android.permission.INTERNET"/>
</manifest>
@@ -0,0 +1,89 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<!-- Permissions for High-Precision Navigation and Background Audio -->
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION"/>
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_LOCATION"/>
<uses-permission android:name="android.permission.POST_NOTIFICATIONS"/>
<uses-permission android:name="android.permission.WAKE_LOCK"/>
<application
android:label="خرائط سيرو"
android:name="${applicationName}"
android:icon="@mipmap/ic_launcher">
<activity
android:name=".MainActivity"
android:exported="true"
android:launchMode="singleTop"
android:taskAffinity=""
android:theme="@style/LaunchTheme"
android:supportsPictureInPicture="true"
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
android:hardwareAccelerated="true"
android:windowSoftInputMode="adjustResize">
<!-- Specifies an Android theme to apply to this Activity as soon as
the Android process has started. This theme is visible to the user
while the Flutter UI initializes. After that, this theme continues
to determine the Window background behind the Flutter UI. -->
<meta-data
android:name="io.flutter.embedding.android.NormalTheme"
android:resource="@style/NormalTheme"
/>
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
<!-- Custom URI Scheme: siromaps:// -->
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="siromaps" />
</intent-filter>
<!-- Standard Geo Scheme: geo:lat,lng -->
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="geo" />
</intent-filter>
</activity>
<!-- Don't delete the meta-data below.
This is used by the Flutter tool to generate GeneratedPluginRegistrant.java -->
<meta-data
android:name="flutterEmbedding"
android:value="2" />
<!-- Android Auto Navigation Service -->
<service
android:name=".car.SiroCarAppService"
android:exported="true">
<intent-filter>
<action android:name="androidx.car.app.CarAppService" />
<category android:name="androidx.car.app.category.NAVIGATION"/>
</intent-filter>
</service>
<meta-data
android:name="androidx.car.app.minCarApiLevel"
android:value="1" />
<meta-data
android:name="com.google.android.gms.car.application"
android:resource="@xml/automotive_app_desc" />
</application>
<!-- Required to query activities that can process text, see:
https://developer.android.com/training/package-visibility and
https://developer.android.com/reference/android/content/Intent#ACTION_PROCESS_TEXT.
In particular, this is used by the Flutter engine in io.flutter.plugin.text.ProcessTextPlugin. -->
<queries>
<intent>
<action android:name="android.intent.action.PROCESS_TEXT"/>
<data android:mimeType="text/plain"/>
</intent>
</queries>
</manifest>
@@ -0,0 +1,63 @@
package com.siro_map.siro_maps
import com.siro_map.siro_maps.car.CarNavigationState
import com.siro_map.siro_maps.car.SiroCarAppService
import io.flutter.embedding.android.FlutterActivity
import io.flutter.embedding.engine.FlutterEngine
import io.flutter.plugin.common.MethodChannel
class MainActivity : FlutterActivity() {
private val CHANNEL = "com.siro.siro_maps/car_navigation"
override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
super.configureFlutterEngine(flutterEngine)
MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL).setMethodCallHandler { call, result ->
when (call.method) {
"isCarAppConnected" -> {
result.success(SiroCarAppService.isConnected)
}
"updateNavState" -> {
try {
val lat = call.argument<Double>("lat") ?: 0.0
val lng = call.argument<Double>("lng") ?: 0.0
val bearing = call.argument<Double>("bearing") ?: 0.0
val speed = call.argument<Double>("speed") ?: 0.0
val instruction = call.argument<String>("instruction") ?: ""
val distanceToStep = call.argument<Double>("distanceToStep") ?: 0.0
val totalDistance = call.argument<Double>("totalDistance") ?: 0.0
val eta = call.argument<Double>("eta") ?: 0.0
val maneuver = call.argument<Int>("maneuver") ?: 0
val isNavigating = call.argument<Boolean>("isNavigating") ?: false
val isMapDarkMode = call.argument<Boolean>("isMapDarkMode") ?: false
val newState = CarNavigationState(
lat = lat,
lng = lng,
bearing = bearing,
speed = speed,
instruction = instruction,
distanceToStep = distanceToStep,
totalDistance = totalDistance,
eta = eta,
maneuver = maneuver,
isNavigating = isNavigating,
isMapDarkMode = isMapDarkMode
)
SiroCarAppService.updateNavState(newState)
result.success(true)
} catch (e: Exception) {
result.error("UPDATE_FAILED", e.localizedMessage, null)
}
}
"stopNavigation" -> {
SiroCarAppService.stopNavigation()
result.success(true)
}
else -> {
result.notImplemented()
}
}
}
}
}
@@ -0,0 +1,44 @@
package com.siro_map.siro_maps.car
data class CarNavigationState(
val lat: Double = 0.0,
val lng: Double = 0.0,
val bearing: Double = 0.0,
val speed: Double = 0.0,
val instruction: String = "",
val distanceToStep: Double = 0.0,
val totalDistance: Double = 0.0,
val eta: Double = 0.0,
val maneuver: Int = 0,
val isNavigating: Boolean = false,
val isMapDarkMode: Boolean = false
) {
val formattedSpeed: String
get() = "${speed.toInt()} كم/س"
val formattedRemainingDistance: String
get() = if (totalDistance >= 1000) {
String.format("%.1f كم", totalDistance / 1000.0)
} else {
"${totalDistance.toInt()} م"
}
val formattedDistanceToStep: String
get() = if (distanceToStep >= 1000) {
String.format("بعد %.1f كم", distanceToStep / 1000.0)
} else {
"بعد ${distanceToStep.toInt()} م"
}
val formattedRemainingDuration: String
get() {
val minutes = (eta / 60.0).toInt()
return if (minutes >= 60) {
val hours = minutes / 60
val remMin = minutes % 60
"$hours س $remMin د"
} else {
"$minutes دقيقة"
}
}
}
@@ -0,0 +1,41 @@
package com.siro_map.siro_maps.car
import androidx.car.app.CarAppService
import androidx.car.app.Session
import androidx.car.app.validation.HostValidator
class SiroCarAppService : CarAppService() {
companion object {
var currentNavState: CarNavigationState = CarNavigationState()
var activeSession: SiroCarSession? = null
fun updateNavState(state: CarNavigationState) {
currentNavState = state
activeSession?.requestScreenUpdate()
}
fun stopNavigation() {
currentNavState = currentNavState.copy(isNavigating = false)
activeSession?.requestScreenUpdate()
}
val isConnected: Boolean
get() = activeSession != null
}
override fun createHostValidator(): HostValidator {
return HostValidator.ALLOW_ALL_HOSTS_VALIDATOR
}
override fun onCreateSession(): Session {
val session = SiroCarSession()
activeSession = session
return session
}
override fun onDestroy() {
activeSession = null
super.onDestroy()
}
}
@@ -0,0 +1,23 @@
package com.siro_map.siro_maps.car
import android.content.Intent
import androidx.car.app.Screen
import androidx.car.app.Session
class SiroCarSession : Session() {
private var activeScreen: SiroNavScreen? = null
init {
SiroCarAppService.activeSession = this
}
override fun onCreateScreen(intent: Intent): Screen {
val screen = SiroNavScreen(carContext)
activeScreen = screen
return screen
}
fun requestScreenUpdate() {
activeScreen?.invalidate()
}
}
@@ -0,0 +1,92 @@
package com.siro_map.siro_maps.car
import androidx.car.app.CarContext
import androidx.car.app.Screen
import androidx.car.app.model.*
class SiroNavScreen(carContext: CarContext) : Screen(carContext) {
override fun onGetTemplate(): Template {
val state = SiroCarAppService.currentNavState
return if (state.isNavigating) {
buildActiveNavTemplate(state)
} else {
buildIdleTemplate(state)
}
}
private fun buildActiveNavTemplate(state: CarNavigationState): Template {
val paneBuilder = Pane.Builder()
// 1. Current instruction & distance
paneBuilder.addRow(
Row.Builder()
.setTitle(state.instruction.ifEmpty { "تابع السير نحو الوجهة" })
.addText(state.formattedDistanceToStep)
.build()
)
// 2. Trip summary (Distance and Duration)
paneBuilder.addRow(
Row.Builder()
.setTitle("المسار المتبقي")
.addText("${state.formattedRemainingDistance} • الوصول خلال ${state.formattedRemainingDuration}")
.build()
)
// 3. Live Speed
paneBuilder.addRow(
Row.Builder()
.setTitle("السرعة الحالية")
.addText(state.formattedSpeed)
.build()
)
// Stop navigation action button
paneBuilder.addAction(
Action.Builder()
.setTitle("إنهاء الملاحة")
.setOnClickListener {
SiroCarAppService.stopNavigation()
invalidate()
}
.build()
)
return PaneTemplate.Builder(paneBuilder.build())
.setTitle("ملاحة سيرو • جارية الآن")
.setHeaderAction(Action.APP_ICON)
.build()
}
private fun buildIdleTemplate(state: CarNavigationState): Template {
val paneBuilder = Pane.Builder()
paneBuilder.addRow(
Row.Builder()
.setTitle("خرائط سيرو السيادية (Siro Maps)")
.addText("التطبيق متصل بنجاح بشاشة السيارة")
.build()
)
paneBuilder.addRow(
Row.Builder()
.setTitle("وضع القيادة الحر")
.addText("السرعة: ${state.formattedSpeed}")
.build()
)
paneBuilder.addRow(
Row.Builder()
.setTitle("بدء الملاحة")
.addText("حدد وجهتك من شاشة الهاتف للانتقال الفوري إلى وضع الملاحة ثلاثية الأبعاد")
.build()
)
return PaneTemplate.Builder(paneBuilder.build())
.setTitle("خرائط سيرو")
.setHeaderAction(Action.APP_ICON)
.build()
}
}
@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Modify this file to customize your launch splash screen -->
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="?android:colorBackground" />
<!-- You can insert your own image assets here -->
<!-- <item>
<bitmap
android:gravity="center"
android:src="@mipmap/launch_image" />
</item> -->
</layer-list>
@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Modify this file to customize your launch splash screen -->
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@android:color/white" />
<!-- You can insert your own image assets here -->
<!-- <item>
<bitmap
android:gravity="center"
android:src="@mipmap/launch_image" />
</item> -->
</layer-list>
Binary file not shown.

After

Width:  |  Height:  |  Size: 7.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- Theme applied to the Android Window while the process is starting when the OS's Dark Mode setting is on -->
<style name="LaunchTheme" parent="@android:style/Theme.Black.NoTitleBar">
<!-- Show a splash screen on the activity. Automatically removed when
the Flutter engine draws its first frame -->
<item name="android:windowBackground">@drawable/launch_background</item>
</style>
<!-- Theme applied to the Android Window as soon as the process has started.
This theme determines the color of the Android Window while your
Flutter UI initializes, as well as behind your Flutter UI while its
running.
This Theme is only used starting with V2 of Flutter's Android embedding. -->
<style name="NormalTheme" parent="@android:style/Theme.Black.NoTitleBar">
<item name="android:windowBackground">?android:colorBackground</item>
</style>
</resources>
@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- Theme applied to the Android Window while the process is starting when the OS's Dark Mode setting is off -->
<style name="LaunchTheme" parent="@android:style/Theme.Light.NoTitleBar">
<!-- Show a splash screen on the activity. Automatically removed when
the Flutter engine draws its first frame -->
<item name="android:windowBackground">@drawable/launch_background</item>
</style>
<!-- Theme applied to the Android Window as soon as the process has started.
This theme determines the color of the Android Window while your
Flutter UI initializes, as well as behind your Flutter UI while its
running.
This Theme is only used starting with V2 of Flutter's Android embedding. -->
<style name="NormalTheme" parent="@android:style/Theme.Light.NoTitleBar">
<item name="android:windowBackground">?android:colorBackground</item>
</style>
</resources>
@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<automotiveApp>
<uses name="navigation" />
</automotiveApp>
@@ -0,0 +1,7 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<!-- The INTERNET permission is required for development. Specifically,
the Flutter tool needs it to communicate with the running application
to allow setting breakpoints, to provide hot reload, etc.
-->
<uses-permission android:name="android.permission.INTERNET"/>
</manifest>
+24
View File
@@ -0,0 +1,24 @@
allprojects {
repositories {
google()
mavenCentral()
}
}
val newBuildDir: Directory =
rootProject.layout.buildDirectory
.dir("../../build")
.get()
rootProject.layout.buildDirectory.value(newBuildDir)
subprojects {
val newSubprojectBuildDir: Directory = newBuildDir.dir(project.name)
project.layout.buildDirectory.value(newSubprojectBuildDir)
}
subprojects {
project.evaluationDependsOn(":app")
}
tasks.register<Delete>("clean") {
delete(rootProject.layout.buildDirectory)
}
+6
View File
@@ -0,0 +1,6 @@
org.gradle.jvmargs=-Xmx8G -XX:MaxMetaspaceSize=4G -XX:ReservedCodeCacheSize=512m -XX:+HeapDumpOnOutOfMemoryError
android.useAndroidX=true
# This newDsl flag was added by the Flutter template
android.newDsl=false
# This builtInKotlin flag was added by the Flutter template
android.builtInKotlin=false
@@ -0,0 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-9.1.0-all.zip
@@ -0,0 +1,26 @@
pluginManagement {
val flutterSdkPath =
run {
val properties = java.util.Properties()
file("local.properties").inputStream().use { properties.load(it) }
val flutterSdkPath = properties.getProperty("flutter.sdk")
require(flutterSdkPath != null) { "flutter.sdk not set in local.properties" }
flutterSdkPath
}
includeBuild("$flutterSdkPath/packages/flutter_tools/gradle")
repositories {
google()
mavenCentral()
gradlePluginPortal()
}
}
plugins {
id("dev.flutter.flutter-plugin-loader") version "1.0.0"
id("com.android.application") version "9.0.1" apply false
id("org.jetbrains.kotlin.android") version "2.3.20" apply false
}
include(":app")
Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 MiB

+34
View File
@@ -0,0 +1,34 @@
**/dgph
*.mode1v3
*.mode2v3
*.moved-aside
*.pbxuser
*.perspectivev3
**/*sync/
.sconsign.dblite
.tags*
**/.vagrant/
**/DerivedData/
Icon?
**/Pods/
**/.symlinks/
profile
xcuserdata
**/.generated/
Flutter/App.framework
Flutter/Flutter.framework
Flutter/Flutter.podspec
Flutter/Generated.xcconfig
Flutter/ephemeral/
Flutter/app.flx
Flutter/app.zip
Flutter/flutter_assets/
Flutter/flutter_export_environment.sh
ServiceDefinitions.json
Runner/GeneratedPluginRegistrant.*
# Exceptions to above rules.
!default.mode1v3
!default.mode2v3
!default.pbxuser
!default.perspectivev3
@@ -0,0 +1,24 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleExecutable</key>
<string>App</string>
<key>CFBundleIdentifier</key>
<string>io.flutter.flutter.app</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>App</string>
<key>CFBundlePackageType</key>
<string>FMWK</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>1.0</string>
</dict>
</plist>
@@ -0,0 +1,2 @@
#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"
#include "Generated.xcconfig"
@@ -0,0 +1,2 @@
#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"
#include "Generated.xcconfig"
+42
View File
@@ -0,0 +1,42 @@
platform :ios, '13.0'
# CocoaPods analytics sends network stats synchronously affecting flutter build latency.
ENV['COCOAPODS_DISABLE_STATS'] = 'true'
project 'Runner', {
'Debug' => :debug,
'Profile' => :release,
'Release' => :release,
}
def flutter_root
generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__)
unless File.exist?(generated_xcode_build_settings_path)
raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first"
end
File.foreach(generated_xcode_build_settings_path) do |line|
matches = line.match(/FLUTTER_ROOT\=(.*)/)
return matches[1].strip if matches
end
raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get"
end
require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root)
flutter_ios_podfile_setup
target 'Runner' do
use_frameworks!
flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__))
target 'RunnerTests' do
inherit! :search_paths
end
end
post_install do |installer|
installer.pods_project.targets.each do |target|
flutter_additional_ios_build_settings(target)
end
end
+67
View File
@@ -0,0 +1,67 @@
PODS:
- connectivity_plus (0.0.1):
- Flutter
- Flutter (1.0.0)
- flutter_tts (0.0.1):
- Flutter
- geolocator_apple (1.2.0):
- Flutter
- FlutterMacOS
- MapLibre (6.19.1)
- maplibre_gl (0.25.0):
- Flutter
- MapLibre (= 6.19.1)
- package_info_plus (0.4.5):
- Flutter
- permission_handler_apple (9.4.8):
- Flutter
- shared_preferences_foundation (0.0.1):
- Flutter
- FlutterMacOS
DEPENDENCIES:
- connectivity_plus (from `.symlinks/plugins/connectivity_plus/ios`)
- Flutter (from `Flutter`)
- flutter_tts (from `.symlinks/plugins/flutter_tts/ios`)
- geolocator_apple (from `.symlinks/plugins/geolocator_apple/darwin`)
- maplibre_gl (from `.symlinks/plugins/maplibre_gl/ios`)
- package_info_plus (from `.symlinks/plugins/package_info_plus/ios`)
- permission_handler_apple (from `.symlinks/plugins/permission_handler_apple/ios`)
- shared_preferences_foundation (from `.symlinks/plugins/shared_preferences_foundation/darwin`)
SPEC REPOS:
trunk:
- MapLibre
EXTERNAL SOURCES:
connectivity_plus:
:path: ".symlinks/plugins/connectivity_plus/ios"
Flutter:
:path: Flutter
flutter_tts:
:path: ".symlinks/plugins/flutter_tts/ios"
geolocator_apple:
:path: ".symlinks/plugins/geolocator_apple/darwin"
maplibre_gl:
:path: ".symlinks/plugins/maplibre_gl/ios"
package_info_plus:
:path: ".symlinks/plugins/package_info_plus/ios"
permission_handler_apple:
:path: ".symlinks/plugins/permission_handler_apple/ios"
shared_preferences_foundation:
:path: ".symlinks/plugins/shared_preferences_foundation/darwin"
SPEC CHECKSUMS:
connectivity_plus: cb623214f4e1f6ef8fe7403d580fdad517d2f7dd
Flutter: cabc95a1d2626b1b06e7179b784ebcf0c0cde467
flutter_tts: 35ac3c7d42412733e795ea96ad2d7e05d0a75113
geolocator_apple: ab36aa0e8b7d7a2d7639b3b4e48308394e8cef5e
MapLibre: 7f24faba45439f80ccb0f83393c29fa32cb81952
maplibre_gl: a2114567cbd1065866614fbd34dfb75ab782aaa2
package_info_plus: af8e2ca6888548050f16fa2f1938db7b5a5df499
permission_handler_apple: 92d754bbaa7361d436db2d6c3c1c2a0fdcec462e
shared_preferences_foundation: 7036424c3d8ec98dfe75ff1667cb0cd531ec82bb
PODFILE CHECKSUM: f8c2dcdfb50bb67645580d28a6bf814fca30bdec
COCOAPODS: 1.16.2
@@ -0,0 +1,753 @@
// !$*UTF8*$!
{
archiveVersion = 1;
classes = {
};
objectVersion = 54;
objects = {
/* Begin PBXBuildFile section */
1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; };
2F67581D0AEBDF779F264C3F /* Pods_RunnerTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8634A31EB55F1034379B54E0 /* Pods_RunnerTests.framework */; };
331C808B294A63AB00263BE5 /* RunnerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 331C807B294A618700263BE5 /* RunnerTests.swift */; };
3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; };
74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; };
7884E8682EC3CC0700C636F2 /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7884E8672EC3CC0400C636F2 /* SceneDelegate.swift */; };
97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; };
97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; };
97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; };
DED4DFD9EC2DAE58D70CA786 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C960332103BB131A82D9B2E2 /* Pods_Runner.framework */; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
331C8085294A63A400263BE5 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 97C146E61CF9000F007C117D /* Project object */;
proxyType = 1;
remoteGlobalIDString = 97C146ED1CF9000F007C117D;
remoteInfo = Runner;
};
/* End PBXContainerItemProxy section */
/* Begin PBXCopyFilesBuildPhase section */
9705A1C41CF9048500538489 /* Embed Frameworks */ = {
isa = PBXCopyFilesBuildPhase;
buildActionMask = 2147483647;
dstPath = "";
dstSubfolderSpec = 10;
files = (
);
name = "Embed Frameworks";
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXCopyFilesBuildPhase section */
/* Begin PBXFileReference section */
06BE9F46C73ECD841329F410 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = "<group>"; };
1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = "<group>"; };
1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = "<group>"; };
331C807B294A618700263BE5 /* RunnerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RunnerTests.swift; sourceTree = "<group>"; };
331C8081294A63A400263BE5 /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = "<group>"; };
5337EC43621A0457D008A10C /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = "<group>"; };
639DEB58BA3D8FCBAC93E4BE /* Pods-RunnerTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.release.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.release.xcconfig"; sourceTree = "<group>"; };
74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = "<group>"; };
74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
7884E8672EC3CC0400C636F2 /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = "<group>"; };
7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = "<group>"; };
8634A31EB55F1034379B54E0 /* Pods_RunnerTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_RunnerTests.framework; sourceTree = BUILT_PRODUCTS_DIR; };
8D4208325A3C7F39D4F140D6 /* Pods-RunnerTests.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.profile.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.profile.xcconfig"; sourceTree = "<group>"; };
9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = "<group>"; };
9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = "<group>"; };
97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; };
97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; };
97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
C960332103BB131A82D9B2E2 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; };
DB7D36DEEE9ACCB065F3D420 /* Pods-RunnerTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.debug.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.debug.xcconfig"; sourceTree = "<group>"; };
DD4D1B74E59D3F8005FB6EC7 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
3AC28CA628E0AC26E0102A03 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
2F67581D0AEBDF779F264C3F /* Pods_RunnerTests.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
97C146EB1CF9000F007C117D /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
DED4DFD9EC2DAE58D70CA786 /* Pods_Runner.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
331C8082294A63A400263BE5 /* RunnerTests */ = {
isa = PBXGroup;
children = (
331C807B294A618700263BE5 /* RunnerTests.swift */,
);
path = RunnerTests;
sourceTree = "<group>";
};
5D024B54A595292C31EE8831 /* Frameworks */ = {
isa = PBXGroup;
children = (
C960332103BB131A82D9B2E2 /* Pods_Runner.framework */,
8634A31EB55F1034379B54E0 /* Pods_RunnerTests.framework */,
);
name = Frameworks;
sourceTree = "<group>";
};
941D76653E87671DE9664C02 /* Pods */ = {
isa = PBXGroup;
children = (
DD4D1B74E59D3F8005FB6EC7 /* Pods-Runner.debug.xcconfig */,
5337EC43621A0457D008A10C /* Pods-Runner.release.xcconfig */,
06BE9F46C73ECD841329F410 /* Pods-Runner.profile.xcconfig */,
DB7D36DEEE9ACCB065F3D420 /* Pods-RunnerTests.debug.xcconfig */,
639DEB58BA3D8FCBAC93E4BE /* Pods-RunnerTests.release.xcconfig */,
8D4208325A3C7F39D4F140D6 /* Pods-RunnerTests.profile.xcconfig */,
);
name = Pods;
path = Pods;
sourceTree = "<group>";
};
9740EEB11CF90186004384FC /* Flutter */ = {
isa = PBXGroup;
children = (
3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */,
9740EEB21CF90195004384FC /* Debug.xcconfig */,
7AFA3C8E1D35360C0083082E /* Release.xcconfig */,
9740EEB31CF90195004384FC /* Generated.xcconfig */,
);
name = Flutter;
sourceTree = "<group>";
};
97C146E51CF9000F007C117D = {
isa = PBXGroup;
children = (
9740EEB11CF90186004384FC /* Flutter */,
97C146F01CF9000F007C117D /* Runner */,
97C146EF1CF9000F007C117D /* Products */,
331C8082294A63A400263BE5 /* RunnerTests */,
941D76653E87671DE9664C02 /* Pods */,
5D024B54A595292C31EE8831 /* Frameworks */,
);
sourceTree = "<group>";
};
97C146EF1CF9000F007C117D /* Products */ = {
isa = PBXGroup;
children = (
97C146EE1CF9000F007C117D /* Runner.app */,
331C8081294A63A400263BE5 /* RunnerTests.xctest */,
);
name = Products;
sourceTree = "<group>";
};
97C146F01CF9000F007C117D /* Runner */ = {
isa = PBXGroup;
children = (
97C146FA1CF9000F007C117D /* Main.storyboard */,
97C146FD1CF9000F007C117D /* Assets.xcassets */,
97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */,
97C147021CF9000F007C117D /* Info.plist */,
1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */,
1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */,
74858FAE1ED2DC5600515810 /* AppDelegate.swift */,
7884E8672EC3CC0400C636F2 /* SceneDelegate.swift */,
74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */,
);
path = Runner;
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
331C8080294A63A400263BE5 /* RunnerTests */ = {
isa = PBXNativeTarget;
buildConfigurationList = 331C8087294A63A400263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */;
buildPhases = (
23F448731FA70C6A69203445 /* [CP] Check Pods Manifest.lock */,
331C807D294A63A400263BE5 /* Sources */,
331C807F294A63A400263BE5 /* Resources */,
3AC28CA628E0AC26E0102A03 /* Frameworks */,
);
buildRules = (
);
dependencies = (
331C8086294A63A400263BE5 /* PBXTargetDependency */,
);
name = RunnerTests;
productName = RunnerTests;
productReference = 331C8081294A63A400263BE5 /* RunnerTests.xctest */;
productType = "com.apple.product-type.bundle.unit-test";
};
97C146ED1CF9000F007C117D /* Runner */ = {
isa = PBXNativeTarget;
buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */;
buildPhases = (
C7736DBBDF22AFA1F0E3CE16 /* [CP] Check Pods Manifest.lock */,
9740EEB61CF901F6004384FC /* Run Script */,
97C146EA1CF9000F007C117D /* Sources */,
97C146EB1CF9000F007C117D /* Frameworks */,
97C146EC1CF9000F007C117D /* Resources */,
9705A1C41CF9048500538489 /* Embed Frameworks */,
3B06AD1E1E4923F5004D2608 /* Thin Binary */,
39F85057EEB25B27A1784669 /* [CP] Embed Pods Frameworks */,
E7A3E39EC6A7EA8ED85523E2 /* [CP] Copy Pods Resources */,
);
buildRules = (
);
dependencies = (
);
name = Runner;
productName = Runner;
productReference = 97C146EE1CF9000F007C117D /* Runner.app */;
productType = "com.apple.product-type.application";
};
/* End PBXNativeTarget section */
/* Begin PBXProject section */
97C146E61CF9000F007C117D /* Project object */ = {
isa = PBXProject;
attributes = {
BuildIndependentTargetsInParallel = YES;
LastUpgradeCheck = 1510;
ORGANIZATIONNAME = "";
TargetAttributes = {
331C8080294A63A400263BE5 = {
CreatedOnToolsVersion = 14.0;
TestTargetID = 97C146ED1CF9000F007C117D;
};
97C146ED1CF9000F007C117D = {
CreatedOnToolsVersion = 7.3.1;
LastSwiftMigration = 1100;
};
};
};
buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */;
compatibilityVersion = "Xcode 9.3";
developmentRegion = en;
hasScannedForEncodings = 0;
knownRegions = (
en,
Base,
);
mainGroup = 97C146E51CF9000F007C117D;
productRefGroup = 97C146EF1CF9000F007C117D /* Products */;
projectDirPath = "";
projectRoot = "";
targets = (
97C146ED1CF9000F007C117D /* Runner */,
331C8080294A63A400263BE5 /* RunnerTests */,
);
};
/* End PBXProject section */
/* Begin PBXResourcesBuildPhase section */
331C807F294A63A400263BE5 /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
97C146EC1CF9000F007C117D /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */,
3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */,
97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */,
97C146FC1CF9000F007C117D /* Main.storyboard in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXResourcesBuildPhase section */
/* Begin PBXShellScriptBuildPhase section */
23F448731FA70C6A69203445 /* [CP] Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
);
inputPaths = (
"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
"${PODS_ROOT}/Manifest.lock",
);
name = "[CP] Check Pods Manifest.lock";
outputFileListPaths = (
);
outputPaths = (
"$(DERIVED_FILE_DIR)/Pods-RunnerTests-checkManifestLockResult.txt",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
showEnvVarsInLog = 0;
};
39F85057EEB25B27A1784669 /* [CP] Embed Pods Frameworks */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist",
);
name = "[CP] Embed Pods Frameworks";
outputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n";
showEnvVarsInLog = 0;
};
3B06AD1E1E4923F5004D2608 /* Thin Binary */ = {
isa = PBXShellScriptBuildPhase;
alwaysOutOfDate = 1;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
"${TARGET_BUILD_DIR}/${INFOPLIST_PATH}",
);
name = "Thin Binary";
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin";
};
9740EEB61CF901F6004384FC /* Run Script */ = {
isa = PBXShellScriptBuildPhase;
alwaysOutOfDate = 1;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
);
name = "Run Script";
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build";
};
C7736DBBDF22AFA1F0E3CE16 /* [CP] Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
);
inputPaths = (
"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
"${PODS_ROOT}/Manifest.lock",
);
name = "[CP] Check Pods Manifest.lock";
outputFileListPaths = (
);
outputPaths = (
"$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
showEnvVarsInLog = 0;
};
E7A3E39EC6A7EA8ED85523E2 /* [CP] Copy Pods Resources */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-input-files.xcfilelist",
);
name = "[CP] Copy Pods Resources";
outputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-output-files.xcfilelist",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources.sh\"\n";
showEnvVarsInLog = 0;
};
/* End PBXShellScriptBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
331C807D294A63A400263BE5 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
331C808B294A63AB00263BE5 /* RunnerTests.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
97C146EA1CF9000F007C117D /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */,
1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */,
7884E8682EC3CC0700C636F2 /* SceneDelegate.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin PBXTargetDependency section */
331C8086294A63A400263BE5 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = 97C146ED1CF9000F007C117D /* Runner */;
targetProxy = 331C8085294A63A400263BE5 /* PBXContainerItemProxy */;
};
/* End PBXTargetDependency section */
/* Begin PBXVariantGroup section */
97C146FA1CF9000F007C117D /* Main.storyboard */ = {
isa = PBXVariantGroup;
children = (
97C146FB1CF9000F007C117D /* Base */,
);
name = Main.storyboard;
sourceTree = "<group>";
};
97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = {
isa = PBXVariantGroup;
children = (
97C147001CF9000F007C117D /* Base */,
);
name = LaunchScreen.storyboard;
sourceTree = "<group>";
};
/* End PBXVariantGroup section */
/* Begin XCBuildConfiguration section */
249021D3217E4FDB00AE95B9 /* Profile */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
CLANG_ANALYZER_NONNULL = YES;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_USER_SCRIPT_SANDBOXING = NO;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_NO_COMMON_BLOCKS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
SUPPORTED_PLATFORMS = iphoneos;
TARGETED_DEVICE_FAMILY = "1,2";
VALIDATE_PRODUCT = YES;
};
name = Profile;
};
249021D4217E4FDB00AE95B9 /* Profile */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
DEVELOPMENT_TEAM = 63CVT8G5P8;
ENABLE_BITCODE = NO;
INFOPLIST_FILE = Runner/Info.plist;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
PRODUCT_BUNDLE_IDENTIFIER = com.siromap.siroMaps;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_VERSION = 5.0;
VERSIONING_SYSTEM = "apple-generic";
};
name = Profile;
};
331C8088294A63A400263BE5 /* Debug */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = DB7D36DEEE9ACCB065F3D420 /* Pods-RunnerTests.debug.xcconfig */;
buildSettings = {
BUNDLE_LOADER = "$(TEST_HOST)";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
GENERATE_INFOPLIST_FILE = YES;
MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = com.siromap.siroMaps.RunnerTests;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_VERSION = 5.0;
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner";
};
name = Debug;
};
331C8089294A63A400263BE5 /* Release */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 639DEB58BA3D8FCBAC93E4BE /* Pods-RunnerTests.release.xcconfig */;
buildSettings = {
BUNDLE_LOADER = "$(TEST_HOST)";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
GENERATE_INFOPLIST_FILE = YES;
MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = com.siromap.siroMaps.RunnerTests;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 5.0;
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner";
};
name = Release;
};
331C808A294A63A400263BE5 /* Profile */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 8D4208325A3C7F39D4F140D6 /* Pods-RunnerTests.profile.xcconfig */;
buildSettings = {
BUNDLE_LOADER = "$(TEST_HOST)";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
GENERATE_INFOPLIST_FILE = YES;
MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = com.siromap.siroMaps.RunnerTests;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 5.0;
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner";
};
name = Profile;
};
97C147031CF9000F007C117D /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
CLANG_ANALYZER_NONNULL = YES;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = dwarf;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES;
ENABLE_USER_SCRIPT_SANDBOXING = NO;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_DYNAMIC_NO_PIC = NO;
GCC_NO_COMMON_BLOCKS = YES;
GCC_OPTIMIZATION_LEVEL = 0;
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
"$(inherited)",
);
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos;
TARGETED_DEVICE_FAMILY = "1,2";
};
name = Debug;
};
97C147041CF9000F007C117D /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
CLANG_ANALYZER_NONNULL = YES;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_USER_SCRIPT_SANDBOXING = NO;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_NO_COMMON_BLOCKS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
SUPPORTED_PLATFORMS = iphoneos;
SWIFT_COMPILATION_MODE = wholemodule;
SWIFT_OPTIMIZATION_LEVEL = "-O";
TARGETED_DEVICE_FAMILY = "1,2";
VALIDATE_PRODUCT = YES;
};
name = Release;
};
97C147061CF9000F007C117D /* Debug */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
DEVELOPMENT_TEAM = 63CVT8G5P8;
ENABLE_BITCODE = NO;
INFOPLIST_FILE = Runner/Info.plist;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
PRODUCT_BUNDLE_IDENTIFIER = com.siromap.siroMaps;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_VERSION = 5.0;
VERSIONING_SYSTEM = "apple-generic";
};
name = Debug;
};
97C147071CF9000F007C117D /* Release */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
DEVELOPMENT_TEAM = 63CVT8G5P8;
ENABLE_BITCODE = NO;
INFOPLIST_FILE = Runner/Info.plist;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
PRODUCT_BUNDLE_IDENTIFIER = com.siromap.siroMaps;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_VERSION = 5.0;
VERSIONING_SYSTEM = "apple-generic";
};
name = Release;
};
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
331C8087294A63A400263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */ = {
isa = XCConfigurationList;
buildConfigurations = (
331C8088294A63A400263BE5 /* Debug */,
331C8089294A63A400263BE5 /* Release */,
331C808A294A63A400263BE5 /* Profile */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = {
isa = XCConfigurationList;
buildConfigurations = (
97C147031CF9000F007C117D /* Debug */,
97C147041CF9000F007C117D /* Release */,
249021D3217E4FDB00AE95B9 /* Profile */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = {
isa = XCConfigurationList;
buildConfigurations = (
97C147061CF9000F007C117D /* Debug */,
97C147071CF9000F007C117D /* Release */,
249021D4217E4FDB00AE95B9 /* Profile */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
};
rootObject = 97C146E61CF9000F007C117D /* Project object */;
}
@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
version = "1.0">
<FileRef
location = "self:">
</FileRef>
</Workspace>
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>IDEDidComputeMac32BitWarning</key>
<true/>
</dict>
</plist>
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>PreviewsEnabled</key>
<false/>
</dict>
</plist>
@@ -0,0 +1,101 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1510"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "97C146ED1CF9000F007C117D"
BuildableName = "Runner.app"
BlueprintName = "Runner"
ReferencedContainer = "container:Runner.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
customLLDBInitFile = "$(SRCROOT)/Flutter/ephemeral/flutter_lldbinit"
shouldUseLaunchSchemeArgsEnv = "YES">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "97C146ED1CF9000F007C117D"
BuildableName = "Runner.app"
BlueprintName = "Runner"
ReferencedContainer = "container:Runner.xcodeproj">
</BuildableReference>
</MacroExpansion>
<Testables>
<TestableReference
skipped = "NO"
parallelizable = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "331C8080294A63A400263BE5"
BuildableName = "RunnerTests.xctest"
BlueprintName = "RunnerTests"
ReferencedContainer = "container:Runner.xcodeproj">
</BuildableReference>
</TestableReference>
</Testables>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
customLLDBInitFile = "$(SRCROOT)/Flutter/ephemeral/flutter_lldbinit"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
enableGPUValidationMode = "1"
allowLocationSimulation = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "97C146ED1CF9000F007C117D"
BuildableName = "Runner.app"
BlueprintName = "Runner"
ReferencedContainer = "container:Runner.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</LaunchAction>
<ProfileAction
buildConfiguration = "Profile"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "97C146ED1CF9000F007C117D"
BuildableName = "Runner.app"
BlueprintName = "Runner"
ReferencedContainer = "container:Runner.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>
@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
version = "1.0">
<FileRef
location = "group:Runner.xcodeproj">
</FileRef>
<FileRef
location = "group:Pods/Pods.xcodeproj">
</FileRef>
</Workspace>
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>IDEDidComputeMac32BitWarning</key>
<true/>
</dict>
</plist>
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>PreviewsEnabled</key>
<false/>
</dict>
</plist>
@@ -0,0 +1,16 @@
import Flutter
import UIKit
@main
@objc class AppDelegate: FlutterAppDelegate, FlutterImplicitEngineDelegate {
override func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
}
func didInitializeImplicitFlutterEngine(_ engineBridge: FlutterImplicitEngineBridge) {
GeneratedPluginRegistrant.register(with: engineBridge.pluginRegistry)
}
}
@@ -0,0 +1,122 @@
{
"images" : [
{
"size" : "20x20",
"idiom" : "iphone",
"filename" : "Icon-App-20x20@2x.png",
"scale" : "2x"
},
{
"size" : "20x20",
"idiom" : "iphone",
"filename" : "Icon-App-20x20@3x.png",
"scale" : "3x"
},
{
"size" : "29x29",
"idiom" : "iphone",
"filename" : "Icon-App-29x29@1x.png",
"scale" : "1x"
},
{
"size" : "29x29",
"idiom" : "iphone",
"filename" : "Icon-App-29x29@2x.png",
"scale" : "2x"
},
{
"size" : "29x29",
"idiom" : "iphone",
"filename" : "Icon-App-29x29@3x.png",
"scale" : "3x"
},
{
"size" : "40x40",
"idiom" : "iphone",
"filename" : "Icon-App-40x40@2x.png",
"scale" : "2x"
},
{
"size" : "40x40",
"idiom" : "iphone",
"filename" : "Icon-App-40x40@3x.png",
"scale" : "3x"
},
{
"size" : "60x60",
"idiom" : "iphone",
"filename" : "Icon-App-60x60@2x.png",
"scale" : "2x"
},
{
"size" : "60x60",
"idiom" : "iphone",
"filename" : "Icon-App-60x60@3x.png",
"scale" : "3x"
},
{
"size" : "20x20",
"idiom" : "ipad",
"filename" : "Icon-App-20x20@1x.png",
"scale" : "1x"
},
{
"size" : "20x20",
"idiom" : "ipad",
"filename" : "Icon-App-20x20@2x.png",
"scale" : "2x"
},
{
"size" : "29x29",
"idiom" : "ipad",
"filename" : "Icon-App-29x29@1x.png",
"scale" : "1x"
},
{
"size" : "29x29",
"idiom" : "ipad",
"filename" : "Icon-App-29x29@2x.png",
"scale" : "2x"
},
{
"size" : "40x40",
"idiom" : "ipad",
"filename" : "Icon-App-40x40@1x.png",
"scale" : "1x"
},
{
"size" : "40x40",
"idiom" : "ipad",
"filename" : "Icon-App-40x40@2x.png",
"scale" : "2x"
},
{
"size" : "76x76",
"idiom" : "ipad",
"filename" : "Icon-App-76x76@1x.png",
"scale" : "1x"
},
{
"size" : "76x76",
"idiom" : "ipad",
"filename" : "Icon-App-76x76@2x.png",
"scale" : "2x"
},
{
"size" : "83.5x83.5",
"idiom" : "ipad",
"filename" : "Icon-App-83.5x83.5@2x.png",
"scale" : "2x"
},
{
"size" : "1024x1024",
"idiom" : "ios-marketing",
"filename" : "Icon-App-1024x1024@1x.png",
"scale" : "1x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

@@ -0,0 +1,23 @@
{
"images" : [
{
"idiom" : "universal",
"filename" : "LaunchImage.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "LaunchImage@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "LaunchImage@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 68 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 B

@@ -0,0 +1,5 @@
# Launch Screen Assets
You can customize the launch screen with your own desired assets by replacing the image files in this directory.
You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images.
@@ -0,0 +1,37 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="12121" systemVersion="16G29" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" colorMatched="YES" initialViewController="01J-lp-oVM">
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="12089"/>
</dependencies>
<scenes>
<!--View Controller-->
<scene sceneID="EHf-IW-A2E">
<objects>
<viewController id="01J-lp-oVM" sceneMemberID="viewController">
<layoutGuides>
<viewControllerLayoutGuide type="top" id="Ydg-fD-yQy"/>
<viewControllerLayoutGuide type="bottom" id="xbc-2k-c8Z"/>
</layoutGuides>
<view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3">
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<imageView opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" image="LaunchImage" translatesAutoresizingMaskIntoConstraints="NO" id="YRO-k0-Ey4">
</imageView>
</subviews>
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstItem="YRO-k0-Ey4" firstAttribute="centerX" secondItem="Ze5-6b-2t3" secondAttribute="centerX" id="1a2-6s-vTC"/>
<constraint firstItem="YRO-k0-Ey4" firstAttribute="centerY" secondItem="Ze5-6b-2t3" secondAttribute="centerY" id="4X2-HB-R7a"/>
</constraints>
</view>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="53" y="375"/>
</scene>
</scenes>
<resources>
<image name="LaunchImage" width="168" height="185"/>
</resources>
</document>
@@ -0,0 +1,26 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="10117" systemVersion="15F34" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" initialViewController="BYZ-38-t0r">
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="10085"/>
</dependencies>
<scenes>
<!--Flutter View Controller-->
<scene sceneID="tne-QT-ifu">
<objects>
<viewController id="BYZ-38-t0r" customClass="FlutterViewController" sceneMemberID="viewController">
<layoutGuides>
<viewControllerLayoutGuide type="top" id="y3c-jy-aDJ"/>
<viewControllerLayoutGuide type="bottom" id="wfy-db-euE"/>
</layoutGuides>
<view key="view" contentMode="scaleToFill" id="8bC-Xf-vdC">
<rect key="frame" x="0.0" y="0.0" width="600" height="600"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/>
</view>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="dkx-z0-nzr" sceneMemberID="firstResponder"/>
</objects>
</scene>
</scenes>
</document>
+97
View File
@@ -0,0 +1,97 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CADisableMinimumFrameDurationOnPhone</key>
<true/>
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleDisplayName</key>
<string>خرائط سيرو</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>siro_maps</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>$(FLUTTER_BUILD_NAME)</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>$(FLUTTER_BUILD_NUMBER)</string>
<key>LSRequiresIPhoneOS</key>
<true/>
<key>NSLocationWhenInUseUsageDescription</key>
<string>يستخدم تطبيق خرائط سيرو موقعك لعرض خريطة تفاعلية وتوفير الملاحة الحية الدقيقة.</string>
<key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
<string>يستخدم تطبيق خرائط سيرو موقعك في الخلفية لتقديم التوجيهات الصوتية الحية وتنبيهات الطريق أثناء القيادة.</string>
<key>UIBackgroundModes</key>
<array>
<string>location</string>
<string>audio</string>
</array>
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<true/>
</dict>
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleTypeRole</key>
<string>Editor</string>
<key>CFBundleURLName</key>
<string>com.siro_map.siro_maps</string>
<key>CFBundleURLSchemes</key>
<array>
<string>siromaps</string>
</array>
</dict>
</array>
<key>UIApplicationSceneManifest</key>
<dict>
<key>UIApplicationSupportsMultipleScenes</key>
<false/>
<key>UISceneConfigurations</key>
<dict>
<key>UIWindowSceneSessionRoleApplication</key>
<array>
<dict>
<key>UISceneClassName</key>
<string>UIWindowScene</string>
<key>UISceneConfigurationName</key>
<string>flutter</string>
<key>UISceneDelegateClassName</key>
<string>$(PRODUCT_MODULE_NAME).SceneDelegate</string>
<key>UISceneStoryboardFile</key>
<string>Main</string>
</dict>
</array>
</dict>
</dict>
<key>UIApplicationSupportsIndirectInputEvents</key>
<true/>
<key>UILaunchStoryboardName</key>
<string>LaunchScreen</string>
<key>UIMainStoryboardFile</key>
<string>Main</string>
<key>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>UISupportedInterfaceOrientations~ipad</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationPortraitUpsideDown</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
</dict>
</plist>
@@ -0,0 +1 @@
#import "GeneratedPluginRegistrant.h"
@@ -0,0 +1,6 @@
import Flutter
import UIKit
class SceneDelegate: FlutterSceneDelegate {
}
@@ -0,0 +1,12 @@
import Flutter
import UIKit
import XCTest
class RunnerTests: XCTestCase {
func testExample() {
// If you add code to the Runner application, consider adding tests here.
// See https://developer.apple.com/documentation/xctest for more information about using XCTest.
}
}
@@ -0,0 +1,30 @@
import '../env/env.dart';
class ApiConstants {
ApiConstants._();
// Official Keys extracted from Siro Ecosystem (Encrypted & Obfuscated via Envied)
static final String mapSaasKey = Env.mapSaasApiKey;
static final String googleMapApiKey = Env.googleMapApiKey;
// MapSaaS Endpoints
static const String mapSaasRoute = 'https://map-saas.intaleqapp.com/api/maps/route';
static const String mapSaasSearch = 'https://map-saas.intaleqapp.com/api/geocoding/search';
static const String mapSaasPlaces = 'https://map-saas.intaleqapp.com/api/geocoding/places';
static const String mapSaasTelemetry = 'https://map-saas.intaleqapp.com/api/telemetry';
static const String mapSaasStyleBase = 'https://map-saas.intaleqapp.com/api/maps/style.json';
static const String googlePlacesNearby = 'https://maps.googleapis.com/maps/api/place/nearbysearch/json';
// 50 km Radius Threshold (Strictly as specified)
static const double maxSearchRadiusMeters = 50000.0;
static const double maxSearchRadiusKm = 50.0;
// Secondary / Alternative Routing Engines
static const String osrmRouteJordan = 'https://routesjo.intaleq.xyz/route/v1/driving';
static const String osrmRouteSyria = 'https://routes-syria.siromove.com/route/v1/driving';
static const String osrmRouteEgypt = 'https://routes-egypt.siromove.com/route/v1/driving';
// Default Jordan Geospatial Center (Amman 7th Circle / Abdoun)
static const double defaultLat = 31.9539;
static const double defaultLng = 35.9106;
}
@@ -0,0 +1,35 @@
import 'package:flutter/material.dart';
class AppColors {
AppColors._();
// Pure White & Canvas
static const Color pureWhite = Color(0xFFFFFFFF);
static const Color canvasLight = Color(0xFFFBFBFD);
static const Color surfaceCard = Color(0xFFFFFFFF);
static const Color surfaceMuted = Color(0xFFF5F5F7);
// Apple Text Palette
static const Color textPrimary = Color(0xFF1D1D1F);
static const Color textSecondary = Color(0xFF424245);
static const Color textMuted = Color(0xFF86868B);
// Sovereign / Apple Accent Colors
static const Color appleBlue = Color(0xFF0071E3);
static const Color appleBlueHover = Color(0xFF0077ED);
static const Color tacticalNavy = Color(0xFF0B192C);
static const Color tacticalEmerald = Color(0xFF059669);
static const Color sovereignGold = Color(0xFFD97706);
static const Color coralDanger = Color(0xFFDC2626);
// Borders & Dividers
static const Color borderSubtle = Color(0x12000000);
static const Color borderGlass = Color(0x1F000000);
static const Color glassFill = Color(0xD1FFFFFF);
// Dark Theme Palette
static const Color darkCanvas = Color(0xFF0B0F17);
static const Color darkSurface = Color(0xFF161A22);
static const Color darkCard = Color(0xFF1F2430);
static const Color darkText = Color(0xFFF5F5F7);
}
@@ -0,0 +1,59 @@
import 'package:flutter/services.dart';
class CarPlatformBridge {
CarPlatformBridge._();
static const _channel = MethodChannel('com.siro.siro_maps/car_navigation');
static bool _isInitialized = false;
static void ensureInitialized() {
if (_isInitialized) return;
_channel.setMethodCallHandler(_handleMethodCall);
_isInitialized = true;
}
static Future<dynamic> _handleMethodCall(MethodCall call) async {
switch (call.method) {
case 'isCarAppConnected':
return false;
default:
throw MissingPluginException();
}
}
static Future<void> updateNavState({
required double lat,
required double lng,
required double bearing,
required double speed,
required String instruction,
required double distanceToStep,
required double totalDistance,
required double eta,
required int maneuver,
required bool isNavigating,
bool isMapDarkMode = false,
}) async {
try {
await _channel.invokeMethod('updateNavState', {
'lat': lat,
'lng': lng,
'bearing': bearing,
'speed': speed,
'instruction': instruction,
'distanceToStep': distanceToStep,
'totalDistance': totalDistance,
'eta': eta,
'maneuver': maneuver,
'isNavigating': isNavigating,
'isMapDarkMode': isMapDarkMode,
});
} catch (_) {}
}
static Future<void> stopNavigation() async {
try {
await _channel.invokeMethod('stopNavigation');
} catch (_) {}
}
}

Some files were not shown because too many files have changed in this diff Show More