Compare commits

..

202 Commits

Author SHA1 Message Date
Evgeny
6ff2737535 Add bible search script 2025-09-02 16:05:24 +08:00
Evgeny
fefa35c95d Small meta data change fix in the Time & Date widget 2025-08-02 14:07:17 +08:00
Evgeny
1fefd5791b Add Time & Date widget 2025-07-22 12:01:46 +08:00
Evgeny
bd02082021 Add info about exec and su ids 2025-07-16 08:46:41 +08:00
Evgeny
fd2ef77480 Remove google search app widget 2025-07-16 06:05:44 +08:00
Evgeny
0e81cb673a Update android widget dumper 2025-07-16 06:04:53 +08:00
Evgeny
51f0eac6ee Update google search app widget 2025-07-15 21:03:39 +08:00
Evgeny
4d3ee33c01 Add aio:colors() table description 2025-07-13 07:24:06 +08:00
Evgeny
74f0b60e97 Fix readme typo 2025-07-07 07:56:48 +08:00
Evgeny
c9caad2410 Add more tests 2025-07-06 07:38:55 +08:00
Evgeny
a897f1ed0d Cleanup 2025-07-05 07:43:25 +08:00
Evgeny
7fb84c38e7 Add habits widget 2025-07-05 07:38:55 +08:00
Evgeny
424eaca556 Add Recent apps widget 2025-06-29 07:16:41 +08:00
Evgeny
c42280333b Update samples 2025-06-27 08:21:14 +08:00
Evgeny
ff61bad9bf Fix typo 2025-06-26 13:39:04 +08:00
Evgeny
e744b0dc46 Fix typo 2025-06-26 12:10:25 +08:00
Evgeny
4b377577e5 Add thirukkural widget 2025-06-24 12:04:51 +08:00
Evgeny
7792fec8ac Update Profile Switcher script 2025-06-12 06:06:52 +08:00
Evgeny
a4d7b80950 Update README 2025-06-12 05:48:33 +08:00
Evgeny
bec53749db Update scripts.zip 2025-06-06 07:43:04 +08:00
Evgeny
bfd42cb2e7 Move some widgets from main to community 2025-06-06 07:42:43 +08:00
Evgeny
eb22ab2829 Replace deprecated functions in currency-ru-wdiget.lua 2025-06-06 07:40:54 +08:00
Evgeny
934cd29dff Update scripts.zip 2025-06-05 17:07:16 +08:00
Evgeny
2ab316efd6 Replace deprecated functions in many scripts 2025-06-05 17:05:34 +08:00
Evgeny
aa215947c9 Small README fix 2025-05-29 06:46:33 +08:00
Evgeny
9a9e21a53d Update scripts.zip 2025-05-20 14:44:18 +08:00
Evgeny
95d8ed99ee Small fix to Google translate search script 2025-05-20 14:43:54 +08:00
Evgeny
3b948cc4ca Update scripts.zip 2025-05-12 06:04:27 +08:00
Evgeny
3aff6de786 Cleanup ru scripts 2025-05-10 14:22:54 +08:00
Evgeny
26ebaa3020 Add system:tz() 2025-05-05 08:35:59 +08:00
Evgeny
d7cd1c7f26 Add tides script 2025-05-03 14:38:33 +08:00
Evgeny
6cdb5a9336 Add sample created by Cursor 2025-05-03 13:20:09 +08:00
Evgeny
82ce7e6eff Cleanup community scripts 2025-05-03 12:56:03 +08:00
Evgeny
d68ab0934f Cleanup main scripts 2025-05-03 10:14:22 +08:00
Evgeny
b9d73171ed Cleanup samples 2025-05-03 09:45:26 +08:00
Evgeny
296fbee028 Add tags field to the apps table 2025-05-03 07:56:35 +08:00
Evgeny
4904a0050a Add ui:show_image() 2025-04-29 06:59:41 +08:00
Evgeny
7074a17b83 Update notify module docs 2025-04-23 19:32:49 +08:00
Evgeny
cc4813730a Update scripts.zip 2025-04-16 07:40:07 +08:00
Evgeny
d5a1103002 Calendar menu: fix first load 2025-04-16 07:39:50 +08:00
Evgeny
e94808c35a Update scripts.zip 2025-04-01 20:11:36 +08:00
Evgeny
f2e28096c5 Update shell-widget.lua 2025-04-01 20:11:10 +08:00
Evgeny
605d4636d8 Add App Categries embed to docs 2025-03-26 08:38:26 +08:00
Evgeny
adfa6fad13 Add step-by-step tutorial 2025-02-20 07:53:57 +08:00
Evgeny
b5b701ce1c birthday widget: add expanded mode support 2025-01-20 18:32:31 +08:00
Evgeny
d1e6efa1d4 Add ui:set_expandable() function 2025-01-19 21:04:14 +08:00
Evgeny
fa1dc0ad31 Move meta widget to samples 2025-01-13 19:14:35 +08:00
Evgeny
79fe5e8e7b Update rich gui sample 2025-01-12 11:13:17 +08:00
Evgeny
3b033d6086 Update what to do script to new API endpoint 2025-01-12 09:06:43 +08:00
Evgeny
da7adad749 Update rich ui docs 2025-01-12 09:04:16 +08:00
Evgeny
dd8e3a8379 Add info about widgets:request_updates() second parameter 2025-01-10 11:12:02 +08:00
Evgeny
c416f29b97 Fix counter widget future day bug 2025-01-09 15:07:44 +08:00
Evgeny
0922b1f2b7 Add info and sample for set_edit_mode_button function 2025-01-08 14:13:29 +08:00
Evgeny
d2651f22cb Add info about new APIs 2024-12-08 09:36:10 +08:00
Evgeny
6107bc47ce Add Timed message widget #fix 2024-11-15 13:16:27 +07:00
Evgeny
41ab2191be Add Alquran script 2024-11-15 13:13:08 +07:00
Evgeny
a4960a9b60 Add Timed message widget 2024-11-15 13:10:11 +07:00
Evgeny
0069d1ed67 Update README 2024-11-14 16:58:40 +07:00
Evgeny
dbb0608176 Update scripts zip 2024-11-11 09:51:48 +07:00
Evgeny
d93c42b50a Fix Weekly Calendar backward compatibility 2024-11-11 09:51:35 +07:00
Evgeny
d1f1be14a1 Update scripts zip 2024-11-11 09:05:37 +07:00
Evgeny
f3100ec22c Update Monthly Calendar 2024-11-11 09:05:19 +07:00
Evgeny
e828444426 Add calendar:is_holiday() 2024-11-11 08:58:20 +07:00
Evgeny
b007f6b756 Add Perplexity search script 2024-11-11 08:54:52 +07:00
Evgeny
91f733c882 Update scripts zip 2024-10-31 20:09:41 +07:00
Evgeny
58d4d41ab3 Fix spelling 2024-10-31 20:03:38 +07:00
Evgeny
98afb5708e Add aio:add_todo() method 2024-10-31 10:05:43 +07:00
zobnin
2b3d6d9b90 Merge pull request #32 from dancek/electricity-price-widget-v1.1
Electricity spot price widget v1.1
2024-10-29 06:21:07 +03:00
Hannu Hartikainen
c12341915f Electricity spot price widget v1.1
The API used has changed the unit string. Update script to match. Also
since the defaults are for Finland, update VAT percentage as it changed
last month.
2024-10-28 13:07:06 +02:00
zobnin
75bbaffda8 Merge pull request #31 from meluskyc/master
Add calendar progress widget
2024-10-27 07:43:13 +03:00
meluskyc
7f3fac9034 Add calendar progress widget 2024-10-26 19:56:26 -04:00
Evgeny
d6e4e53da5 Add Profile dumper and Profile save/restore widgets 2024-09-21 10:27:33 +03:00
Evgeny
1580867d31 Update Google search widget 2024-09-21 10:26:52 +03:00
Evgeny
71bb976fb6 Merge branch 'master' of https://github.com/zobnin/aiolauncher_scripts 2024-09-19 15:24:48 +03:00
zobnin
9903c131ce Merge pull request #27 from fgombault/master
fix duplicate birthday entries
2024-09-19 14:45:35 +03:00
François Gombault
f7e5426ebe fix duplicate birthday entries 2024-09-19 11:44:45 +02:00
Evgeny
97947882df Subrise/Sunset widget: check location permission 2024-09-12 09:42:08 +03:00
Evgeny
11a8cf2ad6 Move dev widgets to the dedicated folder 2024-09-12 09:11:40 +03:00
Evgeny
d063132df2 Add profiles module docs and samples 2024-08-07 20:44:11 +04:00
Evgeny
b5840ee5bb Add Ai module 2024-07-12 09:18:48 +04:00
Evgeny
c4b0969a9d Update Drawer Sample #4 2024-07-12 09:11:32 +04:00
Evgeny
c58dba3173 Update scripts zip 2024-07-05 12:12:22 +04:00
Evgeny
d63f93d39d Move Quotes widget to defunct 2024-07-05 12:11:16 +04:00
Evgeny
64128b5f66 Update ticktcick widget 2024-07-02 15:11:57 +04:00
Evgeny
6ccc1593dc Add TickTick widget 2024-05-28 10:14:03 +04:00
Evgeny
7fdfecc983 Update counter widget to 2.0 2024-05-28 10:13:45 +04:00
Evgeny
b2e60d725b Update README 2024-05-27 17:47:24 +04:00
Evgeny
d5101ca34b Update utils.lua as of AIO Launcher 5.3.1 2024-05-27 16:40:17 +04:00
Evgeny
42d4d4a104 Add another Rich UI sample 2024-05-27 16:35:12 +04:00
Evgeny
ad9af4c2b6 Add prefs sample for the side menu 2024-05-21 17:50:55 +04:00
Evgeny
4e930db750 README: add note about html formatting 2024-05-19 17:22:53 +04:00
Evgeny
acb73cc4de Remove bitcoin from builder sample 2024-05-19 17:18:51 +04:00
Evgeny
430379546c Add info about notifications to README 2024-05-18 13:51:38 +04:00
Evgeny
42473099eb Update some scripts 2024-05-16 15:36:24 +04:00
Evgeny
73b85a1a5c Add settings to Electricity spot price widget 2024-05-16 15:32:08 +04:00
Evgeny
9ab0ab3934 Add on_settings to scripts without it 2024-05-16 15:27:31 +04:00
Evgeny
7d6b925802 Add info and sample for SVG icons and prefs edit dialog #fix 2024-05-16 14:25:40 +04:00
Evgeny
1c2ab3341a Add info and sample for SVG icons and prefs edit dialog 2024-05-16 14:21:05 +04:00
Evgeny
7fb9d43cea Merge branch 'master' of https://github.com/zobnin/aiolauncher_scripts 2024-05-06 18:08:46 +04:00
zobnin
bb6a0a8104 Merge pull request #24 from dancek/electricity-price-widget
Add Electricity spot price widget
2024-05-06 18:08:27 +04:00
Hannu Hartikainen
b83fb1e1bb Add Electricity spot price widget
This widget supports showing electricity spot price for many areas in
Europe. However, there's no configuration UI yet. I'm hoping there will
be native support for editing the values in `prefs` later, but I'm also
open to implementing the configuration UI in the widget if I get some
help.

The chart would be better if displayed with timestamps, but that doesn't
currently work when the timestamps span two days.
2024-05-06 13:45:23 +03:00
Evgeny
969c13fba1 Fixes in samples 2024-05-06 12:30:05 +04:00
Evgeny
bcf5484f49 Update Amdroid Buttons widget 2024-05-06 12:29:43 +04:00
Evgeny
36f4e4856b Add Amdroid Buttons widget 2024-05-05 18:32:01 +04:00
Evgeny
f90e811498 Update Birthdays widget 2024-05-05 16:25:47 +04:00
Evgeny
029781b7c2 Update README: add links 2024-05-05 16:25:27 +04:00
Evgeny
4f4a82993c Update google translate widget 2024-05-02 17:07:15 +04:00
Evgeny
9e88620428 Update scripts zip 2024-04-30 12:56:25 +04:00
Evgeny
721413cd52 Add Solar Cycle and Uptimerobot 2 widgets 2024-04-30 12:30:44 +04:00
Evgeny
71793eb3d3 Fix sunrise/sunset widget 2024-04-30 12:22:16 +04:00
Evgeny
eb118bf32a Move useless scripts to samples 2024-04-28 21:07:43 +04:00
Evgeny
28e0e9baf5 1. Add new APIs to README and samples. 2. Added Birthdays widget 2024-04-27 08:54:40 +04:00
Evgeny
195d205a93 Update rich ui README to reflect 5.2.2 changes 2024-04-23 08:11:47 +04:00
Evgeny
eea5047a2a Update Google search and Quick actions scripts 2024-04-21 08:50:22 +04:00
Evgeny
5eea6d8b91 Move useless scripts to samples folder 2024-04-18 14:47:18 +04:00
Evgeny
3242d9c24f Update Rich GUI sample 2024-04-18 14:42:11 +04:00
Evgeny
3cbe02774f README: add info about ui:set_rpogress() 2024-04-18 14:41:45 +04:00
Evgeny
025032cbe9 Fix error in README 2024-04-16 08:39:18 +04:00
Evgeny
a94a44c43d update amdroid widget 2024-04-14 15:47:42 +04:00
Evgeny
076af78f83 Update scripts zip 2024-04-10 20:02:27 +04:00
Evgeny
7b98d932b2 [calendar-menu] use calendar colors instead of event colors 2024-04-10 20:01:46 +04:00
Evgeny
c1e934c7a1 Update scripts zip 2024-04-09 15:40:56 +04:00
Evgeny
df54b56f50 Move Kodi remote and Uptimerobot scripts out of the main repo 2024-04-09 15:40:28 +04:00
Evgeny
c5fe9fb575 Move Widgets switcher out of main repo 2024-04-09 12:00:49 +04:00
Evgeny
4a8c56de95 Update Widget Switcher script 2024-04-09 11:37:27 +04:00
Evgeny
6d713a3cc0 Update scripts zip 2024-04-09 11:07:31 +04:00
Evgeny
dec11bf971 Update Widget Switcher script 2024-04-09 10:47:57 +04:00
Evgeny
df9354937b Update zip 2024-04-09 10:40:27 +04:00
Evgeny
d2c81220dc Update Widget Switcher script 2024-04-09 10:39:54 +04:00
Evgeny
3182eb0112 Add clarification about on_settings callback 2024-04-08 15:25:04 +04:00
Evgeny
3917c09837 Update google search widget 2024-04-01 08:53:59 +04:00
Evgeny
897fee33b7 Add complex UI docs and samples #2: add color to the text 2024-03-31 08:15:15 +04:00
Evgeny
db7149229a Add complex UI docs and samples 2024-03-30 17:57:49 +04:00
Evgeny
e563c5cbeb move classic todoist widget to defunct folder 2024-03-24 19:39:54 +04:00
Evgeny
91993ae4a6 Add new scripts from Theodor Galanis 2024-03-22 22:02:45 +04:00
Evgeny
d8d9c12e07 Update app widgets samples and community scripts 2024-03-19 14:50:28 +04:00
Evgeny
83150d0654 Add CHANGELOG.md file 2024-03-15 08:13:09 +04:00
Evgeny
adcdedea37 Add 'widgets' module to intercat with Android widgets 2024-03-15 08:07:34 +04:00
Evgeny
87c481a558 Update README 2024-02-21 08:53:32 +04:00
Evgeny
ce9bc66b71 Add jepardy widget sample 2024-02-21 08:53:20 +04:00
Evgeny
8919997106 mv chatgpt script to defunct 2024-01-05 09:24:16 +04:00
Evgeny
36ed0c7288 Update README 2023-11-24 09:05:13 +04:00
Evgeny
e3aaf8b6f1 Update quick actions script 2023-11-19 13:25:02 +04:00
Evgeny
b563f67867 Add ChatGPT search script 2023-11-19 13:24:24 +04:00
Evgeny
fe13d7d708 Update Quick Actions script 2023-11-10 17:17:06 +04:00
Evgeny
b3b57e12fa Move conversations widget to the samples 2023-10-26 18:45:47 +04:00
Evgeny
ad79c9a7d6 Update README 2023-10-18 09:08:27 +04:00
Evgeny
015252323a Add sample 2023-10-18 09:02:16 +04:00
Evgeny
b202daa621 Move startpage script to defunct 2023-10-01 17:24:53 +04:00
Evgeny
8570afbefa Add click listener to Country search script 2023-10-01 16:58:14 +04:00
Evgeny
469b2baf38 Small fix 2023-09-21 15:56:41 +04:00
Evgeny
ac30702679 Update zip 2023-09-20 19:42:08 +04:00
Evgeny
6fc6103576 Contacts menu bug fix 2023-09-20 19:41:19 +04:00
Evgeny
6e9f34809c Update changelog 2023-09-20 19:19:43 +04:00
Evgeny
786f752143 Update zip 2023-09-20 19:13:15 +04:00
Evgeny
52e27e22a0 Change contacts and apps modules APIs 2023-09-20 19:12:53 +04:00
Evgeny
bc003712ff Move sunrise/sunset script to the main folder 2023-09-20 13:27:33 +04:00
Evgeny
da6e0435d5 Contacts menu script: do not load real icons (slow) 2023-09-20 12:56:37 +04:00
Evgeny
7868273f74 Add script to remove all scripts from device 2023-09-12 15:52:28 +04:00
Evgeny
de23ba41de Various fixes to support new AIO version 2023-09-11 11:36:09 +04:00
Evgeny
5ad5be396f Add loclized date to the calendar-menu.lua script 2023-09-08 08:25:27 +04:00
Evgeny
c8f0e8be79 remove chart-sample2.lua 2023-08-17 19:51:24 +04:00
Evgeny
23aa58c76d Move Chuck Norris jokes to defunct 2023-08-17 18:58:02 +04:00
Evgeny
6a48001a9e Update zip 2023-07-26 11:26:32 +04:00
Evgeny
7151dc73a0 1. Small fixes to main widget. 2. Covid widget removed as defunct. 2023-07-26 11:26:18 +04:00
Evgeny
029cc39c7f README: add new APIs 2023-07-21 18:11:34 +04:00
Evgeny
82526addad Update zip 2023-07-21 15:44:03 +04:00
Evgeny
aa131b2e09 Small fixes 2023-07-21 15:43:41 +04:00
Evgeny
878c96ec88 Add long click action to the tasks-menu.lua 2023-07-19 19:15:05 +04:00
Evgeny
d098a0f84d Optimize default drawer scripts 2023-07-19 14:00:35 +04:00
Evgeny
43328239c9 Some README fixes 2023-07-16 09:54:01 +04:00
Evgeny
fab31ee2ee Add side menu scripts to the README header 2023-07-15 18:20:51 +04:00
Evgeny
66ee4ef452 Update zip 2023-07-15 17:43:02 +04:00
Evgeny
3806581020 Fix Widgets switcher widget 2023-07-15 17:41:29 +04:00
Evgeny
cdb9b7aec6 Fix README 2023-07-15 16:57:11 +04:00
Evgeny
081d8c41a1 Reflect latest API changes in the README 2023-07-15 16:51:44 +04:00
Evgeny
b35fd3292c Update utils 2023-07-15 15:39:20 +04:00
Evgeny
7aa368daca add permission checks to main widgets 2023-07-15 13:51:26 +04:00
Evgeny
73dce8ba03 Remove twitter widget from Widgets switcher 2023-07-14 21:20:08 +04:00
Evgeny
92f6e0de16 Add new drawer scripts 2023-07-14 21:18:05 +04:00
Evgeny
082df0cd09 update zip 2023-06-30 13:09:46 +04:00
Evgeny
55aad881e2 Update inspiration quotes widget 2023-06-30 13:09:07 +04:00
Evgeny
ae15d0e730 Update utils.lua 2023-06-30 13:00:26 +04:00
Evgeny
c02e945309 Add url widget 2023-06-14 20:02:27 +04:00
Evgeny
d66a798a99 Add monitor-lite widget 2023-06-13 11:12:21 +04:00
Evgeny
a1644fe137 Add armenian alphabet widget 2023-05-25 09:16:53 +04:00
Evgeny
e872af804e Add prefs module 2023-05-20 09:28:35 +04:00
Evgeny
43856c1fc1 Add prefs module 2023-05-20 09:25:39 +04:00
Evgeny
c7a096d011 Add meta widget script 2023-05-20 09:19:00 +04:00
Evgeny
291d3ef446 fix README 2023-04-18 15:13:44 +04:00
Evgeny
b94901d741 Add calendar event status and color to README 2023-04-12 12:42:32 +04:00
Evgeny
f2cef5d080 move horoscope widget to defunct (API is not working anymore) 2023-04-03 10:20:31 +04:00
Evgeny
706b3bec25 add search settings sample 2023-03-29 13:54:44 +04:00
Evgeny
f47bc68a39 update to fontawesome 6.3.0 2023-03-21 17:19:38 +04:00
Evgeny
2c6d5ffb28 Update scripts.zip 2023-03-16 10:31:13 +04:00
Evgeny
5f888e9c3e Add health widget to the Widgets switcher script 2023-03-16 10:30:54 +04:00
Evgeny
0f088a0708 Update scripts.zip 2023-03-15 09:26:44 +04:00
Evgeny
d176696fc9 Move calenadar search script to communty folder 2023-03-15 09:26:11 +04:00
Evgeny
cadc5aace7 add country search script 2023-03-06 18:55:35 +04:00
Evgeny
57e0c5c790 Add new APIs 2023-02-23 13:23:39 +04:00
Evgeny
75057f8c42 Add new APIs 2023-02-23 13:20:34 +04:00
Evgeny
4dc9f05907 update scripts 2023-01-31 11:01:33 +04:00
189 changed files with 16237 additions and 1428 deletions

100
CHANGELOG.md Normal file
View File

