تنفيذ الأوامر

توضّح هذه الصفحة كيفية تنفيذ الأوامر من خلال التفاعل الصوتي.

تنفيذ طلبات الوسائط

يمكن تقسيم الأوامر ذات الصلة بالوسائط إلى ثلاث مجموعات مختلفة:

  • مصادر الوسائط الخارجية (مثل Spotify المثبَّت في AAOS)
  • مصادر الوسائط في الخلفية (مثل الموسيقى التي يتم بثها عبر VIA)
  • مصادر الوسائط المحلية (مثل راديو السيارة)

التعامل مع أوامر مصدر الوسائط الخارجية

يتم تعريف مصادر الوسائط الخارجية على أنّها تطبيقات Android تتوافق مع واجهات برمجة التطبيقات MediaSessionCompat وMediaBrowseCompat (راجِع مقالة إنشاء تطبيقات وسائط للسيارات للحصول على شرح تفصيلي حول استخدام واجهات برمجة التطبيقات هذه).

ملاحظة مهمة: لربط تطبيق مساعد بـ MediaBrowseService جميع تطبيقات الوسائط المثبّتة في النظام، يجب أن يستوفي التطبيق الشروط التالية:

  1. يجب أن يتم تثبيته على أنّه موقّع من النظام (راجِع إرشادات تطوير تطبيقات الوسائط لنظام التشغيل Android Automotive ونموذج PackageValidator الرمز البرمجي).
  2. يجب أن يكون لديك إذن android.permission.MEDIA_CONTENT_CONTROL على مستوى النظام (راجِع منح أذونات على مستوى النظام).

بالإضافة إلى MediaBrowserCompat وMediaControllerCompat، يوفّر نظام التشغيل Android Automotive ما يلي:

  • CarMediaService تقدّم معلومات مركزية عن مصدر الوسائط المحدّد حاليًا. يُستخدَم هذا الإذن أيضًا لاستئناف تشغيل مصدر وسائط تم تشغيله سابقًا بعد إيقاف السيارة وإعادة تشغيلها.
  • توفّر واجهة برمجة التطبيقات car-media-common طرقًا ملائمة لإدراج تطبيقات الوسائط والاتصال بها والتفاعل معها.

في ما يلي إرشادات خاصة بتنفيذ الأوامر الشائعة للتفاعل الصوتي.

الحصول على قائمة بمصادر الوسائط المثبّتة

يمكن رصد مصادر الوسائط باستخدام PackageManager، وفلترة الخدمات التي تتطابق مع MediaBrowserService.SERVICE_INTERFACE. في بعض السيارات، قد تكون هناك بعض عمليات تنفيذ خدمة متصفّح الوسائط الخاصة، والتي يجب استبعادها. في ما يلي مثال على هذه المنطق:

private Map<String, MediaSource> getAvailableMediaSources() {
    List<String> customMediaServices =
        Arrays.asList(mContext.getResources()
            .getStringArray(R.array.custom_media_packages));
    List<ResolveInfo> mediaServices = mPackageManager.queryIntentServices(
            new Intent(MediaBrowserService.SERVICE_INTERFACE),
            PackageManager.GET_RESOLVED_FILTER);
    Map<String, MediaSource> result = new HashMap<>();
    for (ResolveInfo info : mediaServices) {
        String packageName = info.serviceInfo.packageName;
        if (customMediaServices.contains(packageName)) {
            // Custom media sources should be ignored, as they might have a
            // specialized handling (e.g., radio).
            continue;
        }
        String className = info.serviceInfo.name;
        ComponentName componentName = new ComponentName(packageName,
            className);
        MediaSource source = MediaSource.create(mContext, componentName);
        result.put(source.getDisplayName().toString().toLowerCase(),
            source);
    }
    return result;
}

يُرجى العِلم أنّه يمكن تثبيت مصادر الوسائط أو إلغاء تثبيتها في أي وقت. للحفاظ على دقة القائمة، ننصحك بتنفيذ مثيل BroadcastReceiver لإجراءات الأهداف ACTION_PACKAGE_ADDED وACTION_PACKAGE_CHANGED وACTION_PACKAGE_REPLACED وACTION_PACKAGE_REMOVED.

الربط بمصدر الوسائط التي يتم تشغيلها حاليًا

