From 50e377c44cb63ca36be49cbfec7a0e404fa36c87 Mon Sep 17 00:00:00 2001 From: Xuezhou Dai Date: Fri, 7 Jul 2023 22:42:14 +0800 Subject: [PATCH] Add idle sample (#961) * Add idle sample * Fix typo * Fix onStateChanged threshold * Add README.md * Update file structure * Remove tabs permission * Update api-samples/idle/README.md Co-authored-by: amysteamdev <37001393+AmySteam@users.noreply.github.com> * Update api-samples/idle/service-worker.js Co-authored-by: amysteamdev <37001393+AmySteam@users.noreply.github.com> * Update api-samples/idle/README.md Co-authored-by: amysteamdev <37001393+AmySteam@users.noreply.github.com> * Update api-samples/idle/README.md Co-authored-by: amysteamdev <37001393+AmySteam@users.noreply.github.com> --------- Co-authored-by: amysteamdev <37001393+AmySteam@users.noreply.github.com> --- api-samples/idle/README.md | 19 ++++++ api-samples/idle/history.html | 41 ++++++++++++ api-samples/idle/history.js | 102 +++++++++++++++++++++++++++++ api-samples/idle/manifest.json | 18 +++++ api-samples/idle/sample-128.png | Bin 0 -> 1156 bytes api-samples/idle/sample-16.png | Bin 0 -> 478 bytes api-samples/idle/sample-19.png | Bin 0 -> 130 bytes api-samples/idle/sample-48.png | Bin 0 -> 471 bytes api-samples/idle/service-worker.js | 27 ++++++++ 9 files changed, 207 insertions(+) create mode 100644 api-samples/idle/README.md create mode 100644 api-samples/idle/history.html create mode 100644 api-samples/idle/history.js create mode 100644 api-samples/idle/manifest.json create mode 100644 api-samples/idle/sample-128.png create mode 100644 api-samples/idle/sample-16.png create mode 100644 api-samples/idle/sample-19.png create mode 100644 api-samples/idle/sample-48.png create mode 100644 api-samples/idle/service-worker.js diff --git a/api-samples/idle/README.md b/api-samples/idle/README.md new file mode 100644 index 00000000..bf92ebb5 --- /dev/null +++ b/api-samples/idle/README.md @@ -0,0 +1,19 @@ +# chrome.idle + +This sample demonstrates how to use the [`chrome.idle`](https://developer.chrome.com/docs/extensions/reference/idle/) API. + +## Overview + +In this sample, the `chrome.idle` API detects and stores the history of the user's idle state. + +## Implementation Notes + +The detection interval of [`chrome.idle.onStateChanged`](https://developer.chrome.com/docs/extensions/reference/idle/#event-onStateChanged) event needs to be modified using the [`chrome.idle.setDetectionInterval`](https://developer.chrome.com/docs/extensions/reference/idle/#method-setDetectionInterval) method. + +The idle state history is stored in the [`chrome.storage.session`](https://developer.chrome.com/docs/extensions/reference/storage/#property-session) storage area. + +## Running this extension + +1. Clone this repository. +2. Load this directory in Chrome as an [unpacked extension](https://developer.chrome.com/docs/extensions/mv3/getstarted/development-basics/#load-unpacked). +3. Click the extension's action icon to open the window. diff --git a/api-samples/idle/history.html b/api-samples/idle/history.html new file mode 100644 index 00000000..ddabf029 --- /dev/null +++ b/api-samples/idle/history.html @@ -0,0 +1,41 @@ + + + + + + + +

Idle API Demonstration

+

Current state

+

+ Idle threshold: + +

+ +

+ chrome.idle.queryState(, + ...); + - + +

+

Last state change:

+ +

Idle changes:

+ + + + diff --git a/api-samples/idle/history.js b/api-samples/idle/history.js new file mode 100644 index 00000000..c2c41062 --- /dev/null +++ b/api-samples/idle/history.js @@ -0,0 +1,102 @@ +/** + * Convert a state and time into a nice styled chunk of HTML. + */ +function renderState(state, time) { + const now = Date.now(); + const diff = Math.round((time - now) / 1000); + const str = + diff == 0 + ? 'now' + : Math.abs(diff) + ' seconds ' + (diff > 0 ? 'from now' : 'ago'); + const col = state == 'active' ? '#009900' : '#990000'; + return "" + state + ' ' + str; +} + +/** + * Creates DOM and injects a rendered state into the page. + */ +function renderItem(state, time, parent) { + const dom_item = document.createElement('li'); + dom_item.innerHTML = renderState(state, time); + parent.appendChild(dom_item); +} + +// Store previous state so we can show deltas. This is important +// because the API currently doesn't fire idle messages, and we'd +// like to keep track of last time we went idle. +let laststate = null; +let laststatetime = null; + +/** + * Checks the current state of the browser. + */ +async function checkState() { + const threshold = parseInt(document.querySelector('#idle-threshold').value); + const dom_threshold = document.querySelector('#idle-set-threshold'); + dom_threshold.innerText = threshold; + + // Request the state based off of the user-supplied threshold. + chrome.idle.queryState(threshold, function (state) { + const time = new Date(); + if (laststate != state) { + laststate = state; + laststatetime = time; + } + + // Keep rendering results so we get a nice "seconds elapsed" view. + const dom_result = document.querySelector('#idle-state'); + dom_result.innerHTML = renderState(state, time); + const dom_laststate = document.querySelector('#idle-laststate'); + dom_laststate.innerHTML = renderState(laststate, laststatetime); + }); +} + +/** + * Render the data gathered by the background service worker - should show a log + * of "active" states. + */ +async function renderHistory() { + const dom_history = document.querySelector('#idle-history'); + dom_history.innerHTML = ''; + const { history_log } = await chrome.storage.session.get(['history_log']); + if (!history_log) { + return; + } + + for (let i = 0; i < history_log.length; i++) { + const data = history_log[i]; + renderItem(data['state'], data['time'], dom_history); + } +} + +document.addEventListener('DOMContentLoaded', async function () { + // Set the threshold to the last value the user set, or 15 if not set. + let { threshold: stored_threshold } = await chrome.storage.local.get([ + 'threshold' + ]); + if (!stored_threshold || ![15, 30, 60].includes(stored_threshold)) { + stored_threshold = 15; + } + + document.querySelector( + `#idle-threshold option[value="${stored_threshold}"]` + ).selected = true; + chrome.idle.setDetectionInterval(stored_threshold); + + document + .querySelector('#idle-threshold') + .addEventListener('change', function (e) { + const threshold = parseInt(e.target.value); + chrome.storage.local.set({ threshold: threshold }); + chrome.idle.setDetectionInterval(threshold); + }); + + // Check every second (even though this is overkill - minimum idle + // threshold is 15 seconds) so that the numbers appear to be counting up. + checkState(); + window.setInterval(checkState, 1000); + + // Check every second (see above). + renderHistory(); + window.setInterval(renderHistory, 1000); +}); diff --git a/api-samples/idle/manifest.json b/api-samples/idle/manifest.json new file mode 100644 index 00000000..cf777d0f --- /dev/null +++ b/api-samples/idle/manifest.json @@ -0,0 +1,18 @@ +{ + "name": "Idle - Simple Example", + "version": "1.0.1", + "description": "Demonstrates the Idle API", + "background": { + "service_worker": "service-worker.js" + }, + "permissions": ["idle", "storage"], + "action": { + "default_icon": "sample-19.png" + }, + "icons": { + "16": "sample-16.png", + "48": "sample-48.png", + "128": "sample-128.png" + }, + "manifest_version": 3 +} diff --git a/api-samples/idle/sample-128.png b/api-samples/idle/sample-128.png new file mode 100644 index 0000000000000000000000000000000000000000..96be429f16f8f35f483a9acb3d83be10ea95a6c9 GIT binary patch literal 1156 zcmV-~1bh35P)C0000pP)t-s|NsB| z@9)e20M#!q**`z>zrW&dZ_*zh`QP8q4-fUv&);8P?4O_Ie}CzZkK9jB@2{^af`x?u z00az4L_t(|UhSIMR_ibfMJ0KjlKlVI-i{ochCD^~!|i^ez~b!6$d*@1{}W%ne94X` zQjOdQKGGDjkvE=axYd{6dPbfx(AB737aFfI!gC2OQo85RiP3zh&ipr=sq%e&%yZ zJj5W%oo$~_Uc_h{Y;-5cjkM3ta$_M~gOusUa(D=syA{(215O9|1qIMI*yMrXUA`q6 zWKiTm+UN;C@>3*Ls=N%aIf<=I6x-9y5sf$K$C9ZA$k#V8{M?a;42IELk+3fOVGxVF zNsUPdE?q`%KwOKx;Wy3k9|YGM41mMI`0WQ8SlQv}Mj2v&YKP|PiW&?uKmp1lCfWmo zm6SmUmOe!oXitL<2Rs%;EWtp$=JlT)5`G(07^JMZ=bG&`{8GOPfgY?{`G^W4^4XSe zDK?7vPF@)5hzSc%97&HNPb~Gc{+s#+hI&};kTxfsZifkwIW+L&1o+Uvq7PMw%W6yI zKMgqP9sw<3+=oHkbwO2cJ`sOxP(^hGynu3Gz+U(I;RJlvz|~iM7Txh9_*J@horYaM z;(N-<;dfN-41&`g00X1$9|p;mjTC8E-<*yNG65}tNGApX-1VP}~plTVg5fEs+K|9&zHaRkgbQRcXOD%^66@`Jpa{c|cqTUAvsCren z4*KVxJTpicJ{Q0L{5%08of&kUSL?OqFLwb$bY{S+^G~rDF=3DgUObXWJLvp@_YMe* zHedq*;AFhP+V|Z^1N7Y_gM4PN(p&?>dZ|mRacAIq1I>JA(W zM>3f%B%c<5<%UDE_*mPfaOkwZe+q$!UmPsZ(2q_@AoQtAv(}a+z68X*dmz#E#Qs5x zYgT?JOzHW>R;}9e?;v+fb*{d`LCB?h9Z7*IC!{S2@T7u~6U>JEqIc~)p}BB@wF38LtbQb5|c(@vwnO~EXekw6&2xYYyJ24($4=7faI007-qR`9j8?4qLIU0u);66>6t(;*?{ ze0=Vzs_2S}>Xnu0l9Jnzla+-4006Q{L_t(|US-Hdjsrmy1kot2Mlj--Z`X{QjfV}mNl@r0AqkLdquI@iuGpB$^>@1cu3n;cRDPUVD_1oavf>4eFMfxf-ptYu^F)@=|aQ z);>OI;MrQ+7kE+X^-X|*QtDmcBZN;Yz%Yc63m8BEJODH38e_g>-|+Jb{JB|?f2fZR UNYon(^b07*qoM6N<$f;A`p#Q*>R literal 0 HcmV?d00001 diff --git a/api-samples/idle/sample-19.png b/api-samples/idle/sample-19.png new file mode 100644 index 0000000000000000000000000000000000000000..108b2a777f1a944e127eac186c45d8f004e547c0 GIT binary patch literal 130 zcmeAS@N?(olHy`uVBq!ia0vp^!XV7Z3?x0~E$IhRYymzYuK)l4Kf}OqwtP<%P{iER z#WAFUF}b0!iI26-Z$3+#n}Xh@r~?Q7W-&U8Ptcnf(y)5vll+8&rwI&69Jd8Mnm_($ cn_$8q@{8AV*&2fQphR^o zlCVWYx}T4oNVt=MCotM|chUZJ@Ss8y&O+(yH5$=Go^l8Bnj)JqWP#SI!Mi5FL9tS- z5;;Zen1P{df49_HF0yY*wbP3LdQ}C#0!2~Yj0Ygnix`;nCdr-trXQ<_38`j_{~iDU N002ovPDHLkV1jxB(|P~^ literal 0 HcmV?d00001 diff --git a/api-samples/idle/service-worker.js b/api-samples/idle/service-worker.js new file mode 100644 index 00000000..8c3473b8 --- /dev/null +++ b/api-samples/idle/service-worker.js @@ -0,0 +1,27 @@ +/** + * Stores a state every time it changes, up to 20 items. + */ +chrome.idle.onStateChanged.addListener(async function (newstate) { + let { history_log } = await chrome.storage.session.get(['history_log']); + if (!history_log) { + history_log = []; + } + const time = Date.now(); + if (history_log.length >= 20) { + history_log.pop(); + } + history_log.unshift({ state: newstate, time: time }); + chrome.storage.session.set({ history_log: history_log }); +}); + +/** + * Opens history.html when the browser action is clicked. + */ +chrome.action.onClicked.addListener(function () { + chrome.windows.create({ + url: 'history.html', + width: 700, + height: 600, + type: 'popup' + }); +});