@@ -0,0 +1,100 @@
### 5.5.4
* Added `icon` meta tag
* Added `private_mode` meta tag
* Added `calendar:enabled_calendar_ids()` method
### 5.5.1
* Added `calendar:is_holiday()` method
### 5.5.0
* Added `aio:add_todo()` method
### 5.3.5
* Added `ai` module
### 5.3.1
* Added `string:trim()`, `string:starts_with()` and `string:ends_with()` methods
### 5.3.0
* Added `prefs:show_dialog` method
* Added `system:show_notify()` and `system:cancel_notify()` methods
* Added support for SVG icons to the Rich UI API
### 5.2.3
* Added `on_load()` callback
* Added `on_resume_when_folding` meta tag
### 5.2.1
* Added support for complex UIs
* Added `ui:set_progress()` function
### 5.2.0
* Added `widgets` module to app widgets interaction
### 5.1.0
* Added `add_purchase` action
* Added `on_contacts_loaded()` callback
### 4.9.4
* The `aio:actions()` function now also returns arguments format for each action
* Added function `system:format_date_localized()`
### 4.9.2
* The `apps` module now has an `app()` function that returns selected app table
* You can use `%%fa:ICON_NAME%%` tag in the any text to show FontAwesome icon inside the text
### 4.9.0
* The `apps` module now has an `apps()` function that returns a table with app details, including the app icon;
* The `phone:contacts()` function now returns an icon that can be used in the side menu;
* The `apps:request_icons()` and `phone:request_icons()` functions are deprecated.
### 4.8.0
* Side menu scripts support;
* New modules: `tasks` and `notes`;
* The `ui:colors()` can be called as `aio:colors()`;
* The `apps` and `phone` modules now returns icons;
* The `apps` modules now returns also Android for Work and cloned apps;
* The `apps` module now allows you to sort apps by category;
* The `phone` and `calendar` modules now have functions for requesting access rights;
* Added `phone:open_contact() function;
* Added `aio:actions()` function that returns a table of AIO Launcher actions;
* Added `calendar:open_new_event()` function that shows the system calendar with the new event.
* Added `aio:settings()` and `aio:open_settings()` functions
### Older versions
* 4.0.0 - first version with scripts support;
* 4.1.0 - added `weather` and `cloud` modules;
* 4.1.3 - added `notify`, `files` and `utils` modules;
* 4.1.5 - extended `notify` module, added `folded_string` arg to `ui:show_lines`;
* 4.3.0 - search scripts support;
* 4.4.0 - markdown support;
* 4.4.1 - rich text editor support;
* 4.4.2 - added `fmt` and `html` utility modules;
* 4.4.4 - added `tasker` module;
* 4.4.6 - added `csv` module;
* 4.4.7 - added `intent` module;
* 4.5.0 - the `aio` module has been significantly expanded, also added `system:currency()` and `ui:show_list_dialog()`;
* 4.5.2 - added `anim` and `morph` packages, added `calendar:open_event()` function;
* 4.5.3 - removed get_ prefixes where they are not needed while maintaining backward compatibility;
* 4.5.5 - added `on_action` callback and `calendar:add_event` function;
* 4.5.6 - `aio:active_widgets()` now returns also widget `label`, added `checks` module;
* 4.5.7 - added "fold" and "unfold" actions to the `on_action` callback;
* 4.6.0 - added `system:request_location()` and `tasker:send_command()` functions;
* 4.7.0 - added `ui:build()` and functions to display search results in different formats;
* 4.7.1 - fontawesome updated to version 6.3.0;
* 4.7.4 - added `prefs` module, `settings` module is deprecated.

602
README.md
View File

@@ -2,63 +2,80 @@
Starting from version 4.0, AIO Launcher supports scripts written in the [Lua scripting language](https://en.wikipedia.org/wiki/Lua_(programming_language)). Scripts should be placed in the directory `/sdcard/Android/data/ru.execbit.aiolauncher/files/`. Starting from version 4.0, AIO Launcher supports scripts written in the [Lua scripting language](https://en.wikipedia.org/wiki/Lua_(programming_language)). Scripts should be placed in the directory `/sdcard/Android/data/ru.execbit.aiolauncher/files/`.
There are two types of scripts: There are three types of scripts:
* _Widget scripts_, which can be added to the desktop using the side menu. * _Widget scripts_, which can be added to the desktop using the side menu.
* _Search scripts_ that add results to the search box. These can be enabled in the settings. * _Search scripts_ that add results to the search box. These can be enabled in the settings.
* _Side menu scripts_ that change the side menu.
The type of script is determined by the line (meta tag) at the beginning of the file: The type of script is determined by the line (meta tag) at the beginning of the file:
* `-- type = "widget"` * `-- type = "widget"`
* `-- type = "search"` * `-- type = "search"`
* `-- type = "drawer"`
*Read more about meta tags at the end of the document.* *Read more about meta tags at the end of the document.*
# Links
* [Step-by-step guide to writing scripts for AIO Launcher](README_INTRO.md)
* [AIO Scripting Telegram Group](https://t.me/aio_scripting)
* [AIO Script Store app](https://play.google.com/store/apps/details?id=ru.execbit.aiostore)
* [Lua Guide](https://www.lua.org/pil/contents.html)
* [Many Lua samples](http://lua-users.org/wiki/SampleCode)
# Changelog # Changelog
* 4.0.0 - first version with scripts support; ### 5.7.1
* 4.1.0 - added `weather` and `cloud` modules;
* 4.1.3 - added `notify`, `files` and `utils` modules; * Added `tags` field to the app table
* 4.1.5 - extended `notify` module, added `folded_string` arg to `ui:show_lines`; * Added `system:tz()` method
* 4.3.0 - search scripts support;
* 4.4.0 - markdown support; ### 5.7.0
* 4.4.1 - rich text editor support;
* 4.4.2 - added `fmt` and `html` utility modules; * Added `ui:show_image(uri)` method
* 4.4.4 - added `tasker` module; * Many changes in the `notify` module
* 4.4.6 - added `csv` module;
* 4.4.7 - added `intent` module; ### 5.6.1
* 4.5.0 - the `aio` module has been significantly expanded, also added `system:currency()` and `ui:show_list_dialog()`;
* 4.5.2 - added `anim` and `morph` packages, added `calendar:open_event()` function; * Added `ui:set_expandable()` and `ui:is_expanded()` methods
* 4.5.3 - removed get_ prefixes where they are not needed while maintaining backward compatibility;
* 4.5.5 - added `on_action` callback and `calendar:add_event` function; ### 5.6.0
* 4.5.6 - `aio:active_widgets()` now returns also widget `label`, added `checks` module;
* 4.5.7 - added "fold" and "unfold" actions to the `on_action` callback; * Added `ui:set_edit_mode_buttons()` method
* 4.6.0 - added `system:request_location()` and `tasker:send_command()` functions. * Added size argument to `widgets:request_updates()` method
[Full changelog](CHANGELOG.md)
# Widget scripts # Widget scripts
The work of the widget script begins with one of the three described functions. Main work should be done in one of them. The work of the widget script begins with one of the three described functions. Main work should be done in one of them.
* `on_load()` - called on first script load (_starting with AIO Launcher 5.2.3_).
* `on_resume()` - called every time you return to the desktop. * `on_resume()` - called every time you return to the desktop.
* `on_alarm()` - called every time you return to the desktop, but no more than once every 30 minutes. * `on_alarm()` - called every time you return to the desktop, but no more than once every 30 minutes.
* `on_tick(ticks)` - called every second while the launcher is on the screen. The `ticks` parameter is number of seconds after last return to the launcher. * `on_tick(ticks)` - called every second while the launcher is on the screen. The `ticks` parameter is number of seconds after last return to the launcher.
The `on_resume()` and `on_alarm()` callbacks are also triggered when a widget is added to the screen and the screen is forced to refresh. The `on_resume()` and `on_alarm()` callbacks are also triggered when a widget is added to the screen (if `on_load()` is not defined) and the screen is forced to refresh.
For most network scripts `on_alarm()` should be used. For most network scripts `on_alarm()` should be used.
# Search scripts # Search scripts
Unlike widget scripts, search scripts are launched only when you open the search window. Then the following function is triggered each time a character is entered: Unlike widget scripts, search scripts are launched only when you open the search window:
* `on_load()` - called every time when user opens the search window (_starting with AIO Launcher 5.2.3_).
Then the following function is triggered each time a character is entered:
* `on_search(string)` is run when each character is entered, `string` - entered string. * `on_search(string)` is run when each character is entered, `string` - entered string.
The search script can use two functions to display search results: The search script can use two functions to display search results:
* `search:show(strings, [colors])` - shows results BELOW all other results; * `search:show_buttons(names, [colors], [top])` - show buttons in the search results, the first parameter is table of button names, second - table of button colors in format `#XXXXXX`, `top` - whether the results need to be shown at the top (false by default);
* `search:show_top(strings, [colors])` - shows results BEFORE all other results. * `search:show_lines(lines, [colors], [top])` - show text lines in the search results;
* `search:show_progress(names, progresses, [colors], [top])` - show progress bars in the search results, the first parameter is a table of names, the second is a table of progress bars (from 1 to 100);
Both functions take a table with search results strings as the first argument. Second optional parameter: table with colors of results in format `#XXXXXX`. * `search:show_chart(points, format, [title], [show_grid], [top])` - show chart in the search results, parameters are analogous to `ui:show_chart()`.
When user click on a result, one of the following functions will be executed: When user click on a result, one of the following functions will be executed:
@@ -75,33 +92,78 @@ If you want the script to respond only to search queries that have a word in the
If you put such a tag at the beginning of your script, its `on_search()` function will only be called if a user types something like "youtube funny video" or "yt funny video". The prefix itself will be removed before being passed to the function. If you put such a tag at the beginning of your script, its `on_search()` function will only be called if a user types something like "youtube funny video" or "yt funny video". The prefix itself will be removed before being passed to the function.
# Side menu scripts
With side menu scripts, you can display your own list in the menu. The script can be selected by pulling the menu down.
The script starts with the following function:
* `on_drawer_open()`
In this function, you must prepare a list and display it using one of the following functions:
* `drawer:show_list(lines, [icons], [badges], [show_alphabet])` - shows lines, optionally you can specify a table of icons (in `fa:icon_name` format), a table of lines to be displayed in badges and pass a boolean value: whether to show the alphabet;
* `drawer:show_ext_list(lines, [max_lines)` - shows multiline lines, optionally you can specify the maximum number of lines of each element (default is 5).
The following functions are also available:
* `drawer:add_buttons(icons, default_index)` - shows icons at the bottom of the menu (in `fa:icon_name` format), `default_index` is the index of the selected icon;
* `drawer:clear()` - clears the list;
* `drawer:close()` - closes the menu;
* `drawer:change_view(name)` - switches the menu to another script or display style (argument: either script file name, `sortable` or `categories`).
Clicking on list items will call `on_click(index)`, long-clicking will call `on_long_click(index)`, clicking a bottom icon will call `on_button_click(index)`.
The list output functions support HTML and Markdown (see User Interface section for details).
# API Reference # API Reference
## User Interface ## User Interface
_Available only in widget scripts._ _Available only in widget scripts._
_AIO Launcher also offers a way to create more complex UIs: [instructions](README_RICH_UI.md)_
* `ui:show_text(string)` - displays plain text in widget, repeated call will erase previous text; * `ui:show_text(string)` - displays plain text in widget, repeated call will erase previous text;
* `ui:show_lines(table, [table], [folded_string])` - displays a list of lines with the sender (in the manner of a mail widget), the second argument (optional) - the corresponding senders (formatting in the style of a mail widget), folded\_string (optional) - string to be shown in folded mode; * `ui:show_lines(table, [table], [folded_string])` - displays a list of lines with the sender (in the manner of a mail widget), the second argument (optional) - the corresponding senders (formatting in the style of a mail widget), folded\_string (optional) - string to be shown in folded mode;
* `ui:show_table(table, [main_column], [centering], [folded_value])` - displays table, first argument: table of tables, second argument: main column, it will be stretched, occupying main table space (if argument is zero or not specified all table elements will be stretched evenly), third argument: boolean value indicating whether table cells should be centered, fourth argument: string or table to be shown in folded mode; * `ui:show_table(table, [main_column], [centering], [folded_value])` - displays table, first argument: table of tables, second argument: main column, it will be stretched, occupying main table space (if argument is zero or not specified all table elements will be stretched evenly), third argument: boolean value indicating whether table cells should be centered, fourth argument: string or table to be shown in folded mode;
* `ui:show_buttons(names, [colors])` - displays a list of buttons, the first argument is a table of strings, the second is an optional argument, a table of colors in the format #XXXXXX; * `ui:show_buttons(names, [colors])` - displays a list of buttons, the first argument is a table of strings, the second is an optional argument, a table of colors in the format #XXXXXX;
* `ui:show_progress_bar(text, current_value, max_value, [color])` - shows the progress bar; * `ui:show_progress_bar(text, current_value, max_value, [color])` - shows the progress bar;
* `ui:show_chart(points, [format], [title], [show_grid], [folded_string], [copyright])` - shows the chart, points - table of coordinate tables, format - data format (see below), title - chart name, show\_grid - grid display flag, folded\_string - string for the folded state (otherwise the name will be shown), copyright - string displayed in the lower right corner; * `ui:show_chart(points, [format], [title], [show_grid], [folded_string], [copyright])` - shows the chart, points - table of coordinate tables, format - data format (see below), title - chart name, show\_grid - grid display flag, folded\_string - string for the folded state (otherwise the name will be shown), copyright - string displayed in the lower right corner;
* `ui:show_image(uri)` - show image by URL;
* `ui:show_toast(string)` - shows informational message in Android style; * `ui:show_toast(string)` - shows informational message in Android style;
* `ui:default_title()` - returns the standard widget title (set in the `name` metadata); * `ui:default_title()` - returns the standard widget title (set in the `name` metadata);
* `ui:set_title()` - changes the title of the widget, should be called before the data display function (empty line - reset to the standard title); * `ui:set_title()` - changes the title of the widget, should be called before the data display function (empty line - reset to the standard title);
* `ui:set_folding_flag(boolean)` - sets the flag of the folded mode of the widget, the function should be called before the data display functions; * `ui:set_folding_flag(boolean)` - sets the flag of the folded mode of the widget, the function should be called before the data display functions;
* `ui:folding_flag()` - returns folding flag; * `ui:folding_flag()` - returns folding flag;
* `ui:colors()` - returns table with current theme colors; * `ui:set_expandable()` - shows expand button on widget update;
* `ui:is_expanded()` - checks if expanded mode is enabled;
When you click on any element of the interface, the `on_click(number)` callback will be executed, where number is the ordinal number of the element. A long click calls `on_long_click(number)`. For example, if you use `ui:show_buttons` to show three buttons, then clicking the first button will call `on_click` with argument 1, the second with arguments 2, and so on. If there is only one element on the screen, the argument will always be equal to one and can be omitted. * `ui:set_progress(float)` - sets current widget progress (like in Player and Health widgets);
* `ui:set_edit_mode_buttons(table)` - adds icons listed in the table (formatted as `"fa:name"`) to the edit mode. When an icon is clicked, the function `on_edit_mode_button_click(index)` will be called.
The `ui:show_chart()` function takes a string as its third argument to format the x and y values on the screen. For example, the string `x: date y: number` means that the X-axis values should be formatted as dates, and the Y-values should be formatted as a regular number. There are four formats in total: The `ui:show_chart()` function takes a string as its third argument to format the x and y values on the screen. For example, the string `x: date y: number` means that the X-axis values should be formatted as dates, and the Y-values should be formatted as a regular number. There are four formats in total:
* `number` - an ordinary number with group separation; * `number` - an ordinary number with group separation;
* `float` - the same, but with two decimal places; * `float` - the same, but with two decimal places;
* `date` - date in day.month format; * `date` - date in day.month format;
* `time` - time in hours:minutes format. * `time` - time in hours:minutes format;
* `none` - disable.
### Clicks
When you click on any element of the interface, the `on_click(number)` callback will be executed, where number is the ordinal number of the element. A long click calls `on_long_click(number)`. For example, if you use `ui:show_buttons` to show three buttons, then clicking the first button will call `on_click` with argument 1, the second with arguments 2, and so on. If there is only one element on the screen, the argument will always be equal to one and can be omitted.
### Folding
By default, the script shows either the first line of content or a line specified in the function argument in collapsed mode. However, you can change this behavior using a special meta-tag:
```
-- on_resume_when_folding = "true"
```
In this case, the `on_resume()` callback will be triggered each time the widget is collapsed. Then you can check the widget's collapsed status using the `ui:folding_flag()` function and display different UI depending on the state of this flag.
### HTML and Markdown formatting
The functions `ui:show_text()`, `ui:show_lines()` and `ui:show_table()` support many HTML tags. For example: The functions `ui:show_text()`, `ui:show_lines()` and `ui:show_table()` support many HTML tags. For example:
@@ -114,17 +176,29 @@ First line<br/> Second line
You can also use Markdown markup. To do this, add the prefix `%%mkd%%` to the beginning of the line. Or you can disable the formatting completely with the prefix `%%txt%%`. You can also use Markdown markup. To do this, add the prefix `%%mkd%%` to the beginning of the line. Or you can disable the formatting completely with the prefix `%%txt%%`.
_Keep in mind: HTML formatting and icons will not work if you use the second parameter in `ui:show_lines()`._
### Icons
You can insert FontAwesome icons inside the text, to do this use this syntax: `%%fa:ICON_NAME%%. For example:
```
ui:show_text("<b>This</b> is the text with icons %%fa:face-smile%% %%fa:poo%% <i>and styles</i>")
```
The `ui:show_buttons()` function supports Fontawesome icons. Simply specify `fa:icon_name` as the button name, for example: `fa:play`. The `ui:show_buttons()` function supports Fontawesome icons. Simply specify `fa:icon_name` as the button name, for example: `fa:play`.
_Note: AIO only supports icons up to Fontawesome 6.3.0._
## Dialogs ## Dialogs
_Available only in widget scripts._ _Available only in widget scripts._
* `ui:show_dialog(title, text, [button1_text], [button2_text])` - show dialog, the first argument is the title, the second is the text, button1\_text is the name of the first button, button2\_text is the name of the second button; * `dialogs:show_dialog(title, text, [button1_text], [button2_text])` - show dialog, the first argument is the title, the second is the text, button1\_text is the name of the first button, button2\_text is the name of the second button;
* `ui:show_edit_dialog(title, [text], [default_value])` - show the dialog with the input field: title - title, text - signature, default\_value - standard value of the input field; * `dialogs:show_edit_dialog(title, [text], [default_value])` - show the dialog with the input field: title - title, text - signature, default\_value - standard value of the input field;
* `ui:show_radio_dialog (title, lines, [index])` - show a dialog with a choice: title - title, lines - table of lines, index - index of the default value; * `dialogs:show_radio_dialog (title, lines, [index])` - show a dialog with a choice: title - title, lines - table of lines, index - index of the default value;
* `ui:show_checkbox_dialog(title, lines, [table])` - show dialog with selection of several elements: title - title, lines - table of lines, table - table default values; * `dialogs:show_checkbox_dialog(title, lines, [table])` - show dialog with selection of several elements: title - title, lines - table of lines, table - table default values;
* `ui:show_list_dialog(prefs)` - shows dialog with list of data. * `dialogs:show_list_dialog(prefs)` - shows dialog with list of data.
Dialog button clicks should be handled in the `on_dialog_action(number)` callback, where 1 is the first button, 2 is the second button, and -1 is nothing (dialog just closed). `ui:show_radio_dialog()` returns the index of the selected item or -1 in case the cancel button was pressed. `ui:show_checkbox_dialog()` returns the table of indexes or -1. `ui:show_edit_dialog()` returns text or -1. Dialog button clicks should be handled in the `on_dialog_action(number)` callback, where 1 is the first button, 2 is the second button, and -1 is nothing (dialog just closed). `ui:show_radio_dialog()` returns the index of the selected item or -1 in case the cancel button was pressed. `ui:show_checkbox_dialog()` returns the table of indexes or -1. `ui:show_edit_dialog()` returns text or -1.
@@ -144,7 +218,7 @@ List dialog accepts table as argument:
_Avaialble from: 4.4.1_ _Avaialble from: 4.4.1_
* `ui:show_rich_editor(prefs)` - show complex editor dialog with undo support, lists, time, colors support. It can be used to create notes and tasks style widgets. * `dialogs:show_rich_editor(prefs)` - show complex editor dialog with undo support, lists, time, colors support. It can be used to create notes and tasks style widgets.
Dialog accepts table as argument: Dialog accepts table as argument:
@@ -189,11 +263,63 @@ Here `share`, `copy` and `trash` are the names of the icons, which can be found
When you click on any menu item, the collback `on_context_menu_click(idx)` will be called, where `idx` is the index of the menu item. When you click on any menu item, the collback `on_context_menu_click(idx)` will be called, where `idx` is the index of the menu item.
## Meta widgets
_Avaialble from: 4.7.0_
* `ui:build(table)` - constructs a widget from pieces of other AIO widgets.
The function takes a command table of this format as a parameter:
```
`battery` - battery progress bar;
`ram` - ram progress bar;
`nand` - nand progress bar;
`traffic` - traffic progress bar;
`screen` - screen time progress bar;
`alarm` - next alarm info;
`clock` - current time;
`notes [NUM]` - last NUM notes;
`tasks [NUM]` - last NUM tasks;
`calendar [NUM]` - last NUM calendar events;
`calendarw` - weekly calendar;
`exchage [NUM] [FROM] [TO]` - exchange rate FROM currency TO currency;
`player` - player controls;
`weather [NUM]` - weather forecast for NUM days;
`worldclock [TIME_ZONE]` - time in the given TIME_ZONE;
`notify [NUM]` - last NUM notifications;
`dialogs [NUM]` - last NUM dialogs;
`calculator` - calculator;
`feed [NUM]` - news feed;
`control` - control panel;
`stopwatch` - stopwatch;
`finance [NUM]` - finance tickers;
`financechart` - finance chart;
`contacts [NUM]` - contacts (number of lines);
`apps [NUM]` - frequent apps (number of lines);
`appbox [NUM]` - my apps (number of lines);
`applist [NUM]` - apps list (number of lines);
`appfolders [NUM]` - app folders;
`appcategories [NUM]` - app categries;
`timer` - timers;
`mailbox [NUM]` - mail widget;
`dialer` - dialer;
`recorder` - recorder;
`telegram` - telegram messages;
`smartspacer` - SmartSpacer;
`widgetscontainer` - widgets container;
`tips` - tips;
`text [TEXT]` - just shows TEXT;
`space [NUM]` - NUM of spaces.
```
[Sample](samples/build_ui_sample.lua).
## System ## System
* `system:open_browser(url)` - opens the specified URL in a browser or application that can handle this type of URL; * `system:open_browser(url)` - opens the specified URL in a browser or application that can handle this type of URL;
* `system:exec(string)` - executes a shell command; * `system:exec(string, [id])` - executes a shell command;
* `system:su(string)` - executes a shell command as root; * `system:su(string, [id])` - executes a shell command as root;
* `system:location()` - returns the location in the table with two values (location request is NOT executed, the value previously saved by the system is used); * `system:location()` - returns the location in the table with two values (location request is NOT executed, the value previously saved by the system is used);
* `system:request_location()` - queries the current location and returns it to the `on_location_result` callback; * `system:request_location()` - queries the current location and returns it to the `on_location_result` callback;
* `system:to_clipboard(string)` - copies the string to the clipboard; * `system:to_clipboard(string)` - copies the string to the clipboard;
@@ -202,14 +328,33 @@ When you click on any menu item, the collback `on_context_menu_click(idx)` will
* `system:alarm_sound(seconds)` - make alarm sound; * `system:alarm_sound(seconds)` - make alarm sound;
* `system:share_text(string)` - opens the "Share" system dialog; * `system:share_text(string)` - opens the "Share" system dialog;
* `system:lang()` - returns the language selected in the system; * `system:lang()` - returns the language selected in the system;
* `system:tz()` - returns TimeZone string (example: Africa/Cairo);
* `system:tz_offset()` - returns TimeZone offset in seconds; * `system:tz_offset()` - returns TimeZone offset in seconds;
* `system:currency()` - returns default currency code based on locale; * `system:currency()` - returns default currency code based on locale;
* `system:format_date_localized(format, date)` - returns localized date string (using java formatting);
* `system:battery_info()` - returns table with battery info; * `system:battery_info()` - returns table with battery info;
* `system:system_info()` - returns table with system info. * `system:system_info()` - returns table with system info.
The result of executing a shell command is sent to the `on_shell_result(string)` callback. The result of executing a shell command is sent to the `on_shell_result(string)` or `on_shell_result_$id(string)` (_starting from AIO 5.7.5_) callback.
## Intens * `system:show_notify(table)` - show system notifycation;
* `system:cancel_notify()` - cancel notification.
These two functions can be used to display, update, and delete system notifications. The possible fields for the `table` (each of them is optional) are:
```
`message` - the message displayed in the notification;
`silent` - true if the notification should be silent;
`action1` - the name of the first notification action;
`action2` - the name of the second notification action;
`action3` - the name of the third notification action.
```
When the notification is clicked, the main launcher window will open. When one of the three actions is clicked, the callback `on_notify_action(idx, name)` will be executed with the action's index and name as parameters.
_Keep in mind that the callback will only be executed for scripts of type `widget`._
## Intents
* `intent:start_activity(table)` - starts activity with intent described in the table; * `intent:start_activity(table)` - starts activity with intent described in the table;
* `intent:send_broadcast(table)` - sends broadcast intent described in the table. * `intent:send_broadcast(table)` - sends broadcast intent described in the table.
@@ -234,23 +379,58 @@ Intent table format (all fields are optional):
* `aio:fold_widget(string, [boolean])` - fold/unfold widget (if you do not specify the second argument the state will be switched) (_available from: 4.5.0_); * `aio:fold_widget(string, [boolean])` - fold/unfold widget (if you do not specify the second argument the state will be switched) (_available from: 4.5.0_);
* `aio:is_widget_added(string)` - checks if the widget is added to the screen; * `aio:is_widget_added(string)` - checks if the widget is added to the screen;
* `aio:self_name()` - returns current script file name (_available from: 4.5.0_); * `aio:self_name()` - returns current script file name (_available from: 4.5.0_);
* `aio:send_message(value, [script_name])` - sends lua value to other script or scripts (_avaialble from: 4.5.0_);
* `aio:colors()` - returns table with current theme colors;
* `aio:do_action(string)` - performs an AIO action ([more](https://aiolauncher.app/api.html)); * `aio:do_action(string)` - performs an AIO action ([more](https://aiolauncher.app/api.html));
* `aio:send_message(value, [script_name])` - sends lua value to other script or scripts (_avaialble from: 4.5.0_). * `aio:actions()` - returns a list of available actions;
* `aio:settings()` - returns a list of available AIO Settings sections;
* `aio:open_settings([section])` - open AIO Settings or AIO Settings section;
* `aio:add_todo(icon, text)` - add a TODO item with the specified Fontawesome icon and text.
Format of table elements returned by `aio:available_widgets()`: Format of table elements returned by `aio:available_widgets()`:
* `name` - internal name of the widget; ```
* `type` - widget type: `builtin`, `script` or `plugin`; `name` - internal name of the widget;
* `description` - widget description (usually empty for non-script widgets); `label` - title of the widget;
* `clonable` - true if the widget can have clones (examples: "My apps", "Contacts", "Mailbox" widgets); `type` - widget type: `builtin`, `script` or `plugin`;
* `enabled` - true if the widget is placed on the screen. `description` - widget description (usually empty for non-script widgets);
`clonable` - true if the widget can have clones (examples: "My apps", "Contacts", "Mailbox" widgets);
`enabled` - true if the widget is placed on the screen.
```
Format of table elements returned by `aio:active_widgets()`: Format of table elements returned by `aio:active_widgets()`:
* `name` - internal name of the widget; ```
* `label` - widget visible name; `name` - internal name of the widget;
* `position` - position on the screen; `label` - widget visible name;
* `folded` - true if widget is folded. `position` - position on the screen;
`folded` - true if widget is folded.
```
Format of table elements returned by `aio:actions()`:
```
`name` - action name;
`short_name` - action short name;
`label` - action name visible to the user;
`args` - action arguments if any.
```
Format of table elements returned by `aio:colors()`:
```
`primary_text` base text color;
`secondary_text` color for secondary text (e.g., sender name, time, etc.);
`button` button background color;
`button_text` text color inside buttons;
`progress` general progress bar color;
`progress_good` color for positive progress states (e.g., full battery or charging);
`progress_bad` color for negative progress states (e.g., battery level below 15%);
`enabled_icon` color for enabled icons (see the Control Panel widget);
`disabled_icon` color for disabled icons (see the Control Panel widget);
`accent` accent color;
`badge` badge color.
```
To accept a value sent by the `send_message` function, the receiving script must implement a callback `on_message(value)`. To accept a value sent by the `send_message` function, the receiving script must implement a callback `on_message(value)`.
@@ -272,13 +452,45 @@ function on_action()
end end
``` ```
To change the action of the settings icon in the widget's edit menu, you can add the on_settings() function to the script. It will be called every time the user presses the icon.
```
function on_settings()
ui:show_toast("Settings icon clicked!")
end
```
## Application management ## Application management
* `apps:list([sort_by], [no_hidden])` - returns the package table of all installed applications, `sort_by` - sort option (see below), `no_hidden` - true if no hidden applications are needed; * `apps:apps([sort_by])` - returns the table of tables of all installed applications;
* `apps:name(package)` - returns application name; * `apps:app(package_name)` - return the table of the given application;
* `apps:color(package)` - returns the color of the application in #XXXXXXXX format;
* `apps:launch(package)` - launches the application; * `apps:launch(package)` - launches the application;
* `apps:show_edit_dialog(package)` - shows edit dialog of the application. * `apps:show_edit_dialog(package)` - shows edit dialog of the application;
* `apps:categories()` - returns a table of category tables.
The format of the app table:
```
`pkg` - name of the app package (if it is cloned app or Android for Work app package name will also contain user id, like that: `com.example.app:123`);
`name` - name of the application;
`color` - application color;
`hidden` - true if the application is hidden;
`suspended` - true if the application is suspended;
`category_id` - category ID;
`tags` - array of tags;
`badge` - number on the badge;
`icon` - icon of the application in the form of a link (can be used in the side menu scripts).
```
The format of the category table:
```
`id` - category id (id above 1000 are custom categories);
`name` - category name;
`icon` - category icon;
`color` - category color;
`hidden` - the category is hidden by the user.
```
Sorting options: Sorting options:
@@ -305,42 +517,110 @@ If there is a problem with the network, the `on_network_error_$id` callback will
## Calendar ## Calendar
* `calendar:events([start_date], [end_date], [cal_table])` - returns table of event tables of all calendars, start\_date - event start date, end\_date - event end date, cal\_table - calendar ID table; * `calendar:events([start_date], [end_date], [cal_table])` - returns table of event tables of all calendars, start\_date - event start date, end\_date - event end date, cal\_table - calendar ID table (function will return the string `permission_error` if the launcher does not have permissions to read the calendar);
* `calendar:calendars()` - returns table of calendars tables; * `calendar:calendars()` - returns table of calendars tables;
* `calendar:request_permission()` - requests access rights to the calendar;
* `calendar:show_event_dialog(id)` - shows the event dialog; * `calendar:show_event_dialog(id)` - shows the event dialog;
* `calendar:open_ovent(id)` - opens an event in the system calendar; * `calendar:open_event(id|event_table)` - opens an event in the system calendar;
* `calendar:add_event(event_table)` - adds event to the system calendar. * `calendar:open_new_event([start], [end])` - opens a new event in the calendar, `start` - start date of the event in seconds, `end` - end date of the event;
* `calendar:add_event(event_table)` - adds event to the system calendar;
* `calendar:is_holiday(date)` - returns true if the given date is a holiday or a weekend;
* `calendar:enabled_calendar_ids()` - returns list of calendar IDs enabled in the builtin Calendar widget settings.
Event table format: Event table format:
* `id` - event ID; ```
* `calendar_id` - calendar ID; `id` - event ID;
* `title` - title of the event; `calendar_id` - calendar ID;
* `description` - description of the event; `title` - title of the event;
* `location` - address of the event by string; `description` - description of the event;
* `begin` - start time of the event (in seconds); `color` - color of the event;
* `end` - time of the event end (in seconds); `status` - status string of the event or empty;
* `all_day` - boolean value, which means that the event lasts all day. `location` - address of the event by string;
`begin` - start time of the event (in seconds);
`end` - time of the event end (in seconds);
`all_day` - boolean value, which means that the event lasts all day.
```
Calendar table format: Calendar table format:
* `id` - calendar identifier; ```
* `name` - name of the calendar; `id` - calendar identifier;
* `color` - color of the calendar in the format #XXXXXXXX. `name` - name of the calendar;
`color` - color of the calendar in the format #XXXXXXXX.
```
The function `calendar:request_permission()` calls `on_permission_granted()` callback if the user agrees to grant permission.
## Phone ## Phone
* `phone:contacts()` - returns table of phone contacts; * `phone:contacts()` - returns table of phone contacts (function will return the string `permission_error` if the launcher does not have permissions to read the calendar);
* `phone:request_permission()` - requests access rights to the contacts;
* `phone:make_call(number)` - dial the number in the dialer; * `phone:make_call(number)` - dial the number in the dialer;
* `phone:send_sms(number, [text])` - open SMS application and enter the number, optionally enter text; * `phone:send_sms(number, [text])` - open SMS application and enter the number, optionally enter text;
* `phone:show_contact_dialog(id)` - open contact dialog; * `phone:show_contact_dialog(id|lookup_key)` - open contact dialog;
* `phone:open_contact(id)` - open contact in the contacts app.
Contacts table format: Contacts table format:
* `id` - contact id; ```
* `lookup_key` - unique contact identifier; `id` - contact id;
* `name` - contact name; `lookup_key` - unique contact identifier;
* `number` - contact number. `name` - contact name;
`number` - contact number;
`icon` - contact icon in the form of a link (can be used in the side menu scripts).
```
The function `phone:request_permission()` calls `on_permission_granted()` callback if the user agrees to grant permission.
Upon the first launch of the application, contacts may not yet be loaded, so in the scripts, you can use the `on_contacts_loaded()` callback, which will be called after the contacts are fully loaded.
## Tasks
_Avaialble from: 4.8.0_
* `tasks:load()` - loads tasks;
* `tasks:add(task_table)` - adds the task described in the table;
* `tasks:remove(task_id)` - removes the task with the specified ID;
* `tasks:save(task_table)` - saves the task;
* `tasks:show_editor(task_id)` - shows the task editing dialog.
Once the tasks are loaded, the `on_tasks_loaded()` function will be executed.
The format of the task table:
```
`id` - task ID;
`text` - text;
`date` - date of creation;
`due_date` - deadline;
`completed_date` - task completion date;
`high_priority` - flag of high priority;
`notification` - flag of notification display;
`is_today` - is it today's task?
```
## Notes
_Avaialble from: 4.8.0_
* `notes:load()` - loads notes;
* `notes:add(note_table)` - adds the note described in the table;
* `notes:remove(note_id)` - removes the note with the specified ID;
* `notes:save(note_table)` - saves the note;
* `notes:colors()` - returns a list of colors (index is the color ID);
* `notes:show_editor(note_id)` - shows the note editing dialog.
Once the notes are loaded, the `on_notes_loaded()` function will be executed.
The format of the note table:
```
`id` - note ID;
`text` - text;
`color` - note color ID;
`position` - note position on the screen.
```
## Weather ## Weather
@@ -350,12 +630,14 @@ _Avaialble from: 4.1.0_
Function returns the weather data in the `on_weather_result(result)` callback, where `result` is a table of tables with the following fields: Function returns the weather data in the `on_weather_result(result)` callback, where `result` is a table of tables with the following fields:
* `time` - time in seconds; ```
* `temp` - temperature; `time` - time in seconds;
* `icon_code` - code of weather icon; `temp` - temperature;
* `humidity` - humidity; `icon_code` - code of weather icon;
* `wind_speed` - wind speed; `humidity` - humidity;
* `wind_direction` - wind direction. `wind_speed` - wind speed;
`wind_direction` - wind direction.
```
## Cloud ## Cloud
@@ -370,37 +652,76 @@ _Avaialble from: 4.1.0_
All data are returned in `on_cloud_result(meta, content)`. The first argument is the metadata, either a metadata table (in the case of `list_dir()`) or an error message string. The second argument is the contents of the file (in the case of `get_file()`). All data are returned in `on_cloud_result(meta, content)`. The first argument is the metadata, either a metadata table (in the case of `list_dir()`) or an error message string. The second argument is the contents of the file (in the case of `get_file()`).
## Notifications ## Profiles
_Avaialble from: 5.3.6._
* `profiles:list()` - returns a list of saved profiles;
* `profiles:dump(name)` - saves a new profile with the specified name;
* `profiles:restore(name)` - restores the saved profile;
* `profiles:dump_json()` - creates a new profile but instead of saving it, returns it as a JSON string;
* `profiles:restore_json(json)` - restores a profile previously saved using `dump_json()`.
## AI
_Avaialble from: 5.3.5._
_Requires subscription._
* `ai:complete(text)` - send message to the AI;
* `ai:translate(text, lang)` - translate text to the language with code `lang` (`en`, `de`, `es` etc).
All functions return an answer in the callback `on_ai_answer`. For example:
```
function on_alarm()
ai:complete("Who are you?")
end
function on_ai_answer(answer)
ui:show_text(answer)
end
```
_Keep in mind that the launcher imposes certain limitations on the use of this module. If you use it too frequently, you will receive an `error: rate limit` instead of a response. Additionally, an error will be returned if the request includes too much text._
## Reading notifications
_Available only in widget scripts._ _Available only in widget scripts._
_Avaialble from: 4.1.3_
* `notify:request_current()` - requests current notifications from the launcher; _Avaialble from: 4.1.3._
_This module is intended for reading notifications from other applications. To send notifications, use the `system` module._
* `notify:list()` - returns list of current notifications as table of tables;
* `notify:open(key)` - opens notification with specified key; * `notify:open(key)` - opens notification with specified key;
* `notify:close(key)` - removes the notification with the specified key; * `notify:close(key)` - removes the notification with the specified key;
* `notify:do_action(key, action_id)` - sends notification action (_available from: 4.1.5_); * `notify:do_action(key, action_id)` - sends notification action (_available from: 4.1.5_).
* `notify:consumed(key)` - mark notification as consumed so built-in Notifications widget will not show it;
The `notify:request_current()` function asks for all current notifications. The Launcher returns them one by one to the `on_notify_posted(table)` callback, where table is the table representing the notification. The same callback will be called when a new notification appears. When the notification is closed, the `on_notify_removed(table)` colbeck will be called. The launcher triggers callback when a notification is received or removed:
* `on_notifications_updated()` notification list changed;
Notification table format: Notification table format:
* `key` - a key uniquely identifying the notification; ```
* `time` - time of notification publication in seconds; `key` - a key uniquely identifying the notification;
* `package` - name of the application package that sent the notification; `time` - time of notification publication in seconds;
* `number` - the number on the notification badge, if any; `package` - name of the application package that sent the notification;
* `importance` - notification importance level: from 1 (low) to 4 (high), 3 - standard; `number` - the number on the notification badge, if any;
* `category` - notification category, for example `email`; `importance` - notification importance level: from 1 (low) to 4 (high), 3 - standard;
* `title` - notification title; `category` - notification category, for example `email`;
* `text` - notification text; `title` - notification title;
* `sub_text` - additional notification text; `text` - notification text;
* `big_text` - extended notification text; `sub_text` - additional notification text;
* `is_clearable` - true, if the notification is clearable; `big_text` - extended notification text;
* `group_id` - notification group ID; `is_clearable` - true, if the notification is clearable;
* `messages` - table of tables with fields: `sender`, `text`, `time` (_available from: 4.1.5_); `group_id` - notification group ID;
* `actions` - table notifications actions with fields: `id`, `title`, `have_input` (_available from: 4.1.5_); `messages` - table of tables with fields: `sender`, `text`, `time` (_available from: 4.1.5_);
`actions` - table notifications actions with fields: `id`, `title`, `have_input` (_available from: 4.1.5_);
```
Keep in mind that the AIO Launcher also calls `request_current()` every time you return to the launcher, which means that all scripts will also get notification information in the `on_notify_posted()` callback every time you return to the desktop. Keep in mind that the AIO Launcher also request current notifications every time you return to the launcher, which means that all scripts will also get the `on_notifications_updated()` callback called.
## Files ## Files
@@ -412,21 +733,63 @@ _Avaialble from: 4.1.3_
All files are created in the subdirectory `/sdcard/Android/data/ru.execbit.aiolauncher/files/scripts` without ability to create subdirectories. All files are created in the subdirectory `/sdcard/Android/data/ru.execbit.aiolauncher/files/scripts` without ability to create subdirectories.
## Rich UI
Starting with version 5.2.1, AIO Launcher includes an API that allows for displaying a more complex interface than what the high-level functions of the `ui` module allowed. For example, you can display text of any size, center it, move it up and down, display buttons on the left and right sides of the screen, draw icons of different sizes, and much more. Essentially, you can replicate the appearance of any built-in AIO widget.
[Detailed instructions](README_RICH_UI.md)
## App widgets
Starting from version 5.2.0, AIO Launcher supports interaction with app widgets through scripts. This means that you can create a wrapper for any app's widget that will fully match the appearance and style of AIO. You can also use this API if you need to retrieve information from other applications, such as the balance on your mobile phone account or your car's parking spot. If the application has a widget providing such information, you will be able to access it.
[Detailed instructions](README_APP_WIDGETS.md)
## Settings ## Settings
* `settings:get()` - returns the settings table in an array of words format; _Deprecated in 4.7.4. Use Preferences module._
* `settings:set(table)` - saves the settings table in an array of words format;
* `settings:get_kv()` - returns the settings table in `key=value` format;
* `settings:set_kv(table)` - saves settings table in the format `key=value`;
* `settings:show_dialog()` - show settings change dialog.
User can change settings through the dialog, which is available by clicking on the "gear" in the edit menu of the widget. If in the widget metadata there is a field `arguments_help`, its value will be shown in the edit dialog. If there is a field `arguments_default` - it will be used to get default arguments. * ~~`settings:get()` - returns the settings table in an array of words format;~~
* ~~`settings:set(table)` - saves the settings table in an array of words format;~~
* ~~`settings:get_kv()` - returns the settings table in `key=value` format;~~
* ~~`settings:set_kv(table)` - saves settings table in the format `key=value`;~~
* ~~`settings:show_dialog()` - show settings change dialog.~~
The standard edit dialog can be replaced by your own if you implement the `on_settings()` function. ~~User can change settings through the dialog, which is available by clicking on the "gear" in the edit menu of the widget. If in the widget metadata there is a field `arguments_help`, its value will be shown in the edit dialog. If there is a field `arguments_default` - it will be used to get default arguments.~~
~~The standard edit dialog can be replaced by your own if you implement the `on_settings()` function.~~
## Preferences
The `prefs` module is designed to permanently store the script settings. It is a simple Lua table which saves to disk all the data written to it.
You can use it just like any other table with the exception that it cannot be used as a raw array.
Sample:
```
prefs = require "prefs"
function on_resume()
prefs.new_key = "Hello"
ui:show_lines{prefs.new_key}
end
```
The `new_key` will be present in the table even after the AIO Launcher has been restarted.
The `show_dialog()` method automatically creates a window of current settings from fields defined in prefs. The window will display all fields with a text key and a value of one of three types: string, number, or boolean. All other fields of different types will be omitted. Fields whose names start with an underscore will also be omitted. Script will be reloaded on settings save.
Starting from version 5.5.2, you can change the order of fields in the dialog by simply specifying the order in `prefs._dialog_order`. For example:
```
prefs._dialog_order = "message,start_time,end_time"
```
## Animation and real time updates ## Animation and real time updates
_Available only in widget scripts._ _Available only in widget scripts._
_Avaialble from: 4.5.2_ _Avaialble from: 4.5.2_
The scripts API is designed in a way that every function that changes a widget's UI updates the entire interface. This approach makes the API as simple and convenient as possible for quick scripting, but it also prevents the creation of more complex scripts that change the UI state very often. The scripts API is designed in a way that every function that changes a widget's UI updates the entire interface. This approach makes the API as simple and convenient as possible for quick scripting, but it also prevents the creation of more complex scripts that change the UI state very often.
@@ -439,7 +802,7 @@ There are two modules to solve this problem: `morph` and `anim`. The first is us
* `morph:run_with_delay(delay, function)` - populates specified Lua function with delay `delay`; * `morph:run_with_delay(delay, function)` - populates specified Lua function with delay `delay`;
* `morph:cancel(idx)` - cancels a previously run change if it is not yet complete (e.g., delay or animation is not over). * `morph:cancel(idx)` - cancels a previously run change if it is not yet complete (e.g., delay or animation is not over).
* `anim:blink(idx)` - blinks UI element with index `idx`; * `anim:blink(idx)` - blinks UI element with index `idx`;
* `anim:move(x, y, [delay])` - moves element sideways by specified number of DP and returns back after delay; * `anim:move(idx, x, y, [delay])` - moves element sideways by specified number of DP and returns back after delay;
* `anim:heartbeat(idx)` - animation of heartbeat; * `anim:heartbeat(idx)` - animation of heartbeat;
* `anim:shake(idx)` - shake animation; * `anim:shake(idx)` - shake animation;
* `anim:cancel(idx)` - cancel the running animation. * `anim:cancel(idx)` - cancel the running animation.
@@ -459,7 +822,7 @@ _Avaialble from: 4.4.4_
* `tasker:tasks([project])` - returns a list of all the tasks in the Tasker, the second optional argument is the project for which you want to get the tasks (returns nil if Tasker is not installed or enabled); * `tasker:tasks([project])` - returns a list of all the tasks in the Tasker, the second optional argument is the project for which you want to get the tasks (returns nil if Tasker is not installed or enabled);
* `tasker:projects()` - returns all Tasker projects (returns nil if Tasker is not installed or enabled); * `tasker:projects()` - returns all Tasker projects (returns nil if Tasker is not installed or enabled);
* `tasker:run_task(name, [args])` - executes the task in the Tasker, the second optional argument is a table of variables passed to the task in the format `{ "name" = "value" }`; * `tasker:run_task(name, [args])` - executes the task in the Tasker, the second optional argument is a table of variables passed to the task in the format `{ name = "value" }`;
* `tasker:run_own_task(commands)` - constructs and performs the task on the fly; * `tasker:run_own_task(commands)` - constructs and performs the task on the fly;
* `tasker:send_command(command)` - sends [tasker command](https://tasker.joaoapps.com/commandsystem.html). * `tasker:send_command(command)` - sends [tasker command](https://tasker.joaoapps.com/commandsystem.html).
@@ -497,11 +860,17 @@ The standard Lua API is extended with the following features:
* `string:split(delimeter)` - splits the string using the specified delimiter and returns a table; * `string:split(delimeter)` - splits the string using the specified delimiter and returns a table;
* `string:replace(regexp, string)` - replaces the text found by the regular expression with another text; * `string:replace(regexp, string)` - replaces the text found by the regular expression with another text;
* `string:trim()` - removes leading and trailing spaces from the string;
* `string:starts_with(substring)` - returns true if the string starts with the specified substring;
* `string:ends_with(substring)` - returns true if the string ends with the specified substring;
* `slice(table, start, end)` - returns the part of the table starting with the `start` index and ending with `end` index; * `slice(table, start, end)` - returns the part of the table starting with the `start` index and ending with `end` index;
* `get_index(table, value)` - returns the index of the table element; * `index(table, value)` - returns the index of the table element;
* `get_key(table, value)` - returns the key of the table element; * `key(table, value)` - returns the key of the table element;
* `concat_tables(table1, table2)` - adds elements from array table `table2` to `table1`; * `concat(table1, table2)` - adds elements from array table `table2` to `table1`;
* `reverse(table)` - returns a table in which the elements follow in reverse order;
* `serialize(table)` - serializes the table into executable Lua code;
* `round(x, n)` - rounds the number; * `round(x, n)` - rounds the number;
* `map(function, table)`, `filter(function, table)`, `head(table)`, `tail(table)`, `reduce(function, table)` - several convenience functional utilities form Haskell, Python etc.
AIO Launcher also includes: AIO Launcher also includes:
@@ -523,12 +892,12 @@ In order for AIO Launcher to correctly display information about the script in t
``` ```
-- name = "Covid info" -- name = "Covid info"
-- icon = "fontawesome_icon_name"
-- description = "Cases of illness and death from covid" -- description = "Cases of illness and death from covid"
-- data_source = "https://covid19api.com" -- data_source = "https://covid19api.com"
-- arguments_help = "Specify the country code"
-- arguments_default = "RU"
-- type = "widget" -- type = "widget"
-- foldable = "false" -- foldable = "true"
-- private_mode = "false"
-- author = "Evgeny Zobnin (zobnin@gmail.com)" -- author = "Evgeny Zobnin (zobnin@gmail.com)"
-- version = "1.0" -- version = "1.0"
``` ```
@@ -561,7 +930,8 @@ Some tips on writing and debugging scripts:
* The most convenient way to upload scripts to your smartphone is to use the `install-scripts.sh` script from this repository. This is a sh script for UNIX systems which loads all the scripts from the repository onto the (virtual) memory card of the smartphone using ADB. You can edit it to your liking. * The most convenient way to upload scripts to your smartphone is to use the `install-scripts.sh` script from this repository. This is a sh script for UNIX systems which loads all the scripts from the repository onto the (virtual) memory card of the smartphone using ADB. You can edit it to your liking.
* The easiest way to reload an updated widget script is to swipe the widget to the right and then press the "reload" button. The search scripts will be reloaded automatically next time you open the search window. * The easiest way to reload an updated widget script is to swipe the widget to the right and then press the "reload" button. The search scripts will be reloaded automatically next time you open the search window.
* Since version 4.3.0 AIO Launcher supports widget scripts hot reloading. To enable it, go to AIO Settings -> About and click on the version number 7 times. Then open AIO Settings -> Testing and enable the option "Hot reload scripts on resume". Now when you change the script, it will be automatically reloaded when you return to the desktop. * Since version 4.3.0 AIO Launcher supports widget scripts hot reloading. To enable it, go to AIO Settings -> About and click on the version number 7 times. Then open AIO Settings -> Testing and enable the option "Hot reload scripts on resume". Now when you change the script, it will be automatically reloaded when you return to the desktop.
* Since version 4.4.2 AIO Launcher includes `debug` module with methods: `debug:log(text)`, `debug:toast(text)` and `debug:dialog(text)`. * Since version 4.4.2 AIO Launcher includes `debug` module with methods: `debug:log(text)`, `debug:toast(text)` and `debug:dialog(text)`;
* Since version 4.8.0 you can use `--testing = "true"` meta tag. In this case, launcher will gray out the script and place it at the end of the list in the side menu.
# Contribution # Contribution

132
README_APP_WIDGETS.md Normal file
View File

@@ -0,0 +1,132 @@
Starting from version 5.2.0, AIO Launcher supports interaction with app widgets through scripts. This means that you can create a wrapper for any app's widget that will fully match the appearance and style of AIO. You can also use this API if you need to retrieve information from other applications, such as the balance on your mobile phone account or your car's parking spot. If the application has a widget providing such information, you will be able to access it.
### Introduction
Before you start working with app widgets, you need to find out the app widget provider's name and the widget interface structure. Both can be done using the [android-widget-dumper.lua](dev/android-widget-dumper.lua) script. Enter the package name of the desired application in its global variable `app_pkg` and run the script. It will display all the widgets available for that application. Click on the widget name, and you will see its provider name (first line) and a tree-like structure of its UI. Click on the text to copy it.
_Note: Some widgets can dynamically change their UI depending on their size. If you encounter such a widget, update the value of the `widget_size` variable by specifying a string from `1x1` to `4x4`. In other cases, leave the value as `nil`._
Take, for example, The Weather Channel app (package name: `com.weather.Weather`). Install this app and add its package name to the dumper script. After running the dumper, it will show you a list of widgets. Click on "Widget 2x2". A widget configuration screen will open. Click Done at the top of the configurator screen, and you will see the data of this widget. The first line will be the provider's name:
```
com.weather.Weather/com.weather.Weather.widgets.WeatherWidgetProvider2x2
```
The second block of information is the widget interface structure:
```
relative_layout_1:
image_1: #13356F
relative_layout_2:
image_2: 270x270
text_1: 40°
text_2: Paris, France
text_3: Weather data not available
```
You can see that the widget contains two images and three text fields: `text_1`, `text_2`, `text_3`. The first two contain the information we need: temperature and location.
Now we have everything to write our own widget that will use information from The Weather Channel widget and display it in a format convenient for us!
### Writing a Widget Wrapper
Let's start by writing a widget initialization function:
```
function setup_app_widget()
local id = widgets:setup("com.weather.Weather/com.weather.Weather.widgets.WeatherWidgetProvider2x2")
if (id ~= nil) then
prefs.wid = id
else
ui:show_text("Can't add widget")
end
end
```
Using the `widgets:setup()` method, we ask the launcher to prepare a widget for us. The method returns an identifier needed to work with the widget, or `nil` if an error occurred. Note that this function may trigger the widget's configuration window if it has one.
Having obtained the identifier, we can use it to communicate with the widget. For this purpose, we call the `widgets:request_updates()` method (you can specify the widget size as a string from 1x1 to 4x4 as the second argument):
```
widgets:request_updates(prefs.wid)
```
This method prepares the widget and then calls the `on_widget_updated(bridge)` callback every time the widget updates. The `bridge` object here is the main "window" or "bridge" for interacting with the widget. It's with its help that we will extract information from the widget.
The most straightforward way to do this is to use the `bridge:dump_table()` method, which returns the same UI element hierarchy as the dumper script but in a Lua table format. Here's how we can use it to extract the strings we need and display them on the screen:
```
function on_app_widget_updated(bridge)
local tab = bridge:dump_table()
temp = tab.relative_layout_1.relative_layout_2.text_1
location = tab.relative_layout_1.relative_layout_2.text_2
if location ~= nil and temp ~= nil then
ui:show_text(location..": "..temp)
end
end
```
Run the example [android-widget-sample2.lua](samples/android-widget-sample2.lua) to see how it looks on the screen.
Another way to extract the same information is to use the `bridge:dump_strings()` method:
```
function on_app_widget_updated(bridge)
strings = bridge:dump_strings().values
temp = strings[1]
location = strings[2]
if location ~= nil and temp ~= nil then
ui:show_text(location..": "..temp)
end
end
```
It returns a table of tables, where the `keys` table is an array of UI text elements, and `values` contains the text strings within them. You can use whichever method suits you better.
In both cases, the strings can include HTML tags (`<b>`, `<i>`, `<s>`, `<u>`) if the corresponding styles were applied to the text in the widget.
### Handling Clicks
Extracting information from an app widget is only half the job. Most widgets are interactive, meaning you can click on them to get some response, like adding a new note or opening a currency exchange rate window.
Implementing clicks on widget elements from the script is very simple: just use the `bridge:click()` method, passing it the name of the UI element or the string it contains as an argument:
```
bridge:click(temp)
```
Open the example [android-widget-sample2.lua](samples/android-widget-sample2.lua) to see how this can be used in the script.
### Updating and releasing the Widget
The AIO Launcher widget API is designed to keep the widget in memory all the time while the script is on the screen. This means that after calling `widgets:request_updates()`, the launcher will call the `on_widget_updated()` callback after _every_ widget interface update as long as the script is on the launcher's screen, and the launcher is on the phone's screen.
In most cases, such a mechanism is preferable. However, there are situations when `on_widget_updated()` should only be called when the script asks for it, and the widget itself should not constantly hang in memory. For this, there's a special `bridge:free()` method.
For example, you have a widget whose information you want to receive every morning, not with every update. In this case, you do everything the same as in the example above, but at the end of the `on_app_widget_updated()` function, you call `bridge:free()`. After that, the launcher will release the widget and will no longer call the `on_widget_updated()` method (while the widget itself will not respond to clicks). Then, it's enough to call `widgets:request_updates()` every morning to update the script's content.
### Other Functions
The information provided above is sufficient to handle almost any application widget. However, to write a truly efficient script, you'll need a few more auxiliary functions:
* `widgets:bound(id)` - returns true if the specified widget identifier was previously initialized using `widgets:setup()`. You should _always_ call this function at the beginning of the script and call `widgets:setup()` only if it returns `false`.
* `bridge:color(element_name)` - returns the color of an element in the format `#000000`. With this method, you can find out the color of the text, the background color of the layout (if set), or even extract the primary color from an image.
Other functions:
```
* bridge:label() - returns the name of the widget;
* bridge:provider() - returns the name of the widget provider;
* bridge:dump_tree() - returns the UI tree in text format;
* bridge:dump_json() - returns the UI tree in Json format;
* bridge:dump_colors() - returns a table of color tables, where `keys` are element names, `values` are colors;
* bridge:dump_elements(element_name) - returns a table of tables of specified elements, where `keys` are element names, `values` are their values.
```
### Examples
In the [samples](samples/) directory, you will find several widget examples. Also, pay attention to the Todoist and Chrome widgets in the [community](community/) directory.

