1. 導入と設定
ウェブ機能
ウェブとネイティブの機能の差を埋め、デベロッパーがオープン ウェブ上で優れたエクスペリエンスを簡単に構築できるようにしたいと考えています。Google は、すべてのデベロッパーが優れたウェブ エクスペリエンスを実現するために必要な機能にアクセスできるようにすべきだと強く信じており、より高性能なウェブの実現に取り組んでいます。
ただし、ネイティブでは利用できるものの、ウェブでは利用できない機能(ファイル システムへのアクセスやアイドル状態の検出など)もあります。これらの機能が欠落しているため、一部の種類のアプリはウェブで配信できないか、有用性が低くなります。
Google は、既存のオープン ウェブ プラットフォームの標準プロセスを使用して、オープンで透明性の高い方法でこれらの新機能を設計、開発します。また、相互運用可能な設計を確保するため、設計の反復処理を行う際に、デベロッパーや他のブラウザ ベンダーから早期にフィードバックを得ます。
作成するアプリの概要
この Codelab では、新しく追加されたウェブ API や、フラグの背後でのみ利用可能なウェブ API をいくつか試してみます。そのため、この Codelab では、特定の最終製品の構築ではなく、API 自体と、これらの API が実現するユースケースに焦点を当てています。
学習内容
この Codelab では、最先端の API の基本的なメカニズムをいくつか学びます。なお、これらのメカニズムはまだ確定していません。デベロッパー フローに関するフィードバックをお待ちしております。
必要なもの
この Codelab で紹介する API は最先端の技術であるため、各 API の要件は異なります。各セクションの冒頭にある互換性情報をよくお読みください。
この Codelab の進め方
この Codelab は、必ずしも順番に進める必要はありません。各セクションは独立した API を表しているため、最も興味のあるものを自由に選択してください。
2. Badging API
Badging API の目的は、バックグラウンドで発生したことをユーザーに知らせることです。この Codelab のデモを簡単にするため、この API を使用して、フォアグラウンドで発生していることをユーザーに知らせることにします。その後、バックグラウンドで発生する事柄に頭の中で置き換えることができます。
Airhorner をインストールする
この API を機能させるには、ホーム画面にインストールされた PWA が必要です。そのため、まず、悪名高い airhorner.com などの PWA をインストールします。右上の [インストール] ボタンをクリックするか、その他メニューを使用して手動でインストールします。

確認を求めるメッセージが表示されるので、[インストール] をクリックします。

オペレーティング システムのドックに新しいアイコンが表示されます。クリックすると PWA が起動します。独自のアプリ ウィンドウを持ち、スタンドアロン モードで実行されます。
|
|
バッジの設定
PWA がインストールされたので、バッジに表示する数値データ(バッジには数値のみを含めることができます)が必要です。The Air Horner で簡単にカウントできるのは、ため息、ホーンが鳴らされた回数です。インストールした Airhorner アプリで、ホーンを鳴らしてバッジを確認します。ホーンを鳴らすたびにカウントが増えます。

その仕組みについて説明します。基本的に、コードは次のようになります。
let hornCounter = 0;
const horn = document.querySelector('.horn');
horn.addEventListener('click', () => {
navigator.setExperimentalAppBadge(++hornCounter);
});
エアホーンを数回鳴らし、PWA のアイコンを確認します。エアホーンが鳴るたびにアイコンが更新されます。簡単にできます。

バッジをクリアする
カウンタは 99 までカウントアップしてから、再び 0 からカウントを開始します。手動でリセットすることもできます。DevTools の [コンソール] タブを開き、次の行を貼り付けて Enter キーを押します。
navigator.setExperimentalAppBadge(0);
また、次のスニペットに示すように、バッジを明示的にクリアして削除することもできます。PWA のアイコンは、最初と同じように、バッジのないクリアなアイコンに戻ります。
navigator.clearExperimentalAppBadge();

