פיתוח אפליקציות

כדי להטמיע אפליקציית אינטראקציה קולית (VIA), מבצעים את השלבים הבאים:

  1. יוצרים שלד של VIA.
  2. (אופציונלי) מטמיעים תהליך הגדרה/כניסה.
  3. (אופציונלי) מטמיעים מסך הגדרות.
  4. הצהרה על ההרשאות הנדרשות בקובץ המניפסט.
  5. הטמעת ממשק משתמש של לוחית קול.
  6. הטמעת זיהוי קולי (חובה לכלול הטמעה של RecognitionService API).
  7. מטמיעים את ההצהרה (אופציונלי: אפשר להטמיע את TextToSpeech API).
  8. הטמעת ביצוע פקודות. תוכלו לקרוא על כך בקטע ביצוע פקודות.

בקטעים הבאים מוסבר איך לבצע כל אחד מהשלבים שצוינו למעלה.

יצירת שלד של VIA

מניפסטים

האפליקציה מזוהה כאפליקציה עם אינטראקציה קולית אם הנתונים הבאים כלולים במניפסט:

AndroidManifest.xml

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.myvoicecontrol">
    ...

  <application ... >
    <service android:name=".MyInteractionService"
        android:label="@string/app_name"
        android:permission="android.permission.BIND_VOICE_INTERACTION"
        android:process=":interactor">
      <meta-data
          android:name="android.voice_interaction"
          android:resource="@xml/interaction_service" />
      <intent-filter>
        <action android:name=
          "android.service.voice.VoiceInteractionService" />
      </intent-filter>
    </service>
  </application>
</manifest>

בדוגמה הזו:

  • ממשקי VIA חייבים לחשוף שירות שמרחיב את VoiceInteractionService, עם מסנן Intent לפעולה VoiceInteractionService.SERVICE_INTERFACE ("android.service.voice.VoiceInteractionService").
  • לשירות הזה צריכה להיות הרשאת החתימה של המערכת BIND_VOICE_INTERACTION.
  • השירות הזה צריך לכלול קובץ מטא-נתונים מסוג android.voice_interaction, שיכיל את הפרטים הבאים:

    res/xml/interaction_service.xml

    <voice-interaction-service
        xmlns:android="http://schemas.android.com/apk/res/android"
        android:sessionService=
          "com.example.MyInteractionSessionService"
        android:recognitionService=
          "com.example.MyRecognitionService"
        android:settingsActivity=
          "com.example.MySettingsActivity"
        android:supportsAssist="true"
        android:supportsLaunchVoiceAssistFromKeyguard="true"
        android:supportsLocalInteraction="true" />

פרטים על כל שדה מופיעים במאמר R.styleable#VoiceInteractionService. מאחר שכל ה-VIAs הם גם שירותי זיהוי קול, צריך לכלול ב-Manifest גם את הפרטים הבאים:

AndroidManifest.xml

<manifest ...>
  <uses-permission android:name="android.permission.RECORD_AUDIO"/>
  <application ...>
    ...
    <service android:name=".RecognitionService" ...>
      <intent-filter>
        <action android:name="android.speech.RecognitionService" />
        <category android:name="android.intent.category.DEFAULT" />
      </intent-filter>
      <meta-data
        android:name="android.speech"
        android:resource="@xml/recognition_service" />
    </service>
  </application>
</manifest>

שירותי זיהוי הקול דורשים גם את קטע המטא-נתונים הבא:

res/xml/recognition_service.xml

<recognition-service
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:settingsActivity="com.example.MyRecognizerSettingsActivity" />

VoiceInteractionService,‏ VoiceInteractionSessionService ו-VoiceInteractionSession

בתרשים הבא מוצג מחזור החיים של כל אחת מהישויות האלה:

מחזורי חיים

איור 1. מחזורי חיים

כפי שצוין קודם, VoiceInteractionService הוא נקודת הכניסה ל-VIA. האחריות העיקרית של השירות הזה היא:

  • מפעילים את כל התהליכים שצריכים להמשיך לפעול כל עוד ה-VIA הזה פעיל. לדוגמה, זיהוי של מילת הפעלה.
  • דיווח על פעולות קוליות נתמכות (ראו הקשה לקריאה ב-Voice Assistant).
  • הפעלת סשנים של אינטראקציה קולית ממסך הנעילה (מסך הנעילה).