توفّر السمة CarMediaService طُرقًا للحصول على مصدر الوسائط المحدّد حاليًا، وعندما يتغيّر مصدر الوسائط هذا. قد تحدث هذه التغييرات لأنّ المستخدم تفاعل مع واجهة المستخدم مباشرةً أو بسبب استخدام أزرار الأجهزة في السيارة. من ناحية أخرى، يوفّر تطبيق car-media-common طرقًا ملائمة للاتصال بمصدر وسائط معيّن. في ما يلي مقتطف مبسَّط حول كيفية الاتصال بتطبيق الوسائط المحدّد حاليًا:

public class MediaActuator implements
        MediaBrowserConnector.onConnectedBrowserChanged {
    private final Car mCar;
    private CarMediaManager mCarMediaManager;
    private MediaBrowserConnector mBrowserConnector;

    

    public void initialize(Context context) {
        mCar = Car.createCar(context);
        mBrowserConnector = new MediaBrowserConnector(context, this);
        mCarMediaManager = (CarMediaManager)
            mCar.getCarManager(Car.CAR_MEDIA_SERVICE);
        mBrowserConnector.connectTo(mCarMediaManager.getMediaSource());
        
    }

    @Override
    public void onConnectedBrowserChanged(
            @Nullable MediaBrowserCompat browser) {
        // TODO: Handle connected/disconnected browser
    }

    
}

التحكّم في تشغيل مصدر الوسائط الذي يتم تشغيله حاليًا

باستخدام MediaBrowserCompat مرتبط، يمكنك بسهولة إرسال أوامر التحكّم في النقل إلى التطبيق المستهدف. إليك مثال مبسط:

public class MediaActuator   {
    
    private MediaControllerCompat mMediaController;

    @Override
    public void onConnectedBrowserChanged(
            @Nullable MediaBrowserCompat browser) {
        if (browser != null && browser.isConnected()) {
            mMediaController = new MediaControllerCompat(mContext,
                browser.getSessionToken());
        } else {
            mMediaController = null;
        }
    }

    private boolean playSongOnCurrentSource(String song) {
        if (mMediaController == null) {
            // No source selected.
            return false;
        }
        MediaControllerCompat.TransportControls controls =
            mMediaController.getTransportControls();
        PlaybackStateCompat state = controller.getPlaybackState();
        if (state == null || ((state.getActions() &
                PlaybackStateCompat.ACTION_PLAY_FROM_SEARCH) == 0)) {
            // Source can't play from search
            return false;
        }
        controls.playFromSearch(query, null);
        return true;
    }

    
}

التعامل مع أوامر مصدر الوسائط المحلية (الراديو ومشغّل الأقراص المضغوطة والبلوتوث وUSB)

تتيح مصادر الوسائط المحلية وظائفها للنظام باستخدام واجهتَي برمجة التطبيقات MediaSession وMediaBrowse الموضّحتَين أعلاه. ولمراعاة خصائص كل نوع من الأجهزة، تستخدم خدمات MediaBrowse هذه اصطلاحات معيّنة لتنظيم المعلومات وأوامر الوسائط.

التعامل مع الراديو

يمكن تحديد Radio MediaBrowseService من خلال ACTION_PLAY_BROADCASTRADIO intent filter. ويُتوقّع منهم اتّباع عناصر التحكّم في التشغيل وبنية تصفّح الوسائط الموضّحة في تنفيذ الراديو. يوفّر نظام التشغيل Android Automotive OS مكتبة car-broadcastradio-support تحتوي على ثوابت وطُرق لمساعدة مصنّعي المعدات الأصلية في إنشاء عمليات تنفيذ MediaBrowseService لخدمات الراديو الخاصة بهم التي تتّبع البروتوكول المحدّد، كما يوفّر الدعم للتطبيقات التي تستخدم شجرة التصفّح (مثل تطبيقات VIA).

التعامل مع الإدخال الإضافي وصوت القرص المضغوط ووسائط USB

لا يتوفّر تنفيذ تلقائي لمصادر الوسائط هذه كجزء من مشروع Android مفتوح المصدر (AOSP). الطريقة المقترَحة هي:

التعامل مع البلوتوث

يتم عرض محتوى الوسائط عبر البلوتوث من خلال ملف AVRCP Bluetooth. لتسهيل الوصول إلى هذه الوظيفة، يتضمّن AAOS تنفيذًا لـ MediaBrowserService وMediaSession يجرّد تفاصيل الاتصال (راجِع packages/apps/Bluetooth).

يتم تحديد بنية شجرة متصفّح الوسائط المعنيّة في فئة BrowseTree. يمكن إرسال أوامر التحكّم في التشغيل بالطريقة نفسها التي يتم بها إرسال الأوامر إلى أي تطبيق آخر، وذلك باستخدام تنفيذ MediaSession.