フィードバック
この API についてどう思われましたか?簡単なアンケートにご協力ください。
この API は直感的に使用できましたか?
サンプルを実行できましたか?
他にご意見がございましたらお聞かせください。不足している機能はありましたか?こちらのアンケートにご記入のうえ、フィードバックをお送りくださいますようお願いいたします。ありがとうございました。
3. Native File System API
Native File System API を使用すると、デベロッパーはユーザーのローカル デバイス上のファイルを操作する強力なウェブアプリを構築できます。ユーザーがウェブアプリにアクセス権を付与すると、この API を使用して、ウェブアプリがユーザーのデバイス上のファイルやフォルダに対する変更を直接読み取ったり保存したりできるようになります。
ファイルを読み取る
Native File System API の「Hello, world」は、ローカル ファイルを読み取ってファイルの内容を取得することです。プレーンな .txt ファイルを作成し、テキストを入力します。次に、example.com などの安全なサイト(HTTPS で配信されるサイト)に移動し、DevTools コンソールを開きます。次のコード スニペットをコンソールに貼り付けます。Native File System API にはユーザー操作が必要なため、ドキュメントにダブルクリック ハンドラをアタッチします。ファイル ハンドルは後で必要になるため、グローバル変数にします。
document.ondblclick = async () => {
window.handle = await window.chooseFileSystemEntries();
const file = await handle.getFile();
document.body.textContent = await file.text();
};

次に、example.com ページの任意の場所をダブルクリックすると、ファイル選択ツールが表示されます。

以前に作成した .txt ファイルを選択します。ファイルの内容は、example.com の実際の body の内容に置き換えられます。

ファイルを保存する
次に、変更を加えます。そのため、次のコード スニペットを貼り付けて body を編集可能にします。これで、ブラウザをテキスト エディタのように使用してテキストを編集できます。
document.body.contentEditable = true;

次に、これらの変更を元のファイルに書き戻します。したがって、ファイル ハンドルにライターが必要です。これは、次のスニペットをコンソールに貼り付けることで取得できます。ここでもユーザー操作が必要なので、今回はメイン ドキュメントのクリックを待ちます。
document.onclick = async () => {
const writer = await handle.createWriter();
await writer.truncate(0);
await writer.write(0, document.body.textContent);
await writer.close();
};

ドキュメントをクリック(ダブルクリックではない)すると、権限のプロンプトが表示されます。権限を付与すると、ファイルの内容は以前に body で編集した内容になります。別のエディタでファイルを開いて変更を確認します(または、ドキュメントをもう一度ダブルクリックしてファイルを開き直すことで、プロセスをもう一度開始します)。


おめでとうございます!これで、世界最小のテキスト エディタ [citation needed] が作成されました。
フィードバック
この API についてどう思われましたか?簡単なアンケートにご協力ください。
この API は直感的に使用できましたか?
サンプルを実行できましたか?
他にご意見がございましたらお聞かせください。不足している機能はありましたか?こちらのアンケートにご記入のうえ、フィードバックをお送りくださいますようお願いいたします。ありがとうございました。
4. Shape Detection API
Shape Detection API は、高速化されたシェイプ検出器(人間の顔など)へのアクセスを提供し、静止画像やライブ画像フィードで動作します。オペレーティング システムには、Android の FaceDetector などのパフォーマンスが高く、高度に最適化された特徴検出器があります。Shape Detection API は、これらのネイティブ実装を開き、一連の JavaScript インターフェースを介して公開します。
現在サポートされている機能は、FaceDetector インターフェースによる顔検出、BarcodeDetector インターフェースによるバーコード検出、TextDetector インターフェースによるテキスト検出(光学式文字認識)です。
顔検出
Shape Detection API の魅力的な機能の 1 つが顔検出です。テストするには、顔のあるページが必要です。著者の顔写真が掲載されているこのページから始めるとよいでしょう。次のスクリーンショットのような画面が表示されます。サポートされているブラウザでは、顔の境界ボックスと顔のランドマークが認識されます。
Glitch プロジェクト(特に script.js ファイル)をリミックスまたは編集すると、この処理に必要なコードが少ないことがわかります。

