تطوير تطبيقات الساعات الذكية

|

فيديوهات تعليمية

مشاهدة الفيديو على YouTube

ما هو مطور تطبيقات الساعات الذكية؟

مطور تطبيقات الأجهزة القابلة للارتداء

مطور تطبيقات الأجهزة القابلة للارتداء يركز على إنشاء تطبيقات الساعات الذكية باستخدام Android Wear و WatchOS مع الاستفادة من أدوات مثل Google Fit APIs و HealthKit لتحليل بيانات الصحة واللياقة.

Android Wear

تطوير تطبيقات ساعات Android الذكية

WatchOS

تطوير تطبيقات Apple Watch

HealthKit

تحليل بيانات الصحة من Apple

Fitness APIs

تحليل بيانات اللياقة من Google

اللغات والأدوات المستخدمة

Kotlin/Java

لتطوير تطبيقات Android Wear

Swift

لتطوير تطبيقات WatchOS

HealthKit

إطار عمل Apple لبيانات الصحة

Fitness APIs

أدوات Google لبيانات اللياقة

Android Wear

نظام تشغيل ساعات Android

WatchOS

نظام تشغيل Apple Watch

مهارات مطور الساعات الذكية

1

Kotlin/Java

للتطوير على منصة Android Wear

2

Swift

للتطوير على منصة WatchOS

3

Android Studio

بيئة التطوير لـ Android Wear

4

Xcode

بيئة التطوير لـ WatchOS

5

HealthKit

تحليل بيانات الصحة من Apple

6

Fitness APIs

تحليل بيانات اللياقة من Google

خارطة التعلم خطوة بخطوة

1

الخطوة 1: تعلم Android Wear

Android Wear هو نظام التشغيل الخاص بأجهزة الساعات الذكية التي تعمل بنظام Android

الأهمية:

الأساس لفهم كيفية بناء التطبيقات التي تعمل على ساعات Android الذكية

الأدوات:

Android Studio مع Kotlin/Java

مثال Android Wear أساسي:

// MainActivity.kt - النشاط الرئيسي للتطبيق
package com.example.wearapp

import android.os.Bundle
import android.widget.TextView
import androidx.appcompat.app.AppCompatActivity
import androidx.wear.ambient.AmbientModeSupport
import androidx.wear.widget.WearableLinearLayoutManager
import androidx.wear.widget.WearableRecyclerView

class MainActivity : AppCompatActivity(), AmbientModeSupport.AmbientCallbackProvider {
    
    // قائمة البيانات
    private val healthData = listOf(
        HealthItem("الخطوات", "8,542", "اليوم"),
        HealthItem("السعرات الحرارية", "420", "سعرة"),
        HealthItem("معدل ضربات القلب", "72", "نبضة/دقيقة"),
        HealthItem("المسافة", "6.2", "كم"),
        HealthItem("النوم", "7.5", "ساعة")
    )
    
    // حالة التطبيق
    private var isAmbient = false
    private lateinit var ambientController: AmbientModeSupport.AmbientController
    
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        
        // تهيئة وضع Ambient
        ambientController = AmbientModeSupport.attach(this)
        
        // إعداد RecyclerView للساعات الذكية
        setupRecyclerView()
        
        // تحديث البيانات في الوقت الحقيقي
        updateRealTimeData()
    }
    
    private fun setupRecyclerView() {
        val recyclerView: WearableRecyclerView = findViewById(R.id.recycler_view)
        
        // استخدام WearableLinearLayoutManager للواجهات الدائرية
        recyclerView.layoutManager = WearableLinearLayoutManager(this)
        
        // إضافة CurvingLayoutCallback للانحناء على الساعات المستديرة
        recyclerView.setEdgeItemsCenteringEnabled(true)
        
        // تعيين المحاذاة
        recyclerView.isCircularScrollingGestureEnabled = true
        recyclerView.bezelWidth = 15
        recyclerView.scrollDegreesPerScreen = 90
        
        // تعيين المحول
        val adapter = HealthAdapter(healthData)
        recyclerView.adapter = adapter
        
        // إضافة نقرة على العناصر
        adapter.setOnItemClickListener { position ->
            val item = healthData[position]
            showDetailsDialog(item)
        }
    }
    
    private fun updateRealTimeData() {
        // محاكاة تحديث البيانات في الوقت الحقيقي
        val heartRateTextView: TextView = findViewById(R.id.heart_rate_text)
        val stepsTextView: TextView = findViewById(R.id.steps_text)
        
        // تحديث معدل ضربات القلب كل 5 ثواني
        Thread {
            while (true) {
                Thread.sleep(5000)
                runOnUiThread {
                    // قيم عشوائية لمحاكاة البيانات الحقيقية
                    val randomHeartRate = (60..100).random()
                    val randomSteps = (0..100).random()
                    
                    heartRateTextView.text = "$randomHeartRate"
                    stepsTextView.text = "${healthData[0].value.toInt() + randomSteps}"
                }
            }
        }.start()
    }
    
    private fun showDetailsDialog(item: HealthItem) {
        // عرض تفاصيل العنصر
        val dialog = HealthDetailDialog(this, item)
        dialog.show()
    }
    
    override fun getAmbientCallback(): AmbientModeSupport.AmbientCallback {
        return object : AmbientModeSupport.AmbientCallback() {
            override fun onEnterAmbient(ambientDetails: Bundle?) {
                // الدخول إلى وضع Ambient (توفير الطاقة)
                isAmbient = true
                updateAmbientUI()
            }
            
            override fun onExitAmbient() {
                // الخروج من وضع Ambient
                isAmbient = false
                updateAmbientUI()
            }
            
            override fun onUpdateAmbient() {
                // تحديث الواجهة في وضع Ambient
                updateAmbientData()
            }
        }
    }
    
    private fun updateAmbientUI() {
        // تحديث الواجهة بناءً على وضع Ambient
        val recyclerView: WearableRecyclerView = findViewById(R.id.recycler_view)
        recyclerView.alpha = if (isAmbient) 0.7f else 1.0f
    }
    
    private fun updateAmbientData() {
        // تحديث البيانات في وضع Ambient
        // هنا يمكنك تحديث البيانات فقط عند الحاجة لتوفير الطاقة
    }
    
    // فئة لعناصر البيانات الصحية
    data class HealthItem(
        val title: String,
        val value: String,
        val unit: String
    )
}

// HealthAdapter.kt - محول البيانات
class HealthAdapter(private val items: List) : 
    RecyclerView.Adapter() {
    
    private var itemClickListener: ((Int) -> Unit)? = null
    
    fun setOnItemClickListener(listener: (Int) -> Unit) {
        itemClickListener = listener
    }
    
    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
        val view = LayoutInflater.from(parent.context)
            .inflate(R.layout.item_health, parent, false)
        return ViewHolder(view)
    }
    
    override fun onBindViewHolder(holder: ViewHolder, position: Int) {
        val item = items[position]
        holder.bind(item)
        
        holder.itemView.setOnClickListener {
            itemClickListener?.invoke(position)
        }
    }
    
    override fun getItemCount(): Int = items.size
    
    class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
        private val titleTextView: TextView = itemView.findViewById(R.id.title_text)
        private val valueTextView: TextView = itemView.findViewById(R.id.value_text)
        private val unitTextView: TextView = itemView.findViewById(R.id.unit_text)
        private val iconImageView: ImageView = itemView.findViewById(R.id.icon_image)
        
        fun bind(item: MainActivity.HealthItem) {
            titleTextView.text = item.title
            valueTextView.text = item.value
            unitTextView.text = item.unit
            
            // تعيين الأيقونة المناسبة
            val iconRes = when (item.title) {
                "الخطوات" -> R.drawable.ic_steps
                "السعرات الحرارية" -> R.drawable.ic_calories
                "معدل ضربات القلب" -> R.drawable.ic_heart
                "المسافة" -> R.drawable.ic_distance
                "النوم" -> R.drawable.ic_sleep
                else -> R.drawable.ic_default
            }
            iconImageView.setImageResource(iconRes)
        }
    }
}