التعامل مع أوامر بث الوسائط

لتنفيذ بث الوسائط من جهة الخادم، يجب أن يصبح تطبيق VIA نفسه مصدر وسائط، وأن ينفّذ واجهة برمجة التطبيقات MediaBrowse وMediaSession. يُرجى الرجوع إلى إنشاء تطبيقات وسائط للسيارات. من خلال تنفيذ واجهات برمجة التطبيقات هذه، سيتمكّن تطبيق التحكّم الصوتي من إجراء ما يلي (من بين أمور أخرى):

  • المشاركة بسلاسة في عملية اختيار مصدر الوسائط
  • استئنافها تلقائيًا بعد إعادة تشغيل السيارة
  • توفير عناصر التحكّم في التشغيل والتصفّح باستخدام واجهة مستخدم "مركز الوسائط"
  • تلقّي أحداث أزرار الوسائط العادية في الأجهزة

لا توجد طريقة موحّدة للتفاعل مع جميع تطبيقات التنقّل. للاطّلاع على عمليات الدمج مع "خرائط Google"، راجِع Google Maps for Android Automotive Intents. للحصول على معلومات حول عمليات الدمج مع تطبيقات أخرى، يُرجى التواصل مع مطوّري التطبيقات مباشرةً. قبل إطلاق أي هدف إلى أي تطبيق (بما في ذلك "خرائط Google")، تأكَّد من إمكانية تنفيذ الهدف (راجِع أهداف "خرائط Google"). ويتيح ذلك إمكانية إبلاغ المستخدم في حال عدم توفّر التطبيق المستهدف.

تنفيذ أوامر المركبة

يتم توفير إذن الوصول إلى خصائص المركبة للقراءة والكتابة من خلال CarPropertyManager. يتم توضيح أنواع خصائص المركبة وطريقة تنفيذها وتفاصيل أخرى في إعدادات الخصائص. للحصول على وصف دقيق للخصائص التي يتيحها نظام التشغيل Android، من الأفضل الرجوع مباشرةً إلى hardware/interfaces/automotive/vehicle/2.0/types.hal. يحتوي تعداد VehicleProperty المحدّد هناك على سمات عادية وخاصة بالمورّد، وأنواع البيانات، ووضع التغيير، والوحدات، وتعريف إذن الوصول للقراءة والكتابة.

للوصول إلى هذه الثوابت نفسها من Java، يمكنك استخدام VehiclePropertyIds وفئاته المصاحبة. تتضمّن المواقع المختلفة أذونات Android مختلفة تتحكّم في إمكانية الوصول إليها. يتم الإفصاح عن هذه الأذونات في ملف بيان CarService، ويتم وصف عملية الربط بين السمات والأذونات في VehiclePropertyIds Javadoc، ويتم فرضها في PropertyHalServiceIds.

قراءة إحدى سمات المركبة

في ما يلي مثال يوضّح كيفية قراءة سرعة المركبة:

public class CarActuator ... {
    private final Car mCar;
    private final CarPropertyManager mCarPropertyManager;
    private final TextToSpeech mTTS;

    /** Global VHAL area id */
    public static final int GLOBAL_AREA_ID = 0;

    public CarActuator(Context context, TextToSpeech tts) {
        mCar = Car.createCar(context);
        mCarPropertyManager = (CarPropertyManager) mCar.getCarManager(Car.PROPERTY_SERVICE);
        mTTS = tts;
        ...
    }

    @Nullable
    private void getSpeedInMetersPerSecond() {
        if (!mCarPropertyManager.isPropertyAvailable(VehiclePropertyIds.PERF_VEHICLE_SPEED,
                GLOBAL_AREA_ID)) {
            mTTS.speak("I'm sorry, but I can't read the speed of this vehicle");
            return;
        }
        // Data type and unit can be found in
        // automotive/vehicle/2.0/types.hal
        float speedInMps = mCarPropertyManager.getFloatProperty(
                VehiclePropertyIds.PERF_VEHICLE_SPEED, GLOBAL_AREA_ID);
        int speedInMph = (int)(speedInMetersPerSecond * 2.23694f);
        mTTS.speak(String.format("Sure. Your current speed is %d miles "
                + "per hour", speedInUserUnit);
    }

    ...
}

ضبط سمة مركبة

في ما يلي مثال يوضّح كيفية تشغيل مكيف الهواء الأمامي وإيقافه.

public class CarActuator … {
    …