著者の顔だけでなく、完全に動的なものにしたい場合は、非公開タブまたはゲストモードでこの Google 検索結果ページにアクセスしてください。そのページで、任意の場所を右クリックして [検証] をクリックし、Chrome デベロッパー ツールを開きます。次に、[コンソール] タブに次のスニペットを貼り付けます。このコードは、検出された顔を半透明の赤いボックスでハイライト表示します。
document.querySelectorAll('img[alt]:not([alt=""])').forEach(async (img) => {
try {
const faces = await new FaceDetector().detect(img);
faces.forEach(face => {
const div = document.createElement('div');
const box = face.boundingBox;
const computedStyle = getComputedStyle(img);
const [top, right, bottom, left] = [
computedStyle.marginTop,
computedStyle.marginRight,
computedStyle.marginBottom,
computedStyle.marginLeft
].map(m => parseInt(m, 10));
const scaleX = img.width / img.naturalWidth;
const scaleY = img.height / img.naturalHeight;
div.style.backgroundColor = 'rgba(255, 0, 0, 0.5)';
div.style.position = 'absolute';
div.style.top = `${scaleY * box.top + top}px`;
div.style.left = `${scaleX * box.left + left}px`;
div.style.width = `${scaleX * box.width}px`;
div.style.height = `${scaleY * box.height}px`;
img.before(div);
});
} catch(e) {
console.error(e);
}
});
DOMException メッセージが表示され、すべての画像が処理されていないことがわかります。これは、ファーストビューの画像はデータ URI としてインライン化されるためアクセスできますが、ファーストビュー以外の画像は CORS をサポートするように構成されていない別のドメインから取得されるためです。このデモでは、この点を考慮する必要はありません。
顔のランドマーク検出
macOS では、顔そのものだけでなく、顔のランドマークの検出もサポートしています。顔のランドマークの検出をテストするには、次のスニペットをコンソールに貼り付けます。リマインダー: crbug.com/914348 のため、ランドマークの並びは完璧ではありませんが、この機能の方向性と強力さを確認できます。
document.querySelectorAll('img[alt]:not([alt=""])').forEach(async (img) => {
try {
const faces = await new FaceDetector().detect(img);
faces.forEach(face => {
const div = document.createElement('div');
const box = face.boundingBox;
const computedStyle = getComputedStyle(img);
const [top, right, bottom, left] = [
computedStyle.marginTop,
computedStyle.marginRight,
computedStyle.marginBottom,
computedStyle.marginLeft
].map(m => parseInt(m, 10));
const scaleX = img.width / img.naturalWidth;
const scaleY = img.height / img.naturalHeight;
div.style.backgroundColor = 'rgba(255, 0, 0, 0.5)';
div.style.position = 'absolute';
div.style.top = `${scaleY * box.top + top}px`;
div.style.left = `${scaleX * box.left + left}px`;
div.style.width = `${scaleX * box.width}px`;
div.style.height = `${scaleY * box.height}px`;
img.before(div);
const landmarkSVG = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
landmarkSVG.style.position = 'absolute';
landmarkSVG.classList.add('landmarks');
landmarkSVG.setAttribute('viewBox', `0 0 ${img.width} ${img.height}`);
landmarkSVG.style.width = `${img.width}px`;
landmarkSVG.style.height = `${img.height}px`;
face.landmarks.map((landmark) => {
landmarkSVG.innerHTML += `<polygon class="landmark-${landmark.type}" points="${
landmark.locations.map((point) => {
return `${scaleX * point.x},${scaleY * point.y} `;
}).join(' ')
}" /></svg>`;
});
div.before(landmarkSVG);
});
} catch(e) {
console.error(e);
}
});
バーコード検出
Shape Detection API の 2 つ目の機能はバーコード検出です。以前と同様に、こちらのようなバーコードを含むページが必要です。ブラウザで開くと、さまざまな QR コードが解読された状態で表示されます。Glitch プロジェクト(特に script.js ファイル)をリミックスまたは編集して、その方法を確認してください。