201
README_INTRO.md Normal file
View File

@@ -0,0 +1,201 @@
# Creating Your Own Scripts for AIO Launcher: A Step-by-Step Guide for Beginners
**AIO Launcher** isnt just an alternative to the standard Android home screen — it transforms your device into a powerful tool for personalization through Lua scripting. In this article, well show you how to write your first scripts, presenting three examples ranging from simple text output to an interactive widget and integration with an open API. Well also explain how to add your scripts to the launcher via AIO Store.
---
## How to Create and Load Scripts
You can write scripts for AIO Launcher in any text editor—whether its Sublime Text, Visual Studio Code, or even Notepad. The key is to save your file with the **.lua** extension. Then, using the **AIO Store** app, you can easily add your file to the launcher. In AIO Store, at the end of the list of installed scripts theres a special "Add Script" button that lets you add your own script.
---
## Example 1. Hello, World!
The simplest script is the classic "Hello, World!" In this example, when the widget is launched, a welcome message appears on the screen.
```lua
-- name = "Hello, World!"
-- type = "widget"
function on_load()
-- On the first load of the widget, display the text "Hello, World!".
ui:show_text("Hello, World!")
end
```
*Explanation:*
- The metadata at the beginning of the file sets the scripts name and type.
- The `on_load()` function is called when the script starts.
- The `ui:show_text()` method displays the text on the widget.
---
## Example 2. Interactive Counter
In the second example, we create an interactive counter using two buttons. Instead of displaying the count separately, the current counter value is embedded directly in the label of the "Increase" button. When you press "Increase", the counter increments and the button label updates; pressing "Reset" sets the counter back to zero.
```lua
-- name = "Interactive Counter"
-- type = "widget"
-- Global variable to store the current count.
local counter = 0
-- Function to update the display: shows two buttons with the counter embedded in the first button.
function update_display()
ui:show_buttons({ "Increase (" .. counter .. ")", "Reset" })
end
function on_load()
update_display()
end
-- Function to handle button clicks.
function on_click(index)
if index == 1 then
counter = counter + 1
elseif index == 2 then
counter = 0
end
update_display()
end
```
*Explanation:*
- When the script launches, the `on_load()` function calls `update_display()`, which displays two buttons.
- The first buttons label includes the current counter value (e.g., "Increase (0)").
- The `on_click(index)` function handles button presses: if the first button is pressed, the counter increases; if the second button is pressed, it resets to zero.
- After each action, `update_display()` updates the button labels accordingly.
---
## Example 3. Cat Facts
In the third example, we use the Cat Facts API to retrieve a random cat fact. This version uses the `on_alarm` callback instead of `on_load` so that the widget automatically updates every 30 minutes.
```lua
-- name = "Cat Facts"
-- type = "widget"
-- on_alarm is called automatically, up to once every 30 minutes, to refresh the widget.
function on_alarm()
ui:show_text("Loading a random cat fact...")
http:get("https://catfact.ninja/fact")
end
function on_network_result(body, code)
if code == 200 then
local json = require "json"
local data = json.decode(body) -- The json module converts the JSON string into a Lua table.
if data and data.fact then
ui:show_text("Random Cat Fact:\n" .. data.fact)
else
ui:show_text("Failed to retrieve a cat fact.")
end
else
ui:show_text("Error loading data. Code: " .. code)
end
end
```
*Explanation:*
- In this example, the `on_alarm()` function is used to trigger an automatic update every 30 minutes, ensuring that your widget stays fresh with a new cat fact.
- The `http:get()` call fetches data from the Cat Fact API, and the `json` module decodes the returned JSON string into a Lua table for easy access.
Remember: theres also an `on_resume` callback that is invoked every time you return to the home screen, which you can use for additional actions if desired.
---
## Example 4. Search Engine Selector for the Search Window
In this example, we demonstrate how to create a search script for the AIO Launcher search window. Unlike widget scripts, search scripts are activated when the search window is opened and as the user types their query. This script presents two search suggestions — one for searching on Google and another for Bing. When a suggestion is clicked, the corresponding search engine opens in the browser.
```lua
-- name = "Search Engine Selector"
-- type = "search"
local lastQuery = ""
function on_search(query)
lastQuery = query
local results = {}
if #query > 0 then
table.insert(results, "Search Google for: " .. query)
table.insert(results, "Search Bing for: " .. query)
end
search:show_lines(results)
end
function on_click(index)
if index == 1 then
system:open_browser("https://www.google.com/search?q=" .. lastQuery)
return true
elseif index == 2 then
system:open_browser("https://www.bing.com/search?q=" .. lastQuery)
return true
end
return false
end
```
*Explanation:*
- This search script is identified as a search script by the meta tag `-- type = "search"`.
- The global variable `lastQuery` stores the most recent search query.
- The `on_load()` function is called each time the search window is opened, displaying a default message.
- The `on_search(query)` function is invoked whenever the user types something; it saves the query and builds a list of suggestions, which are displayed via `search:show_lines()`.
- The `on_click(index)` function handles the user's selection: if the first result is clicked, the system opens Google with the query; if the second is clicked, Bing is used.
---
## Example 5. Side Menu Script
This example demonstrates how to create a side menu (drawer) script for AIO Launcher. Side menu scripts allow you to add custom items to the launcher's side menu. The script type is defined by the meta tag `-- type = "drawer"`, and the primary callback function is `on_drawer_open()`, which is invoked when the side menu is opened. When the user selects an item, the `on_click(index)` function is called to execute the corresponding action.
```lua
-- name = "Custom Drawer Menu"
-- type = "drawer"
function on_drawer_open()
local menuItems = {"Open Website", "Update list", "Launch App"}
drawer:show_list(menuItems)
end
function on_click(index)
if index == 1 then
system:open_browser("https://www.example.com")
elseif index == 2 then
local menuItems = {"Open Website", "List updated", "Launch App"}
drawer:show_list(menuItems)
elseif index == 3 then
apps:launch("com.android.contacts")
end
return true
end
```
*Explanation:*
- The meta tags at the top specify the scripts name and indicate that it is a drawer (side menu) script.
- The `on_drawer_open()` function is automatically called when the side menu is opened; it builds a list of menu items (in this case, three items) and displays them using `drawer:show_list()`.
- The `on_click(index)` function handles user selection: clicking the first item opens a website, the second updates items list, and the third launches an application.
## Conclusion
Creating scripts for AIO Launcher opens up endless possibilities for customizing and extending the functionality of your Android device. From simple text output to interactive widgets and integration with external services, all of this is accessible thanks to the powerful API of AIO Launcher and the flexibility of Lua. Use any text editor you prefer to write your scripts, then easily add them to the launcher via AIO Store using the dedicated "Add Script" button at the end of your installed scripts list. Experiment, integrate new data sources, and create unique solutions that make your home screen truly your own!
---
## Sources for Further Study
- citeturn0search0 [Official AIO Launcher Scripts Repository](https://github.com/zobnin/aiolauncher_scripts)
- [AIO Launcher on Google Play](https://play.google.com/store/apps/details?id=ru.execbit.aiolauncher&hl=en)
- [Lua Documentation](https://www.lua.org/pil/)
- [Cat Fact API](https://catfact.ninja/fact)
- [AIO Store](https://play.google.com/store/apps/details?id=ru.execbit.aiolauncher.store)
These resources will help you dive deeper into creating scripts for AIO Launcher and explore new ways to extend your Android devices functionality.

94
README_RICH_UI.md Normal file
View File

@@ -0,0 +1,94 @@
Starting with version 5.2.1, AIO Launcher includes an API that allows for displaying a more complex interface than what the high-level functions of the `ui` module allowed. For example, you can display text of any size, center it, move it up and down, display buttons on the left and right sides of the screen, draw icons of different sizes, and much more. Essentially, you can replicate the appearance of any built-in AIO widget.
Open the example [rich-gui-basic-sample.lua](samples/rich-gui-basic-sample.lua) and study it. As you can see, the new API consists of just one function `gui`, which takes a table describing the UI as input and returns an object that has a `render()` method for drawing this UI.
The UI is built line by line, using commands that add elements from left to right, with the possibility of moving to a new line. The provided example displays two lines, under which are two buttons:
```
{"text", "First line"},
{"new_line", 1},
{"text", "Second line"},
{"new_line", 2},
{"button", "Button #1"},
{"spacer", 2},
{"button", "Button #2"},
```
The first command displays the line "First line", the "new_line" command moves to a new line, adding a space equal to one unit (each unit is 4 pixels). Then, the "text" command adds a new line, followed by a move to a new line and the display of two buttons. Since there is no "new_line" command between the button display commands, they will be displayed in one line, one after the other, from left to right with a gap of 2 units. The "spacer" command is responsible for the horizontal gap.
This example is already useful, but it's too plain. Let's add some colors and vary the text lines a bit: make the first line larger, and write the second line in italic font. Also, let's work on the color of the buttons:
```
{"text", "First line", {size = 21}},
{"new_line", 1},
{"text", "<i>Second line</i>"},
{"new_line", 2},
{"button", "Button #1", {color = "#ff0000"}},
{"spacer", 2},
{"button", "Button #2", {color = "#0000ff"}},
```
To change the size of the text, we used the table `{size = 21}`. This is the standard approach if a command has more than one parameter, then all other arguments are passed in the table. To make the text italic, we used the HTML tag `<i>`. The "text" command supports many tags for text transformation. The color of the buttons is also changed using a table.
It's more interesting now, but again, an interface where all elements are grouped on the left side may not always be suitable. We need a way to align elements on the right side of the screen or in the center. Let's make the first line be in the middle of the line (kind of like a title), and the second button - on the right side:
```
{"text", "First line", {size = 21, gravity = "center_h"}},
{"new_line", 1},
{"text", "<i>Second line</i>"},
{"new_line", 2},
{"button", "Button #1", {color = "#ff0000"}},
{"spacer", 2},
{"button", "Button #2", {color = "#0000ff", gravity = "right"}},
```
Here we used the `gravity` parameter to change the location of the element in the line. Three things are important to know about `gravity`:
1. It changes the position of the element only within the current line;
2. Possible `gravity` values: `left`, `top`, `right`, `bottom`, `center_h` (horizontal centering), `center_v` (vertical centering) and `anchor_prev`;
3. `Gravity` values can be combined, for example, to display a text element in the top right corner of the current line, you can specify `gravity = "top|right"`;
Also, there are two limitations to know about:
1. The `center_h` value is applied to each element separately, meaning if you add two lines with the gravity value `center_h` in one line, they will not both be grouped and displayed in the center, but instead, they will split the screen in half and be displayed each in the center of its half. This situation can be rectified by using the `anchor_prev` as the value for gravity. This flag anchors the current element to the previous element of the current line, so that the `gravity` value of the previous element starts affecting both elements.
2. The `right` value affects not only the element to which it is applied but also all subsequent elements in the current line. That means if you add another button after "Button #2", it will also be on the right side after "Button #2".
Surely you will want to add icons to your UI. There are several ways to do this. The first way is to embed the icon directly into the text, in this case, you can use any icon from the FontAwesome set:
```
{"text", "This is icon: %%fa:microphone%%"}
```
The second way: use the icon command (in this case, you can also specify the size and color of the icon):
```
{"icon", "fa:microphone", {size = 32, color = "#ff0000"}}
```
Third way: display icon in the button:
```
{"button", "fa:microphone"}
{"button", "Text with icon: %%fa:microphone%%"}
```
The second method also allows displaying icons of applications and contacts. How to do this is shown in the example [rich-gui-sample.lua](samples/rich-gui-sample.lua). You can also use the `icon` element to display your own icons in SVG format. Example: [svg-sample.lua](samples/svg-sample.lua).
By the way, if you want the button to stretch across the entire width of the screen, you can use the expand argument:
```
{"button", "Full with button", {expand = true}}
```
Handling clicks on elements works the same as when using the `ui` module API. Just define `on_click()` or `on_long_click()` functions. The first parameter of the function will be the index of the element. How to use this mechanism can be learned in the example [samples/rich-ui-sample.lua].
This is all you need to know about the new API. Below is an example demonstrating all supported elements and all their default parameters:
```
{"text", "", {size = 17, color = "", gravity = "left"}},
{"button", "", {color = "", gravity = "left", expand = "false"}},
{"icon", "", {size = 17, color = "", gravity = "left"}},
{"progress", "", {progress = 0, color = ""}},
{"new_line", 0},
{"spacer", 0},
```

View File

@@ -0,0 +1,26 @@
-- name = "Actions menu"
-- name_id = "actions"
-- description = "Shows aio launcher actions"
-- type = "drawer"
-- aio_version = "4.7.99"
-- author = "Evgeny Zobnin"
-- version = "1.0"
function on_drawer_open()
actions = aio:actions()
labels = map(actions, function(it) return it.label end)
drawer:show_list(labels)
end
function on_click(idx)
aio:do_action(actions[idx].name)
drawer:close()
end
function map(tbl, f)
local ret = {}
for k,v in pairs(tbl) do
ret[k] = f(v)
end
return ret
end

View File

@@ -0,0 +1,64 @@
-- name = "AlQuran"
-- description = "AlQuran"
-- data_source = "https://quran.com/"
-- type = "widget"
-- author = "Nuhu Sule (ncalyx@gmail.com)"
-- version = "2.3"
-- foldable = "false"
local json = require "json"
local verse_data = nil -- Store the verse translation data
function on_alarm()
-- Fetch random verse with English translation
http:get("https://api.alquran.cloud/v1/ayah/random/en.sahih")
end
function on_network_result(result, code)
if code >= 200 and code < 300 then
local response = json.decode(result)
if response and response.data then
-- Store verse data including Surah name, verse number, and English translation
verse_data = {
surah = response.data.surah.englishName,
verse_number = response.data.number,
translation = response.data.text -- English translation only
}
display_verse()
else
ui:show_message("Error loading verse data.")
end
else
-- Show error if the HTTP request fails
ui:show_message("Error fetching verse. Please try again later.")
end
end
function display_verse()
if verse_data then
-- Prepare display lines with English translation
local display_lines = {
"Verse " .. verse_data.verse_number .. ": " .. verse_data.translation
}
local display_titles = {
"Surah: " .. verse_data.surah
}
ui:show_lines(display_lines, display_titles)
end
end
function on_click()
if verse_data then
-- Prepare text to copy to clipboard with English translation only
local clipboard_text = "Verse " .. verse_data.verse_number .. ": " .. verse_data.translation ..
" - Surah: " .. verse_data.surah
system:to_clipboard(clipboard_text)
ui:show_message("Verse copied to clipboard!")
else
ui:show_message("No verse available to copy.")
end
end

View File

@@ -0,0 +1,88 @@
-- name = "Amdroid Buttons"
-- description = "Foldable AIO wrapper for the Amdroid Buttons app widget"
-- type = "widget"
-- author = "Theodor Galanis (t.me/TheodorGalanis)"
-- version = "1.10"
-- foldable = "true"
-- on_resume_when_folding = "true"
-- uses_app = "com.amdroidalarmclock.amdroid"
local prefs = require "prefs"
local indices = {1, 3, 4, 6, 8}
local next_alarm = ""
local accent="#888888"
local temp = {}
function on_resume()
if not widgets:bound(prefs.wid) then
setup_app_widget()
end
accent = aio:colors().accent
widgets:request_updates(prefs.wid)
end
function on_app_widget_updated(bridge)
local tab = bridge:dump_table()
next_alarm = tab.frame_layout_1.v_layout_1.text_1
w_bridge = bridge
local state = ui:folding_flag()
if state == false then
temp = {
{"icon", "fa:alarm-clock", {gravity="center_h|center_v",color = accent}},
{"spacer", 2 },
{"text", next_alarm, {gravity="anchor_prev"}},
{"button", "fa:backward", {gravity = "right"}},
{"spacer", 2},
{"button", "fa:right-long-to-line"},
{"spacer", 2},
{"button", "fa:forward"}
}
else
temp = {
{"icon", "fa:alarm-clock", {gravity="center_h|center_v",color = accent}},
{"spacer", 2 },
{"text", next_alarm, {gravity="anchor_prev"}}
}
end
my_gui = gui(temp)
my_gui.render()
end
function on_click(idx)
if idx == indices[1] or idx == indices[2] then
w_bridge:click(next_alarm)
elseif idx == indices[3] then
w_bridge:click("image_3")
elseif idx == indices[4] then
w_bridge:click("image_1")
elseif idx == indices[5] then
w_bridge:click("image_2")
end
end
function on_long_click(idx)
if idx == indices[1] or idx == indices[2] then
ui:show_toast("Open Amdroid app")
elseif idx == indices[3] then
ui:show_toast("Adjust alarm's next occurance time 10 minutes earlier")
elseif idx == indices[4] then
ui:show_toast("Skip alarm's next occurance")
elseif idx == indices[5] then
ui:show_toast("Adjust alarm's next occurance time 10 minutes later")
end
end
function on_settings()
ui:show_dialog("Amdroid app Buttons widget", "This script wrapper uses Amdroid app's Buttons widget. Long click each button for function description. No settings are required.")
end
function setup_app_widget()
local id = widgets:setup("com.amdroidalarmclock.amdroid/com.amdroidalarmclock.amdroid.AmdroidAppWidgetProvider")
if (id ~= nil) then
prefs.wid = id
else
ui:show_text("Can't add widget")
end
end

View File

@@ -0,0 +1,60 @@
-- name = "Amdroid Next Alarm"
-- description = "AIO wrapper for the Amdroid Next alarm app widget"
-- type = "widget"
-- author = "Theodor Galanis (t.me/TheodorGalanis)"
-- version = "1.11"
-- foldable = "false"
-- uses_app = "com.amdroidalarmclock.amdroid"
local prefs = require "prefs"
local next_alarm = ""
local accent="#888888"
function on_resume()
if not widgets:bound(prefs.wid) then
setup_app_widget()
end
accent = aio:colors().accent
primary = aio:colors().primary_text
widgets:request_updates(prefs.wid)
end
function on_app_widget_updated(bridge)
local tab = bridge:dump_table()
next_alarm = tab.v_layout_1.text_1
w_bridge = bridge
if next_alarm ~= nil
then
my_gui=gui{
{"icon", "fa:alarm-clock", {gravity="center_h|center_v",color = accent}},
{"spacer", 2},
{"text", next_alarm, {gravity="anchor_prev"}}
}
else
my_gui=gui{
{"text", "Empty", {gravity="center_h" }}
}
end
my_gui.render()
end
function on_click(idx)
w_bridge:click(next_alarm)
end
function on_settings()
dialogs:show_dialog("Amdroid app Next Alarm widget", "This script wrapper uses Amdroid app's Next alarm widget to display the next scheduled alarm. No settings are required.")
end
function setup_app_widget()
local id = widgets:setup("com.amdroidalarmclock.amdroid/com.amdroidalarmclock.amdroid.widgets.NextAlarmWidgetProvider")
if (id ~= nil) then
prefs.wid = id
else
ui:show_text("Can't add widget")
end
end

View File

@@ -1,360 +0,0 @@
-- name = "My apps"
-- description = "Simple apps widget"
-- type = "widget"
-- version = "1.0"
-- author = "Andrey Gavrilov"
local utf8 = require "utf8"
local dialog_id = ""
local app_idx = 0
function on_resume()
update_args()
local folding = false
if settings:get_kv()["folding"] == "true" then
folding = true
end
ui:set_folding_flag(folding)
redraw()
end
function on_alarm()
local args = settings:get_kv()
if next(args) == nil then
args["no_hidden"] = true
args["columns"] = 4
args["trim"] = 10
args["folding"] = false
args["1"] = "com.android.settings"
settings:set_kv(args)
redraw()
end
end
function on_settings()
dialog_id = "settings"
local tab = {"Applications list","No hidden setting","Columns number","Trim app name","Autofolding setting","Widget title"}
ui:show_radio_dialog("Settings",tab)
end
function on_dialog_action(data)
if data == -1 then
return
end
if dialog_id == "settings" then
if data == 1 then
dialog_id = "apps"
local tab = get_all_apps("abc",settings:get_kv()["no_hidden"])
ui:show_checkbox_dialog("Select apps",tab[2],args_to_idx(tab[1]))
return
elseif data == 2 then
dialog_id = "no_hidden"
local tab = {"Not show hidden applications"}
local tt = {}
if tostring(settings:get_kv()["no_hidden"]) == "true" then
tt = {1}
end
ui:show_checkbox_dialog("No hidden settings",tab,tt)
return
elseif data == 3 then
dialog_id = "columns"
ui:show_edit_dialog("Columns number","",settings:get_kv()["columns"])
return
elseif data == 4 then
dialog_id = "trim"
ui:show_edit_dialog("Trim app name","0 - not trim",settings:get_kv()["trim"])
return
elseif data == 5 then
dialog_id = "folding"
local tab = {"Autofolding"}
local tt = {}
if tostring(settings:get_kv()["folding"]) == "true" then
tt = {1}
end
ui:show_checkbox_dialog("Autofolding settings",tab,tt)
return
elseif data == 6 then
dialog_id = "name"
ui:show_edit_dialog("Set widget title","Empty - default title",ui:get_default_title())
return
end
elseif dialog_id == "no_hidden" then
local args = settings:get_kv()
if next(data) == nil then
args["no_hidden"] = false
else
args["no_hidden"] = true
end
settings:set_kv(args)
update_args()
redraw()
return
elseif dialog_id == "apps" then
settings:set_kv(idx_to_args(data))
redraw()
return
elseif dialog_id == "columns" then
if data == tostring(tonumber(data)) then
if tonumber(data) == 0 then
data = "1"
end
local args = settings:get_kv()
args["columns"] = math.floor(tonumber(data))
settings:set_kv(args)
update_args()
redraw()
end
return
elseif dialog_id == "trim" then
if data == tostring(tonumber(data)) then
local args = settings:get_kv()
args["trim"] = math.floor(tonumber(data))
settings:set_kv(args)
update_args()
redraw()
end
return
elseif dialog_id == "folding" then
local args = settings:get_kv()
if next(data) == nil then
args["folding"] = false
else
args["folding"] = true
end
settings:set_kv(args)
update_args()
redraw()
return
elseif dialog_id == "name" then
local title = ui:get_default_title()
if data ~= "" then
title = data
end
ui:set_title(title)
return
elseif dialog_id == "move" then
if data == tostring(tonumber(data)) then
local idx = math.floor(tonumber(data))
local args = settings:get_kv()
if idx < 1 then
idx = 1
elseif idx > max_key(args) then
idx = max_key(args)
end
local from = args[tostring(app_idx)]
local to = args[tostring(idx)]
args[tostring(app_idx)] = to
args[tostring(idx)] = from
settings:set_kv(args)
update_args()
redraw()
return
end
end
end
function on_context_menu_click(idx)
if idx == 4 then
apps:show_edit_dialog(settings:get_kv()[tostring(app_idx)])
elseif idx == 1 then
local args = settings:get_kv()
if app_idx == max_key(args) then
return
end
local from = args[tostring(app_idx)]
local to = args[tostring(app_idx+1)]
args[tostring(app_idx)] = to
args[tostring(app_idx+1)] = from
settings:set_kv(args)
update_args()
redraw()
return
elseif idx == 3 then
local args = settings:get_kv()
if app_idx == 1 then
return
end
local from = args[tostring(app_idx)]
local to = args[tostring(app_idx-1)]
args[tostring(app_idx)] = to
args[tostring(app_idx-1)] = from
settings:set_kv(args)
update_args()
redraw()
return
elseif idx == 2 then
local args = settings:get_kv()
local new_args = {}
for k,v in pairs(args) do
if k ~= tostring(app_idx) then
new_args[k] = v
end
end
settings:set_kv(new_args)
update_args()
redraw()
return
elseif idx == 5 then
dialog_id = "move"
local text = "Number from 1 to "..tostring(max_key(settings:get_kv()))
ui:show_edit_dialog("Set position",text,app_idx)
return
end
end
function redraw()
local cols = tonumber(settings:get_kv()["columns"])
if cols == 0 or cols == nil then
cols = 1
end
ui:show_table(table_to_tables(tab_from_args(), cols))
end
function on_click(idx)
apps:launch(settings:get_kv()[tostring(idx)])
end
function on_long_click(idx)
app_idx = idx
ui:show_context_menu({
{ "chevron-right", "Forward" },
{ "times", "Remove" },
{ "chevron-left", "Back" },
{ "edit", "Edit" },
{ "exchange", "Move" }
})
end
function tab_from_args()
local args = settings:get_kv()
local len = tonumber(args["trim"])
if len == nil then
len = 0
end
local tab = {}
for k,v in pairs(args) do
if k == tostring(tonumber(k)) then
tab[tonumber(k)] = get_formatted_name(v,len)
end
end
return tab
end
function get_formatted_name(pkg,len)
local str = apps:get_name(pkg)
if utf8.len(str) > len and len > 0 then
str = utf8.sub(str,1,len-1):gsub("[%. ]*$","").."."
end
return "<font color=\""..apps:get_color(pkg).."\">"..str.."</font>"
end
function table_to_tables(tab, num)
local out_tab = {}
local row = {}
for k,v in ipairs(tab) do
table.insert(row, v)
if k % num == 0 then
table.insert(out_tab, row)
row = {}
end
end
if row ~= {} then
table.insert(out_tab, row)
end
return out_tab
end
function get_all_apps(sort_by,no_hidden)
if tostring(no_hidden) == "true" then
no_hidden = true
else
no_hidden = false
end
local t = settings:get_kv()
local all_apps = apps:get_list(sort_by,no_hidden)
local apps_names = {}
for k,v in ipairs(all_apps) do
apps_names[k] = apps:get_name(v)
end
return {all_apps,apps_names}
end
function args_to_idx(tab)
local args = settings:get_kv()
local t = {}
for k,v in pairs(args) do
local idx = get_index(tab,v)
if idx > 0 then
table.insert(t,idx)
end
end
return t
end
function idx_to_args(tab)
local args = settings:get_kv()
local all_apps = get_all_apps("abc",args["no_hidden"])[1]
local new_args = {}
for i,v in ipairs(tab) do
if get_key(args,all_apps[tonumber(v)]) == 0 then
new_args[tostring(max_key(args)+i)] = all_apps[tonumber(v)]
else
new_args[get_key(args,all_apps[tonumber(v)])] = all_apps[tonumber(v)]
end
end
new_args = sort_by_key(new_args)
new_args["no_hidden"] = args["no_hidden"]
new_args["columns"] = args["columns"]
new_args["trim"] = args["trim"]
new_args["folding"] = args["folding"]
return new_args
end
function update_args()
local args = settings:get_kv()
local all_apps = get_all_apps("abc",args["no_hidden"])[1]
local new_args = {}
for k,v in pairs(args) do
if k == tostring(tonumber(k)) then
if get_index(all_apps,v) ~= 0 then
new_args[k] = v
end
end
end
new_args = sort_by_key(new_args)
new_args["no_hidden"] = args["no_hidden"]
new_args["columns"] = args["columns"]
new_args["trim"] = args["trim"]
new_args["folding"] = args["folding"]
settings:set_kv(new_args)
end
function sort_by_key(tab)
local t = {}
local tt = {}
for k,v in pairs(tab) do
table.insert(t,tonumber(k))
end
table.sort(t)
for i,v in ipairs(t) do
for kk,vv in pairs(tab) do
if kk == tostring(v) then
tt[tostring(i)] = vv
break
end
end
end
return tt
end
function max_key(tab)
local t = {}
for k,v in pairs(tab) do
if k == tostring(tonumber(k)) then
table.insert(t,k)
end
end
table.sort(t)
return #t
end

View File

@@ -0,0 +1,66 @@
-- name = "Армянский алфавит"
-- description = "Армянский алфавит с транскрипцией"
-- type = "widget"
-- author = "Evgeny Zobnin (zobnin@gmail.com)"
-- version = "1.0"
-- lang = "ru"
local tab = {
{
"<b>Աա</b>", "А",
"<b>Բբ</b>", "Б",
"<b>Գգ</b>", "Г",
"<b>Դդ</b>", "Д",
"<b>Եե</b>", "Е",
"<b>Զզ</b>", "З",
},
{
"<b>Էէ</b>", "Э",
"<b>Ըը</b>", "Ы",
"<b>Թթ</b>", "Тh",
"<b>Ժժ</b>", "Ж",
"<b>Իի</b>", "И",
"<b>Լլ</b>", "Ль",
},
{
"<b>Խխ</b>", "Х",
"<b>Ծծ</b>", "Ц",
"<b>Կկ</b>", "K",
"<b>Հհ</b>", "h",
"<b>Ձձ</b>", "Дз",
"<b>Ղղ</b>", "Гг",
},
{
"<b>Ճճ</b>", "Ч",
"<b>Մմ</b>", "М",
"<b>Յյ</b>", "Й",
"<b>Նն</b>", "Н",
"<b>Շշ</b>", "Ш",
"<b>Ոո</b>", "О",
},
{
"<b>Չչ</b>", "Ч",
"<b>Պպ</b>", "П",
"<b>Ջջ</b>", "Дж",
"<b>Ռռ</b>", "Рр",
"<b>Սս</b>", "С",
"<b>Վվ</b>", "В",
},
{
"<b>Տտ</b>", "Т",
"<b>Րր</b>", "Р",
"<b>Ցց</b>", "Цh",
"<b>Ու</b>", "У",
"<b>Փփ</b>", "Пh",
"<b>Քք</b>", "Kh",
},
{
"<b>և</b>", "Ев",
"<b>Oo</b>", "О",
"<b>Ֆֆ</b>", "Ф",
}
}
function on_resume()
ui:show_table(tab)
end

View File

@@ -4,38 +4,40 @@
-- type = "search" -- type = "search"
-- lang = "ru" -- lang = "ru"
-- author = "Andrey Gavrilov" -- author = "Andrey Gavrilov"
-- version = "1.0" -- version = "1.1"
local num = ""
function on_search(input) function on_search(input)
num = input:match("^n (.+)") local num = input:match("^(+?7?%d%d%d%d%d%d%d%d%d%d+)$")
if not num then if not num then
return return
end end
search:show({"Оператор "..num}) show_operator(num)
end end
function on_click() function show_operator(num)
local json = require "json"
local uri = "http://rosreestr.subnets.ru/?get=num&format=json&num=" .. num:gsub("%D", "") local uri = "http://rosreestr.subnets.ru/?get=num&format=json&num=" .. num:gsub("%D", "")
http:get(uri) local tab = {}
return false local result = shttp:get(uri)
end if not result.error then
local t = json.decode(result.body)
function on_network_result(result) if not t.error then
local json = require "json" if not t["0"].country then
local t = json.decode(result) if not t["0"].moved2operator then
if not t.error then table.insert(tab, t["0"].operator)
if not t["0"].country then else
if not t["0"].moved2operator then table.insert(tab, t["0"].moved2operator)
search:show({t["0"].operator, t["0"].region}) end
table.insert(tab, t["0"].region)
else else
search:show({t["0"].moved2operator, t["0"].region}) table.insert(tab, t["0"].country)
table.insert(tab, t["0"].description)
end end
else else
search:show({t["0"].country, t["0"].description}) table.insert(tab, t.error)
end end
else else
search:show({t.error}) table.insert(tab, result.error)
end end
search:show_lines({table.concat(tab, ", ")}, {aio:colors().button})
end end

313
community/bible-search.lua Normal file
View File

@@ -0,0 +1,313 @@
-- name = "Bible Search"
-- type = "search"
-- author = "Evon Smith"
-- description = "Type in Bible book and chapter to open the YouVersion Bible App to that location right from the search window"
-- version = "2.0"
------------------------------------------------------------
-- Complete Bible books list with aliases and chapters
------------------------------------------------------------
local BOOKS = {
{ name = "Genesis", aliases = {"genesis","gen","ge"}, chapters = 50, osis = "GEN" },
{ name = "Exodus", aliases = {"exodus","exo","ex"}, chapters = 40, osis = "EXO" },
{ name = "Leviticus", aliases = {"leviticus","lev","le"}, chapters = 27, osis = "LEV" },
{ name = "Numbers", aliases = {"numbers","num","nu","nm"}, chapters = 36, osis = "NUM" },
{ name = "Deuteronomy", aliases = {"deuteronomy","deut","dt"}, chapters = 34, osis = "DEU" },
{ name = "Joshua", aliases = {"joshua","josh","jos"}, chapters = 24, osis = "JOS" },
{ name = "Judges", aliases = {"judges","judg","jdg"}, chapters = 21, osis = "JDG" },
{ name = "Ruth", aliases = {"ruth","ru"}, chapters = 4, osis = "RUT" },
{ name = "1 Samuel", aliases = {"1samuel","1sam","1sa","i samuel","1 sam"}, chapters = 31, osis = "1SA" },
{ name = "2 Samuel", aliases = {"2samuel","2sam","2sa","ii samuel","2 sam"}, chapters = 24, osis = "2SA" },
{ name = "1 Kings", aliases = {"1kings","1ki","i kings","1 ki"}, chapters = 22, osis = "1KI" },
{ name = "2 Kings", aliases = {"2kings","2ki","ii kings","2 ki"}, chapters = 25, osis = "2KI" },
{ name = "1 Chronicles", aliases = {"1chronicles","1chr","i chronicles","1 chr"}, chapters = 29, osis = "1CH" },
{ name = "2 Chronicles", aliases = {"2chronicles","2chr","ii chronicles","2 chr"}, chapters = 36, osis = "2CH" },
{ name = "Ezra", aliases = {"ezra","ezr"}, chapters = 10, osis = "EZR" },
{ name = "Nehemiah", aliases = {"nehemiah","neh","ne"}, chapters = 13, osis = "NEH" },
{ name = "Esther", aliases = {"esther","est","es"}, chapters = 10, osis = "EST" },
{ name = "Job", aliases = {"job","jb"}, chapters = 42, osis = "JOB" },
{ name = "Psalms", aliases = {"psalms","psalm","ps","psa"}, chapters = 150, osis = "PSA" },
{ name = "Proverbs", aliases = {"proverbs","prov","prv","pr"}, chapters = 31, osis = "PRO" },
{ name = "Ecclesiastes", aliases = {"ecclesiastes","eccl","ecc"}, chapters = 12, osis = "ECC" },
{ name = "Song of Solomon", aliases = {"songofsolomon","song","so","ss","song of songs","songs"}, chapters = 8, osis = "SNG" },
{ name = "Isaiah", aliases = {"isaiah","isa","is"}, chapters = 66, osis = "ISA" },
{ name = "Jeremiah", aliases = {"jeremiah","jer","je"}, chapters = 52, osis = "JER" },
{ name = "Lamentations", aliases = {"lamentations","lam","la"}, chapters = 5, osis = "LAM" },
{ name = "Ezekiel", aliases = {"ezekiel","ezek","eze"}, chapters = 48, osis = "EZK" },
{ name = "Daniel", aliases = {"daniel","dan","da"}, chapters = 12, osis = "DAN" },
{ name = "Hosea", aliases = {"hosea","hos","ho"}, chapters = 14, osis = "HOS" },
{ name = "Joel", aliases = {"joel","jl"}, chapters = 3, osis = "JOL" },
{ name = "Amos", aliases = {"amos","am"}, chapters = 9, osis = "AMO" },
{ name = "Obadiah", aliases = {"obadiah","obad","ob"}, chapters = 1, osis = "OBA" },
{ name = "Jonah", aliases = {"jonah","jon","jh"}, chapters = 4, osis = "JON" },
{ name = "Micah", aliases = {"micah","mic","mc"}, chapters = 7, osis = "MIC" },
{ name = "Nahum", aliases = {"nahum","nah","na"}, chapters = 3, osis = "NAM" },
{ name = "Habakkuk", aliases = {"habakkuk","hab","hb"}, chapters = 3, osis = "HAB" },
{ name = "Zephaniah", aliases = {"zephaniah","zeph","zep"}, chapters = 3, osis = "ZEP" },
{ name = "Haggai", aliases = {"haggai","hag","hg"}, chapters = 2, osis = "HAG" },
{ name = "Zechariah", aliases = {"zechariah","zech","zec"}, chapters = 14, osis = "ZEC" },
{ name = "Malachi", aliases = {"malachi","mal","ml"}, chapters = 4, osis = "MAL" },
{ name = "Matthew", aliases = {"matthew","matt","mt"}, chapters = 28, osis = "MAT" },
{ name = "Mark", aliases = {"mark","mk","mrk"}, chapters = 16, osis = "MRK" },
{ name = "Luke", aliases = {"luke","lk","luk"}, chapters = 24, osis = "LUK" },
{ name = "John", aliases = {"john","jn","jhn"}, chapters = 21, osis = "JHN" },
{ name = "Acts", aliases = {"acts","ac","acts of the apostles"}, chapters = 28, osis = "ACT" },
{ name = "Romans", aliases = {"romans","rom","ro"}, chapters = 16, osis = "ROM" },
{ name = "1 Corinthians", aliases = {"1corinthians","1cor","1co","i corinthians","1 cor"}, chapters = 16, osis = "1CO" },
{ name = "2 Corinthians", aliases = {"2corinthians","2cor","2co","ii corinthians","2 cor"}, chapters = 13, osis = "2CO" },
{ name = "Galatians", aliases = {"galatians","gal","ga"}, chapters = 6, osis = "GAL" },
{ name = "Ephesians", aliases = {"ephesians","eph","ep"}, chapters = 6, osis = "EPH" },
{ name = "Philippians", aliases = {"philippians","phil","php"}, chapters = 4, osis = "PHP" },
{ name = "Colossians", aliases = {"colossians","col","co"}, chapters = 4, osis = "COL" },
{ name = "1 Thessalonians", aliases = {"1thessalonians","1thess","1th","i thessalonians","1 thess"}, chapters = 5, osis = "1TH" },
{ name = "2 Thessalonians", aliases = {"2thessalonians","2thess","2th","ii thessalonians","2 thess"}, chapters = 3, osis = "2TH" },
{ name = "1 Timothy", aliases = {"1timothy","1tim","1ti","i timothy","1 tim"}, chapters = 6, osis = "1TI" },
{ name = "2 Timothy", aliases = {"2timothy","2tim","2ti","ii timothy","2 tim"}, chapters = 4, osis = "2TI" },
{ name = "Titus", aliases = {"titus","tit","ti"}, chapters = 3, osis = "TIT" },
{ name = "Philemon", aliases = {"philemon","philem","phm"}, chapters = 1, osis = "PHM" },
{ name = "Hebrews", aliases = {"hebrews","heb","he"}, chapters = 13, osis = "HEB" },
{ name = "James", aliases = {"james","jas","jm"}, chapters = 5, osis = "JAS" },
{ name = "1 Peter", aliases = {"1peter","1pet","1pe","i peter","1 pet"}, chapters = 5, osis = "1PE" },
{ name = "2 Peter", aliases = {"2peter","2pet","2pe","ii peter","2 pet"}, chapters = 3, osis = "2PE" },
{ name = "1 John", aliases = {"1john","1jn","i john","1 jn"}, chapters = 5, osis = "1JN" },
{ name = "2 John", aliases = {"2john","2jn","ii john","2 jn"}, chapters = 1, osis = "2JN" },
{ name = "3 John", aliases = {"3john","3jn","iii john","3 jn"}, chapters = 1, osis = "3JN" },
{ name = "Jude", aliases = {"jude","jud"}, chapters = 1, osis = "JUD" },
{ name = "Revelation", aliases = {"revelation","rev","re","revelations"}, chapters = 22, osis = "REV" },
}
-- Build alias lookup table
local ALIAS = {}
local function _norm(s) return (s or ""):lower():gsub("[%s%p]+","") end
for _, b in ipairs(BOOKS) do
ALIAS[_norm(b.name)] = b
for _, a in ipairs(b.aliases) do ALIAS[_norm(a)] = b end
end
------------------------------------------------------------
-- State management
------------------------------------------------------------
local suggestions = {} -- Store all clickable suggestions with their URLs
local currentQuery = ""
------------------------------------------------------------
-- Helper functions
------------------------------------------------------------
local function trim(s) return (s or ""):match("^%s*(.-)%s*$") end
-- Enhanced parser that handles multi-word book names better
local function parse_book_chapter(q)
q = trim(q or "")
if q == "" then return nil, nil end
-- Try to match book name with optional chapter
-- This pattern allows for multi-word book names
local book_part, chapter_part = q:match("^(.-)%s+(%d+)$")
if book_part and chapter_part then
-- Found both book and chapter
return trim(book_part), tonumber(chapter_part)
else
-- No chapter number found, treat entire query as book name
return q, nil
end
end
-- Enhanced book resolver for better multi-word matching
local function resolve_book(letters)
if not letters then return nil end
local key = _norm(letters)
-- Exact match first
if ALIAS[key] then return ALIAS[key] end
-- Try partial matching for better multi-word support
local matches = {}
for ak, b in pairs(ALIAS) do
if ak:find("^" .. key) then
table.insert(matches, {alias = ak, book = b, priority = 1})
elseif ak:find(key, 1, true) then
table.insert(matches, {alias = ak, book = b, priority = 2})
end
end
-- Return best match if found
if #matches > 0 then
table.sort(matches, function(a, b)
if a.priority == b.priority then
return #a.alias < #b.alias -- Prefer shorter matches
end
return a.priority < b.priority
end)
return matches[1].book
end
return nil
end
local function build_youversion_url(osis, chapter)
return string.format("youversion://bible?reference=%s.%d", osis, chapter)
end
-- Get matching books based on query
local function get_matching_books(query)
if not query or query == "" then
return BOOKS -- Return all books if no query
end
local matches = {}
local key = _norm(query)
for _, book in ipairs(BOOKS) do
local book_key = _norm(book.name)
local is_match = false
-- Check if book name starts with query
if book_key:find("^" .. key) then
is_match = true
else
-- Check aliases
for _, alias in ipairs(book.aliases) do
if _norm(alias):find("^" .. key) then
is_match = true
break
end
end
end
if is_match then
table.insert(matches, book)
end
end
-- If no matches found with prefix matching, try substring matching
if #matches == 0 then
for _, book in ipairs(BOOKS) do
local book_key = _norm(book.name)
if book_key:find(key, 1, true) then
table.insert(matches, book)
end
end
end
return #matches > 0 and matches or BOOKS
end
------------------------------------------------------------
-- Main search function
------------------------------------------------------------
function on_search(query)
currentQuery = query
suggestions = {}
local book_part, chapter = parse_book_chapter(query)
if book_part then
local specific_book = resolve_book(book_part)
if specific_book and chapter then
-- Specific book and chapter requested
if chapter >= 1 and chapter <= specific_book.chapters then
local url = build_youversion_url(specific_book.osis, chapter)
suggestions[1] = {
text = string.format("Open %s %d in YouVersion", specific_book.name, chapter),
url = url
}
search:show_buttons({suggestions[1].text})
return
else
search:show_lines({
string.format("%s only has %d chapter%s",
specific_book.name,
specific_book.chapters,
specific_book.chapters == 1 and "" or "s")
})
return
end
end
end
-- Show book/chapter suggestions
local matching_books = get_matching_books(book_part)
local button_texts = {}
local max_suggestions = 10 -- Limit number of suggestions shown
local suggestion_count = 0
for _, book in ipairs(matching_books) do
if suggestion_count >= max_suggestions then break end
-- If book has few chapters, show all in one line
if book.chapters <= 5 then
local chapters_text = ""
for ch = 1, book.chapters do
if ch > 1 then chapters_text = chapters_text .. ", " end
chapters_text = chapters_text .. ch
end
suggestion_count = suggestion_count + 1
local idx = #suggestions + 1
suggestions[idx] = {
text = string.format("%s (Ch: %s)", book.name, chapters_text),
book = book,
all_chapters = true
}
table.insert(button_texts, suggestions[idx].text)
else
-- For books with many chapters, show range
suggestion_count = suggestion_count + 1
local idx = #suggestions + 1
suggestions[idx] = {
text = string.format("%s (1-%d chapters)", book.name, book.chapters),
book = book,
all_chapters = true
}
table.insert(button_texts, suggestions[idx].text)
end
end
if #button_texts == 0 then
search:show_lines({"No matching books found. Try: Genesis, John, Psalms..."})
else
search:show_buttons(button_texts)
end
end
------------------------------------------------------------
-- Handle button clicks
------------------------------------------------------------
function on_click(index)
if not index then index = 1 end
local suggestion = suggestions[index]
if not suggestion then return false end
-- If it's a direct chapter link, open it
if suggestion.url then
system:open_browser(suggestion.url)
return true
end
-- If it's a book suggestion, show its chapters
if suggestion.book and suggestion.all_chapters then
local book = suggestion.book
suggestions = {}
local button_texts = {}
-- Create buttons for each chapter
for ch = 1, book.chapters do
local idx = #suggestions + 1
local url = build_youversion_url(book.osis, ch)
suggestions[idx] = {
text = string.format("%s %d", book.name, ch),
url = url
}
table.insert(button_texts, suggestions[idx].text)
end
search:show_buttons(button_texts)
return false -- Don't close search, allow chapter selection
end
return false
end

View File

@@ -0,0 +1,116 @@
-- name = "Birthdays"
-- name_id = "birthday"
-- description = "Shows upcoming birthdays from the contacts"
-- type = "widget"
-- author = "Andrey Gavrilov"
-- version = "1.3"
local prefs = require "prefs"
local fmt = require "fmt"
local months = {
"January",
"February",
"March",
"April",
"May",
"June",
"July",
"August",
"September",
"October",
"November",
"December"
}
function on_resume()
if not prefs.count then
prefs.count = 10
end
phone:request_permission()
end
function on_permission_granted()
contacts = calendar:contacts_events()
redraw()
end
function on_contacts_loaded()
redraw()
end
function redraw()
table.sort(contacts,function(a,b) return a.begin < b.begin end)
events = {}
local lines = {}
local lines_exp = {}
prev_begin = contacts[1].begin
for i,v in ipairs(contacts) do
local fmt_out = fmt_line(v)
local insert = 0
if #lines == 0 then
insert = 1
elseif not (fmt_out == lines[#lines]) then
insert = 1
end
if insert == 1 then
if #lines_exp >= prefs.count and prev_begin ~= v.begin then
break
end
table.insert(events, v)
if #lines < prefs.count then
table.insert(lines, fmt_out)
end
table.insert(lines_exp, fmt_out)
prev_begin = v.begin
end
end
if ui.set_expandable then
if #lines_exp == #lines then
ui:set_expandable(false)
else
ui:set_expandable(true)
end
end
if ui.is_expanded and ui:is_expanded() then
ui:show_lines(lines_exp)
else
ui:show_lines(lines)
end
end
function fmt_line(event)
local line = event.title
if os.date("%y%m%d",event.begin) == os.date("%y%m%d") then
line = fmt.bold(fmt.colored(line, aio:colors().accent))
end
return line .. fmt.secondary(" - ") .. fmt_date(event.begin)
end
function fmt_date(date)
local d = ""
if os.date("%y%m%d",date) == os.date("%y%m%d") then
d = "Today"
elseif os.date("%y%m%d",date-86400) == os.date("%y%m%d") then
d = "Tomorrow"
else
d = months[tonumber(os.date("%m", date))] .. ", " .. tostring(tonumber(os.date("%d", date)))
end
return fmt.secondary(d)
end
function on_click(idx)
calendar:show_event_dialog(events[idx])
end
function on_settings()
dialogs:show_radio_dialog("Number of events", {1,2,3,4,5,6,7,8,9,10}, prefs.count)
end
function on_dialog_action(idx)
if idx == -1 then
return
end
prefs.count = idx
redraw()
end

View File

@@ -0,0 +1,89 @@
-- name = "Calendar Progress"
-- description = "Shows day, week, month, and year progress bars"
-- type = "widget"
-- author = "meluskyc"
-- version = "1.0"
-- foldable = "false"
local prefs = require "prefs"
function on_load()
prefs.show_day = true
prefs.show_week = true
prefs.show_month = true
prefs.show_year = true
end
function on_resume()
local now = os.date("*t", os.time())
local gui_elems = {}
if prefs.show_day then
local seconds_since_day_start =
now.sec + -- current minute
now.min * 60 + -- previous minutes
now.hour * 3600 -- previous hours
local progress_percentage = math.floor((seconds_since_day_start / (24 * 60 * 60)) * 100)
local label = "Day: " .. progress_percentage .. "%"
table.insert(gui_elems, {"progress", label, {progress = progress_percentage}})
if prefs.show_week or prefs.show_month or prefs.show_year then
table.insert(gui_elems, {"new_line", 1})
end
end
if prefs.show_week then
local seconds_since_week_start =
now.sec + -- current minute
now.min * 60 + -- previous minutes
now.hour * 3600 + -- previous hours
(now.wday - 1) * 86400 -- previous days
local progress_percentage = math.floor((seconds_since_week_start / (7 * 24 * 60 * 60)) * 100)
local label = "Week: " .. progress_percentage .. "%"
table.insert(gui_elems, {"progress", label, {progress = progress_percentage}})
if prefs.show_month or prefs.show_year then
table.insert(gui_elems, {"new_line", 1})
end
end
if prefs.show_month then
local days_in_month = os.date("*t", os.time{year=now.year, month=now.month+1, day=0}).day
local seconds_since_month_start =
now.sec + -- current minute
now.min * 60 + -- previous minutes
now.hour * 3600 + -- previous hours
(now.day - 1) * 86400 -- previous days
local progress_percentage = math.floor((seconds_since_month_start / (days_in_month * 24 * 60 * 60)) * 100)
local label = "Month: " .. progress_percentage .. "%"
table.insert(gui_elems, {"progress", label, {progress = progress_percentage}})
if prefs.show_year then
table.insert(gui_elems, {"new_line", 1})
end
end
if prefs.show_year then
local seconds_since_year_start =
now.sec + -- current minute
now.min * 60 + -- previous minutes
now.hour * 3600 + -- previous hours
(now.yday - 1) * 86400 -- previous days
-- check for leap year
local days_in_year = 365
if (now.year % 4 == 0 and now.year % 100 ~= 0) or (now.year % 400 == 0) then
days_in_year = 366
end
local progress_percentage = math.floor((seconds_since_year_start / (days_in_year * 24 * 60 * 60)) * 100)
local label = "Year: " .. progress_percentage .. "%"
table.insert(gui_elems, {"progress", label, {progress = progress_percentage}})
end
gui(gui_elems).render()
end
function on_settings()
prefs:show_dialog()
end

View File

@@ -2,7 +2,7 @@
-- description = "Скрипт показывает историю вызов через прямое чтение базы звонков" -- description = "Скрипт показывает историю вызов через прямое чтение базы звонков"
-- type = "widget" -- type = "widget"
-- author = "Andrey Gavrilov" -- author = "Andrey Gavrilov"
-- version = "1.0" -- version = "1.1"
-- lang = "ru" -- lang = "ru"
-- root = "true" -- root = "true"
@@ -77,7 +77,7 @@ end
function on_click(idx) function on_click(idx)
if math.ceil(idx/3) > #tab then if math.ceil(idx/3) > #tab then
ui:show_radio_dialog("Выберите тип вызовов",types,typ) dialogs:show_radio_dialog("Выберите тип вызовов",types,typ)
else else
local cmd = "am start -a android.intent.action.DIAL -d tel:"..tab[math.ceil(idx/3)].number local cmd = "am start -a android.intent.action.DIAL -d tel:"..tab[math.ceil(idx/3)].number
system:exec(cmd) system:exec(cmd)

View File

@@ -0,0 +1,41 @@
-- name = "Chrome shortcuts"
-- description = "AIO wrapper for the Chrome shortcuts app widget"
-- type = "widget"
-- author = "Evgeny Zobnin (zobnin@gmail.com)"
-- version = "1.0"
-- foldable = "false"
-- uses_app: "com.android.chrome"
local prefs = require "prefs"
prefs._name = "chrome"
local buttons_labels = {"fa:magnifying-glass", "fa:microphone", "fa:hat_cowboy_side", "fa:camera", "fa:gamepad"}
local buttons_targets = {"h_layout_2", "image_2", "image_3", "image_4", "image_5"}
local w_bridge = nil
function on_resume()
if not widgets:bound(prefs.wid) then
setup_app_widget()
end
widgets:request_updates(prefs.wid)
end
function on_app_widget_updated(bridge)
w_bridge = bridge
ui:show_buttons(buttons_labels)
end
function on_click(idx)
w_bridge:click(buttons_targets[idx])
end
function setup_app_widget()
local id = widgets:setup("com.android.chrome/org.chromium.chrome.browser.quickactionsearchwidget.QuickActionSearchWidgetProvider$QuickActionSearchWidgetProviderSearch")
if (id ~= nil) then
prefs.wid = id
else
ui:show_text("Can't add widget")
return
end
end

View File

@@ -2,12 +2,14 @@
-- description = "Time counting widget to fight bad habits" -- description = "Time counting widget to fight bad habits"
-- type = "widget" -- type = "widget"
-- author = "Evgeny Zobnin (zobnin@gmail.com)" -- author = "Evgeny Zobnin (zobnin@gmail.com)"
-- arguments_help = "Enter the date of the start of counting in the format DD.MM.YYYY" -- version = "2.0"
-- version = "1.0" -- on_resume_when_folding = "true"
local prefs = require "prefs"
local date = require "date" local date = require "date"
-- constants -- constants
local max_counters = 5
local year = 365.25 local year = 365.25
local month = 30.43 local month = 30.43
@@ -18,44 +20,129 @@ local milestones = {
} }
local milestones_formatted = { local milestones_formatted = {
"1 day", "3 days", "1 week", "2 weeks", "1+ days", "3+ days", "1+ weeks", "2+ weeks",
"1 months", "3 months", "6 months", "1+ months", "3+ months", "6+ months",
"1 year", "3 years", "5 years", "10 years", "20 years", "100 years" "1+ years", "3+ years", "5+ years", "10+ years", "20+ years", "100+ years"
} }
function on_resume() function on_load()
local args = settings:get() init_default_settings()
end
if next(args) == nil then function on_resume()
ui:show_text("Tap to enter date") local gui_inst = {}
return local lines_num = max_counters
if ui:folding_flag() then
lines_num = 1
end
for i = 1, lines_num do
local title, start_date = parse_settings_string(i)
if title == nil then
break
end
local curr_date = date()
local passed = date.diff(curr_date, start_date)
local passed_days = math.floor(passed:spandays())
if passed_days < 0 then
table.insert(gui_inst, {"text", "Error: the date can't be in the future"})
break
end
local idx = get_milestone_idx(passed)
local passed_str = passed_days.." days"
if prefs.show_milestones and passed_days > 0 then
passed_str = passed_str.." / "..milestones_formatted[idx]
end
local next_milestone_percent = passed_days / milestones[idx+1] * 100
table.insert(gui_inst, {"progress", title..": "..passed_str, {progress = next_milestone_percent}})
end
gui_inst = insert_between_elements(gui_inst, {"new_line", 1})
gui(gui_inst):render()
end
function init_default_settings()
if prefs.counter_1 == nil then
prefs["counter_1"] = "Sample / 31.01.2024"
for i = 2,max_counters do
prefs["counter_"..i] = ""
end
end
if prefs.show_milestones == nil then
prefs.show_milestones = true
end
end
function parse_settings_string(i)
local title_and_date = prefs["counter_"..i]
if title_and_date == nil or #title_and_date == 0 then
return nil
end
local splitted = title_and_date:split("/")
local title = trim(splitted[1])
if title == nil or #title == 0 then
return nil
end
local date_str = trim(splitted[2])
if date_str == nil or #date_str == 0 then
return nil
end
local arr = date_str:split("%.")
if arr == nil or #arr < 3 then
return nil
end end
local arr = args[1]:split("%.")
local start_date = date(arr[3], arr[2], arr[1]) local start_date = date(arr[3], arr[2], arr[1])
local curr_date = date() return title, start_date
local passed = date.diff(curr_date, start_date)
local passed_days = math.floor(passed:spandays())
local idx = get_milestone_idx(passed)
ui:show_progress_bar(passed_days.." days / "..milestones_formatted[idx],
passed_days, milestones[idx])
end end
function on_click() function on_click()
settings:show_dialog() prefs:show_dialog()
end
function on_settings()
prefs:show_dialog()
end end
-- utils -- utils
function trim(s)
if s == nil or #s == 0 then return s end
return s:match("^%s*(.-)%s*$")
end
function insert_between_elements(t, element)
local new_table = {}
for i = 1, #t do
table.insert(new_table, t[i])
if i < #t then
table.insert(new_table, element)
end
end
return new_table
end
function get_milestone_idx(passed) function get_milestone_idx(passed)
local days_passed = passed:spandays() local days_passed = passed:spandays()
local idx = 1 local idx = 0
for k,v in ipairs(milestones) do for k,v in ipairs(milestones) do
if days_passed > v then if days_passed > v then
idx = idx + 1 idx = idx + 1
else
break
end end
end end

2754
community/country-search.lua Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -1,105 +0,0 @@
-- name = "Currencies"
-- description = "Currency rates widget. Click on the date to change it."
-- data_source = "github.com/fawazahmed0/currency-api#readme"
-- type = "widget"
-- author = "Andrey Gavrilov"
-- version = "1.0"
-- arguments_help = "Enter the list of currency pairs in the format usd:rub btc:usd"
-- arguments_default = "usd:rub eur:rub"
json = require "json"
-- constants
local red_color = "#f44336"
local green_color = "#48ad47"
local text_color = ui:get_colors().secondary_text
local equals = "<font color=\""..text_color.."\"> = </font>"
-- global vars
local result_curr = ""
local tabl = {}
function on_resume()
ui:set_folding_flag(true)
ui:show_lines(tabl)
end
function on_alarm()
get_rates("latest", "curr")
end
function get_rates(loc_date,id)
http:get("https://cdn.jsdelivr.net/gh/fawazahmed0/currency-api@1/"..loc_date.."/currencies/usd.json",id)
end
function on_network_result_curr(result)
result_curr = result
local t = json.decode(result)
local dat = t.date
local prev_date = prev_date(dat)
get_rates(prev_date, "prev")
end
function on_network_result_prev(result)
tabl = create_tab(result)
ui:show_lines(tabl)
end
function on_click(idx)
ui:show_edit_dialog("Enter the date", "Enter the date in the format 2020.12.31. A blank value is the current date.")
end
function on_dialog_action(dat)
if dat == "" then dat = "latest" end
get_rates(dat:gsub(".", "-"), "curr")
end
function prev_date(dat)
local prev_date = dat:split("-")
local prev_time = os.time{year=prev_date[1], month=prev_date[2], day=prev_date[3]} - (60*60*24)
return os.date("%Y-%m-%d", prev_time)
end
function create_tab(result)
local curs = settings:get()
local tab = {}
local t_c = json.decode(result_curr)
local t_p = json.decode(result)
-- set title
local dat = t_c.date
ui:set_title(ui:get_default_title().." "..dat:gsub("-", "."))
for idx = 1, #curs, 1 do
local cur = curs[idx]:split(":")
local rate_curr1 = t_c.usd[cur[1]]
local rate_curr2 = t_c.usd[cur[2]]
local rate_prev1 = t_p.usd[cur[1]]
local rate_prev2 = t_p.usd[cur[2]]
local rate_curr = round(rate_curr2/rate_curr1, 4)
local rate_prev = round(rate_prev2/rate_prev1, 4)
local change = round((rate_curr-rate_prev)/rate_prev*100,2)
local line = "1 "..string.upper(cur[1])..equals..rate_curr.." "..string.upper(cur[2])
line = line..get_formatted_change_text(change)
table.insert(tab, line)
end
return tab
end
-- utils --
function get_formatted_change_text(change)
if change > 0 then
return "<font color=\""..green_color.."\"><small>&nbsp+"..change.."%</small></font>"
elseif change < 0 then
return "<font color=\""..red_color.."\"><small>&nbsp"..change.."%</small></font>"
else
return "<font color=\""..text_color.."\"><small>&nbsp"..change.."%</small></font>"
end
end

View File

@@ -47,7 +47,7 @@ function on_click()
http:get("https://www.cbr.ru/scripts/XML_daily.asp?date_req="..dat:replace("%.","/")) http:get("https://www.cbr.ru/scripts/XML_daily.asp?date_req="..dat:replace("%.","/"))
return false return false
else else
system:copy_to_clipboard(val) system:to_clipboard(val)
return true return true
end end
end end
@@ -61,10 +61,10 @@ function on_network_result(res)
return return
end end
end end
search:show({"Нет данных по валюте "..cur},{red}) search:show_buttons({"Нет данных по валюте "..cur},{red})
end end
function on_long_click() function on_long_click()
system:copy_to_clipboard(val) system:to_clipboard(val)
return true return true
end end

View File

@@ -1,71 +0,0 @@
-- name = "Exchange rates"
-- description = "Shows currency rate by code"
-- data_source = "https://api.exchangerate.host"
-- type = "search"
-- author = "Evgeny Zobbin & Andrey Gavrilov"
-- version = "1.0"
-- modules
local json = require "json"
local md_color = require "md_colors"
-- constants
local host = "https://api.exchangerate.host"
local red = md_colors.red_500
local base_currency = system:get_currency()
-- variables
local req_currency = ""
local req_amount = 1
local result = 0
function on_search(inp)
req_currency = ""
req_amount = 1
result = 0
local a,c = inp:match("^(%d*)%s?(%a%a%a)$")
if c == nil then return end
req_currency = c:upper()
if get_index(supported, req_currency) == 0 then
return
end
if a ~= "" then
req_amount = a
end
search:show({"Exchange rate for "..req_amount.." "..req_currency},{red})
end
function on_click()
if result == 0 then
http:get(host.."/convert?from="..req_currency.."&to="..base_currency.."&amount="..req_amount)
return false
else
system:copy_to_clipboard(result)
return true
end
end
function on_long_click()
system:copy_to_clipboard(result)
return true
end
function on_network_result(res)
local tab = json.decode(res)
if tab.success then
search:show({tab.query.amount.." "..tab.query.from.." = "..tab.result.." "..tab.query.to}, {red})
else
search:show({"No data"},{red})
end
end
-- curl -s -XGET 'https://api.exchangerate.host/symbols?format=csv' | cut -d ',' -f 2 | grep -v code | sed 's/$/,/' | tr '\n' ' '
supported = {
"AED", "AFN", "ALL", "AMD", "ANG", "AOA", "ARS", "AUD", "AWG", "AZN", "BAM", "BBD", "BDT", "BGN", "BHD", "BIF", "BMD", "BND", "BOB", "BRL", "BSD", "BTC", "BTN", "BWP", "BYN", "BZD", "CAD", "CDF", "CHF", "CLF", "CLP", "CNH", "CNY", "COP", "CRC", "CUC", "CUP", "CVE", "CZK", "DJF", "DKK", "DOP", "DZD", "EGP", "ERN", "ETB", "EUR", "FJD", "FKP", "GBP", "GEL", "GGP", "GHS", "GIP", "GMD", "GNF", "GTQ", "GYD", "HKD", "HNL", "HRK", "HTG", "HUF", "IDR", "ILS", "IMP", "INR", "IQD", "IRR", "ISK", "JEP", "JMD", "JOD", "JPY", "KES", "KGS", "KHR", "KMF", "KPW", "KRW", "KWD", "KYD", "KZT", "LAK", "LBP", "LKR", "LRD", "LSL", "LYD", "MAD", "MDL", "MGA", "MKD", "MMK", "MNT", "MOP", "MRO", "MRU", "MUR", "MVR", "MWK", "MXN", "MYR", "MZN", "NAD", "NGN", "NIO", "NOK", "NPR", "NZD", "OMR", "PAB", "PEN", "PGK", "PHP", "PKR", "PLN", "PYG", "QAR", "RON", "RSD", "RUB", "RWF", "SAR", "SBD", "SCR", "SDG", "SEK", "SGD", "SHP", "SLL", "SOS", "SRD", "SSP", "STD", "STN", "SVC", "SYP", "SZL", "THB", "TJS", "TMT", "TND", "TOP", "TRY", "TTD", "TWD", "TZS", "UAH", "UGX", "USD", "UYU", "UZS", "VEF", "VES", "VND", "VUV", "WST", "XAF", "XAG", "XAU", "XCD", "XDR", "XOF", "XPD", "XPF", "XPT", "YER", "ZAR", "ZMW", "ZWL",
}

View File

@@ -9,12 +9,12 @@ function on_resume()
end end
function on_network_result(result) function on_network_result(result)
joke = ajson:get_value(result, "object array:attachments object:0 string:text") joke = ajson:read(result, "object array:attachments object:0 string:text")
ui:show_text(joke) ui:show_text(joke)
end end
function on_click() function on_click()
if joke ~= nil then if joke ~= nil then
system:copy_to_clipboard(joke) system:to_clipboard(joke)
end end
end end

View File

@@ -0,0 +1,189 @@
-- name = "Electricity spot price"
-- description = "Day-ahead spot price of electricity"
-- data_source = "https://api.energy-charts.info/"
-- type = "widget"
-- foldable = "true"
-- author = "Hannu Hartikainen <hannu.hartikainen@gmail.com>"
-- version = "1.1"
json = require "json"
prefs = require "prefs"
url_base = "https://api.energy-charts.info/price?bzn=%s&end=%d"
price_data = nil
price_interval = nil
price_unit = nil
unit_multiplier = 1
next_fetch_ts = 0
next_redraw_ts = nil
-- parameters and default values
function on_load()
-- bidding zone: see "Available bidding zones" in https://api.energy-charts.info/
if not prefs.bidding_zone then
prefs.bidding_zone = "FI"
end
-- VAT percentage
if not prefs.vat_percentage then
prefs.vat_percentage = 25.5
end
-- change threshold percentages for showing changes in folded format
if not prefs.oneline_threshold_high then
prefs.oneline_threshold_high = 1.3
end
if not prefs.oneline_threshold_low then
prefs.oneline_threshold_low = 0.7
end
-- url to open when clicked
if not prefs.click_url then
prefs.click_url = "https://www.sahkonhintatanaan.fi/"
end
end
function get_url()
-- at most about 35 hours are known in advance; fetch all known prices
local end_offset = 2*24*60*60
local end_ts = os.time() + end_offset
return string.format(url_base, prefs.bidding_zone, end_ts)
end
function on_alarm()
if os.time() > next_fetch_ts then
http:get(get_url())
end
end
function on_network_result(result, code)
if code >= 200 and code < 299 then
parse_result(result)
draw_widget(true)
end
end
function on_tick(t)
local ts = os.time()
if next_redraw_ts and ts > next_redraw_ts then
draw_widget(true)
end
end
function on_click()
if not ui:folding_flag() then
system:open_browser(prefs.click_url)
else
draw_widget(false)
end
end
function parse_result(result)
price_data = json.decode(result)
local price_count = #price_data.unix_seconds
if price_count < 2 then
return
end
price_interval = price_data.unix_seconds[2] - price_data.unix_seconds[1]
if price_data.unit == "EUR/MWh" or price_data.unit == "EUR / megawatt_hour" then
price_unit = "c/kWh"
unit_multiplier = 0.1
else
price_unit = price_data.unit
unit_multiplier = 1
end
-- assume next day is known 8 hours before it starts
-- (eg. Nord Pool Spot typically publishes dayahead prices at 14 local time)
local next_fetch_offset = 8*60*60
local end_of_data_ts = price_data.unix_seconds[price_count] + price_interval
next_fetch_ts = end_of_data_ts - next_fetch_offset
end
function get_current_idx()
local t = os.time()
for i = 1, #price_data.unix_seconds do
local ts = price_data.unix_seconds[i]
if ts < t and t < (ts+price_interval) then
return i
end
end
end
function price(i)
return price_data.price[i]
end
function get_display_price(idx)
local mul = (1.0 + (prefs.vat_percentage / 100.0)) * unit_multiplier
-- NOTE: float rounding in string.format doesn't work so do it here
return math.floor(100.0 * price(idx) * mul + 0.5) / 100.0
end
function get_display_time(idx)
return os.date("%H", price_data.unix_seconds[idx])
end
function format_price(idx)
return string.format("%0.2f", get_display_price(idx))
end
function format_price_and_unit(idx)
return string.format("%s %s", format_price(idx), price_unit)
end
function format_oneline(idx)
local more_prices = ""
local more_count = 0
local cur_price = price(idx)
for i = idx+1, #price_data.price do
if price(i) > prefs.oneline_threshold_high * cur_price
or price(i) < prefs.oneline_threshold_low * cur_price then
more_count = more_count + 1
more_prices = more_prices .. string.format("⋄ <i>%s</i> <b>%s</b> ", get_display_time(i), get_display_price(i))
cur_price = price(i)
if more_count > 3 then
break
end
end
end
return string.format("<b>%s</b> %s", format_price_and_unit(idx), more_prices)
end
-- NOTE: using timestamps would be better than indices, but the chart element
-- doesn't support times spanning multiple days properly
function make_chart_data(idx)
local chart = {}
for i = idx, #price_data.price do
table.insert(chart, {
i-1,
get_display_price(i)
})
end
return chart
end
function draw_widget(fold)
ui:set_folding_flag(fold)
local idx = get_current_idx()
if not idx then
ui:show_text("Error: no current price data")
-- request fetch on next on_alarm and don't redraw before that
next_fetch_ts = 0
next_redraw_ts = nil
return
end
next_redraw_ts = price_data.unix_seconds[idx+1]
ui:set_title(string.format("Electricity spot price: %s", format_price_and_unit(idx)))
ui:show_chart(make_chart_data(idx), "x: int, y: float", "", true, format_oneline(idx))
end
function on_settings()
if (prefs.show_dialog) then
prefs:show_dialog()
end
end

6783
community/fennel-search.lua Normal file

File diff suppressed because one or more lines are too long

View File

@@ -2,7 +2,7 @@
-- description = "Game" -- description = "Game"
-- type = "widget" -- type = "widget"
-- author = "Andrey Gavrilov" -- author = "Andrey Gavrilov"
-- version = "1.0" -- version = "1.1"
local json = require "json" local json = require "json"
local folded = "15 puzzle" local folded = "15 puzzle"
@@ -96,7 +96,7 @@ end
function on_click(idx) function on_click(idx)
if idx == 0 then if idx == 0 then
ui:show_dialog("Select Action","","Cancel","Reload") dialogs:show_dialog("Select Action","","Cancel","Reload")
return return
else else
local tab = tabs_to_tab(tabs_to_desk(tab_to_tabs(json.decode(files:read("fifteen"))))) local tab = tabs_to_tab(tabs_to_desk(tab_to_tabs(json.decode(files:read("fifteen")))))

View File

@@ -0,0 +1,69 @@
-- name = "Google Tasks"
-- description = "AIO wrapper for the official Google Tasks app widget"
-- type = "widget"
-- author = "Evgeny Zobnin (zobnin@gmail.com)"
-- aio_version = "4.1.99"
-- uses_app = "com.google.android.apps.tasks"
local prefs = require "prefs"
local fmt = require "fmt"
local max_mails = 1
local curr_tab = {}
local w_bridge = nil
function on_resume()
if not widgets:bound(prefs.wid) then
setup_app_widget()
end
widgets:request_updates(prefs.wid)
end
function on_app_widget_updated(bridge)
local tab = bridge:dump_strings().values
-- Bold list name
tab[1] = fmt.bold(tab[1])
-- Remove "A fresh start"
table.remove(tab, #tab-1)
curr_tab = deep_copy(tab)
w_bridge = bridge
-- Change "Anything to add?" string to "Add task"
tab[#tab] = fmt.secondary("Add task")
ui:show_lines(tab)
end
function on_click(idx)
w_bridge:click(curr_tab[idx])
end
function setup_app_widget()
local id = widgets:setup("com.google.android.apps.tasks/com.google.android.apps.tasks.features.widgetlarge.ListWidgetProvider")
if (id ~= nil) then
prefs.wid = id
else
ui:show_text("Can't add widget")
return
end
end
function deep_copy(orig)
local orig_type = type(orig)
local copy
if orig_type == 'table' then
copy = {}
for orig_key, orig_value in next, orig, nil do
copy[deep_copy(orig_key)] = deep_copy(orig_value)
end
setmetatable(copy, deep_copy(getmetatable(orig)))
else
copy = orig
end
return copy
end

View File

@@ -21,29 +21,29 @@ function on_search(input)
text_from = input text_from = input
text_to = "" text_to = ""
search:show_top({"Translate \""..input.."\""}, {blue}) search:show_buttons({"Translate \""..input.."\""}, {blue}, true)
end end
function on_click() function on_click()
if text_to == "" then if text_to == "" then
search:show_top({"Translating..."}, {blue}) search:show_buttons({"Translating..."}, {blue}, true)
request_trans(text_from) request_trans(text_from)
return false return false
else else
system:copy_to_clipboard(text_to) system:to_clipboard(text_to)
return true return true
end end
end end
function request_trans(str) function request_trans(str)
http:get(uri.."&tl="..system:get_lang().."&dt=t&q="..str) http:get(uri.."&tl="..system:lang().."&dt=t&q="..str)
end end
function on_network_result(result, code) function on_network_result(result, code)
if code >= 200 and code < 300 then if code >= 200 and code < 300 then
decode_and_show(result) decode_and_show(result)
else else
search:show_top({"Server error"}, {red}) search:show_buttons({"Server error"}, {red}, true)
end end
end end
@@ -57,7 +57,7 @@ function decode_and_show(result)
--local lang_from = t[3] --local lang_from = t[3]
if text_to ~= "" then if text_to ~= "" then
search:show_top({text_to}, {blue}) search:show_buttons({text_to}, {blue}, true)
end end
end end

View File

@@ -2,7 +2,7 @@
-- data_source = "https://translate.google.com" -- data_source = "https://translate.google.com"
-- type = "widget" -- type = "widget"
-- author = "Andrey Gavrilov" -- author = "Andrey Gavrilov"
-- version = "1.1" -- version = "1.3"
local json = require "json" local json = require "json"
local uri = "http://translate.googleapis.com/translate_a/single?client=gtx&sl=auto" local uri = "http://translate.googleapis.com/translate_a/single?client=gtx&sl=auto"
@@ -13,12 +13,12 @@ function on_resume()
end end
function on_click() function on_click()
ui:show_edit_dialog("Enter text") dialogs:show_edit_dialog("Enter text")
end end
function on_dialog_action(text) function on_dialog_action(text)
if text == "" or text == -1 then if text == "" or text == -1 then
on_alarm() on_resume()
else else
text_from = text text_from = text
translate(text) translate(text)
@@ -26,7 +26,7 @@ function on_dialog_action(text)
end end
function translate(str) function translate(str)
http:get(uri.."&tl="..system:get_lang().."&dt=t&q="..str) http:get(uri.."&tl="..system:lang().."&dt=t&q="..str)
end end
function on_network_result(result) function on_network_result(result)

217
community/habits-widget.lua Normal file
View File

@@ -0,0 +1,217 @@
-- name = "Habit tracker"
-- description = "Daily habit tracker with JSON storage"
-- type = "widget"
-- author = "Sergey Mironov"
-- version = "1.0"
local json = require("json")
local md = require("md_colors")
local buttons = {}
local filename = "button_data.json"
local dialog_state = nil -- Track current dialog state
-- Get current date in YYYY-MM-DD format
local function get_current_date()
return os.date("%Y-%m-%d")
end
-- Load button data from JSON file
local function load_data()
local content = files:read(filename)
if content then
local success, data = pcall(json.decode, content)
if success and data then
buttons = data
else
buttons = {}
end
else
buttons = {}
end
end
-- Save button data to JSON file
local function save_data()
local content = json.encode(buttons)
files:write(filename, content)
end
-- Display all buttons
local function display_buttons()
local names = {}
local colors = {}
local current_date = get_current_date()
-- Add the "+" button at the beginning
table.insert(names, "fa:plus")
table.insert(colors, aio:colors().button) -- Gray color for add button
-- Add existing buttons
for i, button in ipairs(buttons) do
table.insert(names, button.title)
-- Check if button was clicked today
if button.last_clicked == current_date then
table.insert(colors, md.green_600) -- Green if clicked today
else
table.insert(colors, button.color) -- Original color
end
end
ui:show_buttons(names, colors)
end
-- Handle button clicks
function on_click(index)
local current_date = get_current_date()
-- Check if it's the "+" button (first button)
if index == 1 then
-- Show add dialog - ask for title first
dialog_state = {action = "add_title"}
dialogs:show_edit_dialog("Add New Button", "Enter button title:", "")
return
end
-- Handle existing button click (adjust index for "+" button)
local button_index = index - 1
local button = buttons[button_index]
if button.last_clicked == current_date then
-- Already clicked today, reset
button.last_clicked = ""
ui:show_toast("Unmarked: " .. button.title)
else
-- Not clicked today, mark as clicked
button.last_clicked = current_date
ui:show_toast("Marked: " .. button.title)
end
save_data()
display_buttons()
end
-- Handle long clicks for editing
function on_long_click(index)
-- Don't allow long-click on "+" button (first button)
if index == 1 then
return
end
-- Show edit options (adjust index for "+" button)
local button_index = index - 1
dialog_state = {action = "edit_options", index = button_index}
local button = buttons[button_index]
dialogs:show_dialog("Edit Button: " .. button.title, "Choose action:", "Edit", "Delete")
end
-- Handle dialog results
function on_dialog_action(value)
if value == -1 then
-- Cancel pressed
dialog_state = nil
return
end
if not dialog_state then
return
end
if dialog_state.action == "add_title" then
if type(value) == "string" and value:trim() ~= "" then
-- Got title, now ask for color
dialog_state = {action = "add_color", title = value}
dialogs:show_edit_dialog("Add New Button\nTitle: " .. value, "Enter color (e.g., #FF0000):", "#FF0000")
else
ui:show_toast("Invalid title")
dialog_state = nil
end
elseif dialog_state.action == "add_color" then
if type(value) == "string" and value:match("^#%x%x%x%x%x%x$") then
-- Valid color, create button
local new_button = {
title = dialog_state.title,
color = value,
last_clicked = ""
}
table.insert(buttons, new_button)
save_data()
display_buttons()
ui:show_toast("Added: " .. dialog_state.title)
else
ui:show_toast("Invalid color format. Use #RRGGBB")
end
dialog_state = nil
elseif dialog_state.action == "edit_options" then
if value == 1 then
-- Edit button - ask for new title
local button = buttons[dialog_state.index]
dialog_state = {action = "edit_title", index = dialog_state.index}
dialogs:show_edit_dialog("Edit Button", "Enter new title:", button.title)
elseif value == 2 then
-- Delete button
local button = buttons[dialog_state.index]
table.remove(buttons, dialog_state.index)
save_data()
display_buttons()
ui:show_toast("Deleted: " .. button.title)
dialog_state = nil
end
elseif dialog_state.action == "edit_title" then
if type(value) == "string" and value:trim() ~= "" then
-- Got new title, now ask for color
local button = buttons[dialog_state.index]
dialog_state = {action = "edit_color", index = dialog_state.index, title = value}
dialogs:show_edit_dialog("Edit Button\nTitle: " .. value, "Enter new color:", button.color)
else
ui:show_toast("Invalid title")
dialog_state = nil
end
elseif dialog_state.action == "edit_color" then
if type(value) == "string" and value:match("^#%x%x%x%x%x%x$") then
-- Valid color, update button
local button = buttons[dialog_state.index]
button.title = dialog_state.title
button.color = value
save_data()
display_buttons()
ui:show_toast("Updated: " .. button.title)
else
ui:show_toast("Invalid color format. Use #RRGGBB")
end
dialog_state = nil
end
end
-- Initialize widget
function on_resume()
load_data()
-- Add some sample data if empty
if #buttons == 0 then
buttons = {
{
title = "Exercise",
color = md.pink_600,
last_clicked = ""
},
{
title = "Read",
color = md.cyan_600,
last_clicked = ""
},
{
title = "Water",
color = md.light_blue_600,
last_clicked = ""
}
}
save_data()
end
display_buttons()
end

View File

@@ -7,8 +7,8 @@
-- version = "1.0" -- version = "1.0"
function on_alarm() function on_alarm()
local dateStr = os.date('%Y%m%d') local dateStr = os.date('%Y%m%d')
http:get("https://isdayoff.ru/"..dateStr) http:get("https://isdayoff.ru/"..dateStr)
end end
function on_network_result(result) function on_network_result(result)

View File

@@ -28,6 +28,15 @@ local buttons_cmds = { prev_cmd, backward_cmd, play_cmd, stop_cmd, forward_cmd,
local url = nil local url = nil
local curr_idx = nil local curr_idx = nil
function on_preview()
if next(settings:get()) == nil then
ui:show_text("Remote control for Kodi multimedia player")
return
else
on_resume()
end
end
function on_resume() function on_resume()
if next(settings:get()) == nil then if next(settings:get()) == nil then
ui:show_text("Tap to enter Kodi address") ui:show_text("Tap to enter Kodi address")
@@ -84,6 +93,10 @@ function on_network_result_cmd(result)
end end
end end
function on_settings()
settings:show_dialog()
end
-- utils -- utils
function init_url_from_settings() function init_url_from_settings()

View File

@@ -0,0 +1,40 @@
-- name = "Monitor"
-- description = "One line monitor widget"
-- type = "widget"
-- foldable = "false"
-- author = "Evgeny Zobnin (zobnin@gmail.com)"
-- version = "1.0"
local fmt = require "fmt"
local good_color = aio:colors().progress_good
local bad_color = aio:colors().progress_bad
function on_tick(n)
-- Update every ten seconds
if n % 10 == 0 then
update()
end
end
function update()
local batt_percent = system:battery_info().percent
local is_charging = system:battery_info().charging
local mem_total = system:system_info().mem_total
local mem_available = system:system_info().mem_available
local storage_total = system:system_info().storage_total
local storage_available = system:system_info().storage_available
if (is_charging) then
batt_percent = fmt.colored(batt_percent.."%", good_color)
elseif (batt_percent <= 15) then
batt_percent = fmt.colored(batt_percent.."%", bad_color)
else
batt_percent = batt_percent.."%"
end
ui:show_text(
"BATT: "..batt_percent..fmt.space(4)..
"RAM: "..mem_available..fmt.space(4)..
"NAND: "..storage_available
)
end

View File

@@ -18,7 +18,7 @@ local blue = md_colors.light_blue_800
function on_search(input) function on_search(input)
text_from = input text_from = input
text_to = "" text_to = ""
search:show({input},{blue}) search:show_buttons({input},{blue})
end end
function on_click(idx) function on_click(idx)

View File

@@ -14,9 +14,14 @@ function on_resume()
end end
function on_click() function on_click()
aio:show_args_dialog() settings:show_dialog()
end end
function on_network_result(result) function on_network_result(result)
ui:show_text(result) ui:show_text(result)
end end
function on_settings()
settings:show_dialog()
end

View File

@@ -8,7 +8,7 @@ local pass = ""
function on_search(str) function on_search(str)
if str:lower():find(string.lower("password")) then if str:lower():find(string.lower("password")) then
pass = gen_pass() pass = gen_pass()
search:show{pass} search:show_buttons{pass}
end end
end end
@@ -26,5 +26,5 @@ function gen_pass()
end end
function on_click() function on_click()
system:copy_to_clipboard(pass) system:to_clipboard(pass)
end end

View File

@@ -31,3 +31,8 @@ function init_progressbar()
percent = math.floor((current_time - start_period) / ((end_period - start_period) / 100)) percent = math.floor((current_time - start_period) / ((end_period - start_period) / 100))
ui:show_progress_bar(name_period..": "..percent.."%", current_time - start_period, end_period - start_period, "#7069f0ae") ui:show_progress_bar(name_period..": "..percent.."%", current_time - start_period, end_period - start_period, "#7069f0ae")
end end
function on_settings()
settings:show_dialog()
end

View File

@@ -0,0 +1,16 @@
-- name = "Profiles auto dumper"
-- description = "Hidden widget that auto dump profile on every return to the home screen"
-- type = "widget"
-- author = "Evgeny Zobnin (zobnin@gmail.com)"
-- version = "1.0"
-- foldable = "false"
local prof_name = "Auto dumped"
function on_load()
ui:hide_widget()
end
function on_resume()
profiles:dump(prof_name)
end

View File

@@ -0,0 +1,35 @@
-- name = "Profile Switcher"
-- version = "1.1"
-- description = "Tap: restore profile / Long-press: save to profile"
-- type = "widget"
-- author = "Marcus Johansson"
local profs
local profile
function on_load()
profs = profiles:list()
ui:show_buttons(profs)
end
function on_click(idx)
profile = profs[idx]
profiles:restore(profile)
ui:show_toast(profile..": Restored")
end
function on_long_click(idx)
profile = profs[idx]
title = 'Save to "'..profile..'" ?'
text = 'Do you want to save the current screen state to the "'..profile..'" profile ?'
dialogs:show_dialog(title, text, "Yes", "Cancel")
end
function on_dialog_action(value)
if value == 1 then
profiles:dump(profile)
ui:show_toast(profile..": Saved")
elseif value == 2 then
ui:show_toast("Canceled")
end
end

View File

@@ -17,7 +17,7 @@ function on_search(input)
end end
function on_click() function on_click()
system:copy_to_clipboard(ip) system:to_clipboard(ip)
end end
function get_ip() function get_ip()
@@ -27,9 +27,9 @@ end
function on_network_result(result,code) function on_network_result(result,code)
if code >= 200 and code < 300 then if code >= 200 and code < 300 then
ip = result ip = result
search:show({result},{blue}) search:show_buttons({result},{blue})
else else
search:show({"Server Error"},{red}) search:show_buttons({"Server Error"},{red})
end end
end end

View File

@@ -0,0 +1,143 @@
-- name = "Quick Actions"
-- type = "widget"
-- description = "Launcher selected actions widget"
-- arguments_help = "Long click button for options, open widget settings for list of buttons"
--foldable = "true"
-- author = "Theodor Galanis"
-- version = "2.6"
md_colors = require "md_colors"
local icons = { "fa:pen", "fa:edit", "fa:indent", "fa:bars", "fa:sliders-h", "fa:redo", "fa:power-off", "fa:bring-forward", "fa:eraser", "fa:tools", "fa:layer-minus", "fa:layer-group", "fa:user-shield", "fa:lock", "fa:chevron-down", "fa:chevron-up", "fa:notes-medical", "fa:circle-dot", "fa:envelope", "fa:square-full", "fa:microphone", "fa:hand", "fa:search"}
local names = {"Quick menu", "Quick apps menu", "Applications menu", "Toggle headers", "Settings", "Screen refresh", "Restart AIO launcher", "Notifications panel", "Clear notifications", "Quick settings", "Fold all widgets", "Unfold all widgets", "Private mode", "Screen off", "Scroll down", "Scroll up", "Add note", "Start audio recording", "Send mail", "Recent apps", "Voice command", "One-handed mode", "Search"}
local colors = { md_colors.purple_800, md_colors.purple_600, md_colors.amber_900, md_colors.orange_900, md_colors.blue_900, md_colors.deep_purple_800, md_colors.grey_600, md_colors.green_900, md_colors.green_900, md_colors.blue_800, md_colors.pink_300, md_colors.pink_A200, md_colors.green_600, md_colors.grey_800, md_colors.teal_700, md_colors.teal_800, md_colors.orange_700, md_colors.red_800, md_colors.red_900, md_colors.deep_purple_700, md_colors.blue_700, md_colors.amber_800, md_colors.blue_grey_700}
local actions = { "quick_menu", "quick_apps_menu", "apps_menu", "headers", "settings", "refresh", "restart", "notify", "clear_notifications", "quick_settings", "fold", "unfold", "private_mode", "screen_off", "scroll_down", "scroll_up", "add_note", "start_record", "send_mail", "show_recents", "voice", "one_handed", "search"}
local pos = 0
function on_resume()
if next(settings:get()) == nil then
set_default_args()
end
local buttons,colors = get_buttons()
ui:show_buttons(buttons, colors)
end
function on_click(idx)
pos = idx
redraw()
end
function on_long_click(idx)
pos = idx
local tab = settings:get()
label = get_label(actions[get_checkbox_idx()[idx]])
if label == nil then
label = names[get_checkbox_idx()[idx]]
end
ui:show_context_menu({{"angle-left",""},{"ban",""},{"angle-right",""},{icons[get_checkbox_idx()[idx]]:gsub("fa:",""),label}})
end
function on_context_menu_click(menu_idx)
if menu_idx == 1 then
move(-1)
elseif menu_idx == 2 then
remove()
elseif menu_idx == 3 then
move(1)
elseif menu_idx == 4 then
redraw()
end
end
function on_dialog_action(data)
if data == -1 then
return
end
settings:set(data)
on_resume()
end
function on_settings()
axions = aio:actions()
lab = {}
for i = 1, #axions do
lav = get_label(actions[i])
if lav == nil then
table.insert(lab,names[i])
else
table.insert(lab, lav)
end
end
dialogs:show_checkbox_dialog("Select actions", lab, get_checkbox_idx())
end
--utilities--
function redraw()
local buttons,colors = get_buttons()
local checkbox_idx = get_checkbox_idx()
local action = actions[checkbox_idx[pos]]
aio:do_action(action)
ui:show_buttons(buttons, colors)
end
function set_default_args()
local args = {}
for i = 1, #actions do
table.insert(args, i)
end
settings:set(args)
end
function get_checkbox_idx()
local tab = settings:get()
for i = 1, #tab do
tab[i] = tonumber(tab[i])
end
return tab
end
function get_buttons()
local buttons,bcolors = {},{}
local checkbox_idx = get_checkbox_idx()
for i = 1, #checkbox_idx do
table.insert(buttons, icons[checkbox_idx[i]])
table.insert(bcolors, colors[checkbox_idx[i]])
end
return buttons,bcolors
end
function move(x)
local tab = settings:get()
if (pos == 1 and x < 0) or (pos == #tab and x > 0) then
return
end
local cur = tab[pos]
local prev = tab[pos+x]
tab[pos+x] = cur
tab[pos] = prev
settings:set(tab)
local buttons,colors = get_buttons()
ui:show_buttons(buttons, colors)
end
function remove()
local tab = settings:get()
table.remove(tab,pos)
settings:set(tab)
local buttons,colors = get_buttons()
ui:show_buttons(buttons, colors)
end
function get_label(name)
axions = aio:actions()
for _, action in ipairs(axions) do
if action["name"] == name then
return action["label"]
end
end
end

View File

@@ -10,14 +10,14 @@ function on_alarm()
end end
function on_network_result(result) function on_network_result(result)
setup = ajson:get_value(result, "object string:setup") setup = ajson:read(result, "object string:setup")
punchline = ajson:get_value(result, "object string:punchline") punchline = ajson:read(result, "object string:punchline")
ui:show_lines({setup, punchline}) ui:show_lines({setup, punchline})
end end
function on_click() function on_click()
if setup ~= nil then if setup ~= nil then
system:copy_to_clipboard(setup.."\n"..punchline) system:to_clipboard(setup.."\n"..punchline)
end end
end end

View File

@@ -0,0 +1,120 @@
-- name = "Recent Apps"
-- description = "Gets the apps in the recent apps (app overview) screen. Tap to open the app or long press to remove it.\nREQUIRES ROOT and Android 11 or higher."
-- type = "widget"
-- author = "Abi"
-- version = "3.3"
-- foldable = "false"
local entries = {}
function on_resume()
system:su('dumpsys activity recents')
end
function on_shell_result(raw)
local start = raw:find("Visible recent tasks")
if not start then
ui:show_text("No recent apps")
return
end
local trimmed = raw:sub(start)
local blocks = {}
local pat = "%* RecentTaskInfo #%d+:"
local pos = 1
while true do
local s,e = trimmed:find(pat, pos)
if not s then break end
local ns,_ = trimmed:find(pat, e+1)
if ns then
blocks[#blocks+1] = trimmed:sub(s, ns-1)
pos = ns
else
blocks[#blocks+1] = trimmed:sub(s)
break
end
end
entries = {}
for _, block in ipairs(blocks) do
local id = block:match("id=(%d+)")
local has = block:match("hasTask=(%a+)")
local last = block:match("lastActiveTime=(%d+)")
local full = block:match("cmp=([%w%.]+/[%w%._]+)")
local pkg = full and full:match("^([^/]+)")
if id and has and last and pkg then
entries[#entries+1] = {
id = tonumber(id),
hasTask = (has == "true"),
lastActiveTime = tonumber(last),
packageName = pkg,
appName = get_name_or_color(pkg, "name"),
appColor = get_name_or_color(pkg, "color"),
}
end
end
if #entries == 0 then
ui:show_toast("No recent apps parsed")
return
end
do
local names, colors = {}, {}
for i, e in ipairs(entries) do
names[i] = e.appName
colors[i] = e.appColor
end
ui:show_buttons(names, colors)
end
end
function on_click(idx)
local pkg = entries[idx] and entries[idx].packageName
if pkg then
apps:launch(pkg)
else
ui:show_toast("No package at index "..tostring(idx))
end
end
function on_long_click(idx)
local e = entries[idx]
if not e then
ui:show_toast("No app at index "..tostring(idx))
return
end
system:su("am stack remove " .. entries[idx].id)
system:su('dumpsys activity recents')
end
function to_hex_rgb(argb)
if argb < 0 then argb = 4294967296 + argb end
local r = math.floor((argb / 65536) % 256)
local g = math.floor((argb / 256) % 256)
local b = math.floor(argb % 256)
return string.format("#%02X%02X%02X", r, g, b)
end
function get_name_or_color(pkg, nameorcolor)
local info = apps:app(pkg)
if not info then
if nameorcolor == "name" then
return "???"
elseif nameorcolor == "color" then
return "#FFFFFF"
else
return nil
end
end
if nameorcolor == "name" then
return info.name or "???"
elseif nameorcolor == "color" then
return to_hex_rgb(info.color or 0xFFFFFF)
else
return nil
end
end

View File

@@ -10,7 +10,7 @@ function on_search(str)
local region = codes[code] local region = codes[code]
if region ~= nil then if region ~= nil then
search:show{code.." - "..region} search:show_buttons{code.." - "..region}
end end
end end

View File

@@ -0,0 +1,17 @@
-- name = "Settings menu"
-- name_id = "settings"
-- description = "Side menu with AIO settings"
-- aio_version = "4.7.99"
-- type = "drawer"
-- author = "Evgeny Zobnin (zobnin@gmail.com)"
-- version = "1.0"
function on_drawer_open()
settings = aio:settings()
labels = map(function(it) return it.label end, settings)
drawer:show_list(labels)
end
function on_click(idx)
aio:open_settings(settings[idx].name)
end

View File

@@ -18,7 +18,7 @@ text_to=""
function on_search(input) function on_search(input)
text_from = input text_from = input
text_to = "" text_to = ""
search:show({"Share \""..input.."\""}, {blue}) search:show_buttons({"Share \""..input.."\""}, {blue}, true)
end end
function on_click() function on_click()

View File

@@ -7,16 +7,20 @@
current_output = "Click to enter command" current_output = "Click to enter command"
function on_preview()
ui:show_text("Shows the result of executing console commands")
end
function on_resume() function on_resume()
redraw() redraw()
end end
function redraw() function redraw()
ui:show_text(current_output) ui:show_text("%%txt%%"..current_output)
end end
function on_click(idx) function on_click(idx)
ui:show_edit_dialog("Enter command") dialogs:show_edit_dialog("Enter command")
end end
function on_dialog_action(text) function on_dialog_action(text)

View File

@@ -0,0 +1,79 @@
-- name = "Solar Cycle"
-- description = "Shows Sunrise Sunset at your location"
-- data_source = "https://api.sunrise-sunset.org/"
-- type = "widget"
-- author = "Sriram S V, Will Hall"
-- version = "1.0"
-- foldable = "false"
local json = require "json"
local md_colors = require "md_colors"
function on_alarm()
local location=system:location()
url="https://api.sunrise-sunset.org/json?lat="..location[1].."&lng="..location[2].."&formatted=0"
http:get(url)
end
function on_network_result(result)
local t = json.decode(result)
local times_table = {
{
gen_icon("red_900",""),
gen_icon("orange_900", ""),
gen_icon("yellow_900", ""),
gen_icon("orange_900", ""),
gen_icon("red_900", ""),
},
{
time_from_utc(t.results.civil_twilight_begin),
time_from_utc(t.results.sunrise),
time_from_utc(t.results.solar_noon),
time_from_utc(t.results.sunset),
time_from_utc(t.results.civil_twilight_end),
}
}
ui:show_table(times_table, 0, true)
end
function time_from_utc(utc)
return utc_to_local(parse_iso8601_datetime(utc))
end
function gen_icon(md_color, icon)
return "<font color="..md_colors[md_color].."><b>"..icon.."</b></font>"
end
function parse_iso8601_datetime(json_date)
local pattern = "(%d+)%-(%d+)%-(%d+)%a(%d+)%:(%d+)%:([%d%.]+)([Z%+%-]?)(%d?%d?)%:?(%d?%d?)"
local year, month, day, hour, minute,
seconds, offsetsign, offsethour, offsetmin = json_date:match(pattern)
local timestamp = os.time{year = year, month = month,
day = day, hour = hour, min = minute, sec = seconds}
local offset = 0
if offsetsign ~= '' and offsetsign ~= 'Z' then
offset = tonumber(offsethour) * 60 + tonumber(offsetmin)
if xoffset == "-" then offset = offset * -1 end
end
return timestamp + offset * 60
end
function utc_to_local(utctime)
local local_time_str = os.date("%H:%M", utctime)
local utc_time_str = os.date("!%H:%M", utctime)
local function time_to_seconds(timestr)
local hour, minute = timestr:match("(%d+):(%d+)")
return tonumber(hour) * 3600 + tonumber(minute) * 60
end
local local_seconds = time_to_seconds(local_time_str)
local utc_seconds = time_to_seconds(utc_time_str)
local delta = local_seconds - utc_seconds
return os.date("%H:%M", utctime + delta)
end

View File

@@ -1,6 +1,6 @@
-- name = "Sudoku" -- name = "Sudoku"
-- description = "Sudoku games" -- description = "Sudoku games"
-- type = "Game" -- type = "widget"
-- author = "Andrey Gavrilov" -- author = "Andrey Gavrilov"
-- version = "1.0" -- version = "1.0"

View File

@@ -1,32 +0,0 @@
-- name = "Sun Info"
-- description = "Shows Sunrise Sunset at your location"
-- data_source = "https://api.sunrise-sunset.org/"
-- type = "search"
-- author = "Sriram S V"
-- version = "1.0"
-- prefix = "sun"
-- foldable = "false"
local json = require "json"
local date = require "date"
md_colors = require("md_colors")
function on_search(input)
local location=system:location()
get_sun_info(location)
end
function on_network_result(result)
local t = json.decode(result)
local sunrise = date(t.results.sunrise):tolocal():fmt("%r")
local sunset = date(t.results.sunset):tolocal():fmt("%r")
local lines = {"Sunrise: "..sunrise, "Sunset: "..sunset}
local colors = { md_colors.orange_400, md_colors.orange_800 }
search:show_lines(lines,colors)
end
function get_sun_info(location)
url="https://api.sunrise-sunset.org/json?lat="..location[1].."&lng="..location[2].."&date=today&formatted=1"
http:get(url)
end

View File

@@ -1,29 +0,0 @@
-- name = "Sunrise/Sunset"
-- description = "Shows Sunrise Sunset at your location"
-- data_source = "https://api.sunrise-sunset.org/"
-- type = "widget"
-- author = "Sriram S V"
-- version = "1.0"
-- foldable = "false"
local json = require "json"
local date = require "date"
function on_alarm()
local location=system:location()
get_sun_info(location)
end
function on_network_result(result)
local t = json.decode(result)
local table = {
{ "sunrise:", date(t.results.sunrise):tolocal():fmt("%r") },
{ "sunset:", date(t.results.sunset):tolocal():fmt("%r") },
}
ui:show_table(table, 2)
end
function get_sun_info(location)
url="https://api.sunrise-sunset.org/json?lat="..location[1].."&lng="..location[2].."&date=today&formatted=1"
http:get(url)
end

View File

@@ -0,0 +1,80 @@
-- name = "Thirukkural"
-- description = "Thirukkural Widget that refreshes a verse form the Tamil Book Thirukkural. Single click will open the diaglog with multiple translations in English and Tamili. Long press will retrive a new verse. This uses the API from getthirukural"
-- data_source = "https://getthirukural.appspot.com/"
-- type = "widget"
-- author = "Abdul MJ (mjabdulm@gmail.com)"
-- version = "1.0"
-- foldable = "false"
local json = require "json"
local kural_data = nil -- Store the kural translation data
function on_alarm()
-- Fetch random kural with English translation
-- http:get("https://api.alquran.cloud/v1/ayah/random/en.sahih")
http:get("https://getthirukural.appspot.com/api/3.0/kural/rnd?appid=bzh3rnqagllov")
end
function on_network_result(result)
local response = json.decode(result)
if response then
-- Store kural data including athigaram name, kural number
kural_data = {
number = response.number,
line1 = response.line1,
line2 = response.line2,
paal = response.paal,
athigaram = response.athigaram,
iyal = response.iyal,
urai1Author = response.urai1Author,
urai2Author = response.urai2Author,
urai3Author = response.urai3Author,
urai1 = response.urai1,
urai2 = response.urai2,
urai3 = response.urai3,
en = response.en
}
display_kural()
else
ui:show_message("Error loading kural data.")
end
end
function display_kural()
if kural_data then
local display_lines = {
-- kural_data.number .. " : " .. kural_data.line1 .. "<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;" .. kural_data.line2
-- kural_data.line1 .. "<br/>" .. kural_data.line2 .. "&nbsp;-&nbsp;<font color=red style=font-size:0.5em;>" .. kural_data.number .. ":".. kural_data.athigaram .. ":" .. kural_data.paal .. "</font>"
kural_data.line1 .. "<br/>" .. kural_data.line2 .. "&nbsp;-<i><font color=red>" .. kural_data.number .. "</i></font>"
-- kural_data.line1 .. "<br/>" .. kural_data.line2 .. [[-&nbsp;<font color=grey style="font-size: 0.5em;">]] .. kural_data.number .. ":" .. kural_data.athigaram .. "</font>"
}
-- ui:show_lines(display_lines display_titles)
ui:show_lines(display_lines)
end
end
function on_click()
if kural_data then
-- Prepare text to copy to clipboard with English translation only
local title = "Thirukkural:" .. kural_data.number
local text = kural_data.number .. ":" .. kural_data.paal .. ":" .. kural_data.iyal .. ":" .. kural_data.athigaram .. "<br/><br/><b>Kural<br/></b>" .. kural_data.line1 .. "<br/>" .. kural_data.line2 .. "<br/><br/><b>English:</b><br/>" .. kural_data.en .. "<br\><br\><b>" .. kural_data.urai1Author .. "</b><br/>" .. kural_data.urai1 .. "<br\><br\><b>" .. kural_data.urai2Author .. "</b><br/>" .. kural_data.urai2 .. "<br\><br\><b>" .. kural_data.urai3Author .. "</b><br/>" .. kural_data.urai3
-- system:to_clipboard(clipboard_text)
dialogs:show_dialog(title,text)
display_kural()
-- ui:show_lines(clipboard_text)
else
ui:show_message("No kural available to copy.")
end
end
function on_long_click()
-- Fetch random kural on long-click
http:get("https://getthirukural.appspot.com/api/3.0/kural/rnd?appid=bzh3rnqagllov")
end

View File

@@ -0,0 +1,110 @@
-- name = "TickTick"
-- description = "AIO Launhcer wrapper for official TickTick app widget"
-- author = "Evgeny Zobnin (zobnin@gmail.com)"
-- version = "1.0"
-- uses_app = "com.ticktick.task"
local prefs = require "prefs"
local fmt = require "fmt"
local w_bridge = nil
local lines = {}
function on_resume()
if not widgets:bound(prefs.wid) then
setup_app_widget()
end
widgets:request_updates(prefs.wid)
end
function on_app_widget_updated(bridge)
lines = {}
-- We use dump_tree instead of dump_table because Lua tables
-- do not preserve the order of elements (which is important in this case).
-- Additionally, the text tree is simply easier to parse.
local tree = bridge:dump_tree()
local all_lines = extract_list_item_lines(tree)
lines = combine_lines(all_lines)
table.insert(lines, fmt.secondary("Add task"))
w_bridge = bridge
ui:show_lines(lines)
end
function on_click(idx)
if idx == #lines then
-- "Plus" button
w_bridge:click("image_3")
else
-- First task name
w_bridge:click("text_2")
end
end
function on_settings()
w_bridge:click("text_1")
end
-- Extract all elements with a nesting level of 6 (task texts and dates)
function extract_list_item_lines(str)
for line in str:gmatch("[^\n]+") do
if line:match("^%s%s%s%s%s%s%s%s%s%s%s%s") then
table.insert(lines, extract_text_after_colon(line))
end
end
return lines
end
-- Tasks list elements in the the dump are separated by a 1x1 image,
-- so we can use it to understand where one task ends and another begins.
function combine_lines(lines)
local result = {}
local temp_lines = {}
for i, line in ipairs(lines) do
if line == "1x1" then
if #temp_lines > 0 then
table.insert(result, concat_lines(temp_lines))
temp_lines = {}
end
else
table.insert(temp_lines, line)
end
end
if #temp_lines > 0 then
table.insert(result, concat_lines(temp_lines))
end
return result
end
-- The text and date of a task in the dump appear consecutively,
-- and we need to combine them, keeping in mind that the date might be absent.
function concat_lines(lines)
if lines[1] then
lines[1] = fmt.primary(lines[1])
end
if lines[2] then
lines[2] = fmt.secondary(lines[2])
end
return table.concat(lines, fmt.secondary(" - "))
end
function extract_text_after_colon(text)
return text:match(":%s*(.*)")
end
function setup_app_widget()
local id = widgets:setup("com.ticktick.task/com.ticktick.task.activity.widget.GoogleTaskAppWidgetProviderLarge")
if (id ~= nil) then
prefs.wid = id
else
ui:show_text("Can't add widget")
end
end

114
community/tide-widget.lua Normal file
View File

@@ -0,0 +1,114 @@
-- name = "Tide Time"
-- description = "Widget shows high and low tide times for today."
-- data_source = "https://open-meteo.com/en/docs/marine-weather-api"
-- type = "widget"
-- author = "AI Assistant"
-- version = "1.0"
local json = require "json"
local color = require "md_colors"
local text_color = aio:colors().secondary_text
-- variables
local tide_data = nil
local tide_times = {}
function on_alarm()
get_tide_data()
end
function get_tide_data()
local today = os.date("%Y-%m-%d")
local location = system:location()
local url = string.format(
"https://marine-api.open-meteo.com/v1/marine?latitude=%f&longitude=%f&hourly=sea_level_height_msl&timezone=auto&start_date=%s&end_date=%s",
location[1], location[2], today, today
)
http:get(url)
end
function on_network_result(result)
local data = json.decode(result)
if not data or not data.hourly then
ui:show_toast("Error getting data: " .. (result or "no response"))
return
end
tide_data = data
process_tide_data()
display_tide_times()
end
function process_tide_data()
tide_times = {}
local times = tide_data.hourly.time
local heights = tide_data.hourly.sea_level_height_msl
if not times or not heights then
ui:show_text("Invalid data format")
return
end
-- Find local maxima and minima
for i = 2, #heights - 1 do
if heights[i] > heights[i-1] and heights[i] > heights[i+1] then
table.insert(tide_times, {
time = times[i],
height = heights[i],
type = "high tide"
})
elseif heights[i] < heights[i-1] and heights[i] < heights[i+1] then
table.insert(tide_times, {
time = times[i],
height = heights[i],
type = "low tide"
})
end
end
end
function display_tide_times()
if #tide_times == 0 then
ui:show_text("No tide data available")
return
end
local tab = {}
for _, tide in ipairs(tide_times) do
local time = tide.time:match("T(%d+:%d+)")
local height = string.format("%.2f", tide.height)
local type = tide.type
local color = type == "high tide" and color.blue_500 or color.blue_900
table.insert(tab, {
string.format("<font color=\"%s\">%s</font>", color, type),
time,
height .. " m"
})
end
ui:show_table(tab, 3)
ui:set_title(ui:default_title() .. " (" .. os.date("%d.%m.%Y") .. ")")
end
function on_long_click(idx)
ui:show_context_menu({
{"redo", "Refresh"},
{"copy", "Copy data"}
})
end
function on_context_menu_click(menu_idx)
if menu_idx == 1 then
get_tide_data()
ui:show_toast("Updating data...")
elseif menu_idx == 2 then
local text = "Tide times for " .. os.date("%d.%m.%Y") .. ":\n"
for _, tide in ipairs(tide_times) do
local time = tide.time:match("T(%d+:%d+)")
text = text .. string.format("%s: %s (%.2f m)\n", tide.type, time, tide.height)
end
system:copy_to_clipboard(text)
ui:show_toast("Data copied")
end
end

View File

@@ -0,0 +1,41 @@
-- name = "Time & Date"
-- description = "Simple widget showing current time and date"
-- author = "Evgeny Zobnin (zobnin@gmail.com)"
-- type = "widget"
-- aio_version = "5.2.1"
local function draw()
local now = os.date("*t")
local time = string.format("%02d:%02d", now.hour, now.min)
local date = os.date("%a, %d %b")
gui{
{"spacer", 2},
{"text", time, { size = 40 }},
{"text", date, { size = 20, gravity = "right|center_v" }},
{"spacer", 2},
}.render()
end
function on_tick()
draw()
end
function on_click(idx)
-- time
if idx == 2 then
intent:start_activity{
action = "android.intent.action.SHOW_ALARMS"
}
return
end
-- date
if idx == 3 then
intent:start_activity{
action = "android.intent.action.MAIN",
category = "android.intent.category.APP_CALENDAR"
}
end
end

View File

@@ -0,0 +1,58 @@
-- name = "Timed message"
-- description = "A message that is displayed at a specific time"
-- type = "widget"
-- foldable = "false"
-- author = "Evgeny Zobnin (zobnin@gmail.com)"
-- version = "1.0"
local prefs = require "prefs"
function on_load()
prefs._dialog_order = "message,start_time,end_time"
if not prefs.message then
prefs.message = "Sample message"
end
if not prefs.start_time then
prefs.start_time = "00:00"
end
if not prefs.end_time then
prefs.end_time = "23:59"
end
end
function on_resume()
ui:show_text(prefs.message)
hide_or_show()
end
function on_settings()
prefs:show_dialog()
end
function hide_or_show()
local start_time = convert_to_unix_time(prefs.start_time)
local end_time = convert_to_unix_time(prefs.end_time)
local current_time = os.time()
if current_time > start_time and current_time < end_time then
ui:show_widget()
else
ui:hide_widget()
end
end
function convert_to_unix_time(time_str)
local hour, minute = time_str:match("^(%d%d):(%d%d)$")
hour, minute = tonumber(hour), tonumber(minute)
local current_date = os.date("*t")
current_date.hour = hour
current_date.min = minute
current_date.sec = 0
return os.time(current_date)
end

View File

@@ -0,0 +1,125 @@
-- name = "Todoist"
-- description = "AIO wrapper for the official Todoist app widget"
-- type = "widget"
-- author = "Andey Gavrilov"
-- aio_version = "4.1.99"
-- uses_app = "com.todoist"
local prefs = require "prefs"
local fmt = require "fmt"
local widget = "com.todoist/com.todoist.appwidget.provider.ItemListAppWidgetProvider"
local curr_tab = {}
local w_bridge = nil
local colors = {}
function on_resume()
if not widgets:bound(prefs.wid) then
setup_app_widget()
end
widgets:request_updates(prefs.wid)
end
function on_app_widget_updated(bridge)
w_bridge = bridge
colors = bridge:dump_colors()
curr_tab = bridge:dump_table()
curr_tab = parse(curr_tab)
ui:show_lines(curr_tab.lines)
end
function parse(t)
local tab = {}
local images = {}
local lines = {}
local clicks = {}
local tasks = t.v_layout_1.frame_layout_2.list_layout_1
if not tasks then tasks = {} end
local tkeys = {}
for k,v in pairs(tasks) do
table.insert(tkeys,k)
end
table.sort(tkeys,function (a,b) return tonumber(a:match("%d+")) < tonumber(b:match("%d+")) end)
for i1,v1 in ipairs(tkeys) do
for k2,v2 in pairs(tasks[v1]) do
for k3,v3 in pairs(v2) do
if k3:sub(1,5) == "image" then
table.insert(images,k3)
elseif k3:sub(1,8) == "v_layout" then
local text = ""
local subtexts = {}
for k4,v4 in pairs(v3) do
if k4:sub(1,4) == "text" then
table.insert(clicks,k4)
text = v4
local color = colors[images[#images]]
if color ~= "#909090" and color then
text = fmt.colored(fmt.bold(text),color)
end
elseif k4:sub(1,8) == "h_layout" then
subtexts = recursion(v4,subtexts,1,false)
end
end
table.insert(lines,text..table.concat(subtexts))
end
end
end
end
local footer = t.v_layout_1.frame_layout_2
for k,v in pairs(footer) do
if k:sub(1,8) == "relative" then
footer = v
break
end
end
for k1,v1 in pairs(footer) do
for k2,v2 in pairs(v1) do
if k2:sub(1,4) == "text" then
table.insert(images,k2)
table.insert(clicks,k2)
local add_task = "Add task"
if #lines ~= 0 then
add_task = fmt.secondary(add_task)
end
table.insert(lines,add_task)
break
end
end
end
tab["images"] = images
tab["lines"] = lines
tab["clicks"] = clicks
return tab
end
function recursion(t,tab,idx,flag)
for k,v in pairs(t) do
if k:sub(1,4) == "text" then
local a,b = v:match("(%d+)/(%d+)")
if a or flag then
table.insert(tab,idx,fmt.secondary(" - ") .. fmt.colored(v,colors[k]))
end
elseif k:sub(1,8) == "h_layout" then
tab = recursion(v,tab,#tab+1,true)
end
end
return tab
end
function on_click(idx)
w_bridge:click(curr_tab.clicks[idx])
end
function setup_app_widget()
local id = widgets:setup(widget)
if (id ~= nil) then
prefs.wid = id
else
ui:show_toast("Can't add widget")
end
end
function on_settings()
w_bridge:click("text_1")
end

View File

@@ -1,7 +1,7 @@
-- name = "ТВ-Программа" -- name = "ТВ-Программа"
-- description = "Программа передач россиийского ТВ" -- description = "Программа передач россиийского ТВ"
-- type = "widget" -- type = "widget"
-- version = "1.0" -- version = "1.1"
-- lang = "ru" -- lang = "ru"
-- author = "Andrey Gavrilov" -- author = "Andrey Gavrilov"
@@ -23,9 +23,9 @@ end
function on_click(idx) function on_click(idx)
if math.ceil(idx/2) > #tab_desc then if math.ceil(idx/2) > #tab_desc then
ui:show_edit_dialog("Введите название канала","",title) dialogs:show_edit_dialog("Введите название канала","",title)
else else
ui:show_dialog(tab_name[math.ceil(idx/2)].."\n"..tab_time[math.ceil(idx/2)],tab_desc[math.ceil(idx/2)],"Перейти к каналу") dialogs:show_dialog(tab_name[math.ceil(idx/2)].."\n"..tab_time[math.ceil(idx/2)],tab_desc[math.ceil(idx/2)],"Перейти к каналу")
link = tab_link[math.ceil(idx/2)] link = tab_link[math.ceil(idx/2)]
end end
end end

View File

@@ -0,0 +1,73 @@
-- name = "Uptimerobot V2"
-- description = "Shows uptime information from uptimerobot.com. Needs API key. Button-based Version."
-- data_source = "uptimerobot.com"
-- type = "widget"
-- author = "Evgeny Zobnin (zobnin@gmail.com), Will Hall (hello@willhall.uk)"
-- version = "1.0"
-- arguments_help = "Enter your API key"
local json = require "json"
local md_colors = require "md_colors"
-- constants
local api_url = "https://api.uptimerobot.com/v2/"
local base_click_url = "https://uptimerobot.com/dashboard#"
local media_type = "application/x-www-form-urlencoded"
local status_colors = { "grey_500", "green_500", "red_500", "red_500", "red_500", "red_500", "red_500", "orange_500", "red_500" }
-- monitors
local monitor_ids = {}
function on_alarm()
if (next(settings:get()) == nil) then
ui:show_text("Tap to enter API key")
return
end
local key = settings:get()[1]
local body = "api_key="..key.."&format=json"
http:post(api_url.."getMonitors", body, media_type)
end
function on_click(i)
if (next(settings:get()) == nil) then
settings:show_dialog()
else
if(monitor_ids[i] ~= nil) then
system:open_browser(base_click_url..monitor_ids[i])
else
system:open_browser(base_click_url.."mainDashboard")
end
end
end
function on_network_result(result, code)
if (code >= 400) then
ui:show_text("Error: "..code)
return
end
local parsed = json.decode(result)
if (parsed.stat ~= "ok") then
ui:show_text("Error: "..parsed.error.message)
return
end
local names = {}
local colours = {}
for k,v in pairs(parsed.monitors) do
monitor_ids[k] = v.id
names[k] = v.friendly_name
colours[k] = md_colors[status_colors[v.status]] or md_colors["grey_500"]
end
ui:show_buttons(names, colours)
end
function on_settings()
settings:show_dialog()
end

View File

@@ -14,6 +14,15 @@ local api_url = "https://api.uptimerobot.com/v2/"
local click_url = "https://uptimerobot.com/dashboard#mainDashboard" local click_url = "https://uptimerobot.com/dashboard#mainDashboard"
local media_type = "application/x-www-form-urlencoded" local media_type = "application/x-www-form-urlencoded"
function on_preview()
if (next(settings:get()) == nil) then
ui:show_text("Shows uptime information from uptimerobot.com")
return
else
on_alarm()
end
end
function on_alarm() function on_alarm()
if (next(settings:get()) == nil) then if (next(settings:get()) == nil) then
ui:show_text("Tap to enter API key") ui:show_text("Tap to enter API key")
@@ -82,3 +91,8 @@ function table_to_tables(tab, num)
return out_tab return out_tab
end end
function on_settings()
settings:show_dialog()
end

View File

@@ -1,6 +1,6 @@
-- name = "What to do?" -- name = "What to do?"
-- description = "Let's find you something to do" -- description = "Let's find you something to do"
-- data_source = "https://www.boredapi.com/" -- data_source = "https://bored.api.lewagon.com/"
-- type = "widget" -- type = "widget"
-- author = "Evgeny Zobnin (zobnin@gmail.com)" -- author = "Evgeny Zobnin (zobnin@gmail.com)"
-- version = "1.0" -- version = "1.0"
@@ -12,7 +12,7 @@ end
function on_click() function on_click()
system:vibrate(100) system:vibrate(100)
http:get("http://www.boredapi.com/api/activity/") http:get("https://bored.api.lewagon.com/api/activity/")
end end
function on_network_result(result) function on_network_result(result)

View File

@@ -0,0 +1,145 @@
-- name = "Widgets switcher"
-- description = "Turns screen widgets on and off when buttons are pressed"
-- type = "widget"
-- author = "Andrey Gavrilov"
-- version = "3.1"
prefs = require "prefs"
local pos = 0
local buttons,colors = {},{}
function on_alarm()
widgets = get_widgets()
if not prefs.widgets then
prefs.widgets = widgets.name
end
indexes = get_indexes(prefs.widgets, widgets.name)
ui:show_buttons(get_buttons())
end
function on_long_click(idx)
system:vibrate(10)
pos = idx
if idx > #prefs.widgets then
return
end
ui:show_context_menu({{"angle-left",""},{"ban",""},{"angle-right",""},{widgets.icon[indexes[idx]],widgets.label[indexes[idx]]}})
end
function on_click(idx)
system:vibrate(10)
if idx > #prefs.widgets then
on_settings()
return
end
local widget = prefs.widgets[idx]
if not aio:is_widget_added(widget) then
aio:add_widget(widget, get_pos())
aio:fold_widget(widget, false)
else
aio:remove_widget(widget)
end
on_alarm()
end
function on_dialog_action(data)
if data == -1 then
return
end
local tab = {}
for i,v in ipairs(data) do
tab[i] = widgets.name[v]
end
prefs.widgets = tab
on_alarm()
end
function on_settings()
dialogs:show_checkbox_dialog("Select widgets", widgets.label, indexes)
end
function get_indexes(tab1,tab2)
local tab = {}
for i1,v1 in ipairs(tab1) do
for i2,v2 in ipairs(tab2) do
if v1 == v2 then
tab[i1] = i2
break
end
end
end
return tab
end
function get_buttons()
local enabled_color = "#1976d2"
local disabled_color = aio:colors().button
buttons,colors = {},{}
for i,v in ipairs(indexes) do
table.insert(buttons, "fa:" .. widgets.icon[v])
table.insert(colors, widgets.enabled[v] and enabled_color or disabled_color)
end
return buttons,colors
end
function move(x)
local tab = prefs.widgets
if (pos*x == -1) or (pos*x == #tab) then
return
end
local cur = tab[pos]
tab[pos] = tab[pos+x]
tab[pos+x] = cur
prefs.widgets = tab
on_alarm()
end
function remove()
local tab = prefs.widgets
table.remove(tab,pos)
prefs.widgets = tab
on_alarm()
end
function on_context_menu_click(menu_idx)
if menu_idx == 1 then
move(-1)
elseif menu_idx == 2 then
remove()
elseif menu_idx == 3 then
move(1)
end
end
function on_widget_action(action, name)
on_alarm()
end
function get_pos()
local name = aio:self_name()
local tab = aio:active_widgets()
for _,v in ipairs(tab) do
if v.name == name then
return v.position+1
end
end
return 4
end
function get_widgets()
local tab = {}
tab.icon = {}
tab.name = {}
tab.label = {}
tab.enabled = {}
for i,v in ipairs(aio:available_widgets()) do
if v.type == "builtin" then
table.insert(tab.icon, v.icon)
table.insert(tab.name, v.name)
table.insert(tab.label, v.label)
table.insert(tab.enabled, v.enabled)
end
end
return tab
end

View File

@@ -0,0 +1,70 @@
-- name = "ChatGPT"
-- description = "A search script that allows you to task with ChatGPT"
-- data_source = "openai.com"
-- type = "search"
-- author = "Evgeny Zobnin"
-- version = "1.0"
local json = require "json"
-- constants
local ok_color = aio:colors().primary_text
local error_color = aio:colors().progress_bad
local uri = "https://ai.fakeopen.com/v1/chat/completions"
local key = "pk-this-is-a-real-free-pool-token-for-everyone"
-- vars
local question = ""
local answer = ""
local payload_template = [[
{
"model": "gpt-3.5-turbo",
"temperature": 0.8,
"max_tokens": 100,
"messages": [
{
"role": "user",
"content": "%%Q%%. Answer in one sentence."
}
]
}
]]
function on_search(input)
search:show_lines({"Ask ChatGPT: \""..input.."\""}, {ok_color}, true)
question = input
answer = ""
end
function on_click()
if answer == "" then
search:show_lines({"Waiting for answer..."}, {ok_color}, true)
make_request(question)
return false
else
system:copy_to_clipboard(answer)
return true
end
end
function make_request(str)
local payload = payload_template:replace("%%Q%%", str)
http:set_headers{ "Authorization: Bearer "..key }
http:post(uri, payload, "application/json")
end
function on_network_result(result, code)
if code >= 200 and code < 300 then
answer = get_answer(result)
search:show_lines({answer}, {ok_color}, true)
else
search:show_lines({"Server error: "..code}, {error_color}, true)
end
end
function get_answer(result)
local t = json.decode(result)
return t.choices[1].message.content
end

33
defunct/covid-widget.lua Normal file
View File

@@ -0,0 +1,33 @@
-- name = "Covid info"
-- description = "Cases of illness and death from covid"
-- data_source = "covid19api.com"
-- type = "widget"
-- author = "Evgeny Zobnin (zobnin@gmail.com)"
-- version = "1.0"
equals = "<font color=\""..ui:colors().secondary_text.."\"> = </font>"
function on_alarm()
http:get("https://api.covid19api.com/summary")
end
function on_network_result(result, code)
if code >= 200 and code < 299 then
local new = ajson:get_value(result, "object object:Global int:NewConfirmed")
local total = ajson:get_value(result, "object object:Global int:TotalConfirmed")
local newDeaths = ajson:get_value(result, "object object:Global int:NewDeaths")
local totalDeaths = ajson:get_value(result, "object object:Global int:TotalDeaths")
ui:show_lines({
"<b>Disease</b> | total"..equals..comma_value(total).." | new"..equals..comma_value(new),
"<b>Deaths</b> | total"..equals..comma_value(totalDeaths).." | new"..equals..comma_value(newDeaths)
})
end
end
-- credit http://richard.warburton.it
function comma_value(n)
local left,num,right = string.match(n,'^([^%d]*%d)(%d*)(.-)$')
return left..(num:reverse():gsub('(%d%d%d)','%1,'):reverse())..right
end

View File

@@ -0,0 +1,128 @@
-- name = "Google search"
-- description = "AIO wrapper for the Google search app widget - open widget settings for options"
-- type = "widget"
-- author = "Theodor Galanis (t.me/TheodorGalanis)"
-- version = "2.8"
-- foldable = "false"
-- uses_app = "com.google.android.googlequicksearchbox"
local prefs = require "prefs"
local w_bridge = nil
local indices = {}
function on_alarm()
if not widgets:bound(prefs.wid) then
setup_app_widget()
end
if not prefs.mode then
prefs.mode = 1
end
mode = prefs.mode
indices = set_indices()
widgets:request_updates(prefs.wid, "4x1")
end
function on_app_widget_updated(bridge)
w_bridge = bridge
local tab = {
{"button", "fa:magnifying-glass", {expand = true}},
{"spacer", 2},
{"button", "fa:sun-cloud"},
{"spacer", 2},
{"button", "fa:asterisk"},
{"spacer", 2},
{"button", "fa:microphone"},
{"spacer", 2},
{"button", "fa:camera"}
}
tab = set_gui(tab)
my_gui = gui(tab)
my_gui.render()
end
function on_click(idx)
if idx == indices[1] then
w_bridge:click("image_2")
elseif idx == indices[2] then
intent:start_activity(open_weather())
elseif idx == indices[3] then
w_bridge:click("image_3")
elseif idx == indices[4] then
w_bridge:click("image_5")
elseif idx == indices[5] then
w_bridge:click("image_6")
else return
end
end
function on_settings()
local tab = {"Left-handed mode with weather", "Left-handed mode, no weather", "Right-handed mode with weather", "Right-handed mode, no weather" }
dialogs:show_radio_dialog("Select mode", tab, mode)
end
function on_long_click(idx)
if idx == indices[1] then
ui:show_toast("Google search")
elseif idx == indices[2] then
ui:show_toast("Google weather")
elseif idx == indices[3] then
ui:show_toast("Google discover")
elseif idx == indices[4] then
ui:show_toast("Google voice search")
elseif idx == indices[5] then
ui:show_toast("Google Lens")
end
end
function on_dialog_action(data)
if data == -1 then
return
end
prefs.mode = data
on_alarm()
end
function setup_app_widget()
local id = widgets:setup("com.google.android.googlequicksearchbox/com.google.android.googlequicksearchbox.SearchWidgetProvider")
if (id ~= nil) then
prefs.wid = id
else
ui:show_text("Can't add widget")
return
end
end
function set_indices()
local temp = {1, 3, 5, 7, 9}
if mode == 2 then
temp = {1, 9, 3, 5, 7}
elseif mode == 3 then
temp = reverse (temp)
elseif mode == 4 then
temp = {7, 9, 5, 3, 1}
end
return temp
end
function set_gui(tab)
local temp = tab
if mode == 2 or mode == 4 then
table.remove(tab, 3)
table.remove(tab, 3)
end
if mode > 2 then
temp = reverse(temp)
end
return temp
end
function open_weather()
local tab ={}
tab.category = "MAIN"
tab.package = "com.google.android.googlequicksearchbox"
tab.component = "com.google.android.googlequicksearchbox/com.google.android.apps.search.weather.WeatherExportedActivity"
return tab
end

View File

@@ -1,4 +1,4 @@
-- name = "Chuck Norris translated" -- name = "Chuck Norris jokes"
-- description = "Jokes with translation" -- description = "Jokes with translation"
-- data_source = "icndb.com" -- data_source = "icndb.com"
-- type = "widget" -- type = "widget"

View File

@@ -10,11 +10,13 @@ function on_alarm()
http:get("https://api.quotable.io/random") http:get("https://api.quotable.io/random")
end end
function on_network_result(result) function on_network_result(result, code)
quote = ajson:get_value(result, "object string:content") if code >= 200 and code < 299 then
author = ajson:get_value(result, "object string:author") quote = ajson:get_value(result, "object string:content")
author = ajson:get_value(result, "object string:author")
ui:show_lines({ quote }, { author }) ui:show_lines({ quote }, { author })
end
end end
function on_click() function on_click()

View File

@@ -0,0 +1,23 @@
-- name = "AΙΟ actions list"
-- type = "widget"
-- description = "Shows actions returned by aio:actions() function"
--foldable = "true"
-- author = "Theodor Galanis"
-- version = "1"
function on_resume()
actions = aio:actions()
local labels = ""
labels = map(actions, function(it) return it.label end)
names = map(actions, function(it) return it.name end)
ui:show_lines(names, labels)
end
function map(tbl, f)
local ret = {}
for k,v in pairs(tbl) do
ret[k] = f(v)
end
return ret
end

23
dev/aiocolors-widget.lua Normal file
View File

@@ -0,0 +1,23 @@
-- name = "AΙΟ colors"
-- type = "widget"
-- description = "Shows colors returned by aio:colors() function"
--foldable = "true"
-- author = "Theodor Galanis"
-- version = "1"
function on_resume()
local colors = aio:colors()
local colors_strings = stringify_table(colors)
ui:show_lines(colors_strings)
end
function stringify_table(tab)
local new_tab = {}
for k,v in pairs(tab) do
table.insert(new_tab, k..": "..tostring(v))
end
return new_tab
end

View File

@@ -0,0 +1,23 @@
-- name = "AΙΟ widgets list"
-- type = "widget"
-- description = "Shows widgets returned by aio:available_widgets() function"
--foldable = "true"
-- author = "Theodor Galanis"
-- version = "1"
function on_resume()
actions = aio:available_widgets()
local labels = ""
labels = map(actions, function(it) return it.label end)
names = map(actions, function(it) return it.name end)
ui:show_lines(names, labels)
end
function map(tbl, f)
local ret = {}
for k,v in pairs(tbl) do
ret[k] = f(v)
end
return ret
end

View File

@@ -0,0 +1,59 @@
-- name = "Android widgets dumper"
-- foldable = false
-- Place app package name with widget here
app_pkg = "com.google.android.googlequicksearchbox"
--app_pkg = "com.weather.Weather"
--app_pkg = "com.google.android.apps.tasks"
--app_pkg = "com.android.chrome"
--app_pkg = "com.whatsapp"
-- Widget size (string from "1x1" to "4x4")
-- In most cases you can use nil
widget_size = "4x1"
-- Globals
labels = {}
providers = {}
dump = ""
wid = -1
function on_resume()
local list = widgets:list(app_pkg)
if list == nil then
ui:show_text("Error: No widgets")
return
end
labels = map(function(it) return it.label end, list)
providers = map(function(it) return it.provider end, list)
w_content = ""
if wid < 0 then
ui:show_lines(labels)
else
widgets:request_updates(wid, widget_size)
end
end
function on_click(idx)
if w_content == "" then
wid = widgets:setup(providers[idx])
widgets:request_updates(wid, widget_size)
else
system:copy_to_clipboard(w_content)
end
end
function on_app_widget_updated(bridge)
local provider = bridge:provider()
local dump = bridge:dump_tree()
local colors = bridge:dump_colors()
w_content = provider.."\n\n"..dump.."\n\n"..serialize(colors)
ui:show_text("%%txt%%"..w_content)
debug:log("dump:\n\n"..w_content)
end

3
env Normal file
View File

@@ -0,0 +1,3 @@
REPOS="main ru samples community dev"
SCRIPTS_DIR="/sdcard/Android/data/ru.execbit.aiolauncher/files/"

View File

@@ -1,9 +1,8 @@
#!/bin/sh #!/bin/sh
REPOS="main ru samples community" source ./env
SCRIPTS_DIR="/sdcard/Android/data/ru.execbit.aiolauncher/files/"
adb shell rm -rf $SCRIPTS_DIR/*.lua ./rm-scripts.sh
for repo in $REPOS; do for repo in $REPOS; do
adb push $repo/*.lua $SCRIPTS_DIR adb push $repo/*.lua $SCRIPTS_DIR

View File

@@ -1,5 +1,25 @@
-- Standard AIO Launcher library -- Standard AIO Launcher library
function string:trim()
if #self == 0 then return self end
return self:match("^%s*(.-)%s*$")
end
function string:starts_with(start_str)
if #self == 0 or #self < #start_str then return false end
if start_str == nil or start_str == "" then return true end
return self:sub(1, #start_str) == start_str
end
function string:ends_with(end_str)
if #self == 0 or #self < #end_str then return false end
if end_str == nil or end_str == "" then return true end
return self:sub(-#end_str) == end_str
end
function string:split(sep) function string:split(sep)
if sep == nil then if sep == nil then
sep = "%s" sep = "%s"
@@ -29,7 +49,7 @@ function slice(tbl, s, e)
return new return new
end end
function get_index(tab, val) function index(tab, val)
for index, value in ipairs(tab) do for index, value in ipairs(tab) do
if value == val then if value == val then
return index return index
@@ -39,16 +59,75 @@ function get_index(tab, val)
return 0 return 0
end end
function get_key(tab, val) function key(tab, val)
for index, value in pairs(tab) do for index, value in pairs(tab) do
if value == val then if value == val then
return index return index
end end
end end
return 0 return 0
end end
function concat(t1, t2)
for _,v in ipairs(t2) do
table.insert(t1, v)
end
end
function contains(table, val)
for i=1, #table do
if table[i] == val then
return true
end
end
return false
end
function reverse(tab)
for i = 1, math.floor(#tab/2) do
tab[i], tab[#tab-i+1] = tab[#tab-i+1], tab[i]
end
return tab
end
function serialize(tab, ind)
ind = ind and (ind .. " ") or " "
local nl = "\n"
local str = "{" .. nl
for k, v in pairs(tab) do
local pr = (type(k)=="string") and ("[\"" .. k .. "\"] = ") or ""
str = str .. ind .. pr
if type(v) == "table" then
str = str .. serialize(v, ind) .. ",\n"
elseif type(v) == "string" then
str = str .. "\"" .. tostring(v) .. "\",\n"
elseif type(v) == "number" or type(v) == "boolean" then
str = str .. tostring(v) .. ",\n"
else
str = str .. "[[" .. tostring(v) .. "]],\n"
end
end
str = str:gsub(".$","")
str = str .. nl .. ind .. "}"
return str
end
function deep_copy(orig)
local orig_type = type(orig)
local copy
if orig_type == 'table' then
copy = {}
for orig_key, orig_value in next, orig, nil do
copy[deep_copy(orig_key)] = deep_copy(orig_value)
end
setmetatable(copy, deep_copy(getmetatable(orig)))
else
copy = orig
end
return copy
end
function round(x, n) function round(x, n)
local n = math.pow(10, n or 0) local n = math.pow(10, n or 0)
local x = x * n local x = x * n
@@ -59,10 +138,208 @@ end
function use(module, ...) function use(module, ...)
for k,v in pairs(module) do for k,v in pairs(module) do
if _G[k] then if _G[k] then
io.stderr:write("use: skipping duplicate symbol ", k, "\n") print("use: skipping duplicate symbol ", k, "\n")
else else
_G[k] = module[k] _G[k] = module[k]
end end
end end
end end
function for_each(tbl, callback)
for index, value in ipairs(tbl) do
callback(value, index, tbl)
end
end
-- Deprecated
function get_index(tab, val)
return index(tab, val)
end
-- Deprecated
function get_key(tab, val)
return key(tab, val)
end
-- Deprecated
function concat_tables(t1, t2)
concat(t1, t2)
end
-- Functional Library
--
-- @file functional.lua
-- @author Shimomura Ikkei
-- @date 2005/05/18
--
-- @brief porting several convenience functional utilities form Haskell,Python etc..
-- map(function, table)
-- e.g: map(double, {1,2,3}) -> {2,4,6}
function map(func, tbl)
local newtbl = {}
for i,v in pairs(tbl) do
newtbl[i] = func(v)
end
return newtbl
end
-- filter(function, table)
-- e.g: filter(is_even, {1,2,3,4}) -> {2,4}
function filter(func, tbl)
local newtbl= {}
for i,v in pairs(tbl) do
if func(v) then
newtbl[i]=v
end
end
return newtbl
end
-- skip(table, N)
-- e.g: skip({1,2,3,4}, 2) -> {3,4}
function skip(tbl, N)
local result = {}
for i = N+1, #tbl do
table.insert(result, tbl[i])
end
return result
end
-- take(table, N)
-- e.g: take({1,2,3,4}, 2) -> {1,2}
function take(tbl, N)
local result = {}
for i = 1, N do
if tbl[i] == nil then
break
end
table.insert(result, tbl[i])
end
return result
end
-- head(table)
-- e.g: head({1,2,3}) -> 1
function head(tbl)
return tbl[1]
end
-- tail(table)
-- e.g: tail({1,2,3}) -> {2,3}
--
-- XXX This is a BAD and ugly implementation.
-- should return the address to next porinter, like in C (arr+1)
function tail(tbl)
if #tbl < 1 then
return nil
else
local newtbl = {}
local i = 2
while (i <= #tbl) do
table.insert(newtbl, i-1, tbl[i])
i = i + 1
end
return newtbl
end
end
-- foldr(function, default_value, table)
-- e.g: foldr(operator.mul, 1, {1,2,3,4,5}) -> 120
function foldr(func, val, tbl)
for i,v in pairs(tbl) do
val = func(val, v)
end
return val
end
-- reduce(function, table)
-- e.g: reduce(operator.add, {1,2,3,4}) -> 10
function reduce(func, tbl)
return foldr(func, head(tbl), tail(tbl))
end
-- curry(f,g)
-- e.g: printf = curry(io.write, string.format)
-- -> function(...) return io.write(string.format(unpack(arg))) end
function curry(f, g)
return function (...)
return f(g(table.unpack({...})))
end
end
-- bind1(func, binding_value_for_1st)
-- bind2(func, binding_value_for_2nd)
-- @brief
-- Binding argument(s) and generate new function.
-- @see also STL's functional, Boost's Lambda, Combine, Bind.
-- @examples
-- local mul5 = bind1(operator.mul, 5) -- mul5(10) is 5 * 10
-- local sub2 = bind2(operator.sub, 2) -- sub2(5) is 5 -2
function bind1(func, val1)
return function (val2)
return func(val1, val2)
end
end
function bind2(func, val2) -- bind second argument.
return function (val1)
return func(val1, val2)
end
end
-- is(checker_function, expected_value)
-- @brief
-- check function generator. return the function to return boolean,
-- if the condition was expected then true, else false.
-- @example
-- local is_table = is(type, "table")
-- local is_even = is(bind2(math.mod, 2), 1)
-- local is_odd = is(bind2(math.mod, 2), 0)
is = function(check, expected)
return function (...)
if (check(table.unpack({...})) == expected) then
return true
else
return false
end
end
end
-- operator table.
-- @see also python's operator module.
operator = {
mod = math.mod;
pow = math.pow;
add = function(n,m) return n + m end;
sub = function(n,m) return n - m end;
mul = function(n,m) return n * m end;
div = function(n,m) return n / m end;
gt = function(n,m) return n > m end;
lt = function(n,m) return n < m end;
eq = function(n,m) return n == m end;
le = function(n,m) return n <= m end;
ge = function(n,m) return n >= m end;
ne = function(n,m) return n ~= m end;
}
-- enumFromTo(from, to)
-- e.g: enumFromTo(1, 10) -> {1,2,3,4,5,6,7,8,9}
-- TODO How to lazy evaluate in Lua? (thinking with coroutine)
enumFromTo = function (from,to)
local newtbl = {}
local step = bind2(operator[(from < to) and "add" or "sub"], 1)
local val = from
while val <= to do
table.insert(newtbl, table.getn(newtbl)+1, val)
val = step(val)
end
return newtbl
end
-- make function to take variant arguments, replace of a table.
-- this does not mean expand the arguments of function took,
-- it expand the function's spec: function(tbl) -> function(...)
function expand_args(func)
return function(...) return func(arg) end
end

View File

@@ -2,17 +2,12 @@
-- description = "Simple battery info widget" -- description = "Simple battery info widget"
-- author = "Evgeny Zobnin (zobnin@gmail.com)" -- author = "Evgeny Zobnin (zobnin@gmail.com)"
ticks = -1 function on_tick(ticks)
function on_tick()
-- Update one time per 10 seconds -- Update one time per 10 seconds
ticks = ticks + 1
if ticks % 10 ~= 0 then if ticks % 10 ~= 0 then
return return
end end
ticks = 0
local batt_info = system:battery_info() local batt_info = system:battery_info()
local batt_strings = stringify_table(batt_info) local batt_strings = stringify_table(batt_info)
local folded_str = "Battery: "..batt_info.percent.."% | "..batt_info.temp.."° | "..batt_info.voltage.." mV" local folded_str = "Battery: "..batt_info.percent.."% | "..batt_info.temp.."° | "..batt_info.voltage.." mV"

70
main/calendar-menu.lua Normal file
View File

@@ -0,0 +1,70 @@
-- name = "Calendar menu"
-- name_id = "calendar"
-- description = "Shows events from system calendar"
-- type = "drawer"
-- aio_version = "4.7.99"
-- author = "Evgeny Zobnin"
-- version = "1.1"
local fmt = require "fmt"
local have_permission = false
local events = {}
local calendars = {}
function on_drawer_open()
events = calendar:events()
calendars = calendar:calendars()
if events == "permission_error" then
calendar:request_permission()
return
end
have_permission = true
add_cal_colors(events, calendars)
if #events == #drawer:items() then
return
end
lines = map(function(it)
local date = fmt.colored(format_date(it.begin), it.calendar_color)
return date..fmt.space(4)..it.title
end, events)
drawer:show_ext_list(lines)
end
function add_cal_colors(events, cals)
for i, event in ipairs(events) do
for _, cal in ipairs(cals) do
if event.calendar_id == cal.id then
event.calendar_color = cal.color
break
end
end
end
end
function format_date(date)
if system.format_date_localized then
return system:format_date_localized("dd.MM", date)
else
return os.date("%d.%m", date)
end
end
function on_click(idx)
if not have_permission then return end
calendar:show_event_dialog(events[idx].id)
end
function on_long_click(idx)
if not have_permission then return end
calendar:open_event(events[idx].id)
end

View File

@@ -2,7 +2,7 @@
-- description = "Monthly calendar with system calendar events" -- description = "Monthly calendar with system calendar events"
-- type = "widget" -- type = "widget"
-- author = "Andrey Gavrilov" -- author = "Andrey Gavrilov"
-- version = "3.1" -- version = "3.2"
local tab = {} local tab = {}
local line = " " local line = " "
@@ -15,12 +15,11 @@ local month = os.date("%m"):gsub("^0","")
local day = os.date("%d"):gsub("^0","") local day = os.date("%d"):gsub("^0","")
function on_resume() function on_resume()
--ui:set_folding_flag(true) if calendar:events(0,0) == "permission_error" then
ui:show_table(table_to_tables(tab,8),0, true, line) widget_type = "text"
widget_type = "table" ui:show_text("Click to grant permission")
end return
end
function on_alarm()
if next(settings:get()) == nil then if next(settings:get()) == nil then
settings:set(get_all_cals()[2]) settings:set(get_all_cals()[2])
end end
@@ -33,7 +32,7 @@ end
function on_settings() function on_settings()
dialog_id = "settings" dialog_id = "settings"
ui:show_checkbox_dialog("Check calendars", get_all_cals()[3],cal_id_to_id(settings:get())) dialogs:show_checkbox_dialog("Check calendars", get_all_cals()[3],cal_id_to_id(settings:get()))
end end
function on_click(i) function on_click(i)
@@ -43,16 +42,16 @@ function on_click(i)
local time = os.time{year=year,month=month,day=1}-24*60*60 local time = os.time{year=year,month=month,day=1}-24*60*60
year,month = os.date("%Y-%m",time):match("(%d+)-(%d+)") year,month = os.date("%Y-%m",time):match("(%d+)-(%d+)")
year,month = year:gsub("^0",""),month:gsub("^0","") year,month = year:gsub("^0",""),month:gsub("^0","")
on_alarm() on_resume()
elseif i == 8 then elseif i == 8 then
system:vibrate(10) system:vibrate(10)
local time = os.time{year=year,month=month,day=1}+31*24*60*60 local time = os.time{year=year,month=month,day=1}+31*24*60*60
year,month = os.date("%Y-%m",time):match("(%d+)-(%d+)") year,month = os.date("%Y-%m",time):match("(%d+)-(%d+)")
year,month = year:gsub("^0",""),month:gsub("^0","") year,month = year:gsub("^0",""),month:gsub("^0","")
on_alarm() on_resume()
elseif i > 1 and i < 8 then elseif i > 1 and i < 8 then
dialog_id = "date" dialog_id = "date"
ui:show_edit_dialog("Enter month and year", "Format - 12.2020. Empty value - current month", string.format("%02d.%04d", month, year)) dialogs:show_edit_dialog("Enter month and year", "Format - 12.2020. Empty value - current month", string.format("%02d.%04d", month, year))
return return
elseif (i-1)%8 ~= 0 and tab[i] ~= " " then elseif (i-1)%8 ~= 0 and tab[i] ~= " " then
day = tab[i]:match(">(%d+)<"):gsub("^0","") day = tab[i]:match(">(%d+)<"):gsub("^0","")
@@ -72,9 +71,15 @@ function on_click(i)
widget_type = "table" widget_type = "table"
ui:show_table(table_to_tables(tab,8),0, true, line) ui:show_table(table_to_tables(tab,8),0, true, line)
end end
elseif widget_type == "text" then
calendar:request_permission()
end end
end end
function on_permission_granted()
on_resume()
end
function on_dialog_action(data) function on_dialog_action(data)
if data == -1 then if data == -1 then
events = {} events = {}
@@ -90,7 +95,7 @@ function on_dialog_action(data)
return return
end end
year,month = y,m year,month = y,m
on_alarm() on_resume()
return return
elseif not check_date(data) then elseif not check_date(data) then
return return
@@ -103,15 +108,15 @@ function on_dialog_action(data)
end end
month,year = data:match("(%d+)%.(%d+)") month,year = data:match("(%d+)%.(%d+)")
month,year = month:gsub("^0",""),year:gsub("^0","") month,year = month:gsub("^0",""),year:gsub("^0","")
on_alarm() on_resume()
elseif dialog_id == "settings" then elseif dialog_id == "settings" then
settings:set(id_to_cal_id(data)) settings:set(id_to_cal_id(data))
on_alarm() on_resume()
end end
end end
function get_cal(y,m) function get_cal(y,m)
local color = ui:colors() local color = aio:colors()
local events = get_my_events(y,m,0) local events = get_my_events(y,m,0)
local from = os.time{year=y,month=m,day=1} local from = os.time{year=y,month=m,day=1}
local tab = { local tab = {
@@ -146,7 +151,7 @@ function get_cal(y,m)
end end
function format_day(y,m,d,events) function format_day(y,m,d,events)
local color = ui:colors() local color = aio:colors()
local from = os.time{year=y,month=m,day=d,hour=0,min=0,sec=0} local from = os.time{year=y,month=m,day=d,hour=0,min=0,sec=0}
local to = os.time{year=y,month=m,day=d,hour=23,min=59,sec=59} local to = os.time{year=y,month=m,day=d,hour=23,min=59,sec=59}
local yes = false local yes = false
@@ -163,7 +168,7 @@ function format_day(y,m,d,events)
end end
if year == os.date("%Y"):gsub("^0","") and month == os.date("%m"):gsub("^0","") and d == os.date("%d"):gsub("^0","") then if year == os.date("%Y"):gsub("^0","") and month == os.date("%m"):gsub("^0","") and d == os.date("%d"):gsub("^0","") then
dd = "<font color=\""..color.progress_good.."\">"..dd.."</font>" dd = "<font color=\""..color.progress_good.."\">"..dd.."</font>"
elseif os.date("%w",from):gsub("0","7")-5 > 0 then elseif calendar.is_holiday and calendar:is_holiday(os.time{year=y,month=m,day=d}) then
dd = "<font color=\""..color.progress_bad.."\">"..dd.."</font>" dd = "<font color=\""..color.progress_bad.."\">"..dd.."</font>"
else else
dd = "<font color=\""..color.primary_text.."\">"..dd.."</font>" dd = "<font color=\""..color.primary_text.."\">"..dd.."</font>"
@@ -256,7 +261,7 @@ function get_day_tab(events)
end end
function get_lines(events) function get_lines(events)
local color = ui:colors() local color = aio:colors()
local lines = {} local lines = {}
for i,v in ipairs(events) do for i,v in ipairs(events) do
table.insert(lines,"<font color = \""..v[9].."\">•</font>") table.insert(lines,"<font color = \""..v[9].."\">•</font>")

72
main/contacts-menu.lua Normal file
View File

@@ -0,0 +1,72 @@
-- name = "Contacts menu"
-- name_id = "contacts"
-- description = "Shows phone contacts in the side menu"
-- type = "drawer"
-- aio_version = "4.7.99"
-- author = "Evgeny Zobnin"
-- version = "1.0"
local have_permission = false
function on_drawer_open()
local raw_contacts = phone:contacts()
if raw_contacts == "permission_error" then
phone:request_permission()
return
end
have_permission = true
contacts = distinct_by_name(
sort_by_name(phone:contacts())
)
if #contacts == #drawer:items() then
return
end
names = map(contacts, function(it) return it.name end)
keys = map(contacts, function(it) return it.lookup_key end)
icons = map(contacts, function(it)
if it.icon ~= nil then
return it.icon
else
return "fa:phone" -- Backward compatibility
end
end)
drawer:show_list(names, icons, nil, true)
end
function on_click(idx)
if not have_permission then return end
phone:show_contact_dialog(contacts[idx].lookup_key)
end
function map(tbl, f)
local ret = {}
for k,v in pairs(tbl) do
ret[k] = f(v)
end
return ret
end
function sort_by_name(tbl)
table.sort(tbl, function(a,b) return a.name:lower() < b.name:lower() end)
return tbl
end
function distinct_by_name(tbl)
local ret = {}
local names = {}
for _, contact in ipairs(tbl) do
if not names[contact.name] then
table.insert(ret, contact)
end
names[contact.name] = true
end
return ret
end

View File

@@ -1,30 +0,0 @@
-- name = "Covid info"
-- description = "Cases of illness and death from covid"
-- data_source = "covid19api.com"
-- type = "widget"
-- author = "Evgeny Zobnin (zobnin@gmail.com)"
-- version = "1.0"
equals = "<font color=\""..ui:colors().secondary_text.."\"> = </font>"
function on_alarm()
http:get("https://api.covid19api.com/summary")
end
function on_network_result(result)
local new = ajson:get_value(result, "object object:Global int:NewConfirmed")
local total = ajson:get_value(result, "object object:Global int:TotalConfirmed")
local newDeaths = ajson:get_value(result, "object object:Global int:NewDeaths")
local totalDeaths = ajson:get_value(result, "object object:Global int:TotalDeaths")
ui:show_lines({
"<b>Disease</b> | total"..equals..comma_value(total).." | new"..equals..comma_value(new),
"<b>Deaths</b> | total"..equals..comma_value(totalDeaths).." | new"..equals..comma_value(newDeaths)
})
end
function comma_value(n) -- credit http://richard.warburton.it
local left,num,right = string.match(n,'^([^%d]*%d)(%d*)(.-)$')
return left..(num:reverse():gsub('(%d%d%d)','%1,'):reverse())..right
end

View File

@@ -1,5 +1,5 @@
-- name = "Random facts" -- name = "Random facts"
-- description = "Radom useless facts" -- description = "Random useless facts"
-- data_source = "https://uselessfacts.jsph.pl/" -- data_source = "https://uselessfacts.jsph.pl/"
-- type = "widget" -- type = "widget"
-- author = "Evgeny Zobnin (zobnin@gmail.com)" -- author = "Evgeny Zobnin (zobnin@gmail.com)"
@@ -10,14 +10,15 @@ function on_alarm()
http:get("https://uselessfacts.jsph.pl/random.json?language=en") http:get("https://uselessfacts.jsph.pl/random.json?language=en")
end end
function on_network_result(result) function on_network_result(result, code)
text = ajson:get_value(result, "object string:text") if code >= 200 and code < 299 then
text = ajson:read(result, "object string:text")
ui:show_lines{ text } ui:show_lines{ text }
end
end end
function on_click() function on_click()
if text ~= nil then if text ~= nil then
system:copy_to_clipboard(text) system:to_clipboard(text)
end end
end end

View File

@@ -1,24 +1,27 @@
-- name = "Inspiration quotes" -- name = "Inspiration quotes"
-- description = "inspiration.goprogram.ai" -- description = "ZenQuotes.io"
-- data_source = "inspiration.goprogram.ai" -- data_source = "https://zenquotes.io/"
-- type = "widget" -- type = "widget"
-- author = "Evgeny Zobnin (zobnin@gmail.com)" -- author = "Evgeny Zobnin (zobnin@gmail.com)"
-- version = "1.0" -- version = "2.0"
-- foldable = "false" -- foldable = "false"
local json = require "json"
function on_alarm() function on_alarm()
http:get("https://inspiration.goprogram.ai/") http:get("https://zenquotes.io/api/random")
end end
function on_network_result(result) function on_network_result(result, code)
quote = ajson:get_value(result, "object string:quote") if code >= 200 and code < 299 then
author = ajson:get_value(result, "object string:author") res = json.decode(result)
ui:show_lines({ res[1].q }, { res[1].a })
ui:show_lines({ quote }, { author }) end
end end
function on_click() function on_click()
if quote ~= nil then if res ~= nil then
system:copy_to_clipboard(quote) system:to_clipboard(res[1].q.." - "..res[1].a)
end end
end end

View File

@@ -10,6 +10,8 @@ function on_alarm()
http:get("https://api.ipify.org") http:get("https://api.ipify.org")
end end
function on_network_result(result) function on_network_result(result, code)
ui:show_text(result) if code >= 200 and code < 299 then
ui:show_text(result)
end
end end

View File

@@ -0,0 +1,69 @@
-- name = "Sunrise/Sunset"
-- description = "Shows Sunrise Sunset at your location"
-- data_source = "https://api.sunrise-sunset.org/"
-- type = "widget"
-- author = "Sriram S V"
-- version = "1.1"
-- foldable = "false"
local json = require "json"
local fmt = require "fmt"
function on_alarm()
local location = system:location()
if location == nil then
return
elseif location == "permission_error" then
ui:show_text("No location permission")
return
end
local url = "https://api.sunrise-sunset.org/json?lat=" .. location[1] .. "&lng=" .. location[2] .. "&formatted=0"
http:get(url)
end
function on_network_result(result)
local t = json.decode(result)
local sunrise_time = utc_to_local(parse_iso8601_datetime(t.results.sunrise))
local sunset_time = utc_to_local(parse_iso8601_datetime(t.results.sunset))
ui:show_text(
aio:res_string("today", "Today")..":"..
fmt.space(4)..""..fmt.space(2)..sunrise_time..
fmt.space(4)..""..fmt.space(2)..sunset_time
)
end
function parse_iso8601_datetime(json_date)
local pattern = "(%d+)%-(%d+)%-(%d+)%a(%d+)%:(%d+)%:([%d%.]+)([Z%+%-]?)(%d?%d?)%:?(%d?%d?)"
local year, month, day, hour, minute,
seconds, offsetsign, offsethour, offsetmin = json_date:match(pattern)
local timestamp = os.time{year = year, month = month,
day = day, hour = hour, min = minute, sec = seconds}
local offset = 0
if offsetsign ~= '' and offsetsign ~= 'Z' then
offset = tonumber(offsethour) * 60 + tonumber(offsetmin)
if xoffset == "-" then offset = offset * -1 end
end
return timestamp + offset * 60
end
function utc_to_local(utctime)
local local_time_str = os.date("%H:%M", utctime)
local utc_time_str = os.date("!%H:%M", utctime)
local function time_to_seconds(timestr)
local hour, minute = timestr:match("(%d+):(%d+)")
return tonumber(hour) * 3600 + tonumber(minute) * 60
end
local local_seconds = time_to_seconds(local_time_str)
local utc_seconds = time_to_seconds(utc_time_str)
local delta = local_seconds - utc_seconds
return os.date("%H:%M", utctime + delta)
end

View File

@@ -4,17 +4,11 @@
-- author = "Evgeny Zobnin (zobnin@gmail.com)" -- author = "Evgeny Zobnin (zobnin@gmail.com)"
-- version = "1.0" -- version = "1.0"
ticks = -1 function on_tick(ticks)
function on_tick()
-- Update one time per 10 seconds
ticks = ticks + 1
if ticks % 10 ~= 0 then if ticks % 10 ~= 0 then
return return
end end
ticks = 0
local info = system:system_info() local info = system:system_info()
local strings = stringify_table(info) local strings = stringify_table(info)

130
main/tasks-menu.lua Normal file
View File

@@ -0,0 +1,130 @@
-- name = "Notes & tasks menu"
-- name_id = "notes"
-- description = "Shows tasks in the side menu"
-- type = "drawer"
-- aio_version = "4.7.99"
-- author = "Evgeny Zobnin"
-- version = "1.0"
local fmt = require "fmt"
local prefs = require "prefs"
local primary_color = aio:colors().primary_color
local secondary_color = aio:colors().secondary_color
local bottom_buttons = {
"fa:note_sticky", -- notes tab
"fa:list-check", -- tasks tab
"fa:pipe", -- separator
"fa:note_medical", -- new note button
"fa:square_plus" -- new task button
}
local notes_list = {}
local tasks_list = {}
if prefs.curr_tab == nil then
prefs.curr_tab = 1
end
function on_drawer_open()
drawer:add_buttons(bottom_buttons, prefs.curr_tab)
if prefs.curr_tab == 1 then
notes:load()
else
tasks:load()
end
end
function on_tasks_loaded(new_tasks)
tasks_list = new_tasks
local texts = map(tasks_list, task_to_text)
drawer:show_ext_list(texts)
end
function on_notes_loaded(new_notes)
notes_list = new_notes
local texts = map(notes_list, note_to_text)
drawer:show_ext_list(texts)
end
function note_to_text(it)
if it.text == "test note" then
test_note = it
end
if it.color ~= 6 then
return fmt.colored(it.text, notes:colors()[it.color])
else
return it.text
end
end
function task_to_text(it)
local text = ""
local date_str = os.date("%b, %d, %H:%M", it.due_date)
if it.completed_date > 0 then
text = fmt.strike(it.text)
elseif it.due_date < os.time() then
text = fmt.bold(fmt.red(it.text))
elseif it.is_today then
text = fmt.bold(it.text)
else
text = it.text
end
return text.."<br/>"..fmt.space(4)..fmt.small(date_str)
end
function on_click(idx)
if prefs.curr_tab == 1 then
on_note_click(idx)
else
on_task_click(idx)
end
end
function on_note_click(idx)
notes:show_editor(notes_list[idx].id)
end
function on_task_click(idx)
tasks:show_editor(tasks_list[idx].id)
end
function on_long_click(idx)
if prefs.curr_tab == 1 then
on_note_long_click(idx)
else
on_task_long_click(idx)
end
end
function on_note_long_click(idx)
system:to_clipboard(notes_list[idx].text)
end
function on_task_long_click(idx)
system:to_clipboard(tasks_list[idx].text)
end
function on_button_click(idx)
if idx < 3 then
prefs.curr_tab = idx
on_drawer_open()
elseif idx == 4 then
notes:show_editor()
elseif idx == 5 then
tasks:show_editor()
end
end
function map(tbl, f)
local ret = {}
for k,v in pairs(tbl) do
ret[k] = f(v)
end
return ret
end

View File

@@ -18,7 +18,7 @@ function on_alarm()
end end
function redraw() function redraw()
local color = ui:colors() local color = aio:colors()
if unit == "temperature" then if unit == "temperature" then
sum = round(f[units[unit][unit_from]..units[unit][unit_to]](amount),3) sum = round(f[units[unit][unit_from]..units[unit][unit_to]](amount),3)
else else
@@ -37,10 +37,10 @@ end
function on_click(idx) function on_click(idx)
if idx == 1 then if idx == 1 then
dialog_id ="amount" dialog_id ="amount"
ui:show_edit_dialog("Enter amount", "", amount) dialogs:show_edit_dialog("Enter amount", "", amount)
elseif idx == 2 then elseif idx == 2 then
dialog_id = "unit_from" dialog_id = "unit_from"
ui:show_radio_dialog("Select unit",get_radio_tab(unit),get_radio_idx(unit_from)) dialogs:show_radio_dialog("Select unit",get_radio_tab(unit),get_radio_idx(unit_from))
elseif idx == 3 then elseif idx == 3 then
local unit_tmp = unit_from local unit_tmp = unit_from
unit_from = unit_to unit_from = unit_to
@@ -50,10 +50,10 @@ function on_click(idx)
system:copy_to_clipboard(sum) system:copy_to_clipboard(sum)
elseif idx == 5 then elseif idx == 5 then
dialog_id = "unit_to" dialog_id = "unit_to"
ui:show_radio_dialog("Select unit",get_radio_tab(unit),get_radio_idx(unit_to)) dialogs:show_radio_dialog("Select unit",get_radio_tab(unit),get_radio_idx(unit_to))
elseif idx == 6 then elseif idx == 6 then
dialog_id = "unit" dialog_id = "unit"
ui:show_radio_dialog("Select converter",get_radio_tab(""),get_radio_idx(unit)) dialogs:show_radio_dialog("Select converter",get_radio_tab(""),get_radio_idx(unit))
end end
end end

View File

@@ -1,130 +0,0 @@
-- name = "Widgets switcher"
-- description = "Turns screen widgets on and off when buttons are pressed"
-- type = "widget"
-- arguments_help = "Don't change the arguments directly, use the long click menu."
-- author = "Andrey Gavrilov"
-- version = "2.0"
--constants--
local widgets = {"weather","weatheronly","clock","alarm","worldclock","monitor","traffic","player","apps","appbox","applist","contacts","notify","dialogs","dialer","timer","stopwatch","mail","notes","tasks","feed","telegram","twitter","calendar","exchange","finance","bitcoin","control","recorder","calculator","empty","bluetooth","map","remote"}
local icons = {"fa:user-clock","fa:sun-cloud","fa:clock","fa:alarm-clock","fa:business-time","fa:network-wired","fa:exchange","fa:play-circle","fa:robot","fa:th","fa:list","fa:address-card","fa:bell","fa:comment-alt-minus","fa:phone-alt","fa:chess-clock","fa:stopwatch","fa:at","fa:sticky-note","fa:calendar-check","fa:rss-square","fa:paper-plane","fa:dove","fa:calendar-alt","fa:euro-sign","fa:chart-line","fa:coins","fa:wifi","fa:microphone-alt","fa:calculator-alt","fa:eraser","fa:head-side-headphones","fa:map-marked-alt","fa:user-tag"}
local names = {"Clock & weather","Weather","Clock","Alarm","Worldclock","Monitor","Traffic","Player","Frequent apps","My apps","App list","Contacts","Notify","Dialogs","Dialer","Timer","Stopwatch","Mail","Notes","Tasks","Feed","Telegram","Twitter","Calendar","Exchange","Finance","Bitcoin","Control panel","Recorder","Calculator","Empty widget","Bluetooth","Map","User widget"}
--variables--
local pos = 0
function on_resume()
if next(settings:get()) == nil then
set_default_args()
end
ui:set_folding_flag(true)
local buttons,colors = get_buttons()
ui:show_buttons(buttons, colors)
end
function on_click(idx)
pos = idx
redraw()
end
function on_long_click(idx)
pos = idx
local tab = settings:get()
ui:show_context_menu({{"angle-left",""},{"ban",""},{"angle-right",""},{icons[get_checkbox_idx()[idx]]:gsub("fa:",""),names[get_checkbox_idx()[idx]]}})
end
function on_context_menu_click(menu_idx)
if menu_idx == 1 then
move(-1)
elseif menu_idx == 2 then
remove()
elseif menu_idx == 3 then
move(1)
elseif menu_idx == 4 then
redraw()
end
end
function on_dialog_action(data)
if data == -1 then
return
end
settings:set(data)
on_resume()
end
function on_settings()
ui:show_checkbox_dialog("Select widgets", names, get_checkbox_idx())
end
--utilities--
function redraw()
local buttons,colors = get_buttons()
local checkbox_idx = get_checkbox_idx()
local widget = widgets[checkbox_idx[pos]]
if aio:is_widget_added(widget) then
aio:remove_widget(widget)
colors[pos] = "#909090"
else
aio:add_widget(widget)
colors[pos] = "#1976d2"
end
ui:show_buttons(buttons, colors)
end
function set_default_args()
local args = {}
for i = 1, #widgets do
table.insert(args, i)
end
settings:set(args)
end
function get_checkbox_idx()
local tab = settings:get()
for i = 1, #tab do
tab[i] = tonumber(tab[i])
end
return tab
end
function get_buttons()
local buttons,colors = {},{}
local checkbox_idx = get_checkbox_idx()
for i = 1, #checkbox_idx do
table.insert(buttons, icons[checkbox_idx[i]])
if aio:is_widget_added(widgets[checkbox_idx[i]]) then
table.insert(colors, "#1976d2")
else
table.insert(colors, "#909090")
end
end
return buttons,colors
end
function move(x)
local tab = settings:get()
if (pos == 1 and x < 0) or (pos == #tab and x > 0) then
return
end
local cur = tab[pos]
local prev = tab[pos+x]
tab[pos+x] = cur
tab[pos] = prev
settings:set(tab)
local buttons,colors = get_buttons()
ui:show_buttons(buttons, colors)
end
function remove()
local tab = settings:get()
table.remove(tab,pos)
settings:set(tab)
local buttons,colors = get_buttons()
ui:show_buttons(buttons, colors)
end

View File

@@ -18,18 +18,20 @@ function on_alarm()
http:get(random_url) http:get(random_url)
end end
function on_network_result(result) function on_network_result(result, code)
local parsed = json.decode(result) if code >= 200 and code < 299 then
title = parsed.query.random[1].title local parsed = json.decode(result)
title = parsed.query.random[1].title
http:get(summary_url.."&titles="..url.quote(title), "summary") http:get(summary_url.."&titles="..url.quote(title), "summary")
end
end end
function on_network_result_summary(result) function on_network_result_summary(result, code)
local parsed = json.decode(result) if code >= 200 and code < 299 then
local extract = get_extract(parsed) local parsed = json.decode(result)
local extract = get_extract(parsed)
ui:show_lines({ smart_sub(extract, 200) }, { title }) ui:show_lines({ smart_sub(extract, 200) }, { title })
end
end end
function on_click() function on_click()

5
rm-scripts.sh Executable file
View File

@@ -0,0 +1,5 @@
#!/bin/sh
source ./env
adb shell rm -rf $SCRIPTS_DIR/*.lua

View File

@@ -25,7 +25,7 @@ function on_long_click()
if history == "" then if history == "" then
return return
end end
ui:show_dialog("История курсов ЦБ\n"..nominal.." "..cur.." / "..base_cur,history) dialogs:show_dialog("История курсов ЦБ\n"..nominal.." "..cur.." / "..base_cur,history)
end end
function on_settings() function on_settings()
@@ -38,7 +38,7 @@ function on_settings()
idx = i idx = i
end end
end end
ui:show_radio_dialog("Выберите валюту",names,idx) dialogs:show_radio_dialog("Выберите валюту",names,idx)
end end
function on_dialog_action(data) function on_dialog_action(data)
@@ -64,12 +64,12 @@ function on_network_result_today(result,error)
end end
today = today..v.Nominal:value().." "..v.CharCode:value().." = "..v.Value:value():replace(",",".").." "..base_cur today = today..v.Nominal:value().." "..v.CharCode:value().." = "..v.Value:value():replace(",",".").." "..base_cur
end end
ui:show_dialog("Курсы валют ЦБ\n"..date_today,today) dialogs:show_dialog("Курсы валют ЦБ\n"..date_today,today)
end end
function on_network_result_history(result,error) function on_network_result_history(result,error)
history = "" history = ""
local color = ui:get_colors() local color = aio:colors()
local equals = "<font color=\""..color.secondary_text.."\"> = </font>" local equals = "<font color=\""..color.secondary_text.."\"> = </font>"
local xml = require "xml" local xml = require "xml"
local t = xml:parse(result) local t = xml:parse(result)
@@ -111,7 +111,7 @@ function get_rates()
end end
function get_formatted_change_text(change) function get_formatted_change_text(change)
local color = ui:get_colors() local color = aio:colors()
if change > 0 then if change > 0 then
return "<font color=\""..color.progress_good.."\">&nbsp+"..change.."%</font>" return "<font color=\""..color.progress_good.."\">&nbsp+"..change.."%</font>"
elseif change < 0 then elseif change < 0 then

View File

@@ -11,14 +11,14 @@ function on_alarm()
end end
function on_network_result(result) function on_network_result(result)
quote = ajson:get_value(result, "object string:quoteText") quote = ajson:read(result, "object string:quoteText")
author = ajson:get_value(result, "object string:quoteAuthor") author = ajson:read(result, "object string:quoteAuthor")
ui:show_lines({ quote }, { author }) ui:show_lines({ quote }, { author })
end end
function on_click() function on_click()
if quote ~= nil then if quote ~= nil then
system:copy_to_clipboard(quote) system:to_clipboard(quote)
end end
end end

View File

@@ -0,0 +1,17 @@
function on_resume()
ui:show_lines{
"Add todo #1",
"Add todo #2",
"Add todo #3",
}
end
function on_click(idx)
if idx == 1 then
aio:add_todo("face-smile", "Todo #1")
elseif idx == 2 then
aio:add_todo("face-smile-tongue", "Todo #2")
else
aio:add_todo("face-smile-tear", "Todo #3")
end
end

7
samples/ai-sample.lua Normal file
View File

@@ -0,0 +1,7 @@
function on_alarm()
ai:complete("Who are you?")
end
function on_ai_answer(answer)
ui:show_text(answer)
end

View File

@@ -0,0 +1,69 @@
-- name = "Android widgets sample (Gmail)"
local prefs = require "prefs"
local max_mails = 1
local curr_tab = {}
local w_bridge = nil
function on_resume()
if not widgets:bound(prefs.wid) then
setup_app_widget()
end
widgets:request_updates(prefs.wid)
end
function on_app_widget_updated(bridge)
local tab = bridge:dump_strings().values
-- Removing redunant elements like widget title
tab = skip(tab, 4)
-- Each mail consists of 4 text elements:
-- 1. from, 2. time, 3. subject, 4. text
tab = take(tab, max_mails * 4)
-- Concatenate "from" and "time"
tab = concat(tab, {1, 2}, ", ")
curr_tab = tab
w_bridge = bridge
ui:show_lines(tab)
end
function on_click(idx)
w_bridge:click(curr_tab[idx])
end
function setup_app_widget()
local id = widgets:setup("com.google.android.gm/com.google.android.gm.widget.GmailWidgetProvider")
if (id ~= nil) then
prefs.wid = id
else
ui:show_text("Can't add widget")
return
end
end
-- Utils
function concat(tbl, indicesToConcatenate, delimiter)
local resultTable = {}
if #indicesToConcatenate > 0 then
local concatenatedString = {}
for _, index in ipairs(indicesToConcatenate) do
table.insert(concatenatedString, tbl[index])
end
table.insert(resultTable, table.concat(concatenatedString, delimiter))
end
for i = 1, #tbl do
if not contains(indicesToConcatenate, i) then
table.insert(resultTable, tbl[i])
end
end
return resultTable
end

View File

@@ -0,0 +1,56 @@
-- name = "Android widgets sample (The Weather Channel)"
-- uses_app = "com.weather.Weather"
local prefs = require "prefs"
local curr_temp = ""
local w_bridge = nil
function on_resume()
if not widgets:bound(prefs.wid) then
setup_app_widget()
end
widgets:request_updates(prefs.wid)
end
function on_app_widget_updated(bridge)
local tab = bridge:dump_table()
current_temp = tab.relative_layout_1.relative_layout_2.text_1
location = tab.relative_layout_1.relative_layout_2.text_2
w_bridge = bridge
if location ~= nil and current_temp ~= nil then
ui:show_text(location..": "..current_temp)
else
ui:show_text("Empty")
end
end
function on_app_widget_updated_alt(bridge)
local strings = bridge:dump_strings().values
location = strings[2]
current_temp = strings[1]
w_bridge = bridge
if location ~= nil and current_temp ~= nil then
ui:show_text(location..": "..current_temp)
else
ui:show_text("Empty")
end
end
function on_click(idx)
w_bridge:click(current_temp)
end
function setup_app_widget()
local id = widgets:setup("com.weather.Weather/com.weather.Weather.widgets.WeatherWidgetProvider2x2")
if (id ~= nil) then
prefs.wid = id
else
ui:show_text("Can't add widget")
end
end

Some files were not shown because too many files have changed in this diff Show More