// HealthDetailDialog.kt - حوار التفاصيل
class HealthDetailDialog(
    context: Context,
    private val item: MainActivity.HealthItem
) : Dialog(context) {
    
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.dialog_health_detail)
        
        window?.setLayout(
            ViewGroup.LayoutParams.MATCH_PARENT,
            ViewGroup.LayoutParams.WRAP_CONTENT
        )
        
        setupViews()
        loadHistoricalData()
    }
    
    private fun setupViews() {
        val titleTextView: TextView = findViewById(R.id.detail_title)
        val valueTextView: TextView = findViewById(R.id.detail_value)
        val unitTextView: TextView = findViewById(R.id.detail_unit)
        val closeButton: Button = findViewById(R.id.close_button)
        
        titleTextView.text = item.title
        valueTextView.text = item.value
        unitTextView.text = item.unit
        
        closeButton.setOnClickListener {
            dismiss()
        }
        
        // إضافة رسم بياني للبيانات التاريخية
        val chartView: LineChart = findViewById(R.id.chart_view)
        setupChart(chartView)
    }
    
    private fun setupChart(chart: LineChart) {
        // تهيئة الرسم البياني
        chart.description.isEnabled = false
        chart.setTouchEnabled(true)
        chart.isDragEnabled = true
        chart.setScaleEnabled(true)
        chart.setDrawGridBackground(false)
        
        // البيانات التاريخية (7 أيام)
        val entries = ArrayList()
        val days = listOf("أحد", "اثنين", "ثلاثاء", "أربعاء", "خميس", "جمعة", "سبت")
        
        days.forEachIndexed { index, day ->
            val value = when (item.title) {
                "الخطوات" -> (5000..15000).random().toFloat()
                "السعرات الحرارية" -> (300..800).random().toFloat()
                "معدل ضربات القلب" -> (60..100).random().toFloat()
                "المسافة" -> (3..15).random().toFloat()
                "النوم" -> (5..9).random().toFloat()
                else -> 0f
            }
            entries.add(Entry(index.toFloat(), value))
        }
        
        val dataSet = LineDataSet(entries, "التاريخ")
        dataSet.color = Color.BLUE
        dataSet.valueTextColor = Color.BLACK
        dataSet.lineWidth = 2f
        
        val lineData = LineData(dataSet)
        chart.data = lineData
        chart.invalidate()
    }
    
    private fun loadHistoricalData() {
        // محاكاة تحميل البيانات التاريخية
        val progressBar: ProgressBar = findViewById(R.id.progress_bar)
        val dataTextView: TextView = findViewById(R.id.historical_data_text)
        
        progressBar.visibility = View.VISIBLE
        
        Thread {
            Thread.sleep(1000) // محاكاة تأخير الشبكة
            runOnUiThread {
                progressBar.visibility = View.GONE
                dataTextView.text = "تم تحميل البيانات التاريخية لآخر 30 يوم"
            }
        }.start()
    }
}

// ملف AndroidManifest.xml للإذونات
/*





*/
                        
2

الخطوة 2: تعلم WatchOS

WatchOS هو نظام التشغيل الخاص بأجهزة Apple Watch

الأهمية:

الأساس لفهم كيفية بناء التطبيقات التي تعمل على ساعات Apple الذكية

الأدوات:

Xcode مع Swift

مثال WatchOS أساسي:

// ContentView.swift - واجهة التطبيق الرئيسية
import SwiftUI
import HealthKit
import WatchKit

struct ContentView: View {
    // حالة التطبيق
    @State private var stepCount = 0
    @State private var heartRate = 72
    @State private var caloriesBurned = 0
    @State private var distance = 0.0
    @State private var isWorkoutActive = false
    @State private var workoutTime = 0
    
    // HealthKit
    private let healthStore = HKHealthStore()
    @State private var healthDataAvailable = false
    
    // بيانات الصحة
    private let healthMetrics = [
        HealthMetric(title: "الخطوات", value: "0", unit: "خطوة", icon: "figure.walk"),
        HealthMetric(title: "معدل ضربات القلب", value: "72", unit: "نبضة/دقيقة", icon: "heart.fill"),
        HealthMetric(title: "السعرات الحرارية", value: "0", unit: "سعرة", icon: "flame.fill"),
        HealthMetric(title: "المسافة", value: "0", unit: "كم", icon: "map.fill"),
        HealthMetric(title: "النوم", value: "0", unit: "ساعة", icon: "bed.double.fill")
    ]
    
    var body: some View {
        ScrollView {
            VStack(spacing: 12) {
                // رأس التطبيق
                HeaderView()
                
                // بيانات الصحة الرئيسية
                HealthSummaryView(
                    stepCount: $stepCount,
                    heartRate: $heartRate,
                    caloriesBurned: $caloriesBurned,
                    distance: $distance
                )
                
                // قائمة المقاييس الصحية
                HealthMetricsListView(metrics: healthMetrics)
                
                // بدء/إيقاف التمرين
                WorkoutControlView(
                    isActive: $isWorkoutActive,
                    workoutTime: $workoutTime
                )
                
                // التنبيهات
                NotificationsView()
                
                // الإعدادات
                SettingsView(healthDataAvailable: $healthDataAvailable)
            }
            .padding()
        }
        .onAppear {
            requestHealthKitAuthorization()
            startHealthDataUpdates()
        }
    }
    
    private func requestHealthKitAuthorization() {
        // أنواع البيانات الصحية المطلوبة
        let typesToRead: Set = [
            HKObjectType.quantityType(forIdentifier: .stepCount)!,
            HKObjectType.quantityType(forIdentifier: .heartRate)!,
            HKObjectType.quantityType(forIdentifier: .activeEnergyBurned)!,
            HKObjectType.quantityType(forIdentifier: .distanceWalkingRunning)!,
            HKObjectType.categoryType(forIdentifier: .sleepAnalysis)!
        ]
        
        healthStore.requestAuthorization(toShare: nil, read: typesToRead) { success, error in
            DispatchQueue.main.async {
                healthDataAvailable = success
                if success {
                    print("تم منح إذن HealthKit")
                    fetchHealthData()
                } else if let error = error {
                    print("خطأ في إذن HealthKit: \(error.localizedDescription)")
                }
            }
        }
    }
    
    private func fetchHealthData() {
        fetchStepCount()
        fetchHeartRate()
        fetchActiveCalories()
        fetchDistance()
    }
    
    private func fetchStepCount() {
        let stepType = HKQuantityType.quantityType(forIdentifier: .stepCount)!
        let now = Date()
        let startOfDay = Calendar.current.startOfDay(for: now)
        let predicate = HKQuery.predicateForSamples(
            withStart: startOfDay,
            end: now,
            options: .strictStartDate
        )
        
        let query = HKStatisticsQuery(
            quantityType: stepType,
            quantitySamplePredicate: predicate,
            options: .cumulativeSum
        ) { _, result, error in
            guard let result = result, let sum = result.sumQuantity() else {
                print("خطأ في جلب الخطوات: \(error?.localizedDescription ?? "غير معروف")")
                return
            }
            
            let steps = sum.doubleValue(for: HKUnit.count())
            DispatchQueue.main.async {
                stepCount = Int(steps)
            }
        }
        
        healthStore.execute(query)
    }
    
    private func fetchHeartRate() {
        let heartRateType = HKQuantityType.quantityType(forIdentifier: .heartRate)!
        let sortDescriptor = NSSortDescriptor(key: HKSampleSortIdentifierEndDate, ascending: false)
        
        let query = HKSampleQuery(
            sampleType: heartRateType,
            predicate: nil,
            limit: 1,
            sortDescriptors: [sortDescriptor]
        ) { _, samples, error in
            guard let samples = samples as? [HKQuantitySample],
                  let sample = samples.first else {
                print("خطأ في جلب معدل ضربات القلب: \(error?.localizedDescription ?? "غير معروف")")
                return
            }
            
            let heartRateUnit = HKUnit(from: "count/min")
            let heartRate = sample.quantity.doubleValue(for: heartRateUnit)
            
            DispatchQueue.main.async {
                self.heartRate = Int(heartRate)
            }
        }
        
        healthStore.execute(query)
    }
    
    private func fetchActiveCalories() {
        let calorieType = HKQuantityType.quantityType(forIdentifier: .activeEnergyBurned)!
        let now = Date()
        let startOfDay = Calendar.current.startOfDay(for: now)
        let predicate = HKQuery.predicateForSamples(
            withStart: startOfDay,
            end: now,
            options: .strictStartDate
        )
        
        let query = HKStatisticsQuery(
            quantityType: calorieType,
            quantitySamplePredicate: predicate,
            options: .cumulativeSum
        ) { _, result, error in
            guard let result = result, let sum = result.sumQuantity() else {
                print("خطأ في جلب السعرات الحرارية: \(error?.localizedDescription ?? "غير معروف")")
                return
            }
            
            let calories = sum.doubleValue(for: HKUnit.kilocalorie())
            DispatchQueue.main.async {
                caloriesBurned = Int(calories)
            }
        }
        
        healthStore.execute(query)
    }
    
    private func fetchDistance() {
        let distanceType = HKQuantityType.quantityType(forIdentifier: .distanceWalkingRunning)!
        let now = Date()
        let startOfDay = Calendar.current.startOfDay(for: now)
        let predicate = HKQuery.predicateForSamples(
            withStart: startOfDay,
            end: now,
            options: .strictStartDate
        )
        
        let query = HKStatisticsQuery(
            quantityType: distanceType,
            quantitySamplePredicate: predicate,
            options: .cumulativeSum
        ) { _, result, error in
            guard let result = result, let sum = result.sumQuantity() else {
                print("خطأ في جلب المسافة: \(error?.localizedDescription ?? "غير معروف")")
                return
            }
            
            let distanceInMeters = sum.doubleValue(for: HKUnit.meter())
            let distanceInKm = distanceInMeters / 1000
            
            DispatchQueue.main.async {
                distance = distanceInKm
            }
        }
        
        healthStore.execute(query)
    }
    
    private func startHealthDataUpdates() {
        // تحديث البيانات كل 10 ثواني
        Timer.scheduledTimer(withTimeInterval: 10, repeats: true) { _ in
            if healthDataAvailable {
                fetchHealthData()
            }
        }
    }
}