より動的なものが必要な場合は、Google 画像検索を再び使用できます。今回は、ブラウザでシークレット タブまたはゲストモードでこの Google 検索結果ページに移動します。次に、以下のスニペットを Chrome DevTools の [コンソール] タブに貼り付けます。しばらくすると、認識されたバーコードに元の値とバーコード タイプが注釈として追加されます。
document.querySelectorAll('img[alt]:not([alt=""])').forEach(async (img) => {
try {
const barcodes = await new BarcodeDetector().detect(img);
barcodes.forEach(barcode => {
const div = document.createElement('div');
const box = barcode.boundingBox;
const computedStyle = getComputedStyle(img);
const [top, right, bottom, left] = [
computedStyle.marginTop,
computedStyle.marginRight,
computedStyle.marginBottom,
computedStyle.marginLeft
].map(m => parseInt(m, 10));
const scaleX = img.width / img.naturalWidth;
const scaleY = img.height / img.naturalHeight;
div.style.backgroundColor = 'rgba(255, 255, 255, 0.75)';
div.style.position = 'absolute';
div.style.top = `${scaleY * box.top + top}px`;
div.style.left = `${scaleX * box.left - left}px`;
div.style.width = `${scaleX * box.width}px`;
div.style.height = `${scaleY * box.height}px`;
div.style.color = 'black';
div.style.fontSize = '14px';
div.textContent = `${barcode.rawValue}`;
img.before(div);
});
} catch(e) {
console.error(e);
}
});
テキスト検出
Shape Detection API の最後の機能はテキスト検出です。手順はすでにご存じのとおりです。Google ブックスのスキャン結果を含むこのようなテキストを含む画像を含むページが必要です。対応ブラウザでは、認識されたテキストと、テキストの周囲に描画された境界ボックスが表示されます。Glitch プロジェクト(特に script.js ファイル)をリミックスまたは編集して、その方法を確認してください。

この機能を動的にテストするには、シークレット タブまたはゲストモードでこの検索結果ページにアクセスしてください。次に、以下のスニペットを Chrome DevTools の [コンソール] タブに貼り付けます。少し待つと、テキストの一部が認識されます。
document.querySelectorAll('img[alt]:not([alt=""])').forEach(async (img) => {
try {
const texts = await new TextDetector().detect(img);
texts.forEach(text => {
const div = document.createElement('div');
const box = text.boundingBox;
const computedStyle = getComputedStyle(img);
const [top, right, bottom, left] = [
computedStyle.marginTop,
computedStyle.marginRight,
computedStyle.marginBottom,
computedStyle.marginLeft
].map(m => parseInt(m, 10));
const scaleX = img.width / img.naturalWidth;
const scaleY = img.height / img.naturalHeight;
div.style.backgroundColor = 'rgba(255, 255, 255, 0.75)';
div.style.position = 'absolute';
div.style.top = `${scaleY * box.top + top}px`;
div.style.left = `${scaleX * box.left - left}px`;
div.style.width = `${scaleX * box.width}px`;
div.style.height = `${scaleY * box.height}px`;
div.style.color = 'black';
div.style.fontSize = '14px';
div.innerHTML = text.rawValue;
img.before(div);
});
} catch(e) {
console.error(e);
}
});
フィードバック
この API についてどう思われましたか?簡単なアンケートにご協力ください。
この API は直感的に使用できましたか?
サンプルを実行できましたか?
他にご意見がございましたらお聞かせください。不足している機能はありましたか?こちらのアンケートにご記入のうえ、フィードバックをお送りくださいますようお願いいたします。ありがとうございました。
5. Web Share Target API
Web Share Target API を使用すると、インストール済みのウェブアプリを共有ターゲットとして基盤となるオペレーティング システムに登録し、Web Share API またはオペレーティング システム レベルの共有ボタンなどのシステム イベントから共有コンテンツを受信できます。
共有先の PWA をインストールする
まず、共有できる PWA が必要です。今回は Airhorner は役に立ちませんが、Web Share Target デモアプリが役に立ちます。アプリをデバイスのホーム画面にインストールします。

PWA に何かを共有する
次に、Google フォトの写真など、共有するものを選択します。共有ボタンを使用して、共有先としてスクラップブック PWA を選択します。

アプリのアイコンをタップすると、スクラップブックの PWA に直接移動し、写真が表示されます。

