ميزات الجهاز
الحساسات والملفات والمهام في الخلفية
اقرن مستشعرات اللياقة BLE، واستقبل ملفات FIT/GPX/TCX، ونفّذ مهامًا والتطبيق مغلق
1استقبل الملفات والصور والفيديو والروابط من التطبيقات الأخرى#
فعّل 'استقبال المشاركات من التطبيقات الأخرى' في المعالج وحدد ما يقبله تطبيقك: النصوص والروابط وملفات البيانات (FIT وGPX وTCX وCSV وJSON وXML…) والصور والفيديو والصوت والمستندات. يظهر تطبيقك في مكانين، لهذه الأنواع بالضبط:
- نافذة 'فتح باستخدام' في Android عندما يضغط المستخدم على ملف.
- نافذة المشاركة / التصدير التي تعرضها التطبيقات الأخرى، بما فيها أجهزة الرياضة التي تصدّر ملفات التمارين (.fit وgpx. وtcx.). تصدّر معظم تطبيقات اللياقة عبر المشاركة، ولهذا لا تظهر هناك التطبيقات التي تسجّل 'فتح باستخدام' فقط.
اطلب الملف بـ AppMint.getOpenedFile()؛ فهي تنتظرك مهما تأخر تشغيل صفحتك. f.kind هو image أو video أو audio أو document أو data. تصل صيغ النص (gpx وtcx وcsv وjson وxml…) كـ f.text جاهز للاستخدام؛ والملفات الثنائية الصغيرة (.fit) كـ f.base64؛ وكل ملف بيانات ومستند كـ File حقيقي في f.file. تصل الصور والفيديو والصوت كـ f.url، وهو عنوان قابل للبث لـ <video src> / <img src>، مع f.getFile() عند الحاجة إلى البايتات. لا يوجد حد للحجم. يُقرأ الرابط أو التعليق المشارَك بـ WebToApk.getSharedText().
// Ask on startup — the file waits, however late your page mounts
const f = await AppMint.getOpenedFile(); // null if opened normally
if (f) {
if (f.name.endsWith('.gpx') || f.name.endsWith('.tcx')) {
const xml = new DOMParser().parseFromString(f.text, 'text/xml');
const points = xml.querySelectorAll('trkpt, Trackpoint');
console.log('workout with ' + points.length + ' points');
} else if (f.name.endsWith('.fit')) {
// .fit is binary — a real File; parse its bytes
parseFit(new Uint8Array(await f.file.arrayBuffer())); // e.g. fit-file-parser
} else if (f.kind === 'video') {
document.querySelector('video').src = f.url; // streams from disk, seekable
}
}
// A shared link or caption
const shared = JSON.parse(WebToApk.getSharedText() || 'null');
if (shared) console.log('shared:', shared.url || shared.text);2اقرن مستشعرات اللياقة عبر Bluetooth (معدل النبض والسرعة والإيقاع…)#
فعّل إذن Bluetooth في المعالج فيستطيع تطبيقك اقتران مستشعرات BLE القياسية، فكل حزام صدر ومستشعر دراجة متوافق يستخدم ملفات Bluetooth الرسمية نفسها، فلا حاجة إلى كود لكل علامة تجارية:
- أحزمة معدل النبض ← نبضة/د
- مستشعرات السرعة/الإيقاع ← كم/س + دورات الدواسة في الدقيقة
- مقاييس القدرة ← واط
- مستشعرات الجري ← الوتيرة + الخطوات/د
- مستوى بطارية كل مستشعر
المسار: مسح (15 ث) ← يختار المستخدم جهازًا ← اتصال. ثم تتدفق البيانات كأحداث. تحتاج السرعة إلى حجم عجلتك؛ اضبطه مرة واحدة (الافتراضي 2096 مم = عجلة طريق 700x23c).
// 1. Scan — each found sensor fires a 'device' event:
window.addEventListener('appmint:ble', function (e) {
const m = e.detail;
if (m.kind === 'device') {
// {address, name, sensors:['heart_rate','speed_cadence',...], rssi}
addToPickerUI(m);
}
if (m.kind === 'data') {
if (m.type === 'heart_rate') showBpm(m.data.bpm);
if (m.type === 'speed_cadence') {
if (m.data.speedKmh) showSpeed(m.data.speedKmh.toFixed(1));
if (m.data.cadenceRpm) showCadence(Math.round(m.data.cadenceRpm));
}
if (m.type === 'power') showWatts(m.data.watts);
if (m.type === 'battery') showBattery(m.data.percent);
}
if (m.kind === 'error') console.warn('BLE:', m.error);
});
WebToApk.bleSetWheelCircumference(2105); // your wheel, in mm
WebToApk.bleStartScan();
// 2. When the user taps a device from your picker:
WebToApk.bleConnect(device.address);
// 3. Done training:
WebToApk.bleDisconnect();3مهام الخلفية والحفظ التلقائي الدائم (WorkManager)#
لا يمكن لـ JavaScript العمل أثناء إغلاق تطبيقك، وهذا من Android وليس من AppMint. لكن ما يمكن تشغيله هو مهمة تسليم أصلية تسلّمها صفحتك لنظام التشغيل. هناك أداتان:
- workEnqueueOnline: تسليم دائم لمرة واحدة. مثالي للحفظ التلقائي: سلّم البيانات فتُرسَل بـ POST إلى خادمك عند توفر الشبكة، مع إعادة المحاولة بتباعد متزايد، وتبقى بعد إنهاء التطبيق وإعادة التشغيل. إعادة استخدام معرّف المهمة نفسه تستبدل الحفظ المعلق بأحدث حالة.
- workSchedulePeriodic: استدعاء HTTP أصلي متكرر (مشغّل مزامنة، نبض حياة). الحد الأدنى للفاصل في Android هو 15 دقيقة؛ تُرفع القيم الأقصر إليه.
كلتا الأداتين تسلّمان حمولتك أنت إلى رابطك أنت؛ جانب الخادم نقطة نهاية عادية لديك بالفعل (دالة Supabase edge أو API الخاصة بك…).
// Durable auto-save: call this on every important change.
function autoSave(state) {
WebToApk.workEnqueueOnline(
'autosave', // same id → newest state wins
'https://api.example.com/save',
'POST',
JSON.stringify({ 'Authorization': 'Bearer ' + token }),
JSON.stringify(state)
);
}
// Recurring sync every 30 minutes, even if the app is closed:
WebToApk.workSchedulePeriodic(
'sync', 'https://api.example.com/sync', 'POST', '{}', '', 30
);
// Manage jobs:
WebToApk.workCancel('sync');
WebToApk.workList('req1');
window.addEventListener('appmint:work', function (e) {
// {id, status:'enqueued'|'scheduled'|'cancelled'|'failed', reason?, intervalMinutes?}
// or, for workList: {requestId, jobs:[{id, periodic, url, createdAt, pendingDelivery}]}
console.log('work:', e.detail);
});4قائمة فحص وأخطاء شائعة#
- الملفات: يجب أن يكون المفتاح مفعّلًا وقت الإنشاء، فهو يسجّل تطبيقك في manifest الخاص بـ Android، ولا يمكن تغييره بعد التثبيت. أعد الإنشاء بعد التفعيل.
- BLE: يجب أن يكون مفتاح إذن Bluetooth مفعّلًا وقت الإنشاء. يطلب أول مسح من المستخدم إذن وقت التشغيل (Android 12 وما بعده: 'الأجهزة القريبة'؛ الأقدم: الموقع، وهذه قاعدة في Android لمسح BLE، وتطبيقك لا يقرأ الموقع).
- BLE: اقرن داخل تطبيقك أنت، وليس في إعدادات Bluetooth في Android، فمستشعرات اللياقة BLE ليست أجهزة مقترنة كلاسيكية.
- مهام الخلفية: لا تجدول مهمة دورية لـ 'تشغيل كود صفحتي'، فهي تسلّم HTTP إلى خادمك. إذا احتجت إلى معالجة على الجهاز والتطبيق مغلق، فلهذا وُجدت نقطة نهاية التسليم.
- الحفظ التلقائي: استخدم دائمًا معرّف المهمة نفسه. تؤدي المعرّفات المختلفة إلى تراكم عدة عمليات حفظ قديمة.