// مكون رأس التطبيق
struct HeaderView: View {
    var body: some View {
        VStack(spacing: 8) {
            Image(systemName: "applewatch")
                .font(.system(size: 40))
                .foregroundColor(.blue)
            
            Text("ساعتي الصحية")
                .font(.title2)
                .fontWeight(.bold)
                .foregroundColor(.primary)
            
            Text("تابع صحتك ولياقتك")
                .font(.caption)
                .foregroundColor(.secondary)
        }
        .padding()
        .background(Color(.systemBackground))
        .cornerRadius(15)
        .shadow(color: .gray.opacity(0.2), radius: 10, x: 0, y: 5)
    }
}

// مكون ملخص الصحة
struct HealthSummaryView: View {
    @Binding var stepCount: Int
    @Binding var heartRate: Int
    @Binding var caloriesBurned: Int
    @Binding var distance: Double
    
    var body: some View {
        VStack(spacing: 15) {
            Text("ملخص اليوم")
                .font(.headline)
                .fontWeight(.semibold)
            
            HStack(spacing: 15) {
                VStack {
                    Text("\(stepCount)")
                        .font(.title)
                        .fontWeight(.bold)
                        .foregroundColor(.blue)
                    Text("خطوة")
                        .font(.caption)
                        .foregroundColor(.secondary)
                }
                .frame(maxWidth: .infinity)
                .padding()
                .background(Color.blue.opacity(0.1))
                .cornerRadius(10)
                
                VStack {
                    Text("\(heartRate)")
                        .font(.title)
                        .fontWeight(.bold)
                        .foregroundColor(.red)
                    Text("نبضة")
                        .font(.caption)
                        .foregroundColor(.secondary)
                }
                .frame(maxWidth: .infinity)
                .padding()
                .background(Color.red.opacity(0.1))
                .cornerRadius(10)
            }
            
            HStack(spacing: 15) {
                VStack {
                    Text("\(caloriesBurned)")
                        .font(.title)
                        .fontWeight(.bold)
                        .foregroundColor(.orange)
                    Text("سعرة")
                        .font(.caption)
                        .foregroundColor(.secondary)
                }
                .frame(maxWidth: .infinity)
                .padding()
                .background(Color.orange.opacity(0.1))
                .cornerRadius(10)
                
                VStack {
                    Text(String(format: "%.1f", distance))
                        .font(.title)
                        .fontWeight(.bold)
                        .foregroundColor(.green)
                    Text("كم")
                        .font(.caption)
                        .foregroundColor(.secondary)
                }
                .frame(maxWidth: .infinity)
                .padding()
                .background(Color.green.opacity(0.1))
                .cornerRadius(10)
            }
        }
        .padding()
        .background(Color(.systemBackground))
        .cornerRadius(15)
        .shadow(color: .gray.opacity(0.2), radius: 10, x: 0, y: 5)
    }
}

// نموذج بيانات الصحة
struct HealthMetric: Identifiable {
    let id = UUID()
    let title: String
    let value: String
    let unit: String
    let icon: String
}

// مكون قائمة مقاييس الصحة
struct HealthMetricsListView: View {
    let metrics: [HealthMetric]
    
    var body: some View {
        VStack(alignment: .leading, spacing: 12) {
            Text("مقاييس الصحة")
                .font(.headline)
                .fontWeight(.semibold)
                .padding(.horizontal)
            
            ForEach(metrics) { metric in
                HealthMetricRow(metric: metric)
            }
        }
        .padding()
        .background(Color(.systemBackground))
        .cornerRadius(15)
        .shadow(color: .gray.opacity(0.2), radius: 10, x: 0, y: 5)
    }
}

// مكون صف مقياس الصحة
struct HealthMetricRow: View {
    let metric: HealthMetric
    
    var body: some View {
        HStack(spacing: 12) {
            Image(systemName: metric.icon)
                .font(.title2)
                .foregroundColor(.blue)
                .frame(width: 30)
            
            VStack(alignment: .leading, spacing: 2) {
                Text(metric.title)
                    .font(.subheadline)
                    .foregroundColor(.primary)
                Text("\(metric.value) \(metric.unit)")
                    .font(.caption)
                    .foregroundColor(.secondary)
            }
            
            Spacer()
            
            Image(systemName: "chevron.right")
                .font(.caption)
                .foregroundColor(.gray)
        }
        .padding(.vertical, 8)
        .padding(.horizontal)
        .background(Color(.systemGray6))
        .cornerRadius(8)
    }
}

// مكون تحكم في التمرين
struct WorkoutControlView: View {
    @Binding var isActive: Bool
    @Binding var workoutTime: Int
    
    var body: some View {
        VStack(spacing: 15) {
            Text("التمرين")
                .font(.headline)
                .fontWeight(.semibold)
            
            Button(action: {
                isActive.toggle()
                if isActive {
                    startWorkoutTimer()
                } else {
                    workoutTime = 0
                }
            }) {
                Label(
                    isActive ? "إيقاف التمرين" : "بدء التمرين",
                    systemImage: isActive ? "stop.circle.fill" : "play.circle.fill"
                )
                .frame(maxWidth: .infinity)
            }
            .buttonStyle(.borderedProminent)
            .controlSize(.large)
            
            if isActive {
                Text("الوقت: \(formatTime(workoutTime))")
                    .font(.title2)
                    .fontWeight(.bold)
                    .foregroundColor(.green)
            }
        }
        .padding()
        .background(Color(.systemBackground))
        .cornerRadius(15)
        .shadow(color: .gray.opacity(0.2), radius: 10, x: 0, y: 5)
    }
    
    private func startWorkoutTimer() {
        Timer.scheduledTimer(withTimeInterval: 1, repeats: true) { timer in
            if isActive {
                workoutTime += 1
            } else {
                timer.invalidate()
            }
        }
    }
    
    private func formatTime(_ seconds: Int) -> String {
        let minutes = seconds / 60
        let remainingSeconds = seconds % 60
        return String(format: "%02d:%02d", minutes, remainingSeconds)
    }
}

// مكون التنبيهات
struct NotificationsView: View {
    var body: some View {
        VStack(alignment: .leading, spacing: 12) {
            Text("التنبيهات")
                .font(.headline)
                .fontWeight(.semibold)
            
            VStack(alignment: .leading, spacing: 8) {
                NotificationItem(
                    title: "تذكير بالماء",
                    message: "اشرب كوب من الماء",
                    time: "قبل 30 دقيقة"
                )
                
                NotificationItem(
                    title: "تذكير بالحركة",
                    message: "تحرك قليلاً",
                    time: "قبل ساعتين"
                )
                
                NotificationItem(
                    title: "هدف اليوم",
                    message: "حققت 80% من هدف الخطوات",
                    time: "اليوم"
                )
            }
        }
        .padding()
        .background(Color(.systemBackground))
        .cornerRadius(15)
        .shadow(color: .gray.opacity(0.2), radius: 10, x: 0, y: 5)
    }
}

// مكون عنصر التنبيه
struct NotificationItem: View {
    let title: String
    let message: String
    let time: String
    
    var body: some View {
        HStack(alignment: .top, spacing: 10) {
            Image(systemName: "bell.fill")
                .foregroundColor(.orange)
                .frame(width: 20)
            
            VStack(alignment: .leading, spacing: 2) {
                Text(title)
                    .font(.subheadline)
                    .fontWeight(.medium)
                Text(message)
                    .font(.caption)
                    .foregroundColor(.secondary)
            }
            
            Spacer()
            
            Text(time)
                .font(.caption2)
                .foregroundColor(.gray)
        }
        .padding(.vertical, 4)
    }
}

// مكون الإعدادات
struct SettingsView: View {
    @Binding var healthDataAvailable: Bool
    
    var body: some View {
        VStack(alignment: .leading, spacing: 12) {
            Text("الإعدادات")
                .font(.headline)
                .fontWeight(.semibold)
            
            HStack {
                Text("وصول HealthKit")
                Spacer()
                Image(systemName: healthDataAvailable ? "checkmark.circle.fill" : "xmark.circle.fill")
                    .foregroundColor(healthDataAvailable ? .green : .red)
            }
            .padding(.vertical, 4)
            
            Button("تحديث البيانات") {
                // تحديث جميع البيانات
            }
            .buttonStyle(.bordered)
            .frame(maxWidth: .infinity)
        }
        .padding()
        .background(Color(.systemBackground))
        .cornerRadius(15)
        .shadow(color: .gray.opacity(0.2), radius: 10, x: 0, y: 5)
    }
}

// تطبيق WatchOS الرئيسي
@main
struct WatchApp: App {
    var body: some Scene {
        WindowGroup {
            ContentView()
        }
    }
}
                        
3

الخطوة 3: تعلم HealthKit

HealthKit هو إطار عمل من Apple لجمع ومعالجة بيانات الصحة مثل الخطوات، معدل ضربات القلب، وغيرها

الأهمية:

الأساس لفهم كيفية الوصول إلى بيانات الصحة وتحليلها

الأدوات:

Xcode مع Swift

مثال HealthKit متقدم:

// HealthKitManager.swift - مدير HealthKit المتكامل
import Foundation
import HealthKit
import SwiftUI

class HealthKitManager: ObservableObject {
    static let shared = HealthKitManager()
    