その仕組みについて説明します。詳しくは、Scrapbook PWA のウェブアプリ マニフェストをご覧ください。Web Share Target API を機能させるための構成は、マニフェストの "share_target" プロパティにあります。このプロパティの "action" フィールドは、"params" に記載されているパラメータで装飾される URL を指しています。
共有側は、この URL テンプレートを適宜入力します(共有アクションを通じて行うか、デベロッパーが Web Share API を使用してプログラムで制御します)。これにより、受信側はパラメータを抽出して、表示などの処理を行うことができます。
{
"action": "/_share-target",
"enctype": "multipart/form-data",
"method": "POST",
"params": {
"files": [{
"name": "media",
"accept": ["audio/*", "image/*", "video/*"]
}]
}
}
フィードバック
この API についてどう思われましたか?簡単なアンケートにご協力ください。
この API は直感的に使用できましたか?
サンプルを実行できましたか?
他にご意見がございましたらお聞かせください。不足している機能はありましたか?こちらのアンケートにご記入のうえ、フィードバックをお送りくださいますようお願いいたします。ありがとうございました。
6. Wake Lock API
電池の消耗を防ぐため、ほとんどのデバイスはアイドル状態のままになっているとすぐにスリープ状態になります。ほとんどの場合は、これで問題ありませんが、アプリによっては処理を完了するために画面やデバイスをスリープ状態に移行しないようにしなければならないことがあります。Wake Lock API は、デバイスの画面が暗くなったりロックされたりするのを防ぐ方法や、デバイスがスリープ状態になるのを防ぐ方法を提供します。この機能により、これまでネイティブ アプリが必要だった新しいエクスペリエンスが可能になります。
スクリーンセーバーを設定する
Wake Lock API をテストするには、まずデバイスがスリープ状態になることを確認する必要があります。そのため、オペレーティング システムの環境設定ペインで、お好みのスクリーンセーバーを有効にして、1 分後に起動するように設定してください。デバイスをその時間だけ放置して、動作することを確認します(大変な作業です)。以下のスクリーンショットは macOS のものですが、モバイル Android デバイスやサポートされているデスクトップ プラットフォームでもお試しいただけます。

画面ウェイクロックを設定する
スクリーン セーバーが動作していることを確認したら、"screen" タイプのウェイクロックを使用して、スクリーン セーバーが動作しないようにします。Wake Lock デモアプリに移動し、[Activate ]
screen [Wake Lock] チェックボックス。

その瞬間から、ウェイクロックがアクティブになります。デバイスを 1 分間放置すると、スクリーンセーバーが起動していないことが確認できます。
では、その仕組みについて説明します。詳しくは、Wake Lock デモアプリの Glitch プロジェクトにアクセスして、script.js をご覧ください。コードの要約を以下のスニペットに示します。新しいタブを開き(または開いているタブを使用し)、Chrome デベロッパー ツールのコンソールに次のコードを貼り付けます。ウィンドウをクリックすると、10 秒間だけアクティブなウェイクロックが表示され(コンソールログを参照)、スクリーン セーバーは起動しません。
if ('wakeLock' in navigator && 'request' in navigator.wakeLock) {
let wakeLock = null;
const requestWakeLock = async () => {
try {
wakeLock = await navigator.wakeLock.request('screen');
wakeLock.addEventListener('release', () => {
console.log('Wake Lock was released');
});
console.log('Wake Lock is active');
} catch (e) {
console.error(`${e.name}, ${e.message}`);
}
};
requestWakeLock();
window.setTimeout(() => {
wakeLock.release();
}, 10 * 1000);
}