בצורה הפשוטה ביותר, הטמעה של VoiceInteractionService תיראה כך:

public class MyVoiceInteractionService extends VoiceInteractionService {
    private static final List<String> SUPPORTED_VOICE_ACTIONS =
        Arrays.asList(
            CarVoiceInteractionSession.VOICE_ACTION_READ_NOTIFICATION,
            CarVoiceInteractionSession.VOICE_ACTION_REPLY_NOTIFICATION,
            CarVoiceInteractionSession.VOICE_ACTION_HANDLE_EXCEPTION
    );

    @Override
    public void onReady() {
        super.onReady();
        // TODO: Setup hotword detector
    }

    @NonNull
    @Override
    public Set<String> onGetSupportedVoiceActions(
            @NonNull Set<String> voiceActions) {
        Set<String> result = new HashSet<>(voiceActions);
        result.retainAll(SUPPORTED_VOICE_ACTIONS);
        return result;
    }
    ...
}

צריך להטמיע את VoiceInteractionService#onGetSupportedVoiceActions() כדי לטפל בהקשה כדי לקרוא ב-Voice Assistant. המערכת משתמשת ב-VoiceInteractionSessionService כדי ליצור VoiceInteractionSession ולנהל איתו אינטראקציה. יש לו רק אחריות אחת: להתחיל סשנים חדשים כשמתקבלת בקשה.

public class MyVoiceInteractionSessionService extends VoiceInteractionSessionService {
    @Override
    public VoiceInteractionSession onNewSession(Bundle args) {
        return new MyVoiceInteractionSession(this);
    }
}

לבסוף, VoiceInteractionSession הוא המקום שבו יתבצע רוב העבודה. יכול להיות שנעשה שימוש חוזר במופע אחד של סשן כדי להשלים כמה אינטראקציות של משתמשים. ב-AAOS יש עזר CarVoiceInteractionSession שעוזר להטמיע חלק מהפונקציות הייחודיות לכלי רכב.

public class MyVoiceInteractionSession extends CarVoiceInteractionSession {

    public InteractionSession(Context context) {
        super(context);
    }

    @Override
    protected void onShow(String action, Bundle args, int showFlags) {
        closeSystemDialogs();
        // TODO: Unhide UI and update UI state
        // TODO: Start processing audio input
    }
    ...
}

ל-VoiceInteractionSession יש קבוצה גדולה של שיטות קריאה חוזרת (callback) שמפורטות בקטעים הבאים. אפשר למצוא רשימה מלאה במסמכי העזרה של VoiceInteractionSession.

הטמעת תהליך הגדרה/כניסה

אפשר לבצע את ההגדרה והכניסה:

  • במהלך ההצטרפות למכשיר (אשף ההגדרה).
  • במהלך החלפת שירותי אינטראקציה קולית (הגדרות).
  • בפעם הראשונה שמפעילים את האפליקציה.

לפרטים על חוויית המשתמש המומלצת ועל הנחיות חזותיות, אפשר לעיין במאמר עוזרות שהוגדרו מראש: הנחיות בנושא חוויית משתמש.

הגדרה במהלך החלפת שירותי הקול

תמיד יש אפשרות למשתמש לבחור VIA שלא הוגדר כראוי. הסיבות לכך יכולות להיות:

  • המשתמש דילג על אשף ההגדרה לגמרי או על שלב ההגדרה של האינטראקציה הקולית.
  • המשתמש בחר VIA שונה מזה שהוגדר במהלך ההצטרפות למכשיר.

בכל מקרה, ל-VoiceInteractionService יש כמה דרכים לעודד את המשתמש להשלים את ההגדרה:

  • תזכורת להתראה.
  • תשובה קולית אוטומטית כשהמשתמש מנסה להשתמש בה.

הערה: לא מומלץ להציג תהליך הגדרה של VIA ללא בקשה מפורשת מהמשתמש. המשמעות היא ש-VIAs צריכים להימנע מהצגת תוכן באופן אוטומטי במסך הרכב במהלך הפעלת המכשיר או כתוצאה מהחלפת משתמש או ביטול נעילה.