    private let healthStore = HKHealthStore()
    
    @Published var stepCount: Int = 0
    @Published var heartRate: Int = 72
    @Published var activeCalories: Int = 0
    @Published var distance: Double = 0.0
    @Published var sleepHours: Double = 0.0
    @Published var oxygenSaturation: Double = 0.0
    @Published var bloodPressure: (systolic: Int, diastolic: Int)? = nil
    
    @Published var isAuthorized = false
    @Published var isLoading = false
    @Published var lastUpdate = Date()
    
    // أنواع البيانات الصحية المدعومة
    private var supportedTypes: Set {
        var types = Set()
        
        // البيانات الكمية
        if let stepType = HKQuantityType.quantityType(forIdentifier: .stepCount) {
            types.insert(stepType)
        }
        
        if let heartRateType = HKQuantityType.quantityType(forIdentifier: .heartRate) {
            types.insert(heartRateType)
        }
        
        if let calorieType = HKQuantityType.quantityType(forIdentifier: .activeEnergyBurned) {
            types.insert(calorieType)
        }
        
        if let distanceType = HKQuantityType.quantityType(forIdentifier: .distanceWalkingRunning) {
            types.insert(distanceType)
        }
        
        if let oxygenType = HKQuantityType.quantityType(forIdentifier: .oxygenSaturation) {
            types.insert(oxygenType)
        }
        
        if let bloodPressureSystolic = HKQuantityType.quantityType(forIdentifier: .bloodPressureSystolic),
           let bloodPressureDiastolic = HKQuantityType.quantityType(forIdentifier: .bloodPressureDiastolic) {
            types.insert(bloodPressureSystolic)
            types.insert(bloodPressureDiastolic)
        }
        
        // البيانات الفئوية
        if let sleepType = HKCategoryType.categoryType(forIdentifier: .sleepAnalysis) {
            types.insert(sleepType)
        }
        
        return types
    }
    
    private init() {
        checkAuthorization()
    }
    
    // طلب الإذن
    func requestAuthorization() {
        isLoading = true
        
        healthStore.requestAuthorization(toShare: nil, read: supportedTypes) { [weak self] success, error in
            DispatchQueue.main.async {
                self?.isLoading = false
                self?.isAuthorized = success
                
                if success {
                    print("تم منح إذن HealthKit بنجاح")
                    self?.fetchAllHealthData()
                    self?.setupBackgroundDelivery()
                } else if let error = error {
                    print("خطأ في إذن HealthKit: \(error.localizedDescription)")
                }
            }
        }
    }
    
    private func checkAuthorization() {
        for type in supportedTypes {
            let status = healthStore.authorizationStatus(for: type)
            if status == .sharingAuthorized {
                isAuthorized = true
                fetchAllHealthData()
                setupBackgroundDelivery()
                break
            }
        }
    }
    
    // جلب جميع البيانات الصحية
    func fetchAllHealthData() {
        guard isAuthorized else { return }
        
        isLoading = true
        
        let group = DispatchGroup()
        
        group.enter()
        fetchStepCount { group.leave() }
        
        group.enter()
        fetchHeartRate { group.leave() }
        
        group.enter()
        fetchActiveCalories { group.leave() }
        
        group.enter()
        fetchDistance { group.leave() }
        
        group.enter()
        fetchSleepData { group.leave() }
        
        group.enter()
        fetchOxygenSaturation { group.leave() }
        
        group.enter()
        fetchBloodPressure { group.leave() }
        
        group.notify(queue: .main) { [weak self] in
            self?.isLoading = false
            self?.lastUpdate = Date()
            print("تم تحديث جميع البيانات الصحية")
        }
    }
    
    // جلب عدد الخطوات
    private func fetchStepCount(completion: @escaping () -> Void) {
        guard let stepType = HKQuantityType.quantityType(forIdentifier: .stepCount) else {
            completion()
            return
        }
        
        let now = Date()
        let startOfDay = Calendar.current.startOfDay(for: now)
        let predicate = HKQuery.predicateForSamples(
            withStart: startOfDay,
            end: now,
            options: .strictStartDate
        )
        
        let query = HKStatisticsQuery(
            quantityType: stepType,
            quantitySamplePredicate: predicate,
            options: .cumulativeSum
        ) { [weak self] _, result, error in
            DispatchQueue.main.async {
                if let result = result, let sum = result.sumQuantity() {
                    let steps = sum.doubleValue(for: HKUnit.count())
                    self?.stepCount = Int(steps)
                } else if let error = error {
                    print("خطأ في جلب الخطوات: \(error.localizedDescription)")
                }
                completion()
            }
        }
        
        healthStore.execute(query)
    }
    
    // جلب معدل ضربات القلب
    private func fetchHeartRate(completion: @escaping () -> Void) {
        guard let heartRateType = HKQuantityType.quantityType(forIdentifier: .heartRate) else {
            completion()
            return
        }
        
        let sortDescriptor = NSSortDescriptor(key: HKSampleSortIdentifierEndDate, ascending: false)
        let predicate = HKQuery.predicateForSamples(
            withStart: Date().addingTimeInterval(-3600), // آخر ساعة
            end: Date(),
            options: .strictEndDate
        )
        
        let query = HKSampleQuery(
            sampleType: heartRateType,
            predicate: predicate,
            limit: 10,
            sortDescriptors: [sortDescriptor]
        ) { [weak self] _, samples, error in
            DispatchQueue.main.async {
                if let samples = samples as? [HKQuantitySample] {
                    // حساب المتوسط
                    let heartRateUnit = HKUnit(from: "count/min")
                    let total = samples.reduce(0.0) { $0 + $1.quantity.doubleValue(for: heartRateUnit) }
                    let average = total / Double(samples.count)
                    self?.heartRate = Int(average.rounded())
                } else if let error = error {
                    print("خطأ في جلب معدل ضربات القلب: \(error.localizedDescription)")
                }
                completion()
            }
        }
        
        healthStore.execute(query)
    }
    
    // جلب السعرات الحرارية النشطة
    private func fetchActiveCalories(completion: @escaping () -> Void) {
        guard let calorieType = HKQuantityType.quantityType(forIdentifier: .activeEnergyBurned) else {
            completion()
            return
        }
        
        let now = Date()
        let startOfDay = Calendar.current.startOfDay(for: now)
        let predicate = HKQuery.predicateForSamples(
            withStart: startOfDay,
            end: now,
            options: .strictStartDate
        )
        
        let query = HKStatisticsQuery(
            quantityType: calorieType,
            quantitySamplePredicate: predicate,
            options: .cumulativeSum
        ) { [weak self] _, result, error in
            DispatchQueue.main.async {
                if let result = result, let sum = result.sumQuantity() {
                    let calories = sum.doubleValue(for: HKUnit.kilocalorie())
                    self?.activeCalories = Int(calories)
                } else if let error = error {
                    print("خطأ في جلب السعرات الحرارية: \(error.localizedDescription)")
                }
                completion()
            }
        }
        
        healthStore.execute(query)
    }
    
    // جلب المسافة المقطوعة
    private func fetchDistance(completion: @escaping () -> Void) {
        guard let distanceType = HKQuantityType.quantityType(forIdentifier: .distanceWalkingRunning) else {
            completion()
            return
        }
        
        let now = Date()
        let startOfDay = Calendar.current.startOfDay(for: now)
        let predicate = HKQuery.predicateForSamples(
            withStart: startOfDay,
            end: now,
            options: .strictStartDate
        )
        
        let query = HKStatisticsQuery(
            quantityType: distanceType,
            quantitySamplePredicate: predicate,
            options: .cumulativeSum
        ) { [weak self] _, result, error in
            DispatchQueue.main.async {
                if let result = result, let sum = result.sumQuantity() {
                    let distanceInMeters = sum.doubleValue(for: HKUnit.meter())
                    self?.distance = distanceInMeters / 1000 // تحويل إلى كيلومتر
                } else if let error = error {
                    print("خطأ في جلب المسافة: \(error.localizedDescription)")
                }
                completion()
            }
        }
        
        healthStore.execute(query)
    }
    
    // جلب بيانات النوم
    private func fetchSleepData(completion: @escaping () -> Void) {
        guard let sleepType = HKCategoryType.categoryType(forIdentifier: .sleepAnalysis) else {
            completion()
            return
        }
        
        let now = Date()
        let startOfYesterday = Calendar.current.startOfDay(for: now.addingTimeInterval(-86400))
        let predicate = HKQuery.predicateForSamples(
            withStart: startOfYesterday,
            end: now,
            options: .strictStartDate
        )
        
        let query = HKSampleQuery(
            sampleType: sleepType,
            predicate: predicate,
            limit: HKObjectQueryNoLimit,
            sortDescriptors: nil
        ) { [weak self] _, samples, error in
            DispatchQueue.main.async {
                if let samples = samples as? [HKCategorySample] {
                    var totalSleep: TimeInterval = 0
                    
                    for sample in samples {
                        if sample.value == HKCategoryValueSleepAnalysis.asleep.rawValue {
                            totalSleep += sample.endDate.timeIntervalSince(sample.startDate)
                        }
                    }
                    
                    self?.sleepHours = totalSleep / 3600 // تحويل إلى ساعات
                } else if let error = error {
                    print("خطأ في جلب بيانات النوم: \(error.localizedDescription)")
                }
                completion()
            }
        }
        
        healthStore.execute(query)
    }
    
