Upgrade to Pro — share decks privately, control downloads, hide ads and more …

DroidKaigi 2020 - Interactive Canvasを使ってGUIを持ったActions on Googleを作る

DroidKaigi 2020 - Interactive Canvasを使ってGUIを持ったActions on Googleを作る

DroidKaigi 2020 - Interactive Canvasを使ってGUIを持ったActions on Googleを作る(https://droidkaigi.jp/2020/timetable/156644
の登壇用資料です。
残念ながら、Covid19の影響でDroidKaigiが中止となり、登壇はできませんでしたが、録画のセッションとなったこちらの動画でこの資料を使っています。
https://www.youtube.com/watch?v=XdVH4YMlNxw

Takamitsu Mizutori

December 03, 2020
Tweet

More Decks by Takamitsu Mizutori

Other Decks in Programming

Transcript

  1. ࣗݾ঺հ
 ├── ਫௗܟຬ @mizutory ├── Goldrush Computing୅ද └── ϓϩάϥϚʔ ├──

    Android ├── iOS ├── Actions On Google ├── Vue.js └── Python
  2. 'use strict'; // Actions on Google client library͔ΒɺDialogflowϞδϡʔϧΛΠϯϙʔτ͢Δ const {dialogflow}

    = require('actions-on-google'); // firebase-functions packageΛΠϯϙʔτ͢Δ const functions = require('firebase-functions'); // Dialogflow clientΠϯελϯεΛ࡞Δ const app = dialogflow({debug: true}); // 'what_is_this'ͱ͍͏Intentͷϋϯυϥʔͷ࣮૷ app.intent('what_is_this', (conv, {animal, fish, any}) => { if (animal) { conv.close(animal + '͸ಈ෺Ͱ͢Ͷ'); }else if (fish) { conv.close(fish + '͸ڕͰ͢Ͷ'); }else { conv.close(any + '͸ಈ෺Ͱ΋ڕͰ΋͋Γ·ͤΜ'); } }); // DialogflowApp ΦϒδΣΫτΛHTTPS POSTϦΫΤετͷϋϯυϥʔͱͯ͠ొ࿥ exports.dialogflowFirebaseFulfillment = functions.https.onRequest(app); *OUFOUϋϯυϥʔͷྫ *OUFOU໊ ύϥϝʔλʔ໊ʢ&OUJUZ໊ʣ *OUFOUˠɹձ࿩ͷड͚ࡼɹˠɹϋϯυϥʔ &OUJUZˠɹநग़͍ͨ͠Ωʔϫʔυɹˠɹύϥϝʔλʔ
  3. parrot ├── firebase.json ├── functions │ ├── index.js │ ├──

    node_modules │ ├── package-lock.json │ └── package.json └── public ├── 404.html └── index.html functionsϑΥϧμ → Functions: node.jsͷweb APIΛ࡞Δػೳɹ→ɹWebhook publicϑΥϧμ → Hosting: html΍cssͳͲͷWebϖʔδͷΞηοτΛϗεςΟϯά͢Δػೳ -> Interactive Canvas
  4. // 'what_is_this'ͱ͍͏Intentͷϋϯυϥʔͷ࣮૷ app.intent('what_is_this', (conv, {animal, pass, any}) => { if

    (animal) { var result = animals.indexOf(animal); …ɹաڈʹಡΈ্͛ͨಈ෺͡Όͳ͍͔ͷνΣοΫ … //ࠓ౓͸AssistantͷಡΈ্͛Δಈ෺ΛϥϯμϜʹબͿ let newAnimal = getRandomMember(animals); …ɹաڈʹಡΈ্͛ͨಈ෺͡Όͳ͍͔ͷνΣοΫ … //Interactive CanvasʹରԠ͍ͯ͠Ε͹HtmlResponseΛฦ͢ if(hasInteractiveCanvas(conv)){ conv.ask(new HtmlResponse({ url: `https://${firebaseConfig.projectId}.firebaseapp.com/`, data: { scene: 'animal', name: newAnimal, picture: animalsPics[newAnimal] } })); } //Assistant͕ಈ෺Λ౴͑Δ conv.ask(newAnimal); }else { conv.close(any + '͸ಈ෺Ͱ͸͋Γ·ͤΜɻ͋ͳͨͷෛ͚Ͱ͢'); consumedAnimals.length = 0; } }); *OUFSBDUJWF$BOWBT༻ͷϋϯυϥʔ
  5. //Interactive CanvasʹରԠ͍ͯ͠Ε͹HtmlResponseΛฦ͢ if(hasInteractiveCanvas(conv)){ conv.ask(new HtmlResponse({ url: `https://$ {firebaseConfig.projectId}.firebaseapp.com/`, data: {

    scene: 'animal', name: newAnimal, picture: animalsPics[newAnimal] } })); } //Assistant͕ಈ෺Λ౴͑Δ conv.ask(newAnimal); )UNM3FTQPOTFΫϥεͷΦϒδΣΫτΛฦ͢ VSMϑϩϯτΤϯυ JOEFYIUNM ͕ஔ͍ͯ͋Δ৔ॴ EBUBϑϩϯτΤϯυʹૹΔσʔλ
  6. { "payload": { "google": { "expectUserResponse": true, "richResponse": { "items":

    [ { "htmlResponse": { "url": "https://parrot-9855a.firebaseapp.com/", "updatedState": { "scene": "animal", "name": "γϩΫϚ", "picture": "polarbear.jpg" } } }, { "simpleResponse": { "textToSpeech": "γϩΫϚ" } } ] } } } } (PPHMF/FTU)VCͰ͸͜ΕΛ ड͚औͬͯɺ ΢Σϒϖʔδͷϩʔυͱ ൃ࿩Λߦ͏ 8FCIPPL͔ΒͷϨεϙϯεΛݟͯΈΔ
  7. //Interactive CanvasʹରԠ͍ͯ͠Ε͹HtmlResponseΛฦ͢ if(hasInteractiveCanvas(conv)){ conv.ask(new HtmlResponse({ url: `https://${firebaseConfig.projectId}.firebaseapp.com/`, data: { scene:

    'animal', name: newAnimal, picture: animalsPics[newAnimal] } })); } function hasInteractiveCanvas(conv) { const hasInteractiveCanvas = ɹɹɹɹɹɹɹɹɹ conv.surface.capabilities.has('actions.capability.INTERACTIVE_CANVAS'); return hasInteractiveCanvas } ஫ʣσόΠε͕*OUFSBDUJWF$BOWBTͷػೳΛ࣋ͬͯ ͍Δͱ͖͚ͩɺ)UNM3FTQPOTFΛฦ͢Α͏ʹ͢Δ
  8. parrot ├── firebase.json ├── functions │ ├── index.js │ ├──

    node_modules │ ├── package-lock.json │ └── package.json └── public ├── 404.html └── index.html functionsϑΥϧμ → Functions: node.jsͷweb APIΛ࡞Δػೳɹ→ɹWebhook publicϑΥϧμ → Hosting: html΍cssͳͲͷWebϖʔδͷΞηοτΛϗεςΟϯά͢Δػೳ -> Interactive Canvas ͕͜͜ϩʔυ͞ΕΔ 8FCϖʔδ
  9. parrot ├── firebase.json ├── functions │ ├── animal.js │ ├──

    index.js │ ├── node_modules │ ├── package-lock.json │ └── package.json └── public ├── 404.html ├── css │ └── index.css ├── images │ ├── africaboar.jpg │ ├── africaelephant.jpg │ ├── akagitsune.jpg │ ├── alpaca.jpg │ └── zou.jpg ├── index.html └── js └── index.js 'JSFCBTF)PTUJOH޲͚ϑ ϩϯτΤϯυϓϩδΣΫτ ͷߏ଄
  10. JOEFYIUNM <!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <meta name="viewport"

    content="width=device-width, initial-scale=1" /> <title>[؆қ൛]ಈ෺͠ΓͱΓ</title> <link rel="shortcut icon" type="image/x-icon" href="data:image/x-icon;," /> <link rel="stylesheet" href="css/index.css" /> <script src="https://www.gstatic.com/assistant/interactivecanvas/api/ interactive_canvas.min.js"></script> </head> <body> <div class="container"> <div id="animal"> <div id="image_block"> <img id="animalimage" /> </div> <div id="name"> </div> </div> </div> <script src="js/index.js"></script> </body> </html>
  11. JOEFYKT window.onload = () => { interactiveCanvas.ready({ onUpdate(data) { onDataReceived(data)

    } }) } function onDataReceived(data){ if (data.scene === 'animal') { if(data.picture){ document.querySelector('#animalimage').src = `images/${data.picture}`; }else{ document.querySelector('#animalimage').src = `images/no_image.jpg`; } document.querySelector('#name').innerText = `${data.name}`; } } JOUFSBDUJWF$BOWBTʹSFBEZΠϕϯ τʹϦεφʔΛొ࿥͠ɺPO6QEBUF ίʔϧόοΫΛొ࿥͢Δ
  12. μϛʔσʔλΛ࢖ͬͯͷݕূJOEFYKT window.onload = () => { interactiveCanvas.ready({ onUpdate(data) { onDataReceived(data)

    } }) //Debug༻ onDataReceived({ scene: 'animal', picture: "alpaca.jpg", name: "ΞϧύΧ" }) } function onDataReceived(data){ if (data.scene === 'animal') { if(data.picture){ document.querySelector('#animalimage').src = `images/${data.picture}`; }else{ document.querySelector('#animalimage').src = `images/no_image.jpg`; } document.querySelector('#name').innerText = `${data.name}`; } } ϑϩϯτΤϯυίʔυͷσόοά
  13. JOEFYKTɿϔομྖҬͷ֬อ window.onload = () => { interactiveCanvas.ready({ onUpdate(data) { onDataReceived(data)

    } }) interactiveCanvas.getHeaderHeightPx().then((height) => { document.body.style.paddingTop = `${height}px`; }) }
  14. JOEFYIUNM <!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <meta name="viewport"

    content="width=device-width, initial-scale=1" /> <title>[؆қ൛]ಈ෺͠ΓͱΓ</title> <link rel="shortcut icon" type="image/x-icon" href="data:image/x-icon;," /> <link rel="stylesheet" href="css/index.css" /> <script src="https://www.gstatic.com/assistant/interactivecanvas/api/ interactive_canvas.min.js"></script> </head> <body> <div class="container"> <div id="animal"> <div id="image_block"> <img id="animalimage" /> </div> <div id="name"> </div> <button id="pass_button">PASS</button> </div> </div> <script src="js/index.js"></script> </body> </html>
  15. JOEFYKTɿϢʔβʔΠϯλϥΫγϣϯ window.onload = () => { interactiveCanvas.ready({ onUpdate(data) { onDataReceived(data)

    } }) document.querySelector('#pass_button').addEventListener('click', elem => { interactiveCanvas.sendTextQuery("ύε"); }); interactiveCanvas.getHeaderHeightPx().then((height) => { document.body.style.paddingTop = `${height}px`; }) } zύεzΫΤϦʔͷૹ৴
  16. // 'what_is_this'ͱ͍͏Intentͷϋϯυϥʔͷ࣮૷ app.intent('what_is_this', (conv, {animal, pass, any}) => { if

    (animal) { }else if(pass){ //AssistantͷಡΈ্͛Δಈ෺ΛϥϯμϜʹબͿ let newAnimal = getRandomMember(animals); //աڈʹಡΈ্͛ͨΞχϚϧΛௐ΂Δ //Interactive CanvasʹରԠ͍ͯ͠Ε͹HtmlResponseΛฦ͢ if(hasInteractiveCanvas(conv)){ conv.ask(new HtmlResponse({ url: `https://${firebaseConfig.projectId}.firebaseapp.com/`, data: { scene: 'animal', name: newAnimal, picture: animalsPics[newAnimal] } })); } //Assistant͕ಈ෺Λ౴͑Δ conv.ask(newAnimal); }else { conv.close(any + '͸ಈ෺Ͱ͸͋Γ·ͤΜɻ͋ͳͨͷෛ͚Ͱ͢'); consumedAnimals.length = 0; } });
  17. w "DUJPOTPO(PPHMF w J04ɺ"OESPJEΞϓϦ w 8FCɺϑϩϯτΤϯυʢ7VFKTʣ w 8FC"1*ɺόοΫΤϯυʢOPEFKT 1ZUIPO%KBOHP 

    w ػցֶश w *05ɺϩϘοτ "DUJPOʹؔ͢Δ࣭໰΍ˣˣˣͷ։ൃͷ૬ஊ͸ɺɺ NJ[VUPSJ!HPMESVTIDPNQVUJOHDPN ͪ͜Β·Ͱˠ ʮ%SPJE,BJHJΛݟͨʯͱݴͬͯ΋Β͑Δͱॿ͔Γ·͢ʂ !NJ[VUPSZ