תזכורת להתראות

התזכורת בהתראה היא דרך לא פולשנית להצביע על הצורך בהגדרה, ולספק למשתמשים אפשרות לנווט לתהליך ההגדרה של Assistant.

תזכורת להתראות

איור 2. תזכורת להתראות

כך התהליך יתבצע:

תהליך שליחת התזכורות

איור 3. תהליך שליחת התזכורות

תשובה קולית

זהו התהליך הפשוט ביותר להטמעה. הוא מתחיל בהפעלת ביטוי חוזר בקריאה חוזרת (call back) של VoiceInteractionSession#onShow(), מסביר למשתמש מה צריך לעשות ואז שואל אותו (אם ההגדרה מותרת בהתאם למצב של הגבלת חוויית המשתמש) אם הוא רוצה להתחיל את תהליך ההגדרה. אם אי אפשר לבצע את ההגדרה באותו זמן, צריך להסביר גם את המצב הזה.

הגדרה בפעם הראשונה

תמיד יש אפשרות שהמשתמש יפעיל VIA שלא הוגדר כראוי. במקרים כאלה:

  1. מדווחים למשתמש על המצב הזה (לדוגמה, "כדי שהפעולה תתבצע כמו שצריך, אצטרך לבקש ממך לבצע כמה שלבים… ").
  2. אם מנוע ההגבלות של חוויית המשתמש מאפשר זאת (ראו UX_RESTRICTIONS_NO_SETUP), שואלים את המשתמש אם הוא רוצה להתחיל בתהליך ההגדרה ואז פותחים את מסך ההגדרות של ה-VIA.
  3. אחרת (לדוגמה, אם המשתמש נוהג), צריך להשאיר התראה למשתמש ללחוץ על האפשרות כשהדבר יהיה בטוח.

יצירת מסכי הגדרה של אינטראקציה קולית

צריך לפתח את מסכי ההגדרה והכניסה כפעילויות רגילות. ההנחיות לגבי חוויית המשתמש והעיצוב החזותי לפיתוח ממשק המשתמש מפורטות במאמר עוזרות שהוגדרו מראש: הנחיות בנושא חוויית משתמש.

הנחיות כלליות:

  • מודעות VIA צריכות לאפשר למשתמשים להפסיק את תהליך ההגדרה ולהמשיך אותו בכל שלב.
  • לא אמורה להיות אפשרות להגדרה אם ההגבלה UX_RESTRICTIONS_NO_SETUP בתוקף. פרטים נוספים זמינים בהנחיות בנושא הסחת דעת של נהגים.
  • מסכי ההגדרה צריכים להתאים למערכת העיצוב של כל רכב. הפריסה הכללית של המסך, הסמלים, הצבעים והיבטים אחרים צריכים להיות עקביים עם שאר חלקי ממשק המשתמש. פרטים נוספים זמינים במאמר התאמה אישית.

הטמעת מסך הגדרות

שילוב ההגדרות

איור 4. שילוב ההגדרות

מסכי ההגדרות הם פעילויות רגילות ב-Android. אם מטמיעים אותם, צריך להצהיר על נקודת הכניסה שלהם ב-res/xml/interaction_service.xml כחלק מהמניפסטים של VIA (ראו מניפסטים). הקטע 'הגדרות' הוא מקום טוב להמשיך את ההגדרה והכניסה (אם המשתמש לא השלים את התהליך) או להציע אפשרות יציאה מהחשבון או החלפת משתמש, לפי הצורך. בדומה למסכי ההגדרה שמתוארים למעלה, המסכים האלה אמורים:

  • צריך לספק אפשרות לחזור למסך הקודם בערימה של המסכים (לדוגמה, להגדרות הרכב).
  • לא יתאפשרו במהלך נהיגה. פרטים נוספים זמינים בהנחיות בנושא הסחת דעת של נהגים.
  • התאמה לכל מערכת של עיצוב רכב. פרטים נוספים זמינים במאמר התאמה אישית.

הצהרה על ההרשאות הנדרשות בקובץ המניפסט