    // جلب تشبع الأكسجين
    private func fetchOxygenSaturation(completion: @escaping () -> Void) {
        guard let oxygenType = HKQuantityType.quantityType(forIdentifier: .oxygenSaturation) else {
            completion()
            return
        }
        
        let sortDescriptor = NSSortDescriptor(key: HKSampleSortIdentifierEndDate, ascending: false)
        let predicate = HKQuery.predicateForSamples(
            withStart: Date().addingTimeInterval(-86400), // آخر 24 ساعة
            end: Date(),
            options: .strictEndDate
        )
        
        let query = HKSampleQuery(
            sampleType: oxygenType,
            predicate: predicate,
            limit: 1,
            sortDescriptors: [sortDescriptor]
        ) { [weak self] _, samples, error in
            DispatchQueue.main.async {
                if let samples = samples as? [HKQuantitySample],
                   let sample = samples.first {
                    let oxygenValue = sample.quantity.doubleValue(for: HKUnit.percent())
                    self?.oxygenSaturation = oxygenValue * 100 // تحويل إلى نسبة مئوية
                } else if let error = error {
                    print("خطأ في جلب تشبع الأكسجين: \(error.localizedDescription)")
                }
                completion()
            }
        }
        
        healthStore.execute(query)
    }
    
    // جلب ضغط الدم
    private func fetchBloodPressure(completion: @escaping () -> Void) {
        guard let systolicType = HKQuantityType.quantityType(forIdentifier: .bloodPressureSystolic),
              let diastolicType = HKQuantityType.quantityType(forIdentifier: .bloodPressureDiastolic) else {
            completion()
            return
        }
        
        let sortDescriptor = NSSortDescriptor(key: HKSampleSortIdentifierEndDate, ascending: false)
        let predicate = HKQuery.predicateForSamples(
            withStart: Date().addingTimeInterval(-86400), // آخر 24 ساعة
            end: Date(),
            options: .strictEndDate
        )
        
        let group = DispatchGroup()
        var systolicValue: Double?
        var diastolicValue: Double?
        
        group.enter()
        let systolicQuery = HKSampleQuery(
            sampleType: systolicType,
            predicate: predicate,
            limit: 1,
            sortDescriptors: [sortDescriptor]
        ) { _, samples, error in
            if let samples = samples as? [HKQuantitySample],
               let sample = samples.first {
                systolicValue = sample.quantity.doubleValue(for: HKUnit.millimeterOfMercury())
            }
            group.leave()
        }
        
        group.enter()
        let diastolicQuery = HKSampleQuery(
            sampleType: diastolicType,
            predicate: predicate,
            limit: 1,
            sortDescriptors: [sortDescriptor]
        ) { _, samples, error in
            if let samples = samples as? [HKQuantitySample],
               let sample = samples.first {
                diastolicValue = sample.quantity.doubleValue(for: HKUnit.millimeterOfMercury())
            }
            group.leave()
        }
        
        healthStore.execute(systolicQuery)
        healthStore.execute(diastolicQuery)
        
        group.notify(queue: .main) { [weak self] in
            if let systolic = systolicValue, let diastolic = diastolicValue {
                self?.bloodPressure = (Int(systolic), Int(diastolic))
            }
            completion()
        }
    }
    
    // إعداد تحديث الخلفية
    private func setupBackgroundDelivery() {
        for type in supportedTypes {
            let query = HKObserverQuery(sampleType: type, predicate: nil) { [weak self] _, completionHandler, error in
                if error == nil {
                    self?.fetchAllHealthData()
                }
                completionHandler()
            }
            
            healthStore.execute(query)
            healthStore.enableBackgroundDelivery(for: type, frequency: .hourly) { success, error in
                if success {
                    print("تم تمكين تحديث الخلفية لـ \(type.identifier)")
                }
            }
        }
    }
    
    // حفظ بيانات مخصصة
    func saveWorkoutData(calories: Double, distance: Double, duration: TimeInterval, workoutType: HKWorkoutActivityType) {
        guard isAuthorized else { return }
        
        let workout = HKWorkout(
            activityType: workoutType,
            start: Date().addingTimeInterval(-duration),
            end: Date(),
            duration: duration,
            totalEnergyBurned: HKQuantity(unit: HKUnit.kilocalorie(), doubleValue: calories),
            totalDistance: HKQuantity(unit: HKUnit.meter(), doubleValue: distance * 1000),
            metadata: [HKMetadataKeyIndoorWorkout: false]
        )
        
        healthStore.save(workout) { success, error in
            if success {
                print("تم حفظ بيانات التمرين بنجاح")
            } else if let error = error {
                print("خطأ في حفظ التمرين: \(error.localizedDescription)")
            }
        }
    }
    
    // الحصول على الاتجاهات والتوقعات
    func getHealthTrends(completion: @escaping ([HealthTrend]) -> Void) {
        guard isAuthorized else {
            completion([])
            return
        }
        
        var trends: [HealthTrend] = []
        let group = DispatchGroup()
        
        // تحليل اتجاه الخطوات
        group.enter()
        analyzeStepTrend { trend in
            if let trend = trend { trends.append(trend) }
            group.leave()
        }
        
        // تحليل اتجاه معدل ضربات القلب
        group.enter()
        analyzeHeartRateTrend { trend in
            if let trend = trend { trends.append(trend) }
            group.leave()
        }
        
        group.notify(queue: .main) {
            completion(trends)
        }
    }
    
    private func analyzeStepTrend(completion: @escaping (HealthTrend?) -> Void) {
        // تنفيذ تحليل الاتجاهات
        completion(HealthTrend(
            type: .steps,
            direction: .increasing,
            percentage: 15.5,
            message: "زيادة في متوسط الخطوات اليومية"
        ))
    }
    
    private func analyzeHeartRateTrend(completion: @escaping (HealthTrend?) -> Void) {
        completion(HealthTrend(
            type: .heartRate,
            direction: .stable,
            percentage: 2.3,
            message: "معدل ضربات القلب مستقر"
        ))
    }
}

// نموذج اتجاه الصحة
struct HealthTrend {
    enum TrendType {
        case steps, heartRate, calories, distance, sleep
    }
    
    enum TrendDirection {
        case increasing, decreasing, stable
    }
    
    let type: TrendType
    let direction: TrendDirection
    let percentage: Double
    let message: String
}

// HealthKitView.swift - واجهة SwiftUI مع HealthKit
import SwiftUI

struct HealthKitView: View {
    @StateObject private var healthManager = HealthKitManager.shared
    @State private var showingTrends = false
    @State private var healthTrends: [HealthTrend] = []
    
    var body: some View {
        NavigationView {
            ScrollView {
                VStack(spacing: 20) {
                    // رأس التطبيق
                    HealthHeaderView(
                        isAuthorized: healthManager.isAuthorized,
                        lastUpdate: healthManager.lastUpdate
                    )
                    
                    // حالة التحميل
                    if healthManager.isLoading {
                        ProgressView("جاري تحديث البيانات...")
                            .padding()
                    }
                    
                    // بيانات الصحة
                    if healthManager.isAuthorized {
                        HealthDataView(
                            stepCount: healthManager.stepCount,
                            heartRate: healthManager.heartRate,
                            activeCalories: healthManager.activeCalories,
                            distance: healthManager.distance,
                            sleepHours: healthManager.sleepHours,
                            oxygenSaturation: healthManager.oxygenSaturation,
                            bloodPressure: healthManager.bloodPressure
                        )
                        
                        // أزرار التحكم
                        HealthControlsView(
                            onRefresh: { healthManager.fetchAllHealthData() },
                            onShowTrends: {
                                healthManager.getHealthTrends { trends in
                                    healthTrends = trends
                                    showingTrends = true
                                }
                            }
                        )
                    } else {
                        // طلب الإذن
                        AuthorizationView {
                            healthManager.requestAuthorization()
                        }
                    }
                }
                .padding()
            }
            .navigationTitle("HealthKit")
            .sheet(isPresented: $showingTrends) {
                HealthTrendsView(trends: healthTrends)
            }
        }
    }
}

// باقي مكونات SwiftUI مشابهة للمثال السابق...
                        
4

الخطوة 4: تعلم Fitness APIs

Fitness APIs هي أدوات Google لجمع ومعالجة بيانات اللياقة البدنية مثل الخطوات، النشاطات، وغيرها

الأهمية:

الأساس لفهم كيفية الوصول إلى بيانات اللياقة البدنية وتحليلها

الأدوات:

Android Studio مع Kotlin/Java

مثال Fitness APIs:

// FitnessManager.kt - مدير Fitness APIs المتكامل
package com.example.wearfitness