    private void changeFrontAC(boolean turnOn) {
        List<CarPropertyConfig> configs = mCarPropertyManager
                .getPropertyList(new ArraySet<>(Arrays.asList(
                    VehiclePropertyIds.HVAC_AC_ON)));
        if (configs == null || configs.size() != 1) {
            mTTS.speak("I'm sorry, but I can't control the AC of your vehicle");
            return;
        }

        // Find the front area Ids for the AC property.
        int[] areaIds = configs.get(0).getAreaIds();
        List<Integer> areasToChange = new ArrayList<>();
        for (int areaId : areaIds) {
            if ((areaId & (VehicleAreaSeat.SEAT_ROW_1_CENTER
                        | VehicleAreaSeat.SEAT_ROW_1_LEFT
                        | VehicleAreaSeat.SEAT_ROW_1_RIGHT)) == 0) {
                continue;
            }
            boolean isACInAreaAlreadyOn = mCarPropertyManager
                    .getBooleanProperty(VehiclePropertyIds.HVAC_AC_ON, areaId);
            if ((!isACInAreaAlreadyOn && turnOn) || (isACInAreaAlreadyOn && !turnOn)) {
                areasToChange.add(areaId);
            }
        }
        if (areasToChange.isEmpty()) {
            mTTS.speak(String.format("The AC is already %s", turnOn ? "on" : "off"));
            return;
        }

        for (int areaId : areasToChange) {
            mCarPropertyManager.setBooleanProperty(
                VehiclePropertyIds.HVAC_AC_ON, areaId, turnOn);
        }
        mTTS.speak(String.format("Okay, I'm turning your front AC %s",
            turnOn ? "on" : "off"));
    }

    …
}

تنفيذ أوامر التواصل

التعامل مع أوامر المراسلة

يجب أن تتعامل المساعدات الافتراضية المدمجة في السيارة مع الرسائل الواردة باتّباع مسار "النقر للقراءة" الموضّح في النقر للقراءة في المساعد الافتراضي المدمج في السيارة، والذي يمكنه اختياريًا التعامل مع إرسال الردود إلى مرسل الرسالة الواردة. بالإضافة إلى ذلك، يمكن للمساعدين الافتراضيين في السيارة استخدام SmsManager (جزء من حزمة android.telephony) لإنشاء رسائل SMS وإرسالها مباشرةً من السيارة أو عبر البلوتوث.

التعامل مع أوامر المكالمات

وبطريقة مماثلة، يمكن للمساعدات الافتراضية استخدام TelephonyManager لإجراء مكالمات هاتفية والاتصال برقم البريد الصوتي الخاص بالمستخدم. في هذه الحالات، ستتفاعل تطبيقات VIA مع حزمة برامج الاتصال مباشرةً أو مع تطبيق "طلب المكالمات" في السيارة. وفي أي حال، يجب أن يكون تطبيق "طلب المكالمات" في السيارة هو الذي يعرض واجهة المستخدم ذات الصلة بالمكالمات الصوتية للمستخدم.

تنفيذ الطلبات الأخرى

للاطّلاع على قائمة بنقاط الدمج الأخرى المحتملة بين VIA والنظام، راجِع قائمة أهداف Android المعروفة. يمكن حلّ العديد من أوامر المستخدمين من جهة الخادم (على سبيل المثال، قراءة الرسائل الإلكترونية وأحداث التقويم الخاصة بالمستخدمين)، ولا تتطلّب أي تفاعلات مع النظام بخلاف التفاعل الصوتي نفسه.

الإجراءات الغامرة (عرض محتوى مرئي)

عندما تحسّن VIA إجراءات المستخدم أو فهمه، يمكنها تقديم محتوى مرئي تكميلي على شاشة السيارة. للحدّ من تشتيت انتباه السائق، يجب أن يكون هذا المحتوى بسيطًا وموجزًا ويتضمّن إجراءً يمكن اتخاذه. للحصول على تفاصيل حول إرشادات واجهة المستخدم/تجربة المستخدم بشأن الإجراءات الغامرة، يُرجى الاطّلاع على المساعدين المحمّلين مسبقًا: إرشادات تجربة المستخدم.

لإتاحة التخصيص وضمان التناسق مع بقية تصميم وحدة الرأس (HU)، يجب أن تستخدم تطبيقات VIA مكونات مكتبة Car UI لمعظم عناصر واجهة المستخدم. لمزيد من التفاصيل، يُرجى الاطّلاع على التخصيص.