ההרשאות הנדרשות ל-VIA מחולקות לשלוש קטגוריות:

  • הרשאות לחתימה על מערכת אלה הרשאות שניתנות רק לחבילות APK מותקנות מראש בחתימה של המערכת. משתמשים לא יכולים להקצות את ההרשאות האלה, רק יצרני ציוד מקורי (OEM) יכולים להקצות אותן כשהם יוצרים את קובצי האימג' של המערכת. מידע נוסף על קבלת הרשאות חתימה זמין במאמר הענקת הרשאות עם הרשאות מערכת.
  • הרשאות מסוכנות אלה ההרשאות שהמשתמש צריך להעניק באמצעות תיבת הדו-שיח PermissionsController. יצרני ציוד מקורי יכולים להקצות מראש חלק מההרשאות האלה ל-VoiceInteractionService שמוגדר כברירת מחדל. עם זאת, מכיוון שההגדרה שמוגדרת כברירת מחדל עשויה להשתנות ממכשיר למכשיר, אפליקציות צריכות להיות מסוגלות לבקש את ההרשאות האלה לפי הצורך.
  • הרשאות אחרות אלה כל ההרשאות האחרות שלא מחייבות התערבות של משתמשים. ההרשאות האלה מוענקות באופן אוטומטי על ידי המערכת.

לאור מה שצוין למעלה, הקטע הבא מתמקד רק בבקשות להרשאות מסוכנות. צריך לבקש הרשאות רק כשהמשתמש נמצא במסכי הכניסה או ההגדרות.

אם לאפליקציה אין את ההרשאות הנדרשות כדי לפעול, מומלץ להשתמש בביטוי קולי כדי להסביר למשתמש את המצב, ולהציג התראה כדי לספק למשתמש אפשרות לחזור למסכי ההגדרות של VIA. פרטים נוספים זמינים בקטע 1. תזכורת על התראות.

בקשת הרשאות כחלק ממסך ההגדרות

הרשאות מסוכנות מתבקשות באמצעות שיטת ActivityCompat#requestPermission() רגילה (או שיטת חלופית). פרטים על שליחת בקשות להרשאות מופיעים במאמר בקשת הרשאות לאפליקציה.

בקשת הרשאות

איור 5. בקשת הרשאות

הרשאה למאזין להתראות

כדי להטמיע את תהליך TTR, צריך להגדיר את ה-VIA כמאזין להתראות. זו לא הרשאה בפני עצמה, אלא הגדרה שמאפשרת למערכת לשלוח התראות למאזינים רשומים. כדי לבדוק אם ה-VIA קיבל גישה למידע הזה, האפליקציות יכולות:

אם הגישה הזו לא ניתנה מראש, ה-VIA צריך להפנות את המשתמש לקטע 'גישה להתרעות' בהגדרות הרכב, באמצעות שילוב של ביטויים והתראות. אפשר להשתמש בקוד הבא כדי לפתוח את הקטע המתאים באפליקציית ההגדרות:

private void requestNotificationListenerAccess() {
    Intent intent = new Intent(Settings
        .ACTION_NOTIFICATION_LISTENER_SETTINGS);
    intent.putExtra(Settings.EXTRA_APP_PACKAGE, getPackageName());
    startActivity(intent);
}

הטמעת ממשק משתמש של לוחית קול

כשVoiceInteractionSession מקבל קריאה חוזרת מ-onShow(), הוא יכול להציג ממשק משתמש של לוח קולי. להנחיות בנושא חוויית משתמש וחזוניות לגבי הטמעת לוחית הקול,ראו עוזרות שהוגדרו מראש: הנחיות בנושא חוויית משתמש.

הצגת לוחית הקול

איור 6. הצגת לוחית הקול

יש שתי אפשרויות להטמעת ממשק המשתמש הזה:

  • שינוי של VoiceInteractionSession#onCreateContentView()
  • הפעלת פעילות באמצעות VoiceInteractionSession#startAssistantActivity()

שימוש ב-onCreateContentView()

זוהי דרך ברירת המחדל להצגת לוחית קול. בכיתה הבסיסית VoiceInteractionSession נוצר חלון ומנוהל מחזור החיים שלו כל עוד סשן הקול פעיל. אפליקציות צריכות לשנות את VoiceInteractionSession#onCreateContentView() ולהחזיר תצוגה שמצורפת לאותו חלון ברגע שהסשן נוצר. התצוגה הזו אמורה להיות בלתי נראית בהתחלה. כשמתחילה אינטראקציה קולית, התצוגה הזו אמורה להיות גלויה ב-VoiceInteractionSession#onShow() ואז להיעלם שוב ב-VoiceInteractionSession#onHide().