import android.app.Activity
import android.content.Context
import android.util.Log
import com.google.android.gms.auth.api.signin.GoogleSignIn
import com.google.android.gms.auth.api.signin.GoogleSignInAccount
import com.google.android.gms.auth.api.signin.GoogleSignInClient
import com.google.android.gms.auth.api.signin.GoogleSignInOptions
import com.google.android.gms.common.api.ApiException
import com.google.android.gms.fitness.Fitness
import com.google.android.gms.fitness.FitnessOptions
import com.google.android.gms.fitness.data.DataType
import com.google.android.gms.fitness.data.DataSet
import com.google.android.gms.fitness.data.Field
import com.google.android.gms.fitness.request.DataReadRequest
import com.google.android.gms.fitness.request.DataUpdateRequest
import com.google.android.gms.fitness.request.SessionInsertRequest
import com.google.android.gms.fitness.result.DataReadResponse
import com.google.android.gms.tasks.Task
import com.google.android.gms.tasks.Tasks
import java.util.Calendar
import java.util.Date
import java.util.concurrent.TimeUnit

class FitnessManager(private val context: Context) {
    companion object {
        private const val TAG = "FitnessManager"
        
        // أنواع البيانات المدعومة
        private val SUPPORTED_DATA_TYPES = setOf(
            DataType.TYPE_STEP_COUNT_DELTA,
            DataType.TYPE_HEART_RATE_BPM,
            DataType.TYPE_CALORIES_EXPENDED,
            DataType.TYPE_DISTANCE_DELTA,
            DataType.TYPE_ACTIVITY_SEGMENT,
            DataType.TYPE_SLEEP_SEGMENT
        )
    }
    
    // حالة المدير
    private lateinit var googleSignInClient: GoogleSignInClient
    private var fitnessOptions: FitnessOptions? = null
    private var currentAccount: GoogleSignInAccount? = null
    
    // بيانات اللياقة
    data class FitnessData(
        val steps: Long = 0,
        val heartRate: Double = 0.0,
        val calories: Double = 0.0,
        val distance: Double = 0.0,
        val activities: List = emptyList(),
        val sleepData: SleepData? = null,
        val lastUpdated: Date = Date()
    )
    
    data class ActivityData(
        val type: String,
        val duration: Long, // بالمللي ثانية
        val startTime: Date,
        val endTime: Date,
        val calories: Double = 0.0,
        val distance: Double = 0.0
    )
    
    data class SleepData(
        val startTime: Date,
        val endTime: Date,
        val duration: Long,
        val stages: Map // مراحل النوم
    )
    
    // الاتجاهات والتوقعات
    data class FitnessTrend(
        val metric: String,
        val currentValue: Double,
        val previousValue: Double,
        val changePercent: Double,
        val trend: TrendDirection,
        val suggestion: String
    )
    
    enum class TrendDirection {
        INCREASING, DECREASING, STABLE
    }
    
    init() {
        initializeFitnessOptions()
        initializeGoogleSignIn()
    }
    
    private fun initializeFitnessOptions() {
        val builder = FitnessOptions.builder()
        
        // إضافة أنواع البيانات المطلوبة
        SUPPORTED_DATA_TYPES.forEach { dataType ->
            builder.addDataType(dataType, FitnessOptions.ACCESS_READ)
            builder.addDataType(dataType, FitnessOptions.ACCESS_WRITE)
        }
        
        fitnessOptions = builder.build()
    }
    
    private fun initializeGoogleSignIn() {
        val gso = GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)
            .requestEmail()
            .build()
        