フィードバック
この API についてどう思われましたか?簡単なアンケートにご協力ください。
この API は直感的に使用できましたか?
サンプルを実行できましたか?
他にご意見がございましたらお聞かせください。不足している機能はありましたか?こちらのアンケートにご記入のうえ、フィードバックをお送りくださいますようお願いいたします。ありがとうございました。
7. Contact Picker API
特に注目している API は、Contact Picker API です。これにより、ウェブアプリはデバイスのネイティブの連絡先マネージャーから連絡先にアクセスできるようになり、ウェブアプリは連絡先の名前、メールアドレス、電話番号にアクセスできます。連絡先を 1 つだけ取得するか複数取得するか、すべてのフィールドを取得するか、名前、メールアドレス、電話番号のサブセットだけを取得するかを指定できます。
プライバシーへの配慮
ピッカーが開いたら、共有する連絡先を選択できます。[すべて選択] オプションはありませんが、これは意図的なものです。共有は意識的な決定であってほしいと考えています。同様に、アクセスは継続的ではなく、1 回限りの決定です。
連絡先へのアクセス
連絡先へのアクセスは簡単なタスクです。ピッカーが開く前に、必要なフィールド(name、email、telephone のいずれか)と、複数の連絡先にアクセスするか 1 つの連絡先のみにアクセスするかを指定できます。この API は、デモアプリを開くことで Android デバイスでテストできます。関連する ソースコードのセクションは、基本的に次のスニペットです。
getContactsButton.addEventListener('click', async () => {
const contacts = await navigator.contacts.select(
['name', 'email'],
{multiple: true});
if (!contacts.length) {
// No contacts were selected, or picker couldn't be opened.
return;
}
console.log(contacts);
});

8. Async Clipboard API
テキストのコピーと貼り付け
これまで、画像をシステム クリップボードにプログラムでコピーして貼り付ける方法はありませんでした。最近、Async Clipboard API に画像サポートを追加しました。
これで、画像をコピーして貼り付けることができるようになりました。新しい機能として、画像をクリップボードに書き込むこともできます。非同期クリップボード API は、テキストのコピーと貼り付けをしばらくの間サポートしてきました。navigator.clipboard.writeText() を呼び出してテキストをクリップボードにコピーし、後で navigator.clipboard.readText() を呼び出してテキストを貼り付けることができます。
画像のコピーと貼り付け
画像をクリップボードに書き込むこともできるようになりました。これを機能させるには、イメージデータを blob として取得し、クリップボード アイテム コンストラクタに渡す必要があります。最後に、navigator.clipboard.write() を呼び出して、このクリップボード アイテムをコピーできます。
// Copy: Writing image to the clipboard
try {
const imgURL = 'https://developers.google.com/web/updates/images/generic/file.png';
const data = await fetch(imgURL);
const blob = await data.blob();
await navigator.clipboard.write([
new ClipboardItem(Object.defineProperty({}, blob.type, {
value: blob,
enumerable: true
}))
]);
console.log('Image copied.');
} catch(e) {
console.error(e, e.message);
}
クリップボードから画像を貼り付けるのは複雑そうに見えますが、実際にはクリップボード アイテムから blob を取得するだけです。複数ある可能性があるため、目的のものが得られるまでループ処理する必要があります。セキュリティ上の理由から、現時点では PNG 画像のみがサポートされていますが、今後、より多くの画像形式がサポートされる可能性があります。
async function getClipboardContents() {
try {
const clipboardItems = await navigator.clipboard.read();
for (const clipboardItem of clipboardItems) {
try {
for (const type of clipboardItem.types) {
const blob = await clipboardItem.getType(type);
console.log(URL.createObjectURL(blob));
}
} catch (e) {
console.error(e, e.message);
}
}
} catch (e) {
console.error(e, e.message);
}
}
この API の動作は、デモアプリで確認できます。関連するソースコードのスニペットは上記に埋め込まれています。画像をクリップボードにコピーする操作は権限なしで行えますが、クリップボードから貼り付けるにはアクセス権を付与する必要があります。

アクセス権を付与すると、クリップボードから画像を読み取ってアプリケーションに貼り付けることができます。

9. お疲れさまでした。
お疲れさまでした。これでこの Codelab は終了です。繰り返しますが、ほとんどの API はまだ変更中であり、積極的に取り組んでいる最中です。そのため、チームは皆様からのフィードバックを非常に重視しています。皆様のようなユーザーとのやり取りを通じてのみ、これらの API を正しく実装できるからです。
また、機能のランディング ページも定期的にご確認ください。このページは常に最新の状態に保たれ、Google が取り組んでいる API の詳細な記事へのポインタがすべて含まれています。頑張ってください!
Tom と Capabilities チーム一同 🐡