public class MyVoiceInteractionSession extends CarVoiceInteractionSession {
    private View mVoicePlate;
    

    @Override
    public View onCreateContentView() {
        mVoicePlate = inflater.inflate(R.layout.voice_plate, null);
        
   }

    @Override
    protected void onShow(String action, Bundle args, int showFlags) {
        // TODO: Update UI state to "listening"
        mVoicePlate.setVisibility(View.VISIBLE);
    }

    @Override
    public void onHide() {
        mVoicePlate.setVisibility(View.GONE);
    }
    
}

כשמשתמשים בשיטה הזו, כדאי לשנות את הערך של VoiceInteractionSession#onComputeInsets() כדי להביא בחשבון אזורים מוסתרים בממשק המשתמש.

שימוש ב-startAssistantActivity()‎

במקרה כזה, VoiceInteractionSession מעביר את הטיפול בממשק המשתמש של לוח הקול לפעילות רגילה. כשמשתמשים באפשרות הזו, בהפעלה החוזרת (callback) של onPrepareShow() צריך להשבית את היצירה של חלון התוכן שמוגדר כברירת מחדל בהטמעת VoiceInteractionSession (ראו שימוש ב-onCreateContentView()‎). בשעה VoiceInteractionSession#onShow(), הסשן יתחיל את הפעילות של לוח הלחצנים באמצעות VoiceInteractionSession#startAssistantActivity(). השיטה הזו מפעילה את ממשק המשתמש עם הגדרות החלון והדגלים המתאימים לפעילות.

public class MyVoiceInteractionSession extends CarVoiceInteractionSession {
    

    @Override
    public void onPrepareShow(Bundle args, int showFlags) {
        super.onPrepareShow(args, showFlags);
        setUiEnabled(false);
    }

    @Override
    protected void onShow(String action, Bundle args, int showFlags) {
        closeSystemDialogs();
        Intent intent = new Intent(getContext(), VoicePlateActivity.class);
        intent.putExtra(VoicePlateActivity.EXTRA_ACTION, action);
        intent.putExtra(VoicePlateActivity.EXTRA_ARGS, args);
        startAssistantActivity(intent);
    }

    
}

כדי לשמור על תקשורת בין הפעילות הזו לבין VoiceInteractionSession, יכול להיות שתצטרכו להשתמש בקבוצה של כוונות פנימיות או בקישור שירות. לדוגמה, כשמפעילים את VoiceInteractionSession#onHide(), הסשן צריך להיות מסוגל להעביר את הבקשה הזו לפעילות.

חשוב. ב-Automotive, אפשר להציג רק פעילויות עם הערות מיוחדות או פעילויות שמפורטות ב'רשימת ההיתרים' של UXR בזמן הנהיגה. הכלל הזה חל גם על פעילויות שהתחילו באמצעות VoiceInteractionSession#startAssistantActivity(). חשוב לזכור להוסיף הערה לפעילות באמצעות <meta-data android:name="distractionOptimized" android:value="true"/> או לכלול את הפעילות הזו במפתח systemActivityWhitelist של הקובץ /packages/services/Car/service/res/values/config.xml. מידע נוסף זמין במאמר הנחיות בנושא הסחת דעת של נהגים.

הטמעת זיהוי קולי

בקטע הזה נסביר איך להטמיע זיהוי קול באמצעות זיהוי והפעלה של מילות מפתח. מילה מעוררת היא מילה מפעילה שמשמשת להפעלת שאילתה או פעולה חדשה באמצעות הקול. לדוגמה, "Ok Google" או "Hey Google".

זיהוי של מילת הפעלה ב-DSP

מערכת Android מספקת גישה למזהה מילת הפעלה שפועל תמיד ברמת ה-DSP באמצעות AlwaysOnHotwordDetector. דרך להטמיע זיהוי של מילת הפעלה עם מעבד (CPU) שפועל במהירות נמוכה. השימוש בפונקציה הזו מחולק לשני חלקים:

  • יצירת אובייקט של AlwaysOnHotwordDetector.
  • הרשמה של מודל קול לזיהוי מילת הפעלה.