        googleSignInClient = GoogleSignIn.getClient(context, gso)
    }
    
    // تسجيل الدخول إلى Google Fitness
    fun signIn(activity: Activity, requestCode: Int) {
        val options = fitnessOptions ?: return
        
        if (!GoogleSignIn.hasPermissions(GoogleSignIn.getLastSignedInAccount(context), options)) {
            GoogleSignIn.requestPermissions(
                activity,
                requestCode,
                GoogleSignIn.getLastSignedInAccount(context),
                options
            )
        } else {
            // المستخدم مسجل الدخول بالفعل
            currentAccount = GoogleSignIn.getLastSignedInAccount(context)
            Log.d(TAG, "المستخدم مسجل الدخول بالفعل")
        }
    }
    
    // معالجة نتيجة تسجيل الدخول
    fun handleSignInResult(data: Intent?): Boolean {
        val task = GoogleSignIn.getSignedInAccountFromIntent(data)
        return try {
            val account = task.getResult(ApiException::class.java)
            currentAccount = account
            Log.d(TAG, "تم تسجيل الدخول بنجاح: ${account?.email}")
            true
        } catch (e: ApiException) {
            Log.w(TAG, "فشل تسجيل الدخول: ${e.statusCode}")
            false
        }
    }
    
    // تسجيل الخروج
    fun signOut() {
        googleSignInClient.signOut().addOnCompleteListener {
            currentAccount = null
            Log.d(TAG, "تم تسجيل الخروج")
        }
    }
    
    // التحقق من حالة المصادقة
    fun isSignedIn(): Boolean {
        return currentAccount != null && GoogleSignIn.hasPermissions(
            currentAccount,
            fitnessOptions ?: return false
        )
    }
    
    // جلب بيانات اللياقة الشاملة
    suspend fun fetchFitnessData(): FitnessData {
        if (!isSignedIn()) {
            throw IllegalStateException("المستخدم غير مسجل الدخول")
        }
        
        val account = currentAccount ?: throw IllegalStateException("لا يوجد حساب")
        
        return try {
            // جلب البيانات المتوازية
            val stepsTask = fetchStepCount(account)
            val heartRateTask = fetchHeartRate(account)
            val caloriesTask = fetchCalories(account)
            val distanceTask = fetchDistance(account)
            val activitiesTask = fetchActivities(account)
            val sleepTask = fetchSleepData(account)
            
            // انتظار جميع المهام
            Tasks.await(Tasks.whenAll(
                stepsTask, heartRateTask, caloriesTask,
                distanceTask, activitiesTask, sleepTask
            ))
            
            // تجميع النتائج
            FitnessData(
                steps = stepsTask.result,
                heartRate = heartRateTask.result,
                calories = caloriesTask.result,
                distance = distanceTask.result,
                activities = activitiesTask.result,
                sleepData = sleepTask.result
            )
        } catch (e: Exception) {
            Log.e(TAG, "خطأ في جلب بيانات اللياقة: ${e.message}")
            throw e
        }
    }
    
    // جلب عدد الخطوات
    private fun fetchStepCount(account: GoogleSignInAccount): Task {
        return Fitness.getHistoryClient(context, account)
            .readDailyTotal(DataType.TYPE_STEP_COUNT_DELTA)
            .continueWith { task ->
                if (task.isSuccessful) {
                    val dataSet = task.result
                    if (dataSet != null && dataSet.dataPoints.isNotEmpty()) {
                        val steps = dataSet.dataPoints.first()
                            .getValue(Field.FIELD_STEPS).asInt().toLong()
                        steps
                    } else {
                        0L
                    }
                } else {
                    Log.e(TAG, "خطأ في جلب الخطوات: ${task.exception}")
                    0L
                }
            }
    }
    
    // جلب معدل ضربات القلب
    private fun fetchHeartRate(account: GoogleSignInAccount): Task {
        val endTime = Calendar.getInstance().timeInMillis
        val startTime = endTime - TimeUnit.DAYS.toMillis(1)
        
        val request = DataReadRequest.Builder()
            .read(DataType.TYPE_HEART_RATE_BPM)
            .setTimeRange(startTime, endTime, TimeUnit.MILLISECONDS)
            .bucketByTime(1, TimeUnit.HOURS)
            .build()
        
        return Fitness.getHistoryClient(context, account)
            .readData(request)
            .continueWith { task ->
                if (task.isSuccessful) {
                    val response = task.result
                    val dataPoints = response?.getDataSet(DataType.TYPE_HEART_RATE_BPM)?.dataPoints
                    
                    if (!dataPoints.isNullOrEmpty()) {
                        // حساب المتوسط
                        val sum = dataPoints.sumOf { it.getValue(Field.FIELD_BPM).asFloat() }
                        (sum / dataPoints.size).toDouble()
                    } else {
                        0.0
                    }
                } else {
                    Log.e(TAG, "خطأ في جلب معدل ضربات القلب: ${task.exception}")
                    0.0
                }
            }
    }
    
    // جلب السعرات الحرارية
    private fun fetchCalories(account: GoogleSignInAccount): Task {
        return Fitness.getHistoryClient(context, account)
            .readDailyTotal(DataType.TYPE_CALORIES_EXPENDED)
            .continueWith { task ->
                if (task.isSuccessful) {
                    val dataSet = task.result
                    if (dataSet != null && dataSet.dataPoints.isNotEmpty()) {
                        dataSet.dataPoints.first()
                            .getValue(Field.FIELD_CALORIES).asFloat().toDouble()
                    } else {
                        0.0
                    }
                } else {
                    Log.e(TAG, "خطأ في جلب السعرات الحرارية: ${task.exception}")
                    0.0
                }
            }
    }
    
    // جلب المسافة
    private fun fetchDistance(account: GoogleSignInAccount): Task {
        return Fitness.getHistoryClient(context, account)
            .readDailyTotal(DataType.TYPE_DISTANCE_DELTA)
            .continueWith { task ->
                if (task.isSuccessful) {
                    val dataSet = task.result
                    if (dataSet != null && dataSet.dataPoints.isNotEmpty()) {
                        dataSet.dataPoints.first()
                            .getValue(Field.FIELD_DISTANCE).asFloat().toDouble()
                    } else {
                        0.0
                    }
                } else {
                    Log.e(TAG, "خطأ في جلب المسافة: ${task.exception}")
                    0.0
                }
            }
    }
    
    // جلب الأنشطة
    private fun fetchActivities(account: GoogleSignInAccount): Task> {
        val endTime = Calendar.getInstance().timeInMillis
        val startTime = endTime - TimeUnit.DAYS.toMillis(1)
        
        val request = DataReadRequest.Builder()
            .read(DataType.TYPE_ACTIVITY_SEGMENT)
            .setTimeRange(startTime, endTime, TimeUnit.MILLISECONDS)
            .build()
        
        return Fitness.getHistoryClient(context, account)
            .readData(request)
            .continueWith { task ->
                if (task.isSuccessful) {
                    val response = task.result
                    val dataSet = response?.getDataSet(DataType.TYPE_ACTIVITY_SEGMENT)
                    val activities = mutableListOf()
                    
                    dataSet?.dataPoints?.forEach { dataPoint ->
                        val activityType = dataPoint.getValue(Field.FIELD_ACTIVITY).asActivity()
                        val start = Date(dataPoint.getStartTime(TimeUnit.MILLISECONDS))
                        val end = Date(dataPoint.getEndTime(TimeUnit.MILLISECONDS))
                        val duration = end.time - start.time
                        
                        activities.add(
                            ActivityData(
                                type = activityType,
                                duration = duration,
                                startTime = start,
                                endTime = end
                            )
                        )
                    }
                    
                    activities
                } else {
                    Log.e(TAG, "خطأ في جلب الأنشطة: ${task.exception}")
                    emptyList()
                }
            }
    }
    
    // جلب بيانات النوم
    private fun fetchSleepData(account: GoogleSignInAccount): Task {
        val endTime = Calendar.getInstance().timeInMillis
        val startTime = endTime - TimeUnit.DAYS.toMillis(1)
        
        val request = DataReadRequest.Builder()
            .read(DataType.TYPE_SLEEP_SEGMENT)
            .setTimeRange(startTime, endTime, TimeUnit.MILLISECONDS)
            .build()
        
        return Fitness.getHistoryClient(context, account)
            .readData(request)
            .continueWith { task ->
                if (task.isSuccessful) {
                    val response = task.result
                    val dataSet = response?.getDataSet(DataType.TYPE_SLEEP_SEGMENT)
                    
                    if (!dataSet?.dataPoints.isNullOrEmpty()) {
                        val dataPoint = dataSet!!.dataPoints.first()
                        val start = Date(dataPoint.getStartTime(TimeUnit.MILLISECONDS))
                        val end = Date(dataPoint.getEndTime(TimeUnit.MILLISECONDS))
                        val duration = end.time - start.time
                        
                        // تحليل مراحل النوم
                        val stages = mutableMapOf()
                        dataSet.dataPoints.forEach { point ->
                            val stage = point.getValue(Field.FIELD_SLEEP_SEGMENT_TYPE).asInt()
                            val stageDuration = point.getEndTime(TimeUnit.MILLISECONDS) -
                                    point.getStartTime(TimeUnit.MILLISECONDS)
                            
                            val stageName = when (stage) {
                                1 -> "خفيف"
                                2 -> "عميق"
                                3 -> "REM"
                                else -> "مجهول"
                            }
                            
                            stages[stageName] = stages.getOrDefault(stageName, 0) + stageDuration
                        }
                        
                        SleepData(start, end, duration, stages)
                    } else {
                        null
                    }
                } else {
                    Log.e(TAG, "خطأ في جلب بيانات النوم: ${task.exception}")
                    null
                }
            }
    }
    
    // حفظ بيانات تمرين
    fun saveWorkoutSession(
        activityType: String,
        startTime: Date,
        endTime: Date,
        calories: Double,
        distance: Double
    ) {
        if (!isSignedIn()) return
        
        val account = currentAccount ?: return
        
        // إنشاء جلسة تمرين
        val session = com.google.android.gms.fitness.data.Session.Builder()
            .setName("تمرين $activityType")
            .setIdentifier("${startTime.time}_${activityType}")
            .setDescription("تمرين تم تسجيله من التطبيق")
            .setStartTime(startTime.time, TimeUnit.MILLISECONDS)
            .setEndTime(endTime.time, TimeUnit.MILLISECONDS)
            .setActivity(activityType)
            .build()
        
        // إضافة بيانات التمرين
        val calorieDataSet = DataSet.builder(DataType.TYPE_CALORIES_EXPENDED)
            .add(com.google.android.gms.fitness.data.DataPoint.builder(DataType.TYPE_CALORIES_EXPENDED)
                .setTimeInterval(startTime.time, endTime.time, TimeUnit.MILLISECONDS)
                .setField(Field.FIELD_CALORIES, calories.toFloat())
                .build())
            .build()
        
        val distanceDataSet = DataSet.builder(DataType.TYPE_DISTANCE_DELTA)
            .add(com.google.android.gms.fitness.data.DataPoint.builder(DataType.TYPE_DISTANCE_DELTA)
                .setTimeInterval(startTime.time, endTime.time, TimeUnit.MILLISECONDS)
                .setField(Field.FIELD_DISTANCE, distance.toFloat())
                .build())
            .build()
        
        // إنشاء طلب الإدراج
        val insertRequest = SessionInsertRequest.Builder()
            .setSession(session)
            .addDataSet(calorieDataSet)
            .addDataSet(distanceDataSet)
            .build()
        
        // إرسال الطلب
        Fitness.getSessionsClient(context, account)
            .insertSession(insertRequest)
            .addOnSuccessListener {
                Log.d(TAG, "تم حفظ جلسة التمرين بنجاح")
            }
            .addOnFailureListener { e ->
                Log.e(TAG, "فشل حفظ جلسة التمرين: ${e.message}")
            }
    }
    
    // تحليل الاتجاهات
    suspend fun analyzeTrends(): List {
        if (!isSignedIn()) return emptyList()
        
        val account = currentAccount ?: return emptyList()
        
        return try {
            val trends = mutableListOf()
            
            // تحليل اتجاه الخطوات (آخر 7 أيام)
            val stepTrend = analyzeStepTrend(account)
            stepTrend?.let { trends.add(it) }
            
            // تحليل اتجاه السعرات الحرارية
            val calorieTrend = analyzeCalorieTrend(account)
            calorieTrend?.let { trends.add(it) }
            
            // تحليل اتجاه المسافة
            val distanceTrend = analyzeDistanceTrend(account)
            distanceTrend?.let { trends.add(it) }
            
            trends
        } catch (e: Exception) {
            Log.e(TAG, "خطأ في تحليل الاتجاهات: ${e.message}")
            emptyList()
        }
    }
    
    private suspend fun analyzeStepTrend(account: GoogleSignInAccount): FitnessTrend? {
        val endTime = Calendar.getInstance().timeInMillis
        val startTime = endTime - TimeUnit.DAYS.toMillis(7)
        
        val request = DataReadRequest.Builder()
            .aggregate(DataType.TYPE_STEP_COUNT_DELTA, DataType.AGGREGATE_STEP_COUNT_DELTA)
            .bucketByTime(1, TimeUnit.DAYS)
            .setTimeRange(startTime, endTime, TimeUnit.MILLISECONDS)
            .build()
        
        return try {
            val response = Tasks.await(
                Fitness.getHistoryClient(context, account).readData(request)
            )
            
            val buckets = response.buckets
            if (buckets.size >= 2) {
                val current = buckets.last().getDataSet(DataType.AGGREGATE_STEP_COUNT_DELTA)
                val previous = buckets[buckets.size - 2].getDataSet(DataType.AGGREGATE_STEP_COUNT_DELTA)
                
                val currentSteps = current?.dataPoints?.firstOrNull()
                    ?.getValue(Field.FIELD_STEPS)?.asInt()?.toDouble() ?: 0.0
                val previousSteps = previous?.dataPoints?.firstOrNull()
                    ?.getValue(Field.FIELD_STEPS)?.asInt()?.toDouble() ?: 0.0
                
                val changePercent = if (previousSteps > 0) {
                    ((currentSteps - previousSteps) / previousSteps) * 100
                } else 0.0
                
                val trend = when {
                    changePercent > 5 -> TrendDirection.INCREASING
                    changePercent < -5 -> TrendDirection.DECREASING
                    else -> TrendDirection.STABLE
                }
                
                val suggestion = when (trend) {
                    TrendDirection.INCREASING -> "استمر في هذا الأداء الممتاز!"
                    TrendDirection.DECREASING -> "حاول زيادة نشاطك اليومي"
                    TrendDirection.STABLE -> "حافظ على مستواك الحالي"
                }
                
                FitnessTrend(
                    metric = "الخطوات",
                    currentValue = currentSteps,
                    previousValue = previousSteps,
                    changePercent = changePercent,
                    trend = trend,
                    suggestion = suggestion
                )
            } else {
                null
            }
        } catch (e: Exception) {
            Log.e(TAG, "خطأ في تحليل اتجاه الخطوات: ${e.message}")
            null
        }
    }
    
    // تحليل اتجاه السعرات الحرارية (مشابه لـ analyzeStepTrend)
    private suspend fun analyzeCalorieTrend(account: GoogleSignInAccount): FitnessTrend? {
        // تنفيذ مشابه لـ analyzeStepTrend
        return null
    }
    
    // تحليل اتجاه المسافة (مشابه لـ analyzeStepTrend)
    private suspend fun analyzeDistanceTrend(account: GoogleSignInAccount): FitnessTrend? {
        // تنفيذ مشابه لـ analyzeStepTrend
        return null
    }
    
    // إنشاء أهداف يومية
    fun setDailyGoal(metric: String, targetValue: Double) {
        if (!isSignedIn()) return
        
        // حفظ الهدف محلياً أو على السحابة
        val prefs = context.getSharedPreferences("fitness_goals", Context.MODE_PRIVATE)
        prefs.edit().putFloat("goal_$metric", targetValue.toFloat()).apply()
        
        Log.d(TAG, "تم تعيين هدف $metric إلى $targetValue")
    }
    
    // التحقق من تحقيق الأهداف
    suspend fun checkGoals(fitnessData: FitnessData): Map {
        val prefs = context.getSharedPreferences("fitness_goals", Context.MODE_PRIVATE)
        val goals = mutableMapOf()
        
        // التحقق من هدف الخطوات
        val stepGoal = prefs.getFloat("goal_steps", 10000f).toDouble()
        goals["steps"] = fitnessData.steps >= stepGoal
        
        // التحقق من هدف السعرات الحرارية
        val calorieGoal = prefs.getFloat("goal_calories", 500f).toDouble()
        goals["calories"] = fitnessData.calories >= calorieGoal
        
        // التحقق من هدف المسافة
        val distanceGoal = prefs.getFloat("goal_distance", 5f).toDouble()
        goals["distance"] = fitnessData.distance >= distanceGoal
        
        return goals
    }
}

// MainActivity.kt - النشاط الرئيسي
class MainActivity : AppCompatActivity() {
    private lateinit var fitnessManager: FitnessManager
    private lateinit var binding: ActivityMainBinding
    
    companion object {
        private const val RC_SIGN_IN = 9001
    }
    
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)
        
        fitnessManager = FitnessManager(this)
        
        setupUI()
        checkSignInStatus()
    }
    
    private fun setupUI() {
        binding.signInButton.setOnClickListener {
            fitnessManager.signIn(this, RC_SIGN_IN)
        }
        
        binding.refreshButton.setOnClickListener {
            refreshFitnessData()
        }
        
        binding.logoutButton.setOnClickListener {
            fitnessManager.signOut()
            updateUIForSignedOut()
        }
    }
    
    private fun checkSignInStatus() {
        if (fitnessManager.isSignedIn()) {
            updateUIForSignedIn()
            refreshFitnessData()
        } else {
            updateUIForSignedOut()
        }
    }
    
    private fun updateUIForSignedIn() {
        binding.signInButton.visibility = View.GONE
        binding.logoutButton.visibility = View.VISIBLE
        binding.fitnessDataLayout.visibility = View.VISIBLE
    }
    
    private fun updateUIForSignedOut() {
        binding.signInButton.visibility = View.VISIBLE
        binding.logoutButton.visibility = View.GONE
        binding.fitnessDataLayout.visibility = View.GONE
    }
    
    private fun refreshFitnessData() {
        binding.progressBar.visibility = View.VISIBLE
        
        lifecycleScope.launch {
            try {
                val fitnessData = fitnessManager.fetchFitnessData()
                displayFitnessData(fitnessData)
                
                val goals = fitnessManager.checkGoals(fitnessData)
                displayGoals(goals)
                
                val trends = fitnessManager.analyzeTrends()
                displayTrends(trends)
            } catch (e: Exception) {
                Toast.makeText(
                    this@MainActivity,
                    "خطأ في جلب البيانات: ${e.message}",
                    Toast.LENGTH_SHORT
                ).show()
            } finally {
                binding.progressBar.visibility = View.GONE
            }
        }
    }
    
    private fun displayFitnessData(data: FitnessManager.FitnessData) {
        binding.stepsText.text = data.steps.toString()
        binding.heartRateText.text = String.format("%.1f", data.heartRate)
        binding.caloriesText.text = String.format("%.1f", data.calories)
        binding.distanceText.text = String.format("%.2f", data.distance)
        
        binding.lastUpdatedText.text = SimpleDateFormat("HH:mm", Locale.getDefault())
            .format(data.lastUpdated)
    }
    
    private fun displayGoals(goals: Map) {
        binding.stepsGoalIndicator.setImageResource(
            if (goals["steps"] == true) R.drawable.ic_goal_achieved else R.drawable.ic_goal_pending
        )
        
        binding.caloriesGoalIndicator.setImageResource(
            if (goals["calories"] == true) R.drawable.ic_goal_achieved else R.drawable.ic_goal_pending
        )
        
        binding.distanceGoalIndicator.setImageResource(
            if (goals["distance"] == true) R.drawable.ic_goal_achieved else R.drawable.ic_goal_pending
        )
    }
    
    private fun displayTrends(trends: List) {
        val trendText = StringBuilder("الاتجاهات:\n")
        
        trends.forEach { trend ->
            val arrow = when (trend.trend) {
                FitnessManager.TrendDirection.INCREASING -> "↑"
                FitnessManager.TrendDirection.DECREASING -> "↓"
                FitnessManager.TrendDirection.STABLE -> "→"
            }
            
            trendText.append("${trend.metric}: $arrow ${String.format("%.1f", trend.changePercent)}%\n")
            trendText.append("${trend.suggestion}\n\n")
        }
        
        binding.trendsText.text = trendText.toString()
    }
    
    override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
        super.onActivityResult(requestCode, resultCode, data)
        
        if (requestCode == RC_SIGN_IN) {
            if (fitnessManager.handleSignInResult(data)) {
                updateUIForSignedIn()
                refreshFitnessData()
            } else {
                Toast.makeText(this, "فشل تسجيل الدخول", Toast.LENGTH_SHORT).show()
            }
        }
    }
}
                        

هندسة تطبيقات الساعات الذكية

تطبيق الهاتف (Companion)

التطبيق الرئيسي على الهاتف الذكي

تطبيق الساعة (Watch)

التطبيق المصغر على الساعة الذكية

بيانات الصحة واللياقة

HealthKit، Fitness APIs، وأجهزة الاستشعار

أدوات تطوير الساعات الذكية

Android Studio

للتطوير على Android Wear

رسمي متكامل قوي

Xcode

للتطوير على WatchOS

متكامل دقيق رسمي

HealthKit

لبيانات الصحة من Apple

صحة بيانات تحليل

المزايا والتحديات

المزايا

  • طلب عالي: هناك طلب كبير على مطوري تطبيقات الأجهزة القابلة للارتداء خاصة في مجالات الصحة واللياقة
  • أدوات مجانية: معظم الأدوات المستخدمة مثل Android Studio و Xcode مجانية أو تقدم خطط مجانية
  • تأثير إيجابي: يمكنك المساهمة في تحسين حياة المستخدمين من خلال تطبيقات الصحة واللياقة
  • إبداع لا محدود: يمكنك تصميم تطبيقات مبتكرة تعمل على أجهزة متعددة وتقدم تجربة مستخدم فريدة
  • سوق متنامي: سوق الساعات الذكية والأجهزة القابلة للارتداء في نمو مستمر

التحديات

  • منحنى التعلم الحاد: يتطلب فهماً جيداً للغات البرمجة، التصميم، وأساسيات أنظمة التشغيل للأجهزة القابلة للارتداء
  • تعقيد الأنظمة: قد تواجه تحديات في إدارة التطبيقات التي تعتمد على بيانات صحية أو رياضية
  • تحديثات متكررة: الأدوات والمعايير تتطور باستمرار، مما يتطلب تحديث المعرفة بشكل منتظم
  • قيود الأجهزة: الساعات الذكية لها قيود في المعالجة والبطارية والتخزين

أنواع مشاريع الساعات الذكية

تطبيقات الصحة

مراقبة معدل ضربات القلب، النوم، واللياقة

صحة مراقبة بيانات

تطبيقات الرياضة

تتبع التمارين، الجري، والأنشطة البدنية

رياضة تمرين تتبع

تطبيقات الإشعارات

إدارة الإشعارات، الرسائل، والمهام

إشعارات مهام تواصل

الخلاصة

تطوير تطبيقات الساعات الذكية والأجهزة القابلة للارتداء يوفر فرصاً فريدة في مجالات الصحة واللياقة البدنية. من خلال إتقان Android Wear، WatchOS، وأدوات مثل HealthKit و Fitness APIs، يمكنك بناء تطبيقات مبتكرة تحسن من حياة المستخدمين.

نصائح للبدء:

  • ابدأ بتعلم أساسيات Android Wear مع Kotlin/Java
  • تعلم WatchOS مع Swift لساعات Apple
  • أتقن HealthKit لبيانات الصحة من Apple
  • تعلم Fitness APIs لبيانات اللياقة من Google
  • ابنِ مشروع عملي يركز على الصحة أو اللياقة