הטמעה של VoiceInteractionService יכולה ליצור גלאי של מילת הפעלה באמצעות VoiceInteractionService#createAlwaysOnHotwordDetector(), ולהעביר ביטוי מפתח ואזור גיאוגרפי שבו רוצים להשתמש לצורך הזיהוי. כתוצאה מכך, האפליקציה מקבלת קריאה חוזרת מסוג onAvailabilityChanged() עם אחד מהערכים האפשריים הבאים:

  • STATE_HARDWARE_UNAVAILABLE. יכולת ה-DSP לא זמינה במכשיר. במקרה כזה, המערכת משתמשת בזיהוי מילת הפעלה בתוכנה.
  • STATE_HARDWARE_UNSUPPORTED. התמיכה ב-DSP לא זמינה באופן כללי, אבל מערכת ה-DSP לא תומכת בשילוב נתון של ביטוי מפתח ולוקאל. האפליקציה יכולה להשתמש בזיהוי מילת הפעלה באמצעות תוכנה.
  • STATE_HARDWARE_ENROLLED. זיהוי מילת ההפעלה מוכן וניתן להפעיל אותו על ידי קריאה ל-method‏ startRecognition().
  • STATE_HARDWARE_UNENROLLED. מודל אודיו של ביטוי המפתח המבוקש לא זמין, אבל אפשר להירשם.

אפשר להשתמש ב-IVoiceInteractionManagerService#updateKeyphraseSoundModel() כדי להירשם למודלים של צלילים לזיהוי מילת הפעלה. אפשר לרשום כמה מודלים במערכת בכל זמן נתון, אבל רק מודל אחד משויך ל-AlwaysOnHotwordDetector. יכול להיות שזיהוי מילת הפעלה באמצעות DSP לא יהיה זמין בכל המכשירים. מפתחים של VIA צריכים לבדוק את יכולות החומרה באמצעות השיטה getDspModuleProperties(). בקוד לדוגמה שמראה איך לרשום מודלים של צלילים מפורט המידע הנדרש בנושא VoiceEnrollment/src/com/android/test/voiceenrollment/EnrollmentUtil.java. מידע נוסף על זיהוי בו-זמני של מילות מפתח מופיע בקטע תיעוד בו-זמני.

זיהוי של מילת הפעלה בתוכנה

כפי שצוין למעלה, יכול להיות שזיהוי של מילות מפתח חמות ב-DSP לא יהיה זמין בכל המכשירים (לדוגמה, אמולטור Android לא מספק הדמיה של DSP). במקרה כזה, זיהוי הקול באמצעות תוכנה הוא האפשרות היחידה. כדי למנוע הפרעה לאפליקציות אחרות שעשויות להזדקק לגישה למיקרופון, ל-VIAs צריכה להיות גישה לקלט האודיו באמצעות:

שני הקבועים האלה הם @hide וזמינים רק לאפליקציות בחבילה.

ניהול קלט האודיו וזיהוי הקול

קלט האודיו יוטמע באמצעות הקלאס MediaRecorder. מידע נוסף על השימוש ב-API הזה זמין במאמר סקירה כללית על MediaRecorder. גם שירותי אינטראקציה קולית צפויים להיות הטמעות מסוג RecognitionService. כל אפליקציה במערכת שדורשת זיהוי קולי משתמשת ב-API הזה כדי לגשת ליכולת הזו. כדי לבצע זיהוי דיבור ולקבל גישה למיקרופון, ל-VIAs צריך להיות android.permission.RECORD_AUDIO. אפליקציות שמקבלות גישה להטמעה של RecognitionService אמורות לקבל גם את ההרשאה הזו.

לפני Android 10, גישה למיקרופון ניתנה רק לאפליקציה אחת בכל פעם (למעט זיהוי של מילות מפתח, כפי שמתואר למעלה). החל מ-Android 10, אפשר לשתף את הגישה למיקרופון. מידע נוסף זמין במאמר שיתוף קלט האודיו.

גישה לפלט אודיו

כשה-VIA מוכן לספק תשובות מילוליות, חשוב לפעול לפי ההנחיות הבאות: